Windows

钩子 - Windows hook

编程技术的钩子就是在等待捕获系统中的某个消息或者动作

应用程序可以通过设置Hook对某个进程或窗口进行监视,即:对特定事件“挂钩”;一旦预定义特定事件发生,Windows操作系统即会向钩子hook发送通知消息,这时,应用程序可进行响应。

钩子类型

1
2
3
4
5
6
7
8
9
10
11
12
13
WH_CALLWNDPROC :系统将消息发送到指定窗口之前的“钩子”
WH_CALLWNDPROCRET Hooks :消息已经在窗口中处理的“钩子”
WH_CBT Hook :基于计算机培训的“钩子”
WH_DEBUG Hook :差错“钩子”
WH_FOREGROUNDIDLE Hook :前台空闲窗口“钩子”
WH_GETMESSAGE Hook :接收消息投递的“钩子”
WH_JOURNALPLAYBACK Hook :回放以前通过WH_JOURNALRECORD“钩子”记录的输入消息
WH_JOURNALRECORD Hook :输入消息记录“钩子”
WH_KEYBOARD Hook :键盘消息“钩子”
WH_MOUSE Hook :鼠标消息“钩子”
WH_MSGFILTER :对话框、消息框、菜单或滚动条输入消息“钩子”
WH_SYSMSGFILTER Hooks :系统消息“钩子”
WH_SHELL Hook :外壳“钩子”

img

局部钩子:在自身进程中创建一个Hook,可以用来捕获自身进程中的消息

全局钩子:可以捕获操作系统下所有的指定的消息,需要借助于DLL来实现

全局钩子必须要使用DLL,DLL存放了钩子函数的代码。 在操作系统中安装了全局钩子后,只要进程收到可以发出钩子的消息后,全局钩子的DLL文件会被操作系统自动或强行地加载到该进程中。

局部钩子:

安装钩子:

img

参数 idHook:指示欲被安装的挂钩处理过程之类型

参数 lpfn:指向相应的挂钩处理过程

参数 hMod:指示了一个动态链接的句柄,该动态连接库包含了参数lpfn 所指向的挂钩处理过程

参数 dwThreadId:指示了一个线程标识符,挂钩处理过程与线程相关

img

SetWindowsHookEx函数一起使用的应用程序定义或库定义的回调函数

参数1:挂钩过程用于确定如何处理消息的代码。

参数2:生成 击键 消息的密钥的虚拟密钥代码。

参数3:重复计数、扫描代码、扩展键标志、上下文代码、以前的键状态标志和转换状态标志。

创建一个进程:

img

HookTestDlg.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
HHOOK g_hHook;
LRESULT CALLBACK MyKeyboardProc(
int code, // hook code
WPARAM wParam, // virtual-key code
LPARAM lParam // keystroke-message information
)
{
if (code < 0)
{
return CallNextHookEx(g_hHook, code, wParam, lParam);
}
CString strFmt;
strFmt.Format("HT:%c", wParam);
OutputDebugString(strFmt);
return CallNextHookEx(g_hHook, code, wParam, lParam);
}


void CHookTestDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
g_hHook = SetWindowsHookEx(
WH_KEYBOARD,
MyKeyboardProc,
NULL,
GetCurrentThreadId()
);
if (g_hHook == NULL)
{
AfxMessageBox("局部钩子安装失败");
}

}

HookTestDlg.h:

1
2
3
4
5
// 实现
protected:
HICON m_hIcon;
HHOOK m_hHook; //添加的

局部卸载:

img

参数 hhk:要移除的钩子的句柄,SetWindowsHookExA的返回值

1
2
3
4
5
void CHookTestDlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
UnhookWindowsHookEx(g_hHook);
}

安装全局钩子和卸载:

添加Dll

img

dllmain.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"

HHOOK g_hHook;
LRESULT CALLBACK MyKeyboardProc(
int code, // hook code
WPARAM wParam, // virtual-key code
LPARAM lParam // keystroke-message information
)
{
if (code < 0)
{
return CallNextHookEx(g_hHook, code, wParam, lParam);
}

char szBuff[MAXBYTE];
wsprintf(szBuff, "HT:%c", wParam);
OutputDebugString(szBuff);
return CallNextHookEx(g_hHook, code, wParam, lParam);
}


__declspec(dllexport) BOOL InstallHook() {
g_hHook = SetWindowsHookEx(
WH_KEYBOARD,
MyKeyboardProc,
GetModuleHandle("HookDll"),
0
);
return g_hHook != NULL;
}

__declspec(dllexport) VOID UnInstallHook() {
UnhookWindowsHookEx(g_hHook);
}

BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}


HookTestDlg.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__declspec(dllimport) BOOL InstallHook();
__declspec(dllimport) VOID UnInstallHook();
#pragma comment(lib,"./x64/Debug/HookDll.lib")

void CHookTestDlg::OnBnClickedButton3()//全局安装
{
// TODO: 在此添加控件通知处理程序代码
if (!InstallHook()) {
AfxMessageBox("安装全局钩子失败");
}
}
void CHookTestDlg::OnBnClickedButton4()//全局卸载
{
// TODO: 在此添加控件通知处理程序代码
UnInstallHook();
}

服务 - Service

打开服务管理器:

img

img

SC :用来与服务控制管理器和服务进行通信的命令行程序

img

创建一个MFC的进程:

img

OpenSCManager

img

与指定计算机上的服务控制管理器建立连接,并打开指定的服务控制管理器数据库

参数 lpMachineName:目标计算机的名称。

参数lpDatabaseName:服务控制管理器数据库的名称。

参数dwDesiredAccess:对服务控制管理器的访问。

创建服务:

img

创建服务对象并将其添加到指定的服务控制管理器数据库

参数hSCManager:服务控制管理器数据库的句柄

参数lpServiceName:要安装的服务的名称

参数lpDisplayName:用户界面程序用于标识服务的显示名称

参数 dwDesiredAccess:对服务的访问权限

参数dwServiceType:服务类型

参数dwStartType:服务启动选项

参数dwErrorControl:错误的严重性,以及此服务无法启动时采取的操作

参数lpBinaryPathName:服务二进制文件的完全限定路径

参数 lpLoadOrderGroup:此服务所属的加载排序组的名称

参数lpdwTagId:指向变量的指针,该变量接收在 lpLoadOrderGroup 参数中指定的组中唯一的标记值
参数lpDependencies:指向以 null 分隔的服务名称或加载排序组的双 null 终止数组的指针,系统必须在此服务之前启动这些名称

参数lpServiceStartName:运行服务的帐户的名称

参数lpPassword:lpServiceStartName 参数指定的帐户名的密码

img

创建一个控制台程序来写服务器:

img

1
2
3
4
5
6
7
8
9
// MyService.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <windows.h>

int main()
{
OutputDebugString( "Hello World!");
}

复制exe的路径

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// SCPDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "SCP.h"
#include "SCPDlg.h"
#include "afxdialogex.h"
#include<Windows.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif

protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持

// 实现
protected:
DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CSCPDlg 对话框



CSCPDlg::CSCPDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_SCP_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CSCPDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CSCPDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, &CSCPDlg::OnBnClickedButton1)
END_MESSAGE_MAP()


// CSCPDlg 消息处理程序

BOOL CSCPDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();

// 将“关于...”菜单项添加到系统菜单中。

// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);

CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != nullptr)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}

// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标

// TODO: 在此添加额外的初始化代码

return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}

void CSCPDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}

// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。

void CSCPDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文

SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;

// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CSCPDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}


#include<winsvc.h>
void CSCPDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL) {
AfxMessageBox("打开SCM失败");
return;
}
SC_HANDLE hService = CreateService(
hSCM,
m_szServiceName,
m_szServiceName,
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
"C:\\vs\\Windows14\\MyService\\x64\\Release\\MyService.exe",
NULL,
NULL,
NULL,
NULL,
NULL
);
if (hService == NULL) {
AfxMessageBox("创建服务失败");
return;
}
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
AfxMessageBox("创建服务成功");
}

运行结果:

img

如果服务已存在的话 就无法创建

但是启动服务是没有相应的

img

img

1:初始化所有的全局变量
2:注册Handler函数来处理对该服务的控制请求
3:执行初始化
4:初始化结束之后,调用SetServiceStatus将服务状态设置为SERVICE_RUNNING并指定服务准备接受的状态码
5:执行具体的任务,如果没有要执行的具体事务,将控制权返回给调用方

MyService.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// MyService.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <windows.h>

VOID WINAPI ServiceMain(DWORD dwArgc, // number of arguments
LPTSTR* lpszArgv // array of arguments);
) {
OutputDebugString("MSVC: ServiceMain Begin");
}
int main()
{
OutputDebugString( "MSVC: Hello World!");
}

启动服务没有调用servicemain

img

调用servicemain

img

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// MyService.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <windows.h>

char g_szServiceName[MAXBYTE] = { "myservice" };
VOID WINAPI ServiceMain(DWORD dwArgc, // number of arguments
LPTSTR* lpszArgv // array of arguments);
) {
OutputDebugString("MSVC: ServiceMain Begin");
}
int main()
{
SERVICE_TABLE_ENTRY ste[] = {
{g_szServiceName,ServiceMain},
{NULL,NULL}
};

if (StartServiceCtrlDispatcher(ste)) {
OutputDebugString("MSVC: StartServiceCtrlDispatcher sucess");
}
else
OutputDebugString("MSVC: StartServiceCtrlDispatcher failed");
}

img

设置服务运行的状态:

img

参数1:当前服务的状态信息结构的句柄

参数2:指向SERVICE_STATUS 结构的指针包含调用服务的最新状态信息

句柄获取:

img

参数1:由调用线程运行的服务的名称

参数2:指向要注册的处理程序函数的指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// MyService.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <windows.h>
char g_szServiceName[MAXBYTE] = { "myservice" };
SERVICE_STATUS_HANDLE g_hStatus = NULL;
VOID WINAPI MyHandler(DWORD fdwControl)
{
SERVICE_STATUS ss = {};
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ss.dwCurrentState = SERVICE_RUNNING;
ss.dwControlsAccepted = SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_STOP;
switch (fdwControl)
{
case SERVICE_CONTROL_CONTINUE:
OutputDebugString("MSVC: SERVICE_CONTROL_CONTINUE");
break;
case SERVICE_CONTROL_PAUSE:
OutputDebugString("MSVC: SERVICE_CONTROL_PAUSE");
ss.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_STOP:
OutputDebugString("MSVC: SERVICE_CONTROL_STOP");
ss.dwCurrentState = SERVICE_STOPPED;
default:
break;
}
if (SetServiceStatus(g_hStatus, &ss))
{
OutputDebugString("MSVC: SetServiceStatus success");
}
else
{
OutputDebugString("MSVC: SetServiceStatus failed");
}
}
VOID WINAPI ServiceMain(
DWORD dwArgc,
LPTSTR* lpszArgv
)
{
OutputDebugString("MSVC: ServiceMain Begin");
//注册接受控制码的回调函数
g_hStatus = RegisterServiceCtrlHandler(g_szServiceName, MyHandler);
if (g_hStatus == NULL)
{
OutputDebugString("MSVC: RegisterServiceCtrlHandler failed");
return;
}
//初始化

//通知SCM,我已经初始化完了,开始运行了
SERVICE_STATUS ss = {};
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ss.dwCurrentState = SERVICE_RUNNING;
ss.dwControlsAccepted = SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_STOP;
if (SetServiceStatus(g_hStatus, &ss))
{
OutputDebugString("MSVC: SetServiceStatus success");
}
else
{
OutputDebugString("MSVC: SetServiceStatus failed");
}
}
int main()
{
OutputDebugString("MSVC: Hello World!");
SERVICE_TABLE_ENTRY ste[] = {
{g_szServiceName, ServiceMain},
{NULL, NULL}
};
if (StartServiceCtrlDispatcher(ste))
{
OutputDebugString("MSVC: StartServiceCtrlDispatcher success");
}
else
{
OutputDebugString("MSVC: StartServiceCtrlDispatcher failed");
}
}

启动服务:

img

img

停止服务:

img

img

恢复服务:

img

删除服务:

1
sc delete myservice(要删除的服务名称)//以管理员权限运行cmd,输入

img

img