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 : // Implementation of utility functions for module/PDB search and discovery.
16 : // Leverages the debughlp back-end so that behaviour is consistent with those
17 : // tools.
18 :
19 : #include "syzygy/pe/find.h"
20 :
21 : #include "base/environment.h"
22 : #include "base/logging.h"
23 : #include "base/files/file_util.h"
24 : #include "base/strings/string_split.h"
25 : #include "base/strings/utf_string_conversions.h"
26 : #include "syzygy/common/com_utils.h"
27 : #include "syzygy/common/dbghelp_util.h"
28 : #include "syzygy/pdb/pdb_util.h"
29 : #include "syzygy/pe/pdb_info.h"
30 : #include "syzygy/pe/pe_data.h"
31 : #include "syzygy/pe/pe_file.h"
32 :
33 : namespace pe {
34 :
35 : namespace {
36 :
37 E : bool GetEnvVar(const char* name, std::wstring* value) {
38 E : DCHECK(name != NULL);
39 E : DCHECK(value != NULL);
40 E : value->clear();
41 :
42 E : scoped_ptr<base::Environment> env(base::Environment::Create());
43 E : if (env.get() == NULL) {
44 i : LOG(ERROR) << "base::Environment::Create returned NULL.";
45 i : return false;
46 : }
47 :
48 : // If this fails, the environment variable simply does not exist.
49 E : std::string var;
50 E : if (!env->GetVar(name, &var))
51 E : return true;
52 :
53 : if (!base::UTF8ToWide(var.c_str(),
54 : var.size(),
55 E : value)) {
56 i : LOG(ERROR) << "base::UTF8ToWide(\"" << var << "\" failed.";
57 i : return false;
58 : }
59 :
60 E : return true;
61 E : }
62 :
63 : // Return TRUE to continue searching, FALSE if we want the search to stop.
64 E : BOOL CALLBACK FindPdbFileCallback(PCTSTR path, PVOID context) {
65 E : DCHECK(path != NULL);
66 E : DCHECK(context != NULL);
67 :
68 E : base::FilePath pdb_path(path);
69 E : const PdbInfo* pdb_info = static_cast<PdbInfo*>(context);
70 :
71 : pdb::PdbInfoHeader70 pdb_header;
72 E : if (!pdb::ReadPdbHeader(pdb_path, &pdb_header))
73 i : return TRUE;
74 E : if (!pdb_info->IsConsistent(pdb_header))
75 E : return TRUE;
76 :
77 E : return FALSE;
78 E : }
79 :
80 : // Return TRUE to continue searching, FALSE if we want the search to stop.
81 E : BOOL CALLBACK FindPeFileCallback(PCTSTR path, PVOID context) {
82 E : DCHECK(path != NULL);
83 E : DCHECK(context != NULL);
84 :
85 E : base::FilePath pe_path(path);
86 E : const PEFile::Signature* pe_info = static_cast<PEFile::Signature*>(context);
87 :
88 E : PEFile pe_file;
89 E : if (!pe_file.Init(pe_path))
90 i : return TRUE;
91 E : PEFile::Signature pe_sig;
92 E : pe_file.GetSignature(&pe_sig);
93 :
94 : // We don't care about the base address or the path.
95 : if (pe_sig.module_size != pe_info->module_size ||
96 E : pe_sig.module_time_date_stamp != pe_info->module_time_date_stamp) {
97 i : return TRUE;
98 : }
99 :
100 E : return FALSE;
101 E : }
102 :
103 : bool FindFile(const base::FilePath& file_path,
104 : const base::StringPiece16& search_paths,
105 : const void* id,
106 : uint32 data,
107 : uint32 flags,
108 : PFINDFILEINPATHCALLBACKW callback,
109 : void* callback_context,
110 E : base::FilePath* found_file) {
111 E : DCHECK(found_file != NULL);
112 :
113 E : found_file->clear();
114 :
115 : // Make a unique "handle" for this use of the symbolizer.
116 E : HANDLE unique_handle = &unique_handle;
117 E : if (!common::SymInitialize(unique_handle, NULL, false))
118 i : return false;
119 :
120 E : base::FilePath dir = file_path.DirName();
121 E : std::wstring basename = file_path.BaseName().value();
122 :
123 : // Augment the search paths with the directory of file_path and the
124 : // current working directory.
125 E : std::wstring paths;
126 E : if (base::PathExists(dir)) {
127 E : paths.append(dir.value());
128 E : paths.push_back(L';');
129 : }
130 E : paths.append(L".;");
131 E : paths.append(search_paths.begin(), search_paths.end());
132 :
133 : // Search for the file.
134 : wchar_t buffer[MAX_PATH];
135 : BOOL result = ::SymFindFileInPathW(unique_handle,
136 : paths.c_str(),
137 : basename.c_str(),
138 : const_cast<void*>(id),
139 : data,
140 : 0,
141 : flags,
142 : &buffer[0],
143 : callback,
144 E : callback_context);
145 E : if (::SymCleanup(unique_handle) == FALSE) {
146 i : DWORD error = ::GetLastError();
147 i : LOG(ERROR) << "SymCleanup failed: " << common::LogWe(error);
148 i : return false;
149 : }
150 E : if (!result) {
151 : // If there is a zero error code, this simply means that the search failed
152 : // to find anything, which is not an error.
153 E : DWORD error = ::GetLastError();
154 E : if (error == 0)
155 E : return true;
156 :
157 i : LOG(ERROR) << "SymFindFileInPath(\"" << file_path.value() << "\") failed: "
158 : << common::LogWe(error);
159 i : return false;
160 : }
161 :
162 E : *found_file = base::FilePath(buffer);
163 :
164 E : return true;
165 E : }
166 :
167 : } // namespace
168 :
169 : bool PeAndPdbAreMatched(const base::FilePath& pe_path,
170 E : const base::FilePath& pdb_path) {
171 E : pe::PdbInfo pe_pdb_info;
172 E : if (!pe_pdb_info.Init(pe_path))
173 E : return false;
174 : pdb::PdbInfoHeader70 pdb_info;
175 E : if (!pdb::ReadPdbHeader(pdb_path, &pdb_info))
176 E : return false;
177 E : if (!pe_pdb_info.IsConsistent(pdb_info))
178 E : return false;
179 E : return true;
180 E : }
181 :
182 : bool FindModuleBySignature(const PEFile::Signature& module_signature,
183 : const base::StringPiece16& search_paths,
184 E : base::FilePath* module_path) {
185 E : DCHECK(module_path != NULL);
186 :
187 E : std::vector<base::FilePath> candidate_paths;
188 E : if (!module_path->empty())
189 E : candidate_paths.push_back(*module_path);
190 E : candidate_paths.push_back(base::FilePath(module_signature.path));
191 :
192 : const void* id =
193 E : reinterpret_cast<void*>(module_signature.module_time_date_stamp);
194 :
195 : // Try a search based on each of the candidate paths.
196 E : for (size_t i = 0; i < candidate_paths.size(); ++i) {
197 E : const base::FilePath& path = candidate_paths[i];
198 :
199 : if (!FindFile(path,
200 : search_paths,
201 : id,
202 : module_signature.module_size,
203 : SSRVOPT_DWORD,
204 : FindPeFileCallback,
205 : const_cast<PEFile::Signature*>(&module_signature),
206 E : module_path)) {
207 i : return false;
208 : }
209 :
210 : // If the search was successful we can terminate early.
211 E : if (!module_path->empty())
212 E : return true;
213 i : }
214 i : DCHECK(module_path->empty());
215 :
216 i : return true;
217 E : }
218 :
219 : bool FindModuleBySignature(const PEFile::Signature& module_signature,
220 E : base::FilePath* module_path) {
221 E : DCHECK(module_path != NULL);
222 :
223 E : std::wstring search_paths;
224 E : if (!GetEnvVar("PATH", &search_paths))
225 i : return false;
226 :
227 : return FindModuleBySignature(module_signature,
228 : search_paths.c_str(),
229 E : module_path);
230 E : }
231 :
232 : bool FindPdbForModule(const base::FilePath& module_path,
233 : const base::StringPiece16& search_paths,
234 E : base::FilePath* pdb_path) {
235 E : DCHECK(pdb_path != NULL);
236 :
237 E : std::vector<base::FilePath> candidate_paths;
238 E : if (!pdb_path->empty())
239 E : candidate_paths.push_back(*pdb_path);
240 :
241 E : PdbInfo pdb_info;
242 E : if (!pdb_info.Init(module_path))
243 E : return false;
244 E : candidate_paths.push_back(pdb_info.pdb_file_name());
245 :
246 : // Prepend the module path to the symbol path.
247 E : std::wstring search_path(module_path.DirName().value());
248 E : search_path.append(L";");
249 E : search_path.append(search_paths.begin(), search_paths.end());
250 :
251 E : for (size_t i = 0; i < candidate_paths.size(); ++i) {
252 E : const base::FilePath& path = candidate_paths[i];
253 :
254 : if (!FindFile(path,
255 : search_path.c_str(),
256 : &pdb_info.signature(),
257 : pdb_info.pdb_age(),
258 : SSRVOPT_GUIDPTR,
259 : FindPdbFileCallback,
260 : &pdb_info,
261 E : pdb_path)) {
262 i : return false;
263 : }
264 :
265 : // If the search was successful we can terminate early.
266 E : if (!pdb_path->empty())
267 E : return true;
268 E : }
269 E : DCHECK(pdb_path->empty());
270 :
271 E : return true;
272 E : }
273 :
274 : bool FindPdbForModule(const base::FilePath& module_path,
275 E : base::FilePath* pdb_path) {
276 E : DCHECK(pdb_path != NULL);
277 :
278 E : std::wstring search_paths;
279 E : if (!GetEnvVar("_NT_SYMBOL_PATH", &search_paths))
280 i : return false;
281 :
282 : return FindPdbForModule(module_path,
283 : search_paths.c_str(),
284 E : pdb_path);
285 E : }
286 :
287 : } // namespace pe
|