我們知道kernel32.dll里有一個(gè)GetProcAddress函數(shù),可以找到模塊中的函數(shù)地址,,函數(shù)原型是這樣的: WINBASEAPI FARPROC WINAPI GetProcAddress( IN HMODULE hModule, IN LPCSTR lpProcName ); hModule 是模塊的句柄,,說白了就是內(nèi)存中dll模塊的首地址 loProcName 一般指函數(shù)名稱的字符串地址,也可能是指序號,,如何區(qū)分呢,? 我們這樣 if (((DWORD)lpProcName& 0xFFFF0000) == 0) { //這里是序號導(dǎo)出的; } { //這里是函數(shù)名稱導(dǎo)出的 } 最終真找到匹配的函數(shù)地址,然后返回就ok了,,但是前提是你需要了解PE的導(dǎo)出表 下面簡單說一下,首先從PE里找到下面這個(gè)結(jié)構(gòu),,如果不知道如何找的話,建議在壇子里搜索一下PE結(jié)構(gòu)解析的文章,,找到這個(gè)結(jié)構(gòu)應(yīng)該還是很簡單的 typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; DWORD Base; DWORD NumberOfFunctions; DWORD NumberOfNames; DWORD AddressOfFunctions; // RVA from base of image DWORD AddressOfNames; // RVA from base of image DWORD AddressOfNameOrdinals; // RVA from base of image } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; 具體什么意思呢,? 雖然, kanxue老大在這里寫的很漂亮了都 http://www./tutorial/chap8/Chap8-1-7.htm 我還是打算啰嗦一下 Base 函數(shù)以序號導(dǎo)出的時(shí)候的序號基數(shù),,從這個(gè)數(shù)開始遞增 NumberOfFunctions 本dll一共有多少個(gè)導(dǎo)出函數(shù),,不管是以序號還是以函數(shù)名導(dǎo)出 NumberOfFunctions 本dll中以能夠以函數(shù)名稱導(dǎo)出的函數(shù)個(gè)數(shù)(注意,說一下,,其實(shí)函數(shù)里的每一個(gè)函數(shù)都能通過序號導(dǎo)出,,但是為了兼容性等等,也給一些函數(shù)提供用函數(shù)名稱來導(dǎo)出) AddressOfFunctions 指向一個(gè)DWORD數(shù)組首地址,,共有NumberOfFunctions 個(gè)元素,,每一個(gè)元素都是一個(gè)函數(shù)地址 AddressOfNames 指向一個(gè)DWORD數(shù)組首地址,共有NumberOfNames個(gè)元素,,每一個(gè)元素都是一個(gè)字符串(函數(shù)名字符串)首地址 AddressOfNameOrdinals指向一個(gè)WORD數(shù)組首地址,,共有NumberOfNames個(gè)元素,每一個(gè)元素都是一個(gè)函數(shù)序號 我們說的最后倆數(shù)組,,其實(shí)是一種一一對應(yīng)的關(guān)系,,假如分別叫 dwNames[] 和 dwNamesOrdinals[],, 假如dwNames[5]的值(這個(gè)指是一個(gè)地址,,前面都說了)指向的字符串等于“GetValue”,那么dwNamesOrdinals[5]的值(這個(gè)指是一個(gè)序號,前面都說了),,就是GetValue導(dǎo)出函數(shù)的序號啦,,那么怎樣找到地址呢? 這時(shí)候就需要用到第一個(gè)數(shù)組了,,假如名字叫dwFuncAddress[], GetValue的導(dǎo)出地址就是 dwFuncAddress[dwNamesOrdinals[5]] + 模塊基址 好了,,啰嗦了這么多,看代碼: DWORD MyGetProcAddress( HMODULE hModule, // handle to DLL module LPCSTR lpProcName // function name ) { int i=0; PIMAGE_DOS_HEADER pImageDosHeader = NULL; PIMAGE_NT_HEADERS pImageNtHeader = NULL; PIMAGE_EXPORT_DIRECTORY pImageExportDirectory = NULL; pImageDosHeader=(PIMAGE_DOS_HEADER)hModule; pImageNtHeader=(PIMAGE_NT_HEADERS)((DWORD)hModule+pImageDosHeader->e_lfanew); pImageExportDirectory=(PIMAGE_EXPORT_DIRECTORY)((DWORD)hModule+pImageNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); DWORD *pAddressOfFunction = (DWORD*)(pImageExportDirectory->AddressOfFunctions + (DWORD)hModule); DWORD *pAddressOfNames = (DWORD*)(pImageExportDirectory->AddressOfNames + (DWORD)hModule); DWORD dwNumberOfNames = (DWORD)(pImageExportDirectory->NumberOfNames); DWORD dwBase = (DWORD)(pImageExportDirectory->Base); WORD *pAddressOfNameOrdinals = (WORD*)(pImageExportDirectory->AddressOfNameOrdinals + (DWORD)hModule); //這個(gè)是查一下是按照什么方式(函數(shù)名稱or函數(shù)序號)來查函數(shù)地址的 DWORD dwName = (DWORD)lpProcName; if ((dwName & 0xFFFF0000) == 0) { goto xuhao; } for (i=0; i<(int)dwNumberOfNames; i++) { char *strFunction = (char *)(pAddressOfNames[i] + (DWORD)hModule); if (lstrcmp(lpProcName, strFunction) == 0) { return (pAddressOfFunction[pAddressOfNameOrdinals[i]] + (DWORD)hModule); } } return 0; //這個(gè)是通過以序號的方式來查函數(shù)地址的 xuhao: if (dwName < dwBase || dwName > dwBase + pImageExportDirectory->NumberOfFunctions - 1) { return 0; } return (pAddressOfFunction[dwName - dwBase] + (DWORD)hModule); } 好了,,測試一下,, //我們寫的函數(shù)返回的地址 DWORD dw1 = MyGetProcAddress(LoadLibrary("user32.dll"), "MessageBoxA"); //系統(tǒng)函數(shù)返回的地址 DWORD dw2 = (DWORD)GetProcAddress(LoadLibrary("user32.dll"), "MessageBoxA"); 哈,,發(fā)現(xiàn)一樣,突然非常有成就感??! 再試試序號查找 //我們寫的函數(shù)返回的地址 DWORD dw1 = MyGetProcAddress(LoadLibrary("user32.dll"), (LPCSTR)0x110); //系統(tǒng)函數(shù)返回的地址 DWORD dw2 = (DWORD)GetProcAddress(LoadLibrary("user32.dll"), (LPCSTR)0x110); 我們發(fā)現(xiàn)還是一樣,成就感更大啦,。,。哈哈(其實(shí)kernel32.dll的0x110 是GetComputerNameExW的序號,自己可以用LordPE查一下) 突然有一天有人說 你的這個(gè)函數(shù)不行,,然后給你舉了個(gè)例子,,于是你測試了一下,下面是例子 DWORD a1 = (DWORD)MyGetProcAddress(LoadLibrary("kernel32.dll"), "HeapFree"); DWORD a2 = (DWORD)GetProcAddress(LoadLibrary("kernel32.dll"), "HeapFree"); 于是 我們就苦思冥想,,依然不得其解,。。,。 但是我發(fā)現(xiàn)a1表示的地址的內(nèi)容是一個(gè)字符串 "NTDLL.RtlFreeHeap"似乎不能用巧合來說這個(gè)問題,,難道是返回了這個(gè)字符串 還要我們再Load一下Ntdll 然后再找一個(gè)RtlFreeHeap的地址嗎?好了先試驗(yàn)一下 果然ntdll.dll中的 RtlFreeHeap的地址 和a2的值的一樣的,, 似乎印證了什么東西,, 好吧 OD拿來 開啟逆向 kernel32.GetProcAddress 搞了一會頭暈了,看不出頭緒 我老大很有才,,他去翻了翻win2000的源碼 偶也 一目了然 Win2K 源碼 FARPROC GetProcAddress( HMODULE hModule, LPCSTR lpProcName ) /*++ Routine Description: This function retrieves the memory address of the function whose name is pointed to by the lpProcName parameter. The GetProcAddress function searches for the function in the module specified by the hModule parameter, or in the module associated with the current process if hModule is NULL. The function must be an exported function; the module's definition file must contain an appropriate EXPORTS line for the function. If the lpProcName parameter is an ordinal value and a function with the specified ordinal does not exist in the module, GetProcAddress can still return a non-NULL value. In cases where the function may not exist, specify the function by name rather than ordinal value. Only use GetProcAddress to retrieve addresses of exported functions that belong to library modules. The spelling of the function name (pointed to by lpProcName) must be identical to the spelling as it appears in the source library's definition (.DEF) file. The function can be renamed in the definition file. Case sensitive matching is used??? Arguments: hModule - Identifies the module whose executable file contains the function. A value of NULL references the module handle associated with the image file that was used to create the current process. lpProcName - Points to the function name, or contains the ordinal value of the function. If it is an ordinal value, the value must be in the low-order word and zero must be in the high-order word. The string must be a null-terminated character string. Return Value: The return value points to the function's entry point if the function is successful. A return value of NULL indicates an error and extended error status is available using the GetLastError function. --*/ { NTSTATUS Status; PVOID ProcedureAddress; STRING ProcedureName; //+ by blueapplez //這應(yīng)該是按函數(shù)名稱查找 //+ by blueapplez if ( (ULONG_PTR)lpProcName > 0xffff ) { RtlInitString(&ProcedureName,lpProcName); Status = LdrGetProcedureAddress( BasepMapModuleHandle( hModule, FALSE ), &ProcedureName, 0L, &ProcedureAddress ); } //+ by blueapplez //這應(yīng)該是按函數(shù)序號查找 //+ by blueapplez else { Status = LdrGetProcedureAddress( BasepMapModuleHandle( hModule, FALSE ), NULL, PtrToUlong((PVOID)lpProcName), &ProcedureAddress ); } if ( !NT_SUCCESS(Status) ) { BaseSetLastNTError(Status); return NULL; } else { if ( ProcedureAddress == BasepMapModuleHandle( hModule, FALSE ) ) { if ( (ULONG_PTR)lpProcName > 0xffff ) { Status = STATUS_ENTRYPOINT_NOT_FOUND; } else { Status = STATUS_ORDINAL_NOT_FOUND; } BaseSetLastNTError(Status); return NULL; } else { return (FARPROC)ProcedureAddress; } } } LdrGetProcedureAddress 是什么呢,? 繼續(xù)看 NTSTATUS LdrGetProcedureAddress ( IN PVOID DllHandle, IN PANSI_STRING ProcedureName OPTIONAL, IN ULONG ProcedureNumber OPTIONAL, OUT PVOID *ProcedureAddress ) { return LdrpGetProcedureAddress(DllHandle,ProcedureName,ProcedureNumber,ProcedureAddress,TRUE); } LdrpGetProcedureAddress 就太長了 先主要說一下 他里面的一個(gè)關(guān)鍵函數(shù) LdrpSnapThunk的關(guān)鍵的一段,能不能看懂我就不管了 反正我基本看不懂 else { Addr = (PULONG)((ULONG_PTR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions); Thunk->u1.Function = ((ULONG_PTR)DllBase + Addr[OrdinalNumber]); //+ by blueapplez //這段是判斷一下返回的地址是否越界 //越界了 就返回原來的值 //+ by blueapplez if (Thunk->u1.Function > (ULONG_PTR)ExportDirectory && Thunk->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize) ) { UNICODE_STRING UnicodeString; ANSI_STRING ForwardDllName; PVOID ForwardDllHandle; PUNICODE_STRING ForwardProcName; ULONG ForwardProcOrdinal; //+ by blueapplez //如果沒有越界,,就從那個(gè)地址中查找'.',,找到了就再做一次Load Dll,和GetProcAddress //沒有找到'.' 就返回原來的值 //+ by blueapplez ImportString = (PSZ)Thunk->u1.Function; ForwardDllName.Buffer = ImportString, ForwardDllName.Length = (USHORT)(strchr(ImportString, '.') - ImportString); ForwardDllName.MaximumLength = ForwardDllName.Length; st = RtlAnsiStringToUnicodeString(&UnicodeString, &ForwardDllName, TRUE); if (NT_SUCCESS(st)) { #if defined (WX86) if (Wx86ProcessInit) { NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = RtlImageNtHeader(DllBase)->FileHeader.Machine == IMAGE_FILE_MACHINE_I386; } #endif 好了 到此已經(jīng)告一段落,, 下面給出源碼,,有人會問,怎樣做的dll才會出現(xiàn)查找導(dǎo)出表的地址的時(shí)候返回一個(gè)字符串呢,? 其實(shí)就是在 .def文件的EXPORTS后加一句就成(這個(gè)我老大試驗(yàn)了n久才搞定)加上 MsgBox = user32.MessageBoxA DWORD MyGetProcAddress( HMODULE hModule, // handle to DLL module LPCSTR lpProcName // function name ) { int i=0; char *pRet = NULL; PIMAGE_DOS_HEADER pImageDosHeader = NULL; PIMAGE_NT_HEADERS pImageNtHeader = NULL; PIMAGE_EXPORT_DIRECTORY pImageExportDirectory = NULL; pImageDosHeader=(PIMAGE_DOS_HEADER)hModule; pImageNtHeader=(PIMAGE_NT_HEADERS)((DWORD)hModule+pImageDosHeader->e_lfanew); pImageExportDirectory=(PIMAGE_EXPORT_DIRECTORY)((DWORD)hModule+pImageNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); DWORD dwExportRVA = pImageNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; DWORD dwExportSize = pImageNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; DWORD *pAddressOfFunction = (DWORD*)(pImageExportDirectory->AddressOfFunctions + (DWORD)hModule); DWORD *pAddressOfNames = (DWORD*)(pImageExportDirectory->AddressOfNames + (DWORD)hModule); DWORD dwNumberOfNames = (DWORD)(pImageExportDirectory->NumberOfNames); DWORD dwBase = (DWORD)(pImageExportDirectory->Base); WORD *pAddressOfNameOrdinals = (WORD*)(pImageExportDirectory->AddressOfNameOrdinals + (DWORD)hModule); //這個(gè)是查一下是按照什么方式(函數(shù)名稱or函數(shù)序號)來查函數(shù)地址的 DWORD dwName = (DWORD)lpProcName; if ((dwName & 0xFFFF0000) == 0) { goto xuhao; } for (i=0; i<(int)dwNumberOfNames; i++) { char *strFunction = (char *)(pAddressOfNames[i] + (DWORD)hModule); if (strcmp(strFunction, (char *)lpProcName) == 0) { pRet = (char *)(pAddressOfFunction[pAddressOfNameOrdinals[i]] + (DWORD)hModule); goto _exit11; } } //這個(gè)是通過以序號的方式來查函數(shù)地址的 xuhao: if (dwName < dwBase || dwName > dwBase + pImageExportDirectory->NumberOfFunctions - 1) { return 0; } pRet = (char *)(pAddressOfFunction[dwName - dwBase] + (DWORD)hModule); _exit11: //判斷得到的地址有沒有越界 if ((DWORD)pRet<dwExportRVA+(DWORD)hModule || (DWORD)pRet > dwExportRVA+ (DWORD)hModule + dwExportSize) { return (DWORD)pRet; } char pTempDll[100] = {0}; char pTempFuction[100] = {0}; lstrcpy(pTempDll, pRet); char *p = strchr(pTempDll, '.'); if (!p) { return (DWORD)pRet; } *p = 0; lstrcpy(pTempFuction, p+1); lstrcat(pTempDll, ".dll"); HMODULE h = LoadLibrary(pTempDll); if (h == NULL) { return (DWORD)pRet; } return MyGetProcAddress(h, pTempFuction); } 現(xiàn)在測試下 DWORD a1 = (DWORD)MyGetProcAddress(LoadLibrary("kernel32.dll"), (LPCSTR)"HeapFree"); DWORD a2 = (DWORD)GetProcAddress(LoadLibrary("kernel32.dll"), (LPCSTR)"HeapFree"); 發(fā)現(xiàn)值一樣了 ps.如發(fā)現(xiàn)問題 歡迎批評指正,。 專業(yè)路過 2010-09-27, 22:13:40 :D: 哈哈 我正在找這東西 沙發(fā)~~~ pencil 2010-09-27, 22:48:31 收藏了,謝謝分享 Genius 2010-09-28, 11:25:58 有打包好的源碼嗎,? zgphappy 2010-11-09, 23:13:29 很好很強(qiáng)大,。。,。,。。 dwboy 2011-02-24, 10:42:24 之前寫的時(shí)候找過的資料,在這里貼一下做個(gè)備份,,里面有講DLL轉(zhuǎn)發(fā)時(shí)的情況 http://bbs./showthread.php?t=4280 :D:感謝lz... Greater 2011-02-24, 10:57:01 很好很強(qiáng)大 很實(shí)用 樓主辛苦 ycmint 2011-02-24, 12:44:57 感謝分享,。。,。,。以前以為GetProcAddress只是在image中搜iat 取出就完了。,。,。看了后,,才發(fā)現(xiàn)此函數(shù)還完成了dll 的dll ==,。。,。 zeracker 2011-03-06, 16:48:50 感謝樓主無私的分享,。 謝謝咯 灰常之強(qiáng)大,收藏 littlewisp 2011-08-31, 23:48:31 DLL轉(zhuǎn)發(fā)的情況確實(shí)容易忽略,多謝多謝 RegKiller 2011-09-01, 00:21:47 mark 一下 lixupeng 2012-02-17, 15:21:22 挺有用:D:收下 wybx 2012-02-17, 16:14:08 很好你強(qiáng)大??! 收藏了 huajie 2012-02-27, 16:25:59 感謝啊。支持了 BoyXiao 2012-02-29, 15:34:40 樓主 V5 ,。,。。,。 Mx¢Xgt 2013-02-10, 21:38:24 突然有一天有人說 你的這個(gè)函數(shù)不行,,然后給你舉了個(gè)例子,于是你測試了一下,,下面是例子 DWORD a1 = (DWORD)MyGetProcAddress(LoadLibrary("kernel32.dll"), "HeapFree"); DWORD a2 = (DWORD)GetProcAddress(LoadLibrary("kernel32.dll"), "HeapFree"); 于是 我們就苦思冥想,,依然不得其解。,。,。 但是我發(fā)現(xiàn)a1表示的地址的內(nèi)容是一個(gè)字符串 "NTDLL.RtlFreeHeap"似乎不能用巧合來說這個(gè)問題,難道是返回了這個(gè)字符串 還要我們再Load一下Ntdll 然后再找一個(gè)RtlFreeHeap的地址嗎,?好了先試驗(yàn)一下 果然ntdll.dll中的 RtlFreeHeap的地址 和a2的值的一樣的, 這個(gè)問題最近我也遇到過,按我當(dāng)時(shí)的第一反應(yīng),我會這樣做: DWORD a1 = (DWORD)MyGetProcAddress(LoadLibrary("Ntdll .dll"), "HeapFree"); 如函數(shù) RtlMoveMemory |
|