0

Implementing !address feature of Windbg...

I am using VirtualQueryEx to query another Process memory and using getModuleFileName on the base addresses returned from VirtualQueryEx gives the module name. What is left are the other non-module regions of a Process. How do I determine if a file is mapped to a region, or if the region represents the stack or the heap or PEB/TEB etc.

Basically, How do I figure out if a region represents Heap, the stack or PEB. How does Windbg do it?

3 Answers3

2

One approach is to disassemble the code in the debugger extension DLL that implements !address. There is documentation within the Windbg help file on writing an extension. You could use that documentation to reverse engineer where the handler of !address is located. Then browsing through the disassembly you can see what functions it calls.

Windbg has support for debugging another instance of Windbg, specifically to debug an extension DLL. You can use this facility to better delve into the implementation of !address.

While the reverse engineering approach may be tedious, it will be more deterministic than theorizing how !address is implemented and trying out each theory.

Χpẘ
  • 3,403
  • 1
  • 13
  • 22
1

To add to @Χpẘ answer, the reverse of the command shouldn't be really hard as debugger extensions DLLs come with symbols (I already reversed one to explain the internal flag of the !heap command).

Note that it is just a quick overview, I haven't perused inside it too much.

According to the !address documentation the command is located in exts.dll library. The command itself is located in Extension::address.

There are two commands handled there, a kernel mode (KmAnalyzeAddress) and a user mode one (UmAnalyzeAddress).

Inside UmAnalyzeAddress, the code:

  • Parse the command line: UmParseCommandLine(CmdArgs &,UmFilterData &)
  • Check if the process PEB is available IsTypeAvailable(char const *,ulong *) with "${$ntdllsym}!_PEB"
  • Allocate a std::list of user mode ranges: std::list<UmRange,std::allocator<UmRange>>::list<UmRange,std::allocator<UmRange>>(void)
  • Starts a loop to gather the required information:
    • UmRangeData::GetWowState(void)
    • UmMapBuild
    • UmMapFileMappings
    • UmMapModules
    • UmMapPebs
    • UmMapTebsAndStacks
    • UmMapHeaps
    • UmMapPageHeaps
    • UmMapCLR
    • UmMapOthers

Finally the results are finally output to screen using UmPrintResults.

Each of the above function can be simplfied to basic components, e.g. UmFileMappingshas the following central code:

.text:101119E0                 push    edi             ; hFile
.text:101119E1                 push    offset LibFileName ; "psapi.dll"
.text:101119E6                 call    ds:LoadLibraryExW(x,x,x)
.text:101119EC                 mov     [ebp+hLibModule], eax
.text:101119F2                 test    eax, eax
.text:101119F4                 jz      loc_10111BC3
.text:101119FA                 push    offset ProcName ; "GetMappedFileNameW"
.text:101119FF                 push    eax             ; hModule
.text:10111A00                 mov     byte ptr [ebp+var_4], 1
.text:10111A04                 call    ds:GetProcAddress(x,x)

Another example, to find each stacks, the code just loops trhough all threads, get their TEB and call:

.text:1010F44C                 push    offset aNttib_stackbas ; "NtTib.StackBase"
.text:1010F451                 lea     edx, [ebp+var_17C]
.text:1010F457                 lea     ecx, [ebp+var_CC]
.text:1010F45D                 call    ExtRemoteTyped::Field(char const *)

There is a lot of fetching from _PEB, _TEB, _HEAP and other internal structures so it's not probably doable without going directly through those structures. So, I guess that some of the information returned by !address are not accessible through usual / common APIs.

Neitsa
  • 7,693
  • 1
  • 28
  • 45
  • This helps out a lot. So, whenever there is no API available, we should reverse and find the undocumented ones. :) –  Mar 13 '16 at 22:44
0

You need to determine if the address you are interested in lies within a memory mapped file. Check out --> GetMappedFileName. Getting the heap and stack addresses of a process will be a little more problematic as the ranges are dynamic and don't always lie sequentially.

Lol, I don't know, I would start with a handle to the heap. If you can spawn/inherit a process then you more than likely can access the handle to the heap. This function looks promising: GetProcessHeap . That debug app runs as admin, it can walk the process chain and spy on any user level process. I don't think you will be able to access protected memory of kernel mode apps such as File System Filters, however, as they are dug down a little lower by policy.

Ross Bush
  • 14,648
  • 2
  • 32
  • 55
  • GetMappedFileName seems promising. I can use this whenever getMoudleFileName fails. But clearly Windbg is able to find them somehow. I just need to see how. –  Mar 11 '16 at 23:57
  • 1
    Added heap handle function – Ross Bush Mar 12 '16 at 00:01
  • Assume that I've all the rights. Windbg seems to run in userspace, not as System. It does have the right to Query another Process, and I get a process handle to a Process with all these rights. –  Mar 12 '16 at 00:09
  • Yes, you can get the handle of all user level processes if your process is running under an administrator account. – Ross Bush Mar 12 '16 at 00:17
  • I already am. The objective is to write a /proc/pid/maps equivalent. –  Mar 12 '16 at 00:45