Skip to content

Commit

Permalink
Chrome browser process DLL blacklist.
Browse files Browse the repository at this point in the history
This patch allows for blocking of module loading in the browser process.
It does not actually prevent any modules from loading.

BUG=329023
TEST=chrome_elf_unittests

Review URL: https://codereview.chromium.org/107663008

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@241548 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
robertshield@chromium.org committed Dec 18, 2013
1 parent f6b2573 commit ff2cd61
Show file tree
Hide file tree
Showing 24 changed files with 918 additions and 13 deletions.
5 changes: 2 additions & 3 deletions chrome/app/chrome_exe_main_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,8 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prev, wchar_t*, int) {
if (AttemptFastNotify(*CommandLine::ForCurrentProcess()))
return 0;

// The purpose of this call is to force the addition of an entry in the IAT
// for chrome_elf.dll to force a load time dependency.
InitChromeElf();
// Signal Chrome Elf that Chrome has begun to start.
SignalChromeElf();

MetroDriver metro_driver;
if (metro_driver.in_metro_mode())
Expand Down
3 changes: 2 additions & 1 deletion chrome/chrome_exe.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,8 @@
}],
['OS=="win"', {
'dependencies': [
# Note that chrome_elf must be listed first. Do not reorder it.
'../chrome_elf/chrome_elf.gyp:chrome_elf',
'chrome_dll',
'chrome_nacl_win64',
'chrome_process_finder',
Expand All @@ -474,7 +476,6 @@
'../base/base.gyp:base',
'../breakpad/breakpad.gyp:breakpad_handler',
'../breakpad/breakpad.gyp:breakpad_sender',
'../chrome_elf/chrome_elf.gyp:chrome_elf',
'../components/components.gyp:breakpad_component',
'../components/components.gyp:policy',
'../sandbox/sandbox.gyp:sandbox',
Expand Down
1 change: 1 addition & 0 deletions chrome_elf/DEPS
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
include_rules = [
"+sandbox",
]
60 changes: 60 additions & 0 deletions chrome_elf/blacklist.gypi
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright 2013 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.
{
'targets': [
{
'target_name': 'blacklist',
'type': 'static_library',
'sources': [
'blacklist/blacklist.cc',
'blacklist/blacklist.h',
'blacklist/blacklist_interceptions.cc',
'blacklist/blacklist_interceptions.h',
],
'dependencies': [
# Depend on base_static, but do NOT take a dependency on base.gyp:base
# as that would risk pulling in base's link-time dependencies which
# chrome_elf cannot do.
'../base/base.gyp:base_static',
'../sandbox/sandbox.gyp:sandbox',
],
},
{
'target_name': 'blacklist_test_main_dll',
'type': 'shared_library',
'sources': [
'blacklist/test/blacklist_test_main_dll.cc',
'blacklist/test/blacklist_test_main_dll.def',
],
'dependencies': [
'../base/base.gyp:base',
'blacklist',
],
},
{
'target_name': 'blacklist_test_dll_1',
'type': 'loadable_module',
'sources': [
'blacklist/test/blacklist_test_dll_1.cc',
'blacklist/test/blacklist_test_dll_1.def',
],
},
{
'target_name': 'blacklist_test_dll_2',
'type': 'loadable_module',
'sources': [
'blacklist/test/blacklist_test_dll_2.cc',
'blacklist/test/blacklist_test_dll_2.def',
],
},
{
'target_name': 'blacklist_test_dll_3',
'type': 'loadable_module',
'sources': [
'blacklist/test/blacklist_test_dll_3.cc',
],
},
],
}

274 changes: 274 additions & 0 deletions chrome_elf/blacklist/blacklist.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
// Copyright 2013 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 "chrome_elf/blacklist/blacklist.h"

#include <string.h>

#include "base/basictypes.h"
#include "chrome_elf/blacklist/blacklist_interceptions.h"
#include "sandbox/win/src/interception_internal.h"
#include "sandbox/win/src/internal_types.h"
#include "sandbox/win/src/sandbox_utils.h"
#include "sandbox/win/src/service_resolver.h"

// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
extern "C" IMAGE_DOS_HEADER __ImageBase;

namespace blacklist{

const wchar_t* g_troublesome_dlls[kTroublesomeDllsMaxCount] = {};
int g_troublesome_dlls_cur_index = 0;

const wchar_t kRegistryBeaconPath[] = L"SOFTWARE\\Google\\Chrome\\BLBeacon";

} // namespace blacklist

// Allocate storage for thunks in a RWX page of this module to save on doing
// an extra allocation at run time.
#if !defined(_WIN64)
// 64-bit images appear to not support writeable and executable pages.
// This would yield compile warning C4330.
// TODO(robertshield): Add 64 bit support.
#pragma section(".crthunk",read,write,execute)
__declspec(allocate(".crthunk")) sandbox::ThunkData g_thunk_storage;
#endif

namespace {

enum Version {
VERSION_PRE_XP_SP2 = 0, // Not supported.
VERSION_XP_SP2,
VERSION_SERVER_2003, // Also includes XP Pro x64 and Server 2003 R2.
VERSION_VISTA, // Also includes Windows Server 2008.
VERSION_WIN7, // Also includes Windows Server 2008 R2.
VERSION_WIN8, // Also includes Windows Server 2012.
VERSION_WIN8_1,
VERSION_WIN_LAST, // Indicates error condition.
};

// Whether a process is running under WOW64 (the wrapper that allows 32-bit
// processes to run on 64-bit versions of Windows). This will return
// WOW64_DISABLED for both "32-bit Chrome on 32-bit Windows" and "64-bit
// Chrome on 64-bit Windows". WOW64_UNKNOWN means "an error occurred", e.g.
// the process does not have sufficient access rights to determine this.
enum WOW64Status {
WOW64_DISABLED,
WOW64_ENABLED,
WOW64_UNKNOWN,
};

WOW64Status GetWOW64StatusForCurrentProcess() {
typedef BOOL (WINAPI* IsWow64ProcessFunc)(HANDLE, PBOOL);
IsWow64ProcessFunc is_wow64_process = reinterpret_cast<IsWow64ProcessFunc>(
GetProcAddress(GetModuleHandle(L"kernel32.dll"), "IsWow64Process"));
if (!is_wow64_process)
return WOW64_DISABLED;
BOOL is_wow64 = FALSE;
if (!(*is_wow64_process)(GetCurrentProcess(), &is_wow64))
return WOW64_UNKNOWN;
return is_wow64 ? WOW64_ENABLED : WOW64_DISABLED;
}

class OSInfo {
public:
struct VersionNumber {
int major;
int minor;
int build;
};

struct ServicePack {
int major;
int minor;
};

OSInfo() {
OSVERSIONINFOEX version_info = { sizeof(version_info) };
GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info));
version_number_.major = version_info.dwMajorVersion;
version_number_.minor = version_info.dwMinorVersion;
version_number_.build = version_info.dwBuildNumber;
if ((version_number_.major == 5) && (version_number_.minor > 0)) {
// Treat XP Pro x64, Home Server, and Server 2003 R2 as Server 2003.
version_ = (version_number_.minor == 1) ? VERSION_XP_SP2 :
VERSION_SERVER_2003;
if (version_ == VERSION_XP_SP2 && version_info.wServicePackMajor < 2)
version_ = VERSION_PRE_XP_SP2;
} else if (version_number_.major == 6) {
switch (version_number_.minor) {
case 0:
// Treat Windows Server 2008 the same as Windows Vista.
version_ = VERSION_VISTA;
break;
case 1:
// Treat Windows Server 2008 R2 the same as Windows 7.
version_ = VERSION_WIN7;
break;
case 2:
// Treat Windows Server 2012 the same as Windows 8.
version_ = VERSION_WIN8;
break;
default:
version_ = VERSION_WIN8_1;
break;
}
} else if (version_number_.major > 6) {
version_ = VERSION_WIN_LAST;
} else {
version_ = VERSION_PRE_XP_SP2;
}

service_pack_.major = version_info.wServicePackMajor;
service_pack_.minor = version_info.wServicePackMinor;
}

Version version() const { return version_; }
VersionNumber version_number() const { return version_number_; }
ServicePack service_pack() const { return service_pack_; }

private:
Version version_;
VersionNumber version_number_;
ServicePack service_pack_;

DISALLOW_COPY_AND_ASSIGN(OSInfo);
};

bool IsNonBrowserProcess() {
wchar_t* command_line = GetCommandLine();
return (command_line && wcsstr(command_line, L"--type"));
}

} // namespace

namespace blacklist {

bool CreateBeacon() {
HKEY beacon_key = NULL;
DWORD disposition = 0;
LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER,
kRegistryBeaconPath,
0,
NULL,
0,
KEY_WRITE,
NULL,
&beacon_key,
&disposition);
bool success = (result == ERROR_SUCCESS &&
disposition != REG_OPENED_EXISTING_KEY);
if (result == ERROR_SUCCESS)
::RegCloseKey(beacon_key);
return success;
}

bool ClearBeacon() {
LONG result = ::RegDeleteKey(HKEY_CURRENT_USER, kRegistryBeaconPath);
return (result == ERROR_SUCCESS);
}

bool AddDllToBlacklist(const wchar_t* dll_name) {
if (g_troublesome_dlls_cur_index >= kTroublesomeDllsMaxCount)
return false;
for (int i = 0; i < g_troublesome_dlls_cur_index; ++i) {
if (!wcscmp(g_troublesome_dlls[i], dll_name))
return true;
}

// Copy string to blacklist.
wchar_t* str_buffer = new wchar_t[wcslen(dll_name) + 1];
wcscpy(str_buffer, dll_name);

g_troublesome_dlls[g_troublesome_dlls_cur_index] = str_buffer;
g_troublesome_dlls_cur_index++;
return true;
}

bool RemoveDllFromBlacklist(const wchar_t* dll_name) {
for (int i = 0; i < g_troublesome_dlls_cur_index; ++i) {
if (!wcscmp(g_troublesome_dlls[i], dll_name)) {
// Found the thing to remove. Delete it then replace it with the last
// element.
g_troublesome_dlls_cur_index--;
delete[] g_troublesome_dlls[i];
g_troublesome_dlls[i] = g_troublesome_dlls[g_troublesome_dlls_cur_index];
g_troublesome_dlls[g_troublesome_dlls_cur_index] = NULL;
return true;
}
}
return false;
}

bool Initialize(bool force) {
#if defined(_WIN64)
// TODO(robertshield): Implement 64-bit support by providing 64-bit
// interceptors.
return false;
#endif

// Check to see that we found the functions we need in ntdll.
if (!InitializeInterceptImports())
return false;

// Check to see if this is a non-browser process, abort if so.
if (IsNonBrowserProcess())
return false;

// Check to see if a beacon is present, abort if so.
if (!force && !CreateBeacon())
return false;

// Don't try blacklisting on unsupported OS versions.
OSInfo os_info;
if (os_info.version() <= VERSION_PRE_XP_SP2)
return false;

// Pseudo-handle, no need to close.
HANDLE current_process = ::GetCurrentProcess();

// Tells the resolver to patch already patched functions.
const bool kRelaxed = true;

// Create a thunk via the appropriate ServiceResolver instance.
sandbox::ServiceResolverThunk* thunk;
#if defined(_WIN64)
// TODO(robertshield): Use the appropriate thunk for 64-bit support
// when said support is implemented.
#else
if (GetWOW64StatusForCurrentProcess() == WOW64_ENABLED) {
if (os_info.version() >= VERSION_WIN8)
thunk = new sandbox::Wow64W8ResolverThunk(current_process, kRelaxed);
else
thunk = new sandbox::Wow64ResolverThunk(current_process, kRelaxed);
} else if (os_info.version() >= VERSION_WIN8) {
thunk = new sandbox::Win8ResolverThunk(current_process, kRelaxed);
} else {
thunk = new sandbox::ServiceResolverThunk(current_process, kRelaxed);
}
#endif

#if defined(_WIN64)
BYTE* thunk_storage = new BYTE[sizeof(sandbox::ThunkData)];
#else
BYTE* thunk_storage = reinterpret_cast<BYTE*>(&g_thunk_storage);
#endif

thunk->AllowLocalPatches();

// Get ntdll base, target name, interceptor address,
NTSTATUS ret = thunk->Setup(::GetModuleHandle(sandbox::kNtdllName),
reinterpret_cast<void*>(&__ImageBase),
"NtMapViewOfSection",
NULL,
&blacklist::BlNtMapViewOfSection,
thunk_storage,
sizeof(sandbox::ThunkData),
NULL);

delete thunk;
return NT_SUCCESS(ret);
}

} // namespace blacklist
Loading

0 comments on commit ff2cd61

Please sign in to comment.