From a4e04297a708c966171ae97d21b36727d3d00e9d Mon Sep 17 00:00:00 2001 From: CPunch Date: Fri, 29 Apr 2022 15:51:59 -0500 Subject: [PATCH] Tool: added VM Test - includes a tiny demo for decoding secret messages --- lib/include/lvm.h | 14 ++++++++--- tools/CMakeLists.txt | 1 + tools/vmtest/CMakeLists.txt | 14 +++++++++++ tools/vmtest/src/main.c | 46 +++++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 tools/vmtest/CMakeLists.txt create mode 100644 tools/vmtest/src/main.c diff --git a/lib/include/lvm.h b/lib/include/lvm.h index 7ca40c3..f29767b 100644 --- a/lib/include/lvm.h +++ b/lib/include/lvm.h @@ -4,7 +4,9 @@ /* Laika VM: This is an obfuscation technique where vital code can be executed in a stack-based VM, inlined into the function. The VM instruction-set is fairly - simple, see the OP_* for avaliable opcodes and their expected arguments. + simple, see the OP_* enum for avaliable opcodes and their expected arguments. + The VM is turing-complete, however the instruction-set has been curated to + fit this specific use case. */ #include @@ -44,8 +46,9 @@ enum { OP_EXIT, OP_LOADCONST, /* stk_indx[uint8_t] = const_indx[uint8_t] */ OP_PUSHLIT, /* stk_indx[uint8_t].i = uint8_t */ - OP_READ, /* stk_indx[uint8_t] = *(int8_t*)stk_indx[uint8_t] */ - OP_WRITE, /* *(uint8_t*)stk_indx[uint8_t] = stk_indx[uint8_t] */ + OP_READ, /* stk_indx[uint8_t].i = *(int8_t*)stk_indx[uint8_t] */ + OP_WRITE, /* *(uint8_t*)stk_indx[uint8_t].ptr = stk_indx[uint8_t].i */ + OP_INCPTR, /* stk_indx[uint8_t].ptr++ */ /* arithmetic */ OP_ADD, /* stk_indx[uint8_t] = stk_indx[uint8_t] + stk_indx[uint8_t] */ @@ -102,6 +105,11 @@ LAIKA_FORCEINLINE void laikaV_execute(struct sLaikaV_vm *vm) { *(uint8_t*)vm->stack[ptr].ptr = vm->stack[indx].i; break; } + case OP_INCPTR: { + uint8_t ptr = READBYTE; + vm->stack[ptr].ptr++; + break; + } case OP_ADD: BINOP(+); case OP_SUB: BINOP(-); case OP_MUL: BINOP(*); diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index d099502..e928d25 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,3 +1,4 @@ cmake_minimum_required(VERSION 3.10) add_subdirectory(genkey) +add_subdirectory(vmtest) diff --git a/tools/vmtest/CMakeLists.txt b/tools/vmtest/CMakeLists.txt new file mode 100644 index 0000000..f9cc981 --- /dev/null +++ b/tools/vmtest/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.10) + +project(vmTest VERSION 1.0) + +# Put CMake targets (ALL_BUILD/ZERO_CHECK) into a folder +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +# compile vmTest +file(GLOB_RECURSE VMTESTSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/**.c) +add_executable(vmTest ${VMTESTSOURCE}) +target_link_libraries(vmTest PUBLIC LaikaLib) + +# add the 'DEBUG' preprocessor definition if we're compiling as Debug +target_compile_definitions(vmTest PUBLIC "$<$:DEBUG>") diff --git a/tools/vmtest/src/main.c b/tools/vmtest/src/main.c new file mode 100644 index 0000000..11f3cab --- /dev/null +++ b/tools/vmtest/src/main.c @@ -0,0 +1,46 @@ +#include +#include + +#include "lvm.h" +#include "lbox.h" + +/* VM BOX Demo: + A secret message has been xor'd, this tiny bytecode chunk decodes 'data' into + 'unlockedData'. Obviously you wouldn't want a key this simple, more obfuscation + would be good (byte swapping, revolving xor key, etc.) +*/ + +int main(int argv, char **argc) { + uint8_t data[] = { + 0x96, 0xBB, 0xB2, 0xB2, 0xB1, 0xFE, 0x89, 0xB1, + 0xAC, 0xB2, 0xBA, 0xFF, 0xDE, 0x20, 0xEA, 0xBA, + 0xCE, 0xEA, 0xFC, 0x01, 0x9C, 0x23, 0x4D, 0xEE + }; + + struct sLaikaB_box box = { + {0}, /* reserved */ + { /* stack layout: + [0] - unlockedData (ptr) + [1] - data (ptr) + [2] - key (uint8_t) + [3] - working data (uint8_t) + */ + LAIKA_MAKE_VM_IAB(OP_LOADCONST, 0, 0), + LAIKA_MAKE_VM_IAB(OP_LOADCONST, 1, 1), + LAIKA_MAKE_VM_IAB(OP_PUSHLIT, 2, 0xDE), + /* LOOP_START */ + LAIKA_MAKE_VM_IAB(OP_READ, 3, 1), /* load data into working data */ + LAIKA_MAKE_VM_IABC(OP_XOR, 3, 3, 2), /* xor data with key */ + LAIKA_MAKE_VM_IAB(OP_WRITE, 0, 3), /* write data to unlockedData */ + LAIKA_MAKE_VM_IA(OP_INCPTR, 0), + LAIKA_MAKE_VM_IA(OP_INCPTR, 1), + LAIKA_MAKE_VM_IAB(OP_TESTJMP, 3, -17), /* exit loop on null terminator */ + OP_EXIT + } + }; + + laikaB_unlock(&box, data); + printf("%s\n", box.unlockedData); + laikaB_lock(&box); + return 0; +}