You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
118 lines
4.7 KiB
118 lines
4.7 KiB
#ifndef LAIKA_BOX_H |
|
#define LAIKA_BOX_H |
|
|
|
#include <inttypes.h> |
|
|
|
#include "laika.h" |
|
#include "lmem.h" |
|
#include "lvm.h" |
|
#include "lsodium.h" |
|
|
|
#define LAIKA_BOX_SCRATCH_SIZE 128 |
|
#define LAIKA_BOX_HEAPSIZE 256 |
|
|
|
enum { |
|
LAIKA_BOX_UNLOCKED_INDX, /* for output */ |
|
LAIKA_BOX_SCRATCH_INDX, /* for misc. scratch work the vm needs to do (hold keys, etc.) */ |
|
LAIKA_BOX_DATA_INDX /* for input */ |
|
}; |
|
|
|
/* Laika Box: |
|
Laika Boxes are obfuscated storage mediums where data is only in memory for a very short amount of time. |
|
Of course, this can be bypassed with a simple debugger and setting a breakpoint right after the data is 'unlocked', |
|
but the game of obfuscation isn't to prevent the data from being seen, it's to slow the reverse engineer down. |
|
|
|
2 main APIs are exposed here, laikaB_unlock() & laikaB_lock(). Both of which are inlined to make it more painful |
|
for the reverse engineer to quickly dump boxes from memory, forcing them to set breakpoints across the executable. |
|
Each box has its own VM, with it's own deobfuscation routine. This makes static analysis a painful route for string |
|
dumping. These apis, while can be used directly, are abstracted through macros with the pre-built boxes. |
|
|
|
Use LAIKA_BOX_SKID_START & LAIKA_BOX_SKID_END for quick and dirty usage. The data macros in `lboxconfig.h` are passed |
|
to these, which are generated by VMBoxGen (`tools/vmboxgen`). This will be extended in the future with more boxes and such, |
|
however for the time being only LAIKA_BOX_SKID_* is implemented. |
|
*/ |
|
|
|
struct sLaikaB_box { |
|
uint8_t unlockedData[LAIKA_BOX_HEAPSIZE]; |
|
uint8_t scratch[LAIKA_BOX_SCRATCH_SIZE]; |
|
uint8_t code[LAIKA_VM_CODESIZE]; |
|
}; |
|
|
|
/* ==============================================[[ Box Var API ]]=============================================== */ |
|
|
|
#define LAIKA_BOX_STARTVAR(type, ident, box, data) \ |
|
uint8_t __data##ident[LAIKA_VM_CODESIZE] = data; \ |
|
type ident; \ |
|
struct sLaikaB_box __box##ident = box; \ |
|
laikaB_unlock(&__box##ident, __data##ident); \ |
|
ident = (type)__box##ident.unlockedData; |
|
|
|
#define LAIKA_BOX_ENDVAR(ident) \ |
|
laikaB_lock(&__box##ident); |
|
|
|
#ifdef LAIKA_OBFUSCATE |
|
# define LAIKA_BOX_SKID_START(type, ident, strmacro) \ |
|
LAIKA_BOX_STARTVAR(type, ident, LAIKA_BOX_SKID(KEY_##strmacro), DATA_##strmacro) |
|
# define LAIKA_BOX_SKID_END(ident) \ |
|
LAIKA_BOX_ENDVAR(ident) |
|
#else /* disable obfuscations */ |
|
# define LAIKA_BOX_SKID_START(type, ident, strmacro) \ |
|
type ident = strmacro; |
|
# define LAIKA_BOX_SKID_END(ident) ((void)0) /* no-op */ |
|
#endif |
|
|
|
/* ==============================================[[ Laika Boxes ]]=============================================== */ |
|
|
|
/* BOX_SKID decodes null-terminated strings using a provided xor _key. aptly named lol */ |
|
#define LAIKA_BOX_SKID(_key) { \ |
|
.unlockedData = {0}, /* reserved */ \ |
|
.code = { /* stack layout: \ |
|
[0] - unlockedData (ptr) \ |
|
[1] - data (ptr) \ |
|
[2] - key (uint8_t) \ |
|
[3] - working data (uint8_t) \ |
|
*/ \ |
|
LAIKA_MAKE_VM_IAB(OP_LOADCONST, 0, LAIKA_BOX_UNLOCKED_INDX), \ |
|
LAIKA_MAKE_VM_IAB(OP_LOADCONST, 1, LAIKA_BOX_DATA_INDX), \ |
|
LAIKA_MAKE_VM_IAB(OP_PUSHLIT, 2, _key), \ |
|
/* 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 \ |
|
} \ |
|
} |
|
|
|
/* ==============================================[[ Raw Box API ]]=============================================== */ |
|
|
|
LAIKA_FORCEINLINE void* laikaB_unlock(struct sLaikaB_box *box, void *data) { |
|
struct sLaikaV_vm vm = { |
|
/* boxes have 2 reserved constants, [0] for the output, [1] for the input */ |
|
.constList = { |
|
[LAIKA_BOX_UNLOCKED_INDX] = LAIKA_MAKE_VM_PTR(box->unlockedData), |
|
[LAIKA_BOX_SCRATCH_INDX] = LAIKA_MAKE_VM_PTR(box->scratch), |
|
[LAIKA_BOX_DATA_INDX] = LAIKA_MAKE_VM_PTR(data), |
|
}, |
|
.code = {0}, |
|
.stack = {0}, |
|
.pc = 0 |
|
}; |
|
|
|
memcpy(vm.code, box->code, LAIKA_VM_CODESIZE); |
|
laikaV_execute(&vm); |
|
return (void*)box->unlockedData; |
|
} |
|
|
|
/* safely zeros the unlockedData using libsodium's api for clearing sensitive data from memory */ |
|
LAIKA_FORCEINLINE void laikaB_lock(struct sLaikaB_box *box) { |
|
sodium_memzero(box->unlockedData, LAIKA_BOX_HEAPSIZE); |
|
sodium_memzero(box->scratch, LAIKA_BOX_SCRATCH_SIZE); |
|
} |
|
|
|
/* include KEY_* & DATA_* macros for each obfuscated string */ |
|
#include "lboxconfig.h" |
|
|
|
#endif |