SNERT招新题RE部分writeup

SNERT招新题RE部分writeup

十月 19, 2019

学校SNERT团队招新题中的RE部分,由我和另一个朋友出题,我审题,写个详细一点的writeup给刚接触的学弟学妹。

EasyRe

运行程序界面如下:

就是一般的输入flag的界面,32位程序,用32位IDA打开找到主函数(main),再按F5键转化成C语言伪代码:

就很明显能看到倒着的flag啊,复制过来倒转就行了,至于代码是用C++写的,内容是输入字符串之后将输入的字符串逆序后与图片中的逆序flag比较。另外因为这个题的flag就直接存在程序里面,只是逆序了一下,所以是可以直接用记事本之类的编辑文件打开找到这串比较明显的flag的,ollydbg也可以智能搜索搜出这串字符串。

EasyRe2

界面和上题一摸一样,直接用IDA打开是看不到函数的,因为程序有加壳,PEID查壳界面如下:

可以看到UPX,是一种常见的压缩壳,这种壳很好脱,可以在网上找脱壳机,这里我们利用ESP定律手脱:
ollydbg打开,单步一次执行pushad,ESP发生变化变红:

此时右键数据窗口跟随ESP并在该处下硬件访问断点:

之后按F9运行程序,程序停在某处,然后就可以直接在这里dump程序:

之后就可以用IDA打开dump出来的程序看函数了,但是找不到主函数,不知道从该哪里开始分析,就先打开字符串窗口看看字符串,发现有”Wrong”,”You got it”等字符串,猜测应该就是核心代码所在的位置:

于是双击跟进,然后按X查看交叉引用,看看这个字符串在哪些地方被使用了,再跟进找到关键函数:


然后上面那一串数字转化成字符就是flag了。

EasyRe3

程序运行还是要求输入flag,32位程序,无壳。
IDA打开,主函数伪代码如下:

判断输入字符串的长度后将输入的字符串第11位异或0x2C,之后遍历字符串挨个判断qizi(i^2C)是否等于v4[i]+i,都等于则跳转到win()函数,有不相等的则跳转到GG()函数,跟进qizi()函数分析:

可以看出该函数是将0x404040处的数据拷贝到v2再返回v2[a1^0x2C]a1是传进来的参数也就是上层函数的i^0x2C,这里又异或了一次同样的值,两次异或同一个值之后变回原值i,所以返回的实际就是v2[i]。然后我们跟进看0x404040处的数据是什么:

这一长串就是数据了,然后用这串数据倒推需要我们输入的正确flag,脚本如下:

1
2
3
4
5
6
7
8
9
10
s = [0x66, 0x6d, 0x63, 0x6a, 0x7f, 0x59, 0x4e, 0x6c, 0x67, 0x80, 0x26, 0x7d, 0x3d, 0x71,
0x6d, 0x40, 0x83, 0x70, 0x86, 0x43, 0x63, 0x74, 0x59, 0x89, 0x79, 0x73, 0x93, 0x98]
flag = ''
for i in range(28):
if i == 10:
flag += chr((s[i]-i) ^ 0x2c)
else:
flag += chr(s[i]-i)
print(flag)
# flag{THe_w0r1d_1s_t0O_CraZy}

Xor

运行程序要求输入flag,32位程序,无壳。
IDA查看主函数如下:

先判断长度为22后后遍历输入的字符串前21位,让每一个字符串异或它本身后面一个字符串,之后与变量flag比较,跟进查看flag变量的值:

因为不知道最后一位字符是什么(其实可以猜测是’}’),我们只能暴力,的脚本如下:

1
2
3
4
5
6
7
8
s = [0xa, 0xd, 0x6, 0x1c, 0x23, 0x68, 0x42, 0x2d, 0x36, 0x5c, 0x6a,
0x9, 0x33, 0x17, 0xb, 0x26, 0x39, 0x13, 0x1b, 0x4f, 0x5c]
for i in range(255):
flag = ''
flag += chr(i)
for j in range(21):
flag += chr(ord(flag[-1]) ^ s[20-j])
print(flag[::-1])#反向输出

在跑出来的众多结果中可以找到flag:

这里跑出来的结果都能通过程序的验证,但是只有这一个可以通过靶场的题。

simple

还是要求输入flag,还是32位程序,还是无壳。
主函数伪代码如下:

这个伪代码可能看起来有一点奇怪,因为这是用C++写的,但是分析方法是一样的,语言方面唯一需要注意的是::v3v3是两个不同的东西,别问我为什么强调这一点…
还有就是sub1()sub2()两个函数,是利用一个数组手动模拟的一个栈(先进后出),sub1()是进栈,sub2()是出栈,意思说每次sub2()都会取出最近一个sub1()存进栈里的数据。函数伪代码如下:


其中sv7是全局变量,跟进可查看v7的值是-1
加密算法是用程序里程序里的一串字符串s2与输入的字符串异或,结果与s1比较。脚本如下:

1
2
3
4
5
6
7
8
arr = [18, 5, 10, 23, 32, 24, 29, 17, 29, 55, 8, 91,
12, 31, 16, 59, 18, 113, 0, 23, 28, 1, 104, 3]
key = 'this_is_fake_flag?'
flag = ''
for i in range(24):
flag += chr(i ^ ord(key[i % 18]) ^ arr[i])
print(flag)
#flag{thIs_i5_trUe_flag!}

Maze

题名为迷宫,32位程序,无壳,运行程序没有任何提示,试着随便输入一个东西,程序关闭,用ollydbg动态调试发现最后程序输出了一个”Wrong>_<”之后才关闭的,应该是懒惰的出题人没有设置暂停所以程序瞬间就关闭了我们看不到输出的提示,还有开头也没有输入提示”Please input the flag”之类的。
IDA查看主函数伪代码:



可以看出程序先用两堆数据异或生成迷宫,v17表示横坐标,v16表示纵坐标,初始值都为0,即处于迷宫左上角,走到迷宫右下角即达到终点。之后遍历字符串,”u”表示向上走(up),”d”表示向下走,”l”表示向左走,”r”表示向右走,每走一步会判断是否越界或者撞墙。表示迷宫的二维数组中值为1表示墙,值为0表示可以走。但存储两堆用来异或生成迷宫的字符串跟进无法看值,因为用的是string类而且在主函数外声明赋值的全局变量但其实搜索字符串能看到两串奇怪的字符串:

用od继续调试,利用在ida中找到的生成迷宫的代码的地址,在ollydbg中找到该位置下断点,然后运行到该处跟随地址找到存储迷宫的位置:

然后执行程序,迷宫生成:

把迷宫按20*10画出来结果如图:

之后就找一条路从左上到右下。