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) {
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) {
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()) {
return
;
}
if
(isDebuggerConnected())
return
;
System.out.println(
"Sending WAIT chunk"
);
byte
[] data =
new
byte
[] {
0
};
Chunk waitChunk =
new
Chunk(ChunkHandler.type(
"WAIT"
), data,
0
,
1
);
DdmServer.sendChunk(waitChunk);
mWaiting =
true
;
while
(!isDebuggerConnected()) {
try
{ Thread.sleep(SPIN_DELAY); }
catch
(InterruptedException ie) {}
}
mWaiting =
false
;
....
}
public
static
void
waitForDebugger() {
if
(!VMDebug.isDebuggingEnabled()) {
return
;
}
if
(isDebuggerConnected())
return
;
System.out.println(
"Sending WAIT chunk"
);
byte
[] data =
new
byte
[] {
0
};
Chunk waitChunk =
new
Chunk(ChunkHandler.type(
"WAIT"
), data,
0
,
1
);
DdmServer.sendChunk(waitChunk);
mWaiting =
true
;
while
(!isDebuggerConnected()) {
try
{ Thread.sleep(SPIN_DELAY); }
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编辑
,原因: