-
-
[旧帖]
[原创]实现函数GetProcAddress()和GetModuleHandle()
0.00雪花
-
发表于:
2011-3-1 18:03
5142
-
[旧帖] [原创]实现函数GetProcAddress()和GetModuleHandle()
0.00雪花
通过对PE结构的学习,对导入表和导出表相对比较熟悉了。这里自己实现了ANSI的GetProcAddress()和GetModuleHandle()函数,UNICODE的可以仿照来实现。
系统创建进程时根据导入表来加载DLL文件。而如果要调用DLL的函数, 如果导入表中有函数名和地址,则可以直接调用;否则需要通过LoadLibrary()和GetProcAddress()来调用。其中LoadLibrary()完成的功能是:如果DLL不在当前进程内存中, 则加载并设置引用值;否则直接返回DLL的地址并增加引用值。GetProcAddress()的功能是:查找映射到内存中的DLL的导出表,返回对应函数的地址。由于DLL存在索引和函数名称导出两种方式,所以对DLL函数的查找也应支持这两种方式,如果按索引查找则按SelfGetProcAddress(hModule, (LPCSTR)0x01)形式进行,如果按名称查找则按SelfGetProcAddress(hModule, “MessageBoxA”)进行。
// 实现GetProcAddress()函数
// hModule是LoadLibrary()函数返回的地址
// lpProcName是函数字符串的地址
// 返回函数对应的地址
DWORD SelfGetProcAddress(HMODULE hModule, LPCSTR lpProcName)
{
if (hModule==NULL || lpProcName==NULL)
return NULL;
BYTE* btBase = (BYTE*)hModule;
PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)btBase;
PIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS)(btBase+pDosHdr->e_lfanew);
PIMAGE_FILE_HEADER pFileHdr = (PIMAGE_FILE_HEADER)(&pNtHdrs->FileHeader);
PIMAGE_OPTIONAL_HEADER pOptHdr = (PIMAGE_OPTIONAL_HEADER)(&pNtHdrs->OptionalHeader);
IMAGE_DATA_DIRECTORY DataDir = (IMAGE_DATA_DIRECTORY)(pOptHdr->DataDirectory[0]);
if (!DataDir.VirtualAddress || !DataDir.Size)
return NULL;
PIMAGE_EXPORT_DIRECTORY pExpDir = (PIMAGE_EXPORT_DIRECTORY)(btBase+DataDir.VirtualAddress);
if (!pExpDir || !pExpDir->NumberOfNames || !pExpDir->NumberOfFunctions)
return NULL;
PDWORD pNamesAddr = (PDWORD)(btBase + pExpDir->AddressOfNames);
PWORD pOrdisAddr = (PWORD)(btBase + pExpDir->AddressOfNameOrdinals);
PDWORD pFuncsAddr = (PDWORD)(btBase + pExpDir->AddressOfFunctions);
if((DWORD)lpProcName <= 0x0000FFFF) // 按索引查找函数地址
{
DWORD nIndex = (DWORD)lpProcName - pExpDir->Base;
if (nIndex < pExpDir->NumberOfFunctions)
return (DWORD)btBase+pFuncsAddr[nIndex];
}
else // 按函数名查找函数地址
{
for(unsigned i = 0; i<pExpDir->NumberOfNames; i++)
{
PCSTR pszName = (PCSTR)(btBase+pNamesAddr[i]);
WORD nIndex = pOrdisAddr[i];
if(strcmp(lpProcName, pszName) == 0)
return (DWORD)btBase+pFuncsAddr[nIndex];
}
}
return NULL;
}
PEB是进程环境块,存放进程信息。TEB是线程环境块,存放线程信息。FS段寄存器指向当前的TEB结构,在TEB偏移0x30处是PEB指针,通过这个指针即可取得PEB的地址。不过可能你的编程环境中没有PEB和TEB的定义,网络上能搜到相关的定义。
// 实现GetModuleHandle()函数
// DLL名称对应的地址
// 返回模块的地址
HMODULE SelfGetModuleHandle(LPCSTR lpszDllName)
{
PPEB pPEB = NULL;
__asm {
mov eax, fs:[30h]; // fs:[30h]指向PEB
mov pPEB, eax;
}
if (pPEB == NULL) return NULL;
PPEB_LDR_DATA pLdr = pPEB->LoaderData;
PLIST_ENTRY pFlink = (PLIST_ENTRY)(pLdr->InLoadOrderModuleList.Flink);
PLIST_ENTRY pBlink = (PLIST_ENTRY)(pLdr->InLoadOrderModuleList.Blink);
if(NULL == lpszDllName)
{
PLDR_DATA_TABLE_ENTRY pModuleTab = (PLDR_DATA_TABLE_ENTRY)pFlink;
return (HMODULE)pModuleTab->DllBase;
}
WCHAR lpwszName[MAX_PATH] = {0};
int nLen = strlen(lpszDllName) + 1;
int nwLen = MultiByteToWideChar(CP_ACP, 0, lpszDllName, nLen, NULL, 0);
MultiByteToWideChar(CP_ACP, 0, lpszDllName, nLen, lpwszName, nwLen);
for (PLIST_ENTRY pLink = pFlink; pLink!=pBlink; pLink=pLink->Flink)
{
PLDR_DATA_TABLE_ENTRY pModuleTab = (PLDR_DATA_TABLE_ENTRY)pLink;
// 不区分大小写比较
if (_wcsicmp(pModuleTab->BaseDllName.Buffer, lpwszName) == 0)
return (HMODULE)pModuleTab->DllBase;
}
return NULL;
}
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!