VirtualBox

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

Last change on this file since 106078 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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