首页
社区
课程
招聘
[旧帖] [原创]实现函数GetProcAddress()和GetModuleHandle() 0.00雪花
发表于: 2011-3-1 18:03 5142

[旧帖] [原创]实现函数GetProcAddress()和GetModuleHandle() 0.00雪花

2011-3-1 18:03
5142

通过对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;
}


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (2)
雪    币: 34
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
拖进IDA F5一下就来了.
2011-3-1 19:12
0
雪    币: 31
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
我没仔细研究kernel32.dll中这两个函数的实现过程,只是根据功能来实现的。
我通过IDA看到, GetProcAddress是通过ntdll.dll的LdrGetProcedureAddress来实现的。
当研究没多久, 如果有什么问题或缺陷欢迎提出来, 多谢
2011-3-2 10:28
0
游客
登录 | 注册 方可回帖
返回
//