上次學彙編都是2年前了好像,學了一些基本的指令用法。

這次像重新撿起來了,同時想要自己寫點新的玩意,目前的知識不太夠呢

慢慢學吧…


這次開始讀彙編和畫棧結構吧~

先從簡單的開始:

1int add(int a, int b) {
2	return a + b;
3}
4
5int main()
6{
7	int a = add(1, 2);
8	return 0;
9}

彙編如下(Windows平臺):

  1; int main()
  2; {
  3
  4; 這塊應該是上下文的保存工作,後面運行完後要彈出來恢復上下文
  500007FF7533A18D0  push        rbp
  600007FF7533A18D2  push        rdi
  7
  8|rdi < rsp
  9|rbp
 10|... < rbp
 11
 12; rsp 是X64下的棧頂指針寄存器,
 13; 這裡sub,也就是向上移動,應該是開闢棧用
 1400007FF7533A18D3  sub         rsp,108h
 15; 這裡 rbp是棧底
 16; 相加是向下移動,rbp是在rsp+20h的位置也就是128h
 1700007FF7533A18DA  lea         rbp,[rsp+20h]
 18
 19|NULL < rsp [108h]
 20|...
 21|...
 22|NULL < rbp [128h]
 23|...
 24|...
 25|...
 26|rdi
 27|rbp
 28
 29; rcx是計數器寄存器,循環用的
 30; 下面這兩段看起來是VS的調試代碼,當作沒有
 31; 00007FF7533A18DF  lea         rcx,[__F936B9EA_ConsoleApplication1@cpp (07FF7533B3069h)]
 32; 00007FF7533A18E6  call        __CheckForDebuggerJustMyCode (07FF7533A13E3h)
 3300007FF7533A18DF  lea         rcx,[00007FF7533B3069h]
 3400007FF7533A18E6  call        00007FF7533A13E3
 3500007FF7533A18EB  nop
 36
 37;	int a = add(1, 2);
 38; edx, ecx,32位用寄存器,這裡用來傳參
 3900007FF7533A18EC  mov         edx,2
 4000007FF7533A18F1  mov         ecx,1
 41
 42; call 調用的就是add函數的地址, 調用call會壓棧,並且直接jmp到對應指令地址
 4300007FF7533A18F6  call        00007FF7533A144C
 44
 45|add return address < rsp
 46|NULL [108h]
 47|...
 48|...
 49|NULL < rbp [128h] main function
 50|...
 51|...
 52|...
 53|rdi
 54|rbp
 55
 56; 這裡把eax的值寫道rbp+4這個內存裡了,eax應該就是返回值
 5700007FF7533A18FB  mov         dword ptr [rbp+4],eax
 58
 59|NULL [108h] < rsp
 60|...
 61|[rbp + 4] = eax 返回值
 62|...
 63|NULL < rbp [128h] main function
 64|...
 65|...
 66|...
 67|rdi
 68|rbp
 69
 70;	return 0;
 7100007FF7533A18FE  xor         eax,eax
 72; }
 73
 74; 恢復上下文
 75; 下面再說
 7600007FF7533A1900  lea         rsp,[rbp+00000000000000E8h]
 7700007FF7533A1907  pop         rdi
 7800007FF7533A1908  pop         rbp
 7900007FF7533A1909  ret
 80
 81; 最開始兩句就是取出兩個參數,a和b
 82; 並且把這兩個參數放在rsp+10h的位置和rsp+8的位置
 83; int add(int a, int b) {
 8400007FF7533A1D80  mov         dword ptr [rsp+10h],edx
 8500007FF7533A1D84  mov         dword ptr [rsp+8],ecx
 86
 87|ecx value [rsp+8]
 88|edx value [rsp+10h]
 89|...
 90|add return address < rsp
 91|NULL [108h]
 92|...
 93|...
 94|NULL < rbp [128h] main function
 95|...
 96|...
 97|...
 98|rdi
 99|rbp
100
101; 這裡就不分析了,每個函數運行之前都有保存上下文和開闢棧幀的操作
102; 必須保證某些寄存器的值在函數調用前和調用後一致
103; 不然就會棧錯誤
10400007FF7533A1D88  push        rbp
10500007FF7533A1D89  push        rdi
10600007FF7533A1D8A  sub         rsp,0E8h
10700007FF7533A1D91  lea         rbp,[rsp+20h]
10800007FF7533A1D96  lea         rcx,[__F936B9EA_ConsoleApplication1@cpp (07FF7533B3069h)]
10900007FF7533A1D9D  call        __CheckForDebuggerJustMyCode (07FF7533A13E3h)
11000007FF7533A1DA2  nop
111
112|NULL <  rsp[0E8h]
113|...
114|NULL < rbp [rsp + 20h] add function
115|...
116|...
117|...
118|rdi
119|rbp
120|ecx value [rsp+8]
121|edx value [rsp+10h]
122|...
123|add return address
124|NULL [108h]
125|...
126|...
127|NULL < rbp [128h] main function
128|...
129|...
130|...
131|rdi 上一個
132|rbp 上一個
133
134; 這個也不看了,兩個寄存器加起來,結果放在eax裡
135; 	return a + b;
13600007FF7533A1DA3  mov         eax,dword ptr [b]
13700007FF7533A1DA9  mov         ecx,dword ptr [a]
13800007FF7533A1DAF  add         ecx,eax
13900007FF7533A1DB1  mov         eax,ecx
140; }
141
142; 恢復上下文
143; 首先是 將棧頂指針重新指向棧底,至於這裡為什麼做了+0C8h的偏移,我也不知道
144; 隨後彈出rdi和rbp,最後返回,彈出add return address直接jmp
14500007FF7533A1DB3  lea         rsp,[rbp+0C8h]
14600007FF7533A1DBA  pop         rdi
14700007FF7533A1DBB  pop         rbp
14800007FF7533A1DBC  ret