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); }