Hi all,
I’m struggling to understand a bugcheck I’m seeing in a filter driver I own.
I only have access to a crash dump, so I’m currently doing a post-mortem on
it.
I’m hitting null pointer in RtlEqualSid, however value is valid when it’s
passed into its parent function.
My x64 assembler isn’t the best it could be, but I’ve added some debug info
below in the hope that someone can help me understand what’s happening.
Thanks in advance.
// In the call to RtlEqualSid:
// rcx is ffffdf8f`26ccc5a0
// rdx is 0000000000000000
// Something is resetting rbx to 0?
// offending assembly
fffff803ffdc21cf 488b5d28 mov rbx,qword ptr [rbp+28h] // \<--- poi(rbp+28) is ffffdf8f
280c1a60
fffff803ffdc21d3 4c8d442448 lea r8,[rsp+48h] fffff803
ffdc21d8 4c89ac24a0000000 mov qword ptr [rsp+0A0h],r13
fffff803ffdc21e0 498bce mov rcx,r14 fffff803
ffdc21e3 41bd01000000 mov r13d,1
fffff803ffdc21e9 418bd5 mov edx,r13d fffff803
ffdc21ec ff15c6d00000 call qword ptr
[AMFileSystemFilter!_imp_SeQueryInformationToken (fffff803ffdcf2b8)] fffff803
ffdc21f2 448bc0 mov r8d,eax
fffff803ffdc21f5 85c0 test eax,eax fffff803
ffdc21f7 7838 js
AMFileSystemFilter!HandleOwnershipCheckOnWrite+0x161 (fffff803ffdc2231) fffff803
ffdc21f9 488b4c2448 mov rcx,qword ptr [rsp+48h]
fffff803ffdc21fe 488bd3 mov rdx,rbx // \<---- rbx is 0 here fffff803
ffdc2201 488b09 mov rcx,qword ptr [rcx]
fffff803ffdc2204 ff15ced00000 call qword ptr [AMFileSystemFilter!_imp_RtlEqualSid (fffff803
ffdcf2d8)]
**BANG**
1: kd> k
*** Stack trace for last set context - .thread/.cxr resets it
Child-SP RetAddr Call Site
00 ffff96018de6c6a0 fffff803
ffdc220a nt!RtlEqualSid+0x4
01 (Inline Function) ---------------- AMFileSystemFilter!IsTokenFileOwner+0x37 [f:\builds\211\am\f_vnext_full\sources\am\source\amfilterdrivers\amfilesyste mfilter\trustedowner.c @ 102] 02 ffff9601
8de6c6d0 fffff803ffdd4c67 AMFileSystemFilter!HandleOwnershipCheckOnWrite+0x13a [f:\builds\211\am\f_vnext_full\sources\am\source\amfilterdrivers\amfilesyste mfilter\callbacks.c @ 1560] 03 ffff9601
8de6c770 fffff803fd1246ca AMFileSystemFilter!AmFltPreWrite+0x167 [f:\builds\211\am\f_vnext_full\sources\am\source\amfilterdrivers\amfilesyste mfilter\callbacks.c @ 869] 04 ffff9601
8de6c7f0 fffff803fd124278 FLTMGR!FltpPerformPreCallbacks+0x2ea 05 ffff9601
8de6c900 fffff803`fd123386 FLTMGR!FltpPassThroughInternal+0x88
…
1: kd> r
Last set context:
rax=0000000000000000 rbx=0000000000000000 rcx=ffffdf8f26ccc5a0
rdx=0000000000000000 rsi=8000000000000000 rdi=000000000000136c
rip=fffff801a502e6c4 rsp=ffff96018de6c6a0 rbp=ffffc0896a426b00
// instruction that sets rbx is : mov rbx,qword ptr [rbp+28h]
// that address contains a valid pointer, so rbx is valid here
1: kd> dd poi(rbp+0x28)
ffffdf8f280c1a60 00000101 05000000 00000012 00000000 ffffdf8f
280c1a70 03180102 6e664d46 093054c3 db9fc9b8
ffffdf8f280c1a80 016cf204 00000000 69b90010 ffffc089 ffffdf8f
280c1a90 000000ee 00000000 28102618 ffffdf8f
ffffdf8f280c1aa0 00000000 00000000 00000000 00000000 ffffdf8f
280c1ab0 6a6196f0 ffffc089 6a5db250 ffffc089
ffffdf8f280c1ac0 69b90010 ffffc089 00014000 00000000 ffffdf8f
280c1ad0 00000078 00000002 009c009a 00000000
1: kd> !sid ffffdf8f`280c1a60
SID is: S-1-5-18
// This is the struct that contains the FileOwner that’s stored in rbx
before it’s 0
1: kd> dt _UCA_FILE_CONTEXT 0xffffc0896a426b00 AMFileSystemFilter!_UCA_FILE_CONTEXT +0x000 Lock : 0 +0x008 Flags : 0 +0x010 FileName : _UNICODE_STRING "\Device\Mup\infrastore\PIPE\srvsvc" +0x020 CleanupCallback : 0xfffff803
ffdc20c0 void
AMFileSystemFilter!AmCtxStreamContextCleanupCallback+0
+0x028 FileOwner : 0xffffdf8f`280c1a60 Void <— this is what’s
stored in rbx (rbp+28h)
+0x030 ContainsStreams : 0 ‘’
+0x031 IsDirectory : 0 ‘’
+0x032 FileIsManaged : 0 ‘’
// Looking inside SeQueryInformationToken shows that rbx is saved to rsp+18
and restored from rsp+140??
1: kd> u nt!SeQueryInformationToken
nt!SeQueryInformationToken:
fffff801a541ffa0 48895c2418 mov qword ptr [rsp+18h],rbx fffff801
a541ffa5 55 push rbp
fffff801a541ffa6 56 push rsi fffff801
a541ffa7 57 push rdi
fffff801a541ffa8 4154 push r12 ... fffff801
a5420008 418918 mov dword ptr [r8],ebx
fffff801a542000b 33c0 xor eax,eax fffff801
a542000d 488b9c2440010000 mov rbx,qword ptr [rsp+140h]
fffff801a5420015 4881c4f0000000 add rsp,0F0h fffff801
a542001c 415f pop r15
…
//
// Code for the above
//
NTSTATUS
HandleOwnershipCheckOnWrite(In PFLT_INSTANCE Instance,
In PFILE_OBJECT FileObject,
In PUCA_FILE_CONTEXT FileContext,
In HANDLE ProcessId,
In HANDLE ThreadId)
{
PACCESS_TOKEN CallingContextToken;
ERULE_RESULT RuleResult;
BOOLEAN ChangeOwnership;
BOOLEAN CallerIsTrusted;
BOOLEAN IsFileOwner;
NTSTATUS Status;
RuleResult = RULE_DENY;
ChangeOwnership = FALSE;
/*
* The context can change from operation to operation due to
impersonation,
* let’s start by looking up the sid for the user that initiated this
call
*/
CallingContextToken = GetTokenFromCallingContext();
/* Check if the caller is trusted or is a member of a group that is */
Status = IsCallerTrusted(CallingContextToken,
&CallerIsTrusted);
if (!NT_SUCCESS(Status))
{
TRACE_ERROR(TraceHandle, “Failed to check if the caller is trusted :
0x%X”, Status);
}
/* Only trusted owners and callers who own the file are allowed to make
changes */
if (CallerIsTrusted == TRUE)
{
/* We allow this request to go through */
RuleResult = RULE_ALLOW;
return STATUS_SUCCESS;
}
/* Check if the caller owns the file */
Status = IsTokenFileOwner(CallingContextToken,
FileContext->FileOwner,
&IsFileOwner);
if (!NT_SUCCESS(Status))
{
TRACE_ERROR(TraceHandle, “Failed to check if the caller is file
owner : 0x%X”, Status);
}
…
NTSTATUS
IsTokenFileOwner(In PACCESS_TOKEN AccessToken,
In PSID FileOwner,
Out PBOOLEAN IsOwner)
{
PTOKEN_USER CallerTokenUser;
NTSTATUS Status;
FLT_ASSERT(FileOwner);
*IsOwner = FALSE;
/* Get the token user for this security context */
Status = SeQueryInformationToken(AccessToken,
TokenUser,
&CallerTokenUser);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Check if the sids match */
if (RtlEqualSid(CallerTokenUser->User.Sid, FileOwner))
{
/* It is, set the flag */
*IsOwner = TRUE;
}
/* Free the pool, it’s owned by the security manager */
ExFreePoolWithTag(CallerTokenUser, ’ eS’);
return STATUS_SUCCESS;
}