mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-02-16 15:40:14 +00:00
Added support for multidex. Created APK loader to load all DEX files at one time and link method_lookup sections. APK loader uses the manifest file to determine Android version. Fixed a bug loading CDEX from Android 12.
This commit is contained in:
parent
cef30890ec
commit
15b59f82c3
@ -15,11 +15,10 @@
|
||||
*/
|
||||
package ghidra.app.util.opinion;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
@ -306,7 +305,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||
LibraryLookupTable.cleanup();
|
||||
}
|
||||
|
||||
private boolean isCreateExportSymbolFiles(List<Option> options) {
|
||||
protected boolean isCreateExportSymbolFiles(List<Option> options) {
|
||||
boolean isCreateExportSymbolFiles = IS_CREATE_EXPORT_SYMBOL_FILES_DEFAULT;
|
||||
if (options != null) {
|
||||
for (Option option : options) {
|
||||
@ -319,7 +318,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||
return isCreateExportSymbolFiles;
|
||||
}
|
||||
|
||||
private boolean isLoadLibraries(List<Option> options) {
|
||||
protected boolean isLoadLibraries(List<Option> options) {
|
||||
boolean isLoadLibraries = IS_LOAD_LIBRARIES_DEFAULT;
|
||||
if (options != null) {
|
||||
for (Option option : options) {
|
||||
@ -342,8 +341,10 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||
|
||||
monitor.setMessage(provider.getName());
|
||||
|
||||
Address imageBaseAddr = language.getAddressFactory().getDefaultAddressSpace().getAddress(
|
||||
loadSpec.getDesiredImageBase());
|
||||
Address imageBaseAddr = language.getAddressFactory()
|
||||
.getDefaultAddressSpace()
|
||||
.getAddress(
|
||||
loadSpec.getDesiredImageBase());
|
||||
Program program = createProgram(provider, programName, imageBaseAddr, getName(), language,
|
||||
compilerSpec, consumer);
|
||||
|
||||
@ -406,8 +407,10 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||
boolean saveIfModified, MessageLog messageLog, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
|
||||
Map<String, Program> progsByName = programs.stream().filter(Objects::nonNull).collect(
|
||||
Collectors.toMap((p) -> p.getDomainFile().getName(), (p) -> p));
|
||||
Map<String, Program> progsByName = programs.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.collect(
|
||||
Collectors.toMap((p) -> p.getDomainFile().getName(), (p) -> p));
|
||||
|
||||
monitor.initialize(progsByName.size());
|
||||
for (Program program : progsByName.values()) {
|
||||
@ -818,8 +821,9 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||
program.getSymbolTable().getGlobalSymbol(les.getName(), ordSym.getAddress());
|
||||
if (nameSym == null) {
|
||||
String name = les.getName();
|
||||
Symbol s = program.getSymbolTable().createLabel(ordSym.getAddress(), name,
|
||||
program.getGlobalNamespace(), SourceType.IMPORTED);
|
||||
Symbol s = program.getSymbolTable()
|
||||
.createLabel(ordSym.getAddress(), name,
|
||||
program.getGlobalNamespace(), SourceType.IMPORTED);
|
||||
s.setPrimary();
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +27,7 @@ import ghidra.app.util.OptionUtils;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.formats.gfilesystem.FSRL;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.plugin.importer.ProgramMappingService;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
@ -106,6 +105,10 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||
TaskMonitor monitor) throws IOException, CancelledException, InvalidNameException,
|
||||
DuplicateNameException, VersionException {
|
||||
|
||||
if (!isOverrideMainProgramName()) {
|
||||
folder = ProjectDataUtils.createDomainFolderPath(folder, name);
|
||||
}
|
||||
|
||||
List<DomainObject> results = new ArrayList<>();
|
||||
|
||||
if (!loadSpec.isComplete()) {
|
||||
@ -133,10 +136,14 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is the main imported program, use the given name, otherwise, use the
|
||||
// internal program name. The first program in the list is the main imported program
|
||||
String domainFileName =
|
||||
loadedProgram == programs.get(0) ? name : loadedProgram.getName();
|
||||
String domainFileName = loadedProgram.getName();
|
||||
if (isOverrideMainProgramName()) {
|
||||
// If this is the main imported program, use the given name, otherwise, use the
|
||||
// internal program name. The first program in the list is the main imported program
|
||||
if (loadedProgram == programs.get(0)) {
|
||||
domainFileName = name;
|
||||
}
|
||||
}
|
||||
|
||||
if (createProgramFile(loadedProgram, folder, domainFileName, messageLog,
|
||||
monitor)) {
|
||||
@ -162,6 +169,16 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some loaders can return more than one program.
|
||||
* This method indicates whether the first (or main) program's name
|
||||
* should be overridden and changed to the imported file name.
|
||||
* @return true if first program name should be changed
|
||||
*/
|
||||
protected boolean isOverrideMainProgramName() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean loadInto(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog messageLog, Program program, TaskMonitor monitor)
|
||||
|
@ -0,0 +1,296 @@
|
||||
/* ###
|
||||
* 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.app.util.opinion;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import org.jdom.*;
|
||||
import org.jdom.input.SAXBuilder;
|
||||
|
||||
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.recognizer.PkzipRecognizer;
|
||||
import ghidra.app.util.recognizer.Recognizer;
|
||||
import ghidra.file.formats.android.dex.DexHeaderFactory;
|
||||
import ghidra.file.formats.android.dex.format.DexConstants;
|
||||
import ghidra.file.formats.android.dex.format.DexHeader;
|
||||
import ghidra.file.formats.android.multidex.MultiDexLinker;
|
||||
import ghidra.file.formats.android.xml.AndroidXmlFileSystem;
|
||||
import ghidra.file.formats.zip.ZipFileSystem;
|
||||
import ghidra.formats.gfilesystem.FileSystemService;
|
||||
import ghidra.formats.gfilesystem.GFile;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.xml.XmlUtilities;
|
||||
|
||||
public class ApkLoader extends DexLoader {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Android APK";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
|
||||
if (isZip(provider)) {
|
||||
try (ZipFileSystem zipFS = openAPK(provider, TaskMonitor.DUMMY)) {
|
||||
return findLoadSpecs(zipFS, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (Exception e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Program> loadProgram(ByteProvider provider, String programName,
|
||||
DomainFolder programFolder, LoadSpec loadSpec, List<Option> options, MessageLog log,
|
||||
Object consumer, TaskMonitor monitor) throws CancelledException, IOException {
|
||||
|
||||
boolean success = false;
|
||||
List<Program> programList = new ArrayList<>();
|
||||
int dexIndex = 1;//DEX file numbering starts at 1
|
||||
try (ZipFileSystem zipFS = openAPK(provider, monitor)) {
|
||||
while (!monitor.isCancelled()) {
|
||||
GFile classesDexFile =
|
||||
zipFS.lookup("/" + "classes" + (dexIndex == 1 ? "" : dexIndex) + ".dex");
|
||||
|
||||
if (classesDexFile == null) {//done
|
||||
break;
|
||||
}
|
||||
|
||||
monitor.setMessage(
|
||||
"Loading " + classesDexFile.getName() + " from " + programName + "...");
|
||||
|
||||
try (ByteProvider dexProvider =
|
||||
zipFS.getByteProvider(classesDexFile, monitor)) {
|
||||
// defer to the super class (DexLoader) to actually load the DEX file
|
||||
List<Program> program =
|
||||
super.loadProgram(dexProvider, classesDexFile.getName(), programFolder,
|
||||
loadSpec, options, log, consumer, monitor);
|
||||
|
||||
programList.addAll(program);
|
||||
}
|
||||
++dexIndex;
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.appendException(e);
|
||||
}
|
||||
finally {
|
||||
if (!success) {
|
||||
release(programList, consumer);
|
||||
}
|
||||
}
|
||||
link(programList, log, monitor);
|
||||
return programList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isOverrideMainProgramName() {
|
||||
//preserve the classesX.dex file names...
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quickly check if the provider is a ZIP file.
|
||||
* @param provider the byte provider
|
||||
* @return TRUE if a valid ZIP file.
|
||||
*/
|
||||
private boolean isZip(ByteProvider provider) {
|
||||
try {
|
||||
Recognizer recog = new PkzipRecognizer();
|
||||
byte[] bytes = provider.readBytes(0, recog.numberOfBytesRequired());
|
||||
return recog.recognize(bytes) != null;
|
||||
}
|
||||
catch (Exception e) {
|
||||
//ignore
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects the APK to locate the manifest and classes.dex files.
|
||||
* If found use the manifest to determine the Android version.
|
||||
* @param zipFS the ZIP file system of the APK file
|
||||
* @return collection of load specs
|
||||
* @throws IOException if exception occurs reading the ZIP file.
|
||||
*/
|
||||
private Collection<LoadSpec> findLoadSpecs(ZipFileSystem zipFS, TaskMonitor monitor)
|
||||
throws IOException {
|
||||
|
||||
GFile manifestXmlFile = zipFS.lookup("/" + "AndroidManifest.xml");
|
||||
GFile classesDexFile = zipFS.lookup("/" + "classes.dex");
|
||||
|
||||
if (manifestXmlFile != null) {
|
||||
List<LoadSpec> xmlSpecs = processManifest(zipFS, manifestXmlFile, monitor);
|
||||
if (!xmlSpecs.isEmpty()) {
|
||||
//make sure APK contains classes.dex, some are empty
|
||||
if (classesDexFile != null) {
|
||||
return xmlSpecs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//should NOT be else/if
|
||||
if (classesDexFile != null) {
|
||||
return processDEX(zipFS, classesDexFile, monitor);
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the Android manifest file and returns the
|
||||
* Android OS version string. For example, "N" or "S".
|
||||
* Use this string to select the appropriate Sleigh module
|
||||
* in the importer.
|
||||
* @param zipFS the ZIP file system of the APK file
|
||||
* @param manifestFile the manifest file from the ZIP
|
||||
* @param monitor the task monitor
|
||||
* @return Android OS version string. For example, "N" or "S".
|
||||
* @throws IOException if exception occurs reading the manifest file.
|
||||
*/
|
||||
private List<LoadSpec> processManifest(ZipFileSystem zipFS, GFile manifestFile,
|
||||
TaskMonitor monitor)
|
||||
throws IOException {
|
||||
|
||||
List<LoadSpec> loadSpecs = new ArrayList<>();
|
||||
monitor.setMessage("Reading Android Manifest ...");
|
||||
try {
|
||||
ByteProvider byteProvider =
|
||||
zipFS.getByteProvider(manifestFile, monitor);
|
||||
|
||||
try (AndroidXmlFileSystem xmlFS =
|
||||
openManifest(manifestFile.getName(), byteProvider, monitor)) {
|
||||
|
||||
GFile xmlFile = xmlFS.getPayloadFile();
|
||||
ByteProvider xmlFileByteProvider = xmlFS.getByteProvider(xmlFile, monitor);
|
||||
|
||||
SAXBuilder sax = XmlUtilities.createSecureSAXBuilder(false, false);
|
||||
Document document = sax.build(xmlFileByteProvider.getInputStream(0));
|
||||
Element rootElement = document.getRootElement();
|
||||
Attribute attribute = rootElement.getAttribute("platformBuildVersionName");
|
||||
String platformBuildVersionName = attribute.getValue();
|
||||
|
||||
List<QueryResult> queries =
|
||||
QueryOpinionService.query(getName(), DexConstants.MACHINE,
|
||||
platformBuildVersionName);
|
||||
for (QueryResult result : queries) {
|
||||
loadSpecs.add(new LoadSpec(this, 0, result));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
//ignore
|
||||
}
|
||||
return loadSpecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the "classes.dex" file to determine the LoadSpec
|
||||
* @param zipFS the Android APK file system
|
||||
* @param file the classes.dex file from the file system
|
||||
* @param monitor the task monitor
|
||||
* @return the list of LoadSpec, could be empty list
|
||||
* @throws IOException if exception occurs reading the classes.dex file.
|
||||
*/
|
||||
private List<LoadSpec> processDEX(ZipFileSystem zipFS, GFile file, TaskMonitor monitor)
|
||||
throws IOException {
|
||||
|
||||
monitor.setMessage("Reading classes.dex ...");
|
||||
List<LoadSpec> loadSpecs = new ArrayList<>();
|
||||
try {
|
||||
ByteProvider byteProvider =
|
||||
zipFS.getByteProvider(file, monitor);
|
||||
BinaryReader reader = new BinaryReader(byteProvider, true);
|
||||
DexHeader header = DexHeaderFactory.getDexHeader(reader);
|
||||
if (DexConstants.DEX_MAGIC_BASE.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 (CancelledException e) {
|
||||
//ignore
|
||||
}
|
||||
return loadSpecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the Android APK as a ZIP file system.
|
||||
* @param provider the byte provider
|
||||
* @param monitor the task monitor
|
||||
* @return the ZipFileSystem
|
||||
*/
|
||||
private ZipFileSystem openAPK(ByteProvider provider, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
FileSystemService fsService = FileSystemService.getInstance();
|
||||
return fsService.mountSpecificFileSystem(provider.getFSRL(), ZipFileSystem.class, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the Android manifest XML file system.
|
||||
* @param name the name of the file
|
||||
* @param provider the byte provider
|
||||
* @param monitor the task monitor
|
||||
* @return the AndroidXmlFileSystem
|
||||
*/
|
||||
private AndroidXmlFileSystem openManifest(String name, ByteProvider provider,
|
||||
TaskMonitor monitor) {
|
||||
|
||||
AndroidXmlFileSystem xmlFS = new AndroidXmlFileSystem(name, provider);
|
||||
xmlFS.setFilesystemService(FileSystemService.getInstance());
|
||||
xmlFS.setFSRL(provider.getFSRL().getFS());
|
||||
try {
|
||||
xmlFS.open(monitor);
|
||||
}
|
||||
catch (CancelledException | IOException e) {
|
||||
//ignore
|
||||
}
|
||||
return xmlFS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Links the DEX programs together.
|
||||
* @param programList the list of DEX files loaded as programs
|
||||
* @param log the message log
|
||||
* @param monitor the task monitor
|
||||
*/
|
||||
private void link(List<Program> programList, MessageLog log, TaskMonitor monitor) {
|
||||
MultiDexLinker linker = new MultiDexLinker(programList);
|
||||
try {
|
||||
linker.link(monitor);
|
||||
linker.clear(monitor);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.appendException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -13,14 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.file.formats.android.cdex;
|
||||
package ghidra.app.util.opinion;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.opinion.*;
|
||||
import ghidra.file.formats.android.cdex.CDexConstants;
|
||||
import ghidra.file.formats.android.dex.DexHeaderFactory;
|
||||
import ghidra.file.formats.android.dex.format.DexConstants;
|
||||
import ghidra.file.formats.android.dex.format.DexHeader;
|
@ -17,18 +17,35 @@ package ghidra.app.util.opinion;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
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.format.ClassDataItem;
|
||||
import ghidra.file.formats.android.dex.format.ClassDefItem;
|
||||
import ghidra.file.formats.android.dex.format.CodeItem;
|
||||
import ghidra.file.formats.android.dex.format.DexConstants;
|
||||
import ghidra.file.formats.android.dex.format.DexHeader;
|
||||
import ghidra.file.formats.android.dex.format.EncodedMethod;
|
||||
import ghidra.file.formats.android.dex.format.MethodIDItem;
|
||||
import ghidra.file.formats.android.dex.util.DexUtil;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.PointerDataType;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class DexLoader extends AbstractLibrarySupportLoader {
|
||||
@ -102,6 +119,8 @@ public class DexLoader extends AbstractLibrarySupportLoader {
|
||||
createMethods(program, header, item, classDataItem.getVirtualMethods(), monitor,
|
||||
log);
|
||||
}
|
||||
|
||||
markupMethodLookup(program, header, monitor, log);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.appendException(e);
|
||||
@ -157,6 +176,72 @@ public class DexLoader extends AbstractLibrarySupportLoader {
|
||||
}
|
||||
}
|
||||
|
||||
protected void markupMethodLookup(Program program, DexHeader header, TaskMonitor monitor,
|
||||
MessageLog log)
|
||||
throws Exception {
|
||||
|
||||
monitor.setMessage("DEX: processing methods");
|
||||
monitor.setMaximum(header.getMethodIdsSize());
|
||||
monitor.setProgress(0);
|
||||
|
||||
int methodIndex = 0;
|
||||
for (MethodIDItem item : header.getMethods()) {
|
||||
monitor.checkCanceled();
|
||||
monitor.incrementProgress(1);
|
||||
|
||||
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");
|
||||
|
||||
Address methodIndexAddress = DexUtil.toLookupAddress(program, methodIndex);
|
||||
|
||||
if (program.getMemory().getInt(methodIndexAddress) == -1) {
|
||||
program.getListing()
|
||||
.setComment(methodIndexAddress, CodeUnit.PLATE_COMMENT, builder.toString());
|
||||
|
||||
// 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-" + methodIndex, externalName, null,
|
||||
SourceType.ANALYSIS, 0, RefType.DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
program.getListing().createData(methodIndexAddress, new PointerDataType());
|
||||
|
||||
++methodIndex;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
protected Address toAddr(Program program, long offset) {
|
||||
return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
||||
}
|
||||
@ -172,4 +257,27 @@ public class DexLoader extends AbstractLibrarySupportLoader {
|
||||
protected String getMonitorMessageSecondary() {
|
||||
return "DEX Loader: creating method byte code";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCreateExportSymbolFiles(List<Option> options) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isLoadLibraries(List<Option> options) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -432,6 +432,9 @@ public class DexHeaderFormatMarkup {
|
||||
|
||||
if (item.getStaticValuesOffset() > 0) {
|
||||
EncodedArrayItem staticValues = item.getStaticValues();
|
||||
if (staticValues == null) {
|
||||
return;
|
||||
}
|
||||
Address staticAddress =
|
||||
baseAddress.add(DexUtil.adjustOffset(item.getStaticValuesOffset(), header));
|
||||
DataType staticDataType = staticValues.toDataType();
|
||||
@ -454,6 +457,9 @@ public class DexHeaderFormatMarkup {
|
||||
|
||||
if (item.getClassDataOffset() > 0) {
|
||||
ClassDataItem classDataItem = item.getClassDataItem();
|
||||
if (classDataItem == null) {
|
||||
return;
|
||||
}
|
||||
Address classDataAddress =
|
||||
baseAddress.add(DexUtil.adjustOffset(item.getClassDataOffset(), header));
|
||||
DataType classDataDataType = classDataItem.toDataType();
|
||||
@ -656,6 +662,9 @@ public class DexHeaderFormatMarkup {
|
||||
|
||||
if (item.getAnnotationsOffset() > 0) {
|
||||
AnnotationsDirectoryItem annotationsDirectoryItem = item.getAnnotationsDirectoryItem();
|
||||
if (annotationsDirectoryItem == null) {
|
||||
return;
|
||||
}
|
||||
Address annotationsAddress =
|
||||
baseAddress.add(DexUtil.adjustOffset(item.getAnnotationsOffset(), header));
|
||||
DataType annotationsDataType = annotationsDirectoryItem.toDataType();
|
||||
@ -728,6 +737,9 @@ public class DexHeaderFormatMarkup {
|
||||
|
||||
if (item.getInterfacesOffset() > 0) {
|
||||
TypeList interfaces = item.getInterfaces();
|
||||
if (interfaces == null) {
|
||||
return;
|
||||
}
|
||||
Address interfaceAddress =
|
||||
baseAddress.add(DexUtil.adjustOffset(item.getInterfacesOffset(), header));
|
||||
DataType interfaceDataType = interfaces.toDataType();
|
||||
@ -791,29 +803,6 @@ public class DexHeaderFormatMarkup {
|
||||
|
||||
api.setPlateComment(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(externalAddress, methodName, classNameSpace, log);
|
||||
if (methodSymbol != null) {
|
||||
String externalName = methodSymbol.getName(true);
|
||||
program.getReferenceManager()
|
||||
.addExternalReference(methodIndexAddress,
|
||||
"EXTERNAL.dex-" + methodIndex, externalName, null,
|
||||
SourceType.ANALYSIS, 0, RefType.DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
api.createData(methodIndexAddress, new PointerDataType());
|
||||
|
||||
++methodIndex;
|
||||
|
||||
address = address.add(dataType.getLength());
|
||||
@ -876,24 +865,27 @@ public class DexHeaderFormatMarkup {
|
||||
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()) + " ");
|
||||
if (parameters != null) {
|
||||
for (TypeItem parameter : parameters.getItems()) {
|
||||
monitor.checkCanceled();
|
||||
builder.append(
|
||||
DexUtil.convertTypeIndexToString(header, parameter.getType()) + " ");
|
||||
}
|
||||
|
||||
DataType parametersDT = parameters.toDataType();
|
||||
|
||||
Address parametersAddress =
|
||||
baseAddress.add(DexUtil.adjustOffset(item.getParametersOffset(), header));
|
||||
|
||||
api.createData(parametersAddress, parametersDT);
|
||||
|
||||
builder.append("\nParameters Address: " + parametersAddress);
|
||||
builder.append("\n");
|
||||
|
||||
// add reference to the "parametersOffset" field
|
||||
api.createMemoryReference(data.getComponent(2), parametersAddress,
|
||||
RefType.DATA);
|
||||
}
|
||||
|
||||
DataType parametersDT = parameters.toDataType();
|
||||
|
||||
Address parametersAddress =
|
||||
baseAddress.add(DexUtil.adjustOffset(item.getParametersOffset(), header));
|
||||
|
||||
api.createData(parametersAddress, parametersDT);
|
||||
|
||||
builder.append("\nParameters Address: " + parametersAddress);
|
||||
builder.append("\n");
|
||||
|
||||
// add reference to the "parametersOffset" field
|
||||
api.createMemoryReference(data.getComponent(2), parametersAddress, RefType.DATA);
|
||||
}
|
||||
|
||||
api.setPlateComment(address, builder.toString());
|
||||
@ -970,6 +962,10 @@ public class DexHeaderFormatMarkup {
|
||||
// markup string data items
|
||||
Address stringDataAddress =
|
||||
baseAddress.add(DexUtil.adjustOffset(item.getStringDataOffset(), header));
|
||||
|
||||
if (!program.getMemory().contains(stringDataAddress)) {
|
||||
continue;
|
||||
}
|
||||
StringDataItem stringDataItem = item.getStringDataItem();
|
||||
|
||||
if (stringDataItem == null) {
|
||||
|
@ -129,11 +129,15 @@ class DexHeaderFragmentManager {
|
||||
if (!isCreateFragments) {
|
||||
return;
|
||||
}
|
||||
|
||||
monitor.setMessage("DEX: creating fragments");
|
||||
if (header.getDataSize() > 0) {
|
||||
Address start = baseAddress.add(header.getDataOffset());
|
||||
api.createFragment("data", start, header.getDataSize());
|
||||
try {
|
||||
api.createFragment("data", start, header.getDataSize());
|
||||
}
|
||||
catch (NotFoundException e) {
|
||||
//ignore, case for incomplete CDEX
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,8 +54,11 @@ public class ClassDefItem implements StructConverter {
|
||||
if (interfacesOffset > 0) {
|
||||
long oldIndex = reader.getPointerIndex();
|
||||
try {
|
||||
reader.setPointerIndex(DexUtil.adjustOffset(interfacesOffset, dexHeader));
|
||||
_interfaces = new TypeList(reader);
|
||||
int adjustOffset = DexUtil.adjustOffset(interfacesOffset, dexHeader);
|
||||
if (reader.isValidIndex(adjustOffset)) {
|
||||
reader.setPointerIndex(adjustOffset);
|
||||
_interfaces = new TypeList(reader);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
reader.setPointerIndex(oldIndex);
|
||||
@ -65,8 +68,11 @@ public class ClassDefItem implements StructConverter {
|
||||
if (annotationsOffset > 0) {
|
||||
long oldIndex = reader.getPointerIndex();
|
||||
try {
|
||||
reader.setPointerIndex(DexUtil.adjustOffset(annotationsOffset, dexHeader));
|
||||
_annotationsDirectoryItem = new AnnotationsDirectoryItem(reader, dexHeader);
|
||||
int adjustOffset = DexUtil.adjustOffset(annotationsOffset, dexHeader);
|
||||
if (reader.isValidIndex(adjustOffset)) {
|
||||
reader.setPointerIndex(adjustOffset);
|
||||
_annotationsDirectoryItem = new AnnotationsDirectoryItem(reader, dexHeader);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
reader.setPointerIndex(oldIndex);
|
||||
@ -76,8 +82,11 @@ public class ClassDefItem implements StructConverter {
|
||||
if (classDataOffset > 0) {
|
||||
long oldIndex = reader.getPointerIndex();
|
||||
try {
|
||||
reader.setPointerIndex(DexUtil.adjustOffset(classDataOffset, dexHeader));
|
||||
_classDataItem = new ClassDataItem(reader, dexHeader);
|
||||
int adjustOffset = DexUtil.adjustOffset(classDataOffset, dexHeader);
|
||||
if (reader.isValidIndex(adjustOffset)) {
|
||||
reader.setPointerIndex(adjustOffset);
|
||||
_classDataItem = new ClassDataItem(reader, dexHeader);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
reader.setPointerIndex(oldIndex);
|
||||
@ -87,8 +96,11 @@ public class ClassDefItem implements StructConverter {
|
||||
if (staticValuesOffset > 0) {
|
||||
long oldIndex = reader.getPointerIndex();
|
||||
try {
|
||||
reader.setPointerIndex(DexUtil.adjustOffset(staticValuesOffset, dexHeader));
|
||||
_staticValues = new EncodedArrayItem(reader);
|
||||
int adjustOffset = DexUtil.adjustOffset(staticValuesOffset, dexHeader);
|
||||
if (reader.isValidIndex(adjustOffset)) {
|
||||
reader.setPointerIndex(adjustOffset);
|
||||
_staticValues = new EncodedArrayItem(reader);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
reader.setPointerIndex(oldIndex);
|
||||
@ -170,10 +182,12 @@ public class ClassDefItem implements StructConverter {
|
||||
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 (interfaces != null) {
|
||||
for (TypeItem type : interfaces.getItems()) {
|
||||
monitor.checkCanceled();
|
||||
builder.append(
|
||||
"\t" + DexUtil.convertTypeIndexToString(header, type.getType()) + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,9 +211,9 @@ public class CodeItem implements StructConverter {
|
||||
structure.add(DWORD, "debug_info_off", null);
|
||||
structure.add(DWORD, "insns_size", null);
|
||||
structure.add(new ArrayDataType(WORD, instructionSize, WORD.getLength()), "insns", null);
|
||||
if (hasPadding()) {
|
||||
structure.add(WORD, "padding", null);
|
||||
}
|
||||
// if (hasPadding()) {
|
||||
// structure.add(WORD, "padding", null);
|
||||
// }
|
||||
structure.setCategoryPath(new CategoryPath("/dex/code_item"));
|
||||
return structure;
|
||||
}
|
||||
|
@ -124,8 +124,9 @@ public class DexHeader implements StructConverter {
|
||||
}
|
||||
parsed = true;
|
||||
|
||||
reader.setPointerIndex(DexUtil.adjustOffset(mapOffset, this));
|
||||
if (mapOffset > 0) {
|
||||
if (mapOffset > 0 && reader.isValidIndex(mapOffset)) {
|
||||
int adjustedMapOffset = DexUtil.adjustOffset(mapOffset, this);
|
||||
reader.setPointerIndex(adjustedMapOffset);
|
||||
mapList = new MapList(reader);
|
||||
}
|
||||
|
||||
@ -154,9 +155,11 @@ public class DexHeader implements StructConverter {
|
||||
methods.add(new MethodIDItem(reader));
|
||||
}
|
||||
|
||||
reader.setPointerIndex(classDefsIdsOffset);
|
||||
for (int i = 0; i < classDefsIdsSize; ++i) {
|
||||
classDefs.add(new ClassDefItem(reader, this));
|
||||
if (reader.isValidIndex(classDefsIdsOffset)) {
|
||||
reader.setPointerIndex(classDefsIdsOffset);
|
||||
for (int i = 0; i < classDefsIdsSize; ++i) {
|
||||
classDefs.add(new ClassDefItem(reader, this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,8 +40,12 @@ public class PrototypesIDItem implements StructConverter {
|
||||
if (parametersOffset > 0) {
|
||||
long oldIndex = reader.getPointerIndex();
|
||||
try {
|
||||
reader.setPointerIndex(DexUtil.adjustOffset(parametersOffset, dexHeader));
|
||||
_parameters = new TypeList(reader);
|
||||
//starting in Android 12, CDEX files are incomplete
|
||||
int adjustedParametersOffset = DexUtil.adjustOffset(parametersOffset, dexHeader);
|
||||
if (reader.isValidIndex(adjustedParametersOffset)) {
|
||||
reader.setPointerIndex(adjustedParametersOffset);
|
||||
_parameters = new TypeList(reader);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
reader.setPointerIndex(oldIndex);
|
||||
|
@ -0,0 +1,301 @@
|
||||
/* ###
|
||||
* 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.multidex;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.file.formats.android.dex.DexHeaderFactory;
|
||||
import ghidra.file.formats.android.dex.format.DexHeader;
|
||||
import ghidra.file.formats.android.dex.format.MethodIDItem;
|
||||
import ghidra.file.formats.android.dex.util.DexUtil;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.DataIterator;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.symbol.ExternalLocation;
|
||||
import ghidra.program.model.symbol.ExternalManager;
|
||||
import ghidra.program.model.symbol.ExternalReference;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.ReferenceManager;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Links multidex APKs together
|
||||
* via the "method_lookup" section using
|
||||
* external references.
|
||||
*/
|
||||
public final class MultiDexLinker {
|
||||
|
||||
private List<Program> programs;
|
||||
private Map<Program, DexHeader> dexMap = new HashMap<>();
|
||||
private Map<DexHeader, Map<ClassMethodPrototype, Integer>> cmpMap = new HashMap<>();
|
||||
private Map<Program, List<Address>> changeMap = new HashMap<>();
|
||||
|
||||
private class ClassMethodPrototype {
|
||||
private final String className;
|
||||
private final String methodName;
|
||||
private final String prototype;
|
||||
|
||||
ClassMethodPrototype(String className, String methodName, String prototype) {
|
||||
this.className = className;
|
||||
this.methodName = methodName;
|
||||
this.prototype = prototype;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof ClassMethodPrototype) {
|
||||
ClassMethodPrototype cmp = (ClassMethodPrototype) obj;
|
||||
return cmp.className.equals(className) &&
|
||||
cmp.methodName.equals(methodName) &&
|
||||
cmp.prototype.equals(prototype);
|
||||
}
|
||||
return super.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return className.hashCode() + methodName.hashCode() + prototype.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
private class ProgramAddress {
|
||||
private final Program program;
|
||||
private final Address address;
|
||||
|
||||
ProgramAddress(Program program, Address address) {
|
||||
this.program = program;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof ProgramAddress) {
|
||||
ProgramAddress pa = (ProgramAddress) obj;
|
||||
return this.program.equals(pa.program) && this.address.equals(pa.address);
|
||||
}
|
||||
return super.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return program.hashCode() + address.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public MultiDexLinker(List<Program> programs) {
|
||||
this.programs = new ArrayList<>(programs);//create copy of list
|
||||
}
|
||||
|
||||
public void link(TaskMonitor monitor) throws CancelledException, IOException,
|
||||
MemoryAccessException, InvalidInputException, DuplicateNameException {
|
||||
|
||||
Objects.requireNonNull(monitor);
|
||||
cacheHeaderInfo(monitor);
|
||||
linkPrograms(monitor);
|
||||
}
|
||||
|
||||
public void clear(TaskMonitor monitor) throws CancelledException {
|
||||
Objects.requireNonNull(monitor);
|
||||
programs.clear();
|
||||
dexMap.clear();
|
||||
for (DexHeader header : cmpMap.keySet()) {
|
||||
monitor.checkCanceled();
|
||||
cmpMap.get(header).clear();
|
||||
}
|
||||
cmpMap.clear();
|
||||
changeMap.clear();
|
||||
}
|
||||
|
||||
public List<Address> getChangeList(Program program) {
|
||||
return Objects.requireNonNull(changeMap.get(program));
|
||||
}
|
||||
|
||||
private void linkPrograms(TaskMonitor monitor) throws CancelledException, MemoryAccessException,
|
||||
InvalidInputException, DuplicateNameException, IOException {
|
||||
|
||||
monitor.setMaximum(programs.size());
|
||||
monitor.setProgress(1);
|
||||
for (Program program : programs) {
|
||||
monitor.checkCanceled();
|
||||
monitor.incrementProgress(1);
|
||||
monitor.setMessage(program.getName());
|
||||
|
||||
DexHeader dexHeader = dexMap.get(program);
|
||||
|
||||
List<Address> changeList = new ArrayList<>();
|
||||
changeMap.put(program, changeList);
|
||||
|
||||
int transaction = program.startTransaction("multi-dex");
|
||||
try {
|
||||
ReferenceManager referenceManager = program.getReferenceManager();
|
||||
ExternalManager externalManager = program.getExternalManager();
|
||||
|
||||
MemoryBlock methodLookupBlock = program.getMemory().getBlock("method_lookup");
|
||||
AddressSet methodLookupRange =
|
||||
new AddressSet(methodLookupBlock.getStart(), methodLookupBlock.getEnd());
|
||||
|
||||
DataIterator dataIterator =
|
||||
program.getListing().getDefinedData(methodLookupRange, true);
|
||||
|
||||
while (dataIterator.hasNext()) {
|
||||
Data data = dataIterator.next();
|
||||
|
||||
monitor.checkCanceled();
|
||||
monitor.setMessage(program.getName() + " " + data.getMinAddress());
|
||||
|
||||
if (program.getMemory().getInt(data.getMinAddress()) != -1) {
|
||||
continue;//internally linked, ignore
|
||||
}
|
||||
|
||||
if (isExternalReferenceResolved(program, data, monitor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int methodIndex =
|
||||
(int) data.getMinAddress().subtract(methodLookupBlock.getStart()) / 4;
|
||||
|
||||
MethodIDItem methodIDItem = dexHeader.getMethods().get(methodIndex);
|
||||
String className =
|
||||
DexUtil.convertTypeIndexToString(dexHeader, methodIDItem.getClassIndex());
|
||||
String prototype = DexUtil.convertPrototypeIndexToString(dexHeader,
|
||||
methodIDItem.getProtoIndex());
|
||||
String methodName =
|
||||
DexUtil.convertToString(dexHeader, methodIDItem.getNameIndex());
|
||||
|
||||
ProgramAddress pa =
|
||||
findInOtherProgram(program, className, prototype, methodName, monitor);
|
||||
|
||||
if (pa == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (externalManager.getExternalLibraryPath(pa.program.getName()) == null) {
|
||||
externalManager.setExternalPath(pa.program.getName(),
|
||||
pa.program.getDomainFile().getPathname(), true);
|
||||
}
|
||||
|
||||
referenceManager.addExternalReference(data.getMinAddress(),
|
||||
pa.program.getName(), null, pa.address, SourceType.ANALYSIS, 0,
|
||||
RefType.EXTERNAL_REF);
|
||||
|
||||
changeList.add(data.getMinAddress());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(transaction, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ProgramAddress findInOtherProgram(Program program, String className,
|
||||
String prototype, String methodName, TaskMonitor monitor)
|
||||
throws CancelledException, IOException, MemoryAccessException {
|
||||
|
||||
ClassMethodPrototype cmp =
|
||||
new ClassMethodPrototype(className, methodName, prototype);
|
||||
|
||||
for (Program otherProgram : programs) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
if (otherProgram.equals(program)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DexHeader otherDexHeader = dexMap.get(otherProgram);
|
||||
|
||||
Map<ClassMethodPrototype, Integer> map = cmpMap.get(otherDexHeader);
|
||||
|
||||
Integer otherMethodIndex = map.get(cmp);
|
||||
|
||||
if (otherMethodIndex != null) {
|
||||
Address otherAddress = DexUtil.toLookupAddress(program, otherMethodIndex);
|
||||
|
||||
if (otherProgram.getMemory().getInt(otherAddress) != -1) {
|
||||
return new ProgramAddress(otherProgram, otherAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void cacheHeaderInfo(TaskMonitor monitor) throws CancelledException, IOException {
|
||||
monitor.setMaximum(programs.size());
|
||||
monitor.setProgress(1);
|
||||
for (Program program : programs) {
|
||||
monitor.checkCanceled();
|
||||
monitor.incrementProgress(1);
|
||||
monitor.setMessage("Caching DEX header for " + program.getName() + "...");
|
||||
|
||||
DexHeader dexHeader = DexHeaderFactory.getDexHeader(program);
|
||||
dexMap.put(program, dexHeader);
|
||||
|
||||
Map<ClassMethodPrototype, Integer> map = new HashMap<>();
|
||||
cmpMap.put(dexHeader, map);
|
||||
|
||||
int index = 0;
|
||||
for (MethodIDItem item : dexHeader.getMethods()) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
String className =
|
||||
DexUtil.convertTypeIndexToString(dexHeader, item.getClassIndex());
|
||||
|
||||
String methodName = DexUtil.convertToString(dexHeader, item.getNameIndex());
|
||||
|
||||
String prototype =
|
||||
DexUtil.convertPrototypeIndexToString(dexHeader, item.getProtoIndex());
|
||||
|
||||
ClassMethodPrototype cmp =
|
||||
new ClassMethodPrototype(className, methodName, prototype);
|
||||
|
||||
map.put(cmp, index);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isExternalReferenceResolved(Program program, Data data, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
||||
ExternalManager externalManager = program.getExternalManager();
|
||||
Reference[] referencesFrom = data.getReferencesFrom();
|
||||
for (Reference reference : referencesFrom) {
|
||||
monitor.checkCanceled();
|
||||
if (reference instanceof ExternalReference) {
|
||||
ExternalReference extref = (ExternalReference) reference;
|
||||
ExternalLocation externalLocation = extref.getExternalLocation();
|
||||
if (externalManager.getExternalLibraryPath(
|
||||
externalLocation.getLibraryName()) != null) {
|
||||
return true;//already resolved
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,8 +1,65 @@
|
||||
<opinions>
|
||||
<constraint loader="Dalvik Executable (DEX)" compilerSpecID="default">
|
||||
<constraint primary="1" processor="Dalvik" endian="little" size="32" />
|
||||
<constraint primary="1" processor="Dalvik" endian="little" size="32" />
|
||||
</constraint>
|
||||
<constraint loader="Compact Dalvik Executable (CDEX)" compilerSpecID="default">
|
||||
<constraint primary="1" processor="Dalvik" endian="little" size="32" />
|
||||
<constraint primary="1" processor="Dalvik" endian="little" size="32" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" processor="Dalvik" endian="little" size="32" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="K" processor="Dalvik" endian="little" size="32" variant="DEX KitKat" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="4" processor="Dalvik" endian="little" size="32" variant="DEX KitKat" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="L" processor="Dalvik" endian="little" size="32" variant="DEX Lollipop" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="5" processor="Dalvik" endian="little" size="32" variant="DEX Lollipop" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="M" processor="Dalvik" endian="little" size="32" variant="DEX Marshmallow" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="6" processor="Dalvik" endian="little" size="32" variant="DEX Marshmallow" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="N" processor="Dalvik" endian="little" size="32" variant="DEX Nougat" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="7" processor="Dalvik" endian="little" size="32" variant="DEX Nougat" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="O" processor="Dalvik" endian="little" size="32" variant="DEX Oreo" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="8" processor="Dalvik" endian="little" size="32" variant="DEX Oreo" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="P" processor="Dalvik" endian="little" size="32" variant="DEX Pie" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="9" processor="Dalvik" endian="little" size="32" variant="DEX Pie" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="Q" processor="Dalvik" endian="little" size="32" variant="DEX Android10" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="10" processor="Dalvik" endian="little" size="32" variant="DEX Android10" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="R" processor="Dalvik" endian="little" size="32" variant="DEX Android11" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="11" processor="Dalvik" endian="little" size="32" variant="DEX Android11" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="S" processor="Dalvik" endian="little" size="32" variant="DEX Android12" />
|
||||
</constraint>
|
||||
<constraint loader="Android APK" compilerSpecID="default">
|
||||
<constraint primary="1" secondary="12" processor="Dalvik" endian="little" size="32" variant="DEX Android12" />
|
||||
</constraint>
|
||||
</opinions>
|
||||
|
Loading…
Reference in New Issue
Block a user