首页
社区
课程
招聘
[原创]使用Unidbg在CTF-Android题目的快速解题
2023-10-19 21:41 6439

[原创]使用Unidbg在CTF-Android题目的快速解题

2023-10-19 21:41
6439

遇到了一道android题目,最近学了Unidbg。那么就开始掏出Unidbg,皮又痒痒了,想跳跳啦,看看能不能一把嗦

做完这题,发现成了Unidbg的铁杆粉丝,真的是很方便,我再也不用辛辛苦苦动态分析so啦
题目会打包上传,完整Unidbg脚本也会放在后面,本文的重点在于使用Unidbg的分析so能力,关于Unidbg的安装可以自行搜索。

创建一个项目框架
图片描述

Java层分析

使用aapt获得对应的启动Activity

1
aapt dump badging apk名字

开始分析代码

可以看到,重点是分析so的j和p方法

so分析

有两个so文件,先看app使用的libj.so

分析 libj.so

搜索JNI_onload,说明是静态注册函数

j方法

先分析 j 方法,发现没有参数,尝试使用Unidbg跑一遍(完整脚本在后面,这里不占用文章内容了)

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
int __fastcall Java_an_droid_j_MainActivity_j(JNIEnv *a1)
{
  int i; // r1
  int v2; // r0
  int v3; // r0
  char v5[32]; // [sp-40h] [bp-70h] BYREF
  _BYTE v6[36]; // [sp-20h] [bp-50h] BYREF
  JNIEnv *v7; // [sp+4h] [bp-2Ch]
  int *v8; // [sp+8h] [bp-28h]
  int v9; // [sp+Ch] [bp-24h]
  int v10; // [sp+10h] [bp-20h]
  _BYTE *v11; // [sp+14h] [bp-1Ch]
  int v12; // [sp+1Ch] [bp-14h] BYREF
 
  v8 = &v12;
  v7 = a1;
  for ( i = -1178200092; ; i = 52119689 )
  {
    do
    {
      v3 = i;
      i = 1445388760;
    }
    while ( v3 == -1178200092 );
    if ( v3 == 52119689 )
      break;
    v11 = v6;
    strcpy(v5, "FlagLostHelpMeGetItBack");
    v10 = 30;
    v9 = 97;
    v5[29] = 0;
    *(_WORD *)&v5[27] = 0;
    v5[24] = 0;
    *(_WORD *)&v5[25] = 0;
    v5[30] = 80;
    qmemcpy(v6, v5, 0x1Eu);
    v2 = (int)(*v7)->NewStringUTF(v7, v6);
    *v8 = v2;
  }
  return *v8;
}
1
2
3
4
5
6
public String func_j(){
    DvmClass dvmClass=vm.resolveClass("an.droid.j.MainActivity");
    DvmObject<?> object = dvmClass.newObject(null);
    DvmObject<?> object1 = object.callJniMethodObject(emulator, "j()Ljava/lang/String;");
    return object1.getValue().toString();
}

就说这个函数其实并没有什么用,总是返回固定值!

p方法

这里可以使用葫芦娃大佬的插件,Obpo(https://github.com/obpo-project/obpo-plugin)​

使用插件来进行恢复即可(详细使用开看Obpo文档)

耐心等待

发现只是经过一个运算,然后输出对应的值,为了方便以后运算,这里也写出对应的Unidbg方法

1
2
3
4
5
6
public int func_p(int args){
    DvmClass dvmClass=vm.resolveClass("an.droid.j.MainActivity");
    DvmObject<?> object=dvmClass.newObject(null);
    int object1=object.callJniMethodInt(emulator,"p(I)I",args);
    return object1;
}

init方法

该方法在java层没有被调用,但不代表没有用处

1
jstring __fastcall Java_an_droid_j_MainActivity_init(JNIEnv *a1, jobject a2, int a3)

可以看到,它接收一个参数a3,返回值为string类型

其实就是将输入的int类型,分别取高四位与低四位作为两个数据,然后计算一个含“libinit”​的字符串并返回

但是加密比较复杂,我们猜测将正确的zygote值传入,看师傅能得到flag

先尝试传递 zygote=9999,看看结果是怎样的?

可以看到输出了含有libinit的字符串

猜测输入正确的zygote值,也将得到flag

获得正确的zygote值

正确的zygote值要执行99999次才能得到,并且调用了libj.so的p方法,我们也可以使用Unidbg来模拟获得这个值

这里写出p方法对应的Unidbg

1
2
3
4
5
6
public int func_p(int args){
    DvmClass dvmClass=vm.resolveClass("an.droid.j.MainActivity");
    DvmObject<?> object=dvmClass.newObject(null);
    int object1=object.callJniMethodInt(emulator,"p(I)I",args);
    return object1;
}

然后使用Unidbg写出执行脚本

1
2
3
4
5
6
int zygote = 1357024680;
long start =System.currentTimeMillis();
for(int i=0;i<99999;i++){
    zygote =mylesson4.func_p(zygote);
}
System.out.println("99999次后的zygote的值:"+zygote);

但是跑了好长时间,人麻了


将zygote值填入init函数

1
2
3
lesson4 mylesson4=new lesson4(apkFilePath,soFilePath,apkProcessname);
int temp = 1738911344;
System.out.println("flag{" + mylesson4.func_init(temp) + "}");

这就是Unidbg的强大之处,直接模拟,调用so方法一把嗦

完整脚本

开始编写Unidbg脚本(模板复制一下,稍作修改就可以)

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package com.lesson4;
 
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.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.linux.android.dvm.array.ByteArray;
import com.github.unidbg.linux.android.dvm.jni.ProxyDvmObject;
import com.github.unidbg.memory.Memory;
import com.lession1.oasis;
 
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
 
 
public class lesson4 extends AbstractJni{
    private final AndroidEmulator emulator; //android模拟器
    private final VM vm;//vm虚拟机
    private final Module module;
    private  final Memory memory;
    private  final DalvikModule dm;
    //将该类封装起来,以后直接套用模板
    public lesson4(String apkFilePath,String soFilePath,String apkProcessname) throws IOException {
        // 创建模拟器实例,进程名建议依照实际进程名填写,可以规避针对进程名的校验
        emulator = AndroidEmulatorBuilder.for32Bit().setProcessName(apkProcessname).build();
 
        //.addBackendFactory(new DynarmicFactory((true)))  下面会创建一个快速模拟器实例,加载速度快,但是某些特性不支持
        //.setProcessName()设置进程名,避免原进程对进程名进行检验
 
 
        // 获取模拟器的内存操作接口
        memory = emulator.getMemory();
        // 设置系统类库解析  支持1923,因为在main/resources/android只集成了两个版本
        memory.setLibraryResolver(new AndroidResolver(23));
 
 
        // 创建Android虚拟机,传入APK,可以过掉签名校验,路径比如:"unidbg-android\\src\\test\\java\\com\\lesson1\\123.apk"
        vm = emulator.createDalvikVM(new File(apkFilePath));
        vm.setVerbose(false); // 打印日志,会在调用初始化JNI_onload打印一些信息,默认:false
 
        // 加载目标SO
        dm = vm.loadLibrary(new File(soFilePath), true); // 加载so到虚拟内存,第二个参数:是否需要初始化
        //获取本SO模块的句柄
        module = dm.getModule();
 
        vm.setJni(this); //设置Jni,防止报错
        //创建完后,需要调用JNI_onload函数
        //dm.callJNI_OnLoad(emulator); // 调用JNI OnLoad,进行动态注册某些函数。如果都是静态注册,那就不用调用这个函数
 
        //本次样本连个 JNI_onLoad都没有
 
    }
 
    //这个是模拟 bak_libj.so的j方法
    public String func_j(String method,double args){
        DvmClass dvmClass=vm.resolveClass("an.droid.j.MainActivity");
        DvmObject<?> object=dvmClass.newObject(null);
        //获得一个DvmObject对象
        //DvmObject object= ProxyDvmObject.createObject(vm,"an.droid.j"); //因为我创建的类全包名和原app不一样,所以换一种方式来寻找到对应的类对象
        DvmObject object1=object.callJniMethodObject(emulator,method,args);
        String return_value=object1.getValue().toString();
        return return_value;
 
    }
 
 
    //下面两个是模拟 libj.so的init、p和j方法
    //均使用了动态获得dvmclass的方式
    public int func_p(int args){
        DvmClass dvmClass=vm.resolveClass("an.droid.j.MainActivity");
        DvmObject<?> object=dvmClass.newObject(null);
        int object1=object.callJniMethodInt(emulator,"p(I)I",args);
        return object1;
    }
 
    public String func_init(int args){
        DvmClass dvmClass=vm.resolveClass("an.droid.j.MainActivity");
        DvmObject<?> object=dvmClass.newObject(null);
        //方法签名在对应的so文件中推导出,其实不难的,看参数和看返回值
        DvmObject<?> object1 = object.callJniMethodObject(emulator, "init(I)Ljava/lang/String;", args);
        return object1.getValue().toString();
    }
    public String func_j(){
        DvmClass dvmClass=vm.resolveClass("an.droid.j.MainActivity");
        DvmObject<?> object = dvmClass.newObject(null);
        DvmObject<?> object1 = object.callJniMethodObject(emulator, "j()Ljava/lang/String;");
        return object1.getValue().toString();
    }
 
 
    //创建一个main函数
    public static void main(String[] args) throws IOException {
        // 1、需要调用的so文件所在路径
        String soFilePath = "unidbg-android/src/test/java/com/lesson4/libj.so";
        // 2、APK的路径
        String apkFilePath="unidbg-android/src/test/java/com/lesson4/a.apk";
        // 3、apk进程名
        String apkProcessname="an.droid.j";
 
        lesson4 mylesson4=new lesson4(apkFilePath,soFilePath,apkProcessname);
        int temp = 1738911344;
        System.out.println("flag{" + mylesson4.func_init(temp) + "}");
 
//        int zygote = 1357024680;
//        long start =System.currentTimeMillis();
//        for(int i=0;i<99999;i++){
//            zygote =mylesson4.func_p(zygote);
//        }
//        System.out.println("99999次后的zygote的值:"+zygote);
 
 
 
    }
 
}


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

上传的附件:
收藏
点赞5
打赏
分享
最新回复 (1)
雪    币: 19410
活跃值: (29069)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2023-10-20 16:17
2
1
感谢分享
游客
登录 | 注册 方可回帖
返回