com.github.unidbg.linux.android.dvm.BaseVM Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of unidbg Show documentation
Show all versions of unidbg Show documentation
Allows you to emulate an Android ARM32 and/or ARM64 native library
package com.github.unidbg.linux.android.dvm;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.ElfLibraryFile;
import com.github.unidbg.linux.android.dvm.api.Signature;
import com.github.unidbg.spi.LibraryFile;
import net.dongliu.apk.parser.ApkFile;
import net.dongliu.apk.parser.bean.ApkMeta;
import net.dongliu.apk.parser.bean.ApkSigner;
import net.dongliu.apk.parser.bean.CertificateMeta;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.nio.ByteBuffer;
import java.security.cert.CertificateException;
import java.util.*;
public abstract class BaseVM implements VM {
private static final Log log = LogFactory.getLog(BaseVM.class);
final Map classMap = new HashMap<>();
Jni jni;
DvmObject> throwable;
boolean verbose;
@Override
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
@Override
public void throwException(DvmObject> throwable) {
this.throwable = throwable;
}
@Override
public final void setJni(Jni jni) {
this.jni = jni;
}
private final Emulator> emulator;
private final File apkFile;
final Set notFoundClassSet = new HashSet<>();
@Override
public void addNotFoundClass(String className) {
notFoundClassSet.add(className);
}
BaseVM(Emulator> emulator, File apkFile) {
this.emulator = emulator;
this.apkFile = apkFile;
}
final Map> globalObjectMap = new HashMap<>();
final Map> localObjectMap = new HashMap<>();
@Override
public final DvmClass resolveClass(String className, DvmClass... interfaceClasses) {
long hash = Objects.hash(className) & 0xffffffffL;
DvmClass dvmClass = classMap.get(hash);
if (dvmClass == null) {
dvmClass = new DvmClass(this, className, interfaceClasses);
classMap.put(hash, dvmClass);
addObject(dvmClass, true);
}
return dvmClass;
}
final int addObject(DvmObject> object, boolean global) {
if (object == null) {
return 0;
} else {
long hash = object.hashCode() & 0xffffffffL;
if (log.isDebugEnabled()) {
log.debug("addObject hash=0x" + Long.toHexString(hash));
}
if (global) {
globalObjectMap.put(hash, object);
} else {
localObjectMap.put(hash, object);
}
return (int) hash;
}
}
@Override
public final int addLocalObject(DvmObject> object) {
if (object == null) {
return JNI_NULL;
}
return addObject(object, false);
}
@SuppressWarnings("unchecked")
@Override
public final > T getObject(long hash) {
if (localObjectMap.containsKey(hash)) {
return (T) localObjectMap.get(hash);
} else {
return (T) globalObjectMap.get(hash);
}
}
@Override
public final DvmClass findClass(String className) {
return classMap.get(Objects.hash(className) & 0xffffffffL);
}
@Override
public final void deleteLocalRefs() {
localObjectMap.clear();
}
private class ApkLibraryFile implements LibraryFile {
private final File apkFile;
private final String soName;
private final byte[] soData;
private final String packageName;
ApkLibraryFile(File apkFile, String soName, byte[] soData, String packageName) {
this.apkFile = apkFile;
this.soName = soName;
this.soData = soData;
this.packageName = packageName;
}
@Override
public String getName() {
return soName;
}
@Override
public String getMapRegionName() {
return "/data/app-lib/" + packageName + "-1/" + soName;
}
@Override
public LibraryFile resolveLibrary(Emulator> emulator, String soName) throws IOException {
try (ApkFile apkFile = new ApkFile(this.apkFile)) {
byte[] libData = findLibrary(apkFile, soName);
return libData == null ? null : new ApkLibraryFile(this.apkFile, soName, libData, packageName);
}
}
@Override
public byte[] readToByteArray() {
return soData;
}
@Override
public ByteBuffer mapBuffer() {
return ByteBuffer.wrap(soData);
}
@Override
public String getPath() {
return "/data/app-lib/" + packageName + "-1";
}
}
abstract byte[] findLibrary(ApkFile apkFile, String soName) throws IOException;
@Override
public final DalvikModule loadLibrary(String libname, boolean forceCallInit) {
if (apkFile == null) {
throw new UnsupportedOperationException();
}
String soName = "lib" + libname + ".so";
ApkLibraryFile libraryFile = findLibrary(apkFile, soName);
if (libraryFile == null) {
File split = new File(apkFile.getParentFile(), emulator.is64Bit() ? "config.arm64_v8a.apk" : "config.armeabi_v7a.apk");
if (split.canRead()) {
libraryFile = findLibrary(split, soName);
}
}
if (libraryFile == null) {
throw new IllegalStateException("load library failed: " + libname);
}
Module module = emulator.getMemory().load(libraryFile, forceCallInit);
return new DalvikModule(this, module);
}
private ApkLibraryFile findLibrary(File file, String soName) {
try (ApkFile apkFile = new ApkFile(file)) {
byte[] libData = findLibrary(apkFile, soName);
if (libData == null) {
return null;
}
return new ApkLibraryFile(file, soName, libData, apkFile.getApkMeta().getPackageName());
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private Signature[] signatures;
Signature[] getSignatures() {
if (apkFile == null) {
return null;
}
if (signatures != null) {
return signatures;
}
ApkFile apkFile = null;
try {
apkFile = new ApkFile(this.apkFile);
List signatures = new ArrayList<>(10);
for (ApkSigner signer : apkFile.getApkSingers()) {
for (CertificateMeta meta : signer.getCertificateMetas()) {
signatures.add(new Signature(this, meta));
}
}
this.signatures = signatures.toArray(new Signature[0]);
return this.signatures;
} catch (IOException | CertificateException e) {
throw new IllegalStateException(e);
} finally {
IOUtils.closeQuietly(apkFile);
}
}
private ApkMeta apkMeta;
@Override
public String getPackageName() {
if (apkFile == null) {
return null;
}
if (apkMeta != null) {
return apkMeta.getPackageName();
}
ApkFile apkFile = null;
try {
apkFile = new ApkFile(this.apkFile);
apkMeta = apkFile.getApkMeta();
return apkMeta.getPackageName();
} catch (IOException e) {
throw new IllegalStateException(e);
} finally {
IOUtils.closeQuietly(apkFile);
}
}
@Override
public String getManifestXml() {
if (apkFile == null) {
return null;
}
ApkFile apkFile = null;
try {
apkFile = new ApkFile(this.apkFile);
return apkFile.getManifestXml();
} catch (IOException e) {
throw new IllegalStateException(e);
} finally {
IOUtils.closeQuietly(apkFile);
}
}
@Override
public byte[] openAsset(String fileName) {
if (apkFile == null) {
return null;
}
ApkFile apkFile = null;
try {
apkFile = new ApkFile(this.apkFile);
return apkFile.getFileData("assets/" + fileName);
} catch (IOException e) {
throw new IllegalStateException(e);
} finally {
IOUtils.closeQuietly(apkFile);
}
}
String getVersionName() {
if (apkFile == null) {
return null;
}
if (apkMeta != null) {
return apkMeta.getVersionName();
}
ApkFile apkFile = null;
try {
apkFile = new ApkFile(this.apkFile);
apkMeta = apkFile.getApkMeta();
return apkMeta.getVersionName();
} catch (IOException e) {
throw new IllegalStateException(e);
} finally {
IOUtils.closeQuietly(apkFile);
}
}
@Override
public final DalvikModule loadLibrary(File elfFile, boolean forceCallInit) {
Module module = emulator.getMemory().load(new ElfLibraryFile(elfFile), forceCallInit);
return new DalvikModule(this, module);
}
@Override
public final void printMemoryInfo() {
System.gc();
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heap = memoryMXBean.getHeapMemoryUsage();
MemoryUsage nonheap = memoryMXBean.getNonHeapMemoryUsage();
System.err.println("globalObjectSize=" + globalObjectMap.size() + ", localObjectSize=" + localObjectMap.size() + ", classSize=" + classMap.size());
System.err.println("heap: " + memoryUsage(heap) + ", nonheap: " + memoryUsage(nonheap));
}
private String toMB(long memory) {
return (memory * 100 / (1024 * 1024)) / 100F + "MB";
}
private String memoryUsage(MemoryUsage usage) {
return "init=" + toMB(usage.getInit()) + ", used="
+ toMB(usage.getUsed()) + ", committed="
+ toMB(usage.getCommitted()) + ", max="
+ toMB(usage.getMax());
}
@Override
public void callJNI_OnLoad(Emulator> emulator, Module module) {
new DalvikModule(this, module).callJNI_OnLoad(emulator);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy