实现屏蔽 Ctrl + Alt + Del 、Ctrl + Shift + Esc 等热键(二)

2024-02-04 07:52

本文主要是介绍实现屏蔽 Ctrl + Alt + Del 、Ctrl + Shift + Esc 等热键(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

前言

一、概述 Winlogon 的 RPC 例程

二、Handler 函数的消息处理机制

三、讨论如何安全执行 HOOK 代码

3.1 挂钩例程解决方案

3.2 脱钩例程解决方案

3.3 钩子例程解决方案

3.4 地址信息查询函数

3.5 简单保护加载的模块

四、模块如何安全地注入?

4.1 多进程处理

4.2 模块重载问题

4.3 关键注入代码

4.4 提升进程权限

4.5 处理句柄和环境检测

五、编译代码和测试运行

5.1 钩子模块代码

5.2 注入工具代码

5.3 测试结果展示

总结&后记


[正文来源: https://blog.csdn.net/qq_59075481/article/details/135980850,转载请注明出处]

前言

在第一篇文章中,我们主要分析了系统热键处理的关键接口以及如何通过选取特征码精准定位接口函数的入口点。在这一篇中,我们将进一步分析 Winlogon 相关例程的机制,并给出挂钩处理的解决方案。需要注意的是“屏蔽 Ctrl + Alt + Del 、Ctrl + Shift + Esc 等热键”系列是 Windows 系统热键拦截技术分析专栏的一个分支,主要复现 heiheiabcd 的旧文给出的拦截思路。该方案并不是最推荐的方案,如果你想要继续了解,可以查阅同专栏的其他文章。

本系列文章目录:

编号文章标题ID 号
1实现屏蔽 Ctrl + Alt + Del 、Ctrl + Shift + Esc 等热键(一)135899525
2实现屏蔽 Ctrl + Alt + Del 、Ctrl + Shift + Esc 等热键(二)135980850

一、概述 Winlogon 的 RPC 例程

Winlogon 进程通过 SignalManagerWaitForSignal 函数循环等待系统快捷键。而关键的消息回调是通过 RPC 完成的。

Winlogon 首先会在主线程的 WinMain 函数中调用 WlStateMachineInitialize、WMsgClntInitialize 初始化全部的回调函数和事务处理线程(注意 RPC 信息的响应处理是在新线程中完成的,不是主线程);随后进入临界区并调用 StateMachineRun 开始监听事件。StateMachineRun 函数内部实际通过 SignalManagerWaitForSignal 循环等待系统事件。

我想有必要先简单介绍一下初始化后创建的事务线程,我打算在 RPC Hook 路线的第二篇中进一步讲解,我想大家先了解这部分的理论,会对本篇挂钩过程的原理有一个更好的理解,也许看完了图示流程,就会发现除了本篇的方法还有更多的拦截方法,这也是为什么我文章“兵分两路”讲解的原因。

接收到热键消息(有 K 的函数)为例,处理流程可以用下面的简化版理论来总结。

备注:提权等操作过程类似,只不过通过的函数没有 K 字样,在第一篇说过两个函数的功能区别。整理经 WinDbg 和 IDA Pro 的逆向分析结果,并参考了 heiheiabcd 的工作。

首先,客户端的请求通过 Ndr64AsyncClientCall 最终传递进入服务例程,服务例程通过调用 Ndr64AsyncServerCallAll 来完成所有响应过程。

而该过程是通过 rpcrt4!Invoke 函数来派发的间接调用链(了解过 MS-RPC 的应该都知道 Invoke )。

调用过程简化流程图 1

随后,进入关键调用过程:

调用过程简化流程图 2

所有任务都通过 I_WMsgkSendMessage 实现,因为此时需要调用的远程过程函数的参数已经全部在堆栈或寄存器上了。我们将过程划分为三个阶段:(1)测试远程过程,验证客户端信息来确定是否取消后续的调用;(2)验证通过后调用 WMsgKMessageHandler 也就是正真的事务处理例程,在该例程中通过 WlStateMachineSetSignal 设置事件信号,该事件会通知主线程;(3)I_WMsgkSendMessage 进行最后的处理,通过 RpcAsyncCompleteCall 通知客户端完成请求。

总的来说,整个多进程跨线程的异步机制为:客户端(调用方进程)请求某个操作时,服务器(Winlogon)通过特殊的事务处理线程接收消息并验证身份,然后通过设置事件(SetEvent)释放正在等待的 WinMain 主线程,最后事务线程通知客户端请求的操作已经完成,客户端(如果有)等待到消息后类似服务器,释放相关执行过程的线程(阻滞/非阻滞过程)。

这就是为什么我们提出,可以通过拦截 WMsgKMessageHandler 函数来拦截热键的响应过程。因为该函数负责设置事件的过程,拦截它就可以及时中断 RPC 事务处理。

二、Handler 函数的消息处理机制

 WMsgKMessageHandler 和 WMsgMessageHandler 函数是两个消息处理回调。也就是第一篇谈论如何定位的两个函数。

经动态调试他们的主要功能总结如下表所示:

  • WMsgKMessageHandler 主要负责处理由 Csrss 注册的部分 winlogon 进程的快捷键,如:Ctrl + Shift + Esc。此外,他还负责开始菜单的注销,资源管理器上 Alt+F4 的注销/关机/重启。
  • WMsgMessageHandler 主要负责处理用户层发起的大部分会话请求的处理(不包括注销),如:开始菜单发起的重启、关机、切换用户。此外,它还联合 AppInfo Service 服务,一同处理进程的提升管理员权限会话。

下面就是在第一篇也展示过的 Handler 函数的声明,不过我现在要小小纠正一下我对部分参数的解析以及名称:

int __fastcall WMsgKMessageHandler(  // also WMsgMessageHandlerunsigned int         uMachineState,unsigned int         uMsgWLGenericKey,PRPC_ASYNC_STATE     pAsync,LPDWORD              lpStatus
)

备注:主要修正了第四个参数的类型,以及第一个参数的猜测名称。

前三个参数都和设置事件有关系:其中,前两个和操作类型有关;第三个参数实际上指向 StateMachineSignalData 结构体,用于传递额外的数据(如果有),第四个参数是在使用异步注销会话时返回额外的状态信息,其他情况下返回值为 NULL。

通过测试,我们也初步总结了在不同操作下的消息参数值(部分),如下表1,表2 所示。

(1) WMsgMessageHandler 回调参数

表1. WMsgMessageHandler 参数表
uMachineStateuMsgWLGenericKeyMajorAction
0x00010x04002009开始菜单的关机按钮
0x00010x04002003开始菜单的重启按钮
0x05000x1E88请求提升管理员权限
0x05010x06B8已经提升管理员权限
0x04030x0000切换用户(初始)
0x02020x0000切换用户(恢复)
0x02050x0000切换用户(恢复)

(2) WMsgKMessageHandler 回调参数

表2. WMsgKMessageHandler 参数表
uMachineStateuMsgWLGenericKeyMajorAction
0x04040x04Ctrl+Shift+Esc, 任务管理器
0x04040x00Ctrl+Alt+Delete, 安全桌面
0x04040x05Win+L, 锁屏, LogonUI Windows
0x04040x07Win+P, 投影屏幕
0x04040x06Win+U, 设置/辅助功能
0x04040x0CWin+Plus, 放大镜
0x04040x0DWin+Ctrl+Enter, 讲述人
0x04040x0EWin+Enter, 未知
0x04020x05左 Alt+LShift+Print Screen, 高对比度主题
0x04020x01连续按下五次左 Shift,滞粘键
0x04020x02按住右 Shift 键 8 秒,筛选键
0x04010x03Alt+F4 资源管理器,重启计算机
0x00010x4009Alt+F4 资源管理器,关闭计算机
0x00010x0000Alt+F4 资源管理器,注销计算机/开始菜单注销计算机

在分析时 WMsgKMessageHandler 的参数研究的相对多一些,所以它的参数基本上全了。

还有就是上表均是在较新版本的 Win10/11 上测试的,低版本系统 uMachineState 是否相同并未进行测试。这些参数的测试仅是通过挂钩后对照微软的快捷键表查表得到的,官网链接为:Windows 的键盘快捷方式 - Microsoft 支持。

三、讨论如何安全执行 HOOK 代码

安全执行代码对于此类键盘钩子来说非常重要,对 Winlogon 进程的代码注入常常会导致系统不稳定,如果 Winlogon 进程崩溃或者死锁,则会导致一系列的致命错误。

3.1 挂钩例程解决方案

为了不占用系统过多资源,我们采用 API Inline Hook 这一经典的技术。 Inline Hook 通常修改函数入口的第一条指令为 jmp 指令(蹦床)来实现将函数的控制流程导向到自己编写的钩子函数。在钩子函数中处理自己的逻辑,并且在需要调用原始函数的时候,提前恢复入口处被修改过的字节(脱钩),随后通过 Call 或者 jmp 或者 ret 修改 RIP 指向原函数地址即可,调用完成后恢复挂钩。

在 x64 下,一种蹦床指令序列利用了将钩子函数的地址写入 r11 通用寄存器,并利用 jmp 无条件跳转到 r11 指向的地址来实现挂钩过程。下列报告显示了它的汇编和机器码:

Disassembly Report

Raw Hex (zero bytes in bold):

49BB000000000000000041FFE3   

String Literal:

"\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x00\x41\xFF\xE3"

Array Literal:

{ 0x49, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xFF, 0xE3 }

Disassembly:

0:  49 bb 00 00 00 00 00    movabs r11,0x0             ; 这里填充钩子函数地址
7:  00 00 00
a:  41 ff e3                          jmp    r11                         ; 实现跳转

该指令序列的做法只适用于 x64 进程。

我们通过 BYTE 数组存储蹦床函数的指令,随后通过写入钩子函数的地址,得到完整的蹦床函数。

随后,修改原始代码通过 memcpy 来实现,这是因为我们是在注入后 Winlogon 进程内进行修改的。修改前后,通过 VirtualProtect 修改内存保护, memcpy 覆盖原始字节后,通过 FlushInstructionCache 函数刷新 Cache 中的缓存,这是为了确保在 Cache 上的代码和内存中的一致。如果使用 WriteProcessMemory 则函数内部会调用 FlushInstructionCache 函数而不需要我们再去干预。

void HookHandler()
{DWORD oldProtect = 0;LPVOID hHookAddress = lpgHandlerInfo.lpHookAddress;LPVOID hWMsgHandler = &HookedWMsgKMessageHandler;SIZE_T bufferSize = 0;// our trampolineunsigned char boing[] = {0x49, 0xbb, 0xde, 0xad,0xc0, 0xde, 0xde, 0xad,0xc0, 0xde, 0x41, 0xff,0xe3 };// add in the address of our hook*(LPVOID*)(boing + 2) = hWMsgHandler;bufferSize = sizeof(boing);// disable write protectVirtualProtect(hHookAddress, 13, PAGE_EXECUTE_READWRITE, &oldProtect);// save the original bytesmemcpy_s(pOriginalBytes, 13, hHookAddress, 13);// write the hookmemcpy_s(hHookAddress, bufferSize, boing, bufferSize);// Flush Cache to make code workFlushInstructionCache(GetCurrentProcess(), hHookAddress, bufferSize);// enable write protectVirtualProtect(hHookAddress, 13, oldProtect, &oldProtect);}

3.2 脱钩例程解决方案

脱钩是必要的过程,因为我们修改了原始函数的代码,要想执行原始函数,就必须恢复原始代码。其次,卸载模块时也要恢复原始代码,否则程序就会出错。

我们通过全局变量在挂钩例程中已经备份了原始的 13 个字节,脱钩时候只需要恢复这 13 个字节即可。

参考代码如下:

void UnhookHandler()
{DWORD bResponse = FALSE;DWORD oldProtect = 0;PVOID hookAddress = lpgHandlerInfo.lpHookAddress;// disable write protectVirtualProtect(hookAddress, 13, PAGE_EXECUTE_READWRITE, &oldProtect);// recover original bytesmemcpy_s(hookAddress, 13, pOriginalBytes, 13);// Flush Cache to make code workFlushInstructionCache(GetCurrentProcess(), hookAddress, 13);// enable write protectVirtualProtect(hookAddress, 13, oldProtect, &oldProtect);}

这样,是不是很类似于挂钩的过程?

3.3 钩子例程解决方案

钩子例程就是我们需要做处理的函数,由于 WMsgMessageHandler 和 WMsgKMessageHandler 是相同参数的函数,所以我们的钩子例程可以做成通配函数。

首先定义函数的声明:

typedef int(__fastcall* __WMsgKMessageHandler)(unsigned int         uMachineState,unsigned int         uMsgWLGenericKey,PRPC_ASYNC_STATE     pAsync,LPDWORD              lpStatus   // int* pReserved
);

随后,指向原函数地址的指针 LPVOID 用于函数的指针调用。

在钩子例程中设置对感兴趣的参数的筛选即可,不过要注意:如果不执行任何操作,则 Handler 函数必须返回 1 代表结束调用,否则在部分操作中 Winlogon 会持续等待直至死锁,这一点可以通过 IDA Pro 的逆向代码确认。此外,不应该挂钩全部的参数,因为有参数超出我们目前已知的稳定参数列表,拦截这部分操作会造成系统死锁,比如切换用户的操作就不推荐拦截。

一个可供参考的钩子例程如下:

int __fastcall HookedWMsgHandler(unsigned int         uMachineState,unsigned int         uMsgWLGenericKey,PRPC_ASYNC_STATE     pAsync,LPDWORD              lpStatus   // int* pReserved
)
{int dwFunResponse = 0;/** ///* * WMsgMessageHandler 控制 账户状态/UAC 相关的功能* uMachineState       uMsgWLGenericKey        MajorAction* 0001                  04002009            关闭本地计算机* 0001                  04002003            重启本地计算机* 0500                  1E88                请求提升管理员权限* 0501                  06B8                已经提升管理员权限* 0403                  0000                切换用户进行时(建议窗口在 Winlogon 下创建,并置前端)* 0202                  0000                切换用户恢复时(具体操作未知)* 0205                  0000                切换用户恢复时(具体操作未知)* 注意:(1)对于非已知代码的情况,不要使用阻滞过程,否则会导致死锁。*       (2)如果不需要执行指定的过程,函数返回值必须是 1,如果为 0 可能会陷入等待。* * * * WMsgKMessageHandler 控制 系统热键/注销 相关的功能* uMachineState       uMsgWLGenericKey          MajorAction* 0404                  4                       Ctrl+Shift+Esc, 任务管理器* 0404                  0                       Ctrl+Alt+Delete, 安全桌面* 0404                  5                       Win+L, 锁屏, LogonUI Windows* 0404                  7                       Win+P, 投影屏幕* 0404                  6                       Win+U, 设置/辅助功能* 0404                  C                       Win+Plus, 放大镜* 0404                  D                       Win+Ctrl+Enter, 讲述人* 0404                  E                       Win+Enter, 未知* 0402                  5                       左Alt+LShift+Print Screen, 高对比度主题* 0402                  1                       连续按下五次左侧 Shift,滞粘键* 0402                  2                       按住右侧 Shift 键 8 秒,筛选键* 0001                  3                       Alt+F4 资源管理器,重启计算机* 0001                  4009                    Alt+F4 资源管理器,关闭计算机* 0001                  0                       Alt+F4 资源管理器,注销计算机* * */// 样例参数过滤代码if (uMachineState == 0x404){switch(uMsgWLGenericKey){case ID_1:{// do something}break;case ID_2:{// do something}break;default:break;}// UnHookUnhookHandler(NULL);auto WMsgKMessageHandler = (__WMsgKMessageHandler)lpgHandlerInfo.lpHookAddress;// call original funcdwFunResponse = WMsgKMessageHandler(uMachineState, uMsgWLGenericKey, pAsync, lpStatus);// Re-hookHookHandler(NULL);return dwFunResponse;
}

3.4 地址信息查询函数

由于钩子代码通过 Dll 模块实现,上面的函数可以设置为由一个可以查询地址信息的函数来实现,在外部进程中,只需要知道地址,就可以使用远程线程调用位于 winlogon 进程中 Hook 模块的挂钩/脱钩例程。

我们使用一个结构体来记录所需要的所有信息:

typedef struct HANDLER_INFO_STRUCT
{DWORD  cbSize = 0;DWORD  dwMainThread = 0;HMODULE hHookModule = nullptr;LPVOID lpHookHandler = nullptr;LPVOID lpUnhookHandler = nullptr;LPVOID lpSafeFreeLib = nullptr;LPVOID lpWinlogonBase = nullptr;LPVOID lpHookAddress = nullptr;//LPVOID lpEPLFuncAddress = nullptr;
}HANDLER_INFO_STRUCT, * LPHANDLER_INFO_STRUCT;

随后我们创建共享节,以便同步一些信息,里面为部分需要共享的全局变量进行初始化。

#pragma data_seg("WMsgHookData")
CHAR pOriginalBytes[13] = { 0 };
HANDLER_INFO_STRUCT lpgHandlerInfo = { 0 };
#pragma data_seg()
#pragma comment(linker,"/SECTION:WMsgHookData,RWS")

使用下面的函数来拷贝模块共享节中的全局变量 HANDLER_INFO_STRUCT,来使得同样加载了 Hook 模块的挂钩工具,其在外部就可以获得 winlogon 信息的拷贝副本。

BOOL WINAPI GetHandlerAddress(LPHANDLER_INFO_STRUCT lpHandlerInfo)
{// 结构体指针不为空if (lpHandlerInfo == nullptr){OutputDebugStringW(L"Er:[GetHandlerAddress] status:[Invalid nullptr].\n");return FALSE;}// 计算 cbSize 成员的地址SIZE_T cbSizeOff = offsetof(HANDLER_INFO_STRUCT, cbSize);PDWORD lpcbSize = reinterpret_cast<PDWORD>(lpHandlerInfo + cbSizeOff);// 保证传入结构体大小等于预期的大小if (*lpcbSize != sizeof(HANDLER_INFO_STRUCT)){OutputDebugStringW(L"Er:[GetHandlerAddress] status:[Invalid HANDLER_INFO_STRUCT Size].\n");return FALSE;}// 将传入结构体指针中的各个字段赋值给lpHandlerInfo结构体中对应的字段,并返回TRUElpHandlerInfo->lpHookHandler = lpgHandlerInfo.lpHookHandler;lpHandlerInfo->lpUnhookHandler = lpgHandlerInfo.lpUnhookHandler;lpHandlerInfo->lpHookAddress = lpgHandlerInfo.lpHookAddress;lpHandlerInfo->lpWinlogonBase = lpgHandlerInfo.lpWinlogonBase;lpHandlerInfo->lpSafeFreeLib = lpgHandlerInfo.lpSafeFreeLib;lpHandlerInfo->dwMainThread = lpgHandlerInfo.dwMainThread;lpHandlerInfo->hHookModule = lpgHandlerInfo.hHookModule;return TRUE;
}

现在,我们只需要导出这个副本查询函数,即可在任何加载了该模块的进程获取相同的信息拷贝。这里的操作基于假设采用随机化加载基址时,也能安全远程控制挂钩状态;否则,我们只需要固定函数地址就直接远程线程调用了,而不需要传递远程进程中的模块地址。

3.5 简单保护加载的模块

以上代码均在 Dll 模块中实现,并注入到 winlogon 进程。但是,为了防止一些第三方软件卸载模块,我们简单采取一点防护机制。在 R3 下防护动态加载的模块不被意外卸载需要很多的策略,比如:LDR 断链、VAD 记录擦除、PE 头擦除、修改入口函数、内存注入等。这里我们只使用了模块静态化技术这一项技术。

进程中初次加载模块时会将模块映射到地址空间中,当之后多次加载同一个模块时,并不会重新加载这个模块,而是只返回相同的地址。此时,例程会增加一个由进程维护的模块的引用计数这样子的一个变量。当使用 FreeLibrary 卸载模块时,引用计数会减小,只有引用计数减到 0,才会正真释放占用的资源。并且规定,静态加载的模块,其引用计数为 -1,并且不能增加计数或通过 FreeLibrary / LdrUnLoadDll 等卸载模块。模块静态化是一个很常见的模块保护技术,它通过修改模块的引用计数为 -1,来使得模块不可被标准的 API 成功卸载。

另一种类似的技术是通过 uFlags 位域覆盖修改标志位,系统会认为模块是静态的进而阻止模块的卸载。

参考文献:(1)The covert way to find the Reference Count of DLL - www.SecurityXploded.com
                  (2)Make Your Dynamic Module Unfreeable (Anti-FreeLibrary) | secrary[dot]com

这两个技术都通过 LDR_DATA_TABLE_ENTRY 中的成员来完成,前者通过修改 DdagNode 结构中的 LoadCount 以及 LDR 中废弃的(旧系统用到) ObsoleteLoadCount 成员,将他们赋值为 -1,即可修改模块属性为静态:

pLdrDataEntry->DdagNode->LoadCount = 0xffffffff;
pLdrDataEntry->ObsoleteLoadCount = 0xffff;

修改前,动态加载的模块可以被 RemoteDll 等工具卸载:

修改后,按钮灰显,表明这个模块不可以被卸载:

这种修改不需要针对 R3 下哪种工具,只需要在 Winlogon 进程内修改即可。

后者通过修改位域 ProcessStaticImport 为 TRUE(1) 来实现的,该结构体如下:

//0x120 bytes (sizeof)
typedef struct _LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY InLoadOrderLinks;                                            //0x0
    LIST_ENTRY InMemoryOrderLinks;                                          //0x10
    LIST_ENTRY InInitializationOrderLinks;                                  //0x20
    VOID* DllBase;                                                          //0x30
    VOID* EntryPoint;                                                       //0x38
    ULONG SizeOfImage;                                                      //0x40
    UNICODE_STRING FullDllName;                                             //0x48
    UNICODE_STRING BaseDllName;                                             //0x58
    union
    {
        UCHAR FlagGroup[4];                                                 //0x68
        ULONG Flags;                                                        //0x68
        struct
        {
            ULONG PackagedBinary : 1;                                         //0x68
            ULONG MarkedForRemoval : 1;                                       //0x68
            ULONG ImageDll : 1;                                               //0x68
            ULONG LoadNotificationsSent : 1;                                  //0x68
            ULONG TelemetryEntryProcessed : 1;                                //0x68
           
ULONG ProcessStaticImport : 1;                                    //0x68
            ULONG InLegacyLists : 1;                                          //0x68
            ULONG InIndexes : 1;                                              //0x68
            ULONG ShimDll : 1;                                                //0x68
            ULONG InExceptionTable : 1;                                       //0x68
            ULONG ReservedFlags1 : 2;                                         //0x68
            ULONG LoadInProgress : 1;                                         //0x68
            ULONG LoadConfigProcessed : 1;                                    //0x68
            ULONG EntryProcessed : 1;                                         //0x68
            ULONG ProtectDelayLoad : 1;                                       //0x68
            ULONG ReservedFlags3 : 2;                                         //0x68
            ULONG DontCallForThreads : 1;                                     //0x68
            ULONG ProcessAttachCalled : 1;                                    //0x68
            ULONG ProcessAttachFailed : 1;                                    //0x68
            ULONG CorDeferredValidate : 1;                                    //0x68
            ULONG CorImage : 1;                                               //0x68
            ULONG DontRelocate : 1;                                           //0x68
            ULONG CorILOnly : 1;                                              //0x68
            ULONG ChpeImage : 1;                                              //0x68
            ULONG ReservedFlags5 : 2;                                         //0x68
            ULONG Redirected : 1;                                             //0x68
            ULONG ReservedFlags6 : 2;                                         //0x68
            ULONG CompatDatabaseProcessed : 1;                                //0x68
        }uFlags;
    };

    USHORT ObsoleteLoadCount;                                               //0x6c
    USHORT TlsIndex;                                                        //0x6e
    LIST_ENTRY HashLinks;                                                   //0x70
    ULONG TimeDateStamp;                                                    //0x80
    struct ACTIVATION_CONTEXT* EntryPointActivationContext;                 //0x88
    VOID* Lock;                                                             //0x90
    LDR_DDAG_NODE* DdagNode;                                                //0x98
    LIST_ENTRY NodeModuleLink;                                              //0xa0
    struct LDRP_LOAD_CONTEXT* LoadContext;                                  //0xb0
    VOID* ParentDllBase;                                                    //0xb8
    VOID* SwitchBackContext;                                                //0xc0
    RTL_BALANCED_NODE BaseAddressIndexNode;                                 //0xc8
    RTL_BALANCED_NODE MappingInfoIndexNode;                                 //0xe0
    ULONGLONG OriginalBase;                                                 //0xf8
    LARGE_INTEGER LoadTime;                                                 //0x100
    ULONG BaseNameHashValue;                                                //0x108
    LDR_DLL_LOAD_REASON LoadReason;                                         //0x10c
    ULONG ImplicitPathOptions;                                              //0x110
    ULONG ReferenceCount;                                                   //0x114
    ULONG DependentLoadFlags;                                               //0x118
    UCHAR SigningLevel;                                                     //0x11c
}LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;

其实,开启这个标志位保护模块的官方的方法是调用一次 GetModuleHandleEx 并指定 GET_MODULE_HANDLE_EX_FLAG_PIN 标志,但似乎并没法取消掉。

HMODULE hTestModule = 0;
GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, L"模块名称", &hTestModule);

开启该标志位后,无论使用多少次 FreeLibrary 都不会真正卸载模块。

下面的代码实现上述所有功能,需要注意是,有三个链表都需要枚举并修改,关于这个结构枚举的原理可以看我的另一篇文章“利用 LDR_DATA_TABLE 枚举进程模块信息”,此外需要注意如果包含 winternl 头文件,它里面有 ldrp.h 部分结构的重复声明(微软给的结构体不完整)。

ldrp.h:

#pragma once
#include <winnt.h>
#include <WTypesbase.h>
//#include <winternl.h>// Kernels | x64 | Windows 10 | 2016 | 2210 22H2(May 2023 Update)//0x18 bytes (sizeof)
typedef struct _RTL_BALANCED_NODE
{union{_RTL_BALANCED_NODE* Children[2];                             //0x0struct{_RTL_BALANCED_NODE* Left;                                //0x0_RTL_BALANCED_NODE* Right;                               //0x8};};union{struct{UCHAR Red : 1;                                                    //0x10UCHAR Balance : 2;                                                //0x10};ULONGLONG ParentValue;                                                //0x10};
}RTL_BALANCED_NODE, * PRTL_BALANCED_NODE, * LPRTL_BALANCED_NODE;//0x4 bytes (sizeof)
enum _LDR_DLL_LOAD_REASON
{LoadReasonStaticDependency = 0,LoadReasonStaticForwarderDependency = 1,LoadReasonDynamicForwarderDependency = 2,LoadReasonDelayloadDependency = 3,LoadReasonDynamicLoad = 4,LoadReasonAsImageLoad = 5,LoadReasonAsDataLoad = 6,LoadReasonEnclavePrimary = 7,LoadReasonEnclaveDependency = 8,LoadReasonUnknown = -1
};typedef _LDR_DLL_LOAD_REASON    LDR_DLL_LOAD_REASON;//0x10 bytes (sizeof)
typedef struct _LDR_SERVICE_TAG_RECORD
{_LDR_SERVICE_TAG_RECORD* Next;                                           //0x0ULONG ServiceTag;                                                       //0x8
}LDR_SERVICE_TAG_RECORD, * PLDR_SERVICE_TAG_RECORD;//0x8 bytes (sizeof)
typedef struct _LDRP_CSLIST
{SINGLE_LIST_ENTRY* Tail;                                                //0x0
}LDRP_CSLIST;//0x4 bytes (sizeof)
enum _LDR_DDAG_STATE
{LdrModulesMerged = -5,LdrModulesInitError = -4,LdrModulesSnapError = -3,LdrModulesUnloaded = -2,LdrModulesUnloading = -1,LdrModulesPlaceHolder = 0,LdrModulesMapping = 1,LdrModulesMapped = 2,LdrModulesWaitingForDependencies = 3,LdrModulesSnapping = 4,LdrModulesSnapped = 5,LdrModulesCondensed = 6,LdrModulesReadyToInit = 7,LdrModulesInitializing = 8,LdrModulesReadyToRun = 9
};typedef _LDR_DDAG_STATE   LDR_DDAG_STATE;//0x50 bytes (sizeof)
typedef struct _LDR_DDAG_NODE
{LIST_ENTRY Modules;                                                     //0x0PLDR_SERVICE_TAG_RECORD ServiceTagList;                                 //0x10ULONG LoadCount;                                                        //0x18ULONG LoadWhileUnloadingCount;                                          //0x1cULONG LowestLink;                                                       //0x20LDRP_CSLIST Dependencies;                                               //0x28LDRP_CSLIST IncomingDependencies;                                       //0x30LDR_DDAG_STATE State;                                                   //0x38SINGLE_LIST_ENTRY CondenseLink;                                         //0x40ULONG PreorderNumber;                                                   //0x48
}LDR_DDAG_NODE, * PLDR_DDAG_NODE;typedef struct _UNICODE_STRING {USHORT Length;USHORT MaximumLength;PWSTR  Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING* PUNICODE_STRING;
typedef const UNICODE_STRING* PCUNICODE_STRING;//0x120 bytes (sizeof)
typedef struct _LDR_DATA_TABLE_ENTRY
{LIST_ENTRY InLoadOrderLinks;                                            //0x0LIST_ENTRY InMemoryOrderLinks;                                          //0x10LIST_ENTRY InInitializationOrderLinks;                                  //0x20VOID* DllBase;                                                          //0x30VOID* EntryPoint;                                                       //0x38ULONG SizeOfImage;                                                      //0x40UNICODE_STRING FullDllName;                                             //0x48UNICODE_STRING BaseDllName;                                             //0x58union{UCHAR FlagGroup[4];                                                 //0x68ULONG Flags;                                                        //0x68struct{ULONG PackagedBinary : 1;                                         //0x68ULONG MarkedForRemoval : 1;                                       //0x68ULONG ImageDll : 1;                                               //0x68ULONG LoadNotificationsSent : 1;                                  //0x68ULONG TelemetryEntryProcessed : 1;                                //0x68ULONG ProcessStaticImport : 1;                                    //0x68ULONG InLegacyLists : 1;                                          //0x68ULONG InIndexes : 1;                                              //0x68ULONG ShimDll : 1;                                                //0x68ULONG InExceptionTable : 1;                                       //0x68ULONG ReservedFlags1 : 2;                                         //0x68ULONG LoadInProgress : 1;                                         //0x68ULONG LoadConfigProcessed : 1;                                    //0x68ULONG EntryProcessed : 1;                                         //0x68ULONG ProtectDelayLoad : 1;                                       //0x68ULONG ReservedFlags3 : 2;                                         //0x68ULONG DontCallForThreads : 1;                                     //0x68ULONG ProcessAttachCalled : 1;                                    //0x68ULONG ProcessAttachFailed : 1;                                    //0x68ULONG CorDeferredValidate : 1;                                    //0x68ULONG CorImage : 1;                                               //0x68ULONG DontRelocate : 1;                                           //0x68ULONG CorILOnly : 1;                                              //0x68ULONG ChpeImage : 1;                                              //0x68ULONG ReservedFlags5 : 2;                                         //0x68ULONG Redirected : 1;                                             //0x68ULONG ReservedFlags6 : 2;                                         //0x68ULONG CompatDatabaseProcessed : 1;                                //0x68}uFlags;};USHORT ObsoleteLoadCount;                                               //0x6cUSHORT TlsIndex;                                                        //0x6eLIST_ENTRY HashLinks;                                                   //0x70ULONG TimeDateStamp;                                                    //0x80struct ACTIVATION_CONTEXT* EntryPointActivationContext;                 //0x88VOID* Lock;                                                             //0x90LDR_DDAG_NODE* DdagNode;                                                //0x98LIST_ENTRY NodeModuleLink;                                              //0xa0struct LDRP_LOAD_CONTEXT* LoadContext;                                  //0xb0VOID* ParentDllBase;                                                    //0xb8VOID* SwitchBackContext;                                                //0xc0RTL_BALANCED_NODE BaseAddressIndexNode;                                 //0xc8RTL_BALANCED_NODE MappingInfoIndexNode;                                 //0xe0ULONGLONG OriginalBase;                                                 //0xf8LARGE_INTEGER LoadTime;                                                 //0x100ULONG BaseNameHashValue;                                                //0x108LDR_DLL_LOAD_REASON LoadReason;                                         //0x10cULONG ImplicitPathOptions;                                              //0x110ULONG ReferenceCount;                                                   //0x114ULONG DependentLoadFlags;                                               //0x118UCHAR SigningLevel;                                                     //0x11c
}LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;//0x58 bytes (sizeof)
typedef struct _PEB_LDR_DATA32
{ULONG Length; // +0x00BOOLEAN Initialized; // +0x04PVOID SsHandle; // +0x08LIST_ENTRY InLoadOrderModuleList; // +0x0cLIST_ENTRY InMemoryOrderModuleList; // +0x14LIST_ENTRY InInitializationOrderModuleList;// +0x1c
} PEB_LDR_DATA32, * PPEB_LDR_DATA32; // +0x24typedef struct _PEB32
{UCHAR InheritedAddressSpace;                                            //0x0UCHAR ReadImageFileExecOptions;                                         //0x1UCHAR BeingDebugged;                                                    //0x2union{UCHAR BitField;                                                     //0x3struct{UCHAR ImageUsesLargePages : 1;                                    //0x3UCHAR IsProtectedProcess : 1;                                     //0x3UCHAR IsImageDynamicallyRelocated : 1;                            //0x3UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3UCHAR IsPackagedProcess : 1;                                      //0x3UCHAR IsAppContainer : 1;                                         //0x3UCHAR IsProtectedProcessLight : 1;                                //0x3UCHAR IsLongPathAwareProcess : 1;                                 //0x3};};PVOID Mutant;                                                           //0x4PVOID ImageBaseAddress;                                                 //0x8PEB_LDR_DATA32* Ldr;                                                    //0xcstruct RTL_USER_PROCESS_PARAMETERS* ProcessParameters;                  //0x10PVOID SubSystemData;                                                    //0x14PVOID ProcessHeap;                                                      //0x18RTL_CRITICAL_SECTION* FastPebLock;                                      //0x1cSLIST_HEADER* volatile AtlThunkSListPtr;                                //0x20PVOID IFEOKey;                                                          //0x24
} PEB32, * PPEB32;typedef struct _STRING64
{USHORT Length;                                                          //0x0USHORT MaximumLength;                                                   //0x2ULONGLONG Buffer;                                                       //0x8
}STRING64, * LPSTRING64;typedef struct _PEB_LDR_DATA64
{ULONG Length;                                                           //0x0UCHAR Initialized;                                                      //0x4PVOID SsHandle;                                                         //0x8LIST_ENTRY InLoadOrderModuleList;                                       //0x10LIST_ENTRY InMemoryOrderModuleList;                                     //0x20LIST_ENTRY InInitializationOrderModuleList;                             //0x30PVOID EntryInProgress;                                                  //0x40UCHAR ShutdownInProgress;                                               //0x48PVOID ShutdownThreadId;                                                 //0x50
}PEB_LDR_DATA64, * PPEB_LDR_DATA64;typedef struct _PEB64
{UCHAR InheritedAddressSpace;                                            //0x0UCHAR ReadImageFileExecOptions;                                         //0x1UCHAR BeingDebugged;                                                    //0x2union{UCHAR BitField;                                                     //0x3struct{UCHAR ImageUsesLargePages : 1;                                    //0x3UCHAR IsProtectedProcess : 1;                                     //0x3UCHAR IsImageDynamicallyRelocated : 1;                            //0x3UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3UCHAR IsPackagedProcess : 1;                                      //0x3UCHAR IsAppContainer : 1;                                         //0x3UCHAR IsProtectedProcessLight : 1;                                //0x3UCHAR IsLongPathAwareProcess : 1;                                 //0x3};};UCHAR Padding0[4];                                                      //0x4ULONGLONG Mutant;                                                       //0x8ULONGLONG ImageBaseAddress;                                             //0x10PEB_LDR_DATA64* Ldr;                                                    //0x18ULONGLONG ProcessParameters;                                            //0x20ULONGLONG SubSystemData;                                                //0x28ULONGLONG ProcessHeap;                                                  //0x30ULONGLONG FastPebLock;                                                  //0x38ULONGLONG AtlThunkSListPtr;                                             //0x40ULONGLONG IFEOKey;                                                      //0x48
}PEB64, * PPEB64;#ifdef _WIN64
typedef PEB64                   PEB;
typedef PPEB64                  PPEB;
typedef PEB_LDR_DATA64          PEB_LDR_DATA;
typedef PPEB_LDR_DATA64         PPEB_LDR_DATA;
#else
typedef PEB32                   PEB;
typedef PPEB32                  PPEB;
typedef PEB_LDR_DATA32          PEB_LDR_DATA;
typedef PPEB_LDR_DATA32         PPEB_LDR_DATA;
#endif

main.cpp:

DWORD WINAPI EasyProtectLibrary(LPVOID lpThreadParameter)
{auto ldrpt = (LPLDR_PROTECT_STRUCT)lpThreadParameter;if (ldrpt == nullptr) return 0;BOOL bNewValue = ldrpt->bEnableProtect;BOOL bOldProtect = 0;DWORD index = 0;DWORD bResponse = 0;const WCHAR lpFileName[] = HOOK_MODULE_NAME;PPEB_LDR_DATA pPebLdrData = nullptr;PLDR_DATA_TABLE_ENTRY pLdrDataEntry = nullptr;PLIST_ENTRY pListEntryStart = nullptr;PLIST_ENTRY pListEntryEnd = nullptr;SIZE_T ulTestSize = 0;SIZE_T ulRealSize = wcsnlen_s(lpFileName, MAX_PATH);
#ifdef _WIN64ULONGLONG ModuleSum = NULL;PPEB peb = (PPEB)__readgsqword(0x60);
#elseULONG ModuleSum = NULL;PPEB32 peb = (PPEB32)__readfsdword(0x30);
#endif__try {pPebLdrData = peb->Ldr;// 以模块加载顺序排列的链表pListEntryStart = pPebLdrData->InLoadOrderModuleList.Flink;pListEntryEnd = pPebLdrData->InLoadOrderModuleList.Blink;for (index = 0; pListEntryStart != pListEntryEnd; index++){pLdrDataEntry = CONTAINING_RECORD(pListEntryStart,LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);ulTestSize = wcsnlen_s(pLdrDataEntry->BaseDllName.Buffer, MAX_PATH);if (ulTestSize != ulRealSize || ulTestSize == MAX_PATH){pListEntryStart = pListEntryStart->Flink;continue;}if (!_wcsicmp(pLdrDataEntry->BaseDllName.Buffer, lpFileName)){if (bNewValue == TRUE){// 引用计数主要有两个成员,引用计数为 -1 表示静态加载的模块,// 并且不允许卸载pLdrDataEntry->DdagNode->LoadCount = 0xffffffff;pLdrDataEntry->ObsoleteLoadCount = 0xffff;}else {// 引用计数主要有两个成员,引用计数为 -1 表示静态加载的模块,// 并且不允许卸载pLdrDataEntry->DdagNode->LoadCount = 1;pLdrDataEntry->ObsoleteLoadCount = 1;}// ProcessStaticImport 位域如果为 1, 则任何卸载调用都将直接返回 TRUE// 而不做任何资源释放操作pLdrDataEntry->uFlags.ProcessStaticImport = bNewValue;bResponse |= 0x1;break;}pListEntryStart = pListEntryStart->Flink;}// 以内存位置排列的模块链表pListEntryStart = pPebLdrData->InMemoryOrderModuleList.Flink;pListEntryEnd = pPebLdrData->InMemoryOrderModuleList.Blink;for (index = 0; pListEntryStart != pListEntryEnd; index++){pLdrDataEntry = CONTAINING_RECORD(pListEntryStart,LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);ulTestSize = wcsnlen_s(pLdrDataEntry->BaseDllName.Buffer, MAX_PATH);if (ulTestSize != ulRealSize || ulTestSize == MAX_PATH){pListEntryStart = pListEntryStart->Flink;continue;}if (!_wcsicmp(pLdrDataEntry->BaseDllName.Buffer, lpFileName)){if (bNewValue == TRUE){pLdrDataEntry->DdagNode->LoadCount = 0xffffffff;pLdrDataEntry->ObsoleteLoadCount = 0xffff;}else {pLdrDataEntry->DdagNode->LoadCount = 1;pLdrDataEntry->ObsoleteLoadCount = 1;}pLdrDataEntry->uFlags.ProcessStaticImport = bNewValue;bResponse |= 0x2;break;}pListEntryStart = pListEntryStart->Flink;}// 以初始化顺序加载的模块列表pListEntryStart = pPebLdrData->InInitializationOrderModuleList.Flink;pListEntryEnd = pPebLdrData->InInitializationOrderModuleList.Blink;for (index = 0; pListEntryStart != pListEntryEnd; index++){pLdrDataEntry = CONTAINING_RECORD(pListEntryStart,LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);ulTestSize = wcsnlen_s(pLdrDataEntry->BaseDllName.Buffer, MAX_PATH);if (ulTestSize != ulRealSize || ulTestSize == MAX_PATH){pListEntryStart = pListEntryStart->Flink;continue;}if (!_wcsicmp(pLdrDataEntry->BaseDllName.Buffer, lpFileName)){if (bNewValue == TRUE){pLdrDataEntry->DdagNode->LoadCount = 0xffffffff;pLdrDataEntry->ObsoleteLoadCount = 0xffff;}else {pLdrDataEntry->DdagNode->LoadCount = 1;pLdrDataEntry->ObsoleteLoadCount = 1;}pLdrDataEntry->uFlags.ProcessStaticImport = bNewValue;bResponse |= 0x4;break;}pListEntryStart = pListEntryStart->Flink;}}__except (EXCEPTION_EXECUTE_HANDLER) {OutputDebugStringW(L"Er:Exception occurred while accessing memory.\n");return FALSE;}return bResponse;
}

这样,我们只需要在模块初始化和脱钩时,调用该函数就可以在 winlogon 中简单地保护/脱保护钩子模块。

四、模块如何安全地注入?

为什么要考虑模块的安全注入?

4.1 多进程处理

考虑到系统可能因为登陆多个账户而存在多个 winlogon.exe 进程。所以,在注入时,需要额外考虑一些信息。

WTSGetActiveConsoleSessionId 函数检索控制台会话的会话标识符。 控制台会话是当前附加到物理控制台的会话。 将检索到的 Winlogon.exe 的 PID 通过 ProcessIdToSessionId 函数转换为所属的控制台会话 ID,如果控制台会话 ID 相等,则说明该 winlogon.exe 进程属于当前会话。

我们通过下面的代码实现搜索当前用户会话对应的 winlogon.exe 进程的 PID:

DWORD WINAPI GetActiveConsoleSessionId() {return WTSGetActiveConsoleSessionId();
}BOOL WINAPI IsProcessInSession(DWORD processId, DWORD sessionId) {DWORD session;if (!ProcessIdToSessionId(processId, &session)) {printf("Error: ProcessIdToSessionId failed.\n");return FALSE;}return session == sessionId;
}DWORD WINAPI FindWinlogonProcessId() {DWORD dwProcessId = 0;DWORD activeSessionId = GetActiveConsoleSessionId();if (activeSessionId == 0xFFFFFFFF) {printf("Error: Unable to retrieve active console session ID.\n");return 0;}HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (snapshot == INVALID_HANDLE_VALUE) {printf("Error: CreateToolhelp32Snapshot failed.\n");return 0;}PROCESSENTRY32 entry;entry.dwSize = sizeof(PROCESSENTRY32);if (!Process32First(snapshot, &entry)) {printf("Error: Process32First failed.\n");SafeCloseHandle(snapshot);return 0;}do {if (entry.cntThreads <= 1u) continue; // 跳过僵尸进程if (_wcsicmp(entry.szExeFile, L"winlogon.exe") == 0) {if (IsProcessInSession(entry.th32ProcessID, activeSessionId)) {dwProcessId = entry.th32ProcessID;break;}}} while (Process32Next(snapshot, &entry));SafeCloseHandle(snapshot);return dwProcessId;
}

4.2 模块重载问题

不允许重复加载模块,可能产生冲突。通过检索模块是否已经被加载来校验,下面是使用官方支持的检索过程的示例代码:

BOOL WINAPI CheckProcessHasLoadModule(DWORD dwProcessId, LPCWSTR wsFileName) {/** 参数为TH32CS_SNAPMODULE 或 TH32CS_SNAPMODULE32时,* 如果函数失败并返回ERROR_BAD_LENGTH,则重试该函数直至成功* 进程创建未初始化完成时,CreateToolhelp32Snapshot会返回error 299,但其它情况下不会。*/HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, dwProcessId);while (INVALID_HANDLE_VALUE == hSnapshot) {DWORD dwError = GetLastError();if (dwError == ERROR_BAD_LENGTH) {hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, dwProcessId);continue;}else {printf("CreateToolhelp32Snapshot failed: %d, targetProcessId:%d.\n",dwError, dwProcessId);return FALSE;}}MODULEENTRY32W mi = { 0 };mi.dwSize = sizeof(MODULEENTRY32W); // 第一次使用必须初始化成员BOOL bRet = Module32FirstW(hSnapshot, &mi);while (bRet) {// mi.szModule 是短路径if (wcsstr(wsFileName, mi.szModule) || wcsstr(mi.szModule, wsFileName) ) {if (hSnapshot != NULL) CloseHandle(hSnapshot);return TRUE;}mi.dwSize = sizeof(MODULEENTRY32W);bRet = Module32NextW(hSnapshot, &mi);}if (hSnapshot != NULL) SafeCloseHandle(hSnapshot);return FALSE;
}

你可以决定添加重载时允许卸载旧的模块的操作,这在我代码中暂未考虑实现。

4.3 关键注入代码

我们通过 NTCreateThreadEx 注入和实现远程控制挂钩(可以考虑通过进程通信实现)。但是要注意 NTCreateThreadEx 函数在 XP 上不可用,需要在 Win 7 及以上系统才支持。并且 XP 不使用异步 RPC 过程处理 winlogon 信息。

下面的代码分别展示了通用注入代码和钩子控制代码,运用的原理相同。

a. 模块注入代码:

BOOL WINAPI InjectMouldeHandler(HANDLE hProcess,LPCWSTR pszDllFileName
)
{// 1.目标进程句柄if (hProcess == NULL || pszDllFileName == nullptr){wprintf(L"Error: InvalidSyntax error from InjectMouldeHandler.\n");SetLastError(ERROR_INVALID_PARAMETER);return FALSE;}size_t pathSize = (wcslen(pszDllFileName) + 1) * sizeof(wchar_t);SetLastError(0);// 2.在目标进程中申请空间LPVOID lpPathAddr = VirtualAllocEx( hProcess, 0, pathSize,MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);if (NULL == lpPathAddr){wprintf(L"Error[%d]: Failed to apply memory in the target process!\n", GetLastError());return FALSE;}SetLastError(0);// 3.在目标进程中写入 Dll 路径if (FALSE == WriteProcessMemory( hProcess, lpPathAddr, pszDllFileName, pathSize, NULL) ){wprintf(L"Error[%d]: Failed to write module path in target process!\n", GetLastError());return FALSE;}SetLastError(0);// 4.加载 ntdll.dllHMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");if (NULL == hNtdll){wprintf(L"Error[%d]: Failed to load NTDLL.DLL!\n", GetLastError());VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);return FALSE;}SetLastError(0);// 5.获取 LoadLibraryW 的函数地址, FARPROC 可以自适应 32 位与 64 位FARPROC pFuncProcAddr = MyGetProcAddress64(GetModuleHandleW(L"KernelBase.dll"),"LoadLibraryW");if (NULL == pFuncProcAddr){wprintf(L"Error[%d]: Failed to obtain the address of the LoadLibrary function!\n", GetLastError());VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);return FALSE;}// 6.获取 NtCreateThreadEx 函数地址,该函数在32位与64位下原型不同// _WIN64 用来判断编译环境 ,_WIN32用来判断是否是 Windows 系统
#ifdef _WIN64typedef DWORD(WINAPI* __NtCreateThreadEx)(PHANDLE ThreadHandle,ACCESS_MASK DesiredAccess,LPVOID ObjectAttributes,HANDLE ProcessHandle,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,ULONG CreateThreadFlags,SIZE_T ZeroBits,SIZE_T StackSize,SIZE_T MaximumStackSize,LPVOID pUnkown);
#elsetypedef DWORD(WINAPI* __NtCreateThreadEx)(PHANDLE ThreadHandle,ACCESS_MASK DesiredAccess,LPVOID ObjectAttributes,HANDLE ProcessHandle,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,BOOL CreateSuspended,DWORD dwStackSize,DWORD dw1,DWORD dw2,LPVOID pUnkown);
#endifSetLastError(0);__NtCreateThreadEx NtCreateThreadEx =(__NtCreateThreadEx)MyGetProcAddress64(hNtdll, "NtCreateThreadEx");if (NULL == NtCreateThreadEx){wprintf(L"Error[%d]: Failed to obtain NtCreateThreadEx function address!\n", GetLastError());VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);return FALSE;}SetLastError(0);// 7.在目标进程中创建远线程HANDLE hRemoteThread = NULL;DWORD lpExitCode = 0;DWORD dwStatus = NtCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL,hProcess,(LPTHREAD_START_ROUTINE)pFuncProcAddr, lpPathAddr, 0, 0, 0, 0, NULL);if (NULL == hRemoteThread){wprintf(L"Error[%d]: Failed to create thread in target process!\n", GetLastError());VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);return FALSE;}SetLastError(0);// 8.等待线程结束if (WAIT_TIMEOUT == WaitForSingleObject(hRemoteThread, 2000)){wprintf(L"Error[%d]: Remote thread not responding.\n", GetLastError());VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);return FALSE;}GetExitCodeThread(hRemoteThread, &lpExitCode);if (lpExitCode == 0){wprintf(L"Error: Injection module failed in the target process.\n");VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);return FALSE;}// 9.清理环境VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);SafeCloseHandle(hRemoteThread);return TRUE;
}

b. 钩子控制代码:

BOOL WINAPI RemoteHookingHandler(HANDLE hProcess,PVOID  lpProcAddress,LPVOID lpParameter
)
{// 1.目标进程句柄if (hProcess == NULL || lpProcAddress == nullptr){wprintf(L"Error: InvalidSyntax error from RemoteHookingHandler.\n");SetLastError(ERROR_INVALID_PARAMETER);return FALSE;}SetLastError(0);// 4.加载 ntdll.dllHMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");if (NULL == hNtdll){wprintf(L"Error[%d]: Failed to load NTDLL.DLL!\n", GetLastError());return FALSE;}// 6.获取 NtCreateThreadEx 函数地址,该函数在 32 位与 64 位下原型不同// _WIN64 用来判断编译环境 ,_WIN32用来判断是否是 Windows 系统
#ifdef _WIN64typedef DWORD(WINAPI* __NtCreateThreadEx)(PHANDLE ThreadHandle,ACCESS_MASK DesiredAccess,LPVOID ObjectAttributes,HANDLE ProcessHandle,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,ULONG CreateThreadFlags,SIZE_T ZeroBits,SIZE_T StackSize,SIZE_T MaximumStackSize,LPVOID pUnkown);
#elsetypedef DWORD(WINAPI* __NtCreateThreadEx)(PHANDLE ThreadHandle,ACCESS_MASK DesiredAccess,LPVOID ObjectAttributes,HANDLE ProcessHandle,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,BOOL CreateSuspended,DWORD dwStackSize,DWORD dw1,DWORD dw2,LPVOID pUnkown);
#endifSetLastError(0);__NtCreateThreadEx NtCreateThreadEx =(__NtCreateThreadEx)MyGetProcAddress64(hNtdll, "NtCreateThreadEx");if (NULL == NtCreateThreadEx){wprintf(L"Error[%d]: Failed to obtain NtCreateThreadEx function address!\n",GetLastError());return FALSE;}SetLastError(0);// 7.在目标进程中创建远线程HANDLE hRemoteThread = NULL;DWORD lpExitCode = 0;DWORD dwStatus = NtCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL,hProcess, (LPTHREAD_START_ROUTINE)lpProcAddress, lpParameter, 0, 0, 0, 0, NULL);if (NULL == hRemoteThread){wprintf(L"Error[%d]: Failed to create thread in target process!\n", GetLastError());return FALSE;}SetLastError(0);// 8.等待线程结束if (WAIT_TIMEOUT == WaitForSingleObject(hRemoteThread, 15000)){wprintf(L"Error[%d]: Remote thread not responding.\n", GetLastError());return FALSE;}GetExitCodeThread(hRemoteThread, &lpExitCode);if (lpExitCode == 0){wprintf(L"Error: Control HOOK routine failed in the target process.\n");return FALSE;}// 9.清理环境SafeCloseHandle(hRemoteThread);return TRUE;
}

4.4 提升进程权限

进程需要获取足够的权限。可以通过程序清单文件或者以编程的方式来实现获取管理员模拟令牌和调试权限。

下面的代码通过 IsRunAsAdministrator 判断进程是否以管理员身份运行,如果没有,则可以通过 ElevateCurrentProcess 尝试重启进程并请求以管理员身份运行。

BOOL WINAPI IsRunAsAdministrator() // 判断是否以管理员身份运行
{BOOL bIsElevated = FALSE;HANDLE hToken = NULL;if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {TOKEN_ELEVATION te = { 0 };DWORD dwReturnLength = 0;if (GetTokenInformation(hToken, TokenElevation, &te, sizeof(te), &dwReturnLength)) {if (dwReturnLength == sizeof(te))bIsElevated = te.TokenIsElevated;}SafeCloseHandle(hToken);}return bIsElevated;
}BOOL WINAPI ElevateCurrentProcess(LPCWSTR wsFilePath)  // 尝试运行管理员进程
{TCHAR szPath[MAX_PATH] = { 0 };if (GetModuleFileNameW(NULL, szPath, MAX_PATH) != 0){// Launch itself as administrator.SHELLEXECUTEINFO sei = { 0 };sei.cbSize = sizeof(SHELLEXECUTEINFO);sei.lpVerb = L"runas";sei.lpFile = szPath;sei.lpParameters = (LPCTSTR)wsFilePath;sei.nShow = SW_SHOWNORMAL;if (!ShellExecuteEx(&sei)){DWORD dwStatus = GetLastError();if (dwStatus == ERROR_CANCELLED){// The user refused to allow privileges elevation.printf("The user refused to allow privileges elevation.\n");return FALSE;}else if (dwStatus == ERROR_FILE_NOT_FOUND){// The file defined by lpFile was not found and// an error message popped up.printf("Error Cannot Access Files.\n");return FALSE;}return FALSE;}return TRUE;}return FALSE;
}

在成功以管理员身份运行后,就可以启用 SE_DEBUG 模拟令牌访问权限:

BOOL WINAPI EnableDebugPrivilege()
{HANDLE handleToken = NULL;if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &handleToken)) {printf("Error OpenProcessToken.\n");return FALSE;}LUID debugNameValue = { 0 };if (!LookupPrivilegeValueW(nullptr, SE_DEBUG_NAME, &debugNameValue)) {SafeCloseHandle(handleToken);printf("Error LookupPrivilegeValue.\n");return FALSE;}TOKEN_PRIVILEGES tokenPri = { 0 };tokenPri.PrivilegeCount = 1;tokenPri.Privileges[0].Luid = debugNameValue;tokenPri.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;if (!AdjustTokenPrivileges(handleToken, FALSE, &tokenPri, sizeof(tokenPri), nullptr, nullptr)) {SafeCloseHandle(handleToken);printf("Error AdjustTokenPrivileges.\n");return FALSE;}SafeCloseHandle(handleToken);return TRUE;
}

4.5 处理句柄和环境检测

全部的空闲句柄通过及时地 CloseHandle 来完成,以避免句柄泄露:

BOOL WINAPI SafeCloseHandle(HANDLE handle)
{BOOL bResponse = TRUE;if (handle != nullptr) {bResponse = CloseHandle(handle);handle = nullptr;}return bResponse;
}

随着微软缓解策略不断地实施,在未来可能为 Winlogon 进程升级严格的安全策略,通过下面的检测代码判断是否可以执行代码注入:

BOOL WINAPI IsProcessStrictMitigativeProtectionOff(HANDLE hProcess) {PROCESS_MITIGATION_DYNAMIC_CODE_POLICY dynamicCodePolicy = { 0 };PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY signaturePolicy = { 0 };PROCESS_MITIGATION_CONTROL_FLOW_GUARD_POLICY cfgPolicy = { 0 };// Actually retrieve the mitigation policy for ACGif (!GetProcessMitigationPolicy(hProcess,ProcessDynamicCodePolicy, &dynamicCodePolicy, sizeof(dynamicCodePolicy))) {printf("[!] Could not GetProcessMitigationPolicy. [%d]\n", GetLastError());return FALSE;}if (dynamicCodePolicy.ProhibitDynamicCode || dynamicCodePolicy.AllowRemoteDowngrade ||dynamicCodePolicy.AllowThreadOptOut) {printf("Detect[0]: ProcessDynamicCodePolicy on.\n");return FALSE;}// Retrieve mitigation policy for loading arbitrary DLLsif (!GetProcessMitigationPolicy(hProcess,ProcessSignaturePolicy, &signaturePolicy, sizeof(signaturePolicy))) {printf("Could not GetProcessMitigationPolicy. [%d]\n", GetLastError());return FALSE;}if (signaturePolicy.AuditMicrosoftSignedOnly ||signaturePolicy.AuditStoreSignedOnly || signaturePolicy.MicrosoftSignedOnly ||signaturePolicy.MitigationOptIn ||signaturePolicy.StoreSignedOnly) {printf("Detect[1]: ProcessSignaturePolicy on.\n");return FALSE;}// Retrieve mitigation policy for strict Control Flow Guardsif (!GetProcessMitigationPolicy(hProcess,ProcessControlFlowGuardPolicy, &cfgPolicy, sizeof(cfgPolicy))) {printf("Could not GetProcessMitigationPolicy. [%d]\n", GetLastError());return FALSE;}if (cfgPolicy.EnableXfg || cfgPolicy.StrictMode){printf("Detect[2]: ProcessControlFlowGuardPolicy on.\n");return FALSE;}return TRUE;
}

该例程检测进程的部分安全缓解策略(包括 ACG、DSG、CFG 等)的等级,当达到无法注入代码的级别时,函数返回 FALSE,表示不可以继续执行远程代码注入,以防止严重错误。当然,部分策略表示允许远程关闭缓解,可以进一步完善代码实现在可以关闭时候关闭防护。

五、编译代码和测试运行

测试功能的代码调用了挂钩例程、脱钩例程和卸载例程,中间需要回车确认继续。

void RunHookingAndCleanup(HANDLE hWinlogonProc, const HANDLER_INFO_STRUCT& lpHandleInfo) {getchar();if (TRUE == RemoteHookingHandler(hWinlogonProc, lpHandleInfo.lpHookHandler, NULL)) {printf("Enable Hooks in winlogon SUCCESSFULLY.\n");}getchar();if (TRUE == RemoteHookingHandler(hWinlogonProc, lpHandleInfo.lpUnhookHandler, NULL)) {printf("Disable Hooks in winlogon SUCCESSFULLY.\n");}getchar();if (TRUE == RemoteHookingHandler(hWinlogonProc, lpHandleInfo.lpSafeFreeLib, NULL)) {printf("UnLoadLibrary in winlogon SUCCESSFULLY.\n");}SafeCloseHandle(hWinlogonProc);
}

完整的代码我也展示给大家。只作为一个参考,代码还不够完善。

5.1 钩子模块代码

ldrp.h: 

#pragma once
#include <winnt.h>
#include <WTypesbase.h>
//#include <winternl.h>// Kernels | x64 | Windows 10 | 2016 | 2210 22H2(May 2023 Update)//0x18 bytes (sizeof)
typedef struct _RTL_BALANCED_NODE
{union{_RTL_BALANCED_NODE* Children[2];                             //0x0struct{_RTL_BALANCED_NODE* Left;                                //0x0_RTL_BALANCED_NODE* Right;                               //0x8};};union{struct{UCHAR Red : 1;                                                    //0x10UCHAR Balance : 2;                                                //0x10};ULONGLONG ParentValue;                                                //0x10};
}RTL_BALANCED_NODE, * PRTL_BALANCED_NODE, * LPRTL_BALANCED_NODE;//0x4 bytes (sizeof)
enum _LDR_DLL_LOAD_REASON
{LoadReasonStaticDependency = 0,LoadReasonStaticForwarderDependency = 1,LoadReasonDynamicForwarderDependency = 2,LoadReasonDelayloadDependency = 3,LoadReasonDynamicLoad = 4,LoadReasonAsImageLoad = 5,LoadReasonAsDataLoad = 6,LoadReasonEnclavePrimary = 7,LoadReasonEnclaveDependency = 8,LoadReasonUnknown = -1
};typedef _LDR_DLL_LOAD_REASON    LDR_DLL_LOAD_REASON;//0x10 bytes (sizeof)
typedef struct _LDR_SERVICE_TAG_RECORD
{_LDR_SERVICE_TAG_RECORD* Next;                                           //0x0ULONG ServiceTag;                                                       //0x8
}LDR_SERVICE_TAG_RECORD, * PLDR_SERVICE_TAG_RECORD;//0x8 bytes (sizeof)
typedef struct _LDRP_CSLIST
{SINGLE_LIST_ENTRY* Tail;                                                //0x0
}LDRP_CSLIST;//0x4 bytes (sizeof)
enum _LDR_DDAG_STATE
{LdrModulesMerged = -5,LdrModulesInitError = -4,LdrModulesSnapError = -3,LdrModulesUnloaded = -2,LdrModulesUnloading = -1,LdrModulesPlaceHolder = 0,LdrModulesMapping = 1,LdrModulesMapped = 2,LdrModulesWaitingForDependencies = 3,LdrModulesSnapping = 4,LdrModulesSnapped = 5,LdrModulesCondensed = 6,LdrModulesReadyToInit = 7,LdrModulesInitializing = 8,LdrModulesReadyToRun = 9
};typedef _LDR_DDAG_STATE   LDR_DDAG_STATE;//0x50 bytes (sizeof)
typedef struct _LDR_DDAG_NODE
{LIST_ENTRY Modules;                                                     //0x0PLDR_SERVICE_TAG_RECORD ServiceTagList;                                 //0x10ULONG LoadCount;                                                        //0x18ULONG LoadWhileUnloadingCount;                                          //0x1cULONG LowestLink;                                                       //0x20LDRP_CSLIST Dependencies;                                               //0x28LDRP_CSLIST IncomingDependencies;                                       //0x30LDR_DDAG_STATE State;                                                   //0x38SINGLE_LIST_ENTRY CondenseLink;                                         //0x40ULONG PreorderNumber;                                                   //0x48
}LDR_DDAG_NODE, * PLDR_DDAG_NODE;typedef struct _UNICODE_STRING {USHORT Length;USHORT MaximumLength;PWSTR  Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING* PUNICODE_STRING;
typedef const UNICODE_STRING* PCUNICODE_STRING;//0x120 bytes (sizeof)
typedef struct _LDR_DATA_TABLE_ENTRY
{LIST_ENTRY InLoadOrderLinks;                                            //0x0LIST_ENTRY InMemoryOrderLinks;                                          //0x10LIST_ENTRY InInitializationOrderLinks;                                  //0x20VOID* DllBase;                                                          //0x30VOID* EntryPoint;                                                       //0x38ULONG SizeOfImage;                                                      //0x40UNICODE_STRING FullDllName;                                             //0x48UNICODE_STRING BaseDllName;                                             //0x58union{UCHAR FlagGroup[4];                                                 //0x68ULONG Flags;                                                        //0x68struct{ULONG PackagedBinary : 1;                                         //0x68ULONG MarkedForRemoval : 1;                                       //0x68ULONG ImageDll : 1;                                               //0x68ULONG LoadNotificationsSent : 1;                                  //0x68ULONG TelemetryEntryProcessed : 1;                                //0x68ULONG ProcessStaticImport : 1;                                    //0x68ULONG InLegacyLists : 1;                                          //0x68ULONG InIndexes : 1;                                              //0x68ULONG ShimDll : 1;                                                //0x68ULONG InExceptionTable : 1;                                       //0x68ULONG ReservedFlags1 : 2;                                         //0x68ULONG LoadInProgress : 1;                                         //0x68ULONG LoadConfigProcessed : 1;                                    //0x68ULONG EntryProcessed : 1;                                         //0x68ULONG ProtectDelayLoad : 1;                                       //0x68ULONG ReservedFlags3 : 2;                                         //0x68ULONG DontCallForThreads : 1;                                     //0x68ULONG ProcessAttachCalled : 1;                                    //0x68ULONG ProcessAttachFailed : 1;                                    //0x68ULONG CorDeferredValidate : 1;                                    //0x68ULONG CorImage : 1;                                               //0x68ULONG DontRelocate : 1;                                           //0x68ULONG CorILOnly : 1;                                              //0x68ULONG ChpeImage : 1;                                              //0x68ULONG ReservedFlags5 : 2;                                         //0x68ULONG Redirected : 1;                                             //0x68ULONG ReservedFlags6 : 2;                                         //0x68ULONG CompatDatabaseProcessed : 1;                                //0x68}uFlags;};USHORT ObsoleteLoadCount;                                               //0x6cUSHORT TlsIndex;                                                        //0x6eLIST_ENTRY HashLinks;                                                   //0x70ULONG TimeDateStamp;                                                    //0x80struct ACTIVATION_CONTEXT* EntryPointActivationContext;                 //0x88VOID* Lock;                                                             //0x90LDR_DDAG_NODE* DdagNode;                                                //0x98LIST_ENTRY NodeModuleLink;                                              //0xa0struct LDRP_LOAD_CONTEXT* LoadContext;                                  //0xb0VOID* ParentDllBase;                                                    //0xb8VOID* SwitchBackContext;                                                //0xc0RTL_BALANCED_NODE BaseAddressIndexNode;                                 //0xc8RTL_BALANCED_NODE MappingInfoIndexNode;                                 //0xe0ULONGLONG OriginalBase;                                                 //0xf8LARGE_INTEGER LoadTime;                                                 //0x100ULONG BaseNameHashValue;                                                //0x108LDR_DLL_LOAD_REASON LoadReason;                                         //0x10cULONG ImplicitPathOptions;                                              //0x110ULONG ReferenceCount;                                                   //0x114ULONG DependentLoadFlags;                                               //0x118UCHAR SigningLevel;                                                     //0x11c
}LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;//0x58 bytes (sizeof)
typedef struct _PEB_LDR_DATA32
{ULONG Length; // +0x00BOOLEAN Initialized; // +0x04PVOID SsHandle; // +0x08LIST_ENTRY InLoadOrderModuleList; // +0x0cLIST_ENTRY InMemoryOrderModuleList; // +0x14LIST_ENTRY InInitializationOrderModuleList;// +0x1c
} PEB_LDR_DATA32, * PPEB_LDR_DATA32; // +0x24typedef struct _PEB32
{UCHAR InheritedAddressSpace;                                            //0x0UCHAR ReadImageFileExecOptions;                                         //0x1UCHAR BeingDebugged;                                                    //0x2union{UCHAR BitField;                                                     //0x3struct{UCHAR ImageUsesLargePages : 1;                                    //0x3UCHAR IsProtectedProcess : 1;                                     //0x3UCHAR IsImageDynamicallyRelocated : 1;                            //0x3UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3UCHAR IsPackagedProcess : 1;                                      //0x3UCHAR IsAppContainer : 1;                                         //0x3UCHAR IsProtectedProcessLight : 1;                                //0x3UCHAR IsLongPathAwareProcess : 1;                                 //0x3};};PVOID Mutant;                                                           //0x4PVOID ImageBaseAddress;                                                 //0x8PEB_LDR_DATA32* Ldr;                                                    //0xcstruct RTL_USER_PROCESS_PARAMETERS* ProcessParameters;                  //0x10PVOID SubSystemData;                                                    //0x14PVOID ProcessHeap;                                                      //0x18RTL_CRITICAL_SECTION* FastPebLock;                                      //0x1cSLIST_HEADER* volatile AtlThunkSListPtr;                                //0x20PVOID IFEOKey;                                                          //0x24
} PEB32, * PPEB32;typedef struct _STRING64
{USHORT Length;                                                          //0x0USHORT MaximumLength;                                                   //0x2ULONGLONG Buffer;                                                       //0x8
}STRING64, * LPSTRING64;typedef struct _PEB_LDR_DATA64
{ULONG Length;                                                           //0x0UCHAR Initialized;                                                      //0x4PVOID SsHandle;                                                         //0x8LIST_ENTRY InLoadOrderModuleList;                                       //0x10LIST_ENTRY InMemoryOrderModuleList;                                     //0x20LIST_ENTRY InInitializationOrderModuleList;                             //0x30PVOID EntryInProgress;                                                  //0x40UCHAR ShutdownInProgress;                                               //0x48PVOID ShutdownThreadId;                                                 //0x50
}PEB_LDR_DATA64, * PPEB_LDR_DATA64;typedef struct _PEB64
{UCHAR InheritedAddressSpace;                                            //0x0UCHAR ReadImageFileExecOptions;                                         //0x1UCHAR BeingDebugged;                                                    //0x2union{UCHAR BitField;                                                     //0x3struct{UCHAR ImageUsesLargePages : 1;                                    //0x3UCHAR IsProtectedProcess : 1;                                     //0x3UCHAR IsImageDynamicallyRelocated : 1;                            //0x3UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3UCHAR IsPackagedProcess : 1;                                      //0x3UCHAR IsAppContainer : 1;                                         //0x3UCHAR IsProtectedProcessLight : 1;                                //0x3UCHAR IsLongPathAwareProcess : 1;                                 //0x3};};UCHAR Padding0[4];                                                      //0x4ULONGLONG Mutant;                                                       //0x8ULONGLONG ImageBaseAddress;                                             //0x10PEB_LDR_DATA64* Ldr;                                                    //0x18ULONGLONG ProcessParameters;                                            //0x20ULONGLONG SubSystemData;                                                //0x28ULONGLONG ProcessHeap;                                                  //0x30ULONGLONG FastPebLock;                                                  //0x38ULONGLONG AtlThunkSListPtr;                                             //0x40ULONGLONG IFEOKey;                                                      //0x48
}PEB64, * PPEB64;#ifdef _WIN64
typedef PEB64                   PEB;
typedef PPEB64                  PPEB;
typedef PEB_LDR_DATA64          PEB_LDR_DATA;
typedef PPEB_LDR_DATA64         PPEB_LDR_DATA;
#else
typedef PEB32                   PEB;
typedef PPEB32                  PPEB;
typedef PEB_LDR_DATA32          PEB_LDR_DATA;
typedef PPEB_LDR_DATA32         PPEB_LDR_DATA;
#endif

main.cpp

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <rpc.h>
#include <WtsApi32.h>
#include <corecrt_wstdio.h>
#include <Psapi.h>
#include "ldrp.h"
#include <cstddef>#pragma comment(lib, "WtsApi32.lib")#define HOOK_MODULE_NAME L"WMsgKMsgHookCore.dll"typedef int(__fastcall* __WMsgKMessageHandler)(unsigned int         uMachineState,unsigned int         uMsgWLGenericKey,PRPC_ASYNC_STATE     pAsync,LPDWORD              lpStatus   // int* pReserved);typedef struct HANDLER_INFO_STRUCT
{DWORD  cbSize = 0;DWORD  dwMainThread = 0;HMODULE hHookModule = nullptr;LPVOID lpHookHandler = nullptr;LPVOID lpUnhookHandler = nullptr;LPVOID lpSafeFreeLib = nullptr;LPVOID lpWinlogonBase = nullptr;LPVOID lpHookAddress = nullptr;//LPVOID lpEPLFuncAddress = nullptr;
}HANDLER_INFO_STRUCT, * LPHANDLER_INFO_STRUCT;typedef struct _LDR_PROTECT_STRUCT
{BOOL  bEnableProtect;
}LDR_PROTECT_STRUCT, * LPLDR_PROTECT_STRUCT;#pragma data_seg("WMsgHookData")
CHAR pOriginalBytes[13] = { 0 };
HANDLER_INFO_STRUCT lpgHandlerInfo = { 0 };
#pragma data_seg()
#pragma comment(linker,"/SECTION:WMsgHookData,RWS")extern "C" {__declspec(dllexport) BOOL   WINAPI RemoteSetHookBaseAddress(DWORD lpBaseOffest);__declspec(dllexport) BOOL   WINAPI GetHandlerAddress(LPHANDLER_INFO_STRUCT lpHandlerInfo);
}BOOL   WINAPI InitHandlerAddress();
BOOL   WINAPI IsHookBaseAddressValidInternal();
DWORD  WINAPI HookWMsgKMessageExceptionHandler(LPVOID lpThreadParameter);
DWORD  WINAPI UnhookWMsgKMessageExceptionHandler(LPVOID lpThreadParameter);
DWORD  WINAPI SafeFreeLibrary(LPVOID lpThreadParameter);
DWORD  WINAPI EasyProtectLibrary(LPVOID lpThreadParameter);
BOOL   WINAPI SvcMessageBox(LPWSTR lpCap, LPWSTR lpMsg, DWORD style, BOOL bWait, DWORD& result);int __fastcall HookedWMsgKMessageHandler(unsigned int         uMachineState,unsigned int         uMsgWLGenericKey,PRPC_ASYNC_STATE     pAsync,LPDWORD              lpStatus   // int* pReserved
)
{int dwFunResponse = 0;DWORD dwMsgBoxResponse = 0;WCHAR wsTitle[] = L"TestHookingHandler";WCHAR wsSvcMsg[100] = { 0 };/** ///* * WMsgMessageHandler 控制 账户状态/UAC 相关的功能* uMsgCSessionKey       uMsgWLGenericKey        MajorAction* 0001                  04002009            关闭本地计算机* 0001                  04002003            重启本地计算机* 0500                  1E88                请求提升管理员权限* 0501                  06B8                已经提升管理员权限* 0403                  0000                切换用户进行时(建议窗口在 Winlogon 下创建,并置前端)* 0202                  0000                切换用户恢复时(具体操作未知)* 0205                  0000                切换用户恢复时(具体操作未知)* 注意:(1)对于非已知代码的情况,不要使用阻滞过程,否则会导致死锁。*       (2)如果不需要执行指定的过程,函数返回值必须是 1,如果为 0 可能会陷入等待。* * * * WMsgKMessageHandler 控制 系统热键/注销 相关的功能* uMsgCSessionKey       uMsgWLGenericKey        MajorAction* 0404                  4                       Ctrl+Shift+Esc, 任务管理器* 0404                  0                       Ctrl+Alt+Delete, 安全桌面* 0404                  5                       Win+L, 锁屏, LogonUI Windows* 0404                  7                       Win+P, 投影屏幕* 0404                  6                       Win+U, 设置/辅助功能* 0404                  C                       Win+Plus, 放大镜* 0404                  D                       Win+Ctrl+Enter, 讲述人* 0404                  E                       Win+Enter, 未知* 0402                  5                       左Alt+LShift+Print Screen, 高对比度主题* 0402                  1                       连续按下五次左侧 Shift,滞粘键* 0402                  2                       按住右侧 Shift 键 8 秒,筛选键* 0001                  3                       Alt+F4 资源管理器,重启计算机* 0001                  4009                    Alt+F4 资源管理器,关闭计算机* 0001                  0                       Alt+F4 资源管理器,注销计算机* * *///if (uMsgCSessionKey == 0x404)//{swprintf_s(wsSvcMsg, L"Intercepted procedure call message: \nuMsgCSessionKey: %X, uMsgWLGenericKey: %X.\n ", uMachineState, uMsgWLGenericKey);if (SvcMessageBox(wsTitle, wsSvcMsg, MB_YESNO | MB_ICONINFORMATION | MB_SYSTEMMODAL, TRUE, dwMsgBoxResponse)){if (dwMsgBoxResponse == IDNO){return 1;}}//}// UnHookUnhookWMsgKMessageExceptionHandler(NULL);auto WMsgKMessageHandler = (__WMsgKMessageHandler)lpgHandlerInfo.lpHookAddress;dwFunResponse = WMsgKMessageHandler(uMachineState, uMsgWLGenericKey, pAsync, lpStatus);// Re-hookHookWMsgKMessageExceptionHandler(NULL);return dwFunResponse;
}BOOL APIENTRY DllMain( HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved)
{DisableThreadLibraryCalls(hModule);WCHAR wsFileName[MAX_PATH] = { 0 };WCHAR wsDebugPrint[56] = { 0 };switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:GetModuleFileNameW(NULL, wsFileName, MAX_PATH);if (!wcsstr(wsFileName, L"winlogon.exe")){swprintf_s(wsDebugPrint, L"This thread is not in winlogon, TID: [%d].\n",GetCurrentThreadId());OutputDebugStringW(wsDebugPrint);return TRUE;}else {swprintf_s(wsDebugPrint, L"Init Hook in winlogon, TID: [%d].\n",GetCurrentThreadId());OutputDebugStringW(wsDebugPrint);lpgHandlerInfo.hHookModule = hModule;return InitHandlerAddress();}break;case DLL_THREAD_ATTACH:break;case DLL_THREAD_DETACH:break;case DLL_PROCESS_DETACH:if (!wcsstr(wsFileName, L"winlogon.exe")){return TRUE;}UnhookWMsgKMessageExceptionHandler(NULL);break;}return TRUE;
}BOOL WINAPI SvcMessageBox(LPWSTR lpCap, LPWSTR lpMsg, DWORD style, BOOL bWait, DWORD& result)
{if (NULL == lpMsg || NULL == lpCap)return FALSE;result = 0;DWORD sessionXId = WTSGetActiveConsoleSessionId();return WTSSendMessageW(WTS_CURRENT_SERVER_HANDLE, sessionXId,lpCap, (DWORD)wcslen(lpCap) * sizeof(DWORD),lpMsg, (DWORD)wcslen(lpMsg) * sizeof(DWORD),style, 0, &result, bWait);
}BOOL WINAPI InitHandlerAddress()
{WCHAR wsDbPrint[155] = { 0 };swprintf_s(wsDbPrint, L"Op:[GetModuleHandleW] ArgList:[HOOK_MODULE_NAME].\n");OutputDebugStringW(wsDbPrint);SetLastError(0);HMODULE hHookCore = GetModuleHandleW(HOOK_MODULE_NAME);if (hHookCore == NULL){swprintf_s(wsDbPrint, L"Er:[GetModuleHandleW, HOOK_MODULE_NAME] Status:[%d].\n",GetLastError());OutputDebugStringW(wsDbPrint);return FALSE;}swprintf_s(wsDbPrint, L"Op:[GetModuleHandleW] ArgList:[Current].\n");OutputDebugStringW(wsDbPrint);SetLastError(0);HMODULE hModuleBase = GetModuleHandleW(NULL);if (hModuleBase == NULL){swprintf_s(wsDbPrint, L"Er:[GetModuleHandleW, NULL] Status:[%d].\n",GetLastError());OutputDebugStringW(wsDbPrint);return FALSE;}swprintf_s(wsDbPrint, L"Op:[InitHandlerAddress] ArgList:[].\n");OutputDebugStringW(wsDbPrint);// 将函数指针等写入全局变量lpgHandlerInfo.lpWinlogonBase = hModuleBase;  // Winlogon 进程加载基址lpgHandlerInfo.dwMainThread = GetCurrentThreadId();  // 保存初始化例程所在线程TIDlpgHandlerInfo.lpHookHandler = &HookWMsgKMessageExceptionHandler;lpgHandlerInfo.lpUnhookHandler =&UnhookWMsgKMessageExceptionHandler;lpgHandlerInfo.lpSafeFreeLib = &SafeFreeLibrary;//lpgHandlerInfo.lpEPLFuncAddress = &EasyProtectLibrary;if (lpgHandlerInfo.lpHookHandler == NULL ||lpgHandlerInfo.lpUnhookHandler == NULL || lpgHandlerInfo.lpSafeFreeLib == NULL){swprintf_s(wsDbPrint, L"Er:[CheckGlobalParameter] Status:[nullpointer].\n");OutputDebugStringW(wsDbPrint);return FALSE;}swprintf_s(wsDbPrint, L"Op:[InitHandlerAddress], pt:[0x%I64X].\n", reinterpret_cast<UINT64>(&lpgHandlerInfo));OutputDebugStringW(wsDbPrint);return TRUE;
}// 用于检测虚拟地址是否有效,且对当前进程可以读写访问
BOOL IsExecutableAddress(LPVOID VirtualAddress)
{BOOL IsOk = FALSE;MEMORY_BASIC_INFORMATION MemoryBasicInfo = { 0 };if (!VirtualQuery(VirtualAddress, &MemoryBasicInfo, sizeof(MEMORY_BASIC_INFORMATION))) {return IsOk;}if ( (MemoryBasicInfo.State == MEM_COMMIT) &&(   (MemoryBasicInfo.Protect & PAGE_READONLY) ||(MemoryBasicInfo.Protect & PAGE_READWRITE) ||(MemoryBasicInfo.Protect & PAGE_EXECUTE_READ) || (MemoryBasicInfo.Protect & PAGE_EXECUTE_READWRITE) ) ){IsOk = TRUE;}return IsOk;
}// 检测地址指针是否有效
BOOL CheckPointerValidity(LPVOID ptr) {// 获取指针的值uintptr_t pointerValue = reinterpret_cast<uintptr_t>(ptr);WCHAR errString[56] = { 0 };__try {// 额外的检查条件if ((pointerValue >> 56) == 0xFF ||  // 高位是 FF!IsExecutableAddress(ptr)  // 进一步检查指针地址是否可读可写) {throw L"Invalid pointer";}// 如果上面的检查通过,表示指针是有效且可写的return TRUE;}__except (EXCEPTION_EXECUTE_HANDLER) {// 捕获异常,指针无效或不可写swprintf_s(errString, L"Read access is not allowed at 0x%I64X.\n", pointerValue);OutputDebugStringW(errString);return FALSE;}
}BOOL WINAPI RemoteSetHookBaseAddress(DWORD lpBaseOffest)
{if (lpgHandlerInfo.lpWinlogonBase == nullptr ||lpBaseOffest == NULL){SetLastError(ERROR_INVALID_PARAMETER);return FALSE;}PVOID lpHookAddressTemp = reinterpret_cast<PVOID>(lpBaseOffest +reinterpret_cast<UINT64>(lpgHandlerInfo.lpWinlogonBase));// 设置全局指针地址lpgHandlerInfo.lpHookAddress = lpHookAddressTemp;return TRUE;
}BOOL WINAPI IsHookBaseAddressValidInternal()
{PVOID lpWinlogonBaseTemp = lpgHandlerInfo.lpWinlogonBase;PVOID lpHookAddressTemp = lpgHandlerInfo.lpHookAddress;HMODULE hWinlogonHandle =reinterpret_cast<HMODULE>(lpWinlogonBaseTemp);MODULEINFO moduleInfo = { 0 };UINT64 ulModuleBaseLb = 0, ulModuleBaseUp = 0;UINT64 ulHookAddress = reinterpret_cast<UINT64>(lpHookAddressTemp);if (lpWinlogonBaseTemp == nullptr || lpHookAddressTemp == nullptr){SetLastError(ERROR_INVALID_PARAMETER);return FALSE;}if (!CheckPointerValidity(lpWinlogonBaseTemp)){SetLastError(ERROR_INVALID_ADDRESS);return FALSE;}if (!GetModuleInformation(GetCurrentProcess(),hWinlogonHandle, &moduleInfo, sizeof(moduleInfo))) {SetLastError(ERROR_INVALID_DLL);return FALSE;}ulModuleBaseLb = reinterpret_cast<UINT64>(lpWinlogonBaseTemp);ulModuleBaseUp = reinterpret_cast<UINT64>(lpWinlogonBaseTemp) + moduleInfo.SizeOfImage;if (ulHookAddress >= ulModuleBaseUp || ulHookAddress <= ulModuleBaseLb){SetLastError(ERROR_INVALID_DATA);return FALSE;}if (!CheckPointerValidity(lpHookAddressTemp)){SetLastError(ERROR_INVALID_ADDRESS);return FALSE;}return TRUE;
}BOOL WINAPI GetHandlerAddress(LPHANDLER_INFO_STRUCT lpHandlerInfo)
{// 结构体指针不为空if (lpHandlerInfo == nullptr){OutputDebugStringW(L"Er:[GetHandlerAddress] status:[Invalid nullptr].\n");return FALSE;}// 计算 cbSize 成员的地址SIZE_T cbSizeOff = offsetof(HANDLER_INFO_STRUCT, cbSize);PDWORD lpcbSize = reinterpret_cast<PDWORD>(lpHandlerInfo + cbSizeOff);// 保证传入结构体大小等于预期的大小if (*lpcbSize != sizeof(HANDLER_INFO_STRUCT)){OutputDebugStringW(L"Er:[GetHandlerAddress] status:[Invalid HANDLER_INFO_STRUCT Size].\n");return FALSE;}// 将传入结构体指针中的各个字段赋值给lpHandlerInfo结构体中对应的字段,并返回TRUElpHandlerInfo->lpHookHandler = lpgHandlerInfo.lpHookHandler;lpHandlerInfo->lpUnhookHandler = lpgHandlerInfo.lpUnhookHandler;lpHandlerInfo->lpHookAddress = lpgHandlerInfo.lpHookAddress;lpHandlerInfo->lpWinlogonBase = lpgHandlerInfo.lpWinlogonBase;lpHandlerInfo->lpSafeFreeLib = lpgHandlerInfo.lpSafeFreeLib;lpHandlerInfo->dwMainThread = lpgHandlerInfo.dwMainThread;lpHandlerInfo->hHookModule = lpgHandlerInfo.hHookModule;return TRUE;
}DWORD WINAPI HookWMsgKMessageExceptionHandler(LPVOID lpThreadParameter)
{// 首先进行必要的安全检查if (IsHookBaseAddressValidInternal() == FALSE){OutputDebugStringW(L"Er:[HookHandler] status:[Invalid Address].\n");return GetLastError();}DWORD bResponse = FALSE;DWORD oldProtect = 0;LPVOID hHookAddress = lpgHandlerInfo.lpHookAddress;LPVOID hWMsgHandler = &HookedWMsgKMessageHandler;SIZE_T bufferSize = 0;LDR_PROTECT_STRUCT ldrpt = { TRUE };// our trampolineunsigned char boing[] = {0x49, 0xbb, 0xde, 0xad,0xc0, 0xde, 0xde, 0xad,0xc0, 0xde, 0x41, 0xff,0xe3 };// add in the address of our hook*(LPVOID*)(boing + 2) = hWMsgHandler;bufferSize = sizeof(boing);// Enable FreeLibrary Protectldrpt.bEnableProtect = TRUE;if (EasyProtectLibrary(&ldrpt) == 0){OutputDebugStringW(L"Er:[HookHandler] status:[EasyProtectLibrary failed].\n");return FALSE;}// disable write protectSetLastError(0);bResponse = VirtualProtect(hHookAddress, bufferSize, PAGE_EXECUTE_READWRITE, &oldProtect);if (!bResponse){OutputDebugStringW(L"Er:[HookHandler] status:[VirtualProect failed, EXECUTE_READWRITE].\n");return GetLastError();}// save the original bytesif (memcpy_s(pOriginalBytes, bufferSize, hHookAddress, bufferSize) != 0){OutputDebugStringW(L"Er:[HookHandler] status:[memcpy failed, 1:2].\n");return GetLastError();}SetLastError(0);// write the hookif (memcpy_s(hHookAddress, bufferSize, boing, bufferSize) != 0){OutputDebugStringW(L"Er:[HookHandler] status:[memcpy failed, 2:2].\n");return GetLastError();}// Flush Cache to make code workbResponse = FlushInstructionCache(GetCurrentProcess(), hHookAddress, bufferSize);if (!bResponse){OutputDebugStringW(L"Er:[HookHandler] status:[FlushInstructionCache failed].\n");return GetLastError();}// enable write protectbResponse = VirtualProtect(hHookAddress, bufferSize, oldProtect, &oldProtect);if (!bResponse){OutputDebugStringW(L"Er:[HookHandler] status:[VirtualProect failed].\n");return GetLastError();}return TRUE;
}DWORD WINAPI UnhookWMsgKMessageExceptionHandler(LPVOID lpThreadParameter)
{DWORD bResponse = FALSE;DWORD oldProtect = 0;PVOID hookAddress = lpgHandlerInfo.lpHookAddress;// disable write protectbResponse = VirtualProtect(hookAddress, 13, PAGE_EXECUTE_READWRITE, &oldProtect);if (!bResponse){OutputDebugStringW(L"Er:[UnHookHandler] status:[VirtualProect failed, EXECUTE_READWRITE].\n");return bResponse;}// recover original bytesif (memcpy_s(hookAddress, 13, pOriginalBytes, 13) != 0){OutputDebugStringW(L"Er:[UnHookHandler] status:[memcpy failed].\n");return bResponse;}// Flush Cache to make code workbResponse = FlushInstructionCache(GetCurrentProcess(), hookAddress, 13);if (!bResponse){OutputDebugStringW(L"Er:[HookHandler] status:[FlushInstructionCache failed].\n");return GetLastError();}// enable write protectbResponse = VirtualProtect(hookAddress, 13, oldProtect, &oldProtect);if (!bResponse){OutputDebugStringW(L"Er:[UnHookHandler] status:[VirtualProect failed].\n");return bResponse;}return TRUE;
}DWORD WINAPI EasyProtectLibrary(LPVOID lpThreadParameter)
{auto ldrpt = (LPLDR_PROTECT_STRUCT)lpThreadParameter;if (ldrpt == nullptr) return 0;BOOL bNewValue = ldrpt->bEnableProtect;BOOL bOldProtect = 0;DWORD index = 0;DWORD bResponse = 0;const WCHAR lpFileName[] = HOOK_MODULE_NAME;PPEB_LDR_DATA pPebLdrData = nullptr;PLDR_DATA_TABLE_ENTRY pLdrDataEntry = nullptr;PLIST_ENTRY pListEntryStart = nullptr;PLIST_ENTRY pListEntryEnd = nullptr;SIZE_T ulTestSize = 0;SIZE_T ulRealSize = wcsnlen_s(lpFileName, MAX_PATH);
#ifdef _WIN64ULONGLONG ModuleSum = NULL;PPEB peb = (PPEB)__readgsqword(0x60);
#elseULONG ModuleSum = NULL;PPEB32 peb = (PPEB32)__readfsdword(0x30);
#endif__try {pPebLdrData = peb->Ldr;// 以模块加载顺序排列的链表pListEntryStart = pPebLdrData->InLoadOrderModuleList.Flink;pListEntryEnd = pPebLdrData->InLoadOrderModuleList.Blink;for (index = 0; pListEntryStart != pListEntryEnd; index++){pLdrDataEntry = CONTAINING_RECORD(pListEntryStart,LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);ulTestSize = wcsnlen_s(pLdrDataEntry->BaseDllName.Buffer, MAX_PATH);if (ulTestSize != ulRealSize || ulTestSize == MAX_PATH){pListEntryStart = pListEntryStart->Flink;continue;}if (!_wcsicmp(pLdrDataEntry->BaseDllName.Buffer, lpFileName)){if (bNewValue == TRUE){// 引用计数主要有两个成员,引用计数为 -1 表示静态加载的模块,// 并且不允许卸载pLdrDataEntry->DdagNode->LoadCount = 0xffffffff;pLdrDataEntry->ObsoleteLoadCount = 0xffff;}else {// 引用计数主要有两个成员,引用计数为 -1 表示静态加载的模块,// 并且不允许卸载pLdrDataEntry->DdagNode->LoadCount = 1;pLdrDataEntry->ObsoleteLoadCount = 1;}// ProcessStaticImport 位域如果为 1, 则任何卸载调用都将直接返回 TRUE// 而不做任何资源释放操作pLdrDataEntry->uFlags.ProcessStaticImport = bNewValue;bResponse |= 0x1;break;}pListEntryStart = pListEntryStart->Flink;}// 以内存位置排列的模块链表pListEntryStart = pPebLdrData->InMemoryOrderModuleList.Flink;pListEntryEnd = pPebLdrData->InMemoryOrderModuleList.Blink;for (index = 0; pListEntryStart != pListEntryEnd; index++){pLdrDataEntry = CONTAINING_RECORD(pListEntryStart,LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);ulTestSize = wcsnlen_s(pLdrDataEntry->BaseDllName.Buffer, MAX_PATH);if (ulTestSize != ulRealSize || ulTestSize == MAX_PATH){pListEntryStart = pListEntryStart->Flink;continue;}if (!_wcsicmp(pLdrDataEntry->BaseDllName.Buffer, lpFileName)){if (bNewValue == TRUE){pLdrDataEntry->DdagNode->LoadCount = 0xffffffff;pLdrDataEntry->ObsoleteLoadCount = 0xffff;}else {pLdrDataEntry->DdagNode->LoadCount = 1;pLdrDataEntry->ObsoleteLoadCount = 1;}pLdrDataEntry->uFlags.ProcessStaticImport = bNewValue;bResponse |= 0x2;break;}pListEntryStart = pListEntryStart->Flink;}// 以初始化顺序加载的模块列表pListEntryStart = pPebLdrData->InInitializationOrderModuleList.Flink;pListEntryEnd = pPebLdrData->InInitializationOrderModuleList.Blink;for (index = 0; pListEntryStart != pListEntryEnd; index++){pLdrDataEntry = CONTAINING_RECORD(pListEntryStart,LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);ulTestSize = wcsnlen_s(pLdrDataEntry->BaseDllName.Buffer, MAX_PATH);if (ulTestSize != ulRealSize || ulTestSize == MAX_PATH){pListEntryStart = pListEntryStart->Flink;continue;}if (!_wcsicmp(pLdrDataEntry->BaseDllName.Buffer, lpFileName)){if (bNewValue == TRUE){pLdrDataEntry->DdagNode->LoadCount = 0xffffffff;pLdrDataEntry->ObsoleteLoadCount = 0xffff;}else {pLdrDataEntry->DdagNode->LoadCount = 1;pLdrDataEntry->ObsoleteLoadCount = 1;}pLdrDataEntry->uFlags.ProcessStaticImport = bNewValue;bResponse |= 0x4;break;}pListEntryStart = pListEntryStart->Flink;}}__except (EXCEPTION_EXECUTE_HANDLER) {OutputDebugStringW(L"Er:Exception occurred while accessing memory.\n");return FALSE;}return bResponse;
}DWORD WINAPI SafeFreeLibrary(LPVOID lpThreadParameter)
{DWORD dwMainThread = lpgHandlerInfo.dwMainThread;HMODULE hThisModule = lpgHandlerInfo.hHookModule;// Safely free moduleif (dwMainThread != GetCurrentThreadId()){DWORD dwExitCode = TRUE;LDR_PROTECT_STRUCT ldrpt = { FALSE };EasyProtectLibrary(&ldrpt); // 解除保护// 卸载模块FreeLibraryAndExitThread(hThisModule, dwExitCode);return TRUE;}return FALSE;
}

5.2 注入工具代码

测试注入代码如下,由于时间仓促,定位代码直接写在注入过程中的,通过在 BFTracePatternInModule 函数中调整 BYTE pattern 和对应的特征码长度字段来完成不同的挂钩,这需要进一步完善代码。

// WinlogonInject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <stdio.h>
#include <Windows.h>
#include <Tlhelp32.h>
#include <Shlwapi.h>
#include <new>
#include <locale.h>#pragma comment(lib, "Shlwapi.lib")
#define HOOK_MODULE_NAME L"WMsgKMsgHookCore.dll"
#define WINLOGON_NAME    L"winlogon.exe"typedef struct HANDLER_INFO_STRUCT
{DWORD  cbSize = 0;DWORD  dwMainThread = 0;HMODULE hHookModule = nullptr;LPVOID lpHookHandler = nullptr;LPVOID lpUnhookHandler = nullptr;LPVOID lpSafeFreeLib = nullptr;LPVOID lpWinlogonBase = nullptr;LPVOID lpHookAddress = nullptr;//LPVOID lpEPLFuncAddress = nullptr;
}HANDLER_INFO_STRUCT, * LPHANDLER_INFO_STRUCT;typedef struct MAPVIEW_INFO_STRUCT
{HANDLE hFile = NULL;HANDLE hMapping = NULL;LPVOID lpBaseMapView = nullptr;
}MAPVIEW_INFO_STRUCT, * LPMAPVIEW_INFO_STRUCT;typedef BOOL (WINAPI* __GetHandlerAddress)(LPHANDLER_INFO_STRUCT lpHandlerInfo);
typedef BOOL (WINAPI* __SetBaseAddress)(DWORD lpBaseOffest);FARPROC WINAPI MyGetProcAddress64(PVOID lpBaseAddress, LPCSTR FunName);
BOOL    WINAPI InjectMouldeHandler(HANDLE hProcess, LPCWSTR pszDllFileName);
BOOL    WINAPI SafeCloseHandle(HANDLE handle);
DWORD   WINAPI GetActiveConsoleSessionId();
BOOL    WINAPI IsProcessInSession(DWORD processId, DWORD sessionId);
DWORD   WINAPI FindWinlogonProcessId();
BOOL    WINAPI CheckProcessHasLoadModule(DWORD dwProcessId, LPCWSTR wsFileName);
BOOL    WINAPI IsProcessStrictMitigativeProtectionOff(HANDLE hProcess);
BOOL    WINAPI IsRunAsAdministrator();
BOOL    WINAPI ElevateCurrentProcess(LPCWSTR wsFilePath);
BOOL    WINAPI EnableDebugPrivilege();
BOOL    WINAPI RemoteHookingHandler(HANDLE hProcess, PVOID  lpProcAddress, LPVOID lpParameter);
DWORD   WINAPI BFTracePatternInModule(LPCWSTR moduleName, PBYTE pattern, SIZE_T patternSize);BYTE   pattern1[] =
{ 0x48u, 0x8Bu, 0x0Du, 0, 0, 0, 0, 0x49u,0x3Bu, 0xCCu, 0x74u , 0, 0x44, 0x84,0x79, 0x1C , 0x74
};// ETW Trace 特征码 1// ETW Trace 特征码 2
BYTE   pattern2[] =
{0x8Bu, 0x0,   0x8Bu, 0x0,0x48,  0x8Bu, 0x0Du, 0x0,0x0,   0x0,   0x0,   0x0,0x8D,  0x0,   0x0,   0x0,0x0,   0x0,   0x41u, 0x0, 0x05u
};void SetConsoleCodePageToUTF8() {_wsetlocale(LC_ALL, L".UTF8");
}BOOL GetExecutablePath(PWCHAR* binPathBuffer, DWORD& bufferLen) {while (true) {SetLastError(0);DWORD dwwcharLen = bufferLen * sizeof(WCHAR) + sizeof(WCHAR);*binPathBuffer = new WCHAR[dwwcharLen];if (*binPathBuffer == nullptr) {printf("Error Alloc Memory for search.\n");SetLastError(ERROR_OUTOFMEMORY);return FALSE;}DWORD exePathLen = GetModuleFileNameW(nullptr, *binPathBuffer, bufferLen);if (exePathLen == 0) {printf("Error Get Module File Name.\n");return FALSE;}if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)break;delete[] *binPathBuffer;bufferLen += 10;}return TRUE;
}BOOL InjectModuleToWinlogon(const WCHAR* fullModulePath, HANDLE hWinlogonProc) {if (!IsProcessStrictMitigativeProtectionOff(hWinlogonProc)) {printf("Incompatible injection while enabling mitigation policies.\n");return FALSE;}if (TRUE == InjectMouldeHandler(hWinlogonProc, fullModulePath)) {printf("Inject Module to winlogon SUCCESSFULLY.\n");return TRUE;}else {printf("Inject Module to winlogon Failed.\n");return FALSE;}
}BOOL LoadHookCoreAndGetSymbols(const WCHAR* fullDllPath, HANDLER_INFO_STRUCT& lpHandleInfo, DWORD& dwHookBaseOffest
) {HMODULE hHookCore = LoadLibraryW(fullDllPath);if (hHookCore == NULL) {printf("Error[%d]: Failed in Loading Library.\n", GetLastError());return FALSE;}FARPROC pFunGetHandler = MyGetProcAddress64(hHookCore, "GetHandlerAddress");FARPROC pFunSetBaseAddr = MyGetProcAddress64(hHookCore, "RemoteSetHookBaseAddress");if (pFunGetHandler == NULL || pFunSetBaseAddr == NULL) {printf("Error[%d]: Failed in Loading Library.\n", GetLastError());FreeLibrary(hHookCore);return FALSE;}__GetHandlerAddress GetHandlerAddress = (__GetHandlerAddress)pFunGetHandler;__SetBaseAddress RemoteSetHookBaseAddress = (__SetBaseAddress)pFunSetBaseAddr;WCHAR  SystemDirtory[MAX_PATH] = { 0 };WCHAR  winlogonPath[500] = { 0 };GetSystemDirectoryW(SystemDirtory, sizeof(SystemDirtory) / sizeof(TCHAR));swprintf_s(winlogonPath, sizeof(winlogonPath) / sizeof(TCHAR),L"%s\\%s", SystemDirtory, WINLOGON_NAME);dwHookBaseOffest = BFTracePatternInModule(winlogonPath, pattern1, 17);if (dwHookBaseOffest == 0) {printf("Error: Failed to Search Special FunctionAddress.\n");FreeLibrary(hHookCore);return FALSE;}if (!RemoteSetHookBaseAddress(dwHookBaseOffest)) {printf("Error[%d]: Failed to SetHookBaseAddress.\n", GetLastError());FreeLibrary(hHookCore);return FALSE;}lpHandleInfo.cbSize = sizeof(HANDLER_INFO_STRUCT);if (!GetHandlerAddress(&lpHandleInfo)) {printf("Error: Failed GetHandlerAddressInfo.\n");FreeLibrary(hHookCore);return FALSE;}wprintf(L"lpHookHandler: [0x%I64X], lpUnhookHandler: [0x%I64X], lpHook: [0x%I64X].\n",reinterpret_cast<uint64_t>(lpHandleInfo.lpHookHandler),reinterpret_cast<uint64_t>(lpHandleInfo.lpUnhookHandler),reinterpret_cast<uint64_t>(lpHandleInfo.lpHookAddress));return TRUE;
}void RunHookingAndCleanup(HANDLE hWinlogonProc, const HANDLER_INFO_STRUCT& lpHandleInfo) {getchar();if (TRUE == RemoteHookingHandler(hWinlogonProc, lpHandleInfo.lpHookHandler, NULL)) {printf("Enable Hooks in winlogon SUCCESSFULLY.\n");}getchar();if (TRUE == RemoteHookingHandler(hWinlogonProc, lpHandleInfo.lpUnhookHandler, NULL)) {printf("Disable Hooks in winlogon SUCCESSFULLY.\n");}getchar();if (TRUE == RemoteHookingHandler(hWinlogonProc, lpHandleInfo.lpSafeFreeLib, NULL)) {printf("UnLoadLibrary in winlogon SUCCESSFULLY.\n");}SafeCloseHandle(hWinlogonProc);
}BOOL RunAsAdministratorHandler(LPCWSTR lpBinPathName) {// 检查是否以管理员权限运行if (IsRunAsAdministrator() == FALSE){printf("Error require Administrator Token.\n");// 尝试动态提权if (!ElevateCurrentProcess(lpBinPathName)){printf("Failed Auto-Elevate.\n");return FALSE;}else {  // 退出进程exit(0);}}// 启用管理员令牌if (!EnableDebugPrivilege()){printf("Failed to Enable Debug Priviledge.\n");return FALSE;}return TRUE;
}// 主函数
int main() {SetConsoleCodePageToUTF8();//WCHAR binPathBuffer = L'\0';PWCHAR lpbinPathBuffer = 0;DWORD bufferLen = MAX_PATH;if (!GetExecutablePath(&lpbinPathBuffer, bufferLen))return -1;if(!RunAsAdministratorHandler(lpbinPathBuffer)){delete[] lpbinPathBuffer;return -1;}// 删除程序文件名部分if (PathRemoveFileSpecW(lpbinPathBuffer) == 0){delete[] lpbinPathBuffer;printf("Error Remove File Spec.\n");return -1;}wprintf(L"Current WorkingDirectury: %s.\n", lpbinPathBuffer);WCHAR fullDllPath[MAX_PATH] = { 0 };swprintf_s(fullDllPath, MAX_PATH, L"%s\\%s", lpbinPathBuffer, HOOK_MODULE_NAME);DWORD dwWinlogonId = FindWinlogonProcessId();printf("CurrentSession winlogon PID: %d.\n", dwWinlogonId);if (CheckProcessHasLoadModule(dwWinlogonId, HOOK_MODULE_NAME)){printf("Error: Process already Loaded module.\n");return -1;}HANDLE hWinlogonProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwWinlogonId);if (hWinlogonProc == NULL) {delete[] lpbinPathBuffer;printf("Error OpenProcess(PID: %d, error: %d)\n", dwWinlogonId, GetLastError());return -1;}if (!InjectModuleToWinlogon(fullDllPath, hWinlogonProc)){delete[] lpbinPathBuffer;SafeCloseHandle(hWinlogonProc);return -1;}HANDLER_INFO_STRUCT lpHandleInfo = { 0 };DWORD dwHookBaseOffest = 0;if (!LoadHookCoreAndGetSymbols(fullDllPath, lpHandleInfo, dwHookBaseOffest)){delete[] lpbinPathBuffer;SafeCloseHandle(hWinlogonProc);return -1;}// 测试样例RunHookingAndCleanup(hWinlogonProc, lpHandleInfo);system("pause");return 0;
}// 映射模块到内存中
BYTE* NtMapViewofModule(LPCWSTR lpFileName, LPMAPVIEW_INFO_STRUCT lpMapViewInfo)
{HANDLE hFile = NULL;HANDLE hMapping = NULL;LPVOID lpBaseMapView = nullptr;PIMAGE_DOS_HEADER dosHeader = nullptr;PIMAGE_NT_HEADERS ntHeaders = nullptr;PBYTE BaseAddress = nullptr;hFile = CreateFileW(lpFileName, GENERIC_READ,FILE_SHARE_READ, NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, NULL);if (hFile == INVALID_HANDLE_VALUE) {printf("Failed to open file.\n");return FALSE;}hMapping = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);if (hMapping == NULL) {printf("Failed to create file mapping.\n");CloseHandle(hFile);return FALSE;}lpBaseMapView = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);if (lpBaseMapView == NULL) {printf("Failed to map view of file.\n");goto ErrorEndFunction;}dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(lpBaseMapView);if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {UnmapViewOfFile(lpBaseMapView);printf("Not a valid DOS executable.\n");goto ErrorEndFunction;}BaseAddress = reinterpret_cast<BYTE*>(lpBaseMapView);ntHeaders = reinterpret_cast<PIMAGE_NT_HEADERS>(BaseAddress + dosHeader->e_lfanew);if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) {UnmapViewOfFile(lpBaseMapView);printf("Not a valid NT executable.\n");goto ErrorEndFunction;}lpMapViewInfo->hFile = hFile;lpMapViewInfo->hMapping = hMapping;lpMapViewInfo->lpBaseMapView = lpBaseMapView;return BaseAddress;ErrorEndFunction:CloseHandle(hMapping);CloseHandle(hFile);return FALSE;
}// 释放映射到内存中的模块
BOOL NtUnMapViewModule(LPMAPVIEW_INFO_STRUCT lpMapViewInfo)
{if (lpMapViewInfo == nullptr){return FALSE;}BOOL bResponse = FALSE;HANDLE hFile = lpMapViewInfo->hFile;HANDLE hMapping = lpMapViewInfo->hMapping;LPVOID lpBaseMapView = lpMapViewInfo->lpBaseMapView;if (hFile)bResponse = CloseHandle(hFile);if (hMapping)bResponse = CloseHandle(hMapping);if (lpBaseMapView)bResponse = UnmapViewOfFile(lpBaseMapView);lpMapViewInfo->hFile = NULL;lpMapViewInfo->hMapping = NULL;lpMapViewInfo->lpBaseMapView = nullptr;return bResponse;
}// 遍历 pdata 节段信息,搜索指定函数结束地址,并返回下一个函数入口地址
DWORD TraversePDATASectionInfo(PBYTE lpBaseAddress, DWORD dwLastEndAddress, PDWORD dwRealAddress)
{if (dwLastEndAddress == 0) return 0;PIMAGE_DOS_HEADER dosHeader =reinterpret_cast<PIMAGE_DOS_HEADER>(lpBaseAddress);PIMAGE_NT_HEADERS ntHeaders =reinterpret_cast<PIMAGE_NT_HEADERS>(lpBaseAddress + dosHeader->e_lfanew);PIMAGE_SECTION_HEADER sectionHeader = IMAGE_FIRST_SECTION(ntHeaders);for (int i = 0; i < ntHeaders->FileHeader.NumberOfSections; ++i) {if (strcmp(reinterpret_cast<char*>(sectionHeader[i].Name), ".pdata") == 0) {DWORD pdataVirtualAddress = sectionHeader[i].VirtualAddress;DWORD pdataSize = sectionHeader[i].SizeOfRawData;DWORD pdataOffset = pdataVirtualAddress- sectionHeader[i].VirtualAddress+ sectionHeader[i].PointerToRawData;PIMAGE_RUNTIME_FUNCTION_ENTRY pdata =reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(lpBaseAddress + pdataOffset);DWORD numEntries = pdataSize / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY);for (DWORD j = 0; j < numEntries; ++j) {long distance = static_cast<long>(dwLastEndAddress) -static_cast<long>(pdata[j].EndAddress); // 转为有符号数,以便于比较绝对值if (abs(distance) <= 15) // 容错性,编译器优化可能存在折叠函数{memcpy_s(dwRealAddress, sizeof(DWORD), &pdata[j].EndAddress, sizeof(DWORD));return pdata[j + 1].BeginAddress;}}break;}}return FALSE;
}// 暴力搜索函数
DWORD WINAPI BFTracePatternInModule(LPCWSTR moduleName,PBYTE pattern,SIZE_T patternSize
)
{if (pattern == 0 || moduleName == 0 || patternSize == 0){printf("Error Invalid parameter.\n");return 0;}int cwStartTime = 0, cwEndTime = 0; // 计时器存储时间PBYTE lpBaseAddress = 0;MAPVIEW_INFO_STRUCT MapViewInfo = { 0 };// 将模块映射到内存中lpBaseAddress = NtMapViewofModule(moduleName, &MapViewInfo);if (lpBaseAddress == nullptr) {printf("Failed to load module: %ws.\n", moduleName);return 0;}PIMAGE_DOS_HEADER dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(lpBaseAddress);PIMAGE_NT_HEADERS ntHeader = reinterpret_cast<PIMAGE_NT_HEADERS>((reinterpret_cast<BYTE*>(lpBaseAddress)) + dosHeader->e_lfanew);DWORD moduleSize = ntHeader->OptionalHeader.SizeOfImage;wprintf(L"MapView Winlogon Base:0x%I64X.\n", reinterpret_cast<uint64_t>(lpBaseAddress));wprintf(L"Winlogon ModuleSize:%ld Bytes.\n", moduleSize);// 模块大小不能为 0if (moduleSize == 0){printf("Failed to get module information.\n");NtUnMapViewModule(&MapViewInfo);return 0;}DWORD vcMachOffest = 0; // 用于记录查找的特征码偏移for (DWORD i = 0; i < moduleSize; i++){vcMachOffest = 0;DWORD j = 0;for (j; j < patternSize - 1; j++){if (lpBaseAddress[i + j] != pattern[j] && pattern[j] != 0u){break;}}if (j == patternSize - 1){if (lpBaseAddress[i + j] == pattern[j] || pattern[j] == 0u){vcMachOffest = i;break;}}}if (vcMachOffest == 0){NtUnMapViewModule(&MapViewInfo);return 0;}/** 关键算法:向上搜索跳过 HotPatch 以及 BreakSwap 代码段* Created By LianYou 516 at 2024.01.29.*/DWORD baseStart = vcMachOffest; // 存储偏移量DWORD basePatch = vcMachOffest - 1;DWORD baseCCByte = 0, baseNopByte = 0;DWORD baseEnd = 0;        // 上下文的偏移DWORD pDataFunEntry = 0;  // 正真的入口点偏移const uint8_t ccByte = 0xCC;const uint8_t nopByte = 0x90;// 第一个循环找到连续的 0xCC 或者 0x90 首次出现位置while (baseStart - basePatch <= 0x3E8u) // 搜索域限界条件{// 检测到连续的 0xCC 标记偏移量到 baseCCByte 变量if (lpBaseAddress[basePatch] == ccByte&& lpBaseAddress[basePatch - 1] == ccByte){baseCCByte = basePatch - 1;break;}// 检测到连续的 0x90 标记偏移量到 baseNopByte 变量if (lpBaseAddress[basePatch] == nopByte &&lpBaseAddress[basePatch - 1] == nopByte){baseNopByte = basePatch - 1;break;}--basePatch;// 递减循环if (basePatch == 0) break; // 防止数组越界}// 判断是否找到第一个连续 0xCC 字节的位置if (baseCCByte != 0){// 循环检索 0xCC 软件断点指令,实现越过入口点软件断点区域while (baseStart - baseCCByte <= 0x3E8u){if (lpBaseAddress[baseCCByte] != ccByte){baseEnd = baseCCByte + 1;  // 找到 0xCC 最早出现的位置(低地址)break;}if (baseCCByte == 0) break;--baseCCByte;}}// 判断是否找到第一个连续 0x90 字节的位置if (baseNopByte != 0){// 循环检索 0x90 NOP 指令,实现越过入口点热补丁区域while (baseStart - baseNopByte <= 0x3E8u){if (lpBaseAddress[baseNopByte] != nopByte){baseEnd = baseNopByte + 1;  // 找到 0x90 最早出现的位置(低地址)break;}if (baseNopByte == 0) break;--baseNopByte;}}// 依据 text 节映射信息,修正地址DWORD vcFixedOffest = 0;DWORD dwRealAddress = 0;PIMAGE_SECTION_HEADER sectionHeader = IMAGE_FIRST_SECTION(ntHeader);for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; ++i) {if (strcmp(reinterpret_cast<char*>(sectionHeader[i].Name), ".text") == 0) {vcFixedOffest = baseEnd- sectionHeader[i].PointerToRawData+ sectionHeader[i].VirtualAddress;break;}}wprintf(L"End address of the previous function in the entry (estimated): [0x%lX].\n", vcFixedOffest);// 如果正确定位,则返回地址pDataFunEntry = TraversePDATASectionInfo(lpBaseAddress, vcFixedOffest, &dwRealAddress);if (pDataFunEntry != 0){wprintf(L"End address of the previous function in the entry (actual): [0x%lX].\n", dwRealAddress);wprintf(L"The matched function entry point is located at offset: [0x%lX].\n", pDataFunEntry);return pDataFunEntry;}NtUnMapViewModule(&MapViewInfo);return 0;
}BOOL WINAPI SafeCloseHandle(HANDLE handle)
{BOOL bResponse = TRUE;if (handle != nullptr) {bResponse = CloseHandle(handle);handle = nullptr;}return bResponse;
}BOOL WINAPI IsProcessStrictMitigativeProtectionOff(HANDLE hProcess) {PROCESS_MITIGATION_DYNAMIC_CODE_POLICY dynamicCodePolicy = { 0 };PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY signaturePolicy = { 0 };PROCESS_MITIGATION_CONTROL_FLOW_GUARD_POLICY cfgPolicy = { 0 };// Actually retrieve the mitigation policy for ACGif (!GetProcessMitigationPolicy(hProcess,ProcessDynamicCodePolicy, &dynamicCodePolicy, sizeof(dynamicCodePolicy))) {printf("[!] Could not GetProcessMitigationPolicy. [%d]\n", GetLastError());return FALSE;}if (dynamicCodePolicy.ProhibitDynamicCode || dynamicCodePolicy.AllowRemoteDowngrade || dynamicCodePolicy.AllowThreadOptOut) {printf("Detect[0]: ProcessDynamicCodePolicy on.\n");return FALSE;}// Retrieve mitigation policy for loading arbitrary DLLsif (!GetProcessMitigationPolicy(hProcess,ProcessSignaturePolicy, &signaturePolicy, sizeof(signaturePolicy))) {printf("Could not GetProcessMitigationPolicy. [%d]\n", GetLastError());return FALSE;}if (signaturePolicy.AuditMicrosoftSignedOnly ||signaturePolicy.AuditStoreSignedOnly || signaturePolicy.MicrosoftSignedOnly ||signaturePolicy.MitigationOptIn ||signaturePolicy.StoreSignedOnly) {printf("Detect[1]: ProcessSignaturePolicy on.\n");return FALSE;}// Retrieve mitigation policy for strict Control Flow Guardsif (!GetProcessMitigationPolicy(hProcess,ProcessControlFlowGuardPolicy, &cfgPolicy, sizeof(cfgPolicy))) {printf("Could not GetProcessMitigationPolicy. [%d]\n", GetLastError());return FALSE;}if (cfgPolicy.EnableXfg || cfgPolicy.StrictMode){printf("Detect[2]: ProcessControlFlowGuardPolicy on.\n");return FALSE;}return TRUE;
}BOOL WINAPI IsRunAsAdministrator() // 判断是否以管理员身份运行
{BOOL bIsElevated = FALSE;HANDLE hToken = NULL;if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {TOKEN_ELEVATION te = { 0 };DWORD dwReturnLength = 0;if (GetTokenInformation(hToken, TokenElevation, &te, sizeof(te), &dwReturnLength)) {if (dwReturnLength == sizeof(te))bIsElevated = te.TokenIsElevated;}SafeCloseHandle(hToken);}return bIsElevated;
}BOOL WINAPI ElevateCurrentProcess(LPCWSTR wsFilePath)
{TCHAR szPath[MAX_PATH] = { 0 };if (GetModuleFileNameW(NULL, szPath, MAX_PATH) != 0){// Launch itself as administrator.SHELLEXECUTEINFO sei = { 0 };sei.cbSize = sizeof(SHELLEXECUTEINFO);sei.lpVerb = L"runas";sei.lpFile = szPath;sei.lpParameters = (LPCTSTR)wsFilePath;sei.nShow = SW_SHOWNORMAL;if (!ShellExecuteEx(&sei)){DWORD dwStatus = GetLastError();if (dwStatus == ERROR_CANCELLED){// The user refused to allow privileges elevation.printf("The user refused to allow privileges elevation.\n");return FALSE;}else if (dwStatus == ERROR_FILE_NOT_FOUND){// The file defined by lpFile was not found and// an error message popped up.printf("Error Cannot Access Files.\n");return FALSE;}return FALSE;}return TRUE;}return FALSE;
}BOOL WINAPI EnableDebugPrivilege()
{HANDLE handleToken = NULL;if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &handleToken)) {printf("Error OpenProcessToken.\n");return FALSE;}LUID debugNameValue = { 0 };if (!LookupPrivilegeValueW(nullptr, SE_DEBUG_NAME, &debugNameValue)) {SafeCloseHandle(handleToken);printf("Error LookupPrivilegeValue.\n");return FALSE;}TOKEN_PRIVILEGES tokenPri = { 0 };tokenPri.PrivilegeCount = 1;tokenPri.Privileges[0].Luid = debugNameValue;tokenPri.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;if (!AdjustTokenPrivileges(handleToken, FALSE, &tokenPri, sizeof(tokenPri), nullptr, nullptr)) {SafeCloseHandle(handleToken);printf("Error AdjustTokenPrivileges.\n");return FALSE;}SafeCloseHandle(handleToken);return TRUE;
}DWORD WINAPI GetActiveConsoleSessionId() {return WTSGetActiveConsoleSessionId();
}BOOL WINAPI IsProcessInSession(DWORD processId, DWORD sessionId) {DWORD session;if (!ProcessIdToSessionId(processId, &session)) {printf("Error: ProcessIdToSessionId failed.\n");return FALSE;}return session == sessionId;
}DWORD WINAPI FindWinlogonProcessId() {DWORD dwProcessId = 0;DWORD activeSessionId = GetActiveConsoleSessionId();if (activeSessionId == 0xFFFFFFFF) {printf("Error: Unable to retrieve active console session ID.\n");return 0;}HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (snapshot == INVALID_HANDLE_VALUE) {printf("Error: CreateToolhelp32Snapshot failed.\n");return 0;}PROCESSENTRY32 entry;entry.dwSize = sizeof(PROCESSENTRY32);if (!Process32First(snapshot, &entry)) {printf("Error: Process32First failed.\n");SafeCloseHandle(snapshot);return 0;}do {if (entry.cntThreads <= 1u) continue; // 跳过僵尸进程if (_wcsicmp(entry.szExeFile, L"winlogon.exe") == 0) {if (IsProcessInSession(entry.th32ProcessID, activeSessionId)) {dwProcessId = entry.th32ProcessID;break;}}} while (Process32Next(snapshot, &entry));SafeCloseHandle(snapshot);return dwProcessId;
}/// <summary>
/// MyGetProcAddress64 is a function to replace GetProcAddress for 64-bit
/// architecture, used to retrieve the entry address of a function within a module.
/// </summary>
/// <param name="lpBaseAddress">The base address of the module.</param>
/// <param name="FunName">The name of the function to retrieve.</param>
/// <returns>The entry address of the function if found, or 0 if not found.</returns>
FARPROC WINAPI MyGetProcAddress64(PVOID lpBaseAddress, LPCSTR FunName) {if (lpBaseAddress == nullptr || FunName == nullptr){SetLastError(ERROR_INVALID_PARAMETER);return 0;}__try {// Get DOS headerPIMAGE_DOS_HEADER dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(lpBaseAddress);if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {SetLastError(ERROR_IMAGE_MACHINE_TYPE_MISMATCH);return 0;}// Get NT headerPIMAGE_NT_HEADERS ntHeader = reinterpret_cast<PIMAGE_NT_HEADERS>((reinterpret_cast<BYTE*>(lpBaseAddress)) + dosHeader->e_lfanew);if (ntHeader->Signature != IMAGE_NT_SIGNATURE) {SetLastError(ERROR_IMAGE_MACHINE_TYPE_MISMATCH);return 0;}// Get export directoryPIMAGE_EXPORT_DIRECTORY exports = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>((reinterpret_cast<BYTE*>(lpBaseAddress))+ ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);// Get export tablesDWORD* nameTable = reinterpret_cast<DWORD*>((reinterpret_cast<BYTE*>(lpBaseAddress)) + exports->AddressOfNames);DWORD* addressTable = reinterpret_cast<DWORD*>((reinterpret_cast<BYTE*>(lpBaseAddress)) + exports->AddressOfFunctions);WORD* ordinalTable = reinterpret_cast<WORD*>((reinterpret_cast<BYTE*>(lpBaseAddress)) + exports->AddressOfNameOrdinals);DWORD low = 0;DWORD high = exports->NumberOfNames - 1;// Binary search in export tablewhile (low <= high) {DWORD mid = low + (high - low) / 2;PCHAR pFuncName = reinterpret_cast<PCHAR>((reinterpret_cast<BYTE*>(lpBaseAddress)) + nameTable[mid]);if (pFuncName != nullptr) {// Copy function name and ensure null-terminationsize_t strLength = strnlen_s(pFuncName, MAX_PATH);char* buffer = new (std::nothrow) char[strLength + 1];if (buffer != nullptr) {memcpy_s(buffer, strLength + 1, pFuncName, strLength);buffer[strLength] = '\0';// Compare function namesint compareResult = _stricmp(buffer, FunName);delete[] buffer; // Free allocated memoryif (compareResult == 0) {// Function name found, return function addressDWORD functionRVA = addressTable[ordinalTable[mid]];FARPROC pfunAddress = reinterpret_cast<FARPROC>(reinterpret_cast<UINT64>(lpBaseAddress) + functionRVA );SetLastError(0);return pfunAddress;}// Update search range based on comparison resultif (compareResult < 0) {low = mid + 1;}else {high = mid - 1;}}else {printf("Error out of memory.\n");SetLastError(ERROR_OUTOFMEMORY);}}else {printf("Error GetFunctionNameTable.\n");SetLastError(ERROR_ACCESS_DENIED);}}}__except (EXCEPTION_EXECUTE_HANDLER) {printf("Exception occurred while accessing memory.\n");return 0;}return 0; // Function not found
}BOOL WINAPI CheckProcessHasLoadModule(DWORD dwProcessId, LPCWSTR wsFileName) {/** 参数为TH32CS_SNAPMODULE 或 TH32CS_SNAPMODULE32时,* 如果函数失败并返回ERROR_BAD_LENGTH,则重试该函数直至成功* 进程创建未初始化完成时,CreateToolhelp32Snapshot会返回error 299,但其它情况下不会。*/HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, dwProcessId);while (INVALID_HANDLE_VALUE == hSnapshot) {DWORD dwError = GetLastError();if (dwError == ERROR_BAD_LENGTH) {hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, dwProcessId);continue;}else {printf("CreateToolhelp32Snapshot failed: %d, targetProcessId:%d.\n",dwError, dwProcessId);return FALSE;}}MODULEENTRY32W mi = { 0 };mi.dwSize = sizeof(MODULEENTRY32W); // 第一次使用必须初始化成员BOOL bRet = Module32FirstW(hSnapshot, &mi);while (bRet) {// mi.szModule 是短路径if (wcsstr(wsFileName, mi.szModule) || wcsstr(mi.szModule, wsFileName) ) {if (hSnapshot != NULL) CloseHandle(hSnapshot);return TRUE;}mi.dwSize = sizeof(MODULEENTRY32W);bRet = Module32NextW(hSnapshot, &mi);}if (hSnapshot != NULL) SafeCloseHandle(hSnapshot);return FALSE;
}BOOL WINAPI InjectMouldeHandler(HANDLE hProcess,LPCWSTR pszDllFileName
)
{// 1.目标进程句柄if (hProcess == NULL || pszDllFileName == nullptr){wprintf(L"Error: InvalidSyntax error from InjectMouldeHandler.\n");SetLastError(ERROR_INVALID_PARAMETER);return FALSE;}size_t pathSize = (wcslen(pszDllFileName) + 1) * sizeof(wchar_t);SetLastError(0);// 2.在目标进程中申请空间LPVOID lpPathAddr = VirtualAllocEx( hProcess, 0, pathSize,MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);if (NULL == lpPathAddr){wprintf(L"Error[%d]: Failed to apply memory in the target process!\n", GetLastError());return FALSE;}SetLastError(0);// 3.在目标进程中写入 Dll 路径if (FALSE == WriteProcessMemory( hProcess, lpPathAddr, pszDllFileName, pathSize, NULL) ){wprintf(L"Error[%d]: Failed to write module path in target process!\n", GetLastError());return FALSE;}SetLastError(0);// 4.加载 ntdll.dllHMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");if (NULL == hNtdll){wprintf(L"Error[%d]: Failed to load NTDLL.DLL!\n", GetLastError());VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);return FALSE;}SetLastError(0);// 5.获取 LoadLibraryW 的函数地址, FARPROC 可以自适应 32 位与 64 位FARPROC pFuncProcAddr = MyGetProcAddress64(GetModuleHandleW(L"KernelBase.dll"),"LoadLibraryW");if (NULL == pFuncProcAddr){wprintf(L"Error[%d]: Failed to obtain the address of the LoadLibrary function!\n", GetLastError());VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);return FALSE;}// 6.获取 NtCreateThreadEx 函数地址,该函数在32位与64位下原型不同// _WIN64 用来判断编译环境 ,_WIN32用来判断是否是 Windows 系统
#ifdef _WIN64typedef DWORD(WINAPI* __NtCreateThreadEx)(PHANDLE ThreadHandle,ACCESS_MASK DesiredAccess,LPVOID ObjectAttributes,HANDLE ProcessHandle,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,ULONG CreateThreadFlags,SIZE_T ZeroBits,SIZE_T StackSize,SIZE_T MaximumStackSize,LPVOID pUnkown);
#elsetypedef DWORD(WINAPI* __NtCreateThreadEx)(PHANDLE ThreadHandle,ACCESS_MASK DesiredAccess,LPVOID ObjectAttributes,HANDLE ProcessHandle,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,BOOL CreateSuspended,DWORD dwStackSize,DWORD dw1,DWORD dw2,LPVOID pUnkown);
#endifSetLastError(0);__NtCreateThreadEx NtCreateThreadEx =(__NtCreateThreadEx)MyGetProcAddress64(hNtdll, "NtCreateThreadEx");if (NULL == NtCreateThreadEx){wprintf(L"Error[%d]: Failed to obtain NtCreateThreadEx function address!\n", GetLastError());VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);return FALSE;}SetLastError(0);// 7.在目标进程中创建远线程HANDLE hRemoteThread = NULL;DWORD lpExitCode = 0;DWORD dwStatus = NtCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL,hProcess,(LPTHREAD_START_ROUTINE)pFuncProcAddr, lpPathAddr, 0, 0, 0, 0, NULL);if (NULL == hRemoteThread){wprintf(L"Error[%d]: Failed to create thread in target process!\n", GetLastError());VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);return FALSE;}SetLastError(0);// 8.等待线程结束if (WAIT_TIMEOUT == WaitForSingleObject(hRemoteThread, 2000)){wprintf(L"Error[%d]: Remote thread not responding.\n", GetLastError());VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);return FALSE;}GetExitCodeThread(hRemoteThread, &lpExitCode);if (lpExitCode == 0){wprintf(L"Error: Injection module failed in the target process.\n");VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);return FALSE;}// 9.清理环境VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);SafeCloseHandle(hRemoteThread);return TRUE;
}BOOL WINAPI RemoteHookingHandler(HANDLE hProcess,PVOID  lpProcAddress,LPVOID lpParameter
)
{// 1.目标进程句柄if (hProcess == NULL || lpProcAddress == nullptr){wprintf(L"Error: InvalidSyntax error from RemoteHookingHandler.\n");SetLastError(ERROR_INVALID_PARAMETER);return FALSE;}SetLastError(0);// 4.加载 ntdll.dllHMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");if (NULL == hNtdll){wprintf(L"Error[%d]: Failed to load NTDLL.DLL!\n", GetLastError());return FALSE;}// 6.获取 NtCreateThreadEx 函数地址,该函数在 32 位与 64 位下原型不同// _WIN64 用来判断编译环境 ,_WIN32用来判断是否是 Windows 系统
#ifdef _WIN64typedef DWORD(WINAPI* __NtCreateThreadEx)(PHANDLE ThreadHandle,ACCESS_MASK DesiredAccess,LPVOID ObjectAttributes,HANDLE ProcessHandle,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,ULONG CreateThreadFlags,SIZE_T ZeroBits,SIZE_T StackSize,SIZE_T MaximumStackSize,LPVOID pUnkown);
#elsetypedef DWORD(WINAPI* __NtCreateThreadEx)(PHANDLE ThreadHandle,ACCESS_MASK DesiredAccess,LPVOID ObjectAttributes,HANDLE ProcessHandle,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,BOOL CreateSuspended,DWORD dwStackSize,DWORD dw1,DWORD dw2,LPVOID pUnkown);
#endifSetLastError(0);__NtCreateThreadEx NtCreateThreadEx =(__NtCreateThreadEx)MyGetProcAddress64(hNtdll, "NtCreateThreadEx");if (NULL == NtCreateThreadEx){wprintf(L"Error[%d]: Failed to obtain NtCreateThreadEx function address!\n",GetLastError());return FALSE;}SetLastError(0);// 7.在目标进程中创建远线程HANDLE hRemoteThread = NULL;DWORD lpExitCode = 0;DWORD dwStatus = NtCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL,hProcess, (LPTHREAD_START_ROUTINE)lpProcAddress, lpParameter, 0, 0, 0, 0, NULL);if (NULL == hRemoteThread){wprintf(L"Error[%d]: Failed to create thread in target process!\n", GetLastError());return FALSE;}SetLastError(0);// 8.等待线程结束if (WAIT_TIMEOUT == WaitForSingleObject(hRemoteThread, 15000)){wprintf(L"Error[%d]: Remote thread not responding.\n", GetLastError());return FALSE;}GetExitCodeThread(hRemoteThread, &lpExitCode);if (lpExitCode == 0){wprintf(L"Error: Control HOOK routine failed in the target process.\n");return FALSE;}// 9.清理环境SafeCloseHandle(hRemoteThread);return TRUE;
}

5.3 测试结果展示

(1)成功注入钩子模块并启用挂钩:

测试截图 1

(2)按下 Ctrl + Shift + Esc 弹出对话框:

测试截图 2

(3)安全地远程卸载模块:

测试截图 3

 至此,测试完成。


总结&后记

在第一篇文章中,我们主要分析了系统热键处理的关键接口以及如何通过选取特征码精准定位接口函数的入口点。在这一篇中,我们进一步分析了 Winlogon 相关例程的机制,并给出挂钩处理的解决方案。

更新内容:暂无

已知兼容性问题:暂无


发布于:2024.02.03,更新于:2024.02.03

这篇关于实现屏蔽 Ctrl + Alt + Del 、Ctrl + Shift + Esc 等热键(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/676807

相关文章

windos server2022里的DFS配置的实现

《windosserver2022里的DFS配置的实现》DFS是WindowsServer操作系统提供的一种功能,用于在多台服务器上集中管理共享文件夹和文件的分布式存储解决方案,本文就来介绍一下wi... 目录什么是DFS?优势:应用场景:DFS配置步骤什么是DFS?DFS指的是分布式文件系统(Distr

NFS实现多服务器文件的共享的方法步骤

《NFS实现多服务器文件的共享的方法步骤》NFS允许网络中的计算机之间共享资源,客户端可以透明地读写远端NFS服务器上的文件,本文就来介绍一下NFS实现多服务器文件的共享的方法步骤,感兴趣的可以了解一... 目录一、简介二、部署1、准备1、服务端和客户端:安装nfs-utils2、服务端:创建共享目录3、服

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

Python实现高效地读写大型文件

《Python实现高效地读写大型文件》Python如何读写的是大型文件,有没有什么方法来提高效率呢,这篇文章就来和大家聊聊如何在Python中高效地读写大型文件,需要的可以了解下... 目录一、逐行读取大型文件二、分块读取大型文件三、使用 mmap 模块进行内存映射文件操作(适用于大文件)四、使用 pand

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一

Python xmltodict实现简化XML数据处理

《Pythonxmltodict实现简化XML数据处理》Python社区为提供了xmltodict库,它专为简化XML与Python数据结构的转换而设计,本文主要来为大家介绍一下如何使用xmltod... 目录一、引言二、XMLtodict介绍设计理念适用场景三、功能参数与属性1、parse函数2、unpa

C#实现获得某个枚举的所有名称

《C#实现获得某个枚举的所有名称》这篇文章主要为大家详细介绍了C#如何实现获得某个枚举的所有名称,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考一下... C#中获得某个枚举的所有名称using System;using System.Collections.Generic;usi

Go语言实现将中文转化为拼音功能

《Go语言实现将中文转化为拼音功能》这篇文章主要为大家详细介绍了Go语言中如何实现将中文转化为拼音功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 有这么一个需求:新用户入职 创建一系列账号比较麻烦,打算通过接口传入姓名进行初始化。想把姓名转化成拼音。因为有些账号即需要中文也需要英

C# 读写ini文件操作实现

《C#读写ini文件操作实现》本文主要介绍了C#读写ini文件操作实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录一、INI文件结构二、读取INI文件中的数据在C#应用程序中,常将INI文件作为配置文件,用于存储应用程序的

C#实现获取电脑中的端口号和硬件信息

《C#实现获取电脑中的端口号和硬件信息》这篇文章主要为大家详细介绍了C#实现获取电脑中的端口号和硬件信息的相关方法,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 我们经常在使用一个串口软件的时候,发现软件中的端口号并不是普通的COM1,而是带有硬件信息的。那么如果我们使用C#编写软件时候,如