/*++ Copyright (c) 1992 Microsoft Corporation Module Name: FatKd.c Abstract: KD Extension Api for examining Fat specific data structures Author: Tom Jolly [tomjolly] 14-Sep-99 (ntfskd) Keith Kaplan [KeithKa] 24-Apr-96 Portions by Jeff Havens 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 "..\nodetype.h" #include "..\..\cdfs\nodetype.h" #include "..\..\udfs\nodetype.h" #ifdef UDFS_RW_IN_BUILD #include "..\..\udfsrw\nodetype.h" #endif #include "fatkd.h" #include "cdfskd.h" #include "udfskd.h" // // Lookup table that tells how many clear bits (i.e., 0) there are in a byte // CONST UCHAR BitsClearTotal[] = { 8,7,7,6,7,6,6,5,7,6,6,5,6,5,5,4, 7,6,6,5,6,5,5,4,6,5,5,4,5,4,4,3, 7,6,6,5,6,5,5,4,6,5,5,4,5,4,4,3, 6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2, 7,6,6,5,6,5,5,4,6,5,5,4,5,4,4,3, 6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2, 6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2, 5,4,4,3,4,3,3,2,4,3,3,2,3,2,2,1, 7,6,6,5,6,5,5,4,6,5,5,4,5,4,4,3, 6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2, 6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2, 5,4,4,3,4,3,3,2,4,3,3,2,3,2,2,1, 6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2, 5,4,4,3,4,3,3,2,4,3,3,2,3,2,2,1, 5,4,4,3,4,3,3,2,4,3,3,2,3,2,2,1, 4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0 }; VOID DumpStr( IN ULONG FieldOffset, IN ULONG64 StringAddress, IN PUCHAR Label, IN BOOLEAN CrFirst, IN BOOLEAN Wide ) { WCHAR Buffer[512]; ULONG64 StringChars = 0; // *64 USHORT DisplayLength; UNICODE_STRING String; STRING AString; ROE( GetFieldValue( StringAddress, "UNICODE_STRING", "Buffer", StringChars)); ROE( GetFieldValue( StringAddress, "UNICODE_STRING", "Length", DisplayLength)); ROE( GetFieldValue( StringAddress, "UNICODE_STRING", "MaximumLength", String.MaximumLength)); if (DisplayLength > sizeof( Buffer)) { DisplayLength = sizeof(Buffer); } if (CrFirst) { dprintf("\n"); } if (DisplayLength) { if (StringChars) { ReadM( Buffer, StringChars, DisplayLength); String.Length = DisplayLength; String.Buffer = Buffer; } else { AString.Buffer = "<>"; AString.Length = AString.MaximumLength = 23; Wide = FALSE; } } else { AString.Buffer = "<>"; AString.Length = AString.MaximumLength = 9; Wide = FALSE; } if (Wide) { dprintf( "%s: (%03x/%03x) '%wZ'\n", Label, DisplayLength, String.MaximumLength, &String ); } else { dprintf( "%s: (%03x/%03x) '%Z'\n", Label, DisplayLength, AString.MaximumLength, &AString ); } } ULONG Dt( IN UCHAR *Type, IN ULONG64 Addr, IN ULONG Recur, IN ULONG FieldInfoCount, IN FIELD_INFO FieldInfo[] ) { SYM_DUMP_PARAM Param; Param.size = sizeof( Param); Param.sName = Type; Param.Options = DBG_DUMP_RECUR_LEVEL(Recur); Param.addr = Addr; Param.listLink = NULL; Param.Context = NULL; Param.CallbackRoutine = NULL; Param.nFields = FieldInfoCount; Param.Fields = FieldInfo; return Ioctl( IG_DUMP_SYMBOL_INFO, &Param, Param.size); } // // The help strings printed out // static LPSTR Extensions[] = { "FAT/CDFS/UDFS Debugger Extension Commands, where...\n\n [FS] => CD/UDF/FAT\n -> in output denotes address of embedded structure\n", " [FS]mcb [addr] [1] Dump MCB", " [1] Dump out extent list", " [FS]vdo [addr] Volume device object (from direct addr *OR* VCB addr)", " fo [addr] [1] Dump FileObject." "\n [1] Also dump associated Fcb/Ccb structures", " [2] dump SHARED_CACHE_MAP", " ", " d [addr] [...] Dump any tagged structure (any FS). Understands...", " VCB, CCB, fsDATA, IRPCONTEXT, IRPCONTEXTLITE," "\n FCB / SCB (** see command ouput for further options)", " and (UDF ONLY) PCB, VMCB,", " LCB - options", " [1] dump associated FCB" " ", " For structures without options, second parameter is dump recurse level", " ", " db [addr] [recur] As !d, but just a straight DT of the structure based on node type", " ", " cdrawdirent [addr] [count] Dump a COUNT raw ISO9660 dirents starting at ADDR", " ", " udficbsc [addr] (UDFS only) Dump an ICB_CONTEXT", " udftag [addr] Dump out a UDF on disk format descriptor tag in detail (3/7.2)", " ud [addr] [...] Dump any on disc format UDF structure which has a descr.tag", " Currently: PVD,AVDP,VDP,PD,LVD,FSD,FID,FE", " [1] (FE) Dump allocation decriptors", " ", " vmcb [addr] Use on pre 1.10.2000 builds (vmcb not tagged) rather than !d ", " ", " tree [addr] (UDFS only) Dumps a summary of the F/Scb tree (names, ref counts) from", " the specified Scb down. If a Vcb address is specified will", " dump from root down", " ", " udfsplay [addr] [opt] Where (addr) is SCB or LCB address. Dumps out the (ignore case) splay ", " tree below. Opt: 0 -> ignore case, 1 -> exact case, 2 -> short names", " ", " vols List all local file system volumes (Vcb/Vpb/Device/Label etc.)", " ", " countclearbits [addr] [bytes] Count clear bits in mem range", " countdwords [addr] [dword] Count occurences of dword in mem range", " ", " splay [addr] [linksoffsetinstructure] Dumps a list of structures in an RTL_SPLAY_TREE", " given pointer to RTL_SPLAY_LINKS and offset of links", " within containing structure", " ", " fshelp ...", 0 }; STATE FoFlags[] = { { FO_FILE_OPEN, FO_FILE_OPEN, "Open" }, { FO_SYNCHRONOUS_IO, FO_SYNCHRONOUS_IO, "Synchronous" }, { FO_ALERTABLE_IO, FO_ALERTABLE_IO, "Alertable" }, { FO_NO_INTERMEDIATE_BUFFERING, FO_NO_INTERMEDIATE_BUFFERING, "NoIntermediateBuffering" }, { FO_WRITE_THROUGH, FO_WRITE_THROUGH, "WriteThrough"}, { FO_SEQUENTIAL_ONLY, FO_SEQUENTIAL_ONLY, "SequentialOnly"}, { FO_CACHE_SUPPORTED, FO_CACHE_SUPPORTED, "CacheSupported"}, { FO_NAMED_PIPE, FO_NAMED_PIPE, "NamedPipe"}, { FO_STREAM_FILE, FO_STREAM_FILE, "StreamFile"}, { FO_MAILSLOT, FO_MAILSLOT, "MailSlot"}, { FO_GENERATE_AUDIT_ON_CLOSE, FO_GENERATE_AUDIT_ON_CLOSE, "AuditOnClose"}, { FO_DIRECT_DEVICE_OPEN, FO_DIRECT_DEVICE_OPEN, "DirectDeviceOpen"}, { FO_FILE_MODIFIED, FO_FILE_MODIFIED, "Modified"}, { FO_FILE_SIZE_CHANGED, FO_FILE_SIZE_CHANGED, "SizeChanged"}, { FO_CLEANUP_COMPLETE, FO_CLEANUP_COMPLETE, "CleanupComplete"}, { FO_TEMPORARY_FILE, FO_TEMPORARY_FILE, "Temporary"}, { FO_DELETE_ON_CLOSE, FO_DELETE_ON_CLOSE, "DeleteOnClose"}, { FO_OPENED_CASE_SENSITIVE, FO_OPENED_CASE_SENSITIVE, "CaseSensitive"}, { FO_HANDLE_CREATED, FO_HANDLE_CREATED, "HandleCreated"}, { FO_FILE_FAST_IO_READ, FO_FILE_FAST_IO_READ, "FastIoRead"}, { FO_RANDOM_ACCESS, FO_RANDOM_ACCESS, "RandomAccess"}, { FO_FILE_OPEN_CANCELLED, FO_FILE_OPEN_CANCELLED, "OpenCancelled"}, { FO_VOLUME_OPEN, FO_VOLUME_OPEN, "VolumeOpen"}, { 0 } }; // // FSRTL Common header flags // STATE HeaderFlags[] = { { FSRTL_FLAG_FILE_MODIFIED, FSRTL_FLAG_FILE_MODIFIED, "Modified"}, { FSRTL_FLAG_FILE_LENGTH_CHANGED, FSRTL_FLAG_FILE_LENGTH_CHANGED, "LengthChanged"}, { FSRTL_FLAG_LIMIT_MODIFIED_PAGES, FSRTL_FLAG_LIMIT_MODIFIED_PAGES, "LimitModPages"}, { FSRTL_FLAG_ACQUIRE_MAIN_RSRC_EX, FSRTL_FLAG_ACQUIRE_MAIN_RSRC_EX, "ModWrAcqMainEx"}, { FSRTL_FLAG_ACQUIRE_MAIN_RSRC_SH, FSRTL_FLAG_ACQUIRE_MAIN_RSRC_SH, "ModWrAcqMainSh"}, { FSRTL_FLAG_USER_MAPPED_FILE, FSRTL_FLAG_USER_MAPPED_FILE, "UserMapped"}, { FSRTL_FLAG_ADVANCED_HEADER, FSRTL_FLAG_ADVANCED_HEADER, "AdvancedHeader"}, { FSRTL_FLAG_EOF_ADVANCE_ACTIVE, FSRTL_FLAG_EOF_ADVANCE_ACTIVE, "EofAdvanceActive"}, { 0 } }; STATE HeaderFlags2[] = { { FSRTL_FLAG2_DO_MODIFIED_WRITE, FSRTL_FLAG2_DO_MODIFIED_WRITE, "DoModWrite"}, { FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS, FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS, "FilterContexts"}, { FSRTL_FLAG2_PURGE_WHEN_MAPPED, FSRTL_FLAG2_PURGE_WHEN_MAPPED, "PurgeOnMap"}, { 0 } }; ULONG DumpRtlSplay( IN ULONG64 RemoteAddress, IN ELEMENT_DUMP_ROUTINE ProcessElementRoutine, IN ULONG OffsetToContainerStart, IN ULONG Options ) { ULONG64 Left,Right,Parent; ULONG E; if (0 == RemoteAddress) { return 0; } if ((*ExtensionApis.lpCheckControlCRoutine)()) { dprintf("CTRL+C - aborting\n"); // AV, no easy way to bail from the recursion. *((PULONG)0) = 0; } if (E = GetFieldValue( RemoteAddress, "nt!_RTL_SPLAY_LINKS", "LeftChild", Left)) { dprintf("Error %d reading LEFT child link from node at %8I64x\n", E, RemoteAddress - OffsetToContainerStart); return E; } if (E = GetFieldValue( RemoteAddress, "nt!_RTL_SPLAY_LINKS", "RightChild", Right)) { dprintf("Error %d reading RIGHT child link from node at %8I64x\n", E, RemoteAddress - OffsetToContainerStart); return E; } // // Recurse down the left subtree // if (DumpRtlSplay( Left, ProcessElementRoutine, OffsetToContainerStart, Options)) { dprintf("Error ocurred while processing node through LEFT link from node %I64x\n", RemoteAddress - OffsetToContainerStart); } // // Dump this element // (ProcessElementRoutine)(RemoteAddress - OffsetToContainerStart, Options); // // Recurse down the right subtree // if (DumpRtlSplay( Right, ProcessElementRoutine, OffsetToContainerStart, Options)) { dprintf("Error ocurred while processing node through RIGHT link from node %I64x\n", RemoteAddress - OffsetToContainerStart); } return 0; } VOID DumpSplayNodeSimple( IN ULONG64 RemoteAddress, IN LONG Options ) { dprintf("%I64x\n", RemoteAddress); } DUMP_ROUTINE( SimpleSplay) { DumpRtlSplay( Address, DumpSplayNodeSimple, Options, 0); } VOID DumpList( IN ULONG64 RemoteListEntryAddress, IN ELEMENT_DUMP_ROUTINE ProcessElementRoutine, IN ULONG OffsetToContainerStart, IN BOOLEAN ProcessThisEntry, IN ULONG Options ) { LIST_ENTRY64 Entry; ULONG64 CurrentEntryRemoteAddress = 0; // // Read the first LIST_ENTRY structure // ROE( !ReadListEntry( RemoteListEntryAddress, &Entry)); // // Scan through until we end up back at the 3start again. // while (CurrentEntryRemoteAddress != RemoteListEntryAddress) { if (ProcessThisEntry) { (ProcessElementRoutine)( CurrentEntryRemoteAddress - OffsetToContainerStart, Options); } else { ProcessThisEntry = TRUE; } CurrentEntryRemoteAddress = Entry.Flink; ROE( !ReadListEntry( CurrentEntryRemoteAddress, &Entry)); } } /* * Dump structures */ typedef BOOLEAN (WINAPI *PENUM_PROCESS_CALLBACK)(PVOID ProcessAddress, PVOID Process, PVOID ThreadAddress, PVOID Thread); typedef VOID (* ENUM_PROCESSES_FUNC)(PENUM_PROCESS_CALLBACK CallBack); VOID PrintState(STATE *ps, ULONG state) { ULONG ul = 0; if (NULL == ps) { dprintf(" << NULL flags supplied! >>\n"); return; } 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"); } DUMP_ROUTINE( DumpVmcb); DUMP_ROUTINE( DumpFileObject); DUMP_ROUTINE( DumpAnyStruct); VOID PrintHelp ( VOID ); // // NULL dump function means just do DT with type. // NODE_TYPE_INFO_NEW NewNodeTypeCodes[] = { // RO UDFS { UDFS_NTC_DATA_HEADER, "UDFS DATA", "Udfs!UDF_DATA", DumpUdfData}, { UDFS_NTC_VCB, "UDFS VCB", "Udfs!VCB", DumpUdfVcb}, { UDFS_NTC_FCB_INDEX, "UDFS FCB (INDEX)", "Udfs!FCB", DumpUdfFcb}, { UDFS_NTC_FCB_DATA, "UDFS FCB (DATA) ", "Udfs!FCB", DumpUdfFcb}, { UDFS_NTC_FCB_NONPAGED, "UDFS FCB (nonpaged)", "Udfs!FCB_NONPAGED", NULL}, { UDFS_NTC_CCB, "UDFS CCB", "Udfs!CCB", DumpUdfCcb}, { UDFS_NTC_IRP_CONTEXT, "UDFS IRPCONTEXT", "Udfs!IRP_CONTEXT", DumpUdfIrpContext}, { UDFS_NTC_IRP_CONTEXT_LITE, "UDFS IRPCONTEXT LITE", "Udfs!IRP_CONTEXT_LITE",DumpUdfIrpContextLite}, { UDFS_NTC_LCB, "UDFS LCB", "Udfs!LCB", DumpUdfLcb}, { UDFS_NTC_PCB, "UDFS PCB", "Udfs!PCB", DumpUdfPcb}, { UDFS_NTC_VMCB, "UDFS VMCB", "Udfs!VMCB", DumpVmcb}, #ifdef UDFS_RW_IN_BUILD // RW UDFS, same dump fn. { UDFSRW_NTC_DATA_HEADER, "UDFSRW DATA", "Udfs!UDF_DATA", DumpUdfData}, { UDFSRW_NTC_IRP_CONTEXT, "UDFSRW IRPCONTEXT", "Udfs!IRP_CONTEXT", DumpUdfIrpContext}, { UDFSRW_NTC_IRP_CONTEXT_LITE,"UDFSRW IRPCONTEXT LITE", "Udfs!IRP_CONTEXT_LITE",DumpUdfIrpContextLite}, { UDFSRW_NTC_CCB, "UDFSRW CCB", "Udfs!CCB", DumpUdfCcb}, { UDFSRW_NTC_LCB, "UDFSRW LCB", "Udfs!LCB", DumpUdfLcb}, { UDFSRW_NTC_PCB, "UDFSRW PCB", "Udfs!PCB", DumpUdfPcb}, { UDFSRW_NTC_VCB, "UDFSRW VCB", "Udfs!VCB", DumpUdfVcb}, { UDFSRW_NTC_VMCB, "UDFSRW VMCB", "Udfs!VMCB", DumpVmcb}, { UDFSRW_NTC_FCB, "UDFSRW FCB", "Udfs!FCB", DumpUdfFcb}, // TODO: Dump stream SCB list? { UDFSRW_NTC_FCB_NONPAGED, "UDFSRW FCB (nonpaged)", "Udfs!FCB_NONPAGED", DumpUdfFcb}, // RW UDFS specific dump fn. { UDFSRW_NTC_SCB_INDEX, "UDFSRW SCB (INDEX)", "Udfs!SCB_INDEX_TYPE", DumpUdfScb}, { UDFSRW_NTC_SCB_DATA, "UDFSRW SCB (DATA) ", "Udfs!SCB_DATA_TYPE", DumpUdfScb}, { UDFSRW_NTC_SCB_NONPAGED, "UDFSRW SCB (nonpaged)", "Udfs!SCB_NONPAGED", NULL}, #endif { FAT_NTC_VCB, "FAT VCB", "FastFat!VCB", DumpFatVcb}, { FAT_NTC_FCB, "FAT FCB", "FastFat!FCB", DumpFatFcb}, { FAT_NTC_DCB, "FAT DCB", "FastFat!FCB", DumpFatFcb}, { FAT_NTC_CCB, "FAT CCB", "FastFat!CCB", DumpFatCcb}, { FAT_NTC_ROOT_DCB, "FAT ROOT DCB", "FastFat!FCB", DumpFatFcb}, { FAT_NTC_IRP_CONTEXT, "FAT IRP CONTEXT", "FastFat!IRP_CONTEXT", DumpFatIrpContext}, { FAT_NTC_DATA_HEADER, "FAT DATA", "FastFat!FAT_DATA", NULL}, { CDFS_NTC_DATA_HEADER, "CDFS DATA", "Cdfs!CD_DATA", NULL}, { CDFS_NTC_VCB, "CDFS VCB", "Cdfs!VCB", DumpCdVcb}, { CDFS_NTC_FCB_PATH_TABLE, "CDFS FCB (PATH TABLE)","Cdfs!FCB", DumpCdFcb}, { CDFS_NTC_FCB_INDEX, "CDFS FCB (INDEX)", "Cdfs!FCB", DumpCdFcb}, { CDFS_NTC_FCB_DATA, "CDFS FCB (DATA) ", "Cdfs!FCB", DumpCdFcb}, { CDFS_NTC_FCB_NONPAGED, "CDFS FCB (nonpaged)", "Cdfs!FCB_NONPAGED", NULL}, { CDFS_NTC_CCB, "CDFS CCB", "Cdfs!CCB", DumpCdCcb}, { CDFS_NTC_IRP_CONTEXT, "CDFS IRPCONTEXT", "Cdfs!IRP_CONTEXT", DumpCdIrpContext}, { CDFS_NTC_IRP_CONTEXT_LITE, "CDFS IRPCONTEXT (LITE)","Cdfs!IRP_CONTEXT_LITE",NULL}, // { CACHE_NTC_SHARED_CACHE_MAP, "CC Shared Cache Map", "nt!SHARED_CACHE_MAP", NULL}, { 0, "Undefined", NULL, NULL} }; ULONG SearchTypeCodeIndex ( IN USHORT TypeCode, IN NODE_TYPE_INFO_NEW TypeCodes[] ) /*++ Routine Description: Guess at a structure's type code Arguments: TypeCode - Type code from the data structure Return Value: None --*/ { int i = 0; while (TypeCodes[i].TypeCode != 0 && TypeCodes[i].TypeCode != TypeCode) { i++; } return i; } DUMP_ROUTINE( DumpFileObject) { ULONG Result; ULONG Flags, Offset; USHORT ObType; ULONG64 Adr, Scm; dprintf( "\nFileObject @ %I64X\n", Address ); dprintf( "\n[ Options: 1 = dump FsContext & FsContext2, 2 = dump shared cache map ]\n"); ReadM( &ObType, Address, sizeof( USHORT)); if (ObType != IO_TYPE_FILE) { dprintf( "Invalid signature, probably not a file object" ); return; } GetFieldValue( Address, "nt!FILE_OBJECT", "Flags", Flags); dprintf("\nFlags: "); PrintState( FoFlags, Flags); dprintf( "\n" ); Dt( "nt!FILE_OBJECT", Address, 0, 0, NULL); dprintf( "\n" ); GetFieldValue( Address, "nt!FILE_OBJECT", "SectionObjectPointer", Adr); if (Adr) { Dt( "nt!SECTION_OBJECT_POINTERS", Adr, 0, 0, NULL); } GetFieldValue( Adr, "nt!SECTION_OBJECT_POINTERS", "SharedCacheMap", Scm); if (Options & 2) { dprintf( "\n" ); Dt( "nt!SHARED_CACHE_MAP", Scm, 0, 0, NULL); } if (Options & 1) { GetFieldValue( Address, "nt!FILE_OBJECT", "FsContext", Adr); if (Adr) { DumpAnyStruct( Adr, 0, NULL); } GetFieldValue( Address, "nt!FILE_OBJECT", "FsContext2", Adr); if (Adr) { DumpAnyStruct( Adr, 0, NULL); } } dprintf( "\n" ); } DUMP_ROUTINE( DumpVmcb) { ULONG Result; ULONG Offset; dprintf( "\nVMCB @ %I64X\n", Address ); Dt( "udfs!VMCB", Address, 0, 0, NULL); #ifdef UDFS_RW_IN_BUILD if (Options & 2) { USHORT NodeType; // // UDFS in Win2k had no nodetype field in the vmcb // if (!GetFieldValue( Address, "udfs!VMCB", "NodeTypeCode", NodeType)) { if (NodeType == UDFSRW_NTC_VMCB) { // // Summarize the dirty bitmap, if // these fields are present. // // TODO: Dump dirty blocks } } } #endif if (Options & 1) { dprintf("\n\nLbn->Vbn Mappings\n"); ROE( GetFieldOffset( "udfs!VMCB", "LbnIndexed", &Offset)); DumpLargeMcb( Address + Offset, Options, NULL); dprintf("\nVbn->Lbn Mappings\n"); ROE( GetFieldOffset( "udfs!VMCB", "VbnIndexed", &Offset)); DumpLargeMcb( Address + Offset, Options, NULL); } dprintf("\n"); } VOID CountClearBits( IN UINT64 Address, IN ULONG Length ) { UCHAR Buffer[2048]; ULONG Result; ULONG ThisBytes; ULONG ClearBits = 0; ULONG CurrByte; // address, length dprintf("\nCounting clear bits in range 0x%p L 0x%x\n", Address, Length); while (Length) { ThisBytes = (Length > sizeof( Buffer)) ? sizeof( Buffer) : Length; if ( !ReadMemory( (DWORD_PTR)Address, Buffer, ThisBytes, &Result) ) { dprintf( "Unable to read %d bytes at %p\n", ThisBytes, Address); return; } for (CurrByte = 0; CurrByte < ThisBytes; ++CurrByte) { ClearBits += BitsClearTotal[ Buffer[CurrByte]]; } Address += ThisBytes; Length -= ThisBytes; } dprintf("\n0x%x bits clear.\n\n", ClearBits); } VOID CountDwords( IN UINT64 Address, IN ULONG Length, IN ULONG Dword ) { ULONG Buffer[512]; ULONG Result; ULONG ThisBytes; ULONG Count = 0; ULONG CurrByte; // address, length dprintf("\nCounting ocurrences of DWORD 0x%x in range 0x%p L 0x%x\n", Dword, Address, Length); if (Length & 3) { dprintf("Length must be multiple of 4 bytes\n"); return; } while (Length) { ThisBytes = (Length > sizeof( Buffer)) ? sizeof( Buffer) : Length; if ( !ReadMemory( (DWORD_PTR)Address, Buffer, ThisBytes, &Result) ) { dprintf( "Unable to read %d bytes at %p\n", ThisBytes, Address); return; } for (CurrByte = 0; CurrByte < (ThisBytes >> 2); ++CurrByte) { if (Buffer[ CurrByte] == Dword) { ++Count; } } Address += ThisBytes; Length -= ThisBytes; } dprintf("\n0x%x found.\n\n", Count); } DUMP_ROUTINE( DumpAnyStruct) /*++ Routine Description: Dump a tagged structure, guessing based on node type code. Arguments: Address - Gives the address of the structure Return Value: None --*/ { STRUCT_DUMP_ROUTINE Routine; ULONG TagAndSize, *TagAndSizePtr, Result, Error; LONG InfoIndex; RM( Address, TagAndSize, TagAndSizePtr, PULONG, Result ); // // Find out what function should be used to dump the stucture (if we can) // InfoIndex = TypeCodeInfoIndex( (USHORT)(TagAndSize & 0xffff)); Routine = NodeTypeDumpFunction( InfoIndex); if ( 0 == NewNodeTypeCodes[ InfoIndex].TypeCode) { dprintf( "\nNode type %0x%04x unknown.\n", (USHORT)TagAndSize & 0xffff); return; } dprintf("\n%s @ 0x%I64X\n\n", NewNodeTypeCodes[InfoIndex].Text, Address); if (Routine) { // // And call it... // (Routine)(Address, Options, &NewNodeTypeCodes[InfoIndex]); } else { // // No special routine - just DT it, treating first parameter following // address as level of recursion // Error = Dt( NewNodeTypeCodes[InfoIndex].TypeName, Address, Options, 0, NULL); if (Error) { dprintf("Error %d from debugger when dumping structure\n", Error); } } } DUMP_ROUTINE( DtAnyStruct) /*++ Routine Description: Dump a tagged structure, guessing based on node type code. Arguments: Address - Gives the address of the structure Return Value: None --*/ { ULONG TagAndSize, *TagAndSizePtr, Result; ULONG Error; LONG InfoIndex; RM( Address, TagAndSize, TagAndSizePtr, PULONG, Result ); InfoIndex = TypeCodeInfoIndex( (USHORT)(TagAndSize & 0xffff)); dprintf("\n%s @ 0x%X\n\n", NewNodeTypeCodes[InfoIndex].Text, Address); Error = Dt( NewNodeTypeCodes[InfoIndex].TypeName, Address, Options, 0, NULL); if (Error) { dprintf("Error %d\n", Error); } } // // Entry points, parameter parsers, etc. below // VOID ParseAndDump ( IN PCHAR args, IN STRUCT_DUMP_ROUTINE DumpFunction, ULONG Processor, HANDLE hCurrentThread ) /*++ Routine Description: Parse command line arguments and dump an ntfs structure. Arguments: Args - String of arguments to parse. DumpFunction - Function to call with parsed arguments. Return Value: None --*/ { CHAR StringStructToDump[1024]; // See other kd routines for size CHAR Dummy[24]; LARGE_INTEGER StructToDump; LONG Options; LONG ret; // // If the caller specified an address then that's the item we dump // StructToDump.QuadPart = 0; Options = 0; StringStructToDump[0] = '\0'; //dprintf("Args %s\n", args); ret = sscanf( args,"%s %lx", StringStructToDump, &Options ); //dprintf("GetExpr %s\n", StringStructToDump); if (!GetExpressionEx( StringStructToDump, &StructToDump.QuadPart, NULL)) { dprintf("GetExpression failed\n"); return; } //dprintf("Getexpr returned %I64X\n", StructToDump.QuadPart); if (!StructToDump.QuadPart){ dprintf("unable to get expression %s\n",StringStructToDump); return; } (*DumpFunction) ( StructToDump.QuadPart, Options, NULL ); dprintf( "\n" ); } VOID PrintHelp ( VOID ) { int i; for( i=0; Extensions[i]; i++ ) dprintf( " %s\n", Extensions[i] ); } DECLARE_API( fshelp ) { UNREFERENCED_PARAMETER( args ); UNREFERENCED_PARAMETER( dwProcessor ); UNREFERENCED_PARAMETER( dwCurrentPc ); UNREFERENCED_PARAMETER( hCurrentProcess ); UNREFERENCED_PARAMETER( hCurrentThread ); PrintHelp(); } DECLARE_API( d) { ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpAnyStruct, dwProcessor, hCurrentThread ); } DECLARE_API( fo) { ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpFileObject, dwProcessor, hCurrentThread ); } DECLARE_API( vmcb) { ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpVmcb, dwProcessor, hCurrentThread ); } DECLARE_API( db ) { UNREFERENCED_PARAMETER( dwCurrentPc ); UNREFERENCED_PARAMETER( hCurrentProcess ); ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DtAnyStruct, dwProcessor, hCurrentThread ); } UCHAR *FsDataNames[] = { "Udfs!UdfData", "Cdfs!CdData", "Fastfat!FatData", "Ntfs!NtfsData", "" }; UCHAR *FsDataTypes[] = { "udfs!UDF_DATA", "cdfs!CD_DATA", "fastfat!FAT_DATA", "ntfs!NTFS_DATA", "" }; DECLARE_API( vols ) { ULONG Result; UINT64 Addr; UINT64 CurrListEntry, CurrVcb, RealDo, VolDo, Vpb, VpbOnRealDo, QueueHead; USHORT LabelLen; ULONG LinksOffset, LabelOffset; ULONG CurrName = 0, FsNameLen; UCHAR FsName[10]; UCHAR FsVcbName[10]; WCHAR VolLabel[32]; UNICODE_STRING Label; BOOLEAN Itanic = (BOOLEAN)IsPtr64(); UNREFERENCED_PARAMETER( dwCurrentPc ); UNREFERENCED_PARAMETER( hCurrentProcess ); if (Itanic) { dprintf("\nVCB VolDev VPB RealDev On Device? / Label\n"); } else { dprintf("\nVCB VolDev VPB RealDev On Device? / Label\n"); } while ( *FsDataNames[CurrName] != 0) { if (!GetExpressionEx( FsDataNames[CurrName], &Addr, NULL ) || (0 == Addr)) { dprintf("\n<< Failed to lookup '%s' - skipping FS >>\n", FsDataNames[CurrName]); } else { ROE( GetFieldValue( Addr, FsDataTypes[CurrName], "VcbQueue.Flink", CurrListEntry)); ROE( GetFieldOffset( FsDataTypes[CurrName], "VcbQueue", &LinksOffset)); QueueHead = Addr + LinksOffset; if (CurrListEntry != QueueHead) { FsNameLen = (ULONG)(strchr( FsDataNames[CurrName], '!') - FsDataNames[CurrName]); strncpy( FsName, FsDataNames[CurrName], FsNameLen); FsName[FsNameLen] = 0; sprintf( FsVcbName, "%s!VCB", FsName); ROE( GetFieldOffset( FsVcbName, "VcbLinks", &LinksOffset)); ROE( GetFieldOffset( "nt!VPB", "VolumeLabel", &LabelOffset)); dprintf("\n<< %s >>\n\n", FsName); while (CurrListEntry != QueueHead) { CurrVcb = CurrListEntry - LinksOffset; ROE( GetFieldValue( CurrVcb, FsVcbName, "Vpb", Vpb)); if (0 != Vpb) { ROE( GetFieldValue( Vpb, "nt!VPB", "DeviceObject", VolDo)); ROE( GetFieldValue( Vpb, "nt!VPB", "RealDevice", RealDo)); ROE( GetFieldValue( Vpb, "nt!VPB", "VolumeLabelLength", LabelLen)); ROE( GetFieldValue( RealDo, "nt!DEVICE_OBJECT", "Vpb", VpbOnRealDo)); if (LabelLen > sizeof( VolLabel)) { dprintf("Warning - VPB label length is > max allowed\n"); Label.Buffer = NULL; Label.Length = Label.MaximumLength = 0; } else { ReadM( VolLabel, (ULONG)(Vpb + LabelOffset), LabelLen); Label.Buffer = VolLabel; Label.Length = Label.MaximumLength = LabelLen; } } else { dprintf("Null VPB! "); Vpb = VolDo = RealDo = VpbOnRealDo = 0; Label.Length = 0; } if (Itanic) { dprintf("%I64x %I64x %I64x %I64x %s '%wZ'\n", CurrVcb, VolDo, Vpb, RealDo, (Vpb == VpbOnRealDo) ? "YES" : "NO ", &Label); } else { dprintf("%8x %8x %8x %8x %s '%wZ'\n", (ULONG)CurrVcb, (ULONG)VolDo, (ULONG)Vpb, (ULONG) RealDo, (Vpb == VpbOnRealDo) ? "YES" : "NO ", &Label); } ROE( GetFieldValue( CurrListEntry, "nt!LIST_ENTRY", "Flink", CurrListEntry)); } } } CurrName += 1; } } DECLARE_API( countclearbits) { CHAR StringStructToDump[1024]; // See other kd routines for size UINT64 StructToDump; ULONG Length; LONG ret; // // If the caller specified an address then that's the item we dump // StructToDump = 0; Length = 0; StringStructToDump[0] = '\0'; ret = sscanf(args,"%s %lx", StringStructToDump, &Length ); if (!GetExpressionEx( StringStructToDump, &StructToDump, NULL )) { dprintf("unable to get expression %s\n",StringStructToDump); return; } CountClearBits( StructToDump, Length); } DECLARE_API( countdwords) { CHAR StringStructToDump[1024]; // See other kd routines for size UINT64 StructToDump; ULONG Length; ULONG Dword; LONG ret; // // If the caller specified an address then that's the item we dump // StructToDump = 0; Length = 0; StringStructToDump[0] = '\0'; ret = sscanf(args,"%s %lx %lx", StringStructToDump, &Length, &Dword); if (!GetExpressionEx( StringStructToDump, &StructToDump, NULL)) { dprintf("unable to get expression %s\n",StringStructToDump); return; } CountDwords( StructToDump, Length, Dword); } DECLARE_API( dumpcclog) { CHAR StringStart[2048]; UINT64 Start; ULONG Length; ULONG Dword; ULONG Current; ULONG Result; PULONG Buffer = (PULONG)StringStart; LONG ret; // // If the caller specified an address then that's the item we dump // Start = 0; Length = 0; StringStart[0] = '\0'; ret = sscanf(args,"%s %lx", StringStart, &Length); if (!GetExpressionEx( StringStart, &Start, NULL)) { dprintf("unable to get expression %s\n", StringStart); return; } if ( !ReadMemory( (DWORD_PTR)Start, Buffer, sizeof( StringStart), &Result) ) { dprintf( "Unable to read %d bytes at %p\n", sizeof( StringStart), Start); return; } dprintf("\n"); for ( Current = 0; Current < (Length >> 2); Current += 1) { switch( Buffer[Current]) { case -1: dprintf("FlushCache - Enter\n"); break; case -2: dprintf("FlushCache - Leave\n"); break; case -3: dprintf("PurgeCacheSection\n"); break; default: dprintf("%d 0x%08x 0x%08x\n", Buffer[Current] & 3, Buffer[Current] & ~(ULONG)0xfff, ((Buffer[Current] >> 2) & 0x3ff)<<12); break; } } } DECLARE_API( splay) { UNREFERENCED_PARAMETER( dwCurrentPc ); UNREFERENCED_PARAMETER( hCurrentProcess ); ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) SimpleSplay, dwProcessor, hCurrentThread ); }