├── .gitignore ├── Makefile ├── README.md ├── com.ld ├── hello.asm └── loader.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.com 2 | *.elf 3 | *.o 4 | loader 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NASM=/usr/bin/nasm 2 | 3 | all: run 4 | 5 | hello.o: hello.asm 6 | $(NASM) -f elf64 -o $@ $< 7 | 8 | hello.elf: hello.o com.ld 9 | ld -T com.ld -o $@ $< 10 | 11 | hello.com: hello.elf 12 | objcopy -S --output-target=binary $< $@ 13 | 14 | loader: loader.c 15 | gcc -Wall -g -o $@ $< 16 | 17 | run: hello.com loader 18 | ./loader $< 19 | 20 | clean: 21 | rm -f *.o *.elf *.com loader 22 | 23 | .PHONY: all run clean 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linux COM loader 2 | 3 | This is a (very) simple loader for Linux that loads and executes a binary file 4 | in much the same way CP/M and DOS loaded .COM executables. 5 | 6 | 0x10000 bytes are allocated, starting at 0x0000. Binary files, up to 0xff00 7 | bytes in length, are loaded starting at 0x100. Stack top is set to 0x10000 and 8 | loader jumps to instruction at 0x100. 9 | 10 | What would be the program segment prefix (0x0000 - 0x0100) is left unused. 11 | 12 | If you're confused, see [this blog post](https://www.tablix.org/~avian/blog/archives/2016/08/linux_loader_for_dos_like_com_files/) for some details. 13 | 14 | **You can't run DOS .COM executables with this. Code is executed as a 15 | normal Linux process using the current architecture. No attempt is made to 16 | simulate x86 real mode or DOS services.** 17 | 18 | ## Usage 19 | 20 | Disable the security feature that prevents us from mmaping the zero page (as 21 | root): 22 | 23 | # sysctl vm.mmap_min_addr=0 24 | 25 | Run `make` to build the loader, build a "Hello World" .COM binary and execute 26 | it: 27 | 28 | $ make 29 | Hello, World! 30 | 31 | To make kernel aware of the loader (as root): 32 | 33 | # update-binfmts --install com /path/to/loader --extension com 34 | 35 | Now you can run .COM binaries directly! 36 | 37 | $ ./hello.com 38 | Hello, world! 39 | 40 | ## License 41 | 42 | Linux COM loader was written by Tomaz Solc on a Monday evening. It is a silly thing 43 | not meant for serious use. 44 | 45 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 46 | Version 2, December 2004 47 | 48 | Copyright (C) 2004 Sam Hocevar 49 | 50 | Everyone is permitted to copy and distribute verbatim or modified 51 | copies of this license document, and changing it is allowed as long 52 | as the name is changed. 53 | 54 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 55 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 56 | 57 | 0. You just DO WHAT THE FUCK YOU WANT TO. 58 | -------------------------------------------------------------------------------- /com.ld: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | RAM (rwx) : ORIGIN = 0x0100, LENGTH = 0xff00 4 | } 5 | -------------------------------------------------------------------------------- /hello.asm: -------------------------------------------------------------------------------- 1 | call greet 2 | mov rax, 60 3 | mov rdi, 0 4 | syscall 5 | 6 | # A function call, just to show off our working stack. 7 | greet: 8 | mov rax, 1 9 | mov rdi, 1 10 | mov rsi, msg 11 | mov rdx, 14 12 | syscall 13 | ret 14 | 15 | msg db "Hello, World!", 10 16 | -------------------------------------------------------------------------------- /loader.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char **argv) 7 | { 8 | if(argc != 2) { 9 | printf("Linux COM loader by tomaz.solc@tablix.org\nUSAGE: %s file\n", argv[0]); 10 | return 0; 11 | } 12 | const char* path = argv[1]; 13 | 14 | void *p; 15 | /* Allocate space from 0x00000 to 0x10000 16 | * 17 | * Can't mmap file directly at 0x100 because it's not aligned with 18 | * page boundaries. 19 | * 20 | * sudo sysctl vm.mmap_min_addr=0 21 | */ 22 | size_t length = 0x10000; 23 | p = mmap((void*)0, length, PROT_EXEC|PROT_READ|PROT_WRITE, 24 | MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0); 25 | if(p == MAP_FAILED) { 26 | perror("Error in mmap()"); 27 | return -1; 28 | } 29 | 30 | /* Load file at address 0x100. 31 | * 32 | * We could set up PSP as well, but most of it doesn't make much sense 33 | * on Linux. */ 34 | size_t offset = 0x100; 35 | int fd; 36 | fd = open(path, O_RDWR); 37 | if(fd == -1) { 38 | perror("Error in open()"); 39 | return -1; 40 | } 41 | 42 | void *pw = (void*)offset; 43 | while(1) { 44 | ssize_t r = read(fd, pw, 1024); 45 | if(r == -1) { 46 | perror("Error in read()"); 47 | return -1; 48 | } else if(r == 0) { 49 | break; 50 | } else { 51 | pw += r; 52 | } 53 | } 54 | 55 | close(fd); 56 | 57 | /* Set stack pointer to end of allocated area, jump to 0x100. 58 | * 59 | * Note, we still have loader and other stuff mmapped at various 60 | * addresses. It would be nice if we could unmap everything else 61 | * before jumping to code. */ 62 | asm( 63 | "mov $0x10000, %rsp\n" 64 | "jmp 0x100\n" 65 | ); 66 | 67 | return 0; 68 | } 69 | --------------------------------------------------------------------------------