Crash Dump Analysis Patterns (Part 303)
When looking at Execution Residue in Windows ARM64 memory dumps, we may notice Encoded Pointers in the form of authenticated pointers (PAC, Pointer Authentication Code, see the Linux guide and Windows info). For example:
0:000> dps 00000053f62fc000 00000053f6300000
...
00000053`f62ffe28 e817fff7`6a0bc054 functions_c!__scrt_common_main+0x14
...
The return address isn’t possible to use directly (Invalid Pointer):
0:000> ub e817fff7`6a0bc054
e817fff7`6a0bc034 ?? ???
^ Memory access error in ‘ub e817fff7`6a0bc054′
However, the symbolic reference is ok:
0:000> ub functions_c!__scrt_common_main+0x14
functions_c!pre_cpp_initialization+0x7c:
00007ff7`6a0bc034 00000000 ???
00007ff7`6a0bc038 00000000 ???
00007ff7`6a0bc03c 00000000 ???
functions_c!__scrt_common_main:
00007ff7`6a0bc040 d503237f pacibsp
00007ff7`6a0bc044 a9bf7bfd stp fp,lr,[sp,#-0x10]!
00007ff7`6a0bc048 910003fd mov fp,sp
00007ff7`6a0bc04c 97ffd81f bl functions_c!ILT+4284(__security_init_cookie) (00007ff7`6a0b20c8)
00007ff7`6a0bc050 94000016 bl functions_c!__scrt_common_main_seh (00007ff7`6a0bc0a8)
Because of that, Rough Stack that uses the dpS WinDbg command instead, omits such valid symbolic references.
If you find such pointers, you can replace the higher 4-byte part with the higher part of the module start address, for example:
0:000> lm
start end module name
00007ff7`6a0a0000 00007ff7`6a0d0000 functions_c
…
0:000> ub 00007ff7`6a0bc054
functions_c!pre_cpp_initialization+0×7c:
00007ff7`6a0bc034 00000000 ???
00007ff7`6a0bc038 00000000 ???
00007ff7`6a0bc03c 00000000 ???
functions_c!__scrt_common_main:
00007ff7`6a0bc040 d503237f pacibsp
00007ff7`6a0bc044 a9bf7bfd stp fp,lr,[sp,#-0×10]!
00007ff7`6a0bc048 910003fd mov fp,sp
00007ff7`6a0bc04c 97ffd81f bl functions_c!ILT+4284(__security_init_cookie) (00007ff7`6a0b20c8)
00007ff7`6a0bc050 94000016 bl functions_c!__scrt_common_main_seh (00007ff7`6a0bc0a8)
Of course, this may not work for pointers, encoded by the Windows EncodePointer API.
Finally, we write the formal pattern structure card for Encoded Pointer.
Intent
To recognize situations where a pointer stored in memory is not directly usable: its value must be interpreted or transformed before it can be resolved to a valid code or data address.
Context
Appears in:
Stack Trace, Execution Residue, Context Pointer, Historical Information.
Common environments:
- Tagged pointers
- ARM64 pointer authentication (PAC)
- Top-Byte-Ignore tagging (AArch64)
- ASLR and relocations that have not yet been applied in the captured memory
- Managed space compressed and metadata-embedded GC pointers
- Objective-C and Swift tagged ISA pointers
- Sanitizers or checking runtimes that add metadata bits
Problem
A pointer in the dump visually appears to be an address, but fails to resolve using normal symbolic or spatial checks; dereferencing its raw value yields an incorrect memory address or a memory error.
Forces
- Performance/security constraints favor encoded pointer formats
- Debugger views often show raw stack/heap
- Encoding schemes vary by platform and compiler
- Hardware PAC may prevent guessing the correct pointer form without a proper decode context
Symptoms
- Pointer value not inside any loaded module or valid virtual address range
- Symbol resolution differs
- Adjacent stack slots look pointer-like, but this one does not
- Backwards disassembly shows an incorrect frame
Resolution Strategies
- Decode PAC
- Canonicalize upper bits
- Strip tags
- Expand bits
- Apply relocation deltas
- Mask metadata
Resulting Context
After correct interpretation, the pointer becomes:
- Resolvable to a target symbol
- Walkable for call-stack reconstruction
- Safe for dereferencing in analysis context
- Enables further analysis
- Dmitry Vostokov @ DumpAnalysis.org + TraceAnalysis.org -