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