首页
社区
课程
招聘
[原创][推荐]n1ctf ezapk wp
发表于: 2024-11-16 00:45 17856

[原创][推荐]n1ctf ezapk wp

2024-11-16 00:45
17856

又是一个周五晚上,就在刚才搜集到这道题的关键“证据”,到这里这道题算是完全”破解“成功(开心)。所以就写一篇文章,总结下我的心路历程吧,包括但不限于:

总之,完全是新手向的文章,遇到的坑,一步步怎么做,我都会说清楚,即使你是新手也没关系。

然后,这也是我第一次真真切切深入安卓逆向,之前只是静态反编译解决,这次学习了frida,CE(cheat engine),安卓模拟器等工具使用,写篇文章,也算是自己的一个阶段性总结。

看完这篇文章你讲学到:

image.png

题目非常简单,输入n1ctf{flag}, 点击check检查,很正规的安卓题

放入jadx-gui查看一下主逻辑:

可以看到主要逻辑在enc中,enc 属于native 函数,通过JNI调用,enc位于通过System.loadLibrary()的两个so中

ida 打开libnative.so反编译会发现有大量的类似指针数组的调用,其实这是JNI调用

关于JNI调用可以看:[https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html)

native code 想要访问java VM的特性就需要调用JNI函数,调用JNI函数需要JNI interface pointer

并且JNI interface pointer是native函数的第一个参数,如下:

这里 double 会经过名称混淆变为Java_pkg_Cls_f_ILjava_lang_String_2

JNI interface pointer是一个pointer to pointer,具体来说就是一个指针数组,这个数组保存着JNI函数的地址,包括:

但是ida中并没有JNIEnv等等结构体,一个个倒入自动识别太麻烦,手动计算又太蠢

该怎么办呢?

其实真正常用的JNI 函数就那几个,可以看到enc中传入了字符串,所以native函数想要获取这个字符串,会调用关于String的JNI调用 一般为GetStringUTFChars

关于环境:我的pc是mac m1,手头也没有安卓设备,最后选择mu mu pro模拟器(啥都好,就是要花钱)

在mu mu pro模拟器中安装好 frida server后,运行frida server后就可以hook了

运行结果

这里就知道sub_1b148是enc了

接下来,定位enc调用了哪些函数,还是hook

运行结果

这就获取了调用顺序,在ida里看一下,一眼丁真,分别是EOR,rc4,base64

可以看到EOR和rc4的密钥都是rand()获取的,libnative2.so中的.init.array中有个init函数,初始化了随机种子

真正解密会发现解密失败,实际上这里rand被修改了,如法炮制,在libnative1.so的.init.array中有三个函数

这里可以很明显的是一个rand的替换操作,rand替换为了sub_1B140,这个函数恒定返回233,就是真正的密钥了。

如果这个修改rand got表的操作不在.init.got表中,如何找到他呢?

tips:

要看so 在哪被修改了,CE 扫描的时机很重要,要在native2加载的时候扫描一次,然后native1加载后或者再往后的一个时机扫描改变的字节

所以要hook System.loadLibrary()

这里真是大坑了,查看github issues 才知道System.loadLibrary()是不可以hook的函数之一,因为你在Java.perfrom()里使用,但它会修改classloadrer,导致报错

所以最根本的方法就是hook dlopen 或者 android_dlopen_ext

这里我选择 hook android_dlopen_ext,在 native1加载的时候暂停一会,方便CE 扫描

image.png

hook mprotect的调用,关注地址在so地址范围的地址

运行结果

计算偏移 正好是0x43f8 也就是 rand_ptr的位置

CE 查看修改后的内容

image.png

正好是 native1 中 sub_1B140

整体 难度不大 但是很有趣 这个过程中探索了各种工具的使用 各种环境的搭建 还是学到了很多,感谢你看到了这里 祝你玩的开心。

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;
 
    public native String enc(String str);
 
    public native String stringFromJNI();
 
    /* JADX INFO: Access modifiers changed from: protected */
    @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        ActivityMainBinding inflate = ActivityMainBinding.inflate(getLayoutInflater());
        this.binding = inflate;
        setContentView(inflate.getRoot());
        this.binding.CheckButton.setOnClickListener(new View.OnClickListener() { // from class: com.n1ctf2024.ezapk.MainActivity$$ExternalSyntheticLambda0
            @Override // android.view.View.OnClickListener
            public final void onClick(View view) {
                MainActivity.this.m157lambda$onCreate$0$comn1ctf2024ezapkMainActivity(view);
            }
        });
    }
 
    /* JADX INFO: Access modifiers changed from: package-private */
    /* renamed from: lambda$onCreate$0$com-n1ctf2024-ezapk-MainActivity, reason: not valid java name */
    public /* synthetic */ void m157lambda$onCreate$0$comn1ctf2024ezapkMainActivity(View view) {
        String obj = this.binding.flagText.getText().toString();
        if (obj.startsWith("n1ctf{") && obj.endsWith("}")) {
            if (enc(obj.substring(6, obj.length() - 1)).equals("iRrL63tve+H72wjr/HHiwlVu5RZU9XDcI7A=")) {
                Toast.makeText(this, "Congratulations!", 1).show();
                return;
            } else {
                Toast.makeText(this, "Try again.", 0).show();
                return;
            }
        }
        Toast.makeText(this, "Try again.", 0).show();
    }
 
    static {
        System.loadLibrary("native2");
        System.loadLibrary("native1");
    }
}
public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;
 
    public native String enc(String str);
 
    public native String stringFromJNI();
 
    /* JADX INFO: Access modifiers changed from: protected */
    @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        ActivityMainBinding inflate = ActivityMainBinding.inflate(getLayoutInflater());
        this.binding = inflate;
        setContentView(inflate.getRoot());
        this.binding.CheckButton.setOnClickListener(new View.OnClickListener() { // from class: com.n1ctf2024.ezapk.MainActivity$$ExternalSyntheticLambda0
            @Override // android.view.View.OnClickListener
            public final void onClick(View view) {
                MainActivity.this.m157lambda$onCreate$0$comn1ctf2024ezapkMainActivity(view);
            }
        });
    }
 
    /* JADX INFO: Access modifiers changed from: package-private */
    /* renamed from: lambda$onCreate$0$com-n1ctf2024-ezapk-MainActivity, reason: not valid java name */
    public /* synthetic */ void m157lambda$onCreate$0$comn1ctf2024ezapkMainActivity(View view) {
        String obj = this.binding.flagText.getText().toString();
        if (obj.startsWith("n1ctf{") && obj.endsWith("}")) {
            if (enc(obj.substring(6, obj.length() - 1)).equals("iRrL63tve+H72wjr/HHiwlVu5RZU9XDcI7A=")) {
                Toast.makeText(this, "Congratulations!", 1).show();
                return;
            } else {
                Toast.makeText(this, "Try again.", 0).show();
                return;
            }
        }
        Toast.makeText(this, "Try again.", 0).show();
    }
 
    static {
        System.loadLibrary("native2");
        System.loadLibrary("native1");
    }
}
package pkg; 
 
class Cls {
 
     native double f(int i, String s);
 
     ...
 
}
package pkg; 
 
class Cls {
 
     native double f(int i, String s);
 
     ...
 
}
jdouble Java_pkg_Cls_f__ILjava_lang_String_2 (
     JNIEnv *env,        /* interface pointer */
     jobject obj,        /* "this" pointer */
     jint i,             /* argument #1 */
     jstring s)          /* argument #2 */
{
     /* Obtain a C-copy of the Java string */
     const char *str = (*env)->GetStringUTFChars(env, s, 0);
 
     /* process the string */
     ...
 
     /* Now we are done with str */
     (*env)->ReleaseStringUTFChars(env, s, str);
 
     return ...
}
jdouble Java_pkg_Cls_f__ILjava_lang_String_2 (
     JNIEnv *env,        /* interface pointer */
     jobject obj,        /* "this" pointer */
     jint i,             /* argument #1 */
     jstring s)          /* argument #2 */
{
     /* Obtain a C-copy of the Java string */
     const char *str = (*env)->GetStringUTFChars(env, s, 0);
 
     /* process the string */
     ...
 
     /* Now we are done with str */
     (*env)->ReleaseStringUTFChars(env, s, str);
 
     return ...
}
const struct JNINativeInterface ... = {
 
    NULL,
    NULL,
    NULL,
    NULL,
    GetVersion,
 
    DefineClass,
    //... 太长省略
     
    GetJavaVM,
 
    GetStringRegion,
    GetStringUTFRegion,
    //...
    GetObjectRefType
  };
const struct JNINativeInterface ... = {
 
    NULL,
    NULL,
    NULL,
    NULL,
    GetVersion,
 
    DefineClass,
    //... 太长省略
     
    GetJavaVM,
 
    GetStringRegion,
    GetStringUTFRegion,
    //...
    GetObjectRefType
  };
_BYTE *__fastcall iusp9aVAyoMI(__int64 a1, size_t a2)
{
  size_t i; // [xsp+0h] [xbp-40h]
  _BYTE *v4; // [xsp+8h] [xbp-38h]
 
  v4 = malloc(a2);
  __memcpy_chk(v4, a1, a2, -1LL);
  for ( i = 0LL; i < a2; ++i )
    v4[i] ^= rand();
  return v4;
}
_BYTE *__fastcall SZ3pMtlDTA7Q(__int64 a1, int a2)
{
  v20[2] = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  v16 = malloc(a2);
  __memcpy_chk(v16, a1, a2, -1LL);
  v20[1] = 0LL;
  v20[0] = 0LL;
  for ( i = 0; i < 16; ++i )
    *((_BYTE *)v20 + i) = rand();
 // ....
}
_BYTE *__fastcall iusp9aVAyoMI(__int64 a1, size_t a2)
{
  size_t i; // [xsp+0h] [xbp-40h]
  _BYTE *v4; // [xsp+8h] [xbp-38h]
 
  v4 = malloc(a2);
  __memcpy_chk(v4, a1, a2, -1LL);
  for ( i = 0LL; i < a2; ++i )
    v4[i] ^= rand();
  return v4;
}
_BYTE *__fastcall SZ3pMtlDTA7Q(__int64 a1, int a2)
{
  v20[2] = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  v16 = malloc(a2);
  __memcpy_chk(v16, a1, a2, -1LL);

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2024-11-28 20:25 被SleepAlone编辑 ,原因:
上传的附件:
收藏
免费 5
支持
分享
最新回复 (14)
雪    币: 928
活跃值: (1878)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
2
mac m1可以使用genymotion模拟器也挺好用的,写的很详细
2024-11-26 10:43
0
雪    币: 3569
活跃值: (1000)
能力值: ( LV7,RANK:140 )
在线值:
发帖
回帖
粉丝
3
WMBa0 mac m1可以使用genymotion模拟器也挺好用的,写的很详细[em_003]
好的佬 我试试,感谢夸奖
2024-11-27 21:06
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
你好,请问题目在哪下载
2024-11-28 19:52
0
雪    币: 3569
活跃值: (1000)
能力值: ( LV7,RANK:140 )
在线值:
发帖
回帖
粉丝
5
mb_vntjoaqu 你好,请问题目在哪下载
我更新下附件
2024-11-28 20:21
0
雪    币: 230
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
开发者还有这么骚的操作 哈哈
2024-11-30 17:55
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
我用的雷电模拟器,使用这个脚本都用不了,是什么情况
2024-12-4 11:49
0
雪    币: 3569
活跃值: (1000)
能力值: ( LV7,RANK:140 )
在线值:
发帖
回帖
粉丝
8
吃饭去2 我用的雷电模拟器,使用这个脚本都用不了,是什么情况
frida server安装好了吗 给root权限了吗
2024-12-4 14:56
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
SleepAlone frida server安装好了吗 给root权限了吗
雷电用的fridaserver-android-x86-64,有装面具,脚本运行后出来的是libhoudini.so,并且偏移不一样
2024-12-4 21:42
0
雪    币: 3569
活跃值: (1000)
能力值: ( LV7,RANK:140 )
在线值:
发帖
回帖
粉丝
10
libhoudini.so 是专门用于 ARM-on-x86 的仿真, 题目中的两个so都是arm的,你模拟器又是x86的所以会这样,现在安卓不都是arm架构吗,你的为啥是x86 很奇怪
2024-12-5 13:24
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
SleepAlone libhoudini.so 是专门用于 ARM-on-x86 的仿真, 题目中的两个so都是arm的,你模拟器又是x86的所以会这样,现在安卓不都是arm架构吗,你的为啥是x86 很奇怪
我用的是mumu模拟器,Process.findModuleByName('libnative1.so');Module.findBaseAddress("libnative1.so"),这两个返回都是null。估计要用真机来做
2024-12-5 14:45
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
你的是苹果。。。就是arm架构所以使用模拟器没问题
2024-12-5 14:57
0
雪    币: 3569
活跃值: (1000)
能力值: ( LV7,RANK:140 )
在线值:
发帖
回帖
粉丝
13
确实 你的x86跑arm的模拟器 可能会遇到一些问题 真的话 应该就没啥问题
2024-12-5 20:25
0
雪    币: 3569
活跃值: (1000)
能力值: ( LV7,RANK:140 )
在线值:
发帖
回帖
粉丝
14
吃饭去2 你的是苹果。。。就是arm架构所以使用模拟器没问题
确实 你的x86跑arm的模拟器 可能会遇到一些问题 真的话 应该就没啥问题
2024-12-5 20:26
0
雪    币: 3569
活跃值: (1000)
能力值: ( LV7,RANK:140 )
在线值:
发帖
回帖
粉丝
15
SleepAlone 确实 你的x86跑arm的模拟器 可能会遇到一些问题 真的话 应该就没啥问题
真机的话
2024-12-5 20:27
0
游客
登录 | 注册 方可回帖
返回
//