前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >初涉静态调试 - 修改Smali

初涉静态调试 - 修改Smali

作者头像
贺biubiu
发布2019-06-10 23:00:11
1.4K0
发布2019-06-10 23:00:11
举报
文章被收录于专栏:HLQ_StruggleHLQ_Struggle

前言

Enmmm,还记得之前看反编译之后的结果,对于 Smali 文件,简直懵的要死。

今天,一起好好回顾下。

首先来个小 Demo

效果如下:

将生成的 Apk 文件使用 ApkTool 进行解包,之后在 Sublime Text 3 中打开,下面附上下载链接:

链接: https://pan.baidu.com/s/1EbZsk106YLV22TgoVkbhbw 密码:f5v1

打开如下格式:

而接下来,我们重点关注 Smali 目录下的文件:

Smali 解析代码文件

Enmmm,这里还需要借助工具:Dalvik虚拟机操作码,进行辅助。

链接: https://pan.baidu.com/s/14I63tafdQRcBkSm6UO1qaQ 密码:2w7h

首先,我们先来看前三句:

代码语言:javascript
复制
.class public Lcom/hlq/apktooldemo/MainActivity;
.super Landroid/support/v7/app/AppCompatActivity;
.source "MainActivity.java"

首先,我们来依次解析下所代表含义:

  • L 代表 Java 类型文件,也就是 MainActivity;
  • super 则代表父类,也就是继承自 AppCompatActivity;
  • source 则代表文件名称,也就是当前文件名为:MainActivity。

那么依旧这些内容,转化为 Java 代码如下:

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity {

}

接着,往下继续看:

代码语言:javascript
复制
# instance fields
.field private mCheckID:Landroid/widget/Button;

.field private mUserLicenseID:Landroid/widget/EditText;

.field private mUserNameID:Landroid/widget/EditText;

Enmmm,显而易见,这里定义了三个全局变量,并且我们解析下这几行行所代表的含义:

  • instance fields:LZ 理解为类似作用域,表明界限;
  • .field private mCheckID:Landroid/widget/Button:定义一个私有变量名为 MCheckID 且类型为 Button。

那么针对这些,逆推 Java 代码如下:

代码语言:javascript
复制
private Button mCheckID;
private EditText mUserLicenseID;
private EditText mUserNameID;

而接下来,我们来看下 onCreate() 方法又被转化成了什么鬼?

代码语言:javascript
复制
# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
    .locals 1
    .param p1, "savedInstanceState"    # Landroid/os/Bundle;

    .line 21
    invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V

    .line 22
    const v0, 0x7f09001b

    invoke-virtual {p0, v0}, Lcom/hlq/apktooldemo/MainActivity;->setContentView(I)V

    .line 23
    invoke-direct {p0}, Lcom/hlq/apktooldemo/MainActivity;->initView()V

    .line 24
    invoke-direct {p0}, Lcom/hlq/apktooldemo/MainActivity;->initEvent()V

    .line 25
    return-void
.end method

显而易见,有一个名为 onCreate 并且有一个参数为 Bundle 类型,参数名为 savedInstanceState。

而其下则调用了 super,而 {p0, p1} 所代表的含义如下:

  • p0 代表当前,也就是 this,对应类型为 Landroid/support/v7/app/AppCompatActivity;
  • p1 则对应参数 onCreate(Landroid/os/Bundle;)V,这里参数依旧使用上面的 Bundle –> savedInstanceState

而 const v0, 0x7f09001b 则对应的具体类型,经过 LZ 搜索如下:

下面的 invoke-virtual {p0, v0}, Lcom/hlq/apktooldemo/MainActivity; ->setContentView(I)V 解析如下:

  • p0 代表类型为 com/hlq/apktooldemo/MainActivity;
  • v0 则代表引用下面 setContentView(i) 参数对应的则是我们 MainActivity 对应的 layout 文件 id。
代码语言:javascript
复制
.line 23
invoke-direct {p0}, Lcom/hlq/apktooldemo/MainActivity;->initView()V

.line 24
invoke-direct {p0}, Lcom/hlq/apktooldemo/MainActivity;->initEvent()V

而这俩行,则是定义了俩个方法,分别为:

  • initView()V;
  • initEvent()V。

So,结合起来,onCreate Smali 文件转换 Java 文件如下:

代码语言:javascript
复制
@Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
     initView();
     initEvent();
 }

而下面,继续查看 initView 方法:

代码语言:javascript
复制
// void 类型 方法名为 initView
.method private initView()V 
    .locals 2

    // 对应 id 类型为:<public type="id" name="tv_user_name" id="0x7f070085" />
    .line 28
    const v0, 0x7f070085

    // invoke-virtual 调用带参数的虚拟方法。
    // 这里便是实例化此控件 findViewById
    invoke-virtual {p0, v0}, Lcom/hlq/apktooldemo/MainActivity;->findViewById(I)Landroid/view/View;
    // move-result-object v0 移动上一次方法调用的对象引用返回值到 v0
    move-result-object v0
    // 参数类型 EditText
    // check-cast 检查 vx 寄存器中的对象引用是否可以转换成类型 ID 对应类型的实例
    check-cast v0, Landroid/widget/EditText;
    // 参数名 mUserNameID
    // iput-object vx, vy, 字段 ID根据字段 ID 将 vx 寄存器的值存入实例的对象引用字段, vy 寄存器中是该实例的引用。
    iput-object v0, p0, Lcom/hlq/apktooldemo/MainActivity;->mUserNameID:Landroid/widget/EditText;

    // 对应 id 类型为:<public type="id" name="tv_license" id="0x7f070084" />
    .line 29
    const v0, 0x7f070084
    // invoke-virtual 调用带参数的虚拟方法。
    // 这里便是实例化此控件 findViewById
    invoke-virtual {p0, v0}, Lcom/hlq/apktooldemo/MainActivity;->findViewById(I)Landroid/view/View;
    // move-result-object v0 移动上一次方法调用的对象引用返回值到 v0
    move-result-object v0
    // 参数类型 EditText
    check-cast v0, Landroid/widget/EditText;
    // 参数名 mUserLicenseID
    iput-object v0, p0, Lcom/hlq/apktooldemo/MainActivity;->mUserLicenseID:Landroid/widget/EditText;

    // 对应 id 类型为:<public type="id" name="btn_check" id="0x7f070022" />
    // 获取到 id
    .line 30
    const v0, 0x7f070022
    // 拿到 id 对应的类型
    invoke-virtual {p0, v0}, Lcom/hlq/apktooldemo/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v0

    check-cast v0, Landroid/widget/Button;
    // 赋给 v0 对应 java 代码则是:mCheckID = findViewById(R.id.btn_check);
    iput-object v0, p0, Lcom/hlq/apktooldemo/MainActivity;->mCheckID:Landroid/widget/Button;

    .line 34
    return-void
.end method

而在 initEvent() 方法中,则又是如下:

代码语言:javascript
复制
.method private initEvent()V
    .locals 2
    // iget-object-quick vx, vy, 偏移量 获取 vy 寄存器中实例指向+偏移位置的数据区的对象引用, 存入 vx
    .line 37
    iget-object v0, p0, Lcom/hlq/apktooldemo/MainActivity;->mCheckID:Landroid/widget/Button;
    // new-instance vx, 类型 ID 根据类型 ID 或类型新建一个对象实例,并将新建的对象的引用存入 vx
    new-instance v1, Lcom/hlq/apktooldemo/MainActivity$1;
    // invoke-direct {参数}, 方法名 不解析直接调用带参数的方法
    invoke-direct {v1, p0}, Lcom/hlq/apktooldemo/MainActivity$1;-><init>(Lcom/hlq/apktooldemo/MainActivity;)V
    // invoke-virtual/range {vx..vy}, 方法名 调用以寄存器范围为参数的虚拟方法。 该指令第一个寄存器和寄存器的数量将传递给方法
    // 这里调用 setOnClickListener 事件
    invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V

    .line 53
    return-void
.end method

而其下点击事件中关键内容解析如下:

代码语言:javascript
复制
// 获取 EditText 实例
.method static synthetic access$000(Lcom/hlq/apktooldemo/MainActivity;)Landroid/widget/EditText;
    .locals 1
    // 类型为 com/hlq/apktooldemo/MainActivity
    .param p0, "x0"    # Lcom/hlq/apktooldemo/MainActivity;
    // 参数名为 
    .line 14 mUserNameID
    iget-object v0, p0, Lcom/hlq/apktooldemo/MainActivity;->mUserNameID:Landroid/widget/EditText;

    return-object v0
.end method
// 同下同理
.method static synthetic access$100(Lcom/hlq/apktooldemo/MainActivity;)Landroid/widget/EditText;
    .locals 1
    .param p0, "x0"    # Lcom/hlq/apktooldemo/MainActivity;

    .line 14
    iget-object v0, p0, Lcom/hlq/apktooldemo/MainActivity;->mUserLicenseID:Landroid/widget/EditText;

    return-object v0
.end method
// 同下同理
.method static synthetic access$200(Lcom/hlq/apktooldemo/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z
    .locals 1
    .param p0, "x0"    # Lcom/hlq/apktooldemo/MainActivity;
    .param p1, "x1"    # Ljava/lang/String;
    .param p2, "x2"    # Ljava/lang/String;

    .line 14
    invoke-direct {p0, p1, p2}, Lcom/hlq/apktooldemo/MainActivity;->checkData(Ljava/lang/String;Ljava/lang/String;)Z

    move-result v0

    return v0
.end method

// checkData 包含俩个 String 类型参数
.method private checkData(Ljava/lang/String;Ljava/lang/String;)Z
    .locals 3
    // 参数名如下:
    .param p1, "userName"    # Ljava/lang/String;
    .param p2, "userLicense"    # Ljava/lang/String;
    // 调用 getBytes
    .line 56
    invoke-virtual {p1}, Ljava/lang/String;->getBytes()[B
    // move-result-object v0 移动上一次方法调用的对象引用返回值到 v0
    move-result-object v0
    // 临时存入变量中
    const/4 v1, 0x0
    // invoke-static {v0, v1} 调用带参数的静态方法
    invoke-static {v0, v1}, Landroid/util/Base64;->encodeToString([BI)Ljava/lang/String;
    // move-result-object v0 移动上一次方法调用的对象引用返回值到 v0
    move-result-object v0
    // const-string vx, 字符串 ID 存入字符串常量引用到 vx,通过字符串 ID 或字符串
    const-string v1, "\r|\n"
    // 同上
    const-string v2, ""
    // invoke-virtual{参数}, 方法名调用带参数的虚拟方法
    // 这里进行了一个转化 将以上的 “\r|\n” 替换为 “”
    invoke-virtual {v0, v1, v2}, Ljava/lang/String;->replaceAll(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
    // move-result-object v0 移动上一次方法调用的对象引用返回值到 v0
    move-result-object v0
    // 而这里则进行一个判断 校验是否相等
    invoke-virtual {v0, p2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
    // move-result vx 移动上一次方法调用的返回值到vx。0A00 - move-result v0移动上一次方法调用的返回值到 v
    move-result v0
    // 将校验结果返回
    return v0
.end method

而最后,我们简单看下 onClick 中如何处理:

代码语言:javascript
复制
// 调用 onClick 方法 传如 View
# virtual methods
.method public onClick(Landroid/view/View;)V
    .locals 6
    // 声明一个 View 类型参数
    .param p1, "v"    # Landroid/view/View;

    .line 40
    // iget-object-quick vx, vy, 偏移量 获取 vy 寄存器中实例指向+偏移位置的数据区的对象引用, 存入 vx
    iget-object v0, p0, Lcom/hlq/apktooldemo/MainActivity$1;->this$0:Lcom/hlq/apktooldemo/MainActivity;
    // invoke-static{参数}, 方法名 调用带参数的静态方法
    // 获取 EditText 实例
    invoke-static {v0}, Lcom/hlq/apktooldemo/MainActivity;->access$000(Lcom/hlq/apktooldemo/MainActivity;)Landroid/widget/EditText;
    // move-result-object vx 移动上一次方法调用的对象引用返回值到 vx
    // 将结果赋值 v0
    move-result-object v0
    // 调用 getText 方法 获取用户输入值
    invoke-virtual {v0}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
    // 赋值 v0
    move-result-object v0
    // 调用 toString 方法
    invoke-virtual {v0}, Ljava/lang/Object;->toString()Ljava/lang/String;
    // 赋值 v0
    move-result-object v0
    // 定义 String 类型的变量名为 userName 变量 
    .line 41
    .local v0, "userName":Ljava/lang/String;
    // iget-object-quick vx, vy, 偏移量 获取 vy 寄存器中实例指向+偏移位置的数据区的对象引用, 存入 vx
    // 将最终的结果传入 v1
    iget-object v1, p0, Lcom/hlq/apktooldemo/MainActivity$1;->this$0:Lcom/hlq/apktooldemo/MainActivity;

    invoke-static {v1}, Lcom/hlq/apktooldemo/MainActivity;->access$100(Lcom/hlq/apktooldemo/MainActivity;)Landroid/widget/EditText;

    move-result-object v1

    invoke-virtual {v1}, Landroid/widget/EditText;->getText()Landroid/text/Editable;

    move-result-object v1

    invoke-virtual {v1}, Ljava/lang/Object;->toString()Ljava/lang/String;
    // 将值重新赋给 v1
    move-result-object v1
    // 定义 String 类型的变量名为 userLicense 变量
    .line 42
    .local v1, "userLicense":Ljava/lang/String;
    invoke-static {v0}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z

    move-result v2

    const/4 v3, 0x0
    // if-nez vx, 目标 如果 vx != 0,跳转到目标 cond_2
    if-nez v2, :cond_2
    // 调用 isEmpty 判断当前是否为空
    invoke-static {v1}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z
    // move-result vx 移动上一次方法调用的返回值到vx
    move-result v2
    // if-eqz vx, 目标 如果 vx == 0 ,跳转到目标。 vx 是 int 型值
    // 如果当前等于空
    if-eqz v2, :cond_0
    // goto 目标 通过短偏移量注 2 无条件跳转到目标
    goto :goto_1

    .line 46
    :cond_0
    iget-object v2, p0, Lcom/hlq/apktooldemo/MainActivity$1;->this$0:Lcom/hlq/apktooldemo/MainActivity;

    invoke-static {v2, v0, v1}, Lcom/hlq/apktooldemo/MainActivity;->access$200(Lcom/hlq/apktooldemo/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z

    move-result v2

    // 如果等于空 跳转到 cond_1 否则 执行下面 startActivity 方法
    if-eqz v2, :cond_1

    .line 47
    iget-object v2, p0, Lcom/hlq/apktooldemo/MainActivity$1;->this$0:Lcom/hlq/apktooldemo/MainActivity;

    new-instance v3, Landroid/content/Intent;

    iget-object v4, p0, Lcom/hlq/apktooldemo/MainActivity$1;->this$0:Lcom/hlq/apktooldemo/MainActivity;

    const-class v5, Lcom/hlq/apktooldemo/SuccessActivity;

    invoke-direct {v3, v4, v5}, Landroid/content/Intent;-><init>(Landroid/content/Context;Ljava/lang/Class;)V

    invoke-virtual {v2, v3}, Lcom/hlq/apktooldemo/MainActivity;->startActivity(Landroid/content/Intent;)V

    goto :goto_0

    .line 49
    :cond_1
    // 提示异常信息
    iget-object v2, p0, Lcom/hlq/apktooldemo/MainActivity$1;->this$0:Lcom/hlq/apktooldemo/MainActivity;
    // 转化后结果为:当前校验码有误,请核实~!
    // 转化地址:http://tool.oschina.net/encode?type=3
    const-string v4, "\u5f53\u524d\u6821\u9a8c\u7801\u6709\u8bef\uff0c\u8bf7\u6838\u5b9e~\uff01"

    invoke-static {v2, v4, v3}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

    move-result-object v2

    invoke-virtual {v2}, Landroid/widget/Toast;->show()V

    // 直接执行 return
    .line 51
    :goto_0
    return-void
    .line 43
    :cond_2
    :goto_1
    iget-object v2, p0, Lcom/hlq/apktooldemo/MainActivity$1;->this$0:Lcom/hlq/apktooldemo/MainActivity;
    // 请输入正确的用户名以及校验码
    const-string v4, "\u8bf7\u8f93\u5165\u6b63\u786e\u7684\u7528\u6237\u540d\u4ee5\u53ca\u6821\u9a8c\u7801"
    // 调用 makeText 静态方法
    invoke-static {v2, v4, v3}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

    move-result-object v2

    invoke-virtual {v2}, Landroid/widget/Toast;->show()V

    .line 44
    return-void
.end method

由此推断出,转化为 Java 代码应该如下:

代码语言:javascript
复制
String userName = mUserNameID.getText().toString();
String userLicense = mUserLicenseID.getText().toString();
if (TextUtils.isEmpty(userName) || TextUtils.isEmpty(userLicense)) {
    Toast.makeText(MainActivity.this, "请输入正确的用户名以及校验码", Toast.LENGTH_SHORT).show();
    return;
}
if (checkData(userName, userLicense)) {
    startActivity(new Intent(MainActivity.this, SuccessActivity.class));
} else {
    Toast.makeText(MainActivity.this, "当前校验码有误,请核实~!", Toast.LENGTH_SHORT).show();
}

有些生涩,但是好歹翻译出来了。尴尬癌都犯了。。。生涩 ing。。。

修改 Smali 文件,使其达到我们预期效果

从文中提供 Demo 演示图,我们可以看出,当前的小程序主要功能便是,属于用户名以及校验码,验证通过进入欢迎页,否则提示错误异常。

那么,如何在我们不知道验证码的情况下,还能正常进入欢迎页面呢?

结合我们刚刚解析的 Smail 文件,以及转化后的 Java 文件,我们可以得知如下关键内容:

首先,Apk 会对用户输入进行一个非空校验,这个好办,我们随便输入点什么即可绕过;

而关键将通过 Base64 对用户输入进行校验合法性。

那么,我们可以不可以,将这块的逻辑给它逆转一下,比如,我们随便输入,使其程序校验成功,而我们真正录入正确的时候,则认为是失败的。嘿嘿嘿,有点坏哦~

说干就干,一起来修改 Smail 文件。

直接通过搜索定位到 startActivity 方法处:

那么,我们接下来只需要直接将 cond_1 前面的 if - eqz 修改为 if - nez 即可,如下所示:

代码语言:javascript
复制
if-nez v2, :cond_1

.line 47
iget-object v2, p0, Lcom/hlq/apktooldemo/MainActivity$1;->this$0:Lcom/hlq/apktooldemo/MainActivity;

new-instance v3, Landroid/content/Intent;

iget-object v4, p0, Lcom/hlq/apktooldemo/MainActivity$1;->this$0:Lcom/hlq/apktooldemo/MainActivity;

const-class v5, Lcom/hlq/apktooldemo/SuccessActivity;

invoke-direct {v3, v4, v5}, Landroid/content/Intent;-><init>(Landroid/content/Context;Ljava/lang/Class;)V

invoke-virtual {v2, v3}, Lcom/hlq/apktooldemo/MainActivity;->startActivity(Landroid/content/Intent;)V

goto :goto_0

这时候,再次回包:

签个名:

运行查看效果:

6 不 6?

本文到此结束~

大吉大利,晚上吃鸡~~~

欢迎各位老铁关注~不定期发布~见证你我的成长路~!!!

觉得不错,动动小手,转发让更多人看到,3Q,比心~

本文参与?腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-07-25,如有侵权请联系?cloudcommunity@tencent.com 删除

本文分享自 贺biubiu 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
验证码
腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com