写这篇帖子主要是为记录ObRegisterCallbacks函数的相关分析,如有错误,欢迎批评指正。给出的为C伪代码,仅展示逻辑,不保证安全性。
分析
ObRegisterCallbacks函数
C的伪代码如下:
NTSTATUS ObRegisterCallbacks(POB_CALLBACK_REGISTRATION CallbackRegistration, PVOID *RegistrationHandle) {
POB_CALLBACK_ENTRY CallbackEntry;
POB_CALLBACK_HEADER Header;
ULONG TotalSize;
NTSTATUS Status = STATUS_SUCCESS;
ULONG i;
//验证基本结构版本和标志位
if ((CallbackRegistration->Version & 0xFF00) != OB_FLT_REGISTRATION_VERSION ||
CallbackRegistration->OperationCount == 0)
{
return STATUS_INVALID_PARAMETER;
}
//计算需要分配的内存大小(Header + 回调项数组)
TotalSize = sizeof(OB_CALLBACK_HEADER) +
CallbackRegistration->OperationCount * sizeof(OB_CALLBACK_ENTRY);
// 分配带标签的内存池
Header = (POB_CALLBACK_HEADER)ExAllocatePoolWithTag(NonPagedPool, TotalSize, 'ObCf');
if (!Header) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(Header, TotalSize);
// 初始化Header结构
Header->Version = CallbackRegistration->Version;
Header->OperationCount = CallbackRegistration->OperationCount;
Header->RegistrationContext = CallbackRegistration->RegistrationContext;
// 处理每个回调项
for (i = 0; i < CallbackRegistration->OperationCount; i++) {
POB_OPERATION_REGISTRATION Operation = &CallbackRegistration->OperationRegistration[i];
CallbackEntry = &Header->Entries[i];
// 验证Altitude和回调函数
if (Operation->Altitude == 0 ||
!MmVerifyCallbackFunctionCheckFlags(Operation->PreOperation, 0x20) ||
(Operation->PostOperation &&
!MmVerifyCallbackFunctionCheckFlags(Operation->PostOperation, 0x20)))
{
Status = STATUS_INVALID_PARAMETER;
break;
}
// 初始化回调项
CallbackEntry->Altitude = Operation->Altitude;
CallbackEntry->PreOperation = Operation->PreOperation;
CallbackEntry->PostOperation = Operation->PostOperation;
InitializeListHead(&CallbackEntry->Link);
// 按Altitude插入全局链表
Status = ObpInsertCallbackByAltitude(CallbackEntry);
if (!NT_SUCCESS(Status)) {
break;
}
}
// 错误处理:回滚已注册项
if (!NT_SUCCESS(Status)) {
for (ULONG j = 0; j < i; j++) {
ObpRemoveCallbackEntry(&Header->Entries[j]);
}
ExFreePoolWithTag(Header, 'ObCf');
return Status;
}
//设置输出句柄
*RegistrationHandle = Header;
return STATUS_SUCCESS;
}
这是原始汇编代码:
nt!ObRegisterCallbacks:
fffff800`243ca320 48895c2408 mov qword ptr [rsp+8],rbx
fffff800`243ca325 48896c2410 mov qword ptr [rsp+10h],rbp
fffff800`243ca32a 4889742418 mov qword ptr [rsp+18h],rsi
fffff800`243ca32f 57 push rdi
fffff800`243ca330 4154 push r12
fffff800`243ca332 4155 push r13
fffff800`243ca334 4156 push r14
fffff800`243ca336 4157 push r15
fffff800`243ca338 4883ec20 sub rsp,20h
fffff800`243ca33c 0fb701 movzx eax,word ptr [rcx]
fffff800`243ca33f 4c8bf9 mov r15,rcx
fffff800`243ca342 b900ff0000 mov ecx,0FF00h
fffff800`243ca347 33ff xor edi,edi
fffff800`243ca349 6623c1 and ax,cx
fffff800`243ca34c 4c8be2 mov r12,rdx
fffff800`243ca34f b900010000 mov ecx,100h
fffff800`243ca354 8bdf mov ebx,edi
fffff800`243ca356 663bc1 cmp ax,cx
fffff800`243ca359 0f8532740900 jne nt!ObRegisterCallbacks+0x97471 (fffff800`24461791) Branch
nt!ObRegisterCallbacks+0x3f:
fffff800`243ca35f 410fb74702 movzx eax,word ptr [r15+2]
fffff800`243ca364 6685c0 test ax,ax
fffff800`243ca367 0f8424740900 je nt!ObRegisterCallbacks+0x97471 (fffff800`24461791) Branch
nt!ObRegisterCallbacks+0x4d:
fffff800`243ca36d 410fb76f08 movzx ebp,word ptr [r15+8]
fffff800`243ca372 448d6f01 lea r13d,[rdi+1]
fffff800`243ca376 8bc8 mov ecx,eax
fffff800`243ca378 83c520 add ebp,20h
fffff800`243ca37b c1e106 shl ecx,6
fffff800`243ca37e 41b84f62466c mov r8d,6C46624Fh
fffff800`243ca384 03e9 add ebp,ecx
fffff800`243ca386 418bcd mov ecx,r13d
fffff800`243ca389 8bd5 mov edx,ebp
fffff800`243ca38b 448bf5 mov r14d,ebp
fffff800`243ca38e e87dcc1e00 call nt!ExAllocatePoolWithTag (fffff800`245b7010)
fffff800`243ca393 488bf0 mov rsi,rax
fffff800`243ca396 4885c0 test rax,rax
fffff800`243ca399 0f841f730900 je nt!ObRegisterCallbacks+0x9739e (fffff800`244616be) Branch
nt!ObRegisterCallbacks+0x7f:
fffff800`243ca39f 458bc6 mov r8d,r14d
fffff800`243ca3a2 33d2 xor edx,edx
fffff800`243ca3a4 488bc8 mov rcx,rax
fffff800`243ca3a7 e8d48cc4ff call nt!memset (fffff800`24013080)
fffff800`243ca3ac b800010000 mov eax,100h
fffff800`243ca3b1 668906 mov word ptr [rsi],ax
fffff800`243ca3b4 498b4718 mov rax,qword ptr [r15+18h]
fffff800`243ca3b8 48894608 mov qword ptr [rsi+8],rax
fffff800`243ca3bc 450fb74708 movzx r8d,word ptr [r15+8]
fffff800`243ca3c1 412be8 sub ebp,r8d
fffff800`243ca3c4 6644894612 mov word ptr [rsi+12h],r8w
fffff800`243ca3c9 6644894610 mov word ptr [rsi+10h],r8w
fffff800`243ca3ce 8bcd mov ecx,ebp
fffff800`243ca3d0 4803ce add rcx,rsi
fffff800`243ca3d3 48894e18 mov qword ptr [rsi+18h],rcx
fffff800`243ca3d7 498b5710 mov rdx,qword ptr [r15+10h]
fffff800`243ca3db e8e089c4ff call nt!memcpy (fffff800`24012dc0)
fffff800`243ca3e0 8bef mov ebp,edi
fffff800`243ca3e2 66413b7f02 cmp di,word ptr [r15+2]
fffff800`243ca3e7 0f83b6000000 jae nt!ObRegisterCallbacks+0x183 (fffff800`243ca4a3) Branch
nt!ObRegisterCallbacks+0xcd:
fffff800`243ca3ed 448bf5 mov r14d,ebp
fffff800`243ca3f0 49c1e605 shl r14,5
fffff800`243ca3f4 4d037720 add r14,qword ptr [r15+20h]
fffff800`243ca3f8 8bdd mov ebx,ebp
fffff800`243ca3fa 41397e08 cmp dword ptr [r14+8],edi
fffff800`243ca3fe 0f847c730900 je nt!ObRegisterCallbacks+0x97460 (fffff800`24461780) Branch
nt!ObRegisterCallbacks+0xe4:
fffff800`243ca404 498b06 mov rax,qword ptr [r14]
fffff800`243ca407 488b08 mov rcx,qword ptr [rax]
fffff800`243ca40a f6414240 test byte ptr [rcx+42h],40h
fffff800`243ca40e 0f846c730900 je nt!ObRegisterCallbacks+0x97460 (fffff800`24461780) Branch
nt!ObRegisterCallbacks+0xf4:
fffff800`243ca414 498b4e10 mov rcx,qword ptr [r14+10h]
fffff800`243ca418 4885c9 test rcx,rcx
fffff800`243ca41b 0f84a7720900 je nt!ObRegisterCallbacks+0x973a8 (fffff800`244616c8) Branch
nt!ObRegisterCallbacks+0x101:
fffff800`243ca421 ba20000000 mov edx,20h
fffff800`243ca426 e8a552beff call nt!MmVerifyCallbackFunctionCheckFlags (fffff800`23faf6d0)
fffff800`243ca42b 85c0 test eax,eax
fffff800`243ca42d 0f84bf720900 je nt!ObRegisterCallbacks+0x973d2 (fffff800`244616f2) Branch
nt!ObRegisterCallbacks+0x113:
fffff800`243ca433 498b4e18 mov rcx,qword ptr [r14+18h]
fffff800`243ca437 4885c9 test rcx,rcx
fffff800`243ca43a 0f85a0720900 jne nt!ObRegisterCallbacks+0x973c0 (fffff800`244616e0) Branch
nt!ObRegisterCallbacks+0x120:
fffff800`243ca440 48c1e306 shl rbx,6
fffff800`243ca444 4883c320 add rbx,20h
fffff800`243ca448 4803de add rbx,rsi
fffff800`243ca44b 488d4b38 lea rcx,[rbx+38h]
fffff800`243ca44f 48895b08 mov qword ptr [rbx+8],rbx
fffff800`243ca453 48891b mov qword ptr [rbx],rbx
fffff800`243ca456 e8f5d4aeff call nt!ExInitializePushLock (fffff800`23eb7950)
fffff800`243ca45b 418b4608 mov eax,dword ptr [r14+8]
fffff800`243ca45f 488bd3 mov rdx,rbx
fffff800`243ca462 894310 mov dword ptr [rbx+10h],eax
fffff800`243ca465 48897318 mov qword ptr [rbx+18h],rsi
fffff800`243ca469 498b06 mov rax,qword ptr [r14]
fffff800`243ca46c 488b08 mov rcx,qword ptr [rax]
fffff800`243ca46f 48894b20 mov qword ptr [rbx+20h],rcx
fffff800`243ca473 498b4610 mov rax,qword ptr [r14+10h]
fffff800`243ca477 48894328 mov qword ptr [rbx+28h],rax
fffff800`243ca47b 498b4618 mov rax,qword ptr [r14+18h]
fffff800`243ca47f 48894330 mov qword ptr [rbx+30h],rax
fffff800`243ca483 e86c000000 call nt!ObpInsertCallbackByAltitude (fffff800`243ca4f4)
fffff800`243ca488 8bd8 mov ebx,eax
fffff800`243ca48a 85c0 test eax,eax
fffff800`243ca48c 7815 js nt!ObRegisterCallbacks+0x183 (fffff800`243ca4a3) Branch
nt!ObRegisterCallbacks+0x16e:
fffff800`243ca48e 6644016e02 add word ptr [rsi+2],r13w
fffff800`243ca493 4103ed add ebp,r13d
fffff800`243ca496 410fb74f02 movzx ecx,word ptr [r15+2]
fffff800`243ca49b 3be9 cmp ebp,ecx
fffff800`243ca49d 0f824affffff jb nt!ObRegisterCallbacks+0xcd (fffff800`243ca3ed) Branch
nt!ObRegisterCallbacks+0x183:
fffff800`243ca4a3 85db test ebx,ebx
fffff800`243ca4a5 0f884c720900 js nt!ObRegisterCallbacks+0x973d7 (fffff800`244616f7) Branch
nt!ObRegisterCallbacks+0x18b:
fffff800`243ca4ab 663b7e02 cmp di,word ptr [rsi+2]
fffff800`243ca4af 7316 jae nt!ObRegisterCallbacks+0x1a7 (fffff800`243ca4c7) Branch
nt!ObRegisterCallbacks+0x191:
fffff800`243ca4b1 8bc7 mov eax,edi
fffff800`243ca4b3 4103fd add edi,r13d
fffff800`243ca4b6 48c1e006 shl rax,6
fffff800`243ca4ba 44096c3034 or dword ptr [rax+rsi+34h],r13d
fffff800`243ca4bf 0fb74602 movzx eax,word ptr [rsi+2]
fffff800`243ca4c3 3bf8 cmp edi,eax
fffff800`243ca4c5 72ea jb nt!ObRegisterCallbacks+0x191 (fffff800`243ca4b1) Branch
nt!ObRegisterCallbacks+0x1a7:
fffff800`243ca4c7 49893424 mov qword ptr [r12],rsi
nt!ObRegisterCallbacks+0x1ab:
fffff800`243ca4cb 8bc3 mov eax,ebx
nt!ObRegisterCallbacks+0x1ad:
fffff800`243ca4cd 488b5c2450 mov rbx,qword ptr [rsp+50h]
fffff800`243ca4d2 488b6c2458 mov rbp,qword ptr [rsp+58h]
fffff800`243ca4d7 488b742460 mov rsi,qword ptr [rsp+60h]
fffff800`243ca4dc 4883c420 add rsp,20h
fffff800`243ca4e0 415f pop r15
fffff800`243ca4e2 415e pop r14
fffff800`243ca4e4 415d pop r13
fffff800`243ca4e6 415c pop r12
fffff800`243ca4e8 5f pop rdi
fffff800`243ca4e9 c3 ret
nt!ObRegisterCallbacks+0x9739e:
fffff800`244616be b89a0000c0 mov eax,0C000009Ah
fffff800`244616c3 e9058ef6ff jmp nt!ObRegisterCallbacks+0x1ad (fffff800`243ca4cd) Branch
nt!ObRegisterCallbacks+0x973a8:
fffff800`244616c8 49397e18 cmp qword ptr [r14+18h],rdi
fffff800`244616cc 0f84ae000000 je nt!ObRegisterCallbacks+0x97460 (fffff800`24461780) Branch
nt!ObRegisterCallbacks+0x973b2:
fffff800`244616d2 4885c9 test rcx,rcx
fffff800`244616d5 0f84588df6ff je nt!ObRegisterCallbacks+0x113 (fffff800`243ca433) Branch
nt!ObRegisterCallbacks+0x973bb:
fffff800`244616db e9418df6ff jmp nt!ObRegisterCallbacks+0x101 (fffff800`243ca421) Branch
nt!ObRegisterCallbacks+0x973c0:
fffff800`244616e0 ba20000000 mov edx,20h
fffff800`244616e5 e8e6dfb4ff call nt!MmVerifyCallbackFunctionCheckFlags (fffff800`23faf6d0)
fffff800`244616ea 85c0 test eax,eax
fffff800`244616ec 0f854e8df6ff jne nt!ObRegisterCallbacks+0x120 (fffff800`243ca440) Branch
nt!ObRegisterCallbacks+0x973d2:
fffff800`244616f2 bb220000c0 mov ebx,0C0000022h
nt!ObRegisterCallbacks+0x973d7:
fffff800`244616f7 663b7e02 cmp di,word ptr [rsi+2]
fffff800`244616fb 7370 jae nt!ObRegisterCallbacks+0x9744d (fffff800`2446176d) Branch
nt!ObRegisterCallbacks+0x973dd:
fffff800`244616fd bdb8000000 mov ebp,0B8h
nt!ObRegisterCallbacks+0x973e2:
fffff800`24461702 8bc7 mov eax,edi
fffff800`24461704 4c8d7620 lea r14,[rsi+20h]
fffff800`24461708 48c1e006 shl rax,6
fffff800`2446170c 4c03f0 add r14,rax
fffff800`2446170f 65488b042588010000 mov rax,qword ptr gs:[188h]
fffff800`24461718 66ff88e6010000 dec word ptr [rax+1E6h]
fffff800`2446171f 498b4e20 mov rcx,qword ptr [r14+20h]
fffff800`24461723 33d2 xor edx,edx
fffff800`24461725 4803cd add rcx,rbp
fffff800`24461728 e8433c9eff call nt!ExAcquirePushLockExclusiveEx (fffff800`23e45370)
fffff800`2446172d 498b0e mov rcx,qword ptr [r14]
fffff800`24461730 4c397108 cmp qword ptr [rcx+8],r14
fffff800`24461734 7554 jne nt!ObRegisterCallbacks+0x9746a (fffff800`2446178a) Branch
nt!ObRegisterCallbacks+0x97416:
fffff800`24461736 498b4608 mov rax,qword ptr [r14+8]
fffff800`2446173a 4c3930 cmp qword ptr [rax],r14
fffff800`2446173d 754b jne nt!ObRegisterCallbacks+0x9746a (fffff800`2446178a) Branch
nt!ObRegisterCallbacks+0x9741f:
fffff800`2446173f 488908 mov qword ptr [rax],rcx
fffff800`24461742 33d2 xor edx,edx
fffff800`24461744 48894108 mov qword ptr [rcx+8],rax
fffff800`24461748 498b4e20 mov rcx,qword ptr [r14+20h]
fffff800`2446174c 4803cd add rcx,rbp
fffff800`2446174f e81c419eff call nt!ExReleasePushLockEx (fffff800`23e45870)
fffff800`24461754 65488b0c2588010000 mov rcx,qword ptr gs:[188h]
fffff800`2446175d e80e409eff call nt!KiLeaveGuardedRegionUnsafe (fffff800`23e45770)
fffff800`24461762 0fb74602 movzx eax,word ptr [rsi+2]
fffff800`24461766 4103fd add edi,r13d
fffff800`24461769 3bf8 cmp edi,eax
fffff800`2446176b 7295 jb nt!ObRegisterCallbacks+0x973e2 (fffff800`24461702) Branch
nt!ObRegisterCallbacks+0x9744d:
fffff800`2446176d ba4f62466c mov edx,6C46624Fh
fffff800`24461772 488bce mov rcx,rsi
fffff800`24461775 e836591500 call nt!ExFreePool (fffff800`245b70b0)
fffff800`2446177a 90 nop
fffff800`2446177b e94b8df6ff jmp nt!ObRegisterCallbacks+0x1ab (fffff800`243ca4cb) Branch
nt!ObRegisterCallbacks+0x97460:
fffff800`24461780 bb0d0000c0 mov ebx,0C000000Dh
fffff800`24461785 e9198df6ff jmp nt!ObRegisterCallbacks+0x183 (fffff800`243ca4a3) Branch
nt!ObRegisterCallbacks+0x9746a:
fffff800`2446178a b903000000 mov ecx,3
fffff800`2446178f cd29 int 29h
nt!ObRegisterCallbacks+0x97471:
fffff800`24461791 b80d0000c0 mov eax,0C000000Dh
fffff800`24461796 e9328df6ff jmp nt!ObRegisterCallbacks+0x1ad (fffff800`243ca4cd) Branch
逆向ObUnRegisterCallbacks的思路
函数开头保存了一些寄存器的值,包括rbx、rbp、rsi、rdi,以及r12到r15,这说明函数会使用这些寄存器,并在结束时恢复它们。接着分配了0x20字节的栈空间,一般用于局部变量。
然后检查输入参数。这是内核函数的通用操作,可以看一下。
movzx eax, word ptr [rcx],这里rcx是第一个参数,可能是一个指向OB_CALLBACK_REGISTRATION结构的指针。接着用0xFF00进行按位与操作,判断是否设置了特定标志。如果不符合条件,跳转到错误处理部分,返回错误码0xC000000D(STATUS_INVALID_PARAMETER)。
接下来,函数读取r15+2处的字(可能是OperationCount),如果为零,同样跳转到错误处理。这里检查是否有有效的操作类型数量。
然后,函数计算需要分配的内存大小,使用ExAllocatePoolWithTag分配内存,标签是0x6C46624F,可能是'ObCf'的ASCII表示,ObjectCallbackFunction嘛。如果分配失败,返回0xC000009A(STATUS_INSUFFICIENT_RESOURCES)。
分配内存后,用memset清零,这里用RltZeroMemory替代,并设置结构体的各个字段。
然后,通过循环处理每个回调操作,验证每个回调的Altitude和回调函数是否有效,特别是调用MmVerifyCallbackFunctionCheckFlags来验证回调函数的标志位。
在处理每个回调项时,会为每个操作分配一个回调块,并将其插入到全局回调链表中,使用ObpInsertCallbackByAltitude确保按Altitude排序。如果插入失败,函数需要回滚已分配的资源,并返回相应的错误码。
KeEnterCriticalRegion这些就是APC相关函数
内核中也在用ExAllocatePoolWithTag嘛,WDK还非要用ExAllocatePool2:)
几个函数中最引人注目的应该是MmVerifyCallbackFunctionCheckFlags吧,负责对回调合法性的检验,下面有对其分析。
ObUnRegisterCallbacks函数
NTSTATUS ObUnRegisterCallbacks(PVOID RegistrationHandle) {
POB_CALLBACK_HEADER Header = (POB_CALLBACK_HEADER)RegistrationHandle;
USHORT i = 0;
// 遍历所有回调项
while (i < Header->OperationCount) {
POB_CALLBACK_ENTRY Entry = (POB_CALLBACK_ENTRY*)((PUCHAR)Header->Entries + i * 0x40);
// 等待运行保护完成
ExWaitForRundownProtectionRelease(&Entry->RundownProtect);
// 进入临界区并获取推锁
KeEnterCriticalRegion();
ExAcquirePushLockExclusiveEx(&Entry->Lock, 0);
// 从全局链表安全移除
if (ValidateLinkedList(Entry)) { // 链表完整性校验
RemoveEntryList(&Entry->Link);
InitializeListHead(&Entry->Link); // 防止野指针
} else {
KeBugCheckEx(LIST_ENTRY_CORRUPT, 3, 0, 0, 0);
}
// 释放锁并离开临界区
ExReleasePushLockExclusiveEx(&Entry->Lock);
KeLeaveCriticalRegion();
i++;
}
// 释放整个回调结构内存
ExFreePoolWithTag(Header, 'ObCf');
return STATUS_SUCCESS;
}
原始汇编代码:
nt!ObUnRegisterCallbacks:
fffff800`244e0340 48895c2408 mov qword ptr [rsp+8],rbx
fffff800`244e0345 4889742410 mov qword ptr [rsp+10h],rsi
fffff800`244e034a 57 push rdi
fffff800`244e034b 4883ec20 sub rsp,20h
fffff800`244e034f 33f6 xor esi,esi
fffff800`244e0351 488bd9 mov rbx,rcx
fffff800`244e0354 663b7102 cmp si,word ptr [rcx+2]
fffff800`244e0358 737b jae nt!ObUnRegisterCallbacks+0x95 (fffff800`244e03d5) Branch
nt!ObUnRegisterCallbacks+0x1a:
fffff800`244e035a 8bc6 mov eax,esi
fffff800`244e035c 488d7b20 lea rdi,[rbx+20h]
fffff800`244e0360 48c1e006 shl rax,6
fffff800`244e0364 4803f8 add rdi,rax
fffff800`244e0367 488d4f38 lea rcx,[rdi+38h]
fffff800`244e036b e8e01e9fff call nt!ExWaitForRundownProtectionRelease (fffff800`23ed2250)
fffff800`244e0370 65488b042588010000 mov rax,qword ptr gs:[188h]
fffff800`244e0379 66ff88e6010000 dec word ptr [rax+1E6h]
fffff800`244e0380 488b4f20 mov rcx,qword ptr [rdi+20h]
fffff800`244e0384 33d2 xor edx,edx
fffff800`244e0386 4881c1b8000000 add rcx,0B8h
fffff800`244e038d e8de4f96ff call nt!ExAcquirePushLockExclusiveEx (fffff800`23e45370)
fffff800`244e0392 488b0f mov rcx,qword ptr [rdi]
fffff800`244e0395 48397908 cmp qword ptr [rcx+8],rdi
fffff800`244e0399 7558 jne nt!ObUnRegisterCallbacks+0xb3 (fffff800`244e03f3) Branch
nt!ObUnRegisterCallbacks+0x5b:
fffff800`244e039b 488b4708 mov rax,qword ptr [rdi+8]
fffff800`244e039f 483938 cmp qword ptr [rax],rdi
fffff800`244e03a2 754f jne nt!ObUnRegisterCallbacks+0xb3 (fffff800`244e03f3) Branch
nt!ObUnRegisterCallbacks+0x64:
fffff800`244e03a4 488908 mov qword ptr [rax],rcx
fffff800`244e03a7 33d2 xor edx,edx
fffff800`244e03a9 48894108 mov qword ptr [rcx+8],rax
fffff800`244e03ad 488b4f20 mov rcx,qword ptr [rdi+20h]
fffff800`244e03b1 4881c1b8000000 add rcx,0B8h
fffff800`244e03b8 e8b35496ff call nt!ExReleasePushLockEx (fffff800`23e45870)
fffff800`244e03bd 65488b0c2588010000 mov rcx,qword ptr gs:[188h]
fffff800`244e03c6 e8a55396ff call nt!KiLeaveGuardedRegionUnsafe (fffff800`23e45770)
fffff800`244e03cb 0fb74302 movzx eax,word ptr [rbx+2]
fffff800`244e03cf ffc6 inc esi
fffff800`244e03d1 3bf0 cmp esi,eax
fffff800`244e03d3 7285 jb nt!ObUnRegisterCallbacks+0x1a (fffff800`244e035a) Branch
nt!ObUnRegisterCallbacks+0x95:
fffff800`244e03d5 ba4f62466c mov edx,6C46624Fh
fffff800`244e03da 488bcb mov rcx,rbx
fffff800`244e03dd e8ce6c0d00 call nt!ExFreePool (fffff800`245b70b0)
fffff800`244e03e2 488b5c2430 mov rbx,qword ptr [rsp+30h]
fffff800`244e03e7 488b742438 mov rsi,qword ptr [rsp+38h]
fffff800`244e03ec 4883c420 add rsp,20h
fffff800`244e03f0 5f pop rdi
fffff800`244e03f1 c3 ret
nt!ObUnRegisterCallbacks+0xb3:
fffff800`244e03f3 b903000000 mov ecx,3
fffff800`244e03f8 cd29 int 29h
fffff800`244e03fa cc int 3
fffff800`244e03fb cc int 3
fffff800`244e03fc cc int 3
fffff800`244e03fd cc int 3
fffff800`244e03fe cc int 3
fffff800`244e03ff cc int 3
fffff800`244e0400 48895c2408 mov qword ptr [rsp+8],rbx
fffff800`244e0405 48896c2410 mov qword ptr [rsp+10h],rbp
fffff800`244e040a 4889742418 mov qword ptr [rsp+18h],rsi
fffff800`244e040f 57 push rdi
fffff800`244e0410 4883ec20 sub rsp,20h
fffff800`244e0414 488bfa mov rdi,rdx
fffff800`244e0417 488be9 mov rbp,rcx
nt!ObpCallPostOperationCallbacks+0x1a:
fffff800`244e041a 48393f cmp qword ptr [rdi],rdi
fffff800`244e041d 7455 je nt!ObpCallPostOperationCallbacks+0x74 (fffff800`244e0474) Branch
nt!ObpCallPostOperationCallbacks+0x1f:
fffff800`244e041f 488b7708 mov rsi,qword ptr [rdi+8]
fffff800`244e0423 48393e cmp qword ptr [rsi],rdi
fffff800`244e0426 7545 jne nt!ObpCallPostOperationCallbacks+0x6d (fffff800`244e046d) Branch
nt!ObpCallPostOperationCallbacks+0x28:
fffff800`244e0428 488b4608 mov rax,qword ptr [rsi+8]
fffff800`244e042c 483930 cmp qword ptr [rax],rsi
fffff800`244e042f 753c jne nt!ObpCallPostOperationCallbacks+0x6d (fffff800`244e046d) Branch
nt!ObpCallPostOperationCallbacks+0x31:
fffff800`244e0431 48894708 mov qword ptr [rdi+8],rax
fffff800`244e0435 488bd5 mov rdx,rbp
fffff800`244e0438 488938 mov qword ptr [rax],rdi
fffff800`244e043b 488b5e10 mov rbx,qword ptr [rsi+10h]
fffff800`244e043f 488b4618 mov rax,qword ptr [rsi+18h]
fffff800`244e0443 48894518 mov qword ptr [rbp+18h],rax
fffff800`244e0447 488b4b18 mov rcx,qword ptr [rbx+18h]
fffff800`244e044b 488b4330 mov rax,qword ptr [rbx+30h]
fffff800`244e044f 488b4908 mov rcx,qword ptr [rcx+8]
fffff800`244e0453 e8e876b2ff call nt!guard_dispatch_icall (fffff800`24007b40)
fffff800`244e0458 488d4b38 lea rcx,[rbx+38h]
fffff800`244e045c e81fbf96ff call nt!ExReleaseRundownProtection (fffff800`23e4c380)
fffff800`244e0461 33d2 xor edx,edx
fffff800`244e0463 488bce mov rcx,rsi
fffff800`244e0466 e8456c0d00 call nt!ExFreePool (fffff800`245b70b0)
fffff800`244e046b ebad jmp nt!ObpCallPostOperationCallbacks+0x1a (fffff800`244e041a) Branch
nt!ObpCallPostOperationCallbacks+0x6d:
fffff800`244e046d b903000000 mov ecx,3
fffff800`244e0472 cd29 int 29h
nt!ObpCallPostOperationCallbacks+0x74:
fffff800`244e0474 488b4d08 mov rcx,qword ptr [rbp+8]
fffff800`244e0478 ba4f624362 mov edx,6243624Fh
fffff800`244e047d e8be5696ff call nt!ObfDereferenceObjectWithTag (fffff800`23e45b40)
fffff800`244e0482 e8295996ff call nt!KeLeaveCriticalRegion (fffff800`23e45db0)
fffff800`244e0487 488b5c2430 mov rbx,qword ptr [rsp+30h]
fffff800`244e048c 33c0 xor eax,eax
fffff800`244e048e 488b6c2438 mov rbp,qword ptr [rsp+38h]
fffff800`244e0493 488b742440 mov rsi,qword ptr [rsp+40h]
fffff800`244e0498 4883c420 add rsp,20h
fffff800`244e049c 5f pop rdi
fffff800`244e049d c3 ret
分析思路
首先看开头部分。代码开始保存寄存器和分配栈空间,接着初始化esi为0,把rcx(第一个参数)赋值给rbx。比较si和[rbx+2]的值,如果si大于等于的话,跳转到释放内存的部分。这可能是在检查回调项的数量是否处理完毕。
接下来,循环部分处理每个回调项。使用shl rax,6和add rdi,rax来计算每个回调项的位置,大概是每个项的大小为0x40(64字节)。然后调用ExWaitForRundownProtectionRelease,这可能是在等待引用计数归零。然后获取当前线程,减少资源计数,进入临界区,获取PushLock,操作链表来移除回调项,释放锁,离开临界区。循环直到所有项处理完,最后释放内存。
然后看ObpCallPostOperationCallbacks部分。这部分在遍历链表,调用每个回调的后期处理函数。通过比较链表是否为空来决定是否继续。取出链表中的每个节点,进行验证,调用guard_dispatch_icall执行回调函数,释放资源,并释放内存。最后释放对象引用,离开临界区。
这个逆向还原度可能没上一个高。
ObRegisterCallbacks与ObUnRegisterCallbacks的辅助函数
1.MmVerifyCallbackFunctionCheckFlags
BOOLEAN MmVerifyCallbackFunctionCheckFlags(PVOID Address, ULONG Flags)
{
KTHREAD* CurrentThread;
BOOLEAN Result = FALSE;
PMMPTE DataTableEntry;
// 若为1则直接返回FALSE
if (MiGetSystemRegionType(Address) == 1) {
return FALSE;
}
// 进入临界区
CurrentThread = KeGetCurrentThread();
InterlockedDecrement16((SHORT*)&CurrentThread->ResourceCount);
// 以共享模式获取已加载模块资源锁
ExAcquireResourceSharedLite(&PsLoadedModuleResource, TRUE);
// 查找数据表条目
DataTableEntry = MiLookupDataTableEntry(Address, 0);
if (DataTableEntry != NULL) {
// 根据标志位检查结果
if (Flags == 0) {
// Flags为0时,存在条目即通过
Result = TRUE;
} else {
// 检查条目标志位与输入Flags的匹配
ULONG EntryFlags = DataTableEntry->Flags;
if ((EntryFlags & Flags) != 0) {
Result = TRUE;
}
}
}
// 释放资源锁并恢复线程状态
ExReleaseResourceLite(&PsLoadedModuleResource);
KeLeaveCriticalRegionThread(CurrentThread);
return Result;
}
分析思路
开头是保存寄存器的指令,把rbx、rbp、rsi压入栈中,然后push rdi,接着调整rsp分配栈空间。这些操作通常在函数开头。
接下来,mov edi, edx 和 mov rsi, rcx,这里应该是将传入的参数保存到edi和rsi中,然后调MiGetSystemRegionType,结果保存在eax中。比较eax和1,如果相等就跳转到地址fffff800`23faf75e,将eax清零然后跳转到结尾返回。这说明如果MiGetSystemRegionType返回1,函数直接返回0。
如果比较结果不相等(即eax不等于1),则继续执行后面的代码。这里从地址fffff800`23faf6f3开始,读取gs:[188h],也就是当前线程的KTHREAD结构,保存到rbp。然后xor ebx, ebx,将ebx清零,用来作为返回值或者中间变量。接着对[rbp+1E4h]处的字减1,这可能是线程的某个计数器,比如资源计数或递归计数。然后mov dl,1,准备调用ExAcquireResourceSharedLite,传入rcx为PsLoadedModuleResource,dl为1(表示等待资源可用)。
接下来调用MiLookupDataTableEntry,参数是rsi(原rcx,即第一个参数)和edx(此时为0)。检查返回的rax是否为NULL,如果是,跳到后面释放资源的部分。否则,测试edi(原edx,第二个参数)是否为0,如果是,设置ebx为1。否则,检查rax+68h处的值与edi是否相交,如果相交则ebx保持0,否则设为1。之后释放资源,调用ExReleaseResourceLite,然后恢复线程状态,返回ebx的值。
MiLookupDataTableEntry
MiLookupDataTableEntry的原型返回一个指向数据表条目结构的指针,该结构包含Flags字段在偏移0x68处。
如何确定就是Flags呢?
Windbg里dt一下LDR_DATA_TABLE_ENTRY
kd> dt _LDR_DATA_TABLE_ENTRY
+0x068 Flags
只要Flags给一个非0值,就会检查,而ObRegisterCallbacks传的是硬编码0x20,这便是许多帖子都说要将DriverSection的Flag值设为0x20的原因。
因此绕过方法就是Flags值设为0x20。这个更改是通过修改LDR_DATA_TABLE_ENTRY的结构体实现的,后面有分析和示例代码。
2.ObpInsertCallbackByAltitude,Altitude排序插入链表
NTSTATUS ObpInsertCallbackByAltitude(POB_CALLBACK_ENTRY Entry)
{
PLIST_ENTRY PrevEntry;
KeEnterCriticalRegion();
ExAcquirePushLockExclusiveEx(&ObCallbackLock, 0);
// 遍历链表找到合适的位置
LIST_FOR_EACH(PrevEntry, &ObCallbackList) {
POB_CALLBACK_ENTRY Current = CONTAINING_RECORD(PrevEntry, OB_CALLBACK_ENTRY, Link);
if (Entry->Altitude < Current->Altitude) {
break;
}
}
// 插入到PrevEntry之后
InsertTailList(PrevEntry, &Entry->Link);
ExReleasePushLockExclusiveEx(&ObCallbackLock);
KeLeaveCriticalRegion();
return STATUS_SUCCESS;
}
这个Altitude在微软的说明文档中也是模棱两可,测试开发填321000应该可以吧(怀疑),正式的应该要向微软申请。
Altitude的具体就没逆了
3. ObpCallPostOperationCallbacks后操作回调处理,ValidateLinkedList链表完整性校验函数
VOID ObpCallPostOperationCallbacks(PVOID OperationParams, PLIST_ENTRY CallbackList) {
while (!IsListEmpty(CallbackList)) {
PLIST_ENTRY EntryLink = RemoveHeadList(CallbackList);
POB_CALLBACK_ENTRY Entry = CONTAINING_RECORD(EntryLink, OB_CALLBACK_ENTRY, Link);
// 执行后操作回调
if (Entry->PostOperation) {
((POB_POST_OPERATION_CALLBACK)Entry->PostOperation)(
Entry->RegistrationContext,
OperationParams
);
}
// 释放运行保护
ExReleaseRundownProtection(&Entry->RundownProtect);
// 释放回调项内存
ExFreePoolWithTag(Entry, 'CbOp');
}
// 释放操作参数对象
ObfDereferenceObjectWithTag(OperationParams, 'TagO');
KeLeaveCriticalRegion();
}
BOOLEAN ValidateLinkedList(POB_CALLBACK_ENTRY Entry) {
PLIST_ENTRY Prev = Entry->Link.Blink;
PLIST_ENTRY Next = Entry->Link.Flink;
// 检查前后指针一致性
return (Prev->Flink == &Entry->Link) &&
(Next->Blink == &Entry->Link);
}
这几个的分析思路和ObRegisterCallbacks一致
以下的结构体完全依照Windbg的符号分析
4.LDR_DATA_TABLE_ENTRY结构体
typedef struct _LDR_DATA_TABLE_ENTRY {
// 一些链表
LIST_ENTRY InLoadOrderLinks; // +0x000
LIST_ENTRY InMemoryOrderLinks; // +0x010
LIST_ENTRY InInitializationOrderLinks; // +0x020
// EntryPoint之类
PVOID DllBase; // +0x030
PVOID EntryPoint; // +0x038
ULONG SizeOfImage; // +0x040
// 相关信息
UNICODE_STRING FullDllName; // +0x048
UNICODE_STRING BaseDllName; // +0x058
//将这个未命名联合命名为FlagBitfield
//这就是Flags的位置
union _FlagBitfield {
UCHAR FlagGroup[4]; // +0x068
ULONG Flags;
struct _FlagStruct{
ULONG PackagedBinary : 1; // Bit 0
ULONG MarkedForRemoval : 1; // Bit 1
ULONG ImageDll : 1; // Bit 2
ULONG LoadNotificationsSent : 1; // Bit 3
ULONG TelemetryEntryProcessed : 1; // Bit 4
ULONG ProcessStaticImport : 1; // Bit 5
ULONG InLegacyLists : 1; // Bit 6
ULONG InIndexes : 1; // Bit 7
ULONG ShimDll : 1; // Bit 8
ULONG InExceptionTable : 1; // Bit 9
ULONG ReservedFlags1 : 2; // Bits 10-11
ULONG LoadInProgress : 1; // Bit 12
ULONG LoadConfigProcessed : 1; // Bit 13
ULONG EntryProcessed : 1; // Bit 14
ULONG ProtectDelayLoad : 1; // Bit 15
ULONG ReservedFlags3 : 2; // Bits 16-17
ULONG DontCallForThreads : 1; // Bit 18
ULONG ProcessAttachCalled : 1; // Bit 19
ULONG ProcessAttachFailed : 1; // Bit 20
ULONG CorDeferredValidate : 1; // Bit 21
ULONG CorImage : 1; // Bit 22
ULONG DontRelocate : 1; // Bit 23
ULONG CorILOnly : 1; // Bit 24
ULONG ChpeImage : 1; // Bit 25
ULONG ReservedFlags5 : 2; // Bits 26-27
ULONG Redirected : 1; // Bit 28
ULONG ReservedFlags6 : 2; // Bits 29-30
ULONG CompatDatabaseProcessed : 1; // Bit 31
}FlagStruct;
}FlagBitfield;
// 不知道干什么的
USHORT ObsoleteLoadCount; // +0x06c
USHORT TlsIndex; // +0x06e
// 哈希表
LIST_ENTRY HashLinks; // +0x070
ULONG TimeDateStamp; // +0x080
// 上下文
PVOID EntryPointActivationContext; // +0x088
PVOID Lock; // +0x090
PVOID DdagNode; // +0x098 (LDR_DDAG_NODE)
LIST_ENTRY NodeModuleLink; // +0x0a0
// 上一级的基地址
PVOID LoadContext; // +0x0b0 (LDRP_LOAD_CONTEXT)
PVOID ParentDllBase; // +0x0b8
// 地址映射
PVOID SwitchBackContext; // +0x0c0
RTL_BALANCED_NODE BaseAddressIndexNode; // +0x0c8
RTL_BALANCED_NODE MappingInfoIndexNode; // +0x0e0
// 加载的时间
ULONGLONG OriginalBase; // +0x0f8
LARGE_INTEGER LoadTime; // +0x100
// 其他
ULONG BaseNameHashValue; // +0x108
ULONG LoadReason; // +0x10c (LDR_DLL_LOAD_REASON)
ULONG ImplicitPathOptions; // +0x110
ULONG ReferenceCount; // +0x114
ULONG DependentLoadFlags; // +0x118
UCHAR SigningLevel; // +0x11c
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
这便是绕过MmVerifyCallbackFunctionCheckFlags的突破口,将Flags的值设为0x20,示例如下。
BOOLEAN PassMmVerifyCallbackFunction(PDRIVER_OBJECT DriverObject)
{
BOOLEAN bStatus = FALSE;
PLDR_DATA_TABLE_ENTRY pLdrData = NULL;
pLdrData = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
if (pLdrData == NULL) {
DbgPrint("Error in convert PLDR_DATA_TABLE_ENTRY,DriverObject:%pn", DriverObject);
return bStatus;
}
pLdrData->FlagBitfield.Flags = pLdrData->FlagBitfield.Flags | 0x20;
bStatus = TRUE;
return bStatus;
}
效果
5.KLDR_DATA_TABLE_ENTRY
typedef struct _KLDR_DATA_TABLE_ENTRY {
typedef struct _KLDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks; // 链表 0x000
PVOID ExceptionTable; // SEH异常表指针 0x010
ULONG ExceptionTableSize; // 异常表大小0x018
PVOID GpValue; // 全局指针值(GP值)0x020
PVOID NonPagedDebugInfo; // 调试信息指针 0x028
PVOID DllBase; // 已加载模块基地址 0x030
PVOID EntryPoint; // 模块入口点地址0x038
ULONG SizeOfImage; // 映像大小 0x040
UNICODE_STRING FullDllName; // 完整DLL路径名 0x048
UNICODE_STRING BaseDllName; // 基DLL名称0x058
ULONG Flags; // 神奇的Flags 0x068
USHORT LoadCount; // 引用计数 0x06c
union { // 联合体 0x06e
USHORT SignatureLevel : 4; // 代码签名级别(4位)
USHORT SpareFlags : 12; // 保留标志位(12位)
} Unknown1;
PVOID SectionPointer; // 节对象指针 0x070
ULONG CheckSum; // 映像校验和 0x078
ULONG CoverageSectionSize; // 覆盖数据大小 0x07c
PVOID CoverageSection; // 覆盖数据指针 0x080
PVOID LoadedImports; // 描述符指针 0x088
PVOID Spare; // 保留偏移 0x090
ULONG SizeOfImageNotRounded; // 原始映像大小(未对齐) 0x098
ULONG TimeDateStamp; // PE头时间戳 0x09c
} KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY;
这个在一些其他函数中有引用,所以分析一下结构即可。
6.OB_CALLBACK_ENTRY
typedef struct _OB_CALLBACK_ENTRY {
LIST_ENTRY Link; // 链表
PVOID PreOperation; // 预操作回调
PVOID PostOperation; // 后操作回调
PVOID RegistrationContext; // 上下文
EX_PUSH_LOCK Lock; // 锁保护结构
EX_RUNDOWN_REF RundownProtect; // 运行保护
} OB_CALLBACK_ENTRY, *POB_CALLBACK_ENTRY;
这个在ObUnRegisterCallbacks有用到。
总结
以上的测试分析基于Win10 build19041进行,欢迎各位指正。
看雪ID:TurkeybraNC
https://bbs.kanxue.com/user-home-982720.htm
#
原文始发于微信公众号(看雪学苑):逆向分析:Win10 ObRegisterCallbacks的相关分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论