这道题 其实是一道模拟题 作者估计也没想到 被hexRay背刺了 大概思想是模拟了 加 减 异或三种运算 可惜模拟的子片段能被hexRay识别并分析,导致把maxFunSize参数上限调高后,ida直接把运算式生成了:
byte_43841C[6] += byte_43841C[3];
byte_43841C[10] -= byte_43841C[27];
byte_43841C[10] += byte_43841C[6];
byte_43841C[14] += byte_43841C[3];
byte_43841C[28] ^= byte_43841C[24];
byte_43841C[30] += 33;
byte_43841C[12] += 33;
byte_43841C[24] ^= byte_43841C[21];
byte_43841C[22] += 33;
byte_43841C[25] += byte_43841C[10];
byte_43841C[4] += byte_43841C[6];
byte_43841C[12] += 33;
byte_43841C[17] += byte_43841C[0];
byte_43841C[20] += byte_43841C[17];
这就导致解该题 只需要复制出去,反转运算符即可
弱弱吐槽一句(其实作者的模拟汇编片段还是有若干变种的,无奈hexRay太过强大,不愧是价值几万刀的插件)
这里我用两种常见思路解决该题
angr
第一反应肯定是angr,你这不是有几千个运算吗,让angr跑就是了,中间有一个check,跟结果无关,直接hook,pass. 结尾check正确与否的用相同字符个数是不是32,不能直接用angr,会路径爆炸,所以也hook掉,编写我们的模拟check,还有程序开了alsr,让angr在固定位置加载:
import angr
import claripy
import sys
def main(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary,main_opts={'base_addr':0x400000})
start_address = 0x4011C3
initial_state = project.factory.blank_state(addr = start_address)
userin = claripy.BVS('userin',256)
userin_address = 0x43841C
initial_state.memory.store(userin_address,userin)
@project.hook(0x0043306F,length=0x004330C0-0x0043306F)
def skip_puts(state):
pass
check_equals_called_address = 0x00434130
instruction_to_skip_length = 0x004341FD - 0x00434130
@project.hook(check_equals_called_address, length=instruction_to_skip_length)
def skip_check_equals_(state):
user_input_buffer_address = 0x43841c
user_input_buffer_length = 0x20
user_input_string = state.memory.load(
user_input_buffer_address,
user_input_buffer_length
)
v5buff = [0x27, 0x3C, 0xE3, 0xFC, 0x2E, 0x41, 0x07, 0x5E, 0x62, 0xCF, 0xE8, 0xF2, 0x92, 0x80, 0xE2, 0x36, 0xB4, 0xB2, 0x67, 0x77, 0x0F, 0xF6, 0x0D, 0xB6, 0xED, 0x1C, 0x65, 0x8A, 0x07, 0x53, 0xA6, 0x66]
check_against_string = bytes(v5buff)
print(user_input_string)
state.regs.eip = claripy.If(
user_input_string == check_against_string,
claripy.BVV(0x004341FD, 32),
claripy.BVV(0x0043420D, 32)
)
simulation = project.factory.simgr(initial_state)
simulation.explore(find=0x00434202, avoid=0x00434212)
if simulation.found:
solution_state = simulation.found[0]
solution = solution_state.solver.eval(userin, cast_to=bytes).decode('utf-8')
print (solution)
if __name__ == '__main__':
main(sys.argv)
不过很可惜 作者很聪明,加了大量的异或运算,导致生成的式子Z3跑不出来 angr貌似处理异或时不是很聪明,可能退化为枚举了
capstone
思路是,用capstone 反汇编程序,提取其中的子函数片段, 然后匹配我们分析的几种pattern, 模拟逆向运算过程
import capstone
simulate_stack = []
with open('Dizzy.exe','rb') as f:
buff = f.read()
CODE = buff[0x1c3+0x400:0x3206f+0x400] + buff[0x320c0+0x400:0x33130+0x400] + b'#'
pos = 0
md = capstone.Cs(capstone.CS_ARCH_X86,capstone.CS_MODE_64)
md.detail = True
def parserDouble():
global pos
code = CODE[pos:pos+46]
index = 0
imm1 = 0
imm2 = 0
op = ''
#print(code)
for i in md.disasm(code, 0):
#print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))
if(i.insn_name() == 'shl' and i.size == 2):
pos -= 1
if(index == 1):
if(i.insn_name() == 'shl'):
imm1 = 2 ** i.operands[1].imm
else:
imm1 = i.operands[2].imm
if(index == 3):
if(i.insn_name() == 'shl'):
imm2 = 2 ** i.operands[1].imm
else:
imm2 = i.operands[2].imm
if(index == 6):
op = i.insn_name()
index = index + 1
return (0,imm1,imm2,op)
def parserSingle():
global pos
code = CODE[pos:pos+32]
index = 0
imm1 = 0
imm2 = 0
op = ''
for i in md.disasm(code, 0):
if(i.insn_name() == 'shl' and i.size == 2):
pos -= 1
#print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))
if(index == 1):
if(i.insn_name() == 'shl'):
imm1 = 2 ** i.operands[1].imm
else:
imm1 = i.operands[2].imm
if(index == 3):
imm2 = i.operands[1].imm
op = i.insn_name()
index = index + 1
return (1,imm1,imm2,op)
def check_pattern():
global pos
code = CODE[pos:pos + 18]
index = 0
for i in md.disasm(code,0):
if(index == 2):
if(i.insn_name() == 'movzx'):
return 'single'
else:
return 'double'
index = index + 1
while True:
if( CODE[pos] != ord('#') and check_pattern() == 'double'):
res = parserDouble()
#print(res)
pos += 46
simulate_stack.append(res)
if(CODE[pos] != ord('#') and check_pattern() == 'single'):
res = parserSingle()
#print(res)
pos += 32
simulate_stack.append(res)
if(CODE[pos] == ord('#')):
break
cipher = [0x27, 0x3C, 0xE3, 0xFC, 0x2E, 0x41, 0x07, 0x5E, 0x62, 0xCF, 0xE8, 0xF2, 0x92, 0x80, 0xE2, 0x36, 0xB4, 0xB2, 0x67, 0x77, 0x0F, 0xF6, 0x0D, 0xB6, 0xED, 0x1C, 0x65, 0x8A, 0x07, 0x53, 0xA6, 0x66]
# f = open('temper.txt','w')
# target_write = []
while(len(simulate_stack) != 0):
pattern,imm1,imm2,op = simulate_stack.pop()
# target_write.append('{} {} {} {}\n'.format(pattern,imm1,imm2,op))
if pattern == 0:
if op == 'add':
cipher[imm1] -= cipher[imm2]
if op == 'sub':
cipher[imm1] += cipher[imm2]
if op == 'xor':
cipher[imm1] ^= cipher[imm2]
if pattern == 1:
if op == 'add':
cipher[imm1] -= imm2
if op == 'sub':
cipher[imm1] += imm2
if op == 'xor':
cipher[imm1] ^= imm2
# f.writelines(target_write)
flag = ''
for ch in cipher:
flag += chr(ch%256)
print(flag)
结果:
[Running] python -u “c:\Users\fwk\Desktop\solve_capstone.py”
flag{Try_R3vers1ng_W1th_ScR!pt!}
[Done] exited with code=0 in 0.66 seconds
取模是为了模拟char数据类型
也可以将代码注释去掉,生成一个中间操作文件,再用c语言读取模拟操作:
0 16 20 add
0 0 5 add
1 21 54 sub
0 22 31 sub
0 29 25 add
0 18 14 xor
…
C语言解析执行如下:
#include <stdio.h>
#include <string.h>
unsigned char cipher[] = {0x27, 0x3C, 0xE3, 0xFC, 0x2E, 0x41, 0x07, 0x5E, 0x62, 0xCF, 0xE8, 0xF2, 0x92, 0x80, 0xE2, 0x36, 0xB4, 0xB2, 0x67, 0x77, 0x0F, 0xF6, 0x0D, 0xB6, 0xED, 0x1C, 0x65, 0x8A, 0x07, 0x53, 0xA6, 0x66};
int main()
{
int pattern;
int imm1;
int imm2;
char op[64];
FILE* fp = fopen("temper.txt","r");
while(fscanf(fp,"%d %d %d %s",&pattern,&imm1,&imm2,op)!=EOF)
{
if (pattern == 0)
{
if (!strcmp(op,"add"))
cipher[imm1] -= cipher[imm2];
if (!strcmp(op,"sub"))
cipher[imm1] += cipher[imm2];
if (!strcmp(op,"xor"))
cipher[imm1] ^= cipher[imm2];
}
else
{
if (!strcmp(op,"add"))
cipher[imm1] -= imm2;
if (!strcmp(op,"sub"))
cipher[imm1] += imm2;
if (!strcmp(op,"xor"))
cipher[imm1] ^= imm2;
}
}
printf("%s",cipher);
return 0;
}
结果:
[Running] cd “c:\Users\fwk\Desktop” && g++ final_calc.cpp -o final_calc && "c:\Users\fwk\Desktop"final_calc
flag{Try_R3vers1ng_W1th_ScR!pt!}�.@
[Done] exited with code=0 in 0.236 seconds
人工分析出的pattern:
0x004011C3 -- 0x0043306F [)
0x004330C0 -- 00434130 [)
0x400
0x1c3 -- 0x 3206f
0x 320c0 -- 0x 33130
1:
buff[a] += buff[b]
.text:004011C3 mov eax, 1
.text:004011C8 imul ecx, eax, 6
.text:004011CB mov edx, 1
.text:004011D0 imul eax, edx, 3
.text:004011D3 movzx edx, byte_43841C[eax]
.text:004011DA movzx eax, byte_43841C[ecx]
.text:004011E1 add eax, edx
.text:004011E3 mov ecx, 1
.text:004011E8 imul edx, ecx, 6
.text:004011EB mov byte_43841C[edx], al
2:
buff[a] -= buff[b]
.text:004011F1 mov eax, 1
.text:004011F6 imul ecx, eax, 0Ah
.text:004011F9 mov edx, 1
.text:004011FE imul eax, edx, 1Bh
.text:00401201 movzx edx, byte_43841C[eax]
.text:00401208 movzx eax, byte_43841C[ecx]
.text:0040120F sub eax, edx
.text:00401211 mov ecx, 1
.text:00401216 imul edx, ecx, 0Ah
.text:00401219 mov byte_43841C[edx], al
3:
buff[a] ^= buff[b]
.text:0040127B mov eax, 1
.text:00401280 imul ecx, eax, 1Ch
.text:00401283 mov edx, 1
.text:00401288 imul eax, edx, 18h
.text:0040128B movzx edx, byte_43841C[eax]
.text:00401292 movzx eax, byte_43841C[ecx]
.text:00401299 xor eax, edx
.text:0040129B mov ecx, 1
.text:004012A0 imul edx, ecx, 1Ch
.text:004012A3 mov byte_43841C[edx], al
4:
buff[a] += imm
.text:004012A9 mov eax, 1
.text:004012AE imul ecx, eax, 1Eh
.text:004012B1 movzx edx, byte_43841C[ecx]
.text:004012B8 add edx, 21h ; '!'
.text:004012BB mov eax, 1
.text:004012C0 imul ecx, eax, 1Eh
.text:004012C3 mov byte_43841C[ecx], dl
第9 :0x0F
.text:00401365 B8 01 00 00 00 mov eax, 1
.text:0040136A C1 E0 02 shl eax, 2
.text:0040136D B9 01 00 00 00 mov ecx, 1
.text:00401372 6B D1 06 imul edx, ecx, 6
.text:00401375 0F B6 8A 1C 84 43 00 movzx ecx, byte_43841C[edx]
.text:0040137C 0F B6 90 1C 84 43 00 movzx edx, byte_43841C[eax]
.text:00401383 03 D1 add edx, ecx
.text:00401385 B8 01 00 00 00 mov eax, 1
.text:0040138A C1 E0 02 shl eax, 2
.text:0040138D 88 90 1C 84 43 00 mov byte_43841C[eax], dl