|
|
/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
FilmonKd.c
Abstract:
KD Extension Api for examining FileSpy specific data structures.
Note: While this extension can only build in the Windows XP and Server 2003 environments, it can still be used to debug a version of this FileSpy sample built for Windows 2000.
// @@BEGIN_DDKSPLIT
Author:
Molly Brown [MollyBro] 29-Apr-99
Revision History:
Port to platform independent -
Ravisankar Pudipeddi [ravisp] 3-March-01
// @@END_DDKSPLIT
Environment:
User Mode.
--*/
#include "pch.h"
//
// Windows.h doesn't include this definition
//
typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, *PUNICODE_STRING;
#ifndef MAX
#define MAX(a,b) (((a) > (b))?(a):(b))
#endif
/****************************************************************************
Typedefs and constants ****************************************************************************/ typedef PVOID (*PSTRUCT_DUMP_ROUTINE)( IN ULONG64 Address, IN LONG Options, USHORT Processor, HANDLE hCurrentThread );
//
// The help strings printed out
//
static LPSTR Extensions[] = { "FileSpy Debugger Extensions:\n", "attachments [1|2] Dump all the devices FileSpy is attached to ", "devext [address] [1|2] Dump FileSpy device extension", "filenames [1|2] Dumps all the file names cached", 0 };
/******************************************************************************
Function prototypes ******************************************************************************/ VOID PrintHelp ( VOID );
/******************************************************************************
Useful macros ******************************************************************************/
#define xGetFieldValue(Address, Type, Field, Value) \
{ \ if (GetFieldValue(Address, Type, Field, Value)) { \ dprintf("\nCould not read field %s of %s from address: %08p\n", \ (Field), (Type), (Address)); \ return; \ } \ } #define xGetFieldOffset(Type, Field, Offset) \
{ \ if (GetFieldOffset(Type, Field, Offset)) { \ dprintf("\nCould not read offset of field %s from type %s\n", \ (Field), (Type)); \ return; \ } \ }
/*++
/****************************************************************************
Entry points, parameter parsers, etc. below ****************************************************************************/ VOID DumpDeviceExtension ( IN ULONG64 Address, IN LONG Options, USHORT Processor, HANDLE hCurrentThread )
/*++
Routine Description:
Dump a specific device extension.
Arguments:
Address - Gives the address of the device extension to dump
Return Value:
None
--*/
{ ULONG64 pointer, pName; UNICODE_STRING string1, string2; PUCHAR buffer; USHORT length; ULONG offset, offset2; ULONG result; BOOLEAN boolean;
UNREFERENCED_PARAMETER( Processor ); UNREFERENCED_PARAMETER( hCurrentThread );
dprintf( "\nFileSpy device extension: %08p", Address );
//
// Dump the interesting parts of the device extension
//
if (Options <= 1) { //
// Get the device name length
//
xGetFieldValue(Address, "FileSpy!_FILESPY_DEVICE_EXTENSION", "DeviceNames.Length", length);
// Get offset, and addres of string
//
xGetFieldOffset("FileSpy!_FILESPY_DEVICE_EXTENSION", "DeviceNamesBuffer", &offset); pName = Address+offset;
//
// Allocate buffer to hold string.
// We should not call any xGet* macros before freeing the buffer
// as they might just return from the function on failure
//
buffer = LocalAlloc(LPTR, length); if (buffer == NULL) { return; } //
// Read in the string: assuming it's NULL terminated here..
//
if (ReadMemory(pName, buffer, (ULONG) length, &result) && (result == (ULONG) length)) {
string1.Length = string1.MaximumLength = length; string1.Buffer = (PWSTR) buffer;
dprintf( "\n\t(%3x) %s %wZ", offset, "DeviceNames ", &string1); } //
// Free the buffer
//
LocalFree(buffer); buffer = NULL; xGetFieldOffset("FileSpy!_FILESPY_DEVICE_EXTENSION", "LogThisDevice", &offset); xGetFieldValue(Address, "FileSpy!_FILESPY_DEVICE_EXTENSION", "LogThisDevice", boolean);
dprintf( "\n\t(%3x) %s %s", offset, "LogThisDevice ", (boolean ? "TRUE" : "FALSE") );
} else if (Options == 2) { dprintf( "\n\t(OFF) %s", "FIELD NAME VALUE"); dprintf( "\n\t%s", "----------------------------------------------");
xGetFieldOffset("FileSpy!_FILESPY_DEVICE_EXTENSION", "AttachedToDeviceObject", &offset); xGetFieldValue(Address, "FileSpy!_FILESPY_DEVICE_EXTENSION", "AttachedToDeviceObject", pointer); dprintf( "\n\t(%3x) %s %08p", offset, "AttachedToDeviceObject ", pointer);
xGetFieldOffset("FileSpy!_FILESPY_DEVICE_EXTENSION", "DiskDeviceObject", &offset); xGetFieldValue(Address, "FileSpy!_FILESPY_DEVICE_EXTENSION", "DiskDeviceObject", pointer); dprintf( "\n\t(%3x) %s %08p", offset, "DiskDeviceObject ", pointer);
xGetFieldOffset("FileSpy!_FILESPY_DEVICE_EXTENSION", "LogThisDevice", &offset); xGetFieldValue(Address, "FileSpy!_FILESPY_DEVICE_EXTENSION", "LogThisDevice", boolean); dprintf( "\n\t(%3x) %s %s", offset, "LogThisDevice ", (boolean ? "TRUE" : "FALSE") );
xGetFieldOffset("FileSpy!_FILESPY_DEVICE_EXTENSION", "DeviceNames.Length", &offset); xGetFieldValue(Address, "FileSpy!_FILESPY_DEVICE_EXTENSION", "DeviceNames.Length", length); dprintf( "\n\t(%3x) %s %04x", offset, "DeviceNames.Length(bytes) ", length); //
// Save buffersize, since we need it later to print the string
//
string1.Length = string1.MaximumLength = length; xGetFieldOffset("FileSpy!_FILESPY_DEVICE_EXTENSION", "DeviceNames.MaximumLength", &offset); xGetFieldValue(Address, "FileSpy!_FILESPY_DEVICE_EXTENSION", "DeviceNames.MaximumLength", length); dprintf( "\n\t(%3x) %s %04x", offset, "DeviceNames.MaximumLength(bytes) ", length);
xGetFieldOffset("FileSpy!_FILESPY_DEVICE_EXTENSION", "DeviceNames.Buffer", &offset); xGetFieldValue(Address, "FileSpy!_FILESPY_DEVICE_EXTENSION", "DeviceNames.Buffer", pointer); dprintf( "\n\t(%3x) %s %08p", offset, "DeviceNames.Buffer ", pointer);
xGetFieldOffset("FileSpy!_FILESPY_DEVICE_EXTENSION", "UserNames.Length", &offset); xGetFieldValue(Address, "FileSpy!_FILESPY_DEVICE_EXTENSION", "UserNames.Length", length); dprintf( "\n\t(%3x) %s %04x", offset, "UserNames.Length(bytes) ", length);
//
// Update size of buffer needed
//
string2.Length = string2.MaximumLength = length;
xGetFieldOffset("FileSpy!_FILESPY_DEVICE_EXTENSION", "UserNames.MaximumLength", &offset); xGetFieldValue(Address, "FileSpy!_FILESPY_DEVICE_EXTENSION", "UserNames.MaximumLength", length); dprintf( "\n\t(%3x) %s %04x", offset, "UserNames.MaximumLength(bytes) ", length);
xGetFieldOffset("FileSpy!_FILESPY_DEVICE_EXTENSION", "UserNames.Buffer", &offset); xGetFieldValue(Address, "FileSpy!_FILESPY_DEVICE_EXTENSION", "UserNames.Buffer", pointer); dprintf( "\n\t(%3x) %s %08p", offset, "UserNames.Buffer ", pointer);
//
// Get the device names buffer offset
//
xGetFieldOffset("FileSpy!_FILESPY_DEVICE_EXTENSION", "DeviceNamesBuffer", &offset); pName = Address+offset;
//
// Get the user names buffer offset
//
xGetFieldOffset("FileSpy!_FILESPY_DEVICE_EXTENSION", "UserNamesBuffer", &offset2);
//
// Allocate buffer large enough to hold the largest string
// we will serialize access to it
//
buffer = LocalAlloc(LPTR, MAX(string1.MaximumLength, string2.MaximumLength)); if (buffer == NULL) { return; }
string1.Buffer = string2.Buffer = (PWSTR) buffer;
if (ReadMemory(pName, buffer, string1.Length, &result) && (result == string1.Length)) {
dprintf( "\n\t(%3x) %s %wZ", offset, "DeviceNames ", &string1); }
pName = Address+offset2; if (ReadMemory(pName, buffer, string2.Length, &result) && (result == string2.Length)) {
dprintf( "\n\t(%3x) %s %wZ", offset2, "UserNames ", &string2); }
LocalFree(buffer); buffer = NULL;
} else { dprintf ("\nNot a valid option"); } dprintf( "\n" ); }
VOID DumpAttachments ( IN LONG Options, USHORT Processor, HANDLE hCurrentThread ) /*++
Routine Description:
Dump the list of attached devices that is global to FileSpy.
Arguments:
Options - Ignored for now
Return Value:
None
--*/ { ULONG64 address, next; ULONG64 deviceExtensionAddress; ULONG linkOffset;
UNREFERENCED_PARAMETER( Processor ); UNREFERENCED_PARAMETER( hCurrentThread );
address = GetExpression( "FileSpy!gSpyDeviceExtensionList" );
dprintf( "\nAttachedDeviceList: %08p", address );
//
// Establish offset of the linking entry..
//
xGetFieldOffset("FileSpy!_FILESPY_DEVICE_EXTENSION", "NextFileSpyDeviceLink", &linkOffset); xGetFieldValue(address, "nt!_LIST_ENTRY", "Flink", next);
while (next != address) {
deviceExtensionAddress = (next - linkOffset); // i.e., CONTAINING_RECORD( next, _FILESPY_DEVICE_EXTENSION, NextFileSpyDeviceLink );
DumpDeviceExtension( deviceExtensionAddress, Options, Processor, hCurrentThread );
if (CheckControlC()) { return; }
xGetFieldValue(next, "nt!_LIST_ENTRY", "Flink", next); } }
VOID DumpFileNameCache ( IN LONG Options, USHORT Processor, HANDLE hCurrentThread ) /*++
Routine Description:
Dump all the fileObjects and file names that are currently in the file name cache
Arguments:
Options - 1 dumps just the file objects and file names 2 dumps the hash bucket labels along with the file objects and file names
Return Value:
None
--*/ { ULONG64 address; ULONG64 next; ULONG64 pName; ULONG64 fileObject; ULONG64 pHashEntry; ULONG length; ULONG result; ULONG linkOffset; UNICODE_STRING string; LIST_ENTRY64 listEntry; PUCHAR buffer; INT i; ULONG nameCount = 0;
UNREFERENCED_PARAMETER( Processor ); UNREFERENCED_PARAMETER( hCurrentThread );
address = GetExpression( "FileSpy!gHashTable" ); dprintf( "\nHashTable: %08p\n", address);
dprintf( " FileObject Length FileName\n" ); dprintf( " -----------------------------------------\n" );
xGetFieldOffset("FileSpy!_HASH_ENTRY", "List", &linkOffset);
for (i=0; i < HASH_SIZE; i++) {
if (!ReadListEntry(address, &listEntry)) { dprintf("Can't read hash table\n"); return; }
if (Options > 1) { dprintf ("Hash Bucket[%3d]\n", i); }
next = listEntry.Flink;
while (next != address) {
pHashEntry = next - linkOffset;// CONTAINING_RECORD( next, HASH_ENTRY, List );
xGetFieldValue(pHashEntry, "FileSpy!_HASH_ENTRY", "FileObject", fileObject); xGetFieldValue(pHashEntry, "FileSpy!_HASH_ENTRY", "Name.Length", length);
//
// Get the names buffer pointer
//
xGetFieldValue(pHashEntry, "FileSpy!_HASH_ENTRY", "Name.Buffer", pName); //
// Allocate buffer to hold the string
//
buffer = LocalAlloc(LPTR, length); if (buffer != NULL) { string.MaximumLength = string.Length = (USHORT) length; string.Buffer = (PWSTR) buffer; if (ReadMemory(pName, buffer, length, &result) && (result == length)) { dprintf ( " %08p %4d %wZ\n", fileObject, length/sizeof(WCHAR), &string);
} //
// Free the buffer
//
LocalFree(buffer); buffer = NULL; } else { dprintf("\nCould not allocate buffer to hold filename\n"); }
nameCount ++;
if (CheckControlC()) { dprintf("%u Names in cache\n", nameCount); return; } if (!ReadListEntry(next, &listEntry)) { dprintf("Can't read hash table\n"); return; }
next = listEntry.Flink; } //
// Advance address to next hash entry
//
if (IsPtr64()) { address += sizeof(LIST_ENTRY64); } else { address += sizeof(LIST_ENTRY); } } dprintf("%u Names in cache\n", nameCount); }
VOID ParseAndDump ( IN PCSTR args, IN PSTRUCT_DUMP_ROUTINE DumpFunction, USHORT 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
--*/
{ UCHAR StringStructToDump[1024]; // See other kd routines for size
ULONG64 StructToDump = 0; LONG Options = 0;
//
// If the caller specified an address then that's the item we dump
//
if (args) { StructToDump = 0; Options = 0;
StringStructToDump[0] = '\0';
(VOID) sscanf(args,"%s %lx", StringStructToDump, &Options );
StructToDump = GetExpression( StringStructToDump );
if (StructToDump == 0){ dprintf("unable to get expression %s\n",StringStructToDump); return; }
(*DumpFunction) ( StructToDump, Options, Processor, hCurrentThread );
dprintf( "\n" ); } else { PrintHelp(); } }
VOID PrintHelp ( VOID ) /*++
Routine Description:
Dump out one line of help for each DECLARE_API
Arguments:
None
Return Value:
None
--*/ { int i;
for( i=0; Extensions[i]; i++ ) dprintf( " %s\n", Extensions[i] ); }
DECLARE_API( devext )
/*++
Routine Description:
Dump device extension struct
Arguments:
arg - [Address] [options]
Return Value:
None
--*/
{ UNREFERENCED_PARAMETER( dwCurrentPc ); UNREFERENCED_PARAMETER( hCurrentProcess );
ParseAndDump( args, (PSTRUCT_DUMP_ROUTINE) DumpDeviceExtension, (USHORT)dwProcessor, hCurrentThread ); }
DECLARE_API( attachments )
/*++
Routine Description:
Dumps the list of devices we are currently attached to
Arguments:
arg - [options]
Return Value:
None
--*/
{ LONG options = 0; UNREFERENCED_PARAMETER( dwCurrentPc ); UNREFERENCED_PARAMETER( hCurrentProcess ); (VOID)sscanf(args,"%lx", &options ); DumpAttachments( options, (USHORT)dwProcessor, hCurrentThread ); dprintf( "\n" ); }
DECLARE_API( filenames )
/*++
Routine Description:
Dumps all the entries in the file name cache
Arguments:
arg -
Return Value:
None
--*/
{ LONG options = 0; UNREFERENCED_PARAMETER( dwCurrentPc ); UNREFERENCED_PARAMETER( hCurrentProcess ); (VOID)sscanf(args,"%lx", &options );
DumpFileNameCache(options, (USHORT)dwProcessor, hCurrentThread ); }
DECLARE_API( help )
/*++
Routine Description:
Dump the help for this debugger extension module.
Arguments:
arg - None
Return Value:
None
--*/
{ UNREFERENCED_PARAMETER( args ); UNREFERENCED_PARAMETER( dwCurrentPc ); UNREFERENCED_PARAMETER( hCurrentProcess ); UNREFERENCED_PARAMETER( hCurrentThread ); UNREFERENCED_PARAMETER( dwProcessor );
PrintHelp(); }
|