一.从寻址方式的角度看函数的调用
提出问题:
- D3 FF FF FF和07E1080h有什么关系?
C++
int z = Add(1, 2);
汇编代码
007E10A4 push 2
007E10A6 push 1
007E10A8 call Add (07E1080h)
007E10AD add esp,8
007E10B0 mov dword ptr [z],eax
机器码
6A 02
6A 01
E8 D3 FF FF FF
83 C4 08
89 45 FC
猜测:D3 FF FF FF是具有符号的偏移量
实证:计算器
D3 FF FF FF
11111111111111111111111111010011
00000000000000000000000000101100 0x2C
00000000000000000000000000101101 0x2D
00C110ADh - 0C11080h = 2D
发现规律:
- call指的偏移量寻址计算公式:偏移量 = 转跳到的地址 - call指令后一条指令的起始地址
二.从指令的效果看的函数的调用
CALL指令相当于push指令和jmp指令的组合
push 返回地址
jmp 函数入口地址
三.从参数的传递和获取参数的方式看函数调用
C++
int Add(int x, int y) {
int num = x + y;
return num;
}
汇编语言
00FC17C0 push ebp
00FC17C1 mov ebp,esp
00FC17C3 sub esp,0CCh //分配局部变量
00FC17C9 push ebx
00FC17CA push esi
00FC17CB push edi
00FC17CC lea edi,[ebp+FFFFFF34h]
00FC17D2 mov ecx,33h
00FC17D7 mov eax,0CCCCCCCCh
00FC17DC rep stos dword ptr es:[edi]
00FC17DE mov ecx,0FCC02Ch
00FC17E3 call 00FC1339
00FC17E8 mov eax,dword ptr [ebp+8]
00FC17EB add eax,dword ptr [ebp+0Ch]
00FC17EE mov dword ptr [ebp-8],eax
00FC17F1 mov eax,dword ptr [ebp-8]
00FC17F4 pop edi
00FC17F5 pop esi
00FC17F6 pop ebx
00FC17F7 add esp,0CCh
00FC17FD cmp ebp,esp
00FC17FF call 00FC1258
00FC1804 mov esp,ebp //局部变量不存在了
00FC1806 pop ebp
00FC1807 ret
提出问题:
- 怎么获取到栈上存储的参数1和2呢?
- 函数是通过哪一条指令让局部变量消失的呢?
- 这个函数从传递参数开始一共执行了多少条连续的push指令呢?
猜测:通过寄存器保存esp里面的地址,然后寄存器+偏移量去访问实参
实证: 函数开头一定有以下指令
push ebp
mov ebp,esp
...
pop ebp
发现规律:
- 参数的传递是通过push 参数1传递,参数传递时,栈顶地址会不断减少
- 获取参数的方式是通过**[ebp+偏移量]的方式去访问参数**,通过**[ebp-偏移量]的方式访问局部变量**
- 函数里面的局部变量在执行完 } 后就不存在了,不能再使用了
四.返回值
但返回值小于或等于4字节时,通过寄存器存储返回值
mov eax,dword ptr [ebp-8]
提出问题:当返回值超过4字节如何存储返回值呢?
设计实验:
C++代码
struct myrd
{
int i1;
int i2;
};
myrd myfunc() {
myrd r1;
r1.i1 = 1;
r1.i2 = 2;
return r1;
};
int main()
{
myrd r;
r = myfunc();
}
汇编代码