From 0f60516564f3633c7bb3e513926bd2695414eddb Mon Sep 17 00:00:00 2001 From: SpriteOvO Date: Thu, 9 Apr 2020 20:31:08 +0800 Subject: [PATCH 01/38] fixed wrong comment --- src/BlackBone/Include/Types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BlackBone/Include/Types.h b/src/BlackBone/Include/Types.h index 6d9ca098..328f683d 100644 --- a/src/BlackBone/Include/Types.h +++ b/src/BlackBone/Include/Types.h @@ -34,8 +34,8 @@ struct Wow64Barrier // Module type enum eModType { - mt_mod32, // 64 bit module - mt_mod64, // 32 bit module + mt_mod32, // 32 bit module + mt_mod64, // 64 bit module mt_default, // type is deduced from target process mt_unknown // Failed to detect type }; From 99b9d978e1417e4b3a5dff744a569894a331113e Mon Sep 17 00:00:00 2001 From: DKing Date: Fri, 10 Apr 2020 22:30:38 +0800 Subject: [PATCH 02/38] add partial support for windows 20H1 TestMem BSOD. --- src/3rd_party/VersionApi.h | 32 +++- src/BlackBone/Symbols/PatternLoader.cpp | 26 ++- src/BlackBoneDrv/BlackBoneDrv.c | 240 +++++++++++++----------- src/BlackBoneDrv/Private.c | 2 +- src/BlackBoneDrv/Private.h | 7 +- 5 files changed, 180 insertions(+), 127 deletions(-) diff --git a/src/3rd_party/VersionApi.h b/src/3rd_party/VersionApi.h index fb9a5853..0235ed43 100644 --- a/src/3rd_party/VersionApi.h +++ b/src/3rd_party/VersionApi.h @@ -26,7 +26,9 @@ enum eBuildThreshold Build_RS3 = 16299, Build_RS4 = 17134, Build_RS5 = 17763, - Build_RS6 = 18362, + Build_19H1 = 18362, + Build_19H2 = 18363, + Build_20H1 = 19041, Build_RS_MAX = 99999, }; @@ -43,7 +45,9 @@ enum eVerShort Win10_RS3, // Windows 10 Fall Creators update Win10_RS4, // Windows 10 Spring Creators update Win10_RS5, // Windows 10 October 2018 update - Win10_RS6, // Windows 10 May 2019 update + Win10_19H1, // Windows 10 May 2019 update + Win10_19H2, // Windows 10 November 2019 update + Win10_20H1, // Windows 10 April 2020 update }; struct WinVersion @@ -105,8 +109,12 @@ BLACKBONE_API inline void InitVersion() switch (fullver) { case _WIN32_WINNT_WIN10: - if (g_WinVer.native.dwBuildNumber >= Build_RS6) - g_WinVer.ver = Win10_RS6; + if (g_WinVer.native.dwBuildNumber >= Build_20H1) + g_WinVer.ver = Win10_20H1; + else if (g_WinVer.native.dwBuildNumber >= Build_19H2) + g_WinVer.ver = Win10_19H2; + else if (g_WinVer.native.dwBuildNumber >= Build_19H1) + g_WinVer.ver = Win10_19H1; else if (g_WinVer.native.dwBuildNumber >= Build_RS5) g_WinVer.ver = Win10_RS5; else if (g_WinVer.native.dwBuildNumber >= Build_RS4) @@ -278,9 +286,21 @@ IsWindows10RS5OrGreater() } VERSIONHELPERAPI -IsWindows10RS6OrGreater() +IsWindows1019H1OrGreater() { - return IsWindowsVersionOrGreater( HIBYTE( _WIN32_WINNT_WIN10 ), LOBYTE( _WIN32_WINNT_WIN10 ), 0, Build_RS6 ); + return IsWindowsVersionOrGreater( HIBYTE( _WIN32_WINNT_WIN10 ), LOBYTE( _WIN32_WINNT_WIN10 ), 0, Build_19H1 ); +} + +VERSIONHELPERAPI +IsWindows1019H2OrGreater() +{ + return IsWindowsVersionOrGreater( HIBYTE( _WIN32_WINNT_WIN10 ), LOBYTE( _WIN32_WINNT_WIN10 ), 0, Build_19H2 ); +} + +VERSIONHELPERAPI +IsWindows1020H1OrGreater() +{ + return IsWindowsVersionOrGreater( HIBYTE( _WIN32_WINNT_WIN10 ), LOBYTE( _WIN32_WINNT_WIN10 ), 0, Build_20H1 ); } VERSIONHELPERAPI diff --git a/src/BlackBone/Symbols/PatternLoader.cpp b/src/BlackBone/Symbols/PatternLoader.cpp index f342467e..62ceff9c 100644 --- a/src/BlackBone/Symbols/PatternLoader.cpp +++ b/src/BlackBone/Symbols/PatternLoader.cpp @@ -74,7 +74,7 @@ void OSFillPatterns( std::unordered_map& patterns, SymbolDat // LdrpHandleTlsData // 74 33 44 8D 43 09 auto offset = 0x43; - if(IsWindows10RS6OrGreater()) + if(IsWindows1019H1OrGreater()) offset = 0x46; else if (IsWindows10RS4OrGreater()) offset = 0x44; @@ -82,8 +82,12 @@ void OSFillPatterns( std::unordered_map& patterns, SymbolDat patterns.emplace( &result.LdrpHandleTlsData64, OffsetData{ "\x74\x33\x44\x8d\x43\x09", true, offset } ); // RtlInsertInvertedFunctionTable - // 8B FA 49 8D 43 20 - patterns.emplace( &result.RtlInsertInvertedFunctionTable64, OffsetData{ "\x8b\xfa\x49\x8d\x43\x20", true, 0x10 } ); + // 48 8D 54 24 58 48 8B F9 E8 - 20H1 + // 8B FA 49 8D 43 20 - RS3-19H2 + if (IsWindows1020H1OrGreater()) + patterns.emplace( &result.RtlInsertInvertedFunctionTable64, OffsetData{ "\x48\x8d\x54\x24\x58\x48\x8b\xf9\xe8", true, 0x11 } ); + else + patterns.emplace( &result.RtlInsertInvertedFunctionTable64, OffsetData{ "\x8b\xfa\x49\x8d\x43\x20", true, 0x10 } ); // RtlpInsertInvertedFunctionTableEntry // 49 8B E8 48 8B FA 0F 84 @@ -98,8 +102,8 @@ void OSFillPatterns( std::unordered_map& patterns, SymbolDat patterns.emplace( &result.LdrpInvertedFunctionTable32, OffsetData{ "\x33\xF6\x46\x3B\xC6", false, -1, -0x1B } ); // LdrpHandleTlsData - // 33 F6 85 C0 79 03 - RS5+ - // 8B C1 8D 4D AC/BC 51 - RS3-RS4 + // 33 F6 85 C0 79 03 - RS5-20H1 + // 8B C1 8D 4D AC/BC 51 - RS3/RS4 auto pattern = "\x8b\xc1\x8d\x4d\xbc\x51"; if (IsWindows10RS5OrGreater()) pattern = "\x33\xf6\x85\xc0\x79\x03"; @@ -107,7 +111,9 @@ void OSFillPatterns( std::unordered_map& patterns, SymbolDat pattern = "\x8b\xc1\x8d\x4d\xac\x51"; offset = 0x18; - if (IsWindows10RS6OrGreater()) + if (IsWindows1020H1OrGreater()) + offset = 0x2C; + else if (IsWindows1019H1OrGreater()) offset = 0x2E; else if (IsWindows10RS5OrGreater()) offset = 0x2C; @@ -115,8 +121,12 @@ void OSFillPatterns( std::unordered_map& patterns, SymbolDat patterns.emplace( &result.LdrpHandleTlsData32, OffsetData{ pattern, false, offset } ); // LdrProtectMrdata - // 75 24 85 F6 75 08 - patterns.emplace( &result.LdrProtectMrdata, OffsetData{ "\x75\x24\x85\xf6\x75\x08", false, 0x1C } ); + // 75 25 85 F6 75 08 - 20H1 + // 75 24 85 F6 75 08 - RS3-19H2 + if(IsWindows1020H1OrGreater()) + patterns.emplace( &result.LdrProtectMrdata, OffsetData{ "\x75\x25\x85\xf6\x75\x08", false, 0x1D } ); + else + patterns.emplace( &result.LdrProtectMrdata, OffsetData{ "\x75\x24\x85\xf6\x75\x08", false, 0x1C } ); } else if (IsWindows10RS2OrGreater()) { diff --git a/src/BlackBoneDrv/BlackBoneDrv.c b/src/BlackBoneDrv/BlackBoneDrv.c index 60f75255..0a0df4ba 100644 --- a/src/BlackBoneDrv/BlackBoneDrv.c +++ b/src/BlackBoneDrv/BlackBoneDrv.c @@ -269,7 +269,7 @@ NTSTATUS BBInitDynamicData( IN OUT PDYNAMIC_DATA pData ) if (ver_short != WINVER_81) return STATUS_NOT_SUPPORTED; #elif defined (_WIN10_) - if (ver_short < WINVER_10 || WINVER_10_RS7 < ver_short) + if (ver_short < WINVER_10 || WINVER_10_20H1 < ver_short) return STATUS_NOT_SUPPORTED; #endif @@ -289,16 +289,16 @@ NTSTATUS BBInitDynamicData( IN OUT PDYNAMIC_DATA pData ) // Windows 7 SP1 case WINVER_7: case WINVER_7_SP1: - pData->KExecOpt = 0x0D2; - pData->Protection = 0x43C; // Bitfield, bit index - 0xB - pData->ObjTable = 0x200; - pData->VadRoot = 0x448; - pData->NtProtectIndex = 0x04D; - pData->NtCreateThdIndex = 0x0A5; - pData->NtTermThdIndex = 0x50; - pData->PrevMode = 0x1F6; - pData->ExitStatus = 0x380; - pData->MiAllocPage = (ver_short == WINVER_7_SP1) ? 0 : 0; + pData->KExecOpt = 0x0D2; + pData->Protection = 0x43C; // Bitfield, bit index - 0xB + pData->ObjTable = 0x200; + pData->VadRoot = 0x448; + pData->NtProtectIndex = 0x04D; + pData->NtCreateThdExIndex = 0x0A5; + pData->NtTermThdIndex = 0x50; + pData->PrevMode = 0x1F6; + pData->ExitStatus = 0x380; + pData->MiAllocPage = (ver_short == WINVER_7_SP1) ? 0 : 0; if (ver_short == WINVER_7_SP1) { if (NT_SUCCESS( BBScanSection( "PAGE", (PCUCHAR)"\x48\x8D\x56\x20\x48\x8B\x42\x08", 0xCC, 8, (PVOID)&pData->ExRemoveTable ) )) @@ -310,32 +310,32 @@ NTSTATUS BBInitDynamicData( IN OUT PDYNAMIC_DATA pData ) // Windows 8 case WINVER_8: - pData->KExecOpt = 0x1B7; - pData->Protection = 0x648; - pData->ObjTable = 0x408; - pData->VadRoot = 0x590; - pData->NtProtectIndex = 0x04E; - pData->NtCreateThdIndex = 0x0AF; - pData->NtTermThdIndex = 0x51; - pData->PrevMode = 0x232; - pData->ExitStatus = 0x450; - pData->MiAllocPage = 0x3AF374; - pData->ExRemoveTable = 0x487518; + pData->KExecOpt = 0x1B7; + pData->Protection = 0x648; + pData->ObjTable = 0x408; + pData->VadRoot = 0x590; + pData->NtProtectIndex = 0x04E; + pData->NtCreateThdExIndex = 0x0AF; + pData->NtTermThdIndex = 0x51; + pData->PrevMode = 0x232; + pData->ExitStatus = 0x450; + pData->MiAllocPage = 0x3AF374; + pData->ExRemoveTable = 0x487518; break; // Windows 8.1 case WINVER_81: - pData->KExecOpt = 0x1B7; - pData->Protection = 0x67A; - pData->EProcessFlags2 = 0x2F8; - pData->ObjTable = 0x408; - pData->VadRoot = 0x5D8; - pData->NtCreateThdIndex = 0xB0; - pData->NtTermThdIndex = 0x52; - pData->PrevMode = 0x232; - pData->ExitStatus = 0x6D8; - pData->MiAllocPage = 0; - pData->ExRemoveTable = 0x432A88; // 0x38E320; + pData->KExecOpt = 0x1B7; + pData->Protection = 0x67A; + pData->EProcessFlags2 = 0x2F8; + pData->ObjTable = 0x408; + pData->VadRoot = 0x5D8; + pData->NtCreateThdExIndex = 0xB0; + pData->NtTermThdIndex = 0x52; + pData->PrevMode = 0x232; + pData->ExitStatus = 0x6D8; + pData->MiAllocPage = 0; + pData->ExRemoveTable = 0x432A88; // 0x38E320; if (NT_SUCCESS( BBScanSection( "PAGE", (PCUCHAR)"\x48\x8D\x7D\x18\x48\x8B", 0xCC, 6, (PVOID)&pData->ExRemoveTable ) )) pData->ExRemoveTable -= 0x5E; break; @@ -344,118 +344,140 @@ NTSTATUS BBInitDynamicData( IN OUT PDYNAMIC_DATA pData ) case WINVER_10: if (verInfo.dwBuildNumber == 10586) { - pData->KExecOpt = 0x1BF; - pData->Protection = 0x6B2; - pData->EProcessFlags2 = 0x300; - pData->ObjTable = 0x418; - pData->VadRoot = 0x610; - pData->NtCreateThdIndex = 0xB4; - pData->NtTermThdIndex = 0x53; - pData->PrevMode = 0x232; - pData->ExitStatus = 0x6E0; - pData->MiAllocPage = 0; + pData->KExecOpt = 0x1BF; + pData->Protection = 0x6B2; + pData->EProcessFlags2 = 0x300; + pData->ObjTable = 0x418; + pData->VadRoot = 0x610; + pData->NtCreateThdExIndex = 0xB4; + pData->NtTermThdIndex = 0x53; + pData->PrevMode = 0x232; + pData->ExitStatus = 0x6E0; + pData->MiAllocPage = 0; if (NT_SUCCESS( BBScanSection( "PAGE", (PCUCHAR)"\x48\x8D\x7D\x18\x48\x8B", 0xCC, 6, (PVOID)&pData->ExRemoveTable ) )) pData->ExRemoveTable -= 0x5C; break; } else if (verInfo.dwBuildNumber == 14393) { - pData->ver = WINVER_10_RS1; - pData->KExecOpt = 0x1BF; - pData->Protection = pData->buildNo >= 447 ? 0x6CA : 0x6C2; - pData->EProcessFlags2 = 0x300; - pData->ObjTable = 0x418; - pData->VadRoot = 0x620; - pData->NtCreateThdIndex = 0xB6; - pData->NtTermThdIndex = 0x53; - pData->PrevMode = 0x232; - pData->ExitStatus = 0x6F0; - pData->MiAllocPage = 0; + pData->ver = WINVER_10_RS1; + pData->KExecOpt = 0x1BF; + pData->Protection = pData->buildNo >= 447 ? 0x6CA : 0x6C2; + pData->EProcessFlags2 = 0x300; + pData->ObjTable = 0x418; + pData->VadRoot = 0x620; + pData->NtCreateThdExIndex = 0xB6; + pData->NtTermThdIndex = 0x53; + pData->PrevMode = 0x232; + pData->ExitStatus = 0x6F0; + pData->MiAllocPage = 0; if (NT_SUCCESS( BBScanSection( "PAGE", (PCUCHAR)"\x48\x8D\x7D\x18\x48\x8B", 0xCC, 6, (PVOID)&pData->ExRemoveTable ) )) pData->ExRemoveTable -= 0x60; break; } else if (verInfo.dwBuildNumber == 15063) { - pData->ver = WINVER_10_RS2; - pData->KExecOpt = 0x1BF; - pData->Protection = 0x6CA; - pData->EProcessFlags2 = 0x300; - pData->ObjTable = 0x418; - pData->VadRoot = 0x628; - pData->NtCreateThdIndex = 0xB9; - pData->NtTermThdIndex = 0x53; - pData->PrevMode = 0x232; - pData->ExitStatus = 0x6F8; - pData->MiAllocPage = 0; + pData->ver = WINVER_10_RS2; + pData->KExecOpt = 0x1BF; + pData->Protection = 0x6CA; + pData->EProcessFlags2 = 0x300; + pData->ObjTable = 0x418; + pData->VadRoot = 0x628; + pData->NtCreateThdExIndex = 0xB9; + pData->NtTermThdIndex = 0x53; + pData->PrevMode = 0x232; + pData->ExitStatus = 0x6F8; + pData->MiAllocPage = 0; if (NT_SUCCESS( BBScanSection( "PAGE", (PCUCHAR)"\x48\x8B\x47\x20\x48\x83\xC7\x18", 0xCC, 8, (PVOID)&pData->ExRemoveTable ) )) pData->ExRemoveTable -= 0x34; break; } else if (verInfo.dwBuildNumber == 16299) { - pData->ver = WINVER_10_RS3; - pData->KExecOpt = 0x1BF; - pData->Protection = 0x6CA; - pData->EProcessFlags2 = 0x828; // MitigationFlags offset - pData->ObjTable = 0x418; - pData->VadRoot = 0x628; - pData->NtCreateThdIndex = 0xBA; - pData->NtTermThdIndex = 0x53; - pData->PrevMode = 0x232; - pData->ExitStatus = 0x700; - pData->MiAllocPage = 0; + pData->ver = WINVER_10_RS3; + pData->KExecOpt = 0x1BF; + pData->Protection = 0x6CA; + pData->EProcessFlags2 = 0x828; // MitigationFlags offset + pData->ObjTable = 0x418; + pData->VadRoot = 0x628; + pData->NtCreateThdExIndex = 0xBA; + pData->NtTermThdIndex = 0x53; + pData->PrevMode = 0x232; + pData->ExitStatus = 0x700; + pData->MiAllocPage = 0; if (NT_SUCCESS( BBScanSection( "PAGE", (PCUCHAR)"\x48\x83\xC7\x18\x48\x8B\x17", 0xCC, 7, (PVOID)&pData->ExRemoveTable ) )) pData->ExRemoveTable -= 0x34; break; } else if (verInfo.dwBuildNumber == 17134) { - pData->ver = WINVER_10_RS4; - pData->KExecOpt = 0x1BF; - pData->Protection = 0x6CA; - pData->EProcessFlags2 = 0x828; // MitigationFlags offset - pData->ObjTable = 0x418; - pData->VadRoot = 0x628; - pData->NtCreateThdIndex = 0xBB; - pData->NtTermThdIndex = 0x53; - pData->PrevMode = 0x232; - pData->ExitStatus = 0x700; - pData->MiAllocPage = 0; + pData->ver = WINVER_10_RS4; + pData->KExecOpt = 0x1BF; + pData->Protection = 0x6CA; + pData->EProcessFlags2 = 0x828; // MitigationFlags offset + pData->ObjTable = 0x418; + pData->VadRoot = 0x628; + pData->NtCreateThdExIndex = 0xBB; + pData->NtTermThdIndex = 0x53; + pData->PrevMode = 0x232; + pData->ExitStatus = 0x700; + pData->MiAllocPage = 0; if (NT_SUCCESS( BBScanSection( "PAGE", (PCUCHAR)"\x48\x83\xC7\x18\x48\x8B\x17", 0xCC, 7, (PVOID)&pData->ExRemoveTable ) )) pData->ExRemoveTable -= 0x34; break; } else if (verInfo.dwBuildNumber == 17763) { - pData->ver = WINVER_10_RS5; - pData->KExecOpt = 0x1BF; - pData->Protection = 0x6CA; - pData->EProcessFlags2 = 0x820; // MitigationFlags offset - pData->ObjTable = 0x418; - pData->VadRoot = 0x628; - pData->NtCreateThdIndex = 0xBC; - pData->NtTermThdIndex = 0x53; - pData->PrevMode = 0x232; - pData->ExitStatus = 0x700; - pData->MiAllocPage = 0; + pData->ver = WINVER_10_RS5; + pData->KExecOpt = 0x1BF; + pData->Protection = 0x6CA; + pData->EProcessFlags2 = 0x820; // MitigationFlags offset + pData->ObjTable = 0x418; + pData->VadRoot = 0x628; + pData->NtCreateThdExIndex = 0xBC; + pData->NtTermThdIndex = 0x53; + pData->PrevMode = 0x232; + pData->ExitStatus = 0x700; + pData->MiAllocPage = 0; if (NT_SUCCESS( BBScanSection( "PAGE", (PCUCHAR)"\x48\x83\xC7\x18\x48\x8B\x17", 0xCC, 7, (PVOID)&pData->ExRemoveTable ) )) pData->ExRemoveTable -= 0x34; break; } else if (verInfo.dwBuildNumber == 18362 || verInfo.dwBuildNumber == 18363) { - pData->ver = verInfo.dwBuildNumber == 18362 ? WINVER_10_RS6 : WINVER_10_RS7; - pData->KExecOpt = 0x1C3; - pData->Protection = 0x6FA; - pData->EProcessFlags2 = 0x850; // MitigationFlags offset - pData->ObjTable = 0x418; - pData->VadRoot = 0x658; - pData->NtCreateThdIndex = 0xBD; - pData->NtTermThdIndex = 0x53; - pData->PrevMode = 0x232; - pData->ExitStatus = 0x710; - pData->MiAllocPage = 0; + pData->ver = verInfo.dwBuildNumber == 18362 ? WINVER_10_19H1 : WINVER_10_19H2; + pData->KExecOpt = 0x1C3; + pData->Protection = 0x6FA; + pData->EProcessFlags2 = 0x850; // MitigationFlags offset + pData->ObjTable = 0x418; + pData->VadRoot = 0x658; + pData->NtCreateThdExIndex = 0xBD; + pData->NtTermThdIndex = 0x53; + pData->PrevMode = 0x232; + pData->ExitStatus = 0x710; + pData->MiAllocPage = 0; + if (NT_SUCCESS( BBScanSection( "PAGE", (PCUCHAR)"\x48\x83\xC7\x18\x48\x8B\x17", 0xCC, 7, (PVOID)&pData->ExRemoveTable ) )) + pData->ExRemoveTable -= 0x34; + break; + } + else if (verInfo.dwBuildNumber == 19041) + { + pData->ver = WINVER_10_20H1; + // KP + pData->KExecOpt = 0x283; + // EP + pData->Protection = 0x87A; + pData->EProcessFlags2 = 0x9D4; // MitigationFlags offset + pData->ObjTable = 0x570; + pData->VadRoot = 0x7D8; + // KT + pData->PrevMode = 0x232; + // ET + pData->ExitStatus = 0x548; + // SSDT + pData->NtCreateThdExIndex = 0xC1; + pData->NtTermThdIndex = 0x53; + pData->MiAllocPage = 0; if (NT_SUCCESS( BBScanSection( "PAGE", (PCUCHAR)"\x48\x83\xC7\x18\x48\x8B\x17", 0xCC, 7, (PVOID)&pData->ExRemoveTable ) )) pData->ExRemoveTable -= 0x34; break; diff --git a/src/BlackBoneDrv/Private.c b/src/BlackBoneDrv/Private.c index 3e761361..3ae0df66 100644 --- a/src/BlackBoneDrv/Private.c +++ b/src/BlackBoneDrv/Private.c @@ -528,7 +528,7 @@ ZwCreateThreadEx( { NTSTATUS status = STATUS_SUCCESS; - fnNtCreateThreadEx NtCreateThreadEx = (fnNtCreateThreadEx)(ULONG_PTR)GetSSDTEntry( dynData.NtCreateThdIndex ); + fnNtCreateThreadEx NtCreateThreadEx = (fnNtCreateThreadEx)(ULONG_PTR)GetSSDTEntry( dynData.NtCreateThdExIndex ); if (NtCreateThreadEx) { // diff --git a/src/BlackBoneDrv/Private.h b/src/BlackBoneDrv/Private.h index 7c060d1f..82b48c2d 100644 --- a/src/BlackBoneDrv/Private.h +++ b/src/BlackBoneDrv/Private.h @@ -162,8 +162,9 @@ typedef enum _WinVer WINVER_10_RS3 = 0x0A03, // Fall creators update WINVER_10_RS4 = 0x0A04, // Spring creators update WINVER_10_RS5 = 0x0A05, // October 2018 update - WINVER_10_RS6 = 0x0A06, // May 2019 update 19H1 - WINVER_10_RS7 = 0x0A07, // 19H2 update + WINVER_10_19H1 = 0x0A06, // May 2019 update 19H1 + WINVER_10_19H2 = 0x0A07, // November 2019 update 19H2 + WINVER_10_20H1 = 0x0A08, // April 2020 update 20H1 } WinVer; extern PLIST_ENTRY PsLoadedModuleList; @@ -184,7 +185,7 @@ typedef struct _DYNAMIC_DATA ULONG ObjTable; // EPROCESS::ObjectTable ULONG VadRoot; // EPROCESS::VadRoot ULONG NtProtectIndex; // NtProtectVirtualMemory SSDT index - ULONG NtCreateThdIndex; // NtCreateThreadEx SSDT index + ULONG NtCreateThdExIndex; // NtCreateThreadEx SSDT index ULONG NtTermThdIndex; // NtTerminateThread SSDT index ULONG PrevMode; // KTHREAD::PreviousMode ULONG ExitStatus; // ETHREAD::ExitStatus From 273a44c7e01d27781a50b9c299ae8b9fdbe8eb36 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Wed, 15 Apr 2020 16:11:35 +0300 Subject: [PATCH 03/38] Windows api does not have any guarantee that ntstatus will remain unchanged after a successful call, neither it have any guarantee of its value in this case. Though it is working most of the time, correct way is to check call return value. --- src/BlackBone/Process/Threads/Thread.cpp | 5 +- src/BlackBone/Subsystem/NativeSubsystem.cpp | 52 ++++++++------------- src/BlackBone/Subsystem/Wow64Subsystem.cpp | 11 ++--- src/BlackBone/Subsystem/x86Subsystem.cpp | 10 ++-- 4 files changed, 31 insertions(+), 47 deletions(-) diff --git a/src/BlackBone/Process/Threads/Thread.cpp b/src/BlackBone/Process/Threads/Thread.cpp index f5313366..5efab5ce 100644 --- a/src/BlackBone/Process/Threads/Thread.cpp +++ b/src/BlackBone/Process/Threads/Thread.cpp @@ -185,9 +185,8 @@ NTSTATUS Thread::SetContext( _CONTEXT64& ctx, bool dontSuspend /*= false*/ ) /// Status code NTSTATUS Thread::Terminate( DWORD code /*= 0*/ ) { - SetLastNtStatus( STATUS_SUCCESS ); - TerminateThread( _handle, code ); - return LastNtStatus(); + auto r = TerminateThread(_handle, code); + return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } /// diff --git a/src/BlackBone/Subsystem/NativeSubsystem.cpp b/src/BlackBone/Subsystem/NativeSubsystem.cpp index 651dbadd..465555ed 100644 --- a/src/BlackBone/Subsystem/NativeSubsystem.cpp +++ b/src/BlackBone/Subsystem/NativeSubsystem.cpp @@ -67,9 +67,8 @@ Native::~Native() /// Status code NTSTATUS Native::VirtualAllocExT( ptr_t& lpAddress, size_t dwSize, DWORD flAllocationType, DWORD flProtect ) { - SetLastNtStatus( STATUS_SUCCESS ); lpAddress = reinterpret_cast(VirtualAllocEx( _hProcess, reinterpret_cast(lpAddress), dwSize, flAllocationType, flProtect )); - return LastNtStatus(); + return lpAddress != 0 ? STATUS_SUCCESS : LastNtStatus(); } @@ -82,9 +81,8 @@ NTSTATUS Native::VirtualAllocExT( ptr_t& lpAddress, size_t dwSize, DWORD flAlloc /// Status code NTSTATUS Native::VirtualFreeExT( ptr_t lpAddress, size_t dwSize, DWORD dwFreeType ) { - SetLastNtStatus( STATUS_SUCCESS ); - VirtualFreeEx( _hProcess, reinterpret_cast(lpAddress), dwSize, dwFreeType ); - return LastNtStatus(); + auto r = VirtualFreeEx( _hProcess, reinterpret_cast(lpAddress), dwSize, dwFreeType ); + return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } /// @@ -95,14 +93,13 @@ NTSTATUS Native::VirtualFreeExT( ptr_t lpAddress, size_t dwSize, DWORD dwFreeTyp /// Status code NTSTATUS Native::VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION64 lpBuffer ) { - SetLastNtStatus( STATUS_SUCCESS ); - VirtualQueryEx( + auto r = VirtualQueryEx( _hProcess, reinterpret_cast(lpAddress), reinterpret_cast(lpBuffer), sizeof( MEMORY_BASIC_INFORMATION ) ); - return LastNtStatus(); + return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } /// @@ -115,7 +112,6 @@ NTSTATUS Native::VirtualQueryExT( ptr_t lpAddress, MEMORY_INFORMATION_CLASS info { SIZE_T retLen = 0; - SetLastNtStatus( STATUS_SUCCESS ); return SAFE_NATIVE_CALL( NtQueryVirtualMemory, _hProcess, reinterpret_cast(lpAddress), infoClass, lpBuffer, bufSize, &retLen @@ -136,10 +132,9 @@ NTSTATUS Native::VirtualProtectExT( ptr_t lpAddress, DWORD64 dwSize, DWORD flPro if (!flOld) flOld = &junk; - SetLastNtStatus( STATUS_SUCCESS ); - VirtualProtectEx( _hProcess, reinterpret_cast(lpAddress), static_cast(dwSize), flProtect, flOld ); + auto r = VirtualProtectEx( _hProcess, reinterpret_cast(lpAddress), static_cast(dwSize), flProtect, flOld ); - return LastNtStatus(); + return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } /// @@ -152,9 +147,8 @@ NTSTATUS Native::VirtualProtectExT( ptr_t lpAddress, DWORD64 dwSize, DWORD flPro /// Status code NTSTATUS Native::ReadProcessMemoryT( ptr_t lpBaseAddress, LPVOID lpBuffer, size_t nSize, DWORD64 *lpBytes /*= nullptr */ ) { - SetLastNtStatus( STATUS_SUCCESS ); - ReadProcessMemory( _hProcess, reinterpret_cast(lpBaseAddress), lpBuffer, nSize, reinterpret_cast(lpBytes) ); - return LastNtStatus(); + auto r = ReadProcessMemory( _hProcess, reinterpret_cast(lpBaseAddress), lpBuffer, nSize, reinterpret_cast(lpBytes) ); + return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } /// @@ -167,9 +161,8 @@ NTSTATUS Native::ReadProcessMemoryT( ptr_t lpBaseAddress, LPVOID lpBuffer, size_ /// Status code NTSTATUS Native::WriteProcessMemoryT( ptr_t lpBaseAddress, LPCVOID lpBuffer, size_t nSize, DWORD64 *lpBytes /*= nullptr */ ) { - SetLastNtStatus( STATUS_SUCCESS ); - WriteProcessMemory( _hProcess, reinterpret_cast(lpBaseAddress), lpBuffer, nSize, reinterpret_cast(lpBytes) ); - return LastNtStatus(); + auto r = WriteProcessMemory( _hProcess, reinterpret_cast(lpBaseAddress), lpBuffer, nSize, reinterpret_cast(lpBytes) ); + return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } /// @@ -208,7 +201,6 @@ NTSTATUS Native::SetProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, u /// Status code NTSTATUS Native::CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t arg, CreateThreadFlags flags, DWORD access /*= THREAD_ALL_ACCESS*/ ) { - SetLastNtStatus( STATUS_SUCCESS ); NTSTATUS status = 0; auto pCreateThread = GET_IMPORT( NtCreateThreadEx ); @@ -236,7 +228,7 @@ NTSTATUS Native::CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t arg, C reinterpret_cast(arg), win32Flags, NULL ); - status = LastNtStatus(); + status = hThread != NULL ? STATUS_SUCCESS : LastNtStatus(); } return status; @@ -250,9 +242,8 @@ NTSTATUS Native::CreateRemoteThreadT( HANDLE& hThread, ptr_t entry, ptr_t arg, C /// Status code NTSTATUS Native::GetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) { - SetLastNtStatus( STATUS_SUCCESS ); - GetThreadContext( hThread, reinterpret_cast(&ctx) ); - return LastNtStatus(); + auto r = GetThreadContext(hThread, reinterpret_cast(&ctx)); + return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } /// @@ -270,9 +261,8 @@ NTSTATUS Native::GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) } else { - SetLastNtStatus( STATUS_SUCCESS ); - SAFE_CALL( Wow64GetThreadContext, hThread, reinterpret_cast(&ctx) ); - return LastNtStatus(); + auto r = SAFE_CALL(Wow64GetThreadContext, hThread, reinterpret_cast(&ctx)); + return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } } @@ -284,9 +274,8 @@ NTSTATUS Native::GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) /// Status code NTSTATUS Native::SetThreadContextT( HANDLE hThread, _CONTEXT64& ctx ) { - SetLastNtStatus( STATUS_SUCCESS ); - SetThreadContext( hThread, reinterpret_cast(&ctx) ); - return LastNtStatus(); + auto r = SetThreadContext(hThread, reinterpret_cast(&ctx)); + return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } /// @@ -304,9 +293,8 @@ NTSTATUS Native::SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) } else { - SetLastNtStatus( STATUS_SUCCESS ); - SAFE_CALL( Wow64SetThreadContext, hThread, reinterpret_cast(&ctx)); - return LastNtStatus(); + auto r = SAFE_CALL(Wow64SetThreadContext, hThread, reinterpret_cast(&ctx)); + return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } } diff --git a/src/BlackBone/Subsystem/Wow64Subsystem.cpp b/src/BlackBone/Subsystem/Wow64Subsystem.cpp index b9543b8f..bbc49596 100644 --- a/src/BlackBone/Subsystem/Wow64Subsystem.cpp +++ b/src/BlackBone/Subsystem/Wow64Subsystem.cpp @@ -2,6 +2,7 @@ #include "../Misc/DynImport.h" #include "../Include/Macro.h" #include <3rd_party/rewolf-wow64ext/src/wow64ext.h> +#include "../Misc/Trace.hpp" namespace blackbone { @@ -214,9 +215,8 @@ NTSTATUS NativeWow64::GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) } else { - SetLastNtStatus( STATUS_SUCCESS ); - GetThreadContext( hThread, reinterpret_cast(&ctx) ); - return LastNtStatus(); + auto r = GetThreadContext(hThread, reinterpret_cast(&ctx)); + return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } } @@ -251,9 +251,8 @@ NTSTATUS NativeWow64::SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) } else { - SetLastNtStatus( STATUS_SUCCESS ); - SetThreadContext( hThread, reinterpret_cast(&ctx) ); - return LastNtStatus(); + auto r = SetThreadContext(hThread, reinterpret_cast(&ctx)); + return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } } diff --git a/src/BlackBone/Subsystem/x86Subsystem.cpp b/src/BlackBone/Subsystem/x86Subsystem.cpp index 1cb58fb6..fa7acaa6 100644 --- a/src/BlackBone/Subsystem/x86Subsystem.cpp +++ b/src/BlackBone/Subsystem/x86Subsystem.cpp @@ -52,9 +52,8 @@ NTSTATUS x86Native::VirtualQueryExT( ptr_t lpAddress, PMEMORY_BASIC_INFORMATION6 /// Status code NTSTATUS x86Native::GetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) { - SetLastNtStatus( STATUS_SUCCESS ); - GetThreadContext( hThread, reinterpret_cast(&ctx) ); - return LastNtStatus(); + auto r = GetThreadContext(hThread, reinterpret_cast(&ctx)); + return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } /// @@ -77,9 +76,8 @@ NTSTATUS x86Native::GetThreadContextT( HANDLE /*hThread*/, _CONTEXT64& /*ctx*/ ) /// Status code NTSTATUS x86Native::SetThreadContextT( HANDLE hThread, _CONTEXT32& ctx ) { - SetLastNtStatus( STATUS_SUCCESS ); - SetThreadContext( hThread, reinterpret_cast(&ctx) ); - return LastNtStatus(); + auto r = SetThreadContext(hThread, reinterpret_cast(&ctx)); + return r != 0 ? STATUS_SUCCESS : LastNtStatus(); } /// From 4cc4a77973f733c2b31a777a29cecfe9bc8c82b1 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Fri, 17 Apr 2020 19:59:17 +0300 Subject: [PATCH 04/38] use target struct size for copying / fix copying x64 peb size (cherry picked from commit 2d928b8cba6389ce2c79c040fe042850813b38e7) --- src/BlackBone/Subsystem/NativeSubsystem.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/BlackBone/Subsystem/NativeSubsystem.cpp b/src/BlackBone/Subsystem/NativeSubsystem.cpp index 465555ed..e3c4c997 100644 --- a/src/BlackBone/Subsystem/NativeSubsystem.cpp +++ b/src/BlackBone/Subsystem/NativeSubsystem.cpp @@ -332,7 +332,7 @@ ptr_t Native::getPEB( _PEB32* ppeb ) { 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(_PEB32), NULL ); + ReadProcessMemory( _hProcess, reinterpret_cast(ptr), ppeb, sizeof(*ppeb), NULL ); return ptr; } @@ -349,7 +349,7 @@ ptr_t Native::getPEB( _PEB64* ppeb ) 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 ); + ReadProcessMemory( _hProcess, pbi.PebBaseAddress, ppeb, sizeof(*ppeb), NULL ); return reinterpret_cast(pbi.PebBaseAddress); } @@ -373,7 +373,7 @@ ptr_t Native::getTEB( HANDLE hThread, _TEB32* pteb ) 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(_TEB32), NULL ); + ReadProcessMemory( _hProcess, (const uint8_t*)tbi.TebBaseAddress + 0x2000, pteb, sizeof(*pteb), NULL ); return tbi.TebBaseAddress + 0x2000; } @@ -391,7 +391,7 @@ ptr_t Native::getTEB( HANDLE hThread, _TEB64* pteb ) 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(_TEB64), NULL ); + ReadProcessMemory( _hProcess, reinterpret_cast(tbi.TebBaseAddress), pteb, sizeof(*pteb), NULL ); return tbi.TebBaseAddress; } From 878c06280ae1df7d8fbecfe2606ddfd391d4d1da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=B1=E4=BA=A7=E4=B8=BB=E4=B9=89=E6=8E=A5=E7=8F=AD?= =?UTF-8?q?=E4=BA=BA?= <503407184@qq.com> Date: Tue, 28 Apr 2020 11:27:41 +0800 Subject: [PATCH 05/38] get the current memory operation object --- src/BlackBone/Process/RPC/RemoteContext.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/BlackBone/Process/RPC/RemoteContext.hpp b/src/BlackBone/Process/RPC/RemoteContext.hpp index e30f6c1a..f34f5711 100644 --- a/src/BlackBone/Process/RPC/RemoteContext.hpp +++ b/src/BlackBone/Process/RPC/RemoteContext.hpp @@ -35,6 +35,12 @@ class RemoteContext { } + // memory object + BLACKBONE_API inline ProcessMemory& memory() + { + return _memory; + } + // Native context BLACKBONE_API inline _CONTEXT64& native() { @@ -282,4 +288,4 @@ class RemoteContext ptr_t _frame_ptr = 0; // Top stack frame pointer }; -} \ No newline at end of file +} From 79ab4be1f94382798e86b92eee9c8893792e5e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=B1=E4=BA=A7=E4=B8=BB=E4=B9=89=E6=8E=A5=E7=8F=AD?= =?UTF-8?q?=E4=BA=BA?= <503407184@qq.com> Date: Tue, 28 Apr 2020 11:32:58 +0800 Subject: [PATCH 06/38] fix: operation bit of target process --- src/BlackBone/Process/ProcessModules.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BlackBone/Process/ProcessModules.cpp b/src/BlackBone/Process/ProcessModules.cpp index a4af2cd3..a41ca0c9 100644 --- a/src/BlackBone/Process/ProcessModules.cpp +++ b/src/BlackBone/Process/ProcessModules.cpp @@ -135,7 +135,7 @@ ModuleDataPtr ProcessModules::GetModule( /// Module data. nullptr if not found ModuleDataPtr ProcessModules::GetMainModule() { - if (_proc.barrier().x86OS) + if (_proc.barrier().TargetWow64) { _PEB32 peb = { 0 }; if (_proc.core().peb32( &peb ) == 0) @@ -958,4 +958,4 @@ void ProcessModules::reset() _ldrPatched = false; } -} \ No newline at end of file +} From 667b7bba218067c403d9b4ee5de750e18f0db520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=B1=E4=BA=A7=E4=B8=BB=E4=B9=89=E6=8E=A5=E7=8F=AD?= =?UTF-8?q?=E4=BA=BA?= <503407184@qq.com> Date: Tue, 28 Apr 2020 13:37:50 +0800 Subject: [PATCH 07/38] fix: non cross op bit --- src/BlackBone/Subsystem/Wow64Subsystem.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/BlackBone/Subsystem/Wow64Subsystem.cpp b/src/BlackBone/Subsystem/Wow64Subsystem.cpp index bbc49596..776f4f2e 100644 --- a/src/BlackBone/Subsystem/Wow64Subsystem.cpp +++ b/src/BlackBone/Subsystem/Wow64Subsystem.cpp @@ -113,7 +113,13 @@ NTSTATUS NativeWow64::ReadProcessMemoryT( ptr_t lpBaseAddress, LPVOID lpBuffer, DWORD64 junk = 0; if (lpBytes == nullptr) lpBytes = &junk; - + + if (_wowBarrier.targetWow64) { + SetLastNtStatus(STATUS_SUCCESS); + ReadProcessMemory(_hProcess, reinterpret_cast(lpBaseAddress), lpBuffer, nSize, reinterpret_cast(lpBytes)); + return LastNtStatus(); + } + return SAFE_NATIVE_CALL( NtWow64ReadVirtualMemory64, _hProcess, lpBaseAddress, lpBuffer, nSize, lpBytes ); } @@ -130,7 +136,13 @@ NTSTATUS NativeWow64::WriteProcessMemoryT( ptr_t lpBaseAddress, LPCVOID lpBuffer DWORD64 junk = 0; if (lpBytes == nullptr) lpBytes = &junk; - + + if (_wowBarrier.targetWow64) { + SetLastNtStatus(STATUS_SUCCESS); + WriteProcessMemory(_hProcess, reinterpret_cast(lpBaseAddress), lpBuffer, nSize, reinterpret_cast(lpBytes)); + return LastNtStatus(); + } + return SAFE_NATIVE_CALL( NtWow64WriteVirtualMemory64, _hProcess, lpBaseAddress, (LPVOID)lpBuffer, nSize, lpBytes ); } @@ -144,6 +156,10 @@ NTSTATUS NativeWow64::WriteProcessMemoryT( ptr_t lpBaseAddress, LPCVOID lpBuffer NTSTATUS NativeWow64::QueryProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBuffer, uint32_t bufSize ) { ULONG length = 0; + + if (_wowBarrier.targetWow64) + return SAFE_NATIVE_CALL( NtQueryInformationProcess, _hProcess, infoClass, lpBuffer, bufSize, &length ); + return SAFE_NATIVE_CALL( NtWow64QueryInformationProcess64, _hProcess, infoClass, lpBuffer, bufSize, &length ); } @@ -381,4 +397,4 @@ ptr_t NativeWow64::getTEB( HANDLE hThread, _TEB64* pteb ) return 0; } -} \ No newline at end of file +} From 92a627b80e65bf7171e6147012c760b75852f6e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=B1=E4=BA=A7=E4=B8=BB=E4=B9=89=E6=8E=A5=E7=8F=AD?= =?UTF-8?q?=E4=BA=BA?= <503407184@qq.com> Date: Tue, 28 Apr 2020 18:38:26 +0800 Subject: [PATCH 08/38] fix: 32bit read 64bit address failed --- src/BlackBone/Subsystem/Wow64Subsystem.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/BlackBone/Subsystem/Wow64Subsystem.cpp b/src/BlackBone/Subsystem/Wow64Subsystem.cpp index 776f4f2e..6a67abd4 100644 --- a/src/BlackBone/Subsystem/Wow64Subsystem.cpp +++ b/src/BlackBone/Subsystem/Wow64Subsystem.cpp @@ -114,7 +114,7 @@ NTSTATUS NativeWow64::ReadProcessMemoryT( ptr_t lpBaseAddress, LPVOID lpBuffer, if (lpBytes == nullptr) lpBytes = &junk; - if (_wowBarrier.targetWow64) { + if (_wowBarrier.targetWow64 && lpBaseAddress < NativeWow64::maxAddr32()) { SetLastNtStatus(STATUS_SUCCESS); ReadProcessMemory(_hProcess, reinterpret_cast(lpBaseAddress), lpBuffer, nSize, reinterpret_cast(lpBytes)); return LastNtStatus(); @@ -137,7 +137,7 @@ NTSTATUS NativeWow64::WriteProcessMemoryT( ptr_t lpBaseAddress, LPCVOID lpBuffer if (lpBytes == nullptr) lpBytes = &junk; - if (_wowBarrier.targetWow64) { + if (_wowBarrier.targetWow64 && lpBaseAddress < NativeWow64::maxAddr32()) { SetLastNtStatus(STATUS_SUCCESS); WriteProcessMemory(_hProcess, reinterpret_cast(lpBaseAddress), lpBuffer, nSize, reinterpret_cast(lpBytes)); return LastNtStatus(); @@ -157,8 +157,8 @@ NTSTATUS NativeWow64::QueryProcessInfoT( PROCESSINFOCLASS infoClass, LPVOID lpBu { ULONG length = 0; - if (_wowBarrier.targetWow64) - return SAFE_NATIVE_CALL( NtQueryInformationProcess, _hProcess, infoClass, lpBuffer, bufSize, &length ); + //if (_wowBarrier.targetWow64) + // return SAFE_NATIVE_CALL( NtQueryInformationProcess, _hProcess, infoClass, lpBuffer, bufSize, &length ); return SAFE_NATIVE_CALL( NtWow64QueryInformationProcess64, _hProcess, infoClass, lpBuffer, bufSize, &length ); } From 5eb0732d1a1897876c362ae07ba53438d7501770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=B1=E4=BA=A7=E4=B8=BB=E4=B9=89=E6=8E=A5=E7=8F=AD?= =?UTF-8?q?=E4=BA=BA?= <503407184@qq.com> Date: Tue, 28 Apr 2020 18:39:44 +0800 Subject: [PATCH 09/38] fix: 32bit read 64bit address failed --- src/BlackBone/Subsystem/NativeSubsystem.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/BlackBone/Subsystem/NativeSubsystem.h b/src/BlackBone/Subsystem/NativeSubsystem.h index bf5c3935..f0493a63 100644 --- a/src/BlackBone/Subsystem/NativeSubsystem.h +++ b/src/BlackBone/Subsystem/NativeSubsystem.h @@ -221,6 +221,12 @@ class Native /// /// Address value BLACKBONE_API inline ptr_t maxAddr() const { return 0x7FFFFFFEFFFF; } + + /// + /// Get highest possible valid address value + /// + /// Address value + BLACKBONE_API inline ptr_t maxAddr32() const { return 0x7FFFFFFF; } /// /// Get page size @@ -257,4 +263,4 @@ class Native uint32_t _pageSize; }; -} \ No newline at end of file +} From ed951dffe0ee62102cb2363523d6056591e4d733 Mon Sep 17 00:00:00 2001 From: DarthTon Date: Fri, 5 Jun 2020 13:48:45 +0300 Subject: [PATCH 10/38] fixed #379: don't use process pseudo-handle --- src/BlackBone/Process/ProcessCore.cpp | 4 ++++ src/Samples/Main.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/BlackBone/Process/ProcessCore.cpp b/src/BlackBone/Process/ProcessCore.cpp index c6eaaf79..b98e2d9d 100644 --- a/src/BlackBone/Process/ProcessCore.cpp +++ b/src/BlackBone/Process/ProcessCore.cpp @@ -56,6 +56,10 @@ NTSTATUS ProcessCore::Open( HANDLE handle ) _hProcess = handle; _pid = GetProcessId( _hProcess ); + // Some routines in win10 do not support pseudo handle + if (IsWindows10OrGreater() && _pid == GetCurrentProcessId()) + _hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, _pid ); + return Init(); } diff --git a/src/Samples/Main.cpp b/src/Samples/Main.cpp index 939ddb5f..94bc1cc0 100644 --- a/src/Samples/Main.cpp +++ b/src/Samples/Main.cpp @@ -11,6 +11,10 @@ void MapCmdFromMem(); int main( int /*argc*/, char* /*argv[]*/ ) { + blackbone::Process _process; + _ASSERT( NT_SUCCESS( _process.Attach( GetCurrentProcess() ) ) ); + auto m = _process.modules().GetMainModule(); + // List all process PIDs matching name auto pids = Process::EnumByName( L"explorer.exe" ); From e5747ef4c90a6e706bc24665fde1319dffcc04a6 Mon Sep 17 00:00:00 2001 From: DarthTon Date: Fri, 5 Jun 2020 14:54:16 +0300 Subject: [PATCH 11/38] fixed test and code after PR merge --- src/BlackBone/Process/ProcessModules.cpp | 2 +- src/BlackBoneTest/TestBasic.cpp | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/BlackBone/Process/ProcessModules.cpp b/src/BlackBone/Process/ProcessModules.cpp index a41ca0c9..744e49cd 100644 --- a/src/BlackBone/Process/ProcessModules.cpp +++ b/src/BlackBone/Process/ProcessModules.cpp @@ -135,7 +135,7 @@ 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) diff --git a/src/BlackBoneTest/TestBasic.cpp b/src/BlackBoneTest/TestBasic.cpp index e1d92237..d096c74d 100644 --- a/src/BlackBoneTest/TestBasic.cpp +++ b/src/BlackBoneTest/TestBasic.cpp @@ -59,21 +59,22 @@ namespace Testing TEST_METHOD( InvalidHandles ) { - Process proc; + Process baseProc; + baseProc.CreateAndAttach( GetTestHelperHost32() ); - HANDLE hProc = OpenProcess( PROCESS_ALL_ACCESS & ~PROCESS_CREATE_THREAD, FALSE, GetCurrentProcessId() ); + 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 ); - AssertEx::NtSuccess( proc.Attach( hProc ) ); - AssertEx::AreEqual( STATUS_ACCESS_DENIED, proc.threads().CreateNew( reinterpret_cast(&VoidFn), 0 ).status ); - proc.Detach(); - - hProc = OpenProcess( PROCESS_ALL_ACCESS & ~PROCESS_VM_READ, FALSE, GetCurrentProcessId() ); - AssertEx::NtSuccess( proc.Attach( hProc ) ); + Process proc2; + AssertEx::NtSuccess( proc2.Attach( baseProc.pid(), PROCESS_ALL_ACCESS & ~PROCESS_VM_READ ) ); PEB_T peb = { }; - AssertEx::IsNotZero( proc.core().peb( &peb ) ); + AssertEx::IsNotZero( proc2.core().peb( &peb ) ); AssertEx::IsZero( peb.ImageBaseAddress ); AssertEx::AreEqual( STATUS_ACCESS_DENIED, LastNtStatus() ); + + baseProc.Terminate(); } private: From fbd594e500f9eda657c6d24b47deffb35132448e Mon Sep 17 00:00:00 2001 From: DarthTon Date: Fri, 5 Jun 2020 15:23:09 +0300 Subject: [PATCH 12/38] added tests for modules --- src/BlackBoneTest/BlackBoneTest.vcxproj | 1 + .../BlackBoneTest.vcxproj.filters | 3 + src/BlackBoneTest/TestModules.cpp | 67 +++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 src/BlackBoneTest/TestModules.cpp diff --git a/src/BlackBoneTest/BlackBoneTest.vcxproj b/src/BlackBoneTest/BlackBoneTest.vcxproj index a0ee67b9..eb95eeb4 100644 --- a/src/BlackBoneTest/BlackBoneTest.vcxproj +++ b/src/BlackBoneTest/BlackBoneTest.vcxproj @@ -315,6 +315,7 @@ + diff --git a/src/BlackBoneTest/BlackBoneTest.vcxproj.filters b/src/BlackBoneTest/BlackBoneTest.vcxproj.filters index bf59b1a5..aa1c8c2d 100644 --- a/src/BlackBoneTest/BlackBoneTest.vcxproj.filters +++ b/src/BlackBoneTest/BlackBoneTest.vcxproj.filters @@ -43,6 +43,9 @@ Tests + + Tests + diff --git a/src/BlackBoneTest/TestModules.cpp b/src/BlackBoneTest/TestModules.cpp new file mode 100644 index 00000000..995ba31e --- /dev/null +++ b/src/BlackBoneTest/TestModules.cpp @@ -0,0 +1,67 @@ +#include "Common.h" + +namespace Testing +{ + +TEST_CLASS( Modules ) +{ +public: + TEST_METHOD_INITIALIZE( ClassInitialize ) + { + AssertEx::NtSuccess( _proc.Attach( GetCurrentProcessId() ) ); + } + + TEST_METHOD( MainModule ) + { + auto mod = _proc.modules().GetMainModule(); + ValidateModule( *mod, (module_t)GetModuleHandleW( nullptr ) ); + } + + TEST_METHOD( EnumModules ) + { + auto name = L"kernel32.dll"; + + auto manuals = _proc.modules().GetManualModules(); + AssertEx::IsTrue( manuals.empty() ); + + auto byLdrList = _proc.modules().GetModule( name, LdrList ); + + _proc.modules().reset(); + auto byHeaders = _proc.modules().GetModule( name, Sections ); + + ValidateModule( *byLdrList, reinterpret_cast(GetModuleHandleW( name )) ); + ValidateModule( *byHeaders, reinterpret_cast(GetModuleHandleW( name )) ); + + AssertEx::AreEqual( byLdrList->baseAddress, byHeaders->baseAddress ); + AssertEx::AreEqual( byLdrList->name, byHeaders->name ); + } + + void ValidateModule( const ModuleData& mod, module_t expectedBase ) + { + AssertEx::AreEqual( expectedBase, mod.baseAddress ); + AssertEx::IsNotZero( mod.size ); + AssertEx::IsFalse( mod.manual ); + AssertEx::IsFalse( mod.name.empty() ); + AssertEx::IsFalse( mod.fullPath.empty() ); + +#ifdef USE64 + AssertEx::AreEqual( static_cast(mt_mod64), static_cast(mod.type) ); +#else + AssertEx::AreEqual( static_cast(mt_mod32), static_cast(mod.type) ); +#endif + } + + TEST_METHOD( Export ) + { + auto expected = GetProcAddress( GetModuleHandleW( L"kernel32.dll" ), "CreateFileW" ); + 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 ); + } + +private: + Process _proc; +}; +} \ No newline at end of file From fb44f59ce8fa7adb9beabf1c4c8512bab107cdd7 Mon Sep 17 00:00:00 2001 From: DarthTon Date: Sat, 7 Dec 2019 16:03:20 +0200 Subject: [PATCH 13/38] github actions refactoring --- .github/workflows/driver.yaml | 22 +++++++++++++++++++ .github/workflows/lib.yaml | 30 ++++++++++++++++++++++++++ .github/workflows/main.yaml | 40 ----------------------------------- BlackBone.sln | 3 --- src/BlackBoneDrv/Private.c | 2 +- 5 files changed, 53 insertions(+), 44 deletions(-) create mode 100644 .github/workflows/driver.yaml create mode 100644 .github/workflows/lib.yaml delete mode 100644 .github/workflows/main.yaml diff --git a/.github/workflows/driver.yaml b/.github/workflows/driver.yaml new file mode 100644 index 00000000..2a357573 --- /dev/null +++ b/.github/workflows/driver.yaml @@ -0,0 +1,22 @@ +name: Driver +on: + pull_request: + paths: + - 'src/BlackBoneDrv/**' + +jobs: + driver: + name: Build driver + strategy: + matrix: + configuration: [Win10Release, 'Win8.1 Release', 'Win8 Release', 'Win7 Release'] + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v1 + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.0.0 + - name: Build + shell: cmd + run: | + MSBuild.exe src\BlackBoneDrv\BlackBoneDrv.sln /p:Platform="x64" /p:Configuration="${{ matrix.configuration }}" diff --git a/.github/workflows/lib.yaml b/.github/workflows/lib.yaml new file mode 100644 index 00000000..2c459cbf --- /dev/null +++ b/.github/workflows/lib.yaml @@ -0,0 +1,30 @@ +name: Library +on: + pull_request: + paths: + - 'src/**' + - '!src/BlackBoneDrv/**' + +jobs: + library: + name: Build and test library + strategy: + matrix: + platfom: [win32, x64] + configuration: [Release, Release(DLL)] + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v1 + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.0.0 + - name: Add VSTest to PATH + uses: darenm/Setup-VSTest@v1 + - name: Build + shell: cmd + run: | + MSBuild.exe BlackBone.sln /p:CI=true /p:Platform="${{ matrix.platfom }}" /p:Configuration="${{ matrix.configuration }}" + - name: Test + shell: cmd + run: | + vstest.console.exe "build/${{ matrix.platfom }}/${{ matrix.configuration }}/BlackboneTest.dll" diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml deleted file mode 100644 index ac3bb6b2..00000000 --- a/.github/workflows/main.yaml +++ /dev/null @@ -1,40 +0,0 @@ -name: Main -on: [push] - -jobs: - library: - name: Build and test library - strategy: - matrix: - platfom: [win32, x64] - #, Debug(DLL), Release(DLL) - configuration: [Debug, Release] - runs-on: windows-2019 - steps: - - name: Checkout - if: contains(github.event.head_commit.message, 'driver') != true - uses: actions/checkout@v1 - - name: Build - if: contains(github.event.head_commit.message, 'driver') != true - run: | - "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && MSBuild.exe BlackBone.sln /p:CI=true /p:Platform="${{ matrix.platfom }}" /p:Configuration="${{ matrix.configuration }}" - - name: Test - if: contains(github.event.head_commit.message, 'driver') != true - run: | - "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && vstest.console "build/${{ matrix.platfom }}/${{ matrix.configuration }}/BlackboneTest.dll" - - driver: - name: Build driver - strategy: - matrix: - configuration: [Win10Debug, Win10Release, 'Win8.1 Debug', 'Win8.1 Release', 'Win8 Debug', 'Win8 Release', 'Win7 Debug', 'Win7 Release'] - runs-on: windows-2019 - steps: - - name: Checkout - if: contains(github.event.head_commit.message, 'driver') - uses: actions/checkout@v1 - - name: Build - if: contains(github.event.head_commit.message, 'driver') - run: | - "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && MSBuild.exe src\BlackBoneDrv\BlackBoneDrv.sln /p:Platform="x64" /p:Configuration="${{ matrix.configuration }}" - \ No newline at end of file diff --git a/BlackBone.sln b/BlackBone.sln index 1e8f4cf0..fb3377eb 100644 --- a/BlackBone.sln +++ b/BlackBone.sln @@ -6,9 +6,6 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlackBone", "src\BlackBone\BlackBone.vcxproj", "{A2C53563-46F5-4D87-903F-3F1F2FDB2DEB}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Samples", "src\Samples\Samples.vcxproj", "{D31B07B5-C75F-4382-B07F-D95922764BD7}" - ProjectSection(ProjectDependencies) = postProject - {A2C53563-46F5-4D87-903F-3F1F2FDB2DEB} = {A2C53563-46F5-4D87-903F-3F1F2FDB2DEB} - EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlackBoneTest", "src\BlackBoneTest\BlackBoneTest.vcxproj", "{15F6F215-4A5E-4B57-B0A0-90B067111285}" EndProject diff --git a/src/BlackBoneDrv/Private.c b/src/BlackBoneDrv/Private.c index 3ae0df66..05626c39 100644 --- a/src/BlackBoneDrv/Private.c +++ b/src/BlackBoneDrv/Private.c @@ -37,7 +37,7 @@ VOID InitializeDebuggerBlock() context.ContextFlags = CONTEXT_FULL; RtlCaptureContext( &context ); - PDUMP_HEADER dumpHeader = ExAllocatePool( NonPagedPool, DUMP_BLOCK_SIZE ); + PDUMP_HEADER dumpHeader = ExAllocatePoolWithTag( NonPagedPool, DUMP_BLOCK_SIZE, BB_POOL_TAG ); if (dumpHeader) { KeCapturePersistentThreadState( &context, NULL, 0, 0, 0, 0, 0, dumpHeader ); From b42d2abca399a46f997079d997e7b7cebdd5d92b Mon Sep 17 00:00:00 2001 From: DarthTon Date: Sun, 7 Jun 2020 18:15:47 +0300 Subject: [PATCH 14/38] removed broken test code --- src/Samples/Main.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Samples/Main.cpp b/src/Samples/Main.cpp index 94bc1cc0..939ddb5f 100644 --- a/src/Samples/Main.cpp +++ b/src/Samples/Main.cpp @@ -11,10 +11,6 @@ void MapCmdFromMem(); int main( int /*argc*/, char* /*argv[]*/ ) { - blackbone::Process _process; - _ASSERT( NT_SUCCESS( _process.Attach( GetCurrentProcess() ) ) ); - auto m = _process.modules().GetMainModule(); - // List all process PIDs matching name auto pids = Process::EnumByName( L"explorer.exe" ); From d83da070a34e5b390c0530492941f03bb2eff82f Mon Sep 17 00:00:00 2001 From: DarthTon Date: Sun, 7 Jun 2020 18:18:04 +0300 Subject: [PATCH 15/38] run checks on every push --- .github/workflows/driver.yaml | 2 +- .github/workflows/lib.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/driver.yaml b/.github/workflows/driver.yaml index 2a357573..a5f29b7c 100644 --- a/.github/workflows/driver.yaml +++ b/.github/workflows/driver.yaml @@ -1,6 +1,6 @@ name: Driver on: - pull_request: + push: paths: - 'src/BlackBoneDrv/**' diff --git a/.github/workflows/lib.yaml b/.github/workflows/lib.yaml index 2c459cbf..6a3ffc25 100644 --- a/.github/workflows/lib.yaml +++ b/.github/workflows/lib.yaml @@ -1,6 +1,6 @@ name: Library on: - pull_request: + push: paths: - 'src/**' - '!src/BlackBoneDrv/**' From 33739823c243e5f791ff23d891008aebd5b181b5 Mon Sep 17 00:00:00 2001 From: DarthTon Date: Sun, 7 Jun 2020 18:22:20 +0300 Subject: [PATCH 16/38] updated badges --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 18d26da8..a8d58f85 100644 --- a/README.md +++ b/README.md @@ -94,4 +94,4 @@ ## License Blackbone is licensed under the MIT License. Dependencies are under their respective licenses. -[![Build status](https://github.com/DarthTon/BlackBone/workflows/Main/badge.svg)] +![Library](https://github.com/DarthTon/Blackbone/workflows/Library/badge.svg?branch=master) ![Driver](https://github.com/DarthTon/Blackbone/workflows/Driver/badge.svg?branch=master) From bb8f4c6f727f0560572f9dcd0f7c5656f08378b4 Mon Sep 17 00:00:00 2001 From: DarthTon Date: Mon, 8 Jun 2020 00:45:03 +0300 Subject: [PATCH 17/38] fixed #405: mmap: get process heap base from PEB --- src/BlackBone/ManualMap/Native/NtLoader.cpp | 27 +++------------------ 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/src/BlackBone/ManualMap/Native/NtLoader.cpp b/src/BlackBone/ManualMap/Native/NtLoader.cpp index 536a276b..2199e0d7 100644 --- a/src/BlackBone/ManualMap/Native/NtLoader.cpp +++ b/src/BlackBone/ManualMap/Native/NtLoader.cpp @@ -831,30 +831,11 @@ bool NtLdr::FindLdrpModuleIndexBase() template bool NtLdr::FindLdrHeap() { - int32_t retries = 50; - _PEB_T Peb = { 0 }; - - _process.core().peb( &Peb ); - for (; Peb.Ldr == 0 && retries > 0; retries--, Sleep( 10 )) - _process.core().peb( &Peb ); - - if (Peb.Ldr) + _PEB_T peb = { }; + if (_process.core().peb( &peb ) != 0) { - auto Ldr = _process.memory().Read<_PEB_LDR_DATA2_T>( Peb.Ldr ); - if (!Ldr) - return false; - - for (; Ldr->InMemoryOrderModuleList.Flink == Ldr->InMemoryOrderModuleList.Blink && retries > 0; retries--, Sleep( 10 )) - Ldr = _process.memory().Read<_PEB_LDR_DATA2_T>( Peb.Ldr ); - - MEMORY_BASIC_INFORMATION64 mbi = { 0 }; - auto NtdllEntry = Ldr->InMemoryOrderModuleList.Flink; - if (NT_SUCCESS( _process.core().native()->VirtualQueryExT( NtdllEntry, &mbi ) )) - { - _LdrHeapBase = static_cast(mbi.AllocationBase); - assert( _LdrHeapBase != _process.modules().GetModule( L"ntdll.dll" )->baseAddress ); - return true; - } + _LdrHeapBase = peb.ProcessHeap; + return true; } return false; From 231f1747161cce6944589c4a748c4d0a71340fbd Mon Sep 17 00:00:00 2001 From: DarthTon Date: Mon, 8 Jun 2020 16:35:57 +0300 Subject: [PATCH 18/38] fixed #393: added switch to ignore DEP when casting memory protection --- src/BlackBone/Process/MemBlock.cpp | 16 ++++++++++------ src/BlackBone/Process/ProcessMemory.cpp | 6 +++++- src/BlackBone/Process/ProcessMemory.h | 20 ++++++++++++++++++++ 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/BlackBone/Process/MemBlock.cpp b/src/BlackBone/Process/MemBlock.cpp index bc48e394..e212f6fc 100644 --- a/src/BlackBone/Process/MemBlock.cpp +++ b/src/BlackBone/Process/MemBlock.cpp @@ -89,13 +89,15 @@ call_result_t MemBlock::Allocate( ) { ptr_t desired64 = desired; - DWORD newProt = CastProtection( protection, process.core().DEP() ); + DWORD finalProt = protection; + if(process.protectionCasting() == MemProtectionCasting::useDep) + finalProt = CastProtection( protection, process.core().DEP() ); - NTSTATUS status = process.core().native()->VirtualAllocExT( desired64, size, MEM_RESERVE | MEM_COMMIT, newProt ); + 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, newProt ); + 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 @@ -157,13 +159,15 @@ NTSTATUS MemBlock::Protect( DWORD protection, uintptr_t offset /*= 0*/, size_t s if (!_pImpl) return STATUS_MEMORY_NOT_ALLOCATED; - auto prot = CastProtection( protection, _pImpl->_memory->core().DEP() ); + DWORD finalProt = protection; + if (_pImpl->_memory->protectionCasting() == MemProtectionCasting::useDep) + 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, prot ) : - _pImpl->_memory->Protect( _pImpl->_ptr + offset, size, prot, pOld ); + return _pImpl->_physical ? Driver().ProtectMem( _pImpl->_memory->core().pid(), _pImpl->_ptr + offset, size, finalProt ) : + _pImpl->_memory->Protect( _pImpl->_ptr + offset, size, finalProt, pOld ); } /// diff --git a/src/BlackBone/Process/ProcessMemory.cpp b/src/BlackBone/Process/ProcessMemory.cpp index 04bba612..1ce7d591 100644 --- a/src/BlackBone/Process/ProcessMemory.cpp +++ b/src/BlackBone/Process/ProcessMemory.cpp @@ -74,7 +74,11 @@ NTSTATUS ProcessMemory::Protect( ptr_t pAddr, size_t size, DWORD flProtect, DWOR if (pOld == nullptr) pOld = &junk; - return _core.native()->VirtualProtectExT( pAddr, size, CastProtection( flProtect, _core.DEP() ), pOld ); + DWORD finalProt = flProtect; + if (_casting == MemProtectionCasting::useDep) + finalProt = CastProtection( flProtect, _core.DEP() ); + + return _core.native()->VirtualProtectExT( pAddr, size, finalProt, pOld ); } /// diff --git a/src/BlackBone/Process/ProcessMemory.h b/src/BlackBone/Process/ProcessMemory.h index 4e5c28c9..38dbfa9e 100644 --- a/src/BlackBone/Process/ProcessMemory.h +++ b/src/BlackBone/Process/ProcessMemory.h @@ -10,6 +10,13 @@ namespace blackbone { + +enum class MemProtectionCasting +{ + none, // Don't change provided memory protection flags + useDep // Strip executable flag if DEP is off +}; + class ProcessMemory : public RemoteMemory { public: @@ -178,6 +185,18 @@ class ProcessMemory : public RemoteMemory /// Found regions BLACKBONE_API std::vector EnumRegions( bool includeFree = false ); + /// + /// Get memory protection casting behavior + /// + /// current behavior + BLACKBONE_API MemProtectionCasting protectionCasting() { return _casting; } + + /// + /// Set memory protection casting behavior + /// + /// 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; } @@ -188,6 +207,7 @@ class ProcessMemory : public RemoteMemory private: class Process* _process; // Owning process object class ProcessCore& _core; // Core routines + MemProtectionCasting _casting = MemProtectionCasting::useDep; }; } \ No newline at end of file From 8bda02aaa55920e3ce388e7089f3e84631772949 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Fri, 4 Dec 2020 21:57:37 +0200 Subject: [PATCH 19/38] Fixed a memory leak in PDBHelper --- src/BlackBone/Symbols/PDBHelper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BlackBone/Symbols/PDBHelper.cpp b/src/BlackBone/Symbols/PDBHelper.cpp index bb4a8ffe..bd3e0002 100644 --- a/src/BlackBone/Symbols/PDBHelper.cpp +++ b/src/BlackBone/Symbols/PDBHelper.cpp @@ -136,7 +136,7 @@ HRESULT PDBHelper::PopulateSymbols() while (SUCCEEDED( symbols->Next( 1, &symbol, &count ) ) && count != 0) { DWORD rva = 0; - BSTR name = nullptr; + CComBSTR name; symbol->get_relativeVirtualAddress( &rva ); symbol->get_undecoratedName( &name ); @@ -166,4 +166,4 @@ HRESULT PDBHelper::PopulateSymbols() return S_OK; } -} \ No newline at end of file +} From b81d650454b89bb80ff5e350b16684b420dec89e Mon Sep 17 00:00:00 2001 From: Alemarius Nexus Date: Sun, 19 Jul 2020 00:13:41 +0200 Subject: [PATCH 20/38] Added methods for allocating a MemBlock as close to an address as possible. --- src/BlackBone/Process/MemBlock.cpp | 85 ++++++++++++++++++++++++- src/BlackBone/Process/MemBlock.h | 17 +++++ src/BlackBone/Process/ProcessMemory.cpp | 7 +- src/BlackBone/Process/ProcessMemory.h | 12 +++- 4 files changed, 118 insertions(+), 3 deletions(-) diff --git a/src/BlackBone/Process/MemBlock.cpp b/src/BlackBone/Process/MemBlock.cpp index e212f6fc..0b73a0eb 100644 --- a/src/BlackBone/Process/MemBlock.cpp +++ b/src/BlackBone/Process/MemBlock.cpp @@ -109,6 +109,89 @@ call_result_t MemBlock::Allocate( 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*/ + ) +{ + DWORD finalProt = protection; + if(process.protectionCasting() == MemProtectionCasting::useDep) + finalProt = CastProtection( protection, process.core().DEP() ); + + NTSTATUS status = STATUS_SUCCESS; + + uint32_t pageSize = process.core().native()->pageSize(); + ptr_t alignedSize = Align( size, pageSize ); + + SYSTEM_INFO sinfo; + GetNativeSystemInfo(&sinfo); + + MemBlock buf; + + ptr_t leftLimit = (ptr_t) sinfo.lpMinimumApplicationAddress; + ptr_t rightLimit = (ptr_t) sinfo.lpMaximumApplicationAddress; + + ptr_t right = (desired / pageSize) * pageSize; + ptr_t left = right - alignedSize; + + MEMORY_BASIC_INFORMATION64 minfo; + + 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 (!buf.valid()) + return MemBlock::Allocate( process, size, desired, protection, own ); + return buf; +} + /// /// Reallocate existing block for new size /// @@ -257,4 +340,4 @@ void MemBlock::Reset() _pImpl.reset(); } -} \ No newline at end of file +} diff --git a/src/BlackBone/Process/MemBlock.h b/src/BlackBone/Process/MemBlock.h index 31b0eca8..1800d013 100644 --- a/src/BlackBone/Process/MemBlock.h +++ b/src/BlackBone/Process/MemBlock.h @@ -133,6 +133,23 @@ class MemBlock bool own = true ); + /// + /// Allocate new memory block as close to a given location as possible. + /// + /// Process memory routines + /// Block size + /// 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 AllocateClosest( + class ProcessMemory& process, + size_t size, + ptr_t desired, + DWORD protection = PAGE_EXECUTE_READWRITE, + bool own = true + ); + /// /// Reallocate existing block for new size /// diff --git a/src/BlackBone/Process/ProcessMemory.cpp b/src/BlackBone/Process/ProcessMemory.cpp index 1ce7d591..945945a0 100644 --- a/src/BlackBone/Process/ProcessMemory.cpp +++ b/src/BlackBone/Process/ProcessMemory.cpp @@ -29,6 +29,11 @@ call_result_t ProcessMemory::Allocate( size_t size, DWORD protection / 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*/ ) +{ + return MemBlock::AllocateClosest( *this, size, desired, protection, own ); +} + /// /// Free memory /// @@ -206,4 +211,4 @@ std::vector ProcessMemory::EnumRegions( bool include return _core.native()->EnumRegions( includeFree ); } -} \ No newline at end of file +} diff --git a/src/BlackBone/Process/ProcessMemory.h b/src/BlackBone/Process/ProcessMemory.h index 38dbfa9e..9ce9bab4 100644 --- a/src/BlackBone/Process/ProcessMemory.h +++ b/src/BlackBone/Process/ProcessMemory.h @@ -33,6 +33,16 @@ class ProcessMemory : public RemoteMemory /// 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 ); + /// + /// Allocate new memory block as close to a desired location as possible + /// + /// Block size + /// 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 AllocateClosest( size_t size, DWORD protection = PAGE_EXECUTE_READWRITE, ptr_t desired = 0, bool own = true ); + /// /// Free memory /// @@ -210,4 +220,4 @@ class ProcessMemory : public RemoteMemory MemProtectionCasting _casting = MemProtectionCasting::useDep; }; -} \ No newline at end of file +} From 34ca787086e208922bd3b8b5389c70b580525bb9 Mon Sep 17 00:00:00 2001 From: Alemarius Nexus Date: Sun, 19 Jul 2020 00:18:34 +0200 Subject: [PATCH 21/38] Made RemoteLocalHook work, both on x86 and x86_64. Yay! --- src/BlackBone/Process/RPC/RemoteLocalHook.cpp | 302 +++++++++++++----- src/BlackBone/Process/RPC/RemoteLocalHook.h | 94 +++--- 2 files changed, 266 insertions(+), 130 deletions(-) diff --git a/src/BlackBone/Process/RPC/RemoteLocalHook.cpp b/src/BlackBone/Process/RPC/RemoteLocalHook.cpp index 35fa00c9..e5eb6687 100644 --- a/src/BlackBone/Process/RPC/RemoteLocalHook.cpp +++ b/src/BlackBone/Process/RPC/RemoteLocalHook.cpp @@ -3,6 +3,15 @@ #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 { @@ -16,99 +25,165 @@ RemoteLocalHook::~RemoteLocalHook() Restore(); } -NTSTATUS RemoteLocalHook::AllocateMem( ptr_t /*address*/, size_t codeSize ) +NTSTATUS RemoteLocalHook::AllocateMem( ptr_t address, size_t hookCodeSize ) { - auto pagesize = _process.core().native()->pageSize(); - auto size = Align( codeSize, pagesize ) + Align( sizeof( _ctx ), pagesize ); + auto pagesize = _process.core().native()->pageSize(); + auto size = Align( hookCodeSize + THUNK_MAX_SIZE, pagesize ); - auto allocation = _process.memory().Allocate( size, PAGE_EXECUTE_READWRITE ); + auto allocation = _process.memory().AllocateClosest( size, PAGE_EXECUTE_READWRITE, address ); if (!allocation) return allocation.status; _hookData = std::move( allocation.result() ); - _pHookCode = _hookData.ptr(); - _pThunkCode = _pHookCode + _hookData.size() - pagesize; - return allocation.status; } -NTSTATUS RemoteLocalHook::SetHook( ptr_t address, asmjit::Assembler& hook ) +void RemoteLocalHook::SetJumpStrategy(eJumpStrategy strategy) { - _address = address; - NTSTATUS status = AllocateMem( address, hook.getCodeSize() ); + _jumpStrategy = strategy; +} + +void RemoteLocalHook::SetJumpRegister(const asmjit::X86GpReg& reg) +{ + _jumpRegister = ® +} + +size_t RemoteLocalHook::GetDisplacedOriginalCode( uint8_t* code ) +{ + 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; - return _process.core().isWow64() ? SetHook32( address, hook ) : SetHook64( address, hook ); + _ctx.thunkAddr = _hookData.ptr() + maxHookSize; + + + 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; + } + + _prepared = true; + + return STATUS_SUCCESS; } -NTSTATUS RemoteLocalHook::SetHook32( ptr_t address, asmjit::Assembler& hook ) +NTSTATUS RemoteLocalHook::SetHook( ptr_t address, asmjit::Assembler& hook ) { - NTSTATUS status = STATUS_SUCCESS; + bool x64 = !_process.core().isWow64(); auto& mem = _process.memory(); - if (!CopyOldCode( address, false )) - return STATUS_PARTIAL_COPY; + NTSTATUS status = STATUS_SUCCESS; - _ctx.hook32.codeSize = 5; - _ctx.hook32.jmp.opcode = 0xE9; - _ctx.hook32.jmp.ptr = static_cast(_pThunkCode - address - 5); + if (!_prepared) { + status = PrepareHook( address, hook.getCodeSize() ); + if (!NT_SUCCESS( status )) { + return status; + } + } - uint8_t buf[0x1000] = { 0 }; - hook.setBaseAddress( _hookData.ptr() ); - hook.relocCode( buf ); + uint8_t hookCode[256]; + uint8_t* heapHookCode = nullptr; // Only used if hook.getCodeSize() > sizeof(hookCode) - mem.Write( _pHookCode, buf ); - mem.Write( _pThunkCode, _ctx.hook32.codeSize + _ctx.hook32.jmp_size, _ctx.hook32.original_code ); + if (hook.getCodeSize() > sizeof(hookCode)) { + heapHookCode = new uint8_t[hook.getCodeSize()]; + } - DWORD flOld = 0; - mem.Protect( address, sizeof( _ctx.hook32.jmp_code ), PAGE_EXECUTE_READWRITE, &flOld ); - status = mem.Write( address, _ctx.hook32.jmp_code ); - mem.Protect( address, sizeof( _ctx.hook32.jmp_code ), flOld ); + hook.setBaseAddress( _hookData.ptr() ); + hook.relocCode( heapHookCode ? heapHookCode : hookCode ); - if (NT_SUCCESS( status )) - _hooked = true; + uint8_t jmpBackCode[sizeof(_ctx.hookJumpCode)]; + uint8_t jmpBackCodeSize; - return status; -} + jmpBackCodeSize = GenerateJump(jmpBackCode, address + _ctx.origCodeSize, _hookData.ptr() + hook.getCodeSize() + _ctx.origCodeSize, x64); -NTSTATUS RemoteLocalHook::SetHook64( ptr_t /*address*/, asmjit::Assembler& /*hook*/ ) -{ - _hook64 = true; - return STATUS_NOT_IMPLEMENTED; + if (hook.getCodeSize() > (_ctx.thunkAddr - _hookData.ptr())) { + // Can happen if PrepareHook() was called manually with maxCodeSize < hook.getCodeSize(). + delete[] heapHookCode; + return STATUS_NO_MEMORY; + } + + 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); + } + + delete[] heapHookCode; + + 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 ); + + if (NT_SUCCESS( status )) + _hooked = true; + + return status; } NTSTATUS RemoteLocalHook::Restore() { - if (!_hooked) - return STATUS_SUCCESS; - - DWORD flOld = 0; - _process.memory().Protect( _address, sizeof( _ctx.hook32.jmp_code ), PAGE_EXECUTE_READWRITE, &flOld ); - NTSTATUS status = _process.memory().Write( _address, _ctx.hook32.codeSize, _ctx.hook32.original_code ); - _process.memory().Protect( _address, sizeof( _ctx.hook32.jmp_code ), flOld ); - - if (NT_SUCCESS( status )) - { - _hookData.Free(); - _pHookCode = _pThunkCode = 0; - _address = 0; - _hooked = false; - } + NTSTATUS status = STATUS_SUCCESS; + + 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; } -bool RemoteLocalHook::CopyOldCode( ptr_t address, bool x64 ) +NTSTATUS RemoteLocalHook::CopyOldCode( bool x64 ) { - _process.memory().Read( address, sizeof( _ctx.hook32.original_code ), _ctx.hook32.original_code ); + NTSTATUS status = STATUS_SUCCESS; + + _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.hook32.original_code; - uint8_t* old = (uint8_t*)(_hookData.ptr() + _hookData.size() - _process.core().native()->pageSize()); - uint32_t all_len = 0; + uint8_t* src = _ctx.origCode; + ptr_t newAddr = _ctx.thunkAddr; + uint32_t thunkSize = 0; ldasm_data ld = { 0 }; + const int64_t diffMinVals[] = {0ll, -128ll, -32768ll, -8388608ll, -2147483648ll, -549755813888ll, -140737488355328ll, -36028797018963968ll, -9223372036854775808ll}; + const int64_t diffMaxVals[] = {0ll, 127ll, 32767ll, 8388607ll, 2147483647ll, 549755813887ll, 140737488355327ll, 36028797018963967ll, 9223372036854775807ll}; + do { uint32_t len = ldasm( src, &ld, x64 ); @@ -117,7 +192,7 @@ bool RemoteLocalHook::CopyOldCode( ptr_t address, bool x64 ) if (ld.flags & F_INVALID || (len == 1 && (src[ld.opcd_offset] == 0xCC || src[ld.opcd_offset] == 0xC3)) || (len == 3 && src[ld.opcd_offset] == 0xC2) - || len + all_len > 128) + || len + thunkSize > 128) { break; } @@ -125,49 +200,106 @@ bool RemoteLocalHook::CopyOldCode( ptr_t address, 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; memcpy( &diff, src + ofst, sz ); - if(x64) - { - // exit if jump is greater then 2GB - /*if (_abs64( src + len + diff - old ) > INT_MAX) - { - break; - } - else - { - diff += static_cast(src - old); - memcpy( old + ofst, &diff, sz ); - }*/ - } - else - { - //diff += src - old; - memcpy( src + ofst, &diff, sz ); + // An attempted (partial) solution to https://github.com/DarthTon/Blackbone/issues/418 + // TODO: Do NOT adjust the offset if it points to WITHIN the code that's being moved! + + int64_t newDiff = ((int64_t) diff) + (((ptr_t) (_ctx.address+thunkSize))-newAddr); + + if (newDiff < diffMinVals[sz] || newDiff > diffMaxVals[sz]) { + status = STATUS_NOT_IMPLEMENTED; + break; } + + memcpy(_ctx.patchedOrigCode + thunkSize + ofst, &newDiff, sz); } src += len; - old += len; - all_len += len; + newAddr += len; + thunkSize += len; + } while (thunkSize < _ctx.hookJumpCodeSize); - } while (all_len < sizeof( _ctx.hook32.jmp_code )); + assert(thunkSize <= MaxOriginalCodeLen); - // Failed to copy old code, use backup plan - if (all_len >= sizeof( _ctx.hook32.jmp_code )) + if (thunkSize < _ctx.hookJumpCodeSize) + { + // TODO: Anything else we can do now? + } + else { - _ctx.hook32.jmp_size = 5; - _ctx.hook32.codeSize = all_len; - *src = 0xE9; - //*(int32_t*)(src + 1) = (int32_t)address + all_len - (int32_t)old - 5; - return true; + _ctx.origCodeSize = thunkSize; } - return false; + return status; +} + +uint8_t RemoteLocalHook::GenerateJump( uint8_t* code, ptr_t toAddr, ptr_t fromAddr, bool x64 ) const +{ + uint8_t size; + + 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 size; } } diff --git a/src/BlackBone/Process/RPC/RemoteLocalHook.h b/src/BlackBone/Process/RPC/RemoteLocalHook.h index 65377204..2f6f0b3b 100644 --- a/src/BlackBone/Process/RPC/RemoteLocalHook.h +++ b/src/BlackBone/Process/RPC/RemoteLocalHook.h @@ -6,80 +6,84 @@ #include "../MemBlock.h" + namespace blackbone { + /// -/// Hook data, sizeof = 0x50 bytes +/// In-process remote hook /// -#pragma pack(push, 1) -struct HookCtx32 +class RemoteLocalHook { - uint32_t codeSize; // Size of saved code - uint32_t jmp_size; // Size of jump from thunk to original - uint8_t original_code[29]; // Original function code - - union - { - uint8_t jmp_code[5]; // Jump instruction - struct - { - uint8_t opcode; - int32_t ptr; - } jmp; - }; -}; +public: + // Must be large enough to hold ANY jump this code uses + static const size_t MaxHookJumpCodeLen = 14; -struct HookCtx64 -{ - uint64_t dst_ptr; // Target address - uint32_t codeSize; // Size of saved code - uint8_t original_code[32]; // Original function code - uint8_t far_jmp[6]; // Far jump code -}; -#pragma pack(pop) + // 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; -union HookCtx -{ - HookCtx32 hook32; - HookCtx64 hook64; -}; + static const size_t MaxPatchedOriginalCodeLen = MaxOriginalCodeLen; + 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) -/// -/// In-process remote hook -/// -class RemoteLocalHook -{ public: RemoteLocalHook( class Process& process ); ~RemoteLocalHook(); + void SetJumpStrategy(eJumpStrategy strategy); + void SetJumpRegister(const asmjit::X86GpReg& reg); + NTSTATUS SetHook( ptr_t address, asmjit::Assembler& hook ); NTSTATUS Restore(); + NTSTATUS PrepareHook( ptr_t address, size_t maxHookSize ); + + size_t GetDisplacedOriginalCode( uint8_t* code = nullptr ); + + bool isHooked() const { return _hooked; } + private: RemoteLocalHook( const RemoteLocalHook& ) = delete; RemoteLocalHook& operator = (const RemoteLocalHook&) = delete; - NTSTATUS AllocateMem( ptr_t address, size_t codeSize ); - - NTSTATUS SetHook32( ptr_t address, asmjit::Assembler& hook ); + NTSTATUS AllocateMem( ptr_t address, size_t hookCodeSize ); - NTSTATUS SetHook64( ptr_t address, asmjit::Assembler& hook ); + NTSTATUS CopyOldCode( bool x64 ); - bool CopyOldCode( ptr_t address, bool x64 ); + uint8_t GenerateJump( uint8_t* code, ptr_t toAddr, ptr_t fromAddr, bool x64 ) const; private: class Process& _process; HookCtx _ctx; MemBlock _hookData; - ptr_t _pHookCode = 0; - ptr_t _pThunkCode = 0; - ptr_t _address = 0; + eJumpStrategy _jumpStrategy = JumpPushMovRet; + const asmjit::X86GpReg* _jumpRegister = &asmjit::host::rax; + bool _prepared = false; bool _hooked = false; - bool _hook64 = false; }; -} \ No newline at end of file +} From 295da5e2a969404b4985f73237a45dbd9b99f428 Mon Sep 17 00:00:00 2001 From: Alemarius Nexus Date: Sun, 19 Jul 2020 00:22:16 +0200 Subject: [PATCH 22/38] PatternSearch additions: Support for address-aligned search; Use handler functions (e.g. lambda) on match; stop search prematurely if requested by handler function (e.g. when searching for a single match only). --- src/BlackBone/Patterns/PatternSearch.cpp | 270 ++++++++++++++++++----- src/BlackBone/Patterns/PatternSearch.h | 138 +++++++++++- 2 files changed, 338 insertions(+), 70 deletions(-) diff --git a/src/BlackBone/Patterns/PatternSearch.cpp b/src/BlackBone/Patterns/PatternSearch.cpp index 273f5839..ec517df7 100644 --- a/src/BlackBone/Patterns/PatternSearch.cpp +++ b/src/BlackBone/Patterns/PatternSearch.cpp @@ -9,28 +9,28 @@ namespace blackbone { -PatternSearch::PatternSearch( const std::vector& pattern ) - : _pattern( pattern ) +PatternSearch::PatternSearch( const std::vector& pattern, size_t logAlignment /*= 0*/ ) + : _pattern( pattern ), logAlignment(logAlignment) { } -PatternSearch::PatternSearch( const std::initializer_list&& pattern ) - : _pattern( pattern ) +PatternSearch::PatternSearch( const std::initializer_list&& pattern, size_t logAlignment /*= 0*/ ) + : _pattern( pattern ), logAlignment(logAlignment) { } -PatternSearch::PatternSearch( const std::string& pattern ) - : _pattern( pattern.begin(), pattern.end() ) +PatternSearch::PatternSearch( const std::string& pattern, size_t logAlignment /*= 0*/ ) + : _pattern( pattern.begin(), pattern.end() ), logAlignment(logAlignment) { } -PatternSearch::PatternSearch( const char* pattern, size_t len /*= 0*/ ) - : _pattern( pattern, pattern + (len ? len : strlen( pattern )) ) +PatternSearch::PatternSearch( const char* pattern, size_t len /*= 0*/, size_t logAlignment /*= 0*/ ) + : _pattern( pattern, pattern + (len ? len : strlen( pattern )) ), logAlignment(logAlignment) { } -PatternSearch::PatternSearch( const uint8_t* pattern, size_t len /*= 0*/ ) - : _pattern( pattern, pattern + (len ? len : strlen( (const char*)pattern )) ) +PatternSearch::PatternSearch( const uint8_t* pattern, size_t len /*= 0*/, size_t logAlignment /*= 0*/ ) + : _pattern( pattern, pattern + (len ? len : strlen( (const char*)pattern )) ), logAlignment(logAlignment) { } @@ -44,37 +44,42 @@ PatternSearch::PatternSearch( const uint8_t* pattern, size_t len /*= 0*/ ) /// Found results /// Value that will be added to resulting addresses /// Number of found addresses -size_t PatternSearch::Search( - uint8_t wildcard, - void* scanStart, - size_t scanSize, - std::vector& out, - ptr_t value_offset /*= 0*/ +bool PatternSearch::SearchWithHandler( + uint8_t wildcard, + void* scanStart, + size_t scanSize, + MatchHandler handler, + ptr_t value_offset /*= 0*/ ) const { const uint8_t* cstart = (const uint8_t*)scanStart; const uint8_t* cend = cstart + scanSize; + // TODO: Would it be beneficial to use logAlignment here as well? + auto comparer = [&wildcard]( uint8_t val1, uint8_t val2 ) { return (val1 == val2 || val2 == wildcard); }; - for (;;) + bool running = true; + while (running) { const uint8_t* res = std::search( cstart, cend, _pattern.begin(), _pattern.end(), comparer ); if (res >= cend) break; if (value_offset != 0) - out.emplace_back( 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) ); + //out.emplace_back( reinterpret_cast(res) ); + running = !handler( reinterpret_cast(res) ); cstart = res + _pattern.size(); } - return out.size(); + return !running; } /// @@ -86,20 +91,23 @@ size_t PatternSearch::Search( /// Found results /// Value that will be added to resulting addresses /// Number of found addresses -size_t PatternSearch::Search( - void* scanStart, - size_t scanSize, - std::vector& out, - ptr_t value_offset /*= 0*/ - ) const +bool PatternSearch::SearchWithHandler( + 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(); const uint8_t* needle = &_pattern[0]; 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; // // Preprocess @@ -113,26 +121,33 @@ size_t PatternSearch::Search( // // Search // - while (scanSize >= static_cast(nlen)) + bool running = true; + //while (haystack <= haystackEnd && out.size() < maxMatches) + while (haystack <= haystackEnd && running) { for (scan = last; haystack[scan] == needle[scan]; --scan) { if (scan == 0) { if (value_offset != 0) - out.emplace_back( REBASE( haystack, scanStart, value_offset ) ); + //out.emplace_back( REBASE( haystack, scanStart, value_offset ) ); + running = !handler( REBASE( haystack, scanStart, value_offset ) ); else - out.emplace_back( reinterpret_cast(haystack) ); + //out.emplace_back( reinterpret_cast(haystack) ); + running = !handler( reinterpret_cast(haystack) ); break; } } - scanSize -= bad_char_skip[haystack[last]]; haystack += bad_char_skip[haystack[last]]; + + if (logAlignment != 0) { + haystack = (const uint8_t*) (size_t(haystack+alignOffs) & alignMask); + } } - return out.size(); + return !running; } /// @@ -144,23 +159,24 @@ size_t PatternSearch::Search( /// Size of region to scan /// Found results /// Number of found addresses -size_t PatternSearch::SearchRemote( - Process& remote, - uint8_t wildcard, - ptr_t scanStart, - size_t scanSize, - std::vector& out +bool PatternSearch::SearchRemoteWithHandler( + Process& remote, + uint8_t wildcard, + ptr_t scanStart, + size_t scanSize, + 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) - Search( wildcard, pBuffer, scanSize, out, scanStart ); + stopped = SearchWithHandler( wildcard, pBuffer, scanSize, handler, scanStart ); if (pBuffer) VirtualFree( pBuffer, 0, MEM_RELEASE ); - return out.size(); + return stopped; } /// @@ -171,22 +187,23 @@ size_t PatternSearch::SearchRemote( /// Size of region to scan /// Found results /// Number of found addresses -size_t PatternSearch::SearchRemote( - Process& remote, - ptr_t scanStart, - size_t scanSize, - std::vector& out +bool PatternSearch::SearchRemoteWithHandler( + Process& remote, + ptr_t scanStart, + size_t scanSize, + 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) - Search( pBuffer, scanSize, out, scanStart ); + stopped = SearchWithHandler( pBuffer, scanSize, handler, scanStart ); if (pBuffer) VirtualFree( pBuffer, 0, MEM_RELEASE ); - return out.size(); + return stopped; } /// @@ -197,22 +214,21 @@ size_t PatternSearch::SearchRemote( /// Pattern wildcard /// Found results /// Number of found addresses -size_t PatternSearch::SearchRemoteWhole( - Process& remote, - bool useWildcard, - uint8_t wildcard, - std::vector& out +bool PatternSearch::SearchRemoteWholeWithHandler( + Process& remote, + bool useWildcard, + uint8_t wildcard, + MatchHandler handler ) const { MEMORY_BASIC_INFORMATION64 mbi = { 0 }; size_t bufsize = 1 * 1024 * 1024; // 1 MB uint8_t *buf = reinterpret_cast(VirtualAlloc( 0, bufsize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE )); - out.clear(); - auto native = remote.core().native(); - for (ptr_t memptr = native->minAddr(); memptr < native->maxAddr(); memptr = mbi.BaseAddress + mbi.RegionSize) + bool running = true; + for (ptr_t memptr = native->minAddr(); memptr < native->maxAddr() && running; memptr = mbi.BaseAddress + mbi.RegionSize) { auto status = remote.core().native()->VirtualQueryExT( memptr, &mbi ); @@ -237,15 +253,151 @@ size_t PatternSearch::SearchRemoteWhole( continue; if (useWildcard) - Search( wildcard, buf, static_cast(mbi.RegionSize), out, memptr ); + running = !SearchWithHandler( wildcard, buf, static_cast(mbi.RegionSize), handler, memptr ); else - Search( buf, static_cast(mbi.RegionSize), out, memptr ); + running = !SearchWithHandler( buf, static_cast(mbi.RegionSize), handler, memptr ); } VirtualFree( buf, 0, MEM_RELEASE ); - return out.size(); + return !running; +} + + + + +/// +/// Default pattern matching with wildcards. +/// std::search is approximately 2x faster than naive approach. +/// +/// Pattern wildcard +/// Starting address +/// Size of region to scan +/// Found results +/// Value that will be added to resulting addresses +/// Number of found addresses +size_t PatternSearch::Search( + uint8_t wildcard, + void* scanStart, + size_t scanSize, + std::vector& out, + ptr_t value_offset /*= 0*/, + size_t maxMatches /*= SIZE_MAX*/ + ) const +{ + 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); + + return out.size(); +} + +/// +/// Full pattern match, no wildcards. +/// Uses Boyer�Moore�Horspool algorithm. +/// +/// Starting address +/// Size of region to scan +/// Found results +/// Value that will be added to resulting addresses +/// Number of found addresses +size_t PatternSearch::Search( + void* scanStart, + size_t scanSize, + std::vector& out, + ptr_t value_offset /*= 0*/, + size_t maxMatches /*= SIZE_MAX*/ + ) const +{ + 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); + + return out.size(); } +/// +/// Search pattern in remote process +/// +/// Remote process +/// Pattern wildcard +/// Starting address +/// Size of region to scan +/// Found results +/// Number of found addresses +size_t PatternSearch::SearchRemote( + Process& remote, + uint8_t wildcard, + ptr_t scanStart, + size_t scanSize, + std::vector& out, + size_t maxMatches /*= SIZE_MAX*/ + ) const +{ + 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); -} \ No newline at end of file + return out.size(); +} + +/// +/// Search pattern in remote process +/// +/// Remote process +/// Starting address +/// Size of region to scan +/// Found results +/// Number of found addresses +size_t PatternSearch::SearchRemote( + Process& remote, + ptr_t scanStart, + size_t scanSize, + std::vector& out, + size_t maxMatches /*= SIZE_MAX*/ + ) const +{ + 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); + + return out.size(); +} + +/// +/// Search pattern in whole address space of remote process +/// +/// Remote process +/// True if pattern contains wildcards +/// Pattern wildcard +/// Found results +/// Number of found addresses +size_t PatternSearch::SearchRemoteWhole( + Process& remote, + bool useWildcard, + uint8_t wildcard, + std::vector& out, + size_t maxMatches /*= SIZE_MAX*/ + ) const +{ + out.clear(); + + 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); + + return out.size(); +} + + +} diff --git a/src/BlackBone/Patterns/PatternSearch.h b/src/BlackBone/Patterns/PatternSearch.h index 77ed59dc..429d8666 100644 --- a/src/BlackBone/Patterns/PatternSearch.h +++ b/src/BlackBone/Patterns/PatternSearch.h @@ -4,6 +4,7 @@ #include #include +#include #include namespace blackbone @@ -12,14 +13,111 @@ namespace blackbone class PatternSearch { public: - BLACKBONE_API PatternSearch( const std::vector& pattern ); - BLACKBONE_API PatternSearch( const std::initializer_list&& pattern ); - BLACKBONE_API PatternSearch( const std::string& pattern ); - BLACKBONE_API PatternSearch( const char* pattern, size_t len = 0 ); - BLACKBONE_API PatternSearch( const uint8_t* pattern, size_t len = 0 ); + /// + /// 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. + 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 ); + BLACKBONE_API PatternSearch( const char* pattern, size_t len = 0, size_t logAlignment = 0 ); + BLACKBONE_API PatternSearch( const uint8_t* pattern, size_t len = 0, size_t logAlignment = 0 ); BLACKBONE_API ~PatternSearch() = default; + /// + /// Default pattern matching with wildcards and a callback handler for matches. + /// std::search is approximately 2x faster than naive approach. + /// + /// Pattern wildcard + /// Starting address + /// Size of region to scan + /// Callback that is called for every match. If it returns true, the search is stopped prematurely. + /// Value that will be added to resulting addresses + /// true if the callback handler ever returned true (i.e. the search ended prematurely), false otherwise. + BLACKBONE_API bool SearchWithHandler( + uint8_t wildcard, + void* scanStart, + size_t scanSize, + MatchHandler handler, + ptr_t value_offset = 0 + ) const; + + /// + /// Full pattern match, no wildcards, with a callback handler for matches. + /// Uses Boyer�Moore�Horspool algorithm. + /// + /// Starting address + /// Size of region to scan + /// Callback that is called for every match. If it returns true, the search is stopped prematurely. + /// Value that will be added to resulting addresses + /// true if the callback handler ever returned true (i.e. the search ended prematurely), false otherwise. + BLACKBONE_API bool SearchWithHandler( + void* scanStart, + size_t scanSize, + MatchHandler handler, + ptr_t value_offset = 0 + ) const; + + /// + /// Search pattern in remote process with a callback handler for matches + /// + /// Remote process + /// Pattern wildcard + /// Starting address + /// Size of region to scan + /// Callback that is called for every match. If it returns true, the search is stopped prematurely. + /// true if the callback handler ever returned true (i.e. the search ended prematurely), false otherwise. + BLACKBONE_API bool SearchRemoteWithHandler( + class Process& remote, + uint8_t wildcard, + ptr_t scanStart, + size_t scanSize, + MatchHandler handler + ) const; + + /// + /// Search pattern in remote process with a callback handler for matches + /// + /// Remote process + /// Starting address + /// Size of region to scan + /// Callback that is called for every match. If it returns true, the search is stopped prematurely. + /// true if the callback handler ever returned true (i.e. the search ended prematurely), false otherwise. + BLACKBONE_API bool SearchRemoteWithHandler( + class Process& remote, + ptr_t scanStart, + size_t scanSize, + MatchHandler handler + ) const; + + /// + /// Search pattern in whole address space of remote process with a callback handler for matches + /// + /// Remote process + /// True if pattern contains wildcards + /// Pattern wildcard + /// Callback that is called for every match. If it returns true, the search is stopped prematurely. + /// true if the callback handler ever returned true (i.e. the search ended prematurely), false otherwise. + BLACKBONE_API bool SearchRemoteWholeWithHandler( + class Process& remote, + bool useWildcard, + uint8_t wildcard, + MatchHandler handler + ) const; + + + + + + /// /// Default pattern matching with wildcards. /// std::search is approximately 2x faster than naive approach. @@ -29,13 +127,15 @@ class PatternSearch /// Size of region to scan /// Found results /// Value that will be added to resulting addresses + /// Maximum number of matches to collect /// Number of found addresses BLACKBONE_API size_t Search( uint8_t wildcard, void* scanStart, size_t scanSize, std::vector& out, - ptr_t value_offset = 0 + ptr_t value_offset = 0, + size_t maxMatches = SIZE_MAX ) const; /// @@ -46,12 +146,14 @@ class PatternSearch /// Size of region to scan /// Found results /// Value that will be added to resulting addresses + /// Maximum number of matches to collect /// Number of found addresses BLACKBONE_API size_t Search( void* scanStart, size_t scanSize, std::vector& out, - ptr_t value_offset = 0 + ptr_t value_offset = 0, + size_t maxMatches = SIZE_MAX ) const; /// @@ -62,13 +164,15 @@ class PatternSearch /// Starting address /// Size of region to scan /// Found results + /// Maximum number of matches to collect /// Number of found addresses BLACKBONE_API size_t SearchRemote( class Process& remote, uint8_t wildcard, ptr_t scanStart, size_t scanSize, - std::vector& out + std::vector& out, + size_t maxMatches = SIZE_MAX ) const; /// @@ -78,12 +182,14 @@ class PatternSearch /// Starting address /// Size of region to scan /// Found results + /// Maximum number of matches to collect /// Number of found addresses BLACKBONE_API size_t SearchRemote( class Process& remote, ptr_t scanStart, size_t scanSize, - std::vector& out + std::vector& out, + size_t maxMatches = SIZE_MAX ) const; /// @@ -93,16 +199,26 @@ class PatternSearch /// True if pattern contains wildcards /// Pattern wildcard /// Found results + /// Maximum number of matches to collect /// Number of found addresses BLACKBONE_API size_t SearchRemoteWhole( class Process& remote, bool useWildcard, uint8_t wildcard, - std::vector& out + std::vector& out, + 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; + } + private: std::vector _pattern; // Pattern to search + size_t logAlignment; }; -} \ No newline at end of file +} From e4f8d87d5d628818a24a79de6314dec5acbacd08 Mon Sep 17 00:00:00 2001 From: Amrani Date: Wed, 23 Sep 2020 14:00:05 -0700 Subject: [PATCH 23/38] Python win32 api Wrapper --- src/PythonicBlackBone/BlackBone.py | 108 ++++++++++++++++++ .../__pycache__/BlackBone.cpython-38.pyc | Bin 0 -> 3086 bytes .../test/ReadWriteProcMem.py | 32 ++++++ 3 files changed, 140 insertions(+) create mode 100644 src/PythonicBlackBone/BlackBone.py create mode 100644 src/PythonicBlackBone/__pycache__/BlackBone.cpython-38.pyc create mode 100644 src/PythonicBlackBone/test/ReadWriteProcMem.py diff --git a/src/PythonicBlackBone/BlackBone.py b/src/PythonicBlackBone/BlackBone.py new file mode 100644 index 00000000..c5a5f8ad --- /dev/null +++ b/src/PythonicBlackBone/BlackBone.py @@ -0,0 +1,108 @@ +import ctypes as c +from ctypes import wintypes as w +import enum + +# Github : x544D + +class PythonicBlackBone(): + + class DataTypes(enum.Enum): + BOOL = 0 + INT16 = 1 + INT32 = 2 + INT64 = 3 + FLOAT = 4 + DOUBLE = 5 + LONG = 6 + ULONG = 7 + LLONG = 8 + ULLONG = 9 + SIZE_T = 10 + CHAR = 11 + BYTE = 12 + WCHAR = 13 + VOIDP = 14 + + + def ParseType(self, i , value=None): + _ = { + 0:c.c_bool, + 1:c.c_int16, + 2:c.c_int32, + 3:c.c_int64, + 4:c.c_float, + 5:c.c_double, + 6:c.c_long, + 7:c.c_ulong, + 8:c.c_longlong, + 9:c.c_ulonglong, + 10:c.c_size_t, + 11:c.c_char, + 12:c.c_byte, + 13:c.c_wchar, + 14:c.c_void_p, + } + F=_[i] + if value:return F(value=value) + else: return F() + + + + def __init__(self, ProcessId , DesiredAccess=0x000F0000|0x00100000|0xFFF): + if ProcessId is None: + print("+ Please Give a valid PID .") + exit(0) + + print("\t[ AN EASY WIN32API PYTHON WRAPPER CTYPE BASED ]\n\t- This is still under Dev .. !") + self.pid = ProcessId + self.access = DesiredAccess + + self.k32 = c.windll.kernel32 + self.OpenProcess = self.k32.OpenProcess + self.OpenProcess.argtypes = [w.DWORD,w.BOOL,w.DWORD] + self.OpenProcess.restype = w.HANDLE + + self.ReadProcessMemory = self.k32.ReadProcessMemory + self.ReadProcessMemory.argtypes = [w.HANDLE,w.LPCVOID,w.LPVOID,c.c_size_t,c.POINTER(c.c_size_t)] + self.ReadProcessMemory.restype = w.BOOL + + self.WriteProcessMemory = self.k32.WriteProcessMemory + self.WriteProcessMemory.argtypes = [w.HANDLE,w.LPCVOID,w.LPVOID,c.c_size_t,c.POINTER(c.c_size_t)] + self.WriteProcessMemory.restype = w.BOOL + + self.GetLastError = self.k32.GetLastError + self.GetLastError.argtypes = None + self.GetLastError.restype = w.DWORD + + self.CloseHandle = self.k32.CloseHandle + self.CloseHandle.argtypes = [w.HANDLE] + self.CloseHandle.restype = w.BOOL + + self.hProc = self.OpenProcess(self.access , False, self.pid) + if not self.hProc: + print('+ Failed To open a handle to the Target Process .') + exit(0) + + def CheckLastError(self): + return self.GetLastError() + + def DestroyHandle(self): + self.CloseHandle(self.hProc) + del self + + def RPM(self, address, data): + ''' ReadProcessMemory''' + return self.ReadProcessMemory(self.hProc, address, c.byref(data) , c.sizeof(data), None) + + def WPM(self, address, data): + ''' WriteProcessMemory ''' + return self.WriteProcessMemory(self.hProc, address, c.byref(data) , c.sizeof(data), None) + + def __del__(self): + print(f"+ Instance {type(self).__name__} Destroyed .") + + + +if __name__ == "__main__": + print('+ Please Intanciate the Class first .') + exit(0) \ No newline at end of file diff --git a/src/PythonicBlackBone/__pycache__/BlackBone.cpython-38.pyc b/src/PythonicBlackBone/__pycache__/BlackBone.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9dd04d6e8b1737734e52cb47fea4365bc82ab8ea GIT binary patch literal 3086 zcmbtWOK;pp5@z!?oR>zDM}EY+8^(D~oJ`hv7zq#rv0kEOIB5i6sOgqOi5hB~ zlpPHwxyZ)^*n6^ob@Y9I4X=CBaj&@~Uo}Uv>@l1}NYq!w?ym0Ys_JU4*XtDn&rko| z4Xm1B{Et4)KNdc|1f`1rVF(r*oPnFMv1f9N6>T=RZyUlA_9H{sM<#cqE1X?Z&B~H+ zj~FwgCk*0(-7{LH6U=3-v{>#vjQZK(zD#@6a{jUL@g*qb0IApHCe~oW#2TiwcI+dI zJK}<{h4YAUS2RUQc#uk>EGmx-?un|XftN*HOn_HJLrj8K#gv!^uL%pb%;Zzu!)!b0 zN4?oN?Cs7beK}?fJ2mFcIb#IlmN2<3$P60>I9S@nvL!6(VYxDvs$iKauZtR=5Ov-V z6MRxM_>`E$(o=)0V@tQ^!z^4Qk0@I7M1x5Z1>%Ifa^euU1SJBGpiDrKJgE{OlEz7$ zU;?nxn$Eo-=!bhU2=Yo0>?PtLCb<>_|2hccQI9>_=`3?+X=UxkP0c>~P_vsKb7yh6 zb89Vk=R51O%M09I?yTJA&U(R?HFMWT{N55@2-dhgcjwkBw`aduTj0)p{o#DoS(@)Y zG1MhkrQx?myZ!B%1Q1PY zrPa_eQxqgcCMXJ$q97>>lA;YM3X-BADGHLJASnuxY7mgsY6^hE;XDZA1KFC)S&zHD zU?WLlUg`xnF$hl~AV7tH5JA|ClQ836FA&MWMl4AyPWoHab)bbx(V*q(sF6N$=%mp= z1{vw}wnL>?Jj^8NKGY2u@*s&su-|fWJC*Tf&LZ^+OwC!4v&E+|RhOx;+znMKDV49{ zGkphOm@kW?{eO1~kv1yqMd#vdwv*o*uNu!pn<7t6*3oX`#0*iOc0F}DL%E=$lc8u3 zAF`nlm_rk2W%iD<<3cJ8kpnv(Brhu?1Iow%@GT4NUQmNv*V2R@*TA?2wSgudP3}Z0!Q79ZWx(CT9=1Gk?FcITfhhsoOz~TUgDf@2e=2 z_J+cH}Y(`-ShMCH}FI7#Ilz0cg)rlBoM{hekhkMx!Vug}XgQc~P1_I6g?ij#oS z8}*|s2tI;Z`XRtz9=)c^T(iO|tjQ|i*Uq7*lm5ROa>eH zkR9U;{-~>^dLPX#Q@snOt`oEgo^u?zHB-r9;r_2NBBk1Duo@e@I&RW4qsJ-)`9fRd zLpZf)$TIV>H8hWjqpBY>!HykNb8|55kEPQ}%TaGbyReutICnMJQ#=g)7;TJ7Hb7RrTp&B4t0Rxsi(Z=mUqXSYvVpi=yF3+e`aQdTO;&oN`~ zckh0O_Lmro5o7VEIB9Rw1wnNp%TB5s8Spd3`Qo7DPb9%aRxpK|3JeywKOaEhg zVbl+T!JB8bcd1Wah{6md74>v34pBxoBb8?GMHx4;E_}LlDmt0!ErQPoXvJbT Date: Sun, 20 Dec 2020 22:07:34 +0200 Subject: [PATCH 24/38] fixed build errors with latest SDK --- .../rewolf-wow64ext/src/wow64ext.cpp | 6 +++--- src/3rd_party/rewolf-wow64ext/src/wow64ext.h | 4 ++-- src/BlackBone/Include/HandleGuard.h | 1 + src/BlackBone/Misc/DynImport.h | 6 +++--- src/BlackBone/Process/MultPtr.hpp | 20 +++++++++---------- src/BlackBone/Process/ProcessModules.cpp | 1 + src/BlackBone/Process/RPC/RemoteExec.cpp | 2 +- src/BlackBone/Process/RPC/RemoteFunction.hpp | 16 +++++++-------- src/BlackBone/Process/RPC/RemoteLocalHook.cpp | 8 ++++---- src/BlackBoneTest/Common.h | 2 +- src/BlackBoneTest/TestRemoteCall.cpp | 4 ++-- 11 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/3rd_party/rewolf-wow64ext/src/wow64ext.cpp b/src/3rd_party/rewolf-wow64ext/src/wow64ext.cpp index 29213232..29483432 100644 --- a/src/3rd_party/rewolf-wow64ext/src/wow64ext.cpp +++ b/src/3rd_party/rewolf-wow64ext/src/wow64ext.cpp @@ -227,7 +227,7 @@ _move_0: ;// #endif } -bool cmpMem64(void* dstMem, DWORD64 srcMem, size_t sz) +bool cmpMem64(const void* dstMem, DWORD64 srcMem, size_t sz) { if ((nullptr == dstMem) || (0 == srcMem) || (0 == sz)) return false; @@ -299,7 +299,7 @@ DWORD64 getTEB64() return reg.v; } -extern "C" DWORD64 __cdecl GetModuleHandle64(wchar_t* lpModuleName) +extern "C" DWORD64 __cdecl GetModuleHandle64(const wchar_t* lpModuleName) { if (!g_isWow64) return 0; @@ -413,7 +413,7 @@ extern "C" VOID __cdecl SetLastErrorFromX64Call(DWORD64 status) } } -extern "C" DWORD64 __cdecl GetProcAddress64(DWORD64 hModule, char* funcName) +extern "C" DWORD64 __cdecl GetProcAddress64(DWORD64 hModule, const char* funcName) { static DWORD64 _LdrGetProcedureAddress = 0; if (0 == _LdrGetProcedureAddress) diff --git a/src/3rd_party/rewolf-wow64ext/src/wow64ext.h b/src/3rd_party/rewolf-wow64ext/src/wow64ext.h index 278d6c4b..c0fd0b33 100644 --- a/src/3rd_party/rewolf-wow64ext/src/wow64ext.h +++ b/src/3rd_party/rewolf-wow64ext/src/wow64ext.h @@ -362,9 +362,9 @@ struct _CONTEXT64_2 extern "C" { DWORD64 __cdecl X64Call(DWORD64 func, int argC, ...); - DWORD64 __cdecl GetModuleHandle64(wchar_t* lpModuleName); + DWORD64 __cdecl GetModuleHandle64(const wchar_t* lpModuleName); DWORD64 __cdecl getNTDLL64(); - DWORD64 __cdecl GetProcAddress64(DWORD64 hModule, char* funcName); + DWORD64 __cdecl GetProcAddress64(DWORD64 hModule, const char* funcName); SIZE_T __cdecl VirtualQueryEx64(HANDLE hProcess, DWORD64 lpAddress, MEMORY_BASIC_INFORMATION64* lpBuffer, SIZE_T dwLength); DWORD64 __cdecl VirtualAllocEx64(HANDLE hProcess, DWORD64 lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); BOOL __cdecl VirtualFreeEx64(HANDLE hProcess, DWORD64 lpAddress, SIZE_T dwSize, DWORD dwFreeType); diff --git a/src/BlackBone/Include/HandleGuard.h b/src/BlackBone/Include/HandleGuard.h index d611f177..95b85ba7 100644 --- a/src/BlackBone/Include/HandleGuard.h +++ b/src/BlackBone/Include/HandleGuard.h @@ -1,5 +1,6 @@ #pragma once #include "Winheaders.h" +#include namespace blackbone { diff --git a/src/BlackBone/Misc/DynImport.h b/src/BlackBone/Misc/DynImport.h index 4b16d739..04937ec8 100644 --- a/src/BlackBone/Misc/DynImport.h +++ b/src/BlackBone/Misc/DynImport.h @@ -78,9 +78,9 @@ class DynImport /// Function name /// Module name /// true on success - BLACKBONE_API FARPROC load( const std::string& name, const std::wstring& module ) + BLACKBONE_API FARPROC load( const std::string& name, const std::wstring& modName ) { - auto mod = GetModuleHandleW( module.c_str() ); + auto mod = GetModuleHandleW( modName.c_str() ); return load( name, mod ); } @@ -110,7 +110,7 @@ class DynImport }; // Syntax sugar -#define LOAD_IMPORT(name, module) (DynImport::Instance().load( name, module )) +#define LOAD_IMPORT(name, mod) (DynImport::Instance().load( name, mod )) #define GET_IMPORT(name) (DynImport::Instance().get( #name )) #define SAFE_NATIVE_CALL(name, ...) (DynImport::Instance().safeNativeCall( #name, __VA_ARGS__ )) #define SAFE_CALL(name, ...) (DynImport::Instance().safeCall( #name, __VA_ARGS__ )) diff --git a/src/BlackBone/Process/MultPtr.hpp b/src/BlackBone/Process/MultPtr.hpp index 3d3191bd..58f3aef5 100644 --- a/src/BlackBone/Process/MultPtr.hpp +++ b/src/BlackBone/Process/MultPtr.hpp @@ -96,9 +96,9 @@ class multi_ptr_ex : public multi_ptr /// Target process /// Base address /// Offsets - multi_ptr_ex( Process* proc, uintptr_t base = 0, const vecOffsets& offsets = vecOffsets() ) + multi_ptr_ex( Process* proc, uintptr_t base = 0, const multi_ptr::vecOffsets& offsets = multi_ptr::vecOffsets() ) : _proc( proc ) - , multi_ptr( base, offsets ) { } + , multi_ptr( base, offsets ) { } /// /// Commit changed object into process @@ -118,7 +118,7 @@ class multi_ptr_ex : public multi_ptr /// Read object from pointer /// /// Pointer to local copy or nullptr if invalid - virtual type_ptr read() + virtual multi_ptr::type_ptr read() { auto ptr = get_ptr(); if (ptr == 0) @@ -133,18 +133,18 @@ class multi_ptr_ex : public multi_ptr /// Pointer value or 0 if chain is invalid uintptr_t get_ptr() { - uintptr_t ptr = _base; + uintptr_t ptr = multi_ptr::_base; if (!NT_SUCCESS( _proc->memory().Read( ptr, ptr ) )) return 0; - if (!_offsets.empty()) + if (!multi_ptr::_offsets.empty()) { - for (intptr_t i = 0; i < static_cast(_offsets.size()) - 1; i++) - if (!NT_SUCCESS( _proc->memory().Read( ptr + _offsets[i], ptr ) )) + 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; - ptr += _offsets.back(); - if (type_is_ptr) + ptr += multi_ptr::_offsets.back(); + if (multi_ptr::type_is_ptr) if (!NT_SUCCESS( _proc->memory().Read( ptr, ptr ) )) return 0; } @@ -155,6 +155,6 @@ class multi_ptr_ex : public multi_ptr private: Process* _proc = nullptr; // Target process - type _data; // Local object copy + multi_ptr::type _data; // Local object copy }; } \ No newline at end of file diff --git a/src/BlackBone/Process/ProcessModules.cpp b/src/BlackBone/Process/ProcessModules.cpp index 744e49cd..f4683e99 100644 --- a/src/BlackBone/Process/ProcessModules.cpp +++ b/src/BlackBone/Process/ProcessModules.cpp @@ -9,6 +9,7 @@ #include #include +#include #ifdef COMPILER_MSVC #include diff --git a/src/BlackBone/Process/RPC/RemoteExec.cpp b/src/BlackBone/Process/RPC/RemoteExec.cpp index 1a36b44e..76f1a559 100644 --- a/src/BlackBone/Process/RPC/RemoteExec.cpp +++ b/src/BlackBone/Process/RPC/RemoteExec.cpp @@ -459,7 +459,7 @@ NTSTATUS RemoteExec::CreateAPCEvent( DWORD threadID ) // Event name swprintf_s( pEventName, ARRAYSIZE( pEventName ), L"\\BaseNamedObjects\\_MMapEvent_0x%x_0x%x", threadID, GetTickCount() ); - wchar_t* szStringSecurityDis = L"S:(ML;;NW;;;LW)D:(A;;GA;;;S-1-15-2-1)(A;;GA;;;WD)"; + const wchar_t* szStringSecurityDis = L"S:(ML;;NW;;;LW)D:(A;;GA;;;S-1-15-2-1)(A;;GA;;;WD)"; PSECURITY_DESCRIPTOR pDescriptor = nullptr; ConvertStringSecurityDescriptorToSecurityDescriptorW( szStringSecurityDis, SDDL_REVISION_1, &pDescriptor, NULL ); auto guard = std::unique_ptr( pDescriptor, &LocalFree ); diff --git a/src/BlackBone/Process/RPC/RemoteFunction.hpp b/src/BlackBone/Process/RPC/RemoteFunction.hpp index 6ed8ca7f..deff4585 100644 --- a/src/BlackBone/Process/RPC/RemoteFunction.hpp +++ b/src/BlackBone/Process/RPC/RemoteFunction.hpp @@ -176,10 +176,10 @@ template \ class RemoteFunction : public RemoteFunctionBase { public: - using RemoteFunctionBase::RemoteFunctionBase; + using RemoteFunctionBase::RemoteFunctionBase; RemoteFunction( Process& proc, R( __cdecl* ptr )(Args...) ) - : RemoteFunctionBase( proc, reinterpret_cast(ptr) ) + : RemoteFunctionBase( proc, reinterpret_cast(ptr) ) { } }; @@ -190,10 +190,10 @@ template class RemoteFunction : public RemoteFunctionBase { public: - using RemoteFunctionBase::RemoteFunctionBase; + using RemoteFunctionBase::RemoteFunctionBase; RemoteFunction( Process& proc, R( __stdcall* ptr )(Args...) ) - : RemoteFunctionBase( proc, reinterpret_cast(ptr) ) + : RemoteFunctionBase( proc, reinterpret_cast(ptr) ) { } }; @@ -202,10 +202,10 @@ template class RemoteFunction : public RemoteFunctionBase { public: - using RemoteFunctionBase::RemoteFunctionBase; + using RemoteFunctionBase::RemoteFunctionBase; RemoteFunction( Process& proc, R( __thiscall* ptr )(Args...) ) - : RemoteFunctionBase( proc, reinterpret_cast(ptr) ) + : RemoteFunctionBase( proc, reinterpret_cast(ptr) ) { } }; @@ -214,10 +214,10 @@ template class RemoteFunction : public RemoteFunctionBase { public: - using RemoteFunctionBase::RemoteFunctionBase; + using RemoteFunctionBase::RemoteFunctionBase; RemoteFunction( Process& proc, R( __fastcall* ptr )(Args...) ) - : RemoteFunctionBase( proc, reinterpret_cast(ptr) ) + : RemoteFunctionBase( proc, reinterpret_cast(ptr) ) { } }; diff --git a/src/BlackBone/Process/RPC/RemoteLocalHook.cpp b/src/BlackBone/Process/RPC/RemoteLocalHook.cpp index e5eb6687..fb6c8594 100644 --- a/src/BlackBone/Process/RPC/RemoteLocalHook.cpp +++ b/src/BlackBone/Process/RPC/RemoteLocalHook.cpp @@ -181,7 +181,7 @@ NTSTATUS RemoteLocalHook::CopyOldCode( bool x64 ) uint32_t thunkSize = 0; ldasm_data ld = { 0 }; - const int64_t diffMinVals[] = {0ll, -128ll, -32768ll, -8388608ll, -2147483648ll, -549755813888ll, -140737488355328ll, -36028797018963968ll, -9223372036854775808ll}; + const int64_t diffMinVals[] = {0ll, -128ll, -32768ll, -8388608ll, -2147483648ll, -549755813888ll, -140737488355328ll, -36028797018963968ll, -9223372036854775807ll}; const int64_t diffMaxVals[] = {0ll, 127ll, 32767ll, 8388607ll, 2147483647ll, 549755813887ll, 140737488355327ll, 36028797018963967ll, 9223372036854775807ll}; do @@ -232,7 +232,7 @@ NTSTATUS RemoteLocalHook::CopyOldCode( bool x64 ) } else { - _ctx.origCodeSize = thunkSize; + _ctx.origCodeSize = static_cast(thunkSize); } return status; @@ -240,7 +240,7 @@ NTSTATUS RemoteLocalHook::CopyOldCode( bool x64 ) uint8_t RemoteLocalHook::GenerateJump( uint8_t* code, ptr_t toAddr, ptr_t fromAddr, bool x64 ) const { - uint8_t size; + size_t size = 0; auto asmp = AsmFactory::GetAssembler(); auto& a = *asmp; @@ -299,7 +299,7 @@ uint8_t RemoteLocalHook::GenerateJump( uint8_t* code, ptr_t toAddr, ptr_t fromAd } assert(size <= sizeof(_ctx.hookJumpCode)); - return size; + return static_cast(size); } } diff --git a/src/BlackBoneTest/Common.h b/src/BlackBoneTest/Common.h index adbc5cfd..42a27815 100644 --- a/src/BlackBoneTest/Common.h +++ b/src/BlackBoneTest/Common.h @@ -18,7 +18,7 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace blackbone; -constexpr wchar_t* ModuleName = L"BlackBoneTest.dll"; +constexpr wchar_t ModuleName[] = L"BlackBoneTest.dll"; /// /// Some extensions for Assert diff --git a/src/BlackBoneTest/TestRemoteCall.cpp b/src/BlackBoneTest/TestRemoteCall.cpp index d715b0ba..f5ab1f71 100644 --- a/src/BlackBoneTest/TestRemoteCall.cpp +++ b/src/BlackBoneTest/TestRemoteCall.cpp @@ -2,8 +2,8 @@ namespace Testing { - constexpr char* g_string = "The quick brown fox jumps over the lazy dog"; - constexpr wchar_t* g_wstring = L"The quick brown fox jumps over the lazy dog"; + constexpr char g_string[] = "The quick brown fox jumps over the lazy dog"; + constexpr wchar_t g_wstring[] = L"The quick brown fox jumps over the lazy dog"; struct Dummy { From f53be8f54c771f623851518298ca13d747dbc479 Mon Sep 17 00:00:00 2001 From: DarthTon Date: Sun, 20 Dec 2020 22:11:08 +0200 Subject: [PATCH 25/38] update ci --- .github/workflows/driver.yaml | 2 +- .github/workflows/lib.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/driver.yaml b/.github/workflows/driver.yaml index a5f29b7c..6a239a1b 100644 --- a/.github/workflows/driver.yaml +++ b/.github/workflows/driver.yaml @@ -15,7 +15,7 @@ jobs: - name: Checkout uses: actions/checkout@v1 - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.0.0 + uses: microsoft/setup-msbuild@v1.0.2 - name: Build shell: cmd run: | diff --git a/.github/workflows/lib.yaml b/.github/workflows/lib.yaml index 6a3ffc25..ba8194b1 100644 --- a/.github/workflows/lib.yaml +++ b/.github/workflows/lib.yaml @@ -17,7 +17,7 @@ jobs: - name: Checkout uses: actions/checkout@v1 - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.0.0 + uses: microsoft/setup-msbuild@v1.0.2 - name: Add VSTest to PATH uses: darenm/Setup-VSTest@v1 - name: Build From 607e9a3be9ca01133de2b190f2efb17b3d51db40 Mon Sep 17 00:00:00 2001 From: DarthTon Date: Sun, 20 Dec 2020 22:16:32 +0200 Subject: [PATCH 26/38] trigger ci on workflow yaml changes --- .github/workflows/driver.yaml | 3 ++- .github/workflows/lib.yaml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/driver.yaml b/.github/workflows/driver.yaml index 6a239a1b..e67ad5a3 100644 --- a/.github/workflows/driver.yaml +++ b/.github/workflows/driver.yaml @@ -2,7 +2,8 @@ name: Driver on: push: paths: - - 'src/BlackBoneDrv/**' + - '.github/**' + - 'src/BlackBoneDrv/**' jobs: driver: diff --git a/.github/workflows/lib.yaml b/.github/workflows/lib.yaml index ba8194b1..b82d237d 100644 --- a/.github/workflows/lib.yaml +++ b/.github/workflows/lib.yaml @@ -2,6 +2,7 @@ name: Library on: push: paths: + - '.github/**' - 'src/**' - '!src/BlackBoneDrv/**' From ec8ab3dcc20d59dbdc7122c17ffb3e7eff20d9a3 Mon Sep 17 00:00:00 2001 From: Alemarius Nexus Date: Sat, 19 Jun 2021 20:02:25 +0200 Subject: [PATCH 27/38] Rename WordSize macro to BlackBoneWordSize --- src/BlackBone/Include/Macro.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BlackBone/Include/Macro.h b/src/BlackBone/Include/Macro.h index d8d70b53..5ab0cdf8 100644 --- a/src/BlackBone/Include/Macro.h +++ b/src/BlackBone/Include/Macro.h @@ -3,7 +3,7 @@ #include // Architecture-dependent pointer size -#define WordSize sizeof(void*) +#define BlackBoneWordSize sizeof(void*) // Rebase address #define MAKE_PTR(T, pRVA, base) (T)((ptr_t)pRVA + (ptr_t)base) @@ -112,7 +112,7 @@ inline size_t Align( size_t val, size_t alignment ) } // Offset of 'LastStatus' field in TEB -#define LAST_STATUS_OFS (0x598 + 0x197 * WordSize) +#define LAST_STATUS_OFS (0x598 + 0x197 * BlackBoneWordSize) using NTSTATUS = long; From 539ea3a972a8dc1d815c98e6d6b62d033f225ec4 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Tue, 19 Jan 2021 18:47:41 +0200 Subject: [PATCH 28/38] fix remote exec from x32 to x64 process --- src/BlackBone/Process/RPC/RemoteExec.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BlackBone/Process/RPC/RemoteExec.cpp b/src/BlackBone/Process/RPC/RemoteExec.cpp index 76f1a559..d679007c 100644 --- a/src/BlackBone/Process/RPC/RemoteExec.cpp +++ b/src/BlackBone/Process/RPC/RemoteExec.cpp @@ -54,7 +54,7 @@ NTSTATUS RemoteExec::ExecInNewThread( break; case blackbone::AutoSwitch: - switchMode = _process.barrier().type == wow_64_32; + switchMode = _process.barrier().type == wow_32_64; break; } From f257324a64ebe3b1756bf5c55cf6f95912c38b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=B1=E4=BA=A7=E4=B8=BB=E4=B9=89=E6=8E=A5=E7=8F=AD?= =?UTF-8?q?=E4=BA=BA?= <503407184@qq.com> Date: Fri, 7 May 2021 14:26:07 +0800 Subject: [PATCH 29/38] fix debug 1. fix "CreateAPCEvent" hRemoteHandle = sizeof( uintptr_t ) 2. fix ExecInAnyThread 32bit process "callResult" offset "INTRET_OFFSET", not is "RET_OFFSET" --- src/BlackBone/Process/RPC/RemoteExec.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/BlackBone/Process/RPC/RemoteExec.cpp b/src/BlackBone/Process/RPC/RemoteExec.cpp index d679007c..672fb7f3 100644 --- a/src/BlackBone/Process/RPC/RemoteExec.cpp +++ b/src/BlackBone/Process/RPC/RemoteExec.cpp @@ -278,7 +278,11 @@ NTSTATUS RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callRe if (NT_SUCCESS( status )) { WaitForSingleObject( _hWaitEvent, 20 * 1000/*INFINITE*/ ); - status = _userData.Read( RET_OFFSET, callResult ); + + if (!_process.core().isWow64()) + status = _userData.Read( RET_OFFSET, callResult ); + else + status = _userData.Read( INTRET_OFFSET, callResult ); } return status; @@ -485,7 +489,7 @@ NTSTATUS RemoteExec::CreateAPCEvent( DWORD threadID ) if (!DuplicateHandle( GetCurrentProcess(), _hWaitEvent, _process.core().handle(), &hRemoteHandle, 0, FALSE, DUPLICATE_SAME_ACCESS )) return LastNtStatus(); - return _userData.Write( EVENT_OFFSET, sizeof( int32_t ), &hRemoteHandle ); + return _userData.Write( EVENT_OFFSET, sizeof( uintptr_t ), &hRemoteHandle ); } /// @@ -682,4 +686,4 @@ void RemoteExec::reset() _apcPatched = false; } -} \ No newline at end of file +} From 13c26f82ff0fb2456a813bb42b10fcfb11c47033 Mon Sep 17 00:00:00 2001 From: Alemarius Nexus Date: Sat, 19 Jun 2021 19:34:28 +0200 Subject: [PATCH 30/38] Add buffer ping-ponging for RemoteExec, to avoid Sleep() after RPC calls --- src/BlackBone/Process/RPC/RemoteExec.cpp | 94 ++++++++++++++---------- src/BlackBone/Process/RPC/RemoteExec.h | 33 +++++++-- 2 files changed, 80 insertions(+), 47 deletions(-) diff --git a/src/BlackBone/Process/RPC/RemoteExec.cpp b/src/BlackBone/Process/RPC/RemoteExec.cpp index 672fb7f3..21b0f7a0 100644 --- a/src/BlackBone/Process/RPC/RemoteExec.cpp +++ b/src/BlackBone/Process/RPC/RemoteExec.cpp @@ -18,6 +18,7 @@ RemoteExec::RemoteExec( Process& proc ) , _threads( _process.threads() ) , _hWaitEvent( NULL ) , _apcPatched( false ) + , _bufPingPongIdx( 0 ) { } @@ -70,9 +71,9 @@ NTSTATUS RemoteExec::ExecInNewThread( auto createActStack = _mods.GetNtdllExport( "RtlAllocateActivationContextStack", mt_mod64 ); if (createActStack) { - a->GenCall( createActStack->procAddress, { _userData.ptr() + 0x3100 } ); + a->GenCall( createActStack->procAddress, { _userData[_bufPingPongIdx].ptr() + 0x3100 } ); - (*a)->mov( (*a)->zax, _userData.ptr() + 0x3100 ); + (*a)->mov( (*a)->zax, _userData[_bufPingPongIdx].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 ) ); @@ -80,22 +81,23 @@ NTSTATUS RemoteExec::ExecInNewThread( } } - a->GenCall( _userCode.ptr(), { } ); - (*a)->mov( (*a)->zdx, _userData.ptr() + INTRET_OFFSET ); + a->GenCall( _userCode[_bufPingPongIdx].ptr(), { } ); + (*a)->mov( (*a)->zdx, _userData[_bufPingPongIdx].ptr() + INTRET_OFFSET ); (*a)->mov( asmjit::host::dword_ptr( (*a)->zdx ), (*a)->zax ); a->GenEpilogue( switchMode, 4 ); // Execute code in newly created thread - if (!NT_SUCCESS( status = _userCode.Write( size, (*a)->getCodeSize(), (*a)->make() ) )) + if (!NT_SUCCESS( status = _userCode[_bufPingPongIdx].Write( size, (*a)->getCodeSize(), (*a)->make() ) )) return status; - auto thread = _threads.CreateNew( _userCode.ptr() + size, _userData.ptr()/*, HideFromDebug*/ ); + auto thread = _threads.CreateNew( _userCode[_bufPingPongIdx].ptr() + size, _userData[_bufPingPongIdx].ptr()/*, HideFromDebug*/ ); if (!thread) return thread.status; if (!(*thread)->Join()) return LastNtStatus(); - callResult = _userData.Read( INTRET_OFFSET, 0 ); + callResult = _userData[_bufPingPongIdx].Read( INTRET_OFFSET, 0 ); + switchPingPongBuffers(); return STATUS_SUCCESS; } @@ -148,20 +150,22 @@ NTSTATUS RemoteExec::ExecInWorkerThread( PVOID pCode, size_t size, uint64_t& cal _apcPatched = true; }*/ - auto pRemoteCode = _userCode.ptr(); + auto pRemoteCode = _userCode[_bufPingPongIdx].ptr(); // 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.Read( RET_OFFSET, 0 ); + callResult = _userData[_bufPingPongIdx].Read( RET_OFFSET, 0 ); } else return LastNtStatus(); // Ensure APC function fully returns - Sleep( 1 ); + // UPDATE: No longer necessary because of buffer ping-ponging, see switchPingPongBuffers() :) + //Sleep( 1 ); + switchPingPongBuffers(); return status; } @@ -223,7 +227,7 @@ NTSTATUS RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callRe for (int i = 0; i < count; i++) (*a)->mov( asmjit::Mem( asmjit::host::rsp, i * sizeof( uint64_t ) ), regs[i] ); - a->GenCall( _userCode.ptr(), { _userData.ptr() } ); + a->GenCall( _userCode[_bufPingPongIdx].ptr(), { _userData[_bufPingPongIdx].ptr() } ); AddReturnWithEvent( *a ); // Restore registers @@ -249,7 +253,7 @@ NTSTATUS RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callRe (*a)->pusha(); (*a)->pushf(); - a->GenCall( _userCode.ptr(), { _userData.ptr() } ); + a->GenCall( _userCode[_bufPingPongIdx].ptr(), { _userData[_bufPingPongIdx].ptr() } ); (*a)->add( asmjit::host::esp, sizeof( uint32_t ) ); AddReturnWithEvent( *a, mt_mod32, rt_int32, INTRET_OFFSET ); @@ -260,16 +264,16 @@ NTSTATUS RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callRe (*a)->ret(); } - if (NT_SUCCESS( status = _userCode.Write( size, (*a)->getCodeSize(), (*a)->make() ) )) + if (NT_SUCCESS( status = _userCode[_bufPingPongIdx].Write( size, (*a)->getCodeSize(), (*a)->make() ) )) { if (_process.core().isWow64()) { - ctx32.Eip = static_cast(_userCode.ptr() + size); + ctx32.Eip = static_cast(_userCode[_bufPingPongIdx].ptr() + size); status = thd->SetContext( ctx32, true ); } else { - ctx64.Rip = _userCode.ptr() + size; + ctx64.Rip = _userCode[_bufPingPongIdx].ptr() + size; status = thd->SetContext( ctx64, true ); } } @@ -278,13 +282,15 @@ NTSTATUS RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callRe if (NT_SUCCESS( status )) { WaitForSingleObject( _hWaitEvent, 20 * 1000/*INFINITE*/ ); - + if (!_process.core().isWow64()) - status = _userData.Read( RET_OFFSET, callResult ); + status = _userData[_bufPingPongIdx].Read( RET_OFFSET, callResult ); else - status = _userData.Read( INTRET_OFFSET, callResult ); + status = _userData[_bufPingPongIdx].Read( INTRET_OFFSET, callResult ); } + switchPingPongBuffers(); + return status; } @@ -342,9 +348,13 @@ NTSTATUS RemoteExec::CreateRPCEnvironment( WorkerThreadMode mode /*= Worker_None // if (!NT_SUCCESS( status = allocMem( _workerCode ) )) return status; - if (!NT_SUCCESS( status = allocMem( _userCode ) )) + if (!NT_SUCCESS( status = allocMem( _userCode[0] ) )) return status; - if (!NT_SUCCESS( status = allocMem( _userData, 0x4000, PAGE_READWRITE ) )) + 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; // Create RPC thread @@ -423,7 +433,7 @@ call_result_t RemoteExec::CreateWorkerThread() a->GenCall( proc->procAddress, { TRUE, _workerCode.ptr() } ); (*a)->jmp( l_loop ); - a->ExitThreadWithStatus( pExitThread->procAddress, _userData.ptr() ); + a->ExitThreadWithStatus( pExitThread->procAddress, _userData[0].ptr() ); // Write code into process LARGE_INTEGER liDelay = { { 0 } }; @@ -432,7 +442,7 @@ call_result_t RemoteExec::CreateWorkerThread() _workerCode.Write( 0, liDelay ); _workerCode.Write( sizeof(LARGE_INTEGER), (*a)->getCodeSize(), (*a)->make() ); - auto thd = _threads.CreateNew( _workerCode.ptr() + sizeof( LARGE_INTEGER ), _userData.ptr()/*, HideFromDebug*/ ); + auto thd = _threads.CreateNew( _workerCode.ptr() + sizeof( LARGE_INTEGER ), _userData[0].ptr()/*, HideFromDebug*/ ); if (!thd) return thd.status; @@ -489,7 +499,10 @@ NTSTATUS RemoteExec::CreateAPCEvent( DWORD threadID ) if (!DuplicateHandle( GetCurrentProcess(), _hWaitEvent, _process.core().handle(), &hRemoteHandle, 0, FALSE, DUPLICATE_SAME_ACCESS )) return LastNtStatus(); - return _userData.Write( EVENT_OFFSET, sizeof( uintptr_t ), &hRemoteHandle ); + 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 ); } /// @@ -529,8 +542,8 @@ NTSTATUS RemoteExec::PrepareCallAssembly( if (arg.type == AsmVariant::dataStruct || arg.type == AsmVariant::dataPtr) { - _userData.Write( data_offset, arg.size, reinterpret_cast(arg.imm_val) ); - arg.new_imm_val = _userData.ptr() + data_offset; + _userData[_bufPingPongIdx].Write( data_offset, arg.size, reinterpret_cast(arg.imm_val) ); + arg.new_imm_val = _userData[_bufPingPongIdx].ptr() + data_offset; // Add some padding after data data_offset += arg.size + 0x10; @@ -541,7 +554,7 @@ NTSTATUS RemoteExec::PrepareCallAssembly( // This variable contains address of buffer in which return value is copied if (retType == rt_struct) { - args.emplace( args.begin(), AsmVariant( _userData.ptr() + ARGS_OFFSET ) ); + args.emplace( args.begin(), AsmVariant( _userData[_bufPingPongIdx].ptr() + ARGS_OFFSET ) ); args.front().new_imm_val = args.front().imm_val; args.front().type = AsmVariant::structRet; } @@ -552,7 +565,7 @@ NTSTATUS RemoteExec::PrepareCallAssembly( // Retrieve result from XMM0 or ST0 if (retType == rt_float || retType == rt_double) { - a->mov( a->zax, _userData.ptr() + RET_OFFSET ); + a->mov( a->zax, _userData[_bufPingPongIdx].ptr() + RET_OFFSET ); #ifdef USE64 if (retType == rt_double) @@ -578,24 +591,24 @@ NTSTATUS RemoteExec::PrepareCallAssembly( /// Status NTSTATUS RemoteExec::CopyCode( PVOID pCode, size_t size ) { - if (!_userCode.valid()) + if (!_userCode[_bufPingPongIdx].valid()) { auto mem = _memory.Allocate( size ); if (!mem) return mem.status; - _userCode = std::move( mem.result() ); + _userCode[_bufPingPongIdx] = std::move( mem.result() ); } // Reallocate for larger code - if (size > _userCode.size()) + if (size > _userCode[_bufPingPongIdx].size()) { - auto res = _userCode.Realloc( size ); + auto res = _userCode[_bufPingPongIdx].Realloc( size ); if (!res) return res.status; } - return _userCode.Write( 0, size, pCode ); + return _userCode[_bufPingPongIdx].Write( 0, size, pCode ); } /// @@ -613,16 +626,16 @@ void RemoteExec::AddReturnWithEvent( ) { // Allocate block if missing - if (!_userData.valid()) + if (!_userData[_bufPingPongIdx].valid()) { auto mem = _memory.Allocate( 0x4000, PAGE_READWRITE ); if (!mem) return; - _userData = std::move( mem.result() ); + _userData[_bufPingPongIdx] = std::move( mem.result() ); } - ptr_t ptr = _userData.ptr(); + ptr_t ptr = _userData[_bufPingPongIdx].ptr(); auto pSetEvent = _process.modules().GetNtdllExport( "NtSetEvent", mt ); if(pSetEvent) a.SaveRetValAndSignalEvent( pSetEvent->procAddress, ptr + retOffset, ptr + EVENT_OFFSET, ptr + ERR_OFFSET, retType ); @@ -635,7 +648,7 @@ void RemoteExec::TerminateWorker() { // Close remote event handle ptr_t hRemoteEvent = 0; - _userData.Read( EVENT_OFFSET, hRemoteEvent ); + _userData[0].Read( EVENT_OFFSET, hRemoteEvent ); if (hRemoteEvent) { HANDLE hLocal = nullptr; @@ -651,7 +664,8 @@ void RemoteExec::TerminateWorker() if (hLocal) CloseHandle( hLocal ); - _userData.Write( EVENT_OFFSET, 0ll ); + _userData[0].Write( EVENT_OFFSET, 0ll ); + _userData[1].Write( EVENT_OFFSET, 0ll ); } // Close event @@ -679,8 +693,10 @@ void RemoteExec::reset() { TerminateWorker(); - _userCode.Reset(); - _userData.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 082e03f3..c2715873 100644 --- a/src/BlackBone/Process/RPC/RemoteExec.h +++ b/src/BlackBone/Process/RPC/RemoteExec.h @@ -124,7 +124,7 @@ class RemoteExec /// Target assembly helper BLACKBONE_API void SaveCallResult( IAsmHelper& a, uint32_t retOffset = RET_OFFSET ) { - a->mov( a->zdx, _userData.ptr() + retOffset ); + a->mov( a->zdx, _userData[_bufPingPongIdx].ptr() + retOffset ); a->mov( asmjit::host::dword_ptr( a->zdx ), a->zax ); } @@ -136,15 +136,17 @@ class RemoteExec template NTSTATUS GetCallResult( T& result ) { + // 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.Read( _userData.Read( RET_OFFSET, 0 ), sizeof( T ), reinterpret_cast(&result) ); + return _userData[1-_bufPingPongIdx].Read( _userData[1-_bufPingPongIdx].Read( RET_OFFSET, 0 ), sizeof( T ), reinterpret_cast(&result) ); else - return _userData.Read( ARGS_OFFSET, sizeof( T ), reinterpret_cast(&result) ); + return _userData[1-_bufPingPongIdx].Read( ARGS_OFFSET, sizeof( T ), reinterpret_cast(&result) ); } else - return _userData.Read( RET_OFFSET, sizeof( T ), reinterpret_cast(&result) ); + return _userData[1-_bufPingPongIdx].Read( RET_OFFSET, sizeof( T ), reinterpret_cast(&result) ); } /// @@ -153,7 +155,7 @@ class RemoteExec /// BLACKBONE_API NTSTATUS GetLastStatus() { - return _userData.Read( ERR_OFFSET, STATUS_NOT_FOUND ); + return _userData[_bufPingPongIdx].Read( ERR_OFFSET, STATUS_NOT_FOUND ); } /// @@ -207,6 +209,20 @@ class RemoteExec /// Status NTSTATUS CopyCode( PVOID pCode, size_t size ); + void switchPingPongBuffers() + { + // The ExecIn*() methods might return while the remote RPC code is still executing (it still has to + // return after signaling the event), making it possible to start another RPC call which would then + // overwrite the _userData/_userCode blocks that are still in use by the previous call. If subsequent + // RPC calls use the same buffers, this creates a race condition, very rarely resulting in crashes in + // the remote process, especially in KiUserApcDispatcher(). + // + // For this reason, we allocate two separate instances of _userCode and _userData, and switch between + // them for subsequent RPC calls (buffer ping-ponging). This should prevent the race condition without + // having to Sleep() for an arbitrary amount of time after each RPC call. + _bufPingPongIdx = 1-_bufPingPongIdx; + } + RemoteExec( const RemoteExec& ) = delete; RemoteExec& operator =(const RemoteExec&) = delete; @@ -221,10 +237,11 @@ class RemoteExec ThreadPtr _hijackThread; // Thread to use for hijacking HANDLE _hWaitEvent; // APC sync event handle MemBlock _workerCode; // Worker thread address space - MemBlock _userCode; // Codecave for code execution - MemBlock _userData; // Region to store copied structures and strings + MemBlock _userCode[2]; // Codecave for code execution + MemBlock _userData[2]; // Region to store copied structures and strings bool _apcPatched; // KiUserApcDispatcher was patched + int _bufPingPongIdx; // Index of the currently used _userCode/_userData block. See switchPingPongBuffers(). }; -} \ No newline at end of file +} From 0eac7c5c950593cb6873d36ce85710aa19a3d10d Mon Sep 17 00:00:00 2001 From: DarthTon Date: Sun, 20 Jun 2021 21:57:09 +0300 Subject: [PATCH 31/38] code style fixup; added test --- src/BlackBone/Process/RPC/RemoteExec.cpp | 73 ++++++++++++------------ src/BlackBone/Process/RPC/RemoteExec.h | 38 ++++++------ src/BlackBoneTest/TestRemoteCall.cpp | 23 ++++++++ 3 files changed, 77 insertions(+), 57 deletions(-) diff --git a/src/BlackBone/Process/RPC/RemoteExec.cpp b/src/BlackBone/Process/RPC/RemoteExec.cpp index 21b0f7a0..ce4288d5 100644 --- a/src/BlackBone/Process/RPC/RemoteExec.cpp +++ b/src/BlackBone/Process/RPC/RemoteExec.cpp @@ -18,7 +18,7 @@ RemoteExec::RemoteExec( Process& proc ) , _threads( _process.threads() ) , _hWaitEvent( NULL ) , _apcPatched( false ) - , _bufPingPongIdx( 0 ) + , _currentBufferIdx( 0 ) { } @@ -71,9 +71,9 @@ NTSTATUS RemoteExec::ExecInNewThread( auto createActStack = _mods.GetNtdllExport( "RtlAllocateActivationContextStack", mt_mod64 ); if (createActStack) { - a->GenCall( createActStack->procAddress, { _userData[_bufPingPongIdx].ptr() + 0x3100 } ); + a->GenCall( createActStack->procAddress, { _userData[_currentBufferIdx].ptr() + 0x3100 } ); - (*a)->mov( (*a)->zax, _userData[_bufPingPongIdx].ptr() + 0x3100 ); + (*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 ) ); @@ -81,23 +81,23 @@ NTSTATUS RemoteExec::ExecInNewThread( } } - a->GenCall( _userCode[_bufPingPongIdx].ptr(), { } ); - (*a)->mov( (*a)->zdx, _userData[_bufPingPongIdx].ptr() + INTRET_OFFSET ); + a->GenCall( _userCode[_currentBufferIdx].ptr(), { } ); + (*a)->mov( (*a)->zdx, _userData[_currentBufferIdx].ptr() + INTRET_OFFSET ); (*a)->mov( asmjit::host::dword_ptr( (*a)->zdx ), (*a)->zax ); a->GenEpilogue( switchMode, 4 ); - + // Execute code in newly created thread - if (!NT_SUCCESS( status = _userCode[_bufPingPongIdx].Write( size, (*a)->getCodeSize(), (*a)->make() ) )) + if (!NT_SUCCESS( status = _userCode[_currentBufferIdx].Write( size, (*a)->getCodeSize(), (*a)->make() ) )) return status; - auto thread = _threads.CreateNew( _userCode[_bufPingPongIdx].ptr() + size, _userData[_bufPingPongIdx].ptr()/*, HideFromDebug*/ ); + auto thread = _threads.CreateNew( _userCode[_currentBufferIdx].ptr() + size, _userData[_currentBufferIdx].ptr()/*, HideFromDebug*/ ); if (!thread) return thread.status; if (!(*thread)->Join()) return LastNtStatus(); - callResult = _userData[_bufPingPongIdx].Read( INTRET_OFFSET, 0 ); - switchPingPongBuffers(); + callResult = _userData[_currentBufferIdx].Read( INTRET_OFFSET, 0 ); + SwitchActiveBuffer(); return STATUS_SUCCESS; } @@ -150,22 +150,19 @@ NTSTATUS RemoteExec::ExecInWorkerThread( PVOID pCode, size_t size, uint64_t& cal _apcPatched = true; }*/ - auto pRemoteCode = _userCode[_bufPingPongIdx].ptr(); + auto pRemoteCode = _userCode[_currentBufferIdx].ptr(); // 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[_bufPingPongIdx].Read( RET_OFFSET, 0 ); + callResult = _userData[_currentBufferIdx].Read( RET_OFFSET, 0 ); } else return LastNtStatus(); - // Ensure APC function fully returns - // UPDATE: No longer necessary because of buffer ping-ponging, see switchPingPongBuffers() :) - //Sleep( 1 ); - switchPingPongBuffers(); + SwitchActiveBuffer(); return status; } @@ -227,7 +224,7 @@ NTSTATUS RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callRe for (int i = 0; i < count; i++) (*a)->mov( asmjit::Mem( asmjit::host::rsp, i * sizeof( uint64_t ) ), regs[i] ); - a->GenCall( _userCode[_bufPingPongIdx].ptr(), { _userData[_bufPingPongIdx].ptr() } ); + a->GenCall( _userCode[_currentBufferIdx].ptr(), { _userData[_currentBufferIdx].ptr() } ); AddReturnWithEvent( *a ); // Restore registers @@ -253,7 +250,7 @@ NTSTATUS RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callRe (*a)->pusha(); (*a)->pushf(); - a->GenCall( _userCode[_bufPingPongIdx].ptr(), { _userData[_bufPingPongIdx].ptr() } ); + a->GenCall( _userCode[_currentBufferIdx].ptr(), { _userData[_currentBufferIdx].ptr() } ); (*a)->add( asmjit::host::esp, sizeof( uint32_t ) ); AddReturnWithEvent( *a, mt_mod32, rt_int32, INTRET_OFFSET ); @@ -264,16 +261,16 @@ NTSTATUS RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callRe (*a)->ret(); } - if (NT_SUCCESS( status = _userCode[_bufPingPongIdx].Write( size, (*a)->getCodeSize(), (*a)->make() ) )) + if (NT_SUCCESS( status = _userCode[_currentBufferIdx].Write( size, (*a)->getCodeSize(), (*a)->make() ) )) { if (_process.core().isWow64()) { - ctx32.Eip = static_cast(_userCode[_bufPingPongIdx].ptr() + size); + ctx32.Eip = static_cast(_userCode[_currentBufferIdx].ptr() + size); status = thd->SetContext( ctx32, true ); } else { - ctx64.Rip = _userCode[_bufPingPongIdx].ptr() + size; + ctx64.Rip = _userCode[_currentBufferIdx].ptr() + size; status = thd->SetContext( ctx64, true ); } } @@ -284,12 +281,12 @@ NTSTATUS RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callRe WaitForSingleObject( _hWaitEvent, 20 * 1000/*INFINITE*/ ); if (!_process.core().isWow64()) - status = _userData[_bufPingPongIdx].Read( RET_OFFSET, callResult ); + status = _userData[_currentBufferIdx].Read( RET_OFFSET, callResult ); else - status = _userData[_bufPingPongIdx].Read( INTRET_OFFSET, callResult ); + status = _userData[_currentBufferIdx].Read( INTRET_OFFSET, callResult ); } - switchPingPongBuffers(); + SwitchActiveBuffer(); return status; } @@ -415,7 +412,7 @@ call_result_t 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 ); auto proc = _mods.GetExport( ntdll, "NtDelayExecution" ); @@ -501,7 +498,7 @@ NTSTATUS RemoteExec::CreateAPCEvent( DWORD threadID ) status = _userData[0].Write( EVENT_OFFSET, sizeof( uintptr_t ), &hRemoteHandle ); if (!NT_SUCCESS( status )) - return status; + return status; return _userData[1].Write( EVENT_OFFSET, sizeof( uintptr_t ), &hRemoteHandle ); } @@ -542,8 +539,8 @@ NTSTATUS RemoteExec::PrepareCallAssembly( if (arg.type == AsmVariant::dataStruct || arg.type == AsmVariant::dataPtr) { - _userData[_bufPingPongIdx].Write( data_offset, arg.size, reinterpret_cast(arg.imm_val) ); - arg.new_imm_val = _userData[_bufPingPongIdx].ptr() + data_offset; + _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 data_offset += arg.size + 0x10; @@ -554,7 +551,7 @@ NTSTATUS RemoteExec::PrepareCallAssembly( // This variable contains address of buffer in which return value is copied if (retType == rt_struct) { - args.emplace( args.begin(), AsmVariant( _userData[_bufPingPongIdx].ptr() + ARGS_OFFSET ) ); + args.emplace( args.begin(), AsmVariant( _userData[_currentBufferIdx].ptr() + ARGS_OFFSET ) ); args.front().new_imm_val = args.front().imm_val; args.front().type = AsmVariant::structRet; } @@ -565,7 +562,7 @@ NTSTATUS RemoteExec::PrepareCallAssembly( // Retrieve result from XMM0 or ST0 if (retType == rt_float || retType == rt_double) { - a->mov( a->zax, _userData[_bufPingPongIdx].ptr() + RET_OFFSET ); + a->mov( a->zax, _userData[_currentBufferIdx].ptr() + RET_OFFSET ); #ifdef USE64 if (retType == rt_double) @@ -591,24 +588,24 @@ NTSTATUS RemoteExec::PrepareCallAssembly( /// Status NTSTATUS RemoteExec::CopyCode( PVOID pCode, size_t size ) { - if (!_userCode[_bufPingPongIdx].valid()) + if (!_userCode[_currentBufferIdx].valid()) { auto mem = _memory.Allocate( size ); if (!mem) return mem.status; - _userCode[_bufPingPongIdx] = std::move( mem.result() ); + _userCode[_currentBufferIdx] = std::move( mem.result() ); } // Reallocate for larger code - if (size > _userCode[_bufPingPongIdx].size()) + if (size > _userCode[_currentBufferIdx].size()) { - auto res = _userCode[_bufPingPongIdx].Realloc( size ); + auto res = _userCode[_currentBufferIdx].Realloc( size ); if (!res) return res.status; } - return _userCode[_bufPingPongIdx].Write( 0, size, pCode ); + return _userCode[_currentBufferIdx].Write( 0, size, pCode ); } /// @@ -626,16 +623,16 @@ void RemoteExec::AddReturnWithEvent( ) { // Allocate block if missing - if (!_userData[_bufPingPongIdx].valid()) + if (!_userData[_currentBufferIdx].valid()) { auto mem = _memory.Allocate( 0x4000, PAGE_READWRITE ); if (!mem) return; - _userData[_bufPingPongIdx] = std::move( mem.result() ); + _userData[_currentBufferIdx] = std::move( mem.result() ); } - ptr_t ptr = _userData[_bufPingPongIdx].ptr(); + 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 ); diff --git a/src/BlackBone/Process/RPC/RemoteExec.h b/src/BlackBone/Process/RPC/RemoteExec.h index c2715873..1c24bee9 100644 --- a/src/BlackBone/Process/RPC/RemoteExec.h +++ b/src/BlackBone/Process/RPC/RemoteExec.h @@ -124,7 +124,7 @@ class RemoteExec /// Target assembly helper BLACKBONE_API void SaveCallResult( IAsmHelper& a, uint32_t retOffset = RET_OFFSET ) { - a->mov( a->zdx, _userData[_bufPingPongIdx].ptr() + retOffset ); + a->mov( a->zdx, _userData[_currentBufferIdx].ptr() + retOffset ); a->mov( asmjit::host::dword_ptr( a->zdx ), a->zax ); } @@ -136,17 +136,17 @@ class RemoteExec template NTSTATUS GetCallResult( T& result ) { - // 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. + // 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-_bufPingPongIdx].Read( _userData[1-_bufPingPongIdx].Read( RET_OFFSET, 0 ), sizeof( T ), reinterpret_cast(&result) ); + return _userData[1 - _currentBufferIdx].Read( _userData[1 - _currentBufferIdx].Read( RET_OFFSET, 0 ), sizeof( T ), reinterpret_cast(&result) ); else - return _userData[1-_bufPingPongIdx].Read( ARGS_OFFSET, sizeof( T ), reinterpret_cast(&result) ); + return _userData[1 - _currentBufferIdx].Read( ARGS_OFFSET, sizeof( T ), reinterpret_cast(&result) ); } else - return _userData[1-_bufPingPongIdx].Read( RET_OFFSET, sizeof( T ), reinterpret_cast(&result) ); + return _userData[1 - _currentBufferIdx].Read( RET_OFFSET, sizeof( T ), reinterpret_cast(&result) ); } /// @@ -155,7 +155,7 @@ class RemoteExec /// BLACKBONE_API NTSTATUS GetLastStatus() { - return _userData[_bufPingPongIdx].Read( ERR_OFFSET, STATUS_NOT_FOUND ); + return _userData[_currentBufferIdx].Read( ERR_OFFSET, STATUS_NOT_FOUND ); } /// @@ -209,18 +209,18 @@ class RemoteExec /// Status NTSTATUS CopyCode( PVOID pCode, size_t size ); - void switchPingPongBuffers() + void SwitchActiveBuffer() { - // The ExecIn*() methods might return while the remote RPC code is still executing (it still has to - // return after signaling the event), making it possible to start another RPC call which would then - // overwrite the _userData/_userCode blocks that are still in use by the previous call. If subsequent - // RPC calls use the same buffers, this creates a race condition, very rarely resulting in crashes in - // the remote process, especially in KiUserApcDispatcher(). - // - // For this reason, we allocate two separate instances of _userCode and _userData, and switch between - // them for subsequent RPC calls (buffer ping-ponging). This should prevent the race condition without - // having to Sleep() for an arbitrary amount of time after each RPC call. - _bufPingPongIdx = 1-_bufPingPongIdx; + // The ExecIn*() methods might return while the remote RPC code is still executing (it still has to + // return after signaling the event), making it possible to start another RPC call which would then + // overwrite the _userData/_userCode blocks that are still in use by the previous call. If subsequent + // RPC calls use the same buffers, this creates a race condition, very rarely resulting in crashes in + // the remote process, especially in KiUserApcDispatcher(). + // + // For this reason, we allocate two separate instances of _userCode and _userData, and switch between + // them for subsequent RPC calls (buffer ping-ponging). This should prevent the race condition without + // having to Sleep() for an arbitrary amount of time after each RPC call. + _currentBufferIdx = 1 - _currentBufferIdx; } RemoteExec( const RemoteExec& ) = delete; @@ -240,7 +240,7 @@ class RemoteExec MemBlock _userCode[2]; // Codecave for code execution MemBlock _userData[2]; // Region to store copied structures and strings bool _apcPatched; // KiUserApcDispatcher was patched - int _bufPingPongIdx; // Index of the currently used _userCode/_userData block. See switchPingPongBuffers(). + int _currentBufferIdx;// Index of the currently used _userCode/_userData block. See SwitchActiveBuffer(). }; diff --git a/src/BlackBoneTest/TestRemoteCall.cpp b/src/BlackBoneTest/TestRemoteCall.cpp index f5ab1f71..9ae956f0 100644 --- a/src/BlackBoneTest/TestRemoteCall.cpp +++ b/src/BlackBoneTest/TestRemoteCall.cpp @@ -119,6 +119,29 @@ namespace Testing AssertEx::IsZero( memcmp( &_input, &_output, sizeof( _input ) ) ); } + TEST_METHOD( CallLoop ) + { + Process process; + AssertEx::NtSuccess( process.Attach( GetCurrentProcessId() ) ); + AssertEx::NtSuccess( process.remote().CreateRPCEnvironment( Worker_CreateNew, true ) ); + + auto pFN = MakeRemoteFunction( process, &TestFn ); + auto worker = process.remote().getWorker(); + double d = 0.0; + + _input.ival = 0xDEAD; + _input.fval = 1337.0f; + _input.uval = 0xDEADC0DEA4DBEEFull; + + 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() ); + } + } + TEST_METHOD( NtQueryVirtualMemory ) { auto path = GetTestHelperHost(); From e36af76ce750b14442d3fa449ab472a078d71c75 Mon Sep 17 00:00:00 2001 From: Alemarius Nexus Date: Sat, 19 Jun 2021 16:00:51 +0200 Subject: [PATCH 32/38] Support binding RemoteFunctionBase to a thread This avoids having to supply a contextThread for every call to RemoteFunctionBase::Call(). --- src/BlackBone/Process/RPC/RemoteFunction.hpp | 41 ++++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/BlackBone/Process/RPC/RemoteFunction.hpp b/src/BlackBone/Process/RPC/RemoteFunction.hpp index deff4585..ffde610a 100644 --- a/src/BlackBone/Process/RPC/RemoteFunction.hpp +++ b/src/BlackBone/Process/RPC/RemoteFunction.hpp @@ -52,9 +52,10 @@ class RemoteFunctionBase }; public: - RemoteFunctionBase( Process& proc, ptr_t ptr ) + RemoteFunctionBase( Process& proc, ptr_t ptr, ThreadPtr boundThread = nullptr ) : _process( proc ) , _ptr( ptr ) + , _boundThread(boundThread) { static_assert( (... && !std::is_reference_v), @@ -69,6 +70,9 @@ class RemoteFunctionBase 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 )) @@ -155,7 +159,9 @@ class RemoteFunctionBase auto MakeArguments( const std::tuple& args ) { return CallArguments( args, std::index_sequence_for() ); - } + } + + void BindToThread(ThreadPtr thread) { _boundThread = thread; } bool valid() const { return _ptr != 0; } explicit operator bool() const { return valid(); } @@ -163,6 +169,7 @@ class RemoteFunctionBase private: Process& _process; ptr_t _ptr = 0; + ThreadPtr _boundThread = nullptr; }; // Remote function pointer @@ -178,8 +185,8 @@ class RemoteFunction : public RemoteFunctionBase::RemoteFunctionBase; - RemoteFunction( Process& proc, R( __cdecl* ptr )(Args...) ) - : RemoteFunctionBase( proc, reinterpret_cast(ptr) ) + RemoteFunction( Process& proc, R( __cdecl* ptr )(Args...), ThreadPtr boundThread = nullptr ) + : RemoteFunctionBase( proc, reinterpret_cast(ptr), boundThread ) { } }; @@ -192,8 +199,8 @@ class RemoteFunction : public RemoteFunctionBase::RemoteFunctionBase; - RemoteFunction( Process& proc, R( __stdcall* ptr )(Args...) ) - : RemoteFunctionBase( proc, reinterpret_cast(ptr) ) + RemoteFunction( Process& proc, R( __stdcall* ptr )(Args...), ThreadPtr boundThread = nullptr ) + : RemoteFunctionBase( proc, reinterpret_cast(ptr), boundThread ) { } }; @@ -204,8 +211,8 @@ class RemoteFunction : public RemoteFunctionBase::RemoteFunctionBase; - RemoteFunction( Process& proc, R( __thiscall* ptr )(Args...) ) - : RemoteFunctionBase( proc, reinterpret_cast(ptr) ) + RemoteFunction( Process& proc, R( __thiscall* ptr )(Args...), ThreadPtr boundThread = nullptr ) + : RemoteFunctionBase( proc, reinterpret_cast(ptr), boundThread ) { } }; @@ -216,8 +223,8 @@ class RemoteFunction : public RemoteFunctionBase::RemoteFunctionBase; - RemoteFunction( Process& proc, R( __fastcall* ptr )(Args...) ) - : RemoteFunctionBase( proc, reinterpret_cast(ptr) ) + RemoteFunction( Process& proc, R( __fastcall* ptr )(Args...), ThreadPtr boundThread = nullptr ) + : RemoteFunctionBase( proc, reinterpret_cast(ptr), boundThread ) { } }; @@ -229,9 +236,9 @@ class RemoteFunction : public RemoteFunctionBaseFunction address in the remote process /// Function object template -RemoteFunction MakeRemoteFunction( Process& process, ptr_t ptr ) +RemoteFunction MakeRemoteFunction( Process& process, ptr_t ptr, ThreadPtr boundThread = nullptr ) { - return RemoteFunction( process, ptr ); + return RemoteFunction( process, ptr, boundThread ); } /// @@ -240,9 +247,9 @@ RemoteFunction MakeRemoteFunction( Process& process, ptr_t ptr ) /// Function address in the remote process /// Function object template -RemoteFunction MakeRemoteFunction( Process& process, T ptr ) +RemoteFunction MakeRemoteFunction( Process& process, T ptr, ThreadPtr boundThread = nullptr ) { - return RemoteFunction( process, ptr ); + return RemoteFunction( process, ptr, boundThread ); } /// @@ -252,10 +259,10 @@ RemoteFunction MakeRemoteFunction( Process& process, T ptr ) /// Function name or ordinal /// Function object template -RemoteFunction MakeRemoteFunction( Process& process, const std::wstring& modName, const char* name_ord ) +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 ); + return RemoteFunction( process, ptr ? ptr->procAddress : 0, boundThread ); } -} \ No newline at end of file +} From a672509b5458efeb68f65436259b96fa8cd4dcfc Mon Sep 17 00:00:00 2001 From: DarthTon Date: Sun, 20 Jun 2021 22:34:46 +0300 Subject: [PATCH 33/38] bug fix; code style fix; added test for `ExecInAnyThread` --- src/BlackBone/Process/RPC/RemoteExec.cpp | 8 +--- src/BlackBone/Process/RPC/RemoteFunction.hpp | 12 +++--- src/BlackBoneTest/TestRemoteCall.cpp | 41 ++++++++++++++++++++ 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/src/BlackBone/Process/RPC/RemoteExec.cpp b/src/BlackBone/Process/RPC/RemoteExec.cpp index ce4288d5..f907df9b 100644 --- a/src/BlackBone/Process/RPC/RemoteExec.cpp +++ b/src/BlackBone/Process/RPC/RemoteExec.cpp @@ -225,7 +225,7 @@ NTSTATUS RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callRe (*a)->mov( asmjit::Mem( asmjit::host::rsp, i * sizeof( uint64_t ) ), regs[i] ); a->GenCall( _userCode[_currentBufferIdx].ptr(), { _userData[_currentBufferIdx].ptr() } ); - AddReturnWithEvent( *a ); + AddReturnWithEvent( *a, mt_mod64, rt_int32, INTRET_OFFSET ); // Restore registers for (int i = 0; i < count; i++) @@ -279,11 +279,7 @@ NTSTATUS RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callRe if (NT_SUCCESS( status )) { WaitForSingleObject( _hWaitEvent, 20 * 1000/*INFINITE*/ ); - - if (!_process.core().isWow64()) - status = _userData[_currentBufferIdx].Read( RET_OFFSET, callResult ); - else - status = _userData[_currentBufferIdx].Read( INTRET_OFFSET, callResult ); + status = _userData[_currentBufferIdx].Read( INTRET_OFFSET, callResult ); } SwitchActiveBuffer(); diff --git a/src/BlackBone/Process/RPC/RemoteFunction.hpp b/src/BlackBone/Process/RPC/RemoteFunction.hpp index ffde610a..3c092c0a 100644 --- a/src/BlackBone/Process/RPC/RemoteFunction.hpp +++ b/src/BlackBone/Process/RPC/RemoteFunction.hpp @@ -55,7 +55,7 @@ class RemoteFunctionBase RemoteFunctionBase( Process& proc, ptr_t ptr, ThreadPtr boundThread = nullptr ) : _process( proc ) , _ptr( ptr ) - , _boundThread(boundThread) + , _boundThread( boundThread ) { static_assert( (... && !std::is_reference_v), @@ -71,7 +71,7 @@ class RemoteFunctionBase auto a = AsmFactory::GetAssembler( _process.core().isWow64() ); if (!contextThread) - contextThread = _boundThread; + contextThread = _boundThread; // Ensure RPC environment exists status = _process.remote().CreateRPCEnvironment( Worker_None, contextThread != nullptr ); @@ -133,19 +133,19 @@ class RemoteFunctionBase CallArguments a( args, std::index_sequence_for() ); return Call( a, contextThread ); } - + call_result_t Call( const std::initializer_list& args, ThreadPtr contextThread = nullptr ) { CallArguments a( args ); return Call( a, contextThread ); } - + call_result_t operator()( const Args&... args ) { CallArguments a( args... ); return Call( a ); } - + auto MakeArguments( const Args&... args ) { return CallArguments( args... ); @@ -155,7 +155,7 @@ class RemoteFunctionBase { return CallArguments( args ); } - + auto MakeArguments( const std::tuple& args ) { return CallArguments( args, std::index_sequence_for() ); diff --git a/src/BlackBoneTest/TestRemoteCall.cpp b/src/BlackBoneTest/TestRemoteCall.cpp index 9ae956f0..99374fd5 100644 --- a/src/BlackBoneTest/TestRemoteCall.cpp +++ b/src/BlackBoneTest/TestRemoteCall.cpp @@ -142,6 +142,47 @@ namespace Testing } } + TEST_METHOD( BoundThread ) + { + Process process; + AssertEx::NtSuccess( process.Attach( GetCurrentProcessId() ) ); + + DWORD id = 0; + auto code = []( void* ) -> DWORD + { + for (;;) + Sleep( 1 ); + + return ERROR_SUCCESS; + }; + + HANDLE hThread = CreateThread( nullptr, 0, code, nullptr, 0, &id ); + + AssertEx::IsNotNull( hThread ); + AssertEx::IsNotZero( id ); + + auto thread = process.threads().get( id ); + AssertEx::IsNotNull( thread.get() ); + + auto pFN = MakeRemoteFunction( process, &TestFn, thread ); + double d = 0.0; + + _input.ival = 0xDEAD; + _input.fval = 1337.0f; + _input.uval = 0xDEADC0DEA4DBEEFull; + + 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() ); + } + + TerminateThread( hThread, ERROR_SUCCESS ); + CloseHandle( hThread ); + } + TEST_METHOD( NtQueryVirtualMemory ) { auto path = GetTestHelperHost(); From d0597703f674ecc455869533374e96e543f719eb Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 29 Apr 2023 01:43:32 +0000 Subject: [PATCH 34/38] feat: add support for Windows 11 21H2 and 22H2 --- src/3rd_party/VersionApi.h | 22 ++++++++++++++- src/BlackBone/Symbols/PatternLoader.cpp | 36 ++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/3rd_party/VersionApi.h b/src/3rd_party/VersionApi.h index 0235ed43..50bdbf5b 100644 --- a/src/3rd_party/VersionApi.h +++ b/src/3rd_party/VersionApi.h @@ -29,6 +29,8 @@ enum eBuildThreshold Build_19H1 = 18362, Build_19H2 = 18363, Build_20H1 = 19041, + Build_21H2 = 22000, + Build_22H2 = 22621, Build_RS_MAX = 99999, }; @@ -48,6 +50,8 @@ enum eVerShort Win10_19H1, // Windows 10 May 2019 update Win10_19H2, // Windows 10 November 2019 update Win10_20H1, // Windows 10 April 2020 update + Win11_21H2, // Windows 11 + Win11_22H2 // Windows 11 September 2022 update }; struct WinVersion @@ -109,7 +113,11 @@ BLACKBONE_API inline void InitVersion() switch (fullver) { case _WIN32_WINNT_WIN10: - if (g_WinVer.native.dwBuildNumber >= Build_20H1) + if (g_WinVer.native.dwBuildNumber >= Build_22H2) + g_WinVer.ver = Win11_22H2; + else if (g_WinVer.native.dwBuildNumber >= Build_21H2) + g_WinVer.ver = Win11_21H2; + else if (g_WinVer.native.dwBuildNumber >= Build_20H1) g_WinVer.ver = Win10_20H1; else if (g_WinVer.native.dwBuildNumber >= Build_19H2) g_WinVer.ver = Win10_19H2; @@ -303,6 +311,18 @@ IsWindows1020H1OrGreater() return IsWindowsVersionOrGreater( HIBYTE( _WIN32_WINNT_WIN10 ), LOBYTE( _WIN32_WINNT_WIN10 ), 0, Build_20H1 ); } +VERSIONHELPERAPI +IsWindows1121H2OrGreater() +{ + return IsWindowsVersionOrGreater( HIBYTE( _WIN32_WINNT_WIN10 ), LOBYTE( _WIN32_WINNT_WIN10 ), 0, Build_21H2); +} + +VERSIONHELPERAPI +IsWindows1122H2OrGreater() +{ + return IsWindowsVersionOrGreater( HIBYTE( _WIN32_WINNT_WIN10 ), LOBYTE( _WIN32_WINNT_WIN10 ), 0, Build_22H2 ); +} + VERSIONHELPERAPI IsWindowsServer() { diff --git a/src/BlackBone/Symbols/PatternLoader.cpp b/src/BlackBone/Symbols/PatternLoader.cpp index 62ceff9c..842b82f8 100644 --- a/src/BlackBone/Symbols/PatternLoader.cpp +++ b/src/BlackBone/Symbols/PatternLoader.cpp @@ -69,7 +69,41 @@ void FindPattern( const ScanParams& scan32, const ScanParams& scan64, const Offs /// Result void OSFillPatterns( std::unordered_map& patterns, SymbolData& result ) { - if (IsWindows10RS3OrGreater()) + if (IsWindows1121H2OrGreater()) + { + // LdrpHandleTlsData64 + // 41 55 41 56 41 57 48 81 EC F0 00 00 + patterns.emplace(&result.LdrpHandleTlsData64, OffsetData{ "\x41\x55\x41\x56\x41\x57\x48\x81\xEC\xF0\x00\x00", true, 0xf }); + + // RtlInsertInvertedFunctionTable64 + // 48 89 5C 24 08 57 48 83 EC 30 8B DA + patterns.emplace(&result.RtlInsertInvertedFunctionTable64, OffsetData{ "\x48\x89\x5C\x24\x08\x57\x48\x83\xEC\x30\x8B\xDA", true, 0 }); + + // RtlpInsertInvertedFunctionTableEntry64 + // 49 8B E8 48 8B FA 0F 84 + patterns.emplace(&result.LdrpInvertedFunctionTable64, OffsetData{ "\x49\x8b\xe8\x48\x8b\xfa\x0f\x84", true, -1, -0xF, 2, 6 }); + + // RtlInsertInvertedFunctionTable32 + // 53 56 57 8D 45 F8 8B FA + patterns.emplace(&result.RtlInsertInvertedFunctionTable32, OffsetData{ "\x53\x56\x57\x8d\x45\xf8\x8b\xfa", false, 0x8 }); + + // RtlpInsertInvertedFunctionTableEntry32 + // 33 F6 46 3B C6 + patterns.emplace(&result.LdrpInvertedFunctionTable32, OffsetData{ "\x33\xF6\x46\x3B\xC6", false, -1, -0x1B }); + + // LdrpHandleTlsData32 + // 33 f6 85 c0 79 03 + auto offset = 0x2c; + if (IsWindows1122H2OrGreater()) + offset = 0x42; + + patterns.emplace(&result.LdrpHandleTlsData32, OffsetData{ "\x33\xf6\x85\xc0\x79\x03", false, offset }); + + // LdrProtectMrdata + // 75 20 85 f6 75 35 + patterns.emplace(&result.LdrProtectMrdata, OffsetData{ "\x75\x20\x85\xf6\x75\x35", false, 0x1d }); + } + else if (IsWindows10RS3OrGreater()) { // LdrpHandleTlsData // 74 33 44 8D 43 09 From 2708d880eaf6929e111a407507baf7fdddfca60a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 17 May 2022 18:10:26 +0800 Subject: [PATCH 35/38] CreateFile fail with INVALID_HANDLE_VALUE --- src/BlackBone/DriverControl/DriverControl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BlackBone/DriverControl/DriverControl.cpp b/src/BlackBone/DriverControl/DriverControl.cpp index 74ac7bcf..38bb10bf 100644 --- a/src/BlackBone/DriverControl/DriverControl.cpp +++ b/src/BlackBone/DriverControl/DriverControl.cpp @@ -93,7 +93,7 @@ NTSTATUS DriverControl::Reload( std::wstring path /*= L"" */ ) NULL, OPEN_EXISTING, 0, NULL ); - if (!_hDriver) + if (_hDriver != INVALID_HANDLE_VALUE) { _loadStatus = LastNtStatus(); BLACKBONE_TRACE( L"Failed to open driver handle. Status 0x%X", _loadStatus ); From f1a37a875d27cffdcf7339361db38857a2f1fb76 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 17 May 2022 18:16:38 +0800 Subject: [PATCH 36/38] typo --- src/BlackBone/DriverControl/DriverControl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BlackBone/DriverControl/DriverControl.cpp b/src/BlackBone/DriverControl/DriverControl.cpp index 38bb10bf..c8063e21 100644 --- a/src/BlackBone/DriverControl/DriverControl.cpp +++ b/src/BlackBone/DriverControl/DriverControl.cpp @@ -93,7 +93,7 @@ NTSTATUS DriverControl::Reload( std::wstring path /*= L"" */ ) NULL, OPEN_EXISTING, 0, NULL ); - if (_hDriver != INVALID_HANDLE_VALUE) + if (_hDriver == INVALID_HANDLE_VALUE) { _loadStatus = LastNtStatus(); BLACKBONE_TRACE( L"Failed to open driver handle. Status 0x%X", _loadStatus ); From 61fe5252aa92ba736d09c00cfd368bf000e28ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=BE=E6=9C=AF?= <505361340@qq.com> Date: Wed, 15 Sep 2021 13:10:26 +0800 Subject: [PATCH 37/38] Fix a crash on win7 x86 system. --- src/BlackBone/Patterns/PatternSearch.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/BlackBone/Patterns/PatternSearch.cpp b/src/BlackBone/Patterns/PatternSearch.cpp index ec517df7..fcef5732 100644 --- a/src/BlackBone/Patterns/PatternSearch.cpp +++ b/src/BlackBone/Patterns/PatternSearch.cpp @@ -98,6 +98,9 @@ bool PatternSearch::SearchWithHandler( ptr_t value_offset /*= 0*/ ) const { + if (scanSize == 0) + return false; + size_t bad_char_skip[UCHAR_MAX + 1]; const uint8_t* haystack = reinterpret_cast(scanStart); From 5ede6ce50cd8ad34178bfa6cae05768ff6b3859b Mon Sep 17 00:00:00 2001 From: haikejishu <229083030@qq.com> Date: Sun, 3 Apr 2022 14:09:14 +0800 Subject: [PATCH 38/38] fix Test CallLoop bugs when builded with win32 --- src/BlackBone/Process/RPC/RemoteExec.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/BlackBone/Process/RPC/RemoteExec.cpp b/src/BlackBone/Process/RPC/RemoteExec.cpp index f907df9b..cbfd4cf7 100644 --- a/src/BlackBone/Process/RPC/RemoteExec.cpp +++ b/src/BlackBone/Process/RPC/RemoteExec.cpp @@ -553,6 +553,11 @@ NTSTATUS RemoteExec::PrepareCallAssembly( } a.GenPrologue(); + if (_process.core().isWow64()) + { + a->pusha(); + a->pushf(); + } a.GenCall( pfn, args, cc ); // Retrieve result from XMM0 or ST0 @@ -571,6 +576,11 @@ NTSTATUS RemoteExec::PrepareCallAssembly( } AddReturnWithEvent( a, mt_default, retType ); + if (_process.core().isWow64()) + { + a->popf(); + a->popa(); + } a.GenEpilogue(); return STATUS_SUCCESS;