加密在so层的app对于我这样的菜鸡简直是一座大山啊,只好学习一下了,这里推荐一下龙哥的unidbg教程,写的太好了(虽然有的地方我还是不懂)
龙giegie的csdn: blog.csdn.net/qq_38851536…
本篇文章仅供交流学习,侵权删(满满的求生欲)
本次app样本:5Lic5pa55aS05p2hdjIuOC45
一 . 抓包
使用postern转发一下,就能抓到包了
很好,那么我们去jadx里面找一下加密位置吧。
这个app没有壳,直接搜索一下url的后半部分(如果搜加密参数的Name,虽然只有4个地方,但是可能要找一会了)
直接搜索newsgzip
只有一个地方,点进去吧,然后查找一下堆栈,堆栈也只有一条,后面就是一直跟了,因为本篇文章主要介绍unidbg,就不说那么多了。
最终的加密位置
那么加密就是在libvinda.so里面,这个加密参数的值什么@018p啥的一看就不是啥正经加密!一定是做了别的处理,C代码又看不懂,上神器!
二 . unidbg
直接把代码clone下来,跑通如下例子,就说明没问题了
把apk和so文件放到相应的位置,直接用unidbg通用模板跑一下
// 导入通用且标准的类库
import com.github.unidbg.linux.android.dvm.AbstractJni;
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.*;
import com.github.unidbg.memory.Memory;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class dongfang extends AbstractJni {
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
public dongfang() {
// 创建模拟器实例,进程名建议依照实际进程名填写,可以规避针对进程名的校验
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.sina.International").build();
// 获取模拟器的内存操作接口
final Memory memory = emulator.getMemory();
// 设置系统类库解析
memory.setLibraryResolver(new AndroidResolver(23));
// 创建Android虚拟机,传入APK,Unidbg可以替我们做部分签名校验的工作
vm = emulator.createDalvikVM(new File(getPath()+"\src\test\java\com\目标.apk"));
// 加载目标SO
DalvikModule dm = vm.loadLibrary(new File(getPath()+"\src\test\java\com\libvinda.so"), true); // 加载so到虚拟内存
//获取本SO模块的句柄,后续需要用它
module = dm.getModule();
vm.setJni(this); // 设置JNI
vm.setVerbose(true); // 打印日志
dm.callJNI_OnLoad(emulator); // 调用JNI OnLoad
};
public String getPath() {
String path = this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath();
if(System.getProperty("os.name").contains("dows"))
{
path = path.substring(1,path.length());
}
if(path.contains("jar"))
{
path = path.substring(0,path.lastIndexOf("."));
return path.substring(0,path.lastIndexOf("/"));
}
return path.replace("/target/test-classes/", "");
}
public static void main(String[] args) {
com.xxx.dongfang test = new com.xxx.dongfang();
}
}
结果报错了,这个错简单来说就是unidbg没有帮我们定义getApplication()这个方法(我自己是这么想的,大概就是这么个意思,别听我瞎说),我们要自己来解决。
好,这就补完unidbg没有帮我们定义的环境了,再次运行
我们发现已经不报错了,并且so里面的函数和其地址也打印出来了,好耶!
我们的目标函数是ep(前面的图里),地址是0x1a6d,那么我们现在只要知道入参是什么,就可以使用unidbg模拟执行出加密参数了。
使用frida hook ep函数 java层的入参,是str,这就不用我多说了。然后直接模拟执行。
public String calculateS(){
List<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv()); // 第一个参数是env
list.add(0); // 第二个参数,实例方法是jobject,静态方法是jclazz,直接填0,一般用不到。
list.add(vm.addLocalObject(new StringObject(vm, "123")));
Number number = module.callFunction(emulator, 0x1A6C + 1, list.toArray());
String result = vm.getObject(number.intValue()).getValue().toString();
return result;
};
我这里直接用123代替了,因为str真的很长!细心的大佬会发现这里地址+1了,这个就涉及到Thumb 指令集和 Arm 指令集了,具体什么是指令集别问我,问谷歌!这里可以使用IDA进行判断。
1.IDA - Options - General - number of opcode bytes - 设置为 4
2.此时查看 IDA VIew 中 opcode 的长度, 如果出现 2 个字节和 4 个字节的, 说明为 thumb 指令集,如果都是 4 个字节的, 说明是 arm 指令集;
在 Thumb 指令集下, inline hook 的偏移地址需要进行 +1 操作;
回到主题
public static void main(String[] args) {
com.xxx.dongfang test = new com.xxx.dongfang();
System.out.println(test.calculateS());
}
打印一下就发现出结果了!
放一下完整代码和注释,注释要好好看,和下面打jar包有关,上面有的注释我就删了,只保留和打jar包有关的注释。
public dongfang() {
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.sina.International").build();
final Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
# getPath()方法是获取当前目录,打完jar包后就有用了
vm = emulator.createDalvikVM(new File(getPath()+"\src\test\java\com\目标.apk"));
DalvikModule dm = vm.loadLibrary(new File(getPath()+"\src\test\java\com\libvinda.so"), true);
module = dm.getModule();
vm.setJni(this);
vm.setVerbose(true);
dm.callJNI_OnLoad(emulator);
};
public String calculateS(String key,String type){
List<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv());
list.add(0);
# 这里key是每小时会传过来一个需要经过处理去发请求的key,type区分栏目,我这里就是为了演示一下具体怎么python调用jar包传值,字符串不是这样的,简化了一下
list.add(vm.addLocalObject(new StringObject(vm, "123"+"\""+type+"\""+"456"+"\""+key+"\""+"789")));
Number number = module.callFunction(emulator, 0x1A6C + 1, list.toArray());
String result = vm.getObject(number.intValue()).getValue().toString();
return result;
};
@Override
public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
switch (signature) {
case "android/app/ActivityThread->getApplication()Landroid/app/Application;":
return vm.resolveClass("android/app/Application").newObject(null);
}
return super.callObjectMethodV(vm, dvmObject, signature, vaList);
};
public String getPath() {
String path = this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath();
if(System.getProperty("os.name").contains("dows"))
{
path = path.substring(1,path.length());
}
if(path.contains("jar"))
{
path = path.substring(0,path.lastIndexOf("."));
return path.substring(0,path.lastIndexOf("/"));
}
return path.replace("/target/test-classes/", "");
}
public static void main(String[] args) {
# 在这里定义需要从python传过来的值
String type = System.getProperty("type");
String key = System.getProperty("key");
com.*** test = new com.***();
System.out.println(test.calculateS(key,type));
}
三 . 打包jar,python调用
这里要求比较严格的读者就不满意了,你只是生成了加密参数,我怎么用啊。
这里一般有两种方法,一个是jar包调用,一个是使用unidbg-server 起一个服务,我这里介绍打jar包
File → Project Structure , 然后选择 Artifacts, 点加号 Add,jar → From modules with dependencies
这里需要注意下
Main Class这里 填写的是从com.开始 ,到你这个类的类名
倒数第二个填的则是unidbg-master的根目录。
OK之后
之后就会在你unidbg-master的根目录下的 \out\artifacts\unidbg_master_jar 生成一堆jar包,如果像我一样不改输出的jar包名,默认我们写的代码生成的jar包就是unidbg-master.jar,其他jar包应该都是unidbg的依赖。
然后在这个目录下依次创建目录 src\test\java\com(这里要和代码路径对照看) 将apk和so放进去
好,重头戏,python调用,这里安装一下jpype这个包
import jpype
jar_path = os.path.join(os.path.abspath(''), 'D:\work_soft\IDEA\IDEA2021.3.3\workspace\unidbg-master\out\artifacts\unidbg_master_jar\unidbg-master.jar')#第二个参数是jar包的路径
# 启动jvm
jpype.startJVM(jpype.getDefaultJVMPath(), "-ea", "-Djava.class.path=%s" % (jar_path))
# 通过包名,实例化JAVA对象 括号内的是包名 后面的是类名+直接实例化
EncryClass = jpype.JPackage("com.xxx").dongfang()
jiami = EncryClass.calculateS(final_key,type)
print("jiami:",jiami)
然后和请求之类的代码放到一起(请求是啥,我不知道)
完成!!(码了好多字,好累)