hack.lu 2024 Getting into Shape wp

写在前面

  1. 本篇文章详细讨论wasm逆向,针对wasm2c wasm2js (wasm2wat 更是没法看)量大 代码多 且市面上没有出色wasm反编译引擎(JEB 也无济于事),我们如何海量代码中找到核心逻辑,相信看完本篇文章你会获得一些思路。
  2. 本篇文章详细讨论了Salsa20 家族流密码的区别,以及在汇编(WebAssembly)的特征,相信你看完本篇文章,下次再遇到同种的加密算法,可以快速反应。
  3. 最后,算是笔者的一些小探索,探讨rust通过wasm_bindgen编译wasm,一些有趣的小机制,算是一个彩蛋,如果你感兴趣,兴许可以出个有趣的小题目或者实现一个神秘的小功能。

0x00 题目背景

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

flag错时:

QQ_1729844759401.png

flag对时:

QQ_1729844808497.png

0x01 wasm提取

首先将ttf文件放到010 editor中观察

在ttf最后一个成员gasp 后往下的一部分 出现asm(wasm的magic number)

QQ_1729845086118.png

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

使用python脚本提取:

1
2
3
4
5
6
7
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

image.png

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

QQ_1729845841076.png

当然也可以自己编译一个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)]

0x02 wasm文件处理

对于wasm的文件处理可以使用wasm2c或者wasm2js

这里使用wasm2c 对wasm文件进行处理

实际不管使用wasm2c 还是 wasm2js, 代码形式和汇编差不多,并没有多少简化

wasm2c反编译出来的数据是16进制显示,wasm2js是base64编码,这对于我们分析字符串都不方便,这里使用wasm2wat查看dcmp文件中的字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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运行结果的程序

0x03 wasm 逆向-寻找线索

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

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

在wasm2c该缓冲区为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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,
};

加载位置:

1
2
3
4
5
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处所有的引用进行提取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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)

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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的结果输出字符串的引用

1
2
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

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

0x04 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个部分:

    • 4个constants块(都是‘expa’ , ‘nd 3’ , ‘2-by’ , ‘te k’)
    • 8个key块(也就是指定的密钥)
    • 1/2个conter块(Salsa20 2块 64bits / chacha20 1块 32bits)

    并且具体的分布也不同:

  • 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)
1
2
3
4
b ^= (a + d) <<<  7;
c ^= (b + a) <<<  9;
d ^= (c + b) <<< 13;
a ^= (d + c) <<< 18;
  • chacha20-QR(a, b, c, d)
1
2
3
4
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:

1
2
3
4
5
6
7
8
9
10
// Odd round
QR( 048, 12) // column 1
QR( 59, 131) // column 2
QR(10, 1426) // column 3
QR(1537, 11) // column 4
// Even round
QR( 0123) // row 1
QR( 5674) // row 2
QR(10, 1189) // row 3
QR(15, 12, 13, 14) // row 4

chacha20:

1
2
3
4
5
6
7
8
9
10
// Odd round
QR(0, 48, 12) // column 1
QR(1, 59, 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, 78, 13) // diagonal 3
QR(3, 49, 14) // diagonal 4

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//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];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//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. 初始状态矩阵不同:

    初始状态矩阵x:

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与chacha20,就看初始矩阵‘expend 32-byte k’是对角线填充还是一行填充

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

0x05 wasm逆向-算法识别

定位constants填充的位置:

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

前8字节

1
2
3
4
5
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字节

1
2
3
4
5
6
7
8
9
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的填充部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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

密文定位:

1
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前面 密文的可能性很高

尝试解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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中总是在关键逻辑上加上大量的看似无关的代码,人工分析工作量太大

    什么是关键是关键缓冲区?

    我理解为和程序逻辑相关的缓冲区,这里一般为通过wasm2*出来文件中的第一个缓冲区(经验)。通过我自己实验,通过wasm-pack把rust编译成wasm,事实也是如此。

  2. 关键函数

  • 查看wasm2c的转化规则可以发现,自己实现的函数总是带着w2c_的前缀,而wasm_前缀的函数为wasm2*的运行时支持函数。
  • 在w2c_中毫无命名规则的函数通常与主逻辑无关,那么如何在海量的无关函数中找到有用函数,可以在010editor中查看wasm的export section,如本题核心函数就是shape,因为本质上rust要编译成wasm,肯定会在主要函数是实现#[wasm_bindgen]的attribute,实现这个attribute的目的就是让这个函数导出让wasm调用到。

image.png

写在最后

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

我的核心lib.rs:

1
2
3
4
5
6
7
8
9
10
11
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()

image.png

编写html调用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!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,浏览器访问:

image.png

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

就写到这里,感谢你的耐心阅读,我们下次再见

最新回复:
smallzhong_ 2024-10-28 10:55:52
前排支持
SleepAlone 2024-10-28 11:02:10
smallzhong_ 前排支持[em_71]

谢谢大佬!

smallzhong_ 2024-10-28 11:04:01
SleepAlone smallzhong_ 前排支持[em_71] 谢谢大佬!
草(一种植物)
touful 2024-10-30 11:08:07
NB
墨穹呢 2024-10-30 11:19:32
感谢分享
卢本伟牪犇逼 2024-11-22 10:49:28
太强了