NewDriver
本题主要涉及base64加密和RC4加密的知识
拿到一个PE文件,运行之后是个控制台程序,简单的要求输入flag,然后判断是否正确,无壳。
用32位IDA打开,找到main函数,由于栈帧不平衡无法反编译,可以修改堆栈指针使其平衡然后进行反编译,不过该题主函数逻辑不复杂,可以直接看汇编代码。
直接来到开始输入的指令位置
显然,var_38便是用来存储输入的变量
然后是一段计算输入长度的指令,输入长度必须为0x21,之后让输入作为参数调用一个函数,跟进这个函数,发现十分复杂,用xdbg调试后发现,调用该函数后,产生了非常像base64编码的字段,姑且先认为它就是base64加密函数吧。
这里var_138在最开始初始化为一段字符串flag{this_is_not_the_flag_hahaha}。计算var_138的长度,然后调用sub_401000函数,由于这个函数的参数分别是一段缓冲区地址,var_138以及它的长度,与我们的输入无关,功能也是为这段缓冲区赋值,所以得到的一定是定值,可以在调试器中查看
esi中存储的是输入经过base64加密的字符串首地址,这段代码将它和它的长度作为参数调用了sub_4010e0函数,该函数还有一个参数是var_238,大概猜到函数功能就是以var_238为密钥对经过base64加密的字符串再次加密,看到flag之后才知道这个加密函数是RC4加密
最后就是将再次加密的数据与byte_402104处的明文进行比较啦。
总结一下流程:
- 对输入进行base64加密
- 以固定明文生成密钥
- 使用密钥对数据再次加密
- 将生成的密文与正确的密文进行比较
首先已知的是密文,且密钥也可通过动态分析得到,所以通过逆向加密算法我们就可得到经base64加密的flag。进入加密函数中,
逆向加密算法得到解密脚本,这里s是密文,key是密钥
解密得到flag的base64的形式 ZeptZ3l5UHQra25nd19yYzMrYR5wX2Jtc2P2VF9gYNM9。
若本题使用的是标准base64,那通过常用工具或命令行就可以直接得到答案,但本题修改了base64的编码表,置换了一些字符的位置,进入base64加密函数中可以看到给出的编码表为ABCDEFGHIJSTUVWKLMNOPQRXYZabcdqrstuvwxefghijklmnopyz0123456789+/
所以这里我们手写base64的解密脚本。
base64的加密方式是以24位为一个缓冲区,每次加密3个字节,将3个字节分为4个6位二进制数作为4个索引,然后通过索引得到编码表的映射。相对的,解密脚本就是以4个字符为一组,映射成索引,然后组合成24位,再分为3字节即可解密。脚本如下(写得真丑):
然后以4个字符为一组对ZeptZ3l5UHQra25nd19yYzMrYR5wX2Jtc2P2VF9gYNM9进行解密就可以得到flag辣
GodDriver
本题主要涉及rot13加密和解矩阵乘法
和第一题相同,ida依旧无法反编译主函数,不过同样主函数的逻辑比较清晰,可以直接看汇编代码。
分析下来主函数大概做了几件事:
- 从标准输入流读入flag
- 创建管道和子进程使父进程能与其相互通信
- 在子进程中将flag作为参数调用ck1函数
- 将ck1的返回值发送到父进程
- 父进程接收数据,调用ck2,ck3,ck4三个函数
- 根据ck4的返回值来判断输入的正确性
要得到flag,先从ck4入手,这个函数很简单,就是将两个二维数组进行比较,看是否相等,一个是该函数的参数,另一个是一段全局变量的数据,想必这就是最后的密文了,只要根据前面的函数还原出解密算法,就能得到最后的flag了。再看ck3,
先初始化a3这个二维数组,然后一个三重的嵌套循环给a3赋值,实际上这就是两个矩阵相乘然后把结果赋给a3的算法。这里a3是已知的,就是那段密文,a1也是已知的,那么通过解这个矩阵乘法就可以得到a2,而a2就是经过ck1的flag再经过ck2处理后得到的,于是再看ck2
f5出来这一句很迷,直接看汇编,循环体内部是这样的
乍看之下也很奇怪,但细看之后发现它就是一个复制的功能,也就是说输入的经过ck1的flag会直接复制到一个缓冲区中作为ck3的a2参数,最后就只剩ck1函数了
这里都不用仔细看,看到什么小于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 result
def 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 flag
f = open('GodDriver', 'rb')
data = 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 += 1
buf1 = []
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)