You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
592 lines
19 KiB
592 lines
19 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
token.c
|
|
|
|
Abstract:
|
|
|
|
WinDbg Extension Api
|
|
|
|
Author:
|
|
|
|
Ramon J San Andres (ramonsa) 8-Nov-1993
|
|
|
|
Environment:
|
|
|
|
User Mode.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
DECLARE_API( logonsession )
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This extension will dump all logon sessions in the system, or the specified session.
|
|
|
|
Arguments
|
|
|
|
!logonsession LUID InfoLevel
|
|
where luid is the session to dump (or 0 for all sessions) and levels are 1-4.
|
|
|
|
Return Value
|
|
|
|
S_OK or E_INVALIDARG
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Arguments
|
|
//
|
|
|
|
ULONG64 LogonId = 0;
|
|
ULONG Level = 0;
|
|
BOOLEAN bAll = FALSE;
|
|
|
|
//
|
|
// ntoskrnl globals
|
|
//
|
|
|
|
ULONG64 NtBuildNumber = 0;
|
|
ULONG64 SepLogonSessions = 0;
|
|
ULONG64 SepTokenLeakTracking = 0;
|
|
|
|
//
|
|
// for manipulating the LogonSessions tables
|
|
//
|
|
|
|
ULONG64 SessionList = 0;
|
|
ULONG64 CurrentSession = 0;
|
|
ULONG64 NextSession = 0;
|
|
|
|
//
|
|
// for manipulating a particular session
|
|
//
|
|
|
|
ULONG64 TokenListHead = 0;
|
|
ULONG64 NextTokenLink = 0;
|
|
ULONG64 CurrentTokenLink = 0;
|
|
ULONG64 Token = 0;
|
|
ULONG References = 0;
|
|
ULONG TokenCount = 0;
|
|
ULONG SessionCount = 0;
|
|
LUID SessionLuid = {0};
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN Found = FALSE;
|
|
BOOLEAN bChecked = FALSE;
|
|
LONG PointerSize = 0;
|
|
|
|
#define DUMP_TOKENS 0x1
|
|
#define DUMP_SOME_TOKEN_INFO 0x2
|
|
#define DUMP_LOTS_OF_TOKEN_INFO 0x3
|
|
#define DUMP_TONS_OF_TOKEN_INFO 0x4
|
|
#define VALID_SESSION_ADDRESS(x) (SepLogonSessions <= (x) && (x) <= (SepLogonSessions + 0xf * sizeof(ULONG_PTR)))
|
|
|
|
if (strlen(args) < 1)
|
|
{
|
|
dprintf("usage: !logonsession LogonId [InfoLevel 1-4]\n");
|
|
dprintf("\tuse LogonId = 0 to list all sessions\n");
|
|
dprintf("\n\texample: \"!logonsession 3e7 1\" displays system session and all system tokens.\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!GetExpressionEx(
|
|
args,
|
|
&LogonId,
|
|
&args))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (args && *args)
|
|
{
|
|
Level = (ULONG) GetExpression(args);
|
|
}
|
|
|
|
if (LogonId == 0)
|
|
{
|
|
bAll = TRUE;
|
|
dprintf("\nDumping all logon sessions.\n\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("\nSearching for logon session with ID = 0x%x\n\n", LogonId);
|
|
}
|
|
|
|
//
|
|
// read in relevant variables
|
|
//
|
|
|
|
SepLogonSessions = GetPointerValue("nt!SepLogonSessions");
|
|
NtBuildNumber = GetPointerValue("nt!NtBuildNumber");
|
|
|
|
//
|
|
// This is bad but I don't know the right way to figure this out.
|
|
//
|
|
|
|
if ((SepLogonSessions & 0xffffffff00000000) == 0xffffffff00000000)
|
|
{
|
|
PointerSize = 4;
|
|
}
|
|
else
|
|
{
|
|
PointerSize = 8;
|
|
}
|
|
|
|
//
|
|
// See if this is a checked build.
|
|
//
|
|
|
|
if (((ULONG)NtBuildNumber & 0xF0000000) == 0xC0000000)
|
|
{
|
|
bChecked = TRUE;
|
|
|
|
//
|
|
// It is a checked build, so see the value of SepTokenLeakTracking (valid symbol only on chk)
|
|
//
|
|
|
|
SepTokenLeakTracking = GetPointerValue("nt!SepTokenLeakTracking");
|
|
|
|
if (SepTokenLeakTracking)
|
|
{
|
|
dprintf("TokenLeakTracking is ON. Use !tokenleak to view settings.\n\n");
|
|
}
|
|
}
|
|
|
|
SessionList = SepLogonSessions;
|
|
|
|
if (!bAll)
|
|
{
|
|
//
|
|
// we want a particular index into the table
|
|
//
|
|
|
|
SessionList += PointerSize * (LogonId & 0xf);
|
|
}
|
|
|
|
//
|
|
// SessionList currently points at the beginning of a list indexed in SepLogonSessions.
|
|
// Dump it out, printing either all of the entries or only one with a matching LUID.
|
|
//
|
|
|
|
do
|
|
{
|
|
if (CheckControlC())
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
CurrentSession = GetPointerFromAddress(SessionList);
|
|
|
|
while (0 != CurrentSession)
|
|
{
|
|
if (CheckControlC())
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Get the LUID for the CurrentSession
|
|
//
|
|
|
|
GetFieldValue(CurrentSession, "SEP_LOGON_SESSION_REFERENCES", "LogonId", SessionLuid);
|
|
|
|
//
|
|
// if caller wants all sessions, or this one, print it.
|
|
//
|
|
|
|
if (bAll || SessionLuid.LowPart == LogonId)
|
|
{
|
|
Found = TRUE;
|
|
GetFieldValue(CurrentSession, "SEP_LOGON_SESSION_REFERENCES", "ReferenceCount", References);
|
|
|
|
if (bAll)
|
|
{
|
|
dprintf("** Session %3d = 0x%x\n", SessionCount, CurrentSession);
|
|
}
|
|
else
|
|
{
|
|
dprintf("** Session = 0x%x\n", CurrentSession);
|
|
}
|
|
|
|
dprintf(" LogonId = {0x%x 0x%x}\n", SessionLuid.LowPart, SessionLuid.HighPart);
|
|
dprintf(" References = %d\n", References);
|
|
|
|
SessionCount++;
|
|
|
|
//
|
|
// If Level dictates then print out lots more stuff.
|
|
//
|
|
|
|
if (Level != 0)
|
|
{
|
|
if (bChecked == FALSE)
|
|
{
|
|
dprintf("\nNo InfoLevels are valid on free builds.\n");
|
|
}
|
|
else
|
|
{
|
|
TokenCount = 0;
|
|
|
|
//
|
|
// retrieve the token list from the session
|
|
//
|
|
|
|
if (0 == GetFieldValue(
|
|
CurrentSession,
|
|
"SEP_LOGON_SESSION_REFERENCES",
|
|
"TokenList",
|
|
TokenListHead
|
|
))
|
|
{
|
|
|
|
CurrentTokenLink = TokenListHead;
|
|
|
|
do
|
|
{
|
|
if (CheckControlC())
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
GetFieldValue(
|
|
CurrentTokenLink,
|
|
"SEP_LOGON_SESSION_TOKEN",
|
|
"Token",
|
|
Token
|
|
);
|
|
|
|
GetFieldValue(
|
|
CurrentTokenLink,
|
|
"SEP_LOGON_SESSION_TOKEN",
|
|
"ListEntry",
|
|
NextTokenLink
|
|
);
|
|
|
|
if (NextTokenLink != TokenListHead)
|
|
{
|
|
if (TokenCount == 0)
|
|
{
|
|
|
|
ULONG DefaultOwnerIndex = 0;
|
|
ULONG64 UserAndGroups = 0;
|
|
UNICODE_STRING SidString = {0};
|
|
ULONG64 SidAttr = 0;
|
|
ULONG64 pSid = 0;
|
|
ULONG ActualRead = 0;
|
|
UCHAR Buffer[256];
|
|
|
|
GetFieldValue(Token, "nt!_TOKEN", "DefaultOwnerIndex", DefaultOwnerIndex);
|
|
GetFieldValue(Token, "nt!_TOKEN", "UserAndGroups", UserAndGroups);
|
|
|
|
SidAttr = UserAndGroups + (DefaultOwnerIndex * sizeof(SID_AND_ATTRIBUTES));
|
|
|
|
GetFieldValue(SidAttr, "_SID_AND_ATTRIBUTES", "Sid", pSid);
|
|
|
|
dprintf(" Usersid = 0x%x ", pSid);
|
|
|
|
ReadMemory(pSid, Buffer, sizeof(Buffer), &ActualRead);
|
|
|
|
Status = RtlConvertSidToUnicodeString(&SidString, (PSID)Buffer, TRUE);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
dprintf("(%S)\n", SidString.Buffer);
|
|
RtlFreeUnicodeString(&SidString);
|
|
}
|
|
else
|
|
{
|
|
dprintf("!! RtlConvertSidToUnicodeString failed 0x%x\n", Status);
|
|
}
|
|
|
|
dprintf(" Tokens:\n");
|
|
}
|
|
|
|
TokenCount++;
|
|
CurrentTokenLink = NextTokenLink;
|
|
dprintf(" 0x%x ", Token);
|
|
|
|
if (Level > DUMP_TOKENS)
|
|
{
|
|
UCHAR ImageFileName[16];
|
|
ULONG BodyOffset = 0;
|
|
ULONG64 ProcessCid = 0;
|
|
ULONG64 ObjectHeader = 0;
|
|
ULONG64 HandleCount = 0;
|
|
ULONG64 PointerCount = 0;
|
|
|
|
GetFieldValue(Token, "nt!_TOKEN", "ImageFileName", ImageFileName);
|
|
GetFieldValue(Token, "nt!_TOKEN", "ProcessCid", ProcessCid);
|
|
|
|
dprintf(": %13s (%3x) ", ImageFileName, ProcessCid);
|
|
|
|
GetFieldOffset("nt!_OBJECT_HEADER", "Body", &BodyOffset);
|
|
ObjectHeader = Token - BodyOffset;
|
|
GetFieldValue(ObjectHeader, "nt!_OBJECT_HEADER", "PointerCount", PointerCount);
|
|
GetFieldValue(ObjectHeader, "nt!_OBJECT_HEADER", "HandleCount", HandleCount);
|
|
|
|
dprintf(": HandleCount = 0x%I64x PointerCount = 0x%I64x ", HandleCount, PointerCount);
|
|
}
|
|
|
|
if (Level > DUMP_SOME_TOKEN_INFO)
|
|
{
|
|
|
|
ULONG CreateMethod = 0;
|
|
|
|
GetFieldValue(Token, "nt!_TOKEN", "CreateMethod", CreateMethod);
|
|
|
|
switch (CreateMethod)
|
|
{
|
|
case 0xD:
|
|
dprintf(": SepDuplicateToken ");
|
|
break;
|
|
case 0xC:
|
|
dprintf(": SepCreateToken ");
|
|
break;
|
|
case 0xF:
|
|
dprintf(": SepFilterToken ");
|
|
break;
|
|
default:
|
|
dprintf(": Unknown Method ");
|
|
break;
|
|
}
|
|
|
|
if (Level > DUMP_LOTS_OF_TOKEN_INFO)
|
|
{
|
|
ULONG TokenType = 0;
|
|
ULONG ImpersonationLevel = 0;
|
|
ULONG SessionId = 0;
|
|
ULONG Count = 0;
|
|
|
|
GetFieldValue(Token, "nt!_TOKEN", "TokenType", TokenType);
|
|
GetFieldValue(Token, "nt!_TOKEN", "ImpersonationLevel", ImpersonationLevel);
|
|
GetFieldValue(Token, "nt!_TOKEN", "SessionId", SessionId);
|
|
GetFieldValue(Token, "nt!_TOKEN", "Count", Count);
|
|
|
|
dprintf(": Type %d ", TokenType);
|
|
dprintf(": Level %d ", ImpersonationLevel);
|
|
dprintf(": SessionId %d ", SessionId);
|
|
if (SepTokenLeakTracking)
|
|
{
|
|
dprintf(": MethodCount = %3x ", Count);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
} while (NextTokenLink != TokenListHead);
|
|
|
|
dprintf(" %d Tokens listed.\n\n", TokenCount);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
GetFieldValue(CurrentSession, "SEP_LOGON_SESSION_REFERENCES", "Next", NextSession);
|
|
CurrentSession = NextSession;
|
|
}
|
|
|
|
if (bAll)
|
|
{
|
|
SessionList += sizeof(ULONG_PTR);
|
|
}
|
|
|
|
}
|
|
while (bAll && VALID_SESSION_ADDRESS(SessionList));
|
|
|
|
if (bAll)
|
|
{
|
|
dprintf("%d sessions in the system.\n", SessionCount);
|
|
}
|
|
|
|
if (!bAll && !Found)
|
|
{
|
|
dprintf("Session not found.\n");
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
DECLARE_API( tokenleak )
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
!tokenleak displays or sets the Se globals that facilitate tracking and finding token leaks.
|
|
|
|
Arguments
|
|
|
|
usage: !tokenleak [1 | 0 ProcessCid BreakCount MethodWatch]
|
|
where 1 activates and 0 disables token leak tracking
|
|
where ProcessCid is Cid of process to monitor (in hex)
|
|
where BreakCount specifies which numbered call to Method to break on (in hex)
|
|
where MethodWatch specifies which token method to watch (C D or F)
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Nt Globals.
|
|
//
|
|
|
|
ULONG64 SepTokenLeakTracking = 0;
|
|
ULONG64 SepTokenLeakMethodWatch = 0;
|
|
ULONG64 SepTokenLeakMethodCount = 0;
|
|
ULONG64 SepTokenLeakBreakCount = 0;
|
|
ULONG64 SepTokenLeakProcessCid = 0;
|
|
|
|
ULONG64 SepTokenLeakTrackingAddr = 0;
|
|
ULONG64 SepTokenLeakMethodWatchAddr = 0;
|
|
ULONG64 SepTokenLeakMethodCountAddr = 0;
|
|
ULONG64 SepTokenLeakBreakCountAddr = 0;
|
|
ULONG64 SepTokenLeakProcessCidAddr = 0;
|
|
|
|
ULONG64 NtBuildNumber = 0;
|
|
BOOLEAN bModify = TRUE;
|
|
|
|
ULONG64 InputOn = 0;
|
|
ULONG64 InputCid = 0;
|
|
ULONG64 InputMethodWatch = 0;
|
|
ULONG64 InputBreakCount = 0;
|
|
|
|
if (strlen(args) < 1)
|
|
{
|
|
dprintf("usage: !tokenleak [1 | 0 ProcessCid BreakCount MethodWatch]\n");
|
|
dprintf("\t where 1 activates and 0 disables token leak tracking\n");
|
|
dprintf("\t where ProcessCid is Cid of process to monitor (in hex)\n");
|
|
dprintf("\t where BreakCount specifies which numbered call to Method to break on (in hex)\n");
|
|
dprintf("\t where MethodWatch specifies which token method to watch (C D or F)\n\n");
|
|
bModify = FALSE;
|
|
}
|
|
|
|
NtBuildNumber = GetPointerValue("nt!NtBuildNumber");
|
|
|
|
if (((ULONG)NtBuildNumber & 0xf0000000) == 0xF0000000)
|
|
{
|
|
dprintf("This extension only works on checked builds.\n");
|
|
return S_OK;
|
|
}
|
|
|
|
if (bModify)
|
|
{
|
|
SepTokenLeakTrackingAddr = GetExpression("SepTokenLeakTracking");
|
|
SepTokenLeakMethodWatchAddr = GetExpression("SepTokenLeakMethodWatch");
|
|
SepTokenLeakMethodCountAddr = GetExpression("SepTokenLeakMethodCount");
|
|
SepTokenLeakBreakCountAddr = GetExpression("SepTokenLeakBreakCount");
|
|
SepTokenLeakProcessCidAddr = GetExpression("SepTokenLeakProcessCid");
|
|
|
|
if (!GetExpressionEx(
|
|
args,
|
|
&InputOn,
|
|
&args))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
WritePointer(SepTokenLeakTrackingAddr, InputOn);
|
|
|
|
if (InputOn != 0)
|
|
{
|
|
dprintf("\nToken leak tracking is ON.\n\n");
|
|
|
|
while (args && (*args == ' '))
|
|
{
|
|
args++;
|
|
}
|
|
|
|
if (!GetExpressionEx(
|
|
args,
|
|
&InputCid,
|
|
&args))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
while (args && (*args == ' '))
|
|
{
|
|
args++;
|
|
}
|
|
|
|
if (!GetExpressionEx(
|
|
args,
|
|
&InputBreakCount,
|
|
&args))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
while (args && (*args == ' '))
|
|
{
|
|
args++;
|
|
}
|
|
|
|
if (!GetExpressionEx(
|
|
args,
|
|
&InputMethodWatch,
|
|
&args))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
WritePointer(SepTokenLeakProcessCidAddr, InputCid);
|
|
WritePointer(SepTokenLeakBreakCountAddr, InputBreakCount);
|
|
WritePointer(SepTokenLeakMethodCountAddr, 0);
|
|
WritePointer(SepTokenLeakMethodWatchAddr, InputMethodWatch);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Print out the current settings.
|
|
//
|
|
|
|
SepTokenLeakTracking = GetPointerValue("SepTokenLeakTracking");
|
|
SepTokenLeakMethodWatch = GetPointerValue("SepTokenLeakMethodWatch");
|
|
SepTokenLeakMethodCount = GetPointerValue("SepTokenLeakMethodCount");
|
|
SepTokenLeakBreakCount = GetPointerValue("SepTokenLeakBreakCount");
|
|
SepTokenLeakProcessCid = GetPointerValue("SepTokenLeakProcessCid");
|
|
|
|
if (SepTokenLeakTracking)
|
|
{
|
|
|
|
dprintf(" Currently watched method = ");
|
|
switch (SepTokenLeakMethodWatch)
|
|
{
|
|
case 0xD:
|
|
dprintf("SepDuplicateToken\n");
|
|
break;
|
|
case 0xC:
|
|
dprintf("SepCreateToken\n");
|
|
break;
|
|
case 0xF:
|
|
dprintf("SepFilterToken\n");
|
|
break;
|
|
default:
|
|
dprintf("???\n");
|
|
}
|
|
dprintf(" Currently watched process = 0x%x\n", SepTokenLeakProcessCid);
|
|
dprintf(" Method call count = 0x%x\n", SepTokenLeakMethodCount);
|
|
dprintf(" Will break at count = 0x%x\n", SepTokenLeakBreakCount);
|
|
}
|
|
else
|
|
{
|
|
dprintf("\nToken leak tracking is OFF\n");
|
|
}
|
|
return S_OK;
|
|
}
|