Merge remote-tracking branch 'origin/GP-1461_GhidraKnight_Android12--SQUASHED'

This commit is contained in:
Ryan Kurtz
2022-06-06 15:01:52 -04:00
38 changed files with 1750 additions and 650 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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() {

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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");
}
}

View 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()) {

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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";
}
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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_;

View File

@@ -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());
}

View File

@@ -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(

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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(

View File

@@ -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);

View File

@@ -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