1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > 攻防世界逆向高手题之reverse-for-the-holy-grail-350

攻防世界逆向高手题之reverse-for-the-holy-grail-350

时间:2023-12-18 08:49:37

相关推荐

攻防世界逆向高手题之reverse-for-the-holy-grail-350

攻防世界逆向高手题之reverse-for-the-holy-grail-350

继续开启全栈梦想之逆向之旅~

这题是攻防世界逆向高手题的reverse-for-the-holy-grail-350

下载附件,照例扔入exeinfope中查看信息:

64位ELF文件,无壳,运行一下程序看一下主要显示字符串信息:

然后照例扔入IDA64中查看伪代码信息,有main函数看main函数:

.

.

(这里积累第一个经验)

C++前面做题经验中说过了看得是最后那个函数名,因为C++是面向对象的,没有使用"using namespace 类名; "时就没有对应的命名空间,就只能用长类名导入函数。

.

附上以前的笔记:

所以下图1~4的红框都是C++的cout输出函数和cin输入函数,但是cin输入的都是v11,v11能被覆盖就说明不是关键,4、5红框中cin的对象是&userIn[abi:cxx11],这是一个地址,应该就是我们要的flag了

.

.

(这里积累第二个经验)

然后后面代码就渐渐看不懂了,只能从后面往前推,最后一个Auuuuuuuugh是我们前面运行时显示的,由它出发跟踪到v4,前面v4 = stringMod(v9);有一个自定义函数,而自定义函数通常是关键。然后再前面的_M_construct<char *>结构体应该是简单的赋值把,把&userIn[abi:cxx11]先给了v7再给了v9。(看不懂的话自能当它不是关键了,不然总是觉得_M_construct<char *>会有什么隐藏操作的话就没法做了。)

.

.

(这里积累第三个经验)

双击跟踪入stringMod函数,代码有点长,以为有冗余代码,按照以前的经验,查找与输入相关的代码,如下面红框所示:a1是输入的flag,a1给了v2,v2给了v14,v14给了v5和v6,所以这里没有冗余代码,都是关键代码:

.

.

(这里积累第四个经验)

分析第一个循环,有点意思,循环条件是v3 != v1,而v1 = a1[1],v3是从0开始的数,v1是一个字符,怎么也是从32开始的。所以这里一开始用从0一直循环到字符的ASCII码,明显超出了flag输入的位数,一开始我也很懵,后来发现后面有flag位数限制,所以这里超了范围又有什么关系呢,后面限制回来不就行了?(后面限制了这个flag位数为18位)

然后就是这里的if语句,除法符号 ‘/’ 会有余数,所以这里必须是3的倍数,这里的v4=-1一开始我不确定执行有没有影响,后来发现最后return (unsigned int)(v7 * v4);语句表明这个if语句不能执行,所以 i 为 3 的倍数时flag必须等于firstchar数组内的元素。

那么第一个循环就确认了flag的0、3、6、9、12、15、18位。

.

.

(这里积累第五个经验)

接下来分析第二个循环,这里必经一个循环,循环条件也是很有意思&v15 != (__int64 *)v6取v15的地址和v6作比较,v6也是一个地址,关键是v6是rsi源地址寄存器寄存器,没法跟踪栈内位置。后来查了很多资料,有人说rsi相当于rbp的位置,如第二幅图所示,v15地址是rbp-18,所以循环条件是18,也就是输入的flag的位数。

但是也有人说rsirsp的位置,所以这里相差是48,和前面一样超出了flag的位数,但是最后的循环中限制了取18位进行flag操作,所以也不影响。(我更支持这个!)

那么这里必经的循环就是简单的异或操作的,和以前总结的一层、二层加密一样,第一个循环的数没有加密,后面循环的数都是二层加密,解密时要考虑异或回来。

.

.

(这里积累第6个经验)

分析最后一个循环,这个循环比较复杂,所以放上总图一步步来:

.

.

首先v8从1开始,循环条件是到19,所以这里就是前面flag位数的最终确定和根源,把最重要的东西放在最后面也是够呛的,加大了题目难度。

.

.

然后后面就是不同位数的逻辑代码了,因为只有18位,所以一个个手算其实更方便:

先看v11的变化,v11从0开始:

0 120 120 120 120 120 120 120 120 120 120 120

再看v9的变化,v9从0开始,下面v9与v11对应:

0 011 122 233 344 455 566 677 788 899 91010 10

然后看v5的变化,v5是输入flag的首地址但是经过异或加密,和第一个循环一样if成立则对应固定的异或后的位数:

0 123 456 789 101112 131415 161718

标注的是v5对应的位数,也就是thirdchar数组对应着异或后的flag的2,5,8,11,14,17位

同样的在看第二个if条件对应的位数,这里v10和v15的关系有前又有后,其实跟踪起来还是很绕的,但是前面是2,5,8、0,3,6。也应该要想到这里是1,4,7。18位的flag三层循环对应三份位数。

v11:

0 120 120 120 120 120 120 120 120 120 120 120

v10从1开始,v10 *= *v5,因为v10在v5之前,所以v10永远比v5少一:

1 v5[0]v5[0]v5[1]1 v5[3]v5[3]v5[4]1 v5[6]v5[6]v5[7]1 v5[9]v5[9]v5[10]1 v5[12]v5[12]v5[13]1 v5[15]v5[15]v5[16]

v5,因为v5在v10赋值之后,所以v5永远比v10多一:

012 345 678 91011 121314 151617 18

.

.

流程基本梳理完了,现在可以写脚本了,在那之前先dump下三个数组内容:

addr=[0x601840,0x601860,0x601880]list1=[]for i in range(6):for a in addr:list1.append(Dword(a+4*i))print(list1)

.

.

逆向逻辑脚本:

key1=[65, 105, 110, 69, 111, 97,]key2=[751, 708, 732, 711, 734, 764]key3=[471, 12, 580, 606, 147, 108]flag=[0 for i in range(18)]index=0for i in range(0,18,3):flag[i]=key1[index]index+=1print(flag)v7=666xor=[]for i in range(18):xor.append(v7)v7+=v7%5print(xor)index2=0for i in range(2,18,3):flag[i]=key2[index2]^xor[i]index2+=1print(flag)index3=0for i in range(1,18,3):for a in range(32,128,1):if ((flag[i-1]^xor[i-1])*(a^xor[i]))%(flag[i+1]^xor[i+1])==key3[index3]:flag[i]=aindex3+=1breakprint('tuctf{'+''.join(map(chr,flag))+'}')

.

.

(这里积累第7个经验)

注意后面的逆向要考虑回一层的异或加密,所以这里直接把异或加密位数导出来,因为flag只有18位,所以我们也取前18位即可。

然后就是这个v10 % *v5 != masterArray[v9]的逆向,这个很难逆,我看了很多资料都显示直接正向爆破。因为i-1i+1在前面循环中都获取了,所以这里可以直接用,然后注意时刻考虑一层的异或加密即可。

.

.

结果:

.

.

总结:

1:

(这里积累第一个经验) C++前面做题经验中说过了看得是最后那个函数名,因为C++是面向对象的,没有使用"using namespace 类名; "时就没有对应的命名空间,就只能用长类名导入函数。 . 附上以前的笔记:

.

2:

(这里积累第二个经验)

然后后面代码就渐渐看不懂了,只能从后面往前推,最后一个Auuuuuuuugh是我们前面运行时显示的,由它出发跟踪到v4,前面v4 = stringMod(v9);有一个自定义函数,而自定义函数通常是关键。然后再前面的_M_construct<char *>结构体应该是简单的赋值把,把&userIn[abi:cxx11]先给了v7再给了v9。(看不懂的话自能当它不是关键了,不然总是觉得_M_construct<char *>会有什么隐藏操作的话就没法做了。)

3:

(这里积累第三个经验)

双击跟踪入stringMod函数,代码有点长,以为有冗余代码,按照以前的经验,查找与输入相关的代码,如下面红框所示:a1是输入的flag,a1给了v2,v2给了v14,v14给了v5和v6,所以这里没有冗余代码,都是关键代码。

4:

(这里积累第四个经验) 分析第一个循环,有点意思,循环条件是v3 != v1,而v1 = a1[1],v3是从0开始的数,v1是一个字符,怎么也是从32开始的。所以这里一开始用从0一直循环到字符的ASCII码,明显超出了flag输入的位数,一开始我也很懵,后来发现后面有flag位数限制,所以这里超了范围又有什么关系呢,后面限制回来不就行了?(后面限制了这个flag位数为18位)

.

然后就是这里的if语句,除法符号 ‘/’ 会有余数,所以这里必须是3的倍数,这里的v4=-1一开始我不确定执行有没有影响,后来发现最后return (unsigned int)(v7 * v4);语句表明这个if语句不能执行,所以 i 为 3 的倍数时flag必须等于firstchar数组内的元素。

那么第一个循环就确认了flag的0、3、6、9、12、15、18位。

5:

(这里积累第五个经验) 接下来分析第二个循环,这里必经一个循环,循环条件也是很有意思&v15 != (__int64 *)v6取v15的地址和v6作比较,v6也是一个地址,关键是v6是rsi源地址寄存器寄存器,没法跟踪栈内位置。后来查了很多资料,有人说rsi相当于rbp的位置,如第二幅图所示,v15地址是rbp-18,所以循环条件是18,也就是输入的flag的位数。

.

但是也有人说rsirsp的位置,所以这里相差是48,和前面一样超出了flag的位数,但是最后的循环中限制了取18位进行flag操作,所以也不影响。(我更支持这个!)

.

那么这里必经的循环就是简单的异或操作的,和以前总结的一层、二层加密一样,第一个循环的数没有加密,后面循环的数都是二层加密,解密时要考虑异或回来。

6:

(这里积累第6个经验) 分析最后一个循环,这个循环比较复杂,所以放上总图一步步来:

.

首先v8从1开始,循环条件是到19,所以这里就是前面flag位数的最终确定和根源,把最重要的东西放在最后面也是够呛的,加大了题目难度。

.

然后后面就是不同位数的逻辑代码了,因为只有18位,所以一个个手算其实更方便:

.

先看v11的变化,v11从0开始:

0 120 120 120 120 120 120 120 120 120 120 120

.

再看v9的变化,v9从0开始,下面v9与v11对应:

0 011 122 233 344 455 566 677 788 899 91010 10

.

然后看v5的变化,v5是输入flag的首地址但是经过异或加密,和第一个循环一样if成立则对应固定的异或后的位数:

0 123 456 789 101112 131415 161718

.

标注的是v5对应的位数,也就是thirdchar数组对应着异或后的flag的2,5,8,11,14,17位

.

同样的在看第二个if条件对应的位数,这里v10和v15的关系有前又有后,其实跟踪起来还是很绕的,但是前面是2,5,8、0,3,6。也应该要想到这里是1,4,7。18位的flag三层循环对应三份位数。

.

v11:

0 120 120 120 120 120 120 120 120 120 120 120

.

v10从1开始,v10 *= *v5,因为v10在v5之前,所以v10永远比v5少一:

1 v5[0]v5[0]v5[1]1 v5[3]v5[3]v5[4]1 v5[6]v5[6]v5[7]1 v5[9]v5[9]v5[10]1 v5[12]v5[12]v5[13]1 v5[15]v5[15]v5[16]

.

.

v5,因为v5在v10赋值之后,所以v5永远比v10多一:

012 345 678 91011 121314 151617 18

7:

(这里积累第7个经验)

注意后面的逆向要考虑回一层的异或加密,所以这里直接把异或加密位数导出来,因为flag只有18位,所以我们也取前18位即可。

然后就是这个v10 % *v5 != masterArray[v9]的逆向,这个很难逆,我看了很多资料都显示直接正向爆破。因为i-1i+1在前面循环中都获取了,所以这里可以直接用,然后注意时刻考虑一层的异或加密即可。

解毕!敬礼!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。