VMproblem
这是一道vm+golong的题,我们主要学习vm的解题技巧。
1 | void __cdecl main_main() |
这是主函数,能猜测出unk_57D8A0,就是opcode,我个人认为opcode相当于是出题人在这台vm机器上做了什么事!
接下来我们进入函数main___ptr_MzVm__run(v0),
1 | void __golang main___ptr_MzVm__run(__int64 a1) |
也就是说,我们通过对地址偏移的分析,就能够还原每个opcode的指令
先看init的函数,我们的opcode是在这里执行的
1 | __int64 (__usercall **__usercall main___ptr_MzVm__init@<rax>(__int64 a1))@<rax>(__int64 a1) |
简单分析几个函数
1 | int64 __usercall main___ptr_MzVm__init_func2@<rax>(__int64 a1) |
func3 mov r[{Lnum}],r[{Rnum}]
1 | __int64 __usercall main___ptr_MzVm__init_func15@<rax>(__int64 a1) |
这个明显是个对比函数,cmp R[{num}] R[{right}], jne R[19]
1 | op=[[0, 0, 0], [0, 0, 0], [1, 0, 87], [98, 0, 0], [1, 0, 101], [98, 0, 0], [1, 0, 108], [98, 0, 0], [1, 0, 99], [98, 0, 0], [1, 0, 111], [98, 0, 0], [1, 0, 109], [98, 0, 0], [1, 0, 101], [98, 0, 0], [1, 0, 32], [98, 0, 0], [1, 0, 116], [98, 0, 0], [1, 0, 111], [98, 0, 0], [1, 0, 32], [98, 0, 0], [1, 0, 86], [98, 0, 0], [1, 0, 78], [98, 0, 0], [1, 0, 67], [98, 0, 0], [1, 0, 84], [98, 0, 0], [1, 0, 70], [98, 0, 0], [1, 0, 50], [98, 0, 0], [1, 0, 48], [98, 0, 0], [1, 0, 50], [98, 0, 0], [1, 0, 50], [98, 0, 0], [1, 0, 33], [98, 0, 0], [1, 0, 10], [98, 0, 0], [1, 0, 105], [98, 0, 0], [1, 0, 110], [98, 0, 0], [1, 0, 112], [98, 0, 0], [1, 0, 117], [98, 0, 0], [1, 0, 116], [98, 0, 0], [1, 0, 32], [98, 0, 0], [1, 0, 102], [98, 0, 0], [1, 0, 108], [98, 0, 0], [1, 0, 97], [98, 0, 0], [1, 0, 103], [98, 0, 0], [1, 0, 58], [98, 0, 0], [1, 0, 10], [98, 0, 0], [1, 19, 73], [1, 3, 0], [1, 1, 43], [1, 2, 1], [97, 0, 0], [5, 0, 0], [8, 1, 2], [14, 1, 3], [1, 0, 0], [5, 0, 0], [0, 0, 0], [0, 0, 0], [6, 0, 0], [1, 5, 256], [10, 0, 5], [2, 6, 0], [6, 0, 0], [7, 6, 0], [2, 0, 6], [10, 0, 5], [2, 6, 0], [6, 0, 0], [7, 6, 0], [2, 0, 6], [10, 0, 5], [2, 6, 0], [6, 0, 0], [7, 6, 0], [0, 0, 0], [6, 0, 0], [1, 5, 256], [10, 0, 5], [2, 7, 0], [6, 0, 0], [7, 7, 0], [2, 0, 7], [10, 0, 5], [2, 7, 0], [6, 0, 0], [7, 7, 0], [2, 0, 7], [10, 0, 5], [2, 7, 0], [6, 0, 0], [7, 7, 0], [0, 0, 0], [6, 0, 0], [1, 5, 256], [10, 0, 5], [2, 8, 0], [6, 0, 0], [7, 8, 0], [2, 0, 8], [10, 0, 5], [2, 8, 0], [6, 0, 0], [7, 8, 0], [2, 0, 8], [10, 0, 5], [2, 8, 0], [6, 0, 0], [7, 8, 0], [0, 0, 0], [6, 0, 0], [1, 5, 256], [10, 0, 5], [2, 9, 0], [6, 0, 0], [7, 9, 0], [2, 0, 9], [10, 0, 5], [2, 9, 0], [6, 0, 0], [7, 9, 0], [2, 0, 9], [10, 0, 5], [2, 9, 0], [6, 0, 0], [7, 9, 0], [0, 0, 0], [6, 0, 0], [1, 5, 256], [10, 0, 5], [2, 10, 0], [6, 0, 0], [7, 10, 0], [2, 0, 10], [10, 0, 5], [2, 10, 0], [6, 0, 0], [7, 10, 0], [2, 0, 10], [10, 0, 5], [2, 10, 0], [6, 0, 0], [7, 10, 0], [0, 0, 0], [6, 0, 0], [1, 5, 256], [10, 0, 5], [2, 11, 0], [6, 0, 0], [7, 11, 0], [2, 0, 11], [10, 0, 5], [2, 11, 0], [6, 0, 0], [7, 11, 0], [2, 0, 11], [10, 0, 5], [2, 11, 0], [6, 0, 0], [7, 11, 0], [0, 0, 0], [6, 0, 0], [1, 5, 256], [10, 0, 5], [2, 12, 0], [6, 0, 0], [7, 12, 0], [2, 0, 12], [10, 0, 5], [2, 12, 0], [6, 0, 0], [7, 12, 0], [2, 0, 12], [10, 0, 5], [2, 12, 0], [6, 0, 0], [7, 12, 0], [0, 0, 0], [6, 0, 0], [1, 5, 256], [10, 0, 5], [2, 13, 0], [6, 0, 0], [7, 13, 0], [2, 0, 13], [10, 0, 5], [2, 13, 0], [6, 0, 0], [7, 13, 0], [2, 0, 13], [10, 0, 5], [2, 13, 0], [6, 0, 0], [7, 13, 0], [0, 0, 0], [6, 0, 0], [1, 5, 256], [10, 0, 5], [2, 14, 0], [6, 0, 0], [7, 14, 0], [2, 0, 14], [10, 0, 5], [2, 14, 0], [6, 0, 0], [7, 14, 0], [2, 0, 14], [10, 0, 5], [2, 14, 0], [6, 0, 0], [7, 14, 0], [0, 0, 0], [6, 0, 0], [1, 5, 256], [10, 0, 5], [2, 15, 0], [6, 0, 0], [7, 15, 0], [2, 0, 15], [10, 0, 5], [2, 15, 0], [6, 0, 0], [7, 15, 0], [2, 0, 15], [10, 0, 5], [2, 15, 0], [6, 0, 0], [7, 15, 0], [0, 0, 0], [6, 0, 0], [1, 5, 256], [10, 0, 5], [2, 16, 0], [6, 0, 0], [7, 16, 0], [2, 0, 16], [10, 0, 5], [2, 16, 0], [6, 0, 0], [7, 16, 0], [2, 0, 16], [10, 0, 5], [2, 16, 0], [6, 0, 0], [7, 16, 0], [0, 0, 0], [5, 6, 0], [5, 7, 0], [5, 8, 0], [5, 9, 0], [5, 10, 0], [5, 11, 0], [5, 12, 0], [5, 13, 0], [5, 14, 0], [5, 15, 0], [5, 16, 0], [6, 1, 0], [6, 2, 0], [1, 20, 284], [1, 0, 340], [12, 0, 0], [1, 0, 3906065887], [1, 19, 387], [1, 20, 339], [14, 1, 0], [1, 0, 4125344020], [14, 2, 0], [6, 1, 0], [6, 2, 0], [1, 20, 295], [1, 0, 340], [12, 0, 0], [1, 0, 579781142], [1, 19, 387], [1, 20, 339], [14, 1, 0], [1, 0, 2312395361], [14, 2, 0], [6, 1, 0], [6, 2, 0], [1, 20, 306], [1, 0, 340], [12, 0, 0], [1, 0, 1700499305], [1, 19, 387], [1, 20, 339], [14, 1, 0], [1, 0, 612671610], [14, 2, 0], [6, 1, 0], [6, 2, 0], [1, 20, 317], [1, 0, 340], [12, 0, 0], [1, 0, 3655723000], [1, 19, 387], [1, 20, 339], [14, 1, 0], [1, 0, 977540402], [14, 2, 0], [6, 1, 0], [6, 2, 0], [1, 20, 328], [1, 0, 340], [12, 0, 0], [1, 0, 2443935368], [1, 19, 387], [1, 20, 339], [14, 1, 0], [1, 0, 1778148540], [14, 2, 0], [6, 1, 0], [1, 0, 8206181], [14, 1, 0], [1, 0, 393], [12, 0, 0], [99, 0, 0], [1, 3, 2654435769], [1, 4, 613452], [1, 5, 34589], [1, 6, 108471], [1, 7, 1230791], [1, 8, 0], [1, 17, 16], [1, 18, 32], [1, 19, 352], [1, 10, 0], [1, 11, 32], [1, 12, 1], [7, 8, 3], [2, 0, 2], [10, 0, 17], [7, 0, 4], [2, 14, 0], [2, 0, 2], [7, 0, 8], [2, 15, 0], [2, 0, 2], [9, 0, 18], [7, 0, 5], [2, 16, 0], [2, 0, 14], [11, 0, 15], [11, 0, 16], [7, 1, 0], [2, 0, 1], [10, 0, 17], [7, 0, 6], [2, 14, 0], [2, 0, 1], [7, 0, 8], [2, 15, 0], [2, 0, 1], [9, 0, 18], [7, 0, 7], [2, 16, 0], [2, 0, 14], [11, 0, 15], [11, 0, 16], [7, 2, 0], [8, 11, 12], [14, 11, 10], [12, 20, 0], [0, 0, 0], [1, 0, 110], [98, 0, 0], [1, 0, 111], [98, 0, 0], [12, 20, 0], [0, 0, 0], [1, 0, 121], [98, 0, 0], [1, 0, 101], [98, 0, 0], [1, 0, 115], [98, 0, 0], [12, 20, 0]] |
得到:
1 | 0x0: nop |
这个delta一出来,应该是个tea加密,key也有了,我们解密
1 |
|
得到flag:VNCTF{ecd63ae5-8945-4ac4-b5a5-34fc3ade81e7}
从这道题,还学会了稍微恢复一下vm,即创造结构体。
1 | if ( HIDWORD(a1) >= 0x15uLL )//我们能推测出寄存器个数 |
还可以推测栈的大小
1 | struct func |
从而创建结构体,然后更好的分析。
未完待续
wasm
WebAssembly是一种全新的Web编程语言,但是与JavaScript不同,它不是一种让你直接手动编写的语言,而是C / C ++,Rust,C#和TypeScript等不断增加的上层语言的编译目标。
wasm是基于堆栈的虚拟机的二进制指令格式
- wat2wasm:从WebAssembly文本格式转换为 WebAssembly二进制格式
- wasm2wat:wat2wasm的逆函数,从二进制格式转换回文本格式(也称为.wat)
- wasm-objdump:显示有关wasm二进制文件的信息。与objdump类似。
- wasm-interp:使用基于堆栈的解释器解码并运行WebAssembly二进制文件
- wasm-decompile:将wasm二进制文件反编译为可读的类似C的语法。
- wat- desugar:解析规范解释程序支持的.wat文本格式(S表达式,平面语法或混合格式)并打印“规范”平面格式
- wasm2c:将WebAssembly二进制文件转换为C源代码和标头
- wasm-strip:删除WebAssembly二进制文件的部分
- wasm-validate:验证WebAssembly二进制格式的文件
- wast2json:将wasm spec测试格式的文件转换为JSON文件和关联的wasm二进制文件
- wasm-opcodecnt:计算指令的操作码使用量
- spectest-interp:读取Spectest JSON文件,然后在解释器中运行其测
这种类型的题,主要是看处理阶段比较难办,分为两种方法
第一种就是静态分析
我们需要先下载wabt包,然后make一下,之后再进行对wasm文件的反编译
1 | wasm2c xorwasm.wasm -o xorwasm.c |
然后得到还原的c文件和h头文件,之后将wasm-rt.h和wasm-rt-impl.h头⽂件移动到我们反编译出来的.c和.h⽂件夹下,采用gcc编译
1 | gcc -c xorwasm.c -o xorwasm.o |
得到的文件虽然不能执行,但可以静态分析。
第二种是动态分析
如果给了html文件,我们可以在google中直接调试,我们先在终端中起个指令:
1 | python -m http.server -b localhost |
然后就能分析了,打开网址,f12中的source,可以下断点
下完断点就刷新,发现断在了下断点的地方,然后进行调试:F8执⾏(等价于IDA的F9),F10单步步过,F11单步步⼊ ,右侧还有stack中有memory可以看。
OLLVM
控制流平坦化
如果不把ollvm给恢复,那么就很麻烦了,这个ollvm浅层的理解就是将几个跳转转换成很多switch,再搞很多分支,这样就显得很麻烦看起来,我们一般先对这些进行去除控制流平坦化,我们采用deplat.py文件进行去除,这个python文件运行是基于angr环境的。
操作:
先将文件放在deflat-master/flat_control_flow目录下,然后更改deplat文件的第十二行,换成文件路径。
1 | #!/usr/bin/env python3 |
进入ida找到main函数起始位置。
1 | python deflat.py -f normal12 --addr 0x401040 |
使用上面命令,如果终端中出现success就行了,得到的文件很美妙!
socket
第一次减这样类型的题,而且这道题还很复杂。
给了两个文件,一个login一个check,很烦,先看login
1 | void __fastcall __noreturn sub_401DB1(int a1, _QWORD *a2, __int64 a3, __int64 a4, int a5, int a6) |
这是一个socket发送字符串的程序
socket编程是一门技术,它主要是在网络通信中经常用到,我的理解是用户在tcp/ip的基础上通过socket进行信息交互
一般socket使用时先传送端口再传送ip,在发送connect请求
1 | int socket(int domain, int type, int protocol); |
connect函数反编译后:
1 | unsigned __int64 __fastcall connect(int a1, struct sockaddr *a2, int a3) |
限定了我们第一个发过去的必须是2,如果不是就退出,我们查看发送的东西
这个0x52很明显是aes的INV_SBOX,这个还是能看出来的。
看完login再看server的check
1 | __int64 sub_405DA7() |
经典fork函数
1 | __int64 fork() |
我们直接看主函数
1 | unsigned __int64 __fastcall sub_405F6A(unsigned int connfd, __int64 a2, __int64 a3) |
sub_55C070传入的三个参数分别是connfd,客户端的ip和端口,要输入两个东西,要进行三个函数的逆向
第一个checkrsa:
1 | _BOOL8 __fastcall sub_405A10(__int64 a1) |
这里要注意,m转换成16进制要是小端序:
1 | from gmpy2 import* |
第二个函数是一个矩阵:
1 | __int64 __fastcall sub_405BDE(__int64 a1) |
矩阵 * 我们要求的 == random() % 255,而这个random%255已经存在,听师傅说好像没删。
我们采用z3来搞:
1 | from z3 import* |
或者采用syj师傅的sage脚本
1 | arr1 = [[0x71, 0xDB, 0x25, 0x2E, 0x7A, 0x0F], |
得到矩阵再转换成16进制5132d202c32d95b9f978d514e3294220513b15623482b4c02e9afde8bad5ec07486a5488
得到password
最后check分析,展示一下aes的函数:
1 | unsigned __int64 __fastcall sub_4055C0(__int64 roundkeys, __int64 flag, __int64 a3, __int64 a4) |
这个第一次见还真不好搞出来是个aes
行列置换:
1 | __int64 __fastcall sub_40554A(__int64 a1, __int64 a2, __int64 a3) |
轮密钥:
1 | __int64 __fastcall sub_404F60(__int64 a1, __int64 a2, __int64 a3) |
行移位:
1 | bool __fastcall sub_405078(__int64 a1, __int64 a2) |
S盒替换
1 | __int64 __fastcall sub_404FF4(__int64 a1, __int64 a2) |
列混合:
1 | unsigned __int64 __fastcall sub_4053C8(__int64 a1, __int64 a2) |
轮密钥加:
1 | __int64 __fastcall sub_404F60(__int64 a1, __int64 a2, __int64 a3) |
给出aes(256)解密脚本:
1 |
|
得到:7026271d7bb5d404d63a72b88e6b4d63