1 : // Copyright 2012 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/pe/metadata.h"
16 :
17 : #include <time.h>
18 :
19 : #include "base/command_line.h"
20 : #include "base/values.h"
21 : #include "base/json/json_reader.h"
22 : #include "base/json/string_escape.h"
23 : #include "base/strings/stringprintf.h"
24 : #include "base/strings/utf_string_conversions.h"
25 : #include "syzygy/block_graph/block_graph.h"
26 : #include "syzygy/common/defs.h"
27 : #include "syzygy/core/json_file_writer.h"
28 : #include "syzygy/pe/pe_utils.h"
29 :
30 : namespace pe {
31 :
32 : using base::DictionaryValue;
33 : using block_graph::BlockGraph;
34 : using core::RelativeAddress;
35 :
36 : namespace {
37 :
38 : // Metadata JSON keys.
39 : const char kCommandLineKey[] = "command_line";
40 : const char kCreationTimeKey[] = "creation_time";
41 : const char kToolchainVersionKey[] = "toolchain_version";
42 : const char kModuleSignatureKey[] = "module_signature";
43 :
44 : // SyzygyVersion JSON keys.
45 : const char kMajorKey[] = "major";
46 : const char kMinorKey[] = "minor";
47 : const char kBuildKey[] = "build";
48 : const char kPatchKey[] = "patch";
49 : const char kLastChangeKey[] = "last_change";
50 :
51 : // PEFile::Signature JSON keys.
52 : const char kPathKey[] = "path";
53 : const char kBaseAddressKey[] = "base_address";
54 : const char kModuleSizeKey[] = "module_size";
55 : const char kModuleTimeDateStampKey[] = "module_time_date_stamp";
56 : const char kModuleChecksumKey[] = "module_checksum";
57 :
58 E : std::string TimeToString(const base::Time& time) {
59 : // Want the output format to be consistent with what Time::FromString
60 : // accepts as input. An example follows:
61 : // Tue, 15 Nov 1994 12:45:26 GMT.
62 : char buffer[64];
63 E : time_t tt = time.ToTimeT();
64 E : struct tm timeinfo = {};
65 E : gmtime_s(&timeinfo, &tt);
66 E : strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S GMT", &timeinfo);
67 E : return std::string(buffer);
68 E : }
69 :
70 E : bool StringToTime(const std::string& string, base::Time* time) {
71 E : return base::Time::FromString(string.c_str(), time);
72 E : }
73 :
74 : // Outputs a SyzygyVersion object in JSON format as a dictionary. Does not
75 : // output a newline after the dictionary.
76 : bool OutputSyzygyVersion(const version::SyzygyVersion& version,
77 E : core::JSONFileWriter* json_file) {
78 E : DCHECK(json_file != NULL);
79 :
80 E : if (!json_file->OpenDict())
81 i : return false;
82 :
83 E : if (json_file->pretty_print()) {
84 E : std::string comment("Toolchain version: ");
85 E : comment.append(version.GetVersionString());
86 E : if (!json_file->OutputComment(comment))
87 i : return false;
88 E : }
89 :
90 : return json_file->OutputKey(kMajorKey) &&
91 : json_file->OutputInteger(version.major()) &&
92 : json_file->OutputKey(kMinorKey) &&
93 : json_file->OutputInteger(version.minor()) &&
94 : json_file->OutputKey(kBuildKey) &&
95 : json_file->OutputInteger(version.build()) &&
96 : json_file->OutputKey(kPatchKey) &&
97 : json_file->OutputInteger(version.patch()) &&
98 : json_file->OutputKey(kLastChangeKey) &&
99 : json_file->OutputString(version.last_change()) &&
100 E : json_file->CloseDict();
101 E : }
102 :
103 : // Outputs a PEFile::Signature in JSON format as a dictionary. Does not output
104 : // a newline after the dictionary.
105 : bool OutputPEFileSignature(const PEFile::Signature& signature,
106 E : core::JSONFileWriter* json_file) {
107 E : DCHECK(json_file != NULL);
108 :
109 E : std::string path;
110 E : base::WideToUTF8(signature.path.c_str(), signature.path.size(), &path);
111 E : path = base::GetQuotedJSONString(path);
112 :
113 : std::string time_stamp(
114 E : base::StringPrintf("0x%llX", signature.module_time_date_stamp));
115 E : std::string checksum(base::StringPrintf("0x%X", signature.module_checksum));
116 :
117 : return json_file->OpenDict() &&
118 : json_file->OutputKey(kPathKey) &&
119 : json_file->OutputString(signature.path) &&
120 : json_file->OutputKey(kBaseAddressKey) &&
121 : json_file->OutputInteger(signature.base_address.value()) &&
122 : json_file->OutputKey(kModuleSizeKey) &&
123 : json_file->OutputInteger(signature.module_size) &&
124 : json_file->OutputKey(kModuleTimeDateStampKey) &&
125 : json_file->OutputString(time_stamp) &&
126 : json_file->OutputKey(kModuleChecksumKey) &&
127 : json_file->OutputString(checksum) &&
128 E : json_file->CloseDict();
129 E : }
130 :
131 : // Loads a syzygy version from a JSON dictionary.
132 : bool LoadSyzygyVersion(const DictionaryValue& dictionary,
133 E : version::SyzygyVersion* version) {
134 E : DCHECK(version != NULL);
135 :
136 E : int major = 0;
137 E : int minor = 0;
138 E : int build = 0;
139 E : int patch = 0;
140 E : std::string last_change;
141 : if (!dictionary.GetInteger(kMajorKey, &major) ||
142 : !dictionary.GetInteger(kMinorKey, &minor) ||
143 : !dictionary.GetInteger(kBuildKey, &build) ||
144 : !dictionary.GetInteger(kPatchKey, &patch) ||
145 E : !dictionary.GetString(kLastChangeKey, &last_change)) {
146 i : LOG(ERROR) << "Unable to parse SyzygyVersion from JSON dictionary.";
147 i : return false;
148 : }
149 :
150 E : version->set_major(major);
151 E : version->set_minor(minor);
152 E : version->set_build(build);
153 E : version->set_patch(patch);
154 E : version->set_last_change(last_change.c_str());
155 :
156 E : return true;
157 E : }
158 :
159 : // Loads a PEFile::Signature from a JSON dictionary.
160 : bool LoadPEFileSignature(const DictionaryValue& dictionary,
161 E : PEFile::Signature* signature) {
162 E : DCHECK(signature != NULL);
163 :
164 E : std::string path;
165 E : int base_address = 0;
166 E : int module_size = 0;
167 E : std::string stamp;
168 E : std::string checksum;
169 : if (!dictionary.GetString(kPathKey, &path) ||
170 : !dictionary.GetInteger(kBaseAddressKey, &base_address) ||
171 : !dictionary.GetInteger(kModuleSizeKey, &module_size) ||
172 : !dictionary.GetString(kModuleTimeDateStampKey, &stamp) ||
173 E : !dictionary.GetString(kModuleChecksumKey, &checksum)) {
174 i : LOG(ERROR) << "Unable to parse PEFile::Signature from JSON dictionary.";
175 i : return false;
176 : }
177 :
178 E : base::UTF8ToWide(path.c_str(), path.size(), &signature->path);
179 E : signature->base_address = PEFile::AbsoluteAddress(base_address);
180 E : signature->module_size = module_size;
181 :
182 E : char* end = NULL;
183 E : signature->module_time_date_stamp = _strtoui64(stamp.c_str(), &end, 16);
184 E : if (end == stamp.c_str()) {
185 i : LOG(ERROR) << "Unable to parse " << kModuleTimeDateStampKey << ".";
186 i : return false;
187 : }
188 :
189 E : signature->module_checksum = strtoul(checksum.c_str(), &end, 16);
190 E : if (end == checksum.c_str()) {
191 i : LOG(ERROR) << "Unable to parse " << kModuleChecksumKey << ".";
192 i : return false;
193 : }
194 :
195 E : return true;
196 E : }
197 :
198 : } // namespace
199 :
200 E : Metadata::Metadata() {
201 E : }
202 :
203 E : bool Metadata::Init(const PEFile::Signature& module_signature) {
204 : // Populate the command line string.
205 E : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
206 E : DCHECK(cmd_line != NULL);
207 : if (!base::WideToUTF8(cmd_line->GetCommandLineString().c_str(),
208 : cmd_line->GetCommandLineString().size(),
209 E : &command_line_)) {
210 i : LOG(ERROR) << "Unable to convert command-line to UTF8.";
211 i : return false;
212 : }
213 :
214 : // Set the remaining properties.
215 E : creation_time_ = base::Time::Now();
216 E : toolchain_version_ = version::kSyzygyVersion;
217 E : module_signature_ = module_signature;
218 :
219 E : return true;
220 E : }
221 :
222 E : bool Metadata::IsConsistent(const PEFile::Signature& module_signature) const {
223 E : if (!version::kSyzygyVersion.IsCompatible(toolchain_version_)) {
224 i : LOG(ERROR) << "Metadata is not compatible with current toolchain version.";
225 i : return false;
226 : }
227 :
228 E : if (!module_signature.IsConsistent(module_signature_)) {
229 i : LOG(ERROR) << "Metadata is not consistent with input module.";
230 i : return false;
231 : }
232 :
233 E : return true;
234 E : }
235 :
236 E : bool Metadata::SaveToJSON(core::JSONFileWriter* json_file) const {
237 E : DCHECK(json_file != NULL);
238 :
239 : return json_file->OpenDict() &&
240 : json_file->OutputKey(kCommandLineKey) &&
241 : json_file->OutputString(command_line_) &&
242 : json_file->OutputKey(kCreationTimeKey) &&
243 : json_file->OutputString(TimeToString(creation_time_)) &&
244 : json_file->OutputKey(kToolchainVersionKey) &&
245 : OutputSyzygyVersion(toolchain_version_, json_file) &&
246 : json_file->OutputKey(kModuleSignatureKey) &&
247 : OutputPEFileSignature(module_signature_, json_file) &&
248 E : json_file->CloseDict();
249 E : }
250 :
251 E : bool Metadata::LoadFromJSON(const DictionaryValue& metadata) {
252 E : std::string creation_time;
253 E : const DictionaryValue* toolchain_version_dict = NULL;
254 E : const DictionaryValue* module_signature_dict = NULL;
255 : if (!metadata.GetString(kCommandLineKey, &command_line_) ||
256 : !metadata.GetString(kCreationTimeKey, &creation_time) ||
257 : !metadata.GetDictionary(kToolchainVersionKey, &toolchain_version_dict) ||
258 E : !metadata.GetDictionary(kModuleSignatureKey, &module_signature_dict)) {
259 i : LOG(ERROR) << "Unable to parse metadata.";
260 i : return false;
261 : }
262 :
263 : if (!LoadSyzygyVersion(*toolchain_version_dict, &toolchain_version_) ||
264 E : !LoadPEFileSignature(*module_signature_dict, &module_signature_))
265 i : return false;
266 :
267 : // Parse the creation time from its string representation.
268 E : return StringToTime(creation_time, &creation_time_);
269 E : }
270 :
271 E : bool Metadata::SaveToBlock(BlockGraph::Block* block) const {
272 : // Serialize the metadata to a ByteVector.
273 E : core::ByteVector bytes;
274 E : core::ScopedOutStreamPtr out_stream;
275 E : out_stream.reset(core::CreateByteOutStream(std::back_inserter(bytes)));
276 E : core::NativeBinaryOutArchive out_archive(out_stream.get());
277 E : if (!out_archive.Save(*this))
278 i : return false;
279 :
280 : // Output some of the information in duplicate, in a human-readable form, so
281 : // that we can easily grep for this stuff in the actual binaries.
282 E : std::string path;
283 : if (!base::WideToUTF8(module_signature_.path.c_str(),
284 : module_signature_.path.size(),
285 E : &path)) {
286 i : LOG(ERROR) << "Unable to convert module path to UTF8.";
287 i : return false;
288 : }
289 E : std::string text("Command-line: ");
290 E : text.append(command_line_);
291 E : text.append("\nCreation time: ");
292 E : text.append(TimeToString(creation_time_));
293 E : text.append("\nToolchain version: ");
294 E : text.append(toolchain_version_.GetVersionString());
295 E : text.append("\nModule path: ");
296 E : text.append(path);
297 E : text.append("\n");
298 E : if (!out_archive.Save(text))
299 i : return false;
300 E : if (!out_archive.Flush())
301 i : return false;
302 :
303 E : block->SetData(NULL, 0);
304 E : block->set_size(bytes.size());
305 E : if (block->CopyData(bytes.size(), &bytes[0]) == NULL) {
306 i : LOG(ERROR) << "Unable to allocate metadata.";
307 i : return false;
308 : }
309 :
310 E : return true;
311 E : }
312 :
313 E : bool Metadata::LoadFromBlock(const BlockGraph::Block* block) {
314 : // Parse the metadata.
315 E : core::ScopedInStreamPtr in_stream;
316 : in_stream.reset(core::CreateByteInStream(block->data(),
317 E : block->data() + block->data_size()));
318 E : core::NativeBinaryInArchive in_archive(in_stream.get());
319 E : if (!in_archive.Load(this)) {
320 i : LOG(ERROR) << "Unable to parse module metadata.";
321 i : return false;
322 : }
323 :
324 E : return true;
325 E : }
326 :
327 E : bool Metadata::LoadFromPE(const PEFile& pe_file) {
328 : // Get the metadata section data.
329 : size_t metadata_id =
330 E : pe_file.GetSectionIndex(common::kSyzygyMetadataSectionName);
331 E : if (metadata_id == pe::kInvalidSection) {
332 E : LOG(ERROR) << "Module does not contain a metadata section.";
333 E : return false;
334 : }
335 E : const IMAGE_SECTION_HEADER* section = pe_file.section_header(metadata_id);
336 E : DCHECK(section != NULL);
337 E : RelativeAddress metadata_addr(section->VirtualAddress);
338 E : size_t metadata_size = section->Misc.VirtualSize;
339 : const core::Byte* metadata = pe_file.GetImageData(metadata_addr,
340 E : metadata_size);
341 E : if (metadata == NULL) {
342 i : LOG(ERROR) << "Unable to get metadata section data.";
343 i : return false;
344 : }
345 :
346 : // Parse the metadata.
347 E : core::ScopedInStreamPtr in_stream;
348 E : in_stream.reset(core::CreateByteInStream(metadata, metadata + metadata_size));
349 E : core::NativeBinaryInArchive in_archive(in_stream.get());
350 E : if (!in_archive.Load(this)) {
351 i : LOG(ERROR) << "Unable to parse module metadata.";
352 i : return false;
353 : }
354 :
355 E : return true;
356 E : }
357 :
358 E : bool Metadata::operator==(const Metadata& rhs) const {
359 : return command_line_ == rhs.command_line_ &&
360 : creation_time_ == rhs.creation_time_ &&
361 : toolchain_version_ == rhs.toolchain_version_ &&
362 E : module_signature_ == rhs.module_signature_;
363 E : }
364 :
365 : // Serialization 'Save' implementation.
366 E : bool Metadata::Save(core::OutArchive* out_archive) const {
367 E : DCHECK(out_archive != NULL);
368 : return out_archive->Save(command_line_) &&
369 : out_archive->Save(creation_time_) &&
370 : out_archive->Save(toolchain_version_) &&
371 E : out_archive->Save(module_signature_);
372 E : }
373 :
374 : // Serialization 'Load' implementation.
375 E : bool Metadata::Load(core::InArchive* in_archive) {
376 E : DCHECK(in_archive != NULL);
377 : return in_archive->Load(&command_line_) &&
378 : in_archive->Load(&creation_time_) &&
379 : in_archive->Load(&toolchain_version_) &&
380 E : in_archive->Load(&module_signature_);
381 E : }
382 :
383 : } // namespace pe
|