Can anyone define the Windows PE Checksum Algorithm?
Java code below from emmanuel may not work. In my case it hangs and does not complete. I believe this is due to the heavy use of IO in the code: in particular the data.read()'s. This can be swapped with an array as solution. Where the RandomAccessFile fully or incrementally reads the file into a byte array(s).
I attempted this but the calculation was too slow due to the conditional for the checksum offset to skip the checksum header bytes. I would imagine that the OP's C# solution would have a similar problem.
The below code removes this also.
public static long computeChecksum(RandomAccessFile data, int checksumOffset) throws IOException {
...
byte[] barray = new byte[(int) length];
data.readFully(barray);
long i = 0;
long ch1, ch2, ch3, ch4, dword;
while (i < checksumOffset) {
ch1 = ((int) barray[(int) i++]) & 0xff;
...
checksum += dword = ch1 | (ch2 << 8) | (ch3 << 16) | (ch4 << 24);
if (checksum > top) {
checksum = (checksum & 0xffffffffL) + (checksum >> 32);
}
}
i += 4;
while (i < length) {
ch1 = ((int) barray[(int) i++]) & 0xff;
...
checksum += dword = ch1 | (ch2 << 8) | (ch3 << 16) | (ch4 << 24);
if (checksum > top) {
checksum = (checksum & 0xffffffffL) + (checksum >> 32);
}
}
checksum = (checksum & 0xffff) + (checksum >> 16);
checksum = checksum + (checksum >> 16);
checksum = checksum & 0xffff;
checksum += length;
return checksum;
}
I still however think that code was too verbose and clunky so I swapped out the raf with a channel and rewrote the culprit bytes to zero's to eliminate the conditional. This code could still probably do with a cache style buffered read.
public static long computeChecksum2(FileChannel ch, int checksumOffset)
throws IOException {
ch.position(0);
long sum = 0;
long top = (long) Math.pow(2, 32);
long length = ch.size();
ByteBuffer buffer = ByteBuffer.wrap(new byte[(int) length]);
buffer.order(ByteOrder.LITTLE_ENDIAN);
ch.read(buffer);
buffer.putInt(checksumOffset, 0x0000);
buffer.position(0);
while (buffer.hasRemaining()) {
sum += buffer.getInt() & 0xffffffffL;
if (sum > top) {
sum = (sum & 0xffffffffL) + (sum >> 32);
}
}
sum = (sum & 0xffff) + (sum >> 16);
sum = sum + (sum >> 16);
sum = sum & 0xffff;
sum += length;
return sum;
}
The code in the forum post is not strictly the same as what was noted during the actual disassembly of the Windows PE code. The CodeProject article you reference gives the "fold 32-bit value into 16 bits" as:
mov edx,eax ; EDX = EAX
shr edx,10h ; EDX = EDX >> 16 EDX is high order
and eax,0FFFFh ; EAX = EAX & 0xFFFF EAX is low order
add eax,edx ; EAX = EAX + EDX High Order Folded into Low Order
mov edx,eax ; EDX = EAX
shr edx,10h ; EDX = EDX >> 16 EDX is high order
add eax,edx ; EAX = EAX + EDX High Order Folded into Low Order
and eax,0FFFFh ; EAX = EAX & 0xFFFF EAX is low order 16 bits
Which you could translate into C# as:
// given: uint sum = ...;
uint high = sum >> 16; // take high order from sum
sum &= 0xFFFF; // clear out high order from sum
sum += high; // fold high order into low order
high = sum >> 16; // take the new high order of sum
sum += high; // fold the new high order into sum
sum &= 0xFFFF; // mask to 16 bits
Ok, finally got it working ok... my problem was that I was using ints not uints!!! So, this code works (assuming data is 4-byte aligned, otherwise you'll have to pad it out a little) - and PECheckSum is the position of the CheckSum value within the PE (which is clearly not used when calculating the checksum!!!!)
static uint CalcCheckSum(byte[] data, int PECheckSum)
{
long checksum = 0;
var top = Math.Pow(2, 32);
for (var i = 0; i < data.Length / 4; i++)
{
if (i == PECheckSum / 4)
{
continue;
}
var dword = BitConverter.ToUInt32(data, i * 4);
checksum = (checksum & 0xffffffff) + dword + (checksum >> 32);
if (checksum > top)
{
checksum = (checksum & 0xffffffff) + (checksum >> 32);
}
}
checksum = (checksum & 0xffff) + (checksum >> 16);
checksum = (checksum) + (checksum >> 16);
checksum = checksum & 0xffff;
checksum += (uint)data.Length;
return (uint)checksum;
}
No one really answered the original question of "Can anyone define the Windows PE Checksum Algorithm?" so I'm going to define it as simply as possible. A lot of the examples given so far are optimizing for unsigned 32-bit integers (aka DWORDs), but if you just want to understand the algorithm itself at its most fundamental, it is simply this:
Using an unsigned 16-bit integer (aka a WORD) to store the checksum, add up all of the WORDs of the data except for the 4 bytes of the PE optional header checksum. If the file is not WORD-aligned, then the last byte is a 0x00.
Convert the checksum from a WORD to a DWORD and add the size of the file.
The PE checksum algorithm above is effectively the same as the original MS-DOS checksum algorithm. The only differences are the location to skip and replacing the XOR 0xFFFF at the end and adding the size of the file instead.
From my WinPEFile class for PHP, the above algorithm looks like:
$x = 0;
$y = strlen($data);
$val = 0;
while ($x < $y)
{
// Skip the checksum field location.
if ($x === $this->pe_opt_header["checksum_pos"]) $x += 4;
else
{
$val += self::GetUInt16($data, $x, $y);
// In PHP, integers are either signed 32-bit or 64-bit integers.
if ($val > 0xFFFF) $val = ($val & 0xFFFF) + 1;
}
}
// Add the file size.
$val += $y;