DllMain与rundll32详解
0x00 DllMain
对于动态链接库,DllMain是一个可选的入口函数。
程序调用DLL中的导出函数的流程
- 程序调用Windows API
LoadLibrary
- 操作系统寻找LoadLibrary所加载的DLL文件
- 操作系统将DLL文件加载至目标程序进程的内存空间
- 程序自动调用
DllMain
函数 - 程序调用DLL中导出函数
如果DLL被多次LoadLibrary
,那么DllMain
只执行一次,引用基数+1。
DllMain的原型
|
|
0x01 DllMain调用原因
其中fdwReason
用来表示Dll被调用的状态,一共分为四种:
- DLL_PROCESS_ATTACH 被进程加载
- DLL_PROCESS_DETACH 被进程释放
- DLL_THREAD_ATTACH 被线程加载
- DLL_THREAD_DETACH 被线程释放
在DllMain中通常为了捕获DLL被调用的方式来进行某些动作:
|
|
0x02 那些用于测试DLL劫持的DllMain POC
在测试很多DLL劫持的漏洞时,都喜欢用DllMain,是因为DllMain被进程加载或者释放都会自动调用运行。
|
|
0x03 __declspec
__declspec
是Microsoft VC中专用的关键字,它配合着一些属性可以对标准C/C++进行扩充。__declspec
关键字应该出现在声明的前面。
__declspec(dllexport)
用于Windows中的动态库中,声明导出函数、类、对象等供外面调用,省略给出.def文件。即将函数、类等声明为导出函数,供其它程序调用,作为动态库的对外接口函数、类等。
__declspec(dllimport)
用于Windows中,从别的动态库中声明导入函数、类、对象等供本动态库或exe文件使用。当你需要使用DLL中的函数时,往往不需要显示地导入函数,编译器可自动完成。
0x04 关于rundll32
Rundll32.exe是什么?顾名思意,“执行32位的DLL文件”。它的作用是执行DLL文件中的内部函数,这样在进程当中,只会有Rundll32.exe,而不会有DLL后门的进程,这样,就实现了进程上的隐藏。
– 百度百科
经过如上解释,可以总结为,rundll32能够运行一个32位的dll文件,并且在进程列表中只能看到rundll32.exe,但是遍历rundll32.exe的模块列表可以看到进程加载的dll。
并不是所有的dll都能够被rundll32运行。 为何这么说,因为rundll32只支持特定的函数声明方式,并且该函数必须在dll文件的导出表中。关于导出表的知识可以阅读 - 《Windows PE权威指南》 👍
rundll32.exe支持的函数原型如下:
|
|
通过原型可以发现,与Windows平台下的WinMain
一模一样,可以理解为rundll32调用的函数就是一段代码的入口函数。
下面来写一个支持rundll32运行的dll:
- 使用visual studio新建一个dll工程,并声明函数:
rundll
|
|
- 使用rundll32运行dll
通过进程列表查看:
使用wmic process get commandline
也可以获得命令行详情:
还有一种方式是获得该进程加载的模块,这是最精确的了;但windows 7不能直接看到,需要借助工具。(win10可以通过任务查看器看到..)
这个dll是没有DllMain也能够正常运行的,并不是所有的dll都有DllMain这个函数
我经过实验后发现,一个dll有没有DllMain完全取决于当前这个dll对于功能的需求,如果对装载方式的状态没有特定的要求,完全可以省去编写DllMain,因为DllMain不是一个导出函数,永远是一个由系统被动调用的函数。