151-5197-5087
扬州华为授权服务中心
当前位置:网站首页 > 网络设备调试 正文 网络设备调试

[免费专栏] Android安全之动态调试APP的一些技巧「Android Studio调试」

2024-12-05 22:03:21 网络设备调试 28 ℃ 0 评论

也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大

少走了弯路,也就错过了风景,无论如何,感谢经历


转移发布平台通知:将不再在CSDN博客发布新文章,敬请移步知识星球

感谢大家一直以来对我CSDN博客的关注和支持,但是我决定不再在这里发布新文章了。为了给大家提供更好的服务和更深入的交流,我开设了一个知识星球,内部将会提供更深入、更实用的技术文章,这些文章将更有价值,并且能够帮助你更好地解决实际问题。期待你加入我的知识星球,让我们一起成长和进步


Android安全付费专栏长期更新,本篇最新内容请前往:

  • [车联网安全自学篇] Android安全之动态调试APP的一些技巧「Android Studio调试」

0x01 前言

通过之前的文章了解,同学们应该都知道了APK调试的前提条件是app可调试的状态,但一般开发人员在发版的时候,会发release版。因为在一般的手机上,release版本的应用是不可以被调试的,相对来说起到了保护app的作用

PS:在最初的时候,开发Android是没有gradle的,那时候发release版不像现在在gradle配置好就行了,是直接操作AndroidManifest.xml文件中 <application> 标签的属性 android:debuggable="true"

Android系统会通过APP的AndroidManifest文件中设置android:debuggable 属性,去验证App可不可以调试。假如想关掉这个系统验证,一种是重新刷入boot.img 修改方法,另一种是通过hook修改

一般大多数情况下,通常都是通过android:debuggable="true"来进行调试。APK可调试必须具备的条件(两个满足之一),如下:

  • AndroidManifest.xml中的application标签必须包含属性android:debuggable="true"
  • ./default.prop 中ro.debuggable的值必须为1(系统默认调试,在bulid.prop(boot.img)ro.debugable=1

[车联网安全自学篇] Android安全之Android中allowBackup属性浅析「含案例实验」:

  • https://orangey.blog.csdn.net/article/details/122151416

[车联网安全自学篇] Android安全之不反编译APK情况下添加android:debuggable属性

  • https://orangey.blog.csdn.net/article/details/123488541

假设:android:debuggable=“false” 怎么办?

首先,一般情况下发行版的APP都会将android:debuggable="" 设置为 false,使第三方不能直接调试分析APP,这也是厂商出于安全的考虑。

此时同学们,可能会想到,需要反编译Apk并将android:debuggable="" 设置为true。修改完成后进行回编译APK且签名。

重打包APK可能会遇到繁杂的代码、诡异的反抓包,so层的加密……等奇奇怪怪的反重打包技术,而如果对APP进行重打包,那就要面对APP额外的保护措施,例如重打包失败,签名验证等。

假设:./default.prop中ro.debuggable 的值为0,怎么办?

直接修改这个值是成功不了的?这个在之前的文章里面有提到过,因为ro开头的属性是不允许后期修改的,这个值只在系统启动时,也就是开机时才会读取和加载一次。那重启?但每次重启,这个值就会恢复默认,所以就造成了一个死循环。

修改上面说的系统属性值的三种方式:

  • 直接修改default.prop文件中的值,然后重启设备
    • 修改完成需要重启设备的,需要让init进程重新解析属性文件,把属性信息加载内存中方可起作用的(可能会出现设备死机)
  • 改写系统文件,重新编译系统镜像文件,然后刷入到设备中
    • 修改boot.img中的这个属性(default.prop文件),修改系统文件,然后重新编译镜像文件,最后再刷到设备中
  • 注入init进程,修改内存中的属性值
    • 使用ptrace注入到init进程,修改内存中的这些属性值,只要init进程不重启的话,那么这些属性值就会起效(如果init进程挂了重启的话,那么设置就没有任何效果了)

ES文件浏览器查看内存中的debuggable


或getprop命令查看内存中的debuggable

getprop ro.debuggable

  • BDopener 来开启调试的

1.1 Android Studio 断点调试和高级调试

  • 点击debug模式运行

  • 查看调试面板

1.1.1 断点基础调试

  • step into:一步步往下走

点击单步调试按钮或按快捷键F8

切换到logcat查看日志,我打印出的i的值

  • step into:看到方法往里走

比如我们的for循环当中调用了一个stepNext(int i)方法,当我们走到这里想看看这个方法里面的运行过程的时候我们可以这样,当走到这个方法的时候我们可以按下F7或如下图的图标

  • force step into :所有方法看完整

可以让我们看到程序现在所调用的所有方法的实现会让你跟着它走一遍,研究源码使用非常方便

  • step out :有断点下一个,走完断点继续走

例如,如果我们的一个流程当中,包括调用的方法,如果有断点走到下一个断点,如果没有断点,而是在一个调用的方法当中,会跳出这个方法,继续走

  • run to Cursor :下个断点再次相见

会很快执行到下一个断点的位置,而且可以静如任何调用的方法

1.1.2 断点高级调试

  • 跨断点调试

如果设置了多个断点,但现在需要直接跳转到下一个断点,那么直接点击下图图标即可

  • 观察变量

如果想观察1个或者几个变量的值的变化;如果在Variables显示面版中观察;如果程序这里有太多太多的自定义变量和系统变量了,那么就难观察了。

此时,可以点击Watches、点击+号,然后输入变量的名称回车就OK了,而且还会有历史记录

或者选择[Variables]中的变量名然后点击[右键],选择[Add to Watches],然后Watches面板中就有了

  • 设置变量的值

在程序中有很多的条件语句和循环语句,调试也是比较耗时的,我们可以通过快速设置变量的值来加快调试速度

此时,可以选择[Variables]中的变量名然后点击[右键],选择[Set Value…]或者选择之后直接F2(下图为Variables面板)

  • 查看断点

点击之后,可以看到所有的断点,以及位置代码,也可以设置一些属性

  • 停止调试

[停止调试]不是让程序停止,而是跳过所有调试

0x02 工具

  • ES文件浏览器

下载地址:https://pan.baidu.com/s/1DqQtBGJxbHIfp4_HoCaiDQ
提取码:0f60

  • BDOpener 开启APK调试与备份选项的Xposed模块

https://security.tencent.com/index.php/opensource/detail/17

  • Xinstall 如何不重打包调试Android应用

https://www.open-open.com/lib/view/open1426304176732.html

  • BuildProp 增强器(BuildProp Enhancer Make all application attribute android:debuggable=“true”)

https://repo.xposed.info/module/com.jecelyin.buildprop

  • XDebug make all app on your phone debuggable

https://github.com/deskid/XDebug

  • smalidea插件

配置到Android studio的,配置了才能方便看smali文件

安装ideasmali插件,选择File->Settings->Plugins,安装之前下载的ideasmali插件

https://bitbucket.org/JesusFreke/smali/downloads/

  • Detect-It-Easy

Detect It Easy简称Die,是一款专业查壳工具,比PEID强大得多,能查一次查到底。并支持超大文件读取,其他查壳工具无法打开的程序,这个都能读取。虽然没有PEID出名,但是相当的强大,完全可以替代PEID

https://github.com/horsicq/Detect-It-Easy

  • ApkMessenger(ApkMessenger)

https://github.com/sulab999/ApkMessenger

https://www.ghxi.com/apkinfo.html

0x03 无需修改原apk文件

  • 启Fiddler抓包工具后,打开App

  • 点击两条博客

  • 查看Fiddler,根据数据包大小和内容找到需要的那一条

  • 对比A博客和B博客内容的数据包


deviceid、appver、appid、chno的参数值是固定不变的
is_default 是非必填项
login_uid  登录后获取的值
article_id 每篇文章的id
sign 它是64位十六进制数,猜测是两个md5拼接,可以理解为它可能是加密的内容
  • 使用Jadx反编译Apk,搜索url链接的末尾部分,即get_article_info.php

在APP里面,开发人员为了复用性,很多时候APP会将BASE_URL,也就是域名+域名后面的虚拟目录/请求参数拆分开来

将APK后缀改为zip,里面有两个classes.dex 文件


dex2jar 反编译为Java代码

d2j-dex2jar.bat classes.dex
d2j-dex2jar.bat classes2.dex

jadx 依次打开classes-dex2jar.jar、classes2-dex2jar.jar

get_article_info.php


从上面的搜索当中,可以看出只有m字符串是符合要求的,双击代码进去看一下,在这个config(配置)包里,以类变量的方式存放着大量的字符串,如果想引用它,就是b.m

右键查看用例,看一下这个url在哪儿被使用了,发现只有一处

双击第二行的代码,查看详细引用

它包装了一个a方法来取我们的目标URL,再次查找用例

我们这里点开com.sina.sinablog.network.d.a ,可以发现其实它就是a方法上面的那个方法

由上述的消息,我们知道APP发送的网络请求,首先要进行字段的获取和拼接,但在Java中往往由集合map完成,格式类似于{”id“:3,“name”:“lilac”},PUT存入、GET取出,这儿就是一个典型的Hashmap。

注:什么是Map集合?

Map用于保存具有映射关系的数据,Map集合里保存着两组值,一组用于保存Map的ley,另一组保存着Map的value

和查字典类似,通过key找到对应的value,通过页数找到对应的信息。用学生类来说,key相当于学号,value对应name,age,sex等信息。用这种对应关系方便查找

那么在APK中,它的第一步是初始化一个map,之后存入了对应的消息,看着和我们在Fiddler 抓包APP GET请求中的字段一致。

接下来,我们用Smali动态调试跟踪一下集合m从初始化到存入数据的全部过程,但是其实接下来的演示操作不用动态调试也是完全可以的达到最终,但为了更好的观察APP在动态调试时的参数调用以及动态调试操作的过程,我们依然选择用动态调试来做演示。

  • 反编译APK获取Smali代码,因为调试是基于Smali的
java -jar apktool.jar d -f xlblog.apk -o xlblog

  • .method .end method 分别是方法开始和结束的地方

:d.smali 我们需要关注的关键代码,如下

# virtual methods
.method protected a()Ljava/lang/String;
    .locals 1

    .prologue
    .line 27
    const-string v0, "http://app.blog.sina.com.cn/api/article/get_article_info.php"

    return-object v0
.end method

.method public a(Lcom/sina/sinablog/network/d$a;Ljava/lang/String;Ljava/lang/String;I)V
    .locals 4

    .prologue
    const/4 v3, 0x0

    .line 14
    invoke-static {}, Lcom/sina/sinablog/network/d;->m()Ljava/util/HashMap;

    move-result-object v0

    .line 15
    const-string v1, "article_id"

    invoke-virtual {v0, v1, p2}, Ljava/util/HashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

    .line 16
    const-string v1, "blog_uid"

    invoke-virtual {v0, v1, p3}, Ljava/util/HashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

    .line 17
    const-string v1, "is_default"

    invoke-static {v3}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;

    move-result-object v2

    invoke-virtual {v0, v1, v2}, Ljava/util/HashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

    .line 18
    invoke-virtual {p1, v0}, Lcom/sina/sinablog/network/d$a;->setParams(Ljava/util/HashMap;)V

    .line 19
    invoke-virtual {p0}, Lcom/sina/sinablog/network/d;->a()Ljava/lang/String;

    move-result-object v0

    invoke-virtual {p1, v0}, Lcom/sina/sinablog/network/d$a;->setUrl(Ljava/lang/String;)V

    .line 20
    invoke-static {}, Ljava/lang/System;->currentTimeMillis()J

    move-result-wide v0

    invoke-virtual {p1, v0, v1}, Lcom/sina/sinablog/network/d$a;->setRequestTime(J)V

    .line 21
    invoke-virtual {p1, v3}, Lcom/sina/sinablog/network/d$a;->setIsMainThread(Z)V

    .line 22
    invoke-virtual {p0, p1}, Lcom/sina/sinablog/network/d;->a(Lcom/sina/sinablog/network/bf;)V

    .line 23
    return-void
.end method

对比jadx反编译的结果与d.smali的内容的关键节点是一样的,由此确认就是这个smali文件

  • 接下来Android Studio 安装smalidea插件,因为我们要开始动态调试,需要打断点因此要安装Smalidea工具

调试工具的话,几乎所有的主流Java IDE配上smalidea插件都可以对Smali进行动态调试,除此之外,JEB也可以直接调试Smali,IDA也有调试DEX的能力,还有Qtrace等等工具,但调试Smali,建议使用Android Studio+smalidea插件这个组合,操作简单,功能强大,效果也很稳定

打开Android Studio工具,点击File,选择Settings,点击Plugin,再点击installplugin from disk。找到之前下载的smalidea插件选中点击OK,插件就添加成功了,会弹出弹窗,提示重启AndroidStudio,点击重启

android studio smalidea plugin导入版本低的smalide 会报错,在新版的Android studio 中已经不支持了,会出现如下报错:

解决办法如下:

smalidea v0.06 版本修复了新版的Android studio 中不支持的这个问题,下载地址:https://bitbucket.org/JesusFreke/smalidea/downloads/

更新日志https://github.com/JesusFreke/smalidea

  • 打开项目【Open an existing Android Studio Project】,选择xlblog文件夹,等待其加载

:Android Studio 支持导入apk,启动 Android Studio 或者 点击File 是否有 Profile or Debug APK 这个选项,然后导入APK


  • 调试环境配置

在Android Studio工程中右键点击smali文件夹,设定MarkDirectory as -> Sources Root

打开Android Studio的File-> Project Structure选择,选择对应的JDK

  • 配置远程调试的选项,选择Run–>EditConfigurations选项,点击“+”号,新建remote类型调试器,设置Name,修改端口号,可以选择未被占用的任意端口,这里设置为8700


注:Android Studio 连接模拟器会出现失败现象,因为在SDK默认的路径路面,已经有一个adb了,但连接模拟器需要对应的adb,我这里以逍遥游模拟器为例,将下图标注丢到SDK路径下即可解决逍遥游报错的问题


  • 开启系统调试(博主这里的虚拟机是x86,所有对应的mprop要是对应的版本格式)
adb push mprop /data/local/tmp/
adb shell
chmod +x /data/local/tmp/mprop
su
cd /data/local/tmp/mprop
./mprop ro.debuggable 1
setprop ro.debuggable 1
getprop ro.debuggable

  • 查看APP进程并映射端口(不修改源码动态调式,此步骤省略)
调试模式启动app
adb shell am start -D -n 包名/.你要调试的界面
例如:
adb shell am start -D -n com.sina.sinablog/.MainActivity
查看进程号
adb shell ps | findstr 包名
例如:
adb shell ps | findstr com.sina.sinablog

端口映射(使用 jdwp 转发端口)
adb forward tcp:调试端口号 jdwp:进程号
例如:
adb forward tcp:8700 jdwp:6549

  • 下断点并启动调试

显然Smali代码更长的那个才是我们需要的重载方法,在代码左边空白处单击即可下断点


正式开始断点调试之前,首先同学们来了解一下,下面这行代码,不熟悉的可以去看博主同学之前的Smali语法总结的文章,下面的smali语法大致的意思是调用m()方法初始化HashMap并将其值赋给v0(或存储到寄存器v0中)

.line 14
invoke-static {}, Lcom/sina/sinablog/network/d;->m()Ljava/util/HashMap;

move-result-object v0

接下来,等程序运行完move-result-object v0 这一行后,在Watches监视器中添加v0,如下图所示:

随后按F8快捷键或点击Step Over 图标来一步步看着程序往下一步走时v0的变化,可以看到程序在初始化HashMap时,把方法已经塞进去五个字段了,一步步F8,会发现v0里的字段越来越多,没过多久,Get请求的九个字段就全部存储到寄存器v0中了


光靠F8一行一行走是没办法得知的,可以退出调试模式,更加精细的看一下,F7进入到子方法,在m方法中,获得了5个字段,然后Shift+F8跳出方法,返回之前进入子方法停留的地方

  • 出了m方法后,得到了三个字段


  • 在到达setParams方法时按F7进入子方法,就可以看到smali中的sign值了

0x04 Android Studio 普通模式调试APK

需要安装smalidea工具,此处不介绍了,请往前面翻看即可

案例APK:AliCrackme_1.apk

  • 反编译APK
java -jar apktool.jar d -f AliCrackme_1.apk -o AliCrackme

:-d参数,代表反编译得到的smali是Java文件,这里指文件后缀名是Java。如果不带这个参数,后缀名是smali的,否则后最美是java的(其实只是后缀名变了,里面的内容还是smali)

  • 检查android:debuggable 调试属性是否ture,不为ture自行更改或没有android:debuggable 属性自行添加

在AndroidManifest.xml的application添加属性:android:debuggable="true" android:debuggable="true" 它表示该APP应用是否是Debug版本,这将会影响到该APP应用是否能被调试,所以为了调试必须设置为true

  • Android Studio 导入反编译好的smali代码

一路默认即可

导入成功后,我们主要关注的smali文件

  • 在AndroidStudio工程中右键点击smali文件夹,设定MarkDirectory as -> Sources Root

  • 打开AndroidStudio的File-> Project Structure选择,选择对应的JDK

  • 配置远程调试的选项,选择Run–>EditConfigurations选项,点击“+”号,新建remote类型调试器,设置Name,修改端口号,可以选择未被占用的任意端口,这里设置为8700

  • 重新打包生成apk并签名(需卸载原来的apk)
apktool b -d out -o 3.apk
或
apktool b -d out

安装后打开apk,发现是空白,这里是进入了调试状态,并不是出现错误

  • 打开Android Studion

打开eclipse–>新建java project(第一次打开在project中找)–>更改默认路径为 out目录–>选择smali–>finished

  • adb 找不到设备用如下办法
adb kill-server 
adb start-server 
adb remount

PS:也能是adb版本跟Android Studion默认的adb版本有冲突,把模拟器的adb覆盖到Android Studio SDK目录下即可

  • 找到关键点,然后打断点

设置断点(可在OnCreate函数或OnClick函数等的下一语句下断点),这里需要首先分析源代码,找出自己想要下断点的地方,我们通过运行程序知道此次是需要破解密码,那入手点是否能从按钮处寻求突破,通过查看activity_main.xml 文件知道button按钮定义的是@+id/button 这个值

然后全局搜索button,一个个找,查找我们想要的button的关键节点,此处直接给出了有关联的button的地方,如下:

从上图中,可以发现我们从values/public.xml(这个文件很重要,是我们在寻找突破口的重要关键,比如我们有时候需要通过字符串内容来定位到关键点)文件中发现了button的这个值,然后通过这个值确认了button在smali里面关键代码处的位置

MainActivity 中使用了findViewById()方法来获得其中的Button元素,然后给Button创建了一个点击监听事件,如下:

到内部类MainActivity$1,搜搜看,肯定实现了OnClickListener接口,那么直接搜onClick方法


  • 运行程序,设置远程调试工程【1】(此处可跳过,非必须步骤)

在调试服务端这里会看到两个端口号:8600/8700,首先在这里的端口号,代表的是,远程调试服务器端的端口,下面简单来看一下,Java中的调试系统:

为了更好的了解远程调试,这里顺带讲一下DDMS,它的三个角色:

1)JDB Client端(被调试的客户端),这里我们可以认为我们需要破解的程序就是客户端,如果一个程序可以被调试,当启动的时候,会有一个jdwp线程用来和远程调试服务端进行通信

需要破解的程序启动了JDWP线程,注意这个线程也只有当程序是debug模式下才有的,也就是AndroidManifest.xml中的debug属性值必须是true的时候,也就是一开始为什么一开始时要修改这个值的原因

2)JDWP协议(用于传输调试信息的,比如调试的行号,当前的局部变量的信息等),这个就可以说明,为什么我们在一开始的时候,反编译成java文件,因为为了Eclipse导入能够识别的Java文件,然后为什么能够调试呢?因为smali文件中有代码的行号和局部变量等信息,所以可以进行调试的

3)JDB Server端(远程调试的服务端,一般是有JVM端),就是开启一个JVM程序来监听调试端,这里就可以认为是本地的PC机,当然这里必须有端口用来监听,那么上面的8600端口就是这个作用,而且这里端口是从8600开始,后续的程序端口后都是依次加1的,比如其他调试程序

:也可以使用adb jdwp 命令查看,当前设备中可以被调试的程序的进程号信息

  • 使用adb通过静态获取当前界面调用的文件(此处可跳过,非必须步骤)
adb shell dumpsys activity top
  • 运行程序,设置远程调试工程【2】

打上断点,点击右上方的小绿色虫子

使用F6单步调试,F5单步跳入,F7单步跳出进行操作,发现该APP使用v3变量保存了输入的密码

F7 继续往下走,这里看到调用了MainActivity的getTableFromPic方法,获取一个String字符串,从变量的值来看,貌似不是规则的字符串内容,应该是做了加密处理

接下来又遇到一个关键点getPwdFromPic,从字面意义上看,应该是获取正确的密码,用于后面的密码字符串比对

在这里好像对密码做了一次验证的样子,但密码的内容,依然貌似是一个不规则的字符串,跟之前获取的table字符串内容格式很小,继续往下一步走

调用了系统的Log打印,log的tag就是v6保存的值:lil

看到v3是保存的我们输入的密码内容,这里使用utf-8获取他的字节数组,然后传递给access$0方法,使用F7进入这个方法:

就是把传递进来的字节数组,循环遍历,取出字节值,然后转化成int类型,然后在调用上面获取到的table字符串的chatAt来获取指定的字符,使用StringBuilder进行拼接,然后返回即可

v2就是我们拼接加密之后的内容,如下:

Shift+F8 跳出,查看,我们返回来加密的内容是:123456,也就是说123456=>么广亡门义之,密码就不对了,我们输入的内容加密后是v2跟正确的密码v4对比,显示是错误的


再往下面就是密码比对了… …

梳理一下流程:

  • 调用MainActivity中的getTableFromPic方法,获取一个table字符串
  • 通过MainActivity中的getPwdFromPic方法,获取正确的密码内容
  • 获取我们输入内容的utf-8的字节码,然后调用access$0 方法,获取加密之后的内容
  • access$0 方法中在调用bytesToAliSmsCode方法,获取加密之后的内容

通过上面的分析之后知道了获取加密之后的输入内容和正确的密码内容做比较,那现在我们已经掌握了该APP的:密钥库字符串和正确的加密之后的密码,以及加密的逻辑

破解思路:已知道加密之后的字符组成的字符串,可以通过遍历加密之后的字符串,循环遍历,获取字符,然后再去密钥库找到指定的index,然后再转成byte保存到字节数组,最后用utf-8获取一个字符串,最后得出的结果即是破解的密码,如下:

package com.java.poc;

public class AliCrackme_P {
    public static void main(String[] args) {
        // 密钥字符串内容(汉字)
        String tableStr = "一乙二十丁厂七卜人入八九几儿了力乃刀又三于干亏士工土才寸下大丈与万上小口巾山千乞川亿个勺久凡及夕丸么广亡门义之尸弓己已子卫也女飞刃习叉马乡丰王井开夫天无元专云扎艺木五支厅不太犬区历尤友匹车巨牙屯比互切瓦止少日中冈贝内水见午牛手毛气升长仁什片仆化仇币仍仅斤爪反介父从今凶分乏公仓月氏勿欠风丹匀乌凤勾文六方火为斗忆订计户认心尺引丑巴孔队办以允予劝双书幻玉刊示末未击打巧正扑扒功扔去甘世古节本术可丙左厉右石布龙平灭轧东卡北占业旧帅归且旦目叶甲申叮电号田由史只央兄叼叫另叨叹四生失禾丘付仗代仙们仪白仔他斥瓜乎丛令用甩印乐";
        // 程序内部正确的密码(汉字)
        String passwordStr = "义弓么丸广之";
        char[] passwordIndex = new char[passwordStr.length()];
        for (int i=0;i<passwordIndex.length;i++){
            int index = tableStr.indexOf(passwordStr.charAt(i)); // 返回passwordStr字符串值,并在字符串中首次出现的位置第一个字符,每次+1循环计算是否匹配APP正确的密码
            passwordIndex[i] = (char)(index);
        }
        System.out.println("Password:"+new String(passwordIndex));
    }
}

0x05 Android Studio 调试模式调试APK

  • 直接不插入等待调试代码的方法

定位当前界面

adb shell dumpsys activity top
adb shell am start -D -n com.example.simpleencryption/.MainActivity

点击绿色小虫子

接下来就是一步步的调试了,后续步骤跟普通调试差不多

  • 插入等待调试代码的方法
    • 在入口处添加waitForDebugger代码进行调试

以前旧的方法中会在最初的入口处添加waitForDebugger代码进行调试或者使用adb shell am start -D -n com.example.simpleencryption/.MainActivity 来以Debug模式启动应用,但在新的动态调试方法中 Android Studio(3.x以上版本)即使没有添加android.os.Debug.waitForDebugger(),一样可以打断点调试,但是如果添加了android.os.Debug.waitForDebugger(),一样的可以,启动APP的时候会出现一个Wait debug的对话框,但我这里是出现一个白板(如下图),但不影响实际的调试操作

一般的入口Activity,就是程序启动的地方。查找这个Activity的话,方法太多。例如在AndroidManifest.xml中找到,因为入口Activity的action.MAIN和category是固定的,或者aapt查看apk的内容方式,以及用ADB 查看apk 当前的activity(adb dumpsys activity top

添加调试语句设置断点,在主入口Activity的OnCreate函数的第一行添加waitForDebugger代码:

invoke-static {}, Landroid/os/Debug;->waitForDebugger()V

对应的JAVA代码

android.os.Debug.waitForDebugger()
  • 查看启动APP的进程PID,然后进行端口转发
adb shell ps

adb forward tcp:8700 jdwp:1206

其中的“tcp”是之前配置调试环境时指定的端口号,“jdwp”这里指的是我们要调试的程序的进程pid。这里是将本地端口(PC端口)映射到远程端口(手机端口),之后PC端访问8700端口的数据包,会自动转发到手机的1206端口

后续步骤跟直接不插入等待调试代码的方法一样

参考链接

https://www.cnblogs.com/lsgxeva/p/13490827.html

https://blog.csdn.net/ming54ming/article/details/115049718

http://www.520monkey.com/archives/619


你以为你有很多路可以选择,其实你只有一条路可以走


版权说明:如非注明,本站文章均为 扬州驻场服务-网络设备调试-监控维修-南京泽同信息科技有限公司 原创,转载请注明出处和附带本文链接

请在这里放置你的在线分享代码
«    2024年12月    »
1
2345678
9101112131415
16171819202122
23242526272829
3031
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
搜索
最新留言
    文章归档
    网站收藏
    友情链接