[win] Create utilities for helping to populate ModuleDatabase.

Further CLs will move (and test!) the rest of the enumerate_modules_model logic to this collection of helper functions.

BUG=662084
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.win:win10_chromium_x64_rel_ng

Review-Url: https://codereview.chromium.org/2653333002
Cr-Commit-Position: refs/heads/master@{#448536}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 798c4d1..b3629c3 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -260,6 +260,8 @@
     "conflicts/module_database_win.h",
     "conflicts/module_event_sink_impl_win.cc",
     "conflicts/module_event_sink_impl_win.h",
+    "conflicts/module_info_util_win.cc",
+    "conflicts/module_info_util_win.h",
     "content_settings/chrome_content_settings_utils.cc",
     "content_settings/chrome_content_settings_utils.h",
     "content_settings/cookie_settings_factory.cc",
diff --git a/chrome/browser/conflicts/module_database_win.cc b/chrome/browser/conflicts/module_database_win.cc
index af71f6ee..c5a090f 100644
--- a/chrome/browser/conflicts/module_database_win.cc
+++ b/chrome/browser/conflicts/module_database_win.cc
@@ -340,10 +340,19 @@
                          mik.module_time_date_stamp);
 }
 
+// ModuleDatabase::CertificateInfo ---------------------------------------------
+
+ModuleDatabase::CertificateInfo::CertificateInfo() : type(NO_CERTIFICATE) {}
+
 // ModuleDatabase::ModuleInfoData ----------------------------------------------
 
 ModuleDatabase::ModuleInfoData::ModuleInfoData() : process_types(0) {}
 
+ModuleDatabase::ModuleInfoData::ModuleInfoData(const ModuleInfoData& others) =
+    default;
+
+ModuleDatabase::ModuleInfoData::~ModuleInfoData() = default;
+
 // ModuleDatabase::ProcessInfoKey ----------------------------------------------
 
 ModuleDatabase::ProcessInfoKey::ProcessInfoKey(
diff --git a/chrome/browser/conflicts/module_database_win.h b/chrome/browser/conflicts/module_database_win.h
index 73725556..6fa87b8 100644
--- a/chrome/browser/conflicts/module_database_win.h
+++ b/chrome/browser/conflicts/module_database_win.h
@@ -22,6 +22,36 @@
 // be set as the process-wide singleton via SetInstance.
 class ModuleDatabase {
  public:
+  // Used as a unique identifier for a module in a ModuleSet.
+  using ModuleId = int;
+
+  // The type of certificate found for the module.
+  enum CertificateType {
+    // The module is not signed.
+    NO_CERTIFICATE,
+    // The module is signed and the certificate is in the module.
+    CERTIFICATE_IN_FILE,
+    // The module is signed and the certificate is in an external catalog.
+    CERTIFICATE_IN_CATALOG,
+  };
+
+  // Structures for maintaining information about modules.
+  struct ModuleInfoKey;
+  struct CertificateInfo;
+  struct ModuleInfoData;
+  using ModuleMap = std::map<ModuleInfoKey, ModuleInfoData>;
+  using ModuleInfo = ModuleMap::value_type;
+
+  // Used for maintaing a list of modules loaded in a process. Maps module IDs
+  // to load addresses.
+  using ModuleLoadAddresses = std::vector<std::pair<ModuleId, uintptr_t>>;
+
+  // Structures for maintaining information about running processes.
+  struct ProcessInfoKey;
+  struct ProcessInfoData;
+  using ProcessMap = std::map<ProcessInfoKey, ProcessInfoData>;
+  using ProcessInfo = ProcessMap::value_type;
+
   // A ModuleDatabase is by default bound to a provided sequenced task runner.
   // All calls must be made in the context of this task runner, unless
   // otherwise noted. For calls from other contexts this task runner is used to
@@ -79,25 +109,6 @@
   // been found.
   static constexpr size_t kInvalidIndex = ~0u;
 
-  // Used as a unique identifier for a module in a ModuleSet.
-  using ModuleId = int;
-
-  // Structures for maintaining information about modules.
-  struct ModuleInfoKey;
-  struct ModuleInfoData;
-  using ModuleMap = std::map<ModuleInfoKey, ModuleInfoData>;
-  using ModuleInfo = ModuleMap::value_type;
-
-  // Used for maintaing a list of modules loaded in a process. Maps module IDs
-  // to load addresses.
-  using ModuleLoadAddresses = std::vector<std::pair<ModuleId, uintptr_t>>;
-
-  // Structures for maintaining information about running processes.
-  struct ProcessInfoKey;
-  struct ProcessInfoData;
-  using ProcessMap = std::map<ProcessInfoKey, ProcessInfoData>;
-  using ProcessInfo = ProcessMap::value_type;
-
   // Converts a valid |process_type| to a bit for use in a bitmask of process
   // values. Exposed in the header for testing.
   static uint32_t ProcessTypeToBit(content::ProcessType process_type);
@@ -195,16 +206,59 @@
   ModuleId module_id;
 };
 
+// Information about the certificate of a file.
+struct ModuleDatabase::CertificateInfo {
+  CertificateInfo();
+
+  // The type of signature encountered.
+  CertificateType type;
+
+  // Path to the file containing the certificate. Empty if |type| is
+  // NO_CERTIFICATE.
+  base::FilePath path;
+
+  // The "Subject" name of the certificate. This is the signer (ie,
+  // "Google Inc." or "Microsoft Inc.").
+  base::string16 subject;
+};
+
 // This is the mutable portion of the module information, and is the storage
 // type in a std::map.
 struct ModuleDatabase::ModuleInfoData {
   ModuleInfoData();
+  ModuleInfoData(const ModuleInfoData& others);
+  ~ModuleInfoData();
 
   // Set of all process types in which this module has been seen (may not be
   // currently present in a process of that type). This is a conversion of
   // ProcessType enumeration to a bitfield. See "ProcessTypeToBit" and
   // "BitIndexToProcessType" for details.
   uint32_t process_types;
+
+  // The following pieces of information are determined via a detailed
+  // inspection of the module. This is relatively expensive and uses blocking
+  // IO, so is performed in a background task.
+
+  // The module path, not including the basename. This is cleaned and normalized
+  // so that common paths are converted to their environment variable mappings
+  // (ie, %systemroot%). This makes i18n localized paths easily comparable.
+  base::string16 location;
+
+  // The basename of the module.
+  base::string16 basename;
+
+  // The name of the product the module belongs to.
+  base::string16 product_name;
+
+  // The module file description.
+  base::string16 description;
+
+  // The module version. This is usually in the form a.b.c.d (where a, b, c and
+  // d are integers), but may also have fewer than 4 components.
+  base::string16 version;
+
+  // The certificate info for the module.
+  CertificateInfo certificate_info;
 };
 
 // Information about a running process. This ties modules in a ModuleSet to
diff --git a/chrome/browser/conflicts/module_info_util_win.cc b/chrome/browser/conflicts/module_info_util_win.cc
new file mode 100644
index 0000000..2c6c599
--- /dev/null
+++ b/chrome/browser/conflicts/module_info_util_win.cc
@@ -0,0 +1,231 @@
+// Copyright 2017 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/browser/conflicts/module_info_util_win.h"
+
+#include <tlhelp32.h>
+#include <wincrypt.h>
+#include <wintrust.h>
+
+// This must be after wincrypt and wintrust.
+#include <mscat.h>
+
+#include "base/scoped_generic.h"
+#include "base/win/scoped_handle.h"
+
+namespace {
+
+// Helper for scoped tracking an HCERTSTORE.
+struct ScopedHCERTSTORETraits {
+  static HCERTSTORE InvalidValue() { return nullptr; }
+  static void Free(HCERTSTORE store) { ::CertCloseStore(store, 0); }
+};
+using ScopedHCERTSTORE =
+    base::ScopedGeneric<HCERTSTORE, ScopedHCERTSTORETraits>;
+
+// Helper for scoped tracking an HCRYPTMSG.
+struct ScopedHCRYPTMSGTraits {
+  static HCRYPTMSG InvalidValue() { return nullptr; }
+  static void Free(HCRYPTMSG message) { ::CryptMsgClose(message); }
+};
+using ScopedHCRYPTMSG = base::ScopedGeneric<HCRYPTMSG, ScopedHCRYPTMSGTraits>;
+
+// Returns the "Subject" field from the digital signature in the provided
+// binary, if any is present. Returns an empty string on failure.
+base::string16 GetSubjectNameInFile(const base::FilePath& filename) {
+  ScopedHCERTSTORE store;
+  ScopedHCRYPTMSG message;
+
+  // Find the crypto message for this filename.
+  {
+    HCERTSTORE temp_store = nullptr;
+    HCRYPTMSG temp_message = nullptr;
+    bool result =
+        !!CryptQueryObject(CERT_QUERY_OBJECT_FILE, filename.value().c_str(),
+                           CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
+                           CERT_QUERY_FORMAT_FLAG_BINARY, 0, nullptr, nullptr,
+                           nullptr, &temp_store, &temp_message, nullptr);
+    store.reset(temp_store);
+    message.reset(temp_message);
+    if (!result)
+      return base::string16();
+  }
+
+  // Determine the size of the signer info data.
+  DWORD signer_info_size = 0;
+  bool result = !!CryptMsgGetParam(message.get(), CMSG_SIGNER_INFO_PARAM, 0,
+                                   nullptr, &signer_info_size);
+  if (!result)
+    return base::string16();
+
+  // Allocate enough space to hold the signer info.
+  std::unique_ptr<BYTE[]> signer_info_buffer(new BYTE[signer_info_size]);
+  CMSG_SIGNER_INFO* signer_info =
+      reinterpret_cast<CMSG_SIGNER_INFO*>(signer_info_buffer.get());
+
+  // Obtain the signer info.
+  result = !!CryptMsgGetParam(message.get(), CMSG_SIGNER_INFO_PARAM, 0,
+                              signer_info, &signer_info_size);
+  if (!result)
+    return base::string16();
+
+  // Search for the signer certificate.
+  CERT_INFO CertInfo = {0};
+  PCCERT_CONTEXT cert_context = nullptr;
+  CertInfo.Issuer = signer_info->Issuer;
+  CertInfo.SerialNumber = signer_info->SerialNumber;
+
+  cert_context = CertFindCertificateInStore(
+      store.get(), X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0,
+      CERT_FIND_SUBJECT_CERT, &CertInfo, nullptr);
+  if (!cert_context)
+    return base::string16();
+
+  // Determine the size of the Subject name.
+  DWORD subject_name_size = CertGetNameString(
+      cert_context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nullptr, nullptr, 0);
+  if (!subject_name_size)
+    return base::string16();
+
+  base::string16 subject_name;
+  subject_name.resize(subject_name_size);
+
+  // Get subject name.
+  if (!(CertGetNameString(cert_context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
+                          nullptr, const_cast<LPWSTR>(subject_name.c_str()),
+                          subject_name_size))) {
+    return base::string16();
+  }
+
+  return subject_name;
+}
+
+// Helper for scoped tracking a catalog admin context.
+struct CryptCATContextScopedTraits {
+  static PVOID InvalidValue() { return nullptr; }
+  static void Free(PVOID context) { CryptCATAdminReleaseContext(context, 0); }
+};
+using ScopedCryptCATContext =
+    base::ScopedGeneric<PVOID, CryptCATContextScopedTraits>;
+
+// Helper for scoped tracking of a catalog context. A catalog context is only
+// valid with an associated admin context, so this is effectively a std::pair.
+// A custom operator!= is required in order for a null |catalog_context| but
+// non-null |context| to compare equal to the InvalidValue exposed by the
+// traits class.
+class CryptCATCatalogContext {
+ public:
+  CryptCATCatalogContext(PVOID context, PVOID catalog_context)
+      : context_(context), catalog_context_(catalog_context) {}
+
+  bool operator!=(const CryptCATCatalogContext& rhs) const {
+    return catalog_context_ != rhs.catalog_context_;
+  }
+
+  PVOID context() const { return context_; }
+  PVOID catalog_context() const { return catalog_context_; }
+
+ private:
+  PVOID context_;
+  PVOID catalog_context_;
+};
+
+struct CryptCATCatalogContextScopedTraits {
+  static CryptCATCatalogContext InvalidValue() {
+    return CryptCATCatalogContext(nullptr, nullptr);
+  }
+  static void Free(const CryptCATCatalogContext& c) {
+    CryptCATAdminReleaseCatalogContext(c.context(), c.catalog_context(), 0);
+  }
+};
+using ScopedCryptCATCatalogContext =
+    base::ScopedGeneric<CryptCATCatalogContext,
+                        CryptCATCatalogContextScopedTraits>;
+
+// Extracts the subject name and catalog path if the provided file is present in
+// a catalog file.
+void GetCatalogCertificateInfo(const base::FilePath& filename,
+                               ModuleDatabase::CertificateInfo* cert_info) {
+  // Get a crypt context for signature verification.
+  ScopedCryptCATContext context;
+  {
+    PVOID raw_context = nullptr;
+    if (!CryptCATAdminAcquireContext(&raw_context, nullptr, 0))
+      return;
+    context.reset(raw_context);
+  }
+
+  // Open the file of interest.
+  base::win::ScopedHandle file_handle(
+      CreateFileW(filename.value().c_str(), GENERIC_READ,
+                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                  nullptr, OPEN_EXISTING, 0, nullptr));
+  if (!file_handle.IsValid())
+    return;
+
+  // Get the size we need for our hash.
+  DWORD hash_size = 0;
+  CryptCATAdminCalcHashFromFileHandle(file_handle.Get(), &hash_size, nullptr,
+                                      0);
+  if (hash_size == 0)
+    return;
+
+  // Calculate the hash. If this fails then bail.
+  std::vector<BYTE> buffer(hash_size);
+  if (!CryptCATAdminCalcHashFromFileHandle(file_handle.Get(), &hash_size,
+                                           buffer.data(), 0)) {
+    return;
+  }
+
+  // Get catalog for our context.
+  ScopedCryptCATCatalogContext catalog_context(CryptCATCatalogContext(
+      context.get(), CryptCATAdminEnumCatalogFromHash(
+                         context.get(), buffer.data(), hash_size, 0, nullptr)));
+  if (!catalog_context.is_valid())
+    return;
+
+  // Get the catalog info. This includes the path to the catalog itself, which
+  // contains the signature of interest.
+  CATALOG_INFO catalog_info = {};
+  catalog_info.cbStruct = sizeof(catalog_info);
+  if (!CryptCATCatalogInfoFromContext(catalog_context.get().catalog_context(),
+                                      &catalog_info, 0)) {
+    return;
+  }
+
+  // Attempt to get the "Subject" field from the signature of the catalog file
+  // itself.
+  base::FilePath catalog_path(catalog_info.wszCatalogFile);
+  base::string16 subject = GetSubjectNameInFile(catalog_path);
+
+  if (subject.empty())
+    return;
+
+  cert_info->type = ModuleDatabase::CERTIFICATE_IN_CATALOG;
+  cert_info->path = catalog_path;
+  cert_info->subject = subject;
+}
+
+}  // namespace
+
+// Extracts information about the certificate of the given file, if any is
+// found.
+void GetCertificateInfo(const base::FilePath& filename,
+                        ModuleDatabase::CertificateInfo* cert_info) {
+  DCHECK_EQ(ModuleDatabase::NO_CERTIFICATE, cert_info->type);
+  DCHECK(cert_info->path.empty());
+  DCHECK(cert_info->subject.empty());
+
+  GetCatalogCertificateInfo(filename, cert_info);
+  if (cert_info->type == ModuleDatabase::CERTIFICATE_IN_CATALOG)
+    return;
+
+  base::string16 subject = GetSubjectNameInFile(filename);
+  if (subject.empty())
+    return;
+
+  cert_info->type = ModuleDatabase::CERTIFICATE_IN_FILE;
+  cert_info->path = filename;
+  cert_info->subject = subject;
+}
diff --git a/chrome/browser/conflicts/module_info_util_win.h b/chrome/browser/conflicts/module_info_util_win.h
new file mode 100644
index 0000000..ba754f6
--- /dev/null
+++ b/chrome/browser/conflicts/module_info_util_win.h
@@ -0,0 +1,15 @@
+// Copyright 2017 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_BROWSER_CONFLICTS_MODULE_INFO_UTIL_WIN_H_
+#define CHROME_BROWSER_CONFLICTS_MODULE_INFO_UTIL_WIN_H_
+
+#include "chrome/browser/conflicts/module_database_win.h"
+
+// Extracts information about the certificate of the given |file|, populating
+// |cert_info|. It is expected that |cert_info| be freshly constructed.
+void GetCertificateInfo(const base::FilePath& file,
+                        ModuleDatabase::CertificateInfo* cert_info);
+
+#endif  // CHROME_BROWSER_CONFLICTS_MODULE_INFO_UTIL_WIN_H_
diff --git a/chrome/browser/conflicts/module_info_util_win_unittest.cc b/chrome/browser/conflicts/module_info_util_win_unittest.cc
new file mode 100644
index 0000000..f713bd1
--- /dev/null
+++ b/chrome/browser/conflicts/module_info_util_win_unittest.cc
@@ -0,0 +1,39 @@
+// Copyright 2017 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/browser/conflicts/module_info_util_win.h"
+
+#include <memory>
+
+#include "base/base_paths.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(ModuleInfoUtil, GetCertificateInfoUnsigned) {
+  base::FilePath path;
+  ASSERT_TRUE(base::PathService::Get(base::FILE_EXE, &path));
+  ModuleDatabase::CertificateInfo cert_info;
+  GetCertificateInfo(path, &cert_info);
+  EXPECT_EQ(ModuleDatabase::NO_CERTIFICATE, cert_info.type);
+  EXPECT_TRUE(cert_info.path.empty());
+  EXPECT_TRUE(cert_info.subject.empty());
+}
+
+TEST(ModuleInfoUtil, GetCertificateInfoSigned) {
+  std::unique_ptr<base::Environment> env = base::Environment::Create();
+  std::string sysroot;
+  ASSERT_TRUE(env->GetVar("SYSTEMROOT", &sysroot));
+
+  base::FilePath path =
+      base::FilePath::FromUTF8Unsafe(sysroot).Append(L"system32\\kernel32.dll");
+
+  ModuleDatabase::CertificateInfo cert_info;
+  GetCertificateInfo(path, &cert_info);
+  EXPECT_NE(ModuleDatabase::NO_CERTIFICATE, cert_info.type);
+  EXPECT_FALSE(cert_info.path.empty());
+  EXPECT_FALSE(cert_info.subject.empty());
+}
diff --git a/chrome/browser/win/enumerate_modules_model.cc b/chrome/browser/win/enumerate_modules_model.cc
index 98aaffa..641644b3 100644
--- a/chrome/browser/win/enumerate_modules_model.cc
+++ b/chrome/browser/win/enumerate_modules_model.cc
@@ -97,249 +97,8 @@
   return false;
 }
 
-// Helper for scoped tracking an HCERTSTORE.
-struct ScopedHCERTSTORETraits {
-  static HCERTSTORE InvalidValue() { return nullptr; }
-  static void Free(HCERTSTORE store) {
-    ::CertCloseStore(store, 0);
-  }
-};
-using ScopedHCERTSTORE =
-    base::ScopedGeneric<HCERTSTORE, ScopedHCERTSTORETraits>;
-
-// Helper for scoped tracking an HCRYPTMSG.
-struct ScopedHCRYPTMSGTraits {
-  static HCRYPTMSG InvalidValue() { return nullptr; }
-  static void Free(HCRYPTMSG message) {
-    ::CryptMsgClose(message);
-  }
-};
-using ScopedHCRYPTMSG =
-    base::ScopedGeneric<HCRYPTMSG, ScopedHCRYPTMSGTraits>;
-
-// Returns the "Subject" field from the digital signature in the provided
-// binary, if any is present. Returns an empty string on failure.
-base::string16 GetSubjectNameInFile(const base::FilePath& filename) {
-  ScopedHCERTSTORE store;
-  ScopedHCRYPTMSG message;
-
-  // Find the crypto message for this filename.
-  {
-    HCERTSTORE temp_store = nullptr;
-    HCRYPTMSG temp_message = nullptr;
-    bool result = !!CryptQueryObject(CERT_QUERY_OBJECT_FILE,
-      filename.value().c_str(),
-      CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
-      CERT_QUERY_FORMAT_FLAG_BINARY,
-      0,
-      nullptr,
-      nullptr,
-      nullptr,
-      &temp_store,
-      &temp_message,
-      nullptr);
-    store.reset(temp_store);
-    message.reset(temp_message);
-    if (!result)
-      return base::string16();
-  }
-
-  // Determine the size of the signer info data.
-  DWORD signer_info_size = 0;
-  bool result = !!CryptMsgGetParam(message.get(),
-    CMSG_SIGNER_INFO_PARAM,
-    0,
-    nullptr,
-    &signer_info_size);
-  if (!result)
-    return base::string16();
-
-  // Allocate enough space to hold the signer info.
-  std::unique_ptr<BYTE[]> signer_info_buffer(new BYTE[signer_info_size]);
-  CMSG_SIGNER_INFO* signer_info =
-      reinterpret_cast<CMSG_SIGNER_INFO*>(signer_info_buffer.get());
-
-  // Obtain the signer info.
-  result = !!CryptMsgGetParam(message.get(),
-    CMSG_SIGNER_INFO_PARAM,
-    0,
-    signer_info,
-    &signer_info_size);
-  if (!result)
-    return base::string16();
-
-  // Search for the signer certificate.
-  CERT_INFO CertInfo = {0};
-  PCCERT_CONTEXT cert_context = nullptr;
-  CertInfo.Issuer = signer_info->Issuer;
-  CertInfo.SerialNumber = signer_info->SerialNumber;
-
-  cert_context = CertFindCertificateInStore(
-    store.get(),
-    X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
-    0,
-    CERT_FIND_SUBJECT_CERT,
-    &CertInfo,
-    nullptr);
-  if (!cert_context)
-    return base::string16();
-
-  // Determine the size of the Subject name.
-  DWORD subject_name_size = CertGetNameString(
-    cert_context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nullptr, nullptr, 0);
-  if (!subject_name_size)
-    return base::string16();
-
-  base::string16 subject_name;
-  subject_name.resize(subject_name_size);
-
-  // Get subject name.
-  if (!(CertGetNameString(cert_context,
-    CERT_NAME_SIMPLE_DISPLAY_TYPE,
-    0,
-    nullptr,
-    const_cast<LPWSTR>(subject_name.c_str()),
-    subject_name_size))) {
-    return base::string16();
-  }
-
-  return subject_name;
-}
-
-// Helper for scoped tracking a catalog admin context.
-struct CryptCATContextScopedTraits {
-  static PVOID InvalidValue() { return nullptr; }
-  static void Free(PVOID context) {
-    CryptCATAdminReleaseContext(context, 0);
-  }
-};
-using ScopedCryptCATContext =
-    base::ScopedGeneric<PVOID, CryptCATContextScopedTraits>;
-
-// Helper for scoped tracking of a catalog context. A catalog context is only
-// valid with an associated admin context, so this is effectively a std::pair.
-// A custom operator!= is required in order for a null |catalog_context| but
-// non-null |context| to compare equal to the InvalidValue exposed by the
-// traits class.
-class CryptCATCatalogContext {
- public:
-  CryptCATCatalogContext(PVOID context, PVOID catalog_context)
-      : context_(context), catalog_context_(catalog_context) {}
-
-  bool operator!=(const CryptCATCatalogContext& rhs) const {
-    return catalog_context_ != rhs.catalog_context_;
-  }
-
-  PVOID context() const { return context_; }
-  PVOID catalog_context() const { return catalog_context_; }
-
- private:
-  PVOID context_;
-  PVOID catalog_context_;
-};
-
-struct CryptCATCatalogContextScopedTraits {
-  static CryptCATCatalogContext InvalidValue() {
-    return CryptCATCatalogContext(nullptr, nullptr);
-  }
-  static void Free(const CryptCATCatalogContext& c) {
-    CryptCATAdminReleaseCatalogContext(
-        c.context(), c.catalog_context(), 0);
-  }
-};
-using ScopedCryptCATCatalogContext = base::ScopedGeneric<
-    CryptCATCatalogContext, CryptCATCatalogContextScopedTraits>;
-
-// Extracts the subject name and catalog path if the provided file is present in
-// a catalog file.
-void GetCatalogCertificateInfo(const base::FilePath& filename,
-                               ModuleEnumerator::CertificateInfo* cert_info) {
-  // Get a crypt context for signature verification.
-  ScopedCryptCATContext context;
-  {
-    PVOID raw_context = nullptr;
-    if (!CryptCATAdminAcquireContext(&raw_context, nullptr, 0))
-      return;
-    context.reset(raw_context);
-  }
-
-  // Open the file of interest.
-  base::win::ScopedHandle file_handle(CreateFileW(
-    filename.value().c_str(), GENERIC_READ,
-    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-    nullptr, OPEN_EXISTING, 0, nullptr));
-  if (!file_handle.IsValid())
-    return;
-
-  // Get the size we need for our hash.
-  DWORD hash_size = 0;
-  CryptCATAdminCalcHashFromFileHandle(
-      file_handle.Get(), &hash_size, nullptr, 0);
-  if (hash_size == 0)
-    return;
-
-  // Calculate the hash. If this fails then bail.
-  std::vector<BYTE> buffer(hash_size);
-  if (!CryptCATAdminCalcHashFromFileHandle(file_handle.Get(), &hash_size,
-          buffer.data(), 0)) {
-    return;
-  }
-
-  // Get catalog for our context.
-  ScopedCryptCATCatalogContext catalog_context(CryptCATCatalogContext(
-      context.get(),
-      CryptCATAdminEnumCatalogFromHash(context.get(), buffer.data(), hash_size,
-                                       0, nullptr)));
-  if (!catalog_context.is_valid())
-    return;
-
-  // Get the catalog info. This includes the path to the catalog itself, which
-  // contains the signature of interest.
-  CATALOG_INFO catalog_info = {};
-  catalog_info.cbStruct = sizeof(catalog_info);
-  if (!CryptCATCatalogInfoFromContext(
-      catalog_context.get().catalog_context(), &catalog_info, 0)) {
-    return;
-  }
-
-  // Attempt to get the "Subject" field from the signature of the catalog file
-  // itself.
-  base::FilePath catalog_path(catalog_info.wszCatalogFile);
-  base::string16 subject = GetSubjectNameInFile(catalog_path);
-
-  if (subject.empty())
-    return;
-
-  cert_info->type = ModuleEnumerator::CERTIFICATE_IN_CATALOG;
-  cert_info->path = catalog_path;
-  cert_info->subject = subject;
-}
-
-// Extracts information about the certificate of the given file, if any is
-// found.
-void GetCertificateInfo(const base::FilePath& filename,
-                        ModuleEnumerator::CertificateInfo* cert_info) {
-  DCHECK_EQ(ModuleEnumerator::NO_CERTIFICATE, cert_info->type);
-  DCHECK(cert_info->path.empty());
-  DCHECK(cert_info->subject.empty());
-
-  GetCatalogCertificateInfo(filename, cert_info);
-  if (cert_info->type == ModuleEnumerator::CERTIFICATE_IN_CATALOG)
-    return;
-
-  base::string16 subject = GetSubjectNameInFile(filename);
-  if (subject.empty())
-    return;
-
-  cert_info->type = ModuleEnumerator::CERTIFICATE_IN_FILE;
-  cert_info->path = filename;
-  cert_info->subject = subject;
-}
-
 }  // namespace
 
-ModuleEnumerator::CertificateInfo::CertificateInfo() : type(NO_CERTIFICATE) {}
-
 ModuleEnumerator::Module::Module() {
 }
 
@@ -709,10 +468,10 @@
   size_t third_party_loaded = 0;
   size_t third_party_not_loaded = 0;
   for (const auto& module : *enumerated_modules_) {
-    if (module.cert_info.type != ModuleEnumerator::NO_CERTIFICATE) {
+    if (module.cert_info.type != ModuleDatabase::NO_CERTIFICATE) {
       ++signed_modules;
 
-      if (module.cert_info.type == ModuleEnumerator::CERTIFICATE_IN_CATALOG)
+      if (module.cert_info.type == ModuleDatabase::CERTIFICATE_IN_CATALOG)
         ++catalog_modules;
 
       // The first time this certificate is encountered it will be inserted
diff --git a/chrome/browser/win/enumerate_modules_model.h b/chrome/browser/win/enumerate_modules_model.h
index 6a1265b8f..76ce022 100644
--- a/chrome/browser/win/enumerate_modules_model.h
+++ b/chrome/browser/win/enumerate_modules_model.h
@@ -15,6 +15,7 @@
 #include "base/observer_list.h"
 #include "base/strings/string16.h"
 #include "base/timer/timer.h"
+#include "chrome/browser/conflicts/module_info_util_win.h"
 #include "content/public/browser/browser_thread.h"
 #include "url/gurl.h"
 
@@ -73,25 +74,6 @@
     XP           = 1 << 0,
   };
 
-  // The type of certificate found for the module.
-  enum CertificateType {
-    NO_CERTIFICATE,
-    CERTIFICATE_IN_FILE,
-    CERTIFICATE_IN_CATALOG,
-  };
-
-  // Information about the certificate of a file.
-  struct CertificateInfo {
-    CertificateInfo();
-
-    // The type of signature encountered.
-    CertificateType type;
-    // Path to the file containing the certificate. Empty if NO_CERTIFICATE.
-    base::FilePath path;
-    // The "Subject" name of the certificate.
-    base::string16 subject;
-  };
-
   // The structure we populate when enumerating modules.
   struct Module {
     Module();
@@ -126,7 +108,7 @@
     // The duplicate count within each category of modules.
     int duplicate_count;
     // The certificate info for the module.
-    CertificateInfo cert_info;
+    ModuleDatabase::CertificateInfo cert_info;
   };
 
   // A vector typedef of all modules enumerated.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 97f0d6b..986afe79 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3125,6 +3125,7 @@
     "../browser/component_updater/sw_reporter_installer_win_unittest.cc",
     "../browser/conflicts/module_database_win_unittest.cc",
     "../browser/conflicts/module_event_sink_impl_win_unittest.cc",
+    "../browser/conflicts/module_info_util_win_unittest.cc",
     "../browser/content_settings/content_settings_default_provider_unittest.cc",
     "../browser/content_settings/content_settings_mock_observer.cc",
     "../browser/content_settings/content_settings_mock_observer.h",