这道题从我开始看到做出差不多花了8,9个小时,已经是我做过最复杂的逆向了(其中部分还在大佬的帮助下)。没有大佬的帮助,我肯定是做不出的,在此先感谢一下。
写这篇文章的主要目的是为了记录,算是我逆向路上的一个新的里程碑吧。
程序下载:Gadgetzan,patch时间的版本:Gadgetzan_patchtime。
为了方便理解,我附上我的idb文件:Gadgetzan.idb
首先跟着字符串来到我们的主函数:
1 | int __cdecl start_here(int a1) |
得知input长度为16,且加密分在了两个函数内,就是这里的enc_func1
和enc_func2
。
func1先大致看了一眼,是将输入的字符串加密成等长(16bytes)的字符串,我们叫他chg_ipt
。
我们看func2,毕竟逆向是倒推的过程。
1 | int __cdecl enc_func2(BYTE *chg_ipt) |
func2分为PART1和PART2,PART1是将输入的字符串的每一bit按照某个box打乱,放在一个16*16的内存中,内存里看大概是这样的
由于从左到右和从右到左,每1bit被放了两遍,所以一共有8*16*2 = 16*16 = 256。
用于随机的box
伪算法:
1 | for i in range(16*8): |
PART2是用来验证的,根据PART1算出来的bits_arr和另一个16*16的box中的数做一定的运算得出16个数,若全部相等则success。
256的box。
check用的16个数:
伪算法:
1 | for i in range(16): |
由于bits_arr[j*16+i]只存在0和1,所以check_word所异或的只能为box[i*16+j]或0,因此,我们可以爆破这16位0和1。
脚本:
1 | #coding=utf8 |
得到
1 | (1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0) |
再对PART1求逆,就是根据box把相应的bit放回原来的位置
1 | out = [0]*256 |
得到chg_ipt
:cad3e543f23c3fec33a93f95dd8c4c2a
再来看func1。
1 | int __cdecl enc_func1(char *input) |
前面一长段的v16不用管他,因为只是用来生成key的,key可以动态dump。之后就是把key,input传入一个向AES一样的函数,代码如下。
1 | int __cdecl AES_LIKE(int input, int output, int key) |
这个函数总体上没和传统的aes改变多少,主要改了两处:
一是MixColumn函数中的a。
正常的a是这样的。
而这里是0xD,0xE,0xF,0xD。
对这个a求逆我们用matlab。
所以inv_a = [7,1,5,2](此处感谢大佬的帮助)
二是自己加了一个vm函数
1 | int (__cdecl *__cdecl vm_func(_DWORD *a1))(_DWORD, _DWORD) |
字节码解释器代码:(此处再次感谢大佬的帮助233)
1 | unsigned int __cdecl interpreter(struc_1 *vm_ds) |
由此还原字节码:
1 | from pwn import * |
1 | 0x0 : xor $0, $0 |
通过整理逻辑,写出加密函数和解密函数:
1 | def unknow(plain): |
结合前面变形的AES,写出完整的func1逆代码。
1 | head = 'e6b8a1e38082e6b8a1e38082e6b8a1e38082e6b8a1e38082e6b8a1e38082e6b86b18f5bbeb9a13034a799381acc13262133194f3b2d21471546ab592d4e8532a3583528bde1941889460d20938a1e06b0ac04d6cb812591dec78ec8f3890bfa5fea8569220b1171ab4d1c5138c702578bb8513a003974abdefefa632d77f1997554f81eb75fe96f1c12f53e24d5f769aa4ec4822a77b029f4894a4ad9febbd3a010c869674f21067b5dd4385f882351f0d604eb3aa1b4c2ce28fe8817d6455bbfbee8d738f1c9d143ac1de91c243eb8e43aa0a3de9b146110b3eae90765afb2bf3f3bf287cef223c462efcad846d1723'.decode('hex') |
得到我们的输入为:mo4a3Ov2r5qIYgF8
正确后,程序会打印tickets:
可以看出,这是一个二维码。
简单处理并扫描,提示我们flag为输入加上“D4wn”
终于得到了我们的flag:mo4a3Ov2r5qIYgF8D4wn
ye~~~~