Bitdefender Hypervisor Memory Introspection
shellcode.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "shellcode.h"
6 #include "introcpu.h"
7 #include "guests.h"
8 
9 
10 static void
12  _In_ PCHAR Data
13  )
19 {
20  LOG("%s", Data);
21 }
22 
23 
26  _In_ QWORD Gva,
27  _In_ QWORD Gpa,
28  _In_ DWORD CsType,
29  _In_ IG_ARCH_REGS *Registers,
30  _Out_ QWORD *ShellcodeFlags
31  )
48 
61 {
62  INTSTATUS status;
63  PSHEMU_CONTEXT ctx;
64  SHEMU_STATUS shstatus;
65  IG_SEG_REGS segRegisters = { 0 };
66  DWORD csType;
67 
68  if (0 == Gva)
69  {
71  }
72 
73  if (0 == Gpa)
74  {
76  }
77 
78  if (NULL == Registers)
79  {
81  }
82 
83  if (NULL == ShellcodeFlags)
84  {
86  }
87 
88  // shstatus is not used for release builds as it is inside a TRACE, not a LOG
89  UNREFERENCED_PARAMETER(shstatus);
90 
91  ctx = &gGuest.Shemucontext;
92  memzero(ctx, sizeof(*ctx));
93  *ShellcodeFlags = 0;
94 
95  // Fill in the context.
96  ctx->Shellcode = gGuest.ShemuShellcode;
97 
98  ctx->Stack = gGuest.ShemuStack;
99  ctx->StackSize = SHEMU_STACK_SIZE;
100  ctx->StackBase = (Registers->Rsp & PAGE_MASK) - PAGE_SIZE;
101  memzero(ctx->Stack, ctx->StackSize);
102 
103  ctx->Intbuf = gGuest.ShemuInternal;
104  ctx->IntbufSize = SHEMU_SHELLCODE_SIZE + SHEMU_STACK_SIZE;
105  memzero(ctx->Intbuf, ctx->IntbufSize);
106 
107  if ((Registers->Rip & PAGE_OFFSET) >= 0xC00)
108  {
109  DWORD sizeread = 0;
110 
111  // The RIP is in the last 1K of the page, we will read the next page as well.
112  status = IntVirtMemRead(Registers->Rip & PAGE_MASK, SHEMU_SHELLCODE_SIZE,
113  Registers->Cr3, ctx->Shellcode, &sizeread);
114  if (!INT_SUCCESS(status) && (sizeread == 0))
115  {
116  ERROR("[ERROR] IntVirtMemRead shellcode failed for 0x%016llx : 0x%08x\n", Registers->Rip, status);
117  return status;
118  }
119 
120  ctx->ShellcodeSize = sizeread;
121  }
122  else
123  {
124  status = IntPhysicalMemRead(Gpa & PHYS_PAGE_MASK, PAGE_SIZE, ctx->Shellcode, NULL);
125  if (!INT_SUCCESS(status))
126  {
127  ERROR("[ERROR] IntPhysicalMemRead shellcode failed for 0x%016llx : 0x%08x\n",
128  Registers->Rip & PAGE_MASK, status);
129  return status;
130  }
131 
132  ctx->ShellcodeSize = 0x1000;
133  }
134 
135  ctx->ShellcodeBase = Registers->Rip & PAGE_MASK;
136 
137  // Read the stack. We don't care if this fails, we can still emulate the buffer with a NULL stack.
138  // Note: we don't really need the stack - we can determine whether the code is malicious without it.
139  // Anyways, the stack may be read successfully sometimes, and with error some other times.
141 
142  // 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).
143  if (CsType != IG_CS_TYPE_INVALID)
144  {
145  csType = CsType;
146  }
147  else
148  {
149  status = IntGetCurrentMode(IG_CURRENT_VCPU, &csType);
150  if (!INT_SUCCESS(status))
151  {
152  ERROR("[ERROR] IntGetCurrentMode failed: 0x%08x\n", status);
153  return status;
154  }
155  }
156 
157  ctx->Registers.RegCr0 = Registers->Cr0;
158  ctx->Registers.RegCr2 = Registers->Cr2;
159  ctx->Registers.RegCr3 = Registers->Cr3;
160  ctx->Registers.RegCr4 = Registers->Cr4;
161 
162  ctx->Registers.RegFlags = Registers->Flags;
163  ctx->Registers.RegRip = Registers->Rip;
164  ctx->Registers.RegRsp = Registers->Rsp;
165 
166  // Copy the general purpose registers.
167  ctx->Registers.RegRax = Registers->Rax;
168  ctx->Registers.RegRcx = Registers->Rcx;
169  ctx->Registers.RegRdx = Registers->Rdx;
170  ctx->Registers.RegRbx = Registers->Rbx;
171  ctx->Registers.RegRbp = Registers->Rbp;
172  ctx->Registers.RegRsi = Registers->Rsi;
173  ctx->Registers.RegRdi = Registers->Rdi;
174  ctx->Registers.RegR8 = Registers->R8;
175  ctx->Registers.RegR9 = Registers->R9;
176  ctx->Registers.RegR10 = Registers->R10;
177  ctx->Registers.RegR11 = Registers->R11;
178  ctx->Registers.RegR12 = Registers->R12;
179  ctx->Registers.RegR13 = Registers->R13;
180  ctx->Registers.RegR14 = Registers->R14;
181  ctx->Registers.RegR15 = Registers->R15;
182 
183  // We don't need segment registers on Linux.
185  {
186  if (csType == IG_CS_TYPE_64B)
187  {
188  ctx->Segments.Cs.Selector = 0x33;
189  ctx->Segments.Ds.Selector = 0x2b;
190  ctx->Segments.Es.Selector = 0x2b;
191  ctx->Segments.Ss.Selector = 0x2b;
192  ctx->Segments.Fs.Selector = 0x2b;
193  ctx->Segments.Gs.Selector = 0x53;
194 
195  ctx->Segments.Fs.Base = 0;
196 
197  status = IntGsRead(IG_CURRENT_VCPU, &ctx->Segments.Gs.Base);
198  if (!INT_SUCCESS(status))
199  {
200  WARNING("[WARNING] IntGsRead failed: 0x%08x\n", status);
201  ctx->Segments.Gs.Base = 0xBDBD0000;
202  }
203 
204  ctx->TibBase = ctx->Segments.Gs.Base;
205  }
206  else if (csType == IG_CS_TYPE_32B)
207  {
208  status = IntGetSegs(IG_CURRENT_VCPU, &segRegisters);
209  if (!INT_SUCCESS(status))
210  {
211  ERROR("[ERROR] IntGetSegs failed: 0x%08x\n", status);
212  return status;
213  }
214 
215  ctx->Segments.Cs.Selector = segRegisters.CsSelector;
216  ctx->Segments.Ds.Selector = segRegisters.DsSelector;
217  ctx->Segments.Es.Selector = segRegisters.EsSelector;
218  ctx->Segments.Ss.Selector = segRegisters.SsSelector;
219  ctx->Segments.Fs.Selector = segRegisters.FsSelector;
220  ctx->Segments.Gs.Selector = segRegisters.GsSelector;
221  ctx->Segments.Fs.Base = segRegisters.FsBase;
222  ctx->Segments.Gs.Base = segRegisters.GsBase;
223 
224  ctx->TibBase = ctx->Segments.Fs.Base;
225  }
226  else
227  {
228  ERROR("[ERROR] We don't support 16 bit!\n");
229  }
230  }
231 
232  ctx->Mode = (csType == IG_CS_TYPE_64B ? ND_CODE_64 : (csType == IG_CS_TYPE_32B ? ND_CODE_32 : ND_CODE_16));
233  ctx->Ring = 3; // We only support user-mode shellcode emulation here.
234  ctx->MaxInstructionsCount = SHEMU_MAX_INSTRUCTIONS; // We bail out after 256 instructions.
235  // Since we support beta/feedback per shemu flags, we cannot use SHEMU_FLAG_STOP_ON_EXPLOIT; If we do use it,
236  // we may end up in a situation where the first set flag, which causes shemu to stop emulation, is marked
237  // as feedback only. This way, the detection would be skipped, even if by emulating some more instructions we
238  // would get other flags set, which are not feedback only.
239  ctx->Options = 0;
240  ctx->Log = Shemuprint;
241 
242  // Use the default thresholds.
243  ctx->NopThreshold = SHEMU_DEFAULT_NOP_THRESHOLD;
244  ctx->StrThreshold = SHEMU_DEFAULT_STR_THRESHOLD;
245  ctx->MemThreshold = SHEMU_DEFAULT_MEM_THRESHOLD;
246 
247  shstatus = ShemuEmulate(ctx);
248 
250  {
251  // On Linux we must drop the TIB access flag, it's not relevant
252  ctx->Flags &= ~SHEMU_FLAG_TIB_ACCESS;
253  }
254 
255  if (ctx->Flags != 0)
256  {
257  TRACE("[SHELLCODE] Emulation terminated with status 0x%08x, flags: 0x%lx, %d NOPs, emulated %d instructions, "
258  "RIP %lx.\n", shstatus, ctx->Flags, ctx->NopCount, ctx->InstructionsCount, ctx->Registers.RegRip);
259  }
260 
261  // Make sure we clear flags that must always be cleared.
262  *ShellcodeFlags = ctx->Flags & ~gGuest.ShemuOptions.ForceOff;
263 
264  return INT_STATUS_SUCCESS;
265 }
266 
267 
#define _Out_
Definition: intro_sal.h:22
INTSTATUS IntPhysicalMemRead(QWORD PhysicalAddress, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest physical memory range, but only for a single page.
Definition: introcore.c:721
QWORD EsSelector
Definition: glueiface.h:80
SHEMU_CONTEXT Shemucontext
Shellcode emulator context.
Definition: guests.h:406
#define _In_
Definition: intro_sal.h:21
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
QWORD DsSelector
Definition: glueiface.h:76
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
BYTE ShemuInternal[SHEMU_SHELLCODE_SIZE+SHEMU_STACK_SIZE]
The shellcode emulator internal buffer.
Definition: guests.h:413
#define SHEMU_SHELLCODE_SIZE
The shell code buffer size. It should be at least 2 pages in size.
Definition: guests.h:72
#define PAGE_OFFSET
Definition: pgtable.h:32
Holds segment register state.
Definition: glueiface.h:64
#define ERROR(fmt,...)
Definition: glue.h:62
#define PHYS_PAGE_MASK
Definition: pgtable.h:38
int INTSTATUS
The status data type.
Definition: introstatus.h:24
QWORD GsSelector
Definition: glueiface.h:88
INTRO_GUEST_TYPE OSType
The type of the guest.
Definition: guests.h:278
#define LOG(fmt,...)
Definition: glue.h:61
32-bit selector.
Definition: glueiface.h:187
QWORD SsSelector
Definition: glueiface.h:72
BYTE ShemuShellcode[SHEMU_SHELLCODE_SIZE]
The shellcode emulator shellcode buffer.
Definition: guests.h:409
INTSTATUS IntGetCurrentMode(DWORD CpuNumber, DWORD *Mode)
Read the current CS type.
Definition: introcpu.c:977
#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
#define memzero(a, s)
Definition: introcrt.h:35
unsigned long long QWORD
Definition: intro_types.h:53
#define INT_STATUS_INVALID_PARAMETER_4
Definition: introstatus.h:71
#define TRACE(fmt,...)
Definition: glue.h:58
INTSTATUS IntGsRead(DWORD CpuNumber, QWORD *GsValue)
Reads the IA32_GS_BASE guest MSR.
Definition: introcpu.c:289
INTSTATUS IntShcIsSuspiciousCode(QWORD Gva, QWORD Gpa, DWORD CsType, IG_ARCH_REGS *Registers, QWORD *ShellcodeFlags)
Checks if the code located at the given guest virtual address is suspicious or not.
Definition: shellcode.c:25
QWORD FsBase
Definition: glueiface.h:82
static void Shemuprint(PCHAR Data)
Log data.
Definition: shellcode.c:11
BYTE ShemuStack[SHEMU_STACK_SIZE]
The shellcode emulator stack buffer.
Definition: guests.h:411
char * PCHAR
Definition: intro_types.h:56
#define WARNING(fmt,...)
Definition: glue.h:60
QWORD ForceOff
Options that are forcibly disabled.
Definition: guests.h:242
#define PAGE_SIZE
Definition: common.h:70
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
QWORD CsSelector
Definition: glueiface.h:68
uint32_t DWORD
Definition: intro_types.h:49
#define SHEMU_STACK_SIZE
The size of the stack buffer used by shemu.
Definition: guests.h:73
QWORD GsBase
Definition: glueiface.h:86
#define SHEMU_MAX_INSTRUCTIONS
The maximum instructions to be emulated by shemu.
Definition: guests.h:74
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
QWORD FsSelector
Definition: glueiface.h:84
INTSTATUS IntVirtMemRead(QWORD Gva, DWORD Length, QWORD Cr3, void *Buffer, DWORD *RetLength)
Reads data from a guest virtual memory range.
Definition: introcore.c:627
INTRO_PROT_OPTIONS ShemuOptions
Flags which describe the way shemu will give detections.
Definition: guests.h:272
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
64-bit selector.
Definition: glueiface.h:188
Holds register state.
Definition: glueiface.h:30
INTSTATUS IntGetSegs(DWORD CpuNumber, PIG_SEG_REGS Regs)
Read the guest segment registers.
Definition: introcpu.c:995
Invalid selector.
Definition: glueiface.h:185
#define PAGE_MASK
Definition: pgtable.h:35
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68