RE

两个字的开放世界游戏

beam文件的逆向,没见过,应该考信息搜集能力了
搜索一下可以了解到.beam是erlang编译过后的字节码文件
先大致学一下 erlang
大致了解后,上 erlang官网下载一下,配置一下环境。
再搜一下 .beam 的反汇编
找到如下提取字节码命令。

1
io:format("~p~n",[beam_disasm:file("genshin.beam")]).

2023-11-26T120602
得到对应的字节码信息

Text
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
{beam_file,genshin,
[{main,1,2},{module_info,0,11},{module_info,1,13}],
[{vsn,[294325075297162417208450880208266398775]}],
[{version,"8.3.1"},
{options,[]},
{source,
[47,109,110,116,47,100,47,67,84,70,47,20986,39064,47,50,48,50,51,45,
56,45,100,117,105,110,101,105,115,97,105,47,103,101,110,115,104,105,
110,46,101,114,108]}],
[{function,main,1,2,
[{label,1},
{line,1},
{func_info,{atom,genshin},{atom,main},1},
{label,2},
{test,is_nonempty_list,{f,3},[{x,0}]},
{get_list,{x,0},{x,1},{x,0}},
{test,is_nil,{f,3},[{x,0}]},
{move,{x,1},{x,0}},
{call_only,1,{genshin,check,1}},
{label,3},
{move,
{literal,"Usage: xxxxxxxx genshin.beam <input_string>~n"},
{x,0}},
{line,2},
{call_ext_only,1,{extfunc,io,format,1}}]},
{function,check,1,5,
[{line,3},
{label,4},
{func_info,{atom,genshin},{atom,check},1},
{label,5},
{test,is_list,{f,4},[{x,0}]},
{allocate,1,1},
{move,{x,0},{y,0}},
{move,{literal,[21407,31070,46,46,46,126,110]},{x,0}},
{line,4},
{call_ext,1,{extfunc,io,format,1}},
{move,{integer,1},{x,1}},
{move,nil,{x,2}},
{move,{y,0},{x,0}},
{init_yregs,{list,[{y,0}]}},
{line,5},
{call,3,{genshin,transform,3}},
{line,6},
{call_ext,1,{extfunc,erlang,list_to_binary,1}},
{test,is_eq_exact,
{f,6},
[{tr,{x,0},{t_bitstring,8,false}},
{literal,
<<107,114,102,103,130,68,118,106,107,119,88,109,131,70,
114,130,122,81,111,40,107,77,76,38,52,73,72,101>>}]},
{move,{literal,[21551,21160,65281,126,110]},{x,0}},
{line,7},
{call_ext_last,1,{extfunc,io,format,1},1},
{label,6},
{move,{literal,[20851,38381,126,110]},{x,0}},
{line,8},
{call_ext_last,1,{extfunc,io,format,1},1}]},
{function,transform,3,8,
[{line,9},
{label,7},
{func_info,{atom,genshin},{atom,transform},3},
{label,8},
{test,is_nonempty_list,{f,9},[{x,0}]},
{get_list,{x,0},{x,3},{x,0}},
{line,10},
{gc_bif,'bxor',
{f,0},
4,
[{x,3},{tr,{x,1},{t_integer,{1,'+inf'}}}],
{x,3}},
{gc_bif,'+',{f,0},4,[{tr,{x,3},{t_integer,any}},{integer,4}],{x,3}},
{line,11},
{gc_bif,'+',
{f,0},
4,
[{tr,{x,1},{t_integer,{1,'+inf'}}},{integer,1}],
{x,1}},
{test_heap,2,4},
{put_list,{x,3},{x,2},{x,2}},
{call_only,3,{genshin,transform,3}},
{label,9},
{test,is_nil,{f,7},[{x,0}]},
{move,{x,2},{x,0}},
{line,9},
{call_ext_only,1,{extfunc,lists,reverse,1}}]},
{function,module_info,0,11,
[{line,0},
{label,10},
{func_info,{atom,genshin},{atom,module_info},0},
{label,11},
{move,{atom,genshin},{x,0}},
{call_ext_only,1,{extfunc,erlang,get_module_info,1}}]},
{function,module_info,1,13,
[{line,0},
{label,12},
{func_info,{atom,genshin},{atom,module_info},1},
{label,13},
{move,{x,0},{x,1}},
{move,{atom,genshin},{x,0}},
{call_ext_only,2,{extfunc,erlang,get_module_info,2}}]}]}

看起来有点长,大致能猜到意思
可以看到 main 里面调用了 check 来检验
function check 调用了transform,并且有一个{test,is_eq_exact
那么后面的<<107,114,102,103,130,68,118,106,107,119,88,109,131,70,114,130,122,81,111,40,107,77,76,38,52,73,72,101>>应该就是密文了。
观察一下function transform
应该是每一位和下标异或后加上4
据此写脚本梭
最后是可以用escript 命令将 .beam 文件以脚本形式运行来验证
2023-11-26T181014

exp

1
2
3
4
5
6
7
8
enc = [107,114,102,103,130,68,118,106,107,119,88,109,131,70,114,130,122,81,111,40,107,77,76,38,52,73,72,101]
flag = ''
enc = [v - 4 ^ (i+1) for i,v in enumerate((enc))]
print(enc)
for i in enc:
flag += chr(i)
print(flag)
# flag{Funny_erLang_x0r__:)__}

flag

flag{Funny_erLang_x0r__:)__}

主函数分析失败,看看汇编
看到一堆 jz jnz花指令,手动patch
或者 idapython patch 一下

1
2
3
4
5
6
7
start = 0x401530
end = 0x401AD8
while (start < end):
if ((get_wide_byte(start) == 0x74) and (get_wide_byte(start + 2) == 0x75)):
ida_bytes.patch_byte(start + 4, 0x90)
start += 1
print('OK!')

看主函数,非常简单,v7应该是key,经 sub_40188B 加密后与 Str2 比较
2023-11-27T123839
阅读一下 sub_4016C1 和 sub_40188B
2023-11-27T124239
2023-11-27T124303
应该能看出来是 Blowfish或者 Findcrypt/Signsrch 一下
2023-11-27T124453
2023-11-27T124511
解 Blowfish 就行

exp

1
2
3
4
5
6
7
8
9
10
11
from Crypto.Cipher import Blowfish
enc = [0xFC, 0xD6, 0x82, 0x33, 0x86, 0x04, 0x1C, 0xB0, 0x98, 0x94, 0x36, 0xBB, 0x1F, 0x65, 0x62, 0x99, 0x4E, 0x65, 0x2F, 0x7E, 0xE0, 0x60, 0x67, 0x4B, 0x1B, 0xBA, 0x12, 0xB1, 0x54, 0x86, 0x29, 0xE2, 0xC8, 0x3A, 0xD2, 0x2B, 0xFC, 0xD4, 0x59, 0x44, 0x59, 0x7C, 0x48, 0x33, 0xBD, 0x17, 0x39, 0x47]
ciphertext = bytes(enc)
key = b"NewStar"
# iv =
# print(ciphertext)
cipher = Blowfish.new(key, Blowfish.MODE_ECB)
# 解密
decrypted_text = cipher.decrypt(ciphertext).decode()
print("Decrypted Text:", decrypted_text)
# Decrypted Text: flag{YouGotit!!Yougoit!!!!TheFifthPZGALAXYLEVEL}

flag

flag{YouGotit!!Yougoit!!!!TheFifthPZGALAXYLEVEL}

easy_js2

flagflag{加密算法_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
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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>加解密示例</title>
<script>
function encryptText() {
var plainText = document.getElementById("plaintext").value;
var encryptedText = window.encrypt(plainText);
document.getElementById("encryptedtext").value = encryptedText;
}

function decryptText() {
var cipherText = document.getElementById("encryptedtext").value;
var decryptedText = window.decrypt(cipherText);
document.getElementById("plaintext").value = decryptedText;
}
</script>
<style>
body {
font-family: Arial, sans-serif; /* 设置字体 */
margin: 0; /* 清除默认边距 */
padding: 20px; /* 添加内边距 */
}

h1 {
text-align: center; /* 居中标题 */
}

label {
display: block; /* 将标签显示为块级元素 */
}

input[type="text"] {
width: 100%; /* 设置文本输入框的宽度为100% */
padding: 10px; /* 添加内边距 */
margin-bottom: 10px; /* 添加底部外边距 */
}

button {
padding: 10px 20px; /* 添加内边距 */
margin-bottom: 10px; /* 添加底部外边距 */
}

button:first-of-type {
margin-right: 10px; /* 仅对第一个按钮设置右外边距 */
}

</style>
</head>
<body>
<h1>加解密示例</h1>
<label for="plaintext">明文:</label>
<input type="text" id="plaintext"><br><br>
<button onclick="encryptText()">加密</button><br><br>
<label for="encryptedtext">密文:</label>
<input type="text" id="encryptedtext"><br><br>
<button onclick="decryptText()">解密</button>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("output.wasm"), go.importObject).then((result) => {
go.run(result.instance);
});
</script>
</body>
</html>

跟踪一下第九行的encrypt,加个断点
2023-11-29T230205
F9 后跳转到wasm_exec.js
wasm_exec.js 里没是没有用的东西,后来才知道这是生成wasm时的胶水层代码
继续F9,跳转到output.wasm
看起来有点像 python 的opcode
2023-11-29T230649
翻一下wasm,可以发现有aes标志,猜测是aes加密
2023-11-29T235730
接下来就是寻找key了
我们先了解一下wasm
搜索一下 wasm
找到了Hk_Mayfly大佬的文章Wasm逆向分析

Wasm

WebAssembly缩写为wasm,是基于堆栈的虚拟机指令集。Wasm是一种可移植、大小和加载时间效率都很好的二进制格式,可以在现代Web浏览器中运行,并且可以与JavaScript高效交互。

Wabt

Wabt是一个WebAssembly二进制工具包,包含了wasm-assembler(汇编器)、wasm-disassembler(反汇编器)、wasm-interpreter(解释器)、wasm2c(将wasm转换为C语言)、wasm-opcodecnt(统计wasm指令使用频率)、wasm-validate(验证wasm模块是否有效)等工具。

功能
  • wasm-as:将wast文件转换为wasm文件
  • wasm-dis:将wasm文件转换为wast文件
  • wasm-interp:解释执行wasm文件
  • wasm2c:将wasm文件转换为C语言文件
  • wasm-opcodecnt:统计wasm文件中指令的使用频率
  • wasm-validate:验证wasm文件是否有效
  • wasm2wat:将wasm文件转换为wat文件
  • wat2wasm:将wat文件转换为wasm文件
  • wasm-objdump:反汇编wasm文件
  • wasm-strip:去除wasm文件中的调试信息
  • wasm-reduce:减小wasm文件的大小
  • wasm-decompile:反编译wasm文件
  • wasm2json:将wasm文件转换为json文件
  • json2wasm:将json文件转换为wasm文件
  • wasm-opcode-table:生成wasm指令表
  • wasm-interp-logging:解释执行wasm文件并打印日志
安装
1
2
3
4
5
6
git clone --recursive https://github.com/WebAssembly/wabt
cd wabt
mkdir build
cd build
cmake ..
cmake --build .
wasm2c

另存output.wasm到wasm2c文件夹下

1
2
3
wasm2c -o output.c output.wasm
#或者
wasm2c output.wasm -o output.c

目录下生成了两个文件outtput.c和output.h
此时的output.c并不直观,难以阅读,我们需要对其进行优化
将output.c,output.h和wabt/wasm2c项目内的wasm-rt.h,wasm-rt-impl.c,wasm-rt-impl.h三个文件放到同一个文件夹。
gcc编译output.c
直接gcc wasm.c会报错,因为很多wasm的函数没有具体的实现。但是我们可以只编译不链接,我们关心的只是程序本身的逻辑,不关心函数的实现。

1
gcc -c output.c -o output.o

output.o 用IDA打开,分析就行了
搜索main,在w2c_output_main0x2Eencrypt函数里可以看到调用了
w2c_output_crypto0x2Faes0x2ENewCipher
2023-11-30T230907
再跟踪一下参数就能找到key了newstarTrealstar

flag

flag{aes_newstarTrealstar}

TheLastDance

先看看java层的mainactivity
2023-12-01T135539
没有加密逻辑,只有反调试,
检测到没有调试后会调用native层的thelastdance来进行encode
看看so文件
找到encipher函数
2023-12-01T150402
看到0x9E3779B9,应该是XTEA加密
找一下密文和key
2023-12-01T151631
init_function对key进行了修改
跟踪一下xmmword_EA0
看到密文和key
2023-12-01T151745

exp

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
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
void decrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], delta = 0x9E3779B9, sum = delta * num_rounds;
for (i=0; i < num_rounds; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);

}
v[0] = v0; v[1] = v1;
}
void encrypt(uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
for (i=0; i < 32; i++) {
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
sum += delta;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
}
v[0]=v0; v[1]=v1;
}
int main() {
uint32_t const k[4] = {0x34,0x14,0x16,0x0B};
uint32_t enc[] = {
0xF0CE18A6, 0x800DD010, 0xF6F659BA, 0x41EC6546, 0x60E71616, 0x8F4F4D10, 0x853C1F7F, 0x97B91828, 0x0EDDD0AE, 0xEE5E9E90, 0xAA80FD09, 0x652DBBEC};
unsigned int r = 32;
for(int i=0; i<12; i+=2){
decrypt(r, &enc[i], k);
}
printf("Decrypted data is: %s\n",(char*)enc);
return 0;
}
//Decrypted data is: flag{K1k1_S_1ov3_fr0m_Ch1ck_wh0_1s_w0rk1ng_h@rD}4

flag

flag{K1k1_S_1ov3_fr0m_Ch1ck_wh0_1s_w0rk1ng_h@rD}