Bitdefender Hypervisor Memory Introspection
winstack.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "winstack.h"
6 #include "decoder.h"
7 #include "drivers.h"
8 #include "guests.h"
9 #include "introcpu.h"
10 #include "winpe.h"
11 #include "winummodule.h"
12 #include "winthread.h"
13 
14 
15 #define TRAPFRAME_MAX_ITERATIONS 0x100
16 
17 
18 static INTSTATUS
20  _In_ QWORD Gva,
21  _Out_opt_ QWORD *CallAddress
22  )
32 {
33  INTSTATUS status;
34  NDSTATUS ndstatus;
35  BYTE defCode, defData;
36  BYTE code[0x20] = { 0 };
37  INSTRUX instruction;
38  QWORD calledFuncAddress, i;
39  BOOLEAN isCall;
40 
41  status = IntKernVirtMemRead(Gva - 7, sizeof(code), code, NULL);
42  if (!INT_SUCCESS(status))
43  {
44  return status;
45  }
46 
47  // init
48  isCall = TRUE;
49  calledFuncAddress = 0;
50 
51  defData = gGuest.Guest64 ? ND_DATA_64 : ND_DATA_32;
52  defCode = gGuest.Guest64 ? ND_CODE_64 : ND_CODE_32;
53 
54  // We check from big instructions to small ones. This assures that the instructions will be
55  // decoded with prefixes. Also 5-byte instructions (0xe8 calls) are the most frequent ones.
56  // WARNING: Don't add the instruction length to the Gva. The Gva already points to the
57  // instruction after the CALL.
58  for (i = 0; i <= 5; i++)
59  {
60  // we must be sure if the instruction is really a CALL before jumping to cleanup and exit at the end of this
61  // for loop.
62  BOOLEAN bIsInstruxCall = FALSE;
63 
64  ndstatus = NdDecodeEx(&instruction, code + i, sizeof(code) - i, defCode, defData);
65  if (!ND_SUCCESS(ndstatus))
66  {
67  continue;
68  }
69 
70  // get the call address if that's possible
71  switch (instruction.Instruction)
72  {
73  case ND_INS_CALLNR:
74  {
75  bIsInstruxCall = TRUE;
76 
77  calledFuncAddress = Gva + instruction.Operands[0].Info.RelativeOffset.Rel;
78  break;
79  }
80 
81  case ND_INS_CALLNI:
82  {
83  bIsInstruxCall = TRUE;
84 
85  if (ND_OP_IMM == instruction.Operands[0].Type)
86  {
87  calledFuncAddress = instruction.Operands[0].Info.Immediate.Imm;
88  }
89  else if (ND_OP_MEM == instruction.Operands[0].Type)
90  {
91  QWORD fetchAddress;
92 
93  if (instruction.Operands[0].Info.Memory.IsRipRel)
94  {
95  // Disp is already sing extended
96  fetchAddress = Gva + instruction.Operands[0].Info.Memory.Disp;
97  }
98  else if (instruction.Operands[0].Info.Memory.IsDirect)
99  {
100  fetchAddress = instruction.Operands[0].Info.Memory.Disp;
101  }
102  else
103  {
104  // So far so good, but it's using registers.
105  break;
106  }
107 
108  // Get the actual called address
109  status = IntKernVirtMemFetchQword(fetchAddress, &calledFuncAddress);
110  if (!INT_SUCCESS(status))
111  {
112  WARNING("[WARNING] Failed to get function address from 0x%016llx (Call RIP 0x%016llx): 0x%08x\n",
113  fetchAddress, Gva, status);
114  calledFuncAddress = 0;
115  }
116  }
117  else if (ND_OP_OFFS == instruction.Operands[0].Type)
118  {
119  calledFuncAddress = Gva + instruction.Operands[0].Info.RelativeOffset.Rel;
120  }
121 
122  break;
123  }
124 
125  case ND_INS_CALLFI:
126  case ND_INS_CALLFD:
127  bIsInstruxCall = TRUE;
128 
129  if (ND_OP_OFFS == instruction.Operands[0].Type)
130  {
131  calledFuncAddress = Gva + instruction.Operands[0].Info.RelativeOffset.Rel;
132  }
133  else if (ND_OP_MEM == instruction.Operands[0].Type)
134  {
135  QWORD fetchAddress;
136 
137  if (instruction.Operands[0].Info.Memory.IsRipRel)
138  {
139  // Disp is already sing extended
140  fetchAddress = Gva + instruction.Operands[0].Info.Memory.Disp;
141  }
142  else if (instruction.Operands[0].Info.Memory.IsDirect)
143  {
144  fetchAddress = instruction.Operands[0].Info.Memory.Disp;
145  }
146  else
147  {
148  // So far so good, but it's using registers.
149  break;
150  }
151 
152  // Get the actual called address
153  status = IntKernVirtMemFetchQword(fetchAddress, &calledFuncAddress);
154  if (!INT_SUCCESS(status))
155  {
156  WARNING("[WARNING] Failed to get function address from 0x%016llx (Call RIP 0x%016llx): 0x%08x\n",
157  fetchAddress, Gva, status);
158  calledFuncAddress = 0;
159  }
160  }
161  else if (ND_OP_REG == instruction.Operands[0].Type)
162  {
163  calledFuncAddress = 0;
164  }
165  else
166  {
168  }
169  break;
170 
171  default:
172  break;
173  }
174 
175  // Make sure that instruction length is good (it doesn't override return address & it hasn't slack space)
176  // Also, we need to make sure that this instruction is REALLY a CALL instruction before jumping to cleanup
177  // and leave
178  if (Gva == Gva - (7 - i) + instruction.Length && bIsInstruxCall)
179  {
180  goto cleanup_and_leave;
181  }
182  }
183 
184  // If we get here and don't find a good instruction then it's no good
185  isCall = FALSE;
186 
187 cleanup_and_leave:
188  if (!isCall || (calledFuncAddress != 0 && !IS_KERNEL_POINTER_WIN(gGuest.Guest64, calledFuncAddress)))
189  {
190  status = INT_STATUS_NOT_FOUND;
191  goto leave;
192  }
193 
194  if (calledFuncAddress == 0)
195  {
196  goto leave;
197  }
198 
200  calledFuncAddress, &instruction);
201  if (!INT_SUCCESS(status))
202  {
203  status = INT_STATUS_SUCCESS;
204  goto leave;
205  }
206 
207  //if (!ND_IS_INS_JMP(instruction.Instruction))
208  if ((ND_CAT_COND_BR != instruction.Category) && (ND_CAT_UNCOND_BR != instruction.Category))
209  {
210  goto leave;
211  }
212 
213  switch (instruction.Instruction)
214  {
215  case ND_INS_JMPNI: // JMP [...]
216  {
217  if (ND_OP_MEM == instruction.Operands[0].Type)
218  {
219  QWORD fetchAddress;
220 
221  if (instruction.Operands[0].Info.Memory.IsRipRel)
222  {
223  // Disp is already sing extended
224  fetchAddress = calledFuncAddress + instruction.Length + instruction.Operands[0].Info.Memory.Disp;
225  }
226  else if (instruction.Operands[0].Info.Memory.IsDirect)
227  {
228  fetchAddress = instruction.Operands[0].Info.Memory.Disp;
229  }
230  else
231  {
232  // So far so good, but it's using registers.
233  break;
234  }
235 
236  // Get the actual called address
237  status = IntKernVirtMemFetchQword(fetchAddress, &calledFuncAddress);
238  if (!INT_SUCCESS(status))
239  {
240  ERROR("[ERROR] Failed to get function address from 0x%016llx (Call RIP 0x%016llx): 0x%08x\n",
241  fetchAddress, calledFuncAddress, status);
242  calledFuncAddress = 0;
243  }
244  }
245  else if (ND_OP_REG == instruction.Operands[0].Type)
246  {
247  calledFuncAddress = 0;
248  goto leave;
249  }
250 
251  break;
252  }
253  case ND_INS_JMPNR:
254  {
255  calledFuncAddress = calledFuncAddress + instruction.Length + instruction.Operands[0].Info.RelativeOffset.Rel;
256  break;
257  }
258 
259  default:
260  break;
261  }
262 
263 leave:
264  if (NULL != CallAddress)
265  {
266  *CallAddress = FIX_GUEST_POINTER(gGuest.Guest64, calledFuncAddress);
267  }
268 
269  return status;
270 }
271 
272 
273 static INTSTATUS
275  _In_ QWORD Rsp,
276  _In_ QWORD Rip,
277  _In_ DWORD MaxTraces,
278  _In_ QWORD Flags,
279  _Inout_ STACK_TRACE *StackTrace
280  )
296 {
297  QWORD currentRip, currentRsp, newRsp, currentModBase;
298  PBYTE pStack, pStackBase, pModBaseMap;
299  BOOLEAN ripInsideSameModule;
300  KERNEL_DRIVER *pDriver;
301  INTSTATUS status;
302 
303  if (NULL == StackTrace || NULL == StackTrace->Traces)
304  {
306  }
307 
309 
310  StackTrace->NumberOfTraces = 0;
311  StackTrace->StartRip = Rip;
312  StackTrace->Bits64 = TRUE;
313  pStack = pModBaseMap = pStackBase = NULL;
314  currentRip = Rip;
315  currentRsp = Rsp & ~7; // Make sure the stack is 8-bytes aligned.
316  currentModBase = 0;
317  pDriver = NULL;
318 
319  ripInsideSameModule = FALSE;
320 
321  do
322  {
323  RUNTIME_FUNCTION runtimeFunction;
324  DWORD prologueSize, tries, beginRva;
325  QWORD retAddress, retModuleBase, calledAddress, retAddrPtr;
326  BOOLEAN interrupt, exception, hasFramePointer, found, fErrorCode;
327 
328  memzero(&runtimeFunction, sizeof(runtimeFunction));
329  prologueSize = tries = beginRva = 0;
330  found = hasFramePointer = interrupt = exception = fErrorCode = FALSE;
331  retModuleBase = retAddress = calledAddress = retAddrPtr = 0;
332  status = INT_STATUS_SUCCESS;
333 
334  // If we are inside the same module then don't remap the module base
335  if (!ripInsideSameModule)
336  {
337  // Unmap the module if it was previously mapped
338  if (NULL != pModBaseMap)
339  {
340  IntVirtMemUnmap(&pModBaseMap);
341  }
342 
343  // Get the image base of the RIP if we don't already have one. If this fails we get out with an error.
344  if (0 == currentModBase)
345  {
346  pDriver = IntDriverFindByAddress(currentRip);
347  if (NULL == pDriver)
348  {
349  if ((Flags & STACK_FLG_ONLY_DRIVER_ADDRS) &&
350  StackTrace->NumberOfTraces > 0)
351  {
352  ERROR("[ERROR] Failed to find the module base of RIP 0x%016llx RSP 0x%016llx at try %d\n",
353  currentRip, currentRsp, StackTrace->NumberOfTraces);
354  }
355  else if (StackTrace->NumberOfTraces == 0)
356  {
357  // If we are on the first try, the the start RIP can be outside of the driver
358  goto _start_searching;
359  }
360  }
361  else
362  {
363  currentModBase = pDriver->BaseVa;
364  }
365 
366  if (0 == currentModBase)
367  {
368  if (Flags & STACK_FLG_ONLY_DRIVER_ADDRS)
369  {
370  goto _cleanup_and_leave;
371  }
372  else
373  {
374  goto _start_searching;
375  }
376  }
377  }
378 
379  // As we always search in the kernel buffer in the case of NT, we don't need the headers to be mapped
380  // again here, so we'll skip it.
381  if (currentModBase != gGuest.KernelVa)
382  {
383  status = IntVirtMemMap(currentModBase, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &pModBaseMap);
384  if (!INT_SUCCESS(status))
385  {
386  // Extra check for user-mode drivers, which are not present all the time
387  if (pDriver != NULL &&
388  (0 == wstrcasecmp(pDriver->Name, u"win32k.sys") ||
389  0 == wstrcasecmp(pDriver->Name, u"TSDDD.dll") ||
390  0 == wstrcasecmp(pDriver->Name, u"cdd.dll")))
391  {
392  // Even if the number of traces is 0, we don't care, since if this driver would have modified
393  // something it would be present.
394  status = INT_STATUS_SUCCESS;
395  }
396  else
397  {
398  ERROR("[ERROR] Failed mapping driver base 0x%016llx to host: 0x%08x\n",
399  currentModBase, status);
400  }
401 
402  goto _cleanup_and_leave;
403  }
404  }
405  }
406  else if (currentModBase == 0)
407  {
408  if (Flags & STACK_FLG_ONLY_DRIVER_ADDRS)
409  {
410  goto _cleanup_and_leave;
411  }
412  else
413  {
414  goto _start_searching;
415  }
416  }
417 
418  if (currentModBase != gGuest.KernelVa)
419  {
420  status = IntPeGetRuntimeFunction(currentModBase,
421  pModBaseMap,
422  (DWORD)(currentRip - currentModBase),
423  &runtimeFunction);
424  }
425  else
426  {
427  status = IntPeGetRuntimeFunctionInBuffer(currentModBase,
430  (DWORD)(currentRip - currentModBase),
431  &runtimeFunction);
432  }
433 
434  if (!INT_SUCCESS(status) && status != INT_STATUS_NOT_FOUND)
435  {
436  ERROR("[ERROR] Failed getting runtime function for module 0x%016llx RIP 0x%016llx: 0x%08x\n",
437  currentModBase, currentRip, status);
438  goto _cleanup_and_leave;
439  }
440  else if (status == INT_STATUS_NOT_FOUND)
441  {
442  // If an address is not found inside the table, it is supposed to be a leaf function and RSP points to
443  // the functions return address (somewhere in documentations).
444  }
445  else
446  {
447  DWORD ripOffset = (DWORD)(currentRip - currentModBase);
448 
449  // We start with this RVA and see in IntPeParseUnwindData if we need to go back further
450  beginRva = runtimeFunction.BeginAddress;
451 
452  // We have one of those functions that starts somewhere and jumps around. We must assume a
453  // rip offset greater than the length of the prologue.
454  if (ripOffset < runtimeFunction.BeginAddress || ripOffset > runtimeFunction.EndAddress)
455  {
456  ripOffset = runtimeFunction.EndAddress - beginRva; // Assume the prologue executed fully
457  }
458  else
459  {
460  ripOffset -= beginRva;
461  }
462 
463  if (currentModBase != gGuest.KernelVa)
464  {
465  status = IntPeParseUnwindData(currentModBase,
466  pModBaseMap,
467  &runtimeFunction,
468  ripOffset,
469  &prologueSize,
470  &beginRva,
471  &interrupt,
472  &exception,
473  &hasFramePointer);
474  }
475  else
476  {
477  status = IntPeParseUnwindDataInBuffer(currentModBase,
480  &runtimeFunction,
481  ripOffset,
482  &prologueSize,
483  &beginRva,
484  &interrupt,
485  &exception,
486  &hasFramePointer);
487  }
488 
489  if (!INT_SUCCESS(status))
490  {
491  ERROR("[ERROR] IntPeParseUnwindData failed for driver 0x%016llx with RIP 0x%016llx and begining "
492  "RVA 0x%x: 0x%08x\n", currentModBase, currentRip, runtimeFunction.BeginAddress, status);
493  goto _cleanup_and_leave;
494  }
495  }
496 
497 _start_searching:
498  // advance the stack pointer
499  newRsp = currentRsp + prologueSize;
500 
501  // If the function has a frame pointer then inside the function the initial size can grow, and there is no
502  // safe way to know that size (and if that code is actually executed). But if the function has no frame pointer
503  // we can only search in 16 values for the return address
504  if (hasFramePointer)
505  {
506  tries = 0x400; // suppose it doesn't reserve more than 1KB on the stack (if it does, this fails)
507  }
508  else
509  {
510  tries = 16;
511  }
512 
513  if (!hasFramePointer && (interrupt || exception))
514  {
515  // Trap frame is just where the stack is
516  status = IntKernVirtMemFetchQword(newRsp + ((exception) ? 8 : 0), &retAddress);
517  if (!INT_SUCCESS(status))
518  {
519  ERROR("[ERROR] Failed getting the return address form GVA 0x%016llx: 0x%08x",
520  newRsp + (exception ? 8 : 0), status);
521  goto _cleanup_and_leave;
522  }
523 
524  if (IS_KERNEL_POINTER_WIN(TRUE, retAddress))
525  {
526  pDriver = IntDriverFindByAddress(retAddress);
527 
528  retModuleBase = pDriver ? pDriver->BaseVa : 0;
529  }
530  else
531  {
532  retModuleBase = 0;
533  pDriver = NULL;
534  }
535 
536  LOG("[STACK] NONHEUR : Found trap frame at stack address 0x%016llx with ret address 0x%016llx \n",
537  newRsp, retAddress);
538 
539  retAddrPtr = newRsp + ((exception) ? 8 : 0);
540  newRsp += (exception) ? (6 * 8) : (5 * 8);
541  found = TRUE;
542  status = INT_STATUS_SUCCESS;
543  goto _next_stack_frame;
544  }
545 
546  // if we are in an exception assume we found the error code already
547  fErrorCode = exception;
548 
549  // If we are in a normal function, without the .pdb files there is no way to know how many parameters the
550  // function has. So analyze all the pointers from here up.
551  while (tries > 0)
552  {
553  // Map the stack if we didn't previously mapped it or the new stack gets into another page
554  if (((currentRsp & PAGE_MASK) != (newRsp & PAGE_MASK)) || NULL == pStackBase)
555  {
556  if (NULL != pStackBase)
557  {
558  IntVirtMemUnmap(&pStackBase);
559  }
560 
561  currentRsp = newRsp;
562 
563  status = IntVirtMemMap(currentRsp & PAGE_MASK, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &pStackBase);
564  if (!INT_SUCCESS(status))
565  {
566  // the stack is guarded, so we got to the end of the current stackframe (probably)
567  status = INT_STATUS_SUCCESS;
568  goto _cleanup_and_leave;
569  }
570  }
571 
572  // get the value on the stack, but first go to the current offset
573  pStack = pStackBase + (newRsp & PAGE_OFFSET);
574 
575  // Here we can return to a user-mode address and we must parse the stack manually for the trap frame
576  // {[ExceptionErrorCode], RIP, CS, EFLAGS, Old RSP, SS}. We don't know where it is, since the function
577  // uses a frame pointer and the RSP could point anywhere...
578  if (hasFramePointer && (exception || interrupt))
579  {
580  PQWORD pTrapFrame = (PQWORD)pStack;
581  BOOLEAN um, km, fRip, fCs, fSs, fRsp, fEflags;
582  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
583 
584  if (!fErrorCode)
585  {
586  // Error code must be a DWORD
587  if (*pTrapFrame > 0xffffffff)
588  {
589  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
590  fErrorCode = exception;
591  retAddress = 0;
592 
593  goto _next_pointer;
594  }
595 
596  fErrorCode = TRUE;
597  goto _next_pointer;
598  }
599 
600  if (!fRip)
601  {
602  // RIP can be both user and kernel mode
603  if (IS_KERNEL_POINTER_WIN(TRUE, *pTrapFrame) && *pTrapFrame != 0xffffffffffffffff)
604  {
605  km = TRUE;
606  }
607  else if (*pTrapFrame > 0xffffffff && *pTrapFrame != 0xffffffffffffffff)
608  {
609  um = TRUE;
610  }
611  else
612  {
613  newRsp -= interrupt ? 8 : 0;
614  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
615  fErrorCode = exception;
616  retAddress = 0;
617 
618  goto _next_pointer;
619  }
620 
621  retAddrPtr = newRsp;
622  retAddress = *pTrapFrame;
623 
624  fRip = TRUE;
625  goto _next_pointer;
626  }
627 
628  if (!fCs)
629  {
630  // CS cannot be greater than 0xff
631  if (*pTrapFrame > 0xff)
632  {
633  newRsp -= interrupt ? 8 * 2 : 8;
634  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
635  fErrorCode = exception;
636  retAddrPtr = retAddress = 0;
637  goto _next_pointer;
638  }
639 
640  // in kernel mode CS is 8 byte aligned
641  if (km && (*pTrapFrame % 8 != 0))
642  {
643  newRsp -= interrupt ? 8 * 2 : 8;
644  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
645  fErrorCode = exception;
646  retAddrPtr = retAddress = 0;
647  goto _next_pointer;
648  }
649  // in user mode CS can have any alignment
650 
651  fCs = TRUE;
652  goto _next_pointer;
653  }
654 
655  if (!fEflags)
656  {
657  if (*pTrapFrame == 0)
658  {
659  newRsp -= interrupt ? 8 * 3 : 8 * 2;
660  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
661  fErrorCode = exception;
662  retAddrPtr = retAddress = 0;
663  goto _next_pointer;
664  }
665 
666  // EFLAGS must be something nice
667  // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
668  // 0 0 0 0 0 0 0 0 0 0 ID VP IP IF AC VM RF 0 NT PL OF DF IF TF SF ZF 0 AF 0 PF 1 CF
669  if ((*pTrapFrame > (1 << 22)) || // bits [31 - 22]
670  (*pTrapFrame & (1 << 14)) ||
671  (*pTrapFrame & (1 << 5)) ||
672  (*pTrapFrame & (1 << 3)) ||
673  (0 == (*pTrapFrame & (1 << 1)))) // bit 1 must be 1
674  {
675  newRsp -= interrupt ? 8 * 3 : 8 * 2;
676  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
677  fErrorCode = exception;
678  retAddrPtr = retAddress = 0;
679  goto _next_pointer;
680  }
681 
682  fEflags = TRUE;
683  goto _next_pointer;
684  }
685 
686  if (!fRsp)
687  {
688  // RSP is 8 byte (16 ?!) aligned
689  if (0 != *pTrapFrame % 8)
690  {
691  newRsp -= interrupt ? 8 * 4 : 8 * 3;
692  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
693  fErrorCode = exception;
694  retAddrPtr = retAddress = 0;
695  goto _next_pointer;
696  }
697 
698  // RSP must be in the same space as RIP
699  if (km && (!IS_KERNEL_POINTER_WIN(TRUE, *pTrapFrame)))
700  {
701  newRsp -= interrupt ? 8 * 4 : 8 * 3;
702  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
703  fErrorCode = exception;
704  retAddrPtr = retAddress = 0;
705  goto _next_pointer;
706  }
707  else if (um && (IS_KERNEL_POINTER_WIN(TRUE, *pTrapFrame)))
708  {
709  newRsp -= interrupt ? 8 * 4 : 8 * 3;
710  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
711  fErrorCode = exception;
712  retAddrPtr = retAddress = 0;
713  goto _next_pointer;
714  }
715 
716  fRsp = TRUE;
717  goto _next_pointer;
718  }
719 
720  fSs = TRUE;
721  if (fErrorCode && fRip && fCs && fEflags && fRsp && fSs)
722  {
723  newRsp -= interrupt ? 5 * 8 : 4 * 8;
724  if (um)
725  {
726  retModuleBase = 0; // We could somehow search for it, but that's beyond our purpose
727  pDriver = NULL;
728  }
729 
730  // The beginning of the function is the called address for interrupts. No need to search for it
731  calledAddress = currentModBase + beginRva;
732 
733  goto _found_ret;
734  }
735  }
736  else
737  {
738  QWORD crip;
739 
740  // We do a normal analysis here. It's enough to see that the return address is a CALL or a JMP->CALL
741  // (for imported functions).
742  retAddress = *(PQWORD)pStack;
743 
744  // If this isn't a kernel pointer or the address is some pointer on the stack
745  if (!IS_KERNEL_POINTER_WIN(TRUE, retAddress) || retAddress == 0xffffffffffffffff)
746  {
747  goto _next_pointer;
748  }
749 
750  // For normal pointers we must search analyze the call since the rules must apply (JMP/CALL)
751  status = IntStackAnalyzePointer(retAddress, &calledAddress);
752  if (!INT_SUCCESS(status))
753  {
754  goto _next_pointer;
755  }
756 
757  // If there is a call but on a different address, the get the next pointer.
758  // But if the call address is 0 (that means the call involved a register) then we have nothing else
759  // to do
760  if (calledAddress != 0 && currentModBase != 0 && calledAddress != currentModBase + beginRva)
761  {
762  // See if the difference between current function and the called one is greater then maximum
763  // function length
764  if (calledAddress < currentModBase + beginRva &&
765  ((currentModBase + beginRva) - calledAddress > MAX_FUNC_LENGTH))
766  {
767  goto _analyze_jmp_after_call_case;
768  }
769  }
770  else if (calledAddress != 0 &&
771  (calledAddress < currentRip - MAX_FUNC_LENGTH || calledAddress > currentRip))
772  {
773  // if the call does not involve a register and is outside [rip - 0xaa0, rip],
774  // we can assume that this is another call to another function, not ours
775  goto _analyze_jmp_after_call_case;
776  }
777 
778  // We didn't end up in a case in which "we have a CALL [something]" where something is not near the
779  // current rip, so we can just consider this as a success.
780  goto _stack_trace_ok;
781 
782 _analyze_jmp_after_call_case:
783  pDriver = IntDriverFindByAddress(calledAddress);
784  if (NULL == pDriver)
785  {
786  goto _next_pointer;
787  }
788 
790 
791  crip = calledAddress;
792 
793  for (DWORD i = 0; i < 100; i++)
794  {
795  INSTRUX instrux;
796  QWORD calledFuncAddress = 0;
797  BOOLEAN bIsJump = FALSE;
798 
799  status = IntDecDecodeInstruction(IG_CS_TYPE_64B, crip, &instrux);
800  if (!INT_SUCCESS(status))
801  {
802  break;
803  }
804 
805  switch (instrux.Instruction)
806  {
807  case ND_INS_JMPNI: // JMP [...]
808  {
809  bIsJump = TRUE;
810 
811  if (ND_OP_MEM == instrux.Operands[0].Type)
812  {
813  QWORD fetchAddress;
814 
815  if (instrux.Operands[0].Info.Memory.IsRipRel)
816  {
817  // Disp is already sing extended
818  fetchAddress = crip + instrux.Length + instrux.Operands[0].Info.Memory.Disp;
819  }
820  else if (instrux.Operands[0].Info.Memory.IsDirect)
821  {
822  fetchAddress = instrux.Operands[0].Info.Memory.Disp;
823  }
824  else
825  {
826  // So far so good, but it's using registers.
827  break;
828  }
829 
830  // Get the actual called address
831  status = IntKernVirtMemFetchQword(fetchAddress, &calledFuncAddress);
832  if (!INT_SUCCESS(status))
833  {
834  WARNING("[WARNING] Failed to get function address from 0x%016llx "
835  "(Call RIP 0x%016llx): 0x%08x\n", fetchAddress, crip, status);
836  calledFuncAddress = 0;
837  break;
838  }
839  }
840  else if (ND_OP_REG == instrux.Operands[0].Type)
841  {
842  calledFuncAddress = 0;
843  break;
844  }
845  else
846  {
847  // Don't take into account for now other things than JMP REG, JMP [MEM]
848  bIsJump = FALSE;
849  }
850 
851  break;
852  }
853  default:
854  {
855  break;
856  }
857  }
858 
859  if (bIsJump)
860  {
861  if (calledFuncAddress == 0 ||
862  (calledFuncAddress >= currentRip - MAX_FUNC_LENGTH && calledFuncAddress <= currentRip))
863  {
865  calledAddress = calledFuncAddress;
866  goto _stack_trace_ok;
867  }
868  break;
869  }
870 
871  crip += instrux.Length;
872  }
873 
875 
876  // If we ended here then we didn't find a CALL addr, and at addr a JMP REG, so we just go to next
877  // pointer
878  goto _next_pointer;
879 
880 _stack_trace_ok:
881  retAddrPtr = newRsp;
882  }
883 
884 _found_ret:
885  if (IS_KERNEL_POINTER_WIN(TRUE, retAddress))
886  {
887  BOOLEAN bIsCode = FALSE;
888 
889  pDriver = IntDriverFindByAddress(retAddress);
890 
891  if (pDriver)
892  {
893  DWORD rva = (DWORD)(retAddress - pDriver->BaseVa);
894  IMAGE_SECTION_HEADER sec = { 0 };
895 
896  status = IntPeGetSectionHeaderByRva(pDriver->BaseVa, pDriver->Win.MzPeHeaders, rva, &sec);
897  if (!INT_SUCCESS(status))
898  {
899  goto _finish_searching_data_sec;
900  }
901 
903  {
904  bIsCode = TRUE;
905  }
906  }
907 
908 _finish_searching_data_sec:
909  // Ignore non-code sections on stack trace when the call address can't be calculated.
910  // If the return address is a `call rax` we can't actually verify that is valid return, so if the RIP
911  // is not in a CODE/EXEC section, the return address call instruction must actually point where it
912  // should (0xe8 calls, memory calls, etc.)
913  if (!bIsCode && pDriver && calledAddress == 0)
914  {
915  goto _next_pointer;
916  }
917 
918  retModuleBase = pDriver ? pDriver->BaseVa : 0;
919  }
920  else
921  {
922  retModuleBase = 0;
923  pDriver = NULL;
924  }
925 
926  found = TRUE;
927  status = INT_STATUS_SUCCESS;
928  goto _next_stack_frame;
929 
930 _next_pointer:
931  newRsp += 8;
932  tries--;
933  status = INT_STATUS_SUCCESS;
934  }
935 
936 _next_stack_frame:
937  if (!found)
938  {
939  // Don't save and don't go further
940  if (INT_SUCCESS(status) && 0 == StackTrace->NumberOfTraces)
941  {
942  status = INT_STATUS_NOT_FOUND;
943  }
944 
945  goto _cleanup_and_leave;
946  }
947 
948  StackTrace->Traces[StackTrace->NumberOfTraces].CurrentRip = currentRip;
949  StackTrace->Traces[StackTrace->NumberOfTraces].RetAddrPointer = retAddrPtr;
950  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnAddress = retAddress;
951  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnModule = pDriver;
952 
953  // the new RIP is the address we return
954  currentRip = retAddress;
955 
956  // Mark the address imprecise if we have different values
957  if (calledAddress != currentModBase + beginRva)
958  {
959  StackTrace->Traces[StackTrace->NumberOfTraces].Flags |= STACK_CALL_ADDRESS_IMPRECISE;
960  }
961 
962  if (exception)
963  {
964  StackTrace->Traces[StackTrace->NumberOfTraces].Flags |= STACK_EXCEPTION_ROUTINE;
965  }
966  else if (interrupt)
967  {
968  StackTrace->Traces[StackTrace->NumberOfTraces].Flags |= STACK_INTERRUPT_ROUTINE;
969  }
970 
971  // If exists, save the address at the call instruction, not the current function
972  if (calledAddress != 0)
973  {
974  StackTrace->Traces[StackTrace->NumberOfTraces].CalledAddress = calledAddress;
975  }
976  else if (beginRva > 0)
977  {
978  StackTrace->Traces[StackTrace->NumberOfTraces].CalledAddress = currentModBase + beginRva;
979  }
980 
981  // Update the module base to the new driver we are in
982  if (retModuleBase == currentModBase)
983  {
984  ripInsideSameModule = TRUE;
985  }
986  else
987  {
988  ripInsideSameModule = FALSE;
989  currentModBase = retModuleBase;
990  }
991 
992  // finally increment the count
993  StackTrace->NumberOfTraces++;
994 
995  // the current RSP is where we left it (adding the trap frame if that's the case)
996  currentRsp = newRsp + (interrupt ? 5 * 8 : exception ? 4 * 8 : 0);
997 
998  if (retAddrPtr == currentRsp && 0 == retModuleBase && !interrupt && !exception)
999  {
1000  currentRsp += 8;
1001  }
1002 
1003  // Signal an error only if we found no traces (and no error occurred)
1004  if (retModuleBase == 0 && (Flags & STACK_FLG_ONLY_DRIVER_ADDRS))
1005  {
1006  if (StackTrace->NumberOfTraces == 0 && status == INT_STATUS_SUCCESS)
1007  {
1008  ERROR("[ERROR] Didn't found a trace on the stack. RIP at 0x%016llx in module 0x%016llx\n",
1009  currentRip, currentModBase);
1010  status = INT_STATUS_NOT_FOUND;
1011  }
1012 
1013  goto _cleanup_and_leave;
1014  }
1015 
1016  if (!IS_KERNEL_POINTER_WIN(TRUE, retAddress))
1017  {
1018  // we return in user-mode, so we cannot go further
1019  break;
1020  }
1021  } while (StackTrace->NumberOfTraces < MaxTraces);
1022 
1023 _cleanup_and_leave:
1024  if (NULL != pStack)
1025  {
1026  pStack = pStackBase;
1027  IntVirtMemUnmap(&pStack);
1028  }
1029 
1030  if (NULL != pModBaseMap)
1031  {
1032  IntVirtMemUnmap(&pModBaseMap);
1033  }
1034 
1036 
1037  return status;
1038 }
1039 
1040 
1041 static INTSTATUS
1043  _In_ DWORD Stack,
1044  _In_ DWORD Eip,
1045  _In_ DWORD MaxNumberOfTraces,
1046  _In_ QWORD Flags,
1047  _Inout_ STACK_TRACE *StackTrace
1048  )
1067 {
1068  INTSTATUS status;
1069  QWORD ebp, cr3;
1070  BOOLEAN remap;
1071  PDWORD pStack, pOrigStack;
1072 
1073  UNREFERENCED_PARAMETER(Flags);
1074 
1075  if (!IS_KERNEL_POINTER_WIN(FALSE, Stack) || Stack % 4 != 0)
1076  {
1078  }
1079 
1080  if (0 == MaxNumberOfTraces)
1081  {
1083  }
1084 
1085  if (NULL == StackTrace || NULL == StackTrace->Traces)
1086  {
1088  }
1089 
1091 
1092  StackTrace->StartRip = Eip;
1093  StackTrace->NumberOfTraces = 0;
1094  StackTrace->Bits64 = FALSE;
1095  ebp = Stack;
1096  remap = TRUE;
1097  pStack = pOrigStack = NULL;
1098  cr3 = gGuest.Mm.SystemCr3;
1099  status = INT_STATUS_SUCCESS;
1100 
1101  for (DWORD frame = 0; frame < MaxNumberOfTraces; frame++)
1102  {
1103  DWORD retAddress = 0;
1104  DWORD nextFrame = 0;
1105  DWORD remaining;
1106  KERNEL_DRIVER *pMod = NULL;
1107 
1108  remaining = PAGE_REMAINING(ebp);
1109 
1110  if (remap)
1111  {
1112  if (NULL != pOrigStack)
1113  {
1114  IntVirtMemUnmap(&pOrigStack);
1115  }
1116 
1117  status = IntVirtMemMap(ebp, remaining, cr3, 0, &pOrigStack);
1118  if (!INT_SUCCESS(status))
1119  {
1120  LOG("[ERROR] Got to the end of the stack at 0x%016llx: 0x%08x\n", ebp, status);
1121  goto _check_and_leave;
1122  }
1123 
1124  pStack = pOrigStack;
1125  }
1126 
1127  // Always possible since the ebp is DWORD-aligned...
1128  nextFrame = pStack[0];
1129 
1130  if (nextFrame % 4 != 0)
1131  {
1132  WARNING("[WARNING] Unaligned stack: %08x\n", nextFrame);
1133  goto _check_and_leave;
1134  }
1135  else if (!IS_KERNEL_POINTER_WIN(FALSE, nextFrame))
1136  {
1137  TRACE("[INFO] User-Mode stack: %08x\n", nextFrame);
1138  goto _check_and_leave;
1139  }
1140 
1141  if (remaining >= 8)
1142  {
1143  retAddress = pStack[1];
1144  }
1145  else
1146  {
1147  status = IntKernVirtMemFetchDword(ebp + 4, &retAddress);
1148  if (!INT_SUCCESS(status))
1149  {
1150  LOG("[ERROR] Got to the end of the stack at 0x%016llx: 0x%08x\n", ebp + 4, status);
1151  goto _check_and_leave;
1152  }
1153  }
1154 
1155  if (IS_KERNEL_POINTER_WIN(FALSE, retAddress) && 0xffffffff != retAddress)
1156  {
1157  pMod = IntDriverFindByAddress(retAddress);
1158  }
1159 
1160  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnAddress = retAddress;
1161  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnModule = pMod;
1162 
1163  StackTrace->Traces[StackTrace->NumberOfTraces].RetAddrPointer = ebp + 4;
1164  StackTrace->Traces[StackTrace->NumberOfTraces].CalledAddress = 0;
1165  StackTrace->Traces[StackTrace->NumberOfTraces].CurrentRip = 0;
1166 
1167  ++StackTrace->NumberOfTraces;
1168 
1169  if ((ebp & PAGE_MASK) != (nextFrame & PAGE_MASK))
1170  {
1171  // We remap since we are in a different page... No point in recalculating the pStack pointer.
1172  remap = TRUE;
1173  }
1174  else
1175  {
1176  DWORD diff = (ebp > nextFrame) ? (DWORD)(ebp - nextFrame) : (DWORD)(nextFrame - ebp);
1177 
1178  remap = FALSE;
1179 
1180  // Just recalculate the stack pointer, no need to do a whole remap.
1181  pStack = (PDWORD)((PBYTE)pOrigStack + diff);
1182  }
1183 
1184  ebp = nextFrame;
1185  }
1186 
1187 _check_and_leave:
1188  if (NULL != pOrigStack)
1189  {
1190  IntVirtMemUnmap(&pOrigStack);
1191  }
1192 
1194 
1195  if (0 == StackTrace->NumberOfTraces)
1196  {
1197  if (!INT_SUCCESS(status))
1198  {
1199  return status;
1200  }
1201 
1202  return INT_STATUS_NOT_FOUND;
1203  }
1204 
1205  return INT_STATUS_SUCCESS;
1206 }
1207 
1208 
1209 INTSTATUS
1211  _In_ QWORD StackFrame,
1212  _In_ QWORD Rip,
1213  _In_ DWORD MaxNumberOfTraces,
1214  _In_ QWORD Flags,
1215  _Inout_ STACK_TRACE *StackTrace
1216  )
1228 {
1229  if (gGuest.Guest64)
1230  {
1231  return IntWinStackTraceGet64(StackFrame, Rip, MaxNumberOfTraces, Flags, StackTrace);
1232  }
1233  else
1234  {
1235  return IntWinStackTraceGet32((DWORD)StackFrame, (DWORD)Rip, MaxNumberOfTraces, Flags, StackTrace);
1236  }
1237 }
1238 
1239 
1240 static INTSTATUS
1242  _In_ PIG_ARCH_REGS Registers,
1243  _In_ PWIN_PROCESS_OBJECT Process,
1244  _In_ DWORD MaxNumberOfTraces,
1245  _Inout_ STACK_TRACE *StackTrace
1246  )
1257 {
1258  QWORD cr3, ebp;
1259  INTSTATUS status;
1260  PDWORD pStack, pOrigStack;
1261  BOOLEAN remap;
1262 
1263  remap = TRUE;
1264  cr3 = Registers->Cr3;
1265  ebp = Registers->Rbp;
1266  pStack = pOrigStack = NULL;
1267  status = INT_STATUS_UNSUCCESSFUL;
1268 
1269  for (DWORD frame = 0; frame < MaxNumberOfTraces; frame++)
1270  {
1271  DWORD retAddress = 0;
1272  DWORD nextFrame = 0;
1273  DWORD remaining;
1274  PWIN_PROCESS_MODULE pMod = NULL;
1275 
1276  remaining = PAGE_REMAINING(ebp);
1277 
1278  if (remap)
1279  {
1280  if (NULL != pOrigStack)
1281  {
1282  IntVirtMemUnmap(&pOrigStack);
1283  }
1284 
1285  status = IntVirtMemMap(ebp, remaining, cr3, 0, &pOrigStack);
1286  if (!INT_SUCCESS(status))
1287  {
1288  LOG("[ERROR] Got to the end of the stack at 0x%016llx: 0x%08x\n", ebp, status);
1289  goto _check_and_leave;
1290  }
1291 
1292  pStack = pOrigStack;
1293  }
1294 
1295  // Always possible since the ebp is DWORD-aligned...
1296  nextFrame = pStack[0];
1297 
1298  if (nextFrame % 4 != 0)
1299  {
1300  ERROR("[ERROR] Unaligned stack value %08x (current %08llx)\n", nextFrame, ebp);
1301  goto _check_and_leave;
1302  }
1303  else if (IS_KERNEL_POINTER_WIN(FALSE, nextFrame))
1304  {
1305  TRACE("[INFO] Kernel-Mode stack %08x (current %08llx)\n", nextFrame, ebp);
1306  goto _check_and_leave;
1307  }
1308  else if (0 == nextFrame)
1309  {
1310  // Nothing we can do about this
1311  goto _check_and_leave;
1312  }
1313 
1314  if (nextFrame < ebp)
1315  {
1316  ERROR("[ERROR] Return stack frame %x is smaller than current %llx\n", nextFrame, ebp);
1317  goto _check_and_leave;
1318  }
1319 
1320  if (remaining >= 8)
1321  {
1322  retAddress = pStack[1];
1323  }
1324  else
1325  {
1326  status = IntVirtMemRead(ebp + 4, 4, cr3, &retAddress, NULL);
1327  if (!INT_SUCCESS(status))
1328  {
1329  LOG("[ERROR] Got to the end of the stack at 0x%016llx: 0x%08x\n", ebp + 4, status);
1330  goto _check_and_leave;
1331  }
1332  }
1333 
1334  pMod = IntWinUmModFindByAddress(Process, retAddress);
1335  if (NULL == pMod)
1336  {
1337  WARNING("[WARNING] Failed getting the dll base for RIP %08x\n", retAddress);
1338  goto _check_and_leave;
1339  }
1340 
1341  if (pMod->Cache && pMod->Cache->Headers)
1342  {
1344 
1345  status = IntPeGetSectionHeaderByRva(pMod->VirtualBase,
1346  pMod->Cache->Headers,
1347  (DWORD)(retAddress - pMod->VirtualBase),
1348  &sec);
1349  if (!INT_SUCCESS(status) ||
1350  (!(sec.Characteristics & IMAGE_SCN_CNT_CODE) &&
1352  {
1353  goto _save_and_next;
1354  }
1355  }
1356 
1357 _save_and_next:
1358  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnAddress = retAddress;
1359  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnModule = pMod;
1360 
1361  StackTrace->Traces[StackTrace->NumberOfTraces].RetAddrPointer = ebp + 4;
1362  StackTrace->Traces[StackTrace->NumberOfTraces].CalledAddress = 0;
1363  StackTrace->Traces[StackTrace->NumberOfTraces].CurrentRip = 0;
1364 
1365  ++StackTrace->NumberOfTraces;
1366 
1367  if ((ebp & PAGE_MASK) != (nextFrame & PAGE_MASK))
1368  {
1369  // We remap since we are in a different page... No point in recalculating the pStack pointer.
1370  remap = TRUE;
1371  }
1372  else
1373  {
1374  // We know that nextFrame > ebp
1375  DWORD diff = (DWORD)(nextFrame - ebp);
1376 
1377  remap = FALSE;
1378 
1379  // Just recalculate the stack pointer, no need to do a whole remap.
1380  pStack = (PDWORD)((PBYTE)pStack + diff);
1381  }
1382 
1383  ebp = nextFrame;
1384  }
1385 
1386 _check_and_leave:
1387  if (NULL != pStack)
1388  {
1389  IntVirtMemUnmap(&pStack);
1390  }
1391 
1392  if (0 == StackTrace->NumberOfTraces)
1393  {
1394  if (!INT_SUCCESS(status))
1395  {
1396  return status;
1397  }
1398 
1399  return INT_STATUS_NOT_FOUND;
1400  }
1401 
1402  return INT_STATUS_SUCCESS;
1403 }
1404 
1405 
1406 static INTSTATUS
1408  _In_ PIG_ARCH_REGS Registers,
1409  _In_ PWIN_PROCESS_OBJECT Process,
1410  _In_ DWORD MaxNumberOfTraces,
1411  _Inout_ STACK_TRACE *StackTrace
1412  )
1423 {
1424  QWORD stackFrame;
1425  INTSTATUS status;
1426  PBYTE pStack;
1427 
1428  stackFrame = Registers->Rsp;
1429 
1430  for (DWORD j = 0; j < 2; j++)
1431  {
1432  DWORD remaining = PAGE_REMAINING(stackFrame);
1433 
1434  status = IntVirtMemMap(stackFrame, remaining, Registers->Cr3, 0, &pStack);
1435  if (!INT_SUCCESS(status))
1436  {
1437  TRACE("[WARNING] Cannot get a stack at address %llx (start RSP %llx): 0x%08x\n",
1438  stackFrame, Registers->Rsp, status);
1439  return status;
1440  }
1441 
1442  for (DWORD i = 0; i < remaining; i += 8)
1443  {
1444  PWIN_PROCESS_MODULE pMod;
1445  QWORD ret = 0;
1447 
1448  ret = *(QWORD *)(pStack + i);
1449 
1450  pMod = IntWinUmModFindByAddress(Process, ret);
1451  if (NULL == pMod)
1452  {
1453  continue;
1454  }
1455 
1456  if (pMod->Cache && pMod->Cache->Headers)
1457  {
1458  status = IntPeGetSectionHeaderByRva(pMod->VirtualBase,
1459  pMod->Cache->Headers,
1460  (DWORD)(ret - pMod->VirtualBase),
1461  &sec);
1462  if (!INT_SUCCESS(status))
1463  {
1464  continue;
1465  }
1466 
1467  if (!(sec.Characteristics & IMAGE_SCN_CNT_CODE) &&
1469  {
1470  continue;
1471  }
1472  }
1473 
1474  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnAddress = ret;
1475  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnModule = pMod;
1476  StackTrace->Traces[StackTrace->NumberOfTraces].RetAddrPointer = stackFrame + i;
1477 
1478  if (++StackTrace->NumberOfTraces >= MaxNumberOfTraces)
1479  {
1480  break;
1481  }
1482  }
1483 
1484  IntVirtMemUnmap(&pStack);
1485 
1486  if (StackTrace->NumberOfTraces >= MaxNumberOfTraces)
1487  {
1488  break;
1489  }
1490  }
1491 
1492  if (StackTrace->NumberOfTraces == 0)
1493  {
1494  return INT_STATUS_NOT_FOUND;
1495  }
1496 
1497  return INT_STATUS_SUCCESS;
1498 }
1499 
1500 
1501 INTSTATUS
1503  _In_ PIG_ARCH_REGS Registers,
1504  _In_ PWIN_PROCESS_OBJECT Process,
1505  _In_ DWORD MaxNumberOfTraces,
1506  _Out_ STACK_TRACE *StackTrace
1507  )
1518 {
1519  INTSTATUS status;
1520  DWORD csType;
1521 
1522  if (NULL == Registers)
1523  {
1525  }
1526 
1527  if (NULL == Process)
1528  {
1530  }
1531 
1532  if (0 == MaxNumberOfTraces)
1533  {
1535  }
1536 
1537  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, Registers->Rip))
1538  {
1539  return INT_STATUS_NOT_SUPPORTED;
1540  }
1541 
1542  memzero(StackTrace->Traces, MaxNumberOfTraces * sizeof(STACK_ELEMENT));
1543  StackTrace->StartRip = Registers->Rip;
1544  StackTrace->NumberOfTraces = 0;
1545 
1546  status = IntGetCurrentMode(IG_CURRENT_VCPU, &csType);
1547  if (!INT_SUCCESS(status))
1548  {
1549  ERROR("[ERROR] IntGetCurrentMode failed: 0x%08x\n", status);
1550  return status;
1551  }
1552 
1553  if ((csType != IG_CS_TYPE_32B) && (csType != IG_CS_TYPE_64B))
1554  {
1555  ERROR("[ERROR] Unsupported CS type: %d\n", csType);
1556  return INT_STATUS_NOT_SUPPORTED;
1557  }
1558 
1559  if (csType == IG_CS_TYPE_32B)
1560  {
1561  QWORD stackFrame = Registers->Rbp;
1562 
1563  StackTrace->Bits64 = FALSE;
1564 
1565  if ((stackFrame > Registers->Rsp &&
1566  stackFrame - Registers->Rsp > 3 * PAGE_SIZE) ||
1567  (stackFrame < Registers->Rsp &&
1568  Registers->Rsp - stackFrame > 3 * PAGE_SIZE))
1569  {
1570  // We probably are not in a stackframe, so get the first address on the stack and consider it to be the
1571  // return address
1572  PWIN_PROCESS_MODULE pMod;
1573  DWORD retAddress = 0;
1574  BOOLEAN bFound = FALSE;
1575 
1576  stackFrame = Registers->Rsp;
1577 
1578  // Parse the first 8 DWORDs on the stack, because the function might have done some pushes
1579  // (e.g. memset does not do a stack frame, but performs a push edi, so we shouldn't take the edi value as
1580  // the return address)
1581  for (size_t stackIndex = 0; stackIndex < 8; stackIndex++)
1582  {
1583  status = IntVirtMemRead(stackFrame + stackIndex * 4, 4, Registers->Cr3, &retAddress, NULL);
1584  if (!INT_SUCCESS(status))
1585  {
1586  ERROR("[ERROR] RSP 0x%016llx is not present: %08x\n", stackFrame + stackIndex * 4, status);
1587  return status;
1588  }
1589 
1590  pMod = IntWinUmModFindByAddress(Process, retAddress);
1591  if (NULL == pMod)
1592  {
1593  continue;
1594  }
1595 
1596  bFound = TRUE;
1597  break;
1598  }
1599 
1600  if (!bFound)
1601  {
1602  ERROR("[ERROR] DLL base was not found on stack 0x%016llx\n", stackFrame);
1603  return INT_STATUS_NOT_FOUND;
1604  }
1605 
1606  StackTrace->NumberOfTraces = 1;
1607 
1608  StackTrace->Traces[0].ReturnAddress = retAddress;
1609  StackTrace->Traces[0].ReturnModule = pMod;
1610  StackTrace->Traces[0].RetAddrPointer = stackFrame;
1611 
1612  return INT_STATUS_SUCCESS;
1613  }
1614 
1615  status = IntWinStackTraceGetUser32(Registers, Process, MaxNumberOfTraces, StackTrace);
1616  if (INT_SUCCESS(status))
1617  {
1618  return INT_STATUS_SUCCESS;
1619  }
1620 
1621  return INT_STATUS_NOT_FOUND;
1622  }
1623 
1624  StackTrace->Bits64 = TRUE;
1625 
1626  return IntWinStackTraceGetUser64(Registers, Process, MaxNumberOfTraces, StackTrace);
1627 }
1628 
1629 
1630 INTSTATUS
1632  _In_ QWORD UserRsp,
1633  _In_ DWORD SegCs,
1634  _In_ BOOLEAN IsWow64Stack,
1635  _Inout_ DPI_EXTRA_INFO *DpiExtraInfo,
1636  _Out_ BOOLEAN *IsPivoted
1637  )
1652 {
1653  QWORD tibBase;
1654  QWORD stackBase;
1655  QWORD stackLimit;
1656  DWORD csType;
1657  DWORD alignSize;
1658  INTSTATUS status;
1659 
1660  tibBase = 0;
1661  stackBase = 0;
1662  stackLimit = 0;
1663  csType = 0;
1664  status = INT_STATUS_SUCCESS;
1665 
1666  if (NULL == IsPivoted)
1667  {
1669  }
1670 
1671  *IsPivoted = FALSE;
1672  alignSize = gGuest.WordSize;
1673 
1674  if (gGuest.Guest64)
1675  {
1676  switch (SegCs)
1677  {
1679  csType = IG_CS_TYPE_32B;
1680  alignSize = sizeof(DWORD);
1681  break;
1682 
1684  csType = IG_CS_TYPE_64B;
1685  break;
1686 
1687  default:
1688  ERROR("[ERROR] Unrecognized CS value: 0x%08x\n", SegCs);
1690  }
1691  }
1692  else
1693  {
1694  csType = IG_CS_TYPE_32B;
1695  }
1696 
1697  if (0 == UserRsp ||
1698  IS_KERNEL_POINTER_WIN(gGuest.Guest64, UserRsp) ||
1699  UserRsp % alignSize != 0)
1700  {
1702  }
1703 
1704 
1705  status = IntWinThrGetCurrentTib(IG_CS_RING_0, csType, &tibBase);
1706  if (!INT_SUCCESS(status))
1707  {
1708  ERROR("[ERROR] IntWinThrGetCurrentTib failed: 0x%08x\n", status);
1709  return status;
1710  }
1711 
1712  // Teb field will be NULL for WSL threads
1713  if (0 == tibBase)
1714  {
1716  }
1717 
1718  status = IntWinThrGetUmStackBaseAndLimitFromTib(tibBase, csType, gVcpu->Regs.Cr3, &stackBase, &stackLimit);
1719  if (!INT_SUCCESS(status))
1720  {
1721  if ((INT_STATUS_PAGE_NOT_PRESENT == status) ||
1723  {
1724  WARNING("[WARNING] IntWinThrGetUmStackBaseAndLimitFromTib failed: 0x%08x\n", status);
1726  }
1727 
1728  ERROR("[ERROR] IntWinThrGetUmStackBaseAndLimitFromTib failed: 0x%08x\n", status);
1729  return status;
1730  }
1731 
1732  if (IsWow64Stack)
1733  {
1734  DpiExtraInfo->DpiPivotedStackExtraInfo.Wow64StackBase = stackBase;
1735  DpiExtraInfo->DpiPivotedStackExtraInfo.Wow64StackLimit = stackLimit;
1736  }
1737  else
1738  {
1739  DpiExtraInfo->DpiPivotedStackExtraInfo.StackBase = stackBase;
1740  DpiExtraInfo->DpiPivotedStackExtraInfo.StackLimit = stackLimit;
1741  }
1742 
1743  if (UserRsp < stackLimit || UserRsp > stackBase)
1744  {
1745  WARNING("[WARNING] UM stack (0x%016llx) outside of the limit and base interval "
1746  "from the current TIB [0x%016llx, 0x%016llx].\n", UserRsp, stackLimit, stackBase);
1747  *IsPivoted = TRUE;
1748  }
1749 
1750  return status;
1751 }
1752 
1753 
1754 static INTSTATUS
1756  _In_ QWORD KernelStack,
1757  _Inout_ DPI_EXTRA_INFO *DpiExtraInfo,
1758  _Out_ KTRAP_FRAME64 *TrapFrame
1759  )
1775 {
1776  INTSTATUS status;
1777  BYTE *stackPointer;
1778  QWORD stackPointerGVA;
1779  BOOLEAN bFound;
1780 
1781  stackPointer = NULL;
1782  stackPointerGVA = 0;
1783  bFound = FALSE;
1784 
1785  if (!IS_KERNEL_POINTER_WIN(TRUE, KernelStack) || KernelStack % 8 != 0)
1786  {
1788  }
1789 
1790  if (NULL == TrapFrame)
1791  {
1793  }
1794 
1795  stackPointerGVA = ALIGN_DOWN(KernelStack, PAGE_SIZE) - PAGE_SIZE;
1796 
1797  // Map the first page of the stack, the TrapFrame should be there
1798  status = IntVirtMemMap(stackPointerGVA, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &stackPointer);
1799  if (!INT_SUCCESS(status))
1800  {
1801  ERROR("[ERROR] IntVirtMemMap failed from GVA 0x%016llx: 0x%08x\n", stackPointerGVA, status);
1802  return status;
1803  }
1804 
1805  for (QWORD i = 0; i < PAGE_SIZE - sizeof(KTRAP_FRAME64); i += sizeof(QWORD))
1806  {
1807  bFound = IntWinIsUmTrapFrame(stackPointer + i);
1808 
1809  if (bFound)
1810  {
1811  *TrapFrame = *(PKTRAP_FRAME64)(stackPointer + i);
1812 
1813  DpiExtraInfo->DpiPivotedStackExtraInfo.TrapFrameAddress = stackPointerGVA + i;
1814 
1815  TRACE("[INFO] TrapFrame found: 0x%016llx\n", stackPointerGVA + i);
1816  break;
1817  }
1818  }
1819 
1820  IntVirtMemUnmap(&stackPointer);
1821 
1822  if (!bFound)
1823  {
1824  status = INT_STATUS_NOT_FOUND;
1825  }
1826 
1827  return status;
1828 }
1829 
1830 
1831 static INTSTATUS
1833  _In_ DWORD KernelStack,
1834  _Inout_ DPI_EXTRA_INFO *DpiExtraInfo,
1835  _Out_ KTRAP_FRAME32 *TrapFrame
1836  )
1852 {
1853  INTSTATUS status;
1854  BYTE *stackPointer;
1855  QWORD stackPointerGVA;
1856  BOOLEAN bFound;
1857 
1858  status = INT_STATUS_SUCCESS;
1859  stackPointer = NULL;
1860  stackPointerGVA = 0;
1861  bFound = FALSE;
1862 
1863  if (!IS_KERNEL_POINTER_WIN(FALSE, KernelStack) || KernelStack % 4 != 0)
1864  {
1866  }
1867 
1868  if (NULL == TrapFrame)
1869  {
1871  }
1872 
1873  stackPointerGVA = ALIGN_DOWN(KernelStack, PAGE_SIZE) - PAGE_SIZE;
1874 
1875  // Map the first page of the stack, the TrapFrame should be there
1876  status = IntVirtMemMap(stackPointerGVA, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &stackPointer);
1877  if (!INT_SUCCESS(status))
1878  {
1879  ERROR("[ERROR] IntVirtMemMap failed from GVA 0x%016llx: 0x%08x\n", stackPointerGVA, status);
1880  return status;
1881  }
1882 
1883  for (DWORD i = 0; i < PAGE_SIZE - sizeof(KTRAP_FRAME32); i += sizeof(DWORD))
1884  {
1885  bFound = IntWinIsUmTrapFrame(stackPointer + i);
1886 
1887  if (bFound)
1888  {
1889  *TrapFrame = *(PKTRAP_FRAME32)(stackPointer + i);
1890 
1891  DpiExtraInfo->DpiPivotedStackExtraInfo.TrapFrameAddress = stackPointerGVA + i;
1892 
1893  TRACE("[INFO] TrapFrame found: 0x%016llx\n", stackPointerGVA + i);
1894  break;
1895  }
1896  }
1897 
1898  IntVirtMemUnmap(&stackPointer);
1899 
1900  if (!bFound)
1901  {
1902  status = INT_STATUS_NOT_FOUND;
1903  }
1904 
1905  return status;
1906 }
1907 
1908 
1909 INTSTATUS
1911  _Out_ QWORD *UserRsp,
1912  _Out_ DWORD *SegCs,
1913  _In_ BOOLEAN Fallback,
1914  _Inout_ DPI_EXTRA_INFO *DpiExtraInfo
1915  )
1930 {
1931  INTSTATUS status = INT_STATUS_SUCCESS;
1932  QWORD currentThread = 0;
1933 
1934  if (NULL == UserRsp)
1935  {
1937  }
1938 
1939  if (NULL == SegCs)
1940  {
1942  }
1943 
1944  *UserRsp = 0;
1945  *SegCs = 0;
1946 
1947  status = IntWinThrGetCurrentThread(IG_CURRENT_VCPU, &currentThread);
1948  if (!INT_SUCCESS(status))
1949  {
1950  ERROR("[ERROR] IntWinThrGetCurrentThread failed: 0x%08x\n", status);
1951  return status;
1952  }
1953 
1954  if (gGuest.Guest64)
1955  {
1956  QWORD trapFrameAddress = 0;
1957  QWORD stackBase = 0;
1958  KTRAP_FRAME64 trapFrame = { 0 };
1959 
1960  status = IntKernVirtMemFetchQword(currentThread + WIN_KM_FIELD(Thread, TrapFrame), &trapFrameAddress);
1961  if (!INT_SUCCESS(status))
1962  {
1963  ERROR("[ERROR] Failed getting the TrapFrame from Ethread 0x%016llx: 0x%08x\n", currentThread, status);
1964  return status;
1965  }
1966 
1967  // TrapFrame field for WSL thread will be NULL, we can find the TrapFrame on stack
1968  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, trapFrameAddress))
1969  {
1970  QWORD count = 0;
1971 
1972  status = IntKernVirtMemRead(trapFrameAddress, sizeof(KTRAP_FRAME64), &trapFrame, NULL);
1973  if (!INT_SUCCESS(status))
1974  {
1975  ERROR("[ERROR] Failed getting the trap frame from CurrentThread 0x%016llx: 0x%08x\n",
1976  currentThread, status);
1977  return status;
1978  }
1979 
1980  DpiExtraInfo->DpiPivotedStackExtraInfo.TrapFrameAddress = trapFrameAddress;
1981 
1982  while ((IS_KERNEL_POINTER_WIN(gGuest.Guest64, trapFrame.TrapFrame)) &&
1983  (count++ < TRAPFRAME_MAX_ITERATIONS))
1984  {
1985  trapFrameAddress = trapFrame.TrapFrame;
1986 
1987  status = IntKernVirtMemRead(trapFrameAddress, sizeof(KTRAP_FRAME64), &trapFrame, NULL);
1988  if (!INT_SUCCESS(status))
1989  {
1990  ERROR("[ERROR] Failed getting the TrapFrame from CurrentThread 0x%016llx: 0x%08x\n",
1991  currentThread, status);
1992  return status;
1993  }
1994 
1995  DpiExtraInfo->DpiPivotedStackExtraInfo.TrapFrameAddress = trapFrameAddress;
1996  }
1997 
1998  if (count == TRAPFRAME_MAX_ITERATIONS)
1999  {
2000  WARNING("[WARNING] Specially crafted TrapFrame somehow: 0x%016llx\n", trapFrameAddress);
2001  return INT_STATUS_NOT_SUPPORTED;
2002  }
2003  }
2004 
2005  // Check if the found TrapFrame is a valid user-mode one, fall back to stack searching if not
2006  if (!IntWinIsUmTrapFrame(&trapFrame) && Fallback)
2007  {
2008  status = IntKernVirtMemFetchQword(currentThread + WIN_KM_FIELD(Thread, StackBase), &stackBase);
2009  if (!INT_SUCCESS(status))
2010  {
2011  ERROR("[ERROR] Failed getting the StackBase from CurrentThread 0x%016llx: 0x%08x\n",
2012  currentThread, status);
2013  return status;
2014  }
2015 
2016  status = IntWinStackUserTrapFrameGet64(stackBase, DpiExtraInfo, &trapFrame);
2017  if (!INT_SUCCESS(status))
2018  {
2019  ERROR("[ERROR] Failed getting a TrapFrame: 0x%08x.\n", status);
2020  return status;
2021  }
2022  }
2023 
2024  DpiExtraInfo->DpiPivotedStackExtraInfo.CurrentStack = trapFrame.Rsp;
2025 
2026  *UserRsp = trapFrame.Rsp;
2027  *SegCs = trapFrame.SegCs;
2028  }
2029  else
2030  {
2031  KTRAP_FRAME32 trapFrame = { 0 };
2032  DWORD trapFrameAddress = 0;
2033  DWORD stackBase = 0;
2034 
2035  status = IntKernVirtMemFetchDword(currentThread + WIN_KM_FIELD(Thread, TrapFrame), &trapFrameAddress);
2036  if (!INT_SUCCESS(status))
2037  {
2038  ERROR("[ERROR] Failed getting the TrapFrame from Ethread 0x%016llx: 0x%08x\n", currentThread, status);
2039  return status;
2040  }
2041 
2042  // TrapFrame field for WSL thread will be NULL, we can find the TrapFrame on stack
2043  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, trapFrameAddress))
2044  {
2045  status = IntKernVirtMemRead(trapFrameAddress, sizeof(KTRAP_FRAME32), &trapFrame, NULL);
2046  if (!INT_SUCCESS(status))
2047  {
2048  ERROR("[ERROR] Failed reading the TrapFrame from CurrentThread 0x%016llx: 0x%08x\n",
2049  currentThread, status);
2050  return status;
2051  }
2052 
2053  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, trapFrame.Eip))
2054  {
2055  WARNING("[WARNING] Current TrapFrame 0x%08x has a kernel mode Eip: 0x%08x \n",
2056  trapFrameAddress, trapFrame.Eip);
2057 
2059  }
2060 
2061  DpiExtraInfo->DpiPivotedStackExtraInfo.TrapFrameAddress = trapFrameAddress;
2062  }
2063 
2064  // Check if the found TrapFrame is a valid user-mode one, fall back to stack searching if not
2065  if (!IntWinIsUmTrapFrame(&trapFrame) && Fallback)
2066  {
2067  status = IntKernVirtMemFetchDword(currentThread + WIN_KM_FIELD(Thread, StackBase), &stackBase);
2068  if (!INT_SUCCESS(status))
2069  {
2070  ERROR("[ERROR] Failed getting the StackBase from CurrentThread 0x%016llx: 0x%08x\n",
2071  currentThread, status);
2072  return status;
2073  }
2074 
2075  status = IntWinStackUserTrapFrameGet32(stackBase, DpiExtraInfo, &trapFrame);
2076  if (!INT_SUCCESS(status))
2077  {
2078  ERROR("[ERROR] Failed getting a TrapFrame: 0x%08x.\n", status);
2079  return status;
2080  }
2081  }
2082 
2083  DpiExtraInfo->DpiPivotedStackExtraInfo.CurrentStack = trapFrame.HardwareEsp;
2084 
2085  *UserRsp = trapFrame.HardwareEsp;
2086  }
2087 
2088  return INT_STATUS_SUCCESS;
2089 }
2090 
2091 
2092 INTSTATUS
2094  _In_ WIN_PROCESS_OBJECT *Process,
2095  _In_ WIN_PROCESS_OBJECT *RealParent,
2096  _Inout_ DPI_EXTRA_INFO *DpiExtraInfo
2097  )
2106 // It's DpiPivotedStackExtraInfo.CurrentWow64Stack will be set to the rsp upon success.
2110 {
2111  INTSTATUS status = INT_STATUS_SUCCESS;
2112  QWORD tib = 0;
2113  QWORD wow64SaveArea = 0;
2114  QWORD userWow64Rsp = 0;
2115 
2116  // Here we know we are in ring 0 and we want to get the user gs value (TIB), so it's safe to put cs type 64 bits.
2118  if (!INT_SUCCESS(status))
2119  {
2120  ERROR("[ERROR] IntWinThrGetCurrentTib failed: 0x%08x\n", status);
2121  return status;
2122  }
2123 
2124  // These all might be swapped as they reside in UM. We have to check it and bail out if it happens.
2125  status = IntVirtMemRead(tib + WIN_UM_FIELD(Teb, Wow64SaveArea),
2126  gGuest.WordSize, RealParent->Cr3, &wow64SaveArea, NULL);
2128  {
2129  INFO("[INFO] IntVirtMemRead failed: 0x%08x, the page 0x%016llx seems to be swapped out...\n", status,
2130  tib + WIN_UM_FIELD(Teb, Wow64SaveArea));
2131  return INT_STATUS_SUCCESS;
2132  }
2133  else if (!INT_SUCCESS(status))
2134  {
2135  ERROR("[ERROR] IntVirtMemRead failed: 0x%08x\n", status);
2136  return status;
2137  }
2138 
2139  status = IntVirtMemRead(wow64SaveArea + WIN_UM_FIELD(Teb, Wow64StackInSaveArea), sizeof(DWORD), RealParent->Cr3,
2140  &userWow64Rsp, NULL);
2142  {
2143  INFO("[INFO] IntVirtMemRead failed: 0x%08x, the page 0x%016llx seems to be swapped out...\n", status,
2144  wow64SaveArea + WIN_UM_FIELD(Teb, Wow64StackInSaveArea));
2145  return INT_STATUS_SUCCESS;
2146  }
2147  else if (!INT_SUCCESS(status))
2148  {
2149  ERROR("[ERROR] IntVirtMemRead failed: 0x%08x\n", status);
2150  return status;
2151  }
2152 
2153  DpiExtraInfo->DpiPivotedStackExtraInfo.CurrentWow64Stack = userWow64Rsp;
2154 
2155  // Here we should get from fs the stack base and stack limit so we put the dummy cs which we know is valid for wow64
2156  status = IntWinStackUserCheckIsPivoted(userWow64Rsp, CODE_SEG_UM_32_GUEST_64, TRUE, DpiExtraInfo,
2157  &Process->CreationInfo.ParentHasPivotedStack);
2158  if (!INT_SUCCESS(status))
2159  {
2160  ERROR("[ERROR] IntWinStackUserCheckIsPivoted failed: 0x%08x.\n", status);
2161  return status;
2162  }
2163  else if (Process->CreationInfo.ParentHasPivotedStack)
2164  {
2165  WARNING("[WARNING] Process 0x%016llx created with WoW64 pivoted stack.\n", Process->EprocessAddress);
2166  }
2167 
2168  return status;
2169 }
2170 
2171 
2172 BOOLEAN
2174  _In_ void *TrapFrame
2175  )
2183 {
2184  if (gGuest.Guest64)
2185  {
2186  KTRAP_FRAME64 *trapFrame = (KTRAP_FRAME64 *)TrapFrame;
2187 
2188  // EFLAGS check reserved bits
2189  if ((trapFrame->EFlags >= (1 << 22)) ||
2190  (trapFrame->EFlags & (1 << 1)) == 0 ||
2191  (trapFrame->EFlags & (1 << 3)) != 0 ||
2192  (trapFrame->EFlags & (1 << 5)) != 0 ||
2193  (trapFrame->EFlags & (1 << 15)) != 0)
2194  {
2195  return FALSE;
2196  }
2197 
2198  // Guest 64 valid SegCs
2199  switch (trapFrame->SegCs)
2200  {
2202  break;
2203 
2204  default:
2205  return FALSE;
2206  }
2207 
2208  // UM, aligned Rsp
2209  if (trapFrame->Rsp % 8 != 0 ||
2210  IS_KERNEL_POINTER_WIN(TRUE, trapFrame->Rsp) ||
2211  trapFrame->Rsp == 0 ||
2212  trapFrame->Rsp == QWORD_MAX)
2213  {
2214  return FALSE;
2215  }
2216 
2217  // UM Rip
2218  if (IS_KERNEL_POINTER_WIN(TRUE, trapFrame->Rip) ||
2219  trapFrame->Rip == 0 ||
2220  trapFrame->Rip == QWORD_MAX)
2221  {
2222  return FALSE;
2223  }
2224  }
2225  else
2226  {
2227  KTRAP_FRAME32 *trapFrame = (KTRAP_FRAME32 *)TrapFrame;
2228 
2229  // EFLAGS check reserved bits
2230  if ((trapFrame->EFlags >= (1 << 22)) ||
2231  (trapFrame->EFlags & (1 << 1)) == 0 ||
2232  (trapFrame->EFlags & (1 << 3)) != 0 ||
2233  (trapFrame->EFlags & (1 << 5)) != 0 ||
2234  (trapFrame->EFlags & (1 << 15)) != 0)
2235  {
2236  return FALSE;
2237  }
2238 
2239  // Guest 32 valid SegCs
2240  if (trapFrame->SegCs != CODE_SEG_UM_32_GUEST_32)
2241  {
2242  return FALSE;
2243  }
2244 
2245  // Valid Esp
2246  if (IS_KERNEL_POINTER_WIN(FALSE, trapFrame->HardwareEsp) ||
2247  trapFrame->HardwareEsp % 4 != 0 ||
2248  trapFrame->HardwareEsp == 0 ||
2249  trapFrame->HardwareEsp == DWORD_MAX)
2250  {
2251  return FALSE;
2252  }
2253 
2254  // Valid Eip
2255  if (IS_KERNEL_POINTER_WIN(FALSE, trapFrame->Eip) ||
2256  trapFrame->Eip == 0 ||
2257  trapFrame->Eip == DWORD_MAX)
2258  {
2259  return FALSE;
2260  }
2261  }
2262 
2263  return TRUE;
2264 }
#define IMAGE_SCN_MEM_EXECUTE
Definition: winpe.h:472
#define INT_STATUS_PAGE_NOT_PRESENT
Indicates that a virtual address is not present.
Definition: introstatus.h:438
INTSTATUS IntPeParseUnwindDataInBuffer(QWORD ImageBase, BYTE *Buffer, DWORD BufferSize, RUNTIME_FUNCTION *RuntimeFunction, DWORD RipOffset, DWORD *ReservedStack, DWORD *BeginAddress, BOOLEAN *InterruptFunction, BOOLEAN *ExceptionFunction, BOOLEAN *HasFramePointer)
Parse the unwind data for the indicated function and return the prologue size.
Definition: winpe.c:2699
#define MAX_FUNC_LENGTH
The maximum length (in bytes) of a function.
Definition: winpe.h:582
INTSTATUS IntPeGetSectionHeaderByRva(QWORD ImageBase, BYTE *ImageBaseBuffer, DWORD GuestRva, IMAGE_SECTION_HEADER *SectionHeader)
Given a relative virtual address, return the section header which describes the section the RVA lies ...
Definition: winpe.c:682
#define _Out_
Definition: intro_sal.h:22
_Bool BOOLEAN
Definition: intro_types.h:58
DWORD EFlags
Definition: wddefs.h:834
DWORD SegCs
Definition: wddefs.h:833
INTSTATUS IntVirtMemUnmap(void **HostPtr)
Unmaps a memory range previously mapped with IntVirtMemMap.
Definition: introcore.c:2234
INTSTATUS IntPeGetRuntimeFunctionInBuffer(QWORD ImageBase, BYTE *Buffer, DWORD BufferSize, DWORD Rva, RUNTIME_FUNCTION *RuntimeFunction)
Parses the exception directory and gets the runtime function corresponding to the Rva...
Definition: winpe.c:2242
struct _KTRAP_FRAME32 KTRAP_FRAME32
uint8_t BYTE
Definition: intro_types.h:47
static INTSTATUS IntWinStackTraceGetUser32(PIG_ARCH_REGS Registers, PWIN_PROCESS_OBJECT Process, DWORD MaxNumberOfTraces, STACK_TRACE *StackTrace)
Get the user stack trace of a 32 bit windows process.
Definition: winstack.c:1241
struct _KTRAP_FRAME32 * PKTRAP_FRAME32
WINDOWS_GUEST * gWinGuest
Global variable holding the state of a Windows guest.
Definition: winguest.c:35
IG_ARCH_REGS Regs
The current state of the guest registers.
Definition: guests.h:95
static INTSTATUS IntWinStackTraceGet32(DWORD Stack, DWORD Eip, DWORD MaxNumberOfTraces, QWORD Flags, STACK_TRACE *StackTrace)
Get a kernel stack trace starting from the current stack pointer for 32 bit systems.
Definition: winstack.c:1042
#define _In_
Definition: intro_sal.h:21
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:207
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
INTSTATUS IntWinStackUserCheckIsPivoted(QWORD UserRsp, DWORD SegCs, BOOLEAN IsWow64Stack, DPI_EXTRA_INFO *DpiExtraInfo, BOOLEAN *IsPivoted)
Check whether the stack is pivoted by checking if it&#39;s in the bounds of the stack base and limit from...
Definition: winstack.c:1631
#define PAGE_REMAINING(addr)
Definition: pgtable.h:163
WIN_KERNEL_DRIVER Win
Valid only for Windows guests.
Definition: drivers.h:70
DWORD HardwareEsp
Definition: wddefs.h:835
#define STATS_EXIT(id)
Definition: stats.h:148
INTSTATUS IntWinThrGetCurrentTib(IG_CS_RING CurrentRing, IG_CS_TYPE CsType, QWORD *Tib)
Obtain the TIB (Thread Information Block) of the thread running on the current CPU.
Definition: winthread.c:170
#define CODE_SEG_UM_32_GUEST_64
Definition: winstack.h:14
QWORD BaseVa
The guest virtual address of the kernel module that owns this driver object.
Definition: drivers.h:41
#define WIN_UM_FIELD(Structure, Field)
Macro used to access user mode fields inside the WIN_OPAQUE_FIELDS structure.
Definition: winguest.h:764
BOOLEAN IntWinIsUmTrapFrame(void *TrapFrame)
Checks whether a TrapFrame is valid or not.
Definition: winstack.c:2173
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
INTSTATUS IntWinThrGetCurrentThread(DWORD CpuNumber, QWORD *EthreadAddress)
Get the ETHREAD structure address of the thread currently running on the given CPU.
Definition: winthread.c:26
#define PAGE_OFFSET
Definition: pgtable.h:32
Structure that describes a stack trace element.
Definition: guest_stack.h:25
PBYTE MzPeHeaders
The driver`s MZ/PE headers (cached internally).
Definition: windriver.h:34
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
int INTSTATUS
The status data type.
Definition: introstatus.h:24
Measures the cases in which the stack trace mechanism encounters a JMP after a CALL.
Definition: stats.h:81
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
INTSTATUS IntWinStackTraceGet(QWORD StackFrame, QWORD Rip, DWORD MaxNumberOfTraces, QWORD Flags, STACK_TRACE *StackTrace)
Get a kernel stack trace starting from the current stack pointer for 64 bit systems.
Definition: winstack.c:1210
DWORD Eip
Definition: wddefs.h:832
QWORD VirtualBase
Guest virtual address of the loaded module.
Definition: winummodule.h:34
INTSTATUS IntPeParseUnwindData(QWORD ImageBase, BYTE *ImageBaseBuffer, RUNTIME_FUNCTION *RuntimeFunction, DWORD RipOffset, DWORD *ReservedStack, DWORD *BeginAddress, BOOLEAN *InterruptFunction, BOOLEAN *ExceptionFunction, BOOLEAN *HasFramePointer)
Parse the unwind data for the indicated function and return the prologue size.
Definition: winpe.c:2370
INTSTATUS IntWinStackWow64CheckIsPivoted(WIN_PROCESS_OBJECT *Process, WIN_PROCESS_OBJECT *RealParent, DPI_EXTRA_INFO *DpiExtraInfo)
Check whether a wow64 process&#39; stack is pivoted.
Definition: winstack.c:2093
#define LOG(fmt,...)
Definition: glue.h:61
32-bit selector.
Definition: glueiface.h:187
Describes a kernel driver.
Definition: drivers.h:30
uint32_t * PDWORD
Definition: intro_types.h:49
#define STACK_CALL_ADDRESS_IMPRECISE
Flag used to tell that the CalledAddress in a STACK_ELEMENT is not precise (it&#39;s an approximation)...
Definition: guest_stack.h:13
PWIN_PROCESS_MODULE IntWinUmModFindByAddress(PWIN_PROCESS_OBJECT Process, QWORD Gva)
Searches for a user-mode module which contains the indicated guest virtual address.
Definition: winummodule.c:1975
#define INFO(fmt,...)
Definition: glue.h:59
Exposes the functions used to provide Windows Threads related support.
INTSTATUS IntGetCurrentMode(DWORD CpuNumber, DWORD *Mode)
Read the current CS type.
Definition: introcpu.c:977
#define _Inout_
Definition: intro_sal.h:20
INTSTATUS IntKernVirtMemFetchDword(QWORD GuestVirtualAddress, DWORD *Data)
Reads 4 bytes from the guest kernel memory.
Definition: introcore.c:829
#define _Out_opt_
Definition: intro_sal.h:30
INTSTATUS IntKernVirtMemFetchQword(QWORD GuestVirtualAddress, QWORD *Data)
Reads 8 bytes from the guest kernel memory.
Definition: introcore.c:811
#define IG_CURRENT_VCPU
For APIs that take a VCPU number as a parameter, this can be used to specify that the current VCPU sh...
Definition: glueiface.h:324
struct _KTRAP_FRAME64 * PKTRAP_FRAME64
#define STATS_ENTER(id)
Definition: stats.h:141
uint8_t * PBYTE
Definition: intro_types.h:47
#define INT_STATUS_UNSUCCESSFUL
Definition: introstatus.h:335
struct _KTRAP_FRAME64 KTRAP_FRAME64
#define memzero(a, s)
Definition: introcrt.h:35
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:286
#define STACK_INTERRUPT_ROUTINE
Flag used to tell that the ReturnAddress in a STACK_ELEMENT is an interrupt routine.
Definition: guest_stack.h:15
unsigned long long QWORD
Definition: intro_types.h:53
Measures the stack trace mechanism for 32-bit execution contexts.
Definition: stats.h:78
#define TRUE
Definition: intro_types.h:30
#define INT_STATUS_INVALID_PARAMETER_4
Definition: introstatus.h:71
static INTSTATUS IntWinStackUserTrapFrameGet32(DWORD KernelStack, DPI_EXTRA_INFO *DpiExtraInfo, KTRAP_FRAME32 *TrapFrame)
Get a 32 bit trap frame from a kernel stack.
Definition: winstack.c:1832
#define IS_KERNEL_POINTER_WIN(is64, p)
Checks if a guest virtual address resides inside the Windows kernel address space.
Definition: wddefs.h:76
QWORD TrapFrame
Definition: wddefs.h:978
#define TRACE(fmt,...)
Definition: glue.h:58
#define CODE_SEG_UM_64_GUEST_64
Definition: winstack.h:15
#define INT_STATUS_INVALID_PARAMETER_5
Definition: introstatus.h:74
void * Name
The name of the driver.
Definition: drivers.h:54
INTSTATUS IntWinStackUserTrapFrameGetGeneric(QWORD *UserRsp, DWORD *SegCs, BOOLEAN Fallback, DPI_EXTRA_INFO *DpiExtraInfo)
Get a bit trap frame from a kernel stack.
Definition: winstack.c:1910
QWORD KernelVa
The guest virtual address at which the kernel image.
Definition: guests.h:279
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:363
#define WARNING(fmt,...)
Definition: glue.h:60
#define STACK_EXCEPTION_ROUTINE
Flag used to tell that the ReturnAddress in a STACK_ELEMENT is an exception routine.
Definition: guest_stack.h:17
#define QWORD_MAX
Definition: introtypes.h:34
#define PAGE_SIZE
Definition: common.h:53
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
INTSTATUS IntWinThrGetUmStackBaseAndLimitFromTib(QWORD Tib, IG_CS_TYPE CsType, QWORD Cr3, QWORD *StackBase, QWORD *StackLimit)
Obtains the user mode stack base and stack limit values.
Definition: winthread.c:259
#define TRAPFRAME_MAX_ITERATIONS
Definition: winstack.c:15
#define WIN_KM_FIELD(Structure, Field)
Macro used to access kernel mode fields inside the WIN_OPAQUE_FIELDS structure.
Definition: winguest.h:726
uint32_t DWORD
Definition: intro_types.h:49
DWORD KernelBufferSize
The size of the KernelBuffer.
Definition: winguest.h:838
static INTSTATUS IntWinStackTraceGetUser64(PIG_ARCH_REGS Registers, PWIN_PROCESS_OBJECT Process, DWORD MaxNumberOfTraces, STACK_TRACE *StackTrace)
Get the user stack trace of a 64 bit windows process.
Definition: winstack.c:1407
UINT32 Characteristics
Definition: winpe.h:92
#define IMAGE_SCN_CNT_CODE
Definition: winpe.h:425
#define DWORD_MAX
Definition: introtypes.h:33
__must_check INTSTATUS IntVirtMemMap(QWORD Gva, DWORD Length, QWORD Cr3, DWORD Flags, void **HostPtr)
Maps a guest virtual memory range inside Introcore virtual address space.
Definition: introcore.c:2134
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:370
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:48
WINUM_MODULE_CACHE * Cache
Module headers cache.
Definition: winummodule.h:63
#define CODE_SEG_UM_32_GUEST_32
Definition: winstack.h:17
UINT32 EndAddress
Definition: winpe.h:376
INTSTATUS IntVirtMemRead(QWORD Gva, DWORD Length, QWORD Cr3, void *Buffer, DWORD *RetLength)
Reads data from a guest virtual memory range.
Definition: introcore.c:627
#define FIX_GUEST_POINTER(is64, x)
Masks the unused part of a Windows guest virtual address.
Definition: wddefs.h:87
int wstrcasecmp(const WCHAR *buf1, const WCHAR *buf2)
Definition: introcrt.c:98
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
KERNEL_DRIVER * IntDriverFindByAddress(QWORD Gva)
Returns the driver in which Gva resides.
Definition: drivers.c:164
#define INT_STATUS_NO_MAPPING_STRUCTURES
Indicates that not all mapping structures of a virtual address are present.
Definition: introstatus.h:434
DWORD EFlags
Definition: wddefs.h:1016
BYTE * KernelBuffer
A buffer containing the entire kernel image.
Definition: winguest.h:837
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
#define INT_STATUS_NOT_SUPPORTED
Definition: introstatus.h:287
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:57
UINT32 BeginAddress
Definition: winpe.h:375
static INTSTATUS IntWinStackUserTrapFrameGet64(QWORD KernelStack, DPI_EXTRA_INFO *DpiExtraInfo, KTRAP_FRAME64 *TrapFrame)
Get a 64 bit trap frame from a kernel stack.
Definition: winstack.c:1755
64-bit selector.
Definition: glueiface.h:188
static INTSTATUS IntWinStackTraceGet64(QWORD Rsp, QWORD Rip, DWORD MaxTraces, QWORD Flags, STACK_TRACE *StackTrace)
Get a kernel stack trace starting from the current stack pointer for 64 bit systems.
Definition: winstack.c:274
UINT16 SegCs
Definition: wddefs.h:1012
Holds register state.
Definition: glueiface.h:30
Structure that describes a stack trace.
Definition: guest_stack.h:42
#define STACK_FLG_ONLY_DRIVER_ADDRS
Flag that tells to only get addresses inside drivers.
Definition: guest_stack.h:20
INTSTATUS IntWinStackTraceGetUser(PIG_ARCH_REGS Registers, PWIN_PROCESS_OBJECT Process, DWORD MaxNumberOfTraces, STACK_TRACE *StackTrace)
Get the user stack trace of a windows process.
Definition: winstack.c:1502
unsigned long long * PQWORD
Definition: intro_types.h:53
static INTSTATUS IntStackAnalyzePointer(QWORD Gva, QWORD *CallAddress)
Get the address of the kernel function that was called in order to push Gva on the stack as a return ...
Definition: winstack.c:19
BYTE * Headers
A buffer containing the MZ/PE headers of this module.
Definition: winumcache.h:97
#define PAGE_MASK
Definition: pgtable.h:35
INTSTATUS IntPeGetRuntimeFunction(QWORD ImageBase, BYTE *ImageBaseBuffer, DWORD Rva, RUNTIME_FUNCTION *RuntimeFunction)
Parses the exception directory and gets the runtime function corresponding to the Rva...
Definition: winpe.c:2037
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
INTSTATUS IntDecDecodeInstruction(IG_CS_TYPE CsType, QWORD Gva, void *Instrux)
Decode an instruction from the provided guest linear address.
Definition: decoder.c:180
#define FALSE
Definition: intro_types.h:34
#define ALIGN_DOWN(x, a)
Definition: introdefs.h:165
This structure describes a running process inside the guest.
Definition: winprocess.h:81
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68