Кто-нибудь может определить алгоритм контрольной суммы Windows PE? - PullRequest
6 голосов
/ 21 июня 2011

Я хотел бы реализовать это в C #

Я посмотрел здесь: http://www.codeproject.com/KB/cpp/PEChecksum.aspx

И я знаю о функции ImageHlp.dll MapFileAndCheckSum.

Однако,по разным причинам я хотел бы реализовать это сам.

Лучшее, что я нашел здесь, это: http://forum.sysinternals.com/optional-header-checksum-calculation_topic24214.html

Но я не понимаю объяснения.Кто-нибудь может уточнить, как вычисляется контрольная сумма?

Спасибо!

Обновление

Я из примера кода, я не понимаю, что это значит,и как перевести его на C #

sum -= sum < low 16 bits of CheckSum in file // 16-bit borrow 
sum -= low 16 bits of CheckSum in file 
sum -= sum < high 16 bits of CheckSum in file 
sum -= high 16 bits of CheckSum in file 

Update # 2

Спасибо, наткнулся на некоторый код Python, который тоже похож на здесь

    def generate_checksum(self):

    # This will make sure that the data representing the PE image
    # is updated with any changes that might have been made by
    # assigning values to header fields as those are not automatically
    # updated upon assignment.
    #
    self.__data__ = self.write()

    # Get the offset to the CheckSum field in the OptionalHeader
    #
    checksum_offset = self.OPTIONAL_HEADER.__file_offset__ + 0x40 # 64

    checksum = 0

    # Verify the data is dword-aligned. Add padding if needed
    #
    remainder = len(self.__data__) % 4
    data = self.__data__ + ( '\0' * ((4-remainder) * ( remainder != 0 )) )

    for i in range( len( data ) / 4 ):

        # Skip the checksum field
        #
        if i == checksum_offset / 4:
            continue

        dword = struct.unpack('I', data[ i*4 : i*4+4 ])[0]
        checksum = (checksum & 0xffffffff) + dword + (checksum>>32)
        if checksum > 2**32:
            checksum = (checksum & 0xffffffff) + (checksum >> 32)

    checksum = (checksum & 0xffff) + (checksum >> 16)
    checksum = (checksum) + (checksum >> 16)
    checksum = checksum & 0xffff

    # The length is the one of the original data, not the padded one
    #
    return checksum + len(self.__data__)

Однако, это все еще не работает для меня - вот мое преобразование этого кода:

using System;
using System.IO;

namespace CheckSumTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var data = File.ReadAllBytes(@"c:\Windows\notepad.exe");

            var PEStart = BitConverter.ToInt32(data, 0x3c);
            var PECoffStart = PEStart + 4;
            var PEOptionalStart = PECoffStart + 20;
            var PECheckSum = PEOptionalStart + 64;
            var checkSumInFile = BitConverter.ToInt32(data, PECheckSum);
            Console.WriteLine(string.Format("{0:x}", checkSumInFile));

            long checksum = 0;

            var remainder = data.Length % 4;
            if (remainder > 0)
            {
                Array.Resize(ref data, data.Length + (4 - remainder));
            }

            var top = Math.Pow(2, 32);

            for (int i = 0; i < data.Length / 4; i++)
            {
                if (i == PECheckSum / 4)
                {
                    continue;
                }
                var dword = BitConverter.ToInt32(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; 
            Console.WriteLine(string.Format("{0:x}", checksum));

            Console.ReadKey();
        }
    }
}

Может кто-нибудь сказать мне, где я глуп?

Ответы [ 6 ]

5 голосов
/ 22 июня 2011

Хорошо, наконец-то все заработало нормально ... моя проблема была в том, что я использовал целые, а не уинты !!!Итак, этот код работает (при условии, что данные выровнены по 4 байта, в противном случае вам придется немного их дополнить) - и PECheckSum - это позиция значения CheckSum в PE (которое явно не используется при вычислении контрольной суммы!!!!)

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;

}
3 голосов
/ 21 июня 2011

Код в сообщении на форуме не совсем совпадает с тем, что было отмечено при фактической разборке кода Windows PE.В статье CodeProject, на которую вы ссылаетесь , «складывается 32-битное значение в 16 бит» как:

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  

, которое вы можете перевести в C # как:

// 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
2 голосов
/ 16 декабря 2012

Java-код ниже от emmanuel может не работать. В моем случае зависает и не завершается. Я считаю, что это связано с интенсивным использованием ввода-вывода в коде: в частности, data.read (). Это может быть заменено массивом в качестве решения. Где RandomAccessFile полностью или постепенно считывает файл в байтовый массив (ы).

Я пытался это сделать, но вычисление было слишком медленным из-за условного смещения контрольной суммы, чтобы пропустить байты заголовка контрольной суммы. Я полагаю, что решение OP на C # будет иметь аналогичную проблему.

Код ниже также удаляет это.

публичная статическая длинная computeChecksum (данные RandomAccessFile, int checkumOffset) выдает 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;
}

Однако я все еще думаю, что код был слишком многословным и неуклюжим, поэтому я заменил raf каналом и переписал байты преступника в ноль, чтобы устранить условные выражения. Этот код все еще, вероятно, можно сделать с буферизованным чтением в стиле кэша.

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;
}
0 голосов
/ 30 марта 2018

Пример Java не совсем корректен. Следующая реализация Java соответствует результату исходной реализации Microsoft от Imagehlp.MapFileAndCheckSumA.

Важно, чтобы входные байты маскировались с помощью inputByte & 0xff, а результирующее long маскировалось снова, когда оно используется в дополнительном члене с currentWord & 0xffffffffL (рассмотрим L):

    long checksum = 0;
    final long max = 4294967296L; // 2^32

    // verify the data is DWORD-aligned and add padding if needed
    final int remainder = data.length % 4;
    final byte[] paddedData = Arrays.copyOf(data, data.length
            + (remainder > 0 ? 4 - remainder : 0));

    for (int i = 0; i <= paddedData.length - 4; i += 4)
    {
        // skip the checksum field
        if (i == this.offsetToOriginalCheckSum)
            continue;

        // take DWORD into account for computation
        final long currentWord = (paddedData[i] & 0xff)
                               + ((paddedData[i + 1] & 0xff) << 8)
                               + ((paddedData[i + 2] & 0xff) << 16)
                               + ((paddedData[i + 3] & 0xff) << 24);

        checksum = (checksum & 0xffffffffL) + (currentWord & 0xffffffffL);

        if (checksum > max)
            checksum = (checksum & 0xffffffffL) + (checksum >> 32);
    }

    checksum = (checksum & 0xffff) + (checksum >> 16);
    checksum = checksum + (checksum >> 16);
    checksum = checksum & 0xffff;
    checksum += data.length; // must be original data length

В этом случае Java немного неудобен.

0 голосов
/ 10 декабря 2016
private unsafe static int GetSetPEChecksum(byte[] Array) {
    var Value = 0;
    var Count = Array.Length;
    if(Count >= 64)
        fixed (byte* array = Array) {
            var Index = 0;
            var Coff = *(int*)(array + 60);
            if(Coff >= 64 && Count >= Coff + 92) {
                *(int*)(array + Coff + 88) = 0;
                var Bound = Count >> 1;
                if((Count & 1) != 0) Value = array[Count & ~1];
                var Short = (ushort*)array;
                while(Index < Bound) {
                    Value += Short[Index++];
                    Value = (Value & 0xffff) + (Value >> 16);
                    Value = (Value + (Value >> 16)) & 0xffff;
                }
                *(int*)(array + Coff + 88) = Value += Count;
            }
        }
    return Value;
}

Если вам нужны короткие небезопасные ... (не нужно использовать двойные и длинные целые числа и не нужно выравнивать массив внутри алгоритма)

0 голосов
/ 14 мая 2012

Я пытался решить ту же проблему в Java. Вот решение Марка, переведенное на Java, с использованием RandomAccessFile вместо байтового массива в качестве ввода:

static long computeChecksum(RandomAccessFile data, long checksumOffset) throws IOException {
    long checksum = 0;
    long top = (long) Math.pow(2, 32);
    long length = data.length();

    for (long i = 0; i < length / 4; i++) {
        if (i == checksumOffset / 4) {
            data.skipBytes(4);
            continue;
        }

        long ch1 = data.read();
        long ch2 = data.read();
        long ch3 = data.read();
        long ch4 = data.read();

        long dword = ch1 + (ch2 << 8) + (ch3 << 16) + (ch4 << 24);

        checksum = (checksum & 0xffffffffL) + dword + (checksum >> 32);

        if (checksum > top) {
            checksum = (checksum & 0xffffffffL) + (checksum >> 32);
        }
    }

    checksum = (checksum & 0xffff) + (checksum >> 16);
    checksum = checksum + (checksum >> 16);
    checksum = checksum & 0xffff;
    checksum += length;

    return checksum;
}
...