1 : // Copyright 2015 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/kasko/api/internal/crash_key_registration.h"
16 :
17 : #include <psapi.h>
18 :
19 : #include <string.h>
20 :
21 : #include <cstdint>
22 :
23 : #include "base/logging.h"
24 : #include "base/process/process_handle.h"
25 : #include "base/strings/string16.h"
26 : #include "syzygy/common/process_utils.h"
27 : #include "syzygy/kasko/api/crash_key.h"
28 :
29 : // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
30 : extern "C" IMAGE_DOS_HEADER __ImageBase;
31 :
32 : namespace kasko {
33 : namespace api {
34 : namespace internal {
35 :
36 : namespace {
37 :
38 : // Used to store the CrashKey array address and size in the client process.
39 : struct CrashKeyStorage {
40 : const CrashKey* crash_keys;
41 : size_t crash_key_count;
42 : } g_crash_key_storage = {nullptr, 0};
43 :
44 : // Returns the image path of |module| in |process|.
45 E : base::string16 GetModulePath(HANDLE process, HMODULE module) {
46 : base::char16 path_buffer[MAX_PATH];
47 : DWORD bytes_read = ::GetModuleFileNameEx(process, module, path_buffer,
48 E : arraysize(path_buffer));
49 E : if (bytes_read == 0)
50 i : DPLOG(ERROR) << "GetModuleFileNameEx";
51 E : else if (bytes_read >= arraysize(path_buffer))
52 i : return base::string16();
53 :
54 E : return path_buffer;
55 E : }
56 :
57 : // Returns the image path of the current module.
58 E : base::string16 GetCurrentModulePath() {
59 : return GetModulePath(base::GetCurrentProcessHandle(),
60 E : reinterpret_cast<HMODULE>(&__ImageBase));
61 E : }
62 :
63 : // Returns the linker timestamp of the current module.
64 E : DWORD GetCurrentModuleTimestamp() {
65 : uintptr_t image_dos_header_address =
66 E : reinterpret_cast<uintptr_t>(&__ImageBase);
67 : IMAGE_DOS_HEADER* image_dos_header =
68 E : reinterpret_cast<IMAGE_DOS_HEADER*>(image_dos_header_address);
69 : IMAGE_NT_HEADERS* image_nt_header = reinterpret_cast<IMAGE_NT_HEADERS*>(
70 E : image_dos_header_address + image_dos_header->e_lfanew);
71 E : return image_nt_header->FileHeader.TimeDateStamp;
72 E : }
73 :
74 : // Returns the size of |module| in |process|.
75 E : DWORD GetModuleSize(HANDLE process, HMODULE module) {
76 E : MODULEINFO module_info = {0};
77 : if (!::GetModuleInformation(process, module, &module_info,
78 E : sizeof(module_info))) {
79 i : DPLOG(ERROR) << "GetModuleInformation";
80 i : return 0;
81 : }
82 E : DCHECK_NE(0u, module_info.SizeOfImage);
83 E : return module_info.SizeOfImage;
84 E : }
85 :
86 : // Returns the size of the current module.
87 E : DWORD GetCurrentModuleSize() {
88 : return GetModuleSize(base::GetCurrentProcessHandle(),
89 E : reinterpret_cast<HMODULE>(&__ImageBase));
90 E : }
91 :
92 : // Reads a value of type T from |address| in |process| into |value|. Returns
93 : // true if successful.
94 : template <typename T>
95 E : bool ReadValueFromOtherProcess(HANDLE process, uintptr_t address, T* value) {
96 E : DWORD bytes_count = 0;
97 : if (!::ReadProcessMemory(process, reinterpret_cast<void*>(address), value,
98 E : sizeof(T), &bytes_count)) {
99 i : DPLOG(ERROR) << "ReadProcessMemory";
100 i : return false;
101 : }
102 E : if (bytes_count != sizeof(T))
103 i : return false;
104 E : return true;
105 E : }
106 :
107 : // Reads the crash keys from another instance of the current module image,
108 : // loaded into another process.
109 : bool ReadCrashKeysFromProcessModule(HANDLE process,
110 : HMODULE module,
111 E : std::vector<CrashKey>* crash_keys) {
112 : // Calculate the offset of g_crash_key_storage from our base address. It will
113 : // be the same in the other instance.
114 : ptrdiff_t storage_offset = reinterpret_cast<uintptr_t>(&g_crash_key_storage) -
115 E : reinterpret_cast<uintptr_t>(&__ImageBase);
116 :
117 : // Calculate the virtual address of g_crash_key_storage in the other instance.
118 : uintptr_t storage_address =
119 E : storage_offset + reinterpret_cast<uintptr_t>(module);
120 :
121 : // Read the CrashKeyStorage structure, which contains the address and size of
122 : // a CrashKey array in the other process.
123 E : CrashKeyStorage crash_key_storage = {0};
124 E : if (!ReadValueFromOtherProcess(process, storage_address, &crash_key_storage))
125 i : return false;
126 :
127 : // Prepare a buffer and read the CrashKey array into it.
128 E : crash_keys->resize(crash_key_storage.crash_key_count);
129 E : DWORD bytes_count = 0;
130 : if (!::ReadProcessMemory(
131 : process, crash_key_storage.crash_keys, crash_keys->data(),
132 E : sizeof(CrashKey) * crash_keys->size(), &bytes_count)) {
133 i : DPLOG(ERROR) << "ReadProcessMemory";
134 i : crash_keys->clear();
135 i : return false;
136 : }
137 E : if (bytes_count != sizeof(CrashKey) * crash_keys->size()) {
138 i : crash_keys->clear();
139 i : return false;
140 : }
141 :
142 : // Validate the CrashKey array that we read. If any of the names or values is
143 : // not properly terminated, fail the entire operation.
144 E : for (const auto& crash_key : *crash_keys) {
145 : if (::wcsnlen(crash_key.name, CrashKey::kNameMaxLength) ==
146 E : CrashKey::kNameMaxLength) {
147 i : crash_keys->clear();
148 i : return false;
149 : }
150 : if (::wcsnlen(crash_key.value, CrashKey::kValueMaxLength) ==
151 E : CrashKey::kValueMaxLength) {
152 i : crash_keys->clear();
153 i : return false;
154 : }
155 E : }
156 E : return true;
157 E : }
158 :
159 : // Retrieves the linker timestamp from a module loaded into another process.
160 E : DWORD GetOtherModuleTimestamp(HANDLE process, HANDLE module) {
161 : // Read the relative address of the NT_IMAGE_HEADERS structure from the
162 : // IMAGE_DOS_HEADER (which is located at the module's base address).
163 E : decltype(IMAGE_DOS_HEADER::e_lfanew) nt_header_offset = 0;
164 : if (!ReadValueFromOtherProcess(process,
165 : reinterpret_cast<uintptr_t>(module) +
166 : offsetof(IMAGE_DOS_HEADER, e_lfanew),
167 E : &nt_header_offset)) {
168 i : return 0;
169 : }
170 E : if (nt_header_offset == 0)
171 i : return 0;
172 :
173 : // Calculate the address of the timestamp, which is stored in
174 : // image_nt_header.FileHeader.TimeDateStamp.
175 : uintptr_t image_nt_header_address =
176 E : reinterpret_cast<uintptr_t>(module) + nt_header_offset;
177 : uintptr_t image_file_header_address =
178 E : image_nt_header_address + offsetof(IMAGE_NT_HEADERS, FileHeader);
179 : uintptr_t time_date_stamp_address =
180 E : image_file_header_address + offsetof(IMAGE_FILE_HEADER, TimeDateStamp);
181 :
182 : // Read the value of the timestamp.
183 E : decltype(IMAGE_FILE_HEADER::TimeDateStamp) time_date_stamp = 0;
184 : if (!ReadValueFromOtherProcess(process, time_date_stamp_address,
185 E : &time_date_stamp)) {
186 i : return 0;
187 : }
188 :
189 E : return time_date_stamp;
190 E : }
191 :
192 : // Reads a fingerprint of the current module, and provides a method to compare
193 : // that fingerprint to a module in another process.
194 : class CurrentModuleMatcher {
195 : public:
196 : CurrentModuleMatcher()
197 : : path_(GetCurrentModulePath()),
198 : timestamp_(GetCurrentModuleTimestamp()),
199 E : size_(GetCurrentModuleSize()) {}
200 :
201 : // Returns true if the specifed |module| in |process| appears to be the same
202 : // image as the current module.
203 E : bool Matches(HANDLE process, HMODULE module) {
204 : // Give up now if we failed to read any of our own identifying values. As a
205 : // result, we know we will fail later if we fail to read one of the other
206 : // module's values.
207 E : if (path_.empty() || timestamp_ == 0 || size_ == 0)
208 i : return false;
209 :
210 E : base::string16 other_path = GetModulePath(process, module);
211 E : if (other_path != path_)
212 E : return false;
213 :
214 E : DWORD other_size = GetModuleSize(process, module);
215 E : if (other_size != size_)
216 i : return false;
217 :
218 E : DWORD other_timestamp = GetOtherModuleTimestamp(process, module);
219 E : if (other_timestamp != timestamp_)
220 i : return false;
221 :
222 E : return true;
223 E : }
224 :
225 : private:
226 : base::string16 path_;
227 : DWORD timestamp_;
228 : DWORD size_;
229 : };
230 :
231 : } // namespace
232 :
233 E : void RegisterCrashKeys(const CrashKey* crash_keys, size_t count) {
234 E : DCHECK(!g_crash_key_storage.crash_keys);
235 E : g_crash_key_storage.crash_keys = crash_keys;
236 E : DCHECK_EQ(0u, g_crash_key_storage.crash_key_count);
237 E : g_crash_key_storage.crash_key_count = count;
238 E : }
239 :
240 : bool ReadCrashKeysFromProcess(HANDLE process,
241 E : std::vector<CrashKey>* crash_keys) {
242 E : ::common::ModuleVector modules;
243 E : ::common::GetProcessModules(process, &modules);
244 :
245 E : CurrentModuleMatcher module_matcher;
246 :
247 E : for (const auto& module : modules) {
248 E : if (module_matcher.Matches(process, module))
249 E : return ReadCrashKeysFromProcessModule(process, module, crash_keys);
250 E : }
251 i : return false;
252 E : }
253 :
254 : } // namespace internal
255 : } // namespace api
256 : } // namespace kasko
|