source: trunk/src/kernel32/winimagepeldr.cpp@ 1036

Last change on this file since 1036 was 978, checked in by sandervl, 26 years ago

Changes for LX image resource support

File size: 41.6 KB
Line 
1/* $Id: winimagepeldr.cpp,v 1.2 1999-09-18 17:47:10 sandervl Exp $ */
2
3/*
4 * Win32 PE loader Image base class
5 *
6 * Copyright 1998-1999 Sander van Leeuwen (sandervl@xs4all.nl)
7 * Copyright 1998 Knut St. Osmundsen
8 *
9 * Project Odin Software License can be found in LICENSE.TXT
10 *
11 */
12
13#define INCL_DOSFILEMGR /* File Manager values */
14#define INCL_DOSMODULEMGR
15#define INCL_DOSERRORS /* DOS Error values */
16#define INCL_DOSPROCESS /* DOS Process values */
17#define INCL_DOSMISC /* DOS Miscellanous values */
18#define INCL_WIN
19#define INCL_BASE
20#include <os2wrap.h> //Odin32 OS/2 api wrappers
21
22#include <stdio.h>
23#include <string.h>
24#include <stdlib.h>
25
26#include <assert.h>
27#include <misc.h>
28#include <win32type.h>
29#include <winimagebase.h>
30#include <winimagepeldr.h>
31#include <windllpeldr.h>
32#include <pefile.h>
33#include <unicode.h>
34#include <winres.h>
35#include "oslibmisc.h"
36#include "initterm.h"
37#include <win\virtual.h>
38
39char szErrorTitle[] = "Odin";
40char szMemErrorMsg[] = "Memory allocation failure";
41char szFileErrorMsg[] = "File IO error";
42char szPEErrorMsg[] = "Not a valid win32 exe. (perhaps 16 bits windows)";
43char szCPUErrorMsg[] = "Executable doesn't run on x86 machines";
44char szExeErrorMsg[] = "File isn't an executable";
45char szInteralErrorMsg[]= "Internal Error";
46
47#ifndef max
48#define max(a, b) ((a>b) ? a : b)
49#endif
50
51BOOL foutInit = FALSE;
52ofstream fout;
53
54ULONG MissingApi();
55char *hex(ULONG num);
56
57extern ULONG flAllocMem; /*Tue 03.03.1998: knut */
58
59//******************************************************************************
60//******************************************************************************
61Win32PeLdrImage::Win32PeLdrImage(char *szFileName) :
62 Win32ImageBase(-1),
63 nrsections(0), imageSize(0),
64 imageVirtBase(-1), realBaseAddress(0), imageVirtEnd(0),
65 nrNameExports(0), nrOrdExports(0), nameexports(NULL), ordexports(NULL),
66 pResSection(NULL)
67{
68 strcpy(this->szFileName, szFileName);
69
70 strcpy(szModule, OSLibStripPath(szFileName));
71 strupr(szModule);
72 char *dot = strstr(szModule, ".");
73 while(dot) {
74 char *newdot = strstr(dot+1, ".");
75 if(newdot == NULL) break;
76 dot = newdot;
77 }
78 if(dot)
79 *dot = 0;
80
81 if(foutInit == FALSE) {
82 char logname[32];
83 sprintf(logname, "pe_%d.log", loadNr);
84 fout.open(logname, ios::out | ios::trunc);
85 dprintf(("PE LOGFILE for %s: %s", szModule, logname));
86 foutInit = TRUE;
87 }
88}
89//******************************************************************************
90//******************************************************************************
91Win32PeLdrImage::~Win32PeLdrImage()
92{
93 if(realBaseAddress)
94 DosFreeMem((PVOID)realBaseAddress);
95
96 if(nameexports)
97 free(nameexports);
98
99 if(ordexports)
100 free(ordexports);
101}
102//******************************************************************************
103//******************************************************************************
104BOOL Win32PeLdrImage::init(ULONG reservedMem)
105{
106 HANDLE fImgMapping = 0;
107 char szErrorMsg[64];
108 LPVOID win32file = NULL;
109 ULONG filesize, ulRead;
110 PIMAGE_SECTION_HEADER psh;
111 IMAGE_TLS_DIRECTORY *tlsDir = NULL;
112 int nSections, i;
113 char szFullPath[CCHMAXPATH] = "";
114
115 fImgMapping = VIRTUAL_MapFileA(szFileName, &win32file);
116
117 if (fImgMapping == -1) {
118 sprintf(szErrorMsg, "Unable to open %32s\n", szFileName);
119 WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, szErrorMsg, szErrorTitle, 0, MB_OK | MB_ERROR | MB_MOVEABLE);
120 goto failure;
121 }
122
123 if(DosQueryPathInfo(szFileName, FIL_QUERYFULLNAME, szFullPath, sizeof(szFullPath)) == 0) {
124 setFullPath(szFullPath);
125 }
126
127 if(GetPEFileHeader (win32file, &fh) == FALSE) {
128 fout << "Not a valid PE file (probably a 16 bits windows exe/dll)!" << endl;
129 WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, szPEErrorMsg, szErrorTitle, 0, MB_OK | MB_ERROR | MB_MOVEABLE);
130 goto failure;
131 }
132
133 if(!(fh.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE)) {//not valid
134 fout << "Not a valid PE file!" << endl;
135 WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, szPEErrorMsg, szErrorTitle, 0, MB_OK | MB_ERROR | MB_MOVEABLE);
136 goto failure;
137 }
138 if(fh.Machine != IMAGE_FILE_MACHINE_I386) {
139 fout << "You need a REAL CPU to run this code" << endl;
140 WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, szCPUErrorMsg, szErrorTitle, 0, MB_OK | MB_ERROR | MB_MOVEABLE);
141 goto failure;
142 }
143 //IMAGE_FILE_SYSTEM == only drivers (device/file system/video etc)?
144 if(fh.Characteristics & IMAGE_FILE_SYSTEM) {
145 fout << "Can't convert system files" << endl;
146 WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, szExeErrorMsg, szErrorTitle, 0, MB_OK | MB_ERROR | MB_MOVEABLE);
147 goto failure;
148 }
149
150 if(fh.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) {
151 fout << "No fixups, might not run!" << endl;
152 }
153
154 GetPEOptionalHeader (win32file, &oh);
155 fout << "PE file : " << szFileName << endl;
156 fout << "PE Optional header: " << endl;
157 fout << "Preferred address : " << oh.ImageBase << endl;
158 fout << "Base Of Code : " << oh.BaseOfCode << endl;
159 fout << "CodeSize : " << oh.SizeOfCode << endl;
160 fout << "Base Of Data : " << oh.BaseOfData << endl;
161 fout << "Data Size (uninit): " << oh.SizeOfUninitializedData << endl;
162 fout << "Data Size (init) : " << oh.SizeOfInitializedData << endl;
163 fout << "Entry Point : " << oh.AddressOfEntryPoint << endl;
164 fout << "Section Alignment : " << oh.SectionAlignment << endl;
165 fout << "Stack Reserve size: " << oh.SizeOfStackReserve << endl;
166 fout << "Stack Commit size : " << oh.SizeOfStackCommit << endl;
167 fout << "SizeOfHeapReserve : " << oh.SizeOfHeapReserve << endl;
168 fout << "SizeOfHeapCommit : " << oh.SizeOfHeapCommit << endl;
169 fout << "FileAlignment : " << oh.FileAlignment << endl;
170
171 nSections = NR_SECTIONS(win32file);
172
173 if ((psh = (PIMAGE_SECTION_HEADER)SECTIONHDROFF (win32file)) != NULL) {
174 fout << endl << "*************************PE SECTIONS START**************************" << endl;
175 for (i=0; i<nSections; i++) {
176 fout << "Raw data size: " << hex(psh[i].SizeOfRawData) << endl;
177 fout << "Virtual Address: " << hex(psh[i].VirtualAddress) << endl;
178 fout << "Virtual Size: " << hex(psh[i].Misc.VirtualSize) << endl;
179 fout << "Pointer to raw data: " << hex(psh[i].PointerToRawData) << endl;
180 fout << "Section flags: " << hex(psh[i].Characteristics) << endl << endl;
181 if(strcmp(psh[i].Name, ".reloc") == 0) {
182 fout << ".reloc" << endl << endl;
183 addSection(SECTION_RELOC, (char *)win32file+psh[i].PointerToRawData,
184 psh[i].SizeOfRawData, psh[i].VirtualAddress + oh.ImageBase,
185 psh[i].Misc.VirtualSize);
186 continue;
187 }
188 if(strcmp(psh[i].Name, ".edata") == 0) {
189 fout << ".edata" << endl << endl;
190 addSection(SECTION_EXPORT, (char *)win32file+psh[i].PointerToRawData,
191 psh[i].SizeOfRawData, psh[i].VirtualAddress + oh.ImageBase,
192 psh[i].Misc.VirtualSize);
193 continue;
194 }
195 if(strcmp(psh[i].Name, ".rsrc") == 0) {
196 fout << ".rsrc" << endl << endl;
197 addSection(SECTION_RESOURCE, (char *)win32file+psh[i].PointerToRawData,
198 psh[i].SizeOfRawData, psh[i].VirtualAddress + oh.ImageBase,
199 psh[i].Misc.VirtualSize);
200 continue;
201 }
202 if(strcmp(psh[i].Name, ".tls") == 0)
203 {
204 tlsDir = (IMAGE_TLS_DIRECTORY *)ImageDirectoryOffset(win32file, IMAGE_DIRECTORY_ENTRY_TLS);
205 if(tlsDir) {
206 fout << "TLS Directory" << endl;
207 fout << "TLS Address of Index " << hex((ULONG)tlsDir->AddressOfIndex) << endl;
208 fout << "TLS Address of Callbacks " << hex((ULONG)tlsDir->AddressOfCallBacks) << endl;
209 fout << "TLS SizeOfZeroFill " << hex(tlsDir->SizeOfZeroFill) << endl;
210 fout << "TLS Characteristics " << hex(tlsDir->Characteristics) << endl;
211 addSection(SECTION_TLS, (char *)win32file+psh[i].PointerToRawData,
212 psh[i].SizeOfRawData, psh[i].VirtualAddress + oh.ImageBase,
213 psh[i].Misc.VirtualSize);
214 }
215 continue;
216 }
217
218 if(strcmp(psh[i].Name, ".debug") == 0) {
219 fout << ".rdebug" << endl << endl;
220 addSection(SECTION_DEBUG, (char *)win32file+psh[i].PointerToRawData,
221 psh[i].SizeOfRawData, psh[i].VirtualAddress + oh.ImageBase,
222 psh[i].Misc.VirtualSize);
223 continue;
224 }
225 if(IsImportSection(win32file, &psh[i]))
226 {
227 int type = SECTION_IMPORT;
228 fout << "Import Data Section" << endl << endl;
229 if(psh[i].Characteristics & IMAGE_SCN_CNT_CODE) {
230 fout << "Also Code Section" << endl << endl;
231 type |= SECTION_CODE;
232 }
233 addSection(type, (char *)win32file+psh[i].PointerToRawData,
234 psh[i].SizeOfRawData, psh[i].VirtualAddress + oh.ImageBase,
235 psh[i].Misc.VirtualSize);
236 continue;
237 }
238
239 //KSO Sun 1998-08-09: Borland does not alway set the CODE flag for its "CODE" section
240 if( psh[i].Characteristics & IMAGE_SCN_CNT_CODE ||
241 (psh[i].Characteristics & IMAGE_SCN_MEM_EXECUTE &&
242 !(psh[i].Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA))) //KSO: make sure its not marked as a datasection
243 )
244 {
245 fout << "Code Section" << endl << endl;
246 addSection(SECTION_CODE, (char *)win32file+psh[i].PointerToRawData,
247 psh[i].SizeOfRawData, psh[i].VirtualAddress + oh.ImageBase,
248 psh[i].Misc.VirtualSize);
249 continue;
250 }
251 if(!(psh[i].Characteristics & IMAGE_SCN_MEM_WRITE)) { //read only data section
252 fout << "Read Only Data Section" << endl << endl;
253 addSection(SECTION_READONLYDATA, (char *)win32file+psh[i].PointerToRawData,
254 psh[i].SizeOfRawData, psh[i].VirtualAddress + oh.ImageBase,
255 psh[i].Misc.VirtualSize);
256 continue;
257 }
258 if(psh[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
259 fout << "Uninitialized Data Section" << endl << endl;
260 addSection(SECTION_UNINITDATA, (char *)win32file+psh[i].PointerToRawData,
261 psh[i].SizeOfRawData, psh[i].VirtualAddress + oh.ImageBase,
262 psh[i].Misc.VirtualSize);
263 continue;
264 }
265 if(psh[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) {
266 fout << "Initialized Data Section" << endl << endl;
267 addSection(SECTION_INITDATA, (char *)win32file+psh[i].PointerToRawData,
268 psh[i].SizeOfRawData, psh[i].VirtualAddress + oh.ImageBase,
269 psh[i].Misc.VirtualSize);
270 continue;
271 }
272 if(psh[i].Characteristics & (IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ)) {
273 fout << "Other Section, stored as read/write uninit data" << endl << endl;
274 addSection(SECTION_UNINITDATA, (char *)win32file+psh[i].PointerToRawData,
275 psh[i].SizeOfRawData, psh[i].VirtualAddress + oh.ImageBase,
276 psh[i].Misc.VirtualSize);
277 continue;
278 }
279 fout << "Unknown section" << endl;
280 goto failure;
281 }
282 }
283 fout << "*************************PE SECTIONS END **************************" << endl;
284 imageSize += imageVirtBase - oh.ImageBase;
285 fout << "Total size of Image " << imageSize << endl;
286 fout << "imageVirtBase " << imageVirtBase << endl;
287 fout << "imageVirtEnd " << imageVirtEnd << endl;
288
289 //In case there are any gaps between sections, adjust size
290 if(imageSize != imageVirtEnd - oh.ImageBase) {
291 fout << "imageSize != imageVirtEnd - oh.ImageBase!" << endl;
292 imageSize = imageVirtEnd - oh.ImageBase;
293 }
294 if(allocSections(reservedMem) == FALSE) {
295 fout << "Failed to allocate image memory, rc " << errorState << endl;
296 goto failure;
297 }
298 fout << "OS/2 base address " << realBaseAddress << endl;
299 if(storeSections((char *)win32file) == FALSE) {
300 fout << "Failed to store sections, rc " << errorState << endl;
301 goto failure;
302 }
303 if(oh.AddressOfEntryPoint) {
304 entryPoint = realBaseAddress + oh.AddressOfEntryPoint;
305 }
306 else {
307 fout << "EntryPoint == NULL" << endl;
308 entryPoint = NULL;
309 }
310
311 if(tlsDir != NULL) {
312 Section *sect = findSection(SECTION_TLS);
313
314 if(sect == NULL) {
315 fout << "Couldn't find TLS section!!" << endl;
316 goto failure;
317 }
318 setTLSAddress((char *)(sect->realvirtaddr + (tlsDir->StartAddressOfRawData - sect->virtaddr)));
319 setTLSInitSize(tlsDir->EndAddressOfRawData - tlsDir->StartAddressOfRawData);
320 setTLSTotalSize(tlsDir->EndAddressOfRawData - tlsDir->StartAddressOfRawData + tlsDir->SizeOfZeroFill);
321
322 sect = findSectionByAddr((ULONG)tlsDir->AddressOfIndex);
323 if(sect == NULL) {
324 fout << "Couldn't find TLS AddressOfIndex section!!" << endl;
325 goto failure;
326 }
327 setTLSIndexAddr((LPDWORD)(sect->realvirtaddr + ((ULONG)tlsDir->AddressOfIndex - sect->virtaddr)));
328
329 sect = findSectionByAddr((ULONG)tlsDir->AddressOfCallBacks);
330 if(sect == NULL) {
331 fout << "Couldn't find TLS AddressOfCallBacks section!!" << endl;
332 goto failure;
333 }
334 setTLSCallBackAddr((PIMAGE_TLS_CALLBACK *)(sect->realvirtaddr + ((ULONG)tlsDir->AddressOfCallBacks - sect->virtaddr)));
335 }
336
337 if(realBaseAddress != oh.ImageBase) {
338 if(setFixups((PIMAGE_BASE_RELOCATION)ImageDirectoryOffset(win32file, IMAGE_DIRECTORY_ENTRY_BASERELOC)) == FALSE) {
339 fout << "Failed to set fixups" << endl;
340 goto failure;
341 }
342 }
343 if(fh.Characteristics & IMAGE_FILE_DLL) {
344 if(processExports((char *)win32file) == FALSE) {
345 fout << "Failed to process exported apis" << endl;
346 goto failure;
347 }
348 }
349
350 //SvL: Use pointer to image header as module handle now. Some apps needs this
351 hinstance = (HINSTANCE)realBaseAddress;
352
353 if(processImports((char *)win32file) == FALSE) {
354 fout << "Failed to process imports!" << endl;
355 goto failure;
356 }
357
358 IMAGE_SECTION_HEADER sh;
359 if(GetSectionHdrByName (win32file, &sh, ".rsrc")) {
360 //get offset in resource object of directory entry
361 pResDir = (PIMAGE_RESOURCE_DIRECTORY)(sh.VirtualAddress + realBaseAddress);
362 pResourceSectionStart = (ULONG)pResSection->virtaddr - oh.ImageBase;
363 }
364
365 //set final memory protection flags (storeSections sets them to read/write)
366 if(setMemFlags() == FALSE) {
367 fout << "Failed to set memory protection" << endl;
368 goto failure;
369 }
370
371 CloseHandle(fImgMapping);
372 return(TRUE);
373failure:
374 if(fImgMapping) CloseHandle(fImgMapping);
375 errorState = ERROR_INTERNAL;
376 return FALSE;
377}
378//******************************************************************************
379//******************************************************************************
380void Win32PeLdrImage::addSection(ULONG type, char *rawdata, ULONG rawsize, ULONG virtaddress, ULONG virtsize)
381{
382 virtsize = max(rawsize, virtsize);
383
384 section[nrsections].type = type;
385 section[nrsections].rawdata = rawdata;
386 section[nrsections].rawsize = rawsize;
387 section[nrsections].virtaddr = virtaddress;
388
389 if(type == SECTION_RESOURCE) {
390 pResSection = &section[nrsections];
391 }
392 virtsize = ((virtsize - 1) & ~0xFFF) + PAGE_SIZE;
393 imageSize += virtsize;
394 section[nrsections].virtualsize = virtsize;
395
396 if(virtaddress < imageVirtBase)
397 imageVirtBase = virtaddress;
398 if(virtaddress + virtsize > imageVirtEnd)
399 imageVirtEnd = virtaddress + virtsize;
400
401 nrsections++;
402}
403//******************************************************************************
404//******************************************************************************
405BOOL Win32PeLdrImage::allocSections(ULONG reservedMem)
406{
407 APIRET rc;
408 ULONG baseAddress;
409
410 if(fh.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) {
411 fout << "No fixups, might not run!" << endl;
412 return allocFixedMem(reservedMem);
413 }
414 rc = DosAllocMem((PPVOID)&baseAddress, imageSize, PAG_READ | flAllocMem);
415 if(rc) {
416 errorState = rc;
417 return(FALSE);
418 }
419 realBaseAddress = baseAddress;
420 return(TRUE);
421}
422//******************************************************************************
423//******************************************************************************
424Section *Win32PeLdrImage::findSection(ULONG type)
425{
426 for(int i=0;i<nrsections;i++) {
427 if(section[i].type == type) {
428 return &section[i];
429 }
430 }
431 return NULL;
432}
433//******************************************************************************
434//******************************************************************************
435Section *Win32PeLdrImage::findSectionByAddr(ULONG addr)
436{
437 for(int i=0;i<nrsections;i++) {
438 if(section[i].virtaddr <= addr && section[i].virtaddr + section[i].virtualsize > addr) {
439 return &section[i];
440 }
441 }
442 return NULL;
443}
444//******************************************************************************
445#define FALLOC_SIZE (1024*1024)
446//NOTE: Needs testing (while loop)
447//TODO: Free unused (parts of) reservedMem
448//******************************************************************************
449BOOL Win32PeLdrImage::allocFixedMem(ULONG reservedMem)
450{
451 ULONG address = 0;
452 ULONG *memallocs;
453 ULONG alloccnt = 0;
454 ULONG diff, i, baseAddress;
455 APIRET rc;
456
457 realBaseAddress = 0;
458
459 if(reservedMem && reservedMem <= oh.ImageBase &&
460 ((oh.ImageBase - reservedMem) + imageSize < PELDR_RESERVEDMEMSIZE))
461 {
462 //ok, it fits perfectly
463 realBaseAddress = oh.ImageBase;
464 return TRUE;
465 }
466
467 //Reserve enough space to store 4096 pointers to 1MB memory chunks
468 memallocs = (ULONG *)malloc(4096*sizeof(ULONG *));
469 if(memallocs == NULL) {
470 fout << "allocFixedMem: MALLOC FAILED for memallocs" << endl;
471 return FALSE;
472 }
473
474 while(TRUE) {
475 rc = DosAllocMem((PPVOID)&address, FALLOC_SIZE, PAG_READ | flAllocMem);
476 if(rc) break;
477
478 fout << "DosAllocMem returned " << address << endl;
479 if(address + FALLOC_SIZE >= oh.ImageBase) {
480 if(address > oh.ImageBase) {//we've passed it!
481 DosFreeMem((PVOID)address);
482 break;
483 }
484 //found the right address
485 DosFreeMem((PVOID)address);
486
487 diff = oh.ImageBase - address;
488 if(diff) {
489 rc = DosAllocMem((PPVOID)&address, diff, PAG_READ | flAllocMem);
490 if(rc) break;
491 }
492 rc = DosAllocMem((PPVOID)&baseAddress, imageSize, PAG_READ | flAllocMem);
493 if(rc) break;
494
495 if(diff) DosFreeMem((PVOID)address);
496
497 realBaseAddress = baseAddress;
498 break;
499 }
500 memallocs[alloccnt++] = address;
501 }
502 for(i=0;i<alloccnt;i++) {
503 DosFreeMem((PVOID)memallocs[i]);
504 }
505 free(memallocs);
506
507 if(realBaseAddress == 0) //Let me guess.. MS Office app?
508 return(FALSE);
509
510 return(TRUE);
511}
512//******************************************************************************
513//******************************************************************************
514BOOL Win32PeLdrImage::storeSections(char *win32file)
515{
516 int i;
517 APIRET rc;
518 ULONG pagFlags = PAG_COMMIT;
519 ULONG headersize;
520 WINIMAGE_LOOKUP *imgLookup;
521
522 //Commit memory for image header
523 headersize = sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS) +
524 sizeof(IMAGE_SECTION_HEADER) * fh.NumberOfSections;
525
526 if(headersize + sizeof(WINIMAGE_LOOKUP) < PAGE_SIZE) {
527 headersize = PAGE_SIZE;
528 }
529 else {//ooops, just in case this doesn't work
530 fout << "ERROR: storeSections: header too big!!!!!! Fatal error" << endl;
531 return FALSE;
532 }
533
534 rc = DosSetMem((PVOID)realBaseAddress, headersize, pagFlags | PAG_WRITE | PAG_READ);
535 if(rc) {
536 fout << "DosSetMem failed for Image header! " << rc << endl;
537 return FALSE;
538 }
539 // Store the NT header at the load addr
540 memcpy((char *)realBaseAddress, win32file, sizeof(IMAGE_DOS_HEADER));
541 memcpy((char *)PE_HEADER(realBaseAddress), PE_HEADER(win32file), sizeof(IMAGE_NT_HEADERS));
542 memcpy(PE_SECTIONS(realBaseAddress), PE_SECTIONS(win32file),
543 sizeof(IMAGE_SECTION_HEADER) * fh.NumberOfSections );
544
545 imgLookup = WINIMAGE_LOOKUPADDR(realBaseAddress);
546 imgLookup->image = this;
547#ifdef DEBUG
548 imgLookup->magic = MAGIC_WINIMAGE;
549#endif
550
551 // Process all the image sections
552 for(i=0;i<nrsections;i++) {
553 section[i].realvirtaddr = realBaseAddress + (section[i].virtaddr - oh.ImageBase);
554 }
555 for(i=0;i<nrsections;i++) {
556 pagFlags = PAG_COMMIT;
557 switch(section[i].type) {
558 case SECTION_CODE:
559 case (SECTION_CODE | SECTION_IMPORT):
560 case SECTION_INITDATA:
561 case SECTION_UNINITDATA:
562 case SECTION_IMPORT:
563 case SECTION_READONLYDATA:
564 case SECTION_RESOURCE:
565 case SECTION_TLS:
566 pagFlags |= PAG_WRITE | PAG_READ;
567 break;
568 case SECTION_EXPORT:
569 case SECTION_DEBUG:
570 case SECTION_RELOC:
571 pagFlags = 0; //don't commit
572 break;
573 }
574 if(pagFlags == 0) continue; //debug or export section
575
576 rc = DosSetMem((PVOID)section[i].realvirtaddr, section[i].virtualsize, pagFlags);
577 if(rc) {
578 errorState = rc;
579 return(FALSE);
580 }
581 if(section[i].type != SECTION_UNINITDATA) {
582 assert(section[i].rawdata);
583 memcpy((char *)section[i].realvirtaddr, section[i].rawdata, section[i].rawsize);
584 }
585 }
586 return(TRUE);
587}
588//******************************************************************************
589//******************************************************************************
590BOOL Win32PeLdrImage::setMemFlags()
591{
592 int i;
593 APIRET rc;
594 ULONG pagFlags = 0;
595
596 for(i=0;i<nrsections;i++) {
597 pagFlags = 0;
598 switch(section[i].type) {
599 case SECTION_CODE:
600 case (SECTION_CODE | SECTION_IMPORT):
601 pagFlags |= PAG_EXECUTE | PAG_READ;
602 break;
603 case SECTION_INITDATA:
604 case SECTION_UNINITDATA:
605 case SECTION_IMPORT: //TODO: read only?
606 pagFlags |= PAG_WRITE | PAG_READ;
607 break;
608 case SECTION_READONLYDATA:
609 case SECTION_RESOURCE:
610 case SECTION_TLS:
611 pagFlags |= PAG_READ;
612 break;
613 default:
614 continue;
615 }
616 rc = DosSetMem((PVOID)section[i].realvirtaddr, section[i].virtualsize, pagFlags);
617 if(rc) {
618 errorState = rc;
619 return(FALSE);
620 }
621 }
622 return(TRUE);
623}
624//******************************************************************************
625//******************************************************************************
626BOOL Win32PeLdrImage::setFixups(PIMAGE_BASE_RELOCATION prel)
627{
628 int i, j;
629 char *page;
630 ULONG count;
631
632 if(fh.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) {
633 return(TRUE);
634 }
635
636 if(prel) {
637 j = 1;
638 while(prel->VirtualAddress) {
639 page = (char *)((char *)prel + (ULONG)prel->VirtualAddress);
640 count = (prel->SizeOfBlock - 8)/2;
641 fout.setf(ios::hex, ios::basefield);
642 fout << "Page " << j << " Address " << (ULONG)prel->VirtualAddress << " Count " << count << endl;
643 fout.setf(ios::dec, ios::basefield);
644 j++;
645 for(i=0;i<count;i++) {
646 int type = prel->TypeOffset[i] >> 12;
647 int offset = prel->TypeOffset[i] & 0xFFF;
648 switch(type) {
649 case IMAGE_REL_BASED_ABSOLUTE:
650//// fout << "absolute fixup; unused" << endl;
651 break; //skip
652 case IMAGE_REL_BASED_HIGHLOW:
653//// fout << "address " << offset << " type " << type << endl;
654 AddOff32Fixup(oh.ImageBase +
655 prel->VirtualAddress + offset);
656 break;
657 case IMAGE_REL_BASED_HIGH:
658 AddOff16Fixup(oh.ImageBase + prel->VirtualAddress + offset, TRUE);
659 break;
660 case IMAGE_REL_BASED_LOW:
661 AddOff16Fixup(oh.ImageBase + prel->VirtualAddress + offset, FALSE);
662 break;
663 case IMAGE_REL_BASED_HIGHADJ:
664 case IMAGE_REL_BASED_MIPS_JMPADDR:
665 default:
666 fout << "Unknown/unsupported fixup type!" << endl;
667 break;
668 }
669 }
670 prel = (PIMAGE_BASE_RELOCATION)((char*)prel + prel->SizeOfBlock);
671 }//while
672 }
673 else {
674 fout << "No internal fixups found!\n" << endl;
675 errorState = ERROR_INTERNAL;
676 return(FALSE);
677 }
678 return(TRUE);
679}
680//******************************************************************************
681//******************************************************************************
682void Win32PeLdrImage::AddOff32Fixup(ULONG fixupaddr)
683{
684 ULONG orgaddr;
685 ULONG *fixup;
686
687 fixup = (ULONG *)(fixupaddr - oh.ImageBase + realBaseAddress);
688 orgaddr = *fixup;
689 *fixup = realBaseAddress + (*fixup - oh.ImageBase);
690}
691//******************************************************************************
692//******************************************************************************
693void Win32PeLdrImage::AddOff16Fixup(ULONG fixupaddr, BOOL fHighFixup)
694{
695 ULONG orgaddr;
696 USHORT *fixup;
697
698 fixup = (USHORT *)(fixupaddr - oh.ImageBase + realBaseAddress);
699 orgaddr = *fixup;
700 if(fHighFixup) {
701 *fixup += (USHORT)((realBaseAddress - oh.ImageBase) >> 16);
702 }
703 else {
704 *fixup += (USHORT)((realBaseAddress - oh.ImageBase) & 0xFFFF);
705 }
706}
707//******************************************************************************
708//******************************************************************************
709void Win32PeLdrImage::StoreImportByOrd(Win32DllBase *WinDll, ULONG ordinal, ULONG impaddr)
710{
711 ULONG *import;
712 ULONG apiaddr;
713
714 import = (ULONG *)(impaddr - oh.ImageBase + realBaseAddress);
715 apiaddr = WinDll->getApi(ordinal);
716 if(apiaddr == 0) {
717 fout << "--->>> NOT FOUND!";
718 *import = (ULONG)MissingApi;
719 }
720 else *import = apiaddr;
721}
722//******************************************************************************
723//******************************************************************************
724void Win32PeLdrImage::StoreImportByName(Win32DllBase *WinDll, char *impname, ULONG impaddr)
725{
726 ULONG *import;
727 ULONG apiaddr;
728
729 import = (ULONG *)(impaddr - oh.ImageBase + realBaseAddress);
730 apiaddr = WinDll->getApi(impname);
731 if(apiaddr == 0) {
732 fout << "--->>> NOT FOUND!";
733 *import = (ULONG)MissingApi;
734 }
735 else *import = apiaddr;
736}
737//******************************************************************************
738//******************************************************************************
739BOOL Win32PeLdrImage::processExports(char *win32file)
740{
741 IMAGE_SECTION_HEADER sh;
742 PIMAGE_EXPORT_DIRECTORY ped;
743 ULONG *ptrNames, *ptrAddress;
744 USHORT *ptrOrd;
745 int i;
746
747 /* get section header and pointer to data directory for .edata section */
748 if((ped = (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryOffset
749 (win32file, IMAGE_DIRECTORY_ENTRY_EXPORT)) != NULL &&
750 GetSectionHdrByImageDir(win32file, IMAGE_DIRECTORY_ENTRY_EXPORT, &sh) ) {
751
752 fout << "Exported Functions: " << endl;
753 ptrOrd = (USHORT *)((ULONG)ped->AddressOfNameOrdinals -
754 (ULONG)sh.VirtualAddress +
755 (ULONG)sh.PointerToRawData + (ULONG)win32file);
756 ptrNames = (ULONG *)((ULONG)ped->AddressOfNames -
757 (ULONG)sh.VirtualAddress +
758 (ULONG)sh.PointerToRawData + (ULONG)win32file);
759 ptrAddress = (ULONG *)((ULONG)ped->AddressOfFunctions -
760 (ULONG)sh.VirtualAddress +
761 (ULONG)sh.PointerToRawData + (ULONG)win32file);
762 nrOrdExports = ped->NumberOfFunctions;
763 nrNameExports = ped->NumberOfNames;
764
765 int ord, RVAExport;
766 char *name;
767 for(i=0;i<ped->NumberOfNames;i++) {
768 ord = ptrOrd[i] + ped->Base;
769 name = (char *)((ULONG)ptrNames[i] - (ULONG)sh.VirtualAddress +
770 (ULONG)sh.PointerToRawData + (ULONG)win32file);
771 RVAExport = ptrAddress[ptrOrd[i]];
772#ifdef FORWARDERS
773 if(RVAExport < sh.VirtualAddress || RVAExport > sh.VirtualAddress + sh.SizeOfRawData) {
774#endif
775 //points to code (virtual address relative to oh.ImageBase
776 fout << "address 0x";
777 fout.setf(ios::hex, ios::basefield);
778 fout << RVAExport;
779 fout.setf(ios::dec, ios::basefield);
780 fout << " " << name << "@" << ord << endl;
781 AddNameExport(oh.ImageBase + RVAExport, name, ord);
782#ifdef FORWARDERS
783
784 }
785 else {//forwarder
786 char *forward = (char *)((ULONG)RVAExport -
787 (ULONG)sh.VirtualAddress +
788 (ULONG)sh.PointerToRawData +
789 (ULONG)win32file);
790 fout << RVAExport << " " << name << " @" << ord << " is forwarder to " << (int)forward << endl;
791 }
792#endif
793 }
794 for(i=0;i<max(ped->NumberOfNames,ped->NumberOfFunctions);i++) {
795 ord = ped->Base + i; //Correct??
796 RVAExport = ptrAddress[i];
797#ifdef FORWARDERS
798 if(RVAExport < sh.VirtualAddress || RVAExport > sh.VirtualAddress + sh.SizeOfRawData) {
799#endif
800 if(RVAExport) {
801 //points to code (virtual address relative to oh.ImageBase
802 fout << "ord " << ord << " at 0x";
803 fout.setf(ios::hex, ios::basefield);
804 fout << RVAExport << endl;
805 fout.setf(ios::dec, ios::basefield);
806 AddOrdExport(oh.ImageBase + RVAExport, ord);
807 }
808#ifdef FORWARDERS
809 }
810 else {//forwarder or empty
811 char *forward = (char *)((ULONG)RVAExport -
812 (ULONG)sh.VirtualAddress +
813 (ULONG)sh.PointerToRawData +
814 (ULONG)win32file);
815 fout << "ord " << ord << " at 0x";
816 fout.setf(ios::hex, ios::basefield);
817 fout << RVAExport << " is forwarder to 0x" << (int)forward << endl;
818 fout.setf(ios::dec, ios::basefield);
819 }
820#endif
821 }
822 }
823 return(TRUE);
824}
825//******************************************************************************
826//******************************************************************************
827void Win32PeLdrImage::AddNameExport(ULONG virtaddr, char *apiname, ULONG ordinal)
828{
829 ULONG nsize;
830
831 if(nameexports == NULL) {
832 nameExportSize= 4096;
833 nameexports = (NameExport *)malloc(nameExportSize);
834 curnameexport = nameexports;
835 }
836 nsize = (ULONG)curnameexport - (ULONG)nameexports;
837 if(nsize + sizeof(NameExport) + strlen(apiname) > nameExportSize) {
838 nameExportSize += 4096;
839 char *tmp = (char *)nameexports;
840 nameexports = (NameExport *)malloc(nameExportSize);
841 memcpy(nameexports, tmp, nsize);
842 curnameexport = (NameExport *)((ULONG)nameexports + nsize);
843 free(tmp);
844 }
845 curnameexport->virtaddr = realBaseAddress + (virtaddr - oh.ImageBase);
846 curnameexport->ordinal = ordinal;
847 *(ULONG *)curnameexport->name = 0;
848 strcpy(curnameexport->name, apiname);
849
850 curnameexport->nlength = strlen(apiname) + 1;
851 if(curnameexport->nlength < sizeof(curnameexport->name))
852 curnameexport->nlength = sizeof(curnameexport->name);
853
854 curnameexport = (NameExport *)((ULONG)curnameexport->name + curnameexport->nlength);
855}
856//******************************************************************************
857//******************************************************************************
858void Win32PeLdrImage::AddOrdExport(ULONG virtaddr, ULONG ordinal)
859{
860 if(ordexports == NULL) {
861 ordexports = (OrdExport *)malloc(nrOrdExports * sizeof(OrdExport));
862 curordexport = ordexports;
863 }
864 curordexport->virtaddr = realBaseAddress + (virtaddr - oh.ImageBase);
865 curordexport->ordinal = ordinal;
866 curordexport++;
867}
868//******************************************************************************
869/** All initial processing of imports is done here
870 * Should now detect most Borland styled files including the GifCon32.exe and
871 * loader32 from SoftIce. (Stupid Borland!!!)
872 *
873 * knut [Jul 22 1998 2:44am]
874 **/
875//******************************************************************************
876BOOL Win32PeLdrImage::processImports(char *win32file)
877{
878 PIMAGE_IMPORT_DESCRIPTOR pID;
879 IMAGE_SECTION_HEADER shID;
880 IMAGE_SECTION_HEADER shExtra = {0};
881 PIMAGE_OPTIONAL_HEADER pOH;
882 int i,j;
883 BOOL fBorland = 0;
884 int cModules;
885 char *pszModules;
886 char *pszCurModule;
887 char *pszTmp;
888 ULONG *pulImport;
889 ULONG ulCurFixup;
890 int Size;
891 Win32PeLdrDll *WinDll;
892
893/* "algorithm:"
894 * 1) get module names and store them
895 * a) check dwRVAModuleName is within .idata seg - if not find section
896 * 2) iterate thru functions of each module
897 * a) check OriginalFirstThunk is not 0 and that it points to a RVA.
898 * b) if not a) borland-styled PE-file - ARG!!!
899 * check FirstThunk
900 * c) check OriginalFirstThunk/FirstThunk ok RVAs and find right section
901 * d) store ordinal/name import
902 * 3) finished
903 */
904
905 /* 1) get module names */
906 pID = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryOffset(win32file, IMAGE_DIRECTORY_ENTRY_IMPORT);
907 if (pID == NULL)
908 return TRUE;
909 if (!GetSectionHdrByImageDir(win32file, IMAGE_DIRECTORY_ENTRY_IMPORT, &shID))
910 return TRUE;
911
912 //calc size of module list
913 i = Size = cModules = 0;
914 while (pID[i].Name != 0)
915 {
916 //test RVA inside ID-Section
917 if (pID[i].Name >= shID.VirtualAddress && pID[i].Name < shID.VirtualAddress + max(shID.Misc.VirtualSize, shID.SizeOfRawData)) {
918 pszTmp = (char*)(pID[i].Name- shID.VirtualAddress + shID.PointerToRawData + (ULONG)win32file);
919 }
920 else {
921 //is the "Extra"-section already found or do we have to find it?
922 if (pID[i].Name < shExtra.VirtualAddress || pID[i].Name >= shExtra.VirtualAddress + max(shExtra.Misc.VirtualSize, shExtra.SizeOfRawData)) {
923 if (!GetSectionHdrByRVA(win32file, &shExtra, pID[i].Name))
924 return FALSE;
925 }
926 pszTmp = (char*)(pID[i].Name- shExtra.VirtualAddress + shExtra.PointerToRawData + (ULONG)win32file);
927 }
928 Size += strlen(pszTmp) + 1;
929 i++;
930 cModules++;
931 }
932
933 pszModules = (char*)malloc(Size);
934 assert(pszModules != NULL);
935 j = 0;
936 for (i = 0; i < cModules; i++)
937 {
938 //test RVA inside ID-Section
939 if (pID[i].Name >= shID.VirtualAddress && pID[i].Name < shID.VirtualAddress + max(shID.Misc.VirtualSize, shID.SizeOfRawData)) {
940 pszTmp = (char*)(pID[i].Name- shID.VirtualAddress + shID.PointerToRawData + (ULONG)win32file);
941 }
942 else {
943 fBorland = TRUE;
944 //is the "Extra"-section already found or do we have to find it?
945 if (pID[i].Name < shExtra.VirtualAddress || pID[i].Name >= shExtra.VirtualAddress + max(shExtra.Misc.VirtualSize, shExtra.SizeOfRawData))
946 {
947 if (GetSectionHdrByRVA(win32file, &shExtra, pID[i].Name)) {
948 free(pszModules);
949 return FALSE;
950 }
951 }
952 pszTmp = (char*)(pID[i].Name- shExtra.VirtualAddress + shExtra.PointerToRawData + (ULONG)win32file);
953 }
954
955 strcpy(pszModules+j, pszTmp);
956 j += strlen(pszTmp) + 1;
957 }
958 fout << endl;
959 if (fBorland)
960 fout << "Borland-styled PE-File." << endl;
961 //Store modules
962 fout << cModules << " imported Modules: " << endl;
963
964 /* 2) functions */
965 pszCurModule = pszModules;
966 pOH = (PIMAGE_OPTIONAL_HEADER)OPTHEADEROFF(win32file);
967 for (i = 0; i < cModules; i++)
968 {
969 fout << "Module " << pszCurModule << endl;
970 // a) check that OriginalFirstThunk not is 0 and look for Borland-styled PE
971 if (i == 0)
972 {
973 //heavy borland-style test - assume array of thunks is within that style does not change
974 if((ULONG)pID[i].u.OriginalFirstThunk == 0 ||
975 (ULONG)pID[i].u.OriginalFirstThunk < shID.VirtualAddress ||
976 (ULONG)pID[i].u.OriginalFirstThunk >= shID.VirtualAddress + max(shID.Misc.VirtualSize, shID.SizeOfRawData) ||
977 (ULONG)pID[i].u.OriginalFirstThunk >= pOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress &&
978 (ULONG)pID[i].u.OriginalFirstThunk < sizeof(*pID)*cModules + pOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)
979 {
980 fBorland = TRUE;
981 }
982 }
983 //light borland-style test
984 if (pID[i].u.OriginalFirstThunk == 0 || fBorland)
985 pulImport = (ULONG*)pID[i].FirstThunk;
986 else pulImport = (ULONG*)pID[i].u.OriginalFirstThunk;
987
988 // b) check if RVA ok
989 if (!(pulImport > 0 && (ULONG)pulImport < pOH->SizeOfImage)) {
990 fout << "Invalid RVA " << hex((ULONG)pulImport) << endl;
991 break;
992 }
993 // check section
994 if ((ULONG)pulImport < shExtra.VirtualAddress || (ULONG)pulImport >= shExtra.VirtualAddress + max(shExtra.Misc.VirtualSize, shExtra.SizeOfRawData))
995 {
996 if (!GetSectionHdrByRVA(win32file, &shExtra, (ULONG)pulImport))
997 {
998 fout << "warning: could not find section for Thunk RVA " << hex((ULONG)pulImport) << endl;
999 break;
1000 }
1001 }
1002
1003 //SvL: Load dll if needed
1004 fout << "**********************************************************************" << endl;
1005 fout << "************** Import Module " << pszCurModule << endl;
1006 fout << "**********************************************************************" << endl;
1007 WinDll = (Win32PeLdrDll *)Win32DllBase::findModule(pszCurModule);
1008 if(WinDll == NULL)
1009 { //not found, so load it
1010 if(isPEImage(pszCurModule) == FALSE)
1011 {//LX image, so let OS/2 do all the work for us
1012 APIRET rc;
1013 char szModuleFailure[CCHMAXPATH] = "";
1014 ULONG hInstanceNewDll;
1015
1016 rc = DosLoadModule(szModuleFailure, sizeof(szModuleFailure), pszCurModule, (HMODULE *)&hInstanceNewDll);
1017 if(rc) {
1018 dprintf(("DosLoadModule returned %X for %s\n", rc, szModuleFailure));
1019 errorState = rc;
1020 return(FALSE);
1021 }
1022 WinDll = (Win32PeLdrDll *)Win32DllBase::findModule(hInstanceNewDll);
1023 if(WinDll == NULL) {//shouldn't happen!
1024 dprintf(("Just loaded the dll, but can't find it anywhere?!!?"));
1025 errorState = ERROR_INTERNAL;
1026 return(FALSE);
1027 }
1028 }
1029 else {
1030 WinDll = new Win32PeLdrDll(pszCurModule, this);
1031
1032 if(WinDll == NULL) {
1033 fout << "WinDll: Error allocating memory" << endl;
1034 WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, szMemErrorMsg, szErrorTitle, 0, MB_OK | MB_ERROR | MB_MOVEABLE);
1035 errorState = ERROR_INTERNAL;
1036 return(FALSE);
1037 }
1038 fout << "**********************************************************************" << endl;
1039 fout << "********************** Loading Module *********************" << endl;
1040 fout << "**********************************************************************" << endl;
1041 if(WinDll->init(0) == FALSE) {
1042 fout << "Internal WinDll error " << WinDll->getError() << endl;
1043 return(FALSE);
1044 }
1045 if(WinDll->attachProcess() == FALSE) {
1046 fout << "attachProcess failed!" << endl;
1047 errorState = ERROR_INTERNAL;
1048 return(FALSE);
1049 }
1050 }
1051 fout << "**********************************************************************" << endl;
1052 fout << "********************** Finished Loading Module *********************" << endl;
1053 fout << "**********************************************************************" << endl;
1054 }
1055 else fout << "Already found " << pszCurModule << endl;
1056
1057 WinDll->AddRef();
1058
1059 pulImport = (PULONG)((ULONG)pulImport - shExtra.VirtualAddress + (ULONG)win32file + shExtra.PointerToRawData);
1060 j = 0;
1061 ulCurFixup = (ULONG)pID[i].FirstThunk + pOH->ImageBase;
1062 while (pulImport[j] != 0) {
1063 if (pulImport[j] & IMAGE_ORDINAL_FLAG) { //ordinal
1064 fout.setf(ios::hex, ios::basefield);
1065 fout << "0x" << ulCurFixup << " Imported function " << pszCurModule << "@" << (pulImport[j] & ~IMAGE_ORDINAL_FLAG) << endl;
1066 fout.setf(ios::dec, ios::basefield);
1067 StoreImportByOrd(WinDll, pulImport[j] & ~IMAGE_ORDINAL_FLAG, ulCurFixup);
1068 }
1069 else { //name
1070 //check
1071 if (pulImport[j] < shExtra.VirtualAddress || pulImport[j] >= shExtra.VirtualAddress + max(shExtra.Misc.VirtualSize, shExtra.SizeOfRawData)) {
1072 if (!GetSectionHdrByRVA(win32file, &shExtra, pulImport[j]))
1073 {
1074 fout << "warning: could not find section for Import Name RVA " << hex(pulImport[j]) << endl;
1075 break;
1076 }
1077 }
1078 //KSO - Aug 6 1998 1:15am:this eases comparing...
1079 char *pszFunctionName = (char*)(pulImport[j] + (ULONG)win32file + shExtra.PointerToRawData - shExtra.VirtualAddress + 2);
1080 fout.setf(ios::hex, ios::basefield);
1081 fout << "0x" << ulCurFixup << " Imported function " << pszFunctionName << endl;
1082 fout.setf(ios::dec, ios::basefield);
1083 StoreImportByName(WinDll, pszFunctionName, ulCurFixup);
1084 }
1085 ulCurFixup += sizeof(IMAGE_THUNK_DATA);
1086 j++;
1087 }
1088 fout << "**********************************************************************" << endl;
1089 fout << "************** End Import Module " << pszCurModule << endl;
1090 fout << "**********************************************************************" << endl;
1091
1092 pszCurModule += strlen(pszCurModule) + 1;
1093 fout << endl;
1094 }//for (i = 0; i < cModules; i++)
1095
1096 free(pszModules);
1097 return TRUE;
1098}
1099//******************************************************************************
1100//******************************************************************************
1101ULONG MissingApi()
1102{
1103 static BOOL fIgnore = FALSE;
1104 int r;
1105
1106 dprintf(("Missing api called!\n"));
1107 if(fIgnore)
1108 return(0);
1109
1110 do {
1111 r = WinMessageBox(HWND_DESKTOP, NULLHANDLE, "The application has called a non-existing api\n",
1112 "Internal Error", 0, MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION | MB_MOVEABLE);
1113 }
1114 while(r == MBID_RETRY); //giggle
1115
1116 if( r != MBID_IGNORE )
1117 exit(987);
1118
1119 fIgnore = TRUE;
1120 return(0);
1121}
1122/******************************************************************************/
1123/******************************************************************************/
1124/*heximal(decimal) KSO Sun 24.05.1998*/
1125char szHexBuffer[30];
1126
1127char *hex(ULONG num)
1128{
1129 sprintf(szHexBuffer, "0x%+08x (%lu)",num,num);
1130 return szHexBuffer;
1131}
1132//******************************************************************************
1133//******************************************************************************
Note: See TracBrowser for help on using the repository browser.