pe_image_resources.cc revision 116680a4aac90f2aa7413d9095a592090648e557
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// This file contains the implementation for an iterator over a portable
6// executable file's resources.
7
8#include "chrome/installer/test/pe_image_resources.h"
9
10#include "base/logging.h"
11#include "base/win/pe_image.h"
12
13namespace {
14
15// Performs a cast to type |T| of |data| iff |data_size| is sufficient to hold
16// an instance of type |T|.  Returns true on success.
17template<class T>
18bool StructureAt(const uint8* data, size_t data_size, const T** structure) {
19  if (sizeof(T) <= data_size) {
20    *structure = reinterpret_cast<const T*>(data);
21    return true;
22  }
23  return false;
24}
25
26// Recursive function for enumerating entries in an image's resource segment.
27// static
28bool EnumResourcesWorker(
29    const base::win::PEImage& image, const uint8* tree_base, DWORD tree_size,
30    DWORD directory_offset, upgrade_test::EntryPath* path,
31    upgrade_test::EnumResource_Fn callback, uintptr_t context) {
32  bool success = true;
33  const IMAGE_RESOURCE_DIRECTORY* resource_directory;
34
35  if (!StructureAt(tree_base + directory_offset, tree_size - directory_offset,
36                   &resource_directory) ||
37      directory_offset + sizeof(IMAGE_RESOURCE_DIRECTORY) +
38          (resource_directory->NumberOfNamedEntries +
39           resource_directory->NumberOfIdEntries) *
40          sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY) > tree_size) {
41    LOG(DFATAL) << "Insufficient room in resource segment for directory entry.";
42    return false;
43  }
44
45  const IMAGE_RESOURCE_DIRECTORY_ENTRY* scan =
46      reinterpret_cast<const IMAGE_RESOURCE_DIRECTORY_ENTRY*>(
47          tree_base + directory_offset +
48          sizeof(IMAGE_RESOURCE_DIRECTORY));
49  const IMAGE_RESOURCE_DIRECTORY_ENTRY* end = scan +
50      resource_directory->NumberOfNamedEntries +
51      resource_directory->NumberOfIdEntries;
52  for (; success && scan != end; ++scan) {
53    if ((scan->NameIsString != 0) !=
54        (scan - reinterpret_cast<const IMAGE_RESOURCE_DIRECTORY_ENTRY*>(
55            tree_base + directory_offset +
56            sizeof(IMAGE_RESOURCE_DIRECTORY)) <
57            resource_directory->NumberOfNamedEntries)) {
58      LOG(DFATAL) << "Inconsistent number of named or numbered entries.";
59      success = false;
60      break;
61    }
62    if (scan->NameIsString) {
63      const IMAGE_RESOURCE_DIR_STRING_U* dir_string;
64      if (!StructureAt(tree_base + scan->NameOffset,
65                       tree_size - scan->NameOffset, &dir_string) ||
66          scan->NameOffset + sizeof(WORD) +
67              dir_string->Length * sizeof(wchar_t) > tree_size) {
68        LOG(DFATAL) << "Insufficient room in resource segment for entry name.";
69        success = false;
70        break;
71      }
72      path->push_back(
73          upgrade_test::EntryId(std::wstring(&dir_string->NameString[0],
74                                             dir_string->Length)));
75    } else {
76      path->push_back(upgrade_test::EntryId(scan->Id));
77    }
78    if (scan->DataIsDirectory) {
79      success = EnumResourcesWorker(image, tree_base, tree_size,
80                                    scan->OffsetToDirectory, path, callback,
81                                    context);
82    } else {
83      const IMAGE_RESOURCE_DATA_ENTRY* data_entry;
84      if (StructureAt(tree_base + scan->OffsetToData,
85                      tree_size - scan->OffsetToData, &data_entry) &&
86          reinterpret_cast<uint8*>(
87              image.RVAToAddr(data_entry->OffsetToData)) + data_entry->Size <=
88          tree_base + tree_size) {
89        // Despite what winnt.h says, OffsetToData is an RVA.
90        callback(
91            *path,
92            reinterpret_cast<uint8*>(image.RVAToAddr(data_entry->OffsetToData)),
93            data_entry->Size, data_entry->CodePage, context);
94      } else {
95        LOG(DFATAL) << "Insufficient room in resource segment for data entry.";
96        success = false;
97      }
98    }
99    path->pop_back();
100  }
101
102  return success;
103}
104
105}  // namespace
106
107namespace upgrade_test {
108
109// static
110bool EnumResources(const base::win::PEImage& image, EnumResource_Fn callback,
111                   uintptr_t context) {
112  DWORD resources_size =
113      image.GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_RESOURCE);
114  if (resources_size != 0) {
115    EntryPath path_storage;
116    return EnumResourcesWorker(
117        image,
118        reinterpret_cast<uint8*>(
119            image.GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_RESOURCE)),
120        resources_size, 0, &path_storage, callback, context);
121  }
122  return true;
123}
124
125}  // namespace upgrade_test
126