在我测试抖音时,遇到如下崩溃日志:
getString pointer=unicorn@0xbfffe22c, size=14, encoding=UTF-8, ret=/system/bin/su
fstatat64 dirfd=-100, pathname=\system\bin\su, statbuf=unicorn@0xbfffe1c4, flags=0
[21:42:48 095] WARN [cn.banny.unidbg.linux.ARMSyscallHandler] (ARMSyscallHandler:1446) - fstatat64 dirfd=-100, pathname=\system\bin\su, statbuf=unicorn@0xbfffe1c4, flags=0
memory failed: address=0x0, size=1, value=0x0, user=null
unicorn.UnicornException: Invalid memory read (UC_ERR_READ_UNMAPPED)
at unicorn.Unicorn.emu_start(Native Method)
at cn.banny.unidbg.AbstractEmulator.emulate(AbstractEmulator.java:237)
at cn.banny.unidbg.AbstractEmulator.eFunc(AbstractEmulator.java:328)
at cn.banny.unidbg.arm.AbstractARMEmulator.eFunc(AbstractARMEmulator.java:201)
at cn.banny.unidbg.linux.LinuxModule.emulateFunction(LinuxModule.java:154)
at cn.banny.unidbg.linux.android.dvm.DvmClass.callStaticJniMethod(DvmClass.java:141)
at com.bytedance.frameworks.core.encrypt.cms.sign(cms.java:108)
at com.bytedance.frameworks.core.encrypt.cms.main(cms.java:96)
debugger break at: 0x40012a54
r0=0x0 r1=0x414e1111 r2=0x574598f8, r3=0x0 r4=0xf72315d4 r5=0x18353522 r6=0x574598f7 r7=0xd7977dd5 sb=0x61cad990 sl=0x414e1112 fp=0x1 ip=0x0 sp=0xbfffe160 lr=0x61cad990 pc=0x40012a54 cpsr: N=0, Z=1, C=1, V=0, T=1, mode=0b10000
=> [ libcms.so][0x12a55]*[* 01 9b ]*0x40012a54:*ldr r3, [sp, #4]
[ libcms.so] [0x12a57] [ 03 93 ] 0x40012a56: str r3, [sp, #0xc]
[ libcms.so] [0x12a59] [ 03 9b ] 0x40012a58: ldr r3, [sp, #0xc]
[ libcms.so] [0x12a5b] [ 1b 78 ] 0x40012a5a: ldrb r3, [r3]
[ libcms.so] [0x12a5d] [ 00 2b ] 0x40012a5c: cmp r3, #0
[ libcms.so] [0x12a5f] [ 23 46 ] 0x40012a5e: mov r3, r4
[ libcms.so] [0x12a61] [ 04 bf ] 0x40012a60: itt eq
[ libcms.so] [0x12a63] [ 43 f2 22 53 ] 0x40012a62: movweq r3, #0x3522
[ libcms.so] [0x12a67] [ c1 f6 35 03 ] 0x40012a66: movteq r3, #0x1835
[ libcms.so] [0x12a6b] [ 21 e0 ] 0x40012a6a: b #0x40012ab0
我的测试代码如下:
package com.bytedance.frameworks.core.encrypt;
import cn.banny.auxiliary.Inspector;
import cn.banny.unidbg.LibraryResolver;
import cn.banny.unidbg.Module;
import cn.banny.unidbg.arm.ARMEmulator;
import cn.banny.unidbg.linux.android.AndroidARMEmulator;
import cn.banny.unidbg.linux.android.AndroidResolver;
import cn.banny.unidbg.linux.android.dvm.DalvikModule;
import cn.banny.unidbg.linux.android.dvm.DvmClass;
import cn.banny.unidbg.linux.android.dvm.VM;
import cn.banny.unidbg.memory.Memory;
import java.io.File;
import java.io.IOException;
import cn.banny.unidbg.file.FileIO;
import cn.banny.unidbg.file.IOResolver;
import cn.banny.unidbg.linux.android.dvm.*;
import cn.banny.unidbg.linux.android.dvm.api.SystemService;
import cn.banny.unidbg.linux.file.ByteArrayFileIO;
import cn.banny.unidbg.linux.file.SimpleFileIO;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import java.io.;
import java.net.;
public class cms extends AbstractJni implements IOResolver {
private static final int WSG_CODE_OK = 0;
private static final String APP_PACKAGE_NAME = "com.ss.android.ugc.aweme";
private final ARMEmulator emulator;
private final VM vm;
private final DvmClass cmsDVM;
private final DvmClass userinfoDVM;
private final DvmClass tongdunDVM;
private static final String APK_PATH = "src/test/resources/app/660.apk";
private final Module module;
private cms() throws IOException {
Logger.getLogger("cn.banny.unidbg.AbstractEmulator").setLevel(Level.DEBUG);
emulator = new AndroidARMEmulator(APP_PACKAGE_NAME);
emulator.getSyscallHandler().addIOResolver(this);
System.out.println("== init ===");
final Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
memory.setCallInitFunction();
vm = emulator.createDalvikVM(new File(APK_PATH));
vm.setJni(this);
DalvikModule dm = vm.loadLibrary("cms", false);
dm.callJNI_OnLoad(emulator);
module = dm.getModule();
cmsDVM = vm.resolveClass("com/ss/sys/ces/a");
userinfoDVM = vm.resolveClass("com/ss/android/common/applog/UserInfo");
tongdunDVM = vm.resolveClass("com/ss/sys/secuni/b/c");
// IHookZz hookZz = HookZz.getInstance(emulator);
// hookZz.replace(module.base + 0x000733A0 + 1, new ReplaceCallback() {
// @Override
// public HookStatus onCall(Emulator emulator, long originFunction) {
// long currentTimeMillis = System.currentTimeMillis();
// EditableArm32RegisterContext context = emulator.getContext();
// context.setR1((int) currentTimeMillis);
// return HookStatus.LR(emulator, (int) (currentTimeMillis >> 32));
// }
// });
// emulator.attach().debug(emulator);
}
private void destroy() throws IOException {
emulator.close();
System.out.println("module=" + module);
System.out.println("== destroy ===");
}
public static void main(String[] args) throws Exception {
cms test = new cms();
test.sign();
test.destroy();
}
private void sign() {
userinfoDVM.callStaticJniMethod(emulator, "setAppId(I)V", 1128);
Number i = userinfoDVM.callStaticJniMethod(emulator, "initUser(Ljava/lang/String;)I", vm.addLocalObject(new StringObject(vm, "a3668f0afac72ca3f6c1697d29e0e1bb1fef4ab0285319b95ac39fa42c38d05f")));
System.out.println("initUser ret: " + i.intValue());
// Number ret = cmsDVM.callStaticJniMethod(emulator, "e([B)[B",
// vm.addLocalObject(new ByteArray("888888888".getBytes())));
DvmObject context = vm.resolveClass("android/content/Context").newObject(null);
Number ret = tongdunDVM.callStaticJniMethod(emulator, "n0(Landroid/content/Context;)[B", vm.addLocalObject(context));
long hash = ret.intValue() & 0xffffffffL;
ByteArray array = vm.getObject(hash);
Inspector.inspect(array.getValue(), "n0 ret=" + ret + ", offset=" + (System.currentTimeMillis()) + "ms");
vm.deleteLocalRefs();
}
private static final String INSTALL_PATH = "/data/app/com.ss.android.ugc.aweme-1.apk";
@Override
public FileIO resolve(File workDir, String pathname, int oflags) {
System.out.println("pathname: " + pathname);
//System.out.println("workdir: " + workDir.getAbsolutePath());
if (("proc/" + emulator.getPid() + "/status").equals(pathname)) {
return new ByteArrayFileIO(oflags, pathname, "TracerPid:\t0\n".getBytes());
}
else if("/proc/meminfo".equals(pathname))
{
return new SimpleFileIO(oflags, new File("src/main/resources/meminfo"), pathname);
}
else if("/proc/self/status".equals(pathname))
{
return new ByteArrayFileIO(oflags, pathname, "TracerPid:\t0\n".getBytes());
}
return null;
}
@Override
public DvmObject callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
if ("java/lang/System->getProperty(Ljava/lang/String;)Ljava/lang/String;".equals(signature)) {
StringObject string = varArg.getObject(0);
return new StringObject(vm, System.getProperty(string.getValue()));
}
return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
}
public DvmObject callObjectMethodV(BaseVM vm, DvmObject dvmObject, String signature, VaList vaList) {
System.out.println("CallObjectMethodV ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~: " + signature);
if ("Landroid/app/Application->getPackageManager()Landroid/content/pm/PackageManager;".equals(signature)) {
return vm.resolveClass("Landroid/content/pm/PackageManager;").newObject(null);
}
else if ("java/lang/String->getBytes(Ljava/lang/String;)[B".equals(signature)) {
StringObject string = (StringObject) dvmObject;
StringObject encoding = vaList.getObject(0);
System.err.println("string=" + string.getValue() + ", encoding=" + encoding.getValue());
try {
return new ByteArray(string.getValue().getBytes(encoding.getValue()));
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e);
}
} else if ("android/content/Context->getFilesDir()Ljava/io/File;".equals(signature)) {
return vm.resolveClass("Ljava/io/File;").newObject(null);
} else if ("Ljava/io/File;->getAbsolutePath()Ljava/lang/String;".equals(signature)) {
return new StringObject(vm, "/data/data/com.ss.android.ugc.aweme/files");
}else if ("android/telephony/TelephonyManager->getDeviceId()Ljava/lang/String;".equals(signature))
{
return new StringObject(vm, "123456789123456");
}
else if ("android/content/Context->getContentResolver()Landroid/content/ContentResolver;".equals(signature))
{
return vm.resolveClass("android/content/ContentResolver").newObject(null);
}
else if ("android/content/ContentResolver->call(Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Landroid/os/Bundle;)Landroid/os/Bundle;".equals(signature))
{
return vm.resolveClass("android/os/Bundle").newObject(null);
}
else if ("android/os/Bundle->getString(Ljava/lang/String;)Ljava/lang/String;".equals(signature))
{
return new StringObject(vm, "112233448855");
}
else if ("android/content/Context->getPackageManager()Landroid/content/pm/PackageManager;".equals(signature))
{
return vm.resolveClass("android/content/pm/PackageManager").newObject(null);
}
else if ("android/content/Context->getPackageName()Ljava/lang/String;".equals(signature))
{
return new StringObject(vm, "com.ss.android.ugc.aweme");
}
else {
StringObject serviceName = vaList.getObject(0);
return new SystemService(vm, serviceName.getValue());
}
}
public int getStaticIntField(BaseVM vm, DvmClass dvmClass, String signature) {
if ("android/os/Build$VERSION->SDK_INT:I".equals(signature)) {
return 21;
}
return -1;
}
public DvmObject getObjectField(BaseVM vm, DvmObject dvmObject, String signature) {
if ("Landroid/content/pm/ApplicationInfo;->sourceDir:Ljava/lang/String;".equals(signature)) {
return new StringObject(vm, APK_PATH);
}
return null;
}
public int callIntMethodV(BaseVM vm, DvmObject dvmObject, String signature, VaList vaList) {
if ("android/content/Context->checkSelfPermission(Ljava/lang/String;)I".equals(signature)) {
System.out.println("checkSelfPermission: " + vaList.getObject(0));
return 0;
}
return 0;
}
public DvmObject callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
if ("java/security/cert/CertificateFactory->getInstance(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;".equals(signature)) {
return vm.resolveClass("java/security/cert/CertificateFactory").newObject(null);
} else if ("java/security/cert/CertificateFactory->generateCertificate(Ljava/io/InputStream;)Ljava/security/cert/Certificate".equals(signature)) {
return vm.resolveClass("java/security/cert/Certificate").newObject(null);
} else if ("android/net/Uri->parse(Ljava/lang/String;)Landroid/net/Uri;".equals(signature)) {
return vm.resolveClass("android/net/Uri").newObject(null);
}
return null;
}
public boolean callStaticBooleanMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
if (signature.equals("android/os/Debug->isDebuggerConnected()Z"))
return false;
return false;
}
public DvmObject newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "java/io/ByteArrayInputStream-><init>([B)V": {
return vm.resolveClass("java/io/ByteArrayInputStream").newObject(vaList);
}
}
return null;
}
@Override
public DvmObject callObjectMethod(BaseVM vm, DvmObject dvmObject, String signature, VarArg varArg) {
System.out.println("signature: " + signature);
switch (signature) {
case "android/content/Context->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences;":
return vm.resolveClass("android/content/SharedPreferences").newObject(null);
case "android/content/SharedPreferences->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;": {
StringObject name = varArg.getObject(0);
StringObject defValue = varArg.getObject(1);
System.err.println("android/content/SharedPreferences->getString name=" + name.getValue() + ", defValue=" + defValue.getValue());
if ("wsg_conf".equals(name.getValue())) {
return new StringObject(vm, "mZUzsjDkxAqS/YFujbRrX3j/7jhZYgh8GHHqMzMdK9hXjXJ0lBEDsIFuKTfF48ps5gvaplUOkcmLNP5/FEmaqO4oUAad1HzDTQBZobDMeFpub20K+axGeu7PBZ+TztfPdVpMgVfmuHc7/axEHlZFBxadprthaQQtBfjzn2pf7Bp3RcreGaqnfO+QwENoQnpVo8Jj1CFy9vUnD8UDq4nO5S2ivoecc+g1luF9lLs5+4j1uCPK4QjSNhWp2HODbohlx01+uXQpMW1HVc0hjvEk/c8oKxFSJkqo8yZTpJ0Fg4lGc7f8znArzH1X6WanJIfA6O/XR/aUdAoAx9jHcC3QJM2kyM/F/aZB/7I0n5hWWwptaZfWf1nVxQMJrRzZ/X5bPEPCh+1czaeYVwZgEMfgLqo+yfGY8j0exLyd5vQnG4bIAV61X9P03+MXe4M67XmFaS0SsK3PuOYF5ht5I906QqXbcJcjF2YlkvnIC1UAJ70+MSKBLZErujU1GOBnPLLCyBt3PdzBNuwTVchZFPPcKYG7ZR5YEzpTNdxcu06Q8frtapYUus8rS7fHTbvUebdoR3oYDbe6hvzQX9E0Kq2YXOoByfy03I0/hSLW1GP+XYr2UIljf+9DaQxuZ4Qqk3p/PKh1QKlPL+bqC2jZhb1wLP4Vcg+unx/+diKBQsxVdW//J77vfUjj/1icJGrjbxHSUa4PB/YtfMFDfcPUAUOLvRWYyimyE3tvBUUs/RoFKMMFrvrzwIV9B+3oT/L292xdxa4U65DVHR1vULHWgxqW4HXOI9wBxh3GP5F9ZUDAHGjMQqjzaeV/w5LyscAJpsYpCWk9CTI4ixHpk5AvKRNNvq3z/SPZGPy+J0GkUbqT+gTsFyI370Levy3WHXoJxFZ3t2mgsyY1VS8rdbIh8rX5jdq0iGfIVw6D5pqRuhyz5Sgzoc45MJQCcXEniCrr5v4BkAafMybg7uRedFbqza1l1w==");
}
}
case "android/content/Context->getPackageCodePath()Ljava/lang/String;":
return new StringObject(vm, INSTALL_PATH);
case "android/content/Context->getSystemService(Ljava/lang/String;)Ljava/lang/Object;":
StringObject serviceName = varArg.getObject(0);
System.err.println("android/content/Context->getSystemService name=" + serviceName);
return new SystemService(vm, serviceName.getValue());
case "android/telephony/TelephonyManager->getDeviceId()Ljava/lang/String;":
return new StringObject(vm, "353490069873368");
case "java/net/URL->openConnection(Ljava/net/Proxy;)Ljava/net/URLConnection;":
Proxy proxy = (Proxy) varArg.getObject(0).getValue();
URL url = (URL) dvmObject.getValue();
try {
return vm.resolveClass("java/net/HttpURLConnection").newObject(url.openConnection(proxy));
} catch (IOException e) {
throw new IllegalStateException(e);
}
case "java/net/HttpURLConnection->getOutputStream()Ljava/io/OutputStream;": {
HttpURLConnection connection = (HttpURLConnection) dvmObject.getValue();
try {
return vm.resolveClass("java/io/OutputStream").newObject(connection.getOutputStream());
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
case "java/lang/String->getBytes()[B": {
String str = (String) dvmObject.getValue();
System.err.println("java/lang/String->getBytes str=" + str);
return new ByteArray(str.getBytes());
}
case "java/net/HttpURLConnection->getInputStream()Ljava/io/InputStream;": {
HttpURLConnection connection = (HttpURLConnection) dvmObject.getValue();
try {
return vm.resolveClass("java/io/InputStream").newObject(connection.getInputStream());
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
case "java/io/BufferedReader->readLine()Ljava/lang/String;": {
BufferedReader reader = (BufferedReader) dvmObject.getValue();
try {
String line = reader.readLine();
if (line != null) {
System.err.println("java/io/BufferedReader->readLine " + line);
}
return line == null ? null : new StringObject(vm, line);
} catch (IOException e) {
e.printStackTrace();
}
}
case "android/content/SharedPreferences->edit()Landroid/content/SharedPreferences$Editor;": {
return vm.resolveClass("android/content/SharedPreferences$Editor").newObject(null);
}
case "android/content/SharedPreferences$Editor->putString(Ljava/lang/String;Ljava/lang/String;)Landroid/content/SharedPreferences$Editor;": {
StringObject name = varArg.getObject(0);
StringObject value = varArg.getObject(1);
System.err.println("android/content/SharedPreferences$Editor->putString name=" + name.getValue() + ", value=" + value.getValue());
return dvmObject;
}
}
return super.callObjectMethod(vm, dvmObject, signature, varArg);
}
@Override
public DvmObject newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
System.out.println("signature: " + signature);
switch (signature) {
case "java/net/URL-><init>(Ljava/lang/String;)V":
StringObject url = varArg.getObject(0);
System.err.println("open URL: " + url.getValue());
try {
return vm.resolveClass("java/net/URL").newObject(new URL(url.getValue()));
} catch (MalformedURLException e) {
throw new IllegalStateException(e);
}
case "java/io/InputStreamReader-><init>(Ljava/io/InputStream;)V": {
InputStream inputStream = (InputStream) varArg.getObject(0).getValue();
return vm.resolveClass("java/io/InputStreamReader").newObject(new InputStreamReader(inputStream));
}
case "java/io/BufferedReader-><init>(Ljava/io/Reader;)V": {
Reader reader = (Reader) varArg.getObject(0).getValue();
return vm.resolveClass("java/io/BufferedReader").newObject(new BufferedReader(reader));
}
}
return super.newObject(vm, dvmClass, signature, varArg);
}
@Override
public DvmObject getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
System.out.println("signature: " + signature);
if ("java/net/Proxy->NO_PROXY:Ljava/net/Proxy;".equals(signature)) {
return vm.resolveClass("java/net/Proxy").newObject(Proxy.NO_PROXY);
}
return super.getStaticObjectField(vm, dvmClass, signature);
}
@Override
public void callVoidMethod(BaseVM vm, DvmObject dvmObject, String signature, VarArg varArg) {
System.out.println("signature: " + signature);
switch (signature) {
case "java/net/HttpURLConnection->setRequestMethod(Ljava/lang/String;)V": {
HttpURLConnection connection = (HttpURLConnection) dvmObject.getValue();
StringObject method = varArg.getObject(0);
System.err.println("java/net/HttpURLConnection->setRequestMethod method=" + method.getValue());
try {
connection.setRequestMethod(method.getValue());
} catch (ProtocolException e) {
throw new IllegalStateException(e);
}
return;
}
case "java/net/HttpURLConnection->setConnectTimeout(I)V": {
HttpURLConnection connection = (HttpURLConnection) dvmObject.getValue();
int timeout = varArg.getInt(0);
System.err.println("java/net/HttpURLConnection->setConnectTimeout timeout=" + timeout);
connection.setConnectTimeout(timeout);
return;
}
case "java/net/HttpURLConnection->setRequestProperty(Ljava/lang/String;Ljava/lang/String;)V": {
HttpURLConnection connection = (HttpURLConnection) dvmObject.getValue();
StringObject key = varArg.getObject(0);
StringObject value = varArg.getObject(1);
System.err.println("java/net/HttpURLConnection->setRequestProperty key=" + key.getValue() + ", value=" + value.getValue());
connection.setRequestProperty(key.getValue(), value.getValue());
return;
}
case "java/net/HttpURLConnection->setDoOutput(Z)V": {
HttpURLConnection connection = (HttpURLConnection) dvmObject.getValue();
int doOutput = varArg.getInt(0);
System.err.println("java/net/HttpURLConnection->setDoOutput: " + doOutput);
connection.setDoOutput(doOutput != 0);
return;
}
case "java/io/OutputStream->write([B)V": {
OutputStream outputStream = (OutputStream) dvmObject.getValue();
ByteArray array = varArg.getObject(0);
try {
outputStream.write(array.getValue());
} catch (IOException e) {
throw new IllegalStateException(e);
}
return;
}
case "java/io/OutputStream->close()V": {
OutputStream outputStream = (OutputStream) dvmObject.getValue();
try {
outputStream.close();
} catch (IOException e) {
throw new IllegalStateException(e);
}
return;
}
case "java/net/HttpURLConnection->connect()V": {
HttpURLConnection connection = (HttpURLConnection) dvmObject.getValue();
try {
connection.connect();
} catch (IOException e) {
throw new IllegalStateException(e);
}
return;
}
case "java/io/InputStream->close()V": {
InputStream inputStream = (InputStream) dvmObject.getValue();
try {
inputStream.close();
return;
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
case "java/io/BufferedReader->close()V": {
BufferedReader reader = (BufferedReader) dvmObject.getValue();
try {
reader.close();
return;
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
case "java/net/HttpURLConnection->disconnect()V": {
HttpURLConnection connection = (HttpURLConnection) dvmObject.getValue();
connection.disconnect();
return;
}
}
super.callVoidMethod(vm, dvmObject, signature, varArg);
}
@Override
public int callIntMethod(BaseVM vm, DvmObject dvmObject, String signature, VarArg varArg) {
System.out.println("signature: " + signature);
if ("java/net/HttpURLConnection->getResponseCode()I".equals(signature)) {
HttpURLConnection connection = (HttpURLConnection) dvmObject.getValue();
try {
return connection.getResponseCode();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
return super.callIntMethod(vm, dvmObject, signature, varArg);
}
@Override
public boolean callBooleanMethod(BaseVM vm, DvmObject dvmObject, String signature, VarArg varArg) {
System.out.println("signature: " + signature);
if ("android/content/SharedPreferences$Editor->commit()Z".equals(signature)) {
return true;
}
return super.callBooleanMethod(vm, dvmObject, signature, varArg);
}
}
libcms.zip