Sandboxie/Sandboxie/core/drv/syscall.c

1103 lines
33 KiB
C

/*
* Copyright 2004-2020 Sandboxie Holdings, LLC
* Copyright 2020-2024 David Xanatos, xanasoft.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
// Syscall Management
//---------------------------------------------------------------------------
#include "syscall.h"
#include "dll.h"
#include "process.h"
#include "thread.h"
#include "obj.h"
#include "api.h"
#include "util.h"
#include "session.h"
#include "conf.h"
#include "common/pattern.h"
#include "core/low/lowdata.h"
#include "dyn_data.h"
//---------------------------------------------------------------------------
// Functions
//---------------------------------------------------------------------------
static BOOLEAN Syscall_Init_List(void);
static BOOLEAN Syscall_Init_Table(void);
static BOOLEAN Syscall_Init_ServiceData(void);
static void Syscall_ErrorForAsciiName(const UCHAR *name_a);
void Syscall_Update_Lockdown();
//---------------------------------------------------------------------------
static NTSTATUS Syscall_OpenHandle(
PROCESS *proc, SYSCALL_ENTRY *syscall_entry, ULONG_PTR *user_args);
static NTSTATUS Syscall_GetNextProcess(
PROCESS *proc, SYSCALL_ENTRY *syscall_entry, ULONG_PTR *user_args);
static NTSTATUS Syscall_GetNextThread(
PROCESS *proc, SYSCALL_ENTRY *syscall_entry, ULONG_PTR *user_args);
static NTSTATUS Syscall_DeviceIoControlFile(
PROCESS *proc, SYSCALL_ENTRY *syscall_entry, ULONG_PTR *user_args);
static NTSTATUS Syscall_DuplicateHandle(
PROCESS *proc, SYSCALL_ENTRY *syscall_entry, ULONG_PTR *user_args);
#ifdef _M_AMD64
static BOOLEAN Syscall_QuerySystemInfo_SupportProcmonStack(
PROCESS *proc, SYSCALL_ENTRY *syscall_entry, ULONG_PTR *user_args);
#endif
//---------------------------------------------------------------------------
static NTSTATUS Syscall_Api_Query(PROCESS *proc, ULONG64 *parms);
static NTSTATUS Syscall_Api_Invoke(PROCESS *proc, ULONG64 *parms);
//---------------------------------------------------------------------------
static ULONG Syscall_GetIndexFromNtdll(UCHAR *code);
static BOOLEAN Syscall_GetKernelAddr(
ULONG index, void **pKernelAddr, ULONG *pParamCount);
//---------------------------------------------------------------------------
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, Syscall_Init)
#pragma alloc_text (INIT, Syscall_Init_List)
#pragma alloc_text (INIT, Syscall_Init_Table)
#pragma alloc_text (INIT, Syscall_Init_ServiceData)
#pragma alloc_text (INIT, Syscall_Set1)
#pragma alloc_text (INIT, Syscall_Set2)
#pragma alloc_text (INIT, Syscall_ErrorForAsciiName)
#pragma alloc_text (INIT, Syscall_GetIndexFromNtdll)
#pragma alloc_text (INIT, Syscall_GetKernelAddr)
#pragma alloc_text (INIT, Syscall_GetServiceTable)
#endif // ALLOC_PRAGMA
#include "syscall_util.c"
#ifdef HOOK_WIN32K
#include "syscall_win32.c"
#endif
//---------------------------------------------------------------------------
// Variables
//---------------------------------------------------------------------------
static LIST Syscall_List;
static SYSCALL_ENTRY **Syscall_Table = NULL;
static ULONG Syscall_MaxIndex = 0;
static UCHAR *Syscall_NtdllSavedCode = NULL;
static SYSCALL_ENTRY *Syscall_SetInformationThread = NULL;
//---------------------------------------------------------------------------
// Syscall_Init
//---------------------------------------------------------------------------
_FX BOOLEAN Syscall_Init(void)
{
if (! Syscall_Init_List())
return FALSE;
if (! Syscall_Init_Table())
return FALSE;
if (! Syscall_Init_ServiceData())
return FALSE;
#ifdef HOOK_WIN32K
if (Driver_OsBuild >= 14393 && Conf_Get_Boolean(NULL, L"EnableWin32kHooks", 0, TRUE)) {
if (!Syscall_Init_List32())
return FALSE;
if (!Syscall_Init_Table32())
return FALSE;
}
#endif
if (! Syscall_Set1("DuplicateObject", Syscall_DuplicateHandle))
return FALSE;
if (Driver_OsVersion >= DRIVER_WINDOWS_VISTA) {
if (!Syscall_Set1("GetNextProcess", Syscall_GetNextProcess))
return FALSE;
if (!Syscall_Set1("GetNextThread", Syscall_GetNextThread))
return FALSE;
}
if (!Syscall_Set1("DeviceIoControlFile", Syscall_DeviceIoControlFile))
return FALSE;
#ifdef _M_AMD64
if (!Syscall_Set3("QuerySystemInformation", Syscall_QuerySystemInfo_SupportProcmonStack))
return FALSE;
#endif
//
// set API handlers
//
Api_SetFunction(API_QUERY_SYSCALLS, Syscall_Api_Query);
Api_SetFunction(API_INVOKE_SYSCALL, Syscall_Api_Invoke);
//
// finish
//
return TRUE;
}
//---------------------------------------------------------------------------
// Syscall_Init_List
//---------------------------------------------------------------------------
_FX BOOLEAN Syscall_Init_List(void)
{
BOOLEAN success = FALSE;
UCHAR *name, *ntdll_code;
void *ntos_addr;
DLL_ENTRY *dll;
SYSCALL_ENTRY *entry;
ULONG proc_index, proc_offset, syscall_index, param_count;
ULONG name_len, entry_len;
List_Init(&Syscall_List);
//
// prepare the approve and disabled lists
//
LIST disabled_hooks;
Syscall_LoadHookMap(L"DisableWinNtHook", &disabled_hooks);
LIST approved_syscalls;
Syscall_LoadHookMap(L"ApproveWinNtSysCall", &approved_syscalls);
//
// scan each ZwXxx export in NTDLL
//
dll = Dll_Load(Dll_NTDLL);
if (! dll)
goto finish;
proc_offset = Dll_GetNextProc(dll, "Zw", &name, &proc_index);
if (! proc_offset)
goto finish;
while (proc_offset) {
if (name[0] != 'Z' || name[1] != 'w')
break;
name += 2; // skip Zw prefix
for (name_len = 0; (name_len < 64) && name[name_len]; ++name_len)
;
//DbgPrint(" Found SysCall %s\n", name);
entry = NULL;
//
// we don't hook ZwXxx services which do not or may not return to
// caller. this is because the invocation of Syscall_Api_Invoke
// goes through IopXxxControlFile (called by NtDeviceIoControlFile)
// which takes a reference on the file object for our API device
// object, and no return means there is no corresponding dereference
// for the file object. there could be other nasty side effects.
//
// ZwXxx services: ZwContinue, ZwCallbackReturn, ZwRaiseException,
// NtTerminateJobObject, NtTerminateProcess, NtTerminateThread
//
#define IS_PROC_NAME(ln,nm) (name_len == ln && memcmp(name, nm, ln) == 0)
if ( IS_PROC_NAME(8, "Continue")
|| IS_PROC_NAME(10, "ContinueEx")
|| IS_PROC_NAME(14, "CallbackReturn")
|| IS_PROC_NAME(14, "RaiseException")
|| IS_PROC_NAME(18, "TerminateJobObject")
|| IS_PROC_NAME(16, "TerminateProcess")
|| IS_PROC_NAME(15, "TerminateThread")
) {
goto next_zwxxx;
}
//
// on 64-bit Windows, some syscalls are fake, and should be skipped
//
if ( IS_PROC_NAME(15, "QuerySystemTime"))
goto next_zwxxx;
// ICD-10607 - McAfee uses it to pass its own data in the stack. The call is not important to us.
//if ( IS_PROC_NAME(14, "YieldExecution")) // $Workaround$ - 3rd party fix
// goto next_zwxxx;
//
// the Google Chrome "wow_helper" process expects NtMapViewOfSection
// to not be already hooked. although this is needed only on 64-bit
// Vista, this particular syscall is not very important to us, so
// for sake of consistency, we skip hooking it on all platforms
//
//if ( IS_PROC_NAME(16, "MapViewOfSection")) // $Workaround$ - 3rd party fix
// goto next_zwxxx;
if(Syscall_HookMapMatch(name, name_len, &disabled_hooks))
goto next_zwxxx;
#undef IS_PROC_NAME
//
// analyze each ZwXxx export to find the service index number
//
ntos_addr = NULL;
param_count = 0;
ntdll_code = Dll_RvaToAddr(dll, proc_offset);
if (ntdll_code) {
syscall_index = Syscall_GetIndexFromNtdll(ntdll_code);
if (syscall_index == -2) {
//
// if ZwXxx export is not a real syscall, then skip it
//
goto next_zwxxx;
}
if (syscall_index != -1) {
Syscall_GetKernelAddr(
syscall_index, &ntos_addr, &param_count);
//DbgPrint(" Found SysCall: %s, pcnt %d; idx: %d\r\n", name, param_count, syscall_index);
}
}
if (! ntos_addr) {
Syscall_ErrorForAsciiName(name);
goto finish;
}
//
// store an entry for the syscall in our list of syscalls
//
entry_len = sizeof(SYSCALL_ENTRY) + name_len + 1;
entry = Mem_AllocEx(Driver_Pool, entry_len, TRUE);
if (! entry)
goto finish;
entry->syscall_index = (USHORT)syscall_index;
entry->param_count = (USHORT)param_count;
entry->ntdll_offset = proc_offset;
entry->ntos_func = ntos_addr;
entry->handler1_func = NULL;
entry->handler2_func = NULL;
#ifdef _M_AMD64
entry->handler3_func_support_procmon = NULL;
#endif
entry->approved = (Syscall_HookMapMatch(name, name_len, &approved_syscalls) != 0);
entry->name_len = (USHORT)name_len;
memcpy(entry->name, name, name_len);
entry->name[name_len] = '\0';
List_Insert_After(&Syscall_List, NULL, entry);
if (syscall_index > Syscall_MaxIndex)
Syscall_MaxIndex = syscall_index;
//
// process next ZwXxx export
//
next_zwxxx:
proc_offset = Dll_GetNextProc(dll, NULL, &name, &proc_index);
}
success = TRUE;
//
// report an error if we did not find a reasonable number of services
//
if (Syscall_MaxIndex < 100) {
Log_Msg1(MSG_1113, L"100");
success = FALSE;
}
if (Syscall_MaxIndex >= 500) {
Log_Msg1(MSG_1113, L"500");
success = FALSE;
}
finish:
if(!success)
Syscall_MaxIndex = 0;
Syscall_FreeHookMap(&disabled_hooks);
Syscall_FreeHookMap(&approved_syscalls);
return success;
}
//---------------------------------------------------------------------------
// Syscall_Init_Table
//---------------------------------------------------------------------------
_FX BOOLEAN Syscall_Init_Table(void)
{
SYSCALL_ENTRY *entry;
ULONG len, i;
//
// build the table which maps syscall index numbers to entries
//
len = sizeof(SYSCALL_ENTRY *) * (Syscall_MaxIndex + 1);
Syscall_Table = Mem_AllocEx(Driver_Pool, len, TRUE);
if (! Syscall_Table)
return FALSE;
for (i = 0; i <= Syscall_MaxIndex; ++i) {
entry = List_Head(&Syscall_List);
while (entry) {
if (entry->syscall_index == i)
break;
entry = List_Next(entry);
}
Syscall_Table[i] = entry;
}
return TRUE;
}
//---------------------------------------------------------------------------
// Syscall_Init_ServiceData
//---------------------------------------------------------------------------
_FX BOOLEAN Syscall_Init_ServiceData(void)
{
UCHAR *NtdllExports[] = NATIVE_FUNCTION_NAMES;
SYSCALL_ENTRY *entry;
DLL_ENTRY *dll;
UCHAR *ntdll_code;
ULONG i;
//
// allocate some space to save code from ntdll
//
Syscall_NtdllSavedCode = Mem_AllocEx(Driver_Pool, (NATIVE_FUNCTION_SIZE * NATIVE_FUNCTION_COUNT), TRUE);
if (! Syscall_NtdllSavedCode)
return FALSE;
dll = Dll_Load(Dll_NTDLL);
if (! dll)
return FALSE;
//
// save a copy of some syscall functions in ntdll, we will pass
// these to SbieSvc, which in turn will pass them to SbieLow
// (see core/svc/DriverAssistInject.cpp and core/low/lowdata.h)
//
for (i = 0; i < NATIVE_FUNCTION_COUNT; ++i) {
entry = Syscall_GetByName(NtdllExports[i] + 2); // +2 skip Nt prefix
if (! entry)
return FALSE;
ntdll_code = Dll_RvaToAddr(dll, entry->ntdll_offset);
if (! ntdll_code) {
Syscall_ErrorForAsciiName(entry->name);
return FALSE;
}
memcpy(Syscall_NtdllSavedCode + (i * NATIVE_FUNCTION_SIZE), ntdll_code, NATIVE_FUNCTION_SIZE);
}
//
// get the syscall for NtSetInformationThread (see Syscall_Api_Invoke)
//
Syscall_SetInformationThread = Syscall_GetByName("SetInformationThread");
//
// finish
//
return TRUE;
}
//---------------------------------------------------------------------------
// Syscall_GetByName
//---------------------------------------------------------------------------
_FX SYSCALL_ENTRY *Syscall_GetByName(const UCHAR *name)
{
ULONG name_len = strlen(name);
SYSCALL_ENTRY *entry = List_Head(&Syscall_List);
while (entry) {
if (entry->name_len == name_len
&& memcmp(entry->name, name, name_len) == 0) {
return entry;
}
entry = List_Next(entry);
}
Syscall_ErrorForAsciiName(name);
return NULL;
}
//---------------------------------------------------------------------------
// Syscall_Set1
//---------------------------------------------------------------------------
_FX BOOLEAN Syscall_Set1(const UCHAR *name, P_Syscall_Handler1 handler_func)
{
SYSCALL_ENTRY *entry = Syscall_GetByName(name);
if (! entry)
return FALSE;
entry->handler1_func = handler_func;
return TRUE;
}
//---------------------------------------------------------------------------
// Syscall_Set3
//---------------------------------------------------------------------------
#ifdef _M_AMD64
_FX BOOLEAN Syscall_Set3(const UCHAR *name, P_Syscall_Handler3_Support_Procmon_Stack handler_func)
{
SYSCALL_ENTRY *entry = Syscall_GetByName(name);
if (!entry)
return FALSE;
entry->handler3_func_support_procmon = handler_func;
return TRUE;
}
#endif
//---------------------------------------------------------------------------
// Syscall_ErrorForAsciiName
//---------------------------------------------------------------------------
_FX void Syscall_ErrorForAsciiName(const UCHAR *name_a)
{
WCHAR name_w[66];
ULONG i;
for (i = 0; (i < 64) && name_a[i]; ++i)
name_w[i] = name_a[i];
name_w[i] = '\0';
Log_Msg1(MSG_1113, name_w);
}
//---------------------------------------------------------------------------
// Syscall_Invoke
//---------------------------------------------------------------------------
NTSTATUS Sbie_InvokeSyscall_asm(void* func, ULONG count, void* args);
_FX NTSTATUS Syscall_Invoke(SYSCALL_ENTRY *entry, ULONG_PTR *stack)
{
NTSTATUS status;
//
// Note: when directly calling win32k functions with "Core Isolation" (HVCI) enabled
// the nt!guard_dispatch_icall will cause a bugcheck!
// Hence we use a call proxy Sbie_InvokeSyscall_asm instead of a direct call
// alternatively we could disable "Control Flow Guard" for this file
//
__try {
//DbgPrint("[syscall] request param count = %d\n", entry->param_count);
status = Sbie_InvokeSyscall_asm(entry->ntos_func, entry->param_count, stack);
} __except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
return status;
}
//---------------------------------------------------------------------------
// Syscall_Api_Invoke
//---------------------------------------------------------------------------
_FX NTSTATUS Syscall_Api_Invoke(PROCESS *proc, ULONG64 *parms)
{
ULONG_PTR *user_args;
SYSCALL_ENTRY *entry;
ULONG syscall_index;
NTSTATUS status;
#ifdef _M_AMD64
volatile ULONG_PTR ret = 0;
volatile ULONG_PTR UserStack = 0;
volatile PETHREAD pThread = PsGetCurrentThread();
volatile PKTRAP_FRAME pTrapFrame = NULL;
#endif
//int exception;
//
// caller must be sandboxed and service index must be known
//
if (! proc)
return STATUS_NOT_IMPLEMENTED;
syscall_index = (ULONG)parms[1];
#ifdef HOOK_WIN32K
if ((syscall_index & 0x1000) != 0) { // win32k syscall
return Syscall_Api_Invoke32(proc, parms);
}
#endif
syscall_index = (syscall_index & 0xFFF);
//DbgPrint("[syscall] request for service %d / %08X\n", syscall_index, syscall_index);
//
// use direct syscalls to access 64 bit memory from a wow process
// instead of using heaven's gate / wow64ext
//
if (syscall_index == 0xFFF && parms[3] != 0) {
__try {
entry = Syscall_GetByName((UCHAR*)parms[3]);
if(parms[4]) // return found index to the caller to be re used later
*(USHORT*)parms[4] = entry->syscall_index;
} __except (EXCEPTION_EXECUTE_HANDLER) {
entry = NULL;
}
}
else
//
if (Syscall_Table && (syscall_index <= Syscall_MaxIndex))
entry = Syscall_Table[syscall_index];
else
entry = NULL;
if (! entry)
return STATUS_INVALID_SYSTEM_SERVICE;
// DbgPrint("[syscall] request p=%06d t=%06d - BEGIN %s\n", PsGetCurrentProcessId(), PsGetCurrentThreadId(), entry->name);
#ifdef XP_SUPPORT
//
// make sure the thread has sufficient access rights to itself
// then impersonate the full access token for the thread or process
//
if (!Thread_AdjustGrantedAccess()) {
Process_SetTerminated(proc, 5);
}
else
#endif
if(!proc->is_locked_down || entry->approved)
Thread_SetThreadToken(proc); // may set proc->terminated
if (proc->terminated) {
Process_TerminateProcess(proc);
return STATUS_PROCESS_IS_TERMINATING;
}
//
// if we have a handler for this service, invoke it
//
user_args = (ULONG_PTR *)parms[2];
__try {
BOOLEAN traced = FALSE;
const ULONG args_len = entry->param_count * sizeof(ULONG_PTR);
#ifdef _WIN64
ProbeForRead(user_args, args_len, sizeof(ULONG_PTR));
#else ! _WIN64
ProbeForRead(user_args, args_len, sizeof(UCHAR));
#endif _WIN64
#ifdef _M_AMD64
// default - support procmon stack if handler3_func_support_procmon is null.
if (!entry->handler3_func_support_procmon
|| entry->handler3_func_support_procmon(proc, entry, user_args)
)
{
// $Offset$
if (Dyndata_Active && Dyndata_Config.TrapFrame_offset) {
pTrapFrame = (PKTRAP_FRAME) *(ULONG_PTR*)((UCHAR*)pThread + Dyndata_Config.TrapFrame_offset);
if (pTrapFrame) {
ret = pTrapFrame->Rip;
UserStack = pTrapFrame->Rsp;
pTrapFrame->Rsp = pTrapFrame->Rdi; //*pRbp;
pTrapFrame->Rip = pTrapFrame->Rbx; //*pRbx;
}
}
else
{
pTrapFrame = NULL;
}
}
else
{
pTrapFrame = NULL;
}
#endif
//if (proc->ipc_trace & (TRACE_ALLOW | TRACE_DENY))
//{
// if (strcmp(entry->name, "AlpcSendWaitReceivePort") == 0)
// {
// HANDLE hHandle;
// hHandle = (HANDLE*)user_args[0];
// DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_ERROR_LEVEL, "SBIE [syscall] p=%06d t=%06d - %s, handle = %X >>>>>>\n",
// PsGetCurrentProcessId(), PsGetCurrentThreadId(),
// entry->name,
// hHandle);
// }
//}
if (entry->handler1_func) {
status = entry->handler1_func(proc, entry, user_args);
} else {
status = Syscall_Invoke(entry, user_args);
}
// Debug tip. Display all Alpc/Rpc here.
HANDLE hHandle = NULL;
UNICODE_STRING* puStr = NULL;
if (proc->ipc_trace & (TRACE_ALLOW | TRACE_DENY))
{
if ((strcmp(entry->name, "ConnectPort") == 0) ||
(strcmp(entry->name, "AlpcConnectPort") == 0) )
{
hHandle = *(HANDLE*)user_args[0];
puStr = (UNICODE_STRING*)user_args[1];
//DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, "SBIE [syscall] p=%06d t=%06d - %s, '%.*S', status = 0x%X, handle = %X\n",
// PsGetCurrentProcessId(), PsGetCurrentThreadId(),
// entry->name,
// (puStr->Length / 2), puStr->Buffer,
// status, hHandle);
//if (puStr && puStr->Buffer && wcsstr(puStr->Buffer, L"\\RPC Control\\LRPC-"))
//{
//int i = 0; // place breakpoint here if you want to debug a particular port
//}
}
else if ( (strcmp(entry->name, "AlpcCreatePort") == 0) ||
(strcmp(entry->name, "AlpcConnectPortEx") == 0) )
{
hHandle = *(HANDLE*)user_args[0];
POBJECT_ATTRIBUTES pObjectAttributes = (POBJECT_ATTRIBUTES)user_args[1];
if (pObjectAttributes)
puStr = (UNICODE_STRING*)pObjectAttributes->ObjectName;
//DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, "SBIE [syscall] p=%06d t=%06d - %s, '%.*S', status = 0x%X, handle = %X\n",
// PsGetCurrentProcessId(), PsGetCurrentThreadId(),
// entry->name,
// (puStr->Length / 2), puStr->Buffer,
// status, hHandle);
}
else if ((strcmp(entry->name, "ReplyWaitReceivePort") == 0) ||
(strcmp(entry->name, "ReceiveMessagePort") == 0) ||
(strcmp(entry->name, "AlpcSendReply") == 0) ||
(strcmp(entry->name, "AlpcAcceptConnectPort") == 0) ||
(strcmp(entry->name, "AlpcCreatePortSection") == 0) ||
// these 2 APIs will generate a lot of output if we don't check status
((status != STATUS_SUCCESS) && ((strcmp(entry->name, "AlpcSendWaitReceivePort") == 0) || (strcmp(entry->name, "RequestWaitReplyPort") == 0))) )
{
hHandle = (HANDLE*)user_args[0];
//DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, "SBIE [syscall] p=%06d t=%06d - %s, status = 0x%X, handle = %X\n",
// PsGetCurrentProcessId(), PsGetCurrentThreadId(),
// entry->name,
// status, hHandle);
}
else if (strcmp(entry->name, "OpenDirectoryObject") == 0)
{
POBJECT_ATTRIBUTES pObjectAttributes = (POBJECT_ATTRIBUTES)user_args[2];
if (pObjectAttributes)
puStr = (UNICODE_STRING*)pObjectAttributes->ObjectName;
}
}
if (proc->file_trace & (TRACE_ALLOW | TRACE_DENY))
{
if (strcmp(entry->name, "QueryFullAttributesFile") == 0)
{
POBJECT_ATTRIBUTES pObjectAttributes = (POBJECT_ATTRIBUTES)user_args[0];
if (pObjectAttributes)
puStr = (UNICODE_STRING*)pObjectAttributes->ObjectName;
}
else if (strcmp(entry->name, "QueryInformationFile") == 0)
{
hHandle = (HANDLE*)user_args[0];
}
else if (strcmp(entry->name, "CreateFile") == 0 || strcmp(entry->name, "OpenFile") == 0)
{
hHandle = *(HANDLE*)user_args[0];
POBJECT_ATTRIBUTES pObjectAttributes = (POBJECT_ATTRIBUTES)user_args[2];
if (pObjectAttributes)
puStr = (UNICODE_STRING*)pObjectAttributes->ObjectName;
}
else if (strcmp(entry->name, "FsControlFile") == 0 || strcmp(entry->name, "Close") == 0)
{
hHandle = (HANDLE*)user_args[0];
}
}
if (puStr || hHandle)
{
WCHAR trace_str[128];
if (hHandle) {
RtlStringCbPrintfW(trace_str, sizeof(trace_str), L"%.*S, status = 0x%X, handle = %X; ", //59 chars + entry->name
max(strlen(entry->name), 64), entry->name, status, hHandle);
}
else {
RtlStringCbPrintfW(trace_str, sizeof(trace_str), L"%.*S, status = 0x%X; ", //59 chars + entry->name
max(strlen(entry->name), 64), entry->name, status);
}
const WCHAR* strings[4] = { trace_str, trace_str + (entry->name_len + 2), puStr ? puStr->Buffer : NULL, NULL };
ULONG lengths[4] = {entry->name_len, wcslen(trace_str) - (entry->name_len + 4), puStr ? puStr->Length / 2 : 0, 0 };
Session_MonitorPutEx(MONITOR_SYSCALL | (entry->approved ? MONITOR_OPEN : MONITOR_TRACE),
strings, lengths, PsGetCurrentProcessId(), PsGetCurrentThreadId());
traced = TRUE;
}
if (!traced && ((proc->call_trace & TRACE_ALLOW) || ((status != STATUS_SUCCESS) && (proc->call_trace & TRACE_DENY))))
{
// Suppress Sbie's own calls to DeviceIoControlFile
if ((strcmp(entry->name, "DeviceIoControlFile") != 0) || user_args[5] != API_SBIEDRV_CTLCODE)
{
WCHAR trace_str[128];
RtlStringCbPrintfW(trace_str, sizeof(trace_str), L"%.*S, status = 0x%X", //59 chars + entry->name
max(strlen(entry->name), 64), entry->name,
status);
const WCHAR* strings[3] = { trace_str, trace_str + (entry->name_len + 2), NULL };
ULONG lengths[3] = { entry->name_len, wcslen(trace_str) - (entry->name_len + 2), 0 };
Session_MonitorPutEx(MONITOR_SYSCALL | (entry->approved ? MONITOR_OPEN : MONITOR_TRACE),
strings, lengths, PsGetCurrentProcessId(), PsGetCurrentThreadId());
}
}
#ifdef _M_AMD64
if (Dyndata_Active && Dyndata_Config.TrapFrame_offset) {
if (pTrapFrame) {
pTrapFrame->Rip = ret;
pTrapFrame->Rsp = UserStack;
}
}
#endif
} __except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
/*exception = 0;
if (status != 0 && _wcsicmp(proc->image_name, L"chrome.exe") == 0) {
exception = 1;
//if (strstr(entry->name, "Pipe") != 0) exception = 1;
//if (strstr(entry->name, "FsControl") != 0) exception = 1;
}
if (exception) {
DbgPrint("[syscall] request p=%06d t=%06d - END (%0X) %s\n", PsGetCurrentProcessId(), PsGetCurrentThreadId(), status, entry->name);
}*/
if (proc->terminated) {
Process_TerminateProcess(proc);
return STATUS_PROCESS_IS_TERMINATING;
}
//
// clear any thread impersonation set during the syscall, to restore
// use of the highly restricted primary token in this thread
//
// note that in one special case we leave the impersonation token as
// was set by Thread_SetInformationThread_ChangeNotifyToken, see there
//
if (status == STATUS_THREAD_NOT_IN_PROCESS
&& entry == Syscall_SetInformationThread
&& user_args[0] == (ULONG_PTR)NtCurrentThread()) {
status = STATUS_SUCCESS;
} else {
Thread_ClearThreadToken();
}
/*if (! NT_SUCCESS(status)) {
DbgPrint("Process %06d Syscall %04X Status %08X\n", proc->pid, syscall_index, status);
}*/
return status;
}
//---------------------------------------------------------------------------
// Syscall_Api_Query
//---------------------------------------------------------------------------
_FX NTSTATUS Syscall_Api_Query(PROCESS *proc, ULONG64 *parms)
{
ULONG buf_len;
ULONG *user_buf;
ULONG *ptr;
SYSCALL_ENTRY *entry;
#ifdef HOOK_WIN32K
if (parms[2] == 1) { // 1 - win32k
return Syscall_Api_Query32(proc, parms);
}
else if (parms[2] != 0) { // 0 - ntoskrnl
return STATUS_INVALID_PARAMETER;
}
#endif
BOOLEAN add_names = parms[3] != 0;
//
// caller must be our service process
//
if (proc || (PsGetCurrentProcessId() != Api_ServiceProcessId))
return STATUS_ACCESS_DENIED;
//
// allocate user mode space for syscall table
//
buf_len = sizeof(ULONG) // size of buffer
+ sizeof(ULONG) // offset to extra data (for SbieSvc)
+ (NATIVE_FUNCTION_SIZE * NATIVE_FUNCTION_COUNT) // saved code from ntdll
+ List_Count(&Syscall_List) * ((sizeof(ULONG) * 2) + (add_names ? 64 : 0))
+ sizeof(ULONG) * 2 // final terminator entry
;
user_buf = (ULONG *)parms[1];
ProbeForWrite(user_buf, buf_len, sizeof(ULONG));
//
// populate the buffer with syscall data. first we store
// the size of the buffer, and then we leave room for ULONG used
// by SbieSvc, and for the code for four ZwXxx stub functions
//
ptr = user_buf;
*ptr = buf_len;
++ptr;
*ptr = 0; // placeholder for offset to extra offset
++ptr;
memcpy(ptr, Syscall_NtdllSavedCode, (NATIVE_FUNCTION_SIZE * NATIVE_FUNCTION_COUNT));
ptr += (NATIVE_FUNCTION_SIZE * NATIVE_FUNCTION_COUNT) / sizeof(ULONG);
//
// store service index number and (only on 32-bit Windows) also
// the parameter count for each syscall into one ULONG.
// store corresponding offset within ntdll into the other ULONG
//
entry = List_Head(&Syscall_List);
while (entry) {
ULONG syscall_index = (ULONG)entry->syscall_index;
#ifndef _WIN64
ULONG param_count = (ULONG)entry->param_count;
syscall_index |= (param_count * 4) << 24;
#endif ! _WIN64
*ptr = syscall_index;
++ptr;
*ptr = entry->ntdll_offset;
++ptr;
if (add_names) {
memcpy(ptr, entry->name, entry->name_len);
((char*)ptr)[entry->name_len] = 0;
ptr += 16; // 16 * sizeog(ULONG) = 64
}
entry = List_Next(entry);
}
//
// store a final zero terminator entry and return successfully
//
*ptr = 0;
++ptr;
*ptr = 0;
return STATUS_SUCCESS;
}
//---------------------------------------------------------------------------
// Syscall_Update_Lockdown
//---------------------------------------------------------------------------
_FX void Syscall_Update_Lockdown()
{
SYSCALL_ENTRY *entry;
#ifdef HOOK_WIN32K
Syscall_Update_Lockdown32();
#endif
LIST approved_syscalls;
Syscall_LoadHookMap(L"ApproveWinNtSysCall", &approved_syscalls);
entry = List_Head(&Syscall_List);
while (entry) {
entry->approved = (Syscall_HookMapMatch(entry->name, entry->name_len, &approved_syscalls) != 0);
entry = List_Next(entry);
}
Syscall_FreeHookMap(&approved_syscalls);
}
//---------------------------------------------------------------------------
// Syscall_QuerySystemInfo_SupportProcmonStack
//---------------------------------------------------------------------------
#ifdef _M_AMD64
_FX BOOLEAN Syscall_QuerySystemInfo_SupportProcmonStack(
PROCESS *proc, SYSCALL_ENTRY *syscall_entry, ULONG_PTR *user_args)
{
BOOLEAN bRet = TRUE;
// In Win10, case 0xb9 triggers WbCreateWarbirdProcess/WbDispatchOperation/WbSetTrapFrame
// and PspSetContextThreadInternal (Warbird operation?) to deliver a apc call in the current
// thread in user mode. Warbird needs the real thread context.
// https://github.com/xpn/warbird_exploit
// this exploit only works on x86 windows but can still crash a x64 one
// It seems only NtQuerySystemInfomation is doing this.
// Call Syscall_Set3 in Syscall_Init if we see a different syscall does this in the future.
// It is safe to access the parameter. ProbeForRead has been done by the caller already.
if (syscall_entry->param_count > 0)
{
if (user_args[0] == 0xb9)
{
bRet = FALSE;
}
}
return bRet;
}
#endif
//---------------------------------------------------------------------------
// 32-bit and 64-bit code
//---------------------------------------------------------------------------
#ifdef _WIN64
#include "syscall_64.c"
#else ! _WIN64
#include "syscall_32.c"
#endif _WIN64
#include "syscall_open.c"