351 lines
13 KiB
Java
351 lines
13 KiB
Java
// Script to find hardcoded addresses in the binary that need to be relocated
|
|
// @category _Reman3
|
|
// @menupath Reman3.Find and dump Relocations
|
|
|
|
import ghidra.app.script.GhidraScript;
|
|
import ghidra.program.model.listing.*;
|
|
import ghidra.program.model.address.*;
|
|
import ghidra.program.model.scalar.Scalar;
|
|
import ghidra.program.model.symbol.Reference;
|
|
import java.util.*;
|
|
import java.io.*;
|
|
import re3lib.RemanConfig;
|
|
import ghidra.util.task.TaskMonitor;
|
|
import ghidra.program.model.pcode.PcodeOp;
|
|
import ghidra.program.model.pcode.Varnode;
|
|
|
|
public class FindRelocations extends GhidraScript {
|
|
private Set<Address> foundRelocations = new HashSet<>();
|
|
private PrintWriter outputFile;
|
|
|
|
@Override
|
|
public void run() throws Exception {
|
|
RemanConfig.INSTANCE = new RemanConfig(this);
|
|
RemanConfig.INSTANCE.createDirectories();
|
|
|
|
// Create output file for relocations
|
|
File relocFile = new File(RemanConfig.INSTANCE.outputDir, "relocations.txt");
|
|
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);
|
|
|
|
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 analyzeInstruction(Instruction instruction) {
|
|
String mnemonic = instruction.getMnemonicString().toLowerCase();
|
|
|
|
// Use pcode analysis for better accuracy
|
|
if (isRelocatableInstruction(mnemonic)) {
|
|
analyzeInstructionPcode(instruction);
|
|
|
|
// Still check operands for direct absolute addresses
|
|
for (int i = 0; i < instruction.getNumOperands(); i++) {
|
|
analyzeOperand(instruction, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void analyzeInstructionPcode(Instruction instruction) {
|
|
try {
|
|
PcodeOp[] pcode = instruction.getPcode();
|
|
|
|
for (PcodeOp op : pcode) {
|
|
// Look for operations that use absolute addresses
|
|
switch (op.getOpcode()) {
|
|
case PcodeOp.LOAD:
|
|
case PcodeOp.STORE:
|
|
// Memory operations - check if using absolute address
|
|
analyzeMemoryOperation(instruction, op);
|
|
break;
|
|
|
|
case PcodeOp.CALL:
|
|
case PcodeOp.CALLIND:
|
|
// Call operations - check if target is absolute
|
|
analyzeCallOperation(instruction, op);
|
|
break;
|
|
|
|
case PcodeOp.BRANCH:
|
|
case PcodeOp.BRANCHIND:
|
|
// Branch operations - usually relative, but check indirect branches
|
|
analyzeBranchOperation(instruction, op);
|
|
break;
|
|
|
|
case PcodeOp.COPY:
|
|
// Copy operations that move absolute addresses
|
|
analyzeCopyOperation(instruction, op);
|
|
break;
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
// Fallback to reference analysis if pcode fails
|
|
analyzeReferences(instruction);
|
|
}
|
|
}
|
|
|
|
private void analyzeMemoryOperation(Instruction instruction, PcodeOp op) {
|
|
// LOAD/STORE operations with absolute addresses
|
|
Varnode input = op.getInput(1); // Address input
|
|
if (input != null && input.isConstant()) {
|
|
long addr = input.getOffset();
|
|
if (looksLikeAddress(addr)) {
|
|
try {
|
|
Address targetAddr = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(addr);
|
|
if (isInMainMemorySpace(targetAddr)) {
|
|
recordRelocation(instruction.getAddress(), targetAddr,
|
|
instruction.getMnemonicString(), "memory_op");
|
|
}
|
|
} catch (Exception e) {
|
|
// Invalid address
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void analyzeCallOperation(Instruction instruction, PcodeOp op) {
|
|
// Only flag indirect calls or calls with absolute targets
|
|
if (op.getOpcode() == PcodeOp.CALLIND) {
|
|
// Indirect call - check if target address is absolute
|
|
Varnode target = op.getInput(0);
|
|
if (target != null && target.isConstant()) {
|
|
long addr = target.getOffset();
|
|
if (looksLikeAddress(addr)) {
|
|
try {
|
|
Address targetAddr = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(addr);
|
|
if (isInMainMemorySpace(targetAddr)) {
|
|
recordRelocation(instruction.getAddress(), targetAddr,
|
|
instruction.getMnemonicString(), "indirect_call");
|
|
}
|
|
} catch (Exception e) {
|
|
// Invalid address
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Direct calls are usually relative, skip them
|
|
}
|
|
|
|
private void analyzeBranchOperation(Instruction instruction, PcodeOp op) {
|
|
// Only flag indirect branches (direct branches are relative)
|
|
if (op.getOpcode() == PcodeOp.BRANCHIND) {
|
|
Varnode target = op.getInput(0);
|
|
if (target != null && target.isConstant()) {
|
|
long addr = target.getOffset();
|
|
if (looksLikeAddress(addr)) {
|
|
try {
|
|
Address targetAddr = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(addr);
|
|
if (isInMainMemorySpace(targetAddr)) {
|
|
recordRelocation(instruction.getAddress(), targetAddr,
|
|
instruction.getMnemonicString(), "indirect_branch");
|
|
}
|
|
} catch (Exception e) {
|
|
// Invalid address
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Direct branches (BRANCH) are relative, ignore them
|
|
}
|
|
|
|
private void analyzeCopyOperation(Instruction instruction, PcodeOp op) {
|
|
// COPY operations that load absolute addresses (like LEA or MOV immediate)
|
|
Varnode input = op.getInput(0);
|
|
if (input != null && input.isConstant()) {
|
|
long addr = input.getOffset();
|
|
if (looksLikeAddress(addr)) {
|
|
try {
|
|
Address targetAddr = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(addr);
|
|
if (isInMainMemorySpace(targetAddr)) {
|
|
recordRelocation(instruction.getAddress(), targetAddr,
|
|
instruction.getMnemonicString(), "load_address");
|
|
}
|
|
} catch (Exception e) {
|
|
// Invalid address
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void analyzeReferences(Instruction instruction) {
|
|
// Fallback method - but be more selective
|
|
Reference[] refs = instruction.getReferencesFrom();
|
|
for (Reference ref : refs) {
|
|
if (ref.getReferenceType().isData()) {
|
|
// Data references are usually absolute
|
|
Address toAddr = ref.getToAddress();
|
|
if (isInMainMemorySpace(toAddr)) {
|
|
recordRelocation(instruction.getAddress(), toAddr,
|
|
instruction.getMnemonicString(), "data_reference");
|
|
}
|
|
}
|
|
// Skip flow references (calls/jumps) as they're usually relative
|
|
}
|
|
}
|
|
|
|
private boolean isRelocatableInstruction(String mnemonic) {
|
|
// Instructions that commonly use absolute addresses
|
|
return 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"); // conditional jumps
|
|
}
|
|
|
|
private void analyzeOperand(Instruction instruction, int opIndex) {
|
|
Object[] operandObjects = instruction.getOpObjects(opIndex);
|
|
|
|
for (Object obj : operandObjects) {
|
|
if (obj instanceof Address) {
|
|
Address addr = (Address) obj;
|
|
if (isInMainMemorySpace(addr)) {
|
|
recordRelocation(instruction.getAddress(), addr,
|
|
instruction.getMnemonicString(), "operand_" + opIndex);
|
|
}
|
|
} else if (obj instanceof Scalar) {
|
|
Scalar scalar = (Scalar) obj;
|
|
// Check if scalar value looks like an address in our memory space
|
|
long value = scalar.getUnsignedValue();
|
|
if (looksLikeAddress(value)) {
|
|
try {
|
|
Address addr = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(value);
|
|
if (isInMainMemorySpace(addr)) {
|
|
recordRelocation(instruction.getAddress(), addr,
|
|
instruction.getMnemonicString(), "scalar_" + opIndex);
|
|
}
|
|
} catch (Exception e) {
|
|
// Invalid address, ignore
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void analyzeDataReferences(long instructionsProcessed, long totalWork) {
|
|
// Check data sections for absolute addresses
|
|
AddressSetView dataAddresses = currentProgram.getMemory().getLoadedAndInitializedAddressSet();
|
|
long totalDataBytes = dataAddresses.getNumAddresses();
|
|
long processedBytes = 0;
|
|
int pointerSize = currentProgram.getDefaultPointerSize();
|
|
|
|
for (AddressRange range : dataAddresses) {
|
|
if (monitor.isCancelled()) {
|
|
println("Operation cancelled by user");
|
|
return;
|
|
}
|
|
|
|
Address addr = range.getMinAddress();
|
|
while (addr != null && addr.compareTo(range.getMaxAddress()) <= 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");
|
|
}
|
|
} catch (Exception e) {
|
|
// Invalid address, ignore
|
|
}
|
|
}
|
|
|
|
addr = addr.add(pointerSize); // Jump by pointer size for efficiency
|
|
processedBytes += pointerSize;
|
|
|
|
// Update progress every 10000 bytes
|
|
if (processedBytes % 10000 == 0) {
|
|
long currentProgress = instructionsProcessed + (processedBytes / pointerSize);
|
|
monitor.setProgress(currentProgress);
|
|
monitor.setMessage("Analyzing data: " + (processedBytes * 100 / totalDataBytes) + "% complete");
|
|
}
|
|
|
|
} 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 >= 0x400000 && value <= 0x7FFFFFFF; // Typical executable range
|
|
}
|
|
|
|
private void recordRelocation(Address fromAddr, Address toAddr, String instruction, String type) {
|
|
if (foundRelocations.add(toAddr)) {
|
|
String line = String.format("0x%s -> 0x%s (%s) [%s]",
|
|
fromAddr.toString(),
|
|
toAddr.toString(),
|
|
instruction,
|
|
type);
|
|
println(line);
|
|
outputFile.println(line);
|
|
outputFile.flush();
|
|
}
|
|
}
|
|
}
|