VirtualBox

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

Last change on this file since 70772 was 70403, checked in by vboxsync, 6 years ago

Additions,ValidationKit,VBoxPeSetVersion: Force linker to split .bss out from the .data section as NT 3.1 cannot deal with that (it doesn't check the Misc.VirtualSize field and maps raw bytes up to the next section). We tell the linker .bss is uncached, and have VBoxPeSetImage undo this change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.5 KB
Line 
1/* $Id: VBoxPeSetVersion.cpp 70403 2018-01-01 15:30:04Z 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 unsigned long offNtHdrs;
78 {
79 IMAGE_DOS_HEADER MzHdr;
80 if (fread(&MzHdr, sizeof(MzHdr), 1, pFile) != 1)
81 return Error("Failed to read MZ header: %s", strerror(errno));
82 if (MzHdr.e_magic != IMAGE_DOS_SIGNATURE)
83 return Error("Invalid MZ magic: %#x", MzHdr.e_magic);
84 offNtHdrs = MzHdr.e_lfanew;
85 }
86
87 if (fseek(pFile, offNtHdrs, SEEK_SET) != 0)
88 return Error("Failed to seek to PE header at %#lx: %s", offNtHdrs, strerror(errno));
89 IMAGE_NT_HEADERS32 NtHdrs;
90 if (fread(&NtHdrs, sizeof(NtHdrs), 1, pFile) != 1)
91 return Error("Failed to read PE header at %#lx: %s", offNtHdrs, strerror(errno));
92
93 /*
94 * Validate it a little bit.
95 */
96 if (NtHdrs.Signature != IMAGE_NT_SIGNATURE)
97 return Error("Invalid PE signature: %#x", NtHdrs.Signature);
98 if (NtHdrs.FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
99 return Error("Not I386 machine: %#x", NtHdrs.FileHeader.Machine);
100 if (NtHdrs.FileHeader.SizeOfOptionalHeader != sizeof(NtHdrs.OptionalHeader))
101 return Error("Invalid optional header size: %#x", NtHdrs.FileHeader.SizeOfOptionalHeader);
102 if (NtHdrs.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)
103 return Error("Invalid optional header magic: %#x", NtHdrs.OptionalHeader.Magic);
104
105 /*
106 * Do the header modifications.
107 */
108 IMAGE_NT_HEADERS32 NtHdrsNew = NtHdrs;
109 NtHdrsNew.OptionalHeader.MajorOperatingSystemVersion = uNtVersion >> 8;
110 NtHdrsNew.OptionalHeader.MinorOperatingSystemVersion = uNtVersion & 0xff;
111 NtHdrsNew.OptionalHeader.MajorSubsystemVersion = uNtVersion >> 8;
112 NtHdrsNew.OptionalHeader.MinorSubsystemVersion = uNtVersion & 0xff;
113
114 if (uNtVersion <= MK_VER(3, 50))
115 {
116 NtHdrsNew.OptionalHeader.MajorOperatingSystemVersion = 1;
117 NtHdrsNew.OptionalHeader.MinorOperatingSystemVersion = 0;
118 }
119
120 if (memcmp(&NtHdrsNew, &NtHdrs, sizeof(NtHdrs)))
121 {
122 /** @todo calc checksum. */
123 NtHdrsNew.OptionalHeader.CheckSum = 0;
124
125 if ( NtHdrsNew.OptionalHeader.MajorOperatingSystemVersion != NtHdrs.OptionalHeader.MajorOperatingSystemVersion
126 || NtHdrsNew.OptionalHeader.MinorOperatingSystemVersion != NtHdrs.OptionalHeader.MinorOperatingSystemVersion)
127 Info(1,"OperatingSystemVersion %u.%u -> %u.%u",
128 NtHdrs.OptionalHeader.MajorOperatingSystemVersion, NtHdrs.OptionalHeader.MinorOperatingSystemVersion,
129 NtHdrsNew.OptionalHeader.MajorOperatingSystemVersion, NtHdrsNew.OptionalHeader.MinorOperatingSystemVersion);
130 if ( NtHdrsNew.OptionalHeader.MajorSubsystemVersion != NtHdrs.OptionalHeader.MajorSubsystemVersion
131 || NtHdrsNew.OptionalHeader.MinorSubsystemVersion != NtHdrs.OptionalHeader.MinorSubsystemVersion)
132 Info(1,"SubsystemVersion %u.%u -> %u.%u",
133 NtHdrs.OptionalHeader.MajorSubsystemVersion, NtHdrs.OptionalHeader.MinorSubsystemVersion,
134 NtHdrsNew.OptionalHeader.MajorSubsystemVersion, NtHdrsNew.OptionalHeader.MinorSubsystemVersion);
135
136 if (fseek(pFile, offNtHdrs, SEEK_SET) != 0)
137 return Error("Failed to seek to PE header at %#lx: %s", offNtHdrs, strerror(errno));
138 if (fwrite(&NtHdrsNew, sizeof(NtHdrsNew), 1, pFile) != 1)
139 return Error("Failed to write PE header at %#lx: %s", offNtHdrs, strerror(errno));
140 }
141
142 /*
143 * Make the IAT writable for NT 3.1 and drop the non-cachable flag from .bss.
144 *
145 * The latter is a trick we use to prevent the linker from merging .data and .bss,
146 * because NT 3.1 does not honor Misc.VirtualSize and won't zero padd the .bss part
147 * if it's not zero padded in the file. This seemed simpler than adding zero padding.
148 */
149 if ( uNtVersion <= MK_VER(3, 10)
150 && NtHdrsNew.FileHeader.NumberOfSections > 0)
151 {
152 uint32_t cbShdrs = sizeof(IMAGE_SECTION_HEADER) * NtHdrsNew.FileHeader.NumberOfSections;
153 PIMAGE_SECTION_HEADER paShdrs = (PIMAGE_SECTION_HEADER)calloc(1, cbShdrs);
154 if (!paShdrs)
155 return Error("Out of memory");
156 *ppaShdr = paShdrs;
157
158 unsigned long offShdrs = offNtHdrs
159 + RT_UOFFSETOF(IMAGE_NT_HEADERS32,
160 OptionalHeader.DataDirectory[NtHdrsNew.OptionalHeader.NumberOfRvaAndSizes]);
161 if (fseek(pFile, offShdrs, SEEK_SET) != 0)
162 return Error("Failed to seek to section headers at %#lx: %s", offShdrs, strerror(errno));
163 if (fread(paShdrs, cbShdrs, 1, pFile) != 1)
164 return Error("Failed to read section headers at %#lx: %s", offShdrs, strerror(errno));
165
166 bool fFoundBss = false;
167 uint32_t uRvaEnd = NtHdrsNew.OptionalHeader.SizeOfImage;
168 uint32_t uRvaIat = NtHdrsNew.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size > 0
169 ? NtHdrsNew.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress : UINT32_MAX;
170 uint32_t i = NtHdrsNew.FileHeader.NumberOfSections;
171 while (i-- > 0)
172 if (!(paShdrs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
173 {
174 bool fModified = false;
175 if (uRvaIat >= paShdrs[i].VirtualAddress && uRvaIat < uRvaEnd)
176 {
177 if (!(paShdrs[i].Characteristics & IMAGE_SCN_MEM_WRITE))
178 {
179 paShdrs[i].Characteristics |= IMAGE_SCN_MEM_WRITE;
180 fModified = true;
181 }
182 uRvaIat = UINT32_MAX;
183 }
184
185 if ( !fFoundBss
186 && strcmp((const char *)paShdrs[i].Name, ".bss") == 0)
187 {
188 if (paShdrs[i].Characteristics & IMAGE_SCN_MEM_NOT_CACHED)
189 {
190 paShdrs[i].Characteristics &= ~IMAGE_SCN_MEM_NOT_CACHED;
191 fModified = true;
192 }
193 fFoundBss = true;
194 }
195
196 if (fModified)
197 {
198 unsigned long offShdr = offShdrs + i * sizeof(IMAGE_SECTION_HEADER);
199 if (fseek(pFile, offShdr, SEEK_SET) != 0)
200 return Error("Failed to seek to section header #%u at %#lx: %s", i, offShdr, strerror(errno));
201 if (fwrite(&paShdrs[i], sizeof(IMAGE_SECTION_HEADER), 1, pFile) != 1)
202 return Error("Failed to write %8.8s section header header at %#lx: %s",
203 paShdrs[i].Name, offShdr, strerror(errno));
204 if (uRvaIat == UINT32_MAX && fFoundBss)
205 break;
206 }
207
208 /* Advance */
209 uRvaEnd = paShdrs[i].VirtualAddress;
210 }
211
212 }
213
214 return RTEXITCODE_SUCCESS;
215}
216
217
218static int Usage(FILE *pOutput)
219{
220 fprintf(pOutput,
221 "Usage: VBoxPeSetVersion [options] <PE-image>\n"
222 "Options:\n"
223 " -v, --verbose\n"
224 " Increases verbosity.\n"
225 " -q, --quiet\n"
226 " Quiet operation (default).\n"
227 " --nt31, --nt350, --nt351, --nt4, --w2k, --xp, --w2k3, --vista,\n"
228 " --w7, --w8, --w81, --w10\n"
229 " Which version to set. Default: --nt31\n"
230 );
231 return RTEXITCODE_SYNTAX;
232}
233
234
235/** @todo Rewrite this so it can take options and print out error messages. */
236int main(int argc, char **argv)
237{
238 /*
239 * Parse arguments.
240 * This stucks
241 */
242 unsigned uNtVersion = MK_VER(3,10);
243 const char *pszFilename = NULL;
244 bool fAcceptOptions = true;
245 for (int i = 1; i < argc; i++)
246 {
247 const char *psz = argv[i];
248 if (fAcceptOptions && *psz == '-')
249 {
250 char ch = psz[1];
251 psz += 2;
252 if (ch == '-')
253 {
254 if (!*psz)
255 {
256 fAcceptOptions = false;
257 continue;
258 }
259
260 if (strcmp(psz, "verbose") == 0)
261 ch = 'v';
262 else if (strcmp(psz, "quiet") == 0)
263 ch = 'q';
264 else if (strcmp(psz, "help") == 0)
265 ch = 'h';
266 else if (strcmp(psz, "version") == 0)
267 ch = 'V';
268 else
269 {
270 if (strcmp(psz, "nt31") == 0)
271 uNtVersion = MK_VER(3,10);
272 else if (strcmp(psz, "nt350") == 0)
273 uNtVersion = MK_VER(3,50);
274 else if (strcmp(psz, "nt351") == 0)
275 uNtVersion = MK_VER(3,51);
276 else if (strcmp(psz, "nt4") == 0)
277 uNtVersion = MK_VER(4,0);
278 else if (strcmp(psz, "w2k") == 0)
279 uNtVersion = MK_VER(5,0);
280 else if (strcmp(psz, "xp") == 0)
281 uNtVersion = MK_VER(5,1);
282 else if (strcmp(psz, "w2k3") == 0)
283 uNtVersion = MK_VER(5,2);
284 else if (strcmp(psz, "vista") == 0)
285 uNtVersion = MK_VER(6,0);
286 else if (strcmp(psz, "w7") == 0)
287 uNtVersion = MK_VER(6,1);
288 else if (strcmp(psz, "w8") == 0)
289 uNtVersion = MK_VER(6,2);
290 else if (strcmp(psz, "w81") == 0)
291 uNtVersion = MK_VER(6,3);
292 else if (strcmp(psz, "w10") == 0)
293 uNtVersion = MK_VER(10,0);
294 else
295 {
296 fprintf(stderr, "VBoxPeSetVersion: syntax error: Unknown option: --%s\n", psz);
297 return RTEXITCODE_SYNTAX;
298 }
299 continue;
300 }
301 psz = " ";
302 }
303 do
304 {
305 switch (ch)
306 {
307 case 'q':
308 g_cVerbosity = 0;
309 break;
310 case 'v':
311 g_cVerbosity++;
312 break;
313 case 'V':
314 printf("2.0\n");
315 return RTEXITCODE_SUCCESS;
316 case 'h':
317 Usage(stdout);
318 return RTEXITCODE_SUCCESS;
319 default:
320 fprintf(stderr, "VBoxPeSetVersion: syntax error: Unknown option: -%c\n", ch ? ch : ' ');
321 return RTEXITCODE_SYNTAX;
322 }
323 } while ((ch = *psz++) != '\0');
324
325 }
326 else if (!pszFilename)
327 pszFilename = psz;
328 else
329 {
330 fprintf(stderr, "VBoxPeSetVersion: syntax error: More than one PE-image specified!\n");
331 return RTEXITCODE_SYNTAX;
332 }
333 }
334
335 if (!pszFilename)
336 {
337 fprintf(stderr, "VBoxPeSetVersion: syntax error: No PE-image specified!\n");
338 return RTEXITCODE_SYNTAX;
339 }
340 g_pszFilename = pszFilename;
341
342 /*
343 * Process the file.
344 */
345 int rcExit;
346 FILE *pFile = fopen(pszFilename, "r+b");
347 if (pFile)
348 {
349 PIMAGE_SECTION_HEADER paShdrs = NULL;
350 rcExit = UpdateFile(pFile, uNtVersion, &paShdrs);
351 if (paShdrs)
352 free(paShdrs);
353 if (fclose(pFile) != 0)
354 rcExit = Error("fclose failed on '%s': %s", pszFilename, strerror(errno));
355 }
356 else
357 rcExit = Error("Failed to open '%s' for updating: %s", pszFilename, strerror(errno));
358 return rcExit;
359}
360
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use