0.寄存器
4个数据寄存器:EAX、EBX、ECX、EDX
在用途方面,它们有各自默认的用途:
EAX - 用来保存函数的返回值;
ECX - 用来存放this指针。
2个指针寄存器:EBP、ESP
EBP - 基址指针寄存器,作为堆栈数据存取操作的基本地址指针寄存器;
ESP - 栈指针寄存器,指示堆栈的当前偏移地址(堆栈顶部到堆栈起始位置的距离)。
2个变址寄存器:ESI、EDI
变址寄存器主要用于存放存储单元在段内的偏移量,用它们可实现多种存储器操作数的寻址方式,
为以不同的地址形式访问存储单元提供方便。 ESI - 源变址寄存器; EDI - 目标变址寄存器
6个段寄存器:ES、CS、SS、DS、FS和GS
CS - 代码段寄存器(Code Segment Register),存放当前正在执行代码段的起始地址;
DS - 数据段寄存器(Data Segment Register),存放当前正在执行程序所用数据段的起始地址;
SS - 堆栈段寄存器(Stack Segment Register),存放当前正在执行程序暂时保留信息段的起始地址,用来指示堆栈起始位置的指针;
ES/FS/GS - 附加段寄存器(Extra Segment Register),存放程序的数据段的起始地址,为程序设计使用多个数据段带来方便。
-- 16位汇编,示意图如下(段寄存器中的值×16[2^4],得到段基址的值):
1个指令指针寄存器:EIP/IP
指令指针EIP/IP(Instruction Pointer) - 存放下次将要执行的指令在代码段的偏移量。
当取出一条指令后,EIP/IP自动加上该指令的长度或者形成转移地址,又指向下一条指令的地址,
从而可以控制有序的执行程序。
1个标志寄存器:EFlags/Flags
1. 堆栈
堆栈是由程序开辟的一片内存区域;先进后出;总是向下增长的(位于高地址,向低地址进行分配)。示意图如下:
EBP指向函数活动记录的一个固定位置,又称为栈帧指针(Frame Pointer)。
进入一个函数后,其EBP的值为:上层调用点的ESP-8(Call + Push上层函数的EBP)
可通过EBP + 8 访问第一个参数,EBP + 8 + sizeof(第一个参数)来访问第二个参数,... ...
PUSH(压栈):ESP -= 4
Call函数调用:ESP -= 4
POP(弹栈):ESP += 4
函数返回(ret):ESP += 4
函数返回(ret 8):ESP += 8
2. 寻址方式
(1) 立即数寻址
mov eax,44332211h
(2) 寄存器寻址
mov eax,ebx
(3) 直接寻址
mov eax,[1234h] ;[]表示取地址1234h处的DWORD值
(4) 寄存器间接寻址
mov eax,[ebx]
(5) 寄存器相对寻址(带位移量的寄存器间接寻址)
mov eax,[ebx+80h]
(6) 基址变址寻址(基址的变址寻址)
mov eax,[ebx+esi]
(7) 相对基址变址寻址(基址的带位移量的变址寻址)
mov eax,[ebx+esi+80h]
(8) 带比例的变址寻址
mov eax,[esi*2]
(9) 基址的带比例的变址寻址
mov eax,[ebx+esi*4]
(10) 基址的带位移量的带比例的变址寻址
mov eax,[ebx+esi*8+80h]
(11) I/O端口的直接寻址
in eax,80h
(12) I/O端口的寄存器间接寻址
in eax,dx
16位和32位寻址时的4元素规定:
有效地址元素 | 16位寻址 | 32位寻址 |
基址寄存器 | BX,BP | 任何32位通用寄存器 |
变址寄存器 | SI,DI | 除ESP外的任何32位通用寄存器 |
比例因子 | 无(或1) | 1,2,4,8 |
位移量 | 0,8,16位 | 0,8,32位 |
32位存储器寻址方式的组成公式为:
32位有效地址 = 基址寄存器+(变址寄存器 × 比例)+位移量
其中的4个组成部分是:
· 基址寄存器——任何8个32位通用寄存器之一;
· 变址寄存器——除ESP之外的任何32位通用寄存器之一;
· 比例——可以是1∕2∕4∕8(因为操作数的长度可以是1∕2∕4∕8字节);
· 位移量——可以是8∕32位值。
16位存储器分段寻址计算方法:
存储段内的每个单元的物理地址PA(Physical Address),用“段基址 : 段内偏移地址”来表达。
段基址即段地址SA(Segment Address)是相应段的起始地址。
段内偏移地址即偏移地址或称有效地址EA(Effective Address),
它是该单元的物理地址PA到段地址SA的距离(字节数),即:EA = PA - SA。
由于实方式下段寄存器只有16位,EA由寄存器BX、BP、SI或DI的内容来计算,也只有16位,
因此,需要将段寄存器的内容左移4位后与EA相加来达到PA需要的20位地址(1MB空间)。
即:PA = SA + EA = 段寄存器的内容左移4位(×16) + EA
3. 常用汇编指令
参见:http://www.cnblogs.com/caoyawei/archive/2009/04/24/1442569.html
4. 示例分析(VC6反汇编) 注:汇编代码的注释符为 ;
/**************************main****************************/ int main(int argc, char* argv[]) { 004106B0 push ebp // 保存上一层函数栈底指针,esp -= 4 004106B1 mov ebp,esp // ebp = esp 设置当前函数栈底指针 004106B3 sub esp,4Ch // esp -= 4Ch 004106B6 push ebx // 保存ebx到堆栈中,esp -= 4 004106B7 push esi // 保存esi到堆栈中,esp -= 4 004106B8 push edi // 保存edi到堆栈中,esp -= 4 004106B9 lea edi,[ebp-4Ch] // edi = ebp - 4Ch 004106BC mov ecx,13h // ecx = 13h 004106C1 mov eax,0CCCCCCCCh // eax = 0CCCCCCCCh 004106C6 rep stos dword ptr [edi] // 将edi地址起的(13h*4=4Ch)大小的内存区域初始化为0CCh int a = 1; 004106C8 mov dword ptr [ebp-4],1 // [ebp-4] = 1 int b = 2; 004106CF mov dword ptr [ebp-8],2 // [ebp-8] = 2 int c = sum(a, b); // 参数从右向左,依次压栈 004106D6 mov eax,dword ptr [ebp-8] // eax = [ebp-8] 004106D9 push eax // 保存eax到堆栈中,[esp] = eax,esp -= 4 004106DA mov ecx,dword ptr [ebp-4] // ecx = [ebp-4] 004106DD push ecx // 保存ecx到堆栈中,[esp] = ecx,esp -= 4 004106DE call @ILT+5(sum) (0040100a) // 调用@ILT+5跳转,跳转到sum函数,esp -= 4 004106E3 add esp,8 // 调用方进行堆栈平衡 esp += 8(参数a、参数b) 004106E6 mov dword ptr [ebp-0Ch],eax // [ebp-0Ch] = eax(eax中存放的是返回值) return 0; 004106E9 xor eax,eax // eax = 0,效率更高 } 004106EB pop edi // 恢复edi,esp += 4 004106EC pop esi // 恢复esi,esp += 4 004106ED pop ebx // 恢复ebx,esp += 4 004106EE add esp,4Ch // esp += 4Ch 004106F1 cmp ebp,esp // 检查栈是否平衡(即:ebp是否等于esp) 004106F3 call __chkesp (00401060)// 对esp的值进行检查 004106F8 mov esp,ebp // esp = ebp 004106FA pop ebp // 恢复上一层函数栈底指针 004106FB ret // 返回,esp += 4 /*************************************************************/ /******************编译器生成的一个jmp**********************/ 0040100A jmp sum (00401030) // 跳转到sum函数的第1条指令 /*************************************************************/ /**************************sum函数***************************/ int sum(int a, int b) { 00401030 push ebp // esp -= 4 00401031 mov ebp,esp 00401033 sub esp,40h 00401036 push ebx 00401037 push esi 00401038 push edi 00401039 lea edi,[ebp-40h] 0040103C mov ecx,10h 00401041 mov eax,0CCCCCCCCh 00401046 rep stos dword ptr [edi] return a+b; 00401048 mov eax,dword ptr [ebp+8] // eax = [ebp+8],参数a 0040104B add eax,dword ptr [ebp+0Ch] // eax += [ebp+0Ch],参数b } 0040104E pop edi 0040104F pop esi 00401050 pop ebx 00401051 mov esp,ebp 00401053 pop ebp 00401054 ret // 返回,esp += 4 /*************************************************************/ /**************************esp检查***************************/ 00401060 jne __chkesp+3 (00401063) // 若__chkesp+3不等于00401063,则跳转到00401063 00401062 ret 00401063 push ebp // 错误处理代码 00401064 mov ebp,esp 00401066 sub esp,0 00401069 push eax 0040106A push edx 0040106B push ebx 0040106C push esi 0040106D push edi 0040106E push offset string "The value of ESP was not properl"... (00422030) //等价于push 00422030h 00401073 push offset string "" (0042202c) //将字符串""的首地址0042202c值压栈 00401078 push 2Ah 0040107A push offset string "i386\\chkesp.c" (0042201c) 0040107F push 1 00401081 call _CrtDbgReport (00401390) 00401086 add esp,14h 00401089 cmp eax,1 0040108C jne __chkesp+2Fh (004010df) 0040108E int 3 // 软中断 0040108F pop edi 00401090 pop esi 00401091 pop ebx 00401092 pop edx 00401093 pop eax 00401094 mov esp,ebp 00401096 pop ebp 00401097 ret /*************************************************************/
5. 函数调用约定(__cdecl/__stdcall/__fastcall/__thiscall)
参见:http://blog.csdn.net/walkinginthewind/article/details/7580109
http://www.cnblogs.com/Dah/archive/2006/11/29/576867.html
http://hi.baidu.com/148332727/item/9e9b38ad9571afad29ce9dc0
6. 参考
http://jpkc.szpt.edu.cn/2006/IA32/ftp/address/frames/content/CHAPT3/index.htm