mas*_*wok 12 linux x86 assembly gdb disassembly
Heyo,
我已经编写了这个非常基本的主要功能来试验反汇编,还可以看到并希望了解更低层次的情况:
int main() {
return 6;
}
Run Code Online (Sandbox Code Playgroud)
使用gdb来disas main产生这个:
0x08048374 <main+0>: lea 0x4(%esp),%ecx
0x08048378 <main+4>: and $0xfffffff0,%esp
0x0804837b <main+7>: pushl -0x4(%ecx)
0x0804837e <main+10>: push %ebp
0x0804837f <main+11>: mov %esp,%ebp
0x08048381 <main+13>: push %ecx
0x08048382 <main+14>: mov $0x6,%eax
0x08048387 <main+19>: pop %ecx
0x08048388 <main+20>: pop %ebp
0x08048389 <main+21>: lea -0x4(%ecx),%esp
0x0804838c <main+24>: ret
Run Code Online (Sandbox Code Playgroud)
这是我最好的猜测,我认为正在发生什么以及我需要逐行帮助:
lea 0x4(%esp),%ecx
将esp + 4的地址加载到ecx中.为什么我们为esp添加4?
我在某处读到这是命令行参数的地址.但是当我这样做时,x/d $ecx我得到了argc的值.存储的实际命令行参数值在哪里?
and $0xfffffff0,%esp
对齐堆栈
pushl -0x4(%ecx)
将esp最初的地址推入堆栈.这样做的目的是什么?
push %ebp
将基指针推入堆栈
mov %esp,%ebp
将当前堆栈指针移动到基指针中
push %ecx
将原始esp + 4的地址按到堆栈.为什么?
mov $0x6,%eax
我想在这里返回6所以我猜测返回值存储在eax中?
pop %ecx
将ecx还原到堆栈上的值.当我们返回时,为什么我们要ecx为esp + 4?
pop %ebp
将ebp恢复为堆栈上的值
lea -0x4(%ecx),%esp
将esp恢复为原始值
ret
在装配时我是一个n00b所以任何帮助都会很棒!此外,如果你看到任何关于我认为发生的事情的错误陈述,请纠正我.
谢谢你!:]
Sas*_*asQ 12
函数体开头的代码:
push %ebp
mov %esp, %ebp
Run Code Online (Sandbox Code Playgroud)
是创建所谓的堆栈帧,这是一个"坚实的基础",用于引用过程本地的参数和对象.使用%ebp寄存器(如其名称所示)作为基指针,指向过程内本地堆栈的基(或底).
进入程序后,堆栈指针寄存器(%esp)通过调用指令指向存储在堆栈中的返回地址(它是紧接在调用之后的指令的地址).如果你ret现在只是调用,这个地址将从堆栈弹出到%eip(指令指针),代码将从该地址(后面的下一条指令call)继续执行.但是我们还没有回来,对吗?;-)
然后按下%ebp寄存器以保存其先前的值并且不会丢失它,因为您将很快使用它.(顺便说一句,它通常包含调用函数的基指针,当你查看该值时,你会发现一个先前存储的%ebp,这将再次是一个更高级别的函数的基指针,因此你可以跟踪调用堆栈那样.)当你保存时%ebp,你可以在%esp那里存储当前(堆栈指针),这样%ebp就会指向同一个地址:当前本地堆栈的基础.该%esp会来回移动时,你会推栈上弹出值或保留和释放局部变量的过程中.但是%ebp会保持固定,仍然指向本地堆栈帧的基础.
由调用者传递给过程的参数"只是在地面上埋葬"(也就是说,它们相对于基础具有正偏移,因为堆栈向下增长).你有%ebp本地堆栈的基地址,其中的前一个值是%ebp.在它下面(也就是说,它4(%ebp)位于返回地址.所以第一个参数是at 8(%ebp),第二个是at 12(%ebp),依此类推.
局部变量可以在基础上方的堆栈上分配(也就是说,它们相对于基础具有负偏移).只需将N减去%esp并且您刚刚N在堆栈上为局部变量分配了字节,方法是将堆栈的顶部移动到该区域的上方(或者,精确地,下方):-)您可以通过相对于负区域的负偏移来引用该区域.%ebp,即-4(%ebp)是第一个单词,-8(%ebp)是第二个等等.请记住,(%ebp)指向本地堆栈的基础,其中%ebp保存了先前的值.因此,请记住在尝试在过程结束时恢复%ebp通过之前将堆栈还原到先前的位置pop %ebp.你可以这样做两种方式:
1.您可以通过添加回免费的只是局部变量N的%esp(堆栈指针),也就是,移动堆栈的顶部,如果这些局部变量从未去过那儿.(好吧,他们的价值会留在堆栈上,但是他们会被认为是"被释放",并且可能被后续的推动所覆盖,所以引用它们已经不再安全了.它们是尸体; -J)
2.你可以通过简单的恢复倒在地上,并释放所有局部空间刷新堆栈%esp从%ebp已经较早堆栈的基本固定.它会将堆栈指针恢复到刚刚进入过程并保存%esp到的状态%ebp.就像加载以前保存的游戏一样,当你弄乱了某些东西;-)
gcc -S通过添加开关可以使组装更简洁-fomit-frame-pointer.它告诉GCC不要组装任何用于设置/重置堆栈帧的代码,直到确实需要它为止.请记住,它可能会混淆调试器,因为它们通常依赖于那里的堆栈帧来跟踪调用堆栈.但是如果你不需要调试这个二进制文件,它就不会破坏任何东西.这对于发布目标来说非常好,它可以节省一些时空.
有时您可以从.cfi交错的函数头开始遇到一些奇怪的汇编程序指令.这是所谓的呼叫帧信息.调试器使用它来跟踪函数调用.但它也用于高级语言中的异常处理,这需要堆栈展开和其他基于调用堆栈的操作.您也可以通过添加开关在装配中将其关闭-fno-dwarf2-cfi-asm.这告诉GCC使用普通旧标签而不是那些奇怪的.cfi指令,并在程序集的末尾添加一个特殊的数据结构,引用这些标签.这不会关闭CFI,只需将格式更改为"透明"格式:然后程序员可以看到CFI表.