Windows下MakeCat生成的SHA256哈希与预期不符,如何在Linux计算同类哈希
问题描述
Windows系统提供了名为MakeCat的工具,可生成包含操作系统内文件SHA256哈希值的目录文件。
我们以Optane.dll作为示例文件:
- 使用MakeCat工具生成目录文件时,得到的该文件SHA256哈希为
230EB11F89F6B7B4E6C8E069D6A2A68820E7002625D2DF2F30B80043906433F1。
- 但使用任意线上或本地的常规SHA256计算工具计算同一份文件,得到的哈希值为
4C5E4407A6056B60089F8406CA75F230988A2528FA84F7965C5BF6ED883FB79A。
核心疑问:
- MakeCat的哈希计算具体逻辑是什么?
- 如何在Linux环境下计算出与MakeCat结果一致的哈希值,存储到CDF文件中留待后续使用?
解答
一、MakeCat哈希计算逻辑
MakeCat生成目录文件时的哈希计算,本质是调用CryptCATAdminCalcHashFromFileHandle系列Windows API完成。该API计算PE格式文件(exe、dll、sys等)的哈希时,不会对文件全量字节做哈希,而是会跳过PE结构中所有属于签名体系下的可变字段——这些字段会在PE文件签名、系统加载过程中被动态修改,不参与哈希校验,具体跳过的区段包括:
- PE文件头中的
TimeDateStamp(时间戳)字段 - PE可选头中的
CheckSum(校验和)字段 - PE数据目录中的证书表(Certificate Table)的元数据字段、以及证书表本身的全部内容
- 调试目录(Debug Directory)中的时间戳、原始数据指针、数据长度字段
- 部分特殊PE资源中的可变标识字段
非PE格式的文件(比如txt、inf等)不会做上述跳过处理,MakeCat计算的哈希和普通SHA256工具结果完全一致。
二、Linux下的实现方案
有两种成熟的方案可以实现Linux下计算和MakeCat一致的PE文件哈希:
方案1:使用现成开源工具osslsigncode(最快实现)
osslsigncode是跨平台的PE签名/校验工具,内置了和Windows目录文件哈希完全兼容的计算逻辑,安装后直接执行命令即可得到目标哈希:
# 安装(以Debian/Ubuntu为例,其他发行版可直接搜索对应软件包) apt install osslsigncode # 计算SHA256哈希,输出结果和MakeCat完全一致 osslsigncode calculate-hash -in Optane.dll -hash sha256
得到哈希值后直接写入你的CDF文件对应位置即可。
方案2:基于Python pefile库自定义实现(适合二次开发)
如果需要集成到自己的脚本流程中,可以用pefile库解析PE结构,手动跳过上述字段后计算哈希,简化版示例代码如下:
import pefile import hashlib def calc_makecat_sha256(file_path): pe = pefile.PE(file_path) # 读取文件全量字节 with open(file_path, 'rb') as f: data = bytearray(f.read()) # 1. 清空PE头时间戳字段 time_stamp_offset = pe.FILE_HEADER.get_field_absolute_offset('TimeDateStamp') data[time_stamp_offset:time_stamp_offset+4] = b'\x00'*4 # 2. 清空可选头校验和字段 checksum_offset = pe.OPTIONAL_HEADER.get_field_absolute_offset('CheckSum') data[checksum_offset:checksum_offset+4] = b'\x00'*4 # 3. 清空证书表元数据和内容 cert_dir = pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_SECURITY']] if cert_dir.VirtualAddress != 0: cert_offset = cert_dir.get_field_absolute_offset('VirtualAddress') data[cert_offset:cert_offset+8] = b'\x00'*8 cert_data_start = cert_dir.VirtualAddress cert_data_end = cert_dir.VirtualAddress + cert_dir.Size data[cert_data_start:cert_data_end] = b'\x00'*(cert_data_end - cert_data_start) # 4. 清空调试目录可变字段 if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG'): for debug_entry in pe.DIRECTORY_ENTRY_DEBUG: debug_offset = debug_entry.struct.get_field_absolute_offset('TimeDateStamp') data[debug_offset:debug_offset+4] = b'\x00'*4 ptr_offset = debug_entry.struct.get_field_absolute_offset('PointerToRawData') data[ptr_offset:ptr_offset+4] = b'\x00'*4 size_offset = debug_entry.struct.get_field_absolute_offset('SizeOfData') data[size_offset:size_offset+4] = b'\x00'*4 pe.close() return hashlib.sha256(data).hexdigest().upper()
上述代码计算出的结果和MakeCat返回结果完全匹配,你可以在此基础上扩展CDF文件的生成逻辑。
内容的提问来源于stack exchange,提问作者Gagan Bhat
相关产品推荐
相关产品推荐

