(转)VS 反汇编方法及常用汇编…

来源:互联网 发布:java获取当前路径 编辑:程序博客网 时间:2024/05/24 06:37

在调试没有源码的文件时,我们可能要用到反汇编设计。

一、VS反汇编方法

1、调出反汇编窗口。

2、调用寄存器窗口(只有在反汇编下才可见)

如果在调试状态还是没有此菜单项,可试着以下操作:

在VS中点击“工具”->“导入和导出设置”,选择“重置所有设置”,下一步,这时你可以保存当前设置或不保存,我觉得无所谓,下一步,选择“VisualC#开发设置”,“完成”。这样,“调试”->“窗口”->“寄存器”菜单项应该用显示出来了,记得要确保你的程序是在调试的过程中。

3、查看内存

点击“调试”->“窗口”->“内存”->“内存1”...“内存4”(选一个就可以了。)。在内存窗口中的“地址”栏输入地址,按回车即可看到该地地址处的内存信息。

 二、常用汇编指令介绍

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)。

A、_cdecl C调用规则:

(a)参数从右到左进入堆栈

(b)在函数返回后,调用者要负责清除堆栈,这种调用方式通常会生成较大的可执行程序。

B、_stdcall又称为WINAPI,调用规则如下:

(a)参数从右到左进入堆栈;

(b)被调用的函数在返回前自行清理堆栈,这种方式生成的代码比cdecl小

C、Pascal调用规则(主要用于Win16函数库中,现在基本不用):

(a)参数从左到右进入堆栈;

(b)被调用的函数在返回前自行清理堆栈。

(c)不支持可变参数的函数调用

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

空程序:

int main()

{

00411360  push        ebp       ;压入ebp

00411361  mov         ebp,esp     ;ebp= esp,保留esp,待函数调用完再恢复,因为函数调用中肯定会用到esp.

00411363  sub         esp,0C0h;esp-=0C0h(192);为该函数留出临时存储区

;将其他指针或寄存器中的值入栈,以便在函数中使用这些寄存器。

00411369  push        ebx       ;压入ebx

0041136A  push        esi       ;压入esi

0041136B  push        edi       ;压入edi

0041136C  lea         edi,[ebp-0C0h];读入[ebp-0C0h]有效地址,即原esp-0C0h,正好是为该函数留出的临时存储区的最低位

00411372  mov         ecx,30h   ;ecx= 30h(48),30h*4 = 0C0h

00411377  mov         eax,0CCCCCCCCh;eax = 0CCCCCCCCh;

0041137C  repstos    dwordptr es:[edi] ;重复在es:[edi]存入30个;0CCCCCCCCh?Debug模式下把Stack上的变量初始化为0xcc,检查未初始化的问题

return 0;

0041137E  xor         eax,eax   ;将eax清零,作为返回值

}

;各指针出栈

00411380  pop         edi          ;弹出edi

00411381  pop         esi          ;弹出esi

00411382  pop         ebx          ;弹出ebx

00411383  mov         esp,ebp      ;esp复原

00411385  pop         ebp          ;弹出ebp,也复原

00411386  ret                      ;返回

 

 

函数调用:

         

int _tmain(int argc, _TCHAR* argv[])

{

同上理解, 保存现场

004113D0  push        ebp 

004113D1  mov         ebp,esp

004113D3  sub         esp,0F0h;一共留了0F0h(240)空间

004113D9  push        ebx 

004113DA  push        esi 

004113DB  push        edi 

004113DC  lea         edi,[ebp-0F0h]

004113E2  mov         ecx,3Ch; ecx = 3C(60),3C*4 = 0F0h,

004113E7  mov         eax,0CCCCCCCCh

004113EC  repstos    dwordptr es:[edi]

同上理解.

    inta = 1, b = 2, c = 3;

定义a,b,c并存储在为函数留出的临时存储空间中.

004113EE  mov         dwordptr [a],1

004113F5  mov         dwordptr [b],2

004113FC  mov         dwordptr [c],3

    intd = Fun1(a, b, c);

参数反向入栈

00411403  mov         eax,dwordptr [c]

00411406  push        eax 

00411407  mov         ecx,dwordptr [b]

0041140A  push        ecx 

0041140B  mov         edx,dwordptr [a]

0041140E  push        edx 

调用Fun1

0041140F  call        Fun1(4111DBh) ;Call调用时将下一行命令的EIP压入堆栈

恢复因为Fun1参数入栈改变的栈指针,因为Fun1有3个参数,一个整数4个字节,共0Ch(12)个字节

00411414  add         esp,0Ch

00411417  mov         dwordptr [d],eax

将返回值保存在d中.

    return0;

返回值为0,让eax清零

0041141A  xor         eax,eax

 

}

 

恢复现场

0041141C  pop         edi 

0041141D  pop         esi 

0041141E  pop         ebx 

以下全为运行时ESP检查:

先恢复因为为main预留空间而改变的栈指针

0041141F  add         esp,0F0h

00411425  cmp         ebp,esp

00411427  call        @ILT+320(__RTC_CheckEsp)(411145h)

正常时只需要以下两句就可以正常恢复esp,再出栈,又可以恢复ebp.

0041142C  mov         esp,ebp

0041142E  pop         ebp 

0041142F  ret     ;main返回             

 

 

int Fun1(int a, int b, int c)

{

同上理解, 保存现场

00411A70  push        ebp 

00411A71  mov         ebp,esp

00411A73  sub         esp,0E4h;留了0E4H(228)空间,

00411A79  push        ebx 

00411A7A  push        esi 

00411A7B  push        edi 

00411A7C  lea         edi,[ebp-0E4h]

00411A82  mov         ecx,39h; 39H(57)*4 = 0E4H(228)

00411A87  mov         eax,0CCCCCCCCh

00411A8C  repstos    dwordptr es:[edi]

    intd = 4, e = 5;

定义变量

00411A8E  mov         dwordptr [d],4

00411A95  mov         dwordptr [e],5

 

    intf = Fun2(a, b, c, d, e);

再次参数反向入栈

00411A9C  mov         eax,dwordptr [e]

00411A9F  push        eax 

00411AA0  mov         ecx,dwordptr [d]

00411AA3  push        ecx 

00411AA4  mov         edx,dwordptr [c]

00411AA7  push        edx 

00411AA8  mov         eax,dwordptr [b]

00411AAB  push        eax 

00411AAC  mov         ecx,dwordptr [a]

00411AAF  push        ecx 

 

调用Fun2

00411AB0  call        Fun2(4111D6h) ;Call调用时将下一行命令的EIP压入堆栈

 

00411AB5  add         esp,14h;恢复因为参数入栈改变的栈指针,因为Fun2有5个参数,一个整数4个字节,共14h(20)个字节

将Fun2函数的返回值(保存在eax中),赋值给f;

00411AB8  mov         dwordptr [f],eax

 

    returnf;

将保留在f中的Fun1的返回值保存在eax中返回

00411ABB  mov         eax,dwordptr [f]

}

恢复现场

00411ABE  pop         edi 

00411ABF  pop         esi 

00411AC0  pop         ebx 

 

以下全为运行时ESP检查:

先恢复因为预留函数存储控件而改变的栈指针,

00411AC1  add         esp,0E4h

再比较ebp,esp,假如程序运行正确,两个值应该相等.

00411AC7  cmp         ebp,esp

00411AC9  call        @ILT+320(__RTC_CheckEsp)(411145h)

正常时只需要以下两句就可以正常恢复esp,再出栈,又可以恢复ebp.

00411ACE  mov         esp,ebp

00411AD0  pop         ebp 

返回main从pop堆栈中的EIP开始执行

00411AD1  ret   

 

int Fun2(int a, int b, int c, int d, int e)

{

同上理解, 保存现场

00412050  push        ebp 

00412051  mov         ebp,esp

00412053  sub         esp,0E4h;保留0E4H(228)

00412059  push        ebx 

0041205A  push        esi 

0041205B  push        edi 

0041205C  lea         edi,[ebp-0E4h]

00412062  mov         ecx,39h; 39H(57)*4 = 0E4H(228)

00412067  mov         eax,0CCCCCCCCh

0041206C  repstos    dwordptr es:[edi]

 

    intf  = 6, g = 7;

定义变量

0041206E  mov         dwordptr [f],6

00412075  mov         dwordptr [g],7

 

    inth = a + b + c + d + e + f + g;

相加,存入a,再保存在h

0041207C  mov         eax,dwordptr [a]

0041207F  add         eax,dwordptr [b]

00412082  add         eax,dwordptr [c]

00412085  add         eax,dwordptr [d]

00412088  add         eax,dwordptr [e]

0041208B  add         eax,dwordptr [f]

0041208E  add         eax,dwordptr [g]

00412091  mov         dwordptr [h],eax

 

    return  h;

将返回值h的值保存在eax中

00412094  mov         eax,dwordptr [h]

 

}

恢复现场

00412097  pop         edi 

00412098  pop         esi 

00412099  pop         ebx 

0041209A  mov         esp,ebp

0041209C  pop         ebp 

0041209D  ret ;返回fun1 ,从pop堆栈中的EIP开始执行

 

0 0
原创粉丝点击