mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Merge branch 'GP-5396_ryanmkurtz_libobjc'
This commit is contained in:
commit
e2435030bc
7 changed files with 209 additions and 72 deletions
|
@ -110,7 +110,7 @@ public class DyldChainedFixups {
|
||||||
}
|
}
|
||||||
|
|
||||||
fixups.add(new DyldFixup(chainLoc, newChainValue, DyldChainedPtr.getSize(pointerFormat),
|
fixups.add(new DyldFixup(chainLoc, newChainValue, DyldChainedPtr.getSize(pointerFormat),
|
||||||
symbol, libOrdinal));
|
symbol.getName(), libOrdinal));
|
||||||
|
|
||||||
next = DyldChainedPtr.getNext(pointerFormat, chainValue);
|
next = DyldChainedPtr.getNext(pointerFormat, chainValue);
|
||||||
nextOff += next * DyldChainedPtr.getStride(pointerFormat);
|
nextOff += next * DyldChainedPtr.getStride(pointerFormat);
|
||||||
|
@ -161,7 +161,7 @@ public class DyldChainedFixups {
|
||||||
finally {
|
finally {
|
||||||
program.getRelocationTable()
|
program.getRelocationTable()
|
||||||
.add(addr, status, 0, new long[] { fixup.value() }, fixup.size(),
|
.add(addr, status, 0, new long[] { fixup.value() }, fixup.size(),
|
||||||
fixup.symbol() != null ? fixup.symbol().getName() : null);
|
fixup.symbol() != null ? fixup.symbol() : null);
|
||||||
}
|
}
|
||||||
if (fixup.symbol() != null && fixup.libOrdinal() != null) {
|
if (fixup.symbol() != null && fixup.libOrdinal() != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -170,7 +170,7 @@ public class DyldChainedFixups {
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
log.appendMsg("WARNING: Problem fixing up symbol '%s' - %s"
|
log.appendMsg("WARNING: Problem fixing up symbol '%s' - %s"
|
||||||
.formatted(fixup.symbol().getName(), e.getMessage()));
|
.formatted(fixup.symbol(), e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.macho.dyld;
|
package ghidra.app.util.bin.format.macho.dyld;
|
||||||
|
|
||||||
import ghidra.program.model.symbol.Symbol;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores information needed to perform a dyld pointer fixup
|
* Stores information needed to perform a dyld pointer fixup
|
||||||
*
|
*
|
||||||
* @param offset The offset of where to perform the fixup (from some base address/index)
|
* @param offset The offset of where to perform the fixup (from some base address/index)
|
||||||
* @param value The fixed up value
|
* @param value The fixed up value
|
||||||
* @param size The size of the fixup in bytes
|
* @param size The size of the fixup in bytes
|
||||||
* @param symbol The {@link Symbol} associated with the fixup (could be null)
|
* @param symbol The symbol associated with the fixup (could be null)
|
||||||
* @param libOrdinal The library ordinal associated with the fixup (could be null)
|
* @param libOrdinal The library ordinal associated with the fixup (could be null)
|
||||||
*/
|
*/
|
||||||
public record DyldFixup(long offset, long value, int size, Symbol symbol, Integer libOrdinal) {}
|
public record DyldFixup(long offset, long value, int size, String symbol, Integer libOrdinal) {}
|
||||||
|
|
|
@ -135,7 +135,7 @@ public class MachoProgramBuilder {
|
||||||
processMemoryBlocks(machoHeader, provider.getName(), true, true);
|
processMemoryBlocks(machoHeader, provider.getName(), true, true);
|
||||||
|
|
||||||
// Process load commands
|
// Process load commands
|
||||||
processEntryPoint();
|
processEntryPoint(provider.getName());
|
||||||
boolean exportsFound = processExports(machoHeader);
|
boolean exportsFound = processExports(machoHeader);
|
||||||
processSymbolTables(machoHeader, !exportsFound);
|
processSymbolTables(machoHeader, !exportsFound);
|
||||||
processStubs();
|
processStubs();
|
||||||
|
@ -470,9 +470,10 @@ public class MachoProgramBuilder {
|
||||||
* We will sort the discovered entry points by priorities assigned to each type of load
|
* We will sort the discovered entry points by priorities assigned to each type of load
|
||||||
* command, and only use the one with the highest priority.
|
* command, and only use the one with the highest priority.
|
||||||
*
|
*
|
||||||
|
* @param source A name that represents where the memory blocks came from.
|
||||||
* @throws Exception If there was a problem discovering or setting the entry point.
|
* @throws Exception If there was a problem discovering or setting the entry point.
|
||||||
*/
|
*/
|
||||||
protected void processEntryPoint() throws Exception {
|
protected void processEntryPoint(String source) throws Exception {
|
||||||
monitor.setMessage("Processing entry point...");
|
monitor.setMessage("Processing entry point...");
|
||||||
|
|
||||||
final int LC_MAIN_PRIORITY = 1;
|
final int LC_MAIN_PRIORITY = 1;
|
||||||
|
@ -513,14 +514,11 @@ public class MachoProgramBuilder {
|
||||||
realEntryFound = true;
|
realEntryFound = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.appendMsg("Ignoring entry point at: " + addr);
|
log.appendMsg("Ignoring entry point at " + addr + " in " + source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
log.appendMsg("Unable to determine entry point.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean processExports(MachHeader header) throws Exception {
|
protected boolean processExports(MachHeader header) throws Exception {
|
||||||
|
@ -965,7 +963,7 @@ public class MachoProgramBuilder {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fixupExternalLibrary(program, libraryPaths, binding.getLibraryOrdinal(),
|
fixupExternalLibrary(program, libraryPaths, binding.getLibraryOrdinal(),
|
||||||
symbol);
|
symbol.getName());
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
log.appendMsg("WARNING: Problem fixing up symbol '%s' - %s"
|
log.appendMsg("WARNING: Problem fixing up symbol '%s' - %s"
|
||||||
|
@ -1897,11 +1895,11 @@ public class MachoProgramBuilder {
|
||||||
* @param program The {@link Program}
|
* @param program The {@link Program}
|
||||||
* @param libraryPaths A {@link List} of library paths
|
* @param libraryPaths A {@link List} of library paths
|
||||||
* @param libraryOrdinal The library ordinal
|
* @param libraryOrdinal The library ordinal
|
||||||
* @param symbol The {@link Symbol}
|
* @param symbol The symbol
|
||||||
* @throws Exception if an unexpected problem occurs
|
* @throws Exception if an unexpected problem occurs
|
||||||
*/
|
*/
|
||||||
public static void fixupExternalLibrary(Program program, List<String> libraryPaths,
|
public static void fixupExternalLibrary(Program program, List<String> libraryPaths,
|
||||||
int libraryOrdinal, Symbol symbol) throws Exception {
|
int libraryOrdinal, String symbol) throws Exception {
|
||||||
ExternalManager extManager = program.getExternalManager();
|
ExternalManager extManager = program.getExternalManager();
|
||||||
int libraryIndex = libraryOrdinal - 1;
|
int libraryIndex = libraryOrdinal - 1;
|
||||||
if (libraryIndex < 0 || libraryIndex >= libraryPaths.size()) {
|
if (libraryIndex < 0 || libraryIndex >= libraryPaths.size()) {
|
||||||
|
@ -1915,10 +1913,10 @@ public class MachoProgramBuilder {
|
||||||
"Library '%s' not found in external program list".formatted(libraryName));
|
"Library '%s' not found in external program list".formatted(libraryName));
|
||||||
}
|
}
|
||||||
ExternalLocation loc =
|
ExternalLocation loc =
|
||||||
extManager.getUniqueExternalLocation(Library.UNKNOWN, symbol.getName());
|
extManager.getUniqueExternalLocation(Library.UNKNOWN, symbol);
|
||||||
if (loc != null) {
|
if (loc != null) {
|
||||||
try {
|
try {
|
||||||
loc.setName(library, symbol.getName(), SourceType.IMPORTED);
|
loc.setName(library, symbol, SourceType.IMPORTED);
|
||||||
}
|
}
|
||||||
catch (InvalidInputException e) {
|
catch (InvalidInputException e) {
|
||||||
throw new Exception("Symbol name contains illegal characters");
|
throw new Exception("Symbol name contains illegal characters");
|
||||||
|
|
|
@ -91,4 +91,8 @@ public abstract class AbstractFileSystem<METADATATYPE> implements GFileSystem {
|
||||||
return fsIndex.resolveSymlinks(file);
|
return fsIndex.resolveSymlinks(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,17 @@ package ghidra.app.util.opinion;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import ghidra.app.util.MemoryBlockUtils;
|
import ghidra.app.util.*;
|
||||||
import ghidra.app.util.Option;
|
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
|
import ghidra.app.util.bin.format.macho.dyld.DyldCacheMappingAndSlideInfo;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.file.formats.ios.dyldcache.DyldCacheExtractor;
|
import ghidra.file.formats.ios.dyldcache.DyldCacheExtractor;
|
||||||
|
import ghidra.file.formats.ios.dyldcache.DyldCacheFileSystem;
|
||||||
|
import ghidra.formats.gfilesystem.*;
|
||||||
import ghidra.framework.model.DomainObject;
|
import ghidra.framework.model.DomainObject;
|
||||||
import ghidra.framework.model.Project;
|
import ghidra.framework.model.Project;
|
||||||
import ghidra.program.database.mem.FileBytes;
|
import ghidra.program.database.mem.FileBytes;
|
||||||
|
import ghidra.program.model.listing.Group;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
@ -37,6 +40,30 @@ public class DyldCacheExtractLoader extends MachoLoader {
|
||||||
|
|
||||||
public final static String DYLD_CACHE_EXTRACT_NAME = "Extracted DYLD Component";
|
public final static String DYLD_CACHE_EXTRACT_NAME = "Extracted DYLD Component";
|
||||||
|
|
||||||
|
static final String LIBOBJC_OPTION_NAME = "Add libobjc.dylib";
|
||||||
|
static final boolean LIBOBJC_OPTION_DEFAULT = true;
|
||||||
|
|
||||||
|
static final String AUTH_DATA_OPTION_NAME = "Add AUTH_DATA";
|
||||||
|
static final boolean AUTH_DATA_OPTION_DEFAULT = false;
|
||||||
|
|
||||||
|
static final String DIRTY_DATA_OPTION_NAME = "Add DIRTY_DATA";
|
||||||
|
static final boolean DIRTY_DATA_OPTION_DEFAULT = false;
|
||||||
|
|
||||||
|
static final String CONST_DATA_OPTION_NAME = "Add CONST_DATA";
|
||||||
|
static final boolean CONST_DATA_OPTION_DEFAULT = true;
|
||||||
|
|
||||||
|
static final String TEXT_STUBS_OPTION_NAME = "Add TEXT_STUBS";
|
||||||
|
static final boolean TEXT_STUBS_OPTION_DEFAULT = true;
|
||||||
|
|
||||||
|
static final String CONFIG_DATA_OPTION_NAME = "Add CONFIG_DATA";
|
||||||
|
static final boolean CONFIG_DATA_OPTION_DEFAULT = false;
|
||||||
|
|
||||||
|
static final String READ_ONLY_DATA_OPTION_NAME = "Add READ_ONLY_DATA";
|
||||||
|
static final boolean READ_ONLY_DATA_OPTION_DEFAULT = true;
|
||||||
|
|
||||||
|
static final String CONST_TPRO_DATA_OPTION_NAME = "Add CONST_TPRO_DATA";
|
||||||
|
static final boolean CONST_TPRO_DATA_OPTION_DEFAULT = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
|
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
|
||||||
if (provider.length() >= DyldCacheExtractor.FOOTER_V1.length) {
|
if (provider.length() >= DyldCacheExtractor.FOOTER_V1.length) {
|
||||||
|
@ -56,6 +83,7 @@ public class DyldCacheExtractLoader extends MachoLoader {
|
||||||
try {
|
try {
|
||||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||||
MachoExtractProgramBuilder.buildProgram(program, provider, fileBytes, log, monitor);
|
MachoExtractProgramBuilder.buildProgram(program, provider, fileBytes, log, monitor);
|
||||||
|
addOptionalComponents(program, options, log, monitor);
|
||||||
}
|
}
|
||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
return;
|
return;
|
||||||
|
@ -93,7 +121,46 @@ public class DyldCacheExtractLoader extends MachoLoader {
|
||||||
@Override
|
@Override
|
||||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||||
DomainObject domainObject, boolean loadIntoProgram) {
|
DomainObject domainObject, boolean loadIntoProgram) {
|
||||||
return List.of();
|
List<Option> list = new ArrayList<>();
|
||||||
|
list.add(new Option(LIBOBJC_OPTION_NAME, !loadIntoProgram && LIBOBJC_OPTION_DEFAULT,
|
||||||
|
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-libobjc"));
|
||||||
|
list.add(new Option(AUTH_DATA_OPTION_NAME, !loadIntoProgram && AUTH_DATA_OPTION_DEFAULT,
|
||||||
|
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-authData"));
|
||||||
|
list.add(new Option(DIRTY_DATA_OPTION_NAME, !loadIntoProgram && DIRTY_DATA_OPTION_DEFAULT,
|
||||||
|
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-dirtyData"));
|
||||||
|
list.add(new Option(CONST_DATA_OPTION_NAME, !loadIntoProgram && CONST_DATA_OPTION_DEFAULT,
|
||||||
|
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-constData"));
|
||||||
|
list.add(new Option(TEXT_STUBS_OPTION_NAME, !loadIntoProgram && TEXT_STUBS_OPTION_DEFAULT,
|
||||||
|
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-textStubs"));
|
||||||
|
list.add(new Option(CONFIG_DATA_OPTION_NAME, !loadIntoProgram && CONFIG_DATA_OPTION_DEFAULT,
|
||||||
|
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-configData"));
|
||||||
|
list.add(new Option(READ_ONLY_DATA_OPTION_NAME,
|
||||||
|
!loadIntoProgram && READ_ONLY_DATA_OPTION_DEFAULT, Boolean.class,
|
||||||
|
Loader.COMMAND_LINE_ARG_PREFIX + "-readOnlyData"));
|
||||||
|
list.add(new Option(CONST_TPRO_DATA_OPTION_NAME,
|
||||||
|
!loadIntoProgram && CONST_TPRO_DATA_OPTION_DEFAULT, Boolean.class,
|
||||||
|
Loader.COMMAND_LINE_ARG_PREFIX + "-constTproData"));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||||
|
Program program) {
|
||||||
|
if (options != null) {
|
||||||
|
for (Option option : options) {
|
||||||
|
String name = option.getName();
|
||||||
|
if (name.equals(LIBOBJC_OPTION_NAME) || name.equals(AUTH_DATA_OPTION_NAME) ||
|
||||||
|
name.equals(DIRTY_DATA_OPTION_NAME) || name.equals(CONST_DATA_OPTION_NAME) ||
|
||||||
|
name.equals(TEXT_STUBS_OPTION_NAME) || name.equals(CONFIG_DATA_OPTION_NAME) ||
|
||||||
|
name.equals(READ_ONLY_DATA_OPTION_NAME) ||
|
||||||
|
name.equals(CONST_TPRO_DATA_OPTION_NAME)) {
|
||||||
|
if (!Boolean.class.isAssignableFrom(option.getValueClass())) {
|
||||||
|
return "Invalid type for option: " + name + " - " + option.getValueClass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -107,4 +174,77 @@ public class DyldCacheExtractLoader extends MachoLoader {
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addOptionalComponents(Program program, List<Option> options, MessageLog log,
|
||||||
|
TaskMonitor monitor) throws Exception {
|
||||||
|
boolean addLibobjc =
|
||||||
|
OptionUtils.getOption(LIBOBJC_OPTION_NAME, options, LIBOBJC_OPTION_DEFAULT);
|
||||||
|
long flags = 0;
|
||||||
|
if (OptionUtils.getOption(AUTH_DATA_OPTION_NAME, options, AUTH_DATA_OPTION_DEFAULT)) {
|
||||||
|
flags |= DyldCacheMappingAndSlideInfo.DYLD_CACHE_MAPPING_AUTH_DATA;
|
||||||
|
}
|
||||||
|
if (OptionUtils.getOption(DIRTY_DATA_OPTION_NAME, options, DIRTY_DATA_OPTION_DEFAULT)) {
|
||||||
|
flags |= DyldCacheMappingAndSlideInfo.DYLD_CACHE_MAPPING_DIRTY_DATA;
|
||||||
|
}
|
||||||
|
if (OptionUtils.getOption(CONST_DATA_OPTION_NAME, options, CONST_DATA_OPTION_DEFAULT)) {
|
||||||
|
flags |= DyldCacheMappingAndSlideInfo.DYLD_CACHE_MAPPING_CONST_DATA;
|
||||||
|
}
|
||||||
|
if (OptionUtils.getOption(TEXT_STUBS_OPTION_NAME, options, TEXT_STUBS_OPTION_DEFAULT)) {
|
||||||
|
flags |= DyldCacheMappingAndSlideInfo.DYLD_CACHE_MAPPING_TEXT_STUBS;
|
||||||
|
}
|
||||||
|
if (OptionUtils.getOption(CONFIG_DATA_OPTION_NAME, options, CONFIG_DATA_OPTION_DEFAULT)) {
|
||||||
|
flags |= DyldCacheMappingAndSlideInfo.DYLD_CACHE_DYNAMIC_CONFIG_DATA;
|
||||||
|
}
|
||||||
|
if (OptionUtils.getOption(READ_ONLY_DATA_OPTION_NAME, options,
|
||||||
|
READ_ONLY_DATA_OPTION_DEFAULT)) {
|
||||||
|
flags |= DyldCacheMappingAndSlideInfo.DYLD_CACHE_READ_ONLY_DATA;
|
||||||
|
}
|
||||||
|
if (OptionUtils.getOption(CONST_TPRO_DATA_OPTION_NAME, options,
|
||||||
|
CONST_TPRO_DATA_OPTION_DEFAULT)) {
|
||||||
|
flags |= DyldCacheMappingAndSlideInfo.DYLD_CACHE_MAPPING_CONST_TPRO_DATA;
|
||||||
|
}
|
||||||
|
if (!addLibobjc && flags == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try (FileSystemRef fsRef = openDyldCache(program, monitor)) {
|
||||||
|
DyldCacheFileSystem fs = (DyldCacheFileSystem) fsRef.getFilesystem();
|
||||||
|
Set<GFile> files = new HashSet<>();
|
||||||
|
if (addLibobjc) {
|
||||||
|
Optional.ofNullable(fs.lookup("/usr/lib/libobjc.A.dylib")).ifPresent(files::add);
|
||||||
|
}
|
||||||
|
files.addAll(fs.getFiles(flags));
|
||||||
|
for (GFile file : files) {
|
||||||
|
Group[] children = program.getListing().getDefaultRootModule().getChildren();
|
||||||
|
if (Arrays.stream(children).noneMatch(e -> e.getName().contains(file.getPath()))) {
|
||||||
|
ByteProvider p = fs.getByteProvider(file, monitor);
|
||||||
|
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, p, monitor);
|
||||||
|
MachoExtractProgramBuilder.buildProgram(program, p, fileBytes, log, monitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to open the given {@link Program}'s originating {@link DyldCacheFileSystem}
|
||||||
|
*
|
||||||
|
* @param program The {@link Program}
|
||||||
|
* @param monitor A {@link TaskMonitor}
|
||||||
|
* @return A {@link FileSystemRef file system reference} to the open {@link DyldCacheFileSystem}
|
||||||
|
* @throws IOException if an FSRL or IO-related error occurred
|
||||||
|
* @throws CancelledException if the user cancelled the operation
|
||||||
|
*/
|
||||||
|
public static FileSystemRef openDyldCache(Program program, TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException {
|
||||||
|
FSRL fsrl = FSRL.fromProgram(program);
|
||||||
|
if (fsrl == null) {
|
||||||
|
throw new IOException("The program does not have an FSRL property");
|
||||||
|
}
|
||||||
|
String requiredProtocol = DyldCacheFileSystem.DYLD_CACHE_FSTYPE;
|
||||||
|
if (!fsrl.getFS().getProtocol().equals(requiredProtocol)) {
|
||||||
|
throw new IOException("The program's FSRL protocol is '%s' but '%s' is required"
|
||||||
|
.formatted(fsrl.getFS().getProtocol(), requiredProtocol));
|
||||||
|
}
|
||||||
|
FSRLRoot fsrlRoot = fsrl.getFS();
|
||||||
|
return FileSystemService.getInstance().getFilesystem(fsrlRoot, monitor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,7 @@ package ghidra.file.formats.ios.dyldcache;
|
||||||
import static ghidra.formats.gfilesystem.fileinfo.FileAttributeType.*;
|
import static ghidra.formats.gfilesystem.fileinfo.FileAttributeType.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.google.common.collect.*;
|
import com.google.common.collect.*;
|
||||||
|
|
||||||
|
@ -97,8 +96,7 @@ public class DyldCacheFileSystem extends AbstractFileSystem<DyldCacheEntry> {
|
||||||
new DyldCacheEntry(mappedImage.getPath(), i, rangeSet, null, null, -1);
|
new DyldCacheEntry(mappedImage.getPath(), i, rangeSet, null, null, -1);
|
||||||
rangeSet.asRanges().forEach(r -> rangeMap.put(r, entry));
|
rangeSet.asRanges().forEach(r -> rangeMap.put(r, entry));
|
||||||
allDylibRanges.addAll(rangeSet);
|
allDylibRanges.addAll(rangeSet);
|
||||||
fsIndex.storeFile(mappedImage.getPath(), fsIndex.getFileCount(), false, -1,
|
fsIndex.storeFile(mappedImage.getPath(), fsIndex.getFileCount(), false, -1, entry);
|
||||||
entry);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,6 +180,23 @@ public class DyldCacheFileSystem extends AbstractFileSystem<DyldCacheEntry> {
|
||||||
return entry != null ? entry.path() : null;
|
return entry != null ? entry.path() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a {@link List} of {@link GFile files} that have the given mapping flags
|
||||||
|
*
|
||||||
|
* @param flags The desired flags
|
||||||
|
* @return A {@link List} of {@link GFile files} that have the given mapping flags
|
||||||
|
*/
|
||||||
|
public List<GFile> getFiles(long flags) {
|
||||||
|
List<GFile> files = new ArrayList<>();
|
||||||
|
for (DyldCacheEntry entry : rangeMap.asMapOfRanges().values()) {
|
||||||
|
DyldCacheMappingAndSlideInfo mappingAndSlideInfo = entry.mappingAndSlideInfo();
|
||||||
|
if (mappingAndSlideInfo != null && (flags & mappingAndSlideInfo.getFlags()) != 0) {
|
||||||
|
Optional.ofNullable(lookup(entry.path())).ifPresent(files::add);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) {
|
public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) {
|
||||||
FileAttributes result = new FileAttributes();
|
FileAttributes result = new FileAttributes();
|
||||||
|
@ -253,6 +268,7 @@ public class DyldCacheFileSystem extends AbstractFileSystem<DyldCacheEntry> {
|
||||||
*
|
*
|
||||||
* @param dyldCacheName The name of the DYLD Cache
|
* @param dyldCacheName The name of the DYLD Cache
|
||||||
* @param mappingInfo the mapping info
|
* @param mappingInfo the mapping info
|
||||||
|
* @param mappingAndSlideInfo the mapping and slide info (could be null)
|
||||||
* @param mappingIndex The mapping index
|
* @param mappingIndex The mapping index
|
||||||
* @return The DYLD component path of the given DYLD component
|
* @return The DYLD component path of the given DYLD component
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -21,9 +21,10 @@ import docking.action.builder.ActionBuilder;
|
||||||
import ghidra.app.CorePluginPackage;
|
import ghidra.app.CorePluginPackage;
|
||||||
import ghidra.app.context.ProgramLocationActionContext;
|
import ghidra.app.context.ProgramLocationActionContext;
|
||||||
import ghidra.app.plugin.PluginCategoryNames;
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
|
import ghidra.app.util.dialog.AskAddrDialog;
|
||||||
import ghidra.app.util.opinion.DyldCacheExtractLoader;
|
import ghidra.app.util.opinion.DyldCacheExtractLoader;
|
||||||
import ghidra.file.formats.ios.dyldcache.DyldCacheFileSystem;
|
import ghidra.file.formats.ios.dyldcache.DyldCacheFileSystem;
|
||||||
import ghidra.formats.gfilesystem.*;
|
import ghidra.formats.gfilesystem.FileSystemRef;
|
||||||
import ghidra.framework.plugintool.*;
|
import ghidra.framework.plugintool.*;
|
||||||
import ghidra.framework.plugintool.util.PluginStatus;
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
import ghidra.plugin.importer.ImporterUtilities;
|
import ghidra.plugin.importer.ImporterUtilities;
|
||||||
|
@ -63,15 +64,15 @@ public class DyldCacheBuilderPlugin extends Plugin {
|
||||||
protected void init() {
|
protected void init() {
|
||||||
super.init();
|
super.init();
|
||||||
|
|
||||||
String actionName = "Add To Program";
|
final String addActionName = "Add To Program";
|
||||||
new ActionBuilder(actionName, getName())
|
new ActionBuilder(addActionName, getName())
|
||||||
.withContext(ProgramLocationActionContext.class)
|
.withContext(ProgramLocationActionContext.class)
|
||||||
.enabledWhen(p -> p.getProgram()
|
.enabledWhen(context -> context.getProgram()
|
||||||
.getExecutableFormat()
|
.getExecutableFormat()
|
||||||
.equals(DyldCacheExtractLoader.DYLD_CACHE_EXTRACT_NAME))
|
.equals(DyldCacheExtractLoader.DYLD_CACHE_EXTRACT_NAME))
|
||||||
.onAction(plac -> TaskLauncher.launchModal(actionName,
|
.onAction(context -> TaskLauncher.launchModal(addActionName,
|
||||||
monitor -> addMissingDyldCacheComponent(plac.getLocation(), monitor)))
|
monitor -> addMissingDyldCacheComponent(context.getLocation(), monitor)))
|
||||||
.popupMenuPath("References", actionName)
|
.popupMenuPath("References", addActionName)
|
||||||
.popupMenuGroup("Add")
|
.popupMenuGroup("Add")
|
||||||
.helpLocation(new HelpLocation("ImporterPlugin", "Add_To_Program"))
|
.helpLocation(new HelpLocation("ImporterPlugin", "Add_To_Program"))
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
@ -86,31 +87,35 @@ public class DyldCacheBuilderPlugin extends Plugin {
|
||||||
*/
|
*/
|
||||||
private void addMissingDyldCacheComponent(ProgramLocation location, TaskMonitor monitor) {
|
private void addMissingDyldCacheComponent(ProgramLocation location, TaskMonitor monitor) {
|
||||||
Program program = location.getProgram();
|
Program program = location.getProgram();
|
||||||
Address refAddress = location.getRefAddress();
|
Address address = location.getRefAddress();
|
||||||
if (refAddress == null) {
|
if (address == null) {
|
||||||
Msg.showInfo(this, null, name, "No referenced address selected");
|
AskAddrDialog dialog = new AskAddrDialog(name, "Enter address", program, null);
|
||||||
return;
|
if (dialog.isCanceled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
address = dialog.getValueAsAddress();
|
||||||
}
|
}
|
||||||
if (refAddress.getAddressSpace().isExternalSpace()) {
|
if (address.getAddressSpace().isExternalSpace()) {
|
||||||
Msg.showInfo(this, null, name, "External locations are not currently supported");
|
Msg.showInfo(this, null, name, "External locations are not currently supported");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (program.getMemory().contains(refAddress)) {
|
if (program.getMemory().contains(address)) {
|
||||||
Msg.showInfo(this, null, name, "Referenced address already exists in memory");
|
Msg.showInfo(this, null, name,
|
||||||
|
"Address %s already exists in memory".formatted(address));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (FileSystemRef fsRef = openDyldCache(program, monitor)) {
|
try (FileSystemRef fsRef = DyldCacheExtractLoader.openDyldCache(program, monitor)) {
|
||||||
DyldCacheFileSystem fs = (DyldCacheFileSystem) fsRef.getFilesystem();
|
DyldCacheFileSystem fs = (DyldCacheFileSystem) fsRef.getFilesystem();
|
||||||
long refAddr = refAddress.getOffset();
|
long offset = address.getOffset();
|
||||||
String fsPath = fs.findAddress(refAddr);
|
String fsPath = fs.findAddress(offset);
|
||||||
if (fsPath != null) {
|
if (fsPath != null) {
|
||||||
ImporterUtilities.showAddToProgramDialog(fs.getFSRL().appendPath(fsPath), program,
|
ImporterUtilities.showAddToProgramDialog(fs.getFSRL().appendPath(fsPath), program,
|
||||||
tool, monitor);
|
tool, monitor);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Msg.showInfo(this, null, name,
|
Msg.showInfo(this, null, name,
|
||||||
"Address %s not found in %s".formatted(refAddress, fs.toString()));
|
"Address %s not found in %s".formatted(address, fs.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
|
@ -120,28 +125,4 @@ public class DyldCacheBuilderPlugin extends Plugin {
|
||||||
Msg.showError(this, null, name, e.getMessage(), e);
|
Msg.showError(this, null, name, e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to open the given {@link Program}'s originating {@link DyldCacheFileSystem}
|
|
||||||
*
|
|
||||||
* @param program The {@link Program}
|
|
||||||
* @param monitor A {@link TaskMonitor}
|
|
||||||
* @return A {@link FileSystemRef file system reference} to the open {@link DyldCacheFileSystem}
|
|
||||||
* @throws IOException if an FSRL or IO-related error occurred
|
|
||||||
* @throws CancelledException if the user cancelled the operation
|
|
||||||
*/
|
|
||||||
private FileSystemRef openDyldCache(Program program, TaskMonitor monitor)
|
|
||||||
throws IOException, CancelledException {
|
|
||||||
FSRL fsrl = FSRL.fromProgram(program);
|
|
||||||
if (fsrl == null) {
|
|
||||||
throw new IOException("The program does not have an FSRL property");
|
|
||||||
}
|
|
||||||
String requiredProtocol = DyldCacheFileSystem.DYLD_CACHE_FSTYPE;
|
|
||||||
if (!fsrl.getFS().getProtocol().equals(requiredProtocol)) {
|
|
||||||
throw new IOException("The program's FSRL protocol is '%s' but '%s' is required"
|
|
||||||
.formatted(fsrl.getFS().getProtocol(), requiredProtocol));
|
|
||||||
}
|
|
||||||
FSRLRoot fsrlRoot = fsrl.getFS();
|
|
||||||
return FileSystemService.getInstance().getFilesystem(fsrlRoot, monitor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue