xref: /qemu/contrib/elf2dmp/main.c (revision 3fa2d384c245bcee3a9ecfa11f298b76ea4c9d57)
1*3fa2d384SViktor Prutyanov /*
2*3fa2d384SViktor Prutyanov  * Copyright (c) 2018 Virtuozzo International GmbH
3*3fa2d384SViktor Prutyanov  *
4*3fa2d384SViktor Prutyanov  * This work is licensed under the terms of the GNU GPL, version 2 or later.
5*3fa2d384SViktor Prutyanov  *
6*3fa2d384SViktor Prutyanov  */
7*3fa2d384SViktor Prutyanov 
8*3fa2d384SViktor Prutyanov #include "qemu/osdep.h"
9*3fa2d384SViktor Prutyanov #include "err.h"
10*3fa2d384SViktor Prutyanov #include "addrspace.h"
11*3fa2d384SViktor Prutyanov #include "pe.h"
12*3fa2d384SViktor Prutyanov #include "pdb.h"
13*3fa2d384SViktor Prutyanov #include "kdbg.h"
14*3fa2d384SViktor Prutyanov #include "download.h"
15*3fa2d384SViktor Prutyanov #include "qemu/win_dump_defs.h"
16*3fa2d384SViktor Prutyanov 
17*3fa2d384SViktor Prutyanov #define SYM_URL_BASE    "https://msdl.microsoft.com/download/symbols/"
18*3fa2d384SViktor Prutyanov #define PDB_NAME    "ntkrnlmp.pdb"
19*3fa2d384SViktor Prutyanov 
20*3fa2d384SViktor Prutyanov #define INITIAL_MXCSR   0x1f80
21*3fa2d384SViktor Prutyanov 
22*3fa2d384SViktor Prutyanov typedef struct idt_desc {
23*3fa2d384SViktor Prutyanov     uint16_t offset1;   /* offset bits 0..15 */
24*3fa2d384SViktor Prutyanov     uint16_t selector;
25*3fa2d384SViktor Prutyanov     uint8_t ist;
26*3fa2d384SViktor Prutyanov     uint8_t type_attr;
27*3fa2d384SViktor Prutyanov     uint16_t offset2;   /* offset bits 16..31 */
28*3fa2d384SViktor Prutyanov     uint32_t offset3;   /* offset bits 32..63 */
29*3fa2d384SViktor Prutyanov     uint32_t rsrvd;
30*3fa2d384SViktor Prutyanov } __attribute__ ((packed)) idt_desc_t;
31*3fa2d384SViktor Prutyanov 
32*3fa2d384SViktor Prutyanov static uint64_t idt_desc_addr(idt_desc_t desc)
33*3fa2d384SViktor Prutyanov {
34*3fa2d384SViktor Prutyanov     return (uint64_t)desc.offset1 | ((uint64_t)desc.offset2 << 16) |
35*3fa2d384SViktor Prutyanov           ((uint64_t)desc.offset3 << 32);
36*3fa2d384SViktor Prutyanov }
37*3fa2d384SViktor Prutyanov 
38*3fa2d384SViktor Prutyanov static const uint64_t SharedUserData = 0xfffff78000000000;
39*3fa2d384SViktor Prutyanov 
40*3fa2d384SViktor Prutyanov #define KUSD_OFFSET_SUITE_MASK 0x2d0
41*3fa2d384SViktor Prutyanov #define KUSD_OFFSET_PRODUCT_TYPE 0x264
42*3fa2d384SViktor Prutyanov 
43*3fa2d384SViktor Prutyanov #define SYM_RESOLVE(base, r, s) ((s = pdb_resolve(base, r, #s)),\
44*3fa2d384SViktor Prutyanov     s ? printf(#s" = 0x%016lx\n", s) : eprintf("Failed to resolve "#s"\n"), s)
45*3fa2d384SViktor Prutyanov 
46*3fa2d384SViktor Prutyanov static uint64_t rol(uint64_t x, uint64_t y)
47*3fa2d384SViktor Prutyanov {
48*3fa2d384SViktor Prutyanov     return (x << y) | (x >> (64 - y));
49*3fa2d384SViktor Prutyanov }
50*3fa2d384SViktor Prutyanov 
51*3fa2d384SViktor Prutyanov /*
52*3fa2d384SViktor Prutyanov  * Decoding algorithm can be found in Volatility project
53*3fa2d384SViktor Prutyanov  */
54*3fa2d384SViktor Prutyanov static void kdbg_decode(uint64_t *dst, uint64_t *src, size_t size,
55*3fa2d384SViktor Prutyanov         uint64_t kwn, uint64_t kwa, uint64_t kdbe)
56*3fa2d384SViktor Prutyanov {
57*3fa2d384SViktor Prutyanov     size_t i;
58*3fa2d384SViktor Prutyanov     assert(size % sizeof(uint64_t) == 0);
59*3fa2d384SViktor Prutyanov     for (i = 0; i < size / sizeof(uint64_t); i++) {
60*3fa2d384SViktor Prutyanov         uint64_t block;
61*3fa2d384SViktor Prutyanov 
62*3fa2d384SViktor Prutyanov         block = src[i];
63*3fa2d384SViktor Prutyanov         block = rol(block ^ kwn, (uint8_t)kwn);
64*3fa2d384SViktor Prutyanov         block = __builtin_bswap64(block ^ kdbe) ^ kwa;
65*3fa2d384SViktor Prutyanov         dst[i] = block;
66*3fa2d384SViktor Prutyanov     }
67*3fa2d384SViktor Prutyanov }
68*3fa2d384SViktor Prutyanov 
69*3fa2d384SViktor Prutyanov static KDDEBUGGER_DATA64 *get_kdbg(uint64_t KernBase, struct pdb_reader *pdb,
70*3fa2d384SViktor Prutyanov         struct va_space *vs, uint64_t KdDebuggerDataBlock)
71*3fa2d384SViktor Prutyanov {
72*3fa2d384SViktor Prutyanov     const char OwnerTag[4] = "KDBG";
73*3fa2d384SViktor Prutyanov     KDDEBUGGER_DATA64 *kdbg = NULL;
74*3fa2d384SViktor Prutyanov     DBGKD_DEBUG_DATA_HEADER64 kdbg_hdr;
75*3fa2d384SViktor Prutyanov     bool decode = false;
76*3fa2d384SViktor Prutyanov     uint64_t kwn, kwa, KdpDataBlockEncoded;
77*3fa2d384SViktor Prutyanov 
78*3fa2d384SViktor Prutyanov     if (va_space_rw(vs,
79*3fa2d384SViktor Prutyanov                 KdDebuggerDataBlock + offsetof(KDDEBUGGER_DATA64, Header),
80*3fa2d384SViktor Prutyanov                 &kdbg_hdr, sizeof(kdbg_hdr), 0)) {
81*3fa2d384SViktor Prutyanov         eprintf("Failed to extract KDBG header\n");
82*3fa2d384SViktor Prutyanov         return NULL;
83*3fa2d384SViktor Prutyanov     }
84*3fa2d384SViktor Prutyanov 
85*3fa2d384SViktor Prutyanov     if (memcmp(&kdbg_hdr.OwnerTag, OwnerTag, sizeof(OwnerTag))) {
86*3fa2d384SViktor Prutyanov         uint64_t KiWaitNever, KiWaitAlways;
87*3fa2d384SViktor Prutyanov 
88*3fa2d384SViktor Prutyanov         decode = true;
89*3fa2d384SViktor Prutyanov 
90*3fa2d384SViktor Prutyanov         if (!SYM_RESOLVE(KernBase, pdb, KiWaitNever) ||
91*3fa2d384SViktor Prutyanov                 !SYM_RESOLVE(KernBase, pdb, KiWaitAlways) ||
92*3fa2d384SViktor Prutyanov                 !SYM_RESOLVE(KernBase, pdb, KdpDataBlockEncoded)) {
93*3fa2d384SViktor Prutyanov             return NULL;
94*3fa2d384SViktor Prutyanov         }
95*3fa2d384SViktor Prutyanov 
96*3fa2d384SViktor Prutyanov         if (va_space_rw(vs, KiWaitNever, &kwn, sizeof(kwn), 0) ||
97*3fa2d384SViktor Prutyanov                 va_space_rw(vs, KiWaitAlways, &kwa, sizeof(kwa), 0)) {
98*3fa2d384SViktor Prutyanov             return NULL;
99*3fa2d384SViktor Prutyanov         }
100*3fa2d384SViktor Prutyanov 
101*3fa2d384SViktor Prutyanov         printf("[KiWaitNever] = 0x%016lx\n", kwn);
102*3fa2d384SViktor Prutyanov         printf("[KiWaitAlways] = 0x%016lx\n", kwa);
103*3fa2d384SViktor Prutyanov 
104*3fa2d384SViktor Prutyanov         /*
105*3fa2d384SViktor Prutyanov          * If KDBG header can be decoded, KDBG size is available
106*3fa2d384SViktor Prutyanov          * and entire KDBG can be decoded.
107*3fa2d384SViktor Prutyanov          */
108*3fa2d384SViktor Prutyanov         printf("Decoding KDBG header...\n");
109*3fa2d384SViktor Prutyanov         kdbg_decode((uint64_t *)&kdbg_hdr, (uint64_t *)&kdbg_hdr,
110*3fa2d384SViktor Prutyanov                 sizeof(kdbg_hdr), kwn, kwa, KdpDataBlockEncoded);
111*3fa2d384SViktor Prutyanov 
112*3fa2d384SViktor Prutyanov         printf("Owner tag is \'%.4s\'\n", (char *)&kdbg_hdr.OwnerTag);
113*3fa2d384SViktor Prutyanov         if (memcmp(&kdbg_hdr.OwnerTag, OwnerTag, sizeof(OwnerTag))) {
114*3fa2d384SViktor Prutyanov             eprintf("Failed to decode KDBG header\n");
115*3fa2d384SViktor Prutyanov             return NULL;
116*3fa2d384SViktor Prutyanov         }
117*3fa2d384SViktor Prutyanov     }
118*3fa2d384SViktor Prutyanov 
119*3fa2d384SViktor Prutyanov     kdbg = malloc(kdbg_hdr.Size);
120*3fa2d384SViktor Prutyanov     if (!kdbg) {
121*3fa2d384SViktor Prutyanov         return NULL;
122*3fa2d384SViktor Prutyanov     }
123*3fa2d384SViktor Prutyanov 
124*3fa2d384SViktor Prutyanov     if (va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 0)) {
125*3fa2d384SViktor Prutyanov         eprintf("Failed to extract entire KDBG\n");
126*3fa2d384SViktor Prutyanov         return NULL;
127*3fa2d384SViktor Prutyanov     }
128*3fa2d384SViktor Prutyanov 
129*3fa2d384SViktor Prutyanov     if (!decode) {
130*3fa2d384SViktor Prutyanov         return kdbg;
131*3fa2d384SViktor Prutyanov     }
132*3fa2d384SViktor Prutyanov 
133*3fa2d384SViktor Prutyanov     printf("Decoding KdDebuggerDataBlock...\n");
134*3fa2d384SViktor Prutyanov     kdbg_decode((uint64_t *)kdbg, (uint64_t *)kdbg, kdbg_hdr.Size,
135*3fa2d384SViktor Prutyanov                 kwn, kwa, KdpDataBlockEncoded);
136*3fa2d384SViktor Prutyanov 
137*3fa2d384SViktor Prutyanov     va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 1);
138*3fa2d384SViktor Prutyanov 
139*3fa2d384SViktor Prutyanov     return kdbg;
140*3fa2d384SViktor Prutyanov }
141*3fa2d384SViktor Prutyanov 
142*3fa2d384SViktor Prutyanov static void win_context_init_from_qemu_cpu_state(WinContext *ctx,
143*3fa2d384SViktor Prutyanov         QEMUCPUState *s)
144*3fa2d384SViktor Prutyanov {
145*3fa2d384SViktor Prutyanov     WinContext win_ctx = (WinContext){
146*3fa2d384SViktor Prutyanov         .ContextFlags = WIN_CTX_X64 | WIN_CTX_INT | WIN_CTX_SEG | WIN_CTX_CTL,
147*3fa2d384SViktor Prutyanov         .MxCsr = INITIAL_MXCSR,
148*3fa2d384SViktor Prutyanov 
149*3fa2d384SViktor Prutyanov         .SegCs = s->cs.selector,
150*3fa2d384SViktor Prutyanov         .SegSs = s->ss.selector,
151*3fa2d384SViktor Prutyanov         .SegDs = s->ds.selector,
152*3fa2d384SViktor Prutyanov         .SegEs = s->es.selector,
153*3fa2d384SViktor Prutyanov         .SegFs = s->fs.selector,
154*3fa2d384SViktor Prutyanov         .SegGs = s->gs.selector,
155*3fa2d384SViktor Prutyanov         .EFlags = (uint32_t)s->rflags,
156*3fa2d384SViktor Prutyanov 
157*3fa2d384SViktor Prutyanov         .Rax = s->rax,
158*3fa2d384SViktor Prutyanov         .Rbx = s->rbx,
159*3fa2d384SViktor Prutyanov         .Rcx = s->rcx,
160*3fa2d384SViktor Prutyanov         .Rdx = s->rdx,
161*3fa2d384SViktor Prutyanov         .Rsp = s->rsp,
162*3fa2d384SViktor Prutyanov         .Rbp = s->rbp,
163*3fa2d384SViktor Prutyanov         .Rsi = s->rsi,
164*3fa2d384SViktor Prutyanov         .Rdi = s->rdi,
165*3fa2d384SViktor Prutyanov         .R8  = s->r8,
166*3fa2d384SViktor Prutyanov         .R9  = s->r9,
167*3fa2d384SViktor Prutyanov         .R10 = s->r10,
168*3fa2d384SViktor Prutyanov         .R11 = s->r11,
169*3fa2d384SViktor Prutyanov         .R12 = s->r12,
170*3fa2d384SViktor Prutyanov         .R13 = s->r13,
171*3fa2d384SViktor Prutyanov         .R14 = s->r14,
172*3fa2d384SViktor Prutyanov         .R15 = s->r15,
173*3fa2d384SViktor Prutyanov 
174*3fa2d384SViktor Prutyanov         .Rip = s->rip,
175*3fa2d384SViktor Prutyanov         .FltSave = {
176*3fa2d384SViktor Prutyanov             .MxCsr = INITIAL_MXCSR,
177*3fa2d384SViktor Prutyanov         },
178*3fa2d384SViktor Prutyanov     };
179*3fa2d384SViktor Prutyanov 
180*3fa2d384SViktor Prutyanov     *ctx = win_ctx;
181*3fa2d384SViktor Prutyanov }
182*3fa2d384SViktor Prutyanov 
183*3fa2d384SViktor Prutyanov /*
184*3fa2d384SViktor Prutyanov  * Finds paging-structure hierarchy base,
185*3fa2d384SViktor Prutyanov  * if previously set doesn't give access to kernel structures
186*3fa2d384SViktor Prutyanov  */
187*3fa2d384SViktor Prutyanov static int fix_dtb(struct va_space *vs, QEMU_Elf *qe)
188*3fa2d384SViktor Prutyanov {
189*3fa2d384SViktor Prutyanov     /*
190*3fa2d384SViktor Prutyanov      * Firstly, test previously set DTB.
191*3fa2d384SViktor Prutyanov      */
192*3fa2d384SViktor Prutyanov     if (va_space_resolve(vs, SharedUserData)) {
193*3fa2d384SViktor Prutyanov         return 0;
194*3fa2d384SViktor Prutyanov     }
195*3fa2d384SViktor Prutyanov 
196*3fa2d384SViktor Prutyanov     /*
197*3fa2d384SViktor Prutyanov      * Secondly, find CPU which run system task.
198*3fa2d384SViktor Prutyanov      */
199*3fa2d384SViktor Prutyanov     size_t i;
200*3fa2d384SViktor Prutyanov     for (i = 0; i < qe->state_nr; i++) {
201*3fa2d384SViktor Prutyanov         QEMUCPUState *s = qe->state[i];
202*3fa2d384SViktor Prutyanov 
203*3fa2d384SViktor Prutyanov         if (is_system(s)) {
204*3fa2d384SViktor Prutyanov             va_space_set_dtb(vs, s->cr[3]);
205*3fa2d384SViktor Prutyanov             printf("DTB 0x%016lx has been found from CPU #%zu"
206*3fa2d384SViktor Prutyanov                     " as system task CR3\n", vs->dtb, i);
207*3fa2d384SViktor Prutyanov             return !(va_space_resolve(vs, SharedUserData));
208*3fa2d384SViktor Prutyanov         }
209*3fa2d384SViktor Prutyanov     }
210*3fa2d384SViktor Prutyanov 
211*3fa2d384SViktor Prutyanov     /*
212*3fa2d384SViktor Prutyanov      * Thirdly, use KERNEL_GS_BASE from CPU #0 as PRCB address and
213*3fa2d384SViktor Prutyanov      * CR3 as [Prcb+0x7000]
214*3fa2d384SViktor Prutyanov      */
215*3fa2d384SViktor Prutyanov     if (qe->has_kernel_gs_base) {
216*3fa2d384SViktor Prutyanov         QEMUCPUState *s = qe->state[0];
217*3fa2d384SViktor Prutyanov         uint64_t Prcb = s->kernel_gs_base;
218*3fa2d384SViktor Prutyanov         uint64_t *cr3 = va_space_resolve(vs, Prcb + 0x7000);
219*3fa2d384SViktor Prutyanov 
220*3fa2d384SViktor Prutyanov         if (!cr3) {
221*3fa2d384SViktor Prutyanov             return 1;
222*3fa2d384SViktor Prutyanov         }
223*3fa2d384SViktor Prutyanov 
224*3fa2d384SViktor Prutyanov         va_space_set_dtb(vs, *cr3);
225*3fa2d384SViktor Prutyanov         printf("DirectoryTableBase = 0x%016lx has been found from CPU #0"
226*3fa2d384SViktor Prutyanov                 " as interrupt handling CR3\n", vs->dtb);
227*3fa2d384SViktor Prutyanov         return !(va_space_resolve(vs, SharedUserData));
228*3fa2d384SViktor Prutyanov     }
229*3fa2d384SViktor Prutyanov 
230*3fa2d384SViktor Prutyanov     return 1;
231*3fa2d384SViktor Prutyanov }
232*3fa2d384SViktor Prutyanov 
233*3fa2d384SViktor Prutyanov static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps,
234*3fa2d384SViktor Prutyanov         struct va_space *vs, uint64_t KdDebuggerDataBlock,
235*3fa2d384SViktor Prutyanov         KDDEBUGGER_DATA64 *kdbg, uint64_t KdVersionBlock, int nr_cpus)
236*3fa2d384SViktor Prutyanov {
237*3fa2d384SViktor Prutyanov     uint32_t *suite_mask = va_space_resolve(vs, SharedUserData +
238*3fa2d384SViktor Prutyanov             KUSD_OFFSET_SUITE_MASK);
239*3fa2d384SViktor Prutyanov     int32_t *product_type = va_space_resolve(vs, SharedUserData +
240*3fa2d384SViktor Prutyanov             KUSD_OFFSET_PRODUCT_TYPE);
241*3fa2d384SViktor Prutyanov     DBGKD_GET_VERSION64 kvb;
242*3fa2d384SViktor Prutyanov     WinDumpHeader64 h;
243*3fa2d384SViktor Prutyanov     size_t i;
244*3fa2d384SViktor Prutyanov 
245*3fa2d384SViktor Prutyanov     QEMU_BUILD_BUG_ON(KUSD_OFFSET_SUITE_MASK >= PAGE_SIZE);
246*3fa2d384SViktor Prutyanov     QEMU_BUILD_BUG_ON(KUSD_OFFSET_PRODUCT_TYPE >= PAGE_SIZE);
247*3fa2d384SViktor Prutyanov 
248*3fa2d384SViktor Prutyanov     if (!suite_mask || !product_type) {
249*3fa2d384SViktor Prutyanov         return 1;
250*3fa2d384SViktor Prutyanov     }
251*3fa2d384SViktor Prutyanov 
252*3fa2d384SViktor Prutyanov     if (va_space_rw(vs, KdVersionBlock, &kvb, sizeof(kvb), 0)) {
253*3fa2d384SViktor Prutyanov         eprintf("Failed to extract KdVersionBlock\n");
254*3fa2d384SViktor Prutyanov         return 1;
255*3fa2d384SViktor Prutyanov     }
256*3fa2d384SViktor Prutyanov 
257*3fa2d384SViktor Prutyanov     h = (WinDumpHeader64) {
258*3fa2d384SViktor Prutyanov         .Signature = "PAGE",
259*3fa2d384SViktor Prutyanov         .ValidDump = "DU64",
260*3fa2d384SViktor Prutyanov         .MajorVersion = kvb.MajorVersion,
261*3fa2d384SViktor Prutyanov         .MinorVersion = kvb.MinorVersion,
262*3fa2d384SViktor Prutyanov         .DirectoryTableBase = vs->dtb,
263*3fa2d384SViktor Prutyanov         .PfnDatabase = kdbg->MmPfnDatabase,
264*3fa2d384SViktor Prutyanov         .PsLoadedModuleList = kdbg->PsLoadedModuleList,
265*3fa2d384SViktor Prutyanov         .PsActiveProcessHead = kdbg->PsActiveProcessHead,
266*3fa2d384SViktor Prutyanov         .MachineImageType = kvb.MachineType,
267*3fa2d384SViktor Prutyanov         .NumberProcessors = nr_cpus,
268*3fa2d384SViktor Prutyanov         .BugcheckCode = LIVE_SYSTEM_DUMP,
269*3fa2d384SViktor Prutyanov         .KdDebuggerDataBlock = KdDebuggerDataBlock,
270*3fa2d384SViktor Prutyanov         .DumpType = 1,
271*3fa2d384SViktor Prutyanov         .Comment = "Hello from elf2dmp!",
272*3fa2d384SViktor Prutyanov         .SuiteMask = *suite_mask,
273*3fa2d384SViktor Prutyanov         .ProductType = *product_type,
274*3fa2d384SViktor Prutyanov         .SecondaryDataState = kvb.KdSecondaryVersion,
275*3fa2d384SViktor Prutyanov         .PhysicalMemoryBlock = (WinDumpPhyMemDesc64) {
276*3fa2d384SViktor Prutyanov             .NumberOfRuns = ps->block_nr,
277*3fa2d384SViktor Prutyanov         },
278*3fa2d384SViktor Prutyanov         .RequiredDumpSpace = sizeof(h),
279*3fa2d384SViktor Prutyanov     };
280*3fa2d384SViktor Prutyanov 
281*3fa2d384SViktor Prutyanov     for (i = 0; i < ps->block_nr; i++) {
282*3fa2d384SViktor Prutyanov         h.PhysicalMemoryBlock.NumberOfPages += ps->block[i].size / PAGE_SIZE;
283*3fa2d384SViktor Prutyanov         h.PhysicalMemoryBlock.Run[i] = (WinDumpPhyMemRun64) {
284*3fa2d384SViktor Prutyanov             .BasePage = ps->block[i].paddr / PAGE_SIZE,
285*3fa2d384SViktor Prutyanov             .PageCount = ps->block[i].size / PAGE_SIZE,
286*3fa2d384SViktor Prutyanov         };
287*3fa2d384SViktor Prutyanov     }
288*3fa2d384SViktor Prutyanov 
289*3fa2d384SViktor Prutyanov     h.RequiredDumpSpace += h.PhysicalMemoryBlock.NumberOfPages << PAGE_BITS;
290*3fa2d384SViktor Prutyanov 
291*3fa2d384SViktor Prutyanov     *hdr = h;
292*3fa2d384SViktor Prutyanov 
293*3fa2d384SViktor Prutyanov     return 0;
294*3fa2d384SViktor Prutyanov }
295*3fa2d384SViktor Prutyanov 
296*3fa2d384SViktor Prutyanov static int fill_context(KDDEBUGGER_DATA64 *kdbg,
297*3fa2d384SViktor Prutyanov         struct va_space *vs, QEMU_Elf *qe)
298*3fa2d384SViktor Prutyanov {
299*3fa2d384SViktor Prutyanov 	int i;
300*3fa2d384SViktor Prutyanov     for (i = 0; i < qe->state_nr; i++) {
301*3fa2d384SViktor Prutyanov         uint64_t Prcb;
302*3fa2d384SViktor Prutyanov         uint64_t Context;
303*3fa2d384SViktor Prutyanov         WinContext ctx;
304*3fa2d384SViktor Prutyanov         QEMUCPUState *s = qe->state[i];
305*3fa2d384SViktor Prutyanov 
306*3fa2d384SViktor Prutyanov         if (va_space_rw(vs, kdbg->KiProcessorBlock + sizeof(Prcb) * i,
307*3fa2d384SViktor Prutyanov                     &Prcb, sizeof(Prcb), 0)) {
308*3fa2d384SViktor Prutyanov             eprintf("Failed to read CPU #%d PRCB location\n", i);
309*3fa2d384SViktor Prutyanov             return 1;
310*3fa2d384SViktor Prutyanov         }
311*3fa2d384SViktor Prutyanov 
312*3fa2d384SViktor Prutyanov         if (va_space_rw(vs, Prcb + kdbg->OffsetPrcbContext,
313*3fa2d384SViktor Prutyanov                     &Context, sizeof(Context), 0)) {
314*3fa2d384SViktor Prutyanov             eprintf("Failed to read CPU #%d ContextFrame location\n", i);
315*3fa2d384SViktor Prutyanov             return 1;
316*3fa2d384SViktor Prutyanov         }
317*3fa2d384SViktor Prutyanov 
318*3fa2d384SViktor Prutyanov         printf("Filling context for CPU #%d...\n", i);
319*3fa2d384SViktor Prutyanov         win_context_init_from_qemu_cpu_state(&ctx, s);
320*3fa2d384SViktor Prutyanov 
321*3fa2d384SViktor Prutyanov         if (va_space_rw(vs, Context, &ctx, sizeof(ctx), 1)) {
322*3fa2d384SViktor Prutyanov             eprintf("Failed to fill CPU #%d context\n", i);
323*3fa2d384SViktor Prutyanov             return 1;
324*3fa2d384SViktor Prutyanov         }
325*3fa2d384SViktor Prutyanov     }
326*3fa2d384SViktor Prutyanov 
327*3fa2d384SViktor Prutyanov     return 0;
328*3fa2d384SViktor Prutyanov }
329*3fa2d384SViktor Prutyanov 
330*3fa2d384SViktor Prutyanov static int write_dump(struct pa_space *ps,
331*3fa2d384SViktor Prutyanov         WinDumpHeader64 *hdr, const char *name)
332*3fa2d384SViktor Prutyanov {
333*3fa2d384SViktor Prutyanov     FILE *dmp_file = fopen(name, "wb");
334*3fa2d384SViktor Prutyanov     size_t i;
335*3fa2d384SViktor Prutyanov 
336*3fa2d384SViktor Prutyanov     if (!dmp_file) {
337*3fa2d384SViktor Prutyanov         eprintf("Failed to open output file \'%s\'\n", name);
338*3fa2d384SViktor Prutyanov         return 1;
339*3fa2d384SViktor Prutyanov     }
340*3fa2d384SViktor Prutyanov 
341*3fa2d384SViktor Prutyanov     printf("Writing header to file...\n");
342*3fa2d384SViktor Prutyanov 
343*3fa2d384SViktor Prutyanov     if (fwrite(hdr, sizeof(*hdr), 1, dmp_file) != 1) {
344*3fa2d384SViktor Prutyanov         eprintf("Failed to write dump header\n");
345*3fa2d384SViktor Prutyanov         fclose(dmp_file);
346*3fa2d384SViktor Prutyanov         return 1;
347*3fa2d384SViktor Prutyanov     }
348*3fa2d384SViktor Prutyanov 
349*3fa2d384SViktor Prutyanov     for (i = 0; i < ps->block_nr; i++) {
350*3fa2d384SViktor Prutyanov         struct pa_block *b = &ps->block[i];
351*3fa2d384SViktor Prutyanov 
352*3fa2d384SViktor Prutyanov         printf("Writing block #%zu/%zu to file...\n", i, ps->block_nr);
353*3fa2d384SViktor Prutyanov         if (fwrite(b->addr, b->size, 1, dmp_file) != 1) {
354*3fa2d384SViktor Prutyanov             eprintf("Failed to write dump header\n");
355*3fa2d384SViktor Prutyanov             fclose(dmp_file);
356*3fa2d384SViktor Prutyanov             return 1;
357*3fa2d384SViktor Prutyanov         }
358*3fa2d384SViktor Prutyanov     }
359*3fa2d384SViktor Prutyanov 
360*3fa2d384SViktor Prutyanov     return fclose(dmp_file);
361*3fa2d384SViktor Prutyanov }
362*3fa2d384SViktor Prutyanov 
363*3fa2d384SViktor Prutyanov static int pe_get_pdb_symstore_hash(uint64_t base, void *start_addr,
364*3fa2d384SViktor Prutyanov         char *hash, struct va_space *vs)
365*3fa2d384SViktor Prutyanov {
366*3fa2d384SViktor Prutyanov     const char e_magic[2] = "MZ";
367*3fa2d384SViktor Prutyanov     const char Signature[4] = "PE\0\0";
368*3fa2d384SViktor Prutyanov     const char sign_rsds[4] = "RSDS";
369*3fa2d384SViktor Prutyanov     IMAGE_DOS_HEADER *dos_hdr = start_addr;
370*3fa2d384SViktor Prutyanov     IMAGE_NT_HEADERS64 nt_hdrs;
371*3fa2d384SViktor Prutyanov     IMAGE_FILE_HEADER *file_hdr = &nt_hdrs.FileHeader;
372*3fa2d384SViktor Prutyanov     IMAGE_OPTIONAL_HEADER64 *opt_hdr = &nt_hdrs.OptionalHeader;
373*3fa2d384SViktor Prutyanov     IMAGE_DATA_DIRECTORY *data_dir = nt_hdrs.OptionalHeader.DataDirectory;
374*3fa2d384SViktor Prutyanov     IMAGE_DEBUG_DIRECTORY debug_dir;
375*3fa2d384SViktor Prutyanov     OMFSignatureRSDS rsds;
376*3fa2d384SViktor Prutyanov     char *pdb_name;
377*3fa2d384SViktor Prutyanov     size_t pdb_name_sz;
378*3fa2d384SViktor Prutyanov     size_t i;
379*3fa2d384SViktor Prutyanov 
380*3fa2d384SViktor Prutyanov     QEMU_BUILD_BUG_ON(sizeof(*dos_hdr) >= PAGE_SIZE);
381*3fa2d384SViktor Prutyanov 
382*3fa2d384SViktor Prutyanov     if (memcmp(&dos_hdr->e_magic, e_magic, sizeof(e_magic))) {
383*3fa2d384SViktor Prutyanov         return 1;
384*3fa2d384SViktor Prutyanov     }
385*3fa2d384SViktor Prutyanov 
386*3fa2d384SViktor Prutyanov     if (va_space_rw(vs, base + dos_hdr->e_lfanew,
387*3fa2d384SViktor Prutyanov                 &nt_hdrs, sizeof(nt_hdrs), 0)) {
388*3fa2d384SViktor Prutyanov         return 1;
389*3fa2d384SViktor Prutyanov     }
390*3fa2d384SViktor Prutyanov 
391*3fa2d384SViktor Prutyanov     if (memcmp(&nt_hdrs.Signature, Signature, sizeof(Signature)) ||
392*3fa2d384SViktor Prutyanov             file_hdr->Machine != 0x8664 || opt_hdr->Magic != 0x020b) {
393*3fa2d384SViktor Prutyanov         return 1;
394*3fa2d384SViktor Prutyanov     }
395*3fa2d384SViktor Prutyanov 
396*3fa2d384SViktor Prutyanov     printf("Debug Directory RVA = 0x%016x\n",
397*3fa2d384SViktor Prutyanov             data_dir[IMAGE_FILE_DEBUG_DIRECTORY].VirtualAddress);
398*3fa2d384SViktor Prutyanov 
399*3fa2d384SViktor Prutyanov     if (va_space_rw(vs,
400*3fa2d384SViktor Prutyanov                 base + data_dir[IMAGE_FILE_DEBUG_DIRECTORY].VirtualAddress,
401*3fa2d384SViktor Prutyanov                 &debug_dir, sizeof(debug_dir), 0)) {
402*3fa2d384SViktor Prutyanov         return 1;
403*3fa2d384SViktor Prutyanov     }
404*3fa2d384SViktor Prutyanov 
405*3fa2d384SViktor Prutyanov     if (debug_dir.Type != IMAGE_DEBUG_TYPE_CODEVIEW) {
406*3fa2d384SViktor Prutyanov         return 1;
407*3fa2d384SViktor Prutyanov     }
408*3fa2d384SViktor Prutyanov 
409*3fa2d384SViktor Prutyanov     if (va_space_rw(vs,
410*3fa2d384SViktor Prutyanov                 base + debug_dir.AddressOfRawData,
411*3fa2d384SViktor Prutyanov                 &rsds, sizeof(rsds), 0)) {
412*3fa2d384SViktor Prutyanov         return 1;
413*3fa2d384SViktor Prutyanov     }
414*3fa2d384SViktor Prutyanov 
415*3fa2d384SViktor Prutyanov     printf("CodeView signature is \'%.4s\'\n", rsds.Signature);
416*3fa2d384SViktor Prutyanov 
417*3fa2d384SViktor Prutyanov     if (memcmp(&rsds.Signature, sign_rsds, sizeof(sign_rsds))) {
418*3fa2d384SViktor Prutyanov         return 1;
419*3fa2d384SViktor Prutyanov     }
420*3fa2d384SViktor Prutyanov 
421*3fa2d384SViktor Prutyanov     pdb_name_sz = debug_dir.SizeOfData - sizeof(rsds);
422*3fa2d384SViktor Prutyanov     pdb_name = malloc(pdb_name_sz);
423*3fa2d384SViktor Prutyanov     if (!pdb_name) {
424*3fa2d384SViktor Prutyanov         return 1;
425*3fa2d384SViktor Prutyanov     }
426*3fa2d384SViktor Prutyanov 
427*3fa2d384SViktor Prutyanov     if (va_space_rw(vs, base + debug_dir.AddressOfRawData +
428*3fa2d384SViktor Prutyanov                 offsetof(OMFSignatureRSDS, name), pdb_name, pdb_name_sz, 0)) {
429*3fa2d384SViktor Prutyanov         free(pdb_name);
430*3fa2d384SViktor Prutyanov         return 1;
431*3fa2d384SViktor Prutyanov     }
432*3fa2d384SViktor Prutyanov 
433*3fa2d384SViktor Prutyanov     printf("PDB name is \'%s\', \'%s\' expected\n", pdb_name, PDB_NAME);
434*3fa2d384SViktor Prutyanov 
435*3fa2d384SViktor Prutyanov     if (strcmp(pdb_name, PDB_NAME)) {
436*3fa2d384SViktor Prutyanov         eprintf("Unexpected PDB name, it seems the kernel isn't found\n");
437*3fa2d384SViktor Prutyanov         free(pdb_name);
438*3fa2d384SViktor Prutyanov         return 1;
439*3fa2d384SViktor Prutyanov     }
440*3fa2d384SViktor Prutyanov 
441*3fa2d384SViktor Prutyanov     free(pdb_name);
442*3fa2d384SViktor Prutyanov 
443*3fa2d384SViktor Prutyanov     sprintf(hash, "%.08x%.04x%.04x%.02x%.02x", rsds.guid.a, rsds.guid.b,
444*3fa2d384SViktor Prutyanov             rsds.guid.c, rsds.guid.d[0], rsds.guid.d[1]);
445*3fa2d384SViktor Prutyanov     hash += 20;
446*3fa2d384SViktor Prutyanov     for (i = 0; i < 6; i++, hash += 2) {
447*3fa2d384SViktor Prutyanov         sprintf(hash, "%.02x", rsds.guid.e[i]);
448*3fa2d384SViktor Prutyanov     }
449*3fa2d384SViktor Prutyanov 
450*3fa2d384SViktor Prutyanov     sprintf(hash, "%.01x", rsds.age);
451*3fa2d384SViktor Prutyanov 
452*3fa2d384SViktor Prutyanov     return 0;
453*3fa2d384SViktor Prutyanov }
454*3fa2d384SViktor Prutyanov 
455*3fa2d384SViktor Prutyanov int main(int argc, char *argv[])
456*3fa2d384SViktor Prutyanov {
457*3fa2d384SViktor Prutyanov     int err = 0;
458*3fa2d384SViktor Prutyanov     QEMU_Elf qemu_elf;
459*3fa2d384SViktor Prutyanov     struct pa_space ps;
460*3fa2d384SViktor Prutyanov     struct va_space vs;
461*3fa2d384SViktor Prutyanov     QEMUCPUState *state;
462*3fa2d384SViktor Prutyanov     idt_desc_t first_idt_desc;
463*3fa2d384SViktor Prutyanov     uint64_t KernBase;
464*3fa2d384SViktor Prutyanov     void *nt_start_addr = NULL;
465*3fa2d384SViktor Prutyanov     WinDumpHeader64 header;
466*3fa2d384SViktor Prutyanov     char pdb_hash[34];
467*3fa2d384SViktor Prutyanov     char pdb_url[] = SYM_URL_BASE PDB_NAME
468*3fa2d384SViktor Prutyanov         "/0123456789ABCDEF0123456789ABCDEFx/" PDB_NAME;
469*3fa2d384SViktor Prutyanov     struct pdb_reader pdb;
470*3fa2d384SViktor Prutyanov     uint64_t KdDebuggerDataBlock;
471*3fa2d384SViktor Prutyanov     KDDEBUGGER_DATA64 *kdbg;
472*3fa2d384SViktor Prutyanov     uint64_t KdVersionBlock;
473*3fa2d384SViktor Prutyanov 
474*3fa2d384SViktor Prutyanov     if (argc != 3) {
475*3fa2d384SViktor Prutyanov         eprintf("usage:\n\t%s elf_file dmp_file\n", argv[0]);
476*3fa2d384SViktor Prutyanov         return 1;
477*3fa2d384SViktor Prutyanov     }
478*3fa2d384SViktor Prutyanov 
479*3fa2d384SViktor Prutyanov     if (QEMU_Elf_init(&qemu_elf, argv[1])) {
480*3fa2d384SViktor Prutyanov         eprintf("Failed to initialize QEMU ELF dump\n");
481*3fa2d384SViktor Prutyanov         return 1;
482*3fa2d384SViktor Prutyanov     }
483*3fa2d384SViktor Prutyanov 
484*3fa2d384SViktor Prutyanov     if (pa_space_create(&ps, &qemu_elf)) {
485*3fa2d384SViktor Prutyanov         eprintf("Failed to initialize physical address space\n");
486*3fa2d384SViktor Prutyanov         err = 1;
487*3fa2d384SViktor Prutyanov         goto out_elf;
488*3fa2d384SViktor Prutyanov     }
489*3fa2d384SViktor Prutyanov 
490*3fa2d384SViktor Prutyanov     state = qemu_elf.state[0];
491*3fa2d384SViktor Prutyanov     printf("CPU #0 CR3 is 0x%016lx\n", state->cr[3]);
492*3fa2d384SViktor Prutyanov 
493*3fa2d384SViktor Prutyanov     va_space_create(&vs, &ps, state->cr[3]);
494*3fa2d384SViktor Prutyanov     if (fix_dtb(&vs, &qemu_elf)) {
495*3fa2d384SViktor Prutyanov         eprintf("Failed to find paging base\n");
496*3fa2d384SViktor Prutyanov         err = 1;
497*3fa2d384SViktor Prutyanov         goto out_elf;
498*3fa2d384SViktor Prutyanov     }
499*3fa2d384SViktor Prutyanov 
500*3fa2d384SViktor Prutyanov     printf("CPU #0 IDT is at 0x%016lx\n", state->idt.base);
501*3fa2d384SViktor Prutyanov 
502*3fa2d384SViktor Prutyanov     if (va_space_rw(&vs, state->idt.base,
503*3fa2d384SViktor Prutyanov                 &first_idt_desc, sizeof(first_idt_desc), 0)) {
504*3fa2d384SViktor Prutyanov         eprintf("Failed to get CPU #0 IDT[0]\n");
505*3fa2d384SViktor Prutyanov         err = 1;
506*3fa2d384SViktor Prutyanov         goto out_ps;
507*3fa2d384SViktor Prutyanov     }
508*3fa2d384SViktor Prutyanov     printf("CPU #0 IDT[0] -> 0x%016lx\n", idt_desc_addr(first_idt_desc));
509*3fa2d384SViktor Prutyanov 
510*3fa2d384SViktor Prutyanov     KernBase = idt_desc_addr(first_idt_desc) & ~(PAGE_SIZE - 1);
511*3fa2d384SViktor Prutyanov     printf("Searching kernel downwards from 0x%16lx...\n", KernBase);
512*3fa2d384SViktor Prutyanov 
513*3fa2d384SViktor Prutyanov     for (; KernBase >= 0xfffff78000000000; KernBase -= PAGE_SIZE) {
514*3fa2d384SViktor Prutyanov         nt_start_addr = va_space_resolve(&vs, KernBase);
515*3fa2d384SViktor Prutyanov         if (!nt_start_addr) {
516*3fa2d384SViktor Prutyanov             continue;
517*3fa2d384SViktor Prutyanov         }
518*3fa2d384SViktor Prutyanov 
519*3fa2d384SViktor Prutyanov         if (*(uint16_t *)nt_start_addr == 0x5a4d) { /* MZ */
520*3fa2d384SViktor Prutyanov             break;
521*3fa2d384SViktor Prutyanov         }
522*3fa2d384SViktor Prutyanov     }
523*3fa2d384SViktor Prutyanov 
524*3fa2d384SViktor Prutyanov     printf("KernBase = 0x%16lx, signature is \'%.2s\'\n", KernBase,
525*3fa2d384SViktor Prutyanov             (char *)nt_start_addr);
526*3fa2d384SViktor Prutyanov 
527*3fa2d384SViktor Prutyanov     if (pe_get_pdb_symstore_hash(KernBase, nt_start_addr, pdb_hash, &vs)) {
528*3fa2d384SViktor Prutyanov         eprintf("Failed to get PDB symbol store hash\n");
529*3fa2d384SViktor Prutyanov         err = 1;
530*3fa2d384SViktor Prutyanov         goto out_ps;
531*3fa2d384SViktor Prutyanov     }
532*3fa2d384SViktor Prutyanov 
533*3fa2d384SViktor Prutyanov     sprintf(pdb_url, "%s%s/%s/%s", SYM_URL_BASE, PDB_NAME, pdb_hash, PDB_NAME);
534*3fa2d384SViktor Prutyanov     printf("PDB URL is %s\n", pdb_url);
535*3fa2d384SViktor Prutyanov 
536*3fa2d384SViktor Prutyanov     if (download_url(PDB_NAME, pdb_url)) {
537*3fa2d384SViktor Prutyanov         eprintf("Failed to download PDB file\n");
538*3fa2d384SViktor Prutyanov         err = 1;
539*3fa2d384SViktor Prutyanov         goto out_ps;
540*3fa2d384SViktor Prutyanov     }
541*3fa2d384SViktor Prutyanov 
542*3fa2d384SViktor Prutyanov     if (pdb_init_from_file(PDB_NAME, &pdb)) {
543*3fa2d384SViktor Prutyanov         eprintf("Failed to initialize PDB reader\n");
544*3fa2d384SViktor Prutyanov         err = 1;
545*3fa2d384SViktor Prutyanov         goto out_pdb_file;
546*3fa2d384SViktor Prutyanov     }
547*3fa2d384SViktor Prutyanov 
548*3fa2d384SViktor Prutyanov     if (!SYM_RESOLVE(KernBase, &pdb, KdDebuggerDataBlock) ||
549*3fa2d384SViktor Prutyanov             !SYM_RESOLVE(KernBase, &pdb, KdVersionBlock)) {
550*3fa2d384SViktor Prutyanov         err = 1;
551*3fa2d384SViktor Prutyanov         goto out_pdb;
552*3fa2d384SViktor Prutyanov     }
553*3fa2d384SViktor Prutyanov 
554*3fa2d384SViktor Prutyanov     kdbg = get_kdbg(KernBase, &pdb, &vs, KdDebuggerDataBlock);
555*3fa2d384SViktor Prutyanov     if (!kdbg) {
556*3fa2d384SViktor Prutyanov         err = 1;
557*3fa2d384SViktor Prutyanov         goto out_pdb;
558*3fa2d384SViktor Prutyanov     }
559*3fa2d384SViktor Prutyanov 
560*3fa2d384SViktor Prutyanov     if (fill_header(&header, &ps, &vs, KdDebuggerDataBlock, kdbg,
561*3fa2d384SViktor Prutyanov             KdVersionBlock, qemu_elf.state_nr)) {
562*3fa2d384SViktor Prutyanov         err = 1;
563*3fa2d384SViktor Prutyanov         goto out_pdb;
564*3fa2d384SViktor Prutyanov     }
565*3fa2d384SViktor Prutyanov 
566*3fa2d384SViktor Prutyanov     if (fill_context(kdbg, &vs, &qemu_elf)) {
567*3fa2d384SViktor Prutyanov         err = 1;
568*3fa2d384SViktor Prutyanov         goto out_pdb;
569*3fa2d384SViktor Prutyanov     }
570*3fa2d384SViktor Prutyanov 
571*3fa2d384SViktor Prutyanov     if (write_dump(&ps, &header, argv[2])) {
572*3fa2d384SViktor Prutyanov         eprintf("Failed to save dump\n");
573*3fa2d384SViktor Prutyanov         err = 1;
574*3fa2d384SViktor Prutyanov         goto out_kdbg;
575*3fa2d384SViktor Prutyanov     }
576*3fa2d384SViktor Prutyanov 
577*3fa2d384SViktor Prutyanov out_kdbg:
578*3fa2d384SViktor Prutyanov     free(kdbg);
579*3fa2d384SViktor Prutyanov out_pdb:
580*3fa2d384SViktor Prutyanov     pdb_exit(&pdb);
581*3fa2d384SViktor Prutyanov out_pdb_file:
582*3fa2d384SViktor Prutyanov     unlink(PDB_NAME);
583*3fa2d384SViktor Prutyanov out_ps:
584*3fa2d384SViktor Prutyanov     pa_space_destroy(&ps);
585*3fa2d384SViktor Prutyanov out_elf:
586*3fa2d384SViktor Prutyanov     QEMU_Elf_exit(&qemu_elf);
587*3fa2d384SViktor Prutyanov 
588*3fa2d384SViktor Prutyanov     return err;
589*3fa2d384SViktor Prutyanov }
590