某团优选风控分析以及解决



0x01、前言:

  .a) 关于风控:

    0x001. 目前对于各种app的协议分析都会触发一些风控问题,当然每个app的风控策略都会有所不同,比如IP,设备指纹,账号等,操作系统环境等(是否root,检测系统是否root就很容易,还有系统信息等,比如cpu信息,内存信息,传感器信息,相册照片的哈希值,通讯录,电话本,mac地址,蓝牙地址,充电状态,附近的wifi列表等),就目前形式来说,团购买菜类的app风控最为严格,风控很容易会被触发,导致账号被封,设备被封,ip被封等等情况,以下截图是来自某团优选app的部分代码.(可以看得出来,检测机器是否root,也检测了是否安装了xp环境),并且在libmtguard.so这个文件内读取了机型的很多信息,java层也获取了很多信息(某团系的so层获取机型信息都在这个so内做的)

检测xposed安装


public final class DeviceUtil {
    private static int a;
    private static long b;
    private static long c;
    private static long d;
    private static long e;
    private static long f;
    private static LEVEL g;
    private static final String[] h = {"/data/local/su", "/data/local/bin/su", "/data/local/xbin/su", "/system/xbin/su", "/system/bin/su", "/system/bin/.ext/su", "/system/bin/failsafe/su", "/system/sd/xbin/su", "/system/usr/we-need-root/su", "/sbin/su", "/su/bin/su"};
    private static final FileFilter i = new FileFilter() {
        /* class com.meituan.metrics.util.DeviceUtil.AnonymousClass1 */

        public final boolean accept(File file) {
            return Pattern.matches("cpu[0-9]", file.getName());
        }
    };

    public enum LEVEL {
        BEST(4),
        HIGH(3),
        MIDDLE(2),
        LOW(1),
        BAD(-1),
        UN_KNOW(0);
        
        int value;

        private LEVEL(int i) {
            this.value = i;
        }

        public final int getValue() {
            return this.value;
        }
    }

    public static LEVEL a(Context context) {
        if (g != null) {
            return g;
        }
        long d2 = d(context);
        int a2 = a();
        if (d2 <= 0) {
            g = LEVEL.UN_KNOW;
        } else if (d2 > 6442450944L) {
            g = LEVEL.BEST;
        } else if (d2 > IjkMediaMeta.AV_CH_WIDE_RIGHT) {
            g = LEVEL.HIGH;
        } else if (a2 <= 0) {
            g = LEVEL.UN_KNOW;
        } else if (d2 > IjkMediaMeta.AV_CH_WIDE_LEFT && a2 > 4) {
            g = LEVEL.MIDDLE;
        } else if (d2 <= IjkMediaMeta.AV_CH_STEREO_RIGHT || a2 <= 2) {
            g = LEVEL.BAD;
        } else {
            g = LEVEL.LOW;
        }
        return g;
    }

    /* JADX WARNING: Removed duplicated region for block: B:17:0x002c  */
    /* Code decompiled incorrectly, please refer to instructions dump. */
    public static int a() {
        /*
            int r0 = com.meituan.metrics.util.DeviceUtil.a
            if (r0 <= 0) goto L_0x0007
            int r0 = com.meituan.metrics.util.DeviceUtil.a
            return r0
        L_0x0007:
            r0 = 0
            java.lang.String r1 = "/sys/devices/system/cpu/possible"
            int r1 = a(r1)     // Catch:{ Exception -> 0x002a }
            if (r1 != 0) goto L_0x0016
            java.lang.String r1 = "/sys/devices/system/cpu/present"
            int r1 = a(r1)     // Catch:{ Exception -> 0x002a }
        L_0x0016:
            if (r1 != 0) goto L_0x0029
            java.lang.String r1 = "/sys/devices/system/cpu/"
            java.io.File r2 = new java.io.File     // Catch:{ Exception -> 0x002a }
            r2.<init>(r1)     // Catch:{ Exception -> 0x002a }
            java.io.FileFilter r1 = com.meituan.metrics.util.DeviceUtil.i     // Catch:{ Exception -> 0x002a }
            java.io.File[] r1 = r2.listFiles(r1)     // Catch:{ Exception -> 0x002a }
            if (r1 != 0) goto L_0x0028
            goto L_0x002a
        L_0x0028:
            int r1 = r1.length     // Catch:{ Exception -> 0x002a }
        L_0x0029:
            r0 = r1
        L_0x002a:
            if (r0 != 0) goto L_0x002d
            r0 = 1
        L_0x002d:
            com.meituan.metrics.util.DeviceUtil.a = r0
            return r0
        */
        throw new UnsupportedOperationException("Method not decompiled: com.meituan.metrics.util.DeviceUtil.a():int");
    }

    public static boolean b() {
        try {
            for (String str : h) {
                if (new File(str).exists()) {
                    return true;
                }
            }
        } catch (Exception unused) {
        }
        return false;
    }

    public static String c() {
        if (Build.VERSION.SDK_INT >= 21) {
            return TextUtils.join(",", Build.SUPPORTED_ABIS);
        }
        String str = Build.CPU_ABI;
        String str2 = Build.CPU_ABI2;
        if (TextUtils.isEmpty(str2)) {
            return str;
        }
        return str + "," + str2;
    }

    private static int a(String str) {
        Throwable th;
        FileInputStream fileInputStream;
        FileInputStream fileInputStream2 = null;
        try {
            fileInputStream = new FileInputStream(str);
            try {
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream, "UTF-8"));
                String readLine = bufferedReader.readLine();
                bufferedReader.close();
                if (readLine != null) {
                    if (readLine.matches("0-[\\d]+$")) {
                        int parseInt = Integer.parseInt(readLine.substring(2)) + 1;
                        f.a(fileInputStream);
                        return parseInt;
                    }
                }
                f.a(fileInputStream);
                return 0;
            } catch (IOException unused) {
                fileInputStream2 = fileInputStream;
                f.a(fileInputStream2);
                return 0;
            } catch (Throwable th2) {
                th = th2;
                f.a(fileInputStream);
                throw th;
            }
        } catch (IOException unused2) {
            f.a(fileInputStream2);
            return 0;
        } catch (Throwable th3) {
            th = th3;
            fileInputStream = null;
            f.a(fileInputStream);
            throw th;
        }
    }

    public static String c(Context context) {
        long d2 = d(context);
        return d2 > 0 ? String.valueOf(d2) : "N/A";
    }

    private static long d(Context context) {
        if (d > 0) {
            return d;
        }
        if (Build.VERSION.SDK_INT < 16 || context == null) {
            return b("MemTotal:");
        }
        ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
        ActivityManager activityManager = (ActivityManager) context.getSystemService(PushConstants.INTENT_ACTIVITY_NAME);
        activityManager.getMemoryInfo(memoryInfo);
        d = memoryInfo.totalMem;
        e = memoryInfo.threshold;
        long maxMemory = Runtime.getRuntime().maxMemory();
        if (maxMemory == Long.MAX_VALUE) {
            f = (long) activityManager.getMemoryClass();
        } else {
            f = maxMemory;
        }
        return d;
    }

    private static long b(String str) {
        Throwable th;
        long j = 0;
        if (TextUtils.isEmpty(str)) {
            return 0;
        }
        BufferedReader bufferedReader = null;
        try {
            BufferedReader bufferedReader2 = new BufferedReader(new InputStreamReader(new FileInputStream("/proc/meminfo"), "UTF-8"));
            try {
                String readLine = bufferedReader2.readLine();
                while (true) {
                    if (readLine == null) {
                        break;
                    }
                    Object[] split = readLine.split("\\s+");
                    if (str.equals(split[0])) {
                        j = ((long) Integer.parseInt(split[1])) * 1024;
                        break;
                    }
                    readLine = bufferedReader2.readLine();
                }
                f.a(bufferedReader2);
            } catch (Exception unused) {
                bufferedReader = bufferedReader2;
                f.a(bufferedReader);
                return j;
            } catch (Throwable th2) {
                th = th2;
                bufferedReader = bufferedReader2;
                f.a(bufferedReader);
                throw th;
            }
        } catch (Exception unused2) {
            f.a(bufferedReader);
            return j;
        } catch (Throwable th3) {
            th = th3;
            f.a(bufferedReader);
            throw th;
        }
        return j;
    }

    @SuppressLint({"DefaultLocale"})
    public static JSONObject a(JSONObject jSONObject, Context context) {
        long j;
        try {
            jSONObject.put("deviceLevel", a(context));
            jSONObject.put("MemoryTotal", String.format("%.2f MB", Float.valueOf((((float) d(context)) * 1.0f) / 1048576.0f)));
            Object[] objArr = new Object[1];
            if (Build.VERSION.SDK_INT < 16 || context == null) {
                j = b("MemAvailable:");
            } else {
                ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
                ((ActivityManager) context.getSystemService(PushConstants.INTENT_ACTIVITY_NAME)).getMemoryInfo(memoryInfo);
                j = memoryInfo.availMem;
            }
            objArr[0] = Float.valueOf((1.0f * ((float) j)) / 1048576.0f);
            jSONObject.put("MemoryAvailable", String.format("%.2f MB", objArr));
        } catch (Exception unused) {
        }
        return jSONObject;
    }

    public static String d() {
        long j;
        if (b > 0) {
            j = b;
        } else {
            long j2 = 0;
            for (int i2 = 0; i2 < a(); i2++) {
                long a2 = m.a(e.a("/sys/devices/system/cpu/cpu" + i2 + "/cpufreq/cpuinfo_max_freq"), 0L);
                if (a2 > j2) {
                    j2 = a2;
                }
            }
            b = j2;
            j = j2;
        }
        return j > 0 ? String.valueOf(j) : "N/A";
    }

    public static String e() {
        long j;
        if (c > 0) {
            j = c;
        } else {
            long j2 = Long.MAX_VALUE;
            for (int i2 = 0; i2 < a(); i2++) {
                long a2 = m.a(e.a("/sys/devices/system/cpu/cpu" + i2 + "/cpufreq/cpuinfo_min_freq"), 0L);
                if (a2 < j2 && a2 > 0) {
                    j2 = a2;
                }
            }
            if (j2 == Long.MAX_VALUE) {
                j = 0;
            } else {
                c = j2;
                j = j2;
            }
        }
        return j > 0 ? String.valueOf(j) : "N/A";
    }

    public static String b(Context context) {
        long j;
        if (f > 0) {
            j = f;
        } else {
            d(context);
            j = f;
        }
        return j > 0 ? String.valueOf(j) : "N/A";
    }
}

  .b) 解决风控:

    0x001. 解决设备指纹,由于安卓系统的开源开放策略导致在安卓内获取机型信息可以获取到非常多,所以这里想要解决设备指纹,可以有两套方案,那就是详细的分析app, 都在哪里读取的系统信息,通过hook的手段修改这些参数,当然也可以在网络发送数据前,未加密的数据进行替换,加密的数据在加密前进行参数替换也是可以的.第二种方案就是通过rom的定制可以实现未root的情况下实现一键改机。本文中采用的是第二种方案,定制rom + 长连接+ 尽可能的模拟真实用户访问 直到不封账号,不封设备,不封IP 为止.

  .c) 题外话:

    0x001. 至于买菜类的app风控做的这么严格其主要原因可能跟扰乱地方市场价格有关.

0x02、 目标需求:

  .a) 某团优选app风控分析

  .b) app版本6.12.10(目前最新)

  .c) 解决风控问题(付费内容可见)

价格: 2888.00 元
VIP会员价格:2388.00元终身会员价格:1988.00元
温馨提示:登录付款后可永久阅读隐藏内容。 付费可读



已完结...,剩下的事儿,大家自己发挥吧~










友情提示:本文只为技术分享交流,请勿非法用途.产生一切法律问题与本人无关.
本文中所有的调试代码均在gitee仓库中(包含很多app的关键点的hook代码) Git公开仓库地址 欢迎star or fork



在浏览的同时希望给予作者打赏,来支持作者的服务器维护费用.一分也是爱~