diff --git a/java/ghidra/FindRelocations.java b/java/ghidra/FindRelocations.java index 8f7fd470..630123ca 100644 --- a/java/ghidra/FindRelocations.java +++ b/java/ghidra/FindRelocations.java @@ -4,6 +4,7 @@ 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; @@ -55,6 +56,11 @@ public class FindRelocations extends GhidraScript { 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); @@ -84,89 +90,101 @@ public class FindRelocations extends GhidraScript { // 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 + // Check references from this instruction Reference[] refs = instruction.getReferencesFrom(); for (Reference ref : refs) { - // Skip relative references (jumps/calls with relative addressing) - if (ref.getReferenceType().isCall() || ref.getReferenceType().isJump()) { - // For jumps and calls, check if it's using absolute addressing - if (usesAbsoluteAddressing(instruction, ref)) { - Address toAddr = ref.getToAddress(); - 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"); + Address toAddr = ref.getToAddress(); + if (isInMainMemorySpace(toAddr)) { + // Check if the target address appears in the instruction bytes (absolute addressing) + if (containsAbsoluteAddress(instruction, toAddr)) { + recordRelocation(instruction.getAddress(), toAddr, mnemonic, "absolute_" + ref.getReferenceType().getName()); } } } } } + 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) { // 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 - } - } - } - } + 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) { - // Check data sections for absolute addresses - AddressSetView dataAddresses = currentProgram.getMemory().getLoadedAndInitializedAddressSet(); - long totalDataBytes = dataAddresses.getNumAddresses(); + // Only scan actual data sections, not code sections + MemoryBlock[] blocks = currentProgram.getMemory().getBlocks(); long processedBytes = 0; 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()) { println("Operation cancelled by user"); return; } - Address addr = range.getMinAddress(); - while (addr != null && addr.compareTo(range.getMaxAddress()) <= 0) { + if (block.getName() == ".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]; @@ -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; - // 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++; @@ -222,31 +233,6 @@ public class FindRelocations extends GhidraScript { && 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); @@ -274,23 +260,43 @@ public class FindRelocations extends GhidraScript { 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) { - // If it's a data reference, try to get the bytes at that location - try { - byte[] bytes = new byte[4]; // Show 4 bytes for data - 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 "??"; + // 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; + } }