如何将反汇编的C代码划分为函数?

Ada*_*der 5 c windows x86 assembly

我有一个应用程序,它创建.textwin32进程的段转储.然后它将基本块上的代码分开.基本块是一组一个接一个地执行的指令(跳转始终是这些基本块的最后指令).这是一个例子:

Basic block 1
    mov ecx, dword ptr [ecx]
    test ecx, ecx
    je 00401013h

Basic block 2
    mov eax, dword ptr [ecx]
    call dword ptr [eax+08h]

Basic block 3
    test eax, eax
    je 0040100Ah

Basic block 4
    mov edx, dword ptr [eax]
    push 00000001h
    mov ecx, eax
    call dword ptr [edx]

Basic block 5
    ret 000008h
Run Code Online (Sandbox Code Playgroud)

现在我想在函数中对这些基本块进行分组 - 比如哪些基本块构成一个函数.什么是算法?我必须记住,ret一个函数中可能有很多指令.如何检测fast_call功能?

Ale*_*nze 6

将块分组为函数的最简单算法是:

  1. 记下使用call some_address说明进行呼叫的所有地址
  2. 如果这个地址结束后的第一个块结束ret,那么你就完成了这个功能
  3. 跟随块中的跳转到另一个块,依此类推,直到你遵循所有可能的执行路径(记住条件跳转,每个路径将路径分成两个)并且所有路径都已完成ret.您需要识别组织循环的跳转,这样您的程序本身就不会因为进入无限循环而挂起

问题:

  1. 通过从内存中读取函数指针可以间接进行多次调用,例如,您可以使用call [some_address]而不是call some_address
  2. 可以对计算的地址进行一些间接调用
  3. 在返回之前调用其他函数的函数可能jump some_address不会call some_address紧跟其后ret
  4. call some_address可以使用push some_address+ retOR push some_address+ 的组合进行模拟jmp some_other_address
  5. 一些函数可以在它们的末尾共享代码(例如,它们具有不同的入口点,但是一个或多个出口点是相同的)

您可以使用一些启发式方法通过查找最常见的prolog指令序列来确定函数的起始位置:

push ebp
mov ebp, esp
Run Code Online (Sandbox Code Playgroud)

同样,如果在禁止帧指针的情况下编译函数,这可能不起作用(即,它们使用esp而不是ebp在堆栈上访问它们的参数,这是可能的).

编译器(例如MSVC++)也可以用int 3指令填充功能间空间,并且也可以作为即将到来的功能开始的提示.

至于区分各种调用约定,它可能是最简单的查看符号(当然,如果你有它们).MSVC++生成不同的名称前缀和后缀,例如:

  • _function - cdecl
  • _function @ number - stdcall
  • @ function @ number - fastcall

如果无法从符号中提取此信息,则必须分析代码以查看参数如何传递给函数以及函数或其调用者是否将其从堆栈中删除.