From f5aa3adacf8e3d023e5bd7b8a017e49aef996b52 Mon Sep 17 00:00:00 2001 From: DarthTon Date: Tue, 18 Jun 2019 12:44:17 +0300 Subject: [PATCH] use exceptions instead of status chaining --- .gitignore | 2 +- .../rewolf-wow64ext/src/wow64ext.cpp | 4 +- src/BlackBone/Asm/AsmHelper32.cpp | 1 + src/BlackBone/Asm/AsmHelper64.cpp | 1 + src/BlackBone/Asm/AsmVariant.hpp | 5 + src/BlackBone/BlackBone.vcxproj | 4 +- src/BlackBone/BlackBone.vcxproj.filters | 12 +- src/BlackBone/DriverControl/DriverControl.cpp | 46 +- src/BlackBone/DriverControl/DriverControl.h | 6 +- src/BlackBone/Include/CallResult.h | 99 -- src/BlackBone/Include/Exception.h | 106 ++ src/BlackBone/Include/ScopeExit.h | 42 + src/BlackBone/Include/Types.h | 20 + src/BlackBone/LocalHook/LocalHook.hpp | 14 +- src/BlackBone/LocalHook/LocalHookBase.cpp | 8 +- src/BlackBone/LocalHook/LocalHookBase.h | 2 +- src/BlackBone/LocalHook/TraceHook.cpp | 10 +- src/BlackBone/ManualMap/MExcept.cpp | 76 +- src/BlackBone/ManualMap/MExcept.h | 13 +- src/BlackBone/ManualMap/MMap.cpp | 1048 +++++++---------- src/BlackBone/ManualMap/MMap.h | 68 +- src/BlackBone/ManualMap/Native/NtLoader.cpp | 472 ++++---- src/BlackBone/ManualMap/Native/NtLoader.h | 30 +- src/BlackBone/Misc/NameResolve.cpp | 18 +- src/BlackBone/Misc/NameResolve.h | 19 +- src/BlackBone/Misc/StackTrace.h | 248 ++++ src/BlackBone/Misc/Trace.hpp | 18 +- src/BlackBone/Misc/Utils.cpp | 58 +- src/BlackBone/Misc/Utils.h | 55 +- src/BlackBone/PE/ImageNET.cpp | 10 +- src/BlackBone/PE/PEImage.cpp | 8 +- src/BlackBone/PE/PEImage.h | 2 +- src/BlackBone/Patterns/PatternSearch.cpp | 137 +-- src/BlackBone/Patterns/PatternSearch.h | 36 +- src/BlackBone/Process/MemBlock.cpp | 194 +-- src/BlackBone/Process/MemBlock.h | 89 +- src/BlackBone/Process/MultPtr.hpp | 26 +- src/BlackBone/Process/Process.cpp | 269 +++-- src/BlackBone/Process/Process.h | 83 +- src/BlackBone/Process/ProcessCore.cpp | 90 +- src/BlackBone/Process/ProcessCore.h | 41 +- src/BlackBone/Process/ProcessMemory.cpp | 125 +- src/BlackBone/Process/ProcessMemory.h | 71 +- src/BlackBone/Process/ProcessModules.cpp | 314 +++-- src/BlackBone/Process/ProcessModules.h | 73 +- src/BlackBone/Process/RPC/RemoteContext.hpp | 187 ++- src/BlackBone/Process/RPC/RemoteExec.cpp | 517 ++++---- src/BlackBone/Process/RPC/RemoteExec.h | 74 +- src/BlackBone/Process/RPC/RemoteFunction.hpp | 48 +- src/BlackBone/Process/RPC/RemoteHook.cpp | 126 +- src/BlackBone/Process/RPC/RemoteHook.h | 13 +- src/BlackBone/Process/RPC/RemoteLocalHook.cpp | 301 +++-- src/BlackBone/Process/RPC/RemoteLocalHook.h | 74 +- src/BlackBone/Process/RPC/RemoteMemory.cpp | 41 +- src/BlackBone/Process/RPC/RemoteMemory.h | 2 +- src/BlackBone/Process/Threads/Thread.cpp | 53 +- src/BlackBone/Process/Threads/Thread.h | 53 +- src/BlackBone/Process/Threads/Threads.cpp | 46 +- src/BlackBone/Process/Threads/Threads.h | 9 +- src/BlackBone/Subsystem/NativeSubsystem.cpp | 172 +-- src/BlackBone/Subsystem/NativeSubsystem.h | 67 +- src/BlackBone/Subsystem/Wow64Subsystem.cpp | 108 +- src/BlackBone/Subsystem/Wow64Subsystem.h | 46 +- src/BlackBone/Subsystem/x86Subsystem.cpp | 51 +- src/BlackBone/Subsystem/x86Subsystem.h | 24 +- src/BlackBone/Symbols/PatternLoader.cpp | 5 +- src/BlackBone/Symbols/PatternLoader.h | 3 +- src/BlackBone/Symbols/SymbolLoader.cpp | 12 +- src/BlackBone/Symbols/SymbolLoader.h | 6 +- src/BlackBoneTest/BlackBoneTest.vcxproj | 2 + .../BlackBoneTest.vcxproj.filters | 6 + src/BlackBoneTest/Common.h | 6 + src/BlackBoneTest/TestAsmVariant.cpp | 12 +- src/BlackBoneTest/TestBasic.cpp | 81 +- src/BlackBoneTest/TestDriver.cpp | 5 +- src/BlackBoneTest/TestGuard.cpp | 1 - src/BlackBoneTest/TestManualMap.cpp | 38 +- src/BlackBoneTest/TestModules.cpp | 5 +- src/BlackBoneTest/TestMultiPtr.cpp | 5 +- src/BlackBoneTest/TestPatternScan.cpp | 4 +- src/BlackBoneTest/TestPebTeb.cpp | 78 ++ src/BlackBoneTest/TestRemoteCall.cpp | 56 +- src/BlackBoneTest/TestRemoteHook.cpp | 20 +- src/BlackBoneTest/TestRemoteMemory.cpp | 4 +- src/BlackBoneTest/TestScopeExit.cpp | 20 + src/BlackBoneTest/TestSymbols.cpp | 10 +- src/Samples/Main.cpp | 86 +- src/Samples/ManualMap.cpp | 61 +- 88 files changed, 3313 insertions(+), 3298 deletions(-) delete mode 100644 src/BlackBone/Include/CallResult.h create mode 100644 src/BlackBone/Include/Exception.h create mode 100644 src/BlackBone/Include/ScopeExit.h create mode 100644 src/BlackBone/Misc/StackTrace.h create mode 100644 src/BlackBoneTest/TestPebTeb.cpp create mode 100644 src/BlackBoneTest/TestScopeExit.cpp diff --git a/.gitignore b/.gitignore index d0bbc704..3f2edd68 100644 --- a/.gitignore +++ b/.gitignore @@ -81,7 +81,7 @@ GeneratedFiles*/ *.opendb *.db *.sqlite -/.vs +*.vs *.db-shm *.db-wal *.json diff --git a/src/3rd_party/rewolf-wow64ext/src/wow64ext.cpp b/src/3rd_party/rewolf-wow64ext/src/wow64ext.cpp index 29483432..fd5ae334 100644 --- a/src/3rd_party/rewolf-wow64ext/src/wow64ext.cpp +++ b/src/3rd_party/rewolf-wow64ext/src/wow64ext.cpp @@ -83,7 +83,7 @@ extern "C" DWORD64 __cdecl X64Call(DWORD64 func, int argC, ...) reg64 _rdx = { (argC > 0) ? argC--, va_arg(args, DWORD64) : 0 }; reg64 _r8 = { (argC > 0) ? argC--, va_arg(args, DWORD64) : 0 }; reg64 _r9 = { (argC > 0) ? argC--, va_arg(args, DWORD64) : 0 }; - reg64 _rax = { 0 }; + reg64 _rax = { }; reg64 restArgs = { (DWORD64)&va_arg(args, DWORD64) }; @@ -423,7 +423,7 @@ extern "C" DWORD64 __cdecl GetProcAddress64(DWORD64 hModule, const char* funcNam return 0; } - _UNICODE_STRING_T fName = { 0 }; + _UNICODE_STRING_T fName = { }; fName.Buffer = (DWORD64)funcName; fName.Length = (WORD)strlen(funcName); fName.MaximumLength = fName.Length + 1; diff --git a/src/BlackBone/Asm/AsmHelper32.cpp b/src/BlackBone/Asm/AsmHelper32.cpp index ab8eccc8..698ce050 100644 --- a/src/BlackBone/Asm/AsmHelper32.cpp +++ b/src/BlackBone/Asm/AsmHelper32.cpp @@ -201,6 +201,7 @@ void AsmHelper32::PushArg( const AsmVariant& arg, eArgType regidx /*= AT_stack*/ break; case AsmVariant::dataPtr: + case AsmVariant::dataPtrConst: PushArgp( arg.new_imm_val != 0 ? arg.new_imm_val : arg.imm_val, regidx ); break; diff --git a/src/BlackBone/Asm/AsmHelper64.cpp b/src/BlackBone/Asm/AsmHelper64.cpp index 1a08e561..11f559bf 100644 --- a/src/BlackBone/Asm/AsmHelper64.cpp +++ b/src/BlackBone/Asm/AsmHelper64.cpp @@ -186,6 +186,7 @@ void AsmHelper64::PushArg( const AsmVariant& arg, int32_t index ) break; case AsmVariant::dataPtr: + case AsmVariant::dataPtrConst: case AsmVariant::dataStruct: // Use new_imm_val when available. It's populated by remote call engine. PushArgp( arg.new_imm_val != 0 ? arg.new_imm_val : arg.imm_val64, index ); diff --git a/src/BlackBone/Asm/AsmVariant.hpp b/src/BlackBone/Asm/AsmVariant.hpp index 118edb52..f74bac3b 100644 --- a/src/BlackBone/Asm/AsmVariant.hpp +++ b/src/BlackBone/Asm/AsmVariant.hpp @@ -36,6 +36,7 @@ struct AsmVariant imm_double, // double or long double imm_float, // float dataPtr, // pointer to local data (e.g. string or pointer to structure) + dataPtrConst, // pointer to constant local data dataStruct, // structure passed by value structRet, // pointer to space into which return value is copied (used when returning structures by value) mem, // stack variable @@ -102,6 +103,10 @@ struct AsmVariant set( dataStruct, argSize, reinterpret_cast(buf.data()) ); memcpy( buf.data(), &arg, argSize ); } + + // Mark as constant to prevent reading data back + if (type == dataPtr && std::is_const_v>) + type = dataPtrConst; } // Custom size pointer diff --git a/src/BlackBone/BlackBone.vcxproj b/src/BlackBone/BlackBone.vcxproj index cbbc2f04..ca1678a2 100644 --- a/src/BlackBone/BlackBone.vcxproj +++ b/src/BlackBone/BlackBone.vcxproj @@ -703,6 +703,7 @@ xcopy "$(ProjectDir)..\..\DIA\$(Platform)\symsrv.dll" "$(TargetDir)" /Y + @@ -783,7 +784,7 @@ xcopy "$(ProjectDir)..\..\DIA\$(Platform)\symsrv.dll" "$(TargetDir)" /Y - + @@ -809,6 +810,7 @@ xcopy "$(ProjectDir)..\..\DIA\$(Platform)\symsrv.dll" "$(TargetDir)" /Y + diff --git a/src/BlackBone/BlackBone.vcxproj.filters b/src/BlackBone/BlackBone.vcxproj.filters index 6cb2f9d3..3b5b5485 100644 --- a/src/BlackBone/BlackBone.vcxproj.filters +++ b/src/BlackBone/BlackBone.vcxproj.filters @@ -211,6 +211,9 @@ Symbols + + Include + @@ -457,9 +460,6 @@ Subsystem - - Include - AsmJit\Helpers @@ -487,6 +487,12 @@ Syscalls + + Include + + + Misc + diff --git a/src/BlackBone/DriverControl/DriverControl.cpp b/src/BlackBone/DriverControl/DriverControl.cpp index 74ac7bcf..e196bbdf 100644 --- a/src/BlackBone/DriverControl/DriverControl.cpp +++ b/src/BlackBone/DriverControl/DriverControl.cpp @@ -124,7 +124,7 @@ NTSTATUS DriverControl::Unload() /// Status code NTSTATUS DriverControl::MapMemory( DWORD pid, const std::wstring& pipeName, bool mapSections, MapMemoryResult& result ) { - MAP_MEMORY data = { 0 }; + MAP_MEMORY data = { }; DWORD bytes = 0; ULONG sizeRequired = 0; data.pid = pid; @@ -169,8 +169,8 @@ NTSTATUS DriverControl::MapMemory( DWORD pid, const std::wstring& pipeName, bool /// Status code NTSTATUS DriverControl::MapMemoryRegion( DWORD pid, ptr_t base, uint32_t size, MapMemoryRegionResult& result ) { - MAP_MEMORY_REGION data = { 0 }; - MAP_MEMORY_REGION_RESULT mapResult = { 0 }; + MAP_MEMORY_REGION data = { }; + MAP_MEMORY_REGION_RESULT mapResult = { }; DWORD bytes = 0; // Not loaded @@ -227,7 +227,7 @@ NTSTATUS DriverControl::UnmapMemory( DWORD pid ) /// Status code NTSTATUS DriverControl::UnmapMemoryRegion( DWORD pid, ptr_t base, uint32_t size ) { - UNMAP_MEMORY_REGION data = { 0 }; + UNMAP_MEMORY_REGION data = { }; DWORD bytes = 0; data.pid = pid; @@ -304,7 +304,7 @@ NTSTATUS DriverControl::ProtectProcess( NTSTATUS DriverControl::PromoteHandle( DWORD pid, HANDLE handle, DWORD access ) { DWORD bytes = 0; - HANDLE_GRANT_ACCESS grantAccess = { 0 }; + HANDLE_GRANT_ACCESS grantAccess = { }; grantAccess.pid = pid; grantAccess.handle = (ULONGLONG)handle; @@ -323,7 +323,7 @@ NTSTATUS DriverControl::PromoteHandle( DWORD pid, HANDLE handle, DWORD access ) /// /// Allocate virtual memory /// -/// Tarhet PID +/// Target PID /// Desired base. If 0 address is chosed by the system /// Region size /// Allocation type - MEM_RESERVE/MEM_COMMIT @@ -332,8 +332,8 @@ NTSTATUS DriverControl::PromoteHandle( DWORD pid, HANDLE handle, DWORD access ) NTSTATUS DriverControl::AllocateMem( DWORD pid, ptr_t& base, ptr_t& size, DWORD type, DWORD protection, bool physical /*= false*/ ) { DWORD bytes = 0; - ALLOCATE_FREE_MEMORY allocMem = { 0 }; - ALLOCATE_FREE_MEMORY_RESULT result = { 0 }; + ALLOCATE_FREE_MEMORY allocMem = { }; + ALLOCATE_FREE_MEMORY_RESULT result = { }; allocMem.pid = pid; allocMem.base = base; @@ -366,7 +366,7 @@ NTSTATUS DriverControl::AllocateMem( DWORD pid, ptr_t& base, ptr_t& size, DWORD /// /// Free virtual memory /// -/// Tarhet PID +/// Target PID /// Desired base. If 0 address is chosed by the system /// Region size /// Free type - MEM_RELEASE/MEM_DECOMMIT @@ -374,8 +374,8 @@ NTSTATUS DriverControl::AllocateMem( DWORD pid, ptr_t& base, ptr_t& size, DWORD NTSTATUS DriverControl::FreeMem( DWORD pid, ptr_t base, ptr_t size, DWORD type ) { DWORD bytes = 0; - ALLOCATE_FREE_MEMORY freeMem = { 0 }; - ALLOCATE_FREE_MEMORY_RESULT result = { 0 }; + ALLOCATE_FREE_MEMORY freeMem = { }; + ALLOCATE_FREE_MEMORY_RESULT result = { }; freeMem.pid = pid; freeMem.base = base; @@ -411,7 +411,7 @@ NTSTATUS DriverControl::FreeMem( DWORD pid, ptr_t base, ptr_t size, DWORD type ) NTSTATUS DriverControl::ReadMem( DWORD pid, ptr_t base, ptr_t size, PVOID buffer ) { DWORD bytes = 0; - COPY_MEMORY copyMem = { 0 }; + COPY_MEMORY copyMem = { }; copyMem.pid = pid; copyMem.targetPtr = base; @@ -440,7 +440,7 @@ NTSTATUS DriverControl::ReadMem( DWORD pid, ptr_t base, ptr_t size, PVOID buffer NTSTATUS DriverControl::WriteMem( DWORD pid, ptr_t base, ptr_t size, PVOID buffer ) { DWORD bytes = 0; - COPY_MEMORY copyMem = { 0 }; + COPY_MEMORY copyMem = { }; copyMem.pid = pid; copyMem.targetPtr = base; @@ -462,14 +462,14 @@ NTSTATUS DriverControl::WriteMem( DWORD pid, ptr_t base, ptr_t size, PVOID buffe /// Change memory protection /// /// Target PID. -/// Regiod base address +/// Region base address /// Region size /// New protection /// Status code NTSTATUS DriverControl::ProtectMem( DWORD pid, ptr_t base, ptr_t size, DWORD protection ) { DWORD bytes = 0; - PROTECT_MEMORY protectMem = { 0 }; + PROTECT_MEMORY protectMem = { }; protectMem.pid = pid; protectMem.base = base; @@ -550,7 +550,7 @@ NTSTATUS DriverControl::MmapDll( { DWORD bytes = 0; INJECT_DLL data = { IT_MMap }; - UNICODE_STRING ustr = { 0 }; + UNICODE_STRING ustr = { }; // Convert path to native format SAFE_NATIVE_CALL( RtlDosPathNameToNtPathName_U, path.c_str(), &ustr, nullptr, nullptr ); @@ -622,13 +622,13 @@ NTSTATUS DriverControl::MmapDll( /// /// Manually map another system driver into system space /// -/// Fully quialified path to the drver +/// Fully qualified path to the driver /// Status code NTSTATUS DriverControl::MMapDriver( const std::wstring& path ) { DWORD bytes = 0; - MMAP_DRIVER data = { { 0 } }; - UNICODE_STRING ustr = { 0 }; + MMAP_DRIVER data = { }; + UNICODE_STRING ustr = { }; // Not loaded if (_hDriver == INVALID_HANDLE_VALUE) @@ -656,7 +656,7 @@ NTSTATUS DriverControl::MMapDriver( const std::wstring& path ) NTSTATUS DriverControl::ConcealVAD( DWORD pid, ptr_t base, uint32_t size ) { DWORD bytes = 0; - HIDE_VAD hideVAD = { 0 }; + HIDE_VAD hideVAD = { }; hideVAD.base = base; hideVAD.size = size; @@ -707,7 +707,7 @@ NTSTATUS DriverControl::EnumMemoryRegions( DWORD pid, std::vector(malloc( size )); @@ -773,7 +773,7 @@ NTSTATUS DriverControl::LoadDriver( const std::wstring& svcName, const std::wstr /// Status NTSTATUS DriverControl::UnloadDriver( const std::wstring& svcName ) { - UNICODE_STRING Ustr = { 0 }; + UNICODE_STRING Ustr = { }; std::wstring regPath = L"\\registry\\machine\\SYSTEM\\CurrentControlSet\\Services\\" + svcName; SAFE_CALL( RtlInitUnicodeString, &Ustr, regPath.c_str() ); @@ -796,7 +796,7 @@ LSTATUS DriverControl::PrepareDriverRegEntry( const std::wstring& svcName, const RegHandle svcRoot, svcKey; DWORD dwType = 1; LSTATUS status = 0; - WCHAR wszLocalPath[MAX_PATH] = { 0 }; + WCHAR wszLocalPath[MAX_PATH] = { }; swprintf_s( wszLocalPath, ARRAYSIZE( wszLocalPath ), L"\\??\\%s", path.c_str() ); diff --git a/src/BlackBone/DriverControl/DriverControl.h b/src/BlackBone/DriverControl/DriverControl.h index fbd988f2..48e67a81 100644 --- a/src/BlackBone/DriverControl/DriverControl.h +++ b/src/BlackBone/DriverControl/DriverControl.h @@ -99,7 +99,7 @@ class DriverControl /// /// Allocate virtual memory /// - /// Tarhet PID + /// Target PID /// Desired base. If 0 address is chosed by the system /// Region size /// Allocation type - MEM_RESERVE/MEM_COMMIT @@ -110,7 +110,7 @@ class DriverControl /// /// Free virtual memory /// - /// Tarhet PID + /// Target PID /// Desired base. If 0 address is chosed by the system /// Region size /// Free type - MEM_RELEASE/MEM_DECOMMIT @@ -250,7 +250,7 @@ class DriverControl /// /// Manually map another system driver into system space /// - /// Fully quialified path to the drver + /// Fully qualified path to the driver /// Status code BLACKBONE_API NTSTATUS MMapDriver( const std::wstring& path ); diff --git a/src/BlackBone/Include/CallResult.h b/src/BlackBone/Include/CallResult.h deleted file mode 100644 index 3a5c88e5..00000000 --- a/src/BlackBone/Include/CallResult.h +++ /dev/null @@ -1,99 +0,0 @@ -#pragma once -#if _MSC_VER >= 1910 - -#include -#include - -namespace blackbone -{ -/// -/// Function result or failure status -/// -template -struct call_result_t -{ - NTSTATUS status = STATUS_UNSUCCESSFUL; // Execution status - std::optional result_data = std::nullopt; // Returned value - - call_result_t() = default; - - call_result_t( T result_, NTSTATUS status_ = STATUS_SUCCESS ) - : status ( status_ ) - , result_data ( std::move( result_ ) ) - { - assert( result_data.has_value() ); - } - - call_result_t( NTSTATUS status_ ) - : status ( status_ ) - { - assert( status_ != STATUS_SUCCESS ); - } - - inline bool success() const { return NT_SUCCESS( status ); } - inline T& result() { return result_data.value(); } - inline const T& result() const { return result_data.value(); } - inline T result( const T& def_val ) const { return result_data.value_or( def_val ); } - - inline explicit operator bool() const { return NT_SUCCESS( status ); } - inline explicit operator T() const { return result_data.value(); } - - inline T* operator ->() { return &result_data.value(); } - inline T& operator *() { return result_data.value(); } -}; -} - -#else - -#include -#include - -namespace blackbone -{ - /// - /// Function result or failure status - /// - template - struct call_result_t - { - NTSTATUS status = STATUS_UNSUCCESSFUL; // Execution status - std::unique_ptr result_data; // Returned value - - call_result_t() = default; - - call_result_t(T result_, NTSTATUS status_ = STATUS_SUCCESS) - : status(status_) - , result_data(std::make_unique(std::move(result_))) - { - assert(result_data.get()); - } - - call_result_t(NTSTATUS status_) - : status(status_) - { - assert(status_ != STATUS_SUCCESS); - } - - - private: - inline T* value() { - if (!result_data) { - throw std::logic_error("bad optional access."); - } - return result_data.get(); - } - public: - - inline bool success() const { return NT_SUCCESS( status ); } - inline T& result() { return *value(); } - inline const T& result() const { return *value(); } - inline T result(const T& def_val) const { return result_data ? *result_data.get() : def_val; } - - inline explicit operator bool() const { return NT_SUCCESS( status ); } - inline explicit operator T() const { return *value(); } - - inline T* operator ->() { return value(); } - inline T& operator *() { return *value(); } - }; -} -#endif \ No newline at end of file diff --git a/src/BlackBone/Include/Exception.h b/src/BlackBone/Include/Exception.h new file mode 100644 index 00000000..abe9c100 --- /dev/null +++ b/src/BlackBone/Include/Exception.h @@ -0,0 +1,106 @@ +#pragma once +#include "../Misc/Utils.h" +#include "../Misc/Trace.hpp" +#include "../Misc/StackTrace.h" +#include +#include + +namespace blackbone +{ + +class nt_exception : public std::exception +{ +public: + explicit nt_exception( const char* message ) + : _message( message ) + , _status( STATUS_UNSUCCESSFUL ) + { + capture_stack_trace(); + } + + explicit nt_exception( NTSTATUS status ) + : _status( status ) + { + _message = Utils::GetErrorDescriptionA( status ); + capture_stack_trace(); + } + + template + nt_exception( const char* fmt, Args... args ) + : _status( STATUS_UNSUCCESSFUL ) + { + char buf[1024] = { }; + sprintf_s( buf, fmt, args... ); + _message = buf; + + capture_stack_trace(); + } + + template + nt_exception( NTSTATUS status, const char* fmt, Args... args ) + : _status( status ) + { + char buf[1024] = { }; + sprintf_s( buf, fmt, args... ); + + std::ostringstream printer; + printer << buf << ". NTSTATUS 0x" << std::hex << status << ": " << Utils::GetErrorDescriptionA( status ); + _message = printer.str(); + + capture_stack_trace(); + } + + const char* what() const + { + return _message.c_str(); + } + + NTSTATUS status() const + { + return _status; + } + + const char* stack_trace() const + { + return _stackTrace.c_str(); + } + +private: + void capture_stack_trace() + { +#ifdef _DEBUG + CONTEXT ctx = {}; + RtlCaptureContext( &ctx ); + _stackTrace = debug::StackTrace::Capture( &ctx ); +#endif // _DEBUG + } + +private: + std::string _message; + std::string _stackTrace; + NTSTATUS _status; +}; + +#ifdef _DEBUG +#define LOG_ENTRY "Exception in %s - %s:%d. Message: %s\nStack trace:\n%s", __FUNCTION__, __FILE__, __LINE__, e.what(), e.stack_trace() +#else +#define LOG_ENTRY "Exception in %s - %s:%d. Message: %s", __FUNCTION__, __FILE__, __LINE__, e.what() +#endif + +#define THROW_AND_LOG(...) \ +{ \ + auto e = nt_exception( __VA_ARGS__ ); \ + BLACKBONE_TRACE( LOG_ENTRY ); \ + throw e; \ +} + +#define THROW_WITH_STATUS_AND_LOG(status, ...) \ +{ \ + auto e = nt_exception( status, __VA_ARGS__ ); \ + BLACKBONE_TRACE( LOG_ENTRY ); \ + throw e; \ +} + +#define THROW_ON_FAIL_AND_LOG(status, ...) if (!NT_SUCCESS( status )) THROW_WITH_STATUS_AND_LOG( status, __VA_ARGS__ ); + +} diff --git a/src/BlackBone/Include/ScopeExit.h b/src/BlackBone/Include/ScopeExit.h new file mode 100644 index 00000000..4513a271 --- /dev/null +++ b/src/BlackBone/Include/ScopeExit.h @@ -0,0 +1,42 @@ +#pragma once +#include + +namespace blackbone +{ + +#define SCOPE_EXIT_CAT2(x, y) x##y +#define SCOPE_EXIT_CAT(x, y) SCOPE_EXIT_CAT2(x, y) +#define ON_SCOPE_EXIT auto SCOPE_EXIT_CAT(scopeExit_, __COUNTER__) = MakeScopeExit() += [&] + +template +class ScopeExit +{ +public: + ScopeExit( F&& f ) + : _callback( f ) + { + } + + ~ScopeExit() + { + _callback(); + } + + ScopeExit( ScopeExit&& other ) = default; + ScopeExit( const ScopeExit& ) = delete; + ScopeExit& operator=( const ScopeExit& ) = delete; + +private: + F _callback; +}; + +struct MakeScopeExit +{ + template + ScopeExit operator+=( F&& f ) + { + return ScopeExit( std::move( f ) ); + } +}; + +} \ No newline at end of file diff --git a/src/BlackBone/Include/Types.h b/src/BlackBone/Include/Types.h index 328f683d..82ada9f1 100644 --- a/src/BlackBone/Include/Types.h +++ b/src/BlackBone/Include/Types.h @@ -81,4 +81,24 @@ struct ModuleData using ModuleDataPtr = std::shared_ptr; +inline void VirtualFreeWrapper( void* ptr ) +{ + VirtualFree( ptr, 0, MEM_RELEASE ); +} + +template +using raw_ptr = std::unique_ptr; + +template +raw_ptr make_raw_ptr( void* ptr ) +{ + return raw_ptr( static_cast(ptr), &VirtualFreeWrapper ); +} + +template +raw_ptr make_raw_ptr( size_t size, DWORD protection = PAGE_READWRITE ) +{ + return make_raw_ptr( VirtualAlloc( nullptr, size, MEM_COMMIT, protection ) ); +} + } diff --git a/src/BlackBone/LocalHook/LocalHook.hpp b/src/BlackBone/LocalHook/LocalHook.hpp index 8aad7227..1b3c003a 100644 --- a/src/BlackBone/LocalHook/LocalHook.hpp +++ b/src/BlackBone/LocalHook/LocalHook.hpp @@ -239,12 +239,20 @@ class Detour: public HookHandler if (!this->_vecHandler) return false; - this->_breakpoints.insert( std::make_pair( this->_original, (DetourBase*)this ) ); + this->_breakpoints.insert( std::make_pair( this->_original, static_cast(this) ) ); // Add breakpoint to every thread for (auto& thd : thisProc.threads().getAll()) - this->_hwbpIdx[thd->id()] = thd->AddHWBP( reinterpret_cast(this->_original), hwbp_execute, hwbp_1 ).result(); - + { + try + { + this->_hwbpIdx[thd->id()] = thd->AddHWBP( reinterpret_cast(this->_original), hwbp_execute, hwbp_1 ); + } + catch (const nt_exception&) + { + } + } + return this->_hooked = true; } }; diff --git a/src/BlackBone/LocalHook/LocalHookBase.cpp b/src/BlackBone/LocalHook/LocalHookBase.cpp index e3c99863..6b5eba32 100644 --- a/src/BlackBone/LocalHook/LocalHookBase.cpp +++ b/src/BlackBone/LocalHook/LocalHookBase.cpp @@ -24,7 +24,7 @@ bool DetourBase::AllocateBuffer( uint8_t* nearest ) if (_buf != nullptr) return true; - MEMORY_BASIC_INFORMATION mbi = { 0 }; + MEMORY_BASIC_INFORMATION mbi = { }; for (SIZE_T addr = (SIZE_T)nearest; addr > (SIZE_T)nearest - 0x80000000; addr = (SIZE_T)mbi.BaseAddress - 1) { if (!VirtualQuery( (LPCVOID)addr, &mbi, sizeof( mbi ) )) @@ -90,7 +90,7 @@ bool DetourBase::EnableHook() /// true on success bool DetourBase::ToggleHBP( int index, bool enable ) { - CONTEXT context = { 0 }; + CONTEXT context = { }; context.ContextFlags = CONTEXT_DEBUG_REGISTERS; if (GetThreadContext( GetCurrentThread(), &context ) != TRUE) @@ -107,14 +107,14 @@ bool DetourBase::ToggleHBP( int index, bool enable ) /// /// Copy original function bytes /// -/// Origianl function address +/// Original function address void DetourBase::CopyOldCode( uint8_t* ptr ) { // Store original bytes uint8_t* src = ptr; uint8_t* thunk = _origThunk, *original = _origCode; uint32_t all_len = 0; - ldasm_data ld = { 0 }; + ldasm_data ld = { }; do { diff --git a/src/BlackBone/LocalHook/LocalHookBase.h b/src/BlackBone/LocalHook/LocalHookBase.h index cb0ee9a4..79fad73a 100644 --- a/src/BlackBone/LocalHook/LocalHookBase.h +++ b/src/BlackBone/LocalHook/LocalHookBase.h @@ -85,7 +85,7 @@ class DetourBase /// /// Copy original function bytes /// - /// Origianl function address + /// Original function address BLACKBONE_API void CopyOldCode( uint8_t* Ptr ); /// diff --git a/src/BlackBone/LocalHook/TraceHook.cpp b/src/BlackBone/LocalHook/TraceHook.cpp index d7f71a52..4268455b 100644 --- a/src/BlackBone/LocalHook/TraceHook.cpp +++ b/src/BlackBone/LocalHook/TraceHook.cpp @@ -293,14 +293,14 @@ bool TraceHook::CheckBranching( const HookContext& ctx, uintptr_t ip, uintptr_t // Stack pointer changed if (ip - ctx.lastIP >= 8 && sp != ctx.lastSP) { - DISASM info = { 0 }; + DISASM info = { }; info.EIP = ctx.lastIP; #ifdef USE64 info.Archi = 64; #endif - // FIXME: Alternateve for MinGW + // FIXME: Alternative for MinGW #ifdef COMPILER_MSVC // Double-check call instruction using disasm if (Disasm( &info ) > 0 && info.Instruction.BranchType == CallType) @@ -406,8 +406,8 @@ size_t TraceHook::StackBacktrace( uintptr_t ip, uintptr_t sp, vecStackFrames& re // Walk stack for (uintptr_t stackPtr = sp; stackPtr < stack_base && results.size() <= depth; stackPtr += sizeof(void*)) { - uintptr_t stack_val = *(uintptr_t*)stackPtr; - MEMORY_BASIC_INFORMATION meminfo = { 0 }; + uintptr_t stack_val = *reinterpret_cast(stackPtr); + MEMORY_BASIC_INFORMATION meminfo = { }; // Decode value uintptr_t original = stack_val & HIGHEST_BIT_UNSET; @@ -433,7 +433,7 @@ size_t TraceHook::StackBacktrace( uintptr_t ip, uintptr_t sp, vecStackFrames& re // Detect 'call' instruction for (uintptr_t j = 1; j < 8; j++) { - DISASM info = { 0 }; + DISASM info = { }; info.EIP = original - j; #ifdef USE64 diff --git a/src/BlackBone/ManualMap/MExcept.cpp b/src/BlackBone/ManualMap/MExcept.cpp index 1559f677..4e25d55a 100644 --- a/src/BlackBone/ManualMap/MExcept.cpp +++ b/src/BlackBone/ManualMap/MExcept.cpp @@ -206,23 +206,15 @@ uint8_t MExcept::_handler64[] = /// Target process /// Target module /// Partial exception support -/// Error code -NTSTATUS MExcept::CreateVEH( Process& proc, ModuleData& mod, bool partial ) +void MExcept::CreateVEH( Process* proc, ModuleData& mod, bool partial ) { - uint64_t result = 0; - auto& mods = proc.modules(); + auto& mods = proc->modules(); if (mod.type == mt_mod64) { // Add module to module table if (!_pModTable.valid()) - { - auto mem = proc.memory().Allocate( 0x1000, PAGE_READWRITE, 0, false ); - if (!mem) - return mem.status; - - _pModTable = std::move( mem.result() ); - } + _pModTable = proc->memory().Allocate( 0x1000, PAGE_READWRITE, 0, false ); ModuleTable table = { }; _pModTable.Read ( 0, table ); @@ -237,14 +229,10 @@ NTSTATUS MExcept::CreateVEH( Process& proc, ModuleData& mod, bool partial ) // No handler required if (partial) - return STATUS_SUCCESS; + return; // VEH codecave - auto mem = proc.memory().Allocate( 0x2000, PAGE_EXECUTE_READWRITE, 0, false ); - if (!mem) - return mem.status; - - _pVEHCode = std::move( mem.result() ); + _pVEHCode = proc->memory().Allocate( 0x2000, PAGE_EXECUTE_READWRITE, 0 ); BLACKBONE_TRACE( "ManualMap: Vectored hander: 0x%p", _pVEHCode.ptr() ); @@ -281,44 +269,30 @@ NTSTATUS MExcept::CreateVEH( Process& proc, ModuleData& mod, bool partial ) memcpy( newHandler, _handler32, handlerSize ); auto pDecode = mods.GetNtdllExport( "RtlDecodeSystemPointer", mod.type, Sections ); - if (!pDecode) - return pDecode.status; replaceStub( newHandler, handlerSize, 0xDEADDA7A, static_cast(g_symbols.LdrpInvertedFunctionTable32) ); - replaceStub( newHandler, handlerSize, 0xDEADC0DE, static_cast(pDecode->procAddress) ); + replaceStub( newHandler, handlerSize, 0xDEADC0DE, static_cast(pDecode.procAddress) ); replaceStub( newHandler, handlerSize, 0xDEADC0D2, static_cast(g_symbols.LdrProtectMrdata) ); } // Write handler data into target process - if (!NT_SUCCESS( _pVEHCode.Write( 0, handlerSize, newHandler ) )) - { - _pVEHCode.Free(); - return LastNtStatus(); - } + _pVEHCode.Write( 0, handlerSize, newHandler ); // AddVectoredExceptionHandler(0, pHandler); auto pAddHandler = mods.GetNtdllExport( "RtlAddVectoredExceptionHandler", mod.type, Sections ); - if (!pAddHandler) - return pAddHandler.status; - auto a = AsmFactory::GetAssembler( mod.type ); a->GenPrologue(); - a->GenCall( pAddHandler->procAddress, { 0, _pVEHCode.ptr() } ); - proc.remote().AddReturnWithEvent( *a, mod.type ); + a->GenCall( pAddHandler.procAddress, { 0, _pVEHCode.ptr() } ); + proc->remote().AddReturnWithEvent( *a, mod.type ); a->GenEpilogue(); - NTSTATUS status = proc.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result ); - if (result != 0) - { - _hVEH = result; - return STATUS_SUCCESS; - } - else - { - status = proc.remote().GetLastStatus(); - return status; - } + auto result = proc->remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize() ); + if (result == 0) + THROW_WITH_STATUS_AND_LOG( proc->remote().GetLastStatus(), "RtlAddVectoredExceptionHandler failed" ); + + _pVEHCode.Release(); + _hVEH = result; } /// @@ -327,36 +301,28 @@ NTSTATUS MExcept::CreateVEH( Process& proc, ModuleData& mod, bool partial ) /// Target process /// Partial exception support /// Module type -/// Status code -NTSTATUS MExcept::RemoveVEH( Process& proc, bool partial, eModType mt ) +void MExcept::RemoveVEH( Process* proc, bool partial, eModType mt ) { auto a = AsmFactory::GetAssembler( mt ); - uint64_t result = 0; - - auto& mods = proc.modules(); + auto& mods = proc->modules(); auto pRemoveHandler = mods.GetNtdllExport( "RtlRemoveVectoredExceptionHandler", mt ); - if (!pRemoveHandler) - return pRemoveHandler.status; a->GenPrologue(); // RemoveVectoredExceptionHandler(pHandler); - a->GenCall( pRemoveHandler->procAddress, { _hVEH } ); + a->GenCall( pRemoveHandler.procAddress, { _hVEH } ); - proc.remote().AddReturnWithEvent( *a ); + proc->remote().AddReturnWithEvent( *a ); a->GenEpilogue(); // Destroy table and handler if (!partial) { - proc.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result ); + proc->remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize() ); + _pModTable.Free(); _pVEHCode.Free(); _hVEH = 0; - - _pModTable.Free(); } - - return STATUS_SUCCESS; } } diff --git a/src/BlackBone/ManualMap/MExcept.h b/src/BlackBone/ManualMap/MExcept.h index 8c59c3d5..2cc312c6 100644 --- a/src/BlackBone/ManualMap/MExcept.h +++ b/src/BlackBone/ManualMap/MExcept.h @@ -32,7 +32,6 @@ class MExcept { public: BLACKBONE_API MExcept() = default; - BLACKBONE_API ~MExcept() = default; MExcept( const MExcept& ) = delete; MExcept& operator =( const MExcept& ) = delete; @@ -44,8 +43,7 @@ class MExcept /// Target process /// Target module /// Partial exception support - /// Error code - BLACKBONE_API NTSTATUS CreateVEH( class Process& proc, ModuleData& mod, bool partial ); + BLACKBONE_API void CreateVEH( class Process* proc, ModuleData& mod, bool partial ); /// /// Removes VEH from target process @@ -53,8 +51,7 @@ class MExcept /// Target process /// Partial exception support /// Module type - /// Status code - BLACKBONE_API NTSTATUS RemoveVEH( class Process& proc, bool partial, eModType mt ); + BLACKBONE_API void RemoveVEH( class Process* proc, bool partial, eModType mt ); /// /// Reset data @@ -62,9 +59,9 @@ class MExcept BLACKBONE_API void reset() { _pModTable.Free(); } private: - MemBlock _pVEHCode; // VEH function codecave - MemBlock _pModTable; // x64 module address range table - uint64_t _hVEH = 0; // VEH handle + MemBlock _pVEHCode; // VEH function codecave + MemBlock _pModTable; // x64 module address range table + uint64_t _hVEH = 0; // VEH handle static uint8_t _handler32[]; static uint8_t _handler64[]; diff --git a/src/BlackBone/ManualMap/MMap.cpp b/src/BlackBone/ManualMap/MMap.cpp index 64e039c4..e35b1f40 100644 --- a/src/BlackBone/ManualMap/MMap.cpp +++ b/src/BlackBone/ManualMap/MMap.cpp @@ -1,5 +1,7 @@ #include "MMap.h" #include "../Process/Process.h" +#include "../Include/Exception.h" +#include "../Include/ScopeExit.h" #include "../Misc/NameResolve.h" #include "../Misc/Utils.h" #include "../Misc/DynImport.h" @@ -16,16 +18,11 @@ namespace blackbone { -MMap::MMap( Process& proc ) +MMap::MMap( Process* proc ) : _process( proc ) { } -MMap::~MMap(void) -{ - //UnmapAllModules(); -} - /// /// Manually map PE image into underlying target process /// @@ -34,15 +31,23 @@ MMap::~MMap(void) /// Mapping callback. Triggers for each mapped module /// User-supplied callback context /// Mapped image info -call_result_t MMap::MapImage( +ModuleDataPtr MMap::MapImage( const std::wstring& path, eLoadFlags flags /*= NoFlags*/, MapCallback mapCallback /*= nullptr*/, void* context /*= nullptr*/, CustomArgs_t* pCustomArgs /*= nullptr*/ - ) +) { - return MapImageInternal( path, nullptr, 0, false, flags, mapCallback, context, pCustomArgs ); + try + { + return MapImageInternal( path, nullptr, 0, false, flags, mapCallback, context, pCustomArgs ); + } + catch (const std::exception&) + { + Cleanup(); + throw; + } } /// @@ -55,20 +60,28 @@ call_result_t MMap::MapImage( /// Mapping callback. Triggers for each mapped module /// User-supplied callback context /// Mapped image info -call_result_t MMap::MapImage( +ModuleDataPtr MMap::MapImage( size_t size, void* buffer, bool asImage /*= false*/, eLoadFlags flags /*= NoFlags*/, MapCallback mapCallback /*= nullptr*/, void* context /*= nullptr*/, CustomArgs_t* pCustomArgs /*= nullptr*/ - ) +) { // Create fake path wchar_t path[64]; wsprintfW( path, L"MemoryImage_0x%p", buffer ); - return MapImageInternal( path, buffer, size, asImage, flags, mapCallback, context, pCustomArgs ); + try + { + return MapImageInternal( path, buffer, size, asImage, flags, mapCallback, context, pCustomArgs ); + } + catch (const std::exception&) + { + Cleanup(); + throw; + } } /// @@ -82,7 +95,7 @@ call_result_t MMap::MapImage( /// Mapping callback. Triggers for each mapped module /// User-supplied callback context /// Mapped image info -call_result_t MMap::MapImageInternal( +ModuleDataPtr MMap::MapImageInternal( const std::wstring& path, void* buffer, size_t size, bool asImage /*= false*/, @@ -90,30 +103,25 @@ call_result_t MMap::MapImageInternal( MapCallback mapCallback /*= nullptr*/, void* context /*= nullptr*/, CustomArgs_t* pCustomArgs /*= nullptr*/ - ) +) { if (!(flags & ForceRemap)) { // Already loaded - if (auto hMod = _process.modules().GetModule(path)) + if (auto hMod = _process->modules().GetModule( path )) return hMod; } // Prepare target process auto mode = (flags & NoThreads) ? Worker_UseExisting : Worker_CreateNew; - auto status = _process.remote().CreateRPCEnvironment( mode, true ); - if (!NT_SUCCESS( status )) - { - Cleanup(); - return status; - } + _process->remote().CreateRPCEnvironment( mode, true ); // No need to support exceptions if DEP is disabled - if (_process.core().DEP() == false) + if (_process->core().DEP() == false) flags |= NoExceptions; // Ignore MapInHighMem for native x64 process - if (!_process.core().isWow64()) + if (!_process->core().isWow64()) flags &= ~MapInHighMem; // Set native loader callback @@ -124,60 +132,56 @@ call_result_t MMap::MapImageInternal( // Map module and all dependencies auto mod = FindOrMapModule( path, buffer, size, asImage, flags ); - if (!mod) - { - Cleanup(); - return mod.status; - } // Change process base module address if needed if (flags & RebaseProcess && !_images.empty() && _images.rbegin()->get()->peImage.isExe()) { - BLACKBONE_TRACE( L"ManualMap: Rebasing process to address 0x%p", (*mod)->baseAddress ); + BLACKBONE_TRACE( L"ManualMap: Rebasing process to address 0x%p", mod->baseAddress ); // Managed path fix if (_images.rbegin()->get()->peImage.pureIL() && !path.empty()) { CALL_64_86( - (*mod)->type == mt_mod64, - FixManagedPath, - _process.modules().GetMainModule()->baseAddress, - path + mod->type == mt_mod64, + FixManagedPath, + _process->modules().GetMainModule()->baseAddress, + path ); } // PEB64 - _process.memory().Write( - fieldPtr( _process.core().peb64(), &_PEB64::ImageBaseAddress ), - sizeof( uint64_t ), - &(*mod)->baseAddress + _process->memory().Write( + fieldPtr( _process->core().peb64(), &_PEB64::ImageBaseAddress ), + sizeof( uint64_t ), + &mod->baseAddress ); // PEB32 - if(_process.core().isWow64()) + if (_process->core().isWow64()) { - _process.memory().Write( - fieldPtr(_process.core().peb32(), &_PEB32::ImageBaseAddress ), - sizeof( uint32_t ), - &(*mod)->baseAddress + _process->memory().Write( + fieldPtr( _process->core().peb32(), &_PEB32::ImageBaseAddress ), + sizeof( uint32_t ), + &mod->baseAddress ); } } - auto wipeMemory = []( Process& proc, ImageContext* img, uintptr_t offset, uintptr_t size ) + auto wipeMemory = []( Process* proc, ImageContext* img, uintptr_t offset, uintptr_t size ) { size = Align( size, 0x1000 ); std::unique_ptr zeroBuf( new uint8_t[size]() ); if (img->flags & HideVAD) { - Driver().WriteMem( proc.pid(), img->imgMem.ptr() + offset, size, zeroBuf.get() ); + Driver().WriteMem( proc->pid(), img->imgMem.ptr() + offset, size, zeroBuf.get() ); } else { + proc->memory().Protect( img->imgMem.ptr() + offset, size, PAGE_EXECUTE_READWRITE ); img->imgMem.Write( offset, size, zeroBuf.get() ); - proc.memory().Protect( img->imgMem.ptr() + offset, size, PAGE_NOACCESS ); - proc.memory().Free( img->imgMem.ptr() + offset, size, MEM_DECOMMIT ); + proc->memory().Protect( img->imgMem.ptr() + offset, size, PAGE_NOACCESS ); + proc->memory().Free( img->imgMem.ptr() + offset, size, MEM_DECOMMIT ); } }; @@ -199,21 +203,14 @@ call_result_t MMap::MapImageInternal( // Don't run initializer for pure IL dlls if (!img->peImage.pureIL() || img->peImage.isExe()) - status = RunModuleInitializers( img, DLL_PROCESS_ATTACH, pCustomArgs ).status; - - if (!NT_SUCCESS( status )) - { - BLACKBONE_TRACE( L"ManualMap: ModuleInitializers failed for '%ls', status: 0x%X", img->ldrEntry.name.c_str(), status ); - Cleanup(); - return status; - } + RunModuleInitializers( img, DLL_PROCESS_ATTACH, pCustomArgs ); // Wipe header if (img->flags & WipeHeader) wipeMemory( _process, img.get(), 0, img->peImage.headersSize() ); // Wipe discardable sections - if(!img->peImage.pureIL()) + if (!img->peImage.pureIL()) { for (auto& sec : img->peImage.sections()) if (sec.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) @@ -224,7 +221,6 @@ call_result_t MMap::MapImageInternal( } } - //Cleanup(); return mod; } @@ -236,30 +232,27 @@ call_result_t MMap::MapImageInternal( template void MMap::FixManagedPath( ptr_t base, const std::wstring &path ) { - _PEB_T peb = { 0 }; - _PEB_LDR_DATA2_T ldr = { 0 }; - - if (_process.core().peb( &peb ) != 0 && _process.memory().Read( peb.Ldr, sizeof( ldr ), &ldr ) == STATUS_SUCCESS) - { - // Get PEB loader entry - for (auto head = ldr.InLoadOrderModuleList.Flink; - head != fieldPtr( peb.Ldr, &_PEB_LDR_DATA2_T::InLoadOrderModuleList ); - head = _process.memory().Read( head ).result( 0 )) + _PEB_T peb = { }; + if (_process->core().peb( &peb ) == 0) + THROW_AND_LOG( "failed to get PEB base" ); + + // Get PEB loader entry + auto ldr = _process->memory().Read<_PEB_LDR_DATA2_T>( peb.Ldr ); + for (auto head = ldr.InLoadOrderModuleList.Flink; + head != fieldPtr( peb.Ldr, &_PEB_LDR_DATA2_T::InLoadOrderModuleList ); + head = _process->memory().Read( head )) + { + auto localdata = _process->memory().Read<_LDR_DATA_TABLE_ENTRY_BASE_T >( head ); + if (localdata.DllBase == base) { - _LDR_DATA_TABLE_ENTRY_BASE_T localdata = { { 0 } }; + auto len = path.length() * sizeof( wchar_t ); + _process->memory().Write( localdata.FullDllName.Buffer, len + 2, path.c_str() ); + _process->memory().Write( + head + FIELD_OFFSET( _LDR_DATA_TABLE_ENTRY_BASE_T, FullDllName.Length ), + static_cast(len) + ); - _process.memory().Read( head, sizeof( localdata ), &localdata ); - if (localdata.DllBase == base) - { - auto len = path.length()* sizeof( wchar_t ); - _process.memory().Write( localdata.FullDllName.Buffer, len + 2, path.c_str() ); - _process.memory().Write( - head + FIELD_OFFSET( _LDR_DATA_TABLE_ENTRY_BASE_T, FullDllName.Length ), - static_cast(len) - ); - - return; - } + return; } } } @@ -273,14 +266,9 @@ void MMap::FixManagedPath( ptr_t base, const std::wstring &path ) /// If set to true - buffer has image memory layout /// Mapping flags /// Module info -call_result_t MMap::FindOrMapModule( - const std::wstring& path, - void* buffer, size_t size, bool asImage, - eLoadFlags flags /*= NoFlags*/ - ) +ModuleDataPtr MMap::FindOrMapModule( const std::wstring& path, void* buffer, size_t size, bool asImage, eLoadFlags flags /*= NoFlags*/ ) { - NTSTATUS status = STATUS_SUCCESS; - ImageContextPtr pImage( new ImageContext() ); + auto pImage = std::make_shared(); auto& ldrEntry = pImage->ldrEntry; ldrEntry.fullPath = Utils::ToLower( path ); @@ -288,31 +276,25 @@ call_result_t MMap::FindOrMapModule( pImage->flags = flags; // Load and parse image - status = buffer ? pImage->peImage.Load( buffer, size, !asImage ) : pImage->peImage.Load( path, flags & NoSxS ? true : false ); - if (!NT_SUCCESS( status )) + NTSTATUS status = buffer ? pImage->peImage.Load( buffer, size, !asImage ) : pImage->peImage.Load( path, flags & NoSxS ? true : false ); + THROW_ON_FAIL_AND_LOG( status, "failed to load image '%ls'", path.c_str() ); + + // Unload local copy + ON_SCOPE_EXIT { - BLACKBONE_TRACE( L"ManualMap: Failed to load image '%ls'/0x%p. Status 0x%X", path.c_str(), buffer, status ); pImage->peImage.Release(); - return status; - } + }; - // Check if already loaded, but only if doesn't explicitly excluded + // Return existing module if found and not explicitly stated to always map new image if (!(flags & ForceRemap)) { - if (auto hMod = _process.modules().GetModule( path, LdrList, pImage->peImage.mType() )) - { - pImage->peImage.Release(); + if (auto hMod = _process->modules().GetModule( path, LdrList, pImage->peImage.mType() )) return hMod; - } } // Check architecture - if (pImage->peImage.mType() == mt_mod32 && !_process.core().isWow64()) - { - BLACKBONE_TRACE( L"ManualMap: Can't map x86 dll '%ls' into native x64 process", path.c_str() ); - pImage->peImage.Release(); - return STATUS_INVALID_IMAGE_WIN_32; - } + if (pImage->peImage.mType() == mt_mod32 && !_process->core().isWow64()) + THROW_AND_LOG( "can't map x86 image '%ls' into x64 process", path.c_str() ); BLACKBONE_TRACE( L"ManualMap: Loading new image '%ls'", path.c_str() ); @@ -325,200 +307,135 @@ call_result_t MMap::FindOrMapModule( } // Try to map image at it's original ASRL-aware base else if (flags & HideVAD) - { - ptr_t base = pImage->peImage.imageBase(); + { + ptr_t base = pImage->peImage.imageBase(); ptr_t image_size = pImage->peImage.imageSize(); - if (!NT_SUCCESS( Driver().EnsureLoaded() )) - { - pImage->peImage.Release(); - return Driver().status(); - } + status = Driver().EnsureLoaded(); + THROW_ON_FAIL_AND_LOG( status, "failed to load driver" ); // Allocate as physical at desired base - status = Driver().AllocateMem( _process.pid(), base, image_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE, true ); + status = Driver().AllocateMem( _process->pid(), base, image_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE, true ); // Allocate at any base if (!NT_SUCCESS( status )) { base = 0; image_size = pImage->peImage.imageSize(); - status = Driver().AllocateMem( _process.pid(), base, image_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE, true ); + status = Driver().AllocateMem( _process->pid(), base, image_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE, true ); } - // Store allocated region - if (NT_SUCCESS( status )) - { - pImage->imgMem = MemBlock( &_process.memory(), base, static_cast(image_size), PAGE_EXECUTE_READWRITE, true, true ); - } // Stop mapping - else - { - //flags &= ~HideVAD; - BLACKBONE_TRACE( L"ManualMap: Failed to allocate physical memory for image, status 0x%X", status ); - pImage->peImage.Release(); - return status; - } + THROW_ON_FAIL_AND_LOG( status, "failed to allocate physical memory for '%ls'", ldrEntry.name.c_str() ); + + // Store allocated region + pImage->imgMem = MemBlock( &_process->memory(), base, static_cast(image_size), PAGE_EXECUTE_READWRITE, true, true ); } // Allocate normally if something went wrong if (!pImage->imgMem.valid()) - { - auto mem = _process.memory().Allocate( pImage->peImage.imageSize(), PAGE_EXECUTE_READWRITE, pImage->peImage.imageBase() ); - if (!mem) - { - BLACKBONE_TRACE( L"ManualMap: Failed to allocate memory for image, status 0x%X", status ); - pImage->peImage.Release(); - return mem.status; - } - - pImage->imgMem = std::move( mem.result() ); - } + pImage->imgMem = _process->memory().Allocate( pImage->peImage.imageSize(), PAGE_EXECUTE_READWRITE, pImage->peImage.imageBase() ); ldrEntry.baseAddress = pImage->imgMem.ptr(); ldrEntry.size = pImage->peImage.imageSize(); - BLACKBONE_TRACE( L"ManualMap: Image base allocated at 0x%016llx", pImage->imgMem.ptr() ); + BLACKBONE_TRACE( L"ManualMap: '%ls' base allocated at 0x%016llx", ldrEntry.name.c_str(), pImage->imgMem.ptr() ); // Create Activation context for SxS if (pImage->peImage.manifestID() == 0) flags |= NoSxS; if (!(flags & NoSxS)) - { - status = CreateActx( pImage->peImage ); - if (!NT_SUCCESS( status )) - { - pImage->peImage.Release(); - return status; - } - } + CreateActx( pImage->peImage ); // Core image mapping operations - if (!NT_SUCCESS( status = CopyImage( pImage ) )) - { - pImage->peImage.Release(); - return status; - } + CopyImage( pImage ); + RelocateImage( pImage ); - if (!NT_SUCCESS( status = RelocateImage( pImage ) )) - { - pImage->peImage.Release(); - return status; - } - - auto mt = ldrEntry.type; - ModuleDataPtr pMod; + ModuleDataPtr pMod; if (flags & ForceRemap) - pMod = std::make_shared( _process.modules().Canonicalize( ldrEntry, true ) ); + pMod = std::make_shared( _process->modules().Canonicalize( ldrEntry, true ) ); else - pMod = _process.modules().AddManualModule( ldrEntry ); + pMod = _process->modules().AddManualModule( ldrEntry ); - { - // Handle x64 system32 dlls for wow64 process - bool fsRedirect = !(flags & IsDependency) && mt == mt_mod64 && _process.barrier().sourceWow64; + try + { + { + // Handle x64 system32 dlls for wow64 process + bool fsRedirect = !(flags & IsDependency) && ldrEntry.type == mt_mod64 && _process->barrier().sourceWow64; - FsRedirector fsr( fsRedirect ); + FsRedirector fsr( fsRedirect ); - // Import - if (!NT_SUCCESS( status = ResolveImport( pImage ) )) - { - pImage->peImage.Release(); - _process.modules().RemoveManualModule( ldrEntry.name, mt ); - return status; + ResolveImport( pImage ); + if (!(flags & NoDelayLoad)) + ResolveImport( pImage, true ); } - // Delayed import - if (!(flags & NoDelayLoad) && !NT_SUCCESS( status = ResolveImport( pImage, true ) )) - { - pImage->peImage.Release(); - _process.modules().RemoveManualModule( ldrEntry.name, mt ); - return status; - } - } + // Apply proper memory protection for sections + if (!(flags & HideVAD)) + ProtectImageMemory( pImage ); - // Apply proper memory protection for sections - if (!(flags & HideVAD)) - ProtectImageMemory( pImage ); + // Make exception handling possible (C and C++) + if (!(flags & NoExceptions)) + EnableExceptions( pImage ); - // Make exception handling possible (C and C++) - if (!(flags & NoExceptions)) - { - if (!NT_SUCCESS( status = EnableExceptions( pImage ) ) && status != STATUS_NOT_FOUND) - { - BLACKBONE_TRACE( L"ManualMap: Failed to enable exception handling for image %ls", ldrEntry.name.c_str() ); - pImage->peImage.Release(); - _process.modules().RemoveManualModule( ldrEntry.name, mt ); - return status; - } - } + // Initialize security cookie + InitializeCookie( pImage ); - // Initialize security cookie - if (!NT_SUCCESS ( status = InitializeCookie( pImage ) )) - { - BLACKBONE_TRACE( L"ManualMap: Failed to initialize cookie for image %ls", ldrEntry.name.c_str() ); - pImage->peImage.Release(); - _process.modules().RemoveManualModule( ldrEntry.name, mt ); - return status; - } - - // Unlink image from VAD list - if (flags & HideVAD && !NT_SUCCESS( status = ConcealVad( pImage->imgMem ) )) - { - pImage->peImage.Release(); - _process.modules().RemoveManualModule( ldrEntry.name, mt ); - return status; - } + // Unlink image from VAD list + if (flags & HideVAD) + ConcealVad( pImage ); - // Get entry point - pImage->ldrEntry.entryPoint = pImage->peImage.entryPoint( pImage->imgMem.ptr() ); + // Get entry point + pImage->ldrEntry.entryPoint = pImage->peImage.entryPoint( pImage->imgMem.ptr() ); - // Create reference for native loader functions - pImage->ldrEntry.flags = flags & CreateLdrRef ? Ldr_All : Ldr_None; - if (_mapCallback != nullptr) - { - auto mapData = _mapCallback( PostCallback, _userContext, _process, *pMod ); - if(mapData.ldrFlags != Ldr_Ignore) - pImage->ldrEntry.flags = mapData.ldrFlags; - } + // Create reference for native loader functions + pImage->ldrEntry.flags = flags & CreateLdrRef ? Ldr_All : Ldr_None; + if (_mapCallback != nullptr) + { + auto mapData = _mapCallback( PostCallback, _userContext, *_process, *pMod ); + if (mapData.ldrFlags != Ldr_Ignore) + pImage->ldrEntry.flags = mapData.ldrFlags; + } - if (pImage->ldrEntry.flags != Ldr_None) - { - if (!_process.nativeLdr().CreateNTReference( pImage->ldrEntry )) + if (pImage->ldrEntry.flags != Ldr_None) { - BLACKBONE_TRACE( L"ManualMap: Failed to add loader reference for image %ls", ldrEntry.name.c_str() ); + try + { + _process->nativeLdr().CreateNTReference( pImage->ldrEntry ); + } + catch (const nt_exception& ex) + { + BLACKBONE_TRACE( L"ManualMap: Failed to add loader reference for image %ls: ", ldrEntry.name.c_str(), ex.what() ); + } } - } - // Static TLS data - if (!(flags & NoTLS) && !NT_SUCCESS( status = InitStaticTLS( pImage ) )) + // Static TLS data + if (!(flags & NoTLS)) + InitStaticTLS( pImage ); + } + catch (const std::exception&) { - BLACKBONE_TRACE( L"ManualMap: Failed to initialize static TLS for image %ls, status 0x%X", ldrEntry.name.c_str(), status ); - pImage->peImage.Release(); - _process.modules().RemoveManualModule( ldrEntry.name, mt ); - return status; + _process->modules().RemoveManualModule( ldrEntry.name, ldrEntry.type ); + throw; } - + // Fill TLS callbacks pImage->peImage.GetTLSCallbacks( pImage->imgMem.ptr(), pImage->tlsCallbacks ); - // Unload local copy - pImage->peImage.Release(); - // Release ownership of image memory block pImage->imgMem.Release(); // Store image - _images.emplace_back( std::move( pImage ) ); + _images.emplace_back( pImage ); return pMod; } /// /// Unmap all manually mapped modules /// -/// Status code -NTSTATUS MMap::UnmapAllModules() +void MMap::UnmapAllModules() { for (auto img = _images.rbegin(); img != _images.rend(); ++img) { @@ -532,29 +449,27 @@ NTSTATUS MMap::UnmapAllModules() if (!(pImage->flags & NoExceptions)) DisableExceptions( pImage ); - _process.nativeLdr().UnloadTLS( pImage->ldrEntry ); + _process->nativeLdr().UnloadTLS( pImage->ldrEntry ); // Remove from loader if (pImage->ldrEntry.flags != Ldr_None) - _process.nativeLdr().Unlink( pImage->ldrEntry ); + _process->nativeLdr().Unlink( pImage->ldrEntry ); // Free memory pImage->imgMem.Free(); // Remove reference from local modules list - _process.modules().RemoveManualModule( pImage->ldrEntry.name, pImage->peImage.mType() ); - } + _process->modules().RemoveManualModule( pImage->ldrEntry.name, pImage->peImage.mType() ); + } Cleanup(); - return STATUS_SUCCESS; } /// /// Copies image into target process /// /// Image data -/// Status code -NTSTATUS MMap::CopyImage( ImageContextPtr pImage ) +void MMap::CopyImage( ImageContextPtr pImage ) { NTSTATUS status = STATUS_SUCCESS; @@ -565,25 +480,18 @@ NTSTATUS MMap::CopyImage( ImageContextPtr pImage ) // Copy header if (pImage->flags & HideVAD) - status = Driver().WriteMem( _process.pid(), pImage->imgMem.ptr(), dwHeaderSize, pImage->peImage.base() ); - else - status = pImage->imgMem.Write( 0, dwHeaderSize, pImage->peImage.base() ); - - if (!NT_SUCCESS( status )) { - BLACKBONE_TRACE( L"ManualMap: Failed to copy image headers. Status = 0x%x", status ); - return status; + status = Driver().WriteMem( _process->pid(), pImage->imgMem.ptr(), dwHeaderSize, pImage->peImage.base() ); + THROW_ON_FAIL_AND_LOG( status, "failed to copy image '%ls' headers", pImage->ldrEntry.name.c_str() ); } + else + pImage->imgMem.Write( 0, dwHeaderSize, pImage->peImage.base() ); // Set header protection if (!(pImage->flags & HideVAD)) { status = pImage->imgMem.Protect( PAGE_READONLY, 0, dwHeaderSize ); - if (!NT_SUCCESS( status )) - { - BLACKBONE_TRACE( L"ManualMap: Failed to set header memory protection. Status = 0x%x", status ); - return status; - } + THROW_ON_FAIL_AND_LOG( status, "failed to set image '%ls' header memory protection", pImage->ldrEntry.name.c_str() ); } // Copy sections @@ -596,102 +504,72 @@ NTSTATUS MMap::CopyImage( ImageContextPtr pImage ) continue; uint8_t* pSource = reinterpret_cast(pImage->peImage.ResolveRVAToVA( section.VirtualAddress )); - + // Copy section data if (pImage->flags & HideVAD) { status = Driver().WriteMem( - _process.pid(), pImage->imgMem.ptr() + section.VirtualAddress, + _process->pid(), pImage->imgMem.ptr() + section.VirtualAddress, section.SizeOfRawData, pSource - ); - } - else - { - status = pImage->imgMem.Write( section.VirtualAddress, section.SizeOfRawData, pSource ); - } - - if (!NT_SUCCESS( status )) - { - BLACKBONE_TRACE( - L"ManualMap: Failed to copy image section at offset 0x%x. Status = 0x%x", - section.VirtualAddress, status - ); + ); - return status; + THROW_ON_FAIL_AND_LOG( status, + "failed to copy image '%ls' section at offset 0x%x", + pImage->ldrEntry.name.c_str(), section.VirtualAddress + ); } - } - } - return STATUS_SUCCESS; + pImage->imgMem.Write( section.VirtualAddress, section.SizeOfRawData, pSource ); + } + } } /// /// Adjust image memory protection /// /// image data -/// Status code -NTSTATUS MMap::ProtectImageMemory( ImageContextPtr pImage ) +void MMap::ProtectImageMemory( ImageContextPtr pImage ) { // Set section memory protection for (auto& section : pImage->peImage.sections()) { auto prot = GetSectionProt( section.Characteristics ); - if (prot != PAGE_NOACCESS) - { - auto status = pImage->imgMem.Protect( prot, section.VirtualAddress, section.Misc.VirtualSize ); - if (!NT_SUCCESS( status )) - { - BLACKBONE_TRACE( - L"ManualMap: Failed to set section memory protection at offset 0x%x. Status = 0x%x", - section.VirtualAddress, status - ); + NTSTATUS status = STATUS_SUCCESS; - return status; - } - } - // Decommit pages with NO_ACCESS protection + if (prot != PAGE_NOACCESS) + status = pImage->imgMem.Protect( prot, section.VirtualAddress, section.Misc.VirtualSize ); else - { - auto status = _process.memory().Free( pImage->imgMem.ptr() + section.VirtualAddress, section.Misc.VirtualSize, MEM_DECOMMIT ); - if (!NT_SUCCESS( status )) - { - BLACKBONE_TRACE( - L"ManualMap: Failed to set section memory protection at offset 0x%x. Status = 0x%x", - section.VirtualAddress, status - ); - } - } - } + // Decommit pages with NO_ACCESS protection + status = _process->memory().Free( pImage->imgMem.ptr() + section.VirtualAddress, section.Misc.VirtualSize, MEM_DECOMMIT ); - return STATUS_SUCCESS; + THROW_ON_FAIL_AND_LOG( status, + "failed to set image '%ls' section memory protection at offset 0x%x", + pImage->ldrEntry.name.c_str(), section.VirtualAddress + ); + } } /// -/// Fix relocations if image wasn't loaded at base address +/// Fix relocations if image wasn't loaded at base address /// /// image data -/// true on success -NTSTATUS MMap::RelocateImage( ImageContextPtr pImage ) +void MMap::RelocateImage( ImageContextPtr pImage ) { - NTSTATUS status = STATUS_SUCCESS; BLACKBONE_TRACE( L"ManualMap: Relocating image '%ls'", pImage->ldrEntry.fullPath.c_str() ); - // Reloc delta + // Relocation delta ptr_t Delta = pImage->imgMem.ptr() - pImage->peImage.imageBase(); // No need to relocate if (Delta == 0) { BLACKBONE_TRACE( L"ManualMap: No need for relocation" ); - return STATUS_SUCCESS; + return; } // Dll can't be relocated if (!(pImage->peImage.DllCharacteristics() & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE)) - { - BLACKBONE_TRACE( L"ManualMap: Can't relocate image, no relocation flag" ); - return STATUS_INVALID_IMAGE_HASH; - } + THROW_AND_LOG( "can't relocate image '%ls', no relocation flag", pImage->ldrEntry.name.c_str() ); auto start = pImage->peImage.DirectoryAddress( IMAGE_DIRECTORY_ENTRY_BASERELOC ); auto end = start + pImage->peImage.DirectorySize( IMAGE_DIRECTORY_ENTRY_BASERELOC ); @@ -701,13 +579,13 @@ NTSTATUS MMap::RelocateImage( ImageContextPtr pImage ) if (fixrec == nullptr) { BLACKBONE_TRACE( L"ManualMap: Image does not use relocations" ); - return STATUS_SUCCESS; + return; } // Read whole image to process it locally std::unique_ptr localImage( new uint8_t[pImage->ldrEntry.size] ); auto pLocal = localImage.get(); - _process.memory().Read( pImage->imgMem.ptr(), pImage->ldrEntry.size, pLocal ); + _process->memory().Read( pImage->imgMem.ptr(), pImage->ldrEntry.size, pLocal ); while ((uintptr_t)fixrec < end && fixrec->BlockSize) { @@ -740,27 +618,22 @@ NTSTATUS MMap::RelocateImage( ImageContextPtr pImage ) else { // TODO: support for all remaining relocations - BLACKBONE_TRACE( L"ManualMap: Abnormal relocation type %d. Aborting", fixtype ); - return STATUS_INVALID_IMAGE_FORMAT; + THROW_AND_LOG( "image '%ls': abnormal relocation type %d", pImage->ldrEntry.name.c_str(), fixtype ); } } - // next reloc entry + // next relocation entry fixrec = reinterpret_cast(reinterpret_cast(fixrec) + fixrec->BlockSize); } // Apply relocations, skip header if (pImage->flags & HideVAD) - status = Driver().WriteMem( _process.pid(), pImage->ldrEntry.baseAddress + 0x1000, pImage->ldrEntry.size - 0x1000, pLocal + 0x1000 ); - else - status = _process.memory().Write( pImage->ldrEntry.baseAddress + 0x1000, pImage->ldrEntry.size - 0x1000, pLocal + 0x1000 ); - - if (!NT_SUCCESS( status )) { - BLACKBONE_TRACE( L"ManualMap: Failed to apply relocations. Status = 0x%x", status ); + NTSTATUS status = Driver().WriteMem( _process->pid(), pImage->ldrEntry.baseAddress + 0x1000, pImage->ldrEntry.size - 0x1000, pLocal + 0x1000 ); + THROW_ON_FAIL_AND_LOG( status, "failed to apply relocations for image '%ls'", pImage->ldrEntry.name.c_str() ); } - - return status; + else + _process->memory().Write( pImage->ldrEntry.baseAddress + 0x1000, pImage->ldrEntry.size - 0x1000, pLocal + 0x1000 ); } /// @@ -769,10 +642,10 @@ NTSTATUS MMap::RelocateImage( ImageContextPtr pImage ) /// Currently napped image data /// Dependency path /// -call_result_t MMap::FindOrMapDependency( ImageContextPtr pImage, std::wstring& path ) +ModuleDataPtr MMap::FindOrMapDependency( ImageContextPtr pImage, std::wstring& path ) { // Already loaded - auto hMod = _process.modules().GetModule( path, LdrList, pImage->peImage.mType(), pImage->ldrEntry.fullPath.c_str() ); + auto hMod = _process->modules().GetModule( path, LdrList, pImage->peImage.mType(), pImage->ldrEntry.fullPath.c_str() ); if (hMod) return hMod; @@ -781,30 +654,24 @@ call_result_t MMap::FindOrMapDependency( ImageContextPtr pImage, auto flags = NameResolve::EnsureFullPath; // Wow64 fs redirection - if (pImage->ldrEntry.type == mt_mod32 && !_process.barrier().sourceWow64) + if (pImage->ldrEntry.type == mt_mod32 && !_process->barrier().sourceWow64) flags = static_cast(static_cast(flags) | NameResolve::Wow64); auto basedir = pImage->peImage.noPhysFile() ? Utils::GetExeDirectory() : Utils::GetParent( pImage->ldrEntry.fullPath ); - auto status = NameResolve::Instance().ResolvePath( + auto status = NameResolve::Instance().ResolvePath( path, - pImage->ldrEntry.name, - basedir, - flags, - _process, - pImage->peImage.actx() + pImage->ldrEntry.name, + basedir, + flags, + *_process, + pImage->peImage.actx() ); // Do remote SxS probe if (status == STATUS_SXS_IDENTITIES_DIFFERENT) - { - status = ProbeRemoteSxS( path ); - } + ProbeRemoteSxS( path ); - if (!NT_SUCCESS( status )) - { - BLACKBONE_TRACE( L"ManualMap: Failed to resolve dependency path '%ls', status 0x%x", path.c_str(), status ); - return status; - } + THROW_ON_FAIL_AND_LOG( status, "failed to resolve dependency path '%ls'", path.c_str() ); BLACKBONE_TRACE( L"ManualMap: Dependency path resolved to '%ls'", path.c_str() ); @@ -819,7 +686,7 @@ call_result_t MMap::FindOrMapDependency( ImageContextPtr pImage, tmpData.size = 0; tmpData.type = pImage->ldrEntry.type; - data = _mapCallback( PreCallback, _userContext, _process, tmpData ); + data = _mapCallback( PreCallback, _userContext, *_process, tmpData ); } // Loading method @@ -829,13 +696,11 @@ call_result_t MMap::FindOrMapDependency( ImageContextPtr pImage, } else if (data.mtype != MT_None) { - return _process.modules().Inject( path ); + return _process->modules().Inject( path ); } + // Aborted by user - else - { - return STATUS_REQUEST_CANCELED; - } + return nullptr; }; /// @@ -843,17 +708,16 @@ call_result_t MMap::FindOrMapDependency( ImageContextPtr pImage, /// /// Image data /// Resolve delayed import instead -/// Status code -NTSTATUS MMap::ResolveImport( ImageContextPtr pImage, bool useDelayed /*= false */ ) +void MMap::ResolveImport( ImageContextPtr pImage, bool useDelayed /*= false */ ) { auto imports = pImage->peImage.GetImports( useDelayed ); if (imports.empty()) - return STATUS_SUCCESS; + return; // Read whole image to process it locally std::unique_ptr localImage( new uint8_t[pImage->ldrEntry.size] ); auto pLocal = localImage.get(); - _process.memory().Read( pImage->imgMem.ptr(), pImage->ldrEntry.size, pLocal ); + _process->memory().Read( pImage->imgMem.ptr(), pImage->ldrEntry.size, pLocal ); // Traverse entries for (auto& importMod : imports) @@ -862,173 +726,129 @@ NTSTATUS MMap::ResolveImport( ImageContextPtr pImage, bool useDelayed /*= false // Load dependency if needed auto hMod = FindOrMapDependency( pImage, wstrDll ); - if (!hMod) - { - BLACKBONE_TRACE( L"ManualMap: Failed to load dependency '%ls'. Status 0x%x", wstrDll.c_str(), hMod.status ); - return hMod.status; - } - for (auto& importFn : importMod.second) { - call_result_t expData; + exportData result; + + try + { + if (importFn.importByOrd) + result = _process->modules().GetExport( hMod, reinterpret_cast(importFn.importOrdinal) ); + else + result = _process->modules().GetExport( hMod, importFn.importName.c_str() ); + } + catch (const nt_exception&) + { + if (!useDelayed) + throw; + + continue; + } - if (importFn.importByOrd) - expData = _process.modules().GetExport( hMod.result(), reinterpret_cast(importFn.importOrdinal) ); - else - expData = _process.modules().GetExport( hMod.result(), importFn.importName.c_str() ); // Still forwarded, load missing modules - while (expData && expData->procAddress && expData->isForwarded) + while (result.procAddress && result.isForwarded) { - std::wstring wdllpath = expData->forwardModule; + std::wstring wdllpath = result.forwardModule; // Ensure module is loaded auto hFwdMod = FindOrMapDependency( pImage, wdllpath ); - if (!hFwdMod) - { - BLACKBONE_TRACE( L"ManualMap: Failed to load forwarded dependency '%ls'. Status 0x%x", wstrDll.c_str(), hFwdMod.status ); - return hFwdMod.status; - } - if (expData->forwardByOrd) - { - expData = _process.modules().GetExport( - hFwdMod.result(), - reinterpret_cast(expData->forwardOrdinal), - wdllpath.c_str() - ); - } + if (result.forwardByOrd) + result = _process->modules().GetExport( hFwdMod, reinterpret_cast(result.forwardOrdinal), wdllpath.c_str() ); else - expData = _process.modules().GetExport( hFwdMod.result(), expData->forwardName.c_str(), wdllpath.c_str() ); - } - - // Failed to resolve import - if (!expData) - { - if (importFn.importByOrd) - { - BLACKBONE_TRACE( - L"ManualMap: Failed to get import #%d from image '%ls'", - importFn.importOrdinal, - wstrDll.c_str() - ); - } - else - { - BLACKBONE_TRACE( - L"ManualMap: Failed to get import '%ls' from image '%ls'", - Utils::AnsiToWstring( importFn.importName ).c_str(), - wstrDll.c_str() - ); - } - - return expData.status; + result = _process->modules().GetExport( hFwdMod, result.forwardName.c_str(), wdllpath.c_str() ); } if (pImage->ldrEntry.type == mt_mod64) - *reinterpret_cast(pLocal + importFn.ptrRVA) = expData->procAddress; + *reinterpret_cast(pLocal + importFn.ptrRVA) = result.procAddress; else - *reinterpret_cast(pLocal + importFn.ptrRVA) = static_cast(expData->procAddress); + *reinterpret_cast(pLocal + importFn.ptrRVA) = static_cast(result.procAddress); } } - auto status = STATUS_SUCCESS; - // Apply imports, skip header if (pImage->flags & HideVAD) { - status = Driver().WriteMem( - _process.pid(), + NTSTATUS status = Driver().WriteMem( + _process->pid(), pImage->ldrEntry.baseAddress + 0x1000, pImage->ldrEntry.size - 0x1000, pLocal + 0x1000 ); + + THROW_ON_FAIL_AND_LOG( status, "failed to write modified image '%ls'", pImage->ldrEntry.name.c_str() ); } else { - status = _process.memory().Write( - pImage->ldrEntry.baseAddress + 0x1000, - pImage->ldrEntry.size - 0x1000, - pLocal + 0x1000 - ); - } - - // Write function address - if (!NT_SUCCESS( status )) - { - BLACKBONE_TRACE( L"ManualMap: Failed to write import function. Status 0x%x", status ); + _process->memory().Write( pImage->ldrEntry.baseAddress + 0x1000, pImage->ldrEntry.size - 0x1000, pLocal + 0x1000 ); } - - return status; } /// /// Set custom exception handler to bypass SafeSEH under DEP /// /// image data -/// true on success -NTSTATUS MMap::EnableExceptions( ImageContextPtr pImage ) +void MMap::EnableExceptions( ImageContextPtr pImage ) { BLACKBONE_TRACE( L"ManualMap: Enabling exception support for image '%ls'", pImage->ldrEntry.name.c_str() ); bool partial = (pImage->flags & PartialExcept) != 0; - bool success = _process.nativeLdr().InsertInvertedFunctionTable( pImage->ldrEntry ); + bool hasNative = false; - if (pImage->ldrEntry.type == mt_mod64) + try + { + _process->nativeLdr().InsertInvertedFunctionTable( pImage->ldrEntry ); + hasNative = true; + } + catch (const nt_exception& ex) + { + // Can't continue with x86 module + if (pImage->ldrEntry.type != mt_mod64) + THROW_AND_LOG( "failed to insert image '%ls' into inverted function table: ", pImage->ldrEntry.name.c_str(), ex.what() ); + } + + // Fallback to RtlAddFunctionTable + if (!hasNative) { - // Try RtlIsertInvertedTable - if (!success) + // Retry with documented method + auto expTableRVA = pImage->peImage.DirectoryAddress( IMAGE_DIRECTORY_ENTRY_EXCEPTION, pe::RVA ); + size_t size = pImage->peImage.DirectorySize( IMAGE_DIRECTORY_ENTRY_EXCEPTION ); + + // Invoke RtlAddFunctionTable + if (expTableRVA) { - // Retry with documented method - auto expTableRVA = pImage->peImage.DirectoryAddress( IMAGE_DIRECTORY_ENTRY_EXCEPTION, pe::RVA ); - size_t size = pImage->peImage.DirectorySize( IMAGE_DIRECTORY_ENTRY_EXCEPTION ); + auto a = AsmFactory::GetAssembler( pImage->ldrEntry.type ); - // Invoke RtlAddFunctionTable - if (expTableRVA) - { - auto a = AsmFactory::GetAssembler( pImage->ldrEntry.type ); - uint64_t result = 0; - - pImage->pExpTableAddr = expTableRVA + pImage->imgMem.ptr(); - auto pAddTable = _process.modules().GetNtdllExport( "RtlAddFunctionTable", pImage->ldrEntry.type ); - if (!pAddTable) - return pAddTable.status; - - a->GenPrologue(); - a->GenCall( - pAddTable->procAddress, { - pImage->pExpTableAddr, - size / sizeof( IMAGE_RUNTIME_FUNCTION_ENTRY ), - pImage->imgMem.ptr() } - ); + pImage->pExpTableAddr = expTableRVA + pImage->imgMem.ptr(); + auto pAddTable = _process->modules().GetNtdllExport( "RtlAddFunctionTable", pImage->ldrEntry.type ); - _process.remote().AddReturnWithEvent( *a, pImage->ldrEntry.type ); - a->GenEpilogue(); + a->GenPrologue(); + a->GenCall( + pAddTable.procAddress, { + pImage->pExpTableAddr, + size / sizeof( IMAGE_RUNTIME_FUNCTION_ENTRY ), + pImage->imgMem.ptr() } + ); - auto status = _process.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result ); - if (!NT_SUCCESS( status )) - return status; - } - // No exception table - else - return STATUS_NOT_FOUND; + _process->remote().AddReturnWithEvent( *a, pImage->ldrEntry.type ); + a->GenEpilogue(); + + _process->remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize() ); } } - else if (!success) - return STATUS_INVALID_EXCEPTION_HANDLER; // Custom handler not required - if (pImage->ldrEntry.safeSEH || (pImage->ldrEntry.type == mt_mod64 && (pImage->flags & CreateLdrRef || success))) - return STATUS_SUCCESS; + if (pImage->ldrEntry.safeSEH || (pImage->ldrEntry.type == mt_mod64 && (pImage->flags & CreateLdrRef || hasNative))) + return; - return _expMgr.CreateVEH( _process, pImage->ldrEntry, partial ); + _expMgr.CreateVEH( _process, pImage->ldrEntry, partial ); } /// /// Remove custom exception handler /// /// image data -/// true on success -NTSTATUS MMap::DisableExceptions( ImageContextPtr pImage ) +void MMap::DisableExceptions( ImageContextPtr pImage ) { BLACKBONE_TRACE( L"ManualMap: Disabling exception support for image '%ls'", pImage->ldrEntry.name.c_str() ); bool partial = false; @@ -1036,82 +856,73 @@ NTSTATUS MMap::DisableExceptions( ImageContextPtr pImage ) if (pImage->ldrEntry.type == mt_mod64) { if (!pImage->pExpTableAddr) - return STATUS_NOT_FOUND; + THROW_AND_LOG( "failed to disable exceptions for image '%ls': no exception table available", pImage->ldrEntry.name.c_str() ) auto a = AsmFactory::GetAssembler( pImage->ldrEntry.type ); - uint64_t result = 0; - - auto pRemoveTable = _process.modules().GetNtdllExport( "RtlDeleteFunctionTable", pImage->ldrEntry.type ); - if (!pRemoveTable) - return pRemoveTable.status; + auto pRemoveTable = _process->modules().GetNtdllExport( "RtlDeleteFunctionTable", pImage->ldrEntry.type ); a->GenPrologue(); // RtlDeleteFunctionTable(pExpTable); - a->GenCall(pRemoveTable->procAddress, { pImage->pExpTableAddr } ); - _process.remote().AddReturnWithEvent( *a ); + a->GenCall( pRemoveTable.procAddress, { pImage->pExpTableAddr } ); + _process->remote().AddReturnWithEvent( *a ); a->GenEpilogue(); - auto status = _process.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result ); - if (!NT_SUCCESS( status )) - return status; + _process->remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize() ); } partial = (pImage->flags & PartialExcept) != 0; - return _expMgr.RemoveVEH( _process, partial, pImage->peImage.mType() ); + _expMgr.RemoveVEH( _process, partial, pImage->peImage.mType() ); } /// /// Resolve static TLS storage /// /// image data -/// Status code -NTSTATUS MMap::InitStaticTLS( ImageContextPtr pImage ) +void MMap::InitStaticTLS( ImageContextPtr pImage ) { auto pTls = reinterpret_cast(pImage->peImage.DirectoryAddress( IMAGE_DIRECTORY_ENTRY_TLS )); + if (!pTls || !pTls->AddressOfIndex) + return; + auto rebasedTlsPtr = REBASE( pTls, pImage->peImage.base(), pImage->imgMem.ptr() ); - // Use native TLS initialization - if (pTls && pTls->AddressOfIndex) - { - BLACKBONE_TRACE( L"ManualMap: Performing static TLS initialization for image '%ls'", pImage->ldrEntry.name.c_str() ); - return _process.nativeLdr().AddStaticTLSEntry( pImage->ldrEntry, rebasedTlsPtr ); - } + BLACKBONE_TRACE( L"ManualMap: Performing static TLS initialization for image '%ls'", pImage->ldrEntry.name.c_str() ); - return STATUS_SUCCESS; + // Use native TLS initialization + _process->nativeLdr().AddStaticTLSEntry( pImage->ldrEntry, rebasedTlsPtr ); } /// /// Calculate and set security cookie /// /// image data -/// Status code -NTSTATUS MMap::InitializeCookie( ImageContextPtr pImage ) +void MMap::InitializeCookie( ImageContextPtr pImage ) { auto pLoadConfig32 = reinterpret_cast(pImage->peImage.DirectoryAddress( IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG )); auto pLoadConfig64 = reinterpret_cast(pLoadConfig32); if (!pLoadConfig32) - return STATUS_SUCCESS; + return; ptr_t pCookie = pLoadConfig32->SecurityCookie; if (pImage->ldrEntry.type == mt_mod64) pCookie = pLoadConfig64->SecurityCookie; if (!pCookie) - return STATUS_SUCCESS; + return; // // Cookie generation based on MSVC++ compiler // - BLACKBONE_TRACE( L"ManualMap: Performing security cookie initializtion for image '%ls'", pImage->ldrEntry.name.c_str() ); + BLACKBONE_TRACE( L"ManualMap: Performing security cookie initialization for image '%ls'", pImage->ldrEntry.name.c_str() ); - FILETIME systime = { 0 }; - LARGE_INTEGER PerformanceCount = { { 0 } }; + FILETIME systime = { }; + LARGE_INTEGER PerformanceCount = { }; size_t size = sizeof( uint32_t ); GetSystemTimeAsFileTime( &systime ); QueryPerformanceCounter( &PerformanceCount ); - ptr_t cookie = _process.pid() ^ _process.remote().getExecThread()->id() ^ reinterpret_cast(&cookie); + ptr_t cookie = _process->pid() ^ _process->remote().getExecThread()->id() ^ reinterpret_cast(&cookie); if (pImage->ldrEntry.type == mt_mod64) { @@ -1137,7 +948,7 @@ NTSTATUS MMap::InitializeCookie( ImageContextPtr pImage ) cookie |= (cookie | 0x4711) << 16; } - return _process.memory().Write( REBASE( pCookie, pImage->peImage.imageBase(), pImage->imgMem.ptr() ), size, &cookie ); + _process->memory().Write( REBASE( pCookie, pImage->peImage.imageBase(), pImage->imgMem.ptr() ), size, &cookie ); } /// @@ -1151,36 +962,32 @@ NTSTATUS MMap::InitializeCookie( ImageContextPtr pImage ) /// DLL_THREAD_DETTACH /// /// DllMain result -call_result_t MMap::RunModuleInitializers( ImageContextPtr pImage, DWORD dwReason, CustomArgs_t* pCustomArgs /*= nullptr*/ ) +uint64_t MMap::RunModuleInitializers( ImageContextPtr pImage, DWORD dwReason, CustomArgs_t* pCustomArgs /*= nullptr*/ ) { auto a = AsmFactory::GetAssembler( pImage->ldrEntry.type ); - uint64_t result = 0; - auto hNtdll = _process.modules().GetModule( L"ntdll.dll", LdrList, pImage->peImage.mType() ); - auto pActivateActx = _process.modules().GetExport( hNtdll, "RtlActivateActivationContext" ); - auto pDeactivateActx = _process.modules().GetExport( hNtdll, "RtlDeactivateActivationContext" ); + auto hNtdll = _process->modules().GetModule( L"ntdll.dll", LdrList, pImage->peImage.mType() ); + auto pActivateActx = _process->modules().GetExport( hNtdll, "RtlActivateActivationContext" ); + auto pDeactivateActx = _process->modules().GetExport( hNtdll, "RtlDeactivateActivationContext" ); a->GenPrologue(); // ActivateActCtx - if (_pAContext.valid() && pActivateActx) + if (_pAContext.valid()) { (*a)->mov( (*a)->zax, _pAContext.ptr() ); (*a)->mov( (*a)->zax, asmjit::host::dword_ptr( (*a)->zax ) ); - a->GenCall( pActivateActx->procAddress, { 0, (*a)->zax, _pAContext.ptr() + sizeof( ptr_t ) } ); + a->GenCall( pActivateActx.procAddress, { 0, (*a)->zax, _pAContext.ptr() + sizeof( ptr_t ) } ); } // Prepare custom arguments ptr_t customArgumentsAddress = 0; if (pCustomArgs) { - auto memBuf = _process.memory().Allocate( pCustomArgs->size() + sizeof( uint64_t ), PAGE_EXECUTE_READWRITE, 0, false ); - if (!memBuf) - return memBuf.status; - - memBuf->Write( 0, pCustomArgs->size() ); - memBuf->Write( sizeof( uint64_t ), pCustomArgs->size(), pCustomArgs->data() ); - customArgumentsAddress = memBuf->ptr(); + auto memBuf = _process->memory().Allocate( pCustomArgs->size() + sizeof( uint64_t ), PAGE_EXECUTE_READWRITE, 0, false ); + memBuf.Write( 0, pCustomArgs->size() ); + memBuf.Write( sizeof( uint64_t ), pCustomArgs->size(), pCustomArgs->data() ); + customArgumentsAddress = memBuf.ptr(); } // Function order @@ -1190,9 +997,9 @@ call_result_t MMap::RunModuleInitializers( ImageContextPtr pImage, DWO // PTLS_CALLBACK_FUNCTION(pImage->ImageBase, dwReason, NULL); for (auto& pCallback : pImage->tlsCallbacks) { - BLACKBONE_TRACE( + BLACKBONE_TRACE( L"ManualMap: Calling TLS callback at 0x%016llx for '%ls', Reason: %d", - pCallback, pImage->ldrEntry.name.c_str(), dwReason + pCallback, pImage->ldrEntry.name.c_str(), dwReason ); a->GenCall( pCallback, { pImage->imgMem.ptr(), dwReason, customArgumentsAddress } ); @@ -1204,28 +1011,25 @@ call_result_t MMap::RunModuleInitializers( ImageContextPtr pImage, DWO { BLACKBONE_TRACE( L"ManualMap: Calling entry point for '%ls', Reason: %d", pImage->ldrEntry.name.c_str(), dwReason ); a->GenCall( pImage->ldrEntry.entryPoint, { pImage->imgMem.ptr(), dwReason, customArgumentsAddress } ); - _process.remote().SaveCallResult( *a ); + _process->remote().SaveCallResult( *a ); } // DeactivateActCtx - if (_pAContext.valid() && pDeactivateActx) + if (_pAContext.valid()) { (*a)->mov( (*a)->zax, _pAContext.ptr() + sizeof( ptr_t ) ); (*a)->mov( (*a)->zax, asmjit::host::dword_ptr( (*a)->zax ) ); - a->GenCall( pDeactivateActx->procAddress, { 0, (*a)->zax } ); + a->GenCall( pDeactivateActx.procAddress, { 0, (*a)->zax } ); } // Set invalid return code offset to preserve one from DllMain - _process.remote().AddReturnWithEvent( *a, pImage->ldrEntry.type, rt_int32, ARGS_OFFSET ); + _process->remote().AddReturnWithEvent( *a, pImage->ldrEntry.type, rt_int32, ARGS_OFFSET ); a->GenEpilogue(); - NTSTATUS status = _process.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result ); - if (!NT_SUCCESS( status )) - return status; - + uint64_t result = _process->remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize() ); if (pImage->ldrEntry.entryPoint == 0) - return call_result_t( ERROR_SUCCESS, STATUS_SUCCESS ); - + return ERROR_SUCCESS; + BLACKBONE_TRACE( L"ManualMap: DllMain of '%ls' returned %lld", pImage->ldrEntry.name.c_str(), result ); return result; } @@ -1238,82 +1042,60 @@ call_result_t MMap::RunModuleInitializers( ImageContextPtr pImage, DWO /// | hCtx | ACTCTX | file_path | /// ----------------------------- /// -/// Source umage -/// true on success -NTSTATUS MMap::CreateActx( const pe::PEImage& image ) -{ +/// Source image +void MMap::CreateActx( const pe::PEImage& image ) +{ auto a = AsmFactory::GetAssembler( image.mType() ); - NTSTATUS status = STATUS_SUCCESS; uint64_t result = 0; - auto mem = _process.memory().Allocate( 512, PAGE_READWRITE ); - if (!mem) - return mem.status; + _pAContext = _process->memory().Allocate( 512, PAGE_READWRITE ); - _pAContext = std::move( mem.result() ); - - bool switchMode = image.mType() == mt_mod64 && _process.core().isWow64(); - auto kernel32 = _process.modules().GetModule( L"kernel32.dll" ); + bool switchMode = image.mType() == mt_mod64 && _process->core().isWow64(); + auto kernel32 = _process->modules().GetModule( L"kernel32.dll" ); if (!kernel32) - { - BLACKBONE_TRACE( - L"ManualMap: Failed to get kernel32 base, error 0x%08x. Possibly LDR is not available yet", - LastNtStatus() - ); - return LastNtStatus(); - } + THROW_WITH_STATUS_AND_LOG( LastNtStatus(), "failed to get kernel32 base" ); - auto pCreateActx = _process.modules().GetExport( kernel32, "CreateActCtxW" ); - if (!pCreateActx) - { - BLACKBONE_TRACE( - L"ManualMap: Failed to create activation context for image '%ls'. 'CreateActCtxW' is absent", - image.manifestFile().c_str() - ); - return STATUS_ORDINAL_NOT_FOUND; - } + auto pCreateActx = _process->modules().GetExport( kernel32, "CreateActCtxW" ); // CreateActCtx(&act) // Emulate Wow64 if (switchMode) { - _ACTCTXW_T act32 = { 0 }; + _ACTCTXW_T act32 = { }; - act32.cbSize = sizeof(act32); + act32.cbSize = sizeof( act32 ); act32.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID; act32.lpSource = _pAContext.ptr() + sizeof( ptr_t ) + sizeof( act32 ); act32.lpResourceName = image.manifestID(); (*a)->push( _pAContext.ptr() + static_cast(sizeof( ptr_t )) ); - (*a)->mov( asmjit::host::eax, static_cast(pCreateActx->procAddress) ); + (*a)->mov( asmjit::host::eax, static_cast(pCreateActx.procAddress) ); (*a)->call( (*a)->zax ); (*a)->mov( asmjit::host::edx, _pAContext.ptr() ); //a->mov( asmjit::host::dword_ptr( asmjit::host::edx ), asmjit::host::eax ); (*a)->dw( '\x01\x02' ); - auto pTermThd = _process.modules().GetNtdllExport( "NtTerminateThread", mt_mod32 ); + auto pTermThd = _process->modules().GetNtdllExport( "NtTerminateThread", mt_mod32 ); + (*a)->push( (*a)->zax ); (*a)->push( uint32_t( 0 ) ); - (*a)->mov( asmjit::host::eax, static_cast(pTermThd->procAddress) ); + (*a)->mov( asmjit::host::eax, static_cast(pTermThd.procAddress) ); (*a)->call( (*a)->zax ); (*a)->ret( 4 ); - + // Write path to file _pAContext.Write( sizeof( ptr_t ), act32 ); - _pAContext.Write( - sizeof( ptr_t ) + sizeof( act32 ), + _pAContext.Write( + sizeof( ptr_t ) + sizeof( act32 ), (image.manifestFile().length() + 1) * sizeof( wchar_t ), image.manifestFile().c_str() ); - auto pCode = _process.memory().Allocate( 0x1000 ); - if (!pCode) - return pCode.status; + auto pCode = _process->memory().Allocate( 0x1000 ); + pCode.Write( 0, (*a)->getCodeSize(), (*a)->make() ); - pCode->Write( 0, (*a)->getCodeSize(), (*a)->make() ); - - result = _process.remote().ExecDirect( pCode->ptr(), _pAContext.ptr() + sizeof( ptr_t ) ); + result = _process->remote().ExecDirect( pCode.ptr(), _pAContext.ptr() + sizeof( ptr_t ) ); } // Native way else @@ -1333,168 +1115,135 @@ NTSTATUS MMap::CreateActx( const pe::PEImage& image ) } // Write path to file - NTSTATUS status = this->_pAContext.Write( sizeof( ptr_t ), act ); - status |= this->_pAContext.Write( + _pAContext.Write( sizeof( ptr_t ), act ); + _pAContext.Write( sizeof( ptr_t ) + sizeof( act ), (image.manifestFile().length() + 1) * sizeof( wchar_t ), image.manifestFile().c_str() ); - - return status; }; - if (_process.core().isWow64()) - status = fillACTX( _ACTCTXW32() ); - else - status = fillACTX( _ACTCTXW64() ); + _process->core().isWow64() ? fillACTX( _ACTCTXW32{} ) : fillACTX( _ACTCTXW64{} ); a->GenPrologue(); - a->GenCall( pCreateActx->procAddress, { _pAContext.ptr() + sizeof( ptr_t ) } ); + a->GenCall( pCreateActx.procAddress, { _pAContext.ptr() + sizeof( ptr_t ) } ); (*a)->mov( (*a)->zdx, _pAContext.ptr() ); (*a)->mov( (*a)->intptr_ptr( (*a)->zdx ), (*a)->zax ); - _process.remote().AddReturnWithEvent( *a, image.mType() ); + _process->remote().AddReturnWithEvent( *a, image.mType() ); a->GenEpilogue(); - status = _process.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result ); - if (!NT_SUCCESS( status )) - return status; + result = _process->remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize() ); } - if (reinterpret_cast(result) == INVALID_HANDLE_VALUE) { _pAContext.Free(); - - status = _process.remote().GetLastStatus(); - BLACKBONE_TRACE( L"ManualMap: Failed to create activation context for image '%ls'. Status: 0x%x", image.manifestFile().c_str(), status ); - return status; + THROW_WITH_STATUS_AND_LOG( _process->remote().GetLastStatus(), "failed to create activation context for image '%ls'", image.manifestFile().c_str() ); } - - return STATUS_SUCCESS; } /// /// Do SxS path probing in the target process /// /// Path to probe -/// Status code -NTSTATUS MMap::ProbeRemoteSxS( std::wstring& path ) +void MMap::ProbeRemoteSxS( std::wstring& path ) { - NTSTATUS status = STATUS_SUCCESS; - - constexpr uint32_t memSize = 0x1000; + constexpr uint32_t memSize = 0x1000; constexpr uint32_t dll1Offset = 0x300; constexpr uint32_t dll2Offset = 0x600; constexpr uint32_t pathOffset = 0x800; - constexpr uint32_t strOffset = 0xA00; - constexpr uint16_t strSize = 0x200; - - // No underlying function - auto ProbeFn = _process.modules().GetNtdllExport( "RtlDosApplyFileIsolationRedirection_Ustr" ); - if (!ProbeFn) - return ProbeFn.status; + constexpr uint32_t strOffset = 0xA00; + constexpr uint16_t strSize = 0x200; + auto ProbeFn = _process->modules().GetNtdllExport( "RtlDosApplyFileIsolationRedirection_Ustr" ); auto actx = _pAContext.ptr(); - auto pActivateActx = _process.modules().GetNtdllExport( "RtlActivateActivationContext" ); - auto pDeactivateActx = _process.modules().GetNtdllExport( "RtlDeactivateActivationContext" ); - auto pAsm = AsmFactory::GetAssembler( _process.barrier().targetWow64 ); + auto pActivateActx = _process->modules().GetNtdllExport( "RtlActivateActivationContext" ); + auto pDeactivateActx = _process->modules().GetNtdllExport( "RtlDeactivateActivationContext" ); + auto pAsm = AsmFactory::GetAssembler( _process->barrier().targetWow64 ); auto& a = *pAsm.get(); // REmote buffer - auto memBuf = _process.memory().Allocate( memSize, PAGE_READWRITE ); - if (!memBuf) - return memBuf.status; + auto memBuf = _process->memory().Allocate( memSize, PAGE_READWRITE ); // Fill Unicode strings - auto memPtr = memBuf->ptr(); + auto memPtr = memBuf.ptr(); auto fillStr = [&]( auto&& OriginalName ) { - std::remove_reference_t DllName1 = { 0 }; + std::remove_reference_t DllName1 = { }; OriginalName.Length = static_cast(path.length() * sizeof( wchar_t )); OriginalName.MaximumLength = OriginalName.Length; OriginalName.Buffer = static_cast(memPtr + sizeof( OriginalName )); - auto status = _process.memory().Write( memPtr, OriginalName ); - status = _process.memory().Write( memPtr + sizeof( OriginalName ), OriginalName.Length + 2, path.c_str() ); - if (!NT_SUCCESS( status )) - return status; + _process->memory().Write( memPtr, OriginalName ); + _process->memory().Write( memPtr + sizeof( OriginalName ), OriginalName.Length + 2, path.c_str() ); DllName1.Length = 0; DllName1.MaximumLength = strSize; DllName1.Buffer = static_cast(memPtr + strOffset); - return _process.memory().Write( memPtr + dll1Offset, DllName1 ); + return _process->memory().Write( memPtr + dll1Offset, DllName1 ); }; - if (_process.barrier().targetWow64) - status = fillStr( _UNICODE_STRING_T() ); + if (_process->barrier().targetWow64) + fillStr( _UNICODE_STRING_T{} ); else - status = fillStr( _UNICODE_STRING_T() ); - - if (!NT_SUCCESS( status )) - return status; + fillStr( _UNICODE_STRING_T{} ); a.GenPrologue(); // ActivateActCtx - if (actx && pActivateActx) + if (actx) { a->mov( a->zax, actx ); a->mov( a->zax, asmjit::host::dword_ptr( a->zax ) ); - a.GenCall( pActivateActx->procAddress, { 0, a->zax, actx + sizeof( ptr_t ) } ); + a.GenCall( pActivateActx.procAddress, { 0, a->zax, actx + sizeof( ptr_t ) } ); } // RtlDosApplyFileIsolationRedirection_Ustr - a.GenCall( ProbeFn->procAddress, - { - TRUE, - memPtr + 0, 0, - memPtr + dll1Offset, - memPtr + dll2Offset, - memPtr + pathOffset, - 0, 0, 0 - } ); + a.GenCall( ProbeFn.procAddress, + { + TRUE, + memPtr + 0, 0, + memPtr + dll1Offset, + memPtr + dll2Offset, + memPtr + pathOffset, + 0, 0, 0 + } ); - _process.remote().SaveCallResult( a ); + _process->remote().SaveCallResult( a ); // DeactivateActCtx - if (actx && pDeactivateActx) + if (actx) { a->mov( a->zax, actx + sizeof( ptr_t ) ); a->mov( a->zax, asmjit::host::dword_ptr( a->zax ) ); - a.GenCall( pDeactivateActx->procAddress, { 0, a->zax } ); + a.GenCall( pDeactivateActx.procAddress, { 0, a->zax } ); } - _process.remote().AddReturnWithEvent( a, mt_default, rt_int32, ARGS_OFFSET ); + _process->remote().AddReturnWithEvent( a, mt_default, rt_int32, ARGS_OFFSET ); a.GenEpilogue(); - uint64_t result = 0; - if (!NT_SUCCESS( status = _process.remote().ExecInWorkerThread( a->make(), a->getCodeSize(), result ) )) - return status; + NTSTATUS status = static_cast(_process->remote().ExecInWorkerThread( a->make(), a->getCodeSize() )); + THROW_ON_FAIL_AND_LOG( status, "failed execute remote SxS probe for '%ls'", path.c_str() ); - status = static_cast(result); - if (NT_SUCCESS( status )) - { - // Read result back - std::unique_ptr localBuf( new uint8_t[memSize] ); - if (NT_SUCCESS( status = memBuf->Read( 0, memSize, localBuf.get() ) )) - path = reinterpret_cast(localBuf.get() + strOffset); - } + // Read result back + auto localBuf = std::make_unique( memSize ); + memBuf.Read( 0, memSize, localBuf.get() ); - return status; + path = reinterpret_cast(localBuf.get() + strOffset); } /// /// Hide memory VAD node /// /// Image to purge -/// Status code -NTSTATUS MMap::ConcealVad( const MemBlock& imageMem) +void MMap::ConcealVad( ImageContextPtr pImage ) { - return Driver().ConcealVAD( _process.pid(), imageMem.ptr(), static_cast(imageMem.size()) ); + NTSTATUS status = Driver().ConcealVAD( _process->pid(), pImage->imgMem.ptr(), static_cast(pImage->imgMem.size()) ); + THROW_ON_FAIL_AND_LOG( status, "Failed to hide image '%ls' VAD", pImage->ldrEntry.name.c_str() ); } /// @@ -1537,10 +1286,10 @@ NTSTATUS MMap::AllocateInHighMem( MemBlock& imageMem, size_t size ) // Change protection and save address if (NT_SUCCESS( status )) { - _usedBlocks.emplace_back( ptr, size ); + _usedBlocks.emplace_back( ptr, size ); - imageMem = MemBlock( &_process.memory(), ptr, size, PAGE_READWRITE, false ); - _process.memory().Protect( ptr, size, PAGE_READWRITE ); + imageMem = MemBlock( &_process->memory(), ptr, size, PAGE_READWRITE, false ); + _process->memory().Protect( ptr, size, PAGE_READWRITE ); return true; } @@ -1550,12 +1299,11 @@ NTSTATUS MMap::AllocateInHighMem( MemBlock& imageMem, size_t size ) /// /// Remove any traces from remote process /// -/// void MMap::Cleanup() { reset(); _expMgr.reset(); - _process.remote().reset(); + _process->remote().reset(); } @@ -1568,20 +1316,20 @@ DWORD MMap::GetSectionProt( DWORD characteristics ) { DWORD dwResult = PAGE_NOACCESS; - if(characteristics & IMAGE_SCN_MEM_EXECUTE) + if (characteristics & IMAGE_SCN_MEM_EXECUTE) { - if(characteristics & IMAGE_SCN_MEM_WRITE) + if (characteristics & IMAGE_SCN_MEM_WRITE) dwResult = PAGE_EXECUTE_READWRITE; - else if(characteristics & IMAGE_SCN_MEM_READ) + else if (characteristics & IMAGE_SCN_MEM_READ) dwResult = PAGE_EXECUTE_READ; else dwResult = PAGE_EXECUTE; - } + } else { - if(characteristics & IMAGE_SCN_MEM_WRITE) + if (characteristics & IMAGE_SCN_MEM_WRITE) dwResult = PAGE_READWRITE; - else if(characteristics & IMAGE_SCN_MEM_READ) + else if (characteristics & IMAGE_SCN_MEM_READ) dwResult = PAGE_READONLY; else dwResult = PAGE_NOACCESS; diff --git a/src/BlackBone/ManualMap/MMap.h b/src/BlackBone/ManualMap/MMap.h index df18ad08..c2ec8d14 100644 --- a/src/BlackBone/ManualMap/MMap.h +++ b/src/BlackBone/ManualMap/MMap.h @@ -168,8 +168,7 @@ using vecImageCtx = std::vector; class MMap { public: - BLACKBONE_API MMap( class Process& proc ); - BLACKBONE_API ~MMap( void ); + BLACKBONE_API MMap( class Process* proc ); /// /// Manually map PE image into underlying target process @@ -179,7 +178,7 @@ class MMap /// Mapping callback. Triggers for each mapped module /// User-supplied callback context /// Mapped image info - BLACKBONE_API call_result_t MapImage( + BLACKBONE_API ModuleDataPtr MapImage( const std::wstring& path, eLoadFlags flags = NoFlags, MapCallback mapCallback = nullptr, @@ -197,7 +196,7 @@ class MMap /// Mapping callback. Triggers for each mapped module /// User-supplied callback context /// Mapped image info - BLACKBONE_API call_result_t MapImage( + BLACKBONE_API ModuleDataPtr MapImage( size_t size, void* buffer, bool asImage = false, eLoadFlags flags = NoFlags, @@ -209,19 +208,17 @@ class MMap /// /// Unmap all manually mapped modules /// - /// Status code - BLACKBONE_API NTSTATUS UnmapAllModules(); + BLACKBONE_API void UnmapAllModules(); /// /// Remove any traces from remote process /// - /// BLACKBONE_API void Cleanup(); /// /// Reset local data /// - BLACKBONE_API inline void reset() { _images.clear(); _pAContext.Reset(); _usedBlocks.clear(); } + BLACKBONE_API void reset() { _images.clear(); _pAContext.reset(); _usedBlocks.clear(); } private: /// /// Manually map PE image into underlying target process @@ -234,13 +231,13 @@ class MMap /// Mapping callback. Triggers for each mapped module /// User-supplied callback context /// Mapped image info - call_result_t MapImageInternal( + ModuleDataPtr MapImageInternal( const std::wstring& path, void* buffer, size_t size, bool asImage = false, eLoadFlags flags = NoFlags, MapCallback ldrCallback = nullptr, - void* ldrContext = nullptr, + void* context = nullptr, CustomArgs_t* pCustomArgs_t = nullptr ); @@ -258,11 +255,7 @@ class MMap /// Image path /// Mapping flags /// Module info - call_result_t FindOrMapModule( - const std::wstring& path, - void* buffer, size_t size, bool asImage, - eLoadFlags flags = NoFlags - ); + ModuleDataPtr FindOrMapModule( const std::wstring& path, void* buffer, size_t size, bool asImage, eLoadFlags flags = NoFlags ); /// /// Run module initializers(TLS and entry point). @@ -275,64 +268,56 @@ class MMap /// DLL_THREAD_DETTACH /// /// DllMain result - call_result_t RunModuleInitializers( ImageContextPtr pImage, DWORD dwReason, CustomArgs_t* pCustomArgs_t = nullptr ); + uint64_t RunModuleInitializers( ImageContextPtr pImage, DWORD dwReason, CustomArgs_t* pCustomArgs_t = nullptr ); /// /// Copies image into target process /// /// Image data - /// Status code - NTSTATUS CopyImage( ImageContextPtr pImage ); + void CopyImage( ImageContextPtr pImage ); /// /// Adjust image memory protection /// /// image data - /// Status code - NTSTATUS ProtectImageMemory( ImageContextPtr pImage ); + void ProtectImageMemory( ImageContextPtr pImage ); /// /// Fix relocations if image wasn't loaded at base address /// /// image data - /// true on success - NTSTATUS RelocateImage( ImageContextPtr pImage ); + void RelocateImage( ImageContextPtr pImage ); /// /// Resolves image import or delayed image import /// /// Image data /// Resolve delayed import instead - /// Status code - NTSTATUS ResolveImport( ImageContextPtr pImage, bool useDelayed = false ); + void ResolveImport( ImageContextPtr pImage, bool useDelayed = false ); /// - /// Resolve static TLS storage + /// Set custom exception handler to bypass SafeSEH under DEP /// /// image data - /// Status code - NTSTATUS InitStaticTLS( ImageContextPtr pImage ); + void EnableExceptions( ImageContextPtr pImage ); /// - /// Set custom exception handler to bypass SafeSEH under DEP + /// Remove custom exception handler /// /// image data - /// Status code - NTSTATUS EnableExceptions( ImageContextPtr pImage ); + void DisableExceptions( ImageContextPtr pImage ); /// - /// Remove custom exception handler + /// Resolve static TLS storage /// /// image data - /// true on success - NTSTATUS DisableExceptions( ImageContextPtr pImage ); + void InitStaticTLS( ImageContextPtr pImage ); /// /// Calculate and set security cookie /// /// image data - /// Status code - NTSTATUS InitializeCookie( ImageContextPtr pImage ); + void InitializeCookie( ImageContextPtr pImage ); /// /// Return existing or load missing dependency @@ -340,7 +325,7 @@ class MMap /// Currently mapped image data /// Dependency path /// - call_result_t FindOrMapDependency( ImageContextPtr pImage, std::wstring& path ); + ModuleDataPtr FindOrMapDependency( ImageContextPtr pImage, std::wstring& path ); /// /// Create activation context @@ -352,22 +337,19 @@ class MMap /// Manifest container path /// Manifest resource id /// if true - 'path' points to a valid PE file, otherwise - 'path' points to separate manifest file - /// true on success - NTSTATUS CreateActx( const pe::PEImage& image ); + void CreateActx( const pe::PEImage& image ); /// /// Do SxS path probing in the target process /// /// Path to probe - /// Status code - NTSTATUS ProbeRemoteSxS( std::wstring& path ); + void ProbeRemoteSxS( std::wstring& path ); /// /// Hide memory VAD node /// /// Image to purge - /// Status code - NTSTATUS ConcealVad( const MemBlock& imageMem ); + void ConcealVad( ImageContextPtr pImage ); /// /// Allocates memory region beyond 4GB limit @@ -385,7 +367,7 @@ class MMap DWORD GetSectionProt( DWORD characteristics ); private: - class Process& _process; // Target process manager + class Process* _process; // Target process manager MExcept _expMgr; // Exception handler manager vecImageCtx _images; // Mapped images MemBlock _pAContext; // SxS activation context memory address diff --git a/src/BlackBone/ManualMap/Native/NtLoader.cpp b/src/BlackBone/ManualMap/Native/NtLoader.cpp index 2199e0d7..e8e3ff35 100644 --- a/src/BlackBone/ManualMap/Native/NtLoader.cpp +++ b/src/BlackBone/ManualMap/Native/NtLoader.cpp @@ -11,7 +11,7 @@ namespace blackbone { -NtLdr::NtLdr( Process& proc ) +NtLdr::NtLdr( Process* proc ) : _process( proc ) { } @@ -20,34 +20,43 @@ NtLdr::NtLdr( Process& proc ) /// Initialize some loader stuff /// /// Target module type -/// true on successcore().isWow64() ? mt_mod32 : mt_mod64; - // Select loader version - if (_initializedFor == mt_mod32) + auto callNoThrow = [this]( auto fn, auto... args ) { - FindLdrHeap(); - FindLdrpHashTable(); - if (IsWindows8OrGreater()) - FindLdrpModuleIndexBase(); - } - else + try + { + (this->*fn)(args...); + } + catch (const std::exception& e) + { + BLACKBONE_TRACE( "NativeLdr: %s", e.what() ); + } + }; + + auto findAll = [this, callNoThrow]( auto val ) { - FindLdrHeap(); - FindLdrpHashTable(); + callNoThrow( &NtLdr::FindLdrHeap ); + callNoThrow( &NtLdr::FindLdrpHashTable ); if (IsWindows8OrGreater()) - FindLdrpModuleIndexBase(); - } + callNoThrow( &NtLdr::FindLdrpModuleIndexBase ); + }; + + // Select loader version + if (_initializedFor == mt_mod32) + findAll( uint32_t{} ); + else + findAll( uint64_t{} ); _nodeMap.clear(); @@ -60,8 +69,6 @@ bool NtLdr::Init( eModType initFor /*= mt_default*/ ) if (IsWindows8OrGreater() && _LdrpModuleIndexBase == 0) BLACKBONE_TRACE( "NativeLdr: LdrpModuleIndexBase not found" ); #endif - - return true; } /// @@ -69,12 +76,11 @@ bool NtLdr::Init( eModType initFor /*= mt_default*/ ) /// (LdrpHashTable, LdrpModuleIndex( win8+ only ), InMemoryOrderModuleList( win7 only )) /// /// Module data -/// true on success -bool NtLdr::CreateNTReference( NtLdrEntry& mod ) +void NtLdr::CreateNTReference( NtLdrEntry& mod ) { // Skip if (mod.flags == Ldr_None) - return true; + return; // Check if reinitialization is required if (_initializedFor != mod.type) @@ -90,8 +96,9 @@ bool NtLdr::CreateNTReference( NtLdrEntry& mod ) else mod.ldrPtr = CALL_64_86( x64Image, InitW7Node, mod ); + // Should not happen, exception should have been thrown earlier if (mod.ldrPtr == 0) - return false; + THROW_AND_LOG( "failed to allocated ldr node memory" ); _nodeMap.emplace( mod.baseAddress, mod.ldrPtr ); @@ -109,17 +116,15 @@ bool NtLdr::CreateNTReference( NtLdrEntry& mod ) // Insert into ldr lists if (mod.flags & Ldr_ThdCall || (!w8 && mod.flags & Ldr_ModList)) { - _process.memory().Write( FIELD_PTR_64_86( x64Image, mod.ldrPtr, _LDR_DATA_TABLE_ENTRY_BASE_T, Flags ), 0x80004 ); + _process->memory().Write( FIELD_PTR_64_86( x64Image, mod.ldrPtr, _LDR_DATA_TABLE_ENTRY_BASE_T, Flags ), 0x80004 ); ptr_t loadPtr = 0, initptr = 0; loadPtr = FIELD_PTR_64_86( x64Image, mod.ldrPtr, _LDR_DATA_TABLE_ENTRY_BASE_T, InLoadOrderLinks ); - if(w8) + if (w8) initptr = FIELD_PTR_64_86( x64Image, mod.ldrPtr, _LDR_DATA_TABLE_ENTRY_BASE_T, InInitializationOrderLinks ); CALL_64_86( x64Image, InsertMemModuleNode, 0, loadPtr, initptr ); } - - return true; } /// @@ -128,17 +133,14 @@ bool NtLdr::CreateNTReference( NtLdrEntry& mod ) /// node pointer (if nullptr - new dummy node is allocated) /// Module base address /// Node address -template +template ptr_t NtLdr::SetNode( ptr_t ptr, Module pModule ) { - if(ptr == 0) + if (ptr == 0) { - auto mem = _process.memory().Allocate( sizeof( T ), PAGE_READWRITE, 0, false ); - if (!mem) - return 0; - - ptr = mem->ptr(); - mem->Write( offsetOf( &T::DllBase ), pModule ); + auto mem = _process->memory().Allocate( sizeof( T ), PAGE_READWRITE, 0, false ); + ptr = mem.ptr(); + mem.Write( offsetOf( &T::DllBase ), pModule ); } return ptr; @@ -149,8 +151,7 @@ ptr_t NtLdr::SetNode( ptr_t ptr, Module pModule ) /// /// Module data /// TLS directory of target image -/// Status code -NTSTATUS NtLdr::AddStaticTLSEntry( NtLdrEntry& mod, ptr_t tlsPtr ) +void NtLdr::AddStaticTLSEntry( NtLdrEntry& mod, ptr_t tlsPtr ) { bool wxp = IsWindowsXPOrGreater() && !IsWindowsVistaOrGreater(); ptr_t pNode = _nodeMap.count( mod.baseAddress ) ? _nodeMap[mod.baseAddress] : 0; @@ -168,9 +169,6 @@ NTSTATUS NtLdr::AddStaticTLSEntry( NtLdrEntry& mod, ptr_t tlsPtr ) pNode = SetNode<_LDR_DATA_TABLE_ENTRY_BASE32>( pNode, mod.baseAddress ); } - if (pNode == 0) - return STATUS_NO_MEMORY; - // Update ptr if (mod.ldrPtr == 0) mod.ldrPtr = pNode; @@ -178,59 +176,45 @@ NTSTATUS NtLdr::AddStaticTLSEntry( NtLdrEntry& mod, ptr_t tlsPtr ) // Manually add TLS table if (wxp && tlsPtr != 0) { - ptr_t pTeb = 0; - pTeb = _process.remote().getExecThread()->teb( static_cast<_TEB32*>(nullptr) ); + ptr_t pTeb = _process->remote().getExecThread()->teb32(); - auto mem = _process.memory().Allocate( 0x1000, PAGE_READWRITE, 0, false ); - if (!mem) - return mem.status; + auto tlsStore = _process->memory().Allocate( 0x1000, PAGE_READWRITE, 0, false ); - auto tlsStore = std::move( mem.result() ); - - IMAGE_TLS_DIRECTORY remoteTls = { 0 }; - _process.memory().Read( tlsPtr, sizeof( remoteTls ), &remoteTls ); + IMAGE_TLS_DIRECTORY remoteTls = { }; + _process->memory().Read( tlsPtr, sizeof( remoteTls ), &remoteTls ); auto size = remoteTls.EndAddressOfRawData - remoteTls.StartAddressOfRawData; std::unique_ptr buf( new uint8_t[size]() ); - _process.memory().Read( remoteTls.StartAddressOfRawData, size, buf.get() ); + _process->memory().Read( remoteTls.StartAddressOfRawData, size, buf.get() ); tlsStore.Write( 0, tlsStore.ptr() + 0x800 ); tlsStore.Write( 0x800, size, buf.get() ); - return _process.memory().Write( fieldPtr( pTeb, &_TEB32::ThreadLocalStoragePointer ), tlsStore.ptr() ); + _process->memory().Write( fieldPtr( pTeb, &_TEB32::ThreadLocalStoragePointer ), tlsStore.ptr() ); } - // Use native method - if (LdrpHandleTlsData) - { - auto a = AsmFactory::GetAssembler( mod.type ); - uint64_t result = 0; + if (!LdrpHandleTlsData) + THROW_AND_LOG( "LdrpHandleTlsData not found" ); - a->GenPrologue(); - a->GenCall( LdrpHandleTlsData, { pNode }, IsWindows8Point1OrGreater() ? cc_thiscall : cc_stdcall ); - _process.remote().AddReturnWithEvent( *a ); - a->GenEpilogue(); + auto a = AsmFactory::GetAssembler( mod.type ); - auto status = _process.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result ); - if (!NT_SUCCESS( status )) - return status; + a->GenPrologue(); + a->GenCall( LdrpHandleTlsData, { pNode }, IsWindows8Point1OrGreater() ? cc_thiscall : cc_stdcall ); + _process->remote().AddReturnWithEvent( *a ); + a->GenEpilogue(); - return static_cast(result); - } - else - return STATUS_ORDINAL_NOT_FOUND; + NTSTATUS status = static_cast(_process->remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize() )); + THROW_ON_FAIL_AND_LOG( status, "LdrpHandleTlsData failed" ); } - /// /// Create module record in LdrpInvertedFunctionTable /// Used to create fake SAFESEH entries /// /// Module data -/// true on success -bool NtLdr::InsertInvertedFunctionTable( NtLdrEntry& mod ) -{ +void NtLdr::InsertInvertedFunctionTable( NtLdrEntry& mod ) +{ ptr_t RtlInsertInvertedFunctionTable = g_symbols.RtlInsertInvertedFunctionTable64; ptr_t LdrpInvertedFunctionTable = g_symbols.LdrpInvertedFunctionTable64; if (mod.type == mt_mod32) @@ -240,19 +224,18 @@ bool NtLdr::InsertInvertedFunctionTable( NtLdrEntry& mod ) } // Invalid addresses. Probably pattern scan has failed - if (RtlInsertInvertedFunctionTable == 0 || LdrpInvertedFunctionTable == 0) - return false; + if (!RtlInsertInvertedFunctionTable || !LdrpInvertedFunctionTable) + THROW_AND_LOG( "RtlInsertInvertedFunctionTable or LdrpInvertedFunctionTable not found" ); auto InsertP = [&]( auto table ) { - uint64_t result = 0; auto a = AsmFactory::GetAssembler( mod.type ); - auto& memory = _process.memory(); + auto& memory = _process->memory(); memory.Read( LdrpInvertedFunctionTable, sizeof( table ), &table ); for (ULONG i = 0; i < table.Count; i++) if (table.Entries[i].ImageBase == mod.baseAddress) - return true; + return; a->GenPrologue(); @@ -263,10 +246,10 @@ bool NtLdr::InsertInvertedFunctionTable( NtLdrEntry& mod ) else a->GenCall( RtlInsertInvertedFunctionTable, { LdrpInvertedFunctionTable, mod.baseAddress, mod.size } ); - _process.remote().AddReturnWithEvent( *a ); + _process->remote().AddReturnWithEvent( *a ); a->GenEpilogue(); - _process.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result ); + _process->remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize() ); memory.Read( LdrpInvertedFunctionTable, sizeof( table ), &table ); for (DWORD i = 0; i < table.Count; i++) @@ -276,7 +259,10 @@ bool NtLdr::InsertInvertedFunctionTable( NtLdrEntry& mod ) // If Image has SAFESEH, RtlInsertInvertedFunctionTable is enough if (table.Entries[i].SizeOfTable != 0) - return mod.safeSEH = true; + { + mod.safeSEH = true; + return; + } // // Create fake Exception directory @@ -285,8 +271,6 @@ bool NtLdr::InsertInvertedFunctionTable( NtLdrEntry& mod ) // Allocate memory for 2048 possible handlers auto mem = memory.Allocate( sizeof( DWORD ) * 0x800, PAGE_READWRITE, 0, false ); - if (!mem) - return false; // EncodeSystemPointer( mem->ptr() ) uint32_t size = 0; @@ -295,12 +279,12 @@ bool NtLdr::InsertInvertedFunctionTable( NtLdrEntry& mod ) if (mod.type == mt_mod64) { size = sizeof( uint64_t ); - pEncoded = _rotr64( cookie ^ mem->ptr(), cookie & 0x3F ); + pEncoded = _rotr64( cookie ^ mem.ptr(), cookie & 0x3F ); } else { size = sizeof( uint32_t ); - pEncoded = _rotr( cookie ^ mem->ptr(), cookie & 0x1F ); + pEncoded = _rotr( cookie ^ mem.ptr(), cookie & 0x1F ); } // m_LdrpInvertedFunctionTable->Entries[i].ExceptionDirectory @@ -312,18 +296,18 @@ bool NtLdr::InsertInvertedFunctionTable( NtLdrEntry& mod ) // LdrProtectMrdata is used to make it writable when needed DWORD flOld = 0; memory.Protect( LdrpInvertedFunctionTable + field_ofst, sizeof( ptr_t ), PAGE_EXECUTE_READWRITE, &flOld ); - auto status = memory.Write( LdrpInvertedFunctionTable + field_ofst, size, &pEncoded ); + memory.Write( LdrpInvertedFunctionTable + field_ofst, size, &pEncoded ); memory.Protect( LdrpInvertedFunctionTable + field_ofst, sizeof( ptr_t ), flOld, &flOld ); - return NT_SUCCESS( status ); + return; } - return false; + THROW_AND_LOG( "could not find target module in LdrpInvertedFunctionTable" ); }; if (IsWindows8OrGreater()) { - if(mod.type == mt_mod64) + if (mod.type == mt_mod64) return InsertP( _RTL_INVERTED_FUNCTION_TABLE8() ); else return InsertP( _RTL_INVERTED_FUNCTION_TABLE8() ); @@ -358,16 +342,15 @@ NTSTATUS NtLdr::UnloadTLS( const NtLdrEntry& mod, bool noThread /*= false*/ ) return STATUS_ORDINAL_NOT_FOUND; auto a = AsmFactory::GetAssembler( mod.type ); - uint64_t result = 0; a->GenPrologue(); a->GenCall( LdrpReleaseTlsEntry, { mod.ldrPtr, 0 }, IsWindows8Point1OrGreater() ? cc_fastcall : cc_stdcall ); - _process.remote().AddReturnWithEvent( *a ); + _process->remote().AddReturnWithEvent( *a ); a->GenEpilogue(); - _process.remote().CreateRPCEnvironment( noThread ? Worker_UseExisting : Worker_CreateNew, true ); - _process.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result ); + _process->remote().CreateRPCEnvironment( noThread ? Worker_UseExisting : Worker_CreateNew, true ); + _process->remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize() ); return STATUS_SUCCESS; } @@ -381,26 +364,21 @@ template ptr_t NtLdr::InitBaseNode( NtLdrEntry& mod ) { using EntryType = _LDR_DATA_TABLE_ENTRY_BASE_T; - ptr_t entryPtr = AllocateInHeap( mod.type, sizeof( _LDR_DATA_TABLE_ENTRY_W8 ) ).result( 0 ); - if (entryPtr == 0) - return 0; + ptr_t entryPtr = AllocateInHeap( mod.type, sizeof( _LDR_DATA_TABLE_ENTRY_W8 ) ); + assert( entryPtr != 0 ); // Allocate space for Unicode string - _UNICODE_STRING_T strLocal = { 0 }; - auto mem = _process.memory().Allocate( 0x1000, PAGE_READWRITE, 0, false ); - if (!mem) - return 0; - - auto StringBuf = std::move( mem.result() ); + _UNICODE_STRING_T strLocal = { }; + auto StringBuf = _process->memory().Allocate( 0x1000, PAGE_READWRITE, 0, false ); // entryPtr->DllBase = ModuleBase; - _process.memory().Write( fieldPtr( entryPtr, &EntryType::DllBase ), static_cast(mod.baseAddress) ); + _process->memory().Write( fieldPtr( entryPtr, &EntryType::DllBase ), static_cast(mod.baseAddress) ); // entryPtr->SizeOfImage = ImageSize; - _process.memory().Write( fieldPtr( entryPtr, &EntryType::SizeOfImage ), mod.size ); + _process->memory().Write( fieldPtr( entryPtr, &EntryType::SizeOfImage ), mod.size ); // entryPtr->EntryPoint = entryPoint; - _process.memory().Write( fieldPtr( entryPtr, &EntryType::EntryPoint ), static_cast(mod.entryPoint) ); + _process->memory().Write( fieldPtr( entryPtr, &EntryType::EntryPoint ), static_cast(mod.entryPoint) ); // Dll name hash mod.hash = HashString( mod.name ); @@ -412,7 +390,7 @@ ptr_t NtLdr::InitBaseNode( NtLdrEntry& mod ) StringBuf.Write( 0, strLocal.Length + 2, mod.name.c_str() ); // entryPtr->BaseDllName = strLocal; - _process.memory().Write( fieldPtr( entryPtr, &EntryType::BaseDllName ), strLocal ); + _process->memory().Write( fieldPtr( entryPtr, &EntryType::BaseDllName ), strLocal ); // Dll full path strLocal.Length = static_cast(mod.fullPath.length() * sizeof( wchar_t )); @@ -420,10 +398,10 @@ ptr_t NtLdr::InitBaseNode( NtLdrEntry& mod ) StringBuf.Write( 0x800, strLocal.Length + 2, mod.fullPath.c_str() ); // entryPtr->FullDllName = strLocal; - _process.memory().Write( fieldPtr( entryPtr, &EntryType::FullDllName ), strLocal ); + _process->memory().Write( fieldPtr( entryPtr, &EntryType::FullDllName ), strLocal ); // entryPtr->LoadCount = -1; - _process.memory().Write( fieldPtr( entryPtr, &EntryType::LoadCount ), strLocal ); + _process->memory().Write( fieldPtr( entryPtr, &EntryType::LoadCount ), strLocal ); return entryPtr; } @@ -440,28 +418,26 @@ ptr_t NtLdr::InitW8Node( NtLdrEntry& mod ) using DdagType = _LDR_DDAG_NODE; ptr_t entryPtr = InitBaseNode( mod ); - ptr_t DdagNodePtr = AllocateInHeap( mod.type, sizeof( DdagType ) ).result( 0 ); - if (!entryPtr || !DdagNodePtr) - return 0; - + ptr_t DdagNodePtr = AllocateInHeap( mod.type, sizeof( DdagType ) ); + // entryPtr->BaseNameHashValue = hash; - _process.memory().Write( fieldPtr( entryPtr, &EntryType::BaseNameHashValue ), mod.hash ); + _process->memory().Write( fieldPtr( entryPtr, &EntryType::BaseNameHashValue ), mod.hash ); // // Ddag node // // entryPtr->DdagNode = pDdagNode; - _process.memory().Write( fieldPtr( entryPtr, &EntryType::DdagNode ), static_cast(DdagNodePtr) ); + _process->memory().Write( fieldPtr( entryPtr, &EntryType::DdagNode ), static_cast(DdagNodePtr) ); // DdagNodePtr->State = LdrModulesReadyToRun; - _process.memory().Write( fieldPtr( DdagNodePtr, &DdagType::State ), LdrModulesReadyToRun ); + _process->memory().Write( fieldPtr( DdagNodePtr, &DdagType::State ), LdrModulesReadyToRun ); // DdagNodePtr->ReferenceCount = 1; - _process.memory().Write( fieldPtr( DdagNodePtr, &DdagType::ReferenceCount ), 1 ); + _process->memory().Write( fieldPtr( DdagNodePtr, &DdagType::ReferenceCount ), 1 ); // DdagNodePtr->LoadCount = -1; - _process.memory().Write( fieldPtr( DdagNodePtr, &DdagType::LoadCount ), -1 ); + _process->memory().Write( fieldPtr( DdagNodePtr, &DdagType::LoadCount ), -1 ); return entryPtr; } @@ -481,12 +457,12 @@ ptr_t NtLdr::InitW7Node( NtLdrEntry& mod ) return 0; // Forward Links - _process.memory().Write( fieldPtr( entryPtr, &EntryType::ForwarderLinks ), fieldPtr( entryPtr, &EntryType::ForwarderLinks ) ); - _process.memory().Write( fieldPtr( entryPtr, &EntryType::ForwarderLinks ) + sizeof( T ), fieldPtr( entryPtr, &EntryType::ForwarderLinks ) ); + _process->memory().Write( fieldPtr( entryPtr, &EntryType::ForwarderLinks ), fieldPtr( entryPtr, &EntryType::ForwarderLinks ) ); + _process->memory().Write( fieldPtr( entryPtr, &EntryType::ForwarderLinks ) + sizeof( T ), fieldPtr( entryPtr, &EntryType::ForwarderLinks ) ); // Static links - _process.memory().Write( fieldPtr( entryPtr, &EntryType::StaticLinks ), fieldPtr( entryPtr, &EntryType::StaticLinks ) ); - _process.memory().Write( fieldPtr( entryPtr, &EntryType::StaticLinks ) + sizeof( T ), fieldPtr( entryPtr, &EntryType::StaticLinks ) ); + _process->memory().Write( fieldPtr( entryPtr, &EntryType::StaticLinks ), fieldPtr( entryPtr, &EntryType::StaticLinks ) ); + _process->memory().Write( fieldPtr( entryPtr, &EntryType::StaticLinks ) + sizeof( T ), fieldPtr( entryPtr, &EntryType::StaticLinks ) ); return entryPtr; } @@ -502,12 +478,9 @@ void NtLdr::InsertTreeNode( ptr_t nodePtr, const NtLdrEntry& mod ) // // Win8 module tree // - auto root = _process.memory().Read( _LdrpModuleIndexBase ); - if (!root) - return; - - auto LdrNodePtr = structBase( root.result(), &_LDR_DATA_TABLE_ENTRY_W8::BaseAddressIndexNode ); - auto LdrNode = _process.memory().Read<_LDR_DATA_TABLE_ENTRY_W8>( LdrNodePtr ).result( _LDR_DATA_TABLE_ENTRY_W8() ); + auto root = _process->memory().Read( _LdrpModuleIndexBase ); + auto LdrNodePtr = structBase( root, &_LDR_DATA_TABLE_ENTRY_W8::BaseAddressIndexNode ); + auto LdrNode = _process->memory().Read<_LDR_DATA_TABLE_ENTRY_W8>( LdrNodePtr ); bool bRight = false; // Walk tree @@ -518,7 +491,7 @@ void NtLdr::InsertTreeNode( ptr_t nodePtr, const NtLdrEntry& mod ) if (LdrNode.BaseAddressIndexNode.Left) { LdrNodePtr = structBase( LdrNode.BaseAddressIndexNode.Left, &_LDR_DATA_TABLE_ENTRY_W8::BaseAddressIndexNode ); - _process.memory().Read( LdrNodePtr, sizeof(LdrNode), &LdrNode ); + _process->memory().Read( LdrNodePtr, sizeof( LdrNode ), &LdrNode ); } else break; @@ -528,7 +501,7 @@ void NtLdr::InsertTreeNode( ptr_t nodePtr, const NtLdrEntry& mod ) if (LdrNode.BaseAddressIndexNode.Right) { LdrNodePtr = structBase( LdrNode.BaseAddressIndexNode.Right, &_LDR_DATA_TABLE_ENTRY_W8::BaseAddressIndexNode ); - _process.memory().Read( LdrNodePtr, sizeof(LdrNode), &LdrNode ); + _process->memory().Read( LdrNodePtr, sizeof( LdrNode ), &LdrNode ); } else { @@ -540,36 +513,30 @@ void NtLdr::InsertTreeNode( ptr_t nodePtr, const NtLdrEntry& mod ) else { // pLdrNode->DdagNode->ReferenceCount++; - auto Ddag = _process.memory().Read<_LDR_DDAG_NODE>( LdrNode.DdagNode ); - if (!Ddag) - return; - - Ddag->ReferenceCount++; - _process.memory().Write( LdrNode.DdagNode, Ddag.result() ); + auto Ddag = _process->memory().Read<_LDR_DDAG_NODE>( LdrNode.DdagNode ); + Ddag.ReferenceCount++; + _process->memory().Write( LdrNode.DdagNode, Ddag ); return; } } // Insert using RtlRbInsertNodeEx auto a = AsmFactory::GetAssembler( mod.type ); - uint64_t result = 0; - auto RtlRbInsertNodeEx = _process.modules().GetNtdllExport( "RtlRbInsertNodeEx", mod.type ); - if (!RtlRbInsertNodeEx) - return; + auto RtlRbInsertNodeEx = _process->modules().GetNtdllExport( "RtlRbInsertNodeEx", mod.type ); a->GenPrologue(); - a->GenCall( RtlRbInsertNodeEx->procAddress, - { - _LdrpModuleIndexBase, - fieldPtr( LdrNodePtr, &_LDR_DATA_TABLE_ENTRY_W8::BaseAddressIndexNode ), - static_cast(bRight), - fieldPtr( nodePtr, &_LDR_DATA_TABLE_ENTRY_W8::BaseAddressIndexNode ) - } ); + a->GenCall( RtlRbInsertNodeEx.procAddress, + { + _LdrpModuleIndexBase, + fieldPtr( LdrNodePtr, &_LDR_DATA_TABLE_ENTRY_W8::BaseAddressIndexNode ), + static_cast(bRight), + fieldPtr( nodePtr, &_LDR_DATA_TABLE_ENTRY_W8::BaseAddressIndexNode ) + } ); - _process.remote().AddReturnWithEvent( *a, mod.type ); + _process->remote().AddReturnWithEvent( *a, mod.type ); a->GenEpilogue(); - _process.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result ); + _process->remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize() ); } /// @@ -580,12 +547,12 @@ void NtLdr::InsertTreeNode( ptr_t nodePtr, const NtLdrEntry& mod ) template void NtLdr::InsertMemModuleNode( ptr_t pNodeMemoryOrderLink, ptr_t pNodeLoadOrderLink, ptr_t pNodeInitOrderLink ) { - ptr_t pPeb = _process.core().peb(); + ptr_t pPeb = _process->core().peb( static_cast<_PEB_T*>(nullptr) ); ptr_t pLdr = 0; if (pPeb) - pLdr = _process.memory().Read( fieldPtr( pPeb, &PEB_T::Ldr ) ).result( 0 ); - + pLdr = _process->memory().Read( fieldPtr( pPeb, &PEB_T::Ldr ) ); + if (pLdr) { // pLdr->InMemoryOrderModuleList @@ -610,12 +577,12 @@ void NtLdr::InsertMemModuleNode( ptr_t pNodeMemoryOrderLink, ptr_t pNodeLoadOrde template void NtLdr::InsertHashNode( ptr_t pNodeLink, ULONG hash ) { - if(pNodeLink) + if (pNodeLink) { // LrpHashTable record - auto pHashList = _process.memory().Read( _LdrpHashTable + sizeof( _LIST_ENTRY_T )*(hash & 0x1F) ); - if(pHashList) - InsertTailList( pHashList.result(), pNodeLink ); + auto pHashList = _process->memory().Read( _LdrpHashTable + sizeof( _LIST_ENTRY_T )*(hash & 0x1F) ); + if (pHashList) + InsertTailList( pHashList, pNodeLink ); } } @@ -628,17 +595,17 @@ template void NtLdr::InsertTailList( ptr_t ListHead, ptr_t Entry ) { // PrevEntry = ListHead->Blink; - auto PrevEntry = _process.memory().Read( fieldPtr( ListHead, &_LIST_ENTRY_T::Blink ) ).result( 0 ); + auto PrevEntry = _process->memory().Read( fieldPtr( ListHead, &_LIST_ENTRY_T::Blink ) ); // Entry->Flink = ListHead; // Entry->Blink = PrevEntry; - _process.memory().Write( fieldPtr( Entry, &_LIST_ENTRY_T::Flink ), sizeof(T), &ListHead ); - _process.memory().Write( fieldPtr( Entry, &_LIST_ENTRY_T::Blink ), sizeof( T ), &PrevEntry ); + _process->memory().Write( fieldPtr( Entry, &_LIST_ENTRY_T::Flink ), sizeof( T ), &ListHead ); + _process->memory().Write( fieldPtr( Entry, &_LIST_ENTRY_T::Blink ), sizeof( T ), &PrevEntry ); // PrevEntry->Flink = Entry; // ListHead->Blink = Entry; - _process.memory().Write( fieldPtr( PrevEntry, &_LIST_ENTRY_T::Flink ), sizeof( T ), &Entry ); - _process.memory().Write( fieldPtr( ListHead, &_LIST_ENTRY_T::Blink ), sizeof( T ), &Entry ); + _process->memory().Write( fieldPtr( PrevEntry, &_LIST_ENTRY_T::Flink ), sizeof( T ), &Entry ); + _process->memory().Write( fieldPtr( ListHead, &_LIST_ENTRY_T::Blink ), sizeof( T ), &Entry ); } /// @@ -671,11 +638,10 @@ ULONG NtLdr::HashString( const std::wstring& str ) /// Module type /// Size to allocate /// Allocated address -call_result_t NtLdr::AllocateInHeap( eModType mt, size_t size ) +ptr_t NtLdr::AllocateInHeap( eModType mt, size_t size ) { - NTSTATUS status = STATUS_SUCCESS; - auto RtlAllocateHeap = _process.modules().GetNtdllExport( "RtlAllocateHeap", mt ); - if (_LdrHeapBase && RtlAllocateHeap) + auto RtlAllocateHeap = _process->modules().GetNtdllExport( "RtlAllocateHeap", mt ); + if (_LdrHeapBase) { auto a = AsmFactory::GetAssembler( mt ); @@ -683,55 +649,38 @@ call_result_t NtLdr::AllocateInHeap( eModType mt, size_t size ) // HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); // a->GenPrologue(); - a->GenCall( RtlAllocateHeap->procAddress, { _LdrHeapBase, HEAP_ZERO_MEMORY, size } ); - _process.remote().AddReturnWithEvent( (*a), mt ); + a->GenCall( RtlAllocateHeap.procAddress, { _LdrHeapBase, HEAP_ZERO_MEMORY, size } ); + _process->remote().AddReturnWithEvent( (*a), mt ); a->GenEpilogue(); - uint64_t result = 0; - status = _process.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result ); - if (NT_SUCCESS( status )) - return result; + if (auto ptr = _process->remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize() ); ptr) + return ptr; } - else - status = STATUS_ORDINAL_NOT_FOUND; - if (!NT_SUCCESS( status )) - { - auto mem = _process.memory().Allocate( size, PAGE_READWRITE, 0, false ); - if (!mem) - return mem.status; - - return mem->ptr(); - } - - return STATUS_ILLEGAL_FUNCTION; + // Fall back to VirtualAlloc + auto mem = _process->memory().Allocate( size, PAGE_READWRITE, 0, false ); + return mem.ptr(); } /// /// Find LdrpHashTable[] variable /// -/// true on success template -bool NtLdr::FindLdrpHashTable() +void NtLdr::FindLdrpHashTable() { - _PEB_T Peb = { 0 }; - _process.core().peb( &Peb ); + _PEB_T Peb = { }; + _process->core().peb( &Peb ); if (Peb.ImageBaseAddress == 0) - return false; + THROW_WITH_STATUS_AND_LOG( LastNtStatus(), "failed to get process base image address" ); - auto Ldr = _process.memory().Read<_PEB_LDR_DATA2_T>( Peb.Ldr ); - if (!Ldr) - return false; + auto Ldr = _process->memory().Read<_PEB_LDR_DATA2_T>( Peb.Ldr ); // Get loader entry - _LDR_DATA_TABLE_ENTRY_BASE_T entry = { 0 }; - auto entryPtr = structBase( Ldr->InInitializationOrderModuleList.Flink, &_LDR_DATA_TABLE_ENTRY_BASE_T::InInitializationOrderLinks ); - if (!NT_SUCCESS( _process.memory().Read( entryPtr, entry ) )) - return false; + auto entryPtr = structBase( Ldr.InInitializationOrderModuleList.Flink, &_LDR_DATA_TABLE_ENTRY_BASE_T::InInitializationOrderLinks ); + auto entry = _process->memory().Read<_LDR_DATA_TABLE_ENTRY_BASE_T>( entryPtr ); - wchar_t nameBuf[260] = { 0 }; - if (!NT_SUCCESS( _process.memory().Read( entry.BaseDllName.Buffer, entry.BaseDllName.Length + 2, nameBuf ) )) - return false; + wchar_t nameBuf[260] = { }; + _process->memory().Read( entry.BaseDllName.Buffer, entry.BaseDllName.Length + 2, nameBuf ); ULONG NtdllHashIndex = HashString( nameBuf ) & 0x1F; T NtdllBase = static_cast(entry.DllBase); @@ -741,9 +690,7 @@ bool NtLdr::FindLdrpHashTable() T NtdllHashHeadPtr = 0; _LIST_ENTRY_T hashNode = entry.HashLinks; - for (auto e = hashNode.Flink; - e != fieldPtr( entryPtr, &_LDR_DATA_TABLE_ENTRY_BASE_T::HashLinks ); - e = hashNode.Flink) + for (auto e = hashNode.Flink; e != fieldPtr( entryPtr, &_LDR_DATA_TABLE_ENTRY_BASE_T::HashLinks ); e = hashNode.Flink) { if (e >= NtdllBase && e < NtdllEndAddress) { @@ -751,94 +698,77 @@ bool NtLdr::FindLdrpHashTable() break; } - if (!NT_SUCCESS ( _process.memory().Read( hashNode.Flink, hashNode ) )) - return false; + _process->memory().Read( hashNode.Flink, hashNode ); } - if (NtdllHashHeadPtr != 0) - _LdrpHashTable = NtdllHashHeadPtr - NtdllHashIndex * sizeof( _LIST_ENTRY_T ); + if (NtdllHashHeadPtr == 0) + THROW_AND_LOG( "failed to get LdrpHashTableHead ptr" ); - return _LdrpHashTable != 0; + _LdrpHashTable = NtdllHashHeadPtr - NtdllHashIndex * sizeof( _LIST_ENTRY_T ); } /// /// Find LdrpModuleIndex variable under win8 /// -/// true on success template -bool NtLdr::FindLdrpModuleIndexBase() +void NtLdr::FindLdrpModuleIndexBase() { - _PEB_T Peb = { 0 }; - _process.core().peb( &Peb ); + _PEB_T Peb = { }; + _process->core().peb( &Peb ); - if (Peb.ImageBaseAddress != 0) - { - T lastNode = 0; - auto Ldr = _process.memory().Read<_PEB_LDR_DATA2_T>( Peb.Ldr ); - if (!Ldr) - return false; - - auto entryPtr = structBase( Ldr->InInitializationOrderModuleList.Flink, &_LDR_DATA_TABLE_ENTRY_W8::InInitializationOrderLinks ); + if (Peb.ImageBaseAddress == 0) + THROW_WITH_STATUS_AND_LOG( LastNtStatus(), "failed to get process base image address" ); - _RTL_BALANCED_NODE node = { 0 }; - if (!NT_SUCCESS( _process.memory().Read( fieldPtr( entryPtr, &_LDR_DATA_TABLE_ENTRY_W8::BaseAddressIndexNode ), node ) )) - return false; + T lastNode = 0; + auto Ldr = _process->memory().Read<_PEB_LDR_DATA2_T>( Peb.Ldr ); + auto entryPtr = structBase( Ldr.InInitializationOrderModuleList.Flink, &_LDR_DATA_TABLE_ENTRY_W8::InInitializationOrderLinks ); - // Get root node - for(; node.ParentValue; ) - { - // Ignore last few bits - lastNode = node.ParentValue & T( -8 ); - if (!NT_SUCCESS( _process.memory().Read( lastNode, node ) )) - return false; - } + auto node = _process->memory().Read<_RTL_BALANCED_NODE>( fieldPtr( entryPtr, &_LDR_DATA_TABLE_ENTRY_W8::BaseAddressIndexNode ) ); - // Get pointer to root - pe::PEImage ntdllPE; - T* pStart = nullptr; - T* pEnd = nullptr; + // Get root node + for (; node.ParentValue; ) + { + // Ignore last few bits + lastNode = node.ParentValue & T( -8 ); + _process->memory().Read( lastNode, node ); + } - auto pNtdll = _process.modules().GetModule( L"ntdll.dll" ); - std::unique_ptr localBuf( new uint8_t[pNtdll->size] ); - _process.memory().Read( pNtdll->baseAddress, pNtdll->size, localBuf.get() ); + // Get pointer to root + pe::PEImage ntdllPE; + T* pStart = nullptr; + T* pEnd = nullptr; - ntdllPE.Parse( localBuf.get() ); + auto pNtdll = _process->modules().GetModule( L"ntdll.dll" ); + std::unique_ptr localBuf( new uint8_t[pNtdll->size] ); + _process->memory().Read( pNtdll->baseAddress, pNtdll->size, localBuf.get() ); - for (auto& section : ntdllPE.sections()) - if (_stricmp( reinterpret_cast(section.Name), ".data") == 0 ) - { - pStart = reinterpret_cast( localBuf.get() + section.VirtualAddress); - pEnd = reinterpret_cast( localBuf.get() + section.VirtualAddress + section.Misc.VirtualSize); - break; - } + ntdllPE.Parse( localBuf.get() ); - auto iter = std::find( pStart, pEnd, lastNode ); - if (iter != pEnd) + for (auto& section : ntdllPE.sections()) + if (_stricmp( reinterpret_cast(section.Name), ".data" ) == 0) { - _LdrpModuleIndexBase = REBASE( iter, localBuf.get(), pNtdll->baseAddress ); - return true; + pStart = reinterpret_cast(localBuf.get() + section.VirtualAddress); + pEnd = reinterpret_cast(localBuf.get() + section.VirtualAddress + section.Misc.VirtualSize); + break; } - } - return false; + auto iter = std::find( pStart, pEnd, lastNode ); + if (iter == pEnd) + THROW_AND_LOG( "lastNode not found" ); + + _LdrpModuleIndexBase = REBASE( iter, localBuf.get(), pNtdll->baseAddress ); } /// /// Find Loader heap base /// -/// true on success template -bool NtLdr::FindLdrHeap() +void NtLdr::FindLdrHeap() { _PEB_T peb = { }; - if (_process.core().peb( &peb ) != 0) - { + if (_process->core().peb( &peb ) != 0) _LdrHeapBase = peb.ProcessHeap; - return true; - } - - return false; } /// @@ -900,7 +830,7 @@ ptr_t NtLdr::UnlinkFromLdr( const ModuleData& mod ) template ptr_t NtLdr::FindLdrEntry( module_t moduleBase, _LDR_DATA_TABLE_ENTRY_BASE_T* found /*= nullptr*/ ) { - auto native = _process.core().native(); + auto native = _process->core().native(); _PEB_T peb = { }; _PEB_LDR_DATA2_T ldr = { }; _LDR_DATA_TABLE_ENTRY_BASE_T localEntry = { }; @@ -933,18 +863,18 @@ ptr_t NtLdr::FindLdrEntry( module_t moduleBase, _LDR_DATA_TABLE_ENTRY_BASE_T* template void NtLdr::UnlinkListEntry( ptr_t pListLink ) { - T OldFlink = _process.memory().Read( fieldPtr( pListLink, &_LIST_ENTRY_T::Flink ) ).result( 0 ); - T OldBlink = _process.memory().Read( fieldPtr( pListLink, &_LIST_ENTRY_T::Blink ) ).result( 0 ); + T OldFlink = _process->memory().Read( fieldPtr( pListLink, &_LIST_ENTRY_T::Flink ) ); + T OldBlink = _process->memory().Read( fieldPtr( pListLink, &_LIST_ENTRY_T::Blink ) ); // List is empty if (OldBlink == 0 || OldFlink == 0 || OldBlink == OldFlink) return; // OldFlink->Blink = OldBlink; - _process.memory().Write( fieldPtr( OldFlink, &_LIST_ENTRY_T::Blink ), OldBlink ); + _process->memory().Write( fieldPtr( OldFlink, &_LIST_ENTRY_T::Blink ), OldBlink ); // OldBlink->Flink = OldFlink; - _process.memory().Write( fieldPtr( OldBlink, &_LIST_ENTRY_T::Flink ), OldFlink ); + _process->memory().Write( fieldPtr( OldBlink, &_LIST_ENTRY_T::Flink ), OldFlink ); } /// @@ -961,24 +891,20 @@ ptr_t NtLdr::UnlinkTreeNode( const ModuleData& mod, ptr_t ldrEntry, bool noThrea return ldrEntry; auto a = AsmFactory::GetAssembler( mod.type ); - uint64_t result = 0; - - auto RtlRbRemoveNode = _process.modules().GetNtdllExport( "RtlRbRemoveNode" ); - if (!RtlRbRemoveNode) - return 0; + auto RtlRbRemoveNode = _process->modules().GetNtdllExport( "RtlRbRemoveNode" ); a->GenPrologue(); - a->GenCall( RtlRbRemoveNode->procAddress, - { - _LdrpModuleIndexBase, - ldrEntry + offsetOf( &_LDR_DATA_TABLE_ENTRY_W8::BaseAddressIndexNode ) - } ); + a->GenCall( RtlRbRemoveNode.procAddress, + { + _LdrpModuleIndexBase, + ldrEntry + offsetOf( &_LDR_DATA_TABLE_ENTRY_W8::BaseAddressIndexNode ) + } ); - _process.remote().AddReturnWithEvent( *a ); + _process->remote().AddReturnWithEvent( *a ); a->GenEpilogue(); - _process.remote().CreateRPCEnvironment( noThread ? Worker_UseExisting : Worker_CreateNew, true ); - _process.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result ); + _process->remote().CreateRPCEnvironment( noThread ? Worker_UseExisting : Worker_CreateNew, true ); + _process->remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize() ); return ldrEntry; } diff --git a/src/BlackBone/ManualMap/Native/NtLoader.h b/src/BlackBone/ManualMap/Native/NtLoader.h index aab23a82..e2236687 100644 --- a/src/BlackBone/ManualMap/Native/NtLoader.h +++ b/src/BlackBone/ManualMap/Native/NtLoader.h @@ -5,7 +5,6 @@ #include "../../Include/Types.h" #include "../../Include/NativeStructures.h" #include "../../Include/Macro.h" -#include "../../Include/CallResult.h" namespace blackbone { @@ -32,38 +31,34 @@ struct NtLdrEntry : ModuleData class NtLdr { public: - BLACKBONE_API NtLdr( class Process& proc ); + BLACKBONE_API NtLdr( class Process* proc ); /// /// Initialize some loader stuff /// /// Target module type - /// true on success - BLACKBONE_API bool Init( eModType initFor = mt_default ); + BLACKBONE_API void Init( eModType initFor = mt_default ); /// /// Add module to some loader structures /// (LdrpHashTable, LdrpModuleIndex( win8 only ), InMemoryOrderModuleList( win7 only )) /// /// Module data - /// true on success - BLACKBONE_API bool CreateNTReference( NtLdrEntry& mod ); + BLACKBONE_API void CreateNTReference( NtLdrEntry& mod ); /// /// Create thread static TLS array /// /// Module data /// TLS directory of target image - /// Status code - BLACKBONE_API NTSTATUS AddStaticTLSEntry( NtLdrEntry& mod, ptr_t tlsPtr ); + BLACKBONE_API void AddStaticTLSEntry( NtLdrEntry& mod, ptr_t tlsPtr ); /// /// Create module record in LdrpInvertedFunctionTable /// Used to create fake SAFESEH entries /// /// Module data - /// true on success - BLACKBONE_API bool InsertInvertedFunctionTable( NtLdrEntry& mod ); + BLACKBONE_API void InsertInvertedFunctionTable( NtLdrEntry& mod ); /// /// Free static TLS @@ -80,28 +75,25 @@ class NtLdr /// Don't create new threads during unlink /// true on success BLACKBONE_API bool Unlink( const ModuleData& mod, bool noThread = false ); -private: +private: /// /// Find LdrpHashTable[] variable /// - /// true on success template - bool FindLdrpHashTable(); + void FindLdrpHashTable(); /// /// Find LdrpModuleIndex variable under win8 /// - /// true on success template - bool FindLdrpModuleIndexBase(); + void FindLdrpModuleIndexBase(); /// /// Find Loader heap base /// - /// true on success template - bool FindLdrHeap(); + void FindLdrHeap(); /// /// Initialize OS-specific module entry @@ -172,7 +164,7 @@ class NtLdr /// Module type /// Size to allocate /// Allocated address - call_result_t AllocateInHeap( eModType mt, size_t size ); + ptr_t AllocateInHeap( eModType mt, size_t size ); /// /// Get module native node ptr or create new @@ -221,7 +213,7 @@ class NtLdr NtLdr& operator =( const NtLdr& ) = delete; private: - class Process& _process; // Process memory routines + class Process* _process; // Process memory routines ptr_t _LdrpHashTable = 0; // LdrpHashTable address ptr_t _LdrpModuleIndexBase = 0; // LdrpModuleIndex address diff --git a/src/BlackBone/Misc/NameResolve.cpp b/src/BlackBone/Misc/NameResolve.cpp index 1cd68fb6..075144d6 100644 --- a/src/BlackBone/Misc/NameResolve.cpp +++ b/src/BlackBone/Misc/NameResolve.cpp @@ -61,7 +61,7 @@ bool blackbone::NameResolve::InitializeP() PApiSetEntry pDescriptor = pSetMap->entry(i); std::vector vhosts; - wchar_t dllName[MAX_PATH] = { 0 }; + wchar_t dllName[MAX_PATH] = { }; auto nameSize = pSetMap->apiName( pDescriptor, dllName ); std::transform( dllName, dllName + nameSize / sizeof( wchar_t ), dllName, ::towlower ); @@ -106,7 +106,7 @@ NTSTATUS NameResolve::ResolvePath( ) { NTSTATUS status = STATUS_SUCCESS; - wchar_t tmpPath[4096] = { 0 }; + wchar_t tmpPath[4096] = { }; std::wstring completePath; path = Utils::ToLower( std::move( path ) ); @@ -139,7 +139,7 @@ NTSTATUS NameResolve::ResolvePath( } else if (flags & EnsureFullPath) { - wchar_t sys_path[255] = { 0 }; + wchar_t sys_path[255] = { }; GetSystemDirectoryW( sys_path, 255 ); path = std::wstring( sys_path ) + L"\\" + path; @@ -175,8 +175,8 @@ NTSTATUS NameResolve::ResolvePath( { for (int i = 0; i < 0x1000 && res == ERROR_SUCCESS; i++) { - wchar_t value_name[255] = { 0 }; - wchar_t value_data[255] = { 0 }; + wchar_t value_name[255] = { }; + wchar_t value_data[255] = { }; DWORD dwSize = 255; DWORD dwType = 0; @@ -185,7 +185,7 @@ NTSTATUS NameResolve::ResolvePath( if (_wcsicmp( value_data, filename.c_str() ) == 0) { - wchar_t sys_path[255] = { 0 }; + wchar_t sys_path[255] = { }; dwSize = 255; // In Win10 DllDirectory value got screwed, so less reliable method is used @@ -300,9 +300,9 @@ NTSTATUS NameResolve::ResolvePath( /// NTSTATUS NameResolve::ProbeSxSRedirect( std::wstring& path, Process& proc, HANDLE actx /*= INVALID_HANDLE_VALUE*/ ) { - UNICODE_STRING OriginalName = { 0 }; - UNICODE_STRING DllName1 = { 0 }; - UNICODE_STRING DllName2 = { 0 }; + UNICODE_STRING OriginalName = { }; + UNICODE_STRING DllName1 = { }; + UNICODE_STRING DllName2 = { }; PUNICODE_STRING pPath = nullptr; ULONG_PTR cookie = 0; wchar_t wBuf[255]; diff --git a/src/BlackBone/Misc/NameResolve.h b/src/BlackBone/Misc/NameResolve.h index 8c1f0580..2bff5545 100644 --- a/src/BlackBone/Misc/NameResolve.h +++ b/src/BlackBone/Misc/NameResolve.h @@ -11,9 +11,7 @@ namespace blackbone { class NameResolve -{ - using mapApiSchema = std::unordered_map>; - +{ public: enum eResolveFlag { @@ -27,6 +25,10 @@ class NameResolve public: BLACKBONE_API ~NameResolve() = default; + // Ensure singleton + NameResolve( const NameResolve& ) = delete; + NameResolve& operator =( const NameResolve& ) = delete; + BLACKBONE_API static NameResolve& Instance(); /// @@ -63,12 +65,6 @@ class NameResolve /// BLACKBONE_API NTSTATUS ProbeSxSRedirect( std::wstring& path, class Process& proc, HANDLE actx = INVALID_HANDLE_VALUE ); -private: - // Ensure singleton - NameResolve() = default; - NameResolve( const NameResolve& ) = delete; - NameResolve& operator =( const NameResolve& ) = delete; - /// /// Gets the process executable directory /// @@ -76,6 +72,9 @@ class NameResolve /// Process executable directory std::wstring GetProcessDirectory( DWORD pid ); +private: + NameResolve() = default; + /// /// OS dependent api set initialization /// @@ -84,6 +83,8 @@ class NameResolve bool InitializeP(); private: + using mapApiSchema = std::unordered_map>; + mapApiSchema _apiSchema; // Api schema table }; diff --git a/src/BlackBone/Misc/StackTrace.h b/src/BlackBone/Misc/StackTrace.h new file mode 100644 index 00000000..cce77230 --- /dev/null +++ b/src/BlackBone/Misc/StackTrace.h @@ -0,0 +1,248 @@ +#pragma once +#include "../Config.h" +#include "../Include/Winheaders.h" +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "psapi.lib") +#pragma comment(lib, "dbghelp.lib") + +namespace blackbone::debug +{ + +struct module_data +{ + std::string image_name; + std::string module_name; + void* base_address ; + DWORD load_size = 0; + + explicit module_data( void* base = nullptr ) + : base_address( base ) + { + } + + bool operator <( const module_data& rhs ) const + { + return base_address < rhs.base_address; + } +}; + +class symbol +{ +public: + symbol( HANDLE process, DWORD64 address ) + { + memset( &_sym, 0x00, sizeof( _reserved ) ); + + _sym.SizeOfStruct = sizeof( _sym ); + _sym.MaxNameLength = max_name_len; + + SymGetSymFromAddr64( process, address, &_displacement, &_sym ); + } + + std::string name() const + { + return _sym.Name; + } + + std::string undecorated_name() const + { + if (*_sym.Name == '\0') + return ""; + + std::vector und_name( max_name_len ); + UnDecorateSymbolName( _sym.Name, &und_name[0], max_name_len, UNDNAME_COMPLETE ); + return std::string( &und_name[0], strlen( &und_name[0] ) ); + } + + DWORD64 offset( DWORD64 pc ) + { + return pc - _sym.Address; + } + +private: + static constexpr int max_name_len = 1024; + DWORD64 _displacement = 0; + union + { + IMAGEHLP_SYMBOL64 _sym; + uint8_t _reserved[sizeof( IMAGEHLP_SYMBOL64 ) + max_name_len]; + }; +}; + +class get_mod_info +{ +public: + get_mod_info( HANDLE process ) + : _process( process ) + { + } + + module_data operator()( HMODULE module ) + { + module_data ret; + char temp[buffer_length] = { }; + MODULEINFO mi = { }; + + GetModuleInformation( _process, module, &mi, sizeof( mi ) ); + ret.base_address = mi.lpBaseOfDll; + ret.load_size = mi.SizeOfImage; + + GetModuleFileNameExA( _process, module, temp, sizeof( temp ) ); + ret.image_name = temp; + + GetModuleBaseNameA( _process, module, temp, sizeof( temp ) ); + ret.module_name = temp; + + SymLoadModule64( + _process, nullptr, + ret.image_name.data(), + ret.module_name.data(), + reinterpret_cast(ret.base_address), + ret.load_size + ); + + return ret; + } + +private: + HANDLE _process = nullptr; + static constexpr int buffer_length = 4096; +}; + +class StackTrace +{ +public: + static std::string Capture( PCONTEXT ctx ) + { + HANDLE process = GetCurrentProcess(); + HANDLE thread = GetCurrentThread(); + + std::ostringstream result; + + if (!SymInitializeW( process, nullptr, false )) + return "Error. Failed to initialize symbols"; + + DWORD symOptions = SymGetOptions(); + symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME; + SymSetOptions( symOptions ); + + auto modules = load_modules( process ); + DWORD image_type = ImageNtHeader( GetModuleHandleW( nullptr ) )->FileHeader.Machine; + DWORD64 lastPC = 0; + +#ifdef USE64 + int skipCount = 2; +#else + int skipCount = 1; +#endif + + for(STACKFRAME64 frame = first_frame( ctx ); frame.AddrReturn.Offset != 0;) + { + // Sometimes same frame gets walked twice, don't print it second time + if(lastPC != frame.AddrPC.Offset) + { + if (skipCount <= 0) + dump_frame( process, modules, frame, result ); + else + skipCount--; + } + + lastPC = frame.AddrPC.Offset; + if (!StackWalk64( image_type, process, thread, &frame, ctx, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr )) + break; + } + + SymCleanup( process ); + + return result.str(); + } + +private: + static std::vector load_modules( HANDLE process ) + { + DWORD cbNeeded = 0; + std::vector modules; + std::vector module_handles( 1 ); + + EnumProcessModules( process, &module_handles[0], static_cast(module_handles.size() * sizeof( HMODULE )), &cbNeeded ); + module_handles.resize( cbNeeded / sizeof( HMODULE ) ); + EnumProcessModules( process, &module_handles[0], static_cast(module_handles.size() * sizeof( HMODULE )), &cbNeeded ); + + std::transform( module_handles.begin(), module_handles.end(), std::back_inserter( modules ), get_mod_info( process ) ); + std::sort( modules.begin(), modules.end(), std::less() ); + + return modules; + } + + static module_data find_module( const std::vector& modules, void* pc ) + { + auto range = std::upper_bound( modules.begin(), modules.end(), module_data( pc ) ); + if (range != modules.end()) + { + if(range != modules.begin()) + range--; + + if (range->base_address <= pc && pc < static_cast(range->base_address) + range->load_size) + return *range; + } + + return module_data(); + } + + static STACKFRAME64 first_frame( PCONTEXT ctx ) + { + STACKFRAME64 frame = { }; + +#ifdef USE64 + frame.AddrPC.Offset = ctx->Rip; + frame.AddrStack.Offset = ctx->Rsp; + frame.AddrFrame.Offset = ctx->Rbp; +#else + frame.AddrPC.Offset = ctx->Eip; + frame.AddrStack.Offset = ctx->Esp; + frame.AddrFrame.Offset = ctx->Ebp; +#endif + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrStack.Mode = AddrModeFlat; + frame.AddrFrame.Mode = AddrModeFlat; + + // Dummy value + frame.AddrReturn.Offset = 0xDEAD; + + return frame; + } + + static void dump_frame( HANDLE process, const std::vector& modules, const STACKFRAME64& frame, std::ostringstream& printer ) + { + if (frame.AddrPC.Offset != 0) + { + printer << " "; + + auto mod = find_module( modules, reinterpret_cast(frame.AddrPC.Offset) ); + if (!mod.module_name.empty()) + printer << mod.module_name << "!"; + + symbol sym( process, frame.AddrPC.Offset ); + printer << sym.undecorated_name(); + + IMAGEHLP_LINE64 line = { }; + line.SizeOfStruct = sizeof( line ); + DWORD offset; + + if (SymGetLineFromAddr64( process, frame.AddrPC.Offset, &offset, &line )) + printer << " " << line.FileName << ":" << line.LineNumber; + + printer << std::endl; + } + else + printer << "(No Symbols: PC == 0)"; + } +}; + +} diff --git a/src/BlackBone/Misc/Trace.hpp b/src/BlackBone/Misc/Trace.hpp index 7b6c6fc6..929ffd61 100644 --- a/src/BlackBone/Misc/Trace.hpp +++ b/src/BlackBone/Misc/Trace.hpp @@ -14,9 +14,12 @@ namespace blackbone inline void DoTraceV( const char* fmt, va_list va_args ) { - char buf[2048], userbuf[1024]; - vsprintf_s( userbuf, fmt, va_args ); - sprintf_s( buf, "BlackBone: %s\r\n", userbuf ); + constexpr size_t buf_size = 1024 * 1024; + static auto buf = static_cast(VirtualAlloc( nullptr, buf_size, MEM_COMMIT, PAGE_READWRITE )); + static auto userbuf = static_cast(VirtualAlloc( nullptr, buf_size, MEM_COMMIT, PAGE_READWRITE )); + + vsprintf_s( userbuf, buf_size, fmt, va_args ); + sprintf_s( buf, buf_size, "BlackBone: %s\r\n", userbuf ); OutputDebugStringA( buf ); #ifdef CONSOLE_TRACE @@ -26,9 +29,12 @@ inline void DoTraceV( const char* fmt, va_list va_args ) inline void DoTraceV( const wchar_t* fmt, va_list va_args ) { - wchar_t buf[2048], userbuf[1024]; - vswprintf_s( userbuf, fmt, va_args ); - swprintf_s( buf, L"BlackBone: %ls\r\n", userbuf ); + constexpr size_t buf_size = 1024 * 1024; + static auto buf = static_cast(VirtualAlloc( nullptr, buf_size, MEM_COMMIT, PAGE_READWRITE )); + static auto userbuf = static_cast(VirtualAlloc( nullptr, buf_size, MEM_COMMIT, PAGE_READWRITE )); + + vswprintf_s( userbuf, buf_size / sizeof( wchar_t ), fmt, va_args ); + swprintf_s( buf, buf_size / sizeof( wchar_t ), L"BlackBone: %ls\r\n", userbuf ); OutputDebugStringW( buf ); #ifdef CONSOLE_TRACE diff --git a/src/BlackBone/Misc/Utils.cpp b/src/BlackBone/Misc/Utils.cpp index 3d547ba9..c07dcd5a 100644 --- a/src/BlackBone/Misc/Utils.cpp +++ b/src/BlackBone/Misc/Utils.cpp @@ -36,7 +36,7 @@ std::string Utils::WstringToUTF8( const std::wstring& str ) /// wide char string std::wstring Utils::AnsiToWstring( const std::string& input, DWORD locale /*= CP_ACP*/ ) { - wchar_t buf[2048] = { 0 }; + wchar_t buf[2048] = { }; MultiByteToWideChar( locale, 0, input.c_str(), (int)input.length(), buf, ARRAYSIZE( buf ) ); return buf; } @@ -49,7 +49,7 @@ std::wstring Utils::AnsiToWstring( const std::string& input, DWORD locale /*= CP /// ANSI string std::string Utils::WstringToAnsi( const std::wstring& input, DWORD locale /*= CP_ACP*/ ) { - char buf[2048] = { 0 }; + char buf[2048] = { }; WideCharToMultiByte( locale, 0, input.c_str(), (int)input.length(), buf, ARRAYSIZE( buf ), nullptr, nullptr ); return buf; } @@ -62,7 +62,7 @@ std::string Utils::WstringToAnsi( const std::wstring& input, DWORD locale /*= CP /// Formatted string std::wstring Utils::FormatString( const wchar_t* fmt, ... ) { - wchar_t buf[4096] = { 0 }; + wchar_t buf[4096] = { }; va_list vl; va_start( vl, fmt ); @@ -119,7 +119,7 @@ std::wstring Utils::GetParent( const std::wstring& path ) /// Exe directory std::wstring Utils::GetExeDirectory() { - wchar_t imgName[MAX_PATH] = { 0 }; + wchar_t imgName[MAX_PATH] = { }; DWORD len = ARRAYSIZE(imgName); auto pFunc = GET_IMPORT( QueryFullProcessImageNameW ); @@ -165,31 +165,46 @@ std::wstring Utils::ToLower( std::wstring str ) return str; } -/// -/// Get system error description -/// -/// The code. -/// Error message -std::wstring Utils::GetErrorDescription( NTSTATUS code ) +template +std::basic_string GetErrorDescriptionT( NTSTATUS code ) { - LPWSTR lpMsgBuf = nullptr; + std::basic_string result; + T* lpMsgBuf = nullptr; - if (FormatMessageW( + if (Formatter( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandleW( L"ntdll.dll" ), code, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), - (LPWSTR)&lpMsgBuf, 0, NULL ) != 0) + (T*)&lpMsgBuf, 0, NULL ) != 0) { - std::wstring ret( lpMsgBuf ); - + result = lpMsgBuf; LocalFree( lpMsgBuf ); - return ret; } - return L""; + return result; +} + +/// +/// Get system error description +/// +/// The code. +/// Error message +std::wstring Utils::GetErrorDescription( NTSTATUS code ) +{ + return GetErrorDescriptionT( code ); +} + +/// +/// Get system error description +/// +/// The code. +/// Error message +std::string Utils::GetErrorDescriptionA( NTSTATUS code ) +{ + return GetErrorDescriptionT( code ); } /// @@ -203,4 +218,13 @@ bool Utils::FileExists( const std::wstring& path ) } +std::string Utils::NameOrdToString( const char * name_ord ) +{ + if (reinterpret_cast(name_ord) <= 0xFFFF) + return "ordinal " + std::to_string( reinterpret_cast(name_ord) ); + + return name_ord; +} + + } \ No newline at end of file diff --git a/src/BlackBone/Misc/Utils.h b/src/BlackBone/Misc/Utils.h index aa1c6ed8..353e3788 100644 --- a/src/BlackBone/Misc/Utils.h +++ b/src/BlackBone/Misc/Utils.h @@ -1,5 +1,6 @@ #pragma once +#include "../Config.h" #include "../Include/Winheaders.h" #include #include @@ -89,6 +90,7 @@ class Utils /// The code. /// Error message BLACKBONE_API static std::wstring GetErrorDescription( NTSTATUS code ); + BLACKBONE_API static std::string GetErrorDescriptionA( NTSTATUS code ); /// /// Check if file exists @@ -96,6 +98,11 @@ class Utils /// Full-qualified file path /// true if exists BLACKBONE_API static bool FileExists( const std::wstring& path ); + + /// + /// Convert export name or ordinal to string + /// + BLACKBONE_API static std::string NameOrdToString( const char* name_ord ); }; @@ -110,7 +117,13 @@ class CriticalSection InitializeCriticalSection( &_native ); } - BLACKBONE_API ~CriticalSection() + BLACKBONE_API CriticalSection( const CriticalSection& ) = default; + BLACKBONE_API CriticalSection( CriticalSection&& ) = default; + + BLACKBONE_API CriticalSection& operator =( const CriticalSection& ) = default; + BLACKBONE_API CriticalSection& operator =( CriticalSection&& ) = default; + + BLACKBONE_API ~CriticalSection() { DeleteCriticalSection( &_native ); } @@ -137,20 +150,22 @@ class CSLock { public: BLACKBONE_API CSLock( CriticalSection& cs ) - : _cs( cs ) + : _cs( cs ) { cs.lock(); } + BLACKBONE_API CSLock( const CSLock& ) = delete; + BLACKBONE_API CSLock( CSLock&& ) = default; + + BLACKBONE_API CSLock& operator = ( const CSLock& ) = delete; + BLACKBONE_API CSLock& operator = ( CSLock&& ) = default; + BLACKBONE_API ~CSLock() { _cs.unlock(); } -private: - CSLock( const CSLock& ) = delete; - CSLock& operator = ( const CSLock& ) = delete; - private: CriticalSection& _cs; }; @@ -187,22 +202,22 @@ class FsRedirector #if _MSC_VER >= 1900 namespace tuple_detail { - template - void visit_each( T&& t, F f, std::index_sequence ) { auto l = { (f( std::get( t ) ), 0)... }; } +template +void visit_each( T&& t, F f, std::index_sequence ) { auto l = { (f( std::get( t ) ), 0)... }; } - template - void copyTuple( std::tuple const& from, std::vector& to ) +template +void copyTuple( std::tuple const& from, std::vector& to ) +{ + auto func = [&to]( auto& v ) { - auto func = [&to]( auto& v ) - { - auto ptr = to.size(); - to.resize( ptr + sizeof( v ) ); - memcpy( to.data() + ptr, &v, sizeof( v ) ); - return 0; - }; - - visit_each( from, func, std::index_sequence_for() ); - } + auto ptr = to.size(); + to.resize( ptr + sizeof( v ) ); + memcpy( to.data() + ptr, &v, sizeof( v ) ); + return 0; + }; + + visit_each( from, func, std::index_sequence_for() ); +} } #endif } \ No newline at end of file diff --git a/src/BlackBone/PE/ImageNET.cpp b/src/BlackBone/PE/ImageNET.cpp index f56719e7..169f15f1 100644 --- a/src/BlackBone/PE/ImageNET.cpp +++ b/src/BlackBone/PE/ImageNET.cpp @@ -90,9 +90,9 @@ bool ImageNET::Parse( mapMethodRVA* methods /*= nullptr*/ ) HCORENUM hceTypeDefs = 0; mdToken tExtends = 0; HCORENUM hceMethods = 0; - mdTypeRef rTypeDefs[10] = { 0 }; - WCHAR wcName[1024] = { 0 }; - mdToken rTokens[10] = { 0 }; + mdTypeRef rTypeDefs[10] = { }; + WCHAR wcName[1024] = { }; + mdToken rTokens[10] = { }; while (SUCCEEDED( _pMetaImport->EnumTypeDefs( &hceTypeDefs, rTypeDefs, ARRAYSIZE( rTypeDefs ), &dwcTypeDefs ) ) && dwcTypeDefs > 0) @@ -107,7 +107,7 @@ bool ImageNET::Parse( mapMethodRVA* methods /*= nullptr*/ ) && dwcTokens > 0) { DWORD dwCodeRVA, dwAttr; - WCHAR wmName[1024] = { 0 }; + WCHAR wmName[1024] = { }; PCCOR_SIGNATURE pbySigBlob = nullptr; for (UINT j = 0; j < dwcTokens; j++) @@ -155,7 +155,7 @@ std::wstring ImageNET::GetImageRuntimeVer( const wchar_t* ImagePath ) // Legacy runtime. Get exact required version if(!clrCreate) { - wchar_t ver[64] = { 0 }; + wchar_t ver[64] = { }; DWORD bytes = 0; auto clrGetVer = reinterpret_cast( diff --git a/src/BlackBone/PE/PEImage.cpp b/src/BlackBone/PE/PEImage.cpp index f929789a..dcfb9b92 100644 --- a/src/BlackBone/PE/PEImage.cpp +++ b/src/BlackBone/PE/PEImage.cpp @@ -470,10 +470,10 @@ int PEImage::GetTLSCallbacks( module_t targetBase, std::vector& result ) /// Status code NTSTATUS PEImage::PrepareACTX( const wchar_t* filepath /*= nullptr*/ ) { - wchar_t tempPath[256] = { 0 }; + wchar_t tempPath[256] = { }; uint32_t manifestSize = 0; - ACTCTXW act = { 0 }; + ACTCTXW act = { }; act.cbSize = sizeof( act ); // No manifest found, skip @@ -486,7 +486,7 @@ NTSTATUS PEImage::PrepareACTX( const wchar_t* filepath /*= nullptr*/ ) // if (filepath == nullptr) { - wchar_t tempDir[256] = { 0 }; + wchar_t tempDir[256] = { }; GetTempPathW( ARRAYSIZE( tempDir ), tempDir ); if (GetTempFileNameW( tempDir, L"ImageManifest", 0, tempPath ) == 0) @@ -533,7 +533,7 @@ NTSTATUS PEImage::PrepareACTX( const wchar_t* filepath /*= nullptr*/ ) /// Get manifest from image data /// /// Manifest size -/// Mmanifest ID +/// Manifest ID /// Manifest data void* PEImage::GetManifest( uint32_t& size, int32_t& manifestID ) { diff --git a/src/BlackBone/PE/PEImage.h b/src/BlackBone/PE/PEImage.h index 59eaa530..acab6a16 100644 --- a/src/BlackBone/PE/PEImage.h +++ b/src/BlackBone/PE/PEImage.h @@ -307,7 +307,7 @@ class PEImage /// Get manifest from image data /// /// Manifest size - /// Mmanifest ID + /// Manifest ID /// Manifest data void* GetManifest( uint32_t& size, int32_t& manifestID ); diff --git a/src/BlackBone/Patterns/PatternSearch.cpp b/src/BlackBone/Patterns/PatternSearch.cpp index ec517df7..3668821d 100644 --- a/src/BlackBone/Patterns/PatternSearch.cpp +++ b/src/BlackBone/Patterns/PatternSearch.cpp @@ -48,7 +48,7 @@ bool PatternSearch::SearchWithHandler( uint8_t wildcard, void* scanStart, size_t scanSize, - MatchHandler handler, + MatchHandler handler, ptr_t value_offset /*= 0*/ ) const { @@ -70,11 +70,11 @@ bool PatternSearch::SearchWithHandler( break; if (value_offset != 0) - running = !handler( REBASE( res, scanStart, value_offset ) ); + running = !handler( REBASE( res, scanStart, value_offset ) ); //out.emplace_back( REBASE( res, scanStart, value_offset ) ); else //out.emplace_back( reinterpret_cast(res) ); - running = !handler( reinterpret_cast(res) ); + running = !handler( reinterpret_cast(res) ); cstart = res + _pattern.size(); } @@ -92,13 +92,13 @@ bool PatternSearch::SearchWithHandler( /// Value that will be added to resulting addresses /// Number of found addresses bool PatternSearch::SearchWithHandler( - void* scanStart, - size_t scanSize, - MatchHandler handler, - ptr_t value_offset /*= 0*/ - ) const + void* scanStart, + size_t scanSize, + MatchHandler handler, + ptr_t value_offset /*= 0*/ + ) const { - size_t bad_char_skip[UCHAR_MAX + 1]; + size_t bad_char_skip[UCHAR_MAX + 1]; const uint8_t* haystack = reinterpret_cast(scanStart); const uint8_t* haystackEnd = haystack + scanSize - _pattern.size(); @@ -106,8 +106,8 @@ bool PatternSearch::SearchWithHandler( uintptr_t nlen = _pattern.size(); uintptr_t scan = 0; uintptr_t last = nlen - 1; - size_t alignMask = 0xFFFFFFFFFFFFFFFFL << logAlignment; - size_t alignOffs = (1 << logAlignment) - 1; + size_t alignMask = 0xFFFFFFFFFFFFFFFFL << logAlignment; + size_t alignOffs = (1 << logAlignment) - 1; // // Preprocess @@ -131,10 +131,10 @@ bool PatternSearch::SearchWithHandler( { if (value_offset != 0) //out.emplace_back( REBASE( haystack, scanStart, value_offset ) ); - running = !handler( REBASE( haystack, scanStart, value_offset ) ); + running = !handler( REBASE( haystack, scanStart, value_offset ) ); else //out.emplace_back( reinterpret_cast(haystack) ); - running = !handler( reinterpret_cast(haystack) ); + running = !handler( reinterpret_cast(haystack) ); break; } @@ -143,7 +143,7 @@ bool PatternSearch::SearchWithHandler( haystack += bad_char_skip[haystack[last]]; if (logAlignment != 0) { - haystack = (const uint8_t*) (size_t(haystack+alignOffs) & alignMask); + haystack = (const uint8_t*) (size_t(haystack+alignOffs) & alignMask); } } @@ -167,16 +167,12 @@ bool PatternSearch::SearchRemoteWithHandler( MatchHandler handler ) const { - uint8_t *pBuffer = reinterpret_cast(VirtualAlloc( NULL, scanSize, MEM_COMMIT, PAGE_READWRITE )); - - bool stopped = false; - if (pBuffer && remote.memory().Read( scanStart, scanSize, pBuffer ) == STATUS_SUCCESS) - stopped = SearchWithHandler( wildcard, pBuffer, scanSize, handler, scanStart ); - - if (pBuffer) - VirtualFree( pBuffer, 0, MEM_RELEASE ); + auto pBuffer = make_raw_ptr( scanSize ); + if (!pBuffer) + THROW_WITH_STATUS_AND_LOG( LastNtStatus(), "failed to allocate local buffer" ); - return stopped; + remote.memory().Read( scanStart, scanSize, pBuffer.get() ); + return SearchWithHandler( wildcard, pBuffer.get(), scanSize, handler, scanStart ); } /// @@ -194,16 +190,12 @@ bool PatternSearch::SearchRemoteWithHandler( MatchHandler handler ) const { - uint8_t *pBuffer = reinterpret_cast(VirtualAlloc( NULL, scanSize, MEM_COMMIT, PAGE_READWRITE )); + auto pBuffer = make_raw_ptr( scanSize ); + if (!pBuffer) + THROW_WITH_STATUS_AND_LOG( LastNtStatus(), "failed to allocate local buffer" ); - bool stopped = false; - if (pBuffer && remote.memory().Read( scanStart, scanSize, pBuffer ) == STATUS_SUCCESS) - stopped = SearchWithHandler( pBuffer, scanSize, handler, scanStart ); - - if (pBuffer) - VirtualFree( pBuffer, 0, MEM_RELEASE ); - - return stopped; + remote.memory().Read( scanStart, scanSize, pBuffer.get() ); + return SearchWithHandler( pBuffer.get(), scanSize, handler, scanStart ); } /// @@ -221,9 +213,9 @@ bool PatternSearch::SearchRemoteWholeWithHandler( MatchHandler handler ) const { - MEMORY_BASIC_INFORMATION64 mbi = { 0 }; + MEMORY_BASIC_INFORMATION64 mbi = { }; size_t bufsize = 1 * 1024 * 1024; // 1 MB - uint8_t *buf = reinterpret_cast(VirtualAlloc( 0, bufsize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE )); + auto buf = make_raw_ptr( bufsize ); auto native = remote.core().native(); @@ -234,7 +226,7 @@ bool PatternSearch::SearchRemoteWholeWithHandler( if (status == STATUS_INVALID_PARAMETER || status == STATUS_ACCESS_DENIED) break; - else if (status != STATUS_SUCCESS) + if (status != STATUS_SUCCESS) continue; // Filter regions @@ -245,27 +237,24 @@ bool PatternSearch::SearchRemoteWholeWithHandler( if (mbi.RegionSize > bufsize) { bufsize = static_cast(mbi.RegionSize); - VirtualFree( buf, 0, MEM_RELEASE ); - buf = reinterpret_cast(VirtualAlloc( 0, bufsize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE )); + VirtualFree( buf.get(), 0, MEM_RELEASE ); + buf = make_raw_ptr( bufsize ); } - if (remote.memory().Read( memptr, static_cast(mbi.RegionSize), buf ) != STATUS_SUCCESS) + if (!NT_SUCCESS( remote.memory().ReadNoThrow( memptr, static_cast(mbi.RegionSize), buf.get() ) )) continue; if (useWildcard) - running = !SearchWithHandler( wildcard, buf, static_cast(mbi.RegionSize), handler, memptr ); + running = !SearchWithHandler( wildcard, buf.get(), static_cast(mbi.RegionSize), handler, memptr ); else - running = !SearchWithHandler( buf, static_cast(mbi.RegionSize), handler, memptr ); + running = !SearchWithHandler( buf.get(), static_cast(mbi.RegionSize), handler, memptr ); } - VirtualFree( buf, 0, MEM_RELEASE ); - return !running; } - /// /// Default pattern matching with wildcards. /// std::search is approximately 2x faster than naive approach. @@ -282,16 +271,16 @@ size_t PatternSearch::Search( size_t scanSize, std::vector& out, ptr_t value_offset /*= 0*/, - size_t maxMatches /*= SIZE_MAX*/ + size_t maxMatches /*= SIZE_MAX*/ ) const { - if (out.size() >= maxMatches) - return out.size(); + if (out.size() >= maxMatches) + return out.size(); - auto handler = std::bind(PatternSearch::collectAllMatchHandler, std::placeholders::_1, std::ref(out), maxMatches); - SearchWithHandler(wildcard, scanStart, scanSize, handler, value_offset); + auto handler = std::bind(collectAllMatchHandler, std::placeholders::_1, std::ref(out), maxMatches); + SearchWithHandler(wildcard, scanStart, scanSize, handler, value_offset); - return out.size(); + return out.size(); } /// @@ -308,16 +297,16 @@ size_t PatternSearch::Search( size_t scanSize, std::vector& out, ptr_t value_offset /*= 0*/, - size_t maxMatches /*= SIZE_MAX*/ + size_t maxMatches /*= SIZE_MAX*/ ) const { - if (out.size() >= maxMatches) - return out.size(); + if (out.size() >= maxMatches) + return out.size(); - auto handler = std::bind(PatternSearch::collectAllMatchHandler, std::placeholders::_1, std::ref(out), maxMatches); - SearchWithHandler(scanStart, scanSize, handler, value_offset); + auto handler = std::bind(PatternSearch::collectAllMatchHandler, std::placeholders::_1, std::ref(out), maxMatches); + SearchWithHandler(scanStart, scanSize, handler, value_offset); - return out.size(); + return out.size(); } /// @@ -335,16 +324,16 @@ size_t PatternSearch::SearchRemote( ptr_t scanStart, size_t scanSize, std::vector& out, - size_t maxMatches /*= SIZE_MAX*/ + size_t maxMatches /*= SIZE_MAX*/ ) const { - if (out.size() >= maxMatches) - return out.size(); + if (out.size() >= maxMatches) + return out.size(); - auto handler = std::bind(PatternSearch::collectAllMatchHandler, std::placeholders::_1, std::ref(out), maxMatches); - SearchRemoteWithHandler(remote, wildcard, scanStart, scanSize, handler); + auto handler = std::bind(PatternSearch::collectAllMatchHandler, std::placeholders::_1, std::ref(out), maxMatches); + SearchRemoteWithHandler(remote, wildcard, scanStart, scanSize, handler); - return out.size(); + return out.size(); } /// @@ -360,16 +349,16 @@ size_t PatternSearch::SearchRemote( ptr_t scanStart, size_t scanSize, std::vector& out, - size_t maxMatches /*= SIZE_MAX*/ + size_t maxMatches /*= SIZE_MAX*/ ) const { - if (out.size() >= maxMatches) - return out.size(); + if (out.size() >= maxMatches) + return out.size(); - auto handler = std::bind(PatternSearch::collectAllMatchHandler, std::placeholders::_1, std::ref(out), maxMatches); - SearchRemoteWithHandler(remote, scanStart, scanSize, handler); + auto handler = std::bind(PatternSearch::collectAllMatchHandler, std::placeholders::_1, std::ref(out), maxMatches); + SearchRemoteWithHandler(remote, scanStart, scanSize, handler); - return out.size(); + return out.size(); } /// @@ -385,18 +374,18 @@ size_t PatternSearch::SearchRemoteWhole( bool useWildcard, uint8_t wildcard, std::vector& out, - size_t maxMatches /*= SIZE_MAX*/ + size_t maxMatches /*= SIZE_MAX*/ ) const { - out.clear(); + out.clear(); - if (out.size() >= maxMatches) - return out.size(); + if (out.size() >= maxMatches) + return out.size(); - auto handler = std::bind(PatternSearch::collectAllMatchHandler, std::placeholders::_1, std::ref(out), maxMatches); - SearchRemoteWholeWithHandler(remote, useWildcard, wildcard, handler); + auto handler = std::bind(PatternSearch::collectAllMatchHandler, std::placeholders::_1, std::ref(out), maxMatches); + SearchRemoteWholeWithHandler(remote, useWildcard, wildcard, handler); - return out.size(); + return out.size(); } diff --git a/src/BlackBone/Patterns/PatternSearch.h b/src/BlackBone/Patterns/PatternSearch.h index 429d8666..52b37406 100644 --- a/src/BlackBone/Patterns/PatternSearch.h +++ b/src/BlackBone/Patterns/PatternSearch.h @@ -13,17 +13,17 @@ namespace blackbone class PatternSearch { public: - /// - /// Callback to handle a matching address for the Search*WithHandler() methods. - /// If the handler returns true, the search is stopped, else the search continues. - /// - typedef std::function MatchHandler; + /// + /// Callback to handle a matching address for the Search*WithHandler() methods. + /// If the handler returns true, the search is stopped, else the search continues. + /// + typedef std::function MatchHandler; public: - // logAlignment can be used to speed-up the search in some cases. For example, if you know that the start of the pattern - // is always 8-byte-aligned, you can pass logAlignment=3 (2^3 = 8) to skip searching at all addresses that aren't multiples - // of 8. Note that for smaller alignments and depending on the exact pattern, this may not always be faster (it may even be - // a tiny bit slower), so profile it if you care about performance. + // logAlignment can be used to speed-up the search in some cases. For example, if you know that the start of the pattern + // is always 8-byte-aligned, you can pass logAlignment=3 (2^3 = 8) to skip searching at all addresses that aren't multiples + // of 8. Note that for smaller alignments and depending on the exact pattern, this may not always be faster (it may even be + // a tiny bit slower), so profile it if you care about performance. BLACKBONE_API PatternSearch( const std::vector& pattern, size_t logAlignment = 0 ); BLACKBONE_API PatternSearch( const std::initializer_list&& pattern, size_t logAlignment = 0 ); BLACKBONE_API PatternSearch( const std::string& pattern, size_t logAlignment = 0 ); @@ -46,7 +46,7 @@ class PatternSearch uint8_t wildcard, void* scanStart, size_t scanSize, - MatchHandler handler, + MatchHandler handler, ptr_t value_offset = 0 ) const; @@ -62,7 +62,7 @@ class PatternSearch BLACKBONE_API bool SearchWithHandler( void* scanStart, size_t scanSize, - MatchHandler handler, + MatchHandler handler, ptr_t value_offset = 0 ) const; @@ -135,7 +135,7 @@ class PatternSearch size_t scanSize, std::vector& out, ptr_t value_offset = 0, - size_t maxMatches = SIZE_MAX + size_t maxMatches = SIZE_MAX ) const; /// @@ -153,7 +153,7 @@ class PatternSearch size_t scanSize, std::vector& out, ptr_t value_offset = 0, - size_t maxMatches = SIZE_MAX + size_t maxMatches = SIZE_MAX ) const; /// @@ -172,7 +172,7 @@ class PatternSearch ptr_t scanStart, size_t scanSize, std::vector& out, - size_t maxMatches = SIZE_MAX + size_t maxMatches = SIZE_MAX ) const; /// @@ -189,7 +189,7 @@ class PatternSearch ptr_t scanStart, size_t scanSize, std::vector& out, - size_t maxMatches = SIZE_MAX + size_t maxMatches = SIZE_MAX ) const; /// @@ -206,14 +206,14 @@ class PatternSearch bool useWildcard, uint8_t wildcard, std::vector& out, - size_t maxMatches = SIZE_MAX + size_t maxMatches = SIZE_MAX ) const; private: static inline bool collectAllMatchHandler(ptr_t addr, std::vector& out, size_t maxMatches) { - out.emplace_back(addr); - return out.size() >= maxMatches; + out.emplace_back(addr); + return out.size() >= maxMatches; } private: diff --git a/src/BlackBone/Process/MemBlock.cpp b/src/BlackBone/Process/MemBlock.cpp index 0b73a0eb..cfb57b1c 100644 --- a/src/BlackBone/Process/MemBlock.cpp +++ b/src/BlackBone/Process/MemBlock.cpp @@ -2,7 +2,7 @@ #include "ProcessMemory.h" #include "ProcessCore.h" #include "../Subsystem/NativeSubsystem.h" -#include "../Misc/Trace.hpp" +#include "../Include/Exception.h" namespace blackbone { @@ -63,7 +63,7 @@ MemBlock::MemBlock( ProcessMemory* mem, ptr_t ptr, bool own /*= true*/ ) _pImpl->_own = own; _pImpl->_memory = mem; - MEMORY_BASIC_INFORMATION64 mbi = { 0 }; + MEMORY_BASIC_INFORMATION64 mbi = { }; mem->Query( _pImpl->_ptr, &mbi ); _pImpl->_protection = mbi.Protect; @@ -79,8 +79,8 @@ MemBlock::MemBlock( ProcessMemory* mem, ptr_t ptr, bool own /*= true*/ ) /// Desired base address of new block /// Memory protection /// false if caller will be responsible for block deallocation -/// Memory block. If failed - returned block will be invalid -call_result_t MemBlock::Allocate( +/// Memory block +MemBlock MemBlock::Allocate( ProcessMemory& process, size_t size, ptr_t desired /*= 0*/, @@ -91,39 +91,40 @@ call_result_t MemBlock::Allocate( ptr_t desired64 = desired; DWORD finalProt = protection; if(process.protectionCasting() == MemProtectionCasting::useDep) - finalProt = CastProtection( protection, process.core().DEP() ); + finalProt = CastProtection( protection, process.core()->DEP() ); - NTSTATUS status = process.core().native()->VirtualAllocExT( desired64, size, MEM_RESERVE | MEM_COMMIT, finalProt ); + NTSTATUS status = process.core()->native()->VirtualAllocExT( desired64, size, MEM_RESERVE | MEM_COMMIT, finalProt ); if (!NT_SUCCESS( status )) { desired64 = 0; - status = process.core().native()->VirtualAllocExT( desired64, size, MEM_COMMIT, finalProt ); - if (NT_SUCCESS( status )) - return call_result_t( MemBlock( &process, desired64, size, protection, own ), STATUS_IMAGE_NOT_AT_BASE ); - else - return status; + status = process.core()->native()->VirtualAllocExT( desired64, size, MEM_COMMIT, finalProt ); + THROW_ON_FAIL_AND_LOG( status, "failed to allocate memory" ); + + return MemBlock( &process, desired64, size, protection, own ); } + #ifdef _DEBUG - BLACKBONE_TRACE(L"Allocate: Allocating at address 0x%p (0x%X bytes)", static_cast(desired64), size); + BLACKBONE_TRACE(L"Allocate: Allocating at address 0x%llx (0x%X bytes)", desired64, size); #endif + return MemBlock( &process, desired64, size, protection, own ); } -call_result_t MemBlock::AllocateClosest( - class ProcessMemory& process, - size_t size, - ptr_t desired, - DWORD protection /*= PAGE_EXECUTE_READWRITE*/, - bool own /*= true*/ - ) +MemBlock MemBlock::AllocateClosest( + class ProcessMemory& process, + size_t size, + ptr_t desired, + DWORD protection /*= PAGE_EXECUTE_READWRITE*/, + bool own /*= true*/ + ) { DWORD finalProt = protection; if(process.protectionCasting() == MemProtectionCasting::useDep) - finalProt = CastProtection( protection, process.core().DEP() ); + finalProt = CastProtection( protection, process.core()->DEP() ); NTSTATUS status = STATUS_SUCCESS; - uint32_t pageSize = process.core().native()->pageSize(); + uint32_t pageSize = process.core()->native()->pageSize(); ptr_t alignedSize = Align( size, pageSize ); SYSTEM_INFO sinfo; @@ -141,54 +142,55 @@ call_result_t MemBlock::AllocateClosest( while (left != 0) { - if (desired-left < right-desired && left >= leftLimit && left != 0) - { - // Look to the left - if (process.core().native()->VirtualQueryExT( left, &minfo )) - break; - - if (minfo.State == MEM_FREE && minfo.RegionSize >= size) - { - status = process.core().native()->VirtualAllocExT( left, size, MEM_RESERVE | MEM_COMMIT, finalProt ); - if (NT_SUCCESS( status )) - { - buf = MemBlock( &process, left, size, protection, own ); - break; - } - } - else - { - left = minfo.AllocationBase - alignedSize; - } - } - else if (right < rightLimit) - { - // Look to the right - if (process.core().native()->VirtualQueryExT( right, &minfo )) - break; - - if (minfo.State == MEM_FREE && minfo.RegionSize >= size) - { - status = process.core().native()->VirtualAllocExT( right, size, MEM_RESERVE | MEM_COMMIT, finalProt ); - if (NT_SUCCESS( status )) - { - buf = MemBlock( &process, right, size, protection, own ); - break; - } - } - else - { - right = minfo.BaseAddress + minfo.RegionSize; - } - } - else - { - break; - } + if (desired-left < right-desired && left >= leftLimit && left != 0) + { + // Look to the left + if (process.core()->native()->VirtualQueryExT( left, &minfo )) + break; + + if (minfo.State == MEM_FREE && minfo.RegionSize >= size) + { + status = process.core()->native()->VirtualAllocExT( left, size, MEM_RESERVE | MEM_COMMIT, finalProt ); + if (NT_SUCCESS( status )) + { + buf = MemBlock( &process, left, size, protection, own ); + break; + } + } + else + { + left = minfo.AllocationBase - alignedSize; + } + } + else if (right < rightLimit) + { + // Look to the right + if (process.core()->native()->VirtualQueryExT( right, &minfo )) + break; + + if (minfo.State == MEM_FREE && minfo.RegionSize >= size) + { + status = process.core()->native()->VirtualAllocExT( right, size, MEM_RESERVE | MEM_COMMIT, finalProt ); + if (NT_SUCCESS( status )) + { + buf = MemBlock( &process, right, size, protection, own ); + break; + } + } + else + { + right = minfo.BaseAddress + minfo.RegionSize; + } + } + else + { + break; + } } if (!buf.valid()) - return MemBlock::Allocate( process, size, desired, protection, own ); + return Allocate( process, size, desired, protection, own ); + return buf; } @@ -199,21 +201,18 @@ call_result_t MemBlock::AllocateClosest( /// Desired base address of new block /// Memory protection /// New block address -call_result_t MemBlock::Realloc( size_t size, ptr_t desired /*= 0*/, DWORD protection /*= PAGE_EXECUTE_READWRITE*/ ) +ptr_t MemBlock::Realloc( size_t size, ptr_t desired /*= 0*/, DWORD protection /*= PAGE_EXECUTE_READWRITE*/ ) { if (!_pImpl) - return STATUS_MEMORY_NOT_ALLOCATED; + THROW_AND_LOG( "memory not allocated" ); ptr_t desired64 = desired; - auto status = _pImpl->_memory->core().native()->VirtualAllocExT( desired64, size, MEM_COMMIT, protection ); + auto status = _pImpl->_memory->core()->native()->VirtualAllocExT( desired64, size, MEM_COMMIT, protection ); if (!desired64) { desired64 = 0; - status = _pImpl->_memory->core().native()->VirtualAllocExT( desired64, size, MEM_COMMIT, protection ); - if (!NT_SUCCESS( status )) - return status; - - status = STATUS_IMAGE_NOT_AT_BASE; + status = _pImpl->_memory->core()->native()->VirtualAllocExT( desired64, size, MEM_COMMIT, protection ); + THROW_ON_FAIL_AND_LOG( status, "failed to allocate new memory address for block 0x%llx", _pImpl->_ptr ); } // Replace current instance @@ -226,7 +225,7 @@ call_result_t MemBlock::Realloc( size_t size, ptr_t desired /*= 0*/, DWOR _pImpl->_protection = protection; } - return call_result_t( desired64, status ); + return desired64; } /// @@ -244,12 +243,12 @@ NTSTATUS MemBlock::Protect( DWORD protection, uintptr_t offset /*= 0*/, size_t s DWORD finalProt = protection; if (_pImpl->_memory->protectionCasting() == MemProtectionCasting::useDep) - finalProt = CastProtection( protection, _pImpl->_memory->core().DEP() ); + finalProt = CastProtection( protection, _pImpl->_memory->core()->DEP() ); if (size == 0) size = _pImpl->_size; - return _pImpl->_physical ? Driver().ProtectMem( _pImpl->_memory->core().pid(), _pImpl->_ptr + offset, size, finalProt ) : + return _pImpl->_physical ? Driver().ProtectMem( _pImpl->_memory->core()->pid(), _pImpl->_ptr + offset, size, finalProt ) : _pImpl->_memory->Protect( _pImpl->_ptr + offset, size, finalProt, pOld ); } @@ -276,11 +275,11 @@ NTSTATUS MemBlock::MemBlockImpl::Free( size_t size /*= 0 */ ) size = Align( size, 0x1000 ); - NTSTATUS status = _physical ? Driver().FreeMem( _memory->core().pid(), _ptr, size, MEM_RELEASE ) : + NTSTATUS status = _physical ? Driver().FreeMem( _memory->core()->pid(), _ptr, size, MEM_RELEASE ) : _memory->Free( _ptr, size, size == 0 ? MEM_RELEASE : MEM_DECOMMIT ); if (!NT_SUCCESS( status )) - return LastNtStatus(); + return status; if (size == 0) { @@ -308,14 +307,31 @@ NTSTATUS MemBlock::MemBlockImpl::Free( size_t size /*= 0 */ ) /// Otherwise function will fail if there is at least one non-committed page in region. /// /// Status -NTSTATUS MemBlock::Read( uintptr_t offset, size_t size, PVOID pResult, bool handleHoles /*= false*/ ) +NTSTATUS MemBlock::ReadNoThrow( uintptr_t offset, size_t size, PVOID pResult, bool handleHoles ) { if (!_pImpl) return STATUS_MEMORY_NOT_ALLOCATED; - return _pImpl->_memory->Read( _pImpl->_ptr + offset, size, pResult, handleHoles ); + return _pImpl->_memory->ReadNoThrow( _pImpl->_ptr + offset, size, pResult, handleHoles ); } +/// +/// Read data +/// +/// Data offset in block +/// Size of data to read +/// Output buffer +/// +/// If true, function will try to read all committed pages in range ignoring uncommitted. +/// Otherwise function will fail if there is at least one non-committed page in region. +/// +void MemBlock::Read( uintptr_t offset, size_t size, PVOID pResult, bool handleHoles /*= false*/ ) +{ + if (!_pImpl) + THROW_AND_LOG( "memory not allocated" ); + + _pImpl->_memory->Read( _pImpl->_ptr + offset, size, pResult, handleHoles ); +} /// /// Write data @@ -324,20 +340,26 @@ NTSTATUS MemBlock::Read( uintptr_t offset, size_t size, PVOID pResult, bool hand /// Size of data to write /// Buffer to write /// Status -NTSTATUS MemBlock::Write( uintptr_t offset, size_t size, const void* pData ) +NTSTATUS MemBlock::WriteNoThrow( uintptr_t offset, size_t size, const void * pData ) { if (!_pImpl) return STATUS_MEMORY_NOT_ALLOCATED; - return _pImpl->_memory->Write( _pImpl->_ptr + offset, size, pData ); + return _pImpl->_memory->WriteNoThrow( _pImpl->_ptr + offset, size, pData ); } /// -/// Try to free memory and reset pointers +/// Write data /// -void MemBlock::Reset() +/// Data offset in block +/// Size of data to write +/// Buffer to write +void MemBlock::Write( uintptr_t offset, size_t size, const void* pData ) { - _pImpl.reset(); + if (!_pImpl) + THROW_AND_LOG( "memory not allocated" ); + + _pImpl->_memory->Write( _pImpl->_ptr + offset, size, pData ); } } diff --git a/src/BlackBone/Process/MemBlock.h b/src/BlackBone/Process/MemBlock.h index 1800d013..27a82755 100644 --- a/src/BlackBone/Process/MemBlock.h +++ b/src/BlackBone/Process/MemBlock.h @@ -3,7 +3,6 @@ #include "../Include/Winheaders.h" #include "../Include/Macro.h" #include "../Include/Types.h" -#include "../Include/CallResult.h" #include #include @@ -20,20 +19,16 @@ namespace blackbone inline DWORD CastProtection( DWORD prot, bool bDEP ) { if (bDEP == true) - { return prot; - } - else - { - if (prot == PAGE_EXECUTE_READ) - return PAGE_READONLY; - else if (prot == PAGE_EXECUTE_READWRITE) - return PAGE_READWRITE; - else if (prot == PAGE_EXECUTE_WRITECOPY) - return PAGE_WRITECOPY; - else - return prot; - } + + if( prot == PAGE_EXECUTE_READ ) + return PAGE_READONLY; + if (prot == PAGE_EXECUTE_READWRITE) + return PAGE_READWRITE; + if (prot == PAGE_EXECUTE_WRITECOPY) + return PAGE_WRITECOPY; + + return prot; } class MemBlock @@ -124,8 +119,8 @@ class MemBlock /// Desired base address of new block /// Win32 Memory protection flags /// false if caller will be responsible for block deallocation - /// Memory block. If failed - returned block will be invalid - BLACKBONE_API static call_result_t Allocate( + /// Memory block/returns> + BLACKBONE_API static MemBlock Allocate( class ProcessMemory& process, size_t size, ptr_t desired = 0, @@ -142,13 +137,13 @@ class MemBlock /// Win32 Memory protection flags /// false if caller will be responsible for block deallocation /// Memory block. If failed - returned block will be invalid - BLACKBONE_API static call_result_t AllocateClosest( - class ProcessMemory& process, + BLACKBONE_API static MemBlock AllocateClosest( + class ProcessMemory& process, size_t size, ptr_t desired, DWORD protection = PAGE_EXECUTE_READWRITE, bool own = true - ); + ); /// /// Reallocate existing block for new size @@ -157,7 +152,7 @@ class MemBlock /// Desired base address of new block /// Memory protection /// New block address - BLACKBONE_API call_result_t Realloc( size_t size, ptr_t desired = 0, DWORD protection = PAGE_EXECUTE_READWRITE ); + BLACKBONE_API ptr_t Realloc( size_t size, ptr_t desired = 0, DWORD protection = PAGE_EXECUTE_READWRITE ); /// /// Change memory protection @@ -186,7 +181,8 @@ class MemBlock /// Otherwise function will fail if there is at least one non-committed page in region. /// /// Status - BLACKBONE_API NTSTATUS Read( uintptr_t offset, size_t size, PVOID pResult, bool handleHoles = false ); + BLACKBONE_API NTSTATUS ReadNoThrow( uintptr_t offset, size_t size, PVOID pResult, bool handleHoles = false ); + BLACKBONE_API void Read( uintptr_t offset, size_t size, PVOID pResult, bool handleHoles = false ); /// /// Write data @@ -195,13 +191,14 @@ class MemBlock /// Size of data to write /// Buffer to write /// Status - BLACKBONE_API NTSTATUS Write( uintptr_t offset, size_t size, const void* pData ); + BLACKBONE_API NTSTATUS WriteNoThrow( uintptr_t offset, size_t size, const void* pData ); + BLACKBONE_API void Write( uintptr_t offset, size_t size, const void* pData ); /// /// Read data /// /// Data offset in block - /// Defult return value if read has failed + /// Default return value if read has failed /// Read data template T Read( uintptr_t offset, const T& def_val ) @@ -215,12 +212,24 @@ class MemBlock /// Read data /// /// Data offset in block - /// Read data - /// Status code + /// Read data template - NTSTATUS Read( size_t offset, T& val ) + T Read( uintptr_t offset ) { - return Read( offset, sizeof( val ), &val ); + T res; + Read( offset, sizeof( res ), &res ); + return res; + } + + /// + /// Read data + /// + /// Data offset in block + /// Read data + template + void Read( size_t offset, T& val ) + { + Read( offset, sizeof( val ), &val ); } /// @@ -230,50 +239,58 @@ class MemBlock /// Data to write /// Status template - NTSTATUS Write( uintptr_t offset, const T& data ) + void Write( uintptr_t offset, const T& data ) { - return Write( offset, sizeof( data ), &data ); + Write( offset, sizeof( data ), &data ); } /// /// Try to free memory and reset pointers /// - BLACKBONE_API void Reset(); + BLACKBONE_API void reset() + { + _pImpl.reset(); + } /// /// Memory will not be deallocated upon object destruction /// - BLACKBONE_API inline void Release() { if (_pImpl) _pImpl->_own = false; } + BLACKBONE_API void Release() + { + if (_pImpl) + _pImpl->_own = false; + } /// /// Get memory pointer /// /// Memory pointer template - inline T ptr() const { return _pImpl ? (T)_pImpl->_ptr : T( 0 ); } + T ptr() const { return _pImpl ? T(_pImpl->_ptr) : T( 0 ); } /// /// Get block size /// /// Block size - BLACKBONE_API inline size_t size() const { return _pImpl ? _pImpl->_size : 0; } + BLACKBONE_API size_t size() const { return _pImpl ? _pImpl->_size : 0; } /// /// Get block memory protection /// /// Memory protection flags - BLACKBONE_API inline DWORD protection() const { return _pImpl ? _pImpl->_protection : 0; } + BLACKBONE_API DWORD protection() const { return _pImpl ? _pImpl->_protection : 0; } /// /// Validate memory block /// true if memory pointer isn't 0 - BLACKBONE_API inline bool valid() const { return( _pImpl.get() != nullptr && _pImpl->_ptr != 0); } + BLACKBONE_API bool valid() const { return( _pImpl.get() != nullptr && _pImpl->_ptr != 0); } + BLACKBONE_API explicit operator bool() const { return valid(); } /// /// Get memory pointer /// /// Memory pointer - BLACKBONE_API inline operator ptr_t() const { return _pImpl ? _pImpl->_ptr : 0; } + BLACKBONE_API operator ptr_t() const { return _pImpl ? _pImpl->_ptr : 0; } private: std::shared_ptr _pImpl; diff --git a/src/BlackBone/Process/MultPtr.hpp b/src/BlackBone/Process/MultPtr.hpp index 58f3aef5..93c4e747 100644 --- a/src/BlackBone/Process/MultPtr.hpp +++ b/src/BlackBone/Process/MultPtr.hpp @@ -69,7 +69,7 @@ class multi_ptr __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) { BLACKBONE_TRACE( - "Invalid pointer derefrence: base 0x%p, offset 0x%08x, target address 0x%p", + "Invalid pointer dereference: base 0x%p, offset 0x%08x, target address 0x%p", _base, (i > 0 ? _offsets[i] : -1), ptr ); @@ -104,13 +104,13 @@ class multi_ptr_ex : public multi_ptr /// Commit changed object into process /// /// Status code - NTSTATUS commit() + void commit() { auto ptr = get_ptr(); if (ptr == 0) - return STATUS_ACCESS_VIOLATION; + THROW_AND_LOG( "invalid address" ); - return _proc->memory().Write( ptr, sizeof( _data ), &_data ); + _proc->memory().Write( ptr, _data ); } private: @@ -124,7 +124,8 @@ class multi_ptr_ex : public multi_ptr if (ptr == 0) return nullptr; - return NT_SUCCESS( _proc->memory().Read( ptr, sizeof( _data ), &_data ) ) ? &_data : nullptr; + _data = _proc->memory().Read::type>( ptr ); + return &_data; } /// @@ -133,28 +134,23 @@ class multi_ptr_ex : public multi_ptr /// Pointer value or 0 if chain is invalid uintptr_t get_ptr() { - uintptr_t ptr = multi_ptr::_base; - if (!NT_SUCCESS( _proc->memory().Read( ptr, ptr ) )) - return 0; + auto ptr = _proc->memory().Read( multi_ptr::_base ); if (!multi_ptr::_offsets.empty()) { for (intptr_t i = 0; i < static_cast(multi_ptr::_offsets.size()) - 1; i++) - if (!NT_SUCCESS( _proc->memory().Read( ptr + multi_ptr::_offsets[i], ptr ) )) - return 0; + _proc->memory().Read( ptr + multi_ptr::_offsets[i], ptr ); ptr += multi_ptr::_offsets.back(); - if (multi_ptr::type_is_ptr) - if (!NT_SUCCESS( _proc->memory().Read( ptr, ptr ) )) - return 0; + if constexpr (multi_ptr::type_is_ptr) + _proc->memory().Read( ptr, ptr ); } return ptr; } - private: Process* _proc = nullptr; // Target process - multi_ptr::type _data; // Local object copy + multi_ptr::type _data; // Local object copy }; } \ No newline at end of file diff --git a/src/BlackBone/Process/Process.cpp b/src/BlackBone/Process/Process.cpp index 3a18abce..6c8f2e18 100644 --- a/src/BlackBone/Process/Process.cpp +++ b/src/BlackBone/Process/Process.cpp @@ -1,4 +1,5 @@ #include "Process.h" +#include "../Include/Exception.h" #include "../Misc/NameResolve.h" #include "../Misc/DynImport.h" @@ -6,26 +7,54 @@ namespace blackbone { + #define SystemHandleInformation (SYSTEM_INFORMATION_CLASS)16 #define ObjectNameInformation (OBJECT_INFORMATION_CLASS)1 Process::Process() : _core() - , _modules( *this ) + , _modules( this ) , _memory( this ) - , _threads( _core ) - , _hooks( _memory ) - , _localHooks( *this ) - , _remote( *this ) - , _mmap( *this ) - , _nativeLdr( *this ) + , _threads( &_core ) + , _hooks( &_memory ) + , _localHooks( this ) + , _remote( this ) + , _mmap( this ) + , _nativeLdr( this ) { // Ensure InitOnce is called InitializeOnce(); } -Process::~Process(void) +Process::Process( DWORD pid, DWORD access /*= DEFAULT_ACCESS_P */ ) + : Process() +{ + Attach( pid, access ); +} + +Process::Process( const wchar_t* name, DWORD access /*= DEFAULT_ACCESS_P */ ) + : Process() +{ + Attach( name, access ); +} + +Process::Process( HANDLE hProc ) + : Process() +{ + Attach( hProc ); +} + +Process::Process( + const std::wstring& path, + bool suspended, + bool forceInit, + const std::wstring& cmdLine, + const wchar_t* currentDir, + STARTUPINFOW* pStartup +) + : Process() { + CreateAndAttach( path, suspended, forceInit, cmdLine, currentDir, pStartup ); } /// @@ -33,22 +62,20 @@ Process::~Process(void) /// /// Process ID /// Access mask -/// Status code -NTSTATUS Process::Attach( DWORD pid, DWORD access /*= DEFAULT_ACCESS_P*/ ) +void Process::Attach( DWORD pid, DWORD access /*= DEFAULT_ACCESS_P*/ ) { Detach(); - return _core.Open( pid, access ); + _core.Open( pid, access ); } /// /// Attach to existing process /// /// Process handle -/// Status code -NTSTATUS Process::Attach( HANDLE hProc ) +void Process::Attach( HANDLE hProc ) { Detach(); - return _core.Open( hProc ); + _core.Open( hProc ); } /// @@ -56,11 +83,13 @@ NTSTATUS Process::Attach( HANDLE hProc ) /// /// Process name /// Access mask -/// Status code -NTSTATUS Process::Attach( const wchar_t* name, DWORD access /*= DEFAULT_ACCESS_P*/ ) +void Process::Attach( const wchar_t* name, DWORD access /*= DEFAULT_ACCESS_P*/ ) { auto pids = EnumByName( name ); - return pids.empty() ? STATUS_NOT_FOUND : Attach( pids.front(), access ); + if (pids.empty()) + THROW_AND_LOG( "process %ls does not exist", name ); + + Attach( pids.front(), access ); } /// @@ -72,15 +101,14 @@ NTSTATUS Process::Attach( const wchar_t* name, DWORD access /*= DEFAULT_ACCESS_P /// Process command line /// Startup directory /// Additional startup params -/// Status code -NTSTATUS Process::CreateAndAttach( - const std::wstring& path, +void Process::CreateAndAttach( + const std::wstring& path, bool suspended /*= false*/, bool forceInit /*= true*/, const std::wstring& cmdLine /*= L""*/, const wchar_t* currentDir /*= nullptr*/, STARTUPINFOW* pStartup /*= nullptr*/ - ) +) { Detach(); @@ -90,40 +118,65 @@ NTSTATUS Process::CreateAndAttach( pStartup = &si; if (!CreateProcessW( - path.c_str(), const_cast(cmdLine.c_str()), + path.c_str(), const_cast(cmdLine.c_str()), nullptr, nullptr, FALSE, CREATE_SUSPENDED, nullptr, currentDir, pStartup, &pi - )) + )) { - return LastNtStatus(); - } + THROW_WITH_STATUS_AND_LOG( LastNtStatus(), "failed to create process '%ls'", path.c_str() ); + } // Get handle ownership - auto status = _core.Open( pi.hProcess ); - if (NT_SUCCESS( status )) + _core.Open( pi.hProcess ); + + // Check if process must be left in suspended mode + if (suspended) { - // Check if process must be left in suspended mode - if (suspended) - { - // Create new thread to make sure LdrInitializeProcess gets called - if (forceInit) - EnsureInit(); - } - else - ResumeThread( pi.hThread ); + // Create new thread to make sure LdrInitializeProcess gets called + if (forceInit) + EnsureInit(); } + else + ResumeThread( pi.hThread ); // Close unneeded handles CloseHandle( pi.hThread ); +} + +/// +/// Create new process and attach to it +/// +/// Executable path +/// Leave process in suspended state. To resume process one should resume its main thread +/// If 'suspended' is true, this flag will enforce process initialization via second thread +/// Process command line +/// Startup directory +/// Additional startup params +/// Process object +Process Process::CreateNew( + const std::wstring& path, + bool suspended /*= false*/, + bool forceInit /*= true*/, + const std::wstring& cmdLine /*= L""*/, + const wchar_t* currentDir /*= nullptr*/, + STARTUPINFOW* pStartup /*= nullptr */ +) +{ + return Process( path, suspended, forceInit, cmdLine, currentDir, pStartup ); +} - return status; +/// +/// Attach to current process +/// +Process Process::CurrentProcess() +{ + return Process( GetCurrentProcessId() ); } /// /// Detach form current process, if any /// -/// Status code -NTSTATUS Process::Detach() +void Process::Detach() { // Reset data _memory.reset(); @@ -132,8 +185,6 @@ NTSTATUS Process::Detach() _mmap.reset(); _hooks.reset(); _core.Close(); - - return STATUS_SUCCESS; } /// @@ -143,10 +194,7 @@ NTSTATUS Process::Detach() NTSTATUS Process::EnsureInit() { auto pProc = _modules.GetNtdllExport( "NtYieldExecution", mt_default, Sections ); - if (pProc) - return _remote.ExecDirect( pProc->procAddress, 0 ); - - return STATUS_NOT_FOUND; + return _remote.ExecDirect( pProc.procAddress, 0 ); } /// @@ -170,7 +218,7 @@ NTSTATUS Process::Resume() /// /// Checks if process still exists /// -/// +/// true if process is valid and exists bool Process::valid() { DWORD dwExitCode = 0; @@ -185,7 +233,7 @@ bool Process::valid() /// Terminate process /// /// Exit code -/// Stratus code +/// Status code NTSTATUS Process::Terminate( uint32_t code /*= 0*/ ) { TerminateProcess( _core.handle(), code ); @@ -195,39 +243,33 @@ NTSTATUS Process::Terminate( uint32_t code /*= 0*/ ) /// /// Enumerate all open handles /// -/// Found handles or status code -call_result_t> Process::EnumHandles() +/// Found handles +std::vector Process::EnumHandles() { ULONG bufSize = 0x10000; - uint8_t* buffer = (uint8_t*)VirtualAlloc( NULL, bufSize, MEM_COMMIT, PAGE_READWRITE ); ULONG returnLength = 0; std::vector handles; + auto buffer = make_raw_ptr( bufSize ); // Query handle list - NTSTATUS status = SAFE_NATIVE_CALL( NtQuerySystemInformation, SystemHandleInformation, buffer, bufSize, &returnLength ); + NTSTATUS status = SAFE_NATIVE_CALL( NtQuerySystemInformation, SystemHandleInformation, buffer.get(), bufSize, &returnLength ); while (status == STATUS_INFO_LENGTH_MISMATCH) { bufSize *= 2; - VirtualFree( buffer, 0, MEM_RELEASE ); - buffer = (uint8_t*)VirtualAlloc( NULL, bufSize, MEM_COMMIT, PAGE_READWRITE ); - - status = SAFE_NATIVE_CALL( NtQuerySystemInformation, SystemHandleInformation, buffer, bufSize, &returnLength ); + buffer = make_raw_ptr( bufSize ); + status = SAFE_NATIVE_CALL( NtQuerySystemInformation, SystemHandleInformation, buffer.get(), bufSize, &returnLength ); } if (!NT_SUCCESS( status )) - { - VirtualFree( buffer, 0, MEM_RELEASE ); - return status; - } + THROW_WITH_STATUS_AND_LOG( status, "NtQuerySystemInformation failed" ); - SYSTEM_HANDLE_INFORMATION_T* handleInfo = (SYSTEM_HANDLE_INFORMATION_T*)buffer; + auto handleInfo = static_cast(buffer.get()); for (ULONG i = 0; i < handleInfo->HandleCount; i++) { HandleInfo info; ProcessHandle hLocal; - OBJECT_TYPE_INFORMATION_T* pTypeInfo = nullptr; - PVOID pNameInfo = nullptr; - UNICODE_STRING objectName = { 0 }; + std::unique_ptr typeInfo( nullptr, &free ); + std::unique_ptr nameInfo( nullptr, &free ); // Filter process if (handleInfo->Handles[i].ProcessId != _core._pid) @@ -240,7 +282,7 @@ call_result_t> Process::EnumHandles() reinterpret_cast(handleInfo->Handles[i].Handle), GetCurrentProcess(), &hLocal, 0, 0, DUPLICATE_SAME_ACCESS - ); + ); if (!NT_SUCCESS( status )) continue; @@ -248,78 +290,65 @@ call_result_t> Process::EnumHandles() // // Get type information // - pTypeInfo = (OBJECT_TYPE_INFORMATION_T*)malloc( 0x1000 ); - status = SAFE_NATIVE_CALL( NtQueryObject, hLocal, ObjectTypeInformation, pTypeInfo, 0x1000, nullptr ); + typeInfo.reset( static_cast(malloc( 0x1000 )) ); + status = SAFE_NATIVE_CALL( NtQueryObject, hLocal, ObjectTypeInformation, typeInfo.get(), 0x1000, nullptr ); if (!NT_SUCCESS( status )) - { continue; - } // // Obtain object name // - pNameInfo = malloc( 0x1000 ); - status = SAFE_NATIVE_CALL( NtQueryObject, hLocal, ObjectNameInformation, pNameInfo, 0x1000, &returnLength); + nameInfo.reset( static_cast(malloc( 0x1000 )) ); + status = SAFE_NATIVE_CALL( NtQueryObject, hLocal, ObjectNameInformation, nameInfo.get(), 0x1000, &returnLength ); if (!NT_SUCCESS( status )) { - pNameInfo = realloc( pNameInfo, returnLength ); - status = SAFE_NATIVE_CALL( NtQueryObject, hLocal, ObjectNameInformation, pNameInfo, returnLength, nullptr ); + auto old = nameInfo.release(); + nameInfo.reset( static_cast(realloc( old, returnLength )) ); + status = SAFE_NATIVE_CALL( NtQueryObject, hLocal, ObjectNameInformation, nameInfo.get(), returnLength, nullptr ); if (!NT_SUCCESS( status )) - { - free( pTypeInfo ); - free( pNameInfo ); continue; - } } - objectName = *(PUNICODE_STRING)pNameInfo; - // // Fill handle info structure // - info.handle = reinterpret_cast(handleInfo->Handles[i].Handle); - info.access = handleInfo->Handles[i].GrantedAccess; - info.flags = handleInfo->Handles[i].Flags; - info.pObject = handleInfo->Handles[i].Object; + info.handle = reinterpret_cast(handleInfo->Handles[i].Handle); + info.access = handleInfo->Handles[i].GrantedAccess; + info.flags = handleInfo->Handles[i].Flags; + info.pObject = handleInfo->Handles[i].Object; - if (pTypeInfo->Name.Length) - info.typeName = (wchar_t*)pTypeInfo->Name.Buffer; + if (typeInfo->Name.Length) + info.typeName = reinterpret_cast(typeInfo->Name.Buffer); - if (objectName.Length) - info.name = objectName.Buffer; + if (nameInfo->Buffer) + info.name = nameInfo->Buffer; // // Type-specific info // if (_wcsicmp( info.typeName.c_str(), L"Section" ) == 0) { - SECTION_BASIC_INFORMATION_T secInfo = { 0 }; + SECTION_BASIC_INFORMATION_T secInfo = { }; status = SAFE_NATIVE_CALL( NtQuerySection, hLocal, SectionBasicInformation, &secInfo, (ULONG)sizeof( secInfo ), nullptr ); if (NT_SUCCESS( status )) { - info.section.reset( new SectionInfo() ); - - info.section->size = secInfo.Size.QuadPart; - info.section->attrib = secInfo.Attributes; + info.section.size = secInfo.Size.QuadPart; + info.section.attrib = secInfo.Attributes; } } handles.emplace_back( info ); - - free( pTypeInfo ); - free( pNameInfo ); } - VirtualFree( buffer, 0, MEM_RELEASE ); - return call_result_t>( handles, status ); + return handles; } /// /// Search for process by executable name /// /// Process name. If empty - function will retrieve all existing processes -/// Found processses +/// Found processes std::vector Process::EnumByName( const std::wstring& name ) { std::vector found; @@ -327,7 +356,7 @@ std::vector Process::EnumByName( const std::wstring& name ) if (!hProcSnap) return found; - PROCESSENTRY32W tEntry = { 0 }; + PROCESSENTRY32W tEntry = { }; tEntry.dwSize = sizeof( PROCESSENTRY32W ); // Iterate threads @@ -345,45 +374,40 @@ std::vector Process::EnumByName( const std::wstring& name ) /// /// Search for process by executable name or by process ID /// -/// Target process ID. rocess name. If empty - function will retrieve all existing processes +/// Target process ID. If empty - function will retrieve all existing processes /// Process executable name. If empty - function will retrieve all existing processes -/// Found processses -/// If set to true, function will retrieve info ablout process threads -/// Status code -call_result_t> Process::EnumByNameOrPID( - uint32_t pid, - const std::wstring& name, - bool includeThreads /*= false*/ - ) +/// If set to true, function will retrieve info about process threads +/// Found processes +std::vector Process::EnumByNameOrPID( uint32_t pid, const std::wstring& name, bool includeThreads /*= false*/ ) { ULONG bufSize = 0x100; uint8_t tmpbuf[0x100]; - uint8_t* buffer = tmpbuf; + auto buffer = make_raw_ptr( tmpbuf ); ULONG returnLength = 0; std::vector found; // Query process info - NTSTATUS status = SAFE_NATIVE_CALL( NtQuerySystemInformation, (SYSTEM_INFORMATION_CLASS)57, buffer, bufSize, &returnLength ); + NTSTATUS status = SAFE_NATIVE_CALL( NtQuerySystemInformation, SYSTEM_INFORMATION_CLASS( 57 ), buffer.get(), bufSize, &returnLength ); if (!NT_SUCCESS( status )) { bufSize = returnLength; - buffer = (uint8_t*)VirtualAlloc( NULL, bufSize, MEM_COMMIT, PAGE_READWRITE ); - status = SAFE_NATIVE_CALL( NtQuerySystemInformation, (SYSTEM_INFORMATION_CLASS)57, buffer, bufSize, &returnLength ); + buffer.reset( VirtualAlloc( nullptr, bufSize, MEM_COMMIT, PAGE_READWRITE ) ); + status = SAFE_NATIVE_CALL( NtQuerySystemInformation, SYSTEM_INFORMATION_CLASS( 57 ), buffer.get(), bufSize, &returnLength ); if (!NT_SUCCESS( status )) - { - VirtualFree( buffer, 0, MEM_RELEASE ); - return status; - } + THROW_WITH_STATUS_AND_LOG( status, "NtQuerySystemInformation failed" ); } + using info_t = _SYSTEM_PROCESS_INFORMATION_T; + // Parse info - for (auto pInfo = reinterpret_cast<_SYSTEM_PROCESS_INFORMATION_T*>(buffer);;) + for (auto pInfo = static_cast(buffer.get());;) { // Skip idle process, compare name or compare pid - if (pInfo->UniqueProcessId != 0 && ((name.empty() && pid == 0) || _wcsicmp( name.c_str(), (wchar_t*)pInfo->ImageName.Buffer ) == 0 || pid == pInfo->UniqueProcessId)) + if (pInfo->UniqueProcessId != 0 && ((name.empty() && pid == 0) || + _wcsicmp( name.c_str(), (wchar_t*)pInfo->ImageName.Buffer ) == 0 || + pid == pInfo->UniqueProcessId)) { ProcessInfo info; - info.pid = static_cast(pInfo->UniqueProcessId); if (pInfo->ImageName.Buffer) @@ -419,17 +443,14 @@ call_result_t> Process::EnumByNameOrPID( } if (pInfo->NextEntryOffset) - pInfo = reinterpret_cast<_SYSTEM_PROCESS_INFORMATION_T*>((uint8_t*)pInfo + pInfo->NextEntryOffset); + pInfo = reinterpret_cast(reinterpret_cast(pInfo) + pInfo->NextEntryOffset); else - break; + break; } // Sort results std::sort( found.begin(), found.end() ); - - VirtualFree( buffer, 0, MEM_RELEASE ); - return call_result_t>( found, status ); + return found; } - } diff --git a/src/BlackBone/Process/Process.h b/src/BlackBone/Process/Process.h index 8bdc75b6..d00109d8 100644 --- a/src/BlackBone/Process/Process.h +++ b/src/BlackBone/Process/Process.h @@ -11,7 +11,6 @@ #include "../ManualMap/MMap.h" #include "../Include/NativeStructures.h" -#include "../Include/CallResult.h" #include "../Misc/InitOnce.h" #include @@ -39,7 +38,7 @@ struct ProcessInfo std::wstring imageName; std::vector threads; - bool operator < (const ProcessInfo& other) + bool operator < ( const ProcessInfo& other ) { return this->pid < other.pid; } @@ -68,7 +67,7 @@ struct HandleInfo std::wstring name; // Object-specific info - std::shared_ptr section; + SectionInfo section; }; #define DEFAULT_ACCESS_P PROCESS_QUERY_INFORMATION | \ @@ -80,34 +79,48 @@ struct HandleInfo PROCESS_TERMINATE | \ PROCESS_SUSPEND_RESUME | \ PROCESS_DUP_HANDLE + class Process { public: BLACKBONE_API Process(); - BLACKBONE_API ~Process(void); + BLACKBONE_API explicit Process( DWORD pid, DWORD access = DEFAULT_ACCESS_P ); + BLACKBONE_API explicit Process( const wchar_t* name, DWORD access = DEFAULT_ACCESS_P ); + BLACKBONE_API explicit Process( HANDLE hProc ); + BLACKBONE_API Process( + const std::wstring& path, + bool suspended, + bool forceInit, + const std::wstring& cmdLine, + const wchar_t* currentDir, + STARTUPINFOW* pStartup + ); + + Process( const Process& ) = delete; + Process& operator =( const Process& ) = delete; + + Process( Process&& ) = default; + Process& operator =( Process&& ) = default; /// /// Attach to existing process /// /// Process ID /// Access mask - /// Status code - BLACKBONE_API NTSTATUS Attach( DWORD pid, DWORD access = DEFAULT_ACCESS_P ); + BLACKBONE_API void Attach( DWORD pid, DWORD access = DEFAULT_ACCESS_P ); /// /// Attach to existing process /// /// Process name /// Access mask - /// Status code - BLACKBONE_API NTSTATUS Attach( const wchar_t* name, DWORD access = DEFAULT_ACCESS_P ); + BLACKBONE_API void Attach( const wchar_t* name, DWORD access = DEFAULT_ACCESS_P ); /// /// Attach to existing process /// /// Process handle - /// Status code - BLACKBONE_API NTSTATUS Attach( HANDLE hProc ); + BLACKBONE_API void Attach( HANDLE hProc ); /// /// Create new process and attach to it @@ -118,21 +131,34 @@ class Process /// Process command line /// Startup directory /// Additional startup params - /// Status code - BLACKBONE_API NTSTATUS CreateAndAttach( + BLACKBONE_API void CreateAndAttach( const std::wstring& path, bool suspended = false, bool forceInit = true, const std::wstring& cmdLine = L"", const wchar_t* currentDir = nullptr, STARTUPINFOW* pStartup = nullptr - ); + ); + + // Syntax sugar for CreateAndAttach + BLACKBONE_API static Process CreateNew( + const std::wstring& path, + bool suspended = false, + bool forceInit = true, + const std::wstring& cmdLine = L"", + const wchar_t* currentDir = nullptr, + STARTUPINFOW* pStartup = nullptr + ); + + /// + /// Attach to current process + /// + BLACKBONE_API static Process CurrentProcess(); /// /// Detach form current process, if any /// - /// Status code - BLACKBONE_API NTSTATUS Detach(); + BLACKBONE_API void Detach(); /// /// Ensure LdrInitializeProcess gets called @@ -156,7 +182,7 @@ class Process /// Get process ID /// /// Process ID - BLACKBONE_API inline DWORD pid() const { return _core.pid(); } + BLACKBONE_API DWORD pid() const { return _core.pid(); } /// /// Checks if process still exists @@ -168,35 +194,34 @@ class Process /// Terminate process /// /// Exit code - /// Stratus code + /// Status code BLACKBONE_API NTSTATUS Terminate( uint32_t code = 0 ); /// /// Enumerate all open handles /// - /// Found handles or status code - BLACKBONE_API call_result_t> EnumHandles(); + /// Found handles + BLACKBONE_API std::vector EnumHandles(); /// /// Search for process by executable name /// /// Process name. If empty - function will retrieve all existing processes - /// Found processses + /// Found processes BLACKBONE_API static std::vector EnumByName( const std::wstring& name ); /// /// Search for process by executable name or by process ID /// - /// Target process ID. rocess name. If empty - function will retrieve all existing processes + /// Target process ID. If empty - function will retrieve all existing processes /// Process executable name. If empty - function will retrieve all existing processes - /// Found processses - /// If set to true, function will retrieve info ablout process threads - /// Status code - BLACKBONE_API static call_result_t> EnumByNameOrPID( + /// If set to true, function will retrieve info about process threads + /// Found processes + BLACKBONE_API static std::vector EnumByNameOrPID( uint32_t pid, - const std::wstring& name, + const std::wstring& name, bool includeThreads = false - ); + ); // // Subroutines @@ -214,10 +239,6 @@ class Process // Sugar BLACKBONE_API const Wow64Barrier& barrier() const { return _core._native->GetWow64Barrier(); } -private: - Process(const Process&) = delete; - Process& operator =(const Process&) = delete; - private: ProcessCore _core; // Core routines and native subsystem ProcessModules _modules; // Module management diff --git a/src/BlackBone/Process/ProcessCore.cpp b/src/BlackBone/Process/ProcessCore.cpp index b98e2d9d..89286963 100644 --- a/src/BlackBone/Process/ProcessCore.cpp +++ b/src/BlackBone/Process/ProcessCore.cpp @@ -2,6 +2,7 @@ #include "ProcessCore.h" #include "../Misc/DynImport.h" #include "../Include/Macro.h" +#include "../Include/Exception.h" #include <3rd_party/VersionApi.h> namespace blackbone @@ -11,11 +12,6 @@ namespace blackbone #define PROCESS_DEP_ENABLE 0x00000001 #endif -ProcessCore::ProcessCore() - : _native( nullptr ) -{ -} - ProcessCore::~ProcessCore() { Close(); @@ -26,23 +22,25 @@ ProcessCore::~ProcessCore() /// /// Process ID /// Access mask -/// Status -NTSTATUS ProcessCore::Open( DWORD pid, DWORD access ) +/// +void ProcessCore::Open( DWORD pid, DWORD access ) { - // Handle current process differently - _hProcess = (pid == GetCurrentProcessId()) ? GetCurrentProcess() : OpenProcess( access, false, pid ); - - // Some routines in win10 do not support pseudo handle - if (IsWindows10OrGreater() && pid == GetCurrentProcessId()) - _hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid ); - - if (_hProcess) + if (pid == GetCurrentProcessId()) { - _pid = pid; - return Init(); + // Some routines in win10 do not support pseudo handle + if (IsWindows10OrGreater()) + _hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid ); + else + _hProcess = GetCurrentProcess(); } + else + _hProcess = OpenProcess( access, false, pid ); + + if (!_hProcess) + THROW_WITH_STATUS_AND_LOG( LastNtStatus(), "failed to open process" ); - return LastNtStatus(); + _pid = pid; + Init(); } /// @@ -51,7 +49,7 @@ NTSTATUS ProcessCore::Open( DWORD pid, DWORD access ) /// Process ID /// Access mask /// Status -NTSTATUS ProcessCore::Open( HANDLE handle ) +void ProcessCore::Open( HANDLE handle ) { _hProcess = handle; _pid = GetProcessId( _hProcess ); @@ -60,18 +58,17 @@ NTSTATUS ProcessCore::Open( HANDLE handle ) if (IsWindows10OrGreater() && _pid == GetCurrentProcessId()) _hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, _pid ); - return Init(); + Init(); } /// /// Initialize some internal data /// -/// Status code -NTSTATUS ProcessCore::Init() +void ProcessCore::Init() { // Detect x86 OS - SYSTEM_INFO info = { { 0 } }; + SYSTEM_INFO info = { }; GetNativeSystemInfo( &info ); if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) @@ -104,8 +101,37 @@ NTSTATUS ProcessCore::Init() if (SAFE_CALL( GetProcessDEPPolicy, _hProcess, &flags, &perm )) _dep = (flags & PROCESS_DEP_ENABLE) != 0; } +} - return STATUS_SUCCESS; +/// +/// Get WOW64 PEB +/// +/// Retrieved PEB32 +/// PEB pointer +ptr_t ProcessCore::peb( _PEB32 * ppeb ) +{ + if (!_native) + THROW_AND_LOG( "native subsystem not initialized" ); + + auto ptr = _native->getPEB( ppeb ); + if (ptr == 0) + THROW_WITH_STATUS_AND_LOG( LastNtStatus(), "failed to get PEB address" ); + + return ptr; +} + +/// +/// Get native PEB +/// +/// Retrieved PEB64 +/// PEB pointer +ptr_t ProcessCore::peb( _PEB64 * ppeb ) +{ + auto ptr = _native->getPEB( ppeb ); + if (ptr == 0) + THROW_WITH_STATUS_AND_LOG( LastNtStatus(), "failed to get PEB address" ); + + return ptr; } /// @@ -120,16 +146,14 @@ void ProcessCore::Close() bool ProcessCore::isProtected() { - if (_hProcess) - { - _PROCESS_EXTENDED_BASIC_INFORMATION_T info = { 0 }; - info.Size = sizeof( info ); - - _native->QueryProcessInfoT( ProcessBasicInformation, &info, sizeof( info ) ); - return info.Flags.IsProtectedProcess; - } + if (!_hProcess || !_native) + THROW_AND_LOG( "no active process" ); + + _PROCESS_EXTENDED_BASIC_INFORMATION_T info = { }; + info.Size = sizeof( info ); - return false; + _native->QueryProcessInfoT( ProcessBasicInformation, &info, sizeof( info ) ); + return info.Flags.IsProtectedProcess; } } \ No newline at end of file diff --git a/src/BlackBone/Process/ProcessCore.h b/src/BlackBone/Process/ProcessCore.h index d6e2ba4a..b61f72ae 100644 --- a/src/BlackBone/Process/ProcessCore.h +++ b/src/BlackBone/Process/ProcessCore.h @@ -1,6 +1,7 @@ #pragma once #include "../Include/NativeStructures.h" +#include "../Include/Exception.h" #include "../Include/HandleGuard.h" #include "../Subsystem/Wow64Subsystem.h" #include "../Subsystem/x86Subsystem.h" @@ -14,58 +15,57 @@ namespace blackbone class ProcessCore { public: - /// /// Check if target process is running in WOW64 mode /// /// true if process is WOW64 - BLACKBONE_API inline bool isWow64() const { return _native->GetWow64Barrier().targetWow64; } + BLACKBONE_API bool isWow64() const { return _native->GetWow64Barrier().targetWow64; } /// /// Get process handle /// /// Process handle - BLACKBONE_API inline HANDLE handle() const { return _hProcess; } + BLACKBONE_API HANDLE handle() const { return _hProcess; } /// /// Get process ID /// /// Process ID - BLACKBONE_API inline DWORD pid() const { return _pid; } + BLACKBONE_API DWORD pid() const { return _pid; } /// /// Get process data execution prevention state /// /// true if DEP is enabled for process - BLACKBONE_API inline bool DEP() const { return _dep; }; - + BLACKBONE_API bool DEP() const { return _dep; }; + /// /// Get system routines /// /// - BLACKBONE_API inline Native* native() { return _native.get(); } + BLACKBONE_API Native* native() { return _native.get(); } /// /// Get WOW64 PEB /// /// Retrieved PEB32 /// PEB pointer - BLACKBONE_API inline ptr_t peb32( _PEB32* ppeb = nullptr ) { return _native->getPEB( ppeb ); } + BLACKBONE_API ptr_t peb( _PEB32* ppeb ); + BLACKBONE_API ptr_t peb32( _PEB32* ppeb = nullptr ) { return peb( ppeb ); } /// /// Get native PEB /// /// Retrieved PEB64 /// PEB pointer - BLACKBONE_API inline ptr_t peb64( _PEB64* ppeb = nullptr ) { return _native->getPEB( ppeb ); } + BLACKBONE_API ptr_t peb( _PEB64* ppeb ); + BLACKBONE_API ptr_t peb64( _PEB64* ppeb = nullptr ) { return peb( ppeb ); } /// /// Get PEB /// - /// Retrieved PEB /// PEB pointer - template - BLACKBONE_API inline ptr_t peb( _PEB_T* ppeb = nullptr ) { return _native->getPEB( ppeb ); } + BLACKBONE_API ptr_t peb() { return peb( static_cast(nullptr) ); } /// /// Check if process is a protected process @@ -78,30 +78,29 @@ class ProcessCore using ptrNative = std::unique_ptr; private: - ProcessCore(); - ProcessCore( const ProcessCore& ) = delete; - ~ProcessCore(); + ProcessCore() = default; + ProcessCore( const ProcessCore& ) = delete; + ProcessCore( ProcessCore&& ) = default; + + BLACKBONE_API ~ProcessCore(); /// /// Attach to existing process /// /// Process ID /// Access mask - /// Status - NTSTATUS Open( DWORD pid, DWORD access ); + void Open( DWORD pid, DWORD access ); /// /// Attach to existing process by handle /// /// Process handle - /// Status - NTSTATUS Open( HANDLE handle ); + void Open( HANDLE handle ); /// /// Initialize some internal data /// - /// Status code - NTSTATUS Init(); + void Init(); /// /// Close current process handle diff --git a/src/BlackBone/Process/ProcessMemory.cpp b/src/BlackBone/Process/ProcessMemory.cpp index 945945a0..d645b5e1 100644 --- a/src/BlackBone/Process/ProcessMemory.cpp +++ b/src/BlackBone/Process/ProcessMemory.cpp @@ -1,6 +1,6 @@ #include "ProcessMemory.h" #include "Process.h" -#include "../Misc/Trace.hpp" +#include "../Include/Exception.h" namespace blackbone { @@ -8,11 +8,7 @@ namespace blackbone ProcessMemory::ProcessMemory( Process* process ) : RemoteMemory( process ) , _process( process ) - , _core( process->core() ) -{ -} - -ProcessMemory::~ProcessMemory() + , _core( &process->core() ) { } @@ -23,13 +19,13 @@ ProcessMemory::~ProcessMemory() /// Memory protection /// Desired base address of new block /// false if caller will be responsible for block deallocation -/// Memory block. If failed - returned block will be invalid -call_result_t ProcessMemory::Allocate( size_t size, DWORD protection /*= PAGE_EXECUTE_READWRITE*/, ptr_t desired /*= 0*/, bool own /*= true*/ ) +/// Memory block +MemBlock ProcessMemory::Allocate( size_t size, DWORD protection /*= PAGE_EXECUTE_READWRITE*/, ptr_t desired /*= 0*/, bool own /*= true*/ ) { return MemBlock::Allocate( *this, size, desired, protection, own ); } -call_result_t ProcessMemory::AllocateClosest( size_t size, DWORD protection /*= PAGE_EXECUTE_READWRITE*/, ptr_t desired /*= 0*/, bool own /*= true*/ ) +MemBlock ProcessMemory::AllocateClosest( size_t size, DWORD protection /*= PAGE_EXECUTE_READWRITE*/, ptr_t desired /*= 0*/, bool own /*= true*/ ) { return MemBlock::AllocateClosest( *this, size, desired, protection, own ); } @@ -51,7 +47,7 @@ NTSTATUS ProcessMemory::Free( ptr_t pAddr, size_t size /*= 0*/, DWORD freeType / BLACKBONE_TRACE( L"Free: Free at address 0x%p", static_cast(pAddr) ); } #endif - return _core.native()->VirtualFreeExT( pAddr, size, freeType ); + return _core->native()->VirtualFreeExT( pAddr, size, freeType ); } /// @@ -62,7 +58,7 @@ NTSTATUS ProcessMemory::Free( ptr_t pAddr, size_t size /*= 0*/, DWORD freeType / /// Status NTSTATUS ProcessMemory::Query( ptr_t pAddr, PMEMORY_BASIC_INFORMATION64 pInfo ) { - return _core.native()->VirtualQueryExT( pAddr, pInfo ); + return _core->native()->VirtualQueryExT( pAddr, pInfo ); } /// @@ -81,15 +77,15 @@ NTSTATUS ProcessMemory::Protect( ptr_t pAddr, size_t size, DWORD flProtect, DWOR DWORD finalProt = flProtect; if (_casting == MemProtectionCasting::useDep) - finalProt = CastProtection( flProtect, _core.DEP() ); + finalProt = CastProtection( flProtect, _core->DEP() ); - return _core.native()->VirtualProtectExT( pAddr, size, finalProt, pOld ); + return _core->native()->VirtualProtectExT( pAddr, size, finalProt, pOld ); } /// /// Read data /// -/// Memoey address to read from +/// Memory address to read from /// Size of data to read /// Output buffer /// @@ -97,7 +93,7 @@ NTSTATUS ProcessMemory::Protect( ptr_t pAddr, size_t size, DWORD flProtect, DWOR /// Otherwise function will fail if there is at least one non-committed page in region. /// /// Status -NTSTATUS ProcessMemory::Read( ptr_t dwAddress, size_t dwSize, PVOID pResult, bool handleHoles /*= false*/ ) +NTSTATUS ProcessMemory::ReadNoThrow( ptr_t dwAddress, size_t dwSize, PVOID pResult, bool handleHoles /*= false*/ ) { DWORD64 dwRead = 0; if (dwAddress == 0) @@ -105,40 +101,51 @@ NTSTATUS ProcessMemory::Read( ptr_t dwAddress, size_t dwSize, PVOID pResult, boo // Simple read if (!handleHoles) - { - return _core.native()->ReadProcessMemoryT( dwAddress, pResult, dwSize, &dwRead ); - } + return _core->native()->ReadProcessMemoryT( dwAddress, pResult, dwSize, &dwRead ); + // Read all committed memory regions - else + MEMORY_BASIC_INFORMATION64 mbi = { }; + for (ptr_t memptr = dwAddress; memptr < dwAddress + dwSize; memptr = mbi.BaseAddress + mbi.RegionSize) { - MEMORY_BASIC_INFORMATION64 mbi = { 0 }; - - for (ptr_t memptr = dwAddress; memptr < dwAddress + dwSize; memptr = mbi.BaseAddress + mbi.RegionSize) - { - if (_core.native()->VirtualQueryExT( memptr, &mbi ) != STATUS_SUCCESS) - continue; + if (!NT_SUCCESS( _core->native()->VirtualQueryExT( memptr, &mbi ) )) + continue; - // Filter empty regions - if (mbi.State != MEM_COMMIT || mbi.Protect == PAGE_NOACCESS) - continue; + // Filter empty regions + if (mbi.State != MEM_COMMIT || mbi.Protect == PAGE_NOACCESS) + continue; - uint64_t region_ptr = memptr - dwAddress; + uint64_t region_ptr = memptr - dwAddress; - auto status = _core.native()->ReadProcessMemoryT( - mbi.BaseAddress, - reinterpret_cast(pResult) + region_ptr, - static_cast(mbi.RegionSize), - &dwRead - ); + NTSTATUS status = _core->native()->ReadProcessMemoryT( + mbi.BaseAddress, + reinterpret_cast(pResult) + region_ptr, + static_cast(mbi.RegionSize), + &dwRead + ); - if (!NT_SUCCESS( status )) - return status; - } + if (!NT_SUCCESS( status )) + continue; } return STATUS_SUCCESS; } +/// +/// Read data +/// +/// Memory address to read from +/// Size of data to read +/// Output buffer +/// +/// If true, function will try to read all committed pages in range ignoring uncommitted ones. +/// Otherwise function will fail if there is at least one non-committed page in region. +/// +void ProcessMemory::Read( ptr_t dwAddress, size_t dwSize, PVOID pResult, bool handleHoles /*= false*/ ) +{ + NTSTATUS status = ReadNoThrow( dwAddress, dwSize, pResult, handleHoles ); + THROW_ON_FAIL_AND_LOG( status, "failed to read from address 0x%p", dwAddress ); +} + /// /// Read data /// @@ -149,19 +156,19 @@ NTSTATUS ProcessMemory::Read( ptr_t dwAddress, size_t dwSize, PVOID pResult, boo /// If true, function will try to read all committed pages in range ignoring uncommitted ones. /// Otherwise function will fail if there is at least one non-committed page in region. /// -/// Status -NTSTATUS ProcessMemory::Read( const std::vector& adrList, size_t dwSize, PVOID pResult, bool handleHoles /*= false */ ) +void ProcessMemory::Read( const std::vector& adrList, size_t dwSize, PVOID pResult, bool handleHoles /*= false */ ) { if (adrList.empty()) - return STATUS_INVALID_PARAMETER; - if(adrList.size() == 1) + THROW_AND_LOG( "address list is empty" ); + + if (adrList.size() == 1) return Read( adrList.front(), dwSize, pResult, handleHoles ); bool wow64 = _process->barrier().targetWow64; - ptr_t ptr = wow64 ? Read( adrList[0] ).result( 0 ) : Read( adrList[0] ).result( 0 ); + ptr_t ptr = wow64 ? Read( adrList[0] ): Read( adrList[0] ); for (size_t i = 1; i < adrList.size() - 1; i++) - ptr = wow64 ? Read( ptr + adrList[i] ).result( 0 ) : Read( ptr + adrList[i] ).result( 0 ); + ptr = wow64 ? Read( ptr + adrList[i] ) : Read( ptr + adrList[i] ); return Read( ptr + adrList.back(), dwSize, pResult, handleHoles ); } @@ -173,9 +180,21 @@ NTSTATUS ProcessMemory::Read( const std::vector& adrList, size_t dwSize, /// Size of data to write /// Buffer to write /// Status -NTSTATUS ProcessMemory::Write( ptr_t pAddress, size_t dwSize, const void* pData ) +NTSTATUS ProcessMemory::WriteNoThrow( ptr_t pAddress, size_t dwSize, const void * pData ) +{ + return _core->native()->WriteProcessMemoryT( pAddress, pData, dwSize ); +} + +/// +/// Write data +/// +/// Memory address to write to +/// Size of data to write +/// Buffer to write +void ProcessMemory::Write( ptr_t pAddress, size_t dwSize, const void* pData ) { - return _core.native()->WriteProcessMemoryT( pAddress, pData, dwSize ); + NTSTATUS status = _core->native()->WriteProcessMemoryT( pAddress, pData, dwSize ); + THROW_ON_FAIL_AND_LOG( status, "failed to write to address 0x%p", pAddress ); } /// @@ -184,21 +203,21 @@ NTSTATUS ProcessMemory::Write( ptr_t pAddress, size_t dwSize, const void* pData /// Base address + list of offsets /// Size of data to write /// Buffer to write -/// Status -NTSTATUS ProcessMemory::Write( const std::vector& adrList, size_t dwSize, const void* pData ) +void ProcessMemory::Write( const std::vector& adrList, size_t dwSize, const void* pData ) { if (adrList.empty()) - return STATUS_INVALID_PARAMETER; + THROW_AND_LOG( "address list is empty" ); + if (adrList.size() == 1) return Write( adrList.front(), dwSize, pData ); bool wow64 = _process->barrier().targetWow64; - ptr_t ptr = wow64 ? Read( adrList[0] ).result( 0 ) : Read( adrList[0] ).result( 0 ); + ptr_t ptr = wow64 ? Read( adrList[0] ): Read( adrList[0] ); for (size_t i = 1; i < adrList.size() - 1; i++) - ptr = wow64 ? Read( ptr + adrList[i] ).result( 0 ) : Read( ptr + adrList[i] ).result( 0 ); + ptr = wow64 ? Read( ptr + adrList[i] ) : Read( ptr + adrList[i] ); - return Write( ptr + adrList.back(), dwSize, pData ); + Write( ptr + adrList.back(), dwSize, pData ); } /// @@ -208,7 +227,7 @@ NTSTATUS ProcessMemory::Write( const std::vector& adrList, size_t dwSize, /// Found regions std::vector ProcessMemory::EnumRegions( bool includeFree /*= false*/ ) { - return _core.native()->EnumRegions( includeFree ); + return _core->native()->EnumRegions( includeFree ); } } diff --git a/src/BlackBone/Process/ProcessMemory.h b/src/BlackBone/Process/ProcessMemory.h index 9ce9bab4..abd50ad4 100644 --- a/src/BlackBone/Process/ProcessMemory.h +++ b/src/BlackBone/Process/ProcessMemory.h @@ -1,6 +1,7 @@ #pragma once #include "../Include/Winheaders.h" +#include "../Include/Exception.h" #include "RPC/RemoteMemory.h" #include "MemBlock.h" @@ -21,7 +22,9 @@ class ProcessMemory : public RemoteMemory { public: BLACKBONE_API ProcessMemory( class Process* process ); - BLACKBONE_API ~ProcessMemory(); + + ProcessMemory( const ProcessMemory& ) = delete; + ProcessMemory& operator =( const ProcessMemory& ) = delete; /// /// Allocate new memory block @@ -30,8 +33,8 @@ class ProcessMemory : public RemoteMemory /// Memory protection /// Desired base address of new block /// false if caller will be responsible for block deallocation - /// Memory block. If failed - returned block will be invalid - BLACKBONE_API call_result_t Allocate( size_t size, DWORD protection = PAGE_EXECUTE_READWRITE, ptr_t desired = 0, bool own = true ); + /// Memory block + BLACKBONE_API MemBlock Allocate( size_t size, DWORD protection = PAGE_EXECUTE_READWRITE, ptr_t desired = 0, bool own = true ); /// /// Allocate new memory block as close to a desired location as possible @@ -41,7 +44,7 @@ class ProcessMemory : public RemoteMemory /// Desired base address of new block /// false if caller will be responsible for block deallocation /// Memory block. If failed - returned block will be invalid - BLACKBONE_API call_result_t AllocateClosest( size_t size, DWORD protection = PAGE_EXECUTE_READWRITE, ptr_t desired = 0, bool own = true ); + BLACKBONE_API MemBlock AllocateClosest( size_t size, DWORD protection = PAGE_EXECUTE_READWRITE, ptr_t desired = 0, bool own = true ); /// /// Free memory @@ -81,7 +84,8 @@ class ProcessMemory : public RemoteMemory /// Otherwise function will fail if there is at least one non-committed page in region. /// /// Status - BLACKBONE_API NTSTATUS Read( ptr_t dwAddress, size_t dwSize, PVOID pResult, bool handleHoles = false ); + BLACKBONE_API NTSTATUS ReadNoThrow( ptr_t dwAddress, size_t dwSize, PVOID pResult, bool handleHoles = false ); + BLACKBONE_API void Read( ptr_t dwAddress, size_t dwSize, PVOID pResult, bool handleHoles = false ); /// /// Read data @@ -93,8 +97,7 @@ class ProcessMemory : public RemoteMemory /// If true, function will try to read all committed pages in range ignoring uncommitted ones. /// Otherwise function will fail if there is at least one non-committed page in region. /// - /// Status - BLACKBONE_API NTSTATUS Read( const std::vector& adrList, size_t dwSize, PVOID pResult, bool handleHoles = false ); + BLACKBONE_API void Read( const std::vector& adrList, size_t dwSize, PVOID pResult, bool handleHoles = false ); /// /// Write data @@ -103,7 +106,8 @@ class ProcessMemory : public RemoteMemory /// Size of data to write /// Buffer to write /// Status - BLACKBONE_API NTSTATUS Write( ptr_t pAddress, size_t dwSize, const void* pData ); + BLACKBONE_API NTSTATUS WriteNoThrow( ptr_t pAddress, size_t dwSize, const void* pData ); + BLACKBONE_API void Write( ptr_t pAddress, size_t dwSize, const void* pData ); /// /// Write data @@ -111,8 +115,7 @@ class ProcessMemory : public RemoteMemory /// Base address + list of offsets /// Size of data to write /// Buffer to write - /// Status - BLACKBONE_API NTSTATUS Write( const std::vector& adrList, size_t dwSize, const void* pData ); + BLACKBONE_API void Write( const std::vector& adrList, size_t dwSize, const void* pData ); /// /// Read data @@ -120,11 +123,11 @@ class ProcessMemory : public RemoteMemory /// Address to read from /// Read data template - inline call_result_t Read( ptr_t dwAddress ) + T Read( ptr_t dwAddress ) { - auto res = reinterpret_cast(_malloca( sizeof( T ) )); - auto status = Read( dwAddress, sizeof( T ), res ); - return call_result_t( *res, status ); + auto res = static_cast(_malloca( sizeof( T ) )); + Read( dwAddress, sizeof( T ), res ); + return *res; }; /// @@ -133,11 +136,11 @@ class ProcessMemory : public RemoteMemory /// Base address + list of offsets /// Read data template - inline call_result_t Read( std::vector&& adrList ) + T Read( std::vector&& adrList ) { - auto res = reinterpret_cast(_malloca( sizeof( T ) )); - auto status = Read( std::forward>( adrList ), sizeof( T ), res ); - return call_result_t( *res, status ); + auto res = static_cast(_malloca( sizeof( T ) )); + Read( std::forward>( adrList ), sizeof( T ), res ); + return *res; } /// @@ -145,24 +148,22 @@ class ProcessMemory : public RemoteMemory /// /// Address to read from /// Read data - /// Status code template - inline NTSTATUS Read( ptr_t dwAddress, T& result ) + void Read( ptr_t dwAddress, T& result ) { - return Read( dwAddress, sizeof( result ), &result ); - }; + Read( dwAddress, sizeof( result ), &result ); + } /// /// Read data /// /// Base address + list of offsets /// Read data - /// Status code template - inline NTSTATUS Read( std::vector&& adrList, T& result ) + void Read( std::vector&& adrList, T& result ) { - return Read( std::forward>( adrList ), sizeof( result ), &result ); - }; + Read( std::forward>( adrList ), sizeof( result ), &result ); + } /// /// Write data @@ -171,9 +172,9 @@ class ProcessMemory : public RemoteMemory /// Data to write /// Status template - inline NTSTATUS Write( ptr_t dwAddress, const T& data ) + void Write( ptr_t dwAddress, const T& data ) { - return Write( dwAddress, sizeof( T ), &data ); + Write( dwAddress, sizeof( T ), &data ); } /// @@ -183,9 +184,9 @@ class ProcessMemory : public RemoteMemory /// Data to write /// Status template - inline NTSTATUS Write( std::vector&& adrList, const T& data ) + void Write( std::vector&& adrList, const T& data ) { - return Write( std::forward>( adrList ), sizeof( T ), &data ); + Write( std::forward>( adrList ), sizeof( T ), &data ); } /// @@ -207,16 +208,12 @@ class ProcessMemory : public RemoteMemory /// new behavior BLACKBONE_API void protectionCasting( MemProtectionCasting casting ) { _casting = casting; } - BLACKBONE_API inline class ProcessCore& core() { return _core; } - BLACKBONE_API inline class Process* process() { return _process; } - -private: - ProcessMemory( const ProcessMemory& ) = delete; - ProcessMemory& operator =( const ProcessMemory& ) = delete; + BLACKBONE_API class ProcessCore* core() { return _core; } + BLACKBONE_API class Process* process() { return _process; } private: class Process* _process; // Owning process object - class ProcessCore& _core; // Core routines + class ProcessCore* _core; // Core routines MemProtectionCasting _casting = MemProtectionCasting::useDep; }; diff --git a/src/BlackBone/Process/ProcessModules.cpp b/src/BlackBone/Process/ProcessModules.cpp index f4683e99..c8ba81e1 100644 --- a/src/BlackBone/Process/ProcessModules.cpp +++ b/src/BlackBone/Process/ProcessModules.cpp @@ -2,6 +2,7 @@ #include "ProcessModules.h" #include "Process.h" #include "RPC/RemoteExec.h" +#include "../Include/Exception.h" #include "../Misc/NameResolve.h" #include "../Misc/Utils.h" #include "../Symbols/SymbolData.h" @@ -20,30 +21,27 @@ namespace blackbone { -ProcessModules::ProcessModules( class Process& proc ) + +ProcessModules::ProcessModules( class Process* proc ) : _proc( proc ) - , _memory( _proc.memory() ) - , _core( _proc.core() ) + , _memory( &_proc->memory() ) + , _core( &_proc->core() ) , _ldrPatched( false ) { } -ProcessModules::~ProcessModules() -{ -} - /// /// Get module by name /// /// Module name /// Module type. 32 bit or 64 bit -/// Saerch type. +/// Search type. /// Module data. nullptr if not found ModuleDataPtr ProcessModules::GetModule( const std::wstring& name, eModSeachType search /*= LdrList*/, eModType type /*= mt_default*/ - ) +) { std::wstring namecopy( Utils::StripPath( name ) ); return GetModule( namecopy, search, type ); @@ -62,13 +60,13 @@ ModuleDataPtr ProcessModules::GetModule( eModSeachType search /*= LdrList*/, eModType type /*= mt_default*/, const wchar_t* baseModule /*= L""*/ - ) +) { - NameResolve::Instance().ResolvePath( name, Utils::StripPath( baseModule ), L"", NameResolve::ApiSchemaOnly, _proc ); + NameResolve::Instance().ResolvePath( name, Utils::StripPath( baseModule ), L"", NameResolve::ApiSchemaOnly, *_proc ); // Detect module type if (type == mt_default) - type = _proc.barrier().targetWow64 ? mt_mod32 : mt_mod64; + type = _proc->barrier().targetWow64 ? mt_mod32 : mt_mod64; CSLock lck( _modGuard ); @@ -99,16 +97,16 @@ ModuleDataPtr ProcessModules::GetModule( bool strict /*= true*/, eModSeachType search /*= LdrList*/, eModType type /*= mt_default */ - ) +) { // Detect module type if (type == mt_default) - type = _proc.barrier().targetWow64 ? mt_mod32 : mt_mod64; + type = _proc->barrier().targetWow64 ? mt_mod32 : mt_mod64; CSLock lck( _modGuard ); auto compFn = [modBase, strict]( const mapModules::value_type& val ) - { + { if (strict) return (val.second->baseAddress == modBase); else @@ -120,14 +118,14 @@ ModuleDataPtr ProcessModules::GetModule( if (iter != _modules.end()) return iter->second; - UpdateModuleCache( search , type ); + UpdateModuleCache( search, type ); iter = std::find_if( _modules.begin(), _modules.end(), compFn ); if (iter != _modules.end()) return iter->second; - else - return nullptr; + + return nullptr; } /// @@ -136,18 +134,18 @@ ModuleDataPtr ProcessModules::GetModule( /// Module data. nullptr if not found ModuleDataPtr ProcessModules::GetMainModule() { - if (_proc.barrier().targetWow64) + if (_proc->barrier().targetWow64) { - _PEB32 peb = { 0 }; - if (_proc.core().peb32( &peb ) == 0) + _PEB32 peb = { }; + if (_proc->core().peb32( &peb ) == 0) return nullptr; return GetModule( peb.ImageBaseAddress ); } else { - _PEB64 peb = { 0 }; - if (_proc.core().peb64( &peb ) == 0) + _PEB64 peb = { }; + if (_proc->core().peb64( &peb ) == 0) return nullptr; return GetModule( peb.ImageBaseAddress ); @@ -161,15 +159,15 @@ ModuleDataPtr ProcessModules::GetMainModule() /// Module list const ProcessModules::mapModules& ProcessModules::GetAllModules( eModSeachType search /*= LdrList*/ ) { - eModType mt = _core.isWow64() ? mt_mod32 : mt_mod64; + eModType mt = _core->isWow64() ? mt_mod32 : mt_mod64; CSLock lck( _modGuard ); // Remove non-manual modules for (auto iter = _modules.begin(); iter != _modules.end();) { - if (!iter->second->manual) + if (!iter->second->manual) _modules.erase( iter++ ); - else + else ++iter; } @@ -190,10 +188,10 @@ const ProcessModules::mapModules& ProcessModules::GetAllModules( eModSeachType s ProcessModules::mapModules ProcessModules::GetManualModules() { ProcessModules::mapModules mods; - std::copy_if( - _modules.begin(), _modules.end(), - std::inserter( mods, mods.end() ), - []( const auto& mod ) { return mod.second->manual; } + std::copy_if( + _modules.begin(), _modules.end(), + std::inserter( mods, mods.end() ), + []( const auto& mod ) { return mod.second->manual; } ); return mods; @@ -206,7 +204,7 @@ ProcessModules::mapModules ProcessModules::GetManualModules() /// Function name or ordinal /// Import module name. Only used to resolve ApiSchema during manual map. /// Export info. If failed procAddress field is 0 -call_result_t ProcessModules::GetExport( const ModuleDataPtr& hMod, const char* name_ord, const wchar_t* baseModule /*= L""*/ ) +exportData ProcessModules::GetExport( const ModuleDataPtr& hMod, const char* name_ord, const wchar_t* baseModule /*= L""*/ ) { return GetExport( *hMod, name_ord, baseModule ); } @@ -218,32 +216,32 @@ call_result_t ProcessModules::GetExport( const ModuleDataPtr& hMod, /// Function name or ordinal /// Import module name. Only used to resolve ApiSchema during manual map. /// Export info. If failed procAddress field is 0 -call_result_t ProcessModules::GetExport( const ModuleData& hMod, const char* name_ord, const wchar_t* baseModule /*= L"0"*/ ) +exportData ProcessModules::GetExport( const ModuleData& hMod, const char* name_ord, const wchar_t* baseModule /*= L"0"*/ ) { exportData data; // Invalid module if (hMod.baseAddress == 0) - return STATUS_INVALID_PARAMETER_1; + THROW_AND_LOG( "invalid module base address: 0x%x", hMod.baseAddress ); std::unique_ptr expData( nullptr, &free ); - IMAGE_DOS_HEADER hdrDos = { 0 }; - uint8_t hdrNt32[sizeof( IMAGE_NT_HEADERS64 )] = { 0 }; + IMAGE_DOS_HEADER hdrDos = { }; + uint8_t hdrNt32[sizeof( IMAGE_NT_HEADERS64 )] = { }; auto phdrNt32 = reinterpret_cast(hdrNt32); auto phdrNt64 = reinterpret_cast(hdrNt32); DWORD expSize = 0; uintptr_t expBase = 0; - _memory.Read( hMod.baseAddress, sizeof( hdrDos ), &hdrDos ); + _memory->Read( hMod.baseAddress, sizeof( hdrDos ), &hdrDos ); if (hdrDos.e_magic != IMAGE_DOS_SIGNATURE) - return STATUS_INVALID_IMAGE_NOT_MZ; + THROW_AND_LOG( "image '%ls' in not a valid PE file, invalid DOS signature", hMod.name.c_str() ); - _memory.Read( hMod.baseAddress + hdrDos.e_lfanew, sizeof( IMAGE_NT_HEADERS64 ), &hdrNt32 ); + _memory->Read( hMod.baseAddress + hdrDos.e_lfanew, sizeof( IMAGE_NT_HEADERS64 ), &hdrNt32 ); if (phdrNt32->Signature != IMAGE_NT_SIGNATURE) - return STATUS_INVALID_IMAGE_FORMAT; + THROW_AND_LOG( "image '%ls' in not a valid PE file, invalid NT signature", hMod.name.c_str() ); if (phdrNt32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) expBase = phdrNt32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; @@ -261,22 +259,19 @@ call_result_t ProcessModules::GetExport( const ModuleData& hMod, con expData.reset( reinterpret_cast(malloc( expSize )) ); IMAGE_EXPORT_DIRECTORY* pExpData = expData.get(); - if( auto status = _memory.Read( hMod.baseAddress + expBase, expSize, pExpData ); !NT_SUCCESS( status ) ) - return status; + _memory->Read( hMod.baseAddress + expBase, expSize, pExpData ); // Fix invalid directory size if (expSize <= sizeof( IMAGE_EXPORT_DIRECTORY )) { // New size should take care of max number of present names (max name length is assumed to be 255 chars) - expSize = static_cast( - pExpData->AddressOfNameOrdinals - expBase - + max( pExpData->NumberOfFunctions, pExpData->NumberOfNames ) * 255 - ); + auto sectionLimit = max( pExpData->NumberOfFunctions, pExpData->NumberOfNames ) * 255; + expSize = static_cast(pExpData->AddressOfNameOrdinals - expBase + sectionLimit); expData.reset( reinterpret_cast(malloc( expSize )) ); pExpData = expData.get(); - if (auto status = _memory.Read( hMod.baseAddress + expBase, expSize, pExpData ); !NT_SUCCESS( status )) - return status; + + _memory->Read( hMod.baseAddress + expBase, expSize, pExpData ); } WORD* pAddressOfOrds = reinterpret_cast( @@ -305,7 +300,13 @@ call_result_t ProcessModules::GetExport( const ModuleData& hMod, con OrdIndex = static_cast(pAddressOfOrds[i]); } else - return STATUS_NOT_FOUND; + { + THROW_AND_LOG( + "export '%s' not found in the image '%ls', no more NumberOfNames in the export table", + Utils::NameOrdToString( name_ord ).c_str(), + hMod.name.c_str() + ); + } if ((reinterpret_cast(name_ord) <= 0xFFFF && (WORD)((uintptr_t)name_ord) == (OrdIndex + pExpData->Base)) || (reinterpret_cast(name_ord) > 0xFFFF && strcmp( pName, name_ord ) == 0)) @@ -316,9 +317,9 @@ call_result_t ProcessModules::GetExport( const ModuleData& hMod, con if (data.procAddress >= hMod.baseAddress + expBase && data.procAddress <= hMod.baseAddress + expBase + expSize) { - char forwardStr[255] = { 0 }; + char forwardStr[255] = { }; - _memory.Read( data.procAddress, sizeof( forwardStr ), forwardStr ); + _memory->Read( data.procAddress, sizeof( forwardStr ), forwardStr ); std::string chainExp( forwardStr ); @@ -340,14 +341,14 @@ call_result_t ProcessModules::GetExport( const ModuleData& hMod, con auto mt = (phdrNt32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) ? mt_mod32 : mt_mod64; auto hChainMod = GetModule( wDll, LdrList, mt, baseModule ); if (hChainMod == nullptr) - return call_result_t( data, STATUS_SOME_NOT_MAPPED ); + THROW_AND_LOG( "forward module '%ls' could not be found", wDll.c_str() ); // Import by ordinal if (data.forwardByOrd) return GetExport( hChainMod, reinterpret_cast(data.forwardOrdinal), wDll.c_str() ); + // Import by name - else - return GetExport( hChainMod, strName.c_str(), wDll.c_str() ); + return GetExport( hChainMod, strName.c_str(), wDll.c_str() ); } return data; @@ -355,7 +356,11 @@ call_result_t ProcessModules::GetExport( const ModuleData& hMod, con } } - return STATUS_NOT_FOUND; + THROW_AND_LOG( + "export '%s' not found in the image '%ls', no more entries in the export table", + Utils::NameOrdToString( name_ord ).c_str(), + hMod.name.c_str() + ); } /// @@ -364,7 +369,7 @@ call_result_t ProcessModules::GetExport( const ModuleData& hMod, con /// Module name to search in /// Function name or ordinal /// Export info. If failed procAddress field is 0 -call_result_t ProcessModules::GetExport( const std::wstring& modName, const char* name_ord ) +exportData ProcessModules::GetExport( const std::wstring& modName, const char* name_ord ) { return GetExport( GetModule( modName ), name_ord ); } @@ -376,15 +381,15 @@ call_result_t ProcessModules::GetExport( const std::wstring& modName /// Module type. 32 bit or 64 bit /// Search type. /// Export info. If failed procAddress field is 0 -call_result_t ProcessModules::GetNtdllExport( +exportData ProcessModules::GetNtdllExport( const char* name_ord, - eModType type /*= mt_default*/, - eModSeachType search /*= LdrList */ - ) + eModType type /*= mt_default*/, + eModSeachType search /*= LdrList */ +) { auto mod = GetModule( L"ntdll.dll", search, type ); if (!mod) - return STATUS_NOT_FOUND; + THROW_AND_LOG( "ntdll.dll is not loaded" ); return GetExport( mod, name_ord ); } @@ -395,66 +400,61 @@ call_result_t ProcessModules::GetNtdllExport( /// Full-qualified image path /// Injection status code /// Module info. nullptr if failed -call_result_t ProcessModules::Inject( const std::wstring& path, ThreadPtr pThread /*= nullptr*/ ) +ModuleDataPtr ProcessModules::Inject( const std::wstring& path, ThreadPtr pThread /*= nullptr*/ ) { - NTSTATUS status = STATUS_SUCCESS; ModuleDataPtr mod; pe::PEImage img; uint32_t ustrSize = 0; - ptr_t res = 0; + ptr_t res = 0; img.Load( path, true ); img.Release(); // Already loaded - if (mod = GetModule( path, LdrList, img.mType() )) - return call_result_t( mod, STATUS_IMAGE_ALREADY_LOADED ); + if (mod = GetModule( path, LdrList, img.mType() ); mod) + return mod; // Image path - auto modName = _memory.Allocate( 0x1000, PAGE_READWRITE ); - if (!modName) - return modName.status; + auto modName = _memory->Allocate( 0x1000, PAGE_READWRITE ); // Write dll name into target process auto fillDllName = [&modName, &path]( auto& ustr ) { - ustr.Buffer = modName->ptr::type>() + sizeof( ustr ); + ustr.Buffer = modName.ptr::type>() + sizeof( ustr ); ustr.MaximumLength = ustr.Length = static_cast(path.size() * sizeof( wchar_t )); - modName->Write( 0, ustr ); - modName->Write( sizeof( ustr ), path.size() * sizeof( wchar_t ), path.c_str() ); + modName.Write( 0, ustr ); + modName.Write( sizeof( ustr ), path.size() * sizeof( wchar_t ), path.c_str() ); return static_cast(sizeof( ustr )); }; if (img.mType() == mt_mod32) { - _UNICODE_STRING32 ustr = { 0 }; + _UNICODE_STRING32 ustr = { }; ustrSize = fillDllName( ustr ); } else if (img.mType() == mt_mod64) { - _UNICODE_STRING64 ustr = { 0 }; - ustrSize = fillDllName( ustr ); + _UNICODE_STRING64 ustr = { }; + ustrSize = fillDllName( ustr ); } else - return STATUS_INVALID_IMAGE_FORMAT; + THROW_AND_LOG( "unsupported image format: %d", img.mType() ); // Image and process have same processor architecture - bool sameArch = (img.mType() == mt_mod64 && _core.isWow64() == false) || (img.mType() == mt_mod32 && _core.isWow64() == true); + bool sameArch = (img.mType() == mt_mod64 && _core->isWow64() == false) || (img.mType() == mt_mod32 && _core->isWow64() == true); auto pLoadLibrary = GetExport( GetModule( L"kernel32.dll", LdrList, img.mType() ), "LoadLibraryW" ); // Can't inject 32bit dll into native process - if (!_proc.core().isWow64() && img.mType() == mt_mod32) - return STATUS_INVALID_IMAGE_WIN_32; + if (!_proc->core().isWow64() && img.mType() == mt_mod32) + THROW_AND_LOG( "can't inject x86 module into x64 process" ); auto switchMode = NoSwitch; - if (_proc.core().isWow64() && img.mType() == mt_mod64) + if (_proc->core().isWow64() && img.mType() == mt_mod64) switchMode = ForceSwitch; auto pLdrLoadDll = GetNtdllExport( "LdrLoadDll", img.mType(), Sections ); - if (!pLdrLoadDll) - return pLdrLoadDll.status; // Patch LdrFindOrMapDll to enable kernel32.dll loading if (switchMode == ForceSwitch && !_ldrPatched && IsWindows7OrGreater() && !IsWindows8OrGreater()) @@ -465,9 +465,9 @@ call_result_t ProcessModules::Inject( const std::wstring& path, T if (patchBase != 0) { DWORD flOld = 0; - _memory.Protect( patchBase, sizeof( patch ), PAGE_EXECUTE_READWRITE, &flOld ); - _memory.Write( patchBase, sizeof( patch ), patch ); - _memory.Protect( patchBase, sizeof( patch ), flOld, nullptr ); + _memory->Protect( patchBase, sizeof( patch ), PAGE_EXECUTE_READWRITE, &flOld ); + _memory->Write( patchBase, sizeof( patch ), patch ); + _memory->Protect( patchBase, sizeof( patch ), flOld, nullptr ); } _ldrPatched = true; @@ -475,37 +475,31 @@ call_result_t ProcessModules::Inject( const std::wstring& path, T auto a = AsmFactory::GetAssembler( img.mType() ); - a->GenCall( pLdrLoadDll->procAddress, { 0, 0, modName->ptr(), modName->ptr() + 0x800 } ); + a->GenCall( pLdrLoadDll.procAddress, { 0, 0, modName.ptr(), modName.ptr() + 0x800 } ); (*a)->ret(); - _proc.remote().CreateRPCEnvironment( Worker_None, true ); + _proc->remote().CreateRPCEnvironment( Worker_None, true ); // Execute call + NTSTATUS status = STATUS_SUCCESS; if (pThread != nullptr) { - if (pThread == _proc.remote().getWorker()) - status = _proc.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), res ); + if (pThread == _proc->remote().getWorker()) + res = _proc->remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize() ); else - status = _proc.remote().ExecInAnyThread( (*a)->make(), (*a)->getCodeSize(), res, pThread ); + res = _proc->remote().ExecInAnyThread( (*a)->make(), (*a)->getCodeSize(), pThread ); - if (NT_SUCCESS( status )) - status = static_cast(res); + status = static_cast(res); } else - { - status = _proc.remote().ExecInNewThread( (*a)->make(), (*a)->getCodeSize(), res, switchMode ); - if (NT_SUCCESS( status )) - status = static_cast(res); - } + _proc->remote().ExecInNewThread( (*a)->make(), (*a)->getCodeSize(), switchMode ); // Retry with LoadLibrary if possible - if (!NT_SUCCESS(status) && pLoadLibrary && sameArch) + if (!NT_SUCCESS( status ) && sameArch) { - auto result = _proc.remote().ExecDirect( pLoadLibrary->procAddress, modName->ptr() + ustrSize ); + auto result = _proc->remote().ExecDirect( pLoadLibrary.procAddress, modName.ptr() + ustrSize ); if (result == 0) - { - return status; - } + THROW_WITH_STATUS_AND_LOG( status, "failed to load image via LoadLibrary" ); } return GetModule( path, LdrList, img.mType() ); @@ -520,25 +514,22 @@ NTSTATUS ProcessModules::Unload( const ModuleDataPtr& hMod ) { // Module not present or is manually mapped if (hMod->manual || !ValidateModule( hMod->baseAddress )) - return STATUS_NOT_FOUND; + THROW_AND_LOG( "can't unload, invalid module at base 0x%x", hMod->baseAddress ); // Unload routine auto pUnload = GetNtdllExport( "LdrUnloadDll", hMod->type ); - if (!pUnload) - return pUnload.status; // Special case for unloading 64 bit modules from WOW64 process auto threadSwitch = NoSwitch; - if (_proc.core().isWow64() && hMod->type == mt_mod64) + if (_proc->core().isWow64() && hMod->type == mt_mod64) threadSwitch = ForceSwitch; - uint64_t res = 0; auto a = AsmFactory::GetAssembler( hMod->type ); - a->GenCall( pUnload->procAddress, { hMod->baseAddress } ); + a->GenCall( pUnload.procAddress, { hMod->baseAddress } ); (*a)->ret(); - _proc.remote().ExecInNewThread( (*a)->make(), (*a)->getCodeSize(), res, threadSwitch ); + _proc->remote().ExecInNewThread( (*a)->make(), (*a)->getCodeSize(), threadSwitch ); // Remove module from cache _modules.erase( std::make_pair( hMod->name, hMod->type ) ); @@ -552,12 +543,12 @@ NTSTATUS ProcessModules::Unload( const ModuleDataPtr& hMod ) /// true on success bool ProcessModules::Unlink( const ModuleDataPtr& mod ) { - return _proc.nativeLdr().Unlink( *mod ); + return _proc->nativeLdr().Unlink( *mod ); } bool ProcessModules::Unlink( const ModuleData& mod ) { - return _proc.nativeLdr().Unlink( mod ); + return _proc->nativeLdr().Unlink( mod ); } /// @@ -567,15 +558,10 @@ bool ProcessModules::Unlink( const ModuleData& mod ) /// true on success bool ProcessModules::ValidateModule( module_t base ) { - IMAGE_DOS_HEADER idh = { 0 }; - IMAGE_NT_HEADERS inth = { 0 }; - // Validate memory and headers - if (_memory.Read( base, sizeof(idh), &idh ) == STATUS_SUCCESS) - if (idh.e_magic == IMAGE_DOS_SIGNATURE) - if(_memory.Read( base + idh.e_lfanew, sizeof(inth), &inth ) == STATUS_SUCCESS) - if (inth.Signature == IMAGE_NT_SIGNATURE) - return true; + auto idh = _memory->Read( base ); + if (idh.e_magic == IMAGE_DOS_SIGNATURE) + return _memory->Read( base + idh.e_lfanew ).Signature == IMAGE_NT_SIGNATURE; return false; } @@ -627,11 +613,10 @@ void ProcessModules::RemoveManualModule( const std::wstring& filename, eModType void ProcessModules::UpdateModuleCache( eModSeachType search, eModType type ) { - for (const auto& mod : _core.native()->EnumModules( search, type )) + for (const auto& mod : _core->native()->EnumModules( search, type )) _modules.emplace( std::make_pair( mod->name, mod->type ), mod ); } - using namespace asmjit; using namespace asmjit::host; @@ -645,58 +630,47 @@ using namespace asmjit::host; /// Method to call /// Arguments passed into method /// Return code -/// true on success -bool ProcessModules::InjectPureIL( +void ProcessModules::InjectPureIL( const std::wstring& netVersion, const std::wstring& netAssemblyPath, const std::wstring& netAssemblyMethod, const std::wstring& netAssemblyArgs, DWORD& returnCode - ) +) { // split netAssemblyMethod string into class and method names size_t idx = netAssemblyMethod.find_last_of( '.' ); if (idx == std::wstring::npos) - return false; + THROW_AND_LOG( "invalid entry point, could not detect class name" ); std::wstring MethodName = netAssemblyMethod.substr( idx + 1 ); std::wstring tmp = netAssemblyMethod; tmp.erase( idx ); std::wstring ClassName = tmp; - auto mem = _memory.Allocate( 0x10000 ); - if (!mem) - { - returnCode = 7; - return false; - } + MemBlock address; + address = _memory->Allocate( 0x10000 ); - auto address = std::move( mem.result() ); uintptr_t offset = 4; uintptr_t address_VersionString, address_netAssemblyDll, address_netAssemblyClass, - address_netAssemblyMethod, address_netAssemblyArgs; + address_netAssemblyMethod, address_netAssemblyArgs; const std::wstring* strArr[] = { &netVersion, &netAssemblyPath, &ClassName, &MethodName, &netAssemblyArgs }; - uintptr_t* ofstArr[] = { + uintptr_t* ofstArr[] = { &address_VersionString, &address_netAssemblyDll, &address_netAssemblyClass, - &address_netAssemblyMethod, &address_netAssemblyArgs + &address_netAssemblyMethod, &address_netAssemblyArgs }; // Write strings - for (int i = 0; i < ARRAYSIZE( strArr ); i++) + for (size_t i = 0; i < std::size( strArr ); i++) { size_t len = strArr[i]->length(); *(ofstArr[i]) = address.ptr() + offset; // runtime version to use - if (address.Write( offset, len * sizeof(wchar_t) + 2, strArr[i]->c_str( ) ) != STATUS_SUCCESS) - { - returnCode = 5; - return false; - } - + address.Write( offset, len * sizeof( wchar_t ) + 2, strArr[i]->c_str() ); offset = Align( offset + len * sizeof( wchar_t ) + 2, sizeof( uint64_t ) ); } @@ -705,38 +679,27 @@ bool ProcessModules::InjectPureIL( GUID GArray[] = { CLSID_CLRMetaHost, IID_ICLRMetaHost, IID_ICLRRuntimeInfo, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost }; // COM object GUIDs - if (address.Write( offset, sizeof(GArray), GArray ) != STATUS_SUCCESS) - { - returnCode = 10; - return false; - } + address.Write( offset, sizeof( GArray ), GArray ); - uintptr_t address_CLSID_CLRMetaHost = address.ptr() + offset + 0; - uintptr_t address_IID_ICLRMetaHost = address.ptr() + offset + 0x10; - uintptr_t address_IID_ICLRRuntimeInfo = address.ptr() + offset + 0x20; + uintptr_t address_CLSID_CLRMetaHost = address.ptr() + offset + 0; + uintptr_t address_IID_ICLRMetaHost = address.ptr() + offset + 0x10; + uintptr_t address_IID_ICLRRuntimeInfo = address.ptr() + offset + 0x20; uintptr_t address_CLSID_CLRRuntimeHost = address.ptr() + offset + 0x30; - uintptr_t address_IID_ICLRRuntimeHost = address.ptr() + offset + 0x40; + uintptr_t address_IID_ICLRRuntimeHost = address.ptr() + offset + 0x40; - offset += sizeof(GArray); + offset += sizeof( GArray ); std::wstring libName = L"mscoree.dll"; - NameResolve::Instance().ResolvePath( libName, L"", L"", NameResolve::EnsureFullPath, _proc ); + NameResolve::Instance().ResolvePath( libName, L"", L"", NameResolve::EnsureFullPath, *_proc ); auto pMscoree = Inject( libName ); - if(!pMscoree) - { - returnCode = pMscoree.status; - return false; - } // CLRCreateInstance address - auto pfnCreateInstance = GetExport( pMscoree.result(), "CLRCreateInstance" ); - if (!pfnCreateInstance) - return false; + auto pfnCreateInstance = GetExport( pMscoree, "CLRCreateInstance" ); // Scary assembler code incoming! - auto pAsm = AsmFactory::GetAssembler( _proc.core().isWow64() ); + auto pAsm = AsmFactory::GetAssembler( _proc->core().isWow64() ); auto& a = *pAsm; AsmStackAllocator sa( a.assembler(), 0x30 ); // 0x30 - 6 arguments of ExecuteInDefaultAppDomain @@ -754,12 +717,12 @@ bool ProcessModules::InjectPureIL( Label L_ReleaseInterface = a->newLabel(); // stack variables for the injected code - ALLOC_STACK_VAR( sa, stack_MetaHost, ICLRMetaHost* ); - ALLOC_STACK_VAR( sa, stack_RuntimeInfo, ICLRRuntimeInfo* ); - ALLOC_STACK_VAR( sa, stack_RuntimeHost, ICLRRuntimeHost* ); - ALLOC_STACK_VAR( sa, stack_IsStarted, BOOL ); + ALLOC_STACK_VAR( sa, stack_MetaHost, ICLRMetaHost* ); + ALLOC_STACK_VAR( sa, stack_RuntimeInfo, ICLRRuntimeInfo* ); + ALLOC_STACK_VAR( sa, stack_RuntimeHost, ICLRRuntimeHost* ); + ALLOC_STACK_VAR( sa, stack_IsStarted, BOOL ); ALLOC_STACK_VAR( sa, stack_StartupFlags, DWORD ); - ALLOC_STACK_VAR( sa, stack_returnCode, HRESULT ); + ALLOC_STACK_VAR( sa, stack_returnCode, HRESULT ); #ifdef USE64 GpReg callReg = r13; @@ -776,7 +739,7 @@ bool ProcessModules::InjectPureIL( a->xor_( a->zsi, a->zsi ); // CLRCreateInstance() - a.GenCall( (uintptr_t)pfnCreateInstance->procAddress, { address_CLSID_CLRMetaHost, address_IID_ICLRMetaHost, &stack_MetaHost } ); + a.GenCall( static_cast(pfnCreateInstance.procAddress), { address_CLSID_CLRMetaHost, address_IID_ICLRMetaHost, &stack_MetaHost } ); // success? a->test( a->zax, a->zax ); a->jnz( L_Error1 ); @@ -789,7 +752,7 @@ bool ProcessModules::InjectPureIL( // success? a->test( a->zax, a->zax ); a->jnz( L_Error2 ); - + // pRuntimeInterface->IsStarted() a->mov( a->zcx, stack_RuntimeInfo ); a->mov( a->zax, a->intptr_ptr( a->zcx ) ); @@ -810,7 +773,7 @@ bool ProcessModules::InjectPureIL( // jump if already started a->cmp( stack_IsStarted, a->zsi ); - a->jne( L_SkipStart ); + a->jne( L_SkipStart ); // pRuntimeHost->Start() a->mov( a->zcx, stack_RuntimeHost ); @@ -931,23 +894,18 @@ bool ProcessModules::InjectPureIL( a->setBaseAddress( codeAddress ); a->relocCode( codeBuffer.data() ); - if (address.Write( offset, codeSize, codeBuffer.data() ) != STATUS_SUCCESS) - { - returnCode = 17; - return false; - } + address.Write( offset, codeSize, codeBuffer.data() ); // run ze codez - _proc.remote().ExecDirect( (ptr_t)codeAddress, 0 ); + _proc->remote().ExecDirect( (ptr_t)codeAddress, 0 ); // Get the managed return value address.Read( 0, 4, &returnCode ); - - return true; } #endif // COMPILER_MSVC + /// /// Reset local data /// @@ -955,7 +913,7 @@ void ProcessModules::reset() { CSLock lck( _modGuard ); - _modules.clear(); + _modules.clear(); _ldrPatched = false; } diff --git a/src/BlackBone/Process/ProcessModules.h b/src/BlackBone/Process/ProcessModules.h index 2d8e43ec..f2a144e3 100644 --- a/src/BlackBone/Process/ProcessModules.h +++ b/src/BlackBone/Process/ProcessModules.h @@ -2,7 +2,6 @@ #include "../Config.h" #include "../Include/Winheaders.h" -#include "../Include/CallResult.h" #include "../Include/Types.h" #include "../PE/PEImage.h" #include "../Misc/Utils.h" @@ -13,18 +12,15 @@ #include #include -namespace std +template <> +struct std::hash> { - template <> - struct hash < struct pair > + size_t operator()( const std::pair& value ) const { - size_t operator()( const pair& value ) const - { - hash sh; - return sh( value.first ) ^ value.second; - } - }; -} + std::hash sh; + return sh( value.first ) ^ value.second; + } +}; namespace blackbone @@ -40,16 +36,23 @@ struct exportData bool isForwarded = false; // Function is forwarded to another module bool forwardByOrd = false; // Forward is done by ordinal + + explicit operator ptr_t() + { + return procAddress; + } }; class ProcessModules { public: - using mapModules = std::unordered_map, ModuleDataPtr> ; + using mapModules = std::unordered_map, ModuleDataPtr>; public: - BLACKBONE_API ProcessModules( class Process& proc ); - BLACKBONE_API ~ProcessModules(); + BLACKBONE_API ProcessModules( class Process* proc ); + + ProcessModules( const ProcessModules& ) = delete; + ProcessModules operator =( const ProcessModules& ) = delete; /// /// Get module by name @@ -62,7 +65,7 @@ class ProcessModules const std::wstring& name, eModSeachType search = LdrList, eModType type = mt_default - ); + ); /// /// Get module by name @@ -77,7 +80,7 @@ class ProcessModules eModSeachType search = LdrList, eModType type = mt_default, const wchar_t* baseModule = L"" - ); + ); /// /// Get module by base address @@ -92,7 +95,7 @@ class ProcessModules bool strict = true, eModSeachType search = LdrList, eModType type = mt_default - ); + ); /// /// Get process main module @@ -120,10 +123,10 @@ class ProcessModules /// Function name or ordinal /// Import module name. Only used to resolve ApiSchema during manual map. /// Export info. If failed procAddress field is 0 - BLACKBONE_API call_result_t GetExport( - const ModuleDataPtr& hMod, - const char* name_ord, - const wchar_t* baseModule = L"" + BLACKBONE_API exportData GetExport( + const ModuleDataPtr& hMod, + const char* name_ord, + const wchar_t* baseModule = L"" ); /// @@ -133,7 +136,7 @@ class ProcessModules /// Function name or ordinal /// Import module name. Only used to resolve ApiSchema during manual map. /// Export info. If failed procAddress field is 0 - BLACKBONE_API call_result_t GetExport( + BLACKBONE_API exportData GetExport( const ModuleData& hMod, const char* name_ord, const wchar_t* baseModule = L"" @@ -145,7 +148,7 @@ class ProcessModules /// Module name to search in /// Function name or ordinal /// Export info. If failed procAddress field is 0 - BLACKBONE_API call_result_t GetExport( const std::wstring& modName, const char* name_ord ); + BLACKBONE_API exportData GetExport( const std::wstring& modName, const char* name_ord ); /// /// Get export from ntdll @@ -154,10 +157,10 @@ class ProcessModules /// Module type. 32 bit or 64 bit /// Search type. /// Export info. If failed procAddress field is 0 - BLACKBONE_API call_result_t GetNtdllExport( - const char* name_ord, - eModType type = mt_default, - eModSeachType search = LdrList + BLACKBONE_API exportData GetNtdllExport( + const char* name_ord, + eModType type = mt_default, + eModSeachType search = LdrList ); /// @@ -165,7 +168,7 @@ class ProcessModules /// /// Full-qualified image path /// Module info. nullptr if failed - BLACKBONE_API call_result_t Inject( const std::wstring& path, ThreadPtr pThread = nullptr ); + BLACKBONE_API ModuleDataPtr Inject( const std::wstring& path, ThreadPtr pThread = nullptr ); #ifdef COMPILER_MSVC /// @@ -176,14 +179,13 @@ class ProcessModules /// Method to call /// Arguments passed into method /// Return code - /// true on success - BLACKBONE_API bool InjectPureIL( + BLACKBONE_API void InjectPureIL( const std::wstring& netVersion, const std::wstring& netAssemblyPath, const std::wstring& netAssemblyMethod, const std::wstring& netAssemblyArgs, DWORD& returnCode - ); + ); #endif /// @@ -236,15 +238,12 @@ class ProcessModules BLACKBONE_API void reset(); private: - ProcessModules( const ProcessModules& ) = delete; - ProcessModules operator =(const ProcessModules&) = delete; - void UpdateModuleCache( eModSeachType search, eModType type ); private: - class Process& _proc; - class ProcessMemory& _memory; - class ProcessCore& _core; + class Process* _proc; + class ProcessMemory* _memory; + class ProcessCore* _core; mapModules _modules; // Fast lookup cache CriticalSection _modGuard; // Module guard diff --git a/src/BlackBone/Process/RPC/RemoteContext.hpp b/src/BlackBone/Process/RPC/RemoteContext.hpp index f34f5711..a9e64e04 100644 --- a/src/BlackBone/Process/RPC/RemoteContext.hpp +++ b/src/BlackBone/Process/RPC/RemoteContext.hpp @@ -14,56 +14,55 @@ namespace blackbone class RemoteContext { public: - BLACKBONE_API RemoteContext( - ProcessMemory& memory, - Thread& thd, - _CONTEXT64& ctx, + BLACKBONE_API RemoteContext( + ProcessMemory* memory, + Thread* thd, + _CONTEXT64* ctx, ptr_t frame_ptr, BOOL x64, int wordSize - ) + ) : _memory( memory ) , _thd( thd ) , _ctx( ctx ) , _x64Target( x64 ) , _wordSize( wordSize ) - , _frame_ptr( frame_ptr != 0 ? frame_ptr : ctx.Rsp ) - { - } - - BLACKBONE_API ~RemoteContext() + , _frame_ptr( frame_ptr != 0 ? frame_ptr : ctx->Rsp ) { } + RemoteContext( const RemoteContext& ) = delete; + RemoteContext& operator = ( const RemoteContext& ) = delete; + // memory object - BLACKBONE_API inline ProcessMemory& memory() + BLACKBONE_API ProcessMemory* memory() { return _memory; } // Native context - BLACKBONE_API inline _CONTEXT64& native() - { - return _ctx; + BLACKBONE_API _CONTEXT64& native() + { + return *_ctx; } /// /// Get current process thread where exception occurred /// /// Thread - BLACKBONE_API inline Thread& getThread() + BLACKBONE_API Thread& getThread() { - return _thd; + return *_thd; } /// - /// + /// Get current frame return address /// /// Return address - BLACKBONE_API inline const ptr_t returnAddress() const - { + BLACKBONE_API ptr_t returnAddress() const + { ptr_t val = 0; - _memory.Read( _frame_ptr, _wordSize, &val ); + _memory->Read( _frame_ptr, _wordSize, &val ); return val; } @@ -72,20 +71,19 @@ class RemoteContext /// Set return address of current frame /// /// New return address - /// true on success - BLACKBONE_API inline bool returnAddress( ptr_t val ) const - { - return (_memory.Write( _frame_ptr, _wordSize, &val ) == STATUS_SUCCESS); + BLACKBONE_API void returnAddress( ptr_t val ) const + { + _memory->Write( _frame_ptr, _wordSize, &val ); } - + /// /// Set new integer return value. Has no effect on FPU. /// Has effect only if called in return callback /// /// New return value - BLACKBONE_API inline void setReturnValue( ptr_t val ) const - { - memcpy( &_ctx.Rax, &val, _wordSize ); + BLACKBONE_API void setReturnValue( ptr_t val ) const + { + memcpy( &_ctx->Rax, &val, _wordSize ); } /// @@ -96,8 +94,7 @@ class RemoteContext { ptr_t val = returnAddress(); SET_BIT( val, (_wordSize * 8 - 1) ); - if (returnAddress( val ) == false) - return 0; + returnAddress( val ); return val; } @@ -110,8 +107,7 @@ class RemoteContext { auto val = returnAddress(); RESET_BIT( val, (_wordSize * 8 - 1) ); - if (!returnAddress( val )) - return 0; + returnAddress( val ); return val; } @@ -126,27 +122,27 @@ class RemoteContext /// Argument value BLACKBONE_API DWORD64 getArg( int index ) { - if(_x64Target) + if (_x64Target) { switch (index) { - case 0: - return _ctx.Rcx; - case 1: - return _ctx.Rdx; - case 2: - return _ctx.R8; - case 3: - return _ctx.R9; - - default: - return _memory.Read( _ctx.Rsp + 0x28 + (index - 4) * _wordSize ).result( 0 ); + case 0: + return _ctx->Rcx; + case 1: + return _ctx->Rdx; + case 2: + return _ctx->R8; + case 3: + return _ctx->R9; + + default: + return _memory->Read( _ctx->Rsp + 0x28 + (index - 4) * _wordSize ); } } else { DWORD64 val = 0; - _memory.Read( _ctx.Rsp + 4 + index * _wordSize, _wordSize, &val ); + _memory->Read( _ctx->Rsp + 4 + index * _wordSize, _wordSize, &val ); return val; } } @@ -158,35 +154,32 @@ class RemoteContext /// /// 0-based argument index /// New argument value - /// true on success - BLACKBONE_API bool setArg( int index, DWORD64 val ) + BLACKBONE_API void setArg( int index, DWORD64 val ) { if (_x64Target) { switch (index) { - case 0: - _ctx.Rcx = val; - break; - case 1: - _ctx.Rdx = val; - break; - case 2: - _ctx.R8 = val; - break; - case 3: - _ctx.R9 = val; - break; - - default: - return (_memory.Write( _ctx.Rsp + 0x28 + (index - 4) * _wordSize, val ) == STATUS_SUCCESS); + case 0: + _ctx->Rcx = val; + break; + case 1: + _ctx->Rdx = val; + break; + case 2: + _ctx->R8 = val; + break; + case 3: + _ctx->R9 = val; + break; + + default: + _memory->Write( _ctx->Rsp + 0x28 + (index - 4) * _wordSize, val ); } - - return true; } else { - return (_memory.Write( _ctx.Rsp + 4 + index * _wordSize, _wordSize, &val ) == STATUS_SUCCESS); + _memory->Write( _ctx->Rsp + 4 + index * _wordSize, _wordSize, &val ); } } @@ -198,49 +191,47 @@ class RemoteContext BLACKBONE_API DWORD lastError() { ptr_t pteb = 0; - LONG offset = 0; + size_t offset = 0; - if( _x64Target ) + if (_x64Target) { - pteb = _thd.teb( (_TEB64*)nullptr ); - offset = FIELD_OFFSET( _TEB64, LastErrorValue ); + pteb = _thd->teb64(); + offset = offsetOf( &_TEB64::LastErrorValue ); } else { - pteb = _thd.teb( (_TEB32*)nullptr ); - offset = FIELD_OFFSET( _TEB32, LastErrorValue ); + pteb = _thd->teb32(); + offset = offsetOf( &_TEB32::LastErrorValue ); } + DWORD code = 0xFFFFFFFF; if (pteb) - return _memory.Read( pteb + offset ).result( 0xFFFFFFFF ); + _memory->Read( pteb + offset, code ); - return 0xFFFFFFFF; + return code; } /// /// Set last thread error code /// /// Last error code, -1 if function failed - BLACKBONE_API DWORD lastError( DWORD newError ) + BLACKBONE_API void lastError( DWORD newError ) { ptr_t pteb = 0; - LONG offset = 0; + size_t offset = 0; if (_x64Target) { - pteb = _thd.teb( (_TEB64*)nullptr ); - offset = FIELD_OFFSET( _TEB64, LastErrorValue ); + pteb = _thd->teb64(); + offset = offsetOf( &_TEB64::LastErrorValue ); } else { - pteb = _thd.teb( (_TEB32*)nullptr ); - offset = FIELD_OFFSET( _TEB32, LastErrorValue ); + pteb = _thd->teb32(); + offset = offsetOf( &_TEB32::LastErrorValue ); } - if (!pteb) - return 0xFFFFFFFF; - - return _memory.Write( pteb + offset, newError ); + _memory->Write( pteb + offset, newError ); } @@ -250,38 +241,24 @@ class RemoteContext /// Data value BLACKBONE_API ptr_t getUserContext() { - auto pteb = _thd.teb( (_TEB64*)nullptr ); - if (!pteb) - return 0; - - return _memory.Read( pteb + FIELD_OFFSET( _NT_TIB_T, ArbitraryUserPointer ) ).result( 0 ); + auto pteb = _thd->teb64(); + return _memory->Read( pteb + offsetOf( &_NT_TIB_T::ArbitraryUserPointer ) ); } /// /// Set arbitrary thread data /// /// true on success - BLACKBONE_API bool setUserContext( ptr_t context ) + BLACKBONE_API void setUserContext( ptr_t context ) { - auto pteb = _thd.teb( (_TEB64*)nullptr ); - if(pteb) - { - if (_memory.Write( pteb + FIELD_OFFSET( _NT_TIB_T, ArbitraryUserPointer ), context ) == STATUS_SUCCESS) - return true; - } - - return false; + auto pteb = _thd->teb64(); + _memory->Write( pteb + offsetOf( &_NT_TIB_T::ArbitraryUserPointer ), context ); } - -private: - RemoteContext( const RemoteContext& ) = delete; - RemoteContext& operator = ( const RemoteContext& ) = delete; - private: - ProcessMemory& _memory; // Process memory routines - Thread& _thd; // Current thread - _CONTEXT64& _ctx; // Current thread context + ProcessMemory* _memory; // Process memory routines + Thread* _thd; // Current thread + _CONTEXT64* _ctx; // Current thread context BOOL _x64Target = FALSE; // Target process is 64 bit int _wordSize = 4; // 4 for x86, 8 for x64 diff --git a/src/BlackBone/Process/RPC/RemoteExec.cpp b/src/BlackBone/Process/RPC/RemoteExec.cpp index f907df9b..be8803e1 100644 --- a/src/BlackBone/Process/RPC/RemoteExec.cpp +++ b/src/BlackBone/Process/RPC/RemoteExec.cpp @@ -11,12 +11,12 @@ namespace blackbone { -RemoteExec::RemoteExec( Process& proc ) +RemoteExec::RemoteExec( Process* proc ) : _process( proc ) - , _mods( _process.modules() ) - , _memory( _process.memory() ) - , _threads( _process.threads() ) - , _hWaitEvent( NULL ) + , _mods( &proc->modules() ) + , _memory( &proc->memory() ) + , _threads( &proc->threads() ) + , _hWaitEvent( nullptr ) , _apcPatched( false ) , _currentBufferIdx( 0 ) { @@ -32,53 +32,42 @@ RemoteExec::~RemoteExec() /// /// Code to execute /// Code size -/// Code return value /// Switch wow64 thread to long mode upon creation -/// Status -NTSTATUS RemoteExec::ExecInNewThread( - PVOID pCode, size_t size, - uint64_t& callResult, - eThreadModeSwitch modeSwitch /*= AutoSwitch*/ - ) +/// Return code +uint64_t RemoteExec::ExecInNewThread( PVOID pCode, size_t size, eThreadModeSwitch modeSwitch /*= AutoSwitch*/ ) { - NTSTATUS status = STATUS_SUCCESS; - // Write code - if (!NT_SUCCESS( status = CopyCode( pCode, size ) )) - return status; + CopyCode( pCode, size ); bool switchMode = false; switch (modeSwitch) { - case blackbone::ForceSwitch: - switchMode = true; - break; + case blackbone::ForceSwitch: + switchMode = true; + break; - case blackbone::AutoSwitch: - switchMode = _process.barrier().type == wow_32_64; - break; + case blackbone::AutoSwitch: + switchMode = _process->barrier().type == wow_32_64; + break; } - auto a = switchMode ? AsmFactory::GetAssembler( AsmFactory::asm64 ) - : AsmFactory::GetAssembler( _process.core().isWow64() ); + auto a = switchMode ? AsmFactory::GetAssembler( AsmFactory::asm64 ) + : AsmFactory::GetAssembler( _process->core().isWow64() ); a->GenPrologue( switchMode ); // Prepare thread to run in x64 mode - if(switchMode) + if (switchMode) { // Allocate new x64 activation stack - auto createActStack = _mods.GetNtdllExport( "RtlAllocateActivationContextStack", mt_mod64 ); - if (createActStack) - { - a->GenCall( createActStack->procAddress, { _userData[_currentBufferIdx].ptr() + 0x3100 } ); + auto createActStack = _mods->GetNtdllExport( "RtlAllocateActivationContextStack", mt_mod64 ); + a->GenCall( createActStack.procAddress, { _userData[_currentBufferIdx].ptr() + 0x3100 } ); - (*a)->mov( (*a)->zax, _userData[_currentBufferIdx].ptr() + 0x3100 ); - (*a)->mov( (*a)->zax, (*a)->intptr_ptr( (*a)->zax ) ); + (*a)->mov( (*a)->zax, _userData[_currentBufferIdx].ptr() + 0x3100 ); + (*a)->mov( (*a)->zax, (*a)->intptr_ptr( (*a)->zax ) ); - (*a)->mov( (*a)->zdx, asmjit::host::dword_ptr_abs( 0x30 ).setSegment( asmjit::host::gs ) ); - (*a)->mov( (*a)->intptr_ptr( (*a)->zdx, 0x2C8 ), (*a)->zax ); - } + (*a)->mov( (*a)->zdx, asmjit::host::dword_ptr_abs( 0x30 ).setSegment( asmjit::host::gs ) ); + (*a)->mov( (*a)->intptr_ptr( (*a)->zdx, 0x2C8 ), (*a)->zax ); } a->GenCall( _userCode[_currentBufferIdx].ptr(), { } ); @@ -87,46 +76,36 @@ NTSTATUS RemoteExec::ExecInNewThread( a->GenEpilogue( switchMode, 4 ); // Execute code in newly created thread - if (!NT_SUCCESS( status = _userCode[_currentBufferIdx].Write( size, (*a)->getCodeSize(), (*a)->make() ) )) - return status; + _userCode[_currentBufferIdx].Write( size, (*a)->getCodeSize(), (*a)->make() ); - auto thread = _threads.CreateNew( _userCode[_currentBufferIdx].ptr() + size, _userData[_currentBufferIdx].ptr()/*, HideFromDebug*/ ); - if (!thread) - return thread.status; - if (!(*thread)->Join()) - return LastNtStatus(); + auto thread = _threads->CreateNew( _userCode[_currentBufferIdx].ptr() + size, _userData[_currentBufferIdx].ptr()/*, HideFromDebug*/ ); + if (!thread->Join()) + THROW_WITH_STATUS_AND_LOG( LastNtStatus(), "failed to join thread" ); - callResult = _userData[_currentBufferIdx].Read( INTRET_OFFSET, 0 ); + auto callResult = _userData[_currentBufferIdx].Read( INTRET_OFFSET, 0 ); SwitchActiveBuffer(); - return STATUS_SUCCESS; + + return callResult; } /// /// Execute code in context of our worker thread /// -/// Cde to execute +/// Code to execute /// Code size. -/// Execution result -/// Status -NTSTATUS RemoteExec::ExecInWorkerThread( PVOID pCode, size_t size, uint64_t& callResult ) +/// Call result +uint64_t RemoteExec::ExecInWorkerThread( PVOID pCode, size_t size ) { - NTSTATUS status = STATUS_SUCCESS; - // Delegate to another thread if (_hijackThread) - return ExecInAnyThread( pCode, size, callResult, _hijackThread ); + return ExecInAnyThread( pCode, size, _hijackThread ); - assert( _workerThread ); - assert( _hWaitEvent != NULL ); if (!_workerThread || !_hWaitEvent) - return STATUS_INVALID_PARAMETER; + THROW_AND_LOG( "no worker thread or sync event detected" ); // Write code - if (!NT_SUCCESS( status = CopyCode( pCode, size ) )) - return status; - - if (_hWaitEvent) - ResetEvent( _hWaitEvent ); + CopyCode( pCode, size ); + ResetEvent( _hWaitEvent ); // Patch KiUserApcDispatcher /*if (!_apcPatched && IsWindows7OrGreater() && !IsWindows8OrGreater()) @@ -138,10 +117,10 @@ NTSTATUS RemoteExec::ExecInWorkerThread( PVOID pCode, size_t size, uint64_t& cal if (patchBase != 0) { DWORD flOld = 0; - _memory.Protect(patchBase, 6, PAGE_EXECUTE_READWRITE, &flOld); - _memory.Write(patchBase + 0x2, (uint8_t)0x0C); - _memory.Write( patchBase + 0x4, (uint8_t)0x90 ); - _memory.Protect( patchBase, 6, flOld, nullptr ); + _memory->Protect(patchBase, 6, PAGE_EXECUTE_READWRITE, &flOld); + _memory->Write(patchBase + 0x2, (uint8_t)0x0C); + _memory->Write( patchBase + 0x4, (uint8_t)0x90 ); + _memory->Protect( patchBase, 6, flOld, nullptr ); } _apcPatched = true; @@ -154,137 +133,120 @@ NTSTATUS RemoteExec::ExecInWorkerThread( PVOID pCode, size_t size, uint64_t& cal // Execute code in thread context // TODO: Find out why am I passing pRemoteCode as an argument??? - if (NT_SUCCESS( _process.core().native()->QueueApcT( _workerThread->handle(), pRemoteCode, pRemoteCode ) )) - { - status = WaitForSingleObject( _hWaitEvent, 30 * 1000 /*wait 30s*/ ); - callResult = _userData[_currentBufferIdx].Read( RET_OFFSET, 0 ); - } - else - return LastNtStatus(); + NTSTATUS status = _process->core().native()->QueueApcT( _workerThread->handle(), pRemoteCode, pRemoteCode ); + THROW_ON_FAIL_AND_LOG( status, "failed to queue APC to worker thread" ); + WaitForSingleObject( _hWaitEvent, 20 * 1000 ); + const auto callResult = _userData[_currentBufferIdx].Read( RET_OFFSET, 0 ); SwitchActiveBuffer(); - return status; + return callResult; } /// /// Execute code in context of any existing thread /// -/// Cde to execute +/// Code to execute /// Code size. -/// Execution result /// Target thread -/// Status -NTSTATUS RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callResult, ThreadPtr& thd ) +/// Call result +uint64_t RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, ThreadPtr& thd ) { - NTSTATUS status = STATUS_SUCCESS; - _CONTEXT32 ctx32 = { 0 }; - _CONTEXT64 ctx64 = { 0 }; + _CONTEXT32 ctx32 = { }; + _CONTEXT64 ctx64 = { }; assert( _hWaitEvent != NULL ); if (_hWaitEvent == NULL) - return STATUS_NOT_FOUND; - - // Write code - if (!NT_SUCCESS( status = CopyCode( pCode, size ) )) - return status; - - if (_hWaitEvent) - ResetEvent( _hWaitEvent ); + THROW_AND_LOG( "no sync event detected" ); - if (!thd->Suspend()) - return LastNtStatus(); + // Write code + CopyCode( pCode, size ); + ResetEvent( _hWaitEvent ); - auto a = AsmFactory::GetAssembler( _process.core().isWow64() ); - if (!_process.core().isWow64()) { - const int count = 15; - static const asmjit::GpReg regs[] = - { - asmjit::host::rax, asmjit::host::rbx, asmjit::host::rcx, asmjit::host::rdx, asmjit::host::rsi, - asmjit::host::rdi, asmjit::host::r8, asmjit::host::r9, asmjit::host::r10, asmjit::host::r11, - asmjit::host::r12, asmjit::host::r13, asmjit::host::r14, asmjit::host::r15, asmjit::host::rbp - }; + NTSTATUS status = STATUS_SUCCESS; + auto suspended = SuspendedThread( thd ); - if (!NT_SUCCESS( status = thd->GetContext( ctx64, CONTEXT64_CONTROL, true ) )) + auto a = AsmFactory::GetAssembler( _process->core().isWow64() ); + if (!_process->core().isWow64()) { - thd->Resume(); - return status; + const int count = 15; + static const asmjit::GpReg regs[] = + { + asmjit::host::rax, asmjit::host::rbx, asmjit::host::rcx, asmjit::host::rdx, asmjit::host::rsi, + asmjit::host::rdi, asmjit::host::r8, asmjit::host::r9, asmjit::host::r10, asmjit::host::r11, + asmjit::host::r12, asmjit::host::r13, asmjit::host::r14, asmjit::host::r15, asmjit::host::rbp + }; + + status = thd->GetContext( ctx64, CONTEXT64_CONTROL, true ); + THROW_ON_FAIL_AND_LOG( status, "failed to get thread context" ); + + // + // Preserve thread context + // I don't care about FPU, XMM and anything else + // Stack must be aligned on 16 bytes + // + (*a)->sub( asmjit::host::rsp, count * sizeof( uint64_t ) ); + (*a)->pushf(); + + // Save registers + for (int i = 0; i < count; i++) + (*a)->mov( asmjit::Mem( asmjit::host::rsp, i * sizeof( uint64_t ) ), regs[i] ); + + a->GenCall( _userCode[_currentBufferIdx].ptr(), { _userData[_currentBufferIdx].ptr() } ); + AddReturnWithEvent( *a, mt_mod64, rt_int32, INTRET_OFFSET ); + + // Restore registers + for (int i = 0; i < count; i++) + (*a)->mov( regs[i], asmjit::Mem( asmjit::host::rsp, i * sizeof( uint64_t ) ) ); + + (*a)->popf(); + (*a)->add( asmjit::host::rsp, count * sizeof( uint64_t ) ); + + // jmp [rip] + (*a)->dw( '\xFF\x25' ); + (*a)->dd( 0 ); + (*a)->dq( ctx64.Rip ); } - - // - // Preserve thread context - // I don't care about FPU, XMM and anything else - // Stack must be aligned on 16 bytes - // - (*a)->sub( asmjit::host::rsp, count * sizeof( uint64_t ) ); - (*a)->pushf(); - - // Save registers - for (int i = 0; i < count; i++) - (*a)->mov( asmjit::Mem( asmjit::host::rsp, i * sizeof( uint64_t ) ), regs[i] ); - - a->GenCall( _userCode[_currentBufferIdx].ptr(), { _userData[_currentBufferIdx].ptr() } ); - AddReturnWithEvent( *a, mt_mod64, rt_int32, INTRET_OFFSET ); - - // Restore registers - for (int i = 0; i < count; i++) - (*a)->mov( regs[i], asmjit::Mem( asmjit::host::rsp, i * sizeof( uint64_t ) ) ); - - (*a)->popf(); - (*a)->add( asmjit::host::rsp, count * sizeof( uint64_t ) ); - - // jmp [rip] - (*a)->dw( '\xFF\x25' ); - (*a)->dd( 0 ); - (*a)->dq( ctx64.Rip ); - } - else - { - if (!NT_SUCCESS( status = thd->GetContext( ctx32, CONTEXT_CONTROL, true ) )) + else { - thd->Resume(); - return status; - } + status = thd->GetContext( ctx32, CONTEXT_CONTROL, true ); + THROW_ON_FAIL_AND_LOG( status, "failed to get thread context" ); - (*a)->pusha(); - (*a)->pushf(); + (*a)->pusha(); + (*a)->pushf(); - a->GenCall( _userCode[_currentBufferIdx].ptr(), { _userData[_currentBufferIdx].ptr() } ); - (*a)->add( asmjit::host::esp, sizeof( uint32_t ) ); - AddReturnWithEvent( *a, mt_mod32, rt_int32, INTRET_OFFSET ); + a->GenCall( _userCode[_currentBufferIdx].ptr(), { _userData[_currentBufferIdx].ptr() } ); + (*a)->add( asmjit::host::esp, sizeof( uint32_t ) ); + AddReturnWithEvent( *a, mt_mod32, rt_int32, INTRET_OFFSET ); - (*a)->popf(); - (*a)->popa(); + (*a)->popf(); + (*a)->popa(); - (*a)->push( static_cast(ctx32.Eip) ); - (*a)->ret(); - } + (*a)->push( static_cast(ctx32.Eip) ); + (*a)->ret(); + } - if (NT_SUCCESS( status = _userCode[_currentBufferIdx].Write( size, (*a)->getCodeSize(), (*a)->make() ) )) - { - if (_process.core().isWow64()) + _userCode[_currentBufferIdx].Write( size, (*a)->getCodeSize(), (*a)->make() ); + if (_process->core().isWow64()) { ctx32.Eip = static_cast(_userCode[_currentBufferIdx].ptr() + size); status = thd->SetContext( ctx32, true ); - } + } else { ctx64.Rip = _userCode[_currentBufferIdx].ptr() + size; status = thd->SetContext( ctx64, true ); } - } - thd->Resume(); - if (NT_SUCCESS( status )) - { - WaitForSingleObject( _hWaitEvent, 20 * 1000/*INFINITE*/ ); - status = _userData[_currentBufferIdx].Read( INTRET_OFFSET, callResult ); + THROW_ON_FAIL_AND_LOG( status, "failed to set thread context" ); } + WaitForSingleObject( _hWaitEvent, 30 * 1000 /*wait 30s*/ ); + auto callResult = _userData[_currentBufferIdx].Read( INTRET_OFFSET, 0 ); SwitchActiveBuffer(); - return status; + return callResult; } @@ -296,12 +258,9 @@ NTSTATUS RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callRe /// Thread exit code DWORD RemoteExec::ExecDirect( ptr_t pCode, ptr_t arg ) { - auto thread = _threads.CreateNew( pCode, arg/*, HideFromDebug*/ ); - if (!thread) - return thread.status; - - (*thread)->Join(); - return (*thread)->ExitCode(); + auto thread = _threads->CreateNew( pCode, arg ); + thread->Join(); + return thread->ExitCode(); } /// @@ -314,81 +273,56 @@ DWORD RemoteExec::ExecDirect( ptr_t pCode, ptr_t arg ) /// | 8/8 bytes | 8/8 bytes | 8/8 bytes | 8/8 bytes | | /// -------------------------------------------------------------------------------------------------------------------------- /// -/// Worket thread mode +/// Worker thread mode /// Create sync event for worker thread -/// Status -NTSTATUS RemoteExec::CreateRPCEnvironment( WorkerThreadMode mode /*= Worker_None*/, bool bEvent /*= false*/ ) +void RemoteExec::CreateRPCEnvironment( WorkerThreadMode mode /*= Worker_None*/, bool bEvent /*= false*/ ) { DWORD thdID = GetTickCount(); // randomize thread id - NTSTATUS status = STATUS_SUCCESS; - - auto allocMem = [this]( auto& result, uint32_t size = 0x1000, DWORD prot = PAGE_EXECUTE_READWRITE ) -> NTSTATUS - { - if (!result.valid()) - { - auto mem = _memory.Allocate( size, prot ); - if (!mem) - return mem.status; - - result = std::move( *mem ); - } - - return STATUS_SUCCESS; - }; // // Allocate environment codecave // - if (!NT_SUCCESS( status = allocMem( _workerCode ) )) - return status; - if (!NT_SUCCESS( status = allocMem( _userCode[0] ) )) - return status; - if (!NT_SUCCESS( status = allocMem( _userCode[1] ) )) - return status; - if (!NT_SUCCESS( status = allocMem( _userData[0], 0x4000, PAGE_READWRITE ) )) - return status; - if (!NT_SUCCESS( status = allocMem( _userData[1], 0x4000, PAGE_READWRITE ) )) - return status; + if(!_workerCode) + _workerCode = _memory->Allocate( 0x1000 ); + if(!_userCode[0]) + _userCode[0] = _memory->Allocate( 0x1000 ); + if(!_userCode[1]) + _userCode[1] = _memory->Allocate( 0x1000 ); + if(!_userData[0]) + _userData[0] = _memory->Allocate( 0x4000, PAGE_READWRITE ); + if(!_userData[1]) + _userData[1] = _memory->Allocate( 0x4000, PAGE_READWRITE ); // Create RPC thread if (mode == Worker_CreateNew) { - auto thd = CreateWorkerThread(); - if (!thd) - return thd.status; - - thdID = thd.result(); + thdID = CreateWorkerThread(); } // Get thread to hijack else if (mode == Worker_UseExisting) { - _hijackThread = _process.threads().getMostExecuted(); - if (!_hijackThread) - return STATUS_INVALID_THREAD; - + _hijackThread = _process->threads().getMostExecuted(); thdID = _hijackThread->id(); } // Create RPC sync event if (bEvent) - status = CreateAPCEvent( thdID ); - - return status; + CreateAPCEvent( thdID ); } /// /// Create worker RPC thread /// /// Thread ID -call_result_t RemoteExec::CreateWorkerThread() +DWORD RemoteExec::CreateWorkerThread() { - auto a = AsmFactory::GetAssembler( _process.core().isWow64() ); + auto a = AsmFactory::GetAssembler( _process->core().isWow64() ); asmjit::Label l_loop = (*a)->newLabel(); // // Create execution thread // - if(!_workerThread || !_workerThread->valid()) + if (!_workerThread || !_workerThread->valid()) { /*if (_proc.barrier().type == wow_64_32) { @@ -398,7 +332,7 @@ call_result_t RemoteExec::CreateWorkerThread() (*a)->and_( (*a)->zsp, -16 ); // Allocate new x64 activation stack - auto createActStack = _mods.GetNtdllExport( "RtlAllocateActivationContextStack", mt_mod64 ); + auto createActStack = _mods->GetNtdllExport( "RtlAllocateActivationContextStack", mt_mod64 ); if(createActStack) { a->GenCall( createActStack->procAddress, { _userData.ptr() + 0x3000 } ); @@ -410,36 +344,29 @@ call_result_t RemoteExec::CreateWorkerThread() } }*/ - auto ntdll = _mods.GetModule( L"ntdll.dll", Sections ); - auto proc = _mods.GetExport( ntdll, "NtDelayExecution" ); - auto pExitThread = _mods.GetExport( ntdll, "NtTerminateThread" ); - if (!proc || !pExitThread) - return proc.status ? proc.status : pExitThread.status; + auto pNtDelayExecution = _mods->GetNtdllExport( "NtDelayExecution" ); + auto pExitThread = _mods->GetNtdllExport( "NtTerminateThread" ); /* for(;;) - SleepEx(5, TRUE); + NtDelayExecution(TRUE, 5ms); - ExitThread(SetEvent(m_hWaitEvent)); + NtTerminateThread(); */ (*a)->bind( l_loop ); - a->GenCall( proc->procAddress, { TRUE, _workerCode.ptr() } ); + a->GenCall( pNtDelayExecution.procAddress, { TRUE, _workerCode.ptr() } ); (*a)->jmp( l_loop ); - a->ExitThreadWithStatus( pExitThread->procAddress, _userData[0].ptr() ); + a->ExitThreadWithStatus( pExitThread.procAddress, _userData[0].ptr() ); // Write code into process - LARGE_INTEGER liDelay = { { 0 } }; - liDelay.QuadPart = -10 * 1000 * 5; + LARGE_INTEGER liDelay = { }; + liDelay.QuadPart = -10 * 1000 * 5; // 5ms _workerCode.Write( 0, liDelay ); - _workerCode.Write( sizeof(LARGE_INTEGER), (*a)->getCodeSize(), (*a)->make() ); - - auto thd = _threads.CreateNew( _workerCode.ptr() + sizeof( LARGE_INTEGER ), _userData[0].ptr()/*, HideFromDebug*/ ); - if (!thd) - return thd.status; + _workerCode.Write( sizeof( liDelay ), (*a)->getCodeSize(), (*a)->make() ); - _workerThread = std::move( thd.result() ); + _workerThread = _threads->CreateNew( _workerCode.ptr() + sizeof( liDelay ), _userData[_currentBufferIdx].ptr() ); } return _workerThread->id(); @@ -450,13 +377,12 @@ call_result_t RemoteExec::CreateWorkerThread() /// Create event to synchronize APC procedures /// /// The thread identifier. -/// Status code -NTSTATUS RemoteExec::CreateAPCEvent( DWORD threadID ) -{ +void RemoteExec::CreateAPCEvent( DWORD threadID ) +{ if (_hWaitEvent) - return STATUS_SUCCESS; + return; - auto a = AsmFactory::GetAssembler( _process.core().isWow64() ); + auto a = AsmFactory::GetAssembler( _process->core().isWow64() ); wchar_t pEventName[128] = { }; size_t len = sizeof( pEventName ); @@ -472,30 +398,24 @@ NTSTATUS RemoteExec::CreateAPCEvent( DWORD threadID ) auto guard = std::unique_ptr( pDescriptor, &LocalFree ); // Prepare Arguments - ustr.Length = static_cast(wcslen( pEventName ) * sizeof(wchar_t)); + ustr.Length = static_cast(wcslen( pEventName ) * sizeof( wchar_t )); ustr.MaximumLength = static_cast(len); ustr.Buffer = pEventName; obAttr.ObjectName = &ustr; - obAttr.Length = sizeof(obAttr); + obAttr.Length = sizeof( obAttr ); obAttr.SecurityDescriptor = pDescriptor; - auto pOpenEvent = _mods.GetNtdllExport( "NtOpenEvent", mt_default, Sections ); - if (!pOpenEvent) - return pOpenEvent.status; - + auto pOpenEvent = _mods->GetNtdllExport( "NtOpenEvent", mt_default, Sections ); auto status = SAFE_NATIVE_CALL( NtCreateEvent, &_hWaitEvent, EVENT_ALL_ACCESS, &obAttr, 0, static_cast(FALSE) ); - if (!NT_SUCCESS( status )) - return status; + THROW_ON_FAIL_AND_LOG( status, "NtCreateEvent failed" ); HANDLE hRemoteHandle = nullptr; - if (!DuplicateHandle( GetCurrentProcess(), _hWaitEvent, _process.core().handle(), &hRemoteHandle, 0, FALSE, DUPLICATE_SAME_ACCESS )) - return LastNtStatus(); + if (!DuplicateHandle( GetCurrentProcess(), _hWaitEvent, _process->core().handle(), &hRemoteHandle, 0, FALSE, DUPLICATE_SAME_ACCESS )) + THROW_WITH_STATUS_AND_LOG( LastNtStatus(), "DuplicateHandle failed" ); - status = _userData[0].Write( EVENT_OFFSET, sizeof( uintptr_t ), &hRemoteHandle ); - if (!NT_SUCCESS( status )) - return status; - return _userData[1].Write( EVENT_OFFSET, sizeof( uintptr_t ), &hRemoteHandle ); + _userData[0].Write( EVENT_OFFSET, sizeof( uintptr_t ), &hRemoteHandle ); + _userData[1].Write( EVENT_OFFSET, sizeof( uintptr_t ), &hRemoteHandle ); } /// @@ -506,20 +426,19 @@ NTSTATUS RemoteExec::CreateAPCEvent( DWORD threadID ) /// Function arguments /// Calling convention /// Return type -/// Status code -NTSTATUS RemoteExec::PrepareCallAssembly( - IAsmHelper& a, +void RemoteExec::PrepareCallAssembly( + IAsmHelper& a, ptr_t pfn, std::vector& args, eCalligConvention cc, eReturnType retType - ) +) { uintptr_t data_offset = ARGS_OFFSET; // Invalid calling convention if (cc < cc_cdecl || cc > cc_fastcall) - return STATUS_INVALID_PARAMETER_3; + THROW_AND_LOG( "invalid calling convention: %d", cc ); // Copy structures and strings for (auto& arg : args) @@ -533,9 +452,11 @@ NTSTATUS RemoteExec::PrepareCallAssembly( arg.imm_val64 = reinterpret_cast(arg.buf.data()); } - if (arg.type == AsmVariant::dataStruct || arg.type == AsmVariant::dataPtr) + if (arg.type == AsmVariant::dataStruct || arg.type == AsmVariant::dataPtr || arg.type == AsmVariant::dataPtrConst) { - _userData[_currentBufferIdx].Write( data_offset, arg.size, reinterpret_cast(arg.imm_val) ); + if (arg.imm_val != 0) + _userData[_currentBufferIdx].Write( data_offset, arg.size, reinterpret_cast(arg.imm_val) ); + arg.new_imm_val = _userData[_currentBufferIdx].ptr() + data_offset; // Add some padding after data @@ -551,7 +472,7 @@ NTSTATUS RemoteExec::PrepareCallAssembly( args.front().new_imm_val = args.front().imm_val; args.front().type = AsmVariant::structRet; } - + a.GenPrologue(); a.GenCall( pfn, args, cc ); @@ -572,8 +493,6 @@ NTSTATUS RemoteExec::PrepareCallAssembly( AddReturnWithEvent( a, mt_default, retType ); a.GenEpilogue(); - - return STATUS_SUCCESS; } /// @@ -581,27 +500,16 @@ NTSTATUS RemoteExec::PrepareCallAssembly( /// /// Code to copy /// Code size -/// Status -NTSTATUS RemoteExec::CopyCode( PVOID pCode, size_t size ) +void RemoteExec::CopyCode( PVOID pCode, size_t size ) { - if (!_userCode[_currentBufferIdx].valid()) - { - auto mem = _memory.Allocate( size ); - if (!mem) - return mem.status; - - _userCode[_currentBufferIdx] = std::move( mem.result() ); - } + if (!_userCode[_currentBufferIdx]) + _userCode[_currentBufferIdx] = _memory->Allocate( size ); // Reallocate for larger code if (size > _userCode[_currentBufferIdx].size()) - { - auto res = _userCode[_currentBufferIdx].Realloc( size ); - if (!res) - return res.status; - } + _userCode[_currentBufferIdx].Realloc( size ); - return _userCode[_currentBufferIdx].Write( 0, size, pCode ); + _userCode[_currentBufferIdx].Write( 0, size, pCode ); } /// @@ -615,23 +523,16 @@ void RemoteExec::AddReturnWithEvent( IAsmHelper& a, eModType mt /*= mt_default*/, eReturnType retType /*= rt_int32 */, - uint32_t retOffset /*= RET_OFFSET*/ - ) + uint32_t retOffset /*= RET_OFFSET*/ +) { // Allocate block if missing - if (!_userData[_currentBufferIdx].valid()) - { - auto mem = _memory.Allocate( 0x4000, PAGE_READWRITE ); - if (!mem) - return; - - _userData[_currentBufferIdx] = std::move( mem.result() ); - } + if (!_userData[_currentBufferIdx]) + _userData[_currentBufferIdx] = _memory->Allocate( 0x4000, PAGE_READWRITE ); ptr_t ptr = _userData[_currentBufferIdx].ptr(); - auto pSetEvent = _process.modules().GetNtdllExport( "NtSetEvent", mt ); - if(pSetEvent) - a.SaveRetValAndSignalEvent( pSetEvent->procAddress, ptr + retOffset, ptr + EVENT_OFFSET, ptr + ERR_OFFSET, retType ); + auto pSetEvent = _process->modules().GetNtdllExport( "NtSetEvent", mt ); + a.SaveRetValAndSignalEvent( pSetEvent.procAddress, ptr + retOffset, ptr + EVENT_OFFSET, ptr + ERR_OFFSET, retType ); } /// @@ -640,36 +541,44 @@ void RemoteExec::AddReturnWithEvent( void RemoteExec::TerminateWorker() { // Close remote event handle - ptr_t hRemoteEvent = 0; - _userData[0].Read( EVENT_OFFSET, hRemoteEvent ); - if (hRemoteEvent) + if (_userData[0]) { - HANDLE hLocal = nullptr; - DuplicateHandle( - _process.core().handle(), - reinterpret_cast(hRemoteEvent), - GetCurrentProcess(), - &hLocal, - 0, false, - DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS - ); - - if (hLocal) - CloseHandle( hLocal ); - - _userData[0].Write( EVENT_OFFSET, 0ll ); - _userData[1].Write( EVENT_OFFSET, 0ll ); + try { + ptr_t hRemoteEvent = 0; + _userData[0].Read( EVENT_OFFSET, sizeof( ptr_t ), &hRemoteEvent ); + + if (hRemoteEvent) + { + HANDLE hLocal = nullptr; + DuplicateHandle( + _process->core().handle(), + reinterpret_cast(hRemoteEvent), + GetCurrentProcess(), + &hLocal, + 0, false, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS + ); + + if (hLocal) + CloseHandle( hLocal ); + + _userData[0].Write( EVENT_OFFSET, 0ll ); + _userData[1].Write( EVENT_OFFSET, 0ll ); + } + } catch (const std::exception&) + { + } } // Close event - if(_hWaitEvent) + if (_hWaitEvent) { CloseHandle( _hWaitEvent ); _hWaitEvent = NULL; } // Stop thread - if(_workerThread && _workerThread->valid()) + if (_workerThread && _workerThread->valid()) { _workerThread->Terminate(); _workerThread->Join(); @@ -686,11 +595,11 @@ void RemoteExec::reset() { TerminateWorker(); - _userCode[0].Reset(); - _userCode[1].Reset(); - _userData[0].Reset(); - _userData[1].Reset(); - _workerCode.Reset(); + _userCode[0].reset(); + _userCode[1].reset(); + _userData[0].reset(); + _userData[1].reset(); + _workerCode.reset(); _apcPatched = false; } diff --git a/src/BlackBone/Process/RPC/RemoteExec.h b/src/BlackBone/Process/RPC/RemoteExec.h index 1c24bee9..d3d9a0fc 100644 --- a/src/BlackBone/Process/RPC/RemoteExec.h +++ b/src/BlackBone/Process/RPC/RemoteExec.h @@ -28,9 +28,15 @@ class RemoteExec using vecArgs = std::vector; public: - BLACKBONE_API RemoteExec( class Process& proc ); + BLACKBONE_API RemoteExec( class Process* proc ); BLACKBONE_API ~RemoteExec(); + RemoteExec( const RemoteExec& ) = delete; + RemoteExec( RemoteExec&& ) = default; + + RemoteExec& operator =( const RemoteExec& ) = delete; + RemoteExec& operator =( RemoteExec&& ) = default; + /// /// Create environment for future remote procedure calls /// @@ -41,43 +47,35 @@ class RemoteExec /// | 8/8 bytes | 8/8 bytes | 8/8 bytes | 8/8 bytes | | /// -------------------------------------------------------------------------------------------------------------------------- /// - /// Worket thread mode + /// Worker thread mode /// Create sync event for worker thread - /// Status - BLACKBONE_API NTSTATUS CreateRPCEnvironment( WorkerThreadMode mode = Worker_None, bool bEvent = false ); + BLACKBONE_API void CreateRPCEnvironment( WorkerThreadMode mode = Worker_None, bool bEvent = false ); /// /// Create new thread and execute code in it. Wait until execution ends /// /// Code to execute /// Code size - /// Code return value /// Switch wow64 thread to long mode upon creation - /// Status - BLACKBONE_API NTSTATUS ExecInNewThread( - PVOID pCode, size_t size, - uint64_t& callResult, - eThreadModeSwitch modeSwitch = AutoSwitch - ); + /// Return code + BLACKBONE_API uint64_t ExecInNewThread( PVOID pCode, size_t size, eThreadModeSwitch modeSwitch = AutoSwitch ); /// /// Execute code in context of our worker thread /// - /// Cde to execute + /// Code to execute /// Code size. - /// Execution result - /// Status - BLACKBONE_API NTSTATUS ExecInWorkerThread( PVOID pCode, size_t size, uint64_t& callResult ); + /// Call result + BLACKBONE_API uint64_t ExecInWorkerThread( PVOID pCode, size_t size ); /// /// Execute code in context of any existing thread /// - /// Cde to execute + /// Code to execute /// Code size. - /// Execution result /// Target thread - /// Status - BLACKBONE_API NTSTATUS ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callResult, ThreadPtr& thread ); + /// Call result + BLACKBONE_API uint64_t ExecInAnyThread( PVOID pCode, size_t size, ThreadPtr& thread ); /// /// Create new thread with specified entry point and argument @@ -95,8 +93,7 @@ class RemoteExec /// Function arguments /// Calling convention /// Return type - /// Status code - BLACKBONE_API NTSTATUS PrepareCallAssembly( + BLACKBONE_API void PrepareCallAssembly( IAsmHelper& a, ptr_t pfn, std::vector& args, @@ -132,21 +129,20 @@ class RemoteExec /// Retrieve call result /// /// Retrieved result - /// true on success template - NTSTATUS GetCallResult( T& result ) + T GetCallResult() { // This method is called after an RPC call, so the ping pong buffers have already been switched, so // we want to access the OTHER buffer here. if constexpr (sizeof( T ) > sizeof( uint64_t )) { if constexpr (std::is_reference_v) - return _userData[1 - _currentBufferIdx].Read( _userData[1 - _currentBufferIdx].Read( RET_OFFSET, 0 ), sizeof( T ), reinterpret_cast(&result) ); + return _userData[1 - _currentBufferIdx].Read( _userData[1 - _currentBufferIdx].Read( RET_OFFSET ) ); else - return _userData[1 - _currentBufferIdx].Read( ARGS_OFFSET, sizeof( T ), reinterpret_cast(&result) ); + return _userData[1 - _currentBufferIdx].Read( ARGS_OFFSET ); } else - return _userData[1 - _currentBufferIdx].Read( RET_OFFSET, sizeof( T ), reinterpret_cast(&result) ); + return _userData[1 - _currentBufferIdx].Read( RET_OFFSET ); } /// @@ -155,7 +151,7 @@ class RemoteExec /// BLACKBONE_API NTSTATUS GetLastStatus() { - return _userData[_currentBufferIdx].Read( ERR_OFFSET, STATUS_NOT_FOUND ); + return _userData[1 - _currentBufferIdx].Read( ERR_OFFSET, STATUS_NOT_FOUND ); } /// @@ -179,7 +175,7 @@ class RemoteExec /// Ge memory routines /// /// - BLACKBONE_API class ProcessMemory& memory() { return _memory; } + //BLACKBONE_API class ProcessMemory& memory() { return *_memory; } /// /// Reset instance @@ -187,27 +183,24 @@ class RemoteExec BLACKBONE_API void reset(); private: - /// /// Create worker RPC thread /// /// Thread ID - call_result_t CreateWorkerThread(); + DWORD CreateWorkerThread(); /// /// Create event to synchronize APC procedures /// /// The thread identifier. - /// Status code - NTSTATUS CreateAPCEvent( DWORD threadID ); + void CreateAPCEvent( DWORD threadID ); /// /// Copy executable code into remote codecave for future execution /// /// Code to copy /// Code size - /// Status - NTSTATUS CopyCode( PVOID pCode, size_t size ); + void CopyCode( PVOID pCode, size_t size ); void SwitchActiveBuffer() { @@ -223,15 +216,12 @@ class RemoteExec _currentBufferIdx = 1 - _currentBufferIdx; } - RemoteExec( const RemoteExec& ) = delete; - RemoteExec& operator =(const RemoteExec&) = delete; - -private: +private: // Process routines - class Process& _process; - class ProcessModules& _mods; - class ProcessMemory& _memory; - class ProcessThreads& _threads; + class Process* _process; + class ProcessModules* _mods; + class ProcessMemory* _memory; + class ProcessThreads* _threads; ThreadPtr _workerThread; // Worker thread handle ThreadPtr _hijackThread; // Thread to use for hijacking diff --git a/src/BlackBone/Process/RPC/RemoteFunction.hpp b/src/BlackBone/Process/RPC/RemoteFunction.hpp index 3c092c0a..7f9ea515 100644 --- a/src/BlackBone/Process/RPC/RemoteFunction.hpp +++ b/src/BlackBone/Process/RPC/RemoteFunction.hpp @@ -1,6 +1,5 @@ #pragma once -#include "../../Include/CallResult.h" #include "../../Asm/IAsmHelper.h" #include "../Process.h" @@ -63,20 +62,15 @@ class RemoteFunctionBase ); } - call_result_t Call( CallArguments& args, ThreadPtr contextThread = nullptr ) + ReturnType Call( CallArguments& args, ThreadPtr contextThread = nullptr ) { - ReturnType result = {}; - uint64_t tmpResult = 0; - NTSTATUS status = STATUS_SUCCESS; auto a = AsmFactory::GetAssembler( _process.core().isWow64() ); if (!contextThread) contextThread = _boundThread; // Ensure RPC environment exists - status = _process.remote().CreateRPCEnvironment( Worker_None, contextThread != nullptr ); - if (!NT_SUCCESS( status )) - return call_result_t( result, status ); + _process.remote().CreateRPCEnvironment( Worker_None, contextThread != nullptr ); // FPU check constexpr bool isFloat = std::is_same_v; @@ -98,60 +92,53 @@ class RemoteFunctionBase // Choose execution thread if (!contextThread) - { - status = _process.remote().ExecInNewThread( (*a)->make(), (*a)->getCodeSize(), tmpResult ); - } + _process.remote().ExecInNewThread( (*a)->make(), (*a)->getCodeSize() ); else if (contextThread == _process.remote().getWorker()) - { - status = _process.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), tmpResult ); - } + _process.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize() ); else - { - status = _process.remote().ExecInAnyThread( (*a)->make(), (*a)->getCodeSize(), tmpResult, contextThread ); - } + _process.remote().ExecInAnyThread( (*a)->make(), (*a)->getCodeSize(), contextThread ); // Get function return value - if (!NT_SUCCESS( status ) || !NT_SUCCESS( status = _process.remote().GetCallResult( result ) )) - return call_result_t( result, status ); + auto result = _process.remote().GetCallResult(); // Update arguments - for (auto& arg : args.arguments) - if (arg.type == AsmVariant::dataPtr) + for (const auto& arg : args.arguments) + if (arg.type == AsmVariant::dataPtr && arg.imm_val != 0) _process.memory().Read( arg.new_imm_val, arg.size, reinterpret_cast(arg.imm_val) ); - return call_result_t( result, STATUS_SUCCESS ); + return result; } - call_result_t Call( const Args&... args ) + ReturnType Call( const Args&... args ) { CallArguments a( args... ); return Call( a, nullptr ); } - call_result_t Call( const std::tuple& args, ThreadPtr contextThread = nullptr ) + ReturnType Call( const std::tuple& args, ThreadPtr contextThread = nullptr ) { CallArguments a( args, std::index_sequence_for() ); return Call( a, contextThread ); } - call_result_t Call( const std::initializer_list& args, ThreadPtr contextThread = nullptr ) + ReturnType Call( const std::initializer_list& args, ThreadPtr contextThread = nullptr ) { CallArguments a( args ); return Call( a, contextThread ); } - call_result_t operator()( const Args&... args ) + ReturnType operator()( const Args&... args ) { CallArguments a( args... ); return Call( a ); } - auto MakeArguments( const Args&... args ) + auto MakeArguments( const Args&... args ) { return CallArguments( args... ); } - auto MakeArguments( const std::initializer_list& args ) + auto MakeArguments( const std::initializer_list& args ) { return CallArguments( args ); } @@ -233,6 +220,7 @@ class RemoteFunction : public RemoteFunctionBase /// Get remote function object /// +/// Target process for execution /// Function address in the remote process /// Function object template @@ -244,6 +232,7 @@ RemoteFunction MakeRemoteFunction( Process& process, ptr_t ptr, ThreadPtr bou /// /// Get remote function object /// +/// Target process for execution /// Function address in the remote process /// Function object template @@ -255,6 +244,7 @@ RemoteFunction MakeRemoteFunction( Process& process, T ptr, ThreadPtr boundTh /// /// Get remote function object /// +/// Target process for execution /// Remote module name /// Function name or ordinal /// Function object @@ -262,7 +252,7 @@ template RemoteFunction MakeRemoteFunction( Process& process, const std::wstring& modName, const char* name_ord, ThreadPtr boundThread = nullptr ) { auto ptr = process.modules().GetExport( modName, name_ord ); - return RemoteFunction( process, ptr ? ptr->procAddress : 0, boundThread ); + return RemoteFunction( process, ptr.procAddress, boundThread ); } } diff --git a/src/BlackBone/Process/RPC/RemoteHook.cpp b/src/BlackBone/Process/RPC/RemoteHook.cpp index 46dbf1ff..c12a539f 100644 --- a/src/BlackBone/Process/RPC/RemoteHook.cpp +++ b/src/BlackBone/Process/RPC/RemoteHook.cpp @@ -10,9 +10,9 @@ namespace blackbone // CPU will raise #GP if length exceeds this value #define MAX_INSTRUCTION_LENGTH 15 -RemoteHook::RemoteHook( class ProcessMemory& memory ) +RemoteHook::RemoteHook( class ProcessMemory* memory ) : _memory( memory ) - , _core( _memory.core() ) + , _core( _memory->core() ) { } @@ -28,12 +28,12 @@ RemoteHook::~RemoteHook() NTSTATUS RemoteHook::EnsureDebug() { // Close previous debug object - if (_debugPID != 0 && _debugPID != _core.pid()) + if (_debugPID != 0 && _debugPID != _core->pid()) { EndDebug(); } // Already debugging - else if (_debugPID == _core.pid()) + else if (_debugPID == _core->pid()) { return STATUS_SUCCESS; } @@ -44,11 +44,11 @@ NTSTATUS RemoteHook::EnsureDebug() BOOL isDebugged = FALSE; // Can't debug x64 process from x32 one. - if (_core.native()->GetWow64Barrier().type == wow_32_64) + if (_core->native()->GetWow64Barrier().type == wow_32_64) return STATUS_OBJECT_TYPE_MISMATCH; // Already under debugger - CheckRemoteDebuggerPresent( _core.handle(), &isDebugged ); + CheckRemoteDebuggerPresent( _core->handle(), &isDebugged ); if (isDebugged == TRUE) return STATUS_ALREADY_REGISTERED; @@ -100,10 +100,10 @@ NTSTATUS RemoteHook::ApplyP( eHookType type, uint64_t ptr, fnCallback newFn, con GetExitCodeThread( _hEventThd, &exitCode ); } - HookData data = { { 0 } }; + HookData data = { }; // Store old byte - data.oldByte = _memory.Read( ptr ).result(); + data.oldByte = _memory->Read( ptr ); data.type = type; data.onExecute.freeFn = newFn; data.onExecute.classFn.classPtr = pClass; @@ -116,27 +116,37 @@ NTSTATUS RemoteHook::ApplyP( eHookType type, uint64_t ptr, fnCallback newFn, con // Set for single thread if (pThread != nullptr) { - data.hwbp_idx = pThread->AddHWBP( ptr, hwbp_execute, hwbp_1 ).result( -1 ); - if (data.hwbp_idx == -1) + try + { + data.hwbp_idx = pThread->AddHWBP( ptr, hwbp_execute, hwbp_1 ); + } + catch (const nt_exception&) + { return STATUS_NO_MORE_ENTRIES; + } } // Set for all threads else { - for (auto& thread : _memory.process()->threads().getAll()) - thread->AddHWBP( ptr, hwbp_execute, hwbp_1 ); + for (auto& thread : _memory->process()->threads().getAll()) + { + try + { + thread->AddHWBP( ptr, hwbp_execute, hwbp_1 ); + } + catch (const nt_exception&) + { + } + } } } // Write int3 else { DWORD flOld = 0; - _memory.Protect( ptr, sizeof( uint8_t ), PAGE_EXECUTE_READWRITE, &flOld ); - status = _memory.Write( ptr, 0xCC ); - _memory.Protect( ptr, sizeof( uint8_t ), flOld, nullptr ); - - if (!NT_SUCCESS( status )) - return status; + _memory->Protect( ptr, sizeof( uint8_t ), PAGE_EXECUTE_READWRITE, &flOld ); + _memory->Write( ptr, 0xCC ); + _memory->Protect( ptr, sizeof( uint8_t ), flOld, nullptr ); } // Store hooked address @@ -200,11 +210,11 @@ void RemoteHook::Restore( const HookData &hook, uint64_t ptr ) { if (hook.threadID != 0) { - Thread( hook.threadID, &_core ).RemoveHWBP( hook.hwbp_idx ); + Thread( hook.threadID, _core ).RemoveHWBP( hook.hwbp_idx ); } else { - auto threads = _memory.process()->threads().getAll(); + auto threads = _memory->process()->threads().getAll(); for (auto& thread : threads) thread->RemoveHWBP( ptr ); } @@ -213,9 +223,9 @@ void RemoteHook::Restore( const HookData &hook, uint64_t ptr ) else { DWORD flOld = 0; - _memory.Protect( ptr, sizeof( uint8_t ), PAGE_EXECUTE_READWRITE, &flOld ); - _memory.Write( ptr, hook.oldByte ); - _memory.Protect( ptr, sizeof( uint8_t ), flOld, nullptr ); + _memory->Protect( ptr, sizeof( uint8_t ), PAGE_EXECUTE_READWRITE, &flOld ); + _memory->Write( ptr, hook.oldByte ); + _memory->Protect( ptr, sizeof( uint8_t ), flOld, nullptr ); } } @@ -239,30 +249,30 @@ DWORD RemoteHook::EventThreadWrap( LPVOID lpParam ) DWORD RemoteHook::EventThread() { // Try to debug process - if (DebugActiveProcess( _core.pid() ) != TRUE) + if (DebugActiveProcess( _core->pid() ) != TRUE) return LastNtStatus(); DebugSetProcessKillOnExit( FALSE ); - _debugPID = _core.pid(); + _debugPID = _core->pid(); // Determine if target is Wow64 - _x64Target = !_core.native()->GetWow64Barrier().targetWow64; + _x64Target = !_core->native()->GetWow64Barrier().targetWow64; _wordSize = _x64Target ? sizeof( int64_t ) : sizeof( int32_t ); // // Reset debug flag in PEB // - _memory.Write( fieldPtr( _core.peb64(), &_PEB64::BeingDebugged ), uint8_t( 0 ) ); + _memory->Write( fieldPtr( _core->peb64(), &_PEB64::BeingDebugged ), uint8_t( 0 ) ); if (!_x64Target) - _memory.Write( fieldPtr( _core.peb32(), &_PEB32::BeingDebugged ), uint8_t( 0 ) ); + _memory->Write( fieldPtr( _core->peb32(), &_PEB32::BeingDebugged ), uint8_t( 0 ) ); _active = true; // Process debug events while (_active) { - DEBUG_EVENT DebugEv = { 0 }; + DEBUG_EVENT DebugEv = { }; DWORD status = DBG_CONTINUE; if (!WaitForDebugEvent( &DebugEv, 100 )) @@ -280,7 +290,7 @@ DWORD RemoteHook::EventThread() for(auto& hook : _hooks) if (hook.second.type == hwbp && hook.second.threadID == 0) { - Thread th( DebugEv.u.CreateThread.hThread, &_core ); + Thread th( DebugEv.u.CreateThread.hThread, _core ); th.AddHWBP( hook.first, hwbp_execute, hwbp_1 ); } break; @@ -339,7 +349,7 @@ DWORD RemoteHook::OnBreakpoint( const DEBUG_EVENT& DebugEv ) // Prevent highest bit extension. ptr_t addr = (uintptr_t)DebugEv.u.Exception.ExceptionRecord.ExceptionAddress; ptr_t ip = 0, sp = 0; - Thread thd( DebugEv.dwThreadId, &_core ); + Thread thd( DebugEv.dwThreadId, _core ); if (_hooks.count( addr )) { @@ -347,7 +357,7 @@ DWORD RemoteHook::OnBreakpoint( const DEBUG_EVENT& DebugEv ) _CONTEXT32 ctx32; thd.GetContext( ctx64, CONTEXT64_FULL, true ); - if (_memory.core().isWow64()) + if (_core->isWow64()) { thd.GetContext( ctx32, WOW64_CONTEXT_FULL, true ); ip = ctx32.Eip; @@ -363,7 +373,7 @@ DWORD RemoteHook::OnBreakpoint( const DEBUG_EVENT& DebugEv ) std::vector> results; StackBacktrace( ip, sp, thd, results, 1 ); - RemoteContext context( _memory, thd, ctx64, !results.empty() ? results.back().first : 0, _x64Target, _wordSize ); + RemoteContext context( _memory, &thd, &ctx64, !results.empty() ? results.back().first : 0, _x64Target, _wordSize ); // Execute user callback auto& hook = _hooks[addr]; @@ -383,9 +393,9 @@ DWORD RemoteHook::OnBreakpoint( const DEBUG_EVENT& DebugEv ) // Resume execution DWORD flOld = 0; - _memory.Protect( addr, sizeof( hook.oldByte ), PAGE_EXECUTE_READWRITE, &flOld ); - _memory.Write( addr, hook.oldByte ); - _memory.Protect( addr, sizeof( hook.oldByte ), flOld, nullptr ); + _memory->Protect( addr, sizeof( hook.oldByte ), PAGE_EXECUTE_READWRITE, &flOld ); + _memory->Write( addr, hook.oldByte ); + _memory->Protect( addr, sizeof( hook.oldByte ), flOld, nullptr ); _repatch[addr] = true; @@ -410,18 +420,18 @@ DWORD RemoteHook::OnSinglestep( const DEBUG_EVENT& DebugEv ) // Prevent highest bit extension. ptr_t addr = (uintptr_t)DebugEv.u.Exception.ExceptionRecord.ExceptionAddress; ptr_t ip = 0, sp = 0; - bool use64 = !_core.native()->GetWow64Barrier().x86OS; - _CONTEXT32 ctx32 = { 0 }; - _CONTEXT64 ctx64 = { 0 }; + bool use64 = !_core->native()->GetWow64Barrier().x86OS; + _CONTEXT32 ctx32 = { }; + _CONTEXT64 ctx64 = { }; - Thread thd( DebugEv.dwThreadId, &_core ); + Thread thd( DebugEv.dwThreadId, _core ); thd.GetContext( ctx64, CONTEXT64_ALL, true ); ip = ctx64.Rip; sp = ctx64.Rsp; // Use 32 bit context if available - if (_memory.core().isWow64()) + if (_core->isWow64()) { thd.GetContext( ctx32, use64 ? WOW64_CONTEXT_ALL : CONTEXT_ALL, true ); ip = ctx32.Eip; @@ -448,7 +458,7 @@ DWORD RemoteHook::OnSinglestep( const DEBUG_EVENT& DebugEv ) std::vector> results; StackBacktrace( ip, sp, thd, results, 1 ); - RemoteContext context( _memory, thd, ctx64, !results.empty() ? results.back().first : 0, _x64Target, _wordSize ); + RemoteContext context( _memory, &thd, &ctx64, !results.empty() ? results.back().first : 0, _x64Target, _wordSize ); // Execute user callback if(_hooks.count( addr )) @@ -507,9 +517,9 @@ DWORD RemoteHook::OnSinglestep( const DEBUG_EVENT& DebugEv ) else if (hook.type == int3) { DWORD flOld = 0; - _memory.Protect( addr, sizeof( hook.oldByte ), PAGE_EXECUTE_READWRITE, &flOld ); - _memory.Write( place.first, uint8_t( 0xCC ) ); - _memory.Protect( addr, sizeof( hook.oldByte ), flOld, nullptr ); + _memory->Protect( addr, sizeof( hook.oldByte ), PAGE_EXECUTE_READWRITE, &flOld ); + _memory->Write( place.first, uint8_t( 0xCC ) ); + _memory->Protect( addr, sizeof( hook.oldByte ), flOld, nullptr ); } place.second = false; @@ -533,14 +543,14 @@ DWORD RemoteHook::OnAccessViolation( const DEBUG_EVENT& DebugEv ) _CONTEXT32 ctx32; _CONTEXT64 ctx64; - Thread thd( DebugEv.dwThreadId, &_core ); + Thread thd( DebugEv.dwThreadId, _core ); thd.GetContext( ctx64, CONTEXT64_ALL, true ); ip = ctx64.Rip; sp = ctx64.Rsp; // Use 32 bit context if available - if(_memory.core().isWow64()) + if(_core->isWow64()) { thd.GetContext( ctx32, WOW64_CONTEXT_ALL, true ); ip = ctx32.Eip; @@ -551,7 +561,7 @@ DWORD RemoteHook::OnAccessViolation( const DEBUG_EVENT& DebugEv ) std::vector> results; StackBacktrace( ip, sp, thd, results, 1 ); - RemoteContext context( _memory, thd, ctx64, !results.empty() ? results.back().first : 0, _x64Target, _wordSize ); + RemoteContext context( _memory, &thd, &ctx64, !results.empty() ? results.back().first : 0, _x64Target, _wordSize ); // Under AMD64 exception is thrown before 'ret' gets executed // Return must be detected manually @@ -566,14 +576,14 @@ DWORD RemoteHook::OnAccessViolation( const DEBUG_EVENT& DebugEv ) if (hook.flags & returnHook) { - RemoteContext fixedContext( _memory, thd, hook.entryCtx, !results.empty() ? results.back().first : 0, _x64Target, _wordSize ); + RemoteContext fixedContext( _memory, &thd, &hook.entryCtx, !results.empty() ? results.back().first : 0, _x64Target, _wordSize ); if (hook.onReturn.classFn.classPtr && hook.onReturn.classFn.ptr != nullptr) hook.onReturn.classFn.ptr( hook.onExecute.classFn.classPtr, fixedContext ); else if (hook.onReturn.freeFn != nullptr) hook.onReturn.freeFn( fixedContext ); - hook.entryCtx = { 0 }; + hook.entryCtx = { }; } _retHooks.erase( addr ); @@ -620,9 +630,9 @@ DWORD RemoteHook::StackBacktrace( ptr_t ip, ptr_t sp, Thread& thd, std::vectorisWow64()) { - _TEB32 teb32 = { { 0 } }; + _TEB32 teb32 = { }; if (thd.teb( &teb32 ) == 0) return 0; @@ -630,7 +640,7 @@ DWORD RemoteHook::StackBacktrace( ptr_t ip, ptr_t sp, Thread& thd, std::vectorRead( stackPtr, _wordSize, &stack_val ); + MEMORY_BASIC_INFORMATION64 meminfo = { }; ptr_t original = stack_val & 0x7FFFFFFFFFFFFFFF; // Invalid value - if (stack_val < _core.native()->minAddr() || original > _core.native()->maxAddr()) + if (stack_val < _core->native()->minAddr() || original > _core->native()->maxAddr()) continue; // Check if memory is executable - if (_core.native()->VirtualQueryExT( original, &meminfo ) != STATUS_SUCCESS) + if (!NT_SUCCESS( _core->native()->VirtualQueryExT( original, &meminfo ) )) continue; if ( meminfo.AllocationProtect != PAGE_EXECUTE_READ && @@ -665,7 +675,7 @@ DWORD RemoteHook::StackBacktrace( ptr_t ip, ptr_t sp, Thread& thd, std::vectorRead( original - 6, sizeof(codeChunk), codeChunk ); // Detect 'call' instruction // TODO: Implement more reliable way to detect 'call' diff --git a/src/BlackBone/Process/RPC/RemoteHook.h b/src/BlackBone/Process/RPC/RemoteHook.h index fbfd573b..69969b10 100644 --- a/src/BlackBone/Process/RPC/RemoteHook.h +++ b/src/BlackBone/Process/RPC/RemoteHook.h @@ -18,7 +18,7 @@ namespace blackbone class RemoteHook { public: - + // Hook type enum eHookType { @@ -69,7 +69,7 @@ class RemoteHook using setAddresses = std::map; public: - BLACKBONE_API RemoteHook( class ProcessMemory& memory ); + BLACKBONE_API RemoteHook( class ProcessMemory* memory ); BLACKBONE_API ~RemoteHook(); /// @@ -142,7 +142,6 @@ class RemoteHook BLACKBONE_API void reset(); private: - /// /// Hook specified address /// @@ -239,12 +238,12 @@ class RemoteHook RemoteHook& operator =( const RemoteHook& ) = delete; private: - class ProcessMemory& _memory; - class ProcessCore& _core; - CriticalSection _lock; // Hook lock + class ProcessMemory* _memory; + class ProcessCore* _core; + CriticalSection _lock; // Hook lock DWORD _debugPID = 0; // PID of process being debugged - HANDLE _hEventThd = NULL; // Debug Event thread + HANDLE _hEventThd = nullptr; // Debug Event thread BOOL _x64Target = FALSE; // Target is x64 process int _wordSize = 4; // 4 or 8 bytes bool _active = false; // Event thread activity flag diff --git a/src/BlackBone/Process/RPC/RemoteLocalHook.cpp b/src/BlackBone/Process/RPC/RemoteLocalHook.cpp index fb6c8594..0647ad9f 100644 --- a/src/BlackBone/Process/RPC/RemoteLocalHook.cpp +++ b/src/BlackBone/Process/RPC/RemoteLocalHook.cpp @@ -1,21 +1,15 @@ #include "RemoteLocalHook.h" - #include "../Process.h" #include "../../Asm/LDasm.h" - #include - - // Must be enough to hold the displaced original code (instr_align(sizeof(1*jmp64))) plus the return jmp (sizeof(1*jmp64)) #define THUNK_MAX_SIZE 50 - - namespace blackbone { -RemoteLocalHook::RemoteLocalHook( class Process& process ) +RemoteLocalHook::RemoteLocalHook( class Process* process ) : _process( process ) { } @@ -25,57 +19,53 @@ RemoteLocalHook::~RemoteLocalHook() Restore(); } -NTSTATUS RemoteLocalHook::AllocateMem( ptr_t address, size_t hookCodeSize ) +void RemoteLocalHook::AllocateMem( ptr_t /*address*/, size_t hookCodeSize ) { - auto pagesize = _process.core().native()->pageSize(); + auto pagesize = _process->core().native()->pageSize(); auto size = Align( hookCodeSize + THUNK_MAX_SIZE, pagesize ); - auto allocation = _process.memory().AllocateClosest( size, PAGE_EXECUTE_READWRITE, address ); - if (!allocation) - return allocation.status; - - _hookData = std::move( allocation.result() ); - return allocation.status; + _hookData = _process->memory().Allocate( size, PAGE_EXECUTE_READWRITE ); } void RemoteLocalHook::SetJumpStrategy(eJumpStrategy strategy) { - _jumpStrategy = strategy; + _jumpStrategy = strategy; } void RemoteLocalHook::SetJumpRegister(const asmjit::X86GpReg& reg) { - _jumpRegister = ® + _jumpRegister = ® } size_t RemoteLocalHook::GetDisplacedOriginalCode( uint8_t* code ) { - if (!_prepared) - return 0; - if (code) - memcpy(code, _ctx.origCode, _ctx.origCodeSize); - return _ctx.origCodeSize; + if (!_prepared) + return 0; + if (code) + memcpy(code, _ctx.origCode, _ctx.origCodeSize); + return _ctx.origCodeSize; } NTSTATUS RemoteLocalHook::PrepareHook( ptr_t address, size_t maxHookSize ) { - _ctx.address = address; - NTSTATUS status = AllocateMem( address, maxHookSize ); - if (!NT_SUCCESS( status )) - return status; + _ctx.address = address; + AllocateMem( address, maxHookSize ); _ctx.thunkAddr = _hookData.ptr() + maxHookSize; - - bool x64 = !_process.core().isWow64(); + bool x64 = !_process->core().isWow64(); _ctx.hookJumpCodeSize = GenerateJump( _ctx.hookJumpCode, _hookData.ptr(), address, x64 ); - status = CopyOldCode( x64 ); - if (!NT_SUCCESS( status )) { - _hookData.Free(); - _hookData = MemBlock(); - return status; + try + { + CopyOldCode( x64 ); + } + catch (const std::exception&) + { + _hookData.Free(); + _hookData = MemBlock(); + throw; } _prepared = true; @@ -83,101 +73,89 @@ NTSTATUS RemoteLocalHook::PrepareHook( ptr_t address, size_t maxHookSize ) return STATUS_SUCCESS; } -NTSTATUS RemoteLocalHook::SetHook( ptr_t address, asmjit::Assembler& hook ) +void RemoteLocalHook::SetHook( ptr_t address, asmjit::Assembler& hook ) { - bool x64 = !_process.core().isWow64(); - auto& mem = _process.memory(); + bool x64 = !_process->core().isWow64(); + auto& mem = _process->memory(); NTSTATUS status = STATUS_SUCCESS; if (!_prepared) { - status = PrepareHook( address, hook.getCodeSize() ); - if (!NT_SUCCESS( status )) { - return status; - } + status = PrepareHook( address, hook.getCodeSize() ); + THROW_ON_FAIL_AND_LOG(status, "failed to prepare hook") } uint8_t hookCode[256]; uint8_t* heapHookCode = nullptr; // Only used if hook.getCodeSize() > sizeof(hookCode) if (hook.getCodeSize() > sizeof(hookCode)) { - heapHookCode = new uint8_t[hook.getCodeSize()]; + heapHookCode = new uint8_t[hook.getCodeSize()]; } hook.setBaseAddress( _hookData.ptr() ); - hook.relocCode( heapHookCode ? heapHookCode : hookCode ); + hook.relocCode( heapHookCode ? heapHookCode : hookCode ); - uint8_t jmpBackCode[sizeof(_ctx.hookJumpCode)]; - uint8_t jmpBackCodeSize; + uint8_t jmpBackCode[sizeof(_ctx.hookJumpCode)]; + uint8_t jmpBackCodeSize; - jmpBackCodeSize = GenerateJump(jmpBackCode, address + _ctx.origCodeSize, _hookData.ptr() + hook.getCodeSize() + _ctx.origCodeSize, x64); + jmpBackCodeSize = GenerateJump(jmpBackCode, address + _ctx.origCodeSize, _hookData.ptr() + hook.getCodeSize() + _ctx.origCodeSize, x64); - if (hook.getCodeSize() > (_ctx.thunkAddr - _hookData.ptr())) { - // Can happen if PrepareHook() was called manually with maxCodeSize < hook.getCodeSize(). - delete[] heapHookCode; - return STATUS_NO_MEMORY; + if (hook.getCodeSize() > (_ctx.thunkAddr - _hookData.ptr())) { + // Can happen if PrepareHook() was called manually with maxCodeSize < hook.getCodeSize(). + delete[] heapHookCode; + THROW_WITH_STATUS_AND_LOG( STATUS_NO_MEMORY, "maxCodeSize < hook.getCodeSize()") } mem.Write( _hookData.ptr(), hook.getCodeSize(), heapHookCode ? heapHookCode : hookCode ); - mem.Write( _ctx.thunkAddr, _ctx.origCodeSize, _ctx.patchedOrigCode ); - mem.Write( _ctx.thunkAddr + _ctx.origCodeSize, jmpBackCodeSize, jmpBackCode ); - - // Fill region between end of hook and start of thunk with nop. This region is normally empty, but can be non-empty - // if PrepareHook() was called manually with maxCodeSize > hook.getCodeSize(). - for (ptr_t addr = _hookData.ptr() + hook.getCodeSize() ; addr < _ctx.thunkAddr ; addr++) - { - uint8_t nop = 0x90; - mem.Write(addr, nop); - } + mem.Write( _ctx.thunkAddr, _ctx.origCodeSize, _ctx.patchedOrigCode ); + mem.Write( _ctx.thunkAddr + _ctx.origCodeSize, jmpBackCodeSize, jmpBackCode ); - delete[] heapHookCode; + // Fill region between end of hook and start of thunk with nop. This region is normally empty, but can be non-empty + // if PrepareHook() was called manually with maxCodeSize > hook.getCodeSize(). + for (ptr_t addr = _hookData.ptr() + hook.getCodeSize() ; addr < _ctx.thunkAddr ; addr++) + { + uint8_t nop = 0x90; + mem.Write(addr, nop); + } - DWORD flOld = 0; - mem.Protect( address, _ctx.hookJumpCodeSize, PAGE_EXECUTE_READWRITE, &flOld ); - status = mem.Write( address, _ctx.hookJumpCodeSize, _ctx.hookJumpCode ); - mem.Protect( address, _ctx.hookJumpCodeSize, flOld ); + delete[] heapHookCode; - if (NT_SUCCESS( status )) - _hooked = true; + DWORD flOld = 0; + mem.Protect( address, _ctx.hookJumpCodeSize, PAGE_EXECUTE_READWRITE, &flOld ); + status = mem.WriteNoThrow( address, _ctx.hookJumpCodeSize, _ctx.hookJumpCode ); + mem.Protect( address, _ctx.hookJumpCodeSize, flOld ); - return status; + THROW_ON_FAIL_AND_LOG( status, "failed to write hook" ) + _hooked = true; } -NTSTATUS RemoteLocalHook::Restore() +void RemoteLocalHook::Restore() { - NTSTATUS status = STATUS_SUCCESS; + if (_hooked) { + DWORD flOld = 0; + _process->memory().Protect( _ctx.address, _ctx.hookJumpCodeSize, PAGE_EXECUTE_READWRITE, &flOld ); + auto status = _process->memory().WriteNoThrow( _ctx.address, _ctx.origCodeSize, _ctx.origCode ); + _process->memory().Protect( _ctx.address, _ctx.hookJumpCodeSize, flOld ); + + THROW_ON_FAIL_AND_LOG( status, "failed to write original code" ); + } + if (_hookData.valid()) { + _hookData.Free(); + _hookData = MemBlock(); + } - if (_hooked) { - DWORD flOld = 0; - _process.memory().Protect( _ctx.address, _ctx.hookJumpCodeSize, PAGE_EXECUTE_READWRITE, &flOld ); - status = _process.memory().Write( _ctx.address, _ctx.origCodeSize, _ctx.origCode ); - _process.memory().Protect( _ctx.address, _ctx.hookJumpCodeSize, flOld ); - - if (!NT_SUCCESS( status )) { - return status; - } - } - if (_hookData.valid()) { - _hookData.Free(); - _hookData = MemBlock(); - } - - _prepared = false; - _hooked = false; - - return status; + _prepared = false; + _hooked = false; } -NTSTATUS RemoteLocalHook::CopyOldCode( bool x64 ) +void RemoteLocalHook::CopyOldCode( bool x64 ) { - NTSTATUS status = STATUS_SUCCESS; - - _process.memory().Read( _ctx.address, sizeof( _ctx.origCode ), _ctx.origCode ); - memcpy(_ctx.patchedOrigCode, _ctx.origCode, sizeof(_ctx.patchedOrigCode)); + _process->memory().Read( _ctx.address, sizeof( _ctx.origCode ), _ctx.origCode ); + memcpy(_ctx.patchedOrigCode, _ctx.origCode, sizeof(_ctx.patchedOrigCode)); // Store original bytes - uint8_t* src = _ctx.origCode; - ptr_t newAddr = _ctx.thunkAddr; + uint8_t* src = _ctx.origCode; + ptr_t newAddr = _ctx.thunkAddr; uint32_t thunkSize = 0; ldasm_data ld = { 0 }; @@ -200,7 +178,7 @@ NTSTATUS RemoteLocalHook::CopyOldCode( bool x64 ) // if instruction has relative offset, calculate new offset if (ld.flags & F_RELATIVE) { - int32_t diff = 0; + int32_t diff = 0; const uintptr_t ofst = (ld.disp_offset != 0 ? ld.disp_offset : ld.imm_offset); const uintptr_t sz = ld.disp_size != 0 ? ld.disp_size : ld.imm_size; @@ -211,9 +189,8 @@ NTSTATUS RemoteLocalHook::CopyOldCode( bool x64 ) int64_t newDiff = ((int64_t) diff) + (((ptr_t) (_ctx.address+thunkSize))-newAddr); - if (newDiff < diffMinVals[sz] || newDiff > diffMaxVals[sz]) { - status = STATUS_NOT_IMPLEMENTED; - break; + if (newDiff < diffMinVals[sz] || newDiff > diffMaxVals[sz]) { + THROW_WITH_STATUS_AND_LOG( STATUS_NOT_IMPLEMENTED, "can't relocate jump" ); } memcpy(_ctx.patchedOrigCode + thunkSize + ofst, &newDiff, sz); @@ -228,78 +205,76 @@ NTSTATUS RemoteLocalHook::CopyOldCode( bool x64 ) if (thunkSize < _ctx.hookJumpCodeSize) { - // TODO: Anything else we can do now? + // TODO: Anything else we can do now? } else { - _ctx.origCodeSize = static_cast(thunkSize); + _ctx.origCodeSize = static_cast(thunkSize); } - - return status; } uint8_t RemoteLocalHook::GenerateJump( uint8_t* code, ptr_t toAddr, ptr_t fromAddr, bool x64 ) const { - size_t size = 0; - - auto asmp = AsmFactory::GetAssembler(); - auto& a = *asmp; - - int64_t relJmp = toAddr >= fromAddr ? (int64_t) (toAddr-fromAddr) : -(int64_t)(fromAddr-toAddr); - - if (x64 && _abs64( relJmp ) > INT32_MAX) - { - switch (_jumpStrategy) - { - case JumpPushMovRet: - // A relatively non-intrusive way to jmp far on x86_64, leaving all registers intact. - // As described on Nikolay Igotti's blog: - // https://web.archive.org/web/20090504135800/http://blogs.sun.com/nike/entry/long_absolute_jumps_on_amd64 - // See also Gil Dabah's blog post, where it's #3: - // https://www.ragestorm.net/blogs/?p=107 - - // push toAddr[0:31] - *code = 0x68; - *((uint32_t*) (code+1)) = (uint32_t) (toAddr & 0xFFFFFFFF); - - if ((toAddr >> 32) != 0) - { - // mov [rsp+4], toAddr[32:63] - *((uint32_t*) (code+5)) = 0x042444C7; - *((uint32_t*) (code+9)) = (uint32_t) (toAddr >> 32); - - // ret - *(code+13) = 0xC3; - - size = 14; - } - else - { - // ret - *(code+5) = 0xC3; - size = 6; - } - break; - case JumpMovRegRet: - // Alternative method that overwrites a register, but keeps the stack untouched. See #2: - // https://www.ragestorm.net/blogs/?p=107 - a->mov(*_jumpRegister, (uint64_t) toAddr); - a->jmp(*_jumpRegister); - size = a->relocCode(code); - break; - } - } - else - { - // jmp rel toAddr - *code = 0xE9; - *((int32_t*) (code+1)) = (int32_t) (relJmp - 5); - - size = 5; - } - - assert(size <= sizeof(_ctx.hookJumpCode)); - return static_cast(size); + size_t size = 0; + + auto asmp = AsmFactory::GetAssembler(); + auto& a = *asmp; + + int64_t relJmp = toAddr >= fromAddr ? (int64_t) (toAddr-fromAddr) : -(int64_t)(fromAddr-toAddr); + + if (x64 && _abs64( relJmp ) > INT32_MAX) + { + switch (_jumpStrategy) + { + case JumpPushMovRet: + // A relatively non-intrusive way to jmp far on x86_64, leaving all registers intact. + // As described on Nikolay Igotti's blog: + // https://web.archive.org/web/20090504135800/http://blogs.sun.com/nike/entry/long_absolute_jumps_on_amd64 + // See also Gil Dabah's blog post, where it's #3: + // https://www.ragestorm.net/blogs/?p=107 + + // push toAddr[0:31] + *code = 0x68; + *((uint32_t*) (code+1)) = (uint32_t) (toAddr & 0xFFFFFFFF); + + if ((toAddr >> 32) != 0) + { + // mov [rsp+4], toAddr[32:63] + *((uint32_t*) (code+5)) = 0x042444C7; + *((uint32_t*) (code+9)) = (uint32_t) (toAddr >> 32); + + // ret + *(code+13) = 0xC3; + + size = 14; + } + else + { + // ret + *(code+5) = 0xC3; + size = 6; + } + break; + case JumpMovRegRet: + // Alternative method that overwrites a register, but keeps the stack untouched. See #2: + // https://www.ragestorm.net/blogs/?p=107 + a->mov(*_jumpRegister, (uint64_t) toAddr); + a->jmp(*_jumpRegister); + size = a->relocCode(code); + break; + } + } + else + { + // jmp rel toAddr + *code = 0xE9; + *((int32_t*) (code+1)) = (int32_t) (relJmp - 5); + + size = 5; + } + + assert(size <= sizeof(_ctx.hookJumpCode)); + return static_cast(size); } } diff --git a/src/BlackBone/Process/RPC/RemoteLocalHook.h b/src/BlackBone/Process/RPC/RemoteLocalHook.h index 2f6f0b3b..2e979872 100644 --- a/src/BlackBone/Process/RPC/RemoteLocalHook.h +++ b/src/BlackBone/Process/RPC/RemoteLocalHook.h @@ -5,59 +5,58 @@ #include "../../Include/Types.h" #include "../MemBlock.h" - - namespace blackbone { - /// /// In-process remote hook /// class RemoteLocalHook { public: - // Must be large enough to hold ANY jump this code uses - static const size_t MaxHookJumpCodeLen = 14; + // Must be large enough to hold ANY jump this code uses + static const size_t MaxHookJumpCodeLen = 14; - // The displaced code should be at most 1 hook length + 1 instruction - 1 byte (if the hook jump overlaps with only the - // first byte of the following instruction), and x86_64 instructions can be at most 15 bytes. - static const size_t MaxOriginalCodeLen = MaxHookJumpCodeLen + 14; + // The displaced code should be at most 1 hook length + 1 instruction - 1 byte (if the hook jump overlaps with only the + // first byte of the following instruction), and x86_64 instructions can be at most 15 bytes. + static const size_t MaxOriginalCodeLen = MaxHookJumpCodeLen + 14; - static const size_t MaxPatchedOriginalCodeLen = MaxOriginalCodeLen; + static const size_t MaxPatchedOriginalCodeLen = MaxOriginalCodeLen; - enum eJumpStrategy - { - JumpPushMovRet, - JumpMovRegRet - }; + enum eJumpStrategy + { + JumpPushMovRet, + JumpMovRegRet + }; private: - /// - /// Hook data - /// - #pragma pack(push, 1) - struct HookCtx - { - ptr_t address; // Hooked address in original code - ptr_t thunkAddr; - uint8_t origCodeSize; // Size of displaced original code - uint8_t origCode[MaxOriginalCodeLen]; // Copy of displaced original code - uint8_t patchedOrigCode[MaxPatchedOriginalCodeLen]; - uint8_t hookJumpCode[MaxHookJumpCodeLen]; - uint8_t hookJumpCodeSize; - }; - #pragma pack(pop) + /// + /// Hook data + /// + #pragma pack(push, 1) + struct HookCtx + { + ptr_t address; // Hooked address in original code + ptr_t thunkAddr; + uint8_t origCodeSize; // Size of displaced original code + uint8_t origCode[MaxOriginalCodeLen]; // Copy of displaced original code + uint8_t patchedOrigCode[MaxPatchedOriginalCodeLen]; + uint8_t hookJumpCode[MaxHookJumpCodeLen]; + uint8_t hookJumpCodeSize; + }; + #pragma pack(pop) public: - RemoteLocalHook( class Process& process ); - ~RemoteLocalHook(); + BLACKBONE_API RemoteLocalHook( class Process* process ); + RemoteLocalHook( const RemoteLocalHook& ) = delete; + + BLACKBONE_API ~RemoteLocalHook(); void SetJumpStrategy(eJumpStrategy strategy); void SetJumpRegister(const asmjit::X86GpReg& reg); - NTSTATUS SetHook( ptr_t address, asmjit::Assembler& hook ); - NTSTATUS Restore(); + BLACKBONE_API void SetHook( ptr_t address, asmjit::Assembler& hook ); + BLACKBONE_API void Restore(); NTSTATUS PrepareHook( ptr_t address, size_t maxHookSize ); @@ -66,17 +65,14 @@ class RemoteLocalHook bool isHooked() const { return _hooked; } private: - RemoteLocalHook( const RemoteLocalHook& ) = delete; - RemoteLocalHook& operator = (const RemoteLocalHook&) = delete; - - NTSTATUS AllocateMem( ptr_t address, size_t hookCodeSize ); + void AllocateMem( ptr_t address, size_t hookCodeSize ); - NTSTATUS CopyOldCode( bool x64 ); + void CopyOldCode( bool x64 ); uint8_t GenerateJump( uint8_t* code, ptr_t toAddr, ptr_t fromAddr, bool x64 ) const; private: - class Process& _process; + class Process* _process; HookCtx _ctx; MemBlock _hookData; eJumpStrategy _jumpStrategy = JumpPushMovRet; diff --git a/src/BlackBone/Process/RPC/RemoteMemory.cpp b/src/BlackBone/Process/RPC/RemoteMemory.cpp index 92994569..d1a1417d 100644 --- a/src/BlackBone/Process/RPC/RemoteMemory.cpp +++ b/src/BlackBone/Process/RPC/RemoteMemory.cpp @@ -23,7 +23,7 @@ RemoteMemory::~RemoteMemory() /// Status code NTSTATUS RemoteMemory::Map( bool mapSections ) { - MapMemoryResult result = { 0 }; + MapMemoryResult result = { }; // IPC if (!_hPipe) @@ -52,7 +52,7 @@ NTSTATUS RemoteMemory::Map( bool mapSections ) /// Status code NTSTATUS RemoteMemory::Map( ptr_t base, uint32_t size ) { - MapMemoryRegionResult memRes = { 0 }; + MapMemoryRegionResult memRes = { }; // IPC if (!_hPipe) @@ -161,9 +161,6 @@ NTSTATUS RemoteMemory::SetupHook( OperationType hkType ) { static const char* procNames[] = { "NtAllocateVirtualMemory", "NtFreeVirtualMemory", "NtMapViewOfSection", "NtUnmapViewOfSection" }; - uint8_t* pTranslated = nullptr; - ptr_t pProc = 0; - // Can't setup hook without target pipe and shared data if (_targetPipe == NULL || !_pSharedData || !_targetShare) return STATUS_NONE_MAPPED; @@ -180,10 +177,8 @@ NTSTATUS RemoteMemory::SetupHook( OperationType hkType ) auto& modules = _process->modules(); // Local and remote process address - pProc = modules.GetExport( modules.GetModule( L"ntdll.dll" ), procNames[hkType] ).result( exportData() ).procAddress; - pTranslated = (uint8_t*)TranslateAddress( pProc ); - if (!pTranslated) - return STATUS_INVALID_ADDRESS; + ptr_t pProc = modules.GetNtdllExport( procNames[hkType] ).procAddress; + uint8_t* pTranslated = reinterpret_cast(TranslateAddress( pProc )); // IPC if (!_hPipe) @@ -214,8 +209,6 @@ NTSTATUS RemoteMemory::SetupHook( OperationType hkType ) /// true on success bool RemoteMemory::RestoreHook( OperationType hkType ) { - uint8_t* pTranslated = nullptr; - ptr_t pProc = 0; static const char* procNames[] = { "NtAllocateVirtualMemory", "NtFreeVirtualMemory", "NtMapViewOfSection", "NtUnmapViewOfSection" }; // Not hooked @@ -225,10 +218,8 @@ bool RemoteMemory::RestoreHook( OperationType hkType ) auto& modules = _process->modules(); // Local and remote proc address - pProc = modules.GetExport( modules.GetModule( L"ntdll.dll" ), procNames[hkType] ).result( exportData() ).procAddress; - pTranslated = (uint8_t*)TranslateAddress( pProc ); - if (!pTranslated) - return false; + ptr_t pProc = modules.GetNtdllExport( procNames[hkType] ).procAddress; + uint8_t* pTranslated = reinterpret_cast(TranslateAddress( pProc )); // Restore bytes memcpy( pTranslated, (uint8_t*)_pSharedData + sizeof( HookData ) * hkType + FIELD_OFFSET( HookData, original_code ), @@ -294,7 +285,7 @@ void RemoteMemory::HookThread() while (_active) { - OperationData opData = { 0 }; + OperationData opData = { }; // Target endpoint closed if (!ReadFile( _hPipe, &opData, sizeof( opData ), &bytes, NULL )) @@ -343,9 +334,9 @@ void RemoteMemory::BuildGenericHookFn( OperationType opType ) auto& modules = _process->modules(); - auto pEnterCS = modules.GetExport( modules.GetModule( L"ntdll.dll" ), "RtlEnterCriticalSection" ).result( exportData() ); - auto pLeaveCS = modules.GetExport( modules.GetModule( L"ntdll.dll" ), "RtlLeaveCriticalSection" ).result( exportData() ); - auto pWrite = modules.GetExport( modules.GetModule( L"kernel32.dll" ), "WriteFile" ).result( exportData() ); + auto pEnterCS = modules.GetNtdllExport( "RtlEnterCriticalSection" ); + auto pLeaveCS = modules.GetNtdllExport( "RtlLeaveCriticalSection" ); + auto pWrite = modules.GetExport( modules.GetModule( L"kernel32.dll" ), "WriteFile" ); a.GenPrologue(); a.EnableX64CallStack( false ); @@ -468,9 +459,9 @@ void RemoteMemory::BuildGenericHookFn( OperationType opType ) auto& modules = _process->modules(); - auto pEnterCS = modules.GetExport( modules.GetModule( L"ntdll.dll" ), "RtlEnterCriticalSection" ).result( exportData() ).procAddress; - auto pLeaveCS = modules.GetExport( modules.GetModule( L"ntdll.dll" ), "RtlLeaveCriticalSection" ).result( exportData() ).procAddress; - auto pWrite = modules.GetExport( modules.GetModule( L"kernel32.dll" ), "WriteFile" ).result( exportData() ).procAddress; + auto pEnterCS = modules.GetNtdllExport( "RtlEnterCriticalSection" ).procAddress; + auto pLeaveCS = modules.GetNtdllExport( "RtlLeaveCriticalSection" ).procAddress; + auto pWrite = modules.GetExport( modules.GetModule( L"kernel32.dll" ), "WriteFile" ).procAddress; a.GenPrologue(); a->sub( asmjit::host::esp, sa.getTotalSize() ); @@ -494,7 +485,7 @@ void RemoteMemory::BuildGenericHookFn( OperationType opType ) } // RtlEnterCriticalSection - a.GenCall( (uintptr_t)pEnterCS, { (uintptr_t)_targetShare + FIELD_OFFSET( PageContext, csLock ) } ); + a.GenCall( static_cast(pEnterCS), { (uintptr_t)_targetShare + FIELD_OFFSET( PageContext, csLock ) } ); // Storage pointer a->lea( asmjit::host::edx, data ); @@ -524,8 +515,8 @@ void RemoteMemory::BuildGenericHookFn( OperationType opType ) // Operation type a->mov( asmjit::host::dword_ptr( asmjit::host::edx, FIELD_OFFSET( OperationData, allocType ) ), opType ); - a.GenCall( (uintptr_t)pWrite, { (uintptr_t)_targetPipe, asmjit::host::edx, sizeof( OperationData ), &junk, 0 } ); - a.GenCall( (uintptr_t)pLeaveCS, { (uintptr_t)_targetShare + FIELD_OFFSET( PageContext, csLock ) } ); + a.GenCall( static_cast(pWrite), { (uintptr_t)_targetPipe, asmjit::host::edx, sizeof( OperationData ), &junk, 0 } ); + a.GenCall( static_cast(pLeaveCS), { (uintptr_t)_targetShare + FIELD_OFFSET( PageContext, csLock ) } ); // Ignore return value a->xor_( asmjit::host::eax, asmjit::host::eax ); diff --git a/src/BlackBone/Process/RPC/RemoteMemory.h b/src/BlackBone/Process/RPC/RemoteMemory.h index 894bd840..61e9dbd2 100644 --- a/src/BlackBone/Process/RPC/RemoteMemory.h +++ b/src/BlackBone/Process/RPC/RemoteMemory.h @@ -156,7 +156,7 @@ class RemoteMemory PageContext* _pSharedData = nullptr; // Hook related data, shared between processes ptr_t _targetShare = 0; // Address of shared in data in target process bool _active = false; // Hook thread activity flag - bool _hooked[4] = { 0 }; // Hook state + bool _hooked[4] = { }; // Hook state }; } \ No newline at end of file diff --git a/src/BlackBone/Process/Threads/Thread.cpp b/src/BlackBone/Process/Threads/Thread.cpp index 5efab5ce..b8417c69 100644 --- a/src/BlackBone/Process/Threads/Thread.cpp +++ b/src/BlackBone/Process/Threads/Thread.cpp @@ -2,6 +2,7 @@ #include "../ProcessCore.h" #include "../../Misc/DynImport.h" #include "../../Include/Macro.h" +#include "../../Include/Exception.h" namespace blackbone { @@ -30,9 +31,13 @@ Thread::~Thread() /// /// Process TEB /// TEB pointer -blackbone::ptr_t Thread::teb( _TEB32* pteb ) const +ptr_t Thread::teb( _TEB32* pteb ) const { - return _core->native()->getTEB( _handle, pteb ); + ptr_t ptr = _core->native()->getTEB( _handle, pteb ); + if (!ptr) + THROW_WITH_STATUS_AND_LOG( LastNtStatus(), "failed to get TEB32 address" ); + + return ptr; } /// @@ -40,9 +45,13 @@ blackbone::ptr_t Thread::teb( _TEB32* pteb ) const /// /// Process TEB /// TEB pointer -blackbone::ptr_t Thread::teb( _TEB64* pteb ) const +ptr_t Thread::teb( _TEB64* pteb ) const { - return _core->native()->getTEB( _handle, pteb ); + ptr_t ptr = _core->native()->getTEB( _handle, pteb ); + if (!ptr) + THROW_WITH_STATUS_AND_LOG( LastNtStatus(), "failed to get TEB64 address" ); + + return ptr; } /// @@ -59,8 +68,8 @@ bool Thread::Suspend() const auto& barrier = _core->native()->GetWow64Barrier(); if (barrier.type == wow_64_32 && !barrier.x86OS) return (SAFE_CALL(Wow64SuspendThread, _handle ) != -1); - else - return (SuspendThread( _handle ) != -1); + + return (SuspendThread( _handle ) != -1); } /// @@ -205,18 +214,18 @@ bool Thread::Join( int timeout /*= INFINITE*/ ) /// Breakpoint address /// Breakpoint type(read/write/execute) /// Number of bytes to include into breakpoint -/// Index of used breakpoint; -1 if failed -call_result_t Thread::AddHWBP( ptr_t addr, HWBPType type, HWBPLength length ) +/// Index of used breakpoint +int Thread::AddHWBP( ptr_t addr, HWBPType type, HWBPLength length ) { - _CONTEXT64 context64 = { 0 }; - _CONTEXT32 context32 = { 0 }; + _CONTEXT64 context64 = { }; + _CONTEXT32 context32 = { }; bool use64 = !_core->native()->GetWow64Barrier().x86OS; // CONTEXT_DEBUG_REGISTERS can be operated without thread suspension auto status = use64 ? GetContext( context64, CONTEXT64_DEBUG_REGISTERS, true ) : GetContext( context32, CONTEXT_DEBUG_REGISTERS, true ); auto pDR7 = use64 ? reinterpret_cast(&context64.Dr7) : reinterpret_cast(&context32.Dr7); if (!NT_SUCCESS( status )) - return status; + THROW_WITH_STATUS_AND_LOG( status, "failed to get thread context" ); // Check if HWBP is already present for (int i = 0; i < 4; i++) @@ -231,7 +240,7 @@ call_result_t Thread::AddHWBP( ptr_t addr, HWBPType type, HWBPLength length // If all 4 registers are occupied - error if (freeIdx < 0) - return STATUS_NO_MORE_ENTRIES; + THROW_AND_LOG( "no free HWBP slots" ); // Enable corresponding HWBP and local BP flag @@ -244,7 +253,10 @@ call_result_t Thread::AddHWBP( ptr_t addr, HWBPType type, HWBPLength length // Write values to registers status = use64 ? SetContext( context64, true ) : SetContext( context32, true ); - return call_result_t( freeIdx, status ); + if (!NT_SUCCESS( status )) + THROW_WITH_STATUS_AND_LOG(status, "failed to set thread context with HWBP"); + + return freeIdx; } /// @@ -257,8 +269,8 @@ NTSTATUS Thread::RemoveHWBP( int idx ) if (idx < 0 || idx > 4) return false; - _CONTEXT64 context64 = { 0 }; - _CONTEXT32 context32 = { 0 }; + _CONTEXT64 context64 = { }; + _CONTEXT32 context32 = { }; bool use64 = !_core->native()->GetWow64Barrier().x86OS; auto status = use64 ? GetContext( context64, CONTEXT64_DEBUG_REGISTERS, true ) : GetContext( context32, CONTEXT_DEBUG_REGISTERS, true ); auto pDR7 = use64 ? reinterpret_cast(&context64.Dr7) : reinterpret_cast(&context32.Dr7); @@ -281,13 +293,13 @@ NTSTATUS Thread::RemoveHWBP( int idx ) /// true on success NTSTATUS Thread::RemoveHWBP( ptr_t ptr ) { - _CONTEXT64 context64 = { 0 }; - _CONTEXT32 context32 = { 0 }; + _CONTEXT64 context64 = { }; + _CONTEXT32 context32 = { }; bool use64 = !_core->native()->GetWow64Barrier().x86OS; auto status = use64 ? GetContext( context64, CONTEXT64_DEBUG_REGISTERS, true ) : GetContext( context32, CONTEXT_DEBUG_REGISTERS, true ); auto pDR7 = use64 ? reinterpret_cast(&context64.Dr7) : reinterpret_cast(&context32.Dr7); if (!NT_SUCCESS( status )) - return false; + return status; // Search for breakpoint for (int i = 0; i < 4; i++) @@ -374,7 +386,7 @@ DWORD Thread::GetThreadIdT( HANDLE hThread ) // XP version else { - _THREAD_BASIC_INFORMATION_T tbi = { 0 }; + _THREAD_BASIC_INFORMATION_T tbi = { }; ULONG bytes = 0; if (NT_SUCCESS( SAFE_NATIVE_CALL( NtQueryInformationThread, hThread, (THREADINFOCLASS)0, &tbi, (ULONG)sizeof( tbi ), &bytes ) )) @@ -384,5 +396,4 @@ DWORD Thread::GetThreadIdT( HANDLE hThread ) } } - -} \ No newline at end of file +} diff --git a/src/BlackBone/Process/Threads/Thread.h b/src/BlackBone/Process/Threads/Thread.h index f82a6497..71550ae3 100644 --- a/src/BlackBone/Process/Threads/Thread.h +++ b/src/BlackBone/Process/Threads/Thread.h @@ -3,12 +3,14 @@ #include "../../Config.h" #include "../../Include/Winheaders.h" #include "../../Include/NativeStructures.h" -#include "../../Include/CallResult.h" #include "../../Include/HandleGuard.h" #include "../../Include/Types.h" #include "../../Misc/Utils.h" #include +#include +#include "BlackBone/Include/Exception.h" +#include "BlackBone/Include/Macro.h" namespace blackbone { @@ -79,12 +81,12 @@ struct regDR7 uint32_t rw3 : 2; uint32_t len3 : 2; - inline void setLocal( int idx, int val ) { idx == 0 ? l0 = val : (idx == 1 ? l1 = val : (idx == 2 ? l2 = val : l3 = val)); } - inline void setRW ( int idx, int val ) { idx == 0 ? rw0 = val : (idx == 1 ? rw1 = val : (idx == 2 ? rw2 = val : rw3 = val)); } - inline void setLen ( int idx, int val ) { idx == 0 ? len0 = val : (idx == 1 ? len1 = val : (idx == 2 ? len2 = val : len3 = val)); } + void setLocal( int idx, int val ) { idx == 0 ? l0 = val : (idx == 1 ? l1 = val : (idx == 2 ? l2 = val : l3 = val)); } + void setRW ( int idx, int val ) { idx == 0 ? rw0 = val : (idx == 1 ? rw1 = val : (idx == 2 ? rw2 = val : rw3 = val)); } + void setLen ( int idx, int val ) { idx == 0 ? len0 = val : (idx == 1 ? len1 = val : (idx == 2 ? len2 = val : len3 = val)); } - inline bool empty() const { return (l0 | l1 | l2 | l3) ? false : true; } - inline int getFreeIndex() const { return !l0 ? 0 : (!l1 ? 1 : (!l2 ? 2 : (!l3 ? 3 : -1))); } + bool empty() const { return (l0 | l1 | l2 | l3) ? false : true; } + int getFreeIndex() const { return !l0 ? 0 : (!l1 ? 1 : (!l2 ? 2 : (!l3 ? 3 : -1))); } }; @@ -113,7 +115,7 @@ class Thread /// /// Get thread handle /// - /// Thread hande + /// Thread handle BLACKBONE_API HANDLE handle() const { return _handle; } /// @@ -128,6 +130,7 @@ class Thread /// Process TEB /// TEB pointer BLACKBONE_API ptr_t teb( _TEB32* pteb ) const; + BLACKBONE_API ptr_t teb32( _TEB32* pteb = nullptr ) const { return teb( pteb ); } /// /// Get Native TEB @@ -135,12 +138,13 @@ class Thread /// Process TEB /// TEB pointer BLACKBONE_API ptr_t teb( _TEB64* pteb ) const; + BLACKBONE_API ptr_t teb64( _TEB64* pteb = nullptr ) const { return teb( pteb ); } /// /// Get TEB /// /// TEB pointer - BLACKBONE_API ptr_t teb() const { return teb( (TEB_T*)nullptr ); } + BLACKBONE_API ptr_t teb() const { return teb( static_cast(nullptr) ); } /// /// Get thread creation time @@ -232,8 +236,8 @@ class Thread /// Breakpoint address /// Breakpoint type(read/write/execute) /// Number of bytes to include into breakpoint - /// Index of used breakpoint; -1 if failed - BLACKBONE_API call_result_t AddHWBP( ptr_t addr, HWBPType type, HWBPLength length ); + /// Index of used breakpoint + BLACKBONE_API int AddHWBP( ptr_t addr, HWBPType type, HWBPLength length ); /// /// Remove existing hardware breakpoint @@ -254,7 +258,7 @@ class Thread /// BLACKBONE_API void Close(); - BLACKBONE_API inline bool operator ==( const Thread& other ) { return (_id == other._id); } + BLACKBONE_API bool operator ==( const Thread& other ) const { return _id == other._id; } private: /// @@ -273,4 +277,29 @@ class Thread using ThreadPtr = std::shared_ptr; -} \ No newline at end of file +class SuspendedThread +{ +public: + SuspendedThread( ThreadPtr thread ) + : _thread( std::move(thread) ) + { + if (!_thread->Suspend()) + THROW_WITH_STATUS_AND_LOG( LastNtStatus(), "failed to suspend thread %d", thread->id() ); + } + + SuspendedThread(const SuspendedThread&) = delete; + SuspendedThread(SuspendedThread&&) = default; + + ~SuspendedThread() + { + _thread->Resume(); + } + + SuspendedThread& operator =(const SuspendedThread&) = delete; + SuspendedThread& operator =(SuspendedThread&&) = default; + +private: + ThreadPtr _thread; +}; + +} diff --git a/src/BlackBone/Process/Threads/Threads.cpp b/src/BlackBone/Process/Threads/Threads.cpp index 5025b3a8..d3d8497e 100644 --- a/src/BlackBone/Process/Threads/Threads.cpp +++ b/src/BlackBone/Process/Threads/Threads.cpp @@ -1,5 +1,6 @@ #include "Threads.h" #include "../ProcessCore.h" +#include "../../Include/Exception.h" #include "../../DriverControl/DriverControl.h" #include @@ -9,30 +10,26 @@ namespace blackbone { -ProcessThreads::ProcessThreads( ProcessCore& core ) +ProcessThreads::ProcessThreads( ProcessCore* core ) : _core( core ) { } -ProcessThreads::~ProcessThreads() -{ -} - /// /// Create the thread. /// -/// Thread enty point +/// Thread entry point /// Thread argument. /// Thread creation flags /// New thread object -call_result_t ProcessThreads::CreateNew( ptr_t threadProc, ptr_t arg, enum CreateThreadFlags flags /*= NoThreadFlags*/ ) +ThreadPtr ProcessThreads::CreateNew( ptr_t threadProc, ptr_t arg, enum CreateThreadFlags flags /*= NoThreadFlags*/ ) { HANDLE hThd = NULL; - auto status = _core.native()->CreateRemoteThreadT( hThd, threadProc, arg, flags, THREAD_ALL_ACCESS ); + auto status = _core->native()->CreateRemoteThreadT( hThd, threadProc, arg, flags, THREAD_ALL_ACCESS ); if (!NT_SUCCESS( status )) { // Ensure full thread access - status = _core.native()->CreateRemoteThreadT( hThd, threadProc, arg, flags, THREAD_QUERY_LIMITED_INFORMATION ); + status = _core->native()->CreateRemoteThreadT( hThd, threadProc, arg, flags, THREAD_QUERY_LIMITED_INFORMATION ); if (NT_SUCCESS( status )) { if (Driver().loaded()) @@ -41,9 +38,9 @@ call_result_t ProcessThreads::CreateNew( ptr_t threadProc, ptr_t arg, } if (!NT_SUCCESS( status )) - return status; + THROW_WITH_STATUS_AND_LOG( status, "failed to create thread" ); - return std::make_shared( hThd, &_core ); + return std::make_shared( hThd, _core ); } /// @@ -57,7 +54,7 @@ std::vector ProcessThreads::getAll() const if (!hThreadSnapshot) return result; - THREADENTRY32 tEntry = { 0 }; + THREADENTRY32 tEntry = { }; tEntry.dwSize = sizeof( THREADENTRY32 ); // Iterate threads @@ -65,10 +62,10 @@ std::vector ProcessThreads::getAll() const success != FALSE; success = Thread32Next( hThreadSnapshot, &tEntry )) { - if (tEntry.th32OwnerProcessID != _core.pid()) + if (tEntry.th32OwnerProcessID != _core->pid()) continue; - result.emplace_back( std::make_shared( tEntry.th32ThreadID, &_core ) ); + result.emplace_back( std::make_shared( tEntry.th32ThreadID, _core ) ); } return result; @@ -82,7 +79,10 @@ ThreadPtr ProcessThreads::getMain() const { uint64_t mintime = MAXULONG64_2; auto threads = getAll(); - ThreadPtr result = !threads.empty() ? threads.front() : nullptr; + if (threads.empty()) + THROW_AND_LOG( "could not find any threads" ); + + ThreadPtr result = threads.front(); for (const auto& thread : threads) { @@ -105,7 +105,10 @@ ThreadPtr ProcessThreads::getLeastExecuted() const { uint64_t mintime = MAXULONG64_2; auto threads = getAll(); - ThreadPtr result = !threads.empty() ? threads.front() : nullptr; + if (threads.empty()) + THROW_AND_LOG( "could not find any threads" ); + + ThreadPtr result = threads.front(); for (const auto& thread : threads) { @@ -128,7 +131,10 @@ ThreadPtr ProcessThreads::getMostExecuted() const { uint64_t maxtime = 0; auto threads = getAll(); - ThreadPtr result = !threads.empty() ? threads.front() : nullptr; + if (threads.empty()) + THROW_AND_LOG( "could not find any threads" ); + + ThreadPtr result = threads.front(); for (const auto& thread : threads) { @@ -154,7 +160,7 @@ ThreadPtr ProcessThreads::getRandom() const { auto threads = getAll(); if (threads.empty()) - return nullptr; + THROW_AND_LOG( "could not find any threads" ); static std::random_device rd; std::uniform_int_distribution dist( 0, threads.size() - 1 ); @@ -171,8 +177,10 @@ ThreadPtr ProcessThreads::get( DWORD id ) const { auto threads = getAll(); auto iter = std::find_if( threads.begin(), threads.end(), [id]( const auto& thread ) { return thread->id() == id; } ); + if (iter == threads.end()) + THROW_AND_LOG( "no thread with id 0x%x", id ); - return iter != threads.end() ? *iter : nullptr; + return *iter; } } \ No newline at end of file diff --git a/src/BlackBone/Process/Threads/Threads.h b/src/BlackBone/Process/Threads/Threads.h index f34152c6..4ca2d413 100644 --- a/src/BlackBone/Process/Threads/Threads.h +++ b/src/BlackBone/Process/Threads/Threads.h @@ -12,8 +12,7 @@ namespace blackbone class ProcessThreads { public: - BLACKBONE_API ProcessThreads( class ProcessCore& core ); - BLACKBONE_API ~ProcessThreads(); + BLACKBONE_API ProcessThreads( class ProcessCore* core ); ProcessThreads( const ProcessThreads& ) = delete; ProcessThreads& operator =( const ProcessThreads& ) = delete; @@ -21,11 +20,11 @@ class ProcessThreads /// /// Create the thread. /// - /// Thread enty point + /// Thread entry point /// Thread argument. /// Thread creation flags /// New thread object - BLACKBONE_API call_result_t CreateNew( + BLACKBONE_API ThreadPtr CreateNew( ptr_t threadProc, ptr_t arg, enum CreateThreadFlags flags = static_cast(0) @@ -69,7 +68,7 @@ class ProcessThreads BLACKBONE_API ThreadPtr get( DWORD id ) const; private: - class ProcessCore& _core; // Core process functions + class ProcessCore* _core; // Core process functions }; } \ No newline at end of file diff --git a/src/BlackBone/Subsystem/NativeSubsystem.cpp b/src/BlackBone/Subsystem/NativeSubsystem.cpp index e3c4c997..461f46d3 100644 --- a/src/BlackBone/Subsystem/NativeSubsystem.cpp +++ b/src/BlackBone/Subsystem/NativeSubsystem.cpp @@ -10,10 +10,10 @@ namespace blackbone { -Native::Native( HANDLE hProcess, bool x86OS /*= false*/ ) +Native::Native( HANDLE hProcess, bool x86OS /*= false*/ ) noexcept : _hProcess( hProcess ) { - SYSTEM_INFO info = { { 0 } }; + SYSTEM_INFO info = { }; GetNativeSystemInfo( &info ); _pageSize = info.dwPageSize; @@ -48,13 +48,7 @@ Native::Native( HANDLE hProcess, bool x86OS /*= false*/ ) _wowBarrier.type = wow_64_32; _wowBarrier.mismatch = true; } - } -} - -/* -*/ -Native::~Native() -{ + } } /// @@ -65,7 +59,7 @@ Native::~Native() /// Allocation type /// Memory protection /// Status code -NTSTATUS Native::VirtualAllocExT( ptr_t& lpAddress, size_t dwSize, DWORD flAllocationType, DWORD flProtect ) +NTSTATUS Native::VirtualAllocExT( ptr_t& lpAddress, size_t dwSize, DWORD flAllocationType, DWORD flProtect ) noexcept { lpAddress = reinterpret_cast(VirtualAllocEx( _hProcess, reinterpret_cast(lpAddress), dwSize, flAllocationType, flProtect )); return lpAddress != 0 ? STATUS_SUCCESS : LastNtStatus(); @@ -79,7 +73,7 @@ NTSTATUS Native::VirtualAllocExT( ptr_t& lpAddress, size_t dwSize, DWORD flAlloc /// Region size /// Memory release type. /// Status code -NTSTATUS Native::VirtualFreeExT( ptr_t lpAddress, size_t dwSize, DWORD dwFreeType ) +NTSTATUS Native::VirtualFreeExT( ptr_t lpAddress, size_t dwSize, DWORD dwFreeType ) noexcept { auto r = VirtualFreeEx( _hProcess, reinterpret_cast(lpAddress), dwSize, dwFreeType ); return r != 0 ? STATUS_SUCCESS : LastNtStatus(); @@ -91,13 +85,13 @@ NTSTATUS Native::VirtualFreeExT( ptr_t lpAddress, size_t dwSize, DWORD dwFreeTyp /// Address to query /// Retrieved memory info /// Status code -NTSTATUS Native::VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION64 lpBuffer ) +NTSTATUS Native::VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION64 lpBuffer ) noexcept { auto r = VirtualQueryEx( _hProcess, reinterpret_cast(lpAddress), reinterpret_cast(lpBuffer), sizeof( MEMORY_BASIC_INFORMATION ) - ); + ); return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } @@ -108,14 +102,15 @@ NTSTATUS Native::VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION64 l /// Address to query /// Retrieved memory info /// Status code -NTSTATUS Native::VirtualQueryExT( ptr_t lpAddress, MEMORY_INFORMATION_CLASS infoClass, LPVOID lpBuffer, size_t bufSize ) +NTSTATUS Native::VirtualQueryExT( ptr_t lpAddress, MEMORY_INFORMATION_CLASS infoClass, LPVOID lpBuffer, size_t bufSize ) noexcept { SIZE_T retLen = 0; + SetLastNtStatus( STATUS_SUCCESS ); return SAFE_NATIVE_CALL( NtQueryVirtualMemory, _hProcess, reinterpret_cast(lpAddress), infoClass, lpBuffer, bufSize, &retLen - ); + ); } /// @@ -126,7 +121,7 @@ NTSTATUS Native::VirtualQueryExT( ptr_t lpAddress, MEMORY_INFORMATION_CLASS info /// New protection. /// Old protection /// Status code -NTSTATUS Native::VirtualProtectExT( ptr_t lpAddress, DWORD64 dwSize, DWORD flProtect, DWORD* flOld ) +NTSTATUS Native::VirtualProtectExT( ptr_t lpAddress, DWORD64 dwSize, DWORD flProtect, DWORD* flOld ) noexcept { DWORD junk = 0; if (!flOld) @@ -143,9 +138,9 @@ NTSTATUS Native::VirtualProtectExT( ptr_t lpAddress, DWORD64 dwSize, DWORD flPro /// Memory address /// Output buffer /// Number of bytes to read -/// Mumber of bytes read +/// Number of bytes read /// Status code -NTSTATUS Native::ReadProcessMemoryT( ptr_t lpBaseAddress, LPVOID lpBuffer, size_t nSize, DWORD64 *lpBytes /*= nullptr */ ) +NTSTATUS Native::ReadProcessMemoryT( ptr_t lpBaseAddress, LPVOID lpBuffer, size_t nSize, DWORD64 *lpBytes /*= nullptr */ ) noexcept { auto r = ReadProcessMemory( _hProcess, reinterpret_cast(lpBaseAddress), lpBuffer, nSize, reinterpret_cast(lpBytes) ); return r != 0 ? STATUS_SUCCESS : LastNtStatus(); @@ -157,9 +152,9 @@ NTSTATUS Native::ReadProcessMemoryT( ptr_t lpBaseAddress, LPVOID lpBuffer, size_ /// Memory address /// Buffer to write /// Number of bytes to read -/// Mumber of bytes read +/// Number of bytes read /// Status code -NTSTATUS Native::WriteProcessMemoryT( ptr_t lpBaseAddress, LPCVOID lpBuffer, size_t nSize, DWORD64 *lpBytes /*= nullptr */ ) +NTSTATUS Native::WriteProcessMemoryT( ptr_t lpBaseAddress, LPCVOID lpBuffer, size_t nSize, DWORD64 *lpBytes /*= nullptr */ ) noexcept { auto r = WriteProcessMemory( _hProcess, reinterpret_cast(lpBaseAddress), lpBuffer, nSize, reinterpret_cast(lpBytes) ); return r != 0 ? STATUS_SUCCESS : LastNtStatus(); @@ -172,7 +167,7 @@ NTSTATUS Native::WriteProcessMemoryT( ptr_t lpBaseAddress, LPCVOID lpBuffer, siz /// Output buffer /// Buffer size /// Status code -NTSTATUS Native::QueryProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ) +NTSTATUS Native::QueryProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ) noexcept { ULONG length = 0; return SAFE_NATIVE_CALL( NtQueryInformationProcess, _hProcess, infoClass, lpBuffer, bufSize, &length ); @@ -185,7 +180,7 @@ NTSTATUS Native::QueryProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, /// Input buffer /// Buffer size /// Status code -NTSTATUS Native::SetProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ) +NTSTATUS Native::SetProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ) noexcept { return SAFE_NATIVE_CALL( NtSetInformationProcess, _hProcess, infoClass, lpBuffer, bufSize ); } @@ -199,9 +194,9 @@ NTSTATUS Native::SetProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, u /// Creation flags /// Access override /// Status code -NTSTATUS Native::CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t arg, CreateThreadFlags flags, DWORD access /*= THREAD_ALL_ACCESS*/ ) +NTSTATUS Native::CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t arg, CreateThreadFlags flags, DWORD access /*= THREAD_ALL_ACCESS*/ ) noexcept { - NTSTATUS status = 0; + NTSTATUS status = 0; auto pCreateThread = GET_IMPORT( NtCreateThreadEx ); if (pCreateThread) @@ -211,7 +206,7 @@ NTSTATUS Native::CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t arg, C _hProcess, reinterpret_cast(entry), reinterpret_cast(arg), static_cast(flags), 0, 0x1000, 0x100000, NULL - ); + ); if (!NT_SUCCESS( status )) hThread = NULL; @@ -223,10 +218,10 @@ NTSTATUS Native::CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t arg, C if (flags & CreateSuspended) win32Flags = CREATE_SUSPENDED; - hThread = CreateRemoteThread( + hThread = CreateRemoteThread( _hProcess, NULL, 0, reinterpret_cast(entry), reinterpret_cast(arg), win32Flags, NULL - ); + ); status = hThread != NULL ? STATUS_SUCCESS : LastNtStatus(); } @@ -240,7 +235,7 @@ NTSTATUS Native::CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t arg, C /// Thread handle. /// Thread context /// Status code -NTSTATUS Native::GetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) +NTSTATUS Native::GetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) noexcept { auto r = GetThreadContext(hThread, reinterpret_cast(&ctx)); return r != 0 ? STATUS_SUCCESS : LastNtStatus(); @@ -252,16 +247,16 @@ NTSTATUS Native::GetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) /// Thread handle. /// Thread context /// Status code -NTSTATUS Native::GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) +NTSTATUS Native::GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) noexcept { // Target process is x64. WOW64 CONTEXT is not available. if (_wowBarrier.targetWow64 == false) { - return 0; + return STATUS_NOT_SUPPORTED; } else { - auto r = SAFE_CALL(Wow64GetThreadContext, hThread, reinterpret_cast(&ctx)); + auto r = SAFE_CALL( Wow64GetThreadContext, hThread, reinterpret_cast(&ctx) ); return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } } @@ -272,7 +267,7 @@ NTSTATUS Native::GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) /// Thread handle. /// Thread context /// Status code -NTSTATUS Native::SetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) +NTSTATUS Native::SetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) noexcept { auto r = SetThreadContext(hThread, reinterpret_cast(&ctx)); return r != 0 ? STATUS_SUCCESS : LastNtStatus(); @@ -284,16 +279,17 @@ NTSTATUS Native::SetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) /// Thread handle. /// Thread context /// Status code -NTSTATUS Native::SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) +NTSTATUS Native::SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) noexcept { // Target process is x64. 32bit CONTEXT is not available. if (_wowBarrier.targetWow64 == false) { - return 0; + + return STATUS_NOT_SUPPORTED; } else { - auto r = SAFE_CALL(Wow64SetThreadContext, hThread, reinterpret_cast(&ctx)); + auto r = SAFE_CALL( Wow64SetThreadContext, hThread, reinterpret_cast(&ctx) ); return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } } @@ -305,7 +301,7 @@ NTSTATUS Native::SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) /// APC function /// APC argument /// Status code -NTSTATUS Native::QueueApcT( HANDLE hThread, ptr_t func, ptr_t arg ) +NTSTATUS Native::QueueApcT( HANDLE hThread, ptr_t func, ptr_t arg ) noexcept { if (_wowBarrier.type == wow_64_32) { @@ -321,21 +317,23 @@ NTSTATUS Native::QueueApcT( HANDLE hThread, ptr_t func, ptr_t arg ) /// /// Retrieved PEB /// PEB pointer -ptr_t Native::getPEB( _PEB32* ppeb ) +ptr_t Native::getPEB( _PEB32* ppeb ) noexcept { // Target process is x64. PEB32 is not available. if (_wowBarrier.targetWow64 == false) { + SetLastNtStatus( STATUS_NOT_SUPPORTED ); return 0; } - else - { - ptr_t ptr = 0; - if (NT_SUCCESS( SAFE_NATIVE_CALL( NtQueryInformationProcess, _hProcess, ProcessWow64Information, &ptr, (ULONG)sizeof( ptr ), nullptr ) ) && ppeb) - ReadProcessMemory( _hProcess, reinterpret_cast(ptr), ppeb, sizeof(*ppeb), NULL ); - return ptr; + ptr_t ptr = 0; + if (NT_SUCCESS( SAFE_NATIVE_CALL( NtQueryInformationProcess, _hProcess, ProcessWow64Information, &ptr, ULONG( sizeof( ptr ) ), nullptr ) )) + { + if (!ppeb || ReadProcessMemory( _hProcess, reinterpret_cast(ptr), ppeb, sizeof( _PEB32 ), nullptr )) + return ptr; } + + return 0; } /// @@ -343,15 +341,19 @@ ptr_t Native::getPEB( _PEB32* ppeb ) /// /// Retrieved PEB /// PEB pointer -ptr_t Native::getPEB( _PEB64* ppeb ) +ptr_t Native::getPEB( _PEB64* ppeb ) noexcept { - PROCESS_BASIC_INFORMATION pbi = { 0 }; + PROCESS_BASIC_INFORMATION pbi = { }; ULONG bytes = 0; - if (NT_SUCCESS( SAFE_NATIVE_CALL( NtQueryInformationProcess, _hProcess, ProcessBasicInformation, &pbi, (ULONG)sizeof( pbi ), &bytes ) ) && ppeb) - ReadProcessMemory( _hProcess, pbi.PebBaseAddress, ppeb, sizeof(*ppeb), NULL ); + SAFE_NATIVE_CALL( NtQueryInformationProcess, _hProcess, ProcessBasicInformation, &pbi, ULONG( sizeof( pbi ) ), &bytes ); + if (bytes > 0) + { + if (!ppeb || ReadProcessMemory( _hProcess, pbi.PebBaseAddress, ppeb, sizeof( _PEB32 ), nullptr )) + return reinterpret_cast(pbi.PebBaseAddress); + } - return reinterpret_cast(pbi.PebBaseAddress); + return 0; } /// @@ -359,25 +361,27 @@ ptr_t Native::getPEB( _PEB64* ppeb ) /// /// Retrieved TEB /// TEB pointer -ptr_t Native::getTEB( HANDLE hThread, _TEB32* pteb ) +ptr_t Native::getTEB( HANDLE hThread, _TEB32* pteb ) noexcept { // Target process is x64. TEB32 is not available. if (_wowBarrier.targetWow64 == false) { + SetLastNtStatus( STATUS_NOT_SUPPORTED ); return 0; } - // Retrieving TEB32 from x64 process. - else - { - _THREAD_BASIC_INFORMATION_T tbi = { 0 }; - ULONG bytes = 0; - if (NT_SUCCESS( SAFE_NATIVE_CALL( NtQueryInformationThread, hThread, (THREADINFOCLASS)0, &tbi, (ULONG)sizeof( tbi ), &bytes ) ) && pteb) - ReadProcessMemory( _hProcess, (const uint8_t*)tbi.TebBaseAddress + 0x2000, pteb, sizeof(*pteb), NULL ); + // Retrieving TEB32 from x64 process. + _THREAD_BASIC_INFORMATION_T tbi = { }; + ULONG bytes = 0; - return tbi.TebBaseAddress + 0x2000; + SAFE_NATIVE_CALL( NtQueryInformationThread, hThread, (THREADINFOCLASS)0, &tbi, ULONG( sizeof( tbi ) ), &bytes ); + if (bytes > 0) + { + if (!pteb || ReadProcessMemory( _hProcess, (const uint8_t*)tbi.TebBaseAddress + 0x2000, pteb, sizeof( _TEB32 ), nullptr )) + return tbi.TebBaseAddress + 0x2000; } + return 0; } /// @@ -385,15 +389,19 @@ ptr_t Native::getTEB( HANDLE hThread, _TEB32* pteb ) /// /// Retrieved TEB /// TEB pointer -ptr_t Native::getTEB( HANDLE hThread, _TEB64* pteb ) +ptr_t Native::getTEB( HANDLE hThread, _TEB64* pteb ) noexcept { - _THREAD_BASIC_INFORMATION_T tbi = { 0 }; + _THREAD_BASIC_INFORMATION_T tbi = { }; ULONG bytes = 0; - if (NT_SUCCESS( SAFE_NATIVE_CALL( NtQueryInformationThread, hThread, (THREADINFOCLASS)0, &tbi, (ULONG)sizeof( tbi ), &bytes ) ) && pteb) - ReadProcessMemory( _hProcess, reinterpret_cast(tbi.TebBaseAddress), pteb, sizeof(*pteb), NULL ); + SAFE_NATIVE_CALL( NtQueryInformationThread, hThread, (THREADINFOCLASS)0, &tbi, ULONG( sizeof( tbi ) ), &bytes ); + if (bytes > 0) + { + if (!pteb || ReadProcessMemory( _hProcess, reinterpret_cast(tbi.TebBaseAddress), pteb, sizeof( _TEB64 ), nullptr )) + return tbi.TebBaseAddress; + } - return tbi.TebBaseAddress; + return 0; } /// @@ -401,9 +409,9 @@ ptr_t Native::getTEB( HANDLE hThread, _TEB64* pteb ) /// /// If true - non-allocated regions will be included in list /// Found regions -std::vector Native::EnumRegions( bool includeFree /*= false*/ ) +std::vector Native::EnumRegions( bool includeFree /*= false*/ ) noexcept { - MEMORY_BASIC_INFORMATION64 mbi = { 0 }; + MEMORY_BASIC_INFORMATION64 mbi = { }; std::vector results; for (ptr_t memptr = minAddr(); memptr < maxAddr(); memptr = mbi.BaseAddress + mbi.RegionSize) @@ -429,22 +437,22 @@ std::vector Native::EnumRegions( bool includeFree /* /// Found modules /// Module count template -std::vector Native::EnumModulesT() +std::vector Native::EnumModulesT() noexcept { NTSTATUS status = STATUS_SUCCESS; _PEB_T peb = { }; _PEB_LDR_DATA2_T ldr = { }; std::vector result; - if (getPEB( &peb ) != 0 && ReadProcessMemoryT( peb.Ldr, &ldr, sizeof( ldr ), 0 ) == STATUS_SUCCESS) + if (getPEB( &peb ) != 0 && ReadProcessMemoryT( peb.Ldr, &ldr, sizeof( ldr ) ) == STATUS_SUCCESS) { for (T head = ldr.InLoadOrderModuleList.Flink; NT_SUCCESS( status ) && head != (peb.Ldr + FIELD_OFFSET( _PEB_LDR_DATA2_T, InLoadOrderModuleList )); status = ReadProcessMemoryT( static_cast(head), &head, sizeof( head ) )) { ModuleData data; - wchar_t localPath[512] = { 0 }; - _LDR_DATA_TABLE_ENTRY_BASE_T localdata = { { 0 } }; + wchar_t localPath[512] = { }; + _LDR_DATA_TABLE_ENTRY_BASE_T localdata = { }; ReadProcessMemoryT( head, &localdata, sizeof( localdata ), 0 ); ReadProcessMemoryT( localdata.FullDllName.Buffer, localPath, localdata.FullDllName.Length ); @@ -473,9 +481,9 @@ std::vector Native::EnumModulesT() /// /// Found modules /// Sections count -std::vector Native::EnumSections() +std::vector Native::EnumSections() noexcept { - MEMORY_BASIC_INFORMATION64 mbi = { 0 }; + MEMORY_BASIC_INFORMATION64 mbi = { }; ptr_t lastBase = 0; std::vector result; @@ -492,10 +500,10 @@ std::vector Native::EnumSections() if (mbi.State != MEM_COMMIT || mbi.Type != SEC_IMAGE || lastBase == mbi.AllocationBase) continue; - uint8_t buf[0x1000] = { 0 }; + uint8_t buf[0x1000] = { }; _UNICODE_STRING_T* ustr = (decltype(ustr))(buf + 0x800); - status = VirtualQueryExT( mbi.AllocationBase, MemorySectionName, ustr, sizeof(buf) / 2 ); + status = VirtualQueryExT( mbi.AllocationBase, MemorySectionName, ustr, sizeof( buf ) / 2 ); // Get additional if (NT_SUCCESS( status )) @@ -516,7 +524,7 @@ std::vector Native::EnumSections() if (phdrDos->e_magic != IMAGE_DOS_SIGNATURE || phdrNt32->Signature != IMAGE_NT_SIGNATURE) { // Iterate until region end - MEMORY_BASIC_INFORMATION64 mbi2 = { 0 }; + MEMORY_BASIC_INFORMATION64 mbi2 = { }; for (ptr_t memptr2 = mbi.AllocationBase; memptr2 < maxAddr(); memptr2 = mbi2.BaseAddress + mbi2.RegionSize) if (!NT_SUCCESS( VirtualQueryExT( memptr2, &mbi2 ) ) || mbi2.Type != SEC_IMAGE) { @@ -526,7 +534,7 @@ std::vector Native::EnumSections() data.type = mt_unknown; } - else if( phdrNt32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC ) + else if (phdrNt32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { data.size = phdrNt32->OptionalHeader.SizeOfImage; data.type = mt_mod32; @@ -536,7 +544,7 @@ std::vector Native::EnumSections() data.size = phdrNt64->OptionalHeader.SizeOfImage; data.type = mt_mod64; } - else + else continue; // Hack for x86 OS @@ -567,9 +575,9 @@ std::vector Native::EnumSections() /// /// Found modules /// Sections count -std::vector Native::EnumPEHeaders() +std::vector Native::EnumPEHeaders() noexcept { - MEMORY_BASIC_INFORMATION64 mbi = { 0 }; + MEMORY_BASIC_INFORMATION64 mbi = { }; uint8_t buf[0x1000]; ptr_t lastBase = 0; std::vector result; @@ -624,7 +632,7 @@ std::vector Native::EnumPEHeaders() // Try to get section name _UNICODE_STRING_T* ustr = (decltype(ustr))buf; - status = VirtualQueryExT( mbi.AllocationBase, MemorySectionName, ustr, sizeof(buf) ); + status = VirtualQueryExT( mbi.AllocationBase, MemorySectionName, ustr, sizeof( buf ) ); if (status == STATUS_SUCCESS) { @@ -641,7 +649,7 @@ std::vector Native::EnumPEHeaders() } else { - wchar_t name[64] = { 0 }; + wchar_t name[64] = { }; wsprintfW( name, L"Unknown_0x%I64x", data.baseAddress ); data.fullPath = name; @@ -662,7 +670,7 @@ std::vector Native::EnumPEHeaders() /// Found modules /// Module type: x86 or x64 /// Module count -std::vector Native::EnumModules( eModSeachType search/*= LdrList*/, eModType mtype /*= mt_default */ ) +std::vector Native::EnumModules( eModSeachType search/*= LdrList*/, eModType mtype /*= mt_default */ ) noexcept { if (search == LdrList) { @@ -672,11 +680,11 @@ std::vector Native::EnumModules( eModSeachType search/*= LdrList* return CALL_64_86( mtype == mt_mod64, EnumModulesT ); } - else if(search == Sections) + else if (search == Sections) { return EnumSections(); } - else if(search == PEHeaders) + else if (search == PEHeaders) { return EnumPEHeaders(); } diff --git a/src/BlackBone/Subsystem/NativeSubsystem.h b/src/BlackBone/Subsystem/NativeSubsystem.h index f0493a63..5e8a004d 100644 --- a/src/BlackBone/Subsystem/NativeSubsystem.h +++ b/src/BlackBone/Subsystem/NativeSubsystem.h @@ -27,10 +27,10 @@ ENUM_OPS(CreateThreadFlags) class Native { public: - BLACKBONE_API Native( HANDLE hProcess, bool x86OS = false ); - BLACKBONE_API ~Native(); + BLACKBONE_API Native( HANDLE hProcess, bool x86OS = false ) noexcept; + BLACKBONE_API ~Native() = default; - BLACKBONE_API inline const Wow64Barrier& GetWow64Barrier() const { return _wowBarrier; } + BLACKBONE_API const Wow64Barrier& GetWow64Barrier() const noexcept { return _wowBarrier; } /// /// Allocate virtual memory @@ -40,7 +40,7 @@ class Native /// Allocation type /// Memory protection /// Status code - virtual NTSTATUS VirtualAllocExT( ptr_t& lpAddress, size_t dwSize, DWORD flAllocationType, DWORD flProtect ); + virtual NTSTATUS VirtualAllocExT( ptr_t& lpAddress, size_t dwSize, DWORD flAllocationType, DWORD flProtect ) noexcept; /// /// Free virtual memory @@ -49,7 +49,7 @@ class Native /// Region size /// Memory release type. /// Status code - virtual NTSTATUS VirtualFreeExT( ptr_t lpAddress, size_t dwSize, DWORD dwFreeType ); + virtual NTSTATUS VirtualFreeExT( ptr_t lpAddress, size_t dwSize, DWORD dwFreeType ) noexcept; /// /// Change memory protection @@ -59,7 +59,7 @@ class Native /// New protection. /// Old protection /// Status code - virtual NTSTATUS VirtualProtectExT( ptr_t lpAddress, DWORD64 dwSize, DWORD flProtect, DWORD* flOld ); + virtual NTSTATUS VirtualProtectExT( ptr_t lpAddress, DWORD64 dwSize, DWORD flProtect, DWORD* flOld ) noexcept; /// /// Read virtual memory @@ -67,9 +67,9 @@ class Native /// Memory address /// Output buffer /// Number of bytes to read - /// Mumber of bytes read + /// Number of bytes read /// Status code - virtual NTSTATUS ReadProcessMemoryT( ptr_t lpBaseAddress, LPVOID lpBuffer, size_t nSize, DWORD64 *lpBytes = nullptr ); + virtual NTSTATUS ReadProcessMemoryT( ptr_t lpBaseAddress, LPVOID lpBuffer, size_t nSize, DWORD64 *lpBytes = nullptr ) noexcept; /// /// Write virtual memory @@ -77,9 +77,9 @@ class Native /// Memory address /// Buffer to write /// Number of bytes to read - /// Mumber of bytes read + /// Number of bytes read /// Status code - virtual NTSTATUS WriteProcessMemoryT( ptr_t lpBaseAddress, LPCVOID lpBuffer, size_t nSize, DWORD64 *lpBytes = nullptr ); + virtual NTSTATUS WriteProcessMemoryT( ptr_t lpBaseAddress, LPCVOID lpBuffer, size_t nSize, DWORD64 *lpBytes = nullptr ) noexcept; /// /// Query virtual memory @@ -87,7 +87,7 @@ class Native /// Address to query /// Retrieved memory info /// Status code - virtual NTSTATUS VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION64 lpBuffer ); + virtual NTSTATUS VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION64 lpBuffer ) noexcept; /// /// Query virtual memory @@ -95,7 +95,7 @@ class Native /// Address to query /// Retrieved memory info /// Status code - virtual NTSTATUS VirtualQueryExT( ptr_t lpAddress, MEMORY_INFORMATION_CLASS infoClass, LPVOID lpBuffer, size_t bufSize ); + virtual NTSTATUS VirtualQueryExT( ptr_t lpAddress, MEMORY_INFORMATION_CLASS infoClass, LPVOID lpBuffer, size_t bufSize ) noexcept; /// /// Call NtQueryInformationProcess for underlying process @@ -104,7 +104,7 @@ class Native /// Output buffer /// Buffer size /// Status code - virtual NTSTATUS QueryProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ); + virtual NTSTATUS QueryProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ) noexcept; /// /// Call NtSetInformationProcess for underlying process @@ -113,7 +113,7 @@ class Native /// Input buffer /// Buffer size /// Status code - virtual NTSTATUS SetProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ); + virtual NTSTATUS SetProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ) noexcept; /// /// Creates new thread in the remote process @@ -124,7 +124,7 @@ class Native /// Creation flags /// Access override /// Status code - virtual NTSTATUS CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t arg, CreateThreadFlags flags, DWORD access = THREAD_ALL_ACCESS ); + virtual NTSTATUS CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t arg, CreateThreadFlags flags, DWORD access = THREAD_ALL_ACCESS ) noexcept; /// /// Get native thread context @@ -132,7 +132,7 @@ class Native /// Thread handle. /// Thread context /// Status code - virtual NTSTATUS GetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ); + virtual NTSTATUS GetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) noexcept; /// /// Get WOW64 thread context @@ -140,7 +140,7 @@ class Native /// Thread handle. /// Thread context /// Status code - virtual NTSTATUS GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ); + virtual NTSTATUS GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) noexcept; /// /// Set native thread context @@ -148,7 +148,7 @@ class Native /// Thread handle. /// Thread context /// Status code - virtual NTSTATUS SetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ); + virtual NTSTATUS SetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) noexcept; /// /// Set WOW64 thread context @@ -156,7 +156,7 @@ class Native /// Thread handle. /// Thread context /// Status code - virtual NTSTATUS SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ); + virtual NTSTATUS SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) noexcept; /// /// NtQueueApcThread @@ -165,42 +165,42 @@ class Native /// APC function /// APC argument /// Status code - virtual NTSTATUS QueueApcT( HANDLE hThread, ptr_t func, ptr_t arg ); + virtual NTSTATUS QueueApcT( HANDLE hThread, ptr_t func, ptr_t arg ) noexcept; /// /// Get WOW64 PEB /// /// Retrieved PEB /// PEB pointer - virtual ptr_t getPEB( _PEB32* ppeb ); + virtual ptr_t getPEB( _PEB32* ppeb ) noexcept; /// /// Get native PEB /// /// Retrieved PEB /// PEB pointer - virtual ptr_t getPEB( _PEB64* ppeb ); + virtual ptr_t getPEB( _PEB64* ppeb ) noexcept; /// /// Get WOW64 TEB /// /// Retrieved TEB /// TEB pointer - virtual ptr_t getTEB( HANDLE hThread, _TEB32* pteb ); + virtual ptr_t getTEB( HANDLE hThread, _TEB32* pteb ) noexcept; /// /// Get native TEB /// /// Retrieved TEB /// TEB pointer - virtual ptr_t getTEB( HANDLE hThread, _TEB64* pteb ); + virtual ptr_t getTEB( HANDLE hThread, _TEB64* pteb ) noexcept; /// /// Enumerate valid memory regions /// /// If true - non-allocated regions will be included in list /// Found regions> - BLACKBONE_API std::vector EnumRegions( bool includeFree = false ); + BLACKBONE_API std::vector EnumRegions( bool includeFree = false ) noexcept; /// /// Enumerate process modules @@ -208,20 +208,20 @@ class Native /// Found modules /// Module type: x86 or x64 /// Module count - BLACKBONE_API std::vector EnumModules( eModSeachType search = LdrList, eModType mtype = mt_default ); + BLACKBONE_API std::vector EnumModules( eModSeachType search = LdrList, eModType mtype = mt_default ) noexcept; /// /// Get lowest possible valid address value /// /// Address value - BLACKBONE_API inline ptr_t minAddr() const { return 0x10000; } + BLACKBONE_API ptr_t minAddr() const noexcept { return 0x10000; } /// /// Get highest possible valid address value /// /// Address value - BLACKBONE_API inline ptr_t maxAddr() const { return 0x7FFFFFFEFFFF; } - + BLACKBONE_API ptr_t maxAddr() const noexcept { return 0x7FFFFFFEFFFF; } + /// /// Get highest possible valid address value /// @@ -232,7 +232,8 @@ class Native /// Get page size /// /// Address value - BLACKBONE_API inline uint32_t pageSize() const { return _pageSize; } + BLACKBONE_API uint32_t pageSize() const noexcept { return _pageSize; } + private: /// @@ -241,21 +242,21 @@ class Native /// Found modules /// Module count template - std::vector EnumModulesT(); + std::vector EnumModulesT() noexcept; /// /// Enum process section objects /// /// Found modules /// Sections count - std::vector EnumSections(); + std::vector EnumSections() noexcept; /// /// Enum pages containing valid PE headers /// /// Found modules /// Sections count - std::vector EnumPEHeaders(); + std::vector EnumPEHeaders() noexcept; protected: HANDLE _hProcess; // Process handle diff --git a/src/BlackBone/Subsystem/Wow64Subsystem.cpp b/src/BlackBone/Subsystem/Wow64Subsystem.cpp index 6a67abd4..0df968ab 100644 --- a/src/BlackBone/Subsystem/Wow64Subsystem.cpp +++ b/src/BlackBone/Subsystem/Wow64Subsystem.cpp @@ -7,15 +7,11 @@ namespace blackbone { -NativeWow64::NativeWow64( HANDLE hProcess ) +NativeWow64::NativeWow64( HANDLE hProcess ) noexcept : Native( hProcess ) { } -NativeWow64::~NativeWow64() -{ -} - /// /// Allocate virtual memory /// @@ -24,7 +20,7 @@ NativeWow64::~NativeWow64() /// Allocation type /// Memory protection /// Status code -NTSTATUS NativeWow64::VirtualAllocExT( ptr_t& lpAddress, size_t dwSize, DWORD flAllocationType, DWORD flProtect ) +NTSTATUS NativeWow64::VirtualAllocExT( ptr_t& lpAddress, size_t dwSize, DWORD flAllocationType, DWORD flProtect ) noexcept { DWORD64 size64 = dwSize; static ptr_t ntavm = GetProcAddress64( getNTDLL64(), "NtAllocateVirtualMemory" ); @@ -41,7 +37,7 @@ NTSTATUS NativeWow64::VirtualAllocExT( ptr_t& lpAddress, size_t dwSize, DWORD fl /// Region size /// Memory release type. /// Status code -NTSTATUS NativeWow64::VirtualFreeExT( ptr_t lpAddress, size_t dwSize, DWORD dwFreeType ) +NTSTATUS NativeWow64::VirtualFreeExT( ptr_t lpAddress, size_t dwSize, DWORD dwFreeType ) noexcept { static ptr_t ntfvm = GetProcAddress64( getNTDLL64(), "NtFreeVirtualMemory" ); if (ntfvm == 0) @@ -59,7 +55,7 @@ NTSTATUS NativeWow64::VirtualFreeExT( ptr_t lpAddress, size_t dwSize, DWORD dwFr /// Address to query /// Retrieved memory info /// Status code -NTSTATUS NativeWow64::VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION64 lpBuffer ) +NTSTATUS NativeWow64::VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION64 lpBuffer ) noexcept { static ptr_t ntqvm = GetProcAddress64( getNTDLL64(), "NtQueryVirtualMemory" ); if (ntqvm == 0) @@ -74,7 +70,7 @@ NTSTATUS NativeWow64::VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATIO /// Address to query /// Retrieved memory info /// Status code -NTSTATUS NativeWow64::VirtualQueryExT( ptr_t lpAddress, MEMORY_INFORMATION_CLASS infoClass, LPVOID lpBuffer, size_t bufSize ) +NTSTATUS NativeWow64::VirtualQueryExT( ptr_t lpAddress, MEMORY_INFORMATION_CLASS infoClass, LPVOID lpBuffer, size_t bufSize ) noexcept { static ptr_t ntqvm = GetProcAddress64( getNTDLL64(), "NtQueryVirtualMemory" ); if (ntqvm == 0) @@ -91,7 +87,7 @@ NTSTATUS NativeWow64::VirtualQueryExT( ptr_t lpAddress, MEMORY_INFORMATION_CLASS /// New protection. /// Old protection /// Status code -NTSTATUS NativeWow64::VirtualProtectExT( ptr_t lpAddress, DWORD64 dwSize, DWORD flProtect, DWORD* flOld ) +NTSTATUS NativeWow64::VirtualProtectExT( ptr_t lpAddress, DWORD64 dwSize, DWORD flProtect, DWORD* flOld ) noexcept { static ptr_t ntpvm = GetProcAddress64( getNTDLL64(), "NtProtectVirtualMemory" ); if (ntpvm == 0) @@ -106,9 +102,9 @@ NTSTATUS NativeWow64::VirtualProtectExT( ptr_t lpAddress, DWORD64 dwSize, DWORD /// Memory address /// Output buffer /// Number of bytes to read -/// Mumber of bytes read +/// Number of bytes read /// Status code -NTSTATUS NativeWow64::ReadProcessMemoryT( ptr_t lpBaseAddress, LPVOID lpBuffer, size_t nSize, DWORD64 *lpBytes /*= nullptr */ ) +NTSTATUS NativeWow64::ReadProcessMemoryT( ptr_t lpBaseAddress, LPVOID lpBuffer, size_t nSize, DWORD64 *lpBytes /*= nullptr */ ) noexcept { DWORD64 junk = 0; if (lpBytes == nullptr) @@ -129,9 +125,9 @@ NTSTATUS NativeWow64::ReadProcessMemoryT( ptr_t lpBaseAddress, LPVOID lpBuffer, /// Memory address /// Buffer to write /// Number of bytes to read -/// Mumber of bytes read +/// Number of bytes read /// Status code -NTSTATUS NativeWow64::WriteProcessMemoryT( ptr_t lpBaseAddress, LPCVOID lpBuffer, size_t nSize, DWORD64 *lpBytes /*= nullptr */ ) +NTSTATUS NativeWow64::WriteProcessMemoryT( ptr_t lpBaseAddress, LPCVOID lpBuffer, size_t nSize, DWORD64 *lpBytes /*= nullptr */ ) noexcept { DWORD64 junk = 0; if (lpBytes == nullptr) @@ -153,7 +149,7 @@ NTSTATUS NativeWow64::WriteProcessMemoryT( ptr_t lpBaseAddress, LPCVOID lpBuffer /// Output buffer /// Buffer size /// Status code -NTSTATUS NativeWow64::QueryProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ) +NTSTATUS NativeWow64::QueryProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ) noexcept { ULONG length = 0; @@ -171,7 +167,7 @@ NTSTATUS NativeWow64::QueryProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBu /// Input buffer /// Buffer size /// Status code -NTSTATUS NativeWow64::SetProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ) +NTSTATUS NativeWow64::SetProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ) noexcept { static ptr_t ntspi = GetProcAddress64( getNTDLL64(), "NtSetInformationProcess" ); if (ntspi == 0) @@ -188,7 +184,7 @@ NTSTATUS NativeWow64::SetProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuff /// Thread argument /// Creation flags /// Status code*/ -NTSTATUS NativeWow64::CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t arg, CreateThreadFlags flags, DWORD access ) +NTSTATUS NativeWow64::CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t arg, CreateThreadFlags flags, DWORD access ) noexcept { // Try to use default routine if possible /*if(_wowBarrier.targetWow64 == true) @@ -208,7 +204,7 @@ NTSTATUS NativeWow64::CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t a NtCreateThreadEx, 11, (DWORD64)&hThd2, (DWORD64)access, 0ull, (DWORD64)_hProcess, (DWORD64)entry, (DWORD64)arg, (DWORD64)flags, 0ull, 0x1000ull, 0x100000ull, 0ull - )); + )); hThread = reinterpret_cast(hThd2); return status; @@ -222,7 +218,7 @@ NTSTATUS NativeWow64::CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t a /// Thread handle. /// Thread context /// Status code -NTSTATUS NativeWow64::GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) +NTSTATUS NativeWow64::GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) noexcept { // Target process is x64. 32bit CONTEXT is not available. if (_wowBarrier.targetWow64 == false) @@ -243,7 +239,7 @@ NTSTATUS NativeWow64::GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) /// Thread handle. /// Thread context /// Status code -NTSTATUS NativeWow64::GetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) +NTSTATUS NativeWow64::GetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) noexcept { static ptr_t gtc = GetProcAddress64( getNTDLL64(), "NtGetContextThread" ); if (gtc == 0) @@ -258,7 +254,7 @@ NTSTATUS NativeWow64::GetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) /// Thread handle. /// Thread context /// Status code -NTSTATUS NativeWow64::SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) +NTSTATUS NativeWow64::SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) noexcept { // Target process is x64. 32bit CONTEXT is not available. if (_wowBarrier.targetWow64 == false) @@ -278,7 +274,7 @@ NTSTATUS NativeWow64::SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) /// Thread handle. /// Thread context /// Status code -NTSTATUS NativeWow64::SetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) +NTSTATUS NativeWow64::SetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) noexcept { static ptr_t stc = GetProcAddress64( getNTDLL64(), "NtSetContextThread" ); if (stc == 0) @@ -294,7 +290,7 @@ NTSTATUS NativeWow64::SetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) /// APC function /// APC argument /// Status code -NTSTATUS NativeWow64::QueueApcT( HANDLE hThread, ptr_t func, ptr_t arg ) +NTSTATUS NativeWow64::QueueApcT( HANDLE hThread, ptr_t func, ptr_t arg ) noexcept { if (_wowBarrier.targetWow64) return Native::QueueApcT( hThread, func, arg ); @@ -311,23 +307,26 @@ NTSTATUS NativeWow64::QueueApcT( HANDLE hThread, ptr_t func, ptr_t arg ) /// /// Retrieved PEB /// PEB pointer -ptr_t NativeWow64::getPEB( _PEB32* ppeb ) +ptr_t NativeWow64::getPEB( _PEB32* ppeb ) noexcept { // Target process is x64. PEB32 is not available. if (_wowBarrier.targetWow64 == false) { + SetLastNtStatus( STATUS_NOT_SUPPORTED ); return 0; } - else - { - PROCESS_BASIC_INFORMATION pbi = { 0 }; - ULONG bytes = 0; - if (NT_SUCCESS( SAFE_NATIVE_CALL( NtQueryInformationProcess, _hProcess, ProcessBasicInformation, &pbi, (ULONG)sizeof( pbi ), &bytes ) ) && ppeb) - ReadProcessMemory( _hProcess, pbi.PebBaseAddress, ppeb, sizeof(_PEB32), NULL ); + PROCESS_BASIC_INFORMATION pbi = { }; + ULONG bytes = 0; - return reinterpret_cast(pbi.PebBaseAddress); + SAFE_NATIVE_CALL( NtQueryInformationProcess, _hProcess, ProcessBasicInformation, &pbi, (ULONG)sizeof( pbi ), &bytes ); + if (bytes > 0) + { + if (!ppeb || ReadProcessMemory( _hProcess, pbi.PebBaseAddress, ppeb, sizeof( _PEB32 ), nullptr )) + return reinterpret_cast(pbi.PebBaseAddress); } + + return 0; } /// @@ -335,14 +334,17 @@ ptr_t NativeWow64::getPEB( _PEB32* ppeb ) /// /// Retrieved PEB /// PEB pointer -ptr_t NativeWow64::getPEB( _PEB64* ppeb ) +ptr_t NativeWow64::getPEB( _PEB64* ppeb ) noexcept { - _PROCESS_BASIC_INFORMATION_T info = { 0 }; + _PROCESS_BASIC_INFORMATION_T info = { }; ULONG bytes = 0; - SAFE_NATIVE_CALL( NtWow64QueryInformationProcess64, _hProcess, ProcessBasicInformation, &info, (ULONG)sizeof( info ), &bytes ); - if (bytes > 0 && NT_SUCCESS( SAFE_NATIVE_CALL( NtWow64ReadVirtualMemory64, _hProcess, info.PebBaseAddress, ppeb, (ULONG)sizeof( _PEB64 ), nullptr ) )) - return info.PebBaseAddress; + SAFE_NATIVE_CALL( NtWow64QueryInformationProcess64, _hProcess, ProcessBasicInformation, &info, ULONG( sizeof( info ) ), &bytes ); + if (bytes > 0) + { + if (!ppeb || NT_SUCCESS( SAFE_NATIVE_CALL( NtWow64ReadVirtualMemory64, _hProcess, info.PebBaseAddress, ppeb, ULONG( sizeof( _PEB64 ) ), nullptr ) )) + return info.PebBaseAddress; + } return 0; } @@ -352,23 +354,26 @@ ptr_t NativeWow64::getPEB( _PEB64* ppeb ) /// /// Retrieved TEB /// TEB pointer -ptr_t NativeWow64::getTEB( HANDLE hThread, _TEB32* pteb ) +ptr_t NativeWow64::getTEB( HANDLE hThread, _TEB32* pteb ) noexcept { // Target process is x64. TEB32 is not available. if (_wowBarrier.targetWow64 == false) { + SetLastNtStatus( STATUS_NOT_SUPPORTED ); return 0; } - else - { - _THREAD_BASIC_INFORMATION_T tbi = { 0 }; - ULONG bytes = 0; - - if (NT_SUCCESS( SAFE_NATIVE_CALL( NtQueryInformationThread, hThread, (THREADINFOCLASS)0, &tbi, (ULONG)sizeof( tbi ), &bytes ) ) && pteb) - ReadProcessMemory( _hProcess, (LPCVOID)((uintptr_t)tbi.TebBaseAddress), pteb, sizeof( _TEB32 ), nullptr ); - return static_cast(tbi.TebBaseAddress); + _THREAD_BASIC_INFORMATION_T tbi = { }; + ULONG bytes = 0; + + SAFE_NATIVE_CALL( NtQueryInformationThread, hThread, (THREADINFOCLASS)0, &tbi, ULONG( sizeof( tbi ) ), &bytes ); + if (bytes > 0) + { + if (!pteb || ReadProcessMemory( _hProcess, (LPCVOID)((uintptr_t)tbi.TebBaseAddress), pteb, sizeof( _TEB32 ), nullptr )) + return tbi.TebBaseAddress; } + + return 0; } /// @@ -376,9 +381,9 @@ ptr_t NativeWow64::getTEB( HANDLE hThread, _TEB32* pteb ) /// /// Retrieved TEB /// TEB pointer -ptr_t NativeWow64::getTEB( HANDLE hThread, _TEB64* pteb ) +ptr_t NativeWow64::getTEB( HANDLE hThread, _TEB64* pteb ) noexcept { - _THREAD_BASIC_INFORMATION_T info = { 0 }; + _THREAD_BASIC_INFORMATION_T info = { }; ULONG bytes = 0; static ptr_t ntQit = GetProcAddress64( getNTDLL64(), "NtQueryInformationThread" ); @@ -389,10 +394,13 @@ ptr_t NativeWow64::getTEB( HANDLE hThread, _TEB64* pteb ) return 0; } - X64Call( ntQit, 5, (DWORD64)hThread, 0ull, (DWORD64)&info, (DWORD64)sizeof(info), (DWORD64)&bytes ); + X64Call( ntQit, 5, (DWORD64)hThread, 0ull, (DWORD64)&info, (DWORD64)sizeof( info ), (DWORD64)&bytes ); - if (bytes > 0 && NT_SUCCESS( SAFE_NATIVE_CALL( NtWow64ReadVirtualMemory64, _hProcess, info.TebBaseAddress, pteb, sizeof( _TEB64 ), nullptr ) )) - return static_cast(info.TebBaseAddress); + if (bytes > 0) + { + if (!pteb || NT_SUCCESS( SAFE_NATIVE_CALL( NtWow64ReadVirtualMemory64, _hProcess, info.TebBaseAddress, pteb, sizeof( _TEB64 ), nullptr ) )) + return info.TebBaseAddress; + } return 0; } diff --git a/src/BlackBone/Subsystem/Wow64Subsystem.h b/src/BlackBone/Subsystem/Wow64Subsystem.h index b1e23c69..a6de3d69 100644 --- a/src/BlackBone/Subsystem/Wow64Subsystem.h +++ b/src/BlackBone/Subsystem/Wow64Subsystem.h @@ -8,8 +8,8 @@ namespace blackbone class NativeWow64 : public Native { public: - BLACKBONE_API NativeWow64( HANDLE hProcess ); - BLACKBONE_API ~NativeWow64(); + BLACKBONE_API NativeWow64( HANDLE hProcess ) noexcept; + BLACKBONE_API ~NativeWow64() = default; /// /// Allocate virtual memory @@ -19,7 +19,7 @@ class NativeWow64 : public Native /// Allocation type /// Memory protection /// Status code - virtual NTSTATUS VirtualAllocExT( ptr_t& lpAddress, size_t dwSize, DWORD flAllocationType, DWORD flProtect ); + virtual NTSTATUS VirtualAllocExT( ptr_t& lpAddress, size_t dwSize, DWORD flAllocationType, DWORD flProtect ) noexcept; /// /// Free virtual memory @@ -28,7 +28,7 @@ class NativeWow64 : public Native /// Region size /// Memory release type. /// Status code - virtual NTSTATUS VirtualFreeExT( ptr_t lpAddress, size_t dwSize, DWORD dwFreeType ); + virtual NTSTATUS VirtualFreeExT( ptr_t lpAddress, size_t dwSize, DWORD dwFreeType ) noexcept; /// /// Change memory protection @@ -38,7 +38,7 @@ class NativeWow64 : public Native /// New protection. /// Old protection /// Status code - virtual NTSTATUS VirtualProtectExT( ptr_t lpAddress, DWORD64 dwSize, DWORD flProtect, DWORD* flOld ); + virtual NTSTATUS VirtualProtectExT( ptr_t lpAddress, DWORD64 dwSize, DWORD flProtect, DWORD* flOld ) noexcept; /// /// Read virtual memory @@ -46,9 +46,9 @@ class NativeWow64 : public Native /// Memory address /// Output buffer /// Number of bytes to read - /// Mumber of bytes read + /// Number of bytes read /// Status code - virtual NTSTATUS ReadProcessMemoryT( ptr_t lpBaseAddress, LPVOID lpBuffer, size_t nSize, DWORD64 *lpBytes = nullptr ); + virtual NTSTATUS ReadProcessMemoryT( ptr_t lpBaseAddress, LPVOID lpBuffer, size_t nSize, DWORD64 *lpBytes = nullptr ) noexcept; /// /// Write virtual memory @@ -56,9 +56,9 @@ class NativeWow64 : public Native /// Memory address /// Buffer to write /// Number of bytes to read - /// Mumber of bytes read + /// Number of bytes read /// Status code - virtual NTSTATUS WriteProcessMemoryT( ptr_t lpBaseAddress, LPCVOID lpBuffer, size_t nSize, DWORD64 *lpBytes = nullptr ); + virtual NTSTATUS WriteProcessMemoryT( ptr_t lpBaseAddress, LPCVOID lpBuffer, size_t nSize, DWORD64 *lpBytes = nullptr ) noexcept; /// /// Query virtual memory @@ -66,7 +66,7 @@ class NativeWow64 : public Native /// Address to query /// Retrieved memory info /// Status code - virtual NTSTATUS VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION64 lpBuffer ); + virtual NTSTATUS VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION64 lpBuffer ) noexcept; /// /// Query virtual memory @@ -74,7 +74,7 @@ class NativeWow64 : public Native /// Address to query /// Retrieved memory info /// Status code - virtual NTSTATUS VirtualQueryExT( ptr_t lpAddress, MEMORY_INFORMATION_CLASS infoClass, LPVOID lpBuffer, size_t bufSize ); + virtual NTSTATUS VirtualQueryExT( ptr_t lpAddress, MEMORY_INFORMATION_CLASS infoClass, LPVOID lpBuffer, size_t bufSize ) noexcept; /// /// Call NtQueryInformationProcess for underlying process @@ -83,7 +83,7 @@ class NativeWow64 : public Native /// Output buffer /// Buffer size /// Status code - virtual NTSTATUS QueryProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ); + virtual NTSTATUS QueryProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ) noexcept; /// /// Call NtSetInformationProcess for underlying process @@ -92,7 +92,7 @@ class NativeWow64 : public Native /// Input buffer /// Buffer size /// Status code - virtual NTSTATUS SetProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ); + virtual NTSTATUS SetProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ) noexcept; /// /// Creates new thread in the remote process @@ -102,7 +102,7 @@ class NativeWow64 : public Native /// Thread argument /// Creation flags /// Status code - virtual NTSTATUS CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t arg, CreateThreadFlags flags, DWORD access = THREAD_ALL_ACCESS ); + virtual NTSTATUS CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t arg, CreateThreadFlags flags, DWORD access = THREAD_ALL_ACCESS ) noexcept; /// /// Get native thread context @@ -110,7 +110,7 @@ class NativeWow64 : public Native /// Thread handle. /// Thread context /// Status code - virtual NTSTATUS GetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ); + virtual NTSTATUS GetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) noexcept; /// /// Get WOW64 thread context @@ -118,7 +118,7 @@ class NativeWow64 : public Native /// Thread handle. /// Thread context /// Status code - virtual NTSTATUS GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ); + virtual NTSTATUS GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) noexcept; /// /// Set native thread context @@ -126,7 +126,7 @@ class NativeWow64 : public Native /// Thread handle. /// Thread context /// Status code - virtual NTSTATUS SetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ); + virtual NTSTATUS SetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) noexcept; /// /// Set WOW64 thread context @@ -134,7 +134,7 @@ class NativeWow64 : public Native /// Thread handle. /// Thread context /// Status code - virtual NTSTATUS SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ); + virtual NTSTATUS SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) noexcept; /// /// NtQueueApcThread @@ -143,35 +143,35 @@ class NativeWow64 : public Native /// APC function /// APC argument /// Status code - virtual NTSTATUS QueueApcT( HANDLE hThread, ptr_t func, ptr_t arg ); + virtual NTSTATUS QueueApcT( HANDLE hThread, ptr_t func, ptr_t arg ) noexcept; /// /// Get WOW64 PEB /// /// Retrieved PEB /// PEB pointer - virtual ptr_t getPEB( _PEB32* ppeb ); + virtual ptr_t getPEB( _PEB32* ppeb ) noexcept; /// /// Get native PEB /// /// Retrieved PEB /// PEB pointer - virtual ptr_t getPEB( _PEB64* ppeb ); + virtual ptr_t getPEB( _PEB64* ppeb ) noexcept; /// /// Get WOW64 TEB /// /// Retrieved TEB /// TEB pointer - virtual ptr_t getTEB( HANDLE hThread, _TEB32* pteb ); + virtual ptr_t getTEB( HANDLE hThread, _TEB32* pteb ) noexcept; /// /// Get native TEB /// /// Retrieved TEB /// TEB pointer - virtual ptr_t getTEB( HANDLE hThread, _TEB64* pteb ); + virtual ptr_t getTEB( HANDLE hThread, _TEB64* pteb ) noexcept; }; } \ No newline at end of file diff --git a/src/BlackBone/Subsystem/x86Subsystem.cpp b/src/BlackBone/Subsystem/x86Subsystem.cpp index fa7acaa6..1bf52c0c 100644 --- a/src/BlackBone/Subsystem/x86Subsystem.cpp +++ b/src/BlackBone/Subsystem/x86Subsystem.cpp @@ -5,30 +5,27 @@ namespace blackbone { -x86Native::x86Native( HANDLE hProcess ) +x86Native::x86Native( HANDLE hProcess ) noexcept : Native( hProcess, true ) { } -x86Native::~x86Native() -{ -} - /// /// Query virtual memory /// /// Address to query /// Retrieved memory info /// Status code -NTSTATUS x86Native::VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION64 lpBuffer ) +NTSTATUS x86Native::VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION64 lpBuffer ) noexcept { - MEMORY_BASIC_INFORMATION tmp = { 0 }; + MEMORY_BASIC_INFORMATION tmp = { }; NTSTATUS status = SAFE_NATIVE_CALL( NtQueryVirtualMemory, _hProcess, reinterpret_cast(lpAddress), MemoryBasicInformation, &tmp, sizeof( tmp ), nullptr ); + if (status != STATUS_SUCCESS) return status; @@ -50,7 +47,7 @@ NTSTATUS x86Native::VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION6 /// Thread handle. /// Thread context /// Status code -NTSTATUS x86Native::GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) +NTSTATUS x86Native::GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) noexcept { auto r = GetThreadContext(hThread, reinterpret_cast(&ctx)); return r != 0 ? STATUS_SUCCESS : LastNtStatus(); @@ -62,7 +59,7 @@ NTSTATUS x86Native::GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) /// Thread handle. /// Thread context /// Status code -NTSTATUS x86Native::GetThreadContextT( HANDLE /*hThread*/, _CONTEXT64& /*ctx*/ ) +NTSTATUS x86Native::GetThreadContextT( HANDLE /*hThread*/, _CONTEXT64& /*ctx*/ ) noexcept { // There is no x64 context under x86 OS return STATUS_NOT_SUPPORTED; @@ -74,7 +71,7 @@ NTSTATUS x86Native::GetThreadContextT( HANDLE /*hThread*/, _CONTEXT64& /*ctx*/ ) /// Thread handle. /// Thread context /// Status code -NTSTATUS x86Native::SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) +NTSTATUS x86Native::SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) noexcept { auto r = SetThreadContext(hThread, reinterpret_cast(&ctx)); return r != 0 ? STATUS_SUCCESS : LastNtStatus(); @@ -86,7 +83,7 @@ NTSTATUS x86Native::SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) /// Thread handle. /// Thread context /// Status code -NTSTATUS x86Native::SetThreadContextT( HANDLE /*hThread*/, _CONTEXT64& /*ctx*/ ) +NTSTATUS x86Native::SetThreadContextT( HANDLE /*hThread*/, _CONTEXT64& /*ctx*/ ) noexcept { // There is no x64 context under x86 OS return STATUS_NOT_SUPPORTED; @@ -97,15 +94,19 @@ NTSTATUS x86Native::SetThreadContextT( HANDLE /*hThread*/, _CONTEXT64& /*ctx*/ ) /// /// Retrieved PEB /// PEB pointer -ptr_t x86Native::getPEB( _PEB32* ppeb ) +ptr_t x86Native::getPEB( _PEB32* ppeb ) noexcept { - PROCESS_BASIC_INFORMATION pbi = { 0 }; + PROCESS_BASIC_INFORMATION pbi = { }; ULONG bytes = 0; - if (NT_SUCCESS( SAFE_NATIVE_CALL( NtQueryInformationProcess, _hProcess, ProcessBasicInformation, &pbi, (ULONG)sizeof( pbi ), &bytes ) ) && ppeb) - ReadProcessMemory( _hProcess, pbi.PebBaseAddress, ppeb, sizeof(_PEB32), NULL ); + SAFE_NATIVE_CALL( NtQueryInformationProcess, _hProcess, ProcessBasicInformation, &pbi, ULONG( sizeof( pbi ) ), &bytes ); + if (bytes > 0) + { + if(!ppeb || ReadProcessMemory( _hProcess, pbi.PebBaseAddress, ppeb, sizeof( _PEB32 ), nullptr )) + return reinterpret_cast(pbi.PebBaseAddress); + } - return reinterpret_cast(pbi.PebBaseAddress); + return 0; } /// @@ -113,7 +114,7 @@ ptr_t x86Native::getPEB( _PEB32* ppeb ) /// /// Retrieved PEB /// PEB pointer -ptr_t x86Native::getPEB( _PEB64* /*ppeb*/ ) +ptr_t x86Native::getPEB( _PEB64* /*ppeb*/ ) noexcept { // There is no x64 PEB under x86 OS SetLastNtStatus( STATUS_NOT_SUPPORTED ); @@ -125,15 +126,19 @@ ptr_t x86Native::getPEB( _PEB64* /*ppeb*/ ) /// /// Retrieved TEB /// TEB pointer -ptr_t x86Native::getTEB( HANDLE hThread, _TEB32* pteb ) +ptr_t x86Native::getTEB( HANDLE hThread, _TEB32* pteb ) noexcept { - _THREAD_BASIC_INFORMATION_T tbi = { 0 }; + _THREAD_BASIC_INFORMATION_T tbi = { }; ULONG bytes = 0; - if (NT_SUCCESS( SAFE_NATIVE_CALL( NtQueryInformationThread, hThread, (THREADINFOCLASS)0, &tbi, (ULONG)sizeof( tbi ), &bytes ) ) && pteb) - ReadProcessMemory( _hProcess, (LPCVOID)((uintptr_t)tbi.TebBaseAddress), pteb, sizeof(_TEB32), NULL ); + SAFE_NATIVE_CALL( NtQueryInformationThread, hThread, (THREADINFOCLASS)0, &tbi, (ULONG)sizeof( tbi ), &bytes ); + if (bytes > 0) + { + if (!pteb || ReadProcessMemory( _hProcess, (LPCVOID)((uintptr_t)tbi.TebBaseAddress), pteb, sizeof( _TEB32 ), nullptr )) + return tbi.TebBaseAddress; + } - return tbi.TebBaseAddress; + return 0; } /// @@ -141,7 +146,7 @@ ptr_t x86Native::getTEB( HANDLE hThread, _TEB32* pteb ) /// /// Retrieved TEB /// TEB pointer -ptr_t x86Native::getTEB( HANDLE /*hThread*/, _TEB64* /*pteb*/ ) +ptr_t x86Native::getTEB( HANDLE /*hThread*/, _TEB64* /*pteb*/ ) noexcept { // There is no x64 TEB under x86 OS SetLastNtStatus( STATUS_NOT_SUPPORTED ); diff --git a/src/BlackBone/Subsystem/x86Subsystem.h b/src/BlackBone/Subsystem/x86Subsystem.h index c5ae57be..d850ea30 100644 --- a/src/BlackBone/Subsystem/x86Subsystem.h +++ b/src/BlackBone/Subsystem/x86Subsystem.h @@ -11,8 +11,8 @@ namespace blackbone class x86Native : public Native { public: - BLACKBONE_API x86Native( HANDLE hProcess ); - BLACKBONE_API ~x86Native(); + BLACKBONE_API x86Native( HANDLE hProcess ) noexcept; + BLACKBONE_API ~x86Native() = default; /// /// Query virtual memory @@ -20,7 +20,7 @@ class x86Native : public Native /// Address to query /// Retrieved memory info /// Status code - virtual NTSTATUS VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION64 lpBuffer ); + virtual NTSTATUS VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION64 lpBuffer ) noexcept; /// /// Get WOW64 thread context @@ -28,7 +28,7 @@ class x86Native : public Native /// Thread handle. /// Thread context /// Status code - virtual NTSTATUS GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ); + virtual NTSTATUS GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) noexcept; /// /// Get native thread context @@ -36,7 +36,7 @@ class x86Native : public Native /// Thread handle. /// Thread context /// Status code - virtual NTSTATUS GetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ); + virtual NTSTATUS GetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) noexcept; /// /// Set WOW64 thread context @@ -44,7 +44,7 @@ class x86Native : public Native /// Thread handle. /// Thread context /// Status code - virtual NTSTATUS SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ); + virtual NTSTATUS SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) noexcept; /// /// Set native thread context @@ -52,37 +52,35 @@ class x86Native : public Native /// Thread handle. /// Thread context /// Status code - virtual NTSTATUS SetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ); + virtual NTSTATUS SetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) noexcept; /// /// Gets WOW64 PEB /// /// Retrieved PEB /// PEB pointer - virtual ptr_t getPEB( _PEB32* ppeb ); + virtual ptr_t getPEB( _PEB32* ppeb ) noexcept; /// /// Get native PEB /// /// Retrieved PEB /// PEB pointer - virtual ptr_t getPEB( _PEB64* ppeb ); + virtual ptr_t getPEB( _PEB64* ppeb ) noexcept; /// /// Get WOW64 TEB /// /// Retrieved TEB /// TEB pointer - virtual ptr_t getTEB( HANDLE hThread, _TEB32* pteb ); + virtual ptr_t getTEB( HANDLE hThread, _TEB32* pteb ) noexcept; /// /// Get native TEB /// /// Retrieved TEB /// TEB pointer - virtual ptr_t getTEB( HANDLE hThread, _TEB64* pteb ); - -private: + virtual ptr_t getTEB( HANDLE hThread, _TEB64* pteb ) noexcept; }; } \ No newline at end of file diff --git a/src/BlackBone/Symbols/PatternLoader.cpp b/src/BlackBone/Symbols/PatternLoader.cpp index 62ceff9c..dc4623e4 100644 --- a/src/BlackBone/Symbols/PatternLoader.cpp +++ b/src/BlackBone/Symbols/PatternLoader.cpp @@ -231,8 +231,7 @@ void OSFillPatterns( std::unordered_map& patterns, SymbolDat /// Mapped x86 ntdll /// Mapped x64 ntdll /// Result -/// Status code -NTSTATUS ScanSymbolPatterns( const pe::PEImage& ntdll32, const pe::PEImage& ntdll64, SymbolData& result ) +void ScanSymbolPatterns( const pe::PEImage& ntdll32, const pe::PEImage& ntdll64, SymbolData& result ) { ScanParams scan32, scan64; @@ -304,8 +303,6 @@ NTSTATUS ScanSymbolPatterns( const pe::PEImage& ntdll32, const pe::PEImage& ntdl BLACKBONE_TRACE( "PatternData: APC64PatchAddress not found" ); } #endif - - return STATUS_SUCCESS; } } \ No newline at end of file diff --git a/src/BlackBone/Symbols/PatternLoader.h b/src/BlackBone/Symbols/PatternLoader.h index f59727e1..a2165c61 100644 --- a/src/BlackBone/Symbols/PatternLoader.h +++ b/src/BlackBone/Symbols/PatternLoader.h @@ -11,7 +11,6 @@ namespace blackbone /// Mapped x86 ntdll /// Mapped x64 ntdll /// Result -/// Status code -NTSTATUS ScanSymbolPatterns( const pe::PEImage& ntdll32, const pe::PEImage& ntdll64, SymbolData& result ); +void ScanSymbolPatterns( const pe::PEImage& ntdll32, const pe::PEImage& ntdll64, SymbolData& result ); } \ No newline at end of file diff --git a/src/BlackBone/Symbols/SymbolLoader.cpp b/src/BlackBone/Symbols/SymbolLoader.cpp index e042d7c3..e48e0693 100644 --- a/src/BlackBone/Symbols/SymbolLoader.cpp +++ b/src/BlackBone/Symbols/SymbolLoader.cpp @@ -29,8 +29,7 @@ SymbolLoader::SymbolLoader() /// Load symbol addresses from PDB or and pattern scans /// /// Found symbols -/// Status code -NTSTATUS SymbolLoader::Load( SymbolData& result ) +void SymbolLoader::Load( SymbolData& result ) { auto [ntdll32, ntdll64] = LoadImages(); @@ -38,7 +37,7 @@ NTSTATUS SymbolLoader::Load( SymbolData& result ) LoadFromSymbols( ntdll32, ntdll64, result ); // Fill missing symbols from patterns - return LoadFromPatterns( ntdll32, ntdll64, result ); + LoadFromPatterns( ntdll32, ntdll64, result ); } /// @@ -86,10 +85,9 @@ NTSTATUS SymbolLoader::LoadFromSymbols( const pe::PEImage& ntdll32, const pe::PE /// Loaded x86 ntdll image /// Loaded x64 ntdll image /// Found symbols -/// Status code -NTSTATUS SymbolLoader::LoadFromPatterns( const pe::PEImage& ntdll32, const pe::PEImage& ntdll64, SymbolData& result ) +void SymbolLoader::LoadFromPatterns( const pe::PEImage& ntdll32, const pe::PEImage& ntdll64, SymbolData& result ) { - return ScanSymbolPatterns( ntdll32, ntdll64, result ); + ScanSymbolPatterns( ntdll32, ntdll64, result ); } /// @@ -100,7 +98,7 @@ std::pair SymbolLoader::LoadImages() { pe::PEImage ntdll32, ntdll64; - wchar_t buf[MAX_PATH] = { 0 }; + wchar_t buf[MAX_PATH] = { }; GetWindowsDirectoryW( buf, MAX_PATH ); std::wstring windir( buf ); diff --git a/src/BlackBone/Symbols/SymbolLoader.h b/src/BlackBone/Symbols/SymbolLoader.h index 3a815805..5d6e102a 100644 --- a/src/BlackBone/Symbols/SymbolLoader.h +++ b/src/BlackBone/Symbols/SymbolLoader.h @@ -18,8 +18,7 @@ class SymbolLoader /// Load symbol addresses from PDB or and pattern scans /// /// Found symbols - /// Status code - BLACKBONE_API NTSTATUS Load( SymbolData& result ); + BLACKBONE_API void Load( SymbolData& result ); /// /// Load symbol addresses from PDBs @@ -36,8 +35,7 @@ class SymbolLoader /// Loaded x86 ntdll image /// Loaded x64 ntdll image /// Found symbols - /// Status code - BLACKBONE_API NTSTATUS LoadFromPatterns( const pe::PEImage& ntdll32, const pe::PEImage& ntdll64, SymbolData& result ); + BLACKBONE_API void LoadFromPatterns( const pe::PEImage& ntdll32, const pe::PEImage& ntdll64, SymbolData& result ); /// /// Load ntdll images from the disk diff --git a/src/BlackBoneTest/BlackBoneTest.vcxproj b/src/BlackBoneTest/BlackBoneTest.vcxproj index eb95eeb4..62d4dd13 100644 --- a/src/BlackBoneTest/BlackBoneTest.vcxproj +++ b/src/BlackBoneTest/BlackBoneTest.vcxproj @@ -315,6 +315,8 @@ + + diff --git a/src/BlackBoneTest/BlackBoneTest.vcxproj.filters b/src/BlackBoneTest/BlackBoneTest.vcxproj.filters index aa1c8c2d..942330e3 100644 --- a/src/BlackBoneTest/BlackBoneTest.vcxproj.filters +++ b/src/BlackBoneTest/BlackBoneTest.vcxproj.filters @@ -43,6 +43,12 @@ Tests + + Tests + + + Tests + Tests diff --git a/src/BlackBoneTest/Common.h b/src/BlackBoneTest/Common.h index 42a27815..66d97805 100644 --- a/src/BlackBoneTest/Common.h +++ b/src/BlackBoneTest/Common.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include @@ -14,6 +15,7 @@ #include #include +#include using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace blackbone; @@ -46,6 +48,10 @@ class AssertEx : public Assert namespace Microsoft::VisualStudio::CppUnitTestFramework { template<> inline std::wstring ToString( const AsmVariant::eType& t ) { RETURN_WIDE_STRING( t ); } + +#ifdef USE64 + //template<> inline std::wstring ToString(const intptr_t& t) { RETURN_WIDE_STRING(t); } +#endif } inline std::wstring GetTestHelperDir() diff --git a/src/BlackBoneTest/TestAsmVariant.cpp b/src/BlackBoneTest/TestAsmVariant.cpp index 1e441687..fa2017bc 100644 --- a/src/BlackBoneTest/TestAsmVariant.cpp +++ b/src/BlackBoneTest/TestAsmVariant.cpp @@ -91,7 +91,7 @@ namespace Testing { AsmVariant a( "Text" ); - AssertEx::AreEqual( AsmVariant::dataPtr, a.type ); + AssertEx::AreEqual( AsmVariant::dataPtrConst, a.type ); } TEST_METHOD( StringConstPtr ) @@ -99,7 +99,7 @@ namespace Testing const char* cstring = "Text"; AsmVariant a( cstring ); - AssertEx::AreEqual( AsmVariant::dataPtr, a.type ); + AssertEx::AreEqual( AsmVariant::dataPtrConst, a.type ); AssertEx::AreEqual( reinterpret_cast(cstring), a.imm_val64 ); } @@ -108,7 +108,7 @@ namespace Testing const char str_array[] = "Text"; AsmVariant a( str_array ); - AssertEx::AreEqual( AsmVariant::dataPtr, a.type ); + AssertEx::AreEqual( AsmVariant::dataPtrConst, a.type ); AssertEx::AreEqual( reinterpret_cast(str_array), a.imm_val64 ); AssertEx::AreEqual( sizeof( str_array ), a.size ); } @@ -128,7 +128,7 @@ namespace Testing { AsmVariant a( L"Text" ); - AssertEx::AreEqual( AsmVariant::dataPtr, a.type ); + AssertEx::AreEqual( AsmVariant::dataPtrConst, a.type ); } TEST_METHOD( WStringConstPtr ) @@ -136,7 +136,7 @@ namespace Testing const wchar_t* cwstring = L"Text"; AsmVariant a( cwstring ); - AssertEx::AreEqual( AsmVariant::dataPtr, a.type ); + AssertEx::AreEqual( AsmVariant::dataPtrConst, a.type ); AssertEx::AreEqual( reinterpret_cast(cwstring), a.imm_val64 ); } @@ -145,7 +145,7 @@ namespace Testing const wchar_t wstr_array[] = L"Text"; AsmVariant a( wstr_array ); - AssertEx::AreEqual( a.type, AsmVariant::dataPtr ); + AssertEx::AreEqual( a.type, AsmVariant::dataPtrConst ); AssertEx::AreEqual( reinterpret_cast(wstr_array), a.imm_val64 ); AssertEx::AreEqual( sizeof( wstr_array ), a.size ); } diff --git a/src/BlackBoneTest/TestBasic.cpp b/src/BlackBoneTest/TestBasic.cpp index d096c74d..167efe3b 100644 --- a/src/BlackBoneTest/TestBasic.cpp +++ b/src/BlackBoneTest/TestBasic.cpp @@ -1,7 +1,7 @@ #include "Common.h" namespace Testing -{ +{ TEST_MODULE_INITIALIZE( ModuleInit ) { InitializeOnce(); @@ -10,74 +10,39 @@ namespace Testing TEST_CLASS( Generic ) { public: - TEST_METHOD_INITIALIZE( ClassInitialize ) + static DWORD CALLBACK VoidFn( void* ) { - AssertEx::NtSuccess( _proc.Attach( GetCurrentProcessId() ) ); + return 0; } - TEST_METHOD( PEB ) + TEST_METHOD( InvalidHandles ) { - _PEB32 peb32 = { }; - _PEB64 peb64 = { }; - - auto ppeb32 = _proc.core().peb32( &peb32 ); - auto ppeb64 = _proc.core().peb64( &peb64 ); + auto procBase = Process::CreateNew( GetTestHelperHost32() ); + auto proc1 = Process( procBase.pid(), PROCESS_ALL_ACCESS & ~PROCESS_CREATE_THREAD ); - AssertEx::IsNotZero( ppeb64 ); - AssertEx::IsNotZero( peb64.Ldr ); - - if (_proc.barrier().targetWow64) + try { - AssertEx::IsNotZero( ppeb32 ); - AssertEx::IsNotZero( peb32.Ldr ); + auto thread = proc1.threads().CreateNew( reinterpret_cast(&VoidFn), 0 ); + AssertEx::Fail( L"Must throw" ); } - } - - TEST_METHOD( TEB ) - { - _TEB32 teb32 = { }; - _TEB64 teb64 = { }; - - auto pteb32 = _proc.threads().getMain()->teb( &teb32 ); - auto pteb64 = _proc.threads().getMain()->teb( &teb64 ); - - AssertEx::IsNotZero( pteb64 ); - AssertEx::IsNotZero( teb64.ClientId.UniqueThread ); - - if (_proc.barrier().targetWow64) + catch (const nt_exception& e) { - AssertEx::IsNotZero( pteb32 ); - AssertEx::IsNotZero( teb32.ClientId.UniqueThread ); - AssertEx::IsNotZero( teb32.ClientId.UniqueThread ); + AssertEx::AreEqual( STATUS_ACCESS_DENIED, e.status() ); } - } + + auto proc2 = Process( procBase.pid(), PROCESS_ALL_ACCESS & ~PROCESS_VM_READ ); - static DWORD CALLBACK VoidFn( void* ) - { - return 0; - } - - TEST_METHOD( InvalidHandles ) - { - Process baseProc; - baseProc.CreateAndAttach( GetTestHelperHost32() ); - - Process proc1; - AssertEx::NtSuccess( proc1.Attach( baseProc.pid(), PROCESS_ALL_ACCESS & ~PROCESS_CREATE_THREAD ) ); - AssertEx::AreEqual( STATUS_ACCESS_DENIED, proc1.threads().CreateNew( reinterpret_cast(&VoidFn), 0 ).status ); - - Process proc2; - AssertEx::NtSuccess( proc2.Attach( baseProc.pid(), PROCESS_ALL_ACCESS & ~PROCESS_VM_READ ) ); - - PEB_T peb = { }; - AssertEx::IsNotZero( proc2.core().peb( &peb ) ); - AssertEx::IsZero( peb.ImageBaseAddress ); - AssertEx::AreEqual( STATUS_ACCESS_DENIED, LastNtStatus() ); + try + { + PEB_T peb = { }; + proc2.core().peb( &peb ); + } + catch (const nt_exception& e) + { + AssertEx::AreEqual( STATUS_ACCESS_DENIED, e.status() ); + } - baseProc.Terminate(); + procBase.Terminate(); } - - private: - Process _proc; }; } \ No newline at end of file diff --git a/src/BlackBoneTest/TestDriver.cpp b/src/BlackBoneTest/TestDriver.cpp index d44f8be4..e4643c37 100644 --- a/src/BlackBoneTest/TestDriver.cpp +++ b/src/BlackBoneTest/TestDriver.cpp @@ -16,7 +16,7 @@ namespace Testing public: TEST_METHOD_INITIALIZE( ClassInitialize ) { - AssertEx::NtSuccess( _explorer.Attach( L"explorer.exe" ) ); + _explorer.Attach( L"explorer.exe" ); NTSTATUS status = Driver().EnsureLoaded(); if (!NT_SUCCESS( status )) { @@ -46,8 +46,7 @@ namespace Testing { CHECK_AND_SKIP; - Process thisProc; - AssertEx::NtSuccess( thisProc.Attach( GetCurrentProcessId() ) ); + Process thisProc( GetCurrentProcessId() ); // Make current process protected AssertEx::NtSuccess( Driver().ProtectProcess( GetCurrentProcessId(), Policy_Enable ) ); diff --git a/src/BlackBoneTest/TestGuard.cpp b/src/BlackBoneTest/TestGuard.cpp index 15f83f10..c154e672 100644 --- a/src/BlackBoneTest/TestGuard.cpp +++ b/src/BlackBoneTest/TestGuard.cpp @@ -1,5 +1,4 @@ #include "Common.h" -#include namespace Testing { diff --git a/src/BlackBoneTest/TestManualMap.cpp b/src/BlackBoneTest/TestManualMap.cpp index c753fe42..994c092d 100644 --- a/src/BlackBoneTest/TestManualMap.cpp +++ b/src/BlackBoneTest/TestManualMap.cpp @@ -68,25 +68,20 @@ namespace Testing private: void MapFromFile( const std::wstring& hostPath, const std::wstring& dllPath ) { - Process proc; - NTSTATUS status = proc.CreateAndAttach( hostPath ); - AssertEx::NtSuccess( status ); - proc.EnsureInit(); + auto proc = Process::CreateNew( hostPath ); + Sleep( 500 ); auto image = proc.mmap().MapImage( dllPath, ManualImports, &MapCallback ); - AssertEx::IsTrue( image.success() ); - AssertEx::IsNotNull( image.result().get() ); + AssertEx::IsNotNull( image.get() ); - auto g_loadDataPtr = proc.modules().GetExport( image.result(), "g_LoadData" ); - AssertEx::IsTrue( g_loadDataPtr.success() ); - AssertEx::IsNotZero( g_loadDataPtr->procAddress ); + auto g_loadDataPtr = proc.modules().GetExport( image, "g_LoadData" ); + AssertEx::IsNotZero( g_loadDataPtr.procAddress ); - auto g_loadData = proc.memory().Read( g_loadDataPtr->procAddress ); - AssertEx::IsTrue( g_loadData.success() ); + auto g_loadData = proc.memory().Read( g_loadDataPtr.procAddress ); proc.Terminate(); - ValidateDllLoad( g_loadData.result() ); + ValidateDllLoad( g_loadData ); } void MapFromMemory( const std::wstring& hostPath, const std::wstring& dllPath ) @@ -94,25 +89,20 @@ namespace Testing auto[buf, size] = GetFileData( dllPath ); AssertEx::IsNotZero( size ); - Process proc; - NTSTATUS status = proc.CreateAndAttach( hostPath ); - AssertEx::NtSuccess( status ); - proc.EnsureInit(); + auto proc = Process::CreateNew( hostPath ); + Sleep( 500 ); auto image = proc.mmap().MapImage( size, buf.get(), false, ManualImports, &MapCallback ); - AssertEx::IsTrue( image.success() ); - AssertEx::IsNotNull( image.result().get() ); + AssertEx::IsNotNull( image.get() ); - auto g_loadDataPtr = proc.modules().GetExport( image.result(), "g_LoadData" ); - AssertEx::IsTrue( g_loadDataPtr.success() ); - AssertEx::IsNotZero( g_loadDataPtr->procAddress ); + auto g_loadDataPtr = proc.modules().GetExport( image, "g_LoadData" ); + AssertEx::IsNotZero( g_loadDataPtr.procAddress ); - auto g_loadData = proc.memory().Read( g_loadDataPtr->procAddress ); - AssertEx::IsTrue( g_loadData.success() ); + auto g_loadData = proc.memory().Read( g_loadDataPtr.procAddress ); proc.Terminate(); - ValidateDllLoad( g_loadData.result() ); + ValidateDllLoad( g_loadData ); } void ValidateDllLoad( const DllLoadData& data ) diff --git a/src/BlackBoneTest/TestModules.cpp b/src/BlackBoneTest/TestModules.cpp index 995ba31e..2f3a2ad7 100644 --- a/src/BlackBoneTest/TestModules.cpp +++ b/src/BlackBoneTest/TestModules.cpp @@ -8,7 +8,7 @@ TEST_CLASS( Modules ) public: TEST_METHOD_INITIALIZE( ClassInitialize ) { - AssertEx::NtSuccess( _proc.Attach( GetCurrentProcessId() ) ); + _proc.Attach( GetCurrentProcessId() ); } TEST_METHOD( MainModule ) @@ -57,8 +57,7 @@ TEST_CLASS( Modules ) AssertEx::IsNotNull( reinterpret_cast(expected) ); auto result = _proc.modules().GetExport( L"kernel32.dll", "CreateFileW" ); - AssertEx::IsTrue( result.success() ); - AssertEx::AreEqual( reinterpret_cast(expected), result->procAddress ); + AssertEx::AreEqual( reinterpret_cast(expected), result.procAddress ); } private: diff --git a/src/BlackBoneTest/TestMultiPtr.cpp b/src/BlackBoneTest/TestMultiPtr.cpp index bea2cf7d..987ba918 100644 --- a/src/BlackBoneTest/TestMultiPtr.cpp +++ b/src/BlackBoneTest/TestMultiPtr.cpp @@ -87,8 +87,7 @@ namespace Testing TEST_METHOD( Remote ) { - Process proc; - AssertEx::NtSuccess( proc.Attach( GetCurrentProcessId() ) ); + Process proc( GetCurrentProcessId() ); const float newVal = 25.0f; @@ -98,7 +97,7 @@ namespace Testing AssertEx::IsNotZero( pVal_ex->fval ); pVal_ex->fval = 25.0; - AssertEx::AreEqual( STATUS_SUCCESS, ptr_ex.commit() ); + ptr_ex.commit(); pVal_ex = ptr_ex.get(); AssertEx::IsNotNull( pVal_ex ); diff --git a/src/BlackBoneTest/TestPatternScan.cpp b/src/BlackBoneTest/TestPatternScan.cpp index db44b7b3..3a11b9a0 100644 --- a/src/BlackBoneTest/TestPatternScan.cpp +++ b/src/BlackBoneTest/TestPatternScan.cpp @@ -7,13 +7,13 @@ namespace Testing public: TEST_METHOD_INITIALIZE( ClassInitialize ) { - AssertEx::NtSuccess( _proc.Attach( L"explorer.exe" ) ); + _proc.Attach( L"explorer.exe" ); } // Scan all allocated process memory TEST_METHOD( Simple ) { - PatternSearch ps1( "\x48\x89\xD0" ); + PatternSearch ps1( "\x48\x89" ); std::vector results; ps1.SearchRemoteWhole( _proc, false, 0, results ); diff --git a/src/BlackBoneTest/TestPebTeb.cpp b/src/BlackBoneTest/TestPebTeb.cpp new file mode 100644 index 00000000..20866aba --- /dev/null +++ b/src/BlackBoneTest/TestPebTeb.cpp @@ -0,0 +1,78 @@ +#include "Common.h" + +namespace Testing +{ + TEST_CLASS( PebTeb ) + { + public: + TEST_METHOD_INITIALIZE( ClassInitialize ) + { + _proc32.CreateAndAttach( GetTestHelperHost32() ); + _proc64.CreateAndAttach( GetTestHelperHost64() ); + Sleep( 500 ); + } + + TEST_METHOD( Peb32 ) + { + _PEB32 peb32 = { }; + auto ppeb32 = _proc32.core().peb32( &peb32 ); + + AssertEx::IsNotZero( ppeb32 ); + AssertEx::IsNotZero( peb32.Ldr ); + } + + TEST_METHOD( NoPeb32 ) + { + try + { + auto ppeb32 = _proc64.core().peb32(); + } + catch (const nt_exception& e) + { + AssertEx::AreEqual( e.status(), STATUS_NOT_SUPPORTED ); + } + } + + TEST_METHOD( Peb64 ) + { + _PEB64 peb64 = { }; + auto ppeb64 = _proc64.core().peb64( &peb64 ); + + AssertEx::IsNotZero( ppeb64 ); + AssertEx::IsNotZero( peb64.Ldr ); + } + + TEST_METHOD( Teb32 ) + { + _TEB32 teb32 = { }; + auto pteb32 = _proc32.threads().getMain()->teb32( &teb32 ); + + AssertEx::IsNotZero( pteb32 ); + AssertEx::IsNotZero( teb32.ClientId.UniqueThread ); + } + + TEST_METHOD( NoTeb32 ) + { + try + { + auto pteb32 = _proc64.threads().getMain()->teb32(); + } + catch (const nt_exception& e) + { + AssertEx::AreEqual( e.status(), STATUS_NOT_SUPPORTED ); + } + } + + TEST_METHOD( Teb64 ) + { + _TEB64 teb64 = { }; + auto pteb64 = _proc64.threads().getMain()->teb64( &teb64 ); + + AssertEx::IsNotZero( pteb64 ); + AssertEx::IsNotZero( teb64.ClientId.UniqueThread ); + } + + private: + Process _proc32, _proc64; + }; +} \ No newline at end of file diff --git a/src/BlackBoneTest/TestRemoteCall.cpp b/src/BlackBoneTest/TestRemoteCall.cpp index 99374fd5..3b5636e6 100644 --- a/src/BlackBoneTest/TestRemoteCall.cpp +++ b/src/BlackBoneTest/TestRemoteCall.cpp @@ -99,8 +99,7 @@ namespace Testing TEST_METHOD( LocalCall ) { - Process process; - AssertEx::NtSuccess( process.Attach( GetCurrentProcessId() ) ); + Process process( GetCurrentProcessId() ); auto pFN = MakeRemoteFunction( process, &TestFn ); double d = 0.0; @@ -108,11 +107,9 @@ namespace Testing _input.fval = 1337.0f; _input.uval = 0xDEADC0DEA4DBEEFull; - auto[status, result] = pFN.Call( { 1, 2.0f, 3.0, &d, 5ll, _cbuf, _wbuf, &_output, _input } ); - AssertEx::NtSuccess( status ); - AssertEx::IsTrue( result.has_value() ); + auto result = pFN.Call( { 1, 2.0f, 3.0, &d, 5ll, _cbuf, _wbuf, &_output, _input } ); - AssertEx::AreEqual( 1 + 5, result.value() ); + AssertEx::AreEqual( 1 + 5, result ); AssertEx::AreEqual( 3.0 + 2.0f, d, 0.001 ); AssertEx::AreEqual( _cbuf, g_string ); AssertEx::AreEqual( _wbuf, g_wstring ); @@ -122,8 +119,8 @@ namespace Testing TEST_METHOD( CallLoop ) { Process process; - AssertEx::NtSuccess( process.Attach( GetCurrentProcessId() ) ); - AssertEx::NtSuccess( process.remote().CreateRPCEnvironment( Worker_CreateNew, true ) ); + process.Attach( GetCurrentProcessId() ); + process.remote().CreateRPCEnvironment( Worker_CreateNew, true ); auto pFN = MakeRemoteFunction( process, &TestFn ); auto worker = process.remote().getWorker(); @@ -135,17 +132,15 @@ namespace Testing for(auto i = 0; i < 10000; i++) { - auto [status, result] = pFN.Call( { 1, 2.0f, 3.0, &d, 5ll, _cbuf, _wbuf, &_output, _input }, worker ); - AssertEx::NtSuccess( status ); - AssertEx::IsTrue( result.has_value() ); - AssertEx::AreEqual( 1 + 5, result.value() ); + auto result = pFN.Call( { 1, 2.0f, 3.0, &d, 5ll, _cbuf, _wbuf, &_output, _input }, worker ); + AssertEx::AreEqual( 1 + 5, result); } } TEST_METHOD( BoundThread ) { Process process; - AssertEx::NtSuccess( process.Attach( GetCurrentProcessId() ) ); + process.Attach( GetCurrentProcessId() ); DWORD id = 0; auto code = []( void* ) -> DWORD @@ -173,10 +168,8 @@ namespace Testing for (auto i = 0; i < 100; i++) { - auto [status, result] = pFN.Call( { 1, 2.0f, 3.0, &d, 5ll, _cbuf, _wbuf, &_output, _input } ); - AssertEx::NtSuccess( status ); - AssertEx::IsTrue( result.has_value() ); - AssertEx::AreEqual( 1 + 5, result.value() ); + auto result = pFN.Call( { 1, 2.0f, 3.0, &d, 5ll, _cbuf, _wbuf, &_output, _input } ); + AssertEx::AreEqual( 1 + 5, result ); } TerminateThread( hThread, ERROR_SUCCESS ); @@ -189,8 +182,7 @@ namespace Testing AssertEx::IsTrue( Utils::FileExists( path ) ); // Give process some time to initialize - Process process; - AssertEx::NtSuccess( process.CreateAndAttach( path ) ); + auto process = Process::CreateNew( path ); Sleep( 100 ); auto hMainMod = process.modules().GetMainModule(); @@ -211,8 +203,7 @@ namespace Testing process.Terminate(); - AssertEx::NtSuccess( result.status ); - AssertEx::NtSuccess( result.result() ); + AssertEx::NtSuccess( result ); std::wstring name( reinterpret_cast(buf + sizeof( UNICODE_STRING )) ); AssertEx::AreNotEqual( name.npos, name.rfind( Utils::StripPath( path ) ) ); @@ -224,38 +215,31 @@ namespace Testing AssertEx::IsTrue( Utils::FileExists( path ) ); // Give process some time to initialize - Process process; - AssertEx::NtSuccess( process.CreateAndAttach( path ) ); + auto process = Process::CreateNew( path ); Sleep( 100 ); auto CreateFileWPtr = process.modules().GetExport( L"kernel32.dll", "CreateFileW" ); auto WriteFilePtr = process.modules().GetExport( L"kernel32.dll", "WriteFile" ); auto CloseHandlePtr = process.modules().GetExport( L"kernel32.dll", "CloseHandle" ); - AssertEx::IsTrue( CreateFileWPtr.success() ); - AssertEx::IsTrue( WriteFilePtr.success() ); - AssertEx::IsTrue( CloseHandlePtr.success() ); - uint8_t writeBuf[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; - RemoteFunction pCreateFile( process, CreateFileWPtr->procAddress ); - RemoteFunction pWriteFile( process, WriteFilePtr->procAddress ); - RemoteFunction pCloseHandle( process, CloseHandlePtr->procAddress ); + RemoteFunction pCreateFile( process, CreateFileWPtr.procAddress ); + RemoteFunction pWriteFile( process, WriteFilePtr.procAddress ); + RemoteFunction pCloseHandle( process, CloseHandlePtr.procAddress ); auto filePath = L"DummyFile_FileOP.dat"; auto handle = pCreateFile.Call( filePath, GENERIC_WRITE, 0x7, nullptr, CREATE_ALWAYS, 0, nullptr ); - AssertEx::NtSuccess( handle.status ); - AssertEx::IsNotNull( handle.result() ); + AssertEx::IsNotNull( handle ); DWORD bytes = 0; - auto success = pWriteFile.Call( { handle.result(), writeBuf, sizeof( writeBuf ), &bytes, nullptr } ); - pCloseHandle.Call( handle.result(), nullptr ); + auto success = pWriteFile.Call( { handle, writeBuf, sizeof( writeBuf ), &bytes, nullptr } ); + pCloseHandle.Call( handle, nullptr ); process.Terminate(); - AssertEx::NtSuccess( success.status ); - AssertEx::IsTrue( success.result() ); + AssertEx::IsTrue( success ); AssertEx::AreEqual( static_cast(sizeof( writeBuf )), bytes ); // Check locally diff --git a/src/BlackBoneTest/TestRemoteHook.cpp b/src/BlackBoneTest/TestRemoteHook.cpp index 19494d72..d0b5f557 100644 --- a/src/BlackBoneTest/TestRemoteHook.cpp +++ b/src/BlackBoneTest/TestRemoteHook.cpp @@ -52,29 +52,26 @@ namespace Testing auto path = GetTestHelperHost(); AssertEx::IsFalse( path.empty() ); - // Give process some time to initialize - AssertEx::NtSuccess( hooker.process.CreateAndAttach( path ) ); + hooker.process.CreateAndAttach( path ); Sleep( 100 ); // Remote helper function auto terminatePtr = hooker.process.modules().GetExport( hooker.process.modules().GetMainModule(), "TerminateProc" ); - AssertEx::IsTrue( terminatePtr.success() ); // Get function auto pHookFn = hooker.process.modules().GetNtdllExport( "NtOpenProcess" ); - AssertEx::IsTrue( pHookFn.success() ); // Hook and try to terminate from remote process - AssertEx::NtSuccess( hooker.process.hooks().Apply( RemoteHook::hwbp, pHookFn->procAddress, &HookClass::HookNtOpenProcess, hooker ) ); + AssertEx::NtSuccess( hooker.process.hooks().Apply( RemoteHook::hwbp, pHookFn.procAddress, &HookClass::HookNtOpenProcess, hooker ) ); - auto terminate = MakeRemoteFunction( hooker.process, terminatePtr->procAddress ); + auto terminate = MakeRemoteFunction( hooker.process, terminatePtr.procAddress ); auto result = terminate( GetCurrentProcessId() ); hooker.process.Terminate(); - AssertEx::IsTrue( result.success() ); - AssertEx::AreEqual( ERROR_ACCESS_DENIED, result.result() ); + AssertEx::IsTrue( result ); + AssertEx::AreEqual( ERROR_ACCESS_DENIED, result ); AssertEx::AreEqual( 1, hooker.calls ); } @@ -86,19 +83,18 @@ namespace Testing AssertEx::IsFalse( path.empty() ); // Give process some time to initialize - AssertEx::NtSuccess( hooker.process.CreateAndAttach( path ) ); + hooker.process.CreateAndAttach( path ); Sleep( 100 ); // Get function auto pHookFn = hooker.process.modules().GetNtdllExport( "NtAllocateVirtualMemory" ); - AssertEx::IsTrue( pHookFn.success() ); PVOID base = nullptr; SIZE_T size = 0xDEAD; - auto NtAllocateVirtualMemory = MakeRemoteFunction( hooker.process, pHookFn->procAddress ); + auto NtAllocateVirtualMemory = MakeRemoteFunction( hooker.process, pHookFn.procAddress ); // Hook and try to call - AssertEx::NtSuccess( hooker.process.hooks().Apply( RemoteHook::hwbp, pHookFn->procAddress, &HookClass::HookNtAllocateVirtualMemory, hooker ) ); + AssertEx::NtSuccess( hooker.process.hooks().Apply( RemoteHook::hwbp, pHookFn.procAddress, &HookClass::HookNtAllocateVirtualMemory, hooker ) ); auto result = NtAllocateVirtualMemory.Call( { GetCurrentProcess(), &base, 0, &size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE } ); hooker.process.Terminate(); diff --git a/src/BlackBoneTest/TestRemoteMemory.cpp b/src/BlackBoneTest/TestRemoteMemory.cpp index 8baf6b7c..36bfe6eb 100644 --- a/src/BlackBoneTest/TestRemoteMemory.cpp +++ b/src/BlackBoneTest/TestRemoteMemory.cpp @@ -12,8 +12,6 @@ namespace Testing public: TEST_METHOD( Everything ) { - Process proc; - NTSTATUS status = Driver().EnsureLoaded(); if (!NT_SUCCESS( status )) { @@ -21,7 +19,7 @@ namespace Testing return; } - AssertEx::NtSuccess( proc.Attach( L"explorer.exe" ) ); + Process proc( L"explorer.exe" ); AssertEx::NtSuccess( proc.memory().Map( false ) ); // Translate main module base address diff --git a/src/BlackBoneTest/TestScopeExit.cpp b/src/BlackBoneTest/TestScopeExit.cpp new file mode 100644 index 00000000..d5f68f26 --- /dev/null +++ b/src/BlackBoneTest/TestScopeExit.cpp @@ -0,0 +1,20 @@ +#include "Common.h" + +namespace Testing +{ + TEST_CLASS( ScopeExit ) + { + public: + TEST_METHOD( Simple ) + { + constexpr int expected = 10; + int a = 0; + { + ON_SCOPE_EXIT{ a = expected; }; + AssertEx::AreEqual( 0, a ); + } + + AssertEx::AreEqual( expected, a ); + } + }; +} \ No newline at end of file diff --git a/src/BlackBoneTest/TestSymbols.cpp b/src/BlackBoneTest/TestSymbols.cpp index a3fe0891..70efb45f 100644 --- a/src/BlackBoneTest/TestSymbols.cpp +++ b/src/BlackBoneTest/TestSymbols.cpp @@ -15,7 +15,15 @@ namespace Testing AssertEx::IsNotZero( ntdll32.imageBase() ); AssertEx::IsNotZero( ntdll64.imageBase() ); - AssertEx::NtSuccess( sl.LoadFromPatterns( ntdll32, ntdll64, fromPatterns ) ); + sl.LoadFromPatterns( ntdll32, ntdll64, fromPatterns ); + + AssertEx::IsNotZero( fromPatterns.LdrpHandleTlsData32 ); + AssertEx::IsNotZero( fromPatterns.LdrpHandleTlsData64 ); + AssertEx::IsNotZero( fromPatterns.LdrpInvertedFunctionTable32 ); + AssertEx::IsNotZero( fromPatterns.LdrpInvertedFunctionTable64 ); + AssertEx::IsNotZero( fromPatterns.LdrProtectMrdata ); + AssertEx::IsNotZero( fromPatterns.RtlInsertInvertedFunctionTable32 ); + AssertEx::IsNotZero( fromPatterns.RtlInsertInvertedFunctionTable64 ); if (NT_SUCCESS( sl.LoadFromSymbols( ntdll32, ntdll64, fromSymbols ) )) { diff --git a/src/Samples/Main.cpp b/src/Samples/Main.cpp index 939ddb5f..7bec8951 100644 --- a/src/Samples/Main.cpp +++ b/src/Samples/Main.cpp @@ -11,6 +11,15 @@ void MapCmdFromMem(); int main( int /*argc*/, char* /*argv[]*/ ) { + try + { + Process::CreateNew( L"C:\\InvalidName.*?" ); + } + catch (const nt_exception& e) + { + std::cout << e.what(); + } + // List all process PIDs matching name auto pids = Process::EnumByName( L"explorer.exe" ); @@ -21,8 +30,9 @@ int main( int /*argc*/, char* /*argv[]*/ ) auto all = Process::EnumByNameOrPID( 0, L"" ); // Attach to a process - if (Process explorer; !pids.empty() && NT_SUCCESS( explorer.Attach( pids.front() ) )) + try { + auto explorer = Process( pids.front() ); auto& core = explorer.core(); // Get bitness info about this and target processes @@ -37,15 +47,17 @@ int main( int /*argc*/, char* /*argv[]*/ ) [[maybe_unused]] auto peb_ptr = core.peb( &peb ); // Get all process handles - if (auto handles = explorer.EnumHandles(); handles) + if (auto handles = explorer.EnumHandles(); true) { // do stuff with handles... } } + catch (const std::exception&) + { + } // Start new suspended process and attach immediately - Process notepad; - notepad.CreateAndAttach( L"C:\\windows\\system32\\notepad.exe", true ); + auto notepad = Process::CreateNew( L"C:\\windows\\system32\\notepad.exe", true ); { // do stuff... notepad.Resume(); @@ -67,9 +79,6 @@ int main( int /*argc*/, char* /*argv[]*/ ) // Get export symbol from module found by name auto LoadLibraryWPtr = modules.GetExport( L"kernel32.dll", "LoadLibraryW" ); - if (LoadLibraryWPtr) - { - } // Unlink module from loader structures if (modules.Unlink( mainMod )) @@ -85,7 +94,7 @@ int main( int /*argc*/, char* /*argv[]*/ ) // // Read memory // - IMAGE_DOS_HEADER dosHeader = { }; + IMAGE_DOS_HEADER dosHeader = {}; // Method 1 memory.Read( mainMod->baseAddress, dosHeader ); @@ -94,7 +103,7 @@ int main( int /*argc*/, char* /*argv[]*/ ) memory.Read( mainMod->baseAddress, sizeof( dosHeader ), &dosHeader ); // Method 3 - auto[status, dosHeader2] = memory.Read( mainMod->baseAddress ); + auto dosHeader2 = memory.Read( mainMod->baseAddress ); // Change memory protection if (NT_SUCCESS( memory.Protect( mainMod->baseAddress, sizeof( dosHeader ), PAGE_READWRITE ) )) @@ -111,13 +120,18 @@ int main( int /*argc*/, char* /*argv[]*/ ) } // Allocate memory - if (auto[status2, block] = memory.Allocate( 0x1000, PAGE_EXECUTE_READWRITE ); NT_SUCCESS( status2 )) + try { + auto block = memory.Allocate( 0x1000, PAGE_EXECUTE_READWRITE ); + // Write into memory block - block->Write( 0x10, 12.0 ); + block.Write( 0x10, 12.0 ); // Read from memory block - [[maybe_unused]] auto dval = block->Read( 0x10, 0.0 ); + [[maybe_unused]] auto dval = block.Read( 0x10, 0.0 ); + } + catch (const std::exception&) + { } // Enumerate regions @@ -136,7 +150,7 @@ int main( int /*argc*/, char* /*argv[]*/ ) auto thread = notepad.threads().get( mainThread->id() ); // Get context - CONTEXT_T ctx = { }; + CONTEXT_T ctx = {}; if (thread->GetContext( ctx, CONTEXT_FLOATING_POINT )) { // Set context @@ -165,50 +179,46 @@ int main( int /*argc*/, char* /*argv[]*/ ) { auto& remote = notepad.remote(); remote.CreateRPCEnvironment( Worker_None, true ); - + auto GetModuleHandleWPtr = notepad.modules().GetExport( L"kernel32.dll", "GetModuleHandleW" ); - if (GetModuleHandleWPtr) - { - // Direct execution in the new thread without stub - [[maybe_unused]] DWORD mod = remote.ExecDirect( GetModuleHandleWPtr->procAddress, 0 ); - } + + // Direct execution in the new thread without stub + [[maybe_unused]] DWORD mod = remote.ExecDirect( GetModuleHandleWPtr.procAddress, 0 ); // Execute in the new thread using stub - if (auto asmPtr = AsmFactory::GetAssembler(); asmPtr && GetModuleHandleWPtr) + if (auto asmPtr = AsmFactory::GetAssembler(); asmPtr) { auto& a = *asmPtr; a.GenPrologue(); - a.GenCall( static_cast(GetModuleHandleWPtr->procAddress), { nullptr }, cc_stdcall ); + a.GenCall( static_cast(GetModuleHandleWPtr.procAddress), { nullptr }, cc_stdcall ); a.GenEpilogue(); - uint64_t result = 0; - remote.ExecInNewThread( a->make(), a->getCodeSize(), result ); + remote.ExecInNewThread( a->make(), a->getCodeSize() ); } // Execute in main thread auto mainThread = notepad.threads().getMain(); - if (auto asmPtr = AsmFactory::GetAssembler(); asmPtr && mainThread && GetModuleHandleWPtr) + if (auto asmPtr = AsmFactory::GetAssembler(); asmPtr && mainThread) { auto& a = *asmPtr; a.GenPrologue(); - a.GenCall( static_cast(GetModuleHandleWPtr->procAddress), { nullptr }, cc_stdcall ); + a.GenCall( static_cast(GetModuleHandleWPtr.procAddress), { nullptr }, cc_stdcall ); a.GenEpilogue(); - uint64_t result = 0; - remote.ExecInAnyThread( a->make(), a->getCodeSize(), result, mainThread ); + remote.ExecInAnyThread( a->make(), a->getCodeSize(), mainThread ); } } // Pattern scanning - if (Process process; NT_SUCCESS( process.Attach( GetCurrentProcessId() ) )) + /*if (Process process; NT_SUCCESS( process.Attach( GetCurrentProcessId() ) )) { PatternSearch ps{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; std::vector results; ps.SearchRemoteWhole( process, false, 0, results ); - } + }*/ // Remote function calls { @@ -216,23 +226,25 @@ int main( int /*argc*/, char* /*argv[]*/ ) if (auto pMessageBoxW = MakeRemoteFunction( notepad, L"user32.dll", "MessageBoxW" )) { auto result = pMessageBoxW( HWND_DESKTOP, L"Hello world!", L"Title", MB_OKCANCEL ); - if (*result == IDCANCEL) + if (result == IDCANCEL) { } } // Call in specific thread auto mainThread = notepad.threads().getMain(); - if (auto pIsGUIThread = MakeRemoteFunction( notepad, L"user32.dll", "IsGUIThread" ); pIsGUIThread && mainThread) + if (auto pIsGUIThread = MakeRemoteFunction( notepad, L"user32.dll", "IsGUIThread" ); + pIsGUIThread && mainThread) { auto result = pIsGUIThread.Call( { FALSE }, mainThread ); - if (*result) + if (result) { } } // Complex args - if (auto pMultiByteToWideChar = MakeRemoteFunction( notepad, L"kernel32.dll", "MultiByteToWideChar" )) + if (auto pMultiByteToWideChar = MakeRemoteFunction( + notepad, L"kernel32.dll", "MultiByteToWideChar" )) { auto args = pMultiByteToWideChar.MakeArguments( { CP_ACP, 0, "Sample text", -1, nullptr, 0 } ); std::wstring converted( 32, L'\0' ); @@ -243,13 +255,13 @@ int main( int /*argc*/, char* /*argv[]*/ ) auto length = pMultiByteToWideChar.Call( args ); if (length) - converted.resize( *length - 1 ); + converted.resize( length - 1 ); } } // Direct syscalls, currently works for x64 only { - uint8_t buf[32] = { }; + uint8_t buf[32] = {}; uintptr_t bytes = 0; NTSTATUS status = syscall::nt_syscall( @@ -257,7 +269,7 @@ int main( int /*argc*/, char* /*argv[]*/ ) GetCurrentProcess(), GetModuleHandle( nullptr ), buf, - sizeof(buf), + sizeof( buf ), &bytes ); @@ -273,4 +285,4 @@ int main( int /*argc*/, char* /*argv[]*/ ) MapCmdFromMem(); return 0; -} \ No newline at end of file +} diff --git a/src/Samples/ManualMap.cpp b/src/Samples/ManualMap.cpp index 206f0744..2fc192dc 100644 --- a/src/Samples/ManualMap.cpp +++ b/src/Samples/ManualMap.cpp @@ -50,16 +50,19 @@ void MapCalcFromFile() std::wcout << L"Manual image mapping test" << std::endl; std::wcout << L"Trying to map C:\\windows\\system32\\calc.exe into current process" << std::endl; - auto image = thisProc.mmap().MapImage( L"C:\\windows\\system32\\calc.exe", ManualImports | RebaseProcess, callback ); - if (!image) + try { - std::wcout << L"Mapping failed with error 0x" << std::hex << image.status - << L". " << Utils::GetErrorDescription( image.status ) << std::endl << std::endl; - } - else + auto image = thisProc.mmap().MapImage( L"C:\\windows\\system32\\calc.exe", ManualImports | RebaseProcess, callback ); std::wcout << L"Successfully mapped, unmapping\n"; - - thisProc.mmap().UnmapAllModules(); + thisProc.mmap().UnmapAllModules(); + } + catch (const nt_exception& ex) + { + std::cout << "Exception: " << ex.what() << std::endl; +#if _DEBUG + std::cout << "Stack trace: " << std::endl << ex.stack_trace(); +#endif + } } /* @@ -67,36 +70,32 @@ void MapCalcFromFile() */ void MapCmdFromMem() { - Process thisProc; - thisProc.Attach( GetCurrentProcessId() ); - - void* buf = nullptr; - auto size = 0; + auto thisProc = Process::CurrentProcess(); std::wcout << L"Manual image mapping from buffer test" << std::endl; std::wcout << L"Trying to map C:\\windows\\system32\\cmd.exe into current process" << std::endl; // Get image context - HANDLE hFile = CreateFileW( L"C:\\windows\\system32\\cmd.exe", FILE_GENERIC_READ, 0x7, 0, OPEN_EXISTING, 0, 0 ); - if (hFile != INVALID_HANDLE_VALUE) + auto hFile = Handle( CreateFileW( L"C:\\windows\\system32\\cmd.exe", FILE_GENERIC_READ, 0x7, 0, OPEN_EXISTING, 0, 0 ) ); + if (!hFile) + THROW_AND_LOG( "failed to read 'C:\\windows\\system32\\cmd.exe'" ); + + DWORD bytes = 0; + auto size = GetFileSize( hFile, nullptr ); + auto buf = make_raw_ptr( size ); + ReadFile( hFile, buf.get(), size, &bytes, nullptr ); + + try { - DWORD bytes = 0; - size = GetFileSize( hFile, NULL ); - buf = VirtualAlloc( NULL, size, MEM_COMMIT, PAGE_READWRITE ); - ReadFile( hFile, buf, size, &bytes, NULL ); - CloseHandle( hFile ); + auto image = thisProc.mmap().MapImage( size, buf.get(), false, CreateLdrRef | RebaseProcess | NoDelayLoad ); + std::wcout << L"Successfully mapped, unmapping\n"; + thisProc.mmap().UnmapAllModules(); } - - auto image = thisProc.mmap().MapImage( size, buf, false, CreateLdrRef | RebaseProcess | NoDelayLoad ); - if (!image) + catch (const nt_exception& ex) { - std::wcout << L"Mapping failed with error 0x" << std::hex << image.status - << L". " << Utils::GetErrorDescription( image.status ) << std::endl << std::endl; + std::cout << "Exception: " << ex.what() << std::endl; +#if _DEBUG + std::cout << "Stack trace: " << std::endl << ex.stack_trace(); +#endif } - else - std::wcout << L"Successfully mapped, unmapping\n"; - - VirtualFree( buf, 0, MEM_RELEASE ); - - thisProc.mmap().UnmapAllModules(); }