VirtualBox

source: vbox/trunk/src/bldprogs/VBoxCheckImports.cpp@ 72331

Last change on this file since 72331 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.8 KB
Line 
1/* $Id: VBoxCheckImports.cpp 69500 2017-10-28 15:14:05Z vboxsync $ */
2/** @file
3 * IPRT - Checks that a windows image only imports from a given set of DLLs.
4 */
5
6/*
7 * Copyright (C) 2012-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <iprt/formats/mz.h>
23#include <iprt/formats/pecoff.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28
29/*********************************************************************************************************************************
30* Structures and Typedefs *
31*********************************************************************************************************************************/
32typedef struct
33{
34 const char *pszImage;
35 FILE *pFile;
36 bool f32Bit;
37 union
38 {
39 IMAGE_NT_HEADERS32 Nt32;
40 IMAGE_NT_HEADERS64 Nt64;
41 } Hdrs;
42
43 uint32_t cSections;
44 PIMAGE_SECTION_HEADER paSections;
45} MYIMAGE;
46
47
48static bool Failed(MYIMAGE *pThis, const char *pszFmt, ...)
49{
50 va_list va;
51 fprintf(stderr, "error '%s': ", pThis->pszImage);
52 va_start(va, pszFmt);
53 vfprintf(stderr, pszFmt, va);
54 va_end(va);
55 fprintf(stderr, "\n");
56 return false;
57}
58
59
60static bool ReadPeHeaders(MYIMAGE *pThis)
61{
62 /*
63 * MZ header.
64 */
65 IMAGE_DOS_HEADER MzHdr;
66 if (fread(&MzHdr, sizeof(MzHdr), 1, pThis->pFile) != 1)
67 return Failed(pThis, "Reading DOS header");
68
69 if (MzHdr.e_magic != IMAGE_DOS_SIGNATURE)
70 return Failed(pThis, "No MZ magic (found %#x)", MzHdr.e_magic);
71
72 if (fseek(pThis->pFile, MzHdr.e_lfanew, SEEK_SET) != 0)
73 return Failed(pThis, "Seeking to %#lx", (unsigned long)MzHdr.e_lfanew);
74
75 /*
76 * NT signature + file header.
77 */
78 if (fread(&pThis->Hdrs.Nt32, offsetof(IMAGE_NT_HEADERS32, OptionalHeader), 1, pThis->pFile) != 1)
79 return Failed(pThis, "Reading NT file header");
80
81 if (pThis->Hdrs.Nt32.Signature != IMAGE_NT_SIGNATURE)
82 return Failed(pThis, "No PE magic (found %#x)", pThis->Hdrs.Nt32.Signature);
83
84 if (pThis->Hdrs.Nt32.FileHeader.SizeOfOptionalHeader == sizeof(pThis->Hdrs.Nt32.OptionalHeader))
85 pThis->f32Bit = true;
86 else if (pThis->Hdrs.Nt32.FileHeader.SizeOfOptionalHeader == sizeof(pThis->Hdrs.Nt64.OptionalHeader))
87 pThis->f32Bit = false;
88 else
89 return Failed(pThis, "Unsupported SizeOfOptionalHeaders value: %#x",
90 pThis->Hdrs.Nt32.FileHeader.SizeOfOptionalHeader);
91
92 /*
93 * NT optional header.
94 */
95 if (fread(&pThis->Hdrs.Nt32.OptionalHeader, pThis->Hdrs.Nt32.FileHeader.SizeOfOptionalHeader, 1, pThis->pFile) != 1)
96 return Failed(pThis, "Reading NT optional header");
97
98 if ( pThis->Hdrs.Nt32.OptionalHeader.Magic
99 != (pThis->f32Bit ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC) )
100 return Failed(pThis, "Bad optional header magic: %#x", pThis->Hdrs.Nt32.OptionalHeader.Magic);
101
102 uint32_t NumberOfRvaAndSizes = pThis->f32Bit
103 ? pThis->Hdrs.Nt32.OptionalHeader.NumberOfRvaAndSizes
104 : pThis->Hdrs.Nt64.OptionalHeader.NumberOfRvaAndSizes;
105 if (NumberOfRvaAndSizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
106 return Failed(pThis, "Unsupported NumberOfRvaAndSizes value: %#x", NumberOfRvaAndSizes);
107
108 /*
109 * Read the section table.
110 */
111 pThis->cSections = pThis->Hdrs.Nt32.FileHeader.NumberOfSections;
112 if (!pThis->cSections)
113 return Failed(pThis, "No sections in image!");
114 pThis->paSections = (PIMAGE_SECTION_HEADER)calloc(sizeof(pThis->paSections[0]), pThis->cSections);
115 if (!pThis->paSections)
116 return Failed(pThis, "Out of memory!");
117 if (fread(pThis->paSections, sizeof(pThis->paSections[0]), pThis->cSections, pThis->pFile) != pThis->cSections)
118 return Failed(pThis, "Reading NT section headers");
119
120
121 return true;
122}
123
124
125static bool ReadAtRva(MYIMAGE *pThis, uint32_t uRva, void *pvBuf, size_t cbToRead)
126{
127 unsigned const uRvaOrg = uRva;
128 size_t const cbToReadOrg = cbToRead;
129
130 /*
131 * Header section.
132 */
133 int iSh = -1;
134 uint32_t uSectRva = 0;
135 uint32_t offSectRaw = 0;
136 uint32_t cbSectRaw = pThis->f32Bit
137 ? pThis->Hdrs.Nt32.OptionalHeader.SizeOfHeaders
138 : pThis->Hdrs.Nt64.OptionalHeader.SizeOfHeaders;
139 uint32_t cbSectMax = pThis->paSections[0].VirtualAddress;
140
141 for (;;)
142 {
143 /* Read if we've got a match. */
144 uint32_t off = uRva - uSectRva;
145 if (off < cbSectMax)
146 {
147 uint32_t cbThis = cbSectMax - off;
148 if (cbThis > cbToRead)
149 cbThis = (uint32_t)cbToRead;
150
151 memset(pvBuf, 0, cbThis);
152
153 if (off < cbSectRaw)
154 {
155 if (fseek(pThis->pFile, offSectRaw + off, SEEK_SET) != 0)
156 return Failed(pThis, "Seeking to %#x", offSectRaw + off);
157 if (fread(pvBuf, RT_MIN(cbThis, cbSectRaw - off), 1, pThis->pFile) != 1)
158 return Failed(pThis, "Reading %u bytes at %#x", RT_MIN(cbThis, cbSectRaw - off), offSectRaw + off);
159 }
160
161 cbToRead -= cbThis;
162 if (!cbToRead)
163 return true;
164 pvBuf = (uint8_t *)pvBuf + cbThis;
165 }
166
167 /* next section */
168 iSh++;
169 if ((unsigned)iSh >= pThis->cSections)
170 return Failed(pThis, "RVA %#x LB %u is outside the image", uRvaOrg, cbToReadOrg);
171 uSectRva = pThis->paSections[iSh].VirtualAddress;
172 offSectRaw = pThis->paSections[iSh].PointerToRawData;
173 cbSectRaw = pThis->paSections[iSh].SizeOfRawData;
174 if ((unsigned)iSh + 1 < pThis->cSections)
175 cbSectMax = pThis->paSections[iSh + 1].VirtualAddress - uSectRva;
176 else
177 cbSectMax = pThis->paSections[iSh].Misc.VirtualSize;
178 }
179}
180
181
182static bool ReadStringAtRva(MYIMAGE *pThis, uint32_t uRva, char *pszBuf, size_t cbMax)
183{
184 uint32_t const uRvaOrg = uRva;
185
186 /*
187 * Try read the whole string at once.
188 */
189 uint32_t cbImage = pThis->f32Bit
190 ? pThis->Hdrs.Nt32.OptionalHeader.SizeOfImage
191 : pThis->Hdrs.Nt64.OptionalHeader.SizeOfImage;
192 uint32_t cbThis = uRva < cbImage ? cbImage - uRva : 1;
193 if (cbThis > cbMax)
194 cbThis = (uint32_t)cbMax;
195 if (!ReadAtRva(pThis, uRva, pszBuf, cbThis))
196 return false;
197 if (memchr(pszBuf, 0, cbThis) != NULL)
198 return true;
199
200 /*
201 * Read more, byte-by-byte.
202 */
203 for (;;)
204 {
205 cbMax -= cbThis;
206 if (!cbMax)
207 return Failed(pThis, "String to long at %#x", uRvaOrg);
208 pszBuf += cbThis;
209 uRva += cbThis;
210
211 cbThis = 1;
212 if (!ReadAtRva(pThis, uRva, pszBuf, cbThis))
213 return false;
214 if (!*pszBuf)
215 return true;
216 }
217}
218
219
220static void *ReadAtRvaAlloc(MYIMAGE *pThis, uint32_t uRva, size_t cbToRead)
221{
222 void *pvBuf = malloc(cbToRead);
223 if (pvBuf)
224 {
225 if (ReadAtRva(pThis, uRva, pvBuf, cbToRead))
226 return pvBuf;
227 free(pvBuf);
228 }
229 else
230 Failed(pThis, "Out of memory!");
231 return NULL;
232}
233
234
235static bool ParseAndCheckImports(MYIMAGE *pThis, const char **papszAllowed, unsigned cAllowed)
236{
237 /*
238 * Do we have an import directory? If so, read it.
239 */
240 IMAGE_DATA_DIRECTORY ImpDir = pThis->f32Bit
241 ? pThis->Hdrs.Nt32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
242 : pThis->Hdrs.Nt64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
243 if (ImpDir.Size == 0)
244 return true;
245
246 uint32_t cImps = ImpDir.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
247 if (cImps * sizeof(IMAGE_IMPORT_DESCRIPTOR) != ImpDir.Size)
248 return Failed(pThis, "Import directory size is not a multiple of IMAGE_IMPORT_DESCRIPTOR: %#x", ImpDir.Size);
249
250 PIMAGE_IMPORT_DESCRIPTOR paImps = (PIMAGE_IMPORT_DESCRIPTOR)ReadAtRvaAlloc(pThis, ImpDir.VirtualAddress, ImpDir.Size);
251 if (!paImps)
252 return false;
253
254 /* There is usually an empty entry at the end. */
255 if ( paImps[cImps - 1].Name == 0
256 || paImps[cImps - 1].FirstThunk == 0)
257 cImps--;
258
259 /*
260 * Do the processing.
261 */
262 bool fRc = true;
263 for (uint32_t i = 0; i < cImps; i++)
264 {
265 /* Read the import name string. */
266 char szName[128];
267 if (!ReadStringAtRva(pThis, paImps[i].Name, szName, sizeof(szName)))
268 {
269 fRc = false;
270 break;
271 }
272
273 /* Check it against the list of allowed DLLs. */
274 bool fFound = false;
275 unsigned j = cAllowed;
276 while (j-- > 0)
277 if (stricmp(papszAllowed[j], szName) == 0)
278 {
279 fFound = true;
280 break;
281 }
282 if (!fFound)
283 fRc = Failed(pThis, "Illegal import: '%s'", szName);
284 }
285
286 free(paImps);
287 return fRc;
288}
289
290
291static int usage(const char *argv0)
292{
293 printf("usage: %s --image <image> [allowed-dll [..]]\n", argv0);
294 return RTEXITCODE_SUCCESS;
295}
296
297
298int main(int argc, char **argv)
299{
300 /*
301 * Parse arguments.
302 */
303 const char *pszImage = NULL;
304 const char **papszAllowed = (const char **)calloc(argc, sizeof(const char *));
305 unsigned cAllowed = 0;
306
307 for (int i = 1; i < argc; i++)
308 {
309 const char *psz = argv[i];
310 if (*psz == '-')
311 {
312 if (!strcmp(psz, "--image") || !strcmp(psz, "-i"))
313 {
314 if (++i >= argc)
315 {
316 fprintf(stderr, "syntax error: File name expected after '%s'.\n", psz);
317 return RTEXITCODE_SYNTAX;
318 }
319 pszImage = argv[i];
320 }
321 else if ( !strcmp(psz, "--help")
322 || !strcmp(psz, "-help")
323 || !strcmp(psz, "-h")
324 || !strcmp(psz, "-?") )
325 return usage(argv[0]);
326 else if ( !strcmp(psz, "--version")
327 || !strcmp(psz, "-V"))
328 {
329 printf("$Revision: 69500 $\n");
330 return RTEXITCODE_SUCCESS;
331 }
332 else
333 {
334 fprintf(stderr, "syntax error: Unknown option '%s'.\n", psz);
335 return RTEXITCODE_SYNTAX;
336 }
337 }
338 else
339 papszAllowed[cAllowed++] = argv[i];
340 }
341
342 if (!pszImage)
343 {
344 fprintf(stderr, "syntax error: No input file specified.\n");
345 return RTEXITCODE_SYNTAX;
346 }
347
348 /*
349 * Open the image and process headers.
350 */
351 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
352 MYIMAGE MyImage;
353 memset(&MyImage, 0, sizeof(MyImage));
354 MyImage.pszImage = pszImage;
355 MyImage.pFile = fopen(pszImage, "rb");
356 if (MyImage.pFile)
357 {
358 if ( ReadPeHeaders(&MyImage)
359 && ParseAndCheckImports(&MyImage, papszAllowed, cAllowed))
360 rcExit = RTEXITCODE_SUCCESS;
361
362 fclose(MyImage.pFile);
363 free(MyImage.paSections);
364 }
365 else
366 Failed(&MyImage, "Failed to open image for binary reading.");
367
368 return rcExit;
369}
370
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette