mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-27 06:30:51 +00:00
GP-2049 Add DumpFileLoader support for Apport format
This commit is contained in:
parent
e66e5b70e9
commit
114895b79d
@ -13,6 +13,9 @@ data/ExtensionPoint.manifest||GHIDRA||||END|
|
||||
data/android/eclipse-classpath||GHIDRA||reviewed||END|
|
||||
data/android/eclipse-project||GHIDRA||reviewed||END|
|
||||
data/crypto/README.txt||GHIDRA||||END|
|
||||
data/languages/apport.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/topics/FileFormatsPlugin/FileFormats.html||GHIDRA||||END|
|
||||
|
@ -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>
|
23
Ghidra/Features/FileFormats/data/languages/minidump.opinion
Normal file
23
Ghidra/Features/FileFormats/data/languages/minidump.opinion
Normal 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>
|
@ -1,7 +1,6 @@
|
||||
<opinions>
|
||||
<constraint loader="Dump File Loader">
|
||||
<constraint compilerSpecID="windows">
|
||||
<!-- Page/Userdump MachineImageType values -->
|
||||
<!-- constraint primary="21064" processor="ALPHA" endian="little" size="64" -->
|
||||
<constraint primary="1824" processor="ARM" endian="little" size="32" variant="v8" />
|
||||
<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="586" processor="x86" endian="little" size="32" />
|
||||
<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 compilerSpecID="default">
|
||||
<constraint primary="601" 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="620" processor="PowerPC" endian="little" size="32" />
|
||||
<constraint primary="3" processor="PowerPC" endian="little" size="32" />
|
||||
</constraint>
|
||||
</constraint>
|
||||
</opinions>
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package ghidra.file.formats.dump;
|
||||
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.data.DataType;
|
||||
|
||||
public class DumpData {
|
||||
@ -25,6 +26,7 @@ public class DumpData {
|
||||
private boolean generateSymbol;
|
||||
private boolean generateFragment;
|
||||
private long size;
|
||||
private AddressSpace space;
|
||||
|
||||
public DumpData(long offset, DataType dt) {
|
||||
this(offset, dt, dt.getDisplayName(), false, true);
|
||||
@ -99,4 +101,12 @@ public class DumpData {
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public AddressSpace getAddressSpace() {
|
||||
return space;
|
||||
}
|
||||
|
||||
public void setAddressSpace(AddressSpace space) {
|
||||
this.space = space;
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,9 @@ import java.util.Map.Entry;
|
||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||
import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility;
|
||||
import ghidra.app.services.DataTypeManagerService;
|
||||
import ghidra.app.util.MemoryBlockUtils;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
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.listing.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class DumpFile {
|
||||
|
||||
// If data defined so must intAddressRanges which are used to create memory blocks
|
||||
protected List<DumpData> data = new ArrayList<DumpData>();
|
||||
// Interior ranges are actual defined memory ranges.
|
||||
// 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<>();
|
||||
|
||||
private FileBytes fileBytes;
|
||||
|
||||
public DumpFile(DumpFileReader reader, ProgramBasedDataTypeManager dtm, List<Option> options,
|
||||
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) {
|
||||
// Override if needed
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ 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.apport.Apport;
|
||||
import ghidra.file.formats.dump.mdmp.Minidump;
|
||||
import ghidra.file.formats.dump.pagedump.Pagedump;
|
||||
import ghidra.file.formats.dump.userdump.Userdump;
|
||||
@ -30,6 +31,7 @@ import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.database.mem.MemoryMapDB;
|
||||
import ghidra.program.database.register.AddressRangeObjectMap;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.DataUtilities;
|
||||
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 String MEMORY = "Memory";
|
||||
|
||||
private Map<AddressRange, String> ranges = new HashMap<>();
|
||||
private AddressRangeObjectMap<String> rangeMap = new AddressRangeObjectMap<>();
|
||||
|
||||
private MessageLog log;
|
||||
private boolean joinBlocks;
|
||||
@ -104,6 +106,8 @@ public class DumpFileLoader extends AbstractLibrarySupportLoader {
|
||||
return Userdump.getMachineType(reader);
|
||||
case Minidump.SIGNATURE:
|
||||
return Minidump.getMachineType(reader);
|
||||
case Apport.SIGNATURE:
|
||||
return Apport.getMachineType(reader);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -117,13 +121,12 @@ public class DumpFileLoader extends AbstractLibrarySupportLoader {
|
||||
protected void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program, TaskMonitor monitor, MessageLog log)
|
||||
throws CancelledException, IOException {
|
||||
|
||||
this.log = log;
|
||||
parseDumpFile(provider, program, options, monitor);
|
||||
parseDumpFile(provider, program, options, loadSpec, monitor);
|
||||
}
|
||||
|
||||
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();
|
||||
int size = language.getDefaultSpace().getSize();
|
||||
DumpFileReader reader = new DumpFileReader(provider, true, size);
|
||||
@ -144,31 +147,38 @@ public class DumpFileLoader extends AbstractLibrarySupportLoader {
|
||||
case Minidump.SIGNATURE:
|
||||
df = new Minidump(reader, dtm, options, monitor);
|
||||
break;
|
||||
case Apport.SIGNATURE:
|
||||
df = new Apport(reader, dtm, options, monitor, loadSpec, log);
|
||||
break;
|
||||
}
|
||||
if (df != null) {
|
||||
groupRanges(program, provider, df.getExteriorAddressRanges(), monitor);
|
||||
loadRanges(program, provider, df.getInteriorAddressRanges(), monitor);
|
||||
groupRanges(program, df, monitor);
|
||||
loadRanges(program, df, monitor);
|
||||
applyStructures(program, df, monitor);
|
||||
df.analyze(monitor);
|
||||
}
|
||||
}
|
||||
|
||||
public void loadRanges(Program program, ByteProvider provider,
|
||||
Map<Address, DumpAddressObject> daos, TaskMonitor monitor) {
|
||||
public void loadRanges(Program program, DumpFile df, TaskMonitor monitor) {
|
||||
Map<Address, DumpAddressObject> daos = df.getInteriorAddressRanges();
|
||||
if (daos.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
monitor.setMessage("Creating file bytes");
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||
FileBytes fileBytes = df.getFileBytes(monitor);
|
||||
if (fileBytes == null) {
|
||||
Msg.error(this,
|
||||
"File bytes not provided by DumpFile: " + df.getClass().getSimpleName());
|
||||
return;
|
||||
}
|
||||
int count = 0;
|
||||
monitor.setMessage("Tagging blocks");
|
||||
monitor.initialize(daos.size());
|
||||
for (Address address : daos.keySet()) {
|
||||
DumpAddressObject d = daos.get(address);
|
||||
String name = d.getProviderId();
|
||||
for (AddressRange range : ranges.keySet()) {
|
||||
if (range.contains(address)) {
|
||||
name = ranges.get(range);
|
||||
break;
|
||||
}
|
||||
String name = rangeMap.getObject(address);
|
||||
if (name == null) {
|
||||
name = d.getProviderId();
|
||||
}
|
||||
d.setRangeName(name);
|
||||
monitor.setProgress(count++);
|
||||
@ -237,27 +247,31 @@ public class DumpFileLoader extends AbstractLibrarySupportLoader {
|
||||
}
|
||||
}
|
||||
|
||||
public void groupRanges(Program program, ByteProvider provider,
|
||||
Map<Address, DumpAddressObject> daos, TaskMonitor monitor) throws CancelledException {
|
||||
public void groupRanges(Program program, DumpFile df, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
Map<Address, DumpAddressObject> daos = df.getExteriorAddressRanges();
|
||||
if (daos.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
monitor.setMessage("Assigning ranges");
|
||||
monitor.initialize(daos.size());
|
||||
int count = 0;
|
||||
for (Entry<Address, DumpAddressObject> entry : daos.entrySet()) {
|
||||
monitor.checkCanceled();
|
||||
monitor.setProgress(count++);
|
||||
DumpAddressObject d = entry.getValue();
|
||||
Address address = entry.getKey();
|
||||
if (d.getBase() == 0) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
AddressRangeImpl range = new AddressRangeImpl(address, d.getLength());
|
||||
ranges.put(range, d.getProviderId());
|
||||
rangeMap.setObject(address, address.addNoWrap(d.getLength() - 1),
|
||||
d.getProviderId());
|
||||
}
|
||||
catch (AddressOverflowException | AddressOutOfBoundsException
|
||||
| IllegalArgumentException e) {
|
||||
Msg.warn(this, e.getMessage());
|
||||
}
|
||||
monitor.setProgress(count++);
|
||||
monitor.checkCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,9 +280,14 @@ public class DumpFileLoader extends AbstractLibrarySupportLoader {
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
monitor.setMessage("Applying data structures");
|
||||
List<DumpData> data = df.getData();
|
||||
if (data.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
monitor.initialize(data.size());
|
||||
int count = 0;
|
||||
for (DumpData dd : data) {
|
||||
monitor.checkCanceled();
|
||||
monitor.setProgress(count++);
|
||||
Address address = program.getImageBase().addWrap(dd.getOffset());
|
||||
try {
|
||||
if (dd.getDataType() == null) {
|
||||
@ -289,8 +308,6 @@ public class DumpFileLoader extends AbstractLibrarySupportLoader {
|
||||
Msg.error(this,
|
||||
"Could not create " + dd.getDataType().getName() + " at " + address);
|
||||
}
|
||||
monitor.setProgress(count++);
|
||||
monitor.checkCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user