Sandboxie/Sandboxie/core/drv/ipc.c

1934 lines
56 KiB
C

/*
* Copyright 2004-2020 Sandboxie Holdings, LLC
* Copyright 2020-2023 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/>.
*/
//---------------------------------------------------------------------------
// Inter-Process Communication
//---------------------------------------------------------------------------
#include "ipc.h"
#include "syscall.h"
#include "process.h"
#include "session.h"
#include "obj.h"
#include "conf.h"
#include "util.h"
#include "api.h"
#include "common/my_version.h"
#include "common/pattern.h"
//---------------------------------------------------------------------------
// Functions
//---------------------------------------------------------------------------
static BOOLEAN Ipc_Init_Type(
const WCHAR *TypeName, P_Syscall_Handler2 handler, ULONG createEx);
static BOOLEAN Ipc_InitPaths(PROCESS *proc);
static BOOLEAN Ipc_IsComServer(PROCESS *proc);
//---------------------------------------------------------------------------
static NTSTATUS Ipc_CheckGenericObject(
PROCESS *proc, void *Object, UNICODE_STRING *Name,
ULONG Operation, ACCESS_MASK GrantedAccess);
static NTSTATUS Ipc_CheckPortObject(
PROCESS *proc, void *Object, UNICODE_STRING *Name,
ULONG Operation, ACCESS_MASK GrantedAccess);
static NTSTATUS Ipc_CheckJobObject(
PROCESS *proc, void *Object, UNICODE_STRING *Name,
ULONG Operation, ACCESS_MASK GrantedAccess);
static NTSTATUS Ipc_CheckObjectName(HANDLE handle, KPROCESSOR_MODE mode);
//---------------------------------------------------------------------------
static NTSTATUS Ipc_Api_DuplicateObject(PROCESS *proc, ULONG64 *parms);
static NTSTATUS Ipc_Api_CreateDirOrLink(PROCESS *proc, ULONG64 *parms);
static NTSTATUS Ipc_Api_OpenDeviceMap(PROCESS *proc, ULONG64 *parms);
static NTSTATUS Ipc_Api_QuerySymbolicLink(PROCESS *proc, ULONG64 *parms);
//---------------------------------------------------------------------------
NTSTATUS Thread_GetKernelHandleForUserHandle(
HANDLE *OutKernelHandle, HANDLE InUserHandle);
//---------------------------------------------------------------------------
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, Ipc_Init)
#pragma alloc_text (INIT, Ipc_Init_Type)
#endif // ALLOC_PRAGMA
//---------------------------------------------------------------------------
// Variables
//---------------------------------------------------------------------------
static const WCHAR *Ipc_Event_TypeName = L"Event";
static const WCHAR *Ipc_EventPair_TypeName = L"EventPair";
static const WCHAR *Ipc_KeyedEvent_TypeName = L"KeyedEvent";
static const WCHAR *Ipc_Mutant_TypeName = L"Mutant";
static const WCHAR *Ipc_Semaphore_TypeName = L"Semaphore";
static const WCHAR *Ipc_Section_TypeName = L"Section";
static const WCHAR *Ipc_JobObject_TypeName = L"JobObject";
static const WCHAR *Ipc_SymLink_TypeName = L"SymbolicLinkObject";
static const WCHAR *Ipc_Directory_TypeName = L"DirectoryObject";
static PERESOURCE Ipc_DirLock = NULL;
static LIST Ipc_ObjDirs;
//---------------------------------------------------------------------------
// Structures and Types
//---------------------------------------------------------------------------
typedef struct _DIR_OBJ_HANDLE {
LIST_ELEM list_elem;
HANDLE handle;
} DIR_OBJ_HANDLE;
//---------------------------------------------------------------------------
// Ipc_Init
//---------------------------------------------------------------------------
_FX BOOLEAN Ipc_Init(void)
{
const UCHAR *_PortSyscallNames[] = {
"ConnectPort", "SecureConnectPort", "CreatePort",
"AlpcConnectPort", "AlpcCreatePort", NULL
};
const UCHAR **NamePtr;
List_Init(&Ipc_ObjDirs);
if (! Mem_GetLockResource(&Ipc_DirLock, TRUE))
return FALSE;
//
// set object open handlers for generic objects
//
#define Ipc_Init_Type_Generic(TypeName, ex) \
if (! Ipc_Init_Type(TypeName, Ipc_CheckGenericObject, ex)) \
return FALSE;
Ipc_Init_Type_Generic(Ipc_Event_TypeName, 0);
Ipc_Init_Type_Generic(Ipc_EventPair_TypeName, 0);
Ipc_Init_Type_Generic(Ipc_KeyedEvent_TypeName, 0);
Ipc_Init_Type_Generic(Ipc_Mutant_TypeName, 0);
Ipc_Init_Type_Generic(Ipc_Semaphore_TypeName, 0);
Ipc_Init_Type_Generic(Ipc_Section_TypeName, 17763); // NtCreateSectionEx introduced in windows 10 1809
Ipc_Init_Type_Generic(Ipc_SymLink_TypeName, 0);
Ipc_Init_Type_Generic(Ipc_Directory_TypeName, 9200); // NtCreateDirectoryObjectEx introduced windows 8
#undef Ipc_Init_Type_Generic
if (! Ipc_Init_Type(Ipc_JobObject_TypeName, Ipc_CheckJobObject, 0))
return FALSE;
//
// set object open handlers for port objects
//
for (NamePtr = _PortSyscallNames; *NamePtr; ++NamePtr) {
if (Driver_OsVersion >= DRIVER_WINDOWS_VISTA ||
memcmp(*NamePtr, "Alpc", 4) != 0) {
if (! Syscall_Set2(*NamePtr, Ipc_CheckPortObject))
return FALSE;
}
}
if (Driver_OsVersion >= DRIVER_WINDOWS_8) {
if (! Syscall_Set2("AlpcConnectPortEx", Ipc_CheckPortObject))
return FALSE;
}
//
// register object filter callbacks on Vista SP1 and later
//
if (Driver_OsVersion > DRIVER_WINDOWS_VISTA) {
if (Conf_Get_Boolean(NULL, L"EnableObjectFiltering", 0, TRUE)) {
if (!Obj_Load_Filter())
return FALSE;
}
}
//
// set up port request filter handlers
//
if (! Syscall_Set1("ImpersonateClientOfPort", Ipc_ImpersonatePort))
return FALSE;
if (Driver_OsVersion >= DRIVER_WINDOWS_VISTA) {
if (! Syscall_Set1(
"AlpcImpersonateClientOfPort", Ipc_ImpersonatePort))
return FALSE;
//
// protect use of NtRequestPort, NtRequestWaitReplyPort, and
// NtAlpcSendWaitReceivePort on Windows Vista to prevent use
// of EndTask and NetUserChangePassword, when called through our
// syscall interface.
//
// note that if a malicious program calls NtAlpcSendWaitReceivePort
// directly (without using our syscall interface to elevate first)
// to emulate NetUserChangePassword, then it gets status code
// STATUS_PRIVILEGE_NOT_HELD because the restricted process token
// does not include the change notify privilege.
//
// on Windows Vista, direct use of NtAlpcSendWaitReceivePort to
// emulate EndTask (without using our syscall interface to elevate
// first) will be blocked by UIPI. (But note that applications in
// other sandboxed will still be killable.)
//
// on Windows XP, the real NtRequestPort and NtRequestWaitReplyPort
// in the kernel are already hooked by the gui_xp module.
//
if (! Syscall_Set1("RequestPort", Ipc_RequestPort))
return FALSE;
if (! Syscall_Set1("RequestWaitReplyPort", Ipc_RequestPort))
return FALSE;
if (! Syscall_Set1(
"AlpcSendWaitReceivePort", Ipc_AlpcSendWaitReceivePort))
return FALSE;
}
//
// set API handlers
//
Api_SetFunction(API_DUPLICATE_OBJECT, Ipc_Api_DuplicateObject);
Api_SetFunction(API_CREATE_DIR_OR_LINK, Ipc_Api_CreateDirOrLink);
Api_SetFunction(API_OPEN_DEVICE_MAP, Ipc_Api_OpenDeviceMap);
Api_SetFunction(API_QUERY_SYMBOLIC_LINK, Ipc_Api_QuerySymbolicLink);
//Api_SetFunction(API_ALLOW_SPOOLER_PRINT_TO_FILE, Ipc_Api_AllowSpoolerPrintToFile);
#ifdef XP_SUPPORT
#ifndef _WIN64
Api_SetFunction(API_SET_LSA_AUTH_PKG, Ipc_Api_SetLsaAuthPkg);
#endif ! _WIN64
#endif
Api_SetFunction(API_GET_DYNAMIC_PORT_FROM_PID, Ipc_Api_GetDynamicPortFromPid);
Api_SetFunction(API_OPEN_DYNAMIC_PORT, Ipc_Api_OpenDynamicPort);
//
// prepare dynamic ports
//
if (!Mem_GetLockResource(&Ipc_Dynamic_Ports.pPortLock, TRUE))
return FALSE;
List_Init(&Ipc_Dynamic_Ports.Ports);
//
// finish
//
return TRUE;
}
//---------------------------------------------------------------------------
// Ipc_Init_Type
//---------------------------------------------------------------------------
_FX BOOLEAN Ipc_Init_Type(const WCHAR *TypeName, P_Syscall_Handler2 handler, ULONG createEx)
{
WCHAR nameW[64];
UCHAR nameA[64];
ULONG i, n;
wcscpy(nameW, L"Open");
wcscat(nameW, TypeName);
n = wcslen(nameW);
for (i = 0; i <= n; ++i)
nameA[i] = (UCHAR)nameW[i];
if (! Syscall_Set2(nameA, handler))
return FALSE;
wcscpy(nameW, L"Create");
wcscat(nameW, TypeName);
n = wcslen(nameW);
for (i = 0; i <= n; ++i)
nameA[i] = (UCHAR)nameW[i];
if (! Syscall_Set2(nameA, handler))
return FALSE;
if (createEx && Driver_OsBuild >= createEx) {
strcat(nameA, "Ex");
if (! Syscall_Set2(nameA, handler))
return FALSE;
}
return TRUE;
}
//---------------------------------------------------------------------------
// Ipc_CreateBoxPath
//---------------------------------------------------------------------------
_FX BOOLEAN Ipc_CreateBoxPath(PROCESS *proc)
{
NTSTATUS status;
UCHAR sd[128];
OBJECT_ATTRIBUTES objattrs;
UNICODE_STRING objname;
HANDLE handle;
WCHAR *ptr;
ULONG retries;
//
// initialize object attributes to create sandbox paths
//
RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
RtlSetDaclSecurityDescriptor(&sd, TRUE, NULL, FALSE);
InitializeObjectAttributes(
&objattrs, &objname,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, // | OBJ_PERMANENT,
NULL, &sd);
//
// main loop: try to create sandbox path, removing last path component
// each time we get STATUS_OBJECT_PATH_NOT_FOUND
//
ptr = NULL;
retries = 0;
while (retries < 64) {
++retries;
RtlInitUnicodeString(&objname, proc->box->ipc_path);
status = ZwCreateDirectoryObject(
&handle, DIRECTORY_ALL_ACCESS, &objattrs);
if (status == STATUS_OBJECT_PATH_NOT_FOUND) {
//
// if parent directories are missing, terminate the
// path at the last backslash and retry the loop
//
WCHAR *ptr2 = wcsrchr(proc->box->ipc_path, L'\\');
if (ptr)
*ptr = L'\\';
ptr = ptr2;
if (ptr)
*ptr = L'\0';
} else {
//
// if we could create the path, we check if any backslashes
// were zeroed out. if so, we restart the loop from the top
// using the full path. otherwise, we're done
//
if (NT_SUCCESS(status)) {
//ZwClose(handle);
KIRQL irql;
KeRaiseIrql(APC_LEVEL, &irql);
ExAcquireResourceExclusiveLite(Ipc_DirLock, TRUE);
DIR_OBJ_HANDLE *obj_handle = Mem_Alloc(Driver_Pool, sizeof(DIR_OBJ_HANDLE));
obj_handle->handle = handle;
List_Insert_After(&Ipc_ObjDirs, NULL, obj_handle);
ExReleaseResourceLite(Ipc_DirLock);
KeLowerIrql(irql);
}
if (status == STATUS_OBJECT_NAME_COLLISION)
status = STATUS_SUCCESS;
if (ptr) {
*ptr = L'\\';
ptr = NULL;
} else
break;
}
}
if (retries >= 64)
status = STATUS_UNSUCCESSFUL;
if (! NT_SUCCESS(status)) {
Log_Status_Ex_Process(MSG_IPC_CREATE_BOX_PATH, 0, status, proc->box->ipc_path, -1, proc->pid);
}
return (NT_SUCCESS(status));
}
//---------------------------------------------------------------------------
// Ipc_InitPaths
//---------------------------------------------------------------------------
_FX BOOLEAN Ipc_InitPaths(PROCESS* proc)
{
#ifdef USE_MATCH_PATH_EX
static const WCHAR* _NormalPath = L"NormalIpcPath";
#endif
static const WCHAR* _OpenPath = L"OpenIpcPath";
static const WCHAR* _ClosedPath = L"ClosedIpcPath";
static const WCHAR* _ReadPath = L"ReadIpcPath";
#ifndef USE_TEMPLATE_PATHS
static const WCHAR* openpaths[] = {
L"\\Windows\\ApiPort",
L"\\Sessions\\*\\Windows\\ApiPort",
L"\\Sessions\\*\\Windows\\SharedSection",
L"\\Windows\\SharedSection", // bSession0
L"\\Sessions\\*\\BaseNamedObjects\\CrSharedMem_*", // now required by Chromium browsers
L"\\ThemeApiPort",
L"\\KnownDlls\\*",
#ifdef _WIN64
L"\\KnownDlls32\\*",
#endif _WIN64
#ifdef _M_ARM64
L"\\KnownDllsChpe32\\*",
#endif _M_ARM64
L"\\NLS\\*",
L"*\\BaseNamedObjects*\\ShimCacheMutex",
L"*\\BaseNamedObjects*\\ShimSharedMemory",
L"*\\BaseNamedObjects*\\SHIMLIB_LOG_MUTEX",
L"*\\BaseNamedObjects*\\msgina: ReturnToWelcome",
L"\\Security\\LSA_AUTHENTICATION_INITIALIZED",
L"\\LsaAuthenticationPort",
L"\\NlsCacheMutant",
L"\\KernelObjects\\*",
//
// misc services
//
L"\\NLAPublicPort",
L"\\RPC Control\\nlaapi",
L"\\RPC Control\\tapsrvlpc",
L"\\RPC Control\\senssvc",
L"\\RPC Control\\samss lpc",
L"*\\BaseNamedObjects*\\SENS Information Cache",
L"*\\BaseNamedObjects*\\TabletHardwarePresent",
L"*\\BaseNamedObjects*\\userenv: * Group Policy has been applied",
L"*\\BaseNamedObjects*\\TermSrvReadyEvent",
//
// network
//
L"\\RPC Control\\dhcpcsvc",
L"\\RPC Control\\dhcpcsvc6",
L"\\RPC Control\\DNSResolver",
L"\\RPC Control\\RasmanRpc",
L"*\\BaseNamedObjects*\\WininetStartupMutex",
L"*\\BaseNamedObjects*\\WininetConnectionMutex",
L"*\\BaseNamedObjects*\\WininetProxyRegistryMutex",
L"*\\BaseNamedObjects*\\RasPbFile",
//
// cicero multi-language subsystem
//
L"*\\BaseNamedObjects*\\CTF.*",
L"*\\BaseNamedObjects*\\MSCTF.*",
L"*\\BaseNamedObjects*\\MSUIM.*",
L"*\\BaseNamedObjects*\\CtfmonInstMutex*",
L"*\\BaseNamedObjects*\\CiceroSharedMemDefault*",
L"*\\BaseNamedObjects*\\CicLoadWinStaWinSta*",
//
// sysinternals dbgview
//
L"*\\BaseNamedObjects*\\DBWinMutex",
L"*\\BaseNamedObjects*\\DBWIN_BUFFER",
L"*\\BaseNamedObjects*\\DBWIN_BUFFER_READY",
L"*\\BaseNamedObjects*\\DBWIN_DATA_READY",
//
// multimedia
//
L"\\RPC Control\\AudioSrv",
L"*\\BaseNamedObjects*\\mmGlobalPnpInfo",
L"*\\BaseNamedObjects*\\Guard*mmGlobalPnpInfoGuard",
L"*\\BaseNamedObjects*\\MidiMapper_modLongMessage_RefCnt",
L"*\\BaseNamedObjects*\\MidiMapper_Configure",
L"*\\BaseNamedObjects*\\SsiMidiDllCs",
L"*\\BaseNamedObjects*\\StaccatoSynthCore11Mutex",
L"*\\BaseNamedObjects*\\WDMAUD_Callbacks",
L"*\\BaseNamedObjects*\\DirectSound*",
L"*\\BaseNamedObjects*\\AMResourceMutex*",
L"*\\BaseNamedObjects*\\AMResourceMapping*",
L"*\\BaseNamedObjects*\\VideoRenderer",
L"*\\BaseNamedObjects*\\VIDEOMEMORY",
L"*\\BaseNamedObjects*\\mxrapi",
L"*\\BaseNamedObjects*\\mixercallback",
L"*\\BaseNamedObjects*\\hardwaremixercallback",
L"*\\BaseNamedObjects*\\DINPUTWINMM",
L"*\\BaseNamedObjects*\\DDrawDriverObjectListMutex",
L"*\\BaseNamedObjects*\\__DDrawExclMode__",
L"*\\BaseNamedObjects*\\__DDrawCheckExclMode__",
L"*\\BaseNamedObjects*\\DDrawWindowListMutex",
L"*\\BaseNamedObjects*\\DDrawCheckFullscreenSemaphore",
L"*\\BaseNamedObjects*\\D3D9CheckFullscreenSemaphore",
L"*\\BaseNamedObjects*\\WinMMConsoleAudioEvent",
L"*\\BaseNamedObjects*\\SYSTEM_AUDIO_STREAM_*", // AudioDg
// following resource is needed on Windows Vista and later,
// but also provided by the SRS Audio Sandbox program
L"*\\BaseNamedObjects*\\AudioEngineDuplicateHandleApiPort*",
// nComputing audio server/driver ?
L"\\RPC Control\\NCWTSAudioServer",
//
// printer
//
L"\\RPC Control\\spoolss",
L"*\\BaseNamedObjects*\\EPSON-PrgMtr-*",
L"*\\BaseNamedObjects*\\RouterPreInitEvent",
//
// sandboxie service
//
L"\\RPC Control\\" SBIESVC L"Port",
L"*\\BaseNamedObjects*\\" SANDBOXIE L"_StartMenu_WorkArea_*",
//
// third party
//
L"*\\BaseNamedObjects*\\ATITRAY_SMEM",
L"*\\BaseNamedObjects*\\ATITRAY_OSDM",
L"*\\BaseNamedObjects*\\AMCreateListenSock*",
L"*\\BaseNamedObjects*\\AMIPC_*", // Ad Muncher
L"*\\BaseNamedObjects*\\devldr32", // DevLdr32 sound card driver
L"*\\BaseNamedObjects*\\ThreatfireApiHookIpc2Map", // ThreatFire
//
// third party - key-logger-related
//
L"*\\BaseNamedObjects*\\00MemoryShareKeyloggerHunter",
//
// hardware - wacom tablet
//
L"*\\BaseNamedObjects*\\WacomNewFrontAppEventName",
L"*\\BaseNamedObjects*\\WacomTouchingAppNameMutexName",
//
// end
//
L"\\...\\*", // objects in an unnamed directory
NULL
};
static const WCHAR* openpaths_vista[] = {
//
// misc services (vista)
//
L"\\RPC Control\\SLCTransportEndpoint-*", // licensing
L"\\RPC Control\\wpcsvc", // parental controls
L"*\\BaseNamedObjects*\\BFE_Notify_Event_*", // firewall
//
// multimedia (vista)
//
L"\\UxSmsApiPort",
L"\\MmcssApiPort",
L"*\\BaseNamedObjects*\\Dwm-*-ApiPort-*", // aero
L"*\\BaseNamedObjects*\\DwmDxBltEvent*", // aero
L"\\RPC Control\\AudioClientRpc",
#ifdef _WIN64
//
// 64-bit spooler for 32-bit programs
//
L"*\\BaseNamedObjects*\\WinSpl64To32Mutex*_0",
L"\\RPC Control\\splwow64_*_0",
L"\\RPC Control\\umpdproxy_*_0",
#endif _WIN64
NULL
};
static const WCHAR* openpaths_windows7[] = {
L"\\ConsoleEvent-0x*",
L"*\\BaseNamedObjects*\\ConsoleEvent-0x*",
L"\\RPC Control\\console-0x*-lpc-handle",
L"\\RPC Control\\ConsoleEvent-0x*",
L"\\RPC Control\\ConsoleLPC-0x*",
L"\\RPC Control\\lsapolicylookup",
L"\\RPC Control\\lsasspirpc",
L"\\RPC Control\\LSARPC_ENDPOINT",
L"\\RPC Control\\umpo",
L"*\\BaseNamedObjects*\\FlipEx*",
L"*\\BaseNamedObjects*\\FontCachePort",
L"*\\BaseNamedObjects*\\FntCache-*",
NULL
};
static const WCHAR* openpaths_windows8[] = {
L"\\Windows\\Theme*",
L"\\Sessions\\*\\Windows\\Theme*",
L"\\Sessions\\*\\Windows\\DwmApiPort",
L"*\\BaseNamedObjects*\\DWM_DX_FULLSCREEN_TRANSITION_EVENT",
#ifdef _WIN64
//
// 64-bit spooler for 32-bit programs
//
L"*\\BaseNamedObjects*\\WinSpl64To32Mutex*_2000",
L"\\RPC Control\\splwow64_*_2000",
L"\\RPC Control\\umpdproxy_*_2000",
#endif _WIN64
NULL
};
static const WCHAR* openpaths_windows10[] = {
L"*\\BaseNamedObjects*\\CoreMessagingRegistrar",
L"*\\BaseNamedObjects\\[CoreUI]-*",
// open paths 11
L"*\\BaseNamedObjects\\SM*:WilStaging_*", // 22449.1000 accesses this before sbiedll load
#ifdef _M_ARM64
L"\\{BEC19D6F-D7B2-41A8-860C-8787BB964F2D}", // 22621.819 used by emulated processes
#endif _M_ARM64
NULL
};
#ifdef USE_MATCH_PATH_EX
//static const WCHAR *normalpaths[] = {
// NULL
//};
#endif
static const WCHAR *readpaths[] = {
L"\\??\\pipe\\*",
L"$:explorer.exe",
NULL
};
#endif
ULONG i;
BOOLEAN ok;
//
// normal paths
//
#ifdef USE_MATCH_PATH_EX
ok = Process_GetPaths(proc, &proc->normal_ipc_paths, proc->box->name, _NormalPath, FALSE);
#ifdef USE_TEMPLATE_PATHS
if (ok)
ok = Process_GetTemplatePaths(proc, &proc->normal_ipc_paths, _NormalPath);
#else
//if (ok && proc->use_privacy_mode) {
// for (i = 0; normalpaths[i] && ok; ++i) {
// ok = Process_AddPath(proc, &proc->normal_ipc_paths, NULL,
// TRUE, normalpaths[i], FALSE);
// }
//}
#endif
if (!ok) {
Log_MsgP1(MSG_INIT_PATHS, _NormalPath, proc->pid);
return FALSE;
}
#endif
//
// open paths
//
ok = Process_GetPaths(proc, &proc->open_ipc_paths, proc->box->name, _OpenPath, FALSE);
#ifdef USE_TEMPLATE_PATHS
if (ok)
ok = Process_GetTemplatePaths(proc, &proc->open_ipc_paths, _OpenPath);
#else
//
// if configuration option OpenProtectedStorage applies,
// then allow access to ProtectedStorage objects
//
//if (ok && Conf_Get_Boolean(
// proc->box->name, Driver_OpenProtectedStorage, 0, FALSE)) {
//
// static const WCHAR *_PstEvent =
// L"*\\BaseNamedObjects*\\PS_SERVICE_STARTED";
// static const WCHAR *_PstPort =
// L"\\RPC Control\\protected_storage";
//
// ok = Process_AddPath(
// proc, &proc->open_ipc_paths, NULL, TRUE, _PstEvent, FALSE);
// if (ok) {
// ok = Process_AddPath(
// proc, &proc->open_ipc_paths, NULL, TRUE, _PstPort, FALSE);
// }
//}
//
// add default/built-in open paths
//
if (ok) {
for (i = 0; openpaths[i] && ok; ++i) {
ok = Process_AddPath(proc, &proc->open_ipc_paths, NULL,
TRUE, openpaths[i], FALSE);
}
}
if (ok && Driver_OsVersion >= DRIVER_WINDOWS_VISTA) {
for (i = 0; openpaths_vista[i] && ok; ++i) {
ok = Process_AddPath(proc, &proc->open_ipc_paths, NULL,
TRUE, openpaths_vista[i], FALSE);
}
}
if (ok && Driver_OsVersion >= DRIVER_WINDOWS_7) {
for (i = 0; openpaths_windows7[i] && ok; ++i) {
ok = Process_AddPath(proc, &proc->open_ipc_paths, NULL,
TRUE, openpaths_windows7[i], FALSE);
}
}
if (ok && Driver_OsVersion >= DRIVER_WINDOWS_8) {
for (i = 0; openpaths_windows8[i] && ok; ++i) {
ok = Process_AddPath(proc, &proc->open_ipc_paths, NULL,
TRUE, openpaths_windows8[i], FALSE);
}
}
if (ok && Driver_OsVersion >= DRIVER_WINDOWS_10) {
for (i = 0; openpaths_windows10[i] && ok; ++i) {
ok = Process_AddPath(proc, &proc->open_ipc_paths, NULL,
TRUE, openpaths_windows10[i], FALSE);
}
}
#endif
if (! ok) {
Log_MsgP1(MSG_INIT_PATHS, _OpenPath, proc->pid);
return FALSE;
}
//
// closed paths
//
ok = Process_GetPaths(proc, &proc->closed_ipc_paths, proc->box->name, _ClosedPath, FALSE);
#ifdef USE_TEMPLATE_PATHS
if (ok)
ok = Process_GetTemplatePaths(proc, &proc->closed_ipc_paths, _ClosedPath);
#endif
if (! ok) {
Log_MsgP1(MSG_INIT_PATHS, _ClosedPath, proc->pid);
return FALSE;
}
//
// read-only paths
//
ok = Process_GetPaths(proc, &proc->read_ipc_paths, proc->box->name, _ReadPath, FALSE);
#ifdef USE_TEMPLATE_PATHS
if (ok)
ok = Process_GetTemplatePaths(proc, &proc->read_ipc_paths, _ReadPath);
#else
if (ok) {
for (i = 0; readpaths[i] && ok; ++i) {
ok = Process_AddPath(proc, &proc->read_ipc_paths, NULL,
TRUE, readpaths[i], FALSE);
}
}
#endif
if (! ok) {
Log_MsgP1(MSG_INIT_PATHS, _ReadPath, proc->pid);
return FALSE;
}
proc->ipc_namespace_isoaltion = Conf_Get_Boolean(proc->box->name, L"NtNamespaceIsolation", 0, TRUE);
//
// other options
//
proc->ipc_warn_startrun = Conf_Get_Boolean(
proc->box->name, L"NotifyStartRunAccessDenied", 0, TRUE);
proc->ipc_warn_open_proc = Conf_Get_Boolean(
proc->box->name, L"NotifyProcessAccessDenied", 0, FALSE);
//
// block password
//
proc->ipc_block_password =
Conf_Get_Boolean(proc->box->name, L"BlockPassword", 0, TRUE); // OpenLsaSSPI (Security Support Provider Interface)
proc->ipc_open_lsa_endpoint =
Conf_Get_Boolean(proc->box->name, L"OpenLsaEndpoint", 0, FALSE);
proc->ipc_open_sam_endpoint =
Conf_Get_Boolean(proc->box->name, L"OpenSamEndpoint", 0, FALSE);
proc->ipc_allowSpoolerPrintToFile =
Conf_Get_Boolean(proc->box->name, L"AllowSpoolerPrintToFile", 0, FALSE);
proc->ipc_openPrintSpooler =
Conf_Get_Boolean(proc->box->name, L"OpenPrintSpooler", 0, FALSE);
//
// if process is launched as a COM server process by DcomLaunch service
// outside the sandbox, then we will need to restart it as Start.exe
// (see also Custom_ComServer in core/dll/custom.c)
//
if (ok && Ipc_IsComServer(proc)) {
proc->untouchable = TRUE;
}
//
// finish
//
return TRUE;
}
//---------------------------------------------------------------------------
// Ipc_IsComServer
//---------------------------------------------------------------------------
_FX BOOLEAN Ipc_IsComServer(PROCESS *proc)
{
HANDLE ParentId;
PROCESS *pproc;
//
// we conclude this is a COM server process if...
//
// - the process is forced
//
if (! proc->forced_process)
return FALSE;
//
// - the executable image is iexplore.exe or wmplayer.exe or winamp.exe
// or kmplayer.exe (from outside the sandbox)
//
if (proc->image_from_box)
return FALSE;
// $Workaround$ - 3rd party fix
if (_wcsicmp(proc->image_name, L"iexplore.exe") != 0 &&
_wcsicmp(proc->image_name, L"wmplayer.exe") != 0 &&
_wcsicmp(proc->image_name, L"winamp.exe") != 0 &&
_wcsicmp(proc->image_name, L"kmplayer.exe") != 0) {
return FALSE;
}
//
// - parent is not sandboxed
//
MyGetParentId(&ParentId);
if (! ParentId)
return FALSE;
pproc = Process_Find(ParentId, NULL);
if (pproc)
return FALSE;
//
// - parent process is a system process
//
if (! MyIsProcessRunningAsSystemAccount(ParentId))
return FALSE;
//
// process is most likely a COM server process
//
return TRUE;
}
//---------------------------------------------------------------------------
// Ipc_InitProcess
//---------------------------------------------------------------------------
_FX BOOLEAN Ipc_InitProcess(PROCESS *proc)
{
BOOLEAN ok = Ipc_InitPaths(proc);
//
// finish
//
return ok;
}
//---------------------------------------------------------------------------
// Ipc_IsRunRestricted
//---------------------------------------------------------------------------
_FX BOOLEAN Ipc_IsRunRestricted(PROCESS *proc)
{
//
// check Start/Run restrictions
// issue message SBIE1308 when Start/Run restrictions apply
//
PATTERN *pattern = List_Head(&proc->closed_ipc_paths);
while (pattern) {
const WCHAR *source = Pattern_Source(pattern);
if (source[0] == L'*' && source[1] == L'\0') {
if (proc->ipc_warn_startrun) {
Process_LogMessage(proc, MSG_STARTRUN_ACCESS_DENIED);
proc->ipc_warn_startrun = FALSE;
}
return TRUE;
}
pattern = List_Next(pattern);
}
return FALSE;
}
//---------------------------------------------------------------------------
// Ipc_CheckGenericObject
//---------------------------------------------------------------------------
_FX NTSTATUS Ipc_CheckGenericObject(
PROCESS *proc, void *Object, UNICODE_STRING *Name,
ULONG Operation, ACCESS_MASK GrantedAccess)
{
NTSTATUS status;
BOOLEAN IsBoxedPath;
POBJECT_TYPE ObjectType;
ObjectType = pObGetObjectType(Object);
// If the client port object is unnamed, check the server port object. This happens with dynamic ports like the spooler and WPAD.
// (and possibly others)
if (!Name->Length)
{
OBJECT_NAME_INFORMATION *ServerPortName;
ULONG NameLength;
status = Obj_GetName(proc->pool, Object, &ServerPortName, &NameLength);
if (ServerPortName && ServerPortName->Name.Buffer && ServerPortName->Name.Length)
{
//DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_ERROR_LEVEL, "Ipc_CheckGenericObject Server Name = %S\n", ServerPortName->Name.Buffer);
Name = &ServerPortName->Name; // use the server name
}
}
//
// allow unconditional access to unnamed objects
//
if (! Name->Length)
return STATUS_SUCCESS;
///
// check if the specified path leads inside the box
//
IsBoxedPath = FALSE;
if (Box_IsBoxedPath(proc->box, ipc, Name))
IsBoxedPath = TRUE;
//
// allow/deny rules:
// if path leads inside the sandbox, we allow access
//
status = STATUS_SUCCESS;
if (! IsBoxedPath) {
const WCHAR *pattern;
#ifdef USE_MATCH_PATH_EX
ULONG mp_flags;
#else
BOOLEAN is_open, is_closed;
#endif
//
// deny access in two cases:
// - if unsandboxed path matches a closed path
// - if unsandboxed path does not match an open path
//
#ifdef USE_MATCH_PATH_EX
mp_flags = Process_MatchPathEx(proc,
Name->Buffer, Name->Length / sizeof(WCHAR), L'i',
&proc->normal_ipc_paths, &proc->open_ipc_paths, &proc->closed_ipc_paths,
NULL, NULL, &pattern);
#else
pattern = Process_MatchPath(
proc->pool,
Name->Buffer, Name->Length / sizeof(WCHAR),
&proc->open_ipc_paths, &proc->closed_ipc_paths,
&is_open, &is_closed);
#endif
//
// KnownDll objects: prevent DELETE access
//
#ifdef USE_MATCH_PATH_EX
if (((mp_flags & TRUE_PATH_MASK) == TRUE_PATH_OPEN_FLAG) && pattern[0] == L'\\' && pattern[1] == L'K'
&& (wcsncmp(pattern, L"\\KnownDlls", 10) == 0)) { // L"\\KnownDlls\\*", L"\\KnownDlls32\\*",
#else
if (is_open && pattern[0] == L'\\' && pattern[1] == L'K'
&& (wcsncmp(pattern, L"\\KnownDlls", 10) == 0)) { // L"\\KnownDlls\\*", L"\\KnownDlls32\\*",
#endif
if (GrantedAccess & (DELETE | SECTION_EXTEND_SIZE))
status = STATUS_ACCESS_DENIED;
}
#ifdef USE_MATCH_PATH_EX
else if (((mp_flags & TRUE_PATH_MASK) != TRUE_PATH_OPEN_FLAG) && ((mp_flags & COPY_PATH_MASK) == COPY_PATH_OPEN_FLAG))
#else
else if (!is_open && !is_closed)
#endif
{
#define IS_OBJECT_TYPE(l,t) (ObjectType && ObjectType->Name.Length == l * sizeof(WCHAR) \
&& ObjectType->Name.Buffer && _wcsnicmp(ObjectType->Name.Buffer, t, l) == 0)
if (IS_OBJECT_TYPE(12, L"SymbolicLink")) {
//
// we enforce only CreateSymbolicLinkObject to use copy paths,
// OpenSymbolicLinkObject can use true paths if the access is read only
//
ACCESS_MASK RestrictedAccess = DELETE | WRITE_OWNER | WRITE_DAC;
RestrictedAccess |= SYMBOLIC_LINK_SET;
if(Operation == OBJ_OP_OPEN && (GrantedAccess & RestrictedAccess) == 0)
#ifdef USE_MATCH_PATH_EX
mp_flags = TRUE_PATH_OPEN_FLAG;
#else
is_open = TRUE;
#endif
}
else if (IS_OBJECT_TYPE(9, L"Directory")) {
//
// we only enforce CreateDirectoryObject/CreateDirectoryObjectEx
//
// it seems that named object creation always does an additional access check
// regardless of what access is granted on the root handle
//
ACCESS_MASK RestrictedAccess = DELETE | WRITE_OWNER | WRITE_DAC;
//RestrictedAccess |= DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY;
if (!proc->ipc_namespace_isoaltion || (Operation == OBJ_OP_OPEN && (GrantedAccess & RestrictedAccess) == 0))
#ifdef USE_MATCH_PATH_EX
mp_flags = TRUE_PATH_OPEN_FLAG;
#else
is_open = TRUE;
#endif
}
else //if (IS_OBJECT_TYPE(9, L"ALPC Port"))
if (Ipc_Dynamic_Ports.pPortLock)
{
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(Ipc_Dynamic_Ports.pPortLock, TRUE);
IPC_DYNAMIC_PORT* port = List_Head(&Ipc_Dynamic_Ports.Ports);
while (port)
{
if (_wcsicmp(Name->Buffer, port->wstrPortName) == 0)
{
// dynamic version of RPC ports, see also ipc_spl.c
// and RpcBindingFromStringBindingW in core/dll/rpcrt.c
#ifdef USE_MATCH_PATH_EX
mp_flags = TRUE_PATH_OPEN_FLAG;
#else
is_open = TRUE;
#endif
break;
}
port = List_Next(port);
}
ExReleaseResourceLite(Ipc_Dynamic_Ports.pPortLock);
KeLeaveCriticalRegion();
}
#undef IS_OBJECT_TYPE
}
#ifdef USE_MATCH_PATH_EX
if ((mp_flags & TRUE_PATH_MASK) == 0 || ((mp_flags & TRUE_PATH_MASK) != TRUE_PATH_OPEN_FLAG))
#else
if (is_closed || (! is_open))
#endif
status = STATUS_ACCESS_DENIED;
}
//
// trace the request if so desired
//
if (proc->ipc_trace & (TRACE_ALLOW | TRACE_DENY)) {
WCHAR access_str[24];
WCHAR letter;
if ((! NT_SUCCESS(status)) && (proc->ipc_trace & TRACE_DENY))
letter = L'D';
else if (NT_SUCCESS(status) && (proc->ipc_trace & TRACE_ALLOW))
letter = L'A';
else
letter = 0;
// $Workaround$ - 3rd party fix
if (letter) {
//
// sysinternals dbgview
//
WCHAR *backslash = wcsrchr(Name->Buffer, L'\\');
if (backslash) {
++backslash;
if (wcscmp(backslash, L"DBWinMutex") == 0 ||
wcscmp(backslash, L"DBWIN_BUFFER") == 0 ||
wcscmp(backslash, L"DBWIN_BUFFER_READY") == 0 ||
wcscmp(backslash, L"DBWIN_DATA_READY") == 0) {
letter = 0;
}
}
}
if (letter) {
ULONG mon_type = MONITOR_IPC;
if (!IsBoxedPath) {
if (NT_SUCCESS(status))
mon_type |= MONITOR_OPEN;
else
mon_type |= MONITOR_DENY;
}
RtlStringCbPrintfW(access_str, sizeof(access_str), L"(I%c) %08X", letter, GrantedAccess);
//Log_Debug_Msg(mon_type, access_str, Name->Buffer);
if (Session_MonitorCount) {
const WCHAR* strings[4] = { Name->Buffer, access_str, ObjectType ? ObjectType->Name.Buffer : NULL, NULL };
Session_MonitorPutEx(mon_type, strings, NULL, PsGetCurrentProcessId(), PsGetCurrentThreadId());
}
}
}
else if (Session_MonitorCount && !proc->disable_monitor) {
ULONG mon_type = MONITOR_IPC;
WCHAR *mon_name = Name->Buffer;
if (IsBoxedPath)
mon_name += proc->box->ipc_path_len / sizeof(WCHAR) - 1;
else if (NT_SUCCESS(status))
mon_type |= MONITOR_OPEN;
else
mon_type |= MONITOR_DENY;
Session_MonitorPut(mon_type, mon_name, proc->pid);
}
// DbgPrint("Process <%06d> Status <%08X> Object <%S>\n", proc->pid, status, Name->Name.Buffer);
return status;
}
//---------------------------------------------------------------------------
// Ipc_CheckPortObject
//---------------------------------------------------------------------------
_FX NTSTATUS Ipc_CheckPortObject(
PROCESS *proc, void *Object, UNICODE_STRING *Name,
ULONG Operation, ACCESS_MASK GrantedAccess)
{
void *PortObject = Ipc_GetServerPort(Object);
if (! PortObject)
return STATUS_SUCCESS;
return Ipc_CheckGenericObject(proc, PortObject, Name, Operation, GrantedAccess);
}
//---------------------------------------------------------------------------
// Ipc_CheckJobObject
//---------------------------------------------------------------------------
_FX NTSTATUS Ipc_CheckJobObject(
PROCESS* proc, void* Object, UNICODE_STRING* Name,
ULONG Operation, ACCESS_MASK GrantedAccess)
{
//
// we don't mind if a program in the sandbox creates or opens a job
// object, but the job object must be named (as opposed to other IPC
// objects), and must not include the right to assign to processes.
// Ipc_CheckGenericObject additionally makes sure the job name path
// is inside the sandbox
//
if (!proc->can_use_jobs) {
if (GrantedAccess & (JOB_OBJECT_ASSIGN_PROCESS | JOB_OBJECT_TERMINATE))
return STATUS_ACCESS_DENIED;
}
if (! Name->Length)
return STATUS_ACCESS_DENIED;
return Ipc_CheckGenericObject(proc, Object, Name, Operation, GrantedAccess);
}
//---------------------------------------------------------------------------
// Ipc_Api_DuplicateObject
//---------------------------------------------------------------------------
_FX NTSTATUS Ipc_Api_DuplicateObject(PROCESS *proc, ULONG64 *parms)
{
API_DUPLICATE_OBJECT_ARGS *args = (API_DUPLICATE_OBJECT_ARGS *)parms;
PEPROCESS OtherProcessObject;
HANDLE OtherProcessHandle;
HANDLE SourceProcessHandle;
HANDLE SourceHandle;
HANDLE TargetProcessHandle;
HANDLE *TargetHandle;
HANDLE DuplicatedHandle;
ULONG DesiredAccess;
ULONG HandleAttributes;
ULONG Options;
NTSTATUS status;
//
// this API must be invoked by a sandboxed process
//
if (! proc)
return STATUS_NOT_IMPLEMENTED;
//
// collect and verify parameters
//
SourceHandle = args->source_handle.val;
TargetHandle = args->target_handle.val;
OtherProcessHandle = args->process_handle.val;
ProbeForWrite(TargetHandle, sizeof(HANDLE), sizeof(ULONG_PTR));
DesiredAccess = args->desired_access.val;
Options = args->options.val;
HandleAttributes = 0;
if (Options & DUPLICATE_INHERIT) {
HandleAttributes |= OBJ_INHERIT;
Options &= ~DUPLICATE_INHERIT;
}
//
// other process must not be sandboxed. this makes sure this API
// is not used for communication by processes running in two
// different sandboxes
//
status = ObReferenceObjectByHandle(OtherProcessHandle, 0, *PsProcessType,
UserMode, &OtherProcessObject, NULL);
if (! NT_SUCCESS(status))
return status;
if (Process_Find(PsGetProcessId(OtherProcessObject), NULL)) {
// other process is sandboxed
ObDereferenceObject(OtherProcessObject);
return STATUS_ACCESS_DENIED;
}
//
// if the caller holds SeDebugPrivilege, we allow duplication from
// any other process. otherwise, only from processes with same SID
//
if (! SeSinglePrivilegeCheck(
RtlConvertLongToLuid(SE_DEBUG_PRIVILEGE), ExGetPreviousMode())) {
UNICODE_STRING SidString;
ULONG SessionId;
status = Process_GetSidStringAndSessionId(
OtherProcessHandle, NULL, &SidString, &SessionId);
if (NT_SUCCESS(status)) {
if (_wcsicmp(proc->box->sid, SidString.Buffer) != 0)
status = STATUS_ACCESS_DENIED;
RtlFreeUnicodeString(&SidString);
}
if (! NT_SUCCESS(status)) {
ObDereferenceObject(OtherProcessObject);
return status;
}
}
//
// open the other process for PROCESS_DUP_HANDLE access
//
status = ObOpenObjectByPointer(
OtherProcessObject, 0, NULL, PROCESS_DUP_HANDLE,
*PsProcessType, UserMode, &OtherProcessHandle);
ObDereferenceObject(OtherProcessObject);
if (! NT_SUCCESS(status))
return status;
if (Options & DUPLICATE_INTO_OTHER) {
SourceProcessHandle = NtCurrentProcess();
TargetProcessHandle = OtherProcessHandle;
Options &= ~DUPLICATE_INTO_OTHER;
} else {
SourceProcessHandle = OtherProcessHandle;
TargetProcessHandle = NtCurrentProcess();
}
//
// set up an exception catching block so we can always close
// the powerful OtherProcessHandle
//
__try {
//
// if the source handle is in the current process, make sure it is
// an unnamed object, before trying to duplicate it. this makes sure
// NtDuplicateObject is used for inter-process exchange of private
// handles, and not to hijack access to objects outside the sandbox
//
if (IS_ARG_CURRENT_PROCESS(SourceProcessHandle)) {
status = Ipc_CheckObjectName(SourceHandle, UserMode);
//
// if the source handle is in another process, we have to duplicate
// it before we can examine it, so duplicate first without the
// DUPLICATE_CLOSE_SOURCE option
//
} else if (IS_ARG_CURRENT_PROCESS(TargetProcessHandle)) {
//
// we duplicate the handle into kernel space such that that user
// won't be able to grab it while we are evaluaiting it
//
HANDLE SourceProcessKernelHandle;
status = Thread_GetKernelHandleForUserHandle(&SourceProcessKernelHandle, SourceProcessHandle);
if (NT_SUCCESS(status)) {
HANDLE TargetProcessKernelHandle = ZwCurrentProcess(); // TargetProcessHandle == NtCurrentProcess();
//
// driver verifier wants us to provide a kernel handle as process handles
// but the source handle must be a user handle and the ZwDuplicateObject
// function creates another user handle hence NtClose
//
status = ZwDuplicateObject(
SourceProcessKernelHandle, SourceHandle,
TargetProcessKernelHandle, &DuplicatedHandle,
DesiredAccess, HandleAttributes,
Options & ~DUPLICATE_CLOSE_SOURCE);
if (NT_SUCCESS(status)) {
status = Ipc_CheckObjectName(DuplicatedHandle, UserMode);
NtClose(DuplicatedHandle);
}
ZwClose(SourceProcessKernelHandle);
}
} else
status = STATUS_INVALID_HANDLE;
//
// if all checks were passed duplicate the handle
//
if (NT_SUCCESS(status)) {
HANDLE SourceProcessKernelHandle = (HANDLE)-1;
HANDLE TargetProcessKernelHandle = (HANDLE)-1;
if (!IS_ARG_CURRENT_PROCESS(SourceProcessHandle))
status = Thread_GetKernelHandleForUserHandle(&SourceProcessKernelHandle, SourceProcessHandle);
if (NT_SUCCESS(status)) {
if (!IS_ARG_CURRENT_PROCESS(TargetProcessHandle))
status = Thread_GetKernelHandleForUserHandle(&TargetProcessKernelHandle, TargetProcessHandle);
if (NT_SUCCESS(status)) {
status = ZwDuplicateObject(
SourceProcessKernelHandle, SourceHandle,
TargetProcessKernelHandle, &DuplicatedHandle,
DesiredAccess, HandleAttributes, Options);
*TargetHandle = DuplicatedHandle;
}
}
if (SourceProcessKernelHandle && !IS_ARG_CURRENT_PROCESS(SourceProcessKernelHandle))
ZwClose(SourceProcessKernelHandle);
if (TargetProcessKernelHandle && !IS_ARG_CURRENT_PROCESS(TargetProcessKernelHandle))
ZwClose(TargetProcessKernelHandle);
}
//
// end exception block and close OtherProcessHandle
//
} __except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
NtClose(OtherProcessHandle);
return status;
}
//---------------------------------------------------------------------------
// Ipc_CheckObjectName
//---------------------------------------------------------------------------
_FX NTSTATUS Ipc_CheckObjectName(HANDLE handle, KPROCESSOR_MODE mode)
{
NTSTATUS status;
OBJECT_TYPE *object;
USHORT TypeLength;
WCHAR *TypeBuffer;
status = ObReferenceObjectByHandle(
handle, 0, NULL, mode, &object, NULL);
if (! NT_SUCCESS(status))
return status;
TypeLength = 0;
TypeBuffer = NULL;
if (Driver_OsVersion >= DRIVER_WINDOWS_7) {
//
// on Windows 7, the new ObQueryNameInfo API returns the offset
// to the OBJECT_HEADER_NAME_INFO structure, or zero if the
// object does not have a name
//
ULONG NameInfoOffset = pObQueryNameInfo(object);
if (! NameInfoOffset) {
//
// use the new ObGetObjectType API to get the object type
//
OBJECT_TYPE_VISTA_SP1 *ObjectType = pObGetObjectType(object);
TypeLength = ObjectType->Name.Length;
TypeBuffer = ObjectType->Name.Buffer;
}
}
#ifdef XP_SUPPORT
else {
//
// on earlier versions of Windows, the object header precedes the
// object body and contains an OBJECT_HEADER_NAME_INFO structure
// and a direct pointer to the object type
//
OBJECT_HEADER *ObjectHeader = OBJECT_TO_OBJECT_HEADER(object);
OBJECT_HEADER_NAME_INFO *NameInfo =
OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
if (! NameInfo) {
if (Driver_OsVersion >= DRIVER_WINDOWS_VISTA &&
Driver_OsBuild > 6000) {
OBJECT_TYPE_VISTA_SP1 *ObjectType =
(OBJECT_TYPE_VISTA_SP1 *)ObjectHeader->Type;
TypeLength = ObjectType->Name.Length;
TypeBuffer = ObjectType->Name.Buffer;
} else {
OBJECT_TYPE *ObjectType = ObjectHeader->Type;
TypeLength = ObjectType->Name.Length;
TypeBuffer = ObjectType->Name.Buffer;
}
}
//DbgPrint("Object %08X Has NameInfo %08X TypeBuffer %*.*S\n", object, NameInfo, TypeLength/sizeof(WCHAR), TypeLength/sizeof(WCHAR), TypeBuffer);
}
#endif
//
// if we have the type name here, it means the object is unnamed,
// and we need to make sure the type name is acceptable
//
status = STATUS_ACCESS_DENIED;
if (TypeLength && TypeBuffer) {
#define IS_OBJECT_TYPE(l,n) \
(TypeLength == l * sizeof(WCHAR) && \
_wcsnicmp(TypeBuffer, n, l) == 0)
if (IS_OBJECT_TYPE( 5,Ipc_Event_TypeName) ||
IS_OBJECT_TYPE( 9,Ipc_EventPair_TypeName) ||
IS_OBJECT_TYPE(10,Ipc_KeyedEvent_TypeName) ||
IS_OBJECT_TYPE( 6,Ipc_Mutant_TypeName) ||
IS_OBJECT_TYPE( 9,Ipc_Semaphore_TypeName) ||
IS_OBJECT_TYPE( 7,Ipc_Section_TypeName) ) {
status = STATUS_SUCCESS;
}
#undef IS_OBJECT_TYPE
}
//
// finish
//
ObDereferenceObject(object);
return status;
}
//---------------------------------------------------------------------------
// Ipc_Api_CreateDirOrLink
//---------------------------------------------------------------------------
_FX NTSTATUS Ipc_Api_CreateDirOrLink(PROCESS *proc, ULONG64 *parms)
{
API_CREATE_DIR_OR_LINK_ARGS *args =
(API_CREATE_DIR_OR_LINK_ARGS *)parms;
NTSTATUS status;
HANDLE handle;
UNICODE_STRING64 *user_uni;
WCHAR *user_buf, *objname_buf = NULL, *target_buf;
ULONG user_len, objname_len, target_len;
OBJECT_ATTRIBUTES objattrs;
UNICODE_STRING objname, target;
//
// this API must be invoked by a sandboxed process
//
if (! proc)
return STATUS_NOT_IMPLEMENTED;
status = STATUS_SUCCESS;
handle = NULL;
//
// copy first user parameter: objname
//
user_uni = (UNICODE_STRING64 *)args->objname.val;
ProbeForRead(user_uni, sizeof(UNICODE_STRING64), sizeof(ULONG_PTR));
user_len = user_uni->Length;
user_buf = (WCHAR *)(ULONG_PTR)user_uni->Buffer;
if (user_len >= sizeof(WCHAR) && user_len < 2048 && user_buf) {
objname_len = user_len & ~1;
ProbeForRead(user_buf, objname_len, sizeof(WCHAR));
objname_buf = Mem_Alloc(proc->pool, objname_len + sizeof(WCHAR));
if (! objname_buf)
status = STATUS_INSUFFICIENT_RESOURCES;
else {
memcpy(objname_buf, user_buf, objname_len);
objname_buf[objname_len / sizeof(WCHAR)] = L'\0';
}
} else
status = STATUS_INVALID_PARAMETER;
if (! NT_SUCCESS(status))
return status;
//
// copy second user parameter: target. note that if we fail here,
// we don't even bother freeing up memory, since its allocated
// to the pool of the sandboxed process
//
target_buf = NULL;
target_len = 0;
user_uni = (UNICODE_STRING64 *)args->target.val;
if (user_uni) {
ProbeForRead(user_uni, sizeof(UNICODE_STRING64), sizeof(ULONG_PTR));
user_len = user_uni->Length;
user_buf = (WCHAR *)(ULONG_PTR)user_uni->Buffer;
if (user_len >= sizeof(WCHAR) && user_len < 2048 && user_buf) {
target_len = user_len & ~1;
ProbeForRead(user_buf, target_len, sizeof(WCHAR));
target_buf = Mem_Alloc(proc->pool, target_len + sizeof(WCHAR));
if (! target_buf)
status = STATUS_INSUFFICIENT_RESOURCES;
else {
memcpy(target_buf, user_buf, target_len);
target_buf[target_len / sizeof(WCHAR)] = L'\0';
}
} else
status = STATUS_INVALID_PARAMETER;
}
if (! NT_SUCCESS(status)) {
Mem_Free(objname_buf, objname_len + sizeof(WCHAR));
return status;
}
//
// make sure the paths are within the sandbox
//
InitializeObjectAttributes(
&objattrs, &objname,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, // | OBJ_PERMANENT,
NULL, Driver_PublicSd);
RtlInitUnicodeString(&objname, objname_buf);
if (! Box_IsBoxedPath(proc->box, ipc, &objname))
status = STATUS_ACCESS_DENIED;
else if (target_buf) {
RtlInitUnicodeString(&target, target_buf);
if (! Box_IsBoxedPath(proc->box, ipc, &target))
status = STATUS_ACCESS_DENIED;
else {
status = ZwCreateSymbolicLinkObject(
&handle, SYMBOLIC_LINK_ALL_ACCESS, &objattrs, &target);
}
Mem_Free(target_buf, target_len + sizeof(WCHAR));
} else {
status = ZwCreateDirectoryObject(
&handle, DIRECTORY_ALL_ACCESS, &objattrs);
if (NT_SUCCESS(status)) {
if (Driver_LowLabelSd) {
ZwSetSecurityObject(
handle, LABEL_SECURITY_INFORMATION, Driver_LowLabelSd);
}
}
}
if (handle != NULL) {
//ZwClose(handle);
KIRQL irql;
KeRaiseIrql(APC_LEVEL, &irql);
ExAcquireResourceExclusiveLite(Ipc_DirLock, TRUE);
DIR_OBJ_HANDLE *obj_handle = Mem_Alloc(Driver_Pool, sizeof(DIR_OBJ_HANDLE));
obj_handle->handle = handle;
List_Insert_After(&Ipc_ObjDirs, NULL, obj_handle);
ExReleaseResourceLite(Ipc_DirLock);
KeLowerIrql(irql);
}
Mem_Free(objname_buf, objname_len + sizeof(WCHAR));
if (status == STATUS_OBJECT_NAME_COLLISION)
status = STATUS_SUCCESS;
return status;
}
//---------------------------------------------------------------------------
// Ipc_Api_OpenDeviceMap
//---------------------------------------------------------------------------
_FX NTSTATUS Ipc_Api_OpenDeviceMap(PROCESS *proc, ULONG64 *parms)
{
API_OPEN_DEVICE_MAP_ARGS *args =
(API_OPEN_DEVICE_MAP_ARGS *)parms;
NTSTATUS status;
HANDLE handle;
UNICODE_STRING objname;
OBJECT_ATTRIBUTES objattrs;
//
// this API must be invoked by a sandboxed process
//
if (! proc)
return STATUS_NOT_IMPLEMENTED;
//
// open the device map for the current process
//
InitializeObjectAttributes(
&objattrs, &objname, OBJ_CASE_INSENSITIVE, NULL, NULL);
RtlInitUnicodeString(&objname, L"\\??");
status = ZwOpenDirectoryObject(&handle, 0, &objattrs);
if (! NT_SUCCESS(status))
return status;
//
// copy handle into user space
//
__try {
HANDLE *user_handle = args->handle.val;
ProbeForRead(user_handle, sizeof(HANDLE), sizeof(HANDLE));
*user_handle = handle;
} __except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
if (! NT_SUCCESS(status))
NtClose(handle);
return status;
}
//---------------------------------------------------------------------------
// Ipc_Api_QuerySymbolicLink
//---------------------------------------------------------------------------
_FX NTSTATUS Ipc_Api_QuerySymbolicLink(PROCESS *proc, ULONG64 *parms)
{
API_QUERY_SYMBOLIC_LINK_ARGS *args =
(API_QUERY_SYMBOLIC_LINK_ARGS *)parms;
OBJECT_ATTRIBUTES objattrs;
UNICODE_STRING objname;
HANDLE handle;
WCHAR *buf;
WCHAR *user_buf;
ULONG user_len;
NTSTATUS status;
//
// this API must be invoked by a sandboxed process
//
if (! proc)
return STATUS_NOT_IMPLEMENTED;
//
// check input buffers
//
user_buf = args->name_buf.val;
user_len = args->name_len.val / sizeof(WCHAR);
if ((! user_buf) || (! user_len) || (user_len > 4096))
return STATUS_INVALID_PARAMETER;
//
// copy user object name into kernel buffer
//
buf = Mem_Alloc(proc->pool, (user_len + 8) * sizeof(WCHAR));
if (! buf)
return STATUS_INSUFFICIENT_RESOURCES;
ProbeForRead(user_buf, sizeof(WCHAR) * user_len, sizeof(WCHAR));
wmemcpy(buf, user_buf, user_len);
buf[user_len] = L'\0';
RtlInitUnicodeString(&objname, buf);
InitializeObjectAttributes(&objattrs,
&objname, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
// DbgPrint("Process %06d ||| Query <%S>\n", PsGetCurrentProcessId(), buf);
//
// open and query symbolic link into kernel buffer
//
status = ZwOpenSymbolicLinkObject(
&handle, SYMBOLIC_LINK_QUERY, &objattrs);
if (NT_SUCCESS(status)) {
objname.Length = (USHORT)(user_len * sizeof(WCHAR));
objname.MaximumLength = objname.Length;
objname.Buffer = buf;
status = ZwQuerySymbolicLinkObject(handle, &objname, NULL);
ZwClose(handle);
}
//
// write kernel buffer into user buffer
//
if (NT_SUCCESS(status)) {
__try {
ULONG len = objname.Length / sizeof(WCHAR);
if (len >= user_len - 1)
status = STATUS_BUFFER_TOO_SMALL;
else {
buf[len] = L'\0';
ProbeForRead(
user_buf, sizeof(WCHAR) * (len + 1), sizeof(WCHAR));
wmemcpy(user_buf, buf, len + 1);
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
}
// DbgPrint("Process %06d ||| Status <%08X>\n", PsGetCurrentProcessId(), status);
Mem_Free(buf, (user_len + 8) * sizeof(WCHAR));
return status;
}
//---------------------------------------------------------------------------
// Api_Unload
//---------------------------------------------------------------------------
_FX void Ipc_Unload(void)
{
if (Ipc_Dynamic_Ports.pPortLock)
Mem_FreeLockResource(&Ipc_Dynamic_Ports.pPortLock);
if (Ipc_DirLock == NULL)
return; // Early driver initialization failed
DIR_OBJ_HANDLE* obj_handle = List_Head(&Ipc_ObjDirs);
while (obj_handle) {
ZwClose(obj_handle->handle);
obj_handle = List_Next(obj_handle);
}
Mem_FreeLockResource(&Ipc_DirLock);
}