This commit is contained in:
parent
faf2e134f2
commit
f1b346fb0e
|
@ -10,81 +10,85 @@ import ghidra.program.model.symbol.Reference;
|
|||
import java.util.*;
|
||||
import java.io.*;
|
||||
import re3lib.RemanConfig;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class FindRelocations extends GhidraScript {
|
||||
private Set<Address> foundRelocations = new HashSet<>();
|
||||
private PrintWriter outputFile;
|
||||
|
||||
|
||||
long addrMin, addrMax;
|
||||
|
||||
@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.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();
|
||||
|
||||
|
||||
// Check for instructions that commonly use absolute addresses
|
||||
if (isRelocatableInstruction(mnemonic)) {
|
||||
// Check operands for absolute addresses
|
||||
for (int i = 0; i < instruction.getNumOperands(); i++) {
|
||||
analyzeOperand(instruction, i);
|
||||
}
|
||||
|
||||
|
||||
// Check references from this instruction - but filter out relative references
|
||||
Reference[] refs = instruction.getReferencesFrom();
|
||||
for (Reference ref : refs) {
|
||||
|
@ -107,27 +111,27 @@ public class FindRelocations extends GhidraScript {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
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);
|
||||
recordRelocation(instruction.getAddress(), addr,
|
||||
instruction.getMnemonicString(), "operand_" + opIndex);
|
||||
}
|
||||
} else if (obj instanceof Scalar) {
|
||||
Scalar scalar = (Scalar) obj;
|
||||
|
@ -137,8 +141,8 @@ public class FindRelocations extends GhidraScript {
|
|||
try {
|
||||
Address addr = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(value);
|
||||
if (isInMainMemorySpace(addr)) {
|
||||
recordRelocation(instruction.getAddress(), addr,
|
||||
instruction.getMnemonicString(), "scalar_" + opIndex);
|
||||
recordRelocation(instruction.getAddress(), addr,
|
||||
instruction.getMnemonicString(), "scalar_" + opIndex);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Invalid address, ignore
|
||||
|
@ -147,32 +151,32 @@ public class FindRelocations extends GhidraScript {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
value |= ((long) (bytes[i] & 0xFF)) << (i * 8);
|
||||
}
|
||||
|
||||
|
||||
if (looksLikeAddress(value)) {
|
||||
try {
|
||||
Address targetAddr = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(value);
|
||||
|
@ -183,17 +187,17 @@ public class FindRelocations extends GhidraScript {
|
|||
// 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++;
|
||||
|
@ -201,61 +205,63 @@ public class FindRelocations extends GhidraScript {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean isInMainMemorySpace(Address addr) {
|
||||
if (addr == null) return false;
|
||||
|
||||
if (addr == null)
|
||||
return false;
|
||||
|
||||
// Check if address is in a loaded memory block
|
||||
return currentProgram.getMemory().contains(addr) &&
|
||||
!addr.getAddressSpace().isOverlaySpace();
|
||||
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
|
||||
return value >= addrMin
|
||||
&& value <= addrMax; // Typical executable range
|
||||
}
|
||||
|
||||
|
||||
private boolean usesAbsoluteAddressing(Instruction instruction, Reference ref) {
|
||||
// Check the instruction bytes to determine addressing mode
|
||||
String mnemonic = instruction.getMnemonicString().toLowerCase();
|
||||
|
||||
|
||||
// For x86, most conditional jumps (JA, JE, JNE, etc.) use relative addressing
|
||||
if (mnemonic.startsWith("j") && !mnemonic.equals("jmp")) {
|
||||
return false; // Conditional jumps are typically relative
|
||||
}
|
||||
|
||||
|
||||
// For JMP and CALL, check the operand representation
|
||||
for (int i = 0; i < instruction.getNumOperands(); i++) {
|
||||
String operandStr = instruction.getDefaultOperandRepresentation(i);
|
||||
// If operand shows as a direct address (not offset), it might be absolute
|
||||
// But we need to be more sophisticated here...
|
||||
|
||||
|
||||
// Check if this is an indirect reference [address] which would be absolute
|
||||
if (operandStr.contains("[") && operandStr.contains("]")) {
|
||||
return true; // Indirect addressing typically uses absolute addresses
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// For now, assume most jumps/calls are relative unless proven otherwise
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private void recordRelocation(Address fromAddr, Address toAddr, String instruction, String type) {
|
||||
if (foundRelocations.add(toAddr)) {
|
||||
String instructionBytes = getInstructionBytesString(fromAddr);
|
||||
String line = String.format("0x%s -> 0x%s (%s) [%s] | %s",
|
||||
fromAddr.toString(),
|
||||
toAddr.toString(),
|
||||
instruction,
|
||||
type,
|
||||
instructionBytes);
|
||||
String line = String.format("0x%s -> 0x%s (%s) [%s] | %s",
|
||||
fromAddr.toString(),
|
||||
toAddr.toString(),
|
||||
instruction,
|
||||
type,
|
||||
instructionBytes);
|
||||
println(line);
|
||||
outputFile.println(line);
|
||||
outputFile.flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String getInstructionBytesString(Address addr) {
|
||||
try {
|
||||
Instruction instruction = currentProgram.getListing().getInstructionAt(addr);
|
||||
|
@ -263,7 +269,8 @@ public class FindRelocations extends GhidraScript {
|
|||
byte[] bytes = instruction.getBytes();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
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));
|
||||
}
|
||||
return sb.toString();
|
||||
|
@ -275,7 +282,8 @@ public class FindRelocations extends GhidraScript {
|
|||
currentProgram.getMemory().getBytes(addr, bytes);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
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));
|
||||
}
|
||||
return sb.toString();
|
||||
|
|
Loading…
Reference in New Issue