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 m : extern "C" IMAGE_DOS_HEADER __ImageBase;
31 :
32 m : namespace kasko {
33 m : namespace api {
34 m : namespace internal {
35 :
36 m : namespace {
37 :
38 : // Used to store the CrashKey array address and size in the client process.
39 m : struct CrashKeyStorage {
40 m : const CrashKey* crash_keys;
41 m : size_t crash_key_count;
42 m : } g_crash_key_storage = {nullptr, 0};
43 :
44 : // Returns the image path of |module| in |process|.
45 m : base::string16 GetModulePath(HANDLE process, HMODULE module) {
46 m : base::char16 path_buffer[MAX_PATH];
47 m : DWORD bytes_read = ::GetModuleFileNameEx(process, module, path_buffer,
48 m : arraysize(path_buffer));
49 m : if (bytes_read == 0)
50 m : DPLOG(ERROR) << "GetModuleFileNameEx";
51 m : else if (bytes_read >= arraysize(path_buffer))
52 m : return base::string16();
53 :
54 m : return path_buffer;
55 m : }
56 :
57 : // Returns the image path of the current module.
58 m : base::string16 GetCurrentModulePath() {
59 m : return GetModulePath(base::GetCurrentProcessHandle(),
60 m : reinterpret_cast<HMODULE>(&__ImageBase));
61 m : }
62 :
63 : // Returns the linker timestamp of the current module.
64 m : DWORD GetCurrentModuleTimestamp() {
65 m : uintptr_t image_dos_header_address =
66 m : reinterpret_cast<uintptr_t>(&__ImageBase);
67 m : IMAGE_DOS_HEADER* image_dos_header =
68 m : reinterpret_cast<IMAGE_DOS_HEADER*>(image_dos_header_address);
69 m : IMAGE_NT_HEADERS* image_nt_header = reinterpret_cast<IMAGE_NT_HEADERS*>(
70 m : image_dos_header_address + image_dos_header->e_lfanew);
71 m : return image_nt_header->FileHeader.TimeDateStamp;
72 m : }
73 :
74 : // Returns the size of |module| in |process|.
75 m : DWORD GetModuleSize(HANDLE process, HMODULE module) {
76 m : MODULEINFO module_info = {0};
77 m : if (!::GetModuleInformation(process, module, &module_info,
78 m : sizeof(module_info))) {
79 m : DPLOG(ERROR) << "GetModuleInformation";
80 m : return 0;
81 m : }
82 m : DCHECK_NE(0u, module_info.SizeOfImage);
83 m : return module_info.SizeOfImage;
84 m : }
85 :
86 : // Returns the size of the current module.
87 m : DWORD GetCurrentModuleSize() {
88 m : return GetModuleSize(base::GetCurrentProcessHandle(),
89 m : reinterpret_cast<HMODULE>(&__ImageBase));
90 m : }
91 :
92 : // Reads a value of type T from |address| in |process| into |value|. Returns
93 : // true if successful.
94 m : template <typename T>
95 m : bool ReadValueFromOtherProcess(HANDLE process, uintptr_t address, T* value) {
96 m : DWORD bytes_count = 0;
97 m : if (!::ReadProcessMemory(process, reinterpret_cast<void*>(address), value,
98 m : sizeof(T), &bytes_count)) {
99 m : DPLOG(ERROR) << "ReadProcessMemory";
100 m : return false;
101 m : }
102 m : if (bytes_count != sizeof(T))
103 m : return false;
104 m : return true;
105 m : }
106 :
107 : // Reads the crash keys from another instance of the current module image,
108 : // loaded into another process.
109 m : bool ReadCrashKeysFromProcessModule(HANDLE process,
110 m : HMODULE module,
111 m : 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 m : ptrdiff_t storage_offset = reinterpret_cast<uintptr_t>(&g_crash_key_storage) -
115 m : reinterpret_cast<uintptr_t>(&__ImageBase);
116 :
117 : // Calculate the virtual address of g_crash_key_storage in the other instance.
118 m : uintptr_t storage_address =
119 m : 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 m : CrashKeyStorage crash_key_storage = {0};
124 m : if (!ReadValueFromOtherProcess(process, storage_address, &crash_key_storage))
125 m : return false;
126 :
127 : // Prepare a buffer and read the CrashKey array into it.
128 m : crash_keys->resize(crash_key_storage.crash_key_count);
129 m : DWORD bytes_count = 0;
130 m : if (!::ReadProcessMemory(
131 m : process, crash_key_storage.crash_keys, crash_keys->data(),
132 m : sizeof(CrashKey) * crash_keys->size(), &bytes_count)) {
133 m : DPLOG(ERROR) << "ReadProcessMemory";
134 m : crash_keys->clear();
135 m : return false;
136 m : }
137 m : if (bytes_count != sizeof(CrashKey) * crash_keys->size()) {
138 m : crash_keys->clear();
139 m : return false;
140 m : }
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 m : for (const auto& crash_key : *crash_keys) {
145 m : if (::wcsnlen(crash_key.name, CrashKey::kNameMaxLength) ==
146 m : CrashKey::kNameMaxLength) {
147 m : crash_keys->clear();
148 m : return false;
149 m : }
150 m : if (::wcsnlen(crash_key.value, CrashKey::kValueMaxLength) ==
151 m : CrashKey::kValueMaxLength) {
152 m : crash_keys->clear();
153 m : return false;
154 m : }
155 m : }
156 m : return true;
157 m : }
158 :
159 : // Retrieves the linker timestamp from a module loaded into another process.
160 m : 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 m : decltype(IMAGE_DOS_HEADER::e_lfanew) nt_header_offset = 0;
164 m : if (!ReadValueFromOtherProcess(process,
165 m : reinterpret_cast<uintptr_t>(module) +
166 m : offsetof(IMAGE_DOS_HEADER, e_lfanew),
167 m : &nt_header_offset)) {
168 m : return 0;
169 m : }
170 m : if (nt_header_offset == 0)
171 m : return 0;
172 :
173 : // Calculate the address of the timestamp, which is stored in
174 : // image_nt_header.FileHeader.TimeDateStamp.
175 m : uintptr_t image_nt_header_address =
176 m : reinterpret_cast<uintptr_t>(module) + nt_header_offset;
177 m : uintptr_t image_file_header_address =
178 m : image_nt_header_address + offsetof(IMAGE_NT_HEADERS, FileHeader);
179 m : uintptr_t time_date_stamp_address =
180 m : image_file_header_address + offsetof(IMAGE_FILE_HEADER, TimeDateStamp);
181 :
182 : // Read the value of the timestamp.
183 m : decltype(IMAGE_FILE_HEADER::TimeDateStamp) time_date_stamp = 0;
184 m : if (!ReadValueFromOtherProcess(process, time_date_stamp_address,
185 m : &time_date_stamp)) {
186 m : return 0;
187 m : }
188 :
189 m : return time_date_stamp;
190 m : }
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 m : class CurrentModuleMatcher {
195 m : public:
196 m : CurrentModuleMatcher()
197 m : : path_(GetCurrentModulePath()),
198 m : timestamp_(GetCurrentModuleTimestamp()),
199 m : size_(GetCurrentModuleSize()) {}
200 :
201 : // Returns true if the specifed |module| in |process| appears to be the same
202 : // image as the current module.
203 m : 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 m : if (path_.empty() || timestamp_ == 0 || size_ == 0)
208 m : return false;
209 :
210 m : base::string16 other_path = GetModulePath(process, module);
211 m : if (other_path != path_)
212 m : return false;
213 :
214 m : DWORD other_size = GetModuleSize(process, module);
215 m : if (other_size != size_)
216 m : return false;
217 :
218 m : DWORD other_timestamp = GetOtherModuleTimestamp(process, module);
219 m : if (other_timestamp != timestamp_)
220 m : return false;
221 :
222 m : return true;
223 m : }
224 :
225 m : private:
226 m : base::string16 path_;
227 m : DWORD timestamp_;
228 m : DWORD size_;
229 m : };
230 :
231 m : } // namespace
232 :
233 m : void RegisterCrashKeys(const CrashKey* crash_keys, size_t count) {
234 m : DCHECK(!g_crash_key_storage.crash_keys);
235 m : g_crash_key_storage.crash_keys = crash_keys;
236 m : DCHECK_EQ(0u, g_crash_key_storage.crash_key_count);
237 m : g_crash_key_storage.crash_key_count = count;
238 m : }
239 :
240 m : bool ReadCrashKeysFromProcess(HANDLE process,
241 m : std::vector<CrashKey>* crash_keys) {
242 m : ::common::ModuleVector modules;
243 m : ::common::GetProcessModules(process, &modules);
244 :
245 m : CurrentModuleMatcher module_matcher;
246 :
247 m : for (const auto& module : modules) {
248 m : if (module_matcher.Matches(process, module))
249 m : return ReadCrashKeysFromProcessModule(process, module, crash_keys);
250 m : }
251 m : return false;
252 m : }
253 :
254 m : } // namespace internal
255 m : } // namespace api
256 m : } // namespace kasko
|