Android SELinux安全机制与权限管理那些事

news2024/12/25 22:16:28

文章目录

  • 前言
  • 权限管理
    • 系统应用
    • 特权应用
    • 历史漏洞
      • 广播的保护机制
      • CVE-2020-0391
  • SELinux

前言

在 Android 漏洞挖掘和安全研究过程中,不可避免地会涉及到跟 Android SELinux 安全机制打交道,比如当你手握一个 System 应用的路径穿越的漏洞的时候想去覆写其他应用沙箱的可执行文件的时候,SELinux 极大可能成为你成功路上最大的拦路虎……

通过 ps -efZ 命令可以查看 Android 系统当前运行的进程信息和 SELinux 标签信息:
在这里插入图片描述 可以看到 Android 系统在 SELiunx 上对应用标签划分了 系统应用(system_app)、特权应用(priv_app)、平台签名应用(platform_app)、不可信应用(untrusted_app)等。

同时,Android 的权限管理机制又对权限做了以下几类划分(具体可参见Android官方文档):
在这里插入图片描述
可以看到,权限管控这里又出现了 system 应用或 privileged 应用才能使用的权限,那么这里的 system 应用或 privileged 应用跟 SELinux 机制的 系统应用(system_app)、特权应用(priv_app)是否有区别?下面逐一来学习下。

【补充】可以使用adb shell dumpsys package permision |grep -i prot命令来获取权限等级。

权限管理

先来看下 Android 权限管控机制里的 system 应用或 privileged 应用是如何区分和定义的。

先附上两篇不错的参考文章,以表敬意:

  1. Android 权限的一些细节;
  2. “系统应用”与CVE-2020-0391”。

系统应用

第1类 System App:特殊的 Uid

追溯 AOSP 源码,可以看到 Android 对 system app 的判断逻辑为:具有 ApplicationInfo.FLAG_SYSTEM 标记的,被视为 System app。

//frameworks/base/core/java/android/content/pm/ApplicationInfo.java
    public boolean isSystemApp() {
        return (flags & ApplicationInfo.FLAG_SYSTEM) != 0;
    }

而 FLAG_SYSTEM 标记由 PackageManagerService 的构造函数中赋予:

//frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.se", SE_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

可以看到:

android.uid.system
android.uid.phone
android.uid.log
android.uid.nfc
android.uid.bluetooth
android.uid.shell
android.uid.se
android.uid.networkstack
android.uid.uwb

等这类具备特殊 shared uid 的 app 都被赋予了 ApplicationInfo.FLAG_SYSTEM 标志,这是第一类 System App。

第2类 System App:特殊的路径

在 InitAppsHelper 下可以看到以下扫描 System app 路径的函数:

   //frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java
   private void scanSystemDirs(PackageParser2 packageParser, ExecutorService executorService) {
        File frameworkDir = new File(Environment.getRootDirectory(), "framework");

        // Collect vendor/product/system_ext overlay packages. (Do this before scanning
        // any apps.)
        // For security and version matching reason, only consider overlay packages if they
        // reside in the right directory.
        for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
            final ScanPartition partition = mDirsToScanAsSystem.get(i);
            if (partition.getOverlayFolder() == null) {
                continue;
            }
            scanDirTracedLI(partition.getOverlayFolder(), /* frameworkSplits= */ null,
                    mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
                    packageParser, executorService);
        }

        scanDirTracedLI(frameworkDir, null,
                mSystemParseFlags,
                mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
                packageParser, executorService);
        if (!mPm.mPackages.containsKey("android")) {
            throw new IllegalStateException(
                    "Failed to load frameworks package; check log for warnings");
        }

        for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
            final ScanPartition partition = mDirsToScanAsSystem.get(i);
            if (partition.getPrivAppFolder() != null) {
                scanDirTracedLI(partition.getPrivAppFolder(), /* frameworkSplits= */ null,
                        mSystemParseFlags,
                        mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag,
                        packageParser, executorService);
            }
            scanDirTracedLI(partition.getAppFolder(), /* frameworkSplits= */ null,
                    mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
                    packageParser, executorService);
        }
    }

对于安装到 mDirsToScanAsSystem 里面的应用会设置 msystemScanFlags(这个标记仅在 PMS 内部使用,实际上会对应上 ApplicationInfo.FLAG_SYSTEM):

private final int mSystemScanFlags;
static final int SCAN_AS_SYSTEM = 1 << 16;
……
mSystemScanFlags = mScanFlags | SCAN_AS_SYSTEM;

而 mDirsToScanAsSystem 的来源如下:

//frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java
final class InitAppsHelper {
    private final PackageManagerService mPm;
    private final List<ScanPartition> mDirsToScanAsSystem;
    ……
    InitAppsHelper(PackageManagerService pm, ApexManager apexManager,InstallPackageHelper installPackageHelper,
        List<ScanPartition> systemPartitions) { //这里的systemPartitions正是下文的scanPartitions.addAll(mSystemPartitions);
        mPm = pm;
        ……
        mDirsToScanAsSystem = getSystemScanPartitions();
        ……
    }
    ……
    private List<ScanPartition> getSystemScanPartitions() {
        final List<ScanPartition> scanPartitions = new ArrayList<>();
        scanPartitions.addAll(mSystemPartitions);
        scanPartitions.addAll(getApexScanPartitions());
        Slog.d(TAG, "Directories scanned as system partitions: " + scanPartitions);
        return scanPartitions;
    }

不得不说往下追溯 mSystemPartitions 复杂到让人有点犯迷糊了,此处直接放结果吧:

   //frameworks/base/core/java/android/content/pm/PackagePartitions.java
   /**
     * The list of all system partitions that may contain packages in ascending order of
     * specificity (the more generic, the earlier in the list a partition appears).
     */
    private static final ArrayList<SystemPartition> SYSTEM_PARTITIONS =
            new ArrayList<>(Arrays.asList(
                    new SystemPartition(Environment.getRootDirectory(),
                            PARTITION_SYSTEM, Partition.PARTITION_NAME_SYSTEM,
                            true /* containsPrivApp */, false /* containsOverlay */),
                    new SystemPartition(Environment.getVendorDirectory(),
                            PARTITION_VENDOR, Partition.PARTITION_NAME_VENDOR,
                            true /* containsPrivApp */, true /* containsOverlay */),
                    new SystemPartition(Environment.getOdmDirectory(),
                            PARTITION_ODM, Partition.PARTITION_NAME_ODM,
                            true /* containsPrivApp */, true /* containsOverlay */),
                    new SystemPartition(Environment.getOemDirectory(),
                            PARTITION_OEM, Partition.PARTITION_NAME_OEM,
                            false /* containsPrivApp */, true /* containsOverlay */),
                    new SystemPartition(Environment.getProductDirectory(),
                            PARTITION_PRODUCT, Partition.PARTITION_NAME_PRODUCT,
                            true /* containsPrivApp */, true /* containsOverlay */),
                    new SystemPartition(Environment.getSystemExtDirectory(),
                            PARTITION_SYSTEM_EXT, Partition.PARTITION_NAME_SYSTEM_EXT,
                            true /* containsPrivApp */, true /* containsOverlay */)));

因此可以得出,以下路径下的应用均被 PMS 视为 System App:

/system/framework
/system/app
/system/priv-app
/system_ext/app
/system_ext/priv-app
/system_ext/overlay
/odm/app
/odm/priv-app
/odm/overlay
/oem/app
/oem/overlay
/vendor/app
/vendor/priv-app
/vendor/overlay
/product/app
/product/priv-app
/product/overlay

特权应用

Privileged app,我们称之为 特权 app,主要原因是此类特权 app 可以使用 protectionLevel 为 signatureOrSystemsignature|privileged 的权限。实际上上面已经解释过这两个 protectionLevel 的关系,signature|privileged 已替代了 signatureOrSystem

//frameworks/base/core/java/android/content/pm/PermissionInfo.java
public static int fixProtectionLevel(int level) {
    if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
       level = PROTECTION_SIGNATURE | PROTECTION_FLAG_PRIVILEGED;
    }
    ……
    return level;
}

回归到特权应用的定义:从 ApplicationInfo 的 isPrivilegedApp() 可以看出特权 app 是具有 ApplicationInfo.PRIVATE_FLAG_PRIVILEGED 标志的一类 app。

//frameworks/base/core/java/android/content/pm/ApplicationInfo.java
public boolean isPrivilegedApp() {
    return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
}

那么哪些应用会具备 ApplicationInfo.PRIVATE_FLAG_PRIVILEGED 标志?同样有两类。

第1类 Privileged App:特殊的 Uid

前面介绍 System App 的时候已经看到了,PMS 给部分特殊 Shared uid 赋予 System Flag 的同时也赋予了特权 Flag:

//frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.se", SE_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

故在 PMS 中,以下 Uid 的应用既属于 System App,又属于 Privileged App:

/system/framework
/system/app
/system/priv-app
/system_ext/app
/system_ext/priv-app
/system_ext/overlay
/odm/app
/odm/priv-app
/odm/overlay
/oem/app
/oem/overlay
/vendor/app
/vendor/priv-app
/vendor/overlay
/product/app
/product/priv-app
/product/overlay

第2类 Privileged App:特殊的路径

不翻源码了,直接看 官方说明文档 吧:
在这里插入图片描述
简而言之,以下路径的应用被视为特权应用:

/system/framework
/system/priv-app
/system_ext/priv-app
/odm/priv-app
/vendor/priv-app
/product/priv-app

注意,并非特权应用就可以申请并默认拥有所有 signature|privileged 权限,特权应用想使用某些系统特许权限,需要把白名单添加到 privapp-permissions-platform.xml 文件中(当然也可以单独建立一个文件,例如 com.android.systemui.xml 就是 SystemU I的特权白名单文件)。

关于特权白名单的更多信息也可参考:Android系统开发的特权白名单。

AOSP 包含可根据需要自定义的许可名单实现, 对于包含在 AOSP 中的应用,其权限已在 /etc/permissions/privapp-permissions-platform.xml 中列入许可名单。如果有不应授予的权限,请修改 XML,用 “deny-permission” 标记代替 “permission” 标记。示例:

<!-- This XML file declares which signature|privileged permissions to grant to
privileged apps that come with the platform -->

    <permissions>
    <privapp-permissions package="com.android.backupconfirm">
    <permission name="android.permission.BACKUP"/>
    <permission name="android.permission.CRYPT_KEEPER"/>
    </privapp-permissions>

    <privapp-permissions package="com.android.cellbroadcastreceiver">

    <!-- Don't allow the application to interact across users -->

    <deny-permission name="android.permission.INTERACT_ACROSS_USERS"/>
    <permission name="android.permission.MANAGE_USERS"/>
    <permission name="android.permission.MODIFY_PHONE_STATE"/>
    <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
    <permission name="android.permission.RECEIVE_EMERGENCY_BROADCAST"/>
    </privapp-permissions>
    ...

最后,简单总结下系统应用和特权应用的区分:

  1. Privileged app 一定也属于 System app,即 System app = Normal System app + Privileged app。
  2. 当我们说起 system app 时,通常指的是前面提到的特定 uid 和特定目录中的 app,包含了普通的 system app 和 特权 app;
  3. 而当我们说起有访问 System 权限或者 privileged 权限的 app 时,通常指特权 app;
  4. 平台签名应用指的是应用的签名和 Android 包一样的应用,和是否是系统应用、特权应用无关。

历史漏洞

要介绍的同样源于 “系统应用”与CVE-2020-0391” 讲述的关于上述 “系统应用” 划分机制出现的历史漏洞。

广播的保护机制

Android 广播机制中有些广播并不是所有应用都可以随意发送的,比如可以在 frameworks/base/core/res/AndroidManifest.xml 看到 AOSP 定义的受保护的广播:
在这里插入图片描述
问题1:哪些应用可以发送这些受保护的广播?

看 AMS 的源码 可以知道有 7 个特殊 Uid 的应用和 isPersistent=true 的应用可以发送,否则将抛出安全异常:
在这里插入图片描述
问题2:那哪些应用可以定义受保护的广播?

在常规应用想定义受保护的广播是不被允许的:
在这里插入图片描述
但是看源码发现 “com.android.providers.telephony” 或 “com.android.systemui” 等应用却可以定义:
在这里插入图片描述
引用大佬的分析,ScanPackageUtils.java 文件中的 applyPolicy 函数中 if ((scanFlags & SCAN_AS_SYSTEM) != 0) 对立分支存在注释:“Non system apps cannot mark any broadcast as protected”:
在这里插入图片描述
即 system app 无法定义受保护广播,那么反过来就是 system app 能将广播标志为受保护的状态。

CVE-2020-0391

先看下美国国家漏洞库的相关描述:
在这里插入图片描述

有点狠,由于未强制执行的受保护广播,可能会以系统身份执行任意命令……

此漏洞于 2020年9月谷歌安全公告 发布,受影响版本仅限于 Android 9 和 Android 10:
在这里插入图片描述
Android 9 以前的版本不受影响,那肯定是 “改出来的” 漏洞……

直接看 Google 的 补丁代码:
在这里插入图片描述
可以看到,清除保护广播定义的代码跑到了 if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) 的条件下了?

也就是说,只有特权应用能定义保护广播了,普通系统应用并不行。然而从上面的章节可以知道,特权应用的范围比系统应用小很多。那么那些原先由非特权的系统应用定义受保护的广播怎么办?还能生效且受系统保护吗?答案当然是不能……

漏洞修改方案是把错误分支代码改回去:
在这里插入图片描述
上文提到该漏洞的影响是任意命令执行,那么是哪个失效的“受保护”广播的杰作?

答案是紧随其后 2020年10月谷歌安全公告 公布的 CVE-2020-11164,但是它归属于闭源组件……
在这里插入图片描述
看不到修改的代码,那就引用大佬的分析吧。

漏洞出现在一个名为 Perfdump 的系统应用上,路径是/system/app/Perfdump/Perfdump.apk,显然这不是一个特权应用,所以它定义的保护广播会失效,那么它定义了哪些没被保护的保护广播呢?其中之一是:

<protected-broadcast android:name="android.perfdump.action.EXT_EXEC_SHELL"/>

一看这名字觉得不得了,不会是一个执行代码的功能吧,猜对了,真的是这样。而且这个应用还是 system uid,也就是说,普通应用可以发一个广播,然后以 system 的身份执行命令。

POC 程序如下:

Intent intent = new Intent("android.perfdump.action.EXT_EXEC_SHELL");
intent.setClassName("com.qualcomm.qti.perfdump", "com.qualcomm.qti.perfdump.StaticReceiver");
intent.putExtra("callerPackageName", "com.test");
intent.putExtra("shellCommand", <command_to_execute>);
sendBroadcast(intent);

SELinux

参考文章:

  1. SELinux在Android中的应用;
  2. Android 用户组权限,SELinux心得总结;
  3. Android 8.1 安全机制 — SEAndroid & SELinux;
  4. Android 11魔形女系列漏洞分析;

未完待续……先留存个挺清晰的图:
在这里插入图片描述

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

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

相关文章

DS:基于鸢尾花数据集利用多种数据降维技术(PCA、SVD、MDS、LDA、T-SNE)实现三维可视化

DS&#xff1a;基于鸢尾花数据集利用多种数据降维技术(PCA、SVD、MDS、LDA、T-SNE)实现三维可视化 目录 基于鸢尾花数据集利用多种数据降维技术(PCA、SVD、MDS、LDA、T-SNE)实现三维可视化 # 1、加载示例数据集&#xff08;鸢尾花数据集&#xff09; # 2、数据预处理 # T1、…

媒体专访是品牌初创阶段宣传的重要手段

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 很早以前&#xff0c;有位前辈跟我讲&#xff0c;人的成功分两种&#xff0c;一种是借助平台成功&#xff0c;一种是自己创业成功&#xff0c; 前者成功的概率很大&#xff0c;只需要选好…

【5.18】二、黑盒测试方法—因果图与决策表法

目录 2.3 因果图与决策表法 2.3.1 因果图设计法 2.3.2 决策表 2.3.3 实例&#xff1a;三角形决策表 2.3.4 实例&#xff1a;工资发放决策表 2.3 因果图与决策表法 等价类划分法与边界值分析法主要侧重于输入条件&#xff0c;却没有考虑这些输入之间的关系&#xff0c;如…

单链表和双向链表如何执行删除操作

在实际的软件开发中&#xff0c;从链表中删除一个数据无外乎这两种情况&#xff1a; 删除结点中“值等于某个给定值”的结点&#xff1b;删除给定指针指向的结点。 1.从头结点开始一个一个依次遍历对比&#xff0c;直到找到值等于给定值的结点&#xff0c;然后再通过我前面讲…

LeetCode高频算法刷题记录4

文章目录 1. 二叉树的最近公共祖先【中等】1.1 题目描述1.2 解题思路1.3 代码实现 2. 全排列【中等】2.1 题目描述2.2 解题思路2.3 代码实现 3. 相交链表【简单】3.1 题目描述3.2 解题思路3.3 代码实现 4. 合并 K 个升序链表【困难】4.1 题目描述4.2 解题思路4.3 代码实现 5. 环…

chatgpt赋能Python-python3_4怎么下载

Python3.4的下载和安装 Python是一种流行的编程语言&#xff0c;它被广泛用于算法学习、数据分析和网站开发等领域。Python3.4是Python编程语言的一个版本&#xff0c;它在性能和功能方面都得到了改进。如果你希望学习Python3.4或者使用它开发项目&#xff0c;那么你需要下载并…

Jetpack Compose中的附带效应简介及使用

前言 附带效应是指LaunchedEffect、DisposableEffect、rememberCoroutineScope、rememberUpdatedState、produceState 、derivedStateOf的使用。附带效应这4个字在google官方文档上的表达与解释挺让人难以理解的。其实个人认为准确的描述应该是外部产生的数据向Compose状态作用…

chatgpt赋能Python-python3_8_1怎么用

Python3.8.1使用指南&#xff1a;让您的编程更加高效 Python是一种流行的高级编程语言&#xff0c;它以简洁明了的语法和丰富的库而著称。如果您是一名新手或有经验的程序员&#xff0c;Python都是一个很好的选择。在Python3.8.1中&#xff0c;新的功能和改进将进一步提高Pyth…

chatgpt赋能Python-python2的n次方程序

Python2的N次方程序&#xff1a;一个实用的编程工具 Python2是广泛使用的编程语言之一&#xff0c;它是一种强大且灵活的开源语言&#xff0c;被广泛应用于科学计算、数据分析、Web开发等领域。在Python2中&#xff0c;N次方程序是一种非常基础的程序&#xff0c;但是它在实际…

二叉树的基本认识(一)

要了解二叉树&#xff0c;就必然要知道什么是树&#xff0c;树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的…

C++类模板的具体化

目录 分类 代码 分析 一、类模板 一&#xff09;代码 二&#xff09;注意事项 三&#xff09;运行结果 二、完全具体化的模板类 一&#xff09;代码 二&#xff09;注意事项 三&#xff09;执行结果 三、部分具体化的模板类 一&#xff09;代码 二&#xff09;注…

【研发工具】Yapi接口管理平台内网Centos8下搭建

1 环境依赖安装 环境要求 nodejs&#xff08;7.6) &#xff08;本文安装12.18.3&#xff09; mongodb&#xff08;2.6&#xff09;&#xff08;本文安装5.0.17&#xff09; 1.1 安装Nodejs 1.1.1 下载安装包 下载地址&#xff1a;https://nodejs.org/zh-cn/download/这里下载…

代码随想录训练营Day42|背包问题

目录 学习目标 学习内容 416. 分割等和子集 学习目标 01背包问题&#xff0c;你该了解这些&#xff01; 01背包问题&#xff0c;你该了解这些&#xff01; 滚动数组 416. 分割等和子集 学习内容 problems/背包理论基础01背包-1.md programmercarl/leetcode-master&#…

Java【网络编程2】详解ServerSocket和Socket类, 逐行代码解析如何服务器客户端通信(附代码)

文章目录 前言一、认识 Socket(套接字), TCP 协议和 UDP 协议1, 什么是 Socket(套接字)2, 浅谈 TCP 协议和 UDP 协议的区别和特点 二、基于 TCP 协议的 Socket API1, ServerSocket 类2, Socket 类 三、逐行代码解析网络编程1, 逐行解析客户端1.1, 核心成员方法 start() 2, 逐行…

【黑马笔记】Servlet简易教程

1. Servlet demo 0. 新建web app项目 记得去 web.xml 中删除多余的配置&#xff0c;以及新建对应的文件夹 1. 导入 Servlet依赖坐标 <dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version&…

四块ACM区域赛金牌,我队友

是的&#xff0c;毕业12年以后&#xff0c;他来找我。     痛失网名了属于是&#xff0c;但是这不重要&#xff0c;​怎么说呢&#xff1f;有点激动&#xff0c;我得把这件事情记录下来&#xff0c;这是一位重量级的人物&#xff0c;也是大家眼中别人家的孩子。     他…

Python网络爬虫:Scrapy和Beautiful Soup的使用和数据处理技巧

章节一&#xff1a;引言 在当今互联网时代&#xff0c;数据的价值越来越被重视&#xff0c;而网络爬虫作为一种强大的工具&#xff0c;可以帮助我们从互联网中提取有用的数据。Python作为一门广泛应用于数据科学和网络开发的编程语言&#xff0c;有着丰富的库和框架来支持网络…

SD卡数据恢复软件哪个最好 SD卡数据恢复多少钱

SD卡是一种常见的数字存储卡&#xff0c;由于其体积小、存储能力强&#xff0c;至今仍然被应用在手机&#xff0c;行车记录仪&#xff0c;微型摄像机中。但SD卡在使用过程中可能会出现一些故障或者数据丢失的情况。那么今天小编就给大家介绍一下SD卡数据恢复软件哪个最好&#…

泊松分布和指数分布的关系

泊松分布和指数分布的关系 泊松分布用于描述单位时间&#xff08;或面积内&#xff09;随机事件发生的次数&#xff08;离散型随机变量&#xff09;&#xff0c; λ \lambda λ表示随机事件在单位时间&#xff08;或面积内&#xff09;发生的平均次数 The Poisson distributio…

操作系统进程调度算法——先来先服务、时间片轮转、优先级调度算法

一、先来先服务调度算法 &#xff08;1&#xff09;算法内容&#xff1a;先来先服务调度算法是一种最简单的调度算法&#xff0c;可以应用于高级调度也可以运用于低级调度。高级调度时&#xff0c;FCFS调度算法按照作业进入后备作业队列的先后顺序选择作业进入内存&#xff0c…