谈谈Android 安全策略SElinux

2023-05-16

不积跬步无以至千里,补全自己的短板,完善体系,站在巨人的肩膀上,看到的更远,写这篇文章也算是对这个知识点的总结。

一,背景

SElinux出现之前,Linux上的安全模型叫DAC(Discretionary Access Control 自主访问控制),它的核心思想就是:进程拥有的权限与执行它的用户权限相同,比如,以root用户启动camera, 那么camera就拥有root的用户权限,我们知道root的权限是很大的,可以说为所欲为。

所以DAC方式管理太过宽松,只要应用获得了root权限,可以在后台做很多我们不知道的事情,所以在4.4之前都建议不要root破解,我记得5.0之前还有很多应用提示“如果想要运行应用,需要root”,现在想想,不知道该应用做了哪些不可告人的事情。

后来,研究过AOSP的应该知道Android5.0算是一次大的跨度升级,在5.0上google启用了新的权限管理,增加了系统安全性,

SElinux是一种新的安全模型,称为MAC(Mandatory Access Control 强制访问控制),他的思想就是:任何进程想要在SElinux系统中干任何事, 都必须在安全策略配置文件中赋予权限,凡是没有出现在安全策略配置文件中的权限,进程没有权限操作任何事。

我们来看个例子

 下面这条SELinux语句表示 允许(allow )hal_nfc_default域(domain)中的进程  ”创建“类型为nfc_vendor_data_file的文件

allow hal_nfc_default nfc_vendor_data_file:file { create rw_file_perms };

如果没有在hal_nfc.te中使用上例中的权限配置allow语句,则hal_nfc_default就无法在data目录创建文件,即使hal_nfc_default具有root权限

DAC和MAC对比总结:

1)Android 系统先做DAC检查, 如果没有通过DAC检查,操作直接失败,通过DAC后,再做MAC权限检查。

2)SELinux中也有用户的概念,但和Linux中原有的user概念不同, Linux中的超级用户root在SELinux中可能就是一个没权限,没地位打打酱油的“路人甲”。

写这篇文章的目的是跟着前辈的经验,能够看懂现有的策略文件,能够编写新添加的权限文件或语句。

二,SELinux Policy 语言介绍

Linux有种思想,即万事万物皆文件,普通文件是文件,目录是文件,设备是文件。

SELinux中,每种东西都会被赋予一个安全属性,官方说法叫Security Context。Security Context(以后用SContext表示)是一个字符串,主要由三部分组成,分为进程和文件两种。

SEAndroid中,进程的SContext可通过ps -ZA命令查看,

最左边的LABEL列值进程的SContext,以第一个u:r:init:s0为例,说明代表含义

  • u为user的意思。SEAndroid中定义了一个SELinux用户,值为u。
  • r为role的意思。role是角色之意,它是SELinux中一种比较高层次,更方便的权限管理思路,即Role Based Access Control(基于角色的访问控制,简称为RBAC)。简单点说,一个u可以属于多个role,不同的role具有不同的权限。
  • init,代表该进程所属的Domain为init。MAC的基础管理思路其实不是针对上面的RBAC,而是所谓的Type Enforcement Accesc Control(简称TEAC,一般用TE表示)。对进程来说,Type就是Domain。比如init这个Domain有什么权限,都需要通过[例子1]中allow语句来说明。
  • S0和SELinux为了满足军用和教育行业而设计的Multi-Level Security(MLS)机制有关。简单点说,MLS将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问。

文件的Security Context,可通过ls -lZ命令查看,

上图中倒数第二列为手机内根目录下文件和目录的SContext信息,同样以第一行u:object_r:vendor_file:s0为例,说明代表含义

  • u:同样是user之意,它代表创建这个文件的SELinux user。
  • object_r:文件是死的东西,它没法扮演角色,所以在SELinux中,死的东西都用object_r来表示它的role。
  • vendor_file:死的东西的Type,和进程的Domain其实是一个意思,它表示odm目录对应的Type是vendor_file。
  • s0:MLS的级别。

根据SELinux规范,完整的SContext字符串为:

user:role:type[:range]

注意,方括号中的内容表示可选项,s0属于range中的一部分。

三,TE介绍

MAC(Mandatory Access Control 强制访问控制)基本管理单位是TEAC(Type Enforcement Access Control), 然后是高一级别的Role Based Accesc Control。RBAC是基于TE的,而TE也是SELinux中最主要的部分。我们常添加的allow就是TE的范畴。

3.1 根据 SELinux 规范,完整的 SELinux 策略规则语句格式为:

allow domains types:classes permissions;

- Domain - 一个进程或一组进程的标签。也称为域类型,因为它只是指进程的类型。
- Type - 一个对象(例如,文件、套接字)或一组对象的标签。
- Class - 要访问的对象(例如,文件、套接字)的类型。
- Permission - 要执行的操作(例如,读取、写入)。

= allow : 允许主体对客体进行操作
= neverallow :拒绝主体对客体进行操作
= dontaudit : 表示不记录某条违反规则的决策信息
= auditallow :记录某项决策信息,通常 SElinux 只记录失败的信息,应用这条规则后会记录成功的决策信息。

使用政策规则时将遵循的结构示例:

语句:
allow hal_nfc_default nfc_vendor_data_file:file { create rw_file_perms };
  • allow:TE的allow语句,表示授权。除了allow之外,还有allowaudit、dontaudit、neverallow等。
  • hal_nfc_default:source type,也叫subject,domain。
  • nfc_vendor_data_file:target type,代表其后的file所对应的Type.
  • file:代表Object Class,它代表能够给subject操作的一类东西。例如File、Dir、socket等。在Android系统中,有一个其他Linux系统没有的Object Class,那就是Binder。
  • create rw_file_perms:在该类Object Class中所定义的操作

下面根据几个实例了解权限的写法

#允许zygote域中的进程search或getattr类型为appdomain的目录。注意,多个perm_set

#可用{}括起来

allow zygote appdomain:dir { getattr search };

#来个复杂点的:

#source_type为unconfineddomain target_type为一组type,由
#{ fs_type dev_type file_type }构成。object_class也包含两个,为{ chr_file file }

#perm_set语法比较奇特,前面有一个~号。它表示除了{entrypoint relabelto}之外,{chr_file #file}这两个object_class所拥有的其他操作

allow unconfineddomain {fs_type dev_type file_type}:{ chr_file file }   \

 ~{entrypoint relabelto};

#特殊符号除了~外,还有-号和*号,其中:

# 1):-号表示去除某项内容。

# 2):*号表示所有内容。

#下面这条语句中,source_type为属于appdomain,但不属于unconfinedomain的进程。

#而 *表示所有和capability2相关的权限

#neverallow:表示绝不允许。

neverallow { appdomain -unconfineddomain } self:capability2 *;

 特别注意,前面曾提到说权限必须显示声明,没有声明的话默认就没有权限。那neverallow语句就没必要存在了。因为”无权限“是不需要声明的。确实如此,neverallow语句的作用只是在生成安全策略文件时进行检查,判断是否有违反neverallow语句的allow语句。

3.2 Object Class有哪些?

文件路径: system/sepolicy/private/security_classes

# file-related classes
class filesystem
class file  #代表普通文件
class dir   #代表目录
class fd    #代表文件描述符
class lnk_file  #代表链接文件
class chr_file  #代表字符设备文件

# network-related classes
class socket   #socket
class tcp_socket
class udp_socket

......
class binder   #Android 平台特有的 binder
class zygote   #Android 平台特有的 zygote

3.3 Permissions

指的是某种class 所拥有的权限,以file这种object class而言,拥有的permission如下

查看路径:system/sepolicy/private/access_vectors

#
# Define common prefixes for access vectors
#
# common common_name { permission_name ... }


#
# Define a common prefix for file access vectors.
#

common file
{
    ioctl
    read
    write
    create
    getattr
    setattr
    lock
    relabelfrom
    relabelto
    append
    map
    unlink
    link
    rename
    execute
    quotaon
    mounton
    audit_access
    open
    execmod
    watch
    watch_mount
    watch_sb
    watch_with_perm
    watch_reads
}

从上面例子可以看出SELinux定义perm set方式使用common命令,实际使用中我们可以参考该文件的定义

#除了common外,还有一种class命令也可定义perm set,如下面的例子:

#class命令的完整格式是:

#class class_name [ inherits common_name ] { permission_name ... }

#inherits表示继承了某个common定义的权限  注意,class命令定义的权限其实针对得就是

#某个object class。它不能被其他class继承

class dir inherits file {

   add_name  remove_name reparent search rmdir open audit_access execmod

}

#来看SEAndroid中的binder和property_service这两个Object class定义了哪些操作权限

class binder {

      impersonate  call set_context_mgr transfer }

class property_service { set }

3.4 type

type命令的完整格式为:type type_id [alias alias_id,] [attribute_id]

其中,方括号中的内容为可选。alias指定了type的别名,可以指定多个别名。

四,SELinux应用

上面理论讲述了一堆,估计你已经有点不耐烦,下面我们以实际使用解决问题来说说,

SELinux分两种模式:enforceing mode(限制访问) 和permissive mode(只审查权限,不限制)

4.1怎么关闭这个权限限制呢?

4.1.1 临时关闭,当你的手机是debug模式时可以通过adb shell设置

setenfroce 0

setenforce 命令修改的是 /sys/fs/selinux/enforce 节点的值,0 表示permissive,1 表示 enforcing,是 kernel 意义上的修改 selinux 的策略。系统重启后,节点值会复位

4.1.2永久关闭

SECURITY_SELINUX 设置为 false,重新编译 kernel

make bootimage 重新编译boot,刷机验证

4.1.3修改代码

设置 ro.boot.selinux=permissive 属性,并且修改在 system/core/init/Android.mk 中设置用于 user 版本下 selinux 模式为 permissive

ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
init_options += \
    -DALLOW_LOCAL_PROP_OVERRIDE=1 \
    -DALLOW_PERMISSIVE_SELINUX=1 \                                                                                              
    -DREBOOT_BOOTLOADER_ON_PANIC=1 \
    -DDUMP_ON_UMOUNT_FAILURE=1
else
init_options += \
    -DALLOW_LOCAL_PROP_OVERRIDE=0 \
    -DALLOW_PERMISSIVE_SELINUX=1 \ // 修改为1,表示允许 selinux 为 permissive
    -DREBOOT_BOOTLOADER_ON_PANIC=0 \
    -DDUMP_ON_UMOUNT_FAILURE=0
endif

4.2 系统运行权限不够,avc异常处理

在系统开发过程中,经常遇到的一个问题就是没有权限操作某个动作,直观的就是功能未生效。出现此类问题我们可以通过adb logcat 抓取系统log,过滤log中avc子串,看看是否有报对应进程的权限问题。或者关掉权限看看功能是否正常。

案例1,我们抓到一份异常

可以看到INxpNfc没有find权限,分析如下:

谁缺少权限:scontext=u:r:hal_nfc_default:s0  ---->hal_nfc_default

对什么缺少权限:tcontext=u:object_r:default_android_hwservice:s0 ---> default_android_hwservice

什么类型的权限:tclass=hwservice_manager ---> hwservice_manager

缺少什么权限:denied{find} --->find

找到关于nfc的te文件按照套路添加定义

allow scontext_type tcontext_type:tclass_type { denied }

故 allow hal_nfc_default default_android_hwservice:hwservice_manager {find}

也可以使用audit2allow工具生成这个sepolicy规则,路径:AOSP/external/selinux/prebuilts/bin

注意:使用这个工具,需要先 souce build/envsetup.sh -->lunch 之后,audit2allow -i avc_log.txt 会在终端输出,如果想输出到指定文件,使用-p 后面跟路径名

BOARD_SEPOLICY_DIRS += device/mediatek/sepolicy 通过这个命令添加厂家自定义的 sepolicy 规则

新添加的这个权限有可能会引起neverallowed,出现问题可以重新定义相同类型的type,以规避权限检查

案例2

SELinux : avc:  denied  { find } for interface=android.hardware.secure_element::ISecureElement sid=u:r:nfc:s0 pid=21355 scontext=u:r:nfc:s0 tcontext=u:object_r:hal_secure_element_hwservice:s0 tclass=hwservice_manager permissive=0

添加权限allow nfc hal_secure_element_hwservice:hwservice_manager find;

会发现编译报错

libsepol.report_failure: neverallow on line 5 of system/sepolicy/public/hal_secure_element.te (or line 19894 of policy.conf) violated by allow nfc hal_secure_element_hwservice:hwservice_manager { find };
libsepol.check_assertions: 1 neverallow failures occurred
Error while expanding policy

解决方案添加hal_client_domain(nfc, hal_secure_element)

4.3 SELinux Android 源码架构

- externel/selinux:包含编译 sepolicy 策略文件的一些实用构建工具
    - external/selinux/libselinux:提供了帮助用户进程使用 SELinux 的一些函数
    - external/selinux/libsepol:提供了供安全策略文件编译时使用的一个工具 checkcon
- system/sepolicy:包含 Android SELinux 核心安全策略(te 文件),编译生成 sepolicy 文件
    - file_contexts: 系统中所有文件的安全上下文
    - property_contexts: 系统中所有属性的安全上下文
    - seapp_contexts:定义用户、seinfo和域之间的关系,用于确定用户进程的安全上下文
    - sepolicy:二进制文件,保存系统安全策略,系统初始化时会把它设置到内核中

SELinux 虚拟文件系统在 sys/fs/selinux 下,该目录下的文件是 SELinux 内核和用户进程进行通信的接口,libselinux 就是利用这边的接口进行操作

 4.3.1 init进程SEAndroid启动源码分析

文件路径:/system/core/init/init.cpp

int main(int argc, char** argv) {
    ...
    if (is_first_stage) {
        ... 

        // Set up SELinux, loading the SELinux policy.
        selinux_initialize(true); 

        // We're in the kernel domain, so re-exec init to transition to the init domain now
        // that the SELinux policy has been loaded.
        if (selinux_android_restorecon("/init", 0) == -1) {
            PLOG(ERROR) << "restorecon failed";
            security_failure();
        }
        ...
    }
}

可以看到 SEAndroid 的启动设置在 init 进程内核态执行(first_stage)过程中,selinux_initialize 函数就是主要的初始化过程,包含加载 sepolicy 策略文件到内核的 LSM 模块中。

文件路径:/system/core/init/init.cpp

static void selinux_initialize(bool in_kernel_domain) {
    Timer t;

    selinux_callback cb;
    cb.func_log = selinux_klog_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);
    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    // 标识 init 进程的内核态执行和用户态执行
    if (in_kernel_domain) {
        LOG(INFO) << "Loading SELinux policy";
        if (!selinux_load_policy()) {
            panic();
        }

        bool kernel_enforcing = (security_getenforce() == 1);
        bool is_enforcing = selinux_is_enforcing();
        if (kernel_enforcing != is_enforcing) {
            if (security_setenforce(is_enforcing)) {
                PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
                security_failure();
            }
        }

        std::string err;
        if (!WriteFile("/sys/fs/selinux/checkreqprot", "0", &err)) {
            LOG(ERROR) << err;
            security_failure();
        }

        // init's first stage can't set properties, so pass the time to the second stage.
        setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
    } else {
        selinux_init_all_handles();
    }
}

selinux_set_callback
selinux_set_callback 用来向 libselinux 设置 SEAndroid 日志和审计回调函数

selinux_load_policy
这个函数用来加载 sepolicy 策略文件,并通过 mmap 映射的方式将 sepolicy 的安全策略加载到 SELinux LSM 模块中去。

下面具体分析一下流程:

文件路径:system/core/init/init.cpp

static bool selinux_load_policy() {
    return selinux_is_split_policy_device() ? selinux_load_split_policy()
                                            : selinux_load_monolithic_policy();
}

这里区分了从哪里加载安全策略文件,第一个是从 /vendor/etc/selinux/precompiled_sepolicy 读取,第二个是直接从根目录 /sepolicy
中读取,最终调用的方法都是 selinux_android_load_policy_from_fd

int selinux_android_load_policy_from_fd(int fd, const char *description)
{
    int rc;
    struct stat sb;
    void *map = NULL;
    static int load_successful = 0;

    /*
     * Since updating policy at runtime has been abolished
     * we just check whether a policy has been loaded before
     * and return if this is the case.
     * There is no point in reloading policy.
     */
    if (load_successful){
      selinux_log(SELINUX_WARNING, "SELinux: Attempted reload of SELinux policy!/n");
      return 0;
    }

    set_selinuxmnt(SELINUXMNT);
    if (fstat(fd, &sb) < 0) {
        selinux_log(SELINUX_ERROR, "SELinux:  Could not stat %s:  %s\n",
                description, strerror(errno));
        return -1;
    }
    map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (map == MAP_FAILED) {
        selinux_log(SELINUX_ERROR, "SELinux:  Could not map %s:  %s\n",
                description, strerror(errno));
        return -1;
    }

    rc = security_load_policy(map, sb.st_size);
    if (rc < 0) {
        selinux_log(SELINUX_ERROR, "SELinux:  Could not load policy:  %s\n",
                strerror(errno));
        munmap(map, sb.st_size);
        return -1;
    }

    munmap(map, sb.st_size);
    selinux_log(SELINUX_INFO, "SELinux: Loaded policy from %s\n", description);
    load_successful = 1;
    return 0;
}

(1)set_selinuxmnt 函数设置内核 SELinux 文件系统路径,这里的值为 /sys/fs/selinux,SELinux 文件系统用来与内核空间 SELinux LSM 模块空间。

(2)通过 fstat 获取 sepolicy 文件(fd 值)的状态信息,通过 mmap 函数将文件内容映射到内存中,起始地址为 map

(3)security_load_policy 调用另一个 security_load_policy 函数将已经映射到内存中的 SEAndroid 的安全策略加载到内核空间的 SELinux LSM 模块中去。

int security_load_policy(void *data, size_t len)
{
    char path[PATH_MAX];
    int fd, ret;

    if (!selinux_mnt) {
        errno = ENOENT;
        return -1;
    }

    snprintf(path, sizeof path, "%s/load", selinux_mnt);
    fd = open(path, O_RDWR | O_CLOEXEC);
    if (fd < 0)
        return -1;

    ret = write(fd, data, len);
    close(fd);
    if (ret < 0)
        return -1;
    return 0;
}

函数 security_load_policy 的实现很简单,它首先打开 /sys/fs/selinux/load 文件,然后将参数 data 所描述的安全策略写入到这个文件中去。由于 /sys/fs/selinux 是由内核空间的 SELinux LSM 模块导出来的文件系统接口,因此当我们将安全策略写入到位于该文件系统中的 load 文件时,就相当于是将安全策略从用户空间加载到 SELinux LSM 模块中去了。

以后 SELinux LSM 模块中的 Security Server 就可以通过它来进行安全检查

(4)加载完成,释放 sepolicy 文件占用的内存,并且关闭 sepolicy 文件

selinux_init_all_handles
在 init 进程的用户态启动过程中会调用这个函数初始化 file_context、 property_context 相关内容 handler,根据前面的描述,init 进程给一些文件或者系统属性进行安全上下文检查时会使用 libselinux 的 API,查询文件根目录下 file_context、file_context 的安全上下文内容。所以需要先得到这些文件的 handler,以便可以用来查询系统文件和系统属性的安全上下文。

static void selinux_init_all_handles(void)
{
    sehandle = selinux_android_file_context_handle();
    selinux_android_set_sehandle(sehandle);
    sehandle_prop = selinux_android_prop_context_handle();
}

4.3.2 SEAndroid 编译

audit2allow -i avc_log.txt 即可自动输出生成的policy 

source build/envsetup.sh

lunch

mma system/sepolicy/
adb push out/target/product/xx/vendor/etc/selinux/ vendor/etc/selinux/
adb push out/target/product/xx/system/etc/selinux/ system/etc/selinux/

参考:

深入理解SELinux/SEAndroid(第一部分) - 邓凡平的个人页面 - OSCHINA - 中文开源技术交流社区

android 8.1 安全机制 — SEAndroid & SELinux_岁月斑驳7的博客-CSDN博客_android selinux

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

谈谈Android 安全策略SElinux 的相关文章

  • kendoGrid合并单元格

    kendoGrid默认API中并没有合并单元格的方法 xff0c 故自己借鉴网友的代码并做了改造 xff0c 下述代码及完成了kendoGrid中的行的合并 xff08 在 dataBound事件中完成 xff09 span class t
  • Git合并指定文件到其他分支

    1 合并某个分支上的指定commit span class token comment 在dev分支合并bugfix分支上的 ac0ca63 commit span git branch span class token operator
  • shell命令—find

    find命令 span class token comment 删除 home fengshuiyue目录下一周前的目录 span fengshuiyue 64 ralc span class token operator gt span
  • shell命令—date

    date命令 span class token comment 获取当前时间 日期格式是 YYYY mm dd HH MM SS span span class token function date span span class tok
  • 大华硬盘录像机、网络摄像机、 网络硬盘录像机外网远程设置DDNS方法

    1 为了便于解释在下文介绍中 xff0c 硬盘录像机 网络摄像机 网络硬盘录像机统一称为 大华设备 2 外网最好是电信 如果不是 xff0c 那必须要确认客户的外网 IP 是唯一的 xff0c 不是与其他用户共用的 3 首先确保 大华设备
  • LibCurl HTTP部分详细介绍

    目录索引 xff1a 一 LibCurl基本编程框架 二 一些基本的函数 三 curl easy setopt函数部分选项介绍 四 curl easy perform 函数说明 xff08 error 状态码 xff09 五 libcurl
  • navicat连接oracle报错:ORA-12737 Instant Client Light:unsupported server character set ZHS16GBK

    今天使用Navicat连接Oracle数据库 xff0c 报了下面的这个错误 xff1a ORA 12737 Instant Client Light unsupported server character set ZHS16GBK 从这
  • RabbitMQ 与 Kafka深度解析(二)

    介绍 作为一名处理大量基于微服务的系统的软件架构师 xff0c 我经常会遇到一个反复出现的问题 xff1a 我应该使用RabbitMQ还是Kafka xff1f 出于某种原因 xff0c 许多开发人员认为这些技术是可以互换的 虽然在某些情况
  • VMWare报错"指定的文件不是虚拟磁盘"或“The file specified is not a virtual disk”

    今天打开原来创建的虚拟机 xff0c 突然报错 指定的文件不是虚拟磁盘 xff0c 如下图 xff1a 由于之前这个虚拟机创建了快照 xff0c 因此下面的解决方法是基于快照的 1 打开虚拟机的 vmx文件 xff0c 我的虚拟机名字为 U
  • Javascript模块加载框架——seajs

    最近看了一些开源web的项目 xff0c 发现其前台采用的框架seajs在编写JavaScript代码上很是方便 xff0c 现将学习的记录记于此 1 什么是JavaScript模块加载 为了解决不同javascript库里操作对象的命名冲
  • JS实现浏览器打印、打印预览

    目前正在做浏览器端采用JS方式实现打印这么一个功能 xff0c JS打印实现的方法很多 xff0c 但是兼容各个浏览器实现打印预览的功能有些棘手 xff0c 现将实现的内容及遇到的问题记录下来 xff0c 希望有大牛看到所提的问题后可以给予
  • 日期与天干地支算法

    天干地支五行对照表 天干 地支与五行的对应表 甲 乙 丙 丁 戊 己 庚 辛 壬 癸 阳 阴 阳 阴 阳 阴 阳 阴 阳 阴 木 木 火 火 土 土 金 金 水 水 子 丑 寅 卯 辰 巳 午 未 申 酉 戌 亥 鼠 牛 虎 兔 龙 蛇 马
  • TR技术评审节点

    产品开发中 xff0c TR是技术评审节点 下面是某产品的技术评审点 xff0c 供参考 xff1a TR1 概念阶段技术评审点 xff1a 产品需求和概念技术评审 xff08 业务需求评审 xff09 是ISO IEC TR 19768
  • linux虚拟机重新启动后不能联网的问题

    这里解决的是linux中 xff0c 曾经能连上网的情况下 xff0c 重启后或一段时间没上后 xff0c 不能联网的问题 xff08 安装后就没连上网 xff0c 就乖乖安装各种教程来一下啊 xff0c 这里可能不一定适合你 当然 xff
  • Spring MVC执行流程

    Spring MVC 框架是高度可配置的 xff0c 包含多种视图技术 xff0c 例如 JSP FreeMarker Tiles iText 和 POI Spring MVC 框架并不关心使用的视图技术 xff0c 也不会强迫开发者只使用
  • 数据分析必备——SQL入门基础知识

    数据说 梦想季 一直很喜欢一句话 xff1a 山鸟与鱼不同路 xff0c 从此山水不相逢 意思就是如果你现在不够优秀 xff0c 即使遇见了 xff0c 也不配拥有 努力是唯一的方向 xff01 导读 xff1a 科学技术的快速发展正在改变
  • CentOS6.5安装nginx

    转载 xff1a http blog csdn net yinwenjie article details 46620711 2 Nginx的安装 2 1 准备工作 操作系统 xff1a centOS 6 5 Nginx的下载地址 xff1
  • Spring源码剖析之AbstractApplicationContext抽象类的refresh()方法

    Spring源码剖析之AbstractApplicationContext抽象类的refresh 方法 简书 Spring中AbstractApplicationContext抽象类的refresh 方法是用来刷新Spring的应用上下文的
  • Python多进程任务如何识别谁是谁

    在Python多进程 多线程编程中 xff0c 如果使用future和add done callback 经常会遇到如何区分异步任务的问题 这里推荐两种方式 方式一 xff1a 给future新增属性 def call back futur
  • spring事务配置的五种方式

    https blog csdn net m8396017 article details 51615806 spring事务配置的五种方式 第一种方式 xff1a 每个Bean都有一个代理 第二种方式 xff1a 所有Bean共享一个代理基

随机推荐

  • dependency 和dependencyManagement 的区别

    https blog csdn net m0 37664223 article details 105836943 dependencyManagement 和dependency 的区别 dependencyManagement 我们项目
  • Ubuntu系统使用root远程登录

    https blog csdn net weixin 45239621 article details 115523031 Ubuntu系统使用root远程登录的操作 一般这种情况是某某云服务器或者新的Ubuntu系统 xff08 仅Ubu
  • npm 安装详细教程

    https blog csdn net Cleve baby article details 125632341 转载 xff1a http xiaoyaojones blog 163 com blog static 28370125201
  • Linux新建用户,切换后只显示$问题

    问题 xff1a linux新建用户 xff0c 切换后只显示 的问题 xff0c 而且有些命令也使用不了 xff0c 解决方法如下 1 新建用户命令 root登录 useradd d usr sam m jiang 此命令创建了一个用户j
  • DTI(dwi)使用FSL做预处理及做TBSS处理流程(fsleyes查看结果)

    预处理 刚开始我用的数据初始文件是多个dcm格式的文件 xff0c 当时我从格式转换 提取b0 波脑 涡流 计算张量下来是没有问题的 后来我用dwi的文件 xff08 包含四维的 nii gz bvec bval文件 xff09 xff0c
  • 引用的问题

    1 二者的区别 xff08 1 xff09 引用 访问一个变量是直接访问 xff0c 而指针是间接访问 xff08 2 xff09 引用 是一个变量的别名 xff0c 本身不单独分配自己的内存空间 xff0c 而指针有自己的内存空间 xff
  • Android抓取log文件的方法

    很简单 xff0c 使用命令行来就可以 xff0c 步骤如下 xff1a 1 adb devices 检查调试设备是否连接好 xff0c 没有调试设备抓不了 2 adb logcat c 3 cd Desktop 进入你要存放日志文件的目录
  • ESLint 配置入门

    大家好 xff0c 我是前端西瓜哥 xff0c 今天带大家了解 ESLint 的配置项 ESLint 是一款检查 JavaScript 程序是否符合特定的规则的工具 比如字符串用单引号还是双引号 xff0c tab 缩进用 2 个空格还是
  • linux开机自动登陆

    在超级用户的身份下 编辑vim etc gdm custom conf span class hljs number 1 span span class hljs comment GDM configuration storage span
  • Python(Web服务)全链路日志个跟踪

    1 背景 在我们的实际项目中 xff0c 尤其以Web服务为例 xff0c 经常遇到要做日志跟踪的场景 我们经常采用的方式是 xff0c 生成一个trace id 在全链路的调用中都输出这个id进行跟踪 这里需要处理的几个问题是 xff1a
  • BCLinux用yum安装报Your license is invalid.

    1 介绍 BCLinux基于 CentOS 进行定制化 xff0c 满足企业对于通用 Linux 操作系统的需求 xff0c 提供标准化 平台化的产品发布及运行环境 xff0c 该版本与红帽商业版本及社区版本完全兼容 2 步骤 安装好操作系
  • Excel表格数据如何批量乘以一个数字

    今天跟大家分享一下Excel表格数据如何批量乘以一个数字 1 打开Excel文件 xff0c 我们想要批量将数字乘以10 2 首先我们选择所有数据单元格区域 3 点击下图选项 xff08 Excel工具箱 xff0c 百度即可了解详细下载安
  • Win2003系统部署SSL证书(部署https教程)

    在windows 2003操作系统下 xff0c IIS 6 环境的服务器SSL证书安装教程 安装前 xff0c 请准备好SSL证书 部署前请退出服务器内安装的杀毒软件 xff08 360 金山 安全狗等安全软件有可能导致SSL证书部署出错
  • Android启动模式之singleinstance的坑

    Android启动模式之singleinstance的坑 前言 在实际应用中 xff0c 使用singleinstance启动模式时 xff0c 会遇到一些奇奇怪怪的问题 Android有四种启动模式 xff0c 分别是standard x
  • 关于使用iconfont图标总生成小长方形框的解决办法

    起因是在联系仿写网易的静态页面的时候 xff0c 使用iconfont小图标的时候 xff0c 引入正确 xff0c 但是在页面上一直显示的是一个长方形小框的页面 各种搜索终于找到了解决办法 主要原因是由于iconfont css中路径不对
  • Qt和MFC的效率对比

    Qt和MFC的效率对比 之前一直做mfc xff0c 昨天看了一晚上的Qt xff0c 瞬间就喜欢上它了 xff0c Qt在windows下应该没有mfc的运行效率高 xff0c 但是我想知道差多少 xff0c 不知有没有大牛做过这方面的对
  • android 移植

    from http wiki kldp org wiki php AndroidPortingOnRealTarget s 6 1 Contents 1 Introduction 2 Copyright and Acknowledgemen
  • LD3320语音识别模块+JQ8900-TF语音模块实现简单的语音交互控制

    玩这个真的要感谢头条的强大推荐 xff0c 清明节回家的大巴车上 xff0c 无聊的刷着头条 xff0c 很智能的给我推荐了一款语音识别模块 xff0c 一直很想自己试着玩一把的我 xff0c 按奈不住 xff0c 点开视频看到了介绍 xf
  • 智能配网方案 Airkiss 技术原理介绍及应用

    写在前面 站在巨人的肩膀上 xff0c 可以看得更远 一 什么是Air Kiss 可以说AirKiss 是微信硬件平台提供的一种WIFI硬件设备快速配置连接网络的技术 xff0c 是一种创新性的信息传递技术 通过该技术可以便捷的向一台具有W
  • 谈谈Android 安全策略SElinux

    不积跬步无以至千里 xff0c 补全自己的短板 xff0c 完善体系 xff0c 站在巨人的肩膀上 xff0c 看到的更远 xff0c 写这篇文章也算是对这个知识点的总结 一 xff0c 背景 SElinux出现之前 xff0c Linux