内容包含下列几部份:
一。 DLL创建
a. __declspec(dllexport) 关键字导出函数
b. DEF文件方式导出函数名或仅导出函数序号
二。DLL调用
a. 静态方式调用
b. 动态方式调用
c. 仅函数序号方式的调用
三。如何劫持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
b. DEF文件方式导出函数名或仅导出函数序号
1. 编译DLL代码
#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
2. 创建DEF文件:testdll.def
LIBRARY "testdll.lib" ;导出模块名
DESCRIPTION "DEF文件方式"
EXPORTS ;关键字,后面要跟导出函数信息
Test @2 ;导出Test函数,导出序号为2
;Test @3 noname ;导出函数Test,导出序号为3,并不导出函数名
各种情况下的导出效果对比:
不使用DEF文件的效果如下:
当为 "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;
}
///
c. 仅函数序号方式的调用
例子对应DEF方式生成库时" Test @3 noname ;导出函数Test,导出序号为3,并不导出函数名"创建的DLL库
#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
在不知道一个EXE源码的情况下,将自己写的代码注入到其中,有两种方法:
1. 直接向对方进程空间使用写内存的方式写入代码
2. 将自己的代码写入到DLL文件中,然后通过劫持EXE所调用的DLL的方式来达到目地。
劫持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环境变量中列出的目录
具体实现步骤如下:
2. 将其剪到指定目录下: E:\\XclCode\\testdll\\dll
3. 创建一个名字一样的DLL工程,创建testdll.dll库
例子代码如下:
#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
其中关于函数,有两种方法可以参考下。
4. 创建一个调用测试程序,可参照" b. 动态方式调用"例子的代码
5. 将第三步创建的劫持的dll,放入到与第四步创建的exe同一目录下
6. 动行即可看到,MessageBox被弹出了两次
如何解决DLL劫持问题,官网有完整的说明:
http://msdn.microsoft.com/en-us/library/ms682586%28v=vs.85%29.aspx
MAIL: xcl_168@aliyun.com
BLOG: http://blog.csdn.net/xcl168