上次学汇编都是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