From 5a8cb683ede7e8b80f6c5c16ae2c825d256fb705 Mon Sep 17 00:00:00 2001 From: hugsy Date: Wed, 8 Nov 2023 11:21:17 -0800 Subject: [PATCH 1/2] deleted lua backdoor --- .github/workflows/analyze.yml | 76 ++-- .github/workflows/build.yml | 2 +- .github/workflows/docs.yml | 2 +- CMakeLists.txt | 12 - Docs/examples/common/utils.md | 34 -- Modules/Backdoor/CMakeLists.txt | 33 -- Modules/Backdoor/Include/Backdoor.hpp | 156 ------- Modules/Backdoor/Source/Backdoor.cpp | 584 -------------------------- Modules/CMakeLists.txt | 109 +++-- Modules/Common/Include/Context.hpp | 4 - Modules/Process/CMakeLists.txt | 2 + README.md | 18 +- Tools/Win32/Backdoor/Backdoor.cpp | 35 -- Tools/Win32/CMakeLists.txt | 3 - pwn++/Source/Win32/dllmain.cpp | 16 - 15 files changed, 108 insertions(+), 978 deletions(-) delete mode 100644 Modules/Backdoor/CMakeLists.txt delete mode 100644 Modules/Backdoor/Include/Backdoor.hpp delete mode 100644 Modules/Backdoor/Source/Backdoor.cpp delete mode 100644 Tools/Win32/Backdoor/Backdoor.cpp diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml index 0e091238..b2ab1829 100644 --- a/.github/workflows/analyze.yml +++ b/.github/workflows/analyze.yml @@ -1,38 +1,38 @@ -name: "Code Analysis" - -on: - push: - branches: [main, dev] - - pull_request: - branches: [main, dev] - - workflow_dispatch: - -jobs: - codeql: - runs-on: windows-latest - - permissions: - security-events: write - actions: read - contents: read - - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: true - - - name: Initialize - uses: github/codeql-action/init@v2 - - - name: Build - run: | - Import-Module .\.github\Invoke-VisualStudio.ps1 - Invoke-VisualStudio2022x64 - cmake -S . -B ./build -A x64 -DPWN_BUILD_TOOLKIT=OFF -DPWN_INCLUDE_DISASSEMBLER=OFF -DPWN_ENABLE_LUA_BACKDOOR=OFF -DPWN_BUILD_DOCS=OFF -DPWN_BUILD_TESTING=OFF -DPWN_LOG_USE_COLOR=OFF - cmake --build ./build --verbose --config Debug - - - name: Analyze - uses: github/codeql-action/analyze@v2 +name: "Code Analysis" + +on: + push: + branches: [main, dev] + + pull_request: + branches: [main, dev] + + workflow_dispatch: + +jobs: + codeql: + runs-on: windows-latest + + permissions: + security-events: write + actions: read + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: true + + - name: Initialize + uses: github/codeql-action/init@v2 + + - name: Build + run: | + Import-Module .\.github\Invoke-VisualStudio.ps1 + Invoke-VisualStudio2022x64 + cmake -S . -B ./build -A x64 -DPWN_BUILD_TOOLKIT=OFF -DPWN_INCLUDE_DISASSEMBLER=OFF -DPWN_BUILD_DOCS=OFF -DPWN_BUILD_TESTING=OFF -DPWN_LOG_USE_COLOR=OFF + cmake --build ./build --verbose --config Debug + + - name: Analyze + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 64d6adc5..a6c04a2a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: build: name: "${{ matrix.variants.os }}/${{ matrix.variants.arch }}/${{ matrix.variants.config }}" env: - CMAKE_FLAGS: '-DPWN_BUILD_DOCS=OFF -DPWN_DISASSEMBLE_X86=ON -DPWN_DISASSEMBLE_ARM64=ON -DPWN_BUILD_TOOLKIT=ON -DPWN_BUILD_TESTING=ON -DPWN_ENABLE_LUA_BACKDOOR=OFF' + CMAKE_FLAGS: '-DPWN_BUILD_DOCS=OFF -DPWN_DISASSEMBLE_X86=ON -DPWN_DISASSEMBLE_ARM64=ON -DPWN_BUILD_TOOLKIT=ON -DPWN_BUILD_TESTING=ON' NB_CPU: 1 strategy: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 75a80e68..937fb931 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -18,7 +18,7 @@ concurrency: cancel-in-progress: true env: - CMAKE_FLAGS: "-DPWN_BUILD_DOCS=ON -DPWN_ENABLE_LUA_BACKDOOR=OFF -DPWN_INCLUDE_DISASSEMBLER=OFF -DPWN_BUILD_TOOLKIT=OFF -DPWN_BUILD_TESTING=OFF" + CMAKE_FLAGS: "-DPWN_BUILD_DOCS=ON -DPWN_INCLUDE_DISASSEMBLER=OFF -DPWN_BUILD_TOOLKIT=OFF -DPWN_BUILD_TESTING=OFF" jobs: deploy: diff --git a/CMakeLists.txt b/CMakeLists.txt index 4966f7d7..29ff93b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,6 @@ option(PWN_DISASSEMBLE_X86 "Compile with Zydis disassembler support (X86/64 only option(PWN_DISASSEMBLE_ARM64 "Compile with BinaryNinja disassembler support (ARM64)" ON) option(PWN_BUILD_SHARED_LIBS "Build as a shared library" OFF) -option(PWN_ENABLE_LUA_BACKDOOR "Enable Lua scripting for backdoor" OFF) # Deprecated, will be deleted soon if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) # pwn++ is NOT a top dir (i.e. build as dependency) @@ -40,7 +39,6 @@ endif() set(PWNLIB_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}) -# set(PWNLIB_DEPS_DIR ${PWNLIB_ROOT_DIR}/Deps) set(PWN_INCLUDE_DISASSEMBLER FALSE) set(CMAKE_INSTALL_LIBDIR Library) set(CMAKE_INSTALL_BINDIR Binary) @@ -113,16 +111,6 @@ if(WIN32) install(DIRECTORY $ DESTINATION ${PROJECT_NAME}/Dependencies/Include) endif(WIN32) -# if(PWN_ENABLE_LUA_BACKDOOR) -# option(BUILD_SHARED_LIBS "" OFF) -# add_subdirectory(${PWNLIB_DEPS_DIR}/lua) -# add_library(PWN::Deps::Lua ALIAS lua) - -# install(TARGETS lua DESTINATION ${PROJECT_NAME}/Dependencies/Library) -# install(TARGETS lua-header DESTINATION ${PROJECT_NAME}/Dependencies/Include) - -# list(APPEND PWN_MODULES Backdoor) -# endif() if(PWN_INCLUDE_DISASSEMBLER) list(APPEND PWN_MODULES Assembly) endif(PWN_INCLUDE_DISASSEMBLER) diff --git a/Docs/examples/common/utils.md b/Docs/examples/common/utils.md index acd67dbd..9f794e4c 100644 --- a/Docs/examples/common/utils.md +++ b/Docs/examples/common/utils.md @@ -127,37 +127,3 @@ void wmain() } ``` - -### Lua VM backdoor - -Namespace: `pwn::backdoor` - -The lib embeds a Lua VM (if compiled with the flag `PWN_ENABLE_LUA_BACKDOOR`) which allows to script your way into a remote process where the pwn++.dll is injected. On Windows it will use a Named Pipe (see tools/win32/Backdoor for a standalone example) - -```powershell -> .\Backdoor.exe -[DEBUG] {c:\temp\backdoor.cpp:645:wmain()} Starting as PID=15004 -[...] -[DEBUG] {Z:\pwn++\src\pwn++\win32\backdoor.cpp:548:start()} Listening for connection on '\\.\pipe\WindowsBackupService_202004L_1932' -[DEBUG] {Z:\pwn++\src\pwn++\win32\backdoor.cpp:253:WaitNextConnectionAsync()} Waiting for connection -``` - -Now you can use any client to connect and interact with the Named Pipe - -```lua -> .\NamedPipe.exe '\\.\pipe\WindowsBackupService_202004L_1932' ->>> return pwn.version() ->> Sent 20 bytes -<< Received 6 bytes ---- -0.1.3 ---- ->>> return pwn.process.pid() ->> Sent 24 bytes -<< Received 6 bytes ---- -15004 ---- ->>> -``` - diff --git a/Modules/Backdoor/CMakeLists.txt b/Modules/Backdoor/CMakeLists.txt deleted file mode 100644 index 378044fb..00000000 --- a/Modules/Backdoor/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -if(NOT PWN_ENABLE_LUA_BACKDOOR) - return() -endif() - -project(Backdoor CXX) -message(STATUS "Configuring module '${PROJECT_NAME}'") -set(DEPS - PWN::Common - PWN::Deps::Lua -) - -set(PROJECT_DIR ${PWNLIB_MODULE_DIR}/${PROJECT_NAME}) -set(INTERFACE_DIR ${PROJECT_DIR}/Include) -set(SOURCE_DIR ${PROJECT_DIR}/Source) -set(HEADER_DIR ${SOURCE_DIR}/Include) -set(TEST_DIR ${PROJECT_DIR}/Tests) - -list(APPEND SOURCE_FILES - - ${SOURCE_DIR}/Backdoor.cpp -) - -add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES}) -add_dependencies(${PROJECT_NAME} ${DEPS}) -add_library(PWN::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) -target_include_directories(${PROJECT_NAME} PUBLIC ${INTERFACE_DIR} PRIVATE ${HEADER_DIR}) -target_link_libraries(${PROJECT_NAME} PUBLIC ${DEPS}) - -install(DIRECTORY ${INTERFACE_DIR} DESTINATION ${CMAKE_PROJECT_NAME}) - -if(PWN_BUILD_TESTING) - # add_subdirectory(Tests) -endif(PWN_BUILD_TESTING) diff --git a/Modules/Backdoor/Include/Backdoor.hpp b/Modules/Backdoor/Include/Backdoor.hpp deleted file mode 100644 index 359bb5c2..00000000 --- a/Modules/Backdoor/Include/Backdoor.hpp +++ /dev/null @@ -1,156 +0,0 @@ -#pragma once - -#ifdef PWN_INCLUDE_BACKDOOR -#include "common.hpp" - -/// -/// Definition of the `pwn` module in the Lua VM -/// -EXTERN_C_START -#include -#include -#include -EXTERN_C_END - - -#ifdef PWN_BUILD_FOR_WINDOWS -#define PWN_BACKDOOR_PIPENAME \ - L"\\\\.\\pipe\\WindowsBackupService" \ - L"_" STR(__cplusplus) L"_" STR(_MSC_VER) -#define PWN_BACKDOOR_MAX_MESSAGE_SIZE 2048 -#else -#error "todo: backdoor for linux" -#endif - -/// -/// @brief Interface for the backdoor -/// This interface is cross-plaform and should not expose/use any OS-specifics. -/// -namespace pwn::Backdoor -{ - -enum class ThreadState -{ - Uninitialized, - // Wait for a new command - ReadyToRead, - // Pending read I/O - ReadInProgress, - // Command read, process and reply - ReadFinished, - // Received the TerminationEvent or a error occured, wait for last IO to finish - Stopping, - // Close & clean up - Stopped, -}; - -typedef class _ThreadConfig -{ -public: - _ThreadConfig() : - Tid(0), - Lock(), - hThread(INVALID_HANDLE_VALUE), - hPipe(INVALID_HANDLE_VALUE), - hStateChangeEvent(INVALID_HANDLE_VALUE), - request(nullptr), - response(nullptr), - pLuaVm(nullptr), - command_number(0) - { - this->hStateChangeEvent = ::CreateEvent(nullptr, false, false, nullptr); - this->State = ThreadState::Uninitialized; - - ::RtlSecureZeroMemory(&this->oReadWrite, sizeof(OVERLAPPED)); - } - - bool - SetState(ThreadState NewState) - { - std::lock_guard scoped_lock(this->Lock); - this->State = NewState; - return ::SetEvent(this->hStateChangeEvent); - } - - friend std::ostream& - operator<<(std::ostream& os, const _ThreadConfig& obj) - { - os << "Client(Tid=" << obj.Tid << ")"; - return os; - } - - u32 Tid; - std::mutex Lock; - ThreadState State; - std::unique_ptr request; - usize request_size; - std::unique_ptr response; - usize response_size; - usize command_number; - lua_State* pLuaVm; - - HANDLE hThread; - HANDLE hPipe; - HANDLE hStateChangeEvent; - OVERLAPPED oReadWrite; - -} ThreadConfig; - -/// -/// @brief Start the backdoor thread -/// -/// @return the thread id of the listening thread on success; an Error() otherwise -/// -Result PWNAPI -start(); - - -/// -/// @brief Stop the backdoor thread, in a thread safe way -/// -/// @return Ok() on success, Error() on error -/// -Result PWNAPI -stop(); - - -namespace pwn::Lua -{ -/// -/// @brief Wrapper for `pwn::version` -/// Takes no argument -/// Returns the version a string -/// -/// @param l -/// @return int -/// -int -pwn_version(lua_State* l); - -/// -/// @brief Wrapper for `pwn::utils::hexdump` -/// Takes: -/// - Bytearray to hexdump -/// Returns the hexdump as a string -/// -/// @param l -/// @return int -/// -int -pwn_utils_hexdump(lua_State* l); - -/// -/// @brief Wrapper for `pwn::process::pid` -/// Takes no argument -/// Returns the pid of the backdoored process as a string -/// -/// @param l -/// @return int -/// -int -pwn_process_pid(lua_State* l); - -} // namespace lua -}; // namespace pwn::backdoor - -#endif // PWN_INCLUDE_BACKDOOR diff --git a/Modules/Backdoor/Source/Backdoor.cpp b/Modules/Backdoor/Source/Backdoor.cpp deleted file mode 100644 index c819da36..00000000 --- a/Modules/Backdoor/Source/Backdoor.cpp +++ /dev/null @@ -1,584 +0,0 @@ -#ifdef PWN_INCLUDE_BACKDOOR - -#include "backdoor.hpp" - -#include -#include -#include - -#include "handle.hpp" -#include "pwn.hpp" -#include "utils.hpp" - -using namespace pwn::utils; - - -namespace pwn::Backdoor -{ - -/// -/// @brief Where the LUA VM lives -/// -namespace pwn::Lua -{ - -// -// Function and module registration arrays -// -static const luaL_Reg pwn_module_functions[] = {{"version", pwn_version}, {nullptr, nullptr}}; -static const luaL_Reg pwn_utils_module_functions[] = {{"hexdump", pwn_utils_hexdump}, {nullptr, nullptr}}; -static const luaL_Reg pwn_process_module_functions[] = {{"pid", pwn_process_pid}, {nullptr, nullptr}}; - - -/// -/// @brief LUA VM initialization function -/// -lua_State* -init() -{ - lua_State* LuaVm = nullptr; - - dbg(L"[backdoor] Initializing Lua VM"); - - // - // Initialize the VM - // - LuaVm = luaL_newstate(); - - // - // Load some basic modules - // -#if LUA_VERSION_NUM >= 501 - luaL_openlibs(LuaVm); -#else - luaopen_base(LuaVm); - luaopen_table(LuaVm); - luaopen_io(LuaVm); - luaopen_string(LuaVm); - luaopen_math(LuaVm); -#endif - - // - // Create the `pwn` module - // - luaL_newlib(LuaVm, pwn_module_functions); - -#define REGISTER_LUA_SUBMODULE(name) \ - { \ - luaL_newlib(LuaVm, pwn_##name##_module_functions); \ - lua_setfield(LuaVm, -2, STR(name)); \ - } - - REGISTER_LUA_SUBMODULE(utils); - REGISTER_LUA_SUBMODULE(process); - -#undef REGISTER_LUA_SUBMODULE - - // - // Expose the `pwn` root module - // - lua_setglobal(LuaVm, "pwn"); - - return LuaVm; -} // namespace lua - - -/// -/// @brief LUA VM deallocation function -/// -void -close(lua_State* LuaVm) -{ - if ( LuaVm ) - { - dbg(L"[backdoor] Deinitializing Lua VM"); - lua_close(LuaVm); - LuaVm = nullptr; - } - else - { - warn(L"[backdoor] Lua VM is not initialized"); - } -} - - -/// -/// @brief -/// -/// @param cfg -/// @param os -/// @param index -/// -void -return_values(ThreadConfig* cfg, std::stringstream& os, const usize index) -{ - lua_State* LuaVm = cfg->pLuaVm; - - if ( index == 0 ) - return; - - switch ( lua_type(LuaVm, -1) ) - { - case LUA_TNIL: - os << "(nil)"; - break; - - case LUA_TBOOLEAN: - os << (lua_toboolean(LuaVm, -1) == 1) ? "true" : "false"; - break; - - case LUA_TSTRING: - os << lua_tostring(LuaVm, -1); - break; - - case LUA_TNUMBER: - // TODO also support long, double, etc. - os << std::to_string(lua_tointeger(LuaVm, -1)); - break; - - default: - os << ""; - } - lua_pop(LuaVm, 1); - - os << "\n"; - - return return_values(cfg, os, index - 1); -} - - -/// -/// @brief -/// -/// @param cfg -/// @return Result -/// -auto -execute(ThreadConfig* cfg) -> Result -{ - std::stringstream os; - - lua_State* LuaVm = cfg->pLuaVm; - if ( !LuaVm ) - { - err(L"The VM is not ready"); - return Err(ErrorCode::NotInitialized); - } - - const usize initial_stack_size = lua_gettop(LuaVm); - const std::string name = std::format("backdoor-command-{}", cfg->command_number++); - const std::string request = std::string((const char*)cfg->request.get(), cfg->request_size); - - if ( request == "exit" ) - { - return Err(ErrorCode::TerminationError); - } - - auto LuaRc = luaL_loadbuffer(LuaVm, request.c_str(), request.size(), name.c_str()); - if ( LuaRc ) - { - std::string response = lua_tostring(LuaVm, -1); - lua_pop(LuaVm, 1); - return Ok(response); - } - - lua_pcall(LuaVm, 0, LUA_MULTRET, 0); - const usize new_stack_size = lua_gettop(LuaVm); - const usize nb_retvalues = (new_stack_size - initial_stack_size); - return_values(cfg, os, nb_retvalues); - - return Ok(os.str()); -} - - -// -// Module `pwn` function definitions below -// - -int -pwn_version(lua_State* l) -{ - std::string version = pwn::utils::to_string(pwn::Version); - lua_pushstring(l, version.c_str()); - return 1; -} - - -int -pwn_utils_hexdump(lua_State* l) -{ - double d = luaL_checknumber(l, 1); - lua_pushnil(l); - return 1; -} - - -int -pwn_process_pid(lua_State* l) -{ - lua_pushinteger(l, ::GetCurrentProcessId()); - return 1; -} -} // namespace pwn::Lua - -namespace -{ - -auto -OpenPipe() -> Result -{ - auto hPipe = ::CreateNamedPipeW( - PWN_BACKDOOR_PIPENAME, - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - PWN_BACKDOOR_MAX_MESSAGE_SIZE, - PWN_BACKDOOR_MAX_MESSAGE_SIZE, - 0, - nullptr); - - if ( INVALID_HANDLE_VALUE == hPipe ) - { - pwn::log::perror(L"CreateNamedPipeW()"); - return Err(ErrorCode::RuntimeError); - } - - return Ok(hPipe); -} - - -auto -WaitNextConnectionAsync(const HANDLE hPipe, LPOVERLAPPED oConnect) -> Result -{ - bool bIsPending = false; - - dbg(L"Waiting for connection"); - const bool bIsConnected = ::ConnectNamedPipe(hPipe, oConnect); - - if ( bIsConnected ) - { - pwn::log::perror(L"ConnectNamedPipe()"); - return Err(ErrorCode::ConnectionError); - } - - switch ( ::GetLastError() ) - { - case ERROR_IO_PENDING: - bIsPending = true; - break; - case ERROR_PIPE_CONNECTED: - ::SetEvent(oConnect->hEvent); - bIsPending = false; - break; - default: - pwn::log::perror(L"ConnectNamedPipe()"); - return Err(ErrorCode::ConnectionError); - } - - return Ok(bIsPending); -} - - -/// -/// @brief Thread routine for each new client to the pipe -/// -/// @param lpThreadParams -/// @return DWORD -/// -DWORD WINAPI -HandleClientThread(const LPVOID lpThreadParams) -{ - if ( lpThreadParams == nullptr ) - { - // expected the pipe handle as parameter - return ERROR_INVALID_PARAMETER; - } - - const auto cfg = reinterpret_cast(lpThreadParams); - const auto hPipe = pwn::UniqueHandle {cfg->hPipe}; - cfg->pLuaVm = lua::init(); - cfg->SetState(ThreadState::ReadyToRead); - - while ( cfg->State != ThreadState::Stopped ) - { - DWORD size = 0; - - const DWORD Status = ::WaitForSingleObject(cfg->hStateChangeEvent, 0); - switch ( Status ) - { - // We expect a success, any other case should stop the execution - case WAIT_OBJECT_0: - break; - - default: - cfg->SetState(ThreadState::Stopping); - pwn::log::perror(L"WaitForMultipleObjects"); - break; - } - - if ( cfg->State == ThreadState::Stopping ) - { - dbg(L"Termination event received"); - ::FlushFileBuffers(hPipe.get()); - // TODO: also wait for last io - cfg->SetState(ThreadState::Stopped); - continue; - } - - // - // Wait for a command - // - if ( cfg->State == ThreadState::ReadyToRead ) - { - cfg->request = std::make_unique(PWN_BACKDOOR_MAX_MESSAGE_SIZE); - ::RtlSecureZeroMemory(cfg->request.get(), PWN_BACKDOOR_MAX_MESSAGE_SIZE); - - DWORD dwNumberOfByteRead; - auto bRes = ::ReadFile( - hPipe.get(), - cfg->request.get(), - PWN_BACKDOOR_MAX_MESSAGE_SIZE, - &dwNumberOfByteRead, - &cfg->oReadWrite); - - if ( bRes ) - { - cfg->request_size = dwNumberOfByteRead; - cfg->SetState(ThreadState::ReadFinished); - } - else - { - switch ( ::GetLastError() ) - { - case ERROR_IO_PENDING: - cfg->SetState(ThreadState::ReadInProgress); - break; - default: - pwn::log::perror(L"ReadFile()"); - cfg->SetState(ThreadState::Stopping); - } - } - continue; - } - - // - // Finish overlapped read IO - // - if ( cfg->State == ThreadState::ReadInProgress ) - { - if ( ::GetOverlappedResult(hPipe.get(), &cfg->oReadWrite, &size, true) ) - { - cfg->request_size = size; - cfg->SetState(ThreadState::ReadFinished); - } - else - { - switch ( ::GetLastError() ) - { - case ERROR_IO_PENDING: - cfg->SetState(ThreadState::ReadInProgress); - break; - default: - pwn::log::perror(L"GetOverlappedResult()"); - cfg->SetState(ThreadState::Stopping); - } - } - continue; - } - - // - // Input read is done, process the command and send back the result - // - if ( cfg->State == ThreadState::ReadFinished ) - { - DWORD dwNumberOfByteRead = 0; - - auto res = lua::execute(cfg); - if ( Failed(res) ) - { - if ( Error(res).code == ErrorCode::TerminationError ) - warn(L"Termination requested by user"); - cfg->SetState(ThreadState::Stopping); - continue; - } - - std::string response = Value(res); - cfg->response_size = response.size(); - cfg->response = std::make_unique(cfg->response_size); - - ::RtlCopyMemory(cfg->response.get(), response.c_str(), cfg->response_size); - - // TODO: for now, it's ok to make write blocking - const bool bRes = - ::WriteFile(hPipe.get(), cfg->response.get(), cfg->response_size, &dwNumberOfByteRead, nullptr); - - if ( bRes == false ) - { - pwn::log::perror(L"WriteFile()"); - cfg->SetState(ThreadState::Stopping); - } - else - { - cfg->SetState(ThreadState::ReadyToRead); - } - - ::RtlSecureZeroMemory(cfg->response.get(), cfg->response_size); - continue; - } - } - - dbg(L"Disconnecting session TID={}", cfg->Tid); - ::DisconnectNamedPipe(hPipe.get()); - - lua::close(cfg->pLuaVm); - return NO_ERROR; -} - - -auto -StartClientSession(const HANDLE hPipe) -> Result> -{ - dbg(L"New connection, initalizing new client"); - DWORD dwThreadId = 0; - - auto client = std::make_shared(); - client->hPipe = hPipe; - client->hThread = ::CreateThread(nullptr, 0, HandleClientThread, client.get(), 0, &dwThreadId); - - if ( client->hThread == INVALID_HANDLE_VALUE || (dwThreadId == 0u) ) - { - pwn::log::perror(L"CreateThread()"); - return Err(ErrorCode::RuntimeError); - } - - client->Tid = dwThreadId; - - dbg(L"Started client thread TID={}", client->Tid); - return Ok(client); -} - - -auto -AllowNextClient() -> Result -{ - HANDLE hPipe = INVALID_HANDLE_VALUE; - - // Prepare the pip - { - const auto res = OpenPipe(); - if ( Failed(res) ) - { - return Error(res); - } - - hPipe = Value(res); - } - - - // Wait for the next client on the pipe - { - OVERLAPPED oConnect; - ::RtlSecureZeroMemory(&oConnect, sizeof(OVERLAPPED)); - oConnect.hEvent = ::CreateEvent(nullptr, false, false, nullptr); - - const auto res = WaitNextConnectionAsync(hPipe, &oConnect); - if ( Failed(res) ) - { - return res; - } - - const bool bIsPending = Value(res); - - while ( true ) - { - const DWORD dwWait = ::WaitForSingleObjectEx(oConnect.hEvent, INFINITE, true); - switch ( dwWait ) - { - case WAIT_IO_COMPLETION: - // Completion is pending, wait for it to finish - break; - case 0: - if ( bIsPending ) - { - // collect the result of the connect operation. - - DWORD dwTransferedBytes; - ::GetOverlappedResult(hPipe, &oConnect, &dwTransferedBytes, false); - } - - // - // the connection is ready, start the handling thread - // - { - auto const res = StartClientSession(hPipe); - if ( Failed(res) ) - { - err(L"Failed to initialize the new client"); - return Error(res); - } - - auto client = Value(res); - - // - // Insert the client configuration in the global context - // - { - std::lock_guard lock(pwn::Context.m_ConfigMutex); - Context.m_backdoor_clients.push_back(client); - } - - return Ok(true); - } - - default: - return Ok(false); - } - - break; - } - } - - return Err(ErrorCode::GenericError); -} - - -} // namespace - -auto -start() -> Result -{ - dbg(L"Listening for connection on '{}'", PWN_BACKDOOR_PIPENAME); - - Context.m_backdoor_thread = std::jthread::jthread( - [] - { - while ( true ) - { - AllowNextClient(); - } - }); - - return Ok(true); -} - - -auto -stop() -> Result -{ - std::vector handles; - std::lock_guard lock(pwn::Context.m_ConfigMutex); - const usize sz = Context.m_backdoor_clients.size(); - - for ( auto const& client : Context.m_backdoor_clients ) - { - dbg(L"Stopping client {}", client->Tid); - client->SetState(ThreadState::Stopped); - handles.push_back(client->hThread); - } - - return Ok(::WaitForMultipleObjects(sz, handles.data(), true, INFINITE) == WAIT_OBJECT_0); -} - -} // namespace pwn::Backdoor - -#endif // PWN_INCLUDE_BACKDOOR diff --git a/Modules/CMakeLists.txt b/Modules/CMakeLists.txt index facf1257..74e191d7 100644 --- a/Modules/CMakeLists.txt +++ b/Modules/CMakeLists.txt @@ -1,55 +1,54 @@ -set(PWNLIB_MODULE_DIR ${PWNLIB_ROOT_DIR}/Modules CACHE INTERNAL "PWNLIB_MODULE_DIR") - -# -# Set up the variables exported to constants.hpp -# -set(PWN_LIBRARY_NAME ${PROJECT_NAME}) -set(PWN_LIBRARY_AUTHOR ${PROJECT_AUTHOR}) -set(PWN_LIBRARY_LICENSE ${PROJECT_LICENSE}) -set(PWN_LIBRARY_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) -set(PWN_LIBRARY_VERSION_MINOR ${PROJECT_VERSION_MINOR}) -set(PWN_LIBRARY_VERSION_PATCH ${PROJECT_VERSION_PATCH}) -set(PWN_LIBRARY_VERSION_RELEASE "Standalone") -set(PWN_LIBRARY_VERSION ${PROJECT_VERSION}) -set(PWN_BUILD_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) -set(PWN_BUILD_OS ${CMAKE_SYSTEM_NAME}) - -# -# Print out cmake compile info -# -message(STATUS "PWN_LOG_USE_COLOR ${PWN_LOG_USE_COLOR}") -message(STATUS "PWN_BUILD_TOOLKIT ${PWN_BUILD_TOOLKIT}") -message(STATUS "PWN_BUILD_TESTING ${PWN_BUILD_TESTING}") -message(STATUS "PWN_BUILD_DOCS ${PWN_BUILD_DOCS}") -message(STATUS "PWN_INCLUDE_DISASSEMBLER ${PWN_INCLUDE_DISASSEMBLER}") -message(STATUS "PWN_ENABLE_LUA_BACKDOOR ${PWN_ENABLE_LUA_BACKDOOR}") - -# -# If `git` is found, declare the branch/commit hash for debugging -# -find_package(Git) - -if(Git_FOUND) - execute_process( - COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD - OUTPUT_VARIABLE GIT_RELEASE_BRANCH - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - ) - execute_process( - COMMAND ${GIT_EXECUTABLE} log -n 1 --pretty=format:%t - OUTPUT_VARIABLE GIT_RELEASE_COMMIT - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - ) - - string(STRIP "${GIT_RELEASE_BRANCH}" GIT_RELEASE_BRANCH) - string(STRIP "${GIT_RELEASE_COMMIT}" GIT_RELEASE_COMMIT) - - set(PWN_LIBRARY_VERSION_RELEASE "${GIT_RELEASE_BRANCH}:${GIT_RELEASE_COMMIT}") -endif() - -# -# Compile all modules -# -foreach(MODULE ${PWN_MODULES}) - add_subdirectory(${MODULE}) -endforeach() +set(PWNLIB_MODULE_DIR ${PWNLIB_ROOT_DIR}/Modules CACHE INTERNAL "PWNLIB_MODULE_DIR") + +# +# Set up the variables exported to constants.hpp +# +set(PWN_LIBRARY_NAME ${PROJECT_NAME}) +set(PWN_LIBRARY_AUTHOR ${PROJECT_AUTHOR}) +set(PWN_LIBRARY_LICENSE ${PROJECT_LICENSE}) +set(PWN_LIBRARY_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) +set(PWN_LIBRARY_VERSION_MINOR ${PROJECT_VERSION_MINOR}) +set(PWN_LIBRARY_VERSION_PATCH ${PROJECT_VERSION_PATCH}) +set(PWN_LIBRARY_VERSION_RELEASE "Standalone") +set(PWN_LIBRARY_VERSION ${PROJECT_VERSION}) +set(PWN_BUILD_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) +set(PWN_BUILD_OS ${CMAKE_SYSTEM_NAME}) + +# +# Print out cmake compile info +# +message(STATUS "PWN_LOG_USE_COLOR ${PWN_LOG_USE_COLOR}") +message(STATUS "PWN_BUILD_TOOLKIT ${PWN_BUILD_TOOLKIT}") +message(STATUS "PWN_BUILD_TESTING ${PWN_BUILD_TESTING}") +message(STATUS "PWN_BUILD_DOCS ${PWN_BUILD_DOCS}") +message(STATUS "PWN_INCLUDE_DISASSEMBLER ${PWN_INCLUDE_DISASSEMBLER}") + +# +# If `git` is found, declare the branch/commit hash for debugging +# +find_package(Git) + +if(Git_FOUND) + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD + OUTPUT_VARIABLE GIT_RELEASE_BRANCH + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + execute_process( + COMMAND ${GIT_EXECUTABLE} log -n 1 --pretty=format:%t + OUTPUT_VARIABLE GIT_RELEASE_COMMIT + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + + string(STRIP "${GIT_RELEASE_BRANCH}" GIT_RELEASE_BRANCH) + string(STRIP "${GIT_RELEASE_COMMIT}" GIT_RELEASE_COMMIT) + + set(PWN_LIBRARY_VERSION_RELEASE "${GIT_RELEASE_BRANCH}:${GIT_RELEASE_COMMIT}") +endif() + +# +# Compile all modules +# +foreach(MODULE ${PWN_MODULES}) + add_subdirectory(${MODULE}) +endforeach() diff --git a/Modules/Common/Include/Context.hpp b/Modules/Common/Include/Context.hpp index eeeae2e5..8e9888e4 100644 --- a/Modules/Common/Include/Context.hpp +++ b/Modules/Common/Include/Context.hpp @@ -13,10 +13,6 @@ using namespace pwn; class GlobalContext { public: -#ifdef PWN_INCLUDE_BACKDOOR - std::jthread m_backdoor_thread; - std::vector> m_backdoor_clients; -#endif u64 CryptoSeed; std::mutex m_ConsoleMutex; std::mutex m_ConfigMutex; diff --git a/Modules/Process/CMakeLists.txt b/Modules/Process/CMakeLists.txt index 42085f18..88563b4a 100644 --- a/Modules/Process/CMakeLists.txt +++ b/Modules/Process/CMakeLists.txt @@ -13,6 +13,8 @@ elseif("${CMAKE_GENERATOR_PLATFORM}" STREQUAL win32) set(ASM_DIR ${SOURCE_DIR}/Win32/asm/x86) elseif("${CMAKE_GENERATOR_PLATFORM}" STREQUAL arm64) set(ASM_DIR ${SOURCE_DIR}/Win32/asm/arm64) +else() + message(FATAL_ERROR "Unknown arch ${CMAKE_GENERATOR_PLATFORM}") endif() if(WIN32) diff --git a/README.md b/README.md index a6613ace..159a9e3f 100644 --- a/README.md +++ b/README.md @@ -24,19 +24,25 @@ Git-Clone the template in [`hugsy/template-pwn`](https://github.com/hugsy/pwn--t ```cmake include(FetchContent) FetchContent_Declare( - Deps_Pwn + pwn++ GIT_REPOSITORY https://github.com/hugsy/pwn--.git - GIT_TAG main # or whatever + GIT_TAG main # or whatever other tag ) -FetchContent_MakeAvailable(Deps_Pwn) +FetchContent_MakeAvailable(pwn++) ``` ## What is it? A poor rewrite of my [PwnLib](https://github.com/hugsy/pwnlib) DLL in modern C++, battery-included pwn kit for Windows (and a bit for Linux). -The idea is to provide in C on Windows the same kind of functionalities than [pwntools](https://github.com/Gallopsled/pwntools) does in Python on Linux. -It's also a toy library meant for exploring Windows in a more friendly way. So if you're looking for years of poorly written C/C++ tangled with performant -inefficient ways to explore Windows at low-level, go no further friend this library is for you. +## Why? + +Because: + - I wanted a quick way to bootstrap my low-level experiments + - it's unacceptable to struggle every time I need a `hexdump`-like function + - modern C++ allows to do crazy useful offsec stuff, completely underused + - I like writing code + +The idea is to provide in C on Windows the same kind of functionalities than [pwntools](https://github.com/Gallopsled/pwntools) does in Python on Linux. It's also a toy library meant for exploring Windows in a more friendly way. So if you're looking for years of poorly written C++ tangled with performant inefficient ways to experiment low-level, go no further friend this library is for you. _Note_: the original `PwnLib` was written around Windows 7 for feature testing. This is 100% Windows 10/11 focused, so expect things to go wrong if you use any other Windows version. Some stuff may also go wrong in x86. Better use 64. It's not a bug but a design choice 😋 diff --git a/Tools/Win32/Backdoor/Backdoor.cpp b/Tools/Win32/Backdoor/Backdoor.cpp deleted file mode 100644 index 932b5bef..00000000 --- a/Tools/Win32/Backdoor/Backdoor.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/// -/// @file Backdoor -/// -/// @author hugsy (hugsy [AT] blah [DOT] cat) -/// -/// @brief -/// - -#include -using namespace pwn; - - -auto -wmain(const int argc, const wchar_t** argv) -> int -{ - Context.Set(Log::LogLevel::Debug); - - Utils::Random::seed(); - - info(L"Starting backdoor thread"); - { - auto res = Backdoor::start(); - if ( Failed(res) ) - { - err(L"Couldn't start backdoor thread"); - exit(EXIT_FAILURE); - } - } - - Utils::Pause(); - - Backdoor::stop(); - - return EXIT_SUCCESS; -} diff --git a/Tools/Win32/CMakeLists.txt b/Tools/Win32/CMakeLists.txt index 8915828d..30eb5fd9 100644 --- a/Tools/Win32/CMakeLists.txt +++ b/Tools/Win32/CMakeLists.txt @@ -16,9 +16,6 @@ set( ProcessGhosting ) -# if(PWN_INCLUDE_BACKDOOR) -# set(WIN32_TOOLS ${WIN32_TOOLS} Backdoor) -# endif(PWN_INCLUDE_BACKDOOR) foreach(TOOL_DIR ${WIN32_TOOLS}) file(GLOB SOURCE_FILES ${PWNLIB_TOOLS_WIN32_DIR}/${TOOL_DIR}/*.cpp ${PWNLIB_TOOLS_WIN32_DIR}/${TOOL_DIR}/*.asm) add_executable(${TOOL_DIR} WIN32 ${SOURCE_FILES}) diff --git a/pwn++/Source/Win32/dllmain.cpp b/pwn++/Source/Win32/dllmain.cpp index 723d91d3..92c0b492 100644 --- a/pwn++/Source/Win32/dllmain.cpp +++ b/pwn++/Source/Win32/dllmain.cpp @@ -10,28 +10,12 @@ OnAttachRoutine() // Initialize the RNG // utils::random::seed(); - -#ifdef PWN_INCLUDE_BACKDOOR - // - // Start the backdoor thread - // - { - auto res = Backdoor::start(); - if ( Failed(res) ) - { - err(L"Backdoor initialization failed"); - } - } -#endif // PWN_INCLUDE_BACKDOOR } void OnDetachRoutine() { -#ifdef PWN_INCLUDE_BACKDOOR - pwn::backdoor::Stop(); -#endif // PWN_INCLUDE_BACKDOOR } From f24939836eca5f393e67f7f0f1a45058af5f2373 Mon Sep 17 00:00:00 2001 From: hugsy Date: Wed, 8 Nov 2023 12:06:09 -0800 Subject: [PATCH 2/2] fixed missing `switch-break` in disassembly instruction formatting --- Modules/Assembly/Source/Disassembler.cpp | 4 ++++ Modules/CMakeLists.txt | 5 +++++ README.md | 3 ++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Modules/Assembly/Source/Disassembler.cpp b/Modules/Assembly/Source/Disassembler.cpp index 8bc4034b..86dc9f14 100644 --- a/Modules/Assembly/Source/Disassembler.cpp +++ b/Modules/Assembly/Source/Disassembler.cpp @@ -223,6 +223,8 @@ Disassembler::Format(Instruction& insn, uptr Address) { return Err(ErrorCode::ExternalApiCallFailed); } + + break; } #endif // PWN_DISASSEMBLE_X86 @@ -239,6 +241,8 @@ Disassembler::Format(Instruction& insn, uptr Address) { return Err(ErrorCode::ExternalApiCallFailed); } + + break; } #endif // PWN_DISASSEMBLE_ARM64 diff --git a/Modules/CMakeLists.txt b/Modules/CMakeLists.txt index 74e191d7..9465eb90 100644 --- a/Modules/CMakeLists.txt +++ b/Modules/CMakeLists.txt @@ -23,6 +23,11 @@ message(STATUS "PWN_BUILD_TESTING ${PWN_BUILD_TESTING}") message(STATUS "PWN_BUILD_DOCS ${PWN_BUILD_DOCS}") message(STATUS "PWN_INCLUDE_DISASSEMBLER ${PWN_INCLUDE_DISASSEMBLER}") +if(PWN_INCLUDE_DISASSEMBLER) + message(STATUS "PWN_INCLUDE_DISASSEMBLER_X86 ${PWN_DISASSEMBLE_X86}") + message(STATUS "PWN_INCLUDE_DISASSEMBLER_ARM64 ${PWN_DISASSEMBLE_ARM64}") +endif() + # # If `git` is found, declare the branch/commit hash for debugging # diff --git a/README.md b/README.md index 159a9e3f..5819325e 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,8 @@ A poor rewrite of my [PwnLib](https://github.com/hugsy/pwnlib) DLL in modern C++ Because: - I wanted a quick way to bootstrap my low-level experiments - - it's unacceptable to struggle every time I need a `hexdump`-like function + - it's unacceptable to struggle to disassemble code, to cross-compile your exploit or simply to have `hexdump`-like function + - only C/C++ provides the real right way to write assembly code - modern C++ allows to do crazy useful offsec stuff, completely underused - I like writing code