xref: /reactos/ntoskrnl/kdbg/kdb.c (revision b15963ab)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS kernel
4  * FILE:            ntoskrnl/kdbg/kdb.c
5  * PURPOSE:         Kernel Debugger
6  *
7  * PROGRAMMERS:     Gregor Anich
8  */
9 
10 /* INCLUDES ******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #include "kdb.h"
14 
15 /* TYPES *********************************************************************/
16 
17 /* DEFINES *******************************************************************/
18 
19 #define KDB_STACK_SIZE                   (4096*3)
20 #ifdef _M_AMD64
21 #define KDB_STACK_ALIGN                 16
22 #define KDB_STACK_RESERVE               (5 * sizeof(PVOID)) /* Home space + return address */
23 #else
24 #define KDB_STACK_ALIGN                 4
25 #define KDB_STACK_RESERVE               sizeof(ULONG) /* Return address */
26 #endif
27 #define KDB_MAXIMUM_BREAKPOINT_COUNT     256
28 #define KDB_MAXIMUM_HW_BREAKPOINT_COUNT  4
29 #define KDB_MAXIMUM_SW_BREAKPOINT_COUNT  256
30 
31 #define __STRING(x) #x
32 #define _STRING(x) __STRING(x)
33 
34 /* GLOBALS *******************************************************************/
35 
36 static LONG KdbEntryCount = 0;
37 static DECLSPEC_ALIGN(KDB_STACK_ALIGN) CHAR KdbStack[KDB_STACK_SIZE];
38 
39 static ULONG KdbBreakPointCount = 0;  /* Number of used breakpoints in the array */
40 static KDB_BREAKPOINT KdbBreakPoints[KDB_MAXIMUM_BREAKPOINT_COUNT] = {{0}};  /* Breakpoint array */
41 static ULONG KdbSwBreakPointCount = 0;  /* Number of enabled software breakpoints */
42 static ULONG KdbHwBreakPointCount = 0;  /* Number of enabled hardware breakpoints */
43 static PKDB_BREAKPOINT KdbSwBreakPoints[KDB_MAXIMUM_SW_BREAKPOINT_COUNT]; /* Enabled software breakpoints, orderless */
44 static PKDB_BREAKPOINT KdbHwBreakPoints[KDB_MAXIMUM_HW_BREAKPOINT_COUNT]; /* Enabled hardware breakpoints, orderless */
45 static PKDB_BREAKPOINT KdbBreakPointToReenable = NULL; /* Set to a breakpoint struct when single stepping after
46                                                           a software breakpoint was hit, to reenable it */
47 static BOOLEAN KdbpEvenThoughWeHaveABreakPointToReenableWeAlsoHaveARealSingleStep;
48 LONG KdbLastBreakPointNr = -1;  /* Index of the breakpoint which cause KDB to be entered */
49 ULONG KdbNumSingleSteps = 0; /* How many single steps to do */
50 BOOLEAN KdbSingleStepOver = FALSE; /* Whether to step over calls/reps. */
51 static BOOLEAN KdbEnteredOnSingleStep = FALSE; /* Set to true when KDB was entered because of single step */
52 PEPROCESS KdbCurrentProcess = NULL;  /* The current process context in which KDB runs */
53 PEPROCESS KdbOriginalProcess = NULL; /* The process in whichs context KDB was intered */
54 PETHREAD KdbCurrentThread = NULL;  /* The current thread context in which KDB runs */
55 PETHREAD KdbOriginalThread = NULL; /* The thread in whichs context KDB was entered */
56 PKDB_KTRAP_FRAME KdbCurrentTrapFrame = NULL; /* Pointer to the current trapframe */
57 static KDB_KTRAP_FRAME KdbTrapFrame = { 0 };  /* The trapframe which was passed to KdbEnterDebuggerException */
58 static KDB_KTRAP_FRAME KdbThreadTrapFrame = { 0 }; /* The trapframe of the current thread (KdbCurrentThread) */
59 static KAPC_STATE KdbApcState;
60 extern BOOLEAN KdbpBugCheckRequested;
61 
62 /* Array of conditions when to enter KDB */
63 static KDB_ENTER_CONDITION KdbEnterConditions[][2] =
64 {
65     /* First chance       Last chance */
66     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 0: Zero divide */
67     { KdbEnterFromKmode,  KdbDoNotEnter },       /* 1: Debug trap */
68     { KdbDoNotEnter,      KdbEnterAlways },      /* 2: NMI */
69     { KdbEnterFromKmode,  KdbDoNotEnter },       /* 3: INT3 */
70     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 4: Overflow */
71     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 5: BOUND range exceeded */
72     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 6: Invalid opcode */
73     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 7: No math coprocessor fault */
74     { KdbEnterAlways,     KdbEnterAlways },      /* 8: Double Fault */
75     { KdbEnterAlways,     KdbEnterAlways },      /* 9: Unknown(9) */
76     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 10: Invalid TSS */
77     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 11: Segment Not Present */
78     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 12: Stack fault */
79     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 13: General protection fault */
80     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 14: Page fault */
81     { KdbEnterAlways,     KdbEnterAlways },      /* 15: Reserved (15) */
82     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 16: FPU fault */
83     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 17: Alignment Check */
84     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 18: Machine Check */
85     { KdbDoNotEnter,      KdbEnterFromKmode },   /* 19: SIMD fault */
86     { KdbEnterFromKmode,  KdbDoNotEnter },       /* 20: Assertion failure */
87     { KdbDoNotEnter,      KdbEnterFromKmode }    /* Last entry: used for unknown exceptions */
88 };
89 
90 /* Exception descriptions */
91 static const CHAR *ExceptionNrToString[] =
92 {
93     "Divide Error",
94     "Debug Trap",
95     "NMI",
96     "Breakpoint",
97     "Overflow",
98     "BOUND range exceeded",
99     "Invalid Opcode",
100     "No Math Coprocessor",
101     "Double Fault",
102     "Unknown(9)",
103     "Invalid TSS",
104     "Segment Not Present",
105     "Stack Segment Fault",
106     "General Protection",
107     "Page Fault",
108     "Reserved(15)",
109     "Math Fault",
110     "Alignment Check",
111     "Machine Check",
112     "SIMD Fault",
113     "Assertion Failure"
114 };
115 
116 /* FUNCTIONS *****************************************************************/
117 
118 static VOID
KdbpKdbTrapFrameFromKernelStack(PVOID KernelStack,PKDB_KTRAP_FRAME KdbTrapFrame)119 KdbpKdbTrapFrameFromKernelStack(
120     PVOID KernelStack,
121     PKDB_KTRAP_FRAME KdbTrapFrame)
122 {
123     ULONG_PTR *StackPtr;
124 
125     RtlZeroMemory(KdbTrapFrame, sizeof(KDB_KTRAP_FRAME));
126     StackPtr = (ULONG_PTR *) KernelStack;
127 #ifdef _M_IX86
128     KdbTrapFrame->Ebp = StackPtr[3];
129     KdbTrapFrame->Edi = StackPtr[4];
130     KdbTrapFrame->Esi = StackPtr[5];
131     KdbTrapFrame->Ebx = StackPtr[6];
132     KdbTrapFrame->Eip = StackPtr[7];
133     KdbTrapFrame->Esp = (ULONG) (StackPtr + 8);
134     KdbTrapFrame->SegSs = KGDT_R0_DATA;
135     KdbTrapFrame->SegCs = KGDT_R0_CODE;
136     KdbTrapFrame->SegDs = KGDT_R0_DATA;
137     KdbTrapFrame->SegEs = KGDT_R0_DATA;
138     KdbTrapFrame->SegGs = KGDT_R0_DATA;
139 #endif
140 
141     /* FIXME: what about the other registers??? */
142 }
143 
144 /*!\brief Overwrites the instruction at \a Address with \a NewInst and stores
145  *        the old instruction in *OldInst.
146  *
147  * \param Process  Process in which's context to overwrite the instruction.
148  * \param Address  Address at which to overwrite the instruction.
149  * \param NewInst  New instruction (written to \a Address)
150  * \param OldInst  Old instruction (read from \a Address)
151  *
152  * \returns NTSTATUS
153  */
154 static NTSTATUS
KdbpOverwriteInstruction(IN PEPROCESS Process,IN ULONG_PTR Address,IN UCHAR NewInst,OUT PUCHAR OldInst OPTIONAL)155 KdbpOverwriteInstruction(
156     IN  PEPROCESS Process,
157     IN  ULONG_PTR Address,
158     IN  UCHAR NewInst,
159     OUT PUCHAR OldInst  OPTIONAL)
160 {
161     NTSTATUS Status;
162     ULONG Protect;
163     PEPROCESS CurrentProcess = PsGetCurrentProcess();
164     KAPC_STATE ApcState;
165 
166     /* Get the protection for the address. */
167     Protect = MmGetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address));
168 
169     /* Return if that page isn't present. */
170     if (Protect & PAGE_NOACCESS)
171     {
172         return STATUS_MEMORY_NOT_ALLOCATED;
173     }
174 
175     /* Attach to the process */
176     if (CurrentProcess != Process)
177     {
178         KeStackAttachProcess(&Process->Pcb, &ApcState);
179     }
180 
181     /* Make the page writeable if it is read only. */
182     if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ))
183     {
184         MmSetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address),
185                          (Protect & ~(PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) | PAGE_READWRITE);
186     }
187 
188     /* Copy the old instruction back to the caller. */
189     if (OldInst)
190     {
191         Status = KdbpSafeReadMemory(OldInst, (PUCHAR)Address, 1);
192         if (!NT_SUCCESS(Status))
193         {
194             if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ))
195             {
196                 MmSetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address), Protect);
197             }
198 
199             /* Detach from process */
200             if (CurrentProcess != Process)
201             {
202                 KeUnstackDetachProcess(&ApcState);
203             }
204 
205             return Status;
206         }
207     }
208 
209     /* Copy the new instruction in its place. */
210     Status = KdbpSafeWriteMemory((PUCHAR)Address, &NewInst, 1);
211 
212     /* Restore the page protection. */
213     if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ))
214     {
215         MmSetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address), Protect);
216     }
217 
218     /* Detach from process */
219     if (CurrentProcess != Process)
220     {
221         KeUnstackDetachProcess(&ApcState);
222     }
223 
224     return Status;
225 }
226 
227 /*!\brief Checks whether the given instruction can be single stepped or has to be
228  *        stepped over using a temporary breakpoint.
229  *
230  * \retval TRUE   Instruction is a call.
231  * \retval FALSE  Instruction is not a call.
232  */
233 BOOLEAN
KdbpShouldStepOverInstruction(ULONG_PTR Eip)234 KdbpShouldStepOverInstruction(
235     ULONG_PTR Eip)
236 {
237     UCHAR Mem[3];
238     ULONG i = 0;
239 
240     if (!NT_SUCCESS(KdbpSafeReadMemory(Mem, (PVOID)Eip, sizeof (Mem))))
241     {
242         KdbPrintf("Couldn't access memory at 0x%p\n", Eip);
243         return FALSE;
244     }
245 
246     /* Check if the current instruction is a call. */
247     while ((i < sizeof (Mem)) && (Mem[i] == 0x66 || Mem[i] == 0x67))
248         i++;
249 
250     if (i == sizeof (Mem))
251         return FALSE;
252 
253     if (Mem[i] == 0xE8 || Mem[i] == 0x9A || Mem[i] == 0xF2 || Mem[i] == 0xF3 ||
254         (((i + 1) < sizeof (Mem)) && Mem[i] == 0xFF && (Mem[i+1] & 0x38) == 0x10))
255     {
256         return TRUE;
257     }
258 
259     return FALSE;
260 }
261 
262 /*!\brief Steps over an instruction
263  *
264  * If the given instruction should be stepped over, this function inserts a
265  * temporary breakpoint after the instruction and returns TRUE, otherwise it
266  * returns FALSE.
267  *
268  * \retval TRUE   Temporary breakpoint set after instruction.
269  * \retval FALSE  No breakpoint was set.
270  */
271 BOOLEAN
KdbpStepOverInstruction(ULONG_PTR Eip)272 KdbpStepOverInstruction(
273     ULONG_PTR Eip)
274 {
275     LONG InstLen;
276 
277     if (!KdbpShouldStepOverInstruction(Eip))
278         return FALSE;
279 
280     InstLen = KdbpGetInstLength(Eip);
281     if (InstLen < 1)
282         return FALSE;
283 
284     if (!NT_SUCCESS(KdbpInsertBreakPoint(Eip + InstLen, KdbBreakPointTemporary, 0, 0, NULL, FALSE, NULL)))
285         return FALSE;
286 
287     return TRUE;
288 }
289 
290 /*!\brief Steps into an instruction (interrupts)
291  *
292  * If the given instruction should be stepped into, this function inserts a
293  * temporary breakpoint at the target instruction and returns TRUE, otherwise it
294  * returns FALSE.
295  *
296  * \retval TRUE   Temporary breakpoint set at target instruction.
297  * \retval FALSE  No breakpoint was set.
298  */
299 BOOLEAN
KdbpStepIntoInstruction(ULONG_PTR Eip)300 KdbpStepIntoInstruction(
301     ULONG_PTR Eip)
302 {
303     KDESCRIPTOR Idtr = {0};
304     UCHAR Mem[2];
305     INT IntVect;
306     ULONG IntDesc[2];
307     ULONG_PTR TargetEip;
308 
309     /* Read memory */
310     if (!NT_SUCCESS(KdbpSafeReadMemory(Mem, (PVOID)Eip, sizeof (Mem))))
311     {
312         // KdbPrintf("Couldn't access memory at 0x%p\n", Eip);
313         return FALSE;
314     }
315 
316     /* Check for INT instruction */
317     /* FIXME: Check for iret */
318     if (Mem[0] == 0xcc)
319         IntVect = 3;
320     else if (Mem[0] == 0xcd)
321         IntVect = Mem[1];
322     else if (Mem[0] == 0xce && KdbCurrentTrapFrame->EFlags & (1<<11)) /* 1 << 11 is the overflow flag */
323         IntVect = 4;
324     else
325         return FALSE;
326 
327     if (IntVect < 32) /* We should be informed about interrupts < 32 by the kernel, no need to breakpoint them */
328     {
329         return FALSE;
330     }
331 
332     /* Read the interrupt descriptor table register  */
333     __sidt(&Idtr.Limit);
334     if (IntVect >= (Idtr.Limit + 1) / 8)
335     {
336         // KdbPrintf("IDT does not contain interrupt vector %d.\n", IntVect);
337         return TRUE;
338     }
339 
340     /* Get the interrupt descriptor */
341     if (!NT_SUCCESS(KdbpSafeReadMemory(IntDesc, (PVOID)((ULONG_PTR)Idtr.Base + (IntVect * 8)), sizeof(IntDesc))))
342     {
343         // KdbPrintf("Couldn't access memory at 0x%p\n", (ULONG_PTR)Idtr.Base + (IntVect * 8));
344         return FALSE;
345     }
346 
347     /* Check descriptor and get target eip (16 bit interrupt/trap gates not supported) */
348     if ((IntDesc[1] & (1 << 15)) == 0) /* not present */
349     {
350         return FALSE;
351     }
352     if ((IntDesc[1] & 0x1f00) == 0x0500) /* Task gate */
353     {
354         /* FIXME: Task gates not supported */
355         return FALSE;
356     }
357     else if (((IntDesc[1] & 0x1fe0) == 0x0e00) || /* 32 bit Interrupt gate */
358              ((IntDesc[1] & 0x1fe0) == 0x0f00))   /* 32 bit Trap gate */
359     {
360         /* FIXME: Should the segment selector of the interrupt gate be checked? */
361         TargetEip = (IntDesc[1] & 0xffff0000) | (IntDesc[0] & 0x0000ffff);
362     }
363     else
364     {
365         return FALSE;
366     }
367 
368     /* Insert breakpoint */
369     if (!NT_SUCCESS(KdbpInsertBreakPoint(TargetEip, KdbBreakPointTemporary, 0, 0, NULL, FALSE, NULL)))
370         return FALSE;
371 
372     return TRUE;
373 }
374 
375 /*!\brief Gets the number of the next breakpoint >= Start.
376  *
377  * \param Start   Breakpoint number to start searching at. -1 if no more breakpoints are found.
378  *
379  * \returns Breakpoint number (-1 if no more breakpoints are found)
380  */
381 LONG
KdbpGetNextBreakPointNr(IN ULONG Start OPTIONAL)382 KdbpGetNextBreakPointNr(
383     IN ULONG Start  OPTIONAL)
384 {
385     for (; Start < RTL_NUMBER_OF(KdbBreakPoints); Start++)
386     {
387         if (KdbBreakPoints[Start].Type != KdbBreakPointNone)
388             return Start;
389     }
390 
391     return -1;
392 }
393 
394 /*!\brief Returns information of the specified breakpoint.
395  *
396  * \param BreakPointNr         Number of the breakpoint to return information of.
397  * \param Address              Receives the address of the breakpoint.
398  * \param Type                 Receives the type of the breakpoint (hardware or software)
399  * \param Size                 Size - for memory breakpoints.
400  * \param AccessType           Access type - for hardware breakpoints.
401  * \param DebugReg             Debug register - for enabled hardware breakpoints.
402  * \param Enabled              Whether the breakpoint is enabled or not.
403  * \param Process              The owning process of the breakpoint.
404  * \param ConditionExpression  The expression which was given as condition for the bp.
405  *
406  * \returns NULL on failure, pointer to a KDB_BREAKPOINT struct on success.
407  */
408 BOOLEAN
KdbpGetBreakPointInfo(IN ULONG BreakPointNr,OUT ULONG_PTR * Address OPTIONAL,OUT KDB_BREAKPOINT_TYPE * Type OPTIONAL,OUT UCHAR * Size OPTIONAL,OUT KDB_ACCESS_TYPE * AccessType OPTIONAL,OUT UCHAR * DebugReg OPTIONAL,OUT BOOLEAN * Enabled OPTIONAL,OUT BOOLEAN * Global OPTIONAL,OUT PEPROCESS * Process OPTIONAL,OUT PCHAR * ConditionExpression OPTIONAL)409 KdbpGetBreakPointInfo(
410     IN  ULONG BreakPointNr,
411     OUT ULONG_PTR *Address  OPTIONAL,
412     OUT KDB_BREAKPOINT_TYPE *Type  OPTIONAL,
413     OUT UCHAR *Size  OPTIONAL,
414     OUT KDB_ACCESS_TYPE *AccessType  OPTIONAL,
415     OUT UCHAR *DebugReg  OPTIONAL,
416     OUT BOOLEAN *Enabled  OPTIONAL,
417     OUT BOOLEAN *Global  OPTIONAL,
418     OUT PEPROCESS *Process  OPTIONAL,
419     OUT PCHAR *ConditionExpression  OPTIONAL)
420 {
421     PKDB_BREAKPOINT bp;
422 
423     if (BreakPointNr >= RTL_NUMBER_OF(KdbBreakPoints) ||
424         KdbBreakPoints[BreakPointNr].Type == KdbBreakPointNone)
425     {
426         return FALSE;
427     }
428 
429     bp = KdbBreakPoints + BreakPointNr;
430     if (Address)
431         *Address = bp->Address;
432 
433     if (Type)
434         *Type = bp->Type;
435 
436     if (bp->Type == KdbBreakPointHardware)
437     {
438         if (Size)
439             *Size = bp->Data.Hw.Size;
440 
441         if (AccessType)
442             *AccessType = bp->Data.Hw.AccessType;
443 
444         if (DebugReg && bp->Enabled)
445             *DebugReg = bp->Data.Hw.DebugReg;
446     }
447 
448     if (Enabled)
449         *Enabled = bp->Enabled;
450 
451     if (Global)
452         *Global = bp->Global;
453 
454     if (Process)
455         *Process = bp->Process;
456 
457     if (ConditionExpression)
458         *ConditionExpression = bp->ConditionExpression;
459 
460     return TRUE;
461 }
462 
463 /*!\brief Inserts a breakpoint into the breakpoint array.
464  *
465  * The \a Process of the breakpoint is set to \a KdbCurrentProcess
466  *
467  * \param Address              Address at which to set the breakpoint.
468  * \param Type                 Type of breakpoint (hardware or software)
469  * \param Size                 Size of breakpoint (for hardware/memory breakpoints)
470  * \param AccessType           Access type (for hardware breakpoins)
471  * \param ConditionExpression  Expression which must evaluate to true for conditional breakpoints.
472  * \param Global               Wether the breakpoint is global or local to a process.
473  * \param BreakPointNumber     Receives the breakpoint number on success
474  *
475  * \returns NTSTATUS
476  */
477 NTSTATUS
KdbpInsertBreakPoint(IN ULONG_PTR Address,IN KDB_BREAKPOINT_TYPE Type,IN UCHAR Size OPTIONAL,IN KDB_ACCESS_TYPE AccessType OPTIONAL,IN PCHAR ConditionExpression OPTIONAL,IN BOOLEAN Global,OUT PLONG BreakPointNr OPTIONAL)478 KdbpInsertBreakPoint(
479     IN  ULONG_PTR Address,
480     IN  KDB_BREAKPOINT_TYPE Type,
481     IN  UCHAR Size  OPTIONAL,
482     IN  KDB_ACCESS_TYPE AccessType  OPTIONAL,
483     IN  PCHAR ConditionExpression  OPTIONAL,
484     IN  BOOLEAN Global,
485     OUT PLONG BreakPointNr  OPTIONAL)
486 {
487     LONG_PTR i;
488     PVOID Condition;
489     PCHAR ConditionExpressionDup;
490     LONG ErrOffset;
491     CHAR ErrMsg[128];
492 
493     ASSERT(Type != KdbBreakPointNone);
494 
495     if (Type == KdbBreakPointHardware)
496     {
497         if ((Address % Size) != 0)
498         {
499             KdbPrintf("Address (0x%p) must be aligned to a multiple of the size (%d)\n", Address, Size);
500             return STATUS_UNSUCCESSFUL;
501         }
502 
503         if (AccessType == KdbAccessExec && Size != 1)
504         {
505             KdbPuts("Size must be 1 for execution breakpoints.\n");
506             return STATUS_UNSUCCESSFUL;
507         }
508     }
509 
510     if (KdbBreakPointCount == KDB_MAXIMUM_BREAKPOINT_COUNT)
511     {
512         return STATUS_UNSUCCESSFUL;
513     }
514 
515     /* Parse condition expression string and duplicate it */
516     if (ConditionExpression)
517     {
518         Condition = KdbpRpnParseExpression(ConditionExpression, &ErrOffset, ErrMsg);
519         if (!Condition)
520         {
521             if (ErrOffset >= 0)
522                 KdbPrintf("Couldn't parse expression: %s at character %d\n", ErrMsg, ErrOffset);
523             else
524                 KdbPrintf("Couldn't parse expression: %s", ErrMsg);
525 
526             return STATUS_UNSUCCESSFUL;
527         }
528 
529         i = strlen(ConditionExpression) + 1;
530         ConditionExpressionDup = ExAllocatePoolWithTag(NonPagedPool, i, TAG_KDBG);
531         RtlCopyMemory(ConditionExpressionDup, ConditionExpression, i);
532     }
533     else
534     {
535         Condition = NULL;
536         ConditionExpressionDup = NULL;
537     }
538 
539     /* Find unused breakpoint */
540     if (Type == KdbBreakPointTemporary)
541     {
542         for (i = RTL_NUMBER_OF(KdbBreakPoints) - 1; i >= 0; i--)
543         {
544             if (KdbBreakPoints[i].Type == KdbBreakPointNone)
545                 break;
546         }
547     }
548     else
549     {
550         for (i = 0; i < (LONG)RTL_NUMBER_OF(KdbBreakPoints); i++)
551         {
552             if (KdbBreakPoints[i].Type == KdbBreakPointNone)
553                 break;
554         }
555     }
556 
557     ASSERT(i < (LONG)RTL_NUMBER_OF(KdbBreakPoints));
558 
559     /* Set the breakpoint */
560     ASSERT(KdbCurrentProcess);
561     KdbBreakPoints[i].Type = Type;
562     KdbBreakPoints[i].Address = Address;
563     KdbBreakPoints[i].Enabled = FALSE;
564     KdbBreakPoints[i].Global = Global;
565     KdbBreakPoints[i].Process = KdbCurrentProcess;
566     KdbBreakPoints[i].ConditionExpression = ConditionExpressionDup;
567     KdbBreakPoints[i].Condition = Condition;
568 
569     if (Type == KdbBreakPointHardware)
570     {
571         KdbBreakPoints[i].Data.Hw.Size = Size;
572         KdbBreakPoints[i].Data.Hw.AccessType = AccessType;
573     }
574 
575     KdbBreakPointCount++;
576 
577     if (Type != KdbBreakPointTemporary)
578         KdbPrintf("Breakpoint %d inserted.\n", i);
579 
580     /* Try to enable the breakpoint */
581     KdbpEnableBreakPoint(i, NULL);
582 
583     /* Return the breakpoint number */
584     if (BreakPointNr)
585         *BreakPointNr = i;
586 
587     return STATUS_SUCCESS;
588 }
589 
590 /*!\brief Deletes a breakpoint
591  *
592  * \param BreakPointNr  Number of the breakpoint to delete. Can be -1
593  * \param BreakPoint    Breakpoint to delete. Can be NULL.
594  *
595  * \retval TRUE   Success.
596  * \retval FALSE  Failure (invalid breakpoint number)
597  */
598 BOOLEAN
KdbpDeleteBreakPoint(IN LONG BreakPointNr OPTIONAL,IN OUT PKDB_BREAKPOINT BreakPoint OPTIONAL)599 KdbpDeleteBreakPoint(
600     IN LONG BreakPointNr  OPTIONAL,
601     IN OUT PKDB_BREAKPOINT BreakPoint  OPTIONAL)
602 {
603     if (BreakPointNr < 0)
604     {
605         ASSERT(BreakPoint);
606         BreakPointNr = BreakPoint - KdbBreakPoints;
607     }
608 
609     if (BreakPointNr < 0 || BreakPointNr >= KDB_MAXIMUM_BREAKPOINT_COUNT)
610     {
611         KdbPrintf("Invalid breakpoint: %d\n", BreakPointNr);
612         return FALSE;
613     }
614 
615     if (!BreakPoint)
616     {
617         BreakPoint = KdbBreakPoints + BreakPointNr;
618     }
619 
620     if (BreakPoint->Type == KdbBreakPointNone)
621     {
622         KdbPrintf("Invalid breakpoint: %d\n", BreakPointNr);
623         return FALSE;
624     }
625 
626     if (BreakPoint->Enabled && !KdbpDisableBreakPoint(-1, BreakPoint))
627         return FALSE;
628 
629     if (BreakPoint->Type != KdbBreakPointTemporary)
630         KdbPrintf("Breakpoint %d deleted.\n", BreakPointNr);
631 
632     BreakPoint->Type = KdbBreakPointNone;
633     KdbBreakPointCount--;
634 
635     return TRUE;
636 }
637 
638 /*!\brief Checks if the breakpoint was set by the debugger
639  *
640  * Tries to find a breakpoint in the breakpoint array which caused
641  * the debug exception to happen.
642  *
643  * \param ExpNr      Exception Number (1 or 3)
644  * \param TrapFrame  Exception trapframe
645  *
646  * \returns Breakpoint number, -1 on error.
647  */
648 static LONG
KdbpIsBreakPointOurs(IN NTSTATUS ExceptionCode,IN PCONTEXT Context)649 KdbpIsBreakPointOurs(
650     IN NTSTATUS ExceptionCode,
651     IN PCONTEXT Context)
652 {
653     ULONG i;
654     ASSERT(ExceptionCode == STATUS_SINGLE_STEP || ExceptionCode == STATUS_BREAKPOINT);
655 
656     if (ExceptionCode == STATUS_BREAKPOINT) /* Software interrupt */
657     {
658         ULONG_PTR BpPc = KeGetContextPc(Context) - 1; /* Get EIP of INT3 instruction */
659         for (i = 0; i < KdbSwBreakPointCount; i++)
660         {
661             ASSERT((KdbSwBreakPoints[i]->Type == KdbBreakPointSoftware ||
662                    KdbSwBreakPoints[i]->Type == KdbBreakPointTemporary));
663             ASSERT(KdbSwBreakPoints[i]->Enabled);
664 
665             if (KdbSwBreakPoints[i]->Address == BpPc)
666             {
667                 return KdbSwBreakPoints[i] - KdbBreakPoints;
668             }
669         }
670     }
671     else if (ExceptionCode == STATUS_SINGLE_STEP) /* Hardware interrupt */
672     {
673         UCHAR DebugReg;
674 
675         for (i = 0; i < KdbHwBreakPointCount; i++)
676         {
677             ASSERT(KdbHwBreakPoints[i]->Type == KdbBreakPointHardware &&
678                    KdbHwBreakPoints[i]->Enabled);
679             DebugReg = KdbHwBreakPoints[i]->Data.Hw.DebugReg;
680 
681             if ((Context->Dr6 & ((ULONG_PTR)1 << DebugReg)) != 0)
682             {
683                 return KdbHwBreakPoints[i] - KdbBreakPoints;
684             }
685         }
686     }
687 
688     return -1;
689 }
690 
691 /*!\brief Enables a breakpoint.
692  *
693  * \param BreakPointNr  Number of the breakpoint to enable Can be -1.
694  * \param BreakPoint    Breakpoint to enable. Can be NULL.
695  *
696  * \retval TRUE   Success.
697  * \retval FALSE  Failure.
698  *
699  * \sa KdbpDisableBreakPoint
700  */
701 BOOLEAN
KdbpEnableBreakPoint(IN LONG BreakPointNr OPTIONAL,IN OUT PKDB_BREAKPOINT BreakPoint OPTIONAL)702 KdbpEnableBreakPoint(
703     IN LONG BreakPointNr  OPTIONAL,
704     IN OUT PKDB_BREAKPOINT BreakPoint  OPTIONAL)
705 {
706     NTSTATUS Status;
707     INT i;
708     ULONG ul;
709 
710     if (BreakPointNr < 0)
711     {
712         ASSERT(BreakPoint);
713         BreakPointNr = BreakPoint - KdbBreakPoints;
714     }
715 
716     if (BreakPointNr < 0 || BreakPointNr >= KDB_MAXIMUM_BREAKPOINT_COUNT)
717     {
718         KdbPrintf("Invalid breakpoint: %d\n", BreakPointNr);
719         return FALSE;
720     }
721 
722     if (!BreakPoint)
723     {
724         BreakPoint = KdbBreakPoints + BreakPointNr;
725     }
726 
727     if (BreakPoint->Type == KdbBreakPointNone)
728     {
729         KdbPrintf("Invalid breakpoint: %d\n", BreakPointNr);
730         return FALSE;
731     }
732 
733     if (BreakPoint->Enabled)
734     {
735         KdbPrintf("Breakpoint %d is already enabled.\n", BreakPointNr);
736         return TRUE;
737     }
738 
739     if (BreakPoint->Type == KdbBreakPointSoftware ||
740         BreakPoint->Type == KdbBreakPointTemporary)
741     {
742         if (KdbSwBreakPointCount >= KDB_MAXIMUM_SW_BREAKPOINT_COUNT)
743         {
744             KdbPrintf("Maximum number of SW breakpoints (%d) used. "
745                       "Disable another breakpoint in order to enable this one.\n",
746                       KDB_MAXIMUM_SW_BREAKPOINT_COUNT);
747             return FALSE;
748         }
749 
750         Status = KdbpOverwriteInstruction(BreakPoint->Process, BreakPoint->Address,
751                                           0xCC, &BreakPoint->Data.SavedInstruction);
752         if (!NT_SUCCESS(Status))
753         {
754             KdbPrintf("Couldn't access memory at 0x%p\n", BreakPoint->Address);
755             return FALSE;
756         }
757 
758         KdbSwBreakPoints[KdbSwBreakPointCount++] = BreakPoint;
759     }
760     else
761     {
762         if (BreakPoint->Data.Hw.AccessType == KdbAccessExec)
763             ASSERT(BreakPoint->Data.Hw.Size == 1);
764 
765         ASSERT((BreakPoint->Address % BreakPoint->Data.Hw.Size) == 0);
766 
767         if (KdbHwBreakPointCount >= KDB_MAXIMUM_HW_BREAKPOINT_COUNT)
768         {
769             KdbPrintf("Maximum number of HW breakpoints (%d) already used. "
770                       "Disable another breakpoint in order to enable this one.\n",
771                       KDB_MAXIMUM_HW_BREAKPOINT_COUNT);
772             return FALSE;
773         }
774 
775         /* Find unused hw breakpoint */
776         ASSERT(KDB_MAXIMUM_HW_BREAKPOINT_COUNT == 4);
777         for (i = 0; i < KDB_MAXIMUM_HW_BREAKPOINT_COUNT; i++)
778         {
779             if ((KdbTrapFrame.Dr7 & (0x3 << (i * 2))) == 0)
780                 break;
781         }
782 
783         ASSERT(i < KDB_MAXIMUM_HW_BREAKPOINT_COUNT);
784 
785         /* Set the breakpoint address. */
786         switch (i)
787         {
788             case 0:
789                 KdbTrapFrame.Dr0 = BreakPoint->Address;
790                 break;
791             case 1:
792                 KdbTrapFrame.Dr1 = BreakPoint->Address;
793                 break;
794             case 2:
795                 KdbTrapFrame.Dr2 = BreakPoint->Address;
796                 break;
797             case 3:
798                 KdbTrapFrame.Dr3 = BreakPoint->Address;
799                 break;
800         }
801 
802         /* Enable the global breakpoint */
803         KdbTrapFrame.Dr7 |= (0x2 << (i * 2));
804 
805         /* Enable the exact match bits. */
806         KdbTrapFrame.Dr7 |= 0x00000300;
807 
808         /* Clear existing state. */
809         KdbTrapFrame.Dr7 &= ~(0xF << (16 + (i * 4)));
810 
811         /* Set the breakpoint type. */
812         switch (BreakPoint->Data.Hw.AccessType)
813         {
814             case KdbAccessExec:
815                 ul = 0;
816                 break;
817             case KdbAccessWrite:
818                 ul = 1;
819                 break;
820             case KdbAccessRead:
821             case KdbAccessReadWrite:
822                 ul = 3;
823                 break;
824             default:
825                 ASSERT(0);
826                 return TRUE;
827                 break;
828         }
829 
830         KdbTrapFrame.Dr7 |= (ul << (16 + (i * 4)));
831 
832         /* Set the breakpoint length. */
833         KdbTrapFrame.Dr7 |= ((BreakPoint->Data.Hw.Size - 1) << (18 + (i * 4)));
834 
835         /* Update KdbCurrentTrapFrame - values are taken from there by the CLI */
836         if (&KdbTrapFrame != KdbCurrentTrapFrame)
837         {
838             KdbCurrentTrapFrame->Dr0 = KdbTrapFrame.Dr0;
839             KdbCurrentTrapFrame->Dr1 = KdbTrapFrame.Dr1;
840             KdbCurrentTrapFrame->Dr2 = KdbTrapFrame.Dr2;
841             KdbCurrentTrapFrame->Dr3 = KdbTrapFrame.Dr3;
842             KdbCurrentTrapFrame->Dr6 = KdbTrapFrame.Dr6;
843             KdbCurrentTrapFrame->Dr7 = KdbTrapFrame.Dr7;
844         }
845 
846         BreakPoint->Data.Hw.DebugReg = i;
847         KdbHwBreakPoints[KdbHwBreakPointCount++] = BreakPoint;
848     }
849 
850     BreakPoint->Enabled = TRUE;
851     if (BreakPoint->Type != KdbBreakPointTemporary)
852         KdbPrintf("Breakpoint %d enabled.\n", BreakPointNr);
853 
854     return TRUE;
855 }
856 
857 /*!\brief Disables a breakpoint.
858  *
859  * \param BreakPointNr  Number of the breakpoint to disable. Can be -1
860  * \param BreakPoint    Breakpoint to disable. Can be NULL.
861  *
862  * \retval TRUE   Success.
863  * \retval FALSE  Failure.
864  *
865  * \sa KdbpEnableBreakPoint
866  */
867 BOOLEAN
KdbpDisableBreakPoint(IN LONG BreakPointNr OPTIONAL,IN OUT PKDB_BREAKPOINT BreakPoint OPTIONAL)868 KdbpDisableBreakPoint(
869     IN LONG BreakPointNr  OPTIONAL,
870     IN OUT PKDB_BREAKPOINT BreakPoint  OPTIONAL)
871 {
872     ULONG i;
873     NTSTATUS Status;
874 
875     if (BreakPointNr < 0)
876     {
877         ASSERT(BreakPoint);
878         BreakPointNr = BreakPoint - KdbBreakPoints;
879     }
880 
881     if (BreakPointNr < 0 || BreakPointNr >= KDB_MAXIMUM_BREAKPOINT_COUNT)
882     {
883         KdbPrintf("Invalid breakpoint: %d\n", BreakPointNr);
884         return FALSE;
885     }
886 
887     if (!BreakPoint)
888     {
889         BreakPoint = KdbBreakPoints + BreakPointNr;
890     }
891 
892     if (BreakPoint->Type == KdbBreakPointNone)
893     {
894         KdbPrintf("Invalid breakpoint: %d\n", BreakPointNr);
895         return FALSE;
896     }
897 
898     if (BreakPoint->Enabled == FALSE)
899     {
900         KdbPrintf("Breakpoint %d is not enabled.\n", BreakPointNr);
901         return TRUE;
902     }
903 
904     if (BreakPoint->Type == KdbBreakPointSoftware ||
905         BreakPoint->Type == KdbBreakPointTemporary)
906     {
907         ASSERT(KdbSwBreakPointCount > 0);
908         Status = KdbpOverwriteInstruction(BreakPoint->Process, BreakPoint->Address,
909                                           BreakPoint->Data.SavedInstruction, NULL);
910 
911         if (!NT_SUCCESS(Status))
912         {
913             KdbPuts("Couldn't restore original instruction.\n");
914             return FALSE;
915         }
916 
917         for (i = 0; i < KdbSwBreakPointCount; i++)
918         {
919             if (KdbSwBreakPoints[i] == BreakPoint)
920             {
921                 KdbSwBreakPoints[i] = KdbSwBreakPoints[--KdbSwBreakPointCount];
922                 i = -1; /* if the last breakpoint is disabled dont break with i >= KdbSwBreakPointCount */
923                 break;
924             }
925         }
926 
927         if (i != MAXULONG) /* not found */
928             ASSERT(0);
929     }
930     else
931     {
932         ASSERT(BreakPoint->Type == KdbBreakPointHardware);
933 
934         /* Clear the breakpoint. */
935         KdbTrapFrame.Dr7 &= ~(0x3 << (BreakPoint->Data.Hw.DebugReg * 2));
936         if ((KdbTrapFrame.Dr7 & 0xFF) == 0)
937         {
938             /* If no breakpoints are enabled then clear the exact match flags. */
939             KdbTrapFrame.Dr7 &= 0xFFFFFCFF;
940         }
941 
942         for (i = 0; i < KdbHwBreakPointCount; i++)
943         {
944             if (KdbHwBreakPoints[i] == BreakPoint)
945             {
946                 KdbHwBreakPoints[i] = KdbHwBreakPoints[--KdbHwBreakPointCount];
947                 i = -1; /* if the last breakpoint is disabled dont break with i >= KdbHwBreakPointCount */
948                 break;
949             }
950         }
951 
952         if (i != MAXULONG) /* not found */
953             ASSERT(0);
954     }
955 
956     BreakPoint->Enabled = FALSE;
957     if (BreakPoint->Type != KdbBreakPointTemporary)
958         KdbPrintf("Breakpoint %d disabled.\n", BreakPointNr);
959 
960     return TRUE;
961 }
962 
963 /*!\brief Gets the first or last chance enter-condition for exception nr. \a ExceptionNr
964  *
965  * \param ExceptionNr  Number of the exception to get condition of.
966  * \param FirstChance  Whether to get first or last chance condition.
967  * \param Condition    Receives the condition setting.
968  *
969  * \retval TRUE   Success.
970  * \retval FALSE  Failure (invalid exception nr)
971  */
972 BOOLEAN
KdbpGetEnterCondition(IN LONG ExceptionNr,IN BOOLEAN FirstChance,OUT KDB_ENTER_CONDITION * Condition)973 KdbpGetEnterCondition(
974     IN LONG ExceptionNr,
975     IN BOOLEAN FirstChance,
976     OUT KDB_ENTER_CONDITION *Condition)
977 {
978     if (ExceptionNr >= (LONG)RTL_NUMBER_OF(KdbEnterConditions))
979         return FALSE;
980 
981     *Condition = KdbEnterConditions[ExceptionNr][FirstChance ? 0 : 1];
982     return TRUE;
983 }
984 
985 /*!\brief Sets the first or last chance enter-condition for exception nr. \a ExceptionNr
986  *
987  * \param ExceptionNr  Number of the exception to set condition of (-1 for all)
988  * \param FirstChance  Whether to set first or last chance condition.
989  * \param Condition    The new condition setting.
990  *
991  * \retval TRUE   Success.
992  * \retval FALSE  Failure (invalid exception nr)
993  */
994 BOOLEAN
KdbpSetEnterCondition(IN LONG ExceptionNr,IN BOOLEAN FirstChance,IN KDB_ENTER_CONDITION Condition)995 KdbpSetEnterCondition(
996     IN LONG ExceptionNr,
997     IN BOOLEAN FirstChance,
998     IN KDB_ENTER_CONDITION Condition)
999 {
1000     if (ExceptionNr < 0)
1001     {
1002         for (ExceptionNr = 0; ExceptionNr < (LONG)RTL_NUMBER_OF(KdbEnterConditions); ExceptionNr++)
1003         {
1004             if (ExceptionNr == 1 || ExceptionNr == 8 ||
1005                 ExceptionNr == 9 || ExceptionNr == 15) /* Reserved exceptions */
1006             {
1007                 continue;
1008             }
1009 
1010             KdbEnterConditions[ExceptionNr][FirstChance ? 0 : 1] = Condition;
1011         }
1012     }
1013     else
1014     {
1015         if (ExceptionNr >= (LONG)RTL_NUMBER_OF(KdbEnterConditions) ||
1016             ExceptionNr == 1 || ExceptionNr == 8 || /* Do not allow changing of the debug */
1017             ExceptionNr == 9 || ExceptionNr == 15)  /* trap or reserved exceptions */
1018         {
1019             return FALSE;
1020         }
1021 
1022         KdbEnterConditions[ExceptionNr][FirstChance ? 0 : 1] = Condition;
1023     }
1024 
1025     return TRUE;
1026 }
1027 
1028 /*!\brief Switches to another thread context
1029  *
1030  * \param ThreadId  Id of the thread to switch to.
1031  *
1032  * \retval TRUE   Success.
1033  * \retval FALSE  Failure (i.e. invalid thread id)
1034  */
1035 BOOLEAN
KdbpAttachToThread(PVOID ThreadId)1036 KdbpAttachToThread(
1037     PVOID ThreadId)
1038 {
1039     PETHREAD Thread = NULL;
1040     PEPROCESS Process;
1041 
1042     /* Get a pointer to the thread */
1043     if (!NT_SUCCESS(PsLookupThreadByThreadId(ThreadId, &Thread)))
1044     {
1045         KdbpPrint("Invalid thread id: 0x%08x\n", (ULONG_PTR)ThreadId);
1046         return FALSE;
1047     }
1048     Process = Thread->ThreadsProcess;
1049 
1050     if (KeIsExecutingDpc() && Process != KdbCurrentProcess)
1051     {
1052         KdbpPrint("Cannot attach to thread within another process while executing a DPC.\n");
1053         ObDereferenceObject(Thread);
1054         return FALSE;
1055     }
1056 
1057     /* Save the current thread's context (if we previously attached to a thread) */
1058     if (KdbCurrentThread != KdbOriginalThread)
1059     {
1060         ASSERT(KdbCurrentTrapFrame == &KdbThreadTrapFrame);
1061         /* Actually, we can't save the context, there's no guarantee that there was a trap frame */
1062     }
1063     else
1064     {
1065         ASSERT(KdbCurrentTrapFrame == &KdbTrapFrame);
1066     }
1067 
1068     /* Switch to the thread's context */
1069     if (Thread != KdbOriginalThread)
1070     {
1071         /* The thread we're attaching to isn't the thread on which we entered
1072          * kdb and so the thread we're attaching to is not running. There
1073          * is no guarantee that it actually has a trap frame. So we have to
1074          * peek directly at the registers which were saved on the stack when the
1075          * thread was preempted in the scheduler */
1076         KdbpKdbTrapFrameFromKernelStack(Thread->Tcb.KernelStack,
1077                                         &KdbThreadTrapFrame);
1078         KdbCurrentTrapFrame = &KdbThreadTrapFrame;
1079     }
1080     else /* Switching back to original thread */
1081     {
1082         KdbCurrentTrapFrame = &KdbTrapFrame;
1083     }
1084     KdbCurrentThread = Thread;
1085 
1086     /* Attach to the thread's process */
1087     ASSERT(KdbCurrentProcess == PsGetCurrentProcess());
1088     if (KdbCurrentProcess != Process)
1089     {
1090         if (KdbCurrentProcess != KdbOriginalProcess) /* detach from previously attached process */
1091         {
1092             KeUnstackDetachProcess(&KdbApcState);
1093         }
1094 
1095         if (KdbOriginalProcess != Process)
1096         {
1097             KeStackAttachProcess(&Process->Pcb, &KdbApcState);
1098         }
1099 
1100         KdbCurrentProcess = Process;
1101     }
1102 
1103     ObDereferenceObject(Thread);
1104     return TRUE;
1105 }
1106 
1107 /*!\brief Switches to another process/thread context
1108  *
1109  * This function switches to the first thread in the specified process.
1110  *
1111  * \param ProcessId  Id of the process to switch to.
1112  *
1113  * \retval TRUE   Success.
1114  * \retval FALSE  Failure (i.e. invalid process id)
1115  */
1116 BOOLEAN
KdbpAttachToProcess(PVOID ProcessId)1117 KdbpAttachToProcess(
1118     PVOID ProcessId)
1119 {
1120     PEPROCESS Process = NULL;
1121     PETHREAD Thread;
1122     PLIST_ENTRY Entry;
1123 
1124     /* Get a pointer to the process */
1125     if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process)))
1126     {
1127         KdbpPrint("Invalid process id: 0x%08x\n", (ULONG_PTR)ProcessId);
1128         return FALSE;
1129     }
1130 
1131     Entry = Process->ThreadListHead.Flink;
1132     ObDereferenceObject(Process);
1133     if (Entry == &KdbCurrentProcess->ThreadListHead)
1134     {
1135         KdbpPrint("No threads in process 0x%p, cannot attach to process!\n", ProcessId);
1136         return FALSE;
1137     }
1138 
1139     Thread = CONTAINING_RECORD(Entry, ETHREAD, ThreadListEntry);
1140 
1141     return KdbpAttachToThread(Thread->Cid.UniqueThread);
1142 }
1143 
1144 /**
1145  * @brief   Calls the main interactive debugger loop.
1146  **/
1147 static VOID
KdbpCallMainLoop(VOID)1148 KdbpCallMainLoop(VOID)
1149 {
1150     KdbpCliMainLoop(KdbEnteredOnSingleStep);
1151 }
1152 
1153 /**
1154  * @brief
1155  * Internal function to enter KDBG and run the specified procedure.
1156  *
1157  * Disables interrupts, releases display ownership, ...
1158  *
1159  * @param[in]   Procedure
1160  * The procedure to execute under the KDBG environment.
1161  * Either execute the main interactive debugger loop (KdbpCallMainLoop)
1162  * or run the KDBinit file (KdbpCliInterpretInitFile).
1163  **/
1164 static VOID
KdbpInternalEnter(_In_ VOID (* Procedure)(VOID))1165 KdbpInternalEnter(
1166     _In_ VOID (*Procedure)(VOID))
1167 {
1168     PETHREAD Thread;
1169     PVOID SavedInitialStack, SavedStackBase, SavedKernelStack;
1170     ULONG SavedStackLimit;
1171 
1172     KbdDisableMouse();
1173 
1174     /* Take control of the display */
1175     if (KdpDebugMode.Screen)
1176         KdpScreenAcquire();
1177 
1178     /* Call the specified debugger procedure on a different stack */
1179     Thread = PsGetCurrentThread();
1180     SavedInitialStack = Thread->Tcb.InitialStack;
1181     SavedStackBase = Thread->Tcb.StackBase;
1182     SavedStackLimit = Thread->Tcb.StackLimit;
1183     SavedKernelStack = Thread->Tcb.KernelStack;
1184     Thread->Tcb.InitialStack = Thread->Tcb.StackBase = (char*)KdbStack + KDB_STACK_SIZE;
1185     Thread->Tcb.StackLimit = (ULONG_PTR)KdbStack;
1186     Thread->Tcb.KernelStack = (char*)KdbStack + KDB_STACK_SIZE;
1187 
1188     // KdbPrintf("Switching to KDB stack 0x%08x-0x%08x (Current Stack is 0x%08x)\n",
1189     //           Thread->Tcb.StackLimit, Thread->Tcb.StackBase, Esp);
1190 
1191     KdbpStackSwitchAndCall(KdbStack + KDB_STACK_SIZE - KDB_STACK_RESERVE, Procedure);
1192 
1193     Thread->Tcb.InitialStack = SavedInitialStack;
1194     Thread->Tcb.StackBase = SavedStackBase;
1195     Thread->Tcb.StackLimit = SavedStackLimit;
1196     Thread->Tcb.KernelStack = SavedKernelStack;
1197 
1198     /* Release the display */
1199     if (KdpDebugMode.Screen)
1200         KdpScreenRelease();
1201 
1202     KbdEnableMouse();
1203 }
1204 
1205 static ULONG
KdbpGetExceptionNumberFromStatus(IN NTSTATUS ExceptionCode)1206 KdbpGetExceptionNumberFromStatus(
1207     IN NTSTATUS ExceptionCode)
1208 {
1209     ULONG Ret;
1210 
1211     switch (ExceptionCode)
1212     {
1213         case STATUS_INTEGER_DIVIDE_BY_ZERO:
1214             Ret = 0;
1215             break;
1216         case STATUS_SINGLE_STEP:
1217             Ret = 1;
1218             break;
1219         case STATUS_BREAKPOINT:
1220             Ret = 3;
1221             break;
1222         case STATUS_INTEGER_OVERFLOW:
1223             Ret = 4;
1224             break;
1225         case STATUS_ARRAY_BOUNDS_EXCEEDED:
1226             Ret = 5;
1227             break;
1228         case STATUS_ILLEGAL_INSTRUCTION:
1229             Ret = 6;
1230             break;
1231         case STATUS_FLOAT_INVALID_OPERATION:
1232             Ret = 7;
1233             break;
1234         case STATUS_STACK_OVERFLOW:
1235             Ret = 12;
1236             break;
1237         case STATUS_ACCESS_VIOLATION:
1238             Ret = 14;
1239             break;
1240         case STATUS_DATATYPE_MISALIGNMENT:
1241             Ret = 17;
1242             break;
1243         case STATUS_FLOAT_MULTIPLE_TRAPS:
1244             Ret = 18;
1245             break;
1246         case STATUS_ASSERTION_FAILURE:
1247             Ret = 20;
1248             break;
1249 
1250         default:
1251             Ret = RTL_NUMBER_OF(KdbEnterConditions) - 1;
1252             break;
1253     }
1254 
1255     return Ret;
1256 }
1257 
1258 /*!\brief KDB Exception filter
1259  *
1260  * Called by the exception dispatcher.
1261  *
1262  * \param ExceptionRecord  Unused.
1263  * \param PreviousMode     UserMode if the exception was raised from umode, otherwise KernelMode.
1264  * \param Context          Context, IN/OUT parameter.
1265  * \param TrapFrame        Exception TrapFrame.
1266  * \param FirstChance      TRUE when called before exception frames were serached,
1267  *                         FALSE for the second call.
1268  *
1269  * \returns KD_CONTINUE_TYPE
1270  */
1271 KD_CONTINUE_TYPE
KdbEnterDebuggerException(IN PEXCEPTION_RECORD64 ExceptionRecord,IN KPROCESSOR_MODE PreviousMode,IN PCONTEXT Context,IN BOOLEAN FirstChance)1272 KdbEnterDebuggerException(
1273     IN PEXCEPTION_RECORD64 ExceptionRecord,
1274     IN KPROCESSOR_MODE PreviousMode,
1275     IN PCONTEXT Context,
1276     IN BOOLEAN FirstChance)
1277 {
1278     KDB_ENTER_CONDITION EnterCondition;
1279     KD_CONTINUE_TYPE ContinueType = kdHandleException;
1280     PKDB_BREAKPOINT BreakPoint;
1281     ULONG ExpNr;
1282     ULONGLONG ull;
1283     BOOLEAN Resume = FALSE;
1284     BOOLEAN EnterConditionMet = TRUE;
1285     ULONG OldEflags;
1286     KIRQL OldIrql;
1287     NTSTATUS ExceptionCode;
1288     VOID (*EntryPoint)(VOID) = KdbpCallMainLoop;
1289 
1290     ExceptionCode = (ExceptionRecord ? ExceptionRecord->ExceptionCode : STATUS_BREAKPOINT);
1291 
1292     KdbCurrentProcess = PsGetCurrentProcess();
1293 
1294     /* Set continue type to kdContinue for single steps and breakpoints */
1295     if (ExceptionCode == STATUS_SINGLE_STEP ||
1296         ExceptionCode == STATUS_BREAKPOINT ||
1297         ExceptionCode == STATUS_ASSERTION_FAILURE)
1298     {
1299         ContinueType = kdContinue;
1300     }
1301 
1302     /* Check if we should handle the exception. */
1303     /* FIXME - won't get all exceptions here :( */
1304     ExpNr = KdbpGetExceptionNumberFromStatus(ExceptionCode);
1305     EnterCondition = KdbEnterConditions[ExpNr][FirstChance ? 0 : 1];
1306     if (EnterCondition == KdbDoNotEnter ||
1307         (EnterCondition == KdbEnterFromUmode && PreviousMode == KernelMode) ||
1308         (EnterCondition == KdbEnterFromKmode && PreviousMode != KernelMode))
1309     {
1310         EnterConditionMet = FALSE;
1311     }
1312 
1313     /* If we stopped on one of our breakpoints then let the user know */
1314     KdbLastBreakPointNr = -1;
1315     KdbEnteredOnSingleStep = FALSE;
1316 
1317     if (FirstChance && (ExceptionCode == STATUS_SINGLE_STEP || ExceptionCode == STATUS_BREAKPOINT) &&
1318         (KdbLastBreakPointNr = KdbpIsBreakPointOurs(ExceptionCode, Context)) >= 0)
1319     {
1320         BreakPoint = KdbBreakPoints + KdbLastBreakPointNr;
1321 
1322         if (ExceptionCode == STATUS_BREAKPOINT)
1323         {
1324             /* ... and restore the original instruction */
1325             if (!NT_SUCCESS(KdbpOverwriteInstruction(KdbCurrentProcess, BreakPoint->Address,
1326                                                      BreakPoint->Data.SavedInstruction, NULL)))
1327             {
1328                 KdbPuts("Couldn't restore original instruction after INT3! Cannot continue execution.\n");
1329                 KeBugCheck(0); // FIXME: Proper bugcode!
1330             }
1331 
1332             /* Also since we are past the int3 now, decrement EIP in the
1333                TrapFrame. This is only needed because KDBG insists on working
1334                with the TrapFrame instead of with the Context, as it is supposed
1335                to do. The context has already EIP point to the int3, since
1336                KiDispatchException accounts for that. Whatever we do here with
1337                the TrapFrame does not matter anyway, since KiDispatchException
1338                will overwrite it with the values from the Context! */
1339             KeSetContextPc(Context, KeGetContextPc(Context) - 1);
1340         }
1341 
1342         if ((BreakPoint->Type == KdbBreakPointHardware) &&
1343             (BreakPoint->Data.Hw.AccessType == KdbAccessExec))
1344         {
1345             Resume = TRUE; /* Set the resume flag when continuing execution */
1346         }
1347         /*
1348          * When a temporary breakpoint is hit we have to make sure that we are
1349          * in the same context in which it was set, otherwise it could happen
1350          * that another process/thread hits it before and it gets deleted.
1351          */
1352         else if (BreakPoint->Type == KdbBreakPointTemporary &&
1353                  BreakPoint->Process == KdbCurrentProcess)
1354         {
1355             ASSERT((Context->EFlags & EFLAGS_TF) == 0);
1356 
1357             /* Delete the temporary breakpoint which was used to step over or into the instruction */
1358             KdbpDeleteBreakPoint(-1, BreakPoint);
1359 
1360             if (--KdbNumSingleSteps > 0)
1361             {
1362                 if ((KdbSingleStepOver && !KdbpStepOverInstruction(KeGetContextPc(Context))) ||
1363                     (!KdbSingleStepOver && !KdbpStepIntoInstruction(KeGetContextPc(Context))))
1364                 {
1365                     Context->EFlags |= EFLAGS_TF;
1366                 }
1367 
1368                 goto continue_execution; /* return */
1369             }
1370 
1371             KdbEnteredOnSingleStep = TRUE;
1372         }
1373         /*
1374          * If we hit a breakpoint set by the debugger we set the single step flag,
1375          * ignore the next single step and reenable the breakpoint.
1376          */
1377         else if (BreakPoint->Type == KdbBreakPointSoftware ||
1378                  BreakPoint->Type == KdbBreakPointTemporary)
1379         {
1380             ASSERT(ExceptionCode == STATUS_BREAKPOINT);
1381             Context->EFlags |= EFLAGS_TF;
1382             KdbBreakPointToReenable = BreakPoint;
1383         }
1384 
1385         /* Make sure that the breakpoint should be triggered in this context */
1386         if (!BreakPoint->Global && BreakPoint->Process != KdbCurrentProcess)
1387         {
1388             goto continue_execution; /* return */
1389         }
1390 
1391         /* Check if the condition for the breakpoint is met. */
1392         if (BreakPoint->Condition)
1393         {
1394             /* Setup the KDB trap frame */
1395             KdbTrapFrame = *Context;
1396 
1397             ull = 0;
1398             if (!KdbpRpnEvaluateParsedExpression(BreakPoint->Condition, &KdbTrapFrame, &ull, NULL, NULL))
1399             {
1400                 /* FIXME: Print warning? */
1401             }
1402             else if (ull == 0) /* condition is not met */
1403             {
1404                 goto continue_execution; /* return */
1405             }
1406         }
1407 
1408         if (BreakPoint->Type == KdbBreakPointSoftware)
1409         {
1410             KdbPrintf("\nEntered debugger on breakpoint #%d: EXEC 0x%04x:0x%p\n",
1411                       KdbLastBreakPointNr, Context->SegCs & 0xffff, KeGetContextPc(Context));
1412         }
1413         else if (BreakPoint->Type == KdbBreakPointHardware)
1414         {
1415             KdbPrintf("\nEntered debugger on breakpoint #%d: %s 0x%08x\n",
1416                       KdbLastBreakPointNr,
1417                       (BreakPoint->Data.Hw.AccessType == KdbAccessRead) ? "READ" :
1418                       ((BreakPoint->Data.Hw.AccessType == KdbAccessWrite) ? "WRITE" :
1419                       ((BreakPoint->Data.Hw.AccessType == KdbAccessReadWrite) ? "RDWR" : "EXEC")),
1420                       BreakPoint->Address);
1421         }
1422     }
1423     else if (ExceptionCode == STATUS_SINGLE_STEP)
1424     {
1425         /* Silently ignore a debugger initiated single step. */
1426         if ((Context->Dr6 & 0xf) == 0 && KdbBreakPointToReenable)
1427         {
1428             /* FIXME: Make sure that the breakpoint was really hit (check bp->Address vs. tf->Eip) */
1429             BreakPoint = KdbBreakPointToReenable;
1430             KdbBreakPointToReenable = NULL;
1431             ASSERT(BreakPoint->Type == KdbBreakPointSoftware ||
1432                    BreakPoint->Type == KdbBreakPointTemporary);
1433 
1434             /*
1435              * Reenable the breakpoint we disabled to execute the breakpointed
1436              * instruction.
1437              */
1438             if (!NT_SUCCESS(KdbpOverwriteInstruction(KdbCurrentProcess, BreakPoint->Address, 0xCC,
1439                                                      &BreakPoint->Data.SavedInstruction)))
1440             {
1441                 KdbPrintf("Warning: Couldn't reenable breakpoint %d\n",
1442                           BreakPoint - KdbBreakPoints);
1443             }
1444 
1445             /* Unset TF if we are no longer single stepping. */
1446             if (KdbNumSingleSteps == 0)
1447                 Context->EFlags &= ~EFLAGS_TF;
1448 
1449             if (!KdbpEvenThoughWeHaveABreakPointToReenableWeAlsoHaveARealSingleStep)
1450             {
1451                 goto continue_execution; /* return */
1452             }
1453         }
1454 
1455         /* Quoth the raven, 'Nevermore!' */
1456         KdbpEvenThoughWeHaveABreakPointToReenableWeAlsoHaveARealSingleStep = FALSE;
1457 
1458         /* Check if we expect a single step */
1459         if ((Context->Dr6 & 0xf) == 0 && KdbNumSingleSteps > 0)
1460         {
1461             /*ASSERT((Context->Eflags & EFLAGS_TF) != 0);*/
1462             if (--KdbNumSingleSteps > 0)
1463             {
1464                 if ((KdbSingleStepOver && KdbpStepOverInstruction(KeGetContextPc(Context))) ||
1465                     (!KdbSingleStepOver && KdbpStepIntoInstruction(KeGetContextPc(Context))))
1466                 {
1467                     Context->EFlags &= ~EFLAGS_TF;
1468                 }
1469                 else
1470                 {
1471                     Context->EFlags |= EFLAGS_TF;
1472                 }
1473 
1474                 goto continue_execution; /* return */
1475             }
1476             else
1477             {
1478                 Context->EFlags &= ~EFLAGS_TF;
1479                 KdbEnteredOnSingleStep = TRUE;
1480             }
1481         }
1482         else
1483         {
1484             if (!EnterConditionMet)
1485             {
1486                 return kdHandleException;
1487             }
1488 
1489             KdbPuts("\nEntered debugger on unexpected debug trap!\n");
1490         }
1491     }
1492     else if (ExceptionCode == STATUS_BREAKPOINT)
1493     {
1494         /* Do the condition check and banner display only if we enter
1495          * from a true code breakpoint. We skip those when running the
1496          * KDBinit file, because it is done via an artificial breakpoint. */
1497         if (KdbInitFileBuffer)
1498         {
1499             EntryPoint = KdbpCliInterpretInitFile;
1500             goto EnterKdbg;
1501         }
1502 
1503         if (!EnterConditionMet)
1504         {
1505             return kdHandleException;
1506         }
1507 
1508         KdbPrintf("\nEntered debugger on embedded INT3 at 0x%04x:0x%p.\n",
1509                   Context->SegCs & 0xffff, KeGetContextPc(Context));
1510 EnterKdbg:;
1511     }
1512     else
1513     {
1514         const CHAR *ExceptionString = (ExpNr < RTL_NUMBER_OF(ExceptionNrToString)) ?
1515                                       ExceptionNrToString[ExpNr] :
1516                                       "Unknown/User defined exception";
1517 
1518         if (!EnterConditionMet)
1519         {
1520             return ContinueType;
1521         }
1522 
1523         KdbPrintf("\nEntered debugger on %s-chance exception (Exception Code: 0x%x) (%s)\n",
1524                   FirstChance ? "first" : "last", ExceptionCode, ExceptionString);
1525 
1526         if (ExceptionCode == STATUS_ACCESS_VIOLATION &&
1527             ExceptionRecord && ExceptionRecord->NumberParameters != 0)
1528         {
1529             ULONG_PTR TrapCr2 = __readcr2();
1530             KdbPrintf("Memory at 0x%p could not be accessed\n", TrapCr2);
1531         }
1532     }
1533 
1534     /* Once we enter the debugger we do not expect any more single steps to happen */
1535     KdbNumSingleSteps = 0;
1536 
1537     /* Update the current process pointer */
1538     KdbCurrentProcess = KdbOriginalProcess = PsGetCurrentProcess();
1539     KdbCurrentThread = KdbOriginalThread = PsGetCurrentThread();
1540     KdbCurrentTrapFrame = &KdbTrapFrame;
1541 
1542     /* Setup the KDB trap frame */
1543     KdbTrapFrame = *Context;
1544 
1545     /* Enter critical section */
1546     OldEflags = __readeflags();
1547     _disable();
1548 
1549     /* HACK: Save the current IRQL and pretend we are at dispatch level */
1550     OldIrql = KeGetCurrentIrql();
1551     if (OldIrql > DISPATCH_LEVEL)
1552         KeLowerIrql(DISPATCH_LEVEL);
1553 
1554     /* Exception inside the debugger? Game over. */
1555     if (InterlockedIncrement(&KdbEntryCount) > 1)
1556     {
1557         __writeeflags(OldEflags);
1558         return kdHandleException;
1559     }
1560 
1561     /* Enter KDBG proper and run either the main loop or the KDBinit file */
1562     KdbpInternalEnter(EntryPoint);
1563 
1564     /* Check if we should single step */
1565     if (KdbNumSingleSteps > 0)
1566     {
1567         /* Variable explains itself! */
1568         KdbpEvenThoughWeHaveABreakPointToReenableWeAlsoHaveARealSingleStep = TRUE;
1569 
1570         if ((KdbSingleStepOver && KdbpStepOverInstruction(KeGetContextPc(KdbCurrentTrapFrame))) ||
1571             (!KdbSingleStepOver && KdbpStepIntoInstruction(KeGetContextPc(KdbCurrentTrapFrame))))
1572         {
1573             ASSERT((KdbCurrentTrapFrame->EFlags & EFLAGS_TF) == 0);
1574             /*KdbCurrentTrapFrame->EFlags &= ~EFLAGS_TF;*/
1575         }
1576         else
1577         {
1578             KdbTrapFrame.EFlags |= EFLAGS_TF;
1579         }
1580     }
1581 
1582     /* We can't update the current thread's trapframe 'cause it might not have one */
1583 
1584     /* Detach from attached process */
1585     if (KdbCurrentProcess != KdbOriginalProcess)
1586     {
1587         KeUnstackDetachProcess(&KdbApcState);
1588     }
1589 
1590     /* Update the exception Context */
1591     *Context = KdbTrapFrame;
1592 
1593     /* Decrement the entry count */
1594     InterlockedDecrement(&KdbEntryCount);
1595 
1596     /* HACK: Raise back to old IRQL */
1597     if (OldIrql > DISPATCH_LEVEL)
1598         KeRaiseIrql(OldIrql, &OldIrql);
1599 
1600     /* Leave critical section */
1601     __writeeflags(OldEflags);
1602 
1603     /* Check if user requested a bugcheck */
1604     if (KdbpBugCheckRequested)
1605     {
1606         /* Clear the flag and bugcheck the system */
1607         KdbpBugCheckRequested = FALSE;
1608         KeBugCheck(MANUALLY_INITIATED_CRASH);
1609     }
1610 
1611 continue_execution:
1612     /* Clear debug status */
1613     if (ExceptionCode == STATUS_BREAKPOINT) /* FIXME: Why clear DR6 on INT3? */
1614     {
1615         /* Set the RF flag so we don't trigger the same breakpoint again. */
1616         if (Resume)
1617         {
1618             Context->EFlags |= EFLAGS_RF;
1619         }
1620 
1621         /* Clear dr6 status flags. */
1622         Context->Dr6 &= ~0x0000e00f;
1623 
1624         if (!(KdbEnteredOnSingleStep && KdbSingleStepOver))
1625         {
1626             /* Skip the current instruction */
1627             KeSetContextPc(Context, KeGetContextPc(Context) + KD_BREAKPOINT_SIZE);
1628         }
1629     }
1630 
1631     return ContinueType;
1632 }
1633 
1634 VOID
KdbpGetCommandLineSettings(_In_ PCSTR p1)1635 KdbpGetCommandLineSettings(
1636     _In_ PCSTR p1)
1637 {
1638 #define CONST_STR_LEN(x) (sizeof(x)/sizeof(x[0]) - 1)
1639 
1640     while (p1 && *p1)
1641     {
1642         /* Skip leading whitespace */
1643         while (*p1 == ' ') ++p1;
1644 
1645         if (!_strnicmp(p1, "FIRSTCHANCE", CONST_STR_LEN("FIRSTCHANCE")))
1646         {
1647             p1 += CONST_STR_LEN("FIRSTCHANCE");
1648             KdbpSetEnterCondition(-1, TRUE, KdbEnterAlways);
1649         }
1650 
1651         /* Move on to the next option */
1652         p1 = strchr(p1, ' ');
1653     }
1654 }
1655 
1656 NTSTATUS
KdbpSafeReadMemory(OUT PVOID Dest,IN PVOID Src,IN ULONG Bytes)1657 KdbpSafeReadMemory(
1658     OUT PVOID Dest,
1659     IN PVOID Src,
1660     IN ULONG Bytes)
1661 {
1662     return KdpCopyMemoryChunks((ULONG64)(ULONG_PTR)Src,
1663                                Dest,
1664                                Bytes,
1665                                0,
1666                                MMDBG_COPY_UNSAFE,
1667                                NULL);
1668 }
1669 
1670 NTSTATUS
KdbpSafeWriteMemory(OUT PVOID Dest,IN PVOID Src,IN ULONG Bytes)1671 KdbpSafeWriteMemory(
1672     OUT PVOID Dest,
1673     IN PVOID Src,
1674     IN ULONG Bytes)
1675 {
1676     return KdpCopyMemoryChunks((ULONG64)(ULONG_PTR)Dest,
1677                                Src,
1678                                Bytes,
1679                                0,
1680                                MMDBG_COPY_UNSAFE | MMDBG_COPY_WRITE,
1681                                NULL);
1682 }
1683