GP-5343: Importer filesystem mirroring

This commit is contained in:
Ryan Kurtz
2025-07-10 14:35:04 -04:00
parent 8c4e368fb2
commit c0fe84f0bd
68 changed files with 1494 additions and 1359 deletions
@@ -21,7 +21,6 @@ import java.util.*;
import org.jdom.*;
import org.jdom.input.SAXBuilder;
import ghidra.app.util.Option;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.MessageLog;
@@ -37,7 +36,6 @@ import ghidra.file.formats.android.xml.AndroidXmlFileSystem;
import ghidra.file.formats.zip.ZipFileSystem;
import ghidra.formats.gfilesystem.FileSystemService;
import ghidra.formats.gfilesystem.GFile;
import ghidra.framework.model.Project;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@@ -64,15 +62,15 @@ public class ApkLoader extends DexLoader {
}
@Override
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
Project project, String programFolderPath, LoadSpec loadSpec, List<Option> options,
MessageLog log, Object consumer, TaskMonitor monitor)
protected List<Loaded<Program>> loadProgram(ImporterSettings settings)
throws IOException, LoadException, CancelledException {
MessageLog log = settings.log();
TaskMonitor monitor = settings.monitor();
boolean success = false;
List<Loaded<Program>> allLoadedPrograms = new ArrayList<>();
int dexIndex = 1;//DEX file numbering starts at 1
try (ZipFileSystem zipFS = openAPK(provider, monitor)) {
try (ZipFileSystem zipFS = openAPK(settings.provider(), monitor)) {
while (!monitor.isCancelled()) {
GFile classesDexFile =
zipFS.lookup("/" + "classes" + (dexIndex == 1 ? "" : dexIndex) + ".dex");
@@ -81,16 +79,17 @@ public class ApkLoader extends DexLoader {
break;
}
monitor.setMessage(
"Loading " + classesDexFile.getName() + " from " + programName + "...");
monitor.setMessage("Loading " + classesDexFile.getName() + " from " +
settings.importName() + "...");
try (ByteProvider dexProvider =
zipFS.getByteProvider(classesDexFile, monitor)) {
// defer to the super class (DexLoader) to actually load the DEX file
List<Loaded<Program>> loadedPrograms =
super.loadProgram(dexProvider, classesDexFile.getName(), project,
joinPaths(programFolderPath, programName), loadSpec, options, log,
consumer, monitor);
ImporterSettings newSettings = new ImporterSettings(dexProvider,
classesDexFile.getName(), settings.project(), settings.projectRootPath(),
settings.mirrorFsLayout(), settings.loadSpec(), settings.options(),
settings.consumer(), settings.log(), settings.monitor());
List<Loaded<Program>> loadedPrograms = super.loadProgram(newSettings);
allLoadedPrograms.addAll(loadedPrograms);
}
@@ -72,21 +72,21 @@ public class DexLoader extends AbstractProgramWrapperLoader {
}
@Override
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program, TaskMonitor monitor, MessageLog log) throws IOException {
public void load(Program program, ImporterSettings settings) throws IOException {
MessageLog log = settings.log();
TaskMonitor monitor = settings.monitor();
monitor.setMessage(getMonitorMessagePrimary());
try {
Address start = program.getAddressFactory().getDefaultAddressSpace().getAddress(0x0);
long length = provider.length();
long length = settings.provider().length();
try (InputStream inputStream = provider.getInputStream(0)) {
try (InputStream inputStream = settings.provider().getInputStream(0)) {
program.getMemory()
.createInitializedBlock(getMemoryBlockName(), start, inputStream, length,
monitor, false);
}
BinaryReader reader = new BinaryReader(provider, true);
BinaryReader reader = new BinaryReader(settings.provider(), true);
DexHeader header = DexHeaderFactory.getDexHeader(reader);
monitor.setMessage(getMonitorMessageSecondary());
@@ -244,7 +244,7 @@ public class DexLoader extends AbstractProgramWrapperLoader {
@Override
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
DomainObject domainObject, boolean loadIntoProgram) {
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
return Collections.emptyList();
}
@@ -26,7 +26,6 @@ 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.Project;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.listing.Group;
import ghidra.program.model.listing.Program;
@@ -77,14 +76,14 @@ public class DyldCacheExtractLoader extends MachoLoader {
}
@Override
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program, TaskMonitor monitor, MessageLog log) throws IOException {
public void load(Program program, ImporterSettings settings) throws IOException {
try {
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
MachoExtractProgramBuilder.buildProgram(program, provider, fileBytes, false, log,
monitor);
addOptionalComponents(program, options, log, monitor);
FileBytes fileBytes =
MemoryBlockUtils.createFileBytes(program, settings.provider(), settings.monitor());
MachoExtractProgramBuilder.buildProgram(program, settings.provider(), fileBytes, false,
settings.log(), settings.monitor());
addOptionalComponents(program, settings.options(), settings.log(), settings.monitor());
}
catch (CancelledException e) {
return;
@@ -98,20 +97,20 @@ public class DyldCacheExtractLoader extends MachoLoader {
}
@Override
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
List<Option> options, MessageLog log, Program program, TaskMonitor monitor)
protected void loadProgramInto(Program program, ImporterSettings settings)
throws IOException, LoadException, CancelledException {
FSRL fsrl = provider.getFSRL();
FSRL fsrl = settings.provider().getFSRL();
Group[] children = program.getListing().getDefaultRootModule().getChildren();
if (Arrays.stream(children).anyMatch(e -> e.getName().contains(fsrl.getPath()))) {
log.appendMsg("%s has already been added".formatted(fsrl.getPath()));
settings.log().appendMsg("%s has already been added".formatted(fsrl.getPath()));
return;
}
try {
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
MachoExtractProgramBuilder.buildProgram(program, provider, fileBytes, true, log,
monitor);
addOptionalComponents(program, options, log, monitor);
FileBytes fileBytes =
MemoryBlockUtils.createFileBytes(program, settings.provider(), settings.monitor());
MachoExtractProgramBuilder.buildProgram(program, settings.provider(), fileBytes, true,
settings.log(), settings.monitor());
addOptionalComponents(program, settings.options(), settings.log(), settings.monitor());
}
catch (CancelledException e) {
return;
@@ -141,7 +140,7 @@ public class DyldCacheExtractLoader extends MachoLoader {
@Override
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
DomainObject domainObject, boolean loadIntoProgram) {
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
List<Option> list = new ArrayList<>();
list.add(new Option(LIBOBJC_OPTION_NAME, !loadIntoProgram && LIBOBJC_OPTION_DEFAULT,
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-libobjc"));
@@ -185,19 +184,18 @@ public class DyldCacheExtractLoader extends MachoLoader {
}
@Override
protected boolean isLoadLibraries(List<Option> options) {
protected boolean isLoadLibraries(ImporterSettings settings) {
return false;
}
@Override
protected boolean shouldSearchAllPaths(Program program, List<Option> options, MessageLog log) {
protected boolean shouldSearchAllPaths(Program program, ImporterSettings settings) {
return false;
}
@Override
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, TaskMonitor monitor)
throws CancelledException, IOException {
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms,
ImporterSettings settings) throws CancelledException, IOException {
// Do nothing
}
@@ -21,16 +21,13 @@ import java.util.*;
import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.Option;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.file.formats.ios.fileset.MachoFileSetExtractor;
import ghidra.formats.gfilesystem.FSRL;
import ghidra.framework.model.DomainObject;
import ghidra.framework.model.Project;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.listing.Group;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* A {@link Loader} for Mach-O file set entries extracted by Ghidra from a Mach-O file set
@@ -52,13 +49,13 @@ public class MachoFileSetExtractLoader extends MachoLoader {
}
@Override
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program, TaskMonitor monitor, MessageLog log) throws IOException {
public void load(Program program, ImporterSettings settings) throws IOException {
try {
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
MachoExtractProgramBuilder.buildProgram(program, provider, fileBytes, false, log,
monitor);
FileBytes fileBytes =
MemoryBlockUtils.createFileBytes(program, settings.provider(), settings.monitor());
MachoExtractProgramBuilder.buildProgram(program, settings.provider(), fileBytes, false,
settings.log(), settings.monitor());
}
catch (CancelledException e) {
return;
@@ -72,19 +69,19 @@ public class MachoFileSetExtractLoader extends MachoLoader {
}
@Override
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
MessageLog log, Program program, TaskMonitor monitor)
protected void loadProgramInto(Program program, ImporterSettings settings)
throws IOException, LoadException, CancelledException {
FSRL fsrl = provider.getFSRL();
FSRL fsrl = settings.provider().getFSRL();
Group[] children = program.getListing().getDefaultRootModule().getChildren();
if (Arrays.stream(children).anyMatch(e -> e.getName().contains(fsrl.getPath()))) {
log.appendMsg("%s has already been added".formatted(fsrl.getPath()));
settings.log().appendMsg("%s has already been added".formatted(fsrl.getPath()));
return;
}
try {
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
MachoExtractProgramBuilder.buildProgram(program, provider, fileBytes, true, log,
monitor);
FileBytes fileBytes =
MemoryBlockUtils.createFileBytes(program, settings.provider(), settings.monitor());
MachoExtractProgramBuilder.buildProgram(program, settings.provider(), fileBytes, true,
settings.log(), settings.monitor());
}
catch (CancelledException e) {
return;
@@ -114,24 +111,23 @@ public class MachoFileSetExtractLoader extends MachoLoader {
@Override
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
DomainObject domainObject, boolean loadIntoProgram) {
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
return List.of();
}
@Override
protected boolean isLoadLibraries(List<Option> options) {
protected boolean isLoadLibraries(ImporterSettings settings) {
return false;
}
@Override
protected boolean shouldSearchAllPaths(Program program, List<Option> options, MessageLog log) {
protected boolean shouldSearchAllPaths(Program program, ImporterSettings settings) {
return false;
}
@Override
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, TaskMonitor monitor)
throws CancelledException, IOException {
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms,
ImporterSettings settings) throws CancelledException, IOException {
// Do nothing
}
}
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -106,12 +106,11 @@ public class DumpFileLoader extends AbstractProgramWrapperLoader {
}
@Override
@SuppressWarnings("hiding")
protected void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program, TaskMonitor monitor, MessageLog log)
protected void load(Program program, ImporterSettings settings)
throws CancelledException, IOException {
this.log = log;
parseDumpFile(provider, program, options, loadSpec, monitor);
this.log = settings.log();
parseDumpFile(settings.provider(), program, settings.options(), settings.loadSpec(),
settings.monitor());
}
private void parseDumpFile(ByteProvider provider, Program program, List<Option> options,
@@ -300,7 +299,7 @@ public class DumpFileLoader extends AbstractProgramWrapperLoader {
@Override
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
DomainObject domainObject, boolean isLoadIntoProgram) {
DomainObject domainObject, boolean isLoadIntoProgram, boolean mirrorFsLayout) {
List<Option> options = new ArrayList<>();
try {
int size = loadSpec.getLanguageCompilerSpec().getLanguage().getDefaultSpace().getSize();
@@ -22,6 +22,7 @@ import ghidra.app.util.*;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.*;
import ghidra.app.util.opinion.Loader.ImporterSettings;
import ghidra.file.formats.dump.DumpFile;
import ghidra.file.formats.dump.DumpFileReader;
import ghidra.framework.store.LockException;
@@ -79,6 +80,7 @@ public class Apport extends DumpFile {
private void createBlocksFromElf(LoadSpec loadSpec, TaskMonitor monitor)
throws IOException, CancelledException {
Object consumer = new Object();
try (
DecodedProvider provider =
new DecodedProvider(this, reader.getByteProvider(), monitor)) {
@@ -86,7 +88,13 @@ public class Apport extends DumpFile {
Option base = new Option(ElfLoaderOptionsFactory.IMAGE_BASE_OPTION_NAME,
Long.toHexString(header.getMemoryInfo(0).getBaseAddress()));
options.add(base);
elfLoader.load(provider, loadSpec, options, program, monitor, log);
program.addConsumer(consumer);
ImporterSettings settings = new ImporterSettings(provider, program.getName(), null,
null, false, loadSpec, options, consumer, log, monitor);
elfLoader.load(program, settings);
}
finally {
program.release(consumer);
}
Memory memory = program.getMemory();
@@ -62,12 +62,17 @@ public class DumpPeShim extends PeLoader {
return;
}
program.setEffectiveImageBase(minAddress);
Object consumer = new Object();
try {
load(provider, loadSpec, options, program, monitor, log);
program.addConsumer(consumer);
ImporterSettings settings = new ImporterSettings(provider, program.getName(), null,
null, false, loadSpec, options, consumer, log, monitor);
load(program, settings);
monitor.checkCancelled();
}
finally {
program.setEffectiveImageBase(null);
program.release(consumer);
}
shiftModule();