Laika/lib/include/core/lbox.h

123 lines
6.3 KiB
C

#ifndef LAIKA_BOX_H
#define LAIKA_BOX_H
#include "core/lmem.h"
#include "core/lsodium.h"
#include "core/lvm.h"
#include "laika.h"
#include <inttypes.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