reman3/GhidraScripts/FindRelocations.java

449 lines
16 KiB
Java

// Script to find hardcoded addresses in the binary that need to be relocated
// @category _Reman3
// @menupath Reman3.Find and dump Relocations
// @importpackage org.sqlite
import ghidra.app.script.GhidraScript;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.address.*;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.pcode.*;
import java.util.*;
import java.io.*;
import re3lib.RemanConfig;
public class FindRelocations extends GhidraScript {
private Set<String> foundRelocations = new HashSet<>();
private PrintWriter outputFile;
long addrMin, addrMax;
private void analyzeInstruction(Instruction instruction) {
String mnemonic = instruction.getMnemonicString().toLowerCase();
boolean trace = instruction.getAddress().getOffset() == 0x004ac0f0;
if (trace) {
println("DEBUG: " + mnemonic + " at " + instruction.getAddress());
}
// Check for instructions that commonly use absolute addresses
if (isRelocatableInstruction(mnemonic)) {
// Always run both analyses to catch all cases
// Pcode analysis is better for complex addressing, reference analysis catches
// simple cases
Set<String> foundAddresses = new HashSet<>();
analyzePcodeForConstants(instruction, trace, foundAddresses);
analyzeReferences(instruction, trace, foundAddresses);
}
}
@Override
public void run() throws Exception {
RemanConfig.INSTANCE = new RemanConfig(this);
RemanConfig.INSTANCE.createDirectories();
addrMin = RemanConfig.INSTANCE.staticMemoryBlockStart.getOffset();
addrMax = RemanConfig.INSTANCE.staticMemoryBlockEnd.getOffset();
// Create output file for relocations
File relocFile = new File(RemanConfig.INSTANCE.outputDir, "relocations.def");
outputFile = new PrintWriter(new FileWriter(relocFile));
try {
println("Counting instructions and data for progress tracking...");
// Count total work for progress monitoring
long totalInstructions = currentProgram.getListing().getNumInstructions();
long totalDataBytes = currentProgram.getMemory().getLoadedAndInitializedAddressSet().getNumAddresses();
long totalWork = totalInstructions + (totalDataBytes / currentProgram.getDefaultPointerSize());
monitor.initialize(totalWork);
monitor.setMessage("Scanning for relocations...");
println("Scanning " + totalInstructions + " instructions and " + totalDataBytes + " data bytes...");
// Get all instructions in the program
InstructionIterator instructions = currentProgram.getListing().getInstructions(true);
long processedInstructions = 0;
while (instructions.hasNext()) {
if (monitor.isCancelled()) {
println("Operation cancelled by user");
return;
}
Instruction instruction = instructions.next();
analyzeInstruction(instruction);
// Check if we've gone past the end address
if (instruction.getAddress().getOffset() >= 0x00844190) {
break;
}
processedInstructions++;
if (processedInstructions % 1000 == 0) {
monitor.setProgress(processedInstructions);
monitor.setMessage("Processed " + processedInstructions + "/" + totalInstructions + " instructions...");
}
}
monitor.setProgress(totalInstructions);
monitor.setMessage("Analyzing data sections...");
// Also check data references
analyzeDataReferences(totalInstructions, totalWork);
monitor.setMessage("Scan complete");
println("Found " + foundRelocations.size() + " potential relocations");
println("Results saved to: " + relocFile.getAbsolutePath());
} finally {
if (outputFile != null) {
outputFile.close();
}
}
}
private void analyzePcodeForConstants(Instruction instruction, boolean trace, Set<String> foundAddresses) {
try {
// Get the pcode operations for this instruction
PcodeOp[] pcodeOps = instruction.getPcode();
if (trace) {
println("DEBUG: Analyzing pcode for " + instruction.getAddress());
for (PcodeOp op : pcodeOps) {
println("DEBUG: PcodeOp: " + op.toString());
}
}
for (PcodeOp op : pcodeOps) {
// Look for constants in the pcode that look like addresses
Varnode[] inputs = op.getInputs();
for (Varnode input : inputs) {
if (input.isConstant()) {
long constantValue = input.getOffset();
// Check if this constant looks like an address in our target range
if (looksLikeAddress(constantValue)) {
if (trace) {
println("DEBUG: Found constant: 0x" + Long.toHexString(constantValue) + " that looks like an address");
}
try {
Address targetAddr = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(constantValue);
if (isInMainMemorySpace(targetAddr)) {
String addrKey = targetAddr.toString();
if (!foundAddresses.contains(addrKey)) {
// Find where this constant appears in the instruction bytes
int operandOffset = findAbsoluteAddressOffset(instruction, targetAddr, trace);
if (operandOffset >= 0) {
recordRelocation(instruction.getAddress(), targetAddr, instruction.getMnemonicString(),
"pcode_constant", operandOffset);
foundAddresses.add(addrKey);
}
}
}
} catch (Exception e) {
if (trace) {
println("DEBUG: Invalid address from constant: " + e.getMessage());
}
}
}
}
// Also check for address space references (like *[ram]0x77d4c8)
else if (input.isAddress() && input.getAddress() != null) {
long addressValue = input.getAddress().getOffset();
if (trace) {
println("DEBUG: Found address varnode: 0x" + Long.toHexString(addressValue));
}
if (looksLikeAddress(addressValue)) {
try {
Address targetAddr = input.getAddress();
if (isInMainMemorySpace(targetAddr)) {
String addrKey = targetAddr.toString();
if (!foundAddresses.contains(addrKey)) {
int operandOffset = findAbsoluteAddressOffset(instruction, targetAddr, trace);
if (operandOffset >= 0) {
recordRelocation(instruction.getAddress(), targetAddr, instruction.getMnemonicString(),
"pcode_address", operandOffset);
foundAddresses.add(addrKey);
}
}
}
} catch (Exception e) {
if (trace) {
println("DEBUG: Invalid address from varnode: " + e.getMessage());
}
}
}
}
}
}
} catch (Exception e) {
if (trace) {
println("DEBUG: Error analyzing pcode: " + e.getMessage());
}
}
}
private void analyzeReferences(Instruction instruction, boolean trace, Set<String> foundAddresses) {
// Check references from this instruction (original approach)
Reference[] refs = instruction.getReferencesFrom();
for (Reference ref : refs) {
Address toAddr = ref.getToAddress();
boolean isInMainMemorySpace = isInMainMemorySpace(toAddr);
if (trace) {
println("DEBUG: Reference to " + toAddr + " isInMainMemorySpace: " + isInMainMemorySpace);
}
if (isInMainMemorySpace) {
String addrKey = toAddr.toString();
if (!foundAddresses.contains(addrKey)) {
// Check if the target address appears in the instruction bytes (absolute
// addressing)
int operandOffset = findAbsoluteAddressOffset(instruction, toAddr, trace);
if (operandOffset >= 0) {
recordRelocation(instruction.getAddress(), toAddr, instruction.getMnemonicString(),
"reference_" + ref.getReferenceType().getName(), operandOffset);
foundAddresses.add(addrKey);
} else {
if (trace) {
println("DEBUG: operandOffset was not found: " + operandOffset);
}
}
}
}
}
}
private int findAbsoluteAddressOffset(Instruction instruction, Address targetAddr, boolean trace) {
try {
byte[] instructionBytes = instruction.getBytes();
long targetValue = targetAddr.getOffset();
// Convert target address to little-endian byte array (x86 32-bit)
byte[] targetBytes = new byte[4];
targetBytes[0] = (byte) (targetValue & 0xFF);
targetBytes[1] = (byte) ((targetValue >> 8) & 0xFF);
targetBytes[2] = (byte) ((targetValue >> 16) & 0xFF);
targetBytes[3] = (byte) ((targetValue >> 24) & 0xFF);
// Search for the target address bytes in the instruction and return offset
int offset = findSequenceOffset(instructionBytes, targetBytes, trace);
return offset;
} catch (Exception e) {
if (trace) {
println("findAbsoluteAddressOffset exception: " + e.getMessage());
}
return -1;
}
}
private String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
if (i > 0)
sb.append(" ");
sb.append(String.format("%02x", bytes[i] & 0xFF));
}
return sb.toString();
}
private int findSequenceOffset(byte[] haystack, byte[] needle, boolean trace) {
if (trace) {
println("findSequenceOffset inspected bytes: " + bytesToHex(haystack) + " (length: " + haystack.length + ")");
}
if (needle.length > haystack.length) {
if (trace) {
println("findSequenceOffset needle.length > haystack.length");
}
return -1;
}
for (int i = 0; i <= haystack.length - needle.length; i++) {
boolean found = true;
for (int j = 0; j < needle.length; j++) {
if (haystack[i + j] != needle[j]) {
found = false;
break;
}
}
if (found) {
return i; // Return the offset where the sequence starts
}
}
return -1;
}
private boolean isRelocatableInstruction(String mnemonic) {
// Instructions that commonly use absolute addresses
return true;
// mnemonic.equals("mov") || mnemonic.equals("lea") ||
// mnemonic.equals("call") || mnemonic.equals("jmp") ||
// mnemonic.equals("push") || mnemonic.equals("cmp") ||
// mnemonic.equals("test") || mnemonic.equals("add") ||
// mnemonic.equals("sub") || mnemonic.equals("and") ||
// mnemonic.equals("or") || mnemonic.equals("xor") ||
// mnemonic.startsWith("j"); // All jumps, we'll filter by byte analysis
}
private void analyzeDataReferences(long instructionsProcessed, long totalWork) {
// Only scan actual data sections, not code sections
MemoryBlock[] blocks = currentProgram.getMemory().getBlocks();
long processedBytes = 0;
int pointerSize = currentProgram.getDefaultPointerSize();
for (MemoryBlock block : blocks) {
// Skip executable blocks (code sections)
if (block.isExecute()) {
continue;
}
// Only scan initialized data blocks
if (!block.isInitialized()) {
continue;
}
if (monitor.isCancelled()) {
println("Operation cancelled by user");
return;
}
if (block.getName().equals(".rsrc")) {
continue;
}
println("Scanning data block: " + block.getName() + " (" + block.getStart() + " - " + block.getEnd() + ")");
Address addr = block.getStart();
while (addr != null && addr.compareTo(block.getEnd()) <= 0) {
try {
// Check if this location contains a pointer-sized value
byte[] bytes = new byte[pointerSize];
currentProgram.getMemory().getBytes(addr, bytes);
long value = 0;
for (int i = 0; i < bytes.length; i++) {
value |= ((long) (bytes[i] & 0xFF)) << (i * 8);
}
if (looksLikeAddress(value)) {
try {
Address targetAddr = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(value);
if (isInMainMemorySpace(targetAddr)) {
recordRelocation(addr, targetAddr, "data", "pointer", 0);
}
} catch (Exception e) {
// Invalid address, ignore
}
}
addr = addr.add(pointerSize);
processedBytes += pointerSize;
} catch (Exception e) {
addr = addr.add(1);
processedBytes++;
}
}
}
}
private boolean isInMainMemorySpace(Address addr) {
if (addr == null)
return false;
// Check if address is in a loaded memory block
return currentProgram.getMemory().contains(addr) &&
!addr.getAddressSpace().isOverlaySpace();
}
private boolean looksLikeAddress(long value) {
// Heuristic: check if value is in reasonable address range
// Adjust these ranges based on your target architecture
return value >= addrMin
&& value <= addrMax; // Typical executable range
}
private void recordRelocation(Address fromAddr, Address toAddr, String instruction, String type, int operandOffset) {
String relocKey = fromAddr.toString() + " -> " + toAddr.toString();
if (foundRelocations.add(relocKey)) {
String instructionBytes = getInstructionBytesString(fromAddr);
Address operandPtr = fromAddr.add(operandOffset);
String line = String.format("REL(0x%s, 0x%s, 0x%s) // %s [%s] | %s",
fromAddr.toString(),
operandPtr.toString(),
toAddr.toString(),
instruction,
type,
instructionBytes);
outputFile.println(line);
outputFile.flush();
}
}
private String getInstructionBytesString(Address addr) {
try {
Instruction instruction = currentProgram.getListing().getInstructionAt(addr);
if (instruction != null) {
byte[] bytes = instruction.getBytes();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
if (i > 0)
sb.append(" ");
sb.append(String.format("%02x", bytes[i] & 0xFF));
}
return sb.toString();
} else {
// This is a data reference, show the pointer bytes
byte[] bytes = new byte[4]; // Show 4 bytes for data
int bytesRead = currentProgram.getMemory().getBytes(addr, bytes);
if (bytesRead > 0) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytesRead; i++) {
if (i > 0)
sb.append(" ");
sb.append(String.format("%02x", bytes[i] & 0xFF));
}
return sb.toString();
}
}
} catch (Exception e) {
// Check if this is in an initialized memory block
if (currentProgram.getMemory().contains(addr)) {
return "unreadable";
}
}
return "??";
}
private boolean isPartOfFunction(Address addr) {
// Check if there's an instruction at this address
Instruction instruction = currentProgram.getListing().getInstructionAt(addr);
if (instruction != null) {
return true;
}
// Check if this address is within any function's body
Function function = currentProgram.getFunctionManager().getFunctionContaining(addr);
if (function != null) {
// Additional check: make sure we're in the function body, not just data
// referenced by it
AddressSetView functionBody = function.getBody();
return functionBody.contains(addr);
}
return false;
}
}