一.这道题应该就是正常情况下的一个难题了,没有强大的伪代码分析和写脚本的功底,这道题确实不太好写,我看网上有好多angr指令解出来的,虽然这也是种方法,但是我觉得还是用正常的方法写,自己才能进步的更快。
1.正常IDA打开:
主函数是比较正常的,flag一共有22位,第18-22行有个判断,我们可以知道a1的值,注意第12行sub_41644A函数,这个函数没有参数,点进去里面是个套娃,通过动态调试还有一次次的实验尝试,最终知道了是将输入的字符串每一个加“i”,然后再异或“i”。
2.第13-17行的运算过程也比较正常,直接正向写脚本爆破就可以,因为第16行的
a1[i] = ((int)(unsigned __int8)a1[i] >> 3) | (32 * a1[i]);
这个逆向过程确实不太好写(反正我是不会逆着写。。),所以我们通过正向的过程,爆破一下输入的值,加上最后的判断,就能得到flag经过sub_41644A函数变换后的值,具体的python脚本:
a1 = [0x6E, 0x10, 0xEC, 0x13, 0xC1, 0xCB, 0xF0, 0x2D, 0xC6, 0x32,
0xFD, 0x86, 0xEE, 0xCB, 0x89, 0x92, 0x3C, 0x46, 0x49, 0x71,
0x62, 0x57]
dword_424080 = [0x21,0x3f,0xa3,0xe9,0x8f,0x00]
for j in range(1,len(a1)):
for i in range(256):
c = i
c += (dword_424080[j % 6] >> 6) ^ (16 * dword_424080[(j - 1) % 6])
c = ((c&0xff) >> 3) | (32 * (c&0xff))
if (c&0xff) == a1[j]:
print(chr(i),end='')
#pue|nYD^]DwNZaOBJ{!
我们得到了经过变化的flag从第二位到最后一位的值,再根据动态调试,第一位输入什么就输出什么,所以根据最后一个判断,它的值是0x6E,也就是“n”。这时候,我们可以再写一个脚本,逆向还原出flag:
a = 'npue|nYD^]DwNZaOBJ{!' #刚才得到的字符串前面加“n”
for i in range(len(a)):
flag = ''
flag += chr((ord(a[i])^i)-i) #通过动调得到
print(flag,end='')
#npuctf{WDNMDo6F_OBFU!}
本来以为这肯定就是flag了,没想到还是我年轻了。。。
3.中间三位o6F感觉和flag关系不大,其中三位中的第一位应该是“_”,剩下两位直接爆破:
from itertools import *
import subprocess #subprocess 模块允许你生成新的进程,连接它们的输入、输出、错误管道,并且获取它们的返回码
for i in range(1):
for j in range(32,127):
for k in range(32,127):
flag ="npuctf{WDNMD_"+chr(j)+chr(k)+"_OBFU!}"
p = subprocess.Popen([r"C:\\Users\\admin\\Desktop\\attachment.exe"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.stdin.write(flag.encode())
p.stdin.close()
out=p.stdout.read()
p.stdout.close()
if "E".encode() not in out:
print(flag)
exit()
这个爆破脚本浅显易懂,稍微复杂点的可能是subprocess这个模块,具体可以参考:https://docs.python.org/zh-cn/3/library/subprocess.html#using-the-subprocess-module
运行得到flag
npuctf{WDNMD_LJ_OBFU!}
像这个爆破脚本的话, 有的windows下的exe程序可以爆破出来,有的爆破不出来,这个我还正在研究,不过上述爆破脚本完全可以当成一个模板来使用,具体了解原理的大佬可以一起交流一下。
二.此外还有第二种,也就是angr指令的方式得到flag。
from angr import *
p = Project('C:\\Users\\admin\\Desktop\\attachment.exe',auto_load_libs = False)
state = p.factory.blank_state(addr=0x004164F8)
simfd = state.posix.get_fd(0)
data,real_size = simfd.read_data(22)
state.memory.store(0x0042612C,data)
state.memory.store(0x00426020,data)
sm = p.factory.simulation_manager(state)
sm.one_active.options.add(options.LAZY_SOLVES)
sm.explore(find = 0x00416609,avoid = 0x004165EA)
text = sm.one_found.solver.eval(sm.one_found.memory.load(0x0042612C,22),cast_to = bytes)
print (text)
这个脚本是参照别的大佬的wp,具体的angr原理可以参考:
https://xz.aliyun.com/t/3990
之后我会做angr指令专题详解,运行之后得到:
最终得到flag:
flag{WDNMD_LJ_OBFU!}