Goal
Let’s modify our Go program from Lab 3.1 (manual_mapper.go
) to handle base relocations. If the DLL could not be loaded at its preferred ImageBase
, this lab adds the logic to parse the DLL’s relocation table and patch all necessary addresses within the mapped image based on the actual load address.
Now, even with ASLR, it’s often the case, and especially for DLLs loaded reflectively into a process that hasn’t had its address space heavily populated yet, that the preferred ImageBase
is often available, meaning the delta
is zero and the relocation code path doesn’t run. So in this lab we’re going to artificially “force” relocations here, otherwise we’ll implement logic which won’t even be tested.
So in addition to introducing our relocation logic we’ll modify the start of the allocation process to deliberately occupy the DLL’s preferred ImageBase
before attempting to allocate the main block for the DLL. This will force the main allocation to use the fallback mechanism (letting the OS choose an address), resulting in a non-zero delta and ensuring your relocation logic gets tested
Note we’ll once again load call_dll.dll
.
Code
//go:build windows
// +build windows
package main
import (
"bytes"
"encoding/binary"
"fmt"
"log"
"os"
"runtime" // Import runtime package
"unsafe" // Needed for pointer conversions with syscall/windows
"golang.org/x/sys/windows"
)
// --- Existing PE Structures ---
type IMAGE_DOS_HEADER struct {
Magic uint16 // Magic number (MZ)
Cblp uint16 // Bytes on last page of file
Cp uint16 // Pages in file
Crlc uint16 // Relocations
Cparhdr uint16 // Size of header in paragraphs
MinAlloc uint16 // Minimum extra paragraphs needed
MaxAlloc uint16 // Maximum extra paragraphs needed
Ss uint16 // Initial (relative) SS value
Sp uint16 // Initial SP value
Csum uint16 // Checksum
Ip uint16 // Initial IP value
Cs uint16 // Initial (relative) CS value
Lfarlc uint16 // File address of relocation table
Ovno uint16 // Overlay number
Res [4]uint16 // Reserved words
Oemid uint16 // OEM identifier (for e_oeminfo)
Oeminfo uint16 // OEM information; e_oemid specific
Res2 [10]uint16 // Reserved words
Lfanew int32 // File address of new exe header (PE header offset)
}
type IMAGE_FILE_HEADER struct {
Machine uint16 // Architecture type
NumberOfSections uint16 // Number of sections
TimeDateStamp uint32 // Time and date stamp
PointerToSymbolTable uint32 // Pointer to symbol table
NumberOfSymbols uint32 // Number of symbols
SizeOfOptionalHeader uint16 // Size of optional header
Characteristics uint16 // File characteristics
}
type IMAGE_DATA_DIRECTORY struct {
VirtualAddress uint32 // RVA of the directory
Size uint32 // Size of the directory
}
type IMAGE_OPTIONAL_HEADER64 struct {
Magic uint16 // Magic number (0x20b for PE32+)
MajorLinkerVersion uint8
MinorLinkerVersion uint8
SizeOfCode uint32
SizeOfInitializedData uint32
SizeOfUninitializedData uint32
AddressOfEntryPoint uint32 // RVA of the entry point
BaseOfCode uint32
ImageBase uint64 // Preferred base address
SectionAlignment uint32
FileAlignment uint32
MajorOperatingSystemVersion uint16
MinorOperatingSystemVersion uint16
MajorImageVersion uint16
MinorImageVersion uint16
MajorSubsystemVersion uint16
MinorSubsystemVersion uint16
Win32VersionValue uint32
SizeOfImage uint32 // Total size of the image in memory
SizeOfHeaders uint32 // Size of headers (DOS + PE + Section Headers)
CheckSum uint32
Subsystem uint16
DllCharacteristics uint16
SizeOfStackReserve uint64
SizeOfStackCommit uint64
SizeOfHeapReserve uint64
SizeOfHeapCommit uint64
LoaderFlags uint32
NumberOfRvaAndSizes uint32
DataDirectory [16]IMAGE_DATA_DIRECTORY // Array of data directories
}
type IMAGE_SECTION_HEADER struct {
Name [8]byte // Section name (null-padded)
VirtualSize uint32 // Actual size used in memory
VirtualAddress uint32 // RVA of the section
SizeOfRawData uint32 // Size of section data on disk
PointerToRawData uint32 // File offset of section data
PointerToRelocations uint32 // File offset of relocations
PointerToLinenumbers uint32 // File offset of line numbers
NumberOfRelocations uint16 // Number of relocations
NumberOfLinenumbers uint16 // Number of line numbers
Characteristics uint32 // Section characteristics (flags like executable, readable, writable)
}
// --- NEW: Relocation Structures/Constants ---
type IMAGE_BASE_RELOCATION struct { //nolint:revive // Windows struct
VirtualAddress uint32 // RVA of the page this block applies to
SizeOfBlock uint32 // Total size of this relocation block (including header)
}
const (
IMAGE_DOS_SIGNATURE = 0x5A4D // "MZ"
IMAGE_NT_SIGNATURE = 0x00004550 // "PE\0\0"
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 // Base Relocation Table index in DataDirectory
IMAGE_REL_BASED_DIR64 = 10 // Relocation type for 64-bit addresses (x64)
IMAGE_REL_BASED_ABSOLUTE = 0 // Padding/nop relocation type
// Adding Memory constants if not already implicitly available via windows package
MEM_COMMIT = 0x00001000
MEM_RESERVE = 0x00002000
MEM_RELEASE = 0x8000
PAGE_READWRITE = 0x04
PAGE_EXECUTE_READWRITE = 0x40
)
// --- Existing Helper Functions ---
func sectionNameToString(nameBytes [8]byte) string {
n := bytes.IndexByte(nameBytes[:], 0)
if n == -1 {
n = 8
}
return string(nameBytes[:n])
}
// --- Main Function ---
func main() {
// Ensure running on Windows
if runtime.GOOS != "windows" {
log.Fatal("[-] This program must be run on Windows.")
}
fmt.Println("[+] Starting Manual DLL Mapper (with Forced Relocation)...")
// --- Step 1: Read DLL and Parse Headers ---
if len(os.Args) < 2 {
log.Fatalf("[-] Usage: %s <path_to_dll>\n", os.Args[0])
}
dllPath := os.Args[1]
fmt.Printf("[+] Reading file: %s\n", dllPath)
dllBytes, err := os.ReadFile(dllPath)
if err != nil {
log.Fatalf("[-] Failed to read file '%s': %v\n", dllPath, err)
}
reader := bytes.NewReader(dllBytes)
var dosHeader IMAGE_DOS_HEADER
if err := binary.Read(reader, binary.LittleEndian, &dosHeader); err != nil {
log.Fatalf("[-] Failed to read DOS header: %v\n", err)
}
if dosHeader.Magic != IMAGE_DOS_SIGNATURE {
log.Fatalf("[-] Invalid DOS signature")
}
if _, err := reader.Seek(int64(dosHeader.Lfanew), 0); err != nil {
log.Fatalf("[-] Failed to seek to NT Headers: %v\n", err)
}
var peSignature uint32
if err := binary.Read(reader, binary.LittleEndian, &peSignature); err != nil {
log.Fatalf("[-] Failed to read PE signature: %v\n", err)
}
if peSignature != IMAGE_NT_SIGNATURE {
log.Fatalf("[-] Invalid PE signature")
}
var fileHeader IMAGE_FILE_HEADER
if err := binary.Read(reader, binary.LittleEndian, &fileHeader); err != nil {
log.Fatalf("[-] Failed to read File Header: %v\n", err)
}
var optionalHeader IMAGE_OPTIONAL_HEADER64
if err := binary.Read(reader, binary.LittleEndian, &optionalHeader); err != nil {
log.Fatalf("[-] Failed to read Optional Header: %v\n", err)
}
if optionalHeader.Magic != 0x20b {
log.Printf("[!] Warning: Optional Header Magic is not PE32+ (0x20b).")
}
fmt.Println("[+] Parsed PE Headers successfully.")
fmt.Printf("[+] Target ImageBase: 0x%X\n", optionalHeader.ImageBase)
fmt.Printf("[+] Target SizeOfImage: 0x%X (%d bytes)\n", optionalHeader.SizeOfImage, optionalHeader.SizeOfImage)
// --- End Step 1 ---
// --- *** HERE WE ARE FORCING RELOCATION BY OCCUPYING IMAGEBASE, WE'LL REMOVE THIS AFTER THIS LAB *** ---
preferredImageBase := uintptr(optionalHeader.ImageBase) // Get preferred base from parsed header
fmt.Printf("[+] Attempting to reserve preferred ImageBase (0x%X) to force relocation...\n", preferredImageBase)
blockingAllocSize := uintptr(4096) // Allocate just one page
blockingAllocBase, errBlock := windows.VirtualAlloc(preferredImageBase, blockingAllocSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE)
if errBlock != nil {
log.Printf("[!] Warning: Could not allocate blocking memory at preferred base 0x%X: %v. Relocation might not be forced.", preferredImageBase, errBlock)
} else {
fmt.Printf("[+] Successfully allocated blocking memory at 0x%X. Relocation should be forced.\n", blockingAllocBase)
// Defer release of the blocking allocation
defer func() {
fmt.Println("[+] Freeing blocking allocation...")
errFree := windows.VirtualFree(blockingAllocBase, 0, MEM_RELEASE)
if errFree != nil {
log.Printf("[!] Warning: Failed to free blocking allocation: %v", errFree)
} else {
fmt.Println("[+] Blocking allocation freed.")
}
}()
}
// --- *** End *** ---
// --- Step 2: Allocate Memory for DLL ---
fmt.Printf("[+] Allocating 0x%X bytes of memory for DLL...\n", optionalHeader.SizeOfImage)
allocSize := uintptr(optionalHeader.SizeOfImage)
// preferredBase already defined above
allocBase, err := windows.VirtualAlloc(preferredImageBase, allocSize, windows.MEM_RESERVE|windows.MEM_COMMIT, windows.PAGE_EXECUTE_READWRITE)
if err != nil {
// If preferred base failed (as expected), let the OS choose the address
fmt.Printf("[*] Failed to allocate at preferred base 0x%X (EXPECTED): %v. Trying arbitrary address...\n", preferredImageBase, err)
allocBase, err = windows.VirtualAlloc(0, allocSize, windows.MEM_RESERVE|windows.MEM_COMMIT, windows.PAGE_EXECUTE_READWRITE)
if err != nil {
log.Fatalf("[-] Failed to allocate memory at arbitrary address: %v\n", err)
}
} else {
// This case means our blocking allocation failed AND the preferred base was free
fmt.Println("[*] Allocated at preferred base unexpectedly (blocking allocation might have failed).")
}
fmt.Printf("[+] DLL memory allocated successfully at actual base address: 0x%X\n", allocBase)
// Defer release of the main DLL allocation
defer func() {
fmt.Printf("[+] Attempting to free main DLL allocation at 0x%X...\n", allocBase)
err := windows.VirtualFree(allocBase, 0, windows.MEM_RELEASE)
if err != nil {
log.Printf("[!] Warning: Failed to free main DLL memory: %v\n", err)
} else {
fmt.Println("[+] Main DLL memory freed successfully.")
}
}()
// --- End Step 2 ---
// --- Step 3: Copy Headers into Allocated Memory ---
fmt.Printf("[+] Copying PE headers (%d bytes) to allocated memory...\n", optionalHeader.SizeOfHeaders)
headerSize := uintptr(optionalHeader.SizeOfHeaders)
dllBytesPtr := uintptr(unsafe.Pointer(&dllBytes[0]))
var bytesWritten uintptr
err = windows.WriteProcessMemory(windows.CurrentProcess(), allocBase, (*byte)(unsafe.Pointer(dllBytesPtr)), headerSize, &bytesWritten)
if err != nil || bytesWritten != headerSize {
log.Fatalf("[-] Failed to copy PE headers to allocated memory: %v (Bytes written: %d)", err, bytesWritten)
}
fmt.Printf("[+] Copied %d bytes of headers successfully.\n", bytesWritten)
// --- End Step 3 ---
// --- Step 4: Copy Sections into Allocated Memory ---
// NoteL Code adjusted slightly from Lab 3.1 to read section headers from allocBase
fmt.Println("[+] Copying sections...")
firstSectionHeaderOffset := uintptr(dosHeader.Lfanew) + 4 + unsafe.Sizeof(fileHeader) + uintptr(fileHeader.SizeOfOptionalHeader)
sectionHeaderSize := unsafe.Sizeof(IMAGE_SECTION_HEADER{}) // Get size of struct for iteration
for i := uint16(0); i < fileHeader.NumberOfSections; i++ {
// *** Read section header from the *mapped* headers in allocBase ***
currentSectionHeaderAddr := allocBase + firstSectionHeaderOffset + uintptr(i)*sectionHeaderSize
sectionHeader := (*IMAGE_SECTION_HEADER)(unsafe.Pointer(currentSectionHeaderAddr))
// *** End modification for reading section header ***
sectionName := sectionNameToString(sectionHeader.Name)
fmt.Printf(" [*] Processing Section %d: '%s'\n", i, sectionName)
if sectionHeader.SizeOfRawData == 0 {
fmt.Printf(" [*] Skipping section '%s' (SizeOfRawData is 0).\n", sectionName)
continue
}
sourceAddr := dllBytesPtr + uintptr(sectionHeader.PointerToRawData)
destAddr := allocBase + uintptr(sectionHeader.VirtualAddress)
sizeToCopy := uintptr(sectionHeader.SizeOfRawData)
fmt.Printf(" [*] Copying %d bytes from file offset 0x%X to VA 0x%X\n", sizeToCopy, sectionHeader.PointerToRawData, destAddr)
err = windows.WriteProcessMemory(windows.CurrentProcess(), destAddr, (*byte)(unsafe.Pointer(sourceAddr)), sizeToCopy, &bytesWritten)
if err != nil || bytesWritten != sizeToCopy {
log.Fatalf(" [-] Failed to copy section '%s': %v (Bytes written: %d)", sectionName, err, bytesWritten)
}
fmt.Printf(" [+] Copied section '%s' successfully (%d bytes).\n", sectionName, bytesWritten)
}
fmt.Println("[+] All sections copied.")
// --- End Step 4 ---
// --- *** NEW: Step 5: Process Base Relocations *** ---
fmt.Println("[+] Checking if base relocations are needed...")
delta := int64(allocBase) - int64(optionalHeader.ImageBase) // Use the parsed preferred base
if delta == 0 {
// This should NOT happen if the blocking allocation worked
fmt.Println("[!] Image loaded at preferred base unexpectedly. Relocations not tested.")
} else {
fmt.Printf("[+] Image loaded at non-preferred base (Delta: 0x%X). Processing relocations...\n", delta)
// Find the Base Relocation Directory entry using the parsed optionalHeader
relocDirEntry := optionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]
relocDirRVA := relocDirEntry.VirtualAddress
relocDirSize := relocDirEntry.Size
if relocDirRVA == 0 || relocDirSize == 0 {
fmt.Println("[!] Warning: Image rebased, but no relocation directory found or empty.")
} else {
fmt.Printf("[+] Relocation Directory found at RVA 0x%X, Size 0x%X\n", relocDirRVA, relocDirSize)
relocTableBase := allocBase + uintptr(relocDirRVA) // VA of the start of the .reloc section in allocBase
relocTableEnd := relocTableBase + uintptr(relocDirSize)
currentBlockAddr := relocTableBase
totalFixups := 0
// Iterate through IMAGE_BASE_RELOCATION blocks
for currentBlockAddr < relocTableEnd {
// Read block header directly from allocBase memory
blockHeader := (*IMAGE_BASE_RELOCATION)(unsafe.Pointer(currentBlockAddr))
// Check for end marker or invalid size
if blockHeader.VirtualAddress == 0 || blockHeader.SizeOfBlock <= uint32(unsafe.Sizeof(IMAGE_BASE_RELOCATION{})) {
break // Stop processing
}
numEntries := (blockHeader.SizeOfBlock - uint32(unsafe.Sizeof(IMAGE_BASE_RELOCATION{}))) / 2
entryPtr := currentBlockAddr + unsafe.Sizeof(IMAGE_BASE_RELOCATION{}) // Pointer to first entry
// Iterate through the 16-bit entries in this block
for i := uint32(0); i < numEntries; i++ {
// Read entry directly from allocBase memory
entry := *(*uint16)(unsafe.Pointer(entryPtr + uintptr(i*2)))
relocType := entry >> 12
offset := entry & 0xFFF
if relocType == IMAGE_REL_BASED_DIR64 {
// Calculate the absolute VA within allocBase where the patch needs to be applied
patchAddr := allocBase + uintptr(blockHeader.VirtualAddress) + uintptr(offset)
// Read the original 64-bit value directly from allocBase memory
originalValuePtr := (*uint64)(unsafe.Pointer(patchAddr))
originalValue := *originalValuePtr
// Apply the delta
newValue := uint64(int64(originalValue) + delta)
// Write the new value back directly into allocBase memory
*originalValuePtr = newValue
totalFixups++
} else if relocType != IMAGE_REL_BASED_ABSOLUTE {
fmt.Printf(" [!] Warning: Skipping unhandled relocation type %d at offset 0x%X\n", relocType, offset)
}
}
// Move to the next block header
currentBlockAddr += uintptr(blockHeader.SizeOfBlock)
}
fmt.Printf("[+] Relocation processing complete. Total fixups applied: %d\n", totalFixups)
}
}
// --- *** End Step 5 *** ---
// --- Step 6: Self-Check -
fmt.Println("[+] Manual mapping process complete (Headers, Sections copied, Relocations potentially applied).")
fmt.Println("[+] Self-Check Suggestion: Use a debugger (like Delve for Go or x64dbg for the process)")
fmt.Println(" to inspect the memory at the allocated base address (0x%X).", allocBase)
fmt.Println(" Verify that the 'MZ' and 'PE' signatures are present at the start")
fmt.Println(" and that data corresponding to sections appears at the correct RVAs.")
fmt.Println(" If relocations occurred, check known absolute addresses (if any) were patched.")
fmt.Println("\n[+] Press Enter to free memory and exit.")
fmt.Scanln()
fmt.Println("[+] Mapper finished.")
// Deferred VirtualFree calls will execute now
}
// Helper functions
func machineTypeToString(machine uint16) string {
switch machine {
case 0x0:
return "Unknown"
case 0x14c:
return "x86 (I386)"
case 0x8664:
return "x64 (AMD64)"
case 0xaa64:
return "ARM64"
case 0x1c0:
return "ARM"
default:
return "Other"
}
}
func magicTypeToString(magic uint16) string {
switch magic {
case 0x10b:
return "PE32 (32-bit)"
case 0x20b:
return "PE32+ (64-bit)"
default:
return "Unknown/Invalid"
}
}
Code Breakdown
Note I am only explaining the logic that was added or significantly changed compared to our Manual Mapper.
New Structs/Constants
Structs
IMAGE_BASE_RELOCATION
struct: Added to define the layout of relocation block headers found in the .reloc
section. Contains VirtualAddress
(RVA of the page) and SizeOfBlock
.
Constants
IMAGE_DIRECTORY_ENTRY_BASERELOC
(5): Index for the relocation table in the Data Directory.
IMAGE_REL_BASED_DIR64
(10): Type indicating a 64-bit address requires patching by adding the delta.
IMAGE_REL_BASED_ABSOLUTE
(0): Type indicating a padding entry to be skipped.
Memory constants (MEM_COMMIT
, MEM_RESERVE
, MEM_RELEASE
, PAGE_READWRITE
, PAGE_EXECUTE_READWRITE
): Added explicitly for clarity, though they are available in the windows
package.
Imports
errors
, runtime
, syscall
packages were added/ensured for error handling, OS checks, and potential future syscall use.
Force Relocation by Occupying Preferred Base
- This is where we now force relocation by intentionally occupying the
ImageBase
. - We do this by retrieving
preferredImageBase
from the parsedoptionalHeader
. - We then call
windows.VirtualAlloc
specifically requesting a single page at thatpreferredImageBase
.
Step 2: Allocate Memory for DLL
- We’re still going to attempt
windows.VirtualAlloc
atpreferredImageBase
. - But of course now since we’ve occupied a page at the location, we expect it to fail
- Our existing fallback logic (
windows.VirtualAlloc
with 0 address) remains the same as before, but it should now execute, resulting inallocBase
being different frompreferredImageBase
.
Step 5: Process Base Relocations
This is where all the important base relocation logic we learned about in Theory 4.1 is now implemented:
- Calculate Delta: Calculates
delta = int64(allocBase) - int64(preferredImageBase)
. This difference drives the patching process. - Check Delta: Proceeds only if
delta
is non-zero. - Locate Relocation Directory: Accesses
optionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]
to get the RVA and Size of the relocation table. Handles cases where the directory doesn’t exist or is empty. - Calculate Table Addresses: Determines the start (
relocTableBase
) and end (relocTableEnd
) VAs of the relocation table within theallocBase
memory region. - Iterate Blocks: Enters a
for
loop that processes the table block by block (IMAGE_BASE_RELOCATION
).- Reads the
blockHeader
directly from memory atcurrentBlockAddr
. - Checks for a zero-sized block to terminate.
- Calculates the number of 16-bit entries (
numEntries
) in the current block. - Calculates the starting address (
entryPtr
) of the entries following the header.
- Reads the
- Iterate Entries: Enters an inner
for
loop iteratingnumEntries
times.- Reads the 16-bit
entry
value directly from memory. - Extracts the
relocType
(top 4 bits) andoffset
(bottom 12 bits).
- Reads the 16-bit
- Apply Fixup: Checks if
relocType == IMAGE_REL_BASED_DIR64
.- Calculates the absolute VA (
patchAddr
) withinallocBase
that needs patching (allocBase + blockRVA + offset
). - Uses
unsafe.Pointer
to castpatchAddr
to*uint64
to read theoriginalValue
directly from memory. - Calculates the
newValue
by adding thedelta
. - Writes the
newValue
back to the memory location using the*uint64
pointer. - Increments
totalFixups
. - Skips
IMAGE_REL_BASED_ABSOLUTE
and warns about other types.
- Calculates the absolute VA (
- Advance: Moves
currentBlockAddr
to the next block usingblockHeader.SizeOfBlock
.
Instructions
- Compile the base relocator.
GOOS=windows GOARCH=amd64 go build
- Then copy it over to target system and invoke from command-line, providing as argument the dll you’d like to analyze, for example
".\base_reloc.exe .\calc_dll.dll"
Results
PS C:\Users\vuilhond\Desktop> .\base_reloc.exe .\calc_dll.dll
[+] Starting Manual DLL Mapper (with Forced Relocation)...
[+] Reading file: .\calc_dll.dll
[+] Parsed PE Headers successfully.
[+] Target ImageBase: 0x26A5B0000
[+] Target SizeOfImage: 0x22000 (139264 bytes)
[+] Attempting to reserve preferred ImageBase (0x26A5B0000) to force relocation...
[+] Successfully allocated blocking memory at 0x26A5B0000. Relocation should be forced.
[+] Allocating 0x22000 bytes of memory for DLL...
[*] Failed to allocate at preferred base 0x26A5B0000 (EXPECTED): Attempt to access invalid address.. Trying arbitrary address...
[+] DLL memory allocated successfully at actual base address: 0x1D74D540000
[+] Copying PE headers (1536 bytes) to allocated memory...
[+] Copied 1536 bytes of headers successfully.
[+] Copying sections...
[*] Processing Section 0: '.text'
[*] Copying 6144 bytes from file offset 0x600 to VA 0x1D74D541000
[+] Copied section '.text' successfully (6144 bytes).
[*] Processing Section 1: '.data'
[*] Copying 512 bytes from file offset 0x1E00 to VA 0x1D74D543000
[+] Copied section '.data' successfully (512 bytes).
[*] Processing Section 2: '.rdata'
[*] Copying 1536 bytes from file offset 0x2000 to VA 0x1D74D544000
[+] Copied section '.rdata' successfully (1536 bytes).
[*] Processing Section 3: '.pdata'
[*] Copying 1024 bytes from file offset 0x2600 to VA 0x1D74D545000
[+] Copied section '.pdata' successfully (1024 bytes).
[*] Processing Section 4: '.xdata'
[*] Copying 512 bytes from file offset 0x2A00 to VA 0x1D74D546000
[+] Copied section '.xdata' successfully (512 bytes).
[*] Processing Section 5: '.bss'
[*] Skipping section '.bss' (SizeOfRawData is 0).
[*] Processing Section 6: '.edata'
[*] Copying 512 bytes from file offset 0x2C00 to VA 0x1D74D548000
[+] Copied section '.edata' successfully (512 bytes).
[*] Processing Section 7: '.idata'
[*] Copying 2048 bytes from file offset 0x2E00 to VA 0x1D74D549000
[+] Copied section '.idata' successfully (2048 bytes).
[*] Processing Section 8: '.tls'
[*] Copying 512 bytes from file offset 0x3600 to VA 0x1D74D54A000
[+] Copied section '.tls' successfully (512 bytes).
[*] Processing Section 9: '.reloc'
[*] Copying 512 bytes from file offset 0x3800 to VA 0x1D74D54B000
[+] Copied section '.reloc' successfully (512 bytes).
[*] Processing Section 10: '/4'
[*] Copying 1024 bytes from file offset 0x3A00 to VA 0x1D74D54C000
[+] Copied section '/4' successfully (1024 bytes).
[*] Processing Section 11: '/19'
[*] Copying 37888 bytes from file offset 0x3E00 to VA 0x1D74D54D000
[+] Copied section '/19' successfully (37888 bytes).
[*] Processing Section 12: '/31'
[*] Copying 6656 bytes from file offset 0xD200 to VA 0x1D74D557000
[+] Copied section '/31' successfully (6656 bytes).
[*] Processing Section 13: '/45'
[*] Copying 6656 bytes from file offset 0xEC00 to VA 0x1D74D559000
[+] Copied section '/45' successfully (6656 bytes).
[*] Processing Section 14: '/57'
[*] Copying 2560 bytes from file offset 0x10600 to VA 0x1D74D55B000
[+] Copied section '/57' successfully (2560 bytes).
[*] Processing Section 15: '/70'
[*] Copying 512 bytes from file offset 0x11000 to VA 0x1D74D55C000
[+] Copied section '/70' successfully (512 bytes).
[*] Processing Section 16: '/81'
[*] Copying 6144 bytes from file offset 0x11200 to VA 0x1D74D55D000
[+] Copied section '/81' successfully (6144 bytes).
[*] Processing Section 17: '/97'
[*] Copying 5120 bytes from file offset 0x12A00 to VA 0x1D74D55F000
[+] Copied section '/97' successfully (5120 bytes).
[*] Processing Section 18: '/113'
[*] Copying 512 bytes from file offset 0x13E00 to VA 0x1D74D561000
[+] Copied section '/113' successfully (512 bytes).
[+] All sections copied.
[+] Checking if base relocations are needed...
[+] Image loaded at non-preferred base (Delta: 0x1D4E2F90000). Processing relocations...
[+] Relocation Directory found at RVA 0xB000, Size 0x64
[+] Relocation processing complete. Total fixups applied: 41
[+] Manual mapping process complete (Headers, Sections copied, Relocations potentially applied).
[+] Self-Check Suggestion: Use a debugger (like Delve for Go or x64dbg for the process)
to inspect the memory at the allocated base address (0x%X). 2024226947072
Verify that the 'MZ' and 'PE' signatures are present at the start
and that data corresponding to sections appears at the correct RVAs.
If relocations occurred, check known absolute addresses (if any) were patched.
[+] Press Enter to free memory and exit.
[+] Mapper finished.
[+] Attempting to free main DLL allocation at 0x1D74D540000...
[+] Main DLL memory freed successfully.
[+] Freeing blocking allocation...
[+] Blocking allocation freed.
Discussion
Given that we’ve intentionally occupied ImageBase, the output is exactly what we expect:
- **
Successfully allocated blocking memory at 0x26A5B0000. Relocation should be forced.
- This confirms the preferred base was occupied. Failed to allocate at preferred base 0x26A5B0000 (EXPECTED): ... Trying arbitrary address...
followed byDLL memory allocated successfully at actual base address: 0x1D74D540000
- This shows the loader correctly fell back to an OS-chosen address.Image loaded at non-preferred base (Delta: 0x1D4E2F90000). Processing relocations...
- The non-zero delta correctly triggered the relocation code path.Relocation processing complete. Total fixups applied: 41
- The code successfully parsed the.reloc
section and applied the necessary patches.
Conclusion
Most excellent. Let’s move on ahead and get cracking with our IAT resolution.