Question

Background Information

For saving out crash dumps, I have a script passed to cdb.exe in the Debugger value of the AeDebug registry key:

C:\progra~1\debugg~1\cdb.exe -p %ld -e %ld -g -y SRV*c:\mss*http://msdl.microsoft.com/download/symbols -c "$<d:\tgticker\Dumps\RDFD.cdbscript"

Here is the first portion of the script:

as /c CrashFirstModule .printf "%mu", @@c++((*(ntdll!_LDR_DATA_TABLE_ENTRY**)&@$peb->Ldr->InLoadOrderModuleList.Flink)->BaseDllName.Buffer) 

.logopen /t d:\tgticker\dumps\${CrashFirstModule}_process.log

* (...)

The Problem

With symbols, this works exactly as I would like, I get log files with sensible names like:

  • LHCBDRDT.exe_process_147c_2009-01-06_23-10-05-371.log

However, if symbols are not available, I get a log file name like this:

  • ${CrashFirstModule}_process_17a8_2009-01-06_23-10-01-124.log

This is because the alias command has failed to set the alias. The alias command is one that I harvested from DumpAnalysis.org. This command pulls the name out of the PEB Header for the image, using ntdll.dll. Without symbols for the OS, it doesn't know where to find the function it is calling from ntdll.dll.

The Question

Does anyone know or have a command to get the name of the image as an alias for use in filenames which would still work in these situations?

Was it helpful?

Solution

So here I am years later with an answer.

The Answer

In the absence of symbol files, this is the best way I've found to get the name of the executable that crashed so that it can be used in a filename for writing a log file or crash dump from a script:

aS ${/v:CrashFirstModule} "UnknownModule"
.foreach /pS b /ps b (name {.imgscan}) { .if($spat("${name}","*.exe") !=0){aS ${/v:CrashFirstModule} "${name}"; .break} }

After these two lines, CrashFirstModule will be aliased to either "UnknownModule" or the name of the executable. This only works if the executable ends in ".exe", but that seems reasonable to me, and works fine in the case where I'm using it. You could add another .if to handle other ending if you need to support things like ".com".

The Answer: Explained

.imgscan

.imgscan gives a list of executable modules, which will include .exe, .dll, .drv etc. This is the starting point for finding the executable name.

0:000> .imgscan
MZ at 01000000, prot 00000002, type 01000000 - size 14000
  Name: notepad.exe
MZ at 73070000, prot 00000002, type 01000000 - size 27000
  Name: WINSPOOL.DRV
MZ at 762b0000, prot 00000002, type 01000000 - size 49000
  Name: comdlg32.dll
MZ at 76f50000, prot 00000002, type 01000000 - size 13000
  Name: Secur32.dll
MZ at 77380000, prot 00000002, type 01000000 - size 91000
  Name: USER32.dll
MZ at 77420000, prot 00000002, type 01000000 - size 103000
  Name: COMCTL32.dll
MZ at 77ba0000, prot 00000002, type 01000000 - size 5a000
  Name: msvcrt.dll
MZ at 77c00000, prot 00000002, type 01000000 - size 48000
  Name: GDI32.dll
MZ at 77c50000, prot 00000002, type 01000000 - size a0000
  Name: RPCRT4.dll
MZ at 77e40000, prot 00000002, type 01000000 - size 102000
  Name: KERNEL32.dll
MZ at 7c800000, prot 00000002, type 01000000 - size c3000
  Name: ntdll.dll
MZ at 7c8d0000, prot 00000002, type 01000000 - size 7ff000
  Name: SHELL32.dll
MZ at 7d180000, prot 00000002, type 01000000 - size 52000
  Name: SHLWAPI.dll
MZ at 7d1e0000, prot 00000002, type 01000000 - size 9c000
  Name: ADVAPI32.dll

.foreach

.foreach is used to walk the list of images. /pS specifies how far into the list the first value is. /ps specifies the distance between values. (b = 11 in hex) This is necessary as .foreach will separate on spaces. With these arguments, the list becomes:

0:000> .foreach /pS b /ps b (name {.imgscan}) { .echo name }
notepad.exe
WINSPOOL.DRV
comdlg32.dll
Secur32.dll
USER32.dll
COMCTL32.dll
msvcrt.dll
GDI32.dll
RPCRT4.dll
KERNEL32.dll
ntdll.dll
SHELL32.dll
SHLWAPI.dll
ADVAPI32.dll

$spat

$spat is the MASM wildcard string match function. It will match the first argument against the pattern in the second argument. It is not case sensitive, so this will match NOTEPAD.EXE as well as NotePad.eXe, etc.

.if($spat("${name}","*.exe") !=0) {.echo "found it!"}

${}

${} is the alias interpreter. You embed ${<alias name>} wherever you want the value of your alias written in a string. if you are using the alias in a command, you can just use it, so .echo CrashFirstmodule would echo out notepad.exe. In those cases where you actually mean the name of the alias, you can specify it as ${/v:<alias name>} which will just resolve to the alias name. This expansion prevention is necessary when reassigning an alias. aS CrashFirstModule "${name}" would have resulted in setting the alias UnknownModule to notepad.exe, as CrashFirstModule would have been expanded to its value before the command was executed.

aS

aS is one of the commands to assign aliases. aS is terminated by ; or the end of line and will strip the " from the entry. The following line will alias CrashFirstModule to UnknownModule:

aS ${/v:CrashFirstModule} "UnknownModule"

.break

.break ends the .foreach after a match is found.

End

That's all the pieces that make up the command I'm using. I hope someone else gets some benefit from this question and answer!

OTHER TIPS

why not use peb info? Below is what you need:

?? @$peb->ProcessParameters

ntdll.dll will be present in every process, so I'm guessing the problem is symbol loading.

Anyway, this should work to get rid of the line break:

.foreach(Module {lm 1m}) { aS CrashApp Module; .break }

.foreach(Module {lm 1m a $exentry}) { aS CrashApp Module }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top