CTF逆向-[MRCTF2020]VirtualTree-恒成立的jz花指令去除及smc变换原执行流程在二叉树上的应用,通过逆向思维编写脚本以解决
CTF逆向-[MRCTF2020]VirtualTree-恒成立的jz花指令去除及smc变换原执行流程在二叉树上的应用,通过逆向思维编写脚本以解决
来源:https://buuoj.cn/
内容:
附件: 链接:https://pan.baidu.com/s/1JuQqLSLskTFxOCbXZgUR8g?pwd=da2a 提取码:da2a
答案:MRCTF{@_7r3e_f0r_fuNN!}
总体思路
发现有花指令,去除
发现有smc,动调让其执行
对处理流程中涉及到的函数编写逆向思维脚本反着算
对二叉树的结构进行复原,并写出题目要求的顺序的遍历,按遍历进行操作
得到flag
详细步骤
查看文件内容
检查发现程序中广泛存在该类花指令,jump到一个指令的中间位置。
这种花指令其实是通过先清空eax以后然后给一个恒成立的跳转,并在跳转下面加了一个
_emit 0xe8
,如下图。_asm {xor eax,eaxjz label1_emit 0xE8label1: }
对于该花指令,只需要在下面一行右键选
patching
-patch byte
,将e8
(执行)改为90
(跳过)即可,然后选中下面原来没能正常反汇编的数据,按c点force
强行转换就可以得到原文了。处理完成以后即可得到main函数,进去查看分析整理流程。
其中
f_bintree_xor
是点进去以后观察发现分别调用了a1,a1+4,a1+8,是典型的struct样式,猜测是二叉树(BinTree),右键a1点击create new struct创建一个结构。这样就会看得比较清楚了,发现它是将树的每一位异或了输入值(&g_input + g_counter++) ^= a1->data
并且该异或的位置是在左遍历和右遍历中间,是中序遍历
查看
f_bintree_xor
的引用,发现除了main函数,还有其他地方也引用了,该地方是程序csu初始化的地方。进入该函数,发现存在一些预处理,该预处理通过smc修改了f_encode_loop函数里面的内容,将每个0x401510替换成汇编list中的一个项。
查看
list
中的函数,发现也是有花指令,去除花指令以后得到函数原本的含义int __cdecl f_add_a2(int a1, char a2) {int result; // eaxresult = a1;*((_BYTE *)&g_input + a1) += a2;return result; }
int __cdecl f_xor_ginputIndexOfa2(int a1, int a2) {int result; // eaxresult = *((unsigned __int8 *)&g_input + a2);*((_BYTE *)&g_input + a1) ^= result;return result; }
int __cdecl f_abs_inputA1_minus_inputA2(int a1, int a2) {__int64 v2; // raxint result; // eaxv2 = *((unsigned __int8 *)&g_input + a1) - *((unsigned __int8 *)&g_input + a2);// input[a1] - input[a2]result = (HIDWORD(v2) ^ v2) - HIDWORD(v2); // = lowDWORD(v2) - highDWORD(v2)*((_BYTE *)&g_input + a1) = result;return result; }
注意:
f_abs_inputA1_minus_inputA2
abs(input[a1]-input[a2]) 其实就是abs绝对值函数的计算方式,可以参考计算机组成原理
运行程序,观察发现
f_loop
函数已经被修改完成int f_encode_loop() {f_add_a2(0, 10);f_xor_ginputIndexOfa2(1, 2);f_add_a2(2, 7);f_abs_inputA1_minus_inputA2(3, 7);f_xor_ginputIndexOfa2(4, 5);f_abs_inputA1_minus_inputA2(6, 1);f_add_a2(7, 3);f_xor_ginputIndexOfa2(8, 7);f_abs_inputA1_minus_inputA2(9, 8);f_abs_inputA1_minus_inputA2(10, 7);f_xor_ginputIndexOfa2(11, 12);f_abs_inputA1_minus_inputA2(12, 2);f_xor_ginputIndexOfa2(14, 15);return f_add_a2(15, 2); }
写出对应的Python逆向思维处理
def f_add_a2(a1: int, a2: int):flag[a1] -= a2def f_xor_ginputIndexOfa2(a1: int, a2: int):flag[a1] ^= flag[a2]def f_abs_inputA1_minus_inputA2(a1: int, a2: int):if flag[a1] < flag[a2]:# 如果flag[a1]<flag[a2]则原来的flag[a1]-flag[a2]是负数,所以需要取负flag[a1] = -flag[a1]flag[a1] += flag[a2]commands = ''' f_add_a2(0, 10) f_xor_ginputIndexOfa2(1, 2) f_add_a2(2, 7) f_abs_inputA1_minus_inputA2(3, 7) f_xor_ginputIndexOfa2(4, 5) f_abs_inputA1_minus_inputA2(6, 1) f_add_a2(7, 3) f_xor_ginputIndexOfa2(8, 7) f_abs_inputA1_minus_inputA2(9, 8) f_abs_inputA1_minus_inputA2(10, 7) f_xor_ginputIndexOfa2(11, 12) f_abs_inputA1_minus_inputA2(12, 2) f_xor_ginputIndexOfa2(14, 15) f_add_a2(15, 2) '''.split('\n') commands = commands[::-1] for command in commands:if len(command) < 5:continueeval(command)
同时发现在csu下面,其还将f_bintree_xor函数的地址给修改了,变成了
f_bintree_xor_same
函数的地址,这个函数的逻辑是后序遍历。查看
g_tree
的应用,发现除了main函数,还有其他地方也引用了,该地方是程序csu初始化的地方。找到二叉树初始化的位置,并用同样的方法将其转换为BinTree结构- #mermaid-svg-FQ3auSTmYiNVrpCz {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-FQ3auSTmYiNVrpCz .error-icon{fill:#552222;}#mermaid-svg-FQ3auSTmYiNVrpCz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FQ3auSTmYiNVrpCz .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-FQ3auSTmYiNVrpCz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FQ3auSTmYiNVrpCz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FQ3auSTmYiNVrpCz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FQ3auSTmYiNVrpCz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FQ3auSTmYiNVrpCz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FQ3auSTmYiNVrpCz .marker.cross{stroke:#333333;}#mermaid-svg-FQ3auSTmYiNVrpCz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FQ3auSTmYiNVrpCz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FQ3auSTmYiNVrpCz .cluster-label text{fill:#333;}#mermaid-svg-FQ3auSTmYiNVrpCz .cluster-label span{color:#333;}#mermaid-svg-FQ3auSTmYiNVrpCz .label text,#mermaid-svg-FQ3auSTmYiNVrpCz span{fill:#333;color:#333;}#mermaid-svg-FQ3auSTmYiNVrpCz .node rect,#mermaid-svg-FQ3auSTmYiNVrpCz .node circle,#mermaid-svg-FQ3auSTmYiNVrpCz .node ellipse,#mermaid-svg-FQ3auSTmYiNVrpCz .node polygon,#mermaid-svg-FQ3auSTmYiNVrpCz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FQ3auSTmYiNVrpCz .node .label{text-align:center;}#mermaid-svg-FQ3auSTmYiNVrpCz .node.clickable{cursor:pointer;}#mermaid-svg-FQ3auSTmYiNVrpCz .arrowheadPath{fill:#333333;}#mermaid-svg-FQ3auSTmYiNVrpCz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FQ3auSTmYiNVrpCz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FQ3auSTmYiNVrpCz .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-FQ3auSTmYiNVrpCz .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-FQ3auSTmYiNVrpCz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FQ3auSTmYiNVrpCz .cluster text{fill:#333;}#mermaid-svg-FQ3auSTmYiNVrpCz .cluster span{color:#333;}#mermaid-svg-FQ3auSTmYiNVrpCz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FQ3auSTmYiNVrpCz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}12345678910111213141516NULLNULLNULLNULLNULLNULLNULLNULLNULL
同时发现在初始化额
则该二叉树的后序输出为
[13, 12, 7, 16, 15, 11, 6, 3, 10, 5, 14, 9, 8, 4, 2, 1]
发现data的赋值是
(*(&v1 + j))->data = j + 65;
,即从65开始每往后增加1将g_key的值按shift+e复制出来,
17637703522E4A28521B17123A0A6C6200000000
,编写逆向脚本flag = '17637703522E4A28521B17123A0A6C6200000000' # g_key的值 flag = [x for x in bytearray.fromhex(flag)]def f_add_a2(a1: int, a2: int):flag[a1] -= a2def f_xor_ginputIndexOfa2(a1: int, a2: int):flag[a1] ^= flag[a2]def f_abs_inputA1_minus_inputA2(a1: int, a2: int):if flag[a1] < flag[a2]:# 如果flag[a1]<flag[a2]则原来的flag[a1]-flag[a2]是负数,所以需要取负flag[a1] = -flag[a1]flag[a1] += flag[a2]commands = ''' f_add_a2(0, 10) f_xor_ginputIndexOfa2(1, 2) f_add_a2(2, 7) f_abs_inputA1_minus_inputA2(3, 7) f_xor_ginputIndexOfa2(4, 5) f_abs_inputA1_minus_inputA2(6, 1) f_add_a2(7, 3) f_xor_ginputIndexOfa2(8, 7) f_abs_inputA1_minus_inputA2(9, 8) f_abs_inputA1_minus_inputA2(10, 7) f_xor_ginputIndexOfa2(11, 12) f_abs_inputA1_minus_inputA2(12, 2) f_xor_ginputIndexOfa2(14, 15) f_add_a2(15, 2) '''.split('\n') commands = commands[::-1] for command in commands:if len(command) < 5:continueeval(command)# 后序遍历v13 v12 v7 v11 v6 v3 v10 v5 v14 v9 v8 v4 v2 v1 order = [13, 12, 7, 16, 15, 11, 6, 3, 10, 5, 14, 9, 8, 4, 2, 1] order = [x-1 for x in order] # data为从65开始每往后增加1,即65+index for index, i in enumerate(order):flag[index] ^= (65+order[index])flag = [chr(x) for x in flag] flag = ''.join(flag) print(flag) # @_7r3e_f0r_fuNN!
根据题目要求
your flag: MRCTF{%s}\n
最后答案为MRCTF{@_7r3e_f0r_fuNN!}
注意在后序输出中,是先遍历左子树,即B,再遍历右子树即C,最后才是A。(这里面的B和C都是一个狗,和sin狗的那个狗是同一个狗,什么妖魔鬼怪都可放,x)
前序:ABC 中序:BAC 后序:BCA
- #mermaid-svg-OmjU5vkvgCddHzSv {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-OmjU5vkvgCddHzSv .error-icon{fill:#552222;}#mermaid-svg-OmjU5vkvgCddHzSv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OmjU5vkvgCddHzSv .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-OmjU5vkvgCddHzSv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OmjU5vkvgCddHzSv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OmjU5vkvgCddHzSv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OmjU5vkvgCddHzSv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OmjU5vkvgCddHzSv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OmjU5vkvgCddHzSv .marker.cross{stroke:#333333;}#mermaid-svg-OmjU5vkvgCddHzSv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OmjU5vkvgCddHzSv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OmjU5vkvgCddHzSv .cluster-label text{fill:#333;}#mermaid-svg-OmjU5vkvgCddHzSv .cluster-label span{color:#333;}#mermaid-svg-OmjU5vkvgCddHzSv .label text,#mermaid-svg-OmjU5vkvgCddHzSv span{fill:#333;color:#333;}#mermaid-svg-OmjU5vkvgCddHzSv .node rect,#mermaid-svg-OmjU5vkvgCddHzSv .node circle,#mermaid-svg-OmjU5vkvgCddHzSv .node ellipse,#mermaid-svg-OmjU5vkvgCddHzSv .node polygon,#mermaid-svg-OmjU5vkvgCddHzSv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OmjU5vkvgCddHzSv .node .label{text-align:center;}#mermaid-svg-OmjU5vkvgCddHzSv .node.clickable{cursor:pointer;}#mermaid-svg-OmjU5vkvgCddHzSv .arrowheadPath{fill:#333333;}#mermaid-svg-OmjU5vkvgCddHzSv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OmjU5vkvgCddHzSv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OmjU5vkvgCddHzSv .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-OmjU5vkvgCddHzSv .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-OmjU5vkvgCddHzSv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OmjU5vkvgCddHzSv .cluster text{fill:#333;}#mermaid-svg-OmjU5vkvgCddHzSv .cluster span{color:#333;}#mermaid-svg-OmjU5vkvgCddHzSv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OmjU5vkvgCddHzSv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}ABC
其他文档
- 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
- Python反汇编方法 Python的pyc字节码反编译反汇编相关知识
- [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逆向-[FlareOn1]Shellolololol-栈上执行,多层smc的动调得到最终结果
加密
- Python 基于pycryptodome,实现对AES、DES、3DES、RSA等常用加密算法的使用,文末附各种加密的源码示例
- [CTF逆向-FlareOn2]very_success-WP_rol循环位移加密
- base64换表
- [CTF逆向-CISCN2018]2ex-WP_mips-32架构以及base64换表
- [CTF逆向-De1CTF2019]Re_Sign-简单脱壳和base64换表编码的深度算法跟踪
花指令
- [CTF逆向-SCTF2019]babyre-WP_简单去花指令和流程识别
流程混淆的扁平化处理
[CTF逆向-RoarCTF2019]polyre-WP_控制流扁平化去混淆idcpy去指令
CTF逆向-[SUCTF2019]hardcpp-使用优化过的deflat.py处理混淆的控制流并将cpp的lambda解析得到实际处理逻辑
CTF逆向-[MRCTF2020]VirtualTree-恒成立的jz花指令去除及smc变换原执行流程在二叉树上的应用,通过逆向思维编写脚本以解决相关推荐
- CTF逆向-[b01lers2020]little_engine-cpp基本函数用法和byte类型要点
CTF逆向-[b01lers2020]little_engine-cpp基本函数用法和byte类型要点 来源:https://buuoj.cn/ 内容: 附件:https://pan.baidu.co ...
- 190321 逆向-花指令去除(脚本)
Pizza的脚本中是通过get_bytes和patch_bytes两个API来将所有机器码读出然后再Patch回去的 中间匹配pattern的过程是构造字符串然后find来控制 这样操作的优点是fin ...
- 花指令如何用c语言编写,逆向学习笔记之花指令
概念 花指令是企图隐藏掉不想被逆向工程的代码块(或其它功能)的一种方法,在真实代码中插入一些垃圾代码的同时还保证原有程序的正确执行,而程序无法很好地反编译, 难以理解程序内容,达到混淆视听的效果. 简 ...
- 逆向分析基础 --- 花指令实现及清除
一.基本概念 花指令:目的是干扰ida和od等软件对程序的静态分析.使这些软件无法正常反汇编出原始代码. 常用的两类反汇编算法: 1.线性扫描算法:逐行反汇编(无法将数据和内容进行区分) 2.递归行进 ...
- [XMAN2018排位赛]Dragon Quest [MRCTF2020]VirtualTree
文章目录 [XMAN2018排位赛]Dragon Quest 拖入ida v5 = start_quest((std::string *)v7); sanitize_input(v6); 核心代码1 ...
- 【2021.12.25】ctf逆向中常见加密算法和编码识别
[2021.12.25]ctf逆向中常见加密算法和编码识别(含exe及wp) 文章目录 [2021.12.25]ctf逆向中常见加密算法和编码识别(含exe及wp) 0.前言 1.基础加密手法 2.b ...
- CTF逆向-Upx脱壳攻防世界simple unpack
文章目录 前言 UPX 技术原理 应用范围 软件使用 CTF实战 程序查壳 UPX脱壳 总结 前言 加壳软件分两类: 压缩壳:压缩的目的是减少程序体积,如 ASPack.UPX.PECompact 等 ...
- [高考数学]恒成立问题
[高考数学] 恒成立问题 问题例子 问题的抽象 方法 解释 例题 问题例子 例:不等式 x2+ax+1>0x^2+ax+1>0x2+ax+1>0 在RRR 上恒成立,求 aaa 的取 ...
- 二次不等式恒成立求参数范围
前言 对二次不等式的恒成立问题的类型和对应的处理策略做以总结,总原则:利用三个二次的关系,进行相应的转化划归.比如本来是解二次不等式,结果我们却是利用其对应的二次函数的图像来思考.为了让思考的模型简单 ...
最新文章
- 【沟通交流】弱关系向强关系的转变
- dom加载完再执行 vue_vue中等页面dom加载完毕后执行某方法?
- Label Assign综述:提升目标检测上限
- 在计算机中 鼠标器属于,在计算机中,鼠标器属于()。
- android 16进制 全透明_你有几种实现方案Android 设备唯一标识?
- 美团java研发岗二面:java静态方法存储在哪个区
- 【蓝桥杯单片机】IIC通讯协议与EEPROM(AT24C02)(官方驱动源码改写)
- Redmi K50 Pro核心配置曝光:搭载天玑9000旗舰4nm芯片
- SPSS 协方差分析(图文+数据集)【SPSS 020期】
- od 调试java_OD调试初步概念——修改窗口标题
- C++第三方库管理工具vcpkg使用教程
- 【软件】3DsMax2009 下载百度云盘下载(附教程)
- PSV 3.60 固化升级到 3.68 破解完全攻略
- kdj买卖指标公式源码_长短KDJ源码与kdj顶底背离指标公式(附图)
- C/C++整数除法以及保留小数位的问题
- Vue学习之旅----vuex实现不同组件的数据共享 数据持久化
- “初中三年,死磕这一点,英语次次110分以上!”教了一辈子英语的老教师如是说...
- iphone 自定义铃声制作
- 南阳oj入门题-A+B Problem
- 解锁三星bl锁有几种方法_如何判断三星手机bootloader是否解锁_免费解锁BL的3个方法...
热门文章
- STM32学习笔记1----STM32F429系统时钟
- 用Matlab求解一维非稳态导热问题(有限差分法+显式离散)
- 使用网店复制工具的注意事项及操作流程
- 云队友 | 小米最重要的两个商业启示:做对的事与找对的人
- python程序调用写好的代码_扣丁学堂浅谈Python编程中NotImplementedError的使用方法...
- 各大厂应用实践 — 大数据
- 完全代码实现UITableView下拉更新,异步请求数据
- Windows快捷键---原创总结zyh
- carsim+simulink联合仿真实现变道 包含路径规划算法+mpc轨迹跟踪算法 可选simulink版本和c++版本算法 可以适用于弯道道路,弯道车道保持,弯道变道
- iOS14+中广告标识(idfa)获取方式