shellcode loader
2、C++函数指针实现shellcode loader
1、课程目标
- 理解函数指针在ShellCode执行中的应用
- 掌握多种函数指针类型定义方式
- 了解不同调用约定对执行的影响
- 实现健壮的函数指针Loader
2、名词解释
| 术语 | 全称 | 解释 |
|---|---|---|
| Function Pointer | 函数指针 | 指向函数的指针,可用于间接调用 |
| cdecl | C Declaration | C语言默认调用约定,调用者清理栈 |
| stdcall | Standard Call | Windows API调用约定,被调用者清理栈 |
| fastcall | Fast Call | 使用寄存器传递前两个参数 |
| typedef | Type Definition | 类型定义,用于创建类型别名 |
3、使用工具
| 工具 | 用途 | 备注 |
|---|---|---|
| Visual Studio | 开发编译 | 支持x86/x64 |
| x64dbg | 调试分析 | 观察调用过程 |
| IDA Pro | 静态分析 | 分析生成的代码 |
4、技术原理
1. 函数指针基础
// 基本语法
return_type (*pointer_name)(parameter_types);
// 例子
void (*func_ptr)(); // 无参无返回
int (*func_ptr)(int, int); // 两参数返回int
void* (*func_ptr)(size_t); // 返回void*
2. 使用typedef简化
// 定义类型
typedef void (*SHELLCODE_FUNC)();
typedef int (*SHELLCODE_FUNC_INT)(int);
// 使用
SHELLCODE_FUNC func = (SHELLCODE_FUNC)address;
func();
3. 调用约定对比
cdecl: 调用者清理栈,参数从右向左入栈
stdcall: 被调用者清理栈,参数从右向左入栈
fastcall: 前两个参数通过ECX/EDX传递,其余入栈
thiscall: this指针通过ECX传递(C++方法)
4、代码实现
1. 基础函数指针Loader
// func_ptr_loader.cpp
// 函数指针ShellCode Loader
#include <windows.h>
#include <stdio.h>
// 测试ShellCode
unsigned char shellcode[] = {
0x90, 0x90, 0x90, 0x90, // NOP
0x31, 0xC0, // xor eax, eax
0x40, // inc eax (return 1)
0xC3 // ret
};
//=============================================================================
// 方法1: 最基础的函数指针
//=============================================================================
typedef void (*SHELLCODE_VOID)();
void Method1_BasicVoid() {
printf("[*] Method 1: Basic Void Function Pointer\n");
LPVOID mem = VirtualAlloc(NULL, sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!mem) {
printf("[-] VirtualAlloc failed\n");
return;
}
memcpy(mem, shellcode, sizeof(shellcode));
// 转换为函数指针并调用
SHELLCODE_VOID func = (SHELLCODE_VOID)mem;
printf("[*] Calling shellcode at %p\n", mem);
func();
printf("[+] Returned from shellcode\n");
VirtualFree(mem, 0, MEM_RELEASE);
}
//=============================================================================
// 方法2: 带返回值的函数指针
//=============================================================================
typedef int (*SHELLCODE_INT)();
void Method2_WithReturn() {
printf("[*] Method 2: Function Pointer with Return Value\n");
LPVOID mem = VirtualAlloc(NULL, sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!mem) return;
memcpy(mem, shellcode, sizeof(shellcode));
SHELLCODE_INT func = (SHELLCODE_INT)mem;
int result = func();
printf("[+] ShellCode returned: %d (0x%X)\n", result, result);
VirtualFree(mem, 0, MEM_RELEASE);
}
//=============================================================================
// 方法3: 带参数的函数指针
//=============================================================================
typedef int (*SHELLCODE_PARAMS)(int a, int b);
// 测试带参数的ShellCode (返回 a + b)
unsigned char shellcode_add[] = {
0x8B, 0x44, 0x24, 0x04, // mov eax, [esp+4] ; 第一个参数
0x03, 0x44, 0x24, 0x08, // add eax, [esp+8] ; 加上第二个参数
0xC3 // ret
};
void Method3_WithParams() {
printf("[*] Method 3: Function Pointer with Parameters\n");
LPVOID mem = VirtualAlloc(NULL, sizeof(shellcode_add),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!mem) return;
memcpy(mem, shellcode_add, sizeof(shellcode_add));
SHELLCODE_PARAMS func = (SHELLCODE_PARAMS)mem;
int result = func(10, 20);
printf("[+] func(10, 20) = %d\n", result);
VirtualFree(mem, 0, MEM_RELEASE);
}
//=============================================================================
// 方法4: 使用指定调用约定
//=============================================================================
#ifdef _WIN32
typedef int (__cdecl *SHELLCODE_CDECL)(int, int);
typedef int (__stdcall *SHELLCODE_STDCALL)(int, int);
// stdcall版本的加法ShellCode
unsigned char shellcode_add_stdcall[] = {
0x8B, 0x44, 0x24, 0x04, // mov eax, [esp+4]
0x03, 0x44, 0x24, 0x08, // add eax, [esp+8]
0xC2, 0x08, 0x00 // ret 8 (清理8字节参数)
};
void Method4_CallingConvention() {
printf("[*] Method 4: Calling Convention\n");
// cdecl版本
LPVOID mem1 = VirtualAlloc(NULL, sizeof(shellcode_add),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(mem1, shellcode_add, sizeof(shellcode_add));
SHELLCODE_CDECL func_cdecl = (SHELLCODE_CDECL)mem1;
int r1 = func_cdecl(5, 3);
printf("[+] cdecl: func(5, 3) = %d\n", r1);
// stdcall版本
LPVOID mem2 = VirtualAlloc(NULL, sizeof(shellcode_add_stdcall),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(mem2, shellcode_add_stdcall, sizeof(shellcode_add_stdcall));
SHELLCODE_STDCALL func_stdcall = (SHELLCODE_STDCALL)mem2;
int r2 = func_stdcall(5, 3);
printf("[+] stdcall: func(5, 3) = %d\n", r2);
VirtualFree(mem1, 0, MEM_RELEASE);
VirtualFree(mem2, 0, MEM_RELEASE);
}
#endif
//=============================================================================
// 方法5: Lambda表达式封装 (C++11)
//=============================================================================
void Method5_LambdaWrapper() {
printf("[*] Method 5: Lambda Wrapper\n");
LPVOID mem = VirtualAlloc(NULL, sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!mem) return;
memcpy(mem, shellcode, sizeof(shellcode));
// 使用lambda封装
auto execute = [mem]() -> int {
typedef int (*SC_FUNC)();
SC_FUNC func = (SC_FUNC)mem;
return func();
};
int result = execute();
printf("[+] Lambda returned: %d\n", result);
VirtualFree(mem, 0, MEM_RELEASE);
}
//=============================================================================
// 方法6: 使用std::function (C++11)
//=============================================================================
#include <functional>
void Method6_StdFunction() {
printf("[*] Method 6: std::function\n");
LPVOID mem = VirtualAlloc(NULL, sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!mem) return;
memcpy(mem, shellcode, sizeof(shellcode));
// 包装为std::function
std::function<int()> func = reinterpret_cast<int(*)()>(mem);
int result = func();
printf("[+] std::function returned: %d\n", result);
VirtualFree(mem, 0, MEM_RELEASE);
}
//=============================================================================
// 方法7: 两步分配(安全方式)
//=============================================================================
void Method7_TwoStepAlloc() {
printf("[*] Method 7: Two-Step Allocation (RW -> RX)\n");
// 步骤1: 分配可写内存
LPVOID mem = VirtualAlloc(NULL, sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!mem) return;
printf("[+] Allocated RW memory at %p\n", mem);
// 复制ShellCode
memcpy(mem, shellcode, sizeof(shellcode));
// 步骤2: 修改为可执行
DWORD oldProtect;
if (!VirtualProtect(mem, sizeof(shellcode), PAGE_EXECUTE_READ, &oldProtect)) {
printf("[-] VirtualProtect failed\n");
VirtualFree(mem, 0, MEM_RELEASE);
return;
}
printf("[+] Changed to RX protection\n");
// 执行
typedef int (*SC_FUNC)();
SC_FUNC func = (SC_FUNC)mem;
int result = func();
printf("[+] Returned: %d\n", result);
VirtualFree(mem, 0, MEM_RELEASE);
}
//=============================================================================
// 方法8: 带异常处理
//=============================================================================
void Method8_WithException() {
printf("[*] Method 8: With Exception Handling\n");
LPVOID mem = VirtualAlloc(NULL, sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!mem) return;
memcpy(mem, shellcode, sizeof(shellcode));
typedef int (*SC_FUNC)();
SC_FUNC func = (SC_FUNC)mem;
__try {
int result = func();
printf("[+] Returned: %d\n", result);
}
__except(EXCEPTION_EXECUTE_HANDLER) {
printf("[-] Exception: 0x%08X\n", GetExceptionCode());
}
VirtualFree(mem, 0, MEM_RELEASE);
}
//=============================================================================
// 方法9: 从文件加载
//=============================================================================
void Method9_FromFile(const char* filename) {
printf("[*] Method 9: Load from File\n");
HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("[-] Cannot open file: %s\n", filename);
return;
}
DWORD fileSize = GetFileSize(hFile, NULL);
printf("[+] File size: %lu bytes\n", fileSize);
LPVOID mem = VirtualAlloc(NULL, fileSize,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!mem) {
CloseHandle(hFile);
return;
}
DWORD bytesRead;
ReadFile(hFile, mem, fileSize, &bytesRead, NULL);
CloseHandle(hFile);
printf("[+] Loaded %lu bytes to %p\n", bytesRead, mem);
typedef void (*SC_FUNC)();
SC_FUNC func = (SC_FUNC)mem;
__try {
func();
printf("[+] Execution completed\n");
}
__except(EXCEPTION_EXECUTE_HANDLER) {
printf("[-] Exception: 0x%08X\n", GetExceptionCode());
}
VirtualFree(mem, 0, MEM_RELEASE);
}
//=============================================================================
// 主函数
//=============================================================================
int main(int argc, char* argv[]) {
printf("========================================\n");
printf(" Function Pointer ShellCode Loader \n");
printf("========================================\n\n");
// 执行所有方法
Method1_BasicVoid();
printf("\n");
Method2_WithReturn();
printf("\n");
Method3_WithParams();
printf("\n");
#ifdef _WIN32
Method4_CallingConvention();
printf("\n");
#endif
Method5_LambdaWrapper();
printf("\n");
Method6_StdFunction();
printf("\n");
Method7_TwoStepAlloc();
printf("\n");
Method8_WithException();
printf("\n");
// 文件加载
if (argc >= 2) {
Method9_FromFile(argv[1]);
}
printf("[*] All methods completed.\n");
return 0;
}
2. 泛型函数指针Loader类
// generic_loader.hpp
// 泛型ShellCode Loader类
#pragma once
#include <windows.h>
#include <functional>
#include <memory>
#include <stdexcept>
template<typename Signature>
class ShellcodeLoader;
// 特化版本
template<typename ReturnType, typename... Args>
class ShellcodeLoader<ReturnType(Args...)> {
public:
using FuncType = ReturnType(*)(Args...);
private:
LPVOID m_memory;
SIZE_T m_size;
bool m_executable;
public:
ShellcodeLoader() : m_memory(nullptr), m_size(0), m_executable(false) {}
~ShellcodeLoader() {
Free();
}
// 禁止拷贝
ShellcodeLoader(const ShellcodeLoader&) = delete;
ShellcodeLoader& operator=(const ShellcodeLoader&) = delete;
// 允许移动
ShellcodeLoader(ShellcodeLoader&& other) noexcept
: m_memory(other.m_memory), m_size(other.m_size),
m_executable(other.m_executable) {
other.m_memory = nullptr;
other.m_size = 0;
}
// 加载ShellCode
bool Load(const void* shellcode, SIZE_T size) {
Free();
m_size = size;
m_memory = VirtualAlloc(nullptr, size,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!m_memory) return false;
memcpy(m_memory, shellcode, size);
return true;
}
// 设为可执行
bool MakeExecutable() {
if (!m_memory || m_executable) return false;
DWORD oldProtect;
if (!VirtualProtect(m_memory, m_size, PAGE_EXECUTE_READ, &oldProtect)) {
return false;
}
m_executable = true;
return true;
}
// 执行
ReturnType Execute(Args... args) {
if (!m_memory) {
throw std::runtime_error("No shellcode loaded");
}
if (!m_executable) {
if (!MakeExecutable()) {
throw std::runtime_error("Failed to make executable");
}
}
FuncType func = reinterpret_cast<FuncType>(m_memory);
return func(args...);
}
// 运算符重载
ReturnType operator()(Args... args) {
return Execute(args...);
}
// 获取地址
LPVOID GetAddress() const { return m_memory; }
SIZE_T GetSize() const { return m_size; }
private:
void Free() {
if (m_memory) {
VirtualFree(m_memory, 0, MEM_RELEASE);
m_memory = nullptr;
m_size = 0;
m_executable = false;
}
}
};
// 使用示例
/*
int main() {
unsigned char code[] = { 0x31, 0xC0, 0x40, 0xC3 }; // xor eax,eax; inc eax; ret
ShellcodeLoader<int()> loader;
loader.Load(code, sizeof(code));
int result = loader.Execute(); // 或 loader()
printf("Result: %d\n", result);
return 0;
}
*/
3、课后作业
3.1、作业1:添加加密支持
扩展Loader,支持在加载前对ShellCode进行XOR解密。
3.2、作业2:实现Lazy Loading
实现延迟加载机制,在首次调用时才分配内存和复制ShellCode。
3.3、作业3:添加校验功能
在执行前对ShellCode进行完整性校验(如CRC32或MD5)。