VS反汇编相关指令

寄存器:

  • EAX:累加(Accumulator)寄存器加法乘法指令的缺省寄存器,常用于函数返回值
  • EBX:基址(Base)寄存器,以它为基址访问内存
  • ECX:计数器(Counter)寄存器,常用作字符串和循环操作中的计数器
  • EDX:数据(Data)寄存器,常用于乘除法和I/O指针,常用来存放整数除法产生的余数
  • ESI:源变址寄存器 ,
    EDI:目的变址寄存器   ====>>变址寄存器主要用于存放存储单元在段内的偏移量,用它们可实现多种存储器操作数.的寻址方式,为以不同的地址方式访问存储单元提供方便。作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果。 它们也可作一般的存储器指针使用。但需要注意的是:在串处理指令中,SI用作隐含的源串地址,默认在DS中;DI用做隐含的目的串地址,默认在ES中;即 DS:ESI指向源串,而ES:EDI指向目标串.。
  • ESP:堆栈(Stack)指针寄存器,指向堆栈顶部
  • EBP:基址指针寄存器,指向当前堆栈底部
  • EIP:指令寄存器,指向下一条指令的地址

VS反汇编相关指令_第1张图片

注意:栈区顶端朝下(即内存低地址),堆区朝上。

常用指令:

为了照顾到没学过汇编程序的同志们,这里简单介绍一下常见的几种汇编指令。

A、add:加法指令,第一个是目标操作数,第二个是源操作数,格式为:目标操作数 = 目标操作数 + 源操作数;

B、sub:减法指令,格式同 add;

C、call:调用函数,一般函数的参数放在寄存器中;

D、ret:跳转会调用函数的地方。对应于call,返回到对应的call调用的下一条指令,若有返回值,则放入eax中;

E、push:把一个32位的操作数压入堆栈中,这个操作在32位机中会使得esp被减4(字节),esp通常是指向栈顶的(这里要指出的是:学过单片机的同学请注意单片机种的堆栈与Windows下的堆栈是不同的,请参考相应资料),这里顶部是地址小的区域,那么,压入堆栈的数据越多,esp也就越来越小;

F、pop:与push相反,esp每次加4(字节),一个数据出栈。pop的参数一般是一个寄存器,栈顶的数据被弹出到这个寄存器中;

一般不会把sub、add这样的算术指令,以及call、ret这样的跳转指令归入堆栈相关指令中。但是实际上在函数参数传递过程中,sub和add最常用来操作堆栈;call和ret对堆栈也有影响。

G、mov:数据传送。第一个参数是目的操作数,第二个参数是源操作数,就是把源操作数拷贝到目的一份。

H、xor:异或指令,这本身是一个逻辑运算指令,但在汇编指令中通常会见到它被用来实现清零功能。

              用 xor eax,eax这种操作来实现 mov eax,0,可以使速度更快,占用字节数更少。

I、lea:取得第二个参数地址后放入到前面的寄存器(第一个参数)中。

               然而lea也同样可以实现mov的操作,例如:

                                  lea edi,[ebx-0ch]

方括号表示存储单元,也就是提取方括号中的数据所指向的内容,然而lea提取内容的地址,这样就实现了把(ebx-0ch)放入到了edi中,但是mov指令是不支持第二个操作数是一个寄存器减去一个数值的。

J、stos:串行存储指令,它实现把eax中的数据放入到edi所指的地址中,同时edi后移4个字节,这里的stos实际上对应的是stosd,其他的还有stosb,stosw分别对应1,2个字节。

K、jmp:无条件跳转指令,对应于大量的条件跳转指令。

L、jg:条件跳转,大于时成立,进行跳转,通常条件跳转之前会有一条比较指令(用于设置标志位)。

M、jl:小于时跳转。

N、jge:大于等于时跳转。

O、cmp:比较大小指令,结果用来设置标志位。

函数的参数传递方式:

 

函数调用规则指的是调用者和被调用函数间传递参数及返回参数的方法,在Windows上,常用的有:Pascal方式、WINAPI方式(_stdcall)、C方式(_cdecl)。

  1. _cdecl C调用规则

    参数从右到左进入堆栈 
    在函数返回后,调用者要负责清除堆栈。 
    这种调用方式通常会生成较大的可执行程序。

  2. _stdcall又称为WINAPI,调用规则为

    参数从右到左进入堆栈 
    被调函数在返回前自行清理堆栈

    这种方式生成的代码比cdecl小

  3. Pascal调用规则(现在基本不用)

    参数从左到右进入堆栈 
    被调函数在返回前自行清理堆栈 
    不支持可变参数的函数调用

下面是一段汇编代码

下面是按调用约定__stdcall 调用函数test(int p1,int p2)的汇编代码

假设执行函数前堆栈指针ESP为NN

push   p2    ;参数2入栈, ESP -= 4h , ESP = NN - 4h

push   p1    ;参数1入栈, ESP -= 4h , ESP = NN - 8h

call test    ;压入返回地址 ESP -= 4h, ESP = NN - 0Ch  (注意0C是16进制,相当于十进制的12)

;//进入函数内

{

push   ebp                        ;保护先前EBP指针, EBP入栈, ESP-=4h, ESP = NN - 10h(也是16进制,相当于十进制的16)

mov    ebp, esp                   ;设置EBP指针指向栈顶 NN-10h

mov    eax, dword ptr  [ebp+0ch]  ;ebp+0ch为NN-4h,即参数2的位置

mov    ebx, dword ptr  [ebp+08h]  ;ebp+08h为NN-8h,即参数1的位置

sub    esp, 8                     ;局部变量所占空间ESP-=8, ESP = NN-18h

...

add    esp, 8                     ;释放局部变量, ESP+=8, ESP = NN-10h

pop    ebp                        ;出栈,恢复EBP, ESP+=4, ESP = NN-0Ch

ret    8                          ;ret返回,弹出返回地址,ESP+=4, ESP=NN-08h, 后面加操作数8为平衡堆栈,ESP+=8,ESP=NN, 恢复进入函数前的堆栈.

}

 

你可能感兴趣的:(VS)