IDA 技巧(65) 栈帧视图

admin 2024年11月2日09:27:33评论15 views字数 4097阅读13分39秒阅读模式

栈帧是由当前函数管理的栈的一部分,包含该函数使用的数据。

背景

栈帧通常包含以下数据:

  • 局部和临时变量;
  • 传入参数(对于使用栈传递参数的调用约定);
  • 保存的易失性寄存器;
  • 其他记录信息(例如x86上的返回地址)。

由于栈在执行过程中可能会不可预测地变化,栈帧及其部分没有固定地址。因此,IDA使用伪结构来表示其布局。这个结构与结构视图中的其他结构非常相似,但有一些区别:

  1. 帧结构没有名称,不包含在全局结构列表中;只能从相应的函数中访问。
  2. 显示的是从帧指针的偏移(正负都有),而不是从结构开始的偏移。
  3. 它可能包含特殊成员来表示保存的返回地址和/或保存的寄存器区域。

栈帧视图

要打开栈帧视图:

  • 编辑 > 函数 > 栈变量… 或在反汇编(IDA视图)中定位到一个函数时按 CtrlK
  • 在反汇编或伪代码中双击或按 Enter 键选择一个栈变量。

在此视图中,您可以执行与结构视图中大多数相同的操作:

  1. 定义新的或更改现有的栈变量(D);
  2. 重命名变量(N);
  3. 创建数组(*)或结构实例(AltQ)。

示例

考虑这个有漏洞的程序:

#include <stdio.h>
int main () {
    char username[8];
    int allow = 0;
    printf("Enter your username, please: ");
    gets(username); // 用户输入“malicious”
    if (grantAccess(username)) {
        allow = 1;
    }
    if (allow != 0) { // 被用户名的溢出覆盖。
        privilegedAction();
        return 0;
    }
}

当由旧版GCC编译时,可能会生成以下汇编:

.text:0000000000400580 main proc near; DATA XREF: _start+1D↑o
.text:0000000000400580
.text:0000000000400580 var_10= byte ptr -10h
.text:0000000000400580 var_4= dword ptr -4
.text:0000000000400580
.text:0000000000400580 ; __unwind {
.text:0000000000400580 push rbp
.text:0000000000400581 mov rbp, rsp
.text:0000000000400584 sub rsp, 10h
.text:0000000000400588 mov [rbp+var_4], 0
.text:000000000040058F mov edi, offset format; "Enter your username, please: "
.text:0000000000400594 mov eax, 0
.text:0000000000400599 call printf
.text:000000000040059E lea rax, [rbp+var_10]
.text:00000000004005A2 mov rdi, rax
.text:00000000004005A5 call gets
.text:00000000004005AA lea rax, [rbp+var_10]
.text:00000000004005AE mov rdi, rax
.text:00000000004005B1 call grantAccess
.text:00000000004005B6 test eax, eax
.text:00000000004005B8 jz short loc_4005C1
.text:00000000004005BA mov [rbp+var_4], 1
.text:00000000004005C1
.text:00000000004005C1 loc_4005C1: ; CODE XREF: main+38↑j
.text:00000000004005C1 cmp [rbp+var_4], 0
.text:00000000004005C5 jz short loc_4005D1
.text:00000000004005C7 mov eax, 0
.text:00000000004005CC call privilegedAction
.text:00000000004005D1
.text:00000000004005D1 loc_4005D1: ; CODE XREF: main+45↑j
.text:00000000004005D1 mov eax, 0
.text:00000000004005D6 leave
.text:00000000004005D7 retn
.text:00000000004005D7 ; } // starts at 400580
.text:00000000004005D7 main endp

打开栈帧后,我们可以看到以下图像:

IDA 技巧(65) 栈帧视图
栈帧1

通过比较源代码和反汇编,我们可以推断出var_10usernamevar_4allow。由于代码只获取缓冲区的起始地址,IDA无法检测其完整大小并创建了一个单字节变量。为了改进它,按*键将var_10转换为8字节数组。我们还可以将变量重命名为其正确名称。

IDA 技巧(65) 栈帧视图
栈帧2

由于IDA以自然内存顺序显示栈帧布局(地址向下增加),我们可以立即看到漏洞代码演示的问题:gets函数没有边界检查,因此输入长字符串可以溢出username缓冲区并覆盖allow变量。由于代码只检查非零值,这将绕过检查并导致执行privilegedAction函数。

帧偏移和栈变量

如上所述,在栈帧视图中,结构偏移是相对于帧指针显示的。在某些情况下,如上例中,它是一个实际的处理器寄存器(RBP)。例如,变量allow位于距帧指针-4的偏移处,IDA在反汇编列表中使用此值作为符号名称而不是原始数值偏移:

.text:0000000000400580 allow= dword ptr -4
[...]
.text:0000000000400588 mov [rbp+allow], 0
[...]

通过按#K键,您可以要求IDA显示指令的原始形式:

.text:0000000000400588 mov dword ptr [rbp-4], 0

再次按K键返回到栈变量表示。

在其他情况下,帧指针可以只是一个用于方便的任意位置(通常是函数入口处栈指针值的固定偏移)。这在使用帧指针省略编译的二进制文件中很常见,这是一种常见的优化技术。在这种情况下,IDA可能会使用额外的增量来补偿函数不同部分中栈指针的变化。例如,考虑这个函数:

.text:10001030 sub_10001030 proc near; DATA XREF: sub_100010B0:loc_100010E7↓o
.text:10001030
.text:10001030 LCData= byte ptr -0Ch
.text:10001030 var_4= dword ptr -4
.text:10001030
.text:10001030 sub esp, 0Ch
.text:10001033 mov eax, dword_100B2960
.text:10001038 push esi
.text:10001039 mov [esp+10h+var_4], eax
.text:1000103D xor esi, esi
.text:1000103F call ds:GetThreadLocale
.text:10001045 push 7 ; cchData
.text:10001047 lea ecx, [esp+14h+LCData]
.text:1000104B push ecx ; lpLCData
.text:1000104C push 1004h ; LCType
.text:10001051 push eax ; Locale
.text:10001052 call ds:GetLocaleInfoA
.text:10001058 test eax, eax
.text:1000105A jz short loc_1000107D
.text:1000105C mov al, [esp+10h+LCData]
.text:10001060 test al, al
.text:10001062 lea ecx, [esp+10h+LCData]
.text:10001066 jz short loc_1000107D

在这里,显式帧指针(ebp)未使用,IDA安排栈帧以便返回地址放置在偏移0处:

-00000010 ; Frame size: 10; Saved regs: 0; Purge: 0
-00000010 ;
-00000010
-00000010 db ? ; undefined
-0000000F db ? ; undefined
-0000000E db ? ; undefined
-0000000D db ? ; undefined
-0000000C LCData db ?
-0000000B db ? ; undefined
-0000000A db ? ; undefined
-00000009 db ? ; undefined
-00000008 db ? ; undefined
-00000007 db ? ; undefined
-00000006 db ? ; undefined
-00000005 db ? ; undefined
-00000004 var_4 dd ?
+00000000 rdb 4 dup(?)
+00000004
+00000004 ; end of stack variables

为了补偿栈指针的变化(sub esp, 0Chpush指令),在栈变量操作数中需要添加值10h14h。因此,我们可以很容易地看到100010471000105C处的指令引用相同的变量,即使在原始形式中它们使用不同的偏移([esp+8][esp+4])。

更多文章

立即关注【二进制磨剑】公众号

👉👉👉【IDA 技巧合集】👈👈👈
👉👉👉【Github 安全项目合集】👈👈👈
学习零基础学习 IDA 逆向
【课程完结!内容揭秘!】7 天打造 IDA 9.0 大师:从零基础到逆向精英
知识星球🪐

IDA 技巧(65) 栈帧视图

原文始发于微信公众号(二进制磨剑):IDA 技巧(65) 栈帧视图

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月2日09:27:33
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   IDA 技巧(65) 栈帧视图https://cn-sec.com/archives/3346593.html

发表评论

匿名网友 填写信息