mirror of
https://github.com/DarthTon/Blackbone.git
synced 2026-10-29 12:13:24 +08:00
MMap: further arch unification
This commit is contained in:
@@ -170,7 +170,7 @@ void AsmHelper32::SaveRetValAndSignalEvent(
|
||||
|
||||
// Save last NT status
|
||||
_assembler.mov( asmjit::host::edx, asmjit::host::dword_ptr_abs( 0x18 ).setSegment( asmjit::host::fs ) );
|
||||
_assembler.add( asmjit::host::edx, LAST_STATUS_OFS );
|
||||
_assembler.add( asmjit::host::edx, 0x598 + 0x197 * sizeof( uint32_t ) );
|
||||
_assembler.mov( asmjit::host::edx, asmjit::host::dword_ptr( asmjit::host::edx ) );
|
||||
_assembler.mov( asmjit::host::eax, errPtr );
|
||||
_assembler.mov( asmjit::host::dword_ptr( asmjit::host::eax ), asmjit::host::edx );
|
||||
|
||||
@@ -146,7 +146,7 @@ void AsmHelper64::SaveRetValAndSignalEvent(
|
||||
|
||||
// Save last NT status
|
||||
_assembler.mov( asmjit::host::rdx, asmjit::host::dword_ptr_abs( 0x30 ).setSegment( asmjit::host::gs ) ); // TEB ptr
|
||||
_assembler.add( asmjit::host::rdx, LAST_STATUS_OFS );
|
||||
_assembler.add( asmjit::host::rdx, 0x598 + 0x197 * sizeof( uint64_t ) );
|
||||
_assembler.mov( asmjit::host::rdx, asmjit::host::dword_ptr( asmjit::host::rdx ) );
|
||||
_assembler.mov( asmjit::host::rax, lastStatusPtr );
|
||||
_assembler.mov( asmjit::host::dword_ptr( asmjit::host::rax ), asmjit::host::rdx );
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "../Config.h"
|
||||
#include <stdint.h>
|
||||
|
||||
// Architecture-dependent pointer size
|
||||
#define WordSize sizeof(void*)
|
||||
@@ -71,6 +72,18 @@
|
||||
template<int s>
|
||||
struct CompileTimeSizeOf;
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr size_t offsetOf( U T::*member )
|
||||
{
|
||||
return (size_t)&((T*)nullptr->*member);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr uint64_t fieldPtr( uint64_t base, U T::*member )
|
||||
{
|
||||
return base + offsetOf( member );
|
||||
}
|
||||
|
||||
// Type-unsafe cast.
|
||||
template<typename _Tgt, typename _Src>
|
||||
inline _Tgt brutal_cast( const _Src& src )
|
||||
@@ -110,3 +123,5 @@ inline NTSTATUS SetLastNtStatus( NTSTATUS status )
|
||||
{
|
||||
return *(NTSTATUS*)((unsigned char*)NtCurrentTeb() + LAST_STATUS_OFS) = status;
|
||||
}
|
||||
|
||||
#define SharedUserData32 ((KUSER_SHARED_DATA* const)0x7FFE0000)
|
||||
|
||||
@@ -445,6 +445,17 @@ namespace blackbone
|
||||
ULONG NonPagedPoolUsage;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct _OBJECT_ATTRIBUTES_T
|
||||
{
|
||||
ULONG Length;
|
||||
T RootDirectory;
|
||||
T ObjectName;
|
||||
ULONG Attributes;
|
||||
T SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
|
||||
T SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
|
||||
};
|
||||
|
||||
struct _XSAVE_FORMAT64
|
||||
{
|
||||
WORD ControlWord;
|
||||
@@ -679,37 +690,47 @@ namespace blackbone
|
||||
T PatchInformation;
|
||||
};
|
||||
|
||||
typedef struct _RTL_INVERTED_FUNCTION_TABLE_ENTRY
|
||||
template<typename T>
|
||||
struct _RTL_INVERTED_FUNCTION_TABLE_ENTRY
|
||||
{
|
||||
PIMAGE_RUNTIME_FUNCTION_ENTRY ExceptionDirectory;
|
||||
PVOID ImageBase;
|
||||
T ExceptionDirectory; // PIMAGE_RUNTIME_FUNCTION_ENTRY
|
||||
T ImageBase;
|
||||
ULONG ImageSize;
|
||||
ULONG SizeOfTable;
|
||||
} RTL_INVERTED_FUNCTION_TABLE_ENTRY, *PRTL_INVERTED_FUNCTION_TABLE_ENTRY;
|
||||
};
|
||||
|
||||
typedef _UNICODE_STRING_T<DWORD> _UNICODE_STRING32;
|
||||
typedef _UNICODE_STRING_T<DWORD64> _UNICODE_STRING64;
|
||||
typedef _UNICODE_STRING_T<DWORD_PTR> UNICODE_STRING_T;
|
||||
|
||||
typedef _PEB_T<DWORD, DWORD64, 34> _PEB32;
|
||||
typedef _PEB_T<DWORD64, DWORD, 30> _PEB64;
|
||||
typedef _PEB_T<DWORD, DWORD64, 34> _PEB32;
|
||||
typedef _PEB_T<DWORD64, DWORD, 30> _PEB64;
|
||||
|
||||
typedef _TEB_T<DWORD> _TEB32;
|
||||
typedef _TEB_T<DWORD64> _TEB64;
|
||||
typedef _TEB_T<DWORD_PTR> TEB_T;
|
||||
#ifdef USE64
|
||||
typedef _PEB64 PEB_T;
|
||||
#else
|
||||
typedef _PEB32 PEB_T;
|
||||
#endif
|
||||
|
||||
typedef _PEB_LDR_DATA2<DWORD> _PEB_LDR_DATA232;
|
||||
typedef _PEB_LDR_DATA2<DWORD64> _PEB_LDR_DATA264;
|
||||
typedef _PEB_LDR_DATA2<DWORD_PTR> PEB_LDR_DATA_T;
|
||||
typedef _TEB_T<DWORD> _TEB32;
|
||||
typedef _TEB_T<DWORD64> _TEB64;
|
||||
typedef _TEB_T<DWORD_PTR> TEB_T;
|
||||
|
||||
typedef _LDR_DATA_TABLE_ENTRY_BASE<DWORD> _LDR_DATA_TABLE_ENTRY_BASE32;
|
||||
typedef _LDR_DATA_TABLE_ENTRY_BASE<DWORD64> _LDR_DATA_TABLE_ENTRY_BASE64;
|
||||
typedef _LDR_DATA_TABLE_ENTRY_BASE<DWORD_PTR> LDR_DATA_TABLE_ENTRY_BASE_T;
|
||||
typedef _PEB_LDR_DATA2<DWORD> _PEB_LDR_DATA232;
|
||||
typedef _PEB_LDR_DATA2<DWORD64> _PEB_LDR_DATA264;
|
||||
typedef _PEB_LDR_DATA2<DWORD_PTR> PEB_LDR_DATA_T;
|
||||
|
||||
typedef _CONTEXT_T<DWORD> _CONTEXT32;
|
||||
typedef _CONTEXT_T<DWORD64> _CONTEXT64;
|
||||
typedef _CONTEXT_T<DWORD_PTR> CONTEXT_T;
|
||||
typedef _LDR_DATA_TABLE_ENTRY_BASE<DWORD> _LDR_DATA_TABLE_ENTRY_BASE32;
|
||||
typedef _LDR_DATA_TABLE_ENTRY_BASE<DWORD64> _LDR_DATA_TABLE_ENTRY_BASE64;
|
||||
typedef _LDR_DATA_TABLE_ENTRY_BASE<DWORD_PTR> LDR_DATA_TABLE_ENTRY_BASE_T;
|
||||
|
||||
typedef _CONTEXT_T<DWORD> _CONTEXT32;
|
||||
typedef _CONTEXT_T<DWORD64> _CONTEXT64;
|
||||
typedef _CONTEXT_T<DWORD_PTR> CONTEXT_T;
|
||||
|
||||
typedef _SECTION_BASIC_INFORMATION_T<DWORD> _SECTION_BASIC_INFORMATION32;
|
||||
typedef _SECTION_BASIC_INFORMATION_T<DWORD64> _SECTION_BASIC_INFORMATION64;
|
||||
typedef _SECTION_BASIC_INFORMATION_T<DWORD_PTR> SECTION_BASIC_INFORMATION_T;
|
||||
typedef _SECTION_BASIC_INFORMATION_T<DWORD_PTR> SECTION_BASIC_INFORMATION_T;
|
||||
|
||||
typedef _SYSTEM_HANDLE_INFORMATION_T<DWORD> _SYSTEM_HANDLE_INFORMATION32;
|
||||
typedef _SYSTEM_HANDLE_INFORMATION_T<DWORD64> _SYSTEM_HANDLE_INFORMATION64;
|
||||
@@ -719,11 +740,13 @@ namespace blackbone
|
||||
typedef _OBJECT_TYPE_INFORMATION_T<DWORD64> _OBJECT_TYPE_INFORMATION64;
|
||||
typedef _OBJECT_TYPE_INFORMATION_T<DWORD_PTR> OBJECT_TYPE_INFORMATION_T;
|
||||
|
||||
#ifdef USE64
|
||||
typedef _PEB64 PEB_T;
|
||||
#else
|
||||
typedef _PEB32 PEB_T;
|
||||
#endif
|
||||
typedef _OBJECT_ATTRIBUTES_T<DWORD> _OBJECT_ATTRIBUTES32;
|
||||
typedef _OBJECT_ATTRIBUTES_T<DWORD64> _OBJECT_ATTRIBUTES64;
|
||||
typedef _OBJECT_ATTRIBUTES_T<DWORD_PTR> OBJECT_ATTRIBUTES_T;
|
||||
|
||||
typedef _ACTCTXW_T<DWORD> _ACTCTXW32;
|
||||
typedef _ACTCTXW_T<DWORD64> _ACTCTXW64;
|
||||
typedef _ACTCTXW_T<DWORD_PTR> ACTCTXW_T;
|
||||
}
|
||||
|
||||
#include "ApiSet.h"
|
||||
|
||||
@@ -16,14 +16,14 @@ namespace blackbone
|
||||
_LARGE_INTEGER LoadTime;
|
||||
};
|
||||
|
||||
typedef struct _RTL_INVERTED_FUNCTION_TABLE7
|
||||
template<typename T>
|
||||
struct _RTL_INVERTED_FUNCTION_TABLE7
|
||||
{
|
||||
ULONG Count;
|
||||
ULONG MaxCount;
|
||||
ULONG Epoch;
|
||||
RTL_INVERTED_FUNCTION_TABLE_ENTRY Entries[0x200];
|
||||
|
||||
} RTL_INVERTED_FUNCTION_TABLE7, *PRTL_INVERTED_FUNCTION_TABLE7;
|
||||
_RTL_INVERTED_FUNCTION_TABLE_ENTRY<T> Entries[0x200];
|
||||
};
|
||||
|
||||
#pragma warning(default : 4201)
|
||||
}
|
||||
@@ -72,15 +72,15 @@ namespace blackbone
|
||||
unsigned long ImplicitPathOptions;
|
||||
};
|
||||
|
||||
typedef struct _RTL_INVERTED_FUNCTION_TABLE8
|
||||
template<typename T>
|
||||
struct _RTL_INVERTED_FUNCTION_TABLE8
|
||||
{
|
||||
ULONG Count;
|
||||
ULONG MaxCount;
|
||||
ULONG Epoch;
|
||||
UCHAR Overflow;
|
||||
RTL_INVERTED_FUNCTION_TABLE_ENTRY Entries[0x200];
|
||||
|
||||
} RTL_INVERTED_FUNCTION_TABLE8, *PRTL_INVERTED_FUNCTION_TABLE8;
|
||||
_RTL_INVERTED_FUNCTION_TABLE_ENTRY<T> Entries[0x200];
|
||||
};
|
||||
|
||||
#pragma warning(default : 4201)
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "../Process/Process.h"
|
||||
#include "../Include/Macro.h"
|
||||
#include "../Misc/Trace.hpp"
|
||||
#include "../Misc/PattrernLoader.h"
|
||||
#include "../Asm/LDasm.h"
|
||||
|
||||
namespace blackbone
|
||||
@@ -217,7 +218,7 @@ MExcept::~MExcept()
|
||||
/// <param name="mt">Mosule type</param>
|
||||
/// <param name="partial">Partial exception support</param>
|
||||
/// <returns>Error code</returns>
|
||||
NTSTATUS MExcept::CreateVEH( uintptr_t pTargetBase, size_t imageSize, eModType mt, bool partial )
|
||||
NTSTATUS MExcept::CreateVEH( ptr_t pTargetBase, size_t imageSize, eModType mt, bool partial )
|
||||
{
|
||||
auto a = AsmFactory::GetAssembler( mt );
|
||||
uint64_t result = 0;
|
||||
@@ -304,7 +305,7 @@ NTSTATUS MExcept::CreateVEH( uintptr_t pTargetBase, size_t imageSize, eModType m
|
||||
// LdrpInvertedFunctionTable
|
||||
if (*(uint32_t*)pData == 0xDEADDA7A)
|
||||
{
|
||||
*(uint32_t*)pData = static_cast<uint32_t>(_proc.nativeLdr().LdrpInvertedFunctionTable());
|
||||
*(uint32_t*)pData = static_cast<uint32_t>(g_PatternLoader->data().LdrpInvertedFunctionTable32);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -318,7 +319,7 @@ NTSTATUS MExcept::CreateVEH( uintptr_t pTargetBase, size_t imageSize, eModType m
|
||||
// LdrProtectMrdata address
|
||||
if (*(uint32_t*)pData == 0xDEADC0D2)
|
||||
{
|
||||
*(uint32_t*)pData = static_cast<uint32_t>(_proc.nativeLdr().LdrProtectMrdata());
|
||||
*(uint32_t*)pData = static_cast<uint32_t>(g_PatternLoader->data().LdrProtectMrdata);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -344,10 +345,17 @@ NTSTATUS MExcept::CreateVEH( uintptr_t pTargetBase, size_t imageSize, eModType m
|
||||
_proc.remote().AddReturnWithEvent( *a, mt );
|
||||
a->GenEpilogue();
|
||||
|
||||
_proc.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result );
|
||||
_hVEH = result;
|
||||
|
||||
return (_hVEH == 0 ? STATUS_NOT_FOUND : STATUS_SUCCESS);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -43,7 +43,7 @@ protected:
|
||||
/// <param name="mt">Mosule type</param>
|
||||
/// <param name="partial">Partial exception support</param>
|
||||
/// <returns>Error code</returns>
|
||||
BLACKBONE_API NTSTATUS CreateVEH( uintptr_t pTargetBase, size_t imageSize, eModType mt, bool partial );
|
||||
BLACKBONE_API NTSTATUS CreateVEH( ptr_t pTargetBase, size_t imageSize, eModType mt, bool partial );
|
||||
|
||||
/// <summary>
|
||||
/// Removes VEH from target process
|
||||
|
||||
@@ -362,7 +362,7 @@ call_result_t<ModuleDataPtr> MMap::FindOrMapModule(
|
||||
ldrEntry.baseAddress = pImage->imgMem.ptr();
|
||||
ldrEntry.size = pImage->peImage.imageSize();
|
||||
|
||||
BLACKBONE_TRACE( L"ManualMap: Image base allocated at 0x%p", pImage->imgMem.ptr<uintptr_t>() );
|
||||
BLACKBONE_TRACE( L"ManualMap: Image base allocated at 0x%016x", pImage->imgMem.ptr() );
|
||||
|
||||
// Create Activation context for SxS
|
||||
if (pImage->peImage.manifestID() == 0)
|
||||
@@ -661,7 +661,7 @@ NTSTATUS MMap::RelocateImage( ImageContext* pImage )
|
||||
BLACKBONE_TRACE( L"ManualMap: Relocating image '%ls'", pImage->ldrEntry.fullPath.c_str() );
|
||||
|
||||
// Reloc delta
|
||||
uintptr_t Delta = pImage->imgMem.ptr<uintptr_t>() - static_cast<uintptr_t>(pImage->peImage.imageBase());
|
||||
ptr_t Delta = pImage->imgMem.ptr() - pImage->peImage.imageBase();
|
||||
|
||||
// No need to relocate
|
||||
if (Delta == 0)
|
||||
@@ -681,6 +681,10 @@ NTSTATUS MMap::RelocateImage( ImageContext* pImage )
|
||||
return STATUS_INVALID_IMAGE_FORMAT;
|
||||
}
|
||||
|
||||
size_t size = sizeof( ptr_t );
|
||||
if (pImage->ldrEntry.type == mt_mod32)
|
||||
size = sizeof( uint32_t );
|
||||
|
||||
while ((uintptr_t)fixrec < end && fixrec->BlockSize)
|
||||
{
|
||||
DWORD count = (fixrec->BlockSize - 8) >> 1; // records count
|
||||
@@ -698,14 +702,15 @@ NTSTATUS MMap::RelocateImage( ImageContext* pImage )
|
||||
if (fixtype == IMAGE_REL_BASED_HIGHLOW || fixtype == IMAGE_REL_BASED_DIR64)
|
||||
{
|
||||
uintptr_t fixRVA = fixoffset + fixrec->PageRVA;
|
||||
uintptr_t val = *reinterpret_cast<uintptr_t*>(pImage->peImage.ResolveRVAToVA( fixoffset + fixrec->PageRVA )) + Delta;
|
||||
ptr_t val = pImage->peImage.ResolveRVAToVA( fixoffset + fixrec->PageRVA );
|
||||
val = pImage->ldrEntry.type == mt_mod64 ? *reinterpret_cast<uint64_t*>(val) : *reinterpret_cast<uint32_t*>(val);
|
||||
val += Delta;
|
||||
|
||||
auto status = STATUS_SUCCESS;
|
||||
|
||||
if (pImage->flags & HideVAD)
|
||||
status = Driver().WriteMem( _process.pid(), pImage->imgMem.ptr() + fixRVA, sizeof( val ), &val );
|
||||
status = Driver().WriteMem( _process.pid(), pImage->imgMem.ptr() + fixRVA, size, &val );
|
||||
else
|
||||
status = pImage->imgMem.Write( fixRVA, val );
|
||||
status = pImage->imgMem.Write( fixRVA, size, &val );
|
||||
|
||||
// Apply relocation
|
||||
if (!NT_SUCCESS( status ))
|
||||
@@ -744,11 +749,17 @@ call_result_t<ModuleDataPtr> MMap::FindOrMapDependency( ImageContext* pImage, st
|
||||
|
||||
BLACKBONE_TRACE( L"ManualMap: Loading new dependency '%ls'", path.c_str() );
|
||||
|
||||
auto flags = NameResolve::EnsureFullPath;
|
||||
|
||||
// Wow64 fs redirection
|
||||
if (pImage->ldrEntry.type == mt_mod32 && !_process.barrier().sourceWow64)
|
||||
flags = static_cast<NameResolve::eResolveFlag>(static_cast<int32_t>(flags) | NameResolve::Wow64);
|
||||
|
||||
auto basedir = pImage->peImage.noPhysFile() ? Utils::GetExeDirectory() : Utils::GetParent( pImage->ldrEntry.fullPath );
|
||||
auto status = NameResolve::Instance().ResolvePath(
|
||||
path, pImage->ldrEntry.name,
|
||||
basedir,
|
||||
NameResolve::EnsureFullPath,
|
||||
path,
|
||||
pImage->ldrEntry.name,
|
||||
basedir, flags,
|
||||
_process.pid(),
|
||||
pImage->peImage.actx()
|
||||
);
|
||||
@@ -803,6 +814,10 @@ NTSTATUS MMap::ResolveImport( ImageContext* pImage, bool useDelayed /*= false */
|
||||
if (imports.empty())
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
auto entrySize = sizeof( uint32_t );
|
||||
if (pImage->ldrEntry.type == mt_mod64)
|
||||
entrySize = sizeof( uint64_t );
|
||||
|
||||
// Traverse entries
|
||||
for (auto& importMod : imports)
|
||||
{
|
||||
@@ -864,12 +879,9 @@ NTSTATUS MMap::ResolveImport( ImageContext* pImage, bool useDelayed /*= false */
|
||||
auto status = STATUS_SUCCESS;
|
||||
|
||||
if (pImage->flags & HideVAD)
|
||||
{
|
||||
uintptr_t address = static_cast<uintptr_t>(expData->procAddress);
|
||||
status = Driver().WriteMem( _process.pid(), pImage->imgMem.ptr() + importFn.ptrRVA, sizeof( address ), &address );
|
||||
}
|
||||
status = Driver().WriteMem( _process.pid(), pImage->imgMem.ptr() + importFn.ptrRVA, entrySize, &expData->procAddress );
|
||||
else
|
||||
status = pImage->imgMem.Write( importFn.ptrRVA, static_cast<uintptr_t>(expData->procAddress) );
|
||||
status = pImage->imgMem.Write( importFn.ptrRVA, entrySize, &expData->procAddress );
|
||||
|
||||
// Write function address
|
||||
if (!NT_SUCCESS( status ))
|
||||
@@ -1012,44 +1024,54 @@ NTSTATUS MMap::InitStaticTLS( ImageContext* pImage )
|
||||
/// <returns>Status code</returns>
|
||||
NTSTATUS MMap::InitializeCookie( ImageContext* pImage )
|
||||
{
|
||||
auto pLoadConfig = reinterpret_cast<PIMAGE_LOAD_CONFIG_DIRECTORY>(pImage->peImage.DirectoryAddress( IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG ));
|
||||
auto pLoadConfig32 = reinterpret_cast<PIMAGE_LOAD_CONFIG_DIRECTORY32>(pImage->peImage.DirectoryAddress( IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG ));
|
||||
auto pLoadConfig64 = reinterpret_cast<PIMAGE_LOAD_CONFIG_DIRECTORY64>(pLoadConfig32);
|
||||
|
||||
ptr_t pCookie = pLoadConfig32->SecurityCookie;
|
||||
if (pImage->ldrEntry.type == mt_mod64)
|
||||
pCookie = pLoadConfig64->SecurityCookie;
|
||||
|
||||
//
|
||||
// Cookie generation based on MSVC++ compiler
|
||||
//
|
||||
if (pLoadConfig && pLoadConfig->SecurityCookie)
|
||||
if (pLoadConfig32 && pCookie)
|
||||
{
|
||||
BLACKBONE_TRACE( L"ManualMap: Performing security cookie initializtion for image '%ls'", pImage->ldrEntry.name.c_str() );
|
||||
|
||||
FILETIME systime = { 0 };
|
||||
LARGE_INTEGER PerformanceCount = { { 0 } };
|
||||
uintptr_t cookie = 0;
|
||||
size_t size = sizeof( uint32_t );
|
||||
|
||||
GetSystemTimeAsFileTime( &systime );
|
||||
QueryPerformanceCounter( &PerformanceCount );
|
||||
|
||||
cookie = _process.pid() ^ _process.remote().getWorker()->id() ^ reinterpret_cast<uintptr_t>(&cookie);
|
||||
ptr_t cookie = _process.pid() ^ _process.remote().getWorker()->id() ^ reinterpret_cast<uintptr_t>(&cookie);
|
||||
|
||||
#ifdef USE64
|
||||
cookie ^= *reinterpret_cast<uint64_t*>(&systime);
|
||||
cookie ^= (PerformanceCount.QuadPart << 32) ^ PerformanceCount.QuadPart;
|
||||
cookie &= 0xFFFFFFFFFFFF;
|
||||
if (pImage->ldrEntry.type == mt_mod64)
|
||||
{
|
||||
size = sizeof( uint64_t );
|
||||
|
||||
if (cookie == 0x2B992DDFA232)
|
||||
cookie++;
|
||||
#else
|
||||
cookie ^= *reinterpret_cast<uint64_t*>(&systime);
|
||||
cookie ^= (PerformanceCount.QuadPart << 32) ^ PerformanceCount.QuadPart;
|
||||
cookie &= 0xFFFFFFFFFFFF;
|
||||
|
||||
cookie ^= systime.dwHighDateTime ^ systime.dwLowDateTime;
|
||||
cookie ^= PerformanceCount.LowPart;
|
||||
cookie ^= PerformanceCount.HighPart;
|
||||
if (cookie == 0x2B992DDFA232)
|
||||
cookie++;
|
||||
}
|
||||
else
|
||||
{
|
||||
cookie &= 0xFFFFFFFF;
|
||||
cookie ^= systime.dwHighDateTime ^ systime.dwLowDateTime;
|
||||
cookie ^= PerformanceCount.LowPart;
|
||||
cookie ^= PerformanceCount.HighPart;
|
||||
|
||||
if (cookie == 0xBB40E64E)
|
||||
cookie++;
|
||||
else if (!(cookie & 0xFFFF0000))
|
||||
cookie |= (cookie | 0x4711) << 16;
|
||||
#endif
|
||||
if (cookie == 0xBB40E64E)
|
||||
cookie++;
|
||||
else if (!(cookie & 0xFFFF0000))
|
||||
cookie |= (cookie | 0x4711) << 16;
|
||||
}
|
||||
|
||||
return _process.memory().Write( REBASE( pLoadConfig->SecurityCookie, pImage->peImage.imageBase(), pImage->imgMem.ptr<ptr_t>() ), cookie );
|
||||
return _process.memory().Write( REBASE( pCookie, pImage->peImage.imageBase(), pImage->imgMem.ptr() ), size, &cookie );
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
@@ -1106,7 +1128,7 @@ call_result_t<uint64_t> MMap::RunModuleInitializers( ImageContext* pImage, DWORD
|
||||
if (!( pImage->flags & NoTLS ))
|
||||
for (auto& pCallback : pImage->tlsCallbacks)
|
||||
{
|
||||
BLACKBONE_TRACE( L"ManualMap: Calling TLS callback at 0x%p for '%ls', Reason: %d",
|
||||
BLACKBONE_TRACE( L"ManualMap: Calling TLS callback at 0x%016llx for '%ls', Reason: %d",
|
||||
pCallback, pImage->ldrEntry.name.c_str(), dwReason );
|
||||
|
||||
a->GenCall( pCallback, { pImage->imgMem.ptr(), dwReason, customArgumentsAddress } );
|
||||
@@ -1134,7 +1156,7 @@ call_result_t<uint64_t> MMap::RunModuleInitializers( ImageContext* pImage, DWORD
|
||||
if (!( pImage->flags & NoTLS ))
|
||||
for (auto& pCallback : pImage->tlsCallbacks)
|
||||
{
|
||||
BLACKBONE_TRACE( L"ManualMap: Calling TLS callback at 0x%p for '%ls', Reason: %d",
|
||||
BLACKBONE_TRACE( L"ManualMap: Calling TLS callback at 0x%016llx for '%ls', Reason: %d",
|
||||
pCallback, pImage->ldrEntry.name.c_str(), dwReason );
|
||||
|
||||
a->GenCall( pCallback, { pImage->imgMem.ptr(), dwReason, customArgumentsAddress } );
|
||||
@@ -1180,7 +1202,6 @@ NTSTATUS MMap::CreateActx( const pe::PEImage& image )
|
||||
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
uint64_t result = 0;
|
||||
ACTCTXW act = { 0 };
|
||||
|
||||
auto mem = _process.memory().Allocate( 512, PAGE_READWRITE );
|
||||
if (!mem)
|
||||
@@ -1188,16 +1209,6 @@ NTSTATUS MMap::CreateActx( const pe::PEImage& image )
|
||||
|
||||
_pAContext = std::move( mem.result() );
|
||||
|
||||
act.cbSize = sizeof( act );
|
||||
act.lpSource = reinterpret_cast<LPCWSTR>(_pAContext.ptr<uintptr_t>() + sizeof( ptr_t ) + sizeof( act ));
|
||||
|
||||
// Ignore some fields for pure manifest file
|
||||
if (!image.noPhysFile())
|
||||
{
|
||||
act.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
|
||||
act.lpResourceName = MAKEINTRESOURCEW( image.manifestID() );
|
||||
}
|
||||
|
||||
bool switchMode = image.mType() == mt_mod64 && _process.core().isWow64();
|
||||
auto pCreateActx = _process.modules().GetExport( _process.modules().GetModule( L"kernel32.dll" ), "CreateActCtxW" );
|
||||
if (!pCreateActx)
|
||||
@@ -1251,8 +1262,37 @@ NTSTATUS MMap::CreateActx( const pe::PEImage& image )
|
||||
// Native way
|
||||
else
|
||||
{
|
||||
a->GenPrologue();
|
||||
auto fillACTX = [this, &image]( auto&& act )
|
||||
{
|
||||
memset( &act, 0, sizeof( act ) );
|
||||
|
||||
act.cbSize = sizeof( act );
|
||||
act.lpSource = static_cast<decltype(act.lpSource)>(this->_pAContext.ptr() + sizeof( ptr_t ) + sizeof( act ));
|
||||
|
||||
// Ignore some fields for pure manifest file
|
||||
if (!image.noPhysFile())
|
||||
{
|
||||
act.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
|
||||
act.lpResourceName = static_cast<decltype(act.lpResourceName)>(image.manifestID());
|
||||
}
|
||||
|
||||
// Write path to file
|
||||
NTSTATUS status = this->_pAContext.Write( sizeof( ptr_t ), act );
|
||||
status |= this->_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() );
|
||||
|
||||
a->GenPrologue();
|
||||
a->GenCall( pCreateActx->procAddress, { _pAContext.ptr() + sizeof( ptr_t ) } );
|
||||
|
||||
(*a)->mov( (*a)->zdx, _pAContext.ptr() );
|
||||
@@ -1261,14 +1301,6 @@ NTSTATUS MMap::CreateActx( const pe::PEImage& image )
|
||||
_process.remote().AddReturnWithEvent( *a );
|
||||
a->GenEpilogue();
|
||||
|
||||
// Write path to file
|
||||
_pAContext.Write( sizeof( ptr_t ), act );
|
||||
_pAContext.Write(
|
||||
sizeof( ptr_t ) + sizeof(act),
|
||||
(image.manifestFile().length() + 1) * sizeof(wchar_t),
|
||||
image.manifestFile().c_str()
|
||||
);
|
||||
|
||||
status = _process.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result );
|
||||
if (!NT_SUCCESS( status ))
|
||||
return status;
|
||||
|
||||
@@ -32,7 +32,6 @@ bool NtLdr::Init()
|
||||
if (IsWindows8OrGreater())
|
||||
FindLdrpModuleIndexBase();
|
||||
|
||||
ScanPatterns();
|
||||
_nodeMap.clear();
|
||||
|
||||
// Report errors
|
||||
@@ -64,41 +63,42 @@ bool NtLdr::CreateNTReference( NtLdrEntry& mod )
|
||||
// Win 8 and higher
|
||||
if (IsWindows8OrGreater())
|
||||
{
|
||||
ULONG hash = 0;
|
||||
_LDR_DATA_TABLE_ENTRY_W8 *pEntry = InitW8Node( mod );
|
||||
_nodeMap.emplace( std::make_pair( mod.baseAddress, pEntry ) );
|
||||
ptr_t entry = InitW8Node( mod );
|
||||
_nodeMap.emplace( std::make_pair( mod.baseAddress, entry ) );
|
||||
|
||||
// Insert into LdrpHashTable
|
||||
if (mod.flags & Ldr_HashTable)
|
||||
InsertHashNode( GET_FIELD_PTR( pEntry, HashLinks ), hash );
|
||||
InsertHashNode( fieldPtr( entry, &_LDR_DATA_TABLE_ENTRY_W8::HashLinks ), mod.hash );
|
||||
|
||||
// Insert into module graph
|
||||
if (mod.flags & Ldr_ModList)
|
||||
InsertTreeNode( pEntry, mod );
|
||||
InsertTreeNode( entry, mod );
|
||||
|
||||
if (mod.flags & Ldr_ThdCall)
|
||||
{
|
||||
_process.memory().Write( GET_FIELD_PTR( pEntry, Flags ), 0x80004 );
|
||||
InsertMemModuleNode( 0, GET_FIELD_PTR( pEntry, InLoadOrderLinks ),
|
||||
GET_FIELD_PTR( pEntry, InInitializationOrderLinks ) );
|
||||
_process.memory().Write( fieldPtr( entry, &_LDR_DATA_TABLE_ENTRY_W8::Flags ), 0x80004 );
|
||||
InsertMemModuleNode<DWORD_PTR>(
|
||||
0,
|
||||
fieldPtr( entry, &_LDR_DATA_TABLE_ENTRY_W8::InLoadOrderLinks ),
|
||||
fieldPtr( entry, &_LDR_DATA_TABLE_ENTRY_W8::InInitializationOrderLinks )
|
||||
);
|
||||
}
|
||||
}
|
||||
// Windows 7 and earlier
|
||||
else
|
||||
{
|
||||
ULONG hash = 0;
|
||||
_LDR_DATA_TABLE_ENTRY_W7 *pEntry = InitW7Node( mod );
|
||||
_nodeMap.emplace( std::make_pair( mod.baseAddress, pEntry ) );
|
||||
_nodeMap.emplace( std::make_pair( mod.baseAddress, reinterpret_cast<ptr_t>(pEntry) ) );
|
||||
|
||||
// Insert into LdrpHashTable
|
||||
if (mod.flags & Ldr_HashTable)
|
||||
InsertHashNode( GET_FIELD_PTR( pEntry, HashLinks ), hash );
|
||||
InsertHashNode( GET_FIELD_PTR( pEntry, HashLinks ), mod.hash );
|
||||
|
||||
// Insert into LDR list
|
||||
if (mod.flags & (Ldr_ModList | Ldr_ThdCall))
|
||||
{
|
||||
_process.memory().Write( GET_FIELD_PTR( pEntry, Flags ), 0x80004 );
|
||||
InsertMemModuleNode( 0, GET_FIELD_PTR( pEntry, InLoadOrderLinks ), 0 );
|
||||
InsertMemModuleNode<DWORD_PTR>( 0, GET_FIELD_PTR( pEntry, InLoadOrderLinks ), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,16 +111,16 @@ bool NtLdr::CreateNTReference( NtLdrEntry& mod )
|
||||
/// <param name="ptr">node pointer (if nullptr - new dummy node is allocated)</param>
|
||||
/// <param name="pModule">Module base address</param>
|
||||
/// <returns>Node address</returns>
|
||||
template<typename T>
|
||||
T* NtLdr::SetNode( T* ptr, void* pModule )
|
||||
template<typename T, typename T2>
|
||||
ptr_t NtLdr::SetNode( ptr_t ptr, T2 pModule )
|
||||
{
|
||||
if(ptr == nullptr)
|
||||
if(ptr == 0)
|
||||
{
|
||||
auto mem = _process.memory().Allocate( sizeof( T ), PAGE_READWRITE, 0, false );
|
||||
if (!mem)
|
||||
return nullptr;
|
||||
return 0;
|
||||
|
||||
ptr = mem->ptr<T*>();
|
||||
ptr = mem->ptr();
|
||||
mem->Write( FIELD_OFFSET2( T*, DllBase ), pModule );
|
||||
}
|
||||
|
||||
@@ -136,20 +136,29 @@ T* NtLdr::SetNode( T* ptr, void* pModule )
|
||||
NTSTATUS NtLdr::AddStaticTLSEntry( NtLdrEntry& mod, IMAGE_TLS_DIRECTORY *pTls )
|
||||
{
|
||||
bool wxp = IsWindowsXPOrGreater() && !IsWindowsVistaOrGreater();
|
||||
void* pNode = _nodeMap.count( mod.baseAddress ) ? _nodeMap[mod.baseAddress] : nullptr;
|
||||
ptr_t pNode = _nodeMap.count( mod.baseAddress ) ? _nodeMap[mod.baseAddress] : 0;
|
||||
|
||||
// Allocate appropriate structure
|
||||
if ((pNode = SetNode( reinterpret_cast<PLDR_DATA_TABLE_ENTRY>(pNode), reinterpret_cast<void*>(mod.baseAddress) )) == nullptr)
|
||||
ptr_t LdrpHandleTlsData = 0;
|
||||
if (mod.type == mt_mod64)
|
||||
{
|
||||
LdrpHandleTlsData = g_PatternLoader->data().LdrpHandleTlsData64;
|
||||
pNode = SetNode<_LDR_DATA_TABLE_ENTRY_BASE64>( pNode, mod.baseAddress );
|
||||
}
|
||||
else
|
||||
{
|
||||
LdrpHandleTlsData = g_PatternLoader->data().LdrpHandleTlsData32;
|
||||
pNode = SetNode<_LDR_DATA_TABLE_ENTRY_BASE32>( pNode, mod.baseAddress );
|
||||
}
|
||||
|
||||
if (pNode == 0)
|
||||
return STATUS_NO_MEMORY;
|
||||
|
||||
// Manually add TLS table
|
||||
if ((wxp || _process.barrier().type == wow_64_32) && pTls != nullptr)
|
||||
if (wxp && pTls != nullptr)
|
||||
{
|
||||
ptr_t pTeb = 0;
|
||||
if (wxp)
|
||||
pTeb = _process.remote().getWorker()->teb( static_cast<_TEB32*>(nullptr) );
|
||||
else
|
||||
pTeb = _process.remote().getWorker()->teb( static_cast<_TEB64*>(nullptr) );
|
||||
pTeb = _process.remote().getWorker()->teb( static_cast<_TEB32*>(nullptr) );
|
||||
|
||||
auto mem = _process.memory().Allocate( 0x1000, PAGE_READWRITE, 0, false );
|
||||
if (!mem)
|
||||
@@ -168,22 +177,17 @@ NTSTATUS NtLdr::AddStaticTLSEntry( NtLdrEntry& mod, IMAGE_TLS_DIRECTORY *pTls )
|
||||
tlsStore.Write( 0, tlsStore.ptr<uintptr_t>() + 0x800 );
|
||||
tlsStore.Write( 0x800, size, buf.get() );
|
||||
|
||||
if (wxp)
|
||||
_process.memory().Write( pTeb + FIELD_OFFSET( _TEB32, ThreadLocalStoragePointer ), tlsStore.ptr<DWORD32>() );
|
||||
else
|
||||
_process.memory().Write( pTeb + FIELD_OFFSET( _TEB64, ThreadLocalStoragePointer ), tlsStore.ptr<DWORD64>() );
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
return _process.memory().Write( pTeb + FIELD_OFFSET( _TEB32, ThreadLocalStoragePointer ), tlsStore.ptr<uint32_t>() );
|
||||
}
|
||||
|
||||
// Use native method
|
||||
if (_LdrpHandleTlsData)
|
||||
if (LdrpHandleTlsData)
|
||||
{
|
||||
auto a = AsmFactory::GetAssembler( mod.type );
|
||||
uint64_t result = 0;
|
||||
|
||||
a->GenPrologue();
|
||||
a->GenCall( _LdrpHandleTlsData, { reinterpret_cast<uintptr_t>(pNode) }, IsWindows8Point1OrGreater() ? cc_thiscall : cc_stdcall );
|
||||
a->GenCall( LdrpHandleTlsData, { pNode }, IsWindows8Point1OrGreater() ? cc_thiscall : cc_stdcall );
|
||||
_process.remote().AddReturnWithEvent( *a );
|
||||
a->GenEpilogue();
|
||||
|
||||
@@ -197,6 +201,7 @@ NTSTATUS NtLdr::AddStaticTLSEntry( NtLdrEntry& mod, IMAGE_TLS_DIRECTORY *pTls )
|
||||
return STATUS_ORDINAL_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create module record in LdrpInvertedFunctionTable
|
||||
/// Used to create fake SAFESEH entries
|
||||
@@ -205,84 +210,109 @@ NTSTATUS NtLdr::AddStaticTLSEntry( NtLdrEntry& mod, IMAGE_TLS_DIRECTORY *pTls )
|
||||
/// <returns>true on success</returns>
|
||||
bool NtLdr::InsertInvertedFunctionTable( NtLdrEntry& mod )
|
||||
{
|
||||
RTL_INVERTED_FUNCTION_TABLE7 table = { 0 };
|
||||
PRTL_INVERTED_FUNCTION_TABLE_ENTRY Entries = nullptr;
|
||||
uint64_t result = 0;
|
||||
auto a = AsmFactory::GetAssembler( mod.type );
|
||||
auto& memory = _process.memory();
|
||||
|
||||
// Invalid addresses. Probably pattern scan has failed
|
||||
if (_RtlInsertInvertedFunctionTable == 0 || _LdrpInvertedFunctionTable == 0)
|
||||
return false;
|
||||
|
||||
if (IsWindows8OrGreater())
|
||||
Entries = reinterpret_cast<decltype(Entries)>(GET_FIELD_PTR( reinterpret_cast<PRTL_INVERTED_FUNCTION_TABLE8>(&table), Entries ));
|
||||
else
|
||||
Entries = reinterpret_cast<decltype(Entries)>(GET_FIELD_PTR( &table, Entries ));
|
||||
|
||||
//
|
||||
// Try to find module address in table.
|
||||
// If found - no additional work is required.
|
||||
//
|
||||
memory.Read( _LdrpInvertedFunctionTable, sizeof( table ), &table );
|
||||
for (ULONG i = 0; i < table.Count; i++)
|
||||
if (reinterpret_cast<ptr_t>(Entries[i].ImageBase) == mod.baseAddress)
|
||||
return true;
|
||||
|
||||
a->GenPrologue();
|
||||
|
||||
if (IsWindows8Point1OrGreater())
|
||||
a->GenCall( _RtlInsertInvertedFunctionTable, { mod.baseAddress, mod.size }, cc_fastcall );
|
||||
else if (IsWindows8OrGreater())
|
||||
a->GenCall( _RtlInsertInvertedFunctionTable, { mod.baseAddress, mod.size } );
|
||||
else
|
||||
a->GenCall( _RtlInsertInvertedFunctionTable, { _LdrpInvertedFunctionTable, mod.baseAddress, mod.size } );
|
||||
|
||||
_process.remote().AddReturnWithEvent( *a );
|
||||
a->GenEpilogue();
|
||||
|
||||
_process.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result );
|
||||
memory.Read( _LdrpInvertedFunctionTable, sizeof( table ), &table );
|
||||
|
||||
for(DWORD i = 0; i < table.Count; i++)
|
||||
ptr_t RtlInsertInvertedFunctionTable = g_PatternLoader->data().RtlInsertInvertedFunctionTable64;
|
||||
ptr_t LdrpInvertedFunctionTable = g_PatternLoader->data().LdrpInvertedFunctionTable64;
|
||||
if (mod.type == mt_mod32)
|
||||
{
|
||||
if (reinterpret_cast<ptr_t>(Entries[i].ImageBase) != mod.baseAddress)
|
||||
continue;
|
||||
|
||||
// If Image has SAFESEH, RtlInsertInvertedFunctionTable is enough
|
||||
if (Entries[i].SizeOfTable != 0)
|
||||
return mod.safeSEH = true;
|
||||
|
||||
//
|
||||
// Create fake Exception directory
|
||||
// Directory will be filled later, during exception handling
|
||||
//
|
||||
PIMAGE_RUNTIME_FUNCTION_ENTRY pImgEntry = nullptr;
|
||||
|
||||
// Allocate memory for 512 possible handlers
|
||||
auto mem = memory.Allocate( sizeof( DWORD ) * 0x200, PAGE_READWRITE, 0, false );
|
||||
if (!mem)
|
||||
return false;
|
||||
|
||||
pImgEntry = mem->ptr<decltype(pImgEntry)>();
|
||||
auto pEncoded = EncodeSystemPointer( pImgEntry );
|
||||
|
||||
// m_LdrpInvertedFunctionTable->Entries[i].ExceptionDirectory
|
||||
uintptr_t field_ofst = reinterpret_cast<uintptr_t>(&Entries[i].ExceptionDirectory)
|
||||
- reinterpret_cast<uintptr_t>(&table);
|
||||
|
||||
// In Win10 _LdrpInvertedFunctionTable is located in mrdata section
|
||||
// mrdata is read-only by default
|
||||
// LdrProtectMrdata is used to make it writable when needed
|
||||
DWORD flOld = 0;
|
||||
memory.Protect( _LdrpInvertedFunctionTable + field_ofst, sizeof( uintptr_t ), PAGE_EXECUTE_READWRITE, &flOld );
|
||||
auto status = memory.Write( _LdrpInvertedFunctionTable + field_ofst, pEncoded );
|
||||
memory.Protect( _LdrpInvertedFunctionTable + field_ofst, sizeof( uintptr_t ), flOld, &flOld );
|
||||
|
||||
return NT_SUCCESS( status );
|
||||
RtlInsertInvertedFunctionTable = g_PatternLoader->data().RtlInsertInvertedFunctionTable32;
|
||||
LdrpInvertedFunctionTable = g_PatternLoader->data().LdrpInvertedFunctionTable32;
|
||||
}
|
||||
|
||||
return false;
|
||||
// Invalid addresses. Probably pattern scan has failed
|
||||
if (RtlInsertInvertedFunctionTable == 0 || LdrpInvertedFunctionTable == 0)
|
||||
return false;
|
||||
|
||||
auto InsertP = [&]( auto&& table )
|
||||
{
|
||||
uint64_t result = 0;
|
||||
auto a = AsmFactory::GetAssembler( mod.type );
|
||||
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;
|
||||
|
||||
a->GenPrologue();
|
||||
|
||||
if (IsWindows8Point1OrGreater())
|
||||
a->GenCall( RtlInsertInvertedFunctionTable, { mod.baseAddress, mod.size }, cc_fastcall );
|
||||
else if (IsWindows8OrGreater())
|
||||
a->GenCall( RtlInsertInvertedFunctionTable, { mod.baseAddress, mod.size } );
|
||||
else
|
||||
a->GenCall( RtlInsertInvertedFunctionTable, { LdrpInvertedFunctionTable, mod.baseAddress, mod.size } );
|
||||
|
||||
_process.remote().AddReturnWithEvent( *a );
|
||||
a->GenEpilogue();
|
||||
|
||||
_process.remote().ExecInWorkerThread( (*a)->make(), (*a)->getCodeSize(), result );
|
||||
memory.Read( LdrpInvertedFunctionTable, sizeof( table ), &table );
|
||||
|
||||
for (DWORD i = 0; i < table.Count; i++)
|
||||
{
|
||||
if (table.Entries[i].ImageBase != mod.baseAddress)
|
||||
continue;
|
||||
|
||||
// If Image has SAFESEH, RtlInsertInvertedFunctionTable is enough
|
||||
if (table.Entries[i].SizeOfTable != 0)
|
||||
return mod.safeSEH = true;
|
||||
|
||||
//
|
||||
// Create fake Exception directory
|
||||
// Directory will be filled later, during exception handling
|
||||
//
|
||||
|
||||
// Allocate memory for 2048 possible handlers
|
||||
auto mem = memory.Allocate( sizeof( DWORD ) * 0x800, PAGE_READWRITE, 0, false );
|
||||
if (!mem)
|
||||
return false;
|
||||
|
||||
uint32_t size = 0;
|
||||
ptr_t pEncoded = 0;
|
||||
auto cookie = *reinterpret_cast<uint32_t*>(0x7FFE0330);
|
||||
if (mod.type == mt_mod64)
|
||||
{
|
||||
size = sizeof( uint64_t );
|
||||
pEncoded = _rotr64( cookie ^ mem->ptr<uint64_t>(), cookie & 0x3F );
|
||||
}
|
||||
else
|
||||
{
|
||||
size = sizeof( uint32_t );
|
||||
pEncoded = _rotr( cookie ^ mem->ptr<uint32_t>(), cookie & 0x1F );
|
||||
}
|
||||
|
||||
// m_LdrpInvertedFunctionTable->Entries[i].ExceptionDirectory
|
||||
uintptr_t field_ofst = reinterpret_cast<ptr_t>(&table.Entries[i].ExceptionDirectory)
|
||||
- reinterpret_cast<ptr_t>(&table);
|
||||
|
||||
// In Win10 LdrpInvertedFunctionTable is located in mrdata section
|
||||
// mrdata is read-only by default
|
||||
// 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.Protect( LdrpInvertedFunctionTable + field_ofst, sizeof( ptr_t ), flOld, &flOld );
|
||||
|
||||
return NT_SUCCESS( status );
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (IsWindows8OrGreater())
|
||||
{
|
||||
if(mod.type == mt_mod64)
|
||||
return InsertP( _RTL_INVERTED_FUNCTION_TABLE8<DWORD64>() );
|
||||
else
|
||||
return InsertP( _RTL_INVERTED_FUNCTION_TABLE8<DWORD>() );
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mod.type == mt_mod64)
|
||||
return InsertP( _RTL_INVERTED_FUNCTION_TABLE7<DWORD64>() );
|
||||
else
|
||||
return InsertP( _RTL_INVERTED_FUNCTION_TABLE7<DWORD>() );
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -290,82 +320,71 @@ bool NtLdr::InsertInvertedFunctionTable( NtLdrEntry& mod )
|
||||
/// </summary>
|
||||
/// <param name="mod">Module data</param>
|
||||
/// <returns>Pointer to created entry</returns>
|
||||
_LDR_DATA_TABLE_ENTRY_W8* NtLdr::InitW8Node( NtLdrEntry& mod )
|
||||
ptr_t NtLdr::InitW8Node( NtLdrEntry& mod )
|
||||
{
|
||||
UNICODE_STRING strLocal = { 0 };
|
||||
typedef _LDR_DATA_TABLE_ENTRY_W8 EntryType;
|
||||
|
||||
ptr_t entry = AllocateInHeap( mod.type, sizeof( EntryType ) ).result( 0 );
|
||||
ptr_t pDdagNode = AllocateInHeap( mod.type, sizeof( _LDR_DDAG_NODE ) ).result( 0 );
|
||||
if (!entry || !pDdagNode)
|
||||
return 0;
|
||||
|
||||
// Allocate space for Unicode string
|
||||
UNICODE_STRING strLocal = { 0 };
|
||||
auto mem = _process.memory().Allocate( 0x1000, PAGE_READWRITE, 0, false );
|
||||
if (!mem)
|
||||
return nullptr;
|
||||
return 0;
|
||||
|
||||
auto StringBuf = std::move( mem.result() );
|
||||
_LDR_DATA_TABLE_ENTRY_W8* pEntry = reinterpret_cast<decltype(pEntry)>(AllocateInHeap(
|
||||
mod.type, sizeof( *pEntry )
|
||||
).result( 0 ));
|
||||
|
||||
if(pEntry)
|
||||
{
|
||||
_LDR_DDAG_NODE* pDdagNode = reinterpret_cast<decltype(pDdagNode)>(AllocateInHeap(
|
||||
mod.type, sizeof( *pDdagNode )
|
||||
).result(0));
|
||||
// pEntry->DllBase = ModuleBase;
|
||||
_process.memory().Write( fieldPtr( entry, &EntryType::DllBase ), static_cast<uintptr_t>(mod.baseAddress) );
|
||||
|
||||
if(pDdagNode)
|
||||
{
|
||||
// pEntry->DllBase = ModuleBase;
|
||||
_process.memory().Write( GET_FIELD_PTR( pEntry, DllBase ), static_cast<uintptr_t>(mod.baseAddress) );
|
||||
// pEntry->SizeOfImage = ImageSize;
|
||||
_process.memory().Write( fieldPtr( entry, &EntryType::SizeOfImage ), mod.size );
|
||||
|
||||
// pEntry->SizeOfImage = ImageSize;
|
||||
_process.memory().Write( GET_FIELD_PTR( pEntry, SizeOfImage ), mod.size );
|
||||
// pEntry->EntryPoint = entryPoint;
|
||||
_process.memory().Write( fieldPtr( entry, &EntryType::EntryPoint ), static_cast<uintptr_t>(mod.entryPoint) );
|
||||
|
||||
// pEntry->EntryPoint = entryPoint;
|
||||
_process.memory().Write( GET_FIELD_PTR( pEntry, EntryPoint ), static_cast<uintptr_t>(mod.entryPoint) );
|
||||
// Dll name and name hash
|
||||
SAFE_CALL( RtlInitUnicodeString, &strLocal, mod.name.c_str() );
|
||||
mod.hash = HashString( mod.name );
|
||||
|
||||
// Dll name and name hash
|
||||
SAFE_CALL( RtlInitUnicodeString, &strLocal, mod.name.c_str() );
|
||||
mod.hash = HashString( mod.name );
|
||||
// Write into buffer
|
||||
strLocal.Buffer = StringBuf.ptr<PWSTR>();
|
||||
StringBuf.Write( 0, mod.name.length() * sizeof( wchar_t ) + 2, mod.name.c_str() );
|
||||
|
||||
// Write into buffer
|
||||
strLocal.Buffer = StringBuf.ptr<PWSTR>();
|
||||
StringBuf.Write( 0, mod.name.length() * sizeof(wchar_t) + 2, mod.name.c_str() );
|
||||
// pEntry->BaseDllName = strLocal;
|
||||
_process.memory().Write( fieldPtr( entry, &EntryType::BaseDllName ), strLocal );
|
||||
|
||||
// pEntry->BaseDllName = strLocal;
|
||||
_process.memory().Write( GET_FIELD_PTR( pEntry, BaseDllName ), strLocal );
|
||||
// Dll full path
|
||||
SAFE_CALL( RtlInitUnicodeString, &strLocal, mod.fullPath.c_str() );
|
||||
strLocal.Buffer = reinterpret_cast<PWSTR>(StringBuf.ptr<uint8_t*>() + 0x800);
|
||||
StringBuf.Write( 0x800, mod.fullPath.length() * sizeof( wchar_t ) + 2, mod.fullPath.c_str() );
|
||||
|
||||
// Dll full path
|
||||
SAFE_CALL( RtlInitUnicodeString, &strLocal, mod.fullPath.c_str() );
|
||||
strLocal.Buffer = reinterpret_cast<PWSTR>(StringBuf.ptr<uint8_t*>() + 0x800);
|
||||
StringBuf.Write( 0x800, mod.fullPath.length() * sizeof(wchar_t) + 2, mod.fullPath.c_str() );
|
||||
|
||||
// pEntry->FullDllName = strLocal;
|
||||
_process.memory().Write( GET_FIELD_PTR( pEntry, FullDllName ), strLocal );
|
||||
// pEntry->FullDllName = strLocal;
|
||||
_process.memory().Write( fieldPtr( entry, &EntryType::FullDllName ), strLocal );
|
||||
|
||||
// pEntry->BaseNameHashValue = hash;
|
||||
_process.memory().Write( GET_FIELD_PTR( pEntry, BaseNameHashValue ), mod.hash );
|
||||
// pEntry->BaseNameHashValue = hash;
|
||||
_process.memory().Write( fieldPtr( entry, &EntryType::BaseNameHashValue ), mod.hash );
|
||||
|
||||
//
|
||||
// Ddag node
|
||||
//
|
||||
//
|
||||
// Ddag node
|
||||
//
|
||||
|
||||
// pEntry->DdagNode = pDdagNode;
|
||||
_process.memory().Write( GET_FIELD_PTR( pEntry, DdagNode ), pDdagNode );
|
||||
// pEntry->DdagNode = pDdagNode;
|
||||
_process.memory().Write( fieldPtr( entry, &EntryType::DdagNode ), pDdagNode );
|
||||
|
||||
// pDdagNode->State = LdrModulesReadyToRun;
|
||||
_process.memory().Write( GET_FIELD_PTR( pDdagNode, State ), LdrModulesReadyToRun );
|
||||
// pDdagNode->State = LdrModulesReadyToRun;
|
||||
_process.memory().Write( fieldPtr( pDdagNode, &_LDR_DDAG_NODE::State ), LdrModulesReadyToRun );
|
||||
|
||||
// pDdagNode->ReferenceCount = 1;
|
||||
_process.memory().Write( GET_FIELD_PTR( pDdagNode, ReferenceCount ), 1 );
|
||||
// pDdagNode->ReferenceCount = 1;
|
||||
_process.memory().Write( fieldPtr( pDdagNode, &_LDR_DDAG_NODE::ReferenceCount ), 1 );
|
||||
|
||||
// pDdagNode->LoadCount = -1;
|
||||
_process.memory().Write( GET_FIELD_PTR( pDdagNode, LoadCount ), -1 );
|
||||
// pDdagNode->LoadCount = -1;
|
||||
_process.memory().Write( fieldPtr( pDdagNode, &_LDR_DDAG_NODE::LoadCount ), -1 );
|
||||
|
||||
return pEntry;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -438,7 +457,7 @@ _LDR_DATA_TABLE_ENTRY_W7* NtLdr::InitW7Node( NtLdrEntry& mod )
|
||||
/// </summary>
|
||||
/// <param name="pNode">Node to insert</param>
|
||||
/// <param name="mod">Module data</param>
|
||||
void NtLdr::InsertTreeNode( _LDR_DATA_TABLE_ENTRY_W8* pNode, const NtLdrEntry& mod )
|
||||
void NtLdr::InsertTreeNode( ptr_t pNode, const NtLdrEntry& mod )
|
||||
{
|
||||
//
|
||||
// Win8 module tree
|
||||
@@ -503,9 +522,9 @@ void NtLdr::InsertTreeNode( _LDR_DATA_TABLE_ENTRY_W8* pNode, const NtLdrEntry& m
|
||||
a->GenCall( static_cast<uintptr_t>(RtlRbInsertNodeEx->procAddress),
|
||||
{
|
||||
_LdrpModuleIndexBase,
|
||||
GET_FIELD_PTR( pLdrNode, BaseAddressIndexNode ),
|
||||
fieldPtr( (ptr_t)pLdrNode, &_LDR_DATA_TABLE_ENTRY_W8::BaseAddressIndexNode ),
|
||||
static_cast<uintptr_t>(bRight),
|
||||
GET_FIELD_PTR( pNode, BaseAddressIndexNode )
|
||||
fieldPtr( (ptr_t)pNode, &_LDR_DATA_TABLE_ENTRY_W8::BaseAddressIndexNode )
|
||||
} );
|
||||
|
||||
_process.remote().AddReturnWithEvent( *a, mod.type );
|
||||
@@ -519,27 +538,28 @@ void NtLdr::InsertTreeNode( _LDR_DATA_TABLE_ENTRY_W8* pNode, const NtLdrEntry& m
|
||||
/// </summary>
|
||||
/// <param name="pNodeMemoryOrderLink">InMemoryOrderModuleList link of entry to be inserted</param>
|
||||
/// <param name="pNodeLoadOrderLink">InLoadOrderModuleList link of entry to be inserted</param>
|
||||
void NtLdr::InsertMemModuleNode( uintptr_t pNodeMemoryOrderLink, uintptr_t pNodeLoadOrderLink, uintptr_t pNodeInitOrderLink )
|
||||
template<typename T>
|
||||
void NtLdr::InsertMemModuleNode( ptr_t pNodeMemoryOrderLink, ptr_t pNodeLoadOrderLink, ptr_t pNodeInitOrderLink )
|
||||
{
|
||||
PEB_T* pPeb = reinterpret_cast<PEB_T*>(_process.core().peb());
|
||||
PEB_LDR_DATA_T* pLdr = nullptr;
|
||||
ptr_t pPeb = _process.core().peb( (_PEB_T2<T>::type*)nullptr );
|
||||
ptr_t pLdr = 0;
|
||||
|
||||
if (pPeb)
|
||||
pLdr = _process.memory().Read<PEB_LDR_DATA_T*>( GET_FIELD_PTR( pPeb, Ldr ) ).result( nullptr );
|
||||
pLdr = _process.memory().Read<T>( fieldPtr( pPeb, &PEB_T::Ldr ) ).result( 0 );
|
||||
|
||||
if (pLdr)
|
||||
{
|
||||
// pLdr->InMemoryOrderModuleList
|
||||
if (pNodeMemoryOrderLink)
|
||||
InsertTailList( GET_FIELD_PTR( pLdr, InMemoryOrderModuleList ), pNodeMemoryOrderLink );
|
||||
InsertTailList<T>( fieldPtr( pLdr, &_PEB_LDR_DATA2<T>::InMemoryOrderModuleList ), pNodeMemoryOrderLink );
|
||||
|
||||
// pLdr->InLoadOrderModuleList
|
||||
if (pNodeLoadOrderLink)
|
||||
InsertTailList( GET_FIELD_PTR( pLdr, InLoadOrderModuleList ), pNodeLoadOrderLink );
|
||||
InsertTailList<T>( fieldPtr( pLdr, &_PEB_LDR_DATA2<T>::InLoadOrderModuleList ), pNodeLoadOrderLink );
|
||||
|
||||
// pLdr->InInitializationOrderModuleList
|
||||
if (pNodeInitOrderLink)
|
||||
InsertTailList( GET_FIELD_PTR( pLdr, InInitializationOrderModuleList ), pNodeInitOrderLink );
|
||||
InsertTailList<T>( fieldPtr( pLdr, &_PEB_LDR_DATA2<T>::InInitializationOrderModuleList ), pNodeInitOrderLink );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,14 +568,14 @@ void NtLdr::InsertMemModuleNode( uintptr_t pNodeMemoryOrderLink, uintptr_t pNode
|
||||
/// </summary>
|
||||
/// <param name="pNodeLink">Link of entry to be inserted</param>
|
||||
/// <param name="hash">Module hash</param>
|
||||
void NtLdr::InsertHashNode( uintptr_t pNodeLink, ULONG hash )
|
||||
void NtLdr::InsertHashNode( ptr_t pNodeLink, ULONG hash )
|
||||
{
|
||||
if(pNodeLink)
|
||||
{
|
||||
// LrpHashTable record
|
||||
auto pHashList = _process.memory().Read<uintptr_t>( _LdrpHashTable + sizeof(LIST_ENTRY)*(hash & 0x1F) );
|
||||
if(pHashList)
|
||||
InsertTailList( pHashList.result(), pNodeLink );
|
||||
InsertTailList<DWORD_PTR>( pHashList.result(), pNodeLink );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,20 +584,21 @@ void NtLdr::InsertHashNode( uintptr_t pNodeLink, ULONG hash )
|
||||
/// </summary>
|
||||
/// <param name="ListHead">List head pointer</param>
|
||||
/// <param name="Entry">Entry list link to be inserted</param>
|
||||
void NtLdr::InsertTailList( uintptr_t ListHead, uintptr_t Entry )
|
||||
template<typename T>
|
||||
void NtLdr::InsertTailList( ptr_t ListHead, ptr_t Entry )
|
||||
{
|
||||
// PrevEntry = ListHead->Blink;
|
||||
auto PrevEntry = _process.memory().Read<uintptr_t>( GET_FIELD_PTR( reinterpret_cast<PLIST_ENTRY>(ListHead), Blink ) ).result( 0 );
|
||||
auto PrevEntry = _process.memory().Read<T>( fieldPtr( ListHead, &_LIST_ENTRY_T<T>::Blink ) ).result( 0 );
|
||||
|
||||
// Entry->Flink = ListHead;
|
||||
// Entry->Blink = PrevEntry;
|
||||
_process.memory().Write( GET_FIELD_PTR( reinterpret_cast<PLIST_ENTRY>(Entry), Flink ), ListHead );
|
||||
_process.memory().Write( GET_FIELD_PTR( reinterpret_cast<PLIST_ENTRY>(Entry), Blink ), PrevEntry );
|
||||
_process.memory().Write( fieldPtr( Entry, &_LIST_ENTRY_T<T>::Flink ), sizeof(T), &ListHead );
|
||||
_process.memory().Write( fieldPtr( Entry, &_LIST_ENTRY_T<T>::Blink ), sizeof( T ), &PrevEntry );
|
||||
|
||||
// PrevEntry->Flink = Entry;
|
||||
// ListHead->Blink = Entry;
|
||||
_process.memory().Write( GET_FIELD_PTR( reinterpret_cast<PLIST_ENTRY>(PrevEntry), Flink ), Entry );
|
||||
_process.memory().Write( GET_FIELD_PTR( reinterpret_cast<PLIST_ENTRY>(ListHead), Blink ), Entry );
|
||||
_process.memory().Write( fieldPtr( PrevEntry, &_LIST_ENTRY_T<T>::Flink ), sizeof( T ), &Entry );
|
||||
_process.memory().Write( fieldPtr( ListHead, &_LIST_ENTRY_T<T>::Blink ), sizeof( T ), &Entry );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -735,36 +756,6 @@ bool NtLdr::FindLdrpModuleIndexBase()
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Search for RtlInsertInvertedFunctionTable, LdrpInvertedFunctionTable, LdrpHandleTlsData.
|
||||
/// </summary>
|
||||
/// <returns>true on success</returns>
|
||||
bool NtLdr::ScanPatterns( )
|
||||
{
|
||||
auto& data = g_PatternLoader->data();
|
||||
|
||||
_LdrKernel32PatchAddress = static_cast<uintptr_t>(data.LdrKernel32PatchAddress);
|
||||
_APC64PatchAddress = static_cast<uintptr_t>(data.APC64PatchAddress);
|
||||
_LdrProtectMrdata = static_cast<uintptr_t>(data.LdrProtectMrdata);
|
||||
|
||||
if (_process.core().isWow64())
|
||||
{
|
||||
_LdrpHandleTlsData = static_cast<uintptr_t>(data.LdrpHandleTlsData32);
|
||||
_LdrpInvertedFunctionTable = static_cast<uintptr_t>(data.LdrpInvertedFunctionTable32);
|
||||
_RtlInsertInvertedFunctionTable = static_cast<uintptr_t>(data.RtlInsertInvertedFunctionTable32);
|
||||
}
|
||||
else
|
||||
{
|
||||
_LdrpHandleTlsData = static_cast<uintptr_t>(data.LdrpHandleTlsData64);
|
||||
_LdrpInvertedFunctionTable = static_cast<uintptr_t>(data.LdrpInvertedFunctionTable64);
|
||||
_RtlInsertInvertedFunctionTable = static_cast<uintptr_t>(data.RtlInsertInvertedFunctionTable64);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Find Loader heap base
|
||||
/// </summary>
|
||||
|
||||
@@ -70,16 +70,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="mod">Module data</param>
|
||||
/// <returns>true on success</returns>
|
||||
BLACKBONE_API bool Unlink( const ModuleData& mod );
|
||||
|
||||
//
|
||||
// Get some not exported values
|
||||
//
|
||||
BLACKBONE_API inline uintptr_t LdrpInvertedFunctionTable( ) const { return _LdrpInvertedFunctionTable; }
|
||||
BLACKBONE_API inline uintptr_t LdrKernel32PatchAddress() const { return _LdrKernel32PatchAddress; }
|
||||
BLACKBONE_API inline uintptr_t APC64PatchAddress() const { return _APC64PatchAddress; }
|
||||
BLACKBONE_API inline uintptr_t LdrProtectMrdata() const { return _LdrProtectMrdata; }
|
||||
|
||||
BLACKBONE_API bool Unlink( const ModuleData& mod );
|
||||
private:
|
||||
|
||||
/// <summary>
|
||||
@@ -94,12 +85,6 @@ private:
|
||||
/// <returns>true on success</returns>
|
||||
bool FindLdrpModuleIndexBase();
|
||||
|
||||
/// <summary>
|
||||
/// Search for RtlInsertInvertedFunctionTable, LdrpInvertedFunctionTable, LdrpHandleTlsDatas.
|
||||
/// </summary>
|
||||
/// <returns>true on success</returns>
|
||||
bool ScanPatterns();
|
||||
|
||||
/// <summary>
|
||||
/// Find Loader heap base
|
||||
/// </summary>
|
||||
@@ -111,7 +96,7 @@ private:
|
||||
/// </summary>
|
||||
/// <param name="mod">Module data</param>
|
||||
/// <returns>Pointer to created entry</returns>
|
||||
_LDR_DATA_TABLE_ENTRY_W8* InitW8Node( NtLdrEntry& mod );
|
||||
ptr_t InitW8Node( NtLdrEntry& mod );
|
||||
|
||||
/// <summary>
|
||||
/// Initialize OS-specific module entry
|
||||
@@ -125,28 +110,30 @@ private:
|
||||
/// </summary>
|
||||
/// <param name="pNode">Node to insert</param>
|
||||
/// <param name="mod">Module data</param>
|
||||
void InsertTreeNode( _LDR_DATA_TABLE_ENTRY_W8* pNode, const NtLdrEntry& mod );
|
||||
void InsertTreeNode( ptr_t pNode, const NtLdrEntry& mod );
|
||||
|
||||
/// <summary>
|
||||
/// Insert entry into LdrpHashTable[]
|
||||
/// </summary>
|
||||
/// <param name="pNodeLink">Link of entry to be inserted</param>
|
||||
/// <param name="hash">Module hash</param>
|
||||
void InsertHashNode( uintptr_t pNodeLink, ULONG hash );
|
||||
void InsertHashNode( ptr_t pNodeLink, ULONG hash );
|
||||
|
||||
/// <summary>
|
||||
/// Insert entry into InLoadOrderModuleList and InMemoryOrderModuleList
|
||||
/// </summary>
|
||||
/// <param name="pNodeMemoryOrderLink">InMemoryOrderModuleList link of entry to be inserted</param>
|
||||
/// <param name="pNodeLoadOrderLink">InLoadOrderModuleList link of entry to be inserted</param>
|
||||
void InsertMemModuleNode( uintptr_t pNodeMemoryOrderLink, uintptr_t pNodeLoadOrderLink, uintptr_t pNodeInitOrderLink );
|
||||
template<typename T>
|
||||
void InsertMemModuleNode( ptr_t pNodeMemoryOrderLink, ptr_t pNodeLoadOrderLink, ptr_t pNodeInitOrderLink );
|
||||
|
||||
/// <summary>
|
||||
/// Insert entry into standard double linked list
|
||||
/// </summary>
|
||||
/// <param name="ListHead">List head pointer</param>
|
||||
/// <param name="Entry">Entry list link to be inserted</param>
|
||||
void InsertTailList( uintptr_t ListHead, uintptr_t Entry );
|
||||
template<typename T>
|
||||
void InsertTailList( ptr_t ListHead, ptr_t Entry );
|
||||
|
||||
/// <summary>
|
||||
/// Hash image name
|
||||
@@ -169,8 +156,8 @@ private:
|
||||
/// <param name="ptr">node pointer (if nullptr - new dummy node is allocated)</param>
|
||||
/// <param name="pModule">Module base address</param>
|
||||
/// <returns>Node address</returns>
|
||||
template<typename T>
|
||||
T* SetNode( T* ptr, void* pModule );
|
||||
template<typename T, typename T2>
|
||||
ptr_t SetNode( ptr_t ptr, T2 pModule );
|
||||
|
||||
/// <summary>
|
||||
/// Unlink module from PEB_LDR_DATA
|
||||
@@ -206,19 +193,13 @@ private:
|
||||
NtLdr& operator =(const NtLdr&) = delete;
|
||||
|
||||
private:
|
||||
class Process& _process; // Process memory routines
|
||||
class Process& _process; // Process memory routines
|
||||
|
||||
uintptr_t _LdrpHashTable = 0; // LdrpHashTable address
|
||||
uintptr_t _LdrpModuleIndexBase = 0; // LdrpModuleIndex address
|
||||
uintptr_t _LdrHeapBase = 0; // Loader heap base address
|
||||
uintptr_t _LdrKernel32PatchAddress = 0; // Address to patch to enable kernel32 loading under win7
|
||||
uintptr_t _APC64PatchAddress = 0; // Address to patch for x64->WOW64 APC dispatching under win7
|
||||
uintptr_t _LdrpHandleTlsData = 0; // LdrpHandleTlsData address
|
||||
uintptr_t _LdrpInvertedFunctionTable = 0; // LdrpInvertedFunctionTable address
|
||||
uintptr_t _RtlInsertInvertedFunctionTable = 0; // RtlInsertInvertedFunctionTable address
|
||||
uintptr_t _LdrProtectMrdata = 0; // LdrProtectMrdata address
|
||||
uintptr_t _LdrpHashTable = 0; // LdrpHashTable address
|
||||
uintptr_t _LdrpModuleIndexBase = 0; // LdrpModuleIndex address
|
||||
uintptr_t _LdrHeapBase = 0; // Loader heap base address
|
||||
|
||||
std::map<ptr_t, void*> _nodeMap; // Allocated native structures
|
||||
std::map<ptr_t, ptr_t> _nodeMap; // Allocated native structures
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
/// <param name="...args">Function args</param>
|
||||
/// <returns>Function result or 0 if import not found</returns>
|
||||
template<typename T, typename... Args>
|
||||
inline auto safeCall( const std::string& name, Args&&... args ) -> typename std::result_of<T(Args...)>::type
|
||||
inline auto safeCall( const std::string& name, Args&&... args )
|
||||
{
|
||||
auto pfn = DynImport::get<T>( name );
|
||||
return pfn ? pfn( std::forward<Args>( args )... ) : (std::result_of<T( Args... )>::type)(0);
|
||||
|
||||
@@ -189,7 +189,10 @@ NTSTATUS NameResolve::ResolvePath(
|
||||
dwSize = 255;
|
||||
|
||||
// In Win10 DllDirectory value got screwed, so less reliable method is used
|
||||
GetSystemDirectoryW( sys_path, dwSize );
|
||||
if (flags & Wow64)
|
||||
GetSystemWow64DirectoryW( sys_path, dwSize );
|
||||
else
|
||||
GetSystemDirectoryW( sys_path, dwSize );
|
||||
|
||||
if (res == ERROR_SUCCESS)
|
||||
{
|
||||
@@ -232,7 +235,10 @@ NTSTATUS NameResolve::ResolvePath(
|
||||
//
|
||||
// 4. The system directory
|
||||
//
|
||||
GetSystemDirectoryW( tmpPath, ARRAYSIZE( tmpPath ) );
|
||||
if (flags & Wow64)
|
||||
GetSystemWow64DirectoryW( tmpPath, ARRAYSIZE( tmpPath ) );
|
||||
else
|
||||
GetSystemDirectoryW( tmpPath, ARRAYSIZE( tmpPath ) );
|
||||
|
||||
completePath = std::wstring( tmpPath ) + L"\\" + filename;
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ PEImage::PEImage( void )
|
||||
|
||||
PEImage::~PEImage( void )
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "RPC/RemoteExec.h"
|
||||
#include "../Misc/NameResolve.h"
|
||||
#include "../Misc/Utils.h"
|
||||
#include "../Misc/PattrernLoader.h"
|
||||
#include "../Asm/AsmFactory.h"
|
||||
|
||||
#include <memory>
|
||||
@@ -18,10 +19,6 @@
|
||||
|
||||
namespace blackbone
|
||||
{
|
||||
|
||||
typedef std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(&CloseHandle)> handlePtr;
|
||||
|
||||
|
||||
ProcessModules::ProcessModules( class Process& proc )
|
||||
: _proc( proc )
|
||||
, _memory( _proc.memory() )
|
||||
@@ -437,7 +434,7 @@ call_result_t<ModuleDataPtr> ProcessModules::Inject( const std::wstring& path )
|
||||
if (switchMode == ForceSwitch && !_ldrPatched && IsWindows7OrGreater() && !IsWindows8OrGreater())
|
||||
{
|
||||
uint8_t patch[] = { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };
|
||||
auto patchBase = _proc.nativeLdr().LdrKernel32PatchAddress();
|
||||
auto patchBase = g_PatternLoader->data().LdrKernel32PatchAddress;
|
||||
|
||||
if (patchBase != 0)
|
||||
{
|
||||
@@ -870,7 +867,7 @@ bool ProcessModules::InjectPureIL(
|
||||
|
||||
// write JIT code to target
|
||||
size_t codeSize = a->getCodeSize();
|
||||
uintptr_t codeAddress = address.ptr<uintptr_t>() + offset;
|
||||
ptr_t codeAddress = address.ptr() + offset;
|
||||
|
||||
std::vector<uint8_t> codeBuffer( codeSize );
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "RemoteExec.h"
|
||||
#include "../Process.h"
|
||||
#include "../../Misc/DynImport.h"
|
||||
#include "../../Misc/PattrernLoader.h"
|
||||
|
||||
#include <VersionHelpers.h>
|
||||
#include <sddl.h>
|
||||
@@ -113,6 +114,9 @@ NTSTATUS RemoteExec::ExecInWorkerThread( PVOID pCode, size_t size, uint64_t& cal
|
||||
{
|
||||
NTSTATUS dwResult = STATUS_SUCCESS;
|
||||
|
||||
if (_proc.barrier().type == wow_64_32)
|
||||
return ExecInAnyThread( pCode, size, callResult, _hWorkThd );
|
||||
|
||||
// Create thread if needed
|
||||
CreateRPCEnvironment();
|
||||
|
||||
@@ -130,7 +134,7 @@ NTSTATUS RemoteExec::ExecInWorkerThread( PVOID pCode, size_t size, uint64_t& cal
|
||||
{
|
||||
if (_proc.barrier().type == wow_64_32)
|
||||
{
|
||||
auto patchBase = _proc.nativeLdr().APC64PatchAddress();
|
||||
auto patchBase = g_PatternLoader->data().APC64PatchAddress;
|
||||
|
||||
if (patchBase != 0)
|
||||
{
|
||||
@@ -150,8 +154,8 @@ 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???
|
||||
auto pRemoteCode = _userCode.ptr<PVOID>();
|
||||
if (NT_SUCCESS( SAFE_NATIVE_CALL( NtQueueApcThread, _hWorkThd->handle(), pRemoteCode, pRemoteCode, nullptr, nullptr ) ))
|
||||
auto pRemoteCode = _userCode.ptr();
|
||||
if (NT_SUCCESS( _proc.core().native()->NtQueueApcThreadT( _hWorkThd->handle(), pRemoteCode, pRemoteCode ) ))
|
||||
{
|
||||
dwResult = WaitForSingleObject( _hWaitEvent, 30 * 1000 /*wait 30s*/ );
|
||||
callResult = _userData.Read<uint64_t>( RET_OFFSET, 0 );
|
||||
@@ -275,7 +279,7 @@ NTSTATUS RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callRe
|
||||
if (NT_SUCCESS( status ))
|
||||
{
|
||||
WaitForSingleObject( _hWaitEvent, INFINITE );
|
||||
status = _userData.Read( INTRET_OFFSET, callResult );
|
||||
status = _userData.Read( RET_OFFSET, callResult );
|
||||
}
|
||||
|
||||
return status;
|
||||
@@ -358,16 +362,7 @@ NTSTATUS RemoteExec::CreateRPCEnvironment( bool bThread /*= true*/, bool bEvent
|
||||
|
||||
// Create RPC sync event
|
||||
if (bEvent)
|
||||
{
|
||||
if (_proc.barrier().type != wow_32_64)
|
||||
{
|
||||
status = CreateAPCEvent( thdID );
|
||||
}
|
||||
else
|
||||
{
|
||||
status = STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
status = CreateAPCEvent( thdID );
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -386,8 +381,7 @@ call_result_t<DWORD> RemoteExec::CreateWorkerThread()
|
||||
//
|
||||
if(!_hWorkThd->valid())
|
||||
{
|
||||
eModType mt = mt_default;
|
||||
if (_proc.barrier().type == wow_64_32)
|
||||
/*if (_proc.barrier().type == wow_64_32)
|
||||
{
|
||||
a->SwitchTo64();
|
||||
|
||||
@@ -405,9 +399,9 @@ call_result_t<DWORD> RemoteExec::CreateWorkerThread()
|
||||
(*a)->mov( (*a)->zdx, asmjit::host::dword_ptr_abs( 0x18 ).setSegment( asmjit::host::fs ) );
|
||||
(*a)->mov( (*a)->intptr_ptr( (*a)->zdx, 0x2c8 ), (*a)->zax );
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
auto ntdll = _mods.GetModule( L"ntdll.dll", Sections, mt );
|
||||
auto ntdll = _mods.GetModule( L"ntdll.dll", Sections );
|
||||
auto proc = _mods.GetExport( ntdll, "NtDelayExecution" );
|
||||
auto pExitThread = _mods.GetExport( ntdll, "NtTerminateThread" );
|
||||
if (!proc || !pExitThread)
|
||||
@@ -462,11 +456,6 @@ NTSTATUS RemoteExec::CreateAPCEvent( DWORD threadID )
|
||||
OBJECT_ATTRIBUTES obAttr = { 0 };
|
||||
UNICODE_STRING ustr = { 0 };
|
||||
|
||||
// Detect ntdll type
|
||||
eModType mt = mt_default;
|
||||
if (_proc.barrier().type == wow_64_32)
|
||||
mt = mt_mod64;
|
||||
|
||||
// Event name
|
||||
swprintf_s( pEventName, ARRAYSIZE( pEventName ), L"\\BaseNamedObjects\\_MMapEvent_0x%x_0x%x", threadID, GetTickCount() );
|
||||
|
||||
@@ -476,14 +465,14 @@ NTSTATUS RemoteExec::CreateAPCEvent( DWORD threadID )
|
||||
|
||||
// Prepare Arguments
|
||||
ustr.Length = static_cast<USHORT>(wcslen( pEventName ) * sizeof(wchar_t));
|
||||
ustr.MaximumLength = static_cast<USHORT>(sizeof(pEventName));
|
||||
ustr.MaximumLength = static_cast<USHORT>(len);
|
||||
ustr.Buffer = pEventName;
|
||||
|
||||
obAttr.ObjectName = &ustr;
|
||||
obAttr.Length = sizeof(obAttr);
|
||||
obAttr.SecurityDescriptor = pDescriptor;
|
||||
|
||||
auto pOpenEvent = _mods.GetNtdllExport( "NtOpenEvent", mt, Sections );
|
||||
auto pOpenEvent = _mods.GetNtdllExport( "NtOpenEvent", mt_default, Sections );
|
||||
if (!pOpenEvent)
|
||||
return pOpenEvent.status;
|
||||
|
||||
@@ -494,9 +483,36 @@ NTSTATUS RemoteExec::CreateAPCEvent( DWORD threadID )
|
||||
if (!NT_SUCCESS( status ))
|
||||
return status;
|
||||
|
||||
ustr.Buffer = reinterpret_cast<PWSTR>(_userData.ptr<uintptr_t>() + ARGS_OFFSET + sizeof( obAttr ) + sizeof( ustr ));
|
||||
obAttr.ObjectName = reinterpret_cast<PUNICODE_STRING>(_userData.ptr<uintptr_t>() + ARGS_OFFSET + sizeof(obAttr));
|
||||
obAttr.SecurityDescriptor = nullptr;
|
||||
auto fillAttributes = [this, len, pEventName]( auto& obAttr, auto& ustr )
|
||||
{
|
||||
ustr.Buffer = static_cast<decltype(ustr.Buffer)>(this->_userData.ptr() + ARGS_OFFSET + sizeof( obAttr ) + sizeof( ustr ));
|
||||
ustr.Length = static_cast<USHORT>(wcslen( pEventName ) * sizeof( wchar_t ));
|
||||
ustr.MaximumLength = static_cast<USHORT>(len);
|
||||
|
||||
obAttr.Length = sizeof( obAttr );
|
||||
obAttr.ObjectName = static_cast<decltype(obAttr.ObjectName)>(this->_userData.ptr() + ARGS_OFFSET + sizeof( obAttr ));
|
||||
|
||||
NTSTATUS status = this->_userData.Write( ARGS_OFFSET, obAttr );
|
||||
status |= this->_userData.Write( ARGS_OFFSET + sizeof( obAttr ), ustr );
|
||||
status |= this->_userData.Write( ARGS_OFFSET + sizeof( obAttr ) + sizeof( ustr ), len, pEventName );
|
||||
return status;
|
||||
};
|
||||
|
||||
if (_proc.core().isWow64())
|
||||
{
|
||||
_OBJECT_ATTRIBUTES32 obAttr32 = { 0 };
|
||||
_UNICODE_STRING32 ustr32 = { 0 };
|
||||
status = fillAttributes( obAttr32, ustr32 );
|
||||
}
|
||||
else
|
||||
{
|
||||
_OBJECT_ATTRIBUTES64 obAttr64 = { 0 };
|
||||
_UNICODE_STRING64 ustr64 = { 0 };
|
||||
status = fillAttributes( obAttr64, ustr64 );
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS( status ))
|
||||
return status;
|
||||
|
||||
a->GenCall( pOpenEvent->procAddress, {
|
||||
_userData.ptr() + EVENT_OFFSET,
|
||||
@@ -507,17 +523,11 @@ NTSTATUS RemoteExec::CreateAPCEvent( DWORD threadID )
|
||||
// Save status
|
||||
(*a)->mov( (*a)->zdx, _userData.ptr() + ERR_OFFSET );
|
||||
(*a)->mov( asmjit::host::dword_ptr( (*a)->zdx ), asmjit::host::eax );
|
||||
|
||||
(*a)->ret();
|
||||
|
||||
status = _userData.Write( ARGS_OFFSET, obAttr );
|
||||
status |= _userData.Write( ARGS_OFFSET + sizeof(obAttr), ustr );
|
||||
status |= _userData.Write( ARGS_OFFSET + sizeof(obAttr) + sizeof(ustr), len, pEventName );
|
||||
if (!NT_SUCCESS( status ))
|
||||
return status;
|
||||
|
||||
ExecInNewThread( (*a)->make(), (*a)->getCodeSize(), dwResult );
|
||||
status = _userData.Read<NTSTATUS>( ERR_OFFSET, -1 );
|
||||
status = ExecInNewThread( (*a)->make(), (*a)->getCodeSize(), dwResult, NoSwitch );
|
||||
if (NT_SUCCESS( status ))
|
||||
status = _userData.Read<NTSTATUS>( ERR_OFFSET, -1 );
|
||||
}
|
||||
|
||||
return status;
|
||||
|
||||
@@ -118,7 +118,7 @@ public:
|
||||
/// <param name="a">Target assembly helper</param>
|
||||
BLACKBONE_API inline void SaveCallResult( IAsmHelper& a, uint32_t retOffset = RET_OFFSET )
|
||||
{
|
||||
a->mov( a->zdx, _userData.ptr<uintptr_t>() + retOffset );
|
||||
a->mov( a->zdx, _userData.ptr() + retOffset );
|
||||
a->mov( asmjit::host::dword_ptr( a->zdx ), a->zax );
|
||||
}
|
||||
|
||||
|
||||
@@ -303,6 +303,18 @@ NTSTATUS Native::SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx )
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NtQueueApcThread
|
||||
/// </summary>
|
||||
/// <param name="hThread">Thread handle.</param>
|
||||
/// <param name="func">APC function</param>
|
||||
/// <param name="arg">APC argument</param>
|
||||
/// <returns>Status code</returns>
|
||||
NTSTATUS Native::NtQueueApcThreadT( HANDLE hThread, ptr_t func, ptr_t arg )
|
||||
{
|
||||
return SAFE_NATIVE_CALL( NtQueueApcThread, hThread, reinterpret_cast<PVOID>(func), reinterpret_cast<PVOID>(arg), nullptr, nullptr );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get WOW64 PEB
|
||||
/// </summary>
|
||||
|
||||
@@ -158,6 +158,15 @@ public:
|
||||
/// <returns>Status code</returns>
|
||||
virtual NTSTATUS SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx );
|
||||
|
||||
/// <summary>
|
||||
/// NtQueueApcThread
|
||||
/// </summary>
|
||||
/// <param name="hThread">Thread handle.</param>
|
||||
/// <param name="func">APC function</param>
|
||||
/// <param name="arg">APC argument</param>
|
||||
/// <returns>Status code</returns>
|
||||
virtual NTSTATUS NtQueueApcThreadT( HANDLE hThread, ptr_t func, ptr_t arg );
|
||||
|
||||
/// <summary>
|
||||
/// Get WOW64 PEB
|
||||
/// </summary>
|
||||
|
||||
@@ -272,6 +272,25 @@ NTSTATUS NativeWow64::SetThreadContextT( HANDLE hThread, _CONTEXT64& ctx )
|
||||
return static_cast<NTSTATUS>(X64Call( stc, 2, (DWORD64)hThread, (DWORD64)&ctx ));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NtQueueApcThread
|
||||
/// </summary>
|
||||
/// <param name="hThread">Thread handle.</param>
|
||||
/// <param name="func">APC function</param>
|
||||
/// <param name="arg">APC argument</param>
|
||||
/// <returns>Status code</returns>
|
||||
NTSTATUS NativeWow64::NtQueueApcThreadT( HANDLE hThread, ptr_t func, ptr_t arg )
|
||||
{
|
||||
if (_wowBarrier.targetWow64)
|
||||
return Native::NtQueueApcThreadT( hThread, func, arg );
|
||||
|
||||
static ptr_t qat = GetProcAddress64( getNTDLL64(), "NtQueueApcThread" );
|
||||
if (qat == 0)
|
||||
return STATUS_ORDINAL_NOT_FOUND;
|
||||
|
||||
return static_cast<NTSTATUS>(X64Call( qat, 5, (DWORD64)hThread, func, arg, 0ull, 0ull ));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets WOW64 PEB
|
||||
/// </summary>
|
||||
|
||||
@@ -136,6 +136,15 @@ public:
|
||||
/// <returns>Status code</returns>
|
||||
virtual NTSTATUS SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx );
|
||||
|
||||
/// <summary>
|
||||
/// NtQueueApcThread
|
||||
/// </summary>
|
||||
/// <param name="hThread">Thread handle.</param>
|
||||
/// <param name="func">APC function</param>
|
||||
/// <param name="arg">APC argument</param>
|
||||
/// <returns>Status code</returns>
|
||||
virtual NTSTATUS NtQueueApcThreadT( HANDLE hThread, ptr_t func, ptr_t arg );
|
||||
|
||||
/// <summary>
|
||||
/// Get WOW64 PEB
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user