题目分析
首先查壳后发现没有壳,直接使用IDA打开,但是使用F5反编译的时候,出现栈帧报错,使用ALT+K修复一下,然后直接F5得到主函数结构如下
分析主流程:
- 输入字符
- 判断字符长度
- 经过sub_401160函数处理
- 经过sub_401000函数处理
- 经过sub_4010E0函数处理
- 和最终的byte_402104数组比对,如果不一致就exit,否则最后输出Congratulation。
逆向分析
输入的字符串经过一系列变化后,与byte_402104处的代码进行比较,如果一致则成功,提取byte_402104数组的值,长度大小为44位。
1 | start_address = 0x00402104 |
接下来我们来看对字符串进行的变换操作,依次经过sub_401160、sub_401100、sub_4011E0三个函数,在sub_4010E0函数中,对v3输入字符串的操作依赖于v7故我们先分析sub_401000函数
sub_401000函数
输入,输出,长度
经过以上代码后,在a1处得到的是01 02 03 …FF。而在*v6所指向的位置是flag{this_is_not_the_flag_hahaha}的循环直到256个字符。
算法分析(KSA):
- 两组字符串,每组是256个字符串,分别命名为a[],b[],下标值v3初始为0
- 接下来的操作循环256次
- 依次取a[],b[]中对应位置的字符,下标v3,三者相加后取余数256后得到新的v3值,交换a[v3]和b[v3]的值
- 得到最后的字符串
这里是进行swap()操作,最后在38行给a1赋值。这里每次的a1的值也就是传入的v7的值最后的结果是固定的,我们通过动态调试得到如下数据:
经过数据处理:
1 | fi = open("new.txt",'r') |
得到v7的初始的值。
sub_4010E0函数分析
算法分析(PRGA):
- result是传入的密钥流,v6的指向是密钥流中的每个字符,v4是每个字符和v4相加后取余的结果
- 对于明文a2,循环其长度a3次,每次与某一指定字节,文中红线部分,做异或。
在这一步中关键点是红线部分的值,通过代码改写,可以发现红线部分的值是定值,那么我们再异或一次就可以得到初始的字符串了。
1 | v3 = 0 |
sub_401160函数
这是一个变换了密码表的base64加密,基本原则是3*8 = 4*6即将3个8字节的字符,变成4个由6字节编码的字符。
这里每次取三个字符,在do-while循环中通过或和移位操作将三个字符保存到一个数中。
这里的do-while循环中,每次将v3 右移,取6个字节(0x3f),作为Byte_402138数组的下标(下表中存储的就是源字符),得到加密字符。解密时我们要先提取出byte_402138数组,根据上文得到的字符串解出下标值。
1 | start_address = 0x00402138 |
总结
1 | flag = "" |
在题目中依次调用了变种的base64以及rc4算法,在动态调试的时候,将Option Header中的DLL Characteristics该为0,可以实现IDA和OD中的地址分布一样。