mirror of
https://github.com/CPunch/Laika.git
synced 2024-11-24 05:31:03 +00:00
123 lines
6.3 KiB
C
123 lines
6.3 KiB
C
#ifndef LAIKA_BOX_H
|
|
#define LAIKA_BOX_H
|
|
|
|
#include "laika.h"
|
|
#include "lmem.h"
|
|
#include "lsodium.h"
|
|
#include "lvm.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 |