xctf战役_部分re题目
这次比赛做了四道简单的RE…难的师父做了嘻嘻,我好菜我好菜我好菜,Helica tql!
cycle graph
主要记录一份图路径搜索算法的代码hh
|
|
天津垓
两个考点,第一个是反调试,第二个是smc
反调试中一个是枚举窗口检测ida od等等,还有一个是判断STARTUPINFO信息
题目有两个验证部分,第一个验证是代码中直接可见的,我用z3求解解出来了,第二个是需要经过smc后才可见的,我做题的时候并没有发现smc,用的持续单步然后dump出解密后的代码的方式。 不过这个smc函数并不在基本的流程里面,主要是使用了cygwin这个动态库,然后调用的,我查了一下cygwin这个动态库,主要是用来在windows平台上实现linux平台函数使用的,我后来调试的时候也是发现先进入cygwin.dll然后就到了smc函数的地方,我没太懂这个具体的原理==下面是smc的部分
|
|
在 Windows 程序中使用了VirtualProtect()函数来改变虚拟内存区域的属性。函数声明如下:
|
|
VirtualProtect()函数有4个参数,lpAddress是要改变属性的内存起始地址,dwSize是要改变属性的内存区域大小,flAllocationType是内存新的属性类型,lpflOldProtect内存原始属性类型保存地址。而flAllocationType部分值如下表。在 SMC 中常用的是 0x40。
这里smc就是一个简单的与第一部分的输入进行一个异或,使用idc脚本可以得到修改后的代码
第二部分我还是用z3求解,但是因为乘数是素数也可以直接求逆元
fxck!
这道题出的还挺那啥的,出题人一手替换文件把我坑惨了…题目最开始的验证部分写的有问题,但是我也有自己的问题,第一个就是不会base58(u1s1就算不知道base58我就连16进制转58进制都没看出来…活该我菜)第二个就是不会brainfuck,我以为是个虚拟机分析了好久,虽然分析出来加密的过程了但是用时太长了…记录一下这两个
加密部分
加密部分就是个base58,典型特征就是转58(0x3a)进制,还有查表操作,反编译的代码如下
|
|
验证部分
验证部分是一个brainfuck语言的解释器,这种语言基于一个简单的机器模型,除了指令,这个机器还包括:一个以字节为单位、被初始化为零的数组、一个指向该数组的指针(初始时指向数组的第一个字节)、以及用于输入输出的两个字节流。关于这个语言的介绍:
Brainfuck
Brainfuck在线解析器
这个语言一共只包含如下8个状态
典型特征是代码中包含指针的加减以及指针指向值的加减
|
|
提取byte_6020A0中的指令,使用下面的解释器可以对brainfuck反编译
|
|
easyparser
这道题是做出来让我最高兴的一道题(xiaoku.jpg)主要是查了一下发现用了Rust,Rust代码里面会多了很多安全检查的部分来扰乱一下视线(比如说做加法之前会检查加法会不会溢出),去掉这些安全检查的部分之后倒是挺容易看出来解释器的逻辑,之后就是体力活了==
wp里面有句话记录一下,或许以后出题用得着呢嘻嘻
一个vm逆向题目,在.init_array中初始化了一部分数据,在.fin_array中进行flag的检查,由于Rust编程
规范不允许在main前和main后编写逻辑,所以先用Rust写了一个so库,用c语言进行一次包裹
逆出来以后可以看出来流程,第一层用来检查输入长度等于38,第二层将输入的中间部分,左移2后与99异或等于特定值.下面是反编译的代码
|
|
clock
从这里开始都是我不会的题==
这个题的加密流程很容易能看出来,但是我不会解…后来师父来了很快就做出来了略(Helica好厉害好厉害我好菜我好菜),wp中说是穷举钟控的寄存器初态,对另外两个寄存器快速相关攻击。
程序实现了一个时钟控制的非线性移位寄存器,由一个lfsr控制另外两个lfsr的输出。
|
|
用相关性攻击,先分析输出与三个lfsr的关系,可知输出与n2和n3相同的概率是0.75,和n1相同的概率是0.5。先分别爆破猜出n2和n3,之后再爆破推出n1。
|
|
X1cT34m给了一个c版本的,应该要快很多,记录一下
|
|
关于LFSR的一些资料
线性反馈移位寄存器与梅森旋转算法
深入分析CTF中的LFSR类题目(一)
深入分析CTF中的LFSR类题目(二)
baby_wasi
趁着这道题好好学习了一下wasm,感觉收获还挺多的(Helica tql tql tql)
反编译wasm
这道题用了wasmer-c-api来构建,主程序为baby_wasi,program.wasm为子程序,主要处理字符串变换逻辑.首先对program.wasm逆向分析.基础教程:
一种Wasm逆向静态分析方法
反汇编的话,可以用wasm2wat
把wasm反汇编成wat,https://developer.mozilla.org/zh-CN/docs/WebAssembly/Understanding_the_text_format 这里面对wat进行了一些解释
还可以把wasm转成c语言的格式,用wasm2c
|
|
但是因为生成的c语言很长而且基本跟看wat没什么区别,所以需要再编译成二进制文件放到ida里面去看
将之前反编译出来的wasm.c,wasm.h,以及wabt项目内的wasm-rt.h,wasm-rt-impl.c,wasm-rt-impl.h三个文件放到同一个文件夹。
直接gcc wasm.c会报错,因为很多wasm的函数没有具体的实现。但是我们可以只编译不链接,我们关心的只是程序本身的逻辑,不需要真正编译出能运行的elf来。
|
|
得到的还未连接的elf文件wasm.o, 将wasm.o放到ida里面分析会比较清楚一些。
查找main函数
从反编译的代码里面可以看到有_start函数,然后需要从这一堆函数里面找到关键函数…对于wasm,所有的字符串会被存放在二进制文件的末尾,而且wasm并不是直接对地址的引用,想找到这些字符串会比较困难。Nu1L的wp里面说识别出来malloc,free,exit这些函数然后才推测出来main函数的位置,我在谷歌上找到了一份保留函数名称的代码,可以对照着识别出来main函数,函数如下:
|
|
然后根据这个可以识别出来main函数,找到main函数以后就比较好找关键函数的位置了,关键函数如下:
|
|
f40的参数是1024,f69的参数是1047,刚好对应之前的字符串常量之间的偏移,memory的初始化如下:
|
|
可以看到memory + 1024中是"Your lucky number"所以可以大致猜到f40是printf函数
然后其他的字符串偏移如下:
|
|
memory + 1024 - 0x32FC0 + 0x32FD7 = memory + 1047,所以f69的第一个参数是"%64",推断出f69是scanf函数。附加一些wasm中函数含义
|
|
wasmer-c-api
主程序baby_wasi加载并执行program.wasm,其中baby_wasi是使用了一些wasmer-c-api来执行wasm的,需要了解一下这些api的含义才能更好的理解程序执行过程. wasmer-c-api的手册https://docs.rs/wasmer-runtime-c-api/0.16.2/wasmer_runtime_c_api/
找到了一个使用wasmer-c-api执行wasm的例子,对照这个例子理解一下:
|
|
然后对比着看我们的baby_wasi:
|
|
可以看到baby_wasi把boom函数导入了wasm的env中,然后来看一下program.wat中相应的部分:
|
|
在刚才找到的关键代码处:
|
|
Z_envZ_boomZ_vii
就是boom函数
所以在对输入进行异或之后,把异或后的内容的地址作为参数调用了boom函数
|
|
可以看到boom函数直接执行了输入后异或的代码,简单来说就是我们输入一个长度小于64的字符串,然后程序把这个字符串异或以后执行,所以我们需要传入一段经过异或的shell,程序就可以执行这个shell了。异或的部分根据wp来看是key[i]=emirp[lucky_nubmer + i] % 256, emirp 即为反素数序列,可以参考:https://oeis.org/A006567 我们可以直接还原代码对shell异或
exp
|
|
Rubik(魔方)
题目是Rubik加上提供了URF三种操作,所以是一个魔方题。然后魔方的状态使用最多9个字节描述,说明是一个2*2的魔方(3*3的魔方至少需要21个字节描述)
在线魔方求解器
OptimalSolver-Nu1L
py222-Helica
我感觉helica这个魔方求解器的逻辑要简单一些(Nu1L给的魔方求解器能求出来一些py222不能给的解法),所以我还是用了师父给的,毕竟我研究了挺久只会这个…
2*2魔方简介
首先一个标准的魔方是如下结构的:
|
|
一个魔方分为UDFBRL六个面,按照上面这个标准的魔方展开图来说,891011这个面是F面,0123这个面是U面,4567这个面是R面,然后FUR这三个操作就是分别对应这三个面做顺时针旋转操作(F2指对F面顺时针旋转两次, F’指对F面逆时针旋转一次)
py222用法
solver.py里面给出了求解的调用过程
|
|
魔方还原
明白是一个2*2的魔方之后,主要的难点在于给的状态并不是按照标准魔方状态给的,就是给的9字节状态,转成24长度的数组后,数组的0123位并不对于上面给的魔方标准状态的0123位置,为了还原位置我们需要记录对标准魔方分别做URF后的状态。题目中给的标准状态数字是0xB6D9246DB492249,我们在旋转魔方操作之前将表示状态的内存中的数字修改为0xB6D9246DB492249,然后分别做URF操作,记录操作后的状态
|
|
用下面程序还原分别做URF操作后魔方的状态
|
|
根据上面的结果可以按照字符串顺序还原出这个魔方对应的状态
|
|
也就是说,字符串中第0个位置对应py222给出的标准魔方的第15个位置,字符串中第1个位置对应py222给出的标准魔方的第13个位置
然后我们根据字符的顺序,将这些字符填入它们在标准魔方中的对应位置后求解
|
|