mirror of
				https://github.com/FDOS/kernel.git
				synced 2025-10-31 03:14:40 +01:00 
			
		
		
		
	has trouble without quotes because $$ can be expanded as a shell variable when passed to system(). git-svn-id: https://svn.code.sf.net/p/freedos/svn/kernel/trunk@1443 6ac86273-5f31-0410-b378-82cca8765d1b
		
			
				
	
	
		
			464 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			464 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /****************************************************************/
 | |
| /*                                                              */
 | |
| /*                          exeflat.c                           */
 | |
| /*                                                              */
 | |
| /*                 EXE flattening program                       */
 | |
| /*                                                              */
 | |
| /*                      Copyright (c) 2001                      */
 | |
| /*                      Bart E. Oldeman                         */
 | |
| /*                      All Rights Reserved                     */
 | |
| /*                                                              */
 | |
| /* This file is part of DOS-C.                                  */
 | |
| /*                                                              */
 | |
| /* DOS-C is free software; you can redistribute it and/or       */
 | |
| /* modify it under the terms of the GNU General Public License  */
 | |
| /* as published by the Free Software Foundation; either version */
 | |
| /* 2, or (at your option) any later version.                    */
 | |
| /*                                                              */
 | |
| /* DOS-C is distributed in the hope that it will be useful, but */
 | |
| /* WITHOUT ANY WARRANTY; without even the implied warranty of   */
 | |
| /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See    */
 | |
| /* the GNU General Public License for more details.             */
 | |
| /*                                                              */
 | |
| /* You should have received a copy of the GNU General Public    */
 | |
| /* License along with DOS-C; see the file COPYING.  If not,     */
 | |
| /* write to the Free Software Foundation, 675 Mass Ave,         */
 | |
| /* Cambridge, MA 02139, USA.                                    */
 | |
| /****************************************************************/
 | |
| 
 | |
| /*
 | |
| 
 | |
| Usage: exeflat (src.exe) (dest.sys) (relocation-factor)
 | |
| 
 | |
| large portions copied from task.c
 | |
| 
 | |
| */
 | |
| 
 | |
| /* history
 | |
| 
 | |
|         10/??/01 - Bart Oldeman 
 | |
|                 primary release
 | |
| 
 | |
|         11/28/01 - tom ehlert   
 | |
|                 added -UPX option to make the kernel compressable with UPX
 | |
| 
 | |
| */
 | |
| 
 | |
| #include "portab.h"
 | |
| #include "exe.h"
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <ctype.h>
 | |
| 
 | |
| #define BUFSIZE 32768u
 | |
| 
 | |
| #define KERNEL_START 0x10 /* the kernel code really starts here at 60:10 */
 | |
| 
 | |
| typedef struct {
 | |
|   UWORD off, seg;
 | |
| } farptr;
 | |
| 
 | |
| static int compReloc(const void *p1, const void *p2)
 | |
| {
 | |
|   farptr *r1 = (farptr *) p1;
 | |
|   farptr *r2 = (farptr *) p2;
 | |
|   if (r1->seg > r2->seg)
 | |
|     return 1;
 | |
|   if (r1->seg < r2->seg)
 | |
|     return -1;
 | |
|   if (r1->off > r2->off)
 | |
|     return 1;
 | |
|   if (r1->off < r2->off)
 | |
|     return -1;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static void usage(void)
 | |
| {
 | |
|   printf("usage: exeflat (src.exe) (dest.sys) (relocation-factor)\n");
 | |
|   printf
 | |
|       ("               -S10   - Silent relocate segment 10 (down list)\n");
 | |
|   
 | |
|   exit(1);
 | |
| }
 | |
| 
 | |
| static int exeflat(const char *srcfile, const char *dstfile,
 | |
|                    const char *start, short *silentSegments, short silentcount,
 | |
|                    int UPX, exe_header *header)
 | |
| {
 | |
|   int i, j;
 | |
|   size_t bufsize;
 | |
|   farptr *reloc;
 | |
|   UWORD start_seg;
 | |
|   ULONG size, to_xfer;
 | |
|   UBYTE **buffers;
 | |
|   UBYTE **curbuf;
 | |
|   FILE *src, *dest;
 | |
|   short silentdone = 0;
 | |
|   int compress_sys_file;
 | |
| 
 | |
|   if ((src = fopen(srcfile, "rb")) == NULL)
 | |
|   {
 | |
|     printf("Source file %s could not be opened\n", srcfile);
 | |
|     exit(1);
 | |
|   }
 | |
|   if (fread(header, sizeof(*header), 1, src) != 1)
 | |
|   {
 | |
|     printf("Error reading header from %s\n", srcfile);
 | |
|     fclose(src);
 | |
|     exit(1);
 | |
|   }
 | |
|   if (header->exSignature != MAGIC)
 | |
|   {
 | |
|     printf("Source file %s is not a valid .EXE\n", srcfile);
 | |
|     fclose(src);
 | |
|     exit(1);
 | |
|   }
 | |
|   start_seg = (UWORD)strtol(start, NULL, 0);
 | |
|   if (header->exExtraBytes == 0)
 | |
|     header->exExtraBytes = 0x200;
 | |
|   printf("header len = %lu = 0x%lx\n", header->exHeaderSize * 16UL,
 | |
|          header->exHeaderSize * 16UL);
 | |
|   size =
 | |
|     ((DWORD) (header->exPages - 1) << 9) + header->exExtraBytes -
 | |
|     header->exHeaderSize * 16UL;
 | |
|   printf("image size (less header) = %lu = 0x%lx\n", size, size);
 | |
|   printf("first relocation offset = %u = 0x%u\n", header->exOverlay,
 | |
|          header->exOverlay);
 | |
| 
 | |
|   /* first read file into memory chunks */
 | |
|   fseek(src, header->exHeaderSize * 16UL, SEEK_SET);
 | |
|   buffers = malloc((size_t)((size + BUFSIZE - 1) / BUFSIZE) * sizeof(char *));
 | |
|   if (buffers == NULL)
 | |
|   {
 | |
|     printf("Allocation error\n");
 | |
|     exit(1);
 | |
|   }
 | |
|   bufsize = BUFSIZE;
 | |
|   for (to_xfer = size, curbuf = buffers; to_xfer > 0;
 | |
|        to_xfer -= bufsize, curbuf++)
 | |
|   {
 | |
|     if (to_xfer < BUFSIZE)
 | |
|       bufsize = (size_t)to_xfer;
 | |
|     *curbuf = malloc(bufsize);
 | |
|     if (*curbuf == NULL)
 | |
|     {
 | |
|       printf("Allocation error\n");
 | |
|       exit(1);
 | |
|     }
 | |
|     if (fread(*curbuf, sizeof(char), bufsize, src) != bufsize)
 | |
|     {
 | |
|       printf("Source file read error %ld %d\n", to_xfer, bufsize);
 | |
|       exit(1);
 | |
|     }
 | |
|   }
 | |
|   if (header->exRelocTable && header->exRelocItems)
 | |
|   {
 | |
|     fseek(src, header->exRelocTable, SEEK_SET);
 | |
|     reloc = malloc(header->exRelocItems * sizeof(farptr));
 | |
|     if (reloc == NULL)
 | |
|     {
 | |
|       printf("Allocation error\n");
 | |
|       exit(1);
 | |
|     }
 | |
|     if (fread(reloc, sizeof(farptr), header->exRelocItems, src) !=
 | |
|         header->exRelocItems)
 | |
|     {
 | |
|       printf("Source file read error\n");
 | |
|       exit(1);
 | |
|     }
 | |
|   }
 | |
|   fclose(src);
 | |
|   qsort(reloc, header->exRelocItems, sizeof(reloc[0]), compReloc);
 | |
|   for (i = 0; i < header->exRelocItems; i++)
 | |
|   {
 | |
|     ULONG spot = ((ULONG) reloc[i].seg << 4) + reloc[i].off;
 | |
|     UBYTE *spot0 = &buffers[(size_t)(spot / BUFSIZE)][(size_t)(spot % BUFSIZE)];
 | |
|     UBYTE *spot1 = &buffers[(size_t)((spot + 1) / BUFSIZE)][(size_t)((spot + 1) % BUFSIZE)];
 | |
|     UWORD segment = ((UWORD) * spot1 << 8) + *spot0;
 | |
| 
 | |
|     for (j = 0; j < silentcount; j++)
 | |
|       if (segment == silentSegments[j])
 | |
|       {
 | |
|         silentdone++;
 | |
|         goto dontPrint;
 | |
|       }
 | |
|     
 | |
|     printf("relocation at 0x%04x:0x%04x ->%04x\n", reloc[i].seg,
 | |
|            reloc[i].off, segment);
 | |
|     
 | |
|   dontPrint:
 | |
|     
 | |
|     segment += start_seg;
 | |
|     *spot0 = segment & 0xff;
 | |
|     *spot1 = segment >> 8;
 | |
|   }
 | |
| 
 | |
|   printf("\nProcessed %d relocations, %d not shown\n",
 | |
|          header->exRelocItems, silentdone);
 | |
| 
 | |
|   if ((dest = fopen(dstfile, "wb+")) == NULL)
 | |
|   {
 | |
|     printf("Destination file %s could not be created\n", dstfile);
 | |
|     exit(1);
 | |
|   }
 | |
| 
 | |
|   /* The biggest .sys file that UPX accepts seems to be 65419 bytes long */
 | |
|   compress_sys_file = size < 65420;
 | |
|   if (UPX) {
 | |
|       printf("Compressing kernel - %s format\n", (compress_sys_file)?"sys":"exe");
 | |
|   }
 | |
|   if (UPX && !compress_sys_file) {
 | |
|     /* write header without relocations to file */
 | |
|     exe_header nheader = *header;
 | |
|     nheader.exRelocItems = 0;
 | |
|     nheader.exHeaderSize = 2;
 | |
|     size += 32;
 | |
|     nheader.exPages = (UWORD)(size >> 9);
 | |
|     nheader.exExtraBytes = (UWORD)size & 511;
 | |
|     if (nheader.exExtraBytes)
 | |
|       nheader.exPages++;
 | |
|     if (fwrite(&nheader, sizeof(nheader), 1, dest) != 1) {
 | |
|       printf("Destination file write error\n");
 | |
|       exit(1);
 | |
|     }
 | |
|     fseek(dest, 32UL, SEEK_SET);
 | |
|   }
 | |
| 
 | |
|   /* write dest file from memory chunks */
 | |
|   bufsize = BUFSIZE;
 | |
|   for (to_xfer = size, curbuf = buffers; to_xfer > 0;
 | |
|        to_xfer -= bufsize, curbuf++)
 | |
|   {
 | |
|     if (to_xfer < BUFSIZE)
 | |
|       bufsize = (size_t)to_xfer;
 | |
|     if (fwrite(*curbuf, sizeof(char), bufsize, dest) != bufsize)
 | |
| 
 | |
|     {
 | |
|       printf("Destination file write error\n");
 | |
|       exit(1);
 | |
|     }
 | |
|     free(*curbuf);
 | |
|   }
 | |
| 
 | |
|   if (UPX && compress_sys_file) {
 | |
|     /* overwrite first 8 bytes with SYS header */
 | |
|     UWORD dhdr[4];
 | |
|     fseek(dest, 0, SEEK_SET);
 | |
|     for (i = 0; i < 3; i++)
 | |
|       dhdr[i] = 0xffff;
 | |
|     /* strategy will jump to us, interrupt never called */
 | |
|     dhdr[3] = KERNEL_START;
 | |
|     fwrite(dhdr, sizeof(dhdr), 1, dest);
 | |
|   }
 | |
|   fclose(dest);
 | |
|   return compress_sys_file;
 | |
| }
 | |
| 
 | |
| static void write_header(FILE *dest, size_t size)
 | |
| {
 | |
|   /* UPX HEADER jump $+2+size */
 | |
|   static char JumpBehindCode[] = {
 | |
|     /* kernel config header - 32 bytes */
 | |
|     0xeb, 0x1b,               /*     jmp short realentry */
 | |
|     'C', 'O', 'N', 'F', 'I', 'G', 32 - 2 - 6 - 2 - 3, 0,      /* WORD */
 | |
|     0,                        /* DLASortByDriveNo            db 0  */
 | |
|     1,                        /* InitDiskShowDriveAssignment db 1  */
 | |
|     2,                        /* SkipConfigSeconds           db 2  */
 | |
|     0,                        /* ForceLBA                    db 0  */
 | |
|     1,                        /* GlobalEnableLBAsupport      db 1  */
 | |
|     0,                        /* BootHarddiskSeconds               */
 | |
| 
 | |
|     'n', 'u', 's', 'e', 'd',     /* unused filler bytes                              */
 | |
|     8, 7, 6, 5, 4, 3, 2, 1,
 | |
|     /* real-entry: jump over the 'real' image do the trailer */
 | |
|     0xe9, 0, 0                /* 100: jmp 103 */
 | |
|   };
 | |
| 
 | |
|   struct x {
 | |
|     char y[sizeof(JumpBehindCode) == 0x20 ? 1 : -1];
 | |
|   };
 | |
| 
 | |
|   fseek(dest, 0, SEEK_SET);
 | |
|   /* this assumes <= 0xfe00 code in kernel */
 | |
|   *(short *)&JumpBehindCode[0x1e] += (short)size;
 | |
|   fwrite(JumpBehindCode, 1, 0x20, dest);
 | |
| }
 | |
| 
 | |
| static void write_trailer(FILE *dest, size_t size, int compress_sys_file,
 | |
|   exe_header *header)
 | |
| {
 | |
|   /* UPX trailer */
 | |
|   /* hand assembled - so this remains ANSI C ;-) */
 | |
|   /* well almost: we still need packing and assume little endian ... */
 | |
|   /* move kernel down to place CONFIG-block, which added above,
 | |
|      at start_seg-2:0 (e.g. 0x5e:0) instead of 
 | |
|      start_seg:0 (e.g. 0x60:0) and store there boot drive number
 | |
|      from BL; kernel.asm will then check presence of additional
 | |
|      CONFIG-block at this address. */
 | |
|   static char trailer[] = {   /* shift down everything by sizeof JumpBehindCode */
 | |
|     0xB9, 0x00, 0x00,         /*  0 mov cx,offset trailer     */
 | |
|     0x0E,                     /*  3 push cs                   */
 | |
|     0x1F,                     /*  4 pop ds (=60)              */
 | |
|     0x8C, 0xC8,               /*  5 mov ax,cs                 */
 | |
|     0x48,                     /*  7 dec ax                    */
 | |
|     0x48,                     /*  8 dec ax                    */
 | |
|     0x8E, 0xC0,               /*  9 mov es,ax                 */
 | |
|     0x93,                     /* 11 xchg ax,bx (to get al=bl) */
 | |
|     0x31, 0xFF,               /* 12 xor di,di                 */
 | |
|     0xFC,                     /* 14 cld                       */
 | |
|     0xAA,                     /* 15 stosb (store drive number)*/
 | |
|     0x8B, 0xF7,               /* 16 mov si,di                 */
 | |
|     0xF3, 0xA4,               /* 18 rep movsb                 */
 | |
|     0x1E,                     /* 20 push ds                   */
 | |
|     0x58,                     /* 21 pop  ax                   */
 | |
|     0x05, 0x00, 0x00,         /* 22 add ax,...                */
 | |
|     0x8E, 0xD0,               /* 25 mov ss,ax                 */
 | |
|     0xBC, 0x00, 0x00,         /* 27 mov sp,...                */
 | |
|     0x31, 0xC0,               /* 30 xor ax,ax                 */
 | |
|     0xFF, 0xE0                /* 32 jmp ax                    */
 | |
|   };
 | |
| 
 | |
|   *(short *)&trailer[1] = (short)size + 0x20;
 | |
|   *(short *)&trailer[23] = header->exInitSS;
 | |
|   *(short *)&trailer[28] = header->exInitSP;
 | |
|   if (compress_sys_file) {
 | |
|     /* replace by jmp word ptr [6]: ff 26 06 00
 | |
|        (the .SYS strategy handler which will unpack) */
 | |
|     *(long *)&trailer[30] = 0x000626ffL;
 | |
|     /* set up a 4K stack for the UPX decompressor to work with */
 | |
|     *(short *)&trailer[23] = 0x1000;
 | |
|     *(short *)&trailer[28] = 0x1000;
 | |
|   }
 | |
|   fwrite(trailer, 1, sizeof trailer, dest);
 | |
| }
 | |
| 
 | |
| int main(int argc, char **argv)
 | |
| {
 | |
|   short silentSegments[20], silentcount = 0;
 | |
|   static exe_header header; /* must be initialized to zero */
 | |
|   int UPX = FALSE;
 | |
|   int i;
 | |
|   size_t sz, len, len2, n;
 | |
|   int compress_sys_file;
 | |
|   char *buffer, *tmpexe, *cmdbuf;
 | |
|   FILE *dest;
 | |
|   long size;
 | |
| 
 | |
|   /* if no arguments provided, show usage and exit */
 | |
|   if (argc < 4) usage();
 | |
| 
 | |
|   /* do optional argument processing here */
 | |
|   for (i = 4; i < argc && !UPX; i++)
 | |
|   {
 | |
|     char *argptr = argv[i];
 | |
|     
 | |
|     if (argptr[0] != '-' && argptr[0] != '/')
 | |
|       usage();
 | |
|     
 | |
|     argptr++;
 | |
| 
 | |
|     switch (toupper(argptr[0]))
 | |
|     {
 | |
|       case 'U':
 | |
|         UPX = i;
 | |
|         break;
 | |
|       case 'S':
 | |
|         if (silentcount >= LENGTH(silentSegments))
 | |
|         {
 | |
|           printf("can't handle more then %d silent's\n",
 | |
|                  LENGTH(silentSegments));
 | |
|           exit(1);
 | |
|         }
 | |
|         
 | |
|         silentSegments[silentcount++] = (short)strtol(argptr + 1, NULL, 0);
 | |
|         break;
 | |
|         
 | |
|       default:
 | |
|         usage();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* arguments left :
 | |
|      infile outfile relocation offset */
 | |
| 
 | |
|   compress_sys_file = exeflat(argv[1], argv[2], argv[3],
 | |
|                               silentSegments, silentcount,
 | |
|                               UPX, &header);
 | |
|   if (!UPX)
 | |
|     exit(0);
 | |
| 
 | |
|   /* move kernel.sys tmp.exe */
 | |
|   tmpexe = argv[2];
 | |
|   if (!compress_sys_file)
 | |
|   {
 | |
|     tmpexe = "tmp.exe";
 | |
|     rename(argv[2], tmpexe);
 | |
|   }
 | |
| 
 | |
|   len2 = strlen(tmpexe) + 1;
 | |
|   sz = len2;
 | |
|   if (sz < 256) sz = 256;
 | |
|   cmdbuf = malloc(sz);
 | |
|   len = 0;
 | |
|   for (i = UPX+1; i < argc; i++)
 | |
|   {
 | |
|     n = strlen(argv[i]);
 | |
|     if (len + len2 + n + 2 >= sz) {
 | |
|       sz *= 2;
 | |
|       cmdbuf = realloc(cmdbuf, sz);
 | |
|     }
 | |
|     if (i > UPX+1)
 | |
|       cmdbuf[len++] = ' ';
 | |
|     memcpy(cmdbuf + len, argv[i], n + 1);
 | |
|     len += n;
 | |
|   }
 | |
|   cmdbuf[len++] = ' ';
 | |
|   /* if tmpexe is tmpfile set above no quotes needed, if user needs quotes should add on cmd line */
 | |
|   memcpy(cmdbuf + len, tmpexe, len2);
 | |
|   cmdbuf[len + len2] = '\0';
 | |
|   printf("%s\n", cmdbuf);
 | |
|   if (system(cmdbuf))
 | |
|   {
 | |
|     printf("Problems executing %s\n", cmdbuf);
 | |
|     printf("Removing [%s]\n", tmpexe);
 | |
|     remove(tmpexe);
 | |
|     exit(1);
 | |
|   }
 | |
|   free(cmdbuf);
 | |
| 
 | |
|   if (!compress_sys_file)
 | |
|   {
 | |
|     exeflat(tmpexe, argv[2], argv[3],
 | |
|             silentSegments, silentcount,
 | |
|             FALSE, &header);
 | |
|     remove(tmpexe);
 | |
|   }
 | |
| 
 | |
|   /* argv[2] now contains the final flattened file: just
 | |
|      header and trailer need to be added */
 | |
|   /* the compressed file has two chunks max */
 | |
| 
 | |
|   if ((dest = fopen(argv[2], "rb+")) == NULL)
 | |
|   {
 | |
|     printf("Destination file %s could not be opened\n", argv[2]);
 | |
|     exit(1);
 | |
|   }
 | |
| 
 | |
|   buffer = malloc(0xfe01);
 | |
| 
 | |
|   fread(buffer, 0xfe01, 1, dest);
 | |
|   size = ftell(dest);
 | |
|   if (size >= 0xfe00u)
 | |
|   {
 | |
|     printf("kernel; size too large - must be <= 0xfe00\n");
 | |
|     exit(1);
 | |
|   }
 | |
|   fseek(dest, 0, SEEK_SET);
 | |
|   write_header(dest, (size_t)size);
 | |
|   fwrite(buffer, (size_t)size, 1, dest);
 | |
|   write_trailer(dest, (size_t)size, compress_sys_file, &header);
 | |
|   return 0;
 | |
| }
 |