首页
社区
课程
招聘
mac ida7.0 调试 Android10 应用崩溃解决方案
发表于: 2024-4-22 12:57 9960

mac ida7.0 调试 Android10 应用崩溃解决方案

2024-4-22 12:57
9960

ida 调试时经常需要加载 待调试动态库 之前 attach 到进程,常用的方式是以调试模式(adb shell am set-debug-app -w [--persistent] <packagename>)启动 app,使其处于 Waiting For Debugger 状态,然后 ida attach 到 app,最后使用 jdb -attach 命令使 app 度过 Waiting For Debugger,之后就可以在 ida 中进行调试了。

但是这个模式有些缺点:

第 1 个缺点还能忍受,第 2 个缺点就很致命,因为有些软件只有在 Android 10 及之上版本才能运行。

第 2 个问题有大神给出这个 解决方案 没看懂...,所以想到根据 Waiting For Debugger 机制的系统实现方式,修改下源码,这样可以不使用 jdb -attach 也能达到同样的效果。

自主控制 app 启动是否进入阻塞状态,等待 ida attach 后解除阻塞状态。

手机:pixel 3, AOSP 12.0.0_r34
虚拟机:VMware® Workstation 17 Player(17.5.1 build-23298084)

am set-debug-app 命令入手查找 Waiting For Debugger 系统实现,最终找到 set-debug-app 执行位置

阻塞线程实现

弹窗实现

模拟系统实现增加一套调试逻辑,难点在于 ActivityThread 如何知道当前 thread 是否需要等待,以及如何通知 ActivityThread 取消等待,跨进程通讯处理比较麻烦,简单起见使用系统属性处理。

增加系统属性

这种缺点就是,重启设备后,这些属性值会被重置成默认值。
修改 init.rc 增加系统属性,此处不修改也行,可在 adb shell 中直接 setprop 添加此值。

修改 ActivityThread 处理这俩属性

Windows 11 ida7.5 以及 mac ida7.0 测试可行,但是 threads 窗口只显示一个线程,有大神 提到 ida android_server 判定是否加载 libc 是通过固定路径 /system/lib[64]/libc.so 比较的,Android 10 引入的模块化特性导致 libc 路径发生变化,所以匹配逻辑有问题,官方这个在 ida7.4sp1 版本增加了 IDA_LIBC_PATH 解决了这个问题。

ida7.4sp1 及之后版本只需要在执行 android_server64 之前,export ·IDA_LIBC_PATH· 就可以了,比如

很遗憾,mac 是我的主力开发工具,使用的是 7.0 版本,所以需要修改 android_server64 二进制文件解决。经过调试 android_sever64 验证修改二进制的方式确实可以显示其他线程,调试及修改方式如下:

调试

所以,修改二进制只需要将断点处的 CBZ 修改成 CBNZ 就可以了,打补丁过程

private void handleBindApplication(AppBindData data) {
    ...
    if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) {
    // XXX should have option to change the port.
    Debug.changeDebugPort(8100);
    if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) {
        Slog.w(TAG, "Application " + data.info.getPackageName()
                + " is waiting for the debugger on port 8100...");
 
        IActivityManager mgr = ActivityManager.getService();
        try {
            mgr.showWaitingForDebugger(mAppThread, true); // 显示弹窗
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
 
        Debug.waitForDebugger(); // 阻塞线程
 
        try {
            mgr.showWaitingForDebugger(mAppThread, false); // 隐藏弹窗
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
 
    } else {
        Slog.w(TAG, "Application " + data.info.getPackageName()
                + " can be debugged on port 8100...");
    }
    ...
}
private void handleBindApplication(AppBindData data) {
    ...
    if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) {
    // XXX should have option to change the port.
    Debug.changeDebugPort(8100);
    if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) {
        Slog.w(TAG, "Application " + data.info.getPackageName()
                + " is waiting for the debugger on port 8100...");
 
        IActivityManager mgr = ActivityManager.getService();
        try {
            mgr.showWaitingForDebugger(mAppThread, true); // 显示弹窗
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
 
        Debug.waitForDebugger(); // 阻塞线程
 
        try {
            mgr.showWaitingForDebugger(mAppThread, false); // 隐藏弹窗
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
 
    } else {
        Slog.w(TAG, "Application " + data.info.getPackageName()
                + " can be debugged on port 8100...");
    }
    ...
}
public static void waitForDebugger() {
    if (!VMDebug.isDebuggingEnabled()) {
        //System.out.println("debugging not enabled, not waiting");
        return;
    }
    if (isDebuggerConnected())
        return;
 
    // if DDMS is listening, inform them of our plight
    System.out.println("Sending WAIT chunk");
    byte[] data = new byte[] { 0 };     // 0 == "waiting for debugger"
    Chunk waitChunk = new Chunk(ChunkHandler.type("WAIT"), data, 0, 1);
    DdmServer.sendChunk(waitChunk);
 
    mWaiting = true;
    while (!isDebuggerConnected()) {
        try { Thread.sleep(SPIN_DELAY); } // SPIN_DELAY = 200
        catch (InterruptedException ie) {}
    }
    mWaiting = false;
    ....
}
public static void waitForDebugger() {
    if (!VMDebug.isDebuggingEnabled()) {
        //System.out.println("debugging not enabled, not waiting");
        return;
    }
    if (isDebuggerConnected())
        return;
 
    // if DDMS is listening, inform them of our plight
    System.out.println("Sending WAIT chunk");
    byte[] data = new byte[] { 0 };     // 0 == "waiting for debugger"
    Chunk waitChunk = new Chunk(ChunkHandler.type("WAIT"), data, 0, 1);
    DdmServer.sendChunk(waitChunk);
 
    mWaiting = true;
    while (!isDebuggerConnected()) {
        try { Thread.sleep(SPIN_DELAY); } // SPIN_DELAY = 200
        catch (InterruptedException ie) {}
    }
    mWaiting = false;
    ....
}
static final int WAIT_FOR_DEBUGGER_UI_MSG = 6;
...
case WAIT_FOR_DEBUGGER_UI_MSG: {
    synchronized (ActivityManagerService.this) {
        ProcessRecord app = (ProcessRecord)msg.obj;
        if (msg.arg1 != 0) {
            if (!app.waitedForDebugger) {
                Dialog d = new AppWaitingForDebuggerDialog(
                        ActivityManagerService.this,
                        mUiContext, app);
                app.waitDialog = d;
                app.waitedForDebugger = true;
                d.show();
            }
        } else {
            if (app.waitDialog != null) {
                app.waitDialog.dismiss();
                app.waitDialog = null;
            }
        }
    }
} break;
static final int WAIT_FOR_DEBUGGER_UI_MSG = 6;
...
case WAIT_FOR_DEBUGGER_UI_MSG: {
    synchronized (ActivityManagerService.this) {
        ProcessRecord app = (ProcessRecord)msg.obj;
        if (msg.arg1 != 0) {
            if (!app.waitedForDebugger) {
                Dialog d = new AppWaitingForDebuggerDialog(
                        ActivityManagerService.this,
                        mUiContext, app);
                app.waitDialog = d;
                app.waitedForDebugger = true;
                d.show();
            }

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

最后于 2024-4-23 19:13 被LGD丶ynlyxy编辑 ,原因:
收藏
免费 3
支持
分享
最新回复 (6)
雪    币: 3535
活跃值: (31011)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2024-4-23 14:41
1
雪    币: 6
活跃值: (1282)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
解决方案:   使用Windows
2024-4-25 14:10
1
雪    币: 16533
活跃值: (6519)
能力值: ( LV13,RANK:923 )
在线值:
发帖
回帖
粉丝
4
我给出的那个patch方案是你用ida附加后,直接将 __dl_notify_gdb_of_load 函数中两个 BL              rtld_db_dlactivity给NOP掉,然后用jdb附加
2024-4-26 17:02
0
雪    币: 16533
活跃值: (6519)
能力值: ( LV13,RANK:923 )
在线值:
发帖
回帖
粉丝
5
其实有更为简单的方式,但是有一定的缺点,可以用frida spwan的方式,然后用ida附加,然后再%resume,但是得看它有没有相应的检测方式,有的话需要过
2024-4-26 17:07
0
雪    币: 204
活跃值: (136)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
大帅锅 我给出的那个patch方案是你用ida附加后,直接将 __dl_notify_gdb_of_load 函数中两个 BL rtld_db_dlactivity给NOP掉,然后用 ...
是的,但是每次都 jdb -attach 也很不方便,不知道是不是我使用的问题,我用 jdb -attach 后加不了断点,后续调查原因,如果可以同时断点调试,还得需要调整解决方式,但是最终解决肯定是修改系统源码或者使用 magisk 模块解决,依赖外部工具会分散注意力,有的时候会遇到频繁启停调试时,外部工具会很麻烦。
2024-4-26 21:12
0
雪    币: 1845
活跃值: (1822)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
这个我照着改了一下,这个线程等待是在app启动的时候暂停的,还没有触发debug,只是延缓启动
2024-6-25 17:41
0
游客
登录 | 注册 方可回帖
返回
//