mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-25 17:00:36 -09:00
Merge remote-tracking branch 'origin/GP-1461_GhidraKnight_Android12--SQUASHED'
This commit is contained in:
@@ -30,8 +30,11 @@ import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.StringDataType;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.NotEmptyException;
|
||||
import ghidra.util.exception.NotFoundException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public abstract class FileFormatAnalyzer implements Analyzer {
|
||||
@@ -151,7 +154,7 @@ public abstract class FileFormatAnalyzer implements Analyzer {
|
||||
}
|
||||
|
||||
protected ProgramFragment createFragment(Program program, String fragmentName, Address start,
|
||||
Address end) throws Exception {
|
||||
Address end) throws DuplicateNameException, NotFoundException {
|
||||
ProgramModule module = program.getListing().getDefaultRootModule();
|
||||
ProgramFragment fragment = getFragment(module, fragmentName);
|
||||
if (fragment == null) {
|
||||
@@ -187,18 +190,17 @@ public abstract class FileFormatAnalyzer implements Analyzer {
|
||||
return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
||||
}
|
||||
|
||||
protected Data createData(Program program, Address address, DataType datatype)
|
||||
throws Exception {
|
||||
protected Data createData(Program program, Address address, DataType datatype) throws CodeUnitInsertionException {
|
||||
if (datatype instanceof StringDataType) {
|
||||
CreateStringCmd cmd = new CreateStringCmd(address);
|
||||
if (!cmd.applyTo(program)) {
|
||||
throw new RuntimeException(cmd.getStatusMsg());
|
||||
throw new CodeUnitInsertionException(cmd.getStatusMsg());
|
||||
}
|
||||
}
|
||||
else {
|
||||
CreateDataCmd cmd = new CreateDataCmd(address, datatype);
|
||||
if (!cmd.applyTo(program)) {
|
||||
throw new RuntimeException(cmd.getStatusMsg());
|
||||
throw new CodeUnitInsertionException(cmd.getStatusMsg());
|
||||
}
|
||||
}
|
||||
return program.getListing().getDefinedDataAt(address);
|
||||
|
||||
@@ -15,11 +15,13 @@
|
||||
*/
|
||||
package ghidra.file.formats.android.art;
|
||||
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.MemoryByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.BinaryLoader;
|
||||
import ghidra.file.analyzers.FileFormatAnalyzer;
|
||||
import ghidra.file.formats.android.oat.OatConstants;
|
||||
import ghidra.file.formats.android.oat.OatUtilities;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.data.DataType;
|
||||
@@ -49,7 +51,7 @@ public class ArtAnalyzer extends FileFormatAnalyzer {
|
||||
//HACK:
|
||||
//Make analyzer appear after ART is merged with OAT program
|
||||
//Currently, analyzers will not recognize the new ART block being added
|
||||
|| OatConstants.isOAT(program);
|
||||
|| OatUtilities.isOAT(program);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package ghidra.file.formats.android.bootimg;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.plugin.core.analysis.AnalysisWorker;
|
||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
@@ -27,7 +29,10 @@ import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.NotFoundException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class BootImageAnalyzer extends FileFormatAnalyzer implements AnalysisWorker {
|
||||
@@ -47,6 +52,11 @@ public class BootImageAnalyzer extends FileFormatAnalyzer implements AnalysisWor
|
||||
return "Annotates Android Boot, Recovery, or Vendor Image files.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWorkerName() {
|
||||
return "BootImageAnalyzer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAnalyze(Program program) {
|
||||
try {
|
||||
@@ -68,6 +78,7 @@ public class BootImageAnalyzer extends FileFormatAnalyzer implements AnalysisWor
|
||||
@Override
|
||||
public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
||||
throws Exception {
|
||||
|
||||
this.messageLog = log;
|
||||
AutoAnalysisManager manager = AutoAnalysisManager.getAnalysisManager(program);
|
||||
return manager.scheduleWorker(this, null, false, monitor);
|
||||
@@ -75,7 +86,8 @@ public class BootImageAnalyzer extends FileFormatAnalyzer implements AnalysisWor
|
||||
|
||||
@Override
|
||||
public boolean analysisWorkerCallback(Program program, Object workerContext,
|
||||
TaskMonitor monitor) throws Exception, CancelledException {
|
||||
TaskMonitor monitor)
|
||||
throws Exception, CancelledException {
|
||||
|
||||
Address address = program.getMinAddress();
|
||||
|
||||
@@ -83,76 +95,10 @@ public class BootImageAnalyzer extends FileFormatAnalyzer implements AnalysisWor
|
||||
BinaryReader reader = new BinaryReader(provider, true);
|
||||
|
||||
if (BootImageUtil.isBootImage(program)) {
|
||||
|
||||
BootImageHeader header = BootImageHeaderFactory.getBootImageHeader(reader);
|
||||
|
||||
if (!BootImageConstants.BOOT_MAGIC.equals(header.getMagic())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DataType headerDataType = header.toDataType();
|
||||
|
||||
Data headerData = createData(program, address, headerDataType);
|
||||
|
||||
if (headerData == null) {
|
||||
messageLog.appendMsg("Unable to create header data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
createFragment(program, headerDataType.getName(), toAddr(program, 0),
|
||||
toAddr(program, header.getPageSize()));
|
||||
|
||||
if (header.getKernelSize() > 0) {
|
||||
Address start = toAddr(program, header.getKernelOffset());
|
||||
Address end = toAddr(program, header.getKernelOffset() + header.getKernelSize());
|
||||
createFragment(program, BootImageConstants.KERNEL, start, end);
|
||||
}
|
||||
|
||||
if (header.getRamdiskSize() > 0) {
|
||||
Address start = toAddr(program, header.getRamdiskOffset());
|
||||
Address end = toAddr(program, header.getRamdiskOffset() + header.getRamdiskSize());
|
||||
createFragment(program, BootImageConstants.RAMDISK, start, end);
|
||||
}
|
||||
|
||||
if (header.getSecondSize() > 0) {
|
||||
Address start = toAddr(program, header.getSecondOffset());
|
||||
Address end = toAddr(program, header.getSecondOffset() + header.getSecondSize());
|
||||
createFragment(program, BootImageConstants.SECOND_STAGE, start, end);
|
||||
}
|
||||
|
||||
changeDataSettings(program, monitor);
|
||||
markupBootImage(program, reader, monitor);
|
||||
}
|
||||
else if (BootImageUtil.isVendorBootImage(program)) {
|
||||
VendorBootImageHeader header =
|
||||
VendorBootImageHeaderFactory.getVendorBootImageHeader(reader);
|
||||
|
||||
if (!header.getMagic().equals(BootImageConstants.VENDOR_BOOT_MAGIC)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DataType headerDataType = header.toDataType();
|
||||
|
||||
Data headerData = createData(program, address, headerDataType);
|
||||
|
||||
if (headerData == null) {
|
||||
messageLog.appendMsg("Unable to create header data.");
|
||||
}
|
||||
|
||||
createFragment(program, headerDataType.getName(), toAddr(program, 0),
|
||||
toAddr(program, headerData.getLength()));
|
||||
|
||||
if (header.getVendorRamdiskSize() > 0) {
|
||||
Address start = toAddr(program, header.getVendorRamdiskOffset());
|
||||
Address end = toAddr(program,
|
||||
header.getVendorRamdiskOffset() + header.getVendorRamdiskSize());
|
||||
createFragment(program, BootImageConstants.RAMDISK, start, end);
|
||||
}
|
||||
|
||||
if (header.getDtbSize() > 0) {
|
||||
Address start = toAddr(program, header.getDtbOffset());
|
||||
Address end = toAddr(program, header.getDtbOffset() + header.getDtbSize());
|
||||
createFragment(program, BootImageConstants.DTB, start, end);
|
||||
}
|
||||
markupVendorBootImage(program, reader, monitor);
|
||||
}
|
||||
|
||||
removeEmptyFragments(program);
|
||||
@@ -160,8 +106,120 @@ public class BootImageAnalyzer extends FileFormatAnalyzer implements AnalysisWor
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWorkerName() {
|
||||
return "BootImageAnalyzer";
|
||||
private void markupBootImage(Program program, BinaryReader reader, TaskMonitor monitor)
|
||||
throws IOException, DuplicateNameException, NotFoundException,
|
||||
CodeUnitInsertionException {
|
||||
|
||||
Address address = program.getMinAddress();
|
||||
|
||||
BootImageHeader header = BootImageHeaderFactory.getBootImageHeader(reader);
|
||||
|
||||
DataType headerDataType = header.toDataType();
|
||||
Data headerData = createData(program, address, headerDataType);
|
||||
if (headerData == null) {
|
||||
messageLog.appendMsg("Unable to create header data.");
|
||||
return;
|
||||
}
|
||||
createFragment(program, headerDataType.getName(), toAddr(program, 0),
|
||||
toAddr(program, header.getPageSize()));
|
||||
|
||||
if (header.getKernelSize() > 0) {
|
||||
Address start = toAddr(program, header.getKernelOffset());
|
||||
Address end = toAddr(program, header.getKernelOffset() + header.getKernelSize());
|
||||
createFragment(program, BootImageConstants.KERNEL, start, end);
|
||||
}
|
||||
|
||||
if (header.getRamdiskSize() > 0) {
|
||||
Address start = toAddr(program, header.getRamdiskOffset());
|
||||
Address end = toAddr(program, header.getRamdiskOffset() + header.getRamdiskSize());
|
||||
createFragment(program, BootImageConstants.RAMDISK, start, end);
|
||||
}
|
||||
|
||||
if (header.getSecondSize() > 0) {
|
||||
Address start = toAddr(program, header.getSecondOffset());
|
||||
Address end = toAddr(program, header.getSecondOffset() + header.getSecondSize());
|
||||
createFragment(program, BootImageConstants.SECOND_STAGE, start, end);
|
||||
}
|
||||
|
||||
changeDataSettings(program, monitor);
|
||||
}
|
||||
|
||||
private void markupVendorBootImage(Program program, BinaryReader reader, TaskMonitor monitor)
|
||||
throws IOException, DuplicateNameException, CodeUnitInsertionException,
|
||||
NotFoundException, CancelledException {
|
||||
|
||||
Address address = program.getMinAddress();
|
||||
|
||||
VendorBootImageHeader header =
|
||||
VendorBootImageHeaderFactory.getVendorBootImageHeader(reader);
|
||||
|
||||
DataType headerDataType = header.toDataType();
|
||||
Data headerData = createData(program, address, headerDataType);
|
||||
if (headerData == null) {
|
||||
messageLog.appendMsg("Unable to create header data.");
|
||||
}
|
||||
createFragment(program, headerDataType.getName(), toAddr(program, 0),
|
||||
toAddr(program, headerData.getLength()));
|
||||
|
||||
markupVendorRamdisk(program, header);
|
||||
|
||||
if (header.getDtbSize() > 0) {
|
||||
Address start = toAddr(program, header.getDtbOffset());
|
||||
Address end = toAddr(program, header.getDtbOffset() + header.getDtbSize());
|
||||
createFragment(program, BootImageConstants.DTB, start, end);
|
||||
}
|
||||
|
||||
markupVendorBootImageV4(header, program, monitor);
|
||||
}
|
||||
|
||||
private void markupVendorRamdisk(Program program, VendorBootImageHeader header)
|
||||
throws IOException, DuplicateNameException, NotFoundException {
|
||||
|
||||
if (header.getNestedVendorRamdiskCount() > 1) {
|
||||
for (int i = 0; i < header.getNestedVendorRamdiskCount(); ++i) {
|
||||
Address start = toAddr(program, header.getNestedVendorRamdiskOffset(i));
|
||||
Address end = toAddr(program,
|
||||
header.getNestedVendorRamdiskOffset(i) +
|
||||
header.getNestedVendorRamdiskSize(i));
|
||||
createFragment(program, BootImageConstants.RAMDISK + "_" + i, start, end);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (header.getVendorRamdiskSize() > 0) {
|
||||
Address start = toAddr(program, header.getVendorRamdiskOffset());
|
||||
Address end = toAddr(program,
|
||||
header.getVendorRamdiskOffset() + header.getVendorRamdiskSize());
|
||||
createFragment(program, BootImageConstants.RAMDISK, start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void markupVendorBootImageV4(VendorBootImageHeader header, Program program,
|
||||
TaskMonitor monitor) throws DuplicateNameException, NotFoundException,
|
||||
CancelledException, IOException, CodeUnitInsertionException {
|
||||
|
||||
if (header instanceof VendorBootImageHeaderV4) {
|
||||
VendorBootImageHeaderV4 v4 = (VendorBootImageHeaderV4) header;
|
||||
|
||||
if (v4.getVendorRamdiskTableEntrySize() > 0) {
|
||||
Address start = toAddr(program, v4.getVendorRamdiskTableOffset());
|
||||
Address end = toAddr(program,
|
||||
v4.getVendorRamdiskTableOffset() + v4.getVendorRamdiskTableSize());
|
||||
createFragment(program, "Ramdisk Table", start, end);
|
||||
|
||||
for (VendorRamdiskTableEntryV4 entry : v4.getVendorRamdiskTableEntryList()) {
|
||||
monitor.checkCanceled();
|
||||
DataType entryDataType = entry.toDataType();
|
||||
createData(program, start, entryDataType);
|
||||
start = start.add(entryDataType.getLength());
|
||||
}
|
||||
}
|
||||
|
||||
if (v4.getBootConfigSize() > 0) {
|
||||
Address start = toAddr(program, v4.getBootConfigOffset());
|
||||
Address end = toAddr(program, v4.getBootConfigOffset() + v4.getBootConfigSize());
|
||||
createFragment(program, "Boot Config", start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,23 +16,34 @@
|
||||
package ghidra.file.formats.android.bootimg;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.ByteProviderWrapper;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
import ghidra.formats.gfilesystem.GFile;
|
||||
import ghidra.formats.gfilesystem.GFileImpl;
|
||||
import ghidra.formats.gfilesystem.GFileSystemBase;
|
||||
import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
|
||||
import ghidra.formats.gfilesystem.factory.GFileSystemBaseFactory;
|
||||
import ghidra.formats.gfilesystem.fileinfo.*;
|
||||
import ghidra.formats.gfilesystem.fileinfo.FileAttribute;
|
||||
import ghidra.formats.gfilesystem.fileinfo.FileAttributeType;
|
||||
import ghidra.formats.gfilesystem.fileinfo.FileAttributes;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.CryptoException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@FileSystemInfo(type = "androidvendorbootimg", description = "Android Vendor Boot Images", factory = GFileSystemBaseFactory.class)
|
||||
@FileSystemInfo(
|
||||
type = "androidvendorbootimg",
|
||||
description = "Android Vendor Boot Images",
|
||||
factory = GFileSystemBaseFactory.class
|
||||
)
|
||||
public class VendorBootImageFileSystem extends GFileSystemBase {
|
||||
|
||||
private VendorBootImageHeader header;
|
||||
private GFileImpl ramdiskFile;
|
||||
private List<GFileImpl> ramdiskFileList = new ArrayList<>();
|
||||
private GFileImpl dtbFile;
|
||||
private List<GFileImpl> fileList = new ArrayList<>();
|
||||
|
||||
@@ -55,10 +66,24 @@ public class VendorBootImageFileSystem extends GFileSystemBase {
|
||||
throw new IOException("Invalid Android boot image file!");
|
||||
}
|
||||
|
||||
if (header.getVendorRamdiskSize() > 0) {
|
||||
ramdiskFile = GFileImpl.fromFilename(this, root, BootImageConstants.RAMDISK, false,
|
||||
header.getVendorRamdiskSize(), null);
|
||||
fileList.add(ramdiskFile);
|
||||
if ( header.getNestedVendorRamdiskCount() > 1) {
|
||||
for (int i = 0; i < header.getNestedVendorRamdiskCount(); ++i) {
|
||||
if (header.getNestedVendorRamdiskSize(i) > 0) {
|
||||
GFileImpl ramdiskFile = GFileImpl.fromFilename(
|
||||
this, root, BootImageConstants.RAMDISK + "_" + i,
|
||||
false, header.getNestedVendorRamdiskSize(i), null);
|
||||
fileList.add(ramdiskFile);
|
||||
ramdiskFileList.add(ramdiskFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (header.getVendorRamdiskSize() > 0) {
|
||||
GFileImpl ramdiskFile = GFileImpl.fromFilename(this, root, BootImageConstants.RAMDISK, false,
|
||||
header.getVendorRamdiskSize(), null);
|
||||
fileList.add(ramdiskFile);
|
||||
ramdiskFileList.add(ramdiskFile);
|
||||
}
|
||||
}
|
||||
if (header.getDtbSize() > 0) {
|
||||
dtbFile = GFileImpl.fromFilename(this, root, BootImageConstants.DTB, false,
|
||||
@@ -69,7 +94,8 @@ public class VendorBootImageFileSystem extends GFileSystemBase {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
ramdiskFile = null;
|
||||
fileList.clear();
|
||||
ramdiskFileList.clear();
|
||||
dtbFile = null;
|
||||
header = null;
|
||||
super.close();
|
||||
@@ -83,12 +109,14 @@ public class VendorBootImageFileSystem extends GFileSystemBase {
|
||||
|
||||
@Override
|
||||
public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) {
|
||||
if (file == ramdiskFile) {
|
||||
return FileAttributes.of(
|
||||
FileAttribute.create(FileAttributeType.COMMENT_ATTR,
|
||||
"This is a ramdisk, it is a GZIP file containing a CPIO archive."));
|
||||
for (int i = 0; i < ramdiskFileList.size(); ++i) {
|
||||
if (ramdiskFileList.get(i) == file) {
|
||||
return FileAttributes.of(
|
||||
FileAttribute.create(FileAttributeType.COMMENT_ATTR,
|
||||
"This is a ramdisk, it is a GZIP file containing a CPIO archive."));
|
||||
}
|
||||
}
|
||||
else if (file == dtbFile) {
|
||||
if (file == dtbFile) {
|
||||
return FileAttributes.of(
|
||||
FileAttribute.create(FileAttributeType.COMMENT_ATTR,
|
||||
"This is a DTB file. It appears unused at this time."));
|
||||
@@ -100,14 +128,18 @@ public class VendorBootImageFileSystem extends GFileSystemBase {
|
||||
public ByteProvider getByteProvider(GFile file, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
if (file == ramdiskFile) {
|
||||
return new ByteProviderWrapper(provider, header.getVendorRamdiskOffset(),
|
||||
Integer.toUnsignedLong(header.getVendorRamdiskSize()), file.getFSRL());
|
||||
for (int i = 0; i < ramdiskFileList.size(); ++i) {
|
||||
if (ramdiskFileList.get(i) == file) {
|
||||
return new ByteProviderWrapper(provider, header.getNestedVendorRamdiskOffset(i),
|
||||
Integer.toUnsignedLong(header.getNestedVendorRamdiskSize(i)), file.getFSRL());
|
||||
}
|
||||
}
|
||||
else if (file == dtbFile) {
|
||||
|
||||
if (file == dtbFile) {
|
||||
return new ByteProviderWrapper(provider, header.getDtbOffset(),
|
||||
Integer.toUnsignedLong(header.getDtbSize()), file.getFSRL());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
*/
|
||||
package ghidra.file.formats.android.bootimg;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
|
||||
/**
|
||||
*
|
||||
* Base class to represent a Vendor Boot Image header.
|
||||
*/
|
||||
public abstract class VendorBootImageHeader implements StructConverter {
|
||||
|
||||
@@ -31,4 +33,17 @@ public abstract class VendorBootImageHeader implements StructConverter {
|
||||
public abstract long getDtbOffset();
|
||||
|
||||
public abstract int getDtbSize();
|
||||
|
||||
public long getNestedVendorRamdiskCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public long getNestedVendorRamdiskOffset(int index) throws IOException {
|
||||
return getVendorRamdiskOffset();
|
||||
}
|
||||
|
||||
public int getNestedVendorRamdiskSize(int index) throws IOException {
|
||||
return getVendorRamdiskSize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.data.StructureDataType;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
@@ -86,6 +87,7 @@ public class VendorBootImageHeaderV3 extends VendorBootImageHeader {
|
||||
dtb_addr = reader.readNextLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMagic() {
|
||||
return magic;
|
||||
}
|
||||
@@ -106,13 +108,16 @@ public class VendorBootImageHeaderV3 extends VendorBootImageHeader {
|
||||
return ramdisk_addr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVendorRamdiskSize() {
|
||||
return vendor_ramdisk_size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getVendorRamdiskOffset() {
|
||||
return page_size;
|
||||
//NOTE:
|
||||
//the header can be larger than 1 page due to cmd line
|
||||
return NumericUtilities.getUnsignedAlignedValue(header_size, getPageSize());
|
||||
}
|
||||
|
||||
public String getCmdline() {
|
||||
@@ -131,6 +136,7 @@ public class VendorBootImageHeaderV3 extends VendorBootImageHeader {
|
||||
return header_size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDtbSize() {
|
||||
return dtb_size;
|
||||
}
|
||||
@@ -141,9 +147,8 @@ public class VendorBootImageHeaderV3 extends VendorBootImageHeader {
|
||||
|
||||
@Override
|
||||
public long getDtbOffset() {
|
||||
int o = ((2112 + page_size - 1) / page_size);
|
||||
int p = ((vendor_ramdisk_size + page_size - 1) / page_size);
|
||||
return (o + p) * page_size;
|
||||
long value = getVendorRamdiskOffset() + getVendorRamdiskSize();
|
||||
return NumericUtilities.getUnsignedAlignedValue(value, getPageSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,15 +16,19 @@
|
||||
package ghidra.file.formats.android.bootimg;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/include/bootimg/bootimg.h#401
|
||||
*
|
||||
* <pre>
|
||||
* The structure of the vendor boot image version 4, which is required to be
|
||||
* present when a version 4 boot image is used, is as follows:
|
||||
@@ -101,12 +105,40 @@ public class VendorBootImageHeaderV4 extends VendorBootImageHeaderV3 {
|
||||
private int vendor_ramdisk_table_entry_size;
|
||||
private int bootconfig_size;
|
||||
|
||||
private List<VendorRamdiskTableEntryV4> ramdiskTableEntryList = new ArrayList<>();
|
||||
|
||||
public VendorBootImageHeaderV4(BinaryReader reader) throws IOException {
|
||||
super(reader);
|
||||
vendor_ramdisk_table_size = reader.readNextInt();
|
||||
vendor_ramdisk_table_entry_num = reader.readNextInt();
|
||||
vendor_ramdisk_table_entry_size = reader.readNextInt();
|
||||
bootconfig_size = reader.readNextInt();
|
||||
|
||||
BinaryReader cloneReader = reader.clone(getVendorRamdiskTableOffset());
|
||||
for (int i = 0; i < vendor_ramdisk_table_entry_num; ++i) {
|
||||
ramdiskTableEntryList.add(new VendorRamdiskTableEntryV4(cloneReader));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNestedVendorRamdiskCount() {
|
||||
return vendor_ramdisk_table_entry_num;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNestedVendorRamdiskOffset(int index) throws IOException {
|
||||
return getVendorRamdiskOffset() +
|
||||
ramdiskTableEntryList.get(index).getRamdiskOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNestedVendorRamdiskSize(int index) throws IOException {
|
||||
return ramdiskTableEntryList.get(index).getRamdiskSize();
|
||||
}
|
||||
|
||||
public long getVendorRamdiskTableOffset() {
|
||||
long value = getDtbOffset() + getDtbSize();
|
||||
return NumericUtilities.getUnsignedAlignedValue(value, getPageSize());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,6 +165,11 @@ public class VendorBootImageHeaderV4 extends VendorBootImageHeaderV3 {
|
||||
return vendor_ramdisk_table_entry_size;
|
||||
}
|
||||
|
||||
public long getBootConfigOffset() {
|
||||
long value = getVendorRamdiskTableOffset() + getVendorRamdiskTableSize();
|
||||
return NumericUtilities.getUnsignedAlignedValue(value, getPageSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Size in bytes for the bootconfig section
|
||||
* @return size in bytes for the bootconfig section
|
||||
@@ -141,6 +178,10 @@ public class VendorBootImageHeaderV4 extends VendorBootImageHeaderV3 {
|
||||
return bootconfig_size;
|
||||
}
|
||||
|
||||
public List<VendorRamdiskTableEntryV4> getVendorRamdiskTableEntryList() {
|
||||
return ramdiskTableEntryList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure structure = (Structure) super.toDataType();
|
||||
@@ -148,7 +189,7 @@ public class VendorBootImageHeaderV4 extends VendorBootImageHeaderV3 {
|
||||
structure.setName("vendor_boot_img_hdr_v4");
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
//ignore
|
||||
// ignore
|
||||
}
|
||||
structure.add(DWORD, "vendor_ramdisk_table_size", null);
|
||||
structure.add(DWORD, "vendor_ramdisk_table_entry_num", null);
|
||||
|
||||
@@ -35,13 +35,14 @@ public class VendorRamdiskTableEntryV4 implements StructConverter {
|
||||
ramdisk_size = reader.readNextInt();
|
||||
ramdisk_offset = reader.readNextInt();
|
||||
ramdisk_type = reader.readNextInt();
|
||||
ramdisk_name = reader.readNextAsciiString(BootImageConstants.VENDOR_RAMDISK_NAME_SIZE);
|
||||
board_id =
|
||||
reader.readNextIntArray(BootImageConstants.VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE);
|
||||
ramdisk_name = reader.readNextAsciiString(
|
||||
BootImageConstants.VENDOR_RAMDISK_NAME_SIZE);
|
||||
board_id = reader.readNextIntArray(
|
||||
BootImageConstants.VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Size in bytes for the ramdisk image
|
||||
* Size in bytes for the ramdisk image
|
||||
* @return ramdisk size
|
||||
*/
|
||||
public int getRamdiskSize() {
|
||||
@@ -57,7 +58,7 @@ public class VendorRamdiskTableEntryV4 implements StructConverter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of the ramdisk
|
||||
* Type of the ramdisk
|
||||
* @return ramdisk type
|
||||
*/
|
||||
public int getRamdiskType() {
|
||||
@@ -65,7 +66,7 @@ public class VendorRamdiskTableEntryV4 implements StructConverter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Ascii ramdisk name
|
||||
* Ascii ramdisk name
|
||||
* @return the ascii ramdisk name
|
||||
*/
|
||||
public String getRamdiskName() {
|
||||
@@ -73,8 +74,8 @@ public class VendorRamdiskTableEntryV4 implements StructConverter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Hardware identifiers describing the board, soc or platform
|
||||
* which this ramdisk is intended to be loaded on.
|
||||
* Hardware identifiers describing the board, soc or platform which this ramdisk
|
||||
* is intended to be loaded on.
|
||||
* @return the board ID
|
||||
*/
|
||||
public int[] getBoardID() {
|
||||
|
||||
@@ -15,70 +15,24 @@
|
||||
*/
|
||||
package ghidra.file.formats.android.fbpk;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class FBPK implements StructConverter {
|
||||
private String magic;
|
||||
private int unknown1;
|
||||
private String version;
|
||||
private int partitionCount;
|
||||
private int size;
|
||||
private List<FBPK_Partition> partitions = new ArrayList<>();
|
||||
|
||||
public FBPK(BinaryReader reader) throws IOException {
|
||||
magic = reader.readNextAsciiString(FBPK_Constants.FBPK.length());
|
||||
unknown1 = reader.readNextInt();
|
||||
version = reader.readNextAsciiString(FBPK_Constants.VERSION_MAX_LENGTH);
|
||||
partitionCount = reader.readNextInt();
|
||||
size = reader.readNextInt();
|
||||
|
||||
for (int i = 0; i < partitionCount; ++i) {
|
||||
FBPK_Partition partition = new FBPK_Partition(reader);
|
||||
partitions.add(partition);
|
||||
reader.setPointerIndex(partition.getOffsetToNextPartitionTable());
|
||||
}
|
||||
}
|
||||
|
||||
public String getMagic() {
|
||||
return magic;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public int getPartitionCount() {
|
||||
return partitionCount;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public List<FBPK_Partition> getPartitions() {
|
||||
return partitions;
|
||||
}
|
||||
|
||||
public int getUnknown1() {
|
||||
return unknown1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure struct = new StructureDataType(FBPK.class.getSimpleName(), 0);
|
||||
struct.add(STRING, FBPK_Constants.FBPK.length(), "magic", null);
|
||||
struct.add(DWORD, "unknown1", null);
|
||||
struct.add(STRING, FBPK_Constants.VERSION_MAX_LENGTH, "version", null);
|
||||
struct.add(DWORD, "count", null);
|
||||
struct.add(DWORD, "size", null);
|
||||
return struct;
|
||||
}
|
||||
|
||||
public interface FBPK extends StructConverter {
|
||||
/**
|
||||
* Returns the MAGIC value.
|
||||
* @return the MAGIC value
|
||||
*/
|
||||
public int getMagic();
|
||||
/**
|
||||
* Returns the version.
|
||||
* @return the version
|
||||
*/
|
||||
public int getVersion();
|
||||
/**
|
||||
* Returns the list of partitions.
|
||||
* @return the list of partitions
|
||||
*/
|
||||
public List<FBPK_Partition> getPartitions();
|
||||
}
|
||||
|
||||
@@ -15,23 +15,28 @@
|
||||
*/
|
||||
package ghidra.file.formats.android.fbpk;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.services.AbstractAnalyzer;
|
||||
import ghidra.app.services.AnalyzerType;
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.MemoryByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.file.analyzers.FileFormatAnalyzer;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class FBPK_Analyzer extends AbstractAnalyzer {
|
||||
public class FBPK_Analyzer extends FileFormatAnalyzer {
|
||||
|
||||
public FBPK_Analyzer() {
|
||||
super("Android FBPK Analyzer", "Annotates Android FBPK Files", AnalyzerType.BYTE_ANALYZER);
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Android FBPK Analyzer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Annotates Android FBPK Files";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,48 +50,39 @@ public class FBPK_Analyzer extends AbstractAnalyzer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
||||
throws CancelledException {
|
||||
public boolean isPrototype() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
||||
throws Exception {
|
||||
|
||||
Address headerAddress = program.getMinAddress();
|
||||
ByteProvider provider = new MemoryByteProvider(program.getMemory(), headerAddress);
|
||||
BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian());
|
||||
try {
|
||||
FBPK header = new FBPK(reader);
|
||||
FBPK header = FBPK_Factory.getFBPK(reader);
|
||||
DataType headerDataType = header.toDataType();
|
||||
Data headerData = program.getListing().createData(headerAddress, headerDataType);
|
||||
if (headerData == null) {
|
||||
log.appendMsg("Unable to apply FBPK data, stopping - " + headerAddress);
|
||||
return false;
|
||||
}
|
||||
|
||||
Address address = headerAddress.add(headerDataType.getLength());
|
||||
|
||||
List<FBPK_Partition> partitions = header.getPartitions();
|
||||
for (int i = 0; i < partitions.size(); ++i) {
|
||||
FBPK_Partition partition = partitions.get(i);
|
||||
|
||||
DataType partitionDataType = partition.toDataType();
|
||||
Data partitionData = program.getListing().createData(address, partitionDataType);
|
||||
if (partitionData == null) {
|
||||
log.appendMsg("Unable to apply partition data, stopping - " + address);
|
||||
return false;
|
||||
monitor.initialize(header.getPartitions().size());
|
||||
monitor.setMessage("Marking up paritions...");
|
||||
for (FBPK_Partition partition : header.getPartitions()) {
|
||||
monitor.checkCanceled();
|
||||
monitor.incrementProgress(1);
|
||||
partition.markup(program, address, monitor, log);
|
||||
if (partition.getOffsetToNextPartitionTable() > 0) {
|
||||
address = address.getNewAddress(partition.getOffsetToNextPartitionTable());
|
||||
}
|
||||
program.getListing()
|
||||
.setComment(address, CodeUnit.PLATE_COMMENT,
|
||||
partition.getName() + " - " + i);
|
||||
address = address.add(partitionDataType.getLength());
|
||||
|
||||
if (partition.isDirectory()) {
|
||||
if (!processFBPT(program, address, partition, monitor, log)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
address = address.add(partition.getHeaderSize());
|
||||
}
|
||||
else if (partition.isFile()) {
|
||||
//unused, but leave as placeholder for future
|
||||
}
|
||||
|
||||
address = address.getNewAddress(partition.getOffsetToNextPartitionTable());
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -97,39 +93,4 @@ public class FBPK_Analyzer extends AbstractAnalyzer {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean processFBPT(Program program, Address address, FBPK_Partition partition,
|
||||
TaskMonitor monitor, MessageLog log) throws Exception {
|
||||
|
||||
FBPT fbpt = partition.getFBPT();
|
||||
DataType fbptDataType = fbpt.toDataType();
|
||||
Data fbptData = program.getListing().createData(address, fbptDataType);
|
||||
if (fbptData == null) {
|
||||
log.appendMsg("Unable to apply FBPT data, stopping - " + address);
|
||||
return false;
|
||||
}
|
||||
String comment = "FBPT" + "\n" + "Num of entries: " + fbpt.getNEntries();
|
||||
program.getListing().setComment(address, CodeUnit.PLATE_COMMENT, comment);
|
||||
address = address.add(fbptDataType.getLength());
|
||||
|
||||
return processFbPtEntries(program, address, fbpt, monitor, log);
|
||||
}
|
||||
|
||||
private boolean processFbPtEntries(Program program, Address address, FBPT fbpt,
|
||||
TaskMonitor monitor, MessageLog log) throws Exception {
|
||||
for (int i = 0; i < fbpt.getEntries().size(); ++i) {
|
||||
FBPT_Entry entry = fbpt.getEntries().get(i);
|
||||
monitor.checkCanceled();
|
||||
DataType entryDataType = entry.toDataType();
|
||||
Data entryData = program.getListing().createData(address, entryDataType);
|
||||
if (entryData == null) {
|
||||
log.appendMsg("Unable to apply FBPT Entry data, stopping - " + address);
|
||||
return false;
|
||||
}
|
||||
program.getListing()
|
||||
.setComment(address, CodeUnit.PLATE_COMMENT, entry.getName() + " - " + i);
|
||||
address = address.add(entryDataType.getLength());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,27 +20,53 @@ import ghidra.program.model.mem.Memory;
|
||||
|
||||
public final class FBPK_Constants {
|
||||
|
||||
public final static String FBPK = "FBPK";
|
||||
public final static String FBPT = "FBPT";
|
||||
public final static String PARTITION_TABLE = "partition table";
|
||||
public final static String LAST_PARTITION_ENTRY = "last_parti";
|
||||
public static final int VERSION_1 = 1;
|
||||
public static final int VERSION_2 = 2;
|
||||
|
||||
public final static int PARTITION_TYPE_DIRECTORY = 0;
|
||||
public final static int PARTITION_TYPE_FILE = 1;
|
||||
public final static int NAME_MAX_LENGTH = 36;
|
||||
public final static int VERSION_MAX_LENGTH = 68;
|
||||
public static final String FBPK = "FBPK";
|
||||
public static final String FBPT = "FBPT";
|
||||
public static final String UFPK = "UFPK";
|
||||
public static final String UFSM = "UFSM";
|
||||
public static final String UFSP = "UFSP";
|
||||
|
||||
public static final int FBPK_MAGIC = 0x4B504246;
|
||||
public static final int FBPT_MAGIC = 0x54504246;
|
||||
public static final int UFPK_MAGIC = 0x4B504655;
|
||||
public static final int UFSM_MAGIC = 0x4D534655;
|
||||
public static final int UFSP_MAGIC = 0x50534655;
|
||||
|
||||
public static final int NAME_MAX_LENGTH = 36;
|
||||
public static final int PARTITION_TYPE_DIRECTORY = 0;
|
||||
public static final int PARTITION_TYPE_FILE = 1;
|
||||
|
||||
public static final String PARTITION_TABLE = "partition table";
|
||||
public static final String V1_LAST_PARTITION_ENTRY = "last_parti";
|
||||
|
||||
|
||||
public static final int V1_VERSION_MAX_LENGTH = 68;
|
||||
public static final int V1_PADDING_LENGTH = 2;
|
||||
|
||||
public static final String V2_PARTITION = "partition:";
|
||||
public static final String V2_UFS = "ufs";
|
||||
public static final Object V2_UFSFWUPDATE = "ufsfwupdate";
|
||||
|
||||
public static final int V2_PARTITION_NAME_MAX_LENGTH = 76;
|
||||
public static final int V2_STRING1_MAX_LENGTH = 16;
|
||||
public static final int V2_STRING2_MAX_LENGTH = 68;
|
||||
public static final int V2_FORMAT_MAX_LENGTH = 14;
|
||||
public static final int V2_GUID_MAX_LENGTH = 44;
|
||||
public static final int V2_UFPK_STRING1_MAX_LENGTH = 76;
|
||||
|
||||
public static boolean isFBPK(Program program) {
|
||||
try {
|
||||
Memory memory = program.getMemory();
|
||||
byte[] bytes = new byte[FBPK.length()];
|
||||
memory.getBytes(program.getMinAddress(), bytes);
|
||||
String magic = new String(bytes).trim();
|
||||
return FBPK.equals(magic);
|
||||
int magic = memory.getInt(program.getMinAddress());
|
||||
return magic == FBPK_MAGIC;
|
||||
}
|
||||
catch (Exception e) {
|
||||
//ignore
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/* ###
|
||||
* 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.android.fbpk;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.file.formats.android.fbpk.v1.FBPKv1;
|
||||
import ghidra.file.formats.android.fbpk.v2.FBPKv2;
|
||||
|
||||
public class FBPK_Factory {
|
||||
public final static FBPK getFBPK(BinaryReader reader)
|
||||
throws IOException {
|
||||
|
||||
if (reader.length() > 8) {
|
||||
int magic = reader.readInt(0);
|
||||
int version = reader.readInt(4);
|
||||
|
||||
if (magic == FBPK_Constants.FBPK_MAGIC) {
|
||||
switch (version) {
|
||||
case FBPK_Constants.VERSION_1: {
|
||||
return new FBPKv1(reader);
|
||||
}
|
||||
case FBPK_Constants.VERSION_2: {
|
||||
return new FBPKv2(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IOException("Unsupported " + FBPK_Constants.FBPK + " version: " + version);
|
||||
}
|
||||
throw new IOException("Invalid " + FBPK_Constants.FBPK + " file");
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,11 @@ import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.CryptoException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@FileSystemInfo(type = "androidbootloaderfbpk", // ([a-z0-9]+ only)
|
||||
description = "Android Boot Loader Image (FBPK)", factory = GFileSystemBaseFactory.class)
|
||||
@FileSystemInfo(
|
||||
type = "androidbootloaderfbpk", // ([a-z0-9]+ only)
|
||||
description = "Android Boot Loader Image (FBPK)",
|
||||
factory = GFileSystemBaseFactory.class
|
||||
)
|
||||
|
||||
public class FBPK_FileSystem extends GFileSystemBase {
|
||||
private List<GFileImpl> fileList = new ArrayList<>();
|
||||
@@ -46,7 +49,7 @@ public class FBPK_FileSystem extends GFileSystemBase {
|
||||
@Override
|
||||
public void open(TaskMonitor monitor) throws IOException, CryptoException, CancelledException {
|
||||
BinaryReader reader = new BinaryReader(provider, true /*might not always be LE*/ );
|
||||
FBPK header = new FBPK(reader);
|
||||
FBPK header = FBPK_Factory.getFBPK(reader);
|
||||
List<FBPK_Partition> partitions = header.getPartitions();
|
||||
for (FBPK_Partition partition : partitions) {
|
||||
if (partition.isFile()) {
|
||||
|
||||
@@ -15,90 +15,114 @@
|
||||
*/
|
||||
package ghidra.file.formats.android.fbpk;
|
||||
|
||||
import java.io.IOException;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.flatapi.FlatProgramAPI;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
public abstract class FBPK_Partition implements StructConverter {
|
||||
|
||||
public class FBPK_Partition implements StructConverter {
|
||||
private int type;
|
||||
private String name;
|
||||
private int dataSize;
|
||||
private int unknown1;
|
||||
private int offsetToNextPartitionTable;
|
||||
private int unknown2;
|
||||
private FBPT fbpt;
|
||||
private long dataStartOffset;
|
||||
protected int headerSize;
|
||||
protected int type;
|
||||
protected String name;
|
||||
protected int partitionIndex;
|
||||
|
||||
public FBPK_Partition(BinaryReader reader) throws IOException {
|
||||
type = reader.readNextInt();
|
||||
name = reader.readNextAsciiString(FBPK_Constants.NAME_MAX_LENGTH);
|
||||
dataSize = reader.readNextInt();
|
||||
unknown1 = reader.readNextInt();
|
||||
offsetToNextPartitionTable = reader.readNextInt();
|
||||
unknown2 = reader.readNextInt();
|
||||
if (type == FBPK_Constants.PARTITION_TYPE_DIRECTORY) {
|
||||
fbpt = new FBPT(reader);
|
||||
}
|
||||
else if (type == FBPK_Constants.PARTITION_TYPE_FILE) {
|
||||
dataStartOffset = reader.getPointerIndex();
|
||||
}
|
||||
/**
|
||||
* Returns the size of the partition's header, in bytes.
|
||||
* @return the size of the partition's header, in bytes
|
||||
*/
|
||||
public final int getHeaderSize() {
|
||||
return headerSize;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
/**
|
||||
* Returns the partition's type.
|
||||
* @return the partition's type
|
||||
*/
|
||||
public final int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
/**
|
||||
* Returns the partition's name.
|
||||
* @return the partition's name
|
||||
*/
|
||||
public final String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Fast Boot Partition Table
|
||||
* @return the Fast Boot Partition Table, could be null if file
|
||||
* Returns the offsets to the start of this
|
||||
* partition's data payload.
|
||||
* @return offsets to the partition's data
|
||||
*/
|
||||
public FBPT getFBPT() {
|
||||
return fbpt;
|
||||
public abstract long getDataStartOffset();
|
||||
|
||||
/**
|
||||
* Returns the partition's data payload size.
|
||||
* @return the partition's data payload size
|
||||
*/
|
||||
public abstract int getDataSize();
|
||||
|
||||
/**
|
||||
* Returns true if this partition represents a file.
|
||||
* @return true if this partition represents a file
|
||||
*/
|
||||
public abstract boolean isFile();
|
||||
|
||||
/**
|
||||
* Returns the offset to the next partition (for non-adjoining partitions).
|
||||
* Returns 0 is the next partition is adjoingin (immediately following the previous).
|
||||
* @return offset to the next partition, or 0
|
||||
*/
|
||||
public abstract int getOffsetToNextPartitionTable();
|
||||
|
||||
/**
|
||||
* Returns the partition's index.
|
||||
* @return the partition's index
|
||||
*/
|
||||
public final int getPartitionIndex() {
|
||||
return partitionIndex;
|
||||
}
|
||||
|
||||
public long getDataStartOffset() {
|
||||
return dataStartOffset;
|
||||
}
|
||||
/**
|
||||
* Annotates the program with this partition's data structures.
|
||||
* @param program the program to markup
|
||||
* @param address the address of the partition
|
||||
* @param monitor the task monitor
|
||||
* @param log the message log
|
||||
* @throws Exception if any exception occurs during markup
|
||||
*/
|
||||
public void markup(Program program, Address address, TaskMonitor monitor, MessageLog log) throws Exception {
|
||||
FlatProgramAPI api = new FlatProgramAPI(program);
|
||||
|
||||
public int getDataSize() {
|
||||
return dataSize;
|
||||
}
|
||||
DataType partitionDataType = toDataType();
|
||||
|
||||
public int getOffsetToNextPartitionTable() {
|
||||
return offsetToNextPartitionTable;
|
||||
}
|
||||
Data partitionData = program.getListing().createData(address, partitionDataType);
|
||||
|
||||
public boolean isDirectory() {
|
||||
return getType() == FBPK_Constants.PARTITION_TYPE_DIRECTORY;
|
||||
}
|
||||
if (partitionData == null) {
|
||||
log.appendMsg("Unable to apply partition data, stopping - " + address);
|
||||
return;
|
||||
}
|
||||
|
||||
public boolean isFile() {
|
||||
return getType() == FBPK_Constants.PARTITION_TYPE_FILE;
|
||||
}
|
||||
program.getListing()
|
||||
.setComment(address, CodeUnit.PLATE_COMMENT,
|
||||
getName() + " - " + getPartitionIndex());
|
||||
|
||||
public int getUnknown1() {
|
||||
return unknown1;
|
||||
}
|
||||
api.createFragment(getName(), address, partitionDataType.getLength());
|
||||
|
||||
public int getUnknown2() {
|
||||
return unknown2;
|
||||
}
|
||||
Address dataStart = api.toAddr(getDataStartOffset());
|
||||
api.createFragment(getName(), dataStart, getDataSize());
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure struct = new StructureDataType(FBPK_Partition.class.getSimpleName(), 0);
|
||||
struct.add(DWORD, "type", null);
|
||||
struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH, "name", null);
|
||||
struct.add(DWORD, "dataSize", null);
|
||||
struct.add(DWORD, "unknown1", null);
|
||||
struct.add(DWORD, "offsetToNextPartitionTable", null);
|
||||
struct.add(DWORD, "unknown2", null);
|
||||
return struct;
|
||||
Data offsetData = partitionData.getComponent(2);
|
||||
api.createMemoryReference(offsetData, dataStart, RefType.DATA);
|
||||
|
||||
address = address.add(partitionDataType.getLength());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,159 +15,53 @@
|
||||
*/
|
||||
package ghidra.file.formats.android.fbpk;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.flatapi.FlatProgramAPI;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class FBPT implements StructConverter {
|
||||
private String magic;
|
||||
private int unknown1;
|
||||
private int unknown2;
|
||||
private int unknown3;
|
||||
private int nEntries;
|
||||
private int unknownA;
|
||||
private int unknownB;
|
||||
private int unknownC;
|
||||
private int unknownD;
|
||||
private int unknownE;
|
||||
private int unknownF;
|
||||
private int unknownG;
|
||||
private int unknownH;
|
||||
private int unknownI;
|
||||
private int unknownJ;
|
||||
private int unknownK;
|
||||
private int unknownL;
|
||||
private int unknownM;
|
||||
private List<FBPT_Entry> entries = new ArrayList<>();
|
||||
public abstract class FBPT implements StructConverter {
|
||||
|
||||
public FBPT(BinaryReader reader) throws IOException {
|
||||
magic = reader.readNextAsciiString(FBPK_Constants.FBPT.length());
|
||||
unknown1 = reader.readNextInt();
|
||||
unknown2 = reader.readNextInt();
|
||||
unknown3 = reader.readNextInt();
|
||||
nEntries = reader.readNextInt();
|
||||
unknownA = reader.readNextInt();
|
||||
unknownB = reader.readNextInt();
|
||||
unknownC = reader.readNextInt();
|
||||
unknownD = reader.readNextInt();
|
||||
unknownE = reader.readNextInt();
|
||||
unknownF = reader.readNextInt();
|
||||
unknownG = reader.readNextInt();
|
||||
unknownH = reader.readNextInt();
|
||||
unknownI = reader.readNextInt();
|
||||
unknownJ = reader.readNextInt();
|
||||
unknownK = reader.readNextInt();
|
||||
unknownL = reader.readNextInt();
|
||||
unknownM = reader.readNextInt();
|
||||
public abstract String getMagic();
|
||||
|
||||
for (int i = 0; i < nEntries; ++i) {
|
||||
entries.add(new FBPT_Entry(reader, i == nEntries - 1));
|
||||
public abstract List<FBPT_Entry> getEntries();
|
||||
|
||||
public void processFBPT(Program program, Address address, TaskMonitor monitor, MessageLog log) throws Exception {
|
||||
FlatProgramAPI api = new FlatProgramAPI(program);
|
||||
|
||||
DataType fbptDataType = toDataType();
|
||||
Data fbptData = program.getListing().createData(address, fbptDataType);
|
||||
if (fbptData == null) {
|
||||
log.appendMsg("Unable to apply FBPT data, stopping - " + address);
|
||||
return;
|
||||
}
|
||||
String comment = "FBPT" + "\n" + "Num of entries: " + getEntries().size();
|
||||
program.getListing().setComment(address, CodeUnit.PLATE_COMMENT, comment);
|
||||
api.createFragment(FBPK_Constants.FBPT, address, fbptDataType.getLength());
|
||||
address = address.add(fbptDataType.getLength());
|
||||
|
||||
processFbPtEntries(program, address, monitor, log);
|
||||
}
|
||||
|
||||
private void processFbPtEntries(Program program, Address address, TaskMonitor monitor, MessageLog log) throws Exception {
|
||||
int i = 0;
|
||||
FlatProgramAPI api = new FlatProgramAPI(program);
|
||||
for (FBPT_Entry entry : getEntries()) {
|
||||
monitor.checkCanceled();
|
||||
DataType entryDataType = entry.toDataType();
|
||||
Data entryData = program.getListing().createData(address, entryDataType);
|
||||
if (entryData == null) {
|
||||
log.appendMsg("Unable to apply FBPT Entry data, stopping - " + address);
|
||||
return;
|
||||
}
|
||||
program.getListing().setComment(address, CodeUnit.PLATE_COMMENT, entry.getName() + " - " + i++);
|
||||
api.createFragment(FBPK_Constants.FBPT, address, entryDataType.getLength());
|
||||
address = address.add(entryDataType.getLength());
|
||||
}
|
||||
}
|
||||
|
||||
public String getMagic() {
|
||||
return magic;
|
||||
}
|
||||
|
||||
public int getNEntries() {
|
||||
return nEntries;
|
||||
}
|
||||
|
||||
public List<FBPT_Entry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public int getUnknown1() {
|
||||
return unknown1;
|
||||
}
|
||||
|
||||
public int getUnknown2() {
|
||||
return unknown2;
|
||||
}
|
||||
|
||||
public int getUnknown3() {
|
||||
return unknown3;
|
||||
}
|
||||
|
||||
public int getUnknownA() {
|
||||
return unknownA;
|
||||
}
|
||||
|
||||
public int getUnknownB() {
|
||||
return unknownB;
|
||||
}
|
||||
|
||||
public int getUnknownC() {
|
||||
return unknownC;
|
||||
}
|
||||
|
||||
public int getUnknownD() {
|
||||
return unknownD;
|
||||
}
|
||||
|
||||
public int getUnknownE() {
|
||||
return unknownE;
|
||||
}
|
||||
|
||||
public int getUnknownF() {
|
||||
return unknownF;
|
||||
}
|
||||
|
||||
public int getUnknownG() {
|
||||
return unknownG;
|
||||
}
|
||||
|
||||
public int getUnknownH() {
|
||||
return unknownH;
|
||||
}
|
||||
|
||||
public int getUnknownI() {
|
||||
return unknownI;
|
||||
}
|
||||
|
||||
public int getUnknownJ() {
|
||||
return unknownJ;
|
||||
}
|
||||
|
||||
public int getUnknownK() {
|
||||
return unknownK;
|
||||
}
|
||||
|
||||
public int getUnknownL() {
|
||||
return unknownL;
|
||||
}
|
||||
|
||||
public int getUnknownM() {
|
||||
return unknownM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure struct = new StructureDataType(FBPT.class.getSimpleName(), 0);
|
||||
struct.add(STRING, FBPK_Constants.FBPT.length(), "magic", null);
|
||||
struct.add(DWORD, "unknown1", null);
|
||||
struct.add(DWORD, "unknown2", null);
|
||||
struct.add(DWORD, "unknown3", null);
|
||||
struct.add(DWORD, "nEntries", null);
|
||||
struct.add(DWORD, "unknownA", null);
|
||||
struct.add(DWORD, "unknownB", null);
|
||||
struct.add(DWORD, "unknownC", null);
|
||||
struct.add(DWORD, "unknownD", null);
|
||||
struct.add(DWORD, "unknownE", null);
|
||||
struct.add(DWORD, "unknownF", null);
|
||||
struct.add(DWORD, "unknownG", null);
|
||||
struct.add(DWORD, "unknownH", null);
|
||||
struct.add(DWORD, "unknownI", null);
|
||||
struct.add(DWORD, "unknownJ", null);
|
||||
struct.add(DWORD, "unknownK", null);
|
||||
struct.add(DWORD, "unknownL", null);
|
||||
struct.add(DWORD, "unknownM", null);
|
||||
return struct;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,91 +15,10 @@
|
||||
*/
|
||||
package ghidra.file.formats.android.fbpk;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class FBPT_Entry implements StructConverter {
|
||||
private String name;
|
||||
private String guid1;
|
||||
private String guid2;
|
||||
private String padding;
|
||||
private int unknown1;
|
||||
private int unknown2;
|
||||
private int unknown3;
|
||||
private boolean isLast;
|
||||
public abstract class FBPT_Entry implements StructConverter {
|
||||
|
||||
public FBPT_Entry(BinaryReader reader, boolean isLast) throws IOException {
|
||||
this.isLast = isLast;
|
||||
name = reader.readNextAsciiString(FBPK_Constants.NAME_MAX_LENGTH);//not +1
|
||||
guid1 = reader.readNextAsciiString(FBPK_Constants.NAME_MAX_LENGTH + 1);
|
||||
guid2 = reader.readNextAsciiString(FBPK_Constants.NAME_MAX_LENGTH + 1);
|
||||
padding = reader.readNextAsciiString(2);
|
||||
if (FBPK_Constants.LAST_PARTITION_ENTRY.equals(name)) {
|
||||
return;
|
||||
}
|
||||
unknown1 = reader.readNextInt();
|
||||
if (!isLast) {
|
||||
unknown2 = reader.readNextInt();
|
||||
unknown3 = reader.readNextInt();
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getGuid1() {
|
||||
return guid1;
|
||||
}
|
||||
|
||||
public String getGuid2() {
|
||||
return guid2;
|
||||
}
|
||||
|
||||
public String getPadding() {
|
||||
return padding;
|
||||
}
|
||||
|
||||
public int getUnknown1() {
|
||||
return unknown1;
|
||||
}
|
||||
|
||||
public int getUnknown2() {
|
||||
return unknown2;
|
||||
}
|
||||
|
||||
public int getUnknown3() {
|
||||
return unknown3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure struct = new StructureDataType(FBPT_Entry.class.getSimpleName(), 0);
|
||||
struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH, "name", null);
|
||||
struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH + 1, "guid1", null);
|
||||
struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH + 1, "guid2", null);
|
||||
struct.add(STRING, 2, "padding", null);
|
||||
if (FBPK_Constants.LAST_PARTITION_ENTRY.equals(name) || isLast) {
|
||||
try {
|
||||
struct.setName(FBPT_Entry.class.getSimpleName() + "_last");
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
else {
|
||||
struct.add(DWORD, "unknown1", null);
|
||||
if (!isLast) {
|
||||
struct.add(DWORD, "unknown2", null);
|
||||
struct.add(DWORD, "unknown3", null);
|
||||
}
|
||||
}
|
||||
return struct;
|
||||
}
|
||||
public abstract String getName();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/* ###
|
||||
* 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.android.fbpk.v1;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.file.formats.android.fbpk.FBPK;
|
||||
import ghidra.file.formats.android.fbpk.FBPK_Constants;
|
||||
import ghidra.file.formats.android.fbpk.FBPK_Partition;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.data.StructureDataType;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class FBPKv1 implements FBPK {
|
||||
private int magic;
|
||||
private int version;
|
||||
private String string;
|
||||
private int partitionCount;
|
||||
private int size;
|
||||
private List<FBPK_Partition> partitions = new ArrayList<>();
|
||||
|
||||
public FBPKv1(BinaryReader reader) throws IOException {
|
||||
magic = reader.readNextInt();
|
||||
version = reader.readNextInt();
|
||||
string = reader.readNextAsciiString(FBPK_Constants.V1_VERSION_MAX_LENGTH);
|
||||
partitionCount = reader.readNextInt();
|
||||
size = reader.readNextInt();
|
||||
|
||||
for (int i = 0; i < partitionCount; ++i) {
|
||||
FBPKv1_Partition partition = new FBPKv1_Partition(reader);
|
||||
partitions.add(partition);
|
||||
reader.setPointerIndex(partition.getOffsetToNextPartitionTable());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMagic() {
|
||||
return magic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return string;
|
||||
}
|
||||
|
||||
public int getPartitionCount() {
|
||||
return partitionCount;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FBPK_Partition> getPartitions() {
|
||||
return partitions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure struct = new StructureDataType(FBPKv1.class.getSimpleName(), 0);
|
||||
struct.add(STRING, FBPK_Constants.FBPK.length(), "magic", null);
|
||||
struct.add(DWORD, "version", null);
|
||||
struct.add(STRING, FBPK_Constants.V1_VERSION_MAX_LENGTH, "string", null);
|
||||
struct.add(DWORD, "partitionCount", null);
|
||||
struct.add(DWORD, "size", null);
|
||||
return struct;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/* ###
|
||||
* 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.android.fbpk.v1;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.file.formats.android.fbpk.FBPK_Constants;
|
||||
import ghidra.file.formats.android.fbpk.FBPK_Partition;
|
||||
import ghidra.file.formats.android.fbpk.FBPT;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.data.StructureDataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class FBPKv1_Partition extends FBPK_Partition {
|
||||
|
||||
private int dataSize;
|
||||
private int unknown1;
|
||||
private int offsetToNextPartitionTable;
|
||||
private int unknown2;
|
||||
private FBPTv1 fbpt;
|
||||
private long dataStartOffset;
|
||||
|
||||
public FBPKv1_Partition(BinaryReader reader) throws IOException {
|
||||
long start = reader.getPointerIndex();
|
||||
|
||||
type = reader.readNextInt();
|
||||
name = reader.readNextAsciiString(FBPK_Constants.NAME_MAX_LENGTH);
|
||||
dataSize = reader.readNextInt();
|
||||
unknown1 = reader.readNextInt();
|
||||
offsetToNextPartitionTable = reader.readNextInt();
|
||||
unknown2 = reader.readNextInt();
|
||||
|
||||
headerSize = (int) (reader.getPointerIndex() - start);
|
||||
|
||||
if (type == FBPK_Constants.PARTITION_TYPE_DIRECTORY) {
|
||||
fbpt = new FBPTv1(reader);
|
||||
}
|
||||
else if (type == FBPK_Constants.PARTITION_TYPE_FILE) {
|
||||
dataStartOffset = reader.getPointerIndex();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FBPT.
|
||||
* Could be null if this partition is a FILE.
|
||||
* @return the FBPT
|
||||
*/
|
||||
public FBPT getFBPT() {
|
||||
return fbpt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDataStartOffset() {
|
||||
return dataStartOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDataSize() {
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
public int getOffsetToNextPartitionTable() {
|
||||
return offsetToNextPartitionTable;
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return getType() == FBPK_Constants.PARTITION_TYPE_DIRECTORY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFile() {
|
||||
return getType() == FBPK_Constants.PARTITION_TYPE_FILE;
|
||||
}
|
||||
|
||||
public int getUnknown1() {
|
||||
return unknown1;
|
||||
}
|
||||
|
||||
public int getUnknown2() {
|
||||
return unknown2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markup(Program program, Address address, TaskMonitor monitor, MessageLog log) throws Exception {
|
||||
|
||||
super.markup(program, address, monitor, log);
|
||||
|
||||
if (isDirectory()) {
|
||||
if (fbpt != null) {
|
||||
fbpt.processFBPT(program, address.add(headerSize), monitor, log);
|
||||
}
|
||||
}
|
||||
else if (isFile()) {
|
||||
//unused, but leave as placeholder for future
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure struct = new StructureDataType(FBPKv1_Partition.class.getSimpleName(), 0);
|
||||
struct.add(DWORD, "type", null);
|
||||
struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH, "name", null);
|
||||
struct.add(DWORD, "dataSize", null);
|
||||
struct.add(DWORD, "unknown1", null);
|
||||
struct.add(DWORD, "offsetToNextPartitionTable", null);
|
||||
struct.add(DWORD, "unknown2", null);
|
||||
return struct;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/* ###
|
||||
* 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.android.fbpk.v1;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.file.formats.android.fbpk.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class FBPTv1 extends FBPT {
|
||||
private String magic;
|
||||
private int nEntries;
|
||||
private List<FBPT_Entry> entries = new ArrayList<>();
|
||||
|
||||
public FBPTv1(BinaryReader reader) throws IOException {
|
||||
magic = reader.readNextAsciiString(FBPK_Constants.FBPT.length());
|
||||
reader.readNextInt();//unknown0
|
||||
reader.readNextInt();//unknown1
|
||||
reader.readNextInt();//unknown2
|
||||
nEntries = reader.readNextInt();
|
||||
reader.readNextInt();//unknown3
|
||||
reader.readNextInt();//unknown4
|
||||
reader.readNextInt();//unknown5
|
||||
reader.readNextInt();//unknown6
|
||||
reader.readNextInt();//unknown7
|
||||
reader.readNextInt();//unknown8
|
||||
reader.readNextInt();//unknown9
|
||||
reader.readNextInt();//unknownA
|
||||
reader.readNextInt();//unknownB
|
||||
reader.readNextInt();//unknownC
|
||||
reader.readNextInt();//unknownD
|
||||
reader.readNextInt();//unknownE
|
||||
reader.readNextInt();//unknownF
|
||||
|
||||
for (int i = 0; i < nEntries; ++i) {
|
||||
entries.add(new FBPTv1_Entry(reader, i == nEntries - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMagic() {
|
||||
return magic;
|
||||
}
|
||||
|
||||
public int getNEntries() {
|
||||
return nEntries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FBPT_Entry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure struct = new StructureDataType(FBPTv1.class.getSimpleName(), 0);
|
||||
struct.add(STRING, FBPK_Constants.FBPT.length(), "magic", null);
|
||||
struct.add(DWORD, "unknown0", null);
|
||||
struct.add(DWORD, "unknown1", null);
|
||||
struct.add(DWORD, "unknown2", null);
|
||||
struct.add(DWORD, "nEntries", null);
|
||||
struct.add(DWORD, "unknown3", null);
|
||||
struct.add(DWORD, "unknown4", null);
|
||||
struct.add(DWORD, "unknown5", null);
|
||||
struct.add(DWORD, "unknown6", null);
|
||||
struct.add(DWORD, "unknown7", null);
|
||||
struct.add(DWORD, "unknown8", null);
|
||||
struct.add(DWORD, "unknown9", null);
|
||||
struct.add(DWORD, "unknownA", null);
|
||||
struct.add(DWORD, "unknownB", null);
|
||||
struct.add(DWORD, "unknownC", null);
|
||||
struct.add(DWORD, "unknownD", null);
|
||||
struct.add(DWORD, "unknownE", null);
|
||||
struct.add(DWORD, "unknownF", null);
|
||||
return struct;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/* ###
|
||||
* 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.android.fbpk.v1;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.file.formats.android.fbpk.FBPK_Constants;
|
||||
import ghidra.file.formats.android.fbpk.FBPT_Entry;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.data.StructureDataType;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class FBPTv1_Entry extends FBPT_Entry {
|
||||
private String name;
|
||||
private String guid1;
|
||||
private String guid2;
|
||||
private String padding;
|
||||
private int unknown1;
|
||||
private int unknown2;
|
||||
private int unknown3;
|
||||
private boolean isLast;
|
||||
|
||||
public FBPTv1_Entry(BinaryReader reader, boolean isLast) throws IOException {
|
||||
this.isLast = isLast;
|
||||
name = reader.readNextAsciiString(FBPK_Constants.NAME_MAX_LENGTH);//not +1
|
||||
guid1 = reader.readNextAsciiString(FBPK_Constants.NAME_MAX_LENGTH + 1);
|
||||
guid2 = reader.readNextAsciiString(FBPK_Constants.NAME_MAX_LENGTH + 1);
|
||||
padding = reader.readNextAsciiString(FBPK_Constants.V1_PADDING_LENGTH);
|
||||
if (FBPK_Constants.V1_LAST_PARTITION_ENTRY.equals(name)) {
|
||||
return;
|
||||
}
|
||||
unknown1 = reader.readNextInt();
|
||||
if (!isLast) {
|
||||
unknown2 = reader.readNextInt();
|
||||
unknown3 = reader.readNextInt();
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getGuid1() {
|
||||
return guid1;
|
||||
}
|
||||
|
||||
public String getGuid2() {
|
||||
return guid2;
|
||||
}
|
||||
|
||||
public String getPadding() {
|
||||
return padding;
|
||||
}
|
||||
|
||||
public int getUnknown1() {
|
||||
return unknown1;
|
||||
}
|
||||
|
||||
public int getUnknown2() {
|
||||
return unknown2;
|
||||
}
|
||||
|
||||
public int getUnknown3() {
|
||||
return unknown3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure struct = new StructureDataType(FBPTv1_Entry.class.getSimpleName(), 0);
|
||||
struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH, "name", null);
|
||||
struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH + 1, "guid1", null);
|
||||
struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH + 1, "guid2", null);
|
||||
struct.add(STRING, FBPK_Constants.V1_PADDING_LENGTH, "padding", null);
|
||||
if (FBPK_Constants.V1_LAST_PARTITION_ENTRY.equals(name) || isLast) {
|
||||
try {
|
||||
struct.setName(FBPTv1_Entry.class.getSimpleName() + "_last");
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
else {
|
||||
struct.add(DWORD, "unknown1", null);
|
||||
if (!isLast) {
|
||||
struct.add(DWORD, "unknown2", null);
|
||||
struct.add(DWORD, "unknown3", null);
|
||||
}
|
||||
}
|
||||
return struct;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/* ###
|
||||
* 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.android.fbpk.v2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.file.formats.android.fbpk.FBPK;
|
||||
import ghidra.file.formats.android.fbpk.FBPK_Constants;
|
||||
import ghidra.file.formats.android.fbpk.FBPK_Partition;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.data.StructureDataType;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class FBPKv2 implements FBPK {
|
||||
private int magic;
|
||||
private int version;
|
||||
private int unknown1;
|
||||
private int unknown2;
|
||||
private String string1;
|
||||
private String string2;
|
||||
private int unknown3;
|
||||
private int partitionCount;
|
||||
private int size;
|
||||
|
||||
private List<FBPK_Partition> partitions = new ArrayList<>();
|
||||
|
||||
public FBPKv2(BinaryReader reader) throws IOException {
|
||||
magic = reader.readNextInt();
|
||||
version = reader.readNextInt();
|
||||
unknown1 = reader.readNextInt();
|
||||
unknown2 = reader.readNextInt();
|
||||
string1 = reader.readNextAsciiString(FBPK_Constants.V2_STRING1_MAX_LENGTH);
|
||||
string2 = reader.readNextAsciiString(FBPK_Constants.V2_STRING2_MAX_LENGTH);
|
||||
unknown3 = reader.readNextInt();
|
||||
partitionCount = reader.readNextInt();
|
||||
size = reader.readNextInt();
|
||||
|
||||
for (int i = 0; i < partitionCount; ++i) {
|
||||
partitions.add(new FBPKv2_Partition(reader));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMagic() {
|
||||
return magic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public int getPartitionCount() {
|
||||
return partitionCount;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FBPK_Partition> getPartitions() {
|
||||
return new ArrayList<FBPK_Partition>(partitions);
|
||||
}
|
||||
|
||||
public int getUnknown1() {
|
||||
return unknown1;
|
||||
}
|
||||
|
||||
public int getUnknown2() {
|
||||
return unknown2;
|
||||
}
|
||||
|
||||
public int getUnknown3() {
|
||||
return unknown3;
|
||||
}
|
||||
|
||||
public String getString1() {
|
||||
return string1;
|
||||
}
|
||||
|
||||
public String getString2() {
|
||||
return string2;
|
||||
};
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure struct = new StructureDataType(FBPKv2.class.getSimpleName(), 0);
|
||||
struct.add(STRING, FBPK_Constants.FBPK.length(), "magic", null);
|
||||
struct.add(DWORD, "version", null);
|
||||
struct.add(DWORD, "unknown1", null);
|
||||
struct.add(DWORD, "unknown2", null);
|
||||
struct.add(STRING, FBPK_Constants.V2_STRING1_MAX_LENGTH, "string1", null);
|
||||
struct.add(STRING, FBPK_Constants.V2_STRING2_MAX_LENGTH, "string2", null);
|
||||
struct.add(DWORD, "unknown3", null);
|
||||
struct.add(DWORD, "partitionCount", null);
|
||||
struct.add(DWORD, "size", null);
|
||||
return struct;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/* ###
|
||||
* 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.android.fbpk.v2;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.file.formats.android.fbpk.FBPK_Constants;
|
||||
import ghidra.file.formats.android.fbpk.FBPK_Partition;
|
||||
import ghidra.file.formats.android.fbpk.FBPT;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.data.StructureDataType;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class FBPKv2_Partition extends FBPK_Partition {
|
||||
|
||||
private long offset;
|
||||
private int unknown1;
|
||||
private int size;
|
||||
private int unknown2;
|
||||
private int paritionType;
|
||||
private int unknown3;
|
||||
|
||||
private FBPT fbpt;
|
||||
private UFPK ufpk;
|
||||
private UFSM ufsm;
|
||||
private UFSP ufsp;
|
||||
|
||||
public FBPKv2_Partition(BinaryReader reader) throws IOException {
|
||||
long start = reader.getPointerIndex();
|
||||
|
||||
type = reader.readNextInt();
|
||||
name = reader.readNextAsciiString(FBPK_Constants.V2_PARTITION_NAME_MAX_LENGTH);
|
||||
offset = reader.readNextInt();
|
||||
unknown1 = reader.readNextInt();
|
||||
size = reader.readNextInt();
|
||||
unknown2 = reader.readNextInt();
|
||||
paritionType = reader.readNextInt();
|
||||
unknown3 = reader.readNextInt();
|
||||
|
||||
headerSize = (int) (reader.getPointerIndex() - start);
|
||||
|
||||
BinaryReader clone = reader.clone(offset);
|
||||
|
||||
if (paritionType == 0) {
|
||||
if (name.startsWith(FBPK_Constants.V2_PARTITION)) {
|
||||
fbpt = new FBPTv2(clone);
|
||||
}
|
||||
if (name.equals(FBPK_Constants.V2_UFS)) {
|
||||
if (clone.peekNextInt() == FBPK_Constants.UFSM_MAGIC) {
|
||||
ufsm = new UFSM(clone);
|
||||
}
|
||||
else if (clone.peekNextInt() == FBPK_Constants.UFSP_MAGIC) {
|
||||
ufsp = new UFSP(clone);
|
||||
}
|
||||
}
|
||||
if (name.equals(FBPK_Constants.V2_UFSFWUPDATE)) {
|
||||
ufpk = new UFPK(clone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDataStartOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDataSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public FBPT getFBPT() {
|
||||
return fbpt;
|
||||
}
|
||||
|
||||
public UFPK getUFPK() {
|
||||
return ufpk;
|
||||
}
|
||||
|
||||
public UFSM getUFSM() {
|
||||
return ufsm;
|
||||
}
|
||||
|
||||
public UFSP getUFSP() {
|
||||
return ufsp;
|
||||
}
|
||||
|
||||
public int getUnknown1() {
|
||||
return unknown1;
|
||||
}
|
||||
|
||||
public int getUnknown2() {
|
||||
return unknown2;
|
||||
}
|
||||
|
||||
public int getParitionType() {
|
||||
return paritionType;
|
||||
}
|
||||
|
||||
public int getUnknown3() {
|
||||
return unknown3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFile() {
|
||||
return paritionType == FBPK_Constants.PARTITION_TYPE_FILE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffsetToNextPartitionTable() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markup(Program program, Address address, TaskMonitor monitor, MessageLog log)
|
||||
throws Exception {
|
||||
|
||||
super.markup(program, address, monitor, log);
|
||||
|
||||
processFBPT(program, monitor, log);
|
||||
processUFPK(program, monitor, log);
|
||||
processUFSM(program, monitor, log);
|
||||
processUFSP(program, monitor, log);
|
||||
}
|
||||
|
||||
private void processFBPT(Program program, TaskMonitor monitor, MessageLog log)
|
||||
throws Exception {
|
||||
|
||||
Address address = program.getMinAddress().getNewAddress(offset);
|
||||
|
||||
if (fbpt != null) {
|
||||
fbpt.processFBPT(program, address, monitor, log);
|
||||
}
|
||||
}
|
||||
|
||||
private void processUFPK(Program program, TaskMonitor monitor, MessageLog log)
|
||||
throws Exception {
|
||||
|
||||
Address address = program.getMinAddress().getNewAddress(offset);
|
||||
|
||||
if (ufpk != null) {
|
||||
DataType dataType = ufpk.toDataType();
|
||||
Data data = program.getListing().createData(address, dataType);
|
||||
if (data == null) {
|
||||
log.appendMsg("Unable to apply " + FBPK_Constants.UFPK_MAGIC +
|
||||
" data, stopping - " + address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processUFSM(Program program, TaskMonitor monitor, MessageLog log)
|
||||
throws Exception {
|
||||
|
||||
Address address = program.getMinAddress().getNewAddress(offset);
|
||||
|
||||
if (ufsm != null) {
|
||||
DataType dataType = ufsm.toDataType();
|
||||
Data data = program.getListing().createData(address, dataType);
|
||||
if (data == null) {
|
||||
log.appendMsg(
|
||||
"Unable to apply " + FBPK_Constants.UFSM + " data, stopping - " + address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processUFSP(Program program, TaskMonitor monitor, MessageLog log)
|
||||
throws Exception {
|
||||
|
||||
Address address = program.getMinAddress().getNewAddress(offset);
|
||||
|
||||
if (ufsp != null) {
|
||||
DataType dataType = ufsp.toDataType();
|
||||
Data data = program.getListing().createData(address, dataType);
|
||||
if (data == null) {
|
||||
log.appendMsg(
|
||||
"Unable to apply " + FBPK_Constants.UFSP + " data, stopping - " + address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure struct = new StructureDataType(FBPKv2_Partition.class.getSimpleName(), 0);
|
||||
struct.add(DWORD, "type", null);
|
||||
struct.add(STRING, FBPK_Constants.V2_PARTITION_NAME_MAX_LENGTH, "name", null);
|
||||
struct.add(DWORD, "offset", null);
|
||||
struct.add(DWORD, "unknown1", null);
|
||||
struct.add(DWORD, "size", null);
|
||||
struct.add(DWORD, "unknown2", null);
|
||||
struct.add(DWORD, "paritionType", null);
|
||||
struct.add(DWORD, "unknown3", null);
|
||||
return struct;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/* ###
|
||||
* 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.android.fbpk.v2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.file.formats.android.fbpk.FBPK_Constants;
|
||||
import ghidra.file.formats.android.fbpk.FBPT;
|
||||
import ghidra.file.formats.android.fbpk.FBPT_Entry;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.data.StructureDataType;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class FBPTv2 extends FBPT {
|
||||
private String magic;
|
||||
private int nEntries;
|
||||
private String guid;
|
||||
|
||||
private List<FBPT_Entry> entries = new ArrayList<>();
|
||||
|
||||
public FBPTv2(BinaryReader reader) throws IOException {
|
||||
magic = reader.readNextAsciiString(FBPK_Constants.FBPT.length());
|
||||
reader.readNextInt();//unknown0
|
||||
reader.readNextInt();//unknown1
|
||||
reader.readNextInt();//unknown2
|
||||
nEntries = reader.readNextInt();
|
||||
guid = reader.readNextAsciiString(FBPK_Constants.V2_GUID_MAX_LENGTH);
|
||||
reader.readNextInt();//unknown3
|
||||
reader.readNextInt();//unknown4
|
||||
reader.readNextInt();//unknown5
|
||||
|
||||
for (int i = 0; i < nEntries; ++i) {
|
||||
entries.add(new FBPTv2_Entry(reader, i == nEntries - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMagic() {
|
||||
return magic;
|
||||
}
|
||||
|
||||
public int getNEntries() {
|
||||
return nEntries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FBPT_Entry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public String getGUID() {
|
||||
return guid;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure struct = new StructureDataType(FBPTv2.class.getSimpleName(), 0);
|
||||
struct.add(STRING, FBPK_Constants.FBPT.length(), "magic", null);
|
||||
struct.add(DWORD, "unknown0", null);
|
||||
struct.add(DWORD, "unknown1", null);
|
||||
struct.add(DWORD, "unknown2", null);
|
||||
struct.add(DWORD, "nEntries", null);
|
||||
struct.add(STRING, FBPK_Constants.V2_GUID_MAX_LENGTH, "guid", null);
|
||||
struct.add(DWORD, "unknown3", null);
|
||||
struct.add(DWORD, "unknown4", null);
|
||||
struct.add(DWORD, "unknown5", null);
|
||||
return struct;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/* ###
|
||||
* 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.android.fbpk.v2;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.file.formats.android.fbpk.FBPK_Constants;
|
||||
import ghidra.file.formats.android.fbpk.FBPT_Entry;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.data.StructureDataType;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class FBPTv2_Entry extends FBPT_Entry {
|
||||
private String name;
|
||||
private String guid1;
|
||||
private String guid2;
|
||||
private String format;
|
||||
private int unknown1;
|
||||
private int unknown2;
|
||||
private int unknown3;
|
||||
private boolean isLast;
|
||||
|
||||
public FBPTv2_Entry(BinaryReader reader, boolean isLast) throws IOException {
|
||||
this.isLast = isLast;
|
||||
name = reader.readNextAsciiString(FBPK_Constants.NAME_MAX_LENGTH);//not +1
|
||||
guid1 = reader.readNextAsciiString(FBPK_Constants.NAME_MAX_LENGTH + 1);
|
||||
guid2 = reader.readNextAsciiString(FBPK_Constants.NAME_MAX_LENGTH + 1);
|
||||
format = reader.readNextAsciiString(FBPK_Constants.V2_FORMAT_MAX_LENGTH);
|
||||
unknown1 = reader.readNextInt();
|
||||
unknown2 = reader.readNextInt();
|
||||
unknown3 = reader.readNextInt();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getGuid1() {
|
||||
return guid1;
|
||||
}
|
||||
|
||||
public String getGuid2() {
|
||||
return guid2;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public int getUnknown1() {
|
||||
return unknown1;
|
||||
}
|
||||
|
||||
public int getUnknown2() {
|
||||
return unknown2;
|
||||
}
|
||||
|
||||
public int getUnknown3() {
|
||||
return unknown3;
|
||||
}
|
||||
|
||||
public boolean isLast() {
|
||||
return isLast;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure struct = new StructureDataType(FBPTv2_Entry.class.getSimpleName(), 0);
|
||||
struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH, "name", null);
|
||||
struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH + 1, "guid1", null);
|
||||
struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH + 1, "guid2", null);
|
||||
struct.add(STRING, FBPK_Constants.V2_FORMAT_MAX_LENGTH, "format", null);
|
||||
struct.add(DWORD, "unknown1", null);
|
||||
struct.add(DWORD, "unknown2", null);
|
||||
struct.add(DWORD, "unknown3", null);
|
||||
return struct;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/* ###
|
||||
* 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.android.fbpk.v2;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.file.formats.android.fbpk.FBPK_Constants;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.data.StructureDataType;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class UFPK implements StructConverter {
|
||||
private String magic;
|
||||
private int unknown1;
|
||||
private String string1;
|
||||
|
||||
public UFPK(BinaryReader reader) throws IOException {
|
||||
magic = reader.readNextAsciiString(FBPK_Constants.UFPK.length());
|
||||
unknown1 = reader.readNextInt();
|
||||
string1 = reader.readNextAsciiString(FBPK_Constants.V2_UFPK_STRING1_MAX_LENGTH);
|
||||
}
|
||||
|
||||
public String getMagic() {
|
||||
return magic;
|
||||
}
|
||||
|
||||
public int getUnknown1() {
|
||||
return unknown1;
|
||||
}
|
||||
|
||||
public String getString1() {
|
||||
return string1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure struct = new StructureDataType(UFPK.class.getSimpleName(), 0);
|
||||
struct.add(STRING, magic.length(), "magic", null);
|
||||
struct.add(DWORD, "unknown1", null);
|
||||
struct.add(STRING, FBPK_Constants.V2_UFPK_STRING1_MAX_LENGTH, "string2", null);
|
||||
return struct;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/* ###
|
||||
* 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.android.fbpk.v2;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.file.formats.android.fbpk.FBPK_Constants;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.data.StructureDataType;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class UFSM implements StructConverter {
|
||||
private String magic;
|
||||
private int unknown1;
|
||||
private String string1;
|
||||
|
||||
public UFSM(BinaryReader reader) throws IOException {
|
||||
magic = reader.readNextAsciiString(FBPK_Constants.UFSM.length());
|
||||
unknown1 = reader.readNextInt();
|
||||
}
|
||||
|
||||
public String getMagic() {
|
||||
return magic;
|
||||
}
|
||||
|
||||
public int getUnknown1() {
|
||||
return unknown1;
|
||||
}
|
||||
|
||||
public String getString1() {
|
||||
return string1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure struct = new StructureDataType(UFSM.class.getSimpleName(), 0);
|
||||
struct.add(STRING, magic.length(), "magic", null);
|
||||
struct.add(DWORD, "unknown1", null);
|
||||
return struct;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/* ###
|
||||
* 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.android.fbpk.v2;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.file.formats.android.fbpk.FBPK_Constants;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.data.StructureDataType;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class UFSP implements StructConverter {
|
||||
private String magic;
|
||||
private int unknown1;
|
||||
private String string1;
|
||||
|
||||
public UFSP(BinaryReader reader) throws IOException {
|
||||
magic = reader.readNextAsciiString(FBPK_Constants.UFSP.length());
|
||||
unknown1 = reader.readNextInt();
|
||||
}
|
||||
|
||||
public String getMagic() {
|
||||
return magic;
|
||||
}
|
||||
|
||||
public int getUnknown1() {
|
||||
return unknown1;
|
||||
}
|
||||
|
||||
public String getString1() {
|
||||
return string1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
Structure struct = new StructureDataType(UFSP.class.getSimpleName(), 0);
|
||||
struct.add(STRING, magic.length(), "magic", null);
|
||||
struct.add(DWORD, "unknown1", null);
|
||||
return struct;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,12 +16,6 @@
|
||||
package ghidra.file.formats.android.oat;
|
||||
|
||||
import ghidra.app.util.bin.format.elf.ElfSectionHeaderConstants;
|
||||
import ghidra.app.util.opinion.ElfLoader;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolTable;
|
||||
|
||||
/**
|
||||
* https://android.googlesource.com/platform/art/+/marshmallow-mr3-release/runtime/oat.h
|
||||
@@ -47,6 +41,28 @@ public final class OatConstants {
|
||||
|
||||
public final static String DOT_OAT_PATCHES_SECTION_NAME = ".oat_patches";
|
||||
|
||||
/* Keys from the OAT header "key/value" store. */
|
||||
public final static String kApexVersionsKey = "apex-versions";
|
||||
public final static String kBootClassPathKey = "bootclasspath";
|
||||
public final static String kBootClassPathChecksumsKey = "bootclasspath-checksums";
|
||||
public final static String kClassPathKey = "classpath";
|
||||
public final static String kCompilationReasonKey = "compilation-reason";
|
||||
public final static String kCompilerFilter = "compiler-filter";
|
||||
public final static String kConcurrentCopying = "concurrent-copying";
|
||||
public final static String kDebuggableKey = "debuggable";
|
||||
public final static String kDex2OatCmdLineKey = "dex2oat-cmdline";
|
||||
public final static String kDex2OatHostKey = "dex2oat-host";
|
||||
public final static String kHasPatchInfoKey = "has-patch-info";
|
||||
public final static String kImageLocationKey = "image-location";
|
||||
public final static String kNativeDebuggableKey = "native-debuggable";
|
||||
public final static String kPicKey = "pic";
|
||||
public final static String kRequiresImage = "requires-image";
|
||||
|
||||
/** Boolean value used in the Key/Value store for TRUE. */
|
||||
public final static String kTrueValue = "true";
|
||||
/** Boolean value used in the Key/Value store for FALSE. */
|
||||
public final static String kFalseValue = "false";
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
// NOTE: we plan to only support RELEASE versions...
|
||||
// Upper case indicates supported version.
|
||||
@@ -78,17 +94,13 @@ public final class OatConstants {
|
||||
public final static String VERSION_10_RELEASE = "170";
|
||||
public final static String VERSION_11_RELEASE = "183";
|
||||
public final static String VERSION_12_RELEASE = "195";
|
||||
public final static String VERSION_S_V2_PREVIEW = "199";
|
||||
|
||||
public final static int VERSION_LENGTH = 3;//3 bytes in length
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
|
||||
|
||||
/**
|
||||
* This array contains version that have been actively tested and verified.
|
||||
* All other version will be considered unsupported until tested on exemplar firmware.
|
||||
* This array contains versions that have been actively tested and verified.
|
||||
* All other versions will be considered unsupported until tested on exemplar firmware.
|
||||
*/
|
||||
public final static String [ ] SUPPORTED_VERSIONS = new String [ ] {
|
||||
public final static String [] SUPPORTED_VERSIONS = new String [] {
|
||||
VERSION_KITKAT_RELEASE,
|
||||
VERSION_LOLLIPOP_RELEASE,
|
||||
VERSION_LOLLIPOP_MR1_FI_RELEASE,
|
||||
@@ -103,28 +115,9 @@ public final class OatConstants {
|
||||
VERSION_10_RELEASE,
|
||||
VERSION_11_RELEASE,
|
||||
VERSION_12_RELEASE,
|
||||
VERSION_S_V2_PREVIEW,
|
||||
};
|
||||
|
||||
/** Keys from the OAT header "key/value" store. */
|
||||
public final static String kImageLocationKey = "image-location";
|
||||
public final static String kDex2OatCmdLineKey = "dex2oat-cmdline";
|
||||
public final static String kDex2OatHostKey = "dex2oat-host";
|
||||
public final static String kPicKey = "pic";
|
||||
public final static String kHasPatchInfoKey = "has-patch-info";
|
||||
public final static String kDebuggableKey = "debuggable";
|
||||
public final static String kNativeDebuggableKey = "native-debuggable";
|
||||
public final static String kCompilerFilter = "compiler-filter";
|
||||
public final static String kClassPathKey = "classpath";
|
||||
public final static String kBootClassPathKey = "bootclasspath";
|
||||
public final static String kBootClassPathChecksumsKey = "bootclasspath-checksums";
|
||||
public final static String kConcurrentCopying = "concurrent-copying";
|
||||
public final static String kCompilationReasonKey = "compilation-reason";
|
||||
|
||||
/** Boolean value used in the Key/Value store for TRUE. */
|
||||
public final static String kTrueValue = "true";
|
||||
/** Boolean value used in the Key/Value store for FALSE. */
|
||||
public final static String kFalseValue = "false";
|
||||
|
||||
//@formatter:on
|
||||
|
||||
/**
|
||||
@@ -141,46 +134,4 @@ public final class OatConstants {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given program contain OAT information.
|
||||
* Checks for the program being an ELF, and containing the three magic OAT symbols.
|
||||
* @param program the program to inspect
|
||||
* @return true if the program is OAT
|
||||
*/
|
||||
public final static boolean isOAT(Program program) {
|
||||
if (program != null) {
|
||||
String executableFormat = program.getExecutableFormat();
|
||||
if (ElfLoader.ELF_NAME.equals(executableFormat)) {
|
||||
MemoryBlock roDataBlock =
|
||||
program.getMemory().getBlock(ElfSectionHeaderConstants.dot_rodata);
|
||||
if (roDataBlock != null) {
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Symbol oatDataSymbol = symbolTable.getPrimarySymbol(roDataBlock.getStart());
|
||||
return oatDataSymbol != null && oatDataSymbol.getName().equals(SYMBOL_OAT_DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version string from the OAT program, or "unknown" if not found/valid.
|
||||
* @param program the program to inspect
|
||||
* @return the OAT version
|
||||
*/
|
||||
final static String getOatVersion(Program program) {
|
||||
if (OatConstants.isOAT(program)) {
|
||||
Symbol symbol = OatUtilities.getOatDataSymbol(program);
|
||||
Address address = symbol.getAddress().add(MAGIC.length());
|
||||
byte[] versionBytes = new byte[VERSION_LENGTH];
|
||||
try {
|
||||
program.getMemory().getBytes(address, versionBytes);
|
||||
return new String(versionBytes).trim();
|
||||
}
|
||||
catch (Exception e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,12 +25,24 @@ import ghidra.file.formats.android.dex.format.DexHeader;
|
||||
import ghidra.file.formats.android.oat.oatdexfile.OatDexFile;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.data.Array;
|
||||
import ghidra.program.model.data.ArrayDataType;
|
||||
import ghidra.program.model.data.DWordDataType;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Undefined1DataType;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.model.symbol.Equate;
|
||||
import ghidra.program.model.symbol.EquateTable;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
import ghidra.program.model.symbol.ReferenceManager;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolTable;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@@ -53,7 +65,7 @@ public class OatHeaderAnalyzer extends FileFormatAnalyzer {
|
||||
|
||||
@Override
|
||||
public boolean canAnalyze(Program program) {
|
||||
return OatConstants.isOAT(program);
|
||||
return OatUtilities.isOAT(program);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -63,6 +63,7 @@ public final class OatHeaderFactory {
|
||||
case OatConstants.VERSION_11_RELEASE:
|
||||
return new OatHeader_11(reader);
|
||||
case OatConstants.VERSION_12_RELEASE:
|
||||
case OatConstants.VERSION_S_V2_PREVIEW:
|
||||
return new OatHeader_12(reader);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ import ghidra.util.exception.DuplicateNameException;
|
||||
* https://android.googlesource.com/platform/art/+/refs/heads/android-s-beta-5/runtime/oat.h#125
|
||||
*
|
||||
* https://android.googlesource.com/platform/art/+/refs/heads/android12-release/runtime/oat.h#125
|
||||
*
|
||||
* https://android.googlesource.com/platform/art/+/refs/heads/android-s-v2-preview-1/runtime/oat.h#125
|
||||
*/
|
||||
public class OatHeader_12 extends OatHeader {
|
||||
protected int oat_checksum_;
|
||||
|
||||
@@ -19,7 +19,9 @@ import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.MemoryByteProvider;
|
||||
import ghidra.app.util.bin.format.elf.ElfSectionHeaderConstants;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.ElfLoader;
|
||||
@@ -42,7 +44,7 @@ public final class OatUtilities {
|
||||
* Returns null when the "oatdata" symbol does not exist.
|
||||
*/
|
||||
public static BinaryReader getBinaryReader(Program program) {
|
||||
if (OatConstants.isOAT(program)) {
|
||||
if (isOAT(program)) {
|
||||
Symbol symbol = getOatDataSymbol(program);
|
||||
if (symbol != null && symbol.getName().equals(OatConstants.SYMBOL_OAT_DATA)) {
|
||||
ByteProvider provider =
|
||||
@@ -53,6 +55,28 @@ public final class OatUtilities {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given program contain OAT information.
|
||||
* Checks for the program being an ELF, and containing the three magic OAT symbols.
|
||||
* @param program the program to inspect
|
||||
* @return true if the program is OAT
|
||||
*/
|
||||
public static boolean isOAT(Program program) {
|
||||
if (program != null) {
|
||||
String executableFormat = program.getExecutableFormat();
|
||||
if (ElfLoader.ELF_NAME.equals(executableFormat)) {
|
||||
MemoryBlock roDataBlock =
|
||||
program.getMemory().getBlock(ElfSectionHeaderConstants.dot_rodata);
|
||||
if (roDataBlock != null) {
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Symbol oatDataSymbol = symbolTable.getPrimarySymbol(roDataBlock.getStart());
|
||||
return oatDataSymbol != null && oatDataSymbol.getName().equals(OatConstants.SYMBOL_OAT_DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isELF(Program program) {
|
||||
return ElfLoader.ELF_NAME.equals(program.getExecutableFormat());
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ public class OatClassFactory {
|
||||
case OatConstants.VERSION_11_RELEASE:
|
||||
return new OatClass_Android11(reader, classDataItem, oatVersion);
|
||||
case OatConstants.VERSION_12_RELEASE:
|
||||
case OatConstants.VERSION_S_V2_PREVIEW:
|
||||
return new OatClass_Android12(reader, classDataItem, oatVersion);
|
||||
default:
|
||||
throw new UnsupportedOatVersionException(
|
||||
|
||||
@@ -50,6 +50,7 @@ public final class OatDexFileFactory {
|
||||
case OatConstants.VERSION_11_RELEASE:
|
||||
return new OatDexFile_Android11(reader, bundle);
|
||||
case OatConstants.VERSION_12_RELEASE:
|
||||
case OatConstants.VERSION_S_V2_PREVIEW:
|
||||
return new OatDexFile_Android12(reader, bundle);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ public final class OatQuickMethodHeaderFactory {
|
||||
case OatConstants.VERSION_11_RELEASE:
|
||||
return 8;
|
||||
case OatConstants.VERSION_12_RELEASE:
|
||||
case OatConstants.VERSION_S_V2_PREVIEW:
|
||||
return 4;
|
||||
}
|
||||
throw new IOException("OatQuickMethodHeader unsupported OAT version: " + oatVersion);
|
||||
|
||||
@@ -41,6 +41,7 @@ public final class TypeLookupTableFactory {
|
||||
case OatConstants.VERSION_11_RELEASE:
|
||||
return new TypeLookupTable_Android11(reader);
|
||||
case OatConstants.VERSION_12_RELEASE:
|
||||
case OatConstants.VERSION_S_V2_PREVIEW:
|
||||
return new TypeLookupTable_Android12(reader);
|
||||
default:
|
||||
throw new IOException(new UnsupportedOatVersionException(
|
||||
|
||||
@@ -15,14 +15,19 @@
|
||||
*/
|
||||
package ghidra.file.formats.android.vdex;
|
||||
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.MemoryByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.file.analyzers.FileFormatAnalyzer;
|
||||
import ghidra.file.formats.android.dex.format.DexHeader;
|
||||
import ghidra.file.formats.android.oat.OatConstants;
|
||||
import ghidra.file.formats.android.oat.OatUtilities;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.ArrayDataType;
|
||||
import ghidra.program.model.data.ByteDataType;
|
||||
import ghidra.program.model.data.DWordDataType;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
@@ -48,11 +53,11 @@ public class VdexHeaderAnalyzer extends FileFormatAnalyzer {
|
||||
|
||||
@Override
|
||||
public boolean canAnalyze(Program program) {
|
||||
return VdexConstants.isVDEX(program)
|
||||
//HACK:
|
||||
//Make analyzer appear after VDEX is merged with OAT program
|
||||
//Currently, analyzers will not recognize the new VDEX block being added
|
||||
|| OatConstants.isOAT(program);
|
||||
// Return true if this program is just a VDEX, but also return
|
||||
// true if the program is an OAT.
|
||||
// On Android at runtime, VDEX is merged with OAT in memory.
|
||||
// Allow this analyzer to also run on OAT files to look for existence of VDEX.
|
||||
return VdexConstants.isVDEX(program) || OatUtilities.isOAT(program);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,7 +70,8 @@ public class VdexHeaderAnalyzer extends FileFormatAnalyzer {
|
||||
throws Exception {
|
||||
Address address = VdexConstants.findVDEX(program);
|
||||
if (address == null) {
|
||||
log.appendMsg(getClass().getSimpleName() + " - no vdex header found in memory, skipping");
|
||||
log.appendMsg(
|
||||
getClass().getSimpleName() + " - no vdex header found in memory, skipping");
|
||||
return true;
|
||||
}
|
||||
ByteProvider provider = new MemoryByteProvider(program.getMemory(), address);
|
||||
|
||||
@@ -21,14 +21,19 @@ import java.util.List;
|
||||
import ghidra.app.cmd.comments.SetCommentCmd;
|
||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.MemoryByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.file.analyzers.FileFormatAnalyzer;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@@ -188,14 +193,14 @@ public class NewExt4Analyzer extends FileFormatAnalyzer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Data createData( Program program, Address address, DataType datatype ) throws Exception {
|
||||
protected Data createData( Program program, Address address, DataType datatype ) throws CodeUnitInsertionException {
|
||||
if ( program.getMemory( ).contains( address ) ) {
|
||||
return super.createData( program, address, datatype );
|
||||
}
|
||||
if ( program2 != null && program2.getMemory( ).contains( address ) ) {
|
||||
return super.createData( program2, address, datatype );
|
||||
}
|
||||
throw new RuntimeException( "Cannot create data, neither program contains that address." );
|
||||
throw new CodeUnitInsertionException( "Cannot create data, neither program contains that address." );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user