首页
社区
课程
招聘
[原创]完美实现GetProcAddress
发表于: 2010-9-27 22:11 41814

[原创]完美实现GetProcAddress

2010-9-27 22:11
41814

我们知道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]] + 模块基址
好了,啰嗦了这么多,看代码:

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);
}
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;
            }
        }
}

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

收藏
免费 11
支持
分享
最新回复 (38)
雪    币: 538
活跃值: (269)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
  哈哈  我正在找这东西  沙发~~~
2010-9-27 22:13
0
雪    币: 1163
活跃值: (137)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
3
收藏了,谢谢分享
2010-9-27 22:48
0
雪    币: 425
活跃值: (205)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
有打包好的源码吗?
2010-9-28 11:25
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
很好很强大。。。。。
2010-11-9 23:13
0
雪    币: 45
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
之前写的时候找过的资料,在这里贴一下做个备份,里面有讲DLL转发时的情况
http://bbs.pediy.com/showthread.php?t=4280
感谢lz...
2011-2-24 10:42
0
雪    币: 13
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
很好很强大 很实用 楼主辛苦
2011-2-24 10:57
0
雪    币: 1149
活跃值: (888)
能力值: ( LV13,RANK:260 )
在线值:
发帖
回帖
粉丝
8
感谢分享。。。。以前以为GetProcAddress只是在image中搜iat 取出就完了。。。看了后,才发现此函数还完成了dll 的dll ==。。。
2011-2-24 12:44
0
雪    币: 367
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
感谢楼主无私的分享。

谢谢咯

灰常之强大,收藏
2011-3-6 16:48
0
雪    币: 5327
活跃值: (3719)
能力值: ( LV13,RANK:283 )
在线值:
发帖
回帖
粉丝
10
DLL转发的情况确实容易忽略,多谢多谢
2011-8-31 23:48
0
雪    币: 191
活跃值: (345)
能力值: ( LV9,RANK:450 )
在线值:
发帖
回帖
粉丝
11
mark 一下
2011-9-1 00:21
0
雪    币: 563
活跃值: (95)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
挺有用收下
2012-2-17 15:21
0
雪    币: 15
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
13
很好你强大啊! 收藏了
2012-2-17 16:14
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
感谢啊。支持了
2012-2-27 16:25
0
雪    币: 244
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
楼主 V5 。。。。
2012-2-29 15:34
0
雪    币: 656
活跃值: (448)
能力值: ( LV12,RANK:360 )
在线值:
发帖
回帖
粉丝
16
突然有一天有人说 你的这个函数不行,然后给你举了个例子,于是你测试了一下,下面是例子
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的值的一样的,


这个问题最近我也遇到过,按我当时的第一反应,我会这样做:
DWORD a1 = (DWORD)MyGetProcAddress(LoadLibrary("Ntdll .dll"), "HeapFree");


如函数 RtlMoveMemory
2013-2-10 21:38
0
雪    币: 104
活跃值: (27)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
17
牛X啊,偑服
2013-3-14 11:40
0
雪    币: 71
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
刚看到
谢谢
2013-3-14 12:19
0
雪    币: 8764
活跃值: (5240)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
19
很实用的技术。顶一下。
2013-3-14 13:07
0
雪    币: 215
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
学而时习之,不亦乐乎?
2013-3-15 09:08
0
雪    币: 77
活跃值: (48)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
mark
2014-1-16 16:55
0
雪    币: 2359
活跃值: (288)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
mark 一下
2014-1-17 22:48
0
雪    币: 3
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
很好 , 学习了。。
2014-5-15 22:55
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
win7下可以用么?
2014-7-8 18:29
0
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
25
就这么精了!!!!
2014-7-8 18:59
0
游客
登录 | 注册 方可回帖
返回
//