616 lines
14 KiB
C
616 lines
14 KiB
C
|
#include <nt.h>
|
||
|
#include <ntrtl.h>
|
||
|
#include <nturtl.h>
|
||
|
#include <ntverp.h>
|
||
|
#include <windef.h>
|
||
|
#include <winbase.h>
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <wdbgexts.h>
|
||
|
|
||
|
//
|
||
|
// globals
|
||
|
//
|
||
|
EXT_API_VERSION ApiVersion = {
|
||
|
(VER_PRODUCTVERSION_W >> 8),
|
||
|
(VER_PRODUCTVERSION_W & 0xff),
|
||
|
EXT_API_VERSION_NUMBER64,
|
||
|
0
|
||
|
};
|
||
|
WINDBG_EXTENSION_APIS ExtensionApis;
|
||
|
USHORT SavedMajorVersion;
|
||
|
USHORT SavedMinorVersion;
|
||
|
|
||
|
//
|
||
|
// Names of interesting structures
|
||
|
//
|
||
|
CHAR * NDIS_PROTOCOL_BLOCK_NAME = "_NDIS_PROTOCOL_BLOCK";
|
||
|
CHAR * NDIS_STRING_NAME = "_UNICODE_STRING";
|
||
|
CHAR * NDIS_OPEN_BLOCK_NAME = "_NDIS_OPEN_BLOCK";
|
||
|
CHAR * NDIS_MINIPORT_BLOCK_NAME = "_NDIS_MINIPORT_BLOCK";
|
||
|
CHAR * NDIS_COMMON_OPEN_BLOCK_NAME = "_NDIS_COMMON_OPEN_BLOCK";
|
||
|
|
||
|
DllInit(
|
||
|
HANDLE hModule,
|
||
|
DWORD dwReason,
|
||
|
DWORD dwReserved
|
||
|
)
|
||
|
{
|
||
|
if (dwReason == DLL_PROCESS_ATTACH) {
|
||
|
DisableThreadLibraryCalls(hModule);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
WinDbgExtensionDllInit(
|
||
|
PWINDBG_EXTENSION_APIS64 lpExtensionApis,
|
||
|
USHORT MajorVersion,
|
||
|
USHORT MinorVersion
|
||
|
)
|
||
|
{
|
||
|
ExtensionApis = *lpExtensionApis;
|
||
|
|
||
|
SavedMajorVersion = MajorVersion;
|
||
|
SavedMinorVersion = MinorVersion;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
CheckVersion(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
LPEXT_API_VERSION
|
||
|
ExtensionApiVersion(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
return &ApiVersion;
|
||
|
}
|
||
|
|
||
|
const char *retstr[] = {
|
||
|
"?",
|
||
|
"memory read error",
|
||
|
"symbol type index not found",
|
||
|
"symbol type info not found",
|
||
|
"fields did not match",
|
||
|
"null sym dump param",
|
||
|
"null field name",
|
||
|
"incorrect version info",
|
||
|
"exit on controlc",
|
||
|
"cannot allocate memory",
|
||
|
"insufficient space to copy",
|
||
|
};
|
||
|
|
||
|
DECLARE_API( help )
|
||
|
{
|
||
|
dprintf("dtext debugger extension commands:\n\n");
|
||
|
dprintf(" find <type> - Enumerate all objects of a given type\n");
|
||
|
dprintf(" find <type> <field> <op> <value> - Search for object matching condition\n");
|
||
|
dprintf(" refcounts - Dump refcounts for NDIS (TCPIP-only) and WANARP\n");
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// List of operators recognized by !find command
|
||
|
//
|
||
|
char *validops[] = {
|
||
|
"==",
|
||
|
"!=",
|
||
|
"T",
|
||
|
NULL
|
||
|
};
|
||
|
#define OP_EQUAL 0
|
||
|
#define OP_NOT_EQUAL 1
|
||
|
#define OP_TRUE 2
|
||
|
|
||
|
//
|
||
|
// List of types recognized by !find command
|
||
|
//
|
||
|
char *validtypes[] = {
|
||
|
"ao",
|
||
|
"IGMPAddr",
|
||
|
"nte",
|
||
|
NULL
|
||
|
};
|
||
|
#define TYP_AO 0
|
||
|
#define TYP_IGMP_ADDR 1
|
||
|
#define TYP_NTE 2
|
||
|
|
||
|
void
|
||
|
ProcessRecord(
|
||
|
ULONG64 Addr,
|
||
|
char *structstr,
|
||
|
char *fieldstr,
|
||
|
int op,
|
||
|
ULONG64 value
|
||
|
)
|
||
|
{
|
||
|
ULONG64 actual;
|
||
|
int ret;
|
||
|
char buff[80];
|
||
|
SYM_DUMP_PARAM Sym = {
|
||
|
sizeof(SYM_DUMP_PARAM),
|
||
|
structstr,
|
||
|
0,
|
||
|
Addr,
|
||
|
NULL, NULL, NULL, 0, NULL
|
||
|
};
|
||
|
|
||
|
if (op != OP_TRUE) {
|
||
|
GetFieldValue(Addr, structstr, fieldstr, actual);
|
||
|
}
|
||
|
|
||
|
switch (op) {
|
||
|
case OP_EQUAL:
|
||
|
if (actual != value)
|
||
|
return;
|
||
|
break;
|
||
|
case OP_NOT_EQUAL:
|
||
|
if (actual == value)
|
||
|
return;
|
||
|
break;
|
||
|
case OP_TRUE:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Dump the type
|
||
|
//
|
||
|
dprintf("Found %p:\n", Addr);
|
||
|
Ioctl(IG_DUMP_SYMBOL_INFO, &Sym, Sym.size);
|
||
|
dprintf("\n");
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Unfortunately, there's no way (that I know of) to tell
|
||
|
// when this changes in tcpip.sys, so for now we hard code this.
|
||
|
//
|
||
|
#define IGMP_TABLE_SIZE 32
|
||
|
|
||
|
void
|
||
|
ForEachIGMPAddr(
|
||
|
char *fieldstr,
|
||
|
int op,
|
||
|
ULONG64 value)
|
||
|
{
|
||
|
ULONG64 Table, Addr, NTE, NetTable;
|
||
|
ULONG Offset;
|
||
|
ULONG Size, i, j;
|
||
|
ULONG Stride;
|
||
|
char buff[80];
|
||
|
ULONG NetTableSize;
|
||
|
|
||
|
Size = IGMP_TABLE_SIZE;
|
||
|
|
||
|
if ((op!=OP_TRUE) && GetFieldOffset("tcpip!IGMPAddr", fieldstr, &Offset) != 0) {
|
||
|
dprintf("Can't get offset of %s in IGMPAddr block!\n", fieldstr);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Stride = GetTypeSize("PVOID");
|
||
|
|
||
|
NetTableSize = (ULONG)GetExpression("poi(tcpip!NET_TABLE_SIZE)");
|
||
|
|
||
|
//
|
||
|
// Walk NTE list
|
||
|
//
|
||
|
NetTable = GetExpression("poi(tcpip!NewNetTableList)");
|
||
|
for (j=0; j<NetTableSize; j++) {
|
||
|
if (CheckControlC())
|
||
|
break;
|
||
|
|
||
|
sprintf(buff, "poi(%p)", NetTable + j * Stride);
|
||
|
NTE = GetExpression(buff);
|
||
|
while (NTE) {
|
||
|
if (CheckControlC())
|
||
|
break;
|
||
|
|
||
|
GetFieldValue(NTE, "tcpip!NetTableEntry", "nte_igmplist", Table);
|
||
|
for (i=0; i<Size; i++) {
|
||
|
if (CheckControlC())
|
||
|
break;
|
||
|
|
||
|
//
|
||
|
// Walk IGMPAddr list
|
||
|
//
|
||
|
sprintf(buff, "poi(%p)", Table + i * Stride);
|
||
|
Addr = GetExpression(buff);
|
||
|
while (Addr) {
|
||
|
if (CheckControlC())
|
||
|
break;
|
||
|
|
||
|
ProcessRecord(Addr, "tcpip!IGMPAddr", fieldstr, op, value);
|
||
|
|
||
|
GetFieldValue(Addr, "tcpip!IGMPAddr", "iga_next", Addr);
|
||
|
}
|
||
|
}
|
||
|
GetFieldValue(NTE, "tcpip!NetTableEntry", "nte_next", NTE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ForEachNTE(
|
||
|
char *fieldstr,
|
||
|
int op,
|
||
|
ULONG64 value
|
||
|
)
|
||
|
{
|
||
|
ULONG64 Table, Addr, NTE, NetTable;
|
||
|
ULONG Offset;
|
||
|
ULONG i, j;
|
||
|
ULONG Stride;
|
||
|
char buff[80];
|
||
|
ULONG NetTableSize;
|
||
|
|
||
|
if ((op!=OP_TRUE) && GetFieldOffset("tcpip!NetTableEntry", fieldstr, &Offset) != 0) {
|
||
|
dprintf("Can't get offset of %s in NetTableEntry block!\n", fieldstr);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Stride = GetTypeSize("PVOID");
|
||
|
|
||
|
NetTableSize = (ULONG)GetExpression("poi(tcpip!NET_TABLE_SIZE)");
|
||
|
|
||
|
//
|
||
|
// Walk NTE list
|
||
|
//
|
||
|
NetTable = GetExpression("poi(tcpip!NewNetTableList)");
|
||
|
for (j=0; j<NetTableSize; j++) {
|
||
|
if (CheckControlC())
|
||
|
break;
|
||
|
|
||
|
sprintf(buff, "poi(%p)", NetTable + j * Stride);
|
||
|
NTE = GetExpression(buff);
|
||
|
while (NTE) {
|
||
|
if (CheckControlC())
|
||
|
break;
|
||
|
|
||
|
ProcessRecord(NTE, "tcpip!NetTableEntry", fieldstr, op, value);
|
||
|
|
||
|
GetFieldValue(NTE, "tcpip!NetTableEntry", "nte_next", NTE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ForEachAO(
|
||
|
char *fieldstr,
|
||
|
int op,
|
||
|
ULONG64 value
|
||
|
)
|
||
|
{
|
||
|
ULONG64 Table, Addr;
|
||
|
ULONG Offset;
|
||
|
ULONG Size, i;
|
||
|
ULONG Stride;
|
||
|
char buff[80];
|
||
|
|
||
|
if ((op!=OP_TRUE) && GetFieldOffset("tcpip!AddrObj", fieldstr, &Offset) != 0) {
|
||
|
dprintf("Can't get offset of %s in AddrObj block!\n", fieldstr);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Stride = GetTypeSize("PVOID");
|
||
|
|
||
|
// Get tcpip!AddrObjTableSize
|
||
|
Size = (ULONG)GetExpression("poi(tcpip!AddrObjTableSize)");
|
||
|
|
||
|
// Get tcpip!AddrObjTable
|
||
|
Table = GetExpression("poi(tcpip!AddrObjTable)");
|
||
|
|
||
|
// for each table entry
|
||
|
for (i=0; i<Size; i++) {
|
||
|
if (CheckControlC())
|
||
|
break;
|
||
|
|
||
|
// Walk AO list
|
||
|
sprintf(buff, "poi(%p)", Table + i * Stride);
|
||
|
Addr = GetExpression(buff);
|
||
|
while (Addr) {
|
||
|
if (CheckControlC())
|
||
|
break;
|
||
|
ProcessRecord(Addr, "tcpip!AddrObj", fieldstr, op, value);
|
||
|
|
||
|
GetFieldValue(Addr, "tcpip!AddrObj", "ao_next", Addr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DECLARE_API( find )
|
||
|
{
|
||
|
char typestr[80], fieldstr[80], opstr[80], valuestr[80];
|
||
|
int op, type;
|
||
|
ULONG64 value;
|
||
|
|
||
|
if (!args)
|
||
|
return;
|
||
|
|
||
|
if (sscanf(args, "%s%s%s%s", typestr, fieldstr, opstr, valuestr) != 4) {
|
||
|
if (sscanf(args, "%s", typestr) != 1) {
|
||
|
dprintf("usage: find <type> <field> <op> <value>\n");
|
||
|
return;
|
||
|
}
|
||
|
op = OP_TRUE;
|
||
|
} else {
|
||
|
//
|
||
|
// Validate op argument
|
||
|
//
|
||
|
for (op=0; validops[op]; op++) {
|
||
|
if (!_stricmp(validops[op], opstr))
|
||
|
break;
|
||
|
}
|
||
|
if (!validops[op]) {
|
||
|
dprintf("Invalid <op> value. Valid values are:\n");
|
||
|
for (op=0; validops[op]; op++)
|
||
|
dprintf(" %s\n", validops[op]);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Validate type argument
|
||
|
//
|
||
|
for (type=0; validtypes[type]; type++) {
|
||
|
if (!_stricmp(validtypes[type], typestr))
|
||
|
break;
|
||
|
}
|
||
|
if (!validtypes[type]) {
|
||
|
dprintf("Invalid <type> value. Valid values are:\n");
|
||
|
for (type=0; validtypes[type]; type++)
|
||
|
dprintf(" %s\n", validtypes[type]);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Parse valuestr
|
||
|
//
|
||
|
value = GetExpression(valuestr);
|
||
|
|
||
|
switch(type) {
|
||
|
case TYP_AO:
|
||
|
ForEachAO(fieldstr, op, value);
|
||
|
break;
|
||
|
case TYP_IGMP_ADDR:
|
||
|
ForEachIGMPAddr(fieldstr, op, value);
|
||
|
break;
|
||
|
case TYP_NTE:
|
||
|
ForEachNTE(fieldstr, op, value);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get 'size' bytes from the debuggee program at 'dwAddress' and place it
|
||
|
// in our address space at 'ptr'. Use 'type' in an error printout if necessary
|
||
|
//
|
||
|
// This function was stolen from ndiskd
|
||
|
//
|
||
|
BOOL
|
||
|
GetData(
|
||
|
IN LPVOID ptr,
|
||
|
IN ULONG64 dwAddress,
|
||
|
IN ULONG size,
|
||
|
IN PCSTR type
|
||
|
)
|
||
|
{
|
||
|
BOOL b;
|
||
|
ULONG BytesRead;
|
||
|
ULONG count = size;
|
||
|
|
||
|
while (size > 0) {
|
||
|
if (count >= 3000)
|
||
|
count = 3000;
|
||
|
|
||
|
b = ReadMemory(dwAddress, ptr, count, &BytesRead );
|
||
|
|
||
|
if (!b || BytesRead != count) {
|
||
|
dprintf( "Unable to read %u bytes at %lX, for %s\n", size, dwAddress,
|
||
|
type );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
dwAddress += count;
|
||
|
size -= count;
|
||
|
ptr = (LPVOID)((ULONG_PTR)ptr + count);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
#define MAX_STRING_LENGTH 256
|
||
|
|
||
|
//
|
||
|
// This function was stolen from ndiskd
|
||
|
//
|
||
|
BOOL
|
||
|
GetName(
|
||
|
ULONG64 UnicodeStringAddr,
|
||
|
UCHAR *abuf
|
||
|
)
|
||
|
{
|
||
|
USHORT i;
|
||
|
WCHAR ubuf[MAX_STRING_LENGTH];
|
||
|
ULONG MaxChars;
|
||
|
|
||
|
ULONG64 BufAddr;
|
||
|
USHORT Length;
|
||
|
USHORT MaximumLength;
|
||
|
|
||
|
ULONG64 Val;
|
||
|
|
||
|
GetFieldValue(UnicodeStringAddr, NDIS_STRING_NAME, "Buffer", Val);
|
||
|
BufAddr = Val;
|
||
|
|
||
|
GetFieldValue(UnicodeStringAddr, NDIS_STRING_NAME, "Length", Val);
|
||
|
Length = (USHORT)Val;
|
||
|
|
||
|
GetFieldValue(UnicodeStringAddr, NDIS_STRING_NAME, "MaximumLength", Val);
|
||
|
MaximumLength = (USHORT)Val;
|
||
|
|
||
|
//
|
||
|
// Truncate so that we don't crash with bad data.
|
||
|
//
|
||
|
MaxChars = (Length > MAX_STRING_LENGTH)? MAX_STRING_LENGTH: Length;
|
||
|
|
||
|
if (!GetData(ubuf, BufAddr, MaxChars, "STRING")) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < Length/2; i++) {
|
||
|
abuf[i] = (UCHAR)ubuf[i];
|
||
|
}
|
||
|
abuf[i] = 0;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This function was stolen from ndiskd
|
||
|
//
|
||
|
BOOL
|
||
|
PrintName(
|
||
|
ULONG64 UnicodeStringAddr
|
||
|
)
|
||
|
{
|
||
|
UCHAR abuf[MAX_STRING_LENGTH+1];
|
||
|
|
||
|
if (!GetName(UnicodeStringAddr, abuf))
|
||
|
return FALSE;
|
||
|
|
||
|
dprintf("%s", abuf);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
DECLARE_API( refcounts )
|
||
|
{
|
||
|
ULONG64 Addr;
|
||
|
LONG sent, done;
|
||
|
ULONG ret, ret2;
|
||
|
ULONG64 ProtocolListAddr;
|
||
|
ULONG64 ProtocolAddr, ProtocolAddr2, OpenAddr, MiniportAddr;
|
||
|
ULONG Offset;
|
||
|
ULONG64 Val;
|
||
|
ULONG64 Refs;
|
||
|
UCHAR abuf[MAX_STRING_LENGTH+1];
|
||
|
|
||
|
//
|
||
|
// Check LANARP refcounts
|
||
|
//
|
||
|
if (!GetExpressionEx("ndis!ndisProtocolList", &ProtocolListAddr, NULL)) {
|
||
|
dprintf("failed to locate ndis!ndisProtocolList\n");
|
||
|
return;
|
||
|
}
|
||
|
ReadPtr(ProtocolListAddr, &ProtocolAddr);
|
||
|
while (ProtocolAddr != 0) {
|
||
|
if (CheckControlC())
|
||
|
break;
|
||
|
|
||
|
if (GetFieldOffset(NDIS_PROTOCOL_BLOCK_NAME,
|
||
|
"ProtocolCharacteristics.Name", &Offset) != 0)
|
||
|
{
|
||
|
dprintf("Cant get offset of Name in Protocol block!");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get protocol name
|
||
|
//
|
||
|
if (!GetName(ProtocolAddr + Offset, abuf)) {
|
||
|
dprintf("Cant get Name in Protocol block!");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (_stricmp(abuf, "TCPIP") && _stricmp(abuf, "TCPIP_WANARP")) {
|
||
|
// dprintf("Skipping ndis protocol %s...\n", abuf);
|
||
|
ret = GetFieldValue(ProtocolAddr, NDIS_PROTOCOL_BLOCK_NAME,
|
||
|
"NextProtocol", ProtocolAddr);
|
||
|
if (ret)
|
||
|
dprintf("get NextProtocol failed, ret=%d\n", ret);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
dprintf("protocol %p: %s\n", ProtocolAddr, abuf);
|
||
|
ret = GetFieldValue(ProtocolAddr, NDIS_PROTOCOL_BLOCK_NAME, "OpenQueue",
|
||
|
OpenAddr);
|
||
|
if (ret)
|
||
|
dprintf("get OpenQueue failed, ret=%d\n", ret);
|
||
|
|
||
|
while (OpenAddr) {
|
||
|
if (CheckControlC())
|
||
|
break;
|
||
|
|
||
|
//
|
||
|
// Sanity check back pointer
|
||
|
//
|
||
|
ret = GetFieldValue(OpenAddr, NDIS_COMMON_OPEN_BLOCK_NAME,
|
||
|
"ProtocolHandle", ProtocolAddr2);
|
||
|
if (ret)
|
||
|
dprintf("get ProtocolHandle failed, ret=%d\n", ret);
|
||
|
if (ProtocolAddr2 != ProtocolAddr) {
|
||
|
dprintf("mopen linkage error protocol %p mopen %p protocol %p\n",
|
||
|
ProtocolAddr, OpenAddr, ProtocolAddr2);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
ret = GetFieldValue(OpenAddr, NDIS_COMMON_OPEN_BLOCK_NAME,
|
||
|
"MiniportHandle", MiniportAddr);
|
||
|
if (ret)
|
||
|
dprintf("get MiniportHandle failed, ret=%d\n", ret);
|
||
|
|
||
|
ret = GetFieldValue(MiniportAddr, NDIS_MINIPORT_BLOCK_NAME,
|
||
|
"pAdapterInstanceName", Val);
|
||
|
if (ret)
|
||
|
dprintf("get pAdapterInstanceName failed, ret=%d\n", ret);
|
||
|
|
||
|
dprintf(" miniport %p : ", MiniportAddr);
|
||
|
PrintName(Val);
|
||
|
|
||
|
ret = GetFieldValue(OpenAddr, NDIS_COMMON_OPEN_BLOCK_NAME,
|
||
|
"References", Refs);
|
||
|
if (ret)
|
||
|
dprintf("get References failed, ret=%d\n", ret);
|
||
|
|
||
|
dprintf("\n mopen %p references : %I64d\n", OpenAddr, Refs);
|
||
|
|
||
|
ret = GetFieldValue(OpenAddr, NDIS_COMMON_OPEN_BLOCK_NAME,
|
||
|
"ProtocolNextOpen", OpenAddr);
|
||
|
if (ret)
|
||
|
dprintf("get ProtocolNextOpen failed, ret=%d\n", ret);
|
||
|
}
|
||
|
|
||
|
ret = GetFieldValue(ProtocolAddr, NDIS_PROTOCOL_BLOCK_NAME,
|
||
|
"NextProtocol", ProtocolAddr);
|
||
|
if (ret)
|
||
|
dprintf("get NextProtocol failed, ret=%d\n", ret);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check WANARP refcounts
|
||
|
//
|
||
|
if (!GetExpressionEx("ndiswan!glsendcount", &Addr, NULL)) {
|
||
|
dprintf("failed to locate ndiswan!glsendcount\n");
|
||
|
return;
|
||
|
}
|
||
|
ReadMemory(Addr, &sent, sizeof(sent), NULL);
|
||
|
|
||
|
Addr = GetExpression("ndiswan!glsendcompletecount");
|
||
|
ReadMemory(Addr, &done, sizeof(done), NULL);
|
||
|
|
||
|
dprintf("WANARP: references : %ld\n", sent-done);
|
||
|
|
||
|
return;
|
||
|
}
|