IDA API - 查询 cdecl 函数的参数数量

逆向工程 艾达 Python 职能
2021-06-14 10:11:29

我正在编写一个脚本,除其他外,它读取并使用函数的参数进行一些处理。现在,我找到给定函数的参数数量的解决方案addr是:

(GetStrucSize(GetFrame(addr)) - GetFrameSize(addr))/4

这是在 x86 上。问题是,例如,当我打电话时,printf()两个GetXXX()电话之间的差异是 5,这显然搞砸了数学。

目前,我已经通过检查第一个参数是否是带有格式说明符的格式字符串,然后手动解析它以获得实际计数来解决它,但这并不是真正可扩展的。

在 API 范围内有没有办法处理这个问题?

编辑:我应该在问题中更清楚地说明这一点,但这具体指的是 IDA API。我可以自己编写代码来计算清理,但为了保持我的代码干净,我试图避免大量代码查询它是否是 stdcall 和 cdecl 然后自己搜索/解析

1个回答

对于 cdecl,调用者需要清理堆栈。所以在函数调用之后的某个地方必须有一个堆栈操作,从堆栈中删除参数(删除调整堆栈指针)

PUSH 0xABCDEF01
CALL function
ADD ESP, 4 ; sizeof(DWORD) (the parameter)

它不需要直接在通话之后。(我认为 VS08 有时会将返回值检查放在它之前)所以这同样有效

PUSH 0xABCDEF01
CALL function
TEST EAX, EAX
ADD ESP, 4 ; sizeof(DWORD) (the parameter)
; A conditional jump probably

就像这样

PUSH 0xABCDEF01
CALL function
POP EAX 

因为 EAX 持有一个 DWORD,因此sizeof(DWORD)从堆栈中删除了== 4 个字节。(这显然也适用于所有其他通用扩展寄存器)。(例如,对于N可以使用的参数,这是可能的,N POP但我确信没有编译器会这样做,并且对于每N> 2 它将导致更大的代码,因此在手写汇编中发生也是不现实的。)

不存在最多N可在调用后检查以确定该堆栈操作是否为清理代码的指令。但是,我认为 5 几乎适用于所有情况(如果您碰巧已经知道此功能是 CDECL,则可以根据需要进行检查)

同样对于返回值不重要但在检查另一个函数的返回值之前调用的函数,可以像这样保存一个变量:

CALL function1
PUSH EAX ; Save EAX
PUSH something
CALL function2
POP EAX ; Remove the something parameter
POP EAX ; Restore the return value of function1

你也可以尝试计算PUSH但它带有自己的中间指令 - 这真的很常见:

PUSH something
LEA EAX, something_else ; Get a pointer to something_else
PUSH EAX 
PUSH more
CALL function

因此,一旦遇到不是PUSH.

总之,没有完美的解决方案,但我认为两者都可以产生相当可靠的价值;您选择哪个(或者两者都选择?)取决于您,IMO 第一个(搜索堆栈清理)更容易实现。