如何使用Python pefile库检测PE可执行文件中的main函数?
定位PE文件main函数的实现方案
方案1:PE文件存在调试符号/导出表时直接读取
这种情况适合带符号的编译产物,或者main被导出的程序,直接通过pefile读取符号表即可,实现逻辑:
- 遍历PE的符号表,匹配
main/wmain/_tmain等常见的主函数名称,直接获取对应的地址
对应参考代码:
pe = pefile.PE(filename, fast_load=False) # 关闭fast_load才能加载符号表 if hasattr(pe, 'DIRECTORY_ENTRY_SYMBOL'): for sym in pe.DIRECTORY_ENTRY_SYMBOL: sym_name = sym.name.decode().strip() if sym_name in ('main', 'wmain', '_tmain', '_main'): main_rva = sym.Value print(f"找到main函数,RVA地址:{hex(main_rva)},文件偏移:{hex(pe.get_offset_from_rva(main_rva))}")
方案2:无符号表时通过入口点逆向定位
绝大多数MSVC/MinGW编译的Windows程序,入口点(pe.OPTIONAL_HEADER.AddressOfEntryPoint,即OEP)指向CRT启动函数,启动函数内部最终会调用用户定义的main函数,我们可以反汇编OEP附近的代码,识别调用主函数的指令即可定位main地址:
实现逻辑:
- 先获取OEP对应的文件偏移和内存基址
- 反汇编OEP后至少0x200字节长度的代码(CRT启动代码长度一般不超过这个范围)
- 识别符合main调用特征的指令:通常是在一系列参数压栈/寄存器传参(x64下是rcx、rdx、r8三个寄存器分别传argc、argv、envp)之后的call指令,该call的目标地址就是main函数起始地址
基于你现有代码的修改参考:
import pefile from capstone import * def find_main(filename): pe = pefile.PE(filename, fast_load=True) oep_rva = pe.OPTIONAL_HEADER.AddressOfEntryPoint # 找到OEP所在的段 target_sec = None for sec in pe.sections: if sec.contains_rva(oep_rva): target_sec = sec break if not target_sec: return None # 计算OEP在段数据中的偏移 oep_sec_offset = oep_rva - target_sec.VirtualAddress # 读取包含OEP的段数据,从OEP位置开始取0x300长度足够覆盖CRT启动代码 seg_data = target_sec.get_data() disasm_data = seg_data[oep_sec_offset : oep_sec_offset + 0x300] # x64模式反汇编,x86的话改成CS_MODE_32 md = Cs(CS_ARCH_X86, CS_MODE_64) md.detail = True # 基址用OEP的虚拟地址 base_addr = pe.OPTIONAL_HEADER.ImageBase + oep_rva # 遍历指令找call for insn in md.disasm(disasm_data, base_addr): if insn.mnemonic == 'call': # 获取call的目标地址 call_target = int(insn.op_str, 16) # 转换为RVA判断是否在.text段范围内,避免是调用其他系统API call_target_rva = call_target - pe.OPTIONAL_HEADER.ImageBase if target_sec.contains_rva(call_target_rva): main_rva = call_target_rva print(f"定位到main函数RVA:{hex(main_rva)},文件偏移:{hex(pe.get_offset_from_rva(main_rva))}") return main_rva return None
注意事项:
- 如果是32位PE,需要把capstone的模式改为
CS_MODE_32,32位下调用main前会有3次压栈操作对应argc、argv、envp三个参数,增加该特征过滤结果会更准确 - 自定义入口的非常规编译程序不适用该规则,需要针对性调整匹配特征
内容的提问来源于stack exchange,提问作者hashy
相关产品推荐
相关产品推荐

