IDA调试Android的so文件(SWPU 2019-easyapp)

0.静态分析找到题目关键点

用jeb打开apk,先看程序入口点:
请添加图片描述

根据包名找到验证方法!
请添加图片描述

public void onClick(View view) {
   if(password.getText().toString().equals(MainActivity.this.Encrypt())) {
        Toast.makeText(MainActivity.this, "登录成功", 1).show();
        return;
    }

    Toast.makeText(MainActivity.this, "登录失败", 0).show();
}

分析发现主要是将flag和Encrypt()函数的返回值进行对比!
继续看发现Encrypt()函数是native层的方法~

    static {
        System.loadLibrary("native-lib");
    }

    public native String Encrypt() {
    }

导出so文件,拖入ida开始分析Encrypt()函数!

int __cdecl Java_com_example_ndktest2_MainActivity_Encrypt(_JNIEnv *a1)
{
  char *v1; // eax
  char *v2; // eax
  char *v4; // [esp+24h] [ebp-B8h]
  char v5[13]; // [esp+2Dh] [ebp-AFh] BYREF
  char dest[6]; // [esp+3Ah] [ebp-A2h] BYREF
  char v7[128]; // [esp+40h] [ebp-9Ch] BYREF
  char v8[28]; // [esp+C0h] [ebp-1Ch] BYREF

  strcpy(dest, "flag{");
  strcpy(&v5[7], "wllm");
  v5[12] = 0;
  strcpy(v8, "welcome");
  strcpy(v5, "newbee");
  base64_encode(v5, v7);
  v4 = strcat(dest, &v5[7]);
  v1 = strcat(v8, v5);
  v2 = strcat(v4, v1);
  return _JNIEnv::NewStringUTF(a1, v2);
}

觉得很简单,flag中一部分通过base64加密后和flag{wllmwelcome拼接起来,最后试着提交密码,发现是错误的!非常的不理解QAQ

准备动调调试一下看看是不是base64或者哪里被修改了!在这个方法下个断点

Java_com_example_ndktest2_MainActivity_Encrypt(_JNIEnv *a1)

然后用ida+jeb+adb+雷神模拟器-》进行动态调试,实现过程在文章末尾…

1.根据静态分析开始动态调试

发现下了个断点后根本Encrypt()函数未被断下来非常不理解QAQ
总结一下过去写native层的apk的时候都涉及到一个函数_JNIEnv::NewStringUTF(a1, v2)将传入的字符串进行格式统一,所以这个函数是一定会被用到的,那我直接在_JNIEnv::NewStringUTF(a1, v2)这个函数下个断点来试试!

.text:A4FD1090 _ZN7_JNIEnv12NewStringUTFEPKc proc near ; CODE XREF: _JNIEnv::NewStringUTF(char const*)↑j
.text:A4FD1090                                         ; DATA XREF: .got.plt:off_A4FFEDF0↓o
.text:A4FD1090
.text:A4FD1090 this            = dword ptr  8
.text:A4FD1090 arg_4           = dword ptr  0Ch
.text:A4FD1090
.text:A4FD1090 ; __unwind { // A4FC7000
.text:A4FD1090                 push    ebp
.text:A4FD1091                 mov     ebp, esp
.text:A4FD1093                 push    ebx
.text:A4FD1094                 push    edi

断点下在:A4FD1093,因为push ebp;mov ebp, esp;要保存栈帧
发现真的断了下来,说明没有反调试而是Encrypt()被混淆了或者被hook了
根据此时的函数调用栈找到了正真的函数test()
请添加图片描述
调用路径:test()->call art_quick_generic_jni_trampoline->call NewStringUTF
请添加图片描述

找到目标函数test才是正真的Encrypt()函数!!
去test函数下个断点,看看程序的返回值!
请添加图片描述
找到flag 了就是他"YouaretheB3ST"

2.解决Encrypt()函数是如何被混淆成test()的

但是依旧不理解这个调用的是Encrypt()却调用的test()
疯狂找wp:

  1. 第十届SWPUCTFwriteup-安全客 - 安全资讯平台 (anquanke.com)
    讲解到了与JNI_Onload函数有关但不理解QAQ
.data:A4FFF008 off_A4FFF008    dd offset aEncrypt      ; DATA XREF: sub_A4FD1570+21↑o
.data:A4FFF008                                         ; "Encrypt"
.data:A4FFF00C                 dd offset aLjavaLangStrin ; "()Ljava/lang/String;"
.data:A4FFF010                 dd offset test

继续查资料:

  1. JNI 学习笔记——通过RegisterNatives注册原生方法 - 简书 (jianshu.com)

知道了如何注册native方法的原理:

  1. java层执行:System.loadLibrary(“NativeLib”); //NativeLib 为native模块名称
  2. 触发函数JNI_Onload来注册方法
static JNINativeMethod methods[] = {
        {"getNativeString", "()Ljava/lang/String;", reinterpret_cast<void*>(getString)}
};

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
    JNIEnv* env;
    if (JNI_OK != vm->GetEnv(reinterpret_cast<void**> (&env),JNI_VERSION_1_4)) {
        LOGW("JNI_OnLoad could not get JNI env");
        return JNI_ERR;
    }

    g_jvm = vm; //用于后面获取JNIEnv
    jclass clazz = env->FindClass("com/example/myndkproj/NativeLib");  //获取Java NativeLib类

    //注册Native方法
    if (env->RegisterNatives(clazz, methods, sizeof(methods)/sizeof((methods)[0])) < 0) {
        LOGW("RegisterNatives error");
        return JNI_ERR;
    }

    return JNI_VERSION_1_4;
}

知道了注册原理和ida的伪代码很像!但是依旧不理解QAQ为啥!!

继续问chatgpt!

问:
1.我在调试apk时,apk注册了一个native方法Encrypt(),这个函数是写在.so文件里的,所以我在so文件下个断点但发现并未断下,反而so文件中的test()函数被调用了,apk调用的明明是Encrypt()方法为啥调用了test()函数
2.我可以确定函数名无错,Android有什么方法可以修改调用的函数
3._JNIEnv::RegisterNatives(a1, Class, a3, a4)
4.JNINativeMethod*数组如何写的

终于问到了关键点!!!>>>>>
JNINativeMethod结构体数组用于在JNI中注册本地方法。
每个JNINativeMethod结构体包含两个字段:

`char* name`      本地方法
`char* signature` 签名
`void* fnPtr`     实际的本地函数

发现不就是ida里面看到的!

.data:A4FFF008 off_A4FFF008    dd offset aEncrypt      ; DATA XREF: sub_A4FD1570+21↑o
.data:A4FFF008                                         ; "Encrypt"
.data:A4FFF00C                 dd offset aLjavaLangStrin ; "()Ljava/lang/String;"
.data:A4FFF010                 dd offset test

发现off_A4FFF008其实就是JNINativeMethod!!!

定义JNINativeMethod数组, 声明需要注册的方法

static JNINativeMethod methods[] = {
        {"getNativeString", "()Ljava/lang/String;", reinterpret_cast<void*>(getString)}
};

其实那篇原理的文章就讲了但是我没理解QAQ
其中getNativeString为Java类中定义的Native方法名。
()Ljava/lang/String; 为方法的签名, ()表示该方法无参数, Ljava/lang/String;表示返回值为Java中的String类型。具体签名规则请参考《JNI学习笔记》 中的内容。
reinterpret_cast<void*>(getString) 为Native实现的方法名。这里强制转换成了函数指针。
作者:JellyJoe_943
链接:https://www.jianshu.com/p/216a41352fd8
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

IDA调试Android的so文件

借鉴;[原创]新手关于ida动态调试so的一些坑总结-Android安全-看雪-安全社区|安全招聘|kanxue.com

启动模拟器后看看手机设备有几个:

C:\Users\Administrator>adb devices
List of devices attached
emulator-5554   device

高版本的雷电9.0模拟器有些坑,会自动启动两个安卓端口,所以连接的时候需要加IP

C:\Users\Administrator>adb -s 127.0.0.1:5555 push E:\ReverseTools\IDA8.3\dbgsrv\android_server64 /data/local/tmp/
E:\ReverseTools\IDA8.3\dbgsrv\android_server64: 1 file pushed, 0 skipped. 100.3 MB/s (1282848 bytes in 0.012s)

下载雷电模拟器5.0版,安卓后启动!
开始启动shell看看手机型号:
知识来源:adb查看手机设备型号、品牌、机型等信息_abd 获取手机型号-CSDN博客

C:\Users\Administrator>adb shell
root@aosp:/ #  getprop ro.product.cpu.abi
x86

很显然是x86的那么接下来就是使用ida动态调试了,先把server push进模拟器~
请添加图片描述

C:\Users\Administrator>
adb push E:\ReverseTools\IDA8.3\dbgsrv\android_x86_server /data/local/tmp

E:\ReverseTools\IDA8.3\dbgsrv\android_x86_server: 1 file pushed, 0 skipped. 281.0 MB/s (1183352 bytes in 0.004s)

#给server文件权限
C:\Users\Administrator>adb shell
root@aosp:/ # cd /data/local/tmp/
root@aosp:/ # chmod 777 android_x86_server
#运行,成功
root@aosp:/data/local/tmp # ./android_x86_server
IDA Android x86 32-bit remote debug server(ST) v8.3.28. Hex-Rays (c) 2004-2023
2024-05-07 18:53:35 Listening on 0.0.0.0:23946...

在将手机的端口转发到Windows就可以了!

C:\Users\Administrator>adb forward tcp:23946 tcp:23946
23946

成功完成模拟器和ida的通信搭建!

接下来就算用jeb开始调试了!
先看看manifest有没有调试权限!
请添加图片描述

并且找到包名:<activity android:name="com.example.ndktest2.MainActivity">
有那么就可以开启程序的调试模式了用adb:
在cmd启动命令:

C:\Users\Administrator>adb shell am start -D -n com.example.ndktest2/.MainActivity
Starting: Intent { cmp=com.example.ndktest2/.MainActivity }

成功
请添加图片描述

打开jeb导出x86文件!
请添加图片描述

再将so文件导入ida!

在启动jeb的调试模式!
请添加图片描述

双击进程成功附加!!再去看看模拟器发现进入界面了!

下面就打开ida下个断点了,在Encrypt函数下个断点!
再修改一下:
请添加图片描述

如果不是默认调试端口就修改
请添加图片描述

由于Windows无法启动Android程序所以只能采取attach的模式!
请添加图片描述

选择目标
请添加图片描述

附加后会弹出:
请添加图片描述

下断点就可以调试了!
成功获得flag!
请添加图片描述

  • 8
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值