在本篇中,你将会学习到如何快速定位到关键代码的相关知识和技巧,其核心技术点我这里总结一下就是:顺藤摸瓜!在茫茫一望无际的Smali代码中要找到我们想要的那段关键代码,如果你采用从头到尾捋逻辑,那难度无疑是大海捞针,工作量大得惊人,所以要想找到那个我们最终想要的“甜瓜”,掌握必要的快速定位技巧是不可或缺的!闲话少说,就让我们赶快开始今天的学习吧!
想要顺藤摸瓜,我们还需要一个利器傍身,那就是ADM,设备监控器。下面就让我们开始逆向解析的第一步,打开ADM设备监控器:这里首先要确保你的手机已经连接电脑
在最左边一列显示的是目前手机中安装的所有应用列表,这里展示的是每一个应用的包名。中间下面部分是手机状态的实时输出,这里并不是我们关注的地方,最右边的OpenGL Trace View我们也不用管。看到中间部分的空白吗?这才是我们最关注的地方,当然我们现在还没有开始解析界面,所以这里空空如也~
讲解还是需要一个例子,这里我使用百度手机助手来为大家演示。首先我们需要在最左边的应用列表中找到百度手机助手的包名:com.baidu.appsearch
找到后进行点击,你会发现在最上面操作栏图标变为了可点击,而不再是灰色:
下面我们就可以打开百度手机助手App,来到我们想要进行修改原有逻辑的地方,比如我这里想要修改百度手机助手里面对应用的评论操作,那么我就来到评论应用的界面,然后点击操作栏中的图标,接下来就会对该界面就行自动解析,解析完成你就会看到手机上的界面出现在中间位置:
现在评论界面已经被解析展示出来了,这里博主说一句,所使用的百度手机助手版本为5.6.1,并不是最新的版本,所以评论界面和最新不同。ADM还是非常智能的,这里你根本不需要太多的复杂操作,只需要把鼠标在界面上移动,旁边就会随着移动自动展示鼠标下的布局或者控件,比如这里界面中”发表评论“是我们的关注点,所以我们把鼠标移动到发表评论位置:
移动到之后点击一下就会锁定,然后我们在右边的显示框中就可以很清楚的看到这个布局的架构:整体使用的是RelativeLayout布局,里面嵌套了三个View控件和一个TextView控件,在右边的显示框中我们也是可以进行点击查看的,这里我们主要看的是控件的ID,ID值会在下面Node Detail的resource-id中展示,如果没有任何展示,说明该控件没有设定ID值。比如在上图中的View,下面Node Detail的resource-id中没有任何展示,说明该View没有设定ID,也就是告诉了我们,激发”发表评论“这个点击事件的并不是该View控件!因为要设定点击监听,肯定是要获取到该控件的。所以我们的目标就是在三个View控件和一个TextView控件要找到一个带有ID值的控件!
最后我们会发现只有Text View控件有ID:
通过 resource-id我们很清楚的就知道该ID值为app_content_btn_control_text,拿到了ID值我们下面就可以开始第二步,在smali代码中进行全局搜索该ID字符,看它在哪里被引用。
这里进行全局搜索是在AndroidSudio中进行,这里我默认你会使用Android Studio进行全局搜索。打开全局搜索,输入ID值,如下图:
这里并没有搜索到具体逻辑中的引用,很正常,回想一下在java代码中我们是如何获得一个控件的?通过R.id.xxx我们获取到该控件。在Java代码中我们可以直观的感受到这个字符被引用,那么这里是Smali代码,接近机器语言,在Smali代码中如果引用某一个资源,直接使用的是该资源所在的地址,这里为资源标识符,也就是在R.java文件中标识的地址。如下图所示:
上图只是给大家展示一下例子,和我们教案对象无关。从上图中我们可以看到,每一个ID都在这里赋值了一块标识符,我们通过R.id.xxx访问到的是该资源所在的地址。findViewById ()方法自然也是根据这个地址找到该控件。那么这里我们进行全局搜索的对象应该为”发表评论“该Text View控件所在地址,Smali代码中在哪里引用了该地址,就说明这里引用了该Text View。地址也很明朗,我们直接去R.java文件中看看是谁就好了,在上面的全局搜索中已经找到了该ID在R.java文件中的位置:
很清楚的看到该ID地址为”0x7f09025e“ ,下面我i们就把全局搜索对象改为”0x7f09025e“,再次进行搜索:
果然如此,我们很快就获得了Text View的引用位置。public和R不用管,这两个都是实际的逻辑引用,剩下我们就发现有两个活动进行了引用,一个是LocalSystemAppRecycleActivity,一个是AppDetailsActivity,其中AppDetailsActivity引用次数最多,引用了三次,而LocalSystemAppRecycleActivity只引用了一次。这里我们还需要判断哪一个活动才是我们需要的关键代码呢?
这里我们遵守一个原则:选多不选少。为什么?当一个资源在某一个类中被引用的次数比较多,我们就可以大概确定这个类是这个资源的消费类。毕竟被引用的目的只有一个,那就是使用它,被使用的次数越多,说明这个资源在这个类中起到的作用越大,对这个类也就越重要!同理,不单单指的是资源,也可以是一个变量值,一个类的对象,都遵守这个原则。
当然也会出现多个类中引用次数一致的情况,对于这种情况我们就需要进行相关的动态调试了,在每个类引用处打上断点,然后进行调试,观察程序走进了哪几个类,注意程序执行的先后顺序,这样就很容易分辨得出具体消费类了。
在本次教案用例中,我们使用选多不选少原则,判断AppDetailsActivity为主要的消费类,下面我们就可以直接进入AppDetailsActivity活动中,看看引用TextView控件地址的代码,这里为了方便下次解析阅读,建议找到关键代码后记得加上注释,Smali中加入注释关键符为”#“,而不是Java代码中的”//“或者”/**/“。这里我已经加上了注释”#发表评论的实例“。
下面就是第三步,进行代码逻辑分析了。代码逻辑分析的目的就是找出最终的那段关键代码所在。我们通过第二步的全局搜索发现,在AppDetailsActivity活动中”发表评论“TextView控件被引用了三处,这三处分别都干了什么,以及”发表评论“这个TextView控件在活动中的声明是什么,我们一概不知请,所以只能开始代码的逻辑分析,找出关键代码所在!
我们首先先看一下出现的第一处:
......(省略)
......(省略)
invoke-virtual {v0, v5}, Landroid/view/View;->setVisibility(I)V
iget-object v0, p0, Lcom/baidu/appsearch/appcontent/AppDetailsActivity;->v:Landroid/widget/TextView;
if-nez v0, :cond_4
const v0, 0x7f09025e#“发表评论”textView的实例
invoke-virtual {p0, v0}, Lcom/baidu/appsearch/appcontent/AppDetailsActivity;->findViewById(I)Landroid/view/View;
move-result-object v0
check-cast v0, Landroid/widget/TextView;
iput-object v0, p0, Lcom/baidu/appsearch/appcontent/AppDetailsActivity;->v:Landroid/widget/TextView;
:cond_4
iget-object v0, p0, Lcom/baidu/appsearch/appcontent/AppDetailsActivity;->v:Landroid/widget/TextView;
const/4 v2, -0x1
......(省略)
......(省略)
是不是感觉瞬间头都大了,卧槽这是什么啊?先别急,从上面的smali代码中我们能够得到一条非常有效的信息,那就是该TextView在活动中声明的情况!其中关键代码为:
const v0, 0x7f09025e#“发表评论”textView的实例
invoke-virtual {p0, v0}, Lcom/baidu/appsearch/appcontent/AppDetailsActivity;->findViewById(I)Landroid/view/View;
move-result-object v0
check-cast v0, Landroid/widget/TextView;
iput-object v0, p0, Lcom/baidu/appsearch/appcontent/AppDetailsActivity;->v:Landroid/widget/TextView;
还记得我们之前学习的Smali语法吗,这里就要派上用场了。首先const是赋值命令,这里把地址交给了v0寄存器,然后调用了findViewById()方法,把v0寄存器中的地址为参数值传进去,p0我们有说过,这里指的是”this“指针。然后接受一个返回值放入v0寄存器中。翻译成Java代码就是findViewById(R.id.xxxx)。执行了强转,也就是 (TextView)findViewById(R.id.xxxx),这是我们都很熟悉的。下面就进行了赋值,这里就可以看出具体赋值对象了,变量名为”v“。也就是说该TextView控件在该类中是变量v声明。翻译一下就是”this.v=(TextView)findViewById(R.id.app_content_btn_control_text)“。
下面就需要jadx工具上场了,在jadx直接打开该Apk文件,打开AppDetailsActivity查看它的Java代码,不知道jadx使用方法的可以看我上一篇博客,打开后如下:
在左边中直接点击v变量,就会自动定位到 该变量的声明处。下面我们就可以直接在jadx中查找变量v在该类中使用的情况,你可以直接快捷键”Ctrl+F“,就会打开搜索框,在里面输入”this.v“,就可以直接进行查看了:
jadx的搜索功能和AndroidStudio的搜索比较相像,使用起来会让你很熟悉,记得勾选”标记全部“:
这样颜色区分就会显示出来了,在代码中很明显就会看到! 那么我们就可以愉快的查找关键代码啦,目的是找到它的点击事件,要首先查找它注册点击事件的代码,也就是this.v.setOnClickListener();,可是你怎么也找不到,只能发现TextView在活动中设置了几次文本信息。这是怎么回事?难不成我们遗漏了什么?找错方向了?既然没有找到this.v.setOnClickListener();那就说明该TextView并没有设置点击事件,那么”发表评论“的点击事件到底是谁注册的?仔细想一下,我们还真的遗漏了一个,那就是RelativeLayout布局!赶快回去瞅瞅RelativeLayout布局:
发现这个布局还真的设置了ID值 ,ID值为”app_content_btn_control_progress“,很好,那么下面就简单了,使用我们上面的三部曲,直接定位到该布局:
发现别的类也引用了,但是我们不用管,因为上面我们已经明确的是AppDetailsActivity是具体的消费类, 所以直接进入AppDetailsActivity中引用布局的位置,代码为;
const v0, 0x7f09020a
invoke-virtual {p0, v0}, Lcom/baidu/appsearch/appcontent/AppDetailsActivity;->findViewById(I)Landroid/view/View;
move-result-object v0
check-cast v0, Lcom/baidu/appsearch/ui/ColorfulProgressBar;
iput-object v0, p0, Lcom/baidu/appsearch/appcontent/AppDetailsActivity;->K:Lcom/baidu/appsearch/ui/ColorfulProgressBar;
发现是变量k,这里你可能会疑问,不是RelativeLayout布局吗,怎么这里变成了ColorfulProgressBar?这里很明显的是,这个ColorfulProgressBar是百度自己写的一个自定义布局,继承的是RelativeLayout,不信我们可以去看一下:
看见了吧,.这里指明了继承自 RelativeLayout布局。
下面我们就通过jadx进行查找变量k是否注册了点击事件,发现还真的注册了,有三处:
这里要注意下是,查找的时候要勾选区分大小写:
因为这里是全混淆的代码,变量里面有大写的K和小写的k,这里我们要查找的变量值为大写的K。
我们发现这里注册的事件分别是变量an,ao,c。我们可以看一下这三个变量都是什么:
原来是Onclicklistener类。怪不得我们没有发现 AppDetailsActivity类继承了View.OnClickListener接口,原来这里的点击事件都是通过外部的接口实现的!
至此我们整个快速定位的流程和学习就已经结束了。
> 总结一下,三步走战略:
>
> 1.通过ADM进行界面解析,找出相关布局或者控件的ID值;
>
> 2.通过Android Studio对ID进行全局搜索,然后在R文件中获取该ID对应的地址,接着全局搜索该地址;找到后按照选多不选少的原则进行判断;
>
> 3.对引用处的smali代码进行逻辑分析,找出关键变量;然后在jdax中进行查找,开始下面细致的分析工作。
今天学习了这么多不知道你记住了多少,赶快消化消化今天的知识点,多多练习,相信你很快就会掌握的!