因实验需要nachos,故开始学习分析nachos源码。在分析.s文件中遇到了汇编指令,在学习过程中遇到不少问题,以下是问题及其解答:
1.寄存器
eax, ebx, ecx, edx, esi, edi, ebp, esp等都是X86 汇编语言中CPU上的通用寄存器的名称,是32位的寄存器。
EAX 是"累加器"(accumulator), 它是很多加法乘法指令的缺省寄存器,作为函数返回值
EBX 是"基地址"(base)寄存器, 在内存寻址时存放基地址。
ECX 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。
EDX 则总是被用来放整数除法产生的余数。
ESI/EDI分别叫做"源/目标索引寄存器"(source/destination index),因为在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目标串.
EBP是"基址指针"(BASE POINTER), 它最经常被用作高级语言函数调用的"框架指针"(frame pointer). 在破解的时候,经常可以看见一个标准的函数起始代码:
push ebp ;保存当前ebp
mov ebp,esp ;EBP设为当前堆栈指针
sub esp, xxx ;预留xxx字节给函数临时变量.
这样一来,EBP 构成了该函数的一个框架, 在EBP上方分别是原来的EBP, 返回地址和参数. EBP下方则是临时变量. 函数返回时作 mov esp,ebp/pop ebp/ret 即可.
ESP 专门用作堆栈指针,被形象地称为栈顶指针,堆栈的顶部是地址小的区域,压入堆栈的数据越多,ESP也就越来越小。在32位平台上,ESP每次减少4字节。
2.函数调用堆栈和栈帧
为单个过程分配的那部分栈就叫做栈帧。栈帧的最顶端以两个指针界定——帧指针(寄存器ebp)和栈指针(esp)。ebp指向函数头,位置固定不动,esp指向函数尾,随函数内部变量的增加或减少而移动。每一个被调用的函数都有一个自己的栈帧结构,并且栈帧结构是由函数自己形成的。需要注意的是:CPU中的寄存器ebp和esp都只有一个。程序地址空间分配图:
3.指令
(1)MOV、LEA和[]问题
mov 将数据从源操作传到目的操作数中,操作数也可以是立即数。 当num是变量时:
mov dx,num 按照变量名称,取来它的内容;
mov dx,[num] 按照变量地址,取来它的内容。 两个是等价的 LEA同理。
以上mov为直接寻址。间接寻址 mov eax,[00400000]传的是地址400000这个地址里的值。
lea指令用来将一个内存地址赋给目的操作数
lea eax,[00400000]
那么eax寄存器中的值就是400000。lea第二操作数可以是复杂的表达式,用以代替较多的mov等操作。如
lea eas,[ebx+ecx*2+1]
等效于:
mov eax, ebx
add eax, ecx
add eax, ecx
add eax, 1
(2)rep stos dword ptr es:[edi]
rep指令重复后面的指令. ecx储存着重复的次数,stos指令将eax中的值拷贝到es:edi指向的地址中,然后edi执行完成后增加或减少。
4.简单程序反编译尝试
#include<iostream>
using namespace std;
int f(int x,int y) {
return x + y;
}
int main() {
int a = 3;
int b = f(5,6);
return 0;
}
int main() {
011BC710 push ebp //保存ebp
011BC711 mov ebp,esp //令ebp等于esp
011BC713 sub esp,0D8h //esp下移0D8h,给栈帧分配空间
011BC719 push ebx //保存ebx
011BC71A push esi
011BC71B push edi
011BC71C lea edi,[ebp-0D8h] //令edi的值等于ebp-0D8h,即栈帧的顶部
011BC722 mov ecx,36h //令ecx等于36h,即0D8h的四分之一
011BC727 mov eax,0CCCCCCCCh //int 3指令
011BC72C rep stos dword ptr es:[edi]//将ebp到ebp-0D8h中的内容赋值成int 3
int a = 3;
011BC72E mov dword ptr [a],3 //给变量a赋值 dword ptr 代表为4个字节
int b = f(5,6);
011BC735 push 6 //参数6入栈
011BC737 push 5 //参数5入栈
011BC739 call f (011B1B09h) //调用函数f
011BC73E add esp,8 //esp上移8个字节 局部变量5,6
011BC741 mov dword ptr [b],eax //将eax中的返回值赋给b
return 0;
011BC744 xor eax,eax //eax清零
}
011BC746 pop edi //弹出edi
011BC747 pop esi
011BC748 pop ebx
011BC749 add esp,0D8h //清空栈帧
011BC74F cmp ebp,esp //比价ebp和esp是否相等,用于堆栈平衡检查
011BC751 call __RTC_CheckEsp (011B145Bh) //VS的检查
011BC756 mov esp,ebp //将ebp赋给esp
011BC758 pop ebp //还原ebp
011BC759 ret //返回
int f(int x,int y) {
011BC6D0 push ebp
011BC6D1 mov ebp,esp
011BC6D3 sub esp,0C0h
011BC6D9 push ebx
011BC6DA push esi
011BC6DB push edi
011BC6DC lea edi,[ebp-0C0h]
011BC6E2 mov ecx,30h
011BC6E7 mov eax,0CCCCCCCCh
011BC6EC rep stos dword ptr es:[edi]
return x + y;
011BC6EE mov eax,dword ptr [x]
011BC6F1 add eax,dword ptr [y]
}
011BC6F4 pop edi
011BC6F5 pop esi
011BC6F6 pop ebx
011BC6F7 mov esp,ebp
011BC6F9 pop ebp
011BC6FA ret
参考资料 :
https://bbs.pediy.com/thread-216976.htm