0x00 前言
早闻Nu1L出了一本《从0到1:CTFer成长之路》
今天逛BUU的时候发现多了一栏N1BOOK
点开做一下,发现第二题竟然被卡住了,输出的flag后面有不可见字符而且后五六个字符似乎没啥含义
折腾挺大一会才发现原来是因为忽略了一个细节…
耗时比我想象中的多,就当吸取不仔细的教训吧…
0x01 分析
来到main函数,可以看到程序定义了一个数组
但是这里有一个导致我后期输出的flag一直有问题的细节
可以看到…前一个还是[rbp+var_2A]
,下一个就变成了[rbp+var_28]
,也就是说[rbp+var_29]
不见了…
后期我还纳闷…明明flag要求长度45,但为什么这个数组只有44长度…然后自己在最后补了个0…
根据常识可知[rbp+var_29] = 0
继续分析,进行了一番变量、函数重命名以后得到如下反汇编结果
这是后来才知道的这个函数是RC4算法函数……
由于第一次见这个算法的时候没想起来这是RC4,我直接去看相关算法然后逆的
进去看看RC4函数
1 | __int64 __fastcall RC4(__int64 a1, __int64 input, __int64 a3) |
根据sub_40067A((const char *)a1, (__int64)&v5);
所传入的参数可以知道,这是一个与我们输入的flag无关的函数。
具体分析了一下可以知道它的作用是根据main函数中的key去得到一个数组v5。
显然可以通过gdb调试(简便)或者自己写个算法(较麻烦,因为如果用python写,还要处理一些数据类型如unsigned int/unsigned __int8等的问题)得到这个数组v5。
得到的v5会传给sub_400753((__int64)&v5, (const char *)input, v3);
函数。
其作用是生成校验数组v3,也就是a3,也就是RC4的第三个参数RC4output,然后去与main函数中生成的数组进行对比校验,具体操作是通过一个数与我们的输入进行异或。
分析一下,上一句中谈到的”一个数”,是与输入的flag无关的,即无论输入什么flag,只要满足flag.length == 45,在while循环中每次异或时循环次数相同时这个数都是固定不变的,那么可以将这些数(对应xor那句下断,它存在rdx中)提取出来作为一个数组,直接与main函数中的数组异或,便可得到flag。
0x02 思路
思路一
由于sub_40067A
生成v5数组的过程是与输入无关的,那么可以通过gdb调试或者算法等方法生成v5,然后模拟sub_400753
的逆过程生成flag。
思路二
根据上面的分析,可以将sub_400753
中与input[i]
异或的那个数提取出来作为数组,与main函数的数组异或即可生成flag。
思路三
后来才知道这个是RC4算法,有了密文(main数组即可转换为密文)和key(Nu1Lctf233),即可生成明文flag。
不过网上各个RC4算法好像不太一致,在线网站生成结果也不太一样……
最终找到了一个能用的算法。
0x03 解密脚本
1 | # -*- coding: UTF-8 -*- |
0x04 总结
细节很重要。
熟记各种常见算法会对逆向很有帮助。