程序无壳,直接ida打开: 观察基本的逻辑,分别由两次输入密码,并且都是6位,然后再进行加密和字符串的比较。首先观察第一次输入,程序将第一次的输入经过atoi函数处理后与"@DBApp "进行拼接,再进行一个加密,观察atoi函数: 但似乎看不出什么逻辑,直接打断点再动调几次看函数输出的结果,来反向推测出函数功能即可,使用汇编来观察更为直观,直接观察下方图片中的ax寄存器值,将其转化为10进制,发现1E240 就是123456 ,可见ato函数的功能就是将输入的6位数字字符 转化位相应的实际值,输入英文字符 时返回固定为0 (自行实验): 继续下面的if语句判断,根据atoi函数的返回值,确定了输入的password对应整数值必须大于100000 ,限定了password范围(利用这个范围再后面可以直接破解出密码)。 继续跟进后面的加密函数,发现并不是类似与异或的加密,而是使用hash算法,具体是那种hash可以根据器给定的编号0x8004 : 查询资料后发现0x8004u hash是sha1加密,几种常见的hash加密编号(价值)如下:具体的网站在这里 根据前文分析出的password范围(100000~999999 ),编写脚本进行爆破:
import hashlib
for i in range(100000,1000000):
tmp=str(i)+"@DBApp"
if hashlib.sha1(tmp.encode('utf-8')).hexdigest()=="6e32d0943418c2c33385bc35a1470250dd8923a9":
print(tmp)
#123321@DBApp
所以,破解出来第一次的密码为123321 ,关于sub_401230函数里面的逻辑可以查看这篇大佬的文章 。 继续向后分析第二次加密,其将第一次的结果String1与第二次的输入password_1进行拼接,然后再次加密比较,接下来直接跟进后面的加密函数: 10.发现这里的加密后先前大差不差,但是这次加密函数的编号选择为0x8003u ,通过查询上面的网站发现是hash中的md5加密,这里如果直接利用脚本破解很不现实,因为第二次加密的password没有给出范围,这就意味着会有128的6次方种情况需要遍历: 这里继续向后观察最后一个加密函数j_encrpt,传入的参数是两次拼接的password+”@DBApp“(未加密),j_encrpt函数的逻辑是,找到一个名为 “AAA” 的文件从中读取数据给到 lpBuffer ,然后经过sub_401005函数处理,sub_401005函数是将读取出来的数据与password进行异或并给到原来的lpBuffer : 最后,打开一个名为"dbapp.rtf"的.rtf文件,.rtf 文件实际上一个word文件,将加密后的lpBuffer写入到"dbapp.rtf"文件,去学习了相关.rtf文件的知识后发现,.rtf写入时在文件头部分 {\rtf1 是 必不可少的 ,详细的参照这篇大佬的文章 。 所以,从文件"AAA"终读取出来的数据与password异或后必须出现**{\rtf1** ,这是只需要知道AAA文件中的内容即可异或出第二次的password,这里读取"AAA"文件的类容听说可以使用工具Resource Hacker,但是,这里由第二种方法,直接patch修改strcmp的条件 后动调,然后去内存中查找从"AAA"提取出来的数据,只需要前6位即可异或出password,如下: 这里使用工具Resource Hacker观察"AAA"文件内容一致,工具Resource Hacker原版的下载地址如下:地址 : 这里直接给出最后提取出来的数据key=[0x05,0x7D,0x41,0x15,0x26,0x01],最后解密脚本如下:
res="{\\rtf1"
key=[0x05,0x7D,0x41,0x15,0x26,0x01]
for i in range(len(key)):
print(chr(ord(res[i])^key[i]),end="")
#~!3a@0
所以第二次的password为~!3a@0,回去将strcmp的patch修改回来,再根据得到的flag跑一边即可得到flag=Flag{N0_M0re_Free_Bugs}:
总结:
hash算法可根据其编号来选择。 .rtf文件的开头必须为 {\rtf1 。 使用工具Resource Hacker 可以查看文件内容。 在新建一个.rtf文件时,用16进制编辑器查看里面内容: 在里面写入内容后,经过验证发现不同的.rtf文件的文件头都是以**{\rtf1**开头: