一级难度...我做了,不对,看wp看了好久的题目。
还是先看看有没有壳以及多少位
64位的,elf文件,我运行过,好像是没有结果,毕竟这是一道地图题目。
那就找string和main函数,很容易的就发现了,在main函数里面的有一个函数点开就能看到
所以,就直接看代码吧。
首先这个,main函数有两个函数,第一个
这里面,其实第5行和第7行,我用去寻找了什么意思,说是 “栈保护金丝雀值(Stack Canary),用于检测缓冲区溢出攻击”,毕竟第7行这个return 确实是在校验什么,因为任何一个数^他自己都为0,如果这个数有变化就不会正常返回0了。那么没有迷惑选项,可以看到,这里面就是定义了一个恶心的,好长的变量,我就直接叫AB0了,定义他为0.其实也可以xref(交叉引用)一下(选中直接按“x”)
看到最后一行,赋值为0.
然后就是main函数还有一个do while循环,这个循环要正常返回和V4有关,我们就直接打开V4就好。
__int64 sub_564F0B5ED940()
{
int v0; // eax
int v2; // [rsp+8h] [rbp-218h]
int v3; // [rsp+Ch] [rbp-214h]
char v4[520]; // [rsp+10h] [rbp-210h] BYREF
unsigned __int64 v5; // [rsp+218h] [rbp-8h]
v5 = __readfsqword(0x28u); // 输入值
v3 = 0; // 索引,不会清零
memset(v4, 0, 0x200uLL); // 给V4赋值为0
_isoc99_scanf(&unk_564F0B5EE278, v4, v4);
while ( 1 )
{
do
{
v2 = 0;
sub_564F0B5ED86C();
v0 = v4[v3];
if ( v0 == 'd' )
{
v2 = sub_564F0B5EDE23();
}
else if ( v0 > 'd' )
{
if ( v0 == 's' )
{
v2 = sub_564F0B5EDC5A();
}
else if ( v0 == 'w' )
{
v2 = sub_564F0B5EDA92();
}
}
else
{
if ( v0 == 27 )
return 0xFFFFFFFFLL; // 十进制的-1
if ( v0 == 'a' )
v2 = sub_564F0B5EDFEC();
}
++v3;
}
while ( v2 != 1 );
if ( dword_564F0B7EFAB0 == 2 )
break;
++dword_564F0B7EFAB0;
}
puts("success! the flag is flag{md5(your input)}");
return 1LL;
}
我看到代码是挺无语的,又点开看了几个函数...没有看的欲望了。真的是,还是太年轻,太浮躁了。
首先V5好像没啥作用,会不会就是前面那个金丝雀值,然后因为一些运算没了。让V4初始化全零,再scanf,那么可以确定V4是我们用户要输入的值,这里面有两个循环,一个while一个do while。再看这个V3一直在用,而且不在循环不会被清零,V4[V3]看着就是起到索引的作用的。再看看末尾,出现了熟悉的AB0,所以就可以看出来,这个最外层的while循环会循环3次,直到AB0等于2跳出去。还有就是,如果你把那些做判断的数字换成字符,机会发现里面包含了,a w s d和Esc,这不就是方向键吗,这个时候可以合理想想会不会是地图题目。这些分析的差不多,我们就可以看do while 循环里面的内容了。这里面的V2没啥作用,比较像引用函数?我觉得这个数字有没有无所谓。我们打开这个86C结尾的函数看看吧。
其实看到这里,如果你能分析出这个玩意到底在干嘛,那么这道题目基本就出来了。首先,这里面的V3就是前面说的金丝雀值,对函数逻辑没啥用,干扰的就不看。最明显的会看到,有 i 和 j 联合的组合技。两个循环,像不像15X15的地图,以及后面的 i X15+j 就差在旁边表备注“我是地图啦”。但是这个地图怪怪的,他有一个判断,判断这个点是不是等于3,如果是,那么就赋值给AB4和AB8( i 和 j 循环一个一个位置寻找3)。还有就是AB0也在里面,我们之前知道,AB0和最外层循环有关,那么对于这个内循环的函数来说,这个AB0是不会被清零的,就是说每次循环的这个AB0的值是不一样的,我们还可以知道这个dword_564F0B7EF020是一个有675字节的数组,225*3=675,这个是不是有三个地图啊,结合AB0的0,1,2的取值,我们再查看这个数组
这就是地图了,这里面5 dup(1):有5个1的意思,我们可以shift+e提取数据。前面的dd是4个字节的意思,那么,这也就是说,一个数据是4个字节,这点我倒是没注意,你可以看看这个数组的大小其实就是DWORD[675],那么提取数据就有坑了,我就是被坑了。
第一和第二个是有无空格的16进制表示,就比如 “77889966” 和“77 88 99 66”的区别。第三个是转化成字符串的形式,但是这个是有范围的,不能用字符串表示就就有很多个“/x”。第四个和第五个,你注意了,这是char,他是一个字节一个字节表示的,你一个数据的内存是4字节,这么搞就错了。你只要知道我一个数据是多大,相应的就不会有问题,第六个是,自己改变的,可能是字符或数字,这个就比较智能一点,所以我们选这个的话,他就会以4字节一个数据表示
我们数据前5个是1,第二幅图就是用char来表示了,所以就会有问题。我一开始也是写脚本提取数据的,但是就是会发现数据有问题,结果就是这个数据的字节数搞错了。这边我可以提供两个办法提取数据。
1.直接写脚本,很简单的
# “get_wide_byte(地址)" 1字节 # "get_wide_word(地址)" 2字节 # "get_wide_dword(地址)" 4字节 # "get_qwordn(地址)” 8字节 # “get_bytes(地址,字节数)"size个字节 # ,根据后缀可以分别知道提取了多少字节,依次是1,2,4,8,size字节 addr=0x000564F0B7EF020 arr=[] for i in range(0,675): arr.append(get_wide_byte(addr+i)) print(arr)
看清楚数据的字节数就没问题了,一般这种可以直接提取成数组的数据,方便我后面处理
2.直接用IDA里的,再用python脚本,改变一下数据格式
def main(): # 初始化存储675个地图元素的列表 ous = [''] * 675 # 读取675个输入值并提取每个值的首字符 for i in range(675): ins = input().strip() # 读取一行输入并去除空白字符 if ins: # 确保输入不为空 ous[i] = ins[0] # 只取第一个字符 for i in range(675): if i !=674: print(ous[i], end=',') else: print(ous[i]) if __name__ == "__main__": main()
前面可以看到,用IDA里面的,数据之间有空格换行,这段代码主要是strip()这个函数,可以
读取一行输入并去除空白字符,简直就是量身定做,呃(⊙﹏⊙),好像有换行也无所谓,直接是python数组的,但是这道题目要寻找3的位置,也不好说,就是我这么搞就是为了可以直观的看到地图,如果只是跑代码,直接在IDA里面复制数据也不用多处理什么。
得到数据,我们就可以搞地图出来啦,可以脚本可以手动,都很快也很简单
[1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 0, 3, 1, 1, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 0, 3, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0,
1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0]
或许看的不明白吧,但是你可以ctrl+F,全局搜索,有高亮显示,就很明显了。我们寻找还没有分析后面的代码,但是如果看图就可以知道大概了,首先每一个图里面有一个3,0很没有规律,而且太多了,估计是墙。我们直接找1,第一幅图最明显,3经过1的路径会看到4,这个4会不会就是出口?再看后面两副,基本上就是了,那我们基本就知道这道题目的意思。
我这边就选一个,这些名字特别长的,但是我们直接这就是数组和我们的AB0和 i 大哥和 j 大哥,他们就是3的地址,每经过一个1的地址,就会把这个1变成3,前面的3变成1,就这么跑下去,直到3的位置变成4了,那就结束循环了(结束内循环,毕竟是有两个图的)。最后就是获得路径了,可以直接看出来,也可以直接跑代码。如果要跑代码可以看我的《攻防时间:maze》这里面讲了。
ddsssddddsssdssdddddsssddddsssaassssdddsddssddwddssssssdddssssdddssMD5加密
aeea66fcac7fa80ed8f79f38ad5bb953
flag{aeea66fcac7fa80ed8f79f38ad5bb953}