Skip to content

Latest commit

 

History

History

IoExternalDmaUnblock

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

IoExternalDmaUnblock

Description

The callback object is created inside ntoskrnl.exe. The root of function tree that frames actions on this callback object is PiCslInitialize function, where PipCslConsoleLockState is initialized to 0 and PipCslCreateCallback is called in order to create the callback object (symbol PipCslCallbackObject). After the object is successfully created, the callback function PipCslStateChangeCallback is registered. The goal of this callback is to check if PipCslInitialized was initialized (not 0), otherwise the system bugchecks (code 0xCA), then call PipCslUpdateState with argument value 1 or 2. The latter call compares the value of PipCslConsoleLockState with the argument value and if the values are different and the argument value is 1 - PipCslUnlockCallback is called. An outline of what was mentioned:

NTSTATUS __stdcall PiCslInitialize()
{
  NTSTATUS v0;

  PipCslConsoleLockState = 0;
  v0 = PipCslCreateCallback();
  if ( v0 >= 0 )
  {
    ExRegisterCallback((PCALLBACK_OBJECT)PipCslCallbackObject, (PCALLBACK_FUNCTION)PipCslStateChangeCallback, 0i64);
    PipCslInitialized = 1;
  }
  return v0;
}

What is PipCslUnlockCallback doing? Skimming the references we observe that the only write to this global function pointer is inside PipDmgInitPhaseOne function and the written value is the address of PipDmgConsoleUnlockCallback function. It is important to mention that the initialization happens only when PipDmaGuardPolicy is not 0. As described on Policy CSP - DmaGuard, the value 0 stands for most restrictive policy, so the callback is going to be initialized in other cases, like when the policy is 1 or 2 (there are other undocumented values, like 3). These primitives seem to be related to Kernel DMA Protection. In the end, PipDmgConsoleUnlockCallback is responsible for calling PipDmgFlushQueueAndRestartDevices function when PipDmaGuardPolicy is equal to 2.

Callback object notifications are sent from ntoskrnl's NtPowerInformation, when InformationLevel argument has value 0x5F, through calling PnpWinlogonExternalDmaNotification function. Argument1 parameter of ExNotifyCallback is NtPowerInformation's second parameter named InputBuffer and Argument2 is always set to 0.

There's another callback function registered inside pci.sys by function PciCreateConsoleLockCallback. The callback function name is PciConsoleLockCallback which is a wrapper for PciSetConsoleState. Inside the wrapper, first argument value (parameter Argument1) is checked if 0 / NULL and if not then the argument to later call is value 1 otherwise 2. PciSetConsoleState goal is to iterate over every bus in every PCI segment and verify if the bus is affected by console lock (a - PciBusAffectedByConsoleLock is called) and should be disabled (b - PciBusShouldBeDisabledByConsoleLock is called). If only (a) is satisfied then IoInvalidateDeviceRelations is called. It is important to note that all of the above actions are done only when PciConsoleState global variable value is different than the value of the argument passed to the function.

We were curios about any executables, libraries or drivers that make use of NtPowerInformation and call it with first argument being 0x5F. For that we searched recursively inside %SystemRoot%\System32 looking for files containing the string NtPowerInformation (in different code pages like ANSI, UTF-8, UTF-16). Then using HexRays decompiler plugin and two scripts (watch out, unpolished code!) allowing to run IDA Pro in batch mode we found 91 files calling NtPowerInformation at least once. And from those, the only one using 0x5F as an argument is inside a function (NotifyUserPresenceOnDesktopForDMAProtection) hosted by winlogon.exe. Here's the pseudocode:

//
// When symbols are loaded the function prototype is:
// void __fastcall NotifyUserPresenceOnDesktopForDMAProtection(int, struct _WLSM_GLOBAL_CONTEXT *)
//
void **__fastcall sub_1400391B8(int a1, __int64 a2)
{
  __int64 v2; // rcx
  void **result; // rax
  NTSTATUS v4; // eax
  __int64 v5; // rdx
  __int64 v6; // r8
  __int64 v7; // r9
  const wchar_t *v8; // r9
  int v9; // [rsp+40h] [rbp+8h]
  char InputBuffer; // [rsp+48h] [rbp+10h]

  v9 = a1;
  v2 = *(_QWORD *)(a2 + 16);
  InputBuffer = 0;
  if ( (unsigned int)sub_140027BB8(v2) ) // CSession::IsBoundToConsole
  {
    InputBuffer = v9;
    //
    // TraceApplicationPowerMessageEnd - 0x1F
    //
    v4 = NtPowerInformation(TraceApplicationPowerMessageEnd|0x40, &InputBuffer, 1u, 0i64, 0);
    v7 = (unsigned int)v4;
    ...

EOF