You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 00:54:03