Hack.lu 2024 Getting into Shape 解析
2024-11-21 18:9:0 Author: mp.weixin.qq.com(查看原文) 阅读量:2 收藏

1.本篇文章详细讨论wasm逆向,针对wasm2c wasm2js (wasm2wat 更是没法看)量大 代码多 且市面上没有出色wasm反编译引擎(JEB 也无济于事),我们如何海量代码中找到核心逻辑,相信看完本篇文章你会获得一些思路。

2.本篇文章详细讨论了Salsa20 家族流密码的区别,以及在汇编(WebAssembly)的特征,相信你看完本篇文章,下次再遇到同种的加密算法,可以快速反应。

3.最后,算是笔者的一些小探索,探讨rust通过wasm_bindgen编译wasm,一些有趣的小机制,算是一个彩蛋,如果你感兴趣,兴许可以出个有趣的小题目或者实现一个神秘的小功能。

一、题目背景

题目附件给了一个tff(TrueTypeFont)文件,在该文件中嵌入一个wasm, 该wasm文件的源码通过rust编写,该代码写了flag的checker部分,checker部分算法使用chacha20加密,通过字体的控制完成映射,并显示,显示结果:

flag错时:

flag对时:

二、wasm提取

首先将ttf文件放到010 editor中观察,在ttf最后一个成员gasp 后往下的一部分 出现asm(wasm的magic number)。

推测这里是嵌入了wasm文件(实际就是harfbuzz中可选的wasm shaper)

使用python脚本提取:

with open('challenge.ttf','rb') as file:
read_bytes = file.read()

with open('challenge.wasm','wb') as ff:
start = 0x1eb9c
end = start + 0x12841b
ff.write(read_bytes[start:end])

end的选取 在使用wasm2wat等工具时会报错,根据报错逐渐调整end:

可以看到结尾的‘字符串’是/+.\S*$/的格式, 按照这个规律challenge的wasm的end应在这里:

当然也可以自己编译一个wasm比较一下,下面是我自己编译的demo.wasm(wasm-pack 构建rust项目)[[how to Compile a WebAssembly module from rust](https://developer.mozilla.org/en-US/docs/WebAssembly/Rust_to_Wasm)]

三、wasm文件处理

对于wasm的文件处理可以使用wasm2c或者wasm2js。这里使用wasm2c 对wasm文件进行处理。

实际不管使用wasm2c 还是 wasm2js, 代码形式和汇编差不多,并没有多少简化。wasm2c反编译出来的数据是16进制显示,wasm2js是base64编码,这对于我们分析字符串都不方便,这里使用wasm2wat查看dcmp文件中的字符串:

data d_Usersmsanftcargoregistrysrci(offset: 1048576) =
"/Users/msanft/.cargo/registry/src/index.crates.io-6f17d22bba15001f/reg"
"ex-automata-0.4.8/src/util/pool.rs\00\00\10\00h\00\00\00=\02\00\00\1c\00"
"\00\00\00\00\00\00\04\00\00\00\04\00\00\00\01\00\00\00/Users/msanft/.c"
"argo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.4."
"8/src/util/pool.rs\88\00\10\00h\00\00\00^\02\00\00\1c\00\00\00\88\00\10"
"\00h\00\00\00k\02\00\002\00\00\00\88\00\10\00h\00\00\00\01\03\00\00\15"
"\00\00\00StreamCipherError\00\00\00expand 32-byte kcalled `Result::unw"
"rap()` on an `Err` valueMq\ac\14v\b1\9al\aa\ec\86qA\12\f3\bfkr\f0\a5kz"
"\fc\d1\1aR\9c\d3\ba\00\00\00\00\00\00\00\00\01\00\00\00\02\00\00\00/Us"
"ers/msanft/.cargo/registry/src/index.crates.io-6f17d22bba15001f/cipher"
"-0.4.4/src/stream.rs\00\00\00\9c\01\10\00]\00\00\00x\00\00\00'\00\00\00"
"flag\{([^{}]*)\}src/lib.rs\00\00\1c\02\10\00\0a\00\00\00\18\00\00\00-\00"
"\00\00nah\f0\9f\98\90\f0\9f\98\90yasss!!\f0\9f\98\8c\f0\9f\92\85\00\00"
"\03\00\00\00\0c\00\00\00\04\00\00\00\04\00\00\00\1c\02\10\00\0a\00\00\00"
"\15\00\00\00.\00\00\00Couldn't copy buffer contents\00\00\00t\02\10\00"
"\1d\00\00\00/Users/msanft/Documents/Documents - Moritz\e2\80\99s MacBo"
"ok Pro/harfbuzz-wasm-examples/harfbuzz-wasm/src/lib.rs\00\00\9c\02\10\00"
"j\00\00\00>\01\00\00;\00\00\00Couldn't set buffer contents\18\03\10\00"
"\1c\00\00\00\9c\02\10\00j\00\00\00^\01\00\00\11\00\00\00regex: thread "
"ID allocation space exhausted\00L\03\10\00+\00\00\00/Users/msanft/.car"
"go/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.4.8/"
"src/util/pool.rs\80\03\10\00h\00\00\00^\01\00\00\11\00\00\00\00\00\00\00"
"\04\00\00\00\04\00\00\00\07\00\00\00Error\00\00\00\08\00\00\00\0c\00\00"
"\00\04\00\00\00\09\00\00\00\0a\00\00\00\0b";

可以看到有关于flag的正则表达式:flag{([^{}]*)。

关键的.rs,如arfbuzz-wasm-examples/harfbuzz-wasm/src/lib.rs, google一下就会知道,这是github项目,在该仓库中我可以找到在m1上可以使用harfbuzz的工具:https://github.com/harfbuzz/harfbuzz-wasm-examples/tree/main/fontgoggles-wasm-m1

正是前面提到的查看flag checker运行结果的程序。

四、wasm逆向-寻找线索

这里查看wasm2c反编译出来的c语言,太长,太多,总共有41万行,找个函数引用vscode都会卡住。

人工分析显然是不太可能,这是我编写了一个对“flag{([^{}]*)”所在缓冲区的引用搜集。

在wasm2c该缓冲区为:

static const u8 data_segment_data_w2c_challenge_d0[] = {
0x2f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x6d, 0x73, 0x61, 0x6e, 0x66,
0x74, 0x2f, 0x2e, 0x63, 0x61, 0x72, 0x67, 0x6f, 0x2f, 0x72, 0x65, 0x67,
0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x69, 0x6e,
0x64, 0x65, 0x78, 0x2e, 0x63, 0x72, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x69,
0x6f, 0x2d, 0x36, 0x66, 0x31, 0x37, 0x64, 0x32, 0x32, 0x62, 0x62, 0x61,
0x31, 0x35, 0x30, 0x30, 0x31, 0x66, 0x2f, 0x72, 0x65, 0x67, 0x65, 0x78,
0x2d, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x61, 0x2d, 0x30, 0x2e,
0x34, 0x2e, 0x38, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x75, 0x74, 0x69, 0x6c,
0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2e, 0x72, 0x73, 0x00, 0x00, 0x10, 0x00,
0x68, 0x00, 0x00, 0x00, 0x3d, 0x02, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x2f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x6d,
0x73, 0x61, 0x6e, 0x66, 0x74, 0x2f, 0x2e, 0x63, 0x61, 0x72, 0x67, 0x6f,
0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x73, 0x72,
0x63, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, 0x63, 0x72, 0x61, 0x74,
0x65, 0x73, 0x2e, 0x69, 0x6f, 0x2d, 0x36, 0x66, 0x31, 0x37, 0x64, 0x32,
0x32, 0x62, 0x62, 0x61, 0x31, 0x35, 0x30, 0x30, 0x31, 0x66, 0x2f, 0x72,
0x65, 0x67, 0x65, 0x78, 0x2d, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74,
0x61, 0x2d, 0x30, 0x2e, 0x34, 0x2e, 0x38, 0x2f, 0x73, 0x72, 0x63, 0x2f,
0x75, 0x74, 0x69, 0x6c, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2e, 0x72, 0x73,
0x88, 0x00, 0x10, 0x00, 0x68, 0x00, 0x00, 0x00, 0x5e, 0x02, 0x00, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x10, 0x00, 0x68, 0x00, 0x00, 0x00,
0x6b, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x88, 0x00, 0x10, 0x00,
0x68, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72,
0x45, 0x72, 0x72, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x65, 0x78, 0x70, 0x61,
0x6e, 0x64, 0x20, 0x33, 0x32, 0x2d, 0x62, 0x79, 0x74, 0x65, 0x20, 0x6b,
0x63, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x60, 0x52, 0x65, 0x73, 0x75,
0x6c, 0x74, 0x3a, 0x3a, 0x75, 0x6e, 0x77, 0x72, 0x61, 0x70, 0x28, 0x29,
0x60, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x60, 0x45, 0x72, 0x72,
0x60, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x71, 0xac, 0x14, 0x76,
0xb1, 0x9a, 0x6c, 0xaa, 0xec, 0x86, 0x71, 0x41, 0x12, 0xf3, 0xbf, 0x6b,
0x72, 0xf0, 0xa5, 0x6b, 0x7a, 0xfc, 0xd1, 0x1a, 0x52, 0x9c, 0xd3, 0xba,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x2f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x6d,
0x73, 0x61, 0x6e, 0x66, 0x74, 0x2f, 0x2e, 0x63, 0x61, 0x72, 0x67, 0x6f,
0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x73, 0x72,
0x63, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, 0x63, 0x72, 0x61, 0x74,
0x65, 0x73, 0x2e, 0x69, 0x6f, 0x2d, 0x36, 0x66, 0x31, 0x37, 0x64, 0x32,
0x32, 0x62, 0x62, 0x61, 0x31, 0x35, 0x30, 0x30, 0x31, 0x66, 0x2f, 0x63,
0x69, 0x70, 0x68, 0x65, 0x72, 0x2d, 0x30, 0x2e, 0x34, 0x2e, 0x34, 0x2f,
0x73, 0x72, 0x63, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2e, 0x72,
0x73, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x10, 0x00, 0x5d, 0x00, 0x00, 0x00,
0x78, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x66, 0x6c, 0x61, 0x67,
0x5c, 0x7b, 0x28, 0x5b, 0x5e, 0x7b, 0x7d, 0x5d, 0x2a, 0x29, 0x5c, 0x7d,
0x73, 0x72, 0x63, 0x2f, 0x6c, 0x69, 0x62, 0x2e, 0x72, 0x73, 0x00, 0x00,
0x1c, 0x02, 0x10, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x2d, 0x00, 0x00, 0x00, 0x6e, 0x61, 0x68, 0xf0, 0x9f, 0x98, 0x90, 0xf0,
0x9f, 0x98, 0x90, 0x79, 0x61, 0x73, 0x73, 0x73, 0x21, 0x21, 0xf0, 0x9f,
0x98, 0x8c, 0xf0, 0x9f, 0x92, 0x85, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x1c, 0x02, 0x10, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x2e, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x75, 0x6c, 0x64, 0x6e, 0x27, 0x74,
0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72,
0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x00, 0x00, 0x00,
0x74, 0x02, 0x10, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x2f, 0x55, 0x73, 0x65,
0x72, 0x73, 0x2f, 0x6d, 0x73, 0x61, 0x6e, 0x66, 0x74, 0x2f, 0x44, 0x6f,
0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x44, 0x6f, 0x63, 0x75,
0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x2d, 0x20, 0x4d, 0x6f, 0x72, 0x69,
0x74, 0x7a, 0xe2, 0x80, 0x99, 0x73, 0x20, 0x4d, 0x61, 0x63, 0x42, 0x6f,
0x6f, 0x6b, 0x20, 0x50, 0x72, 0x6f, 0x2f, 0x68, 0x61, 0x72, 0x66, 0x62,
0x75, 0x7a, 0x7a, 0x2d, 0x77, 0x61, 0x73, 0x6d, 0x2d, 0x65, 0x78, 0x61,
0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x68, 0x61, 0x72, 0x66, 0x62, 0x75,
0x7a, 0x7a, 0x2d, 0x77, 0x61, 0x73, 0x6d, 0x2f, 0x73, 0x72, 0x63, 0x2f,
0x6c, 0x69, 0x62, 0x2e, 0x72, 0x73, 0x00, 0x00, 0x9c, 0x02, 0x10, 0x00,
0x6a, 0x00, 0x00, 0x00, 0x3e, 0x01, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00,
0x43, 0x6f, 0x75, 0x6c, 0x64, 0x6e, 0x27, 0x74, 0x20, 0x73, 0x65, 0x74,
0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x74,
0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x9c, 0x02, 0x10, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x5e, 0x01, 0x00, 0x00,
0x11, 0x00, 0x00, 0x00, 0x72, 0x65, 0x67, 0x65, 0x78, 0x3a, 0x20, 0x74,
0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x49, 0x44, 0x20, 0x61, 0x6c, 0x6c,
0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x70, 0x61, 0x63,
0x65, 0x20, 0x65, 0x78, 0x68, 0x61, 0x75, 0x73, 0x74, 0x65, 0x64, 0x00,
0x4c, 0x03, 0x10, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x2f, 0x55, 0x73, 0x65,
0x72, 0x73, 0x2f, 0x6d, 0x73, 0x61, 0x6e, 0x66, 0x74, 0x2f, 0x2e, 0x63,
0x61, 0x72, 0x67, 0x6f, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
0x79, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e,
0x63, 0x72, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x69, 0x6f, 0x2d, 0x36, 0x66,
0x31, 0x37, 0x64, 0x32, 0x32, 0x62, 0x62, 0x61, 0x31, 0x35, 0x30, 0x30,
0x31, 0x66, 0x2f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x2d, 0x61, 0x75, 0x74,
0x6f, 0x6d, 0x61, 0x74, 0x61, 0x2d, 0x30, 0x2e, 0x34, 0x2e, 0x38, 0x2f,
0x73, 0x72, 0x63, 0x2f, 0x75, 0x74, 0x69, 0x6c, 0x2f, 0x70, 0x6f, 0x6f,
0x6c, 0x2e, 0x72, 0x73, 0x80, 0x03, 0x10, 0x00, 0x68, 0x00, 0x00, 0x00,
0x5e, 0x01, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x45, 0x72, 0x72, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x0b,
};

加载位置:

static void init_memories(w2c_challenge* instance) {
wasm_rt_allocate_memory(&instance->w2c_memory, 23, 65536, 0);
LOAD_DATA(instance->w2c_memory, 1048576u, data_segment_data_w2c_challenge_d0, 1061);
//....
}

下面使用python脚本对1048576-101048576+1061处所有的引用进行提取。

ss = [0x2f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x6d, 0x73, 0x61, 0x6e, 0x66, 
0x74, 0x2f, 0x2e, 0x63, 0x61, 0x72, 0x67, 0x6f, 0x2f, 0x72, 0x65, 0x67,
0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x69, 0x6e,
0x64, 0x65, 0x78, 0x2e, 0x63, 0x72, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x69,
0x6f, 0x2d, 0x36, 0x66, 0x31, 0x37, 0x64, 0x32, 0x32, 0x62, 0x62, 0x61,
0x31, 0x35, 0x30, 0x30, 0x31, 0x66, 0x2f, 0x72, 0x65, 0x67, 0x65, 0x78,
0x2d, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x61, 0x2d, 0x30, 0x2e,
0x34, 0x2e, 0x38, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x75, 0x74, 0x69, 0x6c,
0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2e, 0x72, 0x73, 0x00, 0x00, 0x10, 0x00,
0x68, 0x00, 0x00, 0x00, 0x3d, 0x02, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x2f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x6d,
0x73, 0x61, 0x6e, 0x66, 0x74, 0x2f, 0x2e, 0x63, 0x61, 0x72, 0x67, 0x6f,
0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x73, 0x72,
0x63, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, 0x63, 0x72, 0x61, 0x74,
0x65, 0x73, 0x2e, 0x69, 0x6f, 0x2d, 0x36, 0x66, 0x31, 0x37, 0x64, 0x32,
0x32, 0x62, 0x62, 0x61, 0x31, 0x35, 0x30, 0x30, 0x31, 0x66, 0x2f, 0x72,
0x65, 0x67, 0x65, 0x78, 0x2d, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74,
0x61, 0x2d, 0x30, 0x2e, 0x34, 0x2e, 0x38, 0x2f, 0x73, 0x72, 0x63, 0x2f,
0x75, 0x74, 0x69, 0x6c, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2e, 0x72, 0x73,
0x88, 0x00, 0x10, 0x00, 0x68, 0x00, 0x00, 0x00, 0x5e, 0x02, 0x00, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, 0x10, 0x00, 0x68, 0x00, 0x00, 0x00,
0x6b, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x88, 0x00, 0x10, 0x00,
0x68, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72,
0x45, 0x72, 0x72, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x65, 0x78, 0x70, 0x61,
0x6e, 0x64, 0x20, 0x33, 0x32, 0x2d, 0x62, 0x79, 0x74, 0x65, 0x20, 0x6b,
0x63, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x60, 0x52, 0x65, 0x73, 0x75,
0x6c, 0x74, 0x3a, 0x3a, 0x75, 0x6e, 0x77, 0x72, 0x61, 0x70, 0x28, 0x29,
0x60, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x60, 0x45, 0x72, 0x72,
0x60, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x71, 0xac, 0x14, 0x76,
0xb1, 0x9a, 0x6c, 0xaa, 0xec, 0x86, 0x71, 0x41, 0x12, 0xf3, 0xbf, 0x6b,
0x72, 0xf0, 0xa5, 0x6b, 0x7a, 0xfc, 0xd1, 0x1a, 0x52, 0x9c, 0xd3, 0xba,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x2f, 0x55, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x6d,
0x73, 0x61, 0x6e, 0x66, 0x74, 0x2f, 0x2e, 0x63, 0x61, 0x72, 0x67, 0x6f,
0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x73, 0x72,
0x63, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, 0x63, 0x72, 0x61, 0x74,
0x65, 0x73, 0x2e, 0x69, 0x6f, 0x2d, 0x36, 0x66, 0x31, 0x37, 0x64, 0x32,
0x32, 0x62, 0x62, 0x61, 0x31, 0x35, 0x30, 0x30, 0x31, 0x66, 0x2f, 0x63,
0x69, 0x70, 0x68, 0x65, 0x72, 0x2d, 0x30, 0x2e, 0x34, 0x2e, 0x34, 0x2f,
0x73, 0x72, 0x63, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2e, 0x72,
0x73, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x10, 0x00, 0x5d, 0x00, 0x00, 0x00,
0x78, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x66, 0x6c, 0x61, 0x67,
0x5c, 0x7b, 0x28, 0x5b, 0x5e, 0x7b, 0x7d, 0x5d, 0x2a, 0x29, 0x5c, 0x7d,
0x73, 0x72, 0x63, 0x2f, 0x6c, 0x69, 0x62, 0x2e, 0x72, 0x73, 0x00, 0x00,
0x1c, 0x02, 0x10, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x2d, 0x00, 0x00, 0x00, 0x6e, 0x61, 0x68, 0xf0, 0x9f, 0x98, 0x90, 0xf0,
0x9f, 0x98, 0x90, 0x79, 0x61, 0x73, 0x73, 0x73, 0x21, 0x21, 0xf0, 0x9f,
0x98, 0x8c, 0xf0, 0x9f, 0x92, 0x85, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x1c, 0x02, 0x10, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x2e, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x75, 0x6c, 0x64, 0x6e, 0x27, 0x74,
0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72,
0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x00, 0x00, 0x00,
0x74, 0x02, 0x10, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x2f, 0x55, 0x73, 0x65,
0x72, 0x73, 0x2f, 0x6d, 0x73, 0x61, 0x6e, 0x66, 0x74, 0x2f, 0x44, 0x6f,
0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x44, 0x6f, 0x63, 0x75,
0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x2d, 0x20, 0x4d, 0x6f, 0x72, 0x69,
0x74, 0x7a, 0xe2, 0x80, 0x99, 0x73, 0x20, 0x4d, 0x61, 0x63, 0x42, 0x6f,
0x6f, 0x6b, 0x20, 0x50, 0x72, 0x6f, 0x2f, 0x68, 0x61, 0x72, 0x66, 0x62,
0x75, 0x7a, 0x7a, 0x2d, 0x77, 0x61, 0x73, 0x6d, 0x2d, 0x65, 0x78, 0x61,
0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x68, 0x61, 0x72, 0x66, 0x62, 0x75,
0x7a, 0x7a, 0x2d, 0x77, 0x61, 0x73, 0x6d, 0x2f, 0x73, 0x72, 0x63, 0x2f,
0x6c, 0x69, 0x62, 0x2e, 0x72, 0x73, 0x00, 0x00, 0x9c, 0x02, 0x10, 0x00,
0x6a, 0x00, 0x00, 0x00, 0x3e, 0x01, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00,
0x43, 0x6f, 0x75, 0x6c, 0x64, 0x6e, 0x27, 0x74, 0x20, 0x73, 0x65, 0x74,
0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x74,
0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x9c, 0x02, 0x10, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x5e, 0x01, 0x00, 0x00,
0x11, 0x00, 0x00, 0x00, 0x72, 0x65, 0x67, 0x65, 0x78, 0x3a, 0x20, 0x74,
0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x49, 0x44, 0x20, 0x61, 0x6c, 0x6c,
0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x70, 0x61, 0x63,
0x65, 0x20, 0x65, 0x78, 0x68, 0x61, 0x75, 0x73, 0x74, 0x65, 0x64, 0x00,
0x4c, 0x03, 0x10, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x2f, 0x55, 0x73, 0x65,
0x72, 0x73, 0x2f, 0x6d, 0x73, 0x61, 0x6e, 0x66, 0x74, 0x2f, 0x2e, 0x63,
0x61, 0x72, 0x67, 0x6f, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
0x79, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e,
0x63, 0x72, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x69, 0x6f, 0x2d, 0x36, 0x66,
0x31, 0x37, 0x64, 0x32, 0x32, 0x62, 0x62, 0x61, 0x31, 0x35, 0x30, 0x30,
0x31, 0x66, 0x2f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x2d, 0x61, 0x75, 0x74,
0x6f, 0x6d, 0x61, 0x74, 0x61, 0x2d, 0x30, 0x2e, 0x34, 0x2e, 0x38, 0x2f,
0x73, 0x72, 0x63, 0x2f, 0x75, 0x74, 0x69, 0x6c, 0x2f, 0x70, 0x6f, 0x6f,
0x6c, 0x2e, 0x72, 0x73, 0x80, 0x03, 0x10, 0x00, 0x68, 0x00, 0x00, 0x00,
0x5e, 0x01, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x45, 0x72, 0x72, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x0b]

import re

re_s = '104[8-9]\d\d\d'

with open('challenge.c','r') as file:
read_bytes = file.read()

res = re.findall(re_s,read_bytes)

start = 1048576

for r in res:
address = int(r)
address -= start
if address > 1061:
continue
temp = b''
for i in ss[address:address+32]:# use 32 just want to see more
temp += i.to_bytes()
print(temp,address+start)

运行结果:

python3.11 parse.py
b'/Users/msanft/.cargo/registry/sr' 1048576
b'/Users/msanft/.cargo/registry/sr' 1048576
b't\x02\x10\x00\x1d\x00\x00\x00/Users/msanft/Documents/' 1049236
b"\x9c\x02\x10\x00j\x00\x00\x00>\x01\x00\x00;\x00\x00\x00Couldn't set buf" 1049352
b'flag\\{([^{}]*)\\}src/lib.rs\x00\x00\x1c\x02\x10\x00' 1049100
b'\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x07\x00\x00\x00Error\x00\x00\x00\x08\x00\x00\x00\x0c\x00\x00\x00' 1049592
b'\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x07\x00\x00\x00Error\x00\x00\x00\x08\x00\x00\x00\x0c\x00\x00\x00' 1049592
b'/Users/msanft/.cargo/registry/sr' 1048712
b'/Users/msanft/.cargo/registry/sr' 1048576
b'/Users/msanft/.cargo/registry/sr' 1048576
b'\x08\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\t\x00\x00\x00\n\x00\x00\x00\x0b' 1049616
b'\x08\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\t\x00\x00\x00\n\x00\x00\x00\x0b' 1049616
b'\x00\x00\x10\x00h\x00\x00\x00=\x02\x00\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00' 1048680
b'called `Result::unwrap()` on an ' 1048900
b'\x03\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x1c\x02\x10\x00\n\x00\x00\x00\x15\x00\x00\x00.\x00\x00\x00' 1049172
b"\x1c\x02\x10\x00\n\x00\x00\x00\x15\x00\x00\x00.\x00\x00\x00Couldn't copy bu" 1049188
b'\x88\x00\x10\x00h\x00\x00\x00^\x02\x00\x00\x1c\x00\x00\x00\x88\x00\x10\x00h\x00\x00\x00k\x02\x00\x002\x00\x00\x00' 1048816
b'\x88\x00\x10\x00h\x00\x00\x00k\x02\x00\x002\x00\x00\x00\x88\x00\x10\x00h\x00\x00\x00\x01\x03\x00\x00\x15\x00\x00\x00' 1048832
b'\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00/Users/msanft/.c' 1048696
b'\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00/Users/msanft/.c' 1048696
b'\x88\x00\x10\x00h\x00\x00\x00\x01\x03\x00\x00\x15\x00\x00\x00StreamCipherErro' 1048848
b'2-byte kcalled `Result::unwrap()' 1048892
b'expand 32-byte kcalled `Result::' 1048884
b'Mq\xac\x14v\xb1\x9al\xaa\xec\x86qA\x12\xf3\xbfkr\xf0\xa5kz\xfc\xd1\x1aR\x9c\xd3\xba\x00\x00\x00' 1048943
b'yasss!!\xf0\x9f\x98\x8c\xf0\x9f\x92\x85\x00\x00\x03\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00' 1049155
b'nah\xf0\x9f\x98\x90\xf0\x9f\x98\x90yasss!!\xf0\x9f\x98\x8c\xf0\x9f\x92\x85\x00\x00\x03\x00\x00\x00' 1049144
b'\x18\x03\x10\x00\x1c\x00\x00\x00\x9c\x02\x10\x00j\x00\x00\x00^\x01\x00\x00\x11\x00\x00\x00regex: t' 1049396
b'\x9c\x02\x10\x00j\x00\x00\x00^\x01\x00\x00\x11\x00\x00\x00regex: thread ID' 1049404
b'called `Result::unwrap()` on an ' 1048900
b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00/Users/msanft/.c' 1048972
b"\x9c\x01\x10\x00]\x00\x00\x00x\x00\x00\x00'\x00\x00\x00flag\\{([^{}]*)\\}" 1049084
b'L\x03\x10\x00+\x00\x00\x00/Users/msanft/.cargo/reg' 1049464
b'\x80\x03\x10\x00h\x00\x00\x00^\x01\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x07\x00\x00\x00' 1049576
b'StreamCipherError\x00\x00\x00expand 32-by' 1048864
b'\x08\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\t\x00\x00\x00\n\x00\x00\x00\x0b' 1049616
b'Error\x00\x00\x00\x08\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\t\x00\x00\x00\n\x00\x00\x00\x0b' 1049608

这里为两个checker的结果输出字符串的引用:

b'yasss!!\xf0\x9f\x98\x8c\xf0\x9f\x92\x85\x00\x00\x03\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00' 1049155
b'nah\xf0\x9f\x98\x90\xf0\x9f\x98\x90yasss!!\xf0\x9f\x98\x8c\xf0\x9f\x92\x85\x00\x00\x03\x00\x00\x00' 1049144

“expand 32-byte”为slasa20流密码家族中magic number:

b'2-byte kcalled `Result::unwrap()' 1048892
b'expand 32-byte kcalled `Result::' 1048884

五、wasm逆向-算法辨析

因为wasm程序不管怎么转化,始终都是‘汇编’的形式,又臭又长,所以我们的逆向还是要回归到这些汇编细节上,所以我们必须要了解这些算法在汇编层次上的不同

根据‘expand 32-byte k’我们知道了程序使用了salsa20家族中的某一个流密码算法

salsa20 流密码家族包括:

◆Salsa20

  • Xsalsa20

◆chacha20

  • Xchacha20

Xsalsa20 是Salsa20的改进,chacha20是Salsa20的变种,同理Xchacha20是在chacha20基础上的改进。

Salsa20与chacha20的区别:

1.初始矩阵为64字节的4*4的32bits数组,分为4个部分:

◆Salsa20

"expa"KeyKeyKey
Key"nd 3"NonceNonce
CounterCounter"2-by"Key
KeyKeyKey"te k"

◆chacha20

"expa""nd 3""2-by""te k"
KeyKeyKeyKey
KeyKeyKeyKey
CounterNonceNonceNonce

1.ARX (ADD-Rotate-Xor) operations

salsa20 与 chacha20都基于ARX,包括32 bit 的加法,异或,旋转。

在代码上体现为QR()函数:

◆salsa20-QR(a, b, c, d)

b ^= (a + d) <<<  7;
c ^= (b + a) <<< 9;
d ^= (c + b) <<< 13;
a ^= (d + c) <<< 18;

◆chacha20-QR(a, b, c, d)

a += b; d ^= a; d <<<= 16;
c += d; b ^= c; b <<<= 12;
a += b; d ^= a; d <<<= 8;
c += d; b ^= c; b <<<= 7;

1.偶数轮与奇数轮

salsa20与chacha20都会进行20轮QR(),其中10轮偶数轮,10奇数轮。

◆salsa20与chacha20都会在偶数轮处理列

◆在salsa20中偶数轮会处理每一行

◆在chacha20中奇数轮会处理每个对角线

其中具体参与运算的块也不尽相同,具体如下:

salsa20:

// Odd round
QR( 0, 4, 8, 12) // column 1
QR( 5, 9, 13, 1) // column 2
QR(10, 14, 2, 6) // column 3
QR(15, 3, 7, 11) // column 4
// Even round
QR( 0, 1, 2, 3) // row 1
QR( 5, 6, 7, 4) // row 2
QR(10, 11, 8, 9) // row 3
QR(15, 12, 13, 14) // row 4

chacha20:

// Odd round
QR(0, 4, 8, 12) // column 1
QR(1, 5, 9, 13) // column 2
QR(2, 6, 10, 14) // column 3
QR(3, 7, 11, 15) // column 4
// Even round
QR(0, 5, 10, 15) // diagonal 1 (main diagonal)
QR(1, 6, 11, 12) // diagonal 2
QR(2, 7, 8, 13) // diagonal 3
QR(3, 4, 9, 14) // diagonal 4

最后体现在代码上(C语言):

//salsa20
#include <stdint.h>
#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
#define QR(a, b, c, d)( \
b ^= ROTL(a + d, 7), \
c ^= ROTL(b + a, 9), \
d ^= ROTL(c + b,13), \
a ^= ROTL(d + c,18))
#define ROUNDS 20

void salsa20_block(uint32_t out[16], uint32_t const in[16])
{
int i;
uint32_t x[16];

for (i = 0; i < 16; ++i)
x[i] = in[i];
// 10 loops × 2 rounds/loop = 20 rounds
for (i = 0; i < ROUNDS; i += 2) {
// Odd round
QR(x[ 0], x[ 4], x[ 8], x[12]); // column 1
QR(x[ 5], x[ 9], x[13], x[ 1]); // column 2
QR(x[10], x[14], x[ 2], x[ 6]); // column 3
QR(x[15], x[ 3], x[ 7], x[11]); // column 4
// Even round
QR(x[ 0], x[ 1], x[ 2], x[ 3]); // row 1
QR(x[ 5], x[ 6], x[ 7], x[ 4]); // row 2
QR(x[10], x[11], x[ 8], x[ 9]); // row 3
QR(x[15], x[12], x[13], x[14]); // row 4
}
for (i = 0; i < 16; ++i)
out[i] = x[i] + in[i];
}

//chacha20
#include<stdint.h>#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
#define QR(a, b, c, d) ( \
a += b, d ^= a, d = ROTL(d, 16), \
c += d, b ^= c, b = ROTL(b, 12), \
a += b, d ^= a, d = ROTL(d, 8), \
c += d, b ^= c, b = ROTL(b, 7))
#define ROUNDS 20

void chacha_block(uint32_t out[16], uint32_tconst in[16])
{
int i;
uint32_t x[16];

for (i = 0; i < 16; ++i)
x[i] = in[i];
// 10 loops × 2 rounds/loop = 20 rounds
for (i = 0; i < ROUNDS; i += 2) {
// Odd round
QR(x[0], x[4], x[ 8], x[12]);// column 1
QR(x[1], x[5], x[ 9], x[13]);// column 2
QR(x[2], x[6], x[10], x[14]);// column 3
QR(x[3], x[7], x[11], x[15]);// column 4
// Even round
QR(x[0], x[5], x[10], x[15]);// diagonal 1 (main diagonal)
QR(x[1], x[6], x[11], x[12]);// diagonal 2
QR(x[2], x[7], x[ 8], x[13]);// diagonal 3
QR(x[3], x[4], x[ 9], x[14]);// diagonal 4
}
for (i = 0; i < 16; ++i)
out[i] = x[i] + in[i];
}

slasa20 与 Xslasa20的区别:

具体参考了这篇论文:https://cr.yp.to/snuffle/xsalsa-20110204.pdf

1.初始状态矩阵不同:

0123
4567
891011
12131415

Xslasa20在slasa20的基础上使用192bit的nonce,其中x6,x7,x8,x9为nonce,其他与slasa20相同。

1.算法不同

你可能注意到counter部分消失了,流密码没有counter怎么能行,其实这部分在Hslasa20实现了。

HSlasa20是XSalsa20独有的部分,并在Xsalsa20中首先运行,来产生额外的64bits nonce和64bits counter。

Hslasa20根据Xslasa20的初始矩阵,产生256 bits的数据(z0,z1,...,z15)

为什么是256bits?因为根据安全考虑,hslasa20会任意的丢弃一般的256bits,相关细节请参考上面的论文。

如在论文的例子里丢弃了(z0,z5,z10,z15,z6,z7,z8,z9).Xslasa20随后根据(z0,z1,...,z15) 补充额外的64bits nonce和64bits counter(摘抄自论文):

◆(x′0,x′5,x′10,x′15) is the Salsa20 constant

◆(x′1,x′2,x′3,x′4,x′11,x′12,x′13,x′14) = (z0,z5,z10,z15,z6,z7,z8,z9)

◆(x′6,x′7) is the last 64 bits of the 192-bit nonce

◆(x′8,x′9) is a 64-bit block counter

随后便是和slasa20一样的流程。

chacha20 与 Xchacha20 区别

原理与slasa20与Xslasa20相同,不多赘述

小结

看slasa20与chacha20,就看初始矩阵‘expend 32-byte k’是对角线填充还是一行填充。

看slasa20与Xslasa20,chacha20与Xchacha20,就看初始矩阵nonce的填充长度。

六、wasm逆向-算法识别

定位constants填充的位置:

b'2-byte kcalled `Result::unwrap()' 1048892
b'expand 32-byte kcalled `Result::' 1048884

前8字节:

var_i0 = var_p3;
var_i1 = 1048884u;
var_j1 = i64_load(&instance->w2c_memory, (u64)(var_i1));
var_l808 = var_j1;
i64_store(&instance->w2c_memory, (u64)(var_i0) + 288, var_j1);
var_i0 = 0u;
var_p2 = var_i0;
var_i0 = var_p3;
var_i1 = 296u;
var_i0 += var_i1;
var_i1 = 1048892u;
var_j1 = i64_load(&instance->w2c_memory, (u64)(var_i1));
var_l808 = var_j1;
i64_store(&instance->w2c_memory, (u64)(var_i0), var_j1);

计算前后8字节的位置的距离:296-288 = 8。可以发现地址是连续的8字节,这说明是按行填充,那么使用的算法是chacha20。

当然在wasm2wat转化的dcmp文件中也可以找到chacha20.rs的信息,但是藏在巨量的字符串里,只能尝试搜索试一试,或者做一个验证也可。

如法炮仗,找到key填充的部分,按照8字节填充规律,key为32字节,需填充四次,地址应为304,312,320,328。

果然找到了key的填充部分:

var_i0 = var_p3;
var_i0 = i32_load(&instance->w2c_memory, (u64)(var_i0) + 4u);
var_p0 = var_i0;
var_i0 = var_p3;
var_i0 = i32_load(&instance->w2c_memory, (u64)(var_i0));
var_p4 = var_i0;
var_i0 = var_p3;
var_i1 = 304u;
var_i0 += var_i1;
var_j1 = 1374463283923456787ull;
i64_store(&instance->w2c_memory, (u64)(var_i0), var_j1);
var_i0 = var_p3;
var_i1 = 312u;
var_i0 += var_i1;
var_j1 = 1374463283923456787ull;
i64_store(&instance->w2c_memory, (u64)(var_i0), var_j1);
var_i0 = var_p3;
var_i1 = 320u;
var_i0 += var_i1;
var_j1 = 1374463283923456787ull;
i64_store(&instance->w2c_memory, (u64)(var_i0), var_j1);
var_i0 = var_p3;
var_i1 = 328u;
var_i0 += var_i1;
var_j1 = 1374463283923456787ull;
i64_store(&instance->w2c_memory, (u64)(var_i0), var_j1);

hex(1374463283923456787) = ‘0x13’ * 8 key即为‘0x13’ * 32

如法炮制找到nonce的位置:

vvar_i0 = var_p3;
var_i1 = 128u;
var_i0 += var_i1;
var_p1 = var_i0;
var_i1 = 926365495u; // 0x37373737 last 4bytes nonce
i32_store(&instance->w2c_memory, (u64)(var_i0), var_i1);
var_i0 = var_p3;
var_i1 = 160u;
var_i0 += var_i1;
var_j1 = var_l808;
i64_store(&instance->w2c_memory, (u64)(var_i0), var_j1);
var_i0 = var_p3;
var_i1 = 168u;
var_i0 += var_i1;
var_j1 = 1374463283923456787ull;
i64_store(&instance->w2c_memory, (u64)(var_i0), var_j1); // maybe matrix copy
var_i0 = var_p3;
var_i1 = 176u;
var_i0 += var_i1;
var_j1 = 1374463283923456787ull;
i64_store(&instance->w2c_memory, (u64)(var_i0), var_j1);
var_i0 = var_p3;
var_i1 = 184u;
var_i0 += var_i1;
var_j1 = 1374463283923456787ull;
i64_store(&instance->w2c_memory, (u64)(var_i0), var_j1);
var_i0 = var_p3;
var_i1 = 192u;
var_i0 += var_i1;
var_j1 = 1374463283923456787ull;
i64_store(&instance->w2c_memory, (u64)(var_i0), var_j1);
var_i0 = var_p3;
var_i1 = 1048884u;
var_j1 = i64_load(&instance->w2c_memory, (u64)(var_i1));
var_l808 = var_j1;
i64_store(&instance->w2c_memory, (u64)(var_i0) + 288, var_j1);
var_i0 = var_p3;
var_j1 = 3978709506094217015ull; //0x3737373737373737 first 8bytes nonce
i64_store(&instance->w2c_memory, (u64)(var_i0) + 120, var_j1);

nonce 为 ‘0x37’ * 12

密文定位:

b'Mq\xac\x14v\xb1\x9al\xaa\xec\x86qA\x12\xf3\xbfkr\xf0\xa5kz\xfc\xd1\x1aR\x9c\xd3\xba\x00\x00\x00' 1048943

该引用十分可疑 起出现在yasss!!!和nah前面 密文的可能性很高。

尝试解密:

from Crypto.Cipher import ChaCha20
from Crypto.Random import get_random_bytes

def chacha20_decrypt(key, nonce, ciphertext):
cipher = ChaCha20.new(key=key, nonce=nonce)
plaintext = cipher.decrypt(ciphertext)
return plaintext

key = b'\x13' * 32
nonce = b'\x37' * 12

ciphertext = b"Mq\xac\x14v\xb1\x9al\xaa\xec\x86qA\x12\xf3\xbfkr\xf0\xa5kz\xfc\xd1\x1aR\x9c\xd3\xba"

decrypted_text = chacha20_decrypt(key, nonce, ciphertext)
print("Decrypted text:", decrypted_text)#Decrypted text: b'H4rfBuzZ_g0t_m3_1n70_5h4P3_!!'

放入前面提到的程序中,验证成功,此题得解!

先说题目,这是一道创新性极高,难度极高,同时趣味性极高的题目。首先是ttf文件,里面竟然可以暗藏wasm,通过调研这是harfbuzz的wasm shaper,在harfbuzz-wasm-examples学习到tff文件也可以变着花样甚至可以实现一个计算器,在本题中除了是一个正常的字体以外,竟然还是一个flag checker,对所有符合flag{}正则的字符串,输出正误结果,真是太妙了。

再说我的做题过程,大部分时间花在了如何想法让wasm好分析,最后折腾下来还是老老实实的分析又臭又长的跟汇编没区别的c语言,但是在这个过程,我总结出逆向wasm的一个方法:

1.寻找对关键关键缓冲区的引用,来寻找关键逻辑的蛛丝马迹。因为通过其他语言编译成wasm,表现在wasm中总是在关键逻辑上加上大量的看似无关的代码,人工分析工作量太大

2.关键函数

◆查看wasm2c的转化规则可以发现,自己实现的函数总是带着w2c_的前缀,而wasm_前缀的函数为wasm2*的运行时支持函数。

◆在w2c_中毫无命名规则的函数通常与主逻辑无关,那么如何在海量的无关函数中找到有用函数,可以在010editor中查看wasm的export section,如本题核心函数就是shape,因为本质上rust要编译成wasm,肯定会在主要函数是实现#[wasm_bindgen]的attribute,实现这个attribute的目的就是让这个函数导出让wasm调用到。

在解这道题的时候我也试着自己写一个rust程序编译成wasm,然后反编译出来找找异同点,来给这道题找思路。

我的核心lib.rs:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
pub fn alert(s: &str);
}

#[wasm_bindgen(start)]
pub fn greet() {
alert(&format!("Hello, {}!", "123"));
}

可以看到这里我和官方教程所不同的是 我对greet函数的实现了#[wasm_bindgen(start)]而不是#[wasm_bindgen]。加了start可以起到什么作用?

在生成的wasm文件中可以看到__wbindgen_start()中除了默认的call __wbindgen_init_externref_table() , 还增加了 call greet()

编写html调用它:

<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<title>hello-wasm example</title>
</head>
<body>
<script type="module">
import init, { greet } from "./pkg/hello_wasm.js";
init().then(() => {
// greet("WebAssembly");
});
</script>
</body>
</html>

python起一个http,浏览器访问:

发现直接就调用了greet()

原理

跟进init()发现调用了 __wbg_init()

async function __wbg_init(module_or_path) {
if (wasm !== undefined) return wasm;

if (typeof module_or_path !== 'undefined') {
if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
({module_or_path} = module_or_path)
} else {
console.warn('using deprecated parameters for the initialization function; pass a single object instead')
}
}

if (typeof module_or_path === 'undefined') {
module_or_path = new URL('hello_wasm_bg.wasm', import.meta.url);
}
const imports = __wbg_get_imports();

if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
module_or_path = fetch(module_or_path);
}

__wbg_init_memory(imports);

const { instance, module } = await __wbg_load(await module_or_path, imports);

return __wbg_finalize_init(instance, module);
}

__wbg_init()在最后调用了_wbg_finalize_init()

function __wbg_finalize_init(instance, module) {
wasm = instance.exports;
__wbg_init.__wbindgen_wasm_module = module;
cachedUint8ArrayMemory0 = null;

wasm.__wbindgen_start();
return wasm;
}

可以发现__wbg_finalize_init()调用了__wbindgen_start(),而__wbindgen_start()将调用到我们greet(),最后返回完成初始化。这就跟c语言中的init_array一样。

那么在这里我们可以通过这个功能实现一些hook,反调试,加密,解密,混淆等等。

看雪ID:SleepAlone

https://bbs.kanxue.com/user-home-950548.htm

*本文为看雪论坛精华文章,由 SleepAlone 原创,转载请注明来自看雪社区

# 往期推荐

1、PWN入门:整数溢出

2、野蛮fuzz:持久性fuzz

3、修改PE导入表注入DLL——实例图文教程

4、【入门篇】Android漏洞挖掘,实战演示挖掘技巧

5、野蛮fuzz:深入了解代码覆盖率

球分享

球点赞

球在看

点击阅读原文查看更多


文章来源: https://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458583634&idx=2&sn=fce67386bdfb35038ce0aa2c45f07cdd&chksm=b18c32d886fbbbce4b492c95ac9aaf2a8757219b61ac6d2fd3cf02d6d20bf3ef69c158b4d0fb&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh