# HG changeset patch # User Bob Owen # Date 1730277955 0 # Wed Oct 30 08:45:55 2024 +0000 Add back SANDBOX_EXPORTS because we still currently need this for plugin-container.exe diff --git a/sandbox/win/src/interception.cc b/sandbox/win/src/interception.cc --- a/sandbox/win/src/interception.cc +++ b/sandbox/win/src/interception.cc @@ -351,16 +351,26 @@ bool InterceptionManager::IsInterception } ResultCode InterceptionManager::PatchNtdll(bool hot_patch_needed) { // Maybe there is nothing to do if (!hot_patch_needed && interceptions_.empty()) return SBOX_ALL_OK; if (hot_patch_needed) { +#if defined(SANDBOX_EXPORTS) +// Make sure the functions are not excluded by the linker. +#if defined(_WIN64) +#pragma comment(linker, "/include:TargetNtMapViewOfSection64") +#pragma comment(linker, "/include:TargetNtUnmapViewOfSection64") +#else +#pragma comment(linker, "/include:_TargetNtMapViewOfSection@44") +#pragma comment(linker, "/include:_TargetNtUnmapViewOfSection@12") +#endif +#endif // defined(SANDBOX_EXPORTS) ADD_NT_INTERCEPTION(NtMapViewOfSection, MAP_VIEW_OF_SECTION_ID, 44); ADD_NT_INTERCEPTION(NtUnmapViewOfSection, UNMAP_VIEW_OF_SECTION_ID, 12); } // Reserve a full 64k memory range in the child process. HANDLE child = child_->Process(); BYTE* thunk_base = reinterpret_cast(::VirtualAllocEx( child, nullptr, kAllocGranularity, MEM_RESERVE, PAGE_NOACCESS)); @@ -422,28 +432,53 @@ ResultCode InterceptionManager::PatchCli DllInterceptionData* dll_data) { DCHECK(thunks); DCHECK(dll_data); HMODULE ntdll_base = ::GetModuleHandle(kNtdllName); if (!ntdll_base) return SBOX_ERROR_NO_HANDLE; + char* interceptor_base = nullptr; + +#if defined(SANDBOX_EXPORTS) + interceptor_base = reinterpret_cast(child_->MainModule()); + base::ScopedNativeLibrary local_interceptor(::LoadLibrary(child_->Name())); +#endif // defined(SANDBOX_EXPORTS) + ServiceResolverThunk thunk(child_->Process(), /*relaxed=*/true); for (auto interception : interceptions_) { const std::wstring ntdll(kNtdllName); if (interception.dll != ntdll) return SBOX_ERROR_BAD_PARAMS; if (INTERCEPTION_SERVICE_CALL != interception.type) return SBOX_ERROR_BAD_PARAMS; +#if defined(SANDBOX_EXPORTS) + // We may be trying to patch by function name. + if (!interception.interceptor_address) { + const char* address; + NTSTATUS ret = thunk.ResolveInterceptor( + local_interceptor.get(), interception.interceptor.c_str(), + reinterpret_cast(&address)); + if (!NT_SUCCESS(ret)) { + ::SetLastError(GetLastErrorFromNtStatus(ret)); + return SBOX_ERROR_CANNOT_RESOLVE_INTERCEPTION_THUNK; + } + + // Translate the local address to an address on the child. + interception.interceptor_address = + interceptor_base + + (address - reinterpret_cast(local_interceptor.get())); + } +#endif // defined(SANDBOX_EXPORTS) NTSTATUS ret = thunk.Setup( - ntdll_base, nullptr, interception.function.c_str(), + ntdll_base, interceptor_base, interception.function.c_str(), interception.interceptor.c_str(), interception.interceptor_address, &thunks->thunks[dll_data->num_thunks], thunk_bytes - dll_data->used_bytes, nullptr); if (!NT_SUCCESS(ret)) { ::SetLastError(GetLastErrorFromNtStatus(ret)); return SBOX_ERROR_CANNOT_SETUP_INTERCEPTION_THUNK; } diff --git a/sandbox/win/src/interception.h b/sandbox/win/src/interception.h --- a/sandbox/win/src/interception.h +++ b/sandbox/win/src/interception.h @@ -218,16 +218,42 @@ class InterceptionManager { }; // This macro simply calls interception_manager.AddToPatchedFunctions with // the given service to intercept (INTERCEPTION_SERVICE_CALL), and assumes that // the interceptor is called "TargetXXX", where XXX is the name of the service. // Note that num_params is the number of bytes to pop out of the stack for // the exported interceptor, following the calling convention of a service call // (WINAPI = with the "C" underscore). +#if SANDBOX_EXPORTS +#if defined(_WIN64) +#define MAKE_SERVICE_NAME(service, params) "Target" #service "64" +#else +#define MAKE_SERVICE_NAME(service, params) "_Target" #service "@" #params +#endif + +#define ADD_NT_INTERCEPTION(service, id, num_params) \ + AddToPatchedFunctions(kNtdllName, #service, \ + sandbox::INTERCEPTION_SERVICE_CALL, \ + MAKE_SERVICE_NAME(service, num_params), id) + +#define INTERCEPT_NT(manager, service, id, num_params) \ + ((&Target##service) ? manager->ADD_NT_INTERCEPTION(service, id, num_params) \ + : false) + +// When intercepting the EAT it is important that the patched version of the +// function not call any functions imported from system libraries unless +// |TargetServices::InitCalled()| returns true, because it is only then that +// we are guaranteed that our IAT has been initialized. +#define INTERCEPT_EAT(manager, dll, function, id, num_params) \ + ((&Target##function) ? manager->AddToPatchedFunctions( \ + dll, #function, sandbox::INTERCEPTION_EAT, \ + MAKE_SERVICE_NAME(function, num_params), id) \ + : false) +#else // SANDBOX_EXPORTS #if defined(_WIN64) #define MAKE_SERVICE_NAME(service) &Target##service##64 #else #define MAKE_SERVICE_NAME(service) &Target##service #endif #define ADD_NT_INTERCEPTION(service, id, num_params) \ AddToPatchedFunctions( \ @@ -240,12 +266,13 @@ class InterceptionManager { // When intercepting the EAT it is important that the patched version of the // function not call any functions imported from system libraries unless // |TargetServices::InitCalled()| returns true, because it is only then that // we are guaranteed that our IAT has been initialized. #define INTERCEPT_EAT(manager, dll, function, id, num_params) \ manager->AddToPatchedFunctions( \ dll, #function, sandbox::INTERCEPTION_EAT, \ reinterpret_cast(MAKE_SERVICE_NAME(function)), id) +#endif // SANDBOX_EXPORTS } // namespace sandbox #endif // SANDBOX_WIN_SRC_INTERCEPTION_H_ diff --git a/sandbox/win/src/sandbox_types.h b/sandbox/win/src/sandbox_types.h --- a/sandbox/win/src/sandbox_types.h +++ b/sandbox/win/src/sandbox_types.h @@ -197,17 +197,21 @@ class BrokerServices; class TargetServices; // Contains the pointer to a target or broker service. struct SandboxInterfaceInfo { raw_ptr broker_services; raw_ptr target_services; }; +#if SANDBOX_EXPORTS +#define SANDBOX_INTERCEPT extern "C" __declspec(dllexport) +#else #define SANDBOX_INTERCEPT extern "C" +#endif enum InterceptionType { INTERCEPTION_INVALID = 0, INTERCEPTION_SERVICE_CALL, // Trampoline of an NT native call INTERCEPTION_EAT, INTERCEPTION_UNLOAD_MODULE, // Unload the module (don't patch) INTERCEPTION_LAST // Placeholder for last item in the enumeration }; diff --git a/sandbox/win/src/target_process.cc b/sandbox/win/src/target_process.cc --- a/sandbox/win/src/target_process.cc +++ b/sandbox/win/src/target_process.cc @@ -215,35 +215,60 @@ ResultCode TargetProcess::Create( base_address_ = GetProcessBaseAddress(process_info.process_handle()); DCHECK(base_address_); if (!base_address_) { *win_error = ::GetLastError(); ::TerminateProcess(process_info.process_handle(), 0); return SBOX_ERROR_CANNOT_FIND_BASE_ADDRESS; } +#if !defined(SANDBOX_EXPORTS) if (base_address_ != CURRENT_MODULE()) { ::TerminateProcess(process_info.process_handle(), 0); return SBOX_ERROR_INVALID_TARGET_BASE_ADDRESS; } +#endif sandbox_process_info_.Set(process_info.Take()); return SBOX_ALL_OK; } +void* TargetProcess::GetChildAddress(const char* name, const void* address) { +#if SANDBOX_EXPORTS + HMODULE module = ::LoadLibrary(exe_name_.get()); + if (!module) return nullptr; + + void* child_addr = reinterpret_cast(::GetProcAddress(module, name)); + if (!child_addr) { + return nullptr; + } + + size_t offset = + reinterpret_cast(child_addr) - reinterpret_cast(module); + ::FreeLibrary(module); + return reinterpret_cast(MainModule()) + offset; +#else + return const_cast(address); +#endif +} + ResultCode TargetProcess::TransferVariable(const char* name, const void* address, size_t size) { if (!sandbox_process_info_.IsValid()) return SBOX_ERROR_UNEXPECTED_CALL; + void* child_var = GetChildAddress(name, address); + if (!child_var) { + return SBOX_ERROR_CANNOT_FIND_VARIABLE_ADDRESS; + } + SIZE_T written; - if (!::WriteProcessMemory(sandbox_process_info_.process_handle(), - const_cast(address), address, size, - &written)) { + if (!::WriteProcessMemory(sandbox_process_info_.process_handle(), child_var, + address, size, &written)) { return SBOX_ERROR_CANNOT_WRITE_VARIABLE_VALUE; } if (written != size) return SBOX_ERROR_INVALID_WRITE_VARIABLE_SIZE; return SBOX_ALL_OK; } @@ -379,29 +404,30 @@ void TargetProcess::Terminate() { ::TerminateProcess(sandbox_process_info_.process_handle(), 0); } ResultCode TargetProcess::VerifySentinels() { if (!sandbox_process_info_.IsValid()) return SBOX_ERROR_UNEXPECTED_CALL; DWORD value = 0; SIZE_T read; - + void* sentinel_start = + GetChildAddress("g_sentinel_value_start", &g_sentinel_value_start); if (!::ReadProcessMemory(sandbox_process_info_.process_handle(), - &g_sentinel_value_start, &value, sizeof(DWORD), - &read)) { + sentinel_start, &value, sizeof(DWORD), &read)) { return SBOX_ERROR_CANNOT_READ_SENTINEL_VALUE; } if (read != sizeof(DWORD)) return SBOX_ERROR_INVALID_READ_SENTINEL_SIZE; if (value != g_sentinel_value_start) return SBOX_ERROR_MISMATCH_SENTINEL_VALUE; - if (!::ReadProcessMemory(sandbox_process_info_.process_handle(), - &g_sentinel_value_end, &value, sizeof(DWORD), - &read)) { + void* sentinel_end = + GetChildAddress("g_sentinel_value_end", &g_sentinel_value_end); + if (!::ReadProcessMemory(sandbox_process_info_.process_handle(), sentinel_end, + &value, sizeof(DWORD), &read)) { return SBOX_ERROR_CANNOT_READ_SENTINEL_VALUE; } if (read != sizeof(DWORD)) return SBOX_ERROR_INVALID_READ_SENTINEL_SIZE; if (value != g_sentinel_value_end) return SBOX_ERROR_MISMATCH_SENTINEL_VALUE; return SBOX_ALL_OK; diff --git a/sandbox/win/src/target_process.h b/sandbox/win/src/target_process.h --- a/sandbox/win/src/target_process.h +++ b/sandbox/win/src/target_process.h @@ -88,16 +88,20 @@ class TargetProcess { // Creates a mock TargetProcess used for testing interceptions. static std::unique_ptr MakeTargetProcessForTesting( HANDLE process, HMODULE base_address); private: FRIEND_TEST_ALL_PREFIXES(TargetProcessTest, FilterEnvironment); + + // Get the address in the child for a given variable name. + void* GetChildAddress(const char* name, const void* address); + // Verify the target process looks the same as the broker process. ResultCode VerifySentinels(); // Filters an environment to only include those that have an entry in // `to_keep`. static std::wstring FilterEnvironment( const wchar_t* env, const base::span to_keep);