安恒月赛杯9月逆向

NewDriver
本题主要涉及base64加密和RC4加密的知识

拿到一个PE文件,运行之后是个控制台程序,简单的要求输入flag,然后判断是否正确,无壳。
用32位IDA打开,找到main函数,由于栈帧不平衡无法反编译,可以修改堆栈指针使其平衡然后进行反编译,不过该题主函数逻辑不复杂,可以直接看汇编代码。
直接来到开始输入的指令位置
安恒月赛杯9月逆向
文章图片

显然,var_38便是用来存储输入的变量
安恒月赛杯9月逆向
文章图片

然后是一段计算输入长度的指令,输入长度必须为0x21,之后让输入作为参数调用一个函数,跟进这个函数,发现十分复杂,用xdbg调试后发现,调用该函数后,产生了非常像base64编码的字段,姑且先认为它就是base64加密函数吧。
安恒月赛杯9月逆向
文章图片

这里var_138在最开始初始化为一段字符串flag{this_is_not_the_flag_hahaha}。计算var_138的长度,然后调用sub_401000函数,由于这个函数的参数分别是一段缓冲区地址,var_138以及它的长度,与我们的输入无关,功能也是为这段缓冲区赋值,所以得到的一定是定值,可以在调试器中查看
【安恒月赛杯9月逆向】安恒月赛杯9月逆向
文章图片

esi中存储的是输入经过base64加密的字符串首地址,这段代码将它和它的长度作为参数调用了sub_4010e0函数,该函数还有一个参数是var_238,大概猜到函数功能就是以var_238为密钥对经过base64加密的字符串再次加密,看到flag之后才知道这个加密函数是RC4加密
安恒月赛杯9月逆向
文章图片

最后就是将再次加密的数据与byte_402104处的明文进行比较啦。
总结一下流程:

  1. 对输入进行base64加密
  2. 以固定明文生成密钥
  3. 使用密钥对数据再次加密
  4. 将生成的密文与正确的密文进行比较
首先已知的是密文,且密钥也可通过动态分析得到,所以通过逆向加密算法我们就可得到经base64加密的flag。进入加密函数中,
安恒月赛杯9月逆向
文章图片

逆向加密算法得到解密脚本,这里s是密文,key是密钥
安恒月赛杯9月逆向
文章图片

解密得到flag的base64的形式 ZeptZ3l5UHQra25nd19yYzMrYR5wX2Jtc2P2VF9gYNM9。
若本题使用的是标准base64,那通过常用工具或命令行就可以直接得到答案,但本题修改了base64的编码表,置换了一些字符的位置,进入base64加密函数中可以看到给出的编码表为ABCDEFGHIJSTUVWKLMNOPQRXYZabcdqrstuvwxefghijklmnopyz0123456789+/
所以这里我们手写base64的解密脚本。
base64的加密方式是以24位为一个缓冲区,每次加密3个字节,将3个字节分为4个6位二进制数作为4个索引,然后通过索引得到编码表的映射。相对的,解密脚本就是以4个字符为一组,映射成索引,然后组合成24位,再分为3字节即可解密。脚本如下(写得真丑):
安恒月赛杯9月逆向
文章图片

然后以4个字符为一组对ZeptZ3l5UHQra25nd19yYzMrYR5wX2Jtc2P2VF9gYNM9进行解密就可以得到flag辣

GodDriver
本题主要涉及rot13加密和解矩阵乘法
和第一题相同,ida依旧无法反编译主函数,不过同样主函数的逻辑比较清晰,可以直接看汇编代码。
分析下来主函数大概做了几件事:
  • 从标准输入流读入flag
  • 创建管道和子进程使父进程能与其相互通信
  • 在子进程中将flag作为参数调用ck1函数
  • 将ck1的返回值发送到父进程
  • 父进程接收数据,调用ck2,ck3,ck4三个函数
  • 根据ck4的返回值来判断输入的正确性
要得到flag,先从ck4入手,这个函数很简单,就是将两个二维数组进行比较,看是否相等,一个是该函数的参数,另一个是一段全局变量的数据,想必这就是最后的密文了,只要根据前面的函数还原出解密算法,就能得到最后的flag了。再看ck3,
安恒月赛杯9月逆向
文章图片

先初始化a3这个二维数组,然后一个三重的嵌套循环给a3赋值,实际上这就是两个矩阵相乘然后把结果赋给a3的算法。这里a3是已知的,就是那段密文,a1也是已知的,那么通过解这个矩阵乘法就可以得到a2,而a2就是经过ck1的flag再经过ck2处理后得到的,于是再看ck2
安恒月赛杯9月逆向
文章图片

f5出来这一句很迷,直接看汇编,循环体内部是这样的
安恒月赛杯9月逆向
文章图片

乍看之下也很奇怪,但细看之后发现它就是一个复制的功能,也就是说输入的经过ck1的flag会直接复制到一个缓冲区中作为ck3的a2参数,最后就只剩ck1函数了
安恒月赛杯9月逆向
文章图片

这里都不用仔细看,看到什么小于a大于z,又是加13又是减13的肯定就是rot13了,就是以13为步长的移位加密。
至此,所有加密过程都已知了,根据这些过程写出解密脚本就好了,rot13可以直接写,而解矩阵乘法可以用微软的z3库比较容易地得到结果(算法大佬绕道),最后附上脚本
from z3 import *def bytes_to_dwords(str): result = [] str = list(str) for i in range(len(str)): str[i] = ord(str[i]) for i in range(0, len(str), 4): s = str[i:i+4] n = s[3] n = n << 8 n += s[2] n = n << 8 n += s[1] n = n << 8 n += s[0] result.append(n) return resultdef rot13(s): flag = '' lenth = len(s) for c in s: tmp = ord(c) + 13 if c.isupper(): if tmp > 90: tmp = tmp - 26 elif c.islower(): if tmp > 122: tmp = tmp - 26 else: tmp = ord(c) flag += chr(tmp) return flagf = open('GodDriver', 'rb') data = https://www.it610.com/article/f.read() f.close() flag_table = data[0x1160:0x1260] box = data[0x1060:0x1160] flag_table = bytes_to_dwords(flag_table) box = bytes_to_dwords(box) buf1_x = [BitVec('flag%d' % i, 16) for i in range(8 * 8)] s = Solver() k = 0 while True: if k >= 8: break for l in range(8): sum = 0 for m in range(8): sum += box[8 * k + m] * buf1_x[8 * m + l] s.add(sum == flag_table[8 * k + l]) k += 1buf1 = [] if (s.check() == sat): m = s.model() for i in range(64): buf1.append(m[buf1_x[i]].as_long()) for i in range(len(buf1)): buf1[i] = chr(buf1[i] & 0Xff) flag = ''.join(buf1)print rot13(flag)


    推荐阅读