第三届上海大学生网络安全大赛juckcode
这个比赛在几个星期前就结束了,作为一个学逆向2个半月的菜鸟,我没有能力参加,只是问同学要题目自己做了一遍。(i春秋上有原题)
0x0 前期准备
题目下载解压之后有flag.enc和juckcode.exe两个文件,打开flag.enc发现:
没见过的密文格式,看来是一场恶战。用PEiD和EXEInfoPE分别打开juckcode.exe,没发现壳。再用PEiD的插件–Krypto ANALyzer,没发现已知的加密算法。在cmd里输入.\juckcode.exe 123456,输出结果是:
GFtF@@AHqrHHEEFBsBsvCHH@GFtF@@@@@H@@
看来flag就是命令行参数经过juckcode输出和上图一样的字符串。
0x1 静态分析
打开IDA PRO 7.0加载juckcode.exe(这里最好先用aslr_disabler.exe取消掉系统对程序的空间格局随机化),shift+F12查看一下有没有关键字,看到有一个:
查看交叉引用,定位到汇编地址,再Force Analyze,看到了一个有用的函数:
原来juckcode.exe也可以通过./flag文件作为输入。但是这里出现的代码实在是看不下去,放弃静态分析。
0x2 动态分析
我新建了一个文件—“flag”,里面写着flag{123456}。然后打开OD后试着F8到底,发现有几个大跳,感觉每个大跳就是完成一个任务,特征是有一堆无法解析的16进制,然后就是代码(跳转到的地方在retn比较下面):
00403412是第一个大跳后的地址。继续F8了一会,发现了很关键的一个位置:
这里的DS:[006B97E8]指向了一串字符串,本能地放入BASE64解码器解码:
可见00403412这部分代码就是把输入进行BASE64编码。
第二次大跳会跳到4034F7:
继续F8到403510,可以看到EAX指向了一串不知道干嘛用的字符串:
403531又出现一串新的字符串:
经过多次F8,发现了第三个不知道什么意思的字符串:
跟进了很久也不见有新的大跳,但是有两个地方很可疑:
这两个地方都是把ds:[eax]的值传给ss:[ebp+edx*4-0x4xx]。
经过反复观察,我发现[ebp+edx*4-0x410]上面的ds:[eax]是指向ZmxhZ3sxMjM0NTY3ODlhYmN9
的指针,
40F上面的ds:[eax]指向pqyhp7txcnN0dXa9
,
然后40D对应AACAgICAAIAAgACAAICAAICA
,
40E对应YmLi4uLiYuJi4mLiuLiYuLi
。
而且这里的大跳点在:
ss:[ebp-0x95c]恒定为0x10,每次执行完上述操作eax+1,直到0x10才跳转。
这就是说每次循环执行将输入的字符串的base64编码第i位放入410,将pqyhp7txcnN0dXa9
的第i位放入40f,后面两个字符串类似。
我们直接执行到大跳之后,然后F8到这:
这是个64位字符串,粗略看一下发现和我的推测一样。
经过了不少F8,又发现一个制造字符串的地方:
ss:[ebp-0x810]每经过一次循环就会产生两个字符,开头是6696009a...
结束循环的地方如上图所示,当ss:[ebp-0x978]==0x30时,会置一个变量为0,多次传递变量到ecx再test ecx,ecx。此时ecx时0,所以je 403700成立,大跳。
大跳后按几次F8可以发现一段奇怪的字符串:
这里看了半天也没发现什么,我决定往下F8看看。(其中我在jnz下面一行下了断点,跳过了循环很多次的jnz),又发现了一个关键的地方:
edx是从ss:[ebp+ecx-0x810]中获取的,而ss:[ebp+ecx-0x810]正正是我上面提到的奇怪的字符串!!!edx在这里加了0x10,在后面又赋值回奇怪字符串的地址上。那我看看整段奇怪字符串+0x10之后是什么东西:
40376c是跳转条件:eax==0x60时,跳到4037A8,再F8一下,查看ss:[ebp-0x810]里的数据:
这就是flag.enc的形式了。
最后一个问题就是:奇怪的字符串到底是怎么来的?
其实这里只有0-9和a-f,一看就知道是16进制。经过我们班mingo大神的指导后,才知道上面生成了
ZpYAmqmAxyLChhiAZp4g37uIstLCxxiAMcYAjnuIMNJA00iANd4gTXmAZaLC99iA
之后base64解码了一次,因为解码后的第一位是f,后面大多是乱码,而奇怪字符串的前两个字符‘66’恰恰是f的ascii码(16进制),所以推测是base64解码。
接下来将这些16进制字符串的16进制再加0x10就可以得到FFIF@@...
字符串的形式了。
上面还有一个问题就是我输入的flag太短导致最后结果比flag.enc里的短得多。我后来测试了输入flag{123456789abc},发现AACAgICAAIAAgACAAICAAICA
和YmLi4uLiYuJi4mLiuLiYuLi
的24位全用上了,不像上面只用了16位,而且pqyhp7txcnN0dXa9
变成了一串24位的字符串,不太一样,不过这不妨碍我们写逆程序,因为我们写逆程序时根本不需要用到这三个字符串。
0x3 逆向代码
可以愉快地写我的(看不下去的)python了:
import base64
import string
secret = 'FFIF@@IqqIH@sGBBsBHFAHH@FFIuB@tvrrHHrFuBD@qqqHH@GFtuB@EIqrHHCDuBsBqurHH@EuGuB@trqrHHCDuBsBruvHH@FFIF@@AHqrHHEEFBsBGtvHH@FBHuB@trqrHHADFBD@rquHH@FurF@@IqqrHHvGuBD@tCDHH@EuGuB@tvrrHHCDuBD@tCDHH@FuruB@tvrIH@@DBBsBGtvHH@GquuB@EIqrHHvGuBsBtGEHH@EuGuB@tvrIH@BDqBsBIFEHH@GFtF@@IqqrHHEEFBD@srBHH@GBsuB@trqrHHIFFBD@rquHH@FFIuB@tvrrHHtCDB@@'
a = []
b = ''
for ch in secret:
a.append(ord(ch) - 16);
i = 0
m = ''
while i< len(a):
m = chr(a[i])+chr(a[i+1])
n = int(m,16)
b += chr(n)
i += 2
a = base64.encodestring(b)
a=a.replace('\n','')
i = 0
q = ''
while i< len(a):
if i%4 == 0:
q += a[i]
i += 1
# the result is not the right padding for base64,so I add an '='
q += '='
q = base64.decodestring(q)
print q
PS: 1.python的base64库在encode时会自动在76个字符之后加上换行符,
所以要replace掉;
2.如果最后不加上=号的话会报错:
binascii.Error: Incorrect padding
;
0x4 总结
这个程序的加密步骤就是:
1.将输入(或.\flag里的内容)base64编码生成a;
2.每次循环将a的内容和三串字符串的每一位拼凑起来,直至到达字符串长度,生成b;
3.将b进行base64解码,生成了乱码c;
4.将乱码c的每一位的ascii码转为字符串(比如第一位是‘f’,ascii码是66,则将66转换成字符串‘66’),全部转化成功后生成d;
5.将d的每一位的ascii码再加0x10(比如上面生成的字符串‘66’,ascii码是36 36,再加0x10就是‘FF’(46 46)),全部加上0x10之后生成e;
6.e就是flag.enc里的密文格式了;
6个小时的挑战,搞出来以后特别有成就感,希望我能在这条路上坚持下去。
特别鸣谢:mingo老