1 // Copyright 2014 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.
5 #include "chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win.h"
10 #include "base/i18n/case_conversion.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/strings/string16.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/win/registry.h"
17 #include "chrome/browser/install_verification/win/module_info.h"
18 #include "chrome/browser/install_verification/win/module_verification_common.h"
19 #include "chrome/browser/net/service_providers_win.h"
20 #include "chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.h"
21 #include "chrome/browser/safe_browsing/path_sanitizer.h"
22 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
23 #include "chrome/common/safe_browsing/csd.pb.h"
24 #include "chrome_elf/chrome_elf_constants.h"
25 #include "components/variations/variations_associated_data.h"
27 namespace safe_browsing
{
31 const REGSAM kKeyReadNoNotify
= (KEY_READ
) & ~(KEY_NOTIFY
);
33 // The modules on which we will run VerifyModule.
34 const wchar_t* const kModulesToVerify
[] = {
40 // The registry keys to collect data from.
41 const RegistryKeyInfo kRegKeysToCollect
[] = {
42 {HKEY_CURRENT_USER
, L
"Software\\CSAStats"},
45 // Helper function for expanding all environment variables in |path|.
46 std::wstring
ExpandEnvironmentVariables(const std::wstring
& path
) {
47 static const DWORD kMaxBuffer
= 32 * 1024; // Max according to MSDN.
48 std::wstring path_expanded
;
49 DWORD path_len
= MAX_PATH
;
51 DWORD result
= ExpandEnvironmentStrings(
52 path
.c_str(), base::WriteInto(&path_expanded
, path_len
), path_len
);
54 // Failed to expand variables. Return the original string.
58 if (result
<= path_len
)
59 return path_expanded
.substr(0, result
- 1);
61 } while (path_len
< kMaxBuffer
);
66 // Helper function to convert HKEYs to strings.
67 base::string16
HKEYToString(HKEY key
) {
68 DCHECK_EQ(HKEY_CURRENT_USER
, key
);
69 return L
"HKEY_CURRENT_USER";
72 // Helper function to extract data from a single registry key (and all of its
73 // subkeys), recursively.
74 void CollectRegistryDataForKey(
75 const base::win::RegKey
& key
,
76 ClientIncidentReport_EnvironmentData_OS_RegistryKey
* key_pb
) {
77 using RegistryValueProto
=
78 ClientIncidentReport_EnvironmentData_OS_RegistryValue
;
79 using RegistryKeyProto
= ClientIncidentReport_EnvironmentData_OS_RegistryKey
;
81 DWORD num_subkeys
= 0;
82 DWORD max_subkey_name_len
= 0;
84 DWORD max_value_name_len
= 0;
85 DWORD max_value_len
= 0;
86 LONG result
= RegQueryInfoKey(
87 key
.Handle(), NULL
, NULL
, NULL
, &num_subkeys
, &max_subkey_name_len
, NULL
,
88 &num_values
, &max_value_name_len
, &max_value_len
, NULL
, NULL
);
90 DWORD max_name_len
= std::max(max_subkey_name_len
, max_value_name_len
) + 1;
91 std::vector
<wchar_t> name_buffer(max_name_len
);
93 if (num_values
!= 0) {
94 std::vector
<uint8
> value_buffer(max_value_len
!= 0 ? max_value_len
: 1);
96 DWORD value_type
= REG_NONE
;
99 std::wstring name_str
;
100 RegistryValueProto
* registry_value
;
101 for (DWORD i
= 0; i
< num_values
;) {
102 name_size
= static_cast<DWORD
>(name_buffer
.size());
103 value_size
= static_cast<DWORD
>(value_buffer
.size());
104 result
= ::RegEnumValue(key
.Handle(), i
, &name_buffer
[0], &name_size
,
105 NULL
, &value_type
, &value_buffer
[0], &value_size
);
107 case ERROR_NO_MORE_ITEMS
:
111 registry_value
= key_pb
->add_value();
113 name_str
.assign(&name_buffer
[0], name_size
);
114 registry_value
->set_name(base::WideToUTF8(name_str
));
117 registry_value
->set_type(value_type
);
118 registry_value
->set_data(&value_buffer
[0], value_size
);
122 case ERROR_MORE_DATA
:
123 if (value_size
> value_buffer
.size())
124 value_buffer
.resize(value_size
);
125 // |name_size| does not include space for the terminating NULL.
126 if (name_size
+ 1 > name_buffer
.size())
127 name_buffer
.resize(name_size
+ 1);
136 if (num_subkeys
!= 0) {
138 std::vector
<std::wstring
> subkey_names
;
140 // Get the names of them.
141 for (DWORD i
= 0; i
< num_subkeys
;) {
142 name_size
= static_cast<DWORD
>(name_buffer
.size());
143 result
= RegEnumKeyEx(key
.Handle(), i
, &name_buffer
[0], &name_size
, NULL
,
146 case ERROR_NO_MORE_ITEMS
:
150 subkey_names
.push_back(std::wstring(&name_buffer
[0], name_size
));
153 case ERROR_MORE_DATA
:
154 name_buffer
.resize(name_size
+ 1);
161 // Extract the data (if possible).
162 base::win::RegKey subkey
;
163 for (const auto& subkey_name
: subkey_names
) {
164 if (subkey
.Open(key
.Handle(), subkey_name
.c_str(), kKeyReadNoNotify
) ==
166 RegistryKeyProto
* subkey_pb
= key_pb
->add_key();
167 subkey_pb
->set_name(base::WideToUTF8(subkey_name
));
168 CollectRegistryDataForKey(subkey
, subkey_pb
);
176 bool CollectDlls(ClientIncidentReport_EnvironmentData_Process
* process
) {
177 // Retrieve the module list.
178 std::set
<ModuleInfo
> loaded_modules
;
179 if (!GetLoadedModules(&loaded_modules
))
182 // Sanitize path of each module and add it to the incident report along with
184 PathSanitizer path_sanitizer
;
185 scoped_refptr
<BinaryFeatureExtractor
> feature_extractor(
186 new BinaryFeatureExtractor());
187 for (const auto& module
: loaded_modules
) {
188 base::FilePath
dll_path(module
.name
);
189 base::FilePath
sanitized_path(dll_path
);
190 path_sanitizer
.StripHomeDirectory(&sanitized_path
);
192 ClientIncidentReport_EnvironmentData_Process_Dll
* dll
= process
->add_dll();
194 base::WideToUTF8(base::i18n::ToLower(sanitized_path
.value())));
195 dll
->set_base_address(module
.base_address
);
196 dll
->set_length(module
.size
);
197 // TODO(grt): Consider skipping this for valid system modules.
198 if (!feature_extractor
->ExtractImageFeatures(
200 BinaryFeatureExtractor::kOmitExports
,
201 dll
->mutable_image_headers(),
202 nullptr /* signed_data */)) {
203 dll
->clear_image_headers();
210 void RecordLspFeature(ClientIncidentReport_EnvironmentData_Process
* process
) {
211 WinsockLayeredServiceProviderList lsp_list
;
212 GetWinsockLayeredServiceProviders(&lsp_list
);
214 // For each LSP, we extract and sanitize the path.
215 PathSanitizer path_sanitizer
;
216 std::set
<std::wstring
> lsp_paths
;
217 for (size_t i
= 0; i
< lsp_list
.size(); ++i
) {
218 base::FilePath
lsp_path(ExpandEnvironmentVariables(lsp_list
[i
].path
));
219 path_sanitizer
.StripHomeDirectory(&lsp_path
);
220 lsp_paths
.insert(base::i18n::ToLower(lsp_path
.value()));
223 // Look for a match between LSPs and loaded dlls.
224 for (int i
= 0; i
< process
->dll_size(); ++i
) {
225 if (lsp_paths
.count(base::UTF8ToWide(process
->dll(i
).path()))) {
226 process
->mutable_dll(i
)
227 ->add_feature(ClientIncidentReport_EnvironmentData_Process_Dll::LSP
);
232 void CollectDllBlacklistData(
233 ClientIncidentReport_EnvironmentData_Process
* process
) {
234 PathSanitizer path_sanitizer
;
235 base::win::RegistryValueIterator
iter(HKEY_CURRENT_USER
,
236 blacklist::kRegistryFinchListPath
);
237 for (; iter
.Valid(); ++iter
) {
238 base::FilePath
dll_name(iter
.Value());
239 path_sanitizer
.StripHomeDirectory(&dll_name
);
240 process
->add_blacklisted_dll(dll_name
.AsUTF8Unsafe());
244 void CollectModuleVerificationData(
245 const wchar_t* const modules_to_verify
[],
246 size_t num_modules_to_verify
,
247 ClientIncidentReport_EnvironmentData_Process
* process
) {
249 using ModuleState
= ClientIncidentReport_EnvironmentData_Process_ModuleState
;
251 for (size_t i
= 0; i
< num_modules_to_verify
; ++i
) {
252 scoped_ptr
<ModuleState
> module_state(new ModuleState());
254 int num_bytes_different
= 0;
255 bool scan_complete
= VerifyModule(modules_to_verify
[i
],
257 &num_bytes_different
);
259 if (module_state
->modified_state() == ModuleState::MODULE_STATE_UNMODIFIED
)
262 if (module_state
->modified_state() == ModuleState::MODULE_STATE_MODIFIED
) {
263 UMA_HISTOGRAM_COUNTS_10000(
264 "ModuleIntegrityVerification.BytesModified.WithoutByteSet",
265 num_bytes_different
);
268 if (!scan_complete
) {
269 UMA_HISTOGRAM_ENUMERATION(
270 "ModuleIntegrityVerification.RelocationsUnordered", i
,
271 num_modules_to_verify
);
274 process
->mutable_module_state()->AddAllocated(module_state
.release());
279 void CollectRegistryData(
280 const RegistryKeyInfo
* keys_to_collect
,
281 size_t num_keys_to_collect
,
282 google::protobuf::RepeatedPtrField
<
283 ClientIncidentReport_EnvironmentData_OS_RegistryKey
>* key_data
) {
284 using RegistryKeyProto
= ClientIncidentReport_EnvironmentData_OS_RegistryKey
;
285 for (size_t i
= 0; i
< num_keys_to_collect
; ++i
) {
286 const RegistryKeyInfo
& key_info
= keys_to_collect
[i
];
287 base::win::RegKey
reg_key(key_info
.rootkey
, key_info
.subkey
,
289 if (reg_key
.Valid()) {
290 RegistryKeyProto
* regkey_pb
= key_data
->Add();
291 std::wstring rootkey_name
= HKEYToString(key_info
.rootkey
);
292 rootkey_name
+= L
"\\";
293 rootkey_name
+= key_info
.subkey
;
294 regkey_pb
->set_name(base::WideToUTF8(rootkey_name
));
295 CollectRegistryDataForKey(reg_key
, regkey_pb
);
300 void CollectPlatformProcessData(
301 ClientIncidentReport_EnvironmentData_Process
* process
) {
302 CollectDlls(process
);
303 RecordLspFeature(process
);
304 CollectDllBlacklistData(process
);
305 CollectModuleVerificationData(
306 kModulesToVerify
, arraysize(kModulesToVerify
), process
);
309 void CollectPlatformOSData(ClientIncidentReport_EnvironmentData_OS
* os_data
) {
310 const std::string reg_data_param_value
= variations::GetVariationParamValue(
311 "SafeBrowsingIncidentReportingService", "collect_reg_data");
312 if (reg_data_param_value
== "true") {
313 CollectRegistryData(kRegKeysToCollect
, arraysize(kRegKeysToCollect
),
314 os_data
->mutable_registry_key());
317 } // namespace safe_browsing