Dll
Dll就是我们通常所说的动态链接库(Dynamic Link Library),它和应用程序在本质是没有任何区别的,它也是一个可执行文件,它们都有相同的PE结构。
但是两者在应用上面还是有一些区别的,Dll一般作用exe应用程序的补充体,它是一个模块,用来被exe文件加载。
在Windows中有一部分exe文件也是能够被加载的,比如.oxc后缀的控件格式。
为什么大部分应用程序喜欢使Dll?
- 扩展了应用程序的特性;
- 简化了项目管理;
- 节省内存(一份Dll在系统中只有一份,需要用到这个Dll的程序,通过分页机制映射到当前进程中);
- 促进资源共享;
- 促进本地化;
- 解决各版本之间的差异。
Dll和应用程序共享同一个进程空间
- 在Dll中分配的内存必须由Dll来进行释放;
- 应用程序不会因为Dll的卸载而释放空间。
Dll入口函数
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:// 被加载的时候
// Dll的初始化
case DLL_THREAD_ATTACH:// 当应用程序中有新线程启动的时候
// 这个时间点非常重要,应用程序新启动的线程加载本Dll模块时才调用,一般是对于线程安全的设置
case DLL_THREAD_DETACH:// 被卸载的时候
case DLL_PROCESS_DETACH:// 某个线程终止的时候
// Dll的销毁
break;
}
return TRUE;
}
Dll导出函数
对于一个动态链接库来说,最重要的是它的导出函数,只有导出函数才能被调用它的应用程序来使用。导出函数的格式如下:
extern "C" bool _declspec(dllexport) OnHook();
键盘拦截器的实现
Dll的实现
#pragma once
// WindowsMsgHookDemo.h文件
typedef void(*pFunc)(char *str);
extern "C" bool _declspec(dllexport) OnHook();
extern "C" bool _declspec(dllexport) OnSetFunc(pFunc func);
extern "C" bool _declspec(dllexport) UnHook();
// WindowsHookDemo.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "WindowsMsgHookDemo.h"
#include <tchar.h>
#include <cstdio>
extern HMODULE g_hModule;
HHOOK g_hHook = nullptr;
pFunc g_myFunc;
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
if (code == HC_ACTION)
{
char str[MAXBYTE] = { 0 };
if (lParam & 0x80000000)
{
sprintf_s(str, "[%c] 键被抬起 - 键盘码 [%04d]\r\n", wParam, wParam);
}
else
{
sprintf_s(str, "[%c] 键被按下 - 键盘码 [%04d]\r\n", wParam, wParam);
}
g_myFunc(str);
}
return CallNextHookEx(g_hHook, code, wParam, lParam);
}
bool OnHook()
{
bool bRet = true;
if (g_hHook == nullptr)
{
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hModule, 0);
}
else
{
bRet = false;
}
return bRet;
}
bool OnSetFunc(pFunc func)
{
g_myFunc = func;
return true;
}
bool UnHook()
{
bool bRet = true;
if (g_hHook != nullptr)
{
UnhookWindowsHookEx(g_hHook);
g_hHook = nullptr;
}
else
{
bRet = false;
}
return bRet;
}
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
HMODULE g_hModule;
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hModule = hModule;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
测试程序
将上面生成的WindowsHookDemo.dll放到测试程序中,能够被加载。
// DllTestDemo.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <Windows.h>
typedef void(*pFunc)(char *str);
typedef bool (*FUNC)(pFunc pfunc);
void myPrint(char *str)
{
printf(str);
}
int main()
{
HMODULE hDll = LoadLibrary(L"WindowsHookDemo.dll");
FARPROC OnHook = GetProcAddress(hDll, "OnHook");
FUNC OnSetFunc = (FUNC)GetProcAddress(hDll, "OnSetFunc");
FARPROC UnHook = GetProcAddress(hDll, "UnHook");
OnSetFunc(myPrint);
OnHook();
printf("OnHook()......\r\n");
BOOL ret;
MSG msg;
while ((ret = GetMessage(&msg, nullptr, 0, 0)))
{
if (ret == -1)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnHook();
printf("UnHook()......\r\n");
return 0;
}
运行测试程序,就会将所有的键盘输入信息打印到控制台上面。
注意事项
这个键盘拦截器需要注意一个函数的使用:
HHOOK WINAPI SetWindowsHookEx(
_In_ int idHook,
_In_ HOOKPROC lpfn,
_In_ HINSTANCE hMod,
_In_ DWORD dwThreadId
);
我们监控的是键盘,所以还需要一个键盘钩子的回调函数,定义如下:
LRESULT CALLBACK KeyboardProc(
_In_ int code,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
这个函数一定要注意的是:
Remarks
An application installs the hook procedure by specifying the WH_KEYBOARD hook type and a pointer to the hook procedure in a call to the SetWindowsHookEx function.
This hook may be called in the context of the thread that installed it. The call is made by sending a message to the thread that installed the hook. Therefore, the thread that installed the hook must have a message loop.
如果加载这个Dll的应用程序没有消息循环,那么就不能正常运行,达不到预期的效果。
像上面的测试Dll程序时,我们自己写的exe程序,肯定有源码,在调试情况下LoadLibrary的时候会自动的将Dll附加到exe程序中,但是有的时候我们并没有exe文件的源码,也就是无法调试exe程序,如果我们想调试被加载的我们的Dll程序,就需要使用vs给我们提供的附加到进程的功能,如下图所示:
这样就能够进入到Dll设置的断点中了,达到了调试的目的。