├── .gitignore ├── Makefile ├── src ├── stack.h ├── instructions.h ├── main.h ├── stack.c ├── registers.h ├── registers.c ├── main.c └── instructions.c ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .gdb_history 2 | *.out 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | sources = src/* 2 | outfile = vm.out 3 | 4 | ccf = -Wall 5 | 6 | default: 7 | make build 8 | 9 | build: $(sources) 10 | gcc $(sources) -o $(outfile) $(ccf) 11 | 12 | clean: 13 | rm -f $(outfile) 14 | -------------------------------------------------------------------------------- /src/stack.h: -------------------------------------------------------------------------------- 1 | #ifndef VM_STACK_HEADER 2 | #define VM_STACK_HEADER 3 | 4 | typedef struct Stack { 5 | // LIFO stack 6 | 7 | // max size of the stack 8 | int max_size; 9 | 10 | // pointers for the stack 11 | int *stack, *stack_base, *stack_end, **stack_pointer; 12 | 13 | } Stack; 14 | 15 | void setup_stack(Stack *stack); 16 | void print_stack(Stack *stack); 17 | void stack_inc(Stack *stack); 18 | void stack_dec(Stack *stack); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/instructions.h: -------------------------------------------------------------------------------- 1 | #ifndef VM_INSTRUCTIONS_HEADER 2 | #define VM_INSTRUCTIONS_HEADER 3 | 4 | #include "registers.h" 5 | 6 | void my_mov(Registers *regs, Stack *stack, int shellcode); 7 | void my_add(Registers *regs, Stack *stack, int shellcode); 8 | void my_sub(Registers *regs, Stack *stack, int shellcode); 9 | void my_jmp(Registers *regs, Stack *stack, int shellcode); 10 | void my_call(Registers *regs, Stack *stack, int shellcode); 11 | void my_exit(Registers *regs, Stack *stack, int shellcode); 12 | void my_cmp(Registers *regs, Stack *stack, int shellcode); 13 | void my_push(Registers *regs, Stack *stack, int shellcode); 14 | void my_pop(Registers *regs, Stack *stack, int shellcode); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | #ifndef VM_MAIN_HEADER 2 | #define VM_MAIN_HEADER 3 | 4 | #include 5 | #include 6 | 7 | #include "registers.h" 8 | 9 | #define TRUE 1 10 | #define FALSE 0 11 | #define STACK_UNIT sizeof(void *) 12 | #define FLAG_ZERO 0x10000000 13 | 14 | #define except(msg) \ 15 | do { \ 16 | perror(msg); \ 17 | exit(EXIT_FAILURE); \ 18 | } while (0) 19 | 20 | 21 | void emulate(Registers *regs, Stack *stack, int shellcode); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://img.shields.io/github/languages/code-size/oxninja/c-vm) 2 | ![](https://img.shields.io/tokei/lines/github/oxninja/c-vm) 3 | ![](https://img.shields.io/maintenance/yes/2022) 4 | 5 | # C-VM 6 | 7 | My try to implement a virtual CPU in C 8 | 9 | More details on [my blog article](https://0xninja.fr/c-vm/). 10 | 11 | This VM implements the following features: 12 | 13 | - [X] `mov reg, reg/val` 14 | - [X] `add reg, reg/val` 15 | - [X] `sub reg, reg/val` 16 | - [X] `exit` 17 | - [X] `cmp reg, reg/val` 18 | - [ ] `push reg/val` 19 | - [ ] `pop reg` 20 | - [ ] `jmp addr` 21 | - [ ] `call label` 22 | - [X] Registers 23 | - [X] `a b c d`: common operations 24 | - [X] Flags (one register with flags in it) 25 | - [X] Zero flag `0x10000000` 26 | - [ ] Virtual stack 27 | - [ ] Virtual heap 28 | -------------------------------------------------------------------------------- /src/stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "main.h" 5 | #include "stack.h" 6 | 7 | void setup_stack(Stack *stack) { 8 | int stack_size = stack->max_size * STACK_UNIT; 9 | stack->stack = (int *) malloc(stack_size); 10 | stack->stack_base = stack->stack; 11 | stack->stack_end = stack->stack + stack_size; 12 | stack->stack_pointer = &stack->stack; 13 | } 14 | 15 | void print_stack(Stack *stack) { 16 | printf("--- Stack content ---\n"); 17 | int stack_size = *stack->stack_pointer - stack->stack; 18 | for(int i = 0; i < stack_size; i += STACK_UNIT) { 19 | printf("[+0x%04x]: %x\n", i, *(*(stack->stack_pointer + i))); 20 | } 21 | } 22 | 23 | void stack_inc(Stack *stack) { 24 | stack->stack_pointer += STACK_UNIT; 25 | } 26 | 27 | void stack_dec(Stack *stack) { 28 | stack->stack_pointer -= STACK_UNIT; 29 | } 30 | -------------------------------------------------------------------------------- /src/registers.h: -------------------------------------------------------------------------------- 1 | #ifndef VM_REGISTERS_HEADER 2 | #define VM_REGISTERS_HEADER 3 | 4 | #include "stack.h" 5 | 6 | typedef struct Registers { 7 | // common operations registers 8 | int a, b, c, d; 9 | 10 | // array to work with when manipulating registers' indexes 11 | // each element will point to the address of the correspponding register 12 | // see `setup_registers()` for more details 13 | int *registers[4]; 14 | 15 | // stack registers 16 | void *sb, *sp; 17 | 18 | // flags are stored in one integer, using masks to extract them 19 | // remainder, zero (cmp) 20 | int flags; 21 | } Registers; 22 | 23 | void setup_registers(Registers *regs); 24 | void print_registers(Registers *regs); 25 | void reset_registers(Registers *regs); 26 | void set_zero_flag(Registers *regs, int cmp); 27 | void setup_stack_registers(Registers *regs, Stack *stack); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /src/registers.c: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | #include "registers.h" 3 | #include "stack.h" 4 | 5 | void setup_registers(Registers *regs) { 6 | // each element of the array points to the corresponding register's address 7 | regs->registers[0] = ®s->a; 8 | regs->registers[1] = ®s->b; 9 | regs->registers[2] = ®s->c; 10 | regs->registers[3] = ®s->d; 11 | } 12 | 13 | void print_registers(Registers *regs) { 14 | printf("=== Registers: ===\n"); 15 | printf("a: 0x%x\n", regs->a); 16 | printf("b: 0x%x\n", regs->b); 17 | printf("c: 0x%x\n", regs->c); 18 | printf("d: 0x%x\n", regs->d); 19 | printf("flags: 0x%x\n", regs->flags); 20 | printf("=== Stack: ===\n"); 21 | printf("sb: %p\n", regs->sb); 22 | printf("sp: %p\n", regs->sp); 23 | } 24 | 25 | void reset_registers(Registers *regs) { 26 | regs->a = 0; 27 | regs->b = 0; 28 | regs->c = 0; 29 | regs->d = 0; 30 | regs->flags = 0; 31 | } 32 | 33 | void setup_stack_registers(Registers *regs, Stack *stack) { 34 | regs->sb = stack->stack_base; 35 | regs->sp = stack->stack_pointer; 36 | } 37 | 38 | void set_zero_flag(Registers *regs, int cmp) { 39 | if (cmp == TRUE) { 40 | regs->flags |= FLAG_ZERO; 41 | } else if (cmp == FALSE) { 42 | regs->flags |= FLAG_ZERO; 43 | regs->flags ^= FLAG_ZERO; 44 | } else { 45 | except("Invalid value for set_zero_flag (cmp is neither of TRUE or FALSE)"); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "main.h" 5 | #include "instructions.h" 6 | #include "registers.h" 7 | #include "stack.h" 8 | 9 | int main(void) { 10 | // init the stack 11 | Stack stack; 12 | // stack of 0x100 pointers max 13 | stack.max_size = 0x100; 14 | // setup the stack 15 | setup_stack(&stack); 16 | 17 | // init the registers 18 | Registers regs; 19 | // setup the registers' array 20 | setup_registers(®s); 21 | // setup the registers 22 | setup_stack_registers(®s, &stack); 23 | 24 | // set everything to 0 25 | reset_registers(®s); 26 | 27 | // mov a, 0x45 28 | emulate(®s, &stack, 0x1100045); 29 | print_registers(®s); 30 | 31 | // push 0x2b 32 | emulate(®s, &stack, 0x0200002b); 33 | print_registers(®s); 34 | print_stack(&stack); 35 | 36 | // pop a 37 | emulate(®s, &stack, 0x09000000); 38 | print_registers(®s); 39 | print_stack(&stack); 40 | 41 | return 0; 42 | } 43 | 44 | 45 | void emulate(Registers *regs, Stack *stack, int shellcode) { 46 | int opcode = (shellcode & 0xff000000) >> 0x18; 47 | 48 | // instructions is an array of pointers of function 49 | // each index points to the according function corresponding to the opcode 50 | // it is very easy to change the opcode for a certain function 51 | void (*instructions[10])(Registers *, Stack *, int); 52 | // no opcode 0 defined for the moment 53 | instructions[1] = my_mov; 54 | instructions[2] = my_push; 55 | instructions[3] = my_add; 56 | instructions[4] = my_sub; 57 | instructions[5] = my_jmp; 58 | instructions[6] = my_cmp; 59 | instructions[7] = my_call; 60 | instructions[8] = my_exit; 61 | instructions[9] = my_pop; 62 | // this is not optimal, as this occurs every time we want to emulate code 63 | // one should declare this array once for all for better performance 64 | 65 | (*instructions[opcode])(regs, stack, shellcode); 66 | } 67 | -------------------------------------------------------------------------------- /src/instructions.c: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | #include "stack.h" 3 | #include "instructions.h" 4 | 5 | void my_mov(Registers *regs, Stack *stack, int shellcode) { 6 | int is_reg1 = (shellcode & 0x00f00000) >> 0x14; 7 | if (is_reg1 == 0x1) { 8 | // get index of target reg 9 | int target_reg = (shellcode & 0x000f0000) >> 0x10; 10 | // get value to mov 11 | int is_reg2 = (shellcode & 0x0000f000) >> 0xc; 12 | // get moved value 13 | int value = (shellcode & 0x00000fff); 14 | // if source is a register and not a value 15 | if (is_reg2 == 0x1) { 16 | int source_reg = value >> 0x8; 17 | value = *regs->registers[source_reg]; 18 | } 19 | 20 | // finally, move the value into the register 21 | *regs->registers[target_reg] = value; 22 | 23 | } else { 24 | except("Invalid value for mov (arg a is not a register)"); 25 | } 26 | } 27 | 28 | void my_add(Registers *regs, Stack *stack, int shellcode) { 29 | int is_reg1 = (shellcode & 0x00f00000) >> 0x14; 30 | if (is_reg1 == 0x1) { 31 | // get index of target reg 32 | int target_reg = (shellcode & 0x000f0000) >> 0x10; 33 | // get value to add 34 | int is_reg2 = (shellcode & 0x0000f000) >> 0xc; 35 | // get value 36 | int value = (shellcode & 0x00000fff); 37 | // if source is a register and not a value 38 | if (is_reg2 == 0x1) { 39 | int source_reg = value >> 0x8; 40 | value = *regs->registers[source_reg]; 41 | } 42 | 43 | // finally, add the value into the register 44 | *regs->registers[target_reg] += value; 45 | 46 | } else { 47 | except("Invalid value for add (arg a is not a register)"); 48 | } 49 | } 50 | 51 | void my_sub(Registers *regs, Stack *stack, int shellcode) { 52 | int is_reg1 = (shellcode & 0x00f00000) >> 0x14; 53 | if (is_reg1 == 0x1) { 54 | // get index of target reg 55 | int target_reg = (shellcode & 0x000f0000) >> 0x10; 56 | // get value to sub 57 | int is_reg2 = (shellcode & 0x0000f000) >> 0xc; 58 | // get value 59 | int value = (shellcode & 0x00000fff); 60 | // if source is a register and not a value 61 | if (is_reg2 == 0x1) { 62 | int source_reg = value >> 0x8; 63 | value = *regs->registers[source_reg]; 64 | } 65 | 66 | // finally, sub the value into the register 67 | *regs->registers[target_reg] -= value; 68 | 69 | } else { 70 | except("Invalid value for sub (arg a is not a register)"); 71 | } 72 | } 73 | 74 | void my_jmp(Registers *regs, Stack *stack, int shellcode) {} 75 | void my_call(Registers *regs, Stack *stack, int shellcode) {} 76 | 77 | void my_exit(Registers *regs, Stack *stack, int shellcode) { exit(regs->a); } 78 | 79 | void my_cmp(Registers *regs, Stack *stack, int shellcode) { 80 | int is_reg1 = (shellcode & 0x00f00000) >> 0x14; 81 | if (is_reg1 == 0x1) { 82 | // get index of target reg 83 | int target_reg = (shellcode & 0x000f0000) >> 0x10; 84 | // get value to cmp 85 | int is_reg2 = (shellcode & 0x0000f000) >> 0xc; 86 | // get cmp value 87 | int value = (shellcode & 0x00000fff); 88 | // if source is a register and not a value 89 | if (is_reg2 == 0x1) { 90 | int source_reg = value >> 0x8; 91 | value = *regs->registers[source_reg]; 92 | } 93 | 94 | // finally, cmp the value with the register's and set the flags 95 | if (*regs->registers[target_reg] == value) { 96 | set_zero_flag(regs, TRUE); 97 | } else { 98 | set_zero_flag(regs, FALSE); 99 | } 100 | 101 | } else { 102 | except("Invalid value for mov (arg a is not a register)"); 103 | } 104 | } 105 | 106 | void my_push(Registers *regs, Stack *stack, int shellcode) { 107 | int value = shellcode & 0x00ffffff; 108 | int *pointer = &value; 109 | *stack->stack_pointer = pointer; 110 | stack_inc(stack); 111 | } 112 | 113 | void my_pop(Registers *regs, Stack *stack, int shellcode) { 114 | // int target_reg = (shellcode & 0x00f00000) >> 0x14; 115 | stack_dec(stack); 116 | // int value = *stack->stack_pointer; 117 | // regs->registers[target_reg] = value; 118 | } 119 | --------------------------------------------------------------------------------