Process Hacker
maplib.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * mapped library
4  *
5  * Copyright (C) 2010 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 /*
24  * This file contains functions to load and retrieve information for
25  * library/archive files (lib). The file format for archive files is explained
26  * in the PE/COFF specification located at:
27  *
28  * http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
29  */
30 
31 #include <ph.h>
32 
34  _In_ PPH_MAPPED_ARCHIVE MappedArchive,
35  _In_ PVOID Address,
36  _In_ SIZE_T Length
37  );
38 
40  _In_ PPH_MAPPED_ARCHIVE MappedArchive,
41  _In_ PIMAGE_ARCHIVE_MEMBER_HEADER Header,
42  _Out_ PPH_MAPPED_ARCHIVE_MEMBER Member
43  );
44 
46  _Out_ PPH_MAPPED_ARCHIVE MappedArchive,
47  _In_ PVOID ViewBase,
48  _In_ SIZE_T Size
49  )
50 {
51  NTSTATUS status;
52  PCHAR start;
53 
54  start = (PCHAR)ViewBase;
55 
56  memset(MappedArchive, 0, sizeof(PH_MAPPED_ARCHIVE));
57  MappedArchive->ViewBase = ViewBase;
58  MappedArchive->Size = Size;
59 
60  __try
61  {
62  // Verify the file signature.
63 
64  PhpMappedArchiveProbe(MappedArchive, start, IMAGE_ARCHIVE_START_SIZE);
65 
66  if (memcmp(start, IMAGE_ARCHIVE_START, IMAGE_ARCHIVE_START_SIZE) != 0)
67  PhRaiseStatus(STATUS_INVALID_IMAGE_FORMAT);
68  }
69  __except (EXCEPTION_EXECUTE_HANDLER)
70  {
71  return GetExceptionCode();
72  }
73 
74  // Get the members.
75  // Note: the names are checked.
76 
77  // First linker member
78 
80  MappedArchive,
81  (PIMAGE_ARCHIVE_MEMBER_HEADER)(start + IMAGE_ARCHIVE_START_SIZE),
82  &MappedArchive->FirstLinkerMember
83  );
84 
85  if (!NT_SUCCESS(status))
86  return status;
87 
88  if (MappedArchive->FirstLinkerMember.Type != LinkerArchiveMemberType)
89  return STATUS_INVALID_PARAMETER;
90 
91  MappedArchive->FirstStandardMember = &MappedArchive->FirstLinkerMember;
92 
93  // Second linker member
94 
96  &MappedArchive->FirstLinkerMember,
97  &MappedArchive->SecondLinkerMember
98  );
99 
100  if (!NT_SUCCESS(status))
101  return status;
102 
103  if (MappedArchive->SecondLinkerMember.Type != LinkerArchiveMemberType)
104  return STATUS_INVALID_PARAMETER;
105 
106  // Longnames member
107  // This member doesn't seem to be mandatory, contrary to the specification.
108  // So we'll check if it's actually a longnames member, and if not, ignore
109  // it.
110 
112  &MappedArchive->SecondLinkerMember,
113  &MappedArchive->LongnamesMember
114  );
115 
116  if (
117  NT_SUCCESS(status) &&
118  MappedArchive->LongnamesMember.Type == LongnamesArchiveMemberType
119  )
120  {
121  MappedArchive->HasLongnamesMember = TRUE;
122  MappedArchive->LastStandardMember = &MappedArchive->LongnamesMember;
123  }
124  else
125  {
126  MappedArchive->LastStandardMember = &MappedArchive->SecondLinkerMember;
127  }
128 
129  return STATUS_SUCCESS;
130 }
131 
133  _In_opt_ PWSTR FileName,
134  _In_opt_ HANDLE FileHandle,
135  _In_ BOOLEAN ReadOnly,
136  _Out_ PPH_MAPPED_ARCHIVE MappedArchive
137  )
138 {
139  NTSTATUS status;
140 
141  status = PhMapViewOfEntireFile(
142  FileName,
143  FileHandle,
144  ReadOnly,
145  &MappedArchive->ViewBase,
146  &MappedArchive->Size
147  );
148 
149  if (NT_SUCCESS(status))
150  {
151  status = PhInitializeMappedArchive(
152  MappedArchive,
153  MappedArchive->ViewBase,
154  MappedArchive->Size
155  );
156 
157  if (!NT_SUCCESS(status))
158  {
159  NtUnmapViewOfSection(NtCurrentProcess(), MappedArchive->ViewBase);
160  }
161  }
162 
163  return status;
164 }
165 
167  _Inout_ PPH_MAPPED_ARCHIVE MappedArchive
168  )
169 {
170  return NtUnmapViewOfSection(
171  NtCurrentProcess(),
172  MappedArchive->ViewBase
173  );
174 }
175 
177  _In_ PPH_MAPPED_ARCHIVE MappedArchive,
178  _In_ PVOID Address,
179  _In_ SIZE_T Length
180  )
181 {
182  PhProbeAddress(Address, Length, MappedArchive->ViewBase, MappedArchive->Size, 1);
183 }
184 
194  _In_ PPH_MAPPED_ARCHIVE_MEMBER Member,
195  _Out_ PPH_MAPPED_ARCHIVE_MEMBER NextMember
196  )
197 {
198  PIMAGE_ARCHIVE_MEMBER_HEADER nextHeader;
199 
200  nextHeader = (PIMAGE_ARCHIVE_MEMBER_HEADER)PTR_ADD_OFFSET(
201  Member->Data,
202  Member->Size
203  );
204 
205  // 2 byte alignment.
206  if ((ULONG_PTR)nextHeader & 0x1)
207  nextHeader = (PIMAGE_ARCHIVE_MEMBER_HEADER)PTR_ADD_OFFSET(nextHeader, 1);
208 
210  Member->MappedArchive,
211  nextHeader,
212  NextMember
213  );
214 }
215 
217  _In_ PPH_MAPPED_ARCHIVE MappedArchive,
218  _In_ PIMAGE_ARCHIVE_MEMBER_HEADER Header,
219  _Out_ PPH_MAPPED_ARCHIVE_MEMBER Member
220  )
221 {
222  WCHAR integerString[11];
223  ULONG64 size;
224  PH_STRINGREF string;
225  PWSTR digit;
226  PSTR slash;
227 
228  if ((ULONG_PTR)Header >= (ULONG_PTR)MappedArchive->ViewBase + MappedArchive->Size)
229  return STATUS_NO_MORE_ENTRIES;
230 
231  __try
232  {
233  PhpMappedArchiveProbe(MappedArchive, Header, sizeof(IMAGE_ARCHIVE_MEMBER_HEADER));
234  }
235  __except (EXCEPTION_EXECUTE_HANDLER)
236  {
237  return GetExceptionCode();
238  }
239 
240  Member->MappedArchive = MappedArchive;
241  Member->Header = Header;
242  Member->Data = PTR_ADD_OFFSET(Header, sizeof(IMAGE_ARCHIVE_MEMBER_HEADER));
243  Member->Type = NormalArchiveMemberType;
244 
245  // Read the size string, terminate it after the last digit and parse it.
246 
247  if (!PhCopyStringZFromBytes(Header->Size, 10, integerString, 11, NULL))
248  return STATUS_INVALID_PARAMETER;
249 
250  string.Buffer = integerString;
251  string.Length = 0;
252  digit = string.Buffer;
253 
254  while (iswdigit(*digit++))
255  string.Length += sizeof(WCHAR);
256 
257  if (!PhStringToInteger64(&string, 10, &size))
258  return STATUS_INVALID_PARAMETER;
259 
260  Member->Size = (ULONG)size;
261 
262  __try
263  {
264  PhpMappedArchiveProbe(MappedArchive, Member->Data, Member->Size);
265  }
266  __except (EXCEPTION_EXECUTE_HANDLER)
267  {
268  return GetExceptionCode();
269  }
270 
271  // Parse the name.
272 
273  if (!PhCopyBytesZ(Header->Name, 16, Member->NameBuffer, 20, NULL))
274  return STATUS_INVALID_PARAMETER;
275 
276  Member->Name = Member->NameBuffer;
277 
278  slash = strchr(Member->NameBuffer, '/');
279 
280  if (!slash)
281  return STATUS_INVALID_PARAMETER;
282 
283  // Special names:
284  // * If the slash is the first character, then this is a linker member.
285  // * If there is a slash after the slash which is a first character, then
286  // this is the longnames member.
287  // * If there are digits after the slash, then the real name is stored
288  // in the longnames member.
289 
290  if (slash == Member->NameBuffer)
291  {
292  if (Member->NameBuffer[1] == '/')
293  {
294  // Longnames member. Set the name to "/".
295  Member->NameBuffer[0] = '/';
296  Member->NameBuffer[1] = 0;
297 
298  Member->Type = LongnamesArchiveMemberType;
299  }
300  else
301  {
302  // Linker member. Set the name to "".
303  Member->NameBuffer[0] = 0;
304 
305  Member->Type = LinkerArchiveMemberType;
306  }
307  }
308  else
309  {
310  if (isdigit(slash[1]))
311  {
312  PSTR digita;
313  ULONG64 offset64;
314  ULONG offset;
315 
316  // The name is stored in the longnames member.
317  // Note: we make sure we have the longnames member first.
318 
319  if (!MappedArchive->LongnamesMember.Header)
320  return STATUS_INVALID_PARAMETER;
321 
322  // Find the last digit and null terminate the string there.
323 
324  digita = slash + 2;
325 
326  while (isdigit(*digita))
327  digita++;
328 
329  *digita = 0;
330 
331  // Parse the offset and make sure it lies within the
332  // longnames member.
333 
334  if (!PhCopyStringZFromBytes(slash + 1, -1, integerString, 11, NULL))
335  return STATUS_INVALID_PARAMETER;
336  PhInitializeStringRefLongHint(&string, integerString);
337  if (!PhStringToInteger64(&string, 10, &offset64))
338  return STATUS_INVALID_PARAMETER;
339 
340  offset = (ULONG)offset64;
341 
342  if (offset >= MappedArchive->LongnamesMember.Size)
343  return STATUS_INVALID_PARAMETER;
344 
345  // TODO: Probe the name.
346  Member->Name = (PSTR)PTR_ADD_OFFSET(MappedArchive->LongnamesMember.Data, offset);
347  }
348  else
349  {
350  // Null terminate the string.
351  slash[0] = 0;
352  }
353  }
354 
355  return STATUS_SUCCESS;
356 }
357 
359  _In_ PPH_MAPPED_ARCHIVE_MEMBER Member
360  )
361 {
362  PIMAGE_FILE_HEADER header;
363 
364  header = (PIMAGE_FILE_HEADER)Member->Data;
365 
366  return header->Machine != IMAGE_FILE_MACHINE_UNKNOWN;
367 }
368 
370  _In_ PPH_MAPPED_ARCHIVE_MEMBER Member,
372  )
373 {
374  IMPORT_OBJECT_HEADER *importHeader;
375 
376  importHeader = (IMPORT_OBJECT_HEADER *)Member->Data;
377 
378  if (Member->Type != NormalArchiveMemberType)
379  return STATUS_INVALID_PARAMETER;
380  if (
381  importHeader->Sig1 != IMAGE_FILE_MACHINE_UNKNOWN ||
382  importHeader->Sig2 != IMPORT_OBJECT_HDR_SIG2
383  )
384  return STATUS_INVALID_PARAMETER;
385 
386  Entry->Type = (BYTE)importHeader->Type;
387  Entry->NameType = (BYTE)importHeader->NameType;
388  Entry->Machine = importHeader->Machine;
389 
390  // TODO: Probe the name.
391  Entry->Name = (PSTR)PTR_ADD_OFFSET(importHeader, sizeof(IMPORT_OBJECT_HEADER));
392  Entry->DllName = (PSTR)PTR_ADD_OFFSET(Entry->Name, strlen(Entry->Name) + 1);
393 
394  // Ordinal/NameHint are union'ed, so these statements are exactly the same.
395  // It's there in case this changes in the future.
396  if (Entry->NameType == IMPORT_OBJECT_ORDINAL)
397  {
398  Entry->Ordinal = importHeader->Ordinal;
399  }
400  else
401  {
402  Entry->NameHint = importHeader->Hint;
403  }
404 
405  return STATUS_SUCCESS;
406 }