分享
 
 
 

Debug Tutorial Part 4: Writing WINDBG Extensions

王朝system·作者佚名  2006-04-18
窄屏简体版  字體: |||超大  

Introduction

Welcome to the fourth installment of this debugging series. In this installment, we will be getting a bit away from actual debugging for a bit and dive into creating helpful debugging aids. I definitely would never condone writing debugging aids just to write them. I would find a reason to write something, perhaps something you do all the time that gets tedious. Once you have found something you would love to automate, I would then see how you could automate it.

This is what I have done. During my debugging escapades, I always search the stack and other locations for strings. Why do I do this? People are not computers, we understand language rather than numbers. This being the case, a lot of applications and even drivers are written based upon strings. I would not say everything is a string, but there's usually a string somewhere. If you think about it, you really don't need strings at all. We could all just use numbers and never use another string again. Some may think well, when you expose a UI of course, you're going to eventually run into a string somewhere... Well, doesn't have to be that way, now does it? I mean, I'm not even talking about just User Interfaces, I'm talking about the guts of programs that aren't even exposed to the user at all. Programmers are still human and like to talk in some language beyond binary. This being the case, even the internals of applications sometimes use strings to represent things. You can find strings just about anywhere, even in drivers.

So There're Strings, So What?

Well, they provide an added level of readability to an application. This is the first rule of thumb, perhaps if you could find a string somewhere on the stack, you could better track down what the application is doing. These strings could be environment strings, file names, device names (COM1, \Device\xxxx, etc.), names of other objects, user names, GUIDs, etc. This information can better help track down what the application is doing and where it could be in your program.

Another interesting concept is, I don't know this for a fact, but it's my belief that the most common buffer overruns are due to invalid string manipulations. Whether it be forgetting the NULL character, or misjudging allocation since an API returns # of characters and not # of bytes. If a string overwrote memory in your program and you can find the string, it's a lot easier to track down who created it.

Where do we start?

I start on the stack. I have a trap, the first thing I do after "KB" and "DDS ESP" would then be to use "DC ESP". This command dumps the DWORDs on one side and the printable characters on the other. Let's see an example of this:

0:000> dc esp

0006febc 77d43a09 77d43c7d 0006fefc 00000000 .:.w}<.w........

0006fecc 00000000 00000000 00000000 0006ff1c ................

0006fedc 010028e4 0006fefc 00000000 00000000 .(..............

0006feec 00000000 00000000 77e7ad86 00091ee7 ...........w....

0006fefc 001a03e4 00000118 0000ffff bf8a75ed .............u..

0006ff0c 0768a2ca 00000229 00000251 00000000 ..h.)...Q.......

0006ff1c 0006ffc0 01006c54 01000000 00000000 ....Tl..........

0006ff2c 00091ee7 0000000a 00000000 00000000 ................

0:000> dc

0006ff3c 7ffdf000 80543940 f544fc5c 00000044 ....@9T.\.D.D...

0006ff4c 00092b28 00092b48 00092b70 00000000 (+..H+..p+......

0006ff5c 00000000 00000000 00000000 00000000 ................

0006ff6c 00000000 00000000 00000000 00000000 ................

0006ff7c 00000000 ffffffff ffffffff ffffffff ................

0006ff8c 00091ee7 00000000 00000001 00272620 ............ &'.

0006ff9c 00272d00 00000000 00000000 0006ff34 .-'.........4...

0006ffac e24296d0 0006ffe0 01006d14 01001888 ..B......m......

0:000>

0006ffbc 00000000 0006fff0 77e814c7 00000000 ...........w....

0006ffcc 00000000 7ffdf000 f544fcf0 0006ffc8 ..........D.....

0006ffdc 80534504 ffffffff 77e94809 77e91210 .ES......H.w...w

0006ffec 00000000 00000000 00000000 01006ae0 .............j..

0006fffc 00000000 78746341 00000020 00000001 ....Actx .......

0007000c 00000654 0000007c 00000000 00000020 T...|....... ...

0007001c 00000000 00000014 00000001 00000003 ................

0007002c 00000034 000000ac 00000001 00000000 4...............

0:000>

0007003c 00000000 00000000 00000000 00000000 ................

0007004c 00000002 00000000 00000000 00000000 ................

0007005c 00000168 00000190 00000000 2d59495b h...........[IY-

0007006c 000002f8 00000032 0000032c 000002b8 ....2...,.......

0007007c 00000010 00000002 0000008c 00000002 ................

0007008c 00000001 000000ac 00000538 00000001 ........8.......

0007009c 00000002 000005e4 00000070 00000001 ........p.......

000700ac 64487353 0000002c 00000001 00000001 SsHd,...........

0:000>

000700bc 00000003 00000002 0000008c 00000001 ................

000700cc 00000000 0000002c 0000005e 0000005e ....,...^...^...

000700dc 00000000 00000000 00000000 00000000 ................

000700ec 00000000 00000000 00000000 00000000 ................

000700fc 00000000 00000002 00000028 00000034 ........(...4...

0007010c 003a0043 0057005c 004e0049 004f0044 C.:.\.W.I.N.D.O.

0007011c 00530057 0030002e 0057005c 006e0069 W.S...0.\.W.i.n.

0007012c 00780053 005c0073 00000000 00000000 S.x.s.\.........

I started notepad.exe and just broke into it, then dumped the stack of the primary (and only) thread. The strings on the stack are "local arrays", such as declaring char x[10]; in your function. These aren't the only strings in a program though. There are others and these others are stored in pointers that are declared as local variables and even passed to functions such as CreateFile. The first parameter of CreateFile takes a string.

So, what do I usually do? I then search the stack for memory locations which could be strings and I then do "DC" again on them or "DA" for Dump ANSI string or "DU" for Dump Unicode String. The problem with this is that it's slow and tedious. I could not find any debugger command to do this for me (if there is one, let me know), so I ended up writing my own.

Writing Your Own?

WINDBG supports DLLs created by anyone as long as they export functions and behave in a manner defined by Microsoft. This means you can write PLUGINS! It used to be that people would write plug-ins to !<mydatastructure> <address> which basically dumped the members of their data structure along with the names. However, WINDBG supports the "dt" command that if you have a PDB (symbol file) it can do this for you without writing any code! Let's see an example of this.

Using the "dt <yourdll>!<your data structure>" will dump the structure's content along with their names. Let's look at a quick example.

0:000> dt ntdll!_PEB

+0x000 InheritedAddressSpace : UChar

+0x001 ReadImageFileExecOptions : UChar

+0x002 BeingDebugged : UChar

+0x003 SpareBool : UChar

+0x004 Mutant : Ptr32 Void

+0x008 ImageBaseAddress : Ptr32 Void

+0x00c Ldr : Ptr32 _PEB_LDR_DATA

+0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS

+0x014 SubSystemData : Ptr32 Void

+0x018 ProcessHeap : Ptr32 Void

+0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION

+0x020 FastPebLockRoutine : Ptr32 Void

+0x024 FastPebUnlockRoutine : Ptr32 Void

+0x028 EnvironmentUpdateCount : Uint4B

+0x02c KernelCallbackTable : Ptr32 Void

+0x030 SystemReserved : [1] Uint4B

+0x034 ExecuteOptions : Pos 0, 2 Bits

+0x034 SpareBits : Pos 2, 30 Bits

+0x038 FreeList : Ptr32 _PEB_FREE_BLOCK

+0x03c TlsExpansionCounter : Uint4B

+0x040 TlsBitmap : Ptr32 Void

+0x044 TlsBitmapBits : [2] Uint4B

+0x04c ReadOnlySharedMemoryBase : Ptr32 Void

+0x050 ReadOnlySharedMemoryHeap : Ptr32 Void

+0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void

+0x058 AnsiCodePageData : Ptr32 Void

+0x05c OemCodePageData : Ptr32 Void

+0x060 UnicodeCaseTableData : Ptr32 Void

+0x064 NumberOfProcessors : Uint4B

+0x068 NtGlobalFlag : Uint4B

+0x070 CriticalSectionTimeout : _LARGE_INTEGER

+0x078 HeapSegmentReserve : Uint4B

+0x07c HeapSegmentCommit : Uint4B

+0x080 HeapDeCommitTotalFreeThreshold : Uint4B

+0x084 HeapDeCommitFreeBlockThreshold : Uint4B

+0x088 NumberOfHeaps : Uint4B

+0x08c MaximumNumberOfHeaps : Uint4B

+0x090 ProcessHeaps : Ptr32 Ptr32 Void

+0x094 GdiSharedHandleTable : Ptr32 Void

+0x098 ProcessStarterHelper : Ptr32 Void

+0x09c GdiDCAttributeList : Uint4B

+0x0a0 LoaderLock : Ptr32 Void

+0x0a4 OSMajorVersion : Uint4B

+0x0a8 OSMinorVersion : Uint4B

+0x0ac OSBuildNumber : Uint2B

+0x0ae OSCSDVersion : Uint2B

+0x0b0 OSPlatformId : Uint4B

+0x0b4 ImageSubsystem : Uint4B

+0x0b8 ImageSubsystemMajorVersion : Uint4B

+0x0bc ImageSubsystemMinorVersion : Uint4B

+0x0c0 ImageProcessAffinityMask : Uint4B

+0x0c4 GdiHandleBuffer : [34] Uint4B

+0x14c PostProcessInitRoutine : Ptr32

+0x150 TlsExpansionBitmap : Ptr32 Void

+0x154 TlsExpansionBitmapBits : [32] Uint4B

+0x1d4 SessionId : Uint4B

+0x1d8 AppCompatFlags : _ULARGE_INTEGER

+0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER

+0x1e8 pShimData : Ptr32 Void

+0x1ec AppCompatInfo : Ptr32 Void

+0x1f0 CSDVersion : _UNICODE_STRING

+0x1f8 ActivationContextData : Ptr32 Void

+0x1fc ProcessAssemblyStorageMap : Ptr32 Void

+0x200 SystemDefaultActivationContextData : Ptr32 Void

+0x204 SystemAssemblyStorageMap : Ptr32 Void

+0x208 MinimumStackCommit : Uint4B

0:000>

We just dumped out the structure information for the _PEB structure defined by Windows. We will now attempt to find our PEB and dump this address.

0:000> !teb

TEB at 7ffde000

ExceptionList: 0006ffb0

StackBase: 00070000

StackLimit: 0005f000

SubSystemTib: 00000000

FiberData: 00001e00

ArbitraryUserPointer: 00000000

Self: 7ffde000

EnvironmentPointer: 00000000

ClientId: 00000b80 . 00000f40

RpcHandle: 00000000

Tls Storage: 00000000

PEB Address: 7ffdf000

LastErrorValue: 0

LastStatusValue: c0000034

Count Owned Locks: 0

HardErrorMode: 0

0:000> dt ntdll!_PEB 7ffdf000

+0x000 InheritedAddressSpace : 0 ''

+0x001 ReadImageFileExecOptions : 0 ''

+0x002 BeingDebugged : 0x1 ''

+0x003 SpareBool : 0 ''

+0x004 Mutant : 0xffffffff

+0x008 ImageBaseAddress : 0x01000000

+0x00c Ldr : 0x00191ea0

+0x010 ProcessParameters : 0x00020000

+0x014 SubSystemData : (null)

+0x018 ProcessHeap : 0x00090000

+0x01c FastPebLock : 0x77fc49e0

+0x020 FastPebLockRoutine : 0x77f5b2a0

+0x024 FastPebUnlockRoutine : 0x77f5b380

+0x028 EnvironmentUpdateCount : 1

+0x02c KernelCallbackTable : 0x77d42a38

+0x030 SystemReserved : [1] 0

+0x034 ExecuteOptions : 0y00

+0x034 SpareBits : 0y000000000000000000000000000000 (0)

+0x038 FreeList : (null)

+0x03c TlsExpansionCounter : 0

+0x040 TlsBitmap : 0x77fc4680

+0x044 TlsBitmapBits : [2] 0x7ff

+0x04c ReadOnlySharedMemoryBase : 0x7f6f0000

+0x050 ReadOnlySharedMemoryHeap : 0x7f6f0000

+0x054 ReadOnlyStaticServerData : 0x7f6f0688 -> (null)

+0x058 AnsiCodePageData : 0x7ffb0000

+0x05c OemCodePageData : 0x7ffc1000

+0x060 UnicodeCaseTableData : 0x7ffd2000

+0x064 NumberOfProcessors : 1

+0x068 NtGlobalFlag : 0x70

+0x070 CriticalSectionTimeout : _LARGE_INTEGER 0xffffe86d`079b8000

+0x078 HeapSegmentReserve : 0x100000

+0x07c HeapSegmentCommit : 0x2000

+0x080 HeapDeCommitTotalFreeThreshold : 0x10000

+0x084 HeapDeCommitFreeBlockThreshold : 0x1000

+0x088 NumberOfHeaps : 5

+0x08c MaximumNumberOfHeaps : 0x10

+0x090 ProcessHeaps : 0x77fc5a80 -> 0x00090000

+0x094 GdiSharedHandleTable : 0x00360000

+0x098 ProcessStarterHelper : (null)

+0x09c GdiDCAttributeList : 0x14

+0x0a0 LoaderLock : 0x77fc1774

+0x0a4 OSMajorVersion : 5

+0x0a8 OSMinorVersion : 1

+0x0ac OSBuildNumber : 0xa28

+0x0ae OSCSDVersion : 0x100

+0x0b0 OSPlatformId : 2

+0x0b4 ImageSubsystem : 2

+0x0b8 ImageSubsystemMajorVersion : 4

+0x0bc ImageSubsystemMinorVersion : 0

+0x0c0 ImageProcessAffinityMask : 0

+0x0c4 GdiHandleBuffer : [34] 0

+0x14c PostProcessInitRoutine : (null)

+0x150 TlsExpansionBitmap : 0x77fc4660

+0x154 TlsExpansionBitmapBits : [32] 0

+0x1d4 SessionId : 0

+0x1d8 AppCompatFlags : _ULARGE_INTEGER 0x0

+0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER 0x0

+0x1e8 pShimData : (null)

+0x1ec AppCompatInfo : (null)

+0x1f0 CSDVersion : _UNICODE_STRING "Service Pack 1"

+0x1f8 ActivationContextData : 0x00080000

+0x1fc ProcessAssemblyStorageMap : 0x000929a8

+0x200 SystemDefaultActivationContextData : 0x00070000

+0x204 SystemAssemblyStorageMap : (null)

+0x208 MinimumStackCommit : 0

0:000>

If you remember correctly, the !teb gives you the "thread environment block". A part of this block will show you the PEB for a process. Now, you see that not only does it print the information contained in the data structure, it also knows how to display the information based upon the type of data. Why did I show you this? This is because of what I specified above. The first thing people do or want to do is start to write debug extensions to dump all their data structures and this is not feasible anymore! You then have to keep changing your debug code every time you add or move information around. It's best to use the "dt" command and painlessly find all the data contained in any of your internal data structures.

Starting To Write The Extension

So, my proposed extension is !dumpstrings. This will go through a memory location and dump all the pointers. I can then do !dumpstrings esp and dump the strings to all pointers on the stack.

First, let's start with how you write the extension. WINDBG requires you at least export two functions. These functions in my source are posted below:

/***********************************************************

* ExtensionApiVersion

*

* Purpose: WINDBG will call this function to get the version

* of the API

*

* Parameters:

* Void

*

* Return Values:

* Pointer to a EXT_API_VERSION structure.

*

***********************************************************/

LPEXT_API_VERSION WDBGAPI ExtensionApiVersion (void)

{

return &g_ExtApiVersion;

}

/***********************************************************

* WinDbgExtensionDllInit

*

* Purpose: WINDBG will call this function to initialize

* the API

*

* Parameters:

* Pointer to the API functions, Major Version, Minor Version

*

* Return Values:

* Nothing

*

***********************************************************/

VOID WDBGAPI WinDbgExtensionDllInit (PWINDBG_EXTENSION_APIS

lpExtensionApis, USHORT usMajorVersion,

USHORT usMinorVersion)

{

ExtensionApis = *lpExtensionApis;

}

The first function, ExtensionApiVersion, simply returns a version, and all we do is supply version numbers that WINDBG would expect to make it happy. Here is the declaration of g_ExtApiVersion.

/***********************************************************

* Global Variable Needed For Versioning

***********************************************************/

EXT_API_VERSION g_ExtApiVersion = {

5 ,

5 ,

EXT_API_VERSION_NUMBER ,

0

} ;

The EXT_API_VERSION_NUMBER is declared in wdbgexts.h. Please note that there are other variations of debugger extension DLLs, such as using ntsdexts.h. I am going over just one simple example that works with the current CDB and WinDbg debuggers that you can download off of Microsoft's web site. I am also using windbgexts.h, not ntsdexts.h. If you look at your SDK include files, you will notice you have both headers.

So, how is EXT_API_VESRION_NUMBER declared? On my system, it is declared as:

#define EXT_API_VERSION_NUMBER 5

typedef struct EXT_API_VERSION {

USHORT MajorVersion;

USHORT MinorVersion;

USHORT Revision;

USHORT Reserved;

} EXT_API_VERSION, *LPEXT_API_VERSION;

Where did you come up with 5, 5? I just debugged some of the other extensions that come with WINDBG to find those numbers. I also found that older WINDBGs use 3, 5. This is really a moot point though, we just need a basic frame work written, after which we can simply write all the commands we want! I'm not big on making things like this big issues or digging deep into their meaning when, it's really irrelevant to me as long as my extension loads.

The WinDbgExtensionDllInit API simply gives your application a virtual function table. This table *must* be named a certain name. Well, it doesn't have to be but it's easier when it is. The reason is that the windbgexts.h contains macros to make function calls to functions stored in this structure. If you don't use the same name, you'll have to create the calls yourself. This is my global in the code:

/***********************************************************

* Global Variable Needed For Functions

***********************************************************/

WINDBG_EXTENSION_APIS ExtensionApis = {0};

It's really no big deal, the macros just make it a little easier, that's all. You can do whatever you like though. This is the dump of WINDBGEXTS.H macros:

#define dprintf (ExtensionApis.lpOutputRoutine)

#define GetExpression (ExtensionApis.lpGetExpressionRoutine)

#define GetSymbol (ExtensionApis.lpGetSymbolRoutine)

#define Disassm (ExtensionApis.lpDisasmRoutine)

#define CheckControlC (ExtensionApis.lpCheckControlCRoutine)

#define ReadMemory (ExtensionApis.lpReadProcessMemoryRoutine)

#define WriteMemory (ExtensionApis.lpWriteProcessMemoryRoutine)

#define GetContext (ExtensionApis.lpGetThreadContextRoutine)

#define SetContext (ExtensionApis.lpSetThreadContextRoutine)

#define Ioctl (ExtensionApis.lpIoctlRoutine)

#define StackTrace (ExtensionApis.lpStackTraceRoutine)

What's next? Aside from these two APIs, you can have a CheckVersion() function to force your extension to only use commands on certain versions of WINDBG. I found this completely useless and didn't implement it as it's not required. So, let's just write a function!

The first function will be simple. We'll do "!help" which will dump the help.

/***********************************************************

* !help

*

* Purpose: WINDBG will call this API when the user types !help

*

*

* Parameters:

* N/A

*

* Return Values:

* N/A

*

***********************************************************/

DECLARE_API (help)

{

dprintf("Toby's Debug Extensions\n\n");

dprintf("!dumpstrings <ADDRESS register> - Dumps 20 strings in" "ANSI/UNICODE using this address as a pointer to strings (useful for" "dumping strings on the stack) \n");

/* String Split so it is readable in this article. */

}

dprintf(); is basically, "debug printf" and it's exactly like printf()! It will print output to the debugger. The DECLARE_API(<command>) is a simple way to declare the API name. Remember, the name you give the function is the name you use in the debugger. I called this help, so to use it, it's !help or !<dllname>.help. This is a simple function that will simply display a help message to the user.

The next thing we want to do is implement our string function. This function will take a parameter which will be a memory address. I also want it to work like current commands, if you do dc <address> then dc again, it will continue where it left off dumping the memory. So, let's see what I've come up with.

/***********************************************************

* !dumpstrings

*

* Purpose: WINDBG will call this API when the user types !dumpstrings

*

*

* Parameters:

* !dumpstrings or !dumpstrings <ADDRESS register>

*

* Return Values:

* N/A

*

***********************************************************/

DECLARE_API (dumpstrings)

{

static ULONG Address = 0;

ULONG GetAddress, StringAddress, Index = 0, Bytes;

WCHAR MyString[51] = {0};

GetAddress = GetExpression(args);

if(GetAddress != 0)

{

Address = GetAddress;

}

dprintf("STACK ADDR STRING \n");

for(Index = 0; Index < 4*20; Index+=4)

{

memset(MyString, 0, sizeof(MyString));

Bytes = 0;

ReadMemory(Address + Index, &StringAddress,

sizeof(StringAddress), &Bytes);

if(Bytes)

{

Bytes = 0;

ReadMemory(StringAddress, MyString,

sizeof(MyString) - 2, &Bytes);

if(Bytes)

{

dprintf("%08x : %08x = (UNICODE) \"%ws\"\n",

Address + Index, StringAddress, MyString);

dprintf("%08x : %08x = (ANSI) \"%s\"\n",

Address + Index, StringAddress, MyString);

}

else

{

dprintf("%08x : %08x = Address Not Valid\n",

Address + Index, StringAddress);

}

}

else

{

dprintf("%08x : Address Not Valid\n", Address + Index);

}

}

Address += Index;

}

So, the first function I use is GetExpression(). On the newer WINDBGs, it works like this. ADDRESS GetExpression(SYMBOLIC STRING). You pass in a symbolic string, such as the arguments to the command and it attempts to resolve it to an address. The arguments to the command are stored in args, so I pass in args. This will resolve symbols, addresses or even registers to numbers as would be with the case of passing in ESP.

I now have a static variable. If GetExpression() returned 0, it's possible there are no arguments. If this is the case, I use Address, my static variable. This works if someone does !dumpstrings. It will continue where it left off. At the end of the function, I always save Address as being the next location to dump.

The next function I use is dprintf() which works just like using printf. I've explained this one already, so onto the next tidbit. Addresses are 4 bytes long, so I will increment this address by 4 each time around in the loop. I want to dump 20 addresses, so I loop from 0 to 4*20. Very simple indeed.

Now, you cannot simply reference the memory returned as you are not in the process space of the application. So, WINDBG provides functions to do this such as ReadMemory(). (The windbgexts.h provides prototypes for all the APIs, so you can experiment if you cannot find the API online.) The ReadMemory() function takes 4 arguments:

ReadMemory(Address In Process To Read,

Local Variable to store the memory read,

size of the local variable,

pointer to a DWORD that returns the number

of bytes read from the memory location);

So, we pass in our pointer to the memory in the application, a pointer to place the data on return, then receive the number of bytes. If no bytes are returned, we print an invalid memory address; if the bytes are returned, we then reference this memory location to read up to 49 bytes (we have 51 and put two NULLs at the end for Unicode support). If I was able to read anything, I then attempt to display it using the dprintf() function in ANSI then in UNICODE. If the memory returns 0 bytes, I print an error specifying it's not a valid address. This is all very simple.

The next thing we need to do is create our .DEF file. This file will simply export our functions.

LIBRARY "TDBG.DLL"

EXPORTS

WinDbgExtensionDllInit

ExtensionApiVersion

dumpstrings

help

Now, we need to build it. I like using make files and I use Visual Slickedit as my editor. I don't use the VC++ IDE at all. So, in the project, I've created a make file. This is how you would set it up. First, run VCVARS32.BAT which is located in the BIN directory of your VC++ installation. I moved mine to C:\ for easier use. The next thing to do is simply type "nmake" in the directory that the source code was extracted to.

C:\Programming\Programs\debugext\src\debug>\vcvars32

Setting environment for using Microsoft Visual C++ tools.

C:\Programming\Programs\debugext\src\debug>nmake

Microsoft (R) Program Maintenance Utility Version 6.00.8168.0

Copyright (C) Microsoft Corp 1988-1998. All rights reserved.

cl /nologo /MD /W3 /Oxs /Zi /I ".\inc" /D "WIN32" /DLL /D "_WINDOWS"

/Fr.\obj\i386\\ /Fo.\obj\i386\\ /Fd.\obj\i386\\ /c .\tdbg.c

tdbg.c

C:\PROGRA~1\MICROS~2\VC98\INCLUDE\wdbgexts.h(526) : warning C4101: 'li' : unrefe

renced local variable

link.exe /DLL /nologo /def:tdbg.def /out:..\..\bin\tdbg.dll /pdb:tdbg.p

db /debug /debugtype:both USER32.LIB KERNEL32.LIB .\obj\i386\tdbg.obj

Creating library ..\..\bin\tdbg.lib and object ..\..\bin\tdbg.exp

rebase.exe -b 0x00100000 -x ..\..\bin -a ..\..\bin\tdbg.dll

REBASE: Total Size of mapping 0x00010000

REBASE: Range 0x00100000 -0x00110000

C:\Programming\Programs\debugext\src\debug>nmake clean

Microsoft (R) Program Maintenance Utility Version 6.00.8168.0

Copyright (C) Microsoft Corp 1988-1998. All rights reserved.

Deleted file - C:\Programming\Programs\debugext\src\debug\obj\i386\tdbg.obj

Deleted file - C:\Programming\Programs\debugext\src\debug\obj\i386\tdbg.sbr

Deleted file - C:\Programming\Programs\debugext\src\debug\obj\i386\vc60.pdb

C:\Programming\Programs\debugext\src\debug>

If you want to build again, you'd need to either change the source so the date is updated or type "nmake clean" to rebuild. You should now have a binary.

Volume in drive C has no label.

Volume Serial Number is 2CF8-F7B5

Directory of C:\Programming\Programs\debugext\bin

03/25/2004 08:56 PM <DIR> .

03/25/2004 08:56 PM <DIR> ..

03/25/2004 09:53 PM 2,412 tdbg.dbg

03/25/2004 09:53 PM 20,752 tdbg.dll

03/25/2004 09:53 PM 1,044 tdbg.exp

03/25/2004 09:53 PM 2,538 tdbg.lib

03/25/2004 09:53 PM 82,944 tdbg.pdb

5 File(s) 109,690 bytes

2 Dir(s) 12,229,009,408 bytes free

Simply copy this binary to a location that can be found by your WinDbg, such as your windows directory. You can specify the path to load these extensions using !load and !unload (to unload the extension if you built a new version/ want to force the debugger to unload it so you can reload the next one). You can use !<dll name>.<function name> or just !<function name>. The mentioning of the binary name will force WINDBG to look for it to load it. It can also help to distinguish between which DLL to use for a command if multiple DLL extensions export it.

Let's Try It Out

Now that we have created our debug extension, I move it to a location where WINDBG can load it (such as my windows directory). I then use "!tdbg.dumpstrings esp" to dump all the strings on the stack. This is the same application (restarted so addresses may change) restarted (I verified with dc esp, I get the same strings on the stack as before). I now want to dump all the pointers to strings on the stack. Let's try this and see what happens!

0:000> !tdbg.dumpstrings esp

STACK ADDR STRING

0006febc : 77d43a09 = (UNICODE) ""

0006febc : 77d43a09 = (ANSI) "┬?"

0006fec0 : 77d43c7d = (UNICODE) ""

0006fec0 : 77d43c7d = (ANSI) "ïN♦ü???"

0006fec4 : 0006fefc = (UNICODE) ""

0006fec4 : 0006fefc = (ANSI) "?♥↔"

0006fec8 : 00000000 = Address Not Valid

0006fecc : 00000000 = Address Not Valid

0006fed0 : 00000000 = Address Not Valid

0006fed4 : 00000000 = Address Not Valid

0006fed8 : 0006ff1c = (UNICODE) ""

0006fed8 : 0006ff1c = (ANSI) "└ ♠"

0006fedc : 010028e4 = (UNICODE) ""

0006fedc : 010028e4 = (ANSI) "à└uöΦ├∩ 5î¢"

0006fee0 : 0006fefc = (UNICODE) ""

0006fee0 : 0006fefc = (ANSI) "?♥↔"

0006fee4 : 00000000 = Address Not Valid

0006fee8 : 00000000 = Address Not Valid

0006feec : 00000000 = Address Not Valid

0006fef0 : 00000000 = Address Not Valid

0006fef4 : 77e7ad86 = (UNICODE) ""

0006fef4 : 77e7ad86 = (ANSI) "â|___FCKpd___14diams;"

0006fef8 : 00091ee8 = (UNICODE) ""

0006fef8 : 00091ee8 = (ANSI) ""

0006fefc : 001d03de = (UNICODE) ""

0006fefc : 001d03de = (ANSI) ""

0006ff00 : 00000118 = Address Not Valid

0006ff04 : 0000ffff = Address Not Valid

0006ff08 : bf8a75ed = Address Not Valid

0:000> !tdbg.dumpstrings

STACK ADDR STRING

0006ff0c : 077d5cc8 = Address Not Valid

0006ff10 : 000001f3 = Address Not Valid

0006ff14 : 000001df = Address Not Valid

0006ff18 : 00000000 = Address Not Valid

0006ff1c : 0006ffc0 = (UNICODE) ""

0006ff1c : 0006ffc0 = (ANSI) "≡ ♠"

0006ff20 : 01006c54 = (UNICODE) ""

0006ff20 : 01006c54 = (ANSI) "ï≡ëuä9]ΣuV §▄?"

0006ff24 : 01000000 = (UNICODE) ""

0006ff24 : 01000000 = (ANSI) "MZÉ"

0006ff28 : 00000000 = Address Not Valid

0006ff2c : 00091ee8 = (UNICODE) ""

0006ff2c : 00091ee8 = (ANSI) ""

0006ff30 : 0000000a = Address Not Valid

0006ff34 : 00000000 = Address Not Valid

0006ff38 : 00000000 = Address Not Valid

0006ff3c : 7ffdf000 = (UNICODE) ""

0006ff3c : 7ffdf000 = (ANSI) ""

0006ff40 : 80543940 = Address Not Valid

0006ff44 : f4910c5c = Address Not Valid

0006ff48 : 00000044 = Address Not Valid

0006ff4c : 00092b30 = (UNICODE) ""

0006ff4c : 00092b30 = (ANSI) ""

0006ff50 : 00092b50 = (UNICODE) ""

0006ff50 : 00092b50 = (ANSI) "WinSta0\Default"

0006ff54 : 00092b78 = (UNICODE) ""

0006ff54 : 00092b78 = (ANSI) "C:\WINDOWS.0\System32\notepad.exe"

0006ff58 : 00000000 = Address Not Valid

0:000> !tdbg.dumpstrings

STACK ADDR STRING

0006ff5c : 00000000 = Address Not Valid

0006ff60 : 00000000 = Address Not Valid

0006ff64 : 00000000 = Address Not Valid

0006ff68 : 00000000 = Address Not Valid

0006ff6c : 00000000 = Address Not Valid

0006ff70 : 00000000 = Address Not Valid

0006ff74 : 00000000 = Address Not Valid

0006ff78 : 00000000 = Address Not Valid

0006ff7c : 00000000 = Address Not Valid

0006ff80 : ffffffff = Address Not Valid

0006ff84 : ffffffff = Address Not Valid

0006ff88 : ffffffff = Address Not Valid

0006ff8c : 00091ee8 = (UNICODE) ""

0006ff8c : 00091ee8 = (ANSI) ""

0006ff90 : 00000000 = Address Not Valid

0006ff94 : 00000001 = Address Not Valid

0006ff98 : 00272620 = (UNICODE) ""

0006ff98 : 00272620 = (ANSI) "(&'"

0006ff9c : 00272d00 = (UNICODE) ""

0006ff9c : 00272d00 = (ANSI) "?-'"

0006ffa0 : 00000000 = Address Not Valid

0006ffa4 : 00000000 = Address Not Valid

0006ffa8 : 0006ff34 = (UNICODE) ""

0006ffa8 : 0006ff34 = (ANSI) ""

We found two strings on the stack. Not bad and all I did was start Notepad. I didn't do anything else. This is a simple example of how this API could be used. Take a memory location and dump out all the pointers to strings. You are always going to get garbage and invalid address dumped on the stack (or any memory location for that matter). It's the valid addresses that contain strings that we are looking for.

Conclusion

I hope you enjoyed learning how to write a debug extension and hopefully even find the example useful! There are other APIs listed which you can look up or perhaps another tutorial can explain them in the future. The basics of reading memory and displaying information were covered. Hopefully, in the future, we can explore more advanced debugging commands.

About Toby Opferman

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有