Android逆向工程:实战!讲解修改插入Smali代码的规则,带你快速进行二次改造!

小伙伴们大家好,又是一个周过去了,之前我们讲解的知识学的还好么?那么今天我们就要接着往前进,学习难点中的难点:关于如何在Smali代码中修改原有逻辑,添加或插入我们自己的逻辑!可以这么说,整个逆向工程中,修改Smali代码改变原有逻辑是最终实现的目的,我们之前学习的逆向工具的使用,快速定位的技巧,基本上都是为它服务的。

而逆向工程最迷人的地方也就在于此,更改原有逻辑执行特殊操作,会使人有很大的成就感和满足感!这里当然也要说一下,切不可将技术用于违法犯罪的事情上呦,查找软件漏洞,改进软件功能,提高软件安全,这才是我们逆向工程师应该做的~

好了废话少说,就让我们赶快开始今天的学习把!今天的讲解示例还是百度手机助手。今天的目的是修改手机助手中的评论逻辑,这里我们已经知道了,当我们对某一个应用发表过一次评论后,那么接下来我们就无法再次对该应用评论,只能修改评论或者评论别人的评论,下图所示:

 那么接下来我们就先实现一个简单的小目标:修改原有逻辑,使之就算评论过之后显示的还是“发表评论”,而不是“修改评论”!具体怎么实现,那就让我们一块来学习把!

首先我们还是先通过上一篇博客中的学习,通过快速定位三步走快速定位到该控件,你会发现这个空间还是我们上篇博客中提高的那个Text View,在主要的消费活动AppDetailsActivity中,该Text View多次调用了setText()方法,说明它改动了很多次的文本信息,其中包括修改文本“发表评论”为“修改评论"!

那么这里我们就应该确定需要寻找到的关键代码:该Text View设置文本为”修改评论“的代码语句!

首先第一步,全局搜素字符串”修改评论“,在String.xml文件中发现了该字符串:

一般情况下一个商业级的App都是通过引用资源中的字符串来设置某些文本信息的,而不是直接在代码中写死,这样做的目的一是引用灵活,二是你也看见了,挡我们一下,不让我们这么快就发现关键代码。

我们从String.xml文件中可以获知一个信息,就是中文”修改评论“在项目中的英文形式:comment_modify,获取到英文形式,那么下面我们就开始全局搜索这个英文字符串,这一步的目的是找到该字符串被引用的地址:

 显而易见,引用地址为:0x7f0a0683,那么接下来我们就继续全局搜索该地址:

很快就发现了在主要的消费活动AppDetailsActivity中的引用,而且只有一处引用,那我们点击进去看看它的关键代码:

const v1, 0x7f0a0683#  字符串:修改评论

    invoke-virtual {v0, v1}, Landroid/widget/TextView;->setText(I)V#   这里textView设置为“修改评论”

    iget-object v0, p0, Lcom/baidu/appsearch/appcontent/AppDetailsActivity;->K:Lcom/baidu/appsearch/ui/ColorfulProgressBar;

    invoke-virtual {v0, v5}, Lcom/baidu/appsearch/ui/ColorfulProgressBar;->setEnabled(Z)V

 接下来就是Smali代码的分析了,首先是赋值指令,把字符串”修改评论“的地址放进了v1寄存器中,接下来就是调用了TextView的setText()方法,参数为v1寄存器中的地址,很显而易见的就是,这里就是TextView设置文本为”修改评论“的地方!接下来就轮到我们jadx登场了,首先我们先确定这段关键代码所处的方法:

关键方法为private void x(),打开用jadx打开百度手机助手Apk文件,进入 AppDetailsActivity.java文件,查看方法x():

 从上面的x()方法中可以看出到,有两处调用了setText()方法,一处是:this.v.setText(R.string.comment_modify);,另一处是:this.v.setText(R.string.detail_comment_send);,我们可以很容易的在这里看出他们分别引用的资源,comment_modify为:修改评论,detail_comment_send为:发表评论!很显然这里this.v.setText(R.string.comment_modify);是关注的重点,看看他的执行条件,发现存在一个关键判断:this.V.h是否为空。如果this.V.h为空的话,那么就会执行this.v.setText(R.string.detail_comment_send);,即TextView文本设置为:发表评论;如果this.V.h不为空的话,那么就会执行this.v.setText(R.string.comment_modify);,即TextView文本设置为:修改评论!既然这个this.V.h变量被当作这么重要的关键判断,那么我们还真的有必要去看看这个变量是什么:

变量V是类k的对象,h是类k中的一个变量,h是类b的对象,那么我们可以去看一下这个类b是何方神圣:

一眼看出这是序列化,很明显这里是实体类,估计是评论的实体类。那么这里我们就可以猜测:每次评论过之后,百度都会构建出一个评论实体类临时储存下来,然后在活动中判断这个评论实体类是否为空,如果为空,说明用户还没有评论,那么TextView就设置文本为:发表评论;如果不为空,说明用户已经评论过, 那么TextView就设置文本为:修改评论!

下面我们就开始思考:如何保证每次评论过之后TextView文本还是”发表评论“呢?

很显然,我们已经发现了控制开关,思路就是:当每次发现his.V.h不为空的时候,那么就把它设置为空!

接下来就是正式的修改Smali代码中原有的逻辑了,这里我们修改Smali代码还是通过Android Studio来完成。

首先我们来到判断this.V.h是否为空的代码处:

 if-eqz v0, :cond_2#这里就是if(this.V.h!=null)

    iget-object v0, p0, Lcom/baidu/appsearchzpandfuan/appcontent/AppDetailsActivity;->v:Landroid/widget/TextView;

    const v1, 0x7f0a0683#  字符串:修改评论

    invoke-virtual {v0, v1}, Landroid/widget/TextView;->setText(I)V#   这里textView设置为“修改评论”

    iget-object v0, p0, Lcom/baidu/appsearch/appcontent/AppDetailsActivity;->K:Lcom/baidu/appsearch/ui/ColorfulProgressBar;

    invoke-virtual {v0, v5}, Lcom/baidu/appsearch/ui/ColorfulProgressBar;->setEnabled(Z)V

    goto :goto_0

    :cond_2

 这里就是判断的地方,首先执行指令if-eqz v0, :cond_2,判断v0寄存器是否为0,这里v0寄存器中放置就是this.V.h变量,判断是否为0在Smali代码中就是判断是否为空,不为空就执行下面的语句,为空就跳到:cond_2分支处。接下来我们就要添加上自己的逻辑判断,在该if判断之前我们先行判断this.V.h的值,发现this.V.h不为空的话,那么就把它设置为空值,保证在进行下面的判断中,this.V.h始终处于空值状态!

加入我们的代码逻辑:

if-eqz v0, :cond_11#这里加上了一个判断,如果评论的实例不为空,就设置它为空

    const v0,0x0

    iget-object v7, p0, Lcom/baidu/appsearch/appcontent/AppDetailsActivity;->V:Lcom/baidu/appsearch/appcontent/c/k;

    iput-object v0, v7, Lcom/baidu/appsearch/appcontent/c/k;->h:Lcom/baidu/appsearch/appcontent/comment/b; 

    :cond_11

    if-eqz v0, :cond_2#这里就是if(this.V.h!=null)

    iget-object v0, p0, Lcom/baidu/appsearchzpandfuan/appcontent/AppDetailsActivity;->v:Landroid/widget/TextView;

    const v1, 0x7f0a0683#  字符串:修改评论

    invoke-virtual {v0, v1}, Landroid/widget/TextView;->setText(I)V#   这里textView设置为“修改评论”

    iget-object v0, p0, Lcom/baidu/appsearch/appcontent/AppDetailsActivity;->K:Lcom/baidu/appsearch/ui/ColorfulProgressBar;

    invoke-virtual {v0, v5}, Lcom/baidu/appsearch/ui/ColorfulProgressBar;->setEnabled(Z)V

    goto :goto_0

    :cond_2

这里我们加入了一个很简单的判断,执行指令if-eqz v0, :cond_11,判断v0是否为空值,重点是下面当不为空的时候执行语句,const v0, 0x0,这条指令意思为把0,即null赋值给v0寄存器,因为下面的判断就是根据v0寄存器中的值,单单把v0赋值为空还不太行,因为别忘了this.V.h变量还是有值的,为了以防万一,我们还需要把this.V.h这个变量赋值为空!

首先通过iget-object指令拿到this.V变量,然后通过iput-object指令,用v0寄存器中的空值为h变量赋值,:cond_11跳转接口放在接下来的if判断上面,这样就完成了整个修改工作。如果没错的话,评论过之后,程序首先进入我们的逻辑判断中,把v0寄存器和this.V.h变量赋值为空,之后再来到下面的判断,就会执行this.v.setText(R.string.detail_comment_send);语句,把textView本文设置为:发表评论!

下面我们就开始测试一下吧,打开Android Killer,点击最上面”Android“,点击”编译“,就会自动进行打包签名:

打包签名完成后会生成新的Apk,放在项目的bin文件夹下:

 我们把新生成的Apk文件复制到手机中,运行,发现没有奔溃现象,然后点击”发表评论“发表一条评论,完成后发现下面按钮显示的还是”发表评论“!如图所示:

第一个显示的评论”支持不错的应用“是我发表的,发表完成后可以看到下面显示的还是”发表评论“,至此说明,我们修改原有的Smali代码 逻辑成功!你可以再次点击发表评论,结果你会发现弹出”评论失败“,这说明百度手机助手在提交评论的时候还判断了别的多个状态,我们这里只是单纯的把this.V.h设置为空,只能满足界面的改变。至于真正的深层次修改百度手机助手的评论逻辑,博主将不会带领大家去探讨,这里我们仅限于技术的学习,搞破坏是不可以的。

好了,接下来讲解一下主要的技术要点。不知道你是否关注了下面这两句Smali代码:

    iget-object v7, p0, Lcom/baidu/appsearch/appcontent/AppDetailsActivity;->V:Lcom/baidu/appsearch/appcontent/c/k;

    iput-object v0, v7, Lcom/baidu/appsearch/appcontent/c/k;->h:Lcom/baidu/appsearch/appcontent/comment/b;

你可能会说,这很普通的Smali代码啊,没啥稀奇的,就是我们刚才添加上用来修改this.V.h变量为空的代码语句。注意到那个v7寄存器了吗?这个v7寄存器是我们自己主动新增的,在之前的这个方法中并没有v7这个寄存器存在。为什么要添加的是v7寄存器呢?而不是v3,v4或者v5呢?

这是因为在这个方法中x(),被使用到的寄存器最高为v6,为什么不使用v3,v4或者v5,那是因为它们在这个方法中都有使用的逻辑,如果使用他们,就相当于修改了他们里面原本的数据,那么接下来导致的后果就是程序崩溃,或者回编译失败!

所以修改Smali代码第一条需要遵守的规则就是:如果需要使用到一个寄存器来放置数据,那么最好尽量使用一个新的寄存器,而不是原来就已经存在的!

你以为这样就没事了吗?不,我们还需要修改一个地方,就是这里:

.method private x()V
    .locals 7

    const/4 v6, 0x4

    const/4 v5, 0x1

    const/4 v4, 0x0

    const/4 v3, 0x0

 看到.locals了吗,这句指令表示该方法会使用到7个寄存器,表示会有v0到v6总共七个寄存器被使用,那么如果你又在这个方法里新增了一个寄存器,这里新增了v7,超过了这个指令值,导致的后果就是程序崩溃!所以,这里需要把.locals后面的值修改为:8,表示我们将会使用到8个寄存器,这样才会把v7寄存器声明进来。

修改Smali代码第二条需要遵守的规则:如果在方法中新增了本地寄存器,则需要修改.locals的值,使之包含添加的寄存器。注意:.locals最高不要超过15,超过15则会导致回编译失败!这是因为Smali规则,使用寄存器的范围为v0至v15。

通过上面 两条规则我们就可以看出,在方法内修改Smali代码原有逻辑是比较麻烦,需要考虑到很多,那么下面第三条规则就是:尽量不要过多的在原方法内修改逻辑,最好在类中新建一个方法,然后在需要修改逻辑的地方使用p0寄存器进行调用即可!

根据第三条规则,实际上我们修改逻辑还可以这么写:在活动中新建一个Void无参方法,在这个方法内我们来设置this.V.h的值为空,然后在x()返回内,直接调用这个方法即可 。这样做的好处就是,不用修改.loclas的值,因为我们调用这个void无参方法是通过p0调用的,p0就是this指针,不会占用多余的寄存器。

第四条规则:新建的方法不要写入过多的逻辑操作,正确的做法是把逻辑分散到多个方法中,即你可以新建多个方法来共同处理这个逻辑,但不可把过多的逻辑都写入一个方法内!

为什么?其实原因还是因为Smali规则中寄存器范围为v0至v15。你如果把很多的逻辑处理都写入一个方法内,这个方法使用寄存器的数量超过16个,那么就会导致回编译失败的问题。什么情况乱下属于过多逻辑,就是使用到的寄存器数量超过16,实际上,当寄存器数量超过10个的时候,就建议你考虑逻辑分散了,寄存器数量为7,8个为正常。

 好了,今天的讲解到此就结束了。又不懂的地方可以在文章下方评论留言,我会一一解答。

需要引用的地方请标明出处,谢谢合作!

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值