Bitdefender Hypervisor Memory Introspection
decoder.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "decoder.h"
6 #include "gpacache.h"
7 #include "guests.h"
8 #include "icache.h"
9 #include "introcpu.h"
10 #include "kernvm.h"
11 #include "lixprocess.h"
12 #include "winprocesshp.h"
13 
22 #define REG_GPRV(ctx, reg) (*((&(ctx)->Rax) + (reg)))
23 #define REG_GPRP(ctx, reg) ((&(ctx)->Rax) + (reg))
32 
41 #define IS_ACCESS_IN_KERNEL_WIN(is64, gla, size) IS_KERNEL_POINTER_WIN((is64), (gla)) && \
42  IS_KERNEL_POINTER_WIN((is64), (gla) + (size)-1)
43 #define IS_ACCESS_IN_KERNEL_LIX(gla, size) IS_KERNEL_POINTER_LIX((gla)) && \
51  IS_KERNEL_POINTER_LIX((gla) + (size)-1)
52 
54 enum
55 {
60 
67 #define GET_SIGN(sz, x) ((sz) == 1 ? ((x)&0x80) >> 7 : \
68  (sz) == 2 ? ((x)&0x8000) >> 15 : \
69  (sz) == 4 ? ((x)&0x80000000) >> 31 : (x) >> 63)
70 
71 
72 static void
74  _In_ QWORD Dst,
75  _In_ QWORD Src1,
76  _In_ QWORD Src2,
77  _In_ DWORD Size,
78  _In_ PIG_ARCH_REGS Regs,
79  _In_ DWORD FlagsMode
80  )
91 {
92  BYTE pfArr[16] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
93 
94  // Mask the operands with their respective size.
95  Dst = ND_TRIM(Size, Dst);
96  Src1 = ND_TRIM(Size, Src1);
97  Src2 = ND_TRIM(Size, Src2);
98 
99  // PF set if the first bytes has an even number of 1 bits.
100  if ((pfArr[Dst & 0xF] + pfArr[(Dst >> 4) & 0xF]) % 2 == 0)
101  {
102  Regs->Flags |= CPU_EFLAGS_PF;
103  }
104  else
105  {
106  Regs->Flags &= ~CPU_EFLAGS_PF;
107  }
108 
109  // ZF set if the result is zero.
110  if (Dst == 0)
111  {
112  Regs->Flags |= CPU_EFLAGS_ZF;
113  }
114  else
115  {
116  Regs->Flags &= ~CPU_EFLAGS_ZF;
117  }
118 
119  // SF is set if the sign flag is set.
120  if (GET_SIGN(Size, Dst) != 0)
121  {
122  Regs->Flags |= CPU_EFLAGS_SF;
123  }
124  else
125  {
126  Regs->Flags &= ~CPU_EFLAGS_SF;
127  }
128 
129  // OF and CF are handled differently for some instructions.
130  if (FM_LOGIC == FlagsMode)
131  {
132  // OF and CF are cleared on logic instructions.
133  Regs->Flags &= ~(CPU_EFLAGS_OF | CPU_EFLAGS_CF);
134  }
135  else
136  {
137  // Set CF.
138  if ((FM_SUB == FlagsMode) && (Src1 < Src2))
139  {
140  Regs->Flags |= CPU_EFLAGS_CF;
141  }
142  else if ((FM_ADD == FlagsMode) && (Dst < Src1))
143  {
144  Regs->Flags |= CPU_EFLAGS_CF;
145  }
146  else
147  {
148  Regs->Flags &= ~CPU_EFLAGS_CF;
149  }
150 
151  // Set OF.
152  if (FM_SUB == FlagsMode)
153  {
154  if ((GET_SIGN(Size, Src1) && !GET_SIGN(Size, Src2) && !GET_SIGN(Size, Dst)) ||
155  (!GET_SIGN(Size, Src1) && GET_SIGN(Size, Src2) && GET_SIGN(Size, Dst)))
156  {
157  Regs->Flags |= CPU_EFLAGS_OF;
158  }
159  else
160  {
161  Regs->Flags &= ~CPU_EFLAGS_OF;
162  }
163  }
164  else if (FM_ADD == FlagsMode)
165  {
166  if ((GET_SIGN(Size, Src1) == GET_SIGN(Size, Src2) && GET_SIGN(Size, Src1) != GET_SIGN(Size, Dst)))
167  {
168  Regs->Flags |= CPU_EFLAGS_OF;
169  }
170  else
171  {
172  Regs->Flags &= ~CPU_EFLAGS_OF;
173  }
174  }
175  }
176 }
177 
178 
179 INTSTATUS
181  _In_ IG_CS_TYPE CsType,
182  _In_ QWORD Gva,
183  _Out_ void *Instrux
184  )
201 {
202  NDSTATUS ndstatus;
203  INTSTATUS status;
204  BYTE defData, defCode;
205  BYTE code[16] = { 0 };
206  BOOLEAN partialFetch;
207  size_t fetchedBytes;
208 
209  if (NULL == Instrux)
210  {
212  }
213 
214  partialFetch = FALSE;
215 
216  if (IG_CS_TYPE_64B == CsType)
217  {
218  defData = ND_DATA_64;
219  defCode = ND_CODE_64;
220  }
221  else if (IG_CS_TYPE_32B == CsType)
222  {
223  defData = ND_DATA_32;
224  defCode = ND_CODE_32;
225  }
226  else if (IG_CS_TYPE_16B == CsType)
227  {
228  defData = ND_DATA_16;
229  defCode = ND_CODE_16;
230  }
231  else
232  {
234  }
235 
236  if ((Gva & PAGE_MASK) != ((Gva + ND_MAX_INSTRUCTION_LENGTH) & PAGE_MASK))
237  {
238  void *p;
239  DWORD size;
240 
241  size = PAGE_REMAINING(Gva);
242 
243  status = IntVirtMemMap(Gva, size, 0, 0, &p);
244  if (!INT_SUCCESS(status))
245  {
246  goto cleanup_and_leave;
247  }
248 
249  memcpy(code, p, size);
250  fetchedBytes = size;
251 
252  IntVirtMemUnmap(&p);
253 
254  status = IntVirtMemMap(Gva + size, 16 - size, 0, 0, &p);
255  if (!INT_SUCCESS(status))
256  {
257  partialFetch = TRUE;
258  goto partial_fetch;
259  }
260 
261  memcpy(&code[size], p, 16 - size);
262  fetchedBytes += 16 - size;
263 
264  IntVirtMemUnmap(&p);
265 partial_fetch:
266  ;
267  }
268  else
269  {
270  void *p;
271 
272  status = IntVirtMemMap(Gva, 16, 0, 0, &p);
273  if (!INT_SUCCESS(status))
274  {
275  goto cleanup_and_leave;
276  }
277 
278  memcpy(code, p, 16);
279  fetchedBytes = 16;
280 
281  IntVirtMemUnmap(&p);
282  }
283 
284  ndstatus = NdDecodeEx(Instrux, code, fetchedBytes, defCode, defData);
285  if ((ND_STATUS_BUFFER_TOO_SMALL == ndstatus) && (partialFetch))
286  {
288  goto cleanup_and_leave;
289  }
290  else if (!ND_SUCCESS(ndstatus))
291  {
292  status = INT_STATUS_DISASM_ERROR;
293  goto cleanup_and_leave;
294  }
295  else
296  {
297  // no error from disasm
298  status = INT_STATUS_SUCCESS;
299  }
300 
301 cleanup_and_leave:
302 
303  return status;
304 }
305 
306 
307 INTSTATUS
309  _In_reads_bytes_(BufferSize) PBYTE Buffer,
310  _In_ size_t BufferSize,
311  _In_ IG_CS_TYPE CsType,
312  _Out_ void *Instrux
313  )
328 {
329  NDSTATUS ndstatus;
330  INTSTATUS status;
331  BYTE defData, defCode;
332 
333  if (NULL == Buffer)
334  {
336  }
337 
338  if (0 == BufferSize)
339  {
341  }
342 
343  if (NULL == Instrux)
344  {
346  }
347 
348  if (IG_CS_TYPE_64B == CsType)
349  {
350  defData = ND_DATA_64;
351  defCode = ND_CODE_64;
352  }
353  else if (IG_CS_TYPE_32B == CsType)
354  {
355  defData = ND_DATA_32;
356  defCode = ND_CODE_32;
357  }
358  else if (IG_CS_TYPE_16B == CsType)
359  {
360  defData = ND_DATA_16;
361  defCode = ND_CODE_16;
362  }
363  else
364  {
366  }
367 
368  ndstatus = NdDecodeEx(Instrux, Buffer, BufferSize, defCode, defData);
369  if (!ND_SUCCESS(ndstatus))
370  {
371  status = INT_STATUS_DISASM_ERROR;
372  goto cleanup_and_leave;
373  }
374 
375  status = INT_STATUS_SUCCESS;
376 
377 cleanup_and_leave:
378 
379  return status;
380 }
381 
382 
383 INTSTATUS
385  _In_ DWORD CpuNumber,
386  _In_ IG_ARCH_REGS *Registers,
387  _In_opt_ IG_SEG_REGS *Segments,
388  _Out_ INSTRUX *Instrux
389  )
407 {
408  INTSTATUS status;
409  IG_SEG_REGS segs;
410  DWORD csType = 0;
411 
412  if (NULL == Instrux)
413  {
415  }
416 
417  if (NULL == Segments)
418  {
419  status = IntGetSegs(CpuNumber, &segs);
420  if (!INT_SUCCESS(status))
421  {
422  ERROR("[ERROR] IntGetSegs failed: 0x%08x\n", status);
423  return status;
424  }
425 
426  Segments = &segs;
427  }
428 
429  // Get the CS type for this cpu (we need it in order to see if it's in 16 bit, 32 bit or 64 bit).
430  status = IntGetCurrentMode(CpuNumber, &csType);
431  if (!INT_SUCCESS(status))
432  {
433  ERROR("[ERROR] IntGetCurrentMode failed: 0x%08x\n", status);
434  return status;
435  }
436 
437  // Make sure the CPU is either in 32 bit or in 64 bit.
438  if ((csType != IG_CS_TYPE_16B) && (csType != IG_CS_TYPE_32B) && (csType != IG_CS_TYPE_64B))
439  {
440  ERROR("[ERROR] Unsupported CS type: %d\n", csType);
442  }
443 
444  return IntDecDecodeInstruction(csType, Segments->CsBase + Registers->Rip, Instrux);
445 }
446 
447 
448 INTSTATUS
450  _In_ void *Cache,
451  _In_ DWORD CpuNumber,
452  _In_ PIG_ARCH_REGS Registers,
453  _Out_ PINSTRUX Instrux,
454  _In_ DWORD Options,
455  _Out_opt_ BOOLEAN *CacheHit,
456  _Out_opt_ BOOLEAN *Added
457  )
480 {
481  INTSTATUS status;
482  DWORD ring, mode;
483  QWORD cr3, rip;
484  BOOLEAN global;
485 
486  if (NULL == Cache)
487  {
489  }
490 
491  if (NULL == Registers)
492  {
494  }
495 
496  if (NULL == Instrux)
497  {
499  }
500 
501  mode = 0;
502  global = FALSE;
503 
504  status = IntGetCurrentMode(CpuNumber, &mode);
505  if (!INT_SUCCESS(status))
506  {
507  ERROR("[ERROR] IntGetCurrentMode failed: 0x%08x\n", status);
508  return status;
509  }
510 
511  // Get the current CPL of the running VCPU. We use a common x64 invariant: user mode < 0xFFFF800000000000 -
512  // this greatly improves performance on Xen.
513  if (IG_CS_TYPE_64B == mode)
514  {
515  // 64 bit mode (not 32 bit or compatibility mode), RIP == linear RIP - CS has base 0 in 64 bits mode.
516  rip = Registers->Rip;
517 
518  ring = (rip < 0xFFFF800000000000 ? IG_CS_RING_3 : IG_CS_RING_0);
519  }
520  else
521  {
522  IG_SEG_REGS segs;
523 
524  // 32 bit or compatibility mode. We have to get the segment registers to determine the linear RIP.
525  status = IntGetSegs(CpuNumber, &segs);
526  if (!INT_SUCCESS(status))
527  {
528  ERROR("[ERROR] IntGetSegs failed: 0x%08x\n", status);
529  return status;
530  }
531 
532  ring = (segs.SsAr >> 5) & 3;
533 
534  rip = segs.CsBase + Registers->Rip;
535  }
536 
537  cr3 = Registers->Cr3;
538 
539  // Check if we're in user-mode or kernel-mode.
540  if (IG_CS_RING_0 == ring)
541  {
542  // We're in kernel mode, we can make the entry global - match it in any VA space.
543  global = TRUE;
544  cr3 = 0;
545  }
546 
547  // Lookup the instruction inside the icache.
548  status = IntIcLookupInstruction(Cache, Instrux, rip, cr3);
549  if (!INT_SUCCESS(status))
550  {
551  // Not found, decode it now.
552  status = IntDecDecodeInstruction(mode, rip, Instrux);
553  if ((INT_STATUS_PAGE_NOT_PRESENT == status) || (INT_STATUS_NO_MAPPING_STRUCTURES == status))
554  {
555  TRACE("[INFO] The page containing the RIP has been swapped out; will retry the instruction.\n");
556  return status;
557  }
558  else if (!INT_SUCCESS(status))
559  {
560  ERROR("[ERROR] IntDecDecodeInstructionAt failed: 0x%08x\n", status);
561  return status;
562  }
563 
564  // Cache instructions if and only if we're not in real mode or unpaged and the caller requests it
565  if ((0 != (Registers->Cr0 & CR0_PE)) &&
566  (0 != (Registers->Cr0 & CR0_PG)) &&
567  (0 == (Options & DEC_OPT_NO_CACHE)))
568  {
569  BOOLEAN goodCr3 = FALSE;
570 
571  // Make sure a process with this CR3 exists. The reason we have to do this is EFI; during BOOT, exits may
572  // be triggered from the EFI runtime, with CR3s which do not belong to any process. Caching such entries
573  // in the icache may lead to issues, since that CR3 will be freed without us knowing (since it doesn't
574  // belong to any process, no process termination event will take place).
575  if (0 != cr3)
576  {
577  // Non-null cr3, make sure this belongs to a valid process.
578  void *pProc;
579 
581  {
582  pProc = IntWinProcFindObjectByCr3(cr3);
583  }
584  else if (gGuest.OSType == introGuestLinux)
585  {
586  pProc = IntLixTaskFindByCr3(cr3);
587  }
588  else
589  {
590  pProc = NULL;
591  }
592 
593  if (NULL != pProc)
594  {
595  goodCr3 = TRUE;
596  }
597  }
598  else
599  {
600  // cr3 is 0, we can cache this, since we will use the SystemCR3.
601  goodCr3 = TRUE;
602  }
603 
604  // Add it to the instruction cache.
605  if (goodCr3)
606  {
607  status = IntIcAddInstruction(Cache, Instrux, rip, cr3, global);
608  if (!INT_SUCCESS(status) && (INT_STATUS_PAGE_NOT_PRESENT != status) &&
610  {
611  // It's not an issue that we couldn't add the instruction to the cache.
612  WARNING("[WARNING] IntIcAddInstruction failed: 0x%08x\n", status);
613  }
614  }
615  else
616  {
617  WARNING("[WARNING] Cannot cache %s, RIP 0x%016llx, CR3 0x%016llx, as no valid process exists!\n",
618  Instrux->Mnemonic, rip, cr3);
619  status = INT_STATUS_NOT_SUPPORTED;
620  }
621 
622  if (NULL != Added)
623  {
624  *Added = !!INT_SUCCESS(status);
625  }
626  }
627  else if (NULL != Added)
628  {
629  *Added = FALSE;
630  }
631 
632  if (NULL != CacheHit)
633  {
634  *CacheHit = FALSE;
635  }
636  }
637  else
638  {
639  if (NULL != CacheHit)
640  {
641  *CacheHit = TRUE;
642  }
643  if (NULL != Added)
644  {
645  *Added = FALSE;
646  }
647  }
648 
649  return INT_STATUS_SUCCESS;
650 }
651 
652 
653 static INTSTATUS
655  _In_ PINSTRUX Instrux,
656  _In_ PND_OPERAND Operand,
657  _In_ PIG_ARCH_REGS Registers,
658  _Out_ DWORD *AccessSize
659  )
674 {
675  DWORD accessSize;
676 
677  UNREFERENCED_PARAMETER(Registers);
678 
679  accessSize = Operand->Size;
680 
681  if ((0 == accessSize) || (ND_SIZE_UNKNOWN == accessSize))
682  {
683  // We still don't know the accessed size. Check if this is XSAVE & friends. If it is, we need to compute
684  // the actual accessed size.
685  if ((ND_INS_XSAVE == Instrux->Instruction) || (ND_INS_XSAVEC == Instrux->Instruction) ||
686  (ND_INS_XSAVEOPT == Instrux->Instruction) || (ND_INS_XSAVES == Instrux->Instruction) ||
687  (ND_INS_XRSTOR == Instrux->Instruction) || (ND_INS_XRSTORS == Instrux->Instruction))
688  {
690  (void *)(size_t)IG_CURRENT_VCPU,
691  &accessSize,
692  sizeof(accessSize));
693  if (!INT_SUCCESS(status))
694  {
695  ERROR("[ERROR] IntQueryGuestInfo failed: 0x%08x\n", status);
696  return status;
697  }
698  }
699 
700  if ((ND_INS_FXSAVE == Instrux->Instruction) || (ND_INS_FXRSTOR == Instrux->Instruction))
701  {
702  // Size is always 512 bytes for FXSAVE/FXRSTOR. It includes FPU state, FPU control/tag/etc. words & SSE
703  // state.
704  accessSize = 512;
705  }
706 
707  if ((ND_INS_BNDLDX == Instrux->Instruction) || (ND_INS_BNDSTX == Instrux->Instruction))
708  {
709  // BNDSTX/BNDLDX access the BND page tables. The accessed size, is therefore:
710  // 4B (x86) or 8B (x64) of the access is inside the BND directory
711  // 16B (x86) or 32B (x64) if the access is inside the BND table
712  // For now, assume the worst (maximum size), but a full MPX walk should be done to deduce if the access
713  // size is 4/8 bytes or 16/32 bytes.
714  accessSize = (Instrux->DefCode == ND_CODE_64) ? 32 : 16;
715  }
716  }
717  else if (ND_SIZE_CACHE_LINE == accessSize)
718  {
721  accessSize = 64;
722  }
723 
724  *AccessSize = accessSize;
725 
726  return INT_STATUS_SUCCESS;
727 }
728 
729 
730 INTSTATUS
732  _In_ PINSTRUX Instrux,
733  _In_ PIG_ARCH_REGS Registers,
734  _In_ QWORD Gla,
735  _In_ BYTE AccessType,
736  _Out_ DWORD *AccessSize
737  )
767 {
768  DWORD i;
769 
770  UNREFERENCED_PARAMETER(Registers);
772 
773  // NOTE: We use the fact that there isn't a single instruction that has multiple memory operands, except for
774  // MOVS/CMPS.
775  for (i = 0; i < Instrux->OperandsCount; i++)
776  {
777  // Check if this is the memory operand.
778  // We stop at the first operand that has at least some of the desired flags.
779  if ((ND_OP_MEM == Instrux->Operands[i].Type) && !!(Instrux->Operands[i].Access.Access & AccessType))
780  {
781  return IntDecDecodeOperandSize(Instrux, &Instrux->Operands[i], Registers, AccessSize);
782  }
783  }
784 
785  return INT_STATUS_NOT_FOUND;
786 }
787 
788 
789 INTSTATUS
791  _In_ PINSTRUX Instrux,
792  _In_ PND_OPERAND Operand,
793  _In_opt_ PIG_ARCH_REGS Registers,
794  _Out_ QWORD *LinearAddress
795  )
808 {
809  INTSTATUS status;
810  IG_ARCH_REGS gprRegs;
811  IG_SEG_REGS segRegs;
812  PND_OPERAND pOp;
813  QWORD gla;
814 
815  if (NULL == Registers)
816  {
817  status = IntGetGprs(IG_CURRENT_VCPU, &gprRegs);
818  if (!INT_SUCCESS(status))
819  {
820  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
821  return status;
822  }
823 
824  Registers = &gprRegs;
825  }
826 
827  pOp = Operand;
828 
829  if (pOp->Type != ND_OP_MEM)
830  {
832  }
833 
834  // We can't handle VSIB and compressed displacement.
835  if (pOp->Info.Memory.IsVsib)
836  {
837  ERROR("[ERROR] VSIB is not supported! Use IntDecComputeVsibLinearAddresses.\n");
839  }
840 
841  // We have the mem operand. Compute the linear address.
842  status = IntGetSegs(IG_CURRENT_VCPU, &segRegs);
843  if (!INT_SUCCESS(status))
844  {
845  ERROR("[ERROR] IntGetSegs failed with status: 0x%x\n", status);
846  return status;
847  }
848 
849  if (pOp->Info.Memory.HasSeg)
850  {
851  switch (pOp->Info.Memory.Seg)
852  {
853  case NDR_ES:
854  gla = segRegs.EsBase;
855  break;
856  case NDR_CS:
857  gla = segRegs.CsBase;
858  break;
859  case NDR_SS:
860  gla = segRegs.SsBase;
861  break;
862  case NDR_DS:
863  gla = segRegs.DsBase;
864  break;
865  case NDR_FS:
866  gla = segRegs.FsBase;
867  break;
868  case NDR_GS:
869  gla = segRegs.GsBase;
870  break;
871  default:
872  gla = 0;
873  break;
874  }
875  }
876  else
877  {
878  gla = 0;
879  }
880 
881  // Direct addressing. No RIP relative, base or SIB addressing can be present.
882  if (pOp->Info.Memory.IsDirect)
883  {
884  gla += pOp->Info.Memory.Disp;
885 
886  goto done;
887  }
888 
889  // Handle base.
890  if (pOp->Info.Memory.HasBase)
891  {
892  // Seg.Base + Base
893  QWORD base = ((PQWORD)&Registers->Rax)[pOp->Info.Memory.Base];
894 
895  base &= ND_SIZE_TO_MASK(pOp->Info.Memory.BaseSize);
896 
897  gla += base;
898  }
899 
900  // Handle index.
901  if (pOp->Info.Memory.HasIndex)
902  {
903  // Seg.Base [+ Base] + Index * Scale
904  QWORD index = ((PQWORD)&Registers->Rax)[pOp->Info.Memory.Index];
905 
906  index &= ND_SIZE_TO_MASK(pOp->Info.Memory.IndexSize);
907 
908  gla += index * pOp->Info.Memory.Scale;
909  }
910 
911  // Handle displacement.
912  if (pOp->Info.Memory.HasDisp)
913  {
914  if (pOp->Info.Memory.HasCompDisp)
915  {
916  gla += pOp->Info.Memory.Disp * pOp->Info.Memory.CompDispSize;
917  }
918  else
919  {
920  gla += pOp->Info.Memory.Disp;
921  }
922  }
923 
924  // RIP relative addressing.
925  if (pOp->Info.Memory.IsRipRel)
926  {
927  gla += Registers->Rip + Instrux->Length;
928  }
929 
930  // Special handling for BT, BTR, BTS, BTC instructions with bitbase addressing.
931  if (pOp->Info.Memory.IsBitbase)
932  {
933  QWORD bitbase, op1size, op2size;
934 
935  if ((Instrux->Operands[1].Type != ND_OP_REG) || (Instrux->Operands[1].Info.Register.Type != ND_REG_GPR))
936  {
938  }
939 
940  op1size = Instrux->Operands[0].Size;
941  op2size = Instrux->Operands[1].Size;
942 
943  bitbase = ND_SIGN_EX(op2size, ((QWORD *)&Registers->Rax)[Instrux->Operands[1].Info.Register.Reg]);
944 
945  if (bitbase & (1ULL << 63))
946  {
947  gla -= ((~bitbase >> 3) & ~(op1size - 1)) + op1size;
948  }
949  else
950  {
951  gla += (bitbase >> 3) & ~(op1size - 1);
952  }
953  }
954 
955  // Special handling for stack operations: if we have a PUSH, we have to subtract the accessed size, as, in fact,
956  // RSP - size is accessed, not RSP.
957  if (pOp->Info.Memory.IsStack)
958  {
959  if (pOp->Access.Write || pOp->Access.CondWrite)
960  {
961  gla -= pOp->Size;
962  }
963  }
964 
965 done:
966  *LinearAddress = gla;
967 
968  return INT_STATUS_SUCCESS;
969 }
970 
971 
972 static INTSTATUS
974  _In_ PINSTRUX Instrux,
975  _In_ PND_OPERAND Operand,
976  _In_opt_ PIG_ARCH_REGS Registers,
977  _In_opt_ PIG_XSAVE_AREA XsaveArea,
978  _Out_writes_to_(16, Operand->Info.Memory.Vsib.ElemCount) QWORD *LinearAddresses
979  )
996 {
997  INTSTATUS status;
998  IG_ARCH_REGS gprRegs;
999  IG_SEG_REGS segRegs;
1000  PND_OPERAND pOp;
1001  QWORD baseseg;
1002  DWORD i;
1003  OPERAND_VALUE indexValue = { 0 };
1004  union
1005  {
1006  DWORD dindexes[16];
1007  QWORD qindexes[8];
1008  } vsibindex;
1009 
1010  if (NULL == Registers)
1011  {
1012  status = IntGetGprs(IG_CURRENT_VCPU, &gprRegs);
1013  if (!INT_SUCCESS(status))
1014  {
1015  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
1016  return status;
1017  }
1018 
1019  Registers = &gprRegs;
1020  }
1021 
1022  pOp = Operand;
1023 
1024  if ((pOp->Type != ND_OP_MEM) || !pOp->Info.Memory.IsVsib)
1025  {
1027  }
1028 
1029  // We have the mem operand. Compute the linear address.
1030  status = IntGetSegs(IG_CURRENT_VCPU, &segRegs);
1031  if (!INT_SUCCESS(status))
1032  {
1033  ERROR("[ERROR] IntGetSegs failed with status: 0x%x\n", status);
1034  return status;
1035  }
1036 
1037  // Fetch the SSE index register.
1038  status = IntDecGetSseRegValue(XsaveArea, pOp->Info.Memory.Index, pOp->Info.Memory.IndexSize, &indexValue);
1039  if (!INT_SUCCESS(status))
1040  {
1042  {
1043  ERROR("[ERROR] IntDecGetSseRegValue failed: 0x%08x\n", status);
1044  }
1045 
1046  return status;
1047  }
1048 
1049  if (indexValue.Size > sizeof(vsibindex))
1050  {
1051  ERROR("[ERROR] The index value is too large: %u bytes!\n", indexValue.Size);
1052  return INT_STATUS_NOT_SUPPORTED;
1053  }
1054 
1055  memcpy(&vsibindex, indexValue.Value.ByteValues, indexValue.Size);
1056 
1057  if (pOp->Info.Memory.HasSeg)
1058  {
1059  switch (pOp->Info.Memory.Seg)
1060  {
1061  case NDR_ES:
1062  baseseg = segRegs.EsBase;
1063  break;
1064  case NDR_CS:
1065  baseseg = segRegs.CsBase;
1066  break;
1067  case NDR_SS:
1068  baseseg = segRegs.SsBase;
1069  break;
1070  case NDR_DS:
1071  baseseg = segRegs.DsBase;
1072  break;
1073  case NDR_FS:
1074  baseseg = segRegs.FsBase;
1075  break;
1076  case NDR_GS:
1077  baseseg = segRegs.GsBase;
1078  break;
1079  default:
1080  baseseg = 0;
1081  break;
1082  }
1083  }
1084  else
1085  {
1086  baseseg = 0;
1087  }
1088 
1089  for (i = 0; i < Operand->Info.Memory.Vsib.ElemCount; i++)
1090  {
1091  QWORD gla = baseseg;
1092 
1093  // Handle base.
1094  if (pOp->Info.Memory.HasBase)
1095  {
1096  // Seg.Base + Base
1097  QWORD base = ((PQWORD)&Registers->Rax)[pOp->Info.Memory.Base];
1098 
1099  base &= ND_SIZE_TO_MASK(pOp->Info.Memory.BaseSize);
1100 
1101  gla += base;
1102  }
1103 
1104  // Handle index.
1105  if (pOp->Info.Memory.HasIndex)
1106  {
1107  QWORD index;
1108 
1109  // Seg.Base [+ Base] + Index * Scale
1110  if (pOp->Info.Memory.Vsib.IndexSize == 4)
1111  {
1112  index = vsibindex.dindexes[i];
1113  }
1114  else
1115  {
1116  index = vsibindex.qindexes[i];
1117  }
1118 
1119  gla += index * pOp->Info.Memory.Scale;
1120  }
1121 
1122  // Handle displacement.
1123  if (pOp->Info.Memory.HasDisp)
1124  {
1125  if (pOp->Info.Memory.HasCompDisp)
1126  {
1127  gla += pOp->Info.Memory.Disp * pOp->Info.Memory.CompDispSize;
1128  }
1129  else
1130  {
1131  gla += pOp->Info.Memory.Disp;
1132  }
1133  }
1134 
1135  // RIP relative addressing.
1136  if (pOp->Info.Memory.IsRipRel)
1137  {
1138  gla += Registers->Rip + Instrux->Length;
1139  }
1140 
1141  LinearAddresses[i] = gla;
1142  }
1143 
1144  return INT_STATUS_SUCCESS;
1145 }
1146 
1147 
1148 INTSTATUS
1150  _In_ PINSTRUX Instrux,
1151  _In_opt_ PIG_ARCH_REGS Registers,
1152  _Out_ QWORD *LinearAddress
1153  )
1165 {
1166  DWORD i;
1167 
1168  if (NULL == Instrux)
1169  {
1171  }
1172 
1173  if (NULL == LinearAddress)
1174  {
1176  }
1177 
1178  i = 0;
1179 
1180  // Find the memory operand.
1181  while (i < Instrux->OperandsCount)
1182  {
1183  if ((Instrux->Operands[i].Type == ND_OP_MEM) && (Instrux->Operands[i].Access.Read ||
1184  Instrux->Operands[i].Access.CondRead))
1185  {
1186  break;
1187  }
1188 
1189  i++;
1190  }
1191 
1192  if (i >= Instrux->OperandsCount)
1193  {
1194  return INT_STATUS_NOT_FOUND;
1195  }
1196 
1197  return IntDecComputeLinearAddress(Instrux, &Instrux->Operands[i], Registers, LinearAddress);
1198 }
1199 
1200 
1201 INTSTATUS
1203  _In_ PINSTRUX Instrux,
1204  _In_opt_ PIG_ARCH_REGS Registers,
1205  _Out_ QWORD *LinearAddress
1206  )
1218 {
1219  DWORD i;
1220 
1221  if (NULL == Instrux)
1222  {
1224  }
1225 
1226  if (NULL == LinearAddress)
1227  {
1229  }
1230 
1231  i = 0;
1232 
1233  // Find the memory operand.
1234  while (i < Instrux->OperandsCount)
1235  {
1236  if ((Instrux->Operands[i].Type == ND_OP_MEM) && (Instrux->Operands[i].Access.Write ||
1237  Instrux->Operands[i].Access.CondWrite))
1238  {
1239  break;
1240  }
1241 
1242  i++;
1243  }
1244 
1245  if (i >= Instrux->OperandsCount)
1246  {
1247  return INT_STATUS_NOT_FOUND;
1248  }
1249 
1250  return IntDecComputeLinearAddress(Instrux, &Instrux->Operands[i], Registers, LinearAddress);
1251 }
1252 
1253 
1254 static INTSTATUS
1256  _In_ PINSTRUX Instrux,
1257  _In_ DWORD OperandIndex,
1258  _In_opt_ PIG_ARCH_REGS Registers,
1259  _In_ OPERAND_VALUE *OpValue,
1260  _In_ BOOLEAN Commit
1261  )
1278 {
1279  INTSTATUS status;
1280  PND_OPERAND pOp;
1281  IG_ARCH_REGS regs;
1282  QWORD gla;
1283  BOOLEAN commitRegs;
1284 
1285  gla = 0;
1286  commitRegs = FALSE;
1287 
1288  if (NULL == Registers)
1289  {
1290  status = IntGetGprs(IG_CURRENT_VCPU, &regs);
1291  if (!INT_SUCCESS(status))
1292  {
1293  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
1294  return status;
1295  }
1296 
1297  Registers = &regs;
1298  }
1299 
1300  pOp = &Instrux->Operands[OperandIndex];
1301 
1302  if (pOp->Type == ND_OP_MEM)
1303  {
1304  // Decode the linear address.
1305  status = IntDecComputeLinearAddress(Instrux, pOp, Registers, &gla);
1306  if (!INT_SUCCESS(status))
1307  {
1308  ERROR("[ERROR] IntDecComputeLinearAddress failed: 0x%08x\n", status);
1309  return status;
1310  }
1311 
1312  // Write the data.
1313  status = IntVirtMemSafeWrite(Registers->Cr3, gla, OpValue->Size, OpValue->Value.ByteValues, IG_CS_RING_0);
1314  if (!INT_SUCCESS(status))
1315  {
1316  ERROR("[ERROR] IntVirtMemSafeWrite failed: 0x%08x\n", status);
1317  return status;
1318  }
1319 
1320  // Modify the stack pointer, if needed.
1321  if (pOp->Info.Memory.IsStack)
1322  {
1323  Registers->Rsp -= pOp->Size;
1324  commitRegs = TRUE;
1325  }
1326  }
1327  else if (pOp->Type == ND_OP_REG)
1328  {
1329  if (pOp->Info.Register.Type == ND_REG_GPR)
1330  {
1331  PBYTE dst = NULL;
1332 
1333  if ((pOp->Size == ND_SIZE_8BIT) && (ND_ENCM_LEGACY == Instrux->EncMode) && (!Instrux->HasRex) &&
1334  (pOp->Info.Register.Reg >= NDR_RSP))
1335  {
1336  dst = (PBYTE) & ((PQWORD)&Registers->Rax)[pOp->Info.Register.Reg - 4] + 1;
1337  }
1338  else
1339  {
1340  dst = (PBYTE) & ((PQWORD)&Registers->Rax)[pOp->Info.Register.Reg];
1341  }
1342 
1343  switch (OpValue->Size)
1344  {
1345  case ND_SIZE_8BIT:
1346  *dst = OpValue->Value.ByteValues[0];
1347  break;
1348  case ND_SIZE_16BIT:
1349  *((PWORD)dst) = OpValue->Value.WordValues[0];
1350  break;
1351  case ND_SIZE_32BIT:
1352  *((PDWORD)dst) = OpValue->Value.DwordValues[0];
1353  *((PDWORD)(dst + 4)) = 0;
1354  break;
1355  case ND_SIZE_64BIT:
1356  *((PQWORD)dst) = OpValue->Value.QwordValues[0];
1357  break;
1358  default:
1359  break;
1360  }
1361 
1362  commitRegs = TRUE;
1363  }
1364  else if (pOp->Info.Register.Type == ND_REG_SSE)
1365  {
1366  status = IntDecSetSseRegValue(NULL, pOp->Info.Register.Reg, OpValue->Size, OpValue, Commit);
1367  if (!INT_SUCCESS(status))
1368  {
1369  ERROR("[ERROR] IntDecSetSseRegValue failed for register %d with size %d: 0x%08x\n",
1370  pOp->Info.Register.Reg, OpValue->Size, status);
1371  }
1372  return status;
1373  }
1374  else
1375  {
1376  ERROR("[ERROR] Unsupported register type: %d\n", pOp->Info.Register.Type);
1377  return INT_STATUS_NOT_SUPPORTED;
1378  }
1379  }
1380  else
1381  {
1382  ERROR("[ERROR] Unsupported operand type: %d\n", pOp->Type);
1383  return INT_STATUS_NOT_SUPPORTED;
1384  }
1385 
1386  if (commitRegs && Commit)
1387  {
1388  status = IntSetGprs(IG_CURRENT_VCPU, Registers);
1389  if (!INT_SUCCESS(status))
1390  {
1391  ERROR("[ERROR] IntSetGprs failed: 0x%08x\n", status);
1392  return status;
1393  }
1394  }
1395 
1396  return INT_STATUS_SUCCESS;
1397 }
1398 
1399 
1400 static INTSTATUS
1402  _In_ PINSTRUX Instrux,
1403  _In_ DWORD OperandIndex,
1404  _In_opt_ PIG_ARCH_REGS Registers,
1405  _In_opt_ PBYTE MemoryValue,
1406  _Out_ OPERAND_VALUE *WrittenValue
1407  )
1427 {
1428  INTSTATUS status = INT_STATUS_SUCCESS;
1429  IG_ARCH_REGS regs;
1430  QWORD opValue[4] = {0xbdbdbad, 0xbdbdbad, 0xbdbdbad, 0xbdbdbad}; // in case we will ever want to read a YMM value
1431  BYTE opSize = 0;
1432  PND_OPERAND pOp;
1433  //QWORD operandValue;
1434 
1435  if (OperandIndex >= Instrux->OperandsCount)
1436  {
1438  }
1439 
1440  pOp = &Instrux->Operands[OperandIndex];
1441  opSize = (BYTE)pOp->Size;
1442 
1443  if (opSize > sizeof(opValue) || 0 == opSize)
1444  {
1445  return INT_STATUS_NOT_SUPPORTED;
1446  }
1447 
1448  // If the caller didn't pass any, then read from the current VCPU.
1449  if (NULL == Registers)
1450  {
1451  status = IntGetGprs(IG_CURRENT_VCPU, &regs);
1452  if (!INT_SUCCESS(status))
1453  {
1454  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
1455  return status;
1456  }
1457 
1458  Registers = &regs;
1459  }
1460 
1461  if ((ND_OP_REG == pOp->Type) && (ND_REG_GPR == pOp->Info.Register.Type))
1462  {
1463  DWORD gprIndex = 0;
1464 
1465  // special check for ah, bh, ch, dh
1466  if ((ND_SIZE_8BIT == pOp->Size) && (pOp->Info.Register.Reg >= NDR_RSP) &&
1467  !Instrux->HasRex && (ND_ENCM_LEGACY == Instrux->EncMode))
1468  {
1469  gprIndex = pOp->Info.Register.Reg - 4;
1470 
1471  opValue[0] = ((PQWORD)&Registers->Rax)[gprIndex];
1472  opValue[0] = (opValue[0] >> 8) & 0xFF;
1473  }
1474  else
1475  {
1476  gprIndex = pOp->Info.Register.Reg;
1477 
1478  opValue[0] = ((PQWORD)&Registers->Rax)[gprIndex];
1479  }
1480  }
1481  else if (ND_OP_REG == pOp->Type && ND_REG_SSE == pOp->Info.Register.Type)
1482  {
1483  status = IntDecGetSseRegValue(NULL, pOp->Info.Register.Reg, pOp->Info.Register.Size, WrittenValue);
1484  if (!INT_SUCCESS(status))
1485  {
1486  ERROR("[ERROR] IntDecGetSseRegValue failed for instruction %s with status: 0x%x\n",
1487  Instrux->Mnemonic, status);
1488  return status;
1489  }
1490  WrittenValue->Size = (BYTE)Instrux->Operands[OperandIndex].Size;
1491  return INT_STATUS_SUCCESS;
1492  }
1493  else if (ND_OP_IMM == pOp->Type)
1494  {
1495  opValue[0] = pOp->Info.Immediate.Imm;
1496  }
1497  else if (ND_OP_MEM == pOp->Type)
1498  {
1499  if (NULL == MemoryValue)
1500  {
1501  QWORD guestVa = 0;
1502  DWORD retLength = 0;
1503 
1504  status = IntDecComputeLinearAddress(Instrux, pOp, Registers, &guestVa);
1505  if (!INT_SUCCESS(status))
1506  {
1507  ERROR("[ERROR] IntDecComputeLinearAddress failed: 0x%08x\n", status);
1508  return status;
1509  }
1510 
1511  // Safe: We check if opSize is greater than sizeof(opValue).
1512  status = IntVirtMemRead(guestVa, opSize, Registers->Cr3, &opValue, &retLength);
1513  if (!INT_SUCCESS(status))
1514  {
1515  WARNING("[WARNING] IntVirtMemRead failed for %llx: 0x%x\n", guestVa, status);
1516  return status;
1517  }
1518  if (retLength != (DWORD)opSize)
1519  {
1520  WARNING("[WARNING] IntVirtMemRead completed with no errors, but the returned size "
1521  "(%d) is not the expected size (%d\n",
1522  retLength, opSize);
1523  }
1524  }
1525  else
1526  {
1527  switch (opSize)
1528  {
1529  case 1:
1530  opValue[0] = *MemoryValue;
1531  break;
1532  case 2:
1533  opValue[0] = *(PWORD)MemoryValue;
1534  break;
1535  case 4:
1536  opValue[0] = *(PDWORD)MemoryValue;
1537  break;
1538  case 8:
1539  opValue[0] = *(PQWORD)MemoryValue;
1540  break;
1541  default:
1542  memcpy(opValue, MemoryValue, opSize);
1543  break;
1544  }
1545  }
1546  }
1547  else
1548  {
1549  // we don't know what to do with this
1550  ERROR("[ERROR] Unsupported operand type %d\n", pOp->Type);
1551  return INT_STATUS_NOT_SUPPORTED;
1552  }
1553 
1554  if (opSize > 8)
1555  {
1556  memcpy(WrittenValue->Value.QwordValues, opValue, opSize);
1557  }
1558  else
1559  {
1560  WrittenValue->Value.QwordValues[0] = ND_TRIM(opSize, opValue[0]);
1561  }
1562 
1563  WrittenValue->Size = opSize;
1564 
1565  return INT_STATUS_SUCCESS;
1566 }
1567 
1568 
1569 INTSTATUS
1571  _In_ PINSTRUX Instrux,
1572  _In_opt_ BYTE *SrcValueBuffer
1573  )
1591 {
1592  IG_ARCH_REGS *regs = &gVcpu->Regs;
1593  OPERAND_VALUE finalValue = { 0 };
1594  OPERAND_VALUE srcValue = { 0 };
1595  BOOLEAN hasSrc = FALSE;
1596  INTSTATUS status;
1597  DWORD ring;
1598 
1599  if (NULL == Instrux)
1600  {
1602  }
1603 
1604  if (gVcpu->AccessSize > ND_MAX_REGISTER_SIZE)
1605  {
1606  ERROR("[ERROR] Unsupported access size: 0x%08x\n", gVcpu->AccessSize);
1607  return INT_STATUS_NOT_SUPPORTED;
1608  }
1609 
1610  if (Instrux->EncMode != ND_ENCM_LEGACY && Instrux->EncMode != ND_ENCM_VEX)
1611  {
1612  ERROR("[ERROR] Unsupported encoding: %02d\n", Instrux->EncMode);
1613  IntDumpInstruction(Instrux, regs->Rip);
1614  return INT_STATUS_NOT_SUPPORTED;
1615  }
1616 
1617  // The RIP must be in kernel
1620  {
1621  ERROR("[ERROR] RIP is not in kernel: 0x%016llx\n", regs->Rip);
1622  return INT_STATUS_NOT_SUPPORTED;
1623  }
1624 
1625  // The access must be done in kernel
1626  if ((gGuest.OSType == introGuestWindows &&
1630  {
1631  ERROR("[ERROR] Access is not in kernel: [0x%016llx, 0x%016llx)\n", gVcpu->Gla, gVcpu->Gla + gVcpu->AccessSize);
1632  return INT_STATUS_NOT_SUPPORTED;
1633  }
1634 
1635  // The access must be read
1636  if (ND_ACCESS_READ != (Instrux->MemoryAccess & ND_ACCESS_READ))
1637  {
1638  ERROR("[ERROR] Access is not read: 0x%02x\n", Instrux->MemoryAccess);
1639  return INT_STATUS_NOT_SUPPORTED;
1640  }
1641 
1642  status = IntGetCurrentRing(IG_CURRENT_VCPU, &ring);
1643  if (!INT_SUCCESS(status))
1644  {
1645  ERROR("[ERROR] IntGetCurrentRing failed: 0x%08x\n", status);
1646  return status;
1647  }
1648 
1649  // The current ring must be 0
1650  if (ring != IG_CS_RING_0)
1651  {
1652  ERROR("[ERROR] Ring is not 0: %d\n", ring);
1653  return INT_STATUS_NOT_SUPPORTED;
1654  }
1655 
1656  if (SrcValueBuffer != NULL)
1657  {
1658  memcpy(srcValue.Value.ByteValues, SrcValueBuffer, gVcpu->AccessSize);
1659  srcValue.Size = gVcpu->AccessSize;
1660  hasSrc = TRUE;
1661  }
1662 
1663  status = INT_STATUS_SUCCESS;
1664 
1665  switch (Instrux->Instruction)
1666  {
1667  case ND_INS_MOVZX:
1668  case ND_INS_MOV:
1669  {
1670  DWORD dstSize = Instrux->Operands[0].Size;
1671 
1672  if (!hasSrc)
1673  {
1674  status = IntGetValueFromOperand(Instrux, 1, regs, NULL, &srcValue);
1675  if (!INT_SUCCESS(status))
1676  {
1677  ERROR("[ERROR] IntGetValueFromOperand failed: 0x%08x\n", status);
1678  return status;
1679  }
1680  }
1681 
1682  // Copy just the size of the destination operand (can't be more than a QWORD)
1683  finalValue.Value.QwordValues[0] = srcValue.Value.QwordValues[0];
1684  finalValue.Size = dstSize;
1685  break;
1686  }
1687 
1688  case ND_INS_AND:
1689  case ND_INS_OR:
1690  case ND_INS_XOR:
1691  {
1692  // Read the first operand and compute the new value
1693  OPERAND_VALUE val1 = { 0 };
1694 
1695  if (!hasSrc)
1696  {
1697  status = IntGetValueFromOperand(Instrux, 1, regs, NULL, &srcValue);
1698  if (!INT_SUCCESS(status))
1699  {
1700  ERROR("[ERROR] IntGetValueFromOperand failed: 0x%08x\n", status);
1701  return status;
1702  }
1703  }
1704 
1705  status = IntGetValueFromOperand(Instrux, 0, regs, NULL, &val1);
1706  if (!INT_SUCCESS(status))
1707  {
1708  ERROR("[ERROR] IntGetValueFromOperand failed: 0x%08x\n", status);
1709  return status;
1710  }
1711 
1712  if (ND_INS_AND == Instrux->Instruction)
1713  {
1714  finalValue.Value.QwordValues[0] = val1.Value.QwordValues[0] & srcValue.Value.QwordValues[0];
1715  }
1716  else if (ND_INS_OR == Instrux->Instruction)
1717  {
1718  finalValue.Value.QwordValues[0] = val1.Value.QwordValues[0] | srcValue.Value.QwordValues[0];
1719  }
1720  else if (ND_INS_XOR == Instrux->Instruction)
1721  {
1722  finalValue.Value.QwordValues[0] = val1.Value.QwordValues[0] ^ srcValue.Value.QwordValues[0];
1723  }
1724  else
1725  {
1726  ERROR("[ERROR] Someone forgot to add a case here? Instruction: %s\n", Instrux->Mnemonic);
1727  return INT_STATUS_NOT_SUPPORTED;
1728  }
1729 
1730  finalValue.Size = val1.Size;
1731 
1732  // We have to set the flags
1733  IntDecSetFlags(finalValue.Value.QwordValues[0], val1.Value.QwordValues[0],
1734  srcValue.Value.QwordValues[0], finalValue.Size, regs, FM_LOGIC);
1735 
1736  break;
1737  }
1738 
1739  case ND_INS_MOVDQA:
1740  case ND_INS_MOVDQU:
1741  case ND_INS_MOVAPS:
1742  {
1743  // Loads the first operand with the value from the second
1744  // Intel SDM specifies that when the MOVAPS source operand is a memory, it must be aligned on a 16- or 32- or
1745  // 64-byte boundary, or a #GP will be generated. But since we are treating an EPT violation caused by this
1746  // instruction we know that it could not generate a #GP, so there's no need to check for alignment here
1747  // and inject a #GP in guest
1748  DWORD dstSize = Instrux->Operands[0].Size;
1749 
1750  if (!hasSrc)
1751  {
1752  status = IntGetValueFromOperand(Instrux, 1, regs, NULL, &srcValue);
1753  if (!INT_SUCCESS(status))
1754  {
1755  ERROR("[ERROR] IntGetValueFromOperand failed: 0x%08x\n", status);
1756  return status;
1757  }
1758  }
1759 
1760  // Nothing else to do, except set the operand value (which is done after the switch)
1761  memcpy(finalValue.Value.ByteValues, srcValue.Value.ByteValues, srcValue.Size);
1762  // The actual size we have to set is the size of the destination
1763  finalValue.Size = dstSize;
1764 
1765  break;
1766  }
1767 
1768  case ND_INS_VMOVDQU:
1769  {
1770  if (!hasSrc)
1771  {
1772  status = IntGetValueFromOperand(Instrux, 1, regs, NULL, &srcValue);
1773  if (!INT_SUCCESS(status))
1774  {
1775  ERROR("[ERROR] IntGetValueFromOperand failed: 0x%08x\n", status);
1776  return status;
1777  }
1778  }
1779 
1780  if (ND_ENCM_VEX == Instrux->EncMode)
1781  {
1782  // DEST[255:0] <- SRC[255:0] or DEST[128:0] <- SRC[128:0]
1783  // DEST[MAXVL-1:256] <- 0 or DEST[MAXVL-1:128] <- 0
1784  ND_OPERAND_SIZE maxvl;
1785  status = IntDecGetMaxvl(&maxvl);
1786  if (!INT_SUCCESS(status))
1787  {
1788  ERROR("[ERROR] IntDecGetMaxvl failed: 0x%08x\n", status);
1789  return status;
1790  }
1791 
1792  // finalValue is already zeroed
1793 
1794  memcpy(finalValue.Value.ByteValues, srcValue.Value.ByteValues, srcValue.Size);
1795  finalValue.Size = maxvl;
1796  }
1797  else
1798  {
1799  status = INT_STATUS_NOT_SUPPORTED;
1800  }
1801 
1802  break;
1803  }
1804 
1805  default:
1806  ERROR("[ERROR] Unsupported instruction: %s!\n", Instrux->Mnemonic);
1807  return INT_STATUS_NOT_SUPPORTED;
1808  }
1809 
1810  if (!INT_SUCCESS(status))
1811  {
1812  return status;
1813  }
1814 
1815  for (SIZE_T i = 0; i < PAGE_COUNT_4K(gVcpu->Gla, gVcpu->AccessSize); i++)
1816  {
1817  QWORD gla = gVcpu->Gla + (i * PAGE_SIZE_4K);
1819  if (!INT_SUCCESS(status))
1820  {
1821  ERROR("[ERROR] IntDecEmulatePageWalk failed for 0x%016llx: 0x%08x\n", gla, status);
1822  return status;
1823  }
1824  }
1825 
1826  regs->Rip += Instrux->Length;
1827  status = IntSetGprs(IG_CURRENT_VCPU, regs);
1828  if (!INT_SUCCESS(status))
1829  {
1830  ERROR("[ERROR] IntSetGprs failed: 0x%08x\n", status);
1831  return status;
1832  }
1833 
1834  // Set the value
1835  status = IntSetValueForOperand(Instrux, 0, regs, &finalValue, TRUE);
1836  if (!INT_SUCCESS(status))
1837  {
1838  ERROR("[ERROR] IntSetValueForOperand failed: 0x%08x\n", status);
1839 
1840  // Restore RIP
1841  regs->Rip -= Instrux->Length;
1842  status = IntSetGprs(IG_CURRENT_VCPU, regs);
1843  if (!INT_SUCCESS(status))
1844  {
1845  ERROR("[ERROR] IntSetGprs failed: 0x%08x\n", status);
1846  // This is bad. We can't really continue, as the guest is now in an unstable state: the instruction was not
1847  // emulated, but the RIP points to the next instruction. Even if we switch the order of operations:
1850  IntBugCheck();
1851  }
1852 
1853  return status;
1854  }
1855 
1856  return INT_STATUS_SUCCESS;
1857 }
1858 
1859 
1860 INTSTATUS
1862  _In_ PINSTRUX Instrux,
1863  _In_opt_ PIG_ARCH_REGS Registers,
1864  _In_opt_ PBYTE MemoryValue,
1865  _Out_ OPERAND_VALUE *WrittenValue
1866  )
1888 {
1890  IG_ARCH_REGS regs;
1891  OPERAND_VALUE writtenValue = { 0 };
1892  QWORD value;
1893  BOOLEAN bComputeResult = FALSE;
1894  OPERAND_VALUE value1 = { 0 };
1895  OPERAND_VALUE value2 = { 0 };
1896 
1897  if (NULL == Instrux)
1898  {
1900  }
1901 
1902  if (NULL == WrittenValue)
1903  {
1905  }
1906 
1907  // If the caller didn't pass any, then read from the current VCPU.
1908  if (NULL == Registers)
1909  {
1910  status = IntGetGprs(IG_CURRENT_VCPU, &regs);
1911  if (!INT_SUCCESS(status))
1912  {
1913  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
1914  return status;
1915  }
1916 
1917  Registers = &regs;
1918  }
1919 
1920  // each instruction is unique and special
1921  switch (Instrux->Instruction)
1922  {
1923  // all we need is the second operand, simple
1924  case ND_INS_MOV:
1925  case ND_INS_STOS:
1926  case ND_INS_MOVS:
1927  case ND_INS_MOVNTI:
1928  case ND_INS_MOVZX:
1929  status = IntGetValueFromOperand(Instrux, 1, Registers, MemoryValue, &writtenValue);
1930  if (!INT_SUCCESS(status))
1931  {
1932  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
1933  Instrux->Mnemonic, status);
1934  return status;
1935  }
1936 
1937  break;
1938 
1939  case ND_INS_MOVSX:
1940  case ND_INS_MOVSXD:
1941  status = IntGetValueFromOperand(Instrux, 1, Registers, MemoryValue, &value1);
1942  if (!INT_SUCCESS(status))
1943  {
1944  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
1945  Instrux->Mnemonic, status);
1946  return status;
1947  }
1948 
1949  bComputeResult = TRUE;
1950 
1951  break;
1952 
1953  case ND_INS_XCHG:
1954  if (ND_OP_MEM == Instrux->Operands[1].Type)
1955  {
1956  // get the value from the first operand
1957  status = IntGetValueFromOperand(Instrux, 0, Registers, MemoryValue, &writtenValue);
1958  if (!INT_SUCCESS(status))
1959  {
1960  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
1961  Instrux->Mnemonic, status);
1962  return status;
1963  }
1964  }
1965  else
1966  {
1967  // get the value from the second operand
1968  status = IntGetValueFromOperand(Instrux, 1, Registers, MemoryValue, &writtenValue);
1969  if (!INT_SUCCESS(status))
1970  {
1971  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
1972  Instrux->Mnemonic, status);
1973  return status;
1974  }
1975  }
1976 
1977  break;
1978 
1979  case ND_INS_CMPXCHG:
1980  // Compares the value in the AL, AX, EAX, or RAX register with the first operand (destination operand).
1981  // If the two values are equal, the second operand (source operand) is loaded into the destination operand.
1982  // Otherwise, the destination operand is loaded into the AL, AX, EAX or RAX register.
1983 
1984  // get the value from the destination operand
1985  status = IntGetValueFromOperand(Instrux, 0, Registers, MemoryValue, &value2);
1986  if (!INT_SUCCESS(status))
1987  {
1988  WARNING("[WARNING] IntGetValueFromOperand failed for CMPXCHG (destination operand) with status: 0x%x\n",
1989  status);
1990  return status;
1991  }
1992 
1993  value1.Value.QwordValues[0] = ND_TRIM(value2.Size, Registers->Rax);
1994 
1995  if (value1.Value.QwordValues[0] == value2.Value.QwordValues[0])
1996  {
1997  // get the value from the source operand
1998  status = IntGetValueFromOperand(Instrux, 1, Registers, MemoryValue, &writtenValue);
1999  if (!INT_SUCCESS(status))
2000  {
2001  WARNING("[WARNING] IntGetValueFromOperand failed for CMPXCHG (source operand) with status: 0x%x\n",
2002  status);
2003  return status;
2004  }
2005  }
2006  else
2007  {
2008  // destination remains unchanged
2009  writtenValue = value2;
2010  }
2011 
2012  break;
2013 
2014  case ND_INS_CMPXCHG8B:
2015  // Compares the 64-bit value in EDX:EAX with the operand (destination operand). If the values are equal,
2016  // the 64-bit value in ECX:EBX is stored in the destination operand.
2017  // Otherwise, the value in the destination operand is loaded into EDX:EAX.
2018  // The destination operand is an 8-byte memory location.
2019  // For the EDX:EAX and ECX:EBX register pairs, EDX and ECX contain the high-order 32 bits and EAX and EBX
2020  // contain the low-order 32 bits of a 64-bit value.
2021 
2022  // get the value from EDX:EAX
2023  value1.Value.QwordValues[0] = ((Registers->Rdx & 0xFFFFFFFF) << 32) | (Registers->Rax & 0xFFFFFFFF);
2024 
2025  // get the operand value
2026  status = IntGetValueFromOperand(Instrux, 0, Registers, MemoryValue, &value2);
2027  if (!INT_SUCCESS(status))
2028  {
2029  WARNING("[WARNING] IntGetValueFromOperand failed for CMPXCHG8B with status: 0x%x\n", status);
2030  return status;
2031  }
2032 
2033  // compare them
2034  if (value1.Value.QwordValues[0] == value2.Value.QwordValues[0])
2035  {
2036  // return ECX:EBX
2037  writtenValue.Value.QwordValues[0] = ((Registers->Rcx & 0xFFFFFFFF) << 32) | (Registers->Rbx & 0xFFFFFFFF);
2038  }
2039  else
2040  {
2041  // destination remains unchanged
2042  writtenValue = value2;
2043  }
2044 
2045  writtenValue.Size = 8;
2046 
2047  break;
2048 
2049  case ND_INS_CMPXCHG16B:
2050  {
2051  // Compares the 128-bit value in RDX:RAX with the operand (destination operand). If the values are equal,
2052  // the 128-bit value in RCX:RBX is stored in the destination operand.
2053  // Otherwise, the value in the destination operand is loaded into RDX:RAX.
2054  // The destination operand is a 16-byte memory location.
2055  // For the RDX:RAX and RCX:RBX register pairs, RDX and RCX contain the high-order 64 bits and RAX and RBX
2056  // contain the low-order 64bits of a 128-bit value.
2057 
2058  OPERAND_VALUE destinationValue = { 0 };
2059 
2060  // get the operand value
2061  status = IntGetValueFromOperand(Instrux, 0, Registers, MemoryValue, &destinationValue);
2062  if (!INT_SUCCESS(status))
2063  {
2064  WARNING("[WARNING] IntGetValueFromOperand failed for CMPXCHG16B with status: 0x%x\n", status);
2065  return status;
2066  }
2067 
2068  // compare them
2069  if ((Registers->Rdx == destinationValue.Value.QwordValues[0]) &&
2070  (Registers->Rax == destinationValue.Value.QwordValues[1]))
2071  {
2072  WrittenValue->Value.QwordValues[0] = Registers->Rcx;
2073  WrittenValue->Value.QwordValues[1] = Registers->Rbx;
2074  }
2075  else
2076  {
2077  // destination remains unchanged
2078  WrittenValue->Value.QwordValues[0] = destinationValue.Value.QwordValues[0];
2079  WrittenValue->Value.QwordValues[1] = destinationValue.Value.QwordValues[1];
2080  }
2081 
2082  // return here
2083  WrittenValue->Size = 16;
2084 
2085  return INT_STATUS_SUCCESS;
2086  }
2087 
2088  // we need to take values from 2 operands for the next instructions
2089  case ND_INS_ADD:
2090  case ND_INS_SUB:
2091  case ND_INS_ADC:
2092  case ND_INS_SBB:
2093  case ND_INS_MUL:
2094  case ND_INS_DIV:
2095  case ND_INS_IDIV:
2096  case ND_INS_AND:
2097  case ND_INS_OR:
2098  case ND_INS_XOR:
2099  case ND_INS_RCL:
2100  case ND_INS_RCR:
2101  case ND_INS_ROL:
2102  case ND_INS_ROR:
2103  case ND_INS_BTS:
2104  case ND_INS_BTR:
2105  case ND_INS_BTC:
2106  status = IntGetValueFromOperand(Instrux, 0, Registers, MemoryValue, &value1);
2107  if (!INT_SUCCESS(status))
2108  {
2109  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2110  Instrux->Mnemonic, status);
2111  return status;
2112  }
2113 
2114  status = IntGetValueFromOperand(Instrux, 1, Registers, MemoryValue, &value2);
2115  if (!INT_SUCCESS(status))
2116  {
2117  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2118  Instrux->Mnemonic, status);
2119  return status;
2120  }
2121 
2122  bComputeResult = TRUE;
2123 
2124  break;
2125 
2126  case ND_INS_XADD:
2127  if (ND_OP_MEM == Instrux->Operands[1].Type)
2128  {
2129  // get the value from the first operand
2130  status = IntGetValueFromOperand(Instrux, 0, Registers, MemoryValue, &writtenValue);
2131  if (!INT_SUCCESS(status))
2132  {
2133  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2134  Instrux->Mnemonic, status);
2135  return status;
2136  }
2137  }
2138  else
2139  {
2140  // written value = operand1 + operand2
2141  status = IntGetValueFromOperand(Instrux, 0, Registers, MemoryValue, &value1);
2142  if (!INT_SUCCESS(status))
2143  {
2144  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2145  Instrux->Mnemonic, status);
2146  return status;
2147  }
2148 
2149  status = IntGetValueFromOperand(Instrux, 1, Registers, MemoryValue, &value2);
2150  if (!INT_SUCCESS(status))
2151  {
2152  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2153  Instrux->Mnemonic, status);
2154  return status;
2155  }
2156 
2157  bComputeResult = TRUE;
2158  }
2159 
2160  break;
2161 
2162  // 1 & 2 operands version: value1 = op1 value, value2 = op2 value
2163  // 3 operands version: value1 = op2 value, value2 = op3 value
2164  case ND_INS_IMUL:
2165  if (3 == Instrux->ExpOperandsCount)
2166  {
2167  status = IntGetValueFromOperand(Instrux, 1, Registers, MemoryValue, &value1);
2168  if (!INT_SUCCESS(status))
2169  {
2170  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2171  Instrux->Mnemonic, status);
2172  return status;
2173  }
2174 
2175  status = IntGetValueFromOperand(Instrux, 2, Registers, MemoryValue, &value2);
2176  if (!INT_SUCCESS(status))
2177  {
2178  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2179  Instrux->Mnemonic, status);
2180  return status;
2181  }
2182  }
2183  else
2184  {
2185  status = IntGetValueFromOperand(Instrux, 0, Registers, MemoryValue, &value1);
2186  if (!INT_SUCCESS(status))
2187  {
2188  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2189  Instrux->Mnemonic, status);
2190  return status;
2191  }
2192 
2193  status = IntGetValueFromOperand(Instrux, 1, Registers, MemoryValue, &value2);
2194  if (!INT_SUCCESS(status))
2195  {
2196  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2197  Instrux->Mnemonic, status);
2198  return status;
2199  }
2200  }
2201 
2202  bComputeResult = TRUE;
2203 
2204  break;
2205 
2206  case ND_INS_INC:
2207  case ND_INS_DEC:
2208  status = IntGetValueFromOperand(Instrux, 0, Registers, MemoryValue, &value1);
2209  if (!INT_SUCCESS(status))
2210  {
2211  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2212  Instrux->Mnemonic, status);
2213  return status;
2214  }
2215 
2216  value2.Value.QwordValues[0] = 1;
2217  bComputeResult = TRUE;
2218 
2219  break;
2220 
2221  case ND_INS_NOT:
2222  status = IntGetValueFromOperand(Instrux, 0, Registers, MemoryValue, &writtenValue);
2223  if (!INT_SUCCESS(status))
2224  {
2225  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2226  Instrux->Mnemonic, status);
2227  return status;
2228  }
2229 
2230  writtenValue.Value.QwordValues[0] = ~writtenValue.Value.QwordValues[0];
2231 
2232  break;
2233 
2234  case ND_INS_NEG:
2235  status = IntGetValueFromOperand(Instrux, 0, Registers, MemoryValue, &writtenValue);
2236  if (!INT_SUCCESS(status))
2237  {
2238  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2239  Instrux->Mnemonic, status);
2240  return status;
2241  }
2242 
2243  writtenValue.Value.QwordValues[0] = 0 - writtenValue.Value.QwordValues[0];
2244 
2245  break;
2246 
2247  case ND_INS_LIDT:
2248  case ND_INS_LGDT:
2249  // don't use IntGetValueFromOperand because that function will try to read data from the memory address
2250  // we need to return the value that will be written intro IDTR/GDTR
2251  status = IntDecComputeLinearAddress(Instrux, &Instrux->Operands[0], Registers, &value);
2252  if (!INT_SUCCESS(status))
2253  {
2254  ERROR("[ERROR] IntDecComputeLinearAddress failed for %s, operand 0: 0x%x\n", Instrux->Mnemonic, status);
2255  return status;
2256  }
2257 
2258  writtenValue.Value.QwordValues[0] = value;
2259  writtenValue.Size = (BYTE)Instrux->Operands[0].Size;
2260 
2261  break;
2262 
2263  case ND_INS_MOVDQU:
2264  case ND_INS_MOVAPD:
2265  case ND_INS_MOVAPS:
2266  case ND_INS_MOVUPD:
2267  case ND_INS_MOVUPS:
2268  {
2269  // Moves 128 bits of packed single-precision floating-point values from the source operand (second operand) to
2270  // the destination operand (first operand). This instruction can be used to load an XMM register from a 128-bit
2271  // memory location, to store the contents of an XMM register into a 128-bit memory location, or to move data
2272  // between two XMM registers.
2273 
2274  // get the operand value
2275  status = IntGetValueFromOperand(Instrux, 1, Registers, MemoryValue, WrittenValue);
2276  if (!INT_SUCCESS(status))
2277  {
2278  ERROR("[ERROR] IntGetValueFromOperand failed for MOVUPS with status: 0x%x\n", status);
2279  return status;
2280  }
2281 
2282  return INT_STATUS_SUCCESS;
2283  }
2284 
2285  // we get here for cases in which the destination operand is memory and the source operand is a register,
2286  // in which case dest[31:0] = src[31:0] and dest[mxvl-1:32] = 0
2287  case ND_INS_MOVSS:
2288  {
2289  status = IntGetValueFromOperand(Instrux, 1, Registers, MemoryValue, &writtenValue);
2290  if (!INT_SUCCESS(status))
2291  {
2292  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2293  Instrux->Mnemonic, status);
2294  return status;
2295  }
2296 
2297  break;
2298  }
2299 
2300  case ND_INS_MOVHPS:
2301  case ND_INS_MOVHPD:
2302  {
2303  OPERAND_VALUE destinationValue = { 0 };
2304 
2305  status = IntGetValueFromOperand(Instrux, 1, Registers, MemoryValue, &writtenValue);
2306  if (!INT_SUCCESS(status))
2307  {
2308  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2309  Instrux->Mnemonic, status);
2310  return status;
2311  }
2312  status = IntGetValueFromOperand(Instrux, 0, Registers, MemoryValue, &destinationValue);
2313  if (!INT_SUCCESS(status))
2314  {
2315  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2316  Instrux->Mnemonic, status);
2317  return status;
2318  }
2319 
2320  WrittenValue->Value.QwordValues[1] = writtenValue.Value.QwordValues[0];
2321  WrittenValue->Value.QwordValues[0] = destinationValue.Value.QwordValues[0];
2322  WrittenValue->Size = 16;
2323 
2324  return INT_STATUS_SUCCESS;
2325  }
2326 
2327  case ND_INS_MOVLPS:
2328  case ND_INS_MOVLPD:
2329  status = IntGetValueFromOperand(Instrux, 1, Registers, MemoryValue, &writtenValue);
2330  if (!INT_SUCCESS(status))
2331  {
2332  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2333  Instrux->Mnemonic, status);
2334  return status;
2335  }
2336  break;
2337 
2338  case ND_INS_ADDPS:
2339  {
2340  OPERAND_VALUE source1 = { 0 };
2341  OPERAND_VALUE source2 = { 0 };
2342 
2343  status = IntGetValueFromOperand(Instrux, 0, Registers, MemoryValue, &source1);
2344  if (!INT_SUCCESS(status))
2345  {
2346  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2347  Instrux->Mnemonic, status);
2348  return status;
2349  }
2350  status = IntGetValueFromOperand(Instrux, 1, Registers, MemoryValue, &source2);
2351  if (!INT_SUCCESS(status))
2352  {
2353  WARNING("[WARNING] IntGetValueFromOperand failed for instruction %s with status: 0x%x\n",
2354  Instrux->Mnemonic, status);
2355  return status;
2356  }
2357 
2358  WrittenValue->Value.DwordValues[0] = source1.Value.DwordValues[0] + source2.Value.DwordValues[0];
2359  WrittenValue->Value.DwordValues[1] = source1.Value.DwordValues[1] + source2.Value.DwordValues[1];
2360  WrittenValue->Value.DwordValues[2] = source1.Value.DwordValues[2] + source2.Value.DwordValues[2];
2361  WrittenValue->Value.DwordValues[3] = source1.Value.DwordValues[3] + source2.Value.DwordValues[3];
2362 
2363 
2364  WrittenValue->Size = 16;
2365 
2366  return INT_STATUS_SUCCESS;
2367  }
2368 
2369  default:
2370  WARNING("[WARNING] Unsupported instruction: %s\n", Instrux->Mnemonic);
2371  return INT_STATUS_NOT_SUPPORTED;
2372  }
2373 
2374  // now that we have the operand values, compute the instruction result (if we need to)
2375  if (bComputeResult)
2376  {
2377  BYTE opSize;
2378 
2379  opSize = (BYTE)Instrux->Operands[0].Size;
2380 
2381  switch (Instrux->Instruction)
2382  {
2383  case ND_INS_MOVSX:
2384  case ND_INS_MOVSXD:
2385  writtenValue.Value.QwordValues[0] = ND_SIGN_EX(value1.Size, value1.Value.QwordValues[0]);
2386  break;
2387 
2388  case ND_INS_ADD:
2389  case ND_INS_INC: // we have 1 in value2, so we can treat INC as an ADD op1, 1
2390  case ND_INS_XADD: // temp = src + dest; src = dest; dest = temp; we are interested only in dest's value
2391  writtenValue.Value.QwordValues[0] = value1.Value.QwordValues[0] + value2.Value.QwordValues[0];
2392  break;
2393 
2394  case ND_INS_SUB:
2395  case ND_INS_DEC: // we have 1 in value2, so we can treat DEC as an SUB op1, 1
2396  writtenValue.Value.QwordValues[0] = value1.Value.QwordValues[0] - value2.Value.QwordValues[0];
2397  break;
2398 
2399  case ND_INS_ADC:
2400  writtenValue.Value.QwordValues[0] = value1.Value.QwordValues[0] +
2401  value2.Value.QwordValues[0] +
2402  DEC_GET_FLAG(Registers->Flags, DEC_EFLAG_CF);
2403  break;
2404 
2405  case ND_INS_SBB:
2406  writtenValue.Value.QwordValues[0] = value1.Value.QwordValues[0] -
2407  (value2.Value.QwordValues[0] +
2408  DEC_GET_FLAG(Registers->Flags, DEC_EFLAG_CF));
2409  break;
2410 
2411  case ND_INS_MUL:
2412  writtenValue.Value.QwordValues[0] = value1.Value.QwordValues[0] * value2.Value.QwordValues[0];
2413  break;
2414 
2415  case ND_INS_IMUL:
2416  {
2417  INT64 signedValue1 = (INT64)value1.Value.QwordValues[0];
2418  INT64 signedValue2 = (INT64)value2.Value.QwordValues[0];
2419 
2420  writtenValue.Value.QwordValues[0] = (QWORD)(signedValue1 * signedValue2);
2421  break;
2422  }
2423 
2424  case ND_INS_DIV:
2425  if (value2.Value.QwordValues[0] != 0)
2426  {
2427  writtenValue.Value.QwordValues[0] = value1.Value.QwordValues[0] / value2.Value.QwordValues[0];
2428  }
2429  else
2430  {
2431  writtenValue.Value.QwordValues[0] = 0; // or -1 ?
2432  }
2433  break;
2434 
2435  case ND_INS_IDIV:
2436  {
2437  INT64 signedValue1 = (INT64)value1.Value.QwordValues[0];
2438  INT64 signedValue2 = (INT64)value2.Value.QwordValues[0];
2439 
2440  if (signedValue2 != 0)
2441  {
2442  writtenValue.Value.QwordValues[0] = (QWORD)(signedValue1 / signedValue2);
2443  }
2444  else
2445  {
2446  writtenValue.Value.QwordValues[0] = 0; // or -1 ?
2447  }
2448  break;
2449  }
2450 
2451  case ND_INS_AND:
2452  writtenValue.Value.QwordValues[0] = value1.Value.QwordValues[0] & value2.Value.QwordValues[0];
2453  break;
2454 
2455  case ND_INS_OR:
2456  writtenValue.Value.QwordValues[0] = value1.Value.QwordValues[0] | value2.Value.QwordValues[0];
2457  break;
2458 
2459  case ND_INS_XOR:
2460  writtenValue.Value.QwordValues[0] = value1.Value.QwordValues[0] ^ value2.Value.QwordValues[0];
2461  break;
2462 
2463  // see "RCL/RCR/ROL/ROR-Rotate" in Intel SDM 4.2 Instruction Set Reference, N-Z
2464  case ND_INS_RCL:
2465  {
2466  DWORD count = 0;
2467  BYTE cf = 0;
2468 
2469  if (ND_SIZE_8BIT == opSize)
2470  {
2471  count = (DWORD)((value2.Value.QwordValues[0] & 0x1F) % 9);
2472  }
2473  else if (ND_SIZE_16BIT == opSize)
2474  {
2475  count = (DWORD)((value2.Value.QwordValues[0] & 0x1F) % 17);
2476  }
2477  else if (ND_SIZE_32BIT == opSize)
2478  {
2479  count = (DWORD)(value2.Value.QwordValues[0] & 0x1F);
2480  }
2481  else if (ND_SIZE_64BIT == opSize)
2482  {
2483  count = (DWORD)(value2.Value.QwordValues[0] & 0x3F);
2484  }
2485 
2486  cf = DEC_GET_FLAG(Registers->Flags, DEC_EFLAG_CF);
2487 
2488  while (0 != count)
2489  {
2490  BYTE tempCf = ND_MSB(opSize, value1.Value.QwordValues[0]);
2491 
2492  value1.Value.QwordValues[0] = (value1.Value.QwordValues[0] << 1) + cf;
2493 
2494  cf = tempCf;
2495 
2496  count--;
2497  }
2498 
2499  writtenValue = value1;
2500 
2501  break;
2502  }
2503 
2504  case ND_INS_RCR:
2505  {
2506  DWORD count = 0;
2507  BYTE cf = 0;
2508 
2509  if (ND_SIZE_8BIT == opSize)
2510  {
2511  count = (DWORD)((value2.Value.QwordValues[0] & 0x1F) % 9);
2512  }
2513  else if (ND_SIZE_16BIT == opSize)
2514  {
2515  count = (DWORD)((value2.Value.QwordValues[0] & 0x1F) % 17);
2516  }
2517  else if (ND_SIZE_32BIT == opSize)
2518  {
2519  count = (DWORD)(value2.Value.QwordValues[0] & 0x1F);
2520  }
2521  else if (ND_SIZE_64BIT == opSize)
2522  {
2523  count = (DWORD)(value2.Value.QwordValues[0] & 0x3F);
2524  }
2525 
2526  cf = DEC_GET_FLAG(Registers->Flags, DEC_EFLAG_CF);
2527 
2528  while (0 != count)
2529  {
2530  BYTE tempCf = ND_LSB(opSize, value1.Value.QwordValues[0]);
2531 
2532  value1.Value.QwordValues[0] = (value1.Value.QwordValues[0] >> 1) + ((QWORD)cf << ((opSize * 8) - 1));
2533 
2534  cf = tempCf;
2535 
2536  count--;
2537  }
2538 
2539  writtenValue = value1;
2540 
2541  break;
2542  }
2543 
2544  case ND_INS_ROL:
2545  writtenValue.Value.QwordValues[0] = (value1.Value.QwordValues[0] << value2.Value.QwordValues[0]) |
2546  (value1.Value.QwordValues[0] >> ((opSize * 8ull) -
2547  value2.Value.QwordValues[0]));
2548 
2549  break;
2550 
2551  case ND_INS_ROR:
2552  writtenValue.Value.QwordValues[0] = (value1.Value.QwordValues[0] >> value2.Value.QwordValues[0]) |
2553  (value1.Value.QwordValues[0] << ((opSize * 8ull) -
2554  value2.Value.QwordValues[0]));
2555 
2556  break;
2557 
2558  case ND_INS_BTS:
2559  writtenValue.Value.QwordValues[0] = (value1.Value.QwordValues[0] | (1ULL << (value2.Value.QwordValues[0] %
2560  (opSize * 8ull))));
2561 
2562  break;
2563 
2564  case ND_INS_BTR:
2565  writtenValue.Value.QwordValues[0] = (value1.Value.QwordValues[0] & ~(1ULL << (value2.Value.QwordValues[0] %
2566  (opSize * 8ull))));
2567 
2568  break;
2569 
2570  case ND_INS_BTC:
2571  writtenValue.Value.QwordValues[0] = (value1.Value.QwordValues[0] ^ (1ULL << (value2.Value.QwordValues[0] %
2572  (opSize * 8ull))));
2573 
2574  break;
2575 
2576  // this should never happen
2577  default:
2578  WARNING("[WARNING] Unsupported instruction: %s\n", Instrux->Mnemonic);
2579  return INT_STATUS_NOT_SUPPORTED;
2580  }
2581 
2582  writtenValue.Size = opSize;
2583  }
2584 
2585  *WrittenValue = writtenValue;
2586 
2587  return INT_STATUS_SUCCESS;
2588 }
2589 
2590 
2591 INTSTATUS
2593  _In_ DWORD CpuNumber,
2594  _In_ PINSTRUX Instrux
2595  )
2610 {
2611  INTSTATUS status;
2612  IG_ARCH_REGS regs;
2613  OPERAND_VALUE value = { 0 };
2614 
2615  if (NULL == Instrux)
2616  {
2618  }
2619 
2620  status = IntGetGprs(CpuNumber, &regs);
2621  if (!INT_SUCCESS(status))
2622  {
2623  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
2624  return status;
2625  }
2626 
2627  if (ND_INS_PUSH == Instrux->Instruction)
2628  {
2629  status = IntGetValueFromOperand(Instrux, 0, &regs, NULL, &value);
2630  if (!INT_SUCCESS(status))
2631  {
2632  ERROR("[ERROR] IntGetValueFromOperand failed: 0x%08x\n", status);
2633  return status;
2634  }
2635 
2636  status = IntSetValueForOperand(Instrux, 1, &regs, &value, TRUE);
2637  if (!INT_SUCCESS(status))
2638  {
2639  ERROR("[ERROR] IntSetValueForOperand failed: 0x%08x\n", status);
2640  return status;
2641  }
2642  }
2643  else if (ND_INS_MOV == Instrux->Instruction)
2644  {
2645  status = IntGetValueFromOperand(Instrux, 1, &regs, NULL, &value);
2646  if (!INT_SUCCESS(status))
2647  {
2648  ERROR("[ERROR] IntGetValueFromOperand failed: 0x%08x\n", status);
2649  return status;
2650  }
2651 
2652  status = IntSetValueForOperand(Instrux, 0, &regs, &value, TRUE);
2653  if (!INT_SUCCESS(status))
2654  {
2655  ERROR("[ERROR] IntSetValueForOperand failed: 0x%08x\n", status);
2656  return status;
2657  }
2658  }
2659  else
2660  {
2661  return INT_STATUS_NOT_SUPPORTED;
2662  }
2663 
2664  if (INT_SUCCESS(status))
2665  {
2666  regs.Rip += Instrux->Length;
2667 
2668  status = IntSetGprs(CpuNumber, &regs);
2669  if (!INT_SUCCESS(status))
2670  {
2671  ERROR("[ERROR] IntSetGprs failed: 0x%08x\n", status);
2672  }
2673  }
2674 
2675  return status;
2676 }
2677 
2678 
2679 static __forceinline QWORD
2681  _In_ void *Address,
2682  _In_ DWORD Size,
2683  _In_ QWORD New,
2684  _In_ QWORD Old
2685  )
2699 {
2700  if (8 == Size)
2701  {
2702  return (QWORD)_InterlockedCompareExchange64(Address, New, Old);
2703  }
2704  else if (4 == Size)
2705  {
2706  return (DWORD)_InterlockedCompareExchange(Address, New, Old);
2707  }
2708  else if (2 == Size)
2709  {
2710  return (WORD)_InterlockedCompareExchange16(Address, New, Old);
2711  }
2712  else
2713  {
2714  return (BYTE)_InterlockedCompareExchange8(Address, New, Old);
2715  }
2716 }
2717 
2718 
2719 INTSTATUS
2721  _Out_ QWORD *NewValue
2722  )
2742 {
2743  INTSTATUS status = INT_STATUS_SUCCESS;
2744  QWORD gpa = 0, gla = 0, oldval = 0, newval = 0, actval = 0;
2745  DWORD pto = 0, size = 0;
2746  PBYTE pPage;
2747  OPERAND_VALUE src = { 0 };
2748  OPERAND_VALUE dst = { 0 };
2749  IG_ARCH_REGS shRegs;
2750  INSTRUX *instrux;
2751 
2752  if (NULL == NewValue)
2753  {
2755  }
2756 
2757  gpa = gVcpu->Gpa;
2758  gla = gVcpu->Gla;
2759  instrux = &gVcpu->Instruction;
2760 
2761  // Make sure this instruction does in fact a memory write.
2762  if (instrux->Operands[0].Type != ND_OP_MEM)
2763  {
2764  ERROR("[ERROR] First operand is not memory for PT instruction, type is %d!\n", instrux->Operands[0].Type);
2765  return INT_STATUS_NOT_SUPPORTED;
2766  }
2767 
2768  if (instrux->Operands[0].Size > 8)
2769  {
2770  ERROR("[ERROR] First operand is greater than 8 bytes in size, size is %d!\n", instrux->Operands[0].Size);
2771  return INT_STATUS_NOT_SUPPORTED;
2772  }
2773 
2774  // The page table offset.
2775  pto = gpa & PAGE_OFFSET;
2776 
2777  // The memory access size. The memory is the first operand (checked above).
2778  size = instrux->Operands[0].Size;
2779 
2780  if (pto + size > PAGE_SIZE)
2781  {
2782  ERROR("[ERROR] Access spans outside the page: offset 0x%x, size %d!\n", pto, size);
2783  return INT_STATUS_NOT_SUPPORTED;
2784  }
2785 
2786  // Get a reference to the actual PTE, so we can atomically make the modification.
2787  status = IntGpaCacheFindAndAdd(gGuest.GpaCache, gpa, &pPage);
2788  if (!INT_SUCCESS(status))
2789  {
2790  ERROR("[ERROR] IntGpaCacheFetchAndAdd failed for GPA 0x%016llx, GLA 0x%016llx: 0x%08x\n", gpa, gla, status);
2791  return status;
2792  }
2793 
2794 _retry_emulation:
2795  // We need to operate on a local copy of the registers until we can commit the modified regs. Note that if we retry
2796  // an emulation, the shadow regs state will be overwritten with the unmodified registers stored in VCPU, so we start
2797  // from scratch, basically.
2798  memcpy(&shRegs, &gVcpu->Regs, sizeof(IG_ARCH_REGS));
2799 
2800  // Fetch the old value from the PTE.
2801  oldval = (size == 8) ? *((QWORD *)(pPage + pto))
2802  : (size == 4) ? *((DWORD *)(pPage + pto))
2803  : (size == 2) ? *((WORD *)(pPage + pto))
2804  : *((BYTE *)(pPage + pto));
2805 
2806  // We support only a small subset of PT accessing instructions - the most common. All the rest will be emulated
2807  // by the hypervisor/integrator emulator or will be single-stepped.
2808  switch (instrux->Instruction)
2809  {
2810  case ND_INS_MOV:
2811  case ND_INS_STOS:
2812  {
2813  // Fetch the source.
2814  status = IntGetValueFromOperand(instrux, 1, &shRegs, NULL, &src);
2815  if (!INT_SUCCESS(status))
2816  {
2817  ERROR("[ERROR] IntGetValueFromOperand failed: 0x%08x\n", status);
2818  goto cleanup_and_exit;
2819  }
2820 
2821  // The new value is the instruction source operand.
2822  newval = ND_TRIM(src.Size, src.Value.QwordValues[0]);
2823 
2824  actval = IntDecAtomicStore(pPage + pto, size, newval, oldval);
2825 
2826  // Actual value in memory is not the same as the known old value - retry.
2827  if (actval != oldval)
2828  {
2829  goto _retry_emulation;
2830  }
2831 
2832  *NewValue = newval;
2833  }
2834  break;
2835 
2836  case ND_INS_XCHG:
2837  {
2838  // Fetch the source.
2839  status = IntGetValueFromOperand(instrux, 1, &shRegs, NULL, &src);
2840  if (!INT_SUCCESS(status))
2841  {
2842  ERROR("[ERROR] IntGetValueFromOperand failed: 0x%08x\n", status);
2843  goto cleanup_and_exit;
2844  }
2845 
2846  // The destination operand will be stored inside the source operand, and the source operand will be stored
2847  // in memory.
2848  dst.Size = (BYTE)size;
2849  dst.Value.QwordValues[0] = oldval;
2850 
2851  // Store the source. We know it's a register, so it's ok to do it without commit.
2852  status = IntSetValueForOperand(instrux, 1, &shRegs, &dst, FALSE);
2853  if (!INT_SUCCESS(status))
2854  {
2855  ERROR("[ERROR] IntSetValueForOperand failed: 0x%08x\n", status);
2856  goto cleanup_and_exit;
2857  }
2858 
2859  // The new value is the source operand.
2860  newval = src.Value.QwordValues[0];
2861 
2862  // Write the destination.
2863  actval = IntDecAtomicStore(pPage + pto, size, newval, oldval);
2864 
2865  // Actual value in memory is not the same as the known old value - retry.
2866  if (actval != oldval)
2867  {
2868  goto _retry_emulation;
2869  }
2870 
2871  *NewValue = newval;
2872  }
2873  break;
2874 
2875  case ND_INS_CMPXCHG:
2876  {
2877  QWORD rax;
2878 
2879  // Fetch the source.
2880  status = IntGetValueFromOperand(instrux, 1, &shRegs, NULL, &src);
2881  if (!INT_SUCCESS(status))
2882  {
2883  ERROR("[ERROR] IntGetValueFromOperand failed: 0x%08x\n", status);
2884  goto cleanup_and_exit;
2885  }
2886 
2887  // EAX/RAX is compared to the memory value. If they're equal, the source value is loaded in memory.
2888  // Otherwise, the memory value is loaded in EAX/RAX.
2889  rax = ND_TRIM(size, shRegs.Rax);
2890 
2891  // The (tentative) new value is the source operand.
2892  newval = src.Value.QwordValues[0];
2893 
2894  // Atomically handle the CMPXCHG operation.
2895  actval = IntDecAtomicStore(pPage + pto, size, newval, rax);
2896 
2897  IntDecSetFlags(rax - actval, rax, actval, src.Size, &shRegs, FM_SUB);
2898 
2899  // Note that we don't retry emulation on CMPXCHG; we simply set the flags and move on.
2900  if (actval == rax)
2901  {
2902  // Values equal, the exchange was made, the new value is the source operand.
2903  *NewValue = newval;
2904  }
2905  else
2906  {
2907  // The exchange wasn't made, the value is not changed.
2908  if ((src.Size == 4) && (ND_CODE_64 == instrux->DefCode))
2909  {
2910  shRegs.Rax = 0;
2911  }
2912 
2913  // Values are not equal, load the destination into the accumulator.
2914  memcpy(&shRegs.Rax, &actval, src.Size);
2915 
2916  *NewValue = actval;
2917  }
2918  }
2919  break;
2920 
2921  case ND_INS_CMPXCHG8B:
2922  {
2923  QWORD edx_eax;
2924 
2925  // The size is 8 bytes. We compare EDX:EAX and store ECX:EBX if ZF.
2926  edx_eax = ((shRegs.Rdx & 0xFFFFFFFF) << 32) | (shRegs.Rax & 0xFFFFFFFF);
2927 
2928  // The (tentative) new value is the ECX:EBX pair.
2929  newval = ((shRegs.Rcx & 0xFFFFFFFF) << 32) | (shRegs.Rbx & 0xFFFFFFFF);
2930 
2931  actval = _InterlockedCompareExchange64((INT64 *)(pPage + pto), newval, edx_eax);
2932 
2933  if (actval == edx_eax)
2934  {
2935  // Values equal, the exchange was made, the new value is the source operand.
2936  shRegs.Flags |= CPU_EFLAGS_ZF;
2937 
2938  *NewValue = newval;
2939  }
2940  else
2941  {
2942  // The exchange wasn't made, the value is not changed.
2943  shRegs.Flags &= ~CPU_EFLAGS_ZF;
2944 
2945  shRegs.Rdx = (actval >> 32) & 0xFFFFFFFF;
2946  shRegs.Rax = (actval & 0xFFFFFFFF);
2947 
2948  *NewValue = actval;
2949  }
2950  }
2951  break;
2952 
2953  case ND_INS_AND:
2954  case ND_INS_XOR:
2955  case ND_INS_OR:
2956  {
2957  QWORD res;
2958 
2959  // Fetch the source.
2960  status = IntGetValueFromOperand(instrux, 1, &shRegs, NULL, &src);
2961  if (!INT_SUCCESS(status))
2962  {
2963  ERROR("[ERROR] IntGetValueFromOperand failed: 0x%08x\n", status);
2964  goto cleanup_and_exit;
2965  }
2966 
2967  // Fetch the current memory value. We know the size of both operands will be the same!
2968  dst.Size = (BYTE)size;
2969  dst.Value.QwordValues[0] = oldval;
2970 
2971  res = (ND_INS_AND == instrux->Instruction) ? (dst.Value.QwordValues[0] & src.Value.QwordValues[0])
2972  : (ND_INS_XOR == instrux->Instruction) ? (dst.Value.QwordValues[0] ^ src.Value.QwordValues[0])
2973  : (dst.Value.QwordValues[0] | src.Value.QwordValues[0]);
2974 
2975  IntDecSetFlags(res, dst.Value.QwordValues[0], src.Value.QwordValues[0], src.Size, &shRegs, FM_LOGIC);
2976 
2977  // The new value is the AND/XOR/OR between the source operand and the old memory value (the destination).
2978  newval = res;
2979 
2980  // Write the destination.
2981  actval = IntDecAtomicStore(pPage + pto, size, newval, oldval);
2982 
2983  // Actual value in memory is not the same as the known old value - retry.
2984  if (actval != oldval)
2985  {
2986  goto _retry_emulation;
2987  }
2988 
2989  *NewValue = newval;
2990  }
2991  break;
2992 
2993  case ND_INS_BTC:
2994  case ND_INS_BTR:
2995  case ND_INS_BTS:
2996  {
2997  QWORD btgla, mask;
2998 
2999  // Compute the linear address encoded in the instruction.
3000  status = IntDecComputeLinearAddress(instrux, &instrux->Operands[0], &shRegs, &btgla);
3001  if (!INT_SUCCESS(status))
3002  {
3003  ERROR("[ERROR] IntDecComputeLinearAddress failed: 0x%08x\n", status);
3004  goto cleanup_and_exit;
3005  }
3006 
3007  // BT* instructions have bitbase addressing, so make sure the encoded gla is the same as the faulted gla.
3008  if (btgla != gVcpu->Gla)
3009  {
3010  TRACE("[PTEMU] GLA mismatch: 0x%016llx - 0x%016llx, will not emulate.\n", btgla, gVcpu->Gla);
3011  status = INT_STATUS_NOT_SUPPORTED;
3012  goto cleanup_and_exit;
3013  }
3014 
3015  // Get the bit offset.
3016  status = IntGetValueFromOperand(instrux, 1, &shRegs, NULL, &src);
3017  if (!INT_SUCCESS(status))
3018  {
3019  ERROR("[ERROR] IntGetValueFromOperand failed: 0x%08x\n", status);
3020  goto cleanup_and_exit;
3021  }
3022 
3023  // If the source operand is immediate, the bit offset is truncated to the max destination size.
3024  if (instrux->Operands[1].Type == ND_OP_IMM)
3025  {
3026  src.Value.QwordValues[0] %= instrux->Operands[0].Size * 8ull;
3027  }
3028 
3029  if (src.Value.QwordValues[0] >= instrux->Operands[0].Size * 8ull)
3030  {
3031  TRACE("[PTEMU] Bit offset to high: %llu\n", src.Value.QwordValues[0]);
3032  status = INT_STATUS_NOT_SUPPORTED;
3033  goto cleanup_and_exit;
3034  }
3035 
3036  mask = 1ULL << src.Value.QwordValues[0];
3037 
3038  dst.Size = (BYTE)size;
3039  dst.Value.QwordValues[0] = oldval;
3040 
3041  // Set/clear the CF.
3042  if (dst.Value.QwordValues[0] & mask)
3043  {
3044  shRegs.Flags |= CPU_EFLAGS_CF;
3045  }
3046  else
3047  {
3048  shRegs.Flags &= ~CPU_EFLAGS_CF;
3049  }
3050 
3051  // Set/clear/complement the bit.
3052  dst.Value.QwordValues[0] = (ND_INS_BTS == instrux->Instruction) ? (dst.Value.QwordValues[0] | mask)
3053  : (ND_INS_BTR == instrux->Instruction) ? (dst.Value.QwordValues[0] & ~mask)
3054  : (dst.Value.QwordValues[0] ^ mask);
3055 
3056  // The new value is the old memory value (destination operand) with the modified bit.
3057  newval = dst.Value.QwordValues[0];
3058 
3059  // Write the destination.
3060  actval = IntDecAtomicStore(pPage + pto, size, newval, oldval);
3061 
3062  // Actual value in memory is not the same as the known old value - retry.
3063  if (actval != oldval)
3064  {
3065  goto _retry_emulation;
3066  }
3067 
3068  *NewValue = newval;
3069  }
3070  break;
3071 
3072  default:
3073  {
3074  char text[ND_MIN_BUF_SIZE];
3075 
3076  NdToText(instrux, shRegs.Rip, ND_MIN_BUF_SIZE, text);
3077 
3078  ERROR("[ERROR] Instruction 0x%016llx:'%s' not supported, writing at GLA %llx, GPA %llx.\n",
3079  shRegs.Rip, text, gla, gpa);
3080  status = INT_STATUS_NOT_SUPPORTED;
3081  goto cleanup_and_exit;
3082  }
3083  }
3084 
3085 cleanup_and_exit:
3086  if (INT_SUCCESS(status))
3087  {
3088  // In case of success, advance the RIP & commit the registers state.
3089  shRegs.Rip += instrux->Length;
3090 
3091  status = IntSetGprs(gVcpu->Index, &shRegs);
3092  if (!INT_SUCCESS(status))
3093  {
3094  ERROR("[ERROR] IntSetGprs failed: 0x%08x\n", status);
3095  }
3096  else
3097  {
3098  memcpy(&gVcpu->Regs, &shRegs, sizeof(IG_ARCH_REGS));
3099  }
3100  }
3101 
3102  // Release the mapped PT.
3104 
3105  return status;
3106 }
3107 
3108 
3109 INTSTATUS
3111  _In_ PINSTRUX Instrux,
3112  _Out_ DWORD *Count
3113  )
3128 {
3129  DWORD i, count;
3130 
3131  if (NULL == Instrux)
3132  {
3134  }
3135 
3136  if (NULL == Count)
3137  {
3139  }
3140 
3141  count = 0;
3142 
3143  for (i = 0; i < Instrux->OperandsCount; i++)
3144  {
3145  // Ignore shadow stack - no CPUs support it for now.
3146  if ((Instrux->Operands[i].Type == ND_OP_MEM) && (!Instrux->Operands[i].Info.Memory.IsShadowStack))
3147  {
3148  // VSIB will lead to several memory accesses to be made.
3149  count += Instrux->Operands[i].Info.Memory.IsVsib ? Instrux->Operands[i].Info.Memory.Vsib.ElemCount : 1;
3150  }
3151  }
3152 
3153  *Count = count;
3154 
3155  return INT_STATUS_SUCCESS;
3156 }
3157 
3158 
3159 INTSTATUS
3161  _In_ PINSTRUX Instrux,
3162  _In_opt_ PIG_ARCH_REGS Registers,
3163  _In_opt_ PIG_XSAVE_AREA XsaveArea,
3164  _Out_writes_(*Count) MEMADDR *Gla,
3165  _Inout_ DWORD *Count
3166  )
3184 {
3185  INTSTATUS status;
3186  IG_ARCH_REGS regs;
3187  DWORD i, count;
3188 
3189  if (NULL == Instrux)
3190  {
3192  }
3193 
3194  if (NULL == Registers)
3195  {
3196  status = IntGetGprs(IG_CURRENT_VCPU, &regs);
3197  if (!INT_SUCCESS(status))
3198  {
3199  return status;
3200  }
3201 
3202  Registers = &regs;
3203  }
3204 
3205  if (NULL == Gla)
3206  {
3208  }
3209 
3210  count = 0;
3211 
3212  for (i = 0; i < Instrux->OperandsCount; i++)
3213  {
3214  if (Instrux->Operands[i].Type == ND_OP_MEM)
3215  {
3216  if (Instrux->Operands[i].Info.Memory.IsShadowStack)
3217  {
3218  continue;
3219  }
3220 
3221  if (count >= *Count)
3222  {
3224  }
3225 
3226  // Special handling for VSIB addressing.
3227  if (Instrux->Operands[i].Info.Memory.IsVsib)
3228  {
3229  // There can't be more than 16 accessed locations (ZMM size / DWORD = 16)
3230  QWORD vsibglas[16] = { 0 }, j = 0;
3231 
3232  if (Instrux->Operands[i].Info.Memory.Vsib.ElemCount > 16)
3233  {
3234  ERROR("[ERROR] Too many VSIB elements accessed: %d\n",
3235  Instrux->Operands[i].Info.Memory.Vsib.ElemCount);
3236  continue;
3237  }
3238 
3239  status = IntDecComputeVsibLinearAddresses(Instrux, &Instrux->Operands[i],
3240  Registers, XsaveArea, vsibglas);
3241  if (!INT_SUCCESS(status))
3242  {
3243  ERROR("[ERROR] IntDecComputeVsibLinearAddresses failed: 0x%08x\n", status);
3244  continue;
3245  }
3246 
3247  for (j = 0; j < Instrux->Operands[i].Info.Memory.Vsib.ElemCount; j++)
3248  {
3249  if (count >= *Count)
3250  {
3252  }
3253 
3254  Gla[count].Access = Instrux->Operands[i].Access.Access;
3255  Gla[count].Size = Instrux->Operands[i].Info.Memory.Vsib.ElemSize;
3256  Gla[count].Gla = vsibglas[j];
3257 
3258  count++;
3259  }
3260  }
3261  else
3262  {
3263  status = IntDecComputeLinearAddress(Instrux, &Instrux->Operands[i], Registers, &Gla[count].Gla);
3264  if (!INT_SUCCESS(status))
3265  {
3266  ERROR("[ERROR] IntDecComputeLinearAddress failed at op %d: 0x%08x", i, status);
3267  continue;
3268  }
3269 
3270  status = IntDecDecodeOperandSize(Instrux, &Instrux->Operands[i], Registers, &Gla[count].Size);
3271  if (!INT_SUCCESS(status))
3272  {
3273  ERROR("[ERROR] IntDecDecodeOperandSize failed: 0x%08x\n", status);
3274  continue;
3275  }
3276 
3277  Gla[count].Access = Instrux->Operands[i].Access.Access;
3278 
3279  count++;
3280  }
3281  }
3282  }
3283 
3284  *Count = count;
3285 
3286  return INT_STATUS_SUCCESS;
3287 }
3288 
3289 
3290 static INTSTATUS
3292  _In_opt_ PIG_XSAVE_AREA XsaveArea,
3293  _In_ DWORD Reg,
3294  _In_ DWORD Size,
3295  _When_(Set == TRUE, _In_) _When_(Set == FALSE, _Out_) OPERAND_VALUE *Value,
3296  _In_ BOOLEAN Set,
3297  _In_ BOOLEAN Commit
3298  )
3315 {
3316  INTSTATUS status;
3317  IG_XSAVE_AREA *xsave;
3318  XSAVE_AREA xa = { 0 };
3319  int cpuidregs[4];
3320 
3321  if (NULL == Value)
3322  {
3324  }
3325 
3326  if (NULL == XsaveArea)
3327  {
3328  // This may fail if the needed function is not implemented in Xen libs.
3329  status = IntGetXsaveArea(IG_CURRENT_VCPU, &xa);
3330  if (!INT_SUCCESS(status))
3331  {
3332  ERROR("[ERROR] IntGetXsaveArea failed: 0x%08x\n", status);
3333  return status;
3334  }
3335  }
3336  else
3337  {
3338  xa.XsaveArea = XsaveArea;
3339  }
3340 
3341  xsave = xa.XsaveArea;
3342 
3343  // The XSAVE area format (the interesting parts, at least):
3344  // 0: FPU stuff
3345  // MM0 .. MM7/ST0 .. ST7
3346  // 1: XMM0 .. XMM15
3347  // XSAVE extended header
3348  // 2: YMM0_Hi .. YMM15_Hi (bits 128:255 of each YMM register; bits 0:127 are the same as XMM0 .. XMM15)
3349  // 3: MPX state
3350  // 5: AVX512 mask registers
3351  // 6: ZMM0_Hi .. ZMM15_Hi (bits 256:511 of ZMM0 .. ZMM15 registers; bits 128:255 are the same as YMM0_Hi.. YMM15_Hi,
3352  // bits 0:127 are the same as XMM0 .. XMM15)
3353  // 7: ZMM16 .. ZMM31
3354  // ...
3355  // In order to get the offset of a given state, we need to execute CPUID.(EAX=0DH,ECX=x), where x is the desired
3356  // state (1 - XMM, 2 - YMM, 7 - ZMM, etc.)
3357 
3358  if (ND_SIZE_64BIT == Size)
3359  {
3360  // MMX register requested.
3361  if (Reg >= ND_MAX_MMX_REGS)
3362  {
3364  goto cleanup_and_exit;
3365  }
3366 
3367  if (Set)
3368  {
3369  memcpy(&xsave->Mm0 + Reg, Value->Value.QwordValues, ND_SIZE_64BIT);
3370  }
3371  else
3372  {
3373  memcpy(Value->Value.QwordValues, &xsave->Mm0 + Reg, ND_SIZE_64BIT);
3374  }
3375  }
3376  else
3377  {
3378  // SSE register requested. Copy the content, chunk by chunk.
3379  if (Reg >= ND_MAX_SSE_REGS)
3380  {
3382  goto cleanup_and_exit;
3383  }
3384 
3385  if (!gGuest.Guest64 && Reg >= 8)
3386  {
3387  // Outside 64-bit mode, only registers 0-7 can be accessed
3388  status = INT_STATUS_NOT_SUPPORTED;
3389  goto cleanup_and_exit;
3390  }
3391 
3392  // Handle XMM register access.
3393  if (ND_SIZE_128BIT <= Size)
3394  {
3395  if (Reg < 16)
3396  {
3397  // XMM0 - XMM15 accessed, we can copy the content directly.
3398  if (Set)
3399  {
3400  memcpy(&xsave->Xmm0 + Reg, Value->Value.QwordValues, ND_SIZE_128BIT);
3401  }
3402  else
3403  {
3404  memcpy(Value->Value.QwordValues, &xsave->Xmm0 + Reg, ND_SIZE_128BIT);
3405  }
3406  }
3407  else
3408  {
3409  // XMM16 - XMM31 accessed. Get the offset of the Hi_ZMM16 - Hi_ZMM31, that contains the XMM16 .. XMM31
3410  // registers, and copy the low 128 bit.
3411  __cpuidex(cpuidregs, 0xD, 0x7);
3412 
3413  if (0 != cpuidregs[1])
3414  {
3415  if (Set)
3416  {
3417  memcpy((PBYTE)xsave + cpuidregs[1] + (Reg - 16) * 64ull,
3418  Value->Value.QwordValues,
3419  ND_SIZE_128BIT);
3420  }
3421  else
3422  {
3423  memcpy(Value->Value.QwordValues,
3424  (PBYTE)xsave + cpuidregs[1] + (Reg - 16) * 64ull,
3425  ND_SIZE_128BIT);
3426  }
3427  }
3428  else
3429  {
3430  status = INT_STATUS_NOT_SUPPORTED;
3431  goto cleanup_and_exit;
3432  }
3433  }
3434  }
3435 
3436  // Handle YMM register access.
3437  if (ND_SIZE_256BIT <= Size)
3438  {
3439  if (Reg < 16)
3440  {
3441  // Get the offset of the YMM0_Hi - YMM15_Hi, that contains the high portion of the YMM registers.
3442  __cpuidex(cpuidregs, 0xD, 0x2);
3443 
3444  if (0 != cpuidregs[1])
3445  {
3446  if (Set)
3447  {
3448  memcpy((PBYTE)xsave + cpuidregs[1] + Reg * 16ull, Value->Value.ByteValues + 16, ND_SIZE_128BIT);
3449  }
3450  else
3451  {
3452  memcpy(Value->Value.ByteValues + 16, (PBYTE)xsave + cpuidregs[1] + Reg * 16ull, ND_SIZE_128BIT);
3453  }
3454  }
3455  else
3456  {
3457  status = INT_STATUS_NOT_SUPPORTED;
3458  goto cleanup_and_exit;
3459  }
3460  }
3461  else
3462  {
3463  // Get the offset of the Hi16_ZMM16 - Hi16_ZMM31, that contain the YMM16-YMM31 registers.
3464  __cpuidex(cpuidregs, 0xD, 0x7);
3465 
3466  if (0 != cpuidregs[1])
3467  {
3468  if (Set)
3469  {
3470  memcpy((PBYTE)xsave + cpuidregs[1] + (Reg - 16) * 64ull + 16,
3471  Value->Value.ByteValues + 16, ND_SIZE_128BIT);
3472  }
3473  else
3474  {
3475  memcpy(Value->Value.ByteValues + 16,
3476  (PBYTE)xsave + cpuidregs[1] + (Reg - 16) * 64ull + 16, ND_SIZE_128BIT);
3477  }
3478  }
3479  else
3480  {
3481  status = INT_STATUS_NOT_SUPPORTED;
3482  goto cleanup_and_exit;
3483  }
3484  }
3485  }
3486 
3487  // Handle ZMM register access.
3488  if (ND_SIZE_512BIT <= Size)
3489  {
3490  if (Reg < 16)
3491  {
3492  // Get the offset of the ZMM0_Hi - ZMM15_Hi, that contains the high part of ZMM0 - ZMM15 registers.
3493  __cpuidex(cpuidregs, 0xD, 0x6);
3494 
3495  if (0 != cpuidregs[1])
3496  {
3497  if (Set)
3498  {
3499  memcpy((PBYTE)xsave + cpuidregs[1] + Reg * 32ull, Value->Value.ByteValues + 32, ND_SIZE_256BIT);
3500  }
3501  else
3502  {
3503  memcpy(Value->Value.ByteValues + 32, (PBYTE)xsave + cpuidregs[1] + Reg * 32ull, ND_SIZE_256BIT);
3504  }
3505  }
3506  else
3507  {
3508  status = INT_STATUS_NOT_SUPPORTED;
3509  goto cleanup_and_exit;
3510  }
3511  }
3512  else
3513  {
3514  // Get the offset of the Hi16_ZMM, that contains the high ZMM registers.
3515  __cpuidex(cpuidregs, 0xD, 0x7);
3516 
3517  if (0 != cpuidregs[1])
3518  {
3519  if (Set)
3520  {
3521  memcpy((PBYTE)xsave + cpuidregs[1] + (Reg - 16) * 64ull + 32,
3522  Value->Value.ByteValues + 32, ND_SIZE_256BIT);
3523  }
3524  else
3525  {
3526  memcpy(Value->Value.ByteValues + 32,
3527  (PBYTE)xsave + cpuidregs[1] + (Reg - 16) * 64ull + 32, ND_SIZE_256BIT);
3528  }
3529  }
3530  else
3531  {
3532  status = INT_STATUS_NOT_SUPPORTED;
3533  goto cleanup_and_exit;
3534  }
3535  }
3536  }
3537  }
3538 
3539  status = INT_STATUS_SUCCESS;
3540 
3541  if (Commit)
3542  {
3543  status = IntSetXsaveArea(IG_CURRENT_VCPU, &xa);
3544  if (!INT_SUCCESS(status))
3545  {
3546  ERROR("IntSetXsaveArea failed: 0x%08x\n", status);
3547  }
3548  }
3549 
3550 cleanup_and_exit:
3551  if (xsave != XsaveArea)
3552  {
3553  IntFreeXsaveArea(xa);
3554  }
3555 
3556  return status;
3557 }
3558 
3559 
3560 INTSTATUS
3562  _In_opt_ PIG_XSAVE_AREA XsaveArea,
3563  _In_ DWORD Reg,
3564  _In_ DWORD Size,
3565  _Out_ OPERAND_VALUE *Value
3566  )
3580 {
3581  return IntDecGetSetSseRegValue(XsaveArea, Reg, Size, Value, FALSE, FALSE);
3582 }
3583 
3584 
3585 INTSTATUS
3587  _In_opt_ PIG_XSAVE_AREA XsaveArea,
3588  _In_ DWORD Reg,
3589  _In_ DWORD Size,
3590  _In_ OPERAND_VALUE *Value,
3591  _In_ BOOLEAN Commit
3592  )
3607 {
3608  return IntDecGetSetSseRegValue(XsaveArea, Reg, Size, Value, TRUE, Commit);
3609 }
3610 
3611 
3612 INTSTATUS
3614  _In_ QWORD Gla,
3615  _In_ QWORD Cr3,
3616  _In_ DWORD Flags
3617  )
3630 {
3631  INTSTATUS status;
3632  VA_TRANSLATION tr;
3633 
3634  status = IntTranslateVirtualAddressEx(Gla, Cr3, 0, &tr);
3635  if (!INT_SUCCESS(status))
3636  {
3637  ERROR("[ERROR] IntTranslateVirtualAddressEx failed: 0x%08x\n", status);
3638  return status;
3639  }
3640 
3641  // Bits 8:5 and 2:1 of the PDPTE are Reserved on x86 PAE.
3642  // Setting those will cause a #GP on a cr3 switch.
3643  for (DWORD i = (gGuest.PaeEnabled && !gGuest.Guest64) ? 1 : 0; i < tr.MappingsCount; i++)
3644  {
3645  PQWORD p;
3646 
3647  status = IntPhysMemMap(tr.MappingsTrace[i], 8, 0, &p);
3648  if (!INT_SUCCESS(status))
3649  {
3650  ERROR("[ERROR] IntPhysMemMap failed for 0x%016llx: 0x%08x\n", tr.MappingsTrace[i], status);
3651  return status;
3652  }
3653 
3654  if ((Flags & PW_FLAGS_SET_A) && (*p & PT_P))
3655  {
3656  *p |= PT_A;
3657  }
3658 
3659  if ((Flags & PW_FLAGS_SET_D) && ((i == tr.MappingsCount - 1) && (*p & PT_A) && (*p & PT_P) && (*p & PT_RW)))
3660  {
3661  *p |= PT_D;
3662  }
3663 
3664  IntPhysMemUnmap(&p);
3665  }
3666 
3667  return INT_STATUS_SUCCESS;
3668 }
3669 
3670 
3671 INTSTATUS
3673  _Out_ ND_OPERAND_SIZE *Maxvl
3674  )
3683 {
3684  QWORD xcr0;
3685  INTSTATUS status;
3686 
3687  status = IntGetXcr0(IG_CURRENT_VCPU, &xcr0);
3688  if (!INT_SUCCESS(status))
3689  {
3690  return status;
3691  }
3692 
3693  if (XCR0_AVX_512_STATE == (xcr0 & XCR0_AVX_512_STATE))
3694  {
3695  *Maxvl = ND_SIZE_512BIT;
3696  return INT_STATUS_SUCCESS;
3697  }
3698 
3699  if (0 != (xcr0 & XCR0_YMM_HI128))
3700  {
3701  *Maxvl = ND_SIZE_256BIT;
3702  return INT_STATUS_SUCCESS;
3703  }
3704 
3705  if (0 != (xcr0 & XCR0_SSE))
3706  {
3707  *Maxvl = ND_SIZE_128BIT;
3708  return INT_STATUS_SUCCESS;
3709  }
3710 
3711  return INT_STATUS_NOT_FOUND;
3712 }
QWORD Mm0[2]
Definition: glueiface.h:106
#define _In_opt_
Definition: intro_sal.h:16
#define INT_STATUS_PAGE_NOT_PRESENT
Indicates that a virtual address is not present.
Definition: introstatus.h:438
#define DEC_OPT_NO_CACHE
Flag used to hint the instruction decoder to not use the instruction cache.
Definition: decoder.h:30
_Bool BOOLEAN
Definition: intro_types.h:58
#define _Out_
Definition: intro_sal.h:22
#define CPU_EFLAGS_SF
Definition: processor.h:16
long long INT64
Definition: intro_types.h:45
INTSTATUS IntVirtMemUnmap(void **HostPtr)
Unmaps a memory range previously mapped with IntVirtMemMap.
Definition: introcore.c:2234
static INTSTATUS IntDecComputeVsibLinearAddresses(PINSTRUX Instrux, PND_OPERAND Operand, PIG_ARCH_REGS Registers, PIG_XSAVE_AREA XsaveArea, QWORD *LinearAddresses)
Decode VSIB addresses from the given instruction.
Definition: decoder.c:973
INTSTATUS IntDecEmulatePTWrite(QWORD *NewValue)
Emulate a page-table write.
Definition: decoder.c:2720
uint16_t * PWORD
Definition: intro_types.h:48
Describes an XSAVE area format.
Definition: glueiface.h:93
QWORD CsBase
Definition: glueiface.h:66
static int16_t _InterlockedCompareExchange16(int16_t volatile *Destination, int16_t Exchange, int16_t Comparand)
Definition: intrinsics.h:611
INTSTATUS IntGetXcr0(DWORD CpuNumber, QWORD *Xcr0Value)
Get the value of the guest XCR0 register.
Definition: introcpu.c:1030
LIX_TASK_OBJECT * IntLixTaskFindByCr3(QWORD Cr3)
Finds the Linux process having the provided Cr3.
Definition: lixprocess.c:942
BYTE ByteValues[ND_MAX_REGISTER_SIZE]
Definition: decoder.h:54
QWORD DsBase
Definition: glueiface.h:74
uint8_t BYTE
Definition: intro_types.h:47
INTSTATUS IntIcLookupInstruction(PINS_CACHE Cache, PINSTRUX Instrux, QWORD Gva, QWORD Cr3)
Lookup an instruction inside the cache.
Definition: icache.c:495
#define CR0_PG
Definition: processor.h:40
IG_ARCH_REGS Regs
The current state of the guest registers.
Definition: guests.h:95
DWORD Index
The VCPU number.
Definition: guests.h:172
#define _In_
Definition: intro_sal.h:21
#define XCR0_AVX_512_STATE
Definition: processor.h:80
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
INTSTATUS IntDecDecodeInstructionAtRipWithCache(void *Cache, DWORD CpuNumber, PIG_ARCH_REGS Registers, PINSTRUX Instrux, DWORD Options, BOOLEAN *CacheHit, BOOLEAN *Added)
Decode an instruction using the cache.
Definition: decoder.c:449
#define PAGE_REMAINING(addr)
Definition: pgtable.h:163
uint16_t WORD
Definition: intro_types.h:48
#define INT_STATUS_DISASM_ERROR
Indicates a decoder error.
Definition: introstatus.h:442
QWORD Xmm0[2]
Definition: glueiface.h:114
INTSTATUS IntGetGprs(DWORD CpuNumber, PIG_ARCH_REGS Regs)
Get the current guest GPR state.
Definition: introcpu.c:827
#define IS_ACCESS_IN_KERNEL_LIX(gla, size)
Checks if a memory access is done inside the Linux kernel virtual address space.
Definition: decoder.c:50
#define CPU_EFLAGS_PF
Definition: processor.h:13
Describes a memory address, as used in an instruction.
Definition: decoder.h:39
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
#define PAGE_OFFSET
Definition: pgtable.h:32
Holds segment register state.
Definition: glueiface.h:64
INTSTATUS IntDecDecodeDestinationLinearAddressFromInstruction(PINSTRUX Instrux, PIG_ARCH_REGS Registers, QWORD *LinearAddress)
Decode the destination memory linear address.
Definition: decoder.c:1202
#define ERROR(fmt,...)
Definition: glue.h:62
#define PAGE_COUNT_4K(addr, bytes)
Definition: pgtable.h:174
int INTSTATUS
The status data type.
Definition: introstatus.h:24
#define DEC_GET_FLAG(eflags, flag)
Gets the value of the indicated flag.
Definition: decoder.h:27
INTSTATUS IntDecEmulateRead(PINSTRUX Instrux, BYTE *SrcValueBuffer)
Emulate a read access.
Definition: decoder.c:1570
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
INTSTATUS IntQueryGuestInfo(DWORD InfoClass, void *InfoParam, void *Buffer, DWORD BufferLength)
Definition: glue.c:226
static INTSTATUS IntDecDecodeOperandSize(PINSTRUX Instrux, PND_OPERAND Operand, PIG_ARCH_REGS Registers, DWORD *AccessSize)
Decode the size of the given operand.
Definition: decoder.c:654
QWORD SsBase
Definition: glueiface.h:70
QWORD Flags
Definition: glueiface.h:49
#define IntFreeXsaveArea(xa)
Frees an XSAVE area.
Definition: introcpu.h:286
#define CPU_EFLAGS_CF
Definition: processor.h:11
QWORD SsAr
Definition: glueiface.h:73
#define CPU_EFLAGS_ZF
Definition: processor.h:15
#define PW_FLAGS_SET_D
Definition: decoder.h:130
INTRO_GUEST_TYPE OSType
The type of the guest.
Definition: guests.h:278
static INTSTATUS IntGetValueFromOperand(PINSTRUX Instrux, DWORD OperandIndex, PIG_ARCH_REGS Registers, PBYTE MemoryValue, OPERAND_VALUE *WrittenValue)
Get the value of an instruction operand.
Definition: decoder.c:1401
INSTRUX Instruction
The current instruction, pointed by the guest RIP.
Definition: guests.h:88
#define _Out_writes_(expr)
Definition: intro_sal.h:28
#define INT_STATUS_OPERATION_NOT_IMPLEMENTED
Definition: introstatus.h:125
INTSTATUS IntDecDecodeSourceLinearAddressFromInstruction(PINSTRUX Instrux, PIG_ARCH_REGS Registers, QWORD *LinearAddress)
Decode the source memory linear address.
Definition: decoder.c:1149
INTSTATUS IntDecGetSseRegValue(PIG_XSAVE_AREA XsaveArea, DWORD Reg, DWORD Size, OPERAND_VALUE *Value)
Get the value of a vector register. Wrapper over IntDecGetSetSseRegValue.
Definition: decoder.c:3561
INTSTATUS IntDecEmulatePageWalk(QWORD Gla, QWORD Cr3, DWORD Flags)
Definition: decoder.c:3613
32-bit selector.
Definition: glueiface.h:187
uint32_t * PDWORD
Definition: intro_types.h:49
DWORD AccessSize
The size of the memory access. Valid only for EPT exits.
Definition: guests.h:103
Logic operation.
Definition: decoder.c:56
IG_CS_TYPE
The type of the code segment.
Definition: glueiface.h:183
INTSTATUS IntDecComputeLinearAddress(PINSTRUX Instrux, PND_OPERAND Operand, PIG_ARCH_REGS Registers, QWORD *LinearAddress)
Given an instruction and a memory operand, it will compute the guest linear address encoded by that o...
Definition: decoder.c:790
#define XCR0_SSE
Definition: processor.h:67
INTSTATUS IntDecDecodeInstructionFromBuffer(PBYTE Buffer, size_t BufferSize, IG_CS_TYPE CsType, void *Instrux)
Decode an instruction from the provided buffer.
Definition: decoder.c:308
DWORD MappingsCount
The number of entries inside the MappingsTrace and MappingsEntries arrays.
Definition: introcore.h:123
INTSTATUS IntGetCurrentMode(DWORD CpuNumber, DWORD *Mode)
Read the current CS type.
Definition: introcpu.c:977
INTSTATUS IntGpaCacheRelease(PGPA_CACHE Cache, QWORD Gpa)
Release a previously used cached entry.
Definition: gpacache.c:678
#define PT_RW
Definition: pgtable.h:84
#define _Inout_
Definition: intro_sal.h:20
static int8_t _InterlockedCompareExchange8(int8_t volatile *Destination, int8_t Exchange, int8_t Comparand)
Definition: intrinsics.h:603
TIMER_FRIENDLY void IntDumpInstruction(INSTRUX *Instruction, QWORD Rip)
This function dumps a given instruction (textual disassembly).
Definition: dumper.c:583
#define CR0_PE
Definition: processor.h:30
INTSTATUS IntDecGetMaxvl(ND_OPERAND_SIZE *Maxvl)
Computes the maximum vector length, given the enabled states inside the XCR0 register.
Definition: decoder.c:3672
#define _Out_opt_
Definition: intro_sal.h:30
Describes an operand value.
Definition: decoder.h:50
#define IS_KERNEL_POINTER_LIX(p)
Definition: lixguest.h:11
QWORD QwordValues[ND_MAX_REGISTER_SIZE/8]
Definition: decoder.h:57
#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
uint8_t * PBYTE
Definition: intro_types.h:47
BOOLEAN PaeEnabled
True if Physical Address Extension is enabled.
Definition: guests.h:295
INTSTATUS IntDecSetSseRegValue(PIG_XSAVE_AREA XsaveArea, DWORD Reg, DWORD Size, OPERAND_VALUE *Value, BOOLEAN Commit)
Sets the value of a vector register. Wrapper over IntDecGetSetSseRegValue.
Definition: decoder.c:3586
__noreturn void IntBugCheck(void)
Definition: glue.c:917
#define INT_STATUS_UNSUCCESSFUL
Definition: introstatus.h:335
XSAVE area container.
Definition: introcpu.h:59
#define PT_P
Definition: pgtable.h:83
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:290
unsigned long long QWORD
Definition: intro_types.h:53
static QWORD IntDecAtomicStore(void *Address, DWORD Size, QWORD New, QWORD Old)
Atomically store a value in memory.
Definition: decoder.c:2680
void * GpaCache
The currently used GPA cache.
Definition: guests.h:403
#define TRUE
Definition: intro_types.h:30
#define INT_STATUS_INVALID_PARAMETER_4
Definition: introstatus.h:71
union _OPERAND_VALUE::@22 Value
The actual operand value.
#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 Gpa
The accessed guest physical address. Valid only for EPT exits.
Definition: guests.h:101
#define CPU_EFLAGS_OF
Definition: processor.h:20
#define TRACE(fmt,...)
Definition: glue.h:58
INTSTATUS IntDecDecodeAccessSize(PINSTRUX Instrux, PIG_ARCH_REGS Registers, QWORD Gla, BYTE AccessType, DWORD *AccessSize)
Decode the memory access size of a given instruction.
Definition: decoder.c:731
PWIN_PROCESS_OBJECT IntWinProcFindObjectByCr3(QWORD Cr3)
Finds a process by its kernel CR3.
Definition: winprocesshp.c:195
#define INT_STATUS_INVALID_PARAMETER_5
Definition: introstatus.h:74
QWORD FsBase
Definition: glueiface.h:82
static void __cpuidex(int32_t info[4], int32_t level, int32_t ecx)
Definition: intrinsics.h:270
INTSTATUS IntTranslateVirtualAddressEx(QWORD Gva, QWORD Cr3, DWORD Flags, VA_TRANSLATION *Translation)
Translates a guest virtual address to a guest physical address.
Definition: introcore.c:1863
INTSTATUS IntDecGetAccessedMem(PINSTRUX Instrux, PIG_ARCH_REGS Registers, PIG_XSAVE_AREA XsaveArea, MEMADDR *Gla, DWORD *Count)
Decode each accessed address by an instruction.
Definition: decoder.c:3160
#define PT_A
Definition: pgtable.h:88
#define WARNING(fmt,...)
Definition: glue.h:60
DWORD Size
The operand size.
Definition: decoder.h:60
#define PAGE_SIZE
Definition: common.h:70
INTSTATUS IntVirtMemSafeWrite(QWORD Cr3, QWORD VirtualAddress, DWORD Size, void *Buffer, DWORD Ring)
Safely modify guest memory.
Definition: kernvm.c:498
INTSTATUS IntSetXsaveArea(DWORD CpuNumber, XSAVE_AREA *XsaveArea)
Sets the contents of the guest XSAVE area.
Definition: introcpu.c:1097
enum @251 INT_FLAGS_MODE
Describes the flags affected by an instruction.
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
INTSTATUS IntDecDecodeInstructionAtRip(DWORD CpuNumber, IG_ARCH_REGS *Registers, IG_SEG_REGS *Segments, INSTRUX *Instrux)
Decode an instruction at current RIP on the provided VCPU.
Definition: decoder.c:384
#define INT_STATUS_DATA_BUFFER_TOO_SMALL
Definition: introstatus.h:194
#define __forceinline
Definition: introtypes.h:61
uint32_t DWORD
Definition: intro_types.h:49
#define PW_FLAGS_SET_A
Definition: decoder.h:129
Addition.
Definition: decoder.c:58
#define _In_reads_bytes_(expr)
Definition: intro_sal.h:25
QWORD GsBase
Definition: glueiface.h:86
DWORD DwordValues[ND_MAX_REGISTER_SIZE/4]
Definition: decoder.h:56
INTSTATUS IntSetGprs(DWORD CpuNumber, PIG_ARCH_REGS Regs)
Sets the values of the guest GPRs.
Definition: introcpu.c:905
__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
INTSTATUS IntDecDecodeInstruction(IG_CS_TYPE CsType, QWORD Gva, void *Instrux)
Decode an instruction from the provided guest linear address.
Definition: decoder.c:180
INTSTATUS IntDecGetAccessedMemCount(PINSTRUX Instrux, DWORD *Count)
Decode the number of memory locations accessed by an instruction.
Definition: decoder.c:3110
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
#define XCR0_YMM_HI128
Definition: processor.h:68
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 _When_(expr, arg)
Definition: intro_sal.h:26
static int64_t _InterlockedCompareExchange64(int64_t volatile *Destination, int64_t Exchange, int64_t Comparand)
Definition: intrinsics.h:627
INTSTATUS IntIcAddInstruction(PINS_CACHE Cache, PINSTRUX Instruction, QWORD Gva, QWORD Cr3, BOOLEAN Global)
Adds an instruction to the cache.
Definition: icache.c:952
#define PT_D
Definition: pgtable.h:89
Get the size of the guest XSAVE area for a VCPU.
Definition: glueiface.h:260
QWORD MappingsTrace[MAX_TRANSLATION_DEPTH]
Contains the physical address of each entry within the translation tables.
Definition: introcore.h:111
INTSTATUS IntGpaCacheFindAndAdd(PGPA_CACHE Cache, QWORD Gpa, void **Hva)
Search for an entry in the GPA cache, and add it, if it wasn&#39;t found.
Definition: gpacache.c:451
#define INT_STATUS_NO_MAPPING_STRUCTURES
Indicates that not all mapping structures of a virtual address are present.
Definition: introstatus.h:434
#define GET_SIGN(sz, x)
Get the sign bit of a value.
Definition: decoder.c:67
__must_check INTSTATUS IntPhysMemMap(QWORD PhysAddress, DWORD Length, DWORD Flags, void **HostPtr)
Maps a guest physical address inside Introcore VA space.
Definition: glue.c:338
Encapsulates information about a virtual to physical memory translation.
Definition: introcore.h:102
#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
IG_XSAVE_AREA * XsaveArea
The contents of the XSAVE area.
Definition: introcpu.h:62
64-bit selector.
Definition: glueiface.h:188
static INTSTATUS IntSetValueForOperand(PINSTRUX Instrux, DWORD OperandIndex, PIG_ARCH_REGS Registers, OPERAND_VALUE *OpValue, BOOLEAN Commit)
Set the value of an instruction operand.
Definition: decoder.c:1255
Holds register state.
Definition: glueiface.h:30
INTSTATUS IntGetSegs(DWORD CpuNumber, PIG_SEG_REGS Regs)
Read the guest segment registers.
Definition: introcpu.c:995
INTSTATUS IntGetCurrentRing(DWORD CpuNumber, DWORD *Ring)
Read the current protection level.
Definition: introcpu.c:959
#define _Out_writes_to_(expr, expr2)
Definition: intro_sal.h:29
unsigned long long * PQWORD
Definition: intro_types.h:53
static INTSTATUS IntDecGetSetSseRegValue(PIG_XSAVE_AREA XsaveArea, DWORD Reg, DWORD Size, OPERAND_VALUE *Value, BOOLEAN Set, BOOLEAN Commit)
Gets or sets the value of a vector register.
Definition: decoder.c:3291
Subtraction.
Definition: decoder.c:57
#define IS_ACCESS_IN_KERNEL_WIN(is64, gla, size)
Checks if a memory access is done inside the Windows kernel virtual address space.
Definition: decoder.c:41
INTSTATUS IntPhysMemUnmap(void **HostPtr)
Unmaps an address previously mapped with IntPhysMemMap.
Definition: glue.c:396
#define PAGE_MASK
Definition: pgtable.h:35
#define PAGE_SIZE_4K
Definition: pgtable.h:10
QWORD EsBase
Definition: glueiface.h:78
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
QWORD Gla
The accessed guest virtual address. Valid only for EPT exits.
Definition: guests.h:102
static void IntDecSetFlags(QWORD Dst, QWORD Src1, QWORD Src2, DWORD Size, PIG_ARCH_REGS Regs, DWORD FlagsMode)
Sets the flags according to the result of an operation.
Definition: decoder.c:73
16-bit selector.
Definition: glueiface.h:186
#define DEC_EFLAG_CF
Definition: decoder.h:24
static int32_t _InterlockedCompareExchange(int32_t volatile *Destination, int32_t Exchange, int32_t Comparand)
Definition: intrinsics.h:619
INTSTATUS IntGetXsaveArea(DWORD CpuNumber, XSAVE_AREA *XsaveArea)
Get the contents of the guest XSAVE area.
Definition: introcpu.c:1048
INTSTATUS IntDecGetWrittenValueFromInstruction(PINSTRUX Instrux, PIG_ARCH_REGS Registers, PBYTE MemoryValue, OPERAND_VALUE *WrittenValue)
Decode a written value from a memory write instruction.
Definition: decoder.c:1861
size_t SIZE_T
Definition: intro_types.h:60
#define FALSE
Definition: intro_types.h:34
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68
INTSTATUS IntDecEmulateInstruction(DWORD CpuNumber, PINSTRUX Instrux)
Emulate a MOV or a PUSH instruction.
Definition: decoder.c:2592