进入某款罗盘 App 后,点击打马赛克的位置
会跳转到网站页面
如今想实现的功能是,不想让广告网页随着点击触发。
搞破解一定要站在开发的角度去考虑问题,如果开发一个跳转网站的程序,肯定就需要意图 intent。
例如,写一个跳转到打电话的页面
package com.bmstd.createActivity;import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bt_call = findViewById(R.id.bt_call);
bt_call.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + 12345));
startActivity(intent);
}
});
}
}
要想实现页面的跳转,一定是要有一个按钮点击的,然后通过 new Intent,并 setAction 和 setData ,最后 startActivity(intent) 才可以实现跳转。
也就是说,只要最后一步 startActivity 这个函数不执行,那么就一定可以实现永不跳转到网页页面。
由上面的开发得知,此 App 一定使用了 android.content.Intent 类。
通过 objection 搜索一下这个类。
执行命令
android hooking search classes android.content.Intent
从结果得知,确实存在这个类。
于是对这个类进行全面 hook ,把里面的所有方法都 hook 到
hook 后再点击跳转处
此时就触发了调用
从调用中看出,果然调用了 setData 方法,所以和开发是一致的,此时只要 hook 住 startActivity 函数,并且不执行函数体,就算是成功了。
下一步就是找到 startActivity 的所在类,通过 api 查找发现所在类就是 android.content.Context
但是这个 android.content.Context 这个类太大了,无法 hook 住他的方法。
所以只能找起子类,于是定位到了,他的一个间接子类 android.app.Activity
编写 Js 代码,对 android.app.Activity.startActivity 方法进行 hook ,hook 住后,不执行里面的方法,即可完成不跳转。
function hook_intent() {
Java.perform(function () {
var Activity = Java.use("android.app.Activity");
Activity.startActivity.overload('android.content.Intent').implementation = function (intent) { };
})
}
setImmediate(hook_intent)
此时执行 js 代码后,无论怎样点击,都不会发生跳转。
进入某款八字 App 后,会弹出"本软件需要购买注册码后方可使用"。从下图得知,这是通过机器码生成的注册码,也就是一机一码的形式。
随意输入一串字符串,肯定是错误的。于是会弹出,注册码校验失败,请重新输入注册码。
试试闯大运,在静态反编译中找,是否有以上字符串。结果在 RegistMachine 成功找到。
通过上述的代码中,可知输入的字符串长度大于 6 ,就会直接提示"注册码校验失败,请重新输入注册码"。只要输入的注册码长度小于等于 6,就会进入第二个 if 语句中,只要第二个 if 语句成立,就会提示 "注册码校验成功,您可以无限制使用本软件了"。
所以把目光集中在
if (RegistMachine.getFingerPrint(editText.getText().toString(), str2).equals(trim))
editText.getText().toString() 就是第一个输入框的,也就是 App 本身提供给用户的
那么 str2 是什么呢?
通过源码追溯,找到 str2 是静态函数 showRegister 的参数。
再继续向上找,是谁调用了函数 showRegister 。发现是静态 show 方法。
再继续向上找,是谁调用了静态函数 show。找到了是 MainActivity 的 onCreate 方法调用了 show 。
于是定位出 str2 是固定的,str2 就是 a8ycuxpbz2020。
又要把目光聚焦到,刚刚的判断
if (RegistMachine.getFingerPrint(editText.getText().toString(), str2).equals(trim))
如今 editText.getText().toString(), str2 已经全部确定了,不确定的就是 RegistMachine.getFingerPrint 函数的执行结果,也就是说,只要主动调用一下 RegistMachine.getFingerPrint 函数,这个函数的返回值就是注册码!
编写 js 代码
function pop_up_registration_code() {
Java.perform(function () {
let RegistMachine = Java.use("com.xxxxx.xxxxxxxxxxxxxxx.RegistMachine");
var result_code = RegistMachine.getFingerPrint("1c470c", "a8ycuxpbz2020")
console.log("result_code => ", result_code)
})
}setImmediate(pop_up_registration_code)
执行后,就会自吐注册码了
将输入的注册码输入,就会提示注册码校验成功
同样 App 需要根本识别码,然后生成注册码。
同样根据关键字搜索,搜索到点击的类,并定位到逻辑代码。
最关键的就是逻辑判断
if (!C0424cd.m29g() || !C0424cd.m28h() || !C0424cd.m27i() || !C0424cd.m26j() || !C0424cd.m25k())
所以只要 cd.g() ,cd.h() ,cd.i(),cd.j(),cd.k() 这 5 个函数都返回 true ,那么就可以忽略掉算法细节,输入任何字符串都会注册成功。
直接编写 js 代码修改内部逻辑
function registration_success() {
Java.perform(function () {
var cd = Java.use("com.android.xxxx.xxxxxxx.cd");
cd.g.implementation = function () {
return true;
}; cd.h.implementation = function () {
return true;
};
cd.i.implementation = function () {
return true;
};
cd.j.implementation = function () {
return true;
};
cd.k.implementation = function () {
return true;
};
})
}
setImmediate(registration_success)
这时无论输入什么字符串,都会成功注册。
转载:https://forum.butian.net/share/2216
作者:bmstd
点击下方小卡片或扫描下方二维码观看更多技术文章
师傅们点赞、转发、在看就是最大的支持