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 @Override
public String getName( ) { public String getName() {
return "Dalvik Executable (DEX)"; return "Dalvik Executable (DEX)";
} }
@@ -70,83 +70,106 @@ public class DexLoader extends AbstractLibrarySupportLoader {
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options, public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program, TaskMonitor monitor, MessageLog log) throws IOException { Program program, TaskMonitor monitor, MessageLog log) throws IOException {
monitor.setMessage( "DEX Loader: creating dex memory" ); monitor.setMessage(getMonitorMessagePrimary());
try { try {
Address start = program.getAddressFactory().getDefaultAddressSpace().getAddress( 0x0 ); Address start = program.getAddressFactory().getDefaultAddressSpace().getAddress(0x0);
long length = provider.length(); long length = provider.length();
try (InputStream inputStream = provider.getInputStream(0)) { try (InputStream inputStream = provider.getInputStream(0)) {
program.getMemory().createInitializedBlock(".dex", start, inputStream, length, program.getMemory()
monitor, false); .createInitializedBlock(getMemoryBlockName(), start, inputStream, length,
monitor, false);
} }
BinaryReader reader = new BinaryReader( provider, true ); BinaryReader reader = new BinaryReader(provider, true);
DexHeader header = DexHeaderFactory.getDexHeader( reader ); DexHeader header = DexHeaderFactory.getDexHeader(reader);
monitor.setMessage( "DEX Loader: creating method byte code" ); monitor.setMessage(getMonitorMessageSecondary());
createMethodLookupMemoryBlock( program, monitor ); createMethodLookupMemoryBlock(program, monitor);
createMethodByteCodeBlock( program, length, monitor); createMethodByteCodeBlock(program, length, monitor);
for ( ClassDefItem item : header.getClassDefs( ) ) { for (ClassDefItem item : header.getClassDefs()) {
monitor.checkCanceled( ); monitor.checkCanceled();
ClassDataItem classDataItem = item.getClassDataItem( ); ClassDataItem classDataItem = item.getClassDataItem();
if ( classDataItem == null ) { if (classDataItem == null) {
continue; continue;
} }
createMethods( program, header, item, classDataItem.getDirectMethods( ), monitor, log ); createMethods(program, header, item, classDataItem.getDirectMethods(), monitor,
createMethods( program, header, item, classDataItem.getVirtualMethods( ), monitor, log ); log);
createMethods(program, header, item, classDataItem.getVirtualMethods(), monitor,
log);
} }
} }
catch ( Exception e) { catch (Exception e) {
log.appendException( e ); log.appendException(e);
} }
} }
private void createMethodByteCodeBlock(Program program, long length, TaskMonitor monitor) throws Exception { protected void createMethodByteCodeBlock(Program program, long length, TaskMonitor monitor)
Address address = toAddr( program, DexUtil.METHOD_ADDRESS ); throws Exception {
MemoryBlock block = program.getMemory( ).createInitializedBlock( "method_bytecode", address, length, (byte) 0xff, monitor, false ); Address address = toAddr(program, DexUtil.METHOD_ADDRESS);
block.setRead( true ); MemoryBlock block = program.getMemory()
block.setWrite( false ); .createInitializedBlock("method_bytecode", address, length, (byte) 0xff, monitor,
block.setExecute( true ); false);
block.setRead(true);
block.setWrite(false);
block.setExecute(true);
} }
private void createMethodLookupMemoryBlock(Program program, TaskMonitor monitor) throws Exception { protected void createMethodLookupMemoryBlock(Program program, TaskMonitor monitor)
Address address = toAddr( program, DexUtil.LOOKUP_ADDRESS ); throws Exception {
MemoryBlock block = program.getMemory( ).createInitializedBlock( "method_lookup", address, DexUtil.MAX_METHOD_LENGTH, (byte) 0xff, monitor, false ); Address address = toAddr(program, DexUtil.LOOKUP_ADDRESS);
block.setRead( true ); MemoryBlock block = program.getMemory()
block.setWrite( false ); .createInitializedBlock("method_lookup", address, DexUtil.MAX_METHOD_LENGTH,
block.setExecute( false ); (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 { protected void createMethods(Program program, DexHeader header, ClassDefItem item,
for ( int i = 0 ; i < methods.size( ) ; ++i ) { List<EncodedMethod> methods, TaskMonitor monitor, MessageLog log) throws Exception {
monitor.checkCanceled( ); 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 if (codeItem == null) {
//TODO //external method
} }
else { else {
Address methodAddress = toAddr( program, DexUtil.METHOD_ADDRESS + encodedMethod.getCodeOffset( ) ); Address methodAddress =
toAddr(program, DexUtil.METHOD_ADDRESS + encodedMethod.getCodeOffset());
byte [] instructionBytes = codeItem.getInstructionBytes( ); byte[] instructionBytes = codeItem.getInstructionBytes();
program.getMemory( ).setBytes( methodAddress, instructionBytes ); 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 ) { protected Address toAddr(Program program, long offset) {
return program.getAddressFactory( ).getDefaultAddressSpace( ).getAddress( 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 @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 { MessageLog log) throws CancelledException {
try { try {
return analyze(program, set, monitor, log); return analyze(program, set, monitor, log);
@@ -58,28 +58,28 @@ public abstract class FileFormatAnalyzer implements Analyzer {
} }
@Override @Override
final public void analysisEnded(Program program) { public void analysisEnded(Program program) {
// do nothing // do nothing
} }
@Override @Override
final public void registerOptions(Options options, Program program) { public void registerOptions(Options options, Program program) {
// do nothing // do nothing
} }
@Override @Override
final public void optionsChanged(Options options, Program program) { public void optionsChanged(Options options, Program program) {
// do nothing // do nothing
} }
@Override @Override
final public boolean removed(Program program, AddressSetView set, TaskMonitor monitor, public boolean removed(Program program, AddressSetView set, TaskMonitor monitor,
MessageLog log) throws CancelledException { MessageLog log) throws CancelledException {
return false; return false;
} }
@Override @Override
final public boolean supportsOneTimeAnalysis() { public boolean supportsOneTimeAnalysis() {
return false; return false;
} }
@@ -121,15 +121,6 @@ public abstract class FileFormatAnalyzer implements Analyzer {
if (isAscii && bytes.length > 1) { if (isAscii && bytes.length > 1) {
changeFormatToString(component); 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()); address = address.add(data.getLength());
} }
@@ -54,7 +54,7 @@ public class ArtAnalyzer extends FileFormatAnalyzer {
@Override @Override
public boolean isPrototype() { public boolean isPrototype() {
return true; return false;
} }
@Override @Override
@@ -75,7 +75,7 @@ public class ArtAnalyzer extends FileFormatAnalyzer {
DataType headerDataType = header.toDataType(); DataType headerDataType = header.toDataType();
//only set "image base" when ART header not defined at "image begin" //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()); Address imageBase = toAddr(program, header.getImageBegin());
if (BinaryLoader.BINARY_NAME.equals(program.getExecutableFormat())) { if (BinaryLoader.BINARY_NAME.equals(program.getExecutableFormat())) {
@@ -17,7 +17,8 @@ package ghidra.file.formats.android.art;
import java.io.IOException; 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.program.model.data.*;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@@ -85,8 +86,7 @@ public class ArtBlock implements StructConverter, ArtCompression {
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
String name = StructConverterUtil.parseName(ArtBlock.class); Structure structure = new StructureDataType(ArtBlock.class.getSimpleName(), 0);
Structure structure = new StructureDataType(name, 0);
structure.setCategoryPath(new CategoryPath("/art")); structure.setCategoryPath(new CategoryPath("/art"));
structure.add(DWORD, "storage_mode_", storage_mode_.name()); structure.add(DWORD, "storage_mode_", storage_mode_.name());
structure.add(DWORD, "data_offset_", null); structure.add(DWORD, "data_offset_", null);
@@ -20,7 +20,7 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock; 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 { 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_PIE_RELEASE = "056";
public final static String VERSION_10_RELEASE = "074";//Q public final static String VERSION_10_RELEASE = "074";//Q
public final static String VERSION_11_RELEASE = "085";//R public final static String VERSION_11_RELEASE = "085";//R
public final static String VERSION_12_RELEASE = "099";//S
// "005",// kitkat-release // "005",// kitkat-release
// "009",// lollipop-release // "009",// lollipop-release
@@ -75,6 +76,7 @@ public final class ArtConstants {
VERSION_PIE_RELEASE, VERSION_PIE_RELEASE,
VERSION_10_RELEASE, VERSION_10_RELEASE,
VERSION_11_RELEASE, VERSION_11_RELEASE,
VERSION_12_RELEASE,
//@formatter:on //@formatter:on
}; };
@@ -20,6 +20,7 @@ import java.io.IOException;
import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.BinaryReader;
import ghidra.file.formats.android.art.android10.ArtHeader_10; import ghidra.file.formats.android.art.android10.ArtHeader_10;
import ghidra.file.formats.android.art.android11.ArtHeader_11; 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.kitkat.ArtHeader_KitKat;
import ghidra.file.formats.android.art.lollipop.ArtHeader_Lollipop; import ghidra.file.formats.android.art.lollipop.ArtHeader_Lollipop;
import ghidra.file.formats.android.art.lollipop.ArtHeader_LollipopMR1WFC; import ghidra.file.formats.android.art.lollipop.ArtHeader_LollipopMR1WFC;
@@ -45,7 +46,7 @@ public final class ArtFactory {
String version = reader.readAsciiString(4, 4); String version = reader.readAsciiString(4, 4);
if (magic.equals(ArtConstants.MAGIC)) { if (magic.equals(ArtConstants.MAGIC)) {
if (ArtConstants.isSupportedVersion(version)) { if (ArtConstants.isSupportedVersion(version)) {
switch (version ) { switch (version) {
case ArtConstants.VERSION_KITKAT_RELEASE: case ArtConstants.VERSION_KITKAT_RELEASE:
return new ArtHeader_KitKat(reader); return new ArtHeader_KitKat(reader);
case ArtConstants.VERSION_LOLLIPOP_RELEASE: case ArtConstants.VERSION_LOLLIPOP_RELEASE:
@@ -70,6 +71,8 @@ public final class ArtFactory {
return new ArtHeader_10(reader); return new ArtHeader_10(reader);
case ArtConstants.VERSION_11_RELEASE: case ArtConstants.VERSION_11_RELEASE:
return new ArtHeader_11(reader); 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 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.program.model.data.*;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@@ -58,8 +59,7 @@ public class ArtField implements StructConverter {
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
String name = StructConverterUtil.parseName(ArtField.class); Structure structure = new StructureDataType(ArtField.class.getSimpleName(), 0);
Structure structure = new StructureDataType(name, 0);
structure.setCategoryPath(new CategoryPath("/art")); structure.setCategoryPath(new CategoryPath("/art"));
structure.add(new Pointer32DataType(), "declaring_class_", null); structure.add(new Pointer32DataType(), "declaring_class_", null);
structure.add(DWORD, "access_flags_", null); structure.add(DWORD, "access_flags_", null);
@@ -19,7 +19,8 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.data.*;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@@ -53,8 +54,8 @@ public class ArtFieldGroup implements StructConverter {
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
String name = StructConverterUtil.parseName(ArtFieldGroup.class); Structure structure = new StructureDataType(
Structure structure = new StructureDataType(name + "_" + fieldCount, 0); ArtFieldGroup.class.getSimpleName() + "_" + fieldCount, 0);
structure.setCategoryPath(new CategoryPath("/art")); structure.setCategoryPath(new CategoryPath("/art"));
structure.add(DWORD, "fieldCount", null); structure.add(DWORD, "fieldCount", null);
for (int i = 0; i < fieldCount; ++i) { for (int i = 0; i < fieldCount; ++i) {
@@ -19,7 +19,8 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.data.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@@ -137,8 +138,7 @@ public abstract class ArtHeader implements StructConverter {
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(ArtHeader.class); Structure structure = new StructureDataType(ArtHeader.class.getSimpleName(), 0);
Structure structure = new StructureDataType(className, 0);
structure.add(STRING, 4, "magic_", null); structure.add(STRING, 4, "magic_", null);
structure.add(STRING, 4, "version_", null); structure.add(STRING, 4, "version_", null);
structure.setCategoryPath(new CategoryPath("/art")); 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 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.program.model.data.*;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@@ -259,8 +260,7 @@ public class ArtMethod implements StructConverter {
DataType ptr32 = new Pointer32DataType(); DataType ptr32 = new Pointer32DataType();
DataType ptr64 = new Pointer64DataType(); DataType ptr64 = new Pointer64DataType();
String name = StructConverterUtil.parseName(ArtMethod.class); Structure struct = new StructureDataType(ArtMethod.class.getSimpleName(), 0);
Structure struct = new StructureDataType(name, 0);
struct.setCategoryPath(new CategoryPath("/art")); struct.setCategoryPath(new CategoryPath("/art"));
if (ArtConstants.VERSION_MARSHMALLOW_RELEASE.equals(artVersion)) { if (ArtConstants.VERSION_MARSHMALLOW_RELEASE.equals(artVersion)) {
@@ -19,7 +19,8 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.data.*;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@@ -65,8 +66,8 @@ public class ArtMethodGroup implements StructConverter {
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
String name = StructConverterUtil.parseName(ArtMethodGroup.class); Structure structure = new StructureDataType(
Structure structure = new StructureDataType(name + "_" + methodCount, 0); ArtMethodGroup.class.getSimpleName() + "_" + methodCount, 0);
structure.setCategoryPath(new CategoryPath("/art")); structure.setCategoryPath(new CategoryPath("/art"));
if (pointerSize == 8) { if (pointerSize == 8) {
structure.add(QWORD, "methodCount", null); structure.add(QWORD, "methodCount", null);
@@ -15,9 +15,9 @@
*/ */
package ghidra.file.formats.android.art; package ghidra.file.formats.android.art;
final class UnsupportedArtVersionException extends Exception { public final class UnsupportedArtVersionException extends Exception {
UnsupportedArtVersionException(String magic, String version) { 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 java.util.List;
import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.art.*; import ghidra.file.formats.android.art.*;
import ghidra.file.formats.android.util.DecompressionManager; import ghidra.file.formats.android.util.DecompressionManager;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
@@ -79,7 +78,7 @@ public class ArtHeader_10 extends ArtHeader {
image_roots_ = reader.readNextInt(); image_roots_ = reader.readNextInt();
pointer_size_ = reader.readNextInt(); pointer_size_ = reader.readNextInt();
sections = new ImageSections_10(reader, this); sections = ArtImageSectionsFactory.getArtImageSections(reader, this);
sections.parseSections(reader); sections.parseSections(reader);
parseImageMethods(reader); parseImageMethods(reader);
@@ -157,8 +156,9 @@ public class ArtHeader_10 extends ArtHeader {
} }
/** /**
* App images currently require a boot image, * App images currently require a boot image, if the size is non zero then it is
* if the size is non zero then it is an app image header. * an app image header.
*
* @return true if this header represents an app image * @return true if this header represents an app image
*/ */
public boolean isAppImage() { public boolean isAppImage() {
@@ -196,12 +196,11 @@ public class ArtHeader_10 extends ArtHeader {
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType(); Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_10.class);
try { try {
structure.setName(className); structure.setName(ArtHeader_10.class.getSimpleName());
} }
catch (InvalidNameException e) { 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); structure.add(DWORD, "image_reservation_size_", null);
@@ -20,10 +20,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.art.*; import ghidra.file.formats.android.art.*;
import ghidra.file.formats.android.art.android10.ImageMethod_10; 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.file.formats.android.util.DecompressionManager;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure; import ghidra.program.model.data.Structure;
@@ -85,7 +83,7 @@ public class ArtHeader_11 extends ArtHeader {
image_roots_ = reader.readNextInt(); image_roots_ = reader.readNextInt();
pointer_size_ = reader.readNextInt(); pointer_size_ = reader.readNextInt();
sections = new ImageSections_10(reader, this); sections = ArtImageSectionsFactory.getArtImageSections(reader, this);
sections.parseSections(reader); sections.parseSections(reader);
parseImageMethods(reader); parseImageMethods(reader);
@@ -210,9 +208,8 @@ public class ArtHeader_11 extends ArtHeader {
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType(); Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_11.class);
try { try {
structure.setName(className); structure.setName(ArtHeader_11.class.getSimpleName());
} }
catch (InvalidNameException e) { catch (InvalidNameException e) {
//ignore //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 java.io.IOException;
import ghidra.app.util.bin.BinaryReader; 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.ArtHeader;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure; import ghidra.program.model.data.Structure;
@@ -152,11 +151,11 @@ public class ArtHeader_KitKat extends ArtHeader {
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType(); Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_KitKat.class);
try { try {
structure.setName(className); structure.setName(ArtHeader_KitKat.class.getSimpleName());
} }
catch (InvalidNameException e) { catch (InvalidNameException e) {
//ignore
} }
structure.add(DWORD, "image_begin_", null); structure.add(DWORD, "image_begin_", null);
@@ -18,7 +18,6 @@ package ghidra.file.formats.android.art.lollipop;
import java.io.IOException; import java.io.IOException;
import ghidra.app.util.bin.BinaryReader; 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.ArtHeader;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure; import ghidra.program.model.data.Structure;
@@ -128,11 +127,11 @@ public class ArtHeader_Lollipop extends ArtHeader {
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType(); Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_Lollipop.class);
try { try {
structure.setName(className); structure.setName(ArtHeader_Lollipop.class.getSimpleName());
} }
catch (InvalidNameException e) { catch (InvalidNameException e) {
//ignore
} }
structure.add(DWORD, "image_begin_", null); structure.add(DWORD, "image_begin_", null);
@@ -18,7 +18,6 @@ package ghidra.file.formats.android.art.lollipop;
import java.io.IOException; import java.io.IOException;
import ghidra.app.util.bin.BinaryReader; 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.ArtHeader;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure; import ghidra.program.model.data.Structure;
@@ -127,11 +126,11 @@ public class ArtHeader_LollipopMR1WFC extends ArtHeader {
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType(); Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_LollipopMR1WFC.class);
try { try {
structure.setName(className); structure.setName(ArtHeader_LollipopMR1WFC.class.getSimpleName());
} }
catch (InvalidNameException e) { catch (InvalidNameException e) {
//ignore
} }
structure.add(DWORD, "image_begin_", null); structure.add(DWORD, "image_begin_", null);
@@ -18,9 +18,7 @@ package ghidra.file.formats.android.art.marshmallow;
import java.io.IOException; import java.io.IOException;
import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil; import ghidra.file.formats.android.art.*;
import ghidra.file.formats.android.art.ArtHeader;
import ghidra.file.formats.android.art.ArtImageSections;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure; import ghidra.program.model.data.Structure;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
@@ -69,7 +67,7 @@ public class ArtHeader_Marshmallow extends ArtHeader {
pointer_size_ = reader.readNextInt(); pointer_size_ = reader.readNextInt();
compile_pic_ = reader.readNextInt(); compile_pic_ = reader.readNextInt();
sections = new ImageSections_Marshmallow(reader, this); sections = ArtImageSectionsFactory.getArtImageSections(reader, this);
sections.parseSections(reader); sections.parseSections(reader);
for (int i = 0; i < image_methods_.length; ++i) { 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 { public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType(); Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_Marshmallow.class);
try { try {
structure.setName(className); structure.setName(ArtHeader_Marshmallow.class.getSimpleName());
} }
catch (InvalidNameException e) { catch (InvalidNameException e) {
} }
@@ -18,7 +18,6 @@ package ghidra.file.formats.android.art.nougat;
import java.io.IOException; import java.io.IOException;
import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.art.*; import ghidra.file.formats.android.art.*;
import ghidra.file.formats.android.util.DecompressionManager; import ghidra.file.formats.android.util.DecompressionManager;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
@@ -62,10 +61,6 @@ public class ArtHeader_Nougat extends ArtHeader implements ArtCompression {
parse(reader); parse(reader);
} }
protected ArtImageSections getImageSections(BinaryReader reader) {
return new ImageSections_Nougat(reader, this);
}
@Override @Override
protected void parse(BinaryReader reader) throws IOException { protected void parse(BinaryReader reader) throws IOException {
image_begin_ = reader.readNextInt(); image_begin_ = reader.readNextInt();
@@ -85,7 +80,7 @@ public class ArtHeader_Nougat extends ArtHeader implements ArtCompression {
compile_pic_ = reader.readNextInt(); compile_pic_ = reader.readNextInt();
is_pic_ = reader.readNextInt(); is_pic_ = reader.readNextInt();
sections = getImageSections(reader); sections = ArtImageSectionsFactory.getArtImageSections(reader, this);
sections.parseSections(reader); sections.parseSections(reader);
parseImageMethods(reader); parseImageMethods(reader);
@@ -212,9 +207,8 @@ public class ArtHeader_Nougat extends ArtHeader implements ArtCompression {
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType(); Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_Nougat.class);
try { try {
structure.setName(className); structure.setName(ArtHeader_Nougat.class.getSimpleName());
} }
catch (InvalidNameException e) { catch (InvalidNameException e) {
//ignore, just use original name should this fail //ignore, just use original name should this fail
@@ -18,8 +18,6 @@ package ghidra.file.formats.android.art.nougat;
import java.io.IOException; import java.io.IOException;
import ghidra.app.util.bin.BinaryReader; 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.DataType;
import ghidra.program.model.data.Structure; import ghidra.program.model.data.Structure;
import ghidra.util.InvalidNameException; import ghidra.util.InvalidNameException;
@@ -34,10 +32,6 @@ public class ArtHeader_NougatMR2Pixel extends ArtHeader_Nougat {
super(reader); super(reader);
} }
protected ArtImageSections getImageSections(BinaryReader reader) {
return new ImageSections_NougatMR2Pixel(reader, this);
}
@Override @Override
public int getArtMethodCountForVersion() { public int getArtMethodCountForVersion() {
return ImageMethod_Nougat.kImageMethodsCount.ordinal(); return ImageMethod_Nougat.kImageMethodsCount.ordinal();
@@ -46,9 +40,8 @@ public class ArtHeader_NougatMR2Pixel extends ArtHeader_Nougat {
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType(); Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_NougatMR2Pixel.class);
try { try {
structure.setName(className); structure.setName(ArtHeader_NougatMR2Pixel.class.getSimpleName());
} }
catch (InvalidNameException e) { catch (InvalidNameException e) {
//ignore //ignore
@@ -18,8 +18,6 @@ package ghidra.file.formats.android.art.oreo;
import java.io.IOException; import java.io.IOException;
import ghidra.app.util.bin.BinaryReader; 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.file.formats.android.art.nougat.ArtHeader_NougatMR2Pixel;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure; import ghidra.program.model.data.Structure;
@@ -35,10 +33,6 @@ public class ArtHeader_Oreo extends ArtHeader_NougatMR2Pixel {
super(reader); super(reader);
} }
protected ArtImageSections getImageSections(BinaryReader reader) {
return new ImageSections_Oreo(reader, this);
}
@Override @Override
public int getArtMethodCountForVersion() { public int getArtMethodCountForVersion() {
return ImageMethod_Oreo.kImageMethodsCount.ordinal(); return ImageMethod_Oreo.kImageMethodsCount.ordinal();
@@ -47,9 +41,8 @@ public class ArtHeader_Oreo extends ArtHeader_NougatMR2Pixel {
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType(); Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_Oreo.class);
try { try {
structure.setName(className); structure.setName(ArtHeader_Oreo.class.getSimpleName());
} }
catch (InvalidNameException e) { catch (InvalidNameException e) {
//ignore //ignore
@@ -18,8 +18,6 @@ package ghidra.file.formats.android.art.oreo;
import java.io.IOException; import java.io.IOException;
import ghidra.app.util.bin.BinaryReader; 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.DataType;
import ghidra.program.model.data.Structure; import ghidra.program.model.data.Structure;
import ghidra.util.InvalidNameException; import ghidra.util.InvalidNameException;
@@ -34,10 +32,6 @@ public class ArtHeader_OreoMR1 extends ArtHeader_Oreo {
super(reader); super(reader);
} }
protected ArtImageSections getImageSections(BinaryReader reader) {
return new ImageSections_OreoMR1(reader, this);
}
@Override @Override
public int getArtMethodCountForVersion() { public int getArtMethodCountForVersion() {
return ImageMethod_Oreo.kImageMethodsCount.ordinal(); return ImageMethod_Oreo.kImageMethodsCount.ordinal();
@@ -46,9 +40,8 @@ public class ArtHeader_OreoMR1 extends ArtHeader_Oreo {
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType(); Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_OreoMR1.class);
try { try {
structure.setName(className); structure.setName(ArtHeader_OreoMR1.class.getSimpleName());
} }
catch (InvalidNameException e) { catch (InvalidNameException e) {
//ignore //ignore
@@ -18,7 +18,6 @@ package ghidra.file.formats.android.art.pie;
import java.io.IOException; import java.io.IOException;
import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverterUtil;
import ghidra.file.formats.android.art.*; import ghidra.file.formats.android.art.*;
import ghidra.file.formats.android.util.DecompressionManager; import ghidra.file.formats.android.util.DecompressionManager;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
@@ -82,7 +81,7 @@ public class ArtHeader_Pie extends ArtHeader implements ArtCompression {
compile_pic_ = reader.readNextInt(); compile_pic_ = reader.readNextInt();
is_pic_ = reader.readNextInt(); is_pic_ = reader.readNextInt();
sections = new ImageSections_Pie(reader, this); sections = ArtImageSectionsFactory.getArtImageSections(reader, this);
sections.parseSections(reader); sections.parseSections(reader);
parseImageMethods(reader); parseImageMethods(reader);
@@ -205,9 +204,8 @@ public class ArtHeader_Pie extends ArtHeader implements ArtCompression {
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType(); Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_Pie.class);
try { try {
structure.setName(className); structure.setName(ArtHeader_Pie.class.getSimpleName());
} }
catch (InvalidNameException e) { catch (InvalidNameException e) {
//ignore //ignore
@@ -94,7 +94,7 @@ public class BootImageHeaderV0 extends BootImageHeader {
@Override @Override
public int getRamdiskOffset() { public int getRamdiskOffset() {
return page_size + getKernelPageCount() * page_size;//see header comment... return page_size + getKernelPageCount() * page_size;
} }
public int getRamdiskAddress() { public int getRamdiskAddress() {
@@ -67,7 +67,7 @@ public class BootImageHeaderV3 extends BootImageHeader {
*/ */
@Override @Override
public int getKernelPageCount() { public int getKernelPageCount() {
return (int)(pageAlign(kernel_size) / BootImageConstants.V3_PAGE_SIZE); return (int) (pageAlign(kernel_size) / BootImageConstants.V3_PAGE_SIZE);
} }
@Override @Override
@@ -16,21 +16,14 @@
package ghidra.file.formats.android.cdex; package ghidra.file.formats.android.cdex;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.*; import java.util.*;
import ghidra.app.util.Option;
import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.*; import ghidra.app.util.opinion.*;
import ghidra.file.formats.android.dex.DexHeaderFactory; import ghidra.file.formats.android.dex.DexHeaderFactory;
import ghidra.file.formats.android.dex.format.*; import ghidra.file.formats.android.dex.format.DexConstants;
import ghidra.file.formats.android.dex.util.DexUtil; import ghidra.file.formats.android.dex.format.DexHeader;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.util.task.TaskMonitor;
public class CDexLoader extends DexLoader { public class CDexLoader extends DexLoader {
@@ -82,98 +75,18 @@ public class CDexLoader extends DexLoader {
} }
@Override @Override
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options, protected String getMemoryBlockName() {
Program program, TaskMonitor monitor, MessageLog log) throws IOException { return ".cdex";
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);
}
} }
private void createMethodByteCodeBlock(Program program, long length, TaskMonitor monitor) @Override
throws Exception { protected String getMonitorMessagePrimary() {
Address address = toAddr(program, DexUtil.METHOD_ADDRESS); return "CDEX Loader: creating cdex memory";
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) @Override
throws Exception { protected String getMonitorMessageSecondary() {
Address address = toAddr(program, DexUtil.LOOKUP_ADDRESS); return "CDEX Loader: creating method byte code";
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);
} }
} }
@@ -74,16 +74,34 @@ public final class DexHeaderFactory {
* @throws IOException should an error occur reading DEX bytes * @throws IOException should an error occur reading DEX bytes
*/ */
public final static DexHeader getDexHeader(BinaryReader reader) throws IOException { 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(); long index = reader.getPointerIndex();
String magic = new String(reader.readByteArray(index, 4)); String magic = new String(reader.readByteArray(index, 4));
if (DexConstants.DEX_MAGIC_BASE.equals(magic)) { if (DexConstants.DEX_MAGIC_BASE.equals(magic)) {
DexHeader header = new DexHeader(reader); DexHeader header = new DexHeader(reader);
header.parse(reader); if (fullParse) {
header.parse(reader);
}
return header; return header;
} }
if (CDexConstants.MAGIC.equals(magic)) { if (CDexConstants.MAGIC.equals(magic)) {
CDexHeader header = new CDexHeader(reader); CDexHeader header = new CDexHeader(reader);
header.parse(reader); if (fullParse) {
header.parse(reader);
}
return header; return header;
} }
throw new IOException("Not a recognized DEX/CDEX variant: " + magic); 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.DexHeaderFactory;
import ghidra.file.formats.android.dex.format.*; import ghidra.file.formats.android.dex.format.*;
import ghidra.file.formats.android.dex.util.DexUtil; import ghidra.file.formats.android.dex.util.DexUtil;
import ghidra.framework.main.AppInfo;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace; import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
@@ -23,13 +23,20 @@ import ghidra.app.util.importer.MessageLog;
import ghidra.file.analyzers.FileFormatAnalyzer; import ghidra.file.analyzers.FileFormatAnalyzer;
import ghidra.file.formats.android.cdex.CDexConstants; import ghidra.file.formats.android.cdex.CDexConstants;
import ghidra.file.formats.android.dex.format.DexConstants; import ghidra.file.formats.android.dex.format.DexConstants;
import ghidra.framework.options.Options;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.exception.NotEmptyException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer { 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 @Override
public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws Exception { throws Exception {
@@ -41,12 +48,21 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
return true; return true;
} }
DexHeaderFormatMarkup markup = new DexHeaderFormatMarkup(program, baseAddress); DexHeaderFormatMarkup markup = new DexHeaderFormatMarkup(this, program, baseAddress);
markup.markup(monitor, log); markup.markup(monitor, log);
return true; return true;
} }
boolean isCreateFragments() {
return isCreateFragments;
}
@Override
public void removeEmptyFragments(Program program) throws NotEmptyException {
super.removeEmptyFragments(program);
}
@Override @Override
public boolean canAnalyze(Program program) { public boolean canAnalyze(Program program) {
ByteProvider provider = ByteProvider provider =
@@ -83,4 +99,20 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
public boolean isPrototype() { public boolean isPrototype() {
return false; 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 class CodeItem implements StructConverter {
public static final String CODE_ITEM = "code_item";
protected short registersSize; protected short registersSize;
protected short incomingSize; protected short incomingSize;
protected short outgoingSize; protected short outgoingSize;
@@ -200,7 +202,7 @@ public class CodeItem implements StructConverter {
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
String suffix = hasPadding() ? "_p" : ""; 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 structure = new StructureDataType(name, 0);
structure.add(WORD, "registers_size", null); structure.add(WORD, "registers_size", null);
structure.add(WORD, "ins_size", null); structure.add(WORD, "ins_size", null);
@@ -212,16 +214,6 @@ public class CodeItem implements StructConverter {
if (hasPadding()) { if (hasPadding()) {
structure.add(WORD, "padding", null); 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")); structure.setCategoryPath(new CategoryPath("/dex/code_item"));
return structure; return structure;
} }
@@ -19,7 +19,8 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.data.*;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@@ -71,8 +72,7 @@ public class FBPK implements StructConverter {
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(FBPK.class); Structure struct = new StructureDataType(FBPK.class.getSimpleName(), 0);
Structure struct = new StructureDataType(className, 0);
struct.add(STRING, FBPK_Constants.FBPK.length(), "magic", null); struct.add(STRING, FBPK_Constants.FBPK.length(), "magic", null);
struct.add(DWORD, "unknown1", null); struct.add(DWORD, "unknown1", null);
struct.add(STRING, FBPK_Constants.VERSION_MAX_LENGTH, "version", null); struct.add(STRING, FBPK_Constants.VERSION_MAX_LENGTH, "version", null);
@@ -92,8 +92,7 @@ public class FBPK_Partition implements StructConverter {
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(FBPK_Partition.class); Structure struct = new StructureDataType(FBPK_Partition.class.getSimpleName(), 0);
Structure struct = new StructureDataType(className, 0);
struct.add(DWORD, "type", null); struct.add(DWORD, "type", null);
struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH, "name", null); struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH, "name", null);
struct.add(DWORD, "dataSize", null); struct.add(DWORD, "dataSize", null);
@@ -19,7 +19,8 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.data.*;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@@ -147,8 +148,7 @@ public class FBPT implements StructConverter {
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(FBPT.class); Structure struct = new StructureDataType(FBPT.class.getSimpleName(), 0);
Structure struct = new StructureDataType(className, 0);
struct.add(STRING, FBPK_Constants.FBPT.length(), "magic", null); struct.add(STRING, FBPK_Constants.FBPT.length(), "magic", null);
struct.add(DWORD, "unknown1", null); struct.add(DWORD, "unknown1", null);
struct.add(DWORD, "unknown2", null); struct.add(DWORD, "unknown2", null);
@@ -17,7 +17,8 @@ package ghidra.file.formats.android.fbpk;
import java.io.IOException; 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.program.model.data.*;
import ghidra.util.InvalidNameException; import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@@ -78,15 +79,14 @@ public class FBPT_Entry implements StructConverter {
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(FBPT_Entry.class); Structure struct = new StructureDataType(FBPT_Entry.class.getSimpleName(), 0);
Structure struct = new StructureDataType(className, 0);
struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH, "name", null); 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, "guid1", null);
struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH + 1, "guid2", null); struct.add(STRING, FBPK_Constants.NAME_MAX_LENGTH + 1, "guid2", null);
struct.add(STRING, 2, "padding", null); struct.add(STRING, 2, "padding", null);
if (FBPK_Constants.LAST_PARTITION_ENTRY.equals(name) || isLast) { if (FBPK_Constants.LAST_PARTITION_ENTRY.equals(name) || isLast) {
try { try {
struct.setName(className + "_last"); struct.setName(FBPT_Entry.class.getSimpleName() + "_last");
} }
catch (InvalidNameException e) { catch (InvalidNameException e) {
//ignore //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_q_preview_1 = "166";
public final static String VERSION_10_RELEASE = "170"; public final static String VERSION_10_RELEASE = "170";
public final static String VERSION_11_RELEASE = "183"; 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 public final static int VERSION_LENGTH = 3;//3 bytes in length
@@ -101,6 +102,7 @@ public final class OatConstants {
VERSION_PIE_RELEASE, VERSION_PIE_RELEASE,
VERSION_10_RELEASE, VERSION_10_RELEASE,
VERSION_11_RELEASE, VERSION_11_RELEASE,
VERSION_12_RELEASE,
}; };
/** Keys from the OAT header "key/value" store. */ /** 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.bin.*;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.file.analyzers.FileFormatAnalyzer; 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.Address;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
@@ -21,7 +21,9 @@ import java.util.*;
import generic.continues.RethrowContinuesFactory; import generic.continues.RethrowContinuesFactory;
import ghidra.app.util.bin.*; import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.elf.*; 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.dex.format.DexHeader;
import ghidra.file.formats.android.oat.oatdexfile.OatDexFile;
import ghidra.formats.gfilesystem.*; import ghidra.formats.gfilesystem.*;
import ghidra.formats.gfilesystem.annotations.FileSystemInfo; import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
import ghidra.formats.gfilesystem.factory.GFileSystemBaseFactory; import ghidra.formats.gfilesystem.factory.GFileSystemBaseFactory;
@@ -100,7 +102,7 @@ public class OatFileSystem extends GFileSystemBase {
ElfSectionHeader roDataSection = elf.getSection(ElfSectionHeaderConstants.dot_rodata); ElfSectionHeader roDataSection = elf.getSection(ElfSectionHeaderConstants.dot_rodata);
if (roDataSection == null) { if (roDataSection == null) {
//TODO should we check? throw new IOException("rodata section does not exist.");
} }
baseOffset = roDataSection.getOffset(); baseOffset = roDataSection.getOffset();
@@ -109,8 +111,7 @@ public class OatFileSystem extends GFileSystemBase {
new ByteProviderWrapper(provider, baseOffset, roDataSection.getSize()); new ByteProviderWrapper(provider, baseOffset, roDataSection.getSize());
BinaryReader reader = new BinaryReader(wrapper, elf.isLittleEndian()); BinaryReader reader = new BinaryReader(wrapper, elf.isLittleEndian());
OatHeader oatHeader = OatHeaderFactory.newOatHeader(reader); OatHeader oatHeader = OatHeaderFactory.newOatHeader(reader);
//oatHeader.parse( reader, null ); OatHeaderFactory.parseOatHeader(oatHeader, null, reader, monitor, new MessageLog());
OatHeaderFactory.parseOatHeader(oatHeader, reader, monitor);
monitor.incrementProgress(1); monitor.incrementProgress(1);
dexFileList = oatHeader.getOatDexFileList(); dexFileList = oatHeader.getOatDexFileList();
@@ -184,7 +185,7 @@ public class OatFileSystem extends GFileSystemBase {
throw new IOException("Invalid / unknown file: " + file); throw new IOException("Invalid / unknown file: " + file);
} }
OatDexFile oatDexFileHeader = dexFileList.get(index); OatDexFile oatDexFileHeader = dexFileList.get(index);
return new ByteProviderWrapper(provider, oatDexFileHeader.getDexFileOffset(), return new ByteProviderWrapper(provider, baseOffset + oatDexFileHeader.getDexFileOffset(),
oatDexFileHeader.getDexHeader().getFileSize(), file.getFSRL()); oatDexFileHeader.getDexHeader().getFileSize(), file.getFSRL());
} }
@@ -16,10 +16,13 @@
package ghidra.file.formats.android.oat; package ghidra.file.formats.android.oat;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.*;
import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter; 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 * Base OatHeader implementations
@@ -34,6 +37,16 @@ public abstract class OatHeader implements StructConverter {
protected String magic; protected String magic;
protected String version; 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 { protected OatHeader(BinaryReader reader) throws IOException {
magic = new String(reader.readNextByteArray(OatConstants.MAGIC.length())); magic = new String(reader.readNextByteArray(OatConstants.MAGIC.length()));
version = reader.readNextAsciiString(4); version = reader.readNextAsciiString(4);
@@ -43,12 +56,34 @@ public abstract class OatHeader implements StructConverter {
* Parses the OAT header beyond the MAGIC and VERSION fields. * Parses the OAT header beyond the MAGIC and VERSION fields.
* The "additionalData" is used to future proof the parsers. * The "additionalData" is used to future proof the parsers.
* For example, its needed for handling vdex files. * 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) public void parse(BinaryReader reader, OatBundle bundle)
throws IOException, UnsupportedOatVersionException; 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". * Returns the MAGIC string, i.e. "oat\n".
* @return the MAGIC string, i.e. "oat\n".
*/ */
public String getMagic() { public String getMagic() {
return magic; return magic;
@@ -56,39 +91,55 @@ public abstract class OatHeader implements StructConverter {
/** /**
* Returns the VERSION string, e.g. "001", "009", etc. * Returns the VERSION string, e.g. "001", "009", etc.
* @return the VERSION string, e.g. "001", "009", etc.
*/ */
public String getVersion() { public String getVersion() {
return version; 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. * 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(); abstract public int getDexFileCount();
/** /**
* Returns the size (in bytes) of the Key/Value store contained inside this OAT file. * 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(); abstract public int getKeyValueStoreSize();
/** /**
* Returns a list of the OatDexFileHeader, a structure defining the embedded DEX files. * 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(); abstract public List<OatDexFile> getOatDexFileList();
/** /**
* Returns the OAT instruction set (ARM, X86, etc). * Returns the OAT instruction set (ARM, X86, etc).
* @return the OAT instruction set (ARM, X86, etc).
*/ */
abstract public OatInstructionSet getInstructionSet(); 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. * The executable code can also be located using the "oatexec" symbol.
* @return the offset to the executable code.
*/ */
abstract public int getExecutableOffset(); abstract public int getExecutableOffset();
/** /**
* Returns the OAT checksum value. * Returns the OAT checksum value.
* @return the OAT checksum value.
*/ */
abstract public int getChecksum(); abstract public int getChecksum();
} }
@@ -22,6 +22,7 @@ import ghidra.app.util.bin.format.elf.ElfSectionHeaderConstants;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.file.analyzers.FileFormatAnalyzer; import ghidra.file.analyzers.FileFormatAnalyzer;
import ghidra.file.formats.android.dex.format.DexHeader; 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.Address;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
@@ -57,7 +58,7 @@ public class OatHeaderAnalyzer extends FileFormatAnalyzer {
@Override @Override
public boolean isPrototype() { public boolean isPrototype() {
return true; return false;
} }
@Override @Override
@@ -19,8 +19,8 @@ import java.io.IOException;
import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.file.formats.android.vdex.VdexFactory; import ghidra.file.formats.android.oat.bundle.OatBundle;
import ghidra.file.formats.android.vdex.VdexHeader; import ghidra.file.formats.android.oat.bundle.OatBundleFactory;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@@ -62,35 +62,21 @@ public final class OatHeaderFactory {
return new OatHeader_10(reader); return new OatHeader_10(reader);
case OatConstants.VERSION_11_RELEASE: case OatConstants.VERSION_11_RELEASE:
return new OatHeader_11(reader); return new OatHeader_11(reader);
case OatConstants.VERSION_12_RELEASE:
return new OatHeader_12(reader);
} }
} }
} }
throw new UnsupportedOatVersionException(magic, version); 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, public final static void parseOatHeader(OatHeader oatHeader, Program oatProgram,
BinaryReader reader, TaskMonitor monitor, MessageLog log) BinaryReader reader, TaskMonitor monitor, MessageLog log)
throws UnsupportedOatVersionException, IOException { throws UnsupportedOatVersionException, IOException {
if (oatHeader.getVersion().equals(OatConstants.VERSION_OREO_RELEASE) || OatBundle bundle = OatBundleFactory.getOatBundle(oatProgram, oatHeader, monitor, log);
oatHeader.getVersion().equals(OatConstants.VERSION_OREO_M2_RELEASE) || oatHeader.parse(reader, bundle);
oatHeader.getVersion().equals(OatConstants.VERSION_PIE_RELEASE) || bundle.close();
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);
}
} }
} }
@@ -19,7 +19,7 @@ import java.io.IOException;
import java.util.*; import java.util.*;
import ghidra.app.util.bin.BinaryReader; 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.program.model.data.*;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@@ -36,10 +36,6 @@ public class OatHeader_10 extends OatHeader {
protected int quick_resolution_trampoline_offset_; protected int quick_resolution_trampoline_offset_;
protected int quick_to_interpreter_bridge_offset_; protected int quick_to_interpreter_bridge_offset_;
protected int key_value_store_size_; 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 { OatHeader_10(BinaryReader reader) throws IOException {
super(reader); super(reader);
@@ -59,23 +55,8 @@ public class OatHeader_10 extends OatHeader {
} }
@Override @Override
public void parse(BinaryReader reader, Object additionalData) public int getOatDexFilesOffset(BinaryReader reader) {
throws IOException, UnsupportedOatVersionException { return oat_dex_files_offset_;
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));
}
} }
@Override @Override
@@ -110,8 +91,7 @@ public class OatHeader_10 extends OatHeader {
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(OatHeader_10.class); Structure structure = new StructureDataType(OatHeader_10.class.getSimpleName(), 0);
Structure structure = new StructureDataType(className, 0);
structure.add(STRING, 4, "magic_", null); structure.add(STRING, 4, "magic_", null);
structure.add(STRING, 4, "version_", null); structure.add(STRING, 4, "version_", null);
structure.add(DWORD, "oat_checksum_", 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.add(STRING, value.length() + 1, "key_value_store_[" + i + "].value", null);
} }
structure.setCategoryPath(new CategoryPath("/oat"));
return structure; return structure;
} }
@@ -19,7 +19,7 @@ import java.io.IOException;
import java.util.*; import java.util.*;
import ghidra.app.util.bin.BinaryReader; 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.program.model.data.*;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@@ -37,10 +37,6 @@ public class OatHeader_11 extends OatHeader {
protected int quick_resolution_trampoline_offset_; protected int quick_resolution_trampoline_offset_;
protected int quick_to_interpreter_bridge_offset_; protected int quick_to_interpreter_bridge_offset_;
protected int key_value_store_size_; 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 { OatHeader_11(BinaryReader reader) throws IOException {
super(reader); super(reader);
@@ -61,23 +57,8 @@ public class OatHeader_11 extends OatHeader {
} }
@Override @Override
public void parse(BinaryReader reader, Object additionalData) public int getOatDexFilesOffset(BinaryReader reader) {
throws IOException, UnsupportedOatVersionException { return oat_dex_files_offset_;
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));
}
} }
@Override @Override
@@ -112,8 +93,7 @@ public class OatHeader_11 extends OatHeader {
@Override @Override
public DataType toDataType() throws DuplicateNameException, IOException { public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(OatHeader_11.class); Structure structure = new StructureDataType(OatHeader_11.class.getSimpleName(), 0);
Structure structure = new StructureDataType(className, 0);
structure.add(STRING, 4, "magic_", null); structure.add(STRING, 4, "magic_", null);
structure.add(STRING, 4, "version_", null); structure.add(STRING, 4, "version_", null);
structure.add(DWORD, "oat_checksum_", 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.add(STRING, value.length() + 1, "key_value_store_[" + i + "].value", null);
} }
structure.setCategoryPath(new CategoryPath("/oat"));
return structure; return structure;
} }

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