diff options
author | cpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-09-18 00:26:01 +0000 |
---|---|---|
committer | cpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-09-18 00:26:01 +0000 |
commit | 9cd0144cccf979ecce9f193f77e50c05544bc4a7 (patch) | |
tree | 548283eb1965017d2f1c46e9bb63aa8cbc7816d5 /sandbox | |
parent | 35aa85ac00fbfa16bd9f52b05e47eb09a3b6a0ce (diff) | |
download | chromium_src-9cd0144cccf979ecce9f193f77e50c05544bc4a7.zip chromium_src-9cd0144cccf979ecce9f193f77e50c05544bc4a7.tar.gz chromium_src-9cd0144cccf979ecce9f193f77e50c05544bc4a7.tar.bz2 |
Add an interface to the sandbox to block dll from loading in the target process
- new interface is TargetPolicy::AddDllToUnload
- Added integration tests and unit tests.
Review URL: http://codereview.chromium.org/2413
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@2348 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox')
-rw-r--r-- | sandbox/src/interception.cc | 23 | ||||
-rw-r--r-- | sandbox/src/interception.h | 30 | ||||
-rw-r--r-- | sandbox/src/interception_agent.cc | 22 | ||||
-rw-r--r-- | sandbox/src/interception_agent.h | 16 | ||||
-rw-r--r-- | sandbox/src/interception_internal.h | 9 | ||||
-rw-r--r-- | sandbox/src/interception_unittest.cc | 48 | ||||
-rw-r--r-- | sandbox/src/sandbox_nt_util.cc | 64 | ||||
-rw-r--r-- | sandbox/src/sandbox_nt_util.h | 30 | ||||
-rw-r--r-- | sandbox/src/sandbox_policy.h | 29 | ||||
-rw-r--r-- | sandbox/src/sandbox_policy_base.cc | 7 | ||||
-rw-r--r-- | sandbox/src/sandbox_policy_base.h | 15 | ||||
-rw-r--r-- | sandbox/src/sandbox_types.h | 7 | ||||
-rw-r--r-- | sandbox/src/target_interceptions.cc | 26 | ||||
-rw-r--r-- | sandbox/src/unload_dll_test.cc | 90 | ||||
-rw-r--r-- | sandbox/tests/integration_tests/sbox_integration_tests.vcproj | 4 |
15 files changed, 351 insertions, 69 deletions
diff --git a/sandbox/src/interception.cc b/sandbox/src/interception.cc index 198cdbf..8ee56f4 100644 --- a/sandbox/src/interception.cc +++ b/sandbox/src/interception.cc @@ -30,6 +30,9 @@ namespace sandbox { SANDBOX_INTERCEPT SharedMemory* g_interceptions; +// Magic constant that identifies that this function is not to be patched. +const char kUnloadDLLDummyFunction[] = "@"; + InterceptionManager::InterceptionManager(TargetProcess* child_process, bool relaxed) : child_(child_process), names_used_(false), relaxed_(relaxed) { @@ -49,7 +52,6 @@ bool InterceptionManager::AddToPatchedFunctions( function.interceptor_address = replacement_code_address; interceptions_.push_back(function); - return true; } @@ -65,7 +67,19 @@ bool InterceptionManager::AddToPatchedFunctions( interceptions_.push_back(function); names_used_ = true; + return true; +} +bool InterceptionManager::AddToUnloadModules(const wchar_t* dll_name) { + InterceptionData module_to_unload; + module_to_unload.type = INTERCEPTION_UNLOAD_MODULE; + module_to_unload.dll = dll_name; + // The next two are dummy values that make the structures regular, instead + // of having special cases. They should not be used. + module_to_unload.function = kUnloadDLLDummyFunction; + module_to_unload.interceptor_address = reinterpret_cast<void*>(1); + + interceptions_.push_back(module_to_unload); return true; } @@ -204,6 +218,7 @@ bool InterceptionManager::SetupDllInfo(const InterceptionData& data, *buffer = reinterpret_cast<char*>(*buffer) + required; // set up the dll info to be what we know about it at this time + dll_info->unload_module = (data.type == INTERCEPTION_UNLOAD_MODULE); dll_info->record_bytes = required; dll_info->offset_to_functions = required; dll_info->num_functions = 0; @@ -221,6 +236,12 @@ bool InterceptionManager::SetupInterceptionInfo(const InterceptionData& data, DCHECK(buffer); DCHECK(*buffer); + if ((dll_info->unload_module) && + (data.function != kUnloadDLLDummyFunction)) { + // Can't specify a dll for both patch and unload. + NOTREACHED(); + } + FunctionInfo* function = reinterpret_cast<FunctionInfo*>(*buffer); size_t name_bytes = data.function.size(); diff --git a/sandbox/src/interception.h b/sandbox/src/interception.h index 83169f9..7859883 100644 --- a/sandbox/src/interception.h +++ b/sandbox/src/interception.h @@ -6,8 +6,8 @@ // for the sandboxed process. For more datails see // http://wiki/Main/ChromeSandboxInterceptionDesign -#ifndef SANDBOX_SRC_INTERCEPTION_H__ -#define SANDBOX_SRC_INTERCEPTION_H__ +#ifndef SANDBOX_SRC_INTERCEPTION_H_ +#define SANDBOX_SRC_INTERCEPTION_H_ #include <list> #include <string> @@ -59,7 +59,8 @@ struct DllInterceptionData; // class InterceptionManager { // The unit test will access private members. - FRIEND_TEST(InterceptionManagerTest, BufferLayout); + FRIEND_TEST(InterceptionManagerTest, BufferLayout1); + FRIEND_TEST(InterceptionManagerTest, BufferLayout2); public: // An interception manager performs interceptions on a given child process. @@ -99,11 +100,14 @@ class InterceptionManager { InterceptionType interception_type, const char* replacement_function_name); + // The interception agent will unload the dll with dll_name. + bool AddToUnloadModules(const wchar_t* dll_name); + // Initializes all interceptions on the client. // Returns true on success. // // The child process must be created suspended, and cannot be resumed until - // after this method returns. In addition, no action should be perfomed on + // after this method returns. In addition, no action should be performed on // the child that may cause it to resume momentarily, such as injecting // threads or APCs. // @@ -114,11 +118,11 @@ class InterceptionManager { private: // Used to store the interception information until the actual set-up. struct InterceptionData { - InterceptionType type; // Interception type - std::wstring dll; // Name of dll to intercept - std::string function; // Name of function to intercept - std::string interceptor; // Name of interceptor function - const void* interceptor_address; // Interceptor's entry point + InterceptionType type; // Interception type. + std::wstring dll; // Name of dll to intercept. + std::string function; // Name of function to intercept. + std::string interceptor; // Name of interceptor function. + const void* interceptor_address; // Interceptor's entry point. }; // Calculates the size of the required configuration buffer. @@ -169,10 +173,10 @@ class InterceptionManager { bool CopyDataToChild(const void* local_buffer, size_t buffer_bytes, void** remote_buffer) const; - // Performs the cold patch (from the parent) of ntdll.dll. + // Performs the cold patch (from the parent) of ntdll. // Returns true on success. // - // This method will inser aditional interceptions to launch the interceptor + // This method will insert additional interceptions to launch the interceptor // agent on the child process, if there are additional interceptions to do. bool PatchNtdll(bool hot_patch_needed); @@ -196,10 +200,10 @@ class InterceptionManager { // true if we are allowed to patch already-patched functions. bool relaxed_; - DISALLOW_EVIL_CONSTRUCTORS(InterceptionManager); + DISALLOW_COPY_AND_ASSIGN(InterceptionManager); }; } // namespace sandbox -#endif // SANDBOX_SRC_INTERCEPTION_H__ +#endif // SANDBOX_SRC_INTERCEPTION_H_ diff --git a/sandbox/src/interception_agent.cc b/sandbox/src/interception_agent.cc index 61ca03a..4efca01 100644 --- a/sandbox/src/interception_agent.cc +++ b/sandbox/src/interception_agent.cc @@ -76,9 +76,11 @@ bool InterceptionAgent::DllMatch(const UNICODE_STRING* full_path, return false; } -void InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path, +bool InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path, const UNICODE_STRING* name, + uint32 image_flags, void* base_address) { + // It is a regular dll, it can be in our table to be patched or unloaded. DllPatchInfo* dll_info = interceptions_->dll_list; int i = 0; for (; i < interceptions_->num_intercepted_dlls; i++) { @@ -88,12 +90,18 @@ void InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path, dll_info = reinterpret_cast<DllPatchInfo*>( reinterpret_cast<char*>(dll_info) + dll_info->record_bytes); } + + // The dll is not in our list of interest. if (i == interceptions_->num_intercepted_dlls) - return; + return true; + + // The dll must be unloaded. + if (dll_info->unload_module) + return false; // Purify causes this condition to trigger. if (dlls_[i]) - return; + return true; size_t buffer_bytes = offsetof(DllInterceptionData, thunks) + dll_info->num_functions * sizeof(ThunkData); @@ -102,7 +110,9 @@ void InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path, DCHECK_NT(dlls_[i]); if (!dlls_[i]) - return; + return true; + + // The dll must be patched. dlls_[i]->data_bytes = buffer_bytes; dlls_[i]->num_thunks = 0; @@ -117,6 +127,7 @@ void InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path, VERIFY_SUCCESS(g_nt.ProtectVirtualMemory(NtCurrentProcess, &to_protect, &real_size, PAGE_EXECUTE_READ, &old_protect)); + return true; } void InterceptionAgent::OnDllUnload(void* base_address) { @@ -137,7 +148,8 @@ bool InterceptionAgent::PatchDll(const DllPatchInfo* dll_info, DllInterceptionData* thunks) { DCHECK_NT(NULL != thunks); DCHECK_NT(NULL != dll_info); - + DCHECK_NT(!dll_info->unload_module); + const FunctionInfo* function = reinterpret_cast<const FunctionInfo*>( reinterpret_cast<const char*>(dll_info) + dll_info->offset_to_functions); diff --git a/sandbox/src/interception_agent.h b/sandbox/src/interception_agent.h index 5fd9f1f..b33d6d3 100644 --- a/sandbox/src/interception_agent.h +++ b/sandbox/src/interception_agent.h @@ -23,10 +23,12 @@ struct DllPatchInfo; class ResolverThunk; // The InterceptionAgent executes on the target application, and it is in charge -// of setting up the desired interceptions. +// of setting up the desired interceptions or indicating what module needs to +// be unloaded. // -// The exposed API consists of two methods: GetInterceptionAgent to retrieve the -// single class instance, and OnDllLoad to process a dll being loaded. +// The exposed API consists of three methods: GetInterceptionAgent to retrieve +// the single class instance, OnDllLoad and OnDllUnload to process a dll being +// loaded and unloaded respectively. // // This class assumes that it will get called for every dll being loaded, // starting with kernel32, so the singleton will be instantiated from within the @@ -37,12 +39,14 @@ class InterceptionAgent { static InterceptionAgent* GetInterceptionAgent(); // This method should be invoked whenever a new dll is loaded to perform the - // required patches. + // required patches. If the return value is false, this dll should not be + // allowed to load. + // // full_path is the (optional) full name of the module being loaded and name // is the internal module name. If full_path is provided, it will be used // before the internal name to determine if we care about this dll. - void OnDllLoad(const UNICODE_STRING* full_path, const UNICODE_STRING* name, - void* base_address); + bool OnDllLoad(const UNICODE_STRING* full_path, const UNICODE_STRING* name, + uint32 image_flags, void* base_address); // Performs cleanup when a dll is unloaded. void OnDllUnload(void* base_address); diff --git a/sandbox/src/interception_internal.h b/sandbox/src/interception_internal.h index 67cfe31..e6e91bf 100644 --- a/sandbox/src/interception_internal.h +++ b/sandbox/src/interception_internal.h @@ -3,11 +3,11 @@ // found in the LICENSE file. // Defines InterceptionManager, the class in charge of setting up interceptions -// for the sandboxed process. For more datails see +// for the sandboxed process. For more details see: // http://wiki/Main/ChromeSandboxInterceptionDesign -#ifndef SANDBOX_SRC_INTERCEPTION_INTERNAL_H__ -#define SANDBOX_SRC_INTERCEPTION_INTERNAL_H__ +#ifndef SANDBOX_SRC_INTERCEPTION_INTERNAL_H_ +#define SANDBOX_SRC_INTERCEPTION_INTERNAL_H_ #include "sandbox/src/sandbox_types.h" @@ -37,6 +37,7 @@ struct DllPatchInfo { size_t record_bytes; // rounded to sizeof(size_t) bytes size_t offset_to_functions; int num_functions; + bool unload_module; wchar_t dll_name[1]; // placeholder for null terminated name // FunctionInfo function_info[] // followed by the functions to intercept }; @@ -66,5 +67,5 @@ struct DllInterceptionData { } // namespace sandbox -#endif // SANDBOX_SRC_INTERCEPTION_INTERNAL_H__ +#endif // SANDBOX_SRC_INTERCEPTION_INTERNAL_H_ diff --git a/sandbox/src/interception_unittest.cc b/sandbox/src/interception_unittest.cc index 9fe94ee..551aaa5 100644 --- a/sandbox/src/interception_unittest.cc +++ b/sandbox/src/interception_unittest.cc @@ -70,7 +70,7 @@ void WalkBuffer(void* buffer, size_t size, int* num_dlls, int* num_functions, } } -TEST(InterceptionManagerTest, BufferLayout) { +TEST(InterceptionManagerTest, BufferLayout1) { wchar_t exe_name[MAX_PATH]; ASSERT_NE(0, GetModuleFileName(NULL, exe_name, MAX_PATH - 1)); @@ -150,5 +150,51 @@ TEST(InterceptionManagerTest, BufferLayout) { EXPECT_EQ(4, num_names); } +TEST(InterceptionManagerTest, BufferLayout2) { + wchar_t exe_name[MAX_PATH]; + ASSERT_NE(0, GetModuleFileName(NULL, exe_name, MAX_PATH - 1)); + + TargetProcess *target = MakeTestTargetProcess(::GetCurrentProcess(), + ::GetModuleHandle(exe_name)); + + InterceptionManager interceptions(target, true); + + // Any pointer will do for a function pointer. + void* function = &interceptions; + + interceptions.AddToUnloadModules(L"some01.dll"); + interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtCreateFile", + INTERCEPTION_SERVICE_CALL, function); + interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateFileEx", + INTERCEPTION_EAT, function); + interceptions.AddToUnloadModules(L"some02.dll"); + interceptions.AddToPatchedFunctions(L"kernel32.dll", "SomeFileEx", + INTERCEPTION_SMART_SIDESTEP, function); + + // Verify that all interceptions were added + ASSERT_EQ(5, interceptions.interceptions_.size()); + + size_t buffer_size = interceptions.GetBufferSize(); + scoped_ptr<BYTE> local_buffer(new BYTE[buffer_size]); + + ASSERT_TRUE(interceptions.SetupConfigBuffer(local_buffer.get(), + buffer_size)); + + // At this point, the interceptions should have been separated into two + // groups: one group with the local ("cold") interceptions, and another + // group with the interceptions belonging to dlls that will be "hot" + // patched on the client. The second group lives on local_buffer, and the + // first group remains on the list of interceptions, in this case just one. + EXPECT_EQ(1, interceptions.interceptions_.size()); + + int num_dlls, num_functions, num_names; + WalkBuffer(local_buffer.get(), buffer_size, &num_dlls, &num_functions, + &num_names); + + EXPECT_EQ(3, num_dlls); + EXPECT_EQ(4, num_functions); + EXPECT_EQ(0, num_names); +} + } // namespace sandbox diff --git a/sandbox/src/sandbox_nt_util.cc b/sandbox/src/sandbox_nt_util.cc index 008c02d..a7f1be0 100644 --- a/sandbox/src/sandbox_nt_util.cc +++ b/sandbox/src/sandbox_nt_util.cc @@ -277,22 +277,31 @@ UNICODE_STRING* AnsiToUnicode(const char* string) { return out_string; } -UNICODE_STRING* GetImageNameFromModule(HMODULE module) { +UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags) { UNICODE_STRING* out_name = NULL; __try { do { + *flags = 0; PEImage pe(module); if (!pe.VerifyMagic()) break; + *flags |= MODULE_IS_PE_IMAGE; PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory(); - if (!exports) - break; + if (exports) { + char* name = reinterpret_cast<char*>(pe.RVAToAddr(exports->Name)); + out_name = AnsiToUnicode(name); + } - char* name = reinterpret_cast<char*>(pe.RVAToAddr(exports->Name)); + PIMAGE_NT_HEADERS headers = pe.GetNTHeaders(); + if (headers) { + if (headers->OptionalHeader.AddressOfEntryPoint) + *flags |= MODULE_HAS_ENTRY_POINT; + if (headers->OptionalHeader.SizeOfCode) + *flags |= MODULE_HAS_CODE; + } - out_name = AnsiToUnicode(name); } while (false); } __except(EXCEPTION_EXECUTE_HANDLER) { } @@ -332,6 +341,51 @@ UNICODE_STRING* GetBackingFilePath(PVOID address) { } } +UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path) { + if ((!module_path) || (!module_path->Buffer)) + return NULL; + + wchar_t* sep = NULL; + int start_pos = module_path->Length / sizeof(wchar_t) - 1; + int ix = start_pos; + + for(; ix >= 0; --ix) { + if (module_path->Buffer[ix] == L'\\') { + sep = &module_path->Buffer[ix]; + break; + } + } + + // Ends with path separator. Not a valid module name. + if ((ix == start_pos) && sep) + return NULL; + + // No path separator found. Use the entire name. + if ((ix == 0) && !sep) { + sep = &module_path->Buffer[-1]; + } + + // Add one to the size so we can null terminate the string. + size_t size_bytes = (start_pos - ix + 1) * sizeof(wchar_t); + char* str_buffer = new(NT_ALLOC) char[size_bytes + sizeof(UNICODE_STRING)]; + if (!str_buffer) + return NULL; + + UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>(str_buffer); + out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]); + out_string->Length = size_bytes - sizeof(wchar_t); + out_string->MaximumLength = size_bytes; + + NTSTATUS ret = CopyData(out_string->Buffer, &sep[1], out_string->Length); + if (!NT_SUCCESS(ret)) { + operator delete(out_string, NT_ALLOC); + return NULL; + } + + out_string->Buffer[out_string->Length / sizeof(wchar_t)] = L'\0'; + return out_string; +} + NTSTATUS AutoProtectMemory::ChangeProtection(void* address, size_t bytes, ULONG protect) { DCHECK_NT(!changed_); diff --git a/sandbox/src/sandbox_nt_util.h b/sandbox/src/sandbox_nt_util.h index b21a1dd..700e83f 100644 --- a/sandbox/src/sandbox_nt_util.h +++ b/sandbox/src/sandbox_nt_util.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SANDBOX_SRC_SANDBOX_NT_UTIL_H__ -#define SANDBOX_SRC_SANDBOX_NT_UTIL_H__ +#ifndef SANDBOX_SRC_SANDBOX_NT_UTIL_H_ +#define SANDBOX_SRC_SANDBOX_NT_UTIL_H_ #include "base/basictypes.h" #include "sandbox/src/nt_internals.h" @@ -89,17 +89,27 @@ bool InitHeap(); // Returns true if the provided handle refers to the current process. bool IsSameProcess(HANDLE process); -// Returns the name for a given module. The returned buffer must be freed with -// a placement delete from our ntdll level allocator: +enum MappedModuleFlags { + MODULE_IS_PE_IMAGE = 1, // Module is an executable. + MODULE_HAS_ENTRY_POINT = 2, // Execution entry point found. + MODULE_HAS_CODE = 4 // Non zero size of executable sections. +}; + +// Returns the name and characteristics for a given PE module. The return +// value is the name as defined by the export table and the flags is any +// combination of the MappedModuleFlags enumeration. // -// UNICODE_STRING* name = GetImageNameFromModule(HMODULE module); +// The returned buffer must be freed with a placement delete from the ntdll +// level allocator: +// +// UNICODE_STRING* name = GetPEImageInfoFromModule(HMODULE module, &flags); // if (!name) { // // probably not a valid dll // return; // } // InsertYourLogicHere(name); // operator delete(name, NT_ALLOC); -UNICODE_STRING* GetImageNameFromModule(HMODULE module); +UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags); // Returns the full path and filename for a given dll. // May return NULL if the provided address is not backed by a named section, or @@ -107,6 +117,12 @@ UNICODE_STRING* GetImageNameFromModule(HMODULE module); // be freed with a placement delete (see GetImageNameFromModule example). UNICODE_STRING* GetBackingFilePath(PVOID address); +// Returns the last component of a path that contains the module name. +// It will return NULL if the path is not a full path or if the path ends +// with the path separator. The returned buffer must be freed with a placement +// delete (see GetImageNameFromModule example). +UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path); + // Returns true if the parameters correspond to a dll mapped as code. bool IsValidImageSection(HANDLE section, PVOID *base, PLARGE_INTEGER offset, PULONG view_size); @@ -147,5 +163,5 @@ bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length, } // namespace sandbox -#endif // SANDBOX_SRC_SANDBOX_NT_UTIL_H__ +#endif // SANDBOX_SRC_SANDBOX_NT_UTIL_H_ diff --git a/sandbox/src/sandbox_policy.h b/sandbox/src/sandbox_policy.h index b47f6ad..6e3983a 100644 --- a/sandbox/src/sandbox_policy.h +++ b/sandbox/src/sandbox_policy.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SANDBOX_SRC_SANDBOX_POLICY_H__ -#define SANDBOX_SRC_SANDBOX_POLICY_H__ +#ifndef SANDBOX_SRC_SANDBOX_POLICY_H_ +#define SANDBOX_SRC_SANDBOX_POLICY_H_ #include "base/basictypes.h" #include "sandbox/src/sandbox_types.h" @@ -13,6 +13,16 @@ namespace sandbox { class TargetPolicy { public: + // Increments the reference count of this object. The reference count must + // be incremented if this interface is given to another component. + virtual void AddRef() = 0; + + // Decrements the reference count of this object. When the reference count + // is zero the object is automatically destroyed. + // Indicates that the caller is done with this interface. After calling + // release no other method should be called. + virtual void Release() = 0; + // Sets the security level for the target process' two tokens. // This setting is permanent and cannot be changed once the target process is // spawned. @@ -144,19 +154,14 @@ class TargetPolicy { virtual ResultCode AddRule(SubSystem subsystem, Semantics semantics, const wchar_t* pattern) = 0; - // Increments the reference count of this object. The reference count must - // be incremented if this interface is given to another component. - virtual void AddRef() = 0; - - // Decrements the reference count of this object. When the reference count - // is zero the object is automatically destroyed. - // Indicates that the caller is done with this interface. After calling - // release no other method should be called. - virtual void Release() = 0; + // Adds a dll that will be unloaded in the target process before it gets + // a chance to initialize itself. Typically, dlls that cause the target + // to crash go here. + virtual ResultCode AddDllToUnload(const wchar_t* dll_name) = 0; }; } // namespace sandbox -#endif // SANDBOX_SRC_SANDBOX_POLICY_H__ +#endif // SANDBOX_SRC_SANDBOX_POLICY_H_ diff --git a/sandbox/src/sandbox_policy_base.cc b/sandbox/src/sandbox_policy_base.cc index d5010af..f50453b 100644 --- a/sandbox/src/sandbox_policy_base.cc +++ b/sandbox/src/sandbox_policy_base.cc @@ -345,6 +345,13 @@ bool PolicyBase::SetupAllInterceptions(TargetProcess* target) { } } + if (!blacklisted_dlls_.empty()) { + std::vector<std::wstring>::iterator it = blacklisted_dlls_.begin(); + for (; it != blacklisted_dlls_.end(); ++it) { + manager.AddToUnloadModules(it->c_str()); + } + } + if (!SetupBasicInterceptions(&manager)) return false; diff --git a/sandbox/src/sandbox_policy_base.h b/sandbox/src/sandbox_policy_base.h index 6f89f2a..4b81dbd 100644 --- a/sandbox/src/sandbox_policy_base.h +++ b/sandbox/src/sandbox_policy_base.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SANDBOX_SRC_SANDBOX_POLICY_BASE_H__ -#define SANDBOX_SRC_SANDBOX_POLICY_BASE_H__ +#ifndef SANDBOX_SRC_SANDBOX_POLICY_BASE_H_ +#define SANDBOX_SRC_SANDBOX_POLICY_BASE_H_ #include <Windows.h> #include <list> @@ -80,6 +80,11 @@ class PolicyBase : public Dispatcher, public TargetPolicy { virtual ResultCode AddRule(SubSystem subsystem, Semantics semantics, const wchar_t* pattern); + virtual ResultCode AddDllToUnload(const wchar_t* dll_name) { + blacklisted_dlls_.push_back(std::wstring(dll_name)); + return SBOX_ALL_OK; + } + std::wstring GetDesktop() const { return desktop_; } @@ -142,11 +147,13 @@ class PolicyBase : public Dispatcher, public TargetPolicy { bool file_system_init_; // Operation mode for the interceptions. bool relaxed_interceptions_; + // The list of dlls to unload in the target process. + std::vector<std::wstring> blacklisted_dlls_; - DISALLOW_EVIL_CONSTRUCTORS(PolicyBase); + DISALLOW_COPY_AND_ASSIGN(PolicyBase); }; } // namespace sandbox -#endif // SANDBOX_SRC_SANDBOX_POLICY_BASE_H__ +#endif // SANDBOX_SRC_SANDBOX_POLICY_BASE_H_ diff --git a/sandbox/src/sandbox_types.h b/sandbox/src/sandbox_types.h index f76c5c6..db6173f 100644 --- a/sandbox/src/sandbox_types.h +++ b/sandbox/src/sandbox_types.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SANDBOX_SRC_SANDBOX_TYPES_H__ -#define SANDBOX_SRC_SANDBOX_TYPES_H__ +#ifndef SANDBOX_SRC_SANDBOX_TYPES_H_ +#define SANDBOX_SRC_SANDBOX_TYPES_H_ namespace sandbox { @@ -65,10 +65,11 @@ enum InterceptionType { INTERCEPTION_EAT, INTERCEPTION_SIDESTEP, // Preamble patch INTERCEPTION_SMART_SIDESTEP, // Preamble patch but bypass internal calls + INTERCEPTION_UNLOAD_MODULE, // Unload the module (don't patch) INTERCEPTION_LAST // Placeholder for last item in the enumeration }; } // namespace sandbox -#endif // SANDBOX_SRC_SANDBOX_TYPES_H__ +#endif // SANDBOX_SRC_SANDBOX_TYPES_H_ diff --git a/sandbox/src/target_interceptions.cc b/sandbox/src/target_interceptions.cc index 0bf9cd6..466c9ba 100644 --- a/sandbox/src/target_interceptions.cc +++ b/sandbox/src/target_interceptions.cc @@ -11,6 +11,8 @@ namespace sandbox { +SANDBOX_INTERCEPT NtExports g_nt; + // Hooks NtMapViewOfSection to detect the load of DLLs. If hot patching is // required for this dll, this functions patches it. NTSTATUS WINAPI TargetNtMapViewOfSection( @@ -41,18 +43,26 @@ NTSTATUS WINAPI TargetNtMapViewOfSection( if (!IsValidImageSection(section, base, offset, view_size)) break; - UNICODE_STRING* module_name = GetImageNameFromModule( - reinterpret_cast<HMODULE>(*base)); - - if (!module_name) - break; - + UINT image_flags; + UNICODE_STRING* module_name = + GetImageInfoFromModule(reinterpret_cast<HMODULE>(*base), &image_flags); UNICODE_STRING* file_name = GetBackingFilePath(*base); + if ((!module_name) && (image_flags & MODULE_HAS_CODE)) { + // If the module has no exports we retrieve the module name from the + // full path of the mapped section. + module_name = ExtractModuleName(file_name); + } + InterceptionAgent* agent = InterceptionAgent::GetInterceptionAgent(); - if (agent) - agent->OnDllLoad(file_name, module_name, *base); + if (agent) { + if (!agent->OnDllLoad(file_name, module_name, image_flags, *base)) { + // Interception agent is demanding to un-map the module. + g_nt.UnmapViewOfSection(process, *base); + ret = STATUS_UNSUCCESSFUL; + } + } if (module_name) operator delete(module_name, NT_ALLOC); diff --git a/sandbox/src/unload_dll_test.cc b/sandbox/src/unload_dll_test.cc new file mode 100644 index 0000000..19097fd --- /dev/null +++ b/sandbox/src/unload_dll_test.cc @@ -0,0 +1,90 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/scoped_handle.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "sandbox/src/sandbox.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/target_services.h" +#include "sandbox/tests/common/controller.h" + +namespace sandbox { + +// Loads and or unloads a DLL passed in the second parameter of argv. +// The first parameter of argv is 'L' = load, 'U' = unload or 'B' for both. +SBOX_TESTS_COMMAND int UseOneDLL(int argc, wchar_t **argv) { + if (argc != 2) + return SBOX_TEST_FAILED_TO_RUN_TEST; + int rv = SBOX_TEST_FAILED_TO_RUN_TEST; + + wchar_t option = (argv[0])[0]; + if ((option == L'L') || (option == L'B')) { + HMODULE module1 = ::LoadLibraryW(argv[1]); + rv = (module1 == NULL) ? SBOX_TEST_FAILED : SBOX_TEST_SUCCEEDED; + } + + if ((option == L'U') || (option == L'B')) { + HMODULE module2 = ::GetModuleHandleW(argv[1]); + rv = FreeLibrary(module2) ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED; + } + return rv; +} + +// Opens an event passed as the first parameter of argv. +SBOX_TESTS_COMMAND int SimpleOpenEvent(int argc, wchar_t **argv) { + if (argc != 1) + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + + ScopedHandle event_open(::OpenEvent(SYNCHRONIZE, FALSE, argv[0])); + return event_open.Get() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED; +} + +TEST(UnloadDllTest, BaselineAvicapDll) { + TestRunner runner; + runner.SetTestState(BEFORE_REVERT); + runner.SetTimeout(2000); + // Add a sync rule, because that ensures that the interception agent has + // more than one item in its internal table. + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, + TargetPolicy::EVENTS_ALLOW_ANY, L"t0001")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"UseOneDLL L avicap32.dll")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"UseOneDLL B avicap32.dll")); +} + +TEST(UnloadDllTest, UnloadAviCapDllNoPatching) { + TestRunner runner; + runner.SetTestState(BEFORE_REVERT); + runner.SetTimeout(2000); + sandbox::TargetPolicy* policy = runner.GetPolicy(); + policy->AddDllToUnload(L"avicap32.dll"); + EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL L avicap32.dll")); + EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL B avicap32.dll")); +} + +TEST(UnloadDllTest, UnloadAviCapDllWithPatching) { + TestRunner runner; + runner.SetTimeout(2000); + runner.SetTestState(BEFORE_REVERT); + sandbox::TargetPolicy* policy = runner.GetPolicy(); + policy->AddDllToUnload(L"avicap32.dll"); + + ScopedHandle handle1(::CreateEvent(NULL, FALSE, FALSE, L"tst0001")); + + // Add a couple of rules that ensures that the interception agent add EAT + // patching on the client which makes sure that the unload dll record does + // not interact badly with them. + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY, + TargetPolicy::REG_ALLOW_ANY, + L"HKEY_LOCAL_MACHINE\\Software\\Microsoft")); + EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, + TargetPolicy::EVENTS_ALLOW_ANY, L"tst0001")); + + EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL L avicap32.dll")); + + runner.SetTestState(AFTER_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"SimpleOpenEvent tst0001")); +} + +} // namespace sandbox + diff --git a/sandbox/tests/integration_tests/sbox_integration_tests.vcproj b/sandbox/tests/integration_tests/sbox_integration_tests.vcproj index a747745..53816e7 100644 --- a/sandbox/tests/integration_tests/sbox_integration_tests.vcproj +++ b/sandbox/tests/integration_tests/sbox_integration_tests.vcproj @@ -232,6 +232,10 @@ RelativePath="..\..\src\sync_policy_test.cc" > </File> + <File + RelativePath="..\..\src\unload_dll_test.cc" + > + </File> </Files> <Globals> </Globals> |