source: trunk/src/peldr/pe.c@ 22016

Last change on this file since 22016 was 22016, checked in by dmik, 13 years ago

pe: Don't add .exe if there is an extension or path in the file name spec.

Some Win32 applications (e.g. installers) start executables which have
custom extensions using the full path. Adding .exe in this case obviously
breaks them.

File size: 16.9 KB
Line 
1/*
2 * PELDR main exe loader code
3 *
4 * Copyright 1998-2001 Sander van Leeuwen (sandervl@xs4all.nl)
5 * Copyright 2012 Dmitriy Kuminov
6 *
7 * Command line options:
8 * /OPT:[x1=y,x2=z,..]
9 * x = CURDIR -> set current directory to y
10 * (not other options available at this time)
11 *
12 * Project Odin Software License can be found in LICENSE.TXT
13 */
14
15#define INCL_DOSFILEMGR /* File Manager values */
16#define INCL_DOSERRORS /* DOS Error values */
17#define INCL_DOSPROCESS /* DOS Process values */
18#define INCL_DOSMISC /* DOS Miscellanous values */
19#define INCL_DOSMODULEMGR
20#define INCL_DOSSESMGR
21#define INCL_WIN
22#include <os2.h>
23#include <bseord.h>
24#include <win32type.h>
25#include <misc.h>
26#include <wprocess.h>
27#include <win/peexe.h>
28#include <odinpe.h>
29#include "pe.h"
30
31#include "helpers.h"
32
33char INFO_BANNER[] = "Usage: PE winexe commandline";
34char szErrorTitle[] = "Odin";
35char szLoadErrorMsg[] = "Can't load executable %s";
36char szFileNotFound[] = "File not found: %s";
37char szFileErrorMsg[] = "File IO error";
38char szPEErrorMsg[] = "Not a valid win32 exe. (perhaps 16 bits windows)";
39char szCPUErrorMsg[] = "%s doesn't run on x86 machines";
40char szExeErrorMsg[] = "%s isn't an executable";
41char szInteralErrorMsg[]= "Internal Error while loading %s";
42char szInteralErrorMsg1[]= "Internal Error";
43char szNoKernel32Msg[] = "Can't load/find kernel32.dll (rc=%d, module %s)";
44char szBadKernel32Msg[] = "Invalid or outdated kernel32.dll";
45char szErrorExports[] = "Unable to process exports of %s";
46char szErrorMemory[] = "Memory allocation failure while loading %s";
47char szErrorImports[] = "Failure to load \"%s\" due to bad or missing %s";
48
49char szErrDosStartSession[] = "Failed to start win16 (%s) for %s (rc=%d)";
50
51char fullpath[CCHMAXPATH];
52
53typedef HAB (* APIENTRY WININITIALIZEPROC)(ULONG flOptions);
54typedef BOOL (* APIENTRY WINTERMINATEPROC)(HAB hab);
55typedef HMQ (* APIENTRY WINCREATEMSGQUEUEPROC) (HAB hab, LONG cmsg);
56typedef BOOL (* APIENTRY WINDESTROYMSGQUEUEPROC) (HMQ hmq);
57typedef ULONG (* APIENTRY WINMESSAGEBOXPROC) (HWND hwndParent,
58 HWND hwndOwner,
59 PCSZ pszText,
60 PCSZ pszCaption,
61 ULONG idWindow,
62 ULONG flStyle);
63typedef void (* KRNL32EXCEPTPROC) (void *exceptframe);
64
65WININITIALIZEPROC MyWinInitialize = 0;
66WINTERMINATEPROC MyWinTerminate = 0;
67WINCREATEMSGQUEUEPROC MyWinCreateMsgQueue = 0;
68WINDESTROYMSGQUEUEPROC MyWinDestroyMsgQueue = 0;
69WINMESSAGEBOXPROC MyWinMessageBox = 0;
70KRNL32EXCEPTPROC Krnl32SetExceptionHandler = 0;
71KRNL32EXCEPTPROC Krnl32UnsetExceptionHandler = 0;
72
73
74WIN32CTOR CreateWin32Exe = 0;
75ULONG reservedMemory = 0;
76BOOL fConsoleApp = FALSE;
77
78BOOL AllocateExeMem(char *filename, BOOL *fNEExe);
79
80#ifdef DEBUG
81void print(const char *fmt, ...)
82{
83 static char buf[1024];
84 char *s, *e;
85 ULONG dummy;
86 int len;
87 va_list args;
88 va_start(args, fmt);
89 len = vsnprintf(buf, sizeof(buf), fmt, args);
90 va_end(args);
91 s = buf;
92 while (*s)
93 {
94 e = (char *)str_find_char(s, '\n');
95 DosWrite((HFILE)1, s, e - s, &dummy);
96 if (*e == '\n')
97 {
98 DosWrite((HFILE)1, "\n\r", 2, &dummy);
99 e++;
100 }
101 s = e;
102 }
103}
104#define DBG(a) do { print a; } while (0)
105#else
106#define DBG(a) do {} while (0)
107#endif
108
109//******************************************************************************
110//******************************************************************************
111int simple_main()
112{
113 DBG(("PE: BEGIN (DEBUG mode)\n"));
114
115 HAB hab = 0; /* PM anchor block handle */
116 HMQ hmq = 0; /* Message queue handle */
117 char exeName[CCHMAXPATH];
118 char fullpath[CCHMAXPATH * 2];
119 char errorMod[CCHMAXPATH];
120 char szErrorMsg[512];
121 char *pszErrorMsg = NULL;
122 APIRET rc;
123 HMODULE hmodPMWin = 0, hmodKernel32 = 0;
124 PTIB ptib;
125 PPIB ppib;
126 char *cmdline, *win32cmdline, *peoptions;
127 BOOL fVioConsole, fIsNEExe;
128
129 char *pszTemp, *pszTemp2;
130
131 DosGetInfoBlocks(&ptib, &ppib);
132
133#ifdef COMMAND_LINE_VERSION
134 if(DosGetInfoBlocks(&ptib, &ppib) == 0)
135 {
136 //switch process type to PM so the command line app can create PM
137 //windows
138 ppib->pib_ultype = 3;
139 }
140#endif
141
142 fullpath[0] = 0;
143 DosQueryModuleName(ppib->pib_hmte, sizeof(fullpath), fullpath);
144
145 pszTemp = fullpath + strlen(fullpath) - 1;
146 while (pszTemp >= fullpath && (*pszTemp != '\\' && *pszTemp != '/'))
147 --pszTemp;
148 if (pszTemp >= fullpath)
149 {
150 *pszTemp = 0;
151 strcat(fullpath, ";%BeginLIBPATH%");
152 DosSetExtLIBPATH(fullpath, BEGIN_LIBPATH);
153 DBG(("PE: Added '%s' to BEGINLIBPATH\n", fullpath));
154 }
155
156 cmdline = ppib->pib_pchcmd;
157 if (cmdline)
158 {
159 cmdline += strlen(cmdline) + 1; // skip executable name
160 if (!*cmdline)
161 cmdline = NULL;
162 }
163
164 if (cmdline)
165 {
166 DBG(("PE: Full command line: '%s'\n", cmdline));
167
168 pszTemp = (char *)str_skip_char(cmdline, ' ');
169
170 // get PE options
171 if (str_starts_with(pszTemp, "/OPT:["))
172 {
173 const char *end = str_find_char(pszTemp , ']');
174 if (*end)
175 pszTemp = (char *)str_skip_char(end + 1, ' ');
176 else
177 peoptions = NULL;
178 }
179
180 // get Win32 executable name
181 int delim = ' ';
182 if (*pszTemp == '"')
183 {
184 pszTemp++;
185 delim = '"';
186 }
187 pszTemp2 = exeName;
188 while (*pszTemp && *pszTemp != delim &&
189 (pszTemp2 - exeName) < CCHMAXPATH - 1)
190 {
191 *pszTemp2++ = *pszTemp++;
192 }
193 *pszTemp2 = '\0';
194 if (delim == '"')
195 pszTemp++;
196 pszTemp = (char *)str_skip_char(pszTemp, ' ');
197
198 // get Win32 command line
199 win32cmdline = pszTemp;
200
201 // add the .EXE extension if missing (only if there's no path)
202 if (*str_find_char(exeName, '.') == '\0' &&
203 *str_find_char(exeName, '\\') == '\0' &&
204 *str_find_char(exeName, '/') == '\0')
205 {
206 int i = strlen(exeName);
207 if (i < 4 || !((exeName[i-1] == 'E' || exeName[i-1] == 'e') &&
208 (exeName[i-2] == 'X' || exeName[i-2] == 'x') &&
209 (exeName[i-3] == 'E' || exeName[i-3] == 'e') &&
210 exeName[i-4] == '.'))
211 {
212 if (i + 4 < CCHMAXPATH)
213 strcat(exeName, ".exe");
214 }
215 }
216
217 // try to locate the executable
218 pszTemp = exeName + strlen(exeName) - 1;
219 while (pszTemp >= exeName && (*pszTemp != '\\' && *pszTemp != '/'))
220 --pszTemp;
221 if (pszTemp < exeName)
222 {
223 // no path information, perform a search
224
225 char newExeName[CCHMAXPATH];
226
227 if(DosSearchPath( SEARCH_CUR_DIRECTORY | SEARCH_ENVIRONMENT | SEARCH_IGNORENETERRS
228 , "WINDOWSPATH" /* environment value */
229 , exeName /* Name of file to look for */
230 , newExeName /* Result of the search */
231 , sizeof(newExeName) /* Length of search buffer */
232 ) == NO_ERROR)
233 {
234 strcpy(exeName, newExeName);
235 }
236 }
237
238 DBG(("PE: Win32 EXE: '%s'\n", exeName));
239 DBG(("PE: Win32 command line: '%s'\n", win32cmdline));
240
241 FILESTATUS3 fstat3;
242 if (DosQueryPathInfo(exeName, FIL_STANDARD, (PVOID)&fstat3, sizeof(fstat3)) != NO_ERROR)
243 {
244 snprintf(szErrorMsg, sizeof(szErrorMsg), szFileNotFound, exeName);
245 pszErrorMsg = szErrorMsg;
246 }
247 }
248 else
249 {
250 // No command line, show the usage message
251 pszErrorMsg = INFO_BANNER;
252 }
253
254 if (!pszErrorMsg)
255 {
256 if (AllocateExeMem(exeName, &fIsNEExe))
257 {
258 if (fIsNEExe)
259 {
260 STARTDATA sdata = {0};
261 ULONG idSession;
262 PID pid;
263
264 sdata.Length = sizeof(sdata);
265 sdata.PgmName = (PSZ)"w16odin.exe";
266 strcpy(fullpath, exeName);
267 strcat(fullpath, " ");
268 strcat(fullpath, win32cmdline);
269 sdata.PgmInputs = fullpath;
270 sdata.FgBg = SSF_FGBG_FORE;
271 sdata.SessionType = SSF_TYPE_WINDOWEDVDM;
272 rc = DosStartSession(&sdata, &idSession, &pid);
273 if (rc)
274 {
275 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrDosStartSession, sdata.PgmName, exeName, rc);
276 pszErrorMsg = szErrorMsg;
277 }
278 else
279 {
280 // we don't need to do anything else, return shortly
281 DBG(("PE: END (returning 0)\n"));
282 return 0;
283 }
284 }
285 }
286 else
287 {
288 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorMemory, exeName);
289 pszErrorMsg = szErrorMsg;
290 }
291 }
292
293 // Resolve PM functions (needed to show the pszErrorMsg box too)
294 rc = DosLoadModule(exeName, sizeof(exeName), "PMWIN", &hmodPMWin);
295 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32INITIALIZE, NULL, (PFN *)&MyWinInitialize);
296 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32TERMINATE, NULL, (PFN *)&MyWinTerminate);
297 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32CREATEMSGQUEUE, NULL, (PFN *)&MyWinCreateMsgQueue);
298 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32DESTROYMSGQUEUE, NULL, (PFN *)&MyWinDestroyMsgQueue);
299 rc = DosQueryProcAddr(hmodPMWin, ORD_WIN32MESSAGEBOX, NULL, (PFN *)&MyWinMessageBox);
300
301 if ((hab = MyWinInitialize(0)) &&
302 (hmq = MyWinCreateMsgQueue(hab, 0)))
303 {
304 if (!pszErrorMsg)
305 {
306 errorMod[0] = 0;
307 rc = DosLoadModule(errorMod, sizeof(errorMod), "KERNEL32", &hmodKernel32);
308 if (rc)
309 {
310 snprintf(szErrorMsg, sizeof(szErrorMsg), szNoKernel32Msg, rc, errorMod);
311 pszErrorMsg = szErrorMsg;
312 }
313 else
314 {
315 rc = DosQueryProcAddr(hmodKernel32, 0, "_CreateWin32PeLdrExe@36", (PFN *)&CreateWin32Exe);
316 if (rc)
317 {
318 snprintf(szErrorMsg, sizeof(szErrorMsg), szBadKernel32Msg);
319 pszErrorMsg = szErrorMsg;
320 }
321 }
322
323 if (!pszErrorMsg)
324 {
325#ifdef COMMAND_LINE_VERSION
326 fVioConsole = TRUE;
327#else
328 fVioConsole = FALSE;
329#endif
330 DBG(("PE: fVioConsole: %d\n", fVioConsole));
331
332 rc = CreateWin32Exe(exeName, win32cmdline, peoptions, reservedMemory, 0,
333 fConsoleApp, fVioConsole, errorMod, sizeof(errorMod));
334 if (rc != LDRERROR_SUCCESS)
335 {
336 switch (rc)
337 {
338 case LDRERROR_INVALID_MODULE:
339 snprintf(szErrorMsg, sizeof(szErrorMsg), szLoadErrorMsg, exeName);
340 break;
341 case LDRERROR_INVALID_CPU:
342 snprintf(szErrorMsg, sizeof(szErrorMsg), szCPUErrorMsg, exeName);
343 break;
344 case LDRERROR_FILE_SYSTEM:
345 snprintf(szErrorMsg, sizeof(szErrorMsg), szExeErrorMsg, exeName);
346 break;
347 case LDRERROR_MEMORY:
348 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorMemory, exeName);
349 break;
350 case LDRERROR_EXPORTS:
351 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorExports, exeName);
352 break;
353 case LDRERROR_IMPORTS:
354 snprintf(szErrorMsg, sizeof(szErrorMsg), szErrorImports, exeName, errorMod);
355 break;
356 case LDRERROR_INVALID_SECTION:
357 default:
358 snprintf(szErrorMsg, sizeof(szErrorMsg), szInteralErrorMsg, exeName);
359 break;
360 }
361
362 pszErrorMsg = szErrorMsg;
363 }
364 }
365 }
366
367 if (pszErrorMsg)
368 MyWinMessageBox(HWND_DESKTOP, NULLHANDLE, pszErrorMsg, szErrorTitle,
369 0, MB_OK | MB_ERROR | MB_MOVEABLE);
370 }
371 else
372 {
373 DBG(("PE: WinInitialize/WinCreateMsgQueue failed!\n"));
374 }
375
376 if(hmodKernel32)
377 DosFreeModule(hmodKernel32);
378
379 if (hmq)
380 MyWinDestroyMsgQueue(hmq);
381 if (hab)
382 MyWinTerminate(hab);
383
384 if(hmodPMWin)
385 DosFreeModule(hmodPMWin);
386
387#ifdef DEBUG
388 if (pszErrorMsg)
389 DBG(("PE: Error: '%s'\n", pszErrorMsg));
390#endif
391
392 DBG(("PE: END (returning %d)\n", pszErrorMsg ? 1 : 0));
393 return pszErrorMsg ? 1 : 0;
394}
395
396//******************************************************************************
397//SvL: Reserve memory for win32 exes without fixups
398// This is done before any Odin or PMWIN dll is loaded, so we'll get
399// a very low virtual address. (which is exactly what we want)
400//******************************************************************************
401BOOL AllocateExeMem(char *filename, BOOL *fNEExe)
402{
403 HFILE dllfile = 0;
404 char *tmp;
405 ULONG action, ulRead, signature;
406 APIRET rc;
407 IMAGE_DOS_HEADER doshdr;
408 IMAGE_OPTIONAL_HEADER oh;
409 IMAGE_FILE_HEADER fh;
410 ULONG address = 0;
411 ULONG alloccnt = 0;
412 ULONG diff, i, baseAddress;
413 ULONG ulSysinfo, flAllocMem = 0;
414 BOOL ret = FALSE;
415
416 //Reserve enough space to store 4096 pointers to 1MB memory chunks
417 static ULONG memallocs[4096];
418
419 *fNEExe = FALSE;
420
421 rc = DosOpen(filename, &dllfile, &action, 0, FILE_READONLY, OPEN_ACTION_OPEN_IF_EXISTS|OPEN_ACTION_FAIL_IF_NEW, OPEN_SHARE_DENYNONE|OPEN_ACCESS_READONLY, NULL);
422 if(rc) {
423 goto end; //oops
424 }
425
426 //read dos header
427 if(DosRead(dllfile, (LPVOID)&doshdr, sizeof(doshdr), &ulRead)) {
428 goto end;
429 }
430 if(DosSetFilePtr(dllfile, doshdr.e_lfanew, FILE_BEGIN, &ulRead)) {
431 goto end;
432 }
433 //read signature dword
434 if(DosRead(dllfile, (LPVOID)&signature, sizeof(signature), &ulRead)) {
435 goto end;
436 }
437 //read pe header
438 if(DosRead(dllfile, (LPVOID)&fh, sizeof(fh), &ulRead)) {
439 goto end;
440 }
441 //read optional header
442 if(DosRead(dllfile, (LPVOID)&oh, sizeof(oh), &ulRead)) {
443 goto end;
444 }
445 if(doshdr.e_magic != IMAGE_DOS_SIGNATURE || signature != IMAGE_NT_SIGNATURE) {
446 if(LOWORD(signature) == IMAGE_OS2_SIGNATURE) {
447 *fNEExe = TRUE;
448 }
449 goto end;
450 }
451 fConsoleApp = (oh.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI);
452
453 DBG(("PE: AllocateExeMem: oh.Subsystem: %d\n", oh.Subsystem));
454 DBG(("PE: AllocateExeMem: oh.ImageBase: 0x%08X\n", oh.ImageBase));
455
456 // check for high memory support
457 rc = DosQuerySysInfo(QSV_VIRTUALADDRESSLIMIT, QSV_VIRTUALADDRESSLIMIT, &ulSysinfo, sizeof(ulSysinfo));
458 if (rc == 0 && ulSysinfo > 512) //VirtualAddresslimit is in MB
459 {
460 flAllocMem = PAG_ANY; // high memory support. Let's use it!
461 }
462
463 DBG(("PE: AllocateExeMem: VIRTUALADDRESSLIMIT: %d (rc %d)\n", ulSysinfo, rc));
464
465 if(oh.ImageBase < 512*1024*1024) {
466 flAllocMem = 0;
467 }
468 else {
469 if(flAllocMem == 0) {
470 goto end; //no support for > 512 MB
471 }
472 }
473 while(TRUE) {
474 rc = DosAllocMem((PPVOID)&address, FALLOC_SIZE, PAG_READ | flAllocMem);
475 if(rc) break;
476
477 if(address + FALLOC_SIZE >= oh.ImageBase) {
478 if(address > oh.ImageBase) {//we've passed it!
479 DosFreeMem((PVOID)address);
480 break;
481 }
482 //found the right address
483 DosFreeMem((PVOID)address);
484
485 diff = oh.ImageBase - address;
486 if(diff) {
487 rc = DosAllocMem((PPVOID)&address, diff, PAG_READ | flAllocMem);
488 if(rc) break;
489 }
490 rc = DosAllocMem((PPVOID)&baseAddress, oh.SizeOfImage, PAG_READ | PAG_WRITE | flAllocMem);
491 if(rc) break;
492
493 if(diff) DosFreeMem((PVOID)address);
494
495 reservedMemory = baseAddress;
496 break;
497 }
498 memallocs[alloccnt++] = address;
499 }
500 for(i=0;i<alloccnt;i++) {
501 DosFreeMem((PVOID)memallocs[i]);
502 }
503 DBG(("PE: AllocateExeMem: reservedMemory: 0x%08X\n", reservedMemory));
504 ret = TRUE;
505end:
506 if(dllfile) DosClose(dllfile);
507 DBG(("PE: AllocateExeMem: returning %d\n", ret));
508 return ret;
509}
510//******************************************************************************
511//******************************************************************************
Note: See TracBrowser for help on using the repository browser.