windows-nt/Source/XPSP1/NT/base/fs/ntfs/ntfskd/ntfskd.c
2020-09-26 16:20:57 +08:00

3732 lines
103 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
NtfsKd.c
Abstract:
KD Extension Api for examining Ntfs specific data structures
Author:
Keith Kaplan [KeithKa] 24-Apr-1996
Portions by Jeff Havens
Ported to IA64 (wesw) 5-Aug-2000
Environment:
User Mode.
Revision History:
--*/
#include "pch.h"
#undef FlagOn
#undef WordAlign
#undef LongAlign
#undef QuadAlign
#undef DebugPrint
#undef MAXULONGLONG
#define KDEXT
#include "gentable.h"
#undef DebugTrace
#include <cc.h>
#undef UpdateSequenceStructureSize
#undef UpdateSequenceArraySize
#include <lfsdisk.h>
#pragma hdrstop
#define AVERAGE(TOTAL,COUNT) ((COUNT) != 0 ? (TOTAL)/(COUNT) : 0)
const UCHAR FileSignature[4] = {'F', 'I', 'L', 'E'};
VOID
ResetFileSystemStatistics (
IN ULONG64 VcbAddress,
IN USHORT Processor,
IN HANDLE hCurrentThread
);
VOID
DumpFileSystemStatistics (
IN ULONG64 VcbAddress,
IN USHORT Processor,
IN HANDLE hCurrentThread
);
/*
* Dump structures
*/
typedef struct _STATE {
ULONG mask;
ULONG value;
CHAR *pszname;
} STATE;
STATE CcbState[] = {
{ CCB_FLAG_IGNORE_CASE, CCB_FLAG_IGNORE_CASE, "IgnoreCase"},
{ CCB_FLAG_OPEN_AS_FILE, CCB_FLAG_OPEN_AS_FILE, "OpenAsFile"},
{ CCB_FLAG_WILDCARD_IN_EXPRESSION, CCB_FLAG_WILDCARD_IN_EXPRESSION, "WildcardInExpression"},
{ CCB_FLAG_OPEN_BY_FILE_ID, CCB_FLAG_OPEN_BY_FILE_ID, "OpenByFileId"},
{ CCB_FLAG_USER_SET_LAST_MOD_TIME, CCB_FLAG_USER_SET_LAST_MOD_TIME, "SetLastModTime"},
{ CCB_FLAG_USER_SET_LAST_CHANGE_TIME, CCB_FLAG_USER_SET_LAST_CHANGE_TIME, "SetLastChangeTime"},
{ CCB_FLAG_USER_SET_LAST_ACCESS_TIME, CCB_FLAG_USER_SET_LAST_ACCESS_TIME, "SetLastAccessTime"},
{ CCB_FLAG_TRAVERSE_CHECK, CCB_FLAG_TRAVERSE_CHECK, "TraverseCheck"},
{ CCB_FLAG_RETURN_DOT, CCB_FLAG_RETURN_DOT, "ReturnDot"},
{ CCB_FLAG_RETURN_DOTDOT, CCB_FLAG_RETURN_DOTDOT, "ReturnDotDot"},
{ CCB_FLAG_DOT_RETURNED, CCB_FLAG_DOT_RETURNED, "DotReturned"},
{ CCB_FLAG_DOTDOT_RETURNED, CCB_FLAG_DOTDOT_RETURNED, "DotDotReturned"},
{ CCB_FLAG_DELETE_FILE, CCB_FLAG_DELETE_FILE, "DeleteFile"},
{ CCB_FLAG_DENY_DELETE, CCB_FLAG_DENY_DELETE, "DenyDelete"},
{ CCB_FLAG_ALLOCATED_FILE_NAME, CCB_FLAG_ALLOCATED_FILE_NAME, "AllocatedFileName"},
{ CCB_FLAG_CLEANUP, CCB_FLAG_CLEANUP, "Cleanup"},
{ CCB_FLAG_SYSTEM_HIVE, CCB_FLAG_SYSTEM_HIVE, "SystemHive"},
{ CCB_FLAG_PARENT_HAS_DOS_COMPONENT, CCB_FLAG_PARENT_HAS_DOS_COMPONENT, "ParentHasDosComponent"},
{ CCB_FLAG_DELETE_ON_CLOSE, CCB_FLAG_DELETE_ON_CLOSE, "DeleteOnClose"},
{ CCB_FLAG_CLOSE, CCB_FLAG_CLOSE, "Close"},
{ CCB_FLAG_UPDATE_LAST_MODIFY, CCB_FLAG_UPDATE_LAST_MODIFY, "UpdateLastModify"},
{ CCB_FLAG_UPDATE_LAST_CHANGE, CCB_FLAG_UPDATE_LAST_CHANGE, "UpdateLastChange"},
{ CCB_FLAG_SET_ARCHIVE, CCB_FLAG_SET_ARCHIVE, "SetArchive"},
{ CCB_FLAG_DIR_NOTIFY, CCB_FLAG_DIR_NOTIFY, "DirNotify"},
{ CCB_FLAG_ALLOW_XTENDED_DASD_IO, CCB_FLAG_ALLOW_XTENDED_DASD_IO, "AllowExtendedDasdIo"},
{ CCB_FLAG_READ_CONTEXT_ALLOCATED, CCB_FLAG_READ_CONTEXT_ALLOCATED, "ReadContextAllocated"},
{ CCB_FLAG_DELETE_ACCESS, CCB_FLAG_DELETE_ACCESS, "DeleteAccess"},
{ 0 }
};
STATE FcbState[] = {
{ FCB_STATE_FILE_DELETED, FCB_STATE_FILE_DELETED, "FileDeleted" },
{ FCB_STATE_NONPAGED, FCB_STATE_NONPAGED, "Nonpaged" },
{ FCB_STATE_PAGING_FILE, FCB_STATE_PAGING_FILE, "PagingFile" },
{ FCB_STATE_DUP_INITIALIZED, FCB_STATE_DUP_INITIALIZED, "DupInitialized" },
{ FCB_STATE_UPDATE_STD_INFO, FCB_STATE_UPDATE_STD_INFO, "UpdateStdInfo" },
{ FCB_STATE_PRIMARY_LINK_DELETED, FCB_STATE_PRIMARY_LINK_DELETED, "PrimaryLinkDeleted" },
{ FCB_STATE_IN_FCB_TABLE, FCB_STATE_IN_FCB_TABLE, "InFcbTable" },
{ FCB_STATE_SYSTEM_FILE, FCB_STATE_SYSTEM_FILE, "SystemFile" },
{ FCB_STATE_COMPOUND_DATA, FCB_STATE_COMPOUND_DATA, "CompoundData" },
{ FCB_STATE_COMPOUND_INDEX, FCB_STATE_COMPOUND_INDEX, "CompoundIndex" },
{ FCB_STATE_LARGE_STD_INFO, FCB_STATE_LARGE_STD_INFO, "LargeStdInfo" },
{ FCB_STATE_MODIFIED_SECURITY, FCB_STATE_MODIFIED_SECURITY, "ModifiedSecurity" },
{ FCB_STATE_DIRECTORY_ENCRYPTED, FCB_STATE_DIRECTORY_ENCRYPTED, "DirectoryEncrypted" },
{ FCB_STATE_VALID_USN_NAME, FCB_STATE_VALID_USN_NAME, "ValidUsnName" },
{ FCB_STATE_USN_JOURNAL, FCB_STATE_USN_JOURNAL, "UsnJournal" },
{ FCB_STATE_ENCRYPTION_PENDING, FCB_STATE_ENCRYPTION_PENDING, "EncryptionPending" },
{ 0 }
};
STATE NtfsFlags[] = {
{ NTFS_FLAGS_SMALL_SYSTEM, NTFS_FLAGS_SMALL_SYSTEM, "SmallSystem" },
{ NTFS_FLAGS_MEDIUM_SYSTEM, NTFS_FLAGS_MEDIUM_SYSTEM, "MediumSystem" },
{ NTFS_FLAGS_LARGE_SYSTEM, NTFS_FLAGS_LARGE_SYSTEM, "LargeSystem" },
{ NTFS_FLAGS_CREATE_8DOT3_NAMES, NTFS_FLAGS_CREATE_8DOT3_NAMES, "Create8dot3names" },
{ NTFS_FLAGS_ALLOW_EXTENDED_CHAR, NTFS_FLAGS_ALLOW_EXTENDED_CHAR, "AllowExtendedChar" },
{ NTFS_FLAGS_DISABLE_LAST_ACCESS, NTFS_FLAGS_DISABLE_LAST_ACCESS, "DisableLastAccess" },
{ NTFS_FLAGS_ENCRYPTION_DRIVER, NTFS_FLAGS_ENCRYPTION_DRIVER, "EncryptionDriver" },
{ 0 }
};
STATE ScbState[] = {
{ SCB_STATE_TRUNCATE_ON_CLOSE, SCB_STATE_TRUNCATE_ON_CLOSE, "TruncateOnClose" },
{ SCB_STATE_DELETE_ON_CLOSE, SCB_STATE_DELETE_ON_CLOSE, "DeleteOnClose" },
{ SCB_STATE_CHECK_ATTRIBUTE_SIZE, SCB_STATE_CHECK_ATTRIBUTE_SIZE, "CheckAttributeSize" },
{ SCB_STATE_ATTRIBUTE_RESIDENT, SCB_STATE_ATTRIBUTE_RESIDENT, "AttributeResident" },
{ SCB_STATE_UNNAMED_DATA, SCB_STATE_UNNAMED_DATA, "UnnamedData" },
{ SCB_STATE_HEADER_INITIALIZED, SCB_STATE_HEADER_INITIALIZED, "HeaderInitialized" },
{ SCB_STATE_NONPAGED, SCB_STATE_NONPAGED, "Nonpaged" },
{ SCB_STATE_USA_PRESENT, SCB_STATE_USA_PRESENT, "UsaPresent" },
{ SCB_STATE_ATTRIBUTE_DELETED, SCB_STATE_ATTRIBUTE_DELETED, "AttributeDeleted" },
{ SCB_STATE_FILE_SIZE_LOADED, SCB_STATE_FILE_SIZE_LOADED, "FileSizeLoaded" },
{ SCB_STATE_MODIFIED_NO_WRITE, SCB_STATE_MODIFIED_NO_WRITE, "ModifiedNoWrite" },
{ SCB_STATE_SUBJECT_TO_QUOTA, SCB_STATE_SUBJECT_TO_QUOTA, "SubjectToQuota" },
{ SCB_STATE_UNINITIALIZE_ON_RESTORE, SCB_STATE_UNINITIALIZE_ON_RESTORE, "UninitializeOnRestore" },
{ SCB_STATE_RESTORE_UNDERWAY, SCB_STATE_RESTORE_UNDERWAY, "RestoreUnderway" },
{ SCB_STATE_NOTIFY_ADD_STREAM, SCB_STATE_NOTIFY_ADD_STREAM, "NotifyAddStream" },
{ SCB_STATE_NOTIFY_REMOVE_STREAM, SCB_STATE_NOTIFY_REMOVE_STREAM, "NotifyRemoveStream" },
{ SCB_STATE_NOTIFY_RESIZE_STREAM, SCB_STATE_NOTIFY_RESIZE_STREAM, "NotifyResizeStream" },
{ SCB_STATE_NOTIFY_MODIFY_STREAM, SCB_STATE_NOTIFY_MODIFY_STREAM, "NotifyModifyStream" },
{ SCB_STATE_TEMPORARY, SCB_STATE_TEMPORARY, "Temporary" },
{ SCB_STATE_WRITE_COMPRESSED, SCB_STATE_WRITE_COMPRESSED, "Compressed" },
{ SCB_STATE_REALLOCATE_ON_WRITE, SCB_STATE_REALLOCATE_ON_WRITE, "DeallocateOnWrite" },
{ SCB_STATE_DELAY_CLOSE, SCB_STATE_DELAY_CLOSE, "DelayClose" },
{ SCB_STATE_WRITE_ACCESS_SEEN, SCB_STATE_WRITE_ACCESS_SEEN, "WriteAccessSeen" },
{ SCB_STATE_CONVERT_UNDERWAY, SCB_STATE_CONVERT_UNDERWAY, "ConvertUnderway" },
{ SCB_STATE_VIEW_INDEX, SCB_STATE_VIEW_INDEX, "ViewIndex" },
{ SCB_STATE_DELETE_COLLATION_DATA, SCB_STATE_DELETE_COLLATION_DATA, "DeleteCollationData" },
{ SCB_STATE_VOLUME_DISMOUNTED, SCB_STATE_VOLUME_DISMOUNTED, "VolumeDismounted" },
{ SCB_STATE_PROTECT_SPARSE_MCB, SCB_STATE_PROTECT_SPARSE_MCB, "ProtectSparseMcb" },
{ SCB_STATE_MULTIPLE_OPENS, SCB_STATE_MULTIPLE_OPENS, "MultipleOpens" },
{ 0 }
};
STATE ScbPersist[] = {
{ SCB_PERSIST_USN_JOURNAL, SCB_PERSIST_USN_JOURNAL, "UsnJournal" },
{ 0 }
};
STATE VcbState[] = {
{ VCB_STATE_VOLUME_MOUNTED, VCB_STATE_VOLUME_MOUNTED, "Mounted" },
{ VCB_STATE_LOCKED, VCB_STATE_LOCKED, "Locked" },
{ VCB_STATE_REMOVABLE_MEDIA, VCB_STATE_REMOVABLE_MEDIA, "RemovableMedia" },
{ VCB_STATE_VOLUME_MOUNTED_DIRTY, VCB_STATE_VOLUME_MOUNTED_DIRTY, "MountedDirty" },
{ VCB_STATE_RESTART_IN_PROGRESS, VCB_STATE_RESTART_IN_PROGRESS, "RestartInProgress" },
{ VCB_STATE_FLAG_SHUTDOWN, VCB_STATE_FLAG_SHUTDOWN, "FlagShutdown" },
{ VCB_STATE_NO_SECONDARY_AVAILABLE, VCB_STATE_NO_SECONDARY_AVAILABLE, "NoSecondaryAvailable" },
{ VCB_STATE_RELOAD_FREE_CLUSTERS, VCB_STATE_RELOAD_FREE_CLUSTERS, "ReloadFreeClusters" },
{ VCB_STATE_PRELOAD_MFT, VCB_STATE_PRELOAD_MFT, "PreloadMft" },
{ VCB_STATE_VOL_PURGE_IN_PROGRESS, VCB_STATE_VOL_PURGE_IN_PROGRESS, "VolPurgeInProgress" },
{ VCB_STATE_TEMP_VPB, VCB_STATE_TEMP_VPB, "TempVpb" },
{ VCB_STATE_PERFORMED_DISMOUNT, VCB_STATE_PERFORMED_DISMOUNT, "PerformedDismount" },
{ VCB_STATE_VALID_LOG_HANDLE, VCB_STATE_VALID_LOG_HANDLE, "ValidLogHandle" },
{ VCB_STATE_DELETE_UNDERWAY, VCB_STATE_DELETE_UNDERWAY, "DeleteUnderway" },
{ VCB_STATE_REDUCED_MFT, VCB_STATE_REDUCED_MFT, "ReducedMft" },
{ VCB_STATE_EXPLICIT_LOCK, VCB_STATE_EXPLICIT_LOCK, "ExplicitLock" },
{ VCB_STATE_DISALLOW_DISMOUNT, VCB_STATE_DISALLOW_DISMOUNT, "DisallowDismount" },
{ VCB_STATE_VALID_OBJECT_ID, VCB_STATE_VALID_OBJECT_ID, "ValidObjectId" },
{ VCB_STATE_OBJECT_ID_CLEANUP, VCB_STATE_OBJECT_ID_CLEANUP, "ObjectIdCleanup" },
{ VCB_STATE_USN_DELETE, VCB_STATE_USN_DELETE, "UsnDelete" },
{ VCB_STATE_USN_JOURNAL_PRESENT, VCB_STATE_USN_JOURNAL_PRESENT, "UsnJournalPresent" },
{ VCB_STATE_EXPLICIT_DISMOUNT, VCB_STATE_EXPLICIT_DISMOUNT, "ExplicitDismount" },
{ 0 }
};
STATE LcbState[] = {
{ LCB_STATE_DELETE_ON_CLOSE, LCB_STATE_DELETE_ON_CLOSE, "DeleteOnClose" },
{ LCB_STATE_LINK_IS_GONE, LCB_STATE_LINK_IS_GONE, "LinkIsGone" },
{ LCB_STATE_EXACT_CASE_IN_TREE, LCB_STATE_EXACT_CASE_IN_TREE, "ExactCaseInTree" },
{ LCB_STATE_IGNORE_CASE_IN_TREE, LCB_STATE_IGNORE_CASE_IN_TREE, "IgnoreCaseInTree" },
{ LCB_STATE_DESIGNATED_LINK, LCB_STATE_DESIGNATED_LINK, "DesignatedLink" },
{ LCB_STATE_VALID_HASH_VALUE, LCB_STATE_VALID_HASH_VALUE, "ValidHashValue" },
{ 0 }
};
char* LogOperation[] = {
{ "Noop " },
{ "CompensationLogRecord " },
{ "InitializeFileRecordSegment " },
{ "DeallocateFileRecordSegment " },
{ "WriteEndOfFileRecordSegment " },
{ "CreateAttribute " },
{ "DeleteAttribute " },
{ "UpdateResidentValue " },
{ "UpdateNonresidentValue " },
{ "UpdateMappingPairs " },
{ "DeleteDirtyClusters " },
{ "SetNewAttributeSizes " },
{ "AddIndexEntryRoot " },
{ "DeleteIndexEntryRoot " },
{ "AddIndexEntryAllocation " },
{ "DeleteIndexEntryAllocation " },
{ "WriteEndOfIndexBuffer " },
{ "SetIndexEntryVcnRoot " },
{ "SetIndexEntryVcnAllocation " },
{ "UpdateFileNameRoot " },
{ "UpdateFileNameAllocation " },
{ "SetBitsInNonresidentBitMap " },
{ "ClearBitsInNonresidentBitMap " },
{ "HotFix " },
{ "EndTopLevelAction " },
{ "PrepareTransaction " },
{ "CommitTransaction " },
{ "ForgetTransaction " },
{ "OpenNonresidentAttribute " },
{ "OpenAttributeTableDump " },
{ "AttributeNamesDump " },
{ "DirtyPageTableDump " },
{ "TransactionTableDump " },
{ "UpdateRecordDataRoot " },
{ "UpdateRecordDataAllocation " }
};
#define LastLogOperation 0x22
char* AttributeTypeCode[] = {
{ "$UNUSED " }, // (0X0)
{ "$STANDARD_INFORMATION " }, // (0x10)
{ "$ATTRIBUTE_LIST " }, // (0x20)
{ "$FILE_NAME " }, // (0x30)
{ "$OBJECT_ID " }, // (0x40)
{ "$SECURITY_DESCRIPTOR " }, // (0x50)
{ "$VOLUME_NAME " }, // (0x60)
{ "$VOLUME_INFORMATION " }, // (0x70)
{ "$DATA " }, // (0x80)
{ "$INDEX_ROOT " }, // (0x90)
{ "$INDEX_ALLOCATION " }, // (0xA0)
{ "$BITMAP " }, // (0xB0)
{ "$REPARSE_POINT " }, // (0xC0)
{ "$EA_INFORMATION " }, // (0xD0)
{ "$EA " }, // (0xE0)
{ " INVALID TYPE CODE " }, // (0xF0)
{ "$LOGGED_UTILITY_STREAM " } // (0x100)
};
char * LogEvent[] =
{
"SCE_VDL_CHANGE",
"SCE_ZERO_NC",
"SCE_ZERO_C",
"SCE_READ",
"SCE_WRITE",
"SCE_ZERO_CAV",
"SCE_ZERO_MF",
"SCE_ZERO_FST",
"SCE_CC_FLUSH",
"SCE_CC_FLUSH_AND_PURGE",
"SCE_WRITE_FILE_SIZES",
"SCE_ADD_ALLOCATION",
"SCE_ADD_SP_ALLOCATION",
"SCE_SETCOMP_ADD_ALLOCATION",
"SCE_SETSPARSE_ADD_ALLOCATION",
"SCE_MOD_ATTR_ADD_ALLOCATION",
"SCE_REALLOC1",
"SCE_REALLOC2",
"SCE_REALLOC3",
"SCE_SETCOMPRESS",
"SCE_SETSPARSE",
"SCE_ZERO_STREAM",
"SCE_VDD_CHANGE",
"SCE_CC_SET_SIZE",
"SCE_ZERO_C_TAIL_COMPRESSION",
"SCE_ZERO_C_HEAD_COMPRESSION",
"SCE_MAX_EVENT"
};
struct {
NODE_TYPE_CODE TypeCode;
char *Text;
} NodeTypeCodes[] = {
{ NTFS_NTC_DATA_HEADER, "Data Header" },
{ NTFS_NTC_VCB, "Vcb" },
{ NTFS_NTC_FCB, "Fcb" },
{ NTFS_NTC_SCB_INDEX, "ScbIndex" },
{ NTFS_NTC_SCB_ROOT_INDEX, "ScbRootIndex" },
{ NTFS_NTC_SCB_DATA, "ScbData" },
{ NTFS_NTC_SCB_MFT, "ScbMft" },
{ NTFS_NTC_SCB_NONPAGED, "ScbNonPaged" },
{ NTFS_NTC_CCB_INDEX, "CcbIndex" },
{ NTFS_NTC_CCB_DATA, "CcbData" },
{ NTFS_NTC_IRP_CONTEXT, "IrpContext" },
{ NTFS_NTC_LCB, "Lcb" },
{ NTFS_NTC_PREFIX_ENTRY, "PrefixEntry" },
{ NTFS_NTC_QUOTA_CONTROL, "QuotaControl" },
{ NTFS_NTC_USN_RECORD, "UsnRecord" },
{ 0, "Unknown" }
};
ULONG
MyGetFieldData(
IN ULONG64 TypeAddress,
IN PUCHAR Type,
IN PUCHAR Field,
IN ULONG OutSize,
OUT PULONG64 pOutValue,
OUT PULONG64 pOutAddress
)
/*++
Routine Description:
Retrieves the symbol information for a field within a structure.
Arguments:
TypeAddress - Virtual address of the structure
Type - Qualified type string
Field - Field name
OutSize - Size of the field
pOutValue - Value of the vield
pOutAddress - Virtual address of the field
Return Value:
Zero is success otherwise failure.
--*/
{
ULONG RetVal = 0;
FIELD_INFO flds = {
Field,
NULL,
OutSize,
DBG_DUMP_FIELD_FULL_NAME | DBG_DUMP_FIELD_COPY_FIELD_DATA | DBG_DUMP_FIELD_RETURN_ADDRESS,
0,
pOutValue
};
SYM_DUMP_PARAM Sym = {
sizeof(SYM_DUMP_PARAM),
Type,
DBG_DUMP_NO_PRINT,
TypeAddress,
NULL,
NULL,
NULL,
1,
&flds
};
ZeroMemory( pOutValue, OutSize );
RetVal = Ioctl( IG_DUMP_SYMBOL_INFO, &Sym, Sym.size );
if (OutSize < flds.size) {
if (OutSize == sizeof(ULONG64)) {
*pOutValue = Sym.Fields->address;
} else {
memset( pOutValue, 0, OutSize );
}
}
if (pOutAddress) {
if (RetVal == 0) {
*pOutAddress = Sym.Fields->address;
} else {
*pOutAddress = 0;
}
}
return RetVal;
}
VOID
DumpValue(
IN ULONG64 Address,
IN PCHAR Type,
IN PCHAR Field
)
/*++
Routine Description:
Prints the value of a 64/32 bit value based on
a symbol name and address.
Arguments:
Address - Virtual address of the value
Type - Qualified type string
Field - Field name
Return Value:
None.
--*/
{
static ULONG64 ValueBuffer[128];
ULONG64 Value,OutputAddress;
if (MyGetFieldData( Address, Type, Field, sizeof(Value), (PVOID)ValueBuffer, &OutputAddress )) {
Value = 0;
} else {
Value = ValueBuffer[0];
}
dprintf( "\n(%03x) ", (ULONG)(OutputAddress-Address) );
dprintf( " %s", FormatValue(Value) );
dprintf( " %s ", Field );
}
VOID
DumpPtrValue(
IN ULONG64 Address,
IN PCHAR TextStr
)
/*++
Routine Description:
Prints the value of a pointer.
Arguments:
Address - Virtual address of the value
TextStr - Tag to print with the pointer value
Return Value:
None.
--*/
{
ULONG64 PtrValue;
ULONG BytesRead;
if (ReadMemory( Address, &PtrValue, sizeof(PtrValue), &BytesRead )) {
dprintf( "\n %s %s", FormatValue(PtrValue), TextStr );
}
}
ULONG64
ReadValue(
IN ULONG64 Address,
IN PCHAR Type,
IN PCHAR Field
)
/*++
Routine Description:
Reads the value of a 64/32 bit value
Arguments:
Address - Virtual address of the value
Type - Qualified type string
Field - Field name
Return Value:
The 64/32 bit value or zero.
--*/
{
static ULONG64 ValueBuffer[128];
ULONG64 Value,OutputAddress;
if (MyGetFieldData( Address, Type, Field, sizeof(Value), (PVOID)ValueBuffer, &OutputAddress )) {
Value = 0;
} else {
Value = ValueBuffer[0];
}
return Value;
}
ULONG
ReadUlongValue(
IN ULONG64 Address,
IN PCHAR Type,
IN PCHAR Field
)
/*++
Routine Description:
Reads the value of a 32 bit value
Arguments:
Address - Virtual address of the value
Type - Qualified type string
Field - Field name
Return Value:
The 32 bit value or zero.
--*/
{
static ULONG ValueBuffer[128];
ULONG Value;
ULONG64 OutputAddress;
if (MyGetFieldData( Address, Type, Field, sizeof(Value), (PVOID)ValueBuffer, &OutputAddress )) {
Value = 0;
} else {
Value = ValueBuffer[0];
}
return Value;
}
USHORT
ReadShortValue(
IN ULONG64 Address,
IN PCHAR Type,
IN PCHAR Field
)
/*++
Routine Description:
Reads the value of a 16 bit value
Arguments:
Address - Virtual address of the value
Type - Qualified type string
Field - Field name
Return Value:
The 16 bit value or zero.
--*/
{
static USHORT ValueBuffer[128];
USHORT Value;
ULONG64 OutputAddress;
if (MyGetFieldData( Address, Type, Field, sizeof(Value), (PVOID)ValueBuffer, &OutputAddress )) {
Value = 0;
} else {
Value = ValueBuffer[0];
}
return Value;
}
VOID
DumpUnicodeString(
IN ULONG64 Address,
IN PCHAR Type,
IN PCHAR Field
)
/*++
Routine Description:
Prints the value (the actual string) of a string
contained in a UNICODE_STRING structure
Arguments:
Address - Virtual address of the structure
Type - Qualified type string for the structure containing the string
Field - Field name for the string
Return Value:
None.
--*/
{
ULONG64 Value;
ULONG64 OutputAddress;
USHORT Length;
ULONG64 BufferAddr;
PWSTR Buffer;
if (MyGetFieldData( Address, Type, Field, 0, (PVOID)&Value, &OutputAddress ) == 0) {
if (ReadMemory( OutputAddress, &Length, sizeof(Length), (PULONG)&Value )) {
if (Length) {
GetFieldOffset( "UNICODE_STRING", "Buffer", (PULONG)&Value );
OutputAddress += Value;
if (ReadMemory( OutputAddress, &BufferAddr, GetTypeSize("PWSTR"), (PULONG)&Value )) {
if (BufferAddr) {
Buffer = (PWSTR) malloc( Length + sizeof(WCHAR) );
if (Buffer) {
if (ReadMemory( BufferAddr, Buffer, Length, (PULONG)&Value )) {
Buffer[Length/sizeof(WCHAR)] = 0;
dprintf( "\n(%03x) %s %s [%ws]",
(ULONG)(OutputAddress-Address),
FormatValue(BufferAddr),
Field,
Buffer
);
free( Buffer );
return;
}
free( Buffer );
}
}
}
}
}
}
dprintf( "\n(%03x) %16x %s",
(ULONG)(OutputAddress-Address),
0,
Field
);
return;
}
BOOL
DumpString(
IN ULONG64 Address,
IN PCHAR Type,
IN PCHAR LengthField,
IN PCHAR StringField
)
/*++
Routine Description:
Prints the value (the actual string) of a string
contained in a structure with a corresponding
length field as another field member.
Arguments:
Address - Virtual address of the structure
Type - Qualified type string for the structure containing the string
LengthField - Field name for the length value
StringField - Field name for the string
Return Value:
TRUE for success, FALSE for failure
--*/
{
BOOL Result = FALSE;
ULONG Length;
PWSTR String;
ULONG Offset;
//
// read in the length
//
if (LengthField == NULL) {
Length = GetTypeSize(StringField) / sizeof(WCHAR);
} else {
Length = (ULONG)ReadValue( Address, Type, LengthField );
}
if (Length) {
Length *= sizeof(WCHAR);
//
// allocate some memory to hold the file name
//
String = malloc( Length + sizeof(WCHAR) );
if (String) {
//
// get the field offset of the string
//
if (!GetFieldOffset( Type, StringField, &Offset )) {
//
// compute the address of the string
//
Address += Offset;
//
// read the unicode characters for the string
//
if (ReadMemory( Address, String, Length, &Offset )) {
//
// zero terminate the string so we can print it out properly
//
String[Length/sizeof(WCHAR)] = 0;
//
// finally print the data
//
dprintf( "%ws", String );
Result = TRUE;
}
}
//
// free the string memory
//
free( String );
}
}
return Result;
}
ULONG64
ReadArrayValue(
IN ULONG64 Address,
IN PCHAR Type,
IN PCHAR Field,
IN ULONG Index
)
/*++
Routine Description:
Reads a value/element contained in an array.
Arguments:
Address - Virtual address of the structure
Type - Qualified type string for the structure containing the array
Field - Field name for the array
Index - The element that is requested
Return Value:
The element value or zero
--*/
{
CHAR Buff[64];
sprintf( Buff, "%s[%d]", Field, Index );
return ReadValue( Address, Type, Buff );
}
ULONG
GetOffset(
IN LPSTR Type,
IN LPSTR Field
)
/*++
Routine Description:
Gets the offset for a field within a structure
Arguments:
Type - Qualified type string for the structure containing the field
Field - Field name
Return Value:
The offset value or zero
--*/
{
FIELD_INFO flds = {
(PUCHAR)Field,
(PUCHAR)"",
0,
DBG_DUMP_FIELD_FULL_NAME | DBG_DUMP_FIELD_RETURN_ADDRESS,
0,
NULL};
SYM_DUMP_PARAM Sym = {
sizeof (SYM_DUMP_PARAM),
(PUCHAR)Type,
DBG_DUMP_NO_PRINT,
0,
NULL,
NULL,
NULL,
1,
&flds
};
ULONG Err;
Sym.nFields = 1;
Err = Ioctl( IG_DUMP_SYMBOL_INFO, &Sym, Sym.size );
if (Err == 0) {
return (ULONG) (flds.address - Sym.addr);
}
return -1;
}
PSTR
FormatValue(
ULONG64 addr
)
/*++
Routine Description:
Format a 64 bit address, showing the high bits or not
according to various flags. This version does not print
leading 0's.
An array of static string buffers is used, returning a different
buffer for each successive call so that it may be used multiple
times in the same print.
Arguments:
addr - Supplies the value to format
Return Value:
A pointer to the string buffer containing the formatted number
--*/
{
#define MAX_FORMAT_STRINGS 8
static CHAR strings[MAX_FORMAT_STRINGS][18];
static int next = 0;
LPSTR string;
string = strings[next];
++next;
if (next >= MAX_FORMAT_STRINGS) {
next = 0;
}
if ((KdDebuggerData.KernBase >> 32) != 0) {
//
// we're on a 64bit machines
//
sprintf( string, "%08x`%08x", (ULONG)(addr>>32), (ULONG)addr );
} else {
sprintf( string, "%08x", (ULONG)addr );
}
return string;
}
VOID
PrintState(
STATE *ps,
ULONG state
)
/*++
Routine Description:
Prints a state string based on the provided state value
Arguments:
ps - State string array
State - State value
Return Value:
None
--*/
{
ULONG ul = 0;
while (ps->mask != 0) {
ul |= ps->mask;
if ((state & ps->mask) == ps->value) {
dprintf(" %s", ps->pszname);
}
ps++;
}
state &= ~ul;
if (state != 0) {
dprintf(" +%lx!!", state);
}
dprintf("\n");
}
const char *
TypeCodeGuess (
IN NODE_TYPE_CODE TypeCode
)
/*++
Routine Description:
Guess at a structure's type code
Arguments:
TypeCode - Type code from the data structure
Return Value:
None
--*/
{
int i = 0;
while (NodeTypeCodes[i].TypeCode != 0 &&
NodeTypeCodes[i].TypeCode != TypeCode) {
i++;
}
return NodeTypeCodes[i].Text;
}
VOID
FindData(
IN ULONG64 FileObjectAddress,
IN ULONG64 Offset,
IN BOOL Trace,
OUT PULONG64 DataAddress
)
/*++
Routine Description:
Find the cache address for a given file object at the given offset.
Arguments:
FileObjectAddress - Gives the address of the file object to dump
Offset - Gives the offset within the file to dump
DataAddress - Where to store the address of the data. This will
contain 0 if the data at the given offset is not mapped.
Return Value:
None.
--*/
{
ULONG64 VacbAddr; // the address of the vacb
ULONG64 VacbAddrAddr; // the address of the address of the vacb
ULONG VacbNumber;
ULONG OffsetWithinVacb;
ULONG Level;
ULONG Shift;
ULONG OffsetForLevel;
LONGLONG OriginalOffset = Offset;
ULONG PtrSize = GetTypeSize("PVOID");
ULONG Type, InVacbsOffset;
ULONG64 SectionObjectPointer, SharedCacheMap, Vacbs, SectionSize_Quad;
*DataAddress = 0;
if (Trace) {
dprintf( "\n FindData for FileObject %08p", FileObjectAddress );
}
if (GetFieldValue(FileObjectAddress, "FILE_OBJECT", "Type", Type)) {
dprintf("Unable to read FILE_OBJECT at %p\n", FileObjectAddress);
return;
}
//
// Before we get into too much trouble, make sure this looks like a FileObject.
//
//
// Type of a FileObject must be IO_TYPE_FILE.
//
if (Type != IO_TYPE_FILE) {
dprintf( "\nFILE_OBJECT type signature does not match, type code is %s",
TypeCodeGuess((USHORT) Type ));
return;
}
GetFieldValue(FileObjectAddress, "FILE_OBJECT",
"SectionObjectPointer", SectionObjectPointer);
if (Trace) {
dprintf( " Section Object Pointers: %08p", SectionObjectPointer );
}
if (GetFieldValue(SectionObjectPointer,
"SECTION_OBJECT_POINTERS",
"SharedCacheMap",
SharedCacheMap)) {
dprintf("Unable to read SECTION_OBJECT_POINTERS at %p\n", SectionObjectPointer);
return;
}
if (Trace) {
dprintf( "\n Shared Cache Map: %08p", SharedCacheMap );
}
if (GetFieldValue(SharedCacheMap,
"SHARED_CACHE_MAP",
"Vacbs",
Vacbs)) {
dprintf("Unable to read SHARED_CACHE_MAP at %p\n", SharedCacheMap);
return;
}
GetFieldValue(SharedCacheMap, "SHARED_CACHE_MAP",
"SectionSize.QuadPart", SectionSize_Quad);
OffsetWithinVacb = (((ULONG) Offset) & (VACB_MAPPING_GRANULARITY - 1));
GetFieldOffset("SHARED_CACHE_MAP", "InitialVacbs", &InVacbsOffset);
if (Trace) {
dprintf( " File Offset: %I64x ", Offset );
}
if (Vacbs == (SharedCacheMap + InVacbsOffset)) {
//
// Small file case -- we're using one of the Vacbs in the Shared Cache Map's
// embedded array.
//
CHAR Buff[50];
VacbNumber = (ULONG) (Offset >> VACB_OFFSET_SHIFT);
if (VacbNumber >= PREALLOCATED_VACBS) {
dprintf( "\nInvalid VacbNumber for resident Vacb" );
return;
}
sprintf(Buff, "InitialVacbs[%d]", VacbNumber);
GetFieldValue(SharedCacheMap, "SHARED_CACHE_MAP",
Buff, VacbAddr);
if (Trace) {
dprintf( "in VACB number %x", VacbNumber );
}
} else if (SectionSize_Quad <= VACB_SIZE_OF_FIRST_LEVEL) {
//
// Medium file case -- we're using a single level (linear) structure to
// store the Vacbs.
//
VacbNumber = (ULONG) (Offset >> VACB_OFFSET_SHIFT);
VacbAddrAddr = Vacbs + (VacbNumber * PtrSize);
if (ReadPtr(VacbAddrAddr, &VacbAddr)) {
dprintf("Unable to read at %p\n", VacbAddrAddr);
return;
}
if (Trace) {
dprintf( "in VACB number %x", VacbNumber );
}
} else {
//
// Large file case -- multilevel Vacb storage.
//
Level = 0;
Shift = VACB_OFFSET_SHIFT + VACB_LEVEL_SHIFT;
//
// Loop to calculate how many levels we have and how much we have to
// shift to index into the first level.
//
do {
Level += 1;
Shift += VACB_LEVEL_SHIFT;
} while (SectionSize_Quad > ((ULONG64)1 << Shift));
//
// Now descend the tree to the bottom level to get the caller's Vacb.
//
Shift -= VACB_LEVEL_SHIFT;
// dprintf( "Shift: 0x%x\n", Shift );
OffsetForLevel = (ULONG) (Offset >> Shift);
VacbAddrAddr = Vacbs + (OffsetForLevel * PtrSize);
if (ReadPtr(VacbAddrAddr, &VacbAddr)) {
dprintf("Unable to read at %p\n", VacbAddrAddr);
return;
}
while ((VacbAddr != 0) && (Level != 0)) {
Level -= 1;
Offset &= ((LONGLONG)1 << Shift) - 1;
Shift -= VACB_LEVEL_SHIFT;
// dprintf( "Shift: 0x%x\n", Shift );
OffsetForLevel = (ULONG) (Offset >> Shift);
VacbAddrAddr = VacbAddr + (OffsetForLevel * PtrSize);
if (ReadPtr(VacbAddrAddr, &VacbAddr)) {
dprintf("Unable to read at %p\n", VacbAddrAddr);
return;
}
}
}
if (VacbAddr != 0) {
ULONG64 Base;
if (Trace) {
dprintf( "\n Vacb: %08p", VacbAddr );
}
if (GetFieldValue(VacbAddr, "_VACB", "BaseAddress", Base)) {
dprintf("Unable to read VACB base address at %p.", VacbAddr);
return;
}
if (Trace) {
dprintf( "\n Your data is at: %08p", (Base + OffsetWithinVacb) );
}
*DataAddress = Base + OffsetWithinVacb;
} else {
if (Trace) {
dprintf( "\n Data at offset %I64x not mapped", OriginalOffset );
}
}
return;
}
DECLARE_DUMP_FUNCTION( DumpCcb )
/*++
Routine Description:
Dump a specific ccb.
Arguments:
Address - Gives the address of the fcb to dump
Return Value:
None
--*/
{
ULONG64 Value;
INIT_DUMP();
Value = ReadValue( Address, SYM(CCB), "NodeTypeCode" );
//
// Before we get into too much trouble, make sure this looks like a ccb.
//
//
// Type of an fcb record must be NTFS_NTC_CCB_DATA or NTFS_NTC_CCB_INDEX
//
if (Value != NTFS_NTC_CCB_DATA && Value != NTFS_NTC_CCB_INDEX) {
dprintf( "\nCCB signature does not match, type code is %s", TypeCodeGuess( (NODE_TYPE_CODE)Value ));
return;
}
//
// Having established that this looks like a ccb, let's dump the
// interesting parts.
//
dprintf( "\nCcb: %s", FormatValue(Address) );
Value = ReadValue( Address, SYM(CCB), "Flags" );
PrintState( CcbState, (ULONG)Value );
DumpValue( Address, SYM(CCB), "Flags" );
dprintf( "\n OpenType: " );
Value = ReadValue( Address, SYM(CCB), "TypeOfOpen" );
switch (Value) {
case UserFileOpen :
dprintf( "UserFileOpen" );
break;
case UserDirectoryOpen :
dprintf( "UserDirectoryOpen" );
break;
case UserVolumeOpen :
dprintf( "UserVolumeOpen" );
break;
case StreamFileOpen :
dprintf( "StreamFileOpen" );
break;
case UserViewIndexOpen :
dprintf( "UserViewIndexOpen" );
break;
}
DumpUnicodeString( Address, SYM(CCB), "FullFileName" );
DumpValue( Address, SYM(CCB), "LastFileNameOffset" );
DumpValue( Address, SYM(CCB), "EaModificationCount" );
DumpValue( Address, SYM(CCB), "NextEaOffset" );
DumpValue( Address, SYM(CCB), "Lcb" );
DumpValue( Address, SYM(CCB), "TypeOfOpen" );
DumpValue( Address, SYM(CCB), "IndexContext" );
DumpValue( Address, SYM(CCB), "QueryLength" );
DumpValue( Address, SYM(CCB), "QueryBuffer" );
DumpValue( Address, SYM(CCB), "IndexEntryLength" );
DumpValue( Address, SYM(CCB), "IndexEntry" );
DumpValue( Address, SYM(CCB), "LcbLinks.Flink" );
DumpValue( Address, SYM(CCB), "FcbToAcquire" );
dprintf( "\n" );
}
ULONG
DumpFcbLinks(
IN PFIELD_INFO ListElement,
IN PVOID Context
)
/*++
Routine Description:
Enumeration callback function for FcbLinks
Arguments:
ListElement - Pointer to the containing record
Context - Opaque context passed from the origination function
Return Value:
TRUE to discontinue the enumeration
FALSE to continue the enumeration
--*/
{
ULONG64 Lcb = ListElement->address;
PDUMP_ENUM_CONTEXT dec = (PDUMP_ENUM_CONTEXT)Context;
if (CheckControlC()) {
return TRUE;
}
if (dec->Options >= 1) {
DumpLcb( Lcb, 0, dec->Options-1, dec->Processor, dec->hCurrentThread );
} else {
dprintf( "\n Lcb %s", FormatValue(Lcb) );
}
return FALSE;
}
ULONG
DumpScbLinks(
IN PFIELD_INFO ListElement,
IN PVOID Context
)
/*++
Routine Description:
Enumeration callback function for ScbLinks
Arguments:
ListElement - Pointer to the containing record
Context - Opaque context passed from the origination function
Return Value:
TRUE to discontinue the enumeration
FALSE to continue the enumeration
--*/
{
ULONG64 Scb = ListElement->address;
PDUMP_ENUM_CONTEXT dec = (PDUMP_ENUM_CONTEXT)Context;
ULONG Offset = 0;
if (CheckControlC()) {
return TRUE;
}
if (dec->Options >= 1) {
DumpScb( Scb, 0, dec->Options-1, dec->Processor, dec->hCurrentThread );
} else {
dprintf( "\n Scb %s", FormatValue(Scb) );
}
return FALSE;
}
DECLARE_DUMP_FUNCTION( DumpFcb )
/*++
Routine Description:
Dump a specific fcb.
Arguments:
Address - Gives the address of the fcb to dump
Return Value:
None
--*/
{
ULONG64 Value;
DUMP_ENUM_CONTEXT dec;
INIT_DUMP();
Value = ReadValue( Address, SYM(FCB), "NodeTypeCode" );
//
// Before we get into too much trouble, make sure this looks like an fcb.
//
//
// Type of an fcb record must be NTFS_NTC_FCB
//
if (Value != NTFS_NTC_FCB) {
dprintf( "\nFCB signature does not match, type code is %s", TypeCodeGuess( (NODE_TYPE_CODE)Value ) );
return;
}
dprintf( "\nFcb: %s", FormatValue(Address) );
//
// Having established that this looks like an fcb, let's dump the
// interesting parts.
//
PrintState( FcbState, (ULONG)ReadValue( Address, SYM(FCB), "FcbState" ) );
DumpValue( Address, SYM(FCB), "FcbState" );
DumpValue( Address, SYM(FCB), "FileReference" );
DumpValue( Address, SYM(FCB), "CleanupCount" );
DumpValue( Address, SYM(FCB), "CloseCount" );
DumpValue( Address, SYM(FCB), "ReferenceCount" );
DumpValue( Address, SYM(FCB), "FcbDenyDelete" );
DumpValue( Address, SYM(FCB), "FcbDeleteFile" );
DumpValue( Address, SYM(FCB), "BaseExclusiveCount" );
DumpValue( Address, SYM(FCB), "EaModificationCount" );
DumpValue( Address, SYM(FCB), "Vcb" );
DumpValue( Address, SYM(FCB), "FcbMutex" );
DumpValue( Address, SYM(FCB), "Resource" );
DumpValue( Address, SYM(FCB), "PagingIoResource" );
DumpValue( Address, SYM(FCB), "InfoFlags" );
DumpValue( Address, SYM(FCB), "LinkCount" );
DumpValue( Address, SYM(FCB), "TotalLinks" );
DumpValue( Address, SYM(FCB), "SharedSecurity" );
DumpValue( Address, SYM(FCB), "QuotaControl" );
DumpValue( Address, SYM(FCB), "ClassId" );
DumpValue( Address, SYM(FCB), "OwnerId" );
DumpValue( Address, SYM(FCB), "DelayedCloseCount" );
DumpValue( Address, SYM(FCB), "SecurityId" );
DumpValue( Address, SYM(FCB), "FcbUsnRecord" );
//
// walk the queue of links for this file
//
dec.hCurrentThread = hCurrentThread;
dec.Processor = Processor;
dec.Options = Options;
dprintf( "\n\nLinks:" );
Value = ReadValue( Address, SYM(FCB), "LcbQueue.Flink" );
if (Value) {
ListType( SYM(LCB), Value, TRUE, "FcbLinks.Flink", (PVOID)&dec, DumpFcbLinks );
}
dprintf( "\n\nStreams:" );
Value = ReadValue( Address, SYM(FCB), "ScbQueue.Flink" );
if (Value) {
ListType( SYM(SCB), Value, TRUE, "FcbLinks.Flink", (PVOID)&dec, DumpScbLinks );
}
dprintf( "\n" );
}
DECLARE_DUMP_FUNCTION( DumpFcbTable )
/*++
Routine Description:
Dump the fcb table.
Arguments:
Address - Gives the address of the fcb table to dump
Return Value:
None
--*/
{
ULONG64 Value;
ULONG64 TableElemAddr;
ULONG64 RestartKey;
ULONG64 FcbAddr;
ULONG Offset1;
ULONG Offset2;
PWSTR FileName;
BOOL GotIt;
INIT_DUMP();
//
// Dump the FcbTable
//
Value = ReadValue( Address, SYM(RTL_AVL_TABLE), "CompareRoutine" );
if (Value != GetExpression("NTFS!NtfsFcbTableCompare")) {
dprintf( "\nThe address [%s] does not seem to point to a FCB table", FormatValue(Address) );
return;
}
dprintf( "\n FcbTable %s", FormatValue(Address) );
dprintf( "\n FcbTable has %x elements", (ULONG)ReadValue( Address, SYM(RTL_AVL_TABLE), "NumberGenericTableElements" ) );
RestartKey = 0;
for (TableElemAddr = KdEnumerateGenericTableWithoutSplaying(Address, &RestartKey);
TableElemAddr != 0;
TableElemAddr = KdEnumerateGenericTableWithoutSplaying(Address, &RestartKey)) {
FcbAddr = ReadValue( TableElemAddr, SYM(FCB_TABLE_ELEMENT), "Fcb" );
if (Options >= 1) {
DumpFcb( FcbAddr, 0, Options - 2, Processor, hCurrentThread );
} else {
GotIt = FALSE;
//
// get the address of the FCB.LcbQueue LIST_ENTRY
//
Value = ReadValue( FcbAddr, SYM(FCB), "LcbQueue.Flink" );
if (Value) {
//
// get the offset of the LCB.FcbLinks LIST_ENTRY
//
if (!GetFieldOffset( SYM(LCB), "FcbLinks.Flink", &Offset1 )) {
//
// get the field offset of the FCB.LcbQueue LIST_ENTRY
//
if (!GetFieldOffset( SYM(FCB), "LcbQueue.Flink", &Offset2 )) {
//
// check to see if the list is empty
//
if (Value != FcbAddr+Offset2) {
//
// compute the address of the LCB
//
Value -= Offset1;
//
// get the length of the file name
//
Offset2 = (ULONG)(ReadValue( Value, SYM(LCB), "FileNameLength" ) * GetTypeSize("WCHAR"));
if (Offset2) {
//
// allocate some memory to hold the file name
//
FileName = malloc( Offset2 + GetTypeSize("WCHAR") );
if (FileName) {
//
// get the field offset of the LCB.FileName
//
if (!GetFieldOffset( SYM(LCB), "FileName", &Offset1 )) {
//
// compute the address of the file name character array
//
Value += Offset1;
//
// read the unicode characters for the file name
//
if (ReadMemory( Value, FileName, Offset2, (PULONG)&Offset1 )) {
//
// zero terminate the name so we can print it out properly
//
FileName[Offset2/GetTypeSize("WCHAR")] = 0;
//
// finally print the data
//
GotIt = TRUE;
dprintf( "\n Fcb %s for FileReference %08lx FcbTableElement %s %ws 0x%x",
FormatValue(FcbAddr),
(ULONG)ReadValue( TableElemAddr, SYM(FCB_TABLE_ELEMENT), "FileReference.SegmentNumberLowPart" ),
FormatValue(TableElemAddr),
FileName,
(ULONG)ReadValue( FcbAddr, SYM(FCB), "CleanupCount" )
);
}
}
//
// free the file name memory
//
free( FileName );
}
}
}
}
}
}
if (!GotIt) {
dprintf( "\n Fcb %s for FileReference %08lx FcbTableElement %s <filename unavailable> 0x%x",
FormatValue(FcbAddr),
(ULONG)ReadValue( TableElemAddr, SYM(FCB_TABLE_ELEMENT), "FileReference.SegmentNumberLowPart" ),
FormatValue(TableElemAddr),
(ULONG)ReadValue( FcbAddr, SYM(FCB), "CleanupCount" )
);
}
}
if (CheckControlC( )) {
break;
}
} // endfor
}
DECLARE_DUMP_FUNCTION( DumpFileObject )
/*++
Routine Description:
Dump a FileObject.
Arguments:
Address - Gives the address of the FileObject to dump
Return Value:
None
--*/
{
ULONG64 Value;
INIT_DUMP();
Value = ReadValue( Address, SYM(FILE_OBJECT), "Type" );
if (Value != IO_TYPE_FILE) {
dprintf( "Invalid signature, probably not a file object" );
return;
}
dprintf( "\nFileObject: %p", Address );
Value = ReadValue( Address, SYM(FILE_OBJECT), "FsContext" );
if (Value) {
DumpScb( Value, 0, Options, Processor, hCurrentThread );
Value = ReadValue( Address, SYM(FILE_OBJECT), "FsContext2" );
if (Value) {
DumpCcb( Value, 0, Options, Processor, hCurrentThread );
}
}
DumpValue( Address, SYM(FILE_OBJECT), "DeviceObject" );
DumpValue( Address, SYM(FILE_OBJECT), "Vpb" );
DumpValue( Address, SYM(FILE_OBJECT), "FsContext" );
DumpValue( Address, SYM(FILE_OBJECT), "FsContext2" );
DumpValue( Address, SYM(FILE_OBJECT), "SectionObjectPointer" );
DumpValue( Address, SYM(FILE_OBJECT), "PrivateCacheMap" );
DumpValue( Address, SYM(FILE_OBJECT), "FinalStatus" );
DumpValue( Address, SYM(FILE_OBJECT), "RelatedFileObject" );
DumpValue( Address, SYM(FILE_OBJECT), "LockOperation" );
DumpValue( Address, SYM(FILE_OBJECT), "DeletePending" );
DumpValue( Address, SYM(FILE_OBJECT), "ReadAccess" );
DumpValue( Address, SYM(FILE_OBJECT), "WriteAccess" );
DumpValue( Address, SYM(FILE_OBJECT), "DeleteAccess" );
DumpValue( Address, SYM(FILE_OBJECT), "SharedRead" );
DumpValue( Address, SYM(FILE_OBJECT), "SharedWrite" );
DumpValue( Address, SYM(FILE_OBJECT), "SharedDelete" );
DumpValue( Address, SYM(FILE_OBJECT), "Flags" );
DumpUnicodeString( Address, SYM(FILE_OBJECT), "FileName" );
DumpValue( Address, SYM(FILE_OBJECT), "CurrentByteOffset" );
DumpValue( Address, SYM(FILE_OBJECT), "Waiters" );
DumpValue( Address, SYM(FILE_OBJECT), "Busy" );
DumpValue( Address, SYM(FILE_OBJECT), "LastLock" );
DumpValue( Address, SYM(FILE_OBJECT), "Lock" );
DumpValue( Address, SYM(FILE_OBJECT), "Event" );
DumpValue( Address, SYM(FILE_OBJECT), "CompletionContext" );
dprintf( "\n" );
}
DECLARE_DUMP_FUNCTION( DumpFileObjectFromIrp )
/*++
Routine Description:
Dump a FileObject given an Irp.
Arguments:
Address - Gives the address of the Irp where the FileObject can be found
Return Value:
None
--*/
{
ULONG64 Value;
INIT_DUMP();
Value = ReadValue( Address, SYM(IRP), "Type" );
if (Value != IO_TYPE_IRP) {
dprintf( "IRP signature does not match, probably not an IRP\n" );
return;
}
dprintf( "\nIrp: %s", FormatValue(Address) );
//
// only the current irp stack is worth dumping
// the - 1 is there because irp.CurrentLocation is 1 based
//
Value = Address + GetTypeSize(NT(IRP)) + (GetTypeSize(NT(IO_STACK_LOCATION)) * (ReadValue( Address, NT(IRP), "CurrentLocation" ) - 1));
Value = ReadValue( Value, NT(IO_STACK_LOCATION), "FileObject" );
DumpFileObject( Value, 0, Options, Processor, hCurrentThread );
}
DECLARE_DUMP_FUNCTION( DumpFileRecord )
/*++
Routine Description:
Dump a FileRecord given a FileObject or Fcb.
Arguments:
Address - Gives the address of a FileObject or Fcb.
Return Value:
None
--*/
{
ULONG64 Value;
ULONG64 DataAddress;
ULONG64 ScbAddress;
ULONG64 FcbAddress;
ULONG64 VcbAddress;
ULONG64 FoAddress;
INIT_DUMP();
Value = ReadValue( Address, NT(FILE_OBJECT), "Type" );
switch (Value) {
case IO_TYPE_FILE:
dprintf( "\nFileObject: %s", FormatValue(Address) );
ScbAddress = ReadValue( Address, NT(FILE_OBJECT), "FsContext" );
if (ScbAddress == 0) {
dprintf( "No FsContext in the file object" );
return;
}
FcbAddress = ReadValue( ScbAddress, SYM(SCB), "Fcb" );
break;
case NTFS_NTC_FCB:
dprintf( "\nFcb: %s", FormatValue(Address) );
FcbAddress = Address;
ScbAddress = 0;
break;
case NTFS_NTC_SCB_DATA:
case NTFS_NTC_SCB_INDEX:
dprintf( "\nScb: %s", FormatValue(Address) );
ScbAddress = Address;
FcbAddress = ReadValue( ScbAddress, SYM(SCB), "Fcb" );
break;
default:
dprintf( "Invalid signature, not a file object or Fcb" );
return;
}
VcbAddress = ReadValue( FcbAddress, SYM(FCB), "Vcb" );
dprintf( " Vcb: %s", FormatValue(VcbAddress) );
dprintf( " FRS: %08lx,%04lx",
ReadValue( FcbAddress, SYM(FCB), "FileReference.SegmentNumberLowPart" ),
ReadValue( FcbAddress, SYM(FCB), "FileReference.SequenceNumber" ));
ScbAddress = ReadValue( VcbAddress, SYM(VCB), "MftScb" );
dprintf( " MftScb: %s", FormatValue(ScbAddress) );
dprintf( "reading fo in mftscb 0x%x 0x%x\n", GetOffset( SYM(SCB), "Header.FilterContexts" ), GetOffset( SYM(SCB), "Header.PendingEofAdvances" ) );
FoAddress = ReadValue( ScbAddress, SYM(SCB), "FileObject" );
dprintf( "finding data in mft fo 0x%s\n", FormatValue(FoAddress) );
FindData( FoAddress,
ReadValue( FcbAddress, SYM(FCB), "FileReference.SegmentNumberLowPart" ) * ReadValue( VcbAddress, SYM(VCB), "BytesPerFileRecordSegment" ),
Options,
&DataAddress
);
if (DataAddress == 0) {
dprintf( "\nFileRecord is not mapped" );
} else {
dprintf( "\nFileRecord at: %s", FormatValue(DataAddress) );
DumpFileRecordContents( DataAddress, 0, Options, Processor, hCurrentThread );
}
}
DECLARE_DUMP_FUNCTION( DumpFileRecordContents )
/*++
Routine Description:
Dump a FileObject's contents given a pointer to where the FR is cached.
Arguments:
Address - Gives the address where the FR is cached.
Return Value:
None
--*/
{
ULONG64 Value;
ULONG64 AttrAddress;
INIT_DUMP();
Value = ReadValue( Address, SYM(FILE_RECORD_SEGMENT_HEADER), "MultiSectorHeader.Signature" );
if ((ULONG)Value != *(PULONG)FileSignature) {
dprintf( "Not a file record %x", (ULONG)Value );
return;
}
AttrAddress = Address + ReadValue( Address, SYM(FILE_RECORD_SEGMENT_HEADER), "FirstAttributeOffset" );
while ((Value = ReadValue( AttrAddress, SYM(ATTRIBUTE_RECORD_HEADER), "TypeCode" )) != 0xffffffff) {
dprintf( "\nAttribute type %x %s", (ULONG)Value, AttributeTypeCode[Value/0x10] );
dprintf( " at offset %x", AttrAddress - Address );
AttrAddress += ReadValue( AttrAddress, SYM(ATTRIBUTE_RECORD_HEADER), "RecordLength" );
if (CheckControlC()) {
break;
}
}
}
DECLARE_DUMP_FUNCTION( DumpIrpContext )
/*++
Routine Description:
Dump an IrpContext.
Arguments:
Address - Gives the address of the IrpContext to dump
Return Value:
None
--*/
{
ULONG64 Value;
INIT_DUMP();
dprintf( "\nIrpContext: %s", FormatValue(Address) );
Value = ReadValue( Address, SYM(IRP_CONTEXT), "NodeTypeCode" );
if (Value != NTFS_NTC_IRP_CONTEXT) {
dprintf( "\nIRP_CONTEXT signature does not match, type code is %s", TypeCodeGuess( (NODE_TYPE_CODE)Value ) );
return;
}
Value = ReadValue( Address, SYM(IRP_CONTEXT), "OriginatingIrp" );
if (Value) {
DumpFileObjectFromIrp( Value, 0, Options, Processor, hCurrentThread );
}
dprintf( "\n" );
}
DECLARE_DUMP_FUNCTION( DumpIrpContextFromThread )
/*++
Routine Description:
Dump an IrpContext given a Thread.
Arguments:
Address - Gives the address of the Thread where the IrpContext can be found
Return Value:
None
--*/
{
ULONG64 Value;
INIT_DUMP();
//
// Lookup the current thread if the user didn't specify one.
//
if (Address == 0) {
GetCurrentThreadAddr( Processor, &Address );
}
Value = ReadValue( Address, NT(ETHREAD), "TopLevelIrp" );
if (Value) {
dprintf( "\nThread %s", FormatValue(Address) );
Value = ReadValue( Value, SYM(TOP_LEVEL_CONTEXT), "ThreadIrpContext" );
DumpIrpContext( Value, 0, Options, Processor, hCurrentThread );
}
dprintf( "\n" );
}
DECLARE_DUMP_FUNCTION( DumpLcb )
/*++
Routine Description:
Dump an Lcb.
Arguments:
Address - Gives the address of the Lcb to dump
Return Value:
None
--*/
{
ULONG64 Value;
INIT_DUMP();
Value = ReadValue( Address, SYM(LCB), "NodeTypeCode" );
if (Value != NTFS_NTC_LCB) {
dprintf( "\nLCB signature does not match, type code is %s", TypeCodeGuess( (NODE_TYPE_CODE)Value ) );
return;
}
dprintf( "\nLcb: %s", FormatValue(Address) );
PrintState( LcbState, (ULONG)ReadValue( Address, SYM(LCB), "LcbState" ) );
DumpUnicodeString( Address, SYM(LCB), "FileName" );
DumpValue( Address, SYM(LCB), "Scb" );
DumpValue( Address, SYM(LCB), "Fcb" );
}
DECLARE_DUMP_FUNCTION( DumpLogFile )
/*++
Routine Description:
Dump a log file.
Arguments:
Address - Gives the address of the Vcb whose log file should be dumped
Return Value:
None
--*/
{
ULONG64 Value;
ULONG64 VcbAddress;
ULONG64 LogFileSize;
ULONG64 LogFileScb;
ULONG SeqNumberBits;
ULONG64 LogFileOffset;
LONG LogFileMask;
USHORT RedoOperation;
USHORT UndoOperation;
ULONG64 LogDataAddress;
INIT_DUMP();
LogFileOffset = Options;
Value = ReadValue( Address, SYM(VCB), "NodeTypeCode" );
switch (Value) {
case NTFS_NTC_FCB:
dprintf( "\nFcb: %s", FormatValue(Address) );
VcbAddress = ReadValue( Address, SYM(FCB), "Vcb" );
break;
case NTFS_NTC_VCB:
dprintf( "\nVcb: %s", FormatValue(Address) );
VcbAddress = Address;
break;
default:
dprintf( "\nSignature is not an FCB or VCB, type code is %s", TypeCodeGuess( (NODE_TYPE_CODE)Value) );
return;
}
if (LogFileOffset == 0) {
LogFileOffset = ReadValue( VcbAddress, SYM(VCB), "LastRestartArea.QuadPart" );
}
dprintf( " Starting at LSN: 0x%016I64x", LogFileOffset );
LogFileScb = ReadValue( VcbAddress, SYM(VCB), "LogFileScb" );
LogFileSize = ReadValue( LogFileScb, SYM(SCB), "Header.FileSize.QuadPart" );
for (SeqNumberBits=0; LogFileSize!=0; SeqNumberBits+=1,LogFileSize=((ULONGLONG)(LogFileSize)) >> 1 ) {
}
LogFileMask = (1 << (SeqNumberBits - 3)) - 1;
while (TRUE) {
LogFileOffset &= LogFileMask; // clear some bits
LogFileOffset = LogFileOffset << 3; // multiply by 8
Value = ReadValue( VcbAddress, SYM(VCB), "LogFileObject" );
FindData( Value, LogFileOffset, FALSE, &LogDataAddress );
if (LogDataAddress != 0) {
//
// It's mapped.
//
RedoOperation = ReadShortValue( LogDataAddress+GetTypeSize(SYM(LFS_RECORD_HEADER)), SYM(NTFS_LOG_RECORD_HEADER), "RedoOperation" );
UndoOperation = ReadShortValue( LogDataAddress+GetTypeSize(SYM(LFS_RECORD_HEADER)), SYM(NTFS_LOG_RECORD_HEADER), "UndoOperation" );
if (RedoOperation <= LastLogOperation && UndoOperation <= LastLogOperation) {
dprintf( "\nRedo: %s", LogOperation[RedoOperation] );
dprintf( " Undo: %s", LogOperation[UndoOperation] );
dprintf( " Lsn: 0x%08lx", (ULONG)ReadValue( LogDataAddress, SYM(LFS_RECORD_HEADER), "ThisLsn.LowPart" ) );
}
} else {
break;
}
if (CheckControlC()) {
break;
}
LogFileOffset = (ULONG)ReadValue( LogDataAddress, SYM(LFS_RECORD_HEADER), "ClientUndoNextLsn.LowPart" );
if (LogFileOffset == 0) {
break;
}
}
}
DECLARE_DUMP_FUNCTION( DumpTransaction )
/*++
Routine Description:
Dump a log file.
Arguments:
Address - Gives the address of the irpcontext to trace the transaction for
Return Value:
None
--*/
{ ULONG64 TransactionId;
ULONG64 VcbAddress;
ULONG64 TransactionTable;
LSN FirstLsn;
LSN CurrentLsn;
ULONG64 LogFileObject;
ULONG64 LogFileSize;
ULONG64 LogFileScb;
ULONG SeqNumberBits;
ULONG64 LogFileOffset;
LONG LogFileMask;
USHORT RedoOperation;
USHORT UndoOperation;
ULONG64 LogDataAddress;
ULONG64 MftScbAddress;
ULONG64 MftFileObject;
ULONG64 DataAddress;
USHORT Type;
INIT_DUMP();
//
// Determine what type of input it is
//
Type = (USHORT) ReadValue( Address, SYM(IRP_CONTEXT), "NodeTypeCode" );
if (Type == NTFS_NTC_FCB) {
//
// Its an Fcb so read the filerecord and find the last LSN on disk from it
//
VcbAddress = ReadValue( Address, SYM(FCB), "Vcb" );
MftScbAddress = ReadValue( VcbAddress, SYM(VCB), "MftScb" );
MftFileObject = ReadValue( MftScbAddress, SYM(SCB), "FileObject" );
FindData( MftFileObject,
ReadValue( Address, SYM(FCB), "FileReference.SegmentNumberLowPart" ) * ReadValue( VcbAddress, SYM(VCB), "BytesPerFileRecordSegment" ),
0,
&DataAddress
);
CurrentLsn.QuadPart = ReadValue( DataAddress, SYM(FILE_RECORD_SEGMENT_HEADER), "Lsn" );
dprintf( "Searching for last LSN: 0x%I64x on disk for file: 0x%I64x\n\n", CurrentLsn,
ReadValue( Address, SYM(FCB), "FileReference.SegmentNumberLowPart" ));
} else if (Type == NTFS_NTC_VCB ) {
//
// Its a vcb and filerecord so directly get the last LSN from it
//
VcbAddress = Address;
CurrentLsn.QuadPart = ReadValue( Options, SYM(FILE_RECORD_SEGMENT_HEADER), "Lsn" );
dprintf( "0x%x\n", Options );
dprintf( "Searching for last LSN: 0x%I64x on disk for file: 0x%I64x\n\n", CurrentLsn,
ReadValue( Options, SYM(FILE_RECORD_SEGMENT_HEADER), "SegmentNumberLowPart" ));
} else if (Type == NTFS_NTC_IRP_CONTEXT) {
//
// Read in the transaction id and then find the transaction entry in the table
//
TransactionId = ReadValue( Address, SYM(IRP_CONTEXT), "TransactionId" );
VcbAddress = ReadValue( Address, SYM(IRP_CONTEXT), "Vcb" );
TransactionTable = ReadValue( VcbAddress, SYM(VCB), "TransactionTable.Table" );
FirstLsn.QuadPart = ReadValue( TransactionTable + TransactionId, SYM( TRANSACTION_ENTRY), "FirstLsn.QuadPart" );
CurrentLsn.QuadPart = ReadValue( TransactionTable + TransactionId, SYM( TRANSACTION_ENTRY), "PreviousLsn.QuadPart" );
if (TransactionId == 0) {
dprintf( "No transaction active for this irpcontext\n" );
return;
}
dprintf( "Transaction: 0x%I64x from Lsn: 0x%I64x to 0x%I64x\n\n", TransactionId, FirstLsn, CurrentLsn );
} else {
dprintf( "Unknown type 0x%x for ptr 0x%p\n", Type, Address );
return;
}
LogFileScb = ReadValue( VcbAddress, SYM(VCB), "LogFileScb" );
LogFileSize = ReadValue( LogFileScb, SYM(SCB), "Header.FileSize.QuadPart" );
LogFileObject = ReadValue( VcbAddress, SYM(VCB), "LogFileObject" );
for (SeqNumberBits=0; LogFileSize!=0; SeqNumberBits+=1,LogFileSize=((ULONGLONG)(LogFileSize)) >> 1 ) {
}
LogFileMask = (1 << (SeqNumberBits - 3)) - 1;
LogFileOffset = CurrentLsn.QuadPart;
while (TRUE) {
LogFileOffset &= LogFileMask; // clear some bits
LogFileOffset = LogFileOffset << 3; // multiply by 8
FindData( LogFileObject, LogFileOffset, FALSE, &LogDataAddress );
if (LogDataAddress != 0) {
//
// It's mapped.
//
RedoOperation = ReadShortValue( LogDataAddress+GetTypeSize(SYM(LFS_RECORD_HEADER)), SYM(NTFS_LOG_RECORD_HEADER), "RedoOperation" );
UndoOperation = ReadShortValue( LogDataAddress+GetTypeSize(SYM(LFS_RECORD_HEADER)), SYM(NTFS_LOG_RECORD_HEADER), "UndoOperation" );
if (RedoOperation <= LastLogOperation && UndoOperation <= LastLogOperation) {
dprintf( "Record: %p Lsn: %I64x Prev: %I64x Undo: %I64x\n",
LogDataAddress,
ReadValue( LogDataAddress, SYM(LFS_RECORD_HEADER), "ThisLsn.QuadPart" ),
ReadValue( LogDataAddress, SYM(LFS_RECORD_HEADER), "ClientPreviousLsn.QuadPart" ),
ReadValue( LogDataAddress, SYM(LFS_RECORD_HEADER), "ClientUndoNextLsn.QuadPart" ) );
dprintf( "Redo: %s Undo: %s\n\n", LogOperation[RedoOperation], LogOperation[UndoOperation] );
}
} else {
dprintf( "Data not mapped in log for offset: 0x%I64x\n", LogFileOffset );
break;
}
if (CheckControlC()) {
break;
}
LogFileOffset = (ULONG)ReadValue( LogDataAddress, SYM(LFS_RECORD_HEADER), "ClientPreviousLsn.QuadPart" );
if (LogFileOffset == 0) {
break;
}
}
}
DECLARE_DUMP_FUNCTION( DumpMcb )
/*++
Routine Description:
Dump an Mcb.
Arguments:
Address - Gives the address of the Mcb to dump
Return Value:
None
--*/
{
ULONG64 NtfsMcbArray;
ULONG64 MappingPairsAddress;
ULONG RangeIdx;
ULONG NtfsMcbArraySizeInUse;
INIT_DUMP();
dprintf( "\nNtfsMcb: %s", FormatValue(Address) );
DumpValue( Address, SYM(NTFS_MCB), "FcbHeader" );
DumpValue( Address, SYM(NTFS_MCB), "PoolType" );
DumpValue( Address, SYM(NTFS_MCB), "NtfsMcbArraySizeInUse" );
DumpValue( Address, SYM(NTFS_MCB), "NtfsMcbArraySize" );
DumpValue( Address, SYM(NTFS_MCB), "NtfsMcbArray" );
DumpValue( Address, SYM(NTFS_MCB), "FastMutex" );
NtfsMcbArray = ReadValue( Address, SYM(NTFS_MCB), "NtfsMcbArray" );
NtfsMcbArraySizeInUse = (ULONG)ReadValue( Address, SYM(NTFS_MCB), "NtfsMcbArraySizeInUse" );
for (RangeIdx=0; RangeIdx<NtfsMcbArraySizeInUse; RangeIdx++) {
dprintf( "\n Range %d", RangeIdx );
DumpValue( NtfsMcbArray, SYM(NTFS_MCB_ARRAY), "StartingVcn" );
DumpValue( NtfsMcbArray, SYM(NTFS_MCB_ARRAY), "EndingVcn" );
DumpValue( NtfsMcbArray, SYM(NTFS_MCB_ARRAY), "NtfsMcbEntry" );
MappingPairsAddress = ReadValue( NtfsMcbArray, SYM(NTFS_MCB_ARRAY), "NtfsMcbEntry" ) +
GetOffset(SYM(NTFS_MCB_ENTRY),"LargeMcb") +
GetOffset(SYM(LARGE_MCB),"Mapping");
DumpPtrValue( MappingPairsAddress, "MappingPairs" );
//
// Go on to the next range.
//
NtfsMcbArray += GetTypeSize(SYM(NTFS_MCB_ARRAY));
if (CheckControlC()) {
break;
}
}
dprintf( "\n" );
}
ULONG
DumpVcbQueue(
IN PFIELD_INFO ListElement,
IN PVOID Context
)
/*++
Routine Description:
Enumeration callback function for the Vcb Queue
Arguments:
ListElement - Pointer to the containing record
Context - Opaque context passed from the origination function
Return Value:
TRUE to discontinue the enumeration
FALSE to continue the enumeration
--*/
{
ULONG64 Vcb = ListElement->address;
PDUMP_ENUM_CONTEXT dec = (PDUMP_ENUM_CONTEXT)Context;
ULONG64 Vpb;
if (CheckControlC()) {
return TRUE;
}
if (dec->Options >= 1) {
DumpVcb( Vcb, 0, dec->Options-1, dec->Processor, dec->hCurrentThread );
} else {
Vpb = ReadValue( Vcb, SYM(VCB), "Vpb" );
dprintf( "\n Vcb %s label: ", FormatValue(Vcb) );
if (!DumpString( Vpb, NT(VPB), "VolumeLabelLength", "VolumeLabel" )) {
dprintf( "<label unavailable>" );
}
}
return FALSE;
}
DECLARE_DUMP_FUNCTION( DumpNtfsData )
/*++
Routine Description:
Dump the list of Vcbs for the global NtfsData.
Arguments:
Options - If 1, we recurse into the Vcbs and dump them
Return Value:
None
--*/
{
ULONG64 Value;
DUMP_ENUM_CONTEXT dec;
INIT_DUMP();
Address = GetExpression( "Ntfs!NtfsData" );
dprintf( "\nNtfsData: %s", FormatValue(Address) );
Value = ReadValue( Address, SYM(NTFS_DATA), "NodeTypeCode" );
if (Value != NTFS_NTC_DATA_HEADER) {
dprintf( "\nNtfsData signature does not match, type code is %s", TypeCodeGuess( (NODE_TYPE_CODE)Value ) );
return;
}
PrintState( NtfsFlags, (ULONG)ReadValue( Address, SYM(NTFS_DATA), "Flags" ) );
//
// dump the vcb queue (mounted volumes)
//
dec.hCurrentThread = hCurrentThread;
dec.Processor = Processor;
dec.Options = Options;
Value = ReadValue( Address, SYM(NTFS_DATA), "VcbQueue.Flink" );
if (Value) {
ListType( SYM(VCB), Value, TRUE, "VcbLinks.Flink", (PVOID)&dec, DumpVcbQueue );
}
dprintf( "\n" );
DumpValue( Address, SYM(NTFS_DATA), "DriverObject" );
DumpValue( Address, SYM(NTFS_DATA), "Resource" );
DumpValue( Address, SYM(NTFS_DATA), "AsyncCloseActive" );
DumpValue( Address, SYM(NTFS_DATA), "ReduceDelayedClose" );
DumpValue( Address, SYM(NTFS_DATA), "AsyncCloseCount" );
DumpValue( Address, SYM(NTFS_DATA), "OurProcess" );
DumpValue( Address, SYM(NTFS_DATA), "DelayedCloseCount" );
DumpValue( Address, SYM(NTFS_DATA), "FreeFcbTableSize" );
DumpValue( Address, SYM(NTFS_DATA), "FreeEresourceSize" );
DumpValue( Address, SYM(NTFS_DATA), "FreeEresourceTotal" );
DumpValue( Address, SYM(NTFS_DATA), "FreeEresourceMiss" );
DumpValue( Address, SYM(NTFS_DATA), "FreeEresourceArray" );
DumpValue( Address, SYM(NTFS_DATA), "Flags" );
}
DECLARE_DUMP_FUNCTION( DumpScb )
/*++
Routine Description:
Dump an Scb.
Arguments:
Address - Gives the address of the Scb to dump
Options - If 1, we dump the Fcb & Vcb for this Scb
Return Value:
None
--*/
{
ULONG64 Value = 0;
INIT_DUMP();
_try {
Value = ReadValue( Address, SYM(SCB), "ScbState" );
if (!Value) {
_leave;
}
dprintf( "\nScb: %s", FormatValue(Address) );
PrintState( ScbState, (ULONG)Value );
dprintf( "\nScbPersist:" );
PrintState( ScbPersist, (ULONG)ReadValue( Address, SYM(SCB), "ScbPersist" ) );
Value = ReadValue( Address, SYM(FSRTL_COMMON_FCB_HEADER), "NodeTypeCode" );
if (!Value) {
_leave;
}
dprintf( "\n ScbType: " );
switch ( Value ) {
case NTFS_NTC_SCB_INDEX:
dprintf( "Index" );
break;
case NTFS_NTC_SCB_ROOT_INDEX:
dprintf( "RootIndex" );
break;
case NTFS_NTC_SCB_DATA:
dprintf( "Data" );
break;
case NTFS_NTC_SCB_MFT:
dprintf( "Mft" );
break;
default:
dprintf( "!!!UNKNOWN SCBTYPE!!!, type code is %s", TypeCodeGuess( (NODE_TYPE_CODE)Value ) );
break;
}
if (Options >= 1) {
Value = ReadValue( Address, SYM(SCB), "Fcb" );
if (Value) {
DumpFcb( Value, 0, Options - 1, Processor, hCurrentThread );
}
Value = ReadValue( Address, SYM(SCB), "Vcb" );
if (Value) {
DumpVcb( Value, 0, Options - 1, Processor, hCurrentThread );
}
} else {
DumpValue( Address, SYM(SCB), "Fcb" );
DumpValue( Address, SYM(SCB), "Vcb" );
DumpValue( Address, SYM(SCB), "Mcb" );
DumpValue( Address, SYM(SCB), "NonCachedCleanupCount" );
DumpValue( Address, SYM(SCB), "CleanupCount" );
DumpValue( Address, SYM(SCB), "CloseCount" );
DumpValue( Address, SYM(SCB), "ShareAccess" );
DumpValue( Address, SYM(SCB), "AttributeTypeCode" );
DumpValue( Address, SYM(SCB), "AttributeName.Length" );
DumpValue( Address, SYM(SCB), "AttributeName.Buffer" );
DumpValue( Address, SYM(SCB), "AttributeFlags" );
DumpValue( Address, SYM(SCB), "CompressionUnit" );
DumpValue( Address, SYM(SCB), "FileObject" );
DumpValue( Address, SYM(SCB), "NonpagedScb" );
DumpValue( Address, SYM(SCB), "EncryptionContext" );
}
} finally {
}
dprintf( "\n" );
}
DECLARE_DUMP_FUNCTION( DumpVcb )
/*++
Routine Description:
Dump a Vcb.
Arguments:
Address - Gives the address of the Vcb to dump
Options - If 1, we also dump the root Lcb and the Fcb table
If 2, we dump everything for option 1, and also dump the Fcbs in the Fcb table
Return Value:
None
--*/
{
ULONG64 Value = 0;
INIT_DUMP();
Value = ReadValue( Address, SYM(VCB), "NodeTypeCode" );
dprintf( "\n Vcb: %s", FormatValue(Address) );
if (Value != NTFS_NTC_VCB) {
dprintf( "\nVCB signature does not match, type code is %s", TypeCodeGuess( (NODE_TYPE_CODE)Value ) );
return;
}
PrintState( VcbState, (ULONG)ReadValue( Address, SYM(VCB), "VcbState" ) );
DumpValue( Address, SYM(VCB), "CleanupCount" );
DumpValue( Address, SYM(VCB), "CloseCount" );
DumpValue( Address, SYM(VCB), "ReadOnlyCloseCount" );
DumpValue( Address, SYM(VCB), "SystemFileCloseCount" );
DumpValue( Address, SYM(VCB), "UsnJournal" );
DumpValue( Address, SYM(VCB), "MftScb" );
DumpValue( Address, SYM(VCB), "Mft2Scb" );
DumpValue( Address, SYM(VCB), "LogFileScb" );
DumpValue( Address, SYM(VCB), "VolumeDasdScb" );
DumpValue( Address, SYM(VCB), "RootIndexScb" );
DumpValue( Address, SYM(VCB), "BitmapScb" );
DumpValue( Address, SYM(VCB), "AttributeDefTableScb" );
DumpValue( Address, SYM(VCB), "UpcaseTableScb" );
DumpValue( Address, SYM(VCB), "BadClusterFileScb" );
DumpValue( Address, SYM(VCB), "QuotaTableScb" );
DumpValue( Address, SYM(VCB), "ReparsePointTableScb" );
DumpValue( Address, SYM(VCB), "OwnerIdTableScb" );
DumpValue( Address, SYM(VCB), "SecurityDescriptorStream" );
DumpValue( Address, SYM(VCB), "SecurityIdIndex" );
DumpValue( Address, SYM(VCB), "SecurityDescriptorHashIndex" );
DumpValue( Address, SYM(VCB), "ExtendDirectory" );
DumpValue( Address, SYM(VCB), "ObjectIdTableScb" );
DumpValue( Address, SYM(VCB), "MftBitmapScb" );
DumpValue( Address, SYM(VCB), "RootLcb" );
DumpValue( Address, SYM(VCB), "FcbTable" );
DumpValue( Address, SYM(VCB), "Statistics" );
DumpValue( Address, SYM(VCB), "Resource" );
if (Options < 0) {
ResetFileSystemStatistics( Address, Processor, hCurrentThread );
} else if (Options >= 2) {
DumpFileSystemStatistics( Address, Processor, hCurrentThread );
} else if (Options >= 1) {
DumpLcb( ReadValue( Address, SYM(VCB), "RootLcb" ), 0, Options - 1, Processor, hCurrentThread );
DumpFcbTable( ReadValue( Address, SYM(VCB), "FcbTable" ), 0, Options - 1, Processor, hCurrentThread );
}
dprintf( "\n" );
}
VOID
ResetFileSystemStatistics (
IN ULONG64 VcbAddress,
IN USHORT Processor,
IN HANDLE hCurrentThread
)
/*++
Routine Description:
Dump the file system statitics of a vcb
Arguments:
Vcb - Suppplies a pointer to a vcb that the debugger has already loaded.
Return Value:
None
--*/
{
ULONG Result;
PUCHAR Stat;
ULONG64 StatsAddr;
dprintf( "\n" );
dprintf( "\n" );
//
// Write the Statistics structure based on the processor, but
// skip over the file system type and version field.
//
Result = GetTypeSize(SYM(FILE_SYSTEM_STATISTICS));
Stat = malloc( Result );
if (Stat) {
StatsAddr = ReadValue( VcbAddress, SYM(VCB), "Statistics" );
if (StatsAddr) {
if (!WriteMemory( StatsAddr + (Result * Processor) + GetOffset(SYM(FILE_SYSTEM_STATISTICS),"Common.UserFileReads"),
&Stat,
Result - GetOffset(SYM(FILE_SYSTEM_STATISTICS),"Common.UserFileReads"),
&Result) ) {
dprintf( "%s: Unable to reset Statistics\n", FormatValue(StatsAddr) );
}
}
free( Stat );
dprintf( "**** %s: Resetting Filesystem Statistics complete ****\n", FormatValue(StatsAddr) );
}
DumpFileSystemStatistics( VcbAddress, Processor, hCurrentThread );
}
VOID
DumpFileSystemStatistics (
IN ULONG64 VcbAddress,
IN USHORT Processor,
IN HANDLE hCurrentThread
)
/*++
Routine Description:
Dump the file system statitics of a vcb
Arguments:
Vcb - Suppplies a pointer to a vcb that the debugger has already loaded.
Return Value:
None
--*/
{
ULONG Result;
ULONG64 StatsAddr;
FILE_SYSTEM_STATISTICS Stat;
ULONG TotalReads;
ULONG TotalReadBytes;
ULONG TotalWrites;
ULONG TotalWriteBytes;
ULONG TotalClustersReturned;
ULONG AverageRequestSize;
ULONG AverageRunSize;
ULONG AverageHintSize;
ULONG AverageCacheSize;
ULONG AverageCacheMissSize;
UNREFERENCED_PARAMETER( hCurrentThread );
//
// Read in the Statistics structure based on the processor
//
StatsAddr = ReadValue( VcbAddress, SYM(VCB), "Statistics" );
if ( !ReadMemory( StatsAddr + (GetTypeSize(SYM(FILE_SYSTEM_STATISTICS)) * Processor),
&Stat,
GetTypeSize(SYM(FILE_SYSTEM_STATISTICS)),
&Result) ) {
dprintf( "%08lx: Unable to read Statistics\n", StatsAddr );
return;
}
//
// Sum up all the paging i/o reads and writes
//
TotalReads = Stat.Common.UserFileReads + Stat.Common.MetaDataReads + Stat.Ntfs.UserIndexReads + Stat.Ntfs.LogFileReads;
TotalReadBytes = Stat.Common.UserFileReadBytes + Stat.Common.MetaDataReadBytes + Stat.Ntfs.UserIndexReadBytes + Stat.Ntfs.LogFileReadBytes;
TotalWrites = Stat.Common.UserFileWrites + Stat.Common.MetaDataWrites + Stat.Ntfs.UserIndexWrites + Stat.Ntfs.LogFileWrites;
TotalWriteBytes = Stat.Common.UserFileWriteBytes + Stat.Common.MetaDataWriteBytes + Stat.Ntfs.UserIndexWriteBytes + Stat.Ntfs.LogFileWriteBytes;
//
// Sum up the total number of clusters returned
//
TotalClustersReturned = Stat.Ntfs.Allocate.HintsClusters + Stat.Ntfs.Allocate.CacheClusters + Stat.Ntfs.Allocate.CacheMissClusters;
//
// Compute the average cluster count requested, returned, from hints, and from the cache, and cache misses.
//
AverageRequestSize = AVERAGE(Stat.Ntfs.Allocate.Clusters, Stat.Ntfs.Allocate.Calls);
AverageRunSize = AVERAGE(TotalClustersReturned, Stat.Ntfs.Allocate.RunsReturned);
AverageHintSize = AVERAGE(Stat.Ntfs.Allocate.HintsClusters, Stat.Ntfs.Allocate.HintsHonored);
AverageCacheSize = AVERAGE(Stat.Ntfs.Allocate.CacheClusters, Stat.Ntfs.Allocate.Cache);
AverageCacheMissSize = AVERAGE(Stat.Ntfs.Allocate.CacheMissClusters, Stat.Ntfs.Allocate.CacheMiss);
dprintf( "\n" );
dprintf( "\n File System Statistics @ %s for Processor = %d", FormatValue(StatsAddr), Processor );
dprintf( "\n FileSystemType / Version = %d / %d", Stat.Common.FileSystemType, Stat.Common.Version );
dprintf( "\n" );
dprintf( "\n Exceptions LogFileFull = %ld Other = %ld", Stat.Ntfs.LogFileFullExceptions, Stat.Ntfs.OtherExceptions );
dprintf( "\n" );
dprintf( "\n Reads Bytes Writes Bytes" );
dprintf( "\n ----- ----- ------ -----" );
dprintf( "\n" );
dprintf( "\n UserFile %10ld (%10ld)%10ld (%10ld)", Stat.Common.UserFileReads, Stat.Common.UserFileReadBytes, Stat.Common.UserFileWrites, Stat.Common.UserFileWriteBytes );
dprintf( "\n UserDisk %10ld %10ld", Stat.Common.UserDiskReads, Stat.Common.UserDiskWrites );
dprintf( "\n" );
dprintf( "\n MetaData %10ld (%10ld)%10ld (%10ld)", Stat.Common.MetaDataReads, Stat.Common.MetaDataReadBytes, Stat.Common.MetaDataWrites, Stat.Common.MetaDataWriteBytes );
dprintf( "\n MetaDisk %10ld %10ld", Stat.Common.MetaDataDiskReads, Stat.Common.MetaDataDiskWrites );
dprintf( "\n" );
dprintf( "\n Mft %10ld (%10ld)%10ld (%10ld)", Stat.Ntfs.MftReads, Stat.Ntfs.MftReadBytes, Stat.Ntfs.MftWrites, Stat.Ntfs.MftWriteBytes );
dprintf( "\n Mft2 %10ld (%10lx)", Stat.Ntfs.Mft2Writes, Stat.Ntfs.Mft2WriteBytes );
dprintf( "\n RootIndex%10ld (%10ld)%10ld (%10ld)", Stat.Ntfs.RootIndexReads, Stat.Ntfs.RootIndexReadBytes, Stat.Ntfs.RootIndexWrites, Stat.Ntfs.RootIndexWriteBytes );
dprintf( "\n Bitmap %10ld (%10ld)%10ld (%10ld)", Stat.Ntfs.BitmapReads, Stat.Ntfs.BitmapReadBytes, Stat.Ntfs.BitmapWrites, Stat.Ntfs.BitmapWriteBytes );
dprintf( "\n MftBitmap%10ld (%10ld)%10ld (%10ld)", Stat.Ntfs.MftBitmapReads, Stat.Ntfs.MftBitmapReadBytes, Stat.Ntfs.MftBitmapWrites, Stat.Ntfs.MftBitmapWriteBytes );
dprintf( "\n" );
dprintf( "\n UserIndex %10ld (%10ld)%10ld (%10ld)", Stat.Ntfs.UserIndexReads, Stat.Ntfs.UserIndexReadBytes, Stat.Ntfs.UserIndexWrites, Stat.Ntfs.UserIndexWriteBytes );
dprintf( "\n" );
dprintf( "\n LogFile %10ld (%10ld)%10ld (%10ld)", Stat.Ntfs.LogFileReads, Stat.Ntfs.LogFileReadBytes, Stat.Ntfs.LogFileWrites, Stat.Ntfs.LogFileWriteBytes );
dprintf( "\n" );
dprintf( "\n TOTAL %10ld (%10ld)%10ld (%10ld)", TotalReads, TotalReadBytes, TotalWrites, TotalWriteBytes );
dprintf( "\n" );
dprintf( "\n Write Create SetInfo Flush" );
dprintf( "\n ----- ------ ------- -----" );
dprintf( "\n MftWritesUserLevel %5d %5d %5d %5d", Stat.Ntfs.MftWritesUserLevel.Write, Stat.Ntfs.MftWritesUserLevel.Create, Stat.Ntfs.MftWritesUserLevel.SetInfo, Stat.Ntfs.MftWritesUserLevel.Flush );
dprintf( "\n Mft2WritesUserLevel %5d %5d %5d %5d", Stat.Ntfs.Mft2WritesUserLevel.Write, Stat.Ntfs.Mft2WritesUserLevel.Create, Stat.Ntfs.Mft2WritesUserLevel.SetInfo, Stat.Ntfs.Mft2WritesUserLevel.Flush );
dprintf( "\n BitmapWritesUserLevel %5d %5d %5d", Stat.Ntfs.BitmapWritesUserLevel.Write, Stat.Ntfs.BitmapWritesUserLevel.Create, Stat.Ntfs.BitmapWritesUserLevel.SetInfo );
dprintf( "\n MftBitmapWritesUserLevel %5d %5d %5d %5d", Stat.Ntfs.MftBitmapWritesUserLevel.Write, Stat.Ntfs.MftBitmapWritesUserLevel.Create, Stat.Ntfs.MftBitmapWritesUserLevel.SetInfo, Stat.Ntfs.MftBitmapWritesUserLevel.Flush );
dprintf( "\n" );
dprintf( "\n FlushForLogFileFull LazyWriter UserRequest" );
dprintf( "\n ------------------- ---------- -----------" );
dprintf( "\n MftWrites %5d %5d %5d", Stat.Ntfs.MftWritesFlushForLogFileFull, Stat.Ntfs.MftWritesLazyWriter, Stat.Ntfs.MftWritesUserRequest );
dprintf( "\n Mft2Writes %5d %5d %5d", Stat.Ntfs.Mft2WritesFlushForLogFileFull, Stat.Ntfs.Mft2WritesLazyWriter, Stat.Ntfs.Mft2WritesUserRequest );
dprintf( "\n BitmapWrites %5d %5d %5d", Stat.Ntfs.BitmapWritesFlushForLogFileFull, Stat.Ntfs.BitmapWritesLazyWriter, Stat.Ntfs.BitmapWritesUserRequest );
dprintf( "\n MftBitmapWrites %5d %5d %5d", Stat.Ntfs.MftBitmapWritesFlushForLogFileFull, Stat.Ntfs.MftBitmapWritesLazyWriter, Stat.Ntfs.MftBitmapWritesUserRequest );
dprintf( "\n" );
dprintf( "\n Allocate Total Average" );
dprintf( "\n Clusters Runs Hints Clusters RunSize" );
dprintf( "\n ---- ----- -------- -------" );
dprintf( "\n Requested %10ld %10ld %10ld %10ld", Stat.Ntfs.Allocate.Calls, Stat.Ntfs.Allocate.Hints, Stat.Ntfs.Allocate.Clusters, AverageRequestSize );
dprintf( "\n Returned %10ld %10ld %10ld %10ld", Stat.Ntfs.Allocate.RunsReturned, Stat.Ntfs.Allocate.HintsHonored, TotalClustersReturned, AverageRunSize );
dprintf( "\n" );
dprintf( "\n FromHints %10ld %10ld %10ld", Stat.Ntfs.Allocate.HintsHonored, Stat.Ntfs.Allocate.HintsClusters, AverageHintSize );
dprintf( "\n CacheHit %10ld %10ld %10ld", Stat.Ntfs.Allocate.Cache, Stat.Ntfs.Allocate.CacheClusters, AverageCacheSize );
dprintf( "\n CacheMiss %10ld %10ld %10ld", Stat.Ntfs.Allocate.CacheMiss, Stat.Ntfs.Allocate.CacheMissClusters, AverageCacheMissSize );
dprintf( "\n" );
}
DECLARE_DUMP_FUNCTION( DumpSysCache )
/*++
Routine Description:
Dump the syscache buffers. The target system must have been
built with syscache enabled.
Arguments:
Address - Gives the address of the Vcb to dump
Return Value:
None
--*/
{
ULONG SyscacheLogEntryCount;
ULONG CurrentSyscacheLogEntry;
PSYSCACHE_LOG pLog = NULL;
int iEnd;
int iStart;
int iTemp;
int iIndex;
INIT_DUMP();
if (GetOffset( SYM(VCB), "SyscacheScb" ) == -1) {
//
// the system was not built with syscache debug
//
dprintf( "\nthe target system does not have syscache debug enabled\n" );
return;
}
if (Options != 0) {
dprintf( "Direct buffer dump\n" );
dprintf("Num Entries: 0x%x\n", Options );
dprintf("Current Entry: 0x%x\n", Options );
CurrentSyscacheLogEntry = Options;
SyscacheLogEntryCount = Options;
pLog = (PSYSCACHE_LOG) malloc( GetTypeSize(SYM(SYSCACHE_LOG)) * SyscacheLogEntryCount );
if (!pLog) {
return;
}
if (!ReadMemory( Address, pLog, GetTypeSize(SYM(SYSCACHE_LOG)) * SyscacheLogEntryCount, &iTemp )) {
dprintf( "Unable to read SCB.SyscacheLog\n" );
return;
}
} else {
SyscacheLogEntryCount = ReadUlongValue(Address,SYM(SCB),"SyscacheLogEntryCount");
CurrentSyscacheLogEntry = ReadUlongValue(Address,SYM(SCB),"CurrentSyscacheLogEntry");
dprintf("Num Entries: 0x%x\n", SyscacheLogEntryCount );
dprintf("Current Entry: 0x%x\n", ReadUlongValue(Address,SYM(SCB),"CurrentSyscacheLogEntry") );
pLog = (PSYSCACHE_LOG) malloc( GetTypeSize(SYM(SYSCACHE_LOG)) * SyscacheLogEntryCount );
if (!pLog) {
return;
}
if (!ReadMemory( ReadValue(Address,SYM(SCB),"SyscacheLog"), pLog,
GetTypeSize(SYM(SYSCACHE_LOG)) * SyscacheLogEntryCount, &iTemp )) {
dprintf( "Unable to read SCB.SyscacheLog\n" );
return;
}
}
if (CurrentSyscacheLogEntry > SyscacheLogEntryCount) {
iStart = CurrentSyscacheLogEntry;
iEnd = CurrentSyscacheLogEntry + SyscacheLogEntryCount;;
} else {
iStart = 0;
iEnd = CurrentSyscacheLogEntry;
}
for (iIndex= iStart; iIndex < iEnd; iIndex++) {
iTemp = iIndex % SyscacheLogEntryCount;
if (iStart == 0) {
dprintf("Entry: 0x%x\n", iIndex);
} else {
dprintf("Entry: 0x%x\n", iIndex - SyscacheLogEntryCount);
}
dprintf("Event: 0x%x ", pLog[iTemp ].Event);
if (pLog[iTemp].Event < SCE_MAX_EVENT) {
dprintf("(%s)\n", LogEvent[pLog[iTemp].Event]);
} else {
dprintf("\n");
}
dprintf("Flags: 0x%x (", pLog[iTemp].Flags);
if (pLog[iTemp].Flags & SCE_FLAG_WRITE) {
dprintf("write ");
}
if (pLog[iTemp].Flags & SCE_FLAG_READ) {
dprintf("read ");
}
if (pLog[iTemp].Flags & SCE_FLAG_PAGING) {
dprintf("paging io ");
}
if (pLog[iTemp].Flags & SCE_FLAG_ASYNC) {
dprintf("asyncfileobj ");
}
if (pLog[iTemp].Flags & SCE_FLAG_SET_ALLOC) {
dprintf("setalloc ");
}
if (pLog[iTemp].Flags & SCE_FLAG_SET_EOF) {
dprintf("seteof ");
}
if (pLog[iTemp].Flags & SCE_FLAG_CANT_WAIT) {
dprintf("cantwait ");
}
if (pLog[iTemp].Flags & SCE_FLAG_SYNC_PAGING) {
dprintf("synchpaging ");
}
if (pLog[iTemp].Flags & SCE_FLAG_LAZY_WRITE) {
dprintf("lazywrite ");
}
if (pLog[iTemp].Flags & SCE_FLAG_CACHED) {
dprintf("cached ");
}
if (pLog[iTemp].Flags & SCE_FLAG_ON_DISK_READ) {
dprintf("fromdisk ");
}
if (pLog[iTemp].Flags & SCE_FLAG_RECURSIVE) {
dprintf("recursive ");
}
if (pLog[iTemp].Flags & SCE_FLAG_NON_CACHED) {
dprintf("noncached ");
}
if (pLog[iTemp].Flags & SCE_FLAG_UPDATE_FROM_DISK) {
dprintf("updatefromdisk ");
}
if (pLog[iTemp].Flags & SCE_FLAG_COMPRESSED) {
dprintf("compressed ");
}
if (pLog[iTemp].Flags & SCE_FLAG_SET_VDL) {
dprintf("setvdl ");
}
if (pLog[iTemp].Flags & SCE_FLAG_FASTIO) {
dprintf("fastio ");
}
dprintf(")\n");
dprintf("Start: 0x%I64x Range: 0x%I64x Result: 0x%I64x\n",
pLog[iTemp].Start, pLog[iTemp].Range, pLog[iTemp].Result);
dprintf("\n");
}
if (pLog) {
free(pLog);
}
}
DECLARE_DUMP_FUNCTION( DumpExtents )
/*++
Routine Description:
Dump the extents for a file
Arguments:
Address - Gives the address of the Vcb to dump
Return Value:
None
--*/
{
UCHAR FormCode;
PVOID Buffer;
PBYTE TempByte;
ULONG ChangedLCNBytes;
ULONG ChangedVCNBytes;
ULONG Increment;
ULONG Index;
ULONG Increment2;
ULONG LCN = 0;
ULONG VCN = 0;
ULONG RecordLength;
USHORT MappingPairsOffset;
INIT_DUMP();
FormCode = (UCHAR)ReadValue( Address, SYM(ATTRIBUTE_RECORD_HEADER), "FormCode" );
if (!(FormCode & NONRESIDENT_FORM)) {
dprintf( "resident attribute\n" );
return;
}
DumpValue( Address, SYM(ATTRIBUTE_RECORD_HEADER), "Form.Nonresident.MappingPairsOffset" );
DumpValue( Address, SYM(ATTRIBUTE_RECORD_HEADER), "Form.Nonresident.LowestVcn" );
DumpValue( Address, SYM(ATTRIBUTE_RECORD_HEADER), "Form.Nonresident.HighestVcn" );
DumpValue( Address, SYM(ATTRIBUTE_RECORD_HEADER), "Form.Nonresident.AllocatedLength" );
dprintf( "\n" );
RecordLength = ReadUlongValue( Address, SYM(ATTRIBUTE_RECORD_HEADER), "RecordLength" );
if (!RecordLength) {
return;
}
MappingPairsOffset = ReadShortValue( Address, SYM(ATTRIBUTE_RECORD_HEADER), "Form.Nonresident.MappingPairsOffset" );
if (!MappingPairsOffset) {
return;
}
RecordLength -= MappingPairsOffset;
Buffer = malloc( RecordLength );
if (!ReadMemory( Address+MappingPairsOffset, Buffer, RecordLength, &RecordLength )) {
dprintf( "Unable to read memory at %s\n", FormatValue(Address+MappingPairsOffset) );
free( Buffer );
return;
}
TempByte = Buffer;
//
// walk byte stream
//
while(*TempByte != 0) {
ChangedLCNBytes = *TempByte >> 4;
ChangedVCNBytes = *TempByte & (0x0f);
TempByte++;
for (Increment=0, Index=0; Index < ChangedVCNBytes; Index++) {
Increment+= *TempByte++ << (8 * Index);
}
for (Increment2 =0, Index=0; Index < ChangedLCNBytes; Index++) {
Increment2+= *TempByte++ << (8 * Index);
}
//
// if last bit is set (this is a neg) extend with 0xff
//
if (0x80 & (*(TempByte-1))) {
for(; Index < GetTypeSize("ULONG"); Index++) {
Increment2 += 0xff << (8 * Index);
}
}
LCN += Increment2;
dprintf( "LCN: 0x%x VCN: 0x%x Clusters: 0x%x ", LCN, VCN, Increment );
for (Index = ChangedLCNBytes + ChangedVCNBytes + 1; Index > 0; Index--) {
dprintf( "%02x", *(TempByte - Index));
}
dprintf( "\n" );
VCN += Increment;
}
free( Buffer );
}
ULONG
ThreadListCallback(
IN PFIELD_INFO listElement,
IN PVOID Context
)
/*++
Routine Description:
Enumeration callback function for cachedrecords to check for cached file records
Arguments:
ListElement - Pointer to the containing record
Context - Opaque context passed from the origination function
Return Value:
TRUE to discontinue the enumeration
FALSE to continue the enumeration
--*/
{
PDUMP_ENUM_CONTEXT dec = (PDUMP_ENUM_CONTEXT)Context;
ULONG64 ThreadAddress=listElement->address;
ULONG64 TopLevelIrp;
ULONG NtfsSig;
ULONG64 ThreadIrpContext;
int Index;
ULONG RecordSize;
ULONG64 RecordAddress;
ULONG64 FileRecordBcb;
ULONG64 FileRecord;
ULONG UnsafeSegmentNumber;
TopLevelIrp = ReadValue( ThreadAddress, NT(ETHREAD), "TopLevelIrp" );
if (TopLevelIrp) {
NtfsSig = ReadUlongValue( TopLevelIrp, SYM(TOP_LEVEL_CONTEXT), "Ntfs" );
if (NtfsSig == 0x5346544e) {
ThreadIrpContext = ReadValue( TopLevelIrp, SYM(TOP_LEVEL_CONTEXT), "ThreadIrpContext" );
if (ThreadIrpContext) {
RecordSize = GetTypeSize(SYM(IRP_FILE_RECORD_CACHE_ENTRY));
RecordAddress = ThreadIrpContext + GetOffset(SYM(IRP_CONTEXT),"FileRecordCache");
dprintf ("record: 0x%x\n", RecordAddress );
for (Index=0; Index<IRP_FILE_RECORD_MAP_CACHE_SIZE; Index++) {
FileRecord = ReadValue( RecordAddress, SYM(IRP_FILE_RECORD_CACHE_ENTRY), "FileRecord" );
FileRecordBcb = ReadValue( RecordAddress, SYM(IRP_FILE_RECORD_CACHE_ENTRY), "FileRecordBcb" );
if (FileRecord) {
UnsafeSegmentNumber = ReadUlongValue( RecordAddress, SYM(IRP_FILE_RECORD_CACHE_ENTRY), "UnsafeSegmentNumber" );
dprintf( "Thread: 0x%s FileRecord: 0x%s Bcb: 0x%s SegmentNum: 0x%x\n",
FormatValue(ThreadAddress),
FormatValue(FileRecord),
FormatValue(FileRecordBcb),
UnsafeSegmentNumber
);
}
RecordAddress += RecordSize;
}
}
}
}
return FALSE;
}
ULONG
ProcessListCallback(
IN PFIELD_INFO listElement,
IN PVOID Context
)
/*++
Routine Description:
Enumeration callback function for cachedrecords to check for cached file records
Arguments:
ListElement - Pointer to the containing record
Context - Opaque context passed from the origination function
Return Value:
TRUE to discontinue the enumeration
FALSE to continue the enumeration
--*/
{
PDUMP_ENUM_CONTEXT dec = (PDUMP_ENUM_CONTEXT)Context;
ULONG64 ProcAddress=listElement->address;
ULONG64 FirstThread;
FirstThread = ReadValue( ProcAddress, NT(EPROCESS), "Pcb.ThreadListHead.Flink" );
if (FirstThread) {
ListType( NT(ETHREAD), FirstThread, 1, "Tcb.ThreadListEntry.Flink", (PVOID)dec, &ThreadListCallback );
}
return FALSE;
}
DECLARE_DUMP_FUNCTION( DumpCachedRecords )
/*++
Routine Description:
Walk all processes and dump any which are holding filerecords cached in
irpcontexts
Arguments:
arg - none
Return Value:
None
--*/
{
ULONG64 FirstProcess;
DUMP_ENUM_CONTEXT dec;
INIT_DUMP();
FirstProcess = ReadValue( KdDebuggerData.PsActiveProcessHead, NT(LIST_ENTRY), "Flink" );
if (FirstProcess == 0) {
dprintf( "Unable to read _LIST_ENTRY @ %s \n", FormatValue(KdDebuggerData.PsActiveProcessHead) );
return;
}
dec.hCurrentThread = hCurrentThread;
dec.Processor = Processor;
dec.Options = Options;
ListType( NT(EPROCESS), FirstProcess, 1, "ActiveProcessLinks.Flink", (PVOID)&dec, &ProcessListCallback );
}
DECLARE_DUMP_FUNCTION( DumpHashTable )
/*++
Routine Description:
Dump a prefix hash table
Arguments:
arg - none
Return Value:
None
--*/
{
ULONG64 HashSegmentsOffset;
ULONG64 HashSegmentAddress;
ULONG64 HashSegmentPtr;
ULONG HashEntrySize;
DWORD Index;
DWORD Index2;
ULONG BytesRead;
ULONG64 NextAddr;
ULONG PtrSize;
INIT_DUMP();
dprintf( "Hash Table: 0x%s\n", FormatValue(Address) );
dprintf( "Max Buckets: 0x%x Splitpoint: 0x%x\n",
ReadUlongValue( Address, SYM(NTFS_HASH_TABLE), "MaxBucket" ),
ReadUlongValue( Address, SYM(NTFS_HASH_TABLE), "SplitPoint" ) );
HashSegmentsOffset = GetOffset(SYM(NTFS_HASH_TABLE),"HashSegments");
HashEntrySize = GetTypeSize(SYM(NTFS_HASH_ENTRY));
PtrSize = GetTypeSize(SYM(PVOID));
HashSegmentAddress = Address + HashSegmentsOffset;
for (Index=0; Index<HASH_MAX_SEGMENT_COUNT; Index++) {
HashSegmentAddress += (Index * PtrSize);
if (ReadMemory( HashSegmentAddress, &HashSegmentPtr, PtrSize, &BytesRead ) && HashSegmentPtr) {
for (Index2=0; Index2<HASH_MAX_INDEX_COUNT; Index2++) {
NextAddr = HashSegmentPtr + (Index2 * PtrSize);
while (NextAddr) {
if (Address2 == 0 || ReadValue( NextAddr, SYM(NTFS_HASH_ENTRY), "HashLcb" ) == Address2) {
dprintf( "Lcb: 0x%s Hash: 0x%x\n",
FormatValue(ReadValue( NextAddr, SYM(NTFS_HASH_ENTRY), "HashLcb" )),
ReadUlongValue( NextAddr, SYM(NTFS_HASH_ENTRY), "HashValue" ) );
}
NextAddr = ReadValue( NextAddr, SYM(NTFS_HASH_ENTRY), "NextEntry" );
if (CheckControlC()) {
return;
}
}
if (CheckControlC()) {
return;
}
}
}
if (CheckControlC()) {
return;
}
}
}
ULONG
FindIndexScb(
IN PFIELD_INFO ListElement,
IN PVOID Context
)
/*++
Routine Description:
Enumeration callback function to locate the index scb
Arguments:
ListElement - Pointer to the containing record
Context - Opaque context passed from the origination function
Return Value:
TRUE to discontinue the enumeration
FALSE to continue the enumeration
--*/
{
ULONG64 Scb = ListElement->address;
PDUMP_ENUM_CONTEXT dec = (PDUMP_ENUM_CONTEXT)Context;
if (CheckControlC()) {
return TRUE;
}
if (ReadValue( Scb, SYM(SCB), "AttributeTypeCode" ) == $INDEX_ALLOCATION) {
dec->ReturnValue = Scb;
return TRUE;
}
return FALSE;
}
DECLARE_DUMP_FUNCTION( DumpFcbLcbChain )
/*++
Routine Description:
Dump a fcb - lcb - chain to find the bottom
Arguments:
arg - the initial fcb
Return Value:
None
--*/
{
ULONG64 FcbAddress = Address;
ULONG64 ScbAddress = 0;
ULONG64 LcbAddress = 0;
ULONG64 Link = 0;
DUMP_ENUM_CONTEXT dec;
ULONG64 Value;
INIT_DUMP();
do {
if (ReadValue( FcbAddress, SYM(FCB), "NodeTypeCode" ) != NTFS_NTC_FCB) {
dprintf( "Not an FCB at 0x%s\n", FormatValue(FcbAddress) );
return;
}
//
// initialize the enum context for all out enumerations
//
dec.hCurrentThread = hCurrentThread;
dec.Processor = Processor;
dec.Options = Options;
dec.ReturnValue = 0;
//
// Find the index SCB
//
Value = ReadValue( FcbAddress, SYM(FCB), "ScbQueue.Flink" );
if (Value) {
ListType( SYM(SCB), Value, TRUE, "FcbLinks.Flink", (PVOID)&dec, FindIndexScb );
if (dec.ReturnValue) {
ScbAddress = dec.ReturnValue;
}
}
if (ScbAddress == 0) {
dprintf( "unable to find index scb in fcb: 0x%s\n", FormatValue(FcbAddress) );
return;
}
dprintf( "Scb: 0x%s, NameLen: 0x%x Counts: 0x%x 0x%x\n",
FormatValue(ScbAddress),
ReadShortValue( ScbAddress, SYM(SCB), "ScbType.Index.NormalizedName.MaximumLength" ),
ReadUlongValue( ScbAddress, SYM(SCB), "CleanupCount" ),
ReadUlongValue( ScbAddress, SYM(SCB), "CloseCount" )
);
Value = ReadValue( ScbAddress, SYM(SCB), "ScbType.Index.LcbQueue.Flink" );
if (Value != (ScbAddress + GetOffset(SYM(SCB),"Index.LcbQueue.Flink"))) {
//
// Read the 1st lcb
//
LcbAddress = Value - GetOffset(SYM(LCB),"ScbLinks");
dprintf( "lcb: 0x%s Cleanup: 0x%x fcb: 0x%s\n",
FormatValue(LcbAddress),
ReadUlongValue( LcbAddress, SYM(LCB), "CleanupCount" ),
FormatValue(ReadValue( LcbAddress, SYM(LCB), "Fcb" ))
);
FcbAddress = ReadValue( LcbAddress, SYM(LCB), "Fcb" );
} else {
dprintf( "lcbqueue empty: 0x%s\n", FormatValue(ScbAddress) );
return;
}
if (CheckControlC()) {
return;
}
} while ( TRUE );
}
ULONG
EnumOverflow(
IN PFIELD_INFO ListElement,
IN PVOID Context
)
/*++
Routine Description:
Enumeration callback function to dump the overflow queue
Arguments:
ListElement - Pointer to the containing record
Context - Opaque context passed from the origination function
Return Value:
TRUE to discontinue the enumeration
FALSE to continue the enumeration
--*/
{
ULONG64 IrpContext = ListElement->address;
PDUMP_ENUM_CONTEXT dec = (PDUMP_ENUM_CONTEXT)Context;
ULONG64 OriginatingIrp;
ULONG64 MdlAddress;
if (CheckControlC()) {
return TRUE;
}
OriginatingIrp = ReadValue( IrpContext, SYM(IRP_CONTEXT), "OriginatingIrp" );
if (OriginatingIrp) {
dprintf( "0x%s 0x%x ", FormatValue(OriginatingIrp), ReadValue( OriginatingIrp, SYM(IRP), "Cancel" ) );
MdlAddress = ReadValue( OriginatingIrp, SYM(IRP), "MdlAddress" );
if (MdlAddress) {
dprintf( "0x%s", FormatValue(ReadValue( MdlAddress, NT(MDL), "Process" )) );
}
}
return FALSE;
}
DECLARE_DUMP_FUNCTION( DumpOverflow )
/*++
Routine Description:
Dump the overflow queue
Arguments:
arg - Vcb
Return Value:
None
--*/
{
ULONG64 VcbAddress;
ULONG64 VdoAddress;
ULONG64 Value;
DUMP_ENUM_CONTEXT dec;
INIT_DUMP();
VcbAddress = Address;
VdoAddress = VcbAddress - GetOffset(SYM(VOLUME_DEVICE_OBJECT),"Vcb");
dprintf( "Volume Device: 0x%s Vcb: 0x%s OverflowCount: 0x%x\n",
FormatValue(VdoAddress), FormatValue(VcbAddress),
ReadUlongValue( VdoAddress, SYM(VOLUME_DEVICE_OBJECT), "OverflowQueueCount" ) );
dprintf("\nIrpContext Irp Cancelled Process\n");
Value = ReadValue( VdoAddress, SYM(VOLUME_DEVICE_OBJECT), "OverflowQueue.Flink" );
if (Value && Value != VdoAddress + GetOffset(SYM(VOLUME_DEVICE_OBJECT),"OverflowQueue.Flink")) {
dec.hCurrentThread = hCurrentThread;
dec.Processor = Processor;
dec.Options = Options;
dec.ReturnValue = 0;
ListType( SYM(IRP_CONTEXT), Value, TRUE, "ListEntry.Flink", (PVOID)&dec, EnumOverflow );
}
}
DECLARE_DUMP_FUNCTION( DumpCachedRuns )
/*++
Routine Description:
Dumps the cached runs array
Arguments:
Address - Gives the address of the cached runs array to be dumped
Return Value:
None
--*/
{
ULONG64 AvailRuns;
ULONG64 MaxUsed;
ULONG64 ClusterRunSize;
int Index;
LCN Lcn;
LONGLONG Length;
ULONG64 LcnArray;
USHORT LenIndex;
ULONG64 LengthArray;
ULONG BytesRead;
USHORT WindowStart;
USHORT WindowEnd;
ULONG DelWindowIndex = 0;
ULONG64 DelArray;
ULONG64 DelWindowSize;
ULONG64 DelLengthCount;
LONGLONG PrevLength = -1;
INIT_DUMP();
dprintf( "CachedRun: %p ", Address );
AvailRuns = ReadValue( Address, SYM(NTFS_CACHED_RUNS), "Avail" );
MaxUsed = ReadValue( Address, SYM(NTFS_CACHED_RUNS), "Used" );
ClusterRunSize = GetTypeSize( SYM(NTFS_LCN_CLUSTER_RUN) );
LcnArray = ReadValue( Address, SYM(NTFS_CACHED_RUNS), "LcnArray" );
LengthArray = ReadValue( Address, SYM(NTFS_CACHED_RUNS), "LengthArray" );
DelLengthCount = ReadValue( Address, SYM(NTFS_CACHED_RUNS), "DelLengthCount" );
DelArray = ReadValue( Address, SYM(NTFS_CACHED_RUNS), "DeletedLengthWindows" );
DelWindowSize = GetTypeSize( SYM(NTFS_DELETED_RUNS) );
if (DelWindowIndex < DelLengthCount) {
WindowStart = (USHORT)ReadValue( DelArray + DelWindowSize * DelWindowIndex, SYM(NTFS_DELETED_RUNS), "StartIndex" );
WindowEnd = (USHORT)ReadValue( DelArray + DelWindowSize * DelWindowIndex, SYM(NTFS_DELETED_RUNS), "EndIndex" );
DelWindowIndex++;
}
dprintf( "Avail: 0x%I64x Used: 0x%I64x\n", AvailRuns, MaxUsed );
dprintf( "Lcns ranges sorted by length\n\n" );
for (Index=0; Index < MaxUsed; Index++) {
if (Index == WindowStart) {
dprintf( "DeleteWindow: 0x%x to 0x%x\n", WindowStart, WindowEnd );
Index = WindowEnd;
if (DelWindowIndex < DelLengthCount) {
WindowStart = (USHORT)ReadValue( DelArray + DelWindowSize * DelWindowIndex, SYM(NTFS_DELETED_RUNS), "StartIndex" );
WindowEnd = (USHORT)ReadValue( DelArray + DelWindowSize * DelWindowIndex, SYM(NTFS_DELETED_RUNS), "EndIndex" );
DelWindowIndex++;
}
continue;
}
ReadMemory( LengthArray + Index * sizeof( USHORT ), &LenIndex, sizeof( USHORT ), &BytesRead );
if (NTFS_CACHED_RUNS_DEL_INDEX != LenIndex) {
Lcn = ReadValue( LcnArray + LenIndex * (ClusterRunSize), SYM(NTFS_LCN_CLUSTER_RUN), "Lcn" );
Length = ReadValue( LcnArray + LenIndex * (ClusterRunSize), SYM(NTFS_LCN_CLUSTER_RUN), "RunLength" );
if (Length < PrevLength) {
dprintf( "WARNING: OUT OF ORDER ENTRY\n" );
}
PrevLength = Length;
dprintf( "0x%x: LcnIndex: 0x%x Lcn: 0x%I64x Length: 0x%I64x\n", Index, LenIndex, Lcn, Length );
}
if (CheckControlC()) {
return;
}
}
}