(・∀・)つ原题(提取码: d9sa)

初赛

ezre

一个python字节码文件,可以用pycdas查看字节码。我们借此来分析一下python字节码文件的结构。

涉及到闭包函数的内容,总的来说闭包就是函数被定义在了函数里面。

闭包,看这一篇就够了——带你看透闭包的本质,百发百中-CSDN博客

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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
ezRe (Python 3.9)
[Code]
File Name: flag_checker.py #文件信息
Object Name: <module> #表示这是模块的主代码(即文件的最外层代码,不是函数或类)。
Arg Count: 0 #表示模块没有参数。
Pos Only Arg Count: 0 #表示没有仅限位置参数。
KW Only Arg Count: 0 #表示没有仅限关键字参数。
Locals: 0 #表示模块中没有局部变量。
Stack Size: 7 #栈大小
Flags: 0x00000040 (CO_NOFREE) #标志位,CO_NOFREE表示这段代码没有自由变量(即没有闭包)。
[Names] #全局变量、函数和模块的名称列表。
'base64'
'input'
'text'
'key'
'list'
'range'
's'
'j'
'i'
'len'
'data'
'_'
'append'
'result'
'zip'
'c'
'k'
'chr'
'ord'
'b64encode'
'encode'
'decode'
'enc'
'print'
[Var Names] #模块中的局部变量
[Free Vars] #自由变量
[Cell Vars] #闭包变量
[Constants] #常量池
0
None
'Flag: '
'7e021a7dd49e4bd0837e22129682551b' #密钥
[Code] #嵌套代码对象,类似list(ord(i) for i in range(102)),本质就是一个列表所以会显示为常量。同时python也会把这个封装为一个隐式函数,后面调用的时候以函数形式调用
File Name: flag_checker.py #属于的文件
Object Name: <listcomp> #对象的名字,列表推导式
Arg Count: 1 #有一个参数
Pos Only Arg Count: 0 #下面的同上
KW Only Arg Count: 0
Locals: 2
Stack Size: 4
Flags: 0x00000043 (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)
[Names]
'ord'
[Var Names]
'.0'
'i'
[Free Vars]
[Cell Vars]
[Constants]
102
[Disassembly] #字节码指令
0 BUILD_LIST 0 #建立一个列表对象
2 LOAD_FAST 0: .0 #加载参数
4 FOR_ITER 16 (to 22) #迭代器迭代对象
6 STORE_FAST 1: i #存储到i
8 LOAD_GLOBAL 0: ord #加载函数名称
10 LOAD_FAST 1: i #加载变量
12 CALL_FUNCTION 1 #调用函数
14 LOAD_CONST 0: 102 #加载常量
16 BINARY_XOR #异或操作,现在栈顶的两个值是i和102
18 LIST_APPEND 2 #追加结果到列表中,列表保存到栈顶
20 JUMP_ABSOLUTE 4 #返回,继续迭代
22 RETURN_VALUE #返回栈顶值,即列表
'<listcomp>' #列表推导式的名称(函数名)
256
50
1
''
51
'w53Cj3HDgzTCsSM5wrg6FMKcw58Qw7RZSFLCljRxwrxbwrVdw4AEwqMjw7/DkMKTw4/Cv8Onw4NGw7jDmSdcwq4GGg==' #密文
'yes!'
'try again...'
[Disassembly] #字节操作码,
0 LOAD_CONST 0: 0
2 LOAD_CONST 1: None #加载常量
4 JUMP_FORWARD 0 (to 6) #跳转到6
6 JUMP_FORWARD 0 (to 8)
8 JUMP_FORWARD 0 (to 10)
10 IMPORT_NAME 0: base64 #导入模块
12 STORE_NAME 0: base64
14 LOAD_NAME 1: input #获取输入
16 LOAD_CONST 2: 'Flag: '
18 CALL_FUNCTION 1 #调用input函数
20 STORE_NAME 2: text #存储到text
22 LOAD_CONST 3: '7e021a7dd49e4bd0837e22129682551b' #加载密钥
24 STORE_NAME 3: key #存储到key
26 LOAD_CONST 4: <CODE> <listcomp> #加载列表递推式代码
28 LOAD_CONST 5: '<listcomp>' #加载常量,列表递推式的名字
30 MAKE_FUNCTION 0 #以创建函数形式了列表递推式对象
32 LOAD_NAME 3: key #加载key
34 GET_ITER #迭代器,迭代对象key
36 CALL_FUNCTION 1 #调用列表递推式,key被作为迭代对象传入
38 STORE_NAME 3: key #保存返回结果到key
40 LOAD_NAME 4: list #获取函数名list
42 LOAD_NAME 5: range #获取函数名range
44 LOAD_CONST 6: 256 #获取range的值
46 CALL_FUNCTION 1 #调用函数range
48 CALL_FUNCTION 1 #调用函数list
50 STORE_NAME 6: s #把列表存储到s中,s为一个ASCII码表
52 LOAD_CONST 0: 0 #加载0
54 STORE_NAME 7: j #把0赋值给j
56 LOAD_NAME 5: range
58 LOAD_CONST 6: 256
60 CALL_FUNCTION 1 #调用函数range(256)
62 GET_ITER #以range(256)为对象
64 FOR_ITER 62 (to 128) #开始迭代,如果迭代完毕,跳转到128
66 STORE_NAME 8: i #存储迭代的数据
68 LOAD_NAME 7: j
70 LOAD_NAME 6: s
72 LOAD_NAME 8: i
74 BINARY_SUBSCR #下标操作,s[i]
76 BINARY_ADD #栈顶2个数据相加,这里为s[i]+j
78 LOAD_NAME 3: key
80 LOAD_NAME 8: i
82 LOAD_NAME 9: len
84 LOAD_NAME 3: key #调用函数len(key)
86 CALL_FUNCTION 1
88 BINARY_MODULO #对栈顶的两个元素取余,这里是i%len(key)
90 BINARY_SUBSCR #下标操作,现在栈顶为key和i%len(key),即为key[i%len(key)]
92 BINARY_ADD #栈顶两个相加,现在是s[i]+j+key[i%len(key)]
94 LOAD_CONST 6: 256
96 BINARY_MODULO #对栈顶两个元素取余,现在是(s[i]+j+key[i%len(key)])%256
98 STORE_NAME 7: j #保存结果到j中
100 LOAD_NAME 6: s
102 LOAD_NAME 7: j
104 BINARY_SUBSCR #下标操作s[j]
106 LOAD_NAME 6: s
108 LOAD_NAME 8: i
110 BINARY_SUBSCR #下标操作s[i]
112 ROT_TWO #交换栈顶两个元素
114 LOAD_NAME 6: s
116 LOAD_NAME 8: i
118 STORE_SUBSCR #赋值栈顶元素到s[i]
120 LOAD_NAME 6: s
122 LOAD_NAME 7: j
124 STORE_SUBSCR #赋值栈顶元素到s[j],这两步就是交换下标
126 JUMP_ABSOLUTE 64 #迭代继续,跳转回开始位置
128 LOAD_CONST 0: 0
130 DUP_TOP #复制一遍栈顶的值,这里为0
132 STORE_NAME 8: i
134 STORE_NAME 7: j #把栈顶的值复制给i和j,这里是两个0
136 BUILD_LIST 0
138 STORE_NAME 10: data ##建立列表data
140 LOAD_NAME 5: range
142 LOAD_CONST 7: 50
144 CALL_FUNCTION 1
146 GET_ITER
148 FOR_ITER 88 (to 238) #对range(50)进行迭代
150 STORE_NAME 11: _ #迭代值放入_
152 LOAD_NAME 8: i
154 LOAD_CONST 8: 1
156 BINARY_ADD #i+1
158 LOAD_CONST 6: 256
160 BINARY_MODULO #(i+1)%256
162 STORE_NAME 8: i #i=(i+1)%256
164 LOAD_NAME 7: j
166 LOAD_NAME 6: s
168 LOAD_NAME 8: i
170 BINARY_SUBSCR #s[i]
172 BINARY_ADD #s[i]+j
174 LOAD_CONST 6: 256
176 BINARY_MODULO #(s[i]+j)%256
178 STORE_NAME 7: j #j = (s[i]+j)%256
180 LOAD_NAME 6: s
182 LOAD_NAME 7: j
184 BINARY_SUBSCR #s[j]
186 LOAD_NAME 6: s
188 LOAD_NAME 8: i
190 BINARY_SUBSCR #s[i]
192 ROT_TWO
194 LOAD_NAME 6: s
196 LOAD_NAME 8: i
198 STORE_SUBSCR
200 LOAD_NAME 6: s
202 LOAD_NAME 7: j
204 STORE_SUBSCR #交换s[i]和s[j]
206 LOAD_NAME 10: data
208 LOAD_METHOD 12: append
210 LOAD_NAME 6: s
212 LOAD_NAME 6: s
214 LOAD_NAME 8: i
216 BINARY_SUBSCR #s[i]
218 LOAD_NAME 6: s
220 LOAD_NAME 7: j
222 BINARY_SUBSCR #s[j]
224 BINARY_ADD #s[i]+s[j]
226 LOAD_CONST 6: 256
228 BINARY_MODULO #(s[i]+s[j])%256
230 BINARY_SUBSCR #s[(s[i]+s[j])%256]
232 CALL_METHOD 1 #data.append(s[(s[i]+s[j])%256])
234 POP_TOP #清理栈顶
236 JUMP_ABSOLUTE 148 #继续迭代
238 LOAD_CONST 9: ''
240 STORE_NAME 13: result #定义空字符串result
242 LOAD_NAME 14: zip
244 LOAD_NAME 2: text
246 LOAD_NAME 10: data
248 CALL_FUNCTION 2 #调用zip函数有两个参数,就是text和data,放回一个元组,把数据成对成列表放在元组中,例如[(text[0],data[0]),(text[1],data[1]),...]
250 GET_ITER
252 FOR_ITER 32 (to 286) #对元组进行迭代
254 UNPACK_SEQUENCE 2 #解包,提示赋值2个变量
256 STORE_NAME 15: c #赋值数据给c,k
258 STORE_NAME 16: k
260 LOAD_NAME 13: result
262 LOAD_NAME 17: chr
264 LOAD_NAME 18: ord
266 LOAD_NAME 15: c
268 CALL_FUNCTION 1 #ord(c)
270 LOAD_NAME 16: k
272 BINARY_XOR #ord(c) ^ k
274 LOAD_CONST 10: 51
276 BINARY_XOR #ord(c) ^ k ^51
278 CALL_FUNCTION 1 #chr(ord(c) ^ k ^51)
280 INPLACE_ADD #result + chr(ord(c) ^ k ^51)
282 STORE_NAME 13: result #result = result
284 JUMP_ABSOLUTE 252 #继续迭代
286 LOAD_NAME 0: base64
288 LOAD_METHOD 19: b64encode
290 LOAD_NAME 13: result
292 LOAD_METHOD 20: encode
294 CALL_METHOD 0 #result.encode
296 CALL_METHOD 1 #base64.b64encode(result.encode)
298 LOAD_METHOD 21: decode
300 CALL_METHOD 0 #base64.b64encode(result.encode).decode
302 STORE_NAME 22: enc
304 LOAD_NAME 22: enc
306 LOAD_CONST 11: 'w53Cj3HDgzTCsSM5wrg6FMKcw58Qw7RZSFLCljRxwrxbwrVdw4AEwqMjw7/DkMKTw4/Cv8Onw4NGw7jDmSdcwq4GGg=='
308 COMPARE_OP 2 (==) #判断enc是否等于上面的base64.b64encode(result.encode).decode
310 POP_JUMP_IF_FALSE 324
314 LOAD_NAME 23: print
316 LOAD_CONST 12: 'yes!'
318 CALL_FUNCTION 1
320 POP_TOP
322 JUMP_FORWARD 8 (to 332)
324 LOAD_NAME 23: print
326 LOAD_CONST 13: 'try again...'
328 CALL_FUNCTION 1
330 POP_TOP
332 LOAD_CONST 1: None
334 RETURN_VALUE

上面代码的加密解密脚本

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
import base64

def enc():
text = input("flag:")

key = "7e021a7dd49e4bd0837e22129682551b"

key = list(ord(i) ^ 102 for i in key)

s = list(range(256))

j = 0

for i in range(256):
j = (s[i] + j + key[i % len(key)]) % 256
tmp = s[i]
s[i] = s[j]
s[j] = tmp

i = j = 0
data = list()
for _ in range(50):
i = (i + 1) % 256
j = (s[i] + j) % 256
tmp = s[i]
s[i] = s[j]
s[j] = tmp
data.append(s[(s[i] + s[j]) % 256])

result = ""
for c, k in zip(text, data):
result += chr(ord(c) ^ k ^ 51)

result = base64.b64encode(result.encode()).decode()
enc = 'w53Cj3HDgzTCsSM5wrg6FMKcw58Qw7RZSFLCljRxwrxbwrVdw4AEwqMjw7/DkMKTw4/Cv8Onw4NGw7jDmSdcwq4GGg=='

if result == enc:
print("yes!")
else:
print("try again...")



def dec():
enc = 'w53Cj3HDgzTCsSM5wrg6FMKcw58Qw7RZSFLCljRxwrxbwrVdw4AEwqMjw7/DkMKTw4/Cv8Onw4NGw7jDmSdcwq4GGg=='
key = "7e021a7dd49e4bd0837e22129682551b"
text = base64.b64decode(enc.encode()).decode()

key = list(ord(i) ^ 102 for i in key)

s = list(i for i in range(256))

j = 0

for i in range(256):
j = (s[i] + j + key[i % len(key)]) % 256
tmp = s[i]
s[i] = s[j]
s[j] = tmp

i = j = 0
data = list()
for _ in range(50):
i = (i + 1) % 256
j = (s[i] + j) % 256
tmp = s[i]
s[i] = s[j]
s[j] = tmp
data.append(s[(s[i] + s[j]) % 256])

result = ""
for c, k in zip(text, data):
result += chr(ord(c) ^ k ^ 51)

print(result)


dec()


Midre

先去主函数的花

image-20250105153707030

image-20250105153813904

nop完后发现还有问题,我们在下面的代码中按c试探一下,发现从第二个字节码开始就可以恢复为正常函数,所以还要一个垃圾指令,nop掉后即可看到主函数逻辑。

image-20250105154053668

加密函数里面还有花指令,nop掉后即可看到完整代码。

image-20250105154237983

去掉花指令后,可以看到主函数。首先是一个异或”what’s this”,后面是一个加密函数。

image-20250105152745694

在这个加密函数里面有一个比较,可以找到密文。前面有一个函数,像是在按密钥的长度决定加密的轮数,再看加密函数的参数,像极了一个AES加密,且有两个常量’5855eab53a2275d3’和’b051a57d6d05b393’,推测一个是密钥一个是IV。

image-20250105152911268

image-20250105153327594

直接进行解密即可得到结果

image-20250105153250922

决赛

reverse1

两个rc4加密,一个是原版另一个是被修改的版本。加密过程就是用key1先加密key,再用加密的key去加密flag。所以按照这个逻辑来逆向即可解密。rc4原版加密是对称加密,而修改版的rc4只要把减号改为加号就行。

在比赛时这题并没有写出来,脚本最后的结果一直是乱码,后面复盘时发现了一个关键的东西。当盒子是char类型时,只要盒子里的元素被当作下标时,就要把这个下标转成(unsigned char),如果不转的话在后面的下标就是一个负数。当盒子是unsigned char类型时,(box[v5] + box[v6])作为下标时要转化为(unsigned char),如果不转化的话(box[v5] + box[v6])就有可能会超过下标255。所以box[(unsigned char)(box[v5] + box[v6])]实际上就是box[(box[v5] + box[v6])%256]。

  • 所以在写题中一定要注意下标的数值是否合规。
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
#include <stdio.h>
#include <string.h>

char input[32] = {
0x4E, 0x47, 0x38, 0x47, 0x62, 0x0A, 0x79, 0x6A, 0x03, 0x66, 0xC0, 0x69, 0x8D, 0x1C, 0x84, 0x0F,
0x54, 0x4A, 0x3B, 0x08, 0xE3, 0x30, 0x4F, 0xB9, 0x6C, 0xAB, 0x36, 0x24, 0x52, 0x81, 0xCF, 0x00
};
char key[100] = "ban_debug!";
char key1[] = "keykey";
unsigned char box[256];

int getbox(unsigned char*box, char*key,int keylen) {
char v4;
int v6 = 0;
int v8[256];
memset(v8, 0, 0x400uLL);
for (int i = 0; i <= 255; ++i)
{
box[i] = i;
v8[i] = (unsigned char)key[i % keylen];
}
v6 = 0;
for (int j = 0; j <= 255; ++j)
{

v6 = (v8[j] + v6 + box[j]) % 256;
v4 = box[j];
box[j] = box[v6];
box[v6] = v4;
}
return 0;
}


int dec1(unsigned char*box, unsigned char* input,int len) {
char v4;
int v5;
int v6;
int i;

v5 = 0;
v6 = 0;
for (i = 0; ; ++i)
{
if (len <= i)
break;
v5 = (v5 + 1) % 256;
v6 = (v6 + box[v5]) % 256;
v4 = box[v5];
box[v5] = box[v6];
box[v6] = v4;
input[i] ^= box[(unsigned char)(box[v5] + box[v6])];
}
return 0;
}

int dec2(unsigned char* box,char* input, int len) {
char v4;
int v5;
int v6;
int i;

v5 = 0;
v6 = 0;
for (i = 0; ; ++i)
{
if (len <= i)
break;
v5 = (v5 + 1) % 256;
v6 = (v6 + box[v5]) % 256;
v4 = box[v5];
box[v5] = box[v6];
box[v6] = v4;
input[i] += box[(unsigned char)(box[v5] + box[v6])];
}
return 0;
}


int main() {

getbox(box, key1, strlen(key1));
dec1(box, (unsigned char*)key, strlen(key));
memset(box, 0, sizeof(box));
getbox(box, key, strlen(key));
dec2(box, input, 32);
for (int i = 0; i < 31; i++) {
printf("%c", input[i]);
}

//puts(input);

}

reverse2

用010editer把ABC全部修改为UPX,ida分析后就是一个base64加密函数,直接就可以看到明文和码表,直接解密就得flag

reverse3

在main函数下面有个sub_140002E90函数,这个创建了一个子进程,并进行了一些写内存操作,可执行文件就是上面那串字符串,有一个异或0x11加密,解密得到C:\Users\Public\1.exe。

image-20250112112501482

在1.exe的mian函数上面有一个函数。

image-20250112113434343

逐一分析看看,能在sub_140001690函数里面找到一个255大小的盒,但是这个盒是被修改了的不是常规的AES盒,sub_140001380函数像是一个AES的密钥轮函数。继续往main函数上面看,还能看到密钥拓展函数。这就是一个AES无疑了。这时候我们再回头看main函数。

image-20250112113803015

main函数的逻辑大概如下图。有一个加载密钥的函数,一个输入函数,还有密文。这个时候就可以解密了,但是不出所料的解不出来。这里加了反调试,我们调试不了,于是我们用fridahook来获取值。

image-20250112113919083

要hook的数据(手动计算IDA中的地址偏移用于hook,这里是0x0000000140017060后面的0x17060)

1.密钥0x0000000140017060

image-20250112114416287

Snipaste_2025-01-12_03-23-48

2.s盒0x140005160

[!NOTE]

hook盒和密文时,在加密函数使用盒时hook,最开始就hook的话可能在盒被修改前。

image-20250112114456928

Snipaste_2025-01-12_03-22-48

3.密文0x1400152DC

image-20250112114633555

image-20250112222904968

4.输入0x140001E24的rdx

mian函数中我们的输入被保存到了v11中,然后在下面的for循环调用,我们直接获取寄存器的值就行,[rbp+rax+0E0h+var_100]就是v11的数据。

image-20250112114921154

!要把可见字符拆分成两段分别hook,因为输入字符最好要保持在48个以内(小于等于密文长度)

Snipaste_2025-01-12_03-23-04

Snipaste_2025-01-12_03-23-30

对比分析发现我们的输入被改了,单字节加密,就是被加了一点偏移。我们可以打印所有可见字符,构建一个表查找对应查找原来的数据。密钥也被修改了。s盒也被修改了,对比没有魔改的AES盒是相同的,所以AES的魔改又被改回了正常的AES加密,直接用常规解密就行。密文是不变的。我们直接提出来写出解密脚本。以下为c和py的解密脚本和hook脚本。

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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
#include <stdint.h>
#include <stdio.h>
#include <string.h>

typedef struct {
uint32_t eK[44], dK[44]; // encKey, decKey
int Nr; // 10 rounds
}AesKey;

#define BLOCKSIZE 16 //AES-128分组长度为16字节

// uint8_t y[4] -> uint32_t x
#define LOAD32H(x, y) \
do { (x) = ((uint32_t)((y)[0] & 0xff)<<24) | ((uint32_t)((y)[1] & 0xff)<<16) | \
((uint32_t)((y)[2] & 0xff)<<8) | ((uint32_t)((y)[3] & 0xff));} while(0)

// uint32_t x -> uint8_t y[4]
#define STORE32H(x, y) \
do { (y)[0] = (uint8_t)(((x)>>24) & 0xff); (y)[1] = (uint8_t)(((x)>>16) & 0xff); \
(y)[2] = (uint8_t)(((x)>>8) & 0xff); (y)[3] = (uint8_t)((x) & 0xff); } while(0)

// 从uint32_t x中提取从低位开始的第n个字节
#define BYTE(x, n) (((x) >> (8 * (n))) & 0xff)

/* used for keyExpansion */
// 字节替换然后循环左移1位
#define MIX(x) (((S[BYTE(x, 2)] << 24) & 0xff000000) ^ ((S[BYTE(x, 1)] << 16) & 0xff0000) ^ \
((S[BYTE(x, 0)] << 8) & 0xff00) ^ (S[BYTE(x, 3)] & 0xff))

// uint32_t x循环左移n位
#define ROF32(x, n) (((x) << (n)) | ((x) >> (32-(n))))
// uint32_t x循环右移n位
#define ROR32(x, n) (((x) >> (n)) | ((x) << (32-(n))))

/* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
// AES-128轮常量
static const uint32_t rcon[10] = {
0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, 0x10000000UL,
0x20000000UL, 0x40000000UL, 0x80000000UL, 0x1B000000UL, 0x36000000UL
};
// S盒
unsigned char S[256] = {
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
};

//逆S盒
unsigned char inv_S[256] = {
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
};

/* copy in[16] to state[4][4] */
int loadStateArray(uint8_t(*state)[4], const uint8_t* in) {
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
state[j][i] = *in++;
}
}
return 0;
}

/* copy state[4][4] to out[16] */
int storeStateArray(uint8_t(*state)[4], uint8_t* out) {
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
*out++ = state[j][i];
}
}
return 0;
}
//秘钥扩展
int keyExpansion(const uint8_t* key, uint32_t keyLen, AesKey* aesKey) {

if (NULL == key || NULL == aesKey) {
printf("keyExpansion param is NULL\n");
return -1;
}

if (keyLen != 16) {
printf("keyExpansion keyLen = %d, Not support.\n", keyLen);
return -1;
}

uint32_t* w = aesKey->eK; //加密秘钥
uint32_t* v = aesKey->dK; //解密秘钥

/* keyLen is 16 Bytes, generate uint32_t W[44]. */

/* W[0-3] */
for (int i = 0; i < 4; ++i) {
LOAD32H(w[i], key + 4 * i);
}

/* W[4-43] */
for (int i = 0; i < 10; ++i) {
w[4] = w[0] ^ MIX(w[3]) ^ rcon[i];
w[5] = w[1] ^ w[4];
w[6] = w[2] ^ w[5];
w[7] = w[3] ^ w[6];
w += 4;
}

w = aesKey->eK + 44 - 4;
//解密秘钥矩阵为加密秘钥矩阵的倒序,方便使用,把ek的11个矩阵倒序排列分配给dk作为解密秘钥
//即dk[0-3]=ek[41-44], dk[4-7]=ek[37-40]... dk[41-44]=ek[0-3]
for (int j = 0; j < 11; ++j) {

for (int i = 0; i < 4; ++i) {
v[i] = w[i];
}
w -= 4;
v += 4;
}

return 0;
}

// 轮秘钥加
int addRoundKey(uint8_t(*state)[4], const uint32_t* key) {
uint8_t k[4][4];

/* i: row, j: col */
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
k[i][j] = (uint8_t)BYTE(key[j], 3 - i); /* 把 uint32 key[4] 先转换为矩阵 uint8 k[4][4] */
state[i][j] ^= k[i][j];
}
}

return 0;
}

//字节替换
int subBytes(uint8_t(*state)[4]) {
/* i: row, j: col */
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
state[i][j] = S[state[i][j]]; //直接使用原始字节作为S盒数据下标
}
}

return 0;
}

//逆字节替换
int invSubBytes(uint8_t(*state)[4]) {
/* i: row, j: col */
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
state[i][j] = inv_S[state[i][j]];
}
}
return 0;
}

//行移位
int shiftRows(uint8_t(*state)[4]) {
uint32_t block[4] = { 0 };

/* i: row */
for (int i = 0; i < 4; ++i) {
//便于行循环移位,先把一行4字节拼成uint_32结构,移位后再转成独立的4个字节uint8_t
LOAD32H(block[i], state[i]);
block[i] = ROF32(block[i], 8 * i);
STORE32H(block[i], state[i]);
}

return 0;
}

//逆行移位
int invShiftRows(uint8_t(*state)[4]) {
uint32_t block[4] = { 0 };

/* i: row */
for (int i = 0; i < 4; ++i) {
LOAD32H(block[i], state[i]);
block[i] = ROR32(block[i], 8 * i);
STORE32H(block[i], state[i]);
}

return 0;
}

/* Galois Field (256) Multiplication of two Bytes */
// 两字节的伽罗华域乘法运算
uint8_t GMul(uint8_t u, uint8_t v) {
uint8_t p = 0;

for (int i = 0; i < 8; ++i) {
if (u & 0x01) { //
p ^= v;
}

int flag = (v & 0x80);
v <<= 1;
if (flag) {
v ^= 0x1B; /* x^8 + x^4 + x^3 + x + 1 */
}

u >>= 1;
}

return p;
}

// 列混合
int mixColumns(uint8_t(*state)[4]) {
uint8_t tmp[4][4];
uint8_t M[4][4] = { {0x02, 0x03, 0x01, 0x01},
{0x01, 0x02, 0x03, 0x01},
{0x01, 0x01, 0x02, 0x03},
{0x03, 0x01, 0x01, 0x02} };

/* copy state[4][4] to tmp[4][4] */
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
tmp[i][j] = state[i][j];
}
}

for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) { //伽罗华域加法和乘法
state[i][j] = GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j])
^ GMul(M[i][2], tmp[2][j]) ^ GMul(M[i][3], tmp[3][j]);
}
}

return 0;
}

// 逆列混合
int invMixColumns(uint8_t(*state)[4]) {
uint8_t tmp[4][4];
uint8_t M[4][4] = { {0x0E, 0x0B, 0x0D, 0x09},
{0x09, 0x0E, 0x0B, 0x0D},
{0x0D, 0x09, 0x0E, 0x0B},
{0x0B, 0x0D, 0x09, 0x0E} }; //使用列混合矩阵的逆矩阵

/* copy state[4][4] to tmp[4][4] */
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
tmp[i][j] = state[i][j];
}
}

for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
state[i][j] = GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j])
^ GMul(M[i][2], tmp[2][j]) ^ GMul(M[i][3], tmp[3][j]);
}
}

return 0;
}

// AES-128加密接口,输入key应为16字节长度,输入长度应该是16字节整倍数,
// 这样输出长度与输入长度相同,函数调用外部为输出数据分配内存
int aesEncrypt(const uint8_t* key, uint32_t keyLen, const uint8_t* pt, uint8_t* ct, uint32_t len) {

AesKey aesKey;
uint8_t* pos = ct;
const uint32_t* rk = aesKey.eK; //解密秘钥指针
uint8_t out[BLOCKSIZE] = { 0 };
uint8_t actualKey[16] = { 0 };
uint8_t state[4][4] = { 0 };

if (NULL == key || NULL == pt || NULL == ct) {
printf("param err.\n");
return -1;
}

if (keyLen > 16) {
printf("keyLen must be 16.\n");
return -1;
}

if (len % BLOCKSIZE) {
printf("inLen is invalid.\n");
return -1;
}

memcpy(actualKey, key, keyLen);
keyExpansion(actualKey, 16, &aesKey); // 秘钥扩展

// 使用ECB模式循环加密多个分组长度的数据
for (int i = 0; i < len; i += BLOCKSIZE) {
// 把16字节的明文转换为4x4状态矩阵来进行处理
loadStateArray(state, pt);
// 轮秘钥加
addRoundKey(state, rk);

for (int j = 1; j < 10; ++j) {
rk += 4;
subBytes(state); // 字节替换
shiftRows(state); // 行移位
mixColumns(state); // 列混合
addRoundKey(state, rk); // 轮秘钥加
}

subBytes(state); // 字节替换
shiftRows(state); // 行移位
// 此处不进行列混合
addRoundKey(state, rk + 4); // 轮秘钥加

// 把4x4状态矩阵转换为uint8_t一维数组输出保存
storeStateArray(state, pos);

pos += BLOCKSIZE; // 加密数据内存指针移动到下一个分组
pt += BLOCKSIZE; // 明文数据指针移动到下一个分组
rk = aesKey.eK; // 恢复rk指针到秘钥初始位置
}
return 0;
}

// AES128解密, 参数要求同加密
int aesDecrypt(const uint8_t* key, uint32_t keyLen, const uint8_t* ct, uint8_t* pt, uint32_t len) {
AesKey aesKey;
uint8_t* pos = pt;
const uint32_t* rk = aesKey.dK; //解密秘钥指针
uint8_t out[BLOCKSIZE] = { 0 };
uint8_t actualKey[16] = { 0 };
uint8_t state[4][4] = { 0 };

if (NULL == key || NULL == ct || NULL == pt) {
printf("param err.\n");
return -1;
}

if (keyLen > 16) {
printf("keyLen must be 16.\n");
return -1;
}

if (len % BLOCKSIZE) {
printf("inLen is invalid.\n");
return -1;
}

memcpy(actualKey, key, keyLen);
keyExpansion(actualKey, 16, &aesKey); //秘钥扩展,同加密

for (int i = 0; i < len; i += BLOCKSIZE) {
// 把16字节的密文转换为4x4状态矩阵来进行处理
loadStateArray(state, ct);
// 轮秘钥加,同加密
addRoundKey(state, rk);

for (int j = 1; j < 10; ++j) {
rk += 4;
invShiftRows(state); // 逆行移位
invSubBytes(state); // 逆字节替换,这两步顺序可以颠倒
addRoundKey(state, rk); // 轮秘钥加,同加密
invMixColumns(state); // 逆列混合
}

invSubBytes(state); // 逆字节替换
invShiftRows(state); // 逆行移位
// 此处没有逆列混合
addRoundKey(state, rk + 4); // 轮秘钥加,同加密

storeStateArray(state, pos); // 保存明文数据
pos += BLOCKSIZE; // 输出数据内存指针移位分组长度
ct += BLOCKSIZE; // 输入数据内存指针移位分组长度
rk = aesKey.dK; // 恢复rk指针到秘钥初始位置
}
return 0;
}
// 方便输出16进制数据
void printHex(uint8_t* ptr, int len, char* tag) {
printf("%s\ndata[%d]: ", tag, len);
for (int i = 0; i < len; ++i) {
printf("%.2X ", *ptr++);
}
printf("\n");
}
char table[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ";
char res[] = { 0x36,0x35,0x34,0x33,0x3a,0x39,0x38,0x37,0x3e,0x3d,0x85,0x84,0x83,0x8a,0x89,0x88,0x87,0x8e,0x8d,0x8c,0x8b,0x92,0x91,0x90,0x8f,0x76,0x75,0x74,0x73,0x7a,0x79,0x78,0x77,0x7e,0x7d,0x7c,0xa5,0xa4,0xa3,0xaa,0xa9,0xa8,0xa7,0xae,0xad,0xac,0xab,0xb2,
0xb1,0xb0,0xaf,0x96,0x95,0x94,0x93,0x9a,0x99,0x98,0x97,0x9e,0x9d,0x9c,0x45,0x44,0x43,0x4a,0x49,0x48,0x47,0x4e,0x4d,0x4c,0x4b,0x52,0x51,0x50,0x4f,0x3c,0x3b,0x42,0x41,0x40,0x3f,0xa6,0x9b,0xa2,0xa1,0xa0,0x9f,0x86,0x7b,0x82,0x81,0x80,0x66,0x66
};
char getcc(char in) {
for (size_t i = 0; i < 96; i++)
{
if (in == res[i])
{
return table[i];
}

}

}

int main() {


// case 1
const uint8_t key[16] = { 0x05,0x06,0x07,0x08,0x37,0x42,0x4d,0x58,0x63,0x00,0x0a,0x0c,0x0d,0x0e,0x0f,0x10 }; //十六字节密钥
const uint8_t pt[48] = { 0x71,0x55,0x7f,0xa8,0xfa,0x0e,0xa3,0x19,0xa0,0x5c,0xf9,0x0e,0x9b,0x0b,0x5e,0xfc,0xb5,0xa8,0x49,0xfd,0x90,0x99,0x74,0xc7,0x77,0x02,0x6a,0xf5,0x9a,0x6a,0xba,0x7f,0xfb,0xe7,0x68,0xda,0x54,0xee,0xe8,0xbb,0x78,0x01,0xe7,0xbb,0xa2,0x95,0x95,0xfa };
uint8_t ct[16] = { 0 }; // 外部申请输出数据内存,用于加密后的数据
uint8_t plain[16] = { 0 }; // 外部申请输出数据内存,用于解密后的数据


for (int i = 0; i < 3; i++) {
for (int j = 0; j < 16; j++) {
ct[j] = pt[j + i * 16];
}
aesDecrypt(key, 16, ct, plain, 16);
for (int k = 0; k < 16; k++) {
printf("%c", getcc(plain[k]));
}
}


return 0;
}


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
import base64
from base64 import encode
from Crypto.Cipher import AES

key = bytes.fromhex("0506070837424d5863000a0c0d0e0f10") #需要加密的内容,bytes类型

aes = AES.new(key,AES.MODE_ECB) #创建一个aes对象
enc ="71557FA8FA0EA319A05CF90E9B0B5EFCB5A849FD909974C777026AF59A6ABA7FFBE768DA54EEE8BB7801E7BBA29595FA0F"
result =""
for i in range(len(enc)//32):
#print(enc[i*32:(i+1)*32:])
den_text = aes.decrypt(bytes.fromhex(enc[i*32:(i+1)*32:])) # 解密密文
result+= den_text.hex()
#print(den_text.hex(),end='')

table = dict()
chars = "!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ "
enc_char ="""45 43 4a 49 48 47 4e 4d 4c 4b 52 51 50 4f 36
35 34 33 3a 39 38 37 3e 3d 3c 3b 42 41 40 3f a6
a5 a4 a3 aa a9 a8 a7 ae ad ac ab b2 b1 b0 af 96
95 94 93 9a 99 98 97 9e 9d 9c 9b a2 a1 a0 9f 86 85
84 83 8a 89 88 87 8e 8d 8c 8b 92 91 90 8f 76 75
74 73 7a 79 78 77 7e 7d 7c 7b 82 81 80 66 66 66 """

b = enc_char.replace(' ','').replace('\n','')

for i,char in enumerate(chars):
tmp = b[2 * i:2 * i + 2]
table[bytes.fromhex(tmp)] = char

flag =""
for i in bytes.fromhex(result):
flag+= table.get(i.to_bytes(),'?')
print(flag)

hook脚本

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
var inter=setInterval(function () {
var sgame = Process.findModuleByName("1.exe");
var baseaddr = sgame.base
if(sgame==null){
console.log("无");
return;
}

console.log("base"+baseaddr);

clearInterval(inter);
console.log("sbox")
console.log(hexdump(baseaddr.add(0x005160),{length:255,ansi:true}))
console.log("key")
console.log(hexdump(baseaddr.add(0x0017060),{length:16,ansi:true}))

Interceptor.attach(baseaddr.add(0x001E7F), {
onEnter: function (args) {
var rax=this.context.rax;
console.log("secret" +rax);
console.log(hexdump(ptr(rax),{length: 48,ansi:true}));
}}),
Interceptor.attach(baseaddr.add(0x001E29), {
onEnter: function (args) {
var rdx=this.context.rdx;
console.log("input" +rdx);
console.log(hexdump(ptr(rdx),{length: 95,ansi:true}));
}
})
,
Interceptor.attach(baseaddr.add(0x001720), {
onEnter: function (args) {
console.log(hexdump(baseaddr.add(0x5160),{length: 256,ansi:true}));
}
})
},1)

参考wp A1natas 2024 浙江省赛决赛 WriteUp