正己 发表于 2022-11-18 10:54

《安卓逆向这档事》第五节课后小作业贴

本帖最后由 正己 于 2022-12-29 10:41 编辑

# 一.目录
---
[《安卓逆向这档事》一、模拟器环境搭建](https://www.52pojie.cn/thread-1695141-1-1.html)
[《安卓逆向这档事》二、初识APK文件结构、双开、汉化、基础修改](https://www.52pojie.cn/thread-1695796-1-1.html)
[《安卓逆向这档事》三、初识smail,vip终结者](https://www.52pojie.cn/thread-1701353-1-1.html)
[《安卓逆向这档事》四、恭喜你获得广告&弹窗静默卡](https://www.52pojie.cn/thread-1706691-1-1.html)
[《安卓逆向这档事》五、1000-7=?&动态调试&Log插桩](https://www.52pojie.cn/thread-1714727-2-1.html)

# 二.前言
---
为了提高大家的积极性,特意开了这个作业贴。
编程这件事,一定要做好笔记,并且关掉视频自己动手实操一遍,这样效果最佳!!!
**帖子我设置只有我可见,等一周后取消,切记不要回复和作业无关内容,否则会被扣分,前50个名交作业的同学有奖励。**

jjjwwy 发表于 2022-11-19 01:01

交作业啦方法一:

第一步:定位提示关键字“无效用户名”,并搜索,定位到OnClick,发现调用checkSN方法做判断。

第二步:双击进入checkSN方法,在return处下断

第三步:进入调试模式,跟踪寄存器V5的值,得到SN码,注册完成

方法二:
直接在return处Log插桩打印

dx52mm 发表于 2023-2-4 21:06

正己 发表于 2023-2-4 20:56
这个注册的字符串其实是写在arsc里的string,他有一个对应的id,jeb在反编译的时候会工具这个id自动生成 ...

尝试了下:
1)在arsc中搜索字符串“注册”,出来一条,点击进去发现“successed”表示“恭喜您!注册成功”;
2)再返回搜索“successed”,出来两条,其中string是刚才的,点击type-info进去,发现“0x7f05000c”表示“successed”,中间转了一次。
3)最后再回到classes.dex中搜索代码“0x7f05000c”,可以定位到关键判断。
之前对于这一个知识学的不仔细,一直不明白,终于搞懂了,谢谢正己大神。

wgd0ay 发表于 2022-12-1 15:04

方法三:Log插桩
1、安装完作业apk,然后进入classes.dex文件内,搜索0x7F05000B定位到代码

2、找到checkSN方法,长按跳转进方法,简单分析一下smali代码
.method private checkSN(Ljava/lang/String;Ljava/lang/String;)Z
    .registers 13
    .param p1, "userName"# Ljava/lang/String; 参数寄存器 p1 保存的是用户名 userName
    .param p2, "sn"# Ljava/lang/String; 参数寄存器 p2 保存的是注册码 sn

.prologue
    const/4 v7, 0x0# 将 0x0 存入寄存器 v7

      .line 45
      if-eqz p1, :cond_9# 如果 p1,即 userName 等于 0,跳转到 cond_9

    :try_start_3
      invoke-virtual {p1}, Ljava/lang/String;->length()I# 调用 userName.length()

      move-result v8   # 将 userName.length() 的执行结果存入寄存器 v8

      if-nez v8, :cond_a# 如果 v8 不等于 0,跳转到 cond_a

      .line 69
      :cond_9
    :goto_9
      return v7

            .line 47
            :cond_a
            if-eqz p2, :cond_9 # 如果 p2,即注册码 sn 等于 0,跳转到 cond_9

            invoke-virtual {p2}, Ljava/lang/String;->length()I # 执行 sn.length()

            move-result v8# 将 sn.length() 执行结果存入寄存器 v8

            const/16 v9, 0x10# 将 0x10 存入寄存器 v9

                if-ne v8, v9, :cond_9 # 如果 sn.length != 0x10 ,跳转至 cond_9

                  .line 49
                  const-string v8, "MD5"# 将字符串 "MD5" 存入寄存器 v8
                       
                                    # 调用静态方法 MessageDigest.getInstance("MD5")
                  invoke-static {v8}, Ljava/security/MessageDigest;->getInstance(Ljava/lang/String;)Ljava/security/MessageDigest;

move-result-object v1 # 将上一步方法的返回结果赋给寄存器 v1,这里是 MessageDigest 对象

    .line 50
    .local v1, "digest":Ljava/security/MessageDigest;
invoke-virtual {v1}, Ljava/security/MessageDigest;->reset()V # 调用 digest.reset() 方法

    .line 51
    invoke-virtual {p1}, Ljava/lang/String;->getBytes()[B# 调用 userName.getByte() 方法

      move-result-object v8   # 上一步得到的字节数组存入 v8

      invoke-virtual {v1, v8}, Ljava/security/MessageDigest;->update() 方法

      .line 52
      invoke-virtual {v1}, Ljava/security/MessageDigest;->digest()[B# 调用 digest.digest() 方法

      move-result-object v0   # 上一步的执行结果存入 v0,是一个 byte[] 对象

      .line 53
      .local v0, "bytes":[B
      const-string v8, ""# 将字符串 "" 存入 v8
   
            # 调用 MainActivity 中的 toHexString(byte[] b,String s) 方法
      invoke-static {v0, v8}, Lcom/droider/crackme0201/MainActivity;->toHexString([BLjava/lang/String;)Ljava/lang/String;

move-result-object v3 # 上一步方法返回的字符串存入 v3

    .line 54
    .local v3, "hexstr":Ljava/lang/String;
new-instance v5, Ljava/lang/StringBuilder; # 新建 StringBuilder 对象


3、分析在第117行可知v6为最终真实的的sn注册码,只需在后面添加如下代码并保存
invoke-static {v6}, Lcom/mtools/LogUtils;->v(Ljava/lang/Object;)V

4、然后将log插桩dex文件放入包内并改名为classes2.dex,并打包重新安装

5、LSPosed 选中程序,算法助手开启应用总开关、Log捕获不在赘述
6、运行后随便输入用户名和注册码,翻看日志,可以看到用户名对应正确的注册码

7、进行验证,注册成功

momingloG 发表于 2022-12-10 19:52

因为最近还在学习xposed,所以就尝试写了个模块,嘿嘿

功能:输入用户名后直接点击“注册”就可以注册成功啦

```
    /**
   * 破解题目
   */
    class HookCrackMe {
      /**
         * 计算注册码
         *
         * @Param userName 用户名
         * @Return 注册码
         */
      public String calculateSn(String userName) {
            MessageDigest digest = null;
            try {
                digest = MessageDigest.getInstance("MD5");
                digest.reset();
                digest.update(userName.getBytes());
                byte[] bytes = digest.digest();

                StringBuilder hexString = new StringBuilder();
                for (byte b : bytes) {
                  String hex = Integer.toHexString(b & 255);
                  if (hex.length() == 1) {
                        hexString.append('0');
                  }
                  hexString.append(hex).append("");
                }

                String hexstr = hexString.toString();

                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < hexstr.length(); i += 2) {
                  sb.append(hexstr.charAt(i));
                }
                return sb.toString();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
            return null;

      }

      public void hookCrackMe() {
            XposedHelpers.findAndHookMethod("com.droider.crackme0201.MainActivity", lpparam.classLoader, "onClick", android.view.View.class, new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                  // 获取“用户名”控件
                  EditText edit_userName = (EditText) XposedHelpers.getObjectField(param.thisObject, "edit_userName");
                  // 获取用户输入的用户名
                  String userName = edit_userName.getText().toString();
                  // 判断用户名是否为空
                  if (!"".equals(userName)) {
                        // 获取“注册码”控件
                        EditText edit_sn = (EditText) XposedHelpers.getObjectField(param.thisObject, "edit_sn");
                        // 计算注册码
                        String sn = calculateSn(userName);
                        if (sn != null) {
                            // 设置注册码
                            edit_sn.setText(sn);
                        }
                  }
                }

                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                  super.afterHookedMethod(param);
                  Button btn_register = (Button) XposedHelpers.getObjectField(param.thisObject, "btn_register");
                  btn_register.setEnabled(true);
                  btn_register.setText("已破解");
                }


            });
      }
    }
```

Tonyha7 发表于 2022-11-18 14:45

{:301_997:}

苏紫方璇 发表于 2022-11-18 15:27

用户名:苏紫方璇
注册码:bf68a45158fb4f77

算法取用户名MD5的奇数位
不知道为啥,我这jeb估计有问题,用的mumu模拟器和手机真机都连不上,直接看代码猜吧

苏紫方璇 发表于 2022-11-18 15:48

就这个样子,adb是连接上了,jdk和sdk应该也没问题,有装as可以编译安卓程序

yuhan694 发表于 2022-11-19 00:10

用户名取MD5的奇数位

huashengyue 发表于 2022-11-19 12:25

1)如果不看视频介绍,首先无从下手,其次是不知道关键字,看不懂代码。
2)到了调试阶段,从WINDOWS的cmd窗口执行ADB命令,出现错误提示如下:

正己 发表于 2022-11-19 13:15

huashengyue 发表于 2022-11-19 12:25
1)如果不看视频介绍,首先无从下手,其次是不知道关键字,看不懂代码。
2)到了调试阶段,从WINDOWS的cmd ...

你的这个安装包有问题,装一下第四课的,你可以替换了activity,adb找不到这个activity

AngMer 发表于 2022-11-19 14:27

cx2018 发表于 2022-11-19 15:17


有点疑惑为啥v6显示不出来,不知道问题在哪,无奈只能用显示出的值来搞,可以看到v3是显示出来的32位可能是md5。
smail转Java分析checksn方法

看代码的意思大概是用用户名生成md5,然后根据32位md5间隔的取数字(取index为0,2,4...的数)作为密钥与输入的sn比较输出结果。那如果图1中v3显示的为md5的话直接人工间隔取出16位数就好了(有点笨QAQ)。试试,这样不知道算不算成功
页: [1] 2 3 4 5
查看完整版本: 《安卓逆向这档事》第五节课后小作业贴