【发布时间】:2015-09-22 16:39:08
【问题描述】:
在玩弄内存以更好地了解进程内存布局和幕后的一般情况时,我未能完全理解它。想象一下下面的代码:
#include <stdio.h>
#include <string.h>
int main(int argc,char **argv) {
char buf[32];
strcpy(buf,argv[1]);
return 0;
}
从IDA 转储(dec 不是hex):
已添加
var_30= dword ptr -30h
var_2C= dword ptr -2Ch
var_20= dword ptr -20h
arg_4= dword ptr 0Ch
结束
push ebp
mov ebp, esp
and esp, 4294967280
sub esp, 48
call sub_401920
mov eax, [ebp+12]
add eax, 4
mov eax, [eax]
mov [esp+4], eax
lea eax, [esp+16]
mov [esp], eax
call strcpy
mov eax, 0
leave
retn
我的解释:
- 1) 将
EBP推入堆栈 - 2) 将
ESP与EBP对齐 - 3)
and esp, 4294967280编译器模式可能可以忽略 (?) - 4) 从
ESP中减去 48 个字节,分配 48 个字节的大小 - N) 我使用的编译器按 16 字节的块分配内存效率低下,即如果你有一个整数,它将分配 16 个字节,如果你超过 16,它将使用 32、48、64 等等
- 5) 调用 (3) 中与编译器模式相关的函数可能会被忽略 (?)
由于EBP+0x0-0x3 存储了EBP 指针和EBP+0x4-0x7 返回地址,我们可以在这里看到发生了什么。
- 1) 将指针
argv移动到EAX - 2) 将 4 个字节添加到
EAX(现在指向EBP+12+4) - 3) 将指针
EBP+12+4移动到EAX中 - N)
EBP+12+4将等于argv[1] - 4) 将
argv[1]的指针移动到堆栈ESP+4 - 5) ???
- 6) 将
buf[32]的内容存储在 ESP+0 (?) 上
这个问题,尽管如果得到回答,非常感谢,但与其说是ASM,不如说是:
据我了解,此函数的堆栈框架应如下所示:
[ ] < ESP+0x0-0x3
[ ]
[ ]
[ ]
[ ]
[ ]
[ ]
[ ]
[ ]
[ ]
[ ]
[ ] < ESP+0x2C-0x2F
[EBP] < EBP+0x0-0x3
[RET] < EBP+0x4-0x7
[ARG]
其中ARG (EBP+0x8+) 包含函数的参数。
混乱
当我使用 44 字节数据
A作为用户输入时,它导致堆栈溢出,而堆栈上的argv[1]指针只有 4 个其他字节,那么单个字节来自哪里? /p>EBP是 4 个字节,因为它是一个指针,但是当我使用 45 个字节的数据时,整个EBP已经被 A 覆盖了。EIP(afaik) 由覆盖EBP+0x3-0x7(RET) 控制,大小也是 4 个字节。但是,46 字节的数据导致EIP中途被 A 重写,47 字节 3/4 和 48 字节被 A 完全覆盖EIP
最后,发送一个太大的缓冲区EIP 时有什么理由不再重写?是因为它开始覆盖之前的堆栈帧导致更早的速成课程吗?
【问题讨论】:
-
这看起来是一种非常糟糕的分析编译器行为的方法。只需让编译器发出带有符号和标签的程序集(例如
gcc -S)。您显示的一半代码是主前设置。 -
@KerrekSB 我不想分析编译器行为。我正在尝试分析进程内存布局和堆栈行为。
-
认为什么决定了进程的内存布局和堆栈行为?您查看的汇编代码是由编译器生成的。理解代码意味着理解编译器生成该代码的原因。你正在做的是试图分析编译器的行为。无论如何,您所问的问题都不清楚。您不恰当地使用寄存器名称来引用堆栈上的位置并没有帮助。
-
IDA 有一个调试器,您可以使用它来单步执行汇编指令并观察它们对堆栈和寄存器的影响。这样做,很多问题都会得到解答。
-
EIP 是一个寄存器,除了通过程序控制指令(JMP、CALL、RET 等)和执行指令后程序计数器的正常前进外,不能更改。它不能“从另一个 1 字节”重写。您的意思显然不是 EIP,而是返回地址存储在堆栈上的内存位置。假设这是真的,那么仍然留下“从另一个 1 字节”是什么意思的问题?还有什么字节?我可以继续这样指出你的问题不清楚的地方,但我的评论空间已经用完了。
标签: c pointers memory assembly stack