Last time we saw
how to view the stack of threads that were terminated
as part of process teardown from the kernel debugger.
You can do the same thing from a user-mode debugger,
and it’s actually a bit easier there.
(The user-mode debugger I’m using is the one that comes with the
Debugging Tools for Windows,
the debugging engine that goes by a number of different front-ends,
such as ntsd
, cdb
, and windbg
.)
A direct translation of the kernel-mode technique from last
time would involve using the
!vadump
command and picking through for the
memory blocks with candidate size and attributes.
But there’s an easier way.
Now would be a good point for me to remind you that this information is for debugging purposes only. The structures and offsets are all implementation details which can change from release to release.
Recall that the TEB begins with some pointers which bound the stack, and the seventh pointer is a self-pointer. What’s even more useful is the thirteenth pointer (offset 0x30 for 32-bit TEBs, offset 0x60 for 64-bit TEBs), because that is where the PEB is stored.
Each process has a single global PEB, so all the TEBs will have the same PEB value at offset 0x30/0x60. And you can figure out the address of the current process’s PEB either by using the !peb command or by simply looking at the TEB you already have.
0:000> dd fs:30 l1 0053:00000030 7efde000
Now you can search through memory looking for that value. If you see any hits at offset 0x30/0x60, then that’s a candidate TEB.
The debugger normally limits memory scans to 256MB.
0:001> s 00000000 L 80000000 00 e0 fd 7e ^ Range error in 's 00000000 l 80000000 00 e0 fd 7e'
Therefore, you have to issue the search eight times (for 32-bit processes) to cover the 2GB user-mode address space.
0:001> s 00000000 L 10000000 00 e0 fd 7e 0009e01c 00 e0 fd 7e 00 d0 fd 7e-44 e0 09 00 7b ef 17 77 ...~...~D...{..w 0009fdc0 00 e0 fd 7e 44 00 00 00-f0 ee 3a 00 10 ef 3a 00 ...~D.....:...:. 0009fe34 00 e0 fd 7e 78 fe 09 00-02 9f 18 77 00 e0 fd 7e ...~x......w...~ 0:001> s 10000000 L 10000000 00 e0 fd 7e 0:001> s 20000000 L 10000000 00 e0 fd 7e 0:001> s 30000000 L 10000000 00 e0 fd 7e 0:001> s 40000000 L 10000000 00 e0 fd 7e 0:001> s 50000000 L 10000000 00 e0 fd 7e 0:001> s 60000000 L 10000000 00 e0 fd 7e 0:001> s 70000000 L 10000000 00 e0 fd 7e 7486af70 00 e0 fd 7e 00 00 00 00-b8 00 16 77 28 00 16 77 ...~.......w(..w 7efda030 00 e0 fd 7e 00 00 00 00-00 00 00 00 00 00 00 00 ...~............ 7efdd030 00 e0 fd 7e 00 00 00 00-00 00 00 00 00 00 00 00 ...~............
Alternatively, you can use the “length sanity check override” by inserting a question mark after the L:
0:001> s 00000000 L?80000000 00 e0 fd 7e 0009e01c 00 e0 fd 7e 00 d0 fd 7e-44 e0 09 00 7b ef 17 77 ...~...~D...{..w 0009fdc0 00 e0 fd 7e 44 00 00 00-f0 ee 3a 00 10 ef 3a 00 ...~D.....:...:. 0009fe34 00 e0 fd 7e 78 fe 09 00-02 9f 18 77 00 e0 fd 7e ...~x......w...~ 7486af70 00 e0 fd 7e 00 00 00 00-b8 00 16 77 28 00 16 77 ...~.......w(..w 7efda030 00 e0 fd 7e 00 00 00 00-00 00 00 00 00 00 00 00 ...~............ 7efdd030 00 e0 fd 7e 00 00 00 00-00 00 00 00 00 00 00 00 ...~............
From the above output, we see that we can quickly reject all but the last two entries because the offset within the page is not the magic value 0x30. (This is a 32-bit process.) Hooray, two debugger commands reduce the search space to just two pages!
At this point, you can continue with the debugging technique from last time, looking at each candidate TEB to see if there’s a valid stack in there.
0 comments