(1)HHOOK是全局唯一的,可以跨进程使用
(2)当创建钩子指定的函数第一次执行的时候,会创建一个线程
(3)当一个线程调用GetMessage的时候会转换为GUI线程,会有一个对应的消息队列,可以通过这个消息队列来传递我们的指令
(4)往某一个指定线程发送消息用PostThreadMessage
(5)当卸载钩子的时候,要确保我们在钩子函数中创建的消息退出,因为卸载钩子会导致DLL卸载,然后如果线程没有结束,会导致违规访问。可以通过消息队列将我们创建的Id传给我们的线程,然后用OpenThread打开对应的句柄,然后调用WaitForSingleObject
(6)由于一个DLL被映射到不同地址空间会有写时复制,导致进程之间数据共享困难,可以通过共享段。
(7)在Realse模式下编译运行
该项目主要有一个exe和一个DLL,这个DLL中有设置钩子以及对应的钩子函数。
先看DISPLib.DLL的实现
DISPLib.h代码:
#pragma once
#define MSG_INJ_SAVE_OK 1232
#define MSG_INJ_OK 1233
#define MSG_INJ_SAVE 1234
#define MSG_INJ_EXIT 1235
#ifdef DISPLIBAPI
#define DISPLIBAPI extern "C" __declspec(dllexport)
#else
#define DISPLIBAPI extern "C" __declspec(dllimport)
#endif // DEBUG
DISPLIBAPI BOOL SetDIPSHook(DWORD dwThreadId);
DISPLib.cpp代码:
#include "pch.h"
#include <windowsx.h>
#include <CommCtrl.h>
#include <string>
#include <sstream>
#include <fstream>
#include "DIPSLib.h"
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam);
DWORD WINAPI ThreadFunc(LPVOID);
void SaveListViewItemPosition(HWND hWndLV);
#pragma data_seg("Shared")
HHOOK g_hHook = NULL;
DWORD g_dwThreadIdDIPS = 0;
#pragma data_seg()
#pragma comment(linker,"/Section:Shared,rws")
HINSTANCE g_hInstDll = NULL;
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
//保存当前地址空间中DLL的基址
g_hInstDll = hModule;
break;
}
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
BOOL WINAPI SetDIPSHook(DWORD dwThreadId)
{
BOOL bOk = FALSE;
if (dwThreadId > 0)
{
if (g_hHook != NULL)
return FALSE;
//记录当前的线程ID
g_dwThreadIdDIPS = GetCurrentThreadId();
//向远程线程设置挂钩
g_hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hInstDll, dwThreadId);
bOk = (g_hHook != NULL);
if (bOk)
{
bOk = PostThreadMessage(dwThreadId, WM_NULL, 0, 0);
}
}
else
{
if (g_hHook == NULL)
return FALSE;
bOk = UnhookWindowsHookEx(g_hHook);
}
return bOk;
}
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
static BOOL bCrearteThreadSuccess = TRUE;
if (bCrearteThreadSuccess)
{
HANDLE hThreadHndle = CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL);
if (hThreadHndle != NULL)
{
bCrearteThreadSuccess = FALSE;
CloseHandle(hThreadHndle);
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
void SaveListViewItemPosition(HWND hWndLV)
{
int nMaxItems = ListView_GetItemCount(hWndLV);
std::wofstream outFile;
outFile.open("D:/DesktopIconPosition.txt", std::ios::out);
if (outFile.good())
{
outFile.imbue(std::locale("chs"));
outFile << L"Item Count:" << nMaxItems << std::endl;
for (int i = 0; i < nMaxItems; i++)
{
TCHAR szName[MAX_PATH] = { 0 };
ListView_GetItemText(hWndLV, i, 0, szName, _countof(szName));
POINT pt;
ListView_GetItemPosition(hWndLV, i, &pt);
outFile << szName << L": x =" << pt.x << L" | y =" << pt.y << std::endl;
}
outFile.close();
}
}
DWORD WINAPI ThreadFunc(LPVOID)
{
DWORD dwCurThreadId = GetCurrentThreadId();
//个DISP发送一个消息,表明线程创建成功
//如果目标线程还没有对应的消息队列,则发送失败
while (!PostThreadMessage(g_dwThreadIdDIPS, MSG_INJ_OK, (WPARAM)dwCurThreadId, 0))
{
Sleep(1000);
}
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
if (msg.message == MSG_INJ_SAVE)
{
if (msg.lParam)
{
SaveListViewItemPosition((HWND)msg.wParam);
break;
}
}
}
return 0;
}
#include "pch.h"中就两行代码:
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>
DISP.exe实现:
DISP.cpp代码:
#include "framework.h"
#include "..\\DISPLib\DIPSLib.h"
#include <windowsx.h>
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
HWND hWndLV = GetFirstChild(GetFirstChild(FindWindow(TEXT("ProgMan"), NULL)));
if (!IsWindow(hWndLV))
{
MessageBox(NULL, L"hWndLV is not Windows", L"Info", MB_OK);
return 1;
}
if (!SetDIPSHook(GetWindowThreadProcessId(hWndLV, NULL)))
{
MessageBox(NULL, L"Set Hook Failure", L"Info", MB_OK);
return 1;
}
DWORD dwInjThreadId = 0;
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
if (msg.message == MSG_INJ_OK)
{
dwInjThreadId = (DWORD)msg.wParam;
while (!PostThreadMessage(dwInjThreadId, MSG_INJ_SAVE, (WPARAM)hWndLV, 1))
{
Sleep(1000);
}
}
//这个确认逻辑可以不要,后面的WaitForSingleObject更加保险,但是考虑到OpenThread可能失败,加了这一个保险,虽然这个逻辑不能保证卸载钩子时线程肯定退出了,但是大部分是ok的
else if(msg.message == MSG_INJ_SAVE_OK)
{
break;
}
}
HANDLE hInjThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, dwInjThreadId);
DWORD wRet = WaitForSingleObject(hInjThreadHandle,INFINITE);
CloseHandle(hInjThreadHandle);
BOOL bOk = SetDIPSHook(0);
if (bOk && wRet == WAIT_OBJECT_0)
{
MessageBox(NULL, L"钩子正常卸载", L"Info", MB_OK);
}
return 0;
}
#include "framework.h"中的代码如下:
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>
// C 运行时头文件
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>