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

99 篇文章 33 订阅

攻防世界逆向高手题之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 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0

再看v9的变化,v9从0开始,下面v9与v11对应:
0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 9 9 9 10 10 10

然后看v5的变化,v5是输入flag的首地址但是经过异或加密,和第一个循环一样if成立则对应固定的异或后的位数:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

标注的是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 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0

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多一:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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=0
for i in range(0,18,3):
	flag[i]=key1[index]
	index+=1
print(flag)

v7=666
xor=[]
for i in range(18):
	xor.append(v7)
	v7+=v7%5
print(xor)

index2=0
for i in range(2,18,3):
	flag[i]=key2[index2]^xor[i]
	index2+=1
print(flag)

index3=0
for 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]=a
			index3+=1
			break
print('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 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0
.
再看v9的变化,v9从0开始,下面v9与v11对应:
0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 9 9 9 10 10 10
.
然后看v5的变化,v5是输入flag的首地址但是经过异或加密,和第一个循环一样if成立则对应固定的异或后的位数:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
.

标注的是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 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0
.
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多一:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

7:
(这里积累第7个经验)
注意后面的逆向要考虑回一层的异或加密,所以这里直接把异或加密位数导出来,因为flag只有18位,所以我们也取前18位即可。
然后就是这个v10 % *v5 != masterArray[v9]的逆向,这个很难逆,我看了很多资料都显示直接正向爆破。因为 i-1i+1在前面循环中都获取了,所以这里可以直接用,然后注意时刻考虑一层的异或加密即可。

解毕!敬礼!

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沐一 · 林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值