CTF逆向-Dig the way Interesting Pointer-通过栈溢出方式覆盖变量以达到修改执行流程的目的
来源:https://buuoj.cn/
内容:dig the way(to get the key)!
附件:链接:https://pan.baidu.com/s/1ohai-S1epbYp2O-hzH8CRQ?pwd=rhz5 提取码:rhz5
答案:8cda1bdb68a72a392a3968a71bdb8cda
总体思路
发现流程正常执行的话无法获得flag
尝试通过栈溢出的方法修改正常的流程
推导出在该流程下能够使得flag输出的条件,并通过栈溢出满足该条件
详细步骤
-
查看文件内容
-
发现有三个方法是明显要被轮流调用的,点进去查看每个方法
-
func0 是交换下标为 a2和a3的值,随后返回1
-
int __cdecl func0(int a1, int a2, int a3) { int v4; // [esp+Ch] [ebp-4h] v4 = *(_DWORD *)(4 * a2 + a1); *(_DWORD *)(a1 + 4 * a2) = *(_DWORD *)(4 * a3 + a1); *(_DWORD *)(a1 + 4 * a3) = v4; return 1; }
-
-
**func1 **是返回
|a[x]+a[y]| - |a[x]| - |a[y]| + 2
-
unsigned int __cdecl func1(int a1, int a2, int a3) { return abs32(*(_DWORD *)(4 * a2 + a1) + *(_DWORD *)(4 * a3 + a1)) - abs32(*(_DWORD *)(4 * a3 + a1)) - abs32(*(_DWORD *)(4 * a2 + a1)) + 2; }
-
-
func2 是返回
|a[x]| + |a[y]| - |a[x]+a[y]| + 2
-
unsigned int __cdecl func2(int a1, int a2, int a3) { return abs32(*(_DWORD *)(4 * a3 + a1)) - abs32(*(_DWORD *)(4 * a3 + a1) + *(_DWORD *)(4 * a2 + a1)) + abs32(*(_DWORD *)(4 * a2 + a1)) + 2; }
-
-
-
整理得到流程
-
发现只有当v_temp[3] == 0的时候,才会调用
get_key
输出flag。 -
根据绝对值的性质,发现只有
v_funcs[1]
可以返回0,且当a=1,b=-1的时候,|a|+|b|-|a+b| + 2 = 0 -
再观察计算流程,发现会逐个执行v_funcs方法。故可以是通过func0交换掉func1和func2的执行顺序,使得func1成为最后被调用的
-
while ( i <= 2 ) { v5 = i + 1; v_temp[v5] = ((int (__cdecl *)(_DWORD *, _DWORD, _DWORD))v_funcs[i])(v_temp, v_temp[4], v_temp[5]); v_temp[4] = ++i; v_temp[5] = i + 1; }
-
模拟流程
-
i=0
- v_temp[1] = v_funcs[0](v_temp,v_temp[4],v_temp[5]) = 1
- v_temp[4] = 1 , v_temp[5] = 2
-
i=1
-
v_temp[2] = v_funcs[2](v_temp,1,2) = |1| + |v_temp[2]| - |1+v_temp[2]| + 2
- 如果 v_temp[2] > 0 则 原式 = 1 + v_temp[2] - 1 - v_temp[2] + 2 = 2
- 如果 v_temp[2] > -1 && v_temp[2] < 0 , 无v_temp[2]符合,不成立
- 覆盖 v_temp[2] < -1 则 原式 = 1 - v_temp[2] + v_temp[2] - 1 + 2 = 2
- 故该式恒等于 2 ,即 v_temp[2] = 2
-
v_temp[4] = 2 , v_temp[5] = 3
-
-
i=2
-
v_temp[3] = v_funcs[1](v_temp,2,3)
= |v_temp[2] + v_temp[3]| - |v_temp[2]| - |v_temp[3]| + 2
= |2 + v_temp[3]| - 2 - |v_temp[3]| + 2
= |2 + v_temp[3]| - |v_temp[3]|
当v_temp[3]>0时,原式恒等于2
当v_temp[3]>-2 && v_temp[3]<0时,原式 = 2 + 2 * v_temp[3] ,即 v_temp[3] = -1时原式为0
当v_temp[3]<-2时,原式=-2 - v_temp[3] + v_temp[3] = -2 不满足
综上所述,使得v_temp[3]=-1即可
-
-
-
最终,即v_temp[3] = -1 ,v_temp[4] = 7 ,v_temp[5] = 8,生成data的脚本如下
-
import struct single_word_len = 4 data = b'' data += b'a'*20 # 以字符a填充满v_input # 以字符a填充 v_temp[0] v_temp[1] v_temp[2] data += b'a'*3*single_word_len data += struct.pack('<i', -1) # 覆盖v_temp[3] = -1 data += struct.pack('<i', 7) # 覆盖v_temp[4] v_func[0] 等效于 v_input[7] data += struct.pack('<i', 8) # 覆盖v_temp[5] v_func[1] 等效于 v_input[8] with open('data', 'wb') as f: f.write(data) # 保存数据到文件
-
-
运行得到flag
8cda1bdb68a72a392a3968a71bdb8cda
其他文档
-
CTF逆向-常用的逆向工具 提取码:pnbt
-
B站教程中国某省队CTF集训(逆向工程部分)
- 中国某省队CTF集训(逆向工程部分)(已授权)(一)
- 基础加密方式例如
XXTEA
、Base64
换表 - Python库
Z3
方程式、不定式等的约束求解
- 基础的假跳转花指令(脏字节)
- 非自然程序流程
- 扁平化程序控制流
- OLLVM程序流程(虚拟机壳) 很难一般不考
- ida里面按
X
键跟踪,寻找所有Ty
为w
的引用(即类型是写入的),通常就是关键位置
- 中国某省队CTF集训(逆向工程部分)(已授权)(二)
- ollydb动调去壳,upx为例子
- python的逆向和自定义虚拟指令
- 使用pycdc 提取码:dorr 解密python编译的exe或者pyc
- 逐条去解析用py字典手动实现的指令调用
- C++编译的程序的逆向
- 中国某省队CTF集训(逆向工程部分)(已授权)(三)
- 简单模运算加密
- base58 寻找一下特别大的数,这种数通常是算法的标识,或者ida7.7版本以上自带的
find crypt
插件ctrl+alt+f
- 常见的关键位置是有新的内存分配的地方通常是关键地方,或者函数中间突然return的地方也是
- 迷宫题 注意绘制出来就好
- 动调题
- 注意观察会执行的反调试分支,例如出现
int 3
,需要跳过去
- 注意观察会执行的反调试分支,例如出现
-
基本知识
更多CTF逆向题通用性做法和常用工具下载参考该博文内容:CTF逆向Reverse题的玩法