/************************************************************************ * * Copyright 2015 by Sean Conner. All Rights Reserved. * * This program 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 * of the License, or (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Comments, questions and criticisms can be sent to: sean@conman.org * *************************************************************************/ /*------------------------------------------------------------------ ; Some links to help with the program. ; ; http://man7.org/linux/man-pages/man2/vm86.2.html ; http://www.ecstaticlyrics.com/notes/vm86 ; ;-------------------------------------------------------------------*/ #include #include #include #include #include /*-------------------------------------------------------------------------- ; This header is part of https://github.com/spc476/CGILib and can be ; downloaded and installed from there, or just remove this line and the call ; to crashreport(). It's only used to determine why a segfault happened ; (and lead to the direct resolution of a bug). ;---------------------------------------------------------------------------*/ #include int main(void) { struct vm86plus_struct vm; unsigned char *mem; int rc; crashreport(SIGSEGV); /* remove if you don't have */ /*------------------------------------------------------------------- ; For the virtual 8086 (the vm86() syscall), we need to map memory into ; the first meg of our process address space (which is all an 8086 can ; address). The memory mapped needs to be readable, writable and ; executable. We probably *don't* actually need a fully contiguous 1 ; megabyte, but this is exploratory code here and it will help to ; determine what exactly we need. ;----------------------------------------------------------------------*/ mem = mmap( 0, 1024*1024, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0 ); if (mem == MAP_FAILED) { perror("mmap()"); return EXIT_FAILURE; } /*-------------------------------------------------------------------- ; Initialize the VM record. Set to 0 initially, then only set the fields ; that are required. We set our CPU to an 8086, set up our code at ; $1000:0010 and our stack to $2000:FFFE. We also signal to Linux that we ; want to handle *all* interrupts. Since we don't have actual hardware ; interrupts, that's okay. But this does ensure we catch any spurious ; interrupts in our code and not with Linux. The interrupts to catch is a ; bit field of 256 entries. There are two fields and from some playing ; around, only one of the two fields needs to be set, but I'm setting both ; just to be pedantic because the documentation for this system call is ; very sparse. ;------------------------------------------------------------------------*/ memset(&vm,0,sizeof(vm)); memset(&vm.int_revectored, 255,sizeof(vm.int_revectored)); memset(&vm.int21_revectored,255,sizeof(vm.int21_revectored)); vm.cpu_type = CPU_086; vm.regs.cs = 0x100; vm.regs.eip = 0x10; vm.regs.ss = 0x200; vm.regs.esp = 0xFFFE; /*--------------------------------------------------------------------- ; Disassebmly of this as 8086 code: ; ; mov ax,1234h ; int 21h ; int 20h ; ; And just in case we are not running in 16-bit mode but 32 bit mode, ; this will decode to: ; ; mov eax,21CD1234h ; int 20h ; ; From our return code, we'll know if we successfully ran in 16 bit mode ; or 32 bit mode. ;------------------------------------------------------------------------*/ mem[0x1010] = 0xB8; mem[0x1011] = 0x34; mem[0x1012] = 0x12; mem[0x1013] = 0xCD; mem[0x1014] = 0x21; mem[0x1015] = 0xCD; mem[0x1016] = 0x20; /*------------------------------------------------------------------- ; And a miracle happens! If you see on the first line: ; ; 2:33 ; ; Then this ran in 16-bit mode. If you see ; ; 2:32 ; ; This was in 32-bit mode. VM86_TYPE() returns VM86_INTx (2) as an INT ; instruction was executed, and VM86_ARG() returns the operand of the INT ; instruction. ;------------------------------------------------------------------------*/ rc = vm86(VM86_ENTER,&vm); if (rc < 0) perror("vm86()"); else { printf("%d:%d\n",VM86_TYPE(rc),VM86_ARG(rc)); printf("%08lX %08lX %08lX\n",vm.regs.eip,vm.regs.eax,vm.regs.orig_eax); printf("%02X\n",mem[0x1010]); } munmap(mem,1024*1024); return EXIT_SUCCESS; }