1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/chrome_elf/third_party_dlls/main.h"
6 
7 #include <windows.h>
8 
9 #include <string>
10 
11 #include "base/command_line.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/hash/sha1.h"
15 #include "base/path_service.h"
16 #include "base/process/launch.h"
17 #include "base/scoped_native_library.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/test/test_reg_util_win.h"
22 #include "base/test/test_timeouts.h"
23 #include "build/build_config.h"
24 #include "chrome/chrome_elf/nt_registry/nt_registry.h"
25 #include "chrome/chrome_elf/sha1/sha1.h"
26 #include "chrome/chrome_elf/third_party_dlls/hook.h"
27 #include "chrome/chrome_elf/third_party_dlls/main_unittest_exe.h"
28 #include "chrome/chrome_elf/third_party_dlls/packed_list_file.h"
29 #include "chrome/chrome_elf/third_party_dlls/packed_list_format.h"
30 #include "chrome/install_static/install_util.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32 
33 namespace third_party_dlls {
34 namespace {
35 
36 constexpr wchar_t kTestExeFilename[] = L"third_party_dlls_test_exe.exe";
37 constexpr wchar_t kTestBlFileName[] = L"blfile";
38 constexpr wchar_t kTestDllName1[] = L"main_unittest_dll_1.dll";
39 constexpr wchar_t kTestDllName1MixedCase[] = L"MaiN_uniTtest_dLL_1.Dll";
40 constexpr wchar_t kTestDllName2[] = L"main_unittest_dll_2.dll";
41 constexpr wchar_t kChineseUnicode[] = {0x68D5, 0x8272, 0x72D0, 0x72F8, 0x002E,
42                                        0x0064, 0x006C, 0x006C, 0x0000};
43 constexpr wchar_t kOldBlacklistDllName[] = L"libapi2hook.dll";
44 
45 struct TestModuleData {
46   std::string image_name;
47   std::string section_path;
48   std::string section_basename;
49   DWORD timedatestamp;
50   DWORD imagesize;
51 };
52 
53 // NOTE: TestTimeouts::action_max_timeout() is not long enough here.
54 base::TimeDelta g_timeout = ::IsDebuggerPresent()
55                                 ? base::TimeDelta::FromMilliseconds(INFINITE)
56                                 : base::TimeDelta::FromMilliseconds(5000);
57 
58 // Centralize child test process control.
LaunchChildAndWait(const base::CommandLine & command_line,int * exit_code)59 void LaunchChildAndWait(const base::CommandLine& command_line, int* exit_code) {
60   base::Process proc =
61       base::LaunchProcess(command_line, base::LaunchOptionsForTest());
62   ASSERT_TRUE(proc.IsValid());
63 
64   *exit_code = 0;
65   if (!proc.WaitForExitWithTimeout(g_timeout, exit_code)) {
66     // Timeout while waiting.  Try to cleanup.
67     proc.Terminate(1, false);
68     ADD_FAILURE();
69   }
70 
71   return;
72 }
73 
74 // Given the name and path of a test DLL, mine the data of interest out of it
75 // and return it via |test_module|.
76 // - Note: the DLL must be loaded into memory by NTLoader to mine all of the
77 //         desired data (not just read from disk).
GetTestModuleData(const std::wstring & file_name,const std::wstring & file_path,TestModuleData * test_module)78 bool GetTestModuleData(const std::wstring& file_name,
79                        const std::wstring& file_path,
80                        TestModuleData* test_module) {
81   base::FilePath path(file_path);
82   path = path.Append(file_name);
83 
84   // Map the target DLL into memory just long enough to mine data out of it.
85   base::ScopedNativeLibrary test_dll(path);
86   if (!test_dll.is_valid())
87     return false;
88 
89   return GetDataFromImageForTesting(
90       test_dll.get(), &test_module->timedatestamp, &test_module->imagesize,
91       &test_module->image_name, &test_module->section_path,
92       &test_module->section_basename);
93 }
94 
95 // Turn given data into a PackedListModule structure.
96 // - |image_name| should be utf-8 at this point.
GeneratePackedListModule(const std::string & image_name,DWORD timedatestamp,DWORD imagesize)97 PackedListModule GeneratePackedListModule(const std::string& image_name,
98                                           DWORD timedatestamp,
99                                           DWORD imagesize) {
100   // Internally, an empty string should not be passed in here.
101   assert(!image_name.empty());
102 
103   // SHA1 hash the two strings into the new struct.
104   PackedListModule packed_module;
105   packed_module.code_id_hash =
106       elf_sha1::SHA1HashString(GetFingerprintString(timedatestamp, imagesize));
107   packed_module.basename_hash = elf_sha1::SHA1HashString(image_name);
108 
109   return packed_module;
110 }
111 
MakePath(const std::wstring & path,const std::wstring & name)112 inline std::wstring MakePath(const std::wstring& path,
113                              const std::wstring& name) {
114   std::wstring full_path(path);
115   full_path.push_back(L'\\');
116   full_path.append(name);
117   return full_path;
118 }
119 
MakeFileCopy(const std::wstring & old_path,const std::wstring & old_name,const std::wstring & new_path,const std::wstring & new_name)120 inline bool MakeFileCopy(const std::wstring& old_path,
121                          const std::wstring& old_name,
122                          const std::wstring& new_path,
123                          const std::wstring& new_name) {
124   base::FilePath source(MakePath(old_path, old_name));
125   base::FilePath destination(MakePath(new_path, new_name));
126   return base::CopyFileW(source, destination);
127 }
128 
129 // Utility function for protecting local registry.
RegRedirect(nt::ROOT_KEY key,registry_util::RegistryOverrideManager * rom)130 void RegRedirect(nt::ROOT_KEY key,
131                  registry_util::RegistryOverrideManager* rom) {
132   ASSERT_NE(key, nt::AUTO);
133   HKEY root = (key == nt::HKCU ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE);
134   base::string16 temp;
135 
136   ASSERT_NO_FATAL_FAILURE(rom->OverrideRegistry(root, &temp));
137   ASSERT_TRUE(nt::SetTestingOverride(key, temp));
138 }
139 
140 // Utility function to disable local registry protection.
CancelRegRedirect(nt::ROOT_KEY key)141 void CancelRegRedirect(nt::ROOT_KEY key) {
142   ASSERT_NE(key, nt::AUTO);
143   ASSERT_TRUE(nt::SetTestingOverride(key, base::string16()));
144 }
145 
146 // Use NtRegistry to query status codes (it's more handy than base).
147 // - Returns true if the key value exists and is type REG_BINARY.
QueryStatusCodes(std::vector<ThirdPartyStatus> * status_array)148 bool QueryStatusCodes(std::vector<ThirdPartyStatus>* status_array) {
149   HANDLE handle = nullptr;
150   if (!nt::OpenRegKey(nt::HKCU,
151                       install_static::GetRegistryPath()
152                           .append(kThirdPartyRegKeyName)
153                           .c_str(),
154                       KEY_QUERY_VALUE, &handle, nullptr)) {
155     return false;
156   }
157 
158   ULONG type = REG_NONE;
159   std::vector<uint8_t> temp_buffer;
160   bool success =
161       nt::QueryRegKeyValue(handle, kStatusCodesRegValue, &type, &temp_buffer);
162   nt::CloseRegKey(handle);
163 
164   if (!success || type != REG_BINARY)
165     return false;
166 
167   ConvertBufferToStatusCodes(temp_buffer, status_array);
168 
169   return true;
170 }
171 
172 //------------------------------------------------------------------------------
173 // ThirdPartyTest class
174 //------------------------------------------------------------------------------
175 
176 class ThirdPartyTest : public testing::Test {
177  protected:
178   ThirdPartyTest() = default;
179 
SetUp()180   void SetUp() override {
181     // Setup temp test dir.
182     ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
183 
184     // Store full path to test file.
185     base::FilePath path = scoped_temp_dir_.GetPath();
186     path = path.Append(kTestBlFileName);
187     bl_test_file_path_ = std::move(path.value());
188 
189     // Also store a copy of current exe directory for efficiency.
190     base::FilePath exe;
191     ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &exe));
192     exe_dir_ = std::move(exe.value());
193 
194     // Create the blacklist file empty.
195     base::File file(base::FilePath(bl_test_file_path_),
196                     base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
197                         base::File::FLAG_SHARE_DELETE |
198                         base::File::FLAG_DELETE_ON_CLOSE);
199     ASSERT_TRUE(file.IsValid());
200 
201     // Leave file handle open for DELETE_ON_CLOSE.
202     bl_file_ = std::move(file);
203   }
204 
TearDown()205   void TearDown() override {}
206 
207   // Overwrite the content of the blacklist file.
WriteModulesToBlacklist(const std::vector<PackedListModule> & list)208   bool WriteModulesToBlacklist(const std::vector<PackedListModule>& list) {
209     bl_file_.SetLength(0);
210 
211     // Write content {metadata}{array_of_modules}.
212     PackedListMetadata meta = {kInitialVersion,
213                                static_cast<uint32_t>(list.size())};
214 
215     if (bl_file_.Write(0, reinterpret_cast<const char*>(&meta), sizeof(meta)) !=
216         static_cast<int>(sizeof(meta))) {
217       return false;
218     }
219     int size = static_cast<int>(list.size() * sizeof(PackedListModule));
220     if (bl_file_.Write(sizeof(PackedListMetadata),
221                        reinterpret_cast<const char*>(list.data()),
222                        size) != size) {
223       return false;
224     }
225 
226     return true;
227   }
228 
GetBlTestFilePath()229   const base::string16& GetBlTestFilePath() { return bl_test_file_path_; }
GetExeDir()230   const base::string16& GetExeDir() { return exe_dir_; }
GetScopedTempDirValue()231   const std::wstring& GetScopedTempDirValue() {
232     return scoped_temp_dir_.GetPath().value();
233   }
234 
235  private:
236   base::ScopedTempDir scoped_temp_dir_;
237   base::File bl_file_;
238   base::string16 bl_test_file_path_;
239   base::string16 exe_dir_;
240 
241   DISALLOW_COPY_AND_ASSIGN(ThirdPartyTest);
242 };
243 
244 //------------------------------------------------------------------------------
245 // Third-party tests
246 //
247 // These tests spawn a child test process to keep the hooking contained to a
248 // separate process.  This prevents test clashes in certain testing
249 // configurations.
250 //------------------------------------------------------------------------------
251 
252 #if defined(OS_WIN)
253 #define MAYBE_Base DISABLED_Base
254 #else
255 #define MAYBE_Base Base
256 #endif
257 // Note: The test module used in this unittest has no export table.
TEST_F(ThirdPartyTest,MAYBE_Base)258 TEST_F(ThirdPartyTest, MAYBE_Base) {
259   // 1. Spawn the test process with NO blacklist.  Expect successful
260   // initialization.
261   base::CommandLine cmd_line1 = base::CommandLine::FromString(kTestExeFilename);
262   cmd_line1.AppendArgNative(GetBlTestFilePath());
263   cmd_line1.AppendArgNative(base::NumberToString16(kTestOnlyInitialization));
264 
265   int exit_code = 0;
266   LaunchChildAndWait(cmd_line1, &exit_code);
267   ASSERT_EQ(kDllLoadSuccess, exit_code);
268 
269   //----------------------------------------------------------------------------
270   // 2. Spawn the test process with NO blacklist.  Expect successful DLL load.
271   base::CommandLine cmd_line2 = base::CommandLine::FromString(kTestExeFilename);
272   cmd_line2.AppendArgNative(GetBlTestFilePath());
273   cmd_line2.AppendArgNative(base::NumberToString16(kTestSingleDllLoad));
274   cmd_line2.AppendArgNative(MakePath(GetExeDir(), kTestDllName1));
275 
276   LaunchChildAndWait(cmd_line2, &exit_code);
277   ASSERT_EQ(kDllLoadSuccess, exit_code);
278 
279   //----------------------------------------------------------------------------
280   // 3. Spawn the test process with blacklist.  Expect failed DLL load.
281   TestModuleData module_data = {};
282   ASSERT_TRUE(GetTestModuleData(kTestDllName1, GetExeDir(), &module_data));
283 
284   // Note: image_name will be empty, as there is no export table in this test
285   //       module.
286   EXPECT_TRUE(module_data.image_name.empty());
287 
288   std::vector<PackedListModule> vector(1);
289   vector.emplace_back(GeneratePackedListModule(module_data.section_basename,
290                                                module_data.timedatestamp,
291                                                module_data.imagesize));
292   ASSERT_TRUE(WriteModulesToBlacklist(vector));
293 
294   base::CommandLine cmd_line3 = base::CommandLine::FromString(kTestExeFilename);
295   cmd_line3.AppendArgNative(GetBlTestFilePath());
296   cmd_line3.AppendArgNative(base::NumberToString16(kTestSingleDllLoad));
297   cmd_line3.AppendArgNative(MakePath(GetExeDir(), kTestDllName1));
298 
299   LaunchChildAndWait(cmd_line3, &exit_code);
300   ASSERT_EQ(kDllLoadFailed, exit_code);
301 
302   //----------------------------------------------------------------------------
303   // 4. Spawn the test process with blacklist.  Expect failed DLL load.
304   //    ** Rename the module with some upper-case characters to test that
305   //       the hook matching handles case properly.
306   ASSERT_TRUE(MakeFileCopy(GetExeDir(), kTestDllName1, GetScopedTempDirValue(),
307                            kTestDllName1MixedCase));
308 
309   // Note: the blacklist is already set from the previous test.
310   // Note: using the module with no export table for this test, to ensure that
311   //       the section name (the rename) is used in the comparison.
312 
313   base::CommandLine cmd_line4 = base::CommandLine::FromString(kTestExeFilename);
314   cmd_line4.AppendArgNative(GetBlTestFilePath());
315   cmd_line4.AppendArgNative(base::NumberToString16(kTestSingleDllLoad));
316   cmd_line4.AppendArgNative(
317       MakePath(GetScopedTempDirValue(), kTestDllName1MixedCase));
318 
319   LaunchChildAndWait(cmd_line4, &exit_code);
320   ASSERT_EQ(kDllLoadFailed, exit_code);
321 }
322 
323 // Note: The test module used in this unittest has no export table.
TEST_F(ThirdPartyTest,WideCharEncoding)324 TEST_F(ThirdPartyTest, WideCharEncoding) {
325   // Rename module to chinese unicode (kChineseUnicode).  Be sure to handle
326   // any conversions to UTF8 appropriately here.  No ASCII.
327   ASSERT_TRUE(MakeFileCopy(GetExeDir(), kTestDllName1, GetScopedTempDirValue(),
328                            kChineseUnicode));
329 
330   // 1) Test a successful DLL load with no blacklist.
331   base::CommandLine cmd_line1 = base::CommandLine::FromString(kTestExeFilename);
332   cmd_line1.AppendArgNative(GetBlTestFilePath());
333   cmd_line1.AppendArgNative(base::NumberToString16(kTestSingleDllLoad));
334   cmd_line1.AppendArgNative(MakePath(GetScopedTempDirValue(), kChineseUnicode));
335 
336   int exit_code = 0;
337   LaunchChildAndWait(cmd_line1, &exit_code);
338   ASSERT_EQ(kDllLoadSuccess, exit_code);
339 
340   //----------------------------------------------------------------------------
341   // 2) Test a failed DLL load with blacklist.
342   TestModuleData module_data = {};
343   ASSERT_TRUE(GetTestModuleData(kChineseUnicode, GetScopedTempDirValue(),
344                                 &module_data));
345 
346   // Note: image_name will be empty, as there is no export table in this test
347   //       module.
348   EXPECT_TRUE(module_data.image_name.empty());
349 
350   std::vector<PackedListModule> vector;
351   vector.emplace_back(GeneratePackedListModule(module_data.section_basename,
352                                                module_data.timedatestamp,
353                                                module_data.imagesize));
354   ASSERT_TRUE(WriteModulesToBlacklist(vector));
355 
356   base::CommandLine cmd_line2 = base::CommandLine::FromString(kTestExeFilename);
357   cmd_line2.AppendArgNative(GetBlTestFilePath());
358   cmd_line2.AppendArgNative(base::NumberToString16(kTestSingleDllLoad));
359   cmd_line2.AppendArgNative(MakePath(GetScopedTempDirValue(), kChineseUnicode));
360 
361   LaunchChildAndWait(cmd_line2, &exit_code);
362   ASSERT_EQ(kDllLoadFailed, exit_code);
363 }
364 
365 // Note: The test module used in this unittest has an export table.
TEST_F(ThirdPartyTest,WideCharEncodingWithExportDir)366 TEST_F(ThirdPartyTest, WideCharEncodingWithExportDir) {
367   // Rename module to chinese unicode (kChineseUnicode).  Be sure to handle
368   // any conversions to UTF8 appropriately here.  No ASCII.
369   ASSERT_TRUE(MakeFileCopy(GetExeDir(), kTestDllName2, GetScopedTempDirValue(),
370                            kChineseUnicode));
371 
372   // 1) Test a successful DLL load with no blacklist.
373   base::CommandLine cmd_line1 = base::CommandLine::FromString(kTestExeFilename);
374   cmd_line1.AppendArgNative(GetBlTestFilePath());
375   cmd_line1.AppendArgNative(base::NumberToString16(kTestSingleDllLoad));
376   cmd_line1.AppendArgNative(MakePath(GetScopedTempDirValue(), kChineseUnicode));
377 
378   int exit_code = 0;
379   LaunchChildAndWait(cmd_line1, &exit_code);
380   ASSERT_EQ(kDllLoadSuccess, exit_code);
381 
382   //----------------------------------------------------------------------------
383   // 2) Test a failed DLL load with blacklist.
384   TestModuleData module_data = {};
385   ASSERT_TRUE(GetTestModuleData(kChineseUnicode, GetScopedTempDirValue(),
386                                 &module_data));
387   // Ensure the export section was found as expected.
388   EXPECT_FALSE(module_data.image_name.empty());
389 
390   // NOTE: a file rename does not affect the module name mined from the export
391   //       table in the PE.  So image_name and section_basename will be
392   //       different. Ensure blacklisting both section name and image name
393   //       works!
394 
395   // 2a) Only blacklist the original DLL name, which should be mined out of the
396   //     export table by the hook, and the load should be blocked.
397   std::vector<PackedListModule> vector;
398   vector.emplace_back(GeneratePackedListModule(
399       base::UTF16ToASCII(kTestDllName2), module_data.timedatestamp,
400       module_data.imagesize));
401   ASSERT_TRUE(WriteModulesToBlacklist(vector));
402 
403   base::CommandLine cmd_line2 = base::CommandLine::FromString(kTestExeFilename);
404   cmd_line2.AppendArgNative(GetBlTestFilePath());
405   cmd_line2.AppendArgNative(base::NumberToString16(kTestSingleDllLoad));
406   cmd_line2.AppendArgNative(MakePath(GetScopedTempDirValue(), kChineseUnicode));
407 
408   LaunchChildAndWait(cmd_line2, &exit_code);
409   ASSERT_EQ(kDllLoadFailed, exit_code);
410 
411   // 2b) Only blacklist the new DLL file name, which should be mined out of the
412   //     section by the hook, and the load should be blocked.
413   vector.clear();
414   vector.emplace_back(GeneratePackedListModule(
415       base::UTF16ToUTF8(kChineseUnicode), module_data.timedatestamp,
416       module_data.imagesize));
417   ASSERT_TRUE(WriteModulesToBlacklist(vector));
418 
419   base::CommandLine cmd_line3 = base::CommandLine::FromString(kTestExeFilename);
420   cmd_line3.AppendArgNative(GetBlTestFilePath());
421   cmd_line3.AppendArgNative(base::NumberToString16(kTestSingleDllLoad));
422   cmd_line3.AppendArgNative(MakePath(GetScopedTempDirValue(), kChineseUnicode));
423 
424   LaunchChildAndWait(cmd_line3, &exit_code);
425   ASSERT_EQ(kDllLoadFailed, exit_code);
426 }
427 
428 // Note: The test module used in this unittest has no export table.
TEST_F(ThirdPartyTest,DeprecatedBlacklistSanityCheck)429 TEST_F(ThirdPartyTest, DeprecatedBlacklistSanityCheck) {
430   // Rename module to something on the old, deprecated, hard-coded blacklist.
431   ASSERT_TRUE(MakeFileCopy(GetExeDir(), kTestDllName1, GetScopedTempDirValue(),
432                            kOldBlacklistDllName));
433 
434   // 1) Test a failed DLL load with no blacklist (the old, hard-coded blacklist
435   //    should trigger a block).
436   base::CommandLine cmd_line1 = base::CommandLine::FromString(kTestExeFilename);
437   cmd_line1.AppendArgNative(GetBlTestFilePath());
438   cmd_line1.AppendArgNative(base::NumberToString16(kTestSingleDllLoad));
439   cmd_line1.AppendArgNative(
440       MakePath(GetScopedTempDirValue(), kOldBlacklistDllName));
441 
442   int exit_code = 0;
443   LaunchChildAndWait(cmd_line1, &exit_code);
444   ASSERT_EQ(kDllLoadFailed, exit_code);
445 }
446 
447 // Note: This test only sanity checks the two SHA1 libraries used on either side
448 //       of the Third-Party block.
449 //       This Side: chrome/chrome_elf\third_party_dlls\*,
450 //                  elf_sha1::SHA1HashString().
451 //       The Other Side: chrome\browser\conflicts\module_list_filter_win.cc,
452 //                       base::SHA1HashString().
TEST_F(ThirdPartyTest,SHA1SanityCheck)453 TEST_F(ThirdPartyTest, SHA1SanityCheck) {
454   // Rename module to chinese unicode (kChineseUnicode).  Be sure to handle
455   // any conversions to UTF8 appropriately here.  No ASCII.
456   ASSERT_TRUE(MakeFileCopy(GetExeDir(), kTestDllName1, GetScopedTempDirValue(),
457                            kChineseUnicode));
458 
459   TestModuleData module_data = {};
460   ASSERT_TRUE(GetTestModuleData(kChineseUnicode, GetScopedTempDirValue(),
461                                 &module_data));
462 
463   // Get hashes from elf_sha1.
464   PackedListModule elf_sha1_generated = GeneratePackedListModule(
465       base::UTF16ToUTF8(kChineseUnicode), module_data.timedatestamp,
466       module_data.imagesize);
467 
468   // Get hashes from base_sha1.
469   const std::string module_basename_hash =
470       base::SHA1HashString(base::UTF16ToUTF8(kChineseUnicode));
471   const std::string module_code_id_hash = base::SHA1HashString(
472       GetFingerprintString(module_data.timedatestamp, module_data.imagesize));
473 
474   // Compare the hashes.
475   EXPECT_EQ(::memcmp(&elf_sha1_generated.basename_hash[0],
476                      module_basename_hash.data(), elf_sha1::kSHA1Length),
477             0);
478   EXPECT_EQ(::memcmp(&elf_sha1_generated.code_id_hash[0],
479                      module_code_id_hash.data(), elf_sha1::kSHA1Length),
480             0);
481 }
482 
483 // Flaky: crbug.com/868233
484 #if defined(OS_WIN)
485 #define MAYBE_PathCaseSensitive DISABLED_PathCaseSensitive
486 #else
487 #define MAYBE_PathCaseSensitive PathCaseSensitive
488 #endif
489 
490 // Test that full section path is left alone, in terms of case.
TEST_F(ThirdPartyTest,MAYBE_PathCaseSensitive)491 TEST_F(ThirdPartyTest, MAYBE_PathCaseSensitive) {
492   // Rename module to have mixed case.
493   ASSERT_TRUE(MakeFileCopy(GetExeDir(), kTestDllName2, GetScopedTempDirValue(),
494                            kTestDllName1MixedCase));
495 
496   // 1) Sanity check that the hook GetDataFromImage() mining leaves the
497   // section path alone.
498   TestModuleData module_data = {};
499   ASSERT_TRUE(GetTestModuleData(kTestDllName1MixedCase, GetScopedTempDirValue(),
500                                 &module_data));
501   // Reminder: this string is actually UTF-8, but this test ensures it is ascii.
502   // Also, |section_path| will be a device path, so convert to drive letter
503   // before comparing.
504   base::FilePath drive;
505   ASSERT_TRUE(base::DevicePathToDriveLetterPath(
506       base::FilePath(base::ASCIIToUTF16(module_data.section_path)), &drive));
507 
508   EXPECT_EQ(drive.value().compare(
509                 MakePath(GetScopedTempDirValue(), kTestDllName1MixedCase)),
510             0);
511 
512   // 2) Now check an actual log.  Successful DLL load with no blacklist is fine
513   //    for this test.
514   base::CommandLine cmd_line1 = base::CommandLine::FromString(kTestExeFilename);
515   cmd_line1.AppendArgNative(GetBlTestFilePath());
516   cmd_line1.AppendArgNative(base::NumberToString16(kTestSingleDllLoad));
517   cmd_line1.AppendArgNative(
518       MakePath(GetScopedTempDirValue(), kTestDllName1MixedCase));
519 
520   int exit_code = 0;
521   LaunchChildAndWait(cmd_line1, &exit_code);
522   ASSERT_EQ(kDllLoadSuccess, exit_code);
523 }
524 
525 // Test the status-code passing in registry.
TEST_F(ThirdPartyTest,StatusCodes)526 TEST_F(ThirdPartyTest, StatusCodes) {
527   // 1. Enable reg override for test net.
528   registry_util::RegistryOverrideManager override_manager;
529   ASSERT_NO_FATAL_FAILURE(RegRedirect(nt::HKCU, &override_manager));
530 
531   // 2. Reset the registry key and value to empty.
532   ASSERT_TRUE(ResetStatusCodesForTesting());
533 
534   // 3. Confirm key and empty value.
535   std::vector<ThirdPartyStatus> code_array;
536   EXPECT_TRUE(QueryStatusCodes(&code_array));
537   EXPECT_EQ(0u, code_array.size());
538 
539   // 4. Add status codes, then verify.
540   ASSERT_NO_FATAL_FAILURE(
541       AddStatusCodeForTesting(ThirdPartyStatus::kFileEmpty));
542   ASSERT_NO_FATAL_FAILURE(
543       AddStatusCodeForTesting(ThirdPartyStatus::kLogsCreateMutexFailure));
544   ASSERT_NO_FATAL_FAILURE(
545       AddStatusCodeForTesting(ThirdPartyStatus::kHookVirtualProtectFailure));
546   EXPECT_TRUE(QueryStatusCodes(&code_array));
547   ASSERT_EQ(3u, code_array.size());
548   EXPECT_EQ(ThirdPartyStatus::kFileEmpty, code_array[0]);
549   EXPECT_EQ(ThirdPartyStatus::kLogsCreateMutexFailure, code_array[1]);
550   EXPECT_EQ(ThirdPartyStatus::kHookVirtualProtectFailure, code_array[2]);
551 
552   // 5. Reset the registry value to empty.
553   EXPECT_TRUE(ResetStatusCodesForTesting());
554   EXPECT_TRUE(QueryStatusCodes(&code_array));
555   EXPECT_EQ(0u, code_array.size());
556 
557   // 6. Disable reg override.
558   ASSERT_NO_FATAL_FAILURE(CancelRegRedirect(nt::HKCU));
559 }
560 
561 }  // namespace
562 }  // namespace third_party_dlls
563