摘要:本文提供一种从样本弱性VM到人性化阅读反编译代码的思路。反编译器前后经历了两个版本,xdisasm.py和ydisasm.py,这里只提供最终版,也只是大概展示了相关成果,心路历程还需各位道友亲自走上一走。
大致经历过的心路:线性反编译,到启发式反编译,代码分块,流程图可视化,反编译到正编译再到反编译。反编译器简单模仿IDAPython或unicorn或miasm的部分思想。xdisasm.py快速的线性反编译能很好应对展平的第0层vm代码,对后面涉及多个函数,尤其再堆栈平衡追踪上有涉及缺陷,所以使用了启发式思路,即后续的反编译由已经反编译的指令引用决定,这也是多数反编译器的合理思路。
如图,程序将将ikey通过hex解码得到bkey(应该是32字节,16个short int)
vmi_t 声明了每层vm使用的参数信息
info_type 前三个vm为6,第四个为3,用来定位后面的pkey所在的表
vmID用来激发各层vm的memset和memcpy函数额外代码。
vmc_ptr指向下一个vm信息项
我们通过IDAPython剥离四个vm代码,用于反编译测试
通过Hi_start_vm_with_vmInfoType_vmInfoTable函数得到每个vm执行的内存结构,和第0层vm的操作码。
第0层vm操作码定义,其他同理
有了第0层vm操作码,启用ydisasm.py下图代码,执行下述命令,
需要有unicorn,keystone的。会生成四个文件,其中
(1)fun0.v.asm 是vm指令反汇编文件。
(2)fun0.gv是vm指令反汇编代码的控制流程图dot,可以通过graphviz的的dot.exe编译生成图片等。如附件的fun0.png
(3)fun0.bin是通过keystone编译的代码,结果类似(4)。
(4)fun0.asm 是yasm(即以前的nasm)可以编译的代码。
其中(3)和(4)是快速打开后续vm的关键,基本原理是用x86汇编,维持栈平衡的各种运算条件下,替换vm指令,然后通过keystone编译为机器码或通过yasm编译器编译为机器码,然后通过IDA反编译为高级伪码。
通过yasm将fun0.asm编译为coff文件
通过IDA反编译fun0.o,如图,这与vm0的代码就类似。
我们可以看到memset(dst,0x33333333,100)在vmID.1层触发的动作
实际效果是memset(dst,1,100),且dst赋值给了pkey位置
有了高级伪码,我们可以轻易得到下一层vm的操作码
同理,ydiasm.py启用fun1相关部分代码,根据选择生成asm,bin,gv等
依次类推,可以得到各vm的可阅读代码,举例如下
各vm中的memset等敏感操作,从fun3.o开始
除了对底层代码段一些数据的比对读写,看下fun3.o的memset触发点,
阅读IDA的高级伪码如果有不清楚的地方,估计还得到汇编代码取看一眼,如下
fun3.o的开头位置,没有任何运算就进行了比较,肯定不对劲,且m2位置传入是零。
回到汇编我们看到F5伪码过滤掉了memcpy(pbkey,pbkey,0)操作,这回触发底层运算,其再fun2.o触发的操作如图,pmem+8即上图的m2
memcpy交给fun1.so继续
fun0.o的memcpy没有额外操作
即
再回到fun3.o的后续代码。图下,途中对vmcptr1即fun1代码做修改,
位置是hex(0x130*4)=0x4C0位置,
我们看下fun1.v.asm(即vm指令反汇编,不是x86反汇编fun1.asm)
如图,
实际是对fun1.o 的memset触发的代码的变动
其会交给下一层的fun2.o的memset处理,同理,会先触发fun1.so的memset操作(再往下是fun0.o),再调用后续fun2.so运算函数处理,如图
再fun1.o中,如图,对于大小100的操作,其会先运算,再调用memset,这时触发fun0.so层的memset;
fun1.o对于大小0x640的操作,会再memset前后加入运算。同理,memset会触发fun0.o的memset
fun0.o的情况如下,再特定参数如(dst,0x33333333,100)会先做一些操作再迁移数据,这时fun0.o触发的memset就是主程序的memset操作。
fun3.png 原始vm指令流程图
生成命令
附件内容
typedef struct vmi_t{
/
/
size:
0x20
int
flag;
int
next
;
void
*
vm_fptr;
int
vm_zs;
int
init;
int
vmID;
int
rev1;
int
rev2;
} vmi;
typedef struct _vmx4{
unsigne char
*
pbkey;
unsigne char
*
pmem;
void
*
vmx_table;
} vmx4;
vmi_t vmx_info_table[]
=
{
.
00
vix0{flag.
6
,
next
:
=
.
20
,vmcptr,vmzs,
0
,vmID.
0
,rev1,rev2}
.
20
vix1{flag.
6
,
next
:
=
.
40
,vmcptr,vmzs,
0
,vmID.
0
,rev1,rev2}
.
40
vix2{flag.
6
,
next
:
=
.
60
,vmcptr,vmzs,
0
,vmID.
0
,rev1,rev2}
.
60
vix3{flag.
3
,
next
:
=
.
80
,vmcptr,vmzs,
0
,vmID.
0
,rev1,rev2}
.
80
vix4{.
00hww
.pbkey,.
04hww
.pmem{.
00hww
.bkLen},&vmx_info_table[
0
]}
}
pmem{ dword m[]
.
00
bkLen
.
04
m1
.
08
m2 should
=
=
0xDE05629C
.
0C
.
10.
..
}
}
typedef struct vmi_t{
/
/
size:
0x20
int
flag;
int
next
;
void
*
vm_fptr;
int
vm_zs;
int
init;
int
vmID;
int
rev1;
int
rev2;
} vmi;
typedef struct _vmx4{
unsigne char
*
pbkey;
unsigne char
*
pmem;
void
*
vmx_table;
} vmx4;
vmi_t vmx_info_table[]
=
{
.
00
vix0{flag.
6
,
next
:
=
.
20
,vmcptr,vmzs,
0
,vmID.
0
,rev1,rev2}
.
20
vix1{flag.
6
,
next
:
=
.
40
,vmcptr,vmzs,
0
,vmID.
0
,rev1,rev2}
.
40
vix2{flag.
6
,
next
:
=
.
60
,vmcptr,vmzs,
0
,vmID.
0
,rev1,rev2}
.
60
vix3{flag.
3
,
next
:
=
.
80
,vmcptr,vmzs,
0
,vmID.
0
,rev1,rev2}
.
80
vix4{.
00hww
.pbkey,.
04hww
.pmem{.
00hww
.bkLen},&vmx_info_table[
0
]}
}
pmem{ dword m[]
.
00
bkLen
.
04
m1
.
08
m2 should
=
=
0xDE05629C
.
0C
.
10.
..
}
}
import
idc
idc.savefile(r
'.\403000_0790.bin'
,
0
,
0x403000
,
0x0790
)
idc.savefile(r
'.\403794_1CBD.bin'
,
0
,
0x403794
,
0x1CBD
)
idc.savefile(r
'.\405454_134A.bin'
,
0
,
0x405454
,
0x134A
)
idc.savefile(r
'.\4067A0_0309.bin'
,
0
,
0x4067A0
,
0x0309
)
import
idc
idc.savefile(r
'.\403000_0790.bin'
,
0
,
0x403000
,
0x0790
)
idc.savefile(r
'.\403794_1CBD.bin'
,
0
,
0x403794
,
0x1CBD
)
idc.savefile(r
'.\405454_134A.bin'
,
0
,
0x405454
,
0x134A
)
idc.savefile(r
'.\4067A0_0309.bin'
,
0
,
0x4067A0
,
0x0309
)
{
esi
=
vmi.vm_ptr
ebp
=
&vmi.vm_ptr
+
vmi.zone_size
*
2
edi
=
ebp <
-
-
-
-
vm_stack_bottom frame_ptr fr
py vm_push(
0x2B
)
px vm_push(
0x2B11
) <
-
-
-
-
vm_ret {push r;exit;exit;}
/
/
@eax
=
r
p3 vm_push(vmi.info_type)
p2 vm_push(vmi
*
vmi_ptr)
p1 vm_push(vm_ret)
cpu{;esi as pc;ebx as r;edi as frp;ebp as stp;
opcode
=
*
pc
switch(opcode){
case
00h
: lea_pb
case
01h
: lea_vb
case
02h
: lea_pdw
case
03h
: movzx_imm8
case
04h
: mov_imm32
case
06h
: jmp
case
07h
: call
case
08h
: jz
case
09h
: jnz
case
0Ah
: begin
case
0Bh
: stk_free
case
0Ch
: end
case
0Dh
: read_dw
case
0Eh
: read_b
case
0Fh
: write_dw
case
10h
: write_b
case
11h
: push
case
12h
:
or
case
13h
: xor
case
14h
:
and
case
15h
: eq
case
16h
: ne
case
17h
: lt
case
18h
: gt
case
1Ah
: ge
case
1Bh
: shl
case
1Ch
: shr
case
1Dh
: add
case
1Eh
: subr
case
1Fh
: imul
case
20h
: divr
case
21h
: modr
case
28h
: memset
case
2Ah
: memcpy
case
2Bh
: exit
}
}
}
{
esi
=
vmi.vm_ptr
ebp
=
&vmi.vm_ptr
+
vmi.zone_size
*
2
edi
=
ebp <
-
-
-
-
vm_stack_bottom frame_ptr fr
py vm_push(
0x2B
)
px vm_push(
0x2B11
) <
-
-
-
-
vm_ret {push r;exit;exit;}
/
/
@eax
=
r
p3 vm_push(vmi.info_type)
p2 vm_push(vmi
*
vmi_ptr)
p1 vm_push(vm_ret)
cpu{;esi as pc;ebx as r;edi as frp;ebp as stp;
opcode
=
*
pc
switch(opcode){
case
00h
: lea_pb
case
01h
: lea_vb
case
02h
: lea_pdw
case
03h
: movzx_imm8
case
04h
: mov_imm32
case
06h
: jmp
case
07h
: call
case
08h
: jz
case
09h
: jnz
case
0Ah
: begin
case
0Bh
: stk_free
case
0Ch
: end
case
0Dh
: read_dw
case
0Eh
: read_b
case
0Fh
: write_dw
case
10h
: write_b
case
11h
: push
case
12h
:
or
case
13h
: xor
case
14h
:
and
case
15h
: eq
case
16h
: ne
case
17h
: lt
case
18h
: gt
case
1Ah
: ge
case
1Bh
: shl
case
1Ch
: shr
case
1Dh
: add
case
1Eh
: subr
case
1Fh
: imul
case
20h
: divr
case
21h
: modr
case
28h
: memset
case
2Ah
: memcpy
case
2Bh
: exit
}
}
}
class
VmOps0(IntEnum):
lea_pb
=
0
lea_vb
=
1
lea_pww
=
2
movzx
=
3
mov
=
4
jmp
=
6
call
=
7
jz
=
8
jnz
=
9
begin
=
0x0A
stk_free
=
0x0B
end
=
0x0C
read_ww
=
0x0D
read_b
=
0x0E
write_ww
=
0x0F
write_b
=
0x10
push
=
0x11
_or
=
0x12
xor
=
0x13
_and
=
0x14
ifeq
=
0x15
ifne
=
0x16
iflt
=
0x17
ifgt
=
0x18
ifge
=
0x1A
shl
=
0x1B
shr
=
0x1C
add
=
0x1D
subr
=
0x1E
imul
=
0x1F
divr
=
0x20
modr
=
0x21
memset
=
0x28
memcpy
=
0x2A
exit
=
0x2B
class
VmOps0(IntEnum):
lea_pb
=
0
lea_vb
=
1
lea_pww
=
2
movzx
=
3
mov
=
4
jmp
=
6
call
=
7
jz
=
8
jnz
=
9
begin
=
0x0A
stk_free
=
0x0B
end
=
0x0C
read_ww
=
0x0D
read_b
=
0x0E
write_ww
=
0x0F
write_b
=
0x10
push
=
0x11
_or
=
0x12
xor
=
0x13
_and
=
0x14
ifeq
=
0x15
ifne
=
0x16
iflt
=
0x17
ifgt
=
0x18
ifge
=
0x1A
shl
=
0x1B
shr
=
0x1C
add
=
0x1D
subr
=
0x1E
imul
=
0x1F
divr
=
0x20
modr
=
0x21
memset
=
0x28
memcpy
=
0x2A
exit
=
0x2B
python ydisasm.py
yasm.exe
-
a x86
-
f coff
-
o fun0.o fun0.asm
yasm.exe
-
a x86
-
f coff
-
o fun0.o fun0.asm
class
VmOps1(IntEnum):
lea_pb
=
0x1B
lea_vb
=
0x07
lea_pww
=
0x05
movzx
=
0x24
mov
=
0x22
jmp
=
0x20
call
=
0x00
jz
=
0x0E
jnz
=
0x06
begin
=
0x09
stk_free
=
0x0A
end
=
0x01
read_ww
=
0x13
read_b
=
0x1C
write_ww
=
0x02
write_b
=
0x0D
push
=
0x29
_or
=
0x0C
xor
=
0x11
_and
=
0x1D
ifeq
=
0x2A
ifne
=
0x27
iflt
=
0x15
ifgt
=
0x1A
ifge
=
0x28
shl
=
0x10
shr
=
0x2B
add
=
3
subr
=
0x17
imul
=
0x08
divr
=
0x21
modr
=
0x26
memset
=
0x0B
memcpy
=
0x0F
exit
=
0x1F
class
VmOps1(IntEnum):
lea_pb
=
0x1B
lea_vb
=
0x07
lea_pww
=
0x05
movzx
=
0x24
mov
=
0x22
jmp
=
0x20
call
=
0x00
jz
=
0x0E
jnz
=
0x06
begin
=
0x09
stk_free
=
0x0A
end
=
0x01
read_ww
=
0x13
read_b
=
0x1C
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2020-11-28 16:47
被HHHso编辑
,原因: