一、前言

这是系列的第十篇,通过该样本可以充分学习如何在Unidbg中补充环境。朋友zh3nu11和我共同完成了这篇内容,感谢。

二、准备


首先我们发现了init函数,它应该就是SO的初始化函数,其余的函数看名字也都很易懂。

三、Init 模拟执行

我们首先用Unidbg跑通Init初始化函数,先搭建基本框架

package com.article10;import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;import java.io.File;public class SecurityUtil extends AbstractJni {private final AndroidEmulator emulator;private final VM vm;private final Module module;public SecurityUtil(){emulator = AndroidEmulatorBuilder.for32Bit().build(); // 创建模拟器实例,要模拟32位或者64位,在这里区分final Memory memory = emulator.getMemory(); // 模拟器的内存操作接口memory.setLibraryResolver(new AndroidResolver(23)); // 设置系统类库解析vm = emulator.createDalvikVM(new File("base.apk"));DalvikModule dm = vm.loadLibrary(new File("libscmain.so"), true);module = dm.getModule();vm.setJni(this);vm.setVerbose(true);dm.callJNI_OnLoad(emulator);}public static void main(String[] args) {SecurityUtil test = new SecurityUtil();}
}

运行产生如下报错

红框即报错原因,内存错误,箭头所指是根本原因,样本中open了这两个文件,但找不到,所以我们需要补充这两个文件。

我们需要逐一做理解

1.proc/23638/xxx 是什么

proc 文件系统由内核提供,它是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。系统中当前运行的每一个进程都有对应的一个目录在 proc 下,以进程的 PID 号为目录名,它们是读取进程信息的接口。

此处的23638就是Unidbg中APP的当前进程,每次运行,Unidbg都会随机化给一个进程PID,就像Linux系统中一样。


我修改了src/main/java/com/github/unidbg/AbstractEmulator.java 中的如图位置,使PID固定,因为PID不停变动可能会影响后续分析,但这不是必须的操作。

2.此处读取cmdline做什么

在Android系统中,进程的cmdline返回应用的进程名。那么此处的目的就很明显了,验证环境是否是”自己“的环境,防止应用被重打包。

3.在Unidbg中如何填补

proc 文件系统是伪文件系统,其目录下的所有文件,读写和正常文件没差别,所以在Unidbg中做好文件的重定向就行了。

三步走,实现IOResolver接口,注册接口,实现resolve方法并补充上我们的逻辑。

package com.article10;import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.linux.file.ByteArrayFileIO;
import com.github.unidbg.memory.Memory;import java.io.File;public class SecurityUtil extends AbstractJni implements IOResolver {private final AndroidEmulator emulator;private final VM vm;private final Module module;public SecurityUtil(){emulator = AndroidEmulatorBuilder.for32Bit().build(); // 创建模拟器实例,要模拟32位或者64位,在这里区分emulator.getSyscallHandler().addIOResolver(this);final Memory memory = emulator.getMemory(); // 模拟器的内存操作接口memory.setLibraryResolver(new AndroidResolver(23)); // 设置系统类库解析vm = emulator.createDalvikVM(new File("base.apk"));DalvikModule dm = vm.loadLibrary(new File("libscmain.so"), true);module = dm.getModule();int pid = emulator.getPid();System.out.println("APP pid:"+pid);vm.setJni(this);vm.setVerbose(true);dm.callJNI_OnLoad(emulator);}public static void main(String[] args) {SecurityUtil test = new SecurityUtil();}@Overridepublic FileResult resolve(Emulator emulator, String pathname, int oflags) {if (("proc/"+emulator.getPid()+"/cmdline").equals(pathname)) {return FileResult.success(new ByteArrayFileIO(oflags, pathname, "ctrip.android.view".getBytes()));}return null;}
}

那status呢?该文件包含该进程的众多信息:可执行文件名、当前状态、PID 和 PPID、实际及有效的 UID 和 GID、内存使用情况、以及其他。我们可以随便看一个

root@hammerhead:/proc/9884 # cat status
Name:   adb
State:  S (sleeping)
Tgid:   9884
Pid:    9884
PPid:   1
TracerPid:  0
Uid:    2000    2000    2000    2000
Gid:    2000    2000    2000    2000
FDSize: 32
Groups: 1003 1004 1007 1011 1015 1028 3001 3002 3003 3006
VmPeak:     4012 kB
VmSize:     2992 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:       880 kB
VmRSS:       880 kB
VmData:     1196 kB
VmStk:       136 kB
VmExe:       104 kB
VmLib:      1304 kB
VmPTE:         8 kB
VmSwap:        0 kB
Threads:    2
SigQ:   1/12274
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000001000
SigCgt: 000000000000a4f8
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: fffffff0000000c0
Cpus_allowed:   f
Cpus_allowed_list:  0-3
voluntary_ctxt_switches:    7
nonvoluntary_ctxt_switches: 46

一般而言,样本检测status是为了其中的TracerPid字段,TracerPid反调试的原理就是检测这个字段是否为0,为0说明没有被调试,不为0说明正在被调试,检测调试器直接退出就可以达到反调试的效果。

所以我们常常这么处理——只返回TracerPid字段

@Override
public FileResult resolve(Emulator emulator, String pathname, int oflags) {if (("proc/"+emulator.getPid()+"/cmdline").equals(pathname)) {return FileResult.success(new ByteArrayFileIO(oflags, pathname, "ctrip.android.view".getBytes()));}if (("proc/" + emulator.getPid() + "/status").equals(pathname)) {return FileResult.<AndroidFileIO>success(new ByteArrayFileIO(oflags, pathname, "TracerPid:\t0\n".getBytes()));}return null;
}

有人可能会担心不保险,万一其他字段也被用到了呢,样本找不到这些字段导向错误逻辑怎么办?这个情况是存在的,我们马上就说,先运行看看效果,emm,似乎跑通了。

我们终于可以开始执行init函数了,但我打算先看看这个getNameByPid函数。


首先Frida Call 测试一下

function callgetPid(pid){var securityUtil = null;Java.perform(function () {Java.choose("ctrip.android.security.SecurityUtil", {//枚举时调用onMatch:function(instance){//打印实例securityUtil = instance;console.log("find instance")},//枚举完成后调用onComplete:function() {console.log("end")}});var result = securityUtil.getNameByPid(pid);console.log(result);})
}

看一下PID


结果是ip.android.view。Unidbg主动调用测试一下

package com.article10;import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.file.linux.AndroidFileIO;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.linux.file.ByteArrayFileIO;
import com.github.unidbg.memory.Memory;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;import java.io.File;
import java.util.ArrayList;
import java.util.List;public class SecurityUtil extends AbstractJni implements IOResolver {private final AndroidEmulator emulator;private final VM vm;private final Module module;public SecurityUtil(){emulator = AndroidEmulatorBuilder.for32Bit().build(); // 创建模拟器实例,要模拟32位或者64位,在这里区分emulator.getSyscallHandler().addIOResolver(this);final Memory memory = emulator.getMemory(); // 模拟器的内存操作接口memory.setLibraryResolver(new AndroidResolver(23)); // 设置系统类库解析vm = emulator.createDalvikVM(new File("base.apk"));DalvikModule dm = vm.loadLibrary(new File(libscmain.so"), true);module = dm.getModule();int pid = emulator.getPid();System.out.println("APP pid:"+pid);vm.setJni(this);vm.setVerbose(true);dm.callJNI_OnLoad(emulator);}public void callgetNameByPid(){List<Object> list = new ArrayList<>(10);list.add(vm.getJNIEnv());list.add(0);list.add(emulator.getPid());Number number = module.callFunction(emulator, 0xee01, list.toArray())[0];String name = vm.getObject(number.intValue()).getValue().toString();System.out.println(name);}public static void main(String[] args) {SecurityUtil test = new SecurityUtil();test.callgetNameByPid();}@Overridepublic FileResult resolve(Emulator emulator, String pathname, int oflags) {if (("proc/"+emulator.getPid()+"/cmdline").equals(pathname)) {return FileResult.success(new ByteArrayFileIO(oflags, pathname, "ctrip.android.view".getBytes()));}if (("proc/" + emulator.getPid() + "/status").equals(pathname)) {return FileResult.<AndroidFileIO>success(new ByteArrayFileIO(oflags, pathname, "TracerPid:\t0\n".getBytes()));}return null;}
}


结果是0。实际上,这就是status 没补齐全带来的锅。这是APP真实的status

2|bullhead:/proc/19929 # cat status
Name:   ip.android.view
State:  R (running)
Tgid:   19929
Pid:    19929
PPid:   17506
TracerPid:      0
Uid:    10148   10148   10148   10148
Gid:    10148   10148   10148   10148
FDSize: 512
Groups: 3002 3003 9997 20148 50148
VmPeak:  2224800 kB
VmSize:  2180604 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:    354920 kB
VmRSS:    322600 kB
VmData:   375124 kB
VmStk:      8192 kB
VmExe:        20 kB
VmLib:    209888 kB
VmPTE:      2024 kB
VmSwap:     2952 kB
Threads:        122
SigQ:   2/6517
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000001204
SigIgn: 0000000000000000
SigCgt: 00000006400096fc
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000000000000000
CapAmb: 0000000000000000
Seccomp:        2
Cpus_allowed:   0f
Cpus_allowed_list:      0-3
Mems_allowed:   1
Mems_allowed_list:      0
voluntary_ctxt_switches:        123395
nonvoluntary_ctxt_switches:     84141

样本正是读取其中的Name,所以Unidbg补环境一定要心细和谨慎,处处可能出问题。看一下修改后的重定向方法

 @Overridepublic FileResult resolve(Emulator emulator, String pathname, int oflags) {if (("proc/"+emulator.getPid()+"/cmdline").equals(pathname)) {return FileResult.success(new ByteArrayFileIO(oflags, pathname, "ctrip.android.view".getBytes()));}if (("proc/" + emulator.getPid() + "/status").equals(pathname)) {return FileResult.success(new ByteArrayFileIO(oflags, pathname, ("Name:   ip.android.view\n" +"State:  R (running)\n" +"Tgid:   "+emulator.getPid()+"\n" +"Pid:    "+emulator.getPid()+"\n" +"PPid:   17506\n" +"TracerPid:      0\n" +"Uid:    10148   10148   10148   10148\n" +"Gid:    10148   10148   10148   10148\n" +"FDSize: 512\n" +"Groups: 3002 3003 9997 20148 50148\n" +"VmPeak:  2224800 kB\n" +"VmSize:  2185240 kB\n" +"VmLck:         0 kB\n" +"VmPin:         0 kB\n" +"VmHWM:    354920 kB\n" +"VmRSS:    324572 kB\n" +"VmData:   379340 kB\n" +"VmStk:      8192 kB\n" +"VmExe:        20 kB\n" +"VmLib:    209888 kB\n" +"VmPTE:      2020 kB\n" +"VmSwap:     3012 kB\n" +"Threads:        127\n" +"SigQ:   2/6517\n" +"SigPnd: 0000000000000000\n" +"ShdPnd: 0000000000000000\n" +"SigBlk: 0000000000001204\n" +"SigIgn: 0000000000000000\n" +"SigCgt: 00000006400096fc\n" +"CapInh: 0000000000000000\n" +"CapPrm: 0000000000000000\n" +"CapEff: 0000000000000000\n" +"CapBnd: 0000000000000000\n" +"CapAmb: 0000000000000000\n" +"Seccomp:        2\n" +"Cpus_allowed:   0f\n" +"Cpus_allowed_list:      0-3\n" +"Mems_allowed:   1\n" +"Mems_allowed_list:      0\n" +"voluntary_ctxt_switches:        21102\n" +"nonvoluntary_ctxt_switches:     20849").getBytes()));}return null;}

接下来步入正题——执行init方法!增改内容如下,并运行。

public void callInit(){List<Object> list = new ArrayList<>(10);list.add(vm.getJNIEnv());list.add(0);DvmObject<?> context = vm.resolveClass("android/content/Context").newObject(null);// contextlist.add(vm.addLocalObject(context));module.callFunction(emulator, 0x36a85, list.toArray());
};public static void main(String[] args) {SecurityUtil test = new SecurityUtil();test.callInit();
}


补JAVA环境是Unidbg中的基础活,但它是有技巧的,善用JNITrace可以补的又快又好,比如本篇的Unidbg环境,就是在1h内补好的。如果补环境有问题或者不会补,欢迎评论区或者课上交流讨论。

见招拆招补充几个后

    @Overridepublic DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {switch (signature) {case "android/os/Environment->getExternalStorageDirectory()Ljava/io/File;":{return vm.resolveClass("java/io/File").newObject(signature);}}return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);}@Overridepublic DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {switch (signature) {case "java/io/File->getPath()Ljava/lang/String;":{System.out.println("PATH:"+dvmObject.getValue());if(dvmObject.getValue().equals("android/os/Environment->getExternalStorageDirectory()Ljava/io/File;")){return new StringObject(vm, "/mnt/sdcard");}if(dvmObject.getValue()=="android/content/Context->getFilesDir()Ljava/io/File;"){return new StringObject(vm, "/data/data/ctrip.android.view/files");}}case "android/content/Context->getPackageResourcePath()Ljava/lang/String;":return new StringObject(vm, "/data/app/ctrip.android.view-fM4xyjk_eygpJsiITNW4JA==/base.apk");case "android/content/Context->getFilesDir()Ljava/io/File;":return vm.resolveClass("java/io/File").newObject(signature);case "android/content/Context->getAssets()Landroid/content/res/AssetManager;":return new AssetManager(vm, signature);}return super.callObjectMethod(vm, dvmObject, signature, varArg);}

报错了,这次看不出啥明显错误
日志全开再看看,好像更迷糊了

但其实这个问题我们早就讲过了,Android中通过libandroid.so对Assets资源文件进行操作,日志中可以看到”getAssets“等字眼,但是由于libandroid.so的依赖SO太多了,Unidbg很难一一处理,所以资源文件相关的处理会报错。为此Unidbg做了一个折中的解决防范——可以自己注册虚拟模块,或者叫虚拟SO,libandroid.so已经由作者实现了,实现了常用的几个Assets操作的API。只用加上这一行就行,需要注意必须在样本SO之前加载或者叫注册进去


继续运行,为什么还报错


是这样的,样本在通过虚拟SO读取资源文件,资源的读取有四种模式

/** Available access modes for opening assets with {@link AAssetManager_open} */
enum {/** No specific information about how data will be accessed. **/AASSET_MODE_UNKNOWN      = 0,/** Read chunks, and seek forward and backward. */AASSET_MODE_RANDOM       = 1,/** Read sequentially, with an occasional forward seek. */AASSET_MODE_STREAMING    = 2,/** Caller plans to ask for a read-only buffer with all data. */AASSET_MODE_BUFFER       = 3
};

但作者只实现了模式1和2,而此处使用mode 0。mode 0 没有啥特殊处理要做,我们直接合并入mode 2和3的逻辑即可。

继续运行,即已经过了这个坑。有的人可能会困惑,Native层读写资源文件做什么?其实这是常见做法,比如把key编码进资源文件的一张图片里,Native层去读取这个资源文件获取key。

下面就是继续补JAVA环境,使用JNItrace辅助,飞速补完。


补完后代码整体长这样

package com.article10;import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.file.linux.AndroidFileIO;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.api.AssetManager;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.linux.file.ByteArrayFileIO;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.virtualmodule.android.AndroidModule;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;import java.io.File;
import java.util.ArrayList;
import java.util.List;public class SecurityUtil extends AbstractJni implements IOResolver {private final AndroidEmulator emulator;private final VM vm;private final Module module;public SecurityUtil(){emulator = AndroidEmulatorBuilder.for32Bit().build(); // 创建模拟器实例,要模拟32位或者64位,在这里区分emulator.getSyscallHandler().addIOResolver(this);final Memory memory = emulator.getMemory(); // 模拟器的内存操作接口memory.setLibraryResolver(new AndroidResolver(23)); // 设置系统类库解析vm = emulator.createDalvikVM(new File("base.apk"));new AndroidModule(emulator, vm).register(memory);DalvikModule dm = vm.loadLibrary(new File("libscmain.so"), true);module = dm.getModule();int pid = emulator.getPid();System.out.println("APP pid:"+pid);vm.setJni(this);vm.setVerbose(true);dm.callJNI_OnLoad(emulator);}public void callgetNameByPid(){List<Object> list = new ArrayList<>(10);list.add(vm.getJNIEnv());list.add(0);list.add(emulator.getPid());Number number = module.callFunction(emulator, 0xee01, list.toArray())[0];String name = vm.getObject(number.intValue()).getValue().toString();System.out.println(name);}public void callInit(){List<Object> list = new ArrayList<>(10);list.add(vm.getJNIEnv());list.add(0);DvmObject<?> context = vm.resolveClass("android/content/Context").newObject(null);// contextlist.add(vm.addLocalObject(context));module.callFunction(emulator, 0x36a85, list.toArray());};public static void main(String[] args) {Logger.getLogger("com.github.unidbg.linux.ARM32SyscallHandler").setLevel(Level.DEBUG);Logger.getLogger("com.github.unidbg.unix.UnixSyscallHandler").setLevel(Level.DEBUG);Logger.getLogger("com.github.unidbg.AbstractEmulator").setLevel(Level.DEBUG);Logger.getLogger("com.github.unidbg.linux.android.dvm.DalvikVM").setLevel(Level.DEBUG);Logger.getLogger("com.github.unidbg.linux.android.dvm.BaseVM").setLevel(Level.DEBUG);Logger.getLogger("com.github.unidbg.linux.android.dvm").setLevel(Level.DEBUG);SecurityUtil test = new SecurityUtil();test.callInit();}@Overridepublic DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {switch (signature) {case "android/os/Environment->getExternalStorageDirectory()Ljava/io/File;":{return vm.resolveClass("java/io/File").newObject(signature);}case "okio/zz->b(I)Ljava/lang/String;":{int key = varArg.getInt(0);switch (key){case 1:{return new StringObject(vm, "353626076466627");}case 0:{return new StringObject(vm, "8cff8823cf19b5ec");}case 101:{return new StringObject(vm, "25483");}case 103:{return new StringObject(vm, "1920*1080");}case 104:{return new StringObject(vm, "");}case 102:{return new StringObject(vm, "17637");}case 105:{return new StringObject(vm, "WIFI");}case 106:{return new StringObject(vm, "0.0.0.0:0");}case 8:{return new StringObject(vm, "0.0.0.0:0");}case 9:{return new StringObject(vm, "");}case 10:{return new StringObject(vm, "00:00:00:00:00:00");}case 107:{return new StringObject(vm, "[full-100]");}case 108:{return new StringObject(vm, "78");}}System.out.println("okio/zz->b(I) Key:"+key);}case "java/net/NetworkInterface->getByName(Ljava/lang/String;)Ljava/net/NetworkInterface;":{String name = null;DvmObject<?> namedvm = varArg.getObject(0);if(namedvm!=null){name = (String) namedvm.getValue();}return vm.resolveClass("java/net/NetworkInterface").newObject(name);}}return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);}@Overridepublic DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {switch (signature) {case "java/io/File->getPath()Ljava/lang/String;":{System.out.println("PATH:"+dvmObject.getValue());if(dvmObject.getValue().equals("android/os/Environment->getExternalStorageDirectory()Ljava/io/File;")){return new StringObject(vm, "/mnt/sdcard");}if(dvmObject.getValue()=="android/content/Context->getFilesDir()Ljava/io/File;"){return new StringObject(vm, "/data/data/ctrip.android.view/files");}}case "android/content/Context->getPackageResourcePath()Ljava/lang/String;":return new StringObject(vm, "/data/app/ctrip.android.view-fM4xyjk_eygpJsiITNW4JA==/base.apk");case "android/content/Context->getFilesDir()Ljava/io/File;":return vm.resolveClass("java/io/File").newObject(signature);case "android/content/Context->getAssets()Landroid/content/res/AssetManager;":return new AssetManager(vm, signature);case "java/net/NetworkInterface->getHardwareAddress()[B":byte[] result = hexStringToByteArray("64BC0C65AA1E");return new ByteArray(vm, result);}return super.callObjectMethod(vm, dvmObject, signature, varArg);}public static byte[] hexStringToByteArray(String s) {int len = s.length();byte[] data = new byte[len / 2];for (int i = 0; i < len; i += 2) {data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)+ Character.digit(s.charAt(i+1), 16));}return data;}@Overridepublic FileResult resolve(Emulator emulator, String pathname, int oflags) {if (("proc/"+emulator.getPid()+"/cmdline").equals(pathname)) {return FileResult.success(new ByteArrayFileIO(oflags, pathname, "ctrip.android.view".getBytes()));}if (("proc/" + emulator.getPid() + "/status").equals(pathname)) {return FileResult.success(new ByteArrayFileIO(oflags, pathname, ("Name:   ip.android.view\n" +"State:  R (running)\n" +"Tgid:   "+emulator.getPid()+"\n" +"Pid:    "+emulator.getPid()+"\n" +"PPid:   17506\n" +"TracerPid:      0\n" +"Uid:    10148   10148   10148   10148\n" +"Gid:    10148   10148   10148   10148\n" +"FDSize: 512\n" +"Groups: 3002 3003 9997 20148 50148\n" +"VmPeak:  2224800 kB\n" +"VmSize:  2185240 kB\n" +"VmLck:         0 kB\n" +"VmPin:         0 kB\n" +"VmHWM:    354920 kB\n" +"VmRSS:    324572 kB\n" +"VmData:   379340 kB\n" +"VmStk:      8192 kB\n" +"VmExe:        20 kB\n" +"VmLib:    209888 kB\n" +"VmPTE:      2020 kB\n" +"VmSwap:     3012 kB\n" +"Threads:        127\n" +"SigQ:   2/6517\n" +"SigPnd: 0000000000000000\n" +"ShdPnd: 0000000000000000\n" +"SigBlk: 0000000000001204\n" +"SigIgn: 0000000000000000\n" +"SigCgt: 00000006400096fc\n" +"CapInh: 0000000000000000\n" +"CapPrm: 0000000000000000\n" +"CapEff: 0000000000000000\n" +"CapBnd: 0000000000000000\n" +"CapAmb: 0000000000000000\n" +"Seccomp:        2\n" +"Cpus_allowed:   0f\n" +"Cpus_allowed_list:      0-3\n" +"Mems_allowed:   1\n" +"Mems_allowed_list:      0\n" +"voluntary_ctxt_switches:        21102\n" +"nonvoluntary_ctxt_switches:     20849").getBytes()));}return null;}
}

接下来遇到报错

样本在读取apk文件,继续在resolve方法中做好重定向的工作

     if (("/data/app/ctrip.android.view-fM4xyjk_eygpJsiITNW4JA==/base.apk").equals(pathname)) {return FileResult.success(new SimpleFileIO(oflags, new File("yourAPkpath\\xxx.apk"), pathname));}

继续运行,可能再补充一些JAVA环境,整体没有报错了,似乎init补充完了。

日志结尾看到样本对这些文件进行访问,虽然没有报错,但我们可能会好奇,它在做什么呢?事实上,如果存在这些文件,则说明手机已经Root,样本在试图访问这些文件,来判断系统是否被Root了。我们这里不做任何处理。

四、Unidbg 模拟执行SimpleSign

先Frida call一下,参数二固定,参数一可自行修改。

function callSimpleSign(){var securityUtil = null;Java.perform(function () {Java.choose("ctrip.android.security.SecurityUtil", {//枚举时调用onMatch:function(instance){//打印实例securityUtil = instance;console.log("find instance")},//枚举完成后调用onComplete:function() {console.log("end")}});var input1 = stringToBytes("7be9f13e7f5426d139cb4e5dbb1fdba7")var result = securityUtil.simpleSign(input1, "getdata");console.log(result);})
}

结果恒为75位长度,且相同输入也会有不同输出,原因未知。

接下来Unidbg中测试

    public void callSimpleSign(){List<Object> list = new ArrayList<>(10);list.add(vm.getJNIEnv());list.add(0);String input = "7be9f13e7f5426d139cb4e5dbb1fdba7";byte[] inputByte = input.getBytes(StandardCharsets.UTF_8);ByteArray inputByteArray = new ByteArray(vm,inputByte);list.add(vm.addLocalObject(inputByteArray));list.add(vm.addLocalObject(new StringObject(vm, "getdata")));Number number = module.callFunction(emulator, 0x80735, list.toArray())[0];System.out.println(vm.getObject(number.intValue()).getValue().toString());};public static void main(String[] args) {Logger.getLogger("com.github.unidbg.linux.ARM32SyscallHandler").setLevel(Level.DEBUG);Logger.getLogger("com.github.unidbg.unix.UnixSyscallHandler").setLevel(Level.DEBUG);Logger.getLogger("com.github.unidbg.AbstractEmulator").setLevel(Level.DEBUG);Logger.getLogger("com.github.unidbg.linux.android.dvm.DalvikVM").setLevel(Level.DEBUG);Logger.getLogger("com.github.unidbg.linux.android.dvm.BaseVM").setLevel(Level.DEBUG);Logger.getLogger("com.github.unidbg.linux.android.dvm").setLevel(Level.DEBUG);SecurityUtil test = new SecurityUtil();test.callInit();System.out.println("call SimpleSign");test.callSimpleSign();}

结果也很顺利,如果报错,则根据缺失的JAVA环境继续补即可。


考虑一个小问题,即使明文一致,结果也一直在变,这是为什么呢?有哪些原因可能导致这个结果呢?

五、尾声

相关程序和资料放群里,防止遇到恶意举报。

SO逆向入门实战教程十:SimpleSign相关推荐

  1. SO逆向入门实战教程九——blackbox

    文章目录 一.前言 二.准备 三.Unidbg模拟执行 四.Unidbg算法还原 五.尾声 一.前言 上篇中,我们借AB之口,讨论了这样一个问题--Unidbg是否适合做算法分析的主力工具,这个问题没 ...

  2. SO逆向入门实战教程八:文件读写

    文章目录 一.前言 二.demo1设计 三.Unidbg模拟执行demo1 四.demo2设计 五.Unidbg模拟执行demo2 六.尾声 一.前言 本篇分析的是自写demo,帮助大家熟悉Unidb ...

  3. SO逆向入门实战教程一:OASIS

    文章目录 一.前言 二.准备 三.Unidbg模拟执行 四.ExAndroidNativeEmu 模拟执行 五.算法分析 六.尾声 一.前言 这是SO逆向入门实战教程的第一篇,总共会有十三篇,十三个实 ...

  4. Spring Boot 入门实战教程

    Spring Boot 2.0 入门实战教程 开发环境:JDK1.8或以上 源码下载:https://pan.baidu.com/s/1Z771VDiuabDBJJV445xLeA 欢迎访问我的个人博 ...

  5. MVC5+EF6 入门完整教程十

    原文:MVC5+EF6 入门完整教程十 本篇是第一阶段的完结篇. 学完这篇后,你应该可以利用MVC进行完整项目的开发了. 本篇主要讲述多表关联数据的更新,以及如何使用原生SQL. 文章提纲 多表关联数 ...

  6. Python之Numpy入门实战教程(2):进阶篇之线性代数

    Numpy.Pandas.Matplotlib是Python的三个重要科学计算库,今天整理了Numpy的入门实战教程.NumPy是使用Python进行科学计算的基础库. NumPy以强大的N维数组对象 ...

  7. Python之Numpy入门实战教程(1):基础篇

    Numpy.Pandas.Matplotlib是Python的三个重要科学计算库,今天整理了Numpy的入门实战教程.NumPy是使用Python进行科学计算的基础库. NumPy以强大的N维数组对象 ...

  8. 视频教程-深度学习与PyTorch入门实战教程-深度学习

    深度学习与PyTorch入门实战教程 新加坡国立大学研究员 龙良曲 ¥399.00 立即订阅 扫码下载「CSDN程序员学院APP」,1000+技术好课免费看 APP订阅课程,领取优惠,最少立减5元 ↓ ...

  9. 【kratos入门实战教程】1-kratos项目搭建和开发环境配置

    1.系列目录 [kratos入门实战教程]0-商城项目介绍 [kratos入门实战教程]1-kratos项目搭建和开发环境配置 [kratos入门实战教程]2-实现注册登陆业务 2.概览 经过上一篇的 ...

最新文章

  1. 微信js sdk动态引用
  2. 【青少年编程】【三级】青蛙捕虫
  3. Selwyn College, Cambridge
  4. Jenkins任务失败,发送邮件通知
  5. 监视Rails进程内存泄漏的技巧
  6. 业界对物联网技术最常见的三大误区解读
  7. mysql中distinct关键字,MySQL关键字Distinct的详细介绍
  8. vs2005下使用ASPNetPage分页的例子1
  9. AIDE手机编程初级教程(零基础向)导航
  10. 未来IT人才市场最热门的12项技能
  11. 我们的地盘,我们做主
  12. 基于lstm+crf实现电子病历实体信息识别 完整的代码+数据集+说明 毕设
  13. 毕索大学计算机科学怎么样,毕索大学计算机硕士介绍
  14. 如何制定人生目标体系
  15. LM75AD温度传感器的应用(1)
  16. vs2017 fatal error LNK1104: 无法打开文件“\.obj”
  17. 滑雪教程-新手必看(上)
  18. 大学期间技术学习方面最有成就感的事?
  19. GS63风扇狂转CPU利用率100%PC电源方案设置
  20. jar包 加密 ----xjar (亲测可用)

热门文章

  1. 魔改写轮眼(python turtle库)
  2. 跑步健身什么品牌蓝牙耳机值得入手?300左右高性价比蓝牙耳机推荐
  3. html鼠标经过事件便箭头,为啥我的代码,鼠标滑过显示是箭头 ,而其他同学划过时都是手指?????????...
  4. java filter response_ZuulFilter中设置Response的原理。
  5. 宝塔Linux面板介绍及安装命令!
  6. 堆和栈的概念和区别 python_堆和栈的概念和区别(转载)
  7. 如何理解面向切面编程中的切面?
  8. 一步步教你打造属于自己的FLV播放器,动态调用外部影片!~
  9. android 今日头条加载动画,Android 仿今日头条简单的刷新效果实例代码
  10. pmml模型文件解析与自定义