Windows 回调机制

😄

1 CALLBACK_OBJECT

本文是内核层的 WDK 开发,并不是 3 环的开发。

本文分析系统版本:

10.png

CALLBACK_OBJECT 是一种内核命名对象(匿名的回调对象是无意义的),内核中使用基于改内核回调对象的回调机制。注意这里的回调机制区别于 NotifyRoutine 回调函数。

回调机制为驱动程序提供了一种通用的方法来发送和接收某类通知,这些通告可以是系统某个部件的状态发生变化而产生的通告,也 可以是开发者自定义的某个条件因为条件成立而产生的通告。

系统已经定义三个回调对象供驱动程序使用,每个回调对象都有一个名称和一组属性,这些属性是在创建对象时定义的:

  • \Callback\SetSystemTime,当系统时间发生变化时,此回调提供通知。
  • \Callback\PowerState,当发生以下情况之一时, \Callback\PowerState 回调对象将提供通知:
    • 系统从交流电源切换到直流电电源,反之亦然。
    • 系统电源策略因用户或应用程序请求而更改。
    • 即将过渡到系统睡眠或关闭状态。 驱动程序可以请求通知,以便可以将代码锁定到内存中,以便于关闭。 在电源管理器发送系统设置-power IRP 之前,会通知回调例程。
  • \Callback\ProcessorAdd,将新处理器添加到系统时, \Callback\ProcessorAdd 回调会提供通知。

回调对象的使用分为以下几个步骤:

  1. 创建回调对象(ExCreateCallback),创建回调对象之前需要先调用 InitializeObjectAttributes 来设置其属性。
  2. 注册回调例程(ExRegisterCallback),将回调函数注册到回调对象上。
  3. 触发回调通知(ExNotifyCallback),指定条件发生时可以调用该函数触发回调函数执行。
  4. 卸载已经注册的回调例程(ExUnregisterCallback )。
  5. 回调对象解引用(ObDereferenceObject),在删除回调对象之前调用。
  6. 删除回调对象。

_CALLBACK_OBJECT 回调对象结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct _CALLBACK_OBJECT 
{
ULONG Signature, // +0x0,'llaC'
KSPIN_LOCK Lock, // ULONG // +0x8,对象锁,操作回调函数链表时使用
LIST_ENTRY RegisteredCallbacks, // +0x10,回调函数链表
BOOLEAN AllowMultipleCallbacks, // +0x20,是否允许该回调对象上挂两个及以上的回调函数
UCHAR reserved [3] // +0x28,保留
}CALLBACK_OBJECT, *PCALLBACK_OBJECT;

// IDA 定义
_CALLBACK_OBJECT struc ; (sizeof=0x29, mappedto_3733)
Signature dq ?
Lock dq ?
RegisteredCallbacks _LIST_ENTRY ?
AllowMultipleCallbacks dq ?
reserved[3] db ?
_CALLBACK_OBJECT ends

1.1 创建回调对象

9.png

在创建对象之前,驱动程序调用 InitializeObjectAttributes 宏来设置其属性。注意:IRQL <= APC_LEVEL。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
VOID InitializeObjectAttributes(
[out] POBJECT_ATTRIBUTES p, // 指向要初始化的 OBJECT_ATTRIBUTES 结构
[in] PUNICODE_STRING n, // 对象名称,L"//Callback//CallBackName"
[in] ULONG a, // 描述对象属性的 Flags
[in] HANDLE r, // 对象目录句柄,可以使用 ZwCreateDirectoryObjec 获取
[in, optional] PSECURITY_DESCRIPTOR s // 回调对象的安全描述符
);

typedef struct _OBJECT_ATTRIBUTES {
ULONG Length; // 此结构中包含的数据的字节数,设置为 sizeof (OBJECT_ATTRIBUTES)
HANDLE RootDirectory; // ObjectName 成员根对象目录的句柄
PUNICODE_STRING ObjectName; // RootDirectory = NULL,则 ObjectName 为完整路径。Unicode 字符串
ULONG Attributes; // 对象句柄属性的标志的位掩码
PVOID SecurityDescriptor; // 安全描述符 (SECURITY_DESCRIPTOR)
PVOID SecurityQualityOfService; // 创建对象时要应用于对象的可选服务质量
} OBJECT_ATTRIBUTES;

#define InitializeObjectAttributes ( p,
n,
a,
r,
s
)
do { \
(p)->Length = sizeof(OBJECT_ATTRIBUTES); \
(p)->RootDirectory = r; \
(p)->Attributes = a; \
(p)->ObjectName = n; \
(p)->SecurityDescriptor = s; \
(p)->SecurityQualityOfService = NULL; \
} while (0)

当回调对象的属性初始化好之后,便可以调用 ExCreateCallback 函数进行对象创建。回调对象必须具有一个名称,该名称不能与系统定义的回调的名称想同。不允许使用未命名的回调对象,调用方必须指定对象的名称和属性。

ExCreateCallback 例程可以创建新的回调对象或代表调用方打开现有回调对象。

1
2
3
4
5
6
NTSTATUS ExCreateCallback(
[out] PCALLBACK_OBJECT *CallbackObject, // 输出值,指向被创建出来的回调对象
[in] POBJECT_ATTRIBUTES ObjectAttributes, // 指向回调对象属性结构
[in] BOOLEAN Create, // 是否创建回调对象 TRUE:创建,FALSE:打开
[in] BOOLEAN AllowMultipleCallbacks // 是否允许将多个回调例程注册到该回调对象上,只有在创建对象时该参数才有意义
);

返回值:如果打开或创建回调对象,将返回 STATUS_SUCCESS。 否则,它将返回 NTSTATUS 错误代码来指示失败的性质。

1.2 注册回调例程

回调对象创建好后,需要调用 ExRegisterCallback 函数来将回调对象与回调例程进行绑定。注意:IRQL <= APC_LEVEL。

1
2
3
4
5
PVOID ExRegisterCallback(
[in, out] PCALLBACK_OBJECT CallbackObject, // 指向回调对象,由 ExCreateCallback 函数返回
[in] PCALLBACK_FUNCTION CallbackFunction, // 指向回调例程
[in, optional] PVOID CallbackContext // 传递给回调例程的参数
);

返回值:如果注册成功则返回 PVOID 类型的注册句柄,该句柄应被视为不透明并保留供系统使用(如给 ExUnregisterCallback 使用)。如果注册失败则返回 NULL。

回调例程定义如下:

1
2
3
4
5
VOID (*PCALLBACK_FUNCTION ) (
IN PVOID CallbackContext, // ExRegisterCallback 指定的参数三
IN PVOID Argument1, // 回调例程参数一
IN PVOID Argument2 // 回调例程参数一
);

如果可以在 DISPATCH_LEVEL 调用回调例程,调用方应从非分页池分配 CallbackContext。

1.3 触发通知

注意:IRQL <= DISPATCH_LEVEL。

ExNotifyCallback 例程会导致指定回调对象上的所有回调例程得到执行。

1
2
3
4
5
void ExNotifyCallback(
[in] PVOID CallbackObject, // 指向要通知的回调对象
[in, optional] PVOID Argument1, // 对应回调例程的参数二
[in, optional] PVOID Argument2 // 对应回调例程的参数三
);

驱动程序必须在 IRQL <= DISPATCH_LEVEL 调用 ExNotifyCallback;系统必须在驱动程序调用此调用的同一 IRQL 处调用回调例程。

注意:调用 ExNotifyCallback 会使回调对象上的所有回调例程按顺序都得到执行,并不能指定去执行某一个回调例程。

使用示例:

1.4 逆向分析ExCreateCallback

调用 ExCreateCallback 之前需要先调用 InitializeObjectAttributes 宏对回调对象 CALLBACK_OBJECT 属性进行初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
PAGE:00000001407105D0 // =============== S U B R O U T I N E =======================================
PAGE:00000001407105D0
PAGE:00000001407105D0 // Attributes: library function bp-based frame fpd=7Fh
PAGE:00000001407105D0
PAGE:00000001407105D0 // NTSTATUS __stdcall ExCreateCallback(PCALLBACK_OBJECT *CallbackObject, POBJECT_ATTRIBUTES ObjectAttributes, BOOLEAN Create, BOOLEAN AllowMultipleCallbacks)
PAGE:00000001407105D0 public ExCreateCallback
PAGE:00000001407105D0 ExCreateCallback proc near // CODE XREF: HvlPhase2Initialize+1C4↑p
PAGE:00000001407105D0 // HaliInitPowerManagement+B4↓p ...
PAGE:00000001407105D0
PAGE:00000001407105D0 Object = qword ptr -90h
PAGE:00000001407105D0 HandleInformation= qword ptr -88h
PAGE:00000001407105D0 var_80 = qword ptr -80h
PAGE:00000001407105D0 var_78 = xmmword ptr -78h
PAGE:00000001407105D0 var_68 = xmmword ptr -68h
PAGE:00000001407105D0 var_58 = xmmword ptr -58h
PAGE:00000001407105D0 var_20 = byte ptr -20h
PAGE:00000001407105D0 Handle = qword ptr -10h
PAGE:00000001407105D0
PAGE:00000001407105D0 // FUNCTION CHUNK AT PAGE:0000000140830C70 SIZE 00000007 BYTES
PAGE:00000001407105D0
PAGE:00000001407105D0 mov rax, rsp
PAGE:00000001407105D3 mov [rax+8], rbx
PAGE:00000001407105D7 mov [rax+18h], rsi
PAGE:00000001407105DB mov [rax+20h], rdi
PAGE:00000001407105DF push rbp
PAGE:00000001407105E0 push r12
PAGE:00000001407105E2 push r13
PAGE:00000001407105E4 push r14
PAGE:00000001407105E6 push r15
PAGE:00000001407105E8 lea rbp, [rax-5Fh]
PAGE:00000001407105EC sub rsp, 90h
PAGE:00000001407105F3 mov rsi, gs:_KPCR.Prcb.CurrentThread
PAGE:00000001407105FC xor r13d, r13d
PAGE:00000001407105FF movups xmm1, xmmword ptr [rdx+10h] // //0x30 bytes (sizeof)
PAGE:00000001407105FF // struct _OBJECT_ATTRIBUTES
PAGE:00000001407105FF // {
PAGE:00000001407105FF // ULONG Length// //0x0
PAGE:00000001407105FF // VOID* RootDirectory// //0x8
PAGE:00000001407105FF // struct _UNICODE_STRING* ObjectName// //0x10
PAGE:00000001407105FF // ULONG Attributes// //0x18
PAGE:00000001407105FF // VOID* SecurityDescriptor// //0x20
PAGE:00000001407105FF // VOID* SecurityQualityOfService// //0x28
PAGE:00000001407105FF // }//
PAGE:0000000140710603 mov r12b, r9b // AllowMultipleCallbacks
PAGE:0000000140710606 mov r14b, r8b // Create
PAGE:0000000140710609 movups xmm0, xmmword ptr [rdx]
PAGE:000000014071060C mov r15, rcx // CallbackObject
PAGE:000000014071060F mov edi, r13d
PAGE:0000000140710612 movups [rbp+7Fh+var_68], xmm1 // ObjectName
PAGE:0000000140710616 mov [rbp+7Fh+Handle], r13
PAGE:000000014071061A psrldq xmm1, 8
PAGE:000000014071061F movd eax, xmm1
PAGE:0000000140710623 movups [rbp+7Fh+var_78], xmm0
PAGE:0000000140710627 mov [rbp+7Fh+HandleInformation], r13
PAGE:000000014071062B movups xmm0, xmmword ptr [rdx+20h]
PAGE:000000014071062F bts eax, 9
PAGE:0000000140710633 mov dword ptr [rbp+7Fh+var_68+8], eax
PAGE:0000000140710636 movups [rbp+7Fh+var_58], xmm0
PAGE:000000014071063A cmp qword ptr [rbp+7Fh+var_68], r13
PAGE:000000014071063E jz loc_1407107AF // 不能使用没有名称的回调对象
PAGE:000000014071063E // STATUS_UNSUCCESSFUL
PAGE:0000000140710644 mov rdx, cs:ExCallbackObjectType
PAGE:000000014071064B lea rax, [rbp+7Fh+Handle]
PAGE:000000014071064F mov [rsp+0B0h+var_80], rax
PAGE:0000000140710654 lea rcx, [rbp+7Fh+var_78]
PAGE:0000000140710658 mov [rsp+0B0h+HandleInformation], r13
PAGE:000000014071065D xor r9d, r9d
PAGE:0000000140710660 xor r8d, r8d
PAGE:0000000140710663 mov dword ptr [rsp+0B0h+Object], r13d
PAGE:0000000140710668 call ObOpenObjectByName // 通过对象名称获取对象句柄
PAGE:000000014071066D mov rdi, [rbp+7Fh+Handle]
PAGE:0000000140710671 mov ebx, eax // 返回的状态码
PAGE:0000000140710673
PAGE:0000000140710673 loc_140710673: // CODE XREF: ExCreateCallback+1E4↓j
PAGE:0000000140710673 test ebx, ebx
PAGE:0000000140710675 jns short loc_140710680 // 说明回调对象已存在,则为打开对象
PAGE:0000000140710675 // 否则不存在回调对象,则应该根据参数 Create 来看是否创建对象
PAGE:0000000140710677 test r14b, r14b
PAGE:000000014071067A jnz short loc_1407106DF // 创建回调对象
PAGE:000000014071067C
PAGE:000000014071067C loc_14071067C: // CODE XREF: ExCreateCallback+1DA↓j
PAGE:000000014071067C test ebx, ebx
PAGE:000000014071067E js short loc_1407106BB
PAGE:0000000140710680
PAGE:0000000140710680 loc_140710680: // CODE XREF: ExCreateCallback+A5↑j
PAGE:0000000140710680 mov r8, cs:ExCallbackObjectType // ObjectType
PAGE:0000000140710687 lea rax, [rbp+7Fh+var_80]
PAGE:000000014071068B mov [rsp+0B0h+HandleInformation], r13 // HandleInformation
PAGE:0000000140710690 xor r9d, r9d // AccessMode
PAGE:0000000140710693 xor edx, edx // DesiredAccess
PAGE:0000000140710695 mov [rsp+0B0h+Object], rax // Object
PAGE:000000014071069A mov rcx, rdi // Handle
PAGE:000000014071069D mov [rbp+7Fh+var_80], r13
PAGE:00000001407106A1 call ObReferenceObjectByHandle
PAGE:00000001407106A6 mov rcx, rdi // Handle
PAGE:00000001407106A9 mov ebx, eax
PAGE:00000001407106AB call ZwClose
PAGE:00000001407106B0 test ebx, ebx
PAGE:00000001407106B2 js short loc_1407106BB
PAGE:00000001407106B4 mov rax, [rbp+7Fh+var_80]
PAGE:00000001407106B8 mov [r15], rax
PAGE:00000001407106BB
PAGE:00000001407106BB loc_1407106BB: // CODE XREF: ExCreateCallback+AE↑j
PAGE:00000001407106BB // ExCreateCallback+E2↑j ...
PAGE:00000001407106BB lea r11, [rsp+0B0h+var_20]
PAGE:00000001407106C3 mov eax, ebx
PAGE:00000001407106C5 mov rbx, [r11+30h]
PAGE:00000001407106C9 mov rsi, [r11+40h]
PAGE:00000001407106CD mov rdi, [r11+48h]
PAGE:00000001407106D1 mov rsp, r11
PAGE:00000001407106D4 pop r15
PAGE:00000001407106D6 pop r14
PAGE:00000001407106D8 pop r13
PAGE:00000001407106DA pop r12
PAGE:00000001407106DC pop rbp
PAGE:00000001407106DD retn
PAGE:00000001407106DD // ---------------------------------------------------------------------------
PAGE:00000001407106DE db 0CCh
PAGE:00000001407106DF // ---------------------------------------------------------------------------
PAGE:00000001407106DF
PAGE:00000001407106DF loc_1407106DF: // CODE XREF: ExCreateCallback+AA↑j
PAGE:00000001407106DF mov rdx, cs:ExCallbackObjectType // 创建回调对象
PAGE:00000001407106E6 lea rax, [rbp+7Fh+HandleInformation]
PAGE:00000001407106EA mov qword ptr [rsp+0B0h+var_68], r13
PAGE:00000001407106EF lea r8, [rbp+7Fh+var_78]
PAGE:00000001407106F3 mov qword ptr [rsp+0B0h+var_78+8], rax
PAGE:00000001407106F8 xor r9d, r9d
PAGE:00000001407106FB mov dword ptr [rsp+0B0h+var_78], r13d
PAGE:0000000140710700 xor ecx, ecx
PAGE:0000000140710702 mov dword ptr [rsp+0B0h+var_80], r13d
PAGE:0000000140710707 mov dword ptr [rsp+0B0h+HandleInformation], 38h
PAGE:000000014071070F call ObCreateObjectEx
PAGE:0000000140710714 mov ebx, eax
PAGE:0000000140710716 test eax, eax
PAGE:0000000140710718 js short loc_1407106BB // 回调对象创建失败
PAGE:000000014071071A mov rbx, [rbp+7Fh+HandleInformation] // typedef _CALLBACK_OBJECT
PAGE:000000014071071A // {
PAGE:000000014071071A // ULONG Signature,
PAGE:000000014071071A // KSPIN_LOCK Lock, // ULONG
PAGE:000000014071071A // LIST_ENTRY RegisteredCallbacks,
PAGE:000000014071071A // BOOLEAN AllowMultipleCallbacks,
PAGE:000000014071071A // UCHAR reserved [3]
PAGE:000000014071071A // }CALLBACK_OBJECT, *PCALLBACK_OBJECT//
PAGE:000000014071071E mov dword ptr [rbx+_CALLBACK_OBJECT.Signature], 'llaC'
PAGE:0000000140710724 lea rax, [rbx+_CALLBACK_OBJECT.RegisteredCallbacks] // rax = &RegisteredCallbacks.Flink
PAGE:0000000140710728 mov byte ptr [rbx+_CALLBACK_OBJECT.AllowMultipleCallbacks], r12b
PAGE:000000014071072C mov [rax+LIST_ENTRY.Blink], rax
PAGE:0000000140710730 mov [rax+LIST_ENTRY.Flink], rax
PAGE:0000000140710733 mov [rbx+_CALLBACK_OBJECT.Lock], r13
PAGE:0000000140710737 dec word ptr [rsi+1E6h] // WaitBlockFill11[x]
PAGE:000000014071073E xor edx, edx // BugCheckParameter1
PAGE:0000000140710740 lea rcx, ExpCallbackListLock // BugCheckParameter2
PAGE:0000000140710747 call ExAcquirePushLockExclusiveEx
PAGE:000000014071074C mov rcx, cs:qword_140C19288
PAGE:0000000140710753 lea rdx, ExpCallbackListHead
PAGE:000000014071075A lea rax, [rbx+_CALLBACK_OBJECT.reserved[3]]
PAGE:000000014071075E cmp [rcx], rdx
PAGE:0000000140710761 jnz FatalListEntryError_99
PAGE:0000000140710767 mov [rax+8], rcx
PAGE:000000014071076B mov [rax], rdx // 0
PAGE:000000014071076E mov [rcx], rax
PAGE:0000000140710771 mov rcx, rsi // CurrentThread
PAGE:0000000140710774 mov cs:qword_140C19288, rax
PAGE:000000014071077B call ExpUnlockCallbackListExclusive
PAGE:0000000140710780 xor r9d, r9d
PAGE:0000000140710783 lea rax, [rbp+7Fh+Handle]
PAGE:0000000140710787 mov [rsp+0B0h+var_80], rax
PAGE:000000014071078C xor edx, edx
PAGE:000000014071078E mov [rsp+0B0h+HandleInformation], r13
PAGE:0000000140710793 mov rcx, rbx
PAGE:0000000140710796 mov dword ptr [rsp+0B0h+Object], r13d
PAGE:000000014071079B lea r8d, [r9+1]
PAGE:000000014071079F call ObInsertObjectEx
PAGE:00000001407107A4 mov rdi, [rbp+7Fh+Handle]
PAGE:00000001407107A8 mov ebx, eax
PAGE:00000001407107AA jmp loc_14071067C
PAGE:00000001407107AF // ---------------------------------------------------------------------------
PAGE:00000001407107AF
PAGE:00000001407107AF loc_1407107AF: // CODE XREF: ExCreateCallback+6E↑j
PAGE:00000001407107AF mov ebx, 0C0000001h // 不能使用没有名称的回调对象
PAGE:00000001407107AF // STATUS_UNSUCCESSFUL
PAGE:00000001407107B4 jmp loc_140710673
PAGE:00000001407107B4 ExCreateCallback endp

2 注册表回调

本文先不研究注册表对象的创建、初始化等(CmpCreateObjectTypes/CmpInitCallbacks/CmpDeleteKeyObject)。

注册表操作的通知实际意义上不是上一篇文章进程、线程、模块通知类型,因为没有使用到 EX_CALLBACK、EX_CALLBACK_ROUTINE_BLOCK 结构来描述通知例程。所以我将其归为回调类操作。

如下图所示,对注册表回调操作的流程。

12.png

2.1 创建/删除回调例程

使用 CmRegisterCallback 安装注册表回调函数,从 Vista 开始将使用 CmRegisterCallbackEx 函数(不推荐使用前者),因为参数内容更丰富。本文主要分析 CmRegisterCallbackEx。可以看到:这两个函数所创建的回调例程都是 PEX_CALLBACK_FUNCTION 类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
NTSTATUS CmRegisterCallback(
[in] PEX_CALLBACK_FUNCTION Function,
[in, optional] PVOID Context,
[out] PLARGE_INTEGER Cookie
);
NTSTATUS CmRegisterCallbackEx(
[in] PEX_CALLBACK_FUNCTION Function,// 指向注册表回调例程,这个参数就是用来监控注册表操作的回调函数
[in] PUNICODE_STRING Altitude,// 回调函数被调用优先级(顺序)
[in] PVOID Driver, // 指向当前驱动程序 DRIVER_OBJECT 结构的指针
[in, optional] PVOID Context, // 是给回调函数的一个参数指针
[out] PLARGE_INTEGER Cookie, // 输出值,记录回调函数传递句柄的信息,用一个全局变量保存下来,取消回调时使用
PVOID Reserved // 保留
);

返回值:

  • 创建成功:STATUS_SUCCESS。

  • 创建失败:返回的 STATUS 是以下之一。

    返回代码 说明
    STATUS_FLT_INSTANCE_ALTITUDE_COLLISION 调用驱动程序或其他驱动程序已经为指定的高度Altitude注册了 RegistryCallback 例程。
    STATUS_INSUFFICIENT_RESOURCES 尝试分配内存失败。

删除回调例程的函数是 CmUnRegisterCallback:

1
2
3
NTSTATUS CmUnRegisterCallback(
[in] LARGE_INTEGER Cookie
);

返回值:

  • 成功:STATUS_SUCCESS。
  • 如果 Cookie 与已安装的注册表回调例程不匹配,则返回 STATUS_INVALID_PARAMETER。
  • 其他 NTSTATUS 错误代码。

2.2 注册表回调例程

注册表回调例程可以监视、阻止或修改注册表操作,原型如下:

1
2
3
4
5
6
NTSTATUS ExCallbackFunction(
[in] PVOID CallbackContext, // CmRegisterCallback/CmRegisterCallbackEx 传来的参数
[in, optional] PVOID Argument1, // 表示注册表的操作类型,是一个 REG_NOTIFY_CLASS 类型的枚举值
[in, optional] PVOID Argument2 // 表示操作类型信息结构。根据 Argument1 参数指定的操作类型,都有一个对应的操作类型信息结构
)
{...}

Argument2 指向一个结构,该结构的类型由 Argument1 决定,是有对应关系的,如下图(具体见 ExCallbackFunction)。

11.png

其中的几个比较常用的类型,它们的意义,以及对应的 Argument2 的结构体的内容如下,都是 REG_XXX_KEY_INFORMATION 数据结构:

REG_NOTIFY_CLASS的值 意义 Argument2结构体
RegNtPreCreateKey 创建注册表之前 PREG_CREATE_KEY_INFORMATION
RegNtPreOpenKey 打开注册表之前 PREG_CREATE_KEY_INFORMATION
RegNtPreDeleteKey 删除键之前 PREG_DELETE_KEY_INFORMATION
RegNtPreDeleteValueKey 删除键值之前 PREG_DELETE_VALUE_KEY_INFORMATION
RegNtPreSetValueKey 修改键值之前 PREG_SET_VALUE_KEY_INFORMATION

返回值:

  • Vista之前:该函数的返回值为 STATUS_SUCCESS 之外的错误码的时候,则表示系统会拒绝操作相应的注册表。
  • Vista及之后:增加了 STATUS_CALLBACK_BYPASS 返回值,可以使配置管理器停止处理这个注册表请求,并且返回 STATUS_SUCCESS 给调用线程。

注意,对注册表的每种操作都有操作前(Pre)调用回调例程,和操作后(Post)调用回调例程。如

  • RegNtPreOpenKey 表示打开指定注册表项前调用回调例程。
  • RegNtPostOpenKey 表示打开指定注册表项后调用回调例程。

我们一般使用操作前的 REG_NOTIFY_CLASS,方便做拦截。在回调例程中直接返回 STATUS_ACCESS_DENIED 即可。

2.3 函数逆向分析

CmRegisterCallback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PAGE:00000001407C64E0 ; NTSTATUS __stdcall CmRegisterCallback(PEX_CALLBACK_FUNCTION Function, PVOID Context, PLARGE_INTEGER Cookie)
PAGE:00000001407C64E0 public CmRegisterCallback
PAGE:00000001407C64E0 CmRegisterCallback proc near ; DATA XREF: .pdata:000000014010F134↑o
PAGE:00000001407C64E0
PAGE:00000001407C64E0 var_18 = byte ptr -18h
PAGE:00000001407C64E0 var_10 = qword ptr -10h
PAGE:00000001407C64E0
PAGE:00000001407C64E0 sub rsp, 38h
PAGE:00000001407C64E4 mov [rsp+38h+var_10], r8
PAGE:00000001407C64E9 mov r9b, 1
PAGE:00000001407C64EC lea r8, CmLegacyAltitude
PAGE:00000001407C64F3 mov [rsp+38h+var_18], 0
PAGE:00000001407C64F8 call CmpRegisterCallbackInternal
PAGE:00000001407C64FD add rsp, 38h
PAGE:00000001407C6501 retn
PAGE:00000001407C6501 ; ---------------------------------------------------------------------------
PAGE:00000001407C6502 db 0CCh
PAGE:00000001407C6502 CmRegisterCallback endp

从上面的代码分析来看,函数还原如下:

1
2
3
4
NTSTATUS __stdcall CmRegisterCallback(PEX_CALLBACK_FUNCTION Function, PVOID Context, PLARGE_INTEGER Cookie)
{
return CmpRegisterCallbackInternal(Function, Context, &CmLegacyAltitude, 1, 0, Cookie);
}

注册表回调结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
PREGISTRY_CALLBACK Callback;

typedef struct _REGISTRY_CALLBACK
{
0x0 LIST_ENTRY ListEntry;
0x10 EX_RUNDOWN_REF RundownRef;
0x18 LARGE_INTEGER Cookie;
0x20 PVOID Context;
0x28 PEX_CALLBACK_FUNCTION Function;
0x30 UCHAR PendingAltitude[8];
0x38 PVOID AllocateAltitudeReturnValue;
0x40 LIST_ENTRY ListEntry2;
} REGISTRY_CALLBACK, *PREGISTRY_CALLBACK;

CmRegisterCallbackEx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/*
NTSTATUS CmRegisterCallbackEx(
[in] PEX_CALLBACK_FUNCTION Function,// 指向注册表回调例程,这个参数就是用来监控注册表操作的回调函数
[in] PCUNICODE_STRING Altitude,// minifilter 驱动中注册表回调函数的调用优先级
[in] PVOID Driver, // 指向当前驱动程序 DRIVER_OBJECT 结构的指针
[in, optional] PVOID Context, // 是给回调函数的一个参数指针
[out] PLARGE_INTEGER Cookie, // 输出值,记录回调函数传递句柄的信息,用一个全局变量保存下来,取消回调时使用
PVOID Reserved // 保留
);
*/

PAGE:00000001408671B0 public CmRegisterCallbackEx
PAGE:00000001408671B0 CmRegisterCallbackEx proc near ; CODE XREF: EtwpRegTraceEnableCallback+71↓p
PAGE:00000001408671B0 ; DATA XREF: .pdata:000000014011AEF4↑o
PAGE:00000001408671B0
PAGE:00000001408671B0 var_18 = byte ptr -18h
PAGE:00000001408671B0 var_10 = qword ptr -10h
PAGE:00000001408671B0 arg_20 = qword ptr 28h
PAGE:00000001408671B0 arg_28 = qword ptr 30h
PAGE:00000001408671B0
PAGE:00000001408671B0 sub rsp, 38h
PAGE:00000001408671B4 mov r10, r9
PAGE:00000001408671B7 test r8, r8
PAGE:00000001408671BA jnz short loc_1408671C3
PAGE:00000001408671BC mov eax, 0C00000F1h
PAGE:00000001408671C1 jmp short loc_1408671EF
PAGE:00000001408671C3 ; ---------------------------------------------------------------------------
PAGE:00000001408671C3
PAGE:00000001408671C3 loc_1408671C3: ; CODE XREF: CmRegisterCallbackEx+A↑j
PAGE:00000001408671C3 cmp [rsp+38h+arg_28], 0
PAGE:00000001408671C9 jz short loc_1408671D2
PAGE:00000001408671CB mov eax, 0C00000F4h
PAGE:00000001408671D0 jmp short loc_1408671EF
PAGE:00000001408671D2 ; ---------------------------------------------------------------------------
PAGE:00000001408671D2
PAGE:00000001408671D2 loc_1408671D2: ; CODE XREF: CmRegisterCallbackEx+19↑j
PAGE:00000001408671D2 mov rax, [rsp+38h+arg_20]
PAGE:00000001408671D7 mov r8, rdx
PAGE:00000001408671DA mov [rsp+38h+var_10], rax
PAGE:00000001408671DF xor r9d, r9d
PAGE:00000001408671E2 mov rdx, r10
PAGE:00000001408671E5 mov [rsp+38h+var_18], 0
PAGE:00000001408671EA call CmpRegisterCallbackInternal
PAGE:00000001408671EF
PAGE:00000001408671EF loc_1408671EF: ; CODE XREF: CmRegisterCallbackEx+11↑j
PAGE:00000001408671EF ; CmRegisterCallbackEx+20↑j
PAGE:00000001408671EF add rsp, 38h
PAGE:00000001408671F3 retn
PAGE:00000001408671F3 ; ---------------------------------------------------------------------------
PAGE:00000001408671F4 db 0CCh
PAGE:00000001408671F4 CmRegisterCallbackEx endp

函数还原如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
NTSTATUS CmRegisterCallbackEx(
[in] PEX_CALLBACK_FUNCTION Function,// 指向注册表回调例程,这个参数就是用来监控注册表操作的回调函数
[in] PCUNICODE_STRING Altitude,// minifilter 驱动中注册表回调函数的调用优先级
[in] PVOID Driver, // 指向当前驱动程序 DRIVER_OBJECT 结构的指针
[in, optional] PVOID Context, // 是给回调函数的一个参数指针
[out] PLARGE_INTEGER Cookie, // 输出值,记录回调函数传递句柄的信息,用一个全局变量保存下来,取消回调时使用
PVOID Reserved // 保留
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;

if(!Driver)
{
Status = 0xC00000F1; // STATUS_INVALID_PARAMETER_3(参数三错误)
return Status;
}
if(!Reserved)
{
Status = 0xC00000F4; // STATUS_INVALID_PARAMETER_6(参数六错误)
return Status;
}

Status = CmpRegisterCallbackInternal(Function, Context, Altitude, 0, 0, Cookie);
return Status;
}

CmpRegisterCallbackInternal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
PAGE:00000001406F3B98 // =============== S U B R O U T I N E =======================================
PAGE:00000001406F3B98
PAGE:00000001406F3B98 // NTSTATUS CmpRegisterCallbackInternal(Function, Context, Altitude, 0, 0, Cookie)
PAGE:00000001406F3B98
PAGE:00000001406F3B98 CmpRegisterCallbackInternal proc near // CODE XREF: CmRegisterInternalCallback+2E↑p
PAGE:00000001406F3B98 // CmRegisterCallback+18↓p ...
PAGE:00000001406F3B98
PAGE:00000001406F3B98 arg_0 = qword ptr 8
PAGE:00000001406F3B98 arg_8 = qword ptr 10h
PAGE:00000001406F3B98 arg_10 = qword ptr 18h
PAGE:00000001406F3B98 arg_18 = qword ptr 20h
PAGE:00000001406F3B98 arg_20 = byte ptr 28h
PAGE:00000001406F3B98 arg_28 = qword ptr 30h
PAGE:00000001406F3B98
PAGE:00000001406F3B98 // FUNCTION CHUNK AT PAGE:0000000140829AC2 SIZE 0000002F BYTES
PAGE:00000001406F3B98
PAGE:00000001406F3B98 mov rax, rsp
PAGE:00000001406F3B9B mov [rax+8], rbx
PAGE:00000001406F3B9F mov [rax+10h], rbp
PAGE:00000001406F3BA3 mov [rax+18h], rsi
PAGE:00000001406F3BA7 mov [rax+20h], rdi
PAGE:00000001406F3BAB push r14
PAGE:00000001406F3BAD sub rsp, 20h
PAGE:00000001406F3BB1 mov rbp, rdx
PAGE:00000001406F3BB4 mov rdi, r8
PAGE:00000001406F3BB7 mov edx, 50h // NumberOfBytes
PAGE:00000001406F3BBC mov r14, rcx
PAGE:00000001406F3BBF mov r8d, 'bcMC' // Tag
PAGE:00000001406F3BC5 mov sil, r9b // sil 是 rdi 低 8 位
PAGE:00000001406F3BC8 lea ecx, [rdx-4Fh] // PoolType
PAGE:00000001406F3BCB call ExAllocatePoolWithTag
PAGE:00000001406F3BD0 mov rbx, rax
PAGE:00000001406F3BD3 test rax, rax
PAGE:00000001406F3BD6 jz loc_140829AC2
PAGE:00000001406F3BDC xor edx, edx // Val
PAGE:00000001406F3BDE mov rcx, rax // Dst
PAGE:00000001406F3BE1 lea r8d, [rdx+50h] // Size
PAGE:00000001406F3BE5 call memset
PAGE:00000001406F3BEA lea rax, [rbx+REGISTRY_CALLBACK.ListEntry2]
PAGE:00000001406F3BEE mov [rbx+REGISTRY_CALLBACK.ListEntry.Blink], rbx
PAGE:00000001406F3BF2 mov [rax+_LIST_ENTRY.Blink], rax
PAGE:00000001406F3BF6 mov ecx, 1 // PoolType
PAGE:00000001406F3BFB mov [rax+_LIST_ENTRY.Flink], rax
PAGE:00000001406F3BFE mov r8d, 'acMC' // Tag
PAGE:00000001406F3C04 movzx eax, [rsp+28h+arg_20] // eax = 参数五 = 0
PAGE:00000001406F3C09 xor eax, dword ptr [rbx+(REGISTRY_CALLBACK.RundownRef._union_1+4)] // 0
PAGE:00000001406F3C0C mov [rbx+REGISTRY_CALLBACK.ListEntry.Flink], rbx
PAGE:00000001406F3C0F and eax, 1
PAGE:00000001406F3C12 xor dword ptr [rbx+(REGISTRY_CALLBACK.RundownRef._union_1+4)], eax
PAGE:00000001406F3C15 mov dword ptr [rbx+REGISTRY_CALLBACK.RundownRef._union_1], 0
PAGE:00000001406F3C1C mov [rbx+REGISTRY_CALLBACK.Context], rbp // 参数 2:Context
PAGE:00000001406F3C20 mov [rbx+REGISTRY_CALLBACK.Function], r14 // 参数 1 :Function
PAGE:00000001406F3C24 movzx eax, word ptr [rdi] // 参数 3:Altitude
PAGE:00000001406F3C27 mov word ptr [rbx+(REGISTRY_CALLBACK.PendingAltitude+2)], ax
PAGE:00000001406F3C2B mov word ptr [rbx+REGISTRY_CALLBACK.PendingAltitude], ax
PAGE:00000001406F3C2F movzx edx, word ptr [rdi] // NumberOfBytes
PAGE:00000001406F3C32 call ExAllocatePoolWithTag // 申请的大小为 Altitude->Lenth
PAGE:00000001406F3C37 mov [rbx+REGISTRY_CALLBACK.AllocateReturnValue], rax
PAGE:00000001406F3C3B test rax, rax
PAGE:00000001406F3C3E jz loc_140829ACC // 0xC000009A,STATUS_INSUFFICIENT_RESOURCES
PAGE:00000001406F3C3E // 内存申请失败
PAGE:00000001406F3C44 movzx r8d, word ptr [rdi] // Size
PAGE:00000001406F3C48 mov rcx, rax // Dst
PAGE:00000001406F3C4B mov rdx, [rdi+8] // Src
PAGE:00000001406F3C4F call memmove
PAGE:00000001406F3C54 mov dl, sil
PAGE:00000001406F3C57 mov rcx, rbx
PAGE:00000001406F3C5A call CmpInsertCallbackInListByAltitude
PAGE:00000001406F3C5F mov rcx, [rsp+28h+arg_28] // Cookie
PAGE:00000001406F3C64 mov edi, eax
PAGE:00000001406F3C66 mov rdx, qword ptr [rbx+REGISTRY_CALLBACK.Cookie]
PAGE:00000001406F3C6A mov [rcx], rdx
PAGE:00000001406F3C6D test eax, eax
PAGE:00000001406F3C6F js loc_140829AD1
PAGE:00000001406F3C75
PAGE:00000001406F3C75 loc_1406F3C75: // CODE XREF: CmpRegisterCallbackInternal+135F54↓j
PAGE:00000001406F3C75 mov eax, edi
PAGE:00000001406F3C77
PAGE:00000001406F3C77 loc_1406F3C77: // CODE XREF: CmpRegisterCallbackInternal+135F2F↓j
PAGE:00000001406F3C77 mov rbx, [rsp+28h+arg_0]
PAGE:00000001406F3C7C mov rbp, [rsp+28h+arg_8]
PAGE:00000001406F3C81 mov rsi, [rsp+28h+arg_10]
PAGE:00000001406F3C86 mov rdi, [rsp+28h+arg_18]
PAGE:00000001406F3C8B add rsp, 20h
PAGE:00000001406F3C8F pop r14
PAGE:00000001406F3C91 retn
PAGE:00000001406F3C91 // ---------------------------------------------------------------------------
PAGE:00000001406F3C92 db 0CCh
PAGE:00000001406F3C92 CmpRegisterCallbackInternal endp

函数还原得:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
NTSTATUS CmpRegisterCallbackInternal
(Function, Context, Altitude/*UNICODE_STRING*/, 0/*ForRundownRef*/, 0, Cookie)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PREGISTRY_CALLBACK pCmpCallback = NULL;
PVOID TempAlloc = NULL;

Altitude->Length = *(PUCHAR)ForRundownRef;

pCmpCallback = (PREGISTRY_CALLBACK)ExAllocatePoolWithTag(PagedPool, 0x50, 'bcMC');
if(!pCmpCallback)
{
Status = 0xC000009A; // STATUS_INSUFFICIENT_RESOURCES
return Status;
}

memset(pCmpCallback, 0, 0x50);

pCmpCallback->ListEntry.Flink = &pCmpCallback->ListEntry;
pCmpCallback->ListEntry2.Flink = &pCmpCallback->ListEntry2;
pCmpCallback->ListEntry2.Blink = &pCmpCallback->ListEntry2;
ForRundownRef ^= pCmpCallback->RundownRef.HighDword;
ForRundownRef &= 1;
pCmpCallback->RundownRef.HighDword ^= ForRundownRef;
pCmpCallback->RundownRef.LowDword = 0;
pCmpCallback->Context = Context;
pCmpCallback->Function = Function;
pCmpCallback->PendingAltitude[0] = *(PWORD)Altitude->Length;
pCmpCallback->PendingAltitude[2] = *(PWORD)Altitude->Length;

pCmpCallback->AllocateReturnValue = ExAllocatePoolWithTag(PagedPool, Altitude->Length, 'acMC');
if(!pCmpCallback->AllocateReturnValue)
{
Status = 0xC000009A; // STATUS_INSUFFICIENT_RESOURCES, 内存申请失败
return Status;
}

memmove(pCmpCallback->AllocateReturnValue, Altitude->Buffer, Altitude->Length);
Status = CmpInsertCallbackInListByAltitude(pCmpCallback, *(PUCHAR)Altitude->Length/*0/1*/);
*Cookie = pCmpCallback->Cookie;

return Status;
}

REGISTRY_CALLBACK 结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
PREGISTRY_CALLBACK Callback;

typedef struct _REGISTRY_CALLBACK
{
0x0 LIST_ENTRY ListEntry;
0x10 EX_RUNDOWN_REF RundownRef;
0x18 LARGE_INTEGER Cookie;
0x20 PVOID Context;
0x28 PEX_CALLBACK_FUNCTION Function;
0x30 UCHAR PendingAltitude[8];
0x38 PVOID AllocateAltitudeReturnValue;
0x40 LIST_ENTRY ListEntry2;
} REGISTRY_CALLBACK, *PREGISTRY_CALLBACK;

POOL_TYPE 的枚举值如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//0x4 bytes (sizeof)
enum _POOL_TYPE
{
NonPagedPool = 0,
NonPagedPoolExecute = 0,
PagedPool = 1,
NonPagedPoolMustSucceed = 2,
DontUseThisType = 3,
NonPagedPoolCacheAligned = 4,
PagedPoolCacheAligned = 5,
NonPagedPoolCacheAlignedMustS = 6,
MaxPoolType = 7,
NonPagedPoolBase = 0,
NonPagedPoolBaseMustSucceed = 2,
NonPagedPoolBaseCacheAligned = 4,
NonPagedPoolBaseCacheAlignedMustS = 6,
NonPagedPoolSession = 32,
PagedPoolSession = 33,
NonPagedPoolMustSucceedSession = 34,
DontUseThisTypeSession = 35,
NonPagedPoolCacheAlignedSession = 36,
PagedPoolCacheAlignedSession = 37,
NonPagedPoolCacheAlignedMustSSession = 38,
NonPagedPoolNx = 512,
NonPagedPoolNxCacheAligned = 516,
NonPagedPoolSessionNx = 544
};

CmpInsertCallbackInListByAltitude

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// CmpInsertCallbackInListByAltitude(pCmpCallback, *(PUCHAR)Altitude->Length/*0/1*/);
PAGE:00000001406F3C9C CmpInsertCallbackInListByAltitude proc near
PAGE:00000001406F3C9C // CODE XREF: CmpRegisterCallbackInternal+C2↑p
PAGE:00000001406F3C9C // DATA XREF: .rdata:0000000140086C40↑o ...
PAGE:00000001406F3C9C
PAGE:00000001406F3C9C arg_0 = qword ptr 8
PAGE:00000001406F3C9C arg_8 = qword ptr 10h
PAGE:00000001406F3C9C arg_10 = qword ptr 18h
PAGE:00000001406F3C9C
PAGE:00000001406F3C9C // FUNCTION CHUNK AT PAGE:0000000140829AF2 SIZE 00000024 BYTES
PAGE:00000001406F3C9C
PAGE:00000001406F3C9C mov [rsp+arg_0], rbx
PAGE:00000001406F3CA1 mov [rsp+arg_8], rbp
PAGE:00000001406F3CA6 mov [rsp+arg_10], rsi
PAGE:00000001406F3CAB push rdi
PAGE:00000001406F3CAC push r14
PAGE:00000001406F3CAE push r15
PAGE:00000001406F3CB0 sub rsp, 20h
PAGE:00000001406F3CB4 mov rax, gs:_KPCR.Prcb.CurrentThread
PAGE:00000001406F3CBD xor ebx, ebx
PAGE:00000001406F3CBF mov bpl, dl // bpl 为 rbp 的低 8 位,参数二
PAGE:00000001406F3CC2 mov rdi, rcx // rdi = 参数一 pCmpCallback
PAGE:00000001406F3CC5 dec word ptr [rax+1E4h] // KTHEREAD.KernelApcDisable
PAGE:00000001406F3CCC xor edx, edx // BugCheckParameter1
PAGE:00000001406F3CCE lea rcx, CmpCallbackListLock // BugCheckParameter2
PAGE:00000001406F3CD5 call ExAcquirePushLockExclusiveEx
PAGE:00000001406F3CDA mov rax, cs:CmpCallbackCookie
PAGE:00000001406F3CE1 lea r15, CallbackListHead
PAGE:00000001406F3CE8 inc rax
PAGE:00000001406F3CEB mov cs:CmpCallbackCookie, rax
PAGE:00000001406F3CF2 mov qword ptr [rdi+REGISTRY_CALLBACK.Cookie], rax
PAGE:00000001406F3CF6 mov rsi, cs:CallbackListHead // rsi = CallbackListHead->Flink
PAGE:00000001406F3CFD cmp rsi, r15
PAGE:00000001406F3D00 jz short loc_1406F3D29 // 如果是空链表,则将新节点插入到链表头
PAGE:00000001406F3D02
PAGE:00000001406F3D02 loc_1406F3D02: // CODE XREF: CmpInsertCallbackInListByAltitude+83↓j
PAGE:00000001406F3D02 lea rcx, [rsi+30h]
PAGE:00000001406F3D06 lea rdx, [rdi+30h]
PAGE:00000001406F3D0A call RtlCompareAltitudes
PAGE:00000001406F3D0F test eax, eax
PAGE:00000001406F3D11 jz loc_140829AF2
PAGE:00000001406F3D17 js short loc_1406F3D21
PAGE:00000001406F3D19
PAGE:00000001406F3D19 loc_1406F3D19: // CODE XREF: CmpInsertCallbackInListByAltitude+135E5B↓j
PAGE:00000001406F3D19 mov rsi, [rsi]
PAGE:00000001406F3D1C cmp rsi, r15
PAGE:00000001406F3D1F jnz short loc_1406F3D02
PAGE:00000001406F3D21
PAGE:00000001406F3D21 loc_1406F3D21: // CODE XREF: CmpInsertCallbackInListByAltitude+7B↑j
PAGE:00000001406F3D21 test eax, eax
PAGE:00000001406F3D23 jz loc_140829AFC
PAGE:00000001406F3D29
PAGE:00000001406F3D29 loc_1406F3D29: // CODE XREF: CmpInsertCallbackInListByAltitude+64↑j
PAGE:00000001406F3D29 // CmpInsertCallbackInListByAltitude+135E63↓j
PAGE:00000001406F3D29 mov rax, [rsi+8] // rax = CallbackListHead->Blink
PAGE:00000001406F3D2D mov rcx, [rax] // rcx = CallbackListHead->Blink->Flink
PAGE:00000001406F3D30 cmp [rcx+8], rax // 判断是否是空链表
PAGE:00000001406F3D34 jnz loc_140829B0F // 不是空链表,触发 0x29 号中断异常
PAGE:00000001406F3D3A mov [rdi], rcx
PAGE:00000001406F3D3D mov [rdi+8], rax
PAGE:00000001406F3D41 mov [rcx+8], rdi
PAGE:00000001406F3D45 mov [rax], rdi // 将新的节点插入到 CallbackListHead 链表头
PAGE:00000001406F3D48 lock inc cs:CmpCallBackCount
PAGE:00000001406F3D4F
PAGE:00000001406F3D4F loc_1406F3D4F: // CODE XREF: CmpInsertCallbackInListByAltitude+135E6E↓j
PAGE:00000001406F3D4F xor edx, edx // BugCheckParameter1
PAGE:00000001406F3D51 lea rcx, CmpCallbackListLock // BugCheckParameter2
PAGE:00000001406F3D58 call ExReleasePushLockEx
PAGE:00000001406F3D5D mov rcx, gs:_KPCR.Prcb.CurrentThread
PAGE:00000001406F3D66 call KeLeaveCriticalRegionThread
PAGE:00000001406F3D6B mov rbp, [rsp+38h+arg_8]
PAGE:00000001406F3D70 mov eax, ebx
PAGE:00000001406F3D72 mov rbx, [rsp+38h+arg_0]
PAGE:00000001406F3D77 mov rsi, [rsp+38h+arg_10]
PAGE:00000001406F3D7C add rsp, 20h
PAGE:00000001406F3D80 pop r15
PAGE:00000001406F3D82 pop r14
PAGE:00000001406F3D84 pop rdi
PAGE:00000001406F3D85 retn
PAGE:00000001406F3D85 // ---------------------------------------------------------------------------
PAGE:00000001406F3D86 db 0CCh
PAGE:00000001406F3D86 CmpInsertCallbackInListByAltitude endp

函数还原得:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
NTSTATUS CmpInsertCallbackInListByAltitude(pCmpCallback, Altitude/*0/1*/)
{
ULONG uRet = 0;
LARGE_INTEGER TempCookie = {0};
PREGISTRY_CALLBACK pTempCmpCallback = NULL;
LIST_ENTRY ListHead = NULL;

CurrentThread.KernelApcDisable -= 1;
ExAcquirePushLockExclusiveEx(&CmpCallbackListLock, 0);

TempCookie = CmpCallbackCookie;
ListHead = &CallbackListHead;
CmpCallbackCookie += 1;

pCmpCallback->Cookie = CmpCallbackCookie;

if(ListHead == ListHead->Flink) // 空链表
{
if(ListHead->Blink->Flink != &ListHead) int 0x29;
// 将新的节点插入到 CallbackListHead 链表尾

pCmpCallback->ListEntry->Flink = &ListHead;
pCmpCallback->ListEntry->Blink = ListHead->Blink;
ListHead->Blink = &pCmpCallback->ListEntry;
ListHead->Blink->Flink = &pCmpCallback->ListEntry;

CmpCallBackCount += 1;

Status = STATUS_SUCCESS;
goto ReadyReturn;
}else
{
do
{
pTempCmpCallback = (REGISTRY_CALLBACK)(CallbackListHead->Flink);
uRet = RtlCompareAltitudes(&(pTempCmpCallback->AllocateReturnValue),
&(pCmpCallback->AllocateReturnValue));
if(uRet == 0)
{
if(Altitude->Lenth == 0)
{
Status = 0xC01C0011;
goto ReadyReturn;
}
}
if(uRet < 0)
{
if(uRet == 0)
{
if(Altitude->Lenth == 0)
{
Status = 0xC01C0011;
goto ReadyReturn;
}else { goto IFInsert; }
}
}

}while(ListHead != ListHead->Flink)

IFInsert:
/* 如果是空链表,将新的节点插入到 CallbackListHead 链表尾 */
}

ReadyReturn:
ExReleasePushLockEx(&CmpCallbackListLock, 0);
KeLeaveCriticalRegionThread(&KTHREAD);
return Status;
}

CmUnRegisterCallback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
NTSTATUS __stdcall CmUnRegisterCallback(LARGE_INTEGER Cookie)
{
LARGE_INTEGER v1; // rbx
NTSTATUS v2; // esi
struct _KTHREAD *v3; // rax
char *v4; // rax
char *v5; // rdi
int v6; // eax
struct _KTHREAD *v7; // rax
_QWORD *v8; // rcx
void **v9; // rax
_QWORD *v10; // rdx
void **v11; // rax
char v12; // r15
struct _KTHREAD *v13; // rax
signed __int64 v14; // r13
_QWORD *v15; // rax
_QWORD *v16; // rbx
_QWORD *v17; // r13
_QWORD *v18; // r14
__int64 v19; // rdx
_QWORD *v20; // rcx
__int64 v21; // rcx
_QWORD *v22; // rax
_QWORD *v23; // rax
__int64 v24; // rax
__int64 v25; // rcx
_QWORD *v26; // rbx
struct _KTHREAD *v27; // rax
__int64 v28; // rbx
PSLIST_ENTRY v29; // rbx
PSLIST_ENTRY v30; // rcx
void *v31; // rcx
__int64 v33; // [rsp+38h] [rbp-80h]
char *v34; // [rsp+40h] [rbp-78h]
__int64 v35; // [rsp+48h] [rbp-70h]
char *v36; // [rsp+50h] [rbp-68h]
__int128 v37; // [rsp+58h] [rbp-60h]
__int128 v38; // [rsp+68h] [rbp-50h]
__int64 v39; // [rsp+78h] [rbp-40h]

v1 = Cookie;
v2 = -1073741811;
v38 = 0i64;
v39 = 0i64;
v37 = 0i64;
v33 = 0i64;
v3 = KeGetCurrentThread();
--LOWORD(v3->_union_108.WaitBlock[3].SpareLong);
ExAcquirePushLockExclusiveEx((ULONG_PTR)&CmpCallbackListLock, 0i64);
do
{
do
{
v4 = CmListGetNextElement((_QWORD **)&CallbackListHead, (_QWORD **)&v33, 0);
v5 = v4;
v34 = v4;
if ( !v4 )
goto LABEL_12;
}
while ( *((_QWORD *)v4 + 3) != v1.QuadPart );
v6 = *((_DWORD *)v4 + 4);
if ( !v6 )
{
v10 = *(_QWORD **)v5;
v11 = (void **)*((_QWORD *)v5 + 1);
if ( *(char **)(*(_QWORD *)v5 + 8i64) == v5 && *v11 == v5 )
{
*v11 = v10;
v10[1] = v11;
ExReleasePushLockEx((ULONG_PTR)&CmpCallbackListLock, 0i64);
KeLeaveCriticalRegionThread((__int64)KeGetCurrentThread());
v2 = 0;
goto LABEL_17;
}
LABEL_33:
__fastfail(3u);
}
}
while ( v6 & 0x80000000 );
_InterlockedOr((volatile signed __int32 *)v5 + 4, 0x80000000);
ExReleasePushLockEx((ULONG_PTR)&CmpCallbackListLock, 0i64);
KeLeaveCriticalRegionThread((__int64)KeGetCurrentThread());
while ( *((_DWORD *)v5 + 4) != 2147483648 )
ExBlockOnAddressPushLock((__int64)&CallbackListDeleteEvent);
v7 = KeGetCurrentThread();
--LOWORD(v7->_union_108.WaitBlock[3].SpareLong);
ExAcquirePushLockExclusiveEx((ULONG_PTR)&CmpCallbackListLock, 0i64);
v8 = *(_QWORD **)v5;
v9 = (void **)*((_QWORD *)v5 + 1);
if ( *(char **)(*(_QWORD *)v5 + 8i64) != v5 || *v9 != v5 )
goto LABEL_33;
*v9 = v8;
v8[1] = v9;
ExReleasePushLockEx((ULONG_PTR)&CmpCallbackListLock, 0i64);
KeLeaveCriticalRegionThread((__int64)KeGetCurrentThread());
v2 = 0;
LABEL_12:
if ( v2 < 0 )
{
ExReleasePushLockEx((ULONG_PTR)&CmpCallbackListLock, 0i64);
KeLeaveCriticalRegionThread((__int64)KeGetCurrentThread());
return v2;
}
LABEL_17:
*((_QWORD *)&v37 + 1) = &v37;
*(_QWORD *)&v37 = &v37;
v12 = 0;
v13 = KeGetCurrentThread();
--LOWORD(v13->_union_108.WaitBlock[3].SpareLong);
ExAcquirePushLockExclusiveEx((ULONG_PTR)&CmpContextListLock, 0i64);
v14 = (signed __int64)(v5 + 64);
v15 = (_QWORD *)*((_QWORD *)v5 + 8);
v16 = (_QWORD *)*((_QWORD *)v5 + 8);
v36 = v5 + 64;
while ( v16 != (_QWORD *)v14 )
{
v17 = (_QWORD *)*v15;
v18 = v15 - 2;
if ( (unsigned __int8)ObReferenceObjectSafe(v15[4]) )
{
v19 = v18[2];
v20 = (_QWORD *)v18[3];
if ( *(_QWORD **)(v19 + 8) != v18 + 2 )
goto LABEL_33;
if ( (_QWORD *)*v20 != v18 + 2 )
goto LABEL_33;
*v20 = v19;
*(_QWORD *)(v19 + 8) = v20;
v21 = *v18;
v22 = (_QWORD *)v18[1];
if ( *(_QWORD **)(*v18 + 8i64) != v18 )
goto LABEL_33;
if ( (_QWORD *)*v22 != v18 )
goto LABEL_33;
*v22 = v21;
*(_QWORD *)(v21 + 8) = v22;
v23 = (_QWORD *)*((_QWORD *)&v37 + 1);
if ( **((__int128 ***)&v37 + 1) != &v37 )
goto LABEL_33;
*v16 = &v37;
v16[1] = v23;
*v23 = v16;
*((_QWORD *)&v37 + 1) = v16;
}
else
{
v12 = 1;
}
v15 = v17;
v16 = v17;
v14 = (signed __int64)(v5 + 64);
}
ExReleasePushLockEx((ULONG_PTR)&CmpContextListLock, 0i64);
KeLeaveCriticalRegionThread((__int64)KeGetCurrentThread());
while ( 1 )
{
v24 = v37;
if ( (__int128 *)v37 == &v37 )
break;
v25 = *(_QWORD *)v37;
if ( *(__int128 **)(v37 + 8) != &v37 || *(_QWORD *)(v25 + 8) != (_QWORD)v37 )
goto LABEL_33;
*(_QWORD *)&v37 = *(_QWORD *)v37;
*(_QWORD *)(v25 + 8) = &v37;
v26 = (_QWORD *)(v24 - 16);
v35 = v24 - 16;
v38 = *(_OWORD *)(v24 - 16 + 48);
(*((void (__fastcall **)(_QWORD))v5 + 5))(*((_QWORD *)v5 + 4));
HalPutDmaAdapter(v26[6]);
ExFreePoolWithTag(v26, 0x63634D43u);
}
while ( v12 )
{
v27 = KeGetCurrentThread();
--LOWORD(v27->_union_108.WaitBlock[3].SpareLong);
ExAcquirePushLockExclusiveEx((ULONG_PTR)&CmpContextListLock, 0i64);
v28 = *((_QWORD *)v5 + 8);
ExReleasePushLockEx((ULONG_PTR)&CmpContextListLock, 0i64);
KeLeaveCriticalRegionThread((__int64)KeGetCurrentThread());
if ( v28 == v14 )
v12 = 0;
else
ExBlockOnAddressPushLock((__int64)&CallbackListDeleteEvent);
}
if ( _InterlockedExchangeAdd(&CmpCallBackCount, 0xFFFFFFFF) == 1 )
{
v29 = RtlpInterlockedFlushSList((PSLIST_HEADER)&CmpCallbackContextSList);
while ( v29 )
{
v30 = v29;
v29 = v29->Next;
ExFreePoolWithTag(v30, 0);
}
}
v31 = (void *)*((_QWORD *)v5 + 7);
if ( v31 )
ExFreePoolWithTag(v31, 0);
ExFreePoolWithTag(v5, 0);
return v2;
}

CmpCallCallBacks

1
2
3
4
__int64 __fastcall CmpCallCallBacks(int a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6)
{
return CmpCallCallBacksEx(a1, a2, 0i64, 1, a4, a5, a6);
}

CmpCallCallBacksEx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
__int64 __fastcall CmpCallCallBacksEx(unsigned int a1, __int64 a2, __int64 a3, char a4, unsigned int a5, __int64 a6, __int64 a7)
{
char v7; // r14
__int64 v8; // rsi
__int64 v9; // rbx
unsigned int v10; // er15
struct _KTHREAD *v11; // rax
void *v12; // rax
__int64 *v13; // r12
struct _KTHREAD *v14; // rax
_SLIST_ENTRY *v15; // r13
struct _KTHREAD *v16; // rcx
bool v17; // zf
struct _SLIST_ENTRY *v18; // rsi
__int64 v19; // rdx
struct _SLIST_ENTRY **v20; // rax
int v21; // ecx
signed int v22; // ecx
struct _KTHREAD *v23; // rcx
__int128 *v24; // r12
int v26; // er9
struct _SLIST_ENTRY *v27; // rbx
struct _SLIST_ENTRY **v28; // rax
__int64 v29; // r14
void **v30; // rax
unsigned int v31; // ecx
struct _KTHREAD *v32; // rax
_SLIST_ENTRY *v33; // rcx
struct _SLIST_ENTRY **v34; // rax
void **v35; // rax
__int64 v36; // rdx
signed int v37; // eax
int v38; // [rsp+0h] [rbp-108h]
char v39; // [rsp+20h] [rbp-E8h]
char v40; // [rsp+21h] [rbp-E7h]
int v41; // [rsp+24h] [rbp-E4h]
struct _SLIST_ENTRY *v42; // [rsp+30h] [rbp-D8h]
__int128 *v43; // [rsp+38h] [rbp-D0h]
__int64 *v44; // [rsp+40h] [rbp-C8h]
__int128 *v45; // [rsp+48h] [rbp-C0h]
struct _KTHREAD *v46; // [rsp+50h] [rbp-B8h]
__int64 v47; // [rsp+58h] [rbp-B0h]
__int128 v48; // [rsp+60h] [rbp-A8h]
__int128 v49; // [rsp+70h] [rbp-98h]
__int128 v50; // [rsp+80h] [rbp-88h]
__int64 v51; // [rsp+90h] [rbp-78h]
__int64 v52; // [rsp+98h] [rbp-70h]
__int64 v53; // [rsp+A0h] [rbp-68h]
__int128 v54; // [rsp+A8h] [rbp-60h]
__int64 v55; // [rsp+120h] [rbp+18h]

v55 = a3;
v7 = a4;
v8 = a3;
v9 = a2;
v10 = a1;
v47 = a3;
v53 = a2;
v52 = a6;
v41 = 0;
v48 = 0i64;
v49 = 0i64;
v50 = 0i64;
v51 = 0i64;
v54 = 0i64;
v43 = 0i64;
v45 = 0i64;
v39 = 0;
v11 = KeGetCurrentThread();
v46 = v11;
if ( a4 == 1 )
{
v12 = v11[1]._union_108.WaitBlock[0].SparePtr;
if ( v12 )
v13 = (__int64 *)*((_QWORD *)v12 + 1);
else
v13 = &CallbackListHead;
v14 = KeGetCurrentThread();
--LOWORD(v14->_union_108.WaitBlock[3].SpareLong);
ExAcquirePushLockSharedEx((ULONG_PTR)&CmpCallbackListLock, 0i64);
v15 = (_SLIST_ENTRY *)a7;
while ( 1 )
{
do
{
v13 = (__int64 *)*v13;
v44 = v13;
v40 = 1;
if ( v13 == &CallbackListHead )
goto LABEL_19;
}
while ( *((_DWORD *)v13 + 4) < 0 );
_InterlockedIncrement((volatile signed __int32 *)v13 + 4);
ExReleasePushLockEx((ULONG_PTR)&CmpCallbackListLock, 0i64);
v16 = KeGetCurrentThread();
v17 = LOWORD(v16->_union_108.WaitBlock[3].SpareLong)++ == -1;
if ( v17
&& (__2022111614201F003F8DAC811AF7E4C67 *)v16->_union_90.ApcState.ApcListHead[0].Flink != &v16->_union_90
&& !HIWORD(v16->_union_108.WaitBlock[3].SpareLong) )
{
KiCheckForKernelApcDelivery();
}
v40 = 0;
v18 = RtlpInterlockedPopEntrySList((PSLIST_HEADER)&CmpCallbackContextSList);
if ( !v18 )
v18 = (struct _SLIST_ENTRY *)ExAllocatePoolWithTag((POOL_TYPE)1, 0x30ui64, 0x69634D43u);
v42 = v18;
if ( !v18 )
break;
*((_QWORD *)&v18[1].Next + 1) = v13;
v18[2].Next = 0i64;
v19 = (__int64)v46;
v18[1].Next = (_SLIST_ENTRY *)v46[1]._union_108.WaitBlock[0].SparePtr;
*(_QWORD *)(v19 + 1432) = v18 + 1;
v20 = *(struct _SLIST_ENTRY ***)(a7 + 8);
if ( *v20 != (struct _SLIST_ENTRY *)a7 )
goto LABEL_90;
v18->Next = (_SLIST_ENTRY *)a7;
*((_QWORD *)&v18->Next + 1) = v20;
*v20 = v18;
*(_QWORD *)(a7 + 8) = v18;
CmpCallbackFillObjectContext(v10, (_QWORD *)v9, v13 + 3);
v21 = ((__int64 (__fastcall *)(__int64))v13[5])(v13[4]);
v41 = v21;
if ( v10 == 14 )
{
v41 = 0;
}
else if ( v21 < 0 )
{
v33 = v18->Next;
v34 = (struct _SLIST_ENTRY **)*((_QWORD *)&v18->Next + 1);
if ( *(&v18->Next->Next + 1) != v18 || *v34 != v18 )
LABEL_90:
__fastfail(3u);
*v34 = v33;
*((_QWORD *)&v33->Next + 1) = v34;
v35 = (void **)v46[1]._union_108.WaitBlock[0].SparePtr;
if ( v35 )
v46[1]._union_108.WaitBlock[0].SparePtr = *v35;
CmpFreeCallbackContext(v18);
if ( _InterlockedDecrement((volatile signed __int32 *)v13 + 4) == 0x80000000 )
{
_InterlockedOr(&v38, 0);
if ( CallbackListDeleteEvent )
ExfUnblockPushLock(&CallbackListDeleteEvent, 0i64);
}
v7 = 0;
v39 = 1;
LABEL_19:
v22 = v41;
goto LABEL_20;
}
switch ( v10 )
{
case 0x1Cu:
LABEL_47:
v18[2].Next = *(_SLIST_ENTRY **)(v9 + 80);
*(_QWORD *)(v9 + 80) = 0i64;
break;
case 8u:
LABEL_48:
v18[2].Next = *(_SLIST_ENTRY **)(v9 + 48);
*(_QWORD *)(v9 + 48) = 0i64;
break;
case 7u:
LABEL_45:
v18[2].Next = *(_SLIST_ENTRY **)(v9 + 40);
*(_QWORD *)(v9 + 40) = 0i64;
break;
case 0xEu:
LABEL_18:
v18[2].Next = *(_SLIST_ENTRY **)(v9 + 8);
*(_QWORD *)(v9 + 8) = 0i64;
break;
default:
switch ( v10 )
{
case 0u:
case 0x1Eu:
goto LABEL_18;
case 1u:
case 5u:
case 6u:
goto LABEL_45;
case 2u:
case 4u:
case 0x22u:
v18[2].Next = *(_SLIST_ENTRY **)(v9 + 16);
*(_QWORD *)(v9 + 16) = 0i64;
goto LABEL_46;
case 3u:
case 0x24u:
case 0x2Fu:
v18[2].Next = *(_SLIST_ENTRY **)(v9 + 32);
*(_QWORD *)(v9 + 32) = 0i64;
goto LABEL_46;
case 9u:
goto LABEL_48;
case 0xFu:
case 0x10u:
case 0x11u:
case 0x12u:
case 0x13u:
case 0x14u:
case 0x15u:
case 0x16u:
case 0x17u:
case 0x18u:
case 0x19u:
case 0x1Bu:
case 0x1Du:
case 0x1Fu:
case 0x21u:
case 0x23u:
case 0x25u:
case 0x27u:
case 0x2Au:
case 0x2Cu:
case 0x2Eu:
case 0x30u:
*(_QWORD *)(v9 + 32) = v18[2].Next;
goto LABEL_46;
case 0x1Au:
goto LABEL_47;
case 0x20u:
v18[2].Next = *(_SLIST_ENTRY **)(v9 + 64);
*(_QWORD *)(v9 + 64) = 0i64;
break;
case 0x26u:
case 0x29u:
case 0x2Bu:
case 0x2Du:
v18[2].Next = *(_SLIST_ENTRY **)(v9 + 24);
*(_QWORD *)(v9 + 24) = 0i64;
break;
default:
goto LABEL_46;
}
break;
}
LABEL_46:
v32 = KeGetCurrentThread();
--LOWORD(v32->_union_108.WaitBlock[3].SpareLong);
ExAcquirePushLockSharedEx((ULONG_PTR)&CmpCallbackListLock, 0i64);
}
if ( _InterlockedDecrement((volatile signed __int32 *)v13 + 4) == 0x80000000 )
{
_InterlockedOr(&v38, 0);
if ( CallbackListDeleteEvent )
ExfUnblockPushLock(&CallbackListDeleteEvent, 0i64);
}
v22 = -1073741670;
v41 = -1073741670;
v39 = 1;
v7 = 0;
LABEL_20:
if ( v40 )
{
ExReleasePushLockEx((ULONG_PTR)&CmpCallbackListLock, 0i64);
v23 = KeGetCurrentThread();
v17 = LOWORD(v23->_union_108.WaitBlock[3].SpareLong)++ == -1;
if ( v17
&& (__2022111614201F003F8DAC811AF7E4C67 *)v23->_union_90.ApcState.ApcListHead[0].Flink != &v23->_union_90
&& !HIWORD(v23->_union_108.WaitBlock[3].SpareLong) )
{
KiCheckForKernelApcDelivery();
}
v22 = v41;
}
v8 = v55;
}
else
{
v15 = (_SLIST_ENTRY *)a7;
v22 = v41;
}
if ( v7 )
{
v24 = v43;
}
else
{
if ( v39 == 1 )
{
v48 = 0i64;
v49 = 0i64;
v50 = 0i64;
v51 = 0i64;
v24 = &v48;
v43 = &v48;
v26 = a5;
if ( (a5 - 27) & 0xFFFFFFFD )
{
*(_QWORD *)&v48 = v52;
}
else
{
if ( v22 == -1073740541 )
v36 = **(_QWORD **)(v9 + 72);
else
v36 = 0i64;
*(_QWORD *)&v48 = v36;
}
if ( v22 == -1073740541 )
{
v37 = 0;
if ( !((v10 - 26) & 0xFFFFFFFD) )
v37 = *(_DWORD *)(v8 + 12);
DWORD2(v48) = v37;
}
else
{
DWORD2(v48) = v22;
v37 = v22;
}
DWORD2(v49) = v37;
*(_QWORD *)&v49 = v53;
if ( v8 )
{
v45 = &v54;
*(_QWORD *)&v54 = &v48;
*((_QWORD *)&v54 + 1) = v47;
}
}
else
{
v24 = (__int128 *)v9;
v43 = (__int128 *)v9;
v45 = (__int128 *)v8;
v26 = v10;
a5 = v10;
}
while ( v15->Next != v15 )
{
v27 = (struct _SLIST_ENTRY *)*((_QWORD *)&v15->Next + 1);
v47 = (__int64)v27;
v28 = (struct _SLIST_ENTRY **)*((_QWORD *)&v27->Next + 1);
if ( v27->Next != v15 || *v28 != v27 )
goto LABEL_90;
*((_QWORD *)&v15->Next + 1) = v28;
*v28 = v15;
v42 = (struct _SLIST_ENTRY *)*((_QWORD *)&v27[1].Next + 1);
v29 = (__int64)v42;
*((_QWORD *)v24 + 4) = v27[2].Next;
CmpCallbackFillObjectContext(v26, v24, (__int64 *)(v29 + 24));
v41 = (*(__int64 (__fastcall **)(_QWORD))(v29 + 40))(*(_QWORD *)(v29 + 32));
v30 = (void **)v46[1]._union_108.WaitBlock[0].SparePtr;
if ( v30 )
v46[1]._union_108.WaitBlock[0].SparePtr = *v30;
v31 = CmpCallBackCount * KeNumberProcessors_0;
if ( (unsigned int)(CmpCallBackCount * KeNumberProcessors_0) > 0x40 )
v31 = 64;
if ( (unsigned __int16)CmpCallbackContextSList >= v31 )
ExFreePoolWithTag(v27, 0);
else
RtlpInterlockedPushEntrySList((PSLIST_HEADER)&CmpCallbackContextSList, v27);
if ( _InterlockedDecrement((volatile signed __int32 *)(v29 + 16)) == 0x80000000 )
{
_InterlockedOr(&v38, 0);
if ( CallbackListDeleteEvent )
ExfUnblockPushLock(&CallbackListDeleteEvent, 0i64);
}
if ( v41 == -1073740541 )
*((_DWORD *)v24 + 2) = *((_DWORD *)v24 + 6);
v22 = 0;
v41 = 0;
v26 = a5;
}
}
if ( v39 == 1 )
{
if ( v24 )
{
v22 = *((_DWORD *)v24 + 6);
if ( v22 >= 0 )
v22 = -1073740541;
}
}
return (unsigned int)v22;
}

CmpEnumerateCallback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
PAGE:000000014076E620 CmpEnumerateCallback proc near          // DATA XREF: .rdata:000000014009B5E0↑o
PAGE:000000014076E620 // .pdata:000000014010B1F8↑o ...
PAGE:000000014076E620
PAGE:000000014076E620 var_28 = dword ptr -28h
PAGE:000000014076E620 arg_0 = qword ptr 8
PAGE:000000014076E620 arg_8 = qword ptr 10h
PAGE:000000014076E620 arg_10 = qword ptr 18h
PAGE:000000014076E620
PAGE:000000014076E620 // FUNCTION CHUNK AT PAGE:0000000140849506 SIZE 00000027 BYTES
PAGE:000000014076E620
PAGE:000000014076E620 mov [rsp+arg_0], rbx
PAGE:000000014076E625 mov [rsp+arg_8], rbp
PAGE:000000014076E62A mov [rsp+arg_10], rsi
PAGE:000000014076E62F push rdi
PAGE:000000014076E630 sub rsp, 20h
PAGE:000000014076E634 mov rax, gs:_KPCR.Prcb.CurrentThread
PAGE:000000014076E63D or ebp, 0FFFFFFFFh
PAGE:000000014076E640 mov rsi, rcx // rsi = pCmpCallback
PAGE:000000014076E643 add [rax+1E4h], bp // KernelApcDisable -= 1
PAGE:000000014076E64A xor edx, edx // BugCheckParameter1
PAGE:000000014076E64C lea rcx, CmpCallbackListLock // BugCheckParameter2
PAGE:000000014076E653 call ExAcquirePushLockSharedEx
PAGE:000000014076E658 mov rdx, [rsi+REGISTRY_CALLBACK.ListEntry.Flink]
PAGE:000000014076E65B lea r8, CallbackListHead
PAGE:000000014076E662 test rdx, rdx
PAGE:000000014076E665 mov rax, rdx
PAGE:000000014076E668 mov rcx, r8
PAGE:000000014076E66B cmovnz rcx, rdx
PAGE:000000014076E66F neg rax // if rax == 0, ZF = 0
PAGE:000000014076E66F // else ZF = 1
PAGE:000000014076E672 sbb rbx, rbx // rbx == 0/-1
PAGE:000000014076E675 and rbx, rdx // rbx = 0/REGISTRY_CALLBACK.ListEntry.Flink
PAGE:000000014076E678 mov rdx, [rcx] // rdx = CallbackListHead->Flink
PAGE:000000014076E67B xor edi, edi
PAGE:000000014076E67D
PAGE:000000014076E67D loc_14076E67D: // CODE XREF: CmpEnumerateCallback+C6↓j
PAGE:000000014076E67D cmp rdx, r8 // while 循环
PAGE:000000014076E67D // 判断是否是空链表
PAGE:000000014076E680 jz short loc_14076E694 // 空链表
PAGE:000000014076E682 mov eax, dword ptr [rdx+REGISTRY_CALLBACK.RundownRef._union_1.Count]
PAGE:000000014076E685 test eax, eax
PAGE:000000014076E687 js short loc_14076E6E3 // 如果当前节点RundownRef._union_1.Count <= 0
PAGE:000000014076E687 // 没有运行引用保护的话,则不处理当前节点,就开始处理下一个节点
PAGE:000000014076E689 lock inc dword ptr [rdx+10h]
PAGE:000000014076E68D mov rdi, [rdx+REGISTRY_CALLBACK.Function]
PAGE:000000014076E691 mov [rsi], rdx // 将该节点摘链??
PAGE:000000014076E694
PAGE:000000014076E694 loc_14076E694: // CODE XREF: CmpEnumerateCallback+60↑j
PAGE:000000014076E694 xor edx, edx // BugCheckParameter1
PAGE:000000014076E696 lea rcx, CmpCallbackListLock // BugCheckParameter2
PAGE:000000014076E69D call ExReleasePushLockEx
PAGE:000000014076E6A2 mov rcx, gs:188h
PAGE:000000014076E6AB call KeLeaveCriticalRegionThread
PAGE:000000014076E6B0 test rbx, rbx
PAGE:000000014076E6B3 jz short loc_14076E6CA
PAGE:000000014076E6B5 mov ecx, ebp
PAGE:000000014076E6B7 lock xadd [rbx+10h], ecx
PAGE:000000014076E6BC add ecx, ebp
PAGE:000000014076E6BE cmp ecx, 80000000h
PAGE:000000014076E6C4 jz loc_140849506
PAGE:000000014076E6CA
PAGE:000000014076E6CA loc_14076E6CA: // CODE XREF: CmpEnumerateCallback+93↑j
PAGE:000000014076E6CA // CmpEnumerateCallback+DAEF3↓j ...
PAGE:000000014076E6CA mov rbx, [rsp+28h+arg_0]
PAGE:000000014076E6CF mov rax, rdi
PAGE:000000014076E6D2 mov rbp, [rsp+28h+arg_8]
PAGE:000000014076E6D7 mov rsi, [rsp+28h+arg_10]
PAGE:000000014076E6DC add rsp, 20h
PAGE:000000014076E6E0 pop rdi
PAGE:000000014076E6E1 retn
PAGE:000000014076E6E1 // ---------------------------------------------------------------------------
PAGE:000000014076E6E2 db 0CCh
PAGE:000000014076E6E3 // ---------------------------------------------------------------------------
PAGE:000000014076E6E3
PAGE:000000014076E6E3 loc_14076E6E3: // CODE XREF: CmpEnumerateCallback+67↑j
PAGE:000000014076E6E3 mov rdx, [rdx] // 如果当前节点RundownRef._union_1.Count <= 0
PAGE:000000014076E6E3 // 没有运行引用保护的话,则不处理当前节点,就开始处理下一个节点
PAGE:000000014076E6E6 jmp short loc_14076E67D // while 循环
PAGE:000000014076E6E6 CmpEnumerateCallback endp // 判断是否是空链表

CmpEnumerateCallback(PREGISTRY_CALLBACK pCmpCallback, )
{
CurrentThread.KernelApcDisable -= 1;
ExAcquirePushLockSharedEx(&CmpCallbackListLock, 0);
}

参考:

2.4 总结

  1. 所有的注册表回调例程都放在 CallbackListHead 链表表尾,该链表每一个成员都是 REGISTRY_CALLBACK 结构。

    CallbackListHead 挂着所有注册表回调函数所在的 REGISTRY_CALLBACK 结构:

    15.png

    图片引自《Lazarus & BYOVD: evil to the Windows core》。

  2. 回调例程是存放在 REGISTRY_CALLBACK 结构中的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    typedef struct _REGISTRY_CALLBACK
    {
    0x0 LIST_ENTRY ListEntry;
    0x10 LARGE_INTEGER Cookie; // EX_RUNDOWN_REF RundownRef;
    0x18 PEX_CALLBACK_FUNCTION Function;
    0x20 PVOID Context;
    0x28 PEX_CALLBACK_FUNCTION Function; //LARGE_INTEGER Cookie;
    0x30 BOOLEAN PendingDelete;
    0x38 ULONGLONG AllocateAltitudeReturnValue;
    0x40 LIST_ENTRY ListEntry2;
    } REGISTRY_CALLBACK, *PREGISTRY_CALLBACK;
  3. 全局变量 CmpCallbackCookie 在每一次插入一个新的回调例程时都是自增 1。

  4. CmpCallBackCount用来表示 CallbackListHead 链表上回调例程个数的一个全局变量。

  5. 每次对链表 CallbackListHead 进行操作前,都要使用全局变量 CmpCallbackListLock 来对链表进行互斥上锁操作。

12.png

3 对象回调

在对内核对象句柄之前、之后的操作,都可以使用对象回调例程来进行监控并阻止。

14.png

3.1 创建/删除对象回调例程

创建对象句柄回调例程的函数是 ObRegisterCallbacks ,删除对象句柄回调例程的函数是 ObUnRegisterCallbacks。

1
2
3
4
NTSTATUS ObRegisterCallbacks(
[in] POB_CALLBACK_REGISTRATION CallbackRegistration, // 指向 OB_CALLBACK_REGISTRATION 结构
[out] PVOID *RegistrationHandle // 输出值,接收当前回调例程的句柄,方便 ObUnRegisterCallbacks 使用
);
  • 参数一: OB_CALLBACK_REGISTRATION 原型如下:

    1
    2
    3
    4
    5
    6
    7
    typedef struct _OB_CALLBACK_REGISTRATION {
    USHORT Version; // 版本,驱动程序应指定 OB_FLT_REGISTRATION_VERSION == ObGetFilterVersion()
    USHORT OperationRegistrationCount; // 参数五 OperationRegistration 数组中的元素个数
    UNICODE_STRING Altitude; // 回调函数被调用优先级(顺序),可以任意设置一个整数
    PVOID RegistrationContext; // 传递给回调例程的参数
    OB_OPERATION_REGISTRATION *OperationRegistration; // 指针,指向 OB_OPERATION_REGISTRATION 结构的数组
    } OB_CALLBACK_REGISTRATION, *POB_CALLBACK_REGISTRATION;
    • OperationRegistration 成员是一个数组,该数组的个数由 OperationRegistrationCount 指定。每个元素都是 OB_OPERATION_REGISTRATION 结构,原型如下:

      1
      2
      3
      4
      5
      6
      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 结构,表示对句柄做什么操作。指定以下一个或多个标志:
      OB_OPERATION_HANDLE_CREATE:已打开或将要打开一个新的进程,线程或桌面句柄
      OB_OPERATION_HANDLE_DUPLICATE:已复制或将要复制进程,线程或桌面句柄
      PreOperation 指向回调例程。在请求操作发生之前,系统调用此例程。POB_PRE_OPERATION_CALLBACK。
      PostOperation 指向回调例程。在请求操作发生之后,系统调用此例程。POB_POST_OPERATION_CALLBACK。

      POB_PRE_OPERATION_CALLBACK、POB_POST_OPERATION_CALLBACK 回调例程原型都是一样的:

      1
      2
      3
      4
      5
      6
      7
      POB_PRE_OPERATION_CALLBACK PobPreOperationCallback;

      OB_PREOP_CALLBACK_STATUS PobPreOperationCallback(
      [in] PVOID RegistrationContext, // 回调函数的参数,由 ObRegisterCallbacks 函数指定
      [in] POB_PRE_OPERATION_INFORMATION OperationInformation //重要的参数
      )
      {...}

      OperationInformation:可以通过修改这个参数,拒绝某些操作的权限,以达到保护进程、线程的目的。它是 OB_PRE_OPERATION_INFORMATION 结构:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      typedef struct _OB_PRE_OPERATION_INFORMATION {
      OB_OPERATION Operation; // 句柄操作的类型,此时从Parameters.CreateHandleInformation获取详细信息
      union {
      ULONG Flags; // 保留,请改用 KernelHandle 成员
      struct {
      ULONG KernelHandle : 1; // 指定句柄是否为内核句柄, TRUE:内核句柄
      ULONG Reserved : 31; // 预留给系统使用
      };
      };
      PVOID Object; // 当前句柄所属的进程/线程对象
      POBJECT_TYPE ObjectType; // 对象类型,只能是 PsThreadType 或 PsProcessType
      PVOID CallContext; // 同回调例程参数一 RegistrationContext
      POB_PRE_OPERATION_PARAMETERS Parameters; // 指向包含特定于操作信息的 OB_PRE_OPERATION_PARAMETERS 联合的指针。
      // Operation 成员确定联合的哪个成员有效。
      } OB_PRE_OPERATION_INFORMATION, *POB_PRE_OPERATION_INFORMATION;

      Parameters 是一个 OB_PRE_OPERATION_PARAMETERS 联合结构:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      typedef union _OB_PRE_OPERATION_PARAMETERS {
      OB_PRE_CREATE_HANDLE_INFORMATION CreateHandleInformation;
      OB_PRE_DUPLICATE_HANDLE_INFORMATION DuplicateHandleInformation;
      } OB_PRE_OPERATION_PARAMETERS, *POB_PRE_OPERATION_PARAMETERS;

      typedef struct _OB_PRE_CREATE_HANDLE_INFORMATION {
      ACCESS_MASK DesiredAccess; // 指定要授予句柄的访问权限,在回调例程可以修改权限
      ACCESS_MASK OriginalDesiredAccess; // 句柄原始的访问权限
      } OB_PRE_CREATE_HANDLE_INFORMATION, *POB_PRE_CREATE_HANDLE_INFORMATION;

      typedef struct _OB_PRE_DUPLICATE_HANDLE_INFORMATION {
      ACCESS_MASK DesiredAccess; // 指定要授予句柄的访问权限,在回调例程可以修改权限
      ACCESS_MASK OriginalDesiredAccess; // 句柄原始的访问权限
      PVOID SourceProcess; // A pointer to the process object for the process that is the source of the handle.
      PVOID TargetProcess; // A pointer to the process object for the process that receives the duplicated handle.
      } OB_PRE_DUPLICATE_HANDLE_INFORMATION, *POB_PRE_DUPLICATE_HANDLE_INFORMATION;

      DesiredAccess 默认情况下和 OriginalDesiredAccess 权限是一样的,即拥有全部权限。在回调函数中可修改该值,可修改的权限类型分为两类:

      • 进程权限,如 PROCESS_TERMINATE(可让进程终止的权限)
      • 线程权限,如 THREAD_SET_CONTEXT(可修改线程上下文的权限)
  • 参数二:RegistrationHandle。接受注册成功后的 handle,在驱动卸载或反注册时需要调用 ObUnRegisterCallbacks,并把该参数传入。

可以看到操作前操作后的 PreOperation、PostOperation 函数对应的 OperationInformation 不同:

  • 操作前,可以对 OB_PRE_OPERATION_INFORMATION.Parameters.CreateHandleInformation.DesiredAccess 权限进行操作。
  • 操作后,可以对 OB_POST_OPERATION_INFORMATION.ReturnStatus 返回码进行操作。

句柄操作前 POB_PRE_OPERATION_CALLBACK 参数结构及句柄操作后 POB_POST_OPERATION_CALLBACK 参数结构如下:

14.png

3.2 ObRegisterCallbacks 分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
NTSTATUS ObRegisterCallbacks(
[in] POB_CALLBACK_REGISTRATION CallbackRegistration, // 指向 OB_CALLBACK_REGISTRATION 结构
[out] PVOID *RegistrationHandle // 输出值,接收当前回调例程的句柄,方便 ObUnRegisterCallbacks 使用
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PCALLBACK_ENTRY pCallbackEntry = NULL;
WORD wVersion = 0;
ULONG64 uNumberOfBytes = 0;
USHORT uOperationRegistrationCount = 0;
USHORT TempNumb = 0;
USHORT TempEdi = 0;
USHORT TempR13d = 0;
PVOID pCallbackEntryAltitude = NULL;

TempR13d = 1;
wVersion = CallbackRegistration->Version;
uOperationRegistrationCount = CallbackRegistration->OperationRegistrationCount;
wVersion &= 0xFF00;

if(wVersion != OB_FLT_REGISTRATION_VERSION/*0x100*/
|| uOperationRegistrationCount == 0)
{
Status = 0xC000000D; // STATUS_INVALID_PARAMETER
return Status;
}

uNumberOfBytes = CallbackRegistration->Altitude.Length; // 这里因为考虑到字节对齐
// 前面4字节,字节对齐后这里应该是一个8的倍数开头
uNumberOfBytes += 0x20;
uOperationRegistrationCount *= 64; // 说明每个 CALLBACK_ENTRY 结构为 64 字节大小
uNumberOfBytes += uOperationRegistrationCount;

pCallbackEntry = (PCALLBACK_ENTRY)ExAllocatePoolWithTag(PagedPool, uNumberOfBytes, 'lFbO');

if(!pCallbackEntry)
{
Status = 0xC000009A; // STATUS_INSUFFICIENT_RESOURCES 内存申请失败
return Status;
}

memset(pCallbackEntry, 0, uNumberOfBytes);

pCallbackEntry->Version = OB_CALLBACK_REGISTRATION; // 0x100
pCallbackEntry->RegistrationContext = CallbackRegistration->RegistrationContext;
pCallbackEntry->AltitudeLength1 = CallbackRegistration->Altitude.Length;
pCallbackEntry->AltitudeLength2 = CallbackRegistration->Altitude.Length;

uNumberOfBytes -= CallbackRegistration->Altitude.Length;
pCallbackEntryAltitude = (PVOID)((ULONG64)pCallbackEntry + uNumberOfBytes);
pCallbackEntry->PointToAltitude = pCallbackEntryAltitude;

memmove(pCallbackEntryAltitude, (PVOID)CallbackRegistration->Altitude, CallbackRegistration->Altitude.Length);

if(TempEdi >= uOperationRegistrationCount) // 第一轮 TempEdi == 0
{
if(TempNumb == 0)
{
if(TempEdi >= pCallbackEntry->buffer1[0]) // 第一轮 TempEdi == 0, pCallbackEntry->buffer1[0] == 0
{
ExFreePoolWithTag(pCallbackEntry, 'lFbO');
return TempNumb;
}
}
if(TempEdi >= pCallbackEntry->buffer1[0])
{
*RegistrationHandle = pCallbackEntry;
return TempEdi;
}else
{
USHORT TempRax = TempEdi; // 第一轮 TempRax == 0
TempEdi += TempR13d; // 此时第一轮 TempEdi == 1, TempR13d == 1
TempRax *= 64;
((ULONG64)pCallbackEntry + TempRax + 0x34) | TempR13d;
...待分析
}
}else // uOperationRegistrationCount > TempEdi, 第一轮 TempEdi == 0
{}

return Status;
}

上述结构分析还是有一些问题,具体参考 《ObRegisterCallBacks的初始化与插入》。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
返回的结构类型为:_CALLBACK_ENTRY
typedef struct _CALLBACK_ENTRY{
0x0 USHORT Version;
0x2 char buffer1[6];
0x8 POB_OPERATION_REGISTRATION RegistrationContext;
0x10 USHORT AltitudeLength1;
0x12 USHORT AltitudeLength2;
0x14 char buffer2[4];
0x18 PVOID PointToAltitude;
0x20 CALLBACK_ENTRY_ITEM Items; // Is actually an array of CALLBACK_ENTRY_ITEMs that are also in a doubly linked list
}CALLBACK_ENTRY, *PCALLBACK_ENTRY;

typedef struct _CALLBACK_ENTRY_ITEM {
0x20 LIST_ENTRY EntryItemList;
0x30 OB_OPERATION Operations;
0x38 CALLBACK_ENTRY* CallbackEntry; // Points to the CALLBACK_ENTRY which we use for ObUnRegisterCallback
0x40 POBJECT_TYPE ObjectType;
0x48 POB_PRE_OPERATION_CALLBACK PreOperation;
0x50 POB_POST_OPERATION_CALLBACK PostOperation;
0x58 __int64 unk;
}CALLBACK_ENTRY_ITEM, *PCALLBACK_ENTRY_ITEM;

ObRegisterCallbacks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
PAGE:00000001407BB720 // NTSTATUS ObRegisterCallbacks(
PAGE:00000001407BB720 // [in] POB_CALLBACK_REGISTRATION CallbackRegistration, // 指向 OB_CALLBACK_REGISTRATION 结构
PAGE:00000001407BB720 // [out] PVOID *RegistrationHandle // 输出值,接收当前回调例程的句柄,方便 ObUnRegisterCallbacks 使用
PAGE:00000001407BB720 // )//
PAGE:00000001407BB720
PAGE:00000001407BB720 public ObRegisterCallbacks
PAGE:00000001407BB720 ObRegisterCallbacks proc near // DATA XREF: .rdata:00000001400AB61C↑o
PAGE:00000001407BB720 // .pdata:000000014010E438↑o
PAGE:00000001407BB720
PAGE:00000001407BB720 var_3FFFFF66 = byte ptr -3FFFFF66h
PAGE:00000001407BB720 arg_0 = qword ptr 8
PAGE:00000001407BB720 arg_8 = qword ptr 10h
PAGE:00000001407BB720 arg_10 = qword ptr 18h
PAGE:00000001407BB720
PAGE:00000001407BB720 // FUNCTION CHUNK AT PAGE:000000014085C236 SIZE 000000DD BYTES
PAGE:00000001407BB720
PAGE:00000001407BB720 mov [rsp+arg_0], rbx
PAGE:00000001407BB725 mov [rsp+arg_8], rbp
PAGE:00000001407BB72A mov [rsp+arg_10], rsi
PAGE:00000001407BB72F push rdi
PAGE:00000001407BB730 push r12
PAGE:00000001407BB732 push r13
PAGE:00000001407BB734 push r14
PAGE:00000001407BB736 push r15
PAGE:00000001407BB738 sub rsp, 20h
PAGE:00000001407BB73C movzx eax, [rcx+OB_CALLBACK_REGISTRATION.Version] // eax = Version
PAGE:00000001407BB73F mov r15, rcx
PAGE:00000001407BB742 mov ecx, 0FF00h
PAGE:00000001407BB747 xor edi, edi
PAGE:00000001407BB749 and ax, cx // Version &= 0xFF00
PAGE:00000001407BB74C mov r12, rdx
PAGE:00000001407BB74F mov ecx, 100h
PAGE:00000001407BB754 mov ebx, edi
PAGE:00000001407BB756 cmp ax, cx
PAGE:00000001407BB759 jnz loc_14085C309 // 1. Version != 0x100(OB_FLT_REGISTRATION_VERSION)
PAGE:00000001407BB759 // 2. OperationRegistrationCount == 0
PAGE:00000001407BB759 //
PAGE:00000001407BB759 // STATUS_INVALID_PARAMETER
PAGE:00000001407BB75F movzx eax, [r15+OB_CALLBACK_REGISTRATION.OperationRegistrationCount]
PAGE:00000001407BB764 test ax, ax
PAGE:00000001407BB767 jz loc_14085C309 // 1. Version != 0x100(OB_FLT_REGISTRATION_VERSION)
PAGE:00000001407BB767 // 2. OperationRegistrationCount == 0
PAGE:00000001407BB767 //
PAGE:00000001407BB767 // STATUS_INVALID_PARAMETER
PAGE:00000001407BB76D movzx ebp, word ptr [r15+8] // UNICODE_STRING.Length
PAGE:00000001407BB76D // 因为考虑到字节对齐,前面4字节,字节对齐后这里应该是一个8的倍数开头
PAGE:00000001407BB772 lea r13d, [rdi+1] // r13d = 0x1
PAGE:00000001407BB776 mov ecx, eax
PAGE:00000001407BB778 add ebp, 20h // ebp = UNICODE_STRING.Length + 0x20
PAGE:00000001407BB77B shl ecx, 6 // ecx = OperationRegistrationCount * 64
PAGE:00000001407BB77E mov r8d, 'lFbO' // Tag
PAGE:00000001407BB784 add ebp, ecx // ebp = UNICODE_STRING.Length + 0x20 + OperationRegistrationCount*64
PAGE:00000001407BB786 mov ecx, r13d // PoolType
PAGE:00000001407BB789 mov edx, ebp // NumberOfBytes
PAGE:00000001407BB78B mov r14d, ebp
PAGE:00000001407BB78E call ExAllocatePoolWithTag // 返回的结构类型为:_CALLBACK_ENTRY
PAGE:00000001407BB78E // typedef struct _CALLBACK_ENTRY{
PAGE:00000001407BB78E // 0x0 USHORT Version//
PAGE:00000001407BB78E // 0x2 char buffer1[6]//
PAGE:00000001407BB78E // 0x8 POB_OPERATION_REGISTRATION RegistrationContext//
PAGE:00000001407BB78E // 0x10 USHORT AltitudeLength1//
PAGE:00000001407BB78E // 0x12 USHORT AltitudeLength2//
PAGE:00000001407BB78E // 0x14 char buffer2[4]//
PAGE:00000001407BB78E // 0x18 PVOID PointToAltitude//
PAGE:00000001407BB78E // 0x20 CALLBACK_ENTRY_ITEM Items// // Is actually an array of CALLBACK_ENTRY_ITEMs that are also in a doubly linked list
PAGE:00000001407BB78E // }CALLBACK_ENTRY, *PCALLBACK_ENTRY//
PAGE:00000001407BB793 mov rsi, rax
PAGE:00000001407BB796 test rax, rax
PAGE:00000001407BB799 jz loc_14085C236 // STATUS_INSUFFICIENT_RESOURCES 内存申请失败
PAGE:00000001407BB79F mov r8d, r14d // Size
PAGE:00000001407BB7A2 xor edx, edx // Val
PAGE:00000001407BB7A4 mov rcx, rax // Dst
PAGE:00000001407BB7A7 call memset
PAGE:00000001407BB7AC mov eax, 100h
PAGE:00000001407BB7B1 mov [rsi], ax
PAGE:00000001407BB7B4 mov rax, [r15+18h] // OB_CALLBACK_REGISTRATION.RegistrationContext
PAGE:00000001407BB7B8 mov [rsi+8], rax
PAGE:00000001407BB7BC movzx r8d, word ptr [r15+8] // Size
PAGE:00000001407BB7C1 sub ebp, r8d // uNumberOfBytes -= CallbackRegistration->Altitude.Length
PAGE:00000001407BB7C4 mov [rsi+12h], r8w
PAGE:00000001407BB7C9 mov [rsi+10h], r8w
PAGE:00000001407BB7CE mov ecx, ebp
PAGE:00000001407BB7D0 add rcx, rsi // Dst
PAGE:00000001407BB7D3 mov [rsi+18h], rcx
PAGE:00000001407BB7D7 mov rdx, [r15+10h] // Src
PAGE:00000001407BB7DB call memmove
PAGE:00000001407BB7E0 mov ebp, edi // ebp = edi = 0
PAGE:00000001407BB7E2 cmp di, [r15+OB_CALLBACK_REGISTRATION.OperationRegistrationCount]
PAGE:00000001407BB7E7 jnb loc_1407BB8A3 // ebx = edi = 0
PAGE:00000001407BB7ED
PAGE:00000001407BB7ED loc_1407BB7ED: // CODE XREF: ObRegisterCallbacks+17D↓j
PAGE:00000001407BB7ED mov r14d, ebp
PAGE:00000001407BB7F0 shl r14, 5 // uNumberOfBytes *= 32
PAGE:00000001407BB7F4 add r14, [r15+20h] // [r15+20h] 为 CALLBACK_ENTRY.Items(CALLBACK_ENTRY_ITEM)
PAGE:00000001407BB7F8 mov ebx, ebp
PAGE:00000001407BB7FA cmp [r14+8], edi // 此时 edi = 0,判断 Items.Blink = 0 ?
PAGE:00000001407BB7FE jz loc_14085C2F8 // STATUS_INVALID_PARAMETER
PAGE:00000001407BB804 mov rax, [r14] // rax = Items.Flink
PAGE:00000001407BB807 mov rcx, [rax] // rcx = Items.Flink.Flink
PAGE:00000001407BB80A test byte ptr [rcx+42h], 40h
PAGE:00000001407BB80E jz loc_14085C2F8 // STATUS_INVALID_PARAMETER
PAGE:00000001407BB814 mov rcx, [r14+10h]
PAGE:00000001407BB818 test rcx, rcx
PAGE:00000001407BB81B jz loc_14085C240
PAGE:00000001407BB821
PAGE:00000001407BB821 loc_1407BB821: // CODE XREF: ObRegisterCallbacks+A0B33↓j
PAGE:00000001407BB821 mov edx, 20h
PAGE:00000001407BB826 call MmVerifyCallbackFunctionCheckFlags
PAGE:00000001407BB82B test eax, eax
PAGE:00000001407BB82D jz loc_14085C26A
PAGE:00000001407BB833
PAGE:00000001407BB833 loc_1407BB833: // CODE XREF: ObRegisterCallbacks+A0B2D↓j
PAGE:00000001407BB833 mov rcx, [r14+18h]
PAGE:00000001407BB837 test rcx, rcx
PAGE:00000001407BB83A jnz loc_14085C258
PAGE:00000001407BB840
PAGE:00000001407BB840 loc_1407BB840: // CODE XREF: ObRegisterCallbacks+A0B44↓j
PAGE:00000001407BB840 shl rbx, 6
PAGE:00000001407BB844 add rbx, 20h
PAGE:00000001407BB848 add rbx, rsi
PAGE:00000001407BB84B lea rcx, [rbx+38h] // SpinLock
PAGE:00000001407BB84F mov [rbx+8], rbx
PAGE:00000001407BB853 mov [rbx], rbx
PAGE:00000001407BB856 call ExInitializePushLock
PAGE:00000001407BB85B mov eax, [r14+8]
PAGE:00000001407BB85F mov rdx, rbx
PAGE:00000001407BB862 mov [rbx+10h], eax
PAGE:00000001407BB865 mov [rbx+18h], rsi
PAGE:00000001407BB869 mov rax, [r14]
PAGE:00000001407BB86C mov rcx, [rax]
PAGE:00000001407BB86F mov [rbx+20h], rcx
PAGE:00000001407BB873 mov rax, [r14+10h]
PAGE:00000001407BB877 mov [rbx+28h], rax
PAGE:00000001407BB87B mov rax, [r14+18h]
PAGE:00000001407BB87F mov [rbx+30h], rax
PAGE:00000001407BB883 call ObpInsertCallbackByAltitude
PAGE:00000001407BB888 mov ebx, eax
PAGE:00000001407BB88A test eax, eax
PAGE:00000001407BB88C js short loc_1407BB8A3 // ebx = edi = 0
PAGE:00000001407BB88E add [rsi+2], r13w
PAGE:00000001407BB893 add ebp, r13d
PAGE:00000001407BB896 movzx ecx, word ptr [r15+2]
PAGE:00000001407BB89B cmp ebp, ecx
PAGE:00000001407BB89D jb loc_1407BB7ED
PAGE:00000001407BB8A3
PAGE:00000001407BB8A3 loc_1407BB8A3: // CODE XREF: ObRegisterCallbacks+C7↑j
PAGE:00000001407BB8A3 // ObRegisterCallbacks+16C↑j ...
PAGE:00000001407BB8A3 test ebx, ebx // ebx = edi = 0
PAGE:00000001407BB8A5 js loc_14085C26F // Rflags.SF = 0,SF来自计算结果的最高位
PAGE:00000001407BB8A5 // ebx == 0,则发生跳转
PAGE:00000001407BB8AB cmp di, [rsi+2] // 第一轮,di、[rsi+2] 都是 0
PAGE:00000001407BB8AF jnb short loc_1407BB8C7
PAGE:00000001407BB8B1
PAGE:00000001407BB8B1 loc_1407BB8B1: // CODE XREF: ObRegisterCallbacks+1A5↓j
PAGE:00000001407BB8B1 mov eax, edi
PAGE:00000001407BB8B3 add edi, r13d
PAGE:00000001407BB8B6 shl rax, 6
PAGE:00000001407BB8BA or [rax+rsi+34h], r13d
PAGE:00000001407BB8BF movzx eax, word ptr [rsi+2]
PAGE:00000001407BB8C3 cmp edi, eax
PAGE:00000001407BB8C5 jb short loc_1407BB8B1
PAGE:00000001407BB8C7
PAGE:00000001407BB8C7 loc_1407BB8C7: // CODE XREF: ObRegisterCallbacks+18F↑j
PAGE:00000001407BB8C7 mov [r12], rsi
PAGE:00000001407BB8CB
PAGE:00000001407BB8CB loc_1407BB8CB: // CODE XREF: ObRegisterCallbacks+A0BD3↓j
PAGE:00000001407BB8CB mov eax, ebx
PAGE:00000001407BB8CD
PAGE:00000001407BB8CD loc_1407BB8CD: // CODE XREF: ObRegisterCallbacks+A0B1B↓j
PAGE:00000001407BB8CD // ObRegisterCallbacks+A0BEE↓j
PAGE:00000001407BB8CD mov rbx, [rsp+48h+arg_0]
PAGE:00000001407BB8D2 mov rbp, [rsp+48h+arg_8]
PAGE:00000001407BB8D7 mov rsi, [rsp+48h+arg_10]
PAGE:00000001407BB8DC add rsp, 20h
PAGE:00000001407BB8E0 pop r15
PAGE:00000001407BB8E2 pop r14
PAGE:00000001407BB8E4 pop r13
PAGE:00000001407BB8E6 pop r12
PAGE:00000001407BB8E8 pop rdi
PAGE:00000001407BB8E9 retn
PAGE:00000001407BB8E9 // ---------------------------------------------------------------------------
PAGE:00000001407BB8EA db 0CCh
PAGE:00000001407BB8EA ObRegisterCallbacks endp

ObUnRegisterCallbacks

ObpCallPreOperationCallbacks

ObpCallPostOperationCallbacks

4 文件对象回调