记一次复现实战去付费-虚拟定位App孙悟空

Fisherman / 2024-10-23 / 原文

这个APP需要通过直接去输入卡号,通过卡号付费实现虚拟定位的操作,同时虚拟定位的功能是在java层的静态代码块中实现的,所以直接去HOOK掉对应的检测卡密的过程就可以了

分析:

JEB实现反编译过程,通过搜索字符串('登录'),发现可以直接去查看到对应的函数位置

            public void run() {
                if(local.mCount != null && local.syms != null) {
                    local.mCount = (long)(((long)local.mCount) + 1L);
                    if(((long)local.mCount) > ((long)local.syms)) {
                        if(!"dqgb".equals("dqgb")) {
                            return;
                        }
                        System.exit(0);
                        return;
                    }
                    local.djsdx.postDelayed(this, 1000L);
                    return;
                }
                local.djsdx.postDelayed(this, 1000L);
            }
        };
        local.zx = new Runnable() {
            @Override
            public void run() {
                local.message = new Message();
                local.message.arg1 = 4;
                local.strpost("name=apk_z&c1=10002&c2=&c4=&c6=1");
                local.djsdx.postDelayed(this, ((long)(((int)local.xhjcfzs) * 60000)));
            }
        };

这里去复写了两个run()的执行函数,同时会去设置事件的同时,提交字符串

local.strpost("name=apk_z&c1=10002&c2=&c4=&c6=1");

这里是对应的strpost的函数实现,可以看到这里是有远程服务的网址的,其实可以考虑到是输入卡密的过程中,通过远程去实现卡密的检测,同时将这里的远程网址进行提交了之后会直接去判断,提交的网址进行local.load

!

这里load的代码我直接就copy到上面

public static void load(Context arg16) {
        local.context = arg16;
        local.jqm = local.getjqm();
        local.uuid = local.getuuid();
        local.ycuuid = local.getini("ycuuid.txt");
        Long v0 = (long)System.currentTimeMillis();
        AlertDialog v1 = local.dialog;
        if(v1 != null) {
            v1.dismiss();
            local.dialog = null;
        }
        AlertDialog.Builder v1_1 = new AlertDialog.Builder(local.context);
        v1_1.setTitle(local.title);
        v1_1.setCancelable(false);
        local.user = local.getini("card.txt");
        EditText v4 = new EditText(local.context);
        local.editText = v4;
        v4.setText(local.user);
        local.editText.setHint("请输入卡号...");
        v1_1.setView(local.editText);
        v1_1.setNegativeButton("解绑", null);
        v1_1.setPositiveButton("登录", null);
        v1_1.setNeutralButton("试用", null);
        AlertDialog v2 = v1_1.create();
        local.dialog = v2;
        v2.setCanceledOnTouchOutside(false);
        Long v2_1 = null;
        String v4_1 = local.getini("logintime.txt");
        if(!v4_1.isEmpty()) {
            v2_1 = (long)(((long)v0) - ((long)(((long)Long.parseLong(v4_1)))));
        }
        if(!local.jcdlzt().booleanValue()) {
            if(local.user.length() > 5) {
                Message v3 = new Message();
                local.message = v3;
                v3.arg1 = 1;
                local.message.arg2 = 2;
                local.strpost("name=apk_login&c1=" + local.rjbh + "&c2=" + local.user + "&c4=" + local.bbh + "&c5=" + local.jqm);
                return;
            }
            local.xsdlck();
            local.handler.obtainMessage();
            Message v6 = new Message();
            local.message = v6;
            v6.arg1 = 0;
            local.strpost("name=apk_qr&c1=" + local.rjbh + "&c2=");
            return;
        }
        if(((long)v2_1) > ((long)(((int)local.xhjcfzs) * 60 * 1000))) {
            Message v3_1 = new Message();
            local.message = v3_1;
            v3_1.arg1 = 1;
            local.message.arg2 = 2;
            local.strpost("name=apk_login&c1=" + local.rjbh + "&c2=" + local.user + "&c4=" + local.bbh + "&c5=" + local.jqm);
        }
    }
    // Detected as a lambda impl.
    public static void openBrowser(Context arg4, String arg5) {
        try {
            Intent v0_1 = new Intent();
            v0_1.setAction("android.intent.action.VIEW");
            v0_1.setData(Uri.parse(arg5));
            if(v0_1.resolveActivity(arg4.getPackageManager()) != null) {
                v0_1.resolveActivity(arg4.getPackageManager());
                arg4.startActivity(Intent.createChooser(v0_1, "请选择浏览器"));
                return;
            }
            arg4.startActivity(v0_1);
        }
        catch(Exception v0) {
            Toast.makeText(arg4.getApplicationContext(), "打开浏览器异常:" + v0.toString(), 0).show();
        }
    }

我们可以找到这里的对应的创建对话提示框的位置(输入卡号的位置)

        AlertDialog.Builder v1_1 = new AlertDialog.Builder(local.context);
        v1_1.setTitle(local.title);
        v1_1.setCancelable(false);
        local.user = local.getini("card.txt");
        EditText v4 = new EditText(local.context);
        local.editText = v4;
        v4.setText(local.user);
        local.editText.setHint("请输入卡号...");
        v1_1.setView(local.editText);
        v1_1.setNegativeButton("解绑", null);
        v1_1.setPositiveButton("登录", null);
        v1_1.setNeutralButton("试用", null);
        AlertDialog v2 = v1_1.create();
        local.dialog = v2;
        v2.setCanceledOnTouchOutside(false);
        Long v2_1 = null;
        String v4_1 = local.getini("logintime.txt");

其实这里我们可以试试直接去HOOK这个load方法了,看看能不能不去创建这个对话框,直接进入程序

Java.perform(function()
{
    var local = Java.use("yz.local");
    local.load.implementation=function(){
        console.log("----------------Hook--------------------");
        return;
    }
})
frida -U -f xu.ux.zheror -l demo.js 

其实发现可以直接过检测了

或者python来实现frida

import frida
import sys
jsCode ="""
    Java.perform(function()
    {
        var local = Java.use("yz.local");
        local.load.implementation=function(){
            console.log("----------------Hook--------------------");
            return;
        }
    });
"""
def on_message(message,date):
    print(message)
device = frida.get_usb_device()
app_name ='xu.nx.zheror'
pid = device.spawn([app_name])
process = device.attach(pid)
script = process.create_script(jsCode)
script.on('message',on_message)
script.load()
device.resume(pid)
sys.stdin.read()

这里一样是可以实现去检测的

重解包,硬改smali,重签名打包

搞完直接重打包,先解包

java -jar apktool.jar d  app.apk

改smail代码

找到对应的yz.local的源码位置 具体的位置是在 apktool\suwukong\smali_classes9\yz\locla.smail 的smail代码

.method public static load(Landroid/content/Context;)V
    .locals 16
    return-void
.end method

把load里面全部删了,就留个返回

生成APK

java -jar apktool.jar b 对应的文件夹

重签名

 keytool -genkey -v -keystore .\abc.keystore -alias new_name -keyalg RSA -keysize 2048 -validity 10000
输入密钥库口令:
您的名字与姓氏是什么?
  [Unknown]:  chen
您的组织单位名称是什么?
  [Unknown]:  chen
您的组织名称是什么?
  [Unknown]:  chen
您所在的城市或区域名称是什么?
  [Unknown]:  chen
您所在的省/市/自治区名称是什么?
  [Unknown]:  chen
该单位的双字母国家/地区代码是什么?
  [Unknown]:  chen
CN=chen, OU=chen, O=chen, L=chen, ST=chen, C=chen是否正确?
  [否]:  y

正在为以下对象生成 2,048 位RSA密钥对和自签名证书 (SHA256withRSA) (有效期为 10,000 天):
         CN=chen, OU=chen, O=chen, L=chen, ST=chen, C=chen
输入 <new_name> 的密钥口令
        (如果和密钥库口令相同, 按回车):
        
#这里有写对应的别名 new_name
#补上签名:
 jarsigner -verbose -keystore .\abc.keystore -signedjar new.apk apktool\apktool\suwukong\dist\瀛欒鑰�.apk new_name 

这里生成的APK就可以直接使用了

通过百度网盘分享的文件:孙行者.apk
链接:https://pan.baidu.com/s/1PH29tMyYuRPw4Ww_uQw0PQ?pwd=asdk
提取码:asdk
--来自百度网盘超级会员V4的分享

复现来源:https://www.52pojie.cn/forum.php?mod=viewthread&tid=1904103&extra=page%3D1%26filter%3Dlastpost%26orderby%3Dlastpost