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

news2024/11/28 4:44:52

目录

前言

一、概述 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 WMsgMessageHandler
    unsigned 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 trampoline
    unsigned 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 protect
    VirtualProtect(hHookAddress, 13, PAGE_EXECUTE_READWRITE, &oldProtect);

    // save the original bytes
    memcpy_s(pOriginalBytes, 13, hHookAddress, 13);

    // write the hook
    memcpy_s(hHookAddress, bufferSize, boing, bufferSize);

    // Flush Cache to make code work
    FlushInstructionCache(GetCurrentProcess(), hHookAddress, bufferSize);

    // enable write protect
    VirtualProtect(hHookAddress, 13, oldProtect, &oldProtect);
    
}

3.2 脱钩例程解决方案

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

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

参考代码如下:

void UnhookHandler()
{
    DWORD bResponse = FALSE;
    DWORD oldProtect = 0;
    PVOID hookAddress = lpgHandlerInfo.lpHookAddress;
    
    // disable write protect
    VirtualProtect(hookAddress, 13, PAGE_EXECUTE_READWRITE, &oldProtect);

    // recover original bytes
    memcpy_s(hookAddress, 13, pOriginalBytes, 13);

    // Flush Cache to make code work
    FlushInstructionCache(GetCurrentProcess(), hookAddress, 13);

    // enable write protect
    VirtualProtect(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;
    }

    // UnHook
    UnhookHandler(NULL);
    auto WMsgKMessageHandler = (__WMsgKMessageHandler)lpgHandlerInfo.lpHookAddress;
    // call original func
    dwFunResponse = WMsgKMessageHandler(uMachineState, uMsgWLGenericKey, pAsync, lpStatus);
    // Re-hook
    HookHandler(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结构体中对应的字段,并返回TRUE
    lpHandlerInfo->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];                             //0x0
        struct
        {
            _RTL_BALANCED_NODE* Left;                                //0x0
            _RTL_BALANCED_NODE* Right;                               //0x8
        };
    };
    union
    {
        struct
        {
            UCHAR Red : 1;                                                    //0x10
            UCHAR 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;                                           //0x0
    ULONG 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;                                                     //0x0
    PLDR_SERVICE_TAG_RECORD ServiceTagList;                                 //0x10
    ULONG LoadCount;                                                        //0x18
    ULONG LoadWhileUnloadingCount;                                          //0x1c
    ULONG LowestLink;                                                       //0x20
    LDRP_CSLIST Dependencies;                                               //0x28
    LDRP_CSLIST IncomingDependencies;                                       //0x30
    LDR_DDAG_STATE State;                                                   //0x38
    SINGLE_LIST_ENTRY CondenseLink;                                         //0x40
    ULONG 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;                                            //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;


//0x58 bytes (sizeof)
typedef struct _PEB_LDR_DATA32
{
    ULONG Length; // +0x00
    BOOLEAN Initialized; // +0x04
    PVOID SsHandle; // +0x08
    LIST_ENTRY InLoadOrderModuleList; // +0x0c
    LIST_ENTRY InMemoryOrderModuleList; // +0x14
    LIST_ENTRY InInitializationOrderModuleList;// +0x1c
} PEB_LDR_DATA32, * PPEB_LDR_DATA32; // +0x24


typedef struct _PEB32
{
    UCHAR InheritedAddressSpace;                                            //0x0
    UCHAR ReadImageFileExecOptions;                                         //0x1
    UCHAR BeingDebugged;                                                    //0x2
    union
    {
        UCHAR BitField;                                                     //0x3
        struct
        {
            UCHAR ImageUsesLargePages : 1;                                    //0x3
            UCHAR IsProtectedProcess : 1;                                     //0x3
            UCHAR IsImageDynamicallyRelocated : 1;                            //0x3
            UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3
            UCHAR IsPackagedProcess : 1;                                      //0x3
            UCHAR IsAppContainer : 1;                                         //0x3
            UCHAR IsProtectedProcessLight : 1;                                //0x3
            UCHAR IsLongPathAwareProcess : 1;                                 //0x3
        };
    };
    PVOID Mutant;                                                           //0x4
    PVOID ImageBaseAddress;                                                 //0x8
    PEB_LDR_DATA32* Ldr;                                                    //0xc
    struct RTL_USER_PROCESS_PARAMETERS* ProcessParameters;                  //0x10
    PVOID SubSystemData;                                                    //0x14
    PVOID ProcessHeap;                                                      //0x18
    RTL_CRITICAL_SECTION* FastPebLock;                                      //0x1c
    SLIST_HEADER* volatile AtlThunkSListPtr;                                //0x20
    PVOID IFEOKey;                                                          //0x24
} PEB32, * PPEB32;

typedef struct _STRING64
{
    USHORT Length;                                                          //0x0
    USHORT MaximumLength;                                                   //0x2
    ULONGLONG Buffer;                                                       //0x8
}STRING64, * LPSTRING64;

typedef struct _PEB_LDR_DATA64
{
    ULONG Length;                                                           //0x0
    UCHAR Initialized;                                                      //0x4
    PVOID SsHandle;                                                         //0x8
    LIST_ENTRY InLoadOrderModuleList;                                       //0x10
    LIST_ENTRY InMemoryOrderModuleList;                                     //0x20
    LIST_ENTRY InInitializationOrderModuleList;                             //0x30
    PVOID EntryInProgress;                                                  //0x40
    UCHAR ShutdownInProgress;                                               //0x48
    PVOID ShutdownThreadId;                                                 //0x50
}PEB_LDR_DATA64, * PPEB_LDR_DATA64;

typedef struct _PEB64
{
    UCHAR InheritedAddressSpace;                                            //0x0
    UCHAR ReadImageFileExecOptions;                                         //0x1
    UCHAR BeingDebugged;                                                    //0x2
    union
    {
        UCHAR BitField;                                                     //0x3
        struct
        {
            UCHAR ImageUsesLargePages : 1;                                    //0x3
            UCHAR IsProtectedProcess : 1;                                     //0x3
            UCHAR IsImageDynamicallyRelocated : 1;                            //0x3
            UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3
            UCHAR IsPackagedProcess : 1;                                      //0x3
            UCHAR IsAppContainer : 1;                                         //0x3
            UCHAR IsProtectedProcessLight : 1;                                //0x3
            UCHAR IsLongPathAwareProcess : 1;                                 //0x3
        };
    };
    UCHAR Padding0[4];                                                      //0x4
    ULONGLONG Mutant;                                                       //0x8
    ULONGLONG ImageBaseAddress;                                             //0x10
    PEB_LDR_DATA64* Ldr;                                                    //0x18
    ULONGLONG ProcessParameters;                                            //0x20
    ULONGLONG SubSystemData;                                                //0x28
    ULONGLONG ProcessHeap;                                                  //0x30
    ULONGLONG FastPebLock;                                                  //0x38
    ULONGLONG AtlThunkSListPtr;                                             //0x40
    ULONGLONG 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 _WIN64
    ULONGLONG ModuleSum = NULL;
    PPEB peb = (PPEB)__readgsqword(0x60);
#else
    ULONG 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;
                    pLdrDataEntry->Flags |= (1 << 5); // 将第六位置为 1
                }
                else {
                    // 引用计数主要有两个成员,引用计数为 -1 表示静态加载的模块,
                    // 并且不允许卸载
                    pLdrDataEntry->DdagNode->LoadCount = 1;
                    pLdrDataEntry->ObsoleteLoadCount = 1;
                    // ProcessStaticImport 位域如果为 1, 则任何卸载调用都将直接返回 TRUE
                    // 而不做任何资源释放操作
                    pLdrDataEntry->Flags &= ~(1 << 5); // 将第六位清零
                }
                
                //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;
                    pLdrDataEntry->Flags |= (1 << 5);
                }
                else {
                    pLdrDataEntry->DdagNode->LoadCount = 1;
                    pLdrDataEntry->ObsoleteLoadCount = 1;
                    pLdrDataEntry->Flags &= ~(1 << 5);
                }
                //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;
                    pLdrDataEntry->Flags |= (1 << 5);
                }
                else {
                    pLdrDataEntry->DdagNode->LoadCount = 1;
                    pLdrDataEntry->ObsoleteLoadCount = 1;
                    pLdrDataEntry->Flags &= ~(1 << 5);
                }
                
                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.dll
    HMODULE 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 _WIN64
    typedef 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
        );
#else
    typedef 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
        );
#endif

    SetLastError(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.dll
    HMODULE 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 _WIN64
    typedef 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
        );
#else
    typedef 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
        );
#endif

    SetLastError(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 ACG
    if (!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 DLLs
    if (!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 Guards
    if (!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];                             //0x0
        struct
        {
            _RTL_BALANCED_NODE* Left;                                //0x0
            _RTL_BALANCED_NODE* Right;                               //0x8
        };
    };
    union
    {
        struct
        {
            UCHAR Red : 1;                                                    //0x10
            UCHAR 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;                                           //0x0
    ULONG 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;                                                     //0x0
    PLDR_SERVICE_TAG_RECORD ServiceTagList;                                 //0x10
    ULONG LoadCount;                                                        //0x18
    ULONG LoadWhileUnloadingCount;                                          //0x1c
    ULONG LowestLink;                                                       //0x20
    LDRP_CSLIST Dependencies;                                               //0x28
    LDRP_CSLIST IncomingDependencies;                                       //0x30
    LDR_DDAG_STATE State;                                                   //0x38
    SINGLE_LIST_ENTRY CondenseLink;                                         //0x40
    ULONG 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;                                            //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;


//0x58 bytes (sizeof)
typedef struct _PEB_LDR_DATA32
{
    ULONG Length; // +0x00
    BOOLEAN Initialized; // +0x04
    PVOID SsHandle; // +0x08
    LIST_ENTRY InLoadOrderModuleList; // +0x0c
    LIST_ENTRY InMemoryOrderModuleList; // +0x14
    LIST_ENTRY InInitializationOrderModuleList;// +0x1c
} PEB_LDR_DATA32, * PPEB_LDR_DATA32; // +0x24


typedef struct _PEB32
{
    UCHAR InheritedAddressSpace;                                            //0x0
    UCHAR ReadImageFileExecOptions;                                         //0x1
    UCHAR BeingDebugged;                                                    //0x2
    union
    {
        UCHAR BitField;                                                     //0x3
        struct
        {
            UCHAR ImageUsesLargePages : 1;                                    //0x3
            UCHAR IsProtectedProcess : 1;                                     //0x3
            UCHAR IsImageDynamicallyRelocated : 1;                            //0x3
            UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3
            UCHAR IsPackagedProcess : 1;                                      //0x3
            UCHAR IsAppContainer : 1;                                         //0x3
            UCHAR IsProtectedProcessLight : 1;                                //0x3
            UCHAR IsLongPathAwareProcess : 1;                                 //0x3
        };
    };
    PVOID Mutant;                                                           //0x4
    PVOID ImageBaseAddress;                                                 //0x8
    PEB_LDR_DATA32* Ldr;                                                    //0xc
    struct RTL_USER_PROCESS_PARAMETERS* ProcessParameters;                  //0x10
    PVOID SubSystemData;                                                    //0x14
    PVOID ProcessHeap;                                                      //0x18
    RTL_CRITICAL_SECTION* FastPebLock;                                      //0x1c
    SLIST_HEADER* volatile AtlThunkSListPtr;                                //0x20
    PVOID IFEOKey;                                                          //0x24
} PEB32, * PPEB32;

typedef struct _STRING64
{
    USHORT Length;                                                          //0x0
    USHORT MaximumLength;                                                   //0x2
    ULONGLONG Buffer;                                                       //0x8
}STRING64, * LPSTRING64;

typedef struct _PEB_LDR_DATA64
{
    ULONG Length;                                                           //0x0
    UCHAR Initialized;                                                      //0x4
    PVOID SsHandle;                                                         //0x8
    LIST_ENTRY InLoadOrderModuleList;                                       //0x10
    LIST_ENTRY InMemoryOrderModuleList;                                     //0x20
    LIST_ENTRY InInitializationOrderModuleList;                             //0x30
    PVOID EntryInProgress;                                                  //0x40
    UCHAR ShutdownInProgress;                                               //0x48
    PVOID ShutdownThreadId;                                                 //0x50
}PEB_LDR_DATA64, * PPEB_LDR_DATA64;

typedef struct _PEB64
{
    UCHAR InheritedAddressSpace;                                            //0x0
    UCHAR ReadImageFileExecOptions;                                         //0x1
    UCHAR BeingDebugged;                                                    //0x2
    union
    {
        UCHAR BitField;                                                     //0x3
        struct
        {
            UCHAR ImageUsesLargePages : 1;                                    //0x3
            UCHAR IsProtectedProcess : 1;                                     //0x3
            UCHAR IsImageDynamicallyRelocated : 1;                            //0x3
            UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3
            UCHAR IsPackagedProcess : 1;                                      //0x3
            UCHAR IsAppContainer : 1;                                         //0x3
            UCHAR IsProtectedProcessLight : 1;                                //0x3
            UCHAR IsLongPathAwareProcess : 1;                                 //0x3
        };
    };
    UCHAR Padding0[4];                                                      //0x4
    ULONGLONG Mutant;                                                       //0x8
    ULONGLONG ImageBaseAddress;                                             //0x10
    PEB_LDR_DATA64* Ldr;                                                    //0x18
    ULONGLONG ProcessParameters;                                            //0x20
    ULONGLONG SubSystemData;                                                //0x28
    ULONGLONG ProcessHeap;                                                  //0x30
    ULONGLONG FastPebLock;                                                  //0x38
    ULONGLONG AtlThunkSListPtr;                                             //0x40
    ULONGLONG 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;
            }
        }
    //}

    // UnHook
    UnhookWMsgKMessageExceptionHandler(NULL);
    auto WMsgKMessageHandler = (__WMsgKMessageHandler)lpgHandlerInfo.lpHookAddress;

    dwFunResponse = WMsgKMessageHandler(uMachineState, uMsgWLGenericKey, pAsync, lpStatus);
    // Re-hook
    HookWMsgKMessageExceptionHandler(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();  // 保存初始化例程所在线程TID
    lpgHandlerInfo.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结构体中对应的字段,并返回TRUE
    lpHandlerInfo->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 trampoline
    unsigned 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 Protect
    ldrpt.bEnableProtect = TRUE;
    if (EasyProtectLibrary(&ldrpt) == 0)
    {
        OutputDebugStringW(L"Er:[HookHandler] status:[EasyProtectLibrary failed].\n");
        return FALSE;
    }

    // disable write protect
    SetLastError(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 bytes
    if (memcpy_s(pOriginalBytes, bufferSize, hHookAddress, bufferSize) != 0)
    {
        OutputDebugStringW(L"Er:[HookHandler] status:[memcpy failed, 1:2].\n");
        return GetLastError();
    }

    SetLastError(0);
    // write the hook
    if (memcpy_s(hHookAddress, bufferSize, boing, bufferSize) != 0)
    {
        OutputDebugStringW(L"Er:[HookHandler] status:[memcpy failed, 2:2].\n");
        return GetLastError();
    }

    // Flush Cache to make code work
    bResponse = FlushInstructionCache(GetCurrentProcess(), hHookAddress, bufferSize);
    if (!bResponse)
    {
        OutputDebugStringW(L"Er:[HookHandler] status:[FlushInstructionCache failed].\n");
        return GetLastError();
    }

    // enable write protect
    bResponse = 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 protect
    bResponse = VirtualProtect(hookAddress, 13, PAGE_EXECUTE_READWRITE, &oldProtect);
    if (!bResponse)
    {
        OutputDebugStringW(L"Er:[UnHookHandler] status:[VirtualProect failed, EXECUTE_READWRITE].\n");
        return bResponse;
    }

    // recover original bytes
    if (memcpy_s(hookAddress, 13, pOriginalBytes, 13) != 0)
    {
        OutputDebugStringW(L"Er:[UnHookHandler] status:[memcpy failed].\n");
        return bResponse;
    }

    // Flush Cache to make code work
    bResponse = FlushInstructionCache(GetCurrentProcess(), hookAddress, 13);
    if (!bResponse)
    {
        OutputDebugStringW(L"Er:[HookHandler] status:[FlushInstructionCache failed].\n");
        return GetLastError();
    }

    // enable write protect
    bResponse = 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 _WIN64
    ULONGLONG ModuleSum = NULL;
    PPEB peb = (PPEB)__readgsqword(0x60);
#else
    ULONG 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 module
    if (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);

    // 模块大小不能为 0
    if (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 ACG
    if (!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 DLLs
    if (!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 Guards
    if (!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 header
        PIMAGE_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 header
        PIMAGE_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 directory
        PIMAGE_EXPORT_DIRECTORY exports = 
            reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>((reinterpret_cast<BYTE*>(lpBaseAddress))
            + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

        // Get export tables
        DWORD* 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 table
        while (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-termination
                size_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 names
                    int compareResult = _stricmp(buffer, FunName);

                    delete[] buffer; // Free allocated memory

                    if (compareResult == 0) {
                        // Function name found, return function address
                        DWORD functionRVA = addressTable[ordinalTable[mid]];
                        FARPROC pfunAddress = reinterpret_cast<FARPROC>(
                            reinterpret_cast<UINT64>(lpBaseAddress) + functionRVA );
                        SetLastError(0);
                        return pfunAddress;
                    }

                    // Update search range based on comparison result
                    if (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.dll
    HMODULE 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 _WIN64
    typedef 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
        );
#else
    typedef 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
        );
#endif

    SetLastError(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.dll
    HMODULE 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 _WIN64
    typedef 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
        );
#else
    typedef 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
        );
#endif

    SetLastError(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.04

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1432811.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

泡泡清新文艺的微社区系统PHP源码

泡泡微社区&#xff0c;小巧而强大&#xff0c;为您带来卓越的社区交流体验。 凭借GoZinc的先进架构&#xff0c;泡泡在保持轻盈身姿的同时&#xff0c;功能一应俱全。前端采用Vue3匠心打造&#xff0c;界面清新简约&#xff0c;三栏式布局仿若Twitter&#xff0c;让您一见倾心…

前端开发中不同语言【react-i18next】

目录 查看并设置语言 单页面&#xff1a;html lang ​编辑 浏览器 自定义翻译&#xff1a;react-i18next 设置 模块&#xff1a;staticData.ts 散(重复利用)&#xff1a;命名空间.json 应用 准备 html标签 查看并设置语言 单页面&#xff1a;html lang 英语: <…

Fink CDC数据同步(五)Kafka数据同步Hive

6、Kafka同步到Hive 6.1 建映射表 通过flink sql client 建Kafka topic的映射表 CREATE TABLE kafka_user_topic(id int,name string,birth string,gender string ) WITH (connector kafka,topic flink-cdc-user,properties.bootstrap.servers 192.168.0.4:6668…

合体积木

欢迎来到程序小院 合体积木 玩法&#xff1a;点击积木移动&#xff0c;将积木合并一起&#xff0c;移动步数越少获得⭐️⭐️越多&#xff0c; 共52关卡&#xff0c;每关卡都有不同的积木摆放&#xff0c;快去闯关吧^^。开始游戏 html <canvas id"gameCanvas" w…

多播路由选择

目录 1 多播路由选择 1.1 转发多播数据报时使用三种方法 (1) 洪泛与剪除 RPB 的要点&#xff1a; 1.检查&#xff0c;转发 2.形成以源为根节点的多播转发树 3.剪枝与嫁接 (2) 隧道技术 (tunneling) (3) 基于核心的发现技术 1.2 几种多播路由选择协议 1 多播路由选择 …

C++实战Opencv第二天——色彩空间转换函数和opencv中图像对象创建与赋值(从零开始,保姆教学)

OpenCV是一个强大的计算机视觉库&#xff0c;使用C作为主要编程语言&#xff0c;对于图像处理和计算机视觉领域具有重要意义。其提供了丰富的功能和算法&#xff0c;使得开发者能够快速实现各种图像处理和计算机视觉应用。OpenCV C为图像处理和计算机视觉领域的开发者提供了一个…

前端JavaScript篇之JavaScript 类数组对象的定义?如何将类数组对象转换为真正的数组

目录 JavaScript 类数组对象的定义&#xff1f;如何将类数组对象转换为真正的数组如何将类数组对象转换为真正的数组 JavaScript 类数组对象的定义&#xff1f;如何将类数组对象转换为真正的数组 类数组对象指的是具有类似数组结构&#xff08;类似于数组的属性或方法&#xf…

西瓜书学习笔记——流形学习(公式推导+举例应用)

文章目录 等度量映射&#xff08;仅保留点与其邻近点的距离&#xff09;算法介绍实验分析 局部线性嵌入&#xff08;不仅保留点与其邻近点的距离还要保留邻近关系&#xff09;算法介绍实验分析 等度量映射&#xff08;仅保留点与其邻近点的距离&#xff09; 算法介绍 等度量映…

vue中 日期选择--本日、本周、本月、本年选择器实现(基于elementui)

效果图&#xff1a; 由于项目需要图标统计展示&#xff0c;需要日期美观化选择如上图所示&#xff0c;代码如下&#xff1a; <template><div class"el-page body"><el-row><el-col class"statistic-analysis-report-style" :span&qu…

Elastic Search 6.x 版本 rollover 配置

背景 业务里有发送消息的请求&#xff0c;如短信、邮件、企信等&#xff0c;这些数据都会存储到 ES 中&#xff0c;用于数据的查询和问题排查等。每天都有几十万至几百万的数据&#xff0c;手动删除数据也比较繁琐&#xff0c;可以通过 ES 的 rollover 机制来实现根据条件自动…

PHP框架详解 - symfony框架

首先说一下为什么要写symfony框架&#xff0c;这个框架也属于PHP的一个框架&#xff0c;小编接触也是3年前&#xff0c;原因是小编接触Golang&#xff0c;发现symfony框架有PHP框架的东西也有Golang的东西&#xff0c;所以决定总结一下&#xff0c;有需要的同学可以参看小编的G…

【数据结构与算法】(7)基础数据结构之双端队列的链表实现、环形数组实现示例讲解

目录 2.6 双端队列1) 概述2) 链表实现3) 数组实现习题E01. 二叉树 Z 字层序遍历-Leetcode 103 2.6 双端队列 1) 概述 双端队列、队列、栈对比 定义特点队列一端删除&#xff08;头&#xff09;另一端添加&#xff08;尾&#xff09;First In First Out栈一端删除和添加&…

电脑上常见的绘图软件有哪些?

现在在电脑上绘图很流行&#xff0c;不仅可以随时更改&#xff0c;还可以提高绘图效率&#xff0c;绘图软件中有很多工具。市场上的计算机绘图软件种类繁多。包括艺术设计、工业绘图和3D绘图。那么每个绘图软件都有自己的特点。那么&#xff0c;哪个更适合计算机绘画软件呢&…

信任与创新 | 回顾通付盾的2023!

-END- 数信云&#xff0c;基于区块链与人工智能的数据安全应用与服务平台

基于SpringBoot的后端导出Excel文件

后端导出Excel&#xff0c;前端下载。 文章目录 后端导出Excel引入依赖写入响应 前端下载后端导出失败和成功返回的内容类型不同&#xff0c;因此需要分别判断。 工具类ServletUtils.javaFileUtils.javafile.js 后端导出Excel 引入依赖 poi 操作xls&#xff0c;doc…&#xff…

【Script】使用pyOpenAnnotate搭建半自动标注工具(附python源码)

文章目录 0. Background1. Method2. Code3. Example: 雄鹿红外图像标注3.1 选择色彩空间3.2 执行阈值3.3 执行形态学操作3.4 轮廓分析以找到边界框3.5 过滤不需要的轮廓3.6 绘制边界框3.7 以需要的格式保存Reference本文将手把手教你用Python和OpenCV搭建一个半自动标注工具(包…

Flutter开发模仿百度云盘创建文件夹功能Draggable和DragTarget的混合使用

使用LongPressDraggable和DragTarget写了个类似于百度云盘管理文件和文件夹的功能&#xff08;为了避免和列表的滑动手势冲突&#xff0c;所以采用LongPressDraggable而不是Draggable&#xff09;&#xff1a; 1、拖拽文件到文件夹中 2、拖拽两个文件可以合并成一个新的文件夹…

【linux】git和gdb调试工具

在linux下提交代码同步到gitee 1.创建一个新的仓库&#xff08;演示步骤&#xff09; 2.init 这两个步骤用于识别提交代码的身份&#xff0c;一个你的名字&#xff0c;一个你的邮箱 开启本地仓库 克隆本地仓库成功 我们将这个仓库拷到了111目录底下. 我们发现少了一个.gitig…

window 镜像---负载篇

前提&#xff1a;需要修改window的powershell执行脚本的策略 步骤&#xff1a;以管理员身份打开powershell&#xff0c;执行 Get-ExecutionPolicy查看当前执行策略&#xff0c;若返回值是Restricted&#xff0c;需执行Set-ExecutionPolicy RemoteSigned powershell 版本信息&am…

SpringMVC精简知识点

SpringMVC 数据格式化基本数据类型和字符串自动转换特殊数据类型和字符串自动转换 验证及国际化应用实例注意事项和使用细节注解的结合使用数据类型转换校验核心类-DatBinder取消某个属性的绑定中文乱码解决处理json和HttpMessageConverter<T>作业布置SpringMVC文件上传自…