好像是第一次觉得自己在参加比赛...
1.RE-open the gate of RE
查看文件头是elf文件
使用IDA打开,查看字符串发现什么都没有
然后使用linux的edb打开动态调试。开头直接jmp到这个函数,F8执行完这个函数就显示了字符串
然后F7跟进这个函数
继续往下执行第二个syscall会卡住,然后直接跳过到执行下面的循环
阅读汇编得知这里是将这串字符串从T开始每个字符与后面一个字符异或
执行完之后的结果如下,最后一个没有被异或的字符为’!’
紧接着下面开始的汇编代码
将0x4000d7与0x40010c处开始的0x28个字符进行对比
其中0x4000d7处的字符串正是一开始被处理的字符
查看到0x40010c处的字符
其中返回结果如果不等就会结束程序,如果相等就会有Yay!压栈
所以知道题目的关键在40010c处的字符串
由上面的异或关系:当前字符与后一个字符进行异或来替换当前字符,最后一个字符不进行异或处理。所以这里解题的关键应该是找到最后一个字符。
先试了0x40010c+0x28当作最后一个,又试了0x40010c+0x29处的字符都不对。
最后看到那个}猜测如果是flag的结尾 那就是那个最后一个不被异或的字符
然后按照猜测和上面那个算法进行逆向
得到flag
python脚本如下
2.RE-Fake
查看文件仍然是elf文件,先用IDA打开,查看字符串仍然没什么收获。于是还是到Linux用edb进行动态调试,顺便在IDA里找到main函数反编译一下
在edb 使用 F9直接来到main函数
然后接下来就可以在IDA与ebd动态静态结合分析
在main函数里,s是输入的字符串
然后根据s的长度用随机数对602160处的数组进行随机数赋值,这里随机数种子一定是给定的,每次产生的随机数数字相同,然后用该数组的每个值与输入的字符逐个进行异或。
然后将处理后的字符串与已知的字符串s2进行对比,如果相同则正确。
这里也可以通过s2来确定字符串的长度 为1d。
看似题目就是这样的,然后处理之后发现仍然不能通过程序验证。
先动态调试
得到固定的随机数组
S2的数组可以在IDA里也可以在edb里得到
输入任意的一串字符进行对比,memcmp函数的返回值是
,意思就是比对相等
然后乱输入其他的值,该函数的返回值都是0,所以猜测这个函数应该被修改过了。
所以步入查看
首先在这里检查了输入的字符串长度是否是0x1d,如果不相等,就返回0
这里再检查了随机数组长度是否是0x1d,如果相等再进行下一步
然后一步一步执行观察,直到看到了
这里让s字符串的第二个字符减去第一个字符的值然后再替换原来的第二个字符
再次执行到这里
发现使用e7-d0,即让原来的第三个字符减去原来的第二个字符再替换成第三个字符
全部执行完之后,s字符串变为
再往后执行
看到下一个cmp指令
将6b与3c进行对比,这里因为我输入的是1,1的Ascii码与随机数组的第一个数0d异或的结果是3c。所以这里就知道了算法。
将处理后的s字符与随机数组逐个进行异或,就得到flag\
python脚本
import re
d=[107, 101, 23, 227, 11, 186, 180, 171, 217, 242, 159, 55, 9, 161, 230, 187, 211, 150, 37, 137, 143, 148, 2, 203, 175, 99, 122, 205, 165 ]
c=[13, 44, 112, 150, 121, 223, 235, 155, 172, 134, 192, 95, 56, 203, 135, 248, 184, 201, 66, 198, 251, 203, 36, 165, 203, 60, 9, 160, 230]
for i in range(0,len(d)):
for j in range(0,150):
if d[i] == j^c[i]:
print(chr(j),end='')
break
3 RE-easy Algorithm
仍然是ELF文件,拖入IDA。查看字符串
交叉引用定位到函数main
注意到a2是main函数的参数,所以是main函数传参,很好想到这里是flag的长度限制。
这里将s分为四个DWORD类型 每个DWORD占四个字符 所以知道这里把16位的flag分成四段每段四位。
然后用四个相同的函数对每段进行处理
然后再通过一个验证,满足五个条件就可以通过。
先看上面的sub_40083c函数如何处理的
进入该函数看到四个常数,很容易想到这是md5算法,于是想先用动态调试验证一下猜想是否正确。
因为用到main函数传参..因为我基础知识不牢固的原因,选择使用gdb调试。但是同时使用edb观察总体的汇编代码
用edb打开,F9到达main函数
可以看到mainh函数开始于0x400e00处,于是在gdb里下断点于此处并且传参
16个’1’
在进入处理函数之前,先找到数据存储在内存的哪里。于是在memset函数之后的0x400f0e下断点,然后一步一步执行查看堆栈
注意到这里eax每次+4,那eax应该就是数组下标,[eax]应该就是存储数组的地方
执行到400f12时查看寄存器
再查看[rax]里的值
这里是输入的16个1
再执行到
记下[rax]的位置
再继续执行到
找到了存储数据的地方
然后根据IDA里的流程,直接来到sub_40083c函数进行处理
下断点在该函数处
,
该函数有三个参数,一个是我们输入的字符串分成四段中的一段,一个是处理数量4,一个是存储处理后的地址。查看执行函数之前的寄存器知道
rax是我们输入的字符串,esi是数量4,rdx就是存储处理完后的字符
执行完第一个函数之后,查看[rdx]里的值。因为第一次执行之前,rdx应该是ebp-0x50
所以是0x7ff......dd00,之后每次应该在dd10,dd20,dd30处。
用1111进行md5加密
发现正是这里存储的数据
所以猜测正确。这里对四段数据分别进行了md5加密
继续往后看,strtol函数百度了一下,是一个把字符串转数字的函数。
因为我们输入的’1111’是字符的形式,这里转为了10进制的数字1111
再看if里的五个条件函数
第一个
要求输入的四段数字,每段都比前面一段的数值大,且每段的第一位不能为0
即 类似于 1000100110021002才可以
再看下面四个一样的函数处理
这里a1是我们输入的字符串中的一段,a2是md5加密后的数据
这里的数据处理比较简单
大概就是把md5的[0:2]和[2:4]的16数字当作一个字符然后分别进行简单处理后与我们输入的四个数字进行对比,不要求按位相同,但是要求我们输入的每个数字只能用一次
比如我们输入的1234 md5处理后的字符为1134,这里1只能通过一次,第二个就不可以通过了。
所以写一个简单的爆破脚本
import hashlib
md = hashlib.md5()#构造一个md5
f1=f2=f3=f4=0
for i in range(1000,10000):
md = hashlib.md5()
md.update(str(i).encode())
str1=md.hexdigest()
x1=(int(str1[0:2],16)&0xf)+48
x2=(int(str1[0:2],16)>>4)+48
x3=(int(str1[2:4],16)&0xf)+48
x4=(int(str1[2:4],16)>>4)+48
if x1==ord(str(i)[0]) or x1== ord(str(i)[1]) or x1==ord(str(i)[2]) or x1==ord(str(i)[3]):
if x2==ord(str(i)[0]) or x2== ord(str(i)[1]) or x2==ord(str(i)[2]) or x2==ord(str(i)[3]):
if x3==ord(str(i)[0]) or x3== ord(str(i)[1]) or x3==ord(str(i)[2])or x3==ord(str(i)[3]):
if x4==ord(str(i)[0]) or x4== ord(str(i)[1]) or x4==ord(str(i)[2])or x4==ord(str(i)[3]):
if(x1!=x2 and x1!=x3 and x1!=x4 and x2!=x3 and x2!=x4 and x3!=x4):
print("x1 "+chr(x1))
print("x2 "+chr(x2))
print("x3 "+chr(x3))
print("x4 "+chr(x4))
print(i)
print('ending')
输入3179679093279624得到flag