mirror of
https://github.com/CPunch/Laika.git
synced 2024-11-13 01:10:05 +00:00
118 lines
4.7 KiB
C
118 lines
4.7 KiB
C
#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 |