为什么Ghidra会错误地解释这一点?该示例非常简单。

在此堆栈中:

0019FF58  $-8      0019FF58        00000002     LOCAL 2
0019FF5C  $-4      0019FF5C        00000001     LOCAL 1
0019FF60  $ ==>    0019FF60        0019FF80     OLD EBP
0019FF64  $+4      0019FF64      | 00401025     return to layout.00401025 from layout.sub_40102C
0019FF68  $+8      0019FF68      | 00000041     PARAM 3
0019FF6C  $+C      0019FF6C      | 0000BABE     PARAM 2
0019FF70  $+10     0019FF70      | 0000CAFE     PARAM 1


Ghidra获取:

undefined4        Stack[0x4]:4   param_1                                 XREF[1]:     00401040 (R)   
undefined4        Stack[0x8]:4   param_2                                 XREF[1]:     00401043 (R)   
undefined4        Stack[0xc]:4   param_3                                 XREF[1]:     00401046 (R)   
undefined4        Stack[-0x8]:4  local_8                                 XREF[1]:     00401032 (W)   
undefined4        Stack[-0xc]:4  local_c                                 XREF[1]:     00401039 (W)  
00401032 C745FC01000000            MOV        dword ptr [EBP  + local_8 ],0x1
00401039 C745F802000000            MOV        dword ptr [EBP  + local_c ],0x2
00401040 8B5D08                    MOV        EBX ,dword ptr [EBP  + param_1 ]
00401043 8B4D0C                    MOV        ECX ,dword ptr [EBP  + param_2 ]
00401046 FF7510                    PUSH       dword ptr [EBP  + param_3 ]


而IDA正确获取:

.text:0040102C var_8           = dword ptr -8
.text:0040102C var_4           = dword ptr -4
.text:0040102C arg_0           = dword ptr  8
.text:0040102C arg_4           = dword ptr  0Ch
.text:0040102C arg_8           = dword ptr  10h
.text:00401032                 mov     [ebp+var_4], 1
.text:00401039                 mov     [ebp+var_8], 2
.text:00401040                 mov     ebx, [ebp+arg_0]
.text:00401043                 mov     ecx, [ebp+arg_4]


评论

您的示例中使用的呼叫约定是Ghidra和IDA

默认值,Ghidra报告“ __stdcall”,我也认为IDA。在任何情况下,调用都是相当标准的,可以由工具识别,并带有RET 0xC

看到这个问题-github.com/NationalSecurityAgency/ghidra/issues/998

#1 楼

TL; DR:


这不是Ghidra的错误。这些值只是命名约定,真正的指令已正确反汇编。
Ghidra会根据函数入口点分配变量名称,并根据此值显示偏移量。
Ghidra的行为似乎类似于具有独立于编译器的通用分配名称的方法。


如R4444所指出的,Ghidra显示相对于entry stack-pointer的变量偏移量,而不是frame-based偏移量。

这里,Ghidra在输入函数时基于ESP(或相应的堆栈指针)分配变量名称,而无需考虑即将到来的PUSH EBP,基本上遵循以下步骤:

0019FF58  $-C      0019FF58        00000002     LOCAL 2
0019FF5C  $-8      0019FF5C        00000001     LOCAL 1
0019FF60  $-4      0019FF60        0019FF80     will store the OLD EBP
0019FF64  $ ==>    0019FF64      | 00401025     return to layout.00401025 from layout.sub_40102C
0019FF68  $+4      0019FF68      | 00000041     PARAM 1
0019FF6C  $+8      0019FF6C      | 0000BABE     PARAM 2
0019FF70  $+C      0019FF70      | 0000CAFE     PARAM 3


这就是Ghidra如何获取值的方式:

Stack[0x4]  -> param_1
Stack[0x8]  -> param_2
Stack[0xc]  -> param_3
Stack[-0x8] -> local_8
Stack[-0xc] -> local_c


必须将其视为变量命名,实际的指令是以正确的偏移量寻址数据。如果我们导航到其中一个令人反感的指令,则可以看到Ghidra在右下角提供了正确的指令,在这种情况下,对于命名变量EBP-4local_8),为[-0x8]



可以通过以下命令永久修改默认的Ghidra行为:Edit > Tool Options > Listing Fields > Operands Field > Markup Stack Variable References,然后Ghidra将显示:

     undefined4        Stack[0x4]:4   param_1                                 XREF[1]:     00401040 (R)   
     undefined4        Stack[0x8]:4   param_2                                 XREF[1]:     00401043 (R)   
     undefined4        Stack[0xc]:4   param_3                                 XREF[1]:     00401046 (R)   
     undefined4        Stack[-0x8]:4  local_8                                 XREF[1]:     00401032 (W)   
     undefined4        Stack[-0xc]:4  local_c                                 XREF[1]:     00401039 (W)   

00401032 C745FC01000000            MOV        dword ptr [EBP  + -0x4 ]=> local_8 ,0x1
00401039 C745F802000000            MOV        dword ptr [EBP  + -0x8 ]=> local_c ,0x2
00401040 8B5D08                    MOV        EBX ,dword ptr [EBP  + 0x8 ]=> param_1
00401043 8B4D0C                    MOV        ECX ,dword ptr [EBP  + 0xc ]=> param_2
00401046 FF7510                    PUSH       dword ptr [EBP  + 0x10 ]=> param_3


这是值不匹配和如何获得这些值,但是为什么Ghidra会根据函数条目来命名变量? @emteere解释说:


基于帧变量的堆栈变量偏移量与基于堆栈指针的堆栈变量偏移量的选择可能会引起混淆。它所允许的是忽略堆栈框架变量,而仅在它们发生的地方创建对堆栈的引用。有很多将堆栈指针加载到不带框架的备用寄存器中的示例,因此在两个不同功能中都存在和没有帧指针的情况下,在入口处使用通用栈基似乎是一个不错的选择,并且不会造成混淆。加载调试信息后,需要在入口处完成到SP的转换。另外,许多编译器都不再使用堆栈帧寄存器。


所以,我想我的解释是通常希望具有基于帧的变量至少在某些扩展性最强的体系结构/编译器中,就像在IDA中看到的那样命名。但是,Ghidra用通用策略命名变量,他们决定通过在输入函数时基于堆栈指针偏移变量来协调不同体系结构/编译器的行为。


来源:


ARM中怀疑不正确的FP堆栈变量Ghidra帮助:“函数签名,属性和变量”