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/stringprintf.h"
21 : #include "base/utf_string_conversions.h"
22 : #include "base/values.h"
23 : #include "base/json/json_reader.h"
24 : #include "base/json/string_escape.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 block_graph::BlockGraph;
33 : using core::RelativeAddress;
34 :
35 : namespace {
36 :
37 : // Metadata JSON keys.
38 : const char kCommandLineKey[] = "command_line";
39 : const char kCreationTimeKey[] = "creation_time";
40 : const char kToolchainVersionKey[] = "toolchain_version";
41 : const char kModuleSignatureKey[] = "module_signature";
42 :
43 : // SyzygyVersion JSON keys.
44 : const char kMajorKey[] = "major";
45 : const char kMinorKey[] = "minor";
46 : const char kBuildKey[] = "build";
47 : const char kPatchKey[] = "patch";
48 : const char kLastChangeKey[] = "last_change";
49 :
50 : // PEFile::Signature JSON keys.
51 : const char kPathKey[] = "path";
52 : const char kBaseAddressKey[] = "base_address";
53 : const char kModuleSizeKey[] = "module_size";
54 : const char kModuleTimeDateStampKey[] = "module_time_date_stamp";
55 : const char kModuleChecksumKey[] = "module_checksum";
56 :
57 E : std::string TimeToString(const Time& time) {
58 : // Want the output format to be consistent with what Time::FromString
59 : // accepts as input. An example follows:
60 : // Tue, 15 Nov 1994 12:45:26 GMT.
61 : char buffer[64];
62 E : time_t tt = time.ToTimeT();
63 E : struct tm timeinfo = {};
64 E : gmtime_s(&timeinfo, &tt);
65 E : strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S GMT", &timeinfo);
66 E : return std::string(buffer);
67 E : }
68 :
69 E : bool StringToTime(const std::string& string, Time* time) {
70 E : return Time::FromString(string.c_str(), time);
71 E : }
72 :
73 : // Outputs a SyzygyVersion object in JSON format as a dictionary. Does not
74 : // output a newline after the dictionary.
75 : bool OutputSyzygyVersion(const common::SyzygyVersion& version,
76 E : core::JSONFileWriter* json_file) {
77 E : DCHECK(json_file != NULL);
78 :
79 E : if (!json_file->OpenDict())
80 i : return false;
81 :
82 E : if (json_file->pretty_print()) {
83 E : std::string comment("Toolchain version: ");
84 E : comment.append(version.GetVersionString());
85 E : if (!json_file->OutputComment(comment))
86 i : return false;
87 E : }
88 :
89 : return json_file->OutputKey(kMajorKey) &&
90 : json_file->OutputInteger(version.major()) &&
91 : json_file->OutputKey(kMinorKey) &&
92 : json_file->OutputInteger(version.minor()) &&
93 : json_file->OutputKey(kBuildKey) &&
94 : json_file->OutputInteger(version.build()) &&
95 : json_file->OutputKey(kPatchKey) &&
96 : json_file->OutputInteger(version.patch()) &&
97 : json_file->OutputKey(kLastChangeKey) &&
98 : json_file->OutputString(version.last_change()) &&
99 E : json_file->CloseDict();
100 E : }
101 :
102 : // Outputs a PEFile::Signature in JSON format as a dictionary. Does not output
103 : // a newline after the dictionary.
104 : bool OutputPEFileSignature(const PEFile::Signature& signature,
105 E : core::JSONFileWriter* json_file) {
106 E : DCHECK(json_file != NULL);
107 :
108 E : std::string path;
109 E : WideToUTF8(signature.path.c_str(), signature.path.size(), &path);
110 E : path = base::GetDoubleQuotedJson(path);
111 :
112 : std::string time_stamp(
113 E : base::StringPrintf("0x%llX", signature.module_time_date_stamp));
114 E : std::string checksum(base::StringPrintf("0x%X", signature.module_checksum));
115 :
116 : return json_file->OpenDict() &&
117 : json_file->OutputKey(kPathKey) &&
118 : json_file->OutputString(signature.path) &&
119 : json_file->OutputKey(kBaseAddressKey) &&
120 : json_file->OutputInteger(signature.base_address.value()) &&
121 : json_file->OutputKey(kModuleSizeKey) &&
122 : json_file->OutputInteger(signature.module_size) &&
123 : json_file->OutputKey(kModuleTimeDateStampKey) &&
124 : json_file->OutputString(time_stamp) &&
125 : json_file->OutputKey(kModuleChecksumKey) &&
126 : json_file->OutputString(checksum) &&
127 E : json_file->CloseDict();
128 E : }
129 :
130 : // Loads a syzygy version from a JSON dictionary.
131 : bool LoadSyzygyVersion(const DictionaryValue& dictionary,
132 E : common::SyzygyVersion* version) {
133 E : DCHECK(version != NULL);
134 :
135 E : int major = 0;
136 E : int minor = 0;
137 E : int build = 0;
138 E : int patch = 0;
139 E : std::string last_change;
140 : if (!dictionary.GetInteger(kMajorKey, &major) ||
141 : !dictionary.GetInteger(kMinorKey, &minor) ||
142 : !dictionary.GetInteger(kBuildKey, &build) ||
143 : !dictionary.GetInteger(kPatchKey, &patch) ||
144 E : !dictionary.GetString(kLastChangeKey, &last_change)) {
145 i : LOG(ERROR) << "Unable to parse SyzygyVersion from JSON dictionary.";
146 i : return false;
147 : }
148 :
149 E : version->set_major(major);
150 E : version->set_minor(minor);
151 E : version->set_build(build);
152 E : version->set_patch(patch);
153 E : version->set_last_change(last_change.c_str());
154 :
155 E : return true;
156 E : }
157 :
158 : // Loads a PEFile::Signature from a JSON dictionary.
159 : bool LoadPEFileSignature(const DictionaryValue& dictionary,
160 E : PEFile::Signature* signature) {
161 E : DCHECK(signature != NULL);
162 :
163 E : std::string path;
164 E : int base_address = 0;
165 E : int module_size = 0;
166 E : std::string stamp;
167 E : std::string checksum;
168 E : Value* value = NULL;
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 : 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 : CommandLine* cmd_line = CommandLine::ForCurrentProcess();
206 E : DCHECK(cmd_line != NULL);
207 : if (!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_ = common::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 (!common::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 (!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
|