TLS反调试,线程局部存储,先于程序EP代码执行
TLS函数里包含循环,再结合IDA反编译代码爆红,可知代码存在自修改
4015C0:(不知这里为何是入口_笑哭.jpg)
main函数开头安装了seh函数(红框)
SEH是window操作系统默认的异常处理机制,逆向分析中,SEH除了基本的异常处理功能外,还大量用于反调试程序
安装后进行输入,然后对输入的长度减了一个0x1C,并随后触发int3断点来到seh函数。
context结构体:
SEH中的判断如图所示。在main函数中对flag长度做的sub操作会影响EFlags的值,当输入长度等于0x1C时,ZFlag置0,SEH函数由此判断输入的长度是否正确,如果错误会将程序的流程引向一段假的flag解密函数。长度正确则对标志位置1。
dword_41A8E0这个标志位将在子线程的函数中被循环检查,静态下该函数还未解密,所以需要进行动态调试等待代码段解密。解密后来到子线程函数。
有反调试,调都调不起来。
后面的就哈哈哈了,一只动调菜狗路过。
vm逆向,两种思路
输入下硬件断点,f9动调
IDA找opcode,分析各个opcode的作用写脚本生成伪汇编代码
IDA打开,没有符号表,所以找导出表start,找到函数入口004019C0,X32dbg跟进想找到输入,但到41594跑飞了,41595没有,分析知这是一个混淆,回到IDA nop掉
IDA nop掉
创建函数并查看伪代码,发现是VM结构,开始分析找opcode
off_4448F4处保存各个opcode对应函数的地址,4449A0处为虚拟机代码起始地址,dword_445BD8为ip,off_4427FC处保存有四个寄存器的地址,dword_445BAC为栈的起始地址,dword_445BC8为esp,opcode中有对栈的操作、跳转操作、取输入的操作以及加减乘除异或五种运算,分析各个opcode的作用后,可以写脚本生成伪汇编代码。
opcode = {0x00:'nop',0x01:'mov',0x02:'pushi',0x03:'pushr',0x04:'pop',0x05:
'print',0x06:'add',0x07:'sub',0x08:'mul',0x09:'div',0x0A:'xor',0x0B:'jmp',
0x0C:'cmp',0x0D:'je',0x0E:'jn',0x0F:'jg',0x10:'jl',0x11:'input',0x12:'clrstr',0x13:'LoadStack',0x14:'LoadString',0xFF:'quit'}
operand = {'nop':0,'mov':2,'pushi':1,'pushr':1,'pop':1,'print':0,'add':2,'sub':2,'mul':2,'div':2,'xor':2,'jmp':1,'cmp':2,'je':1,'jn':1,'jg':1,'jl':1
,'input':0,'clrstr':0,'LoadStack':2,'LoadString':2,'quit':0}
code = [
0x01, 3,3,
0x05, 0x00, 0x00,
0x11, 0x00, 0x00,
0x01, 1,17,
0x0C, 0,1,
0x0D, 10, 0x00,
0x01, 3,1,
0x05, 0x00, 0x00,
0xFF, 0x00, 0x00,
0x01, 2, 0,
0x01, 0, 17,
0x0C, 0, 2,
0x0D, 43, 0x00,
0x14, 0, 2,
0x01, 1, 97,
0x0C, 0, 1,
0x10, 26, 0x00,
0x01, 1, 122,
0x0C, 0, 1,
0x0F, 26, 0x00,
0x01, 1, 71,
0x0A, 0, 1,
0x01, 1, 1,
0x06, 0, 1,
0x0B, 36, 0x00,
0x01, 1, 65,
0x0C, 0, 1,
0x10, 36, 0x00,
0x01, 1, 90,
0x0C, 0, 1,
0x0F, 36, 0x00,
0x01, 1, 75,
0x0A, 0, 1,
0x01, 1, 1,
0x07, 0, 1,
0x01, 1, 16,
0x09, 0, 1,
0x03, 1, 0x00,
0x03, 0, 0x00,
0x01, 1, 1,
0x06, 2, 1,
0x0B, 11, 0x00,
0x02, 0x7, 0x00,
0x02, 0xD, 0x00,
0x02, 0x0, 0x00,
0x02, 0x5, 0x00,
0x02, 0x1, 0x00,
0x02, 0xC, 0x00,
0x02, 0x1, 0x00,
0x02, 0x0, 0x00,
0x02, 0x0, 0x00,
0x02, 0xD, 0x00,
0x02, 0x5, 0x00,
0x02, 0xF, 0x00,
0x02, 0x0, 0x00,
0x02, 0x9, 0x00,
0x02, 0x5, 0x00,
0x02, 0xF, 0x00,
0x02, 0x3, 0x00,
0x02, 0x0, 0x00,
0x02, 0x2, 0x00,
0x02, 0x5, 0x00,
0x02, 0x3, 0x00,
0x02, 0x3, 0x00,
0x02, 0x1, 0x00,
0x02, 0x7, 0x00,
0x02, 0x7, 0x00,
0x02, 0xB, 0x00,
0x02, 0x2, 0x00,
0x02, 0x1, 0x00,
0x02, 0x2, 0x00,
0x02, 0x7, 0x00,
0x02, 0x2, 0x00,
0x02, 0xC, 0x00,
0x02, 0x2, 0x00,
0x02, 0x2, 0x00,
0x01, 2, 1,
0x13, 1, 2,
0x04, 0, 0x00,
0x0C, 0, 1,
0x0E, 91, 0x00,
0x01, 1, 34,
0x0C, 2, 1,
0x0D, 89, 0x00,
0x01, 1, 1,
0x06, 2, 1,
0x0B, 78, 0x00,
0x01, 3, 0,
0x05, 0x00, 0x00,
0xFF, 0x00, 0x00,
0x01, 3, 1,
0x05, 0x00, 0x00,
0xFF, 0x00, 0x00,
]
for i in range(0,len(code),3):
if not code[i] in opcode.keys():
print('unknow')
continue
op = opcode[code[i]]
print(op, end = ' ')
if operand[op] != 0:
print(code[i+1:i+1+operand[op]])
else:
print()
得到伪汇编
mov [3, 3]
print
input
mov [1, 17]
cmp [0, 1]
je [10]
mov [3, 1]
print
quit
mov [2, 0]
mov [0, 17]
cmp [0, 2]
je [43]
LoadString [0, 2]
mov [1, 97]
cmp [0, 1]
jl [26]
mov [1, 122]
cmp [0, 1]
jg [26]
mov [1, 71]
xor [0, 1]
mov [1, 1]
add [0, 1]
jmp [36]
mov [1, 65]
cmp [0, 1]
jl [36]
mov [1, 90]
cmp [0, 1]
jg [36]
mov [1, 75]
xor [0, 1]
mov [1, 1]
sub [0, 1]
mov [1, 16]
div [0, 1]
pushr [1]
pushr [0]
mov [1, 1]
add [2, 1]
jmp [11]
pushi [7]
pushi [13]
pushi [0]
pushi [5]
pushi [1]
pushi [12]
pushi [1]
pushi [0]
pushi [0]
pushi [13]
pushi [5]
pushi [15]
pushi [0]
pushi [9]
pushi [5]
pushi [15]
pushi [3]
pushi [0]
pushi [2]
pushi [5]
pushi [3]
pushi [3]
pushi [1]
pushi [7]
pushi [7]
pushi [11]
pushi [2]
pushi [1]
pushi [2]
pushi [7]
pushi [2]
pushi [12]
pushi [2]
pushi [2]
mov [2, 1]
LoadStack [1, 2]
pop [0]
cmp [0, 1]
jn [91]
mov [1, 34]
cmp [2, 1]
je [89]
mov [1, 1]
add [2, 1]
jmp [78]
mov [3, 0]
print
quit
mov [3, 1]
print
quit
逻辑还挺好懂的,伪代码中,类似于mov、add、sub等指令后面的操作数为寄存器编号,例如 mov [1,2]代表将2号寄存器的内容放入1号寄存器。接下来分析伪汇编代码,发现是对输入的值进行处理,小写字母异或71后+1,大写字母异或75后-1,非字母不处理,处理得到的结果除以16,得到的商和余数分别进栈,并在最后部分比较,因此可以写出exp计算flag
array = [0x7,0xd,0x0,0x5,0x1,0xc,0x1,0x0,0x0,0xd,0x5,0xf,0x0,0x9,0x5,0xf,0x3,0x0,0x2,0x5,0x3,0x3,0x1,0x7,0x7,0xb,0x2,0x1,0x2,0x7,0x2,0xc,0x2,0x2,]
array = array[::-1]
for i in range(0, len(array), 2):
c = array[i] + array[i+1]*16
tmp = (c-1) ^ 71
if tmp >= ord('a') and tmp <= ord('z'):
print(chr(tmp), end = "")
continue
tmp = (c+1) ^ 75
if tmp >= ord('A') and tmp <= ord('Z'):
print(chr(tmp), end = "")
continue
print(chr(c), end = "")
flag{Such_A_EZVM}