#include "sandbox/win/src/process_thread_interception.h"
#include <stdint.h>
#include <optional>
#include "sandbox/win/src/crosscall_client.h"
#include "sandbox/win/src/ipc_tags.h"
#include "sandbox/win/src/policy_params.h"
#include "sandbox/win/src/policy_target.h"
#include "sandbox/win/src/sandbox_factory.h"
#include "sandbox/win/src/sandbox_nt_util.h"
#include "sandbox/win/src/sharedmem_ipc_client.h"
#include "sandbox/win/src/target_services.h"
namespace sandbox {
namespace {
NTSTATUS DuplicateObject(HANDLE handle,
ACCESS_MASK desired_access,
PHANDLE out_handle) {
if (desired_access & MAXIMUM_ALLOWED) {
desired_access |= GENERIC_ALL;
}
return GetNtExports()->DuplicateObject(CURRENT_PROCESS, handle,
CURRENT_PROCESS, out_handle,
desired_access, 0, 0);
}
template <typename T>
std::optional<T> CaptureParameter(const T* parameter) {
if (parameter) {
__try {
return *parameter;
} __except (EXCEPTION_EXECUTE_HANDLER) {
}
}
return std::nullopt;
}
bool ValidObjectAttributes(const OBJECT_ATTRIBUTES* object_attributes) {
if (!object_attributes) {
return true;
}
std::optional<OBJECT_ATTRIBUTES> valid_obj_attr =
CaptureParameter(object_attributes);
return valid_obj_attr.has_value() && !valid_obj_attr->Attributes &&
!valid_obj_attr->ObjectName && !valid_obj_attr->RootDirectory &&
!valid_obj_attr->SecurityDescriptor &&
!valid_obj_attr->SecurityQualityOfService;
}
NTSTATUS CallNtOpenProcessTokenEx(NTSTATUS status,
HANDLE process,
ACCESS_MASK desired_access,
ULONG handle_attributes,
PHANDLE token) {
if (NT_SUCCESS(status)) {
return status;
}
if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) {
return status;
}
if (CURRENT_PROCESS != process ||
!ValidParameter(token, sizeof(HANDLE), WRITE)) {
return status;
}
void* memory = GetGlobalIPCMemory();
if (!memory) {
return status;
}
SharedMemIPCClient ipc(memory);
CrossCallReturn answer = {0};
ResultCode code = CrossCall(ipc, IpcTag::NTOPENPROCESSTOKENEX, process,
desired_access, handle_attributes, &answer);
if (SBOX_ALL_OK != code) {
return status;
}
if (!NT_SUCCESS(answer.nt_status)) {
return answer.nt_status;
}
__try {
*token = answer.handle;
} __except (EXCEPTION_EXECUTE_HANDLER) {
return status;
}
return answer.nt_status;
}
}
NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread,
PHANDLE thread,
ACCESS_MASK desired_access,
POBJECT_ATTRIBUTES object_attributes,
PCLIENT_ID client_id) {
NTSTATUS status =
orig_OpenThread(thread, desired_access, object_attributes, client_id);
if (NT_SUCCESS(status)) {
return status;
}
if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) {
return status;
}
if (!ValidParameter(thread, sizeof(HANDLE), WRITE) ||
!ValidObjectAttributes(object_attributes)) {
return status;
}
std::optional<CLIENT_ID> valid_client_id = CaptureParameter(client_id);
if (!valid_client_id.has_value() || valid_client_id->UniqueProcess) {
return status;
}
if (valid_client_id->UniqueThread == GetCurrentClientId().UniqueThread) {
return DuplicateObject(CURRENT_THREAD, desired_access, thread);
}
void* memory = GetGlobalIPCMemory();
if (!memory) {
return status;
}
SharedMemIPCClient ipc(memory);
CrossCallReturn answer = {0};
ResultCode code = CrossCall(ipc, IpcTag::NTOPENTHREAD, desired_access,
static_cast<uint32_t>(reinterpret_cast<ULONG_PTR>(
valid_client_id->UniqueThread)),
&answer);
if (SBOX_ALL_OK != code) {
return status;
}
if (!NT_SUCCESS(answer.nt_status)) {
return status;
}
__try {
*thread = answer.handle;
} __except (EXCEPTION_EXECUTE_HANDLER) {
return status;
}
return answer.nt_status;
}
NTSTATUS WINAPI TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess,
PHANDLE process,
ACCESS_MASK desired_access,
POBJECT_ATTRIBUTES object_attributes,
PCLIENT_ID client_id) {
NTSTATUS status =
orig_OpenProcess(process, desired_access, object_attributes, client_id);
if (NT_SUCCESS(status)) {
return status;
}
if (!ValidObjectAttributes(object_attributes)) {
return status;
}
std::optional<CLIENT_ID> valid_client_id = CaptureParameter(client_id);
if (!valid_client_id.has_value() ||
valid_client_id->UniqueProcess != GetCurrentClientId().UniqueProcess) {
return status;
}
return DuplicateObject(CURRENT_PROCESS, desired_access, process);
}
NTSTATUS WINAPI
TargetNtOpenProcessToken(NtOpenProcessTokenFunction orig_OpenProcessToken,
HANDLE process,
ACCESS_MASK desired_access,
PHANDLE token) {
return CallNtOpenProcessTokenEx(
orig_OpenProcessToken(process, desired_access, token), process,
desired_access, 0, token);
}
NTSTATUS WINAPI
TargetNtOpenProcessTokenEx(NtOpenProcessTokenExFunction orig_OpenProcessTokenEx,
HANDLE process,
ACCESS_MASK desired_access,
ULONG handle_attributes,
PHANDLE token) {
return CallNtOpenProcessTokenEx(
orig_OpenProcessTokenEx(process, desired_access, handle_attributes,
token),
process, desired_access, handle_attributes, token);
}
HANDLE WINAPI TargetCreateThread(CreateThreadFunction orig_CreateThread,
LPSECURITY_ATTRIBUTES thread_attributes,
SIZE_T stack_size,
LPTHREAD_START_ROUTINE start_address,
LPVOID parameter,
DWORD creation_flags,
LPDWORD thread_id) {
HANDLE hThread = nullptr;
TargetServices* target_services = SandboxFactory::GetTargetServices();
if (!target_services || target_services->GetState()->IsCsrssConnected()) {
hThread = orig_CreateThread(thread_attributes, stack_size, start_address,
parameter, creation_flags, thread_id);
if (hThread)
return hThread;
}
DWORD original_error = ::GetLastError();
do {
if (!target_services)
break;
if (!target_services->GetState()->InitCalled())
break;
__try {
if (thread_id && !ValidParameter(thread_id, sizeof(*thread_id), WRITE))
break;
if (!start_address)
break;
if (thread_attributes)
break;
} __except (EXCEPTION_EXECUTE_HANDLER) {
break;
}
void* memory = GetGlobalIPCMemory();
if (!memory)
break;
SharedMemIPCClient ipc(memory);
CrossCallReturn answer = {0};
ResultCode code = CrossCall(ipc, IpcTag::CREATETHREAD,
reinterpret_cast<LPVOID>(stack_size),
reinterpret_cast<LPVOID>(start_address),
parameter, creation_flags, &answer);
if (SBOX_ALL_OK != code)
break;
::SetLastError(answer.win32_result);
if (ERROR_SUCCESS != answer.win32_result)
return nullptr;
__try {
if (thread_id)
*thread_id = ::GetThreadId(answer.handle);
return answer.handle;
} __except (EXCEPTION_EXECUTE_HANDLER) {
break;
}
} while (false);
::SetLastError(original_error);
return nullptr;
}
}