吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2986|回复: 4
收起左侧

[C&C++ 原创] 自写反内核工具(Anti RootKit)之回调枚举和进程线程枚举(附源码和文档)

  [复制链接]
我还是个孩子丶 发表于 2025-2-25 17:52
本帖最后由 我还是个孩子丶 于 2025-2-25 17:54 编辑

内核枚举

0 说明:

1.因为文档里的截图有一点多,因此在帖子中将全部图片都删了,在压缩包中我导出了一份带图片的PDF文档

2.代码中可能含有BUG 请不要将驱动安装在实体机运行

3.代码中所有的特征均来自于对Win10 1909的分析,放到其他版本不保证准确,后续可能重构代码囊括大部分系统

4.三无产品有bug在所难免各位大佬请见谅hhhh,也希望各位佬们多多指正

5.后续会继续完成应用程序界面和通信相关的代码

6.源代码和有图的文档见文末压缩包,解码密码52pj

1 项目描述:

模拟PcHunter的功能 实现一个类似的枚举Ark(Anti RootKit)工具

初步打算实现的目标:回调枚举(以及通过特征对应的解除注册的函数实现反注册)、驱动枚举、进程线程枚举、应用程序界面、通信框架

目前已经完成:

1.特征码搜索框架
2.全局句柄表枚举进程线程(还没DEBUG)
3.大部分的回调的特征定位和结构解析

进程、线程、载入镜像 回调数组:
    通过PspSetCreateProcessNotifyRoutine获取PspCreateProcessNotifyRoutine变量;
    通过PspSetCreateThreadNotifyRoutine获取PspCreateProcessNotifyRoutine变量;
    通过两个导出函数 PsSetLoadImageNotifyRoutineEx 与 PsRemoveLoadImageNotifyRoutine 定位PspLoadImageNotifyRoutine的变量;

Io定时器(DPC还没弄):
        通过定位IoInitializeTimer获取 IoTimerList;

注册表:
        在CmSetCallbackObjectContext中定位CallbackListHead;

错误检查:
        通过 KeRegisterBugCheckCallback 定位 KeBugCheckCallbackListHead;
        通过 KeRegisterBugCheckReasonCallback定位 KeBugCheckReasonCallbackListHead;

关机:
        通过IoUnregisterShutdownNotification 定位 ShutdownListHead+LastChanceShutdownListHead;

电源设置
        通过PoRegisterPowerSettingCallback定位PopRegisteredPowerSettingCallbsacks;

pnp 回调   
    通过IoRegisterPlugPlayNotification函数定位:PnpProfileNotifyList + PnpDeviceClassNotifyList + PiRegisterKernelSoftRestartNotification函     数
    通过PiRegisterKernelSoftRestartNotification函数定位PnpKsrNotifyList;
    通过PnpUnregisterPlugPlayNotification函数定位PnpDeferredRegistrationList;

对象创建回调:
    全局导出不需要特征
    POBJECT_TYPE pProcessType = NULL;
    POBJECT_TYPE pThreadType = NULL;
    if (pProcessType == NULL) pProcessType = *PsProcessType;
    if (pThreadType == NULL) pThreadType = *PsThreadType;

全局句柄表:
        通过PsLookupThreadByThreadId定位PspCidTable;

2 特征码定位框架(MySearchTools

注意事项:特征匹配的地址可能存在缺页的情况再匹配特征时要先验证是否有效并读几个字节确保其存在于内存中(见FecSearchCodeFromAddressEx中的try

2.1  函数说明

// 特征码初始化以及释放函数 
NTSTATUS FecInitFeatureCode(PFEATURE_CODE FeatureCode, PCHAR code);                //将特征码字符串转变为16进制 且将空格 问号? 星号* 做处理 方便后续搜索
VOID FecFreeFeaturCode(PFEATURE_CODE CodeStruct);

// 从指定地址开始搜索特征码  已不再使用 转为FecSearchCodeFromAddressEx
FecSearchCodeFromAddress
NTSTATUS FecSearchCodeFromAddressEx(                //从指定地址搜索开始搜索特征码 
        ULONG_PTR start,        //起始地址
        _In_opt_ ULONG size,        //搜索大小
        PFEATURE_CODE FeatureCode,        //特征码结构指针
        PUCHAR groups,                        //搜索第几个问号组,可以是一个数组
        UCHAR queryNum,                        //搜索数量
        _Out_ PFEATURE_RESULT FecResults,        //返回的结果数组
        _Out_opt_ PUCHAR outFeatureNum);        //返回数量

// 从指定内核函数开始搜索特征码  已不再使用  转为FecSearchCodeFromKernelFunc
NTSTATUS FecSearchCodeFromKernelFunc
NTSTATUS FecSearchCodeFromKernelFuncEx(
        PFEATURE_CODE FeatureCode,                //特征码
        PUNICODE_STRING FuncName,                //函数名
        PUCHAR groups,                                        //下面的同上
        UCHAR queryNum,
        _Out_ PFEATURE_RESULT FecResults,
        _Out_opt_ PUCHAR outFeatureNum);

ULONG_PTR GetAddressByOffset(PFEATURE_RESULT SearchResult);//将搜索返回的FEATURE_RESULT结构转为地址 用于64位 mov lea call的地址转换

ULONG GetImageFileNameOffsetInEprocess(ULONG_PTR Address); //定位EPROCESS结构中文件名的偏移

//一个 不好用 的将上面的搜索方式融合的函数 能同时搜索n个地址的n个特征码并返回m个结果(m>=n)
NTSTATUS FecSearchFeatureCodeGroup(
        PFEATURE_CODE FeatureCode,
        _In_opt_ PULONG size,
        UCHAR FeatureNum,
        PUCHAR GroupNums,
        PUCHAR SearchMode,
        PULONG_PTR NameAndAddress,
        PUCHAR groups,
        _Out_ PFEATURE_RESULT FecResults,
        _Out_opt_ PUCHAR outFeatureNum,
        _Out_opt_ PUCHAR outGroupNum);

// 特征定位返回的结构  
typedef struct _FEATURE_RESULT
{
        ULONG_PTR Address;         //特征码所在地址
        union                                //值 这么设计为了方便拿取不同长度
        {
                ULONG64 qWord;
                UINT32 dWord;
                USHORT Word;
                UCHAR Byte;
        }Value;
}FEATURE_RESULT, *PFEATURE_RESULT;

3 Io定时器枚举

3.1 特征定位

特征定位:在IoInitializeTimer内存在IopTimerQueueHead即为定时器的链表头,存放_IO_TIMER结构体

这个结构体可以通过 Vergilius项目获取

特征码

48 8D 50 ?? 48 89 70 ?? 4C 8D 05 ?? ?? ?? ?? 48 89 78 ?? 48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 33 C0 

:第5组问号即为链表地址,可以用第6组问号确认ExInterlockedInsertTailList地址是否正确用来确认定位是否有问题

3.2 寻址算法解析

这里没有什么额外的寻址算法,拿到的就是_IO_TIMER结构体中的TimerList,通过CONTAINING_RECORD(pNode, _IO_TIMER, TimerList)即可拿到结构体的首地址

在遍历这个链表,即可枚举出所有的IO定时器

//0x30 bytes (sizeof)
struct _IO_TIMER
{
    SHORT Type;                                                             //0x0
    SHORT TimerFlag;                                                        //0x2
    struct _LIST_ENTRY TimerList;                                           //0x8
    VOID (*TimerRoutine)(struct _DEVICE_OBJECT* arg1, VOID* arg2);          //0x18
    VOID* Context;                                                          //0x20
    struct _DEVICE_OBJECT* DeviceObject;                                    //0x28
}; 

4 全局句柄表枚举(枚举进程线程句柄并解密时进程线程对象)

4.1  特征定位:

PsLookupProcessByProcessId中调用了PspReferenceCidTableEntry

PspReferenceCidYableEntry中使用了PspCidTable

定位路径1PsLookupProcessByProcessId->PspReferenceCidTableEntry->PspCidTbale

PsLookupThreadByThreadId中同样使用了PspCidTable

定位路径2PsLookupThreadByThreadId->PspCidTbale

注意我这个内核文件的Ida解析的稍有问题,KeNumberProcessorsGroup0+12h即为PspCidTable

PsLookupThreadByThreadId中的特征码:

48 8B 05 ?? ?? ?? ?? F7 C1 FC 03 00 00 0F 84 ?? ?? ?? ?? 48 8B D1 48 8B C8 E8 ?? ?? ?? ?? 4C 8B F0 48 85 C0 0F 84 ?? ?? ?? ?? 48 8B 2D ?? ?? ?? ?? 33 C0 

第1、5组问号是即为PspCidTbale的偏移

4.2 PspCidTable句柄表结构解析

PspCidTable中的值是_HANDLE_TABLE结构的指针_HANDLE_TABLE->TableCode指向句柄表的地址,是一个个动态的多层结构

分析ExpLookupHandleTableEntry函数

想要枚举里面所有的元素,首先要判断句柄表是几层的,通过_HANDLE_TABLE->TableCode的最后两位来判断(00为一层、01两层、02三层)

例如,当TableCode的值为0xffffe48d72266001时说明句柄表是二级结构,去掉后两位得到0xffffe48d72266000即为一级句柄表的首地址,里面存放的是所有二级表的地址

据观察最后一层句柄表中的元素是一个16B(64位系统)的结构(网上说是HandleTableEntry结构,通过函数名ExpLookupHandleTableEntry来看很合理,但是网上并没有找到这个结构的声明)。通过观察,发现高8B往往都是填充0,低8B是一个被加密了的_EPROCESS\_ETHREAD结构地址

PsLookupThreadByThreadId可以发现,该地址通过(Val >> 0x10) & 0xFFFFFFFFFFFFFFF0来解密

这样,我们就能得到在系统注册的所有进程、线程的_EPROCESS\_ETHREAD结构,从而枚举系统中的所有进程、线程

4.3 关于如何从_EPROCESS中获取ImageFileName

方法14号进程时固定的System进程 且名字为"System"故通过定位该进程则可以准确获取_EPROCESSImageFileName的偏移

方法2:通过手动导出PsGetProcessImageFileName函数 同样可以通过_EPROCESS结构查询进程的名字 推荐这种方式 省心

5 CreateProcess、CreateThread、LoadImage回调枚举

这三个回调在内核中的数据结构是一样的,Windows 内核维护了一个全局的 PspCreateProcessNotifyRoutinePspCreateThreadNotifyRoutinePspLoadImageNotifyRoutine 三个数组,存储了所有注册的CreateProcessCreateThreadLoadImage回调函数

以枚举CreateProcess为例进行说明:

5.1 特征定位

CreateProcess回调是由PsSetCreateProcessNotifyRoutine来注册的,在其他程序加载时得到通知的回调函数,回调函数的原型为PCREATE_PROCESS_NOTIFY_ROUTINE

NTSTATUS PsSetCreateProcessNotifyRoutine(
  [in] PCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine,
  [in] BOOLEAN                        Remove
);
PCREATE_PROCESS_NOTIFY_ROUTINE PcreateProcessNotifyRoutine;

void PcreateProcessNotifyRoutine(
  [in] HANDLE ParentId,
  [in] HANDLE ProcessId,
  [in] BOOLEAN Create
)
{...}

通过Ida分析PsSetCreateProcessNotifyRoutine(Ex\Ex2)可以得到,这三个函数内部都调用了PspSetCreateProcessNotifyRoutine这个函数

在跟到PspSetCreateProcessNotifyRoutine中,可以看到其内部使用了PspCreateProcessNotifyRoutine这一变量,即为我们要枚举的CreateProcess回调数组的指针

5.2  定位 PspSetCreateProcessNotifyRoutine函数

路径1:在PsSetCreateProcessNotifyRoutine中调用了PspSetCreateProcessNotifyRoutine

0F 95 C2 E8 ?? ?? ?? ?? 

第一组问号的位置

路径2,定位校验PsSetCreateProcessNotifyRoutineEx中同样使用了PspSetCreateProcessNotifyRoutine

E8 ?? ?? ?? ?? 

第一组问号的位置,二次定位确保信息信息正确

5.3  定位PspCreateProcessNotifyRoutine变量

路径1:在PspSetCreateProcessNotifyRoutine中使用了PspCreateProcessNotifyRoutine变量

33 DB 4C 8D 2D ?? ?? ?? ?? 48 8D 0C DD 00 00 00 00 45 33 C0 

第一组问号的位置

路径2:因为寻找第二个变量调用点过于复杂,使用计算PspCreateProcessNotifyRoutine变量调用点与PspSetCreateProcessNotifyRoutine函数的起始地址指点的偏移量的方式来进行校验。偏移两为0x65

5.4 算法解析

注意PspLoadImageNotifyRoutine是一组指针数组,其中存放的值需要&0xFFFFFFFFFFFFFFF8以后是真正的函数地址的地址

这里有一点绕,以这个图为例子:

第一步:我们通过特征定位拿到的是fffff801`1a10be60(有时这个地址需要&0xfffffffffffffff0),也就是PspLoadImageNotifyRoutine的变量地址
第二步:fffff801`1a10be60指向一个数组我们拿到ffffd403`1a10786f 和 ffffd403`1a262f2f这两个值
第三部:ffffd403`1a10786f & 0xfffffffffffffff8 = 0xffffd403`1a107868 以后是一个地址,里面存的是函数所在的地址
第四步:ffffd403`1a107868中存的是fffff801`1b7aaba0,这才是真正函数存放的位置,不过我们一般拿的就是这个地址,就不需要向下算了

CreateThreadLoadImage回调枚举详见代码EnumCallBack.c中的GetCreateThreadNotifyRoutineGetLoadImageNotifyRoutine

5.5 题外话

在分析PspCreateProcessNotifyRoutine变量时发现,PspEnumerateCallback这一未导出函数同时使用了PspCreateProcessNotifyRoutinePspLoadImageNotifyRoutinePspCreateThreadNotifyRoutine,并且通过名字和对函数进行分析发现其就是对这个三种类型回调的枚举,但是并没有找到能够定位这个函数的方法,所以 放弃,有大佬可以搞一下试试。

6 注册表回调枚举(CmpCallBack)

6.1 特征定位:

注册表回调通过CmRegisterCallbackCmUnRegisterCallback来进行注册和注销,在注册表发生变化时被触发

保存在CallbackListHead链表中,结构为_CM_NOTIFY_ENTRY

特征定位路径

路径1CmUnRegisterCallback → CallbackListHead变量

路径2CmSetCallbackObjectContext→ CallbackListHead变量

特征码:

E8 ?? ?? ?? ?? EB 86 48 8B 1D ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? 48 3B D9 74 11 49 8B 07 

2、3问号组都能定位到CallbackListHead表头

路径3CmRegisterCallbackEx → CmpRegisterCallbackInternal → CmpInsertCallbackInListByAltitude → CallbackListHead

路径4CmRegisterCallback → CmpRegisterCallbackInternal → CmpInsertCallbackInListByAltitude → CallbackListHead

考虑到第二种调用路径中本身就有两次变量的使用,因此使用这个函数做特征定位和校验

6.2 结构解析

地址同样没有加密,最后解析的地址是_CM_NOTIFY_ENTRY结构,不过这个结构在vergiliusproject没有找到:

typedef struct _CM_NOTIFY_ENTRY{
    LIST_ENTRY ListEntryHead;
    ULONG Unknow1;
    ULONG UnKnow2;
    LARGE_INTEGER Cookie;
    PVOID Context;
    PVOID Function;
}CM_NOTIFY_ENTRY,*PCM_NOTIFY_ENTRY;

7 ObCall回调枚举

7.1 特征定位

导出的全局变量PsProcessType/PsThreadTypeOBJECT_TYPE类型的二级指针

_OBJECT_TYPE->CallbackList_OB_CALLBACK类型的链表结构的表头

//0xd8 bytes (sizeof)
struct _OBJECT_TYPE
{
        struct _LIST_ENTRY TypeList;                                            //0x0
        struct _UNICODE_STRING Name;                                            //0x10        //进程就是 process  线程就是Thread
        VOID* DefaultObject;                                                    //0x20
        UCHAR Index;                                                            //0x28        //句柄类型ID process 是 7 ;Thread是8
        ULONG TotalNumberOfObjects;                                             //0x2c
        ULONG TotalNumberOfHandles;                                             //0x30
        ULONG HighWaterNumberOfObjects;                                         //0x34
        ULONG HighWaterNumberOfHandles;                                         //0x38
        struct _OBJECT_TYPE_INITIALIZER TypeInfo;                               //0x40
        struct _EX_PUSH_LOCK TypeLock;                                          //0xb8
        ULONG Key;                                                              //0xc0
        struct _LIST_ENTRY CallbackList;                                        //0xc8
};

8 BugCheck回调、BugCheckReason回调枚举

BugCheck回调:在系统发生崩溃时调用(不论崩溃的原因是什么

BugCheckReason回调:在系统发生某个特定类型的崩溃时,调用指定类型BugCheckReason回调

8.1 特征定位

分析KeRegisterBugCheckCallbackKeRegisterBugCheckReasonCallback这两函数,发现其内部分别调用了KeBugCheckCallbackListHeadKeBugCheckReasonCallbackListHead这两个链表

KeBugCheckCallbackListHead特征码:(第一组和第六组)

48 8B 05 ?? ?? ?? ?? 4C 39 40 ?? 0F 85 ?? ?? ?? ?? 48 89 03 40 B6 01 4C 89 43 ?? 48 89 58 ?? 48 89 1D ?? ?? ?? ?? 

KeBugCheckReasonCallbackListHead特征码:第四组,下一行距离函数首地址偏移1401798B8-140179850

80 7B ?? 00 0F 85 ?? ?? ?? ?? 83 FF 04 0F 84 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? 83 FF 06 0F 84 ?? ?? ?? ?? 

8.2 结构解析:

在Wrk中可以找到这两个链表结构的定义

typedef struct _KBUGCHECK_CALLBACK_RECORD {
    LIST_ENTRY Entry;
    PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine;
    PVOID Buffer;
    ULONG Length;
    PUCHAR Component;
    ULONG_PTR Checksum;
    UCHAR State;
} KBUGCHECK_CALLBACK_RECORD, *PKBUGCHECK_CALLBACK_RECORD;

typedef struct _KBUGCHECK_REASON_CALLBACK_RECORD {
    LIST_ENTRY Entry;
    PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine;
    PUCHAR Component;
    ULONG_PTR Checksum;
    KBUGCHECK_CALLBACK_REASON Reason;
    UCHAR State;
} KBUGCHECK_REASON_CALLBACK_RECORD, *PKBUGCHECK_REASON_CALLBACK_RECORD;

typedef enum _KBUGCHECK_CALLBACK_REASON {
    KbCallbackInvalid,
    KbCallbackReserved1,
    KbCallbackSecondaryDumpData,
    KbCallbackDumpIo,
} KBUGCHECK_CALLBACK_REASON;

9 关机回调Shutdown

9.1  定义:

关机回调(Shutdown)驱动程序或内核模块注册的函数,用于在系统关机、重启或休眠前被调用。

通常通过 IoRegisterShutdownNotificationIoRegisterLastChanceShutdownNotification(关机前最后机会) 注册。

存放在IopNotifyShutdownQueueHeadIopNotifyLastChanceShutdownQueueHead两个链表中

9.2 特征定位:

IoRegisterShutdownNotification 为例:通过Wrk分析可以发现,函数将注册关机回调的驱动存放到SHUTDOWN_PACKET结构中并插入到IopNotifyShutdownQueueHead链表当中,并设置驱动标志位的DO_SHUTDOWN_REGISTERED,没有其他的加密或者偏移计算的工作

typedef struct _SHUTDOWN_PACKET {
    LIST_ENTRY ListEntry;
    PDEVICE_OBJECT DeviceObject;
} SHUTDOWN_PACKET, *PSHUTDOWN_PACKET;

NTSTATUS
IoRegisterShutdownNotification(
    IN PDEVICE_OBJECT DeviceObject
    )
{
    PSHUTDOWN_PACKET shutdown;
    PAGED_CODE();
    shutdown = ExAllocatePoolWithTag( NonPagedPool,
                                      sizeof( SHUTDOWN_PACKET ),
                                      'hSoI' );
    if (!shutdown) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    shutdown->DeviceObject = DeviceObject;
    ObReferenceObject(DeviceObject);    

    IopInterlockedInsertHeadList( &IopNotifyShutdownQueueHead,
                                  &shutdown->ListEntry );

    DeviceObject->Flags |= DO_SHUTDOWN_REGISTERED;

    return STATUS_SUCCESS;
}

IopNotifyShutdownQueueHead特征码:

IoUnregisterShutdownNotification函数中存在两次引用,故用此函数定位 第2,3组特征

E8 ?? ?? ?? ?? 48 8B 1D ?? ?? ?? ?? 4C 8D 35 ?? ?? ?? ?? 40 8A E8 EB 43 

IopNotifyLastChanceShutdownQueueHead特征码:

IoUnregisterShutdownNotification函数中同样存在两次引用,故用此函数定位  第1,2组特征

49 3B DE 75 B8 48 8B 1D ?? ?? ?? ?? 4C 8D 35 ?? ?? ?? ?? EB 3B 

题外话:从这里可以看出,这两种回调是用同一个函数解除注册的

10 枚举即插即用(Pnp)回调

10.1 分析

即插即用Pnp回调指在系统发生指定类型的Pnp事件时被调用,通过IoRegisterPlugPlayNotification注册

我的Wrk中没有这个函数的定义,所以只能用IDA看了。

首先,我们通过微软的官方文档可以看到IoRegisterPlugPlayNotification能够注册4种类型的Pnp回调

typedef enum _IO_NOTIFICATION_EVENT_CATEGORY {
  EventCategoryReserved,
  EventCategoryHardwareProfileChange,
  EventCategoryDeviceInterfaceChange,
  EventCategoryTargetDeviceChange,
  EventCategoryKernelSoftRestart
} IO_NOTIFICATION_EVENT_CATEGORY;

然后通过IDA分析这个函数,反汇编发现该函数进行了 初始化PnpInitializeNotifyEntry 的操作然后将这四种不同的回调分开存储到不同的链表当中

通过这里猜测,这四个链表对应四种不同类型的Pnp回调

8.10.2 特征定位

PnpProfileNotifyList

第二组问号

48 8B 05 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 39 10 0F 85 ?? ?? ?? ?? 48 89 17 48 8D 0D ?? ?? ?? ?? 48 89 47 ?? 

PnpDeviceClassNotifyList

41 03 0E F7 E1 C1 EA 02 6B C2 0D 48 8D 15 ?? ?? ?? ?? 2B C8 8B C1 48 C1 E0 04 48 03 C2 

PnpKsrNotifyList

对于EventCategoryKernelSoftRestart类型的回调,IoRegisterPlugPlayNotification中调用了PiRegisterKernelSoftRestartNotification直接进行了插入

首先定位PiRegisterKernelSoftRestartNotification函数,第二组问号的位置

4C 8B 45 ?? 4D 8B CF 49 8B D6 49 8B CC E8 ?? ?? ?? ?? 8B D8 E9 ?? ?? ?? ?? 

:这段代码距离IoRegisterPlugPlayNotification的起始地址较远,可以+EA000以后再开始搜索

得到PiRegisterKernelSoftRestartNotification函数地址以后,在函数中搜索PnpKsrNotifyList变量 (第2、3组问号的位置)

E8 ?? ?? ?? ?? 48 8B 1D ?? ?? ?? ?? 4C 8D 2D ?? ?? ?? ?? 49 3B DD 0F 84 ?? ?? ?? ?? 44 8D 66 ?? 

PnpDeferredRegistrationList

该种类型的回调在PnpInitializeNotifyEntry中初始化完成以后,调用PnpDeferNotification插入到链表中

分析PnpDeferredRegistrationList的使用时,发现其在PnpUnregisterPlugPlayNotification中被使用了两次

PnpUnregisterPlugPlayNotification可由IoUnregisterPlugPlayNotificationIoUnregisterPlugPlayNotificationEx两个导出函数定位

这样似乎比从PnpDeferNotification定位更快更准一些

首先定位PnpUnregisterPlugPlayNotification函数

E8 ?? ?? ?? ?? 48 83 C4 28                         //IoUnregisterPlugPlayNotification 函数本身就没几行代码
E8 ?? ?? ?? ?? 48 83 C4 28                           //IoUnregisterPlugPlayNotificationEx 函数本身就没几行代码

这两个函数本身就只是一个调用,因此很好定位

PnpUnregisterPlugPlayNotification函数中定位PnpDeferredRegistrationList

48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B 35 ?? ?? ?? ?? 4C 8D 2D ?? ?? ?? ??                 //第3、4组问号

链表结构体

在网上找了一下,并没找到这个结构体,只能自己逆一个:

// Win10 1909中 此结构为96B
typedef struct _PNP_CALLBACK_LIST
{
        LIST_ENTRY list;                        //16B
        ULONG32 Category;                        //4B
        ULONG32 SessionId;                        //4B
        ULONG64 UnKnow1;                        //8B 总是0
        ULONG64 CallBackRoutine;        //8B
        ULONG64 Context;                        //8B
        PDRIVER_OBJECT DriverObj;        //8B
        ULONG64 UnKnow2;                        //8B
        ULONG64 PnpTargetDeviceNotifyLock;                //8B
        ULONG64 UnKnowBuffer_104;                        //8B
        ULONG64 UnKnow3[2];                                //16B
}PNP_CALLBACK_LIST,*PPNP_CALLBACK_LIST;

11 电源设置(PowerSetting)回调

PoRegisterPowerSettingCallback中引用的 PopRegisteredPowerSettingCallbsacks存放,是一个链表结构

11.1 特征定位

PoRegisterPowerSettingCallback中定位PopRegisteredPowerSettingCallbsacks:(第二组问号 lea)

48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? 48 39 08 0F 85 ?? ?? ?? ?? 48 89 0B 48 89 43 ?? 48 89 18 48 89 1D ?? ?? ?? ?? E9 ?? ?? ?? ?? 

11.2 结构解析

链表结构:

typedef struct _POWER_SETTING_CALLBACK_LIST
{
        LIST_ENTRY ListEntry;                //16B        0
        ULONG32 Magic;                                //4B        0x10  = 0x74655350
        UCHAR Reserved1[16];                //16B   0x14
        GUID Guid1;                                //16B  0x24
        GUID Guid2;                                //16B 0x34  从PopPrimaryDisplayVisibleStateErratum变量获取
        UCHAR Reserved2[4];                        //4B   0x44
        ULONG64 PowerSettingConfiguration; //8B 0x48        从PopFindPowerSettingConfiguration中获取
        PVOID pCallback;                        //8B        +0x50
        PVOID pContext;                                //8B        +0x58
        PDEVICE_OBJECT pDevic;                //8B        +0x60
}POWER_SETTING_CALLBACK_LIST,*PPOWER_SETTING_CALLBACK_LIST;

IDA中引用此结构体测试是否正确

12 感谢

感谢各位的支持!

EnumTools.zip

667.38 KB, 下载次数: 76, 下载积分: 吾爱币 -1 CB

解码密码:52pj

免费评分

参与人数 9威望 +2 吾爱币 +109 热心值 +9 收起 理由
不爱everyone + 1 + 1 我很赞同!
努力加载中 + 1 + 1 谢谢@Thanks!
2013年 + 1 + 1 我很赞同!
yp17792351859 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
max2012 + 1 + 1 谢谢@Thanks!
苏紫方璇 + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
无名 + 1 + 1 谢谢@Thanks!
笙若 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Knm 发表于 2025-3-3 11:44
有时间了学习一下
anvx 发表于 2025-3-5 09:35
用特征码做会有兼容性问题  现在有解析符号表避免硬编码特征码的方案
 楼主| 我还是个孩子丶 发表于 2025-3-5 17:07
anvx 发表于 2025-3-5 09:35
用特征码做会有兼容性问题  现在有解析符号表避免硬编码特征码的方案

初学者,还没听过这个
kulouxiaohai 发表于 2025-11-17 11:29
这个代码是c语言写的吗?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-4-15 08:42

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表