Bitdefender Hypervisor Memory Introspection
winpower.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "winpower.h"
6 #include "guests.h"
7 #include "ptfilter.h"
8 #include "vecore.h"
9 #include "winagent.h"
10 
11 
39 
40 
41 static INTRO_POWER_STATE
43  _In_ DWORD GuestPowerAction,
44  _In_ DWORD GuestPowerState
45  )
55 {
56  // Shutdown: action is "shutdown" or "shutdown off" and state different from hibernate (can be sleep)
57  if ((GuestPowerAction == PowerActionShutdown ||
58  GuestPowerAction == PowerActionShutdownOff) &&
59  GuestPowerState != PowerSystemHibernate)
60  {
61  return intPowStateShutdown;
62  }
63 
64  // Reset: power action is reset (we don't care about requested power state, as there is not an edge case
65  // like "pressing restart" but system enters on hibernate)
66  if (GuestPowerAction == PowerActionShutdownReset)
67  {
68  return intPowStateReboot;
69  }
70 
71  // Sleep: action must be sleep and the requested power state must be sleep
72  if (GuestPowerAction == PowerActionSleep &&
73  GuestPowerState >= PowerSystemSleeping1 &&
74  GuestPowerState <= PowerSystemSleeping3)
75  {
76  return intPowStateSleeping;
77  }
78 
79  // Hibernate: action can be hibernate/shutdown/shutdown off and the requested power state must be hibernate
80  if ((GuestPowerAction == PowerActionHibernate ||
81  GuestPowerAction == PowerActionShutdown ||
82  GuestPowerAction == PowerActionShutdownOff ||
83  GuestPowerAction == PowerActionSleep) &&
84  GuestPowerState == PowerSystemHibernate)
85  {
86  return intPowStateHibernate;
87  }
88 
89  WARNING("[WARNING] Unknown power action/reason: %d %d!\n", GuestPowerAction, GuestPowerState);
90 
91  return intPowStateUnknown;
92 }
93 
94 
95 static INTSTATUS
97  _Out_ DWORD *RequestedPowerAction,
98  _Out_ DWORD *RequestedPowerState
99  )
110 {
111  if (gGuest.Guest64)
112  {
113  *RequestedPowerState = (DWORD)gVcpu->Regs.Rdx;
114  *RequestedPowerAction = (DWORD)gVcpu->Regs.Rcx;
115  return INT_STATUS_SUCCESS;
116  }
117  else
118  {
119  DWORD buffer[2];
120  INTSTATUS status;
121 
122  status = IntKernVirtMemRead(gVcpu->Regs.Rsp + 0x4, 8, buffer, NULL);
123  if (!INT_SUCCESS(status))
124  {
125  return status;
126  }
127 
128  *RequestedPowerAction = buffer[0];
129  *RequestedPowerState = buffer[1];
130 
131  return INT_STATUS_SUCCESS;
132  }
133 }
134 
135 
136 INTSTATUS
138  void
139  )
146 
147 {
148  INTSTATUS status = INT_STATUS_SUCCESS;
149  BYTE hkbuff[4] = { 0xf3, 0x90, 0xeb, 0xfc };
150 
151  LOG("[POW-SPIN-WAIT] IntWinPowEnableSpinWait called!\n");
152 
153  IntPauseVcpus();
154 
155  // 0x6 is the address after the hypercall, where the spinwait is taking place
156  // if the handler is ever changed, this must also be changed!
157  status = IntDetModifyPublicData(detTagPowerState, hkbuff, sizeof(hkbuff), "spinwait");
158  if (!INT_SUCCESS(status))
159  {
160  ERROR("[ERROR] IntDetModifyPublicData failed: 0x%08x\n", status);
161  }
162 
163  IntResumeVcpus();
164 
165  return status;
166 }
167 
168 
169 INTSTATUS
171  void
172  )
178 {
179  INTSTATUS status;
180  BYTE nopbuff[4] = { 0x66, 0x90, 0x66, 0x90 };
181 
182  LOG("[POW-SPIN-WAIT] IntWinPowDisableSpinWait called!\n");
183 
184  IntPauseVcpus();
185 
186  // 0x6 is the address after the hypercall, where the spinwait is taking place
187  // if the handler is ever changed, this must also be changed!
188  status = IntDetModifyPublicData(detTagPowerState, nopbuff, sizeof(nopbuff), "spinwait");
189  if (!INT_SUCCESS(status))
190  {
191  ERROR("[ERROR] IntDetModifyPublicData failed: 0x%08x\n", status);
192  }
193 
194  IntResumeVcpus();
195 
196  return status;
197 }
198 
199 
200 static INTSTATUS
202  _In_ INTRO_POWER_STATE PowerState
203  )
215 {
216  // Init to not needed hint, so we don't call IntWinPowEnableSpinWait in case INTRO_OPT_IN_GUEST_PT_FILTER
217  // or INTRO_OPT_VE is not set
219 
220  if (PowerState == intPowStateUnknown)
221  {
223  }
224 
225  // INTRO_OPT_IN_GUEST_PT_FILTER and INTRO_OPT_VE are mutually exclusive (this is enforced in IntNewGuestNotification
226  // and IntGuestUpdateCoreOptions
228  {
229  LOG("Removing the PT Filter due to power state change...\n");
231  }
233  {
234  LOG("Removing the #VE Agent due to power state change...\n");
236  }
237 
238  if (!INT_SUCCESS(status))
239  {
240  ERROR("[ERROR] IntPtiRemovePtFilter/IntVeRemoveAgent failed: 0x%08x (options: %016llx)\n",
241  status, gGuest.CoreOptions.Current);
242  }
243  else if (INT_STATUS_NOT_NEEDED_HINT != status)
244  {
245  // if the unloader was deployed then enable the spinwait
246  status = IntWinPowEnableSpinWait();
247  if (!INT_SUCCESS(status))
248  {
249  ERROR("[ERROR] IntWinPowEnableSpinWait failed: 0x%08x\n", status);
250  }
251  }
252 
253  return status;
254 }
255 
256 
257 static void
259  void
260  )
264 {
265  // Set this on TRUE because in this way we'll block other APIs
267 
269 }
270 
271 
272 INTSTATUS
274  _In_ void *Detour
275  )
286 {
287  INTSTATUS status = INT_STATUS_SUCCESS;
288  DWORD requestedPowerState;
289  DWORD requestedPowerAction;
290  INTRO_POWER_STATE internalPowerState;
291 
292  UNREFERENCED_PARAMETER(Detour);
293 
294  status = IntWinPowGetRequestedPowerState(&requestedPowerAction, &requestedPowerState);
295  if (!INT_SUCCESS(status))
296  {
297  ERROR("[ERROR] We could not get the requested power state!");
298  return status;
299  }
300 
301  LOG("[POWER-STATE] Entering power state %d, action %d\n", requestedPowerState, requestedPowerAction);
302 
303  // For power-state >= sleep, we should disable the PT filter as the following might happen inside the guest:
304  // 1. Guest is hybrid sleeping -> it will unmap the PT filter agent, so a BSOD will occur
305  // 2. Guest is hibernating -> the agent will remain inside the guest and a BSOD will most likely occur at resume
306  // also, the name of the patched processes must be restored
307 
308  internalPowerState = IntWinPowFromGuestToIntroPowState(requestedPowerAction, requestedPowerState);
309 
310  // First do the common handling, then handle more specifically depending on which power state guest tries to get in
311  status = IntWinPowHandleEventCommon(internalPowerState);
312  if (!INT_SUCCESS(status))
313  {
314  ERROR("[ERROR] IntWinPowHandleEventCommon failed: 0x%08x\n", status);
315  }
316 
317  LOG("[POWER-STATE] Internal power state: %d\n", internalPowerState);
318 
319  switch (internalPowerState)
320  {
321  case intPowStateSleeping:
322  case intPowStateShutdown:
323  case intPowStateReboot:
324  case intPowStateUnknown:
325  {
326  // We have nothing to do for now
327  break;
328  }
330  {
332 
333  break;
334  }
335  default:
336  {
337  ERROR("[ERROR] Power state %d requested, but we don't have any callback for it!\n", requestedPowerState);
338  return INT_STATUS_NOT_FOUND;
339  }
340  }
341 
342  return INT_STATUS_SUCCESS;
343 }
#define _Out_
Definition: intro_sal.h:22
INTSTATUS IntWinPowHandlePowerStateChange(void *Detour)
Detour callback which is called whenever NtSetSystemPowerState is called, resulting in a hypercall to...
Definition: winpower.c:273
#define INTRO_OPT_VE
Enable the Virtualization exception page table access pre-filtering agent (64-bit Windows only)...
Definition: intro_types.h:475
INTSTATUS IntWinPowEnableSpinWait(void)
This function is called in order to re-enable spin waiting in the handler after it was previously dis...
Definition: winpower.c:137
uint8_t BYTE
Definition: intro_types.h:47
IG_ARCH_REGS Regs
The current state of the guest registers.
Definition: guests.h:95
#define _In_
Definition: intro_sal.h:21
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
static INTRO_POWER_STATE IntWinPowFromGuestToIntroPowState(DWORD GuestPowerAction, DWORD GuestPowerState)
Converts in-guest parameters given to NtSetSystemPowerState to an internal introspection used power s...
Definition: winpower.c:42
INTSTATUS IntWinPowDisableSpinWait(void)
This function is called in order to disable spin waiting after everything we needed to be unloaded wa...
Definition: winpower.c:170
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
INTSTATUS IntResumeVcpus(void)
Resumes the VCPUs previously paused with IntPauseVcpus.
Definition: introcore.c:2355
The guest is about to enter hibernate (S4).
Definition: winpower.h:18
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
int INTSTATUS
The status data type.
Definition: introstatus.h:24
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
The guest is about to enter a sleep state (S1, S2, S3).
Definition: winpower.h:17
INTSTATUS IntPauseVcpus(void)
Pauses all the guest VCPUs.
Definition: introcore.c:2320
#define LOG(fmt,...)
Definition: glue.h:61
#define AG_OPT_INJECT_ON_RIP_POWSTATE_CHANGE
Definition: winagent.h:17
static INTSTATUS IntWinPowGetRequestedPowerState(DWORD *RequestedPowerAction, DWORD *RequestedPowerState)
Gets the parameters of NtSetSystemPowerState depending on OS architecture.
Definition: winpower.c:96
INTSTATUS IntDetModifyPublicData(DETOUR_TAG Tag, void const *Data, DWORD DataSize, char const *PublicDataName)
Modifies public parts of a detour handler.
Definition: detours.c:1751
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:290
QWORD Current
The currently used options.
Definition: guests.h:236
#define TRUE
Definition: intro_types.h:30
INTSTATUS IntVeRemoveAgent(DWORD AgOpts)
Removes the VE agent from guest memory.
Definition: vecore.c:2116
The state is not among the known combinations or it is unused by the introspection engine...
Definition: winpower.h:16
#define WARNING(fmt,...)
Definition: glue.h:60
static void IntWinPowHandleHibernateEvent(void)
Callback called when the change of guest power state to hibernate occurs.
Definition: winpower.c:258
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
The guest is about to reboot.
Definition: winpower.h:19
uint32_t DWORD
Definition: intro_types.h:49
enum _INTRO_POWER_STATE INTRO_POWER_STATE
Detected guest power states.
BOOLEAN EnterHibernate
True if the guest is entering into hibernate.
Definition: guests.h:319
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
static INTSTATUS IntWinPowHandleEventCommon(INTRO_POWER_STATE PowerState)
This function will be called on any power state change event. Everything that we want to uninit on ev...
Definition: winpower.c:201
INTSTATUS IntPtiRemovePtFilter(DWORD AgOpts)
Removes the PT filter.
Definition: ptfilter.c:1736
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:59
void IntGuestUpdateCoreOptions(QWORD NewOptions)
Updates Introcore options.
Definition: guests.c:1426
#define INTRO_OPT_IN_GUEST_PT_FILTER
Enable in-guest page-table filtering (64-bit Windows only).
Definition: intro_types.h:461
INTRO_PROT_OPTIONS CoreOptions
The activation and protection options for this guest.
Definition: guests.h:271