summaryrefslogtreecommitdiffstats
path: root/sandbox/src
diff options
context:
space:
mode:
authorcpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-09-20 02:44:30 +0000
committercpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-09-20 02:44:30 +0000
commitce4e1b0221cfebad0325137dc5330495775d97c2 (patch)
treea60d872b2796754f2ae5a4717e1d9b4403b9d1f4 /sandbox/src
parent54d80bb02aa71eb957a863109392cd97ea1e2496 (diff)
downloadchromium_src-ce4e1b0221cfebad0325137dc5330495775d97c2.zip
chromium_src-ce4e1b0221cfebad0325137dc5330495775d97c2.tar.gz
chromium_src-ce4e1b0221cfebad0325137dc5330495775d97c2.tar.bz2
Redo change 2413 that I had to back out before
- CL is identical and was already lgtm TBR=rvargas Review URL: http://codereview.chromium.org/3179 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@2435 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox/src')
-rw-r--r--sandbox/src/interception.cc23
-rw-r--r--sandbox/src/interception.h30
-rw-r--r--sandbox/src/interception_agent.cc15
-rw-r--r--sandbox/src/interception_agent.h14
-rw-r--r--sandbox/src/interception_internal.h9
-rw-r--r--sandbox/src/interception_unittest.cc48
-rw-r--r--sandbox/src/sandbox_nt_util.cc64
-rw-r--r--sandbox/src/sandbox_nt_util.h28
-rw-r--r--sandbox/src/sandbox_policy.h29
-rw-r--r--sandbox/src/sandbox_policy_base.cc7
-rw-r--r--sandbox/src/sandbox_policy_base.h15
-rw-r--r--sandbox/src/sandbox_types.h7
-rw-r--r--sandbox/src/target_interceptions.cc26
-rw-r--r--sandbox/src/unload_dll_test.cc90
14 files changed, 339 insertions, 66 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 eefac61..6e59d71 100644
--- a/sandbox/src/interception_agent.cc
+++ b/sandbox/src/interception_agent.cc
@@ -76,7 +76,7 @@ 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,
void* base_address) {
DllPatchInfo* dll_info = interceptions_->dll_list;
@@ -88,12 +88,18 @@ void InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path,
dll_info = reinterpret_cast<DllPatchInfo*>(
reinterpret_cast<char*>(dll_info) + dll_info->record_bytes);
}
+
+ // Return now if 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 +108,7 @@ void InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path,
DCHECK_NT(dlls_[i]);
if (!dlls_[i])
- return;
+ return true;
dlls_[i]->data_bytes = buffer_bytes;
dlls_[i]->num_thunks = 0;
@@ -117,6 +123,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) {
diff --git a/sandbox/src/interception_agent.h b/sandbox/src/interception_agent.h
index 5fd9f1f..696dfaa 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,11 +39,13 @@ 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,
+ bool OnDllLoad(const UNICODE_STRING* full_path, const UNICODE_STRING* name,
void* base_address);
// Performs cleanup when a dll is unloaded.
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..196d752 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);
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..8a77533 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, *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
+