Rubato and Chord

Reiley's technical blog

October, 2011

  • Rubato and Chord

    Undocumented WinDBG

    • 2 Comments

    Abstraction and encapsulation are good because they make it easier to build complex systems, however, there are times you have to peek inside the abstraction and demistify the encapsulation. This is especially true for debugging and performance tuning (I will not talk about reverse engineering this time). Familiar yourself with the right tools are very important, and one way to achieve this is to debug into these tools...

    Before getting started, I would like to wetting your appetite with a few questions:

    1. Are there undocumented commands in the Windows Debuggers?
    2. WinDBG supports child process debugging via .childdbg, does Visual Studio Debugger provide the same thing?
    3. Why do I need to put symbols on the target machine while remote debugging .NET applications in Visual Studio 2010?
    4. Why does the command foobarbaz not working as expected?

    Let's revisit the commands in WinDBG. There are three types of commands available:

    1. Debugger Commands
      These are the internal commands, examples are bp, g and pt. Internal commands are case insensitive in most cases (as and aS are exceptions), you can get a brief help of these commands by typing a single question mark (?).
    2. Meta Commands (a.k.a. Dot Commands)
      Meta commands always start with a dot, examples are .childdbg, .dvalloc and .dvfree. Meta commands are case insensitive, you can get a brief help of these commands using the meta command .help.
    3. Extension Commands
      Extension commands are provided by separate debugger extension modules, example are !dh and !sym. Debugging Tools for Windows has provided the SDK for implementing such extension DLLs, and it's fully documented (Writing New Debugger Extensions). Extension commands are case sensitive, some extensions (e.g. Son of Strike) has exported a same function under different names (e.g. SOS!CLRStack, SOS!ClrStack and SOS!clrstack).

    The documented way of working with extensions contains a few commands like .chain, .extmatch, .load, .loadby, .setdll.unload and .unloadall, an undocumented command .extcmds is also available.

    0:000> .extmatch /e dbghelp *
    !dbghelp.chksym
    !dbghelp.dh
    !dbghelp.homedir
    !dbghelp.lmi
    !dbghelp.stackdbg
    !dbghelp.sym
    

    However, once you understand how these extension DLLs work, you can dump the PE/COFF Export Directory to get a full list:

    0:000> .sympath .
    Symbol search path is: .
    Expanded Symbol search path is: .
    
    0:000> .reload /f dbghelp.dll
    *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Debugging Tools for Windows (x64)\dbghelp.dll -
    
    0:000> x dbghelp!*
    00000000`68013dd0 dbghelp!ExtensionApiVersion (<no parameter info>)
    00000000`68013de0 dbghelp!WinDbgExtensionDllInit (<no parameter info>)
    00000000`68014090 dbghelp!fptr (<no parameter info>)
    00000000`68014130 dbghelp!vc7fpo (<no parameter info>)
    00000000`680141b0 dbghelp!stackdbg (<no parameter info>)
    00000000`680146e0 dbghelp!stack_force_ebp (<no parameter info>)
    00000000`680148c0 dbghelp!sym (<no parameter info>)
    00000000`68014a50 dbghelp!symsrv (<no parameter info>)
    00000000`68014bd0 dbghelp!lminfo (<no parameter info>)
    00000000`68015060 dbghelp!lmi (<no parameter info>)
    00000000`680164e0 dbghelp!itoldyouso (<no parameter info>)
    00000000`68016720 dbghelp!chksym (<no parameter info>)
    00000000`68016960 dbghelp!block (<no parameter info>)
    00000000`68016b50 dbghelp!omap (<no parameter info>)
    00000000`68016e10 dbghelp!homedir (<no parameter info>)
    00000000`68017030 dbghelp!srcfiles (<no parameter info>)
    00000000`68018a90 dbghelp!dh (<no parameter info>)
    
    0:000> !itoldyouso
    !IToldYouSo <module> [symbol]
    
    !IToldYouSo tests the validity of a module against a symbol file.
    The module can be specified by either its name or base address.
    If a symbol file is not specified, then the loaded symbol is tested.
    Otherwise, if a pdb or dbg symbol file path is specified, it is tested
    against the loaded module.
    

    Most of the meta commands are implemented in dbgeng.dll, a few are implemented inside specific debuggers (e.g. .browse and .wtitle are WinDBG specific meta commands). Some meta commands are only available for certain modes/targets/platforms, for example, .kdfiles is only available for live kernel debugging on x86-based and Itanium-based processors.

    Some meta commands might be undocumented (e.g. .aliascmds and .sxcmds) or documented separately (e.g. .perfer_dml is documented in dml.doc which can be found under the WinDBG installation folder, .jdinfo is only documented in .help), there is no centralized place to get all these commands.

    The following command would dump all the dot commands exported by dbgeng.dll (dbgeng!DotCommand is an exception, which is not a meta command):

    0:000> x dbgeng!Dot*
    00000000`517aba70 dbgeng!DotRestart = <no type information>
    00000000`51801c70 dbgeng!DotExtMatch = <no type information>
    00000000`517a9ac0 dbgeng!DotLogAppend = <no type information>
    00000000`517ae600 dbgeng!DotTTime = <no type information>
    00000000`517a3d70 dbgeng!DotEchoTimestamps = <no type information>
    00000000`517aacd0 dbgeng!DotPromptAllow = <no type information>
    00000000`517a53e0 dbgeng!DotEventStr = <no type information>
    00000000`517aa0b0 dbgeng!DotOCommand = <no type information>
    00000000`5179de00 dbgeng!DotPageIn = <no type information>
    00000000`517a09f0 dbgeng!DotCache = <no type information>
    00000000`517aaa80 dbgeng!DotPrintf = <no type information>
    00000000`517a60a0 dbgeng!DotFnRet = <no type information>
    00000000`5179d3c0 dbgeng!DotThread = <no type information>
    00000000`517a9a40 dbgeng!DotLocale = <no type information>
    00000000`517a46f0 dbgeng!DotEndSrv = <no type information>
    00000000`517a33b0 dbgeng!DotDumpOff = <no type information>
    00000000`517a5900 dbgeng!DotExr = <no type information>
    00000000`517a2880 dbgeng!DotCxr = <no type information>
    00000000`517a4f50 dbgeng!DotEvents = <no type information>
    00000000`5179fb30 dbgeng!DotApplyDbp = <no type information>
    00000000`517ae350 dbgeng!DotTrap = <no type information>
    00000000`517a98c0 dbgeng!DotKFrames = <no type information>
    00000000`517aca70 dbgeng!DotSleep = <no type information>
    00000000`5179f9e0 dbgeng!DotAllowImageMapping = <no type information>
    00000000`517a3e20 dbgeng!DotEcxr = <no type information>
    00000000`517a5e30 dbgeng!DotFixImports = <no type information>
    00000000`517aa3b0 dbgeng!DotOpenDump = <no type information>
    00000000`517a0500 dbgeng!DotBpSync = <no type information>
    00000000`5179d9b0 dbgeng!DotProcess = <no type information>
    00000000`517ac280 dbgeng!DotServers = <no type information>
    00000000`5179dd80 dbgeng!DotKernelKill = <no type information>
    00000000`517a2ea0 dbgeng!DotDmlStart = <no type information>
    00000000`517ab890 dbgeng!DotRecordBranches = <no type information>
    00000000`517a06c0 dbgeng!DotBugCheck = <no type information>
    00000000`517a2bd0 dbgeng!DotDebugSwWow = <no type information>
    00000000`517a1510 dbgeng!DotContext = <no type information>
    00000000`517a4740 dbgeng!DotEnumTag = <no type information>
    00000000`517a8b40 dbgeng!DotIf = <no type information>
    00000000`5179db50 dbgeng!DotFiber = <no type information>
    00000000`517ab810 dbgeng!DotReboot = <no type information>
    00000000`517a03a0 dbgeng!DotBpCmds = <no type information>
    00000000`5183b550 dbgeng!DotDmlFlow = <no type information>
    00000000`517ace20 dbgeng!DotSrcPath = <no type information>
    00000000`517a3380 dbgeng!DotDumpDebug = <no type information>
    00000000`517adf50 dbgeng!DotProcessInfo = <no type information>
    00000000`5179f950 dbgeng!DotAllowBpBaConvert = <no type information>
    00000000`517ab960 dbgeng!DotReCxr = <no type information>
    00000000`518568c0 dbgeng!DotPCmd = <no type information>
    00000000`517aba10 dbgeng!DotReload = <no type information>
    00000000`5179fe60 dbgeng!DotAttach = <no type information>
    00000000`517aa070 dbgeng!DotNoVersion = <no type information>
    00000000`518eb230 dbgeng!DotLines = <no type information>
    00000000`517a2dd0 dbgeng!DotDmlFile = <no type information>
    00000000`517a7740 dbgeng!DotFormats = <no type information>
    00000000`517accb0 dbgeng!DotSrcNoisy = <no type information>
    00000000`517a3170 dbgeng!DotDumpCab = <no type information>
    00000000`517a8e00 dbgeng!DotIgnoreWowKdContext = <no type information>
    00000000`517ad880 dbgeng!DotTList = <no type information>
    00000000`517ab3c0 dbgeng!DotReadMem = <no type information>
    00000000`517acb20 dbgeng!DotSrcFix = <no type information>
    00000000`517a5670 dbgeng!DotExPtr = <no type information>
    00000000`517a6540 dbgeng!DotForceBranchTrace = <no type information>
    00000000`517a3540 dbgeng!DotDumpPOff = <no type information>
    00000000`517aec40 dbgeng!DotWake = <no type information>
    00000000`517a0370 dbgeng!DotBlock = <no type information>
    00000000`517abcb0 dbgeng!DotSendFile = <no type information>
    00000000`517a62d0 dbgeng!DotFor = <no type information>
    00000000`517a3140 dbgeng!DotDrivers = <no type information>
    00000000`517a9b10 dbgeng!DotLogFile = <no type information>
    00000000`517a37d0 dbgeng!DotDvAlloc = <no type information>
    00000000`517ab0a0 dbgeng!DotPop = <no type information>
    00000000`517d9c20 dbgeng!DotDump = <no type information>
    00000000`517a7c30 dbgeng!DotFrame = <no type information>
    00000000`517af540 dbgeng!DotHelp = <no type information>
    00000000`517a9be0 dbgeng!DotLogOpen = <no type information>
    00000000`517af8b0 dbgeng!DotCommand = <no type information>
    00000000`517a9c00 dbgeng!DotNetSyms = <no type information>
    00000000`517ace80 dbgeng!DotStepFilter = <no type information>
    00000000`517a1660 dbgeng!DotContinue = <no type information>
    00000000`517a05d0 dbgeng!DotBreakin = <no type information>
    00000000`517a4170 dbgeng!DotEnableLongStatus = <no type information>
    00000000`517a6680 dbgeng!DotForceSystemInit = <no type information>
    00000000`517a5f90 dbgeng!DotFnEnt = <no type information>
    00000000`517a0e70 dbgeng!DotCatch = <no type information>
    00000000`517a9840 dbgeng!DotKdFiles = <no type information>
    00000000`517a0ea0 dbgeng!DotChain = <no type information>
    00000000`517a2380 dbgeng!DotCrash = <no type information>
    00000000`5183a420 dbgeng!DotAsm = <no type information>
    00000000`517ac900 dbgeng!DotShowSymFailures = <no type information>
    00000000`517ac1b0 dbgeng!DotServer = <no type information>
    00000000`517ac800 dbgeng!DotShowReadFailures = <no type information>
    00000000`517a6810 dbgeng!DotForEach = <no type information>
    00000000`517e9d60 dbgeng!DotEventLog = <no type information>
    00000000`517ad210 dbgeng!DotSymOpt = <no type information>
    00000000`517a6620 dbgeng!DotForceRadixOutput = <no type information>
    00000000`517a7160 dbgeng!DotFpo = <no type information>
    00000000`517a3fa0 dbgeng!DotEffMach = <no type information>
    00000000`517a7d80 dbgeng!DotFrameRel = <no type information>
    00000000`517ae430 dbgeng!DotTss = <no type information>
    00000000`51725a30 dbgeng!DotAliasCmds = <no type information>
    00000000`517a41d0 dbgeng!DotEnableUnicode = <no type information>
    00000000`517aa030 dbgeng!DotNoShell = <no type information>
    00000000`517aa540 dbgeng!DotOutMask = <no type information>
    00000000`517a9880 dbgeng!DotKdTrans = <no type information>
    00000000`517a3d20 dbgeng!DotEchoTime = <no type information>
    00000000`517aa800 dbgeng!DotPreferDml = <no type information>
    00000000`517a9a10 dbgeng!DotLeave = <no type information>
    00000000`517a7ec0 dbgeng!DotFrameEbpFix = <no type information>
    00000000`517a05a0 dbgeng!DotBreak = <no type information>
    00000000`517a3a30 dbgeng!DotDvFree = <no type information>
    00000000`517a54a0 dbgeng!DotExpr = <no type information>
    00000000`517a30b0 dbgeng!DotDbgDbg = <no type information>
    00000000`517a5930 dbgeng!DotExtCmds = <no type information>
    00000000`517aed10 dbgeng!DotWriteMem = <no type information>
    00000000`517ad040 dbgeng!DotSxCmds = <no type information>
    00000000`517a59e0 dbgeng!DotExtPath = <no type information>
    00000000`517aafc0 dbgeng!DotPush = <no type information>
    00000000`517ad0e0 dbgeng!DotSymFix = <no type information>
    00000000`517a2420 dbgeng!DotCreate = <no type information>
    00000000`517ac2b0 dbgeng!DotShell = <no type information>
    00000000`517a3010 dbgeng!DotDo = <no type information>
    00000000`517aec90 dbgeng!DotWhile = <no type information>
    00000000`517a9d40 dbgeng!DotNetUse = <no type information>
    00000000`517ae170 dbgeng!DotTimeZone = <no type information>
    00000000`517a0f90 dbgeng!DotChildDbg = <no type information>
    00000000`517a1130 dbgeng!DotClients = <no type information>
    00000000`517a4680 dbgeng!DotEndPSrv = <no type information>
    00000000`517a8910 dbgeng!DotHoldMem = <no type information>
    00000000`517a8ea0 dbgeng!DotImgScan = <no type information>
    00000000`517ad3e0 dbgeng!DotTListFromNtQuerySystemInformation = <no type information>
    00000000`517a4b40 dbgeng!DotEventCode = <no type information>
    00000000`517a3630 dbgeng!DotDumpAddRgn = <no type information>
    00000000`517a9790 dbgeng!DotJdInfo = <no type information>
    00000000`517ad330 dbgeng!DotSymPath = <no type information>
    00000000`517ade40 dbgeng!DotTime = <no type information>
    00000000`517a5430 dbgeng!DotExePath = <no type information>
    00000000`517a8d60 dbgeng!DotIgnoreMissingPages = <no type information>
    00000000`517aeaa0 dbgeng!DotTxtSym = <no type information>
    00000000`517a3c70 dbgeng!DotEchoCpuNum = <no type information>
    00000000`517a3c20 dbgeng!DotEcho = <no type information>
    00000000`517aa010 dbgeng!DotNoEngErr = <no type information>
    00000000`517aa7d0 dbgeng!DotPCache = <no type information>
    00000000`5179f8b0 dbgeng!DotAllowExecCmds = <no type information>
    00000000`517a1ab0 dbgeng!DotCorDll = <no type information>
    00000000`517a26e0 dbgeng!DotCreateDir = <no type information>
    00000000`517a1860 dbgeng!DotCopySym = <no type information>
    00000000`517a9af0 dbgeng!DotLogClose = <no type information>
    00000000`517a4230 dbgeng!DotTypeOpt = <no type information>
    00000000`517a0b70 dbgeng!DotCall = <no type information>
    00000000`517abc10 dbgeng!DotSecure = <no type information>
    00000000`517aa230 dbgeng!DotOFilter = <no type information>
    00000000`517a11f0 dbgeng!DotCloseHandle = <no type information>
    00000000`517ab180 dbgeng!DotQuitLock = <no type information>
    00000000`517a9990 dbgeng!DotLastEvent = <no type information>
    00000000`517a2220 dbgeng!DotCorStack = <no type information>
    

    Also, you might have noticed that .elif and .else donnot have their corresponding exports like .if does, they just look like meta commands (the document called them Command Tokens). To understand how the command line got parsed and executed (like a simplified version of compiler front end), I would recommend debugging the debugger by setting a breakpoint on the Win32 Beep function (kernel32!Beep or KERNELBASE!Beep) and use .beep, below is what I got on my Windows XP box:

    0:000> k
    kernel32!Beep
    windbg!DirectCommand+0x12a
    windbg!CmdExecuteCmd+0x90
    windbg!WinCommand::OnNotify+0x467
    windbg!WinBase::BaseProc+0xc91
    USER32!InternalCallWinProc+0x28
    USER32!UserCallWinProcCheckWow+0x150
    USER32!SendMessageWorker+0x4a5
    USER32!SendMessageW+0x7f
    MSFTEDIT!CW32System::SendMessage+0x3d
    MSFTEDIT!CTxtWinHost::TxNotify+0x97
    MSFTEDIT!RichEditWndProc+0x19b
    USER32!InternalCallWinProc+0x28
    USER32!UserCallWinProcCheckWow+0x150
    USER32!DispatchMessageWorker+0x306
    USER32!DispatchMessageW+0xf
    windbg!ProcessNonDlgMessage+0x2a2
    windbg!ProcessPendingMessages+0x64
    windbg!wmain+0x24a
    windbg!_initterm_e+0x163
    kernel32!BaseProcessStart+0x23
    
    0:000> k
    kernel32!Beep
    ntsd!UiCommand+0x287
    ntsd!MainLoop+0x48f
    ntsd!main+0x232
    ntsd!_initterm_e+0x163
    kernel32!BaseProcessStart+0x23
    
    0:000> k
    kernel32!Beep
    cdb!UiCommand+0x287
    cdb!MainLoop+0x48f
    cdb!main+0x232
    cdb!_initterm_e+0x163
    kernel32!BaseProcessStart+0x23
    
    0:000> k
    kernel32!Beep
    kd!UiCommand+0x287
    kd!MainLoop+0x48f
    kd!main+0x1ed
    kd!_initterm_e+0x163
    kernel32!BaseProcessStart+0x23
    

    In order to answer why Visual Studio lacks support for child process debugging, you need to know how the user mode native debugger works, especially how to write a debug event loop. I have an example in Data Breakpoints, and I've purposely left a note in the "A few things to mention" section #4. You can use these hints and debug into the Visual Studio Debugger.

    Regarding the symbol for remote debugging, you will need some background of metadata (ECMA-335 Partition 2) and PDB, also a bit of ICorDebug and ICLRData (mscordacwks!CLRDataCreateInstance). Launch procmon.exe and see who is consuming the symbol files.

    What if a WinDBG command fails to do the job, or is not working as expected? There is no magic, just grab a debugger and debug it, in fact the debugger team has presented you a gift called .dbgdbg to make things a little easier!

    0:000> uf dbgeng!DotReadMem
    dbgeng!DotReadMem:
    0213fc90 8bff            mov     edi,edi
    0213fc92 55              push    ebp
    0213fc93 8bec            mov     ebp,esp
    0213fc95 81ec64080000    sub     esp,864h
    0213fc9b a10c513302      mov     eax,dword ptr [dbgeng!__security_cookie (0233510c)]
    0213fca0 33c5            xor     eax,ebp
    0213fca2 8945fc          mov     dword ptr [ebp-4],eax
    0213fca5 833d4450360200  cmp     dword ptr [dbgeng!g_Process (02365044)],0
    0213fcac 750c            jne     dbgeng!DotReadMem+0x2a (0213fcba)
    
    dbgeng!DotReadMem+0x1e:
    0213fcae 6a00            push    0
    0213fcb0 6818100000      push    1018h
    0213fcb5 e8b6ba1400      call    dbgeng!ReportError (0228b770)
    
    dbgeng!DotReadMem+0x2a:
    0213fcba a1004d3302      mov     eax,dword ptr [dbgeng!g_SymOptions (02334d00)]
    0213fcbf 2500000400      and     eax,40000h
    0213fcc4 740c            je      dbgeng!DotReadMem+0x42 (0213fcd2)
    
    dbgeng!DotReadMem+0x36:
    0213fcc6 6a00            push    0
    0213fcc8 682a100000      push    102Ah
    0213fccd e89eba1400      call    dbgeng!ReportError (0228b770)
    
    dbgeng!DotReadMem+0x42:
    0213fcd2 c785c0f7ffff00000000 mov dword ptr [ebp-840h],0
    0213fcdc 8d8dbcf7ffff    lea     ecx,[ebp-844h]
    0213fce2 51              push    ecx
    0213fce3 6a0d            push    0Dh
    0213fce5 e876c41400      call    dbgeng!StringValue (0228c160)
    0213fcea 8985c4f7ffff    mov     dword ptr [ebp-83Ch],eax
    0213fcf0 8b154c1a3502    mov     edx,dword ptr [dbgeng!g_CurCmd (02351a4c)]
    0213fcf6 8995e4f7ffff    mov     dword ptr [ebp-81Ch],edx
    0213fcfc a14c1a3502      mov     eax,dword ptr [dbgeng!g_CurCmd (02351a4c)]
    0213fd01 668b8dbcf7ffff  mov     cx,word ptr [ebp-844h]
    0213fd08 668908          mov     word ptr [eax],cx
    0213fd0b c785e8f7ffff00000100 mov dword ptr [ebp-818h],10000h
    0213fd15 c785ecf7ffff00000000 mov dword ptr [ebp-814h],0
    0213fd1f 6800001000      push    100000h
    0213fd24 6a03            push    3
    0213fd26 6a01            push    1
    0213fd28 8d95e8f7ffff    lea     edx,[ebp-818h]
    0213fd2e 52              push    edx
    0213fd2f 8d85c8f7ffff    lea     eax,[ebp-838h]
    0213fd35 50              push    eax
    0213fd36 e8053b0400      call    dbgeng!GetRange (02183840)
    0213fd3b 33c9            xor     ecx,ecx
    0213fd3d 8b95e4f7ffff    mov     edx,dword ptr [ebp-81Ch]
    0213fd43 66890a          mov     word ptr [edx],cx
    0213fd46 6a00            push    0
    0213fd48 6880000000      push    80h
    0213fd4d 6a03            push    3
    0213fd4f 6a00            push    0
    0213fd51 6a00            push    0
    0213fd53 6800000080      push    80000000h
    0213fd58 8b85c4f7ffff    mov     eax,dword ptr [ebp-83Ch]
    0213fd5e 50              push    eax
    0213fd5f ff158c743302    call    dword ptr [dbgeng!kernel32_CreateFileW_Ptr (0233748c)]
    0213fd65 8985f4f7ffff    mov     dword ptr [ebp-80Ch],eax
    0213fd6b 83bdf4f7ffffff  cmp     dword ptr [ebp-80Ch],0FFFFFFFFh
    0213fd72 7512            jne     dbgeng!DotReadMem+0xf6 (0213fd86)
    

    As you can see, the .readmem command makes use of kernel32!CreateFileW with dwDesiredAccess = GENERIC_READ and dwShareMode = 0 (instead of FILE_SHARE_READ), that would explain why you got an error "Unable to open file" in some cases. A quick debugging showed CreateFileW failed and last error value is ERROR_SHARING_VIOLATION.

    The documented syntax for .readmem is .readmem FileName Range, while there is no notes for how big the Range value can go, I'll leave this as a homework for you, cheers.

  • Rubato and Chord

    Early Debugging

    • 0 Comments

    Early debugging is a wide topic, on a Windows PC it might be:

    Application Startup

    As we have demonstrated in the user mode debug event loop, when an application was launched from a debugger, the first debug event is CREATE_PROCESS_DEBUG_EVENT. Process creation event is the earliest point a user mode debugger could even reach to.

    Windows debuggers by default would break at ntdll!LdrpDoDebuggerBreak, but we can alter this behavior:

    cdb.exe -xe cpr -xe ld notepad.exe

    CommandLine: notepad.exe
    
    ModLoad: 01000000 01014000   notepad.exe
    
    0:000> lm
    start    end        module name
    01000000 01014000   notepad    (deferred)
    
    0:000> !teb
    TEB at 7ffdf000
    error InitTypeRead( TEB )...
    

    As you can see, debugger extension complains since we are too early. However there are always workarounds as we discussed in Undocumented WinDBG:

    0:000> .imgscan; * Where is Mark Zbikowski?
    MZ at 01000000, prot 00000002, type 01000000 - size 14000
      Name: notepad.exe
    MZ at 7c900000, prot 00000002, type 01000000 - size b2000
      Name: ntdll.dll
    
    0:000> .reload /s /f ntdll.dll=7c900000
    
    0:000> lm
    start    end        module name
    01000000 01014000   notepad    (deferred)
    7c900000 7c9b2000   ntdll      (pdb symbols)
    
    0:000> !teb
    TEB at 7ffdf000
        ExceptionList:        ffffffff
        StackBase:            00080000
        StackLimit:           0006f000
        SubSystemTib:         00000000
        FiberData:            00001e00
        ArbitraryUserPointer: 00000000
        Self:                 7ffdf000
        EnvironmentPointer:   00000000
        ClientId:             000007d4 . 000005b4
        RpcHandle:            00000000
        Tls Storage:          00000000
        PEB Address:          7ffd8000
        LastErrorValue:       0
        LastStatusValue:      0
        Count Owned Locks:    0
        HardErrorMode:        0
    

    If the application is launched by another process, IFEO might help, but always keep in mind there can be side effects.

    Service Startup

    Most of the knowledge about application debugging applies to services, since they are both user mode processes. The only difference is that service can share a single hosting process (e.g. svchost.exe), and would normally start in a different session.

    An excellent article about service debugging can be found at:

    Windows Setup, OS Loader, CSRSS and WinLogon

    The document shipped with Debugging Tools for Windows has some brief introduction.

    MSDN also described these debugging tasks in Specialized Debugging Techniques.

    POST and MBR

    Most of these are real mode code (although MBR might switch CPU to protected mode) dealing with the low level hardware. Not many people are still working on the old memory models (e.g. TINY, SMALL, COMPACT, MEDIUM, LARGE and HUGE) and the A20 line (do you remember Tim Paterson and his debug program?).

    MBR is relatively small, and can be simply debugged using a software emulator.

    I have never worked on POST, but I think people would use software emulators in combination with ICE (In-circuit emulator).

    WinDBG has limited support for real mode debugging.

    (to be continued...)

  • Rubato and Chord

    Use Windows Debuggers for Non-Debugging Tasks

    • 2 Comments

    Many people who has been using Emacs for decades were shocked when they heard that Emacs is actually a text editor instead of an operating system.

    - vi advocator

    Sharing a similar spirit as Emacs, Windows Debuggers are also super good at non-debugging tasks.

    Calculator

    The builtin expression evaluator of Windows Debuggers can be used as a handy calculator:

    0:000> ?? 1+2+3
    int 0n6
    
    0:000> .formats 0x00905a4d
    Evaluate expression:
      Hex:     00000000`00905a4d
      Decimal: 9460301
      Octal:   0000000000000044055115
      Binary:  00000000 00000000 00000000 00000000 00000000 10010000 01011010 01001101
      Chars:   ......ZM
      Time:    Mon Apr 20 19:51:41 1970
      Float:   low 1.32567e-038 high 0
      Double:  4.67401e-317

    Process Manager

    I try not to use the term Task Manager, since the name is already occupied by taskmgr.exe, and we have nothing to do with tasks (taskmgr also has nothing to do with tasks).

    0:000> .tlist -v windbg*

    Shell

    0:000> .shell tlist -t
    0:000> .shell dir
    0:000> !!dir

    Examine Export Table

    cdb.exe -y . -z ntdll.dll -c "x ntdll!*; q"

    Binary Editor

    The basic idea is to load a portion of file data into the address space of a debuggee, perform some inspection or modification, then write back to the file. WinDBG even has a Memory Window, which makes it a perfect GUI Hex Editor!

    To examine the file information such like name, permission and size, use the .shell command.

    0:000> .shell dir *.netmodule 
    
    10/23/2011  12:12 PM             2,048 bar.netmodule
    10/23/2011  12:12 PM             2,048 foo.netmodule
    
    .shell: Process exited
    Press ENTER to continue
    

    We can load the file into the debugee's user mode virtual address space, given that the pages are commited.

    0:000> .dvalloc 0n2048
    Allocated 1000 bytes starting at 00020000
    0:000> .readmem foo.netmodule 20000 L0n2048
    Reading 800 bytes.
    0:000> dt ntdll!_IMAGE_DOS_HEADER 20000
       +0x000 e_magic          : 0x5a4d
       +0x002 e_cblp           : 0x90
       +0x004 e_cp             : 3
       +0x006 e_crlc           : 0
       +0x008 e_cparhdr        : 4
       +0x00a e_minalloc       : 0
       +0x00c e_maxalloc       : 0xffff
       +0x00e e_ss             : 0
       +0x010 e_sp             : 0xb8
       +0x012 e_csum           : 0
       +0x014 e_ip             : 0
       +0x016 e_cs             : 0
       +0x018 e_lfarlc         : 0x40
       +0x01a e_ovno           : 0
       +0x01c e_res            : [4] 0
       +0x024 e_oemid          : 0
       +0x026 e_oeminfo        : 0
       +0x028 e_res2           : [10] 0
       +0x03c e_lfanew         : 0n128
    0:000> dt -r ntdll!_IMAGE_NT_HEADERS 20000+0n128
       +0x000 Signature        : 0x4550
       +0x004 FileHeader       : _IMAGE_FILE_HEADER
          +0x000 Machine          : 0x14c
          +0x002 NumberOfSections : 2
          +0x004 TimeDateStamp    : 0x4ea39447
          +0x008 PointerToSymbolTable : 0
          +0x00c NumberOfSymbols  : 0
          +0x010 SizeOfOptionalHeader : 0xe0
          +0x012 Characteristics  : 0x2102
       +0x018 OptionalHeader   : _IMAGE_OPTIONAL_HEADER
          +0x000 Magic            : 0x10b
          +0x002 MajorLinkerVersion : 0x8 ''
          +0x003 MinorLinkerVersion : 0 ''
          +0x004 SizeOfCode       : 0x400
          +0x008 SizeOfInitializedData : 0x200
          +0x00c SizeOfUninitializedData : 0
          +0x010 AddressOfEntryPoint : 0x22de
          +0x014 BaseOfCode       : 0x2000
          +0x018 BaseOfData       : 0x4000
          +0x01c ImageBase        : 0x400000
          +0x020 SectionAlignment : 0x2000
          +0x024 FileAlignment    : 0x200
          +0x028 MajorOperatingSystemVersion : 4
          +0x02a MinorOperatingSystemVersion : 0
          +0x02c MajorImageVersion : 0
          +0x02e MinorImageVersion : 0
          +0x030 MajorSubsystemVersion : 4
          +0x032 MinorSubsystemVersion : 0
          +0x034 Win32VersionValue : 0
          +0x038 SizeOfImage      : 0x6000
          +0x03c SizeOfHeaders    : 0x200
          +0x040 CheckSum         : 0
          +0x044 Subsystem        : 3
          +0x046 DllCharacteristics : 0x8540
          +0x048 SizeOfStackReserve : 0x100000
          +0x04c SizeOfStackCommit : 0x1000
          +0x050 SizeOfHeapReserve : 0x100000
          +0x054 SizeOfHeapCommit : 0x1000
          +0x058 LoaderFlags      : 0
          +0x05c NumberOfRvaAndSizes : 0x10
          +0x060 DataDirectory    : [16] _IMAGE_DATA_DIRECTORY
             +0x000 VirtualAddress   : 0
             +0x004 Size             : 0
    0:000> !dh 20000
    0:000> .writemem foo.netmodule 20000 L0n2048
    0:000> .dvfree 20000 0

    Be cautious while using .dvfree, the debugger simply backed this by VirtualFreeEx:

    kernel32!VirtualFreeEx
    dbgeng!LiveUserDebugServices::FreeVirtual+0x1e
    dbgeng!LiveUserTargetInfo::FreeVirtual+0x47
    dbgeng!DotDvFree+0xcb
    dbgeng!DotCommand+0x3f
    dbgeng!ProcessCommands+0x4e1
    dbgeng!ProcessCommandsAndCatch+0x49
    dbgeng!Execute+0x2b9
    dbgeng!DebugClient::ExecuteWide+0x6a
    windbg!ProcessCommand+0x156
    windbg!ProcessEngineCommands+0xb2
    windbg!EngineLoop+0x366
    kernel32!BaseThreadStart+0x37
    

    This means .dvfree can be used to free any block of virtual memory owned by the target process.

    When .dvfree is used without the /d option, it would use MEM_RELEASE instead of MEM_DECOMMIT (this can be verified with .dbgdbg), and you have to make sure BaseAddress is the value returned by !address and Size is always zero.

Page 1 of 1 (3 items)