钩子和服务

钩子-windows hook

image-20240715164202113

SetWindowsHookEx

image-20240715170952263

参数说明:

参数 idHook:指示欲被安装的挂钩处理过程之类型,类型如下。
WH_MSGFILTER(-1):安装一个挂钩处理过程,以监视由对话框、消息框、菜单条、或滚动条中的输入事件引发的消息.详情参见MessageProc挂钩处理过程.

WH_JOURNALRECORD(0):安装一个挂钩处理过程,对寄送至系统消息队列的输入消息进行纪录.详情参见JournalRecordProc挂钩处理过程.

WH_JOURNALPLAYBACK(1):安装一个挂钩处理过程,对此前由WH_JOURNALRECORD 挂钩处理过程纪录的消息进行寄送.详情参见 JournalPlaybackProc挂钩处理过程.

WH_KEYBOARD(2):安装一个挂钩处理过程对击键消息进行监视. 详情参见KeyboardProc挂钩处理过程.

WH_GETMESSAGE(3):安装一个挂钩处理过程对寄送至消息队列的消息进行监视,详情参见 GetMsgProc 挂钩处理过程.

WH_CALLWNDPROC(4): 安装一个挂钩处理过程,在系统将消息发送至目标窗口处理过程之前,对该消息进行监视,详情参见CallWndProc挂钩处理过程.

WH_CBT(5) :安装一个挂钩处理过程,接受对CBT应用程序有用的消息 ,详情参见 CBTProc 挂钩处理过程.

WH_SYSMSGFILTER(6):安装一个挂钩处理过程,以监视由对话框、消息框、菜单条、或滚动条中的输入事件引发的消息.这个挂钩处理过程对系统中所有应用程序的这类消息都进行监视.详情参见 SysMsgProc挂钩处理过程.

WH_MOUSE(7):安装一个挂钩处理过程,对鼠标消息进行监视. 详情参见 MouseProc挂钩处理过程.

WH_DEBUG(9):安装一个挂钩处理过程以便对其他挂钩处理过程进行调试, 详情参见DebugProc挂钩处理过程.

WH_SHELL(10):安装一个挂钩处理过程以接受对外壳应用程序有用的通知, 详情参见 ShellProc挂钩处理过程.

WH_FOREGROUNDIDLE(11):安装一个挂钩处理过程,该挂钩处理过程当应用程序的前台线程即将进入空闲状态时被调用,它有助于在空闲时间内执行低优先级的任务.

WH_CALLWNDPROCRET(12):安装一个挂钩处理过程,它对已被目标窗口处理过程处理过了的消息进行监视,详情参见 CallWndRetProc 挂钩处理过程.

WH_KEYBOARD_LL(13):此挂钩只能在Windows NT中被安装,用来对底层的键盘输入事件进行监视.详情参见LowLevelKeyboardProc挂钩处理过程.

WH_MOUSE_LL(14):此挂钩只能在Windows NT中被安装,用来对底层的鼠标输入事件进行监视.详情参见LowLevelMouseProc挂钩处理过程.

参数 lpfn:指向相应的挂钩处理过程.若参数dwThreadId为0或者指示了一个其他进程创建的线程之标识符,则参数lpfn必须指向一个动态链接中的挂钩处理过程.否则,参数lpfn可以指向一个与当前进程相关的代码中定义的挂钩处理过程。

参数 hMod:指示了一个动态链接的句柄,该动态连接库包含了参数lpfn 所指向的挂钩处理过程.若参数dwThreadId指示的线程由当前进程创建,并且相应的挂钩处理过程定义于当前进程相关的代码中,则参数hMod必须被设置为NULL(0)。

参数 dwThreadId:指示了一个线程标识符,挂钩处理过程与线程相关.若此参数值为0,则该挂钩处理过程与所有现存的线程相关。

返回值:若此函数执行成功,则返回值就是该挂钩处理过程的句柄;若此函数执行失败,则返回值为NULL(0).若想获得更多错误信息,请调用GetLasError函数。

KeyboardProc

KeyboardProc钩子子程是同SetWindowsHookEx方法一起使用的、用户定义的或者库定义的回调函数。无论什么时候,当应用程序调用GetMessage 或者PeekMessage方法时,系统都调用该方法,将有一个键盘消息(WM_KEYUP或者WM_KEYDOWN)被处理。

image-20240715173419137

指定钩子子程使用的代码,来决定如何处理该消息。如果code小于0,钩子子程必须将该消息传递给CallNextHookEx方法,自己不进行任何进一步的处理,并且返回由CallNextHookEx方法返回的返回值。该参数可以是以下值之一:

1.HC_ACTION :参数 wParamlParam 包含有键盘敲击消息的信息。

2.HC_NOREMOVE :参数wParamlParam包含有键盘敲击消息的信息,键盘敲击消息还没有被从消息队列中移除。(应用程序调用PeekMessage方法,同时指定PM_NOREMOVE标志。)

wParam :指定生成键盘敲击消息的键的虚拟键码值。

lParam :指定重复次数,扫描代码,扩展键标志,上下文代码,前期的键状态标志,转换状态标志。该参数可下列的一个或者多个值。

0-15 :指定重复的次数。该值是,当用户持续按住一个键时,键盘敲击被重复的次数。

16-23 :指定扫描代码。该值依赖于OEM。

24 :指定该值是否是一个扩展键,例如功能键或者数字键盘上的键。如果是扩展键,该值为1,否则为0。

25-28 :保留

29 :指定上下文代码。如果ALT键被按下,该值为1,否则为0。

30 :指定前面的键状态。如果在消息发出之前该键被按下,值为1;如果键弹起,值为0。

31 :指定转换状态。如果键正在被按下,该值为0,如果正在被释放,则为1。

返回值:如果code小于0,钩子子程必须返回由CallNextHookEx返回的返回值。如果code大于等于0,表示钩子子程没有处理该消息,强烈要求调用CallNextHookEx方法,并返回由它返回的返回值;否则,其它已经安装有WH_KEYBOARD钩子的应用程序将收不到钩子通知,可能导致行为的错误。如果钩子子程处理了该消息,可能返回一个非0值,用来阻止系统将该消息传递给钩子链表中的其它钩子或者目的窗体程序。

新建MFC应用

image-20240715164546421

image-20240715164751094

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
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()
{
g_hHook = SetWindowsHookEx(
WH_KEYBOARD,
MyKeyboardProc,
NULL,
GetCurrentThreadId()
);
if (g_hHook == NULL)
{
AfxMessageBox("局部钩子安装失败");
}
}

UnhookWindowsHookEx

image-20240715192153296

参数:[in] hhk
类型:钩子的句柄
要移除的钩子的句柄,SetWindowsHookExA的返回值。此参数是通过以前调用钩子函数

返回值:类型:布尔
如果函数成功,返回值是非零的。
如果函数失败,返回值为零。若要获取扩展的错误信息,请调用错误码

1
2
3
4
void CHookTestDlg::OnBnClickedButton3()
{
UnhookWindowsHookEx(g_hHook);
}

添加动态链接库

image-20240715192726647

服务service

打开服务管理器:service.msc

image-20240715215610338

image-20240715215541938

新建控制台应用

image-20240715220849711

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

#include <windows.h>

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

生成后复制路径

image-20240715221814731

以管理员身份运行,新建MFC应用

image-20240715220026160

image-20240715220400221

在头文件添加定义

image-20240715222159122

OpenSCManager

OpenSCManager是Windows操作系统提供的一个函数,用于打开服务控制管理器(SCM)数据库。服务控制管理器是Windows系统中的一个组件,负责管理和控制系统中的各种服务。

image-20240715225824108

参数说明:

lpMachineName:机器名称,指定要连接的远程机器名称,如果为NULL,则表示本地机器。
lpDatabaseName:数据库名称,指定要打开的服务数据库的名称。常用的取值有SERVICES_ACTIVE_DATABASE和SERVICES_FAILED_DATABASE。
dwDesiredAccess:访问权限,指定打开服务控制管理器的访问权限。
返回值:返回一个SC_HANDLE类型的句柄,表示打开的服务控制管理器的句柄。如果打开失败,返回NULL。
通过调用OpenSCManager函数,可以获取一个服务控制管理器的句柄,进而可以对服务进行管理和控制,如创建服务、打开已有的服务等。打开服务控制管理器需要适当的权限,通常需要以管理员身份运行。

CreateService

CreateService是Windows操作系统提供的一个函数,用于创建一个新的服务。服务是在Windows操作系统中以后台方式运行的程序,可以在系统启动时自动启动,并在后台提供某种功能或服务。

image-20240715230030361

参数说明:

hSCManager:服务控制管理器的句柄,用于操作服务管理器。
lpServiceName:服务的名称,用于唯一标识一个服务。
lpDisplayName:服务的显示名称,用于在用户界面上显示的友好名称。
dwDesiredAccess:服务的访问权限。
dwServiceType:服务的类型,可以是服务、驱动程序等。
dwStartType:服务的启动类型,指定服务在系统启动时的启动方式。
dwErrorControl:服务的错误控制,指定服务出错时的处理方式。
lpBinaryPathName:服务的可执行文件路径。
lpLoadOrderGroup:服务的加载顺序组,用于指定服务的加载顺序。
lpdwTagId:服务标识号的指针,用于接收新创建服务的标识号。
lpDependencies:服务依赖项,指定服务所依赖的其他服务。
lpServiceStartName:服务的启动用户名。
lpPassword:服务的启动密码。
返回值:

返回一个SC_HANDLE类型的句柄,表示创建的服务的句柄。如果创建失败,返回NULL。
通过调用CreateService函数,可以在系统中创建一个新的服务,并指定该服务的各种属性和配置。创建服务需要适当的权限,通常需要以管理员身份运行。

1
2
// MyService.cpp
char m_szServiceName[MAXBYTE] = { "myservice" };
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
// SCPDlg.cpp: 实现文件
#include<winsvc.h>
void CSCPDlg::OnBnClickedButton1()
{
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:\\Users\\lenovo\\source\\repos\\Windows 14\\MyService\\Debug\\MyService.exe",
NULL,
NULL,
NULL,
NULL,
NULL
);
if (hService == NULL)
{
AfxMessageBox("创建服务失败");
return;
}
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
AfxMessageBox("创建服务成功");
}

运行成功

image-20240715225116700

SetServiceStatus

设置服务运行状态

image-20240716164432785

参数

1
[in] hServiceStatus

当前服务的状态信息结构的句柄。 此句柄由 RegisterServiceCtrlHandlerEx函数返回。

1
[in] lpServiceStatus

指向SERVICE_STATUS 结构的指针包含调用服务的最新状态信息。

返回值

如果该函数成功,则返回值为非零值。

如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。

服务控制管理器可以设置以下错误代码。 其他错误代码可由服务控制管理器调用的注册表函数设置。

展开表

返回代码 说明
ERROR_INVALID_DATA 指定的服务状态结构无效。
ERROR_INVALID_HANDLE 指定的句柄无效。

在编写Windows服务的时候,需要调用API函数::SetServiceStatus()向服务控制管理器(SCM)提交更新当前服务的状态信息,其第2个参数为指向SERVICE_STATUS结构的指针,SERVICE_STATUS结构中包含了表示当前服务状态的信息,对其各成员一一分析:

SERVICE_STATUS

image-20240716165413900

dwServiceType:指明服务可执行文件的类型。如果可执行文件中只有一个单独的服务,就把这个成员设置成SERVICE_WIN32_OWN_PROCESS;如果拥有多个服务的话,就设置成SERVICE_WIN32_SHARE_PROCESS。除了这两个标志之外,如果你的服务需要和桌面发生交互(当然不推荐这样做),就要用“|”运算符附加上SERVICE_INTERACTIVE_PROCESS。这个成员的值在服务的生存期内绝对不应该改变。

dwCurrentState:用于通知SCM此服务的现行状态。为了报告服务仍在初始化,应该把这个成员设置成SERVICE_START_PENDING。

dwControlsAccepted:指明服务接受什么样的控制通知。如果允许一个服务控制程序(SCP)去暂停/继续服务,就把它设成SERVICE_ACCEPT_PAUSE_CONTINUE。很多服务不支持暂停或继续,就必须自己决定在服务中它是否可用。如果你允许一个SCP去停止服务,就要设置它为SERVICE_ACCEPT_STOP。如果服务要在操作系统关闭的时候得到通知,设置它为SERVICE_ACCEPT_SHUTDOWN可以收到预期的结果。这些标志可以用“|”运算符组合。

dwWin32ExitCode和dwServiceSpecificExitCode:是允许服务报告错误的关键,如果希望服务去报告一个Win32错误代码(预定义在WinError.h中),它就设置dwWin32ExitCode为需要的代码。一个服务也可以报告它本身特有的、没有映射到一个预定义的Win32错误代码中的错误。为了这一点,要把dwWin32ExitCode设置为ERROR_SERVICE_SPECIFIC_ERROR,然后还要设置成员dwServiceSpecificExitCode为服务特有的错误代码。当服务运行正常,没有错误可以报告的时候,就设置成员dwWin32ExitCode为NO_ERROR。

dwCheckPoint和dwWaitHint:是一个服务用来报告它当前的事件进展情况的。

RegisterServiceCtrlHandler

image-20240716204124289

参数

1
[in] lpServiceName

由调用线程运行的服务的名称。 这是创建服务时在CreateService函数中指定的服务控制程序的服务名称。

如果服务类型SERVICE_WIN32_OWN_PROCESS,则函数不会验证指定名称是否有效,因为进程中只有一个已注册的服务。

1
[in] lpHandlerProc

指向要注册的处理程序函数的指针。 有关详细信息,请参阅处理程序。

返回值

如果函数成功,则返回值为服务状态句柄。

如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。

服务控制管理器可以设置以下错误代码。

展开表

返回代码 说明
ERROR_NOT_ENOUGH_MEMORY 内存不足,无法将 ANSI 字符串参数转换为 Unicode。 Unicode 字符串参数不会发生此错误。
ERROR_SERVICE_NOT_IN_EXE 进程调用 StartServiceCtrlDispatcher函数时,未正确指定服务条目。

StartServiceCtrlDispatcher

image-20240716212504923

参数
lpServiceTable [in]
指向SERVICE_TABLE_ENTRY结构数组的指针,该结构包含可在调用进程中执行的每个服务的一个条目。表中最后一个条目的成员必须具有NULL值才能指定表的结尾。

返回值
如果函数成功,则返回值为非零值。

如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

服务控制管理器可以设置以下错误代码。其他错误代码可以由服务控制管理器调用的注册表函数设置。

返回码说明
ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
如果程序作为控制台应用程序而不是作为服务运行,则会返回此错误。如果程序将作为控制台应用程序运行以进行调试,请对其进行构造,以便在返回此错误时不会调用特定于服务的代码。

ERROR_INVALID_DATA
指定的调度表包含格式不正确的条目。

ERROR_SERVICE_ALREADY_RUNNING
该进程已调用StartServiceCtrlDispatcher。每个进程只能调用一次StartServiceCtrlDispatcher。

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
// 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");
}
}

成功启动

image-20240716163447037

成功暂停

image-20240716163514902

成功恢复

image-20240716163532664

成功停止

image-20240716163636458

删除服务:以管理员权限运行cmd,输入

1
sc delete myservice(要删除的服务名称)

删除成功

image-20240716213540557

image-20240716213529129