Ghidra 错误地解释堆栈指针

逆向工程 吉德拉 堆栈变量
2021-06-17 08:25:45

为什么 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

吉德拉获得:

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]
1个回答

特尔;博士:

  • 这不是 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-4为命名变量local_8( [-0x8]):

相对于入口的 Ghidra 堆栈指针

这个默认的 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 解释说:

基于帧变量的堆栈变量偏移量与基于堆栈指针的堆栈变量的选择可能会引起一些混淆。它允许忽略堆栈帧变量,并在它们出现的任何地方创建对堆栈的引用。有许多堆栈指针加载到备用寄存器而没有 frame 的例子,因此入口处的通用堆栈基址似乎是一个不错的选择,并且在两个不同的函数中存在和不存在帧指针时不会造成混淆。加载调试信息时,需要在入口处转换为 SP。此外,许多编译器已经完全摆脱了堆栈帧寄存器的使用。

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


资料来源: