MAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(dwBase + pDos->e_lfanew);
// 获取64位NT头
// PIMAGE_NT_HEADERS64 pNt = (PIMAGE_NT_HEADERS64)(dwBase + pDos->e_lfanew);
// 获取数据目录表
PIMAGE_DATA_DIRECTORY pExportDir = pNt->OptionalHeader.DataDirectory;
pExportDir = &(pExportDir[IMAGE_DIRECTORY_ENTRY_EXPORT]);
DWORD dwOffset = pExportDir->VirtualAddress;
// 获取导出表信息结构
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(dwBase + dwOffset);
DWORD dwFunCount = pExport->NumberOfFunctions;
DWORD dwFunNameCount = pExport->NumberOfNames;
DWORD dwModOffset = pExport->Name;
// 获取导出地址表
PDWORD pEAT = (PDWORD)(dwBase + pExport->AddressOfFunctions);
// 获取导出名称表
PDWORD pENT = (PDWORD)(dwBase + pExport->AddressOfNames);
// 获取导出序号表
PWORD pEIT = (PWORD)(dwBase + pExport->AddressOfNameOrdinals);
for (DWORD dwOrdinal = 0; dwOrdinal < dwFunCount; dwOrdinal++)
{
if (!pEAT[dwOrdinal])
{
continue;
}
// 获取序号
DWORD dwID = pExport->Base + dwOrdinal;
// 获取导出函数地址
ULONGLONG dwFunAddrOffset = pEAT[dwOrdinal];
for (DWORD dwIndex = 0; dwIndex < dwFunNameCount; dwIndex++)
{
// 在序号表中查找函数的序号
if (pEIT[dwIndex] == dwOrdinal)
{
// 根据序号索引到函数名称表中的名字
ULONGLONG dwNameOffset = pENT[dwIndex];
char* pFunName = (char*)((ULONGLONG)dwBase + dwNameOffset);
if (!strcmp(pFunName, "GetProcAddress"))
{
// 根据函数名称返回函数地址
return dwBase + dwFunAddrOffset;
}
}
}
}
return 0;
}
// 定义名称指针
typedef ULONGLONG(WINAPI *fnGetProcAddress)(_In_ HMODULE hModule, _In_ LPCSTR lpProcName);
typedef HMODULE(WINAPI *fnLoadLibraryA)(_In_ LPCSTR lpLibFileName);
int main(int argc, char *argv[])
{
DWORD kernel32BaseAddress = GetModuleKernel32();
if (kernel32BaseAddress == 0)
{
return 0;
}
// 获取kernel32基址/获取GetProcAddress的基址
fnGetProcAddress pfnGetProcAddress = (fnGetProcAddress)MyGetProcAddress();
std::cout << pfnGetProcAddress << std::endl;
// 获取Kernel32核心API地址
fnLoadLibraryA pfnLoadLibraryA = (fnLoadLibraryA)pfnGetProcAddress((HMODULE)kernel32BaseAddress, "LoadLibraryA");
printf("自定义读入LoadLibrary = %x \n", pfnLoadLibraryA);
system("pause");
return 0;
}
输出效果如下图所示,我们即可读入fnLoadLibraryA
函数的内存地址;
上述代码的使用也很简单,当我们能够得到GetProcAddress
的内存地址后,就可以使用该内存地址动态定位到任意一个函数地址,我们通过得到LoadLibrary
函数地址,与GetModuleHandleA
函数地址,通过两个函数就可以定位到Windows
系统内任意一个函数,我们以调用MessageBox
弹窗为例,动态输出一个弹窗,该调用方式如下所示。
// 定义名称指针
typedef ULONGLONG(WINAPI *fnGetProcAddress)(_In_ HMODULE hModule, _In_ LPCSTR lpProcName);
typedef HMODULE(WINAPI *fnLoadLibraryA)(_In_ LPCSTR lpLibFileName);
typedef int(WINAPI *fnMessageBox)(HWND hWnd, LPSTR lpText, LPSTR lpCaption, UINT uType);
typedef HMODULE(WINAPI *fnGetModuleHandleA)(_In_opt_ LPCSTR lpModuleName);
typedef BOOL(WINAPI *fnVirtualProtect)(_In_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flNewProtect, _Out_ PDWORD lpflOldProtect);
typedef void(WINAPI *fnExitProcess)(_In_ UINT uExitCode);
int main(int argc, char * argv[])
{
// 获取kernel32基址 / 获取GetProcAddress的基址
fnGetProcAddress pfnGetProcAddress = (fnGetProcAddress)MyGetProcAddress();
ULONGLONG dwBase = GetModuleKernel32();
printf("fnGetProcAddress = %x \n", pfnGetProcAddress);
printf("GetKernel32Addr = %x \n", dwBase);
// 获取Kernel32核心API地址
fnLoadLibraryA pfnLoa