Useless emulator for fun (vmndh-2k12)

by @Jonathan Salwan - 2012-03-26

Emulator NDH 2K12

This emulator was created for the CTF NDH 2012. Some challenges were on the NDH architecture. The NDH architecture is a new architecture which looks like a mix between ARM and x86.

GitHub (Stable v0.1) https://github.com/JonathanSalwan/VMNDH-2k12/

Authors

Installation

$ git clone git@github.com:JonathanSalwan/VMNDH-2k12.git
$ cd ./VMNDH-2k12
$ make

NDH Architecture

STACK   [0000 - 7FFF]  (default ASLR is disable. Possibility to set ASLR with -aslr)
Program [8000 - FFFE]  (default NX & PIE are disable. Possibility to set NX & PIE with -nx -pie)

ASLR genered with:
                  __asm__("mov %gs:0x14, %eax");
                  __asm__("shr $0x08, %eax");
                  __asm__("mov %%eax, %0" : "=m"(aslr));
                  aslr = aslr % 0x3000 + 0x4ffe;

PIE genered with:
                  __asm__("mov %gs:0x14, %eax");
                  __asm__("shr $0x08, %eax");
                  __asm__("mov %%eax, %0" : "=m"(pie));
                  pie = pie % 0x3000 + 0x8000;



 ^   0000>+-----------------+
 |        |                 |             The max size of binary is 0x7ffe.
 |        |                 |
 |        |                 |             Before the program is executed, argv and argc are
 |        |     STACK    ^^ |             pushed on the stack.
 |        |              || |             If an argument is set with (-arg), argc = 1
 |        |                 |             and argv points to the string.
 |        +-----------------+< SP & BP
 6        |     ARG         |             If you don't set an argument, argc and argv
 4        |                 |             are pushed with value 0x00.
 K   8000>+-----------------+< PC
 |        |                 |
 |        |              || |             Exemple1: ./vmndh -file ./binary
 |        |     CODE     vv |
 |        |                 |                       [SP] 0x00 0x00 0x00 0x00 0x00 0x00
 |        |                 |                            <--argc-> <-argv-->
 |        |                 |
 v   ffff>+-----------------+             Exemple2: ./vmndh -file ./binary -arg "abcd"

                                                    [SP] 0x01 0x00 0xac 0x7f 0x00 0x00
                                                         <--argc-> <-argv-->

File Format

[MAGIC][size .text][.text content]

MAGIC: ".NDH"
SIZE:  size of section TEXT
CODE:  instructions

Instructions Encoding

[OPCODE] [OP_FLAGS | !] [OPERAND #1] [OPERAND #2]


[ADD]   <opcode> <FLAG> <REG> <REG | DIR8 | DIR16> (size = 4 or 5 bytes)
        - reg   = add
        - dir8  = addb
        - dir16 = addl

[AND]   <opcode> <FLAG> <REG> <REG | DIR8 | DIR16> (size = 4 or 5 bytes)
        - reg   = and
        - dir8  = andb
        - dir16 = andl

[CALL]  <opcode> <FLAG> <REG | DIR16> (size = 3 or 4 bytes)

[CMP]   <opcode> <FLAG> <REG> <REG | DIR8 | DIR16> (size = 4 or 5 bytes)
        - reg   = cmp
        - dir8  = cmpb
        - dir16 = cmpl

[DEC]   <opcode> <REG> (size = 2)

[DIV]   <opcode> <FLAG> <REG> <REG | DIR8 | DIR16> (size = 4 or 5 bytes)
        - reg   = div
        - dir8  = divb
        - dir16 = divl

[END]   <opcode> (size = 1 byte)

[INC]   <opcode> <REG> (size = 2 bytes)

[JMPL]  <opcode> <DIR16> (size = 3 bytes)

[JMPS]  <opcode> <DIR8> (size = 2 bytes)

[JNZ]   <opcode> <DIR16> (size = 3 bytes)

[JZ]    <opcode> <DIR16> (size = 3 bytes)

[JA]    <opcode> <DIR16> (size = 3 bytes)

[JB]    <opcode> <DIR16> (size = 3 bytes)

[MOV]   <opcode> <FLAG> <REG | REG_INDIRECT> <REG | REG_INDIRECT | DIR8 | DIR16> (size = 4 or 5 bytes)
        - reg   = mov
        - dir8  = movb
        - dir16 = movl
        - indir = mov [rX]

[MUL]   <opcode> <FLAG> <REG> <REG | DIR8 | DIR16> (size = 4 or 5 bytes)
        - reg   = mul
        - dir8  = mulb
        - dir16 = mull

[NOP]   <opcode> (size = 1 byte)

[NOT]   <opcode> <REG> (size = 2 bytes)

[OR]    <opcode> <FLAG> <REG> <REG | DIR8 | DIR16> (size = 4 or 5 bytes)
        - reg   = or
        - dir8  = orb
        - dir16 = orl

[POP]   <opcode> <REG> (size = 2 bytes)

[PUSH]  <opcode> <FLAG> <REG | DIR08 | DIR16> (size = 3 or 4 bytes)
        - reg   = push
        - dir8  = pushb
        - dir16 = pushl

[RET]   <opcode> (size = 1 byte)

[SUB]   <opcode> <FLAG> <REG> <REG | DIR8 | DIR16> (size = 4 or 5 bytes)
        - reg   = sub
        - dir8  = subb
        - dir16 = subl

[SYSCALL] <opcode> (size = 1 byte)

[TEST]  <opcode> <REG> <REG> (size = 3 bytes)

[XCHG]  <opcode> <REG> <REG> (size = 3 bytes

[XOR]   <opcode> <FLAG> <REG> <REG | DIR8 | DIR16> (size = 4 or 5 bytes)
        - reg   = xor
        - dir8  = xorb
        - dir16 = xorl

Syscalls

r0 = syscall number
r1 = arg1
r2 = arg2
r3 = arg3
r4 = arg4

syscalls supported: open(), read(), write(), close(), exit(), setuid(), setgid(), dup2(),
                    send() recv(), socket(), listen(), bind(), accept(), chdir(), chmod(),
                    lseek(), getpid(), getuid(), pause()


[sys_open]    r1 = uint16_t *
              r2 = uint16_t
              r3 = uint16_t

[sys_exit]    r1 = uint16_t

[sys_read]    r1 = uint16_t
              r2 = uint16_t *
              r3 = uint16_t

[sys_write]   r1 = uint16_t
              r2 = uint16_t *
              r3 = uint16_t

[sys_close]   r1 = uint16_t

[sys_exit]    r1 = uint16_t

[sys_setuid]  r1 = uint16_t

[sys_setgid]  r1 = uint16_t

[sys_dup2]    r1 = uint16_t
              r2 = uint16_t

[sys_send]    r1 = uint16_t
              r2 = uint16_t *
              r3 = uint16_t
              r4 = uint16_t


[sys_recv]    r1 = uint16_t
              r2 = uint16_t *
              r3 = uint16_t
              r4 = uint16_t

[sys_socket]  r1 = uint16_t
              r2 = uint16_t
              r3 = uint16_t

[sys_listen]  r1 = uint16_t
              r2 = uint16_t

[sys_bind]    r1 = uint16_t (socket)
              r2 = uint16_t (port)

[sys_accept]  r1 = uint16_t (socket)

[SYS_CHDIR]   r1 = uint16_t *

[SYS_CHMOD]   r1 = uint16_t *
              r2 = uint16_t

[SYS_LSEEK]   r1 = uint16_t
              r2 = uint16_t
              r3 = uint16_t

[SYS_GETPID]  n/a

[SYS_GETUID]  n/a

[SYS_PAUSE]   n/a

Flags

ZF is set with following instructions:

  • ADD
  • SUB
  • MUL
  • DIC
  • INC
  • DEC
  • OR
  • XOR
  • AND
  • NOT
  • TEST
  • CMP

AF & BF are set with following instructions:

  • CMP

AF & BF are used for JA and JB instructions.

Usage

Syntax:  ./vmndh [OPTION]  [FLAG]

OPTION:
             -file         Load binary
             -arg          Binary argument (optional)
FLAG:
             -aslr         Enable ASLR
             -nx           Enable NX bit
             -pie          Enable PIE
             -debug        Debug console
             -core         Generates a core dump when segfault

Compiler

The compiler is written in python and support labels, comments and includes. You can see the following source code with the famous 'Hello World'.

; NDH Hello world sample

.label main
movl r0, :helloworld
movl r1, #0
movl r2, #0
movl r5, #0

.label  loop
    mov r2, [r0]
    test r2,r2
    inc r5
    inc r0
    jnz :loop

movb r0, #4
movb r1, #1
movl r2, :helloworld
mov r3, r5
syscall
end

.label helloworld
.db "Hello World !",0x0A,0

Compile example

$ ./ndasm/ndasm.py -i ./src_asm_challenge/hello_world.asm -o hello_world.ndh
[*] Parsing source file ...
[+] Assembling ...
[*] Linking ...
[*] Creating outfile ...
[*] Done. 71 bytes written.

$ ./vmndh -file ./hello_world.ndh
Hello World !
$