Hack.lu 2024 Getting into Shape 解析

admin 2024年11月23日16:50:00评论9 views字数 26335阅读87分47秒阅读模式

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错时:

Hack.lu 2024 Getting into Shape 解析

flag对时:

Hack.lu 2024 Getting into Shape 解析

二、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:

 

Hack.lu 2024 Getting into Shape 解析

 

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

 

Hack.lu 2024 Getting into Shape 解析

 

当然也可以自己编译一个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�0�010�0h�0�0�0=�2�0�01c�0"
  "�0�0�0�0�0�0�4�0�0�0�4�0�0�0�1�0�0�0/Users/msanft/.c"
  "argo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.4."
  "8/src/util/pool.rs88�010�0h�0�0�0^�2�0�01c�0�0�088�010"
  "�0h�0�0�0k�2�0�02�0�0�088�010�0h�0�0�0�1�3�0�015"
  "�0�0�0StreamCipherError�0�0�0expand 32-byte kcalled `Result::unw"
  "rap()` on an `Err` valueMqac14vb19alaaec86qA12f3bfkrf0a5kz"
  "fcd11aR9cd3ba�0�0�0�0�0�0�0�0�1�0�0�0�2�0�0�0/Us"
  "ers/msanft/.cargo/registry/src/index.crates.io-6f17d22bba15001f/cipher"
  "-0.4.4/src/stream.rs�0�0�09c�110�0]�0�0�0x�0�0�0'�0�0�0"
  "flag{([^{}]*)}src/lib.rs�0�01c�210�0�a�0�0�018�0�0�0-�0"
  "�0�0nahf09f9890f09f9890yasss!!f09f988cf09f9285�0�0"
  "�3�0�0�0�c�0�0�0�4�0�0�0�4�0�0�01c�210�0�a�0�0�0"
  "15�0�0�0.�0�0�0Couldn't copy buffer contents�0�0�0t�210�0"
  "1d�0�0�0/Users/msanft/Documents/Documents - Moritze28099s MacBo"
  "ok Pro/harfbuzz-wasm-examples/harfbuzz-wasm/src/lib.rs�0�09c�210�0"
  "j�0�0�0>�1�0�0;�0�0�0Couldn't set buffer contents18�310�0"
  "1c�0�0�09c�210�0j�0�0�0^�1�0�011�0�0�0regex: thread "
  "ID allocation space exhausted�0L�310�0+�0�0�0/Users/msanft/.car"
  "go/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.4.8/"
  "src/util/pool.rs80�310�0h�0�0�0^�1�0�011�0�0�0�0�0�0�0"
  "�4�0�0�0�4�0�0�0�7�0�0�0Error�0�0�0�8�0�0�0�c�0�0"
  "�0�4�0�0�0�9�0�0�0�a�0�0�0�b";

可以看到有关于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]ddd'

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'tx02x10x00x1dx00x00x00/Users/msanft/Documents/' 1049236
b"x9cx02x10x00jx00x00x00>x01x00x00;x00x00x00Couldn't set buf" 1049352
b'flag\{([^{}]*)\}src/lib.rsx00x00x1cx02x10x00' 1049100
b'x00x00x00x00x04x00x00x00x04x00x00x00x07x00x00x00Errorx00x00x00x08x00x00x00x0cx00x00x00' 1049592
b'x00x00x00x00x04x00x00x00x04x00x00x00x07x00x00x00Errorx00x00x00x08x00x00x00x0cx00x00x00' 1049592
b'/Users/msanft/.cargo/registry/sr' 1048712
b'/Users/msanft/.cargo/registry/sr' 1048576
b'/Users/msanft/.cargo/registry/sr' 1048576
b'x08x00x00x00x0cx00x00x00x04x00x00x00tx00x00x00nx00x00x00x0b' 1049616
b'x08x00x00x00x0cx00x00x00x04x00x00x00tx00x00x00nx00x00x00x0b' 1049616
b'x00x00x10x00hx00x00x00=x02x00x00x1cx00x00x00x00x00x00x00x04x00x00x00x04x00x00x00x01x00x00x00' 1048680
b'called `Result::unwrap()` on an ' 1048900
b'x03x00x00x00x0cx00x00x00x04x00x00x00x04x00x00x00x1cx02x10x00nx00x00x00x15x00x00x00.x00x00x00' 1049172
b"x1cx02x10x00nx00x00x00x15x00x00x00.x00x00x00Couldn't copy bu" 1049188
b'x88x00x10x00hx00x00x00^x02x00x00x1cx00x00x00x88x00x10x00hx00x00x00kx02x00x002x00x00x00' 1048816
b'x88x00x10x00hx00x00x00kx02x00x002x00x00x00x88x00x10x00hx00x00x00x01x03x00x00x15x00x00x00' 1048832
b'x00x00x00x00x04x00x00x00x04x00x00x00x01x00x00x00/Users/msanft/.c' 1048696
b'x00x00x00x00x04x00x00x00x04x00x00x00x01x00x00x00/Users/msanft/.c' 1048696
b'x88x00x10x00hx00x00x00x01x03x00x00x15x00x00x00StreamCipherErro' 1048848
b'2-byte kcalled `Result::unwrap()' 1048892
b'expand 32-byte kcalled `Result::' 1048884
b'Mqxacx14vxb1x9alxaaxecx86qAx12xf3xbfkrxf0xa5kzxfcxd1x1aRx9cxd3xbax00x00x00' 1048943
b'yasss!!xf0x9fx98x8cxf0x9fx92x85x00x00x03x00x00x00x0cx00x00x00x04x00x00x00x04x00x00' 1049155
b'nahxf0x9fx98x90xf0x9fx98x90yasss!!xf0x9fx98x8cxf0x9fx92x85x00x00x03x00x00x00' 1049144
b'x18x03x10x00x1cx00x00x00x9cx02x10x00jx00x00x00^x01x00x00x11x00x00x00regex: t' 1049396
b'x9cx02x10x00jx00x00x00^x01x00x00x11x00x00x00regex: thread ID' 1049404
b'called `Result::unwrap()` on an ' 1048900
b'x00x00x00x00x00x00x00x00x01x00x00x00x02x00x00x00/Users/msanft/.c' 1048972
b"x9cx01x10x00]x00x00x00xx00x00x00'x00x00x00flag\{([^{}]*)\}" 1049084
b'Lx03x10x00+x00x00x00/Users/msanft/.cargo/reg' 1049464
b'x80x03x10x00hx00x00x00^x01x00x00x11x00x00x00x00x00x00x00x04x00x00x00x04x00x00x00x07x00x00x00' 1049576
b'StreamCipherErrorx00x00x00expand 32-by' 1048864
b'x08x00x00x00x0cx00x00x00x04x00x00x00tx00x00x00nx00x00x00x0b' 1049616
b'Errorx00x00x00x08x00x00x00x0cx00x00x00x04x00x00x00tx00x00x00nx00x00x00x0b' 1049608

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

b'yasss!!xf0x9fx98x8cxf0x9fx92x85x00x00x03x00x00x00x0cx00x00x00x04x00x00x00x04x00x00' 1049155
b'nahxf0x9fx98x90xf0x9fx98x90yasss!!xf0x9fx98x8cxf0x9fx92x85x00x00x03x00x00x00' 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" Key Key Key
Key "nd 3" Nonce Nonce
Counter Counter "2-by" Key
Key Key Key "te k"

◆chacha20

"expa" "nd 3" "2-by" "te k"
Key Key Key Key
Key Key Key Key
Counter Nonce Nonce Nonce

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.初始状态矩阵不同:

0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15

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);

后8字节:

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'Mqxacx14vxb1x9alxaaxecx86qAx12xf3xbfkrxf0xa5kzxfcxd1x1aRx9cxd3xbax00x00x00' 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"Mqxacx14vxb1x9alxaaxecx86qAx12xf3xbfkrxf0xa5kzxfcxd1x1aRx9cxd3xba"

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调用到。

Hack.lu 2024 Getting into Shape 解析

 

写在最后

在解这道题的时候我也试着自己写一个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()

Hack.lu 2024 Getting into Shape 解析

编写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,浏览器访问:

Hack.lu 2024 Getting into Shape 解析

 

发现直接就调用了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,反调试,加密,解密,混淆等等。

Hack.lu 2024 Getting into Shape 解析

看雪ID:SleepAlone

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

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

# 往期推荐

1、PWN入门:整数溢出

2、野蛮fuzz:持久性fuzz

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

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

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

Hack.lu 2024 Getting into Shape 解析

球分享

Hack.lu 2024 Getting into Shape 解析
球点赞

Hack.lu 2024 Getting into Shape 解析
球在看

 

 

Hack.lu 2024 Getting into Shape 解析
点击阅读原文查看更多

原文始发于微信公众号(看雪学苑):Hack.lu 2024 Getting into Shape 解析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月23日16:50:00
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Hack.lu 2024 Getting into Shape 解析http://cn-sec.com/archives/3421656.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息