SO逆向入门实战教程十:SimpleSign
一、前言
这是系列的第十篇,通过该样本可以充分学习如何在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相关推荐
- SO逆向入门实战教程九——blackbox
文章目录 一.前言 二.准备 三.Unidbg模拟执行 四.Unidbg算法还原 五.尾声 一.前言 上篇中,我们借AB之口,讨论了这样一个问题--Unidbg是否适合做算法分析的主力工具,这个问题没 ...
- SO逆向入门实战教程八:文件读写
文章目录 一.前言 二.demo1设计 三.Unidbg模拟执行demo1 四.demo2设计 五.Unidbg模拟执行demo2 六.尾声 一.前言 本篇分析的是自写demo,帮助大家熟悉Unidb ...
- SO逆向入门实战教程一:OASIS
文章目录 一.前言 二.准备 三.Unidbg模拟执行 四.ExAndroidNativeEmu 模拟执行 五.算法分析 六.尾声 一.前言 这是SO逆向入门实战教程的第一篇,总共会有十三篇,十三个实 ...
- Spring Boot 入门实战教程
Spring Boot 2.0 入门实战教程 开发环境:JDK1.8或以上 源码下载:https://pan.baidu.com/s/1Z771VDiuabDBJJV445xLeA 欢迎访问我的个人博 ...
- MVC5+EF6 入门完整教程十
原文:MVC5+EF6 入门完整教程十 本篇是第一阶段的完结篇. 学完这篇后,你应该可以利用MVC进行完整项目的开发了. 本篇主要讲述多表关联数据的更新,以及如何使用原生SQL. 文章提纲 多表关联数 ...
- Python之Numpy入门实战教程(2):进阶篇之线性代数
Numpy.Pandas.Matplotlib是Python的三个重要科学计算库,今天整理了Numpy的入门实战教程.NumPy是使用Python进行科学计算的基础库. NumPy以强大的N维数组对象 ...
- Python之Numpy入门实战教程(1):基础篇
Numpy.Pandas.Matplotlib是Python的三个重要科学计算库,今天整理了Numpy的入门实战教程.NumPy是使用Python进行科学计算的基础库. NumPy以强大的N维数组对象 ...
- 视频教程-深度学习与PyTorch入门实战教程-深度学习
深度学习与PyTorch入门实战教程 新加坡国立大学研究员 龙良曲 ¥399.00 立即订阅 扫码下载「CSDN程序员学院APP」,1000+技术好课免费看 APP订阅课程,领取优惠,最少立减5元 ↓ ...
- 【kratos入门实战教程】1-kratos项目搭建和开发环境配置
1.系列目录 [kratos入门实战教程]0-商城项目介绍 [kratos入门实战教程]1-kratos项目搭建和开发环境配置 [kratos入门实战教程]2-实现注册登陆业务 2.概览 经过上一篇的 ...
最新文章
- 微信js sdk动态引用
- 【青少年编程】【三级】青蛙捕虫
- Selwyn College, Cambridge
- Jenkins任务失败,发送邮件通知
- 监视Rails进程内存泄漏的技巧
- 业界对物联网技术最常见的三大误区解读
- mysql中distinct关键字,MySQL关键字Distinct的详细介绍
- vs2005下使用ASPNetPage分页的例子1
- AIDE手机编程初级教程(零基础向)导航
- 未来IT人才市场最热门的12项技能
- 我们的地盘,我们做主
- 基于lstm+crf实现电子病历实体信息识别 完整的代码+数据集+说明 毕设
- 毕索大学计算机科学怎么样,毕索大学计算机硕士介绍
- 如何制定人生目标体系
- LM75AD温度传感器的应用(1)
- vs2017 fatal error LNK1104: 无法打开文件“\.obj”
- 滑雪教程-新手必看(上)
- 大学期间技术学习方面最有成就感的事?
- GS63风扇狂转CPU利用率100%PC电源方案设置
- jar包 加密 ----xjar (亲测可用)
热门文章
- 魔改写轮眼(python turtle库)
- 跑步健身什么品牌蓝牙耳机值得入手?300左右高性价比蓝牙耳机推荐
- html鼠标经过事件便箭头,为啥我的代码,鼠标滑过显示是箭头 ,而其他同学划过时都是手指?????????...
- java filter response_ZuulFilter中设置Response的原理。
- 宝塔Linux面板介绍及安装命令!
- 堆和栈的概念和区别 python_堆和栈的概念和区别(转载)
- 如何理解面向切面编程中的切面?
- 一步步教你打造属于自己的FLV播放器,动态调用外部影片!~
- android 今日头条加载动画,Android 仿今日头条简单的刷新效果实例代码
- pmml模型文件解析与自定义