六层锁机
六层锁机和一个注册机制绕过,这个六层锁机之前分析过有些关卡没过,这里重新分析一遍
环境
Android 13.0
frida 16.1.8
登陆
用jadx打开apk
登陆页面的逻辑是把拿到的username放入
a
中,计算出来的结果需要相同a
是HmacSHA256加密函数密码是通过
a
传入username计算出来的。所以可以通过获取a
的返回值来拿到有三种方式可以获取:
提取出来算法,计算出返回值
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(); } }
使用objection来hook
a
函数拿到返回值android hooking search classes Activity
写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,发送一个消息,设置what
为2001
,表示成功 - 所以,接下来可以考虑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 进行许可。
评论