diff --git a/kernel/kernel.asm b/kernel/kernel.asm index 4f4662b..e2b6a14 100644 --- a/kernel/kernel.asm +++ b/kernel/kernel.asm @@ -46,7 +46,8 @@ STACK_SIZE equ 384/2 ; stack allocated in words %ifidn __OUTPUT_FORMAT__, obj ..start: %endif -entry: +bootloadunit: ; (byte of short jump re-used) +entry: jmp short realentry ;************************************************************ @@ -82,6 +83,10 @@ kernel_config_size: equ configend - config_signature ; must be below-or-equal the size of struct _KernelConfig ; in the file kconfig.h ! + times (32 - 4) - ($ - $$) db 0 +bootloadstack: dd 0 + + ;************************************************************ ; KERNEL CONFIGURATION AREA END ;************************************************************ @@ -101,10 +106,18 @@ kernel_config_size: equ configend - config_signature global realentry realentry: ; execution continues here - clc ; this is patched to stc by exeflat - adc byte [cs:use_upx_config], 0 - ; ! ZF used later in initialise_command_line_buffer + push cs + pop ds + xor di, di + mov byte [di + bootloadunit - $$], bl + push bp + mov word [di + bootloadstack - $$], sp + mov word [di + bootloadstack + 2 - $$], ss + jmp entry_common + + times 0C0h - ($ - $$) nop ; magic offset (used by exeflat) +entry_common: push ax push bx pushf @@ -115,15 +128,32 @@ realentry: ; execution continues here pop bx pop ax + push cs + pop ds jmp IGROUP:kernel_start -use_upx_config: db 0 - beyond_entry: times 256-(beyond_entry-entry) db 0 ; scratch area for data (DOS_PSP) segment INIT_TEXT +%ifdef TEST_FILL_INIT_TEXT + %macro step 0 + %if _LFSR & 1 + %assign _LFSR (_LFSR >> 1) ^ 0x80200003 + %else + %assign _LFSR (_LFSR >> 1) + %endif + %endmacro + + align 16 + %assign _LFSR 1 + %rep 1024 * 8 + dd _LFSR + step + %endrep +%endif + extern _FreeDOSmain extern _query_cpu @@ -131,6 +161,7 @@ segment INIT_TEXT ; kernel start-up ; kernel_start: + cld push bx pushf @@ -143,23 +174,18 @@ kernel_start: extern _kernel_command_line - ; preserve unit in bl + ; INP: ds => entry section, with CONFIG block + ; and compressed entry help data + ; (actually always used now) initialise_command_line_buffer: mov dx, I_GROUP mov es, dx - mov ax, ss - mov ds, ax - mov ax, sp ; ds:ax = ss:sp - mov si, bp ; si = bp - jz .notupx ; ! ZF still left by adc - xor ax, ax - mov ds, ax ; :5E0h -> UPX help data - lds si, [5E0h + 1Ch] ; -> original ss:sp - 2 + mov bl, [bootloadunit] + lds si, [bootloadstack] ; -> original ss:sp - 2 lea ax, [si + 2] ; ax = original sp mov si, word [si] ; si = original bp -.notupx: ; Note that the kernel command line buffer in ; the init data segment is pre-initialised to ; hold 0x00 0xFF in the first two bytes. This @@ -278,24 +304,10 @@ cont: ; Now set up call frame initialise_kernel_config: extern _InitKernelConfig - push ds - pop es ; => DOS data segment (for _BootDrive) mov ax, ss ; => init data segment mov si, 60h mov ds, si ; => entry section - mov si, _LowKernelConfig ; -> our own CONFIG block - ; This block is part of what got decompressed if - ; the kernel is UPX-compressed. And if UPX was - ; used in .SYS mode then the first several words - ; are overwritten with pseudo device header fields. - cmp byte [use_upx_config], 0 ; from UPX ? - je .notupx ; no, use own CONFIG block --> - mov si, 5Eh - mov ds, si - mov si, 2 ; -> CONFIG block of compressed entry - mov bl, [0] ; 05E0h has the boot load unit - mov byte [es:_BootDrive], bl ; overwrite the value in DOS data segment -.notupx: + mov si, _LowKernelConfig ; -> our CONFIG block mov es, ax ; => init data segment mov di, _InitKernelConfig ; -> init's CONFIG block buffer mov cx, kernel_config_size / 2 ; size that we support diff --git a/kernel/makefile b/kernel/makefile index a7c9b31..40bc933 100644 --- a/kernel/makefile +++ b/kernel/makefile @@ -36,8 +36,8 @@ production: ../bin/$(TARGET).sys ../bin/country.sys # -S to avoid showing expected relocations # 0x10 & 0x78 or 0x79 depending on compilation options -kernel.sys: kernel.exe ../utils/exeflat.exe - ..$(DIRSEP)utils$(DIRSEP)exeflat.exe kernel.exe kernel.sys $(LOADSEG) -S0x10 -S0x78 -S0x79 $(UPXOPT) $(XUPX) +kernel.sys: kernel.exe ../utils/exeflat.exe ../utils/upxentry.bin + ..$(DIRSEP)utils$(DIRSEP)exeflat.exe kernel.exe kernel.sys $(LOADSEG) -S0x10 -S0x78 -S0x79 -E..$(DIRSEP)utils$(DIRSEP)upxentry.bin $(UPXOPT) $(XUPX) kernel.exe: $(TARGET).lnk $(OBJS) $(LIBS) $(LINK) @$(TARGET).lnk; diff --git a/utils/exeflat.c b/utils/exeflat.c index 845a358..51313d8 100644 --- a/utils/exeflat.c +++ b/utils/exeflat.c @@ -58,7 +58,7 @@ large portions copied from task.c /* 32 entrypoint structure, 2 entrypoint short jump, 4 near jump / ss:sp storage */ -char kernel_config[KERNEL_CONFIG_LENGTH]; +unsigned char kernel_config[KERNEL_CONFIG_LENGTH]; typedef struct { UWORD off, seg; @@ -90,7 +90,7 @@ static void usage(void) static int exeflat(const char *srcfile, const char *dstfile, const char *start, short *silentSegments, short silentcount, - int UPX, int patchsignal, exe_header *header) + int UPX, UWORD entryparagraphs, exe_header *header) { int i, j; size_t bufsize; @@ -99,7 +99,7 @@ static int exeflat(const char *srcfile, const char *dstfile, ULONG size, to_xfer; UBYTE **buffers; UBYTE **curbuf; - UBYTE *signal; + UWORD curbufoffset; FILE *src, *dest; short silentdone = 0; int compress_sys_file; @@ -123,6 +123,7 @@ static int exeflat(const char *srcfile, const char *dstfile, 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, @@ -223,31 +224,6 @@ static int exeflat(const char *srcfile, const char *dstfile, realentry = ((UWORD)(buffers[0][2]) << 8) + buffers[0][1] + 3; } - signal = &buffers[(size_t)(realentry / BUFSIZE)][(size_t)(realentry % BUFSIZE)]; - if (patchsignal && UPX) - { - if (*signal == 0xF8) /* clc */ - { - *signal = 0xF9; /* stc */ - printf("Signal patched to indicate compression\n"); - } - else if (*signal == 0xF9) - { - printf("Signal is already patched to indicate compression\n"); - } - } - else if (patchsignal) - { - if (*signal == 0xF8) /* clc */ - { - printf("Signal not patched as no compression used\n"); - } - else if (*signal == 0xF9) - { - printf("Signal wrongly patched to indicate compression ??\n"); - } - } - if ((dest = fopen(dstfile, "wb+")) == NULL) { printf("Destination file %s could not be created\n", dstfile); @@ -255,37 +231,54 @@ static int exeflat(const char *srcfile, const char *dstfile, } /* The biggest .sys file that UPX accepts seems to be 65419 bytes long */ - compress_sys_file = size < 65420; + compress_sys_file = (size - (0xC0 - 0x10)) < 65420; if (UPX) { printf("Compressing kernel - %s format\n", (compress_sys_file)?"sys":"exe"); - } - if (UPX && !compress_sys_file) { + 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; + realsize = size + 32 - 0xC0; nheader.exPages = (UWORD)(realsize >> 9); nheader.exExtraBytes = (UWORD)realsize & 511; if (nheader.exExtraBytes) nheader.exPages++; + nheader.exInitCS = -0xC; + nheader.exInitIP = 0xC0; 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 */ - bufsize = BUFSIZE; - for (to_xfer = size, curbuf = buffers; to_xfer > 0; - to_xfer -= bufsize, curbuf++) { - if (to_xfer < BUFSIZE) + struct x { + char y[0xC0 < BUFSIZE ? 1 : -1]; + }; + } + if (UPX) { + curbufoffset = 0xC0; + bufsize = BUFSIZE - 0xC0; + to_xfer = size - 0xC0; + } 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, sizeof(char), bufsize, dest) != bufsize) - + if (fwrite(&(*curbuf)[curbufoffset], sizeof(char), bufsize, dest) != bufsize) { printf("Destination file write error\n"); exit(1); @@ -308,30 +301,59 @@ static int exeflat(const char *srcfile, const char *dstfile, return compress_sys_file; } -static void write_header(FILE *dest, size_t size) +static void write_header(FILE *dest, char const * entryfilename, + exe_header *header) { - /* UPX HEADER jump $+2+size */ - static char JumpBehindCode[] = { - /* kernel config header - 32 bytes */ - 0xeb, 0x1b, /* jmp short realentry */ - 'C', 'O', 'N', 'F', 'I', 'G', 6, 0, /* WORD */ - 0, /* DLASortByDriveNo db 0 */ - 1, /* InitDiskShowDriveAssignment db 1 */ - 2, /* SkipConfigSeconds db 2 */ - 0, /* ForceLBA db 0 */ - 1, /* GlobalEnableLBAsupport db 1 */ - 0, /* BootHarddiskSeconds */ + UWORD stackpointerpatch, stacksegmentpatch, psppatch, csippatch, patchvalue; + UWORD end; + static unsigned char code[256 + 10 + 1]; + FILE * 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); + } - '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 */ - }; + 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 > (0xC0 - 2) || stackpointerpatch < 32 + || stacksegmentpatch > (0xC0 - 2) || stacksegmentpatch < 32 + || psppatch > (0xC0 - 2) || psppatch < 32 + || csippatch > (0xC0 - 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 + 0x6C; + code[stacksegmentpatch] = patchvalue & 0xFF; + code[stacksegmentpatch + 1] = (patchvalue >> 8) & 0xFF; + patchvalue = code[psppatch] + code[psppatch + 1] * 256U; + patchvalue += 0x6C; + 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 + 0x6C; + 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(&JumpBehindCode[2], 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); @@ -340,65 +362,11 @@ static void write_header(FILE *dest, size_t size) printf("Error: Found no kernel config block!\n"); } - 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 */ -0x55, /* 20 push bp */ -0x26, 0x8C, 0x16, 0x1E, 0x00, /* 21 mov word [es:(#32 - 2)], ss */ -0x26, 0x89, 0x26, 0x1C, 0x00, /* 26 mov word [es:(#32 - 4)], sp */ - 0x1E, /* 31 push ds */ - 0x58, /* 32 pop ax */ - 0x05, 0x00, 0x00, /* 33 add ax,... */ - 0x8E, 0xD0, /* 36 mov ss,ax */ - 0xBC, 0x00, 0x00, /* 38 mov sp,... */ - 0x31, 0xC0, /* 41 xor ax,ax */ - 0xFF, 0xE0 /* 43 jmp ax */ - }; - - *(short *)&trailer[1] = (short)size + 0x20; - *(short *)&trailer[34] = header->exInitSS; - *(short *)&trailer[39] = 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[41] = 0x000626ffL; - /* set up a 4K stack for the UPX decompressor to work with */ - *(short *)&trailer[34] = 0x1000; - *(short *)&trailer[39] = 0x1000; + if (fwrite(code, 1, 0xC0, dest) != 0xC0) { + printf("Error writing header code to output file\n"); + exit(1); } - fwrite(trailer, 1, sizeof trailer, dest); } int main(int argc, char **argv) @@ -409,8 +377,8 @@ int main(int argc, char **argv) int i; size_t sz, len, len2, n; int compress_sys_file; - char *buffer, *tmpexe, *cmdbuf; - FILE *dest; + char *buffer, *tmpexe, *cmdbuf, *entryfilename = ""; + FILE *dest, *source; long size; /* if no arguments provided, show usage and exit */ @@ -431,6 +399,9 @@ int main(int argc, char **argv) case 'U': UPX = i; break; + case 'E': + entryfilename = &argptr[1]; + break; case 'S': if (silentcount >= LENGTH(silentSegments)) { @@ -452,7 +423,7 @@ int main(int argc, char **argv) compress_sys_file = exeflat(argv[1], argv[2], argv[3], silentSegments, silentcount, - UPX, 1, &header); + UPX, 0, &header); if (!UPX) exit(0); @@ -499,32 +470,42 @@ int main(int argc, char **argv) { exeflat(tmpexe, argv[2], argv[3], silentSegments, silentcount, - FALSE, 0, &header); - remove(tmpexe); + FALSE, 0xC, &header); } /* 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) + 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); } - - buffer = malloc(0xfe01); - - fread(buffer, 0xfe01, 1, dest); - size = ftell(dest); - if (size >= 0xfe00u) + if ((source = fopen("tmp.bin", "rb")) == NULL) { - printf("kernel; size too large - must be <= 0xfe00\n"); + printf("Source file %s could not be opened\n", "tmp.bin"); 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); + + buffer = malloc(32 * 1024); + if (!buffer) + { + printf("Memory allocation failure\n"); + exit(1); + } + + write_header(dest, entryfilename, &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; } diff --git a/utils/makefile b/utils/makefile index 3709e7c..d7e430d 100644 --- a/utils/makefile +++ b/utils/makefile @@ -2,11 +2,14 @@ CFLAGS = -I..$(DIRSEP)hdr -production: patchobj.com exeflat.exe +production: patchobj.com exeflat.exe upxentry.bin patchobj.com: patchobj.c $(CLT) $(CFLAGS) patchobj.c +upxentry.bin: upxentry.asm + $(NASM) -f bin upxentry.asm -o upxentry.bin + exeflat.exe: exeflat.c ../hdr/exe.h $(CLC) $(CFLAGS) exeflat.c @@ -15,5 +18,5 @@ clobber: clean clean: $(RM) *.obj *.bak *.crf *.xrf *.map *.lst *.las *.cod *.err status.me - $(RM) exeflat.exe patchobj.com + $(RM) exeflat.exe patchobj.com upxentry.bin diff --git a/utils/upxentry.asm b/utils/upxentry.asm new file mode 100644 index 0000000..a04005d --- /dev/null +++ b/utils/upxentry.asm @@ -0,0 +1,47 @@ + cpu 8086 + org 0 + +bootloadunit: ; (byte of short jump re-used) +start: + jmp strict short entry + times (32 - 4) - ($ - $$) db 0 + ; area for CONFIG block + +bootloadstack: ; (dword re-used for original ss:sp) +entry: + ; common setup (copied from kernel.asm) + push cs + pop ds + xor di, di + mov byte [di + bootloadunit - $$], bl + push bp + mov word [di + bootloadstack - $$], sp + mov word [di + bootloadstack + 2 - $$], ss + + ; the UPX DOS/EXE depacker needs a certain ss:sp + cli + mov ax, 0 +patchstacksegment: equ $ - 2 + mov ss, ax + mov sp, 0 +patchstackpointer: equ $ - 2 + sti + + mov ax, -10h +patchpspsegment: equ $ - 2 + mov ds, ax + mov es, ax + + jmp 0:0 +patchcsip: equ $ - 4 +end: + + times 0C0h - ($ - $$) nop +entry_common: + + times 100h - ($ - $$) db 0 + dw patchstackpointer + dw patchstacksegment + dw patchpspsegment + dw patchcsip + dw end