参考地址:https://www.jianshu.com/p/c37b1bdb4757

多开软件检测

多开软件的检测方案这里提供5种,首先4种来自
《Android多开/分身检测》
https://blog.darkness463.top/2018/05/04/Android-Virtual-Check/

《Android虚拟机多开检测》
https://www.jianshu.com/p/216d65d9971e

这里提供代码整理,一键调用,

        VirtualApkCheckUtil.getSingleInstance().checkByPrivateFilePath(this);        VirtualApkCheckUtil.getSingleInstance().checkByOriginApkPackageName(this);        VirtualApkCheckUtil.getSingleInstance().checkByHasSameUid();        VirtualApkCheckUtil.getSingleInstance().checkByMultiApkPackageName();        VirtualApkCheckUtil.getSingleInstance().checkByPortListening(getPackageName(),CallBack);

第5种方案来自我同事的启发,我起名叫端口检测法,具体思路已经单独成文见
《一行代码帮你检测Android多开软件》
https://www.jianshu.com/p/65c841749dd6

测试情况

测试机器/多开软件* 多开分身6.9 平行空间4.0.8389 双开助手3.8.4 分身大师2.5.1 VirtualXP0.11.2 Virtual App *
红米3S/Android6.0/原生eng XXXOO OXOOO OXOOO XOOOO XXXOO XXXOO
华为P9/Android7.0/EUI 5.0 root XXXXO OXOXO OXOXO XOOXO XXXXO XXXOO
小米MIX2/Android8.0/MIUI稳定版9.5 XXXXO OXOXO OXOXO XOOXO XXXXO XXXOO
一加5T/Android8.1/氢OS 5.1 稳定版 XXXXO OXOXO OXOXO XOOXO XXXXO XXXOO

*测试方案顺序如下12345,测试结果X代表未能检测O成功检测多开
*virtual app测试版本是git开源版,商用版已经修复uid的问题
*现在方案1234检测狭义多开已经失效,建议使用方案5做广义多开检测

1.文件路径检测

public boolean checkByPrivateFilePath(Context context) {        String path = context.getFilesDir().getPath();        for (String virtualPkg : virtualPkgs) {            if (path.contains(virtualPkg)) return true;        }        return false;    }

2.应用列表检测

 

简单来说,多开app把原始app克隆了,并让自己的包名跟原始app一样,当使用克隆app时,会检测到原始app的包名会和多开app包名一样(就是有两个一样的包名)

    public boolean checkByOriginApkPackageName(Context context) {        try {            if (context == null)  return false;            int count = 0;            String packageName = context.getPackageName();            PackageManager pm = context.getPackageManager();            List pkgs = pm.getInstalledPackages(0);            for (PackageInfo info : pkgs) {                if (packageName.equals(info.packageName)) {                    count++;                }            }            return count > 1;        } catch (Exception ignore) {        }        return false;    }

3.maps检测

需要维护多款分身包名

    public boolean checkByMultiApkPackageName() {        BufferedReader bufr = null;        try {            bufr = new BufferedReader(new FileReader("/proc/self/maps"));            String line;            while ((line = bufr.readLine()) != null) {                for (String pkg : virtualPkgs) {                    if (line.contains(pkg)) {                        return true;                    }                }            }        } catch (Exception ignore) {        } finally {            if (bufr != null) {                try {                    bufr.close();                } catch (IOException e) {                }            }        }        return false;    }

4.ps检测

 

简单来说,检测自身进程,如果该进程下的包名有不同多个私有文件目录,则认为被多开

    public boolean checkByHasSameUid() {        String filter = getUidStrFormat();//拿uid        String result = CommandUtil.getSingleInstance().exec("ps");        if (result == null || result.isEmpty()) return false;        String[] lines = result.split("\n");        if (lines == null || lines.length <= 0) return false;        int exitDirCount = 0;        for (int i = 0; i < lines.length; i++) {            if (lines[i].contains(filter)) {                int pkgStartIndex = lines[i].lastIndexOf(" ");                String processName = lines[i].substring(pkgStartIndex <= 0                        ? 0 : pkgStartIndex + 1, lines[i].length());                File dataFile = new File(String.format("/data/data/%s", processName, Locale.CHINA));                if (dataFile.exists()) {                    exitDirCount++;                }            }        }        return exitDirCount > 1;    }

5.端口检测

前4种方案,有一种直接对抗的意思,不希望我们的app运行在多开软件中,第5种方案,我们不直接对抗,只要不是在同一机器上同时运行同一app,我们都认为该app没有被多开。
假如同时运行着两个app(无论先开始运行),两个app进行一个通信,如果通信成功,我们则认为其中有一个是克隆体。

        //遍历查找已开启的端口        String tcp6 = CommandUtil.getSingleInstance().exec("cat /proc/net/tcp6");        if (TextUtils.isEmpty(tcp6)) return;        String[] lines = tcp6.split("\n");        ArrayList portList = new ArrayList<>();        for (int i = 0, len = lines.length; i < len; i++) {            int localHost = lines[i].indexOf("0100007F:");//127.0.0.1:            if (localHost < 0) continue;            String singlePort = lines[i].substring(localHost + 9, localHost + 13);            Integer port = Integer.parseInt(singlePort, 16);            portList.add(port);        }

对每个端口开启线程尝试连接,并且发送一段自定义的消息,作为钥匙,这里一般发送包名就行(刚好多开软件会把包名处理)

            Socket socket = new Socket("127.0.0.1", port);            socket.setSoTimeout(2000);            OutputStream outputStream = socket.getOutputStream();            outputStream.write((secret + "\n").getBytes("utf-8"));            outputStream.flush();            socket.shutdownOutput();

之后自己再开启端口监听作为服务器,等待连接,如果被连接上之后且消息匹配,则认为有一个克隆体在同时运行。

private void startServer(String secret) {        Random random = new Random();        ServerSocket serverSocket = null;        try {            serverSocket = new ServerSocket();            serverSocket.bind(new InetSocketAddress("127.0.0.1",                    random.nextInt(55534) + 10000));            while (true) {                Socket socket = serverSocket.accept();                ReadThread readThread = new ReadThread(secret, socket);                readThread.start();//                serverSocket.close();            }        } catch (BindException e) {            startServer(secret);//may be loop forever        } catch (IOException e) {            e.printStackTrace();        }    }

*因为端口通信需要Internet权限,本库不会通过网络上传任何隐私



作者:普通的程序员
链接:https://www.jianshu.com/p/c37b1bdb4757
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

 

更多相关文章

  1. Android(安卓)WebView 总结 —— 使用HTML5播放视频及全屏方案
  2. conversion to dalvik format failed with error 1 错误解决方案
  3. 【Android性能优化】内存泄露和内存溢出(OOM)的引发原因及优化方案
  4. Android(安卓)AdbCommandRejectedException和cannot bind to套接
  5. 【android】数据库升级完整解决方案
  6. Android连续获取当前所连接WiFi及周围热点列表信息的解决方案
  7. Cocos2d-x 3.2编译生成Android程序出错的解决方案:c++_static报
  8. Appium+Python appium启动夜神模拟器定位元素(三)
  9. android 6.0 动态权限解决方案

随机推荐

  1. Android怎么导入jar包
  2. Android(安卓)C2DM学习——服务器端代码
  3. Android中将控件放到线性布局的任意位置(
  4. Android(安卓)仿微信点赞和评论弹出框
  5. Android(安卓)6.0 悬浮窗默认关闭解决方
  6. 在小米手机上,修改APP的launcher应用图标
  7. Android内存泄漏原因及解决办法
  8. 【Android】缩略图Thumbnails
  9. [图解]Android(安卓)View的事件分发机制
  10. Android(安卓)APP存活检测方式