求助,如何从内存加载动态库
1个回答
2018-03-20 · 知道合伙人软件行家
关注
展开全部
程序使用动态库DLL一般分为隐式加载和显式加载两种,分别对应两种链接情况。本文主要搜橘告讨论显式加载的技术问题。我们知道,要显式加载一个DLL,并取得其伍凯中导出的函数地址一般是通过如下步骤:
(1) 用LoadLibrary加载dll文件,获得该dll的模块句柄;
(2) 定义一个函数指针类型,并声明一个变量;
(3) 用GetProcAddress取得该dll中目标函数的地址,赋值给函数指针变量;
(4) 调用函数指针变量。
这个方法要求dll文件位于硬盘上面。现在假设我们的dll已经位于内存中世明,比如通过脱壳、解密或者解压缩得到,能不能不把它写入硬盘文件,而直接从内存加载呢?答案是肯定的。经过多天的研究,非法操作了N次,修改了M个BUG,死亡了若干脑细胞后,终于有了初步的结果,下面做个总结与大家共享。
一、加载的步骤
由于没有相关的资料说明,只能凭借感觉来写。首先LoadLibrary是把dll的代码映射到exe进程的虚拟地址空间中,我们要实现的也是这个。所以先要弄清楚dll的文件结构。好在这个比较简单,它和exe一样也是PE文件结构,关于PE文件的资料很多,阅读一番后,基本上知道了必须做的几个工作:
(1)判断内存数据是否是一个有效的DLL。这个功能通过函数CheckDataValide完成。原型是:
BOOL CMemLoadDll::CheckDataValide(void* lpFileData, int DataLength);
(2)计算加载该DLL所需的虚拟内存大小。这个功能通过函数CalcTotalImageSize完成。原型是:
int CMemLoadDll::CalcTotalImageSize();
(3)将DLL数据复制到所分配的虚拟内存块中。该功能通过函数CopyDllDatas完成。要注意段对齐。
void CMemLoadDll::CopyDllDatas(void* pDest, void* pSrc);
(4)修正基地重定位数据。这个功能通过函数DoRelocation完成。原型是:
void CMemLoadDll::DoRelocation( void *NewBase);
(5)填充该DLL的引入地址表。这个功能由函数FillRavAddress完成。原型是:
BOOL CMemLoadDll::FillRavAddress(void *pImageBase);
(6)根据DLL每个节的属性设置其对应内存页的读写属性。我这里做了简化,所有内存区域都设置成一样的读写属性。
(7)调用入口函数DllMain,完成初始化工作。这一步我一开始忽略了,所以总是发现自己加载的dll和LoadLibrary加载的dll有些不同(我把整块内存区域保存到两个文件中进行比较,够晕的)。只是最近猜想到还需要这一步。
(8)保存dll的基地址(即分配的内存块起始地址),用于查找dll的导出函数。从现在开始这个dll已经完全映射到了进程的虚拟地址空间,可以使用它了。
(9)不需要dll的时候,释放所分配的虚拟内存。
(1) 用LoadLibrary加载dll文件,获得该dll的模块句柄;
(2) 定义一个函数指针类型,并声明一个变量;
(3) 用GetProcAddress取得该dll中目标函数的地址,赋值给函数指针变量;
(4) 调用函数指针变量。
这个方法要求dll文件位于硬盘上面。现在假设我们的dll已经位于内存中世明,比如通过脱壳、解密或者解压缩得到,能不能不把它写入硬盘文件,而直接从内存加载呢?答案是肯定的。经过多天的研究,非法操作了N次,修改了M个BUG,死亡了若干脑细胞后,终于有了初步的结果,下面做个总结与大家共享。
一、加载的步骤
由于没有相关的资料说明,只能凭借感觉来写。首先LoadLibrary是把dll的代码映射到exe进程的虚拟地址空间中,我们要实现的也是这个。所以先要弄清楚dll的文件结构。好在这个比较简单,它和exe一样也是PE文件结构,关于PE文件的资料很多,阅读一番后,基本上知道了必须做的几个工作:
(1)判断内存数据是否是一个有效的DLL。这个功能通过函数CheckDataValide完成。原型是:
BOOL CMemLoadDll::CheckDataValide(void* lpFileData, int DataLength);
(2)计算加载该DLL所需的虚拟内存大小。这个功能通过函数CalcTotalImageSize完成。原型是:
int CMemLoadDll::CalcTotalImageSize();
(3)将DLL数据复制到所分配的虚拟内存块中。该功能通过函数CopyDllDatas完成。要注意段对齐。
void CMemLoadDll::CopyDllDatas(void* pDest, void* pSrc);
(4)修正基地重定位数据。这个功能通过函数DoRelocation完成。原型是:
void CMemLoadDll::DoRelocation( void *NewBase);
(5)填充该DLL的引入地址表。这个功能由函数FillRavAddress完成。原型是:
BOOL CMemLoadDll::FillRavAddress(void *pImageBase);
(6)根据DLL每个节的属性设置其对应内存页的读写属性。我这里做了简化,所有内存区域都设置成一样的读写属性。
(7)调用入口函数DllMain,完成初始化工作。这一步我一开始忽略了,所以总是发现自己加载的dll和LoadLibrary加载的dll有些不同(我把整块内存区域保存到两个文件中进行比较,够晕的)。只是最近猜想到还需要这一步。
(8)保存dll的基地址(即分配的内存块起始地址),用于查找dll的导出函数。从现在开始这个dll已经完全映射到了进程的虚拟地址空间,可以使用它了。
(9)不需要dll的时候,释放所分配的虚拟内存。
本回答被网友采纳
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询