Is there a way to predict/calculate where in a module's memory a function will be located?
*Sigh* The first thing you'll tell me is: you have to have symbols for stuff to work well. I know. (Oh! Do I know!) It isn't an option for me, so I'm looking to make the most of what I have. I'm not trying to get the values of local variables or anything (which would require intimate knowledge of compiler optimizations, etc.). I just would like to know what DLL function we were in.
I have a memory dump from a crashed process. The process loads numerous DLL's, each of which have some exported functions. One thread in the process has some items on the call stack "within" this DLL. The Visual Studio debugger reports them as (the names are changed to protect the innocent):
msvcr120.dll!abort() Line 90
msvcr120.dll!_XcptFilter(unsigned long xcptnum, _EXCEPTION_POINTERS * pxcptinfoptrs) Line 366
MyProcess.exe!016c9a9a()
[Frames below may be incorrect and/or missing, no symbols loaded for Mach4GUI.exe]
[External Code]
MyProcess.exe!016ca390()
[External Code]
ExtLib01.dll!6eb1f75c()
ExtLib01.dll!6eb95991()
ExtLib01.dll!6e658979()
ExtLib01.dll!6e653bab()
ExtLib01.dll!6e66d5dd()
[External Code]
The DLL wasn't built for debugging, and I don't have a .PDB or other source of symbols.
Visual Studio reports this about the DLL module:
Name Path Address
---- ---- -------
ExtLib01.dll C:\MyProj\ExtLib01.dll 6E5E0000-6FE8800
The DLL, of course, has some exported functions (reported by dumpbin /exports ExtLib01.dll
), like:
ordinal hint RVA name
1 0 0009CE30 LibFunc1@16
2 1 0009CDF0 LibFunc2@4
3 2 0009CE60 LibFunc3@4
...
482 1E1 0010FB40 LibFunc482
483 1E2 0010FBD0 LibFunc483
484 1E3 0010FAC0 LibFunc484
Not knowing any better, it seems like there might be enough information here to figure out what functions are in the call stack (if the return address is "within" one of the DLL functions).
Some simple arithmetic on the first item in the call stack, 0x6eb1f75c-0x6E5E0000
, yields an offset into the ExtLib01.dll "module" in memory: +0x0053f75c
, but this doesn't correspond at all to the relative virtual addresses listed in the DLL exports.
So, Visual Studio tells me nothing but the module name and an address (within that module) in the call stack. One might assume that this is because more information isn't available. My experience with Microsoft products suggests that they just might not have implemented such a feature—though in the case of their flagship development tool, surely they would try hard to make it useful.
More confusingly, WinDbg tells me different things about about the call stack for the same thread.
# ChildEBP RetAddr
00 0022cdb8 7740171a ntdll!NtWaitForMultipleObjects+0x15
01 0022ce54 76c519fc KERNELBASE!WaitForMultipleObjectsEx+0x100
02 0022ce9c 76c541d8 kernel32!WaitForMultipleObjectsExImplementation+0xe0
03 0022ceb8 76c780bc kernel32!WaitForMultipleObjects+0x18
04 0022cf24 76c77f7b kernel32!WerpReportFaultInternal+0x186
05 0022cf38 76c77870 kernel32!WerpReportFault+0x70
06 0022cf48 76c777ef kernel32!BasepReportFault+0x20
07 0022cfd4 74fb4820 kernel32!UnhandledExceptionFilter+0x1af
08 0022cfe0 74fb4611 msvcr120!__crtUnhandledException+0x14 [f:\dd\vctools\crt\crtw32\misc\winapisupp.c @ 259]
09 0022d318 74fb7676 msvcr120!_call_reportfault+0xfe [f:\dd\vctools\crt\crtw32\misc\invarg.c @ 201]
0a 0022d328 74fb4e38 msvcr120!abort+0x38 [f:\dd\vctools\crt\crtw32\misc\abort.c @ 90]
0b 0022d340 016c9a9a msvcr120!_XcptFilter+0x14b [f:\dd\vctools\crt\crtw32\misc\winxfltr.c @ 366]
WARNING: Stack unwind information not available. Following frames may be wrong.
0c 0022f8c8 76c5336a MyProcess!SomeSymbol+0xa0d71a
0d 0022f8d4 77b39902 kernel32!BaseThreadInitThunk+0xe
0e 0022f914 77b398d5 ntdll!__RtlUserThreadStart+0x70
0f 0022f92c 00000000 ntdll!_RtlUserThreadStart+0x1b
I can perhaps understand some of the items at the top of the list (everything above UnhandledExceptionFilter
, where Windows Error Reporting is doing its thing, capturing the memory dump, etc.), but why the discrepancy between the two call stacks? I see that both report "Following frames may be wrong." Is this information completely unreliable? What can be known or discerned from the information available?
What can be discerned about the items in the call stack from the information available?
Is it possible to know where a function from a loaded DLL is located in memory of a running process?
I'm willing to use WinDbg if it'll help. I'm willing to use PowerShell within Visual Studio's NuGet console. I'm not unwilling to do some work, but I have no real idea if it's even possible to know something these DLL functions.
Note: I realize the answer may very well be, "Nope. It's impossible." I'm looking for a good answer with an explanation (or references to some sort of authoritative source with an explanation) as to how the DLL's are loaded and its functions called and why a memory dump doesn't contain enough information to figure out where any given function "starts" in memory.