多层自解密,每一层会先清空上一层,解密下一层,然后跳过去。每层的解密逻辑都不完全相同
真实指令分散在每一层中,还有call-pop-add之类的混淆
有反调试,不能直接调试器启动,但可以启动到输入之后再x64dbg附加。
发现输入之后会进入0x140001000的函数对serial做hexdecode,这个函数位置总是固定的。
在它的ret指令处(0x140001084)下断点,r10寄存器指示了serial保存的地方,在这里下硬件读写断点。
一次F9到达断点,是一句rep movsq指令,在此时开始运行下面的脚本:跟踪rep movsq指令对serial的复制,并对复制的目标位置下硬件断点。
(注:$result+0xc是调整后的结果,因为真正处理serial的代码读的并不是第0个字节,而是第0xc个字节)
几秒钟后脚本停下来,是movzx r12d, byte ptr [r13 + 0x25]
指令,这是真正开始处理serial的地方。
dump内存,记录下所有寄存器的值(以及gs:[0x8]和gs:[0x10]两个位置的值)
考虑用unicorn模拟执行+capstone反汇编,目的是方便的跟踪程序执行流程以及后续的分析。
直接写了一个基础版本,发现根本跑不完(毕竟原始程序直接执行都需要几秒钟)
基于模拟执行的分析肯定要反复尝试,速度必须要足够快,因此做了很多努力来加速:
现在,程序能够在1分钟以内生成完整的trace,这个速度差不多可以接受了。
trace有大约80万行,关键指令又分散在垃圾指令中,还是不能人工分析。
下一步考虑基于trace做数据流跟踪(受到 https://bbs.pediy.com/thread-258262.htm 这篇文章启发),看输入的serial从内存中被读出来以及后续计算的流程。
对于任何一条语句,都有来源操作数(内存/寄存器/常量)和目的操作数(内存/寄存器),如果任何一个来源操作数与serial有关("tainted"),则相关的目的操作数也要标记为"tainted",相反的,如果所有来源操作数都没有tainted,则所有目的操作数的tainted标记都要取消。
实现的难度很大,capstone好像没有办法直接获得一条指令相关的源寄存器和目的寄存器(也可能有办法,只是我不知道),只好手动处理部分指令,目标是零漏报的基础上尽可能减少误报。
代码如下:
代码写的不好,修了很久的bug,但是生成的trace还存在问题。
加上数据流跟踪之后,跑一遍trace大约需要2分钟。
附件trace.rar里的t6.txt文件是以KCTF为name、以给出的公开serial为serial作为输入得到的结果。用下面的代码筛选出trace到的指令:
筛选结果在tt6.txt中,大约2500行,这个长度人工分析是可以接受的。
基于数据流分析的结果是可以做死代码消除的,由于没有做,还是有很多垃圾指令。(希望有一种方便的办法能够生成可执行的程序让ida帮忙做这件事)
突破口是读写内存的指令,从这些地方逐渐向上找。
最终分析下来的流程是:
(数据流跟踪的代码有bug导致这里有些指令的trace丢失了(特别是第3步里对前8字节的处理),只好根据上下文去猜)
serial的验证以及从name生成serial的程序如下:
附件:
memdump.rar:运行到真正的验证逻辑首次开始读取serial时的内存状态以及寄存器状态,用于模拟执行
trace.rar:基于模拟执行生成的完整trace文件以及过滤结果。t6.txt和tt6.txt直接对应memdump,输入为KCTF / 公开的序列号;t7.txt和tt7.txt则是在unicorn中把serial对应的内存覆盖为真正的serial后得到的结果。
loop:
find rip,
"F348A5"
,
3
cmp
$result,
0
je out
bphc
sti
sti
find rdi
-
0x60
,
"7D4DBA7A"
,
0xc0
bph $result
+
0xc
, r,
1
run
jmp loop
out:
loop:
find rip,
"F348A5"
,
3
cmp
$result,
0
je out
bphc
sti
sti
find rdi
-
0x60
,
"7D4DBA7A"
,
0xc0
bph $result
+
0xc
, r,
1
run
jmp loop
out:
from
unicorn
import
*
from
unicorn.x86_const
import
*
from
capstone
import
*
from
capstone.x86_const
import
*
import
traceback
import
ctypes
libdodecryptblock
=
ctypes.CDLL(
"./libdodecryptblock.dll"
)
global_tainted_memlist
=
set
()
global_tainted_reglist
=
set
()
global_last_inst
=
None
class
LoopContext:
__slots__
=
[
"tracing"
,
"tracecount"
,
"operations"
,
"direction"
,
"address"
,
"memreg"
]
def
__init__(
self
):
self
.clear()
def
clear(
self
):
self
.tracing
=
False
self
.tracecount
=
0
self
.operations
=
[]
self
.direction
=
None
self
.address
=
None
self
.memreg
=
None
class
HoopPrintContext:
__slots__
=
[
"initial_rsp"
,
"last_rsp"
,
"last_address"
,
"last_inst"
]
def
__init__(
self
):
self
.initial_rsp
=
None
self
.last_rsp
=
None
self
.last_address
=
None
self
.last_inst
=
None
def
handlekeyboardinterupt(func):
def
wrapper_func(
*
args,
*
*
kwargs):
try
:
r
=
func(
*
args,
*
*
kwargs)
except
KeyboardInterrupt:
import
os
os._exit(
0
)
except
Exception:
traceback.print_exc()
import
os
import
sys
sys.stdout.flush()
sys.stderr.flush()
os._exit(
0
)
return
r
return
wrapper_func
def
capstone_reg_to_unicorn_reg(i):
d
=
{
X86_REG_RAX : UC_X86_REG_RAX,
X86_REG_RBX : UC_X86_REG_RBX,
X86_REG_RCX : UC_X86_REG_RCX,
X86_REG_RDX : UC_X86_REG_RDX,
X86_REG_RBP : UC_X86_REG_RBP,
X86_REG_RSP : UC_X86_REG_RSP,
X86_REG_RSI : UC_X86_REG_RSI,
X86_REG_RDI : UC_X86_REG_RDI,
X86_REG_R8 : UC_X86_REG_R8,
X86_REG_R9 : UC_X86_REG_R9,
X86_REG_R10 : UC_X86_REG_R10,
X86_REG_R11 : UC_X86_REG_R11,
X86_REG_R12 : UC_X86_REG_R12,
X86_REG_R13 : UC_X86_REG_R13,
X86_REG_R14 : UC_X86_REG_R14,
X86_REG_R15 : UC_X86_REG_R15,
X86_REG_AL : UC_X86_REG_AL,
X86_REG_BL : UC_X86_REG_BL,
X86_REG_CL : UC_X86_REG_CL,
X86_REG_DL : UC_X86_REG_DL,
X86_REG_SIL : UC_X86_REG_SIL,
X86_REG_DIL : UC_X86_REG_DIL,
X86_REG_RIP : UC_X86_REG_RIP,
}
return
d[i]
def
capstone_reg_to_normal_reg(i):
d
=
{
X86_REG_RAX : X86_REG_RAX,
X86_REG_RBX : X86_REG_RBX,
X86_REG_RCX : X86_REG_RCX,
X86_REG_RDX : X86_REG_RDX,
X86_REG_RBP : X86_REG_RBP,
X86_REG_RSP : X86_REG_RSP,
X86_REG_RSI : X86_REG_RSI,
X86_REG_RDI : X86_REG_RDI,
X86_REG_R8 : X86_REG_R8,
X86_REG_R9 : X86_REG_R9,
X86_REG_R10 : X86_REG_R10,
X86_REG_R11 : X86_REG_R11,
X86_REG_R12 : X86_REG_R12,
X86_REG_R13 : X86_REG_R13,
X86_REG_R14 : X86_REG_R14,
X86_REG_R15 : X86_REG_R15,
X86_REG_EAX : X86_REG_RAX,
X86_REG_EBX : X86_REG_RBX,
X86_REG_ECX : X86_REG_RCX,
X86_REG_EDX : X86_REG_RDX,
X86_REG_EBP : X86_REG_RBP,
X86_REG_ESP : X86_REG_RSP,
X86_REG_ESI : X86_REG_RSI,
X86_REG_EDI : X86_REG_RDI,
X86_REG_R8D : X86_REG_R8,
X86_REG_R9D : X86_REG_R9,
X86_REG_R10D : X86_REG_R10,
X86_REG_R11D : X86_REG_R11,
X86_REG_R12D : X86_REG_R12,
X86_REG_R13D : X86_REG_R13,
X86_REG_R14D : X86_REG_R14,
X86_REG_R15D : X86_REG_R15,
X86_REG_AX : X86_REG_RAX,
X86_REG_BX : X86_REG_RBX,
X86_REG_CX : X86_REG_RCX,
X86_REG_DX : X86_REG_RDX,
X86_REG_BP : X86_REG_RBP,
X86_REG_SP : X86_REG_RSP,
X86_REG_SI : X86_REG_RSI,
X86_REG_DI : X86_REG_RDI,
X86_REG_R8W : X86_REG_R8,
X86_REG_R9W : X86_REG_R9,
X86_REG_R10W : X86_REG_R10,
X86_REG_R11W : X86_REG_R11,
X86_REG_R12W : X86_REG_R12,
X86_REG_R13W : X86_REG_R13,
X86_REG_R14W : X86_REG_R14,
X86_REG_R15W : X86_REG_R15,
X86_REG_AL : X86_REG_RAX,
X86_REG_AH : X86_REG_RAX,
X86_REG_BL : X86_REG_RBX,
X86_REG_BH : X86_REG_RBX,
X86_REG_CL : X86_REG_RCX,
X86_REG_CH : X86_REG_RCX,
X86_REG_DL : X86_REG_RDX,
X86_REG_DH : X86_REG_RDX,
X86_REG_BPL : X86_REG_RBP,
X86_REG_SIL : X86_REG_RSI,
X86_REG_DIL : X86_REG_RDI,
X86_REG_R8B : X86_REG_R8,
X86_REG_R9B : X86_REG_R9,
X86_REG_R10B : X86_REG_R10,
X86_REG_R11B : X86_REG_R11,
X86_REG_R12B : X86_REG_R12,
X86_REG_R13B : X86_REG_R13,
X86_REG_R14B : X86_REG_R14,
X86_REG_R15B : X86_REG_R15,
X86_REG_RIP : X86_REG_RIP,
X86_REG_EFLAGS : X86_REG_EFLAGS,
}
return
d[i]
def
p64(n):
n &
=
0xffffffffffffffff
return
n.to_bytes(
8
,
'little'
)
def
u64(s):
assert
(
len
(s)
=
=
8
)
return
int
.from_bytes(s,
'little'
)
def
ror8(n, c):
return
((n>>c)|(n<<(
8
-
c))) &
0xff
def
rol8(n, c):
return
((n<<c)|(n>>(
8
-
c))) &
0xff
def
sar8(n, c):
if
n &
0x80
:
return
n >> c
else
:
return
((n >> c) | (
0xff
<< (
8
-
c))) &
0xff
with
open
(
"MEM5_143948F5D_0000000140000000_0691E000.mem"
,
"rb"
) as f:
X86_CODE64
=
f.read()
assert
(
len
(X86_CODE64)
=
=
0x691E000
)
md
=
Cs(CS_ARCH_X86, CS_MODE_64)
md.detail
=
True
global_rdtsc_value
=
0
@handlekeyboardinterupt
def
hook_code64(uc, address, size, user_data):
global
global_rdtsc_value
global
global_serial_start
instbytes
=
uc.mem_read(address, size)
inst
=
list
(md.disasm(instbytes, address))[
0
]
print
(
"0x%x\t%s %s"
%
(address, inst.mnemonic, inst.op_str))
rip
=
address
if
instbytes
=
=
b
"\x0f\x31"
:
uc.reg_write(UC_X86_REG_RDX,
0
)
uc.reg_write(UC_X86_REG_RAX, global_rdtsc_value)
global_rdtsc_value
+
=
1000000
uc.reg_write(UC_X86_REG_RIP, rip
+
size)
elif
instbytes
=
=
b
"\xF3\x48\xA5"
:
rsi
=
uc.reg_read(UC_X86_REG_RSI)
rdi
=
uc.reg_read(UC_X86_REG_RDI)
rcx
=
uc.reg_read(UC_X86_REG_RCX)
old_global_tainted_memlist
=
set
(global_tainted_memlist)
for
a
in
old_global_tainted_memlist:
if
rdi <
=
a < (rdi
+
rcx
*
8
):
global_tainted_memlist.remove(a)
for
a
in
old_global_tainted_memlist:
if
rsi <
=
a < (rsi
+
rcx
*
8
):
global_tainted_memlist.add(a
+
rdi
-
rsi)
uc.mem_write(rdi, bytes(uc.mem_read(rsi, rcx
*
8
)))
uc.reg_write(UC_X86_REG_RCX,
0
)
uc.reg_write(UC_X86_REG_RDI, rdi
+
rcx
*
8
)
uc.reg_write(UC_X86_REG_RSI, rsi
+
rcx
*
8
)
uc.reg_write(UC_X86_REG_RIP, rip
+
size)
elif
instbytes
=
=
b
"\xF3\x48\xAB"
:
rax
=
uc.reg_read(UC_X86_REG_RAX)
rdi
=
uc.reg_read(UC_X86_REG_RDI)
rcx
=
uc.reg_read(UC_X86_REG_RCX)
for
a
in
set
(global_tainted_memlist):
if
rdi <
=
a < rdi
+
rcx
*
8
:
global_tainted_memlist.remove(a)
uc.mem_write(rdi, p64(rax)
*
rcx)
uc.reg_write(UC_X86_REG_RDI, rdi
+
rcx
*
8
)
uc.reg_write(UC_X86_REG_RIP, rip
+
size)
elif
instbytes
in
[b
"\x48\x83\xe9\x01"
, b
"\x83\xe9\x01"
, b
"\x48\xff\xc9"
, b
"\xff\xc9"
]:
if
not
user_data.tracing:
user_data.clear()
user_data.tracing
=
True
user_data.address
=
address
else
:
assert
(address
=
=
user_data.address)
user_data.tracing
=
False
rcx
=
uc.reg_read(UC_X86_REG_RCX)
memreg
=
uc.reg_read(capstone_reg_to_unicorn_reg(user_data.memreg))
tmp
=
uc.mem_read(memreg, rcx
-
1
)
jitcode
=
compile
(
"\n"
.join(user_data.operations), "
", mode="
exec
")
buf
=
bytearray(
256
)
for
i
in
range
(
256
):
c
=
i
g
=
{
"c"
:c,
"ror8"
:ror8,
"rol8"
:rol8}
exec
(jitcode, g)
c
=
g[
"c"
]
buf[i]
=
c
tmpb
=
ctypes.create_string_buffer(bytes(tmp),
len
(tmp))
libdodecryptblock.dodecryptblock(ctypes.byref(tmpb),
len
(tmp), ctypes.byref(ctypes.create_string_buffer(bytes(buf),
len
(buf))))
tmp
=
tmpb.raw
uc.mem_write(memreg, bytes(tmp))
uc.reg_write(capstone_reg_to_unicorn_reg(user_data.memreg), memreg
+
user_data.direction
*
(rcx
-
1
))
uc.reg_write(UC_X86_REG_RCX,
1
)
elif
user_data.tracing:
print
(
"tracing"
)
if
user_data.tracecount >
=
100
:
user_data.tracing
=
False
else
:
user_data.tracecount
+
=
1
inst
=
list
(md.disasm(instbytes, address))[
0
]
operands
=
list
(inst.operands)
if
inst.
id
=
=
X86_INS_MOV:
if
operands[
0
].
type
=
=
X86_OP_REG
and
operands[
1
].
type
=
=
X86_OP_MEM:
if
operands[
0
].reg
=
=
X86_REG_AL:
user_data.memreg
=
operands[
1
].mem.base
elif
inst.
id
in
[X86_INS_ADD, X86_INS_SUB, X86_INS_XOR, X86_INS_AND, X86_INS_OR, X86_INS_ROR, X86_INS_ROL]:
if
operands[
0
].
type
=
=
X86_OP_REG
and
operands[
0
].reg
=
=
X86_REG_AL:
if
operands[
1
].
type
=
=
X86_OP_REG:
v
=
uc.reg_read(capstone_reg_to_unicorn_reg(operands[
1
].reg))
elif
operands[
1
].
type
=
=
X86_OP_IMM:
v
=
operands[
1
].imm
else
:
assert
(
0
)
if
inst.
id
=
=
X86_INS_ADD:
pyinst
=
f
"c = (c + {v}) & 0xff"
elif
inst.
id
=
=
X86_INS_SUB:
pyinst
=
f
"c = (c - {v}) & 0xff"
elif
inst.
id
=
=
X86_INS_XOR:
pyinst
=
f
"c = (c ^ {v}) & 0xff"
elif
inst.
id
=
=
X86_INS_AND:
pyinst
=
f
"c = (c & {v}) & 0xff"
elif
inst.
id
=
=
X86_INS_OR:
pyinst
=
f
"c = (c | {v}) & 0xff"
elif
inst.
id
=
=
X86_INS_ROR:
pyinst
=
f
"c = ror8(c, {v})"
elif
inst.
id
=
=
X86_INS_ROL:
pyinst
=
f
"c = rol8(c, {v})"
else
:
assert
(
0
)
user_data.operations.append(pyinst)
elif
inst.
id
in
[X86_INS_NOT, X86_INS_NEG, X86_INS_INC, X86_INS_DEC]:
if
operands[
0
].
type
=
=
X86_OP_REG
and
operands[
0
].reg
=
=
X86_REG_AL:
if
inst.
id
=
=
X86_INS_NOT:
pyinst
=
f
"c = (~c) & 0xff"
elif
inst.
id
=
=
X86_INS_NEG:
pyinst
=
f
"c = (-c) & 0xff"
elif
inst.
id
=
=
X86_INS_INC:
pyinst
=
f
"c = (c + 1) & 0xff"
elif
inst.
id
=
=
X86_INS_DEC:
pyinst
=
f
"c = (c - 1) & 0xff"
else
:
assert
(
0
)
user_data.operations.append(pyinst)
elif
operands[
0
].
type
=
=
X86_OP_REG
and
operands[
0
].reg
=
=
user_data.memreg:
if
inst.
id
=
=
X86_INS_INC:
user_data.direction
=
1
elif
inst.
id
=
=
X86_INS_DEC:
user_data.direction
=
-
1
else
:
pass
else
:
assert
(
"al"
not
in
inst.op_str.split(
','
)[
0
])
pass
@handlekeyboardinterupt
def
hook_code64_2(uc, address, size, user_data):
global
global_last_inst
instbytes
=
uc.mem_read(address, size)
inst
=
list
(md.disasm(instbytes, address))[
0
]
operands
=
list
(inst.operands)
global_last_inst
=
inst
src_regs
=
set
()
target_regs
=
set
()
if
len
(operands) >
0
:
leftopreand
=
operands[
0
]
rightopreand
=
operands[
1
]
if
len
(operands) >
1
else
operands[
0
]
tainted
=
False
if
rightopreand.
type
=
=
X86_OP_REG:
rr
=
rightopreand.reg
src_regs.add(rr)
elif
rightopreand.
type
=
=
X86_OP_MEM:
segment
=
rightopreand.mem.segment
base
=
rightopreand.mem.base
index
=
rightopreand.mem.index
scale
=
rightopreand.mem.scale
disp
=
rightopreand.mem.disp
if
base !
=
0
:
src_regs.add(base)
if
index !
=
0
:
src_regs.add(index)
memaddr
=
disp
+
(uc.reg_read(capstone_reg_to_unicorn_reg(base))
if
base !
=
0
else
0
)
+
(uc.reg_read(capstone_reg_to_unicorn_reg(index))
if
index !
=
0
else
0
)
*
scale
memvalue
=
u64(uc.mem_read(memaddr,
8
))
if
inst.
id
!
=
X86_INS_LEA
and
segment
=
=
0
else
memaddr
if
inst.
id
!
=
X86_INS_LEA
and
((base !
=
0
and
capstone_reg_to_normal_reg(base)
in
global_tainted_reglist)
or
(index !
=
0
and
capstone_reg_to_normal_reg(index)
in
global_tainted_reglist))
and
memaddr
not
in
global_tainted_memlist:
global_tainted_memlist.add(memaddr)
if
leftopreand.
type
=
=
X86_OP_REG:
rw
=
leftopreand.reg
target_regs.add(rw)
if
inst.
id
!
=
X86_INS_MOV
and
inst.
id
!
=
X86_INS_LEA:
src_regs.add(rw)
tainted
=
False
for
rr
in
src_regs:
rr_n
=
capstone_reg_to_normal_reg(rr)
if
rr_n
in
global_tainted_reglist:
tainted
=
True
break
if
(inst.
id
in
[X86_INS_XOR, X86_INS_SUB, X86_INS_AND])
and
operands[
0
].
type
=
=
X86_OP_REG
and
operands[
1
].
type
=
=
X86_OP_REG
and
operands[
0
].reg
=
=
operands[
1
].reg:
tainted
=
False
if
inst.
id
=
=
X86_INS_AND:
target_regs
=
set
()
if
len
(operands) >
0
and
operands[
0
].
type
=
=
X86_OP_REG
and
operands[
0
].reg
=
=
X86_REG_RSP:
tainted
=
False
target_regs
=
set
()
if
inst.
id
=
=
X86_INS_XCHG
and
operands[
0
].
type
=
=
X86_OP_REG
and
operands[
1
].
type
=
=
X86_OP_REG:
tainted
=
False
target_regs
=
set
()
r1
=
operands[
0
].reg
r2
=
operands[
1
].reg
if
(
not
r1
in
global_tainted_reglist)
and
r2
in
global_tainted_reglist:
r1, r2
=
r2, r1
if
r1
in
global_tainted_reglist
and
not
r2
in
global_tainted_reglist:
tainted
=
False
if
r1
in
global_tainted_reglist:
global_tainted_reglist.remove(r1)
if
r2
not
in
global_tainted_reglist:
global_tainted_reglist.add(r2)
elif
r1
in
global_tainted_reglist
and
r2
in
global_tainted_reglist:
tainted
=
True
target_regs.add(r1, r2)
if
inst.
id
in
[X86_INS_CMP, X86_INS_NOT, X86_INS_POP, X86_INS_PUSH]
or
"cmov"
in
inst.mnemonic:
tainted
=
False
target_regs
=
set
()
for
rw
in
target_regs:
rw_n
=
capstone_reg_to_normal_reg(rw)
if
tainted:
lv
=
uc.reg_read(leftopreand.reg)
if
leftopreand.
type
=
=
X86_OP_REG
and
inst.
id
!
=
X86_INS_MOV
else
0xdeadbeefdeadbeefdeadbeef
rv
=
uc.reg_read(rightopreand.reg)
if
rightopreand.
type
=
=
X86_OP_REG
else
(rightopreand.imm
if
rightopreand.
type
=
=
X86_OP_IMM
else
memvalue)
print
(
"\ttainted: 0x%x, 0x%x"
%
(lv, rv))
if
rw_n
not
in
global_tainted_reglist:
global_tainted_reglist.add(rw_n)
else
:
if
rw_n
in
global_tainted_reglist:
global_tainted_reglist.remove(rw_n)
@handlekeyboardinterupt
def
hook_mem_access(uc, access, address, size, value, user_data):
global
global_last_inst
inst
=
global_last_inst
operands
=
list
(inst.operands)
if
access
=
=
UC_MEM_WRITE:
rr
=
None
if
inst.
id
=
=
X86_INS_PUSH:
if
operands[
0
].
type
=
=
X86_OP_REG:
rr
=
operands[
0
].reg
elif
inst.
id
=
=
X86_INS_MOV:
if
operands[
1
].
type
=
=
X86_OP_REG:
rr
=
operands[
1
].reg
tainted
=
False
if
rr
and
capstone_reg_to_normal_reg(rr)
in
global_tainted_reglist:
tainted
=
True
print
(
"\ttainted >>> Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x"
\
%
(address, size, value &
0xffffffffffffffff
))
else
:
pass
for
i
in
range
(size):
if
tainted:
if
(address
+
i)
not
in
global_tainted_memlist:
global_tainted_memlist.add(address
+
i)
else
:
if
(address
+
i)
in
global_tainted_memlist:
global_tainted_memlist.remove(address
+
i)
else
:
tainted
=
False
if
address
in
global_tainted_memlist:
tainted
=
True
rw
=
None
if
len
(operands) >
0
and
operands[
0
].
type
=
=
X86_OP_REG:
rw
=
operands[
0
].reg
if
rw:
rw_n
=
capstone_reg_to_normal_reg(rw)
if
tainted:
print
(
"\ttainted >>> Memory is being READ at 0x%x, data size = %u, data value = 0x%x"
\
%
(address, size,
int
.from_bytes(uc.mem_read(address, size),
'little'
)))
if
rw_n
not
in
global_tainted_reglist:
global_tainted_reglist.add(rw_n)
else
:
if
rw_n
in
global_tainted_reglist:
global_tainted_reglist.remove(rw_n)
ADDRESS
=
0x140000000
ADDRESS_SPACESIZE
=
0x691E000
GS_BASE
=
0x1000
mu
=
Uc(UC_ARCH_X86, UC_MODE_64)
mu.mem_map(ADDRESS, ADDRESS_SPACESIZE)
mu.mem_write(ADDRESS, X86_CODE64)
mu.reg_write(UC_X86_REG_GS_BASE,
0x1000
)
mu.mem_map(GS_BASE,
0x1000
)
mu.mem_write(GS_BASE
+
8
, p64(
0x1443BB3000
))
mu.mem_write(GS_BASE
+
0x10
, p64(
0x143BB0000
))
mu.reg_write(UC_X86_REG_RAX,
0xC3C3C3C3C3C3C3DF
)
mu.reg_write(UC_X86_REG_RBX,
0x0000000140277000
)
mu.reg_write(UC_X86_REG_RCX,
0x0000000000000000
)
mu.reg_write(UC_X86_REG_RDX,
0x000000000027BA61
)
mu.reg_write(UC_X86_REG_RBP,
0x000000014032C4DE
)
mu.reg_write(UC_X86_REG_RSP,
0x0000000143BB2EC0
)
mu.reg_write(UC_X86_REG_RSI,
0x000000014027A000
)
mu.reg_write(UC_X86_REG_RDI,
0x000000014059DE7A
)
mu.reg_write(UC_X86_REG_R8,
0x000000004032C4DE
)
mu.reg_write(UC_X86_REG_R9,
0x000000014027E065
)
mu.reg_write(UC_X86_REG_R10,
0x0000000140001000
)
mu.reg_write(UC_X86_REG_R11,
0x0000000146680000
)
mu.reg_write(UC_X86_REG_R12,
0x0000000000000080
)
mu.reg_write(UC_X86_REG_R13,
0x0000000143BAF4F3
)
mu.reg_write(UC_X86_REG_R14,
0x0000000000000000
)
mu.reg_write(UC_X86_REG_R15,
0x000000000000000
)
mu.reg_write(UC_X86_REG_EFLAGS,
0x0000000000000014
)
mu.reg_write(UC_X86_REG_RIP,
0x0000000143948F58
)
lc
=
LoopContext()
hpc
=
HoopPrintContext()
mu.hook_add(UC_HOOK_CODE, hook_code64, lc)
mu.hook_add(UC_HOOK_CODE, hook_code64_2,
None
)
mu.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, hook_mem_access)
for
i
in
range
(
32
):
global_tainted_memlist.add(
0x143BAF50C
+
i)
try
:
mu.emu_start(
0x0000000143948F58
, ADDRESS
+
ADDRESS_SPACESIZE)
except
KeyboardInterrupt:
import
os
os._exit(
0
)
from
unicorn
import
*
from
unicorn.x86_const
import
*
from
capstone
import
*
from
capstone.x86_const
import
*
import
traceback
import
ctypes
libdodecryptblock
=
ctypes.CDLL(
"./libdodecryptblock.dll"
)
global_tainted_memlist
=
set
()
global_tainted_reglist
=
set
()
global_last_inst
=
None
class
LoopContext:
__slots__
=
[
"tracing"
,
"tracecount"
,
"operations"
,
"direction"
,
"address"
,
"memreg"
]
def
__init__(
self
):
self
.clear()
def
clear(
self
):
self
.tracing
=
False
self
.tracecount
=
0
self
.operations
=
[]
self
.direction
=
None
self
.address
=
None
self
.memreg
=
None
class
HoopPrintContext:
__slots__
=
[
"initial_rsp"
,
"last_rsp"
,
"last_address"
,
"last_inst"
]
def
__init__(
self
):
self
.initial_rsp
=
None
self
.last_rsp
=
None
self
.last_address
=
None
self
.last_inst
=
None
def
handlekeyboardinterupt(func):
def
wrapper_func(
*
args,
*
*
kwargs):
try
:
r
=
func(
*
args,
*
*
kwargs)
except
KeyboardInterrupt:
import
os
os._exit(
0
)
except
Exception:
traceback.print_exc()
import
os
import
sys
sys.stdout.flush()
sys.stderr.flush()
os._exit(
0
)
return
r
return
wrapper_func
def
capstone_reg_to_unicorn_reg(i):
d
=
{
X86_REG_RAX : UC_X86_REG_RAX,
X86_REG_RBX : UC_X86_REG_RBX,
X86_REG_RCX : UC_X86_REG_RCX,
X86_REG_RDX : UC_X86_REG_RDX,
X86_REG_RBP : UC_X86_REG_RBP,
X86_REG_RSP : UC_X86_REG_RSP,
X86_REG_RSI : UC_X86_REG_RSI,
X86_REG_RDI : UC_X86_REG_RDI,
X86_REG_R8 : UC_X86_REG_R8,
X86_REG_R9 : UC_X86_REG_R9,
X86_REG_R10 : UC_X86_REG_R10,
X86_REG_R11 : UC_X86_REG_R11,
X86_REG_R12 : UC_X86_REG_R12,
X86_REG_R13 : UC_X86_REG_R13,
X86_REG_R14 : UC_X86_REG_R14,
X86_REG_R15 : UC_X86_REG_R15,
X86_REG_AL : UC_X86_REG_AL,
X86_REG_BL : UC_X86_REG_BL,
X86_REG_CL : UC_X86_REG_CL,
X86_REG_DL : UC_X86_REG_DL,
X86_REG_SIL : UC_X86_REG_SIL,
X86_REG_DIL : UC_X86_REG_DIL,
X86_REG_RIP : UC_X86_REG_RIP,
}
return
d[i]
def
capstone_reg_to_normal_reg(i):
d
=
{
X86_REG_RAX : X86_REG_RAX,
X86_REG_RBX : X86_REG_RBX,
X86_REG_RCX : X86_REG_RCX,
X86_REG_RDX : X86_REG_RDX,
X86_REG_RBP : X86_REG_RBP,
X86_REG_RSP : X86_REG_RSP,
X86_REG_RSI : X86_REG_RSI,
X86_REG_RDI : X86_REG_RDI,
X86_REG_R8 : X86_REG_R8,
X86_REG_R9 : X86_REG_R9,
X86_REG_R10 : X86_REG_R10,
X86_REG_R11 : X86_REG_R11,
X86_REG_R12 : X86_REG_R12,
X86_REG_R13 : X86_REG_R13,
X86_REG_R14 : X86_REG_R14,
X86_REG_R15 : X86_REG_R15,
X86_REG_EAX : X86_REG_RAX,
X86_REG_EBX : X86_REG_RBX,
X86_REG_ECX : X86_REG_RCX,
X86_REG_EDX : X86_REG_RDX,
X86_REG_EBP : X86_REG_RBP,
X86_REG_ESP : X86_REG_RSP,
X86_REG_ESI : X86_REG_RSI,
X86_REG_EDI : X86_REG_RDI,
X86_REG_R8D : X86_REG_R8,
X86_REG_R9D : X86_REG_R9,
X86_REG_R10D : X86_REG_R10,
X86_REG_R11D : X86_REG_R11,
X86_REG_R12D : X86_REG_R12,
X86_REG_R13D : X86_REG_R13,
X86_REG_R14D : X86_REG_R14,
X86_REG_R15D : X86_REG_R15,
X86_REG_AX : X86_REG_RAX,
X86_REG_BX : X86_REG_RBX,
X86_REG_CX : X86_REG_RCX,
X86_REG_DX : X86_REG_RDX,
X86_REG_BP : X86_REG_RBP,
X86_REG_SP : X86_REG_RSP,
X86_REG_SI : X86_REG_RSI,
X86_REG_DI : X86_REG_RDI,
X86_REG_R8W : X86_REG_R8,
X86_REG_R9W : X86_REG_R9,
X86_REG_R10W : X86_REG_R10,
X86_REG_R11W : X86_REG_R11,
X86_REG_R12W : X86_REG_R12,
X86_REG_R13W : X86_REG_R13,
X86_REG_R14W : X86_REG_R14,
X86_REG_R15W : X86_REG_R15,
X86_REG_AL : X86_REG_RAX,
X86_REG_AH : X86_REG_RAX,
X86_REG_BL : X86_REG_RBX,
X86_REG_BH : X86_REG_RBX,
X86_REG_CL : X86_REG_RCX,
X86_REG_CH : X86_REG_RCX,
X86_REG_DL : X86_REG_RDX,
X86_REG_DH : X86_REG_RDX,
X86_REG_BPL : X86_REG_RBP,
X86_REG_SIL : X86_REG_RSI,
X86_REG_DIL : X86_REG_RDI,
X86_REG_R8B : X86_REG_R8,
X86_REG_R9B : X86_REG_R9,
X86_REG_R10B : X86_REG_R10,
X86_REG_R11B : X86_REG_R11,
X86_REG_R12B : X86_REG_R12,
X86_REG_R13B : X86_REG_R13,
X86_REG_R14B : X86_REG_R14,
X86_REG_R15B : X86_REG_R15,
X86_REG_RIP : X86_REG_RIP,
X86_REG_EFLAGS : X86_REG_EFLAGS,
}
return
d[i]
def
p64(n):
n &
=
0xffffffffffffffff
return
n.to_bytes(
8
,
'little'
)
def
u64(s):
assert
(
len
(s)
=
=
8
)
return
int
.from_bytes(s,
'little'
)
def
ror8(n, c):
return
((n>>c)|(n<<(
8
-
c))) &
0xff
def
rol8(n, c):
return
((n<<c)|(n>>(
8
-
c))) &
0xff
def
sar8(n, c):
if
n &
0x80
:
return
n >> c
else
:
return
((n >> c) | (
0xff
<< (
8
-
c))) &
0xff
with
open
(
"MEM5_143948F5D_0000000140000000_0691E000.mem"
,
"rb"
) as f:
X86_CODE64
=
f.read()
assert
(
len
(X86_CODE64)
=
=
0x691E000
)
md
=
Cs(CS_ARCH_X86, CS_MODE_64)
md.detail
=
True
global_rdtsc_value
=
0
@handlekeyboardinterupt
def
hook_code64(uc, address, size, user_data):
global
global_rdtsc_value
global
global_serial_start
instbytes
=
uc.mem_read(address, size)
inst
=
list
(md.disasm(instbytes, address))[
0
]
print
(
"0x%x\t%s %s"
%
(address, inst.mnemonic, inst.op_str))
rip
=
address
if
instbytes
=
=
b
"\x0f\x31"
:
uc.reg_write(UC_X86_REG_RDX,
0
)
uc.reg_write(UC_X86_REG_RAX, global_rdtsc_value)
global_rdtsc_value
+
=
1000000
uc.reg_write(UC_X86_REG_RIP, rip
+
size)
elif
instbytes
=
=
b
"\xF3\x48\xA5"
:
rsi
=
uc.reg_read(UC_X86_REG_RSI)
rdi
=
uc.reg_read(UC_X86_REG_RDI)
rcx
=
uc.reg_read(UC_X86_REG_RCX)
old_global_tainted_memlist
=
set
(global_tainted_memlist)
for
a
in
old_global_tainted_memlist:
if
rdi <
=
a < (rdi
+
rcx
*
8
):
global_tainted_memlist.remove(a)
for
a
in
old_global_tainted_memlist:
if
rsi <
=
a < (rsi
+
rcx
*
8
):
global_tainted_memlist.add(a
+
rdi
-
rsi)
uc.mem_write(rdi, bytes(uc.mem_read(rsi, rcx
*
8
)))
uc.reg_write(UC_X86_REG_RCX,
0
)
uc.reg_write(UC_X86_REG_RDI, rdi
+
rcx
*
8
)
uc.reg_write(UC_X86_REG_RSI, rsi
+
rcx
*
8
)
uc.reg_write(UC_X86_REG_RIP, rip
+
size)
elif
instbytes
=
=
b
"\xF3\x48\xAB"
:
rax
=
uc.reg_read(UC_X86_REG_RAX)
rdi
=
uc.reg_read(UC_X86_REG_RDI)
rcx
=
uc.reg_read(UC_X86_REG_RCX)
for
a
in
set
(global_tainted_memlist):
if
rdi <
=
a < rdi
+
rcx
*
8
:
global_tainted_memlist.remove(a)
uc.mem_write(rdi, p64(rax)
*
rcx)
uc.reg_write(UC_X86_REG_RDI, rdi
+
rcx
*
8
)
uc.reg_write(UC_X86_REG_RIP, rip
+
size)
elif
instbytes
in
[b
"\x48\x83\xe9\x01"
, b
"\x83\xe9\x01"
, b
"\x48\xff\xc9"
, b
"\xff\xc9"
]:
if
not
user_data.tracing:
user_data.clear()
user_data.tracing
=
True
user_data.address
=
address
else
:
assert
(address
=
=
user_data.address)
user_data.tracing
=
False
rcx
=
uc.reg_read(UC_X86_REG_RCX)
memreg
=
uc.reg_read(capstone_reg_to_unicorn_reg(user_data.memreg))
tmp
=
uc.mem_read(memreg, rcx
-
1
)
jitcode
=
compile
(
"\n"
.join(user_data.operations), "
", mode="
exec
")
buf
=
bytearray(
256
)
for
i
in
range
(
256
):
c
=
i
g
=
{
"c"
:c,
"ror8"
:ror8,
"rol8"
:rol8}
exec
(jitcode, g)
c
=
g[
"c"
]
buf[i]
=
c
tmpb
=
ctypes.create_string_buffer(bytes(tmp),
len
(tmp))
libdodecryptblock.dodecryptblock(ctypes.byref(tmpb),
len
(tmp), ctypes.byref(ctypes.create_string_buffer(bytes(buf),
len
(buf))))
tmp
=
tmpb.raw
uc.mem_write(memreg, bytes(tmp))
uc.reg_write(capstone_reg_to_unicorn_reg(user_data.memreg), memreg
+
user_data.direction
*
(rcx
-
1
))
uc.reg_write(UC_X86_REG_RCX,
1
)
elif
user_data.tracing:
print
(
"tracing"
)
if
user_data.tracecount >
=
100
:
user_data.tracing
=
False
else
:
user_data.tracecount
+
=
1
inst
=
list
(md.disasm(instbytes, address))[
0
]
operands
=
list
(inst.operands)
if
inst.
id
=
=
X86_INS_MOV:
if
operands[
0
].
type
=
=
X86_OP_REG
and
operands[
1
].
type
=
=
X86_OP_MEM:
if
operands[
0
].reg
=
=
X86_REG_AL:
user_data.memreg
=
operands[
1
].mem.base
elif
inst.
id
in
[X86_INS_ADD, X86_INS_SUB, X86_INS_XOR, X86_INS_AND, X86_INS_OR, X86_INS_ROR, X86_INS_ROL]:
if
operands[
0
].
type
=
=
X86_OP_REG
and
operands[
0
].reg
=
=
X86_REG_AL:
if
operands[
1
].
type
=
=
X86_OP_REG:
v
=
uc.reg_read(capstone_reg_to_unicorn_reg(operands[
1
].reg))
elif
operands[
1
].
type
=
=
X86_OP_IMM:
v
=
operands[
1
].imm
else
:
assert
(
0
)
if
inst.
id
=
=
X86_INS_ADD:
pyinst
=
f
"c = (c + {v}) & 0xff"
elif
inst.
id
=
=
X86_INS_SUB:
pyinst
=
f
"c = (c - {v}) & 0xff"
elif
inst.
id
=
=
X86_INS_XOR:
pyinst
=
f
"c = (c ^ {v}) & 0xff"
elif
inst.
id
=
=
X86_INS_AND:
pyinst
=
f
"c = (c & {v}) & 0xff"
elif
inst.
id
=
=
X86_INS_OR:
pyinst
=
f
"c = (c | {v}) & 0xff"
elif
inst.
id
=
=
X86_INS_ROR:
pyinst
=
f
"c = ror8(c, {v})"
elif
inst.
id
=
=
X86_INS_ROL:
pyinst
=
f
"c = rol8(c, {v})"
else
:
assert
(
0
)
user_data.operations.append(pyinst)
elif
inst.
id
in
[X86_INS_NOT, X86_INS_NEG, X86_INS_INC, X86_INS_DEC]:
if
operands[
0
].
type
=
=
X86_OP_REG
and
operands[
0
].reg
=
=
X86_REG_AL:
if
inst.
id
=
=
X86_INS_NOT:
pyinst
=
f
"c = (~c) & 0xff"
elif
inst.
id
=
=
X86_INS_NEG:
pyinst
=
f
"c = (-c) & 0xff"
elif
inst.
id
=
=
X86_INS_INC:
pyinst
=
f
"c = (c + 1) & 0xff"
elif
inst.
id
=
=
X86_INS_DEC:
pyinst
=
f
"c = (c - 1) & 0xff"
else
:
assert
(
0
)
user_data.operations.append(pyinst)
elif
operands[
0
].
type
=
=
X86_OP_REG
and
operands[
0
].reg
=
=
user_data.memreg:
if
inst.
id
=
=
X86_INS_INC:
user_data.direction
=
1
elif
inst.
id
=
=
X86_INS_DEC:
user_data.direction
=
-
1
else
:
pass
else
:
assert
(
"al"
not
in
inst.op_str.split(
','
)[
0
])
pass
@handlekeyboardinterupt
def
hook_code64_2(uc, address, size, user_data):
global
global_last_inst
instbytes
=
uc.mem_read(address, size)
inst
=
list
(md.disasm(instbytes, address))[
0
]
operands
=
list
(inst.operands)
global_last_inst
=
inst
src_regs
=
set
()
target_regs
=
set
()
if
len
(operands) >
0
:
leftopreand
=
operands[
0
]
rightopreand
=
operands[
1
]
if
len
(operands) >
1
else
operands[
0
]
tainted
=
False
if
rightopreand.
type
=
=
X86_OP_REG:
rr
=
rightopreand.reg
src_regs.add(rr)
elif
rightopreand.
type
=
=
X86_OP_MEM:
segment
=
rightopreand.mem.segment
base
=
rightopreand.mem.base
index
=
rightopreand.mem.index
scale
=
rightopreand.mem.scale
disp
=
rightopreand.mem.disp
if
base !
=
0
:
src_regs.add(base)
if
index !
=
0
:
src_regs.add(index)
memaddr
=
disp
+
(uc.reg_read(capstone_reg_to_unicorn_reg(base))
if
base !
=
0
else
0
)
+
(uc.reg_read(capstone_reg_to_unicorn_reg(index))
if
index !
=
0
else
0
)
*
scale
memvalue
=
u64(uc.mem_read(memaddr,
8
))
if
inst.
id
!
=
X86_INS_LEA
and
segment
=
=
0
else
memaddr
if
inst.
id
!
=
X86_INS_LEA
and
((base !
=
0
and
capstone_reg_to_normal_reg(base)
in
global_tainted_reglist)
or
(index !
=
0
and
capstone_reg_to_normal_reg(index)
in
global_tainted_reglist))
and
memaddr
not
in
global_tainted_memlist:
global_tainted_memlist.add(memaddr)
if
leftopreand.
type
=
=
X86_OP_REG:
rw
=
leftopreand.reg
target_regs.add(rw)
if
inst.
id
!
=
X86_INS_MOV
and
inst.
id
!
=
X86_INS_LEA:
src_regs.add(rw)
tainted
=
False
for
rr
in
src_regs:
rr_n
=
capstone_reg_to_normal_reg(rr)
if
rr_n
in
global_tainted_reglist:
tainted
=
True
break
if
(inst.
id
in
[X86_INS_XOR, X86_INS_SUB, X86_INS_AND])
and
operands[
0
].
type
=
=
X86_OP_REG
and
operands[
1
].
type
=
=
X86_OP_REG
and
operands[
0
].reg
=
=
operands[
1
].reg:
tainted
=
False
if
inst.
id
=
=
X86_INS_AND:
target_regs
=
set
()
if
len
(operands) >
0
and
operands[
0
].
type
=
=
X86_OP_REG
and
operands[
0
].reg
=
=
X86_REG_RSP:
tainted
=
False
target_regs
=
set
()
if
inst.
id
=
=
X86_INS_XCHG
and
operands[
0
].
type
=
=
X86_OP_REG
and
operands[
1
].
type
=
=
X86_OP_REG:
tainted
=
False
target_regs
=
set
()
r1
=
operands[
0
].reg
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)