Question about stack frames in a postmortem crash dump

kd2024

Member
Joined
Apr 6, 2024
Posts
11
Say, I have a full kernel memory dump (.dmp file.) After I open it in WinDbg, and run the k command to get the callstack:

9: kd> k
# Child-SP RetAddr Call Site
00 ffff820c`a3755270 fffff803`0eb8d104 nt!KeBugCheck2+0x1fc
01 ffff820c`a3755890 fffff803`0f373680 nt!KeBugCheckEx+0x14
02 ffff820c`a37558a0 fffff803`0f200ed8 nt!MmDeleteProcessAddressSpace+0x99dd0
03 ffff820c`a37558e0 fffff803`0f1ac000 nt!PspProcessDelete+0x278
04 ffff820c`a3755970 fffff803`0ebe6000 nt!ObpRemoveObjectRoutine+0xd0
05 ffff820c`a37559c0 fffff803`0f201cd0 nt!ObfDereferenceObjectWithTag+0x140
06 ffff820c`a37559f0 fffff803`0f1ac000 nt!PspThreadDelete+0x310
07 ffff820c`a3755a50 fffff803`0ec1c248 nt!ObpRemoveObjectRoutine+0xd0
08 (Inline Function) --------`-------- nt!ObfDereferenceObjectWithTag+0xc0
09 (Inline Function) --------`-------- nt!ObfDereferenceObject+0xc0
0a ffff820c`a3755aa0 fffff803`0ec990a8 nt!PspReaper+0x1a8
0b ffff820c`a3755ba0 fffff803`0ec1a684 nt!ExpWorkerThread+0x158
0c ffff820c`a3755d30 fffff803`0ef32a8c nt!PspSystemThreadStartup+0x64
0d ffff820c`a3755d90 00000000`00000000 nt!KiStartSystemThread+0x24
9: kd>

I can click on some of the links that WinDbg provides on the left (00, 01, etc.) or just run:

9: kd> .frame 0n0;dv /t /v

to retrieve stack frame 00.

We usually get something like this for that specific function call:

9: kd> .frame 0n0;dv /t /v
00 ffff820c`a3755270 fffff803`0eb8d104 nt!KeBugCheck2+0x1fc
ffff820c`a3755298 unsigned long BugCheckCode = 0x76
<unavailable> unsigned int64 BugCheckParameter1 = <value unavailable>
ffff820c`a37552c8 unsigned int64 BugCheckParameter2 = 0xffffaf05`5d1d8200
ffff820c`a37552c0 unsigned int64 BugCheckParameter3 = 5
<unavailable> unsigned int64 BugCheckParameter4 = <value unavailable>

I'm trying to understand the workings of the `.frame` feature. Thus my questions (if anyone knows):

1. Where is that information about input parameters for each function stored? Or, in other words, how does WinDbg retrieve it?

2. And why are we sometimes getting "value unavailable" for some parameters?

I guess my overall intent here is an attempt to understand how are those parameters retrieved and whether or not the results of such retrieval can be trusted.
 
Thanks for the link. I'll read it. I guess I should've pointed out that I'm asking it for an Aarch64 CPU, as it is architecture dependent.
 
Yeah, thanks. I know most of that stuff.

My question was about implementation of the .frame command. Does the extension walk the stack back to retrieve all the local parameters for the calls? Sure, you can walk the stack (on Aarch64) by reading FP and LR registers from the stack. But how does it get the input parameters?

I am assuming that some could be deduced by analyzing machine code preceding the call, say, for example (hypothetically):

Code:
add   x2, x2, x19
mov   x0, #4
mov   x1, #0
blr   x8

We know that the first parameter was 4 and the second parameter was 0.

But how does it pull out the rest of the parameters, like the 3rd one in my example above?
 
The .frame command should read from the function prologue and then deduce the arguments from that so you should be able to use u or uf to examine how they're being set. I've always used .frame /r though rather than dv.
 
Code Machine does have a debugger extension which you can download called CMKD.dll, it has a very useful command called !stack -p which will pull out the arguments for you if it can, not sure if it works with ARM though?
 
The .frame command should read from the function prologue and then deduce the arguments from that so you should be able to use u or uf to examine how they're being set. I've always used .frame /r though rather than dv.

I'm still not fully understanding how it does it. I'll follow up on it with the next post. For now let me answer your points.

Yeah, frame /r 0 will give more info. In this case though I clicked the blue link on the left in the call stack, so .frame 0n0;dv /t /v was WinDbg's built in interpretation. The output though of that combined command was still the same.

I also tried the CMKD.dll extension and no, it won't work for me. It doesn't work with the Aarch64 architecture. But it's an interesting concept. If I figure out how they can retrieve frame info (or how does .frame command work), I may write an extension like that. That's a good idea. Thanks
 
so going back to my original question about the .frame command. Here's a slightly different bugcheck.

If I do:

5: kd> .frame /r 0n0;
00 ffffeb85`3bc21d50 fffff800`fa53cb04 nt!KeBugCheck2+0x1fc
x0=0000000000000000 x1=0000000000000005 x2=0000000000000002 x3=0000000000000005
x4=ffffeb853bc21d28 x5=0000000000000001 x6=0000000000000080 x7=0000000000000001
x8=0000000000000000 x9=fffff800faca40b0 x10=0000000000000005 x11=0000000000000000
x12=0000000000000000 x13=00000004e625fb73 x14=fffff800f9f44cd0 x15=0000000000000000
x16=000079c13cbe47b1 x17=000079c13cbe47b1 x18=ffffc780446ce000 x19=fffff800facda580
x20=00000c7bbf09a560 x21=0000000000000000 x22=ffffc780446ce980 x23=ffffeb853bc22ac0
x24=ffffeb853bc24000 x25=ffffffffc0000005 x26=0000000000000001 x27=0000000000000001
x28=0000000000000000 fp=ffffeb853bc21d60 lr=0000000000000000 sp=ffffeb853bc21d50
pc=fffff800fa17c5fc psr=00000144 ---- EL1

for the 0-th frame, which is the bugcheck function itself (the register output is what you see above.)

Then if I do:

5: kd> .frame 0n0;dv /t /v
00 ffffeb85`3bc21d50 fffff800`fa53cb04 nt!KeBugCheck2+0x1fc
ffffeb85`3bc21d78 unsigned long BugCheckCode = 0x7e
<unavailable> unsigned int64 BugCheckParameter1 = <value unavailable>
ffffeb85`3bc21da8 unsigned int64 BugCheckParameter2 = 0xfffff800`fa19138c
ffffeb85`3bc21da0 unsigned int64 BugCheckParameter3 = 0xffffeb85`3bc23038
<unavailable> unsigned int64 BugCheckParameter4 = <value unavailable>

As you can see it couldn't get Arg1. Let's try to check why...

If I actually go look at the assembly code that caused it (this is a copy-and-paste of the actual code - I'm using the address fffff800`fa53cb04 from above):

fffff800`fa53ca68 7f2303d5 pacibsp
fffff800`fa53ca6c fd7bbfa9 stp fp, lr, [sp, #-0x10]!
fffff800`fa53ca70 fd0301aa mov fp, x1
fffff800`fa53ca74 a01f00f9 str x0, [fp, #0x38]
fffff800`fa53ca78 b1430091 add xip1, fp, #0x10
fffff800`fa53ca7c 20fe9fc8 stlr x0, [xip1]
fffff800`fa53ca80 b1430091 add xip1, fp, #0x10
fffff800`fa53ca84 28c2bff8 ldapr x8, [xip1]
fffff800`fa53ca88 080540f9 ldr x8, [x8, #8]
fffff800`fa53ca8c b1630091 add xip1, fp, #0x18
fffff800`fa53ca90 28fe9fc8 stlr x8, [xip1]
fffff800`fa53ca94 b1430091 add xip1, fp, #0x10
fffff800`fa53ca98 28c2bff8 ldapr x8, [xip1]
fffff800`fa53ca9c 080140f9 ldr x8, [x8]
fffff800`fa53caa0 b1830091 add xip1, fp, #0x20
fffff800`fa53caa4 28fe9fc8 stlr x8, [xip1]
fffff800`fa53caa8 b1430091 add xip1, fp, #0x10
fffff800`fa53caac 28c2bff8 ldapr x8, [xip1]
fffff800`fa53cab0 080140f9 ldr x8, [x8]
fffff800`fa53cab4 080940f9 ldr x8, [x8, #0x10]
fffff800`fa53cab8 b1a30091 add xip1, fp, #0x28
fffff800`fa53cabc 28fe9fc8 stlr x8, [xip1]
fffff800`fa53cac0 b1430091 add xip1, fp, #0x10
fffff800`fa53cac4 28c2bff8 ldapr x8, [xip1]
fffff800`fa53cac8 080140f9 ldr x8, [x8]
fffff800`fa53cacc 080180b9 ldrsw x8, [x8]
fffff800`fa53cad0 b1c30091 add xip1, fp, #0x30
fffff800`fa53cad4 28fe9fc8 stlr x8, [xip1]
fffff800`fa53cad8 050080d2 mov x5, #0
fffff800`fa53cadc b1630091 add xip1, fp, #0x18
fffff800`fa53cae0 24c2bff8 ldapr x4, [xip1]
fffff800`fa53cae4 b1830091 add xip1, fp, #0x20
fffff800`fa53cae8 23c2bff8 ldapr x3, [xip1]
fffff800`fa53caec b1a30091 add xip1, fp, #0x28
fffff800`fa53caf0 22c2bff8 ldapr x2, [xip1]
fffff800`fa53caf4 b1c30091 add xip1, fp, #0x30
fffff800`fa53caf8 21c2bff8 ldapr x1, [xip1]
fffff800`fa53cafc c00f8052 mov w0, #0x7E
fffff800`fa53cb00 40fef097 bl ntkrnlmp!KeBugCheck2 (fffff800fa17c400)

Yes, w0 was set to 0x7E, which is the 1st parameter for the KeBugCheck2:

void KeBugCheck2(
unsigned long,
unsigned int64,
unsigned int64,
unsigned int64,
unsigned int64,
_KTRAP_FRAME*
)

It missed Arg1, which is the 2nd parameter for the KeBugCheck2.

But how did it figure out Arg2 to be 0xfffff800`fa19138c, or the 3rd parameter for KeBugCheck2?
 

Has Sysnative Forums helped you? Please consider donating to help us support the site!

Back
Top