mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
GP-1110 Refactor ElfDefaultGotPltMarkup.processDynamicPLTGOT implementation, Correct duplicate ELF relocation table processing, and other minor ELF cleanup
This commit is contained in:
parent
3c1e51d0c0
commit
0a24532bf7
9 changed files with 346 additions and 130 deletions
|
@ -512,18 +512,4 @@ public interface ElfConstants {
|
|||
/** used by NetBSD/avr32 - AVR 32-bit */
|
||||
public static final short EM_AVR32_unofficial = 0x18ad;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* The size in bytes of the entry in the program
|
||||
* location table (PLT).
|
||||
*/
|
||||
public static final int PLT_ENTRY_SIZE = 0x10;
|
||||
|
||||
/**
|
||||
* The size in bytes of the entry in the program
|
||||
* location table (PLT) in ARM files.
|
||||
*/
|
||||
//public static final int PLT_ENTRY_SIZE_ARM = 0x12;
|
||||
|
||||
}
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
*/
|
||||
package ghidra.app.util.bin.format.elf;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.cmd.refs.RemoveReferenceCmd;
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.disassemble.Disassembler;
|
||||
import ghidra.program.disassemble.DisassemblerMessageListener;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
|
@ -38,6 +38,9 @@ import ghidra.util.task.TaskMonitor;
|
|||
*/
|
||||
public class ElfDefaultGotPltMarkup {
|
||||
|
||||
// When PLT head is known and named sections are missing this label will be placed at head of PLT
|
||||
private static final String PLT_HEAD_SYMBOL_NAME = "__PLT_HEAD";
|
||||
|
||||
private ElfLoadHelper elfLoadHelper;
|
||||
private ElfHeader elf;
|
||||
private Program program;
|
||||
|
@ -58,7 +61,7 @@ public class ElfDefaultGotPltMarkup {
|
|||
|
||||
public void process(TaskMonitor monitor) throws CancelledException {
|
||||
if (elf.e_shnum() == 0) {
|
||||
processDynamicPLTGOT(monitor);
|
||||
processDynamicPLTGOT(ElfDynamicType.DT_PLTGOT, ElfDynamicType.DT_JMPREL, monitor);
|
||||
}
|
||||
else {
|
||||
processGOTSections(monitor);
|
||||
|
@ -87,30 +90,55 @@ public class ElfDefaultGotPltMarkup {
|
|||
}
|
||||
}
|
||||
|
||||
private static class PltGotSymbol implements Comparable<PltGotSymbol> {
|
||||
final ElfSymbol elfSymbol;
|
||||
final long offset;
|
||||
|
||||
PltGotSymbol(ElfSymbol elfSymbol, long offset) {
|
||||
this.elfSymbol = elfSymbol;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(PltGotSymbol o) {
|
||||
return Long.compareUnsigned(offset, o.offset);
|
||||
}
|
||||
}
|
||||
|
||||
// When scanning PLT for symbols the min/max entry size are used to control the search
|
||||
private static final int MAX_SUPPORTED_PLT_ENTRY_SIZE = 32;
|
||||
private static final int MIN_SUPPORTED_PLT_ENTRY_SIZE = 8;
|
||||
|
||||
// When scanning PLT for symbol spacing this is the threashold used to stop the search
|
||||
// when the same spacing size is detected in an attempt to identify the PLT entry size
|
||||
private static final int PLT_SYMBOL_SAMPLE_COUNT_THRESHOLD = 10;
|
||||
|
||||
/**
|
||||
* Process GOT table specified by Dynamic Program Header (DT_PLTGOT).
|
||||
* Entry count determined by corresponding relocation table identified by
|
||||
* the dynamic table entry DT_JMPREL.
|
||||
* Process GOT and associated PLT based upon specified dynamic table entries.
|
||||
* The primary goal is to identify the bounds of the GOT and PLT and process
|
||||
* any external symbols which may be defined within the PLT. Processing of PLT
|
||||
* is only critical if it contains external symbols which must be processed, otherwise
|
||||
* they will likely resolve adequately during subsequent analysis.
|
||||
* @param pltGotType dynamic type for dynamic PLTGOT lookup (identifies dynamic PLTGOT)
|
||||
* @param pltGotRelType dynamic type for associated dynamic JMPREL lookup (identifies dynamic PLTGOT relocation table)
|
||||
* @param monitor task monitor
|
||||
* @throws CancelledException thrown if task cancelled
|
||||
*/
|
||||
private void processDynamicPLTGOT(TaskMonitor monitor) throws CancelledException {
|
||||
private void processDynamicPLTGOT(ElfDynamicType pltGotType, ElfDynamicType pltGotRelType,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
ElfDynamicTable dynamicTable = elf.getDynamicTable();
|
||||
if (dynamicTable == null || !dynamicTable.containsDynamicValue(ElfDynamicType.DT_PLTGOT) ||
|
||||
!dynamicTable.containsDynamicValue(ElfDynamicType.DT_JMPREL)) {
|
||||
if (dynamicTable == null || !dynamicTable.containsDynamicValue(pltGotType) ||
|
||||
!dynamicTable.containsDynamicValue(pltGotRelType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: there may be other relocation table affecting the GOT
|
||||
// corresponding to DT_PLTGOT
|
||||
|
||||
AddressSpace defaultSpace = program.getAddressFactory().getDefaultAddressSpace();
|
||||
long imageBaseAdj = elfLoadHelper.getImageBaseWordAdjustmentOffset();
|
||||
|
||||
try {
|
||||
long relocTableAddr =
|
||||
elf.adjustAddressForPrelink(dynamicTable.getDynamicValue(ElfDynamicType.DT_JMPREL));
|
||||
elf.adjustAddressForPrelink(dynamicTable.getDynamicValue(pltGotRelType));
|
||||
|
||||
ElfProgramHeader relocTableLoadHeader =
|
||||
elf.getProgramLoadHeaderContaining(relocTableAddr);
|
||||
|
@ -123,44 +151,199 @@ public class ElfDefaultGotPltMarkup {
|
|||
return;
|
||||
}
|
||||
|
||||
// External dynamic symbol entries in the PLTGOT, if any, will be placed
|
||||
// after any local symbol entries.
|
||||
// External dynamic symbol entries in the GOT, if any, will be placed
|
||||
// after any local symbol entries. Local entries are assumed to have original
|
||||
// bytes of zero, whereas non-local entries will refer to the PLT
|
||||
|
||||
// While DT_PLTGOT identifies the start of the PLTGOT it does not
|
||||
// specify its length. If there are dynamic non-local entries in the
|
||||
// PLTGOT they should have relocation entries in the table identified
|
||||
// by DT_JMPREL. It is important to note that this relocation table
|
||||
// can include entries which affect other processor-specific PLTGOT
|
||||
// tables (e.g., MIPS_PLTGOT) so we must attempt to isolate the
|
||||
// entries which correspond to DT_PLTGOT.
|
||||
|
||||
// WARNING: This implementation makes a potentially bad assumption that
|
||||
// the last relocation entry will identify the endof the PLTGOT if its
|
||||
// offset is beyond the start of the PLTGOT. This assumption could
|
||||
// easily be violated by a processor-specific PLTGOT which falls after
|
||||
// the standard PLTGOT in memory and shares the same relocation table.
|
||||
// While the dynamic value for pltGotType (e.g., DT_PLTGOT) identifies the start of
|
||||
// dynamic GOT table it does not specify its length. The associated relocation
|
||||
// table, identified by the dynamic value for pltGotRelType, will have a relocation
|
||||
// record for each PLT entry linked via the GOT. The number of relocations matches
|
||||
// the number of PLT entries and the one with the greatest offset correspionds
|
||||
// to the last GOT entry. Unfortuntely, the length of each PLT entry and initial
|
||||
// PLT head is unknown. If the binary has not placed external symbols within the PLT
|
||||
// processing and disassembly of the PLT may be skipped.
|
||||
|
||||
long pltgot = elf.adjustAddressForPrelink(
|
||||
dynamicTable.getDynamicValue(ElfDynamicType.DT_PLTGOT));
|
||||
dynamicTable.getDynamicValue(pltGotType));
|
||||
Address gotStart = defaultSpace.getAddress(pltgot + imageBaseAdj);
|
||||
|
||||
ElfRelocation[] relocations = relocationTable.getRelocations();
|
||||
|
||||
long lastGotOffset = relocations[relocations.length - 1].getOffset();
|
||||
if (lastGotOffset < pltgot) {
|
||||
ElfSymbolTable associatedSymbolTable = relocationTable.getAssociatedSymbolTable();
|
||||
if (associatedSymbolTable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Address gotStart = defaultSpace.getAddress(pltgot + imageBaseAdj);
|
||||
Address gotEnd = defaultSpace.getAddress(lastGotOffset + imageBaseAdj);
|
||||
// Create ordered list of PLTGOT symbols based upon offset with GOT.
|
||||
// It assumed that the PLT entry sequence will match this list.
|
||||
ElfSymbol[] symbols = associatedSymbolTable.getSymbols();
|
||||
List<PltGotSymbol> pltGotSymbols = new ArrayList<>();
|
||||
for (ElfRelocation reloc : relocations) {
|
||||
pltGotSymbols
|
||||
.add(new PltGotSymbol(symbols[reloc.getSymbolIndex()], reloc.getOffset()));
|
||||
}
|
||||
Collections.sort(pltGotSymbols);
|
||||
|
||||
// Identify end of GOT table based upon relocation offsets
|
||||
long maxGotOffset = pltGotSymbols.get(pltGotSymbols.size() - 1).offset;
|
||||
Address gotEnd = defaultSpace.getAddress(maxGotOffset + imageBaseAdj);
|
||||
|
||||
processGOT(gotStart, gotEnd, monitor);
|
||||
processDynamicPLT(gotStart, gotEnd, monitor);
|
||||
|
||||
//
|
||||
// Examine the first two GOT entries which correspond to the relocations (i.e., pltGotSymbols).
|
||||
// An adjusted address from the original bytes is computed. These will point into the PLT.
|
||||
// These two pointers will either refer to the same address (i.e., PLT head) or different
|
||||
// addresses which correspond to the first two PLT entries. While likely offcut into each PLT
|
||||
// entry, the differing PLT addresses can be used to identify the PLT entry size/spacing but
|
||||
// not the top of PLT. If symbols are present within the PLT for each entry, they may
|
||||
// be used to identify the PLT entry size/spacing and will be converted to external symbols.
|
||||
//
|
||||
|
||||
long pltEntryCount = pltGotSymbols.size();
|
||||
|
||||
// Get original bytes, converted to addresses, for first two PLT/GOT symbols
|
||||
Address pltAddr1 = null;
|
||||
Address pltAddr2 = null;
|
||||
for (PltGotSymbol pltGotSym : pltGotSymbols) {
|
||||
Address gotEntryAddr = defaultSpace.getAddress(pltGotSym.offset + imageBaseAdj);
|
||||
long originalGotEntry = elfLoadHelper.getOriginalValue(gotEntryAddr, true);
|
||||
if (originalGotEntry == 0) {
|
||||
return; // unexpected original bytes for PLTGOT entry - skip PLT processing
|
||||
}
|
||||
catch (NotFoundException e) {
|
||||
throw new AssertException(e);
|
||||
if (pltAddr1 == null) {
|
||||
pltAddr1 = defaultSpace.getAddress(originalGotEntry + imageBaseAdj);
|
||||
}
|
||||
catch (AddressOutOfBoundsException e) {
|
||||
log("Failed to process GOT: " + e.getMessage());
|
||||
else {
|
||||
pltAddr2 = defaultSpace.getAddress(originalGotEntry + imageBaseAdj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pltAddr2 == null) {
|
||||
return; // unable to find two GOT entries which refer to PLT - skip PLT processing
|
||||
}
|
||||
|
||||
// NOTE: This approach assumes that all PLT entries have the same structure (i.e., instruction sequence)
|
||||
long pltSpacing = pltAddr2.subtract(pltAddr1);
|
||||
if (pltSpacing < 0 || pltSpacing > MAX_SUPPORTED_PLT_ENTRY_SIZE ||
|
||||
(pltSpacing % 2) != 0) {
|
||||
return; // unsupported PLT entry size - skip PLT processing
|
||||
}
|
||||
|
||||
Address minSymbolSearchAddress;
|
||||
long symbolSearchSpacing; // nominal PLT entry size for computing maxSymbolSearchAddress
|
||||
|
||||
Address firstPltEntryAddr = null; // may be offcut within first PLT entry
|
||||
|
||||
if (pltSpacing == 0) { // Entries have same original bytes which refer to PLT head
|
||||
Function pltHeadFunc = elfLoadHelper.createOneByteFunction(null, pltAddr1, false);
|
||||
if (pltHeadFunc.getSymbol().getSource() == SourceType.DEFAULT) {
|
||||
try {
|
||||
pltHeadFunc.setName(PLT_HEAD_SYMBOL_NAME, SourceType.ANALYSIS);
|
||||
}
|
||||
catch (DuplicateNameException | InvalidInputException e) {
|
||||
// Ignore - unexpected
|
||||
}
|
||||
}
|
||||
|
||||
// PLT spacing is not known. pltAddr1 is PLT head
|
||||
minSymbolSearchAddress = pltAddr1.next();
|
||||
|
||||
// Use conservative PLT entry size when computing address limit for PLT symbol search.
|
||||
// For a PLT with an actual entry size of 16 this will reduce the scan to less than half
|
||||
// of the PLT. This should only present an issue for very small PLTs or those
|
||||
// with sparsely placed symbols.
|
||||
symbolSearchSpacing = MIN_SUPPORTED_PLT_ENTRY_SIZE;
|
||||
}
|
||||
else {
|
||||
// PLT spacing is known, but start of entry and head is not known. pltAddr1 points to middle of first PLT entry (not head).
|
||||
firstPltEntryAddr = pltAddr1;
|
||||
minSymbolSearchAddress = pltAddr1.subtract(pltSpacing - 1); // try to avoid picking up symbol which may be at head
|
||||
symbolSearchSpacing = pltSpacing;
|
||||
}
|
||||
|
||||
// Attempt to find symbols located within the PLT.
|
||||
Address maxSymbolSearchAddress =
|
||||
minSymbolSearchAddress.add(pltEntryCount * symbolSearchSpacing);
|
||||
|
||||
// Scan symbols within PLT; helps to identify start of first entry and PLT entry size/spacing if unknown
|
||||
Symbol firstSymbol = null;
|
||||
Symbol lastSymbol = null;
|
||||
long discoveredPltSpacing = Long.MAX_VALUE;
|
||||
Map<Long, Integer> spacingCounts = new HashMap<>();
|
||||
for (Symbol sym : elfLoadHelper.getProgram()
|
||||
.getSymbolTable()
|
||||
.getSymbolIterator(minSymbolSearchAddress, true)) {
|
||||
if (sym.getSource() == SourceType.DEFAULT) {
|
||||
continue;
|
||||
}
|
||||
Address addr = sym.getAddress();
|
||||
if (addr.compareTo(maxSymbolSearchAddress) > 0) {
|
||||
break;
|
||||
}
|
||||
if (firstSymbol == null) {
|
||||
firstSymbol = sym;
|
||||
}
|
||||
if (pltSpacing == 0) {
|
||||
// Collect spacing samples if PLT spacing is unknown
|
||||
if (lastSymbol != null) {
|
||||
long spacing = addr.subtract(lastSymbol.getAddress());
|
||||
if (spacing > MAX_SUPPORTED_PLT_ENTRY_SIZE) {
|
||||
lastSymbol = null; // reset on large symbol spacing
|
||||
continue;
|
||||
}
|
||||
int count =
|
||||
spacingCounts.compute(spacing, (k, v) -> (v == null) ? 1 : v + 1);
|
||||
discoveredPltSpacing = Math.min(discoveredPltSpacing, spacing);
|
||||
if (count == PLT_SYMBOL_SAMPLE_COUNT_THRESHOLD) {
|
||||
break; // stop on 10 occurances of the same spacing (rather arbitrary sample limit)
|
||||
}
|
||||
}
|
||||
lastSymbol = sym;
|
||||
}
|
||||
}
|
||||
|
||||
if (pltSpacing == 0) {
|
||||
if (discoveredPltSpacing == Long.MAX_VALUE ||
|
||||
spacingCounts.get(discoveredPltSpacing) == 1) { // NOTE: required number of symbol-spacing samples could be increased from 1
|
||||
return; // PLT spacing not determined / too large or insufficient PLT symbols - skip PLT processing
|
||||
}
|
||||
pltSpacing = discoveredPltSpacing;
|
||||
}
|
||||
|
||||
if (firstSymbol != null) {
|
||||
// use PLT symbol if found to identify start of first PLT entry
|
||||
int firstSymbolEntryIndex = -1;
|
||||
Address firstSymbolAddr = firstSymbol.getAddress();
|
||||
int entryIndex = 0;
|
||||
for (PltGotSymbol entrySymbol : pltGotSymbols) {
|
||||
if (firstSymbolAddr
|
||||
.equals(elfLoadHelper.getElfSymbolAddress(entrySymbol.elfSymbol))) {
|
||||
firstSymbolEntryIndex = entryIndex;
|
||||
break;
|
||||
}
|
||||
++entryIndex;
|
||||
}
|
||||
if (firstSymbolEntryIndex >= 0) {
|
||||
firstPltEntryAddr = firstSymbolAddr;
|
||||
if (firstSymbolEntryIndex > 0) {
|
||||
firstPltEntryAddr =
|
||||
firstPltEntryAddr.subtract(firstSymbolEntryIndex * pltSpacing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstPltEntryAddr == null) {
|
||||
return; // failed to identify first PLT entry - skip PLT processing
|
||||
}
|
||||
|
||||
Address pltEnd = firstPltEntryAddr.add(pltSpacing * (pltEntryCount - 1));
|
||||
processLinkageTable("PLT", firstPltEntryAddr, pltEnd, monitor);
|
||||
}
|
||||
catch (Exception e) {
|
||||
String msg = "Failed to process " + pltGotType + ": " + e.getMessage();
|
||||
log(msg);
|
||||
Msg.error(this, msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,20 +358,47 @@ public class ElfDefaultGotPltMarkup {
|
|||
private void processGOT(Address gotStart, Address gotEnd, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
||||
boolean imageBaseAlreadySet = elf.isPreLinked();
|
||||
// Bail if GOT was previously marked-up or not within initialized memory
|
||||
MemoryBlock block = memory.getBlock(gotStart);
|
||||
if (block == null || !block.isInitialized()) {
|
||||
return; // unsupported memory region - skip GOT processing
|
||||
}
|
||||
Data data = program.getListing().getDataAt(gotStart);
|
||||
if (data == null || !Undefined.isUndefined(data.getDataType())) {
|
||||
return; // evidence of prior markup - skip GOT processing
|
||||
}
|
||||
|
||||
try {
|
||||
Address newImageBase = null;
|
||||
while (gotStart.compareTo(gotEnd) <= 0) {
|
||||
monitor.checkCanceled();
|
||||
// Fixup first GOT entry which frequently refers to _DYNAMIC but generally lacks relocation (e.g. .got.plt)
|
||||
ElfDynamicTable dynamicTable = elf.getDynamicTable();
|
||||
long imageBaseAdj = elfLoadHelper.getImageBaseWordAdjustmentOffset();
|
||||
if (dynamicTable != null && imageBaseAdj != 0) {
|
||||
long entry1Value = elfLoadHelper.getOriginalValue(gotStart, false);
|
||||
if (entry1Value == dynamicTable.getAddressOffset()) {
|
||||
// TODO: record artificial relative relocation for reversion/export concerns
|
||||
entry1Value += imageBaseAdj; // adjust first entry value
|
||||
if (elf.is64Bit()) {
|
||||
memory.setLong(gotStart, entry1Value);
|
||||
}
|
||||
else {
|
||||
memory.setInt(gotStart, (int) entry1Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Data data = createPointer(gotStart, true);
|
||||
boolean imageBaseAlreadySet = elf.isPreLinked();
|
||||
|
||||
Address newImageBase = null;
|
||||
Address nextGotAddr = gotStart;
|
||||
while (nextGotAddr.compareTo(gotEnd) <= 0) {
|
||||
|
||||
data = createPointer(nextGotAddr, true);
|
||||
if (data == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
gotStart = data.getMaxAddress().add(1);
|
||||
nextGotAddr = data.getMaxAddress().add(1);
|
||||
}
|
||||
catch (AddressOutOfBoundsException e) {
|
||||
break; // no more room
|
||||
|
@ -208,20 +418,20 @@ public class ElfDefaultGotPltMarkup {
|
|||
}
|
||||
}
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
log("Failed to process GOT: " + e.getMessage());
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
log("Failed to adjust image base: " + e.getMessage());
|
||||
}
|
||||
catch (LockException e) {
|
||||
throw new AssertException(e);
|
||||
catch (Exception e) {
|
||||
String msg = "Failed to process GOT at " + gotStart + ": " + e.getMessage();
|
||||
log(msg);
|
||||
Msg.error(this, msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void processPLTSection(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
// TODO: Handle case where PLT is non-executable pointer table
|
||||
// TODO: May want to consider using analysis to fully disassemble PLT, we only
|
||||
// really need to migrate external symbols contained within the PLT
|
||||
|
||||
// FIXME: Code needs help ... bad assumption about PLT head size (e.g., 16)
|
||||
int assumedPltHeadSize = 16;
|
||||
|
||||
if (elf.isRelocatable()) {
|
||||
return; //relocatable files do not have .PLT sections
|
||||
|
@ -229,17 +439,15 @@ public class ElfDefaultGotPltMarkup {
|
|||
|
||||
MemoryBlock pltBlock = memory.getBlock(ElfSectionHeaderConstants.dot_plt);
|
||||
// TODO: This is a band-aid since there are many PLT implementations and this assumes only one.
|
||||
if (pltBlock == null || !pltBlock.isExecute() ||
|
||||
pltBlock.getSize() <= ElfConstants.PLT_ENTRY_SIZE) {
|
||||
if (pltBlock == null || !pltBlock.isExecute() || pltBlock.getSize() <= assumedPltHeadSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
int skipPointers = ElfConstants.PLT_ENTRY_SIZE;
|
||||
int skipPointers = assumedPltHeadSize;
|
||||
|
||||
// ARM, AARCH64 and others may not store pointers at start of .plt
|
||||
if (elf.e_machine() == ElfConstants.EM_ARM || elf.e_machine() == ElfConstants.EM_AARCH64) {
|
||||
// TODO: Should be handled by extension
|
||||
skipPointers = 0;
|
||||
skipPointers = 0; // disassemble entire PLT
|
||||
}
|
||||
|
||||
// Process PLT section
|
||||
|
@ -248,37 +456,6 @@ public class ElfDefaultGotPltMarkup {
|
|||
processLinkageTable(ElfSectionHeaderConstants.dot_plt, minAddress, maxAddress, monitor);
|
||||
}
|
||||
|
||||
private void processDynamicPLT(Address gotStart, Address gotEnd, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
||||
Address pltStart = null;
|
||||
Address pltEnd = null;
|
||||
|
||||
for (Data gotPtr : listing.getDefinedData(new AddressSet(gotStart.next(), gotEnd), true)) {
|
||||
monitor.checkCanceled();
|
||||
if (!gotPtr.isPointer()) {
|
||||
Msg.error(this, "ELF PLTGOT contains non-pointer");
|
||||
return; // unexpected
|
||||
}
|
||||
Address ptr = (Address) gotPtr.getValue();
|
||||
if (ptr.getOffset() == 0) {
|
||||
continue;
|
||||
}
|
||||
MemoryBlock block = memory.getBlock(ptr);
|
||||
if (block == null || block.getName().equals(MemoryBlock.EXTERNAL_BLOCK_NAME)) {
|
||||
continue;
|
||||
}
|
||||
if (pltStart == null) {
|
||||
pltStart = ptr;
|
||||
}
|
||||
pltEnd = ptr;
|
||||
}
|
||||
|
||||
if (pltStart != null) {
|
||||
processLinkageTable("PLT", pltStart, pltEnd, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform disassembly and markup of specified external linkage table which
|
||||
* consists of thunks to external functions. If symbols are defined within the
|
||||
|
@ -292,6 +469,7 @@ public class ElfDefaultGotPltMarkup {
|
|||
public void processLinkageTable(String pltName, Address minAddress, Address maxAddress,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
try {
|
||||
// Disassemble section.
|
||||
// Disassembly is only done so we can see all instructions since many
|
||||
// of them are unreachable after applying relocations
|
||||
|
@ -304,6 +482,13 @@ public class ElfDefaultGotPltMarkup {
|
|||
log("Converted " + count + " " + pltName + " section symbols to external thunks");
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
String msg =
|
||||
"Failed to process " + pltName + " at " + minAddress + ": " + e.getMessage();
|
||||
log(msg);
|
||||
Msg.error(this, msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all symbols over a specified range to thunks to external functions.
|
||||
|
@ -341,15 +526,13 @@ public class ElfDefaultGotPltMarkup {
|
|||
|
||||
private void disassemble(Address start, Address end, Program prog, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
DisassemblerMessageListener dml = msg -> {
|
||||
//don't care...
|
||||
};
|
||||
// TODO: Should we restrict disassembly or follows flows?
|
||||
AddressSet set = new AddressSet(start, end);
|
||||
Disassembler disassembler = Disassembler.getDisassembler(prog, monitor, dml);
|
||||
Disassembler disassembler = Disassembler.getDisassembler(prog, monitor, m -> {
|
||||
/* silent */});
|
||||
while (!set.isEmpty()) {
|
||||
monitor.checkCanceled();
|
||||
AddressSet disset = disassembler.disassemble(set.getMinAddress(), set, true);
|
||||
AddressSet disset = disassembler.disassemble(set.getMinAddress(), null, true);
|
||||
if (disset.isEmpty()) {
|
||||
// Stop on first error but discard error bookmark since
|
||||
// some plt sections are partly empty and must rely
|
||||
|
@ -444,6 +627,8 @@ public class ElfDefaultGotPltMarkup {
|
|||
* then the base of the .so is most likely incorrect. Shift it!
|
||||
*/
|
||||
private Address UglyImageBaseCheck(Data data, Address imageBase) {
|
||||
// TODO: Find sample - e.g., ARM .so - seems too late in import processing to change image base
|
||||
// if any relocations have been applied.
|
||||
if (elf.e_machine() != ElfConstants.EM_ARM) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -530,6 +530,11 @@ public class ElfHeader implements StructConverter, Writeable {
|
|||
}
|
||||
|
||||
long relocTableOffset = relocTableLoadHeader.getOffset(relocTableAddr);
|
||||
for (ElfRelocationTable relocTable : relocationTableList) {
|
||||
if (relocTable.getFileOffset() == relocTableOffset) {
|
||||
return; // skip reloc table previously parsed
|
||||
}
|
||||
}
|
||||
long tableEntrySize =
|
||||
relocEntrySizeType != null ? dynamicTable.getDynamicValue(relocEntrySizeType) : -1;
|
||||
long tableSize = dynamicTable.getDynamicValue(relocTableSizeType);
|
||||
|
|
|
@ -21,6 +21,7 @@ import ghidra.program.model.address.Address;
|
|||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
|
@ -188,4 +189,18 @@ public interface ElfLoadHelper {
|
|||
*/
|
||||
public AddressRange allocateLinkageBlock(int alignment, int size, String purpose);
|
||||
|
||||
/**
|
||||
* <p>Get the original memory value at the specified address if a relocation was applied at the
|
||||
* specified address (not containing). Current memory value will be returned if no relocation
|
||||
* has been applied at specified address. The value size is either 8-bytes if {@link ElfHeader#is64Bit()},
|
||||
* otherwise it will be 4-bytes. This is primarily intended to inspect original bytes within
|
||||
* the GOT which may have had relocations applied to them.
|
||||
* @param addr memory address
|
||||
* @param signExtend if true sign-extend to long, else treat as unsigned
|
||||
* @return original bytes value
|
||||
* @throws MemoryAccessException if memory read fails
|
||||
*/
|
||||
public long getOriginalValue(Address addr, boolean signExtend)
|
||||
throws MemoryAccessException;
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ package ghidra.app.util.bin.format.elf.relocation;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ghidra.app.util.bin.format.elf.*;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
@ -125,7 +127,7 @@ abstract public class ElfRelocationHandler implements ExtensionPoint {
|
|||
public static void markAsUnhandled(Program program, Address relocationAddress, long type,
|
||||
long symbolIndex, String symbolName, MessageLog log) {
|
||||
|
||||
symbolName = symbolName == null ? "<no name>" : symbolName;
|
||||
symbolName = StringUtils.isEmpty(symbolName) ? "<no name>" : symbolName;
|
||||
log.appendMsg("Unhandled Elf Relocation: Type = " + type + " (0x" + Long.toHexString(type) +
|
||||
") at " + relocationAddress + " (Symbol = " + symbolName + ")");
|
||||
BookmarkManager bookmarkManager = program.getBookmarkManager();
|
||||
|
|
|
@ -43,6 +43,7 @@ import ghidra.program.model.data.DataUtilities.ClearDataMode;
|
|||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.model.reloc.Relocation;
|
||||
import ghidra.program.model.reloc.RelocationTable;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
import ghidra.program.model.symbol.*;
|
||||
|
@ -911,6 +912,23 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getOriginalValue(Address addr, boolean signExtend) throws MemoryAccessException {
|
||||
byte[] bytes;
|
||||
int len = elf.is64Bit() ? 8 : 4;
|
||||
Relocation relocation = program.getRelocationTable().getRelocation(addr);
|
||||
if (relocation == null) {
|
||||
bytes = new byte[len];
|
||||
memory.getBytes(addr, bytes);
|
||||
}
|
||||
else {
|
||||
bytes = relocation.getBytes();
|
||||
}
|
||||
DataConverter dataConverter = DataConverter.getInstance(elf.isBigEndian());
|
||||
return signExtend ? dataConverter.getSignedValue(bytes, len)
|
||||
: dataConverter.getValue(bytes, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add reference to previously applied header structure (assumes markupElfHeader previously called)
|
||||
* @param componentName
|
||||
|
|
|
@ -727,7 +727,8 @@ public class MIPS_ElfExtension extends ElfExtension {
|
|||
stubsBlock.getEnd(), monitor);
|
||||
}
|
||||
|
||||
private void fixupGot(ElfLoadHelper elfLoadHelper, TaskMonitor monitor) {
|
||||
private void fixupGot(ElfLoadHelper elfLoadHelper, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
||||
// see Wiki at https://dmz-portal.mips.com/wiki/MIPS_Multi_GOT
|
||||
// see related doc at https://www.cr0.org/paper/mips.elf.external.resolution.txt
|
||||
|
@ -764,6 +765,7 @@ public class MIPS_ElfExtension extends ElfExtension {
|
|||
|
||||
// process local symbol got entries
|
||||
for (int i = 0; i < gotLocalEntryCount; i++) {
|
||||
monitor.checkCanceled();
|
||||
Address gotEntryAddr =
|
||||
adjustTableEntryIfNonZero(gotBaseAddress, i, imageShift, elfLoadHelper);
|
||||
Data pointerData = elfLoadHelper.createData(gotEntryAddr, PointerDataType.dataType);
|
||||
|
@ -775,6 +777,7 @@ public class MIPS_ElfExtension extends ElfExtension {
|
|||
// process global/external symbol got entries
|
||||
int gotIndex = gotLocalEntryCount;
|
||||
for (int i = gotSymbolIndex; i < elfSymbols.length; i++) {
|
||||
monitor.checkCanceled();
|
||||
Address gotEntryAddr = adjustTableEntryIfNonZero(gotBaseAddress, gotIndex++,
|
||||
imageShift, elfLoadHelper);
|
||||
Data pointerData = elfLoadHelper.createData(gotEntryAddr, PointerDataType.dataType);
|
||||
|
@ -795,7 +798,7 @@ public class MIPS_ElfExtension extends ElfExtension {
|
|||
}
|
||||
}
|
||||
|
||||
private void fixupMipsGot(ElfLoadHelper elfLoadHelper, TaskMonitor monitor) {
|
||||
private void fixupMipsGot(ElfLoadHelper elfLoadHelper, TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
ElfHeader elfHeader = elfLoadHelper.getElfHeader();
|
||||
ElfDynamicTable dynamicTable = elfHeader.getDynamicTable();
|
||||
|
@ -830,6 +833,7 @@ public class MIPS_ElfExtension extends ElfExtension {
|
|||
// process local symbol got entries
|
||||
int gotEntryIndex = 1;
|
||||
for (int i = 0; i < gotSymbolIndex; i++) {
|
||||
monitor.checkCanceled();
|
||||
if (!elfSymbols[i].isFunction() || elfSymbols[i].getSectionHeaderIndex() != 0) {
|
||||
continue;
|
||||
}
|
||||
|
@ -849,6 +853,7 @@ public class MIPS_ElfExtension extends ElfExtension {
|
|||
|
||||
private Address adjustTableEntryIfNonZero(Address tableBaseAddr, int entryIndex,
|
||||
long adjustment, ElfLoadHelper elfLoadHelper) throws MemoryAccessException {
|
||||
// TODO: record artificial relative relocation for reversion/export concerns
|
||||
boolean is64Bit = elfLoadHelper.getElfHeader().is64Bit();
|
||||
Memory memory = elfLoadHelper.getProgram().getMemory();
|
||||
Address tableEntryAddr;
|
||||
|
@ -871,6 +876,7 @@ public class MIPS_ElfExtension extends ElfExtension {
|
|||
|
||||
private Address setTableEntryIfZero(Address tableBaseAddr, int entryIndex, long value,
|
||||
ElfLoadHelper elfLoadHelper) throws MemoryAccessException {
|
||||
// TODO: record artificial relative relocation for reversion/export concerns
|
||||
boolean is64Bit = elfLoadHelper.getElfHeader().is64Bit();
|
||||
Memory memory = elfLoadHelper.getProgram().getMemory();
|
||||
Address tableEntryAddr;
|
||||
|
|
|
@ -35,6 +35,9 @@ import ghidra.util.task.TaskMonitor;
|
|||
|
||||
public class PowerPC64_ElfExtension extends ElfExtension {
|
||||
|
||||
private static final int PLT_ENTRY_SIZE = 8; // could be 16(local) or 24 w/ opd_api, 32 for VxWorks
|
||||
private static final int PLT_HEAD_SIZE = 16; // could be 24 w/ obd_api, 32 for VxWorks
|
||||
|
||||
// Elf Dynamic Type Extensions
|
||||
public static final ElfDynamicType DT_PPC64_GLINK = new ElfDynamicType(0x70000000,
|
||||
"DT_PPC64_GLINK", "Specify the start of the .glink section", ElfDynamicValueType.ADDRESS);
|
||||
|
@ -225,8 +228,7 @@ public class PowerPC64_ElfExtension extends ElfExtension {
|
|||
}
|
||||
Program program = elfLoadHelper.getProgram();
|
||||
MemoryBlock pltBlock = program.getMemory().getBlock(pltSection.getNameAsString());
|
||||
// TODO: This is a band-aid since there are many PLT implementations and this assumes only one.
|
||||
if (pltBlock == null || pltBlock.getSize() <= ElfConstants.PLT_ENTRY_SIZE) {
|
||||
if (pltBlock == null) {
|
||||
return;
|
||||
}
|
||||
if (pltSection.isExecutable()) {
|
||||
|
@ -243,14 +245,14 @@ public class PowerPC64_ElfExtension extends ElfExtension {
|
|||
|
||||
// TODO: Uncertain
|
||||
|
||||
Address addr = pltBlock.getStart().add(ElfConstants.PLT_ENTRY_SIZE);
|
||||
Address addr = pltBlock.getStart().add(PLT_HEAD_SIZE);
|
||||
try {
|
||||
while (addr.compareTo(pltBlock.getEnd()) < 0) {
|
||||
monitor.checkCanceled();
|
||||
if (elfLoadHelper.createData(addr, PointerDataType.dataType) == null) {
|
||||
break; // stop early if failed to create a pointer
|
||||
}
|
||||
addr = addr.addNoWrap(8);
|
||||
addr = addr.addNoWrap(PLT_ENTRY_SIZE);
|
||||
}
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
|
|
|
@ -28,8 +28,6 @@ import ghidra.util.task.TaskMonitor;
|
|||
|
||||
public class X86_32_ElfExtension extends ElfExtension {
|
||||
|
||||
// TODO: Add extension types
|
||||
|
||||
@Override
|
||||
public boolean canHandle(ElfHeader elf) {
|
||||
return elf.e_machine() == ElfConstants.EM_386 && elf.is32Bit();
|
||||
|
@ -90,8 +88,7 @@ public class X86_32_ElfExtension extends ElfExtension {
|
|||
|
||||
// MemoryBlock pltBlock = getBlockPLT(pltSection);
|
||||
MemoryBlock pltBlock = memory.getBlock(pltSection.getNameAsString());
|
||||
// TODO: This is a band-aid since there are many PLT implementations and this assumes only one.
|
||||
if (pltBlock == null || pltBlock.getSize() <= ElfConstants.PLT_ENTRY_SIZE) {
|
||||
if (pltBlock == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue