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/pe_file.h"
16 : #include "base/file_path.h"
17 : #include "base/native_library.h"
18 : #include "base/path_service.h"
19 : #include "base/string_util.h"
20 : #include "gmock/gmock.h"
21 : #include "gtest/gtest.h"
22 : #include "syzygy/core/unittest_util.h"
23 : #include "syzygy/pe/unittest_util.h"
24 :
25 : namespace pe {
26 :
27 : namespace {
28 :
29 : using core::AbsoluteAddress;
30 : using core::FileOffsetAddress;
31 : using core::RelativeAddress;
32 :
33 : class PEFileTest: public testing::PELibUnitTest {
34 : typedef testing::PELibUnitTest Super;
35 :
36 : public:
37 E : PEFileTest() : test_dll_(NULL) {
38 E : }
39 :
40 E : virtual void SetUp() OVERRIDE {
41 E : Super::SetUp();
42 :
43 E : FilePath test_dll = testing::GetExeRelativePath(kDllName);
44 E : std::string error;
45 E : test_dll_ = base::LoadNativeLibrary(test_dll, &error);
46 :
47 E : ASSERT_TRUE(image_file_.Init(test_dll));
48 E : }
49 :
50 E : virtual void TearDown() OVERRIDE {
51 E : base::UnloadNativeLibrary(test_dll_);
52 E : Super::TearDown();
53 E : }
54 :
55 : void TestAddressesAreConsistent(RelativeAddress rel,
56 : AbsoluteAddress abs,
57 E : FileOffsetAddress off) {
58 E : AbsoluteAddress abs2;
59 E : RelativeAddress rel2;
60 E : FileOffsetAddress off2;
61 :
62 E : ASSERT_TRUE(image_file_.Translate(rel, &abs2));
63 E : ASSERT_EQ(abs, abs2);
64 :
65 E : ASSERT_TRUE(image_file_.Translate(abs, &rel2));
66 E : ASSERT_EQ(rel, rel2);
67 :
68 E : ASSERT_TRUE(image_file_.Translate(off, &rel2));
69 E : ASSERT_EQ(rel, rel2);
70 :
71 E : ASSERT_TRUE(image_file_.Translate(rel, &off2));
72 E : ASSERT_EQ(off, off2);
73 E : }
74 :
75 : protected:
76 : pe::PEFile image_file_;
77 : base::NativeLibrary test_dll_;
78 : };
79 :
80 : } // namespace
81 :
82 E : TEST_F(PEFileTest, Create) {
83 E : PEFile image_file;
84 :
85 E : ASSERT_EQ(NULL, image_file.dos_header());
86 E : ASSERT_EQ(NULL, image_file.nt_headers());
87 E : ASSERT_EQ(NULL, image_file.section_headers());
88 E : }
89 :
90 E : TEST_F(PEFileTest, Init) {
91 E : EXPECT_TRUE(image_file_.dos_header() != NULL);
92 E : EXPECT_TRUE(image_file_.nt_headers() != NULL);
93 E : EXPECT_TRUE(image_file_.section_headers() != NULL);
94 E : }
95 :
96 E : TEST_F(PEFileTest, GetImageData) {
97 E : const IMAGE_NT_HEADERS* nt_headers = image_file_.nt_headers();
98 E : ASSERT_TRUE(nt_headers != NULL);
99 : const IMAGE_DATA_DIRECTORY* exports =
100 E : &nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
101 :
102 : // We should be able to read the export directory.
103 : ASSERT_TRUE(image_file_.GetImageData(RelativeAddress(exports->VirtualAddress),
104 E : exports->Size) != NULL);
105 :
106 : // We should be able to read it using an absolute address as well.
107 E : AbsoluteAddress abs_addr;
108 : ASSERT_TRUE(image_file_.Translate(RelativeAddress(exports->VirtualAddress),
109 E : &abs_addr));
110 E : ASSERT_TRUE(image_file_.GetImageData(abs_addr, exports->Size) != NULL);
111 :
112 : // But there ought to be a gap in the image data past the header size.
113 : ASSERT_TRUE(image_file_.GetImageData(
114 E : RelativeAddress(nt_headers->OptionalHeader.SizeOfHeaders), 1) == NULL);
115 E : }
116 :
117 E : TEST_F(PEFileTest, ReadImage) {
118 E : const IMAGE_NT_HEADERS* nt_headers = image_file_.nt_headers();
119 E : ASSERT_TRUE(nt_headers != NULL);
120 : const IMAGE_DATA_DIRECTORY* exports =
121 E : &nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
122 :
123 : // We should be able to read the export directory.
124 E : IMAGE_EXPORT_DIRECTORY export_dir = {};
125 : ASSERT_TRUE(image_file_.ReadImage(RelativeAddress(exports->VirtualAddress),
126 : &export_dir,
127 E : sizeof(export_dir)));
128 :
129 : // Check that we actually read something.
130 E : IMAGE_EXPORT_DIRECTORY zero_export_dir = {};
131 E : ASSERT_NE(0, memcmp(&export_dir, &zero_export_dir, sizeof(export_dir)));
132 :
133 : // Now test the ReadImageString function.
134 E : std::vector<RelativeAddress> names(export_dir.NumberOfNames);
135 : ASSERT_TRUE(image_file_.ReadImage(RelativeAddress(export_dir.AddressOfNames),
136 : &names.at(0),
137 E : sizeof(names[0]) * names.size()));
138 :
139 : // Test the same thing using an absolute address.
140 E : AbsoluteAddress abs_names_addr;
141 : ASSERT_TRUE(image_file_.Translate(RelativeAddress(export_dir.AddressOfNames),
142 E : &abs_names_addr));
143 E : std::vector<RelativeAddress> names2(export_dir.NumberOfNames);
144 : ASSERT_TRUE(image_file_.ReadImage(abs_names_addr, &names2.at(0),
145 E : sizeof(names2[0]) * names2.size()));
146 E : ASSERT_EQ(names, names2);
147 :
148 : // Read all the export name strings.
149 E : for (size_t i = 0; i < names.size(); ++i) {
150 E : std::string name1;
151 E : ASSERT_TRUE(image_file_.ReadImageString(names[i], &name1));
152 : ASSERT_TRUE(name1 == "function1" ||
153 : name1 == "function3" ||
154 : name1 == "DllMain" ||
155 : name1 == "CreateFileW" ||
156 : name1 == "TestUnusedFuncs" ||
157 : name1 == "TestExport" ||
158 : name1 == "LabelTestFunc" ||
159 E : name1 == "BringInOle32DelayLib");
160 :
161 E : std::string name2;
162 E : AbsoluteAddress abs_addr;
163 E : ASSERT_TRUE(image_file_.Translate(names[i], &abs_addr));
164 E : ASSERT_TRUE(image_file_.ReadImageString(abs_addr, &name2));
165 E : ASSERT_EQ(name1, name2);
166 E : }
167 E : }
168 :
169 E : TEST_F(PEFileTest, Contains) {
170 E : RelativeAddress relative_base(0);
171 E : AbsoluteAddress absolute_base;
172 E : size_t image_size = image_file_.nt_headers()->OptionalHeader.SizeOfImage;
173 E : RelativeAddress relative_end(image_size);
174 : AbsoluteAddress absolute_end(
175 E : image_file_.nt_headers()->OptionalHeader.ImageBase + image_size);
176 :
177 E : ASSERT_TRUE(image_file_.Translate(relative_base, &absolute_base));
178 E : ASSERT_TRUE(image_file_.Contains(relative_base, 1));
179 E : ASSERT_TRUE(image_file_.Contains(absolute_base, 1));
180 E : ASSERT_FALSE(image_file_.Contains(absolute_base - 1, 1));
181 E : ASSERT_FALSE(image_file_.Contains(absolute_end, 1));
182 E : ASSERT_FALSE(image_file_.Contains(relative_end, 1));
183 :
184 : // TODO(rogerm): test for inclusion at the end of the address space
185 : // The way the address space is built only captures the ranges
186 : // specified as sections in the headers, not the overall image size.
187 : // Either the test needs to be more invasive or the data structure
188 : // needs to be more broadly representative. Note sure which, but
189 : // it's not critical.
190 :
191 : // ASSERT_TRUE(image_file_.Contains(absolute_end - 1, 1));
192 E : }
193 :
194 E : TEST_F(PEFileTest, Translate) {
195 : // Try an address inside the headers (outside of any section).
196 E : AbsoluteAddress abs(image_file_.nt_headers()->OptionalHeader.ImageBase + 3);
197 E : RelativeAddress rel(3);
198 E : FileOffsetAddress off(3);
199 E : ASSERT_NO_FATAL_FAILURE(TestAddressesAreConsistent(rel, abs, off));
200 :
201 : // Now try an address in each of the sections.
202 E : size_t i = 0;
203 E : for (; i < image_file_.nt_headers()->FileHeader.NumberOfSections; ++i) {
204 E : const IMAGE_SECTION_HEADER* section = image_file_.section_header(i);
205 :
206 : AbsoluteAddress abs(section->VirtualAddress +
207 E : image_file_.nt_headers()->OptionalHeader.ImageBase + i);
208 E : RelativeAddress rel(section->VirtualAddress + i);
209 E : FileOffsetAddress off(section->PointerToRawData + i);
210 :
211 E : ASSERT_NO_FATAL_FAILURE(TestAddressesAreConsistent(rel, abs, off));
212 E : }
213 E : }
214 :
215 E : TEST_F(PEFileTest, TranslateOffImageFails) {
216 : const IMAGE_SECTION_HEADER* section = image_file_.section_header(
217 E : image_file_.nt_headers()->FileHeader.NumberOfSections - 1);
218 :
219 : AbsoluteAddress abs_end(image_file_.nt_headers()->OptionalHeader.ImageBase +
220 E : image_file_.nt_headers()->OptionalHeader.SizeOfImage);
221 E : RelativeAddress rel_end(image_file_.nt_headers()->OptionalHeader.SizeOfImage);
222 E : FileOffsetAddress off_end(section->PointerToRawData + section->SizeOfRawData);
223 :
224 E : AbsoluteAddress abs;
225 E : RelativeAddress rel;
226 E : FileOffsetAddress off;
227 E : ASSERT_FALSE(image_file_.Translate(rel_end, &abs));
228 E : ASSERT_FALSE(image_file_.Translate(abs_end, &rel));
229 E : ASSERT_FALSE(image_file_.Translate(off_end, &rel));
230 E : ASSERT_FALSE(image_file_.Translate(rel_end, &off));
231 E : }
232 :
233 E : TEST_F(PEFileTest, TranslateFileOffsetSpaceNotContiguous) {
234 E : size_t data_index = image_file_.GetSectionIndex(".data");
235 E : ASSERT_NE(kInvalidSection, data_index);
236 :
237 : const IMAGE_SECTION_HEADER* data =
238 E : image_file_.section_header(data_index);
239 E : ASSERT_TRUE(data != NULL);
240 :
241 E : RelativeAddress rel1, rel2;
242 E : rel1.set_value(data->VirtualAddress + data->SizeOfRawData - 1);
243 E : rel2.set_value(data->VirtualAddress + data->SizeOfRawData);
244 :
245 E : FileOffsetAddress off1, off2;
246 E : ASSERT_TRUE(image_file_.Translate(rel1, &off1));
247 E : ASSERT_FALSE(image_file_.Translate(rel2, &off2));
248 :
249 E : RelativeAddress rel3;
250 E : off2 = off1 + 1;
251 E : ASSERT_TRUE(image_file_.Translate(off2, &rel3));
252 E : ASSERT_LT(1, rel3 - rel2);
253 E : }
254 :
255 E : TEST_F(PEFileTest, DecodeRelocs) {
256 E : PEFile::RelocSet relocs;
257 E : ASSERT_TRUE(image_file_.DecodeRelocs(&relocs));
258 :
259 E : PEFile::RelocMap reloc_values;
260 E : ASSERT_TRUE(image_file_.ReadRelocs(relocs, &reloc_values));
261 :
262 : // We expect to have some relocs to validate and we expect that
263 : // all relocation table entries and their corresponding values
264 : // fall within the image's address space
265 E : ASSERT_TRUE(!reloc_values.empty());
266 E : PEFile::RelocMap::const_iterator i = reloc_values.begin();
267 E : for (;i != reloc_values.end(); ++i) {
268 : // Note:
269 : // i->first is a relative pointer yielded by the relocation table
270 : // i->second is the absolute value of that pointer (i.e., the relocation)
271 :
272 E : const RelativeAddress &pointer_location(i->first);
273 E : const AbsoluteAddress &pointer_value(i->second);
274 :
275 E : ASSERT_TRUE(image_file_.Contains(pointer_location, sizeof(pointer_value)));
276 E : ASSERT_TRUE(image_file_.Contains(pointer_value, 1));
277 E : }
278 E : }
279 :
280 E : TEST_F(PEFileTest, DecodeExports) {
281 E : PEFile::ExportInfoVector exports;
282 E : ASSERT_TRUE(image_file_.DecodeExports(&exports));
283 :
284 : // This must match the information in the test_dll.def file.
285 : PEFile::ExportInfo expected[] = {
286 E : { RelativeAddress(0), "", "", 1 },
287 E : { RelativeAddress(0), "BringInOle32DelayLib", "", 2 },
288 E : { RelativeAddress(0), "TestExport", "", 3 },
289 E : { RelativeAddress(0), "TestUnusedFuncs", "", 4 },
290 E : { RelativeAddress(0), "LabelTestFunc", "", 5 },
291 E : { RelativeAddress(0), "DllMain", "", 7 },
292 E : { RelativeAddress(0), "function3", "", 9 },
293 E : { RelativeAddress(0), "CreateFileW", "kernel32.CreateFileW", 13 },
294 E : { RelativeAddress(0), "function1", "", 17 },
295 : };
296 :
297 E : EXPECT_EQ(ARRAYSIZE(expected), exports.size());
298 :
299 E : const uint8* module_base = reinterpret_cast<const uint8*>(test_dll_);
300 :
301 : // Resolve the exports and compare.
302 E : for (size_t i = 0; i < arraysize(expected); ++i) {
303 E : if (expected[i].forward.empty()) {
304 : // Look up the functions by ordinal.
305 : const uint8* function = reinterpret_cast<const uint8*>(
306 : base::GetFunctionPointerFromNativeLibrary(
307 E : test_dll_, reinterpret_cast<const char*>(expected[i].ordinal)));
308 E : EXPECT_TRUE(function != NULL);
309 :
310 E : expected[i].function = RelativeAddress(function - module_base);
311 : }
312 E : EXPECT_EQ(expected[i].function, exports.at(i).function);
313 E : EXPECT_EQ(expected[i].name, exports.at(i).name);
314 E : EXPECT_EQ(expected[i].forward, exports.at(i).forward);
315 E : EXPECT_EQ(expected[i].ordinal, exports.at(i).ordinal);
316 E : }
317 E : }
318 :
319 E : TEST_F(PEFileTest, DecodeImports) {
320 E : PEFile::ImportDllVector imports;
321 E : ASSERT_TRUE(image_file_.DecodeImports(&imports));
322 :
323 : // Validation the read imports section.
324 : // The test image imports at least kernel32 and the export_dll.
325 E : ASSERT_LE(2U, imports.size());
326 :
327 E : for (size_t i = 0; i < imports.size(); ++i) {
328 E : PEFile::ImportDll& dll = imports[i];
329 E : if (0 == base::strcasecmp("export_dll.dll", dll.name.c_str())) {
330 E : ASSERT_EQ(3, dll.functions.size());
331 : ASSERT_THAT(dll.functions,
332 E : testing::Contains(PEFile::ImportInfo(0, 0, "function1")));
333 : ASSERT_THAT(dll.functions,
334 E : testing::Contains(PEFile::ImportInfo(1, 0, "function3")));
335 : ASSERT_THAT(dll.functions,
336 E : testing::Contains(PEFile::ImportInfo(0, 7, "")));
337 : }
338 E : }
339 E : }
340 :
341 E : TEST_F(PEFileTest, GetSectionIndexByRelativeAddress) {
342 E : size_t num_sections = image_file_.nt_headers()->FileHeader.NumberOfSections;
343 E : for (size_t i = 0; i < num_sections; ++i) {
344 : RelativeAddress section_start(
345 E : image_file_.section_header(i)->VirtualAddress);
346 E : EXPECT_EQ(i, image_file_.GetSectionIndex(section_start, 1));
347 E : }
348 :
349 : RelativeAddress off_end(image_file_.nt_headers()->OptionalHeader.SizeOfImage +
350 E : 0x10000);
351 E : EXPECT_EQ(kInvalidSection, image_file_.GetSectionIndex(off_end, 1));
352 E : }
353 :
354 E : TEST_F(PEFileTest, GetSectionIndexByAbsoluteAddress) {
355 E : size_t image_base = image_file_.nt_headers()->OptionalHeader.ImageBase;
356 E : size_t num_sections = image_file_.nt_headers()->FileHeader.NumberOfSections;
357 E : for (size_t i = 0; i < num_sections; ++i) {
358 : AbsoluteAddress section_start(
359 E : image_file_.section_header(i)->VirtualAddress + image_base);
360 E : EXPECT_EQ(i, image_file_.GetSectionIndex(section_start, 1));
361 E : }
362 :
363 : AbsoluteAddress off_end(image_file_.nt_headers()->OptionalHeader.SizeOfImage +
364 E : 0x10000 + image_base);
365 E : EXPECT_EQ(kInvalidSection, image_file_.GetSectionIndex(off_end, 1));
366 E : }
367 :
368 E : TEST_F(PEFileTest, GetSectionIndexByName) {
369 E : size_t num_sections = image_file_.nt_headers()->FileHeader.NumberOfSections;
370 E : for (size_t i = 0; i < num_sections; ++i) {
371 E : std::string name = image_file_.GetSectionName(i);
372 E : EXPECT_EQ(i, image_file_.GetSectionIndex(name.c_str()));
373 E : }
374 :
375 E : EXPECT_EQ(kInvalidSection, image_file_.GetSectionIndex(".foobar"));
376 E : }
377 :
378 E : TEST_F(PEFileTest, GetSectionHeaderByRelativeAddress) {
379 E : size_t num_sections = image_file_.nt_headers()->FileHeader.NumberOfSections;
380 E : for (size_t i = 0; i < num_sections; ++i) {
381 : RelativeAddress section_start(
382 E : image_file_.section_header(i)->VirtualAddress);
383 : EXPECT_EQ(image_file_.section_header(i),
384 E : image_file_.GetSectionHeader(section_start, 1));
385 E : }
386 :
387 : RelativeAddress off_end(image_file_.nt_headers()->OptionalHeader.SizeOfImage +
388 E : 0x10000);
389 E : EXPECT_EQ(kInvalidSection, image_file_.GetSectionIndex(off_end, 1));
390 E : }
391 :
392 E : TEST_F(PEFileTest, GetSectionHeaderByAbsoluteAddress) {
393 E : size_t image_base = image_file_.nt_headers()->OptionalHeader.ImageBase;
394 E : size_t num_sections = image_file_.nt_headers()->FileHeader.NumberOfSections;
395 E : for (size_t i = 0; i < num_sections; ++i) {
396 : AbsoluteAddress section_start(
397 E : image_file_.section_header(i)->VirtualAddress + image_base);
398 : EXPECT_EQ(image_file_.section_header(i),
399 E : image_file_.GetSectionHeader(section_start, 1));
400 E : }
401 :
402 : AbsoluteAddress off_end(image_file_.nt_headers()->OptionalHeader.SizeOfImage +
403 E : 0x10000 + image_base);
404 E : EXPECT_EQ(kInvalidSection, image_file_.GetSectionIndex(off_end, 1));
405 E : }
406 :
407 E : TEST_F(PEFileTest, GetSectionHeaderByName) {
408 E : size_t num_sections = image_file_.nt_headers()->FileHeader.NumberOfSections;
409 E : for (size_t i = 0; i < num_sections; ++i) {
410 E : std::string name = image_file_.GetSectionName(i);
411 : EXPECT_EQ(image_file_.section_header(i),
412 E : image_file_.GetSectionHeader(name.c_str()));
413 E : }
414 :
415 E : EXPECT_EQ(NULL, image_file_.GetSectionHeader(".foobar"));
416 E : }
417 :
418 E : TEST(PEFileSignatureTest, Serialization) {
419 E : PEFile::Signature sig;
420 E : sig.path = L"C:\foo\bar.dll";
421 E : sig.base_address = AbsoluteAddress(0x1000000);
422 E : sig.module_size = 12345;
423 E : sig.module_time_date_stamp = 9999999;
424 E : sig.module_checksum = 0xbaadf00d;
425 :
426 E : EXPECT_TRUE(testing::TestSerialization(sig));
427 E : }
428 :
429 E : TEST(PEFileSignatureTest, Consistency) {
430 E : PEFile::Signature sig1;
431 E : sig1.path = L"C:\foo\bar.dll";
432 E : sig1.base_address = AbsoluteAddress(0x1000000);
433 E : sig1.module_size = 12345;
434 E : sig1.module_time_date_stamp = 9999999;
435 E : sig1.module_checksum = 0xbaadf00d;
436 :
437 : // sig2 is the same, but with a different module path.
438 E : PEFile::Signature sig2(sig1);
439 E : sig2.path = L"C:\foo\bar.exe";
440 :
441 E : EXPECT_FALSE(sig1 == sig2);
442 E : EXPECT_TRUE(sig1.IsConsistent(sig2));
443 E : }
444 :
445 : } // namespace pe
|