吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7892|回复: 18
收起左侧

[原创] 菜鸟见识到的一些指令猥琐方法

[复制链接]
hkfans007 发表于 2010-3-17 22:41
【前言】:

只是介绍一些调试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" );
        }

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Hmily 发表于 2010-3-17 23:01
楼主好强的毅力啊,支持下,加精鼓励.
 楼主| hkfans007 发表于 2010-3-17 23:26
本帖最后由 hkfans007 于 2010-3-18 20:50 编辑
楼主好强的毅力啊,支持下,加精鼓励.
Hmily 发表于 2010-3-17 23:01



    哈哈,其实我毅力一般,如果说我一路F7的话。。那确实毅力很强。。其实我正在写一个OD插件。。可以把这些东西全部过滤掉的。。只是现在很乱。。局限性很大。。暂时只能针对正在研究的那个壳。。

等研究到TMD,能够处理掉TMD的那些东西的话,再修修改改一下发出来一起玩 。。。。

如下图:
1212.JPG
Hmily 发表于 2010-3-17 23:33
回复 3# hkfans007


这样看起来爽多了,等你完善后发布!期待~
czjh2008 发表于 2010-3-17 23:37
呵呵!高手来着……
qaz003 发表于 2010-3-17 23:59
服了。。。够耐心的
热火朝天 发表于 2010-3-18 00:29
期待楼主的大作,我看着都累
李东国 发表于 2010-3-18 01:02
我们只有期待了
zjhfh0098 发表于 2010-3-18 07:00
就等你出作品了
root8384 发表于 2010-3-18 10:06
我一个小菜鸟,代码看的头晕啊
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-5-10 11:08

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表