[chrome_elf] Big cleanup and removing dependencies that recently crept in.  PART 1.

- Moving all crash related APIs into one place (and out of chrome_elf_main).
- Started to clean up external dependencies - starting with hook_utils.
- Removed dependency on base/win/iat_patch_function.

BUG=631771

Review-Url: https://codereview.chromium.org/2183263003
Cr-Commit-Position: refs/heads/master@{#411982}
diff --git a/chrome_elf/BUILD.gn b/chrome_elf/BUILD.gn
index 5cc27bd..3de5fb3 100644
--- a/chrome_elf/BUILD.gn
+++ b/chrome_elf/BUILD.gn
@@ -33,9 +33,6 @@
 # in the world.
 shared_library("chrome_elf") {
   sources = [
-    "//chrome/app/chrome_crash_reporter_client_win.cc",
-    "//chrome/app/chrome_crash_reporter_client_win.h",
-    "//chrome/common/chrome_result_codes.h",
     "chrome_elf.def",
     "chrome_elf_main.cc",
     "chrome_elf_main.h",
@@ -44,31 +41,24 @@
     ":blacklist",
     ":chrome_elf_manifest",
     ":chrome_elf_resources",
-    ":chrome_elf_security",
     ":constants",
+    ":crash",
     ":hook_util",
-    "//base",
+    ":security",
     "//build/config/sanitizers:deps",
     "//chrome/install_static:install_static_util",
     "//chrome_elf/nt_registry:nt_registry",
-    "//components/crash/content/app",
-    "//components/crash/core/common",
-    "//content/public/common:result_codes",
-    "//third_party/crashpad/crashpad/client:client",
   ]
   configs += [ "//build/config/win:windowed" ]
   configs -= [ "//build/config/win:console" ]
+
+  # Delay loads in this list will prevent user32.dll
+  # from loading too early.
   ldflags = [
-    "/NODEFAULTLIB:user32.lib",
+    "/DELAYLOAD:advapi32.dll",
     "/DELAYLOAD:dbghelp.dll",
-    "/DELAYLOAD:ole32.dll",
-    "/DELAYLOAD:psapi.dll",
     "/DELAYLOAD:rpcrt4.dll",
-    "/DELAYLOAD:shell32.dll",
-    "/DELAYLOAD:user32.dll",
-    "/DELAYLOAD:winhttp.dll",
     "/DELAYLOAD:winmm.dll",
-    "/DELAYLOAD:ws2_32.dll",
   ]
   if (current_cpu == "x86") {
     # Don"t set an x64 base address (to avoid breaking HE-ASLR).
@@ -80,7 +70,7 @@
 ## source sets
 ##------------------------------------------------------------------------------
 
-source_set("chrome_elf_security") {
+source_set("security") {
   sources = [
     "chrome_elf_security.cc",
     "chrome_elf_security.h",
@@ -129,30 +119,48 @@
     "blacklist/blacklist.h",
     "blacklist/blacklist_interceptions.cc",
     "blacklist/blacklist_interceptions.h",
-    "blacklist/crashpad_helper.cc",
-    "blacklist/crashpad_helper.h",
   ]
   public_deps = [
     "//sandbox",
   ]
   deps = [
     ":constants",
+    ":crash",
     ":hook_util",
+    "//base:base_static",  # pe_image
     "//chrome/install_static:install_static_util",
     "//chrome_elf/nt_registry:nt_registry",
+  ]
+}
 
-    # Still uses base/win/pe_image.h
-    "//base",
-    "//third_party/crashpad/crashpad/client:client",
+static_library("crash") {
+  sources = [
+    "../chrome/app/chrome_crash_reporter_client_win.cc",
+    "../chrome/app/chrome_crash_reporter_client_win.h",
+    "../chrome/common/chrome_result_codes.h",
+    "crash/crash_helper.cc",
+    "crash/crash_helper.h",
+  ]
+  deps = [
+    ":hook_util",
+    "//base:base",  # This needs to go.  DEP of app, crash_keys, client.
+    "//base:base_static",  # pe_image
+    "//chrome/install_static:install_static_util",
+    "//components/crash/content/app:app",
+    "//components/crash/core/common",  # crash_keys
+    "//content/public/common:result_codes",
+    "//third_party/crashpad/crashpad/client:client",  # DumpWithoutCrash
   ]
 }
 
 static_library("hook_util") {
   sources = [
-    "hook_util/thunk_getter.cc",
-    "hook_util/thunk_getter.h",
+    "../base/macros.h",
+    "hook_util/hook_util.cc",
+    "hook_util/hook_util.h",
   ]
   deps = [
+    "//base:base_static",  # pe_image
     "//sandbox",
   ]
 }
@@ -167,15 +175,18 @@
     "blacklist/test/blacklist_test.cc",
     "chrome_elf_util_unittest.cc",
     "elf_imports_unittest.cc",
+    "hook_util/test/hook_util_test.cc",
     "run_all_unittests.cc",
   ]
   include_dirs = [ "$target_gen_dir" ]
   deps = [
     ":blacklist",
     ":blacklist_test_main_dll",
-    ":chrome_elf_security",
     ":constants",
+    ":crash",
     ":hook_util",
+    ":hook_util_test_dll",
+    ":security",
     "//base",
     "//base/test:test_support",
     "//chrome",
@@ -199,17 +210,15 @@
     ":blacklist_test_dll_3",
     ":chrome_elf",
   ]
+
+  # Don't want the test-specific dependencies to affect ChromeElfLoadSanityTest.
+  # In particular, a few system DLLs cause user32 to be loaded, which is bad.
   ldflags = [
-    "/DELAYLOAD:dbghelp.dll",
+    "/DELAYLOAD:advapi32.dll",
     "/DELAYLOAD:ole32.dll",
-    "/DELAYLOAD:psapi.dll",
-    "/DELAYLOAD:rpcrt4.dll",
     "/DELAYLOAD:shell32.dll",
-    "/DELAYLOAD:shlwapi.dll",
     "/DELAYLOAD:user32.dll",
-    "/DELAYLOAD:winhttp.dll",
     "/DELAYLOAD:winmm.dll",
-    "/DELAYLOAD:ws2_32.dll",
   ]
 }
 
@@ -225,18 +234,6 @@
     "//chrome/install_static:install_static_util",
     "//chrome_elf/nt_registry:nt_registry",
   ]
-  ldflags = [
-    "/NODEFAULTLIB:user32.lib",
-    "/DELAYLOAD:dbghelp.dll",
-    "/DELAYLOAD:ole32.dll",
-    "/DELAYLOAD:psapi.dll",
-    "/DELAYLOAD:rpcrt4.dll",
-    "/DELAYLOAD:shell32.dll",
-    "/DELAYLOAD:user32.dll",
-    "/DELAYLOAD:winhttp.dll",
-    "/DELAYLOAD:winmm.dll",
-    "/DELAYLOAD:ws2_32.dll",
-  ]
 }
 
 loadable_module("blacklist_test_dll_1") {
@@ -272,3 +269,13 @@
     "//build/config/sanitizers:deps",
   ]
 }
+
+shared_library("hook_util_test_dll") {
+  sources = [
+    "hook_util/test/hook_util_test_dll.cc",
+    "hook_util/test/hook_util_test_dll.h",
+  ]
+  deps = [
+    "//build/config/sanitizers:deps",
+  ]
+}
diff --git a/chrome_elf/blacklist.gypi b/chrome_elf/blacklist.gypi
index d87255a..3f0fe274 100644
--- a/chrome_elf/blacklist.gypi
+++ b/chrome_elf/blacklist.gypi
@@ -15,8 +15,6 @@
         'blacklist/blacklist.h',
         'blacklist/blacklist_interceptions.cc',
         'blacklist/blacklist_interceptions.h',
-        'blacklist/crashpad_helper.cc',
-        'blacklist/crashpad_helper.h',
       ],
       'dependencies': [
         '../base/base.gyp:base',
@@ -24,7 +22,6 @@
         '../chrome_elf/chrome_elf.gyp:chrome_elf_constants',
         '../chrome_elf/chrome_elf.gyp:chrome_elf_hook_util',
         '../chrome_elf/nt_registry/nt_registry.gyp:chrome_elf_nt_registry',
-        '../components/components.gyp:crash_component',
         '../sandbox/sandbox.gyp:sandbox',
       ],
     },
@@ -38,6 +35,7 @@
       'dependencies': [
         '../base/base.gyp:base',
         '../chrome/chrome.gyp:install_static_util',
+        '../chrome_elf/chrome_elf.gyp:chrome_elf_crash',
         '../chrome_elf/nt_registry/nt_registry.gyp:chrome_elf_nt_registry',
         'blacklist',
       ],
diff --git a/chrome_elf/blacklist/blacklist.cc b/chrome_elf/blacklist/blacklist.cc
index 2b441a6..05d917f 100644
--- a/chrome_elf/blacklist/blacklist.cc
+++ b/chrome_elf/blacklist/blacklist.cc
@@ -12,7 +12,7 @@
 #include "chrome/install_static/install_util.h"
 #include "chrome_elf/blacklist/blacklist_interceptions.h"
 #include "chrome_elf/chrome_elf_constants.h"
-#include "chrome_elf/hook_util/thunk_getter.h"
+#include "chrome_elf/hook_util/hook_util.h"
 #include "chrome_elf/nt_registry/nt_registry.h"
 #include "sandbox/win/src/interception_internal.h"
 #include "sandbox/win/src/internal_types.h"
@@ -291,7 +291,7 @@
   const bool kRelaxed = false;
 
   // Create a thunk via the appropriate ServiceResolver instance.
-  sandbox::ServiceResolverThunk* thunk = GetThunk(kRelaxed);
+  sandbox::ServiceResolverThunk* thunk = elf_hook::HookSystemService(kRelaxed);
 
   // Don't try blacklisting on unsupported OS versions.
   if (!thunk)
diff --git a/chrome_elf/blacklist/blacklist_interceptions.cc b/chrome_elf/blacklist/blacklist_interceptions.cc
index 7d97d85..2f508d4 100644
--- a/chrome_elf/blacklist/blacklist_interceptions.cc
+++ b/chrome_elf/blacklist/blacklist_interceptions.cc
@@ -18,7 +18,7 @@
 // base_static (see base/base.gyp) are allowed here.
 #include "base/win/pe_image.h"
 #include "chrome_elf/blacklist/blacklist.h"
-#include "chrome_elf/blacklist/crashpad_helper.h"
+#include "chrome_elf/crash/crash_helper.h"
 #include "sandbox/win/src/internal_types.h"
 #include "sandbox/win/src/nt_internals.h"
 #include "sandbox/win/src/sandbox_nt_util.h"
@@ -262,7 +262,7 @@
                                    base, zero_bits, commit_size, offset,
                                    view_size, inherit, allocation_type,
                                    protect);
-  } __except(GenerateCrashDump(GetExceptionInformation())) {
+  } __except (elf_crash::GenerateCrashDump(GetExceptionInformation())) {
   }
 
   return ret;
diff --git a/chrome_elf/blacklist/crashpad_helper.cc b/chrome_elf/blacklist/crashpad_helper.cc
deleted file mode 100644
index e1a1ef8..0000000
--- a/chrome_elf/blacklist/crashpad_helper.cc
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2016 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/crashpad_helper.h"
-
-#include "third_party/crashpad/crashpad/client/crashpad_client.h"
-
-int GenerateCrashDump(EXCEPTION_POINTERS* exception_pointers) {
-  crashpad::CrashpadClient::DumpWithoutCrash(
-      *(exception_pointers->ContextRecord));
-  return EXCEPTION_CONTINUE_SEARCH;
-}
diff --git a/chrome_elf/blacklist/crashpad_helper.h b/chrome_elf/blacklist/crashpad_helper.h
deleted file mode 100644
index d5a781b..0000000
--- a/chrome_elf/blacklist/crashpad_helper.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2016 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.
-
-#ifndef CHROME_ELF_BLACKLIST_CRASHPAD_HELPER_H_
-#define CHROME_ELF_BLACKLIST_CRASHPAD_HELPER_H_
-
-#include <windows.h>
-
-// Exception handler for exceptions in chrome_elf which need to be passed on to
-// the next handler in the chain. Examples include exceptions in DllMain,
-// blacklist interception code, etc.
-int GenerateCrashDump(EXCEPTION_POINTERS* exception_pointers);
-
-#endif  // CHROME_ELF_BLACKLIST_CRASHPAD_HELPER_H_
diff --git a/chrome_elf/chrome_elf.gyp b/chrome_elf/chrome_elf.gyp
index c9a412b..9f87b4c 100644
--- a/chrome_elf/chrome_elf.gyp
+++ b/chrome_elf/chrome_elf.gyp
@@ -40,20 +40,16 @@
         'chrome_elf.def',
         'chrome_elf_main.cc',
         'chrome_elf_main.h',
-        '../chrome/app/chrome_crash_reporter_client_win.cc',
-        '../chrome/app/chrome_crash_reporter_client_win.h',
         '<(SHARED_INTERMEDIATE_DIR)/chrome_elf/chrome_elf_version.rc',
       ],
       'dependencies': [
         '../chrome/chrome.gyp:install_static_util',
         'blacklist',
+        'chrome_elf_crash',
         'chrome_elf_hook_util',
         'chrome_elf_resources',
         'chrome_elf_security',
         'nt_registry/nt_registry.gyp:chrome_elf_nt_registry',
-        '../chrome/chrome.gyp:install_static_util',
-        '../components/components.gyp:crash_component',
-        '../components/components.gyp:crash_core_common',
       ],
       'msvs_settings': {
         'VCLinkerTool': {
@@ -89,14 +85,41 @@
       ],
     },
     {
+      'target_name': 'chrome_elf_crash',
+      'type': 'static_library',
+      'include_dirs': [
+        '..',
+      ],
+      'sources': [
+        '../chrome/app/chrome_crash_reporter_client_win.cc',
+        '../chrome/app/chrome_crash_reporter_client_win.h',
+        '../chrome/common/chrome_result_codes.h',
+        'crash/crash_helper.cc',
+        'crash/crash_helper.h',
+      ],
+      'dependencies': [
+        '../base/base.gyp:base', # This needs to go.
+        '../base/base.gyp:base_static', # pe_image
+        '../chrome/chrome.gyp:install_static_util',
+        '../components/components.gyp:crash_component',
+        '../components/components.gyp:crash_core_common', #crash_keys
+        'chrome_elf_hook_util',
+      ],
+    },
+    {
       'target_name': 'chrome_elf_hook_util',
       'type': 'static_library',
       'include_dirs': [
         '..',
       ],
       'sources': [
-        'hook_util/thunk_getter.cc',
-        'hook_util/thunk_getter.h',
+        '../base/macros.h',
+        'hook_util/hook_util.cc',
+        'hook_util/hook_util.h',
+      ],
+      'dependencies': [
+        '../base/base.gyp:base_static', # pe_image
+        '../sandbox/sandbox.gyp:sandbox',
       ],
     },
     {
@@ -143,6 +166,7 @@
         'blacklist_test_dll_2',
         'blacklist_test_dll_3',
         'blacklist_test_main_dll',
+        'chrome_elf_crash',
         'chrome_elf_hook_util',
         'chrome_elf_security',
         'nt_registry/nt_registry.gyp:chrome_elf_nt_registry',
diff --git a/chrome_elf/chrome_elf_main.cc b/chrome_elf/chrome_elf_main.cc
index e2b57aa..fa60e2d 100644
--- a/chrome_elf/chrome_elf_main.cc
+++ b/chrome_elf/chrome_elf_main.cc
@@ -4,127 +4,40 @@
 
 #include "chrome_elf/chrome_elf_main.h"
 
+#include <assert.h>
 #include <windows.h>
-#include <algorithm>
 
-#include "base/lazy_instance.h"
-#include "base/strings/string16.h"
-#include "base/win/iat_patch_function.h"
-#include "build/build_config.h"
-#include "chrome/app/chrome_crash_reporter_client_win.h"
 #include "chrome/install_static/install_util.h"
 #include "chrome_elf/blacklist/blacklist.h"
-#include "chrome_elf/blacklist/crashpad_helper.h"
-#include "chrome_elf/chrome_elf_constants.h"
-#include "components/crash/content/app/crashpad.h"
-#include "components/crash/core/common/crash_keys.h"
-
-namespace {
-
-base::LazyInstance<std::vector<crash_reporter::Report>>::Leaky g_crash_reports =
-    LAZY_INSTANCE_INITIALIZER;
-
-// Gets the exe name from the full path of the exe.
-base::string16 GetExeName() {
-  wchar_t file_path[MAX_PATH] = {};
-  if (!::GetModuleFileName(nullptr, file_path, arraysize(file_path))) {
-    assert(false);
-    return base::string16();
-  }
-  base::string16 file_name_string = file_path;
-  size_t last_slash_pos = file_name_string.find_last_of(L'\\');
-  if (last_slash_pos != base::string16::npos) {
-    file_name_string = file_name_string.substr(
-        last_slash_pos + 1, file_name_string.length() - last_slash_pos);
-  }
-  std::transform(file_name_string.begin(), file_name_string.end(),
-                 file_name_string.begin(), ::tolower);
-  return file_name_string;
-}
-
-void InitializeCrashReportingForProcess() {
-  // We want to initialize crash reporting only in chrome.exe
-  if (GetExeName() != L"chrome.exe")
-    return;
-  ChromeCrashReporterClient::InitializeCrashReportingForProcess();
-}
-
-#if !defined(ADDRESS_SANITIZER)
-// chrome_elf loads early in the process and initializes Crashpad. That in turn
-// uses the SetUnhandledExceptionFilter API to set a top level exception
-// handler for the process. When the process eventually initializes, CRT sets
-// an exception handler which calls TerminateProcess which effectively bypasses
-// us. Ideally we want to be at the top of the unhandled exception filter
-// chain. However we don't have a good way of intercepting the
-// SetUnhandledExceptionFilter API in the sandbox. EAT patching kernel32 or
-// kernelbase should ideally work. However the kernel32 kernelbase dlls are
-// prebound which causes EAT patching to not work. Sidestep works. However it
-// is only supported for 32 bit. For now we use IAT patching for the
-// executable.
-// TODO(ananta).
-// Check if it is possible to fix EAT patching or use sidestep patching for
-// 32 bit and 64 bit for this purpose.
-base::win::IATPatchFunction g_set_unhandled_exception_filter;
-
-LPTOP_LEVEL_EXCEPTION_FILTER WINAPI
-SetUnhandledExceptionFilterPatch(LPTOP_LEVEL_EXCEPTION_FILTER filter) {
-  // Don't set the exception filter. Please see above for comments.
-  return nullptr;
-}
-
-// Please refer above to more information about why we intercept the
-// SetUnhandledExceptionFilter API.
-void DisableSetUnhandledExceptionFilter() {
-  DWORD patched = g_set_unhandled_exception_filter.PatchFromModule(
-      GetModuleHandle(nullptr), "kernel32.dll", "SetUnhandledExceptionFilter",
-      SetUnhandledExceptionFilterPatch);
-  CHECK(patched == 0);
-}
-#endif // !defined(ADDRESS_SANITIZER)
-
-}  // namespace
+#include "chrome_elf/crash/crash_helper.h"
 
 void SignalChromeElf() {
   blacklist::ResetBeacon();
 }
 
-// This helper is invoked by code in chrome.dll to retrieve the crash reports.
-// See CrashUploadListCrashpad. Note that we do not pass an std::vector here,
-// because we do not want to allocate/free in different modules. The returned
-// pointer is read-only.
-extern "C" __declspec(dllexport) void GetCrashReportsImpl(
-    const crash_reporter::Report** reports,
-    size_t* report_count) {
-  crash_reporter::GetReports(g_crash_reports.Pointer());
-  *reports = g_crash_reports.Pointer()->data();
-  *report_count = g_crash_reports.Pointer()->size();
-}
-
-// This helper is invoked by debugging code in chrome to register the client
-// id.
-extern "C" __declspec(dllexport) void SetMetricsClientId(
-    const char* client_id) {
-  if (client_id)
-    crash_keys::SetMetricsClientIdFromGUID(client_id);
-}
-
 BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) {
   if (reason == DLL_PROCESS_ATTACH) {
-    InitializeCrashReportingForProcess();
-    // CRT on initialization installs an exception filter which calls
-    // TerminateProcess. We need to hook CRT's attempt to set an exception
-    // handler and ignore it. Don't do this when ASan is present, or ASan will
-    // fail to install its own unhandled exception filter.
+    if (!elf_crash::InitializeCrashReporting()) {
+#ifdef _DEBUG
+      assert(false);
+#endif  // _DEBUG
+    }
+// CRT on initialization installs an exception filter which calls
+// TerminateProcess. We need to hook CRT's attempt to set an exception.
+// NOTE: Do not hook if ASan is present, or ASan will fail to install
+// its own unhandled exception filter.
 #if !defined(ADDRESS_SANITIZER)
-    DisableSetUnhandledExceptionFilter();
-#endif
+    elf_crash::DisableSetUnhandledExceptionFilter();
+#endif  // !defined (ADDRESS_SANITIZER)
 
     install_static::InitializeProcessType();
 
     __try {
       blacklist::Initialize(false);  // Don't force, abort if beacon is present.
-    } __except(GenerateCrashDump(GetExceptionInformation())) {
+    } __except (elf_crash::GenerateCrashDump(GetExceptionInformation())) {
     }
+  } else if (reason == DLL_PROCESS_DETACH) {
+    elf_crash::ShutdownCrashReporting();
   }
   return TRUE;
 }
diff --git a/chrome_elf/chrome_elf_security.cc b/chrome_elf/chrome_elf_security.cc
index b1ddcd9..2b3c825 100644
--- a/chrome_elf/chrome_elf_security.cc
+++ b/chrome_elf/chrome_elf_security.cc
@@ -11,6 +11,8 @@
 #include "chrome_elf/chrome_elf_constants.h"
 #include "chrome_elf/nt_registry/nt_registry.h"
 
+namespace elf_security {
+
 void EarlyBrowserSecurity() {
   typedef decltype(SetProcessMitigationPolicy)* SetProcessMitigationPolicyFunc;
 
@@ -47,3 +49,4 @@
   }
   return;
 }
+}  // namespace elf_security
diff --git a/chrome_elf/chrome_elf_security.h b/chrome_elf/chrome_elf_security.h
index 3f4db553..faf2057 100644
--- a/chrome_elf/chrome_elf_security.h
+++ b/chrome_elf/chrome_elf_security.h
@@ -5,7 +5,11 @@
 #ifndef CHROME_ELF_CHROME_ELF_SECURITY_H_
 #define CHROME_ELF_CHROME_ELF_SECURITY_H_
 
+namespace elf_security {
+
 // Setup any early browser-process security.
 void EarlyBrowserSecurity();
 
+}  // namespace elf_security
+
 #endif  // CHROME_ELF_CHROME_ELF_SECURITY_H_
diff --git a/chrome_elf/chrome_elf_util_unittest.cc b/chrome_elf/chrome_elf_util_unittest.cc
index ad4671a..05a2f5f 100644
--- a/chrome_elf/chrome_elf_util_unittest.cc
+++ b/chrome_elf/chrome_elf_util_unittest.cc
@@ -112,12 +112,12 @@
 
   // First, ensure that the emergency-off finch signal works.
   EXPECT_TRUE(SetSecurityFinchFlag(true));
-  EarlyBrowserSecurity();
+  elf_security::EarlyBrowserSecurity();
   EXPECT_FALSE(IsSecuritySet());
   EXPECT_TRUE(SetSecurityFinchFlag(false));
 
   // Second, test that the process mitigation is set when no finch signal.
-  EarlyBrowserSecurity();
+  elf_security::EarlyBrowserSecurity();
   EXPECT_TRUE(IsSecuritySet());
 }
 
diff --git a/chrome_elf/crash/crash_helper.cc b/chrome_elf/crash/crash_helper.cc
new file mode 100644
index 0000000..0895a981
--- /dev/null
+++ b/chrome_elf/crash/crash_helper.cc
@@ -0,0 +1,177 @@
+// Copyright 2016 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/crash/crash_helper.h"
+
+#include <assert.h>
+#include <windows.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "chrome/app/chrome_crash_reporter_client_win.h"
+#include "chrome_elf/hook_util/hook_util.h"
+#include "components/crash/content/app/crashpad.h"
+#include "components/crash/core/common/crash_keys.h"
+#include "third_party/crashpad/crashpad/client/crashpad_client.h"
+
+namespace {
+
+// Crash handling from elf is only enabled for the chrome.exe process.
+// Use this global to safely handle the rare case where elf may not be in that
+// process (e.g. tests).
+bool g_crash_helper_enabled = false;
+
+// Gets the exe name from the full path of the exe.
+bool GetExeName(std::wstring* exe_name) {
+  std::wstring file_name;
+  file_name.resize(MAX_PATH);
+  if (!::GetModuleFileNameW(nullptr, &file_name[0], MAX_PATH)) {
+    assert(false);
+    return false;
+  }
+  size_t last_slash_pos = file_name.find_last_of(L'\\');
+  if (last_slash_pos != std::wstring::npos) {
+    *exe_name = file_name.substr(last_slash_pos + 1,
+                                 file_name.length() - last_slash_pos);
+  }
+  std::transform(exe_name->begin(), exe_name->end(), exe_name->begin(),
+                 ::tolower);
+  return true;
+}
+
+// Global pointer to a vector of crash reports.
+// This structure will be initialized in InitializeCrashReportingForProcess()
+// and cleaned up in DllDetachCrashReportingCleanup().
+std::vector<crash_reporter::Report>* g_crash_reports = nullptr;
+
+// chrome_elf loads early in the process and initializes Crashpad. That in turn
+// uses the SetUnhandledExceptionFilter API to set a top level exception
+// handler for the process. When the process eventually initializes, CRT sets
+// an exception handler which calls TerminateProcess which effectively bypasses
+// us. Ideally we want to be at the top of the unhandled exception filter
+// chain. However we don't have a good way of intercepting the
+// SetUnhandledExceptionFilter API in the sandbox. EAT patching kernel32 or
+// kernelbase should ideally work. However the kernel32 kernelbase dlls are
+// prebound which causes EAT patching to not work. Sidestep works. However it
+// is only supported for 32 bit. For now we use IAT patching for the
+// executable.
+// TODO(ananta).
+// Check if it is possible to fix EAT patching or use sidestep patching for
+// 32 bit and 64 bit for this purpose.
+elf_hook::IATHook* g_set_unhandled_exception_filter = nullptr;
+
+// Hook function, which ignores the request to set an unhandled-exception
+// filter.
+LPTOP_LEVEL_EXCEPTION_FILTER WINAPI
+SetUnhandledExceptionFilterPatch(LPTOP_LEVEL_EXCEPTION_FILTER filter) {
+  // Don't set the exception filter. Please see above for comments.
+  return nullptr;
+}
+
+}  // namespace
+
+//------------------------------------------------------------------------------
+// Public chrome_elf crash APIs
+//------------------------------------------------------------------------------
+
+namespace elf_crash {
+
+// NOTE: This function will be called from DllMain during DLL_PROCESS_ATTACH
+// (while we have the loader lock), so do not misbehave.
+bool InitializeCrashReporting() {
+  // We want to initialize crash reporting only in chrome.exe
+  std::wstring proc_name;
+  if (!GetExeName(&proc_name) ||
+      0 != ::wcscmp(proc_name.data(), L"chrome.exe")) {
+#ifdef _DEBUG
+    assert(false);
+#endif  // _DEBUG
+    return false;
+  }
+
+#ifdef _DEBUG
+  assert(g_crash_reports == nullptr);
+  assert(g_set_unhandled_exception_filter == nullptr);
+#endif  // _DEBUG
+
+  // No global objects with destructors, so using global pointers.
+  // DllMain on detach will clean these up.
+  g_crash_reports = new std::vector<crash_reporter::Report>;
+  g_set_unhandled_exception_filter = new elf_hook::IATHook();
+
+  ChromeCrashReporterClient::InitializeCrashReportingForProcess();
+
+  g_crash_helper_enabled = true;
+  return true;
+}
+
+// NOTE: This function will be called from DllMain during DLL_PROCESS_DETACH
+// (while we have the loader lock), so do not misbehave.
+void ShutdownCrashReporting() {
+  if (g_crash_reports != nullptr) {
+    g_crash_reports->clear();
+    delete g_crash_reports;
+  }
+  if (g_set_unhandled_exception_filter != nullptr) {
+    delete g_set_unhandled_exception_filter;
+  }
+}
+
+// Please refer to the comment on g_set_unhandled_exception_filter for more
+// information about why we intercept the SetUnhandledExceptionFilter API.
+void DisableSetUnhandledExceptionFilter() {
+  if (!g_crash_helper_enabled)
+    return;
+  if (g_set_unhandled_exception_filter->Hook(
+          ::GetModuleHandle(nullptr), "kernel32.dll",
+          "SetUnhandledExceptionFilter",
+          SetUnhandledExceptionFilterPatch) != NO_ERROR) {
+#ifdef _DEBUG
+    assert(false);
+#endif  //_DEBUG
+  }
+}
+
+int GenerateCrashDump(EXCEPTION_POINTERS* exception_pointers) {
+  if (g_crash_helper_enabled)
+    crashpad::CrashpadClient::DumpWithoutCrash(
+        *(exception_pointers->ContextRecord));
+  return EXCEPTION_CONTINUE_SEARCH;
+}
+
+}  // namespace elf_crash
+
+//------------------------------------------------------------------------------
+// Exported crash APIs for the rest of the process.
+//------------------------------------------------------------------------------
+
+// This helper is invoked by code in chrome.dll to retrieve the crash reports.
+// See CrashUploadListCrashpad. Note that we do not pass a std::vector here,
+// because we do not want to allocate/free in different modules. The returned
+// pointer is read-only.
+//
+// NOTE: Since the returned pointer references read-only memory that will be
+// cleaned up when this DLL unloads, be careful not to reference the memory
+// beyond that point (e.g. during tests).
+extern "C" __declspec(dllexport) void GetCrashReportsImpl(
+    const crash_reporter::Report** reports,
+    size_t* report_count) {
+  if (!g_crash_helper_enabled)
+    return;
+  crash_reporter::GetReports(g_crash_reports);
+  *reports = g_crash_reports->data();
+  *report_count = g_crash_reports->size();
+}
+
+// This helper is invoked by debugging code in chrome to register the client
+// id.
+extern "C" __declspec(dllexport) void SetMetricsClientId(
+    const char* client_id) {
+  if (!g_crash_helper_enabled)
+    return;
+  if (client_id)
+    crash_keys::SetMetricsClientIdFromGUID(client_id);
+}
diff --git a/chrome_elf/crash/crash_helper.h b/chrome_elf/crash/crash_helper.h
new file mode 100644
index 0000000..4275abe
--- /dev/null
+++ b/chrome_elf/crash/crash_helper.h
@@ -0,0 +1,32 @@
+// Copyright 2016 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.
+
+#ifndef CHROME_ELF_CRASH_CRASH_HELPER_H_
+#define CHROME_ELF_CRASH_CRASH_HELPER_H_
+
+#include <windows.h>
+
+// Keep all crash-related APIs here.  All other chrome_elf code should call here
+// for crash support.
+namespace elf_crash {
+
+// Init the crash handling system for entire process.
+bool InitializeCrashReporting();
+
+// Any late cleanup of the crash handling system.
+void ShutdownCrashReporting();
+
+// Permanently disables subsequent calls to
+// kernel32!SetUnhandledExceptionFilter(), see comment in .cc for why this is
+// needed.
+void DisableSetUnhandledExceptionFilter();
+
+// Exception handler for exceptions in chrome_elf which need to be passed on to
+// the next handler in the chain. Examples include exceptions in DllMain,
+// blacklist interception code, etc.
+// Note: the handler takes a minidump.
+int GenerateCrashDump(EXCEPTION_POINTERS* exception_pointers);
+}
+
+#endif  // CHROME_ELF_CRASH_CRASH_HELPER_H_
diff --git a/chrome_elf/elf_imports_unittest.cc b/chrome_elf/elf_imports_unittest.cc
index 1e399ae..1c40815 100644
--- a/chrome_elf/elf_imports_unittest.cc
+++ b/chrome_elf/elf_imports_unittest.cc
@@ -111,7 +111,9 @@
   // We don't expect user32 to be loaded in chrome_elf_unittests. If this test
   // case fails, then it means that a dependency on user32 has crept into the
   // chrome_elf_unittests executable, which needs to be removed.
-  EXPECT_EQ(nullptr, ::GetModuleHandle(L"user32.dll"));
+  // NOTE: it may be a secondary dependency of another system DLL.  If so,
+  // try adding a "/DELAYLOAD:<blah>.dll" to the build.gn file.
+  ASSERT_EQ(nullptr, ::GetModuleHandle(L"user32.dll"));
 
   HMODULE chrome_elf_module_handle = ::LoadLibrary(dll.value().c_str());
   EXPECT_TRUE(chrome_elf_module_handle != nullptr);
diff --git a/chrome_elf/hook_util/hook_util.cc b/chrome_elf/hook_util/hook_util.cc
new file mode 100644
index 0000000..e16330d
--- /dev/null
+++ b/chrome_elf/hook_util/hook_util.cc
@@ -0,0 +1,334 @@
+// Copyright 2014 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 "hook_util.h"
+
+#include <versionhelpers.h>  // windows.h must be before
+
+#include "base/win/pe_image.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"
+
+namespace {
+
+//------------------------------------------------------------------------------
+// Common hooking utility functions - LOCAL
+//------------------------------------------------------------------------------
+
+#if !defined(_WIN64)
+// 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;
+}
+#endif  // !defined(_WIN64)
+
+// Change the page protections to writable, copy the data,
+// restore protections. Returns a winerror code.
+DWORD PatchMem(void* target, void* new_bytes, size_t length) {
+  if (target == nullptr || new_bytes == nullptr || length == 0)
+    return ERROR_INVALID_PARAMETER;
+
+  // Preserve executable state.
+  MEMORY_BASIC_INFORMATION memory_info = {};
+  if (!::VirtualQuery(target, &memory_info, sizeof(memory_info))) {
+    return GetLastError();
+  }
+
+  DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
+                         PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
+                        memory_info.Protect;
+
+  // Make target writeable.
+  DWORD old_page_protection = 0;
+  if (!::VirtualProtect(target, length,
+                        is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
+                        &old_page_protection)) {
+    return GetLastError();
+  }
+
+  // Write the data.
+  ::memcpy(target, new_bytes, length);
+
+  // Restore old page protection.
+  if (!::VirtualProtect(target, length, old_page_protection,
+                        &old_page_protection)) {
+// Yes, this could fail.  However, memory was already patched.
+#ifdef _DEBUG
+    assert(false);
+#endif  // _DEBUG
+  }
+
+  return NO_ERROR;
+}
+
+//------------------------------------------------------------------------------
+// Import Address Table hooking support - LOCAL
+//------------------------------------------------------------------------------
+
+void* GetIATFunctionPtr(IMAGE_THUNK_DATA* iat_thunk) {
+  if (iat_thunk == nullptr)
+    return nullptr;
+
+  // Works around the 64 bit portability warning:
+  // The Function member inside IMAGE_THUNK_DATA is really a pointer
+  // to the IAT function. IMAGE_THUNK_DATA correctly maps to IMAGE_THUNK_DATA32
+  // or IMAGE_THUNK_DATA64 for correct pointer size.
+  union FunctionThunk {
+    IMAGE_THUNK_DATA thunk;
+    void* pointer;
+  } iat_function;
+
+  iat_function.thunk = *iat_thunk;
+  return iat_function.pointer;
+}
+
+// Used to pass target function information during pe_image enumeration.
+struct IATHookFunctionInfo {
+  bool finished_operation;
+  const char* imported_from_module;
+  const char* function_name;
+  void* new_function;
+  void** old_function;
+  IMAGE_THUNK_DATA** iat_thunk;
+  DWORD return_code;
+};
+
+// Callback function for pe_image enumeration.  This function is called from
+// within PEImage::EnumOneImportChunk().
+// NOTE: Returning true means continue enumerating.  False means stop.
+bool IATFindHookFuncCallback(const base::win::PEImage& image,
+                             const char* module,
+                             DWORD ordinal,
+                             const char* import_name,
+                             DWORD hint,
+                             IMAGE_THUNK_DATA* iat,
+                             void* cookie) {
+  IATHookFunctionInfo* hook_func_info =
+      reinterpret_cast<IATHookFunctionInfo*>(cookie);
+  if (hook_func_info == nullptr)
+    return false;
+
+  // Check for the right module.
+  if (module == nullptr ||
+      ::strnicmp(module, hook_func_info->imported_from_module,
+                 ::strlen(module)) != 0)
+    return true;
+
+  // Check for the right function.
+  if (import_name == nullptr ||
+      ::strnicmp(import_name, hook_func_info->function_name,
+                 ::strlen(import_name)) != 0)
+    return true;
+
+  // At this point, the target function was found.  Even if something fails now,
+  // don't do any further enumerating.
+  hook_func_info->finished_operation = true;
+
+  // This is it.  Do the hook!
+  // 1) Save the old function pointer.
+  *(hook_func_info->old_function) = GetIATFunctionPtr(iat);
+
+  // 2) Save the IAT thunk.
+  *(hook_func_info->iat_thunk) = iat;
+
+  // 3) Sanity check the pointer sizes (architectures).
+  if (sizeof(iat->u1.Function) != sizeof(hook_func_info->new_function)) {
+    hook_func_info->return_code = ERROR_BAD_ENVIRONMENT;
+#ifdef _DEBUG
+    assert(false);
+#endif  // _DEBUG
+    return false;
+  }
+
+  // 4) Sanity check that the new hook function is not actually the
+  //    same as the existing function for this import!
+  if (*(hook_func_info->old_function) == hook_func_info->new_function) {
+    hook_func_info->return_code = ERROR_INVALID_FUNCTION;
+#ifdef _DEBUG
+    assert(false);
+#endif  // _DEBUG
+    return false;
+  }
+
+  // 5) Patch the function pointer.
+  hook_func_info->return_code =
+      PatchMem(&(iat->u1.Function), &(hook_func_info->new_function),
+               sizeof(hook_func_info->new_function));
+
+  return false;
+}
+
+// Applies an import-address-table hook.  Returns a system winerror.h code.
+// Call RemoveIATHook() with |new_function|, |old_function| and |iat_thunk|
+// to remove the hook.
+DWORD ApplyIATHook(HMODULE module_handle,
+                   const char* imported_from_module,
+                   const char* function_name,
+                   void* new_function,
+                   void** old_function,
+                   IMAGE_THUNK_DATA** iat_thunk) {
+  base::win::PEImage target_image(module_handle);
+  if (!target_image.VerifyMagic())
+    return ERROR_INVALID_PARAMETER;
+
+  IATHookFunctionInfo hook_info = {false,
+                                   imported_from_module,
+                                   function_name,
+                                   new_function,
+                                   old_function,
+                                   iat_thunk,
+                                   ERROR_PROC_NOT_FOUND};
+
+  // First go through the IAT. If we don't find the import we are looking
+  // for in IAT, search delay import table.
+  target_image.EnumAllImports(IATFindHookFuncCallback, &hook_info);
+  if (!hook_info.finished_operation) {
+    target_image.EnumAllDelayImports(IATFindHookFuncCallback, &hook_info);
+  }
+
+  return hook_info.return_code;
+}
+
+// Removes an import-address-table hook.  Returns a system winerror.h code.
+DWORD RemoveIATHook(void* intercept_function,
+                    void* original_function,
+                    IMAGE_THUNK_DATA* iat_thunk) {
+  if (GetIATFunctionPtr(iat_thunk) != intercept_function) {
+#ifdef _DEBUG
+    assert(false);
+#endif  // _DEBUG
+    // Someone else has messed with the same target. Cannot unpatch.
+    return ERROR_INVALID_FUNCTION;
+  }
+
+  return PatchMem(&(iat_thunk->u1.Function), &original_function,
+                  sizeof(original_function));
+}
+
+}  // namespace
+
+namespace elf_hook {
+
+//------------------------------------------------------------------------------
+// System Service hooking support
+//------------------------------------------------------------------------------
+
+sandbox::ServiceResolverThunk* HookSystemService(bool relaxed) {
+  // Create a thunk via the appropriate ServiceResolver instance.
+  sandbox::ServiceResolverThunk* thunk = nullptr;
+
+  // No hooking on unsupported OS versions.
+  if (!::IsWindows7OrGreater())
+    return thunk;
+
+  // Pseudo-handle, no need to close.
+  HANDLE current_process = ::GetCurrentProcess();
+
+#if defined(_WIN64)
+  // ServiceResolverThunk can handle all the formats in 64-bit (instead only
+  // handling one like it does in 32-bit versions).
+  thunk = new sandbox::ServiceResolverThunk(current_process, relaxed);
+#else
+  if (GetWOW64StatusForCurrentProcess() == WOW64_ENABLED) {
+    if (::IsWindows10OrGreater())
+      thunk = new sandbox::Wow64W10ResolverThunk(current_process, relaxed);
+    else if (::IsWindows8OrGreater())
+      thunk = new sandbox::Wow64W8ResolverThunk(current_process, relaxed);
+    else
+      thunk = new sandbox::Wow64ResolverThunk(current_process, relaxed);
+  } else if (::IsWindows8OrGreater()) {
+    thunk = new sandbox::Win8ResolverThunk(current_process, relaxed);
+  } else {
+    thunk = new sandbox::ServiceResolverThunk(current_process, relaxed);
+  }
+#endif
+
+  return thunk;
+}
+
+//------------------------------------------------------------------------------
+// Import Address Table hooking support
+//------------------------------------------------------------------------------
+
+IATHook::IATHook()
+    : intercept_function_(nullptr),
+      original_function_(nullptr),
+      iat_thunk_(nullptr) {}
+
+IATHook::~IATHook() {
+  if (intercept_function_ != nullptr) {
+    if (Unhook() != NO_ERROR) {
+#ifdef _DEBUG
+      assert(false);
+#endif  // _DEBUG
+    }
+  }
+}
+
+DWORD IATHook::Hook(HMODULE module,
+                    const char* imported_from_module,
+                    const char* function_name,
+                    void* new_function) {
+  if ((module == 0 || module == INVALID_HANDLE_VALUE) ||
+      imported_from_module == nullptr || function_name == nullptr ||
+      new_function == nullptr)
+    return ERROR_INVALID_PARAMETER;
+
+  // Only hook once per object, to ensure unhook.
+  if (intercept_function_ != nullptr || original_function_ != nullptr ||
+      iat_thunk_ != nullptr) {
+#ifdef _DEBUG
+    assert(false);
+#endif  //_DEBUG
+    return ERROR_SHARING_VIOLATION;
+  }
+
+  DWORD winerror = ApplyIATHook(module, imported_from_module, function_name,
+                                new_function, &original_function_, &iat_thunk_);
+  if (winerror == NO_ERROR)
+    intercept_function_ = new_function;
+
+  return winerror;
+}
+
+DWORD IATHook::Unhook() {
+  if (intercept_function_ == nullptr || original_function_ == nullptr ||
+      iat_thunk_ == nullptr)
+    return ERROR_INVALID_PARAMETER;
+
+  DWORD winerror =
+      RemoveIATHook(intercept_function_, original_function_, iat_thunk_);
+#ifdef _DEBUG
+  if (winerror != NO_ERROR)
+    assert(false);
+#endif  //_DEBUG
+
+  intercept_function_ = nullptr;
+  original_function_ = nullptr;
+  iat_thunk_ = nullptr;
+
+  return winerror;
+}
+
+}  // namespace elf_hook
diff --git a/chrome_elf/hook_util/hook_util.h b/chrome_elf/hook_util/hook_util.h
new file mode 100644
index 0000000..9185045
--- /dev/null
+++ b/chrome_elf/hook_util/hook_util.h
@@ -0,0 +1,66 @@
+// Copyright 2014 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.
+
+#ifndef CHROME_ELF_HOOK_UTIL_THUNK_GETTER_H_
+#define CHROME_ELF_HOOK_UTIL_THUNK_GETTER_H_
+
+#include <windows.h>
+
+#include "base/macros.h"
+
+namespace sandbox {
+class ServiceResolverThunk;
+}
+
+namespace elf_hook {
+
+//------------------------------------------------------------------------------
+// System Service hooking support
+//------------------------------------------------------------------------------
+
+// Creates a |ServiceResolverThunk| based on the OS version. Ownership of the
+// resulting thunk is passed to the caller.
+sandbox::ServiceResolverThunk* HookSystemService(bool relaxed);
+
+//------------------------------------------------------------------------------
+// Import Address Table hooking support
+//------------------------------------------------------------------------------
+class IATHook {
+ public:
+  IATHook();
+  ~IATHook();
+
+  // Intercept a function in an import table of a specific
+  // module. Saves everything needed to Unhook.
+  //
+  // NOTE: Hook can only be called once at a time, to enable Unhook().
+  //
+  // Arguments:
+  // module                 Module to be intercepted
+  // imported_from_module   Module that exports the 'function_name'
+  // function_name          Name of the API to be intercepted
+  // new_function           New function pointer
+  //
+  // Returns: Windows error code (winerror.h). NO_ERROR if successful.
+  DWORD Hook(HMODULE module,
+             const char* imported_from_module,
+             const char* function_name,
+             void* new_function);
+
+  // Unhook the IAT entry.
+  //
+  // Returns: Windows error code (winerror.h). NO_ERROR if successful.
+  DWORD Unhook();
+
+ private:
+  void* intercept_function_;
+  void* original_function_;
+  IMAGE_THUNK_DATA* iat_thunk_;
+
+  DISALLOW_COPY_AND_ASSIGN(IATHook);
+};
+
+}  // namespace elf_hook
+
+#endif  // CHROME_ELF_HOOK_UTIL_THUNK_GETTER_H_
diff --git a/chrome_elf/hook_util/test/hook_util_test.cc b/chrome_elf/hook_util/test/hook_util_test.cc
new file mode 100644
index 0000000..ee2609c
--- /dev/null
+++ b/chrome_elf/hook_util/test/hook_util_test.cc
@@ -0,0 +1,103 @@
+// Copyright 2016 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 <windows.h>
+
+#include "chrome_elf/hook_util/hook_util.h"
+// Compile in this test DLL, so that it's in the IAT.
+#include "chrome_elf/hook_util/test/hook_util_test_dll.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// IATHook test constants.
+const char kIATTestDllName[] = "hook_util_test_dll.dll";
+const char kIATExportedApiFunction[] = "ExportedApi";
+
+// IATHook function, which does nothing.
+void IATHookedExportedApi() {
+  return;
+}
+
+// Shady third-party IATHook function, which also does nothing, but different
+// chunk of code/address.
+void IATHookedExportedApiTwo() {
+  printf("Something to make this function different!\n");
+  return;
+}
+
+class HookTest : public testing::Test {
+ protected:
+  HookTest() {}
+};
+
+//------------------------------------------------------------------------------
+// IATHook tests
+//------------------------------------------------------------------------------
+
+TEST_F(HookTest, IATHook) {
+  // Sanity test with no hook.
+  ASSERT_EQ(0, ExportedApiCallCount());
+  ExportedApi();
+  ExportedApi();
+  ASSERT_EQ(2, ExportedApiCallCount());
+
+  // Apply IAT hook.
+  elf_hook::IATHook iat_hook;
+  if (iat_hook.Hook(::GetModuleHandle(nullptr), kIATTestDllName,
+                    kIATExportedApiFunction,
+                    IATHookedExportedApi) != NO_ERROR) {
+    ADD_FAILURE();
+    return;
+  }
+
+  // Make sure hooking twice with the same object fails.
+  if (iat_hook.Hook(::GetModuleHandle(nullptr), kIATTestDllName,
+                    kIATExportedApiFunction,
+                    IATHookedExportedApi) != ERROR_SHARING_VIOLATION)
+    ADD_FAILURE();
+
+  // Call count should not change with hook.
+  ExportedApi();
+  ExportedApi();
+  ExportedApi();
+  EXPECT_EQ(2, ExportedApiCallCount());
+
+  // Remove hook.
+  if (iat_hook.Unhook() != NO_ERROR)
+    ADD_FAILURE();
+
+  // Sanity test things are back to normal.
+  ExportedApi();
+  EXPECT_EQ(3, ExportedApiCallCount());
+
+  // Double unhook should fail.
+  if (iat_hook.Unhook() != ERROR_INVALID_PARAMETER)
+    ADD_FAILURE();
+
+  // Try hooking a non-existent function.
+  if (iat_hook.Hook(::GetModuleHandle(nullptr), kIATTestDllName, "FooBarred",
+                    IATHookedExportedApi) != ERROR_PROC_NOT_FOUND)
+    ADD_FAILURE();
+
+  // Test the case where someone else hooks our hook!  Unhook() should leave it.
+  if (iat_hook.Hook(::GetModuleHandle(nullptr), kIATTestDllName,
+                    kIATExportedApiFunction,
+                    IATHookedExportedApi) != NO_ERROR) {
+    ADD_FAILURE();
+    return;
+  }
+  elf_hook::IATHook shady_third_party_iat_hook;
+  if (shady_third_party_iat_hook.Hook(::GetModuleHandle(nullptr),
+                                      kIATTestDllName, kIATExportedApiFunction,
+                                      IATHookedExportedApiTwo) != NO_ERROR)
+    ADD_FAILURE();
+  if (iat_hook.Unhook() != ERROR_INVALID_FUNCTION)
+    ADD_FAILURE();
+  if (shady_third_party_iat_hook.Unhook() != NO_ERROR)
+    ADD_FAILURE();
+  // NOTE: the first hook was left in and couldn't be cleaned up.
+}
+
+}  // namespace
diff --git a/chrome_elf/hook_util/test/hook_util_test_dll.cc b/chrome_elf/hook_util/test/hook_util_test_dll.cc
new file mode 100644
index 0000000..59943bc
--- /dev/null
+++ b/chrome_elf/hook_util/test/hook_util_test_dll.cc
@@ -0,0 +1,22 @@
+// Copyright 2016 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/hook_util/test/hook_util_test_dll.h"
+
+namespace {
+
+int g_export_call_count = 0;
+
+}  // namespace
+
+extern "C" __declspec(dllexport) void ExportedApi() {
+  g_export_call_count++;
+}
+
+extern "C" __declspec(dllexport) int ExportedApiCallCount() {
+  return g_export_call_count;
+}
+
+BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) {
+  return TRUE;
+}
diff --git a/chrome_elf/hook_util/test/hook_util_test_dll.h b/chrome_elf/hook_util/test/hook_util_test_dll.h
new file mode 100644
index 0000000..b119c20
--- /dev/null
+++ b/chrome_elf/hook_util/test/hook_util_test_dll.h
@@ -0,0 +1,14 @@
+// Copyright 2016 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.
+
+#ifndef CHROME_ELF_HOOK_UTIL_TEST_HOOK_UTIL_TEST_DLL_H_
+#define CHROME_ELF_HOOK_UTIL_TEST_HOOK_UTIL_TEST_DLL_H_
+
+#include <windows.h>
+
+extern "C" __declspec(dllexport) void ExportedApi();
+
+extern "C" __declspec(dllexport) int ExportedApiCallCount();
+
+#endif  // CHROME_ELF_HOOK_UTIL_TEST_HOOK_UTIL_TEST_DLL_H_
diff --git a/chrome_elf/hook_util/thunk_getter.cc b/chrome_elf/hook_util/thunk_getter.cc
deleted file mode 100644
index 5d15ea0..0000000
--- a/chrome_elf/hook_util/thunk_getter.cc
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2014 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 <stdint.h>
-#include <windows.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"
-
-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_WIN10,
-  VERSION_WIN_LAST,  // Indicates error condition.
-};
-
-#if !defined(_WIN64)
-// 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;
-}
-#endif  // !defined(_WIN64)
-
-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 == 10) {
-      version_ = VERSION_WIN10;
-    } 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);
-};
-
-}  // namespace
-
-sandbox::ServiceResolverThunk* GetThunk(bool relaxed) {
-  // Create a thunk via the appropriate ServiceResolver instance.
-  sandbox::ServiceResolverThunk* thunk = NULL;
-
-  // No thunks for unsupported OS versions.
-  OSInfo os_info;
-  if (os_info.version() <= VERSION_PRE_XP_SP2)
-    return thunk;
-
-  // Pseudo-handle, no need to close.
-  HANDLE current_process = ::GetCurrentProcess();
-
-#if defined(_WIN64)
-  // ServiceResolverThunk can handle all the formats in 64-bit (instead only
-  // handling one like it does in 32-bit versions).
-  thunk = new sandbox::ServiceResolverThunk(current_process, relaxed);
-#else
-  if (GetWOW64StatusForCurrentProcess() == WOW64_ENABLED) {
-    if (os_info.version() >= VERSION_WIN10)
-      thunk = new sandbox::Wow64W10ResolverThunk(current_process, relaxed);
-    else if (os_info.version() >= VERSION_WIN8)
-      thunk = new sandbox::Wow64W8ResolverThunk(current_process, relaxed);
-    else
-      thunk = new sandbox::Wow64ResolverThunk(current_process, relaxed);
-  } else if (os_info.version() >= VERSION_WIN8) {
-    thunk = new sandbox::Win8ResolverThunk(current_process, relaxed);
-  } else {
-    thunk = new sandbox::ServiceResolverThunk(current_process, relaxed);
-  }
-#endif
-
-  return thunk;
-}
diff --git a/chrome_elf/hook_util/thunk_getter.h b/chrome_elf/hook_util/thunk_getter.h
deleted file mode 100644
index 68db8f8..0000000
--- a/chrome_elf/hook_util/thunk_getter.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2014 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.
-
-#ifndef CHROME_ELF_HOOK_UTIL_THUNK_GETTER_H_
-#define CHROME_ELF_HOOK_UTIL_THUNK_GETTER_H_
-
-namespace sandbox {
-class ServiceResolverThunk;
-}
-
-// Creates a |ServiceResolverThunk| based on the OS version. Ownership of the
-// resulting thunk is passed to the caller.
-sandbox::ServiceResolverThunk* GetThunk(bool relaxed);
-
-#endif  // CHROME_ELF_HOOK_UTIL_THUNK_GETTER_H_