首页
社区
课程
招聘
[旧帖] [分享]菜鸟见识到的一些指令猥琐方法 0.00雪花
发表于: 2010-3-29 17:29 1537

[旧帖] [分享]菜鸟见识到的一些指令猥琐方法 0.00雪花

2010-3-29 17:29
1537
【前言】:

只是介绍一些调试execryptor外壳遇到的一些指令猥琐的处理方法....专业术语我也不懂怎么叫,只知道长那个样。。
总的来说。。这些方法拼在一起。。除非有强大的耐力和精力。要不用F7我看是会吐血。。。程序的方法生成下面这些猥琐

我想同样是要用程序的方法来除掉他们。。只是这个程序的编写也是相当不简单(个人感觉)...想要越简单越容易扩展,就越难编写....

期待大牛写出一个强大的工具对付这些猥琐...

【阅读对象】
和我一样的菜鸟。。也许大牛都见多不怪。。他们都有对付这个的法宝了。哈哈!!!

==============================================================================================================
正题:

主要包括以下几种:

1. 代码乱序
2. 代码变形
3. 垃圾代码         ===========> 我暂时没遇到.. 不过可想而知,也是非常恶心的一种方法.. (久闻其大名)..
4. VM             ===========> 这个可以用在大部分的指令... 所以下面的介绍乱序还是变形都可以再使用VM,导致代码变形无法匹配一个变形模板..

【代码乱序方法】

第一种:  jmp
不断的E9远跳.. 跳来跳去的。。就够你爽了。。如果说用F7一步步的跟踪。。估计要吐血。。这个方法对付F7跟踪的就够有效了。。当然这个可以被程序或者脚本处理简简单单的处理掉

eg.
0053CC7C                          jmp     00512830

------------------------------------------------------------------------------------

第二种: push ret   --> 同时这两条指令之间可以加入 jmp .或者其他的 变形跳转指令
push 返回地址
ret

eg.
00628387   68 D5886200           push    006288D5
006296AC   C3                    retn

-------------------------------------------------------------------------------------

第三种:    ===> 这三条指令之间同样可以穿插 push  ret 或者 jmp 指令...

Call 地址
xchg dword ptr [esp], 寄存器
pop 上面使用的寄存器...

eg.
0053A18F    E8 0E1E0000        call    0053BFA2                        
0053BFA2    871C24             xchg    dword ptr [esp], ebx            
005448F9                       pop     ebx                           

----------------------------------------------------------------------------------------

第四种:  ==> SEH , 这种方法既可以 检测硬件断点,同时又可以打乱程序流程,使得前后真正有用的指令无法衔接起来看。。达到反跟踪的目的

注意:这些指令任何两条指令之间都可以穿插前面的3种 乱序方法....

eg.

大概长的样子如下面:  
call  push fs:[0]  mov fs:[0], esp
然后就是 int1  int3 异常指令

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   ---> 真正有用的指令

0062880B   E8 530E0000           call    00629663               
00629663   67:64:FF36 0000       push    dword ptr fs:[0]
00629669   67:64:8926 0000       mov     dword ptr fs:[0], esp
006294CC    64:F1                    int1                                        // 造成异常。。

// SEH 异常处理函数的样子如下

00628871   51                    push    ecx
00629488   8BCC                  mov     ecx, esp
0062948A   81C1 10000000         add     ecx, 10
00629490   8B09                  mov     ecx, dword ptr [ecx]
00629492   C701 13000100         mov     dword ptr [ecx], 10013                // ecx --> CONTEXT
00628F42   81C1 18000000         add     ecx, 18                        //  ecx + 18h  --> Dr7

00628F48   8A01                  mov     al, byte ptr [ecx]                // 如果有硬件断点的话,Dr7 不等于0
00628F4A   81C1 9C000000         add     ecx, 9C                        // context.ebp
00628F50   0001                  add     byte ptr [ecx], al                // context.ebp + context.dr7 (dr7不为0造成堆栈错误)

00629649   81C1 04000000         add     ecx, 4                                // context.eip

// 这里要看如何分析得到 SEH处理完后的返回地址...

0062964F   C701 F5906200         mov     dword ptr [ecx], 006290F5        // context.eip = 006290F5
00629655   33C0                  xor     eax, eax
00629657   59                    pop     ecx
00629658   C3                    retn

// 平衡堆栈,去掉 SEH  --> 和前面的对应..

006290F5    67:64:8F06 0000 pop     dword ptr fs:[0]                        // 去掉 fs:[0]  
006290FB    870424          xchg    dword ptr [esp], eax                // 下面两句去掉 SEH处理函数地址
006290FE    58              pop     eax

// 从这里往后才是接有用的代码...

BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB   --> 真正有用的指令...

===> 从上面可以看到 AAA 和 BBB 这两种真正有用的指令被一个SEH隔开了,这样,我们思维就被SEH干扰了。。没办法将AAA 和 BBB 指令
===> 连起来看.. 就无法理解程序的真正是要做什么的...

--------------------------------------------------------------------------------------------

第五种:

这一种我也不知道叫什么名字。。但是看起来蛮有用的。。和SEH反跟踪的方法很像。。只不过这个可以检测INT3软件断点...
两者刚好可以互补..

代码样式如下:

00520E47    68 65CF4C0D           push    0D4CCF65                        ; 这句是真正有用的代码...

********************************
00520E4C    E8 08EA0200           call    0054F859                        ;
0054F859    870C24                xchg    dword ptr [esp], ecx            ; 将返回地址保存到寄存器中

005479CB    50                    push    eax                             
005479CC    33C0                  xor     eax, eax                        
005479CE    8A01                  mov     al, byte ptr [ecx]              ; 本意是要检测 Int3断点

005479D0                          push    ecx                             ;
0051EA56                          mov     ecx, 99                         ; ecx = 99
004F19EE                          sub     eax, ecx                        ; VM Code
004F19F7    59                    pop     ecx      
                     
004F19F8                          imul    byte ptr [ecx]                  ; VM Code
004F1A01    80C0 5C               add     al, 5C                          

00524988                          nop                                     ; 原本这里可以检测CC软件断点的... 不知道为何这个壳不做。。

00524995                          pop     eax                             ;

0052499F    53                    push    ebx                             
0054D6CE                          mov     ebx, 4EEAC0                     ; ebx = 4EEAC0 ==> 用这个保存前面CALL返回地址
004F37E1                          jmp     004F1A09                        ; VM Code
004F1A0A                          mov     dword ptr [ebx], ecx            ; VM Code
004F1A13    5B                    pop     ebx                             

004F1A14    59                    pop     ecx                             ; 这个对应 00520E4C 和 0054F859 这两行.. (前面介绍的第三种乱序方法)

004F1A15                          pop     esi                             ; 对应 push    0D4CCF65  (真正有用的)

004F1A1E    FF35 C0EA4E00         push    dword ptr [4EEAC0]              ; 用这种方式返回..
0054300A                          retn                                    ;
**********************************

最终有用的代码就只有下面两句:

00520E47    68 65CF4C0D           push    0D4CCF65                        ; 这句是真正有用的代码...
004F1A15                          pop     esi                             ; 对应 push    0D4CCF65  (真正有用的)

可见: 这种方法也起到了反跟踪的作用。。真正有用的代码无法连在一起看。。被分隔开了。。。同样也可以检测CC断点...

============================================================================================================================================

【代码变形】

下面列出几种见到的样式.... 通过这些变形。。一条指令变成5条甚至 10条。。中间又不断穿插那些代码乱序指令,还可以对任何指令践行VM... 所以你要先除掉VM,
然后在除掉乱序 再整理出那些变形指令。。然后在将变形还原回来。。....吐血!!!

还是这些变形可以不断的扩展。。

        //------------------------------------------------------
        // push ebx  ( 8 registers  -- each for one register )
        // mov ebx, ebp
        // xchg dword ptr [esp], ebx        
        //
        //  --> push ebp

        //---------------------------------------------
        // xchg dword ptr [esp], ebp
        // mov  ecx, ebp
        // pop  ebp
        //
        // ===> pop ecx

        //---------------------------------------------
        // push ebx        ( 8 REGISTERS  -- EACH FOR ONE REGISTER )
        // push 9EC3461A
        // pop  ebx
        // sub  ebx, FFB54740
        // and  ebx, DEE9A4C4
        // add  ebx, 6258f045
        // xchg dword ptr [esp], ebx
        //
        // ---> push XXXXXX

        //------------------------------------------------
        // push eax
        // push 5B7761EA
        // pop  eax
        // sub  eax, CF129CAB
        // jb   0053B479
        // add  eax, 87ABB20D
        // add  edx, eax       ---> 这里可以是其他的算术指令 ( xor or add sub .... )
        // pop   eax
        //
        //----> add edx, XXXXXXXX

        //--------------------------------------------------
        // push 3A20A71
        // pop  eax
        // or   eax, DAD460A2
        // add  eax, 1FDEF9BB
        // xor  eax, E16E3DA0
        // add  eax, D353C036
        //
        //===> mov eax, XXXXXXX

        //--------------------------------------
        // push eax
        // pop  edx
        //
        // --> mov edx, eax

=========================================================================================================

【VM】

这个可以将上面的变形 乱序的指令 VM掉。。或者其他有用的指令VM掉。。。 结合起来用的。。可以想象,处理起来是挺费劲的

这个壳的VM很早softworm就分析了。。 呵呵,我等到现在才来学!!差距好远啊,努力学习!!。。。。

参考:  http://bbs.pediy.com/showthread.php?p=117830

贴一段自己分析并且编写处理这个简单VM的代码。。。 从代码可以看出它VM的指令类型有哪些:

// get decrypt data first (it is a DWORD type data) --> opcode is the last byte of decryptData

        decryptData = decryptInstructionData( originalData, eflags );

        

        if( decryptData & 0xE0000000 )

        {

                decryptData = 0x3F;                // NOP.. but 'vmDataSize = 8'

                vmDataSize = 8;

        }

        else

        {

                if( decryptData & 0xC0 )

                {

                        // get 32 bit immediate number

                        Readmemory(&originalData2, address + 4, 4, MM_SILENT);

                        DWORD high5 = originalData2 & 0xFFFFF;

                        DWORD low3  = (decryptData >> 8) & 0xFFF;

                        high5 <<= 0xC;

                        Immediate = high5 | low3;

                        // vmcode size

                        vmDataSize = 8;

                }

        }

        byteCode    = decryptData & 0xFF;

        

        //=======================================================================

        if( byteCode == 0 )

        {

                // push Reg32

                int regIdx = (decryptData >> 8) & 7;

                sprintf( disasm, "push    %s", gRegisterNames32[regIdx] );

        }

        else if( byteCode == 1 )

        {

                // pop Reg32

                int regIdx = (decryptData >> 8 ) & 7;

                sprintf( disasm, "pop     %s", gRegisterNames32[regIdx] );

        }

        else if( byteCode == 2 )

        {

                // mov Reg32, Reg32

                int srcReg        = (decryptData >> 8) & 7;

                int destReg        = (decryptData >> 11) & 7;

                sprintf( disasm, "mov     %s, %s", gRegisterNames32[destReg], gRegisterNames32[srcReg] );

        }

        else if( byteCode == 3 )

        {

                // mov dword ptr [Reg32], reg32

                int srcReg        = (decryptData >> 8) & 7;

                int destReg        = (decryptData >> 11) & 7;

                sprintf( disasm, "mov     dword ptr [%s], %s", gRegisterNames32[destReg], gRegisterNames32[srcReg] );

        }

        else if( byteCode == 4 )

        {

                // mov Reg32, dword ptr [Reg32]

                int srcReg        = (decryptData >> 8) & 7;

                int destReg        = (decryptData >> 11) & 7;

                sprintf( disasm, "mov     %s, dword ptr [%s]", gRegisterNames32[destReg], gRegisterNames32[srcReg] );

        }

        else if( byteCode == 5 )

        {

                // xchg dword ptr [esp], Reg32

                int regIdx = (decryptData >> 8) & 7;

                sprintf( disasm, "xchg    dword ptr [esp], %s", gRegisterNames32[regIdx] );

        }

        else if( byteCode == 6 )

        {

                // retn

                sprintf( disasm, "retn");

        }

        else if( byteCode == 7 )

        {

                // not Reg32

                int regIdx = (decryptData >> 8) & 7;

                sprintf( disasm, "not     %s", gRegisterNames32[regIdx] );

        }

        else if( byteCode == 0x10 )

        {

                // pushfd

                sprintf( disasm, "pushfd");

        }

        else if( byteCode == 0x11 )

        {

                // popfd

                sprintf( disasm, "popfd");        

        }

        else if( byteCode == 0x12 )

        {

                // test reg32, reg32

                int srcReg        = (decryptData>>8) & 7;

                int destReg        = (decryptData>>11) & 7;

                sprintf( disasm, "test     %s, %s", gRegisterNames32[destReg], gRegisterNames32[srcReg] );

        }

        else if( byteCode == 0x13 )

        {

        }

        else if( byteCode == 0x14 )

        {

        }

        else if( byteCode == 0x15 )

        {

                // mov byte ptr [Reg32], 00

                int                regIdx        = (decryptData>>8) & 7;

                BYTE        byteVal        = (decryptData>>11) & 0xFF;

        

                sprintf( disasm, "mov     byte ptr [%s], %02lX", gRegisterNames32[regIdx], byteVal);

        }

        else if( byteCode == 0x16 )

        {

                // ror Reg32, xx

                int regIdx                = (decryptData >> 8) & 7;

                BYTE bitCount        = (decryptData >> 11) & 0xFF;

               

                sprintf( disasm, "ror     %s, %02lX", gRegisterNames32[regIdx], bitCount );

        }

        else if( byteCode == 0x17 )

        {

                // imul byte ptr [Reg32]

                int regIdx = (decryptData >> 8) & 7;

                sprintf( disasm, "imul    byte ptr [%s]", gRegisterNames32[regIdx] );

        }

        else if( byteCode == 0x18 )

        {

                // 注意:  这里有 ah al 的区别.. 但是还没有看懂它是怎么区分的...

                //

                // arithemic type? for one byte  ==> "add al, dl"

                //

                // 8 types of arithmetic instruction between two Reg8

                // 0: add 1: or 2: adc 3: sbb 4: and 5: sub 6: xor 7: cmp

                int type = decryptData & 0x7;

                int srcReg  = (decryptData >> 8) & 7;

                int destReg = (decryptData >> 11) & 7;

        

                sprintf( disasm, "%s     %s, %s", gArithmetic[type], gRegistersName8[destReg],         gRegistersName8[srcReg] );

        }

        else if( byteCode == 0x44 )

        {

                // push Imm32  --> Imm32: normal 32 bit instant number

                sprintf( disasm, "push    %08lX", Immediate );

        }

        else if( byteCode == 0xC0 )

        {

                // jmp XXXXXXXX

                sprintf( disasm, "jmp     %08lX", Immediate );

        }

        else if( byteCode == 0x82 )

        {

                // call XXXXXXXX

                sprintf( disasm, "call    %08lX", Immediate );

        }

        else if( byteCode == 0x83 )

        {

               

        }

        else if( byteCode == 0x84 )

        {

                // push Imm32  --> Imm32: instruction address

                sprintf( disasm, "push    %08lX", Immediate );

        }

        else if( (byteCode & 0xF8) == 0x8 )

        {

                // 8 types of arithmetic instruction between two registers

                // 0: add 1: or 2: adc 3: sbb 4: and 5: sub 6: xor 7: cmp

                int type                        = decryptData & 0x7;

                int srcReg                        = (decryptData>>8) & 7;

                int destReg                        = (decryptData>>11) & 7;

                sprintf( disasm, "%s     %s, %s", gArithmetic[type], gRegisterNames32[destReg], gRegisterNames32[srcReg] );

        

        }

        else if( ((byteCode + 0x40) & 0x87) == 0x80 )

        {

                // add Reg32, Imm32

                int regIdx = (byteCode >> 3) & 0x7;

                sprintf( disasm, "add     %s, %08lX", gRegisterNames32[regIdx], Immediate );

        }

        else if( ((byteCode + 0x40) & 0x87) == 0x81 )

        {

                // xor Reg32, Imm32

                int regIdx = (byteCode >> 3) & 0x7;

                sprintf( disasm, "xor     %s, %08lX", gRegisterNames32[regIdx], Immediate );

        }

        else if( byteCode == 0x20 )

        {

                // test Reg8, Reg8

                int srcReg  = (decryptData >> 8) & 7;

                int destReg = (decryptData >> 11) & 7;

                sprintf( disasm, "test    %s, %s", gRegistersName8[destReg], gRegistersName8[srcReg] );

        }

        else if( byteCode > 0x20 )

        {

                sprintf( disasm, "nop" );

        }
复制代码

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (11)
雪    币: 8
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
看着好长,复制下来以后看了,多谢你的分享~~
2010-3-29 22:13
0
雪    币: 75
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
恩 是挺猥琐
流程整个乱了 两条关键指令里插入了成百上千条垃圾代码
看着就晕
2010-3-30 22:01
0
雪    币: 7
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
先收藏了..晚点再看.
2010-3-31 03:09
0
雪    币: 129
活跃值: (333)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
xed
5
差不多都被你说完了
2010-3-31 10:13
0
雪    币: 213
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
收藏了慢慢看!
2010-3-31 10:17
0
雪    币: 9
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
正在学习,现在还看不懂
2010-3-31 10:35
0
雪    币: 576
活跃值: (1163)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
8
。。。大哥,这个好像是我写的吧?呵呵
2011-1-16 00:42
0
雪    币: 5
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
thx。。。。。。。。。。。
2011-1-16 01:03
0
雪    币: 2076
活跃值: (1724)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
学习一下啊……感谢分享…………
2011-1-16 01:07
0
雪    币: 28
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
。。。大哥,这个好像是我写的吧?呵呵
是吗?
2011-1-29 11:11
0
雪    币: 25
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
学习了....
2011-2-1 14:34
0
游客
登录 | 注册 方可回帖
返回
//