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

news2024/10/5 20:23:03

在很多安全机构的检测中,关于模拟器的运行环境一般也会做监听处理,有的可能允许执行但是会提示用户,有的可能直接禁止在模拟器上运行我方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.coloradmin.cn/o/1718716.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

能解决各行各业的数据传输管控方案长什么样,可以进来看看

在数字化时代&#xff0c;数据成为企业最宝贵的资产之一。然而&#xff0c;随之而来的是数据管控的挑战。企业在数据管控过程中可能会遇到哪些问题&#xff1f;一个能够解决各行各业需求的数据传输管控系统应该如何构建&#xff1f;优秀的数据传输管控优势和特点又在哪里&#…

【期末速成】——计算机组成原理(1)

目录 一、什么是计算机的组成 二、冯诺依曼体系结构计算机的特点 三、计算机系统的层次结构 四、机器语言、汇编语言、高级语言, 五、 编译程序、解释程序、汇编程序 六、已知主频、CPI计算程序运行时间 一、什么是计算机的组成 计算机的组成可以分为五个部件和两个信息…

flask-slqalchemy使用详解

目录 1、flask-sqlalchemy 1.1、flask_sqlalchemy 与sqlalchemy 的关系 1.1.1、 基本定义与用途 1.2、flask_sqlalchemy 的使用 1.2.1、安装相关的库 1.2.2、项目准备 1.2.3、创建ORM模型 1.2.3.1、使用db.create_all()创建表的示例 1.2.3.2、创建多表关联ORM模型 1.…

一维时间序列信号的改进小波降噪方法(MATLAB R2021B)

目前国内外对于小波分析在降噪方面的方法研究中&#xff0c;主要有小波分解与重构法降噪、小波阈值降噪、小波变换模极大值法降噪等三类方法。 (1)小波分解与重构法降噪 早在1988 年&#xff0c;Mallat提出了多分辨率分析的概念&#xff0c;利用小波分析的多分辨率特性进行分…

【Unity】使用Jenkins实现远程Unity打包

前言 很多时候&#xff0c;我们需要自动打包&#xff0c;比如下班了&#xff0c;我要出一个包明天早上用。比如每天夜里12点&#xff0c;我需要定时出一个稳定包。 这个时候就需要Jenkins了。 1.安装环境 安装 jenkins 之前&#xff0c;需要安装Java 。Java下载网站 ①下载…

Unity 实现让物体渲染在最前面

演示 实现方案 1.创建一个shader脚本 2.删掉原来的内容&#xff1a;我们自己写 附上完整的shader代码&#xff1a; Shader "Custom/ZTestAlways" {Properties {_Color ("Color Tint",Color) (1,1,1,1)_MainTex("Main Tex",2D) "white&q…

【EI会议|检索稳定】2024年通讯工程与云计算国际会议(CECC 2024)

2024年通讯工程与云计算国际会议&#xff08;CECC 2024&#xff09; 2024 International Conference on Communication Engineering and Cloud Computing 【重要信息】 大会地点&#xff1a;武汉 大会官网&#xff1a;http://www.iaccecc.com 投稿邮箱&#xff1a;iacceccsub-…

突破 LST/LRT 赛道中心化困境,Puffer Finance 何以重塑以太坊再质押未来

纵观过去的 2023 年&#xff0c;LST 赛道竞争进入“白热化”状态。去中心化、DeFi 增强、全链化成为市场争夺关键词&#xff0c;诸多 LST 赛道老牌项目纷纷陷入“中心化矛盾”&#xff0c;指责对方在以太坊去中心化进程中的不利作为。 在这样的竞争情形下&#xff0c;以太坊联…

skywalking介绍及搭建

链路追踪框架比对&#xff1a; skywalking安装部署&#xff1a; 下载地址&#xff1a;Downloads | Apache SkyWalking 配置微服务与skywalking整合&#xff1a; copy agent/optional-plugins/apm-spring-cloud-getway-xx.jar到plugins&#xff0c;然后重启skywalking 监控界面…

MOE模型入门

一、目录 定义&#xff1a;MOE架构代表类型如何解决expert 平衡的&#xff1f;而不是集中到某一专家。如何训练、微调MOE模型&#xff1f;基础架构优缺点不同MOE 模型实现方式、训练方法 二、实现 定义&#xff1a;MOE架构 MOE:混合专家模型&#xff0c;多个专家共同决策的模…

2024年Kubernetes管理的发展趋势及预测

Kubernetes管理的概念 Kubernetes管理是指用于监督使用Kubernetes的跨机器集群的容器化应用程序的部署、扩展和操作的过程和工具。这个编排平台自动化了部署、管理和扩展容器化应用程序的许多方面&#xff0c;但它也引入了配置、网络、安全性和资源管理方面的复杂性。 有效的K…

Linux - 文件管理高级1

0.管道 | 将前面命令的标准输出传递给管道作为后面的标准输入 1.文件查找 find find 进行文件查找时&#xff0c;默认进行递归查找&#xff0c;会查找隐藏目录下的文件 1.1 用法 # find 查找路径 查找条件... -type // 文件类型 f 普通文件 b 设备 d …

使用LLaMA-Factory微调大模型

使用LLaMA-Factory微调大模型 github 地址 https://github.com/hiyouga/LLaMA-Factory 搭建环境 git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory在 LLaMA-Factory 路径下 创建虚拟环境 conda create -p ./venv python3.10激活环境 c…

迅狐跨境电商系统源码:技术栈与多端集成

随着全球化贸易的不断深入&#xff0c;跨境电商系统源码成为了连接不同国家和地区消费者与商家的重要桥梁。本文将探讨跨境电商系统源码的技术栈以及如何通过多端集成来提升用户体验。 技术栈概览 跨境电商系统源码的技术栈是构建高效、稳定平台的基础。以下是构建跨境电商系…

Dynamics 365:安全的客户参与应用程序

客户参与应用程序使用Microsoft Dataverse提供了一个丰富的安全模型&#xff0c;可以适应许多业务场景。本节为您提供了应考虑的安全措施的特定于产品的指导。 Dataverse安全模型有以下目标&#xff1a; 只允许用户访问他们工作所需的信息。按角色对用户进行分组&#xff0c;并…

Leetcode刷题笔记6

34. 在排序数组中查找元素的第一个和最后一个位置 34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣&#xff08;LeetCode&#xff09; 解法一&#xff1a;暴力查找 [1, 2, 3, 3, 3, 4, 5] t 3 从前往后扫描暴力查找&#xff0c;最坏情况下O(N) 优化 利用数组有序的…

安泰电压放大器的设计要求有哪些内容

电压放大器是电子电路中常见的一种放大器类型&#xff0c;用于将输入电压信号放大到更高的幅度。设计一个高性能的电压放大器需要考虑多个方面的要求和指标。以下是设计电压放大器时需要考虑的几个重要内容&#xff1a; 1.放大增益 放大增益是电压放大器的核心指标&#xff0c;…

再度“痛失”TOP5的小米手机,能否接好这碗AI“大活水”?

国产手机终端需求持续修复&#xff0c;国产品牌商是最大受益者。 近日&#xff0c;中国信通院发布2024年4月国内手机市场运行分析报告。报告显示&#xff0c;今年4月&#xff0c;国内市场手机出货量同比增长了28.8%。按品牌来看&#xff0c;国产品牌手机4月出货量占同期手机出…

ubuntu 18.04 ros1学习

总结了一下&#xff0c;学习内容主要有&#xff1a; 1.ubuntu的基础命令 pwd: 获得当前路径 cd: 进入或者退出一个目录 ls:列举该文件夹下的所有文件名称 mv 移动一个文件到另一个目录中 cp 拷贝一个文件到另一个目录中 rm -r 删除文件 gedit sudo 给予管理员权限 sudo apt-…

HTTP/超文本传输协议(Hypertext Transfer Protocol)及HTTP协议通信步骤介绍和请求、响应阶段详解;

目录 一、HTTP/超文本传输协议 特点和功能 请求-响应模型 版本和扩展 安全性和加密 二、HTTP协议通信步骤介绍 三、请求、响应阶段详解 HTTP请求 HTTP响应 示例 一、HTTP/超文本传输协议 HTTP/超文本传输协议&#xff08;Hypertext Transfer Protocol&#xff09;是…