This commit is contained in:
Guus Waals 2025-05-30 14:36:39 +08:00
parent faf2e134f2
commit f1b346fb0e
1 changed files with 71 additions and 63 deletions

View File

@ -10,81 +10,85 @@ import ghidra.program.model.symbol.Reference;
import java.util.*; import java.util.*;
import java.io.*; import java.io.*;
import re3lib.RemanConfig; import re3lib.RemanConfig;
import ghidra.util.task.TaskMonitor;
public class FindRelocations extends GhidraScript { public class FindRelocations extends GhidraScript {
private Set<Address> foundRelocations = new HashSet<>(); private Set<Address> foundRelocations = new HashSet<>();
private PrintWriter outputFile; private PrintWriter outputFile;
long addrMin, addrMax;
@Override @Override
public void run() throws Exception { public void run() throws Exception {
RemanConfig.INSTANCE = new RemanConfig(this); RemanConfig.INSTANCE = new RemanConfig(this);
RemanConfig.INSTANCE.createDirectories(); RemanConfig.INSTANCE.createDirectories();
addrMin = RemanConfig.INSTANCE.staticMemoryBlockStart.getOffset();
addrMax = RemanConfig.INSTANCE.staticMemoryBlockEnd.getOffset();
// Create output file for relocations // Create output file for relocations
File relocFile = new File(RemanConfig.INSTANCE.outputDir, "relocations.txt"); File relocFile = new File(RemanConfig.INSTANCE.outputDir, "relocations.txt");
outputFile = new PrintWriter(new FileWriter(relocFile)); outputFile = new PrintWriter(new FileWriter(relocFile));
try { try {
println("Counting instructions and data for progress tracking..."); println("Counting instructions and data for progress tracking...");
// Count total work for progress monitoring // Count total work for progress monitoring
long totalInstructions = currentProgram.getListing().getNumInstructions(); long totalInstructions = currentProgram.getListing().getNumInstructions();
long totalDataBytes = currentProgram.getMemory().getLoadedAndInitializedAddressSet().getNumAddresses(); long totalDataBytes = currentProgram.getMemory().getLoadedAndInitializedAddressSet().getNumAddresses();
long totalWork = totalInstructions + (totalDataBytes / currentProgram.getDefaultPointerSize()); long totalWork = totalInstructions + (totalDataBytes / currentProgram.getDefaultPointerSize());
monitor.initialize(totalWork); monitor.initialize(totalWork);
monitor.setMessage("Scanning for relocations..."); monitor.setMessage("Scanning for relocations...");
println("Scanning " + totalInstructions + " instructions and " + totalDataBytes + " data bytes..."); println("Scanning " + totalInstructions + " instructions and " + totalDataBytes + " data bytes...");
// Get all instructions in the program // Get all instructions in the program
InstructionIterator instructions = currentProgram.getListing().getInstructions(true); InstructionIterator instructions = currentProgram.getListing().getInstructions(true);
long processedInstructions = 0; long processedInstructions = 0;
while (instructions.hasNext()) { while (instructions.hasNext()) {
if (monitor.isCancelled()) { if (monitor.isCancelled()) {
println("Operation cancelled by user"); println("Operation cancelled by user");
return; return;
} }
Instruction instruction = instructions.next(); Instruction instruction = instructions.next();
analyzeInstruction(instruction); analyzeInstruction(instruction);
processedInstructions++; processedInstructions++;
if (processedInstructions % 1000 == 0) { if (processedInstructions % 1000 == 0) {
monitor.setProgress(processedInstructions); monitor.setProgress(processedInstructions);
monitor.setMessage("Processed " + processedInstructions + "/" + totalInstructions + " instructions..."); monitor.setMessage("Processed " + processedInstructions + "/" + totalInstructions + " instructions...");
} }
} }
monitor.setProgress(totalInstructions); monitor.setProgress(totalInstructions);
monitor.setMessage("Analyzing data sections..."); monitor.setMessage("Analyzing data sections...");
// Also check data references // Also check data references
analyzeDataReferences(totalInstructions, totalWork); analyzeDataReferences(totalInstructions, totalWork);
monitor.setMessage("Scan complete"); monitor.setMessage("Scan complete");
println("Found " + foundRelocations.size() + " potential relocations"); println("Found " + foundRelocations.size() + " potential relocations");
println("Results saved to: " + relocFile.getAbsolutePath()); println("Results saved to: " + relocFile.getAbsolutePath());
} finally { } finally {
if (outputFile != null) { if (outputFile != null) {
outputFile.close(); outputFile.close();
} }
} }
} }
private void analyzeInstruction(Instruction instruction) { private void analyzeInstruction(Instruction instruction) {
String mnemonic = instruction.getMnemonicString().toLowerCase(); String mnemonic = instruction.getMnemonicString().toLowerCase();
// Check for instructions that commonly use absolute addresses // Check for instructions that commonly use absolute addresses
if (isRelocatableInstruction(mnemonic)) { if (isRelocatableInstruction(mnemonic)) {
// Check operands for absolute addresses // Check operands for absolute addresses
for (int i = 0; i < instruction.getNumOperands(); i++) { for (int i = 0; i < instruction.getNumOperands(); i++) {
analyzeOperand(instruction, i); analyzeOperand(instruction, i);
} }
// Check references from this instruction - but filter out relative references // Check references from this instruction - but filter out relative references
Reference[] refs = instruction.getReferencesFrom(); Reference[] refs = instruction.getReferencesFrom();
for (Reference ref : refs) { for (Reference ref : refs) {
@ -107,27 +111,27 @@ public class FindRelocations extends GhidraScript {
} }
} }
} }
private boolean isRelocatableInstruction(String mnemonic) { private boolean isRelocatableInstruction(String mnemonic) {
// Instructions that commonly use absolute addresses // Instructions that commonly use absolute addresses
return mnemonic.equals("mov") || mnemonic.equals("lea") || return mnemonic.equals("mov") || mnemonic.equals("lea") ||
mnemonic.equals("call") || mnemonic.equals("jmp") || mnemonic.equals("call") || mnemonic.equals("jmp") ||
mnemonic.equals("push") || mnemonic.equals("cmp") || mnemonic.equals("push") || mnemonic.equals("cmp") ||
mnemonic.equals("test") || mnemonic.equals("add") || mnemonic.equals("test") || mnemonic.equals("add") ||
mnemonic.equals("sub") || mnemonic.equals("and") || mnemonic.equals("sub") || mnemonic.equals("and") ||
mnemonic.equals("or") || mnemonic.equals("xor") || mnemonic.equals("or") || mnemonic.equals("xor") ||
mnemonic.startsWith("j"); // conditional jumps mnemonic.startsWith("j"); // conditional jumps
} }
private void analyzeOperand(Instruction instruction, int opIndex) { private void analyzeOperand(Instruction instruction, int opIndex) {
Object[] operandObjects = instruction.getOpObjects(opIndex); Object[] operandObjects = instruction.getOpObjects(opIndex);
for (Object obj : operandObjects) { for (Object obj : operandObjects) {
if (obj instanceof Address) { if (obj instanceof Address) {
Address addr = (Address) obj; Address addr = (Address) obj;
if (isInMainMemorySpace(addr)) { if (isInMainMemorySpace(addr)) {
recordRelocation(instruction.getAddress(), addr, recordRelocation(instruction.getAddress(), addr,
instruction.getMnemonicString(), "operand_" + opIndex); instruction.getMnemonicString(), "operand_" + opIndex);
} }
} else if (obj instanceof Scalar) { } else if (obj instanceof Scalar) {
Scalar scalar = (Scalar) obj; Scalar scalar = (Scalar) obj;
@ -137,8 +141,8 @@ public class FindRelocations extends GhidraScript {
try { try {
Address addr = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(value); Address addr = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(value);
if (isInMainMemorySpace(addr)) { if (isInMainMemorySpace(addr)) {
recordRelocation(instruction.getAddress(), addr, recordRelocation(instruction.getAddress(), addr,
instruction.getMnemonicString(), "scalar_" + opIndex); instruction.getMnemonicString(), "scalar_" + opIndex);
} }
} catch (Exception e) { } catch (Exception e) {
// Invalid address, ignore // Invalid address, ignore
@ -147,32 +151,32 @@ public class FindRelocations extends GhidraScript {
} }
} }
} }
private void analyzeDataReferences(long instructionsProcessed, long totalWork) { private void analyzeDataReferences(long instructionsProcessed, long totalWork) {
// Check data sections for absolute addresses // Check data sections for absolute addresses
AddressSetView dataAddresses = currentProgram.getMemory().getLoadedAndInitializedAddressSet(); AddressSetView dataAddresses = currentProgram.getMemory().getLoadedAndInitializedAddressSet();
long totalDataBytes = dataAddresses.getNumAddresses(); long totalDataBytes = dataAddresses.getNumAddresses();
long processedBytes = 0; long processedBytes = 0;
int pointerSize = currentProgram.getDefaultPointerSize(); int pointerSize = currentProgram.getDefaultPointerSize();
for (AddressRange range : dataAddresses) { for (AddressRange range : dataAddresses) {
if (monitor.isCancelled()) { if (monitor.isCancelled()) {
println("Operation cancelled by user"); println("Operation cancelled by user");
return; return;
} }
Address addr = range.getMinAddress(); Address addr = range.getMinAddress();
while (addr != null && addr.compareTo(range.getMaxAddress()) <= 0) { while (addr != null && addr.compareTo(range.getMaxAddress()) <= 0) {
try { try {
// Check if this location contains a pointer-sized value // Check if this location contains a pointer-sized value
byte[] bytes = new byte[pointerSize]; byte[] bytes = new byte[pointerSize];
currentProgram.getMemory().getBytes(addr, bytes); currentProgram.getMemory().getBytes(addr, bytes);
long value = 0; long value = 0;
for (int i = 0; i < bytes.length; i++) { for (int i = 0; i < bytes.length; i++) {
value |= ((long)(bytes[i] & 0xFF)) << (i * 8); value |= ((long) (bytes[i] & 0xFF)) << (i * 8);
} }
if (looksLikeAddress(value)) { if (looksLikeAddress(value)) {
try { try {
Address targetAddr = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(value); Address targetAddr = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(value);
@ -183,17 +187,17 @@ public class FindRelocations extends GhidraScript {
// Invalid address, ignore // Invalid address, ignore
} }
} }
addr = addr.add(pointerSize); // Jump by pointer size for efficiency addr = addr.add(pointerSize); // Jump by pointer size for efficiency
processedBytes += pointerSize; processedBytes += pointerSize;
// Update progress every 10000 bytes // Update progress every 10000 bytes
if (processedBytes % 10000 == 0) { if (processedBytes % 10000 == 0) {
long currentProgress = instructionsProcessed + (processedBytes / pointerSize); long currentProgress = instructionsProcessed + (processedBytes / pointerSize);
monitor.setProgress(currentProgress); monitor.setProgress(currentProgress);
monitor.setMessage("Analyzing data: " + (processedBytes * 100 / totalDataBytes) + "% complete"); monitor.setMessage("Analyzing data: " + (processedBytes * 100 / totalDataBytes) + "% complete");
} }
} catch (Exception e) { } catch (Exception e) {
addr = addr.add(1); addr = addr.add(1);
processedBytes++; processedBytes++;
@ -201,61 +205,63 @@ public class FindRelocations extends GhidraScript {
} }
} }
} }
private boolean isInMainMemorySpace(Address addr) { private boolean isInMainMemorySpace(Address addr) {
if (addr == null) return false; if (addr == null)
return false;
// Check if address is in a loaded memory block // Check if address is in a loaded memory block
return currentProgram.getMemory().contains(addr) && return currentProgram.getMemory().contains(addr) &&
!addr.getAddressSpace().isOverlaySpace(); !addr.getAddressSpace().isOverlaySpace();
} }
private boolean looksLikeAddress(long value) { private boolean looksLikeAddress(long value) {
// Heuristic: check if value is in reasonable address range // Heuristic: check if value is in reasonable address range
// Adjust these ranges based on your target architecture // Adjust these ranges based on your target architecture
return value >= 0x400000 && value <= 0x7FFFFFFF; // Typical executable range return value >= addrMin
&& value <= addrMax; // Typical executable range
} }
private boolean usesAbsoluteAddressing(Instruction instruction, Reference ref) { private boolean usesAbsoluteAddressing(Instruction instruction, Reference ref) {
// Check the instruction bytes to determine addressing mode // Check the instruction bytes to determine addressing mode
String mnemonic = instruction.getMnemonicString().toLowerCase(); String mnemonic = instruction.getMnemonicString().toLowerCase();
// For x86, most conditional jumps (JA, JE, JNE, etc.) use relative addressing // For x86, most conditional jumps (JA, JE, JNE, etc.) use relative addressing
if (mnemonic.startsWith("j") && !mnemonic.equals("jmp")) { if (mnemonic.startsWith("j") && !mnemonic.equals("jmp")) {
return false; // Conditional jumps are typically relative return false; // Conditional jumps are typically relative
} }
// For JMP and CALL, check the operand representation // For JMP and CALL, check the operand representation
for (int i = 0; i < instruction.getNumOperands(); i++) { for (int i = 0; i < instruction.getNumOperands(); i++) {
String operandStr = instruction.getDefaultOperandRepresentation(i); String operandStr = instruction.getDefaultOperandRepresentation(i);
// If operand shows as a direct address (not offset), it might be absolute // If operand shows as a direct address (not offset), it might be absolute
// But we need to be more sophisticated here... // But we need to be more sophisticated here...
// Check if this is an indirect reference [address] which would be absolute // Check if this is an indirect reference [address] which would be absolute
if (operandStr.contains("[") && operandStr.contains("]")) { if (operandStr.contains("[") && operandStr.contains("]")) {
return true; // Indirect addressing typically uses absolute addresses return true; // Indirect addressing typically uses absolute addresses
} }
} }
// For now, assume most jumps/calls are relative unless proven otherwise // For now, assume most jumps/calls are relative unless proven otherwise
return false; return false;
} }
private void recordRelocation(Address fromAddr, Address toAddr, String instruction, String type) { private void recordRelocation(Address fromAddr, Address toAddr, String instruction, String type) {
if (foundRelocations.add(toAddr)) { if (foundRelocations.add(toAddr)) {
String instructionBytes = getInstructionBytesString(fromAddr); String instructionBytes = getInstructionBytesString(fromAddr);
String line = String.format("0x%s -> 0x%s (%s) [%s] | %s", String line = String.format("0x%s -> 0x%s (%s) [%s] | %s",
fromAddr.toString(), fromAddr.toString(),
toAddr.toString(), toAddr.toString(),
instruction, instruction,
type, type,
instructionBytes); instructionBytes);
println(line); println(line);
outputFile.println(line); outputFile.println(line);
outputFile.flush(); outputFile.flush();
} }
} }
private String getInstructionBytesString(Address addr) { private String getInstructionBytesString(Address addr) {
try { try {
Instruction instruction = currentProgram.getListing().getInstructionAt(addr); Instruction instruction = currentProgram.getListing().getInstructionAt(addr);
@ -263,7 +269,8 @@ public class FindRelocations extends GhidraScript {
byte[] bytes = instruction.getBytes(); byte[] bytes = instruction.getBytes();
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) { for (int i = 0; i < bytes.length; i++) {
if (i > 0) sb.append(" "); if (i > 0)
sb.append(" ");
sb.append(String.format("%02x", bytes[i] & 0xFF)); sb.append(String.format("%02x", bytes[i] & 0xFF));
} }
return sb.toString(); return sb.toString();
@ -275,7 +282,8 @@ public class FindRelocations extends GhidraScript {
currentProgram.getMemory().getBytes(addr, bytes); currentProgram.getMemory().getBytes(addr, bytes);
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) { for (int i = 0; i < bytes.length; i++) {
if (i > 0) sb.append(" "); if (i > 0)
sb.append(" ");
sb.append(String.format("%02x", bytes[i] & 0xFF)); sb.append(String.format("%02x", bytes[i] & 0xFF));
} }
return sb.toString(); return sb.toString();