花指令

刚在52破解看了一篇花指令的文章,感觉挺好的。

第一种花指令是jmp+垃圾数据

比如

jmp label

//这里存放垃圾指令或数据

label:

//正常指令

push ebp

mov ebp,esp

 

第二种是假分支跳转

 xor eax, eax;        

 test eax, eax;        

 jnz  LABEL1;     

  jz LABEL2;

 

xor eax,eax这让eax为0,逻辑运算指令会影响ZF标志位的,所以会置ZF为1,那么永远会跳转到LABEL2处,这样LABEL1处的代码全部是垃圾指令,不管看起来多么正常,统统都是垃圾指令。

 

第三种是call和retn配合,看起来好像调用了一个函数,但实际上啥事也没做就返回了。

我写了一段测试代码。

int main(int argc, char* argv[])
{
	
	printf("hello world");
	int a;
    __asm {
        call LABEL9;
        _emit 0x55;
		_emit 0x8b;
		_emit 0xec;
		_emit 0xff;
		_emit 0x83;
		_emit 0xec;
		_emit 0xc;
    LABEL9:
        add dword ptr ss : [esp], 0x0D;
        ret;
        __emit 0xF3;
    }
    
	a = 3;



return 0;

}

  

        _emit 0x55;
		_emit 0x8b;
		_emit 0xec;
		_emit 0xff;
		_emit 0x83;
		_emit 0xec;
		_emit 0xc;
这中间有_emit的都是垃圾数据,干扰分析者的。
我们真正有效的代码就是
int a;
a=3;
return 0;
没了。
 
add dword ptr ss : [esp], 0x0D;

这个改变函数返回地址的,调用完call Lable9之后,栈顶会存放函数的返回地址,也就是_emit 0x8b这个数据的地址。由于是垃圾数据,我们必须不能让它返回到这个地址,要改一下

改到下一句有效代码的地址处,也就是a=3;这句代码的地址,那么就需要让返回地址加一个值,加多少呢?这个我是反汇编之后用OLLYDBG调试之后算出来的。具体看一下下面的图

0040D708   .  68 6C2F4200                push tes22.00422F6C                      ; /Arg1 = 00422F6C ASCII "hello world"
0040D70D   .  E8 5EFFFFFF                call tes22.0040D670                      ; \tes22.0040D670
0040D712   .  83C4 04                    add esp,0x4
0040D715   .  E8 07000000                call tes22.0040D721
0040D71A   .  55                         push ebp
0040D71B   .  8BEC                       mov ebp,esp
0040D71D      FF                         db FF
0040D71E   .  83EC 0C                    sub esp,0xC
0040D721   .  36:830424 0D               add dword ptr ss:[esp],0xD
0040D726   .  C3                         retn
0040D727   ?  f3:c745 fc 03000000        rep mov dword ptr ss:[ebp-0x4],0x3

调用完call 0040D721时,栈顶的返回地址是0040D71A,我们想让它返回到0040D727这个正常的地方,0040D727-0040D71A=0x0D,也就是说返回地址需要+0xD

这就是

add dword ptr ss:[esp],0xD 这句代码中为什么要加0xD的原因,加多少是和你添加的垃圾数据有关系的,你加的数据越多,这个值就越大。

有个问题,0xD这个值是我编译之后查看Ollydbg才算出来的,能不能在编写程序的时候就可以知道,这个地方不会呀。



这种干扰效果还是比较明显的。我们看一下在IDA中的效果。
0040D708                 push    offset aHelloWorld ; "hello world"
.text:0040D70D                 call    _printf
.text:0040D712                 add     esp, 4
.text:0040D715                 call    near ptr loc_40D71D+4
.text:0040D71A                 push    ebp
.text:0040D71B                 mov     ebp, esp
.text:0040D71D
.text:0040D71D loc_40D71D:                             ; CODE XREF: _main_0+25p
.text:0040D71D                 inc     dword ptr [ebx-7CC9F314h]
.text:0040D71D _main_0         endp ; sp-analysis failed
.text:0040D71D
.text:0040D723                 add     al, 24h
.text:0040D725                 or      eax, 45C7F3C3h
.text:0040D72A                 cld
.text:0040D72B                 add     eax, [eax]
.text:0040D72B ; ---------------------------------------------------------------------------
.text:0040D72D                 db 2 dup(0), 33h
.text:0040D730 ; ---------------------------------------------------------------------------
.text:0040D730                 rcr     byte ptr [edi+5Eh], 5Bh
.text:0040D734                 add     esp, 44h
.text:0040D737                 cmp     ebp, esp
.text:0040D739                 call    __chkesp
.text:0040D73E                 mov     esp, ebp
.text:0040D740                 pop     ebp
.text:0040D741                 retn

是不是感觉特别乱呀,花指令就是这种效果,让你感觉很烦。




还有一种是也是看似分支跳转实际上是无条件跳转
#include "stdafx.h"
#include "windows.h"
int main(int argc, char* argv[])
{
    int a;
    printf("hello world");
        LoadLibrary("fldsjfljsfljsflsjflsjfl");
    __asm{
        cmp eax, 0;
        jc LABEL6_1;
        jnc LABEL6_2;
    LABEL6_1:
        _emit 0xE8;
    LABEL6_2:
    }
    a = 41;



return 0;

}

用LoadLibrary加载一个文件,这个文件很明显不管是谁的电脑上都不可能存在的一个文件,那么返回值一定是0,也就是说cmp eax,0的结果一定是0,也就是一定不会产生进位后者借位

,也就是CF标志位一定不会置1,也就是LABEL6_1的流程永远不会到达,那么Lable6_1的代码就是垃圾代码。我曾经遇到过这种花指令。当时很头疼就放下了。今天自己调试了一遍,感觉我有可以了。

看一下在OD中的效果。

0040D708   .  68 6C2F4200   push tes22.00422F6C                          ; /Arg1 = 00422F6C ASCII "hello world"
0040D70D   .  E8 5EFFFFFF   call tes22.0040D670                          ; \tes22.0040D670
0040D712   .  83C4 04       add esp,0x4
0040D715   .  8BF4          mov esi,esp
0040D717   .  68 A82F4200   push tes22.00422FA8                          ; /FileName = "fldsjfljsfljsflsjflsjfl"
0040D71C   .  FF15 6CA14200 call dword ptr ds:[<&KERNEL32.LoadLibraryA>] ; \LoadLibraryA
0040D722   .  3BF4          cmp esi,esp
0040D724   .  E8 3739FFFF   call tes22.00401060
0040D729   .  83F8 00       cmp eax,0x0
0040D72C   .  72 02         jb short tes22.0040D730
0040D72E   .  73 01         jnb short tes22.0040D731
0040D730   >  E8 C745FC29   call 2A3D1CFC
0040D735   ?  0000          add byte ptr ds:[eax],al
0040D737   ?  0033          add byte ptr ds:[ebx],dh
0040D739   ?  c05f 5e 5b    rcr byte ptr ds:[edi+0x5e],0x5b
0040D73D   .  83C4 44       add esp,0x44
0040D740   .  3BEC          cmp ebp,esp
0040D742   .  E8 1939FFFF   call tes22.00401060
0040D747   .  8BE5          mov esp,ebp

 

看下在IDA中的效果,和在OD中的效果差不多。

 

上一篇:如何用栈来管理函数调用


下一篇:ESP32 之 ESP-IDF 教学WiFi篇(二)—— LwIP 之 TCP 通信