咱們知道kernel32.dll裏有一個GetProcAddress函數,能夠找到模塊中的函數地址,函數原型是這樣的:
WINBASEAPI
FARPROC
WINAPI
GetProcAddress(
IN HMODULE hModule,
IN LPCSTR lpProcName
);
hModule 是模塊的句柄,說白了就是內存中dll模塊的首地址
loProcName 通常指函數名稱的字符串地址,也多是指序號,如何區分呢?
咱們這樣
if (((DWORD)lpProcName& 0xFFFF0000) == 0)
{
//這裏是序號導出的;
}
{
//這裏是函數名稱導出的
}
最終真找到匹配的函數地址,而後返回就ok了,可是前提是你須要瞭解PE的導出表
下面簡單說一下,首先從PE裏找到下面這個結構,若是不知道如何找的話,建議在罈子裏搜索一下PE結構解析的文章,找到這個結構應該仍是很簡單的
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.pediy.com/tutorial/chap8/Chap8-1-7.htm
我仍是打算囉嗦一下
Base 函數以序號導出的時候的序號基數,從這個數開始遞增
NumberOfFunctions 本dll一共有多少個導出函數,無論是以序號仍是以函數名導出
NumberOfFunctions 本dll中以可以以函數名稱導出的函數個數(注意,說一下,其實函數裏的每個函數都能經過序號導出,可是爲了兼容性等等,也給一些函數提供用函數名稱來導出)
AddressOfFunctions 指向一個DWORD數組首地址,共有NumberOfFunctions 個元素,每個元素都是一個函數地址
AddressOfNames 指向一個DWORD數組首地址,共有NumberOfNames個元素,每個元素都是一個字符串(函數名字符串)首地址
AddressOfNameOrdinals指向一個WORD數組首地址,共有NumberOfNames個元素,每個元素都是一個函數序號
咱們說的最後倆數組,實際上是一種一一對應的關係,假如分別叫 dwNames[] 和 dwNamesOrdinals[],
假如dwNames[5]的值(這個指是一個地址,前面都說了)指向的字符串等於「GetValue」,那麼dwNamesOrdinals[5]的值(這個指是一個序號,前面都說了),就是GetValue導出函數的序號啦,那麼怎樣找到地址呢?
這時候就須要用到第一個數組了,假如名字叫dwFuncAddress[], GetValue的導出地址就是
dwFuncAddress[dwNamesOrdinals[5]] + 模塊基址
好了,囉嗦了這麼多,看代碼:數組
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
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);
//
這個是查一下是按照什麼方式(函數名稱or函數序號)來查函數地址的
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;
//
這個是經過以序號的方式來查函數地址的
xuhao:
if
(dwName < dwBase || dwName > dwBase + pImageExportDirectory->NumberOfFunctions - 1)
{
return
0;
}
return
(pAddressOfFunction[dwName - dwBase] + (DWORD)hModule);
}
|
好了,測試一下,
//咱們寫的函數返回的地址
DWORD dw1 = MyGetProcAddress(LoadLibrary("user32.dll"), "MessageBoxA");
//系統函數返回的地址
DWORD dw2 = (DWORD)GetProcAddress(LoadLibrary("user32.dll"), "MessageBoxA");
哈,發現同樣,忽然很是有成就感!!
再試試序號查找
//咱們寫的函數返回的地址
DWORD dw1 = MyGetProcAddress(LoadLibrary("user32.dll"), (LPCSTR)0x110);
//系統函數返回的地址
DWORD dw2 = (DWORD)GetProcAddress(LoadLibrary("user32.dll"), (LPCSTR)0x110);
咱們發現仍是同樣,成就感更大啦。。哈哈(其實kernel32.dll的0x110 是GetComputerNameExW的序號,本身能夠用LordPE查一下)
忽然有一天有人說 你的這個函數不行,而後給你舉了個例子,因而你測試了一下,下面是例子
DWORD a1 = (DWORD)MyGetProcAddress(LoadLibrary("kernel32.dll"), "HeapFree");
DWORD a2 = (DWORD)GetProcAddress(LoadLibrary("kernel32.dll"), "HeapFree");
因而 咱們就苦思冥想,依然不得其解。。。
可是我發現a1表示的地址的內容是一個字符串 "NTDLL.RtlFreeHeap"彷佛不能用巧合來講這個問題,難道是返回了這個字符串 還要咱們再Load一下Ntdll 而後再找一個RtlFreeHeap的地址嗎?好了先試驗一下 果真ntdll.dll中的 RtlFreeHeap的地址 和a2的值的同樣的,
彷佛印證了什麼東西,
好吧 OD拿來 開啓逆向 kernel32.GetProcAddress 搞了一會頭暈了,看不出頭緒
我老大頗有才,他去翻了翻win2000的源碼 偶也 一目瞭然
Win2K 源碼app
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
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;
[COLOR=
"DarkOrange"
]
//
+ by blueapplez
//
這應該是按函數名稱查找
//
+ by blueapplez[
/COLOR
]
if
( (ULONG_PTR)lpProcName > 0xffff ) {
RtlInitString(&ProcedureName,lpProcName);
Status = LdrGetProcedureAddress(
BasepMapModuleHandle( hModule, FALSE ),
&ProcedureName,
0L,
&ProcedureAddress
);
}
[COLOR=
"DarkOrange"
]
//
+ by blueapplez
//
這應該是按函數序號查找
//
+ by blueapplez[
/COLOR
]
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 是什麼呢?
繼續看ide
1
2
3
4
5
6
7
8
9
10
11
12
|
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 就太長了 先主要說一下 他裏面的一個關鍵函數 LdrpSnapThunk的關鍵的一段,能不能看懂我就無論了 反正我基本看不懂
函數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
else
{
Addr = (PULONG)((ULONG_PTR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
Thunk->u1.Function = ((ULONG_PTR)DllBase + Addr[OrdinalNumber]);
[COLOR=
"DarkOrange"
]
//
+ by blueapplez
//
這段是判斷一下返回的地址是否越界
//
越界了 就返回原來的值
//
+ by blueapplez[
/COLOR
]
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;
[COLOR=
"DarkOrange"
]
//
+ by blueapplez
//
若是沒有越界,就從那個地址中查找
'.'
,找到了就再作一次Load Dll,和GetProcAddress
//
沒有找到
'.'
就返回原來的值
//
+ by blueapplez[
/COLOR
]
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
|
好了 到此已經告一段落, 下面給出源碼,有人會問,怎樣作的dll纔會出現查找導出表的地址的時候返回一個字符串呢? 其實就是在 .def文件的EXPORTS後加一句就成(這個我老大試驗了n久才搞定)加上 MsgBox = user32.MessageBoxA
測試
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
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);
//
這個是查一下是按照什麼方式(函數名稱or函數序號)來查函數地址的
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;
}
}
//
這個是經過以序號的方式來查函數地址的
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);
}
|
如今測試下
DWORD a1 = (DWORD)MyGetProcAddress(LoadLibrary("kernel32.dll"), (LPCSTR)"HeapFree");
DWORD a2 = (DWORD)GetProcAddress(LoadLibrary("kernel32.dll"), (LPCSTR)"HeapFree");
發現值同樣了
ps.如發現問題 歡迎批評指正。code