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_cleaner/scanner/matcher_util.h"
6 
7 #include <shlobj.h>
8 
9 #include <memory>
10 #include <string>
11 
12 #include "base/command_line.h"
13 #include "base/path_service.h"
14 #include "base/stl_util.h"
15 #include "base/strings/strcat.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/test/scoped_path_override.h"
18 #include "base/test/test_reg_util_win.h"
19 #include "base/win/registry.h"
20 #include "chrome/chrome_cleaner/os/file_path_sanitization.h"
21 #include "chrome/chrome_cleaner/scanner/signature_matcher.h"
22 #include "chrome/chrome_cleaner/test/resources/grit/test_resources.h"
23 #include "chrome/chrome_cleaner/test/test_file_util.h"
24 #include "chrome/chrome_cleaner/test/test_pup_data.h"
25 #include "chrome/chrome_cleaner/test/test_signature_matcher.h"
26 #include "chrome/chrome_cleaner/test/test_task_scheduler.h"
27 #include "chrome/chrome_cleaner/test/test_util.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 
30 namespace chrome_cleaner {
31 
32 namespace {
33 
34 constexpr wchar_t kFileName1[] = L"File1";
35 constexpr wchar_t kFileName2[] = L"File2";
36 constexpr wchar_t kFileName3[] = L"File3";
37 constexpr wchar_t kFileName4[] = L"File4";
38 
39 constexpr char kFileContent1[] = "This is the file content.";
40 constexpr char kFileContent2[] = "Hi!";
41 constexpr char kFileContent3[] = "Hello World!";
42 constexpr char kFileContent4[] = "Ha!";
43 
44 constexpr char kFileContent[] = "This is the file content.";
45 
46 const char* const kKnownContentDigests[] = {
47     "00D2BB3E285BA62224888A9AD874AC2787D4CF681F30A2FD8EE2873859ECE1DC",
48     // Hash for content: |kFileContent|.
49     "BD283E41A3672B6BDAA574F8BD7176F8BCA95BD81383CDE32AA6D78B1DB0E371",
50 };
51 
52 constexpr wchar_t kKnownOriginalFilename[] = L"uws.exe";
53 constexpr wchar_t kUnknownOriginalFilename[] = L"knowngood.exe";
54 const wchar_t* const kKnownOriginalFilenames[] = {
55     L"dummy entry",
56     L"uws.exe",
57     L"zombie uws.exe",
58 };
59 
60 constexpr wchar_t kKnownCompanyName[] = L"uws vendor inc";
61 constexpr wchar_t kUnknownCompanyName[] = L"paradise";
62 const wchar_t* const kKnownCompanyNames[] = {
63     L"dummy entry",
64     L"uws vendor inc",
65     L"ACME",
66 };
67 
68 constexpr FileDigestInfo kFileContentDigestInfos[] = {
69     {"02544E052F29BBA79C81243EC63B43B6CD85B185461928E65BFF501346C62A75", 33},
70     {"04614470DDF4939091F5EC4A13C92A9EAAACF07CA5C3F713E792E2D21CD24075", 21},
71     // Hash for content: |kFileContent2|.
72     {"82E0B92772BC0DA59AAB0B9231AA006FB37B4F99EC3E853C5A62786A1C7215BD", 4},
73     {"9000000000000000000000000000000000000000000000000000000000000009", 4},
74     {"94F7BDF53CDFDE7AA5E5C90BCDA6793B7377CE39E2591ABC758EBAE8072A275C", 12},
75     // Hash for content: |kFileContent1|.
76     {"BD283E41A3672B6BDAA574F8BD7176F8BCA95BD81383CDE32AA6D78B1DB0E371", 26},
77 };
78 
79 // Messages are logged to a vector for testing.
80 class LoggingTest : public testing::Test {
81  public:
82   LoggingOverride logger_;
83 };
84 
85 }  // namespace
86 
TEST(MatcherUtilTest,IsKnownFileByDigest)87 TEST(MatcherUtilTest, IsKnownFileByDigest) {
88   base::ScopedTempDir temp_dir;
89   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
90   base::FilePath file_path1(temp_dir.GetPath().Append(kFileName1));
91   base::FilePath file_path2(temp_dir.GetPath().Append(kFileName2));
92   base::FilePath file_path3(temp_dir.GetPath().Append(kFileName3));
93 
94   CreateFileWithContent(file_path1, kFileContent, sizeof(kFileContent));
95   CreateFileWithRepeatedContent(file_path2, kFileContent, sizeof(kFileContent),
96                                 2);
97 
98   std::unique_ptr<SignatureMatcher> signature_matcher =
99       std::make_unique<SignatureMatcher>();
100   ASSERT_TRUE(signature_matcher);
101 
102   // Hash: BD283E41A3672B6BDAA574F8BD7176F8BCA95BD81383CDE32AA6D78B1DB0E371.
103   EXPECT_TRUE(IsKnownFileByDigest(file_path1, signature_matcher.get(),
104                                   kKnownContentDigests,
105                                   base::size(kKnownContentDigests)));
106   // Hash: not present.
107   EXPECT_FALSE(IsKnownFileByDigest(file_path2, signature_matcher.get(),
108                                    kKnownContentDigests,
109                                    base::size(kKnownContentDigests)));
110   // The file doesn't exist.
111   EXPECT_FALSE(IsKnownFileByDigest(file_path3, signature_matcher.get(),
112                                    kKnownContentDigests,
113                                    base::size(kKnownContentDigests)));
114 }
115 
TEST(MatcherUtilTest,IsKnownFileByDigestInfo)116 TEST(MatcherUtilTest, IsKnownFileByDigestInfo) {
117   base::ScopedTempDir temp_dir;
118   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
119   base::FilePath file_path1(temp_dir.GetPath().Append(kFileName1));
120   base::FilePath file_path2(temp_dir.GetPath().Append(kFileName2));
121   base::FilePath file_path3(temp_dir.GetPath().Append(kFileName3));
122   base::FilePath file_path4(temp_dir.GetPath().Append(kFileName4));
123 
124   CreateFileWithContent(file_path1, kFileContent1, sizeof(kFileContent1));
125   CreateFileWithContent(file_path2, kFileContent2, sizeof(kFileContent2));
126   CreateFileWithContent(file_path3, kFileContent3, sizeof(kFileContent3));
127 
128   std::unique_ptr<SignatureMatcher> signature_matcher =
129       std::make_unique<SignatureMatcher>();
130 
131   // Search BD283E41A3672B6BDAA574F8BD7176F8BCA95BD81383CDE32AA6D78B1DB0E371.
132   EXPECT_TRUE(IsKnownFileByDigestInfo(file_path1, signature_matcher.get(),
133                                       kFileContentDigestInfos,
134                                       base::size(kFileContentDigestInfos)));
135   // Search 82E0B92772BC0DA59AAB0B9231AA006FB37B4F99EC3E853C5A62786A1C7215BD.
136   EXPECT_TRUE(IsKnownFileByDigestInfo(file_path2, signature_matcher.get(),
137                                       kFileContentDigestInfos,
138                                       base::size(kFileContentDigestInfos)));
139 
140   // Replace the content of file_path2 with a content of the same size, it
141   // must no longer match.
142   ASSERT_EQ(sizeof(kFileContent2), sizeof(kFileContent4));
143   CreateFileWithContent(file_path2, kFileContent4, sizeof(kFileContent4));
144   EXPECT_FALSE(IsKnownFileByDigestInfo(file_path2, signature_matcher.get(),
145                                        kFileContentDigestInfos,
146                                        base::size(kFileContentDigestInfos)));
147 
148   // The digest of |file_path3| is not in the array.
149   EXPECT_FALSE(IsKnownFileByDigestInfo(file_path3, signature_matcher.get(),
150                                        kFileContentDigestInfos,
151                                        base::size(kFileContentDigestInfos)));
152   // The |file_path4| doesn't exist.
153   EXPECT_FALSE(IsKnownFileByDigestInfo(file_path4, signature_matcher.get(),
154                                        kFileContentDigestInfos,
155                                        base::size(kFileContentDigestInfos)));
156 }
157 
TEST(MatcherUtilTest,IsKnownFileByOriginalFilename)158 TEST(MatcherUtilTest, IsKnownFileByOriginalFilename) {
159   base::ScopedTempDir temp_dir;
160   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
161 
162   TestSignatureMatcher signature_matcher;
163 
164   const base::FilePath normalized_temp_dir_path =
165       NormalizePath(temp_dir.GetPath());
166 
167   // A non-existing file should not be recognized.
168   base::FilePath nonexistent_file_path(
169       normalized_temp_dir_path.Append(kFileName1));
170   EXPECT_FALSE(IsKnownFileByOriginalFilename(
171       nonexistent_file_path, &signature_matcher, kKnownOriginalFilenames,
172       base::size(kKnownOriginalFilenames)));
173 
174   // An existing file without version information should not be recognized.
175   base::FilePath file_path2(normalized_temp_dir_path.Append(kFileName2));
176   CreateFileWithContent(file_path2, kFileContent, sizeof(kFileContent));
177   EXPECT_FALSE(IsKnownFileByOriginalFilename(
178       file_path2, &signature_matcher, kKnownOriginalFilenames,
179       base::size(kKnownOriginalFilenames)));
180 
181   // A file with version information but not in the array should not be
182   // recognized.
183   base::FilePath file_path3(normalized_temp_dir_path.Append(kFileName3));
184 
185   VersionInformation goodware_information = {};
186   goodware_information.original_filename = kUnknownOriginalFilename;
187   signature_matcher.MatchVersionInformation(file_path3, goodware_information);
188 
189   CreateFileWithContent(file_path3, kFileContent, sizeof(kFileContent));
190   EXPECT_FALSE(IsKnownFileByOriginalFilename(
191       file_path3, &signature_matcher, kKnownOriginalFilenames,
192       base::size(kKnownOriginalFilenames)));
193 
194   // A file with version information present in the array should be recognized.
195   base::FilePath file_path4(normalized_temp_dir_path.Append(kFileName4));
196 
197   VersionInformation badware_information = {};
198   badware_information.original_filename = kKnownOriginalFilename;
199   signature_matcher.MatchVersionInformation(file_path4, badware_information);
200 
201   CreateFileWithContent(file_path4, kFileContent, sizeof(kFileContent));
202   EXPECT_TRUE(IsKnownFileByOriginalFilename(
203       file_path4, &signature_matcher, kKnownOriginalFilenames,
204       base::size(kKnownOriginalFilenames)));
205 }
206 
TEST(MatcherUtilTest,IsKnownFileByCompanyName)207 TEST(MatcherUtilTest, IsKnownFileByCompanyName) {
208   base::ScopedTempDir temp_dir;
209   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
210 
211   const base::FilePath normalized_temp_dir_path =
212       NormalizePath(temp_dir.GetPath());
213 
214   TestSignatureMatcher signature_matcher;
215 
216   // A non-existing file should not be recognized.
217   base::FilePath nonexistent_file_path(
218       normalized_temp_dir_path.Append(kFileName1));
219   EXPECT_FALSE(IsKnownFileByCompanyName(nonexistent_file_path,
220                                         &signature_matcher, kKnownCompanyNames,
221                                         base::size(kKnownCompanyNames)));
222 
223   // An existing file without version information should not be recognized.
224   base::FilePath file_path2(normalized_temp_dir_path.Append(kFileName2));
225   CreateFileWithContent(file_path2, kFileContent, sizeof(kFileContent));
226   EXPECT_FALSE(IsKnownFileByCompanyName(file_path2, &signature_matcher,
227                                         kKnownCompanyNames,
228                                         base::size(kKnownCompanyNames)));
229 
230   // A file with version information but not in the array should not be
231   // recognized.
232   base::FilePath file_path3(normalized_temp_dir_path.Append(kFileName3));
233 
234   VersionInformation goodware_information = {};
235   goodware_information.company_name = kUnknownCompanyName;
236   signature_matcher.MatchVersionInformation(file_path3, goodware_information);
237 
238   CreateFileWithContent(file_path3, kFileContent, sizeof(kFileContent));
239   EXPECT_FALSE(IsKnownFileByCompanyName(file_path3, &signature_matcher,
240                                         kKnownCompanyNames,
241                                         base::size(kKnownCompanyNames)));
242 
243   // A file with version information present in the array should be recognized.
244   base::FilePath file_path4(normalized_temp_dir_path.Append(kFileName4));
245 
246   VersionInformation badware_information = {};
247   badware_information.company_name = kKnownCompanyName;
248   signature_matcher.MatchVersionInformation(file_path4, badware_information);
249 
250   CreateFileWithContent(file_path4, kFileContent, sizeof(kFileContent));
251   EXPECT_TRUE(IsKnownFileByCompanyName(file_path4, &signature_matcher,
252                                        kKnownCompanyNames,
253                                        base::size(kKnownCompanyNames)));
254 }
255 
256 }  // namespace chrome_cleaner
257