VirtualBox

source: vbox/trunk/src/bldprogs/VBoxPeSetVersion.cpp@ 74938

Last change on this file since 74938 was 73097, checked in by vboxsync, 6 years ago

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.9 KB
Line 
1/* $Id: VBoxPeSetVersion.cpp 73097 2018-07-12 21:06:33Z vboxsync $ */
2/** @file
3 * IPRT - Change the OS and SubSystem version to value suitable for NT v3.1.
4 *
5 * Also make sure the IAT is writable, since NT v3.1 expects this. These are
6 * tricks necessary to make binaries created by newer Visual C++ linkers work
7 * on ancient NT version like W2K, NT4 and NT 3.x.
8 */
9
10/*
11 * Copyright (C) 2012-2017 Oracle Corporation
12 *
13 * This file is part of VirtualBox Open Source Edition (OSE), as
14 * available from http://www.virtualbox.org. This file is free software;
15 * you can redistribute it and/or modify it under the terms of the GNU
16 * General Public License (GPL) as published by the Free Software
17 * Foundation, in version 2 as it comes in the "COPYING" file of the
18 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
19 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
20 */
21
22
23/*********************************************************************************************************************************
24* Header Files *
25*********************************************************************************************************************************/
26#include <iprt/formats/mz.h>
27#include <iprt/formats/pecoff.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31
32
33/*********************************************************************************************************************************
34* Defined Constants And Macros *
35*********************************************************************************************************************************/
36#define MK_VER(a_uHi, a_uLo) ( ((a_uHi) << 8) | (a_uLo))
37
38
39/*********************************************************************************************************************************
40* Global Variables *
41*********************************************************************************************************************************/
42static const char *g_pszFilename;
43static unsigned g_cVerbosity = 0;
44
45
46static int Error(const char *pszFormat, ...)
47{
48 va_list va;
49 va_start(va, pszFormat);
50 char szTmp[1024];
51 _vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
52 va_end(va);
53 fprintf(stderr, "VBoxPeSetVersion: %s: error: %s\n", g_pszFilename, szTmp);
54 return RTEXITCODE_FAILURE;
55}
56
57
58static void Info(unsigned iLevel, const char *pszFormat, ...)
59{
60 if (iLevel <= g_cVerbosity)
61 {
62 va_list va;
63 va_start(va, pszFormat);
64 char szTmp[1024];
65 _vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
66 va_end(va);
67 fprintf(stderr, "VBoxPeSetVersion: %s: info: %s\n", g_pszFilename, szTmp);
68 }
69}
70
71
72static int UpdateFile(FILE *pFile, unsigned uNtVersion, PIMAGE_SECTION_HEADER *ppaShdr)
73{
74 /*
75 * Locate and read the PE header.
76 *
77 * Note! We'll be reading the 64-bit size even for 32-bit since the difference
78 * is 16 bytes, which is less than a section header, so it won't be a problem.
79 */
80 unsigned long offNtHdrs;
81 {
82 IMAGE_DOS_HEADER MzHdr;
83 if (fread(&MzHdr, sizeof(MzHdr), 1, pFile) != 1)
84 return Error("Failed to read MZ header: %s", strerror(errno));
85 if (MzHdr.e_magic != IMAGE_DOS_SIGNATURE)
86 return Error("Invalid MZ magic: %#x", MzHdr.e_magic);
87 offNtHdrs = MzHdr.e_lfanew;
88 }
89
90 if (fseek(pFile, offNtHdrs, SEEK_SET) != 0)
91 return Error("Failed to seek to PE header at %#lx: %s", offNtHdrs, strerror(errno));
92 union
93 {
94 IMAGE_NT_HEADERS32 x32;
95 IMAGE_NT_HEADERS64 x64;
96 } NtHdrs,
97 NtHdrsNew;
98 if (fread(&NtHdrs, sizeof(NtHdrs), 1, pFile) != 1)
99 return Error("Failed to read PE header at %#lx: %s", offNtHdrs, strerror(errno));
100
101 /*
102 * Validate it a little bit.
103 */
104 if (NtHdrs.x32.Signature != IMAGE_NT_SIGNATURE)
105 return Error("Invalid PE signature: %#x", NtHdrs.x32.Signature);
106 uint32_t cbNewHdrs;
107 if (NtHdrs.x32.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
108 {
109 if (NtHdrs.x64.FileHeader.SizeOfOptionalHeader != sizeof(NtHdrs.x64.OptionalHeader))
110 return Error("Invalid optional header size: %#x", NtHdrs.x64.FileHeader.SizeOfOptionalHeader);
111 if (NtHdrs.x64.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC)
112 return Error("Invalid optional header magic: %#x", NtHdrs.x64.OptionalHeader.Magic);
113 if (!uNtVersion)
114 uNtVersion = MK_VER(5, 2);
115 else if (uNtVersion < MK_VER(5, 2))
116 return Error("Selected version is too old for AMD64: %u.%u", uNtVersion >> 8, uNtVersion & 0xff);
117 cbNewHdrs = sizeof(NtHdrsNew.x64);
118 }
119 else if (NtHdrs.x32.FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
120 return Error("Not I386 or AMD64 machine: %#x", NtHdrs.x32.FileHeader.Machine);
121 else
122 {
123 if (NtHdrs.x32.FileHeader.SizeOfOptionalHeader != sizeof(NtHdrs.x32.OptionalHeader))
124 return Error("Invalid optional header size: %#x", NtHdrs.x32.FileHeader.SizeOfOptionalHeader);
125 if (NtHdrs.x32.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)
126 return Error("Invalid optional header magic: %#x", NtHdrs.x32.OptionalHeader.Magic);
127 if (!uNtVersion)
128 uNtVersion = MK_VER(3, 10);
129 cbNewHdrs = sizeof(NtHdrsNew.x32);
130 }
131
132 /*
133 * Do the header modifications.
134 */
135 memcpy(&NtHdrsNew, &NtHdrs, sizeof(NtHdrsNew));
136 NtHdrsNew.x32.OptionalHeader.MajorOperatingSystemVersion = uNtVersion >> 8;
137 NtHdrsNew.x32.OptionalHeader.MinorOperatingSystemVersion = uNtVersion & 0xff;
138 NtHdrsNew.x32.OptionalHeader.MajorSubsystemVersion = uNtVersion >> 8;
139 NtHdrsNew.x32.OptionalHeader.MinorSubsystemVersion = uNtVersion & 0xff;
140 AssertCompileMembersAtSameOffset(IMAGE_NT_HEADERS32, OptionalHeader.MajorOperatingSystemVersion, IMAGE_NT_HEADERS64, OptionalHeader.MajorOperatingSystemVersion);
141 AssertCompileMembersAtSameOffset(IMAGE_NT_HEADERS32, OptionalHeader.MinorOperatingSystemVersion, IMAGE_NT_HEADERS64, OptionalHeader.MinorOperatingSystemVersion);
142 AssertCompileMembersAtSameOffset(IMAGE_NT_HEADERS32, OptionalHeader.MajorSubsystemVersion, IMAGE_NT_HEADERS64, OptionalHeader.MajorSubsystemVersion);
143 AssertCompileMembersAtSameOffset(IMAGE_NT_HEADERS32, OptionalHeader.MinorSubsystemVersion, IMAGE_NT_HEADERS64, OptionalHeader.MinorSubsystemVersion);
144
145 if (uNtVersion <= MK_VER(3, 50))
146 {
147 NtHdrsNew.x32.OptionalHeader.MajorOperatingSystemVersion = 1;
148 NtHdrsNew.x32.OptionalHeader.MinorOperatingSystemVersion = 0;
149 AssertCompileMembersAtSameOffset(IMAGE_NT_HEADERS32, OptionalHeader.MajorOperatingSystemVersion, IMAGE_NT_HEADERS64, OptionalHeader.MajorOperatingSystemVersion);
150 AssertCompileMembersAtSameOffset(IMAGE_NT_HEADERS32, OptionalHeader.MinorOperatingSystemVersion, IMAGE_NT_HEADERS64, OptionalHeader.MinorOperatingSystemVersion);
151 }
152
153 if (memcmp(&NtHdrsNew, &NtHdrs, sizeof(NtHdrs)))
154 {
155 /** @todo calc checksum. */
156 NtHdrsNew.x32.OptionalHeader.CheckSum = 0;
157 AssertCompileMembersAtSameOffset(IMAGE_NT_HEADERS32, OptionalHeader.MinorOperatingSystemVersion, IMAGE_NT_HEADERS64, OptionalHeader.MinorOperatingSystemVersion);
158
159 if ( NtHdrsNew.x32.OptionalHeader.MajorOperatingSystemVersion != NtHdrs.x32.OptionalHeader.MajorOperatingSystemVersion
160 || NtHdrsNew.x32.OptionalHeader.MinorOperatingSystemVersion != NtHdrs.x32.OptionalHeader.MinorOperatingSystemVersion)
161 Info(1,"OperatingSystemVersion %u.%u -> %u.%u",
162 NtHdrs.x32.OptionalHeader.MajorOperatingSystemVersion, NtHdrs.x32.OptionalHeader.MinorOperatingSystemVersion,
163 NtHdrsNew.x32.OptionalHeader.MajorOperatingSystemVersion, NtHdrsNew.x32.OptionalHeader.MinorOperatingSystemVersion);
164 if ( NtHdrsNew.x32.OptionalHeader.MajorSubsystemVersion != NtHdrs.x32.OptionalHeader.MajorSubsystemVersion
165 || NtHdrsNew.x32.OptionalHeader.MinorSubsystemVersion != NtHdrs.x32.OptionalHeader.MinorSubsystemVersion)
166 Info(1,"SubsystemVersion %u.%u -> %u.%u",
167 NtHdrs.x32.OptionalHeader.MajorSubsystemVersion, NtHdrs.x32.OptionalHeader.MinorSubsystemVersion,
168 NtHdrsNew.x32.OptionalHeader.MajorSubsystemVersion, NtHdrsNew.x32.OptionalHeader.MinorSubsystemVersion);
169
170 if (fseek(pFile, offNtHdrs, SEEK_SET) != 0)
171 return Error("Failed to seek to PE header at %#lx: %s", offNtHdrs, strerror(errno));
172 if (fwrite(&NtHdrsNew, cbNewHdrs, 1, pFile) != 1)
173 return Error("Failed to write PE header at %#lx: %s", offNtHdrs, strerror(errno));
174 }
175
176 /*
177 * Make the IAT writable for NT 3.1 and drop the non-cachable flag from .bss.
178 *
179 * The latter is a trick we use to prevent the linker from merging .data and .bss,
180 * because NT 3.1 does not honor Misc.VirtualSize and won't zero padd the .bss part
181 * if it's not zero padded in the file. This seemed simpler than adding zero padding.
182 */
183 if ( uNtVersion <= MK_VER(3, 10)
184 && NtHdrsNew.x32.FileHeader.NumberOfSections > 0)
185 {
186 uint32_t cbShdrs = sizeof(IMAGE_SECTION_HEADER) * NtHdrsNew.x32.FileHeader.NumberOfSections;
187 PIMAGE_SECTION_HEADER paShdrs = (PIMAGE_SECTION_HEADER)calloc(1, cbShdrs);
188 if (!paShdrs)
189 return Error("Out of memory");
190 *ppaShdr = paShdrs;
191
192 unsigned long offShdrs = offNtHdrs
193 + RT_UOFFSETOF_DYN(IMAGE_NT_HEADERS32,
194 OptionalHeader.DataDirectory[NtHdrsNew.x32.OptionalHeader.NumberOfRvaAndSizes]);
195 if (fseek(pFile, offShdrs, SEEK_SET) != 0)
196 return Error("Failed to seek to section headers at %#lx: %s", offShdrs, strerror(errno));
197 if (fread(paShdrs, cbShdrs, 1, pFile) != 1)
198 return Error("Failed to read section headers at %#lx: %s", offShdrs, strerror(errno));
199
200 bool fFoundBss = false;
201 uint32_t uRvaEnd = NtHdrsNew.x32.OptionalHeader.SizeOfImage;
202 uint32_t uRvaIat = NtHdrsNew.x32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size > 0
203 ? NtHdrsNew.x32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress : UINT32_MAX;
204 uint32_t i = NtHdrsNew.x32.FileHeader.NumberOfSections;
205 while (i-- > 0)
206 if (!(paShdrs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
207 {
208 bool fModified = false;
209 if (uRvaIat >= paShdrs[i].VirtualAddress && uRvaIat < uRvaEnd)
210 {
211 if (!(paShdrs[i].Characteristics & IMAGE_SCN_MEM_WRITE))
212 {
213 paShdrs[i].Characteristics |= IMAGE_SCN_MEM_WRITE;
214 fModified = true;
215 }
216 uRvaIat = UINT32_MAX;
217 }
218
219 if ( !fFoundBss
220 && strcmp((const char *)paShdrs[i].Name, ".bss") == 0)
221 {
222 if (paShdrs[i].Characteristics & IMAGE_SCN_MEM_NOT_CACHED)
223 {
224 paShdrs[i].Characteristics &= ~IMAGE_SCN_MEM_NOT_CACHED;
225 fModified = true;
226 }
227 fFoundBss = true;
228 }
229
230 if (fModified)
231 {
232 unsigned long offShdr = offShdrs + i * sizeof(IMAGE_SECTION_HEADER);
233 if (fseek(pFile, offShdr, SEEK_SET) != 0)
234 return Error("Failed to seek to section header #%u at %#lx: %s", i, offShdr, strerror(errno));
235 if (fwrite(&paShdrs[i], sizeof(IMAGE_SECTION_HEADER), 1, pFile) != 1)
236 return Error("Failed to write %8.8s section header header at %#lx: %s",
237 paShdrs[i].Name, offShdr, strerror(errno));
238 if (uRvaIat == UINT32_MAX && fFoundBss)
239 break;
240 }
241
242 /* Advance */
243 uRvaEnd = paShdrs[i].VirtualAddress;
244 }
245
246 }
247
248 return RTEXITCODE_SUCCESS;
249}
250
251
252static int Usage(FILE *pOutput)
253{
254 fprintf(pOutput,
255 "Usage: VBoxPeSetVersion [options] <PE-image>\n"
256 "Options:\n"
257 " -v, --verbose\n"
258 " Increases verbosity.\n"
259 " -q, --quiet\n"
260 " Quiet operation (default).\n"
261 " --nt31, --nt350, --nt351, --nt4, --w2k, --xp, --w2k3, --vista,\n"
262 " --w7, --w8, --w81, --w10\n"
263 " Which version to set. Default: --nt31\n"
264 );
265 return RTEXITCODE_SYNTAX;
266}
267
268
269/** @todo Rewrite this so it can take options and print out error messages. */
270int main(int argc, char **argv)
271{
272 /*
273 * Parse arguments.
274 * This stucks
275 */
276 unsigned uNtVersion = 0;
277 const char *pszFilename = NULL;
278 bool fAcceptOptions = true;
279 for (int i = 1; i < argc; i++)
280 {
281 const char *psz = argv[i];
282 if (fAcceptOptions && *psz == '-')
283 {
284 char ch = psz[1];
285 psz += 2;
286 if (ch == '-')
287 {
288 if (!*psz)
289 {
290 fAcceptOptions = false;
291 continue;
292 }
293
294 if (strcmp(psz, "verbose") == 0)
295 ch = 'v';
296 else if (strcmp(psz, "quiet") == 0)
297 ch = 'q';
298 else if (strcmp(psz, "help") == 0)
299 ch = 'h';
300 else if (strcmp(psz, "version") == 0)
301 ch = 'V';
302 else
303 {
304 if (strcmp(psz, "nt31") == 0)
305 uNtVersion = MK_VER(3,10);
306 else if (strcmp(psz, "nt350") == 0)
307 uNtVersion = MK_VER(3,50);
308 else if (strcmp(psz, "nt351") == 0)
309 uNtVersion = MK_VER(3,51);
310 else if (strcmp(psz, "nt4") == 0)
311 uNtVersion = MK_VER(4,0);
312 else if (strcmp(psz, "w2k") == 0)
313 uNtVersion = MK_VER(5,0);
314 else if (strcmp(psz, "xp") == 0)
315 uNtVersion = MK_VER(5,1);
316 else if (strcmp(psz, "w2k3") == 0)
317 uNtVersion = MK_VER(5,2);
318 else if (strcmp(psz, "vista") == 0)
319 uNtVersion = MK_VER(6,0);
320 else if (strcmp(psz, "w7") == 0)
321 uNtVersion = MK_VER(6,1);
322 else if (strcmp(psz, "w8") == 0)
323 uNtVersion = MK_VER(6,2);
324 else if (strcmp(psz, "w81") == 0)
325 uNtVersion = MK_VER(6,3);
326 else if (strcmp(psz, "w10") == 0)
327 uNtVersion = MK_VER(10,0);
328 else
329 {
330 fprintf(stderr, "VBoxPeSetVersion: syntax error: Unknown option: --%s\n", psz);
331 return RTEXITCODE_SYNTAX;
332 }
333 continue;
334 }
335 psz = " ";
336 }
337 do
338 {
339 switch (ch)
340 {
341 case 'q':
342 g_cVerbosity = 0;
343 break;
344 case 'v':
345 g_cVerbosity++;
346 break;
347 case 'V':
348 printf("2.0\n");
349 return RTEXITCODE_SUCCESS;
350 case 'h':
351 Usage(stdout);
352 return RTEXITCODE_SUCCESS;
353 default:
354 fprintf(stderr, "VBoxPeSetVersion: syntax error: Unknown option: -%c\n", ch ? ch : ' ');
355 return RTEXITCODE_SYNTAX;
356 }
357 } while ((ch = *psz++) != '\0');
358
359 }
360 else if (!pszFilename)
361 pszFilename = psz;
362 else
363 {
364 fprintf(stderr, "VBoxPeSetVersion: syntax error: More than one PE-image specified!\n");
365 return RTEXITCODE_SYNTAX;
366 }
367 }
368
369 if (!pszFilename)
370 {
371 fprintf(stderr, "VBoxPeSetVersion: syntax error: No PE-image specified!\n");
372 return RTEXITCODE_SYNTAX;
373 }
374 g_pszFilename = pszFilename;
375
376 /*
377 * Process the file.
378 */
379 int rcExit;
380 FILE *pFile = fopen(pszFilename, "r+b");
381 if (pFile)
382 {
383 PIMAGE_SECTION_HEADER paShdrs = NULL;
384 rcExit = UpdateFile(pFile, uNtVersion, &paShdrs);
385 if (paShdrs)
386 free(paShdrs);
387 if (fclose(pFile) != 0)
388 rcExit = Error("fclose failed on '%s': %s", pszFilename, strerror(errno));
389 }
390 else
391 rcExit = Error("Failed to open '%s' for updating: %s", pszFilename, strerror(errno));
392 return rcExit;
393}
394
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