GP-804: Added support for Android formats (ART, OAT, ODEX, DEX, CDEX,

VDEX) and Dalvik VM SLEIGH modules for each major Android release up to
11.x (R)
This commit is contained in:
GhidraKnight
2020-06-18 09:59:41 -04:00
committed by Ryan Kurtz
parent 6dd6486627
commit ac5d2cfada
276 changed files with 21287 additions and 2671 deletions

View File

@@ -23,6 +23,7 @@ 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.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;
@@ -46,7 +47,7 @@ public class DexLoader extends AbstractLibrarySupportLoader {
BinaryReader reader = new BinaryReader(provider, true);
try {
DexHeader header = new DexHeader(reader);
DexHeader header = DexHeaderFactory.getDexHeader(reader);
if (DexConstants.DEX_MAGIC_BASE.equals(new String(header.getMagic()))) {
List<QueryResult> queries =
QueryOpinionService.query(getName(), DexConstants.MACHINE, null);
@@ -80,7 +81,7 @@ public class DexLoader extends AbstractLibrarySupportLoader {
}
BinaryReader reader = new BinaryReader( provider, true );
DexHeader header = new DexHeader( reader );
DexHeader header = DexHeaderFactory.getDexHeader( reader );
monitor.setMessage( "DEX Loader: creating method byte code" );

View File

@@ -0,0 +1,27 @@
/* ###
* 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.apex;
/**
* Android Pony EXpress (ApexContants)
*
* https://source.android.com/devices/tech/ota/apex
*
* https://android.googlesource.com/platform/system/apex/+/refs/heads/master/apexd/apex_constants.h
*/
public class ApexContants {
}

View File

@@ -0,0 +1,102 @@
/* ###
* 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 ghidra.app.util.bin.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.BinaryLoader;
import ghidra.file.analyzers.FileFormatAnalyzer;
import ghidra.file.formats.android.oat.OatConstants;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
import ghidra.util.task.TaskMonitor;
public class ArtAnalyzer extends FileFormatAnalyzer {
@Override
public String getName() {
return "Android ART Header Format";
}
@Override
public boolean getDefaultEnablement(Program program) {
return true;
}
@Override
public String getDescription() {
return "Analyzes the Android ART information in this program.";
}
@Override
public boolean canAnalyze(Program program) {
return ArtConstants.isART(program)
//HACK:
//Make analyzer appear after ART is merged with OAT program
//Currently, analyzers will not recognize the new ART block being added
|| OatConstants.isOAT(program);
}
@Override
public boolean isPrototype() {
return true;
}
@Override
public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws Exception {
Address address = ArtConstants.findART(program);
if (address == null) {//ART does not exist so quit, could be OAT
return false;
}
ByteProvider provider = new MemoryByteProvider(program.getMemory(), address);
BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian());
try {
ArtHeader header = ArtFactory.newArtHeader(reader);
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
Address imageBase = toAddr(program, header.getImageBegin());
if (BinaryLoader.BINARY_NAME.equals(program.getExecutableFormat())) {
program.setImageBase(imageBase, true);
createData(program, imageBase, headerDataType);
}
else {
createData(program, address, headerDataType);
}
header.markup(program, monitor);
return true;
}
catch (UnsupportedArtVersionException e) {
log.appendException(e);
}
catch (Exception e) {
throw e;
}
return false;
}
}

View File

@@ -0,0 +1,99 @@
/* ###
* 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.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/android10-release/runtime/image.h
*
*
*/
public class ArtBlock implements StructConverter, ArtCompression {
private ArtStorageMode storage_mode_ = ArtStorageMode.kDefaultStorageMode;
private int data_offset_;
private int data_size_;
private int image_offset_;
private int image_size_;
public ArtBlock(BinaryReader reader) throws IOException {
storage_mode_ = ArtStorageMode.get(reader.readNextInt());
data_offset_ = reader.readNextInt();
data_size_ = reader.readNextInt();
image_offset_ = reader.readNextInt();
image_size_ = reader.readNextInt();
}
@Override
public ArtStorageMode getStorageMode() {
return storage_mode_;
}
@Override
public long getCompressedOffset() {
return Integer.toUnsignedLong(data_offset_);
}
@Override
public int getCompressedSize() {
return data_size_;
}
@Override
public long getDecompressedOffset() {
return Integer.toUnsignedLong(image_offset_);
}
@Override
public int getDecompressedSize() {
return image_size_;
}
public int getDataOffset() {
return data_offset_;
}
public int getDataSize() {
return data_size_;
}
public int getImageOffset() {
return image_offset_;
}
public int getImageSize() {
return image_size_;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = StructConverterUtil.parseName(ArtBlock.class);
Structure structure = new StructureDataType(name, 0);
structure.setCategoryPath(new CategoryPath("/art"));
structure.add(DWORD, "storage_mode_", storage_mode_.name());
structure.add(DWORD, "data_offset_", null);
structure.add(DWORD, "data_size_", null);
structure.add(DWORD, "image_offset_", null);
structure.add(DWORD, "image_size_", null);
return structure;
}
}

View File

@@ -0,0 +1,52 @@
/* ###
* 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;
public interface ArtCompression {
/**
* Storage method for the image, the image may be compressed.
* @return the storage method
* @throws UnknownArtStorageModeException when an unknown storage mode is encountered
*/
public ArtStorageMode getStorageMode() throws UnknownArtStorageModeException;
/**
* Data size for the image data excluding the bitmap and the header.
* For compressed images, this is the compressed size in the file.
* @return the compressed size
*/
public int getCompressedSize();
/**
* Offset to the start of the compressed bytes.
* Also, offset of where to place the decompressed bytes.
* @return the offset to the compressed bytes
*/
public long getCompressedOffset();
/**
* Expected size of the decompressed bytes.
* @return the expected decompressed size
*/
public int getDecompressedSize();
/**
* Offset to the start of the decompressed bytes.
* @return the offset to the dcompressed bytes
*/
public long getDecompressedOffset();
}

View File

@@ -0,0 +1,127 @@
/* ###
* 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 ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
/**
* https://android.googlesource.com/platform/art/+/master/runtime/image.cc
*/
public final class ArtConstants {
public final static String ART_NAME = "Android Runtime (ART)";
public final static String MAGIC = "art\n";
public final static int VERSION_LENGTH = 4;
public final static String VERSION_KITKAT_RELEASE = "005";
public final static String VERSION_LOLLIPOP_RELEASE = "009";
public final static String VERSION_LOLLIPOP_MR1_WFC_RELEASE = "012";
public final static String VERSION_MARSHMALLOW_RELEASE = "017";
public final static String VERSION_NOUGAT_RELEASE = "029";
public final static String VERSION_NOUGAT_MR2_PIXEL_RELEASE = "030";
public final static String VERSION_OREO_RELEASE = "043";
public final static String VERSION_OREO_DR1_RELEASE = "044";
public final static String VERSION_OREO_MR1_RELEASE = "046";
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
// "005",// kitkat-release
// "009",// lollipop-release
// "012",// lollipop-mr1-wfc-release
// "017",// marshmallow-release
// "029",// nougat-release
// "030",// nougat-mr2-pixel-release
// "043",// oreo-release
// "044",// taimen-op1
// "046",// oreo-mr1-release
// "051",//
// "056",// pie-release
// "059",// android-o-mr1-iot-release-1.0.0
// "060",// android-o-mr1-iot-release-1.0.1
// "061",// android-n-iot-release-polk-at1
/**
* NOTE: only going to support RELEASE versions
*/
public final static String[] SUPPORTED_VERSIONS = new String[] {
//@formatter:off
VERSION_KITKAT_RELEASE,
VERSION_LOLLIPOP_RELEASE,
VERSION_LOLLIPOP_MR1_WFC_RELEASE,
VERSION_MARSHMALLOW_RELEASE,
VERSION_NOUGAT_RELEASE,
VERSION_NOUGAT_MR2_PIXEL_RELEASE,
VERSION_OREO_RELEASE,
VERSION_OREO_DR1_RELEASE,
VERSION_OREO_MR1_RELEASE,
VERSION_PIE_RELEASE,
VERSION_10_RELEASE,
VERSION_11_RELEASE,
//@formatter:on
};
public final static boolean isSupportedVersion(String version) {
for (String supportedVersion : SUPPORTED_VERSIONS) {
if (supportedVersion.equals(version)) {
return true;
}
}
return false;
}
public final static boolean isART(Program program) {
if (program != null) {
for (MemoryBlock block : program.getMemory().getBlocks()) {
try {
byte[] bytes = new byte[ArtConstants.MAGIC.length()];
block.getBytes(block.getStart(), bytes);
String magic = new String(bytes);
if (ArtConstants.MAGIC.equals(magic)) {
return true;
}
}
catch (Exception e) {
//ignore
}
}
}
return false;
}
public final static Address findART(Program program) {
if (program != null) {
for (MemoryBlock block : program.getMemory().getBlocks()) {
try {
byte[] bytes = new byte[ArtConstants.MAGIC.length()];
block.getBytes(block.getStart(), bytes);
String magic = new String(bytes);
if (ArtConstants.MAGIC.equals(magic)) {
return block.getStart();
}
}
catch (Exception e) {
//ignore
}
}
}
return null;
}
}

View File

@@ -0,0 +1,79 @@
/* ###
* 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.ArtHeader_10;
import ghidra.file.formats.android.art.android11.ArtHeader_11;
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;
import ghidra.file.formats.android.art.marshmallow.ArtHeader_Marshmallow;
import ghidra.file.formats.android.art.nougat.ArtHeader_Nougat;
import ghidra.file.formats.android.art.nougat.ArtHeader_NougatMR2Pixel;
import ghidra.file.formats.android.art.oreo.ArtHeader_Oreo;
import ghidra.file.formats.android.art.oreo.ArtHeader_OreoMR1;
import ghidra.file.formats.android.art.pie.ArtHeader_Pie;
public final class ArtFactory {
/**
* Returns an ArtHeader of the correct version.
* @param reader the BinaryReader to the ART header
* @return the specific version of the ART header
* @throws IOException should an error occur during reading or parsing
* @throws UnsupportedArtVersionException when the provided version is invalid or not yet implemented.
*/
public final static ArtHeader newArtHeader(BinaryReader reader)
throws IOException, UnsupportedArtVersionException {
String magic = new String(reader.readByteArray(0, ArtConstants.MAGIC.length()));
String version = reader.readAsciiString(4, 4);
if (magic.equals(ArtConstants.MAGIC)) {
if (ArtConstants.isSupportedVersion(version)) {
switch (version ) {
case ArtConstants.VERSION_KITKAT_RELEASE:
return new ArtHeader_KitKat(reader);
case ArtConstants.VERSION_LOLLIPOP_RELEASE:
return new ArtHeader_Lollipop(reader);
case ArtConstants.VERSION_LOLLIPOP_MR1_WFC_RELEASE:
return new ArtHeader_LollipopMR1WFC(reader);
case ArtConstants.VERSION_MARSHMALLOW_RELEASE:
return new ArtHeader_Marshmallow(reader);
case ArtConstants.VERSION_NOUGAT_RELEASE:
return new ArtHeader_Nougat(reader);
case ArtConstants.VERSION_NOUGAT_MR2_PIXEL_RELEASE:
return new ArtHeader_NougatMR2Pixel(reader);
case ArtConstants.VERSION_OREO_RELEASE:
return new ArtHeader_Oreo(reader);
case ArtConstants.VERSION_OREO_DR1_RELEASE:
return new ArtHeader_Oreo(reader);//v043 and v044 are same format
case ArtConstants.VERSION_OREO_MR1_RELEASE:
return new ArtHeader_OreoMR1(reader);
case ArtConstants.VERSION_PIE_RELEASE:
return new ArtHeader_Pie(reader);
case ArtConstants.VERSION_10_RELEASE:
return new ArtHeader_10(reader);
case ArtConstants.VERSION_11_RELEASE:
return new ArtHeader_11(reader);
}
}
}
throw new UnsupportedArtVersionException(magic, version);
}
}

View File

@@ -0,0 +1,71 @@
/* ###
* 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.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/android10-release/runtime/art_field.h
*
*
*/
public class ArtField implements StructConverter {
private int declaring_class_;
private int access_flags_;
private int field_dex_idx_;
private int offset_;
public ArtField(BinaryReader reader) throws IOException {
declaring_class_ = reader.readNextInt();
access_flags_ = reader.readNextInt();
field_dex_idx_ = reader.readNextInt();
offset_ = reader.readNextInt();
}
public int getDeclaringClass() {
return declaring_class_;
}
public int getAccessFlags() {
return access_flags_;
}
public int getFieldDexIndex() {
return field_dex_idx_;
}
public int getOffset() {
return offset_;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = StructConverterUtil.parseName(ArtField.class);
Structure structure = new StructureDataType(name, 0);
structure.setCategoryPath(new CategoryPath("/art"));
structure.add(new Pointer32DataType(), "declaring_class_", null);
structure.add(DWORD, "access_flags_", null);
structure.add(DWORD, "field_dex_idx_", null);
structure.add(DWORD, "offset_", null);
return structure;
}
}

View File

@@ -0,0 +1,66 @@
/* ###
* 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 java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/android10-release/runtime/art_field.h
*
* NOTE: this class does not exist, was created to make field reading easier.
*/
public class ArtFieldGroup implements StructConverter {
private int fieldCount;
private List<ArtField> fieldList = new ArrayList<>();
public ArtFieldGroup(BinaryReader reader) throws IOException {
fieldCount = reader.readNextInt();
if (fieldCount > 0xffff) {//sanity check...
throw new IOException("Too many ART fields: " + fieldCount);
}
for (int i = 0; i < fieldCount; ++i) {
fieldList.add(new ArtField(reader));
}
}
public int getFieldCount() {
return fieldCount;
}
public List<ArtField> getFieldList() {
return fieldList;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = StructConverterUtil.parseName(ArtFieldGroup.class);
Structure structure = new StructureDataType(name + "_" + fieldCount, 0);
structure.setCategoryPath(new CategoryPath("/art"));
structure.add(DWORD, "fieldCount", null);
for (int i = 0; i < fieldCount; ++i) {
structure.add(fieldList.get(i).toDataType(), "field_" + i, null);
}
return structure;
}
}

View File

@@ -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.art;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.*;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* https://android.googlesource.com/platform/art/+/marshmallow-release/runtime/image.h
*/
public abstract class ArtHeader implements StructConverter {
protected String magic_;
protected String version_;
protected List<Long> imageMethodsList = new ArrayList<>();
protected ArtHeader(BinaryReader reader) throws IOException {
magic_ = new String(reader.readNextByteArray(ArtConstants.MAGIC.length()));
version_ = reader.readNextAsciiString(ArtConstants.VERSION_LENGTH);
}
/**
* Returns the magic string: "art\n".
* @return the magic string
*/
public final String getMagic() {
return magic_;
}
/**
* Returns the version string: eg, "001", "017"
* @return the version
*/
public final String getVersion() {
return version_;
}
/**
* Required base address for mapping the image.
*
* Base address of the ART file.
* -1 indicates unsupported
* @return image base address
*/
abstract public int getImageBegin();
/**
* Required base size for mapping the image.
* -1 indicates unsupported
* @return image size
*/
abstract public int getImageSize();
/**
* Returns the checksum of the matching OAT file.
* The checksum is stored in the OAT header and is generated using Adler32.
* -1 indicates unsupported
* @return oat checksum
*/
abstract public int getOatChecksum();
/**
* -1 indicates unsupported
* @return the oat file begin address
*/
abstract public int getOatFileBegin();
/**
* -1 indicates unsupported
* @return the oat file end address
*/
abstract public int getOatFileEnd();
/**
* Returns the offset to the start of the .oatdata section,
* usually defined within the ".rodata" section.
* -1 indicates unsupported
* @return the oat data begin address
*/
abstract public int getOatDataBegin();
/**
* -1 indicates unsupported
* @return the oat data end address
*/
abstract public int getOatDataEnd();
/**
* Pointer size (in bytes).
* @return the pointer size
*/
abstract public int getPointerSize();
abstract public int getArtMethodCountForVersion();
/**
* Parses the ART header data.
* @param reader the binary reader
* @throws IOException if an error occurs parsing the header
*/
abstract protected void parse(BinaryReader reader) throws IOException;
protected final void parseImageMethods(BinaryReader reader) throws IOException {
for (int i = 0; i < getArtMethodCountForVersion(); ++i) {
imageMethodsList.add(reader.readNextLong());
}
}
/**
* Allows each specific version to mark-up the specified program.
* @param program the program to markup
* @param monitor the task monitor
* @throws Exception if an error occurs while marking up the program
*/
abstract public void markup(Program program, TaskMonitor monitor) throws Exception;
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String className = StructConverterUtil.parseName(ArtHeader.class);
Structure structure = new StructureDataType(className, 0);
structure.add(STRING, 4, "magic_", null);
structure.add(STRING, 4, "version_", null);
structure.setCategoryPath(new CategoryPath("/art"));
return structure;
}
}

View File

@@ -0,0 +1,53 @@
/* ###
* 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.*;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.DuplicateNameException;
public class ArtImageSection implements StructConverter {
private int offset_;
private int size_;
public ArtImageSection(BinaryReader reader) throws IOException {
offset_ = reader.readNextInt();
size_ = reader.readNextInt();
}
public int getOffset() {
return offset_;
}
public int getSize() {
return size_;
}
public int getEnd() {
return offset_ + size_;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
DataType dataType = StructConverterUtil.toDataType(ArtImageSection.class);
dataType.setCategoryPath(new CategoryPath("/art"));
return dataType;
}
}

View File

@@ -0,0 +1,522 @@
/* ###
* 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 java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.task.TaskMonitor;
public abstract class ArtImageSections {
public final static int UNSUPPORTED_SECTION = -1;
protected ArtHeader header;
protected List<ArtImageSection> sectionList = new ArrayList<>();
protected List<ArtFieldGroup> fieldGroupList = new ArrayList<>();
protected List<ArtMethodGroup> methodGroupList = new ArrayList<>();
protected List<ArtField> fieldList = new ArrayList<>();
protected List<ArtMethod> methodList = new ArrayList<>();
protected ArtImageSections(BinaryReader reader, ArtHeader header) {
this.header = header;
}
/**
* Returns the section name for the given ordinal.
* For example, if sectionOrdinal is 0 then return "kSectionObjects".
* @param sectionOrdinal the original of the ART section
* @return the ART section name
*/
protected String getSectionName(int sectionOrdinal) {
for (Field field : getClass().getDeclaredFields()) {
if (field.getName().startsWith("kSection")) {
try {
Object value = field.get(null);
if ((int) value == sectionOrdinal) {
return field.getName();
}
}
catch (Exception e) {
//ignore
}
}
}
return "unknown_section_0x" + Integer.toHexString(sectionOrdinal);
}
public abstract int get_kSectionObjects();
public abstract int get_kSectionArtFields();
public abstract int get_kSectionArtMethods();
public abstract int get_kSectionRuntimeMethods();
public abstract int get_kSectionImTables();
public abstract int get_kSectionIMTConflictTables();
public abstract int get_kSectionDexCacheArrays();
public abstract int get_kSectionInternedStrings();
public abstract int get_kSectionClassTable();
public abstract int get_kSectionStringReferenceOffsets();
public abstract int get_kSectionMetadata();
public abstract int get_kSectionImageBitmap();
public abstract int get_kSectionCount(); // Number of elements in enum.
public final List<ArtImageSection> getSectionList() {
return sectionList;
}
public final void parseSections(BinaryReader reader) throws IOException {
for (int i = 0; i < get_kSectionCount(); ++i) {
sectionList.add(new ArtImageSection(reader));
}
}
public final void parse(BinaryReader reader) throws IOException {
parseArtFields(reader);
parseArtMethods(reader);
}
private void parseArtFields(BinaryReader reader) throws IOException {
ArtImageSection kSectionArtFields = sectionList.get(get_kSectionArtFields());
if (kSectionArtFields.getSize() > 0) {
if (reader.length() > kSectionArtFields.getOffset()) {//out of bounds
reader.setPointerIndex(kSectionArtFields.getOffset());
while (reader.getPointerIndex() < Integer
.toUnsignedLong(kSectionArtFields.getEnd())) {
if (ArtConstants.VERSION_MARSHMALLOW_RELEASE.equals(header.getVersion())) {
ArtField field = new ArtField(reader);
fieldList.add(field);
}
else {
ArtFieldGroup group = new ArtFieldGroup(reader);
fieldGroupList.add(group);
}
}
}
}
}
private void parseArtMethods(BinaryReader reader) throws IOException {
ArtImageSection kSectionArtMethods = sectionList.get(get_kSectionArtMethods());
if (kSectionArtMethods.getSize() > 0) {
if (reader.length() > kSectionArtMethods.getOffset()) {//out of bounds
reader.setPointerIndex(kSectionArtMethods.getOffset());
while (reader.getPointerIndex() < Integer
.toUnsignedLong(kSectionArtMethods.getEnd())) {
if (ArtConstants.VERSION_MARSHMALLOW_RELEASE.equals(header.getVersion())) {
ArtMethod method =
new ArtMethod(reader, header.getPointerSize(), header.getVersion());
methodList.add(method);
}
else {
ArtMethodGroup group = new ArtMethodGroup(reader, header.getPointerSize(),
header.getVersion());
methodGroupList.add(group);
}
}
}
}
}
public void markup(Program program, TaskMonitor monitor) throws Exception {
markupSections(program, monitor);
markupFields(program, monitor);
markupMethods(program, monitor);
markupImTables(program, monitor);
markupIMTConflictTables(program, monitor);
markupRuntimeMethods(program, monitor);
markupDexCacheArrays(program, monitor);
markupInternedStrings(program, monitor);
markupClassTables(program, monitor);
}
private void markupSections(Program program, TaskMonitor monitor) throws Exception {
monitor.setMessage("ART - markup sections...");
monitor.setProgress(0);
monitor.setMaximum(sectionList.size());
for (int i = 0; i < sectionList.size(); ++i) {
monitor.checkCanceled();
monitor.incrementProgress(1);
ArtImageSection section = sectionList.get(i);
if (section.getSize() == 0) {
continue;
}
String name = getSectionName(i);
Address address =
program.getMinAddress().getNewAddress(header.getImageBegin() + section.getOffset());
program.getSymbolTable().createLabel(address, name, SourceType.ANALYSIS);
program.getListing()
.setComment(address, CodeUnit.PLATE_COMMENT, "Size: " + section.getSize());
createFragment(program, address, section, name, monitor);
}
}
private void markupFields(Program program, TaskMonitor monitor) throws Exception {
if (get_kSectionArtFields() == UNSUPPORTED_SECTION) {
return;
}
monitor.setMessage("ART - markup fields...");
monitor.setProgress(0);
monitor.setMaximum(fieldList.size());
ArtImageSection section = sectionList.get(get_kSectionArtFields());
if (section.getSize() > 0) {
Address address =
program.getMinAddress().getNewAddress(header.getImageBegin() + section.getOffset());
for (int i = 0; i < fieldList.size(); ++i) {
monitor.checkCanceled();
ArtField field = fieldList.get(i);
DataType dataType = field.toDataType();
program.getListing().createData(address, dataType);
String comment =
"Declaring Class: 0x" + Integer.toHexString(field.getDeclaringClass());
program.getListing().setComment(address, CodeUnit.PLATE_COMMENT, comment);
address = address.add(dataType.getLength());
monitor.incrementProgress(1);
}
}
if (section.getSize() > 0) {
Address address =
program.getMinAddress().getNewAddress(header.getImageBegin() + section.getOffset());
for (int i = 0; i < fieldGroupList.size(); ++i) {
monitor.checkCanceled();
ArtFieldGroup fieldGroup = fieldGroupList.get(i);
DataType dataType = fieldGroup.toDataType();
program.getListing().createData(address, dataType);
if (fieldGroup.getFieldCount() > 0) {
ArtField artField = fieldGroup.getFieldList().get(0);
String comment =
"Declaring Class: 0x" + Integer.toHexString(artField.getDeclaringClass());
program.getListing().setComment(address, CodeUnit.PLATE_COMMENT, comment);
}
address = address.add(dataType.getLength());
monitor.incrementProgress(1);
}
}
}
private void markupMethods(Program program, TaskMonitor monitor) throws Exception {
if (get_kSectionArtMethods() == UNSUPPORTED_SECTION) {
return;
}
monitor.setMessage("ART - markup methods...");
monitor.setProgress(0);
monitor.setMaximum(methodGroupList.size());
ArtImageSection section = sectionList.get(get_kSectionArtMethods());
if (section.getSize() > 0) {
Address address =
program.getMinAddress().getNewAddress(header.getImageBegin() + section.getOffset());
for (int i = 0; i < methodList.size(); ++i) {
monitor.checkCanceled();
ArtMethod method = methodList.get(i);
DataType dataType = method.toDataType();
program.getListing().createData(address, dataType);
String comment =
"Declaring Class: 0x" + Integer.toHexString(method.getDeclaringClass());
program.getListing().setComment(address, CodeUnit.PLATE_COMMENT, comment);
address = address.add(dataType.getLength());
monitor.incrementProgress(1);
}
}
if (section.getSize() > 0) {
Address address =
program.getMinAddress().getNewAddress(header.getImageBegin() + section.getOffset());
for (int i = 0; i < methodGroupList.size(); ++i) {
monitor.checkCanceled();
ArtMethodGroup methodGroup = methodGroupList.get(i);
DataType dataType = methodGroup.toDataType();
program.getListing().createData(address, dataType);
if (methodGroup.getMethodCount() > 0) {
ArtMethod artMethod = methodGroup.getMethodList().get(0);
String comment =
"Declaring Class: 0x" + Integer.toHexString(artMethod.getDeclaringClass());
program.getListing().setComment(address, CodeUnit.PLATE_COMMENT, comment);
}
address = address.add(dataType.getLength());
monitor.incrementProgress(1);
}
}
}
/**
* Interface Methods Tables
*/
private void markupImTables(Program program, TaskMonitor monitor) throws Exception {
if (get_kSectionImTables() == UNSUPPORTED_SECTION) {
return;
}
ArtImageSection section = sectionList.get(get_kSectionImTables());
monitor.setMessage("ART - markup IM tables...");
monitor.setProgress(0);
monitor.setMaximum(section.getSize());
int pointerSize = header.getPointerSize();
if (section.getSize() > 0) {
Address address =
program.getMinAddress().getNewAddress(header.getImageBegin() + section.getOffset());
if (!program.getMemory().contains(address)) {//outside of ART file
return;
}
Address endAddress = address.add(section.getSize());
while (address.compareTo(endAddress) < 0) {
monitor.checkCanceled();
monitor.incrementProgress(pointerSize);
createDataAt(program, address, pointerSize);
address = address.add(pointerSize);
}
}
}
/**
* IMT Conflict Tables
*/
private void markupIMTConflictTables(Program program, TaskMonitor monitor) throws Exception {
if (get_kSectionIMTConflictTables() == UNSUPPORTED_SECTION) {
return;
}
ArtImageSection section = sectionList.get(get_kSectionIMTConflictTables());
monitor.setMessage("ART - markup IMT conflict tables...");
monitor.setProgress(0);
monitor.setMaximum(section.getSize());
int pointerSize = header.getPointerSize();
if (section.getSize() > 0) {
Address address =
program.getMinAddress().getNewAddress(header.getImageBegin() + section.getOffset());
if (!program.getMemory().contains(address)) {//outside of ART file
return;
}
Address endAddress = address.add(section.getSize());
while (address.compareTo(endAddress) < 0) {
monitor.checkCanceled();
monitor.incrementProgress(pointerSize);
createDataAt(program, address, pointerSize);
address = address.add(pointerSize);
}
}
}
private void markupRuntimeMethods(Program program, TaskMonitor monitor) throws Exception {
if (get_kSectionRuntimeMethods() == UNSUPPORTED_SECTION) {
return;
}
ArtImageSection section = sectionList.get(get_kSectionRuntimeMethods());
monitor.setMessage("ART - markup runtime methods...");
monitor.setProgress(0);
monitor.setMaximum(section.getSize());
int pointerSize = header.getPointerSize();
if (section.getSize() > 0) {
Address address =
program.getMinAddress().getNewAddress(header.getImageBegin() + section.getOffset());
if (!program.getMemory().contains(address)) {//outside of ART file
return;
}
Address endAddress = address.add(section.getSize());
while (address.compareTo(endAddress) < 0) {
monitor.checkCanceled();
monitor.incrementProgress(pointerSize);
createDataAt(program, address, pointerSize);
address = address.add(pointerSize);
}
}
}
private void markupDexCacheArrays(Program program, TaskMonitor monitor) throws Exception {
if (get_kSectionDexCacheArrays() == UNSUPPORTED_SECTION) {
return;
}
ArtImageSection section = sectionList.get(get_kSectionDexCacheArrays());
monitor.setMessage("ART - markup dex cache arrays...");
monitor.setProgress(0);
monitor.setMaximum(section.getSize());
if (section.getSize() > 0) {
Address address =
program.getMinAddress().getNewAddress(header.getImageBegin() + section.getOffset());
if (!program.getMemory().contains(address)) {//outside of ART file
return;
}
Address endAddress = address.add(section.getSize());
while (address.compareTo(endAddress) < 0) {
monitor.checkCanceled();
monitor.incrementProgress(4);
createDataAt(program, address, 4);
address = address.add(4);
}
}
}
private void markupInternedStrings(Program program, TaskMonitor monitor) throws Exception {
if (get_kSectionInternedStrings() == UNSUPPORTED_SECTION) {
return;
}
ArtImageSection section = sectionList.get(get_kSectionInternedStrings());
monitor.setMessage("ART - markup interned strings...");
monitor.setProgress(0);
monitor.setMaximum(section.getSize());
if (section.getSize() > 0) {
Address address =
program.getMinAddress().getNewAddress(header.getImageBegin() + section.getOffset());
if (!program.getMemory().contains(address)) {//outside of ART file
return;
}
Address endAddress = address.add(section.getSize());
while (address.compareTo(endAddress) < 0) {
monitor.checkCanceled();
monitor.incrementProgress(4);
createDataAt(program, address, 4);
address = address.add(4);
}
}
}
private void markupClassTables(Program program, TaskMonitor monitor) throws Exception {
if (get_kSectionClassTable() == UNSUPPORTED_SECTION) {
return;
}
ArtImageSection section = sectionList.get(get_kSectionClassTable());
monitor.setMessage("ART - markup class tables...");
monitor.setProgress(0);
monitor.setMaximum(section.getSize());
if (section.getSize() > 0) {
Address address =
program.getMinAddress().getNewAddress(header.getImageBegin() + section.getOffset());
if (!program.getMemory().contains(address)) {//outside of ART file
return;
}
Address endAddress = address.add(section.getSize());
while (address.compareTo(endAddress) < 0) {
monitor.checkCanceled();
monitor.incrementProgress(4);
address = address.add(4);
}
}
}
private void createDataAt(Program program, Address address, int pointerSize) throws Exception {
if (pointerSize == 4) {
program.getListing().createData(address, new DWordDataType());
}
else if (pointerSize == 8) {
program.getListing().createData(address, new QWordDataType());
}
else {
throw new RuntimeException("invalid pointer size");
}
}
private void createFragment(Program program, Address address, ArtImageSection section,
String sectionName, TaskMonitor monitor) {
try {
ProgramModule rootModule = program.getListing().getDefaultRootModule();
ProgramFragment fragment = null;
for (Group group : rootModule.getChildren()) {
if (group.getName().equals(sectionName)) {
fragment = (ProgramFragment) group;
}
}
if (fragment == null) {
fragment = rootModule.createFragment(sectionName);
}
Address endAddress = address.add((Integer.toUnsignedLong(section.getSize()) - 1));
if (sectionList.indexOf(section) == sectionList.size() - 1) {//last section might extend past the end of the program
if (endAddress.compareTo(program.getMaxAddress()) > 0) {
endAddress = program.getMaxAddress();
}
}
fragment.move(address, endAddress);
}
catch (Exception e) {
//ignore...
}
}
}

View File

@@ -0,0 +1,402 @@
/* ###
* 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.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/android10-release/runtime/art_method.h
*
*
*/
public class ArtMethod implements StructConverter {
private final int pointerSize;
private final String artVersion;
private int declaring_class_;
private int access_flags_;
private int dex_code_item_offset_;
private int dex_method_index_;
private short method_index_;
private short hotness_count_;
private short imt_index_;
private short padding_;
private long dex_cache_resolved_methods_;
private long dex_cache_resolved_types_;
private long entry_point_from_interpreter_;
private long entry_point_from_jni_;
private long data_;
private long unknown1_;
private long entry_point_from_quick_compiled_code_;
private int unknown2_;
public ArtMethod(BinaryReader reader, int pointerSize, String artVersion) throws IOException {
this.pointerSize = pointerSize;
this.artVersion = artVersion;
if (ArtConstants.VERSION_MARSHMALLOW_RELEASE.equals(artVersion)) {
if (pointerSize == 4) {
declaring_class_ = reader.readNextInt();
dex_cache_resolved_methods_ = Integer.toUnsignedLong(reader.readNextInt());
dex_cache_resolved_types_ = Integer.toUnsignedLong(reader.readNextInt());
access_flags_ = reader.readNextInt();
dex_code_item_offset_ = reader.readNextInt();
dex_method_index_ = reader.readNextInt();
method_index_ = reader.readNextShort();
padding_ = reader.readNextShort();
entry_point_from_interpreter_ = Integer.toUnsignedLong(reader.readNextInt());
entry_point_from_jni_ = Integer.toUnsignedLong(reader.readNextInt());
entry_point_from_quick_compiled_code_ =
Integer.toUnsignedLong(reader.readNextInt());
}
else if (pointerSize == 8) {
throw new IOException("Unsupported 64-bit ART method format: " + artVersion);
}
}
else if (ArtConstants.VERSION_NOUGAT_RELEASE.equals(artVersion) ||
ArtConstants.VERSION_NOUGAT_MR2_PIXEL_RELEASE.equals(artVersion)) {
if (pointerSize == 4) {
declaring_class_ = reader.readNextInt();
access_flags_ = reader.readNextInt();
dex_code_item_offset_ = reader.readNextInt();
dex_method_index_ = reader.readNextInt();
method_index_ = reader.readNextShort();
hotness_count_ = reader.readNextShort();
dex_cache_resolved_methods_ = Integer.toUnsignedLong(reader.readNextInt());
dex_cache_resolved_types_ = Integer.toUnsignedLong(reader.readNextInt());
entry_point_from_jni_ = Integer.toUnsignedLong(reader.readNextInt());
entry_point_from_quick_compiled_code_ =
Integer.toUnsignedLong(reader.readNextInt());
}
else if (pointerSize == 8) {
declaring_class_ = reader.readNextInt();
access_flags_ = reader.readNextInt();
dex_code_item_offset_ = reader.readNextInt();
dex_method_index_ = reader.readNextInt();
method_index_ = reader.readNextShort();
hotness_count_ = reader.readNextShort();
imt_index_ = reader.readNextShort();
padding_ = reader.readNextShort();
dex_cache_resolved_methods_ = reader.readNextLong();
dex_cache_resolved_types_ = reader.readNextLong();
entry_point_from_jni_ = reader.readNextLong();
entry_point_from_quick_compiled_code_ = reader.readNextLong();
}
}
else if (ArtConstants.VERSION_OREO_RELEASE.equals(artVersion) ||
ArtConstants.VERSION_OREO_DR1_RELEASE.equals(artVersion) ||
ArtConstants.VERSION_OREO_MR1_RELEASE.equals(artVersion)) {
if (pointerSize == 4) {
declaring_class_ = reader.readNextInt();
access_flags_ = reader.readNextInt();
dex_code_item_offset_ = reader.readNextInt();
dex_method_index_ = reader.readNextInt();
method_index_ = reader.readNextShort();
hotness_count_ = reader.readNextShort();
data_ = reader.readNextLong();
entry_point_from_quick_compiled_code_ =
Integer.toUnsignedLong(reader.readNextInt());
}
else if (pointerSize == 8) {
declaring_class_ = reader.readNextInt();
access_flags_ = reader.readNextInt();
dex_code_item_offset_ = reader.readNextInt();
dex_method_index_ = reader.readNextInt();
method_index_ = reader.readNextShort();
hotness_count_ = reader.readNextShort();
imt_index_ = reader.readNextShort();
padding_ = reader.readNextShort();
data_ = reader.readNextLong();
unknown1_ = reader.readNextLong();
entry_point_from_quick_compiled_code_ = reader.readNextLong();
}
}
else if (ArtConstants.VERSION_PIE_RELEASE.equals(artVersion)) {
declaring_class_ = reader.readNextInt();
access_flags_ = reader.readNextInt();
dex_code_item_offset_ = reader.readNextInt();
dex_method_index_ = reader.readNextInt();
method_index_ = reader.readNextShort();
hotness_count_ = reader.readNextShort();
imt_index_ = reader.readNextShort();
padding_ = reader.readNextShort();
if (pointerSize == 4) {
data_ = Integer.toUnsignedLong(reader.readNextInt());
}
else if (pointerSize == 8) {
data_ = reader.readNextLong();
entry_point_from_quick_compiled_code_ = reader.readNextLong();
}
}
else if (ArtConstants.VERSION_10_RELEASE.equals(artVersion)) {
declaring_class_ = reader.readNextInt();
access_flags_ = reader.readNextInt();
dex_code_item_offset_ = reader.readNextInt();
dex_method_index_ = reader.readNextInt();
method_index_ = reader.readNextShort();
hotness_count_ = reader.readNextShort();
imt_index_ = reader.readNextShort();
padding_ = reader.readNextShort();
if (pointerSize == 4) {
data_ = Integer.toUnsignedLong(reader.readNextInt());
}
else if (pointerSize == 8) {
data_ = reader.readNextLong();
entry_point_from_quick_compiled_code_ = reader.readNextLong();
}
}
else if (ArtConstants.VERSION_11_RELEASE.equals(artVersion)) {
declaring_class_ = reader.readNextInt();
access_flags_ = reader.readNextInt();
dex_code_item_offset_ = reader.readNextInt();
dex_method_index_ = reader.readNextInt();
method_index_ = reader.readNextShort();
hotness_count_ = reader.readNextShort();
imt_index_ = reader.readNextShort();
padding_ = reader.readNextShort();
if (pointerSize == 4) {
data_ = Integer.toUnsignedLong(reader.readNextInt());
}
else if (pointerSize == 8) {
data_ = reader.readNextLong();
entry_point_from_quick_compiled_code_ = reader.readNextLong();
}
}
else {
throw new IOException("Unsupported ART method format: " + artVersion);
}
}
public int getDeclaringClass() {
return declaring_class_;
}
public int getAccessFlags() {
return access_flags_;
}
public int getDexCodeItemOffset() {
return dex_code_item_offset_;
}
public int getDexMethodIndex() {
return dex_method_index_;
}
public short getMethodIndex() {
return method_index_;
}
public short getHotnessCount() {
return hotness_count_;
}
public short getImtIndex() {
return imt_index_;
}
public short getPadding() {
return padding_;
}
public long getData() {
return data_;
}
public long getEntryPointFromInterpreter() {
return entry_point_from_interpreter_;
}
public long getEntryPointFromQuickCompiledCode() {
return entry_point_from_quick_compiled_code_;
}
public long getDexCacheResolvedMethods() {
return dex_cache_resolved_methods_;
}
public long getDexCacheResolvedTypes() {
return dex_cache_resolved_types_;
}
public long getEntryPointFromJNI() {
return entry_point_from_jni_;
}
public long getUnknown1() {
return unknown1_;
}
public int getUnknown2() {
return unknown2_;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
DataType ptr32 = new Pointer32DataType();
DataType ptr64 = new Pointer64DataType();
String name = StructConverterUtil.parseName(ArtMethod.class);
Structure struct = new StructureDataType(name, 0);
struct.setCategoryPath(new CategoryPath("/art"));
if (ArtConstants.VERSION_MARSHMALLOW_RELEASE.equals(artVersion)) {
if (pointerSize == 4) {
struct.add(DWORD, "declaring_class_", null);
struct.add(DWORD, "dex_cache_resolved_methods_", null);
struct.add(DWORD, "dex_cache_resolved_types_", null);
struct.add(DWORD, "access_flags_", null);
struct.add(DWORD, "dex_code_item_offset_", null);
struct.add(DWORD, "dex_method_index_", null);
struct.add(WORD, "method_index_", null);
struct.add(WORD, "padding_", null);
struct.add(DWORD, "entry_point_from_interpreter_", null);
struct.add(DWORD, "entry_point_from_jni_", null);
struct.add(DWORD, "entry_point_from_quick_compiled_code_", null);
}
else if (pointerSize == 8) {
throw new IOException("Unsupported 64-bit ART method format: " + artVersion);
}
}
else if (ArtConstants.VERSION_NOUGAT_RELEASE.equals(artVersion) ||
ArtConstants.VERSION_NOUGAT_MR2_PIXEL_RELEASE.equals(artVersion)) {
if (pointerSize == 4) {
struct.add(ptr32, "declaring_class_", null);
struct.add(DWORD, "access_flags_", null);
struct.add(DWORD, "dex_code_item_offset_", null);
struct.add(DWORD, "dex_method_index_", null);
struct.add(WORD, "method_index_", null);
struct.add(WORD, "hotness_count_", null);
struct.add(DWORD, "dex_cache_resolved_methods_", null);
struct.add(DWORD, "dex_cache_resolved_types_", null);
struct.add(ptr32, "entry_point_from_jni_", null);
struct.add(ptr32, "entry_point_from_quick_compiled_code_", null);
}
else if (pointerSize == 8) {
struct.add(ptr32, "declaring_class_", null);
struct.add(DWORD, "access_flags_", null);
struct.add(DWORD, "dex_code_item_offset_", null);
struct.add(DWORD, "dex_method_index_", null);
struct.add(WORD, "method_index_", null);
struct.add(WORD, "hotness_count_", null);
struct.add(WORD, "imt_index_", null);
struct.add(WORD, "padding", null);
struct.add(QWORD, "dex_cache_resolved_methods_", null);
struct.add(QWORD, "dex_cache_resolved_types_", null);
struct.add(ptr64, "entry_point_from_jni_", null);
struct.add(ptr64, "entry_point_from_quick_compiled_code_", null);
}
}
else if (ArtConstants.VERSION_OREO_RELEASE.equals(artVersion) ||
ArtConstants.VERSION_OREO_DR1_RELEASE.equals(artVersion) ||
ArtConstants.VERSION_OREO_MR1_RELEASE.equals(artVersion)) {
if (pointerSize == 4) {
struct.add(ptr32, "declaring_class_", null);
struct.add(DWORD, "access_flags_", null);
struct.add(DWORD, "dex_code_item_offset_", null);
struct.add(DWORD, "dex_method_index_", null);
struct.add(WORD, "method_index_", null);
struct.add(WORD, "hotness_count_", null);
struct.add(QWORD, "data", null);
struct.add(ptr32, "entry_point_from_quick_compiled_code_", null);
}
else if (pointerSize == 8) {
struct.add(ptr32, "declaring_class_", null);
struct.add(DWORD, "access_flags_", null);
struct.add(DWORD, "dex_code_item_offset_", null);
struct.add(DWORD, "dex_method_index_", null);
struct.add(WORD, "method_index_", null);
struct.add(WORD, "hotness_count_", null);
struct.add(WORD, "imt_index_", null);
struct.add(WORD, "padding", null);
struct.add(QWORD, "data", null);
struct.add(QWORD, "unknown1_", null);
struct.add(ptr64, "entry_point_from_quick_compiled_code_", null);
}
}
else if (ArtConstants.VERSION_PIE_RELEASE.equals(artVersion)) {
struct.add(ptr32, "declaring_class_", null);
struct.add(DWORD, "access_flags_", null);
struct.add(DWORD, "dex_code_item_offset_", null);
struct.add(DWORD, "dex_method_index_", null);
struct.add(WORD, "method_index_", null);
struct.add(WORD, "hotness_count_", null);
struct.add(WORD, "imt_index_", null);
struct.add(WORD, "padding", null);
if (pointerSize == 4) {
struct.add(DWORD, "data", null);
}
else if (pointerSize == 8) {
struct.add(QWORD, "data", null);
struct.add(QWORD, "entry_point_from_quick_compiled_code_", null);
}
}
else if (ArtConstants.VERSION_10_RELEASE.equals(artVersion)) {
struct.add(ptr32, "declaring_class_", null);
struct.add(DWORD, "access_flags_", null);
struct.add(DWORD, "dex_code_item_offset_", null);
struct.add(DWORD, "dex_method_index_", null);
struct.add(WORD, "method_index_", null);
struct.add(WORD, "hotness_count_", null);
struct.add(WORD, "imt_index_", null);
struct.add(WORD, "padding", null);
if (pointerSize == 4) {
struct.add(DWORD, "data", null);
}
else if (pointerSize == 8) {
struct.add(QWORD, "data", null);
struct.add(ptr64, "entry_point_from_quick_compiled_code_", null);
}
}
else if (ArtConstants.VERSION_11_RELEASE.equals(artVersion)) {
struct.add(ptr32, "declaring_class_", null);
struct.add(DWORD, "access_flags_", null);
struct.add(DWORD, "dex_code_item_offset_", null);
struct.add(DWORD, "dex_method_index_", null);
struct.add(WORD, "method_index_", null);
struct.add(WORD, "hotness_count_", null);
struct.add(WORD, "imt_index_", null);
struct.add(WORD, "padding", null);
if (pointerSize == 4) {
struct.add(DWORD, "data", null);
}
else if (pointerSize == 8) {
struct.add(QWORD, "data", null);
struct.add(QWORD, "entry_point_from_quick_compiled_code_", null);
}
}
else {
throw new IOException("Unsupported ART method format: " + artVersion);
}
return struct;
}
}

View File

@@ -0,0 +1,83 @@
/* ###
* 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 java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/android10-release/runtime/art_method.h
*
* NOTE: this class does not exist, was created to make method reading easier.
*/
public class ArtMethodGroup implements StructConverter {
private int pointerSize;
private long methodCount;
private List<ArtMethod> methodList = new ArrayList<>();
public ArtMethodGroup(BinaryReader reader, int pointerSize, String artVersion)
throws IOException {
this.pointerSize = pointerSize;
if (pointerSize == 8) {
methodCount = reader.readNextLong();
}
else if (pointerSize == 4) {
methodCount = Integer.toUnsignedLong(reader.readNextInt());
}
if (methodCount > 0xffff) {//sanity check...
throw new IOException("Too many ART methods: " + methodCount);
}
for (int i = 0; i < methodCount; ++i) {
methodList.add(new ArtMethod(reader, pointerSize, artVersion));
}
}
public long getMethodCount() {
return methodCount;
}
public List<ArtMethod> getMethodList() {
return methodList;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = StructConverterUtil.parseName(ArtMethodGroup.class);
Structure structure = new StructureDataType(name + "_" + methodCount, 0);
structure.setCategoryPath(new CategoryPath("/art"));
if (pointerSize == 8) {
structure.add(QWORD, "methodCount", null);
}
else if (pointerSize == 4) {
structure.add(DWORD, "methodCount", null);
}
for (int i = 0; i < methodCount; ++i) {
structure.add(methodList.get(i).toDataType(), "method_" + i, null);
}
return structure;
}
}

View File

@@ -0,0 +1,36 @@
/* ###
* 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;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/pie-release/runtime/image.h
*/
public enum ArtStorageMode {
kStorageModeUncompressed, kStorageModeLZ4, kStorageModeLZ4HC, kStorageModeCount; // Number of elements in enum.
public final static ArtStorageMode kDefaultStorageMode = kStorageModeUncompressed;
public final static int SIZE = 32;//bits
public static ArtStorageMode get(int value) throws UnknownArtStorageModeException {
for (ArtStorageMode mode : values()) {
if (mode.ordinal() == value) {
return mode;
}
}
throw new UnknownArtStorageModeException(value);
}
}

View File

@@ -0,0 +1,68 @@
/* ###
* 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.math.BigInteger;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
public final class ArtUtilities {
static void createFragment(Program program, String fragmentName, Address start,
Address end) throws Exception {
ProgramModule module = program.getListing().getRootModule(0);
ProgramFragment fragment = getFragment(module, fragmentName);
if (fragment == null) {
fragment = module.createFragment(fragmentName);
}
fragment.move(start, end.subtract(1));
}
static ProgramFragment getFragment(ProgramModule module, String fragmentName) {
Group[] groups = module.getChildren();
for (Group group : groups) {
if (group.getName().equals(fragmentName)) {
return (ProgramFragment) group;
}
}
return null;
}
public static Address adjustForThumbAsNeeded(ArtHeader artHeader, Program program,
Address address) {
long displacement = address.getOffset();
if (program.getLanguage()
.getProcessor()
.equals(Processor.findOrPossiblyCreateProcessor("ARM"))) {
if ((displacement & 0x1) == 0x1) {//thumb code?
address = address.subtract(1);
Register register = program.getLanguage().getRegister("TMode");
RegisterValue value = new RegisterValue(register, BigInteger.valueOf(1));
try {
program.getProgramContext().setRegisterValue(address, address, value);
}
catch (ContextChangeException e) {
//log.appendException( e );
//ignore...
}
}
}
return address;
}
}

View File

@@ -0,0 +1,26 @@
/* ###
* 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;
public class UnknownArtStorageModeException extends IOException {
public UnknownArtStorageModeException(int storageMode) {
super("Unrecognized storage mode: 0x" + Integer.toHexString(storageMode));
}
}

View File

@@ -0,0 +1,23 @@
/* ###
* 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;
final class UnsupportedArtVersionException extends Exception {
UnsupportedArtVersionException(String magic, String version) {
super("Unsupported ART version: " + version);
}
}

View File

@@ -0,0 +1,234 @@
/* ###
* 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.android10;
import java.io.IOException;
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;
import ghidra.program.model.data.Structure;
import ghidra.program.model.listing.Program;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/android10-release/runtime/image.h
*/
public class ArtHeader_10 extends ArtHeader {
private int image_reservation_size_;
private int component_count_;
private int image_begin_;
private int image_size_;
private int image_checksum_;
private int oat_checksum_;
private int oat_file_begin_;
private int oat_data_begin_;
private int oat_data_end_;
private int oat_file_end_;
private int boot_image_begin_;
private int boot_image_size_;
private int image_roots_;
private int pointer_size_;
private long[] image_methods_ = new long[ImageMethod_10.kImageMethodsCount.ordinal()];
private int data_size_;
private int blocks_offset_;
private int blocks_count_;
private List<ArtBlock> blocks = new ArrayList<>();
private ArtImageSections sections;
public ArtHeader_10(BinaryReader reader) throws IOException {
super(reader);
parse(reader);
}
@Override
protected void parse(BinaryReader reader) throws IOException {
image_reservation_size_ = reader.readNextInt();
component_count_ = reader.readNextInt();
image_begin_ = reader.readNextInt();
image_size_ = reader.readNextInt();
image_checksum_ = reader.readNextInt();
oat_checksum_ = reader.readNextInt();
oat_file_begin_ = reader.readNextInt();
oat_data_begin_ = reader.readNextInt();
oat_data_end_ = reader.readNextInt();
oat_file_end_ = reader.readNextInt();
boot_image_begin_ = reader.readNextInt();
boot_image_size_ = reader.readNextInt();
image_roots_ = reader.readNextInt();
pointer_size_ = reader.readNextInt();
sections = new ImageSections_10(reader, this);
sections.parseSections(reader);
parseImageMethods(reader);
data_size_ = reader.readNextInt();
blocks_offset_ = reader.readNextInt();
blocks_count_ = reader.readNextInt();
if (blocks_offset_ > 0 && blocks_count_ > 0) {
reader.setPointerIndex(blocks_offset_);
for (int i = 0; i < blocks_count_; ++i) {
blocks.add(new ArtBlock(reader));
}
}
reader = DecompressionManager.decompress(reader, blocks, TaskMonitor.DUMMY);
// NOTE:
// cannot parse the sections until after the blocks are decompressed!
sections.parse(reader);
}
@Override
public int getArtMethodCountForVersion() {
return ImageMethod_10.kImageMethodsCount.ordinal();
}
@Override
public int getImageBegin() {
return image_begin_;
}
@Override
public int getImageSize() {
return image_size_;
}
public int getImageChecksum_() {
return image_checksum_;
}
@Override
public int getOatChecksum() {
return oat_checksum_;
}
@Override
public int getOatDataBegin() {
return oat_data_begin_;
}
@Override
public int getOatDataEnd() {
return oat_data_end_;
}
@Override
public int getOatFileBegin() {
return oat_file_begin_;
}
@Override
public int getOatFileEnd() {
return oat_file_end_;
}
@Override
public int getPointerSize() {
return pointer_size_;
}
public int getBootImageBegin() {
return boot_image_begin_;
}
/**
* 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() {
return boot_image_size_ != 0x0;
}
public int getImageReservationSize() {
return image_reservation_size_;
}
public int getComponentCount() {
return component_count_;
}
public int getImageRoots() {
return image_roots_;
}
public int getDataSize() {
return data_size_;
}
public List<ArtBlock> getBlocks() {
return blocks;
}
@Override
public void markup(Program program, TaskMonitor monitor) throws Exception {
DecompressionManager.decompressOverMemory(program, blocks, monitor);
sections.markup(program, monitor);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_10.class);
try {
structure.setName(className);
}
catch (InvalidNameException e) {
//ignore, just use original name should this fail
}
structure.add(DWORD, "image_reservation_size_", null);
structure.add(DWORD, "component_count_", null);
structure.add(DWORD, "image_begin_", null);
structure.add(DWORD, "image_size_", null);
structure.add(DWORD, "image_checksum_", null);
structure.add(DWORD, "oat_checksum_", null);
structure.add(DWORD, "oat_file_begin_", null);
structure.add(DWORD, "oat_data_begin_", null);
structure.add(DWORD, "oat_data_end_", null);
structure.add(DWORD, "oat_file_end_", null);
structure.add(DWORD, "boot_image_begin_", null);
structure.add(DWORD, "boot_image_size_", null);
structure.add(DWORD, "image_roots_", null);
structure.add(DWORD, "pointer_size_", null);
for (int i = 0; i < sections.getSectionList().size(); ++i) {
structure.add(sections.getSectionList().get(i).toDataType(), "section_" + i, null);
}
for (int i = 0; i < image_methods_.length; ++i) {
structure.add(QWORD, "image_method_" + i, null);
}
structure.add(DWORD, "data_size_", null);
structure.add(DWORD, "blocks_offset_", null);
structure.add(DWORD, "blocks_count_", null);
return structure;
}
}

View File

@@ -0,0 +1,32 @@
/* ###
* 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.android10;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/android10-release/runtime/image.h
*/
public enum ImageMethod_10 {
kResolutionMethod,
kImtConflictMethod,
kImtUnimplementedMethod,
kSaveAllCalleeSavesMethod,
kSaveRefsOnlyMethod,
kSaveRefsAndArgsMethod,
kSaveEverythingMethod,
kSaveEverythingMethodForClinit,
kSaveEverythingMethodForSuspendCheck,
kImageMethodsCount, // Number of elements in enum.
}

View File

@@ -0,0 +1,43 @@
/* ###
* 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.android10;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/android10-release/runtime/image.h
*/
public enum ImageRoot_10 {
kDexCaches,
kClassRoots,
/** Pre-allocated OOME when throwing exception.*/
kOomeWhenThrowingException,
/** Pre-allocated OOME when throwing OOME. */
kOomeWhenThrowingOome,
/** Pre-allocated OOME when handling StackOverflowError. */
kOomeWhenHandlingStackOverflow,
/** Pre-allocated NoClassDefFoundError. */
kNoClassDefFoundError,
/** Different for boot image and app image, see aliases below. */
kSpecialRoots,
kImageRootsMax;
//Aliases
/** The class loader used to build the app image.*/
public final static ImageRoot_10 kAppImageClassLoader = kSpecialRoots;
/** Array of boot image objects that must be kept live. */
public final static ImageRoot_10 kBootImageLiveObjects = kSpecialRoots;
}

View File

@@ -0,0 +1,109 @@
/* ###
* 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.android10;
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/android10-release/runtime/image.h
*/
public class ImageSections_10 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 kSectionDexCacheArrays = 6;
public final static int kSectionInternedStrings = 7;
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_10(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 kSectionDexCacheArrays;
}
@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;
}
}

View File

@@ -0,0 +1,250 @@
/* ###
* 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.android11;
import java.io.IOException;
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;
import ghidra.program.model.listing.Program;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/android10-release/runtime/image.h
*/
public class ArtHeader_11 extends ArtHeader {
private int image_reservation_size_;
private int component_count_;
private int image_begin_;
private int image_size_;
private int image_checksum_;
private int oat_checksum_;
private int oat_file_begin_;
private int oat_data_begin_;
private int oat_data_end_;
private int oat_file_end_;
private int boot_image_begin_;
private int boot_image_size_;
private int boot_image_component_count_;
private int boot_image_checksum_;
private int image_roots_;
private int pointer_size_;
private long[] image_methods_ = new long[ImageMethod_10.kImageMethodsCount.ordinal()];
private int data_size_;
private int blocks_offset_;
private int blocks_count_;
private List<ArtBlock> blocks = new ArrayList<>();
private ArtImageSections sections;
public ArtHeader_11(BinaryReader reader) throws IOException {
super(reader);
parse(reader);
}
@Override
protected void parse(BinaryReader reader) throws IOException {
image_reservation_size_ = reader.readNextInt();
component_count_ = reader.readNextInt();
image_begin_ = reader.readNextInt();
image_size_ = reader.readNextInt();
image_checksum_ = reader.readNextInt();
oat_checksum_ = reader.readNextInt();
oat_file_begin_ = reader.readNextInt();
oat_data_begin_ = reader.readNextInt();
oat_data_end_ = reader.readNextInt();
oat_file_end_ = reader.readNextInt();
boot_image_begin_ = reader.readNextInt();
boot_image_size_ = reader.readNextInt();
boot_image_component_count_ = reader.readNextInt();
boot_image_checksum_ = reader.readNextInt();
image_roots_ = reader.readNextInt();
pointer_size_ = reader.readNextInt();
sections = new ImageSections_10(reader, this);
sections.parseSections(reader);
parseImageMethods(reader);
data_size_ = reader.readNextInt();
blocks_offset_ = reader.readNextInt();
blocks_count_ = reader.readNextInt();
if (blocks_offset_ > 0 && blocks_count_ > 0) {
reader.setPointerIndex(blocks_offset_);
for (int i = 0; i < blocks_count_; ++i) {
blocks.add(new ArtBlock(reader));
}
}
reader = DecompressionManager.decompress(reader, blocks, TaskMonitor.DUMMY);
// NOTE:
// cannot parse the sections until after the blocks are decompressed!
sections.parse(reader);
}
@Override
public int getArtMethodCountForVersion() {
return ImageMethod_10.kImageMethodsCount.ordinal();
}
@Override
public int getImageBegin() {
return image_begin_;
}
@Override
public int getImageSize() {
return image_size_;
}
public int getImageChecksum_() {
return image_checksum_;
}
@Override
public int getOatChecksum() {
return oat_checksum_;
}
@Override
public int getOatDataBegin() {
return oat_data_begin_;
}
@Override
public int getOatDataEnd() {
return oat_data_end_;
}
@Override
public int getOatFileBegin() {
return oat_file_begin_;
}
@Override
public int getOatFileEnd() {
return oat_file_end_;
}
@Override
public int getPointerSize() {
return pointer_size_;
}
public int getBootImageBegin() {
return boot_image_begin_;
}
public int getBootImageComponentCount() {
return boot_image_component_count_;
}
public int getBootImageChecksum() {
return boot_image_checksum_;
}
/**
* App images currently require a boot image,
* if the size is non zero then it is an app image header.
* @return true if app image
*/
public boolean isAppImage() {
return boot_image_size_ != 0x0;
}
public int getImageReservationSize() {
return image_reservation_size_;
}
public int getComponentCount() {
return component_count_;
}
public int getImageRoots() {
return image_roots_;
}
public int getDataSize() {
return data_size_;
}
public List<ArtBlock> getBlocks() {
return blocks;
}
@Override
public void markup(Program program, TaskMonitor monitor) throws Exception {
DecompressionManager.decompressOverMemory(program, blocks, monitor);
sections.markup(program, monitor);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_11.class);
try {
structure.setName(className);
}
catch (InvalidNameException e) {
//ignore
}
structure.add(DWORD, "image_reservation_size_", null);
structure.add(DWORD, "component_count_", null);
structure.add(DWORD, "image_begin_", null);
structure.add(DWORD, "image_size_", null);
structure.add(DWORD, "image_checksum_", null);
structure.add(DWORD, "oat_checksum_", null);
structure.add(DWORD, "oat_file_begin_", null);
structure.add(DWORD, "oat_data_begin_", null);
structure.add(DWORD, "oat_data_end_", null);
structure.add(DWORD, "oat_file_end_", null);
structure.add(DWORD, "boot_image_begin_", null);
structure.add(DWORD, "boot_image_size_", null);
structure.add(DWORD, "boot_image_component_count_", null);
structure.add(DWORD, "boot_image_checksum_", null);
structure.add(DWORD, "image_roots_", null);
structure.add(DWORD, "pointer_size_", null);
for (int i = 0; i < sections.getSectionList().size(); ++i) {
structure.add(sections.getSectionList().get(i).toDataType(), "section_" + i, null);
}
for (int i = 0; i < image_methods_.length; ++i) {
structure.add(QWORD, "image_method_" + i, null);
}
structure.add(DWORD, "data_size_", null);
structure.add(DWORD, "blocks_offset_", null);
structure.add(DWORD, "blocks_count_", null);
return structure;
}
}

View File

@@ -0,0 +1,34 @@
/* ###
* 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.android11;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/android11-release/runtime/image.h
*/
public enum BootImageLiveObjects {
/** Pre-allocated OOME when throwing exception. */
kOomeWhenThrowingException,
/** Pre-allocated OOME when throwing OOME. */
kOomeWhenThrowingOome,
/** Pre-allocated OOME when handling StackOverflowError. */
kOomeWhenHandlingStackOverflow,
/** Pre-allocated NoClassDefFoundError. */
kNoClassDefFoundError,
/** Pre-allocated sentinel for cleared weak JNI references. */
kClearedJniWeakSentinel,
kIntrinsicObjectsStart;
}

View File

@@ -0,0 +1,31 @@
/* ###
* 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.android11;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/android11-release/runtime/image.h
*/
public enum ImageRoot_11 {
kDexCaches, kClassRoots, kSpecialRoots, kImageRootsMax;
//Aliases
/** The class loader used to build the app image.*/
public final static ImageRoot_11 kAppImageClassLoader = kSpecialRoots;
/** Array of boot image objects that must be kept live. */
public final static ImageRoot_11 kBootImageLiveObjects = kSpecialRoots;
}

View File

@@ -0,0 +1,176 @@
/* ###
* 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.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;
import ghidra.program.model.listing.Program;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* @see https://android.googlesource.com/platform/art/+/refs/heads/kitkat-release/runtime/image.cc
*/
public class ArtHeader_KitKat extends ArtHeader {
protected int image_begin_;
protected int image_size_;
protected int image_bitmap_offset_;
protected int image_bitmap_size_;
protected int oat_checksum_;
protected int oat_file_begin_;
protected int oat_data_begin_;
protected int oat_data_end_;
protected int oat_file_end_;
protected int image_roots_;
public ArtHeader_KitKat(BinaryReader reader) throws IOException {
super(reader);
parse(reader);
}
@Override
protected void parse(BinaryReader reader) throws IOException {
image_begin_ = reader.readNextInt();
image_size_ = reader.readNextInt();
image_bitmap_offset_ = reader.readNextInt();
image_bitmap_size_ = reader.readNextInt();
oat_checksum_ = reader.readNextInt();
oat_file_begin_ = reader.readNextInt();
oat_data_begin_ = reader.readNextInt();
oat_data_end_ = reader.readNextInt();
oat_file_end_ = reader.readNextInt();
image_roots_ = reader.readNextInt();
}
@Override
public int getImageBegin() {
return image_begin_;
}
@Override
public int getImageSize() {
return image_size_;
}
/**
* Image bitmap offset in the file.
*/
public int getImageBitmapOffset() {
return image_bitmap_offset_;
}
/**
* Size of the image bitmap.
*/
public int getImageBitmapSize() {
return image_bitmap_size_;
}
/**
* Checksum of the oat file we link to for load time sanity check.
*/
public int getOatChecksum() {
return oat_checksum_;
}
/**
* Start address for oat file. Will be before oat_data_begin_ for .so files.
*/
@Override
public int getOatFileBegin() {
return oat_file_begin_;
}
/**
* Required oat address expected by image Method::GetCode() pointers.
*/
@Override
public int getOatDataBegin() {
return oat_data_begin_;
}
/**
* End of oat data address range for this image file.
*/
@Override
public int getOatDataEnd() {
return oat_data_end_;
}
/**
* End of oat file address range. will be after oat_data_end_ for
* .so files. Used for positioning a following alloc spaces.
*/
@Override
public int getOatFileEnd() {
return oat_file_end_;
}
/**
* Absolute address of an Object[] of objects needed to reinitialize from an image.
*/
public int getImageRoots() {
return image_roots_;
}
@Override
public int getPointerSize() {
return -1; //unsupported
}
@Override
public void markup(Program program, TaskMonitor monitor) throws Exception {
//do nothing for now
}
@Override
public int getArtMethodCountForVersion() {
throw new UnsupportedOperationException();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_KitKat.class);
try {
structure.setName(className);
}
catch (InvalidNameException e) {
}
structure.add(DWORD, "image_begin_", null);
structure.add(DWORD, "image_size_", null);
structure.add(DWORD, "image_bitmap_offset_", null);
structure.add(DWORD, "image_bitmap_size_", null);
structure.add(DWORD, "oat_checksum_", null);
structure.add(DWORD, "oat_file_begin_", null);
structure.add(DWORD, "oat_data_begin_", null);
structure.add(DWORD, "oat_data_end_", null);
structure.add(DWORD, "oat_file_end_", null);
structure.add(DWORD, "image_roots_", null);
return structure;
}
}

View File

@@ -0,0 +1,30 @@
/* ###
* 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.kitkat;
/**
* @see https://android.googlesource.com/platform/art/+/refs/heads/kitkat-release/runtime/image.h
*/
public enum ImageRoot_KitKat {
kResolutionMethod,
kCalleeSaveMethod,
kRefsOnlySaveMethod,
kRefsAndArgsSaveMethod,
kOatLocation,
kDexCaches,
kClassRoots,
kImageRootsMax,
}

View File

@@ -0,0 +1,153 @@
/* ###
* 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.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;
import ghidra.program.model.listing.Program;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* @see https://android.googlesource.com/platform/art/+/refs/heads/lollipop-release/runtime/image.cc
*/
public class ArtHeader_Lollipop extends ArtHeader {
protected int image_begin_;
protected int image_size_;
protected int image_bitmap_offset_;
protected int image_bitmap_size_;
protected int oat_checksum_;
protected int oat_file_begin_;
protected int oat_data_begin_;
protected int oat_data_end_;
protected int oat_file_end_;
protected int patch_delta_;
protected int image_roots_;
public ArtHeader_Lollipop(BinaryReader reader) throws IOException {
super(reader);
parse(reader);
}
@Override
protected void parse(BinaryReader reader) throws IOException {
image_begin_ = reader.readNextInt();
image_size_ = reader.readNextInt();
image_bitmap_offset_ = reader.readNextInt();
image_bitmap_size_ = reader.readNextInt();
oat_checksum_ = reader.readNextInt();
oat_file_begin_ = reader.readNextInt();
oat_data_begin_ = reader.readNextInt();
oat_data_end_ = reader.readNextInt();
oat_file_end_ = reader.readNextInt();
patch_delta_ = reader.readNextInt();
image_roots_ = reader.readNextInt();
}
@Override
public int getImageBegin() {
return image_begin_;
}
@Override
public int getImageSize() {
return image_size_;
}
/**
* The total delta that this image has been patched.
*/
public int getPatchDelta() {
return patch_delta_;
}
/**
* Checksum of the oat file we link to for load time sanity check.
*/
@Override
public int getOatChecksum() {
return oat_checksum_;
}
@Override
public int getPointerSize() {
return -1; //unsupported
}
@Override
public int getOatFileBegin() {
return oat_file_begin_;
}
@Override
public int getOatFileEnd() {
return oat_file_end_;
}
@Override
public int getOatDataBegin() {
return oat_data_begin_;
}
@Override
public int getOatDataEnd() {
return oat_data_end_;
}
@Override
public void markup(Program program, TaskMonitor monitor) throws Exception {
//do nothing for now
}
@Override
public int getArtMethodCountForVersion() {
throw new UnsupportedOperationException();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_Lollipop.class);
try {
structure.setName(className);
}
catch (InvalidNameException e) {
}
structure.add(DWORD, "image_begin_", null);
structure.add(DWORD, "image_size_", null);
structure.add(DWORD, "image_bitmap_offset_", null);
structure.add(DWORD, "image_bitmap_size_", null);
structure.add(DWORD, "oat_checksum_", null);
structure.add(DWORD, "oat_file_begin_", null);
structure.add(DWORD, "oat_data_begin_", null);
structure.add(DWORD, "oat_data_end_", null);
structure.add(DWORD, "oat_file_end_", null);
structure.add(DWORD, "patch_delta_", null);
structure.add(DWORD, "image_roots_", null);
return structure;
}
}

View File

@@ -0,0 +1,153 @@
/* ###
* 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.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;
import ghidra.program.model.listing.Program;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* @see https://android.googlesource.com/platform/art/+/lollipop-mr1-wfc-release/runtime/image.h
*/
public class ArtHeader_LollipopMR1WFC extends ArtHeader {
protected int image_begin_;
protected int image_size_;
protected int image_bitmap_offset_;
protected int image_bitmap_size_;
protected int oat_checksum_;
protected int oat_file_begin_;
protected int oat_data_begin_;
protected int oat_data_end_;
protected int oat_file_end_;
protected int patch_delta_;
protected int image_roots_;
protected int compile_pic_;
public ArtHeader_LollipopMR1WFC(BinaryReader reader) throws IOException {
super(reader);
parse(reader);
}
@Override
protected void parse(BinaryReader reader) throws IOException {
image_begin_ = reader.readNextInt();
image_size_ = reader.readNextInt();
image_bitmap_offset_ = reader.readNextInt();
image_bitmap_size_ = reader.readNextInt();
oat_checksum_ = reader.readNextInt();
oat_file_begin_ = reader.readNextInt();
oat_data_begin_ = reader.readNextInt();
oat_data_end_ = reader.readNextInt();
oat_file_end_ = reader.readNextInt();
patch_delta_ = reader.readNextInt();
image_roots_ = reader.readNextInt();
compile_pic_ = reader.readNextInt();
}
@Override
public int getImageBegin() {
return image_begin_;
}
@Override
public int getImageSize() {
return image_size_;
}
/**
* Boolean (0 or 1) to denote if the image was compiled with --compile-pic option.
*/
public int getCompilePic() {
return compile_pic_;
}
@Override
public int getOatChecksum() {
return oat_checksum_;
}
@Override
public int getPointerSize() {
return -1; //unsupported
}
@Override
public int getOatFileBegin() {
return oat_file_begin_;
}
@Override
public int getOatFileEnd() {
return oat_file_end_;
}
@Override
public int getOatDataBegin() {
return oat_data_begin_;
}
@Override
public int getOatDataEnd() {
return oat_data_end_;
}
@Override
public void markup(Program program, TaskMonitor monitor) throws Exception {
}
@Override
public int getArtMethodCountForVersion() {
throw new UnsupportedOperationException();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_LollipopMR1WFC.class);
try {
structure.setName(className);
}
catch (InvalidNameException e) {
}
structure.add(DWORD, "image_begin_", null);
structure.add(DWORD, "image_size_", null);
structure.add(DWORD, "image_bitmap_offset_", null);
structure.add(DWORD, "image_bitmap_size_", null);
structure.add(DWORD, "oat_checksum_", null);
structure.add(DWORD, "oat_file_begin_", null);
structure.add(DWORD, "oat_data_begin_", null);
structure.add(DWORD, "oat_data_end_", null);
structure.add(DWORD, "oat_file_end_", null);
structure.add(DWORD, "patch_delta_", null);
structure.add(DWORD, "image_roots_", null);
structure.add(DWORD, "compile_pic_", null);
return structure;
}
}

View File

@@ -0,0 +1,31 @@
/* ###
* 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.lollipop;
/**
* @see https://android.googlesource.com/platform/art/+/refs/heads/lollipop-release/runtime/image.h
*/
public enum ImageRoot_Lollipop {
kResolutionMethod,
kImtConflictMethod,
kDefaultImt,
kCalleeSaveMethod,
kRefsOnlySaveMethod,
kRefsAndArgsSaveMethod,
kDexCaches,
kClassRoots,
kImageRootsMax,
}

View File

@@ -0,0 +1,32 @@
/* ###
* 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.lollipop;
/**
* @see https://android.googlesource.com/platform/art/+/lollipop-mr1-wfc-release/runtime/image.h
*/
public enum ImageRoot_LollipopMR1WRC {
kResolutionMethod,
kImtConflictMethod,
kImtUnimplementedMethod,
kDefaultImt,
kCalleeSaveMethod,
kRefsOnlySaveMethod,
kRefsAndArgsSaveMethod,
kDexCaches,
kClassRoots,
kImageRootsMax,
}

View File

@@ -0,0 +1,175 @@
/* ###
* 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.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.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.listing.Program;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* @see https://android.googlesource.com/platform/art/+/marshmallow-release/runtime/image.h
*/
public class ArtHeader_Marshmallow extends ArtHeader {
protected int image_begin_;
protected int image_size_;
protected int oat_checksum_;
protected int oat_file_begin_;
protected int oat_data_begin_;
protected int oat_data_end_;
protected int oat_file_end_;
protected int patch_delta_;
protected int image_roots_;
protected int pointer_size_;
protected int compile_pic_;
protected long[] image_methods_ =
new long[ImageMethod_Marshmallow.kImageMethodsCount.ordinal()];
protected ArtImageSections sections;
public ArtHeader_Marshmallow(BinaryReader reader) throws IOException {
super(reader);
parse(reader);
}
@Override
protected void parse(BinaryReader reader) throws IOException {
image_begin_ = reader.readNextInt();
image_size_ = reader.readNextInt();
oat_checksum_ = reader.readNextInt();
oat_file_begin_ = reader.readNextInt();
oat_data_begin_ = reader.readNextInt();
oat_data_end_ = reader.readNextInt();
oat_file_end_ = reader.readNextInt();
patch_delta_ = reader.readNextInt();
image_roots_ = reader.readNextInt();
pointer_size_ = reader.readNextInt();
compile_pic_ = reader.readNextInt();
sections = new ImageSections_Marshmallow(reader, this);
sections.parseSections(reader);
for (int i = 0; i < image_methods_.length; ++i) {
image_methods_[i] = reader.readNextLong();
}
sections.parse(reader);
}
@Override
public int getImageBegin() {
return image_begin_;
}
@Override
public int getImageSize() {
return image_size_;
}
@Override
public int getPointerSize() {
return pointer_size_;
}
/**
* Image methods.
*/
public long[] getImageMethods() {
return image_methods_;
}
/**
* Checksum of the oat file we link to for load time sanity check.
*/
@Override
public int getOatChecksum() {
return oat_checksum_;
}
@Override
public int getOatFileBegin() {
return oat_file_begin_;
}
@Override
public int getOatFileEnd() {
return oat_file_end_;
}
@Override
public int getOatDataBegin() {
return oat_data_begin_;
}
@Override
public int getOatDataEnd() {
return oat_data_end_;
}
@Override
public void markup(Program program, TaskMonitor monitor) throws Exception {
sections.markup(program, monitor);
}
@Override
public int getArtMethodCountForVersion() {
throw new UnsupportedOperationException();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_Marshmallow.class);
try {
structure.setName(className);
}
catch (InvalidNameException e) {
}
structure.add(DWORD, "image_begin_", null);
structure.add(DWORD, "image_size_", null);
structure.add(DWORD, "oat_checksum_", null);
structure.add(DWORD, "oat_file_begin_", null);
structure.add(DWORD, "oat_data_begin_", null);
structure.add(DWORD, "oat_data_end_", null);
structure.add(DWORD, "oat_file_end_", null);
structure.add(DWORD, "patch_delta_", null);
structure.add(DWORD, "image_roots_", null);
structure.add(DWORD, "pointer_size_", null);
structure.add(DWORD, "compile_pic_", null);
for (int i = 0; i < sections.getSectionList().size(); ++i) {
structure.add(sections.getSectionList().get(i).toDataType(), "section_" + i, null);
}
for (int i = 0; i < image_methods_.length; ++i) {
structure.add(QWORD, "image_method_" + i, null);
}
return structure;
}
}

View File

@@ -0,0 +1,29 @@
/* ###
* 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.marshmallow;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/pie-release/runtime/image.h
*/
public enum ImageMethod_Marshmallow {
kResolutionMethod,
kImtConflictMethod,
kImtUnimplementedMethod,
kCalleeSaveMethod,
kRefsOnlySaveMethod,
kRefsAndArgsSaveMethod,
kImageMethodsCount, // Number of elements in enum.
}

View File

@@ -0,0 +1,31 @@
/* ###
* 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.marshmallow;
/**
* @see https://android.googlesource.com/platform/art/+/marshmallow-release/runtime/image.h
*/
public enum ImageRoot_Marshmallow {
kResolutionMethod,
kImtConflictMethod,
kDefaultImt,
kCalleeSaveMethod,
kRefsOnlySaveMethod,
kRefsAndArgsSaveMethod,
kDexCaches,
kClassRoots,
kImageRootsMax,
}

View File

@@ -0,0 +1,98 @@
/* ###
* 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.marshmallow;
import ghidra.app.util.bin.BinaryReader;
import ghidra.file.formats.android.art.ArtHeader;
import ghidra.file.formats.android.art.ArtImageSections;
public class ImageSections_Marshmallow extends ArtImageSections {
public final static int kSectionObjects = 0;
public final static int kSectionArtFields = 1;
public final static int kSectionArtMethods = 2;
public final static int kSectionInternedStrings = 3;
public final static int kSectionImageBitmap = 4;
public final static int kSectionCount = 5; // Number of elements in enum.
public ImageSections_Marshmallow(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 UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionImTables() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionIMTConflictTables() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionDexCacheArrays() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionInternedStrings() {
return kSectionInternedStrings;
}
@Override
public int get_kSectionClassTable() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionStringReferenceOffsets() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionMetadata() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionImageBitmap() {
return kSectionImageBitmap;
}
@Override
public int get_kSectionCount() {
return kSectionCount;
}
}

View File

@@ -0,0 +1,251 @@
/* ###
* 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.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;
import ghidra.program.model.data.Structure;
import ghidra.program.model.listing.Program;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* https://android.googlesource.com/platform/art/+/nougat-release/runtime/image.h
* https://android.googlesource.com/platform/art/+/nougat-release/runtime/image.cc
*/
public class ArtHeader_Nougat extends ArtHeader implements ArtCompression {
protected int image_begin_;
protected int image_size_;
protected int oat_checksum_;
protected int oat_file_begin_;
protected int oat_data_begin_;
protected int oat_data_end_;
protected int oat_file_end_;
protected int boot_image_begin_;
protected int boot_image_size_;
protected int boot_oat_begin_;
protected int boot_oat_size_;
protected int patch_delta_;
protected int image_roots_;
protected int pointer_size_;
protected int compile_pic_;
protected int is_pic_;
protected int storage_mode_;
protected int data_size_;
protected ArtImageSections sections;
private long _compressedOffset;
public ArtHeader_Nougat(BinaryReader reader) throws IOException {
super(reader);
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();
image_size_ = reader.readNextInt();
oat_checksum_ = reader.readNextInt();
oat_file_begin_ = reader.readNextInt();
oat_data_begin_ = reader.readNextInt();
oat_data_end_ = reader.readNextInt();
oat_file_end_ = reader.readNextInt();
boot_image_begin_ = reader.readNextInt();
boot_image_size_ = reader.readNextInt();
boot_oat_begin_ = reader.readNextInt();
boot_oat_size_ = reader.readNextInt();
patch_delta_ = reader.readNextInt();
image_roots_ = reader.readNextInt();
pointer_size_ = reader.readNextInt();
compile_pic_ = reader.readNextInt();
is_pic_ = reader.readNextInt();
sections = getImageSections(reader);
sections.parseSections(reader);
parseImageMethods(reader);
storage_mode_ = reader.readNextInt();
data_size_ = reader.readNextInt();
//The compressed offset is immediately following this header.
_compressedOffset = reader.getPointerIndex();
reader = DecompressionManager.decompress(reader, this, TaskMonitor.DUMMY);
sections.parse(reader);
}
@Override
public int getArtMethodCountForVersion() {
return ImageMethod_Nougat.kImageMethodsCount.ordinal();
}
@Override
public int getImageBegin() {
return image_begin_;
}
@Override
public int getImageSize() {
return image_size_;
}
/**
* Boot oat begin and end (app image headers only).
* @return the boot oat begin address
*/
public int getBootOatBegin() {
return boot_oat_begin_;
}
/**
* Boot oat begin and end (app image headers only).
* @return the boot oat size
*/
public int getBootOatSize() {
return boot_oat_size_;
}
@Override
public int getOatFileBegin() {
return oat_file_begin_;
}
@Override
public int getOatFileEnd() {
return oat_file_end_;
}
@Override
public int getOatDataBegin() {
return oat_data_begin_;
}
@Override
public int getOatDataEnd() {
return oat_data_end_;
}
/**
* Boolean (0 or 1) to denote if the image can be mapped at a random address,
* this only refers to the .art file.
* Currently, app oat files do not depend on their app image.
* There are no pointers from the app oat code to the app image.
* @return true of position independent code, or false if dependent.
*/
public int getIsPic() {
return is_pic_;
}
@Override
public ArtStorageMode getStorageMode() throws UnknownArtStorageModeException {
return ArtStorageMode.get(storage_mode_);
}
@Override
public long getCompressedOffset() {
return _compressedOffset;
}
@Override
public int getCompressedSize() {
return data_size_;
}
@Override
public long getDecompressedOffset() {
return getCompressedOffset();
}
@Override
public int getDecompressedSize() {
return image_size_;
}
/**
* Checksum of the oat file we link to for load time sanity check.
*/
@Override
public int getOatChecksum() {
return oat_checksum_;
}
@Override
public int getPointerSize() {
return pointer_size_;
}
@Override
public void markup(Program program, TaskMonitor monitor) throws Exception {
DecompressionManager.decompressOverMemory(program, this, monitor);
sections.markup(program, monitor);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_Nougat.class);
try {
structure.setName(className);
}
catch (InvalidNameException e) {
//ignore, just use original name should this fail
}
structure.add(DWORD, "image_begin_", null);
structure.add(DWORD, "image_size_", null);
structure.add(DWORD, "oat_checksum_", null);
structure.add(DWORD, "oat_file_begin_", null);
structure.add(DWORD, "oat_data_begin_", null);
structure.add(DWORD, "oat_data_end_", null);
structure.add(DWORD, "oat_file_end_", null);
structure.add(DWORD, "boot_image_begin_", null);
structure.add(DWORD, "boot_image_size_", null);
structure.add(DWORD, "boot_oat_begin_", null);
structure.add(DWORD, "boot_oat_size_", null);
structure.add(DWORD, "patch_delta_", null);
structure.add(DWORD, "image_roots_", null);
structure.add(DWORD, "pointer_size_", null);
structure.add(DWORD, "compile_pic_", null);
structure.add(DWORD, "is_pic_", null);
for (int i = 0; i < sections.getSectionList().size(); ++i) {
structure.add(sections.getSectionList().get(i).toDataType(), "section_" + i, null);
}
for (int i = 0; i < imageMethodsList.size(); ++i) {
structure.add(QWORD, "image_method_" + i, null);
}
structure.add(DWORD, "storage_mode_", null);
structure.add(DWORD, "data_size_", null);
return structure;
}
}

View File

@@ -0,0 +1,59 @@
/* ###
* 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.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;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/art/+/nougat-mr2-pixel-release/runtime/image.h
*/
public class ArtHeader_NougatMR2Pixel extends ArtHeader_Nougat {
public ArtHeader_NougatMR2Pixel(BinaryReader reader) throws IOException {
super(reader);
}
protected ArtImageSections getImageSections(BinaryReader reader) {
return new ImageSections_NougatMR2Pixel(reader, this);
}
@Override
public int getArtMethodCountForVersion() {
return ImageMethod_Nougat.kImageMethodsCount.ordinal();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_NougatMR2Pixel.class);
try {
structure.setName(className);
}
catch (InvalidNameException e) {
//ignore
}
return structure;
}
}

View File

@@ -0,0 +1,29 @@
/* ###
* 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.nougat;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/pie-release/runtime/image.h
*/
public enum ImageMethod_Nougat {
kResolutionMethod,
kImtConflictMethod,
kImtUnimplementedMethod,
kCalleeSaveMethod,
kRefsOnlySaveMethod,
kRefsAndArgsSaveMethod,
kImageMethodsCount, // Number of elements in enum.
}

View File

@@ -0,0 +1,23 @@
/* ###
* 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.nougat;
/**
* @see https://android.googlesource.com/platform/art/+/nougat-release/runtime/image.h
*/
public enum ImageRoot_Nougat {
kDexCaches, kClassRoots, kImageRootsMax,
}

View File

@@ -0,0 +1,102 @@
/* ###
* 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.nougat;
import ghidra.app.util.bin.BinaryReader;
import ghidra.file.formats.android.art.ArtHeader;
import ghidra.file.formats.android.art.ArtImageSections;
public class ImageSections_Nougat 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 kSectionIMTConflictTables = 4;
public final static int kSectionDexCacheArrays = 5;
public final static int kSectionInternedStrings = 6;
public final static int kSectionClassTable = 7;
public final static int kSectionImageBitmap = 8;
public final static int kSectionCount = 9; // Number of elements in enum.
public ImageSections_Nougat(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 UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionIMTConflictTables() {
return kSectionIMTConflictTables;
}
@Override
public int get_kSectionDexCacheArrays() {
return kSectionDexCacheArrays;
}
@Override
public int get_kSectionInternedStrings() {
return kSectionInternedStrings;
}
@Override
public int get_kSectionClassTable() {
return kSectionClassTable;
}
@Override
public int get_kSectionStringReferenceOffsets() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionMetadata() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionImageBitmap() {
return kSectionImageBitmap;
}
@Override
public int get_kSectionCount() {
return kSectionCount;
}
}

View File

@@ -0,0 +1,103 @@
/* ###
* 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.nougat;
import ghidra.app.util.bin.BinaryReader;
import ghidra.file.formats.android.art.ArtHeader;
import ghidra.file.formats.android.art.ArtImageSections;
public class ImageSections_NougatMR2Pixel 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 kSectionDexCacheArrays = 6;
public final static int kSectionInternedStrings = 7;
public final static int kSectionClassTable = 8;
public final static int kSectionImageBitmap = 9;
public final static int kSectionCount = 10; // Number of elements in enum.
public ImageSections_NougatMR2Pixel(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 kSectionDexCacheArrays;
}
@Override
public int get_kSectionInternedStrings() {
return kSectionInternedStrings;
}
@Override
public int get_kSectionClassTable() {
return kSectionClassTable;
}
@Override
public int get_kSectionStringReferenceOffsets() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionMetadata() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionImageBitmap() {
return kSectionImageBitmap;
}
@Override
public int get_kSectionCount() {
return kSectionCount;
}
}

View File

@@ -0,0 +1,60 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.file.formats.android.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;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/art/+/oreo-release/runtime/image.h
*/
public class ArtHeader_Oreo extends ArtHeader_NougatMR2Pixel {
public ArtHeader_Oreo(BinaryReader reader) throws IOException {
super(reader);
}
protected ArtImageSections getImageSections(BinaryReader reader) {
return new ImageSections_Oreo(reader, this);
}
@Override
public int getArtMethodCountForVersion() {
return ImageMethod_Oreo.kImageMethodsCount.ordinal();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_Oreo.class);
try {
structure.setName(className);
}
catch (InvalidNameException e) {
//ignore
}
return structure;
}
}

View File

@@ -0,0 +1,59 @@
/* ###
* 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.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;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/art/+/oreo-mr1-release/runtime/image.h
*/
public class ArtHeader_OreoMR1 extends ArtHeader_Oreo {
public ArtHeader_OreoMR1(BinaryReader reader) throws IOException {
super(reader);
}
protected ArtImageSections getImageSections(BinaryReader reader) {
return new ImageSections_OreoMR1(reader, this);
}
@Override
public int getArtMethodCountForVersion() {
return ImageMethod_Oreo.kImageMethodsCount.ordinal();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_OreoMR1.class);
try {
structure.setName(className);
}
catch (InvalidNameException e) {
//ignore
}
return structure;
}
}

View File

@@ -0,0 +1,30 @@
/* ###
* 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.oreo;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/oreo-release/runtime/image.h
*/
public enum ImageMethod_Oreo {
kResolutionMethod,
kImtConflictMethod,
kImtUnimplementedMethod,
kSaveAllCalleeSavesMethod,
kSaveRefsOnlyMethod,
kSaveRefsAndArgsMethod,
kSaveEverythingMethod,
kImageMethodsCount, // Number of elements in enum.
}

View File

@@ -0,0 +1,26 @@
/* ###
* 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.oreo;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/oreo-release/runtime/image.h
*/
public enum ImageRoot_Oreo {
kDexCaches,
kClassRoots,
kClassLoader, // App image only.
kImageRootsMax,
}

View File

@@ -0,0 +1,117 @@
/* ###
* 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.oreo;
import ghidra.app.util.bin.BinaryReader;
import ghidra.file.formats.android.art.ArtHeader;
import ghidra.file.formats.android.art.ArtImageSections;
public class ImageSections_Oreo 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 kSectionDexCacheArrays = 6;
public final static int kSectionInternedStrings = 7;
public final static int kSectionClassTable = 8;
public final static int kSectionImageBitmap = 9;
public final static int kSectionCount = 10; // Number of elements in enum.
enum ImageSections {
kSectionObjects,
kSectionArtFields,
kSectionArtMethods,
kSectionRuntimeMethods,
kSectionImTables,
kSectionIMTConflictTables,
kSectionDexCacheArrays,
kSectionInternedStrings,
kSectionClassTable,
kSectionImageBitmap,
kSectionCount, // Number of elements in enum.
};
public ImageSections_Oreo(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 UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionIMTConflictTables() {
return kSectionIMTConflictTables;
}
@Override
public int get_kSectionDexCacheArrays() {
return kSectionDexCacheArrays;
}
@Override
public int get_kSectionInternedStrings() {
return kSectionInternedStrings;
}
@Override
public int get_kSectionClassTable() {
return kSectionClassTable;
}
@Override
public int get_kSectionStringReferenceOffsets() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionMetadata() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionImageBitmap() {
return kSectionImageBitmap;
}
@Override
public int get_kSectionCount() {
return kSectionCount;
}
}

View File

@@ -0,0 +1,103 @@
/* ###
* 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.oreo;
import ghidra.app.util.bin.BinaryReader;
import ghidra.file.formats.android.art.ArtHeader;
import ghidra.file.formats.android.art.ArtImageSections;
public class ImageSections_OreoMR1 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 kSectionDexCacheArrays = 6;
public final static int kSectionInternedStrings = 7;
public final static int kSectionClassTable = 8;
public final static int kSectionImageBitmap = 9;
public final static int kSectionCount = 10; // Number of elements in enum.
public ImageSections_OreoMR1(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 kSectionDexCacheArrays;
}
@Override
public int get_kSectionInternedStrings() {
return kSectionInternedStrings;
}
@Override
public int get_kSectionClassTable() {
return kSectionClassTable;
}
@Override
public int get_kSectionStringReferenceOffsets() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionMetadata() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionImageBitmap() {
return kSectionImageBitmap;
}
@Override
public int get_kSectionCount() {
return kSectionCount;
}
}

View File

@@ -0,0 +1,244 @@
/* ###
* 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.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;
import ghidra.program.model.data.Structure;
import ghidra.program.model.listing.Program;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/pie-release/runtime/image.h
*/
public class ArtHeader_Pie extends ArtHeader implements ArtCompression {
protected int image_begin_;
protected int image_size_;
protected int oat_checksum_;
protected int oat_file_begin_;
protected int oat_data_begin_;
protected int oat_data_end_;
protected int oat_file_end_;
private int boot_image_begin_;
private int boot_image_size_;
private int boot_oat_begin_;
private int boot_oat_size_;
protected int patch_delta_;
protected int image_roots_;
protected int pointer_size_;
protected int compile_pic_;
private int is_pic_;
protected long[] image_methods_ = new long[ImageMethod_Pie.kImageMethodsCount.ordinal()];
private int storage_mode_;
private int data_size_;
private ArtImageSections sections;
private long _compressedOffset;
public ArtHeader_Pie(BinaryReader reader) throws IOException {
super(reader);
parse(reader);
}
@Override
protected void parse(BinaryReader reader) throws IOException {
image_begin_ = reader.readNextInt();
image_size_ = reader.readNextInt();
oat_checksum_ = reader.readNextInt();
oat_file_begin_ = reader.readNextInt();
oat_data_begin_ = reader.readNextInt();
oat_data_end_ = reader.readNextInt();
oat_file_end_ = reader.readNextInt();
boot_image_begin_ = reader.readNextInt();
boot_image_size_ = reader.readNextInt();
boot_oat_begin_ = reader.readNextInt();
boot_oat_size_ = reader.readNextInt();
patch_delta_ = reader.readNextInt();
image_roots_ = reader.readNextInt();
pointer_size_ = reader.readNextInt();
compile_pic_ = reader.readNextInt();
is_pic_ = reader.readNextInt();
sections = new ImageSections_Pie(reader, this);
sections.parseSections(reader);
parseImageMethods(reader);
storage_mode_ = reader.readNextInt();
data_size_ = reader.readNextInt();
_compressedOffset = reader.getPointerIndex();
reader = DecompressionManager.decompress(reader, this, TaskMonitor.DUMMY);
// NOTE:
// cannot parse the sections until after the blocks are decompressed!
sections.parse(reader);
}
@Override
public int getImageBegin() {
return image_begin_;
}
@Override
public int getImageSize() {
return image_size_;
}
@Override
public int getOatChecksum() {
return oat_checksum_;
}
@Override
public int getOatFileBegin() {
return oat_file_begin_;
}
@Override
public int getOatFileEnd() {
return oat_file_end_;
}
@Override
public int getOatDataBegin() {
return oat_data_begin_;
}
@Override
public int getOatDataEnd() {
return oat_data_end_;
}
public int getBootOatBegin() {
return boot_oat_begin_;
}
public int getBootOatSize() {
return boot_oat_size_;
}
public int getBootImageBegin() {
return boot_image_begin_;
}
public int getBootImageSize() {
return boot_image_size_;
}
public int isPIC() {
return is_pic_;
}
public int getCompilePIC() {
return compile_pic_;
}
@Override
public ArtStorageMode getStorageMode() throws UnknownArtStorageModeException {
return ArtStorageMode.get(storage_mode_);
}
@Override
public long getCompressedOffset() {
return _compressedOffset;
}
@Override
public int getCompressedSize() {
return data_size_;
}
@Override
public long getDecompressedOffset() {
return getCompressedOffset();
}
@Override
public int getDecompressedSize() {
return image_size_;
}
@Override
public int getPointerSize() {
return pointer_size_;
}
@Override
public void markup(Program program, TaskMonitor monitor) throws Exception {
DecompressionManager.decompressOverMemory(program, this, monitor);
sections.markup(program, monitor);
}
@Override
public int getArtMethodCountForVersion() {
return ImageMethod_Pie.kImageMethodsCount.ordinal();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
String className = StructConverterUtil.parseName(ArtHeader_Pie.class);
try {
structure.setName(className);
}
catch (InvalidNameException e) {
//ignore
}
structure.add(DWORD, "image_begin_", null);
structure.add(DWORD, "image_size_", null);
structure.add(DWORD, "oat_checksum_", null);
structure.add(DWORD, "oat_file_begin_", null);
structure.add(DWORD, "oat_data_begin_", null);
structure.add(DWORD, "oat_data_end_", null);
structure.add(DWORD, "oat_file_end_", null);
structure.add(DWORD, "boot_image_begin_", null);
structure.add(DWORD, "boot_image_size_", null);
structure.add(DWORD, "boot_oat_begin_", null);
structure.add(DWORD, "boot_oat_size_", null);
structure.add(DWORD, "patch_delta_", null);
structure.add(DWORD, "image_roots_", null);
structure.add(DWORD, "pointer_size_", null);
structure.add(DWORD, "compile_pic_", null);
structure.add(DWORD, "is_pic_", null);
for (int i = 0; i < sections.getSectionList().size(); ++i) {
structure.add(sections.getSectionList().get(i).toDataType(), "section_" + i, null);
}
for (int i = 0; i < image_methods_.length; ++i) {
structure.add(QWORD, "image_method_" + i, null);
}
structure.add(DWORD, "storage_mode_", null);
structure.add(DWORD, "data_size_", null);
return structure;
}
}

View File

@@ -0,0 +1,32 @@
/* ###
* 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.pie;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/pie-release/runtime/image.h
*/
public enum ImageMethod_Pie {
kResolutionMethod,
kImtConflictMethod,
kImtUnimplementedMethod,
kSaveAllCalleeSavesMethod,
kSaveRefsOnlyMethod,
kSaveRefsAndArgsMethod,
kSaveEverythingMethod,
kSaveEverythingMethodForClinit,
kSaveEverythingMethodForSuspendCheck,
kImageMethodsCount, // Number of elements in enum.
}

View File

@@ -0,0 +1,26 @@
/* ###
* 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.pie;
/**
* https://android.googlesource.com/platform/art/+/nougat-release/runtime/image.h
*/
public enum ImageRoot_Pie {
kDexCaches,
kClassRoots,
kClassLoader, // App image only.
kImageRootsMax,
}

View File

@@ -0,0 +1,103 @@
/* ###
* 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.pie;
import ghidra.app.util.bin.BinaryReader;
import ghidra.file.formats.android.art.ArtHeader;
import ghidra.file.formats.android.art.ArtImageSections;
public class ImageSections_Pie 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 kSectionDexCacheArrays = 6;
public final static int kSectionInternedStrings = 7;
public final static int kSectionClassTable = 8;
public final static int kSectionImageBitmap = 9;
public final static int kSectionCount = 10; // Number of elements in enum.
public ImageSections_Pie(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 kSectionDexCacheArrays;
}
@Override
public int get_kSectionInternedStrings() {
return kSectionInternedStrings;
}
@Override
public int get_kSectionClassTable() {
return kSectionClassTable;
}
@Override
public int get_kSectionStringReferenceOffsets() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionMetadata() {
return UNSUPPORTED_SECTION;
}
@Override
public int get_kSectionImageBitmap() {
return kSectionImageBitmap;
}
@Override
public int get_kSectionCount() {
return kSectionCount;
}
}

View File

@@ -32,29 +32,29 @@ public class BootImage implements StructConverter {
private int secondStageAddress;
private int tagsAddress;
private int pageSize;
private int [] unused;
private int[] unused;
private String name;
private String commandLine;
private int [] id;
private int[] id;
public BootImage(ByteProvider provider) throws IOException {
this( new BinaryReader( provider, true ) );
this(new BinaryReader(provider, true));
}
public BootImage(BinaryReader reader) throws IOException {
magic = reader.readNextAsciiString( BootImageConstants.BOOT_IMAGE_MAGIC_SIZE );
kernelSize = reader.readNextInt();
kernelAddress = reader.readNextInt();
ramDiskSize = reader.readNextInt();
ramDiskAddress = reader.readNextInt();
secondStageSize = reader.readNextInt();
secondStageAddress = reader.readNextInt();
tagsAddress = reader.readNextInt();
pageSize = reader.readNextInt();
unused = reader.readNextIntArray( 2 );
name = reader.readNextAsciiString( BootImageConstants.BOOT_NAME_SIZE );
commandLine = reader.readNextAsciiString( BootImageConstants.BOOT_ARGS_SIZE );
id = reader.readNextIntArray( 8 );
magic = reader.readNextAsciiString(BootImageConstants.BOOT_MAGIC_SIZE);
kernelSize = reader.readNextInt();
kernelAddress = reader.readNextInt();
ramDiskSize = reader.readNextInt();
ramDiskAddress = reader.readNextInt();
secondStageSize = reader.readNextInt();
secondStageAddress = reader.readNextInt();
tagsAddress = reader.readNextInt();
pageSize = reader.readNextInt();
unused = reader.readNextIntArray(2);
name = reader.readNextAsciiString(BootImageConstants.BOOT_NAME_SIZE);
commandLine = reader.readNextAsciiString(BootImageConstants.BOOT_ARGS_SIZE);
id = reader.readNextIntArray(8);
}
public String getMagic() {
@@ -74,7 +74,7 @@ public class BootImage implements StructConverter {
}
public int getKernelSizePageAligned() {
int remainder = getPageSize() - ( getKernelSize() % getPageSize() );
int remainder = getPageSize() - (getKernelSize() % getPageSize());
return getKernelSize() + remainder;
}
@@ -91,7 +91,7 @@ public class BootImage implements StructConverter {
}
public int getRamDiskSizePageAligned() {
int remainder = getPageSize() - ( getRamDiskSize() % getPageSize() );
int remainder = getPageSize() - (getRamDiskSize() % getPageSize());
return getRamDiskSize() + remainder;
}
@@ -104,11 +104,11 @@ public class BootImage implements StructConverter {
}
public int getSecondStageOffset() {
return getRamDiskOffset() + ( getRamDiskSizePageAligned() * getPageSize() );
return getRamDiskOffset() + (getRamDiskSizePageAligned() * getPageSize());
}
public int getSecondStageSizePageAligned() {
int remainder = getPageSize() - ( getSecondStageSize() % getPageSize() );
int remainder = getPageSize() - (getSecondStageSize() % getPageSize());
return getSecondStageSize() + remainder;
}
@@ -120,7 +120,7 @@ public class BootImage implements StructConverter {
return pageSize;
}
public int [] getUnused() {
public int[] getUnused() {
return unused;
}
@@ -132,25 +132,25 @@ public class BootImage implements StructConverter {
return commandLine;
}
public int [] getId() {
public int[] getId() {
return id;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = new StructureDataType( "boot_img_hdr", 0 );
structure.add( UTF8, BootImageConstants.BOOT_IMAGE_MAGIC_SIZE, "magic", null );
structure.add( DWORD, "kernelSize", null );
structure.add( DWORD, "kernelAddress", null );
structure.add( DWORD, "ramDiskSize", null );
structure.add( DWORD, "ramDiskAddress", null );
structure.add( DWORD, "secondStageSize", null );
structure.add( DWORD, "secondStageAddress", null );
structure.add( DWORD, "tagsAddress", null );
structure.add( DWORD, "pageSize", null );
Structure structure = new StructureDataType("boot_img_hdr", 0);
structure.add(UTF8, BootImageConstants.BOOT_MAGIC_SIZE, "magic", null);
structure.add(DWORD, "kernelSize", null);
structure.add(DWORD, "kernelAddress", null);
structure.add(DWORD, "ramDiskSize", null);
structure.add(DWORD, "ramDiskAddress", null);
structure.add(DWORD, "secondStageSize", null);
structure.add(DWORD, "secondStageAddress", null);
structure.add(DWORD, "tagsAddress", null);
structure.add(DWORD, "pageSize", null);
structure.add(new ArrayDataType(DWORD, 2, DWORD.getLength()), "unused", null);
structure.add( UTF8, BootImageConstants.BOOT_NAME_SIZE, "name", null );
structure.add( UTF8, BootImageConstants.BOOT_ARGS_SIZE, "commandLine", null );
structure.add(UTF8, BootImageConstants.BOOT_NAME_SIZE, "name", null);
structure.add(UTF8, BootImageConstants.BOOT_ARGS_SIZE, "commandLine", null);
structure.add(new ArrayDataType(DWORD, 8, DWORD.getLength()), "id", null);
return structure;
}

View File

@@ -17,7 +17,9 @@ package ghidra.file.formats.android.bootimg;
import ghidra.app.plugin.core.analysis.AnalysisWorker;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.MemoryByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.file.analyzers.FileFormatAnalyzer;
import ghidra.program.model.address.Address;
@@ -32,7 +34,7 @@ public class BootImageAnalyzer extends FileFormatAnalyzer implements AnalysisWor
@Override
public String getName() {
return "Android Boot or Recovery Image Annotation";
return "Android Boot, Recovery, or Vendor Image Annotation";
}
@Override
@@ -42,13 +44,13 @@ public class BootImageAnalyzer extends FileFormatAnalyzer implements AnalysisWor
@Override
public String getDescription() {
return "Annotates Android Boot and Recovery Image files.";
return "Annotates Android Boot, Recovery, or Vendor Image files.";
}
@Override
public boolean canAnalyze(Program program) {
try {
return BootImageUtil.isBootImage(program);
return BootImageUtil.isBootImage(program) || BootImageUtil.isVendorBootImage(program);
}
catch (Exception e) {
// not a boot image
@@ -61,63 +63,98 @@ public class BootImageAnalyzer extends FileFormatAnalyzer implements AnalysisWor
return true;
}
private MessageLog log;
private MessageLog messageLog;
@Override
public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws Exception {
this.log = log;
this.messageLog = log;
AutoAnalysisManager manager = AutoAnalysisManager.getAnalysisManager(program);
return manager.scheduleWorker(this, null, false, monitor);
}
@Override
public boolean analysisWorkerCallback(Program program, Object workerContext, TaskMonitor monitor)
throws Exception, CancelledException {
public boolean analysisWorkerCallback(Program program, Object workerContext,
TaskMonitor monitor) throws Exception, CancelledException {
Address address = program.getMinAddress();
ByteProvider provider = new MemoryByteProvider(program.getMemory(), address);
BinaryReader reader = new BinaryReader(provider, true);
BootImage header = new BootImage(reader);
if (BootImageUtil.isBootImage(program)) {
if (!header.getMagic().equals(BootImageConstants.BOOT_IMAGE_MAGIC)) {
return false;
BootImageHeader header = BootImageHeaderFactory.getBootImageHeader(reader);
if (!BootImageConstants.BOOT_MAGIC.equals(header.getMagic())) {
return false;
}
DataType headerDataType = header.toDataType();
Data headerData = createData(program, address, headerDataType);
if (headerData == null) {
messageLog.appendMsg("Unable to create header data.");
return false;
}
createFragment(program, headerDataType.getName(), toAddr(program, 0),
toAddr(program, header.getPageSize()));
if (header.getKernelSize() > 0) {
Address start = toAddr(program, header.getKernelOffset());
Address end = toAddr(program, header.getKernelOffset() + header.getKernelSize());
createFragment(program, BootImageConstants.KERNEL, start, end);
}
if (header.getRamdiskSize() > 0) {
Address start = toAddr(program, header.getRamdiskOffset());
Address end = toAddr(program, header.getRamdiskOffset() + header.getRamdiskSize());
createFragment(program, BootImageConstants.RAMDISK, start, end);
}
if (header.getSecondSize() > 0) {
Address start = toAddr(program, header.getSecondOffset());
Address end = toAddr(program, header.getSecondOffset() + header.getSecondSize());
createFragment(program, BootImageConstants.SECOND_STAGE, start, end);
}
changeDataSettings(program, monitor);
}
else if (BootImageUtil.isVendorBootImage(program)) {
VendorBootImageHeader header =
VendorBootImageHeaderFactory.getVendorBootImageHeader(reader);
DataType headerDataType = header.toDataType();
if (!header.getMagic().equals(BootImageConstants.VENDOR_BOOT_MAGIC)) {
return false;
}
Data headerData = createData(program, address, headerDataType);
DataType headerDataType = header.toDataType();
if (headerData == null) {
log.appendMsg("Unable to create header data.");
Data headerData = createData(program, address, headerDataType);
if (headerData == null) {
messageLog.appendMsg("Unable to create header data.");
}
createFragment(program, headerDataType.getName(), toAddr(program, 0),
toAddr(program, headerData.getLength()));
if (header.getVendorRamdiskSize() > 0) {
Address start = toAddr(program, header.getVendorRamdiskOffset());
Address end = toAddr(program,
header.getVendorRamdiskOffset() + header.getVendorRamdiskSize());
createFragment(program, BootImageConstants.RAMDISK, start, end);
}
if (header.getDtbSize() > 0) {
Address start = toAddr(program, header.getDtbOffset());
Address end = toAddr(program, header.getDtbOffset() + header.getDtbSize());
createFragment(program, BootImageConstants.DTB, start, end);
}
}
createFragment(program, headerDataType.getName(), toAddr(program, 0),
toAddr(program, header.getPageSize()));
if (header.getKernelSize() > 0) {
Address start = toAddr(program, header.getKernelOffset());
Address end = toAddr(program, header.getKernelOffset() + header.getKernelSize());
createFragment(program, BootImageConstants.KERNEL, start, end);
}
if (header.getRamDiskSize() > 0) {
Address start = toAddr(program, header.getRamDiskOffset());
Address end = toAddr(program, header.getRamDiskOffset() + header.getRamDiskSize());
createFragment(program, BootImageConstants.RAMDISK, start, end);
}
if (header.getSecondStageSize() > 0) {
Address start = toAddr(program, header.getSecondStageOffset());
Address end =
toAddr(program, header.getSecondStageOffset() + header.getSecondStageSize());
createFragment(program, BootImageConstants.SECOND_STAGE, start, end);
}
changeDataSettings(program, monitor);
removeEmptyFragments(program);
return true;

View File

@@ -15,22 +15,42 @@
*/
package ghidra.file.formats.android.bootimg;
/**
*
* https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/include/bootimg/bootimg.h
*
*/
public final class BootImageConstants {
public final static String BOOT_IMAGE_MAGIC = "ANDROID!";
public final static String BOOT_MAGIC = "ANDROID!";
public final static int BOOT_MAGIC_SIZE = 8;
public final static int BOOT_NAME_SIZE = 16;
public final static int BOOT_ARGS_SIZE = 512;
public final static int BOOT_EXTRA_ARGS_SIZE = 1024;
public final static byte [] BOOT_IMAGE_MAGIC_BYTES = BOOT_IMAGE_MAGIC.getBytes();
public final static int ID_SIZE = 8;
public final static int BOOT_IMAGE_MAGIC_SIZE = BOOT_IMAGE_MAGIC.length();
public final static int V3_HEADER_SIZE = 4096;
public final static int V3_PAGE_SIZE = 4096;
public final static int V4_HEADER_SIZE = V3_HEADER_SIZE;
public final static int V4_PAGE_SIZE = V3_PAGE_SIZE;
public final static int BOOT_NAME_SIZE = 16;
public final static int BOOT_ARGS_SIZE = 512;
public final static String VENDOR_BOOT_MAGIC = "VNDRBOOT";
public final static int VENDOR_BOOT_MAGIC_SIZE = 8;
public final static int VENDOR_BOOT_ARGS_SIZE = 2048;
public final static int VENDOR_BOOT_NAME_SIZE = 16;
public final static String SECOND_STAGE = "second stage";
public final static int VENDOR_RAMDISK_TYPE_NONE = 0;
public final static int VENDOR_RAMDISK_TYPE_PLATFORM = 1;
public final static int VENDOR_RAMDISK_TYPE_RECOVERY = 2;
public final static int VENDOR_RAMDISK_TYPE_DLKM = 3;
public final static int VENDOR_RAMDISK_NAME_SIZE = 32;
public final static int VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE = 16;
public final static String RAMDISK = "ramdisk";
public final static String KERNEL = "kernel";
public final static String SECOND_STAGE = "second stage";
public final static String RAMDISK = "ramdisk";
public final static String KERNEL = "kernel";
public final static String DTB = "dtb";
public final static int HEADER_VERSION_OFFSET = 0x28;
}

View File

@@ -15,11 +15,18 @@
*/
package ghidra.file.formats.android.bootimg;
import java.io.*;
import java.util.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import ghidra.app.util.bin.ByteProvider;
import ghidra.formats.gfilesystem.*;
import ghidra.formats.gfilesystem.GFile;
import ghidra.formats.gfilesystem.GFileImpl;
import ghidra.formats.gfilesystem.GFileSystemBase;
import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
import ghidra.formats.gfilesystem.factory.GFileSystemBaseFactory;
import ghidra.util.exception.CancelledException;
@@ -29,7 +36,7 @@ import ghidra.util.task.TaskMonitor;
@FileSystemInfo(type = "androidbootimg", description = "Android Boot and Recovery Images", factory = GFileSystemBaseFactory.class)
public class BootImageFileSystem extends GFileSystemBase {
private BootImage header;
private BootImageHeader header;
private GFileImpl kernelFile;
private GFileImpl ramdiskFile;
private GFileImpl secondStageFile;
@@ -41,15 +48,16 @@ public class BootImageFileSystem extends GFileSystemBase {
@Override
public boolean isValid(TaskMonitor monitor) throws IOException {
byte[] bytes = provider.readBytes(0, BootImageConstants.BOOT_IMAGE_MAGIC_SIZE);
return Arrays.equals(bytes, BootImageConstants.BOOT_IMAGE_MAGIC_BYTES);
byte[] bytes = provider.readBytes(0, BootImageConstants.BOOT_MAGIC_SIZE);
return Arrays.equals(bytes, BootImageConstants.BOOT_MAGIC.getBytes());
}
@Override
public void open(TaskMonitor monitor) throws IOException, CryptoException, CancelledException {
this.header = new BootImage(provider);
if (!header.getMagic().equals(BootImageConstants.BOOT_IMAGE_MAGIC)) {
this.header = BootImageHeaderFactory.getBootImageHeader(provider, true);
if (!header.getMagic().equals(BootImageConstants.BOOT_MAGIC)) {
throw new IOException("Invalid Android boot image file!");
}
@@ -58,14 +66,15 @@ public class BootImageFileSystem extends GFileSystemBase {
header.getKernelSize(), null);
fileList.add(kernelFile);
}
if (header.getRamDiskSize() > 0) {
if (header.getRamdiskSize() > 0) {
ramdiskFile = GFileImpl.fromFilename(this, root, BootImageConstants.RAMDISK, false,
header.getKernelSize(), null);
header.getRamdiskSize(), null);
fileList.add(ramdiskFile);
}
if (header.getSecondStageSize() > 0) {
if (header.getSecondSize() > 0) {
secondStageFile = GFileImpl.fromFilename(this, root, BootImageConstants.SECOND_STAGE,
false, header.getKernelSize(), null);
false, header.getSecondSize(), null);
fileList.add(secondStageFile);
}
}
@@ -109,12 +118,12 @@ public class BootImageFileSystem extends GFileSystemBase {
}
else if (file == ramdiskFile) {
byte[] ramDiskBytes =
provider.readBytes(header.getRamDiskOffset(), header.getRamDiskSize());
provider.readBytes(header.getRamdiskOffset(), header.getRamdiskSize());
return new ByteArrayInputStream(ramDiskBytes);
}
else if (file == secondStageFile) {
byte[] secondStageBytes =
provider.readBytes(header.getSecondStageOffset(), header.getSecondStageSize());
provider.readBytes(header.getSecondOffset(), header.getSecondSize());
return new ByteArrayInputStream(secondStageBytes);
}
return null;

View File

@@ -0,0 +1,93 @@
/* ###
* 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.bootimg;
import ghidra.app.util.bin.StructConverter;
public abstract class BootImageHeader implements StructConverter {
/**
* Returns the Boot Image MAGIC value.
* @see BootImageConstants
* @return the Boot Image MAGIC value
*/
public abstract String getMagic();
/**
* Returns the page size, as defined in the header.
* @return the page size, as defined in the header
*/
public abstract int getPageSize();
/**
* Returns the kernel size, as defined in the header.
* @return the kernel size, as defined in the header
*/
public abstract int getKernelSize();
/**
* Returns the number of pages used to store the kernel.
* @return the number of pages used to store the kernel
*/
public abstract int getKernelPageCount();
/**
* Returns the kernel file offset
* @return the kernel file offset
*/
public abstract long getKernelOffset();
/**
* Returns the ramdisk size, as defined in the header.
* @return the ramdisk size, as defined in the header
*/
public abstract int getRamdiskSize();
/**
* Returns the number of pages used to store the ramdisk.
* @return the number of pages used to store the ramdisk
*/
public abstract int getRamdiskPageCount();
/**
* Returns the ramdisk file offset.
* @return the ramdisk file offset
*/
public abstract int getRamdiskOffset();
/**
* Returns the second stage size, as defined in the header.
* @return the second stage size, as defined in the header
*/
public abstract int getSecondSize();
/**
* Returns the number of pages used to store the second stage.
* @return the number of pages used to store the second stage
*/
public abstract int getSecondPageCount();
/**
* Returns the second stage file offset.
* @return the second stage file offset
*/
public abstract long getSecondOffset();
/**
* Returns the kernel commandline.
* @return the kernel commandline
*/
public abstract String getCommandLine();
}

View File

@@ -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.bootimg;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
public final class BootImageHeaderFactory {
public final static BootImageHeader getBootImageHeader(ByteProvider provider,
boolean littleEndian) throws IOException {
return getBootImageHeader(new BinaryReader(provider, littleEndian));
}
public final static BootImageHeader getBootImageHeader(BinaryReader reader) throws IOException {
if (!BootImageUtil.isBootImage(reader)) {
throw new IOException("BootImageHeader magic not found.");
}
int version = reader.readInt(BootImageConstants.HEADER_VERSION_OFFSET);
switch (version) {
case 0:
return new BootImageHeaderV0(reader);
case 1:
return new BootImageHeaderV1(reader);
case 2:
return new BootImageHeaderV2(reader);
case 3:
return new BootImageHeaderV3(reader);
case 4:
return new BootImageHeaderV4(reader);
default:
throw new IOException("BootImageHeader unsupported version found: " + version);
}
}
}

View File

@@ -0,0 +1,207 @@
/* ###
* 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.bootimg;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.*;
import ghidra.util.NumericUtilities;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/include/bootimg/bootimg.h#67
*/
public class BootImageHeaderV0 extends BootImageHeader {
private String magic;
private int kernel_size;
private int kernel_addr;
private int ramdisk_size;
private int ramdisk_addr;
private int second_size;
private int second_addr;
private int tags_addr;
private int page_size;
private int header_version;
private int os_version;
private String name;
private String cmdline;
private int[] id;
private String extra_cmdline;
public BootImageHeaderV0(BinaryReader reader) throws IOException {
magic = reader.readNextAsciiString(BootImageConstants.BOOT_MAGIC_SIZE);
kernel_size = reader.readNextInt();
kernel_addr = reader.readNextInt();
ramdisk_size = reader.readNextInt();
ramdisk_addr = reader.readNextInt();
second_size = reader.readNextInt();
second_addr = reader.readNextInt();
tags_addr = reader.readNextInt();
page_size = reader.readNextInt();
header_version = reader.readNextInt();
os_version = reader.readNextInt();
name = reader.readNextAsciiString(BootImageConstants.BOOT_NAME_SIZE);
cmdline = reader.readNextAsciiString(BootImageConstants.BOOT_ARGS_SIZE);
id = reader.readNextIntArray(BootImageConstants.ID_SIZE);
extra_cmdline = reader.readNextAsciiString(BootImageConstants.BOOT_EXTRA_ARGS_SIZE);
}
@Override
public String getMagic() {
return magic;
}
@Override
public int getKernelSize() {
return kernel_size;
}
@Override
public long getKernelOffset() {
return page_size;//see header comment...
}
public int getKernelAddress() {
return kernel_addr;
}
/**
* n = (kernel_size + page_size - 1) / page_size
*/
public int getKernelPageCount() {
return (int) NumericUtilities.getUnsignedAlignedValue(Integer.toUnsignedLong(kernel_size),
Integer.toUnsignedLong(page_size));
}
@Override
public int getRamdiskSize() {
return ramdisk_size;
}
@Override
public int getRamdiskOffset() {
return page_size + getKernelPageCount() * page_size;//see header comment...
}
public int getRamdiskAddress() {
return ramdisk_addr;
}
/**
* m = (ramdisk_size + page_size - 1) / page_size
*/
public int getRamdiskPageCount() {
return (int) NumericUtilities.getUnsignedAlignedValue(Integer.toUnsignedLong(ramdisk_size),
Integer.toUnsignedLong(page_size));
}
@Override
public int getSecondSize() {
return second_size;
}
@Override
public long getSecondOffset() {
return page_size + (getKernelPageCount() + getRamdiskPageCount()) * page_size;//see header comment...
}
public int getSecondAddress() {
return second_addr;
}
/**
* o = (second_size + page_size - 1) / page_size
*/
public int getSecondPageCount() {
return (int) NumericUtilities.getUnsignedAlignedValue(Integer.toUnsignedLong(second_size),
Integer.toUnsignedLong(page_size));
}
/**
* Physical address for kernel tags (if required)
* @return physical address for kernel tags
*/
public int getTagsAddress() {
return tags_addr;
}
@Override
public int getPageSize() {
return page_size;
}
/**
* Version of the boot image header.
* @return version of the boot image header
*/
public int getHeaderVersion() {
return header_version;
}
/**
* Operating system version and security patch level.
* For version "A.B.C" and patch level "Y-M-D":
* (7 bits for each of A, B, C; 7 bits for (Y-2000), 4 bits for M)
* os_version = A[31:25] B[24:18] C[17:11] (Y-2000)[10:4] M[3:0]
* @return OS version
*/
public int getOSVersion() {
return os_version;
}
public String getName() {
return name;
}
@Override
public String getCommandLine() {
return cmdline;
}
public int[] getId() {
return id;
}
public String getExtraCommandLine() {
return extra_cmdline;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = new StructureDataType("boot_img_hdr_v0", 0);
structure.add(UTF8, BootImageConstants.BOOT_MAGIC_SIZE, "magic", null);
structure.add(DWORD, "kernel_size", null);
structure.add(DWORD, "kernel_addr", null);
structure.add(DWORD, "ramdisk_size", null);
structure.add(DWORD, "ramdisk_addr", null);
structure.add(DWORD, "second_size", null);
structure.add(DWORD, "second_addr", null);
structure.add(DWORD, "tags_addr", null);
structure.add(DWORD, "page_size", null);
structure.add(DWORD, "header_version", null);
structure.add(DWORD, "os_version", BootImageUtil.getOSVersionString(os_version));
structure.add(UTF8, BootImageConstants.BOOT_NAME_SIZE, "name", null);
structure.add(UTF8, BootImageConstants.BOOT_ARGS_SIZE, "cmdline", null);
ArrayDataType array =
new ArrayDataType(DWORD, BootImageConstants.ID_SIZE, DWORD.getLength());
structure.add(array, "id", null);
structure.add(UTF8, BootImageConstants.BOOT_EXTRA_ARGS_SIZE, "extra_cmdline", null);
return structure;
}
}

View File

@@ -0,0 +1,75 @@
/* ###
* IP: LICENSE
*/
package ghidra.file.formats.android.bootimg;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.util.InvalidNameException;
import ghidra.util.NumericUtilities;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/include/bootimg/bootimg.h#156
*/
public class BootImageHeaderV1 extends BootImageHeaderV0 {
private int recovery_dtbo_size;
private long recovery_dtbo_offset;
private int header_size;
public BootImageHeaderV1(BinaryReader reader) throws IOException {
super(reader);
recovery_dtbo_size = reader.readNextInt();
recovery_dtbo_offset = reader.readNextLong();
header_size = reader.readNextInt();
}
/**
* Size in bytes for recovery DTBO/ACPIO image
* @return recovery DTBO/ACPIO image byte size
*/
public int getRecoveryDtboSize() {
return recovery_dtbo_size;
}
/**
* p = (recovery_dtbo_size + page_size - 1) / page_size
* @return the recovery DTBO adjusted size
*/
public int getRecoveryDtboSizeAdjusted() {
return (int) NumericUtilities.getUnsignedAlignedValue(
Integer.toUnsignedLong(recovery_dtbo_size), Integer.toUnsignedLong(getPageSize()));
}
/**
* Offset to recovery dtbo/acpio in boot image
* @return the recover DTBO offset
*/
public long getRecoveryDtboOffset() {
return recovery_dtbo_offset;
}
public int getHeaderSize() {
return header_size;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
try {
structure.setName("boot_img_hdr_v1");
}
catch (InvalidNameException e) {
//ignore
}
structure.add(DWORD, "recovery_dtbo_size", null);
structure.add(QWORD, "recovery_dtbo_offset", null);
structure.add(DWORD, "header_size", null);
return structure;
}
}

View File

@@ -0,0 +1,80 @@
/* ###
* 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.bootimg;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.util.InvalidNameException;
import ghidra.util.NumericUtilities;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/include/bootimg/bootimg.h#199
*/
public class BootImageHeaderV2 extends BootImageHeaderV1 {
private int dtb_size;
private long dtb_addr;
public BootImageHeaderV2(BinaryReader reader) throws IOException {
super(reader);
dtb_size = reader.readNextInt();
dtb_addr = reader.readNextLong();
}
/**
* Size in bytes for DTB image
* @return size in bytes for DTB image
*/
public int getDtbSize() {
return dtb_size;
}
/**
* q = (dtb_size + page_size - 1) / page_size
* @return the DTB adjusted size
*/
public int getDtbSizeAdjusted() {
return (int) NumericUtilities.getUnsignedAlignedValue(Integer.toUnsignedLong(dtb_size),
Integer.toUnsignedLong(getPageSize()));
}
/**
* Physical load address for DTB image
* @return physical load address for DTB image
*/
public long getDtbAddress() {
return dtb_addr;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
try {
structure.setName("boot_img_hdr_v2");
}
catch (InvalidNameException e) {
//ignore
}
structure.add(DWORD, "dtb_size", null);
structure.add(QWORD, "dtb_addr", null);
return structure;
}
}

View File

@@ -0,0 +1,158 @@
/* ###
* 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.bootimg;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.NumericUtilities;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/include/bootimg/bootimg.h#250
*/
public class BootImageHeaderV3 extends BootImageHeader {
private String magic;
private int kernel_size;
private int ramdisk_size;
private int os_version;
private int header_size;
private int[] reserved;// 4 elements
private int header_version;
private String cmdline;
public BootImageHeaderV3(BinaryReader reader) throws IOException {
magic = reader.readNextAsciiString(BootImageConstants.BOOT_MAGIC_SIZE);
kernel_size = reader.readNextInt();
ramdisk_size = reader.readNextInt();
os_version = reader.readNextInt();
header_size = reader.readNextInt();
reserved = reader.readNextIntArray(4);
header_version = reader.readNextInt();
cmdline = reader.readNextAsciiString(
BootImageConstants.BOOT_ARGS_SIZE + BootImageConstants.BOOT_EXTRA_ARGS_SIZE);
}
@Override
public String getMagic() {
return magic;
}
@Override
public int getPageSize() {
return BootImageConstants.V3_PAGE_SIZE;
}
@Override
public int getKernelSize() {
return kernel_size;
}
/**
* n = (kernel_size + page_size - 1) / page_size
*/
@Override
public int getKernelPageCount() {
return (int) NumericUtilities.getUnsignedAlignedValue(Integer.toUnsignedLong(kernel_size),
Integer.toUnsignedLong(BootImageConstants.V3_PAGE_SIZE));
}
@Override
public long getKernelOffset() {
return BootImageConstants.V3_PAGE_SIZE;//see header comment...
}
@Override
public int getRamdiskSize() {
return ramdisk_size;
}
/**
* m = (ramdisk_size + page_size - 1) / page_size
*/
@Override
public int getRamdiskPageCount() {
return (int) NumericUtilities.getUnsignedAlignedValue(Integer.toUnsignedLong(ramdisk_size),
Integer.toUnsignedLong(BootImageConstants.V3_PAGE_SIZE));
}
@Override
public int getRamdiskOffset() {
return BootImageConstants.V3_PAGE_SIZE +
(getKernelPageCount() * BootImageConstants.V3_PAGE_SIZE);
}
@Override
public long getSecondOffset() {
// v3 does not contain 2nd stage, just return 0
return 0;
}
@Override
public int getSecondSize() {
// v3 does not contain 2nd stage, just return 0
return 0;
}
@Override
public int getSecondPageCount() {
// v3 does not contain 2nd stage, just return 0
return 0;
}
public int getOSVersion() {
return os_version;
}
public int getHeaderSize() {
return header_size;
}
public int[] getReserved() {
return reserved;
}
public int getHeaderVersion() {
return header_version;
}
public String getCommandLine() {
return cmdline;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = new StructureDataType("boot_img_hdr_v3", 0);
structure.add(UTF8, BootImageConstants.BOOT_MAGIC_SIZE, "magic", null);
structure.add(DWORD, "kernel_size", null);
structure.add(DWORD, "ramdisk_size", null);
structure.add(DWORD, "os_version", BootImageUtil.getOSVersionString(os_version));
structure.add(DWORD, "header_size", null);
ArrayDataType array = new ArrayDataType(DWORD, 4, DWORD.getLength());
structure.add(array, "reserved", null);
structure.add(DWORD, "header_version", null);
structure.add(UTF8,
BootImageConstants.BOOT_ARGS_SIZE + BootImageConstants.BOOT_EXTRA_ARGS_SIZE, "cmdline",
null);
return structure;
}
}

View File

@@ -0,0 +1,59 @@
/* ###
* 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.bootimg;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/include/bootimg/bootimg.h#397
*/
public class BootImageHeaderV4 extends BootImageHeaderV3 {
private int signature_size;
public BootImageHeaderV4(BinaryReader reader) throws IOException {
super(reader);
}
public int getSignatureSize() {
return signature_size;
}
@Override
public int getPageSize() {
return BootImageConstants.V4_PAGE_SIZE;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
try {
structure.setName("boot_img_hdr_v4");
}
catch (InvalidNameException e) {
//ignore
}
structure.add(DWORD, "signature_size", null);
return structure;
}
}

View File

@@ -15,21 +15,67 @@
*/
package ghidra.file.formats.android.bootimg;
import java.util.Arrays;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import java.util.Arrays;
public final class BootImageUtil {
public class BootImageUtil {
public final static boolean isBootImage( Program program ) {
byte [] bytes = new byte[ 8 ];
public final static boolean isBootImage(Program program) {
byte[] bytes = new byte[BootImageConstants.BOOT_MAGIC_SIZE];
try {
Address address = program.getMinAddress();
program.getMemory().getBytes( address, bytes );
program.getMemory().getBytes(address, bytes);
}
catch (Exception e) {}
return Arrays.equals( bytes, BootImageConstants.BOOT_IMAGE_MAGIC_BYTES );
catch (Exception e) {
//ignore
}
return Arrays.equals(bytes, BootImageConstants.BOOT_MAGIC.getBytes());
}
public final static boolean isBootImage(BinaryReader reader) {
try {
String magic = reader.readAsciiString(0, BootImageConstants.BOOT_MAGIC_SIZE);
return BootImageConstants.BOOT_MAGIC.equals(magic);
}
catch (Exception e) {
//ignore
}
return false;
}
public final static boolean isVendorBootImage(Program program) {
byte[] bytes = new byte[BootImageConstants.VENDOR_BOOT_MAGIC_SIZE];
try {
Address address = program.getMinAddress();
program.getMemory().getBytes(address, bytes);
}
catch (Exception e) {
//ignore
}
return Arrays.equals(bytes, BootImageConstants.VENDOR_BOOT_MAGIC.getBytes());
}
public final static boolean isVendorBootImage(BinaryReader reader) {
try {
String magic = reader.readAsciiString(0, BootImageConstants.VENDOR_BOOT_MAGIC_SIZE);
return BootImageConstants.VENDOR_BOOT_MAGIC.equals(magic);
}
catch (Exception e) {
//ignore
}
return false;
}
public final static String getOSVersionString(int os_version) {
int a = (os_version & 0xfe000000) >>> 25;
int b = (os_version & 0x01fc0000) >>> 18;
int c = (os_version & 0x0003f800) >>> 11;
int y = (os_version & 0x000007f0) >>> 4;
int m = (os_version & 0x0000000f);
return a + "." + b + "." + c + "_" + y + "_" + m;
}
}

View File

@@ -22,31 +22,34 @@ import java.io.IOException;
public class PartitionTableEntry {
private byte [] name;
private byte[] name;
private int start;
private int length;
private int flags;
public PartitionTableEntry(ByteProvider provider) throws IOException {
this( new BinaryReader( provider, true ) );
this(new BinaryReader(provider, true));
}
public PartitionTableEntry(BinaryReader reader) throws IOException {
name = reader.readNextByteArray( 16 );
start = reader.readNextInt();
length = reader.readNextInt();
flags = reader.readNextInt();
name = reader.readNextByteArray(16);
start = reader.readNextInt();
length = reader.readNextInt();
flags = reader.readNextInt();
}
public String getName() {
return new String( name );
return new String(name);
}
public int getStart() {
return start;
}
public int getLength() {
return length;
}
public int getFlags() {
return flags;
}

View File

@@ -0,0 +1,116 @@
/* ###
* 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.bootimg;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import ghidra.app.util.bin.ByteProvider;
import ghidra.formats.gfilesystem.GFile;
import ghidra.formats.gfilesystem.GFileImpl;
import ghidra.formats.gfilesystem.GFileSystemBase;
import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
import ghidra.formats.gfilesystem.factory.GFileSystemBaseFactory;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.CryptoException;
import ghidra.util.task.TaskMonitor;
@FileSystemInfo(type = "androidvendorbootimg", description = "Android Vendor Boot Images", factory = GFileSystemBaseFactory.class)
public class VendorBootImageFileSystem extends GFileSystemBase {
private VendorBootImageHeader header;
private GFileImpl ramdiskFile;
private GFileImpl dtbFile;
private List<GFileImpl> fileList = new ArrayList<>();
public VendorBootImageFileSystem(String fileSystemName, ByteProvider provider) {
super(fileSystemName, provider);
}
@Override
public boolean isValid(TaskMonitor monitor) throws IOException {
byte[] bytes = provider.readBytes(0, BootImageConstants.VENDOR_BOOT_MAGIC_SIZE);
return Arrays.equals(bytes, BootImageConstants.VENDOR_BOOT_MAGIC.getBytes());
}
@Override
public void open(TaskMonitor monitor) throws IOException, CryptoException, CancelledException {
this.header = VendorBootImageHeaderFactory.getVendorBootImageHeader(provider, true);
if (!header.getMagic().equals(BootImageConstants.VENDOR_BOOT_MAGIC)) {
throw new IOException("Invalid Android boot image file!");
}
if (header.getVendorRamdiskSize() > 0) {
ramdiskFile = GFileImpl.fromFilename(this, root, BootImageConstants.RAMDISK, false,
header.getVendorRamdiskSize(), null);
fileList.add(ramdiskFile);
}
if (header.getDtbSize() > 0) {
dtbFile = GFileImpl.fromFilename(this, root, BootImageConstants.DTB, false,
header.getDtbSize(), null);
fileList.add(dtbFile);
}
}
@Override
public void close() throws IOException {
ramdiskFile = null;
dtbFile = null;
header = null;
super.close();
}
@Override
public List<GFile> getListing(GFile directory) throws IOException {
return (directory == null || directory.equals(root)) ? new ArrayList<>(fileList)
: Collections.emptyList();
}
@Override
public String getInfo(GFile file, TaskMonitor monitor) {
if (file == ramdiskFile) {
return "This is a ramdisk, it is a GZIP file containing a CPIO archive.";
}
else if (file == dtbFile) {
return "This is a DTB file. It appears unused at this time.";
}
return null;
}
@Override
protected InputStream getData(GFile file, TaskMonitor monitor)
throws IOException, CancelledException, CryptoException {
if (file == ramdiskFile) {
byte[] ramDiskBytes =
provider.readBytes(header.getVendorRamdiskOffset(), header.getVendorRamdiskSize());
return new ByteArrayInputStream(ramDiskBytes);
}
else if (file == dtbFile) {
byte[] dtbBytes = provider.readBytes(header.getDtbOffset(), header.getDtbSize());
return new ByteArrayInputStream(dtbBytes);
}
return null;
}
}

View File

@@ -0,0 +1,34 @@
/* ###
* 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.bootimg;
import ghidra.app.util.bin.StructConverter;
/**
*
*/
public abstract class VendorBootImageHeader implements StructConverter {
public abstract String getMagic();
public abstract long getVendorRamdiskOffset();
public abstract int getVendorRamdiskSize();
public abstract long getDtbOffset();
public abstract int getDtbSize();
}

View File

@@ -0,0 +1,50 @@
/* ###
* 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.bootimg;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
public final class VendorBootImageHeaderFactory {
public final static VendorBootImageHeader getVendorBootImageHeader(ByteProvider provider,
boolean littleEndian) throws IOException {
return getVendorBootImageHeader(new BinaryReader(provider, littleEndian));
}
public final static VendorBootImageHeader getVendorBootImageHeader(BinaryReader reader)
throws IOException {
if (!BootImageUtil.isVendorBootImage(reader)) {
throw new IOException("VendorBootImageHeader magic not found.");
}
int version = reader.readInt(BootImageConstants.VENDOR_BOOT_MAGIC_SIZE);
switch (version) {
case 3:
return new VendorBootImageHeaderV3(reader);
case 4:
return new VendorBootImageHeaderV4(reader);
default:
throw new IOException(
"VendorBootImageHeader unsupported version found: " + version);
}
}
}

View File

@@ -0,0 +1,166 @@
/* ###
* 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.bootimg;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/include/bootimg/bootimg.h#286
* <pre>
* The structure of the vendor boot image (introduced with version 3 and
* required to be present when a v3 boot image is used) is as follows:
*
* +---------------------+
* | vendor boot header | o pages
* +---------------------+
* | vendor ramdisk | p pages
* +---------------------+
* | dtb | q pages
* +---------------------+
* o = (2112 + page_size - 1) / page_size
* p = (vendor_ramdisk_size + page_size - 1) / page_size
* q = (dtb_size + page_size - 1) / page_size
*
* 0. all entities in the boot image are 4096-byte aligned in flash, all
* entities in the vendor boot image are page_size (determined by the vendor
* and specified in the vendor boot image header) aligned in flash
* 1. kernel, ramdisk, vendor ramdisk, and DTB are required (size != 0)
* 2. load the kernel and DTB at the specified physical address (kernel_addr,
* dtb_addr)
* 3. load the vendor ramdisk at ramdisk_addr
* 4. load the generic ramdisk immediately following the vendor ramdisk in
* memory
* 5. set up registers for kernel entry as required by your architecture
* 6. if the platform has a second stage bootloader jump to it (must be
* contained outside boot and vendor boot partitions), otherwise
* jump to kernel_addr
* </pre>
*
*/
public class VendorBootImageHeaderV3 extends VendorBootImageHeader {
private String magic;
private int header_version;
private int page_size;
private int kernel_addr;
private int ramdisk_addr;
private int vendor_ramdisk_size;
private String cmdline;
private int tags_addr;
private String name;
private int header_size;
private int dtb_size;
private long dtb_addr;
public VendorBootImageHeaderV3(BinaryReader reader) throws IOException {
magic = reader.readNextAsciiString(BootImageConstants.VENDOR_BOOT_MAGIC_SIZE);
header_version = reader.readNextInt();
page_size = reader.readNextInt();
kernel_addr = reader.readNextInt();
ramdisk_addr = reader.readNextInt();
vendor_ramdisk_size = reader.readNextInt();
cmdline = reader.readNextAsciiString(BootImageConstants.VENDOR_BOOT_ARGS_SIZE);
tags_addr = reader.readNextInt();
name = reader.readNextAsciiString(BootImageConstants.VENDOR_BOOT_NAME_SIZE);
header_size = reader.readNextInt();
dtb_size = reader.readNextInt();
dtb_addr = reader.readNextLong();
}
public String getMagic() {
return magic;
}
public int getHeaderVersion() {
return header_version;
}
public int getPageSize() {
return page_size;
}
public int getKernelAddress() {
return kernel_addr;
}
public int getRamdiskAddress() {
return ramdisk_addr;
}
public int getVendorRamdiskSize() {
return vendor_ramdisk_size;
}
@Override
public long getVendorRamdiskOffset() {
return page_size;
}
public String getCmdline() {
return cmdline;
}
public int getTagsAddress() {
return tags_addr;
}
public String getName() {
return name;
}
public int getHeaderSize() {
return header_size;
}
public int getDtbSize() {
return dtb_size;
}
public long getDtbAddress() {
return dtb_addr;
}
@Override
public long getDtbOffset() {
int o = ((2112 + page_size - 1) / page_size);
int p = ((vendor_ramdisk_size + page_size - 1) / page_size);
return (o + p) * page_size;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = new StructureDataType("vendor_boot_img_hdr_v3", 0);
structure.add(UTF8, BootImageConstants.VENDOR_BOOT_MAGIC_SIZE, "magic", null);
structure.add(DWORD, "header_version", null);
structure.add(DWORD, "page_size", null);
structure.add(DWORD, "kernel_addr", null);
structure.add(DWORD, "ramdisk_addr", null);
structure.add(DWORD, "vendor_ramdisk_size", null);
structure.add(UTF8, BootImageConstants.VENDOR_BOOT_ARGS_SIZE, "cmdline", null);
structure.add(DWORD, "tags_addr", null);
structure.add(UTF8, BootImageConstants.VENDOR_BOOT_NAME_SIZE, "name", null);
structure.add(DWORD, "header_size", null);
structure.add(DWORD, "dtb_size", null);
structure.add(QWORD, "dtb_addr", null);
return structure;
}
}

View File

@@ -0,0 +1,159 @@
/* ###
* 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.bootimg;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/include/bootimg/bootimg.h#401
* <pre>
* The structure of the vendor boot image version 4, which is required to be
* present when a version 4 boot image is used, is as follows:
*
* +------------------------+
* | vendor boot header | o pages
* +------------------------+
* | vendor ramdisk section | p pages
* +------------------------+
* | dtb | q pages
* +------------------------+
* | vendor ramdisk table | r pages
* +------------------------+
* | bootconfig | s pages
* +------------------------+
*
* o = (2128 + page_size - 1) / page_size
* p = (vendor_ramdisk_size + page_size - 1) / page_size
* q = (dtb_size + page_size - 1) / page_size
* r = (vendor_ramdisk_table_size + page_size - 1) / page_size
* s = (vendor_bootconfig_size + page_size - 1) / page_size
*
* Note that in version 4 of the vendor boot image, multiple vendor ramdisks can
* be included in the vendor boot image. The bootloader can select a subset of
* ramdisks to load at runtime. To help the bootloader select the ramdisks, each
* ramdisk is tagged with a type tag and a set of hardware identifiers
* describing the board, soc or platform that this ramdisk is intended for.
*
* The vendor ramdisk section is consist of multiple ramdisk images concatenated
* one after another, and vendor_ramdisk_size is the size of the section, which
* is the total size of all the ramdisks included in the vendor boot image.
*
* The vendor ramdisk table holds the size, offset, type, name and hardware
* identifiers of each ramdisk. The type field denotes the type of its content.
* The hardware identifiers are specified in the board_id field in each table
* entry. The board_id field is consist of a vector of unsigned integer words,
* and the encoding scheme is defined by the hardware vendor.
*
* For the different type of ramdisks, there are:
* - VENDOR_RAMDISK_TYPE_NONE indicates the value is unspecified.
* - VENDOR_RAMDISK_TYPE_PLATFORM ramdisk contains platform specific bits.
* - VENDOR_RAMDISK_TYPE_RECOVERY ramdisk contains recovery resources.
* - VENDOR_RAMDISK_TYPE_DLKM ramdisk contains dynamic loadable kernel
* modules.
*
* Version 4 of the vendor boot image also adds a bootconfig section to the end
* of the image. This section contains Boot Configuration parameters known at
* build time. The bootloader is responsible for placing this section directly
* after the generic ramdisk, followed by the bootconfig trailer, before
* entering the kernel.
*
* 0. all entities in the boot image are 4096-byte aligned in flash, all
* entities in the vendor boot image are page_size (determined by the vendor
* and specified in the vendor boot image header) aligned in flash
* 1. kernel, ramdisk, and DTB are required (size != 0)
* 2. load the kernel and DTB at the specified physical address (kernel_addr,
* dtb_addr)
* 3. load the vendor ramdisks at ramdisk_addr
* 4. load the generic ramdisk immediately following the vendor ramdisk in
* memory
* 5. load the bootconfig immediately following the generic ramdisk. Add
* additional bootconfig parameters followed by the bootconfig trailer.
* 6. set up registers for kernel entry as required by your architecture
* 7. if the platform has a second stage bootloader jump to it (must be
* contained outside boot and vendor boot partitions), otherwise
* jump to kernel_addr
* </pre>
*
*/
public class VendorBootImageHeaderV4 extends VendorBootImageHeaderV3 {
private int vendor_ramdisk_table_size;
private int vendor_ramdisk_table_entry_num;
private int vendor_ramdisk_table_entry_size;
private int bootconfig_size;
public VendorBootImageHeaderV4(BinaryReader reader) throws IOException {
super(reader);
vendor_ramdisk_table_size = reader.readNextInt();
vendor_ramdisk_table_entry_num = reader.readNextInt();
vendor_ramdisk_table_entry_size = reader.readNextInt();
bootconfig_size = reader.readNextInt();
}
/**
* Size in bytes for the vendor ramdisk table
* @return size in bytes for the vendor ramdisk table
*/
public int getVendorRamdiskTableSize() {
return vendor_ramdisk_table_size;
}
/**
* Number of entries in the vendor ramdisk table
* @return number of entries in the vendor ramdisk table
*/
public int getVendorRamdiskTableEntryNum() {
return vendor_ramdisk_table_entry_num;
}
/**
* Size in bytes for a vendor ramdisk table entry
* @return size in bytes for a vendor ramdisk table entry
*/
public int getVendorRamdiskTableEntrySize() {
return vendor_ramdisk_table_entry_size;
}
/**
* Size in bytes for the bootconfig section
* @return size in bytes for the bootconfig section
*/
public int getBootConfigSize() {
return bootconfig_size;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
try {
structure.setName("vendor_boot_img_hdr_v4");
}
catch (InvalidNameException e) {
//ignore
}
structure.add(DWORD, "vendor_ramdisk_table_size", null);
structure.add(DWORD, "vendor_ramdisk_table_entry_num", null);
structure.add(DWORD, "vendor_ramdisk_table_entry_size", null);
structure.add(DWORD, "bootconfig_size", null);
return structure;
}
}

View File

@@ -0,0 +1,97 @@
/* ###
* 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.bootimg;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
public class VendorRamdiskTableEntryV4 implements StructConverter {
private int ramdisk_size;
private int ramdisk_offset;
private int ramdisk_type;
private String ramdisk_name;
private int[] board_id;
public VendorRamdiskTableEntryV4(BinaryReader reader) throws IOException {
ramdisk_size = reader.readNextInt();
ramdisk_offset = reader.readNextInt();
ramdisk_type = reader.readNextInt();
ramdisk_name = reader.readNextAsciiString(BootImageConstants.VENDOR_RAMDISK_NAME_SIZE);
board_id =
reader.readNextIntArray(BootImageConstants.VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE);
}
/**
* Size in bytes for the ramdisk image
* @return ramdisk size
*/
public int getRamdiskSize() {
return ramdisk_size;
}
/**
* Offset to the ramdisk image in vendor ramdisk section
* @return ramdisk offset
*/
public int getRamdiskOffset() {
return ramdisk_offset;
}
/**
* Type of the ramdisk
* @return ramdisk type
*/
public int getRamdiskType() {
return ramdisk_type;
}
/**
* Ascii ramdisk name
* @return the ascii ramdisk name
*/
public String getRamdiskName() {
return ramdisk_name;
}
/**
* Hardware identifiers describing the board, soc or platform
* which this ramdisk is intended to be loaded on.
* @return the board ID
*/
public int[] getBoardID() {
return board_id;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = new StructureDataType("vendor_ramdisk_table_entry_v4", 0);
structure.add(DWORD, "ramdisk_size", null);
structure.add(DWORD, "ramdisk_offset", null);
structure.add(DWORD, "ramdisk_type", null);
structure.add(UTF8, BootImageConstants.VENDOR_RAMDISK_NAME_SIZE, "ramdisk_name", null);
for (int i = 0; i < BootImageConstants.VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE; ++i) {
structure.add(DWORD, "board_id_" + i, null);
}
return structure;
}
}

View File

@@ -0,0 +1,94 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.file.formats.android.bootldr;
import ghidra.app.services.AbstractAnalyzer;
import ghidra.app.services.AnalyzerType;
import ghidra.app.util.bin.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Annotates the structures contained in an Android Boot Loader image.
*/
public class AndroidBootLoaderAnalyzer extends AbstractAnalyzer {
public AndroidBootLoaderAnalyzer() {
super("Android Boot Loader", "Annotates the Android Boot Loader header components",
AnalyzerType.BYTE_ANALYZER);
}
@Override
public boolean getDefaultEnablement(Program program) {
return AndroidBootLoaderConstants.isBootLoader(program);
}
@Override
public boolean canAnalyze(Program program) {
return AndroidBootLoaderConstants.isBootLoader(program);
}
@Override
public void registerOptions(Options options, Program program) {
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {
AddressSpace addressSpace = program.getAddressFactory().getDefaultAddressSpace();
Address headerAddress = program.getMinAddress();
ByteProvider provider = new MemoryByteProvider(program.getMemory(), headerAddress);
BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian());
try {
AndroidBootLoaderHeader header = new AndroidBootLoaderHeader(reader);
DataType headerDataType = header.toDataType();
Data headerData = program.getListing().createData(headerAddress, headerDataType);
if (headerData == null) {
log.appendMsg("Unable to apply header data, stopping.");
return false;
}
SymbolTable symbolTable = program.getSymbolTable();
Symbol headerSymbol = symbolTable.getPrimarySymbol(headerAddress);
if (headerSymbol == null) {
symbolTable.createLabel(headerAddress, header.getMagic(), SourceType.ANALYSIS);
}
else {
headerSymbol.setName(header.getMagic(), SourceType.ANALYSIS);
}
int runningOffset = header.getStartOffset();
for (AndroidBootLoaderImageInfo imageInfo : header.getImageInfoList()) {
Address address = addressSpace.getAddress(runningOffset);
symbolTable.createLabel(address, imageInfo.getName(), SourceType.ANALYSIS);
program.getBookmarkManager()
.setBookmark(address, BookmarkType.ANALYSIS, "boot", imageInfo.getName());
runningOffset += imageInfo.getSize();
}
return true;
}
catch (Exception e) {
log.appendException(e);
}
return false;
}
}

View File

@@ -0,0 +1,63 @@
/* ###
* 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.bootldr;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
/**
* Source: https://android.googlesource.com/device/lge/mako/+/android-4.2.2_r1/releasetools.py
* #
* # #define BOOTLDR_MAGIC "BOOTLDR!"
* # #define BOOTLDR_MAGIC_SIZE 8
* #
* # struct bootloader_images_header {
* # char magic[BOOTLDR_MAGIC_SIZE];
* # unsigned int num_images;
* # unsigned int start_offset;
* # unsigned int bootldr_size;
* # struct {
* # char name[64];
* # unsigned int size;
* # } img_info[];
* # };
*/
public final class AndroidBootLoaderConstants {
public static final String BOOTLDR_NAME = "bootloader_images_header";
public static final String BOOTLDR_MAGIC = "BOOTLDR!";
public static final int BOOTLDR_MAGIC_SIZE = BOOTLDR_MAGIC.length();
public static final String IMG_INFO_NAME = "img_info";
public static final int IMG_INFO_NAME_LENGTH = 64;
public static boolean isBootLoader(Program program) {
try {
Memory memory = program.getMemory();
byte[] bytes = new byte[AndroidBootLoaderConstants.BOOTLDR_MAGIC_SIZE];
memory.getBytes(program.getMinAddress(), bytes);
String magic = new String(bytes).trim();
return AndroidBootLoaderConstants.BOOTLDR_MAGIC.equals(magic);
}
catch (Exception e) {
//ignore
}
return false;
}
}

View File

@@ -0,0 +1,88 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.file.formats.android.bootldr;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.formats.gfilesystem.GFile;
import ghidra.formats.gfilesystem.GFileImpl;
import ghidra.formats.gfilesystem.GFileSystemBase;
import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
import ghidra.formats.gfilesystem.factory.GFileSystemBaseFactory;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.CryptoException;
import ghidra.util.task.TaskMonitor;
@FileSystemInfo(type = "androidbootloader", // ([a-z0-9]+ only)
description = "Android Boot Loader Image", factory = GFileSystemBaseFactory.class)
public class AndroidBootLoaderFileSystem extends GFileSystemBase {
private List<GFileImpl> fileList = new ArrayList<>();
private List<Integer> offsetList = new ArrayList<>();
public AndroidBootLoaderFileSystem(String fileSystemName, ByteProvider provider) {
super(fileSystemName, provider);
}
@Override
public boolean isValid(TaskMonitor monitor) throws IOException {
byte[] bytes = provider.readBytes(0, AndroidBootLoaderConstants.BOOTLDR_MAGIC_SIZE);
return AndroidBootLoaderConstants.BOOTLDR_MAGIC.equals(new String(bytes).trim());
}
@Override
public void open(TaskMonitor monitor) throws IOException, CryptoException, CancelledException {
BinaryReader reader = new BinaryReader(provider, true /*might not always be LE*/ );
AndroidBootLoaderHeader header = new AndroidBootLoaderHeader(reader);
int runningOffset = header.getStartOffset();
for (AndroidBootLoaderImageInfo imageInfo : header.getImageInfoList()) {
GFileImpl file = GFileImpl.fromFilename(this, root, imageInfo.getName(), false,
imageInfo.getSize(), null);
fileList.add(file);
offsetList.add(runningOffset);
runningOffset += imageInfo.getSize();
}
}
@Override
public List<GFile> getListing(GFile directory) throws IOException {
return new ArrayList<>(fileList);
}
@Override
public String getInfo(GFile file, TaskMonitor monitor) {
return null;
}
@Override
protected InputStream getData(GFile file, TaskMonitor monitor)
throws IOException, CancelledException, CryptoException {
int index = fileList.indexOf(file);
int offset = offsetList.get(index);
return provider.getInputStream(offset);
}
}

View File

@@ -0,0 +1,86 @@
/* ###
* 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.bootldr;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
/**
* Class to represent the Android boot loader header.
*
*/
public class AndroidBootLoaderHeader implements StructConverter {
private String magic;
private int numberOfImages;
private int startOffset;
private int bootLoaderSize;
private List<AndroidBootLoaderImageInfo> imageInfoList =
new ArrayList<AndroidBootLoaderImageInfo>();
public AndroidBootLoaderHeader(BinaryReader reader) throws IOException {
magic = reader.readNextAsciiString(AndroidBootLoaderConstants.BOOTLDR_MAGIC_SIZE);
numberOfImages = reader.readNextInt();
startOffset = reader.readNextInt();
bootLoaderSize = reader.readNextInt();
for (int i = 0; i < numberOfImages; ++i) {
imageInfoList.add(new AndroidBootLoaderImageInfo(reader));
}
}
public String getMagic() {
return magic;
}
public int getNumberOfImages() {
return numberOfImages;
}
public int getStartOffset() {
return startOffset;
}
public int getBootLoaderSize() {
return bootLoaderSize;
}
public List<AndroidBootLoaderImageInfo> getImageInfoList() {
return new ArrayList<AndroidBootLoaderImageInfo>(imageInfoList);
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure struct = new StructureDataType(
AndroidBootLoaderConstants.BOOTLDR_NAME + "_" + numberOfImages, 0);
struct.add(STRING, AndroidBootLoaderConstants.BOOTLDR_MAGIC_SIZE, "magic", null);
struct.add(DWORD, "num_images", null);
struct.add(DWORD, "start_offset", null);
struct.add(DWORD, "bootldr_size", null);
for (int i = 0; i < numberOfImages; ++i) {
struct.add(imageInfoList.get(i).toDataType(), "img_info[" + i + "]", null);
}
return struct;
}
}

View File

@@ -0,0 +1,53 @@
/* ###
* 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.bootldr;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
public class AndroidBootLoaderImageInfo implements StructConverter {
private String name;
private int size;
public AndroidBootLoaderImageInfo(BinaryReader reader) throws IOException {
name = reader.readNextAsciiString(AndroidBootLoaderConstants.IMG_INFO_NAME_LENGTH).trim();
size = reader.readNextInt();
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure struct = new StructureDataType(AndroidBootLoaderConstants.IMG_INFO_NAME, 0);
struct.add(STRING, AndroidBootLoaderConstants.IMG_INFO_NAME_LENGTH, "magic", null);
struct.add(DWORD, "size", null);
return struct;
}
}

View File

@@ -0,0 +1,137 @@
/* ###
* 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.cdex;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.file.formats.android.dex.format.CodeItem;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* https://android.googlesource.com/platform/art/+/refs/heads/android10-release/libdexfile/dex/compact_dex_file.h
* <br>
* https://android.googlesource.com/platform/art/+/master/libdexfile/dex/compact_dex_file.h
*/
public class CDexCodeItem extends CodeItem {
public final static short kRegistersSizeShift = 12;
public final static short kInsSizeShift = 8;
public final static short kOutsSizeShift = 4;
public final static short kTriesSizeSizeShift = 0;
public final static short kInsnsSizeShift = 5;
public final static short kBitPreHeaderRegisterSize = 0;
public final static short kBitPreHeaderInsSize = 1;
public final static short kBitPreHeaderOutsSize = 2;
public final static short kBitPreHeaderTriesSize = 3;
public final static short kBitPreHeaderInsnsSize = 4;
public final static short kFlagPreHeaderRegisterSize = 0x1 << kBitPreHeaderRegisterSize;
public final static short kFlagPreHeaderInsSize = 0x1 << kBitPreHeaderInsSize;
public final static short kFlagPreHeaderOutsSize = 0x1 << kBitPreHeaderOutsSize;
public final static short kFlagPreHeaderTriesSize = 0x1 << kBitPreHeaderTriesSize;
public final static short kFlagPreHeaderInsnsSize = 0x1 << kBitPreHeaderInsnsSize;
public final static short kFlagPreHeaderCombined =
kFlagPreHeaderRegisterSize | kFlagPreHeaderInsSize | kFlagPreHeaderOutsSize |
kFlagPreHeaderTriesSize | kFlagPreHeaderInsnsSize;
private short fields_;
private short insns_count_and_flags_;
public CDexCodeItem(BinaryReader reader) throws IOException {
super();
long startIndex = reader.getPointerIndex();//used for reading preheaders...
/*
* Packed code item data,
* 4 bits each: [registers_size, ins_size, outs_size, tries_size]
*/
fields_ = reader.readNextShort();
registersSize = (short) ((fields_ >> kRegistersSizeShift) & 0xf);
incomingSize = (short) ((fields_ >> kInsSizeShift) & 0xf);
outgoingSize = (short) ((fields_ >> kOutsSizeShift) & 0xf);
triesSize = (short) ((fields_ >> kOutsSizeShift) & 0xf);
/*
* 5 bits, if either of the fields required preheader extension,
* 11 bits for the number of instruction code units.
*/
insns_count_and_flags_ = reader.readNextShort();
instructionSize = (Short.toUnsignedInt(insns_count_and_flags_) >> kInsnsSizeShift);
if (hasPreHeader()) {
if (hasPreHeader(kFlagPreHeaderInsnsSize)) {
startIndex -= 2;
instructionSize += reader.readShort(startIndex);
startIndex -= 2;
instructionSize += (reader.readShort(startIndex) << 16);
}
if (hasPreHeader(kFlagPreHeaderRegisterSize)) {
startIndex -= 2;
registersSize += reader.readShort(startIndex);
}
if (hasPreHeader(kFlagPreHeaderInsSize)) {
startIndex -= 2;
incomingSize += reader.readShort(startIndex);
}
if (hasPreHeader(kFlagPreHeaderOutsSize)) {
startIndex -= 2;
outgoingSize += reader.readShort(startIndex);
}
if (hasPreHeader(kFlagPreHeaderTriesSize)) {
startIndex -= 2;
triesSize += reader.readShort(startIndex);
}
}
if (getInstructionSize() == 0) {
instructionBytes = new byte[0];
instructions = new short[0];
}
else {
instructionBytes = reader.readNextByteArray(getInstructionSize() * 2);
instructions = reader.readNextShortArray(getInstructionSize());
}
}
public boolean hasPreHeader() {
return (insns_count_and_flags_ & kFlagPreHeaderCombined) != 0;
}
public boolean hasPreHeader(short flag) {
return (insns_count_and_flags_ & flag) != 0;
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
String name = "cdex_code_item" + "_" + (getInstructionSize() * 2);
Structure structure = new StructureDataType(name, 0);
structure.add(WORD, "fields_", null);
structure.add(WORD, "insns_count_and_flags_", null);
if (getInstructionSize() > 0) {
structure.add(new ArrayDataType(WORD, getInstructionSize(), WORD.getLength()), "insns_",
null);
}
structure.setCategoryPath(new CategoryPath("/dex/cdex_code_item"));
return structure;
}
}

View File

@@ -0,0 +1,73 @@
/* ###
* 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.cdex;
import ghidra.program.model.listing.Program;
/**
* Android .CDEX files.
*
* CompactDex is a currently ART internal dex file format that aims to reduce
* storage/RAM usage.
*
* https://android.googlesource.com/platform/art/+/master/runtime/dex/compact_dex_file.h
*
* https://android.googlesource.com/platform/art/+/master/libdexfile/dex/compact_dex_file.h
*/
public final class CDexConstants {
public final static String NAME = "Compact Dalvik Executable (CDEX)";
/**
* <pre>
* static constexpr uint8_t kDexMagic[kDexMagicSize] = { 'c', 'd', 'e', 'x' };
* </pre>
*/
public final static String MAGIC = "cdex";
/**
* <pre>
* static constexpr uint8_t kDexMagicVersion[] = {'0', '0', '1', '\0'};
* </pre>
*/
public final static String VERSION_001 = "001";
/**
* <pre>
* static constexpr uint8_t kDexMagicVersion[] = {'0', '0', '2', '\0'};
* </pre>
*/
public final static String VERSION_002 = "002";
/**
* Returns true if the given program contain CDEX information.
* @param program the program to inspect
* @return true if the given program contain CDEX information
*/
public final static boolean isCDEX(Program program) {
if (program != null) {
try {
byte[] bytes = new byte[MAGIC.length()];
program.getMemory().getBytes(program.getMinAddress(), bytes);
return MAGIC.equals(new String(bytes));
}
catch (Exception e) {
//ignore
}
}
return false;
}
}

View File

@@ -0,0 +1,136 @@
/* ###
* 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.cdex;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.file.formats.android.dex.format.DexHeader;
import ghidra.program.model.data.*;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
/**
* CDEX Header extends DEX header, but adds additional members
*
* class Header : public DexFile::Header {
*
*
* https://android.googlesource.com/platform/art/+/master/libdexfile/dex/compact_dex_file.h
*/
public class CDexHeader extends DexHeader {
private int feature_flags_;
private int debug_info_offsets_pos_;
private int debug_info_offsets_table_offset_;
private int debug_info_base_;
private int owned_data_begin_;
private int owned_data_end_;
public CDexHeader(BinaryReader reader) throws IOException {
super(reader);
feature_flags_ = reader.readNextInt();
debug_info_offsets_pos_ = reader.readNextInt();
debug_info_offsets_table_offset_ = reader.readNextInt();
debug_info_base_ = reader.readNextInt();
owned_data_begin_ = reader.readNextInt();
owned_data_end_ = reader.readNextInt();
}
public int getFeatureFlags() {
return feature_flags_;
}
/**
* Position in the compact dex file for the debug info table data starts.
* @return position in the compact dex file for the debug info table data starts
*/
public int getDebugInfoOffsetsPos() {
return debug_info_offsets_pos_;
}
/**
* Offset into the debug info table data where the lookup table exists.
* @return offset into the debug info table data where the lookup table is.
*/
public int getDebugInfoOffsetsTableOffset() {
return debug_info_offsets_table_offset_;
}
/**
* Base offset of where debug info starts in the dex file.
* @return base offset of where debug info starts in the dex file
*/
public int getDebugInfoBase() {
return debug_info_base_;
}
/**
* Range of the shared data section owned by the dex file.
* @return range of the shared data section owned by the dex file
*/
public int getOwnedDataBegin() {
return owned_data_begin_;
}
/**
* Range of the shared data section owned by the dex file.
* @return range of the shared data section owned by the dex file.
*/
public int getOwnedDataEnd() {
return owned_data_end_;
}
@Override
public boolean isDataOffsetRelative() {
return true;
}
@Override
protected void checkMagic() throws IOException {
if (!CDexConstants.MAGIC.equals(new String(getMagic()))) {
throw new IOException("not a cdex file.");
}
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = (Structure) super.toDataType();
try {
structure.setName("cdex_header");
}
catch (InvalidNameException e) {
//ignore, can't happen
}
structure.setCategoryPath(new CategoryPath("/cdex"));
structure.add(DWORD, "feature_flags_", null);
structure.add(DWORD, "debug_info_offsets_pos_", null);
structure.add(DWORD, "debug_info_offsets_table_offset_", null);
structure.add(DWORD, "debug_info_base_", null);
structure.add(DWORD, "owned_data_begin_", null);
structure.add(DWORD, "owned_data_end_", null);
// remove comments to prevent data type conflicts
for (DataTypeComponent component : structure.getComponents()) {
component.setComment(null);
}
return structure;
}
}

View File

@@ -0,0 +1,179 @@
/* ###
* 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.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;
public class CDexLoader extends DexLoader {
@Override
public String getName() {
return CDexConstants.NAME;
}
@Override
public LoaderTier getTier() {
return LoaderTier.UNTARGETED_LOADER;
}
@Override
public int getTierPriority() {
return 100;
}
@Override
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
List<LoadSpec> loadSpecs = new ArrayList<>();
BinaryReader reader = new BinaryReader(provider, true);
try {
byte[] magicBytes = provider.readBytes(0, CDexConstants.MAGIC.length());
if (CDexConstants.MAGIC.equals(new String(magicBytes))) {
DexHeader header = DexHeaderFactory.getDexHeader(reader);//should be CDEX
if (CDexConstants.MAGIC.equals(new String(header.getMagic()))) {
List<QueryResult> queries =
QueryOpinionService.query(getName(), DexConstants.MACHINE, null);
for (QueryResult result : queries) {
loadSpecs.add(new LoadSpec(this, 0, result));
}
if (loadSpecs.isEmpty()) {
loadSpecs.add(new LoadSpec(this, 0, true));
}
}
}
}
catch (Exception e) {
//ignore
}
return loadSpecs;
}
@Override
public boolean supportsLoadIntoProgram() {
return true;
}
@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);
}
}
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);
}
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);
}
}

View File

@@ -0,0 +1,21 @@
/* ###
* 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.cdex;
public final class FeatureFlags {
public final static int kDefaultMethods = 0x1;
}

View File

@@ -0,0 +1,91 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.file.formats.android.dex;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.MemoryByteProvider;
import ghidra.file.formats.android.cdex.CDexConstants;
import ghidra.file.formats.android.cdex.CDexHeader;
import ghidra.file.formats.android.dex.format.DexConstants;
import ghidra.file.formats.android.dex.format.DexHeader;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
public final class DexHeaderFactory {
/**
* Attempts to create DEX header starting at minimum address of the program.
* @param program the program to use to create DEX header
* @return the DEX header
* @throws IOException should an error occur reading DEX bytes
*/
public final static DexHeader getDexHeader(Program program) throws IOException {
return getDexHeader(program, program.getMinAddress());
}
/**
* Attempts to create DEX header starting at the specified address of the program.
* @param program the program to use to create DEX header
* @param address the address in the program to look for DEX header
* @return the DEX header
* @throws IOException should an error occur reading DEX bytes
*/
public final static DexHeader getDexHeader(Program program, Address address)
throws IOException {
ByteProvider provider = new MemoryByteProvider(program.getMemory(), address);
return getDexHeader(provider, !program.getLanguage().isBigEndian());
}
/**
* Attempts to create DEX header using the specified Byte Provider.
* @param provider the byte provider to use to create DEX header
* @param isLittleEndian true if LE, false if BE
* @return the DEX header
* @throws IOException should an error occur reading DEX bytes
*/
public final static DexHeader getDexHeader(ByteProvider provider, boolean isLittleEndian)
throws IOException {
BinaryReader reader = new BinaryReader(provider, isLittleEndian);
return getDexHeader(reader);
}
/**
* 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
* @return the DEX header
* @throws IOException should an error occur reading DEX bytes
*/
public final static DexHeader getDexHeader(BinaryReader reader) 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);
return header;
}
if (CDexConstants.MAGIC.equals(magic)) {
CDexHeader header = new CDexHeader(reader);
header.parse(reader);
return header;
}
throw new IOException("Not a recognized DEX/CDEX variant: " + magic);
}
}

View File

@@ -25,7 +25,8 @@ class DexToJarExceptionHandler implements DexExceptionHandler {
private Exception e;
@Override
public void handleMethodTranslateException(Method method, DexMethodNode node, MethodVisitor visitor, Exception e) {
public void handleMethodTranslateException(Method method, DexMethodNode node,
MethodVisitor visitor, Exception e) {
this.e = e;
}

View File

@@ -21,12 +21,14 @@ import java.util.TreeMap;
import ghidra.app.plugin.core.analysis.AnalysisState;
import ghidra.app.plugin.core.analysis.AnalysisStateInfo;
import ghidra.app.util.bin.*;
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;
import ghidra.util.SystemUtilities;
/**
* This class is used to cache the {@link DexHeader} which holds constant pool and method information.
@@ -52,8 +54,8 @@ final public class DexAnalysisState implements AnalysisState {
private void installMethodList(AddressSpace defaultAddressSpace,
List<EncodedMethod> methodList) {
for (EncodedMethod encodedMethod : methodList) {
Address methodAddress = defaultAddressSpace.getAddress(
DexUtil.METHOD_ADDRESS + encodedMethod.getCodeOffset());
Address methodAddress = defaultAddressSpace
.getAddress(DexUtil.METHOD_ADDRESS + encodedMethod.getCodeOffset());
methodMap.put(methodAddress, encodedMethod);
}
}
@@ -108,10 +110,21 @@ final public class DexAnalysisState implements AnalysisState {
DexAnalysisState analysisState =
AnalysisStateInfo.getAnalysisState(program, DexAnalysisState.class);
if (analysisState == null) {
ByteProvider provider =
new MemoryByteProvider(program.getMemory(), program.getMinAddress());
BinaryReader reader = new BinaryReader(provider, true);
DexHeader dexHeader = new DexHeader(reader);
DexHeader dexHeader = DexHeaderFactory.getDexHeader(program);
analysisState = new DexAnalysisState(program, dexHeader);
AnalysisStateInfo.putAnalysisState(program, analysisState);
}
return analysisState;
}
public static DexAnalysisState getState(Program program, Address address) throws IOException {
DexAnalysisState analysisState =
AnalysisStateInfo.getAnalysisState(program, DexAnalysisState.class);
if (SystemUtilities.isInDevelopmentMode()) {
analysisState = null; //always generate when in debug mode
}
if (analysisState == null) {
DexHeader dexHeader = DexHeaderFactory.getDexHeader(program, address);
analysisState = new DexAnalysisState(program, dexHeader);
AnalysisStateInfo.putAnalysisState(program, analysisState);
}

View File

@@ -21,6 +21,7 @@ import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.MemoryByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.file.analyzers.FileFormatAnalyzer;
import ghidra.file.formats.android.cdex.CDexConstants;
import ghidra.file.formats.android.dex.format.DexConstants;
import ghidra.file.formats.android.dex.util.DexUtil;
import ghidra.program.model.address.*;
@@ -33,32 +34,36 @@ import ghidra.util.task.TaskMonitor;
public class DexCondenseFillerBytesAnalyzer extends FileFormatAnalyzer {
@Override
public boolean analyze( Program program, AddressSetView set, TaskMonitor monitor, MessageLog log ) throws Exception {
AlignmentDataType alignmentDataType = new AlignmentDataType( );
public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws Exception {
AlignmentDataType alignmentDataType = new AlignmentDataType();
Address address = toAddr( program, DexUtil.METHOD_ADDRESS );
MemoryBlock block = program.getMemory().getBlock( address );
Address address = toAddr(program, DexUtil.METHOD_ADDRESS);
MemoryBlock block = program.getMemory().getBlock(address);
if ( block == null ) {
log.appendMsg( "Can't locate block with method byte code!" );
if (block == null) {
log.appendMsg("Can't locate block with method byte code!");
return false;
}
AddressSet blockSet = new AddressSet( block.getStart( ), block.getEnd( ) );
AddressSet blockSet = new AddressSet(block.getStart(), block.getEnd());
AddressSetView undefinedSet = program.getListing().getUndefinedRanges( blockSet, true, monitor );
AddressSetView undefinedSet =
program.getListing().getUndefinedRanges(blockSet, true, monitor);
monitor.setMaximum( undefinedSet.getNumAddressRanges() );
monitor.setProgress( 0 );
monitor.setMessage( "DEX: condensing filler bytes" );
monitor.setMaximum(undefinedSet.getNumAddressRanges());
monitor.setProgress(0);
monitor.setMessage("DEX: condensing filler bytes");
AddressRangeIterator addressRanges = undefinedSet.getAddressRanges();
while ( addressRanges.hasNext( ) ) {
monitor.checkCanceled( );
monitor.incrementProgress( 1 );
while (addressRanges.hasNext()) {
monitor.checkCanceled();
monitor.incrementProgress(1);
AddressRange addressRange = addressRanges.next();
if ( isRangeAllSameBytes( program, addressRange, (byte) 0xff, monitor ) ) {
program.getListing().createData( addressRange.getMinAddress(), alignmentDataType, (int)addressRange.getLength() );
if (isRangeAllSameBytes(program, addressRange, (byte) 0xff, monitor)) {
program.getListing()
.createData(addressRange.getMinAddress(), alignmentDataType,
(int) addressRange.getLength());
}
}
@@ -68,53 +73,55 @@ public class DexCondenseFillerBytesAnalyzer extends FileFormatAnalyzer {
}
@Override
public boolean canAnalyze( Program program ) {
ByteProvider provider = new MemoryByteProvider( program.getMemory( ), program.getMinAddress( ) );
return DexConstants.isDexFile( provider );
public boolean canAnalyze(Program program) {
ByteProvider provider =
new MemoryByteProvider(program.getMemory(), program.getMinAddress());
return DexConstants.isDexFile(provider) || CDexConstants.isCDEX(program);
}
@Override
public AnalyzerType getAnalysisType( ) {
public AnalyzerType getAnalysisType() {
return AnalyzerType.BYTE_ANALYZER;
}
@Override
public boolean getDefaultEnablement( Program program ) {
public boolean getDefaultEnablement(Program program) {
return true;
}
@Override
public String getDescription( ) {
return "Condenses all filler bytes in a DEX file";
public String getDescription() {
return "Condenses all filler bytes in a DEX/CDEX file";
}
@Override
public String getName( ) {
return "Android DEX Condense Filler Bytes";
public String getName() {
return "Android DEX/CDEX Condense Filler Bytes";
}
@Override
public AnalysisPriority getPriority( ) {
return new AnalysisPriority( Integer.MAX_VALUE );
public AnalysisPriority getPriority() {
return new AnalysisPriority(Integer.MAX_VALUE);
}
@Override
public boolean isPrototype( ) {
public boolean isPrototype() {
return false;
}
private boolean isRangeAllSameBytes( Program program, AddressRange addressRange, byte value, TaskMonitor monitor ) throws CancelledException {
byte [] bytes = new byte[ (int) addressRange.getLength() ];
private boolean isRangeAllSameBytes(Program program, AddressRange addressRange, byte value,
TaskMonitor monitor) throws CancelledException {
byte[] bytes = new byte[(int) addressRange.getLength()];
try {
program.getMemory().getBytes( addressRange.getMinAddress(), bytes ) ;
program.getMemory().getBytes(addressRange.getMinAddress(), bytes);
}
catch ( Exception e ) {
catch (Exception e) {
return false;
//ignore
}
for ( byte b : bytes ) {
monitor.checkCanceled( );
if ( b != value ) {
for (byte b : bytes) {
monitor.checkCanceled();
if (b != value) {
return false;
}
}

View File

@@ -24,6 +24,7 @@ import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.MemoryByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.file.analyzers.FileFormatAnalyzer;
import ghidra.file.formats.android.cdex.CDexConstants;
import ghidra.file.formats.android.dex.format.*;
import ghidra.file.formats.android.dex.util.DexUtil;
import ghidra.program.model.address.*;
@@ -50,7 +51,7 @@ public class DexExceptionHandlersAnalyzer extends FileFormatAnalyzer {
public boolean canAnalyze(Program program) {
ByteProvider provider =
new MemoryByteProvider(program.getMemory(), program.getMinAddress());
return DexConstants.isDexFile(provider);
return DexConstants.isDexFile(provider) || CDexConstants.isCDEX(program);
}
@Override
@@ -65,12 +66,12 @@ public class DexExceptionHandlersAnalyzer extends FileFormatAnalyzer {
@Override
public String getDescription() {
return "Disassembles the exception handlers in a DEX file";
return "Disassembles the exception handlers in a DEX/CDEX file";
}
@Override
public String getName() {
return "Android DEX Exception Handlers";
return "Android DEX/CDEX Exception Handlers";
}
@Override
@@ -176,8 +177,8 @@ public class DexExceptionHandlersAnalyzer extends FileFormatAnalyzer {
private void createCatchSymbol(Program program, String catchName, Address catchAddress) {
Namespace catchNameSpace = DexUtil.getOrCreateNameSpace(program, "CatchHandlers");
try {
program.getSymbolTable().createLabel(catchAddress, catchName, catchNameSpace,
SourceType.ANALYSIS);
program.getSymbolTable()
.createLabel(catchAddress, catchName, catchNameSpace, SourceType.ANALYSIS);
}
catch (Exception e) {
Msg.error(this, "Error creating label", e);

View File

@@ -15,31 +15,17 @@
*/
package ghidra.file.formats.android.dex.analyzer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.cmd.disassemble.DisassembleCommand;
import ghidra.app.services.AnalysisPriority;
import ghidra.app.services.AnalyzerType;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.MemoryByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.file.analyzers.FileFormatAnalyzer;
import ghidra.file.formats.android.dex.format.*;
import ghidra.file.formats.android.dex.util.DexUtil;
import ghidra.program.database.ProgramCompilerSpec;
import ghidra.file.formats.android.cdex.CDexConstants;
import ghidra.file.formats.android.dex.format.DexConstants;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.listing.Function.FunctionUpdateType;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.*;
import ghidra.util.exception.*;
import ghidra.program.model.listing.Program;
import ghidra.util.task.TaskMonitor;
public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
@@ -48,40 +34,15 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws Exception {
Address startAddress = toAddr(program, 0x0);
Address baseAddress = toAddr(program, 0x0);
if (getDataAt(program, startAddress) != null) {
if (getDataAt(program, baseAddress) != null) {
log.appendMsg("data already exists.");
return true;
}
Memory memory = program.getMemory();
MemoryBlock block = memory.getBlock(startAddress);
block.setRead(true);
block.setWrite(false);
block.setExecute(false);
DexAnalysisState analysisState = DexAnalysisState.getState(program);
DexHeader header = analysisState.getHeader();
processHeader(program, header);
createInitialFragments(program, header, monitor);
ProgramCompilerSpec.enableJavaLanguageDecompilation(program);
createNamespaces(program, header, monitor, log);
processMap(program, header, monitor, log);
processStrings(program, header, monitor, log);
processTypes(program, header, monitor, log);
processPrototypes(program, header, monitor, log);
processFields(program, header, monitor, log);
processMethods(program, header, monitor, log);
processClassDefs(program, header, monitor, log);
createProgramDataTypes(program, header, monitor, log);
createMethods(program, header, monitor, log);
monitor.setMessage("DEX: cleaning up tree");
removeEmptyFragments(program);
DexHeaderFormatMarkup markup = new DexHeaderFormatMarkup(program, baseAddress);
markup.markup(monitor, log);
return true;
}
@@ -90,7 +51,7 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
public boolean canAnalyze(Program program) {
ByteProvider provider =
new MemoryByteProvider(program.getMemory(), program.getMinAddress());
return DexConstants.isDexFile(provider);
return DexConstants.isDexFile(provider) || CDexConstants.isCDEX(program);
}
@Override
@@ -105,12 +66,12 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
@Override
public String getDescription() {
return "Android DEX Header Format";
return "Android Dalvik EXecutable (DEX) / Compact DEX (CDEX) Header Format";
}
@Override
public String getName() {
return "Android DEX Header Format";
return "Android DEX/CDEX Header Format";
}
@Override
@@ -122,865 +83,4 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
public boolean isPrototype() {
return false;
}
private void createNamespaces(Program program, DexHeader header, TaskMonitor monitor,
MessageLog log) throws Exception {
monitor.setMessage("DEX: creating namespaces");
monitor.setMaximum(header.getClassDefsIdsSize());
monitor.setProgress(0);
// NOTE:
// MUST CREATE ALL OF THE CLASSES AND NAMESPACES FIRST
// OTHERWISE GHIDRA CANNOT HANDLE OBFUSCATED PACKAGES NAMES
// FOR EXAMPLE, "a.a.a.a" and "a.a.a" WHERE THE LAST A IS A METHOD
for (ClassDefItem item : header.getClassDefs()) {
monitor.checkCanceled();
monitor.incrementProgress(1);
String className = DexUtil.convertTypeIndexToString(header, item.getClassIndex());
Namespace classNameSpace =
DexUtil.createNameSpaceFromMangledClassName(program, className);
if (classNameSpace == null) {
log.appendMsg("Failed to create namespace: " + className);
}
}
}
private void createMethods(Program program, DexHeader header, TaskMonitor monitor,
MessageLog log) throws Exception {
monitor.setMessage("DEX: creating methods");
monitor.setMaximum(header.getClassDefsIdsSize());
monitor.setProgress(0);
for (ClassDefItem item : header.getClassDefs()) {
monitor.checkCanceled();
monitor.incrementProgress(1);
ClassDataItem classDataItem = item.getClassDataItem();
if (classDataItem == null) {
continue;
}
createMethods(program, header, item, classDataItem.getDirectMethods(), monitor, log);
createMethods(program, header, item, classDataItem.getVirtualMethods(), monitor, log);
}
}
private void createMethods(Program program, DexHeader header, ClassDefItem item,
List<EncodedMethod> methods, TaskMonitor monitor, MessageLog log) throws Exception {
String className = DexUtil.convertTypeIndexToString(header, item.getClassIndex());
Namespace classNameSpace = DexUtil.createNameSpaceFromMangledClassName(program, className);
if (classNameSpace == null) {
log.appendMsg("No namespace: Skipping methods for " + className);
return;
}
for (EncodedMethod encodedMethod : methods) {
monitor.checkCanceled();
MethodIDItem methodID = header.getMethods().get(encodedMethod.getMethodIndex());
String methodName = DexUtil.convertToString(header, methodID.getNameIndex());
if ((AccessFlags.ACC_CONSTRUCTOR & encodedMethod.getAccessFlags()) != 0) {
methodName = classNameSpace.getName();
}
CodeItem codeItem = encodedMethod.getCodeItem();
if (codeItem == null) {//external
// Address externalAddress = toAddr( program, DexUtil.EXTERNAL_ADDRESS + ( 4 * methodIndex ) );
// createMethodSymbol( program, externalAddress, methodName, classNameSpace );
// createMethodComment( program, externalAddress, header, item, methodID, encodedMethod, codeItem, monitor );
// createData( program, externalAddress, new PointerDataType( ) );
// Function method = createFunction( program, externalAddress );
// method.setCustomVariableStorage( true );
//
// Address methodIndexAddress = toAddr( program, DexUtil.LOOKUP_ADDRESS + ( methodIndex * 4 ) );
// Symbol primarySymbol = program.getSymbolTable().getPrimarySymbol( methodIndexAddress );
// program.getReferenceManager().addExternalReference( methodIndexAddress, (Namespace) null, primarySymbol.getName( ), null, SourceType.ANALYSIS, 0, RefType.EXTERNAL_REF );
}
else {
Address methodAddress =
toAddr(program, DexUtil.METHOD_ADDRESS + encodedMethod.getCodeOffset());
createMethodSymbol(program, methodAddress, methodName, classNameSpace, log);
createMethodComment(program, methodAddress, header, item, methodID, encodedMethod,
codeItem, monitor);
disassembleMethod(program, header, className, encodedMethod.isStatic(),
methodAddress, methodID, codeItem, monitor, log);
}
}
}
private Symbol createMethodSymbol(Program program, Address methodAddress, String methodName,
Namespace classNameSpace, MessageLog log) {
program.getSymbolTable().addExternalEntryPoint(methodAddress);
try {
return program.getSymbolTable()
.createLabel(methodAddress, methodName, classNameSpace, SourceType.ANALYSIS);
}
catch (InvalidInputException e) {
log.appendException(e);
return null;
}
}
private void createMethodComment(Program program, Address methodAddress, DexHeader header,
ClassDefItem item, MethodIDItem methodID, EncodedMethod encodedMethod,
CodeItem codeItem, TaskMonitor monitor) throws CancelledException {
String methodSignature =
DexUtil.convertPrototypeIndexToString(header, methodID.getProtoIndex());
StringBuilder commentBuilder = new StringBuilder();
commentBuilder.append(item.toString(header, -1, monitor) + "\n");
commentBuilder.append("Method Signature: " + methodSignature + "\n");
commentBuilder.append("Method Access Flags:\n");
commentBuilder.append(AccessFlags.toString(encodedMethod.getAccessFlags()) + "\n");
if (codeItem != null) {
commentBuilder.append("Method Register Size: " + codeItem.getRegistersSize() + "\n");
commentBuilder.append("Method Incoming Size: " + codeItem.getIncomingSize() + "\n");
commentBuilder.append("Method Outgoing Size: " + codeItem.getOutgoingSize() + "\n");
commentBuilder.append("Method Debug Info Offset: 0x" +
Integer.toHexString(codeItem.getDebugInfoOffset()) + "\n");
}
commentBuilder
.append("Method ID Offset: 0x" + Long.toHexString(methodID.getFileOffset()) + "\n");
setPlateComment(program, methodAddress, commentBuilder.toString());
}
private void disassembleMethod(Program program, DexHeader header, String className,
boolean isStatic, Address methodAddress, MethodIDItem methodID, CodeItem codeItem,
TaskMonitor monitor, MessageLog log) throws CancelledException {
Language language = program.getLanguage();
DisassembleCommand dCommand = new DisassembleCommand(methodAddress, null, true);
dCommand.applyTo(program);
Function method = createFunction(program, methodAddress);
if (method == null) {
log.appendMsg("Failed to create method at " + methodAddress);
return;
}
int registerIndex = codeItem.getRegistersSize() - codeItem.getIncomingSize();
//TODO create local variables in between
for (int i = 0; i < registerIndex; ++i) {
DataType localDataType = null;//TODO
Register localRegister = language.getRegister("v" + i);
try {
LocalVariableImpl local =
new LocalVariableImpl("local_" + i, 0, localDataType, localRegister, program);
method.addLocalVariable(local, SourceType.ANALYSIS);
}
catch (Exception e) {
log.appendException(e);
}
}
Variable returnVar = null;
ArrayList<Variable> paramList = new ArrayList<>();
int prototypeIndex = methodID.getProtoIndex() & 0xffff;
PrototypesIDItem prototype = header.getPrototypes().get(prototypeIndex);
try {
String returnTypeString =
DexUtil.convertTypeIndexToString(header, prototype.getReturnTypeIndex());
DataType returnDataType =
DexUtil.toDataType(program.getDataTypeManager(), returnTypeString);
returnVar = new ReturnParameterImpl(returnDataType, program);
if (!isStatic) {
String classString =
DexUtil.convertTypeIndexToString(header, methodID.getClassIndex());
DataType thisDataType =
DexUtil.toDataType(program.getDataTypeManager(), classString);
String parameterName = "this";
Variable param = new ParameterImpl(parameterName, thisDataType, program);
paramList.add(param);
}
TypeList parameters = prototype.getParameters();
if (parameters != null) {
for (TypeItem parameterTypeItem : parameters.getItems()) {
monitor.checkCanceled();
String parameterTypeString =
DexUtil.convertTypeIndexToString(header, parameterTypeItem.getType());
DataType parameterDataType =
DexUtil.toDataType(program.getDataTypeManager(), parameterTypeString);
String parameterName =
getParameterName(header, codeItem, paramList.size() - (isStatic ? 0 : 1));
if (parameterName == null) {
parameterName = "p" + paramList.size();
}
Variable param = new ParameterImpl(parameterName, parameterDataType, program);
paramList.add(param);
}
}
method.updateFunction("__stdcall", returnVar, paramList,
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.ANALYSIS);
}
catch (InvalidInputException ex) {
log.appendException(ex);
}
catch (DuplicateNameException ex) {
log.appendException(ex);
}
}
private String getParameterName(DexHeader header, CodeItem codeItem, int parameterOrdinal) {
try {
DebugInfoItem debugInfo = codeItem.getDebugInfo();
int[] debugParameterNames = debugInfo.getParameterNames();
List<StringIDItem> strings = header.getStrings();
StringIDItem stringIDItem = strings.get(debugParameterNames[parameterOrdinal]);
StringDataItem stringDataItem = stringIDItem.getStringDataItem();
return stringDataItem.getString();
}
catch (Exception e) {
// IndexOutOfBoundsException
}
return null;
}
private void processHeader(Program program, DexHeader header) throws Exception {
Address headerAddress = toAddr(program, 0x0);
DataType headerDataType = header.toDataType();
createData(program, headerAddress, headerDataType);
createFragment(program, "header", headerAddress,
headerAddress.add(headerDataType.getLength()));
}
private void processClassDefs(Program program, DexHeader header, TaskMonitor monitor,
MessageLog log) throws Exception {
monitor.setMessage("DEX: processing class definitions");
monitor.setMaximum(header.getClassDefsIdsSize());
monitor.setProgress(0);
Address address = toAddr(program, header.getClassDefsIdsOffset());
int index = 0;
for (ClassDefItem item : header.getClassDefs()) {
monitor.checkCanceled();
monitor.incrementProgress(1);
DataType dataType = item.toDataType();
createData(program, address, dataType);
createFragment(program, "classes", address, address.add(dataType.getLength()));
createClassDefSymbol(program, header, item, address);
processClassInterfaces(program, header, item, monitor);
processClassAnnotations(program, item, monitor, log);
processClassDataItem(program, header, item, monitor);
processClassStaticValues(program, header, item, monitor);
setPlateComment(program, address, item.toString(header, index, monitor));
address = address.add(dataType.getLength());
++index;
}
}
private void createProgramDataTypes(Program program, DexHeader header, TaskMonitor monitor,
MessageLog log) throws CancelledException {
monitor.setMessage("DEX: creating program datatypes");
monitor.setMaximum(header.getTypeIdsSize());
monitor.setProgress(0);
DataTypeManager dtm = program.getDataTypeManager();
int curGroup = -1;
CategoryPath handlePath = null;
List<TypeIDItem> types = header.getTypes();
for (int typeID = 0; typeID < header.getTypeIdsSize(); ++typeID) {
TypeIDItem item = types.get(typeID);
monitor.checkCanceled();
monitor.incrementProgress(1);
String name = DexUtil.convertToString(header, item.getDescriptorIndex());
String[] path = DexUtil.convertClassStringToPathArray(DexUtil.CATEGORY_PATH, name);
if (path == null) {
continue;
}
StringBuilder builder = new StringBuilder();
for (int i = 0; i < path.length - 1; ++i) {
builder.append(CategoryPath.DELIMITER_CHAR);
builder.append(path[i]);
}
CategoryPath catPath = new CategoryPath(builder.toString());
DataType dataType =
new TypedefDataType(catPath, path[path.length - 1], DWordDataType.dataType);
dataType = dtm.resolve(dataType, DataTypeConflictHandler.DEFAULT_HANDLER);
// Create unchanging typedef to each class based on typeID, so we can find class type even if name changes
if (typeID / 100 != curGroup) {
curGroup = typeID / 100;
builder = new StringBuilder();
builder.append(DexUtil.HANDLE_PATH);
builder.append("group").append(curGroup);
handlePath = new CategoryPath(builder.toString());
}
DataType handleType = new TypedefDataType(handlePath, "type" + typeID, dataType);
dtm.resolve(handleType, DataTypeConflictHandler.DEFAULT_HANDLER);
}
}
private void createClassDefSymbol(Program program, DexHeader header, ClassDefItem item,
Address address) {
String className = DexUtil.convertTypeIndexToString(header, item.getClassIndex());
SymbolTable symbolTable = program.getSymbolTable();
try {
Namespace nameSpace = DexUtil.createNameSpaceFromMangledClassName(program, className);
if (nameSpace != null) {
symbolTable.createLabel(address, DexUtil.CLASSDEF_NAME, nameSpace,
SourceType.ANALYSIS);
}
}
catch (Exception ex) {
// Don't worry if we can't laydown this symbol
}
}
private void processClassStaticValues(Program program, DexHeader header, ClassDefItem item,
TaskMonitor monitor) throws DuplicateNameException, IOException, Exception {
if (item.getStaticValuesOffset() > 0) {
EncodedArrayItem staticValues = item.getStaticValues();
Address staticAddress = toAddr(program, item.getStaticValuesOffset());
DataType staticDataType = staticValues.toDataType();
createData(program, staticAddress, staticDataType);
createFragment(program, "class_static_values", staticAddress,
staticAddress.add(staticDataType.getLength()));
StringBuilder builder = new StringBuilder();
builder.append("Class: " +
DexUtil.convertTypeIndexToString(header, item.getClassIndex()) + "\n\n");
builder.append("Static Values:" + "\n");
for (byte b : staticValues.getArray().getValues()) {
builder.append(Integer.toHexString(b & 0xff) + " ");
}
setPlateComment(program, staticAddress, builder.toString());
}
}
private void processClassDataItem(Program program, DexHeader header, ClassDefItem item,
TaskMonitor monitor) throws DuplicateNameException, IOException, Exception {
if (item.getClassDataOffset() > 0) {
ClassDataItem classDataItem = item.getClassDataItem();
Address classDataAddress = toAddr(program, item.getClassDataOffset());
DataType classDataDataType = classDataItem.toDataType();
createData(program, classDataAddress, classDataDataType);
createFragment(program, "class_data", classDataAddress,
classDataAddress.add(classDataDataType.getLength()));
StringBuilder builder = new StringBuilder();
builder.append("Class: " +
DexUtil.convertTypeIndexToString(header, item.getClassIndex()) + "\n\n");
builder.append("Static Fields: " + classDataItem.getStaticFieldsSize() + "\n");
builder.append("Instance Fields: " + classDataItem.getInstanceFieldsSize() + "\n");
builder.append("Direct Methods: " + classDataItem.getDirectMethodsSize() + "\n");
builder.append("Virtual Methods: " + classDataItem.getVirtualMethodsSize() + "\n");
processEncodedFields(program, header, classDataItem.getStaticFields(), monitor);
processEncodedFields(program, header, classDataItem.getInstancesFields(), monitor);
processEncodedMethods(program, header, item, classDataItem.getDirectMethods(), monitor);
processEncodedMethods(program, header, item, classDataItem.getVirtualMethods(),
monitor);
setPlateComment(program, classDataAddress, builder.toString());
}
}
private void processEncodedFields(Program program, DexHeader header,
List<EncodedField> instanceFields, TaskMonitor monitor) throws Exception {
int index = 0;
for (int i = 0; i < instanceFields.size(); ++i) {
monitor.checkCanceled();
EncodedField field = instanceFields.get(i);
int diff = field.getFieldIndexDifference();
if (i == 0) {
index = diff;
}
else {
index += diff;
}
FieldIDItem fieldID = header.getFields().get(index);
StringBuilder builder = new StringBuilder();
builder.append(DexUtil.convertToString(header, fieldID.getNameIndex()) + "\n");
builder.append(AccessFlags.toString(field.getAccessFlags()) + "\n");
builder.append("\n");
Address address = toAddr(program, field.getFileOffset());
DataType dataType = field.toDataType();
createData(program, address, dataType);
setPlateComment(program, address, builder.toString());
createFragment(program, "encoded_fields", address, address.add(dataType.getLength()));
}
}
private void processEncodedMethods(Program program, DexHeader header, ClassDefItem item,
List<EncodedMethod> methods, TaskMonitor monitor) throws Exception {
for (EncodedMethod method : methods) {
monitor.checkCanceled();
MethodIDItem methodID = header.getMethods().get(method.getMethodIndex());
StringBuilder builder = new StringBuilder();
builder.append(
"Method Name: " + DexUtil.convertToString(header, methodID.getNameIndex()) + "\n");
builder.append("Method Offset: 0x" + Long.toHexString(methodID.getFileOffset()) + "\n");
builder.append("Method Flags:\n");
builder.append(AccessFlags.toString(method.getAccessFlags()) + "\n");
builder.append("Code Offset: 0x" + Integer.toHexString(method.getCodeOffset()) + "\n");
builder.append("\n");
Address address = toAddr(program, method.getFileOffset());
DataType dataType = method.toDataType();
createData(program, address, dataType);
setPlateComment(program, address, builder.toString());
createFragment(program, "encoded_methods", address, address.add(dataType.getLength()));
processCodeItem(program, header, item, method, methodID);
}
}
private void processCodeItem(Program program, DexHeader header, ClassDefItem item,
EncodedMethod method, MethodIDItem methodID)
throws DuplicateNameException, IOException, Exception {
if (method.getCodeOffset() > 0) {
Address codeAddress = toAddr(program, method.getCodeOffset());
StringBuilder builder = new StringBuilder();
builder.append(DexUtil.convertTypeIndexToString(header, item.getClassIndex()) + " " +
DexUtil.convertToString(header, methodID.getNameIndex()) + "\n");
setPlateComment(program, codeAddress, builder.toString());
CodeItem codeItem = method.getCodeItem();
DataType codeItemDataType = codeItem.toDataType();
try {
createData(program, codeAddress, codeItemDataType);
int codeItemDataTypeLength = codeItemDataType.getLength();
createFragment(program, "code_item", codeAddress,
codeAddress.add(codeItemDataTypeLength));
Address tempAddress = codeAddress.add(codeItemDataTypeLength);
tempAddress = processCodeItemTrys(program, tempAddress, codeItem);
processCodeItemHandlers(program, codeItem, tempAddress);
}
catch (Exception e) {
//happens when "padding" member has been removed, so struct won't fit
//just ignore it
}
if (codeItem.getDebugInfoOffset() > 0) {
Address debugAddress = toAddr(program, codeItem.getDebugInfoOffset());
DebugInfoItem debug = codeItem.getDebugInfo();
DataType debugDataType = debug.toDataType();
createData(program, debugAddress, debugDataType);
createFragment(program, "debug_info", debugAddress,
debugAddress.add(debugDataType.getLength()));
}
}
}
private void processCodeItemHandlers(Program program, CodeItem codeItem, Address tempAddress)
throws DuplicateNameException, IOException, Exception {
EncodedCatchHandlerList handlerList = codeItem.getHandlerList();
if (handlerList == null) {
return;
}
DataType handlerDataType = handlerList.toDataType();
createData(program, tempAddress, handlerDataType);
createFragment(program, "handlers", tempAddress,
tempAddress.add(handlerDataType.getLength()));
tempAddress = tempAddress.add(handlerDataType.getLength());
for (EncodedCatchHandler handler : handlerList.getHandlers()) {
DataType dataType = handler.toDataType();
createData(program, tempAddress, dataType);
createFragment(program, "handlers", tempAddress, tempAddress.add(dataType.getLength()));
tempAddress = tempAddress.add(dataType.getLength());
}
}
private Address processCodeItemTrys(Program program, Address codeAddress, CodeItem codeItem)
throws DuplicateNameException, IOException, Exception {
Address tempAddress = codeAddress;
for (TryItem tryItem : codeItem.getTries()) {
DataType dataType = tryItem.toDataType();
createData(program, tempAddress, dataType);
createFragment(program, "try", tempAddress, tempAddress.add(dataType.getLength()));
tempAddress = tempAddress.add(dataType.getLength());
}
return tempAddress;
}
private void processClassAnnotations(Program program, ClassDefItem item, TaskMonitor monitor,
MessageLog log)
throws DuplicateNameException, IOException, Exception, CancelledException {
if (item.getAnnotationsOffset() > 0) {
AnnotationsDirectoryItem annotationsDirectoryItem = item.getAnnotationsDirectoryItem();
Address annotationsAddress = toAddr(program, item.getAnnotationsOffset());
DataType annotationsDataType = annotationsDirectoryItem.toDataType();
createData(program, annotationsAddress, annotationsDataType);
createFragment(program, "annotations", annotationsAddress,
annotationsAddress.add(annotationsDataType.getLength()));
if (annotationsDirectoryItem.getClassAnnotationsOffset() > 0) {
Address classAddress =
toAddr(program, annotationsDirectoryItem.getClassAnnotationsOffset());
AnnotationSetItem setItem = annotationsDirectoryItem.getClassAnnotations();
DataType setItemDataType = setItem.toDataType();
createData(program, classAddress, setItemDataType);
createFragment(program, "class_annotations", classAddress,
classAddress.add(setItemDataType.getLength()));
processAnnotationSetItem(program, setItem, monitor, log);
}
for (FieldAnnotation field : annotationsDirectoryItem.getFieldAnnotations()) {
monitor.checkCanceled();
Address fieldAddress = toAddr(program, field.getAnnotationsOffset());
AnnotationSetItem setItem = field.getAnnotationSetItem();
DataType setItemDataType = setItem.toDataType();
createData(program, fieldAddress, setItemDataType);
createFragment(program, "annotation_fields", fieldAddress,
fieldAddress.add(setItemDataType.getLength()));
processAnnotationSetItem(program, setItem, monitor, log);
}
for (MethodAnnotation method : annotationsDirectoryItem.getMethodAnnotations()) {
monitor.checkCanceled();
Address methodAddress = toAddr(program, method.getAnnotationsOffset());
AnnotationSetItem setItem = method.getAnnotationSetItem();
DataType setItemDataType = setItem.toDataType();
createData(program, methodAddress, setItemDataType);
createFragment(program, "annotation_methods", methodAddress,
methodAddress.add(setItemDataType.getLength()));
processAnnotationSetItem(program, setItem, monitor, log);
}
for (ParameterAnnotation parameter : annotationsDirectoryItem
.getParameterAnnotations()) {
monitor.checkCanceled();
Address parameterAddress = toAddr(program, parameter.getAnnotationsOffset());
AnnotationSetReferenceList annotationSetReferenceList =
parameter.getAnnotationSetReferenceList();
DataType listDataType = annotationSetReferenceList.toDataType();
createData(program, parameterAddress, listDataType);
createFragment(program, "annotation_parameters", parameterAddress,
parameterAddress.add(listDataType.getLength()));
for (AnnotationSetReferenceItem refItem : annotationSetReferenceList.getItems()) {
AnnotationItem annotationItem = refItem.getItem();
if (annotationItem != null) {
int annotationsItemOffset = refItem.getAnnotationsOffset();
Address annotationItemAddress = toAddr(program, annotationsItemOffset);
DataType annotationItemDataType = annotationItem.toDataType();
createData(program, annotationItemAddress, annotationItemDataType);
createFragment(program, "annotation_item", annotationItemAddress,
annotationItemAddress.add(annotationItemDataType.getLength()));
}
}
}
}
}
private void processClassInterfaces(Program program, DexHeader header, ClassDefItem item,
TaskMonitor monitor) throws Exception {
if (item.getInterfacesOffset() > 0) {
TypeList interfaces = item.getInterfaces();
Address interfaceAddress = toAddr(program, item.getInterfacesOffset());
DataType interfaceDataType = interfaces.toDataType();
createData(program, interfaceAddress, interfaceDataType);
createFragment(program, "interfaces", interfaceAddress,
interfaceAddress.add(interfaceDataType.getLength()));
StringBuilder builder = new StringBuilder();
builder.append("Class: " +
DexUtil.convertTypeIndexToString(header, item.getClassIndex()) + "\n\n");
builder.append("Implements:" + "\n");
for (TypeItem interfaceItem : interfaces.getItems()) {
monitor.checkCanceled();
builder.append("\t" +
DexUtil.convertTypeIndexToString(header, interfaceItem.getType()) + "\n");
}
setPlateComment(program, interfaceAddress, builder.toString());
}
}
private void processAnnotationSetItem(Program program, AnnotationSetItem setItem,
TaskMonitor monitor, MessageLog log) {
try {
for (AnnotationOffsetItem offsetItem : setItem.getItems()) {
monitor.checkCanceled();
Address aAddress = toAddr(program, offsetItem.getAnnotationsOffset());
AnnotationItem aItem = offsetItem.getItem();
DataType aDataType = aItem.toDataType();
createData(program, aAddress, aDataType);
createFragment(program, "annotation_items", aAddress,
aAddress.add(aDataType.getLength()));
}
}
catch (Exception e) {
log.appendException(e);
}
}
private void processMethods(Program program, DexHeader header, TaskMonitor monitor,
MessageLog log) throws Exception {
monitor.setMessage("DEX: processing methods");
monitor.setMaximum(header.getMethodIdsSize());
monitor.setProgress(0);
Address address = toAddr(program, header.getMethodIdsOffset());
int methodIndex = 0;
for (MethodIDItem item : header.getMethods()) {
monitor.checkCanceled();
monitor.incrementProgress(1);
DataType dataType = item.toDataType();
createData(program, address, dataType);
createFragment(program, "methods", address, address.add(dataType.getLength()));
StringBuilder builder = new StringBuilder();
builder.append("Method Index: 0x" + Integer.toHexString(methodIndex) + "\n");
builder.append(
"Class: " + DexUtil.convertTypeIndexToString(header, item.getClassIndex()) + "\n");
builder.append("Prototype: " +
DexUtil.convertPrototypeIndexToString(header, item.getProtoIndex()) + "\n");
builder.append("Name: " + DexUtil.convertToString(header, item.getNameIndex()) + "\n");
setPlateComment(program, address, builder.toString());
Address methodIndexAddress = DexUtil.toLookupAddress(program, methodIndex);
if (program.getMemory().getInt(methodIndexAddress) == -1) {
// Add placeholder symbol for external functions
String methodName = DexUtil.convertToString(header, item.getNameIndex());
String className = DexUtil.convertTypeIndexToString(header, item.getClassIndex());
Namespace classNameSpace =
DexUtil.createNameSpaceFromMangledClassName(program, className);
if (classNameSpace != null) {
Address externalAddress = DexUtil.toLookupAddress(program, methodIndex);
Symbol methodSymbol = createMethodSymbol(program, externalAddress, methodName,
classNameSpace, log);
if (methodSymbol != null) {
String externalName = methodSymbol.getName(true);
program.getReferenceManager()
.addExternalReference(methodIndexAddress, "EXTERNAL.dex",
externalName, null, SourceType.ANALYSIS, 0, RefType.DATA);
}
}
}
createData(program, methodIndexAddress, new PointerDataType());
++methodIndex;
address = address.add(dataType.getLength());
}
}
private void processFields(Program program, DexHeader header, TaskMonitor monitor,
MessageLog log) throws Exception {
monitor.setMessage("DEX: processing fields");
monitor.setMaximum(header.getFieldIdsSize());
monitor.setProgress(0);
Address address = toAddr(program, header.getFieldIdsOffset());
int index = 0;
for (FieldIDItem item : header.getFields()) {
monitor.checkCanceled();
monitor.incrementProgress(1);
DataType dataType = item.toDataType();
createData(program, address, dataType);
createFragment(program, "fields", address, address.add(dataType.getLength()));
StringBuilder builder = new StringBuilder();
builder.append("Field Index: 0x" + Integer.toHexString(index) + "\n");
builder.append(
"Class: " + DexUtil.convertTypeIndexToString(header, item.getClassIndex()) + "\n");
builder.append(
"Type: " + DexUtil.convertTypeIndexToString(header, item.getTypeIndex()) + "\n");
builder.append("Name: " + DexUtil.convertToString(header, item.getNameIndex()) + "\n");
setPlateComment(program, address, builder.toString());
++index;
address = address.add(dataType.getLength());
}
}
private void processPrototypes(Program program, DexHeader header, TaskMonitor monitor,
MessageLog log) throws Exception {
monitor.setMessage("DEX: processing prototypes");
monitor.setMaximum(header.getProtoIdsSize());
monitor.setProgress(0);
Address address = toAddr(program, header.getProtoIdsOffset());
int index = 0;
for (PrototypesIDItem item : header.getPrototypes()) {
monitor.checkCanceled();
monitor.incrementProgress(1);
DataType dataType = item.toDataType();
createData(program, address, dataType);
createFragment(program, "prototypes", address, address.add(dataType.getLength()));
StringBuilder builder = new StringBuilder();
builder.append("Prototype Index: 0x" + Integer.toHexString(index) + "\n");
builder.append(
"Shorty: " + DexUtil.convertToString(header, item.getShortyIndex()) + "\n");
builder.append("Return Type: " +
DexUtil.convertTypeIndexToString(header, item.getReturnTypeIndex()) + "\n");
if (item.getParametersOffset() > 0) {
builder.append("Parameters: " + "\n");
TypeList parameters = item.getParameters();
for (TypeItem parameter : parameters.getItems()) {
monitor.checkCanceled();
builder.append(
DexUtil.convertTypeIndexToString(header, parameter.getType()) + " ");
}
DataType parametersDT = parameters.toDataType();
Address parametersAddress = toAddr(program, item.getParametersOffset());
createData(program, parametersAddress, parametersDT);
}
setPlateComment(program, address, builder.toString());
++index;
address = address.add(dataType.getLength());
}
}
private void processTypes(Program program, DexHeader header, TaskMonitor monitor,
MessageLog log) throws Exception {
monitor.setMessage("DEX: processing types");
monitor.setMaximum(header.getTypeIdsSize());
monitor.setProgress(0);
Address address = toAddr(program, header.getTypeIdsOffset());
int index = 0;
for (TypeIDItem item : header.getTypes()) {
monitor.checkCanceled();
monitor.incrementProgress(1);
DataType dataType = item.toDataType();
createData(program, address, dataType);
createFragment(program, "types", address, address.add(dataType.getLength()));
StringBuilder builder = new StringBuilder();
builder.append("Type Index: 0x" + Integer.toHexString(index) + "\n");
builder.append(
"\t" + "->" + DexUtil.convertToString(header, item.getDescriptorIndex()));
setPlateComment(program, address, builder.toString());
++index;
address = address.add(dataType.getLength());
}
}
private void processMap(Program program, DexHeader header, TaskMonitor monitor, MessageLog log)
throws Exception {
MapList mapList = header.getMapList();
if (mapList == null) {
return;
}
monitor.setMessage("DEX: processing map");
monitor.setMaximum(mapList.getSize());
monitor.setProgress(0);
Address mapListAddress = toAddr(program, header.getMapOffset());
DataType mapListDataType = mapList.toDataType();
createData(program, mapListAddress, mapListDataType);
createFragment(program, "map", mapListAddress,
mapListAddress.add(mapListDataType.getLength()));
StringBuilder builder = new StringBuilder();
for (MapItem item : header.getMapList().getItems()) {
monitor.checkCanceled();
builder.append(MapItemTypeCodes.toString(item.getType()) + "\n");
}
setPlateComment(program, mapListAddress, builder.toString());
}
private void createInitialFragments(Program program, DexHeader header, TaskMonitor monitor)
throws Exception {
monitor.setMessage("DEX: creating fragments");
if (header.getDataSize() > 0) {
Address start = toAddr(program, header.getDataOffset());
Address end = start.add(header.getDataSize());
createFragment(program, "data", start, end);
}
}
private void processStrings(Program program, DexHeader header, TaskMonitor monitor,
MessageLog log) throws Exception {
monitor.setMessage("DEX: processing strings");
monitor.setMaximum(header.getStringIdsSize());
monitor.setProgress(0);
Address address = toAddr(program, header.getStringIdsOffset());
int index = 0;
for (StringIDItem item : header.getStrings()) {
monitor.checkCanceled();
monitor.incrementProgress(1);
// markup string data items
Address stringDataAddress = toAddr(program, item.getStringDataOffset());
StringDataItem stringDataItem = item.getStringDataItem();
String string = stringDataItem.getString();
try {
DataType stringDataType = stringDataItem.toDataType();
createData(program, stringDataAddress, stringDataType);
setPlateComment(program, stringDataAddress,
Integer.toHexString(index) + "\n\n" + string);
createFragment(program, "string_data", stringDataAddress,
stringDataAddress.add(stringDataType.getLength()));
createStringSymbol(program, stringDataAddress, string, "strings");
}
catch (DuplicateNameException e) {
log.appendException(e); // Report the exception but keep going
}
catch (InvalidInputException e) {
log.appendException(e);
}
// markup string Id items
DataType dataType = item.toDataType();
try {
createData(program, address, dataType);
createFragment(program, "strings", address, address.add(dataType.getLength()));
setPlateComment(program, address,
"String Index: 0x" + Integer.toHexString(index) + "\n\n" + string);
createStringSymbol(program, address, string, "string_data");
}
catch (DuplicateNameException e) {
log.appendException(e); // Report the exception but keep going
}
catch (InvalidInputException e) {
log.appendException(e);
}
++index;
address = address.add(dataType.getLength());
}
}
private void createStringSymbol(Program program, Address address, String string,
String namespace) {
SymbolTable symbolTable = program.getSymbolTable();
if (string.length() > 0) {
Namespace nameSpace = DexUtil.getOrCreateNameSpace(program, namespace);
String symbolName = SymbolUtilities.replaceInvalidChars(string, true);
if (symbolName.length() > SymbolUtilities.MAX_SYMBOL_NAME_LENGTH) {
symbolName = symbolName.substring(0, SymbolUtilities.MAX_SYMBOL_NAME_LENGTH - 20);
}
try {
symbolTable.createLabel(address, symbolName, nameSpace, SourceType.ANALYSIS);
}
catch (InvalidInputException e) {
// TODO Symbol name matches possible default symbol name: BYTE_0
}
}
}
}

View File

@@ -21,6 +21,7 @@ import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.MemoryByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.file.analyzers.FileFormatAnalyzer;
import ghidra.file.formats.android.cdex.CDexConstants;
import ghidra.file.formats.android.dex.format.DexConstants;
import ghidra.file.formats.android.dex.format.DexHeader;
import ghidra.program.model.address.Address;
@@ -33,32 +34,33 @@ import ghidra.util.task.TaskMonitor;
public class DexMarkupDataAnalyzer extends FileFormatAnalyzer {
@Override
public boolean analyze( Program program, AddressSetView set, TaskMonitor monitor, MessageLog log ) throws Exception {
monitor.setMaximum( set == null ? program.getMemory( ).getSize( ) : set.getNumAddresses( ) );
monitor.setProgress( 0 );
public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws Exception {
monitor.setMaximum(set == null ? program.getMemory().getSize() : set.getNumAddresses());
monitor.setProgress(0);
DexAnalysisState analysisState = DexAnalysisState.getState(program);
DexHeader header = analysisState.getHeader();
int headerLength = header.toDataType( ).getLength( );
int headerLength = header.toDataType().getLength();
Listing listing = program.getListing( );
Listing listing = program.getListing();
DataIterator dataIterator = listing.getDefinedData( set, true );
while ( dataIterator.hasNext( ) ) {
monitor.checkCanceled( );
monitor.incrementProgress( 1 );
DataIterator dataIterator = listing.getDefinedData(set, true);
while (dataIterator.hasNext()) {
monitor.checkCanceled();
monitor.incrementProgress(1);
Data data = dataIterator.next( );
Data data = dataIterator.next();
if ( data.getMinAddress( ).getOffset( ) == 0x0 ) {
if (data.getMinAddress().getOffset() == 0x0) {
continue;// skip the main dex header..
}
monitor.setMessage( "DEX: Data markup ... " + data.getMinAddress( ) );
monitor.setMessage("DEX: Data markup ... " + data.getMinAddress());
if ( data.isStructure( ) ) {
processData( data, headerLength, monitor );
if (data.isStructure()) {
processData(data, headerLength, monitor);
}
}
@@ -66,60 +68,63 @@ public class DexMarkupDataAnalyzer extends FileFormatAnalyzer {
}
@Override
public boolean canAnalyze( Program program ) {
ByteProvider provider = new MemoryByteProvider( program.getMemory( ), program.getMinAddress( ) );
return DexConstants.isDexFile( provider );
public boolean canAnalyze(Program program) {
ByteProvider provider =
new MemoryByteProvider(program.getMemory(), program.getMinAddress());
return DexConstants.isDexFile(provider) || CDexConstants.isCDEX(program);
}
@Override
public AnalyzerType getAnalysisType( ) {
public AnalyzerType getAnalysisType() {
return AnalyzerType.DATA_ANALYZER;
}
@Override
public boolean getDefaultEnablement( Program program ) {
public boolean getDefaultEnablement(Program program) {
return true;
}
@Override
public String getDescription( ) {
return "Android DEX Data Markup";
public String getDescription() {
return "Android DEX/CDEX Data Markup";
}
@Override
public String getName( ) {
return "Android DEX Data Markup";
public String getName() {
return "Android DEX/CDEX Data Markup";
}
@Override
public AnalysisPriority getPriority( ) {
return new AnalysisPriority( 5 );
public AnalysisPriority getPriority() {
return new AnalysisPriority(5);
}
@Override
public boolean isPrototype( ) {
public boolean isPrototype() {
return false;
}
private void processData( Data data, int headerLength, TaskMonitor monitor ) throws Exception {
for ( int i = 0 ; i < data.getNumComponents( ) ; ++i ) {
monitor.checkCanceled( );
Data component = data.getComponent( i );
if ( component.getNumComponents( ) > 0 ) {
processData( component, headerLength, monitor );
private void processData(Data data, int headerLength, TaskMonitor monitor) throws Exception {
for (int i = 0; i < data.getNumComponents(); ++i) {
monitor.checkCanceled();
Data component = data.getComponent(i);
if (component.getNumComponents() > 0) {
processData(component, headerLength, monitor);
}
if ( component.getReferencesFrom( ).length > 0 ) {
if (component.getReferencesFrom().length > 0) {
continue;
}
if ( component.getFieldName( ).toLowerCase( ).indexOf( "offset" ) != -1 ) {
Scalar scalar = component.getScalar( 0 );
if ( scalar.getUnsignedValue( ) < headerLength ) {// skip low number points into dex header
if (component.getFieldName().toLowerCase().indexOf("offset") != -1) {
Scalar scalar = component.getScalar(0);
if (scalar.getUnsignedValue() < headerLength) {// skip low number points into dex header
continue;
}
Address destination = component.getMinAddress( ).getNewAddress( scalar.getUnsignedValue( ) );
Program program = component.getProgram( );
ReferenceManager referenceManager = program.getReferenceManager( );
referenceManager.addMemoryReference( component.getMinAddress( ), destination, RefType.DATA, SourceType.ANALYSIS, 0 );
Address destination =
component.getMinAddress().getNewAddress(scalar.getUnsignedValue());
Program program = component.getProgram();
ReferenceManager referenceManager = program.getReferenceManager();
referenceManager.addMemoryReference(component.getMinAddress(), destination,
RefType.DATA, SourceType.ANALYSIS, 0);
}
}
}

View File

@@ -23,7 +23,9 @@ import ghidra.app.services.AnalyzerType;
import ghidra.app.util.bin.*;
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.*;
import ghidra.file.formats.android.dex.util.DexUtil;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*;
@@ -34,101 +36,107 @@ import ghidra.util.task.TaskMonitor;
public class DexMarkupInstructionsAnalyzer extends FileFormatAnalyzer {
@Override
public boolean analyze( Program program, AddressSetView set, TaskMonitor monitor, MessageLog log ) throws Exception {
monitor.setMaximum( set == null ? program.getMemory( ).getSize( ) : set.getNumAddresses( ) );
monitor.setProgress( 0 );
public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws Exception {
monitor.setMaximum(set == null ? program.getMemory().getSize() : set.getNumAddresses());
monitor.setProgress(0);
DexAnalysisState analysisState = DexAnalysisState.getState(program);
DexHeader header = analysisState.getHeader();
// Set-up reader for fill_array_data
ByteProvider provider = new MemoryByteProvider( program.getMemory( ), program.getMinAddress( ) );
BinaryReader reader = new BinaryReader( provider, true );
ByteProvider provider =
new MemoryByteProvider(program.getMemory(), program.getMinAddress());
BinaryReader reader = new BinaryReader(provider, true);
Listing listing = program.getListing( );
Listing listing = program.getListing();
InstructionIterator instructionIterator = listing.getInstructions( set, true );
while ( instructionIterator.hasNext( ) ) {
Instruction instruction = instructionIterator.next( );
InstructionIterator instructionIterator = listing.getInstructions(set, true);
while (instructionIterator.hasNext()) {
Instruction instruction = instructionIterator.next();
monitor.checkCanceled( );
monitor.incrementProgress( 1 );
monitor.setMessage( "DEX: Instruction markup ... " + instruction.getMinAddress( ) );
monitor.checkCanceled();
monitor.incrementProgress(1);
monitor.setMessage("DEX: Instruction markup ... " + instruction.getMinAddress());
String mnemonicString = instruction.getMnemonicString( );
String mnemonicString = instruction.getMnemonicString();
if ( mnemonicString.startsWith( "invoke_super_quick" ) ) {
if (mnemonicString.startsWith("invoke_super_quick")) {
//ignore...
}
else if ( mnemonicString.startsWith( "invoke_virtual_quick" ) ) {
else if (mnemonicString.startsWith("invoke_virtual_quick")) {
//ignore...
}
else if ( mnemonicString.startsWith( "invoke_object_init_range" ) ) {
else if (mnemonicString.startsWith("invoke_object_init_range")) {
//ignore...
}
else if ( mnemonicString.indexOf( "quick" ) > 0 ) {
else if (mnemonicString.indexOf("quick") > 0) {
//ignore...
}
else if ( mnemonicString.startsWith( "const_string" ) ) {
Scalar scalar = instruction.getScalar( 1 );
processString( program, instruction, 1, header, ( int ) scalar.getUnsignedValue( ), log );
else if (mnemonicString.startsWith("const_string")) {
Scalar scalar = instruction.getScalar(1);
processString(program, instruction, 1, header, (int) scalar.getUnsignedValue(),
log);
}
else if ( mnemonicString.equals( "const_class" ) ) {
Scalar scalar = instruction.getScalar( 1 );
processClass( program, instruction, 1, header, ( int ) scalar.getUnsignedValue( ), log );
else if (mnemonicString.equals("const_class")) {
Scalar scalar = instruction.getScalar(1);
processClass(program, instruction, 1, header, (int) scalar.getUnsignedValue(), log);
}
else if ( mnemonicString.equals( "instance_of" ) ) {
Scalar scalar = instruction.getScalar( 2 );
processClass( program, instruction, 2, header, ( int ) scalar.getUnsignedValue( ), log );
else if (mnemonicString.equals("instance_of")) {
Scalar scalar = instruction.getScalar(2);
processClass(program, instruction, 2, header, (int) scalar.getUnsignedValue(), log);
}
else if ( mnemonicString.equals( "check_cast" ) ) {
Scalar scalar = instruction.getScalar( 1 );
processClass( program, instruction, 1, header, ( int ) scalar.getUnsignedValue( ), log );
else if (mnemonicString.equals("check_cast")) {
Scalar scalar = instruction.getScalar(1);
processClass(program, instruction, 1, header, (int) scalar.getUnsignedValue(), log);
}
else if ( mnemonicString.startsWith( "invoke" ) ) {
Scalar scalar = instruction.getScalar( 0 );//method id
processMethod( program, instruction, 0, header, ( int ) scalar.getUnsignedValue( ), log );
else if (mnemonicString.startsWith("invoke")) {
Scalar scalar = instruction.getScalar(0);//method id
processMethod(program, instruction, 0, header, (int) scalar.getUnsignedValue(),
log);
}
else if ( mnemonicString.equals( "new_instance" ) ) {
Scalar scalar = instruction.getScalar( 1 );
processClass( program, instruction, 1, header, ( int ) scalar.getUnsignedValue( ), log );
else if (mnemonicString.equals("new_instance")) {
Scalar scalar = instruction.getScalar(1);
processClass(program, instruction, 1, header, (int) scalar.getUnsignedValue(), log);
}
else if ( mnemonicString.equals( "new_array" ) ) {
Scalar scalar = instruction.getScalar( 2 );
processClass( program, instruction, 2, header, ( int ) scalar.getUnsignedValue( ), log );
else if (mnemonicString.equals("new_array")) {
Scalar scalar = instruction.getScalar(2);
processClass(program, instruction, 2, header, (int) scalar.getUnsignedValue(), log);
}
else if ( mnemonicString.startsWith( "iget" ) ) {
Scalar scalar = instruction.getScalar( 2 );
processField( program, instruction, 2, header, ( int ) scalar.getUnsignedValue( ), log );
else if (mnemonicString.startsWith("iget")) {
Scalar scalar = instruction.getScalar(2);
processField(program, instruction, 2, header, (int) scalar.getUnsignedValue(), log);
}
else if ( mnemonicString.startsWith( "iput" ) ) {
Scalar scalar = instruction.getScalar( 2 );
processField( program, instruction, 2, header, ( int ) scalar.getUnsignedValue( ), log );
else if (mnemonicString.startsWith("iput")) {
Scalar scalar = instruction.getScalar(2);
processField(program, instruction, 2, header, (int) scalar.getUnsignedValue(), log);
}
else if ( mnemonicString.startsWith( "sget" ) ) {
Scalar scalar = instruction.getScalar( 1 );
processField( program, instruction, 1, header, ( int ) scalar.getUnsignedValue( ), log );
else if (mnemonicString.startsWith("sget")) {
Scalar scalar = instruction.getScalar(1);
processField(program, instruction, 1, header, (int) scalar.getUnsignedValue(), log);
}
else if ( mnemonicString.startsWith( "sput" ) ) {
Scalar scalar = instruction.getScalar( 1 );
processField( program, instruction, 1, header, ( int ) scalar.getUnsignedValue( ), log );
else if (mnemonicString.startsWith("sput")) {
Scalar scalar = instruction.getScalar(1);
processField(program, instruction, 1, header, (int) scalar.getUnsignedValue(), log);
}
else if ( mnemonicString.startsWith( "filled_new_array" ) ) {
Scalar scalar = instruction.getScalar( 0 );
processClass( program, instruction, 0, header, ( int ) scalar.getUnsignedValue( ), log );
else if (mnemonicString.startsWith("filled_new_array")) {
Scalar scalar = instruction.getScalar(0);
processClass(program, instruction, 0, header, (int) scalar.getUnsignedValue(), log);
}
else if ( mnemonicString.startsWith( "fill_array_data" ) ) {
Scalar scalar = instruction.getScalar( 1 );
Address address = instruction.getMinAddress( ).add( scalar.getUnsignedValue( ) * 2 );
if ( program.getMemory( ).getShort( address ) != FilledArrayDataPayload.MAGIC ) {
log.appendMsg( "invalid filled array at " + address );
else if (mnemonicString.startsWith("fill_array_data")) {
Scalar scalar = instruction.getScalar(1);
Address address = instruction.getMinAddress().add(scalar.getUnsignedValue() * 2);
if (program.getMemory().getShort(address) != FilledArrayDataPayload.MAGIC) {
log.appendMsg("invalid filled array at " + address);
}
else {
reader.setPointerIndex( address.getOffset( ) );
FilledArrayDataPayload payload = new FilledArrayDataPayload( reader );
DataType dataType = payload.toDataType( );
createData( program, address, dataType );
program.getReferenceManager( ).addMemoryReference( instruction.getMinAddress( ), address, RefType.DATA, SourceType.ANALYSIS, 1 );
reader.setPointerIndex(address.getOffset());
FilledArrayDataPayload payload = new FilledArrayDataPayload(reader);
DataType dataType = payload.toDataType();
createData(program, address, dataType);
program.getReferenceManager()
.addMemoryReference(instruction.getMinAddress(), address, RefType.DATA,
SourceType.ANALYSIS, 1);
}
}
}
@@ -136,85 +144,89 @@ public class DexMarkupInstructionsAnalyzer extends FileFormatAnalyzer {
}
@Override
public boolean canAnalyze( Program program ) {
ByteProvider provider = new MemoryByteProvider( program.getMemory( ), program.getMinAddress( ) );
return DexConstants.isDexFile( provider );
public boolean canAnalyze(Program program) {
ByteProvider provider =
new MemoryByteProvider(program.getMemory(), program.getMinAddress());
return DexConstants.isDexFile(provider) || CDexConstants.isCDEX(program);
}
@Override
public AnalyzerType getAnalysisType( ) {
public AnalyzerType getAnalysisType() {
return AnalyzerType.INSTRUCTION_ANALYZER;
}
@Override
public boolean getDefaultEnablement( Program program ) {
public boolean getDefaultEnablement(Program program) {
return true;
}
@Override
public String getDescription( ) {
return "Android DEX Instruction Markup";
public String getDescription() {
return "Android DEX/CDEX Instruction Markup";
}
@Override
public String getName( ) {
return "Android DEX Instruction Markup";
public String getName() {
return "Android DEX/CDEX Instruction Markup";
}
@Override
public AnalysisPriority getPriority( ) {
return new AnalysisPriority( 4 );
public AnalysisPriority getPriority() {
return new AnalysisPriority(4);
}
@Override
public boolean isPrototype( ) {
public boolean isPrototype() {
return false;
}
private String getClassName( Program program, DexHeader header, int classTypeIndex, MessageLog log ) {
TypeIDItem typeItem = header.getTypes( ).get( classTypeIndex );
StringIDItem stringItem = header.getStrings( ).get( typeItem.getDescriptorIndex( ) );
return stringItem.getStringDataItem( ).getString( );
private String getClassName(Program program, DexHeader header, int classTypeIndex,
MessageLog log) {
TypeIDItem typeItem = header.getTypes().get(classTypeIndex);
StringIDItem stringItem = header.getStrings().get(typeItem.getDescriptorIndex());
return stringItem.getStringDataItem().getString();
}
private String format( String className, String methodName ) {
StringBuilder builder = new StringBuilder( );
if ( className.startsWith( "L" ) && className.endsWith( ";" ) ) {
String str = className.substring( 1, className.length( ) - 1 );
StringTokenizer tokenizer = new StringTokenizer( str, "/" );
while ( tokenizer.hasMoreTokens( ) ) {
String token = tokenizer.nextToken( );
builder.append( token + "::" );
private String format(String className, String methodName) {
StringBuilder builder = new StringBuilder();
if (className.startsWith("L") && className.endsWith(";")) {
String str = className.substring(1, className.length() - 1);
StringTokenizer tokenizer = new StringTokenizer(str, "/");
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
builder.append(token + "::");
}
}
builder.append( methodName );
return builder.toString( );
builder.append(methodName);
return builder.toString();
}
private void setEquate( Program program, Address address, int operand, String equateName, int equateValue ) {
EquateTable equateTable = program.getEquateTable( );
Equate equate = equateTable.getEquate( equateName );
if ( equate == null ) {
private void setEquate(Program program, Address address, int operand, String equateName,
int equateValue) {
EquateTable equateTable = program.getEquateTable();
Equate equate = equateTable.getEquate(equateName);
if (equate == null) {
try {
equate = equateTable.createEquate( equateName, equateValue );
equate = equateTable.createEquate(equateName, equateValue);
}
catch ( Exception e ) {
catch (Exception e) {
// ignore
}
}
if ( equate == null ) {// happens when equate name is invalid
if (equate == null) {// happens when equate name is invalid
return;
}
if ( equate.getValue( ) != equateValue ) {// verify value is same
setEquate( program, address, operand, equateName + "_" + equateValue, equateValue );
if (equate.getValue() != equateValue) {// verify value is same
setEquate(program, address, operand, equateName + "_" + equateValue, equateValue);
return;
}
equate.addReference( address, operand );
equate.addReference(address, operand);
}
private void processMethod( Program program, Instruction instruction, int operand, DexHeader header, int methodIndex, MessageLog log ) {
if ( methodIndex < 0 || methodIndex > header.getMethodIdsSize() ) {
log.appendMsg( "method index not found: " + methodIndex );
private void processMethod(Program program, Instruction instruction, int operand,
DexHeader header, int methodIndex, MessageLog log) {
if (methodIndex < 0 || methodIndex > header.getMethodIdsSize()) {
log.appendMsg("method index not found: " + methodIndex);
return;
}
@@ -227,57 +239,70 @@ public class DexMarkupInstructionsAnalyzer extends FileFormatAnalyzer {
//String valueName = format( className, methodName );
Address methodIndexAddress = header.getMethodAddress( program, methodIndex );
Address methodIndexAddress = header.getMethodAddress(program, methodIndex);
if (methodIndexAddress != Address.NO_ADDRESS)
program.getReferenceManager().addMemoryReference( instruction.getMinAddress(), methodIndexAddress, RefType.UNCONDITIONAL_CALL, SourceType.ANALYSIS, operand );
program.getReferenceManager()
.addMemoryReference(instruction.getMinAddress(), methodIndexAddress,
RefType.UNCONDITIONAL_CALL, SourceType.ANALYSIS, operand);
}
private void processClass( Program program, Instruction instruction, int operand, DexHeader header, int classTypeIndex, MessageLog log ) {
TypeIDItem typeItem = header.getTypes( ).get( classTypeIndex );
StringIDItem stringItem = header.getStrings( ).get( typeItem.getDescriptorIndex( ) );
String className = stringItem.getStringDataItem( ).getString( );
setEquate( program, instruction.getMinAddress( ), operand, className, classTypeIndex );
program.getListing( ).setComment( instruction.getMinAddress( ), CodeUnit.EOL_COMMENT, className );
}
private void processString( Program program, Instruction instruction, int operand, DexHeader header, int stringIndex, MessageLog log ) {
List< StringIDItem > strings = header.getStrings( );
if ( stringIndex < 0 || stringIndex > strings.size( ) ) {
log.appendMsg( "string index not found: " + stringIndex );
private void processClass(Program program, Instruction instruction, int operand,
DexHeader header, int classTypeIndex, MessageLog log) {
if (classTypeIndex < 0 || classTypeIndex > header.getTypes().size()) {
log.appendMsg("class type index not found: " + classTypeIndex);
return;
}
StringIDItem stringIDItem = strings.get( stringIndex );
StringDataItem stringDataItem = stringIDItem.getStringDataItem( );
if ( stringDataItem == null ) {
log.appendMsg( "string data item is null: " + stringIndex );
TypeIDItem typeItem = header.getTypes().get(classTypeIndex);
StringIDItem stringItem = header.getStrings().get(typeItem.getDescriptorIndex());
String className = stringItem.getStringDataItem().getString();
setEquate(program, instruction.getMinAddress(), operand, className, classTypeIndex);
program.getListing()
.setComment(instruction.getMinAddress(), CodeUnit.EOL_COMMENT, className);
}
private void processString(Program program, Instruction instruction, int operand,
DexHeader header, int stringIndex, MessageLog log) {
List<StringIDItem> strings = header.getStrings();
if (stringIndex < 0 || stringIndex > strings.size()) {
log.appendMsg("string index not found: " + stringIndex);
return;
}
StringIDItem stringIDItem = strings.get(stringIndex);
StringDataItem stringDataItem = stringIDItem.getStringDataItem();
if (stringDataItem == null) {
log.appendMsg("string data item is null: " + stringIndex);
return;
}
AddressSpace defaultAddressSpace = program.getAddressFactory().getDefaultAddressSpace();
Address stringAddr = defaultAddressSpace.getAddress(stringIDItem.getStringDataOffset());
program.getReferenceManager().addMemoryReference(instruction.getMinAddress(), stringAddr,
RefType.DATA, SourceType.ANALYSIS, operand);
Address stringAddr = defaultAddressSpace
.getAddress(DexUtil.adjustOffset(stringIDItem.getStringDataOffset(), header));
program.getReferenceManager()
.addMemoryReference(instruction.getMinAddress(), stringAddr, RefType.DATA,
SourceType.ANALYSIS, operand);
// setEquate( program, instruction.getMinAddress( ), operand, stringDataItem.getString( ), stringIndex );
// program.getListing( ).setComment( instruction.getMinAddress( ), CodeUnit.EOL_COMMENT, stringDataItem.getString( ) );
}
private void processField( Program program, Instruction instruction, int operand, DexHeader header, int fieldIndex, MessageLog log ) {
List< FieldIDItem > fields = header.getFields( );
private void processField(Program program, Instruction instruction, int operand,
DexHeader header, int fieldIndex, MessageLog log) {
List<FieldIDItem> fields = header.getFields();
if ( fieldIndex < 0 || fieldIndex > fields.size( ) ) {
log.appendMsg( "field index not found: " + fieldIndex );
if (fieldIndex < 0 || fieldIndex > fields.size()) {
log.appendMsg("field index not found: " + fieldIndex);
return;
}
FieldIDItem fieldIDItem = fields.get( fieldIndex );
FieldIDItem fieldIDItem = fields.get(fieldIndex);
StringIDItem stringItem = header.getStrings( ).get( fieldIDItem.getNameIndex( ) );
String fieldName = stringItem.getStringDataItem( ).getString( );
StringIDItem stringItem = header.getStrings().get(fieldIDItem.getNameIndex());
String fieldName = stringItem.getStringDataItem().getString();
String className = getClassName( program, header, fieldIDItem.getClassIndex( ), log );
String valueName = format( className, fieldName );
String className = getClassName(program, header, fieldIDItem.getClassIndex(), log);
String valueName = format(className, fieldName);
setEquate( program, instruction.getMinAddress( ), operand, fieldName, fieldIndex );
program.getListing( ).setComment( instruction.getMinAddress( ), CodeUnit.EOL_COMMENT, valueName );
setEquate(program, instruction.getMinAddress(), operand, fieldName, fieldIndex);
program.getListing()
.setComment(instruction.getMinAddress(), CodeUnit.EOL_COMMENT, valueName);
}
}

View File

@@ -18,17 +18,29 @@ package ghidra.file.formats.android.dex.analyzer;
import ghidra.app.cmd.disassemble.DisassembleCommand;
import ghidra.app.services.AnalysisPriority;
import ghidra.app.services.AnalyzerType;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.MemoryByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.file.analyzers.FileFormatAnalyzer;
import ghidra.file.formats.android.dex.format.*;
import ghidra.file.formats.android.cdex.CDexConstants;
import ghidra.file.formats.android.dex.format.DexConstants;
import ghidra.file.formats.android.dex.format.PackedSwitchPayload;
import ghidra.file.formats.android.dex.format.SparseSwitchPayload;
import ghidra.file.formats.android.dex.util.DexUtil;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.InstructionIterator;
import ghidra.program.model.listing.Listing;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.*;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.RefType;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.task.TaskMonitor;
public class DexMarkupSwitchTableAnalyzer extends FileFormatAnalyzer {
@@ -53,49 +65,59 @@ public class DexMarkupSwitchTableAnalyzer extends FileFormatAnalyzer {
monitor.incrementProgress(1);
monitor.setMessage("DEX: Instruction markup ... " + instruction.getMinAddress());
if (instruction.getMnemonicString().startsWith("packed_switch")) {
if (instruction.getMnemonicReferences().length > 0) {// already done
continue;
}
Scalar scalar = instruction.getScalar(1);
Address address = instruction.getMinAddress().add(scalar.getUnsignedValue() * 2);
if (program.getMemory().getShort(address) != PackedSwitchPayload.MAGIC) {
log.appendMsg("invalid packed switch at " + address);
}
else {
program.getReferenceManager().addMemoryReference(instruction.getMinAddress(),
address, RefType.DATA, SourceType.ANALYSIS, 1);
try {
if (instruction.getMnemonicString().startsWith("packed_switch")) {
if (instruction.getMnemonicReferences().length > 0) {// already done
continue;
}
Scalar scalar = instruction.getScalar(1);
Address address =
instruction.getMinAddress().add(scalar.getUnsignedValue() * 2);
if (program.getMemory().getShort(address) != PackedSwitchPayload.MAGIC) {
log.appendMsg("invalid packed switch at " + address);
}
else {
program.getReferenceManager()
.addMemoryReference(instruction.getMinAddress(), address,
RefType.DATA, SourceType.ANALYSIS, 1);
reader.setPointerIndex(address.getOffset());
PackedSwitchPayload payload = new PackedSwitchPayload(reader);
DataType dataType = payload.toDataType();
createData(program, address, dataType);
reader.setPointerIndex(address.getOffset());
PackedSwitchPayload payload = new PackedSwitchPayload(reader);
DataType dataType = payload.toDataType();
createData(program, address, dataType);
processPacked(program, instruction, payload, monitor);
//TODO setFallThrough( program, instruction );
processPacked(program, instruction, payload, monitor);
//TODO setFallThrough( program, instruction );
}
}
else if (instruction.getMnemonicString().startsWith("sparse_switch")) {
if (instruction.getMnemonicReferences().length > 0) {// already done
continue;
}
Scalar scalar = instruction.getScalar(1);
Address address =
instruction.getMinAddress().add(scalar.getUnsignedValue() * 2);
if (program.getMemory().getShort(address) != SparseSwitchPayload.MAGIC) {
log.appendMsg("invalid sparse switch at " + address);
}
else {
program.getReferenceManager()
.addMemoryReference(instruction.getMinAddress(), address,
RefType.DATA, SourceType.ANALYSIS, 1);
reader.setPointerIndex(address.getOffset());
SparseSwitchPayload payload = new SparseSwitchPayload(reader);
DataType dataType = payload.toDataType();
createData(program, address, dataType);
processSparse(program, instruction, payload, monitor);
//TODO setFallThrough( program, instruction );
}
}
}
else if (instruction.getMnemonicString().startsWith("sparse_switch")) {
if (instruction.getMnemonicReferences().length > 0) {// already done
continue;
}
Scalar scalar = instruction.getScalar(1);
Address address = instruction.getMinAddress().add(scalar.getUnsignedValue() * 2);
if (program.getMemory().getShort(address) != SparseSwitchPayload.MAGIC) {
log.appendMsg("invalid sparse switch at " + address);
}
else {
program.getReferenceManager().addMemoryReference(instruction.getMinAddress(),
address, RefType.DATA, SourceType.ANALYSIS, 1);
reader.setPointerIndex(address.getOffset());
SparseSwitchPayload payload = new SparseSwitchPayload(reader);
DataType dataType = payload.toDataType();
createData(program, address, dataType);
processSparse(program, instruction, payload, monitor);
//TODO setFallThrough( program, instruction );
}
catch (MemoryAccessException e) {
log.appendMsg("unable to process switch at " + instruction.getMinAddress());
}
}
@@ -106,7 +128,7 @@ public class DexMarkupSwitchTableAnalyzer extends FileFormatAnalyzer {
public boolean canAnalyze(Program program) {
ByteProvider provider =
new MemoryByteProvider(program.getMemory(), program.getMinAddress());
return DexConstants.isDexFile(provider);
return DexConstants.isDexFile(provider) || CDexConstants.isCDEX(program);
}
@Override
@@ -121,12 +143,12 @@ public class DexMarkupSwitchTableAnalyzer extends FileFormatAnalyzer {
@Override
public String getDescription() {
return "Android DEX Switch Table Markup";
return "Android DEX/CDEX Switch Table Markup";
}
@Override
public String getName() {
return "Android DEX Switch Table Markup";
return "Android DEX/CDEX Switch Table Markup";
}
@Override
@@ -157,10 +179,11 @@ public class DexMarkupSwitchTableAnalyzer extends FileFormatAnalyzer {
String caseName = "case_0x" + Integer.toHexString(key);
Address caseAddress = instruction.getMinAddress().add(target * 2);
program.getSymbolTable().createLabel(caseAddress, caseName, nameSpace,
SourceType.ANALYSIS);
program.getReferenceManager().addMemoryReference(instruction.getMinAddress(),
caseAddress, RefType.COMPUTED_JUMP, SourceType.ANALYSIS, CodeUnit.MNEMONIC);
program.getSymbolTable()
.createLabel(caseAddress, caseName, nameSpace, SourceType.ANALYSIS);
program.getReferenceManager()
.addMemoryReference(instruction.getMinAddress(), caseAddress,
RefType.COMPUTED_JUMP, SourceType.ANALYSIS, CodeUnit.MNEMONIC);
DisassembleCommand dCommand = new DisassembleCommand(caseAddress, null, true);
dCommand.applyTo(program);
++key;
@@ -177,10 +200,11 @@ public class DexMarkupSwitchTableAnalyzer extends FileFormatAnalyzer {
String caseName = "case_0x" + Integer.toHexString(payload.getKeys()[i]);
Address caseAddress = instruction.getMinAddress().add(payload.getTargets()[i] * 2);
program.getSymbolTable().createLabel(caseAddress, caseName, nameSpace,
SourceType.ANALYSIS);
program.getReferenceManager().addMemoryReference(instruction.getMinAddress(),
caseAddress, RefType.COMPUTED_JUMP, SourceType.ANALYSIS, CodeUnit.MNEMONIC);
program.getSymbolTable()
.createLabel(caseAddress, caseName, nameSpace, SourceType.ANALYSIS);
program.getReferenceManager()
.addMemoryReference(instruction.getMinAddress(), caseAddress,
RefType.COMPUTED_JUMP, SourceType.ANALYSIS, CodeUnit.MNEMONIC);
DisassembleCommand dCommand = new DisassembleCommand(caseAddress, null, true);
dCommand.applyTo(program);
}

View File

@@ -19,40 +19,102 @@ import java.lang.reflect.Field;
public final class AccessFlags {
public final static int ACC_PUBLIC = 0x1;// public: visible everywhere public: visible everywhere public: visible everywhere
public final static int ACC_PRIVATE = 0x2;// * private: only visible to defining class private: only visible to defining class private: only visible to defining class
public final static int ACC_PROTECTED = 0x4;// * protected: visible to package and subclasses protected: visible to package and subclasses protected: visible to package and subclasses public final
public final static int ACC_STATIC = 0x8;// * static: is not constructed with an outer this reference static: global to defining class static: does not take a this argument
public final static int ACC_FINAL = 0x10;// final: not subclassable final: immutable after construction final: not overridable
public final static int ACC_SYNCHRONIZED = 0x20;// synchronized: associated lock automatically acquired around call to this method. Note: This is only valid to set when ACC_NATIVE is also set.
public final static int ACC_VOLATILE = 0x40;// volatile: special access rules to help with thread safety
public final static int ACC_BRIDGE = 0x40;// bridge method, added automatically by compiler as a type-safe bridge
public final static int ACC_TRANSIENT = 0x80;// transient: not to be saved by default serialization
public final static int ACC_VARARGS = 0x80;// last argument should be treated as a "rest" argument by compiler
public final static int ACC_NATIVE = 0x100;// native: implemented in native code
public final static int ACC_INTERFACE = 0x200;// interface: multiply-implementable abstract class
public final static int ACC_ABSTRACT = 0x400;// abstract: not directly instantiable abstract: unimplemented by this class
public final static int ACC_STRICT = 0x800;// strictfp: strict rules for floating-point arithmetic
public final static int ACC_SYNTHETIC = 0x1000;// not directly defined in source code not directly defined in source code not directly defined in source code
public final static int ACC_ANNOTATION = 0x2000;// declared as an annotation class
public final static int ACC_ENUM = 0x4000;// declared as an enumerated type declared as an enumerated value
/**
* public: visible everywhere
*/
public final static int ACC_PUBLIC = 0x1;
/**
* private: only visible to defining class
*/
public final static int ACC_PRIVATE = 0x2;
/**
* protected: visible to package and subclasses
*/
public final static int ACC_PROTECTED = 0x4;
/**
* static: is not constructed with an outer this reference
*/
public final static int ACC_STATIC = 0x8;
/**
* final: not subclassable
* final: immutable after construction
* final: not overridable
*/
public final static int ACC_FINAL = 0x10;
/**
* synchronized: associated lock automatically acquired around call to this method.
* Note: This is only valid to set when ACC_NATIVE is also set.
*/
public final static int ACC_SYNCHRONIZED = 0x20;
/**
* volatile: special access rules to help with thread safety
*/
public final static int ACC_VOLATILE = 0x40;
/**
* bridge method, added automatically by compiler as a type-safe bridge
*/
public final static int ACC_BRIDGE = 0x40;
/**
* transient: not to be saved by default serialization
*/
public final static int ACC_TRANSIENT = 0x80;
/**
* last argument should be treated as a "rest" argument by compiler
*/
public final static int ACC_VARARGS = 0x80;
/**
* native: implemented in native code
*/
public final static int ACC_NATIVE = 0x100;
/**
* interface: multiply-implementable abstract class
*/
public final static int ACC_INTERFACE = 0x200;
/**
* abstract: not directly instantiable
* abstract: unimplemented by this class
*/
public final static int ACC_ABSTRACT = 0x400;
/**
* strictfp: strict rules for floating-point arithmetic
*/
public final static int ACC_STRICT = 0x800;
/**
* not directly defined in source code
*/
public final static int ACC_SYNTHETIC = 0x1000;
/**
* declared as an annotation class
*/
public final static int ACC_ANNOTATION = 0x2000;
/**
* declared as an enumerated type declared as an enumerated value
*/
public final static int ACC_ENUM = 0x4000;
// (unused) 0x8000
public final static int ACC_CONSTRUCTOR = 0x10000;// constructor method (class or instance initializer)
public final static int ACC_DECLARED_SYNCHRONIZED = 0x20000;// declared synchronized. Note: This has no effect on execution (other than in reflection of this flag, per se).
/**
* constructor method (class or instance initializer)
*/
public final static int ACC_CONSTRUCTOR = 0x10000;
/**
* declared synchronized.
* Note: This has no effect on execution (other than in reflection of this flag, per se).
*/
public final static int ACC_DECLARED_SYNCHRONIZED = 0x20000;
public final static String toString( int value ) {
StringBuilder builder = new StringBuilder( );
public final static String toString(int value) {
StringBuilder builder = new StringBuilder();
try {
Field [] fields = AccessFlags.class.getDeclaredFields( );
for ( Field field : fields ) {
if ( ( field.getInt( null ) & value ) != 0 ) {
builder.append( "\t" + field.getName( ) + "\n" );
Field[] fields = AccessFlags.class.getDeclaredFields();
for (Field field : fields) {
if ((field.getInt(null) & value) != 0) {
builder.append("\t" + field.getName() + "\n");
}
}
}
catch ( Exception e ) {
catch (Exception e) {
// ignore
}
return builder.toString( );
return builder.toString();
}
}

View File

@@ -29,35 +29,37 @@ public class AnnotationElement implements StructConverter {
private int nameIndexLength;// in bytes
private EncodedValue value;
public AnnotationElement( BinaryReader reader ) throws IOException {
public AnnotationElement(BinaryReader reader) throws IOException {
LEB128 leb128 = LEB128.readUnsignedValue(reader);
nameIndex = leb128.asUInt32();
nameIndexLength = leb128.getLength();
value = new EncodedValue( reader );
value = new EncodedValue(reader);
}
public int getNameIndex( ) {
public int getNameIndex() {
return nameIndex;
}
public EncodedValue getValue( ) {
public EncodedValue getValue() {
return value;
}
@Override
public DataType toDataType( ) throws DuplicateNameException, IOException {
DataType encodeValueDataType = value.toDataType( );
public DataType toDataType() throws DuplicateNameException, IOException {
DataType encodeValueDataType = value.toDataType();
String name = "annotation_element" + "_" + nameIndexLength + "_" + encodeValueDataType.getName( );
String name =
"annotation_element" + "_" + nameIndexLength + "_" + encodeValueDataType.getName();
Structure structure = new StructureDataType( name, 0 );
Structure structure = new StructureDataType(name, 0);
structure.add( new ArrayDataType( BYTE, nameIndexLength, BYTE.getLength( ) ), "nameIndex", null );
structure.add(new ArrayDataType(BYTE, nameIndexLength, BYTE.getLength()), "nameIndex",
null);
structure.add( encodeValueDataType, "value", null );
structure.add(encodeValueDataType, "value", null);
structure.setCategoryPath( new CategoryPath( "/dex/annotation_element" ) );
structure.setCategoryPath(new CategoryPath("/dex/annotation_element"));
// try {
// structure.setName( name + "_" + structure.getLength( ) );
// }

View File

@@ -21,44 +21,49 @@ import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
/**
*
* https://android.googlesource.com/platform/art/+/master/libdexfile/dex/dex_file_structs.h
*
*/
public class AnnotationItem implements StructConverter {
private byte visibility;
private EncodedAnnotation annotation;
public AnnotationItem( BinaryReader reader ) throws IOException {
visibility = reader.readNextByte( );
annotation = new EncodedAnnotation( reader );
public AnnotationItem(BinaryReader reader) throws IOException {
visibility = reader.readNextByte();
annotation = new EncodedAnnotation(reader);
}
public byte getVisibility( ) {
public byte getVisibility() {
return visibility;
}
public EncodedAnnotation getAnnotation( ) {
public EncodedAnnotation getAnnotation() {
return annotation;
}
@Override
public DataType toDataType( ) throws DuplicateNameException, IOException {
DataType annotationDataType = annotation.toDataType( );
public DataType toDataType() throws DuplicateNameException, IOException {
DataType annotationDataType = annotation.toDataType();
StringBuilder builder = new StringBuilder( );
builder.append( "annotation_item" + "_" );
builder.append( visibility + "_" );
builder.append( annotationDataType.getName( ) );
StringBuilder builder = new StringBuilder();
builder.append("annotation_item" + "_");
builder.append(visibility + "_");
builder.append(annotationDataType.getName());
Structure structure = new StructureDataType( builder.toString( ), 0 );
structure.add( BYTE, "visibility", null );
structure.add( annotationDataType, "annotation", null );
Structure structure = new StructureDataType(builder.toString(), 0);
structure.add(BYTE, "visibility", null);
structure.add(annotationDataType, "annotation", null);
builder.append( structure.getLength( ) + "_" );
builder.append(structure.getLength() + "_");
structure.setCategoryPath( new CategoryPath( "/dex/annotation_item" ) );
structure.setCategoryPath(new CategoryPath("/dex/annotation_item"));
try {
structure.setName( builder.toString( ) );
structure.setName(builder.toString());
}
catch ( Exception e ) {
catch (Exception e) {
// ignore
}
return structure;

View File

@@ -16,6 +16,7 @@
package ghidra.file.formats.android.dex.format;
import ghidra.app.util.bin.*;
import ghidra.file.formats.android.dex.util.DexUtil;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@@ -27,33 +28,33 @@ public class AnnotationOffsetItem implements StructConverter {
private AnnotationItem _item;
public AnnotationOffsetItem( BinaryReader reader ) throws IOException {
annotationsOffset = reader.readNextInt( );
public AnnotationOffsetItem(BinaryReader reader, DexHeader dexHeader) throws IOException {
annotationsOffset = reader.readNextInt();
if ( annotationsOffset > 0 ) {
long oldIndex = reader.getPointerIndex( );
if (annotationsOffset > 0) {
long oldIndex = reader.getPointerIndex();
try {
reader.setPointerIndex( annotationsOffset );
_item = new AnnotationItem( reader );
reader.setPointerIndex(DexUtil.adjustOffset(annotationsOffset, dexHeader));
_item = new AnnotationItem(reader);
}
finally {
reader.setPointerIndex( oldIndex );
reader.setPointerIndex(oldIndex);
}
}
}
public int getAnnotationsOffset( ) {
public int getAnnotationsOffset() {
return annotationsOffset;
}
public AnnotationItem getItem( ) {
public AnnotationItem getItem() {
return _item;
}
@Override
public DataType toDataType( ) throws DuplicateNameException, IOException {
DataType dataType = StructConverterUtil.toDataType( AnnotationOffsetItem.class );
dataType.setCategoryPath( new CategoryPath( "/dex" ) );
public DataType toDataType() throws DuplicateNameException, IOException {
DataType dataType = StructConverterUtil.toDataType(AnnotationOffsetItem.class);
dataType.setCategoryPath(new CategoryPath("/dex"));
return dataType;
}

View File

@@ -15,53 +15,51 @@
*/
package ghidra.file.formats.android.dex.format;
import ghidra.app.util.bin.*;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
import java.util.*;
/**
* annotation_set_item
*
* referenced from annotations_directory_item, field_annotations_item, method_annotations_item, and annotation_set_ref_item
*
* appears in the data section
*
* alignment: 4 bytes
* https://android.googlesource.com/platform/art/+/master/libdexfile/dex/dex_file_structs.h#258
*/
public class AnnotationSetItem implements StructConverter {
private int size;
private List<AnnotationOffsetItem> items = new ArrayList<AnnotationOffsetItem>();
private static final int MAX_SANE_COUNT = 0x1000;
public AnnotationSetItem( BinaryReader reader ) throws IOException {
size = reader.readNextInt( );
private int size_;
for ( int i = 0 ; i < size ; ++i ) {
items.add( new AnnotationOffsetItem( reader ) );
private int[] entries_;
public AnnotationSetItem(BinaryReader reader, DexHeader dexHeader) throws IOException {
size_ = reader.readNextInt();
if (size_ > MAX_SANE_COUNT) {
throw new IOException(
"Too many annotations specified: 0x" + Integer.toHexString(size_));
}
entries_ = reader.readNextIntArray(size_);
}
public int getSize( ) {
return size;
public int getSize() {
return size_;
}
public List< AnnotationOffsetItem > getItems( ) {
return Collections.unmodifiableList( items );
public int[] getEntries() {
return entries_;
}
@Override
public DataType toDataType( ) throws DuplicateNameException, IOException {
Structure structure = new StructureDataType( "annotation_set_item_" + size, 0 );
structure.add( DWORD, "size", null );
int index = 0;
for ( AnnotationOffsetItem item : items ) {
structure.add( item.toDataType( ), "item" + index, null );
++index;
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = new StructureDataType("annotation_set_item_" + size_, 0);
structure.add(DWORD, "size_", null);
if (size_ > 0) {
ArrayDataType array = new ArrayDataType(DWORD, size_, DWORD.getLength());
structure.add(array, "entries_", null);
}
structure.setCategoryPath( new CategoryPath( "/dex/annotation_set_item" ) );
structure.setCategoryPath(new CategoryPath("/dex/annotation_set_item"));
return structure;
}

View File

@@ -32,33 +32,33 @@ public class AnnotationSetReferenceItem implements StructConverter {
private AnnotationItem _item;
public AnnotationSetReferenceItem( BinaryReader reader ) throws IOException {
annotationsOffset = reader.readNextInt( );
public AnnotationSetReferenceItem(BinaryReader reader) throws IOException {
annotationsOffset = reader.readNextInt();
if ( annotationsOffset > 0 ) {
long oldIndex = reader.getPointerIndex( );
if (annotationsOffset > 0) {
long oldIndex = reader.getPointerIndex();
try {
reader.setPointerIndex( annotationsOffset );
_item = new AnnotationItem( reader );
reader.setPointerIndex(annotationsOffset);
_item = new AnnotationItem(reader);
}
finally {
reader.setPointerIndex( oldIndex );
reader.setPointerIndex(oldIndex);
}
}
}
public int getAnnotationsOffset( ) {
public int getAnnotationsOffset() {
return annotationsOffset;
}
public AnnotationItem getItem( ) {
public AnnotationItem getItem() {
return _item;
}
@Override
public DataType toDataType( ) throws DuplicateNameException, IOException {
DataType dataType = StructConverterUtil.toDataType( AnnotationSetReferenceItem.class );
dataType.setCategoryPath( new CategoryPath( "/dex" ) );
public DataType toDataType() throws DuplicateNameException, IOException {
DataType dataType = StructConverterUtil.toDataType(AnnotationSetReferenceItem.class);
dataType.setCategoryPath(new CategoryPath("/dex"));
return dataType;
}

View File

@@ -36,26 +36,26 @@ import java.util.List;
public class AnnotationSetReferenceList implements StructConverter {
private int size;
private List< AnnotationSetReferenceItem > items = new ArrayList< AnnotationSetReferenceItem >( );
private List<AnnotationSetReferenceItem> items = new ArrayList<AnnotationSetReferenceItem>();
public AnnotationSetReferenceList( BinaryReader reader ) throws IOException {
size = reader.readNextInt( );
public AnnotationSetReferenceList(BinaryReader reader) throws IOException {
size = reader.readNextInt();
for ( int i = 0 ; i < size ; ++i ) {
items.add( new AnnotationSetReferenceItem( reader ) );
for (int i = 0; i < size; ++i) {
items.add(new AnnotationSetReferenceItem(reader));
}
}
@Override
public DataType toDataType( ) throws DuplicateNameException, IOException {
Structure structure = new StructureDataType( "annotation_set_ref_list_" + size, 0 );
structure.add( DWORD, "size", null );
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = new StructureDataType("annotation_set_ref_list_" + size, 0);
structure.add(DWORD, "size", null);
int index = 0;
for ( AnnotationSetReferenceItem item : items ) {
structure.add( item.toDataType( ), "item" + index, null );
for (AnnotationSetReferenceItem item : items) {
structure.add(item.toDataType(), "item" + index, null);
++index;
}
structure.setCategoryPath( new CategoryPath( "/dex/annotation_set_ref_list" ) );
structure.setCategoryPath(new CategoryPath("/dex/annotation_set_ref_list"));
return structure;
}

View File

@@ -15,22 +15,17 @@
*/
package ghidra.file.formats.android.dex.format;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
import java.io.IOException;
import java.util.*;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.file.formats.android.dex.util.DexUtil;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* annotations_directory_item
*
* referenced from class_def_item
*
* appears in the data section
*
* alignment: 4 bytes
* https://android.googlesource.com/platform/art/+/master/libdexfile/dex/dex_file_structs.h#209
*/
public class AnnotationsDirectoryItem implements StructConverter {
@@ -38,95 +33,94 @@ public class AnnotationsDirectoryItem implements StructConverter {
private int fieldsSize;
private int annotatedMethodsSize;
private int annotatedParametersSize;
private List< FieldAnnotation > fieldAnnotations = new ArrayList< FieldAnnotation >( );
private List< MethodAnnotation > methodAnnotations = new ArrayList< MethodAnnotation >( );
private List< ParameterAnnotation > parameterAnnotations = new ArrayList< ParameterAnnotation >( );
private List<FieldAnnotationsItem> fieldAnnotations = new ArrayList<FieldAnnotationsItem>();
private List<MethodAnnotationsItem> methodAnnotations = new ArrayList<MethodAnnotationsItem>();
private List<ParameterAnnotationsItem> parameterAnnotations =
new ArrayList<ParameterAnnotationsItem>();
private AnnotationSetItem _classAnnotations;
public AnnotationsDirectoryItem( BinaryReader reader ) throws IOException {
public AnnotationsDirectoryItem(BinaryReader reader, DexHeader dexHeader) throws IOException {
classAnnotationsOffset = reader.readNextInt( );
fieldsSize = reader.readNextInt( );
annotatedMethodsSize = reader.readNextInt( );
annotatedParametersSize = reader.readNextInt( );
classAnnotationsOffset = reader.readNextInt();
fieldsSize = reader.readNextInt();
annotatedMethodsSize = reader.readNextInt();
annotatedParametersSize = reader.readNextInt();
for ( int i = 0 ; i < fieldsSize ; ++i ) {
fieldAnnotations.add( new FieldAnnotation( reader ) );
for (int i = 0; i < fieldsSize; ++i) {
fieldAnnotations.add(new FieldAnnotationsItem(reader, dexHeader));
}
for ( int i = 0 ; i < annotatedMethodsSize ; ++i ) {
methodAnnotations.add( new MethodAnnotation( reader ) );
}
for ( int i = 0 ; i < annotatedParametersSize ; ++i ) {
parameterAnnotations.add( new ParameterAnnotation( reader ) );
for (int i = 0; i < annotatedMethodsSize; ++i) {
methodAnnotations.add(new MethodAnnotationsItem(reader, dexHeader));
}
if ( classAnnotationsOffset > 0 ){
long oldIndex = reader.getPointerIndex( );
if (classAnnotationsOffset > 0) {
long oldIndex = reader.getPointerIndex();
try {
reader.setPointerIndex( classAnnotationsOffset );
_classAnnotations = new AnnotationSetItem( reader );
reader.setPointerIndex(DexUtil.adjustOffset(classAnnotationsOffset, dexHeader));
_classAnnotations = new AnnotationSetItem(reader, dexHeader);
}
finally {
reader.setPointerIndex( oldIndex );
reader.setPointerIndex(oldIndex);
}
}
}
public int getClassAnnotationsOffset( ) {
public int getClassAnnotationsOffset() {
return classAnnotationsOffset;
}
public int getFieldsSize( ) {
public int getFieldsSize() {
return fieldsSize;
}
public int getAnnotatedMethodsSize( ) {
public int getAnnotatedMethodsSize() {
return annotatedMethodsSize;
}
public int getAnnotatedParametersSize( ) {
public int getAnnotatedParametersSize() {
return annotatedParametersSize;
}
public List< FieldAnnotation > getFieldAnnotations( ) {
return Collections.unmodifiableList( fieldAnnotations );
public List<FieldAnnotationsItem> getFieldAnnotations() {
return Collections.unmodifiableList(fieldAnnotations);
}
public List< MethodAnnotation > getMethodAnnotations( ) {
return Collections.unmodifiableList( methodAnnotations );
public List<MethodAnnotationsItem> getMethodAnnotations() {
return Collections.unmodifiableList(methodAnnotations);
}
public List< ParameterAnnotation > getParameterAnnotations( ) {
return Collections.unmodifiableList( parameterAnnotations );
public List<ParameterAnnotationsItem> getParameterAnnotations() {
return Collections.unmodifiableList(parameterAnnotations);
}
public AnnotationSetItem getClassAnnotations( ) {
public AnnotationSetItem getClassAnnotations() {
return _classAnnotations;
}
@Override
public DataType toDataType( ) throws DuplicateNameException, IOException {
Structure structure = new StructureDataType( "annotations_directory_item_" + fieldsSize + "_" + annotatedMethodsSize + "_" + annotatedParametersSize, 0 );
structure.add( DWORD, "class_annotations_off", null );
structure.add( DWORD, "fields_size", null );
structure.add( DWORD, "annotated_methods_size", null );
structure.add( DWORD, "annotated_parameters_size", null );
public DataType toDataType() throws DuplicateNameException, IOException {
Structure structure = new StructureDataType("annotations_directory_item_" + fieldsSize +
"_" + annotatedMethodsSize + "_" + annotatedParametersSize, 0);
structure.add(DWORD, "class_annotations_off", null);
structure.add(DWORD, "fields_size", null);
structure.add(DWORD, "annotated_methods_size", null);
structure.add(DWORD, "annotated_parameters_size", null);
int index = 0;
for ( FieldAnnotation field : fieldAnnotations ) {
structure.add( field.toDataType( ), "field_" + index, null );
for (FieldAnnotationsItem field : fieldAnnotations) {
structure.add(field.toDataType(), "field_" + index, null);
++index;
}
index = 0;
for ( MethodAnnotation method : methodAnnotations ) {
structure.add( method.toDataType( ), "method_" + index, null );
for (MethodAnnotationsItem method : methodAnnotations) {
structure.add(method.toDataType(), "method_" + index, null);
++index;
}
index = 0;
for ( ParameterAnnotation parameter : parameterAnnotations ) {
structure.add( parameter.toDataType( ), "parameter_" + index, null );
for (ParameterAnnotationsItem parameter : parameterAnnotations) {
structure.add(parameter.toDataType(), "parameter_" + index, null);
++index;
}
structure.setCategoryPath( new CategoryPath( "/dex/annotations_directory_item" ) );
structure.setCategoryPath(new CategoryPath("/dex/annotations_directory_item"));
return structure;
}

View File

@@ -16,12 +16,18 @@
package ghidra.file.formats.android.dex.format;
import java.io.IOException;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.dwarf4.LEB128;
import ghidra.program.model.data.*;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;
public class ClassDataItem implements StructConverter {
@@ -36,12 +42,12 @@ public class ClassDataItem implements StructConverter {
private int directMethodsSizeLength;// in bytes
private int virtualMethodsSizeLength;// in bytes
private List< EncodedField > staticFields = new ArrayList< >( );
private List< EncodedField > instancesFields = new ArrayList< >( );
private List< EncodedMethod > directMethods = new ArrayList< >( );
private List< EncodedMethod > virtualMethods = new ArrayList< >( );
private List<EncodedField> staticFields = new ArrayList<>();
private List<EncodedField> instancesFields = new ArrayList<>();
private List<EncodedMethod> directMethods = new ArrayList<>();
private List<EncodedMethod> virtualMethods = new ArrayList<>();
public ClassDataItem( BinaryReader reader ) throws IOException {
public ClassDataItem(BinaryReader reader, DexHeader dexHeader) throws IOException {
LEB128 leb128 = LEB128.readUnsignedValue(reader);
staticFieldsSize = leb128.asUInt32();
staticFieldsSizeLength = leb128.getLength();
@@ -58,70 +64,70 @@ public class ClassDataItem implements StructConverter {
virtualMethodsSize = leb128.asUInt32();
virtualMethodsSizeLength = leb128.getLength();
for ( int i = 0 ; i < staticFieldsSize ; ++i ) {
staticFields.add( new EncodedField( reader ) );
for (int i = 0; i < staticFieldsSize; ++i) {
staticFields.add(new EncodedField(reader));
}
for ( int i = 0 ; i < instanceFieldsSize ; ++i ) {
instancesFields.add( new EncodedField( reader ) );
for (int i = 0; i < instanceFieldsSize; ++i) {
instancesFields.add(new EncodedField(reader));
}
int methodIndex = 0;
for ( int i = 0 ; i < directMethodsSize ; ++i ) {
EncodedMethod encodedMethod = new EncodedMethod( reader );
directMethods.add( encodedMethod );
methodIndex += encodedMethod.getMethodIndexDifference( );
encodedMethod.setMethodIndex( methodIndex );
for (int i = 0; i < directMethodsSize; ++i) {
EncodedMethod encodedMethod = new EncodedMethod(reader, dexHeader);
directMethods.add(encodedMethod);
methodIndex += encodedMethod.getMethodIndexDifference();
encodedMethod.setMethodIndex(methodIndex);
}
methodIndex = 0;
for ( int i = 0 ; i < virtualMethodsSize ; ++i ) {
EncodedMethod encodedMethod = new EncodedMethod( reader );
virtualMethods.add( encodedMethod );
methodIndex += encodedMethod.getMethodIndexDifference( );
encodedMethod.setMethodIndex( methodIndex );
for (int i = 0; i < virtualMethodsSize; ++i) {
EncodedMethod encodedMethod = new EncodedMethod(reader, dexHeader);
virtualMethods.add(encodedMethod);
methodIndex += encodedMethod.getMethodIndexDifference();
encodedMethod.setMethodIndex(methodIndex);
}
}
public List< EncodedField > getInstancesFields( ) {
return Collections.unmodifiableList( instancesFields );
public List<EncodedField> getInstancesFields() {
return Collections.unmodifiableList(instancesFields);
}
public List< EncodedField > getStaticFields( ) {
return Collections.unmodifiableList( staticFields );
public List<EncodedField> getStaticFields() {
return Collections.unmodifiableList(staticFields);
}
public List< EncodedMethod > getDirectMethods( ) {
return Collections.unmodifiableList( directMethods );
public List<EncodedMethod> getDirectMethods() {
return Collections.unmodifiableList(directMethods);
}
public List< EncodedMethod > getVirtualMethods( ) {
return Collections.unmodifiableList( virtualMethods );
public List<EncodedMethod> getVirtualMethods() {
return Collections.unmodifiableList(virtualMethods);
}
public int getStaticFieldsSize( ) {
public int getStaticFieldsSize() {
return staticFieldsSize;
}
public int getInstanceFieldsSize( ) {
public int getInstanceFieldsSize() {
return instanceFieldsSize;
}
public int getDirectMethodsSize( ) {
public int getDirectMethodsSize() {
return directMethodsSize;
}
public int getVirtualMethodsSize( ) {
public int getVirtualMethodsSize() {
return virtualMethodsSize;
}
public EncodedMethod getMethodByIndex( int index ) {
for ( int i = 0 ; i < directMethods.size() ; ++i ) {
EncodedMethod encodedMethod = directMethods.get( i );
if ( encodedMethod.getMethodIndex( ) == index ) {
public EncodedMethod getMethodByIndex(int index) {
for (int i = 0; i < directMethods.size(); ++i) {
EncodedMethod encodedMethod = directMethods.get(i);
if (encodedMethod.getMethodIndex() == index) {
return encodedMethod;
}
}
for ( int i = 0 ; i < virtualMethods.size() ; ++i ) {
EncodedMethod encodedMethod = virtualMethods.get( i );
if ( encodedMethod.getMethodIndex( ) == index ) {
for (int i = 0; i < virtualMethods.size(); ++i) {
EncodedMethod encodedMethod = virtualMethods.get(i);
if (encodedMethod.getMethodIndex() == index) {
return encodedMethod;
}
}
@@ -129,14 +135,20 @@ public class ClassDataItem implements StructConverter {
}
@Override
public DataType toDataType( ) throws DuplicateNameException, IOException {
public DataType toDataType() throws DuplicateNameException, IOException {
// int unique = 0;
String name = "class_data_item" + "_" + staticFieldsSizeLength + "_" + instanceFieldsSizeLength + "_" + directMethodsSizeLength + "_" + virtualMethodsSizeLength;
Structure structure = new StructureDataType( name, 0 );
structure.add( new ArrayDataType( BYTE, staticFieldsSizeLength, BYTE.getLength( ) ), "static_fields", null );
structure.add( new ArrayDataType( BYTE, instanceFieldsSizeLength, BYTE.getLength( ) ), "instance_fields", null );
structure.add( new ArrayDataType( BYTE, directMethodsSizeLength, BYTE.getLength( ) ), "direct_methods", null );
structure.add( new ArrayDataType( BYTE, virtualMethodsSizeLength, BYTE.getLength( ) ), "virtual_methods", null );
String name =
"class_data_item" + "_" + staticFieldsSizeLength + "_" + instanceFieldsSizeLength +
"_" + directMethodsSizeLength + "_" + virtualMethodsSizeLength;
Structure structure = new StructureDataType(name, 0);
structure.add(new ArrayDataType(BYTE, staticFieldsSizeLength, BYTE.getLength()),
"static_fields", null);
structure.add(new ArrayDataType(BYTE, instanceFieldsSizeLength, BYTE.getLength()),
"instance_fields", null);
structure.add(new ArrayDataType(BYTE, directMethodsSizeLength, BYTE.getLength()),
"direct_methods", null);
structure.add(new ArrayDataType(BYTE, virtualMethodsSizeLength, BYTE.getLength()),
"virtual_methods", null);
// int index = 0;
// for ( EncodedField field : staticFields ) {
// DataType dataType = field.toDataType( );
@@ -161,7 +173,7 @@ public class ClassDataItem implements StructConverter {
// structure.add( dataType, "virtualMethod" + index, null );
// ++index;
// }
structure.setCategoryPath( new CategoryPath( "/dex/class_data_item" ) );
structure.setCategoryPath(new CategoryPath("/dex/class_data_item"));
// try {
// structure.setName( name + "_" + Integer.toHexString( unique ) );
// }

View File

@@ -41,138 +41,147 @@ public class ClassDefItem implements StructConverter {
private ClassDataItem _classDataItem;
private EncodedArrayItem _staticValues;
public ClassDefItem( BinaryReader reader ) throws IOException {
classIndex = reader.readNextInt( );
accessFlags = reader.readNextInt( );
superClassIndex = reader.readNextInt( );
interfacesOffset = reader.readNextInt( );
sourceFileIndex = reader.readNextInt( );
annotationsOffset = reader.readNextInt( );
classDataOffset = reader.readNextInt( );
staticValuesOffset = reader.readNextInt( );
public ClassDefItem(BinaryReader reader, DexHeader dexHeader) throws IOException {
classIndex = reader.readNextInt();
accessFlags = reader.readNextInt();
superClassIndex = reader.readNextInt();
interfacesOffset = reader.readNextInt();
sourceFileIndex = reader.readNextInt();
annotationsOffset = reader.readNextInt();
classDataOffset = reader.readNextInt();
staticValuesOffset = reader.readNextInt();
if ( interfacesOffset > 0 ) {
long oldIndex = reader.getPointerIndex( );
if (interfacesOffset > 0) {
long oldIndex = reader.getPointerIndex();
try {
reader.setPointerIndex( interfacesOffset );
_interfaces = new TypeList( reader );
reader.setPointerIndex(DexUtil.adjustOffset(interfacesOffset, dexHeader));
_interfaces = new TypeList(reader);
}
finally {
reader.setPointerIndex( oldIndex );
reader.setPointerIndex(oldIndex);
}
}
if ( annotationsOffset > 0 ) {
long oldIndex = reader.getPointerIndex( );
if (annotationsOffset > 0) {
long oldIndex = reader.getPointerIndex();
try {
reader.setPointerIndex( annotationsOffset );
_annotationsDirectoryItem = new AnnotationsDirectoryItem( reader );
reader.setPointerIndex(DexUtil.adjustOffset(annotationsOffset, dexHeader));
_annotationsDirectoryItem = new AnnotationsDirectoryItem(reader, dexHeader);
}
finally {
reader.setPointerIndex( oldIndex );
reader.setPointerIndex(oldIndex);
}
}
if ( classDataOffset > 0 ) {
long oldIndex = reader.getPointerIndex( );
if (classDataOffset > 0) {
long oldIndex = reader.getPointerIndex();
try {
reader.setPointerIndex( classDataOffset );
_classDataItem = new ClassDataItem( reader );
reader.setPointerIndex(DexUtil.adjustOffset(classDataOffset, dexHeader));
_classDataItem = new ClassDataItem(reader, dexHeader);
}
finally {
reader.setPointerIndex( oldIndex );
reader.setPointerIndex(oldIndex);
}
}
if ( staticValuesOffset > 0 ) {
long oldIndex = reader.getPointerIndex( );
if (staticValuesOffset > 0) {
long oldIndex = reader.getPointerIndex();
try {
reader.setPointerIndex( staticValuesOffset );
_staticValues = new EncodedArrayItem( reader );
reader.setPointerIndex(DexUtil.adjustOffset(staticValuesOffset, dexHeader));
_staticValues = new EncodedArrayItem(reader);
}
finally {
reader.setPointerIndex( oldIndex );
reader.setPointerIndex(oldIndex);
}
}
}
public int getClassIndex( ) {
public int getClassIndex() {
return classIndex;
}
public int getAccessFlags( ) {
public int getAccessFlags() {
return accessFlags;
}
public int getSuperClassIndex( ) {
public int getSuperClassIndex() {
return superClassIndex;
}
public int getInterfacesOffset( ) {
public int getInterfacesOffset() {
return interfacesOffset;
}
public int getSourceFileIndex( ) {
public int getSourceFileIndex() {
return sourceFileIndex;
}
public int getAnnotationsOffset( ) {
/**
* NOTE: For CDEX files, this value is relative to DataOffset in DexHeader
* @return the relative offset to annotations
*/
public int getAnnotationsOffset() {
return annotationsOffset;
}
public int getClassDataOffset( ) {
public int getClassDataOffset() {
return classDataOffset;
}
public int getStaticValuesOffset( ) {
public int getStaticValuesOffset() {
return staticValuesOffset;
}
public TypeList getInterfaces( ) {
public TypeList getInterfaces() {
return _interfaces;
}
public AnnotationsDirectoryItem getAnnotationsDirectoryItem( ) {
public AnnotationsDirectoryItem getAnnotationsDirectoryItem() {
return _annotationsDirectoryItem;
}
public ClassDataItem getClassDataItem( ) {
public ClassDataItem getClassDataItem() {
return _classDataItem;
}
public EncodedArrayItem getStaticValues( ) {
public EncodedArrayItem getStaticValues() {
return _staticValues;
}
@Override
public DataType toDataType( ) throws DuplicateNameException, IOException {
DataType dataType = StructConverterUtil.toDataType( ClassDefItem.class );
dataType.setCategoryPath( new CategoryPath( "/dex" ) );
public DataType toDataType() throws DuplicateNameException, IOException {
DataType dataType = StructConverterUtil.toDataType(ClassDefItem.class);
dataType.setCategoryPath(new CategoryPath("/dex"));
return dataType;
}
public String toString( DexHeader header, int index, TaskMonitor monitor ) throws CancelledException {
StringBuilder builder = new StringBuilder( );
if ( index != -1 ) {
builder.append( "Class Index: 0x" + Integer.toHexString( index ) + "\n" );
public String toString(DexHeader header, int index, TaskMonitor monitor)
throws CancelledException {
StringBuilder builder = new StringBuilder();
if (index != -1) {
builder.append("Class Index: 0x" + Integer.toHexString(index) + "\n");
}
builder.append( "Class: " + DexUtil.convertTypeIndexToString( header, getClassIndex( ) ) + "\n" );
builder.append( "Class Access Flags:\n" + AccessFlags.toString( getAccessFlags( ) ) + "\n" );
builder.append( "Superclass: " + DexUtil.convertTypeIndexToString( header, getSuperClassIndex( ) ) + "\n" );
builder.append(
"Class: " + DexUtil.convertTypeIndexToString(header, getClassIndex()) + "\n");
builder.append("Class Access Flags:\n" + AccessFlags.toString(getAccessFlags()) + "\n");
builder.append(
"Superclass: " + DexUtil.convertTypeIndexToString(header, getSuperClassIndex()) + "\n");
if ( getInterfacesOffset( ) > 0 ) {
builder.append( "Interfaces: " + "\n" );
TypeList interfaces = getInterfaces( );
for ( TypeItem type : interfaces.getItems( ) ) {
monitor.checkCanceled( );
builder.append( "\t" + DexUtil.convertTypeIndexToString( header, type.getType( ) ) + "\n" );
if (getInterfacesOffset() > 0) {
builder.append("Interfaces: " + "\n");
TypeList interfaces = getInterfaces();
for (TypeItem type : interfaces.getItems()) {
monitor.checkCanceled();
builder.append(
"\t" + DexUtil.convertTypeIndexToString(header, type.getType()) + "\n");
}
}
if ( getSourceFileIndex( ) > 0 ) {
builder.append( "Source File: " + DexUtil.convertToString( header, getSourceFileIndex( ) ) + "\n" );
if (getSourceFileIndex() > 0) {
builder.append(
"Source File: " + DexUtil.convertToString(header, getSourceFileIndex()) + "\n");
}
return builder.toString( );
return builder.toString();
}
}

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