/****************************************************************/ /* */ /* 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 #include #include #include #define BUFSIZE 32768u #define KERNEL_START 0x16 /* the kernel code really starts here at 60:16 */ #define KERNEL_CONFIG_LENGTH (32 - 2 - 4) /* 32 entrypoint structure, 2 entrypoint short jump, 4 near jump / ss:sp storage */ unsigned char kernel_config[KERNEL_CONFIG_LENGTH]; 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, UWORD stubsize, UWORD entryparagraphs, exe_header *header) { int i, j; size_t bufsize; farptr *reloc; UWORD start_seg; ULONG size, to_xfer; UBYTE **buffers; UBYTE **curbuf; UWORD curbufoffset; FILE *src, *dest; short silentdone = 0; int compress_sys_file; UWORD realentry; 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); start_seg += entryparagraphs; 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%x\n", header->exRelocTable, header->exRelocTable); /* 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, (int)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 (UPX) { struct x { char y[(KERNEL_CONFIG_LENGTH + 2) <= BUFSIZE ? 1 : -1]; }; memcpy(kernel_config, &buffers[0][2], KERNEL_CONFIG_LENGTH); } realentry = KERNEL_START; if (buffers[0][0] == 0xeb /* jmp short */) { realentry = buffers[0][1] + 2; } else if (buffers[0][0] == 0xe9 /* jmp near */) { realentry = ((UWORD)(buffers[0][2]) << 8) + buffers[0][1] + 3; } 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 - (0x20 - 0x10)) < 65420; if (UPX) { printf("Compressing kernel - %s format\n", (compress_sys_file)?"sys":"exe"); if (!compress_sys_file) { ULONG realsize; /* write header without relocations to file */ exe_header nheader = *header; nheader.exRelocItems = 0; nheader.exHeaderSize = 2; realsize = size + 32 - stubsize; nheader.exPages = (UWORD)(realsize >> 9); nheader.exExtraBytes = (UWORD)realsize & 511; if (nheader.exExtraBytes) nheader.exPages++; nheader.exInitCS = - (stubsize >> 4); nheader.exInitIP = stubsize; if (fwrite(&nheader, sizeof(nheader), 1, dest) != 1) { printf("Destination file write error\n"); exit(1); } fseek(dest, 32UL, SEEK_SET); } else { printf("DOS/SYS format for UPX not yet supported.\n"); exit(1); } } /* write dest file from memory chunks */ { struct x { char y[0xC0 < BUFSIZE ? 1 : -1]; }; } if (UPX) { curbufoffset = stubsize; bufsize = BUFSIZE - stubsize; to_xfer = size - stubsize; } else { curbufoffset = 0; bufsize = BUFSIZE; to_xfer = size; } for (curbuf = buffers; to_xfer > 0; to_xfer -= bufsize, curbuf++, curbufoffset = 0, bufsize = BUFSIZE) { if (to_xfer < bufsize) bufsize = (size_t)to_xfer; if (fwrite(&(*curbuf)[curbufoffset], 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] = realentry; /* KERNEL_START; */ fwrite(dhdr, sizeof(dhdr), 1, dest); printf("KERNEL_START = 0x%04x\n", realentry); } fclose(dest); return compress_sys_file; } static void write_header(FILE *dest, unsigned char * code, UWORD stubsize, exe_header *header) { UWORD stackpointerpatch, stacksegmentpatch, psppatch, csippatch, patchvalue; UWORD end; stackpointerpatch = code[0x100] + code[0x100 + 1] * 256U; stacksegmentpatch = code[0x102] + code[0x102 + 1] * 256U; psppatch = code[0x104] + code[0x104 + 1] * 256U; csippatch = code[0x106] + code[0x106 + 1] * 256U; end = code[0x108] + code[0x108 + 1] * 256U; if (stackpointerpatch > (end - 2) || stackpointerpatch < 32 || stacksegmentpatch > (end - 2) || stacksegmentpatch < 32 || psppatch > (end - 2) || psppatch < 32 || csippatch > (end - 4) || csippatch < 32 || end > 0xC0 || end < 32) { printf("Invalid entry file patch offsets\n"); exit(1); } patchvalue = code[stackpointerpatch] + code[stackpointerpatch + 1] * 256U; patchvalue += header->exInitSP; code[stackpointerpatch] = patchvalue & 0xFF; code[stackpointerpatch + 1] = (patchvalue >> 8) & 0xFF; patchvalue = code[stacksegmentpatch] + code[stacksegmentpatch + 1] * 256U; patchvalue += header->exInitSS + 0x60 + (stubsize >> 4); code[stacksegmentpatch] = patchvalue & 0xFF; code[stacksegmentpatch + 1] = (patchvalue >> 8) & 0xFF; patchvalue = code[psppatch] + code[psppatch + 1] * 256U; patchvalue += 0x60 + (stubsize >> 4); code[psppatch] = patchvalue & 0xFF; code[psppatch + 1] = (patchvalue >> 8) & 0xFF; patchvalue = header->exInitIP; code[csippatch] = patchvalue & 0xFF; code[csippatch + 1] = (patchvalue >> 8) & 0xFF; patchvalue = header->exInitCS + 0x60 + (stubsize >> 4); code[csippatch + 2] = patchvalue & 0xFF; code[csippatch + 3] = (patchvalue >> 8) & 0xFF; if (0 == memcmp(kernel_config, "CONFIG", 6)) { unsigned long length = kernel_config[6] + kernel_config[7] * 256UL + 8; if (length <= KERNEL_CONFIG_LENGTH) { memcpy(&code[2], kernel_config, length); printf("Copied %lu bytes of kernel config block to header\n", length); } else { printf("Error: Found %lu bytes of kernel config block, too long!\n", length); } } else { printf("Error: Found no kernel config block!\n"); } fseek(dest, 0, SEEK_SET); if (fwrite(code, 1, stubsize, dest) != stubsize) { printf("Error writing header code to output file\n"); exit(1); } } 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, *entryfilename = ""; FILE *dest, *source; long size; static unsigned char code[256 + 10 + 1]; FILE * entryf = NULL; UWORD end; UWORD stubsize = 0; /* 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 'E': entryfilename = &argptr[1]; break; case 'S': if (silentcount >= LENGTH(silentSegments)) { printf("can't handle more then %d silent's\n", (int)LENGTH(silentSegments)); exit(1); } silentSegments[silentcount++] = (short)strtol(argptr + 1, NULL, 0); break; default: usage(); } } if (UPX) { entryf = fopen(entryfilename, "rb"); if (!entryf) { printf("Cannot open entry file\n"); exit(1); } if (fread(code, 1, 256 + 10 + 1, entryf) != 256 + 10) { printf("Invalid entry file length\n"); exit(1); } end = code[0x108] + code[0x108 + 1] * 256U; if (end > 0xC0 || end < 32) { printf("Invalid entry file patch offsets\n"); exit(1); } stubsize = (end + 15U) & ~15U; } /* arguments left : infile outfile relocation offset */ compress_sys_file = exeflat(argv[1], argv[2], argv[3], silentSegments, silentcount, UPX, stubsize, 0, &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, stubsize, stubsize >> 4, &header); } /* argv[2] now contains the final flattened file: just header and trailer need to be added */ /* the compressed file has two chunks max */ rename(argv[2], "tmp.bin"); if ((dest = fopen(argv[2], "wb")) == NULL) { printf("Destination file %s could not be opened\n", argv[2]); exit(1); } if ((source = fopen("tmp.bin", "rb")) == NULL) { printf("Source file %s could not be opened\n", "tmp.bin"); exit(1); } buffer = malloc(32 * 1024); if (!buffer) { printf("Memory allocation failure\n"); exit(1); } write_header(dest, code, stubsize, &header); do { size = fread(buffer, 1, 32 * 1024, source); if (fwrite(buffer, 1, size, dest) != size) { printf("Write failure\n"); exit(1); } } while (size); remove("tmp.bin"); return 0; }