mirror of
https://github.com/CPunch/Laika.git
synced 2026-02-08 01:00:05 +00:00
Refactoring: reorganized files
This commit is contained in:
43
lib/include/core/hashmap.h
Normal file
43
lib/include/core/hashmap.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// https://github.com/tidwall/hashmap.c
|
||||
// Copyright 2020 Joshua J Baker. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef HASHMAP_H
|
||||
#define HASHMAP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct hashmap;
|
||||
|
||||
struct hashmap *hashmap_new(size_t elsize, size_t cap, uint64_t seed0, uint64_t seed1,
|
||||
uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1),
|
||||
int (*compare)(const void *a, const void *b, void *udata),
|
||||
void (*elfree)(void *item), void *udata);
|
||||
struct hashmap *
|
||||
hashmap_new_with_allocator(void *(*malloc)(size_t), void *(*realloc)(void *, size_t),
|
||||
void (*free)(void *), size_t elsize, size_t cap, uint64_t seed0,
|
||||
uint64_t seed1,
|
||||
uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1),
|
||||
int (*compare)(const void *a, const void *b, void *udata),
|
||||
void (*elfree)(void *item), void *udata);
|
||||
void hashmap_free(struct hashmap *map);
|
||||
void hashmap_clear(struct hashmap *map, bool update_cap);
|
||||
size_t hashmap_count(struct hashmap *map);
|
||||
bool hashmap_oom(struct hashmap *map);
|
||||
void *hashmap_get(struct hashmap *map, const void *item);
|
||||
void *hashmap_set(struct hashmap *map, const void *item);
|
||||
void *hashmap_delete(struct hashmap *map, void *item);
|
||||
void *hashmap_probe(struct hashmap *map, uint64_t position);
|
||||
bool hashmap_scan(struct hashmap *map, bool (*iter)(const void *item, void *udata), void *udata);
|
||||
bool hashmap_iter(struct hashmap *map, size_t *i, void **item);
|
||||
|
||||
uint64_t hashmap_sip(const void *data, size_t len, uint64_t seed0, uint64_t seed1);
|
||||
uint64_t hashmap_murmur(const void *data, size_t len, uint64_t seed0, uint64_t seed1);
|
||||
|
||||
// DEPRECATED: use `hashmap_new_with_allocator`
|
||||
void hashmap_set_allocator(void *(*malloc)(size_t), void (*free)(void *));
|
||||
|
||||
#endif
|
||||
154
lib/include/core/ini.h
Normal file
154
lib/include/core/ini.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/* inih -- simple .INI file parser
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
Copyright (C) 2009-2020, Ben Hoyt
|
||||
|
||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||
home page for more info:
|
||||
|
||||
https://github.com/benhoyt/inih
|
||||
|
||||
*/
|
||||
|
||||
#ifndef INI_H
|
||||
#define INI_H
|
||||
|
||||
/* Make this header file easier to include in C++ code */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Nonzero if ini_handler callback should accept lineno parameter. */
|
||||
#ifndef INI_HANDLER_LINENO
|
||||
# define INI_HANDLER_LINENO 0
|
||||
#endif
|
||||
|
||||
/* Typedef for prototype of handler function. */
|
||||
#if INI_HANDLER_LINENO
|
||||
typedef int (*ini_handler)(void *user, const char *section, const char *name, const char *value,
|
||||
int lineno);
|
||||
#else
|
||||
typedef int (*ini_handler)(void *user, const char *section, const char *name, const char *value);
|
||||
#endif
|
||||
|
||||
/* Typedef for prototype of fgets-style reader function. */
|
||||
typedef char *(*ini_reader)(char *str, int num, void *stream);
|
||||
|
||||
/* Parse given INI-style file. May have [section]s, name=value pairs
|
||||
(whitespace stripped), and comments starting with ';' (semicolon). Section
|
||||
is "" if name=value pair parsed before any section heading. name:value
|
||||
pairs are also supported as a concession to Python's configparser.
|
||||
|
||||
For each name=value pair parsed, call handler function with given user
|
||||
pointer as well as section, name, and value (data only valid for duration
|
||||
of handler call). Handler should return nonzero on success, zero on error.
|
||||
|
||||
Returns 0 on success, line number of first error on parse error (doesn't
|
||||
stop on first error), -1 on file open error, or -2 on memory allocation
|
||||
error (only when INI_USE_STACK is zero).
|
||||
*/
|
||||
int ini_parse(const char *filename, ini_handler handler, void *user);
|
||||
|
||||
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
|
||||
close the file when it's finished -- the caller must do that. */
|
||||
int ini_parse_file(FILE *file, ini_handler handler, void *user);
|
||||
|
||||
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
|
||||
filename. Used for implementing custom or string-based I/O (see also
|
||||
ini_parse_string). */
|
||||
int ini_parse_stream(ini_reader reader, void *stream, ini_handler handler, void *user);
|
||||
|
||||
/* Same as ini_parse(), but takes a zero-terminated string with the INI data
|
||||
instead of a file. Useful for parsing INI data from a network socket or
|
||||
already in memory. */
|
||||
int ini_parse_string(const char *string, ini_handler handler, void *user);
|
||||
|
||||
/* Nonzero to allow multi-line value parsing, in the style of Python's
|
||||
configparser. If allowed, ini_parse() will call the handler with the same
|
||||
name for each subsequent line parsed. */
|
||||
#ifndef INI_ALLOW_MULTILINE
|
||||
# define INI_ALLOW_MULTILINE 1
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
|
||||
the file. See https://github.com/benhoyt/inih/issues/21 */
|
||||
#ifndef INI_ALLOW_BOM
|
||||
# define INI_ALLOW_BOM 1
|
||||
#endif
|
||||
|
||||
/* Chars that begin a start-of-line comment. Per Python configparser, allow
|
||||
both ; and # comments at the start of a line by default. */
|
||||
#ifndef INI_START_COMMENT_PREFIXES
|
||||
# define INI_START_COMMENT_PREFIXES ";#"
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow inline comments (with valid inline comment characters
|
||||
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
|
||||
Python 3.2+ configparser behaviour. */
|
||||
#ifndef INI_ALLOW_INLINE_COMMENTS
|
||||
# define INI_ALLOW_INLINE_COMMENTS 1
|
||||
#endif
|
||||
#ifndef INI_INLINE_COMMENT_PREFIXES
|
||||
# define INI_INLINE_COMMENT_PREFIXES ";"
|
||||
#endif
|
||||
|
||||
/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
|
||||
#ifndef INI_USE_STACK
|
||||
# define INI_USE_STACK 1
|
||||
#endif
|
||||
|
||||
/* Maximum line length for any line in INI file (stack or heap). Note that
|
||||
this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
|
||||
#ifndef INI_MAX_LINE
|
||||
# define INI_MAX_LINE 200
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
|
||||
fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
|
||||
zero. */
|
||||
#ifndef INI_ALLOW_REALLOC
|
||||
# define INI_ALLOW_REALLOC 0
|
||||
#endif
|
||||
|
||||
/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
|
||||
is zero. */
|
||||
#ifndef INI_INITIAL_ALLOC
|
||||
# define INI_INITIAL_ALLOC 200
|
||||
#endif
|
||||
|
||||
/* Stop parsing on first error (default is to keep parsing). */
|
||||
#ifndef INI_STOP_ON_FIRST_ERROR
|
||||
# define INI_STOP_ON_FIRST_ERROR 0
|
||||
#endif
|
||||
|
||||
/* Nonzero to call the handler at the start of each new section (with
|
||||
name and value NULL). Default is to only call the handler on
|
||||
each name=value pair. */
|
||||
#ifndef INI_CALL_HANDLER_ON_NEW_SECTION
|
||||
# define INI_CALL_HANDLER_ON_NEW_SECTION 0
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow a name without a value (no '=' or ':' on the line) and
|
||||
call the handler with value NULL in this case. Default is to treat
|
||||
no-value lines as an error. */
|
||||
#ifndef INI_ALLOW_NO_VALUE
|
||||
# define INI_ALLOW_NO_VALUE 0
|
||||
#endif
|
||||
|
||||
/* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory
|
||||
allocation functions (INI_USE_STACK must also be 0). These functions must
|
||||
have the same signatures as malloc/free/realloc and behave in a similar
|
||||
way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */
|
||||
#ifndef INI_CUSTOM_ALLOCATOR
|
||||
# define INI_CUSTOM_ALLOCATOR 0
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INI_H */
|
||||
123
lib/include/core/lbox.h
Normal file
123
lib/include/core/lbox.h
Normal file
@@ -0,0 +1,123 @@
|
||||
#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
|
||||
55
lib/include/core/lerror.h
Normal file
55
lib/include/core/lerror.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef LAIKA_ERROR_H
|
||||
#define LAIKA_ERROR_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;
|
||||
|
||||
/* if eLaika_errIndx is >= 0, we have a safe spot to jump too if an error is thrown */
|
||||
#define LAIKA_ISPROTECTED (eLaika_errIndx >= 0)
|
||||
|
||||
/* LAIKA_ERROR(printf args):
|
||||
if called after a LAIKA_TRY block will jump to the previous LAIKA_CATCH/LAIKA_TRYEND block,
|
||||
otherwise program is exit()'d. if DEBUG is defined printf is called with passed args, else
|
||||
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 */
|
||||
#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_WARN(...) printf("[WARN] : " __VA_ARGS__);
|
||||
#endif
|
||||
|
||||
extern int eLaika_errIndx;
|
||||
extern jmp_buf eLaika_errStack[LAIKA_MAXERRORS];
|
||||
|
||||
#endif
|
||||
60
lib/include/core/lmem.h
Normal file
60
lib/include/core/lmem.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef LAIKA_MEM_H
|
||||
#define LAIKA_MEM_H
|
||||
|
||||
#include "laika.h"
|
||||
|
||||
#define GROW_FACTOR 2
|
||||
|
||||
/* 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);
|
||||
#else
|
||||
# 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)
|
||||
|
||||
/* ========================================[[ Vectors ]]======================================== */
|
||||
|
||||
#define laikaM_countVector(name) name##_COUNT
|
||||
#define laikaM_capVector(name) name##_CAP
|
||||
|
||||
#define laikaM_newVector(type, name) \
|
||||
type *name; \
|
||||
int name##_COUNT; \
|
||||
int name##_CAP;
|
||||
#define laikaM_initVector(name, startCap) \
|
||||
name = NULL; \
|
||||
name##_COUNT = 0; \
|
||||
name##_CAP = startCap;
|
||||
|
||||
#define laikaM_growVector(type, name, needed) \
|
||||
if (name##_COUNT + needed >= name##_CAP || name == NULL) { \
|
||||
name##_CAP = (name##_CAP + needed) * GROW_FACTOR; \
|
||||
name = (type *)laikaM_realloc(name, sizeof(type) * name##_CAP); \
|
||||
}
|
||||
|
||||
/* moves vector elements above indx down by numElem, removing numElem elements at indx */
|
||||
#define laikaM_rmvVector(name, indx, numElem) \
|
||||
do { \
|
||||
int _i, _sz = ((name##_COUNT - indx) - numElem); \
|
||||
for (_i = 0; _i < _sz; _i++) \
|
||||
name[indx + _i] = name[indx + numElem + _i]; \
|
||||
name##_COUNT -= numElem; \
|
||||
} while (0);
|
||||
|
||||
/* moves vector elements above indx up by numElem, inserting numElem elements at indx */
|
||||
#define laikaM_insertVector(name, indx, numElem) \
|
||||
do { \
|
||||
int _i; \
|
||||
for (_i = name##_COUNT; _i > indx; _i--) \
|
||||
name[_i] = name[_i - 1]; \
|
||||
name##_COUNT += numElem; \
|
||||
} while (0);
|
||||
|
||||
void *laikaM_realloc(void *buf, size_t sz);
|
||||
|
||||
#endif
|
||||
15
lib/include/core/lsodium.h
Normal file
15
lib/include/core/lsodium.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef LAIKA_RSA_H
|
||||
#define LAIKA_RSA_H
|
||||
|
||||
#include "sodium.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define LAIKAENC_SIZE(sz) (sz + crypto_box_SEALBYTES)
|
||||
|
||||
bool laikaK_loadKeys(uint8_t *outPub, uint8_t *outPriv, const char *inPub, const char *inPriv);
|
||||
bool laikaK_genKeys(uint8_t *outPub, uint8_t *outPriv);
|
||||
|
||||
bool laikaK_checkAuth(uint8_t *pubKey, uint8_t **authKeys, int keys);
|
||||
|
||||
#endif
|
||||
39
lib/include/core/ltask.h
Normal file
39
lib/include/core/ltask.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef LAIKA_TASK_H
|
||||
#define LAIKA_TASK_H
|
||||
|
||||
#include "laika.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
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;
|
||||
long scheduled;
|
||||
int delta;
|
||||
};
|
||||
|
||||
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);
|
||||
void laikaT_delTask(struct sLaika_taskService *service, struct sLaika_task *task);
|
||||
|
||||
void laikaT_pollTasks(struct sLaika_taskService *service);
|
||||
|
||||
/* will return the delta time in ms till the next event. -1 for no tasks scheduled */
|
||||
int laikaT_timeTillTask(struct sLaika_taskService *service);
|
||||
|
||||
long laikaT_getTime(void);
|
||||
|
||||
#endif
|
||||
178
lib/include/core/lvm.h
Normal file
178
lib/include/core/lvm.h
Normal file
@@ -0,0 +1,178 @@
|
||||
#ifndef LAIKA_VM_H
|
||||
#define LAIKA_VM_H
|
||||
|
||||
/* Laika VM:
|
||||
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
|
||||
fit this specific use case.
|
||||
*/
|
||||
|
||||
#include "core/lerror.h"
|
||||
#include "laika.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define LAIKA_VM_STACKSIZE 64
|
||||
#define LAIKA_VM_CONSTSIZE 32
|
||||
|
||||
struct sLaikaV_vm_val
|
||||
{
|
||||
union
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t *ptr;
|
||||
};
|
||||
};
|
||||
|
||||
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 } \
|
||||
}
|
||||
|
||||
/* constants */
|
||||
#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_IABC(opcode, a, b, c) opcode, a, b, c
|
||||
|
||||
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-- */
|
||||
|
||||
/* arithmetic */
|
||||
OP_ADD, /* stk_indx[uint8_t] = stk_indx[uint8_t] + stk_indx[uint8_t] */
|
||||
OP_SUB, /* stk_indx[uint8_t] = stk_indx[uint8_t] - stk_indx[uint8_t] */
|
||||
OP_MUL, /* stk_indx[uint8_t] = stk_indx[uint8_t] * stk_indx[uint8_t] */
|
||||
OP_DIV, /* stk_indx[uint8_t] = stk_indx[uint8_t] / stk_indx[uint8_t] */
|
||||
OP_AND, /* stk_indx[uint8_t] = stk_indx[uint8_t] & stk_indx[uint8_t] */
|
||||
OP_OR, /* stk_indx[uint8_t] = stk_indx[uint8_t] | stk_indx[uint8_t] */
|
||||
OP_XOR, /* stk_indx[uint8_t] = stk_indx[uint8_t] ^ stk_indx[uint8_t] */
|
||||
|
||||
/* control-flow */
|
||||
OP_TESTJMP, /* if stk_indx[uint8_t] != 0, pc += [int8_t] */
|
||||
|
||||
/* misc. */
|
||||
#ifdef DEBUG
|
||||
OP_DEBUG
|
||||
#endif
|
||||
};
|
||||
|
||||
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; \
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* if stack indx is true, jump by jmp (signed 8-bit int) */
|
||||
if (vm->stack[indx].i)
|
||||
vm->pc += jmp;
|
||||
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
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);
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
LAIKA_ERROR("laikaV_execute: unknown opcode [0x%02x]! pc: %d\n", vm->code[vm->pc],
|
||||
vm->pc);
|
||||
}
|
||||
}
|
||||
|
||||
#undef READBYTE
|
||||
#undef BINOP
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user