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 #include "swapmem.h"
14 
15 
16 #define TRAPFRAME_MAX_ITERATIONS 0x100
17 
18 
19 static INTSTATUS
21  _In_ QWORD Gva,
22  _Out_opt_ QWORD *CallAddress
23  )
33 {
34  INTSTATUS status;
35  NDSTATUS ndstatus;
36  BYTE defCode, defData;
37  BYTE code[0x20] = { 0 };
38  INSTRUX instruction;
39  QWORD calledFuncAddress, i;
40  BOOLEAN isCall;
41 
42  status = IntKernVirtMemRead(Gva - 7, sizeof(code), code, NULL);
43  if (!INT_SUCCESS(status))
44  {
45  return status;
46  }
47 
48  // init
49  isCall = TRUE;
50  calledFuncAddress = 0;
51 
52  defData = gGuest.Guest64 ? ND_DATA_64 : ND_DATA_32;
53  defCode = gGuest.Guest64 ? ND_CODE_64 : ND_CODE_32;
54 
55  // We check from big instructions to small ones. This assures that the instructions will be
56  // decoded with prefixes. Also 5-byte instructions (0xe8 calls) are the most frequent ones.
57  // WARNING: Don't add the instruction length to the Gva. The Gva already points to the
58  // instruction after the CALL.
59  for (i = 0; i <= 5; i++)
60  {
61  // we must be sure if the instruction is really a CALL before jumping to cleanup and exit at the end of this
62  // for loop.
63  BOOLEAN bIsInstruxCall = FALSE;
64 
65  ndstatus = NdDecodeEx(&instruction, code + i, sizeof(code) - i, defCode, defData);
66  if (!ND_SUCCESS(ndstatus))
67  {
68  continue;
69  }
70 
71  // get the call address if that's possible
72  switch (instruction.Instruction)
73  {
74  case ND_INS_CALLNR:
75  {
76  bIsInstruxCall = TRUE;
77 
78  calledFuncAddress = Gva + instruction.Operands[0].Info.RelativeOffset.Rel;
79  break;
80  }
81 
82  case ND_INS_CALLNI:
83  {
84  bIsInstruxCall = TRUE;
85 
86  if (ND_OP_IMM == instruction.Operands[0].Type)
87  {
88  calledFuncAddress = instruction.Operands[0].Info.Immediate.Imm;
89  }
90  else if (ND_OP_MEM == instruction.Operands[0].Type)
91  {
92  QWORD fetchAddress;
93 
94  if (instruction.Operands[0].Info.Memory.IsRipRel)
95  {
96  // Disp is already sign extended
97  fetchAddress = Gva + instruction.Operands[0].Info.Memory.Disp;
98  }
99  else if (instruction.Operands[0].Info.Memory.HasDisp &&
100  !instruction.Operands[0].Info.Memory.HasBase &&
101  !instruction.Operands[0].Info.Memory.HasIndex)
102  {
103  fetchAddress = instruction.Operands[0].Info.Memory.Disp;
104  }
105  else
106  {
107  // So far so good, but it's using registers.
108  break;
109  }
110 
111  // Get the actual called address
112  status = IntKernVirtMemFetchQword(fetchAddress, &calledFuncAddress);
113  if (!INT_SUCCESS(status))
114  {
115  WARNING("[WARNING] Failed to get function address from 0x%016llx (Call RIP 0x%016llx): 0x%08x\n",
116  fetchAddress, Gva, status);
117  calledFuncAddress = 0;
118  }
119  }
120  else if (ND_OP_OFFS == instruction.Operands[0].Type)
121  {
122  calledFuncAddress = Gva + instruction.Operands[0].Info.RelativeOffset.Rel;
123  }
124 
125  break;
126  }
127 
128  case ND_INS_CALLFI:
129  case ND_INS_CALLFD:
130  bIsInstruxCall = TRUE;
131 
132  if (ND_OP_OFFS == instruction.Operands[0].Type)
133  {
134  calledFuncAddress = Gva + instruction.Operands[0].Info.RelativeOffset.Rel;
135  }
136  else if (ND_OP_MEM == instruction.Operands[0].Type)
137  {
138  QWORD fetchAddress;
139 
140  if (instruction.Operands[0].Info.Memory.IsRipRel)
141  {
142  // Disp is already sign extended
143  fetchAddress = Gva + instruction.Operands[0].Info.Memory.Disp;
144  }
145  else if (instruction.Operands[0].Info.Memory.HasDisp &&
146  !instruction.Operands[0].Info.Memory.HasBase &&
147  !instruction.Operands[0].Info.Memory.HasIndex)
148  {
149  fetchAddress = instruction.Operands[0].Info.Memory.Disp;
150  }
151  else
152  {
153  // So far so good, but it's using registers.
154  break;
155  }
156 
157  // Get the actual called address
158  status = IntKernVirtMemFetchQword(fetchAddress, &calledFuncAddress);
159  if (!INT_SUCCESS(status))
160  {
161  WARNING("[WARNING] Failed to get function address from 0x%016llx (Call RIP 0x%016llx): 0x%08x\n",
162  fetchAddress, Gva, status);
163  calledFuncAddress = 0;
164  }
165  }
166  else if (ND_OP_REG == instruction.Operands[0].Type)
167  {
168  calledFuncAddress = 0;
169  }
170  else
171  {
173  }
174  break;
175 
176  default:
177  break;
178  }
179 
180  // Make sure that instruction length is good (it doesn't override return address & it hasn't slack space)
181  // Also, we need to make sure that this instruction is REALLY a CALL instruction before jumping to cleanup
182  // and leave
183  if (Gva == Gva - (7 - i) + instruction.Length && bIsInstruxCall)
184  {
185  goto cleanup_and_leave;
186  }
187  }
188 
189  // If we get here and don't find a good instruction then it's no good
190  isCall = FALSE;
191 
192 cleanup_and_leave:
193  if (!isCall || (calledFuncAddress != 0 && !IS_KERNEL_POINTER_WIN(gGuest.Guest64, calledFuncAddress)))
194  {
195  status = INT_STATUS_NOT_FOUND;
196  }
197 
198  if (NULL != CallAddress)
199  {
200  *CallAddress = FIX_GUEST_POINTER(gGuest.Guest64, calledFuncAddress);
201  }
202 
203  return status;
204 }
205 
206 
207 static INTSTATUS
209  _In_ QWORD Rsp,
210  _In_ QWORD Rip,
211  _In_ DWORD MaxTraces,
212  _In_ QWORD Flags,
213  _Inout_ STACK_TRACE *StackTrace
214  )
230 {
231  QWORD currentRip, currentRsp, newRsp, currentModBase;
232  PBYTE pStack, pStackBase, pModBaseMap;
233  BOOLEAN ripInsideSameModule;
234  KERNEL_DRIVER *pDriver;
235  INTSTATUS status;
236 
237  if (NULL == StackTrace || NULL == StackTrace->Traces)
238  {
240  }
241 
243 
244  StackTrace->NumberOfTraces = 0;
245  StackTrace->StartRip = Rip;
246  StackTrace->Bits64 = TRUE;
247  pStack = pModBaseMap = pStackBase = NULL;
248  currentRip = Rip;
249  currentRsp = Rsp & ~7; // Make sure the stack is 8-bytes aligned.
250  currentModBase = 0;
251  pDriver = NULL;
252 
253  ripInsideSameModule = FALSE;
254 
255  do
256  {
257  RUNTIME_FUNCTION runtimeFunction;
258  DWORD prologueSize, tries, beginRva;
259  QWORD retAddress, retModuleBase, calledAddress, retAddrPtr;
260  BOOLEAN interrupt, exception, hasFramePointer, found, fErrorCode;
261 
262  memzero(&runtimeFunction, sizeof(runtimeFunction));
263  prologueSize = tries = beginRva = 0;
264  found = hasFramePointer = interrupt = exception = fErrorCode = FALSE;
265  retModuleBase = retAddress = calledAddress = retAddrPtr = 0;
266  status = INT_STATUS_SUCCESS;
267 
268  // If we are inside the same module then don't remap the module base
269  if (!ripInsideSameModule)
270  {
271  // Unmap the module if it was previously mapped
272  if (NULL != pModBaseMap)
273  {
274  IntVirtMemUnmap(&pModBaseMap);
275  }
276 
277  // Get the image base of the RIP if we don't already have one. If this fails we get out with an error.
278  if (0 == currentModBase)
279  {
280  pDriver = IntDriverFindByAddress(currentRip);
281  if (NULL == pDriver)
282  {
283  if ((Flags & STACK_FLG_ONLY_DRIVER_ADDRS) &&
284  StackTrace->NumberOfTraces > 0)
285  {
286  ERROR("[ERROR] Failed to find the module base of RIP 0x%016llx RSP 0x%016llx at try %d\n",
287  currentRip, currentRsp, StackTrace->NumberOfTraces);
288  }
289  else if (StackTrace->NumberOfTraces == 0)
290  {
291  // If we are on the first try, the the start RIP can be outside of the driver
292  goto _start_searching;
293  }
294  }
295  else
296  {
297  currentModBase = pDriver->BaseVa;
298  }
299 
300  if (0 == currentModBase)
301  {
302  if (Flags & STACK_FLG_ONLY_DRIVER_ADDRS)
303  {
304  goto _cleanup_and_leave;
305  }
306  else
307  {
308  goto _start_searching;
309  }
310  }
311  }
312 
313  // As we always search in the kernel buffer in the case of NT, we don't need the headers to be mapped
314  // again here, so we'll skip it.
315  if (currentModBase != gGuest.KernelVa)
316  {
317  status = IntVirtMemMap(currentModBase, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &pModBaseMap);
318  if (!INT_SUCCESS(status))
319  {
320  // Extra check for user-mode drivers, which are not present all the time
321  if (pDriver != NULL &&
322  (0 == wstrcasecmp(pDriver->Name, u"win32k.sys") ||
323  0 == wstrcasecmp(pDriver->Name, u"TSDDD.dll") ||
324  0 == wstrcasecmp(pDriver->Name, u"cdd.dll")))
325  {
326  // Even if the number of traces is 0, we don't care, since if this driver would have modified
327  // something it would be present.
328  status = INT_STATUS_SUCCESS;
329  }
330  else
331  {
332  ERROR("[ERROR] Failed mapping driver base 0x%016llx to host: 0x%08x\n",
333  currentModBase, status);
334  }
335 
336  goto _cleanup_and_leave;
337  }
338  }
339  }
340  else if (currentModBase == 0)
341  {
342  if (Flags & STACK_FLG_ONLY_DRIVER_ADDRS)
343  {
344  goto _cleanup_and_leave;
345  }
346  else
347  {
348  goto _start_searching;
349  }
350  }
351 
352  if (currentModBase != gGuest.KernelVa)
353  {
354  status = IntPeGetRuntimeFunction(currentModBase,
355  pModBaseMap,
356  (DWORD)(currentRip - currentModBase),
357  &runtimeFunction);
358  }
359  else
360  {
361  status = IntPeGetRuntimeFunctionInBuffer(currentModBase,
364  (DWORD)(currentRip - currentModBase),
365  &runtimeFunction);
366  }
367 
368  if (!INT_SUCCESS(status) && status != INT_STATUS_NOT_FOUND)
369  {
370  ERROR("[ERROR] Failed getting runtime function for module 0x%016llx RIP 0x%016llx: 0x%08x\n",
371  currentModBase, currentRip, status);
372  goto _cleanup_and_leave;
373  }
374  else if (status == INT_STATUS_NOT_FOUND)
375  {
376  // If an address is not found inside the table, it is supposed to be a leaf function and RSP points to
377  // the functions return address (somewhere in documentations).
378  }
379  else
380  {
381  DWORD ripOffset = (DWORD)(currentRip - currentModBase);
382 
383  // We start with this RVA and see in IntPeParseUnwindData if we need to go back further
384  beginRva = runtimeFunction.BeginAddress;
385 
386  // We have one of those functions that starts somewhere and jumps around. We must assume a
387  // rip offset greater than the length of the prologue.
388  if (ripOffset < runtimeFunction.BeginAddress || ripOffset > runtimeFunction.EndAddress)
389  {
390  ripOffset = runtimeFunction.EndAddress - beginRva; // Assume the prologue executed fully
391  }
392  else
393  {
394  ripOffset -= beginRva;
395  }
396 
397  if (currentModBase != gGuest.KernelVa)
398  {
399  status = IntPeParseUnwindData(currentModBase,
400  pModBaseMap,
401  &runtimeFunction,
402  ripOffset,
403  &prologueSize,
404  &beginRva,
405  &interrupt,
406  &exception,
407  &hasFramePointer);
408  }
409  else
410  {
411  status = IntPeParseUnwindDataInBuffer(currentModBase,
414  &runtimeFunction,
415  ripOffset,
416  &prologueSize,
417  &beginRva,
418  &interrupt,
419  &exception,
420  &hasFramePointer);
421  }
422 
423  if (!INT_SUCCESS(status))
424  {
425  ERROR("[ERROR] IntPeParseUnwindData failed for driver 0x%016llx with RIP 0x%016llx and begining "
426  "RVA 0x%x: 0x%08x\n", currentModBase, currentRip, runtimeFunction.BeginAddress, status);
427  goto _cleanup_and_leave;
428  }
429  }
430 
431 _start_searching:
432  // advance the stack pointer
433  newRsp = currentRsp + prologueSize;
434 
435  // If the function has a frame pointer then inside the function the initial size can grow, and there is no
436  // safe way to know that size (and if that code is actually executed). But if the function has no frame pointer
437  // we can only search in 16 values for the return address
438  if (hasFramePointer)
439  {
440  tries = 0x400; // suppose it doesn't reserve more than 1KB on the stack (if it does, this fails)
441  }
442  else
443  {
444  tries = 16;
445  }
446 
447  if (!hasFramePointer && (interrupt || exception))
448  {
449  // Trap frame is just where the stack is
450  status = IntKernVirtMemFetchQword(newRsp + ((exception) ? 8 : 0), &retAddress);
451  if (!INT_SUCCESS(status))
452  {
453  ERROR("[ERROR] Failed getting the return address form GVA 0x%016llx: 0x%08x",
454  newRsp + (exception ? 8 : 0), status);
455  goto _cleanup_and_leave;
456  }
457 
458  if (IS_KERNEL_POINTER_WIN(TRUE, retAddress))
459  {
460  pDriver = IntDriverFindByAddress(retAddress);
461 
462  retModuleBase = pDriver ? pDriver->BaseVa : 0;
463  }
464  else
465  {
466  retModuleBase = 0;
467  pDriver = NULL;
468  }
469 
470  LOG("[STACK] NONHEUR : Found trap frame at stack address 0x%016llx with ret address 0x%016llx \n",
471  newRsp, retAddress);
472 
473  retAddrPtr = newRsp + ((exception) ? 8 : 0);
474  newRsp += (exception) ? (6 * 8) : (5 * 8);
475  found = TRUE;
476  status = INT_STATUS_SUCCESS;
477  goto _next_stack_frame;
478  }
479 
480  // if we are in an exception assume we found the error code already
481  fErrorCode = exception;
482 
483  // If we are in a normal function, without the .pdb files there is no way to know how many parameters the
484  // function has. So analyze all the pointers from here up.
485  while (tries > 0)
486  {
487  // Map the stack if we didn't previously mapped it or the new stack gets into another page
488  if (((currentRsp & PAGE_MASK) != (newRsp & PAGE_MASK)) || NULL == pStackBase)
489  {
490  if (NULL != pStackBase)
491  {
492  IntVirtMemUnmap(&pStackBase);
493  }
494 
495  currentRsp = newRsp;
496 
497  status = IntVirtMemMap(currentRsp & PAGE_MASK, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &pStackBase);
498  if (!INT_SUCCESS(status))
499  {
500  // the stack is guarded, so we got to the end of the current stackframe (probably)
501  status = INT_STATUS_SUCCESS;
502  goto _cleanup_and_leave;
503  }
504  }
505 
506  // get the value on the stack, but first go to the current offset
507  pStack = pStackBase + (newRsp & PAGE_OFFSET);
508 
509  // Here we can return to a user-mode address and we must parse the stack manually for the trap frame
510  // {[ExceptionErrorCode], RIP, CS, EFLAGS, Old RSP, SS}. We don't know where it is, since the function
511  // uses a frame pointer and the RSP could point anywhere...
512  if (hasFramePointer && (exception || interrupt))
513  {
514  PQWORD pTrapFrame = (PQWORD)pStack;
515  BOOLEAN um, km, fRip, fCs, fSs, fRsp, fEflags;
516  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
517 
518  if (!fErrorCode)
519  {
520  // Error code must be a DWORD
521  if (*pTrapFrame > 0xffffffff)
522  {
523  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
524  fErrorCode = exception;
525  retAddress = 0;
526 
527  goto _next_pointer;
528  }
529 
530  fErrorCode = TRUE;
531  goto _next_pointer;
532  }
533 
534  if (!fRip)
535  {
536  // RIP can be both user and kernel mode
537  if (IS_KERNEL_POINTER_WIN(TRUE, *pTrapFrame) && *pTrapFrame != 0xffffffffffffffff)
538  {
539  km = TRUE;
540  }
541  else if (*pTrapFrame > 0xffffffff && *pTrapFrame != 0xffffffffffffffff)
542  {
543  um = TRUE;
544  }
545  else
546  {
547  newRsp -= interrupt ? 8 : 0;
548  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
549  fErrorCode = exception;
550  retAddress = 0;
551 
552  goto _next_pointer;
553  }
554 
555  retAddrPtr = newRsp;
556  retAddress = *pTrapFrame;
557 
558  fRip = TRUE;
559  goto _next_pointer;
560  }
561 
562  if (!fCs)
563  {
564  // CS cannot be greater than 0xff
565  if (*pTrapFrame > 0xff)
566  {
567  newRsp -= interrupt ? 8 * 2 : 8;
568  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
569  fErrorCode = exception;
570  retAddrPtr = retAddress = 0;
571  goto _next_pointer;
572  }
573 
574  // in kernel mode CS is 8 byte aligned
575  if (km && (*pTrapFrame % 8 != 0))
576  {
577  newRsp -= interrupt ? 8 * 2 : 8;
578  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
579  fErrorCode = exception;
580  retAddrPtr = retAddress = 0;
581  goto _next_pointer;
582  }
583  // in user mode CS can have any alignment
584 
585  fCs = TRUE;
586  goto _next_pointer;
587  }
588 
589  if (!fEflags)
590  {
591  if (*pTrapFrame == 0)
592  {
593  newRsp -= interrupt ? 8 * 3 : 8 * 2;
594  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
595  fErrorCode = exception;
596  retAddrPtr = retAddress = 0;
597  goto _next_pointer;
598  }
599 
600  // EFLAGS must be something nice
601  // 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
602  // 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
603  if ((*pTrapFrame > (1 << 22)) || // bits [31 - 22]
604  (*pTrapFrame & (1 << 14)) ||
605  (*pTrapFrame & (1 << 5)) ||
606  (*pTrapFrame & (1 << 3)) ||
607  (0 == (*pTrapFrame & (1 << 1)))) // bit 1 must be 1
608  {
609  newRsp -= interrupt ? 8 * 3 : 8 * 2;
610  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
611  fErrorCode = exception;
612  retAddrPtr = retAddress = 0;
613  goto _next_pointer;
614  }
615 
616  fEflags = TRUE;
617  goto _next_pointer;
618  }
619 
620  if (!fRsp)
621  {
622  // RSP is 8 byte (16 ?!) aligned
623  if (0 != *pTrapFrame % 8)
624  {
625  newRsp -= interrupt ? 8 * 4 : 8 * 3;
626  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
627  fErrorCode = exception;
628  retAddrPtr = retAddress = 0;
629  goto _next_pointer;
630  }
631 
632  // RSP must be in the same space as RIP
633  if (km && (!IS_KERNEL_POINTER_WIN(TRUE, *pTrapFrame)))
634  {
635  newRsp -= interrupt ? 8 * 4 : 8 * 3;
636  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
637  fErrorCode = exception;
638  retAddrPtr = retAddress = 0;
639  goto _next_pointer;
640  }
641  else if (um && (IS_KERNEL_POINTER_WIN(TRUE, *pTrapFrame)))
642  {
643  newRsp -= interrupt ? 8 * 4 : 8 * 3;
644  um = km = fRip = fCs = fSs = fRsp = fEflags = FALSE;
645  fErrorCode = exception;
646  retAddrPtr = retAddress = 0;
647  goto _next_pointer;
648  }
649 
650  fRsp = TRUE;
651  goto _next_pointer;
652  }
653 
654  fSs = TRUE;
655  if (fErrorCode && fRip && fCs && fEflags && fRsp && fSs)
656  {
657  newRsp -= interrupt ? 5 * 8 : 4 * 8;
658  if (um)
659  {
660  retModuleBase = 0; // We could somehow search for it, but that's beyond our purpose
661  pDriver = NULL;
662  }
663 
664  // The beginning of the function is the called address for interrupts. No need to search for it
665  calledAddress = currentModBase + beginRva;
666 
667  goto _found_ret;
668  }
669  }
670  else
671  {
672  QWORD crip;
673 
674  // We do a normal analysis here. It's enough to see that the return address is a CALL or a JMP->CALL
675  // (for imported functions).
676  retAddress = *(PQWORD)pStack;
677 
678  // If this isn't a kernel pointer or the address is some pointer on the stack
679  if (!IS_KERNEL_POINTER_WIN(TRUE, retAddress) || retAddress == 0xffffffffffffffff)
680  {
681  goto _next_pointer;
682  }
683 
684  // For normal pointers we must search analyze the call since the rules must apply (JMP/CALL)
685  status = IntStackAnalyzePointer(retAddress, &calledAddress);
686  if (!INT_SUCCESS(status))
687  {
688  goto _next_pointer;
689  }
690 
691  // If there is a call but on a different address, the get the next pointer.
692  // But if the call address is 0 (that means the call involved a register) then we have nothing else
693  // to do
694  if (calledAddress != 0 && currentModBase != 0 && beginRva != 0 &&
695  calledAddress != currentModBase + beginRva)
696  {
697  // See if the difference between current function and the called one is greater then maximum
698  // function length
699  if (calledAddress < currentModBase + beginRva &&
700  ((currentModBase + beginRva) - calledAddress > MAX_FUNC_LENGTH))
701  {
702  goto _analyze_jmp_after_call_case;
703  }
704  }
705  else if (calledAddress != 0 &&
706  (calledAddress < currentRip - MAX_FUNC_LENGTH || calledAddress > currentRip))
707  {
708  // if the call does not involve a register and is outside [rip - 0xaa0, rip],
709  // we can assume that this is another call to another function, not ours
710  goto _analyze_jmp_after_call_case;
711  }
712 
713  // We didn't end up in a case in which "we have a CALL [something]" where something is not near the
714  // current rip, so we can just consider this as a success.
715  goto _stack_trace_ok;
716 
717 _analyze_jmp_after_call_case:
718  pDriver = IntDriverFindByAddress(calledAddress);
719  if (NULL == pDriver)
720  {
721  goto _next_pointer;
722  }
723 
725 
726  crip = calledAddress;
727 
728  for (DWORD i = 0; i < 100; i++)
729  {
730  INSTRUX instrux;
731  QWORD calledFuncAddress = 0;
732  BOOLEAN bIsJump = FALSE;
733 
734  status = IntDecDecodeInstruction(IG_CS_TYPE_64B, crip, &instrux);
735  if (!INT_SUCCESS(status))
736  {
737  break;
738  }
739 
740  switch (instrux.Instruction)
741  {
742  case ND_INS_JMPNI: // JMP [...]
743  {
744  bIsJump = TRUE;
745 
746  if (ND_OP_MEM == instrux.Operands[0].Type)
747  {
748  QWORD fetchAddress;
749 
750  if (instrux.Operands[0].Info.Memory.IsRipRel)
751  {
752  // Disp is already sign extended
753  fetchAddress = crip + instrux.Length + instrux.Operands[0].Info.Memory.Disp;
754  }
755  else if (instrux.Operands[0].Info.Memory.HasDisp &&
756  !instrux.Operands[0].Info.Memory.HasBase &&
757  !instrux.Operands[0].Info.Memory.HasIndex)
758  {
759  fetchAddress = instrux.Operands[0].Info.Memory.Disp;
760  }
761  else
762  {
763  // So far so good, but it's using registers.
764  break;
765  }
766 
767  // Get the actual called address
768  status = IntKernVirtMemFetchQword(fetchAddress, &calledFuncAddress);
769  if (!INT_SUCCESS(status))
770  {
771  WARNING("[WARNING] Failed to get function address from 0x%016llx "
772  "(Call RIP 0x%016llx): 0x%08x\n", fetchAddress, crip, status);
773  calledFuncAddress = 0;
774  break;
775  }
776  }
777  else if (ND_OP_REG == instrux.Operands[0].Type)
778  {
779  calledFuncAddress = 0;
780  break;
781  }
782  else
783  {
784  // Don't take into account for now other things than JMP REG, JMP [MEM]
785  bIsJump = FALSE;
786  }
787 
788  break;
789  }
790  case ND_INS_JMPNR:
791  {
792  bIsJump = TRUE;
793  calledFuncAddress = crip + instrux.Length + instrux.Operands[0].Info.RelativeOffset.Rel;
794  break;
795  }
796  default:
797  {
798  break;
799  }
800  }
801 
802  if (bIsJump)
803  {
804  if (calledFuncAddress == 0 ||
805  (calledFuncAddress >= currentRip - MAX_FUNC_LENGTH && calledFuncAddress <= currentRip))
806  {
808  calledAddress = calledFuncAddress;
809  goto _stack_trace_ok;
810  }
811  break;
812  }
813 
814  crip += instrux.Length;
815  }
816 
818 
819  // If we ended here then we didn't find a CALL addr, and at addr a JMP REG, so we just go to next
820  // pointer
821  goto _next_pointer;
822 
823 _stack_trace_ok:
824  retAddrPtr = newRsp;
825  }
826 
827 _found_ret:
828  if (IS_KERNEL_POINTER_WIN(TRUE, retAddress))
829  {
830  BOOLEAN bIsCode = FALSE;
831 
832  pDriver = IntDriverFindByAddress(retAddress);
833 
834  if (pDriver)
835  {
836  DWORD rva = (DWORD)(retAddress - pDriver->BaseVa);
837  IMAGE_SECTION_HEADER sec = { 0 };
838 
839  status = IntPeGetSectionHeaderByRva(pDriver->BaseVa, pDriver->Win.MzPeHeaders, rva, &sec);
840  if (!INT_SUCCESS(status))
841  {
842  goto _finish_searching_data_sec;
843  }
844 
846  {
847  bIsCode = TRUE;
848  }
849  }
850 
851 _finish_searching_data_sec:
852  // Ignore non-code sections on stack trace when the call address can't be calculated.
853  // If the return address is a `call rax` we can't actually verify that is valid return, so if the RIP
854  // is not in a CODE/EXEC section, the return address call instruction must actually point where it
855  // should (0xe8 calls, memory calls, etc.)
856  if (!bIsCode && pDriver && calledAddress == 0)
857  {
858  goto _next_pointer;
859  }
860 
861  retModuleBase = pDriver ? pDriver->BaseVa : 0;
862  }
863  else
864  {
865  retModuleBase = 0;
866  pDriver = NULL;
867  }
868 
869  found = TRUE;
870  status = INT_STATUS_SUCCESS;
871  goto _next_stack_frame;
872 
873 _next_pointer:
874  newRsp += 8;
875  tries--;
876  status = INT_STATUS_SUCCESS;
877  }
878 
879 _next_stack_frame:
880  if (!found)
881  {
882  // Don't save and don't go further
883  if (INT_SUCCESS(status) && 0 == StackTrace->NumberOfTraces)
884  {
885  status = INT_STATUS_NOT_FOUND;
886  }
887 
888  goto _cleanup_and_leave;
889  }
890 
891  StackTrace->Traces[StackTrace->NumberOfTraces].CurrentRip = currentRip;
892  StackTrace->Traces[StackTrace->NumberOfTraces].RetAddrPointer = retAddrPtr;
893  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnAddress = retAddress;
894  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnModule = pDriver;
895 
896  // the new RIP is the address we return
897  currentRip = retAddress;
898 
899  // Mark the address imprecise if we have different values
900  if (calledAddress != currentModBase + beginRva)
901  {
902  StackTrace->Traces[StackTrace->NumberOfTraces].Flags |= STACK_CALL_ADDRESS_IMPRECISE;
903  }
904 
905  if (exception)
906  {
907  StackTrace->Traces[StackTrace->NumberOfTraces].Flags |= STACK_EXCEPTION_ROUTINE;
908  }
909  else if (interrupt)
910  {
911  StackTrace->Traces[StackTrace->NumberOfTraces].Flags |= STACK_INTERRUPT_ROUTINE;
912  }
913 
914  // If exists, save the address at the call instruction, not the current function
915  if (calledAddress != 0)
916  {
917  StackTrace->Traces[StackTrace->NumberOfTraces].CalledAddress = calledAddress;
918  }
919  else if (beginRva > 0)
920  {
921  StackTrace->Traces[StackTrace->NumberOfTraces].CalledAddress = currentModBase + beginRva;
922  }
923 
924  // Update the module base to the new driver we are in
925  if (retModuleBase == currentModBase)
926  {
927  ripInsideSameModule = TRUE;
928  }
929  else
930  {
931  ripInsideSameModule = FALSE;
932  currentModBase = retModuleBase;
933  }
934 
935  // finally increment the count
936  StackTrace->NumberOfTraces++;
937 
938  // the current RSP is where we left it (adding the trap frame if that's the case)
939  currentRsp = newRsp + (interrupt ? 5 * 8 : exception ? 4 * 8 : 0);
940 
941  if (retAddrPtr == currentRsp && 0 == retModuleBase && !interrupt && !exception)
942  {
943  currentRsp += 8;
944  }
945 
946  // Signal an error only if we found no traces (and no error occurred)
947  if (retModuleBase == 0 && (Flags & STACK_FLG_ONLY_DRIVER_ADDRS))
948  {
949  if (StackTrace->NumberOfTraces == 0 && status == INT_STATUS_SUCCESS)
950  {
951  ERROR("[ERROR] Didn't found a trace on the stack. RIP at 0x%016llx in module 0x%016llx\n",
952  currentRip, currentModBase);
953  status = INT_STATUS_NOT_FOUND;
954  }
955 
956  goto _cleanup_and_leave;
957  }
958 
959  if (!IS_KERNEL_POINTER_WIN(TRUE, retAddress))
960  {
961  // we return in user-mode, so we cannot go further
962  break;
963  }
964  } while (StackTrace->NumberOfTraces < MaxTraces);
965 
966 _cleanup_and_leave:
967  if (NULL != pStack)
968  {
969  pStack = pStackBase;
970  IntVirtMemUnmap(&pStack);
971  }
972 
973  if (NULL != pModBaseMap)
974  {
975  IntVirtMemUnmap(&pModBaseMap);
976  }
977 
979 
980  return status;
981 }
982 
983 
984 static INTSTATUS
986  _In_ DWORD Stack,
987  _In_ DWORD Eip,
988  _In_ DWORD MaxNumberOfTraces,
989  _In_ QWORD Flags,
990  _Inout_ STACK_TRACE *StackTrace
991  )
1010 {
1011  INTSTATUS status;
1012  QWORD ebp, cr3;
1013  BOOLEAN remap;
1014  PDWORD pStack, pOrigStack;
1015  DWORD currentRip = 0;
1016  QWORD calledAddr = 0;
1017 
1018  UNREFERENCED_PARAMETER(Flags);
1019 
1020  if (!IS_KERNEL_POINTER_WIN(FALSE, Stack) || Stack % 4 != 0)
1021  {
1023  }
1024 
1025  if (0 == MaxNumberOfTraces)
1026  {
1028  }
1029 
1030  if (NULL == StackTrace || NULL == StackTrace->Traces)
1031  {
1033  }
1034 
1036 
1037  StackTrace->StartRip = Eip;
1038  StackTrace->NumberOfTraces = 0;
1039  StackTrace->Bits64 = FALSE;
1040  ebp = Stack;
1041  remap = TRUE;
1042  pStack = pOrigStack = NULL;
1043  cr3 = gGuest.Mm.SystemCr3;
1044  status = INT_STATUS_SUCCESS;
1045  currentRip = Eip;
1046 
1047  for (DWORD frame = 0; frame < MaxNumberOfTraces; frame++)
1048  {
1049  DWORD retAddress = 0;
1050  DWORD nextFrame = 0;
1051  DWORD remaining;
1052  KERNEL_DRIVER *pMod = NULL;
1053 
1054  remaining = PAGE_REMAINING(ebp);
1055 
1056  if (remap)
1057  {
1058  if (NULL != pOrigStack)
1059  {
1060  IntVirtMemUnmap(&pOrigStack);
1061  }
1062 
1063  status = IntVirtMemMap(ebp, remaining, cr3, 0, &pOrigStack);
1064  if (!INT_SUCCESS(status))
1065  {
1066  LOG("[ERROR] Got to the end of the stack at 0x%016llx: 0x%08x\n", ebp, status);
1067  goto _check_and_leave;
1068  }
1069 
1070  pStack = pOrigStack;
1071  }
1072 
1073  // Always possible since the ebp is DWORD-aligned...
1074  nextFrame = pStack[0];
1075 
1076  if (nextFrame % 4 != 0)
1077  {
1078  WARNING("[WARNING] Unaligned stack: %08x\n", nextFrame);
1079  goto _check_and_leave;
1080  }
1081  else if (!IS_KERNEL_POINTER_WIN(FALSE, nextFrame))
1082  {
1083  TRACE("[INFO] User-Mode stack: %08x\n", nextFrame);
1084  goto _check_and_leave;
1085  }
1086 
1087  if (remaining >= 8)
1088  {
1089  retAddress = pStack[1];
1090  }
1091  else
1092  {
1093  status = IntKernVirtMemFetchDword(ebp + 4, &retAddress);
1094  if (!INT_SUCCESS(status))
1095  {
1096  LOG("[ERROR] Got to the end of the stack at 0x%016llx: 0x%08x\n", ebp + 4, status);
1097  goto _check_and_leave;
1098  }
1099  }
1100 
1101  if (IS_KERNEL_POINTER_WIN(FALSE, retAddress) && 0xffffffff != retAddress)
1102  {
1103  pMod = IntDriverFindByAddress(retAddress);
1104  }
1105 
1106  status = IntStackAnalyzePointer(retAddress, &calledAddr);
1107  if (!INT_SUCCESS(status))
1108  {
1109  calledAddr = 0;
1110  }
1111 
1112  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnAddress = retAddress;
1113  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnModule = pMod;
1114 
1115  StackTrace->Traces[StackTrace->NumberOfTraces].RetAddrPointer = ebp + 4;
1116  StackTrace->Traces[StackTrace->NumberOfTraces].CalledAddress = calledAddr;
1117  StackTrace->Traces[StackTrace->NumberOfTraces].CurrentRip = currentRip;
1118 
1119  currentRip = retAddress;
1120 
1121  ++StackTrace->NumberOfTraces;
1122 
1123  if ((ebp & PAGE_MASK) != (nextFrame & PAGE_MASK))
1124  {
1125  // We remap since we are in a different page... No point in recalculating the pStack pointer.
1126  remap = TRUE;
1127  }
1128  else
1129  {
1130  DWORD diff = (ebp > nextFrame) ? (DWORD)(ebp - nextFrame) : (DWORD)(nextFrame - ebp);
1131 
1132  remap = FALSE;
1133 
1134  // Just recalculate the stack pointer, no need to do a whole remap.
1135  pStack = (PDWORD)((PBYTE)pOrigStack + diff);
1136  }
1137 
1138  ebp = nextFrame;
1139  }
1140 
1141 _check_and_leave:
1142  if (NULL != pOrigStack)
1143  {
1144  IntVirtMemUnmap(&pOrigStack);
1145  }
1146 
1148 
1149  if (0 == StackTrace->NumberOfTraces)
1150  {
1151  if (!INT_SUCCESS(status))
1152  {
1153  return status;
1154  }
1155 
1156  return INT_STATUS_NOT_FOUND;
1157  }
1158 
1159  return INT_STATUS_SUCCESS;
1160 }
1161 
1162 
1163 INTSTATUS
1165  _In_ QWORD StackFrame,
1166  _In_ QWORD Rip,
1167  _In_ DWORD MaxNumberOfTraces,
1168  _In_ QWORD Flags,
1169  _Inout_ STACK_TRACE *StackTrace
1170  )
1182 {
1183  if (gGuest.Guest64)
1184  {
1185  return IntWinStackTraceGet64(StackFrame, Rip, MaxNumberOfTraces, Flags, StackTrace);
1186  }
1187  else
1188  {
1189  return IntWinStackTraceGet32((DWORD)StackFrame, (DWORD)Rip, MaxNumberOfTraces, Flags, StackTrace);
1190  }
1191 }
1192 
1193 
1194 static INTSTATUS
1196  _In_ PIG_ARCH_REGS Registers,
1197  _In_ PWIN_PROCESS_OBJECT Process,
1198  _In_ DWORD MaxNumberOfTraces,
1199  _Inout_ STACK_TRACE *StackTrace
1200  )
1211 {
1212  QWORD cr3, ebp;
1213  INTSTATUS status;
1214  PDWORD pStack, pOrigStack;
1215  BOOLEAN remap;
1216 
1217  remap = TRUE;
1218  cr3 = Registers->Cr3;
1219  ebp = Registers->Rbp;
1220  pStack = pOrigStack = NULL;
1221  status = INT_STATUS_UNSUCCESSFUL;
1222 
1223  for (DWORD frame = 0; frame < MaxNumberOfTraces; frame++)
1224  {
1225  DWORD retAddress = 0;
1226  DWORD nextFrame = 0;
1227  DWORD remaining;
1228  PWIN_PROCESS_MODULE pMod = NULL;
1229 
1230  remaining = PAGE_REMAINING(ebp);
1231 
1232  if (remap)
1233  {
1234  if (NULL != pOrigStack)
1235  {
1236  IntVirtMemUnmap(&pOrigStack);
1237  }
1238 
1239  status = IntVirtMemMap(ebp, remaining, cr3, 0, &pOrigStack);
1240  if (!INT_SUCCESS(status))
1241  {
1242  LOG("[ERROR] Got to the end of the stack at 0x%016llx: 0x%08x\n", ebp, status);
1243  goto _check_and_leave;
1244  }
1245 
1246  pStack = pOrigStack;
1247  }
1248 
1249  // Always possible since the ebp is DWORD-aligned...
1250  nextFrame = pStack[0];
1251 
1252  if (nextFrame % 4 != 0)
1253  {
1254  ERROR("[ERROR] Unaligned stack value %08x (current %08llx)\n", nextFrame, ebp);
1255  goto _check_and_leave;
1256  }
1257  else if (IS_KERNEL_POINTER_WIN(FALSE, nextFrame))
1258  {
1259  TRACE("[INFO] Kernel-Mode stack %08x (current %08llx)\n", nextFrame, ebp);
1260  goto _check_and_leave;
1261  }
1262  else if (0 == nextFrame)
1263  {
1264  // Nothing we can do about this
1265  goto _check_and_leave;
1266  }
1267 
1268  if (nextFrame < ebp)
1269  {
1270  ERROR("[ERROR] Return stack frame %x is smaller than current %llx\n", nextFrame, ebp);
1271  goto _check_and_leave;
1272  }
1273 
1274  if (remaining >= 8)
1275  {
1276  retAddress = pStack[1];
1277  }
1278  else
1279  {
1280  status = IntVirtMemRead(ebp + 4, 4, cr3, &retAddress, NULL);
1281  if (!INT_SUCCESS(status))
1282  {
1283  LOG("[ERROR] Got to the end of the stack at 0x%016llx: 0x%08x\n", ebp + 4, status);
1284  goto _check_and_leave;
1285  }
1286  }
1287 
1288  pMod = IntWinUmModFindByAddress(Process, retAddress);
1289  if (NULL == pMod)
1290  {
1291  WARNING("[WARNING] Failed getting the dll base for RIP %08x\n", retAddress);
1292  goto _check_and_leave;
1293  }
1294 
1295  if (pMod->Cache && pMod->Cache->Headers)
1296  {
1298 
1299  status = IntPeGetSectionHeaderByRva(pMod->VirtualBase,
1300  pMod->Cache->Headers,
1301  (DWORD)(retAddress - pMod->VirtualBase),
1302  &sec);
1303  if (!INT_SUCCESS(status) ||
1304  (!(sec.Characteristics & IMAGE_SCN_CNT_CODE) &&
1306  {
1307  goto _save_and_next;
1308  }
1309  }
1310 
1311 _save_and_next:
1312  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnAddress = retAddress;
1313  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnModule = pMod;
1314 
1315  StackTrace->Traces[StackTrace->NumberOfTraces].RetAddrPointer = ebp + 4;
1316  StackTrace->Traces[StackTrace->NumberOfTraces].CalledAddress = 0;
1317  StackTrace->Traces[StackTrace->NumberOfTraces].CurrentRip = 0;
1318 
1319  ++StackTrace->NumberOfTraces;
1320 
1321  if ((ebp & PAGE_MASK) != (nextFrame & PAGE_MASK))
1322  {
1323  // We remap since we are in a different page... No point in recalculating the pStack pointer.
1324  remap = TRUE;
1325  }
1326  else
1327  {
1328  // We know that nextFrame > ebp
1329  DWORD diff = (DWORD)(nextFrame - ebp);
1330 
1331  remap = FALSE;
1332 
1333  // Just recalculate the stack pointer, no need to do a whole remap.
1334  pStack = (PDWORD)((PBYTE)pStack + diff);
1335  }
1336 
1337  ebp = nextFrame;
1338  }
1339 
1340 _check_and_leave:
1341  if (NULL != pStack)
1342  {
1343  IntVirtMemUnmap(&pStack);
1344  }
1345 
1346  if (0 == StackTrace->NumberOfTraces)
1347  {
1348  if (!INT_SUCCESS(status))
1349  {
1350  return status;
1351  }
1352 
1353  return INT_STATUS_NOT_FOUND;
1354  }
1355 
1356  return INT_STATUS_SUCCESS;
1357 }
1358 
1359 
1360 static INTSTATUS
1362  _In_ PIG_ARCH_REGS Registers,
1363  _In_ PWIN_PROCESS_OBJECT Process,
1364  _In_ DWORD MaxNumberOfTraces,
1365  _In_ QWORD Remaining,
1366  _Inout_ STACK_TRACE *StackTrace
1367  )
1379 {
1380  QWORD stackFrame;
1381  INTSTATUS status;
1382  PBYTE pStack;
1383  DWORD pagesToParse = Remaining > PAGE_SIZE ? 2 : 1;
1384 
1385  stackFrame = Registers->Rsp;
1386 
1387  for (DWORD j = 0; j < pagesToParse; j++)
1388  {
1389  DWORD remaining = PAGE_REMAINING(stackFrame);
1390 
1391  status = IntVirtMemMap(stackFrame, remaining, Registers->Cr3, 0, &pStack);
1392  if (!INT_SUCCESS(status))
1393  {
1394  ERROR("[ERROR] Cannot get a stack at address %llx (start RSP %llx): 0x%08x\n",
1395  stackFrame, Registers->Rsp, status);
1396 
1397  return status;
1398  }
1399 
1400  for (DWORD i = 0; i < remaining; i += 8)
1401  {
1402  PWIN_PROCESS_MODULE pMod;
1403  QWORD ret = 0;
1405 
1406  ret = *(QWORD *)(pStack + i);
1407 
1408  pMod = IntWinUmModFindByAddress(Process, ret);
1409  if (NULL == pMod)
1410  {
1411  continue;
1412  }
1413 
1414  if (pMod->Cache && pMod->Cache->Headers)
1415  {
1416  status = IntPeGetSectionHeaderByRva(pMod->VirtualBase,
1417  pMod->Cache->Headers,
1418  (DWORD)(ret - pMod->VirtualBase),
1419  &sec);
1420  if (!INT_SUCCESS(status))
1421  {
1422  continue;
1423  }
1424 
1425  if (!(sec.Characteristics & IMAGE_SCN_CNT_CODE) &&
1427  {
1428  continue;
1429  }
1430  }
1431 
1432  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnAddress = ret;
1433  StackTrace->Traces[StackTrace->NumberOfTraces].ReturnModule = pMod;
1434  StackTrace->Traces[StackTrace->NumberOfTraces].RetAddrPointer = stackFrame + i;
1435 
1436  if (++StackTrace->NumberOfTraces >= MaxNumberOfTraces)
1437  {
1438  break;
1439  }
1440  }
1441 
1442  IntVirtMemUnmap(&pStack);
1443 
1444  if (StackTrace->NumberOfTraces >= MaxNumberOfTraces)
1445  {
1446  break;
1447  }
1448 
1449  stackFrame += remaining;
1450  }
1451 
1452  if (StackTrace->NumberOfTraces == 0)
1453  {
1454  return INT_STATUS_NOT_FOUND;
1455  }
1456 
1457  return INT_STATUS_SUCCESS;
1458 }
1459 
1460 
1461 static INTSTATUS
1463  _In_ WIN_PROCESS_OBJECT *Process,
1464  _Out_ QWORD *Remaining
1465  )
1493 {
1494  INTSTATUS status;
1495  QWORD tibBase, stackBase, stackLimit;
1496  QWORD vadRoot = 0;
1497  VAD stackVad = { 0 };
1498  BOOLEAN foundSwappedOut = FALSE;
1499 
1500  tibBase = stackBase = stackLimit = 0;
1501 
1503  {
1505  }
1506 
1507  // For shared memory we should analyze the possibility of stack swapped out only when we are in
1508  // the context of the process where the violation is triggered.
1509  if (gVcpu->Regs.Cr3 != Process->Cr3)
1510  {
1511  *Remaining = 0;
1513  }
1514 
1515  status = IntWinThrGetCurrentStackBaseAndLimit(&tibBase, &stackBase, &stackLimit);
1516  if ((INT_STATUS_PAGE_NOT_PRESENT == status) || (INT_STATUS_NO_MAPPING_STRUCTURES == status))
1517  {
1518  LOG("TIB is not present! Will inject #PF for %llx!\n", tibBase);
1519 
1520  // It is safe to inject page faults now, as we are processing a write over a user-mode module from another
1521  // user-mode module inside the same process at this time.
1522  // NOTE: We do not save a handle for this swap event (this is the only one) since this page may be executed
1523  // in the context of multiple threads, which would mean multiple swap-in attempts; therefore, we don't use
1524  // a context or a callback for this swap, which will be removed when terminating the process, if needed.
1526  NULL, 0, NULL, NULL, NULL);
1527  if (!INT_SUCCESS(status))
1528  {
1529  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
1530  return status;
1531  }
1532 
1533  // We'll return this status to signal that a #PF was injected and the action should be retried.
1535  }
1536  else if (!INT_SUCCESS(status))
1537  {
1538  ERROR("[ERROR] IntWinThrGetCurrentStackBaseAndLimit failed: 0x%08x\n", status);
1539  return status;
1540  }
1541 
1542  status = IntKernVirtMemFetchWordSize(Process->EprocessAddress + WIN_KM_FIELD(Process, VadRoot), &vadRoot);
1543  if (!INT_SUCCESS(status))
1544  {
1545  ERROR("[ERROR] IntKernVirtMemFetchWordSize failed: 0x%08x\n", status);
1546  return status;
1547  }
1548 
1549  status = IntWinVadFetchByRange(vadRoot, gVcpu->Regs.Rsp & PAGE_MASK, gVcpu->Regs.Rsp & PAGE_MASK, &stackVad);
1550  if (!INT_SUCCESS(status))
1551  {
1552  ERROR("[ERROR] IntWinVadFetchByRange failed: 0x%08x\n", status);
1553  return status;
1554  }
1555 
1556  *Remaining = stackVad.EndPage + PAGE_SIZE - gVcpu->Regs.Rsp;
1557 
1558  if ((gVcpu->Regs.Rsp < stackLimit) || (gVcpu->Regs.Rsp >= stackBase))
1559  {
1560  // If the stack is pivoted, don't inject anything, we can't assume that we'll succeed.
1561  return INT_STATUS_SUCCESS;
1562  }
1563 
1564  // Inject a page fault on every page starting from the current RSP, up until either the end of the VAD containing
1565  // the stack, or the next page, as it would be enough for our purposes of getting the stacktrace.
1566  // Note: if the first page is not swapped out, and the second is swapped out then this code will inject a #PF
1567  // only on the second page, as it will be swapped out when checking the translation in IntSwapMemReadData.
1568  for (QWORD page = gVcpu->Regs.Rsp & PAGE_MASK;
1569  page <= stackVad.EndPage && page <= (gVcpu->Regs.Rsp & PAGE_MASK) + PAGE_SIZE;
1570  page += PAGE_SIZE)
1571  {
1572  QWORD pa;
1573 
1574  status = IntTranslateVirtualAddress(page, gVcpu->Regs.Cr3, &pa);
1576  {
1577  status = IntSwapMemReadData(gVcpu->Regs.Cr3, page, gGuest.WordSize,
1578  SWAPMEM_OPT_UM_FAULT | SWAPMEM_OPT_NO_DUPS, NULL, 0, NULL, NULL, NULL);
1579  if (!INT_SUCCESS(status))
1580  {
1581  ERROR("[ERROR] IntSwapMemReadData failed: 0x%08x\n", status);
1582  return status;
1583  }
1584 
1585  foundSwappedOut = TRUE;
1586  }
1587  else if (!INT_SUCCESS(status))
1588  {
1589  ERROR("[ERROR] IntTranslateVirtualAddress failed: 0x%08x\n", status);
1590  return status;
1591  }
1592  }
1593 
1594  if (foundSwappedOut)
1595  {
1597  }
1598 
1599  return INT_STATUS_SUCCESS;
1600 }
1601 
1602 
1603 INTSTATUS
1605  _In_ PIG_ARCH_REGS Registers,
1606  _In_ PWIN_PROCESS_OBJECT Process,
1607  _In_ DWORD MaxNumberOfTraces,
1608  _Out_ STACK_TRACE *StackTrace
1609  )
1620 {
1621  INTSTATUS status;
1622  DWORD csType;
1623  QWORD remaining = 0;
1624 
1625  if (NULL == Registers)
1626  {
1628  }
1629 
1630  if (NULL == Process)
1631  {
1633  }
1634 
1635  if (0 == MaxNumberOfTraces)
1636  {
1638  }
1639 
1640  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, Registers->Rip))
1641  {
1642  return INT_STATUS_NOT_SUPPORTED;
1643  }
1644 
1645  memzero(StackTrace->Traces, MaxNumberOfTraces * sizeof(STACK_ELEMENT));
1646  StackTrace->StartRip = Registers->Rip;
1647  StackTrace->NumberOfTraces = 0;
1648 
1649  status = IntWinStackHandleUserStackPagedOut(Process, &remaining);
1650  if (!INT_SUCCESS(status))
1651  {
1652  TRACE("[INFO] IntWinStackHandleUserStackPagedOut failed: 0x%08x\n", status);
1653  return status;
1654  }
1655 
1656  status = IntGetCurrentMode(IG_CURRENT_VCPU, &csType);
1657  if (!INT_SUCCESS(status))
1658  {
1659  ERROR("[ERROR] IntGetCurrentMode failed: 0x%08x\n", status);
1660  return status;
1661  }
1662 
1663  if ((csType != IG_CS_TYPE_32B) && (csType != IG_CS_TYPE_64B))
1664  {
1665  ERROR("[ERROR] Unsupported CS type: %d\n", csType);
1666  return INT_STATUS_NOT_SUPPORTED;
1667  }
1668 
1669  if (csType == IG_CS_TYPE_32B)
1670  {
1671  QWORD stackFrame = Registers->Rbp;
1672 
1673  StackTrace->Bits64 = FALSE;
1674 
1675  if ((stackFrame > Registers->Rsp &&
1676  stackFrame - Registers->Rsp > 3 * PAGE_SIZE) ||
1677  (stackFrame < Registers->Rsp &&
1678  Registers->Rsp - stackFrame > 3 * PAGE_SIZE))
1679  {
1680  // We probably are not in a stackframe, so get the first address on the stack and consider it to be the
1681  // return address
1682  PWIN_PROCESS_MODULE pMod = NULL;
1683  DWORD retAddress = 0;
1684  BOOLEAN bFound = FALSE;
1685 
1686  stackFrame = Registers->Rsp;
1687 
1688  // Parse the first 8 DWORDs on the stack, because the function might have done some pushes
1689  // (e.g. memset does not do a stack frame, but performs a push edi, so we shouldn't take the edi value as
1690  // the return address)
1691  for (size_t stackIndex = 0; stackIndex < MIN(8u, remaining / 4); stackIndex++)
1692  {
1693  status = IntVirtMemRead(stackFrame + stackIndex * 4, 4, Registers->Cr3, &retAddress, NULL);
1694  if (!INT_SUCCESS(status))
1695  {
1696  ERROR("[ERROR] RSP 0x%016llx is not present: %08x\n", stackFrame + stackIndex * 4, status);
1697  return status;
1698  }
1699 
1700  pMod = IntWinUmModFindByAddress(Process, retAddress);
1701  if (NULL == pMod)
1702  {
1703  continue;
1704  }
1705 
1706  bFound = TRUE;
1707  break;
1708  }
1709 
1710  if (!bFound)
1711  {
1712  ERROR("[ERROR] DLL base was not found on stack 0x%016llx\n", stackFrame);
1713  return INT_STATUS_NOT_FOUND;
1714  }
1715 
1716  StackTrace->NumberOfTraces = 1;
1717 
1718  StackTrace->Traces[0].ReturnAddress = retAddress;
1719  StackTrace->Traces[0].ReturnModule = pMod;
1720  StackTrace->Traces[0].RetAddrPointer = stackFrame;
1721 
1722  return INT_STATUS_SUCCESS;
1723  }
1724 
1725  status = IntWinStackTraceGetUser32(Registers, Process, MaxNumberOfTraces, StackTrace);
1726  if (INT_SUCCESS(status))
1727  {
1728  return INT_STATUS_SUCCESS;
1729  }
1730 
1731  return INT_STATUS_NOT_FOUND;
1732  }
1733 
1734  StackTrace->Bits64 = TRUE;
1735 
1736  return IntWinStackTraceGetUser64(Registers, Process, MaxNumberOfTraces, remaining, StackTrace);
1737 }
1738 
1739 
1740 INTSTATUS
1742  _In_ QWORD UserRsp,
1743  _In_ DWORD SegCs,
1744  _In_ BOOLEAN IsWow64Stack,
1745  _Inout_ DPI_EXTRA_INFO *DpiExtraInfo,
1746  _Out_ BOOLEAN *IsPivoted
1747  )
1762 {
1763  QWORD tibBase;
1764  QWORD stackBase;
1765  QWORD stackLimit;
1766  DWORD csType;
1767  DWORD alignSize;
1768  INTSTATUS status;
1769 
1770  tibBase = 0;
1771  stackBase = 0;
1772  stackLimit = 0;
1773  csType = 0;
1774  status = INT_STATUS_SUCCESS;
1775 
1776  if (NULL == IsPivoted)
1777  {
1779  }
1780 
1781  *IsPivoted = FALSE;
1782  alignSize = gGuest.WordSize;
1783 
1784  if (gGuest.Guest64)
1785  {
1786  switch (SegCs)
1787  {
1789  csType = IG_CS_TYPE_32B;
1790  alignSize = sizeof(DWORD);
1791  break;
1792 
1794  csType = IG_CS_TYPE_64B;
1795  break;
1796 
1797  default:
1798  ERROR("[ERROR] Unrecognized CS value: 0x%08x\n", SegCs);
1800  }
1801  }
1802  else
1803  {
1804  csType = IG_CS_TYPE_32B;
1805  }
1806 
1807  if (0 == UserRsp ||
1808  IS_KERNEL_POINTER_WIN(gGuest.Guest64, UserRsp) ||
1809  UserRsp % alignSize != 0)
1810  {
1812  }
1813 
1814 
1815  status = IntWinThrGetCurrentTib(IG_CS_RING_0, csType, &tibBase);
1816  if (!INT_SUCCESS(status))
1817  {
1818  ERROR("[ERROR] IntWinThrGetCurrentTib failed: 0x%08x\n", status);
1819  return status;
1820  }
1821 
1822  // Teb field will be NULL for WSL threads
1823  if (0 == tibBase)
1824  {
1826  }
1827 
1828  status = IntWinThrGetUmStackBaseAndLimitFromTib(tibBase, csType, gVcpu->Regs.Cr3, &stackBase, &stackLimit);
1829  if (!INT_SUCCESS(status))
1830  {
1831  if ((INT_STATUS_PAGE_NOT_PRESENT == status) ||
1833  {
1834  WARNING("[WARNING] IntWinThrGetUmStackBaseAndLimitFromTib failed: 0x%08x\n", status);
1836  }
1837 
1838  ERROR("[ERROR] IntWinThrGetUmStackBaseAndLimitFromTib failed: 0x%08x\n", status);
1839  return status;
1840  }
1841 
1842  if (IsWow64Stack)
1843  {
1844  DpiExtraInfo->DpiPivotedStackExtraInfo.Wow64StackBase = stackBase;
1845  DpiExtraInfo->DpiPivotedStackExtraInfo.Wow64StackLimit = stackLimit;
1846  }
1847  else
1848  {
1849  DpiExtraInfo->DpiPivotedStackExtraInfo.StackBase = stackBase;
1850  DpiExtraInfo->DpiPivotedStackExtraInfo.StackLimit = stackLimit;
1851  }
1852 
1853  if (UserRsp < stackLimit || UserRsp > stackBase)
1854  {
1855  WARNING("[WARNING] UM stack (0x%016llx) outside of the limit and base interval "
1856  "from the current TIB [0x%016llx, 0x%016llx].\n", UserRsp, stackLimit, stackBase);
1857  *IsPivoted = TRUE;
1858  }
1859 
1860  return status;
1861 }
1862 
1863 
1864 static INTSTATUS
1866  _In_ QWORD KernelStack,
1867  _Inout_ DPI_EXTRA_INFO *DpiExtraInfo,
1868  _Out_ KTRAP_FRAME64 *TrapFrame
1869  )
1885 {
1886  INTSTATUS status;
1887  BYTE *stackPointer;
1888  QWORD stackPointerGVA;
1889  BOOLEAN bFound;
1890 
1891  stackPointer = NULL;
1892  stackPointerGVA = 0;
1893  bFound = FALSE;
1894 
1895  if (!IS_KERNEL_POINTER_WIN(TRUE, KernelStack) || KernelStack % 8 != 0)
1896  {
1898  }
1899 
1900  if (NULL == TrapFrame)
1901  {
1903  }
1904 
1905  stackPointerGVA = ALIGN_DOWN(KernelStack, PAGE_SIZE) - PAGE_SIZE;
1906 
1907  // Map the first page of the stack, the TrapFrame should be there
1908  status = IntVirtMemMap(stackPointerGVA, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &stackPointer);
1909  if (!INT_SUCCESS(status))
1910  {
1911  ERROR("[ERROR] IntVirtMemMap failed from GVA 0x%016llx: 0x%08x\n", stackPointerGVA, status);
1912  return status;
1913  }
1914 
1915  for (QWORD i = 0; i < PAGE_SIZE - sizeof(KTRAP_FRAME64); i += sizeof(QWORD))
1916  {
1917  bFound = IntWinIsUmTrapFrame(stackPointer + i);
1918 
1919  if (bFound)
1920  {
1921  *TrapFrame = *(PKTRAP_FRAME64)(stackPointer + i);
1922 
1923  DpiExtraInfo->DpiPivotedStackExtraInfo.TrapFrameAddress = stackPointerGVA + i;
1924 
1925  TRACE("[INFO] TrapFrame found: 0x%016llx\n", stackPointerGVA + i);
1926  break;
1927  }
1928  }
1929 
1930  IntVirtMemUnmap(&stackPointer);
1931 
1932  if (!bFound)
1933  {
1934  status = INT_STATUS_NOT_FOUND;
1935  }
1936 
1937  return status;
1938 }
1939 
1940 
1941 static INTSTATUS
1943  _In_ DWORD KernelStack,
1944  _Inout_ DPI_EXTRA_INFO *DpiExtraInfo,
1945  _Out_ KTRAP_FRAME32 *TrapFrame
1946  )
1962 {
1963  INTSTATUS status;
1964  BYTE *stackPointer;
1965  QWORD stackPointerGVA;
1966  BOOLEAN bFound;
1967 
1968  status = INT_STATUS_SUCCESS;
1969  stackPointer = NULL;
1970  stackPointerGVA = 0;
1971  bFound = FALSE;
1972 
1973  if (!IS_KERNEL_POINTER_WIN(FALSE, KernelStack) || KernelStack % 4 != 0)
1974  {
1976  }
1977 
1978  if (NULL == TrapFrame)
1979  {
1981  }
1982 
1983  stackPointerGVA = ALIGN_DOWN(KernelStack, PAGE_SIZE) - PAGE_SIZE;
1984 
1985  // Map the first page of the stack, the TrapFrame should be there
1986  status = IntVirtMemMap(stackPointerGVA, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &stackPointer);
1987  if (!INT_SUCCESS(status))
1988  {
1989  ERROR("[ERROR] IntVirtMemMap failed from GVA 0x%016llx: 0x%08x\n", stackPointerGVA, status);
1990  return status;
1991  }
1992 
1993  for (DWORD i = 0; i < PAGE_SIZE - sizeof(KTRAP_FRAME32); i += sizeof(DWORD))
1994  {
1995  bFound = IntWinIsUmTrapFrame(stackPointer + i);
1996 
1997  if (bFound)
1998  {
1999  *TrapFrame = *(PKTRAP_FRAME32)(stackPointer + i);
2000 
2001  DpiExtraInfo->DpiPivotedStackExtraInfo.TrapFrameAddress = stackPointerGVA + i;
2002 
2003  TRACE("[INFO] TrapFrame found: 0x%016llx\n", stackPointerGVA + i);
2004  break;
2005  }
2006  }
2007 
2008  IntVirtMemUnmap(&stackPointer);
2009 
2010  if (!bFound)
2011  {
2012  status = INT_STATUS_NOT_FOUND;
2013  }
2014 
2015  return status;
2016 }
2017 
2018 
2019 INTSTATUS
2021  _Out_ QWORD *UserRsp,
2022  _Out_ DWORD *SegCs,
2023  _In_ BOOLEAN Fallback,
2024  _Inout_ DPI_EXTRA_INFO *DpiExtraInfo
2025  )
2040 {
2041  INTSTATUS status = INT_STATUS_SUCCESS;
2042  QWORD currentThread = 0;
2043 
2044  if (NULL == UserRsp)
2045  {
2047  }
2048 
2049  if (NULL == SegCs)
2050  {
2052  }
2053 
2054  *UserRsp = 0;
2055  *SegCs = 0;
2056 
2057  status = IntWinThrGetCurrentThread(IG_CURRENT_VCPU, &currentThread);
2058  if (!INT_SUCCESS(status))
2059  {
2060  ERROR("[ERROR] IntWinThrGetCurrentThread failed: 0x%08x\n", status);
2061  return status;
2062  }
2063 
2064  if (gGuest.Guest64)
2065  {
2066  QWORD trapFrameAddress = 0;
2067  QWORD stackBase = 0;
2068  KTRAP_FRAME64 trapFrame = { 0 };
2069 
2070  status = IntKernVirtMemFetchQword(currentThread + WIN_KM_FIELD(Thread, TrapFrame), &trapFrameAddress);
2071  if (!INT_SUCCESS(status))
2072  {
2073  ERROR("[ERROR] Failed getting the TrapFrame from Ethread 0x%016llx: 0x%08x\n", currentThread, status);
2074  return status;
2075  }
2076 
2077  // TrapFrame field for WSL thread will be NULL, we can find the TrapFrame on stack
2078  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, trapFrameAddress))
2079  {
2080  QWORD count = 0;
2081 
2082  status = IntKernVirtMemRead(trapFrameAddress, sizeof(KTRAP_FRAME64), &trapFrame, NULL);
2083  if (!INT_SUCCESS(status))
2084  {
2085  ERROR("[ERROR] Failed getting the trap frame from CurrentThread 0x%016llx: 0x%08x\n",
2086  currentThread, status);
2087  return status;
2088  }
2089 
2090  DpiExtraInfo->DpiPivotedStackExtraInfo.TrapFrameAddress = trapFrameAddress;
2091 
2092  while ((IS_KERNEL_POINTER_WIN(gGuest.Guest64, trapFrame.TrapFrame)) &&
2093  (count++ < TRAPFRAME_MAX_ITERATIONS))
2094  {
2095  trapFrameAddress = trapFrame.TrapFrame;
2096 
2097  status = IntKernVirtMemRead(trapFrameAddress, sizeof(KTRAP_FRAME64), &trapFrame, NULL);
2098  if (!INT_SUCCESS(status))
2099  {
2100  ERROR("[ERROR] Failed getting the TrapFrame from CurrentThread 0x%016llx: 0x%08x\n",
2101  currentThread, status);
2102  return status;
2103  }
2104 
2105  DpiExtraInfo->DpiPivotedStackExtraInfo.TrapFrameAddress = trapFrameAddress;
2106  }
2107 
2108  if (count == TRAPFRAME_MAX_ITERATIONS)
2109  {
2110  WARNING("[WARNING] Specially crafted TrapFrame somehow: 0x%016llx\n", trapFrameAddress);
2111  return INT_STATUS_NOT_SUPPORTED;
2112  }
2113  }
2114 
2115  // Check if the found TrapFrame is a valid user-mode one, fall back to stack searching if not
2116  if (!IntWinIsUmTrapFrame(&trapFrame) && Fallback)
2117  {
2118  status = IntKernVirtMemFetchQword(currentThread + WIN_KM_FIELD(Thread, StackBase), &stackBase);
2119  if (!INT_SUCCESS(status))
2120  {
2121  ERROR("[ERROR] Failed getting the StackBase from CurrentThread 0x%016llx: 0x%08x\n",
2122  currentThread, status);
2123  return status;
2124  }
2125 
2126  status = IntWinStackUserTrapFrameGet64(stackBase, DpiExtraInfo, &trapFrame);
2127  if (!INT_SUCCESS(status))
2128  {
2129  ERROR("[ERROR] Failed getting a TrapFrame: 0x%08x.\n", status);
2130  return status;
2131  }
2132  }
2133 
2134  DpiExtraInfo->DpiPivotedStackExtraInfo.CurrentStack = trapFrame.Rsp;
2135 
2136  *UserRsp = trapFrame.Rsp;
2137  *SegCs = trapFrame.SegCs;
2138  }
2139  else
2140  {
2141  KTRAP_FRAME32 trapFrame = { 0 };
2142  DWORD trapFrameAddress = 0;
2143  DWORD stackBase = 0;
2144 
2145  status = IntKernVirtMemFetchDword(currentThread + WIN_KM_FIELD(Thread, TrapFrame), &trapFrameAddress);
2146  if (!INT_SUCCESS(status))
2147  {
2148  ERROR("[ERROR] Failed getting the TrapFrame from Ethread 0x%016llx: 0x%08x\n", currentThread, status);
2149  return status;
2150  }
2151 
2152  // TrapFrame field for WSL thread will be NULL, we can find the TrapFrame on stack
2153  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, trapFrameAddress))
2154  {
2155  status = IntKernVirtMemRead(trapFrameAddress, sizeof(KTRAP_FRAME32), &trapFrame, NULL);
2156  if (!INT_SUCCESS(status))
2157  {
2158  ERROR("[ERROR] Failed reading the TrapFrame from CurrentThread 0x%016llx: 0x%08x\n",
2159  currentThread, status);
2160  return status;
2161  }
2162 
2163  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, trapFrame.Eip))
2164  {
2165  WARNING("[WARNING] Current TrapFrame 0x%08x has a kernel mode Eip: 0x%08x \n",
2166  trapFrameAddress, trapFrame.Eip);
2167 
2169  }
2170 
2171  DpiExtraInfo->DpiPivotedStackExtraInfo.TrapFrameAddress = trapFrameAddress;
2172  }
2173 
2174  // Check if the found TrapFrame is a valid user-mode one, fall back to stack searching if not
2175  if (!IntWinIsUmTrapFrame(&trapFrame) && Fallback)
2176  {
2177  status = IntKernVirtMemFetchDword(currentThread + WIN_KM_FIELD(Thread, StackBase), &stackBase);
2178  if (!INT_SUCCESS(status))
2179  {
2180  ERROR("[ERROR] Failed getting the StackBase from CurrentThread 0x%016llx: 0x%08x\n",
2181  currentThread, status);
2182  return status;
2183  }
2184 
2185  status = IntWinStackUserTrapFrameGet32(stackBase, DpiExtraInfo, &trapFrame);
2186  if (!INT_SUCCESS(status))
2187  {
2188  ERROR("[ERROR] Failed getting a TrapFrame: 0x%08x.\n", status);
2189  return status;
2190  }
2191  }
2192 
2193  DpiExtraInfo->DpiPivotedStackExtraInfo.CurrentStack = trapFrame.HardwareEsp;
2194 
2195  *UserRsp = trapFrame.HardwareEsp;
2196  }
2197 
2198  return INT_STATUS_SUCCESS;
2199 }
2200 
2201 
2202 INTSTATUS
2204  _In_ WIN_PROCESS_OBJECT *Process,
2205  _In_ WIN_PROCESS_OBJECT *RealParent,
2206  _Inout_ DPI_EXTRA_INFO *DpiExtraInfo
2207  )
2216 // It's DpiPivotedStackExtraInfo.CurrentWow64Stack will be set to the rsp upon success.
2220 {
2221  INTSTATUS status = INT_STATUS_SUCCESS;
2222  QWORD tib = 0;
2223  QWORD wow64SaveArea = 0;
2224  QWORD userWow64Rsp = 0;
2225 
2226  // 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.
2228  if (!INT_SUCCESS(status))
2229  {
2230  ERROR("[ERROR] IntWinThrGetCurrentTib failed: 0x%08x\n", status);
2231  return status;
2232  }
2233 
2234  // These all might be swapped as they reside in UM. We have to check it and bail out if it happens.
2235  status = IntVirtMemRead(tib + WIN_UM_FIELD(Teb, Wow64SaveArea),
2236  gGuest.WordSize, RealParent->Cr3, &wow64SaveArea, NULL);
2238  {
2239  INFO("[INFO] IntVirtMemRead failed: 0x%08x, the page 0x%016llx seems to be swapped out...\n", status,
2240  tib + WIN_UM_FIELD(Teb, Wow64SaveArea));
2241  return INT_STATUS_SUCCESS;
2242  }
2243  else if (!INT_SUCCESS(status))
2244  {
2245  ERROR("[ERROR] IntVirtMemRead failed: 0x%08x\n", status);
2246  return status;
2247  }
2248 
2249  status = IntVirtMemRead(wow64SaveArea + WIN_UM_FIELD(Teb, Wow64StackInSaveArea), sizeof(DWORD), RealParent->Cr3,
2250  &userWow64Rsp, NULL);
2252  {
2253  INFO("[INFO] IntVirtMemRead failed: 0x%08x, the page 0x%016llx seems to be swapped out...\n", status,
2254  wow64SaveArea + WIN_UM_FIELD(Teb, Wow64StackInSaveArea));
2255  return INT_STATUS_SUCCESS;
2256  }
2257  else if (!INT_SUCCESS(status))
2258  {
2259  ERROR("[ERROR] IntVirtMemRead failed: 0x%08x\n", status);
2260  return status;
2261  }
2262 
2263  DpiExtraInfo->DpiPivotedStackExtraInfo.CurrentWow64Stack = userWow64Rsp;
2264 
2265  // 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
2266  status = IntWinStackUserCheckIsPivoted(userWow64Rsp, CODE_SEG_UM_32_GUEST_64, TRUE, DpiExtraInfo,
2267  &Process->CreationInfo.ParentHasPivotedStack);
2268  if (!INT_SUCCESS(status))
2269  {
2270  ERROR("[ERROR] IntWinStackUserCheckIsPivoted failed: 0x%08x.\n", status);
2271  return status;
2272  }
2273  else if (Process->CreationInfo.ParentHasPivotedStack)
2274  {
2275  WARNING("[WARNING] Process 0x%016llx created with WoW64 pivoted stack.\n", Process->EprocessAddress);
2276  }
2277 
2278  return status;
2279 }
2280 
2281 
2282 BOOLEAN
2284  _In_ void *TrapFrame
2285  )
2293 {
2294  if (gGuest.Guest64)
2295  {
2296  KTRAP_FRAME64 *trapFrame = (KTRAP_FRAME64 *)TrapFrame;
2297 
2298  // EFLAGS check reserved bits
2299  if ((trapFrame->EFlags >= (1 << 22)) ||
2300  (trapFrame->EFlags & (1 << 1)) == 0 ||
2301  (trapFrame->EFlags & (1 << 3)) != 0 ||
2302  (trapFrame->EFlags & (1 << 5)) != 0 ||
2303  (trapFrame->EFlags & (1 << 15)) != 0)
2304  {
2305  return FALSE;
2306  }
2307 
2308  // Guest 64 valid SegCs
2309  if (trapFrame->SegCs != CODE_SEG_UM_64_GUEST_64)
2310  {
2311  return FALSE;
2312  }
2313 
2314  // UM, aligned Rsp
2315  if (trapFrame->Rsp % 8 != 0 ||
2316  IS_KERNEL_POINTER_WIN(TRUE, trapFrame->Rsp) ||
2317  trapFrame->Rsp == 0 ||
2318  trapFrame->Rsp == QWORD_MAX)
2319  {
2320  return FALSE;
2321  }
2322 
2323  // UM Rip
2324  if (IS_KERNEL_POINTER_WIN(TRUE, trapFrame->Rip) ||
2325  trapFrame->Rip == 0 ||
2326  trapFrame->Rip == QWORD_MAX)
2327  {
2328  return FALSE;
2329  }
2330  }
2331  else
2332  {
2333  KTRAP_FRAME32 *trapFrame = (KTRAP_FRAME32 *)TrapFrame;
2334 
2335  // EFLAGS check reserved bits
2336  if ((trapFrame->EFlags >= (1 << 22)) ||
2337  (trapFrame->EFlags & (1 << 1)) == 0 ||
2338  (trapFrame->EFlags & (1 << 3)) != 0 ||
2339  (trapFrame->EFlags & (1 << 5)) != 0 ||
2340  (trapFrame->EFlags & (1 << 15)) != 0)
2341  {
2342  return FALSE;
2343  }
2344 
2345  // Guest 32 valid SegCs
2346  if (trapFrame->SegCs != CODE_SEG_UM_32_GUEST_32)
2347  {
2348  return FALSE;
2349  }
2350 
2351  // Valid Esp
2352  if (IS_KERNEL_POINTER_WIN(FALSE, trapFrame->HardwareEsp) ||
2353  trapFrame->HardwareEsp % 4 != 0 ||
2354  trapFrame->HardwareEsp == 0 ||
2355  trapFrame->HardwareEsp == DWORD_MAX)
2356  {
2357  return FALSE;
2358  }
2359 
2360  // Valid Eip
2361  if (IS_KERNEL_POINTER_WIN(FALSE, trapFrame->Eip) ||
2362  trapFrame->Eip == 0 ||
2363  trapFrame->Eip == DWORD_MAX)
2364  {
2365  return FALSE;
2366  }
2367  }
2368 
2369  return TRUE;
2370 }
#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:2726
#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:707
#define _Out_
Definition: intro_sal.h:22
_Bool BOOLEAN
Definition: intro_types.h:58
DWORD EFlags
Definition: wddefs.h:979
DWORD SegCs
Definition: wddefs.h:978
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:2267
INTSTATUS IntWinThrGetCurrentStackBaseAndLimit(QWORD *TibBase, QWORD *StackBase, QWORD *StackLimit)
Obtains the stack base, stack limit and TIB address of the current thread.
Definition: winthread.c:321
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:1195
struct _KTRAP_FRAME32 * PKTRAP_FRAME32
WINDOWS_GUEST * gWinGuest
Global variable holding the state of a Windows guest.
Definition: winguest.c:37
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:985
#define _In_
Definition: intro_sal.h:21
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:211
#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:1741
#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:980
#define STATS_EXIT(id)
Definition: stats.h:160
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
INTSTATUS IntSwapMemReadData(QWORD Cr3, QWORD VirtualAddress, DWORD Length, DWORD Options, void *Context, DWORD ContextTag, PFUNC_PagesReadCallback Callback, PFUNC_PreInjectCallback PreInject, void **SwapHandle)
Reads a region of guest virtual memory, and calls the indicated callback when all the data is availab...
Definition: swapmem.c:417
#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:778
BOOLEAN IntWinIsUmTrapFrame(void *TrapFrame)
Checks whether a TrapFrame is valid or not.
Definition: winstack.c:2283
#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
#define SWAPMEM_OPT_NO_DUPS
If set, will make sure that a single PF is scheduled for this page.
Definition: swapmem.h:31
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
INTSTATUS IntKernVirtMemFetchWordSize(QWORD GuestVirtualAddress, void *Data)
Reads a guest pointer from the guest kernel memory.
Definition: introcore.c:847
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:1164
DWORD Eip
Definition: wddefs.h:977
QWORD VirtualBase
Guest virtual address of the loaded module.
Definition: winummodule.h:34
#define MIN(a, b)
Definition: introdefs.h:146
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:2395
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:2203
#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:2304
#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
static INTSTATUS IntWinStackTraceGetUser64(PIG_ARCH_REGS Registers, PWIN_PROCESS_OBJECT Process, DWORD MaxNumberOfTraces, QWORD Remaining, STACK_TRACE *StackTrace)
Get the user stack trace of a 64 bit windows process.
Definition: winstack.c:1361
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:153
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:290
#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
INTSTATUS IntTranslateVirtualAddress(QWORD Gva, QWORD Cr3, QWORD *PhysicalAddress)
Translates a guest virtual address to a guest physical address.
Definition: introcore.c:1999
static INTSTATUS IntWinStackHandleUserStackPagedOut(WIN_PROCESS_OBJECT *Process, QWORD *Remaining)
Handles the case when the stack is needed but is swapped out.
Definition: winstack.c:1462
#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:1942
#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:1123
#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:2020
QWORD KernelVa
The guest virtual address at which the kernel image.
Definition: guests.h:283
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:367
#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:70
#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:16
#define WIN_KM_FIELD(Structure, Field)
Macro used to access kernel mode fields inside the WIN_OPAQUE_FIELDS structure.
Definition: winguest.h:740
uint32_t DWORD
Definition: intro_types.h:49
INTSTATUS IntWinVadFetchByRange(QWORD VadRoot, QWORD StartPage, QWORD EndPage, VAD *Vad)
Fetches and returns a VAD object containing the range represented by [StartPage, EndPage].
Definition: winvad.c:3877
DWORD KernelBufferSize
The size of the KernelBuffer.
Definition: winguest.h:852
#define INT_STATUS_INVALID_DATA_VALUE
Definition: introstatus.h:136
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:374
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
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
#define INT_STATUS_STACK_SWAPPED_OUT
Indicates that the stack was needed in order to match the exceptions, but it is swapped out...
Definition: introstatus.h:465
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:1161
BYTE * KernelBuffer
A buffer containing the entire kernel image.
Definition: winguest.h:851
#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:59
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:1865
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:208
UINT16 SegCs
Definition: wddefs.h:1157
Holds register state.
Definition: glueiface.h:30
Structure that describes a stack trace.
Definition: guest_stack.h:42
QWORD EndPage
Definition: winvad.h:106
#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:1604
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:20
BYTE * Headers
A buffer containing the MZ/PE headers of this module.
Definition: winumcache.h:97
A representation of a Windows VAD structure.
Definition: winvad.h:80
#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:2062
#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 SWAPMEM_OPT_UM_FAULT
If set, the PF must be injected only while in user-mode. Use it when reading user-mode memory...
Definition: swapmem.h:21
#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:83
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68