VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 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 93115 2022-01-01 11:31:46Z 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-2022 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 uRva += cbThis;
165 pvBuf = (uint8_t *)pvBuf + cbThis;
166 }
167
168 /* next section */
169 iSh++;
170 if ((unsigned)iSh >= pThis->cSections)
171 return Failed(pThis, "RVA %#x LB %u is outside the image", uRvaOrg, cbToReadOrg);
172 uSectRva = pThis->paSections[iSh].VirtualAddress;
173 offSectRaw = pThis->paSections[iSh].PointerToRawData;
174 cbSectRaw = pThis->paSections[iSh].SizeOfRawData;
175 if ((unsigned)iSh + 1 < pThis->cSections)
176 cbSectMax = pThis->paSections[iSh + 1].VirtualAddress - uSectRva;
177 else
178 cbSectMax = pThis->paSections[iSh].Misc.VirtualSize;
179 }
180}
181
182
183static bool ReadStringAtRva(MYIMAGE *pThis, uint32_t uRva, char *pszBuf, size_t cbMax)
184{
185 uint32_t const uRvaOrg = uRva;
186
187 /*
188 * Try read the whole string at once.
189 */
190 uint32_t cbImage = pThis->f32Bit
191 ? pThis->Hdrs.Nt32.OptionalHeader.SizeOfImage
192 : pThis->Hdrs.Nt64.OptionalHeader.SizeOfImage;
193 uint32_t cbThis = uRva < cbImage ? cbImage - uRva : 1;
194 if (cbThis > cbMax)
195 cbThis = (uint32_t)cbMax;
196 if (!ReadAtRva(pThis, uRva, pszBuf, cbThis))
197 return false;
198 if (memchr(pszBuf, 0, cbThis) != NULL)
199 return true;
200
201 /*
202 * Read more, byte-by-byte.
203 */
204 for (;;)
205 {
206 cbMax -= cbThis;
207 if (!cbMax)
208 return Failed(pThis, "String to long at %#x", uRvaOrg);
209 pszBuf += cbThis;
210 uRva += cbThis;
211
212 cbThis = 1;
213 if (!ReadAtRva(pThis, uRva, pszBuf, cbThis))
214 return false;
215 if (!*pszBuf)
216 return true;
217 }
218}
219
220
221static void *ReadAtRvaAlloc(MYIMAGE *pThis, uint32_t uRva, size_t cbToRead)
222{
223 void *pvBuf = malloc(cbToRead);
224 if (pvBuf)
225 {
226 if (ReadAtRva(pThis, uRva, pvBuf, cbToRead))
227 return pvBuf;
228 free(pvBuf);
229 }
230 else
231 Failed(pThis, "Out of memory!");
232 return NULL;
233}
234
235
236static bool ParseAndCheckImports(MYIMAGE *pThis, const char **papszAllowed, unsigned cAllowed)
237{
238 /*
239 * Do we have an import directory? If so, read it.
240 */
241 IMAGE_DATA_DIRECTORY ImpDir = pThis->f32Bit
242 ? pThis->Hdrs.Nt32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
243 : pThis->Hdrs.Nt64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
244 if (ImpDir.Size == 0)
245 return true;
246
247 uint32_t cImps = ImpDir.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
248 if (cImps * sizeof(IMAGE_IMPORT_DESCRIPTOR) != ImpDir.Size)
249 return Failed(pThis, "Import directory size is not a multiple of IMAGE_IMPORT_DESCRIPTOR: %#x", ImpDir.Size);
250
251 PIMAGE_IMPORT_DESCRIPTOR paImps = (PIMAGE_IMPORT_DESCRIPTOR)ReadAtRvaAlloc(pThis, ImpDir.VirtualAddress, ImpDir.Size);
252 if (!paImps)
253 return false;
254
255 /* There is usually an empty entry at the end. */
256 if ( paImps[cImps - 1].Name == 0
257 || paImps[cImps - 1].FirstThunk == 0)
258 cImps--;
259
260 /*
261 * Do the processing.
262 */
263 bool fRc = true;
264 for (uint32_t i = 0; i < cImps; i++)
265 {
266 /* Read the import name string. */
267 char szName[128];
268 if (!ReadStringAtRva(pThis, paImps[i].Name, szName, sizeof(szName)))
269 {
270 fRc = false;
271 break;
272 }
273
274 /* Check it against the list of allowed DLLs. */
275 bool fFound = false;
276 unsigned j = cAllowed;
277 while (j-- > 0)
278 if (stricmp(papszAllowed[j], szName) == 0)
279 {
280 fFound = true;
281 break;
282 }
283 if (!fFound)
284 fRc = Failed(pThis, "Illegal import: '%s'", szName);
285 }
286
287 free(paImps);
288 return fRc;
289}
290
291
292static int usage(const char *argv0)
293{
294 printf("usage: %s --image <image> [allowed-dll [..]]\n", argv0);
295 return RTEXITCODE_SUCCESS;
296}
297
298
299int main(int argc, char **argv)
300{
301 /*
302 * Parse arguments.
303 */
304 const char *pszImage = NULL;
305 const char **papszAllowed = (const char **)calloc(argc, sizeof(const char *));
306 unsigned cAllowed = 0;
307
308 for (int i = 1; i < argc; i++)
309 {
310 const char *psz = argv[i];
311 if (*psz == '-')
312 {
313 if (!strcmp(psz, "--image") || !strcmp(psz, "-i"))
314 {
315 if (++i >= argc)
316 {
317 fprintf(stderr, "syntax error: File name expected after '%s'.\n", psz);
318 return RTEXITCODE_SYNTAX;
319 }
320 pszImage = argv[i];
321 }
322 else if ( !strcmp(psz, "--help")
323 || !strcmp(psz, "-help")
324 || !strcmp(psz, "-h")
325 || !strcmp(psz, "-?") )
326 return usage(argv[0]);
327 else if ( !strcmp(psz, "--version")
328 || !strcmp(psz, "-V"))
329 {
330 printf("$Revision: 93115 $\n");
331 return RTEXITCODE_SUCCESS;
332 }
333 else
334 {
335 fprintf(stderr, "syntax error: Unknown option '%s'.\n", psz);
336 return RTEXITCODE_SYNTAX;
337 }
338 }
339 else
340 papszAllowed[cAllowed++] = argv[i];
341 }
342
343 if (!pszImage)
344 {
345 fprintf(stderr, "syntax error: No input file specified.\n");
346 return RTEXITCODE_SYNTAX;
347 }
348
349 /*
350 * Open the image and process headers.
351 */
352 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
353 MYIMAGE MyImage;
354 memset(&MyImage, 0, sizeof(MyImage));
355 MyImage.pszImage = pszImage;
356 MyImage.pFile = fopen(pszImage, "rb");
357 if (MyImage.pFile)
358 {
359 if ( ReadPeHeaders(&MyImage)
360 && ParseAndCheckImports(&MyImage, papszAllowed, cAllowed))
361 rcExit = RTEXITCODE_SUCCESS;
362
363 fclose(MyImage.pFile);
364 free(MyImage.paSections);
365 }
366 else
367 Failed(&MyImage, "Failed to open image for binary reading.");
368
369 return rcExit;
370}
371
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