RELO
This commit is contained in:
parent
f1b346fb0e
commit
1e91d93470
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
import ghidra.app.script.GhidraScript;
|
import ghidra.app.script.GhidraScript;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.mem.MemoryBlock;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.scalar.Scalar;
|
import ghidra.program.model.scalar.Scalar;
|
||||||
import ghidra.program.model.symbol.Reference;
|
import ghidra.program.model.symbol.Reference;
|
||||||
|
@ -55,6 +56,11 @@ public class FindRelocations extends GhidraScript {
|
||||||
Instruction instruction = instructions.next();
|
Instruction instruction = instructions.next();
|
||||||
analyzeInstruction(instruction);
|
analyzeInstruction(instruction);
|
||||||
|
|
||||||
|
// Check if we've gone past the end address
|
||||||
|
if (instruction.getAddress().getOffset() >= 0x00844190) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
processedInstructions++;
|
processedInstructions++;
|
||||||
if (processedInstructions % 1000 == 0) {
|
if (processedInstructions % 1000 == 0) {
|
||||||
monitor.setProgress(processedInstructions);
|
monitor.setProgress(processedInstructions);
|
||||||
|
@ -84,89 +90,101 @@ public class FindRelocations extends GhidraScript {
|
||||||
|
|
||||||
// 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 references from this instruction
|
||||||
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();
|
Reference[] refs = instruction.getReferencesFrom();
|
||||||
for (Reference ref : refs) {
|
for (Reference ref : refs) {
|
||||||
// Skip relative references (jumps/calls with relative addressing)
|
Address toAddr = ref.getToAddress();
|
||||||
if (ref.getReferenceType().isCall() || ref.getReferenceType().isJump()) {
|
if (isInMainMemorySpace(toAddr)) {
|
||||||
// For jumps and calls, check if it's using absolute addressing
|
// Check if the target address appears in the instruction bytes (absolute addressing)
|
||||||
if (usesAbsoluteAddressing(instruction, ref)) {
|
if (containsAbsoluteAddress(instruction, toAddr)) {
|
||||||
Address toAddr = ref.getToAddress();
|
recordRelocation(instruction.getAddress(), toAddr, mnemonic, "absolute_" + ref.getReferenceType().getName());
|
||||||
if (isInMainMemorySpace(toAddr)) {
|
|
||||||
recordRelocation(instruction.getAddress(), toAddr, mnemonic, "absolute_reference");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (ref.getReferenceType().isData()) {
|
|
||||||
// Data references are more likely to be absolute
|
|
||||||
Address toAddr = ref.getToAddress();
|
|
||||||
if (isInMainMemorySpace(toAddr)) {
|
|
||||||
recordRelocation(instruction.getAddress(), toAddr, mnemonic, "data_reference");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean containsAbsoluteAddress(Instruction instruction, Address targetAddr) {
|
||||||
|
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
|
||||||
|
return containsSequence(instructionBytes, targetBytes);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsSequence(byte[] haystack, byte[] needle) {
|
||||||
|
if (needle.length > haystack.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
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"); // All jumps, we'll filter by byte analysis
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
private void analyzeDataReferences(long instructionsProcessed, long totalWork) {
|
||||||
// Check data sections for absolute addresses
|
// Only scan actual data sections, not code sections
|
||||||
AddressSetView dataAddresses = currentProgram.getMemory().getLoadedAndInitializedAddressSet();
|
MemoryBlock[] blocks = currentProgram.getMemory().getBlocks();
|
||||||
long totalDataBytes = dataAddresses.getNumAddresses();
|
|
||||||
long processedBytes = 0;
|
long processedBytes = 0;
|
||||||
int pointerSize = currentProgram.getDefaultPointerSize();
|
int pointerSize = currentProgram.getDefaultPointerSize();
|
||||||
|
|
||||||
for (AddressRange range : dataAddresses) {
|
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()) {
|
if (monitor.isCancelled()) {
|
||||||
println("Operation cancelled by user");
|
println("Operation cancelled by user");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Address addr = range.getMinAddress();
|
if (block.getName() == ".rsrc") {
|
||||||
while (addr != null && addr.compareTo(range.getMaxAddress()) <= 0) {
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Scanning data block: " + block.getName() + " (" + block.getStart() + " - " + block.getEnd() + ")");
|
||||||
|
|
||||||
|
Address addr = block.getStart();
|
||||||
|
while (addr != null && addr.compareTo(block.getEnd()) <= 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];
|
||||||
|
@ -188,16 +206,9 @@ public class FindRelocations extends GhidraScript {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addr = addr.add(pointerSize); // Jump by pointer size for efficiency
|
addr = addr.add(pointerSize);
|
||||||
processedBytes += pointerSize;
|
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) {
|
} catch (Exception e) {
|
||||||
addr = addr.add(1);
|
addr = addr.add(1);
|
||||||
processedBytes++;
|
processedBytes++;
|
||||||
|
@ -222,31 +233,6 @@ public class FindRelocations extends GhidraScript {
|
||||||
&& value <= addrMax; // Typical executable range
|
&& 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) {
|
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);
|
||||||
|
@ -274,23 +260,43 @@ public class FindRelocations extends GhidraScript {
|
||||||
sb.append(String.format("%02x", bytes[i] & 0xFF));
|
sb.append(String.format("%02x", bytes[i] & 0xFF));
|
||||||
}
|
}
|
||||||
return sb.toString();
|
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) {
|
} catch (Exception e) {
|
||||||
// If it's a data reference, try to get the bytes at that location
|
// Check if this is in an initialized memory block
|
||||||
try {
|
if (currentProgram.getMemory().contains(addr)) {
|
||||||
byte[] bytes = new byte[4]; // Show 4 bytes for data
|
return "unreadable";
|
||||||
currentProgram.getMemory().getBytes(addr, 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();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
return "??";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "??";
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue