Commit 57b6b5db authored by Findit's avatar Findit

Revert "[chrome_elf, third-party block support] full hook testing."

This reverts commit 2c24de69.

Reason for revert:

Findit (https://goo.gl/kROfz5) identified CL at revision 562467 as the
culprit for failures in the build cycles as shown on:
https://findit-for-me.appspot.com/waterfall/culprit?key=ag9zfmZpbmRpdC1mb3ItbWVyRAsSDVdmU3VzcGVjdGVkQ0wiMWNocm9taXVtLzJjMjRkZTY5NTMyY2IyNmNjMTk5YmIwN2Y5ZWJmYTY3MzBiYzI2ZTIM

Sample Failed Build: https://ci.chromium.org/buildbot/chromium.win/Win7%20Tests%20%28dbg%29%281%29/69346

Sample Failed Step: chrome_elf_unittests

Original change's description:
> [chrome_elf, third-party block support] full hook testing.
> 
> Adding some full tests, including some corner cases.  These tests
> initialize the whole third_party_dlls project, to exercise the new hook.
> 
> Non-test adjustments:
> - pe_image_safe: |directory| == 0 is valid!  (export dir)
> - hook:  Exposed a GetDataFromImageForTesting() function.
>   Also a few fixes in the hook shim:
>   * Use the PEImageSafe constructor that takes an HMODULE.
>   * Use the new GetFingerprintString() api.
>   * Don't hash an empty image or section name.
>   * Pass a name hash instead of non-hashed into LogLoadAttempt
>     if a DLL was allowed.
> - packed_list_file:
>   * GetFingerprintHash() removed from this project.
>   * Also, gracefully handle a blacklist file that exists, but is empty.
> - packed_list_format:
>   * Added "new" GetFingerprintString() API here.
>     It centralizes the format string without doing the hash.
> 
> Test: chrome_elf_unittests.exe, ThirdPartyTest.*
> Bug: 769590
> Change-Id: I592f76b884312bb267eb6747a478e271fc96e689
> Reviewed-on: https://chromium-review.googlesource.com/1069694
> Commit-Queue: Penny MacNeil <pennymac@chromium.org>
> Reviewed-by: Tom Sepez <tsepez@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#562467}

Change-Id: I46cf1feddbc80a60ca92b44152d2f7ad0ef865e7
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 769590
Reviewed-on: https://chromium-review.googlesource.com/1077029
Cr-Commit-Position: refs/heads/master@{#562542}
parent 3b81b85f
...@@ -290,7 +290,6 @@ test("chrome_elf_unittests") { ...@@ -290,7 +290,6 @@ test("chrome_elf_unittests") {
"third_party_dlls/imes_unittest.cc", "third_party_dlls/imes_unittest.cc",
"third_party_dlls/logs_unittest.cc", "third_party_dlls/logs_unittest.cc",
"third_party_dlls/main_unittest.cc", "third_party_dlls/main_unittest.cc",
"third_party_dlls/main_unittest_exe.h",
"third_party_dlls/packed_list_file_unittest.cc", "third_party_dlls/packed_list_file_unittest.cc",
] ]
include_dirs = [ "$target_gen_dir" ] include_dirs = [ "$target_gen_dir" ]
...@@ -321,8 +320,6 @@ test("chrome_elf_unittests") { ...@@ -321,8 +320,6 @@ test("chrome_elf_unittests") {
":blacklist_test_dll_2", ":blacklist_test_dll_2",
":blacklist_test_dll_3", ":blacklist_test_dll_3",
":chrome_elf", ":chrome_elf",
":main_unittest_dll_1",
":main_unittest_dll_2",
":third_party_dlls_test_exe", ":third_party_dlls_test_exe",
] ]
} }
...@@ -365,36 +362,13 @@ test("chrome_elf_import_unittests") { ...@@ -365,36 +362,13 @@ test("chrome_elf_import_unittests") {
] ]
} }
shared_library("main_unittest_dll_1") {
testonly = true
sources = [
"third_party_dlls/main_unittest_dll_1.cc",
]
deps = [
"//build/config:exe_and_shlib_deps",
]
}
shared_library("main_unittest_dll_2") {
testonly = true
sources = [
"third_party_dlls/main_unittest_dll_2.cc",
"third_party_dlls/main_unittest_dll_2.def",
]
deps = [
"//build/config:exe_and_shlib_deps",
]
}
executable("third_party_dlls_test_exe") { executable("third_party_dlls_test_exe") {
testonly = true testonly = true
sources = [ sources = [
"third_party_dlls/main_unittest_exe.cc", "third_party_dlls/main_unittest_exe.cc",
"third_party_dlls/main_unittest_exe.h",
] ]
deps = [ deps = [
":third_party_dlls", ":third_party_dlls",
"//base",
"//build/config:exe_and_shlib_deps", "//build/config:exe_and_shlib_deps",
"//build/win:default_exe_manifest", "//build/win:default_exe_manifest",
"//chrome/install_static:install_static_util", "//chrome/install_static:install_static_util",
......
...@@ -128,7 +128,7 @@ void* PEImageSafe::RVAToAddr(DWORD rva) { ...@@ -128,7 +128,7 @@ void* PEImageSafe::RVAToAddr(DWORD rva) {
void* PEImageSafe::GetImageDirectoryEntryAddr(int directory, void* PEImageSafe::GetImageDirectoryEntryAddr(int directory,
DWORD* directory_size) { DWORD* directory_size) {
assert(directory >= 0 && directory < IMAGE_NUMBEROF_DIRECTORY_ENTRIES && assert(directory > 0 && directory < IMAGE_NUMBEROF_DIRECTORY_ENTRIES &&
ldr_image_mapping_); ldr_image_mapping_);
// GetOptionalHeader() validates the optional header. // GetOptionalHeader() validates the optional header.
......
...@@ -45,8 +45,6 @@ class PEImageSafe { ...@@ -45,8 +45,6 @@ class PEImageSafe {
// Some functions can only be used on images that have been memory mapped by // Some functions can only be used on images that have been memory mapped by
// the NT Loader (e.g. LoadLibrary). This constructor must be used to enable // the NT Loader (e.g. LoadLibrary). This constructor must be used to enable
// that functionality. // that functionality.
// - Note: Full load or LOAD_LIBRARY_AS_IMAGE_RESOURCE required.
// LOAD_LIBRARY_AS_DATAFILE* is not sufficient for some APIs.
PEImageSafe(HMODULE buffer, DWORD buffer_size) PEImageSafe(HMODULE buffer, DWORD buffer_size)
: image_(buffer), image_size_(buffer_size) { : image_(buffer), image_size_(buffer_size) {
ldr_image_mapping_ = !LDR_IS_DATAFILE(buffer); ldr_image_mapping_ = !LDR_IS_DATAFILE(buffer);
......
...@@ -15,7 +15,7 @@ include_rules = [ ...@@ -15,7 +15,7 @@ include_rules = [
"+chrome_elf/third_party_dlls", "+chrome_elf/third_party_dlls",
] ]
specific_include_rules = { specific_include_rules = {
".*_unittest.*": [ ".*_unittest\.cc": [
"+base", "+base",
] ]
} }
\ No newline at end of file
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include "chrome_elf/third_party_dlls/logs.h" #include "chrome_elf/third_party_dlls/logs.h"
#include "chrome_elf/third_party_dlls/main.h" #include "chrome_elf/third_party_dlls/main.h"
#include "chrome_elf/third_party_dlls/packed_list_file.h" #include "chrome_elf/third_party_dlls/packed_list_file.h"
#include "chrome_elf/third_party_dlls/packed_list_format.h"
#include "sandbox/win/src/interception_internal.h" #include "sandbox/win/src/interception_internal.h"
#include "sandbox/win/src/internal_types.h" #include "sandbox/win/src/internal_types.h"
#include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/nt_internals.h"
...@@ -166,8 +165,7 @@ bool GetDataFromImage(PVOID buffer, ...@@ -166,8 +165,7 @@ bool GetDataFromImage(PVOID buffer,
section_path->clear(); section_path->clear();
section_basename->clear(); section_basename->clear();
pe_image_safe::PEImageSafe image(reinterpret_cast<HMODULE>(buffer), pe_image_safe::PEImageSafe image(buffer, buffer_size);
buffer_size);
PIMAGE_FILE_HEADER file_header = image.GetFileHeader(); PIMAGE_FILE_HEADER file_header = image.GetFileHeader();
if (!file_header || if (!file_header ||
image.GetImageBitness() == pe_image_safe::ImageBitness::kUnknown) { image.GetImageBitness() == pe_image_safe::ImageBitness::kUnknown) {
...@@ -206,7 +204,6 @@ bool GetDataFromImage(PVOID buffer, ...@@ -206,7 +204,6 @@ bool GetDataFromImage(PVOID buffer,
// For now, consider it a success if at least one source results in a name. // For now, consider it a success if at least one source results in a name.
// Allow for the rare case of one or the other not being there. // Allow for the rare case of one or the other not being there.
// (E.g.: a module could have no export directory.)
if (image_name->empty() && temp_section_path.empty()) if (image_name->empty() && temp_section_path.empty())
return false; return false;
...@@ -281,16 +278,13 @@ NTSTATUS NewNtMapViewOfSectionImpl( ...@@ -281,16 +278,13 @@ NTSTATUS NewNtMapViewOfSectionImpl(
return ret; return ret;
} }
// Note that one of either image_name or section_basename can be empty. // Note that one of either image_name or section_basename can be empty, and
std::string image_name_hash; // the resulting hash string would be empty as well.
if (!image_name.empty()) std::string image_name_hash = elf_sha1::SHA1HashString(image_name);
image_name_hash = elf_sha1::SHA1HashString(image_name); std::string section_basename_hash =
std::string section_basename_hash; elf_sha1::SHA1HashString(section_basename);
if (!section_basename.empty())
section_basename_hash = elf_sha1::SHA1HashString(section_basename);
std::string fingerprint_hash = std::string fingerprint_hash =
GetFingerprintString(image_size, time_date_stamp); GetFingerprintHash(image_size, time_date_stamp);
fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash);
// Check sources for blacklist decision. // Check sources for blacklist decision.
bool block = false; bool block = false;
...@@ -321,8 +315,7 @@ NTSTATUS NewNtMapViewOfSectionImpl( ...@@ -321,8 +315,7 @@ NTSTATUS NewNtMapViewOfSectionImpl(
// No block. // No block.
// Ensure a non-null image name for the log. Prefer the section basename // Ensure a non-null image name for the log. Prefer the section basename
// (to match the path). // (to match the path).
name_matched = name_matched = section_basename.empty() ? &image_name : &section_basename;
section_basename.empty() ? &image_name_hash : &section_basename_hash;
} }
// IME is an explicit whitelist. // IME is an explicit whitelist.
// TODO(pennymac): create an explicit allow LogType? // TODO(pennymac): create an explicit allow LogType?
...@@ -469,18 +462,4 @@ HookStatus ApplyHook() { ...@@ -469,18 +462,4 @@ HookStatus ApplyHook() {
return HookStatus::kSuccess; return HookStatus::kSuccess;
} }
bool GetDataFromImageForTesting(PVOID buffer,
DWORD buffer_size,
DWORD* time_date_stamp,
DWORD* image_size,
std::string* image_name,
std::string* section_path,
std::string* section_basename) {
if (!g_nt_query_virtual_memory_func)
InitImports();
return GetDataFromImage(buffer, buffer_size, time_date_stamp, image_size,
image_name, section_path, section_basename);
}
} // namespace third_party_dlls } // namespace third_party_dlls
...@@ -5,10 +5,6 @@ ...@@ -5,10 +5,6 @@
#ifndef CHROME_ELF_THIRD_PARTY_DLLS_HOOK_H_ #ifndef CHROME_ELF_THIRD_PARTY_DLLS_HOOK_H_
#define CHROME_ELF_THIRD_PARTY_DLLS_HOOK_H_ #define CHROME_ELF_THIRD_PARTY_DLLS_HOOK_H_
#include <windows.h>
#include <string>
namespace third_party_dlls { namespace third_party_dlls {
// "static_cast<int>(HookStatus::value)" to access underlying value. // "static_cast<int>(HookStatus::value)" to access underlying value.
...@@ -25,15 +21,6 @@ enum class HookStatus { ...@@ -25,15 +21,6 @@ enum class HookStatus {
// - Ensure the rest of third_party_dlls is initialized before hooking. // - Ensure the rest of third_party_dlls is initialized before hooking.
HookStatus ApplyHook(); HookStatus ApplyHook();
// Testing-only access to private GetDataFromImage() function.
bool GetDataFromImageForTesting(PVOID buffer,
DWORD buffer_size,
DWORD* time_date_stamp,
DWORD* image_size,
std::string* image_name,
std::string* section_path,
std::string* section_basename);
} // namespace third_party_dlls } // namespace third_party_dlls
#endif // CHROME_ELF_THIRD_PARTY_DLLS_HOOK_H_ #endif // CHROME_ELF_THIRD_PARTY_DLLS_HOOK_H_
...@@ -7,448 +7,45 @@ ...@@ -7,448 +7,45 @@
#include <windows.h> #include <windows.h>
#include "base/command_line.h" #include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/process/launch.h" #include "base/process/launch.h"
#include "base/scoped_native_library.h"
#include "base/sha1.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/test_timeouts.h" #include "base/test/test_timeouts.h"
#include "chrome_elf/sha1/sha1.h"
#include "chrome_elf/third_party_dlls/hook.h"
#include "chrome_elf/third_party_dlls/main_unittest_exe.h"
#include "chrome_elf/third_party_dlls/packed_list_file.h"
#include "chrome_elf/third_party_dlls/packed_list_format.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace third_party_dlls { namespace third_party_dlls {
namespace { namespace {
constexpr wchar_t kTestExeFilename[] = L"third_party_dlls_test_exe.exe";
constexpr wchar_t kTestBlFileName[] = L"blfile";
constexpr wchar_t kTestDllName1[] = L"main_unittest_dll_1.dll";
constexpr wchar_t kTestDllName1MixedCase[] = L"MaiN_uniTtest_dLL_1.Dll";
constexpr wchar_t kTestDllName2[] = L"main_unittest_dll_2.dll";
constexpr wchar_t kChineseUnicode[] = {0x68D5, 0x8272, 0x72D0, 0x72F8, 0x002E,
0x0064, 0x006C, 0x006C, 0x0000};
constexpr wchar_t kOldBlacklistDllName[] = L"libapi2hook.dll";
struct TestModuleData {
std::string image_name;
std::string section_path;
std::string section_basename;
DWORD timedatestamp;
DWORD imagesize;
};
// NOTE: TestTimeouts::action_max_timeout() is not long enough here. // NOTE: TestTimeouts::action_max_timeout() is not long enough here.
base::TimeDelta g_timeout = ::IsDebuggerPresent() base::TimeDelta g_timeout = ::IsDebuggerPresent()
? base::TimeDelta::FromMilliseconds(INFINITE) ? base::TimeDelta::FromMilliseconds(INFINITE)
: base::TimeDelta::FromMilliseconds(5000); : base::TimeDelta::FromMilliseconds(5000);
// Centralize child test process control.
void LaunchChildAndWait(const base::CommandLine& command_line, int* exit_code) {
base::Process proc =
base::LaunchProcess(command_line, base::LaunchOptionsForTest());
ASSERT_TRUE(proc.IsValid());
*exit_code = 0;
if (!proc.WaitForExitWithTimeout(g_timeout, exit_code)) {
// Timeout while waiting. Try to cleanup.
proc.Terminate(1, false);
ADD_FAILURE();
}
return;
}
// Given the name and path of a test DLL, mine the data of interest out of it
// and return it via |test_module|.
// - Note: the DLL must be loaded into memory by NTLoader to mine all of the
// desired data (not just read from disk).
bool GetTestModuleData(const std::wstring& file_name,
const std::wstring& file_path,
TestModuleData* test_module) {
// Open the test file on disk.
base::FilePath path(file_path);
path = path.Append(file_name);
// Map the target DLL into memory just long enough to mine data out of it.
base::ScopedNativeLibrary test_dll(path);
if (!test_dll.is_valid())
return false;
// Get the DLL file size.
base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid())
return false;
// Once mapped into memory, the image size will be larger than the file on
// disk. This is a good enough estimate that will include the export section
// though.
DWORD file_size = static_cast<DWORD>(file.GetLength());
return GetDataFromImageForTesting(
test_dll.get(), file_size, &test_module->timedatestamp,
&test_module->imagesize, &test_module->image_name,
&test_module->section_path, &test_module->section_basename);
}
// Turn given data into a PackedListModule structure.
// - |image_name| should be utf-8 at this point.
PackedListModule GeneratePackedListModule(const std::string& image_name,
DWORD timedatestamp,
DWORD imagesize) {
// Internally, an empty string should not be passed in here.
assert(!image_name.empty());
// SHA1 hash the two strings, and copy them into the new struct.
std::string code_id = GetFingerprintString(imagesize, timedatestamp);
code_id = elf_sha1::SHA1HashString(code_id);
std::string name_hash = elf_sha1::SHA1HashString(image_name);
PackedListModule packed_module;
::memcpy(packed_module.code_id_hash, code_id.data(), elf_sha1::kSHA1Length);
::memcpy(packed_module.basename_hash, name_hash.data(),
elf_sha1::kSHA1Length);
return packed_module;
}
inline std::wstring MakePath(const std::wstring& path,
const std::wstring& name) {
std::wstring full_path(path);
full_path.push_back(L'\\');
full_path.append(name);
return full_path;
}
inline bool MakeFileCopy(const std::wstring& old_path,
const std::wstring& old_name,
const std::wstring& new_path,
const std::wstring& new_name) {
base::FilePath source(MakePath(old_path, old_name));
base::FilePath destination(MakePath(new_path, new_name));
return base::CopyFileW(source, destination);
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// ThirdPartyTest class // Third-party tests
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class ThirdPartyTest : public testing::Test {
protected:
ThirdPartyTest() = default;
void SetUp() override {
// Setup temp test dir.
ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
// Store full path to test file.
base::FilePath path = scoped_temp_dir_.GetPath();
path = path.Append(kTestBlFileName);
bl_test_file_path_ = std::move(path.value());
// Also store a copy of current exe directory for efficiency.
base::FilePath exe;
ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &exe));
exe_dir_ = std::move(exe.value());
// Create the blacklist file empty.
base::File file(base::FilePath(bl_test_file_path_),
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
base::File::FLAG_SHARE_DELETE |
base::File::FLAG_DELETE_ON_CLOSE);
ASSERT_TRUE(file.IsValid());
// Leave file handle open for DELETE_ON_CLOSE.
bl_file_ = std::move(file);
}
void TearDown() override {}
// Overwrite the content of the blacklist file.
bool WriteModulesToBlacklist(const std::vector<PackedListModule>& list) {
bl_file_.SetLength(0);
// Write content {metadata}{array_of_modules}.
PackedListMetadata meta = {kInitialVersion,
static_cast<uint32_t>(list.size())};
if (bl_file_.Write(0, reinterpret_cast<const char*>(&meta), sizeof(meta)) !=
static_cast<int>(sizeof(meta))) {
return false;
}
int size = static_cast<int>(list.size() * sizeof(PackedListModule));
if (bl_file_.Write(sizeof(PackedListMetadata),
reinterpret_cast<const char*>(list.data()),
size) != size) {
return false;
}
return true;
}
const base::string16& GetBlTestFilePath() { return bl_test_file_path_; }
const base::string16& GetExeDir() { return exe_dir_; }
const std::wstring& GetScopedTempDirValue() {
return scoped_temp_dir_.GetPath().value();
}
private:
base::ScopedTempDir scoped_temp_dir_;
base::File bl_file_;
base::string16 bl_test_file_path_;
base::string16 exe_dir_;
DISALLOW_COPY_AND_ASSIGN(ThirdPartyTest);
};
//------------------------------------------------------------------------------
// Third-party tests
//
// These tests spawn a child test process to keep the hooking contained to a // These tests spawn a child test process to keep the hooking contained to a
// separate process. This prevents test clashes in certain testing // separate process. This prevents test clashes in certain testing
// configurations. // configurations.
//------------------------------------------------------------------------------ TEST(ThirdParty, Main) {
constexpr wchar_t exe_filename[] = L"third_party_dlls_test_exe.exe";
// Note: The test module used in this unittest has no export table.
TEST_F(ThirdPartyTest, Base) {
// 1. Spawn the test process with NO blacklist. Expect successful
// initialization.
base::CommandLine cmd_line1 = base::CommandLine::FromString(kTestExeFilename);
cmd_line1.AppendArgNative(GetBlTestFilePath());
cmd_line1.AppendArgNative(
base::IntToString16(main_unittest_exe::kTestOnlyInitialization));
int exit_code = 0; // 1. Spawn the test process with NO blacklist. Expect successful DLL load.
LaunchChildAndWait(cmd_line1, &exit_code); const wchar_t* sys_dll_test = L"1";
ASSERT_EQ(main_unittest_exe::kDllLoadSuccess, exit_code);
//----------------------------------------------------------------------------
// 2. Spawn the test process with NO blacklist. Expect successful DLL load.
base::CommandLine cmd_line2 = base::CommandLine::FromString(kTestExeFilename);
cmd_line2.AppendArgNative(GetBlTestFilePath());
cmd_line2.AppendArgNative(
base::IntToString16(main_unittest_exe::kTestSingleDllLoad));
cmd_line2.AppendArgNative(MakePath(GetExeDir(), kTestDllName1));
LaunchChildAndWait(cmd_line2, &exit_code);
ASSERT_EQ(main_unittest_exe::kDllLoadSuccess, exit_code);
//----------------------------------------------------------------------------
// 3. Spawn the test process with blacklist. Expect failed DLL load.
TestModuleData module_data = {};
ASSERT_TRUE(GetTestModuleData(kTestDllName1, GetExeDir(), &module_data));
// Note: image_name will be empty, as there is no export table in this test
// module.
EXPECT_TRUE(module_data.image_name.empty());
std::vector<PackedListModule> vector(1);
vector.emplace_back(GeneratePackedListModule(module_data.section_basename,
module_data.timedatestamp,
module_data.imagesize));
ASSERT_TRUE(WriteModulesToBlacklist(vector));
base::CommandLine cmd_line3 = base::CommandLine::FromString(kTestExeFilename);
cmd_line3.AppendArgNative(GetBlTestFilePath());
cmd_line3.AppendArgNative(
base::IntToString16(main_unittest_exe::kTestSingleDllLoad));
cmd_line3.AppendArgNative(MakePath(GetExeDir(), kTestDllName1));
LaunchChildAndWait(cmd_line3, &exit_code);
ASSERT_EQ(main_unittest_exe::kDllLoadFailed, exit_code);
//----------------------------------------------------------------------------
// 4. Spawn the test process with blacklist. Expect failed DLL load.
// ** Rename the module with some upper-case characters to test that
// the hook matching handles case properly.
ASSERT_TRUE(MakeFileCopy(GetExeDir(), kTestDllName1, GetScopedTempDirValue(),
kTestDllName1MixedCase));
// Note: the blacklist is already set from the previous test.
// Note: using the module with no export table for this test, to ensure that
// the section name (the rename) is used in the comparison.
base::CommandLine cmd_line4 = base::CommandLine::FromString(kTestExeFilename);
cmd_line4.AppendArgNative(GetBlTestFilePath());
cmd_line4.AppendArgNative(
base::IntToString16(main_unittest_exe::kTestSingleDllLoad));
cmd_line4.AppendArgNative(
MakePath(GetScopedTempDirValue(), kTestDllName1MixedCase));
LaunchChildAndWait(cmd_line4, &exit_code);
ASSERT_EQ(main_unittest_exe::kDllLoadFailed, exit_code);
}
// Note: The test module used in this unittest has no export table.
TEST_F(ThirdPartyTest, WideCharEncoding) {
// Rename module to chinese unicode (kChineseUnicode). Be sure to handle
// any conversions to UTF8 appropriately here. No ASCII.
ASSERT_TRUE(MakeFileCopy(GetExeDir(), kTestDllName1, GetScopedTempDirValue(),
kChineseUnicode));
// 1) Test a successful DLL load with no blacklist.
base::CommandLine cmd_line1 = base::CommandLine::FromString(kTestExeFilename);
cmd_line1.AppendArgNative(GetBlTestFilePath());
cmd_line1.AppendArgNative(
base::IntToString16(main_unittest_exe::kTestSingleDllLoad));
cmd_line1.AppendArgNative(MakePath(GetScopedTempDirValue(), kChineseUnicode));
int exit_code = 0;
LaunchChildAndWait(cmd_line1, &exit_code);
ASSERT_EQ(main_unittest_exe::kDllLoadSuccess, exit_code);
//----------------------------------------------------------------------------
// 2) Test a failed DLL load with blacklist.
TestModuleData module_data = {};
ASSERT_TRUE(GetTestModuleData(kChineseUnicode, GetScopedTempDirValue(),
&module_data));
// Note: image_name will be empty, as there is no export table in this test
// module.
EXPECT_TRUE(module_data.image_name.empty());
std::vector<PackedListModule> vector;
vector.emplace_back(GeneratePackedListModule(module_data.section_basename,
module_data.timedatestamp,
module_data.imagesize));
ASSERT_TRUE(WriteModulesToBlacklist(vector));
base::CommandLine cmd_line2 = base::CommandLine::FromString(kTestExeFilename);
cmd_line2.AppendArgNative(GetBlTestFilePath());
cmd_line2.AppendArgNative(
base::IntToString16(main_unittest_exe::kTestSingleDllLoad));
cmd_line2.AppendArgNative(MakePath(GetScopedTempDirValue(), kChineseUnicode));
LaunchChildAndWait(cmd_line2, &exit_code);
ASSERT_EQ(main_unittest_exe::kDllLoadFailed, exit_code);
}
// Note: The test module used in this unittest has an export table.
TEST_F(ThirdPartyTest, WideCharEncodingWithExportDir) {
// Rename module to chinese unicode (kChineseUnicode). Be sure to handle
// any conversions to UTF8 appropriately here. No ASCII.
ASSERT_TRUE(MakeFileCopy(GetExeDir(), kTestDllName2, GetScopedTempDirValue(),
kChineseUnicode));
// 1) Test a successful DLL load with no blacklist. base::CommandLine cmd_line = base::CommandLine::FromString(exe_filename);
base::CommandLine cmd_line1 = base::CommandLine::FromString(kTestExeFilename); cmd_line.AppendArgNative(sys_dll_test);
cmd_line1.AppendArgNative(GetBlTestFilePath());
cmd_line1.AppendArgNative(
base::IntToString16(main_unittest_exe::kTestSingleDllLoad));
cmd_line1.AppendArgNative(MakePath(GetScopedTempDirValue(), kChineseUnicode));
int exit_code = 0; base::Process proc =
LaunchChildAndWait(cmd_line1, &exit_code); base::LaunchProcess(cmd_line, base::LaunchOptionsForTest());
ASSERT_EQ(main_unittest_exe::kDllLoadSuccess, exit_code); ASSERT_TRUE(proc.IsValid());
//----------------------------------------------------------------------------
// 2) Test a failed DLL load with blacklist.
TestModuleData module_data = {};
ASSERT_TRUE(GetTestModuleData(kChineseUnicode, GetScopedTempDirValue(),
&module_data));
// Ensure the export section was found as expected.
EXPECT_FALSE(module_data.image_name.empty());
// NOTE: a file rename does not affect the module name mined from the export
// table in the PE. So image_name and section_basename will be
// different. Ensure blacklisting both section name and image name
// works!
// 2a) Only blacklist the original DLL name, which should be mined out of the
// export table by the hook, and the load should be blocked.
std::vector<PackedListModule> vector;
vector.emplace_back(GeneratePackedListModule(
base::UTF16ToASCII(kTestDllName2), module_data.timedatestamp,
module_data.imagesize));
ASSERT_TRUE(WriteModulesToBlacklist(vector));
base::CommandLine cmd_line2 = base::CommandLine::FromString(kTestExeFilename);
cmd_line2.AppendArgNative(GetBlTestFilePath());
cmd_line2.AppendArgNative(
base::IntToString16(main_unittest_exe::kTestSingleDllLoad));
cmd_line2.AppendArgNative(MakePath(GetScopedTempDirValue(), kChineseUnicode));
LaunchChildAndWait(cmd_line2, &exit_code);
ASSERT_EQ(main_unittest_exe::kDllLoadFailed, exit_code);
// 2b) Only blacklist the new DLL file name, which should be mined out of the
// section by the hook, and the load should be blocked.
vector.clear();
vector.emplace_back(GeneratePackedListModule(
base::UTF16ToUTF8(kChineseUnicode), module_data.timedatestamp,
module_data.imagesize));
ASSERT_TRUE(WriteModulesToBlacklist(vector));
base::CommandLine cmd_line3 = base::CommandLine::FromString(kTestExeFilename);
cmd_line3.AppendArgNative(GetBlTestFilePath());
cmd_line3.AppendArgNative(
base::IntToString16(main_unittest_exe::kTestSingleDllLoad));
cmd_line3.AppendArgNative(MakePath(GetScopedTempDirValue(), kChineseUnicode));
LaunchChildAndWait(cmd_line3, &exit_code);
ASSERT_EQ(main_unittest_exe::kDllLoadFailed, exit_code);
}
// Note: The test module used in this unittest has no export table.
TEST_F(ThirdPartyTest, DeprecatedBlacklistSanityCheck) {
// Rename module to something on the old, deprecated, hard-coded blacklist.
ASSERT_TRUE(MakeFileCopy(GetExeDir(), kTestDllName1, GetScopedTempDirValue(),
kOldBlacklistDllName));
// 1) Test a failed DLL load with no blacklist (the old, hard-coded blacklist
// should trigger a block).
base::CommandLine cmd_line1 = base::CommandLine::FromString(kTestExeFilename);
cmd_line1.AppendArgNative(GetBlTestFilePath());
cmd_line1.AppendArgNative(
base::IntToString16(main_unittest_exe::kTestSingleDllLoad));
cmd_line1.AppendArgNative(
MakePath(GetScopedTempDirValue(), kOldBlacklistDllName));
int exit_code = 0; int exit_code = 0;
LaunchChildAndWait(cmd_line1, &exit_code); if (!proc.WaitForExitWithTimeout(g_timeout, &exit_code)) {
ASSERT_EQ(main_unittest_exe::kDllLoadFailed, exit_code); // Timeout while waiting. Try to cleanup.
} proc.Terminate(1, false);
ADD_FAILURE();
// Note: This test only sanity checks the two SHA1 libraries used on either side return;
// of the Third-Party block. }
// This Side: chrome_elf\third_party_dlls\*,
// elf_sha1::SHA1HashString().
// The Other Side: chrome\browser\conflicts\module_list_filter_win.cc,
// base::SHA1HashString().
TEST_F(ThirdPartyTest, SHA1SanityCheck) {
// Rename module to chinese unicode (kChineseUnicode). Be sure to handle
// any conversions to UTF8 appropriately here. No ASCII.
ASSERT_TRUE(MakeFileCopy(GetExeDir(), kTestDllName1, GetScopedTempDirValue(),
kChineseUnicode));
TestModuleData module_data = {};
ASSERT_TRUE(GetTestModuleData(kChineseUnicode, GetScopedTempDirValue(),
&module_data));
// Get hashes from elf_sha1.
PackedListModule elf_sha1_generated = GeneratePackedListModule(
base::UTF16ToUTF8(kChineseUnicode), module_data.timedatestamp,
module_data.imagesize);
// Get hashes from base_sha1.
const std::string module_basename_hash =
base::SHA1HashString(base::UTF16ToUTF8(kChineseUnicode));
const std::string module_code_id_hash =
base::SHA1HashString(base::StringPrintf(
"%08lX%lx", module_data.timedatestamp, module_data.imagesize));
// Compare the hashes.
EXPECT_EQ(::memcmp(elf_sha1_generated.basename_hash,
module_basename_hash.data(), elf_sha1::kSHA1Length),
0);
EXPECT_EQ(::memcmp(elf_sha1_generated.code_id_hash,
module_code_id_hash.data(), elf_sha1::kSHA1Length),
0);
} }
} // namespace } // namespace
......
// Copyright 2018 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>
BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) {
return TRUE;
}
// Copyright 2018 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>
extern "C" {
// Have a dummy export so that the module gets an export table entry.
void DummyExport() {}
} // extern "C"
BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) {
return TRUE;
}
; Copyright 2018 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.
LIBRARY "main_unittest_dll_2.dll"
EXPORTS
DummyExport
...@@ -3,41 +3,10 @@ ...@@ -3,41 +3,10 @@
// found in the LICENSE file. // found in the LICENSE file.
#include <windows.h> #include <windows.h>
#include "chrome_elf/third_party_dlls/main_unittest_exe.h"
#include <stdlib.h> #include <stdlib.h>
#include <shellapi.h>
#include "base/files/file.h"
#include "base/scoped_native_library.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/install_static/product_install_details.h" #include "chrome/install_static/product_install_details.h"
#include "chrome_elf/third_party_dlls/logging_api.h"
#include "chrome_elf/third_party_dlls/main.h" #include "chrome_elf/third_party_dlls/main.h"
#include "chrome_elf/third_party_dlls/packed_list_file.h"
using namespace third_party_dlls::main_unittest_exe;
namespace {
// Function object which invokes LocalFree on its parameter, which must be
// a pointer. To be used with std::unique_ptr and CommandLineToArgvW().
struct LocalFreeDeleter {
inline void operator()(wchar_t** ptr) const { ::LocalFree(ptr); }
};
// Attempt to load a given DLL.
ExitCode LoadDll(std::wstring name) {
base::FilePath dll_path(name);
base::ScopedNativeLibrary dll(dll_path);
if (!dll.is_valid())
return kDllLoadFailed;
return kDllLoadSuccess;
}
} // namespace
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// PUBLIC // PUBLIC
...@@ -47,84 +16,18 @@ ExitCode LoadDll(std::wstring name) { ...@@ -47,84 +16,18 @@ ExitCode LoadDll(std::wstring name) {
// - Init third_party_dlls, which will apply a hook to NtMapViewOfSection. // - Init third_party_dlls, which will apply a hook to NtMapViewOfSection.
// - Attempt to load a specific DLL. // - Attempt to load a specific DLL.
// //
// Arguments:
// #1: path to test blacklist file (mandatory).
// #2: test identifier (mandatory).
// #3: path to dll (test-identifier dependent).
//
// Returns: // Returns:
// - Negative values in case of unexpected error. // - Negative values in case of unexpected error.
// - 0 for successful DLL load. // - 0 for successful DLL load.
// - 1 for failed DLL load. // - 1 for failed DLL load.
int main() { int main(int argc, char** argv) {
// NOTE: The arguments must be treated as unicode for these tests.
int argument_count = 0;
std::unique_ptr<wchar_t* [], LocalFreeDeleter> argv(
::CommandLineToArgvW(::GetCommandLineW(), &argument_count));
if (!argv)
return kBadCommandLine;
if (third_party_dlls::IsThirdPartyInitialized()) if (third_party_dlls::IsThirdPartyInitialized())
return kThirdPartyAlreadyInitialized; _exit(-1);
install_static::InitializeProductDetailsForPrimaryModule(); install_static::InitializeProductDetailsForPrimaryModule();
// Get the required arguments, path to blacklist file and test id to run.
if (argument_count < 3)
return kMissingArgument;
const wchar_t* blacklist_path = argv[1];
if (!blacklist_path || ::wcslen(blacklist_path) == 0) {
return kBadBlacklistPath;
}
const wchar_t* arg2 = argv[2];
int test_id = ::_wtoi(arg2);
if (!test_id)
return kUnsupportedTestId;
// Override blacklist path before initializing.
third_party_dlls::OverrideFilePathForTesting(blacklist_path);
if (!third_party_dlls::Init()) if (!third_party_dlls::Init())
return kThirdPartyInitFailure; _exit(-2);
// Switch on test id.
switch (test_id) {
// Just initialization.
case kTestOnlyInitialization:
break;
// Single DLL load.
case kTestSingleDllLoad: {
if (argument_count < 4)
return kMissingArgument;
const wchar_t* dll_name = argv[3];
if (!dll_name || ::wcslen(dll_name) == 0)
return kBadArgument;
ExitCode code = LoadDll(dll_name);
// Get logging. Ensure the log is as expected.
uint32_t bytes = 0;
DrainLog(nullptr, 0, &bytes);
if (!bytes)
return kEmptyLog;
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[bytes]);
bytes = DrainLog(&buffer[0], bytes, nullptr);
third_party_dlls::LogEntry* entry =
reinterpret_cast<third_party_dlls::LogEntry*>(&buffer[0]);
if ((code == kDllLoadFailed &&
entry->type != third_party_dlls::kBlocked) ||
(code == kDllLoadSuccess &&
entry->type != third_party_dlls::kAllowed)) {
return kUnexpectedLog;
}
return code;
}
// Unsupported argument.
default:
return kUnsupportedTestId;
}
return 0; return 0;
} }
// Copyright 2018 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_THIRD_PARTY_DLLS_MAIN_UNITTEST_EXE_H_
#define CHROME_ELF_THIRD_PARTY_DLLS_MAIN_UNITTEST_EXE_H_
namespace third_party_dlls {
namespace main_unittest_exe {
enum ExitCode {
kDllLoadSuccess = 0,
kDllLoadFailed = 1,
// Unexpected failures are negative ints:
kBadCommandLine = -1,
kThirdPartyAlreadyInitialized = -2,
kThirdPartyInitFailure = -3,
kMissingArgument = -4,
kBadBlacklistPath = -5,
kBadArgument = -6,
kUnsupportedTestId = -7,
kEmptyLog = -8,
kUnexpectedLog = -9,
};
enum TestId {
kTestOnlyInitialization = 1,
kTestSingleDllLoad = 2,
};
} // namespace main_unittest_exe
} // namespace third_party_dlls
#endif // CHROME_ELF_THIRD_PARTY_DLLS_MAIN_UNITTEST_EXE_H_
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <limits> #include <limits>
#include "chrome/install_static/user_data_dir.h" #include "chrome/install_static/user_data_dir.h"
#include "chrome_elf/sha1/sha1.h"
#include "chrome_elf/third_party_dlls/packed_list_format.h" #include "chrome_elf/third_party_dlls/packed_list_format.h"
namespace third_party_dlls { namespace third_party_dlls {
...@@ -56,11 +57,6 @@ FileStatus ReadInArray(HANDLE file, ...@@ -56,11 +57,6 @@ FileStatus ReadInArray(HANDLE file,
if (!::ReadFile(file, &metadata, sizeof(PackedListMetadata), &bytes_read, if (!::ReadFile(file, &metadata, sizeof(PackedListMetadata), &bytes_read,
FALSE) || FALSE) ||
bytes_read != sizeof(PackedListMetadata)) { bytes_read != sizeof(PackedListMetadata)) {
// If |bytes_read| is actually 0, then the file was empty.
// A decision was made to return kArraySizeZero here, instead of a
// full failure.
if (!bytes_read)
return FileStatus::kArraySizeZero;
return FileStatus::kMetadataReadFail; return FileStatus::kMetadataReadFail;
} }
...@@ -168,6 +164,15 @@ FileStatus InitInternal() { ...@@ -168,6 +164,15 @@ FileStatus InitInternal() {
// Public defines & functions // Public defines & functions
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
std::string GetFingerprintHash(DWORD image_size, DWORD time_data_stamp) {
// Max hex 32-bit value is 8 characters long. 2*8+1.
char buffer[17] = {};
::snprintf(buffer, sizeof(buffer), "%08lX%lx", time_data_stamp, image_size);
std::string shell(buffer);
return elf_sha1::SHA1HashString(shell);
}
bool IsModuleListed(const std::string& basename_hash, bool IsModuleListed(const std::string& basename_hash,
const std::string& fingerprint_hash) { const std::string& fingerprint_hash) {
assert(g_initialized); assert(g_initialized);
......
...@@ -27,6 +27,10 @@ enum class FileStatus { ...@@ -27,6 +27,10 @@ enum class FileStatus {
COUNT COUNT
}; };
// Utility function that takes required PE fingerprint data and returns a SHA-1
// fingerprint hash. To be used with IsModuleListed() and logging APIs.
std::string GetFingerprintHash(DWORD image_size, DWORD time_data_stamp);
// Look up a binary based on the required data points. // Look up a binary based on the required data points.
// - Returns true if match found in the list. // - Returns true if match found in the list.
bool IsModuleListed(const std::string& basename_hash, bool IsModuleListed(const std::string& basename_hash,
......
...@@ -169,15 +169,13 @@ TEST_F(ThirdPartyFileTest, Success) { ...@@ -169,15 +169,13 @@ TEST_F(ThirdPartyFileTest, Success) {
// Test matching. // Test matching.
for (const auto& test_module : GetTestArray()) { for (const auto& test_module : GetTestArray()) {
fingerprint_hash = fingerprint_hash =
GetFingerprintString(test_module.imagesize, test_module.timedatestamp); GetFingerprintHash(test_module.imagesize, test_module.timedatestamp);
fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash);
name_hash = elf_sha1::SHA1HashString(test_module.basename); name_hash = elf_sha1::SHA1HashString(test_module.basename);
EXPECT_TRUE(IsModuleListed(name_hash, fingerprint_hash)); EXPECT_TRUE(IsModuleListed(name_hash, fingerprint_hash));
} }
// Test a failure to match. // Test a failure to match.
fingerprint_hash = GetFingerprintString(1337, 0x12345678); fingerprint_hash = GetFingerprintHash(1337, 0x12345678);
fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash);
name_hash = elf_sha1::SHA1HashString("booya.dll"); name_hash = elf_sha1::SHA1HashString("booya.dll");
EXPECT_FALSE(IsModuleListed(name_hash, fingerprint_hash)); EXPECT_FALSE(IsModuleListed(name_hash, fingerprint_hash));
} }
...@@ -186,8 +184,7 @@ TEST_F(ThirdPartyFileTest, Success) { ...@@ -186,8 +184,7 @@ TEST_F(ThirdPartyFileTest, Success) {
TEST_F(ThirdPartyFileTest, NoFiles) { TEST_F(ThirdPartyFileTest, NoFiles) {
ASSERT_EQ(InitFromFile(), FileStatus::kSuccess); ASSERT_EQ(InitFromFile(), FileStatus::kSuccess);
std::string fingerprint_hash = GetFingerprintString(1337, 0x12345678); std::string fingerprint_hash = GetFingerprintHash(1337, 0x12345678);
fingerprint_hash = elf_sha1::SHA1HashString(fingerprint_hash);
std::string name_hash = elf_sha1::SHA1HashString("booya.dll"); std::string name_hash = elf_sha1::SHA1HashString("booya.dll");
EXPECT_FALSE(IsModuleListed(name_hash, fingerprint_hash)); EXPECT_FALSE(IsModuleListed(name_hash, fingerprint_hash));
} }
......
...@@ -20,13 +20,4 @@ const wchar_t kFileSubdir[] = ...@@ -20,13 +20,4 @@ const wchar_t kFileSubdir[] =
// Packed module data cache file. // Packed module data cache file.
const wchar_t kBlFileName[] = L"\\bldata"; const wchar_t kBlFileName[] = L"\\bldata";
std::string GetFingerprintString(uint32_t image_size,
uint32_t time_data_stamp) {
// Max hex 32-bit value is 8 characters long. 2*8+1.
char buffer[17] = {};
::snprintf(buffer, sizeof(buffer), "%08X%x", time_data_stamp, image_size);
return std::string(buffer);
}
} // namespace third_party_dlls } // namespace third_party_dlls
...@@ -10,8 +10,6 @@ ...@@ -10,8 +10,6 @@
#ifndef CHROME_ELF_THIRD_PARTY_DLLS_PACKED_LIST_FORMAT_H_ #ifndef CHROME_ELF_THIRD_PARTY_DLLS_PACKED_LIST_FORMAT_H_
#define CHROME_ELF_THIRD_PARTY_DLLS_PACKED_LIST_FORMAT_H_ #define CHROME_ELF_THIRD_PARTY_DLLS_PACKED_LIST_FORMAT_H_
#include <string>
#include <stdint.h> #include <stdint.h>
#include "chrome_elf/sha1/sha1.h" #include "chrome_elf/sha1/sha1.h"
...@@ -54,22 +52,15 @@ struct PackedListMetadata { ...@@ -54,22 +52,15 @@ struct PackedListMetadata {
struct PackedListModule { struct PackedListModule {
// SHA1 of lowercase, UTF-8 basename (no path). // SHA1 of lowercase, UTF-8 basename (no path).
uint8_t basename_hash[elf_sha1::kSHA1Length]; uint8_t basename_hash[elf_sha1::kSHA1Length];
// Code ID. Get the formatted string via GetFingerprintString(), then SHA1. // Code ID. This is equivalent to the string generated by formatting
// the FileHeader.TimeDateStamp and OptionalHeader.SizeOfImage with the
// formatting string %08X%x. Then SHA1 the string.
uint8_t code_id_hash[elf_sha1::kSHA1Length]; uint8_t code_id_hash[elf_sha1::kSHA1Length];
// A timestamp used for tracking "last attempted load". Used to manage // A timestamp used for tracking "last attempted load". Used to manage
// lifetime of entries in the local caches. // lifetime of entries in the local caches.
uint32_t time_date_stamp; uint32_t time_date_stamp;
}; };
// Centralized utility function that takes required PE fingerprint data and
// returns a formatted fingerprint string. The caller should SHA1 hash the
// returned string.
// - This string is generated by formatting the FileHeader.TimeDateStamp
// and OptionalHeader.SizeOfImage with the formatting string %08X%x.
// - To be used for PackedListModule.code_id_hash, IsModuleListed() and logging
// APIs.
std::string GetFingerprintString(uint32_t image_size, uint32_t time_data_stamp);
// These structs are directly written to a storage type. Therefore the padding // These structs are directly written to a storage type. Therefore the padding
// should be consistent across compilations. // should be consistent across compilations.
static_assert(sizeof(PackedListMetadata) == 8, static_assert(sizeof(PackedListMetadata) == 8,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment