一、引言
fork from [Unidbg 的基本使用(八)](https://www.yuque.com/lilac-2hqvv/xdwlsg/td2t5cvohdedb878?# 《Unidbg 的基本使用(八)》)。
二、处理一
对于 JDK 里包含的类库,可以直接复现其方法逻辑。
比如对 SimpleDateFormat 的操作
1 2 3 4 5 6
| case "java/text/SimpleDateFormat-><init>(Ljava/lang/String;Ljava/util/Locale;)V":{ String pattern = vaList.getObjectArg(0).getValue().toString(); Locale locale = (Locale) vaList.getObjectArg(1).getValue(); simpleDateFormat = new SimpleDateFormat(pattern, locale); return; }
|
比如对 StringBuilder 的操作
1 2 3 4
| case "java/lang/StringBuilder->append(Ljava/lang/String;)Ljava/lang/StringBuilder;":{ String str = vaList.getObjectArg(0).getValue().toString(); return ProxyDvmObject.createObject(vm, ((StringBuilder) dvmObject.getValue()).append(str)); }
|
比如 okhttp3,可以 maven 引入然后正常使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| case "okhttp3/Headers->name(I)Ljava/lang/String;":{ Headers headers = (Headers) dvmObject.getValue(); return new StringObject(vm, headers.name(vaList.getIntArg(0))); } case "okhttp3/Headers->value(I)Ljava/lang/String;":{ Headers headers = (Headers) dvmObject.getValue(); return new StringObject(vm, headers.value(vaList.getIntArg(0))); } case "okio/Buffer->clone()Lokio/Buffer;":{ Buffer buffer = (Buffer) dvmObject.getValue(); return vm.resolveClass("okio/Buffer").newObject(buffer.clone()); } case "okhttp3/Request->newBuilder()Lokhttp3/Request$Builder;": { Request request = (Request) dvmObject.getValue(); return vm.resolveClass("okhttp3/Request$Builder").newObject(request.newBuilder()); } case "okhttp3/Request$Builder->header(Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Request$Builder;": { Request.Builder builder = (Request.Builder) dvmObject.getValue(); builder.header(vaList.getObjectArg(0).getValue().toString(), vaList.getObjectArg(1).getValue().toString()); return dvmObject; } case "okhttp3/Request$Builder->build()Lokhttp3/Request;": { Request.Builder builder = (Request.Builder) dvmObject.getValue(); request = builder.build(); return vm.resolveClass("okhttp3/Request").newObject(request); }
|
三、处理二
样本自定义的类库,可以在JADX
、GDA
、JEB
等工具中反编译,然后把代码逻辑拷贝到 Unidbg 环境里,前文我们就这么处理过 ContainerUtils、SignedQuery,尤其是各种工具类和方法容器。
有些 Android FrameWork 层的类库也同样可以拷贝过来
比如 TextUtils 里的方法
1 2 3 4
| case "android/text/TextUtils->isEmpty(Ljava/lang/CharSequence;)Z":{ String str = vaList.getObjectArg(0).getValue().toString(); return str == null || str.length() == 0; }
|
比如 Android 的 Base64 工具类,可以直接抠出来。
1 2 3 4 5
| case "android/util/Base64->decode(Ljava/lang/String;I)[B":{ String arg = vaList.getObjectArg(0).getValue().toString(); int flag = vaList.getIntArg(1); return new ByteArray(vm, Base64.decode(arg, flag)); }
|
四、处理三
有些 Android FrameWork 类库,使用频率很高,JDK 里又没有其对应,我们就需要复写其语义,实现其基本功能。
比如 SharedPreferences,我们最好写一个小的 demo,实现 Android 中对 SP 的创建、打开、读取、修改、保存这些基本功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import java.io.*;
public class SharedPreferences { private JSONObject jsonObject; private final File mFile;
SharedPreferences(File file) { mFile = file; jsonObject = null; loadFromDisk(); } private void loadFromDisk() { if (mFile.exists() && !mFile.canRead()) { System.out.println("Attempt to read preferences file " + mFile + " without permission"); } BufferedInputStream str; try { str = new BufferedInputStream(new FileInputStream(mFile), 16 * 1024); readSP(str); } catch (Exception e) { System.out.println("getSharedPreferences:" + e); } }
public String getString(String key, String defValue) { String v = (String)jsonObject.get(key); return v != null ? v : defValue; }
public void putString(String key, String value){ jsonObject.put(key, value); }
private void readSP(InputStream in){ char[] buf = new char[16 * 1024]; InputStreamReader input; try { input = new InputStreamReader(in); int len =input.read(buf); String text =new String(buf,0, len); jsonObject = JSON.parseObject(text); } catch (IOException e) { e.printStackTrace(); } } }
|
以及根据样本所操作的属性和方法,构建对应的类去描述它
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class AccessibilityServiceInfo { public String[] packageNames; public String name; public CharSequence label; public String packageName;
public AccessibilityServiceInfo(String packageName, String name, CharSequence label, String[] packageNames){ this.packageName = packageName; this.name = name; this.label = label; this.packageNames = packageNames; } }
|
五、处理四
如果其他方法处理不了或者很麻烦,我们会将方法调用”降级“为简单的数据返回或占位或返回 null,这么做有风险,但总比没法继续执行好。
比如
1 2 3 4 5 6 7 8 9 10 11 12
| case "android/bluetooth/BluetoothAdapter->getDefaultAdapter()Landroid/bluetooth/BluetoothAdapter;":{ return dvmClass.newObject(signature); } case "android/telephony/SubscriptionManager->from(Landroid/content/Context;)Landroid/telephony/SubscriptionManager;":{ return dvmClass.newObject(signature); } case "android/net/Uri->parse(Ljava/lang/String;)Landroid/net/Uri;":{ String key = vaList.getObjectArg(0).getValue().toString(); if(key.equals("content://telephony/siminfo")){ return dvmClass.newObject(key); } }
|
比如
1 2 3 4 5
|
case "android/content/pm/PackageManager->getInstalledPackages(I)Ljava/util/List;":{ return null; }
|
六、尾声
总体上,补 JNI 环境的代码逻辑方面,就这么几种处理办法。
这里提供大量的案例可供参考,当不知道如何补 JNI 环境时可以去检索和参考。
百度网盘链接:https://pan.baidu.com/s/1G0U8frKK3dotY2cryztFjQ 提取码:6666