来源:https://buuoj.cn/
内容:
附件:链接:https://pan.baidu.com/s/17OePBZh7Mvv9neqnh-Sv2g?pwd=d6kg 提取码:d6kg
答案:#flag{mY-CurR1ed_Fns}
总体思路详细步骤发现混淆的执行流,使用原版的处理不太成功,处理后少了很多信息
使用优化版的进行处理得到混淆少得多的代码
逐个点开lambda函数查看其功能,并逐行还原其对input的处理
发现v_input_chr就是v_input[0]并且根据提示,反算md5得到原始值
代入式子计算得到答案
-
查看文件内容
-
发现存在执行流混淆
-
使用deflat.py对其进行反混淆
py .\deflat.py -f .\hardCpp --addr 0x04007e0
-
尝试启动远程动调以查看x和y的值,但发现缺少libc库无法运行
-
静态查看,发现其实是对输入的每一个字符执行了一系列lambda函数,输入的长度为21个字符
-
cpp的lambda这种是可以直接双击查看其具体实现的,逐个进去分析每个lambda的含义:
-
main::$_0::operator()(v27, (unsigned int)v18)
: 直接返回a2-
char __fastcall main::$_0::operator()(__int64 a1, char a2) { return a2; }
-
-
main::$_1::operator()(v25, (unsigned int)*(&v_input + v21 + v_pos - 1))
:也是直接返回a2-
char __fastcall main::$_1::operator()(__int64 a1, char a2) { return a2; }
-
-
main::$_1::operator() const(char)::{lambda(int)#1}::operator()(v16, 7)
:返回a1 % a2-
__int64 __fastcall main::$_1::operator() const(char)::{lambda(int)#1}::operator()(char *a1, int a2) { return (unsigned int)(*a1 % a2); }
-
-
main::$_0::operator() const(char)::{lambda(char)#1}::operator()(v17, (unsigned int)v3)
:返回a1 + a2- 可以看出v_result = *(&v_temp-16) + * v_temp = v_a2 + v_a1
-
main::$_2::operator()(v28, (unsigned int)v18)
:返回 (a1 & 0xff00) + (a2 & 0x00ff)-
v_temp = v_a1; *((_BYTE *)&v_temp - 16) = v_a2; LOBYTE(v_temp) = *((_BYTE *)&v_temp - 16); v_result = v_temp;
-
看出v_result = 高位(v_a1) 低位(v_a2)
-
-
main::$_2::operator() const(char)::{lambda(char)#1}::operator()(v_result, 18LL)
:返回 a1 ^ a2-
__int64 __fastcall main::$_2::operator() const(char)::{lambda(char)#1}::operator()(_BYTE *a1, char a2) { return (unsigned int)(char)(a2 ^ *a1); }
-
-
main::$_3::operator()(v26, (unsigned int)v4)
:返回 (a1 & 0xff00) + (a2 & 0x00ff)-
v_temp = v_a1; *((_BYTE *)&v_temp - 16) = v_a2; LOBYTE(v_temp) = *((_BYTE *)&v_temp - 16); v_result = v_temp;
-
发现逻辑和上两个函数一致,也是 v_result = 高位(v_a1) 低位(v_a2)
-
-
main::$_3::operator() const(char)::{lambda(char)#1}::operator()(v13, 3LL)
:返回 a1 * a2-
__int64 __fastcall main::$_3::operator() const(char)::{lambda(char)#1}::operator()(char *a1, char a2) { return (unsigned int)(a2 * *a1); }
-
-
-
综上所述,程序的流程化简为,经过观察,发现v_input_chr应该是v_input的基地址,则理所当然将v_input_chr作为v_input[0]
-
for(int v_pos=1;v_pos<21;v_pos++){ v18 = v_equal_0 ^ v_input[v_pos - 1]; v17[0] = f_a2(v27, (unsigned int)v18); v16[0] = f_a2_same(v25, (unsigned int)*(&v_input_chr + v_equal_0 + v_pos - 1)); v3 = f_a1_mod_a2(v16, 7); v18 = f_a1_add_a2(v17, (unsigned int)v3); v15[0] = f_byte_merge((__int64)v28, v18); v_result[0] = f_byte_merge((__int64)v28, *(&v_input_chr + v_equal_0 + v_pos - 1)); v4 = f_a1_xor_a2(v_result, 18); v13[0] = f_byte_merge_same((__int64)v26, v4); v5 = f_a1_multi_a2(v13, 3); v12[0] = f_a2(v27, (unsigned int)v5); v6 = f_a1_add_a2(v12, 2LL); v18 = f_a1_xor_a2(v15, v6); v35 = enc[v_pos - 1] != v18; }
-
全程需要保证v35为
true
-
-
根据语义,进一步化简得到,这里是一行一行推的,要耐心
-
for(int v_pos=1;v_pos<21;v_pos++){ v_current_char = v_equal_0 ^ v_input[v_pos - 1];// v_input[i-1] v_current_char_arr[0] = f_a2(v27, (unsigned int)v_current_char);// v_input[i] v_previous_char_arr[0] = f_a2_same(v25, (unsigned int)*(&v_input_chr + v_equal_0 + v_pos - 1));// v_input[i-1] v_prev_mod_7 = f_a1_mod_a2(v_previous_char_arr, 7);// v_input[i-1] % 7 v_current_char = f_a1_add_a2(v_current_char_arr, (unsigned int)v_prev_mod_7);// v_input[i] + v_input[i-1] % 7 v_cur_add_v_prevmod7[0] = f_byte_merge((__int64)v28, v_current_char);// v_input[i] + v_input[i-1] % 7 v_result[0] = f_byte_merge((__int64)v28, *(&v_input_chr + v_equal_0 + v_pos - 1));// v_input[i-1] v_prev_xor_18 = f_a1_xor_a2(v_result, 18);// v_input[i-1] ^ 18 v_prev_xor_18_same[0] = f_byte_merge_same((__int64)v26, v_prev_xor_18);// v_prev_xor_18 v_prev_xor_18_mutil_3 = f_a1_multi_a2(v_prev_xor_18_same, 3);// v_prev_xor_18_mutil_3 v_prev_xor_18_mutil_3_same[0] = f_a2(v27, (unsigned int)v_prev_xor_18_mutil_3); v_prev_xor_18_mutil_3_add2 = f_a1_add_a2(v_prev_xor_18_mutil_3_same, 2LL);// (v_prev ^ 18) * 3 + 2 v_current_char = f_a1_xor_a2(v_cur_add_v_prevmod7, v_prev_xor_18_mutil_3_add2);// (v_input[i] + v_input[i-1] % 7) ^ ((v_prev ^ 18) * 3 + 2) v35 = enc[v_pos - 1] != v_current_char; // 判断enc[v_pos-1] == (v_input[i] + v_input[i-1] % 7) ^ ((v_prev ^ 18) * 3 + 2) }
-
-
综上,题意为迭代判断
enc[v_pos-1] == (v_input[i] + v_input[i-1] % 7) ^ ((v_prev ^ 18) * 3 + 2)
-
双击
enc
变量,shift+e复制他的值F32E1836E14C22D1F98C4076F40E0005A3900EA5
-
再次观察全部代码,发现v_input_chr=getchar()的上一句有一个提示,puts(“func(?)=“01abfc750a0c942167651c40d088531d”?”);// md5(‘#’)
- 猜测是md5,将其拖入到sojson的md5解密中,得到其值为
md5('#')
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7UtH1gs0-1651240604659)(https://raw.githubusercontent.com/serfend/res.image.reference/main/image-20220429214107980.png)]
- 猜测是md5,将其拖入到sojson的md5解密中,得到其值为
-
则v_input[0] = ‘#’
-
即可编写exp如下
-
v_enc = 'F32E1836E14C22D1F98C4076F40E0005A3900EA5' v_enc = bytearray.fromhex(v_enc) # puts("func(?)="01abfc750a0c942167651c40d088531d"?");// md5('#') v_input_chr = ord('#') v_input = [v_input_chr] # 原式: (v_input[i] + v_input[i-1] % 7) ^ ((v_input[i-1] ^ 18) * 3 + 2) == v_enc[i-1] # 两边同时异或((v_input[i-1] ^ 18) * 3 + 2), # 化简为:(v_input[i] + v_input[i-1] % 7) = v_enc[i-1] ^ ((v_input[i-1] ^ 18) * 3 + 2) # 将左边移到右边,化简为:v_input[i] = (v_enc[i-1] ^ ((v_input[i-1] ^ 18) * 3 + 2)) - v_input[i-1] % 7 for i in range(1, 21): v = ((v_input[i-1] ^ 18) * 3 + 2) v_input_i = (v_enc[i-1] ^ v) - (v_input[i-1] % 7) v_input_i &= 0xff # 可打印的字符串范围必然在 0xff 以内,此处防溢出 v_input.append(v_input_i) flag = [chr(x) for x in v_input] print(''.join(flag)) # #flag{mY-CurR1ed_Fns}
-
-
结果为
#flag{mY-CurR1ed_Fns}
-
Python反汇编方法 Python的pyc字节码反编译反汇编相关知识
-
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题的玩法
相关逆向CTF题-
Python
- [CTF逆向-羊城杯 2020]Bytecode-WP-Python字节码反编译
-
远程调试汇编
- CTF逆向-[watevrCTF 2019]Timeout-WP-远程调试和修改程序当前运行位置RIP
-
流程控制
- CTF逆向-Dig the way Interesting Pointer-通过栈溢出方式覆盖变量以达到修改执行流程的目的
-
逆向思维
- [CTF逆向-NPUCTF2020]Baby Obfuscation-逆向思维编写脚本以及函数含义的逻辑理解
- [CTF逆向-MRCTF2020]EasyCpp - C++类型的逆向通用 *** 作方法
- [CTF逆向-SUCTF2018]babyre-WP-cpp简单迭代并按表输出值的爆破
-
安卓
- [CTF逆向-网鼎杯 2020 青龙组]bang-安卓脱壳逆向:frida-dexdump导出得到源码
-
虚拟机
- [CTF逆向-GWCTF 2019]babyvm-WP-虚机模拟流程反向编码和z3约束求解器解方程工具的使用
- [CTF逆向-WMCTF2020]easy_re-WP_虚机-perl加载器截取
-
反调试和SMC
- [CTF逆向-SCTF2019]creakme-WP-基于AES加密算法下的保护:反调试及except_handler和SMC
-
加密
- [CTF逆向-FlareOn2]very_success-WP_rol循环位移加密
- [CTF逆向-CISCN2018]2ex-WP_mips-32架构以及base64换表
-
花指令
- [CTF逆向-SCTF2019]babyre-WP_简单去花指令和流程识别
-
流程混淆的扁平化处理
- [CTF逆向-RoarCTF2019]polyre-WP_控制流扁平化去混淆idcpy去指令
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)