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