完美实现自己的GetProcAddress函数(转载)
我们知道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;
具体什么意思呢?
啰嗦一下
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);
}
好了,测试一下,
//我们写的函数返回的地址
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 源码
代码:
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
//这应该是按函数名称查找
//+ by blueapplez
if ( (ULONG_PTR)lpProcName > 0xffff ) {
RtlInitString(&ProcedureName,lpProcName);
Status = LdrGetProcedureAddress(
BasepMapModuleHandle( hModule, FALSE ),
&ProcedureName,
0L,
&ProcedureAddress
);
}
//+ by blueapplez
//这应该是按函数序号查找
//+ 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 是什么呢?
继续看
代码:
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的关键的一段,
能不能看懂我就不管了 反正我基本看不懂
代码:
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
//如果没有越界,就从那个地址中查找'.',找到了就再做一次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
好了 到此已经告一段落, 下面给出源码,有人会问,怎样做的dll才会出现查找导出表的地址的时候返回
一个字符串呢? 其实就是在 .def文件的EXPORTS后加一句就成(这个我老大试验了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);
//这个是查一下是按照什么方式(函数名称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.如发现问题 欢迎批评指正。
完美实现自己的GetProcAddress函数(转载)的更多相关文章
- JavaScript 数组操作函数--转载+格式整理
JavaScript 数组操作函数(部分)--转载+格式整理 今天看了一篇文章,主要讲的对常用的Js操作函数:push,pop,join,shift,unshift,slice,splice,conc ...
- C++析构函数定义为虚函数(转载)
转载:http://blog.csdn.net/alane1986/article/details/6902233 析构函数执行时先调用派生类的析构函数,其次才调用基类的析构函数.如果析构函数不是虚函 ...
- MySQL常用函数 转载
一.数学函数ABS(x) 返回x的绝对值BIN(x) 返回x的二进制(OCT返回八进制,HEX返回十六进制)CEILING(x) 返 ...
- smarty函数-转载
Smarty常用函数 2009-08-13 14:05:55| 分类: Php |举报 |字号 订阅 1 .include_once语句: 引用文件路径,路径必需正确. eg:include ...
- Java中的Random()函数-----转载
Java中的Random()函数 (2013-01-24 21:01:04) 转载▼ 标签: java random 随机函数 杂谈 分类: Java 今天在做Java练习的时候注意到了Java里面的 ...
- MySql常用日期函数(转载)
/*date_add(date,interval expr type)和date_sub(date,interval expr type)执行日期运算. date 是一个 datetime 或date ...
- mysql字符串函数(转载)
对于针对字符串位置的操作,第一个位置被标记为1. ASCII(str) 返回字符串str的 最左面字符的ASCII代码值.如果str是空字符串, 返回0.如果str是NULL,返回NULL. mysq ...
- GJM :Sql 各种语句 以及函数 [转载]
版权声明:本文原创发表于 [请点击连接前往] ,未经作者同意必须保留此段声明!如有侵权请联系我删帖处理! 1.更改数据库的名称 2.表中有数据的情况下再添加列.删除列 3.在SQLServer 中各种 ...
- EL表达式中fn函数 (转载)
JSTL 使用表达式来简化页面的代码,这对一些标准的方法,例如bean的getter/setter方法,请求参数或者context以及 session中的数据的访问非常方便,但是我们在实际应用中经常需 ...
随机推荐
- 转:如何学习SQL(第一部分:SQL基础)
转自:http://blog.163.com/mig3719@126/blog/static/285720652010950712271/ 1. 为什么学习SQL 自人类社会形成之日起,社会的运转就在 ...
- D3.js 选择元素和绑定数据/使用数据
选择元素和绑定数据是 D3 最基础的内容,本文将对其进行一个简单的介绍. 一.如何选择元素 在 D3 中,用于选择元素的函数有两个: d3.select():是选择所有指定元素的第一个 d3.sele ...
- Redhat 6.4_联网 yum 配置
步骤简述----------------------------------------------------------------- 1. 准备软件包 下载第三步的软件包即可.由于是联网yum配 ...
- TCP拥塞控制
TCP必须使用端到端拥塞控制而不是使网络辅助的拥塞控制,因为IP层不向端系统提供显式的网络拥塞反馈.TCP采用的方法是让每一个发送方根据所感知到的网络拥塞程度来限制其能向连接发送流量的速率. 几个相关 ...
- tomcat集群配置
http://www.finereporthelp.com/help/24/1/3/3.html#a:4.1
- phalcon: acl权限控制
目录控制: public/index.php: $di['aclResource']=function(){ return include_once '../app/config/frontbackA ...
- B2车
晚上10点之后,杭州快速公交B2的司机,把2节车厢,开的像是跑车一样,每次启动都是弹射出去的,好像是在报复白天蜗牛般的速度.真乃是见神杀神,见佛杀佛.
- javaScript DOM编程
1.DOM概述 1.1. 什么是DOM? DOM= Document Object Model,文档对象模型, DOM 是 W3C(万维网联盟)的标准.DOM 定义了访问 HTM ...
- hdu 1021
刚开始直接按题意来写,WA了,唉,果然经验不够..然后开始找规律,本来一看到这种题,第一反应就是规律题,然后看看题意,貌似没啥规律哦!就像当时学DP一样,总是想当然被智商压制了啊喂! #include ...
- robotframework笔记19
后处理输出 使用时自动测试 在测试执行报告和日志生成,并使用它 分别允许创建自定义报告和日志以及结合 和合并的结果. 使用Rebot 简介 rebot [options] robot_outputs ...