mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Merge remote-tracking branch 'origin/Ghidra_9.1'
This commit is contained in:
commit
8e30848804
12 changed files with 450 additions and 62 deletions
|
@ -5,6 +5,9 @@
|
|||
<executable_format name="Mac OS X Mach-O">
|
||||
<functionNamesFile>MachOFunctionsThatDoNotReturn</functionNamesFile>
|
||||
</executable_format>
|
||||
<executable_format name="DYLD Cache">
|
||||
<functionNamesFile>MachOFunctionsThatDoNotReturn</functionNamesFile>
|
||||
</executable_format>
|
||||
<executable_format name="Portable Executable (PE)">
|
||||
<functionNamesFile>PEFunctionsThatDoNotReturn</functionNamesFile>
|
||||
</executable_format>
|
||||
|
|
|
@ -275,6 +275,29 @@ public class DyldCacheHeader implements StructConverter {
|
|||
return localSymbolsInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link DyldCacheSlideInfoCommon}.
|
||||
*
|
||||
* @return the {@link DyldCacheSlideInfoCommon}. Common, or particular version
|
||||
*/
|
||||
public DyldCacheSlideInfoCommon getSlideInfo() {
|
||||
return slideInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return slideInfoOffset
|
||||
*/
|
||||
public long getSlideInfoOffset() {
|
||||
return slideInfoOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return slideInfoSize
|
||||
*/
|
||||
public long getSlideInfoSize() {
|
||||
return slideInfoSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link List} of branch pool address. Requires header to have been parsed.
|
||||
*
|
||||
|
|
|
@ -27,7 +27,6 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
|
||||
|
||||
private int toc_offset;
|
||||
|
@ -36,6 +35,26 @@ public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
|
|||
private int entries_count;
|
||||
private int entries_size;
|
||||
|
||||
public int getTocOffset() {
|
||||
return toc_offset;
|
||||
}
|
||||
|
||||
public int getTocCount() {
|
||||
return toc_count;
|
||||
}
|
||||
|
||||
public int getEntriesOffset() {
|
||||
return entries_offset;
|
||||
}
|
||||
|
||||
public int getEntriesCount() {
|
||||
return entries_count;
|
||||
}
|
||||
|
||||
public int getEntriesSize() {
|
||||
return entries_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link DyldCacheSlideInfo1}.
|
||||
*
|
||||
|
|
|
@ -27,7 +27,6 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
|
||||
|
||||
private int page_size;
|
||||
|
@ -37,6 +36,44 @@ public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
|
|||
private int page_extras_count;
|
||||
private long delta_mask;
|
||||
private long value_add;
|
||||
private short page_starts_entries[];
|
||||
private short page_extras_entries[];
|
||||
|
||||
public long getPageSize() {
|
||||
return ((long)page_size) & 0xffffffff;
|
||||
}
|
||||
|
||||
public long getPageStartsOffset() {
|
||||
return ((long) page_starts_offset) & 0xffffffff;
|
||||
}
|
||||
|
||||
public long getPageStartsCount() {
|
||||
return ((long) page_starts_count) & 0xffffffff;
|
||||
}
|
||||
|
||||
public long getPageExtrasOffset() {
|
||||
return ((long) page_extras_offset) & 0xffffffff;
|
||||
}
|
||||
|
||||
public long getPageExtrasCount() {
|
||||
return ((long) page_extras_count) & 0xffffffff;
|
||||
}
|
||||
|
||||
public long getDeltaMask() {
|
||||
return delta_mask;
|
||||
}
|
||||
|
||||
public long getValueAdd() {
|
||||
return value_add;
|
||||
}
|
||||
|
||||
public short[] getPageStartsEntries() {
|
||||
return page_starts_entries;
|
||||
}
|
||||
|
||||
public short[] getPageExtrasEntries() {
|
||||
return page_extras_entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link DyldCacheSlideInfo2}.
|
||||
|
@ -53,6 +90,8 @@ public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
|
|||
page_extras_count = reader.readNextInt();
|
||||
delta_mask = reader.readNextLong();
|
||||
value_add = reader.readNextLong();
|
||||
page_starts_entries = reader.readNextShortArray(page_starts_count);
|
||||
page_extras_entries = reader.readNextShortArray(page_extras_count);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,7 +27,6 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
*
|
||||
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
|
||||
|
||||
private int page_size;
|
||||
|
@ -35,6 +34,22 @@ public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
|
|||
private long auth_value_add;
|
||||
private short page_starts[];
|
||||
|
||||
public int getPageSize() {
|
||||
return page_size;
|
||||
}
|
||||
|
||||
public int getPageStartsCount() {
|
||||
return page_starts_count;
|
||||
}
|
||||
|
||||
public long getAuthValueAdd() {
|
||||
return auth_value_add;
|
||||
}
|
||||
|
||||
public short[] getPageStarts() {
|
||||
return page_starts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link DyldCacheSlideInfo3}.
|
||||
*
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.util.*;
|
|||
|
||||
import ghidra.app.util.MemoryBlockUtils;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.OptionUtils;
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.format.macho.dyld.DyldArchitecture;
|
||||
|
@ -47,7 +48,13 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader {
|
|||
static final String CREATE_DYLIB_SECTIONS_OPTION_NAME = "Create DYLIB section memory blocks";
|
||||
|
||||
/** Default value for loader option to create memory blocks for DYLIB sections */
|
||||
static final boolean CREATE_DYLIB_SECTIONS_OPTION_DEFAULT = true;
|
||||
static final boolean CREATE_DYLIB_SECTIONS_OPTION_DEFAULT = false;
|
||||
|
||||
/** Loader option to add relocation entries for each fixed chain pointer */
|
||||
static final String ADD_RELOCATION_ENTRIES_OPTION_NAME = "Add relocation entries for fixed chain pointers";
|
||||
|
||||
/** Default value for loader option add relocation entries */
|
||||
static final boolean ADD_RELOCATION_ENTRIES_OPTION_DEFAULT = false;
|
||||
|
||||
@Override
|
||||
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
|
||||
|
@ -84,7 +91,8 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader {
|
|||
try {
|
||||
DyldCacheProgramBuilder.buildProgram(program, provider,
|
||||
MemoryBlockUtils.createFileBytes(program, provider, monitor),
|
||||
shouldProcessSymbols(options), shouldCreateDylibSections(options), log, monitor);
|
||||
shouldProcessSymbols(options), shouldCreateDylibSections(options),
|
||||
shouldAddRelocationEntries(options), log, monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return;
|
||||
|
@ -105,32 +113,23 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader {
|
|||
list.add(
|
||||
new Option(CREATE_DYLIB_SECTIONS_OPTION_NAME, CREATE_DYLIB_SECTIONS_OPTION_DEFAULT,
|
||||
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-createDylibSections"));
|
||||
list.add(
|
||||
new Option(ADD_RELOCATION_ENTRIES_OPTION_NAME, ADD_RELOCATION_ENTRIES_OPTION_DEFAULT,
|
||||
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-addRelocationEntries"));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private boolean shouldProcessSymbols(List<Option> options) {
|
||||
if (options != null) {
|
||||
for (Option option : options) {
|
||||
String optName = option.getName();
|
||||
if (optName.equals(PROCESS_SYMBOLS_OPTION_NAME)) {
|
||||
return (Boolean) option.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return PROCESS_SYMBOLS_OPTION_DEFAULT;
|
||||
return OptionUtils.getOption(PROCESS_SYMBOLS_OPTION_NAME, options, PROCESS_SYMBOLS_OPTION_DEFAULT);
|
||||
}
|
||||
|
||||
private boolean shouldCreateDylibSections(List<Option> options) {
|
||||
if (options != null) {
|
||||
for (Option option : options) {
|
||||
String optName = option.getName();
|
||||
if (optName.equals(CREATE_DYLIB_SECTIONS_OPTION_NAME)) {
|
||||
return (Boolean) option.getValue();
|
||||
return OptionUtils.getOption(CREATE_DYLIB_SECTIONS_OPTION_NAME, options, CREATE_DYLIB_SECTIONS_OPTION_DEFAULT);
|
||||
}
|
||||
}
|
||||
}
|
||||
return CREATE_DYLIB_SECTIONS_OPTION_DEFAULT;
|
||||
|
||||
private boolean shouldAddRelocationEntries(List<Option> options) {
|
||||
return OptionUtils.getOption(ADD_RELOCATION_ENTRIES_OPTION_NAME, options, ADD_RELOCATION_ENTRIES_OPTION_DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,7 +17,10 @@ package ghidra.app.util.opinion;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import ghidra.app.util.MemoryBlockUtils;
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
|
@ -25,15 +28,28 @@ import ghidra.app.util.bin.ByteProvider;
|
|||
import ghidra.app.util.bin.format.macho.MachException;
|
||||
import ghidra.app.util.bin.format.macho.MachHeader;
|
||||
import ghidra.app.util.bin.format.macho.commands.NList;
|
||||
import ghidra.app.util.bin.format.macho.dyld.*;
|
||||
import ghidra.app.util.bin.format.macho.dyld.DyldCacheHeader;
|
||||
import ghidra.app.util.bin.format.macho.dyld.DyldCacheImageInfo;
|
||||
import ghidra.app.util.bin.format.macho.dyld.DyldCacheLocalSymbolsInfo;
|
||||
import ghidra.app.util.bin.format.macho.dyld.DyldCacheMappingInfo;
|
||||
import ghidra.app.util.bin.format.macho.dyld.DyldCacheSlideInfo2;
|
||||
import ghidra.app.util.bin.format.macho.dyld.DyldCacheSlideInfoCommon;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.importer.MessageLogContinuesFactory;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.data.DataUtilities;
|
||||
import ghidra.program.model.data.Pointer64DataType;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.ProgramFragment;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.symbol.SymbolUtilities;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
|
@ -41,9 +57,16 @@ import ghidra.util.task.TaskMonitor;
|
|||
*/
|
||||
public class DyldCacheProgramBuilder extends MachoProgramBuilder {
|
||||
|
||||
private static final int DATA_PAGE_MAP_ENTRY = 1;
|
||||
private static final int BYTES_PER_CHAIN_OFFSET = 4;
|
||||
private static final int CHAIN_OFFSET_MASK = 0x3fff;
|
||||
private static final int DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE = 0x4000;
|
||||
private static final int DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA = 0x8000;
|
||||
|
||||
protected DyldCacheHeader dyldCacheHeader;
|
||||
private boolean shouldProcessSymbols;
|
||||
private boolean shouldCreateDylibSections;
|
||||
private boolean shouldAddRelocationEntries;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DyldCacheProgramBuilder} based on the given information.
|
||||
|
@ -54,15 +77,18 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
|
|||
* @param shouldProcessSymbols True if symbols should be processed; otherwise, false
|
||||
* @param shouldCreateDylibSections True if memory blocks should be created for DYLIB sections;
|
||||
* otherwise, false
|
||||
* @param shouldAddRelocationEntries True to create a relocation entry for each fixed up pointer in pointer chain
|
||||
* @param log The log
|
||||
* @param monitor A cancelable task monitor
|
||||
*/
|
||||
protected DyldCacheProgramBuilder(Program program, ByteProvider provider, FileBytes fileBytes,
|
||||
boolean shouldProcessSymbols, boolean shouldCreateDylibSections, MessageLog log,
|
||||
boolean shouldProcessSymbols, boolean shouldCreateDylibSections,
|
||||
boolean shouldAddRelocationEntries, MessageLog log,
|
||||
TaskMonitor monitor) {
|
||||
super(program, provider, fileBytes, log, monitor);
|
||||
this.shouldProcessSymbols = shouldProcessSymbols;
|
||||
this.shouldCreateDylibSections = shouldCreateDylibSections;
|
||||
this.shouldAddRelocationEntries = shouldAddRelocationEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -74,15 +100,17 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
|
|||
* @param shouldProcessSymbols True if symbols should be processed; otherwise, false
|
||||
* @param shouldCreateDylibSections True if memory blocks should be created for DYLIB sections;
|
||||
* otherwise, false
|
||||
* @param addRelocationEntries True to create a relocation entry for each fixed up pointer in pointer chain
|
||||
* @param log The log
|
||||
* @param monitor A cancelable task monitor
|
||||
* @throws Exception if a problem occurs
|
||||
*/
|
||||
public static void buildProgram(Program program, ByteProvider provider, FileBytes fileBytes,
|
||||
boolean shouldProcessSymbols, boolean shouldCreateDylibSections, MessageLog log,
|
||||
TaskMonitor monitor) throws Exception {
|
||||
boolean shouldProcessSymbols, boolean shouldCreateDylibSections, boolean addRelocationEntries,
|
||||
MessageLog log, TaskMonitor monitor) throws Exception {
|
||||
DyldCacheProgramBuilder dyldCacheProgramBuilder = new DyldCacheProgramBuilder(program,
|
||||
provider, fileBytes, shouldProcessSymbols, shouldCreateDylibSections, log, monitor);
|
||||
provider, fileBytes, shouldProcessSymbols, shouldCreateDylibSections,
|
||||
addRelocationEntries, log, monitor);
|
||||
dyldCacheProgramBuilder.build();
|
||||
}
|
||||
|
||||
|
@ -97,9 +125,11 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
|
|||
|
||||
setDyldCacheImageBase();
|
||||
processDyldCacheMemoryBlocks();
|
||||
fixPageChains();
|
||||
markupHeaders();
|
||||
markupBranchIslands();
|
||||
createSymbols();
|
||||
|
||||
processDylibs();
|
||||
}
|
||||
|
||||
|
@ -212,6 +242,138 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes any chained pointers within each of the data pages.
|
||||
*
|
||||
* @throws MemoryAccessException if there was a problem reading/writing memory.
|
||||
* @throws CancelledException
|
||||
*/
|
||||
private void fixPageChains() throws MemoryAccessException, CancelledException {
|
||||
long fixedAddressCount = 0;
|
||||
|
||||
// locate slide Info
|
||||
DyldCacheSlideInfoCommon slideInfo = dyldCacheHeader.getSlideInfo();
|
||||
if (slideInfo == null || !(slideInfo instanceof DyldCacheSlideInfo2)) {
|
||||
log.appendMsg("Can't handle version " +slideInfo.getVersion() + " slide info, only version 2");
|
||||
return;
|
||||
}
|
||||
DyldCacheSlideInfo2 slideInfo2 = (DyldCacheSlideInfo2) slideInfo;
|
||||
|
||||
List<DyldCacheMappingInfo> mappingInfos = dyldCacheHeader.getMappingInfos();
|
||||
DyldCacheMappingInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY);
|
||||
long dataPageStart = dyldCacheMappingInfo.getAddress();
|
||||
long pageSize = slideInfo2.getPageSize();
|
||||
long pageStartsCount = slideInfo2.getPageStartsCount();
|
||||
|
||||
long deltaMask = slideInfo2.getDeltaMask();
|
||||
long deltaShift = Long.numberOfTrailingZeros(deltaMask);
|
||||
long valueAdd = slideInfo2.getValueAdd();
|
||||
|
||||
short[] pageEntries = slideInfo2.getPageStartsEntries();
|
||||
short[] extraEntries = slideInfo2.getPageExtrasEntries();
|
||||
|
||||
monitor.setMessage("Fixing chained data page pointers...");
|
||||
|
||||
monitor.setMaximum(pageStartsCount);
|
||||
for (int index=0; index < pageStartsCount; index++) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
long page = dataPageStart + (pageSize * index);
|
||||
|
||||
monitor.setProgress(index);
|
||||
|
||||
int pageEntry = ((int)pageEntries[index]) & 0xffff;
|
||||
if (pageEntry == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE) {
|
||||
continue;
|
||||
}
|
||||
if ((pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) != 0) {
|
||||
// go into extras and process list of chain entries for the same page
|
||||
int extraIndex = (pageEntry & CHAIN_OFFSET_MASK);
|
||||
do {
|
||||
pageEntry = ((int) extraEntries[extraIndex]) & 0xffff;
|
||||
long pageOffset = (pageEntry & CHAIN_OFFSET_MASK) * BYTES_PER_CHAIN_OFFSET;
|
||||
|
||||
fixedAddressCount += processPointerChain(page, pageOffset, deltaMask, deltaShift, valueAdd);
|
||||
extraIndex++;
|
||||
} while ((pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0);
|
||||
}
|
||||
else {
|
||||
long pageOffset = pageEntry * BYTES_PER_CHAIN_OFFSET;
|
||||
|
||||
fixedAddressCount += processPointerChain(page, pageOffset, deltaMask, deltaShift, valueAdd);
|
||||
}
|
||||
}
|
||||
|
||||
log.appendMsg("Fixed " + fixedAddressCount + " chained pointers. Creating Pointers");
|
||||
|
||||
monitor.setMessage("Created " + fixedAddressCount + " chained pointers");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes up any chained pointers, starting at the given address.
|
||||
*
|
||||
* @param page within data pages that has pointers to be unchained
|
||||
* @param nextOff offset within the page that is the chain start
|
||||
* @param deltaMask delta offset mask for each value
|
||||
* @param deltaShift shift needed for the deltaMask to extract the next offset
|
||||
* @param valueAdd value to be added to each chain pointer
|
||||
*
|
||||
* @return count of number of addresses fixed
|
||||
* @throws MemoryAccessException
|
||||
* @throws CancelledException
|
||||
*/
|
||||
private long processPointerChain(long page, long nextOff, long deltaMask, long deltaShift, long valueAdd)
|
||||
throws MemoryAccessException, CancelledException {
|
||||
// TODO: should the image base be used to perform the ASLR slide on the pointers.
|
||||
// currently image is kept at it's initial location with no ASLR.
|
||||
Address chainStart = memory.getProgram().getLanguage().getDefaultSpace().getAddress(page);
|
||||
|
||||
long fixedAddressCount = 0;
|
||||
|
||||
byte origBytes[] = new byte[8];
|
||||
|
||||
long valueMask = ~deltaMask;
|
||||
|
||||
long delta = -1;
|
||||
while (delta != 0) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
Address chainLoc = chainStart.add(nextOff);
|
||||
long chainValue = memory.getLong(chainLoc);
|
||||
|
||||
delta = (chainValue & deltaMask) >> deltaShift;
|
||||
chainValue = chainValue & valueMask;
|
||||
if (chainValue != 0) {
|
||||
chainValue += valueAdd;
|
||||
}
|
||||
|
||||
if (shouldAddRelocationEntries) {
|
||||
// Add entry to relocation table for the pointer fixup
|
||||
memory.getBytes(chainLoc, origBytes);
|
||||
program.getRelocationTable().add(chainLoc, (int) 1,
|
||||
new long[] { chainValue }, origBytes, null);
|
||||
}
|
||||
|
||||
memory.setLong(chainLoc, chainValue);
|
||||
|
||||
// create a pointer at the fixed up chain pointer location
|
||||
try {
|
||||
// don't use data utilities. does too much extra checking work
|
||||
listing.createData(chainLoc, Pointer64DataType.dataType);
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
// No worries, something presumably more important was there already
|
||||
}
|
||||
|
||||
fixedAddressCount++;
|
||||
|
||||
nextOff += (delta * 4);
|
||||
}
|
||||
|
||||
return fixedAddressCount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes the DYLD Cache's DYLIB files. This will mark up the DYLIB files, added them to the
|
||||
* program tree, and make memory blocks for them.
|
||||
|
|
|
@ -15,20 +15,51 @@
|
|||
*/
|
||||
package ghidra.program.database.mem;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.junit.*;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.app.plugin.core.memory.UninitializedBlockCmd;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressOverflowException;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.AddressRangeImpl;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.data.ArrayDataType;
|
||||
import ghidra.program.model.data.ByteDataType;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.PointerDataType;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Listing;
|
||||
import ghidra.program.model.listing.ProgramFragment;
|
||||
import ghidra.program.model.listing.ProgramModule;
|
||||
import ghidra.program.model.mem.LiveMemoryHandler;
|
||||
import ghidra.program.model.mem.LiveMemoryListener;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.mem.MemoryBlockException;
|
||||
import ghidra.program.model.mem.MemoryBlockSourceInfo;
|
||||
import ghidra.program.model.mem.MemoryBlockStub;
|
||||
import ghidra.program.model.mem.MemoryBlockType;
|
||||
import ghidra.program.model.mem.MemoryConflictException;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.ReferenceManager;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.ToyProgramBuilder;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
@ -316,6 +347,57 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertEquals(newBlock, block);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBlockByName() throws Exception {
|
||||
|
||||
MemoryBlock block1 = createBlock("Test1", addr(100), 100);
|
||||
MemoryBlock block2 = createBlock("Test2", addr(300), 100);
|
||||
|
||||
MemoryBlock block = mem.getBlock("Test1");
|
||||
assertEquals("Test1", block.getName());
|
||||
assertEquals("get same block", block, block1);
|
||||
|
||||
mem.split(block, addr(150));
|
||||
block = mem.getBlock("Test1");
|
||||
assertEquals("Test1", block.getName());
|
||||
assertEquals(50, block.getSize());
|
||||
|
||||
// non-existent block
|
||||
block = mem.getBlock("NoExist");
|
||||
assertNull(block);
|
||||
|
||||
program.endTransaction(transactionID, true);
|
||||
transactionID = program.startTransaction("Test");
|
||||
|
||||
// now exists
|
||||
mem.getBlock("Test1").setName("NoExist");
|
||||
// Test1 no longer exists
|
||||
assertNull("block deleted", mem.getBlock("Test1"));
|
||||
block = mem.getBlock("NoExist");
|
||||
assertEquals("NoExist", block.getName());
|
||||
|
||||
mem.removeBlock(block, new TaskMonitorAdapter());
|
||||
block = mem.getBlock("NoExist");
|
||||
assertNull("block should be deleted", block);
|
||||
|
||||
// Test1 still doesn't exist
|
||||
block = mem.getBlock("Test1");
|
||||
assertNull("block deleted", block);
|
||||
|
||||
block = mem.getBlock("Test2");
|
||||
assertEquals("Test2", block.getName());
|
||||
|
||||
program.endTransaction(transactionID, true);
|
||||
|
||||
program.undo();
|
||||
|
||||
// Test1 still doesn't exist
|
||||
block = mem.getBlock("Test1");
|
||||
assertNotNull("Undo, Test1 exists again", block);
|
||||
|
||||
transactionID = program.startTransaction("Test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSave() throws Exception {
|
||||
MemoryBlock block1 = createBlock("Test1", addr(0), 100);
|
||||
|
|
|
@ -19,15 +19,39 @@ import java.math.BigInteger;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressIterator;
|
||||
import ghidra.program.model.address.AddressOutOfBoundsException;
|
||||
import ghidra.program.model.address.AddressOverflowException;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.PointerDataType;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.model.lang.InstructionPrototype;
|
||||
import ghidra.program.model.lang.InsufficientBytesException;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.lang.UnknownContextException;
|
||||
import ghidra.program.model.lang.UnknownInstructionException;
|
||||
import ghidra.program.model.listing.ContextChangeException;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.ProgramContext;
|
||||
import ghidra.program.model.mem.ByteMemBufferImpl;
|
||||
import ghidra.program.model.mem.DumbMemBufferImpl;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.ProgramContextImpl;
|
||||
import ghidra.program.model.symbol.FlowType;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolType;
|
||||
|
||||
/**
|
||||
* PseudoDisassembler.java
|
||||
|
@ -71,6 +95,8 @@ public class PseudoDisassembler {
|
|||
|
||||
private boolean respectExecuteFlag = false;
|
||||
|
||||
private AddressSetView executeSet;
|
||||
|
||||
/**
|
||||
* Create a pseudo disassembler for the given program.
|
||||
*/
|
||||
|
@ -86,16 +112,16 @@ public class PseudoDisassembler {
|
|||
this.programContext = program.getProgramContext();
|
||||
}
|
||||
|
||||
public PseudoDisassembler(Language lang, Memory mem) {
|
||||
program = null;
|
||||
/**
|
||||
* @return cached addressSet of executable memory blocks
|
||||
*/
|
||||
private AddressSetView getExecuteSet() {
|
||||
if (executeSet != null) {
|
||||
return executeSet;
|
||||
}
|
||||
|
||||
this.language = lang;
|
||||
|
||||
this.memory = mem;
|
||||
|
||||
pointerSize = language.getDefaultSpace().getPointerSize();
|
||||
|
||||
programContext = new ProgramContextImpl(language.getRegisters());
|
||||
executeSet = memory.getExecuteSet();
|
||||
return executeSet;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -615,8 +641,6 @@ public class PseudoDisassembler {
|
|||
return false;
|
||||
}
|
||||
|
||||
AddressSetView executeSet = memory.getExecuteSet();
|
||||
|
||||
RepeatInstructionByteTracker repeatInstructionByteTracker =
|
||||
new RepeatInstructionByteTracker(MAX_REPEAT_BYTES_LIMIT, null);
|
||||
|
||||
|
@ -777,8 +801,8 @@ public class PseudoDisassembler {
|
|||
}
|
||||
}
|
||||
// if respecting execute flag on memory, test to make sure we did flow into non-execute memory
|
||||
if (respectExecuteFlag && !executeSet.isEmpty() &&
|
||||
!executeSet.contains(flows[j])) {
|
||||
AddressSetView execSet = getExecuteSet();
|
||||
if (respectExecuteFlag && !execSet.isEmpty() && !execSet.contains(flows[j])) {
|
||||
if (!flows[j].isExternalAddress()) {
|
||||
MemoryBlock block = memory.getBlock(flows[j]);
|
||||
// flowing into non-executable, but readable memory is bad
|
||||
|
@ -878,8 +902,8 @@ public class PseudoDisassembler {
|
|||
}
|
||||
|
||||
// check that body does not wander into non-executable memory
|
||||
AddressSetView executeSet = program.getMemory().getExecuteSet();
|
||||
if (respectExecuteFlag && !executeSet.isEmpty() && !body.subtract(executeSet).isEmpty()) {
|
||||
AddressSetView execSet = getExecuteSet();
|
||||
if (respectExecuteFlag && !execSet.isEmpty() && !body.subtract(execSet).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
private MemoryBlock lastBlock;// the last accessed block
|
||||
private LiveMemoryHandler liveMemory;
|
||||
|
||||
// lazy hashmap of block names to blocks, must be reloaded if blocks are removed or added
|
||||
private HashMap<String,MemoryBlock> nameBlockMap = new HashMap<String, MemoryBlock>();
|
||||
private final static MemoryBlock NoBlock = new MemoryBlockStub(); // placeholder for no block, not given out
|
||||
|
||||
Lock lock;
|
||||
private Set<MemoryBlock> potentialOverlappingBlocks;
|
||||
|
||||
|
@ -182,6 +186,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
lastBlock = null;
|
||||
blocks = newBlocks;
|
||||
addrMap.memoryMapChanged(this);
|
||||
nameBlockMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public void setLanguage(Language newLanguage) {
|
||||
|
@ -302,11 +307,25 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
*/
|
||||
@Override
|
||||
public synchronized MemoryBlock getBlock(String blockName) {
|
||||
// find block that might have been cached from previous call
|
||||
MemoryBlock memoryBlock = nameBlockMap.get(blockName);
|
||||
if (memoryBlock != null) {
|
||||
if (memoryBlock == NoBlock) {
|
||||
// found placeholder, have searched and found nothing before
|
||||
return null;
|
||||
}
|
||||
return memoryBlock;
|
||||
}
|
||||
|
||||
for (MemoryBlock block : blocks) {
|
||||
if (block.getName().equals(blockName)) {
|
||||
nameBlockMap.put(blockName, block);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
|
||||
// store placeholder there is no memory block with that name
|
||||
nameBlockMap.put(blockName, NoBlock);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -374,6 +393,9 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
if (program != null) {
|
||||
program.setChanged(ChangeManager.DOCR_MEMORY_BLOCK_CHANGED, block, null);
|
||||
}
|
||||
|
||||
// name could have changed
|
||||
nameBlockMap = new HashMap<>();
|
||||
}
|
||||
|
||||
void fireBytesChanged(Address addr, int count) {
|
||||
|
|
|
@ -390,7 +390,7 @@ class BigRefListV0 extends RefList {
|
|||
if (maxLevel < level) {
|
||||
maxLevel = level;
|
||||
}
|
||||
if (level > currentRefLevel) {
|
||||
if (level >= currentRefLevel) {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -342,7 +342,7 @@ class RefListV0 extends RefList {
|
|||
if (maxLevel < level) {
|
||||
maxLevel = level;
|
||||
}
|
||||
if (level > currentRefLevel) {
|
||||
if (level >= currentRefLevel) {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue