Rytter 发表于 2024-2-20 00:13

Android逆向学习(七)绕过root检测与smali修改学习

# Android逆向学习(七)绕过root检测与smali修改学习

## 一、写在前面

这是吾爱破解正己大大教程的第五个作业,然后我的系统还是ubuntu, 这个是剩下作业的完成步骤。

## 二、任务目标

现在我们已经解决了一些问题,现在剩下的问题有

1. hash校验需要解决
2. 关于root检测的
3. 关于与smali学习的

### 1.解决hash校验的问题

老样子,我们先查看hash校验的代码,看一下这个代码的逻辑

直接把java代码给粘贴过来

```java
public final boolean check_Hash(Context context) {
      String apkPath = getApkPath((Context) this);
      FileInputStream fileInputStream = null;
      try {
            try {
                MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
                byte[] bArr = new byte;
                Ref.IntRef intRef = new Ref.IntRef();
                FileInputStream fileInputStream2 = new FileInputStream(new File(apkPath));
                while (true) {
                  try {
                        int read = fileInputStream2.read(bArr);
                        intRef.element = read;
                        if (read <= 0) {
                            break;
                        }
                        messageDigest.update(bArr, 0, intRef.element);
                  } catch (Exception e) {
                        e = e;
                        fileInputStream = fileInputStream2;
                        e.printStackTrace();
                        if (fileInputStream != null) {
                            try {
                              fileInputStream.close();
                            } catch (IOException e2) {
                              e2.printStackTrace();
                            }
                        }
                        return false;
                  } catch (Throwable th) {
                        th = th;
                        fileInputStream = fileInputStream2;
                        if (fileInputStream != null) {
                            try {
                              fileInputStream.close();
                            } catch (IOException e3) {
                              e3.printStackTrace();
                            }
                        }
                        throw th;
                  }
                }
                String bigInteger = new BigInteger(1, messageDigest.digest()).toString(16);
                Log.e("zj2595", "hash:" + bigInteger);
                boolean areEqual = Intrinsics.areEqual(bigInteger, this.ApkHash);
                try {
                  fileInputStream2.close();
                } catch (IOException e4) {
                  e4.printStackTrace();
                }
                return areEqual;
            } catch (Exception e5) {
                e = e5;
            }
      } catch (Throwable th2) {
            th = th2;
      }
    }

```

### 2.解决root检验的问题

这个一共有三个检测方法,用了一个或操作,然后我就简要总结一下这个三个方法

1. 检查有没有test-keys

    检查发布的系统版本是测试版(test-keys)还是发布版(release-keys),这个不一定,因为有的系统发布版就是test-keys,而且这个很少有人用

2. 检查相应目录下有没有su文件或者有没有superuser.apk这种只有root手机才有的信息

    这个方法经常使用,不过规避方法也很多

3. 使用which命令来检测是否存在su

    这个其实也挺简单的,如果用hook的话

实际上如果一个手机被root之后也是很难被发现的,因为如果root后就可以使用xposed或者frida这些工具进行hook,hook的功能是非常的强大的。

然后这个的解决方法也是很多,比如:

1. 直接修改smali代码,永远返回真(之前讲到过,不讲了)
2. hook这个check代码,永远返回真(之前也讲到过,不讲了)
3. 我想到的是使用frida做一个能够一劳永逸的hook代码

### 3.关于smali学习的题目



这里smali学习部分的作业,我们的目标就是通过修改smali代码,使vip和会员到期时间通过,并且修改钻石数量。

## 三、实现方法

### 问题一的实现

通过对代码的分析我们可以了解到。首先会读取apk的路径,然后计算出hash值,然后使用这个hash值和之前的hash值进行比对,就可以得到最终的正确的hash值。

这个的解决方法就太多了,我们就随便选择一个方式解决掉。

1. 比如frida直接hook这个函数让他永远返回true。
2. hook这个getApkPath((Context) this)的方法,然后修改这个路径到我们自己安装包的路径,不过在上一篇文章中这个方法有点问题(我怀疑是这个apk本身的bug)
3. 修改this.ApkHash,让这里hash值和实际计算的hash值一样。

这些方法在上一篇博客中已经介绍过了,所以这里不再赘述了,直接使用第一个方法给他hook了

```javascript
Java.perform(function () {
    Java.use("com.zj.wuaipojie.ui.ChallengeFifth").check_Hash.implementation = function (j) {
      var result = this.check_Hash(j);
      send(result);
      return true;
    }
})
```

这样就可以很轻松的通过(frida真厉害!)



### 问题二的实现

关于root的实现我们先看一下java代码(注:这个地方smali2java插件是有问题的,所以这一串代码我是通过jd-gui逆向得到的)

```java

public final boolean check_root() {
    return (checkRootMethod1() || checkRootMethod2() || checkRootMethod3());
}
public final boolean checkRootMethod1() {
    String str = Build.TAGS;
    boolean bool2 = false;
    boolean bool1 = bool2;
    if (str != null) {
      bool1 = bool2;
      if (StringsKt.contains$default(str, "test-keys", false, 2, null))
      bool1 = true;
    }
    return bool1;
}

public final boolean checkRootMethod2() {
    for (byte b = 0; b < 10; b++) {
      (new String) = "/system/app/Superuser.apk";
      (new String) = "/sbin/su";
      (new String) = "/system/bin/su";
      (new String) = "/system/xbin/su";
      (new String) = "/data/local/xbin/su";
      (new String) = "/data/local/bin/su";
      (new String) = "/system/sd/xbin/su";
      (new String) = "/system/bin/failsafe/su";
      (new String) = "/data/local/su";
      (new String) = "/su/bin/su";
      if ((new File((new String))).exists())
      return true;
    }
    return false;
}

public final boolean checkRootMethod3() {
    boolean bool1 = false;
    boolean bool2 = false;
    boolean bool3 = false;
    Process process = null;
    try {
      Process process1 = Runtime.getRuntime().exec(new String[] { "/system/xbin/which", "su" });
      process = process1;
      BufferedReader bufferedReader = new BufferedReader();
      process = process1;
      InputStreamReader inputStreamReader = new InputStreamReader();
      process = process1;
      this(process1.getInputStream());
      process = process1;
      this(inputStreamReader);
      process = process1;
      String str = bufferedReader.readLine();
      bool1 = bool3;
      if (str != null)
      bool1 = true;
      bool2 = bool1;
      if (process1 != null) {
      process = process1;
      } else {
      return bool2;
      }
      process.destroy();
    } finally {
      Exception exception = null;
    }
    return bool2;
}
```

我们一共发现了三个检测的方法,这个在前面的分析已经讲过了,然后我们启动一下frida



之后我们写一个frida脚本然后运行一下hook,这个hook的主要目的就是把checkroot2里面的查找跟root有关文件的东西给屏蔽掉,就是如果一个手机是已经root了的,那么就会产生su等文件,我们就是通过frida hook避免这个程序发现这些文件,然后我之前以为camodroid比较全面可以完成这些东西,但是实际运行发现camodroid并不能完全屏蔽掉这些,所以我自己写了一个(在camodroid上改的)。

```javascript
Java.perform(function () {
    Java.use('java.io.File')['$init'].overload('java.lang.String').implementation = function (pathString) {

      let map =
      {
            // Rootbeer
            "/system/xbin/busybox": "/system/xbin/busybo",
            "/system/bin/su": "/system/bin/s",
            "/system/xbin/su": "/system/bin/s",
            "/system/app/Superuser.apk":"1",
            "/sbin/su":"1",
            "/data/local/xbin/su":"1",
            "/data/local/bin/su":"1",
            "/system/sd/xbin/su":"1",
            "/system/bin/failsafe/su":"1",
            "/data/local/su":"1",
            "/su/bin/su":"1",
      };
      var retval;
      if (map != null) {
            retval = this.$init("/system/bin/suasaadfghdfgh");
      }
      else {
            retval = this.$init(pathString);
      }
      return retval;
    };
   Java.use("com.zj.wuaipojie.ui.ChallengeFifth").checkRootMethod1.implementation = function () {
      var result = this.checkRootMethod1();
      send(result);
      send("check1")
      return result;
    }
    Java.use("com.zj.wuaipojie.ui.ChallengeFifth").checkRootMethod2.implementation = function () {
      var result = this.checkRootMethod2();
      send(result);
      send("check2")
      return result;
    }
    Java.use("com.zj.wuaipojie.ui.ChallengeFifth").checkRootMethod3.implementation = function () {
      var result = this.checkRootMethod3();
      send(result);
      send("check3")
      return result;
    }
})
```

结果如下图片



然后有个问题就是check3方法,这个方法本来就有问题



就是正常情况下就不应该使用只使用/system/xbin/which去检测是否有su文件,这个算是吾爱作业软件的一个小bug吧

### 问题三的实现

这个界面在smalilearn里面,我们直接smali2java插件给转了,这个代码不长,所以就直接粘贴到这里了

```java
public final class SmaliLearn extends AppCompatActivity {
    private final int vip_coin;

    public final int isVip() {
      return 0;
    }

    public final long vipEndTime() {
      return 1671889481513L;
    }

    public final int getVip_coin() {
      return this.vip_coin;
    }

    protected void onCreate(Bundle bundle) {
      super.onCreate(bundle);
      setContentView(2131427364);
      ((Button) findViewById(2131230819)).setOnClickListener(new SmaliLearn$.ExternalSyntheticLambda0(this, (TextView) findViewById(2131231219), (TextView) findViewById(2131231221), (TextView) findViewById(2131231220)));
    }

    /* JADX INFO: Access modifiers changed from: private */
    /* renamed from: onCreate$lambda-0reason: not valid java name */
    public static final void m1onCreate$lambda0(SmaliLearn smaliLearn, TextView textView, TextView textView2, TextView textView3, View view) {
      int isVip = smaliLearn.isVip();
      if (isVip == 0) {
            textView.setText("非会员");
      } else if (isVip == 1) {
            textView.setText("会员");
      } else if (isVip == 4) {
            textView.setText("大会员");
      } else if (isVip == 16) {
            textView.setText("超级会员");
      } else if (isVip == 99) {
            textView.setText("至尊会员");
      }
      long time = new Date().getTime();
      SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
      if (smaliLearn.vipEndTime() < time) {
            textView2.setText("已过期");
      } else {
            textView2.setText(simpleDateFormat.format(Long.valueOf(smaliLearn.vipEndTime())));
      }
      int i = smaliLearn.vip_coin;
      if (i != 0) {
            textView3.setText(String.valueOf(i));
      }
    }
}
```

这个修改的部分有

原来的isVIp那个const/16 v0,0x63,就是把原来的v0修改成了99,关于smali这个东西,我是学过微机原理,对MIPS,X64等汇编语言有较多的使用经验,我建议如果你真的想学smali的话可以先去学学这些汇编,不然很难上手smali



这个是修改了时间,我就把第一位修改成了9



这个就是把调用返回值换成了直接进行赋值不调用





然后下面就是我们的运行结果,完结撒花!!!


DreamerPeas 发表于 2024-2-20 01:06

PastYiHJ 发表于 2024-2-20 04:48

感谢大佬的教程

ioyr5995 发表于 2024-2-20 06:52

跟着大佬学习是捷径,谢谢分享

xyer8 发表于 2024-2-20 08:12

感谢分享

wk238 发表于 2024-2-20 08:33

学习一下,看能不能逆向一个古老的游戏软件

guopeng1986216 发表于 2024-2-20 09:23

跟着大佬学习是捷径

debug_cat 发表于 2024-2-20 09:27

感谢分享,过程很详细,其实每个文章带上样本就更好了

DominickFu 发表于 2024-2-20 09:32

有样本吗。想在虚拟机里面自己试一下

jrwapj 发表于 2024-2-20 13:02

感谢楼主分享,内容挺详细的
页: [1] 2 3
查看完整版本: Android逆向学习(七)绕过root检测与smali修改学习