三。如何劫持DLL
一。 DLL创建
a. __declspec(dllexport) 关键字导出函数
代码如下:
#include "stdafx.h" #include <Windows.h> #include <tchar.h> #ifdef _MANAGED #pragma managed(push, off) #endif HANDLE g_hModule = NULL; //保存自己的模块句柄 //导出函数 extern "C" __declspec(dllexport) void Test() { TCHAR tcModulePath[MAX_PATH] = {0}; //获得自身的完整路径 GetModuleFileName( (HMODULE)g_hModule,tcModulePath,MAX_PATH); MessageBox(0,tcModulePath,_T("testdll的加载路径"),0); } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { g_hModule = hModule; //保存模块加载句柄 return TRUE; } #ifdef _MANAGED #pragma managed(pop) #endif
#include "stdafx.h" #include <tchar.h> #ifdef _MANAGED #pragma managed(push, off) #endif HANDLE g_hModule = NULL; //保存自己的模块句柄 // DEF文件方式,函数前不需要导出函数关键字 __declspec(dllexport) void Test() { TCHAR tcModulePath[MAX_PATH] = {0}; //获得自身的完整路径 GetModuleFileName( (HMODULE)g_hModule,tcModulePath,MAX_PATH); MessageBox(0,tcModulePath,_T("testdll的加载路径"),0); } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { g_hModule = hModule; //保存模块加载句柄 return TRUE; } #ifdef _MANAGED #pragma managed(pop) #endif
LIBRARY "testdll.lib" ;导出模块名 DESCRIPTION "DEF文件方式" EXPORTS ;关键字,后面要跟导出函数信息 Test @2 ;导出Test函数,导出序号为2 ;Test @3 noname ;导出函数Test,导出序号为3,并不导出函数名各种情况下的导出效果对比:
当为 "Test @2 ;导出Test函数,导出序号为2" 时:
当为 "Test @3 noname ;导出函数Test,导出序号为3,并且不导出函数名" 时 :
注意看"Ordinal"与"Function"的不同.
二。DLL调用
a. 静态方式调用
例子对应"a. __declspec(dllexport) 关键字导出函数"创建的DLL库
这种方式,需要.lib文件和.DLL文件。
例子中是用"#pragma comment(lib"方式,也可在工程属性中常规和输入中指定.lib及所在目录
#include "stdafx.h" //////////////////////////////////////////////////////////////////// //静态链接方式: #pragma comment(lib,"..\\debug\\testdll.lib") extern "C" void Test(); int _tmain(int argc, _TCHAR* argv[]) { Test(); return 0; } ///////////////////////////////////////////////////////////////////////
b. 动态方式调用
这种方式不需要.lib文件,只要知道DLL导出函数的格式就行了。
#include "stdafx.h" //////////////////////////////////////////////////////////////////// //动态链接方式: typedef void (* TEST)(); int _tmain(int argc, _TCHAR* argv[]) { HMODULE hDll = LoadLibraryA("testdll.dll"); if(hDll) { TEST test = (TEST)GetProcAddress(hDll,"Test"); if(test != NULL) { test(); } FreeLibrary(hDll); } return 0; } ///////////////////////////////////////////////////////////////////////
#include "stdafx.h" //////////////////////////////////////////////////////////////////// //DLL仅导出函数序号,没有导出函数名称的调用方式: typedef void (* TEST)(); int _tmain(int argc, _TCHAR* argv[]) { DWORD dwOrdinal = MAKELONG(3,0); //构造函数序号,低字节为序号,高字为0 HMODULE hDll = LoadLibraryA("testdll.dll"); if(hDll) { TEST test = (TEST)GetProcAddress(hDll,(LPCSTR)dwOrdinal); if(test != NULL) { test(); } FreeLibrary(hDll); } return 0; } ///////////////////////////////////////////////////////////////////////
劫持DLL就是第二种方式的实现方式.
可钻的空子,在于动态链接库文件的加载顺序下,只要让自定义的相同名字的DLL抢先被EXE文件找到,就达到目的了。
顺序如下:
windows xp sp2系统以上会默认开启SafeDllSearchMode,在这种模式下DLL文件的搜索顺序如下所示:
(1)可执行程序加载的目录(可理解为程序安装目录比如 E:\XclCode\testdll\testdll\debug)
(2)系统目录(即 %windir%\system32 )
(3)16位系统目录(即 %windir%\system)
(4)Windows目录(即 %windir%)
(5)当前目录(运行的某个文件所在目录,比如C:\Documents and Settings\Administrator\Desktop\test)
(6)PATH环境变量中列出的目录
具体实现步骤如下:
#include "stdafx.h" #include <string.h> #ifdef _MANAGED #pragma managed(push, off) #endif //这个函数指针保存原始dll导出函数的地址 typedef void (* FUNTEST)(); FUNTEST g_funTest = NULL; /////////////////////////////////////////////////////////////////////// /* //方法一: extern "C" __declspec(dllexport) void Test() { g_funTest(); } */ /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// /* //方法二: //__declspec(naked) : // 不生成栈平衡效验代码,栈空间开辟代码等调试代码,函数内部内嵌汇编代码直接跳转到原始DLL的导出函数中执行 extern "C" __declspec(naked) __declspec(dllexport) void Test() { __asm jmp dword ptr [g_funTest] } */ /////////////////////////////////////////////////////////////////////// BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { //进程加载时调用DLLMain if(ul_reason_for_call ==DLL_PROCESS_ATTACH) { //禁用进程内线程创建或退出而调用DllMain入口函数 DisableThreadLibraryCalls(hModule); //获取原始dll导出的函数地址并保存 char cDllPath[MAX_PATH] = {0}; //GetSystemDirectoryA( (LPSTR)cDllPath,MAX_PATH); //strcat(cDllPath," 1"); //采用导出函数序号的方式调用 MessageBoxA(0,"劫持成功 by xiongchuanliang! 可以为所欲为了。","劫持库",0); //被劫持的,实际要调用的DLL strcpy(cDllPath,"E:\\XclCode\\testdll\\dll\\testdll.dll"); HMODULE hDll = LoadLibraryA(cDllPath); g_funTest = (FUNTEST)GetProcAddress(hDll,"Test"); } return TRUE; } #ifdef _MANAGED #pragma managed(pop) #endif其中关于函数,有两种方法可以参考下。
如何解决DLL劫持问题,官网有完整的说明:
http://msdn.microsoft.com/en-us/library/ms682586%28v=vs.85%29.aspx
MAIL: [email protected]
BLOG: http://blog.csdn.net/xcl168