diff --git a/doc/1.5.7_memory.md b/doc/1.5.7_memory.md index 05eda60..5d013c3 100644 --- a/doc/1.5.7_memory.md +++ b/doc/1.5.7_memory.md @@ -80,55 +80,55 @@ int main() { } ``` -使用 gdb 查看对应的汇编代码: +使用 gdb 查看对应的汇编代码,这里我们给出了详细的注释: ```text gdb-peda$ disassemble main Dump of assembler code for function main: - 0x00000563 <+0>: lea ecx,[esp+0x4] - 0x00000567 <+4>: and esp,0xfffffff0 - 0x0000056a <+7>: push DWORD PTR [ecx-0x4] - 0x0000056d <+10>: push ebp - 0x0000056e <+11>: mov ebp,esp - 0x00000570 <+13>: push ebx + 0x00000563 <+0>: lea ecx,[esp+0x4] ;将 esp+0x4 的地址传给 ecx + 0x00000567 <+4>: and esp,0xfffffff0 ;栈 16 字节对齐 + 0x0000056a <+7>: push DWORD PTR [ecx-0x4] ;ecx-0x4,即原 esp 强制转换为双字数据后压入栈中 + 0x0000056d <+10>: push ebp ;保存调用 main() 函数之前的 ebp,由于在 _start 中将 ebp 清零了,这里的 ebp=0x0 + 0x0000056e <+11>: mov ebp,esp ;把调用 main() 之前的 esp 作为当前栈帧的 ebp + 0x00000570 <+13>: push ebx ;ebx、ecx 入栈 0x00000571 <+14>: push ecx - 0x00000572 <+15>: sub esp,0x10 - 0x00000575 <+18>: call 0x440 <__x86.get_pc_thunk.bx> - 0x0000057a <+23>: add ebx,0x1a86 - 0x00000580 <+29>: mov DWORD PTR [ebp-0x10],0x1 - 0x00000587 <+36>: mov DWORD PTR [ebp-0xc],0x2 - 0x0000058e <+43>: push DWORD PTR [ebp-0xc] - 0x00000591 <+46>: push DWORD PTR [ebp-0x10] - 0x00000594 <+49>: call 0x53d + 0x00000572 <+15>: sub esp,0x10 ;为局部变量 a、b 分配空间并做到 16 字节对齐 + 0x00000575 <+18>: call 0x440 <__x86.get_pc_thunk.bx> ;调用 <__x86.get_pc_thunk.bx> 函数,将 esp 强制转换为双字数据后保存到 ebx + 0x0000057a <+23>: add ebx,0x1a86 ;ebx+0x1a86 + 0x00000580 <+29>: mov DWORD PTR [ebp-0x10],0x1 ;a 第二个入栈所以保存在 ebp-0x10 的位置,此句即 a=1 + 0x00000587 <+36>: mov DWORD PTR [ebp-0xc],0x2 ;b 第一个入栈所以保存在 ebp-0xc 的位置,此句即 b=2 + 0x0000058e <+43>: push DWORD PTR [ebp-0xc] ;将 b 压入栈中 + 0x00000591 <+46>: push DWORD PTR [ebp-0x10] ;将 a 压入栈中 + 0x00000594 <+49>: call 0x53d ;调用 add() 函数,返回值保存在 eax 中 0x00000599 <+54>: add esp,0x8 0x0000059c <+57>: sub esp,0x8 - 0x0000059f <+60>: push eax - 0x000005a0 <+61>: lea eax,[ebx-0x19b0] - 0x000005a6 <+67>: push eax - 0x000005a7 <+68>: call 0x3d0 - 0x000005ac <+73>: add esp,0x10 - 0x000005af <+76>: mov eax,0x0 - 0x000005b4 <+81>: lea esp,[ebp-0x8] - 0x000005b7 <+84>: pop ecx + 0x0000059f <+60>: push eax ;eax 入栈 + 0x000005a0 <+61>: lea eax,[ebx-0x19b0] ;ebx-0x19b0 的地址保存到 eax,该地址处保存字符串 "%d\n" + 0x000005a6 <+67>: push eax ;eax 入栈 + 0x000005a7 <+68>: call 0x3d0 ;调用 printf() 函数 + 0x000005ac <+73>: add esp,0x10 ;调整栈顶指针 esp,删除 add() 和 printf() 的参数 + 0x000005af <+76>: mov eax,0x0 ;eax=0x0 + 0x000005b4 <+81>: lea esp,[ebp-0x8] ;ebp-0x8 的地址保存到 esp + 0x000005b7 <+84>: pop ecx ;弹栈恢复 ecx、ebx、ebp 0x000005b8 <+85>: pop ebx 0x000005b9 <+86>: pop ebp - 0x000005ba <+87>: lea esp,[ecx-0x4] - 0x000005bd <+90>: ret + 0x000005ba <+87>: lea esp,[ecx-0x4] ;ecx-0x4 的地址保存到 esp + 0x000005bd <+90>: ret ;返回 End of assembler dump. gdb-peda$ disassemble add Dump of assembler code for function add: - 0x0000053d <+0>: push ebp - 0x0000053e <+1>: mov ebp,esp - 0x00000540 <+3>: sub esp,0x10 - 0x00000543 <+6>: call 0x5be <__x86.get_pc_thunk.ax> - 0x00000548 <+11>: add eax,0x1ab8 - 0x0000054d <+16>: mov eax,DWORD PTR [ebp+0x8] - 0x00000550 <+19>: mov DWORD PTR [ebp-0x8],eax - 0x00000553 <+22>: mov eax,DWORD PTR [ebp+0xc] - 0x00000556 <+25>: mov DWORD PTR [ebp-0x4],eax - 0x00000559 <+28>: mov edx,DWORD PTR [ebp-0x8] - 0x0000055c <+31>: mov eax,DWORD PTR [ebp-0x4] - 0x0000055f <+34>: add eax,edx - 0x00000561 <+36>: leave + 0x0000053d <+0>: push ebp ;保存调用 add() 函数之前的 ebp + 0x0000053e <+1>: mov ebp,esp ;把调用 add() 之前的 esp 作为当前栈帧的 ebp + 0x00000540 <+3>: sub esp,0x10 ;为局部变量 x、y 分配空间并做到 16 字节对齐 + 0x00000543 <+6>: call 0x5be <__x86.get_pc_thunk.ax> ;调用 <__x86.get_pc_thunk.ax> 函数,将 esp 强制转换为双字数据后保存到 eax + 0x00000548 <+11>: add eax,0x1ab8 ;eax+0x1ab8 + 0x0000054d <+16>: mov eax,DWORD PTR [ebp+0x8] ;将 ebp+0x8 的数据 0x1 传送到 eax,ebp+0x4 为函数返回地址 + 0x00000550 <+19>: mov DWORD PTR [ebp-0x8],eax ;保存 eax 的值 0x1 到 ebp-0x8 的位置 + 0x00000553 <+22>: mov eax,DWORD PTR [ebp+0xc] ;将 ebp+0xc 的数据 0x2 传送到 eax + 0x00000556 <+25>: mov DWORD PTR [ebp-0x4],eax ;保存 eax 的值 0x2 到 ebp-0x4 的位置 + 0x00000559 <+28>: mov edx,DWORD PTR [ebp-0x8] ;取出 ebp-0x8 的值 0x1 到 edx + 0x0000055c <+31>: mov eax,DWORD PTR [ebp-0x4] ;取出 ebp-0x4 的值 0x2 到 eax + 0x0000055f <+34>: add eax,edx ;eax+edx + 0x00000561 <+36>: leave ;返回,相当于 mov esp,ebp; pop ebp; 0x00000562 <+37>: ret End of assembler dump. ``` @@ -136,27 +136,27 @@ End of assembler dump. ```text gdb-peda$ disassemble _start Dump of assembler code for function _start: - 0x00000400 <+0>: xor ebp,ebp - 0x00000402 <+2>: pop esi - 0x00000403 <+3>: mov ecx,esp - 0x00000405 <+5>: and esp,0xfffffff0 - 0x00000408 <+8>: push eax + 0x00000400 <+0>: xor ebp,ebp ;清零 ebp,表示下面的 main() 函数栈帧中 ebp 保存的上一级 ebp 为 0x00000000 + 0x00000402 <+2>: pop esi ;弹栈设置 esi + 0x00000403 <+3>: mov ecx,esp ;暂时保存当前的 esp + 0x00000405 <+5>: and esp,0xfffffff0 ;栈 16 字节对齐 + 0x00000408 <+8>: push eax ;eax、esp、edx 入栈 0x00000409 <+9>: push esp 0x0000040a <+10>: push edx - 0x0000040b <+11>: call 0x432 <_start+50> - 0x00000410 <+16>: add ebx,0x1bf0 - 0x00000416 <+22>: lea eax,[ebx-0x19d0] + 0x0000040b <+11>: call 0x432 <_start+50> ;先将下一条指令地址 0x00000410 压栈,设置 esp 指向它,再调用 0x00000432 处的指令 + 0x00000410 <+16>: add ebx,0x1bf0 ;ebx+0x1bf0 + 0x00000416 <+22>: lea eax,[ebx-0x19d0] ;取 <__libc_csu_fini> 地址传给 eax,然后压栈 0x0000041c <+28>: push eax - 0x0000041d <+29>: lea eax,[ebx-0x1a30] - 0x00000423 <+35>: push eax - 0x00000424 <+36>: push ecx + 0x0000041d <+29>: lea eax,[ebx-0x1a30] ;取 <__libc_csu_init> 地址传入 eax,然后压栈 + 0x00000423 <+35>: push eax + 0x00000424 <+36>: push ecx ;ecx、esi 入栈保存 0x00000425 <+37>: push esi - 0x00000426 <+38>: push DWORD PTR [ebx-0x8] - 0x0000042c <+44>: call 0x3e0 <__libc_start_main@plt> - 0x00000431 <+49>: hlt - 0x00000432 <+50>: mov ebx,DWORD PTR [esp] - 0x00000435 <+53>: ret - 0x00000436 <+54>: xchg ax,ax + 0x00000426 <+38>: push DWORD PTR [ebx-0x8] ;调用 main() 函数之前保存返回地址,其实就是保存 main() 函数的入口地址 + 0x0000042c <+44>: call 0x3e0 <__libc_start_main@plt> ;call 指令调用 main() 函数 + 0x00000431 <+49>: hlt ;hlt 指令使程序停止运行,处理器进入暂停状态,不执行任何操作,不影响标志。当 RESET 线上有复位信号、CPU 响应非屏蔽终端、CPU 响应可屏蔽终端 3 种情况之一时,CPU 脱离暂停状态,执行下一条指令 + 0x00000432 <+50>: mov ebx,DWORD PTR [esp] ;esp 强制转换为双字数据后保存到 ebx + 0x00000435 <+53>: ret ;返回 + 0x00000436 <+54>: xchg ax,ax ;交换 ax 和 ax 的数据,相当于 nop 0x00000438 <+56>: xchg ax,ax 0x0000043a <+58>: xchg ax,ax 0x0000043c <+60>: xchg ax,ax @@ -164,7 +164,21 @@ Dump of assembler code for function _start: End of assembler dump. ``` -#### 调用约定 +#### 函数调用约定 +函数调用约定是对函数调用时如何传递参数的一种约定。调用函数前要先把参数压入栈然后再传递给函数。 + +一个调用约定大概有如下的内容: +- 函数参数的传递顺序和方式 +- 栈的维护方式 +- 名字修饰的策略 + +主要的函数调用约定如下,其中 cdecl 是 C 语言默认的调用约定: + +调用约定 | 出栈方 | 参数传递 | 名字修饰 +--- | --- | --- | --- +cdecl | 函数调用方 | 从右到左的顺序压参数入栈 | 下划线+函数名 +stdcall | 函数本身 | 从右到左的顺序压参数入栈 | 下划线+函数名+@+参数的字节数 +fastcall | 函数本身 | 都两个 DWORD(4 字节)类型或者占更少字节的参数被放入寄存器,其他剩下的参数按从右到左的顺序压入栈 | @+函数名+@+参数的字节数 ## 堆与内存管理