1302 lines
37 KiB
C
1302 lines
37 KiB
C
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
kdextlib.c
|
|
|
|
Abstract:
|
|
|
|
Library routines for dumping data structures given a meta level descrioption
|
|
|
|
Author:
|
|
|
|
Balan Sethu Raman (SethuR) 11-May-1994
|
|
|
|
Notes:
|
|
The implementation tends to avoid memory allocation and deallocation as much as possible.
|
|
Therefore We have choosen an arbitrary length as the default buffer size. A mechanism will
|
|
be provided to modify this buffer length through the debugger extension commands.
|
|
|
|
Revision History:
|
|
|
|
11-Nov-1994 SethuR Created
|
|
|
|
--*/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include "ntverp.h"
|
|
|
|
#define KDEXTMODE
|
|
|
|
#include <windef.h>
|
|
#include <ntkdexts.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include <kdextlib.h>
|
|
#include <..\..\inc\types.h>
|
|
|
|
PNTKD_OUTPUT_ROUTINE lpOutputRoutine;
|
|
PNTKD_GET_EXPRESSION lpGetExpressionRoutine;
|
|
PNTKD_GET_SYMBOL lpGetSymbolRoutine;
|
|
PNTKD_READ_VIRTUAL_MEMORY lpReadMemoryRoutine;
|
|
|
|
#define PRINTF lpOutputRoutine
|
|
#define ERROR lpOutputRoutine
|
|
|
|
#define NL 1
|
|
#define NONL 0
|
|
|
|
#define MAX_LIST_ELEMENTS 4096
|
|
BYTE DataBuffer[4096];
|
|
|
|
#define SETCALLBACKS() \
|
|
lpOutputRoutine = lpExtensionApis->lpOutputRoutine; \
|
|
lpGetExpressionRoutine = lpExtensionApis->lpGetExpressionRoutine; \
|
|
lpGetSymbolRoutine = lpExtensionApis->lpGetSymbolRoutine; \
|
|
lpReadMemoryRoutine = lpExtensionApis->lpReadVirtualMemRoutine;
|
|
|
|
#define DEFAULT_UNICODE_DATA_LENGTH 4096
|
|
USHORT s_UnicodeStringDataLength = DEFAULT_UNICODE_DATA_LENGTH;
|
|
WCHAR s_UnicodeStringData[DEFAULT_UNICODE_DATA_LENGTH];
|
|
WCHAR *s_pUnicodeStringData = s_UnicodeStringData;
|
|
|
|
#define DEFAULT_ANSI_DATA_LENGTH 4096
|
|
USHORT s_AnsiStringDataLength = DEFAULT_ANSI_DATA_LENGTH;
|
|
CHAR s_AnsiStringData[DEFAULT_ANSI_DATA_LENGTH];
|
|
CHAR *s_pAnsiStringData = s_AnsiStringData;
|
|
|
|
//
|
|
// No. of columns used to display struct fields;
|
|
//
|
|
|
|
ULONG s_MaxNoOfColumns = 3;
|
|
ULONG s_NoOfColumns = 1;
|
|
|
|
/*
|
|
* Fetches the data at the given address
|
|
*/
|
|
BOOLEAN
|
|
GetData(PVOID dwAddress, PVOID ptr, ULONG size)
|
|
{
|
|
BOOL b;
|
|
ULONG BytesRead;
|
|
|
|
b = (lpReadMemoryRoutine)(dwAddress, ptr, size, &BytesRead );
|
|
|
|
|
|
if (!b || BytesRead != size )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Fetch the null terminated ASCII string at dwAddress into buf
|
|
*/
|
|
BOOL
|
|
GetString(PUCHAR dwAddress, PSZ buf )
|
|
{
|
|
do
|
|
{
|
|
if (!GetData (dwAddress, buf, 1))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
dwAddress++;
|
|
buf++;
|
|
|
|
} while( *buf != '\0' );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Displays a byte in hexadecimal
|
|
*/
|
|
VOID
|
|
PrintHexChar( UCHAR c )
|
|
{
|
|
PRINTF( "%c%c", "0123456789abcdef"[ (c>>4)&7 ], "0123456789abcdef"[ c&7 ] );
|
|
}
|
|
|
|
/*
|
|
* Displays a buffer of data in hexadecimal
|
|
*/
|
|
VOID
|
|
PrintHexBuf( PUCHAR buf, ULONG cbuf )
|
|
{
|
|
while( cbuf-- ) {
|
|
PrintHexChar( *buf++ );
|
|
PRINTF( " " );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Displays a unicode string
|
|
*/
|
|
BOOL
|
|
PrintStringW(LPSTR msg, PUNICODE_STRING puStr, BOOL nl )
|
|
{
|
|
UNICODE_STRING UnicodeString;
|
|
ANSI_STRING AnsiString;
|
|
BOOL b;
|
|
|
|
if( msg )
|
|
PRINTF( msg );
|
|
|
|
if( puStr->Length == 0 ) {
|
|
if( nl )
|
|
PRINTF( "\n" );
|
|
return TRUE;
|
|
}
|
|
|
|
UnicodeString.Buffer = s_pUnicodeStringData;
|
|
UnicodeString.MaximumLength = s_UnicodeStringDataLength;
|
|
UnicodeString.Length = (puStr->Length > s_UnicodeStringDataLength)
|
|
? s_UnicodeStringDataLength
|
|
: puStr->Length;
|
|
|
|
b = (lpReadMemoryRoutine)(
|
|
(LPVOID) puStr->Buffer,
|
|
UnicodeString.Buffer,
|
|
UnicodeString.Length,
|
|
NULL);
|
|
|
|
if (b) {
|
|
RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE);
|
|
PRINTF("%s%s", AnsiString.Buffer, nl ? "\n" : "" );
|
|
RtlFreeAnsiString(&AnsiString);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
/*
|
|
* Displays a ANSI string
|
|
*/
|
|
BOOL
|
|
PrintStringA(LPSTR msg, PANSI_STRING pStr, BOOL nl )
|
|
{
|
|
ANSI_STRING AnsiString;
|
|
BOOL b;
|
|
|
|
if( msg )
|
|
PRINTF( msg );
|
|
|
|
if( pStr->Length == 0 ) {
|
|
if( nl )
|
|
PRINTF( "\n" );
|
|
return TRUE;
|
|
}
|
|
|
|
AnsiString.Buffer = s_pAnsiStringData;
|
|
AnsiString.MaximumLength = s_AnsiStringDataLength;
|
|
AnsiString.Length = (pStr->Length > (s_AnsiStringDataLength - 1))
|
|
? (s_AnsiStringDataLength - 1)
|
|
: pStr->Length;
|
|
|
|
b = (lpReadMemoryRoutine)(
|
|
(LPVOID) pStr->Buffer,
|
|
AnsiString.Buffer,
|
|
AnsiString.Length,
|
|
NULL);
|
|
|
|
if (b) {
|
|
AnsiString.Buffer[ AnsiString.Length ] = '\0';
|
|
PRINTF("%s%s", AnsiString.Buffer, nl ? "\n" : "" );
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the ULONG value referenced by the pointer given to us
|
|
*/
|
|
VOID
|
|
Next3(
|
|
PVOID Ptr,
|
|
PVOID *pFLink,
|
|
PVOID *pBLink,
|
|
PULONG_PTR pVerify
|
|
)
|
|
{
|
|
PVOID Buffer[4];
|
|
|
|
GetData(Ptr, (PVOID) Buffer, sizeof(PVOID)*3);
|
|
|
|
if (pFLink)
|
|
{
|
|
*pFLink = Buffer[0];
|
|
}
|
|
|
|
if (pBLink)
|
|
{
|
|
*pBLink = Buffer[1];
|
|
}
|
|
|
|
if (pVerify)
|
|
{
|
|
*pVerify = (ULONG_PTR) Buffer[2];
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Displays all the fields of a given struct. This is the driver routine that is called
|
|
* with the appropriate descriptor array to display all the fields in a given struct.
|
|
*/
|
|
|
|
char *NewLine = "\n";
|
|
char *FieldSeparator = " ";
|
|
char *DotSeparator = ".";
|
|
#define NewLineForFields(FieldNo) \
|
|
((((FieldNo) % s_NoOfColumns) == 0) ? NewLine : FieldSeparator)
|
|
#define FIELD_NAME_LENGTH 30
|
|
|
|
VOID
|
|
PrintStructFields(PVOID dwAddress, VOID *ptr, FIELD_DESCRIPTOR *pFieldDescriptors )
|
|
{
|
|
int i;
|
|
int j;
|
|
BYTE ch;
|
|
|
|
// Display the fields in the struct.
|
|
for( i=0; pFieldDescriptors->Name; i++, pFieldDescriptors++ ) {
|
|
|
|
// Indentation to begin the struct display.
|
|
PRINTF( " " );
|
|
|
|
if( strlen( pFieldDescriptors->Name ) > FIELD_NAME_LENGTH ) {
|
|
PRINTF( "%-17s...%s ", pFieldDescriptors->Name, pFieldDescriptors->Name+strlen(pFieldDescriptors->Name)-10 );
|
|
} else {
|
|
PRINTF( "%-30s ", pFieldDescriptors->Name );
|
|
}
|
|
|
|
PRINTF( "(0x%-2X) ", pFieldDescriptors->Offset );
|
|
|
|
switch( pFieldDescriptors->FieldType ) {
|
|
case FieldTypeByte:
|
|
case FieldTypeChar:
|
|
PRINTF( "%-16d%s",
|
|
*(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset ),
|
|
NewLineForFields(i) );
|
|
break;
|
|
|
|
case FieldTypeBoolean:
|
|
PRINTF( "%-16s%s",
|
|
*(BOOLEAN *)(((char *)ptr) + pFieldDescriptors->Offset ) ? "TRUE" : "FALSE",
|
|
NewLineForFields(i));
|
|
break;
|
|
|
|
case FieldTypeBool:
|
|
PRINTF( "%-16s%s",
|
|
*(BOOLEAN *)(((char *)ptr) + pFieldDescriptors->Offset ) ? "TRUE" : "FALSE",
|
|
NewLineForFields(i));
|
|
break;
|
|
|
|
case FieldTypePointer:
|
|
PRINTF( "%-16X%s",
|
|
*(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ),
|
|
NewLineForFields(i) );
|
|
break;
|
|
|
|
case FieldTypeULongULong:
|
|
PRINTF( "%d%s",
|
|
*(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset + sizeof(ULONG)),
|
|
FieldSeparator );
|
|
PRINTF( "%d%s",
|
|
*(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ),
|
|
NewLineForFields(i) );
|
|
break;
|
|
|
|
case FieldTypeListEntry:
|
|
|
|
if ( (PVOID)((PUCHAR)dwAddress + pFieldDescriptors->Offset) ==
|
|
*(PVOID *)(((PUCHAR)ptr) + pFieldDescriptors->Offset ))
|
|
{
|
|
PRINTF( "%s", "List Empty\n" );
|
|
}
|
|
else
|
|
{
|
|
PVOID Address, StartAddress;
|
|
ULONG Count = 0;
|
|
UCHAR Greater = ' ';
|
|
|
|
StartAddress = (PVOID) (((PUCHAR)dwAddress) + pFieldDescriptors->Offset);
|
|
Address = *(PVOID *) (((PUCHAR)ptr) + pFieldDescriptors->Offset);
|
|
|
|
while ((Address != StartAddress) &&
|
|
(++Count < MAX_LIST_ELEMENTS))
|
|
{
|
|
Next3 (Address, &Address, NULL, NULL);
|
|
}
|
|
|
|
if (Address != StartAddress)
|
|
{
|
|
Greater = '>';
|
|
}
|
|
|
|
PRINTF( "%-8X%s",
|
|
*(PVOID *)(((PUCHAR)ptr) + pFieldDescriptors->Offset ),
|
|
FieldSeparator );
|
|
PRINTF( "%-8X, (%c %d Elements)%s",
|
|
*(PVOID *)(((PUCHAR)ptr) + pFieldDescriptors->Offset + sizeof(PVOID)),
|
|
Greater, Count,
|
|
NewLineForFields(i) );
|
|
}
|
|
break;
|
|
|
|
// Ip address: 4 bytes long
|
|
case FieldTypeIpAddr:
|
|
PRINTF( "%X%s",
|
|
*(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ),
|
|
FieldSeparator );
|
|
PRINTF( "(%d%s",
|
|
*(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + 3),
|
|
DotSeparator );
|
|
PRINTF( "%d%s",
|
|
*(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + 2 ),
|
|
DotSeparator );
|
|
PRINTF( "%d%s",
|
|
*(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + 1 ),
|
|
DotSeparator );
|
|
PRINTF( "%d)%s",
|
|
*(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset ),
|
|
NewLineForFields(i) );
|
|
break;
|
|
|
|
// Mac address: 6 bytes long
|
|
case FieldTypeMacAddr:
|
|
for (j=0; j<5; j++)
|
|
{
|
|
PRINTF( "%X%s",
|
|
*(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + j),
|
|
FieldSeparator );
|
|
}
|
|
PRINTF( "%X%s",
|
|
*(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + 5),
|
|
NewLineForFields(i) );
|
|
break;
|
|
|
|
// Netbios name: 16 bytes long
|
|
case FieldTypeNBName:
|
|
//
|
|
// if first byte is printable, print the first 15 bytes as characters
|
|
// and 16th byte as a hex value. otherwise, print all the 16 bytes
|
|
// as hex values
|
|
//
|
|
ch = *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset);
|
|
if (ch >= 0x20 && ch <= 0x7e)
|
|
{
|
|
for (j=0; j<15; j++)
|
|
{
|
|
PRINTF( "%c", *(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + j));
|
|
}
|
|
PRINTF( "<%X>%s",
|
|
*(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + 15),
|
|
NewLineForFields(i) );
|
|
}
|
|
else
|
|
{
|
|
for (j=0; j<16; j++)
|
|
{
|
|
PRINTF( "%.2X",
|
|
*(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset + j));
|
|
}
|
|
PRINTF( "%s", NewLineForFields(i) );
|
|
}
|
|
break;
|
|
|
|
case FieldTypeULong:
|
|
case FieldTypeLong:
|
|
PRINTF( "%-16d%s",
|
|
*(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ),
|
|
NewLineForFields(i) );
|
|
break;
|
|
|
|
case FieldTypeShort:
|
|
PRINTF( "%-16X%s",
|
|
*(SHORT *)(((char *)ptr) + pFieldDescriptors->Offset ),
|
|
NewLineForFields(i) );
|
|
break;
|
|
|
|
case FieldTypeUShort:
|
|
PRINTF( "%-16X%s",
|
|
*(USHORT *)(((char *)ptr) + pFieldDescriptors->Offset ),
|
|
NewLineForFields(i) );
|
|
break;
|
|
|
|
case FieldTypeUnicodeString:
|
|
PrintStringW( NULL, (UNICODE_STRING *)(((char *)ptr) + pFieldDescriptors->Offset ), NONL );
|
|
PRINTF( NewLine );
|
|
break;
|
|
|
|
case FieldTypeAnsiString:
|
|
PrintStringA( NULL, (ANSI_STRING *)(((char *)ptr) + pFieldDescriptors->Offset ), NONL );
|
|
PRINTF( NewLine );
|
|
break;
|
|
|
|
case FieldTypeSymbol:
|
|
{
|
|
UCHAR SymbolName[ 200 ];
|
|
ULONG Displacement;
|
|
PVOID sym = (PVOID)(*(ULONG_PTR *)(((char *)ptr) + pFieldDescriptors->Offset ));
|
|
|
|
lpGetSymbolRoutine( sym, SymbolName, &Displacement );
|
|
PRINTF( "%-16s%s",
|
|
SymbolName,
|
|
NewLineForFields(i) );
|
|
}
|
|
break;
|
|
|
|
case FieldTypeEnum:
|
|
{
|
|
ULONG EnumValue;
|
|
ENUM_VALUE_DESCRIPTOR *pEnumValueDescr;
|
|
// Get the associated numericla value.
|
|
|
|
EnumValue = *((ULONG *)((BYTE *)ptr + pFieldDescriptors->Offset));
|
|
|
|
if ((pEnumValueDescr = pFieldDescriptors->AuxillaryInfo.pEnumValueDescriptor)
|
|
!= NULL) {
|
|
//
|
|
// An auxilary textual description of the value is
|
|
// available. Display it instead of the numerical value.
|
|
//
|
|
|
|
LPSTR pEnumName = NULL;
|
|
|
|
while (pEnumValueDescr->EnumName != NULL) {
|
|
if (EnumValue == pEnumValueDescr->EnumValue) {
|
|
pEnumName = pEnumValueDescr->EnumName;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pEnumName != NULL) {
|
|
PRINTF( "%-16s ", pEnumName );
|
|
} else {
|
|
PRINTF( "%-4d (%-10s) ", EnumValue,"@$#%^&*");
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// No auxilary information is associated with the ehumerated type
|
|
// print the numerical value.
|
|
//
|
|
PRINTF( "%-16d",EnumValue);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FieldTypeStruct:
|
|
PRINTF( "@%-15X%s",
|
|
((PUCHAR)dwAddress + pFieldDescriptors->Offset ),
|
|
NewLineForFields(i) );
|
|
break;
|
|
|
|
case FieldTypeLargeInteger:
|
|
case FieldTypeFileTime:
|
|
default:
|
|
ERROR( "Unrecognized field type %c for %s\n", pFieldDescriptors->FieldType, pFieldDescriptors->Name );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
LPSTR LibCommands[] = {
|
|
"columns <d> -- controls the number of columns in the display ",
|
|
"logdump <Log Address>\n",
|
|
"dump <Struct Type Name>@<address expr>, for eg: !netbtkd.dump tNBTCONFIG@xxxxxx ",
|
|
"devices <netbt!NbtConfig>",
|
|
"connections <netbt!NbtConfig>",
|
|
"verifyll <ListHead> [<Verify>]",
|
|
"cache [Local|Remote]",
|
|
0
|
|
};
|
|
|
|
BOOL
|
|
help(
|
|
DWORD dwCurrentPC,
|
|
PNTKD_EXTENSION_APIS lpExtensionApis,
|
|
LPSTR lpArgumentString
|
|
)
|
|
{
|
|
int i;
|
|
|
|
SETCALLBACKS();
|
|
|
|
for( i=0; Extensions[i]; i++ )
|
|
PRINTF( " %s\n", Extensions[i] );
|
|
|
|
for( i=0; LibCommands[i]; i++ )
|
|
PRINTF( " %s\n", LibCommands[i] );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
columns(
|
|
DWORD dwCurrentPC,
|
|
PNTKD_EXTENSION_APIS lpExtensionApis,
|
|
LPSTR lpArgumentString
|
|
)
|
|
{
|
|
ULONG NoOfColumns;
|
|
int i;
|
|
|
|
SETCALLBACKS();
|
|
|
|
sscanf(lpArgumentString,"%ld",&NoOfColumns);
|
|
|
|
if (NoOfColumns > s_MaxNoOfColumns) {
|
|
// PRINTF( "No. Of Columns exceeds maximum(%ld) -- directive Ignored\n", s_MaxNoOfColumns );
|
|
} else {
|
|
s_NoOfColumns = NoOfColumns;
|
|
}
|
|
|
|
PRINTF("Not Yet Implemented\n");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
globals(
|
|
DWORD dwCurrentPC,
|
|
PNTKD_EXTENSION_APIS lpExtensionApis,
|
|
LPSTR lpArgumentString
|
|
)
|
|
{
|
|
PVOID dwAddress;
|
|
CHAR buf[ 100 ];
|
|
int i;
|
|
int c=0;
|
|
|
|
SETCALLBACKS();
|
|
|
|
strcpy( buf, "srv!" );
|
|
|
|
for( i=0; GlobalBool[i]; i++, c++ ) {
|
|
BOOL b;
|
|
|
|
strcpy( &buf[4], GlobalBool[i] );
|
|
dwAddress = (PVOID) (lpGetExpressionRoutine) (buf);
|
|
if( dwAddress == 0 ) {
|
|
ERROR( "Unable to get address of %s\n", GlobalBool[i] );
|
|
continue;
|
|
}
|
|
if( !GetData( dwAddress,&b, sizeof(b)) )
|
|
return FALSE;
|
|
|
|
PRINTF( "%s%-30s %10s%s",
|
|
c&1 ? " " : "",
|
|
GlobalBool[i],
|
|
b ? " TRUE" : "FALSE",
|
|
c&1 ? "\n" : "" );
|
|
}
|
|
|
|
for( i=0; GlobalShort[i]; i++, c++ ) {
|
|
SHORT s;
|
|
|
|
strcpy( &buf[4], GlobalShort[i] );
|
|
dwAddress = (PVOID) (lpGetExpressionRoutine) ( buf );
|
|
if( dwAddress == 0 ) {
|
|
ERROR( "Unable to get address of %s\n", GlobalShort[i] );
|
|
continue;
|
|
}
|
|
if( !GetData( dwAddress,&s,sizeof(s)) )
|
|
return FALSE;
|
|
|
|
PRINTF( "%s%-30s %10d%s",
|
|
c&1 ? " " : "",
|
|
GlobalShort[i],
|
|
s,
|
|
c&1 ? "\n" : "" );
|
|
}
|
|
|
|
for( i=0; GlobalLong[i]; i++, c++ ) {
|
|
LONG l;
|
|
|
|
strcpy( &buf[4], GlobalLong[i] );
|
|
dwAddress = (PVOID) (lpGetExpressionRoutine) ( buf );
|
|
if( dwAddress == 0 ) {
|
|
ERROR( "Unable to get address of %s\n", GlobalLong[i] );
|
|
continue;
|
|
}
|
|
if( !GetData(dwAddress,&l, sizeof(l)) )
|
|
return FALSE;
|
|
|
|
PRINTF( "%s%-30s %10d%s",
|
|
c&1 ? " " : "",
|
|
GlobalLong[i],
|
|
l,
|
|
c&1 ? "\n" : "" );
|
|
}
|
|
|
|
PRINTF( "\n" );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
version
|
|
(
|
|
DWORD dwCurrentPC,
|
|
PNTKD_EXTENSION_APIS lpExtensionApis,
|
|
LPSTR lpArgumentString
|
|
)
|
|
{
|
|
#if VER_DEBUG
|
|
char *kind = "checked";
|
|
#else
|
|
char *kind = "free";
|
|
#endif
|
|
|
|
SETCALLBACKS();
|
|
|
|
PRINTF( "Redirector debugger Extension dll for %s build %u\n", kind, VER_PRODUCTBUILD );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#define NAME_DELIMITER '@'
|
|
#define NAME_DELIMITERS "@ "
|
|
#define INVALID_INDEX 0xffffffff
|
|
#define MIN(x,y) ((x) < (y) ? (x) : (y))
|
|
|
|
ULONG SearchStructs(LPSTR lpArgument)
|
|
{
|
|
ULONG i = 0;
|
|
STRUCT_DESCRIPTOR *pStructs = Structs;
|
|
ULONG NameIndex = INVALID_INDEX;
|
|
ULONG ArgumentLength = strlen(lpArgument);
|
|
BOOLEAN fAmbigous = FALSE;
|
|
|
|
|
|
while ((pStructs->StructName != 0)) {
|
|
int Result = _strnicmp(lpArgument,
|
|
pStructs->StructName,
|
|
MIN(strlen(pStructs->StructName),ArgumentLength));
|
|
|
|
if (Result == 0) {
|
|
if (NameIndex != INVALID_INDEX) {
|
|
// We have encountered duplicate matches. Print out the
|
|
// matching strings and let the user disambiguate.
|
|
fAmbigous = TRUE;
|
|
break;
|
|
} else {
|
|
NameIndex = i;
|
|
}
|
|
|
|
}
|
|
pStructs++;i++;
|
|
}
|
|
|
|
if (fAmbigous) {
|
|
PRINTF("Ambigous Name Specification -- The following structs match\n");
|
|
PRINTF("%s\n",Structs[NameIndex].StructName);
|
|
PRINTF("%s\n",Structs[i].StructName);
|
|
while (pStructs->StructName != 0) {
|
|
if (_strnicmp(lpArgument,
|
|
pStructs->StructName,
|
|
MIN(strlen(pStructs->StructName),ArgumentLength)) == 0) {
|
|
PRINTF("%s\n",pStructs->StructName);
|
|
}
|
|
pStructs++;
|
|
}
|
|
PRINTF("Dumping Information for %s\n",Structs[NameIndex].StructName);
|
|
}
|
|
|
|
return(NameIndex);
|
|
}
|
|
|
|
VOID DisplayStructs()
|
|
{
|
|
STRUCT_DESCRIPTOR *pStructs = Structs;
|
|
|
|
PRINTF("The following structs are handled .... \n");
|
|
while (pStructs->StructName != 0) {
|
|
PRINTF("\t%s\n",pStructs->StructName);
|
|
pStructs++;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
dump(
|
|
DWORD dwCurrentPC,
|
|
PNTKD_EXTENSION_APIS lpExtensionApis,
|
|
LPSTR lpArgumentString
|
|
)
|
|
{
|
|
PVOID dwAddress;
|
|
|
|
SETCALLBACKS();
|
|
|
|
if( lpArgumentString && *lpArgumentString ) {
|
|
// Parse the argument string to determine the structure to be displayed.
|
|
// Scan for the NAME_DELIMITER ( '@' ).
|
|
|
|
LPSTR lpName = lpArgumentString;
|
|
LPSTR lpArgs = strpbrk(lpArgumentString, NAME_DELIMITERS);
|
|
ULONG Index;
|
|
|
|
if (lpArgs) {
|
|
//
|
|
// The specified command is of the form
|
|
// dump <name>@<address expr.>
|
|
//
|
|
// Locate the matching struct for the given name. In the case
|
|
// of ambiguity we seek user intervention for disambiguation.
|
|
//
|
|
// We do an inplace modification of the argument string to
|
|
// facilitate matching.
|
|
//
|
|
*lpArgs = '\0';
|
|
|
|
Index = SearchStructs(lpName);
|
|
|
|
//
|
|
// Let us restore the original value back.
|
|
//
|
|
|
|
*lpArgs = NAME_DELIMITER;
|
|
|
|
if (INVALID_INDEX != Index) {
|
|
|
|
dwAddress = (PVOID) (lpGetExpressionRoutine)( ++lpArgs );
|
|
if (GetData(dwAddress,DataBuffer,Structs[Index].StructSize)) {
|
|
|
|
PRINTF(
|
|
"++++++++++++++++ %s@%lx ++++++++++++++++\n",
|
|
Structs[Index].StructName,
|
|
dwAddress);
|
|
PrintStructFields(
|
|
dwAddress,
|
|
&DataBuffer,
|
|
Structs[Index].FieldDescriptors);
|
|
PRINTF(
|
|
"---------------- %s@%lx ----------------\n",
|
|
Structs[Index].StructName,
|
|
dwAddress);
|
|
} else {
|
|
PRINTF("Error reading Memory @ %lx\n",dwAddress);
|
|
}
|
|
} else {
|
|
// No matching struct was found. Display the list of
|
|
// structs currently handled.
|
|
|
|
DisplayStructs();
|
|
}
|
|
} else {
|
|
//
|
|
// The command is of the form
|
|
// dump <name>
|
|
//
|
|
// Currently we do not handle this. In future we will map it to
|
|
// the name of a global variable and display it if required.
|
|
//
|
|
|
|
DisplayStructs();
|
|
}
|
|
} else {
|
|
//
|
|
// display the list of structs currently handled.
|
|
//
|
|
|
|
DisplayStructs();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
devices(
|
|
DWORD dwCurrentPC,
|
|
PNTKD_EXTENSION_APIS lpExtensionApis,
|
|
LPSTR lpArgumentString
|
|
)
|
|
{
|
|
PLIST_ENTRY pEntry;
|
|
PLIST_ENTRY pHead;
|
|
tDEVICECONTEXT *pDeviceContext;
|
|
STRUCT_DESCRIPTOR *pStructs = Structs;
|
|
ULONG Index = 0;
|
|
tNBTCONFIG *ConfigPtr = (tNBTCONFIG *) lpArgumentString;
|
|
tDEVICECONTEXT **ppNbtSmbDevice;
|
|
|
|
PVOID dwAddress;
|
|
|
|
SETCALLBACKS();
|
|
|
|
if (!lpArgumentString || !(*lpArgumentString ))
|
|
{
|
|
ConfigPtr = (tNBTCONFIG *) lpGetExpressionRoutine ("netbt!NbtConfig");
|
|
}
|
|
else
|
|
{
|
|
ConfigPtr = (tNBTCONFIG *) lpGetExpressionRoutine (lpArgumentString);
|
|
}
|
|
ppNbtSmbDevice = (tDEVICECONTEXT **) lpGetExpressionRoutine ("netbt!pNbtSmbDevice");
|
|
|
|
while (pStructs->StructName != 0)
|
|
{
|
|
if (!(_strnicmp("tDEVICECONTEXT", pStructs->StructName, 10)))
|
|
{
|
|
break;
|
|
}
|
|
Index++;
|
|
pStructs++;
|
|
}
|
|
|
|
if (pStructs->StructName == 0)
|
|
{
|
|
PRINTF ("ERROR: Could not find structure definition for <tDEVICECONTEXT>\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!GetData(ppNbtSmbDevice, DataBuffer, sizeof (tDEVICECONTEXT *)))
|
|
{
|
|
PRINTF ("ERROR: Could not read pNbtSmbDevice ptr\n");
|
|
}
|
|
else if (!(pDeviceContext = *((tDEVICECONTEXT **) DataBuffer)))
|
|
{
|
|
PRINTF ("pNbtSmbDevice is NULL\n");
|
|
}
|
|
else if (!GetData(pDeviceContext, DataBuffer, Structs[Index].StructSize))
|
|
{
|
|
PRINTF ("ERROR: Could not read pNbtSmbDevice data@ <%p>\n", pDeviceContext);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Dump this Device's Info
|
|
//
|
|
PRINTF("pNbtSmbDevice @ <%p>\n", pDeviceContext);
|
|
PRINTF( "++++++++++++++++ %s @%lx ++++++++++++++++\n", Structs[Index].StructName, pDeviceContext);
|
|
PrintStructFields( pDeviceContext, &DataBuffer, Structs[Index].FieldDescriptors);
|
|
PRINTF("\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
|
|
}
|
|
|
|
pHead = &ConfigPtr->DeviceContexts;
|
|
if (!GetData(ConfigPtr, DataBuffer, sizeof(tNBTCONFIG)))
|
|
{
|
|
PRINTF ("ERROR: Could not read NbtConfig data @<%x>\n", ConfigPtr);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get the number of Devices attached
|
|
//
|
|
{
|
|
PVOID StartAddress;
|
|
PVOID Address;
|
|
ULONG Count = 0;
|
|
PVOID Buffer[4];
|
|
UCHAR Greater = ' ';
|
|
|
|
StartAddress = pHead;
|
|
GetData( StartAddress, Buffer, sizeof(ULONG)*4 );
|
|
Address = Buffer[0];
|
|
|
|
while ((Address != StartAddress) &&
|
|
(++Count < MAX_LIST_ELEMENTS))
|
|
{
|
|
GetData( Address, Buffer, sizeof(ULONG)*4 );
|
|
Address = Buffer[0];
|
|
}
|
|
|
|
PRINTF( "Dumping <%d> Devices attached to NbtConfig@<%x>\n", Count, ConfigPtr);
|
|
}
|
|
|
|
ConfigPtr = (tNBTCONFIG *) DataBuffer;
|
|
pEntry = ConfigPtr->DeviceContexts.Flink;
|
|
|
|
while (pEntry != pHead)
|
|
{
|
|
pDeviceContext = (tDEVICECONTEXT *) CONTAINING_RECORD(pEntry,tDEVICECONTEXT,Linkage);
|
|
if (!GetData(pDeviceContext, DataBuffer, Structs[Index].StructSize))
|
|
{
|
|
PRINTF ("ERROR: Could not read DeviceContext data @<%x>\n", pDeviceContext);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Dump this Device's Info
|
|
//
|
|
PRINTF( "++++++++++++++++ %s @%lx ++++++++++++++++\n", Structs[Index].StructName, pDeviceContext);
|
|
PrintStructFields( pDeviceContext, &DataBuffer, Structs[Index].FieldDescriptors);
|
|
|
|
//
|
|
// Go to next device
|
|
//
|
|
pDeviceContext = (tDEVICECONTEXT *) DataBuffer;
|
|
pEntry = pDeviceContext->Linkage.Flink;
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
BOOL
|
|
connections(
|
|
DWORD dwCurrentPC,
|
|
PNTKD_EXTENSION_APIS lpExtensionApis,
|
|
LPSTR lpArgumentString
|
|
)
|
|
{
|
|
PLIST_ENTRY pEntry, pHead, pClientHead, pClientEntry, pConnHead, pConnEntry;
|
|
tNBTCONFIG *ConfigPtr;
|
|
tADDRESSELE *pAddressEle;
|
|
tCLIENTELE *pClient;
|
|
tCONNECTELE *pConnEle, *pSavConnEle;
|
|
tNAMEADDR *pNameAddr;
|
|
tLISTENREQUESTS *pListen;
|
|
|
|
SETCALLBACKS();
|
|
|
|
PRINTF ("Dumping information on all NetBT conections ...\n");
|
|
|
|
if (!lpArgumentString || !(*lpArgumentString ))
|
|
{
|
|
ConfigPtr = (tNBTCONFIG *) lpGetExpressionRoutine ("netbt!NbtConfig");
|
|
}
|
|
else
|
|
{
|
|
ConfigPtr = (tNBTCONFIG *) (lpGetExpressionRoutine) (lpArgumentString);
|
|
}
|
|
|
|
pHead = &ConfigPtr->AddressHead;
|
|
if (!GetData(ConfigPtr, DataBuffer, sizeof(tNBTCONFIG)))
|
|
{
|
|
PRINTF ("ERROR: Could not read NbtConfig data @<%x>\n", ConfigPtr);
|
|
return FALSE;
|
|
}
|
|
ConfigPtr = (tNBTCONFIG *) DataBuffer;
|
|
Next3 (pHead, &pEntry, NULL, NULL);
|
|
|
|
while (pEntry != pHead)
|
|
{
|
|
pAddressEle = CONTAINING_RECORD(pEntry,tADDRESSELE,Linkage);
|
|
|
|
Next3 (&pAddressEle->pNameAddr, &pNameAddr, NULL, NULL);
|
|
if (!GetData(pNameAddr, DataBuffer, sizeof(tNAMEADDR)))
|
|
{
|
|
PRINTF ("[1] Error reading pNameAddr data @<%x>", pNameAddr);
|
|
return FALSE;
|
|
}
|
|
pNameAddr = (tNAMEADDR *) DataBuffer;
|
|
PRINTF ("Address@<%x> ==> <%-16.16s:%x>\n", pAddressEle, pNameAddr->Name, pNameAddr->Name[15]);
|
|
|
|
pClientHead = &pAddressEle->ClientHead;
|
|
Next3 (pClientHead, &pClientEntry, NULL, NULL);
|
|
while (pClientEntry != pClientHead)
|
|
{
|
|
pClient = CONTAINING_RECORD(pClientEntry,tCLIENTELE,Linkage);
|
|
if (!GetData(pClient, DataBuffer, sizeof(tCLIENTELE)))
|
|
{
|
|
PRINTF ("Error reading pClientEle data @<%p>", pClient);
|
|
continue;
|
|
}
|
|
|
|
PRINTF ("\tClient@<%p> ==> pDevice=<%p>\n", pClient, ((tCLIENTELE *)DataBuffer)->pDeviceContext);
|
|
|
|
PRINTF ("\t\t(ConnectHead):\n");
|
|
pConnHead = &pClient->ConnectHead;
|
|
Next3 (pConnHead, &pConnEntry, NULL, NULL);
|
|
while (pConnEntry != pConnHead)
|
|
{
|
|
pSavConnEle = pConnEle = CONTAINING_RECORD(pConnEntry,tCONNECTELE,Linkage);
|
|
if (!GetData(pConnEle, DataBuffer, sizeof(tCONNECTELE)))
|
|
{
|
|
PRINTF ("[2] Error reading pConnEle data @<%x>", pConnEle);
|
|
return FALSE;
|
|
}
|
|
pConnEle = (tCONNECTELE *) DataBuffer;
|
|
PRINTF ("\t\t ** Connection@<%x> ==> <%-16.16s:%x>:\n",
|
|
pSavConnEle, pConnEle->RemoteName, pConnEle->RemoteName[15]);
|
|
|
|
Next3 (pConnEntry, &pConnEntry, NULL, NULL);
|
|
}
|
|
|
|
PRINTF ("\t\t(ConnectActive):\n");
|
|
pConnHead = &pClient->ConnectActive;
|
|
Next3 (pConnHead, &pConnEntry, NULL, NULL);
|
|
while (pConnEntry != pConnHead)
|
|
{
|
|
pSavConnEle = pConnEle = CONTAINING_RECORD(pConnEntry,tCONNECTELE,Linkage);
|
|
if (!GetData(pConnEle, DataBuffer, sizeof(tCONNECTELE)))
|
|
{
|
|
PRINTF ("[3] Error reading pConnEle data @<%x>", pConnEle);
|
|
return FALSE;
|
|
}
|
|
pConnEle = (tCONNECTELE *) DataBuffer;
|
|
PRINTF ("\t\t ** Connection@<%x> ==> <%-16.16s:%x>:\n",
|
|
pSavConnEle, pConnEle->RemoteName, pConnEle->RemoteName[15]);
|
|
|
|
Next3 (pConnEntry, &pConnEntry, NULL, NULL);
|
|
}
|
|
|
|
PRINTF ("\t\t(ListenHead):\n");
|
|
pConnHead = &pClient->ListenHead;
|
|
Next3 (pConnHead, &pConnEntry, NULL, NULL);
|
|
while (pConnEntry != pConnHead)
|
|
{
|
|
pSavConnEle = pConnEle = CONTAINING_RECORD(pConnEntry,tCONNECTELE,Linkage);
|
|
if (!GetData(pConnEle, DataBuffer, sizeof(tLISTENREQUESTS)))
|
|
{
|
|
PRINTF ("[4] Error reading pListen data @<%x>", pSavConnEle);
|
|
return FALSE;
|
|
}
|
|
pListen = (tLISTENREQUESTS *) DataBuffer;
|
|
PRINTF ("\t\t ** pListen@<%p> ==> pIrp=<%p>\n", pSavConnEle, pListen->pIrp);
|
|
|
|
Next3 (pConnEntry, &pConnEntry, NULL, NULL);
|
|
}
|
|
|
|
Next3 (pClientEntry, &pClientEntry, NULL, NULL);
|
|
}
|
|
Next3 (pEntry, &pEntry, NULL, NULL);
|
|
PRINTF ("\n");
|
|
}
|
|
|
|
PRINTF( "---------------- Connections ----------------\n");
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
verifyll(
|
|
DWORD dwCurrentPC,
|
|
PNTKD_EXTENSION_APIS lpExtensionApis,
|
|
LPSTR lpArgumentString
|
|
)
|
|
{
|
|
PLIST_ENTRY pHead, pCurrentEntry, pNextEntry, pPreviousEntry;
|
|
ULONG_PTR VerifyRead, VerifyIn = 0;
|
|
ULONG Count = 0;
|
|
BOOL fVerifyIn = FALSE;
|
|
BOOL fListCorrupt = FALSE;
|
|
|
|
SETCALLBACKS();
|
|
|
|
PRINTF ("Verifying Linked list ...\n");
|
|
|
|
if (!lpArgumentString || !(*lpArgumentString ))
|
|
{
|
|
PRINTF ("Usage: !NetbtKd.VerifyLL <ListHead> [<Verify]>\n");
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// lpArgumentString = "<pHead> [<Verify>]"
|
|
//
|
|
LPSTR lpVerify;
|
|
|
|
while (*lpArgumentString == ' ')
|
|
{
|
|
lpArgumentString++;
|
|
}
|
|
lpVerify = strpbrk(lpArgumentString, NAME_DELIMITERS);
|
|
|
|
pHead = (PVOID) (lpGetExpressionRoutine) (lpArgumentString);
|
|
if (lpVerify)
|
|
{
|
|
VerifyIn = (lpGetExpressionRoutine) (lpVerify);
|
|
fVerifyIn = TRUE;
|
|
}
|
|
}
|
|
|
|
PRINTF ("** ListHead@<%x>, fVerifyIn=<%x>, VerifyIn=<%x>:\n\n", pHead, fVerifyIn, VerifyIn);
|
|
PRINTF ("Verifying Flinks ...");
|
|
|
|
// Read in the data for the first FLink in the list!
|
|
pPreviousEntry = pHead;
|
|
Next3 (pHead, &pCurrentEntry, NULL, NULL);
|
|
Next3 (pCurrentEntry, &pNextEntry, NULL, &VerifyRead);
|
|
|
|
while ((pCurrentEntry != pHead) &&
|
|
(++Count < MAX_LIST_ELEMENTS))
|
|
{
|
|
if ((fVerifyIn) &&
|
|
(VerifyRead != VerifyIn))
|
|
{
|
|
PRINTF ("Verify FAILURE:\n\t<%d> Elements Read so far, Previous=<%x>, Current=<%x>, Next=<%x>\n",
|
|
Count, pPreviousEntry, pCurrentEntry, pNextEntry);
|
|
fListCorrupt = TRUE;
|
|
break;
|
|
}
|
|
pPreviousEntry = pCurrentEntry;
|
|
pCurrentEntry = pNextEntry;
|
|
Next3 (pCurrentEntry, &pNextEntry, NULL, &VerifyRead);
|
|
}
|
|
|
|
if (!fListCorrupt)
|
|
{
|
|
PRINTF ("SUCCESS: %s<%d> Elements!\n", (pCurrentEntry==pHead? "":"> "), Count);
|
|
}
|
|
|
|
PRINTF ("Verifying Blinks ...");
|
|
|
|
Count = 0;
|
|
fListCorrupt = FALSE;
|
|
// Read in the data for the first BLink in the list!
|
|
pPreviousEntry = pHead;
|
|
Next3 (pHead, NULL, &pCurrentEntry, NULL);
|
|
Next3 (pCurrentEntry, NULL, &pNextEntry, &VerifyRead);
|
|
|
|
while ((pCurrentEntry != pHead) &&
|
|
(++Count < MAX_LIST_ELEMENTS))
|
|
{
|
|
if ((fVerifyIn) &&
|
|
(VerifyRead != VerifyIn))
|
|
{
|
|
PRINTF ("Verify FAILURE:\n\t<%d> Elements Read so far, Previous=<%x>, Current=<%x>, Next=<%x>\n",
|
|
Count, pPreviousEntry, pCurrentEntry, pNextEntry);
|
|
fListCorrupt = TRUE;
|
|
break;
|
|
}
|
|
pPreviousEntry = pCurrentEntry;
|
|
pCurrentEntry = pNextEntry;
|
|
Next3 (pCurrentEntry, NULL, &pNextEntry, &VerifyRead);
|
|
}
|
|
|
|
if (!fListCorrupt)
|
|
{
|
|
PRINTF ("SUCCESS: %s<%d> Elements!\n", (pCurrentEntry==pHead? "":"> "), Count);
|
|
}
|
|
|
|
PRINTF( "---------------- Verify LinkedList ----------------\n");
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
DumpCache(
|
|
tHASHTABLE *pHashTable,
|
|
enum eNbtLocation CacheType
|
|
)
|
|
{
|
|
LONG i, NumBuckets;
|
|
PLIST_ENTRY pHead;
|
|
PLIST_ENTRY pEntry;
|
|
tHASHTABLE HashTbl;
|
|
tNAMEADDR NameAddr, *pNameAddr;
|
|
|
|
if (!GetData(pHashTable, &HashTbl, sizeof(tHASHTABLE)))
|
|
{
|
|
PRINTF ("ERROR: Could not read %s HashTable data @<%x>\n",
|
|
(CacheType == NBT_LOCAL ? "Local":"Remote"), pHashTable);
|
|
return FALSE;
|
|
}
|
|
|
|
NumBuckets = HashTbl.lNumBuckets;
|
|
PRINTF ("\nDumping %s Cache = <%d> buckets:\n",
|
|
(CacheType == NBT_LOCAL ? "Local":"Remote"), NumBuckets);
|
|
PRINTF ("[Bkt#]\t<Address> => <Name > | IpAddr | RefC | State | Ttl\n");
|
|
PRINTF ("-----------------------------------------------------------------------------------\n");
|
|
|
|
for (i=0; i < NumBuckets; i++)
|
|
{
|
|
pHead = &pHashTable->Bucket[i];
|
|
Next3 (pHead, &pEntry, NULL, NULL);
|
|
|
|
//
|
|
// Go through each name in each bucket of the hashtable
|
|
//
|
|
while (pEntry != pHead)
|
|
{
|
|
pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage);
|
|
if (!GetData(pNameAddr, &NameAddr, sizeof(tNAMEADDR)))
|
|
{
|
|
PRINTF ("ERROR: Could not read NameAddr data @<%x>\n", pNameAddr);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((NameAddr.Verify == LOCAL_NAME) || (NameAddr.Verify == REMOTE_NAME))
|
|
{
|
|
PRINTF ("[%d]\t<%x> => <%-15.15s:%2x> | %8x | %d | %8x | %9d\n",
|
|
i, pNameAddr, NameAddr.Name, (NameAddr.Name[15]&0x000000ff), NameAddr.IpAddress, NameAddr.RefCount, NameAddr.NameTypeState, NameAddr.Ttl);
|
|
}
|
|
else
|
|
{
|
|
PRINTF ("ERROR: Bad Name cache entry @ <%x>!\n", pNameAddr);
|
|
return FALSE;
|
|
}
|
|
|
|
Next3 (pEntry, &pEntry, NULL, NULL); // next hash table entry
|
|
}
|
|
} // for ( .. pHashTable .. )
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
cache(
|
|
DWORD dwCurrentPC,
|
|
PNTKD_EXTENSION_APIS lpExtensionApis,
|
|
LPSTR lpArgumentString
|
|
)
|
|
{
|
|
tNBTCONFIG NbtConfig, *pConfig;
|
|
BOOL fDumpLocal = TRUE; // Dump both local and remote cache by default
|
|
BOOL fDumpRemote = TRUE;
|
|
|
|
SETCALLBACKS();
|
|
|
|
if (lpArgumentString && (*lpArgumentString ))
|
|
{
|
|
//
|
|
// lpArgumentString = "[Local|Remote]"
|
|
//
|
|
while (*lpArgumentString == ' ')
|
|
{
|
|
lpArgumentString++;
|
|
}
|
|
|
|
if ((*lpArgumentString == 'l') || (*lpArgumentString == 'L'))
|
|
{
|
|
fDumpRemote = FALSE;
|
|
}
|
|
else if ((*lpArgumentString == 'r') || (*lpArgumentString == 'R'))
|
|
{
|
|
fDumpLocal = FALSE;
|
|
}
|
|
}
|
|
|
|
pConfig = (tNBTCONFIG *) lpGetExpressionRoutine ("netbt!NbtConfig");
|
|
if (!GetData(pConfig, &NbtConfig, sizeof(tNBTCONFIG)))
|
|
{
|
|
PRINTF ("ERROR: Could not read NbtConfig data @<%x>\n", pConfig);
|
|
return FALSE;
|
|
}
|
|
|
|
if (fDumpLocal)
|
|
{
|
|
DumpCache (NbtConfig.pLocalHashTbl, NBT_LOCAL);
|
|
}
|
|
if (fDumpRemote)
|
|
{
|
|
DumpCache (NbtConfig.pRemoteHashTbl, NBT_REMOTE);
|
|
}
|
|
|
|
PRINTF( "---------------- Cache ----------------\n");
|
|
|
|
return (TRUE);
|
|
}
|