The code is here: https://github.com/OxNinja/C-VM
Step 1: stack first#
The first thing I did was implementing the stack, according to the previous posts of this series. I changed the code a little bit, but the idea stays the same.
The patched code for the stack:
1typedef struct stack_node {
2 // Chained list
3 struct stack_node *prev;
4 struct stack_node *next;
5 // Void pointer for values
6 // each stack node will have a pointer towards the target value
7 int *val;
8} stack_node;
9
10typedef struct stack {
11 // Pointers to first and last elements
12 stack_node *first;
13 stack_node *last;
14} stack;
My new take on this project was to code everything in the same .h
file, to ease using this on other projects. The first version was composed of many .h
and .c
files, which helped while developping, but needed more work to implement on external projects.
Now, one shall only #include "vm.h"
to get started!
Step 2: add instructions#
I then re-implemented my old code for the instructions and the parser. This was very easy, and needed 0 fix surprisingly, but who am I to spit like that.
So as the last version, here is the code:
1void vm_emulate(registers *regs, stack *s, int shellcode) {
2 int opcode = (shellcode & 0xff000000) >> 0x18;
3
4 // instructions is an array of pointers of function
5 // each index points to the according function corresponding to the opcode
6 // it is very easy to change the opcode for a certain function
7 void (*instructions[10])(registers *, stack *, int);
8 // no opcode 0 defined for the moment
9 instructions[1] = vm_mov;
10 instructions[2] = vm_push;
11 instructions[3] = vm_add;
12 instructions[4] = vm_sub;
13 instructions[5] = vm_jmp;
14 instructions[6] = vm_cmp;
15 instructions[7] = vm_call;
16 instructions[8] = vm_exit;
17 instructions[9] = vm_pop;
18 // this is not optimal, as this occurs every time we want to emulate code
19 // one should declare this array once for all for better performance
20
21 (*instructions[opcode])(regs, s, shellcode);
22}
Step 3: quick test#
I could test my code with the following simple testcase:
1#include <stdio.h>
2#include <stdlib.h>
3
4#include "vm.h"
5
6int main(void) {
7 // init VM
8 // init stack
9 stack *stack = stack_init();
10 // init heap?
11 // init registers
12 registers *regs = registers_init(stack);
13
14 registers_print(regs);
15
16 // emulate some instructions
17 // mov a, 0x45
18 vm_emulate(regs, stack, 0x1100045);
19 // mov c, 0x2
20 vm_emulate(regs, stack, 0x1120002);
21 registers_print(regs);
22 // exit(a)
23 vm_emulate(regs, stack, 0x8000000);
24
25 return 0;
26}
Step 4: add push/pop#
Wrote code for stack-related stuff (push, pop).