Last Regrets

久违的继续学汇编

· sdttttt

上次学汇编都是2年前了好像,学了一些基本的指令用法。

这次像重新捡起来了,同时想要自己写点新的玩意,目前的知识不太够呢

慢慢学吧…


这次开始读汇编和画栈结构吧~

先从简单的开始:

int add(int a, int b) {
	return a + b;
}

int main()
{
	int a = add(1, 2);
	return 0;
}

汇编如下(Windows平台):

; int main()
; {

; 这块应该是上下文的保存工作,后面运行完后要弹出来恢复上下文
00007FF7533A18D0  push        rbp  
00007FF7533A18D2  push        rdi  

|rdi < rsp
|rbp
|... < rbp

; rsp 是X64下的栈顶指针寄存器,
; 这里sub,也就是向上移动,应该是开辟栈用
00007FF7533A18D3  sub         rsp,108h
; 这里 rbp是栈底
; 相加是向下移动,rbp是在rsp+20h的位置也就是128h
00007FF7533A18DA  lea         rbp,[rsp+20h]

|NULL < rsp [108h]
|...
|...
|NULL < rbp [128h]
|...
|...
|...
|rdi
|rbp 

; rcx是计数器寄存器,循环用的
; 下面这两段看起来是VS的调试代码,当作没有
; 00007FF7533A18DF  lea         rcx,[__F936B9EA_ConsoleApplication1@cpp (07FF7533B3069h)] 
; 00007FF7533A18E6  call        __CheckForDebuggerJustMyCode (07FF7533A13E3h)  
00007FF7533A18DF  lea         rcx,[00007FF7533B3069h]  
00007FF7533A18E6  call        00007FF7533A13E3  
00007FF7533A18EB  nop  

;	int a = add(1, 2);
; edx, ecx,32位用寄存器,这里用来传参
00007FF7533A18EC  mov         edx,2  
00007FF7533A18F1  mov         ecx,1  

; call 调用的就是add函数的地址, 调用call会压栈,并且直接jmp到对应指令地址
00007FF7533A18F6  call        00007FF7533A144C

|add return address < rsp 
|NULL [108h]
|...
|...
|NULL < rbp [128h] main function
|...
|...
|...
|rdi
|rbp 

; 这里把eax的值写道rbp+4这个内存里了,eax应该就是返回值
00007FF7533A18FB  mov         dword ptr [rbp+4],eax

|NULL [108h] < rsp
|...
|[rbp + 4] = eax 返回值
|...
|NULL < rbp [128h] main function
|...
|...
|...
|rdi
|rbp 
 
;	return 0;
00007FF7533A18FE  xor         eax,eax  
; }

; 恢复上下文
; 下面再说
00007FF7533A1900  lea         rsp,[rbp+00000000000000E8h]  
00007FF7533A1907  pop         rdi  
00007FF7533A1908  pop         rbp  
00007FF7533A1909  ret

; 最开始两句就是取出两个参数,a和b
; 并且把这两个参数放在rsp+10h的位置和rsp+8的位置
; int add(int a, int b) {
00007FF7533A1D80  mov         dword ptr [rsp+10h],edx  
00007FF7533A1D84  mov         dword ptr [rsp+8],ecx  

|ecx value [rsp+8]
|edx value [rsp+10h]
|...
|add return address < rsp 
|NULL [108h]
|...
|...
|NULL < rbp [128h] main function
|...
|...
|...
|rdi
|rbp 

; 这里就不分析了,每个函数运行之前都有保存上下文和开辟栈帧的操作
; 必须保证某些寄存器的值在函数调用前和调用后一致
; 不然就会栈错误
00007FF7533A1D88  push        rbp  
00007FF7533A1D89  push        rdi  
00007FF7533A1D8A  sub         rsp,0E8h  
00007FF7533A1D91  lea         rbp,[rsp+20h]  
00007FF7533A1D96  lea         rcx,[__F936B9EA_ConsoleApplication1@cpp (07FF7533B3069h)]  
00007FF7533A1D9D  call        __CheckForDebuggerJustMyCode (07FF7533A13E3h)  
00007FF7533A1DA2  nop  

|NULL <  rsp[0E8h] 
|...
|NULL < rbp [rsp + 20h] add function
|...
|...
|...
|rdi
|rbp
|ecx value [rsp+8]
|edx value [rsp+10h]
|...
|add return address
|NULL [108h]
|...
|...
|NULL < rbp [128h] main function
|...
|...
|...
|rdi 上一个
|rbp 上一个

; 这个也不看了,两个寄存器加起来,结果放在eax里
; 	return a + b;
00007FF7533A1DA3  mov         eax,dword ptr [b]  
00007FF7533A1DA9  mov         ecx,dword ptr [a]  
00007FF7533A1DAF  add         ecx,eax  
00007FF7533A1DB1  mov         eax,ecx  
; }

; 恢复上下文
; 首先是 将栈顶指针重新指向栈底,至于这里为什么做了+0C8h的偏移,我也不知道
; 随后弹出rdi和rbp,最后返回,弹出add return address直接jmp
00007FF7533A1DB3  lea         rsp,[rbp+0C8h]  
00007FF7533A1DBA  pop         rdi  
00007FF7533A1DBB  pop         rbp  
00007FF7533A1DBC  ret