Environment construction
Project address
https://github.com/DarthTon/Blackbone
Project introduction
As Windows developers, they often encounter operations of enumerating processes, enumerating modules, reading and writing process memory; Windows security developers will involve injection, hook, operating PE files and writing drivers. We have to turn over all kinds of materials to make wheels every time. Is there a good open source library to solve this problem? At present, the Blackbone is known, and its code is well written, which can be used as a textbook.
99% of all types of package libraries on the Internet are copy projects, and many big guys' projects refer to the code. It can be seen that the importance and strength of this project are self-evident.
Library content contains
Process interaction(Process interaction) operation PEB32/PEB64 adopt WOW64 barrier supervisor process Process Memory(Process memory) Allocate and free virtual memory Change memory protection properties Read and write virtual memory Process modules(Process module) Enumerate modules loaded by all processes (32/64 bit) Get the address of the exported function Get main module Remove module information from the module list Injection and unloading module(support pure IL images) stay WOW64 Inject 64 bit modules into the process operation PE Threads(Thread) Enumerating threads Create and close threads Get thread exit code Get main thread Administration TEB32/TEB64 join Thread (wait for thread exit) Pause and resume threads Set and clear hardware breakpoints Pattern search Signature matching (support this process and other processes) Remote code execution Executing functions in remote processes Assemble your own code and execute it remotely( shellcode Injection) Various call modes are supported: cdecl/stdcall/thiscall/fastcall Various parameter types are supported support FPU Supports execution in new threads or existing threads shellcode Remote hooking Through soft and hard breakpoints, in the remote process hook function Hook functions upon return(adopt return Hook function) Manual map features Hidden injection, support x86 and x64,Inject any unprotected process, support import table and delayed import, and so on. Driver features Allocate, release and protect memory Read and write user layer and driver layer memory about WOW64 Process, disabling DEP Modify process protection flag Modify handle access Operation process memory (e.g. target process map To this process, etc.) Hide allocated user mode memory User mode dll Injection; Manual mapping(Manual loading module) Manual load drive
Project compilation
The following components need to be installed
- sdk and wdk versions should be the same 10.0.17763.0
- cor.h file and related mscoree The Lib library cannot find a compilation error and needs to be installed NET desktop development
- VC++ 2017 Libs for Spectre (x86 and x64)
- Visual C++ ATL (x86/x64) with Spectre Mitigations
When I compiled with VS2017, I reported an error referring to the deleted function. If I couldn't find a solution, I directly asked someone to compile it for me.
It is said that the latest version only supports VS2019. If you want to use VS2017, you need to select other branch codes.
Project integration
Create a new empty project
Then create a new include folder under the project directory
Copy the three folders in the src directory to the include folder
The above three folders need to be used when referencing header files. The compiled lib library is stored in build
The lib library and header files are then included
#include <iostream> #include "../include/BlackBone/Config.h" #include "../include/BlackBone/Process/Process.h" #include "../include/BlackBone/Process/MultPtr.hpp" #include "../include/BlackBone/Process/RPC/RemoteFunction.hpp" #include "../include/BlackBone/PE/PEImage.h" #include "../include/BlackBone/Misc/Utils.h" #include "../include/BlackBone/Misc/DynImport.h" #include "../include/BlackBone/Syscalls/Syscall.h" #include "../include/BlackBone/Patterns/PatternSearch.h" #include "../include/BlackBone/Asm/LDasm.h" #include "../include/BlackBone/localHook/VTableHook.hpp" #include "../include/BlackBone/DriverControl/DriverControl.h" #ifdef _DEBUG #pragma comment(lib, "..\\include\\build\\Win32\\Debug\\BlackBone.lib") #else #pragma comment(lib, "..\\include\\build\\Win32\\Release\\BlackBone.lib") #endif
using namespace std
using namespace blackbone;
Call a function at will to compile. The compilation indicates that the integration is successful.
Then you need to copy the three files generated by build to the project directory, otherwise the following error will be reported
Project use
The code function of each module is explained below.
Process interaction
Enumerate all process ID s by process name
auto pids = Process::EnumByName(L"explorer.exe");
Enumerate process information according to process ID or process name
auto procInfo = Process::EnumByNameOrPID(4596, L""); for (auto info : procInfo.result()) { wcout << info.imageName << info.pid << info.threads.size(); }
Enumerate all processes
auto all = Process::EnumByNameOrPID(0, L""); for (auto info : all.result()) { wcout << info.imageName << "\t" << info.pid << info.threads.size() << endl; }
Attach a process (open process) and get relevant information
Process process; vector<DWORD> vecPid = Process::EnumByName(L"QQ.exe"); //Attach a process if (!vecPid.empty()&&NT_SUCCESS(process.Attach(vecPid.front()))) { //ProcessCore class ProcessCore& processCore = process.core(); //Process WOW64 information Wow64Barrier barrier = process.barrier(); //Get process ID and process handle DWORD dwPid = processCore.pid(); HANDLE hProcess = processCore.handle(); //Get process PEB PEB_T peb = {}; ptr_t ptrPEB = processCore.peb(&peb); //Gets all handles to the process for (HandleInfo handleInfo:process.EnumHandles().result()) { wcout << handleInfo.name << handleInfo.handle << handleInfo.typeName << endl; }
Start and attach processes
//Start and attach processes Process process; process.CreateAndAttach(L"D:\\InjectTool.exe", true); { //Recovery process process.Resume(); //Delay 100 ms std::this_thread::sleep_for(std::chrono::milliseconds(100)); }
Process module management
//Process module management Process process; if (NT_SUCCESS(process.Attach(L"WeChat.exe"))) { //Module management ProcessModules& proModules = process.modules(); //Get all modules, including x86 and x64 modules auto mapModule = proModules.GetAllModules(); //Get main module ModuleDataPtr mainModule = proModules.GetMainModule(); //Base address of main module module_t baseAddress = mainModule->baseAddress; //Get the address of the exported function call_result_t<exportData> exportFunAddr = proModules.GetExport(L"kernel32.dll", "LoadLibraryW"); if (exportFunAddr.success()) { cout << "LoadLibraryW" << exportFunAddr->procAddress << endl; } //Module broken chain proModules.Unlink(mainModule); auto mapModule2 = proModules.GetAllModules(); //Injection module call_result_t<ModuleDataPtr> ptr= proModules.Inject(L"E:\\Dll1.dll"); //Uninstall module proModules.Unload(ptr); }
There is a very powerful function in it. The module is broken and the actual measurement is feasible~
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-oc7z6bw0-1642037164410) (use of blackbone hacker library. assets/image-20220112125146182.png)]
Process memory management
Process process; if (NT_SUCCESS(process.Attach(L"QQ.exe"))) { //Operation memory class ProcessMemory& memory = process.memory(); //Main module ModuleDataPtr mainModoulePtr = process.modules().GetMainModule(); //PE header information IMAGE_DOS_HEADER dosHeader = {}; //The start information of reading the memory module is the PE header information //The first method memory.Read(mainModoulePtr->baseAddress, dosHeader); //The second method memory.Read(mainModoulePtr->baseAddress, sizeof(dosHeader),&dosHeader); //The third method call_result_t<IMAGE_DOS_HEADER> dosHeaderResult = memory.Read<IMAGE_DOS_HEADER>(mainModoulePtr->baseAddress); //Change memory read / write properties if (NT_SUCCESS(memory.Protect(mainModoulePtr->baseAddress,sizeof(dosHeader), PAGE_READWRITE))) { //The first way to write memory memory.Write(mainModoulePtr->baseAddress, dosHeader); //The second way to write memory memory.Write(mainModoulePtr->baseAddress, sizeof(dosHeader),&dosHeader); } //Allocate memory call_result_t<MemBlock> memBlockResult = memory.Allocate(0x1000, PAGE_READWRITE); if (memBlockResult.success()) { //Write memory memBlockResult->Write(0x10, 12.0); //Read memory memBlockResult->Read<double>(0x10, 0.0); } //Enumerate all valid memory regions vector<MEMORY_BASIC_INFORMATION64> region = memory.EnumRegions(); }
Process thread management
Process process; if (NT_SUCCESS(process.Attach(L"QQ.exe"))) { //Enumerate all threads vector<ThreadPtr> vecTheadPtr = process.threads().getAll(); //Get main thread ThreadPtr mainThread = process.threads().getMain(); //Get thread information according to thread ID ThreadPtr threadPtr = process.threads().get(mainThread->id()); //Get thread context CONTEXT_T ctx = {}; if (threadPtr->GetContext(ctx, CONTEXT_FLOATING_POINT)) { //Set the Dr register here to clear the hardware breakpoint //...... //set thread context threadPtr->SetContext(ctx); } //Wait for the thread to exit threadPtr->Join(100); }
JIT assembly
AsmHelper32 asmPtr = AsmFactory::GetAssembler(); IAsmHelper& a = *asmPtr; //Generate function code a->GenPrologue(); a->add(a->zcx, a->zdx); a->mov(a->zax, a->zdx); a.GenEpilogue(); auto func = reinterpret_cast<uintptr_t(__fastcall*)(uintptr_t, uintptr_t)>(a->make()); uintptr_t r = func(10, 5);
Remote code execution
Process process; if (NT_SUCCESS(process.Attach(L"QQ.exe"))) { //Remote execution class RemoteExec& remoteExe = process.remote(); //Create remote execution environment remoteExe.CreateRPCEnvironment(Worker_None, true); //Get the address of the exported function call_result_t<exportData> GetModuleHandleWPtr = process.modules().GetExport(L"kernel32.dll", "GetModuleHandleW"); if (GetModuleHandleWPtr.success()) { //Creates a new thread with the specified entry point and parameters DWORD mod = remoteExe.ExecDirect(GetModuleHandleWPtr->procAddress, 0); AsmHelperPtr asmPtr = AsmFactory::GetAssembler(); if (asmPtr) { blackbone::IAsmHelper& asmHelper = *asmPtr; asmHelper.GenPrologue(); asmHelper.GenCall(static_cast<uintptr_t>(GetModuleHandleWPtr->procAddress), { nullptr }, blackbone::cc_stdcall); asmHelper.GenEpilogue(); uint64_t result = 0; //Create a new thread and execute code in it. Wait for execution to end remoteExe.ExecInNewThread(asmHelper->make(), asmHelper->getCodeSize(), result); } //Execute on main thread blackbone::ThreadPtr mainThreadPtr = process.threads().getMain(); blackbone::AsmHelperPtr asmHelperPtr = blackbone::AsmFactory::GetAssembler(); if (asmHelperPtr) { blackbone::IAsmHelper& asmHelper = *asmHelperPtr; asmHelper.GenPrologue(); asmHelper.GenCall(static_cast<uintptr_t>(GetModuleHandleWPtr->procAddress), { nullptr }, blackbone::cc_stdcall); asmHelper.GenEpilogue(); uint64_t result = 0; remoteExe.ExecInAnyThread(asmHelper->make(), asmHelper->getCodeSize(), result, mainThreadPtr); } } }
Memory search match
Process process; if (NT_SUCCESS(process.Attach(L"QQ.exe"))) { //Matching template PatternSearch ps{ 1,2,3,4,5,6,7,8,9 }; //search result vector<ptr_t> results; //Memory search ps.SearchRemote(process, false, 0, results); }
The SearchRemote function above is remote search
BLACKBONE_API size_t Search( uint8_t wildcard, void* scanStart, size_t scanSize, std::vector<ptr_t>& out, ptr_t value_offset = 0, size_t maxMatches = SIZE_MAX ) const;
In addition, a Search function is provided to support this process Search, and wildcards are also supported
Remote function call
Process process; if (NT_SUCCESS(process.Attach(L"QQ.exe"))) { //1. Simple direct call //Get remote function object RemoteFunction<decltype(&MessageBoxW)> pMessageBoxW = MakeRemoteFunction<decltype(&MessageBoxW)>(process, L"user32.dll", "MessageBoxW"); if (pMessageBoxW.valid()) { //Remote call auto result = pMessageBoxW(HWND_DESKTOP, L"HelloWorld", L"Title", MB_OK); } //2. Call with a specific thread auto mainThread = process.threads().getMain(); if (auto pIsGuiThread= MakeRemoteFunction<decltype(&IsGUIThread)>(process, L"user32.dll", "IsGUIThread"); pIsGuiThread && mainThread) { auto result = pIsGuiThread.Call({ FALSE }, mainThread); if (*result) { } } //3. Given parameter call if (auto pMultiByteToWideChar = blackbone::MakeRemoteFunction<decltype(&MultiByteToWideChar)>(process, L"kernel32.dll", "MultiByteToWideChar")) { auto args = pMultiByteToWideChar.MakeArguments({ CP_ACP, 0, "Sample text", -1, nullptr, 0 }); std::wstring converted(32, L'\0'); // Set buffer pointer and size manually args.set(4, blackbone::AsmVariant(converted.data(), converted.size() * sizeof(wchar_t))); args.set(5, converted.size()); auto length = pMultiByteToWideChar.Call(args); if (length) converted.resize(*length - 1); } }
The system call applies only to x64
{ uint8_t buf[32] = { }; uintptr_t bytes = 0; NTSTATUS status = syscall::nt_syscall( syscall::get_index( "NtReadVirtualMemory" ), GetCurrentProcess(), GetModuleHandle( nullptr ), buf, sizeof(buf), &bytes ); if (NT_SUCCESS( status )) { } }
dll hidden injection
//Hide injection dll process name dll path adaptation 32-bit and 64 bit void HideInject(const std::wstring& processName, const std::wstring& dllPath) { vector<DWORD> vecPid = Process::EnumByName(processName); if (vecPid.empty()) { MessageBoxA(0, "No target process exists", "Tips", 0); return; } //First, get the information of the target process Process proc; proc.Attach(vecPid.front()); //Ensure that LdrInitializeProcess is called proc.EnsureInit(); //Map PE file to target process 1 PE file path flags (manual mapping import function) callback function auto image = proc.mmap().MapImage(dllPath, ManualImports, &MapCallback2); //Get the address of the exported function auto g_loadDataPtr = proc.modules().GetExport(image.result(), "g_LoadData"); //Call export function auto g_loadData = proc.memory().Read<DllLoadData>(g_loadDataPtr->procAddress); }
The code of the ManualMap module uses the principle of reflective dll injection to expand the PE file in the target process without the help of LoadLibrary, so as to realize hidden injection.
https://github.com/DarthTon/Xenos
In addition, the author also has a very powerful injection tool, which supports three zero ring injection methods and two three ring injection methods. However, this tool has basically been detected by major foreign games.
summary
This project seems to be very powerful, including the encapsulation of various modules. However, in practice, only one or two modules are used. I only need the two functions of feature code search and dll hidden injection, and then I learned other modules by the way.
Unexpectedly, in the final integration, the header files are dependent on each other, and the data types are customized by the author. If you want to take out one of the modules, you will eventually include the whole project code because the header file contains problems.
It's a bit of a chicken rib to add a few unnecessary module codes for a function. In general, the learning value is full, and the practicability is unknown.