安全风险 - 检测设备是否为模拟器

在很多安全机构的检测中,关于模拟器的运行环境一般也会做监听处理,有的可能允许执行但是会提示用户,有的可能直接禁止在模拟器上运行我方APP

如何判断当前 app 是运行在Android真机,还是运行在模拟器? 可能做 Framework 的朋友思维会更开阔一些,不过现在也可以跟我这门外汉一起来稍微了解下

    • 攻略过程
      • 基础思考
      • 进阶思考
    • easyprotector 框架解析
    • 剥离 easyprotector框架 模拟器检测功能
      • CommandUtil (公共、基础)
      • EmulatorCheckUtil (核心)
      • EmulatorCheckCallback (配置)
      • CheckResult(配置)
      • 调用方式

攻略过程

其实我已经很久没有用过模拟器了,不过可以肯定的是模拟器与真机的本质区别大概率在于运行载体

Android 有非常多的模拟器,我已知的有官方自带的 Genymotion 模拟器,三方平台的夜神模拟器、天天模拟器等,所以想要完全鉴别出设备的运行环境,其实应该是存在一定问题的,我们只能说尽可能保证一定的容错率(我有想过很多应用平台提供的云机,但好像大多提供的都是真机,所以此项不在考虑范围之内)

基础思考

以下的一些思考主要结合了 如何判断是否是模拟器还是真机、全面检测设备是否模拟器、一行代码帮你检测Android模拟器、安卓逆向环境检测–模拟器 等多篇新旧文章

  • IMEI 设备识别码:用于唯一标识移动设备(放弃

模拟器的 IMEI 可以修改,早期平板可能没有IMEI,但是随着时代发展很多平板设备已拥有了属于自己的IMEI

  • MAC地址:物理地址,硬件地址,也称局域网地址,由网络设备制造商生产时烧录在网卡(放弃

模拟器的MAC地址是固定的几种,但是这些固定的地址随着模拟器类型递增,没有找到合适的,同时mac地址现在可以被模拟…

  • 通过调用公开或者隐藏的系统API判断放弃

因为调用结果可以轻易被修改,比如直接修改Android的源代码或者借助 Xposed Framework 进行修改(这种场景我虽未参与,但是应该可以参考Java的反射机制)

  • 功能验证:初期模拟器功能并不完善,可以采用类似 打电话、发短信 等方式进行功能测试,但是后续随着模拟器升级已补全对应功能 (放弃)
public boolean isSimulator1() {
        String url = "tel:" + "10086";
        Intent intent = new Intent();
        intent.setData(Uri.parse(url));
        intent.setAction(Intent.ACTION_DIAL);
        // 是否可以处理跳转到拨号的 Intent
        boolean canResolveIntent = intent.resolveActivity(mContext.getPackageManager()) != null;
		return !canResolveIntent;
}
  • 设备IDS、特有文件验证(放弃

涉及到敏感权限时需要申请权限,根据授权结果容易出现误判,同时影响用户体验

    private static String[]known_numbers = {"15555215554","15555215556",
            "15555215558","15555215560","15555215562","15555215564",
            "15555215566","15555215568","15555215570","15555215572",
            "15555215574","15555215576","15555215578","15555215580",
            "15555215582","15555215584",};
    public static Boolean CheckPhoneNumber(Context context){
        TelephonyManager telephonyManager =(TelephonyManager)context
                .getSystemService(Context.TELEPHONY_SERVICE);
        String phonenumber =telephonyManager.getLine1Number();
        for(String number :known_numbers){
            if(number.equalsIgnoreCase(phonenumber)){
                Log.v("Result:","Find PhoneNumber!");
                return true;
            }
        }
        Log.v("Result:","Not Find PhoneNumber!");
        return false;
    }

特有文件检测 - 权限要求

在这里插入图片描述

设备IDS检测 - 权限要求

在这里插入图片描述

  • CPU检测方法:现在的模拟器基本可以做到模拟手机号码,手机品牌,cpu信息等,比如逍遥/夜神模拟器读取 ro.product.board 进行了处理,能得到预先设置的cpu信息(放弃
public static boolean checkIsNotRealPhone() {
    String cpuInfo = readCpuInfo();
    if ((cpuInfo.contains("intel") || cpuInfo.contains("amd"))) {
        return true;
    }
    return false;
}
public static String readCpuInfo() {
    String result = "";
    try {
        String[] args = {"/system/bin/cat", "/proc/cpuinfo"};
        ProcessBuilder cmd = new ProcessBuilder(args);
        Process process = cmd.start();
        StringBuffer sb = new StringBuffer();
        String readLine = "";
        BufferedReader responseReader = new BufferedReader(new InputStreamReader(process.getInputStream(), "utf-8"));
        while ((readLine = responseReader.readLine()) != null) {
            sb.append(readLine);
        }
        responseReader.close();
        result = sb.toString().toLowerCase();
    } catch (IOException ex) {
    }
    return result;
}

进阶思考

在一篇Blog内有看到这样一副图,值得借鉴,因为我最后使用的 easyprotector 框架就做了硬件信息检测

在这里插入图片描述

Tip:有兴趣的可以参考以下方法扩展 easyprotector 框架内的模拟器检测部分

模拟器框架文件

 
UNEXPORT void AntiEmulator::check_file() {
    char *(path[]) = {
            "/system/bin/androVM-prop",   //检测androidVM
            "/system/bin/microvirt-prop", //检测逍遥模拟器--新版本找不到特征
            "/system/lib/libdroid4x.so",  //检测海马模拟器
            "/system/bin/windroyed",      //检测文卓爷模拟器
            "/system/bin/nox-prop",       //检测夜神模拟器--某些版本找不到特征
            "/system/lib/libnoxspeedup.so",//检测夜神模拟器
            "/system/bin/ttVM-prop",      //检测天天模拟器
            "/data/.bluestacks.prop",     //检测bluestacks模拟器  51模拟器
            "/system/bin/duosconfig",     //检测AMIDuOS模拟器
            "/system/etc/xxzs_prop.sh",   //检测星星模拟器
            "/system/etc/mumu-configs/device-prop-configs/mumu.config", //网易MuMu模拟器
            "/system/priv-app/ldAppStore",   //雷电模拟器
            "/system/bin/ldinit",             //雷电模拟器
            "/system/bin/ldmountsf",          //雷电模拟器
            "/system/app/AntStore",          //小蚁模拟器
            "/system/app/AntLauncher",       //小蚁模拟器
            "vmos.prop",                     //vmos虚拟机
            "fstab.titan",                   //光速虚拟机
            "init.titan.rc",                 //光速虚拟机
            "x8.prop",                       //x8沙箱和51虚拟机
            "/system/lib/libc_malloc_debug_qemu.so", //AVD QEMU
            "/system/bin/microvirtd",
            "/dev/socket/qemud",
            "/dev/qemu_pipe"};
    for (int i = 0; i < sizeof(path) / sizeof(char*); i++){
        if (Syscall::check_file_or_dir_exists(path[i])){
            LOGI("check_file  %s file existing", path[i]);
            // TODO 风险
 
        }
    }
}

easyprotector 框架解析

关于 easyprotector框架 文档可以参考 一行代码帮你检测Android模拟器(更新至1.1.0) 会更详细一些

我之所以在 github 选这个框架,主要有几点原因

  • 模拟器检测框架有限
  • 该框架star高
  • 检测方面考虑全面(检测渠道、设备型号、硬件制造商、主板名称、基带信息、第三方应用数量、传感器数量、是否支持蓝牙、是否支持相机、是否支持闪光灯等)
  • 能力之内可以适当扩展检测项
  • 为了避免误判设备类型,内部加入条件判断,只有满足3项模拟器特征才会判定为模拟器

直接通过框架源码,查看下模拟器检测的执行过程

  1. 调用了 EasyProtectorLib.checkIsRunningInEmulator 方法

在这里插入图片描述

  1. EasyProtectorLib 中找到了 checkIsRunningInEmulator 实际调用了 EmulatorCheckUtil

在这里插入图片描述

  1. 查看 EmulatorCheckUtil - readSysProperty 源码即可

源码中 ro.buildro.productgsm.version 含义,做Framework朋友可能比较了解,主要用于检测一些系统级信息

在这里插入图片描述

之前有提到功能扩展和延伸,大家可自行在该类源码中进行扩展,不过最好另起方法,有自信的话改原方法也行

在这里插入图片描述


剥离 easyprotector框架 模拟器检测功能

因为 easyprotector框架 中涉及的功能比较多,我习惯性只抽出了我所需要的部分

CommandUtil (公共、基础)

简单来看主要是通过反射机制获取一些系统的公共资源信息

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;

/**
 * Project Name:EasyProtector
 * Package Name:com.lahm.library
 * Created by lahm on 2018/6/8 16:23 .
 */
public class CommandUtil {
    private CommandUtil() {
    }

    private static class SingletonHolder {
        private static final CommandUtil INSTANCE = new CommandUtil();
    }

    public static final CommandUtil getSingleInstance() {
        return SingletonHolder.INSTANCE;
    }

    public String getProperty(String propName) {
        String value = null;
        Object roSecureObj;
        try {
            roSecureObj = Class.forName("android.os.SystemProperties")
                    .getMethod("get", String.class)
                    .invoke(null, propName);
            if (roSecureObj != null) value = (String) roSecureObj;
        } catch (Exception e) {
            value = null;
        } finally {
            return value;
        }
    }

    public String exec(String command) {
        BufferedOutputStream bufferedOutputStream = null;
        BufferedInputStream bufferedInputStream = null;
        Process process = null;
        try {
            process = Runtime.getRuntime().exec("sh");
            bufferedOutputStream = new BufferedOutputStream(process.getOutputStream());

            bufferedInputStream = new BufferedInputStream(process.getInputStream());
            bufferedOutputStream.write(command.getBytes());
            bufferedOutputStream.write('\n');
            bufferedOutputStream.flush();
            bufferedOutputStream.close();

            process.waitFor();

            String outputStr = getStrFromBufferInputSteam(bufferedInputStream);
            return outputStr;
        } catch (Exception e) {
            return null;
        } finally {
            if (bufferedOutputStream != null) {
                try {
                    bufferedOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedInputStream != null) {
                try {
                    bufferedInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (process != null) {
                process.destroy();
            }
        }
    }

    private static String getStrFromBufferInputSteam(BufferedInputStream bufferedInputStream) {
        if (null == bufferedInputStream) {
            return "";
        }
        int BUFFER_SIZE = 512;
        byte[] buffer = new byte[BUFFER_SIZE];
        StringBuilder result = new StringBuilder();
        try {
            while (true) {
                int read = bufferedInputStream.read(buffer);
                if (read > 0) {
                    result.append(new String(buffer, 0, read));
                }
                if (read < BUFFER_SIZE) {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result.toString();
    }
}

EmulatorCheckUtil (核心)

import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.text.TextUtils;

import static android.content.Context.SENSOR_SERVICE;
import static cn.com.huaan.fund.acts.base.safe.CheckResult.RESULT_EMULATOR;
import static cn.com.huaan.fund.acts.base.safe.CheckResult.RESULT_MAYBE_EMULATOR;
import static cn.com.huaan.fund.acts.base.safe.CheckResult.RESULT_UNKNOWN;


/**
 * Project Name:EasyProtector
 * Package Name:com.lahm.library
 * Created by lahm on 2018/6/8 15:01 .
 */
public class EmulatorCheckUtil {
    private EmulatorCheckUtil() {

    }

    private static class SingletonHolder {
        private static final EmulatorCheckUtil INSTANCE = new EmulatorCheckUtil();
    }

    public static final EmulatorCheckUtil getSingleInstance() {
        return SingletonHolder.INSTANCE;
    }

    public boolean readSysProperty(Context context, EmulatorCheckCallback callback) {
        if (context == null)
            throw new IllegalArgumentException("context must not be null");

        int suspectCount = 0;

        //检测硬件名称
        CheckResult hardwareResult = checkFeaturesByHardware();
        switch (hardwareResult.result) {
            case RESULT_MAYBE_EMULATOR:
                ++suspectCount;
                break;
            case RESULT_EMULATOR:
                if (callback != null) callback.findEmulator("hardware = " + hardwareResult.value);
                return true;
        }

        //检测渠道
        CheckResult flavorResult = checkFeaturesByFlavor();
        switch (flavorResult.result) {
            case RESULT_MAYBE_EMULATOR:
                ++suspectCount;
                break;
            case RESULT_EMULATOR:
                if (callback != null) callback.findEmulator("flavor = " + flavorResult.value);
                return true;
        }

        //检测设备型号
        CheckResult modelResult = checkFeaturesByModel();
        switch (modelResult.result) {
            case RESULT_MAYBE_EMULATOR:
                ++suspectCount;
                break;
            case RESULT_EMULATOR:
                if (callback != null) callback.findEmulator("model = " + modelResult.value);
                return true;
        }

        //检测硬件制造商
        CheckResult manufacturerResult = checkFeaturesByManufacturer();
        switch (manufacturerResult.result) {
            case RESULT_MAYBE_EMULATOR:
                ++suspectCount;
                break;
            case RESULT_EMULATOR:
                if (callback != null)
                    callback.findEmulator("manufacturer = " + manufacturerResult.value);
                return true;
        }

        //检测主板名称
        CheckResult boardResult = checkFeaturesByBoard();
        switch (boardResult.result) {
            case RESULT_MAYBE_EMULATOR:
                ++suspectCount;
                break;
            case RESULT_EMULATOR:
                if (callback != null) callback.findEmulator("board = " + boardResult.value);
                return true;
        }

        //检测主板平台
        CheckResult platformResult = checkFeaturesByPlatform();
        switch (platformResult.result) {
            case RESULT_MAYBE_EMULATOR:
                ++suspectCount;
                break;
            case RESULT_EMULATOR:
                if (callback != null) callback.findEmulator("platform = " + platformResult.value);
                return true;
        }

        //检测基带信息
        CheckResult baseBandResult = checkFeaturesByBaseBand();
        switch (baseBandResult.result) {
            case RESULT_MAYBE_EMULATOR:
                suspectCount += 2;//模拟器基带信息为null的情况概率相当大
                break;
            case RESULT_EMULATOR:
                if (callback != null) callback.findEmulator("baseBand = " + baseBandResult.value);
                return true;
        }

        //检测传感器数量
        int sensorNumber = getSensorNumber(context);
        if (sensorNumber <= 7) ++suspectCount;

        //检测已安装第三方应用数量
        int userAppNumber = getUserAppNumber();
        if (userAppNumber <= 5) ++suspectCount;

        //检测是否支持闪光灯
        boolean supportCameraFlash = supportCameraFlash(context);
        if (!supportCameraFlash) ++suspectCount;
        //检测是否支持相机
        boolean supportCamera = supportCamera(context);
        if (!supportCamera) ++suspectCount;
        //检测是否支持蓝牙
        boolean supportBluetooth = supportBluetooth(context);
        if (!supportBluetooth) ++suspectCount;

        //检测光线传感器
        boolean hasLightSensor = hasLightSensor(context);
        if (!hasLightSensor) ++suspectCount;

        //检测进程组信息
        CheckResult cgroupResult = checkFeaturesByCgroup();
        if (cgroupResult.result == RESULT_MAYBE_EMULATOR) ++suspectCount;

        if (callback != null) {
            StringBuffer stringBuffer = new StringBuffer("Test start")
                    .append("\r\n").append("hardware = ").append(hardwareResult.value)
                    .append("\r\n").append("flavor = ").append(flavorResult.value)
                    .append("\r\n").append("model = ").append(modelResult.value)
                    .append("\r\n").append("manufacturer = ").append(manufacturerResult.value)
                    .append("\r\n").append("board = ").append(boardResult.value)
                    .append("\r\n").append("platform = ").append(platformResult.value)
                    .append("\r\n").append("baseBand = ").append(baseBandResult.value)
                    .append("\r\n").append("sensorNumber = ").append(sensorNumber)
                    .append("\r\n").append("userAppNumber = ").append(userAppNumber)
                    .append("\r\n").append("supportCamera = ").append(supportCamera)
                    .append("\r\n").append("supportCameraFlash = ").append(supportCameraFlash)
                    .append("\r\n").append("supportBluetooth = ").append(supportBluetooth)
                    .append("\r\n").append("hasLightSensor = ").append(hasLightSensor)
                    .append("\r\n").append("cgroupResult = ").append(cgroupResult.value)
                    .append("\r\n").append("suspectCount = ").append(suspectCount);
            callback.findEmulator(stringBuffer.toString());
        }
        //嫌疑值大于3,认为是模拟器
        return suspectCount > 3;
    }

    private int getUserAppNum(String userApps) {
        if (TextUtils.isEmpty(userApps)) return 0;
        String[] result = userApps.split("package:");
        return result.length;
    }

    private String getProperty(String propName) {
        String property = CommandUtil.getSingleInstance().getProperty(propName);
        return TextUtils.isEmpty(property) ? null : property;
    }

    /**
     * 特征参数-硬件名称
     *
     * @return 0表示可能是模拟器,1表示模拟器,2表示可能是真机
     */
    private CheckResult checkFeaturesByHardware() {
        String hardware = getProperty("ro.hardware");
        if (null == hardware) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
        int result;
        String tempValue = hardware.toLowerCase();
        switch (tempValue) {
            case "ttvm"://天天模拟器
            case "nox"://夜神模拟器
            case "cancro"://网易MUMU模拟器
            case "intel"://逍遥模拟器
            case "vbox":
            case "vbox86"://腾讯手游助手
            case "android_x86"://雷电模拟器
                result = RESULT_EMULATOR;
                break;
            default:
                result = RESULT_UNKNOWN;
                break;
        }
        return new CheckResult(result, hardware);
    }

    /**
     * 特征参数-渠道
     *
     * @return 0表示可能是模拟器,1表示模拟器,2表示可能是真机
     */
    private CheckResult checkFeaturesByFlavor() {
        String flavor = getProperty("ro.build.flavor");
        if (null == flavor) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
        int result;
        String tempValue = flavor.toLowerCase();
        if (tempValue.contains("vbox")) result = RESULT_EMULATOR;
        else if (tempValue.contains("sdk_gphone")) result = RESULT_EMULATOR;
        else result = RESULT_UNKNOWN;
        return new CheckResult(result, flavor);
    }

    /**
     * 特征参数-设备型号
     *
     * @return 0表示可能是模拟器,1表示模拟器,2表示可能是真机
     */
    private CheckResult checkFeaturesByModel() {
        String model = getProperty("ro.product.model");
        if (null == model) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
        int result;
        String tempValue = model.toLowerCase();
        if (tempValue.contains("google_sdk")) result = RESULT_EMULATOR;
        else if (tempValue.contains("emulator")) result = RESULT_EMULATOR;
        else if (tempValue.contains("android sdk built for x86")) result = RESULT_EMULATOR;
        else result = RESULT_UNKNOWN;
        return new CheckResult(result, model);
    }

    /**
     * 特征参数-硬件制造商
     *
     * @return 0表示可能是模拟器,1表示模拟器,2表示可能是真机
     */
    private CheckResult checkFeaturesByManufacturer() {
        String manufacturer = getProperty("ro.product.manufacturer");
        if (null == manufacturer) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
        int result;
        String tempValue = manufacturer.toLowerCase();
        if (tempValue.contains("genymotion")) result = RESULT_EMULATOR;
        else if (tempValue.contains("netease")) result = RESULT_EMULATOR;//网易MUMU模拟器
        else result = RESULT_UNKNOWN;
        return new CheckResult(result, manufacturer);
    }

    /**
     * 特征参数-主板名称
     *
     * @return 0表示可能是模拟器,1表示模拟器,2表示可能是真机
     */
    private CheckResult checkFeaturesByBoard() {
        String board = getProperty("ro.product.board");
        if (null == board) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
        int result;
        String tempValue = board.toLowerCase();
        if (tempValue.contains("android")) result = RESULT_EMULATOR;
        else if (tempValue.contains("goldfish")) result = RESULT_EMULATOR;
        else result = RESULT_UNKNOWN;
        return new CheckResult(result, board);
    }

    /**
     * 特征参数-主板平台
     *
     * @return 0表示可能是模拟器,1表示模拟器,2表示可能是真机
     */
    private CheckResult checkFeaturesByPlatform() {
        String platform = getProperty("ro.board.platform");
        if (null == platform) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
        int result;
        String tempValue = platform.toLowerCase();
        if (tempValue.contains("android")) result = RESULT_EMULATOR;
        else result = RESULT_UNKNOWN;
        return new CheckResult(result, platform);
    }

    /**
     * 特征参数-基带信息
     *
     * @return 0表示可能是模拟器,1表示模拟器,2表示可能是真机
     */
    private CheckResult checkFeaturesByBaseBand() {
        String baseBandVersion = getProperty("gsm.version.baseband");
        if (null == baseBandVersion) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
        int result;
        if (baseBandVersion.contains("1.0.0.0")) result = RESULT_EMULATOR;
        else result = RESULT_UNKNOWN;
        return new CheckResult(result, baseBandVersion);
    }

    /**
     * 获取传感器数量
     */
    private int getSensorNumber(Context context) {
        SensorManager sm = (SensorManager) context.getSystemService(SENSOR_SERVICE);
        return sm.getSensorList(Sensor.TYPE_ALL).size();
    }

    /**
     * 获取已安装第三方应用数量
     */
    private int getUserAppNumber() {
        String userApps = CommandUtil.getSingleInstance().exec("pm list package -3");
        return getUserAppNum(userApps);
    }

    /**
     * 是否支持相机
     */
    private boolean supportCamera(Context context) {
        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
    }

    /**
     * 是否支持闪光灯
     */
    private boolean supportCameraFlash(Context context) {
        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
    }

    /**
     * 是否支持蓝牙
     */
    private boolean supportBluetooth(Context context) {
        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
    }

    /**
     * 判断是否存在光传感器来判断是否为模拟器
     * 部分真机也不存在温度和压力传感器。其余传感器模拟器也存在。
     *
     * @return false为模拟器
     */
    private boolean hasLightSensor(Context context) {
        SensorManager sensorManager = (SensorManager) context.getSystemService(SENSOR_SERVICE);
        Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); //光线传感器
        if (null == sensor) return false;
        else return true;
    }

    /**
     * 特征参数-进程组信息
     */
    private CheckResult checkFeaturesByCgroup() {
        String filter = CommandUtil.getSingleInstance().exec("cat /proc/self/cgroup");
        if (null == filter) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
        return new CheckResult(RESULT_UNKNOWN, filter);
    }
}

EmulatorCheckCallback (配置)

回调监听,可以获取到具体检测结果

public interface EmulatorCheckCallback {
    void findEmulator(String emulatorInfo);
}

CheckResult(配置)

对检测结果进行类别划分,方便管理

public class CheckResult {
    public static final int RESULT_MAYBE_EMULATOR = 0;//可能是模拟器
    public static final int RESULT_EMULATOR = 1;//模拟器
    public static final int RESULT_UNKNOWN = 2;//可能是真机

    public int result;
    public String value;

    public CheckResult(int result, String value) {
        this.result = result;
        this.value = value;
    }
}

调用方式

 val readSysProperty = EmulatorCheckUtil.getSingleInstance().readSysProperty(context, null)
 if (readSysProperty) {
     //根据需要进行风险提示等相关业务
     ToastUtils.showToast("您当前可能运行在模拟器设备,请谨防安全风险!")
 }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/665099.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DKTCDR:Domain-Oriented Knowledge Transfer for Cross-Domain Recommendation

Domain-Oriented Knowledge Transfer for Cross-Domain Recommendation IEEE(CCF B.SCI 1)-Guoshuai Zhao, Xiaolong Zhang, Hao Tang, Jialie Shen, and Xueming Qian-2024 思路 在CDR中,构建连接两个域的桥梁是实现跨域推荐的基础。然而现在的CDR方法往往在连接两个域时忽…

Usage - hackthebox

简介 靶场&#xff1a;hackmyvm 靶机&#xff1a;Usage(10.10.11.18) 难度&#xff1a;Easy 靶机链接:https://app.hackthebox.com/machines/Usage 攻击机1&#xff1a;ubuntu22.04 (10.10.16.21) 攻击机2&#xff1a;windows11(10.10.14.33) 扫描 nmap起手 nmap -sT …

身份认证与口令攻击

身份认证与口令攻击 身份认证身份认证的五种方式口令认证静态口令动态口令(一次性口令)动态口令分类 密码学认证一次性口令认证S/KEY协议改进的S/KEY协议 其于共享密钥的认证 口令行为规律和口令猜测口令规律口令猜测 口令破解操作系统口令破解Windows密码存储机制Windows密码破…

整合框架(spring...) 统一异常处理

1、 我们想让异常结果也显示为统一的返回结果对象&#xff0c;并且统一处理系统的异常信息&#xff0c;那么需要统一异常处理。 附加&#xff1a;创建封装错误状态码和错误消息VO 代码如下&#xff1a; Result import io.swagger.v3.oas.annotations.media.Schema; impo…

数组的应用-24点游戏

目录 24点游戏 游戏规则 游戏主要分为三部分 电脑出牌 用户输入算式 电脑判断胜负 总结 24点游戏 游戏规则&#xff1a; 54张扑克抽出大小王&#xff0c;剩余52张用来用于游戏&#xff1b;每一轮从52张牌中随机抽出4张&#xff1b;玩家运用加&#xff0c;减&#xff0…

Java集合【超详细】

文章目录 一、集合框架1.1 概述1.2 数组和集合的区别1.3 Java集合框架体系1.4 数据结构1.4.1 栈、队列、数组、队列1.4.2 二叉树【理解】1.4.3 二叉查找树【理解】1.4.4 平衡二叉树【理解】1.4.5 红黑树【理解】 1.5 泛型 二、Collection集合2.1 Collection 集合概述和使用【应…

统计各个商品今年销售额与去年销售额的增长率及排名变化

文章目录 测试数据需求说明需求实现分步解析 测试数据 -- 创建商品表 DROP TABLE IF EXISTS products; CREATE TABLE products (product_id INT,product_name STRING );INSERT INTO products VALUES (1, Product A), (2, Product B), (3, Product C), (4, Product D), (5, Pro…

VIO System 丨适用于控制器开发前期的测试系统

VIO综述 嵌入式软件的HIL测试需要复杂的测试系统及完整的ECU硬件&#xff0c;这导致通常只能在开发流程的后期阶段进行测试。全新推出的低成本解决方案VIO System&#xff0c;使得在开发前期不仅可以进行总线通讯测试&#xff0c;也可以同时进行I/O信号测试。 该系统旨在通过…

css-Ant-Menu 导航菜单更改为左侧列表行选中

1.Ant-Menu导航菜单 导航菜单是一个网站的灵魂&#xff0c;用户依赖导航在各个页面中进行跳转。一般分为顶部导航和侧边导航&#xff0c;顶部导航提供全局性的类目和功能&#xff0c;侧边导航提供多级结构来收纳和排列网站架构。 2.具体代码 html <!-- 左侧切换 --><…

【计算Nei遗传距离】

报错 Warning message: In adegenet::df2genind(t(x), sep sep, ...) : Markers with no scored alleles have been removed 原因&#xff1a; 直接用plink转换为VCF&#xff0c;丢失了等位基因分型&#xff08;REF ALT&#xff09; &#xff08;plink编码的规则&…

成绩发布小程序哪个好用?

大家好&#xff0c;今天我要来跟大家分享一个超级实用的小秘密——易查分小程序&#xff01;作为老师&#xff0c;你是不是还在为发放成绩而头疼&#xff1f;是不是还在为通知家长而烦恼&#xff1f;别急&#xff0c;易查分小程序来帮你啦&#xff01; 易查分简直是老师们的贴心…

ESP8266使用AT指令登陆新版OneNET平台进行固定数据上报

登陆OneNET进开发者中心 创建产品 创建云平台产品 产品类别和智能化方式选择 产品名称和城市自定义选择&#xff0c;框选部分参照下图&#xff0c;开发方案选标准方案时平台会预置标准物模型和App控制面板&#xff0c;选自定义方案用户可自行定义物模型和App控制面板&…

李廉洋:5.31黄金原油末日砸盘,美盘分析及策略。

黄金消息面分析&#xff1a;过去几天股市的抛售也是金属市场的利多因素。美国商务部将第一季度GDP预期从1.6%下修至1.3%后&#xff0c;美国国债收益率下降。同时&#xff0c;美国劳工部公布&#xff0c;上周首次申请失业救济人数从前一周修正后的21.6万人上升至21.9万人。综合来…

【代码随想录——回溯算法——四周目】

1.重新安排行程 1.1 我的代码&#xff0c;超时通不过 var (used []boolpath []stringres []stringisFind bool )func findItinerary(tickets [][]string) []string {sortTickets(tickets)res make([]string, len(tickets)1)path make([]string, 0)used make([]bool,…

我与C++的爱恋:vector的使用

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;我与C的爱恋 ​ 文章目录 一、vector的简单介绍二、vector的使用构造函数遍历容器对容器的操作vector 的增删查改 一、vector的简单介绍 vector是表示可变大小数组的序列容器 就像数组…

并查集拓展(扩展域并查集)

事实证明&#xff0c;扩展域并查集应该在带权并查集前面讲的&#xff0c;因为比较好理解&#xff0c;而且回过头看带权并查集可能也会更轻松一些。 https://www.luogu.com.cn/problem/P1892https://www.luogu.com.cn/problem/P1892 题目描述 现在有 &#x1d45b; 个人&…

VBA语言専攻每周通知20240531

通知20240531 各位学员∶本周MF系列VBA技术资料增加616-620讲&#xff0c;T3学员看到通知后请免费领取,领取时间5月31日晚上19:00-6月1日晚上20:00。本次增加内容&#xff1a; MF616:创建具有间隔的计时器循环 MF617:计时器的计时与重置 MF618:列出单字符所有可能的排列组合…

怎么把图片大小调小?在线改图片大小的方法

怎么把比较大的图片压缩变小呢&#xff1f;在使用图片的时候&#xff0c;比较常见的一个问题就是图片太大导致无法正常上传&#xff0c;需要将图片处理到合适的大小之后&#xff0c;才可以正常在网上上传。现在一般调整图片大小多会通过使用在线改图片大小的在线工具来处理&…

动态路由协议实验——RIP

动态路由协议实验——RIP 什么是RIP ​ RIP(Routing Information Protocol,路由信息协议&#xff09;是一种内部网关协议&#xff08;IGP&#xff09;&#xff0c;是一种动态路由选择协议&#xff0c;用于自治系统&#xff08;AS&#xff09;内的路由信息的传递。RIP协议基于…

Codigger编码场景介绍(三):调试场景(Debug Scenery)

Codigger&#xff0c;一个专为开发人员设计的工具&#xff0c;致力于为不同的开发场景提供最佳的切换体验。Codigger囊括了多种场景&#xff0c;如传统场景、调试场景、设计器场景、驾驶舱场景以及纯净场景等。在上一篇文章中&#xff0c;我们介绍了驾驶舱场景&#xff0c;今天…