GP-2049 Add DumpFileLoader support for Apport format

This commit is contained in:
d-millar 2022-06-10 10:51:22 -04:00 committed by ghidra1
parent e66e5b70e9
commit 114895b79d
11 changed files with 743 additions and 41 deletions

View File

@ -13,6 +13,9 @@ data/ExtensionPoint.manifest||GHIDRA||||END|
data/android/eclipse-classpath||GHIDRA||reviewed||END| data/android/eclipse-classpath||GHIDRA||reviewed||END|
data/android/eclipse-project||GHIDRA||reviewed||END| data/android/eclipse-project||GHIDRA||reviewed||END|
data/crypto/README.txt||GHIDRA||||END| data/crypto/README.txt||GHIDRA||||END|
data/languages/apport.opinion||GHIDRA||||END|
data/languages/dumpfile.opinion||GHIDRA||||END| data/languages/dumpfile.opinion||GHIDRA||||END|
data/languages/minidump.opinion||GHIDRA||||END|
data/languages/pagedump.opinion||GHIDRA||||END|
src/main/help/help/TOC_Source.xml||GHIDRA||||END| src/main/help/help/TOC_Source.xml||GHIDRA||||END|
src/main/help/help/topics/FileFormatsPlugin/FileFormats.html||GHIDRA||||END| src/main/help/help/topics/FileFormatsPlugin/FileFormats.html||GHIDRA||||END|

View File

@ -0,0 +1,7 @@
<opinions>
<constraint loader="Dump File Loader">
<constraint compilerSpecID="linux">
<constraint primary="amd64" processor="x86" endian="little" size="64" />
</constraint>
</constraint>
</opinions>

View File

@ -0,0 +1,23 @@
<opinions>
<constraint loader="Dump File Loader">
<constraint compilerSpecID="windows">
<constraint primary="0" processor="x86" endian="little" size="32" />
<constraint primary="1" processor="MIPS" endian="little" size="32" />
<!-- constraint primary="2" processor="ALPHA" endian="little" size="32" -->
<constraint primary="4" processor="SuperH4" endian="little" size="32" />
<constraint primary="5" processor="ARM" endian="little" size="32" />
<!-- constraint primary="6" processor="IA64" endian="little" size="32" -->
<!-- constraint primary="7" processor="ALPHA64" endian="little" size="32" -->
<!-- constraint primary="8" processor="MSIL" endian="little" size="32" -->
<constraint primary="9" processor="x86" endian="little" size="64" />
<!-- constraint primary="10" processor="IA32/64" endian="little" size="32" -->
<!-- constraint primary="11" processor="NEUTRAL" endian="little" size="32" -->
<constraint primary="12" processor="ARM" endian="little" size="64" />
<constraint primary="13" processor="ARM" endian="little" size="32" />
<!-- constraint primary="14" processor="IA32" endian="little" size="32" -->
</constraint>
<constraint compilerSpecID="default">
<constraint primary="3" processor="PowerPC" endian="little" size="32" />
</constraint>
</constraint>
</opinions>

View File

@ -1,7 +1,6 @@
<opinions> <opinions>
<constraint loader="Dump File Loader"> <constraint loader="Dump File Loader">
<constraint compilerSpecID="windows"> <constraint compilerSpecID="windows">
<!-- Page/Userdump MachineImageType values -->
<!-- constraint primary="21064" processor="ALPHA" endian="little" size="64" --> <!-- constraint primary="21064" processor="ALPHA" endian="little" size="64" -->
<constraint primary="1824" processor="ARM" endian="little" size="32" variant="v8" /> <constraint primary="1824" processor="ARM" endian="little" size="32" variant="v8" />
<constraint primary="2080" processor="ARM" endian="little" size="32" variant="v8T" /> <!-- THUMB --> <constraint primary="2080" processor="ARM" endian="little" size="32" variant="v8T" /> <!-- THUMB -->
@ -21,28 +20,12 @@
<constraint primary="486" processor="x86" endian="little" size="32" /> <constraint primary="486" processor="x86" endian="little" size="32" />
<constraint primary="586" processor="x86" endian="little" size="32" /> <constraint primary="586" processor="x86" endian="little" size="32" />
<constraint primary="8664" processor="x86" endian="little" size="64" /> <constraint primary="8664" processor="x86" endian="little" size="64" />
<!-- MDMP Architecture values -->
<constraint primary="0" processor="x86" endian="little" size="32" />
<constraint primary="1" processor="MIPS" endian="little" size="32" />
<!-- constraint primary="2" processor="ALPHA" endian="little" size="32" -->
<constraint primary="4" processor="SuperH4" endian="little" size="32" />
<constraint primary="5" processor="ARM" endian="little" size="32" />
<!-- constraint primary="6" processor="IA64" endian="little" size="32" -->
<!-- constraint primary="7" processor="ALPHA64" endian="little" size="32" -->
<!-- constraint primary="8" processor="MSIL" endian="little" size="32" -->
<constraint primary="9" processor="x86" endian="little" size="64" />
<!-- constraint primary="10" processor="IA32/64" endian="little" size="32" -->
<!-- constraint primary="11" processor="NEUTRAL" endian="little" size="32" -->
<constraint primary="12" processor="ARM" endian="little" size="64" />
<constraint primary="13" processor="ARM" endian="little" size="32" />
<!-- constraint primary="14" processor="IA32" endian="little" size="32" -->
</constraint> </constraint>
<constraint compilerSpecID="default"> <constraint compilerSpecID="default">
<constraint primary="601" processor="PowerPC" endian="little" size="32" /> <constraint primary="601" processor="PowerPC" endian="little" size="32" />
<constraint primary="603" processor="PowerPC" endian="little" size="32" /> <constraint primary="603" processor="PowerPC" endian="little" size="32" />
<constraint primary="604" processor="PowerPC" endian="little" size="32" /> <constraint primary="604" processor="PowerPC" endian="little" size="32" />
<constraint primary="620" processor="PowerPC" endian="little" size="32" /> <constraint primary="620" processor="PowerPC" endian="little" size="32" />
<constraint primary="3" processor="PowerPC" endian="little" size="32" />
</constraint> </constraint>
</constraint> </constraint>
</opinions> </opinions>

View File

@ -15,6 +15,7 @@
*/ */
package ghidra.file.formats.dump; package ghidra.file.formats.dump;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
public class DumpData { public class DumpData {
@ -25,6 +26,7 @@ public class DumpData {
private boolean generateSymbol; private boolean generateSymbol;
private boolean generateFragment; private boolean generateFragment;
private long size; private long size;
private AddressSpace space;
public DumpData(long offset, DataType dt) { public DumpData(long offset, DataType dt) {
this(offset, dt, dt.getDisplayName(), false, true); this(offset, dt, dt.getDisplayName(), false, true);
@ -99,4 +101,12 @@ public class DumpData {
public long getSize() { public long getSize() {
return size; return size;
} }
public AddressSpace getAddressSpace() {
return space;
}
public void setAddressSpace(AddressSpace space) {
this.space = space;
}
} }

View File

@ -23,7 +23,9 @@ import java.util.Map.Entry;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager; import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility; import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility;
import ghidra.app.services.DataTypeManagerService; import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.Option; import ghidra.app.util.Option;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory; import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
@ -31,10 +33,12 @@ import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public class DumpFile { public class DumpFile {
// If data defined so must intAddressRanges which are used to create memory blocks
protected List<DumpData> data = new ArrayList<DumpData>(); protected List<DumpData> data = new ArrayList<DumpData>();
// Interior ranges are actual defined memory ranges. // Interior ranges are actual defined memory ranges.
// Exterior ranges are aggregates of interior ranges, typically corresponding to a module // Exterior ranges are aggregates of interior ranges, typically corresponding to a module
@ -62,6 +66,8 @@ public class DumpFile {
protected Map<String, DataTypeManager> managerList = new HashMap<>(); protected Map<String, DataTypeManager> managerList = new HashMap<>();
private FileBytes fileBytes;
public DumpFile(DumpFileReader reader, ProgramBasedDataTypeManager dtm, List<Option> options, public DumpFile(DumpFileReader reader, ProgramBasedDataTypeManager dtm, List<Option> options,
TaskMonitor monitor) { TaskMonitor monitor) {
@ -257,6 +263,22 @@ public class DumpFile {
} }
/**
* Get or create FileBytes within program
* @param monitor task monitor
* @return file bytes object to be used for block creation or null
* @throws IOException if error occurs reading source file or writing to program database
* @throws CancelledException if operation cancelled by user
*/
public FileBytes getFileBytes(TaskMonitor monitor) throws IOException, CancelledException {
if (fileBytes == null) {
monitor.setMessage("Creating file bytes");
fileBytes =
MemoryBlockUtils.createFileBytes(program, reader.getByteProvider(), monitor);
}
return fileBytes;
}
public void analyze(TaskMonitor monitor) { public void analyze(TaskMonitor monitor) {
// Override if needed // Override if needed
} }

View File

@ -23,6 +23,7 @@ import ghidra.app.util.*;
import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.*; import ghidra.app.util.opinion.*;
import ghidra.file.formats.dump.apport.Apport;
import ghidra.file.formats.dump.mdmp.Minidump; import ghidra.file.formats.dump.mdmp.Minidump;
import ghidra.file.formats.dump.pagedump.Pagedump; import ghidra.file.formats.dump.pagedump.Pagedump;
import ghidra.file.formats.dump.userdump.Userdump; import ghidra.file.formats.dump.userdump.Userdump;
@ -30,6 +31,7 @@ import ghidra.framework.model.DomainObject;
import ghidra.framework.store.LockException; import ghidra.framework.store.LockException;
import ghidra.program.database.mem.FileBytes; import ghidra.program.database.mem.FileBytes;
import ghidra.program.database.mem.MemoryMapDB; import ghidra.program.database.mem.MemoryMapDB;
import ghidra.program.database.register.AddressRangeObjectMap;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.DataUtilities; import ghidra.program.model.data.DataUtilities;
import ghidra.program.model.data.ProgramBasedDataTypeManager; import ghidra.program.model.data.ProgramBasedDataTypeManager;
@ -64,7 +66,7 @@ public class DumpFileLoader extends AbstractLibrarySupportLoader {
public static final boolean ANALYZE_EMBEDDED_OBJECTS_OPTION_DEFAULT = false; // must be off by default public static final boolean ANALYZE_EMBEDDED_OBJECTS_OPTION_DEFAULT = false; // must be off by default
public static final String MEMORY = "Memory"; public static final String MEMORY = "Memory";
private Map<AddressRange, String> ranges = new HashMap<>(); private AddressRangeObjectMap<String> rangeMap = new AddressRangeObjectMap<>();
private MessageLog log; private MessageLog log;
private boolean joinBlocks; private boolean joinBlocks;
@ -104,6 +106,8 @@ public class DumpFileLoader extends AbstractLibrarySupportLoader {
return Userdump.getMachineType(reader); return Userdump.getMachineType(reader);
case Minidump.SIGNATURE: case Minidump.SIGNATURE:
return Minidump.getMachineType(reader); return Minidump.getMachineType(reader);
case Apport.SIGNATURE:
return Apport.getMachineType(reader);
} }
} }
catch (IOException e) { catch (IOException e) {
@ -117,13 +121,12 @@ public class DumpFileLoader extends AbstractLibrarySupportLoader {
protected void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options, protected void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program, TaskMonitor monitor, MessageLog log) Program program, TaskMonitor monitor, MessageLog log)
throws CancelledException, IOException { throws CancelledException, IOException {
this.log = log; this.log = log;
parseDumpFile(provider, program, options, monitor); parseDumpFile(provider, program, options, loadSpec, monitor);
} }
private void parseDumpFile(ByteProvider provider, Program program, List<Option> options, private void parseDumpFile(ByteProvider provider, Program program, List<Option> options,
TaskMonitor monitor) throws IOException, CancelledException { LoadSpec loadSpec, TaskMonitor monitor) throws IOException, CancelledException {
Language language = program.getLanguage(); Language language = program.getLanguage();
int size = language.getDefaultSpace().getSize(); int size = language.getDefaultSpace().getSize();
DumpFileReader reader = new DumpFileReader(provider, true, size); DumpFileReader reader = new DumpFileReader(provider, true, size);
@ -144,31 +147,38 @@ public class DumpFileLoader extends AbstractLibrarySupportLoader {
case Minidump.SIGNATURE: case Minidump.SIGNATURE:
df = new Minidump(reader, dtm, options, monitor); df = new Minidump(reader, dtm, options, monitor);
break; break;
case Apport.SIGNATURE:
df = new Apport(reader, dtm, options, monitor, loadSpec, log);
break;
} }
if (df != null) { if (df != null) {
groupRanges(program, provider, df.getExteriorAddressRanges(), monitor); groupRanges(program, df, monitor);
loadRanges(program, provider, df.getInteriorAddressRanges(), monitor); loadRanges(program, df, monitor);
applyStructures(program, df, monitor); applyStructures(program, df, monitor);
df.analyze(monitor); df.analyze(monitor);
} }
} }
public void loadRanges(Program program, ByteProvider provider, public void loadRanges(Program program, DumpFile df, TaskMonitor monitor) {
Map<Address, DumpAddressObject> daos, TaskMonitor monitor) { Map<Address, DumpAddressObject> daos = df.getInteriorAddressRanges();
if (daos.isEmpty()) {
return;
}
try { try {
monitor.setMessage("Creating file bytes"); FileBytes fileBytes = df.getFileBytes(monitor);
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor); if (fileBytes == null) {
Msg.error(this,
"File bytes not provided by DumpFile: " + df.getClass().getSimpleName());
return;
}
int count = 0; int count = 0;
monitor.setMessage("Tagging blocks"); monitor.setMessage("Tagging blocks");
monitor.initialize(daos.size()); monitor.initialize(daos.size());
for (Address address : daos.keySet()) { for (Address address : daos.keySet()) {
DumpAddressObject d = daos.get(address); DumpAddressObject d = daos.get(address);
String name = d.getProviderId(); String name = rangeMap.getObject(address);
for (AddressRange range : ranges.keySet()) { if (name == null) {
if (range.contains(address)) { name = d.getProviderId();
name = ranges.get(range);
break;
}
} }
d.setRangeName(name); d.setRangeName(name);
monitor.setProgress(count++); monitor.setProgress(count++);
@ -237,27 +247,31 @@ public class DumpFileLoader extends AbstractLibrarySupportLoader {
} }
} }
public void groupRanges(Program program, ByteProvider provider, public void groupRanges(Program program, DumpFile df, TaskMonitor monitor)
Map<Address, DumpAddressObject> daos, TaskMonitor monitor) throws CancelledException { throws CancelledException {
Map<Address, DumpAddressObject> daos = df.getExteriorAddressRanges();
if (daos.isEmpty()) {
return;
}
monitor.setMessage("Assigning ranges"); monitor.setMessage("Assigning ranges");
monitor.initialize(daos.size()); monitor.initialize(daos.size());
int count = 0; int count = 0;
for (Entry<Address, DumpAddressObject> entry : daos.entrySet()) { for (Entry<Address, DumpAddressObject> entry : daos.entrySet()) {
monitor.checkCanceled();
monitor.setProgress(count++);
DumpAddressObject d = entry.getValue(); DumpAddressObject d = entry.getValue();
Address address = entry.getKey(); Address address = entry.getKey();
if (d.getBase() == 0) { if (d.getBase() == 0) {
continue; continue;
} }
try { try {
AddressRangeImpl range = new AddressRangeImpl(address, d.getLength()); rangeMap.setObject(address, address.addNoWrap(d.getLength() - 1),
ranges.put(range, d.getProviderId()); d.getProviderId());
} }
catch (AddressOverflowException | AddressOutOfBoundsException catch (AddressOverflowException | AddressOutOfBoundsException
| IllegalArgumentException e) { | IllegalArgumentException e) {
Msg.warn(this, e.getMessage()); Msg.warn(this, e.getMessage());
} }
monitor.setProgress(count++);
monitor.checkCanceled();
} }
} }
@ -266,9 +280,14 @@ public class DumpFileLoader extends AbstractLibrarySupportLoader {
SymbolTable symbolTable = program.getSymbolTable(); SymbolTable symbolTable = program.getSymbolTable();
monitor.setMessage("Applying data structures"); monitor.setMessage("Applying data structures");
List<DumpData> data = df.getData(); List<DumpData> data = df.getData();
if (data.isEmpty()) {
return;
}
monitor.initialize(data.size()); monitor.initialize(data.size());
int count = 0; int count = 0;
for (DumpData dd : data) { for (DumpData dd : data) {
monitor.checkCanceled();
monitor.setProgress(count++);
Address address = program.getImageBase().addWrap(dd.getOffset()); Address address = program.getImageBase().addWrap(dd.getOffset());
try { try {
if (dd.getDataType() == null) { if (dd.getDataType() == null) {
@ -289,8 +308,6 @@ public class DumpFileLoader extends AbstractLibrarySupportLoader {
Msg.error(this, Msg.error(this,
"Could not create " + dd.getDataType().getName() + " at " + address); "Could not create " + dd.getDataType().getName() + " at " + address);
} }
monitor.setProgress(count++);
monitor.checkCanceled();
} }
} }

View File

@ -0,0 +1,174 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.file.formats.dump.apport;
import java.io.IOException;
import java.util.List;
import ghidra.app.util.*;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.*;
import ghidra.file.formats.dump.*;
import ghidra.framework.options.Options;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.ProgramBasedDataTypeManager;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.Msg;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
public class Apport extends DumpFile {
public static final int SIGNATURE = 0x626F7250; // "ProblemType"
ApportHeader header;
private MessageLog log;
public Apport(DumpFileReader reader, ProgramBasedDataTypeManager dtm, List<Option> options,
TaskMonitor monitor, LoadSpec loadSpec, MessageLog log)
throws CancelledException, IOException {
super(reader, dtm, options, monitor);
this.log = log;
Options props = program.getOptions(Program.PROGRAM_INFO);
props.setString("Executable Format", PeLoader.PE_NAME);
initManagerList(null);
header = new ApportHeader(reader, 0L, monitor);
boolean createBlocks =
OptionUtils.getBooleanOptionValue(DumpFileLoader.CREATE_MEMORY_BLOCKS_OPTION_NAME,
options, DumpFileLoader.CREATE_MEMORY_BLOCKS_OPTION_DEFAULT);
if (createBlocks) {
createBlocksFromElf(loadSpec, monitor);
}
buildStructures(loadSpec, monitor);
}
public ApportHeader getFileHeader() {
return header;
}
private void createBlocksFromElf(LoadSpec loadSpec, TaskMonitor monitor)
throws IOException, CancelledException {
try (
DecodedProvider provider =
new DecodedProvider(this, reader.getByteProvider(), monitor)) {
ElfLoader elfLoader = new ElfLoader();
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);
}
Memory memory = program.getMemory();
Address minAddress = memory.getMinAddress();
Listing listing = program.getListing();
ProgramModule root = listing.getDefaultRootModule();
Group[] children = root.getChildren();
try {
for (int i = 0; i < header.getMemoryRegionCount(); i++) {
MemoryInfo minfo = header.getMemoryInfo(i);
String id = minfo.getDescription();
if (id == null) {
id = "Memory";
}
Address addr = minAddress.getNewAddress(minfo.getBaseAddress());
MemoryBlock block = memory.getBlock(addr);
if (block != null) {
String name = block.getName();
block.setName(id);
boolean renamed = false;
try {
Group fragment = children[root.getIndex(name)];
if (fragment != null) {
fragment.setName(i + ":" + id);
renamed = true;
}
}
catch (DuplicateNameException e) {
// ignore
}
if (!renamed) {
Msg.error(this, "Failed to rename module: " + name);
}
}
}
}
catch (LockException e) {
throw new IOException(e); // unexpected during import
}
}
private void buildStructures(LoadSpec loadSpec, TaskMonitor monitor)
throws IOException {
DataType dt = header.toDataType();
try {
ByteProvider byteProvider = reader.getByteProvider();
MemoryBlock headerBlock = MemoryBlockUtils.createInitializedBlock(program, true,
"DumpHeader",
AddressSpace.OTHER_SPACE.getMinAddress(),
//fileBytes,
//d.getRVA(), // offset into filebytes
byteProvider.getInputStream(0),
dt.getLength(), // size
byteProvider.getName(), // comment
"Apport", // source
true, // section.isReadonly(),
true, // section.isWriteable(),
false, //section.isExecutable());
log,
monitor);
program.getListing().createData(headerBlock.getStart(), dt, dt.getLength());
}
catch (AddressOverflowException e) {
throw new AssertException(e);
}
catch (CodeUnitInsertionException e) {
Msg.warn(this, e.getMessage());
}
}
public static String getMachineType(DumpFileReader reader) throws IOException {
ApportHeader header = new ApportHeader(reader, 0L, TaskMonitor.DUMMY);
return header.getMachineImageType();
}
@Override
public FileBytes getFileBytes(TaskMonitor monitor) throws IOException, CancelledException {
// FileBytes not used for original file content
return null;
}
}

View File

@ -0,0 +1,199 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.file.formats.dump.apport;
import java.io.IOException;
import java.util.*;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.StructConverter;
import ghidra.file.formats.dump.DumpFileReader;
import ghidra.program.model.data.*;
import ghidra.util.task.TaskMonitor;
public class ApportHeader implements StructConverter {
public final static String NAME = "APPORT_HEADER";
private Map<String,String> map = new HashMap<>();
private Map<String,Map<String,String>> smaps = new HashMap<>();
private Map<Integer, Integer> lineLens = new HashMap<>();
private Map<Integer, String> keys = new HashMap<>();
private String signature;
protected DumpFileReader reader;
protected long index;
private TaskMonitor monitor;
private int lineCount = 0;
private int memoryRegionOffset;
ApportHeader(DumpFileReader reader, long index, TaskMonitor monitor) throws IOException {
this.reader = reader;
this.index = index;
this.monitor = monitor;
parse();
}
protected void parse() throws IOException {
if (lineCount > 0) {
return;
}
reader.setPointerIndex(index);
List<Integer> lineEnds = new ArrayList<>();
List<String> lines = new ArrayList<>();
ByteProvider provider = reader.getByteProvider();
byte [] bytes = new byte[(int) reader.length()];
int idx = 0;
monitor.setMessage("Parsing file");
monitor.initialize(reader.length());
for (int i = 0; i < reader.length(); i++) {
byte b = provider.readByte(i);
if (b == '\n') {
String l = new String(bytes, 0, idx);
lines.add(l);
lineEnds.add(i);
idx = 0;
lineLens.put(lineCount, l.length());
lineCount++;
monitor.setProgress(i);
} else {
bytes[idx++] = b;
}
if (monitor.isCancelled()) {
break;
}
}
String key = "";
boolean useSubMap = false;
Map<String,String> submap = null;
int sub = 0;
monitor.setMessage("Parsing entries");
monitor.initialize(lineEnds.size());
for (int i = 0; i < lineEnds.size(); i++) {
monitor.setProgress(i);
if (monitor.isCancelled()) {
break;
}
String line = lines.get(i);
if (line.startsWith("CoreDump")) {
memoryRegionOffset = lineEnds.get(i);
}
int sep = line.indexOf(":");
if (sep < 0 || line.substring(0,sep).contains(" ")) {
String subkey = key+"["+sub+"]";
keys.put(i, subkey);
if (useSubMap) {
submap.put(subkey, line);
if (line.length() < 100 && line.contains(": ")) {
String [] split = line.split(": ");
submap.put(split[0], split[1]);
}
}
sub++;
} else {
key = line.substring(0, sep);
String value = line.substring(sep+1).trim();
keys.put(i, key);
useSubMap = value.equals("") || value.equals("base64");
if (useSubMap) {
submap = new HashMap<>();
smaps.put(key, submap);
} else {
map.put(key, value);
submap = null;
}
sub = 0;
}
}
}
/**
* @see ghidra.app.util.bin.StructConverter#toDataType()
*/
public DataType toDataType() {
StructureDataType struct = new StructureDataType(NAME, 0);
for (int i = 0; i < lineCount; i++) {
Integer length = lineLens.get(i);
if (length < 0) {
break;
}
String key = keys.get(i);
StringDataType str = new StringDataType();
Map<String, String> smap = smaps.get(key);
struct.add(str, length+1, key, null);
if (key.equals("CoreDump")) {
break;
}
if (smap != null) {
Structure substruct = new StructureDataType(key, 0);
for (String skey : smap.keySet()) {
length = lineLens.get(++i);
str = new StringDataType();
substruct.add(str, length+1, skey, null);
}
struct.add(substruct, substruct.getDisplayName(), null);
}
}
struct.setCategoryPath(new CategoryPath("/APDMP"));
return struct;
}
public String getSignature() {
return signature;
}
public void setSignature(String signature) {
this.signature = signature;
}
public int getLineCount() {
return lineCount;
}
public String getMachineImageType() {
return map.get("Architecture");
}
public MemoryInfo getMemoryInfo(int i) {
Map<String, String> procMap = smaps.get("ProcMaps");
return new MemoryInfo(procMap.get("ProcMaps["+i+"]"));
}
public int getMemoryRegionCount() {
return smaps.get("ProcMaps").size();
}
public int getMemoryRegionOffset() {
return memoryRegionOffset;
}
public String getBlob(int i) {
Map<String, String> cd = smaps.get("CoreDump");
return cd.get("CoreDump["+i+"]");
}
public int getBlobCount() {
Map<String, String> cd = smaps.get("CoreDump");
return cd.size();
}
}

View File

@ -0,0 +1,172 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.file.formats.dump.apport;
import java.io.*;
import java.nio.file.AccessMode;
import java.nio.file.Files;
import java.nio.file.attribute.*;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Set;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import aQute.lib.io.IO;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.FileByteProvider;
import ghidra.file.formats.dump.DumpFile;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class DecodedProvider implements ByteProvider {
private String name;
private ApportHeader fileHeader;
private long decompressedLength;
private FileByteProvider tempProvider;
private byte compressionMethod;
private byte compressionHeaderFlags;
private Object compressionTimeStamp;
private byte compressionFlags;
private byte compressionOS;
public DecodedProvider(DumpFile df, ByteProvider provider, TaskMonitor monitor)
throws CancelledException, IOException {
Apport pt = (Apport) df;
fileHeader = pt.getFileHeader();
name = provider.getName() + "(decoded)";
init(monitor);
}
private void init(TaskMonitor monitor) throws CancelledException, IOException {
FileAttribute<Set<PosixFilePermission>> permissions = PosixFilePermissions
.asFileAttribute(PosixFilePermissions.fromString("rw-------"));
File tempFile = Files.createTempFile("decode", ".dat", permissions).toFile();
boolean success = false;
try {
try (OutputStream out = IO.outputStream(tempFile)) {
Decoder decoder = Base64.getDecoder();
Inflater inflater = new Inflater(true);
byte[] decompressed = new byte[0x10000000];
monitor.setMessage("Decompressing data");
monitor.initialize(decompressedLength);
int written = 0;
byte[] header = decoder.decode(fileHeader.getBlob(0).trim());
parseHeader(header);
for (int i = 1; i < fileHeader.getBlobCount(); i++) {
monitor.checkCanceled();
byte[] decode = decoder.decode(fileHeader.getBlob(i).trim());
inflater.setInput(decode, 0, decode.length);
int nDecompressed = inflater.inflate(decompressed);
out.write(decompressed, 0, nDecompressed);
written += nDecompressed;
monitor.setProgress(written);
}
decompressedLength = written;
}
tempProvider = new FileByteProvider(tempFile, null, AccessMode.READ);
success = true;
}
catch (DataFormatException e) {
throw new IOException("apport decompress failure", e);
}
finally {
if (!success) {
tempFile.delete();
}
}
}
private void parseHeader(byte[] header) {
compressionMethod = header[2];
compressionHeaderFlags = header[3];
compressionTimeStamp = (((header[4] << 8) | header[5]) << 8 | header[6]) << 8 | header[7];
compressionFlags = header[8];
compressionOS = header[9];
}
@Override
public File getFile() {
return null;
}
@Override
public String getName() {
return name;
}
@Override
public String getAbsolutePath() {
return null;
}
@Override
public long length() {
if (decompressedLength <= 0) {
throw new RuntimeException("Decompressed length = "+decompressedLength);
}
return decompressedLength;
}
@Override
public boolean isValidIndex(long index) {
return index < length();
}
@Override
public void close() throws IOException {
File tempFile = tempProvider.getFile();
tempProvider.close();
tempFile.delete();
}
@Override
public byte readByte(long index) throws IOException {
return readBytes(index,1)[0];
}
@Override
public byte[] readBytes(long index, long length) throws IOException {
try {
return tempProvider.readBytes(index, length);
} catch (IOException e) {
return new byte[(int) length];
}
}
public byte getCompressionMethod() {
return compressionMethod;
}
public byte getCompressionHeaderFlags() {
return compressionHeaderFlags;
}
public Object getCompressionTimeStamp() {
return compressionTimeStamp;
}
public byte getCompressionFlags() {
return compressionFlags;
}
public byte getCompressionOS() {
return compressionOS;
}
}

View File

@ -0,0 +1,92 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.file.formats.dump.apport;
public class MemoryInfo {
public final static String NAME = "MINIDUMP_MEMORY_INFO";
private long baseAddress;
private long regionSize;
private String permissions;
private long rva;
private String description;
private String text;
MemoryInfo(String text) {
this.text = text;
parse();
}
private void parse() {
String[] split = text.trim().split("\\s+");
String range = split[0];
String[] rangeSplit = range.split("-");
long start = Long.parseUnsignedLong(rangeSplit[0], 16);
long stop = Long.parseUnsignedLong(rangeSplit[1], 16);
baseAddress = start;
regionSize = stop - start;
setPermissions(split[1]);
long offset = Long.parseUnsignedLong(split[2], 16);
setRva(offset);
if (split.length > 5) {
setDescription(split[5]);
}
}
public long getBaseAddress() {
return baseAddress;
}
public void setBaseAddress(long baseAddress) {
this.baseAddress = baseAddress;
}
public long getRegionSize() {
return regionSize;
}
public void setRegionSize(long regionSize) {
this.regionSize = regionSize;
}
public String getPermissions() {
return permissions;
}
public void setPermissions(String permissions) {
this.permissions = permissions;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public long getRva() {
return rva;
}
public void setRva(long rva) {
this.rva = rva;
}
}