GP-1247: Changes to support Android 12

This commit is contained in:
GhidraKnight
2021-11-01 10:19:04 -04:00
committed by Ryan Kurtz
parent 91c07fd933
commit 87d4858cf2
137 changed files with 4107 additions and 1572 deletions
@@ -37,7 +37,7 @@ public class DexLoader extends AbstractLibrarySupportLoader {
}
@Override
public String getName( ) {
public String getName() {
return "Dalvik Executable (DEX)";
}
@@ -70,83 +70,106 @@ public class DexLoader extends AbstractLibrarySupportLoader {
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program, TaskMonitor monitor, MessageLog log) throws IOException {
monitor.setMessage( "DEX Loader: creating dex memory" );
monitor.setMessage(getMonitorMessagePrimary());
try {
Address start = program.getAddressFactory().getDefaultAddressSpace().getAddress( 0x0 );
Address start = program.getAddressFactory().getDefaultAddressSpace().getAddress(0x0);
long length = provider.length();
try (InputStream inputStream = provider.getInputStream(0)) {
program.getMemory().createInitializedBlock(".dex", start, inputStream, length,
monitor, false);
program.getMemory()
.createInitializedBlock(getMemoryBlockName(), start, inputStream, length,
monitor, false);
}
BinaryReader reader = new BinaryReader( provider, true );
DexHeader header = DexHeaderFactory.getDexHeader( reader );
BinaryReader reader = new BinaryReader(provider, true);
DexHeader header = DexHeaderFactory.getDexHeader(reader);
monitor.setMessage( "DEX Loader: creating method byte code" );
monitor.setMessage(getMonitorMessageSecondary());
createMethodLookupMemoryBlock( program, monitor );
createMethodByteCodeBlock( program, length, monitor);
createMethodLookupMemoryBlock(program, monitor);
createMethodByteCodeBlock(program, length, monitor);
for ( ClassDefItem item : header.getClassDefs( ) ) {
monitor.checkCanceled( );
for (ClassDefItem item : header.getClassDefs()) {
monitor.checkCanceled();
ClassDataItem classDataItem = item.getClassDataItem( );
if ( classDataItem == null ) {
ClassDataItem classDataItem = item.getClassDataItem();
if (classDataItem == null) {
continue;
}
createMethods( program, header, item, classDataItem.getDirectMethods( ), monitor, log );
createMethods( program, header, item, classDataItem.getVirtualMethods( ), monitor, log );
createMethods(program, header, item, classDataItem.getDirectMethods(), monitor,
log);
createMethods(program, header, item, classDataItem.getVirtualMethods(), monitor,
log);
}
}
catch ( Exception e) {
log.appendException( e );
catch (Exception e) {
log.appendException(e);
}
}
private void createMethodByteCodeBlock(Program program, long length, TaskMonitor monitor) throws Exception {
Address address = toAddr( program, DexUtil.METHOD_ADDRESS );
MemoryBlock block = program.getMemory( ).createInitializedBlock( "method_bytecode", address, length, (byte) 0xff, monitor, false );
block.setRead( true );
block.setWrite( false );
block.setExecute( true );
protected void createMethodByteCodeBlock(Program program, long length, TaskMonitor monitor)
throws Exception {
Address address = toAddr(program, DexUtil.METHOD_ADDRESS);
MemoryBlock block = program.getMemory()
.createInitializedBlock("method_bytecode", address, length, (byte) 0xff, monitor,
false);
block.setRead(true);
block.setWrite(false);
block.setExecute(true);
}
private void createMethodLookupMemoryBlock(Program program, TaskMonitor monitor) throws Exception {
Address address = toAddr( program, DexUtil.LOOKUP_ADDRESS );
MemoryBlock block = program.getMemory( ).createInitializedBlock( "method_lookup", address, DexUtil.MAX_METHOD_LENGTH, (byte) 0xff, monitor, false );
block.setRead( true );
block.setWrite( false );
block.setExecute( false );
protected void createMethodLookupMemoryBlock(Program program, TaskMonitor monitor)
throws Exception {
Address address = toAddr(program, DexUtil.LOOKUP_ADDRESS);
MemoryBlock block = program.getMemory()
.createInitializedBlock("method_lookup", address, DexUtil.MAX_METHOD_LENGTH,
(byte) 0xff, monitor, false);
block.setRead(true);
block.setWrite(false);
block.setExecute(false);
}
private void createMethods( Program program, DexHeader header, ClassDefItem item, List< EncodedMethod > methods, TaskMonitor monitor, MessageLog log ) throws Exception {
for ( int i = 0 ; i < methods.size( ) ; ++i ) {
monitor.checkCanceled( );
protected void createMethods(Program program, DexHeader header, ClassDefItem item,
List<EncodedMethod> methods, TaskMonitor monitor, MessageLog log) throws Exception {
for (int i = 0; i < methods.size(); ++i) {
monitor.checkCanceled();
EncodedMethod encodedMethod = methods.get( i );
EncodedMethod encodedMethod = methods.get(i);
CodeItem codeItem = encodedMethod.getCodeItem( );
CodeItem codeItem = encodedMethod.getCodeItem();
Address methodIndexAddress = DexUtil.toLookupAddress( program, encodedMethod.getMethodIndex( ) );
Address methodIndexAddress =
DexUtil.toLookupAddress(program, encodedMethod.getMethodIndex());
if ( codeItem == null ) {//external method
//TODO
if (codeItem == null) {
//external method
}
else {
Address methodAddress = toAddr( program, DexUtil.METHOD_ADDRESS + encodedMethod.getCodeOffset( ) );
Address methodAddress =
toAddr(program, DexUtil.METHOD_ADDRESS + encodedMethod.getCodeOffset());
byte [] instructionBytes = codeItem.getInstructionBytes( );
program.getMemory( ).setBytes( methodAddress, instructionBytes );
byte[] instructionBytes = codeItem.getInstructionBytes();
program.getMemory().setBytes(methodAddress, instructionBytes);
program.getMemory( ).setInt( methodIndexAddress, (int) methodAddress.getOffset( ) );
program.getMemory().setInt(methodIndexAddress, (int) methodAddress.getOffset());
}
}
}
private Address toAddr( Program program, long offset ) {
return program.getAddressFactory( ).getDefaultAddressSpace( ).getAddress( offset );
protected Address toAddr(Program program, long offset) {
return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
}
protected String getMemoryBlockName() {
return ".dex";
}
protected String getMonitorMessagePrimary() {
return "DEX Loader: creating dex memory";
}
protected String getMonitorMessageSecondary() {
return "DEX Loader: creating method byte code";
}
}
@@ -46,7 +46,7 @@ public abstract class FileFormatAnalyzer implements Analyzer {
}
@Override
final public boolean added(Program program, AddressSetView set, TaskMonitor monitor,
public final boolean added(Program program, AddressSetView set, TaskMonitor monitor,
MessageLog log) throws CancelledException {
try {
return analyze(program, set, monitor, log);
@@ -58,28 +58,28 @@ public abstract class FileFormatAnalyzer implements Analyzer {
}
@Override
final public void analysisEnded(Program program) {
public void analysisEnded(Program program) {
// do nothing
}
@Override
final public void registerOptions(Options options, Program program) {
public void registerOptions(Options options, Program program) {
// do nothing
}
@Override
final public void optionsChanged(Options options, Program program) {
public void optionsChanged(Options options, Program program) {
// do nothing
}
@Override
final public boolean removed(Program program, AddressSetView set, TaskMonitor monitor,
public boolean removed(Program program, AddressSetView set, TaskMonitor monitor,
MessageLog log) throws CancelledException {
return false;
}
@Override
final public boolean supportsOneTimeAnalysis() {
public boolean supportsOneTimeAnalysis() {
return false;
}
@@ -121,15 +121,6 @@ public abstract class FileFormatAnalyzer implements Analyzer {
if (isAscii && bytes.length > 1) {
changeFormatToString(component);
}
/*
if (component.getFieldName().equals("magic") ||
component.getFieldName().equals("identifier") ||
component.getFieldName().equals("compression") ||
component.getFieldName().equals("format") ||
component.getFieldName().equals("type")) {
changeFormatToString(component);
}
*/
}
address = address.add(data.getLength());
}
@@ -54,7 +54,7 @@ public class ArtAnalyzer extends FileFormatAnalyzer {
@Override
public boolean isPrototype() {
return true;
return false;
}
@Override
@@ -75,7 +75,7 @@ public class ArtAnalyzer extends FileFormatAnalyzer {
DataType headerDataType = header.toDataType();
//only set "image base" when ART header not defined at "image begin"
//---this really only opens when ART is "added to" OAT program
//---this only happens when ART is "added to" OAT program
Address imageBase = toAddr(program, header.getImageBegin());
if (BinaryLoader.BINARY_NAME.equals(program.getExecutableFormat())) {
@@ -17,7 +17,8 @@ package ghidra.file.formats.android.art;
import java.io.IOException;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -85,8 +86,7 @@ public class ArtBlock implements StructConverter, ArtCompression {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = StructConverterUtil.parseName(ArtBlock.class);
Structure structure = new StructureDataType(name, 0);
Structure structure = new StructureDataType(ArtBlock.class.getSimpleName(), 0);
structure.setCategoryPath(new CategoryPath("/art"));
structure.add(DWORD, "storage_mode_", storage_mode_.name());
structure.add(DWORD, "data_offset_", null);
@@ -20,7 +20,7 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
/**
* https://android.googlesource.com/platform/art/+/master/runtime/image.cc
* https://android.googlesource.com/platform/art/+/master/runtime/image.cc#31
*/
public final class ArtConstants {
@@ -42,6 +42,7 @@ public final class ArtConstants {
public final static String VERSION_PIE_RELEASE = "056";
public final static String VERSION_10_RELEASE = "074";//Q
public final static String VERSION_11_RELEASE = "085";//R
public final static String VERSION_12_RELEASE = "099";//S
// "005",// kitkat-release
// "009",// lollipop-release
@@ -75,6 +76,7 @@ public final class ArtConstants {
VERSION_PIE_RELEASE,
VERSION_10_RELEASE,
VERSION_11_RELEASE,
VERSION_12_RELEASE,
//@formatter:on
};
@@ -20,6 +20,7 @@ import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.file.formats.android.art.android10.ArtHeader_10;
import ghidra.file.formats.android.art.android11.ArtHeader_11;
import ghidra.file.formats.android.art.android12.ArtHeader_12;
import ghidra.file.formats.android.art.kitkat.ArtHeader_KitKat;
import ghidra.file.formats.android.art.lollipop.ArtHeader_Lollipop;
import ghidra.file.formats.android.art.lollipop.ArtHeader_LollipopMR1WFC;
@@ -45,7 +46,7 @@ public final class ArtFactory {
String version = reader.readAsciiString(4, 4);
if (magic.equals(ArtConstants.MAGIC)) {
if (ArtConstants.isSupportedVersion(version)) {
switch (version ) {
switch (version) {
case ArtConstants.VERSION_KITKAT_RELEASE:
return new ArtHeader_KitKat(reader);
case ArtConstants.VERSION_LOLLIPOP_RELEASE:
@@ -70,6 +71,8 @@ public final class ArtFactory {
return new ArtHeader_10(reader);
case ArtConstants.VERSION_11_RELEASE:
return new ArtHeader_11(reader);
case ArtConstants.VERSION_12_RELEASE:
return new ArtHeader_12(reader);
}
}
}
@@ -17,7 +17,8 @@ package ghidra.file.formats.android.art;
import java.io.IOException;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -58,8 +59,7 @@ public class ArtField implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = StructConverterUtil.parseName(ArtField.class);
Structure structure = new StructureDataType(name, 0);
Structure structure = new StructureDataType(ArtField.class.getSimpleName(), 0);
structure.setCategoryPath(new CategoryPath("/art"));
structure.add(new Pointer32DataType(), "declaring_class_", null);
structure.add(DWORD, "access_flags_", null);
@@ -19,7 +19,8 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -53,8 +54,8 @@ public class ArtFieldGroup implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = StructConverterUtil.parseName(ArtFieldGroup.class);
Structure structure = new StructureDataType(name + "_" + fieldCount, 0);
Structure structure = new StructureDataType(
ArtFieldGroup.class.getSimpleName() + "_" + fieldCount, 0);
structure.setCategoryPath(new CategoryPath("/art"));
structure.add(DWORD, "fieldCount", null);
for (int i = 0; i < fieldCount; ++i) {
@@ -19,7 +19,8 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.DuplicateNameException;
@@ -137,8 +138,7 @@ public abstract class ArtHeader implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(ArtHeader.class);
Structure structure = new StructureDataType(className, 0);
Structure structure = new StructureDataType(ArtHeader.class.getSimpleName(), 0);
structure.add(STRING, 4, "magic_", null);
structure.add(STRING, 4, "version_", null);
structure.setCategoryPath(new CategoryPath("/art"));
@@ -0,0 +1,64 @@
/* ###
* 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.art;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.file.formats.android.art.android10.ImageSections_10;
import ghidra.file.formats.android.art.android12.ImageSections_12;
import ghidra.file.formats.android.art.marshmallow.ImageSections_Marshmallow;
import ghidra.file.formats.android.art.nougat.ImageSections_Nougat;
import ghidra.file.formats.android.art.nougat.ImageSections_NougatMR2Pixel;
import ghidra.file.formats.android.art.oreo.ImageSections_Oreo;
import ghidra.file.formats.android.art.oreo.ImageSections_OreoMR1;
import ghidra.file.formats.android.art.pie.ImageSections_Pie;
public final class ArtImageSectionsFactory {
/**
* Every major version of Android has a different ImageSections enum,
* this method will return the appropriate section one.
* @param reader the binary reader for the ART file
* @param artHeader the ART Header containing the sections
* @returns the ImageSections for the specified ART version
*/
public static ArtImageSections getArtImageSections(BinaryReader reader, ArtHeader artHeader)
throws IOException {
switch (artHeader.getVersion()) {
case ArtConstants.VERSION_MARSHMALLOW_RELEASE:
return new ImageSections_Marshmallow(reader, artHeader);
case ArtConstants.VERSION_NOUGAT_RELEASE:
return new ImageSections_Nougat(reader, artHeader);
case ArtConstants.VERSION_NOUGAT_MR2_PIXEL_RELEASE:
return new ImageSections_NougatMR2Pixel(reader, artHeader);
case ArtConstants.VERSION_OREO_RELEASE:
case ArtConstants.VERSION_OREO_DR1_RELEASE:
return new ImageSections_Oreo(reader, artHeader);
case ArtConstants.VERSION_OREO_MR1_RELEASE:
return new ImageSections_OreoMR1(reader, artHeader);
case ArtConstants.VERSION_PIE_RELEASE:
return new ImageSections_Pie(reader, artHeader);
case ArtConstants.VERSION_10_RELEASE:
case ArtConstants.VERSION_11_RELEASE:
return new ImageSections_10(reader, artHeader);
case ArtConstants.VERSION_12_RELEASE:
return new ImageSections_12(reader, artHeader);
}
throw new IOException(
"Unsupported ART version for ImageSections: " + artHeader.getVersion());
}
}
@@ -17,7 +17,8 @@ package ghidra.file.formats.android.art;
import java.io.IOException;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -259,8 +260,7 @@ public class ArtMethod implements StructConverter {
DataType ptr32 = new Pointer32DataType();
DataType ptr64 = new Pointer64DataType();
String name = StructConverterUtil.parseName(ArtMethod.class);
Structure struct = new StructureDataType(name, 0);
Structure struct = new StructureDataType(ArtMethod.class.getSimpleName(), 0);
struct.setCategoryPath(new CategoryPath("/art"));
if (ArtConstants.VERSION_MARSHMALLOW_RELEASE.equals(artVersion)) {
@@ -19,7 +19,8 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -65,8 +66,8 @@ public class ArtMethodGroup implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = StructConverterUtil.parseName(ArtMethodGroup.class);
Structure structure = new StructureDataType(name + "_" + methodCount, 0);
Structure structure = new StructureDataType(
ArtMethodGroup.class.getSimpleName() + "_" + methodCount, 0);
structure.setCategoryPath(new CategoryPath("/art"));
if (pointerSize == 8) {
structure.add(QWORD, "methodCount", null);
@@ -15,9 +15,9 @@
*/
package ghidra.file.formats.android.art;
final class UnsupportedArtVersionException extends Exception {
public final class UnsupportedArtVersionException extends Exception {
UnsupportedArtVersionException(String magic, String version) {
super("Unsupported ART version: " + version);
super("Unsupported ART (" + magic.trim() + ") for version: " + version);
}
}
@@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.art.*;
import ghidra.file.formats.android.util.DecompressionManager;
import ghidra.program.model.data.DataType;
@@ -79,7 +78,7 @@ public class ArtHeader_10 extends ArtHeader {
image_roots_ = reader.readNextInt();
pointer_size_ = reader.readNextInt();
sections = new ImageSections_10(reader, this);
sections = ArtImageSectionsFactory.getArtImageSections(reader, this);
sections.parseSections(reader);
parseImageMethods(reader);
@@ -157,8 +156,9 @@ public class ArtHeader_10 extends ArtHeader {
}
/**
* App images currently require a boot image,
* if the size is non zero then it is an app image header.
* App images currently require a boot image, if the size is non zero then it is
* an app image header.
*
* @return true if this header represents an app image
*/
public boolean isAppImage() {
@@ -196,12 +196,11 @@ public class ArtHeader_10 extends ArtHeader {
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_10.class);
try {
structure.setName(className);
structure.setName(ArtHeader_10.class.getSimpleName());
}
catch (InvalidNameException e) {
//ignore, just use original name should this fail
// ignore, just use original name should this fail
}
structure.add(DWORD, "image_reservation_size_", null);
@@ -20,10 +20,8 @@ import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.art.*;
import ghidra.file.formats.android.art.android10.ImageMethod_10;
import ghidra.file.formats.android.art.android10.ImageSections_10;
import ghidra.file.formats.android.util.DecompressionManager;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
@@ -85,7 +83,7 @@ public class ArtHeader_11 extends ArtHeader {
image_roots_ = reader.readNextInt();
pointer_size_ = reader.readNextInt();
sections = new ImageSections_10(reader, this);
sections = ArtImageSectionsFactory.getArtImageSections(reader, this);
sections.parseSections(reader);
parseImageMethods(reader);
@@ -210,9 +208,8 @@ public class ArtHeader_11 extends ArtHeader {
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_11.class);
try {
structure.setName(className);
structure.setName(ArtHeader_11.class.getSimpleName());
}
catch (InvalidNameException e) {
//ignore
@@ -0,0 +1,54 @@
/* ###
* 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.art.android12;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.file.formats.android.art.android11.ArtHeader_11;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/android-s-beta-4/runtime/image.h#418
* https://android.googlesource.com/platform/art/+/refs/heads/android-s-beta-4/runtime/image.cc#33
*
* https://android.googlesource.com/platform/art/+/refs/heads/android-s-beta-5/runtime/image.h#418
* https://android.googlesource.com/platform/art/+/refs/heads/android-s-beta-5/runtime/image.cc#33
*
* https://android.googlesource.com/platform/art/+/refs/heads/android12-release/runtime/image.h#418
* https://android.googlesource.com/platform/art/+/refs/heads/android12-release/runtime/image.cc#33
*/
public class ArtHeader_12 extends ArtHeader_11 {
public ArtHeader_12(BinaryReader reader) throws IOException {
super(reader);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
try {
structure.setName(ArtHeader_12.class.getSimpleName());
}
catch (InvalidNameException e) {
// ignore
}
return structure;
}
}
@@ -0,0 +1,110 @@
/* ###
* 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.art.android12;
import ghidra.app.util.bin.BinaryReader;
import ghidra.file.formats.android.art.ArtHeader;
import ghidra.file.formats.android.art.ArtImageSections;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/android-s-beta-4/runtime/image.h#254
*
* https://android.googlesource.com/platform/art/+/refs/heads/android-s-beta-4/runtime/image.h#254
*/
public class ImageSections_12 extends ArtImageSections {
public final static int kSectionObjects = 0;
public final static int kSectionArtFields = 1;
public final static int kSectionArtMethods = 2;
public final static int kSectionRuntimeMethods = 3;
public final static int kSectionImTables = 4;
public final static int kSectionIMTConflictTables = 5;
public final static int kSectionInternedStrings = 6;
public final static int kSectionClassTable = 8;
public final static int kSectionStringReferenceOffsets = 9;
public final static int kSectionMetadata = 10;
public final static int kSectionImageBitmap = 11;
public final static int kSectionCount = 12; // Number of elements in enum.
public ImageSections_12(BinaryReader reader, ArtHeader header) {
super(reader, header);
}
@Override
public int get_kSectionObjects() {
return kSectionObjects;
}
@Override
public int get_kSectionArtFields() {
return kSectionArtFields;
}
@Override
public int get_kSectionArtMethods() {
return kSectionArtMethods;
}
@Override
public int get_kSectionRuntimeMethods() {
return kSectionRuntimeMethods;
}
@Override
public int get_kSectionImTables() {
return kSectionImTables;
}
@Override
public int get_kSectionIMTConflictTables() {
return kSectionIMTConflictTables;
}
@Override
public int get_kSectionDexCacheArrays() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionInternedStrings() {
return kSectionInternedStrings;
}
@Override
public int get_kSectionClassTable() {
return kSectionClassTable;
}
@Override
public int get_kSectionStringReferenceOffsets() {
return kSectionStringReferenceOffsets;
}
@Override
public int get_kSectionMetadata() {
return kSectionMetadata;
}
@Override
public int get_kSectionImageBitmap() {
return kSectionImageBitmap;
}
@Override
public int get_kSectionCount() {
return kSectionCount;
}
}
@@ -18,7 +18,6 @@ package ghidra.file.formats.android.art.kitkat;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.art.ArtHeader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
@@ -152,11 +151,11 @@ public class ArtHeader_KitKat extends ArtHeader {
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_KitKat.class);
try {
structure.setName(className);
structure.setName(ArtHeader_KitKat.class.getSimpleName());
}
catch (InvalidNameException e) {
//ignore
}
structure.add(DWORD, "image_begin_", null);
@@ -18,7 +18,6 @@ package ghidra.file.formats.android.art.lollipop;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.art.ArtHeader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
@@ -128,11 +127,11 @@ public class ArtHeader_Lollipop extends ArtHeader {
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_Lollipop.class);
try {
structure.setName(className);
structure.setName(ArtHeader_Lollipop.class.getSimpleName());
}
catch (InvalidNameException e) {
//ignore
}
structure.add(DWORD, "image_begin_", null);
@@ -18,7 +18,6 @@ package ghidra.file.formats.android.art.lollipop;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.art.ArtHeader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
@@ -127,11 +126,11 @@ public class ArtHeader_LollipopMR1WFC extends ArtHeader {
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_LollipopMR1WFC.class);
try {
structure.setName(className);
structure.setName(ArtHeader_LollipopMR1WFC.class.getSimpleName());
}
catch (InvalidNameException e) {
//ignore
}
structure.add(DWORD, "image_begin_", null);
@@ -18,9 +18,7 @@ package ghidra.file.formats.android.art.marshmallow;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.art.ArtHeader;
import ghidra.file.formats.android.art.ArtImageSections;
import ghidra.file.formats.android.art.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.listing.Program;
@@ -69,7 +67,7 @@ public class ArtHeader_Marshmallow extends ArtHeader {
pointer_size_ = reader.readNextInt();
compile_pic_ = reader.readNextInt();
sections = new ImageSections_Marshmallow(reader, this);
sections = ArtImageSectionsFactory.getArtImageSections(reader, this);
sections.parseSections(reader);
for (int i = 0; i < image_methods_.length; ++i) {
@@ -144,9 +142,8 @@ public class ArtHeader_Marshmallow extends ArtHeader {
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_Marshmallow.class);
try {
structure.setName(className);
structure.setName(ArtHeader_Marshmallow.class.getSimpleName());
}
catch (InvalidNameException e) {
}
@@ -18,7 +18,6 @@ package ghidra.file.formats.android.art.nougat;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.art.*;
import ghidra.file.formats.android.util.DecompressionManager;
import ghidra.program.model.data.DataType;
@@ -62,10 +61,6 @@ public class ArtHeader_Nougat extends ArtHeader implements ArtCompression {
parse(reader);
}
protected ArtImageSections getImageSections(BinaryReader reader) {
return new ImageSections_Nougat(reader, this);
}
@Override
protected void parse(BinaryReader reader) throws IOException {
image_begin_ = reader.readNextInt();
@@ -85,7 +80,7 @@ public class ArtHeader_Nougat extends ArtHeader implements ArtCompression {
compile_pic_ = reader.readNextInt();
is_pic_ = reader.readNextInt();
sections = getImageSections(reader);
sections = ArtImageSectionsFactory.getArtImageSections(reader, this);
sections.parseSections(reader);
parseImageMethods(reader);
@@ -212,9 +207,8 @@ public class ArtHeader_Nougat extends ArtHeader implements ArtCompression {
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_Nougat.class);
try {
structure.setName(className);
structure.setName(ArtHeader_Nougat.class.getSimpleName());
}
catch (InvalidNameException e) {
//ignore, just use original name should this fail
@@ -18,8 +18,6 @@ package ghidra.file.formats.android.art.nougat;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.art.ArtImageSections;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.util.InvalidNameException;
@@ -34,10 +32,6 @@ public class ArtHeader_NougatMR2Pixel extends ArtHeader_Nougat {
super(reader);
}
protected ArtImageSections getImageSections(BinaryReader reader) {
return new ImageSections_NougatMR2Pixel(reader, this);
}
@Override
public int getArtMethodCountForVersion() {
return ImageMethod_Nougat.kImageMethodsCount.ordinal();
@@ -46,9 +40,8 @@ public class ArtHeader_NougatMR2Pixel extends ArtHeader_Nougat {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_NougatMR2Pixel.class);
try {
structure.setName(className);
structure.setName(ArtHeader_NougatMR2Pixel.class.getSimpleName());
}
catch (InvalidNameException e) {
//ignore
@@ -18,8 +18,6 @@ package ghidra.file.formats.android.art.oreo;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.art.ArtImageSections;
import ghidra.file.formats.android.art.nougat.ArtHeader_NougatMR2Pixel;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
@@ -35,10 +33,6 @@ public class ArtHeader_Oreo extends ArtHeader_NougatMR2Pixel {
super(reader);
}
protected ArtImageSections getImageSections(BinaryReader reader) {
return new ImageSections_Oreo(reader, this);
}
@Override
public int getArtMethodCountForVersion() {
return ImageMethod_Oreo.kImageMethodsCount.ordinal();
@@ -47,9 +41,8 @@ public class ArtHeader_Oreo extends ArtHeader_NougatMR2Pixel {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_Oreo.class);
try {
structure.setName(className);
structure.setName(ArtHeader_Oreo.class.getSimpleName());
}
catch (InvalidNameException e) {
//ignore
@@ -18,8 +18,6 @@ package ghidra.file.formats.android.art.oreo;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.art.ArtImageSections;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.util.InvalidNameException;
@@ -34,10 +32,6 @@ public class ArtHeader_OreoMR1 extends ArtHeader_Oreo {
super(reader);
}
protected ArtImageSections getImageSections(BinaryReader reader) {
return new ImageSections_OreoMR1(reader, this);
}
@Override
public int getArtMethodCountForVersion() {
return ImageMethod_Oreo.kImageMethodsCount.ordinal();
@@ -46,9 +40,8 @@ public class ArtHeader_OreoMR1 extends ArtHeader_Oreo {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_OreoMR1.class);
try {
structure.setName(className);
structure.setName(ArtHeader_OreoMR1.class.getSimpleName());
}
catch (InvalidNameException e) {
//ignore
@@ -18,7 +18,6 @@ package ghidra.file.formats.android.art.pie;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.art.*;
import ghidra.file.formats.android.util.DecompressionManager;
import ghidra.program.model.data.DataType;
@@ -82,7 +81,7 @@ public class ArtHeader_Pie extends ArtHeader implements ArtCompression {
compile_pic_ = reader.readNextInt();
is_pic_ = reader.readNextInt();
sections = new ImageSections_Pie(reader, this);
sections = ArtImageSectionsFactory.getArtImageSections(reader, this);
sections.parseSections(reader);
parseImageMethods(reader);
@@ -205,9 +204,8 @@ public class ArtHeader_Pie extends ArtHeader implements ArtCompression {
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_Pie.class);
try {
structure.setName(className);
structure.setName(ArtHeader_Pie.class.getSimpleName());
}
catch (InvalidNameException e) {
//ignore
@@ -94,7 +94,7 @@ public class BootImageHeaderV0 extends BootImageHeader {
@Override
public int getRamdiskOffset() {
return page_size + getKernelPageCount() * page_size;//see header comment...
return page_size + getKernelPageCount() * page_size;
}
public int getRamdiskAddress() {
@@ -67,7 +67,7 @@ public class BootImageHeaderV3 extends BootImageHeader {
*/
@Override
public int getKernelPageCount() {
return (int)(pageAlign(kernel_size) / BootImageConstants.V3_PAGE_SIZE);
return (int) (pageAlign(kernel_size) / BootImageConstants.V3_PAGE_SIZE);
}
@Override
@@ -16,21 +16,14 @@
package ghidra.file.formats.android.cdex;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import ghidra.app.util.Option;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.*;
import ghidra.file.formats.android.dex.DexHeaderFactory;
import ghidra.file.formats.android.dex.format.*;
import ghidra.file.formats.android.dex.util.DexUtil;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.util.task.TaskMonitor;
import ghidra.file.formats.android.dex.format.DexConstants;
import ghidra.file.formats.android.dex.format.DexHeader;
public class CDexLoader extends DexLoader {
@@ -82,98 +75,18 @@ public class CDexLoader extends DexLoader {
}
@Override
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program, TaskMonitor monitor, MessageLog log) throws IOException {
monitor.setMessage("CDEX Loader: creating cdex memory");
try {
Address start = program.getAddressFactory().getDefaultAddressSpace().getAddress(0x0);
long length = provider.length();
try (InputStream inputStream = provider.getInputStream(0)) {
program.getMemory()
.createInitializedBlock(".cdex", start, inputStream, length, monitor,
false);
}
BinaryReader reader = new BinaryReader(provider, true);
DexHeader header = DexHeaderFactory.getDexHeader(reader);
monitor.setMessage("CDEX Loader: creating method byte code");
createMethodLookupMemoryBlock(program, monitor);
createMethodByteCodeBlock(program, length, monitor);
for (ClassDefItem item : header.getClassDefs()) {
monitor.checkCanceled();
ClassDataItem classDataItem = item.getClassDataItem();
if (classDataItem == null) {
continue;
}
createMethods(program, header, item, classDataItem.getDirectMethods(), monitor,
log);
createMethods(program, header, item, classDataItem.getVirtualMethods(), monitor,
log);
}
}
catch (Exception e) {
log.appendException(e);
}
protected String getMemoryBlockName() {
return ".cdex";
}
private void createMethodByteCodeBlock(Program program, long length, TaskMonitor monitor)
throws Exception {
Address address = toAddr(program, DexUtil.METHOD_ADDRESS);
MemoryBlock block = program.getMemory()
.createInitializedBlock("method_bytecode", address, length, (byte) 0xff, monitor,
false);
block.setRead(true);
block.setWrite(false);
block.setExecute(true);
@Override
protected String getMonitorMessagePrimary() {
return "CDEX Loader: creating cdex memory";
}
private void createMethodLookupMemoryBlock(Program program, TaskMonitor monitor)
throws Exception {
Address address = toAddr(program, DexUtil.LOOKUP_ADDRESS);
MemoryBlock block = program.getMemory()
.createInitializedBlock("method_lookup", address, DexUtil.MAX_METHOD_LENGTH,
(byte) 0xff, monitor, false);
block.setRead(true);
block.setWrite(false);
block.setExecute(false);
}
private void createMethods(Program program, DexHeader header, ClassDefItem item,
List<EncodedMethod> methods, TaskMonitor monitor, MessageLog log) throws Exception {
for (int i = 0; i < methods.size(); ++i) {
monitor.checkCanceled();
EncodedMethod encodedMethod = methods.get(i);
CodeItem codeItem = encodedMethod.getCodeItem();
Address methodIndexAddress =
DexUtil.toLookupAddress(program, encodedMethod.getMethodIndex());
if (codeItem == null) {
//external method, ignore
}
else {
Address methodAddress =
toAddr(program, DexUtil.METHOD_ADDRESS + encodedMethod.getCodeOffset());
byte[] instructionBytes = codeItem.getInstructionBytes();
program.getMemory().setBytes(methodAddress, instructionBytes);
program.getMemory().setInt(methodIndexAddress, (int) methodAddress.getOffset());
}
}
}
private Address toAddr(Program program, long offset) {
return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
@Override
protected String getMonitorMessageSecondary() {
return "CDEX Loader: creating method byte code";
}
}
@@ -74,16 +74,34 @@ public final class DexHeaderFactory {
* @throws IOException should an error occur reading DEX bytes
*/
public final static DexHeader getDexHeader(BinaryReader reader) throws IOException {
return getDexHeader(reader, true);
}
/**
* Attempts to create DEX header using the specified Byte Provider.
* NOTE: Use a new binary reader instance, where the underlying ByteProvider is
* based to start of DEX/CDEX. Reading CDEX format requires lots of re-indexing.
* @param reader the binary reader to use to create DEX header
* @param fullParse true if parse method should be invoked
* @return the DEX header
* @throws IOException should an error occur reading DEX bytes
*/
public final static DexHeader getDexHeader(BinaryReader reader, boolean fullParse)
throws IOException {
long index = reader.getPointerIndex();
String magic = new String(reader.readByteArray(index, 4));
if (DexConstants.DEX_MAGIC_BASE.equals(magic)) {
DexHeader header = new DexHeader(reader);
header.parse(reader);
if (fullParse) {
header.parse(reader);
}
return header;
}
if (CDexConstants.MAGIC.equals(magic)) {
CDexHeader header = new CDexHeader(reader);
header.parse(reader);
if (fullParse) {
header.parse(reader);
}
return header;
}
throw new IOException("Not a recognized DEX/CDEX variant: " + magic);
@@ -24,7 +24,6 @@ import ghidra.app.plugin.core.analysis.AnalysisStateInfo;
import ghidra.file.formats.android.dex.DexHeaderFactory;
import ghidra.file.formats.android.dex.format.*;
import ghidra.file.formats.android.dex.util.DexUtil;
import ghidra.framework.main.AppInfo;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.Program;
@@ -23,13 +23,20 @@ import ghidra.app.util.importer.MessageLog;
import ghidra.file.analyzers.FileFormatAnalyzer;
import ghidra.file.formats.android.cdex.CDexConstants;
import ghidra.file.formats.android.dex.format.DexConstants;
import ghidra.framework.options.Options;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.NotEmptyException;
import ghidra.util.task.TaskMonitor;
public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
private static final String CREATE_FRAGMENTS_OPTION_NAME = "Create Fragments";
private static final boolean CREATE_FRAGMENTS_DEFAULT = true;
private boolean isCreateFragments = CREATE_FRAGMENTS_DEFAULT;
@Override
public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws Exception {
@@ -41,12 +48,21 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
return true;
}
DexHeaderFormatMarkup markup = new DexHeaderFormatMarkup(program, baseAddress);
DexHeaderFormatMarkup markup = new DexHeaderFormatMarkup(this, program, baseAddress);
markup.markup(monitor, log);
return true;
}
boolean isCreateFragments() {
return isCreateFragments;
}
@Override
public void removeEmptyFragments(Program program) throws NotEmptyException {
super.removeEmptyFragments(program);
}
@Override
public boolean canAnalyze(Program program) {
ByteProvider provider =
@@ -83,4 +99,20 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
public boolean isPrototype() {
return false;
}
@Override
public void registerOptions(Options options, Program program) {
super.registerOptions(options, program);//do super
options.registerOption(CREATE_FRAGMENTS_OPTION_NAME, CREATE_FRAGMENTS_DEFAULT, null,
"If selected, then create Program Tree fragments for each DEX element. Disable to speed up analysis.");
}
@Override
public void optionsChanged(Options options, Program program) {
super.optionsChanged(options, program);//do super
isCreateFragments = options.getBoolean(CREATE_FRAGMENTS_OPTION_NAME,
CREATE_FRAGMENTS_DEFAULT);
}
}
@@ -0,0 +1,148 @@
/* ###
* 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.dex.analyzer;
import ghidra.app.util.importer.MessageLog;
import ghidra.file.formats.android.dex.format.CodeItem;
import ghidra.file.formats.android.dex.format.DexHeader;
import ghidra.program.flatapi.FlatProgramAPI;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
/**
* A class to manage DEX fragment creation.
* Allows address ranges to coalesce before being created.
* Fragment creation is slow, so support option to skip it.
*/
class DexHeaderFragmentManager {
private Program program;
private Address baseAddress;
private FlatProgramAPI api;
private boolean isCreateFragments;
AddressSet classesAddressSet = new AddressSet();
AddressSet classStaticValuesAddressSet = new AddressSet();
AddressSet classDataAddressSet = new AddressSet();
AddressSet codeItemAddressSet = new AddressSet();
AddressSet encodedFieldsAddressSet = new AddressSet();
AddressSet encodedMethodsAddressSet = new AddressSet();
AddressSet debugInfoAddressSet = new AddressSet();
AddressSet handlersAddressSet = new AddressSet();
AddressSet tryAddressSet = new AddressSet();
AddressSet annotationsAddressSet = new AddressSet();
AddressSet classAnnotationsAddressSet = new AddressSet();
AddressSet annotationFieldsAddressSet = new AddressSet();
AddressSet annotationMethodsAddressSet = new AddressSet();
AddressSet annotationParametersAddressSet = new AddressSet();
AddressSet annotationItemAddressSet = new AddressSet();
AddressSet interfacesAddressSet = new AddressSet();
AddressSet methodsAddressSet = new AddressSet();
AddressSet fieldsAddressSet = new AddressSet();
AddressSet prototypesAddressSet = new AddressSet();
AddressSet typesAddressSet = new AddressSet();
AddressSet mapAddressSet = new AddressSet();
AddressSet stringDataAddressSet = new AddressSet();
AddressSet stringsDataSet = new AddressSet();
AddressSet[] addressSets = new AddressSet[] {
classesAddressSet, classStaticValuesAddressSet, classDataAddressSet,
codeItemAddressSet,
encodedFieldsAddressSet, encodedMethodsAddressSet,
debugInfoAddressSet, handlersAddressSet, tryAddressSet,
annotationsAddressSet, classAnnotationsAddressSet,
annotationFieldsAddressSet, annotationMethodsAddressSet,
annotationParametersAddressSet, annotationItemAddressSet,
interfacesAddressSet, methodsAddressSet, fieldsAddressSet,
prototypesAddressSet, typesAddressSet, mapAddressSet,
stringDataAddressSet, stringsDataSet,
};
private String[] addressSetNames = new String[] {
"classes", "class_static_values", "class_data", CodeItem.CODE_ITEM,
"encoded_fields", "encoded_methods", "debug_info",
"handlers", "try", "annotations", "class_annotations",
"annotation_fields", "annotation_methods", "annotation_parameters", "annotation_item",
"interfaces", "methods", "fields", "prototypes", "types", "map", "string_data", "strings",
};
DexHeaderFragmentManager(Program program, Address baseAddress, FlatProgramAPI api,
boolean isCreateFragments) {
this.program = program;
this.baseAddress = baseAddress;
this.api = api;
this.isCreateFragments = isCreateFragments;
}
void createFragments(TaskMonitor monitor, MessageLog log) throws CancelledException {
if (!isCreateFragments) {
return;
}
monitor.initialize(addressSetNames.length);
for (int i = 0; i < addressSetNames.length; i++) {
createFragment(addressSetNames[i], addressSets[i], monitor, log);
}
}
void createFragment(String fragmentName, AddressSet addressSet, TaskMonitor monitor,
MessageLog log) throws CancelledException {
if (!isCreateFragments) {
return;
}
monitor.incrementProgress(1);
monitor.checkCanceled();
monitor.setMessage("DEX: creating fragment: " + fragmentName + " ...");
try {
ProgramModule module = program.getListing().getDefaultRootModule();
ProgramFragment fragment = api.getFragment(module, fragmentName);
if (fragment == null) {
fragment = module.createFragment(fragmentName);
}
for (AddressRange range : addressSet) {
monitor.checkCanceled();
fragment.move(range.getMinAddress(), range.getMaxAddress());
}
}
catch (Exception e) {
log.appendMsg(e.getMessage());
}
}
void createInitialFragments(DexHeader header, TaskMonitor monitor) throws Exception {
if (!isCreateFragments) {
return;
}
monitor.setMessage("DEX: creating fragments");
if (header.getDataSize() > 0) {
Address start = baseAddress.add(header.getDataOffset());
api.createFragment("data", start, header.getDataSize());
}
}
void createHeaderFragment(Address headerAddress, DataType headerDataType)
throws DuplicateNameException, NotFoundException {
if (!isCreateFragments) {
return;
}
api.createFragment("header", headerAddress, headerDataType.getLength());
}
}
@@ -40,6 +40,8 @@ import ghidra.util.exception.DuplicateNameException;
*/
public class CodeItem implements StructConverter {
public static final String CODE_ITEM = "code_item";
protected short registersSize;
protected short incomingSize;
protected short outgoingSize;
@@ -200,7 +202,7 @@ public class CodeItem implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String suffix = hasPadding() ? "_p" : "";
String name = "code_item" + "_" + (instructionSize * 2) + suffix;
String name = CODE_ITEM + "_" + (instructionSize * 2) + suffix;
Structure structure = new StructureDataType(name, 0);
structure.add(WORD, "registers_size", null);
structure.add(WORD, "ins_size", null);
@@ -212,16 +214,6 @@ public class CodeItem implements StructConverter {
if (hasPadding()) {
structure.add(WORD, "padding", null);
}
// for ( int i = 0 ; i < tries.size( ) ; ++i ) {
// DataType dataType = tries.get( i ).toDataType( );
// structure.add( dataType, "tries_" + i, null );
// unique = dataType.getLength( );
// }
// if ( triesSize != 0 ) {
// DataType dataType = handlers.toDataType( );
// structure.add( dataType, "handlers", null );
// unique = dataType.getLength( );
// }
structure.setCategoryPath(new CategoryPath("/dex/code_item"));
return structure;
}
@@ -19,7 +19,8 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -71,8 +72,7 @@ public class FBPK implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(FBPK.class);
Structure struct = new StructureDataType(className, 0);
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);
@@ -92,8 +92,7 @@ public class FBPK_Partition implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(FBPK_Partition.class);
Structure struct = new StructureDataType(className, 0);
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);
@@ -19,7 +19,8 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -147,8 +148,7 @@ public class FBPT implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(FBPT.class);
Structure struct = new StructureDataType(className, 0);
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);
@@ -17,7 +17,8 @@ package ghidra.file.formats.android.fbpk;
import java.io.IOException;
import ghidra.app.util.bin.*;
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;
@@ -78,15 +79,14 @@ public class FBPT_Entry implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(FBPT_Entry.class);
Structure struct = new StructureDataType(className, 0);
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(className + "_last");
struct.setName(FBPT_Entry.class.getSimpleName() + "_last");
}
catch (InvalidNameException e) {
//ignore
@@ -1,166 +0,0 @@
/* ###
* 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.oat;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.*;
import ghidra.file.formats.android.dex.format.ClassDataItem;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
*
* https://android.googlesource.com/platform/art/+/kitkat-release/runtime/oat_file.h#144
*
* https://android.googlesource.com/platform/art/+/lollipop-release/runtime/oat_file.h#205
*
* https://android.googlesource.com/platform/art/+/marshmallow-release/runtime/oat_file.h#200
*
*/
public class OatClass implements StructConverter {
private String oatVersion;
private short status;
private short type;
private int bitmapSize;
private byte[] bitmap = new byte[0];
private List<OatMethodOffsets> methodOffsets = new ArrayList<OatMethodOffsets>();
OatClass(BinaryReader reader, ClassDataItem classDataItem, String oatVersion)
throws IOException {
this.oatVersion = oatVersion;
status = reader.readNextShort();
type = reader.readNextShort();
int methodOffsetsCount = 0;
if (type == OatClassType.kOatClassSomeCompiled.ordinal()) {
bitmapSize = reader.readNextInt();
bitmap = reader.readNextByteArray(bitmapSize);
//For every set bit, there will be a corresponding entry in method_offsets.;
for (int i = 0; i < bitmapSize; ++i) {
methodOffsetsCount += Integer.bitCount(Byte.toUnsignedInt(bitmap[i]));
}
}
else if (type == OatClassType.kOatClassAllCompiled.ordinal()) {
methodOffsetsCount =
classDataItem.getDirectMethodsSize() + classDataItem.getVirtualMethodsSize();
}
for (int i = 0; i < methodOffsetsCount; ++i) {
methodOffsets.add(OatMethodOffsetsFactory.getOatMethodOffsets(reader, oatVersion));
}
}
/**
* State of class during compilation
* @return the class status
*/
public short getStatus() {
return status;
}
/**
* Returns the class type
* @return the OAT class type
* @see OatClassType
*/
public short getType() {
return type;
}
/**
* Size of compiled methods bitmap (present only when type = 1)
* @return size of methods bitmap
*/
public int getBitmapSize() {
return bitmapSize;
}
/**
* Compiled methods bitmap (present only when type = 1)
* @return methods bitmap
*/
public byte[] getBitmap() {
return bitmap;
}
/**
* Returns true if this method index is declared native in the bitmap
* @param methodIndex the method index
* @return true if this method index is declared native in the bitmap
*/
public boolean isMethodNative(int methodIndex) {
int bytePos = methodIndex / 8;
int bitPos = methodIndex % 8;
return (((bitmap[bytePos] >> bitPos) & 0x1) == 0x1);
}
/**
* methodOffsets is a list of offset that points to the generated
* native code for each compiled method.
* @return list of method offsets
*/
public List<OatMethodOffsets> getMethodOffsets() {
return methodOffsets;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(OatClass.class);
if (bitmapSize > 0) {
className += "_" + bitmapSize;
}
if (methodOffsets.size() > 0) {
className += "_" + methodOffsets.size();
}
Structure structure = new StructureDataType(className, 0);
//structure.add( WORD, "status", null );
if (oatVersion.equals(OatConstants.VERSION_OREO_M2_RELEASE)) {
structure.add(OatClassStatus_OreoM2.toDataType(), "status", null);
}
else {
structure.add(OatClassStatus.toDataType(), "status", null);
}
structure.add(WORD, "type", null);
if (type == OatClassType.kOatClassSomeCompiled.ordinal()) {
structure.add(DWORD, "bitmapSize", null);
if (bitmapSize > 0) {
DataType bitmapDataType = new ArrayDataType(BYTE, bitmapSize, BYTE.getLength());
structure.add(bitmapDataType, "bitmap", null);
}
}
for (int i = 0; i < methodOffsets.size(); ++i) {
structure.add(methodOffsets.get(i).toDataType(), "methodOffsets_" + i, null);
}
structure.setCategoryPath(new CategoryPath("/oat"));
return structure;
}
}
@@ -1,160 +0,0 @@
/* ###
* 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.oat;
import java.lang.reflect.Field;
import ghidra.program.model.data.*;
/**
*
* See https://android.googlesource.com/platform/art/+/marshmallow-release/runtime/mirror/class.h
*
*/
public final class OatClassStatus {
/**
* Class that's temporarily used till class linking time
* has its (vtable) size figured out and has been cloned to one with the
* right size which will be the one used later. The old one is retired and
* will be gc'ed once all refs to the class point to the newly
* cloned version.
*/
public final static short kStatusRetired = -3;
/**
* Class is erroneous. We need to distinguish between classes that
* have been resolved and classes that have not. This is important
* because the const-class instruction needs to return a previously
* resolved class even if its subsequent initialization failed.
* We also need this to decide whether to wrap a previous initialization
* failure in ClassDefNotFound error or not.
*/
public final static short kStatusErrorResolved = -2;
/**
* Class is erroneous. We need to distinguish between classes that
* have been resolved and classes that have not. This is important
* because the const-class instruction needs to return a previously
* resolved class even if its subsequent initialization failed.
* We also need this to decide whether to wrap a previous initialization
* failure in ClassDefNotFound error or not.
*/
public final static short kStatusErrorUnresolved = -1;
/**
* If a Class cannot be found in the class table by FindClass,
* it allocates an new one with AllocClass in the kStatusNotReady
* and calls LoadClass. Note if it does find a class, it may
* not be kStatusResolved and it will try to push it forward toward kStatusResolved.
*/
public final static short kStatusNotReady = 0;
/**
* LoadClass populates with Class with information from the DexFile,
* moving the status to kStatusIdx, indicating that the Class value
* in super_class_ has not been populated. The new Class can
* then be inserted into the classes table.
*/
public final static short kStatusIdx = 1;
/**
* After taking a lock on Class, the ClassLinker will attempt
* to move a kStatusIdx class forward to kStatusLoaded by using
* ResolveClass to initialize the super_class_ and ensuring the
* interfaces are resolved.
*/
public final static short kStatusLoaded = 2;
/**
* Class is just cloned with the right size from temporary
* class that's acting as a placeholder for linking. The old
* class will be retired. New class is set to this status first
* before moving on to being resolved.
*/
public final static short kStatusResolving = 3;
/**
* Still holding the lock on Class, the ClassLinker
* shows linking is complete and fields of the Class populated by making
* it kStatusResolved. Java allows circularities of the form where a super
* class has a field that is of the type of the sub class. We need to be able
* to fully resolve super classes while resolving types for fields.
*/
public final static short kStatusResolved = 4;
/** In the process of being verified. */
public final static short kStatusVerifying = 5;
/**
* The verifier sets a class to this state if it encounters a soft
* failure at compile time. This often happens when there are unresolved
* classes in other dex files, and this status marks a class as
* needing to be verified again at runtime.
*/
public final static short kStatusRetryVerificationAtRuntime = 6;
/** Retrying verification at runtime. */
public final static short kStatusVerifyingAtRuntime = 7;
/** Logically part of linking; done pre-init. */
public final static short kStatusVerified = 8;
/** Class init in progress. */
public final static short kStatusInitializing = 9;
/** Ready to go. */
public final static short kStatusInitialized = 10;
public final static short kStatusMax = 11;
/**
* Returns the field name for the given value.
* If not found, simply returns a hex string of the value.
* @param value the field value
* @return the field name
*/
public static String toString(short value) {
for (Field field : OatClassStatus.class.getDeclaredFields()) {
try {
Object obj = field.get(null);
if (obj != null && obj.equals(value)) {
return field.getName();
}
}
catch (Exception e) {
//ignore...
}
}
return OatClassStatus.class.getSimpleName() + ":0x" + Integer.toHexString(value);
}
public static DataType toDataType() {
EnumDataType enumDataType = new EnumDataType(OatClassStatus.class.getSimpleName(), 2);
for (Field field : OatClassStatus.class.getDeclaredFields()) {
try {
Object obj = field.get(null);
enumDataType.add(field.getName(), (short) obj);
}
catch (Exception e) {
//ignore...
}
}
enumDataType.setCategoryPath(new CategoryPath("/oat"));
return enumDataType;
}
}
@@ -1,164 +0,0 @@
/* ###
* 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.oat;
import java.lang.reflect.Field;
import ghidra.program.model.data.*;
import ghidra.util.UniversalIdGenerator;
/**
*
* See https://android.googlesource.com/platform/art/+/marshmallow-release/runtime/mirror/class.h
*
*/
public final class OatClassStatus_OreoM2 {
/**
* Class that's temporarily used till class linking time
* has its (vtable) size figured out and has been cloned to one with the
* right size which will be the one used later. The old one is retired and
* will be gc'ed once all refs to the class point to the newly
* cloned version.
*/
public final static short kStatusRetired = -3;
/**
* Class is erroneous. We need to distinguish between classes that
* have been resolved and classes that have not. This is important
* because the const-class instruction needs to return a previously
* resolved class even if its subsequent initialization failed.
* We also need this to decide whether to wrap a previous initialization
* failure in ClassDefNotFound error or not.
*/
public final static short kStatusErrorResolved = -2;
/**
* Class is erroneous. We need to distinguish between classes that
* have been resolved and classes that have not. This is important
* because the const-class instruction needs to return a previously
* resolved class even if its subsequent initialization failed.
* We also need this to decide whether to wrap a previous initialization
* failure in ClassDefNotFound error or not.
*/
public final static short kStatusErrorUnresolved = -1;
/**
* If a Class cannot be found in the class table by FindClass,
* it allocates an new one with AllocClass in the kStatusNotReady
* and calls LoadClass. Note if it does find a class, it may
* not be kStatusResolved and it will try to push it forward toward kStatusResolved.
*/
public final static short kStatusNotReady = 0;
/**
* LoadClass populates with Class with information from the DexFile,
* moving the status to kStatusIdx, indicating that the Class value
* in super_class_ has not been populated. The new Class can
* then be inserted shorto the classes table.
*/
public final static short kStatusIdx = 1;
/**
* After taking a lock on Class, the ClassLinker will attempt
* to move a kStatusIdx class forward to kStatusLoaded by using
* ResolveClass to initialize the super_class_ and ensuring the
* shorterfaces are resolved.
*/
public final static short kStatusLoaded = 2;
/**
* Class is just cloned with the right size from temporary
* class that's acting as a placeholder for linking. The old
* class will be retired. New class is set to this status first
* before moving on to being resolved.
*/
public final static short kStatusResolving = 3;
/**
* Still holding the lock on Class, the ClassLinker
* shows linking is complete and fields of the Class populated by making
* it kStatusResolved. Java allows circularities of the form where a super
* class has a field that is of the type of the sub class. We need to be able
* to fully resolve super classes while resolving types for fields.
*/
public final static short kStatusResolved = 4;
/** In the process of being verified. */
public final static short kStatusVerifying = 5;
/**
* The verifier sets a class to this state if it encounters a soft
* failure at compile time. This often happens when there are unresolved
* classes in other dex files, and this status marks a class as
* needing to be verified again at runtime.
*/
public final static short kStatusRetryVerificationAtRuntime = 6;
/** Retrying verification at runtime. */
public final static short kStatusVerifyingAtRuntime = 7;
/** Logically part of linking; done pre-init. */
public final static short kStatusVerified = 8;
/** Superclass validation part of init done. */
public final static short kStatusSuperclassValidated = 9;
/** Class init in progress. */
public final static short kStatusInitializing = 10;
/** Ready to go. */
public final static short kStatusInitialized = 11;
public final static short kStatusMax = 12;
/**
* Returns the field name for the given value.
* If not found, simply returns a hex string of the value.
*/
public static String toString(short value) {
for (Field field : OatClassStatus_OreoM2.class.getDeclaredFields()) {
try {
Object obj = field.get(null);
if (obj != null && obj.equals(value)) {
return field.getName();
}
}
catch (Exception e) {
//ignore...
}
}
return OatClassStatus_OreoM2.class.getSimpleName() + ":0x" + Integer.toHexString(value);
}
public static DataType toDataType() {
EnumDataType enumDataType =
new EnumDataType(OatClassStatus_OreoM2.class.getSimpleName(), 2);
for (Field field : OatClassStatus_OreoM2.class.getDeclaredFields()) {
try {
Object obj = field.get(null);
enumDataType.add(field.getName(), (short) obj);
}
catch (Exception e) {
e.printStackTrace();
//ignore...
}
}
enumDataType.setCategoryPath(new CategoryPath("/oat"));
return enumDataType;
}
}
@@ -77,6 +77,7 @@ public final class OatConstants {
public final static String version_q_preview_1 = "166";
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 int VERSION_LENGTH = 3;//3 bytes in length
@@ -101,6 +102,7 @@ public final class OatConstants {
VERSION_PIE_RELEASE,
VERSION_10_RELEASE,
VERSION_11_RELEASE,
VERSION_12_RELEASE,
};
/** Keys from the OAT header "key/value" store. */
@@ -18,6 +18,8 @@ package ghidra.file.formats.android.oat;
import ghidra.app.util.bin.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.file.analyzers.FileFormatAnalyzer;
import ghidra.file.formats.android.oat.quickmethod.OatQuickMethodHeader;
import ghidra.file.formats.android.oat.quickmethod.OatQuickMethodHeaderFactory;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.DataType;
@@ -21,7 +21,9 @@ import java.util.*;
import generic.continues.RethrowContinuesFactory;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.elf.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.file.formats.android.dex.format.DexHeader;
import ghidra.file.formats.android.oat.oatdexfile.OatDexFile;
import ghidra.formats.gfilesystem.*;
import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
import ghidra.formats.gfilesystem.factory.GFileSystemBaseFactory;
@@ -100,7 +102,7 @@ public class OatFileSystem extends GFileSystemBase {
ElfSectionHeader roDataSection = elf.getSection(ElfSectionHeaderConstants.dot_rodata);
if (roDataSection == null) {
//TODO should we check?
throw new IOException("rodata section does not exist.");
}
baseOffset = roDataSection.getOffset();
@@ -109,8 +111,7 @@ public class OatFileSystem extends GFileSystemBase {
new ByteProviderWrapper(provider, baseOffset, roDataSection.getSize());
BinaryReader reader = new BinaryReader(wrapper, elf.isLittleEndian());
OatHeader oatHeader = OatHeaderFactory.newOatHeader(reader);
//oatHeader.parse( reader, null );
OatHeaderFactory.parseOatHeader(oatHeader, reader, monitor);
OatHeaderFactory.parseOatHeader(oatHeader, null, reader, monitor, new MessageLog());
monitor.incrementProgress(1);
dexFileList = oatHeader.getOatDexFileList();
@@ -184,7 +185,7 @@ public class OatFileSystem extends GFileSystemBase {
throw new IOException("Invalid / unknown file: " + file);
}
OatDexFile oatDexFileHeader = dexFileList.get(index);
return new ByteProviderWrapper(provider, oatDexFileHeader.getDexFileOffset(),
return new ByteProviderWrapper(provider, baseOffset + oatDexFileHeader.getDexFileOffset(),
oatDexFileHeader.getDexHeader().getFileSize(), file.getFSRL());
}
@@ -16,10 +16,13 @@
package ghidra.file.formats.android.oat;
import java.io.IOException;
import java.util.List;
import java.util.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.file.formats.android.oat.bundle.OatBundle;
import ghidra.file.formats.android.oat.oatdexfile.OatDexFile;
import ghidra.file.formats.android.oat.oatdexfile.OatDexFileFactory;
/**
* Base OatHeader implementations
@@ -34,6 +37,16 @@ public abstract class OatHeader implements StructConverter {
protected String magic;
protected String version;
protected List<String> orderedKeyList = new ArrayList<String>();//ordered as defined
protected Map<String, String> key_value_store_ = new HashMap<String, String>();
protected List<OatDexFile> oatDexFileList = new ArrayList<OatDexFile>();
/**
* Base constructor for the OAT headers.
* @param reader the binary reader with the file bytes.
* @throws IOException if an IO exception occurs.
*/
protected OatHeader(BinaryReader reader) throws IOException {
magic = new String(reader.readNextByteArray(OatConstants.MAGIC.length()));
version = reader.readNextAsciiString(4);
@@ -43,12 +56,34 @@ public abstract class OatHeader implements StructConverter {
* Parses the OAT header beyond the MAGIC and VERSION fields.
* The "additionalData" is used to future proof the parsers.
* For example, its needed for handling vdex files.
* @param reader the binary reader with the file bytes.
* @param bundle the fake OAT bundle containing the DEX, VDEX, etc.
* @throws IOException if an IO exception occurs.
* @throws UnsupportedOatVersionException if the OAT version is not supported.
*/
abstract public void parse(BinaryReader reader, Object additionalData)
throws IOException, UnsupportedOatVersionException;
public void parse(BinaryReader reader, OatBundle bundle)
throws IOException, UnsupportedOatVersionException {
int count = 0;
while (count < getKeyValueStoreSize()) {
String key = reader.readNextAsciiString();
String value = reader.readNextAsciiString();
count += key.length() + 1;
count += value.length() + 1;
orderedKeyList.add(key);
key_value_store_.put(key, value);
}
reader.setPointerIndex(getOatDexFilesOffset(reader));
for (int i = 0; i < getDexFileCount(); ++i) {
oatDexFileList.add(
OatDexFileFactory.getOatDexFile(reader, getVersion(), bundle));
}
}
/**
* Returns the MAGIC string, i.e. "oat\n".
* @return the MAGIC string, i.e. "oat\n".
*/
public String getMagic() {
return magic;
@@ -56,39 +91,55 @@ public abstract class OatHeader implements StructConverter {
/**
* Returns the VERSION string, e.g. "001", "009", etc.
* @return the VERSION string, e.g. "001", "009", etc.
*/
public String getVersion() {
return version;
}
/**
* Returns the binary offset to the DEX files.
* @param reader the binary reader with the file bytes.
* @return the binary offset to the DEX files.
*/
abstract public int getOatDexFilesOffset(BinaryReader reader);
/**
* Returns the number of DEX files embedded inside this OAT file.
* @return the number of DEX files embedded inside this OAT file.
*/
abstract public int getDexFileCount();
/**
* Returns the size (in bytes) of the Key/Value store contained inside this OAT file.
* @return the size (in bytes) of the Key/Value store contained inside this OAT file.
*/
abstract public int getKeyValueStoreSize();
/**
* Returns a list of the OatDexFileHeader, a structure defining the embedded DEX files.
* @return a list of the OatDexFileHeader, a structure defining the embedded DEX files.
*/
abstract public List<OatDexFile> getOatDexFileList();
/**
* Returns the OAT instruction set (ARM, X86, etc).
* @return the OAT instruction set (ARM, X86, etc).
*/
abstract public OatInstructionSet getInstructionSet();
/**
* Returns the offset to the executable code, relative to the "oatdata" symbol.
* Returns the offset to the executable code.
* Relative to the "oatdata" symbol.
* The executable code can also be located using the "oatexec" symbol.
* @return the offset to the executable code.
*/
abstract public int getExecutableOffset();
/**
* Returns the OAT checksum value.
* @return the OAT checksum value.
*/
abstract public int getChecksum();
}
@@ -22,6 +22,7 @@ import ghidra.app.util.bin.format.elf.ElfSectionHeaderConstants;
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.oatdexfile.OatDexFile;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.*;
@@ -57,7 +58,7 @@ public class OatHeaderAnalyzer extends FileFormatAnalyzer {
@Override
public boolean isPrototype() {
return true;
return false;
}
@Override
@@ -19,8 +19,8 @@ import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.importer.MessageLog;
import ghidra.file.formats.android.vdex.VdexFactory;
import ghidra.file.formats.android.vdex.VdexHeader;
import ghidra.file.formats.android.oat.bundle.OatBundle;
import ghidra.file.formats.android.oat.bundle.OatBundleFactory;
import ghidra.program.model.listing.Program;
import ghidra.util.task.TaskMonitor;
@@ -62,35 +62,21 @@ public final class OatHeaderFactory {
return new OatHeader_10(reader);
case OatConstants.VERSION_11_RELEASE:
return new OatHeader_11(reader);
case OatConstants.VERSION_12_RELEASE:
return new OatHeader_12(reader);
}
}
}
throw new UnsupportedOatVersionException(magic, version);
}
public final static void parseOatHeader(OatHeader oatHeader, BinaryReader reader,
TaskMonitor monitor) throws UnsupportedOatVersionException, IOException {
parseOatHeader(oatHeader, null, reader, monitor, new MessageLog());
}
public final static void parseOatHeader(OatHeader oatHeader, Program oatProgram,
BinaryReader reader, TaskMonitor monitor, MessageLog log)
throws UnsupportedOatVersionException, IOException {
if (oatHeader.getVersion().equals(OatConstants.VERSION_OREO_RELEASE) ||
oatHeader.getVersion().equals(OatConstants.VERSION_OREO_M2_RELEASE) ||
oatHeader.getVersion().equals(OatConstants.VERSION_PIE_RELEASE) ||
oatHeader.getVersion().equals(OatConstants.VERSION_10_RELEASE) ||
oatHeader.getVersion().equals(OatConstants.VERSION_11_RELEASE)) {
//TODO move instantiation into parse method (will need program?)
VdexHeader vdexHeader = VdexFactory.loadVdexHeader(oatProgram, monitor, log);
oatHeader.parse(reader, vdexHeader);
}
else {
oatHeader.parse(reader, null);
}
OatBundle bundle = OatBundleFactory.getOatBundle(oatProgram, oatHeader, monitor, log);
oatHeader.parse(reader, bundle);
bundle.close();
}
}
@@ -19,7 +19,7 @@ import java.io.IOException;
import java.util.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.oat.oatdexfile.OatDexFile;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -36,10 +36,6 @@ public class OatHeader_10 extends OatHeader {
protected int quick_resolution_trampoline_offset_;
protected int quick_to_interpreter_bridge_offset_;
protected int key_value_store_size_;
protected Map<String, String> key_value_store_ = new HashMap<String, String>();
protected List<String> orderedKeyList = new ArrayList<String>();//ordered as defined
protected List<OatDexFile> oatDexFileList = new ArrayList<OatDexFile>();
OatHeader_10(BinaryReader reader) throws IOException {
super(reader);
@@ -59,23 +55,8 @@ public class OatHeader_10 extends OatHeader {
}
@Override
public void parse(BinaryReader reader, Object additionalData)
throws IOException, UnsupportedOatVersionException {
int count = 0;
while (count < key_value_store_size_) {
String key = reader.readNextAsciiString();
String value = reader.readNextAsciiString();
count += key.length() + 1;
count += value.length() + 1;
orderedKeyList.add(key);
key_value_store_.put(key, value);
}
reader.setPointerIndex(oat_dex_files_offset_);
for (int i = 0; i < dex_file_count_; ++i) {
oatDexFileList
.add(OatDexFileFactory.getOatDexFile(reader, getVersion(), additionalData));
}
public int getOatDexFilesOffset(BinaryReader reader) {
return oat_dex_files_offset_;
}
@Override
@@ -110,8 +91,7 @@ public class OatHeader_10 extends OatHeader {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(OatHeader_10.class);
Structure structure = new StructureDataType(className, 0);
Structure structure = new StructureDataType(OatHeader_10.class.getSimpleName(), 0);
structure.add(STRING, 4, "magic_", null);
structure.add(STRING, 4, "version_", null);
structure.add(DWORD, "oat_checksum_", null);
@@ -134,6 +114,7 @@ public class OatHeader_10 extends OatHeader {
structure.add(STRING, value.length() + 1, "key_value_store_[" + i + "].value", null);
}
structure.setCategoryPath(new CategoryPath("/oat"));
return structure;
}
@@ -19,7 +19,7 @@ import java.io.IOException;
import java.util.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.oat.oatdexfile.OatDexFile;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -37,10 +37,6 @@ public class OatHeader_11 extends OatHeader {
protected int quick_resolution_trampoline_offset_;
protected int quick_to_interpreter_bridge_offset_;
protected int key_value_store_size_;
protected Map<String, String> key_value_store_ = new HashMap<String, String>();
protected List<String> orderedKeyList = new ArrayList<String>();//ordered as defined
protected List<OatDexFile> oatDexFileList = new ArrayList<OatDexFile>();
OatHeader_11(BinaryReader reader) throws IOException {
super(reader);
@@ -61,23 +57,8 @@ public class OatHeader_11 extends OatHeader {
}
@Override
public void parse(BinaryReader reader, Object additionalData)
throws IOException, UnsupportedOatVersionException {
int count = 0;
while (count < key_value_store_size_) {
String key = reader.readNextAsciiString();
String value = reader.readNextAsciiString();
count += key.length() + 1;
count += value.length() + 1;
orderedKeyList.add(key);
key_value_store_.put(key, value);
}
reader.setPointerIndex(oat_dex_files_offset_);
for (int i = 0; i < dex_file_count_; ++i) {
oatDexFileList
.add(OatDexFileFactory.getOatDexFile(reader, getVersion(), additionalData));
}
public int getOatDexFilesOffset(BinaryReader reader) {
return oat_dex_files_offset_;
}
@Override
@@ -112,8 +93,7 @@ public class OatHeader_11 extends OatHeader {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(OatHeader_11.class);
Structure structure = new StructureDataType(className, 0);
Structure structure = new StructureDataType(OatHeader_11.class.getSimpleName(), 0);
structure.add(STRING, 4, "magic_", null);
structure.add(STRING, 4, "version_", null);
structure.add(DWORD, "oat_checksum_", null);
@@ -137,6 +117,7 @@ public class OatHeader_11 extends OatHeader {
structure.add(STRING, value.length() + 1, "key_value_store_[" + i + "].value", null);
}
structure.setCategoryPath(new CategoryPath("/oat"));
return structure;
}

Some files were not shown because too many files have changed in this diff Show More