CTF逆向-[NPUCTF2020]Baby Obfuscation-逆向思维编写脚本以及函数含义的逻辑理解
来源:https://buuoj.cn/
内容:
附件: https://pan.baidu.com/s/1jjjo11izhnEUQaLx00993w?pwd=pxdj 提取码:pxdj
答案:NPUCTF{0bfu5er}
总体思路
根据执行逻辑分析每个函数的含义
逻辑思维简化题目的代码,将其理解成较为简短的句子
逆向思维去编写脚本
详细步骤
-
查看文件内容
-
打开以后发现主函数里面存在一些不知内容的变量和方法
- 由
scanf("%32s", A0X2);
推导 A0X2 为 v_input - 由
for ( i = 0; i <= 64; ++i ) A0X3[i] = i + 1;
推导 A0X3为 index+1 - 将
A0X4
和A0X5
改成arr1
和arr2
- 由
-
双击打开查看这些函数的含义并给他们命名
-
F0X1为
求最大公约数
(高中知识gcd函数) -
int __cdecl F0X1(int a, int b) { if ( b ) return F0X1(b, a % b); else return a; }
-
F0X2为
判断a和b是否都为flase
-
bool __cdecl F0X2(bool a, bool b) { return a == b && !a; }
-
F0X3也是
判断a和b是否都为flase
-
bool __cdecl F0X3(bool a, bool b) { bool v2; // bl bool v3; // al v2 = F0X2(b, b); v3 = F0X2(a, a); return F0X2(v3, v2); }
-
F0X4可以化简,
~
表示去反,~a = int.MAX - a,等于a-b
-
int __cdecl F0X4(int a, int b) { return ~(~a + b); // = int.Max - (int.Max - a + b) = int.Max - int.Max + a - b = a - b }
-
F0X5为 pow(a,b),例如b=7,则二进制为
111
,a=5,则表达 = 5 * 5^2 * 5^4 = 5^7 -
int __cdecl F0X5(int a, int b) { int ans; // [rsp+Ch] [rbp-4h] ans = 1; while ( b ) { if ( (b & 1) != 0 ) // 对b每一二进制位判断是否是1,是则乘上a ans *= a; a *= a; // 每移动一位 a自乘一遍 b >>= 1; } return ans; }
-
-
整理后得到如下,注意看注释内容,并跟着注释自行推导一遍。
-
int __cdecl main(int argc, const char **argv, const char **envp) { int v_equal_2; // eax int v4; // ebx int v5; // esi int v6; // ebx int v7; // ebx int v8; // esi int v9; // ebx int v10; // ebx int v11; // ebx int v12; // esi int v13; // eax int v14; // ebx int v15; // esi int v16; // ebx int v17; // eax bool v18; // bl int v19; // eax int v20; // esi int v21; // ebx int v22; // ebx int v23; // eax int v_equal_16; // eax int v_equal_1; // eax int v26; // ebx int v_index_add1[65]; // [rsp+20h] [rbp-60h] BYREF char v_input[1001]; // [rsp+130h] [rbp+B0h] BYREF int v_temp[1001]; // [rsp+520h] [rbp+4A0h] BYREF int v_arr2[4]; // [rsp+14D0h] [rbp+1450h] int v_arr1[4]; // [rsp+14E0h] [rbp+1460h] int v_input_len; // [rsp+14F0h] [rbp+1470h] int i_1; // [rsp+14F4h] [rbp+1474h] int v_i; // [rsp+14F8h] [rbp+1478h] int i; // [rsp+14FCh] [rbp+147Ch] _main(); memset(v_temp, 0, sizeof(v_temp)); memset(v_index_add1, 0, sizeof(v_index_add1)); for ( i = 0; i <= 64; ++i ) v_index_add1[i] = i + 1; v_arr1[0] = 2; v_arr1[1] = 3; v_arr1[2] = 4; v_arr1[3] = 5; v_arr2[0] = 2; v_arr2[1] = 3; v_arr2[2] = 4; v_arr2[3] = 5; puts("WHERE IS MY KEY!?"); scanf("%32s", v_input); v_input_len = strlen(v_input); v_equal_2 = f_gcd(v_index_add1[v_i], v_index_add1[v_i]); for ( v_i = v_equal_2 / v_index_add1[v_i]; v_i <= v_input_len; ++v_i )// for(i = 1;i <= input_len;i++) { v4 = (v_index_add1[v_i] + v_index_add1[v_i + 1]) * (v_index_add1[v_i] + v_index_add1[v_i + 1]);// (i+1 + i+2)² if ( v4 >= f_pow_ex(2, 2) * v_index_add1[v_i] * v_index_add1[v_i + 1] )// // (i+1 + i+2)² >= 4 * (i+1) * (i+2) // 4i²+12i+9 >= 4 * (i² + 3i + 2) // 1 >= 0 // 一直成立 { v5 = ~v_input[f_subtract(v_i, 1)]; // ~input[i-1] v6 = f_subtract(v_i, 1); v_temp[v_i] = ~(v5 + v_arr1[v6 % f_pow_ex(2, 2)]);// ~(~input[i-1] + arr1[0]) = input[i-1] - arr1[0] } v7 = f_gcd(v_index_add1[v_i], v_index_add1[v_i + 1]);// 1 if ( v7 > f_gcd(v_index_add1[v_i + 1], ~(~v_index_add1[v_i + 1] + v_index_add1[v_i])) )// // 1 > gcd(i+2,~(~(i+2) + i+1)) // 1 > gcd(i+2,i+2 - (i+1)) // 1 > gcd(i+2,1) // 1 > 1 永不成立 { v8 = v_temp[v_i]; v9 = f_subtract(v_i, 1); v_temp[v_i] = ~(~v8 + v_index_add1[v9 % f_pow_ex(2, 2)]) * v8; } v10 = v_index_add1[v_i + 1]; // i+2 v11 = f_pow_ex(2, 1) * v10; // 2i + 4 v12 = v_index_add1[v_i]; // i+1 v13 = f_pow_ex(2, 1); // 2 v14 = f_gcd(v12 * v13, v11); // gcd((i+1) * 2, 2i+4) v15 = f_pow_ex(2, 1); // 2 if ( v14 == v15 * f_gcd(v_index_add1[v_i], v_index_add1[v_i + 1]) )// // gcd((i+1) * 2, 2i+4) == 2 * 1 // gcd(2i+2, 2i+4) == 2 // 从4,6,8,10,12,14,16,18,20..,2n里面找公约数只有2的 // 发现所有的i都符合 // { v16 = f_subtract(v_i, 1); // i-1 v_temp[v_i] ^= v_arr1[v16 % f_pow_ex(2, 2)];// v^= arr[(i-1) % 4] } v17 = f_pow_ex(g_value_3, v_index_add1[v_i]);// // g_value_3 = 3 // v17 = pow(3,i+1) v18 = v17 < v_index_add1[v_i] + 1; // pow(3,i+1) < i+2 v19 = f_pow_ex(2, 4); // 16 if ( f_check_false_false_same(v19 >= v_i, v18) )// // 16 < i && pow(3,i+1) >= i+2 // i>1 && i>16 // i>16是永不成立的 { v20 = ~v_input[f_subtract(v_i, 1)]; // ~input[i-1] v21 = f_subtract(v_i, 1); // i-1 v_temp[v_i] ^= ~(v20 + v_arr1[v21 % f_pow_ex(2, 2)]);// ^= (input[i-1] - arr1[(i-1) %4]) } v22 = f_pow_ex(2, 3); // 8 v23 = f_gcd(v_index_add1[v_i], v_index_add1[v_i]);// 1 v_temp[v_i] *= v22 + f_pow_ex(2, v23 / v_index_add1[v_i]);// *= 8 + 2 = 10 } v_equal_16 = f_pow_ex(2, 4); // 16 if ( f_subtract(v_equal_16, 1) != v_input_len ) goto LABEL_23; v_equal_1 = f_gcd(v_index_add1[i_1], v_index_add1[i_1]); for ( i_1 = v_equal_1 / v_index_add1[i_1]; i_1 <= v_input_len; ++i_1 )// for(i=1;i<=input_len;i++) { v26 = v_temp[i_1]; if ( v26 == f_subtract(g_data[i_1], 1) / 10 )// input[i] * 10 = (g_data[i] - 1) ++::v_i; } if ( ::v_i == v_input_len ) puts("\nPASS"); else LABEL_23: puts("\nDENIED"); return 0; }
-
经过整理,得到其处理的步骤如下
-
程序对该字符串进行处理 - 变换
- input[i] = input[i-1] - arr1[(i-1)%4]
- input[i] ^= arr1[(i-1) % 4]
-
判断
- 输入的是16位
-
- input[i] *= 10
-
- 判断 input[i] * 10 是否等于 g_data[i] -1
- 即 input[i] * 100 == g_data[i] - 1
-
逆向编写上述流程
- input[i] = (g_data[i] - 1) / 100
- input[i] ^= arr1[(i-1) % 4]
- input[i-1] = input[i] + arr1[0]
-
import struct g_data = '00000000791E0000791E0000352100000D170000411F000001190000ED2C0000F91100004926000081250000B52D0000B5140000E5250000312A0000D530000000000000' g_data = bytearray.fromhex(g_data) # 转换为字节 g_data = [g_data[4*x:4*(x+1)] for x in range(int(len(g_data)/4))] # 按4个字节为一个数保存 g_data = [struct.unpack('<I',x)[0] for x in g_data] # 按小端序将原始数据转换为整数 arr1 = [2, 3, 4, 5] # 原始arr flag = [x for x in range(16)] # 初始化flag for i in range(1, 16): temp = int((g_data[i] - 1) / 100) # input[i] * 100 == g_data[i] - 1 temp ^= arr1[(i-1) % 4] # input[i] ^= arr1[(i-1) % 4] temp += arr1[(i-1) % 4] # input[i] = input[i-1] - arr1[0] 即 input[i-1] = input[i] + arr1[0] flag[i-1] = temp flag = [chr(x) for x in flag] # 转换为字符 flag = ''.join(flag) print(flag) # NPUCTF{0bfu5er}
其他文档
-
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题的玩法