当前位置: 首页 > news >正文

上海松江区网站建设公司盐城网站优化推广工作室

上海松江区网站建设公司,盐城网站优化推广工作室,如何做电影网站推广,教育咨询网站模板点击蓝字关注我们来源于网络#xff0c;侵删Inflated!!!C异常化处理OLLVM-控制流平坦化Two PuzzlesException一般碰到C异常逆向#xff0c;确定了异常分发、处理部分#xff0c;直接把call throw改为jmp catch块#xff0c;再F5即可。PS: 多个catch块根据rdx来当为异常处理…点击蓝字关注我们来源于网络侵删Inflated!!!C异常化处理OLLVM-控制流平坦化Two PuzzlesException一般碰到C异常逆向确定了异常分发、处理部分直接把call throw改为jmp catch块再F5即可。PS: 多个catch块根据rdx来当为异常处理数值决定哪个为对应的catch块。关于以上这篇讲的很详细https://4nsw3r.top/2022/02/03/SCTF-REVERSE-CplusExceptionEncrypt-%E8%B5%9B%E5%90%8E%E5%A4%8D%E7%8E%B0/#Clang-x64然而这题没这么简单套了个ollvm基于异常处理的ollvm无论从哪个角度都没法使用之前的老套路。耐心看完这两篇文章就会有所收获对于此题的被异常处理搞乱掉的cfg就会有所理解。https://www.cnblogs.com/catch/p/3604516.htmlhttps://www.cnblogs.com/catch/p/3619379.htmlOLLVM要是平常的ollvm都可以按照这篇来解决https://bluesadi.github.io/0x401RevTrain-Tools/angr/10_%E5%88%A9%E7%94%A8angr%E7%AC%A6%E5%8F%B7%E6%89%A7%E8%A1%8C%E5%8E%BB%E9%99%A4%E6%8E%A7%E5%88%B6%E6%B5%81%E5%B9%B3%E5%9D%A6%E5%8C%96/其他的原理讲的非常好问题是这题并不是那么简单但为了去ollvm我们的思路也是一样的所以要对ollvm的cfg熟悉并懂得我们该如何恢复一个被ollvm混淆后的代码。现在就开始写我对这题的看法参考Write up:https://github.com/Lnkvct/CTF-for-Fun/blob/main/Challenges/Inflated-ACTF2022/writeup.mdhttps://www.cnblogs.com/FW-ltlly/p/16472171.htmllchild师傅的Write uppdf所以没法给链接0x00 日常查壳感觉好久没写wp了无壳64位0x01 CFGGETC在讲这题ollvm与异常处理之前有必要先搞懂我们到底是怎么输入的。一共有三处getc处理我们第一段输入的地方。407629 40553A专门用来处理箭头 405676专门用来处理箭头程序最先开始运行的是 407629这里我们可以输入上下左右箭头与特定的数字。如果是数字程序读取加密进行存放如果是箭头会继续进行处理同时我们的输入还会决定异常类型Official Write up: The value of the first field of the thrown StdObfException object comes from the second input passed to the construct of StdObfException.那么异常处理先不深究继续回来箭头如何处理这个问题。那么箭头其实为三字节码上下左右箭头分别对应 ^[[A ^[[B ^[[C ^[[D。此时开始动调我第一次输入为上箭头同时注意RAX。那么在 407629 第一次处理箭头会读取为1B。随后到 40553A 读取为5B。最后到达 405676 可以发现我们的上箭头代码所对应的字符为A。以上就解释了第一段输入的处理等到最后解密第一段输入就会用到此。OLLVM引用这张图想要去掉ollvm最基本的是要认识这几个块。https://security.tencent.com/index.php/blog/msg/112先抛去原题来认识一下这些名词函数的开始地址为序言Prologue的地址序言的后继为主分发器Main dispatcher后继为主分发器的块为预处理器Predispatcher后继为预处理器的块为真实块Relevant blocks无后继的块为retn块剩下的为无用块与子分发器Sub dispatchers那参考文章总结来说利用angr符号执行去除控制流平坦化的步骤可以归结为三个步骤静态分析CFG得到序言/入口块Prologue、主分发器Main dis。patcher、子分发器/无用块Sub dispatchers、真实块Relevant blocks、预分发器Predispatcher和返回块Return。利用符号执行恢复真实块的前后关系重建控制流。根据第二步重建的控制流Patch程序输出恢复后的可执行文件。简单来说就是获取所有的块利用angr符号执行我们的真实块查看真实块之间的流程再抛去我们不要的块patch程序完成那么具体的实现看文章https://bluesadi.github.io/0x401RevTrain-Tools/angr/10_%E5%88%A9%E7%94%A8angr%E7%AC%A6%E5%8F%B7%E6%89%A7%E8%A1%8C%E5%8E%BB%E9%99%A4%E6%8E%A7%E5%88%B6%E6%B5%81%E5%B9%B3%E5%9D%A6%E5%8C%96/然而这题根本不像啊可以看出这题的CFG根本看不懂不像单单ollvm混淆过的cfg那么漂亮。 Exception为了搞懂CFG为什么成这样了得先了解下异常的原理参考原文https://www.cnblogs.com/catch/p/3604516.html对于最基本的thown catch不再赘述这篇讲到很清楚https://4nsw3r.top/2022/02/03/SCTF-REVERSE-CplusExceptionEncrypt-%E8%B5%9B%E5%90%8E%E5%A4%8D%E7%8E%B0/#Clang-x64异常抛出后发生了什么事情1、如果当前函数没有catch就沿着函数的调用链继续往上抛然后出现两种情况在某个函数中找到相应的catch没找到相应的catch调用 std::terminate() 这个函数是把程序abort。2、如果想找到了相应的catch执行相应的操作。程序中catch的代码块有个专有名词Landing pad3、从抛异常到开始 - 执行Landing pad代码 这整个过程叫作Stack unwind。Stack unwind从抛异常函数开始对调用链上的函数逐个往前查找Landing pad。如果没有找到Landing pad则把程序abort如果找到则记下Landing pad的位置再重新回到抛异常的函数那里开始一帧一帧地清理调用链上各个函数内部的局部变量直到 landing pad 所在的函数为止。void func1() {cs a; // stack unwind时被析构。throw 3; }void func2() {cs b;func1(); }void func3() {cs c;try{func2();}catch (int){//进入这里之前 func1, func2已经被unwind.} }stack unwind的过程可以简单看成函数调用的逆过程这个过程在实现上由一个专门的stack unwind库来实现。stack unwind库在intel平台上属于Itanium ABI 接口中的一部分与具体的语言无关由系统实现任何上层语言都可以通过这个接口的基础实现各自的异常处理GCC就是通过这个接口实现C的异常处理Itanium C ABIltanium C ABI定义了一系列函数以及数据结构来建立整个异常处理的流程及框架主要函数包括以下列_Unwind_RaiseException, _Unwind_Resume, _Unwind_DeleteException, _Unwind_GetGR, _Unwind_SetGR, _Unwind_GetIP, _Unwind_SetIP, _Unwind_GetRegionStart, _Unwind_GetLanguageSpecificData, _Unwind_ForcedUnwind其中 _Unwind_RaiseException() 函数进行stack unwind它在用户执行throw的时被调用。主要功能从当前函数开始对调用链上的每一个函数都调用一个叫做 personality routine 的函数__gxx_personality_v0。personality routine 该函数由上层的语言定义及提供实现。_Unwind_RaiseException() 会在内部把函数栈调用现场重现然后传给 personality routine该函数主要做两件事情1、检查当前函数是否有相对应的catch2、清理调用栈上的局部变量。那么稍稍总结一下就是当程序抛出异常就要进行 stack unwind 操作。而这个操作具体是 _Unwind_RaiseException() 中的 personality routine() 实现了检查catch和清理栈上的局部变量。C ABI基于前面介绍的 ltanium ABI编译器层面也定义了一系列 ABI 与之交互。当我们在代码中写下 throw xxx编译器会分配一个数据结构 __cxa_exception 来表示该异常该异常也有一个头部定义如下struct __cxa_exception {std::type_info * exceptionType;void (*exceptionDestructor) (void *);unexpected_handler unexpectedHandler;terminate_handler terminateHandler;__cxa_exception * nextException;int handlerCount;int handlerSwitchValue;const char * actionRecord;const char * languageSpecificData;void * catchTemp;void * adjustedPtr;_Unwind_Exception unwindHeader; };当用户 throw 一个异常时编译器会帮我们调用相应的函数分配出如下的结构 其中 __cxa_exception 就是头部exception_obj 则是 throw xxx 中的 xxx这两部分在内存中是连续的。异常对象由函数 __cxa_allocate_exception() 进行创建最后由 __cxa_free_exception() 进行销毁当我们在程序里执行了抛出异常的操作编译器为我们做了如下的事情1、调用 cxa_allocate_exception 函数分配一个异常对象cxa_exception数据结构如上。2、调用 __cxa_throw 函数这个函数会将异常对象做一些初始化。3、__cxa_throw() 调用 Itanium ABI 里的 _Unwind_RaiseException() 从而开始 unwind。4、_Unwind_RaiseException() 对调用链上的函数进行 unwind 时调用 personality routine()。5、该异常如能被处理(有相应的 catch)则 personality routine 会依次对调用链上的函数进行清理。6、_Unwind_RaiseException() 将控制权转到相应的catch代码。7、unwind 完成用户代码继续执行。总结太Bravo了再看异常处理有了这些前置知识再看题目中的异常由前面描述可知实现 unwind stack 的具体过程是通过 __gxx_personality_v0即personality routine实现。这时候我们再去IDA里调整此函数。_Unwind_Reason_Code __fastcall _gxx_personality_v0(int Version,_Unwind_Action actions,__int64 exceptionClass,_Unwind_Exception *exceptionObject,_Unwind_Context *context)光标在函数按Y修改类型。scan_eh_tab回忆__gxx_personality_v0函数功能检查当前函数是否有相应的 catch 语句。清理当前函数中的局部变量。在personality routine()下的 scan_eh_tab() 该函数有我们最关心的两个值同时也是魔改处。与源码对比https://code.woboq.org/llvm/libcxxabi/src/cxa_personality.cpp.html#__cxxabiv1::scan_eh_tabShfit F1 - INS 导入结构体。struct scan_results { int64_t ttypeIndex; const uint8_t* actionRecord; const uint8_t* languageSpecificData; uintptr_t landingPad; void* adjustedPtr; _Unwind_Reason_Code reason; };光标在scan_eh_tab函数上按Y修改。void scan_eh_tab(scan_results *results, _Unwind_Action actions, bool native_exception, _Unwind_Exception *unwind_exception, _Unwind_Context *context)Landing padLanding pad指向catch块的分发处只单单拿到landing pad还不够这时候还缺少一个对应异常类型ttypeIndex。ttypeIndex首先要求父类为StdObfException的异常。最后的ttypeIndex由 thrown_object_ptr由我们的第一段输入所决定的thrown_object_ptr 和 原始固定固定typeIndex 决定。Official Write up: And we have figured out that the ttypeIndex is determined by the first field of the thrown StdObfException object and the lptinfo passed to __cxa_throw. The value of the first field of the thrown StdObfException object comes from the second input passed to the construct of StdObfException.那么这两个值到底具体指的是什么其实上面已经给出了答案反复调试可知可以发现我们的第一段输入设置了父类StdObfException。the first field of the thrown StdObfException object 指的就是我们的输入。the lptinfo passed to __cxa_throw 指的就是当 ___cxa_allocate_exception 创建的异常也就是固定的。现在知道了魔改后的流程是从哪里来到哪里去人工方式就是跳到landing pad再设置rdx为ttypeIndex就可以到达我们所对应的catch块。什么叫CFG那么现在知道了routine personality 中的 scan_eh_tab被修改了而IDA平常能识别throw catch这些块的原因就是这些正常的源码。然而landingpad与ttypeIndex都被修改了所以导致了IDA识别的CFG成了这个样子。我们根本没法用肉眼知道throw的块在哪只有通过动调才能确定然而这就导致了原先的deflat脚本都不不行了。原因主要为两点1、无法确定throw后的块2、throw可能对着多个catch块这时候就通过rdittypeIndex进行catch块分发landingPad。原因还有种种就不一一举例就无法正常原先deflat所需要的CFG块。以下开始就是跟着官方脚本复现。我们再回忆一下正常的ollvm的执行流程Prologue入口块- Main dispatcher主分发器- Sub dispathers子分发器- Relevant blocks真实块- Predispather预分发器- Main dispatcher主分发器...总结一下这道题的CFG。我们的下一个真实块取决于系统生产的lptinfo和我们的第一段输入所导致的StdObfException在每个真实块的结束我们不只是跳往与预分发器而是调用 __cxa_throw 进行第二次调度我们称二次调用为 second dispatch。所以我们的执行流就是... - main dispatcher - sub dispatchers - relevant block - throw StdObfException exception - Secondary dispatchers - pre-dispatcher - main dispatcher - ...除此之外程序还抛出了一些真正的异常对于这些异常第二次调用发生于Landing pad末尾。... - main dispatcher - sub dispatchers - relevant block that throws real exceptions - the according real LandingPad block - throw StdObfException exception - Secondary dispatchers - pre-dispatcher - main dispatcher - ...0x02 Deflat Solution去该平坦化控制流有两个步骤找到所有的真实块找到真实块之间的关系Find all relevant blocks我们可以从主分发器开始寻找找到所有子分发器的后继者这些后继者本身不是子分发器。官方WP中一眼丁真发现子分发器由该指令格式组成。sub dispathers such as: cmp jx于是由此区别出来isCmpRI lambda instr: instr.mnemonic cmp and\hasattr(instr.operands[0], _X86RegisterOperand__key) and\hasattr(instr.operands[1], _X86ImmediateOperand__key) isCJmp lambda instr: instr.mnemonic.startswith(j) and \instr.mnemonic ! jmp isSubDispatcher lambda bb: (len(bb.instrs) 2) and\isCmpRI(bb.instrs[0]) and isCJmp(bb.instrs[1])首先判断是否为子分发器然后排除法找到所有真实块。class PatchHelper:## ......# To get all cfgsdef block(self, addr):bb self.cfg.find_basic_block(addr)if bb is None:bb barf.bb_builder.strategy._disassemble_bb(addr, barf.binary.ea_end, {})return bbdef get_relevant_blocks(cfg, patch_helper, main_dispatcher):isCmpRI lambda instr: instr.mnemonic cmp and\hasattr(instr.operands[0], _X86RegisterOperand__key) and\hasattr(instr.operands[1], _X86ImmediateOperand__key)isCJmp lambda instr: instr.mnemonic.startswith(j) and \instr.mnemonic ! jmpisSubDispatcher lambda bb: (len(bb.instrs) 2) and\isCmpRI(bb.instrs[0]) and isCJmp(bb.instrs[1])relevant_blocks []visited set()q SimpleQueue()q.put(patch_helper.block(main_dispatcher))while not q.empty():bb q.get()# Either Sub Patchers or Relevant blocks?if isSubDispatcher(bb):for succ, cond in bb.branches:if succ in visited:continueq.put(patch_helper.block(succ))visited.add(succ)else:relevant_blocks.append(bb)return relevant_blocksRelevant blocks:*******************relevant blocks************************ main_dispatcher:0x404a80 relevant_blocks: [0x409437, 0x406443, 0x404ab8, 0x408031, 0x407842, 0x407d31, 0x407437, 0x407f4f, 0x4076bd, 0x407a6b, 0x40723e, 0x407fc4, 0x409458, 0x407bc7, 0x40732f, 0x407ebc, 0x407566, 0x407960, 0x4070fa, 0x405e7a, 0x4078e3, 0x407e5a, 0x4074ca, 0x405c87, 0x407741, 0x407af5, 0x4072b4, 0x405ded, 0x4077b6, 0x407c6b, 0x4073a4, 0x405b29, 0x4075f9, 0x407a06, 0x4071aa, 0x406cfe, 0x406c94, 0x406ef0, 0x406859, 0x40707d, 0x406b62, 0x406f5f, 0x4065c9, 0x406e5d, 0x406a72, 0x406d7b, 0x406704, 0x406def, 0x406964, 0x40944b, 0x4064a5, 0x405469, 0x405a5f, 0x404fae, 0x40532c, 0x40589c, 0x404d58, 0x4053d3, 0x405923, 0x404ec5, 0x40529a, 0x4057b8, 0x404bc4, 0x405f2a, 0x4056f0, 0x406299, 0x4068f0, 0x4063b0, 0x406bf9, 0x406323, 0x406646, 0x40620f, 0x406b00, 0x4060e7, 0x4067bb, 0x40617c, 0x4069e3, 0x40606d, 0x406521, 0x4051fe, 0x405647, 0x404e14, 0x4055b5, 0x4050cc, 0x40550b, 0x404ca4]Find the flow官网WP指出抽象出来留个坑以后熟了试试。Official Write up: A good idea is to abstract the throw StdObfException - catch process and do the one basic block symbolic execution (You can refer to Deobfuscation: recovering an OLLVM-protected programhttps://blog.quarkslab.com/deobfuscation-recovering-an-ollvm-protected-program.html or 利用符号执行去除控制流平坦化https://security.tencent.com/index.php/blog/msg/112 for more information).于是官网WP又给了个更有趣的方法GDB脚本为了找到真实块之间的流程通过普通的执行然后打印真实块需要的信息但是我们不一样能得到所有的流程因为部分可能没执行到但是我们依然可以利用提取出来的信息去恢复部分控制流并弄清楚如何输入可以恢复更多流程。怎么好像梦到过我在这写wp...生成GDB的脚本如下40A3D4为我们catch块地址_ZN18StdSubObfExceptionC2Ec为了打印异常类型cmds \ set pagination offb *0x40A3D4 commandssilentprintf landingPad: %x\\n, $rdxcontinue endb _ZN18StdSubObfExceptionC2Ec commandssilentprintf selector: %x\\n, $rsicontinue enddefine mytracebreak $arg0commandssilentprintf %x\\n, $pcpython gdb.execute(continue)end endfor bb in relevant_blocks:cmds (fmytrace *{hex(bb.address)} \n) cmds run\n with open(test.gdb, w) as f:f.write(cmds)cat teatin 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefgdb inflated -x test.gdb --batch testin testout于是可以获取真实块接下来的landing pad与异常类型。Breakpoint 1 at 0x40a3d4 ...... Breakpoint 88 at 0x404ca4 4075f9 selector: 0 landingPad: 4089bf 4072b4 selector: 0 landingPad: 408503 4075f9 selector: 2 landingPad: 4089bf 4060e7 selector: 0 ...... 40617c selector: 0 landingPad: 409100 409437 [Inferior 1 (process 13732) exited normally]然后就写个PARSER分析。def parse_logs(logfn, prologue, patch_helper):with open(logfn, r) as f:t f.readlines()i 0selector_s selector: landingpad_s landingPad: relations set()laddr prologuelselector 0landingpad 0while i len(t):try:addr int(t[i], 16)except:i 1continueif not laddr is None:relations.add((laddr, lselector, addr))if t[i1].startswith(selector_s):selector int(t[i1][len(selector_s):], 16)i 2elif t[i1].startswith(landingpad_s):landingpad int(t[i1][len(landingpad_s):], 16)relations.add((addr, -1, landingpad))addr landingpadwhile not patch_helper.is_unreachable(patch_helper.block(addr).direct_branch):addr patch_helper.block(addr).direct_branchif t[i2].startswith(selector_s):selector int(t[i2][len(selector_s):], 16)i 3elif t[i1].startswith([Inferior ):i 1else:print(Warning: %x doesnt have selector. %addr)exit(0)laddr addrlselector selectorreturn list(relations)print(************************flow******************************) relations parse_logs(sys.argv[3], prologue, patch_helper) relations.sort(key lambda x:x) flow {} for bb, selector, child in relations:if bb in flow:while len(flow[bb]) selector:flow[bb].append(-1)flow[bb].append(child)assert(len(flow[bb]) selector1)else:flow[bb] [child] for (k, v) in list(flow.items()):print(%#x: % k, [hex(child) for child in v])Flows:************************flow****************************** 0x404820: [0x4075f9] 0x404ab8: [0x404ab8, 0x406c94] 0x404bc4: [0x407bc7] 0x404ca4: [0x406bf9] 0x404ec5: [0x4053d3] 0x404fae: [0x406b00] 0x4051fe: [0x40707d] 0x4053d3: [0x406521] 0x405469: [0x407d31] 0x4056f0: [0x405a5f, 0x4056f0] 0x4057b8: [0x404ab8] 0x405923: [0x405923, 0x406e5d] 0x405a5f: [0x4067bb] 0x405b29: [0x406964, 0x406646] 0x405c87: [0x405c87, 0x407437] 0x405f2a: [0x405f2a, 0x4063b0] 0x4060e7: [0x40723e] 0x40617c: [0x409437] 0x40620f: [0x405f2a] 0x406299: [0x404bc4, 0x4057b8] 0x4063b0: [0x4063b0, 0x405469] 0x4064a5: [0x406704, 0x40620f] 0x406521: [0x4074ca, 0x404bc4] 0x4065c9: [0x40723e] 0x406646: [0x406964] 0x406704: [0x405c87] 0x4067bb: [0x4082b6] 0x406964: [0x405b29, 0x404ca4] 0x4069e3: [0x408281] 0x406a72: [0x404fae] 0x406b00: [0x406299] 0x406bf9: [0x405923] 0x406c94: [0x4074ca] 0x406cfe: [0x40723e] 0x406e5d: [0x406e5d, 0x4077b6] 0x406f5f: [0x406f5f, 0x407566] 0x40707d: [0x40707d, 0x407960] 0x4070fa: [0x406f5f] 0x4071aa: [0x4056f0] 0x40723e: [0x4072b4] 0x4072b4: [0x4075f9, 0x4071aa] 0x407437: [0x407437, 0x4064a5] 0x4074ca: [0x404ec5, 0x407c6b] 0x407566: [0x407566, 0x407a6b] 0x4075f9: [0x4072b4, -0x1, 0x4060e7, 0x406cfe, 0x4078e3, 0x4065c9] 0x4076bd: [0x404ec5] 0x4077b6: [0x406bf9, 0x4070fa] 0x4078e3: [0x40723e] 0x407960: [0x4081f5] 0x407a6b: [0x4070fa, 0x406704] 0x407bc7: [0x406a72, 0x407bc7] 0x407c6b: [0x4069e3] 0x407d31: [0x407d31, 0x407ebc] 0x407ebc: [0x407ebc, 0x40617c] 0x4081f5: [0x405b29] 0x408281: [0x4051fe] 0x4082b6: [0x4076bd]Patch修复程序环节当我们已经确定了执行流程像抛异常 子分发器什么都是多余的了统统patch掉。对于后继块只有一个的真实块只需要jmp过去。对于有多个后继块的需要通过esi也就是异常类型来改成cmp esi, ... jz即可。def patch_branches(self, bb, va_targets):va_start, size self.get_patchable_from_relblk(bb)if size PatchHelper.JMP_SIZE:print([Warning] patch_jmp at block %x may fail. size: %d.%(bb.address, size))org_start va_startprint(fva_start: {hex(va_start)}, bb addr: {hex(bb.address)}, size: {size})## cmp esi, v instr takes 3 bytes while je xxx takes 6 bytes## And the last jmp instr takes 5 bytes.total_size 9 * len(va_targets) - 4if size total_size:## If the nop block at the end of current block is not large enough,## try to find another nop block and then jump to it.nx_va_start, nx_size self.get_nop_by_size(total_size)if nx_size 0:print([Error] patch_branches needs a nop block with size larger than %d.%(total_size))self.patch_jmp(va_start, nx_va_start)va_start, size nx_va_start, nx_sizefor i, t in enumerate(va_targets[:-1]):cmp_instr bytes([0x83,0xfe,i])self.do_patch(va_start, cmp_instr)va_start len(cmp_instr)cj_instr bytes([PatchHelper.opcode[j],PatchHelper.opcode[e]])if t -1:## -1 represent that we do not know the flow for this selector value for now.cj_instr struct.pack(i, self.func_terminate-va_start-6)# cj_instr asm(fje {hex(self.func_terminate)}, vmava_start)else:cj_instr struct.pack(i, t-va_start-6)# cj_instr asm(fje {hex(t)}, vmava_start)self.do_patch(va_start, cj_instr)va_start len(cj_instr)va_start self.patch_jmp(va_start, va_targets[-1])if va_start org_startsize:print([Warning] patches at (%x, %x) overlaps next blk. %(org_start, va_start))官方完整脚本## filename: deflat.py from ast import Tuple from xmlrpc.client import Boolean from barf.barf import BARF import angr import struct import sys from pwnlib import elf from queue import SimpleQueue # from pwn import *class PatchHelper:opcode {a :0x87, ae:0x83, b :0x82, be:0x86, c :0x82, e :0x84, z :0x84, g :0x8F,ge:0x8D, l :0x8C, le:0x8E, na:0x86, nae:0x82,nb:0x83, nbe:0x87,nc:0x83,ne:0x85, ng:0x8E, nge:0x8C,nl:0x8D, nle:0x8F,no:0x81, np:0x8B, ns:0x89,nz:0x85, o :0x80, p :0x8A, pe:0x8A, po:0x8B, s :0x88, nop:0x90,jmp:0xE9, j:0x0F}JMP_SIZE 5def is_unreachable(self, bb):if isinstance(bb, int):bb self.block(bb)for i in range(len(bb.instrs)):if bb.instrs[i].mnemonic ! call:continuetarget bb.instrs[i].operands[0].immediateif target self.func_terminate:return Truedef block(self, addr):bb self.cfg.find_basic_block(addr)if bb is None:bb barf.bb_builder.strategy._disassemble_bb(addr, barf.binary.ea_end, {})return bbstaticmethoddef is_imm(operand):return (hasattr(operand, _X86ImmediateOperand__key))staticmethoddef is_reg(operand):return (hasattr(operand, _X86RegisterOperand__key))def is_call_throw(self, instr):return instr.mnemonic call and \self.is_imm(instr.operands[0]) and\instr.operands[0].immediate self.func_throwdef is_call_allocate_exception(self, instr):return instr.mnemonic call and \self.is_imm(instr.operands[0]) and\instr.operands[0].immediate self.func_allocate_exceptiondef is_call_obf_exception(self, instr):return instr.mnemonic call and \self.is_imm(instr.operands[0]) and\instr.operands[0].immediate self.func_obf_exceptiondef skip_call_args(self, bb, i):while ((bb.instrs[i].mnemonic in [xor,mov,lea]) and\(len(bb.instrs[i].operands) 0) and (self.is_reg(bb.instrs[i].operands[0])) and\(bb.instrs[i].operands[0].name in [edx, rdx, esi, rsi, edi, rdi])) or \bb.instrs[i].mnemonic nop:i - 1return idef get_patchable_from_relblk(self, bb):i 0end bb.start_address bb.sizewhile i len(bb.instrs) and not self.is_call_throw(bb.instrs[i]):i 1i self.skip_call_args(bb, i-1)if i len(bb.instrs) - 1:start endelse:start bb.instrs[i1].addressself.fill_nops(start, end)return (start, end-start)def __init__(self, proj, elf, barf, cfg) - None:self.p projobj proj.loader.main_objectself.func_terminate obj.symbols_by_name[__clang_call_terminate].rebased_addrself.func_throw obj.plt[__cxa_throw]self.func_allocate_exception obj.plt[__cxa_allocate_exception]self.func_obf_exception obj.symbols_by_name[_ZN18StdSubObfExceptionC2Ec].rebased_addrself.elf elfself.elfData bytearray(self.elf.data)self.barf barfself.cfg cfgself.nops []def append_nop(self, nopblk):if nopblk[1] 0:self.nops.append(nopblk)def finalize(self):self.nops.sort()idx 0while idx len(self.nops) - 1:if self.nops[idx][0] self.nops[idx][1] ! self.nops[idx1][0]:idx 1continueself.nops[idx](self.nops[idx][0], self.nops[idx][1]self.nops[idx1][1])del self.nops[idx1]def fill_nops(self, va_start, va_end):assert not self.elf is Nonestart self.elf.vaddr_to_offset(va_start)end self.elf.vaddr_to_offset(va_end)for i in range(start, end):self.elfData[i] PatchHelper.opcode[nop]def get_nop_by_size(self, min_size):for idx, nop in enumerate(self.nops):if nop[1] min_size:del self.nops[idx]return nopreturn (-1, 0)def do_patch(self, va_start, codes):start self.elf.vaddr_to_offset(va_start)for i in range(len(codes)):self.elfData[starti] codes[i]def patch_jmp(self, va_start, va_target):offset va_target - va_start - PatchHelper.JMP_SIZEjmp bytes([PatchHelper.opcode[jmp]])struct.pack(i, offset)self.do_patch(va_start, jmp)return PatchHelper.JMP_SIZEdef patch_branches(self, bb, va_targets):va_start, size self.get_patchable_from_relblk(bb)if size PatchHelper.JMP_SIZE:print([Warning] patch_jmp at block %x may fail. size: %d.%(bb.address, size))org_start va_startprint(fva_start: {hex(va_start)}, bb addr: {hex(bb.address)}, size: {size})## cmp esi, v instr takes 3 bytes while je xxx takes 6 bytes## And the last jmp instr takes 5 bytes.total_size (36) * len(va_targets) - 4if size total_size:## If the nop block at the end of current block is not large enough,## try to find another nop block and then jump to it.nx_va_start, nx_size self.get_nop_by_size(total_size)if nx_size 0:print(\033[31m[Error]\033[0m patch_branches needs a nop block with size larger than %d.%(total_size))self.patch_jmp(va_start, nx_va_start)va_start, size nx_va_start, nx_sizefor i, t in enumerate(va_targets[:-1]):cmp_instr bytes([0x83,0xfe,i])self.do_patch(va_start, cmp_instr)va_start len(cmp_instr)cj_instr bytes([PatchHelper.opcode[j],PatchHelper.opcode[e]])if t -1:## -1 represent that we do not know the flow for this selector value for now.cj_instr struct.pack(i, self.func_terminate-va_start-6)# cj_instr asm(fje {hex(self.func_terminate)}, vmava_start)else:cj_instr struct.pack(i, t-va_start-6)# cj_instr asm(fje {hex(t)}, vmava_start)self.do_patch(va_start, cj_instr)va_start len(cj_instr)va_start self.patch_jmp(va_start, va_targets[-1])if va_start org_startsize:print([Warning] patches at (%x, %x) overlaps next blk. %(org_start, va_start))def get_relevant_blocks(cfg, patch_helper, main_dispatcher):isCmpRI lambda instr: instr.mnemonic cmp and\hasattr(instr.operands[0], _X86RegisterOperand__key) and\hasattr(instr.operands[1], _X86ImmediateOperand__key)isCJmp lambda instr: instr.mnemonic.startswith(j) and \instr.mnemonic ! jmpisSubDispatcher lambda bb: (len(bb.instrs) 2) and\isCmpRI(bb.instrs[0]) and isCJmp(bb.instrs[1])relevant_blocks []visited set()q SimpleQueue()q.put(patch_helper.block(main_dispatcher))while not q.empty():bb q.get()if isSubDispatcher(bb):patch_helper.append_nop((bb.start_address, bb.size))for succ, cond in bb.branches:if succ in visited:continueq.put(patch_helper.block(succ))visited.add(succ)else:relevant_blocks.append(bb)return relevant_blocksdef parse_logs(logfn, prologue, patch_helper):with open(logfn, r) as f:t f.readlines()i 0selector_s selector: landingpad_s landingPad: relations set()laddr prologuelselector 0landingpad 0while i len(t):try:addr int(t[i], 16)except:i 1continueif not laddr is None:relations.add((laddr, lselector, addr))if t[i1].startswith(selector_s):selector int(t[i1][len(selector_s):], 16)i 2elif t[i1].startswith(landingpad_s):landingpad int(t[i1][len(landingpad_s):], 16)relations.add((addr, -1, landingpad))addr landingpadwhile not patch_helper.is_unreachable(patch_helper.block(addr).direct_branch):addr patch_helper.block(addr).direct_branchif t[i2].startswith(selector_s):selector int(t[i2][len(selector_s):], 16)i 3elif t[i1].startswith([Inferior ):i 1else:print(Warning: %x doesnt have selector. %addr)exit(0)laddr addrlselector selectorreturn list(relations)def generate_gdb_script(relevant_blocks):cmds \ set pagination offb *0x40A3D4 commandssilentprintf landingPad: %x\n, $rdxcontinue endb _ZN18StdSubObfExceptionC2Ec commandssilentprintf selector: %x\n, $rsicontinue enddefine mytracebreak $arg0commandssilentprintf %x\\n, $pcpython gdb.execute(continue)end end for bb in relevant_blocks:cmds (fmytrace *{hex(bb.address)} \n)cmds run\nwith open(test.gdb, w) as f:f.write(cmds)if __name__ __main__:if len(sys.argv) 3:print(Usage: python deflat.py filename function_address(hex) [logfile])exit(0)# context.arch amd64# context.os linux# context.endian littlefilename sys.argv[1]start int(sys.argv[2], 16)origin elf.ELF(filename)b angr.Project(filename, load_options{auto_load_libs: False, main_opts:{custom_base_addr: 0}})barf BARF(filename)cfg barf.recover_cfg(startstart)patch_helper PatchHelper(b, origin, barf, cfg)blocks cfg.basic_blocksprologue startmain_dispatcher patch_helper.block(prologue).direct_branchrelevant_blocks get_relevant_blocks(cfg, patch_helper, main_dispatcher)nop patch_helper.get_patchable_from_relblk(patch_helper.block(prologue))patch_helper.append_nop(nop)print(*******************relevant blocks************************)print(main_dispatcher:%#x % main_dispatcher)print(relevant_blocks:, [hex(bb.address) for bb in relevant_blocks])if len(sys.argv) 4:generate_gdb_script(relevant_blocks)exit(0)print(************************flow******************************)relations parse_logs(sys.argv[3], prologue, patch_helper)relations.sort(key lambda x:x)flow {}for bb, selector, child in relations:if bb in flow:while len(flow[bb]) selector:flow[bb].append(-1)flow[bb].append(child)assert(len(flow[bb]) selector1)else:flow[bb] [child]for (k, v) in list(flow.items()):print(%#x: % k, [hex(child) for child in v])print(************************patch*****************************)patch_helper.finalize()for (parent, childs) in list(flow.items()):## Patch jmpsblk patch_helper.block(parent)patch_helper.patch_branches(blk, childs)## Nop call allocate_exception and call obf_exceptionfor idx, instr in enumerate(blk.instrs):if patch_helper.is_call_allocate_exception(instr) or\patch_helper.is_call_obf_exception(instr):# si patch_helper.skip_call_args(blk, idx-1)1# start blk.instrs[si].addressstart instr.addressend instr.address instr.sizepatch_helper.fill_nops(start, end)with open(filename .recovered, wb) as f:f.write(bytes(patch_helper.elfData))print(Successful! The recovered file: %s % (filename .recovered))Work flow:$ python deflat.py inflated 0x404820 $ gdb inflated -x test.gdb --batch testin testout $ python deflat.py inflated 0x404820 testout按照以上流程test.gdb可能会报个错程序把本身有个\n是脚本中需要打印的但直接转义成真换行了需要手动恢复。观看修复后的流程int __cdecl main(int argc, const char **argv, const char **envp) {......v3 fileno(stdin);tcgetattr(v3, intermiosBufBackup);cfmakeraw(intermiosBuf);tcsetattr(v3, 0, intermiosBuf);*(_OWORD *)v196 0LL;v195 0LL;*(_OWORD *)s 0LL;*(_QWORD *)v196[13] 0LL;v124 v168;v123 v167;v164 v199;v187 v198;v186 v96;v185 v97;v184 v100;v122 s[12];v108 v103;v163 v197;v183 v99;v162 v166;......v5 0LL;do{v72 v4;v98 getc(stdin);v73 v98 24;v74 v98 24 0x1B000000;if ( v98 24 0x31000000 )v74 2;if ( v73 0x37000000 )v74 3;if ( v73 0x33000000 )v74 4;if ( v73 0x34000000 )v74 5;v101 v5;v102 v72;v119 v72;if ( v74 ){if ( v74 1 )_clang_call_terminate(5LL);if ( v74 2 ){v107 v102 (4LL (3 * (unsigned __int8)v101));v85 v98;}else if ( v74 3 ){v107 v102 (5LL (3 * (unsigned __int8)v101));v85 v98;}else{if ( v74 4 )v107 v102 (6LL (3 * (unsigned __int8)v101));elsev107 v102 (7LL (3 * (unsigned __int8)v101));v85 v98;}s[v101] v85;v119 v107;}v5 v101 1;v174 v119;}while ( v101 ! 11 );s[12] 0;v69 fileno(stdin);tcsetattr(v69, 0, intermiosBufBackup);for ( i 0LL; i 5; i )*((_BYTE *)v136 i) byte_40E0F3[i] - byte_40E0F8[i];v188 v190;v190 v136[0];v189 4LL;v191 0;__isoc99_scanf(v190, v122);v26 v188;v175 v188;*(_OWORD *)v188 xmmword_40E040;v26[4] 639210836;*((_BYTE *)v26 20) 16;*(_QWORD *)((char *)v26 34) 0x1005E763241AA6B1LL;*(_OWORD *)((char *)v26 21) xmmword_40E148;__cxa_begin_catch(v26);v155 strlen(v122);v128 0LL;v113 0;v125 v155;v147 0LL;do{v133 v125 - 1;v86 v122[v147];v160 v128;v110 v113;v176 v147;isalnum(v86);v50 (unsigned int)(v160 1);*(v95 (int)v160) v86;v181 v176 1;v130 v50;v112 v110;v146 0LL;if ( (_DWORD)v50 4 ){do{v106 0LL;v149 v146;do{v199[v106 16] byte_40E071[v106] - byte_40E0B2[v106];v106;}while ( v106 0x41 );v56 v163;*(_QWORD *)v163 v164;v165 64LL;v169 (_OWORD *)std::__cxx11::basic_stringchar,std::char_traitschar,std::allocatorchar::_M_create(v56,v165,0LL);v9 (void **)v163;v10 v169;*(_QWORD *)v163 v169;v11 v165;*(_QWORD *)v164 v165;v12 MEMORY[5];v13 MEMORY[0x15];v14 MEMORY[0x25];v10[3] MEMORY[0x35];v10[2] v14;v10[1] v13;*v10 v12;*(_QWORD *)v187 v11;*((_BYTE *)v10 v11) 0;v15 v149;*(v95 v15) std::__cxx11::basic_stringchar,std::char_traitschar,std::allocatorchar::find(v9,(unsigned int)*(v95 v149),0LL);v177 *v9;operator delete(v177);v146 v149 1;}while ( v149 ! 3 );v17 *v186;*v183 (4 * *v57) | ((unsigned __int8)*v186 4) 3;v18 *v185;*v59 (16 * v17) | ((unsigned __int8)*v185 2) 0xF;*v184 *v58 (v18 6);v152 v110;v151 0LL;do{v6 v151;v7 (unsigned __int8)*(v99 v151) / 0xAu;v8 v152;v199[v152 96] (unsigned __int8)*(v99 v151) % 0xAu;v199[v8 97] v7;v151 v6 1;v152 v8 2;v182 v8 2;}while ( v6 ! 2 );v130 0LL;v112 v182;}v128 v130;v113 v112;v125 v133;v147 v181;}while ( v133 );__cxa_end_catch();v193 152788034LL;v192[3] xmmword_40E130;v192[2] xmmword_40E120;v192[1] xmmword_40E110;v192[0] xmmword_40E100;v138 152788034LL;cipher_helper12037464u,StList0ul,1ul,2ul,3ul,4ul,5ul,6ul,7ul,8ul,9ul,10ul,11ul,12ul,13ul,14ul,15ul,16ul,17ul,18ul,19ul,20ul,21ul,22ul,23ul,24ul,25ul,26ul,27ul,28ul,29ul,30ul,31ul,32ul,33ul,34ul,35ul,36ul,37ul,38ul,39ul::get_array(152788034LL,Knows the futility yet does it anyway. );v55 v138;*(_OWORD *)(v138 56) xmmword_40E16D;*(_OWORD *)(v55 40) xmmword_40E15D;*(_QWORD *)(v55 72) 0x6FF0E70B5B3F60A4LL;v137 (void *)0x6FF0E70B5B3F60A4LL;__cxa_begin_catch((void *)0x6FF0E70B5B3F60A4LL);v145 0LL;do{v67 v145;*((_DWORD *)v192 2 * v145) ^ 0x9005408u;v145 v67 1;}while ( v67 ! 8 );__cxa_end_catch();*(_OWORD *)v75 xmmword_40E030;*((_QWORD *)v75 2) 0x48D1556A814FF991LL;*((_QWORD *)v75 5) 0x48B0E10161EA8322LL;v25 -2.526699287193993e95;*(_OWORD *)(v75 24) xmmword_40E185;__cxa_begin_catch(v75);v121 0LL;v109 0;do{v27 v121;v179 (unsigned __int64 *)v192 (unsigned int)v121 / 9uLL;v28 *v179;v29 (unsigned int)v121 % 9;v30 pow(v25, (double)(int)((unsigned int)v121 % 9 1));v178 v28;v31 v28 % (unsigned int)(int)(v30 0.5);y (double)v29;v32 pow(11.0, (double)v29) 0.5;v33 (unsigned int)(int)v32;v25 v32 - 9.223372036854776e18;v158 v27;v157 v109;v111 v109;if ( v31 v33 ){v111 v157 1;v51 v199[(int)v157 96];v52 pow(v25, y) 0.5;v53 (unsigned int)(int)v52;v25 v52 - 9.223372036854776e18;*v179 v178 v51 * v53;}v121 (unsigned int)(v158 1);v109 v111;}while ( (_DWORD)v158 ! 80 );__cxa_end_catch();v88 1;v140 0LL;do{v60 v108;v108[8] 0;*(_QWORD *)v60 0LL;v171 *((_QWORD *)v192 v140);v126 0LL;v170 v140;do{v19 v126;v20 v126 1;v21 pow(v25, (double)((int)v126 1));v22 v171 % (unsigned int)(int)(v21 0.5);v23 pow(11.0, (double)v19) 0.5;v24 (unsigned int)(int)v23;v25 v23 - 9.223372036854776e18;v103[v22 / v24] 1;v141 1LL;v89 v88;v126 v20;}while ( v20 ! 9 );do{v61 v89;if ( !v103[v141] )v61 0;v141;v115 v61;v89 v61;}while ( v141 ! 10 );v140 v170 1;v131 0LL;v87 v115;v88 v115;}while ( v170 ! 8 );do{v68 v108;v108[8] 0;*(_QWORD *)v68 0LL;v172 (double)((int)v131 1);v40 (double)(int)v131;v173 (double)(int)v131;v161 (unsigned int)v131;v142 0LL;do{v62 v142;v63 *((_QWORD *)v192 v142);v64 v63 % (unsigned int)(int)(pow(v40, v172) 0.5);v65 pow(11.0, v173) 0.5;v66 (unsigned int)(int)v65;v40 v65 - 9.223372036854776e18;v103[v64 / v66] 1;v142 v62 1;v144 1LL;v90 v87;}while ( v62 ! 8 );do{v71 v90;if ( !v103[v144] )v71 0;v144;v116 v71;v90 v71;}while ( v144 ! 10 );v131 (unsigned int)(v161 1);v132 0LL;v92 v116;v87 v116;}while ( (_DWORD)v131 ! 9 );do{v54 v108;v108[8] 0;*(_QWORD *)v54 0LL;v135 3 * ((unsigned int)v132 / 3);v134 3 * ((unsigned int)v132 % 3) 1;v129 0LL;v159 (unsigned int)v132;do{v34 v129;v35 *((_QWORD *)v192 (int)(v135 (unsigned int)v129 / 3));v36 (v134 (unsigned int)v129 % 3) % 9;v37 v35 % (unsigned int)(int)(pow(v40, (double)(v36 1)) 0.5);v38 pow(11.0, (double)v36) 0.5;v39 (unsigned int)(int)v38;v40 v38 - 9.223372036854776e18;v103[v37 / v39] 1;v129 (unsigned int)(v34 1);v150 1LL;v94 v92;}while ( v34 ! 8 );do{v70 v94;if ( !v103[v150] )v70 0;v150;v104 v70;v94 v70;}while ( v150 ! 10 );v132 (unsigned int)(v159 1);v92 v104;}while ( (_DWORD)v159 ! 8 );v48 v108;v108[8] 0;*(_QWORD *)v48 0LL;v127 0LL;do{v41 v127;v42 9 - v127;if ( !(_DWORD)v127 )v42 0;v43 *((_QWORD *)v192 v42);v44 v127 1;v45 v43 % (unsigned int)(int)(pow(v40, (double)((int)v127 1)) 0.5);v46 pow(11.0, (double)v41) 0.5;v47 (unsigned int)(int)v46;v40 v46 - 9.223372036854776e18;v103[v45 / v47] 1;v143 1LL;v91 v104;v127 v44;}while ( v44 ! 9 );do{v49 v91;if ( !v103[v143] )v49 0;v143;v117 v49;v91 v49;}while ( v143 ! 10 );v16 v108;v108[8] 0;*(_QWORD *)v16 0LL;v139 0LL;do{v76 v139 1;v77 v139 8;v78 v139 1;if ( v139 8 )v78 0;v79 *((_QWORD *)v192 v139);v80 v79 % (unsigned int)(int)(pow(v40, (double)(v78 1)) 0.5);v81 pow(11.0, (double)v78) 0.5;v82 (unsigned int)(int)v81;v40 v81 - 9.223372036854776e18;v103[v80 / v82] 1;v148 1LL;v93 v117;v139 v76;}while ( !v77 );do{v83 v93;if ( !v103[v148] )v83 0;v148;v118 v83;v93 v83;}while ( v148 ! 10 );return 0; }0x03 Solve the PuzzlesPART ONE之前也提到过由于我们的输入部分流可能执行不到很明显我们刚刚根本没有输入上下左右箭头啥的。所以关于处理上下左右箭头的代码无了。do{v72 v4;input1 getc(stdin);v73 input1 24;shift_input1 input1 24 0x1B000000;if ( input1 24 0x31000000 )shift_input1 2;if ( v73 0x37000000 )shift_input1 3;if ( v73 0x33000000 )shift_input1 4;if ( v73 0x34000000 )shift_input1 5;count v5;v102 v72;v119 v72;if ( shift_input1 ){if ( shift_input1 1 )_clang_call_terminate((void *)5);if ( shift_input1 2 ){v107 v102 (4LL (3 * (unsigned __int8)count));org_input input1;}else if ( shift_input1 3 ){v107 v102 (5LL (3 * (unsigned __int8)count));org_input input1;}else{if ( shift_input1 4 )v107 v102 (6LL (3 * (unsigned __int8)count));elsev107 v102 (7LL (3 * (unsigned __int8)count));org_input input1;}s[count] org_input;v119 v107;}v5 count 1;v174 v119;}while ( count ! 11 );这个时候就可以更改我们的输入指的是输入箭头再输入字符再来一遍。成功解析出我们的第一段输入。由于两个文件分析过程不贴了可以直接看官方WP给出的源码。int part1_size 12; while(count part1_size) {char a getchar();if (a 27) {if (getchar() 91) {char c getchar();try {rmCjJ0(true, c);} catch(Le3KW5 cc) {char c cc.state;if (c 65) {state 0ull (3 * count);} else if (c66) {state 2ull (3 * count);} else if (c67) {state 1ull (3 * count);} else if (c68) {state 3ull (3 * count);}}flag[count] c;}} else if (a1) {state 4ull (3 * count);flag[count] a;} else if (a7) {state 5ull (3 * count);flag[count] a;} else if (a3) {state 6ull (3 * count);flag[count] a;} else if (a4) {state 7ull (3 * count);flag[count] a;}count 1; } // ... Second Part ... // Check Part if (... state 0xb3e659480) {std::cout LIT(Congratulation! \n) LIT(Your flag is ACTF{) flag LIT(_amazing!}) std::endl; }PART TWO这个部分完全跟着lchild的分析来了。接着就是第二段输入。首先是经过一段Base64解码操作再经过取模除十操作得到一个数组。if ( (_DWORD)v50 4 ){do{v106 0LL;v149 v146;do{baseTable[v106 16] byte_40E071[v106] - byte_40E0B2[v106];// baseTablev106;}while ( v106 0x41 );v56 (__int64)v163;*(_QWORD *)v163 v164;v165 64LL;v169 (_OWORD *)std::__cxx11::basic_stringchar,std::char_traitschar,std::allocatorchar::_M_create(v56,v165,0LL);v9 (void **)v163;v10 v169;*(_QWORD *)v163 v169;v11 v165;*(_QWORD *)v164 v165;v12 MEMORY[5];v13 MEMORY[0x15];v14 MEMORY[0x25];v10[3] MEMORY[0x35];v10[2] v14;v10[1] v13;*v10 v12;*(_QWORD *)v187 v11;*((_BYTE *)v10 v11) 0;v15 v149;*(copy_input1 v15) std::__cxx11::basic_stringchar,std::char_traitschar,std::allocatorchar::find(v9,(unsigned int)*(copy_input1 v149),0LL);v177 *v9;operator delete(v177);v146 v149 1;}while ( v149 ! 3 );v17 *v186;*v183 (4 * *v57) | ((unsigned __int8)*v186 4) 3;v18 *v185;*v59 (16 * v17) | ((unsigned __int8)*v185 2) 0xF;*v184 *v58 (v18 6);v152 v110;v151 0LL;do{ // 对输入进行操作分值操作v6 v151;v7 (unsigned __int8)*(v99 v151) / 0xAu;v8 v152;baseTable[v152 96] (unsigned __int8)*(v99 v151) % 0xAu;baseTable[v8 97] v7;v151 v6 1;v152 v8 2;v182 v8 2;}while ( v6 ! 2 );v130 0LL;v112 v182;}v128 v130;v113 v112;copy_len v133;v147 v181; } while ( v133 ); // 以上是对input进行了base64解码之后计算了九个数值和一堆pang臭的代码不过干的事情不是很复杂。第一个循环是复制后两个循环判断行列不难发现这是个数独拿网站一把梭了。具体参考lchild师傅的Write up # https://sudoku.vip/sudoku-x-solver/0x04 GetFlag!!第一个解密就直接移回去即可。第二个解密出数独的值列移动取出值恢复原权位值最后Base64即可s [] t 0xB3E659480 # 每3个字节为一次输入 for i in range(12):s.append(t 0x7)t 3assert t 0 key for i in s:if i 0: key ↑elif i 1: key →elif i 2: key ↓elif i 3: key ←elif i 4: key 1elif i 5: key 7elif i 6: key 3elif i 7: key 4 print(key) # ??↓↓→←→←3417values [0x00000000331b6d84, 0x0000000054cab29a, 0x000000000cd0afcd, 0x000000006636db08, 0x0000000000021528, 0x0000000005d62020, 0x00000000070bc7c1, 0x00000000006739bd, 0x00000000001b084a] table [] for i in values:table.append([])s value ifor j in range(9):table[-1].append(int(value % 11))s %2d % (value % 11)value / 11# print(s[2: ] s[: 2]) 0 0 0 0 0 0 0 4 00 0 5 0 0 0 7 6 00 0 0 0 4 0 0 1 00 0 0 0 0 0 0 8 00 6 3 9 0 0 0 0 00 0 0 0 3 0 5 0 02 9 0 0 8 0 6 0 00 7 0 0 9 3 0 0 03 0 0 0 0 1 0 0 0 # print(sum(table, []).count(0)) # https://sudoku.vip/sudoku-x-solver/solves [ [8, 1, 6, 7, 5, 2, 3, 4, 9], [4, 3, 5, 8, 1, 9, 7, 6, 2], [7, 2, 9, 3, 4, 6, 8, 1, 5], [9, 4, 7, 1, 6, 5, 2, 8, 3], [5, 6, 3, 9, 2, 8, 4, 7, 1], [1, 8, 2, 4, 3, 7, 5, 9, 6], [2, 9, 1, 5, 8, 4, 6, 3, 7], [6, 7, 4, 2, 9, 3, 1, 5, 8], [3, 5, 8, 6, 7, 1, 9, 2, 4] ]# 数独列右移 for i in range(9):solves[i] [solves[i][-1]] solves[i][: -1] # print(solves[i])numbers [] for y in range(9):for x in range(9):if table[y][x] 0: # print(table[y][x])numbers.append(solves[y][x])assert len(numbers) % 2 0flag for i in range(0, len(numbers), 2):flag chr(numbers[i] 10 * numbers[i 1])import base64 # print(flag) print(base64.b64encode(str.encode(flag)))# ↑↑↓↓→←→←3417 # WT05ICpTW0tcPyYxETgMGTBDUSphES1TLgwtVUwd最后输入上上下下右左右左3417再二段。GetFlag!!如果你年满18周岁以上又觉得学【C语言】太难想尝试其他编程语言那么我推荐你学Python现有价值499元Python零基础课程限时免费领取限10个名额▲扫描二维码-免费领取戳“阅读原文”我们一起进步
http://www.pierceye.com/news/650221/

相关文章:

  • 外包做网站的要求怎么写网站建设调查分析
  • 北京网站建设公司哪个最好鲜花网页设计模板
  • 汕头网站制作方法江苏网站设计公司
  • 免费1级做看网站上海策朋网站设计公司
  • 自贡做网站的公司wordpress网站加密码
  • 长春建设网站公司哪家好学校网站建设实施方案
  • 邯郸网站优化怎么用建设通网站会员共享密码
  • 怎么使自己做的网站有音乐简易 建站
  • 如何做免费网站制作郑州网站建设搜索优化
  • 北京网站制作17页谈谈对seo的理解
  • 西安专业建网站网站可信度必须做吗
  • 做神马网站如何做网站的推广
  • 如何提高网站排名的方法建设一个商业网站费用
  • 电商网站平台有哪些做自己的第一个网站
  • 源码资源下载站百度指数 多少流量 网站名
  • 合肥比较好的网站建设公司青阳网站建设
  • 上海地产网站建设甘肃建设厅网站二级建造师报名时间
  • 扬州网站建设推广泊头网站建设甘肃
  • 什么行业要做网站建设推广这些水墨网站设计欣赏
  • 渠道网站wap百度
  • 在网站上如何做天气预报栏wordpress 分类列表
  • 做网站需要投资多少钱做网站的销售团队
  • 苏州哪个公司做门户网站seo优化方案报价
  • 电力建设官方网站做网站送优化
  • 门户网站建设模式包括网站群和中企动力企业邮箱登陆首页
  • 做调查网站的问卷哪个给的钱高wordpress邮箱注册功能
  • 上海php网站开发基于php网站建设
  • 大丰专业做网站做旅游网站当地人服务赚钱吗
  • 长沙网站制作公司推荐seo关键词排名优化
  • 内蒙古住房与城乡建设部网站广州十大软件公司排名