The strongest hacker library Blackbone tutorial

Posted by cac_azure03 on Fri, 14 Jan 2022 13:13:11 +0100

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

  1. sdk and wdk versions should be the same 10.0.17763.0
  2. cor.h file and related mscoree The Lib library cannot find a compilation error and needs to be installed NET desktop development
  3. VC++ 2017 Libs for Spectre (x86 and x64)
  4. 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.

Topics: security Web Security