第三届数信杯数据安全大赛初赛reverse wp
起因
看到决赛通知我才想起来,初赛当时还有道题没做完,当时时间太紧了,压根没时间认真做啊🤣
题目如下:
工程师小王认识到前面开发的程序并不能保证对数据的安全存储,现在对处理程序进行了改进,这次能行吗?分析程序功能,解密文件获取原始数据,提交第8行第2列数据。
花指令去除
一开始跟进 main 函数就能发现 ida 没识别成功
在 0x4016F1 处能看到第一处

这样即可

第二处在 0x4012A5 处

同上处理

第三处在 0x4014E6 处

许久不见 call-ret 型的花指令了,这里真正的代码是从 0x4014F6 开始的,还原后如下

重新识别函数之后就能开心的读代码了
加密分析
三个加密逻辑都在函数 sub_40127A 中
循环左移 rol1
dest 数组就是从文件中读进来的明文数据

shellcode 自解密
注意他从明文的 0x339 开始取了两字节的数据作为 key

shellcode 数据

xor加密

shellcode 解密
很显然题目的重点就在第二重加密了,既然 shellcode 是要执行的,那就先把 shellcode 全部转为代码看看吧

入口部分
可以看到前几字节是正常的:
1 | 41 54 push r12 |
作用
1 | push r12 |
这个 call 会:
- 把下一条指令地址压栈
- 跳到后面 0x45 字节处
数据区
前三条指令都是比较正常的,但后面就变的杂乱无章了,说明他们压根就不是正确的代码,他们很可能是需要解密的数据
call 落点
call 跳到这里:
1 | 41 5b pop r11 |
pop r11 拿到 call 返回地址。
也就是数据区首地址,所以 r11 = &encrypted_data
主循环
取一个加密字节
1 | movzx eax, byte ptr [r11 + r12] |
取 key
1 | mov bl, [rsi + r8] |
因为 r8 只能是 0 或 1 ,所以显然这里是取两字节的 key
异或解密
1 | xor al, bl |
所以最后的逻辑还原大概是这样
1 | for (i = 0; i < 0x42; i++) { |
爆破密钥
由于这段 shellcode 需要的 key 是从明文里来的,也就是说这个程序只能用来加密那个对应的文件(小王这么写是要准备提桶跑路吗🤣),所以只能进行爆破
要注意的一点是,数据区只有 0x42 的长度,在 0x4040EC 还有一段shellcode执行完之后的代码,注意到有两个 pop r11 的操作,但上面只有一个 push r11 的操作,所以解密之后的数据肯定有压栈的操作,这样可以缩小范围(虽然我是做出来之后看到了才想到的)

爆破代码如下:
1 | from capstone import * |
结果如下:

最终的加密逻辑为
1 | f(b) = ( nibble_swap(b-3) ^ 0x13 ) + 6 |
解密
最后写出解密代码如下:
1 | import hashlib |
解密结果如图:
