六层锁机

xiaoeryu Lv5

六层锁机和一个注册机制绕过,这个六层锁机之前分析过有些关卡没过,这里重新分析一遍

环境

Android 13.0

frida 16.1.8

登陆

用jadx打开apk

  • 登陆页面的逻辑是把拿到的username放入a中,计算出来的结果需要相同

  • aHmacSHA256加密函数

  • 密码是通过a传入username计算出来的。所以可以通过获取a的返回值来拿到

    有三种方式可以获取:

    1. 提取出来算法,计算出返回值

      import javax.crypto.Mac;
      import javax.crypto.spec.SecretKeySpec;
      import java.nio.charset.StandardCharsets;
      
      public class PasswordGenerator {
          public static void main(String[] args) {
              String username = "xiaoeryu"; // 用户名
      
              String password = generatePassword(username, username);
              System.out.println("Generated Password: " + password);
          }
      
          private static String generatePassword(String str, String str2) {
              try {
                  SecretKeySpec secretKeySpec = new SecretKeySpec(str2.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
                  Mac mac = Mac.getInstance("HmacSHA256");
                  mac.init(secretKeySpec);
                  return bytesToHex(mac.doFinal(str.getBytes(StandardCharsets.UTF_8)));
              } catch (Exception e) {
                  e.printStackTrace();
                  return "";
              }
          }
      
          private static String bytesToHex(byte[] bytes) {
              StringBuilder sb = new StringBuilder();
              for (byte b : bytes) {
                  String hex = Integer.toHexString(0xff & b);
                  if (hex.length() == 1)
                      sb.append('0');
                  sb.append(hex);
              }
              return sb.toString();
          }
      }
      
    2. 使用objection来hook a函数拿到返回值

      android hooking search classes Activity

    3. 写frida hook脚本来获取返回值:hook起来比较简单就是一个静态函数

      Java.perform(function() {
          var LoginActivity = Java.use('com.example.androiddemo.Activity.LoginActivity');
         
          LoginActivity.a.overload('java.lang.String', 'java.lang.String').implementation = function(arg1, arg2) {
              var result = this.a(arg1, arg2);
              console.log('Arguments: ' + arg1 + ', ' + arg2);
              console.log('Result: ' + result);
              return result;
          };
      });
      

第一关

登录后的第一关

  • 这一关可以不需要关心算法细节,只需要hook a函数,让它返回R4jSLLLLLLLLLLOrLE7/5B+Z6fsl65yj6BgC6YWz66gO6g2t65Pk6a+P65NK44NNROl0wNOLLLL=即可

hook a方法修改返回值:依旧是一个静态函数

Java.perform(function () {
    try {
        var FridaActivity1 = Java.use("com.example.androiddemo.Activity.FridaActivity1");

        // 确认类是否成功加载
        if (FridaActivity1) {
            console.log("Successfully loaded FridaActivity1 class.");
        }

        // Hook the method with byte array as the parameter
        FridaActivity1.a.overload('[B').implementation = function (byteArray) {
            console.log("Hooked a(byte[]) method in FridaActivity1. Returning pre-defined value.");
            return "R4jSLLLLLLLLLLOrLE7/5B+Z6fsl65yj6BgC6YWz66gO6g2t65Pk6a+P65NK44NNROl0wNOLLLL=";
        };
    } catch (e) {
        console.log("Exception: " + e.message);
    }
});

第二关

  • 第二关只要让两个变量都为true就可以了

    static_bool_var:是一个静态变量直接设置就可以

    bool_var:是一个实例变量,需要查找类下的所有实例变量然后再设置

Java.perform(function () {
    function Activity2() {
        var FridaActivity2 = Java.use("com.example.androiddemo.Activity.FridaActivity2");
        // 直接设置静态变量
        FridaActivity2.static_bool_var.value = true;
        // 查找活动实例并设置实例变量
        Java.choose("com.example.androiddemo.Activity.FridaActivity2", {
            onMatch: function (ins) {
                // 直接设置实例变量
                ins.bool_var.value = true;
                console.log("Instance found and bool_var set to true");
            },
            onComplete: function () {
                console.log("Java.choose completed");
            }
        });
    }
    Activity2()
});

第三关

第三层锁机在第二层的基础上有一些改变,如下图

  • 这里跟之前两关差不多,区别在于有一个变量名和函数名相同
Java.perform(function () {
    function Activity3() {
        var FridaActivity3 = Java.use("com.example.androiddemo.Activity.FridaActivity3");
        var BooleanClass = Java.use("java.lang.Boolean");
        // 直接设置静态变量
        FridaActivity3.static_bool_var.value = true;
        // 查找活动实例并设置实例变量
        Java.choose("com.example.androiddemo.Activity.FridaActivity3", {
            onMatch: function (ins) {
                // 直接设置实例变量
                ins.bool_var.value = true;
                // 需要在重名的变量前加一个下划线
                ins._same_name_bool_var.value = true;

                console.log("Instance found and bool_var & same_name_bool_var set to true");
            },
            onComplete: function () {
                console.log("Java.choose completed");
            }
        });
    }

    Activity3()
});

第四关

  • 这里的主要问题是要修改内部类中方法的返回值

有两种方案可以解决

第一种:依次进行hook

第二种:通过反射拿到内部类的所有方法,然后hook并返回true

Java.perform(function () {
    // function forth() {
    //     var InnerClasses = Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses");

    //     // 将 check1 到 check6 方法的返回值修改为 true
    //     InnerClasses.check1.implementation = function () {
    //         console.log("check1 called");
    //         return true;
    //     };

    //     InnerClasses.check2.implementation = function () {
    //         console.log("check2 called");
    //         return true;
    //     };

    //     InnerClasses.check3.implementation = function () {
    //         console.log("check3 called");
    //         return true;
    //     };

    //     InnerClasses.check4.implementation = function () {
    //         console.log("check4 called");
    //         return true;
    //     };

    //     InnerClasses.check5.implementation = function () {
    //         console.log("check5 called");
    //         return true;
    //     };

    //     InnerClasses.check6.implementation = function () {
    //         console.log("check6 called");
    //         return true;
    //     };

    //     console.log("All check methods modified to return true");
    // }
    function forth2() {
        var class_name = "com.example.androiddemo.Activity.FridaActivity4$InnerClasses";
        var InnerClass = Java.use(class_name);
        var all_methods = InnerClass.class.getDeclaredMethods();
        //console.log(all_methods);
        for (var i = 0; i < all_methods.length; i++) {
            var method = all_methods[i];
            console.log(method);
            var substring = method.toString().substr(method.toString().indexOf(class_name) + class_name.length + 1);
            //console.log(substring);
            var finalMethodString = substring.substr(0, substring.indexOf("("));
            console.log(finalMethodString);
            InnerClass[finalMethodString].implementation = function () { return true };
        }
    }

    // forth2()
    forth2()
});

第五关

  • 继续跟进getDynamicDexCheck 函数中
  • 继续跟进loaddex函数中
  • 此处动态加载Dex是用DexClassLoader加载的
Java.perform(function(){
    function fifth(){
        // var methods = Java.enumerateMethods("*!check")
        // console.log(JSON.stringify(methods,null,2))
        Java.enumerateClassLoaders({
            onMatch: function (loader) {
                try {
                    if (loader.findClass("com.example.androiddemo.Dynamic.DynamicCheck")) {
        
                        Java.classFactory.loader = loader;
        
                    }
                }
                catch (error) {
                    console.log(" continuing :" + error)
                }
            },
            onComplete: function () {
                console.log("EnumerateClassloader END")
            }
        })
        Java.use("com.example.androiddemo.Dynamic.DynamicCheck").check.implementation = function(){
            return true;
        }
    }
    fifth()
})

第六关

这一关比较简单:直接hook这几个类中的check方法,修改返回值即可

Java.perform(function () {
    function sixth() {
        var targetClasses = [
            'com.example.androiddemo.Activity.Frida6.Frida6Class0',
            'com.example.androiddemo.Activity.Frida6.Frida6Class1',
            'com.example.androiddemo.Activity.Frida6.Frida6Class2'
        ]
        targetClasses.forEach(function (className) {
            var targetClasses = Java.use(className)
            targetClasses.check.implementation = function () {
                console.log(className + "check called")
                return true
            }
        })
    }
    sixth()
})

注册机绕过

该App程序使用了机器码验证,想办法绕过机器码验证,以便正常使用该App功能。绕过后,随意输入账号密码,将获得flag

用jadx打开apk在登陆页面定位到登陆按钮点击事件

  • 这里通过ID判断是否是登陆按钮,进入之后开始处理mWaittimer的逻辑
  • 创建一个新的Timer对象,并创建一个MyWaitTimerTask对象

跟进去看一下

  • 第一块框出来的这块代码可以看到对URL和获取到的一些设备信息进行了校验
  • 框出来的第二块是校验返回的结果如果是OK,则设置netcom.m_check为1,发送一个消息,设置what2001,表示成功
  • 所以,接下来可以考虑hook CheckUrl函数让其返回OK

这个hook起来比较简单,就是一个静态函数

function main(){
    Java.perform(function(){
        var netcom = Java.use("com.netcom.netcom")
        netcom["CheckUrl"].implementation = function(url){
            return Java.use("java.lang.String").$new("OK")
        }
    })
}
setImmediate(main)

执行脚本后,输入或者不输入内容都会显示flag

  • 标题: 六层锁机
  • 作者: xiaoeryu
  • 创建于 : 2024-07-29 10:41:32
  • 更新于 : 2024-07-29 10:57:42
  • 链接: https://github.com/xiaoeryu/2024/07/29/六层锁机/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论