如何既执行if模块,由执行else模块

类别:编程语言 点击:0 评论:0 推荐:

从上下了一个程序unixc.rar,在readme.txt中,作者说:
该程序模拟UNIX中save与resume函数,并介绍在VC中如何使用汇编进行机器级的操作.
主函数很简单首先引入两个外部函数,
extern"C"表示按传统C命名习惯.函数save将程序指针保存在(*s)中并返回0,为什么有
 if(save(&sp)){...}
if后的语句看起来永远都不会被执行,但是运行结果表明它被执行了.这个问题同UNIX中处理机调度函数(switc
h)的那个if语句(第一句)一样.
程序执行完save(&sp)后得到因为条件为假而执行else语句,却在判断之前将程序指针保存在sp中了.
else语句中的resume(&sp),该函数很狡猾将堆栈中的返回地址改变了,改到了sp所指出,即将程序指针改到了执
行条件判断前.resume返回1,条件满足,执行if语句.
save函数堆栈:
eip ebp+8
s ebp+4
ebp ebp+0
resume函数堆栈与save的相同.

新建一个win32的工程,将unixc.cpp和unix.obj加入过程即可.
unix.obj是用masm6.11生成的:ml /c /coff unix.asm,生成coff格式的obj而不是omf格式.

本文主要通过反汇编来说明,如何通过保存返回地址使得即执行程序的if模块还执行
程序的else模块。

--- E:\程序_规范\UnixC\UnixC.cpp  ----------------------------------------------

1:

2:    #include<stdio.h>

3:    extern "C" int save(long * s);

4:    extern "C" int resume(long * s);

5:    void main()

6:    {

00401030   push        ebp 

 

保存栈底指针

ESP = 0012FF80

EBP = 0012FFC0

0012FF80  C0 FF 12  ...  堆栈内为ebp的值

 

00401031   mov         ebp,esp  //把当前的栈顶当作栈底

 

ESP = 0012FF80

EBP = 0012FF80

 

00401033   sub         esp,44h  //预留44h的栈空间

 

ESP = 0012FF3C   栈顶指向

EBP = 0012FF80   此时的栈底

 

00401036   push        ebx //把要用到的寄存器值进栈

 

ESP = 0012FF38

EBP = 0012FF80

012FF39  F0 FD 7F  瘕.  //为ebx

0012FF3C  02 00 00  ...

 

00401037   push        esi

00401038   push        edi

ESP = 0012FF30

EBP = 0012FF80

0012FF30  00 00 00  ... 为edi

0012FF33  00 00 00  ...  为esi

0012FF36  00 00 00  ... 

0012FF39  F0 FD 7F  瘕.

0012FF3C  02 00 00  ...

 

00401039   lea         edi,[ebp-44h] 

 

EDI = 0012FF3C

 

0040103C   mov         ecx,11h  //计数器 11h*4=44h

00401041   mov         eax,0CCCCCCCCh

00401046   rep stos    dword ptr [edi] //将这44个字节全部初始化为cc  即int 3

7:        long    spp;

0012FF7C  CC CC CC  烫.

&sp = 0x0012ff7c

 

8:        printf("Both the code in the if and the code in the else will be exe!!!\n");

00401048   push        offset string "Both the code in the if and the "... (00422050)

0040104D   call        printf (004010c0)

00401052   add         esp,4

9:        if(save(&spp))

00401055   lea         eax,[ebp-4]

 

//将spp的地址付给eax

eax = 0x0012ff7c

 

00401058   push        eax

 

ESP = 0012FF2C

EBP = 0012FF80

012FF2C  7C FF 12  |..  //eax入栈

0012FF2F  00 00 00  ...

0012FF32  00 00 00  ...

0012FF35  00 00 00  ...

0012FF38  00 F0 FD  .瘕

0012FF3B  7F CC CC  .烫

0012FF3E  CC CC CC  烫.

 

00401059   call        @ILT+5(_main+176080) (0040100a)

ESP = 0012FF28

EBP = 0012FF80

012FF26  12 00 5E  ..^   子程序返回时,下一条要执行的语句的地址

0012FF29  10 40 00  .@.

0012FF2C  7C FF 12  |..

0012FF2F  00 00 00  ...

0012FF32  00 00 00  ...

 

0040105E   add         esp,4 将调用_save时的参数弹出栈

ESP = 0012FF30

EBP = 0012FF80

 

00401061   test        eax,eax

//返回至为零,因为我们置eax为零

00401063   je          main+44h (00401074)

10:       {

11:           printf("return form the if\n");

00401065   push        offset string "return form the if\n" (00422038)

0040106A   call        printf (004010c0)

0040106F   add         esp,4

12:           return;

00401072   jmp         main+5Dh (0040108d)

13:       }

14:       else

15:       {

16:           printf("return form the else\n");

00401074   push        offset string "return form the else\n" (0042201c)

00401079   call        printf (004010c0)

0040107E   add         esp,4

17:           resume(&spp);

00401081   lea         ecx,[ebp-4]

把spp的地址付给ecx,作为函数调用的参数

ECX = 0012FF7C

 

00401084   push        ecx

入栈

ESP = 0012FF2C

EBP = 0012FF80

0012FF2C  7C FF 12  |.. spp的地址

0012FF2F  00 00 00  ...

 

00401085   call        @ILT+0(_main+176098) (00401005)

0040108A   add         esp,4

18:       }

19:       return;

20:   }

0040108D   pop         edi

0040108E   pop         esi

0040108F   pop         ebx

00401090   add         esp,44h

00401093   cmp         ebp,esp

00401095   call        __chkesp (00401140)

0040109A   mov         esp,ebp

0040109C   pop         ebp

0040109D   ret

 

 

子函数_save

_save:

ESP = 0012FF28

EBP = 0012FF80

012FF26  12 00 5E  ..^  _save返回时,下一条要执行的语句的地址

0012FF29  10 40 00  .@.

0012FF2C  7C FF 12  |..

0012FF2F  00 00 00  ...

0012FF32  00 00 00  ...

0042B000   push        ebp

ESP = 0012FF24

 EBP = 0012FF80

0012FF24  80 FF 12  €.. ebp

0012FF27  00 5E 10  .^. _save返回时,下一条要执行的语句的地址

0012FF2A  40 00 7C  @.|

0012FF2D  FF 12 00  ...

0012FF30  00 00 00  ...

 

0042B001   mov         ebp,esp

ESP = 0012FF24

EBP = 0012FF24

 

0042B003   mov         esi,dword ptr [ebp+8]

ESI = 0012FF7C     spp的地址给esi

 

0042B006   mov         eax,dword ptr [ebp+4]

EAX = 0040105E    _save返回时,下一条要执行的语句的地址给eax

 

0042B009   mov         dword ptr [esi],eax

 

在spp的内存地址中保留_save返回时下一条要执行的语句的地址 0040105e

012FF7C  5E 10 40  ^.@

0012FF7F  00 C0 FF  ...

 

0042B00B   pop         ebp

ESP = 0012FF28   ESP= ESP+4

EBP = 0012FF80

012FF28  5E 10 40  ^.@返回时下一条要执行的语句的地址

0012FF2B  00 7C FF  .|.

0012FF2E  12 00 00  ...

0012FF31  00 00 00  ...

 

0042B00C   mov         eax,0  //郅返回结果为0

0042B011   ret 回主函数

子函数_resume

_resume:

0042B012   push        ebp

0042B013   mov         ebp,esp

ESP = 0012FF24

EBP = 0012FF24

012FF24  80 FF 12  €..

0012FF27  00 8A 10  ...  //函数调用返回后,下一条要执行的语句的地址0040108a

0012FF2A  40 00 7C  @.|

0012FF2D  FF 12 00  ...

这时,我么要注意,ebp+4就是函数调用返回后,下一条要执行的语句的地址0040108a

我们想把函数的返回地址改变,改为执行_save后的第一条指令,还指令的地址放在ebp+8中

0042B015   mov         esi,dword ptr [ebp+8]

取出_save后的第一条地址存放的地址为spp的地址 0012ff7c

0042B018   mov         eax,dword ptr [esi]

将esi中的值给eax eax =0x0040105e 这就是改为执行_save后的第一条指令,而不是按正常执行_resume后的第一条指令

0042B01A   mov         dword ptr [ebp+4],eax

修改_resume函数返回后执行的第一条指令的地址

 

012FF24  80 FF 12  €..

012FF27  00 5E 10  .^. //函数调用返回后,下一条要执行的语句的地址由0040108a改为0040105e

0012FF2A  40 00 7C  @.|

0012FF2D  FF 12 00  ...

 

0042B01D   pop         ebp

0042B01E   mov         eax,1

//付值为1,返回,但程序会跳转到_save后的第一条指令,这样else 和if 都执行了

0042B023   ret

总的来说,就是通过一个变量来保存返回断点的下一条指令的地址,然后用该地址
替换程序正常返回地址,从而达到目的。

本文地址:http://com.8s8s.com/it/it25080.htm