PE Checksum Algorithm的较简实现

这篇BLOG是我很早以前写的,因为现在搬移到CNBLOGS了,经过整理后重新发出来。

工作之前的几年一直都在搞计算机安全/病毒相关的东西(纯学习,不作恶),其中PE文件格式是必须知识。有些PE文件,比如驱动,系统会在加载时对checksum进行校验,确保驱动文件的完整性。关于PE文件如何校验,网上有很多资料可以学习,这里有一篇文章《An Analysis of the Windows PE Checksum Algorithm》是对WINDOWS API  CheckSumMappedFile进行逆向分析的。文章的结尾提到WINDOWS的这个校验和算法和IP协议的校验和算法类似,IP的校验和算法实现是RFC1071,如果对其他的校验和算法感兴趣,可以阅读WIKI的《Error Detection and correction》。

但是CheckSumMappedFile的实现略显复杂,不够直观,后来读WRK的代码时,发现了WINDOWS内核在加载驱动时实现的校验和算法要简洁直观很多,分享给大家:

uint32_t calc_checksum(uint32_t checksum, void *data, int length) {
    if (length && data != nullptr) {
        uint32_t sum = 0;
        do {
            sum = *(uint16_t *)data + checksum;
            checksum = (uint16_t)sum + (sum >> 16);
            data = (char *)data + 2;
        } while (--length);
    }

    return checksum + (checksum >> 16);
}

uint32_t generate_pe_checksum(void *file_base, uint32_t file_size) {
    uint32_t file_checksum = 0;
    PIMAGE_NT_HEADERS nt_headers = ImageNtHeader(file_base);
    if (nt_headers) {
        uint32_t header_size = (uintptr_t)nt_headers - (uintptr_t)file_base +
            ((uintptr_t)&nt_headers->OptionalHeader.CheckSum -
            (uintptr_t)nt_headers);
        uint32_t remain_size = (file_size - header_size - 4) >> 1;
        void *remain = &nt_headers->OptionalHeader.Subsystem;
        uint32_t header_checksum = calc_checksum(0, file_base, header_size >> 1);
        file_checksum = calc_checksum(header_checksum, remain, remain_size);
        if (file_size & 1){
            file_checksum += (uint16_t)*((char *)file_base + file_size - 1);
        }
    }

    return (file_size + file_checksum);
}
posted @ 2014-08-21 11:03 011 阅读(...) 评论(...) 编辑 收藏