Sandboxie/Sandboxie/core/drv/hook.c

371 lines
9.7 KiB
C

/*
* Copyright 2004-2020 Sandboxie Holdings, LLC
* Copyright 2020 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/>.
*/
//---------------------------------------------------------------------------
// Hook Management
//---------------------------------------------------------------------------
#define HOOK_WITH_PRIVATE_PARTS
#include "hook.h"
#include "dll.h"
#include "util.h"
#define KERNEL_MODE
#include "../dll/hook_inst.c"
#include "../dll/hook_tramp.c"
//---------------------------------------------------------------------------
// Defines
//---------------------------------------------------------------------------
#define NUM_TARGET_PAGES 4
#define MAX_DEPTH 10
//---------------------------------------------------------------------------
// Functions
//---------------------------------------------------------------------------
static LONG Hook_FindSyscall2(
void *addr, int depth, ULONG_PTR *targets, BOOLEAN check_target,
LONG eax, LONG *skip);
static LONG Hook_CheckSkippedSyscall(LONG eax, LONG *skip);
//---------------------------------------------------------------------------
#pragma alloc_text (INIT, Hook_GetServiceIndex)
#pragma alloc_text (INIT, Hook_FindSyscall2)
//---------------------------------------------------------------------------
// Hook_GetService
//---------------------------------------------------------------------------
_FX BOOLEAN Hook_GetService(
void *DllProc, LONG *SkipIndexes, ULONG ParamCount,
void **NtService, void **ZwService)
{
LONG svc_num;
void *svc_addr;
if (NtService)
*NtService = NULL;
if (ZwService)
*ZwService = NULL;
if ((ULONG_PTR)DllProc < 0x10000) {
svc_num = (ULONG)(ULONG_PTR)DllProc;
} else {
svc_num = Hook_GetServiceIndex(DllProc, SkipIndexes);
if (svc_num == -1)
return FALSE;
}
svc_addr = Hook_GetNtServiceInternal(svc_num, ParamCount);
if (! svc_addr)
return FALSE;
if (NtService)
*NtService = svc_addr;
if (ZwService) {
svc_addr = Hook_GetZwServiceInternal(svc_num);
if (! svc_addr)
return FALSE;
*ZwService = svc_addr;
}
return TRUE;
}
//---------------------------------------------------------------------------
// Hook_GetServiceIndex
//---------------------------------------------------------------------------
_FX LONG Hook_GetServiceIndex(void *DllProc, LONG *SkipIndexes)
{
static LONG SkipIndexes_default[1] = { 0 };
ULONG_PTR *targets;
LONG eax;
if (! SkipIndexes)
SkipIndexes = SkipIndexes_default;
targets = (ULONG_PTR *)ExAllocatePoolWithTag(
PagedPool, PAGE_SIZE * NUM_TARGET_PAGES, tzuk);
if (! targets) {
Log_Msg0(MSG_1104);
return -1;
}
targets[0] = 0;
eax = Hook_FindSyscall2(DllProc, 1, targets, TRUE, -1, SkipIndexes);
ExFreePoolWithTag(targets, tzuk);
if ((eax != -1) && (eax < 0x2000) && ((eax & 0xFFF) < 0x600))
return eax;
return -1;
}
//---------------------------------------------------------------------------
// Hook_FindSyscall2
//---------------------------------------------------------------------------
_FX LONG Hook_FindSyscall2(
void *addr, int depth, ULONG_PTR *targets, BOOLEAN check_target,
LONG eax, LONG *skip)
{
HOOK_INST inst;
ULONG edx;
ULONG i;
BOOLEAN is64;
ULONG count;
#ifdef _WIN64 // 64-bit
is64 = TRUE;
#else // 32-bit
is64 = FALSE;
#endif // xx-bit
if (depth > MAX_DEPTH)
return -1;
//
// for each start address, we record it in our list of jump targets,
// so we can ignore it the next time we have to process that address.
//
// note that check_target is FALSE when we enter a recursive call
// after processing "call edx" or "call [edx]", see below. in this
// case we don't check jump targets because we want to re-analyse
// the sequence leading to SYSENTER every time
//
#ifndef _WIN64
//
// if the target is near the system limit, then it is probably
// SharedUserData and we want to process it every time.
// (again see below for processing of call edx)
//
if (check_target) {
if (((ULONG)addr & 0xF0000000) >= 0x70000000)
check_target = FALSE;
}
#endif ! _WIN64
if (check_target) {
for (i = 0; i < (ULONG)targets[0]; ++i)
if (targets[i] == (ULONG_PTR)addr)
return -1;
++i;
if (i > (PAGE_SIZE * NUM_TARGET_PAGES / sizeof(ULONG_PTR) - 8))
return -1;
targets[i] = (ULONG_PTR)addr;
targets[0] = (ULONG)i;
}
check_target = TRUE;
//
// analyze instructions beginning at the passed address
//
for (count = 0; count < 1024; ++count) {
BOOLEAN ok = Hook_Analyze(addr, TRUE, is64, &inst);
// DbgPrint("%04d At Address %p Ok=%d inst=%02X%02X inst.kind=%d parm=%p\n", depth, addr, ok, inst.op1, inst.op2, inst.kind, (void *)inst.parm);
if ((! ok) || inst.kind == INST_RET || inst.kind == INST_JUMP_MEM)
return -1;
if (inst.kind == INST_SYSCALL) {
eax = Hook_CheckSkippedSyscall(eax, skip);
if (eax != -1)
return eax;
}
#ifndef _WIN64
if (inst.kind == INST_CTLXFER_REG) {
//
// if this instruction is "call edx" or "call [edx]", and
// last instruction we processed was "mov edx, imm32", then
// branch to the following address. (this is used in 32-bit
// Windows code to jump to SharedUserData!SystemCallStub).
//
if (inst.op1 == 0xFF && inst.op2 == 0xD2) {
inst.kind = INST_CTLXFER;
inst.parm = edx;
check_target = FALSE;
} else if (inst.op1 == 0xFF && inst.op2 == 0x12 &&
((ULONG)edx & 0xF0000000) >= 0x70000000) {
__try {
ProbeForRead((ULONG *)edx, sizeof(ULONG), sizeof(ULONG));
inst.parm = *(ULONG *)edx;
inst.kind = INST_CTLXFER;
check_target = FALSE;
} __except (EXCEPTION_EXECUTE_HANDLER) {
inst.parm = 0;
}
if (! inst.parm)
return -1;
} else
return -1;
}
#endif // ! _WIN64
if (inst.kind == INST_CTLXFER) {
/*
if (((ULONG_PTR)inst.parm & 0xFFF00000) == 0x7FF && hookdbg) {
//DbgPrint("%04d Address %p Call %p\n", depth, addr, (void *)inst.parm);
}
*/
eax = Hook_FindSyscall2(
(void *)inst.parm, depth + 1,
targets, check_target,
eax, skip);
check_target = TRUE;
eax = Hook_CheckSkippedSyscall(eax, skip);
if (eax != -1)
return eax;
if (inst.op1 == 0xE9 || // if it was a jump, then stop
inst.op1 == 0xEB)
return -1;
}
edx = 0; // reset edx on every cycle
if (inst.op1 == 0xB8) // mov eax, imm32
eax = (LONG)inst.parm;
else if (inst.op1 == 0xBA) // mov edx, imm32
edx = (ULONG)inst.parm;
addr = ((UCHAR *)addr) + inst.len;
}
return -1;
}
//---------------------------------------------------------------------------
// Hook_CheckSkippedSyscall
//---------------------------------------------------------------------------
_FX LONG Hook_CheckSkippedSyscall(LONG eax, LONG *skip)
{
LONG i;
for (i = 0; i < skip[0]; ++i)
if (eax == skip[i + 1])
return -1;
return eax;
}
//---------------------------------------------------------------------------
// Hook_Api_Tramp
//---------------------------------------------------------------------------
_FX NTSTATUS Hook_Api_Tramp(PROCESS *proc, ULONG64 *parms)
{
NTSTATUS status;
void *Source;
void *Trampoline;
BOOLEAN is64;
if (! proc)
return STATUS_NOT_IMPLEMENTED;
// Source should point to the beginning of a function
Source = (void *)parms[1];
if (! Source)
return STATUS_INVALID_PARAMETER;
// Trampoline is expected to point to a 96-byte writable buffer,
// aligned on a 16-byte boundary.
Trampoline = (void *)parms[2];
if (! Trampoline)
return STATUS_INVALID_PARAMETER;
ProbeForWrite(Trampoline, 96 /* sizeof(HOOK_TRAMP) */, 16);
//
// build the trampoline
//
#ifdef _WIN64
is64 = ! IoIs32bitProcess(NULL);
#else
is64 = FALSE;
#endif _WIN64
if (Hook_BuildTramp(Source, Trampoline, is64, TRUE))
status = STATUS_SUCCESS;
else
status = STATUS_UNSUCCESSFUL;
return status;
}
//---------------------------------------------------------------------------
// 32-bit and 64-bit code
//---------------------------------------------------------------------------
#ifdef _WIN64
#include "hook_64.c"
#else ! _WIN64
#include "hook_32.c"
#endif _WIN64