TPM分析笔记(十二)TPM PCR操作

news2024/11/20 21:33:02

目录

  • 一、PCR初始化(Initializing PCR)
  • 二、PCR的扩展(Extend of a PCR)
    • 2.1 其他PCR命令
  • 三、使用PCR Banks进行扩展(Using Extend with PCR Banks)
  • 四、事件记录(Recording Events)
  • 五、选择多个PCR(Selecting Multiple PCR)
  • 六、PCR报告(Reporting on PCR)
    • 6.1 读取PCR
    • 6.2 证明PCR
  • 七、PCR授权(PCR Authorizations)
    • PCR不在集合中
    • 授权集
    • 策略集
    • 顺序检查
  • 八、PCR分配(PCR Allocation)
  • 九、PCR变更轨迹(PCR Change Tracking)
  • 十、PCR的其它用途(Other Uses for PCR)
  • 总结

TPM 2.0第1部分包含对TPM属性、功能和方法的叙述描述。本篇主要基于TPM 2.0规范Part 1的第17章(PCR Operations),对PCR(Platform Configuration Register, 平台配置寄存器)的相关操作进行解读。

平台配置寄存器是TPM必需的特性之一。它们最初的应用是用于提供一种密码学的方式记录(测量)软件的状态:包括平台上运行的软件和软件使用的配置数据。PCR的更新方法叫做扩展,扩展是一种单向的哈希操作,从而保证测量值不被篡改。这些用于测量的PCR可以被读取来报告这些软件的状态。这些PCR的值可以被签名然后用于更加安全的报告,这被称作认证(或者说引用)。PCR还可以被用于扩展的授权policy从而限制其他TPM对象的使用。

TPM从来不对测量的结果做任何判定。单单根据TPM内部的信息来看,并不能确定测量结果的好坏,或者结果是否安全可信。在测量软件时,TPM仅仅用PCR来记录测量值。至于是否安全,这要到应用程序真正使用PCR用于policy授权的时候,或者是远程请求者请求一个签名认证(quote,引用)然后判定可信性。

TPM2.0新增加的关于PCR的特性是,TPM不再将PCR的哈希算法固定为SHA-1。哈希算法现在可以修改了。有一些TPM的实现包含bank的概念,每一个bank实现一种不同的算法。

一个TPM会实现一定数量的PCR:比如说,PC上使用的TPM实现了24个。这些PCR按照惯例被分配给各种各样的软件层使用,软件类型从早期启动代码到操作系统和应用。这些PCR的分配还可以分为以下两类:需要运行的软件(通常是偶数序号的PCR),和用于定制启动过程的配置文件(通常是奇数序号的PCR)。

一、PCR初始化(Initializing PCR)

在TPM复位和TPM重启时,所有的平台配置寄存器(PCR)被重置为默认的初始条件。 某些PCR可能被TPM Resume指定为保存。 保留的那些将恢复到它们在上次TPM2_Shutdown(STATE)操作时的状态。 当TPM2_Startup()成功完成时,未被TPM Resume指定保存的PCR将处于默认的初始状态。

如果属性允许,PCR也可以通过TPM2_PCR_Reset()或动态可信度量根(D-RTM)序列重置(见34.2)。 PCR属性在特定于平台的规范中定义。 它们确定PCR的重置值以及执行重置所需的位置。

除PCR[0]外,任何PCR的默认初始条件都是所有位CLEAR或所有位SET。 对于PCR[0],默认的初始条件可能是所有位CLEAR、所有位SET、接收到TPM2_Startup()的位置或第一个测量值来自H-CRTM的指示符。 其他平台类型可能使用其他方法来标识访问的位置。

特定于平台的规范可以从上面的选项列表中选择。

规定1:某平台特定规范可以指定PCR[0-16]的默认初始条件为全0,PCR[17-20]的默认初始条件为全1。

规定2:某平台特定规范可以指定PCR[0]的默认初始条件为位置标识符,PCR[1-16]的初始条件为所有0。

说明:“位置标识符”为整型值,取值范围为0到TPM上实现的最大位置。 目前,最大的硬件位置是4。 在TPMA_LOCALITY中,一个4的位置将用八位字节0001 0000表示。 当编码为PCR初始值时,4的位置将由八位字节0000 0100表示。

例3:虚拟TPM可以为每个可能访问它的软件实体使用唯一标识符。
如果特定的软件与特定的PCR相关联,那么该PCR的重置值可能是允许更改它的软件的唯一标识符。

TPM2_PCR_Reset()要求为操作提供适当的授权(见17.7章节)。

二、PCR的扩展(Extend of a PCR)

PCR最初的应用场景是用于表示平台软件的状态,重要软件运行到当前阶段时的历史信息(包括配置信息)。TPM上电时会初始化所有的PCR,初始值由TPM平台相关的规范定义,通常是全0或者全1。命令调用者不能直接向PCR写值。相反,PCR的值是通过被TPM成为extend(扩展)的操作:

PCR new value = Digest of (PCR old value || data to extend)

TPM将会把需要加进来的数据连接到旧的PCR值中。要被扩展到PCR中的数据几乎总是哈希值,尽管TPM并没有限制一定是哈希值。然后TPM会对这个刚刚连接好的值做哈希,然后将新的哈希值存储到PCR中。在每个Extend之后,对于扩展的摘要值的特定顺序和组合,PCR值是唯一的。

除D-RTM外,扩展PCR需要授权(见17.7)。
在这里插入图片描述

2.1 其他PCR命令

PCR相关的命令如下:

  • TPM2_PCR_Extend:几乎是PCR最常用的命令,它用户将一个摘要值扩展到(extend)到PCR中。
  • TPM2_PCR_Event:让TPM来做哈希并将哈希值extend到PCR中。这个命令要求输入消息的长度最长为1024字节。
  • TPM_PCR_Read:读取一个PCR的值,这在我们后面要介绍的验证事件记录中很有用。
  • TPM2_PCR_Reset:复位一个PCR,主要用于为用户层软件分配的PCR(比如前面刚刚介绍的debug用PCR)。大多数的PCR在TPM一个上电周期内是不允许被复位的。
  • TPM_PCR_Allocate:为PCR设定哈希算法。如果需要修改默认的哈希算法,需要使用这个命令,并且大多数情况下只需要执行一次就足够了。
  • TPM2_PCR_SetAuthPolicy:为一个PCR组设定一个授权policy。在PC客户端上并不需要这个命令。
  • TPM2_PCR_SetAuthValue:为一个PCR组设定一个授权口令。在PC客户端上并不需要这个命令。

三、使用PCR Banks进行扩展(Using Extend with PCR Banks)

TPM2_PCR_Extend()有一个句柄来指示要扩展的PCR和要扩展的数据。 扩展数据是一种结构,它包含一个或多个摘要以及摘要的算法标识符。 每个摘要被扩展到具有相同算法的PCR库。 如果没有为其中一个PCR库提供摘要数据,则不对该PCR库中的PCR进行更改。

对于每一个定义了pcrNum的算法,TPM都应该执行如下操作:
在这里插入图片描述

Hash alg:使用与PCR实例相关联的算法对哈希函数进行哈希处理
PCR.digest:在一个PCR中的摘要值
pcrNum:PCR数值选择器
alg:PCR算法选择器
digest:将列表条目中与PCR库具有相同算法标识符的部分进行摘要

说明1:如果一个TPM支持三个PCR库(如SHA-1、SHA256和SHA512),一个带有SHA-1摘要和SHA256摘要的到PCR[2]的扩展将在SHA-1库中扩展到PCR[2],在SHA256库中扩展到PCR[2]。SHA512库中的任何PCR都没有变化。
说明2:系统重启以后,整个平台的运行由CRTM(Core Root of Trust Measurement)开始。CRTM会测量接下来将要运行的软件并将测量值扩展到一个偶数索引的PCR中。然后CRTM还会将软件的配置信息扩展到一个奇数索引的PCR中。这个被测量的软件,可能是BIOS,反过来又会测量和扩展它的下一级软件,这或许是MBR。这个测量链就这样继续下去直到早期的系统内核代码或者这个之后。这个过程中,重要的安全配置文件也会被测量。最终测量的结果就是PCR的值表示了所有扩展到PCR中的测量历史。因为安全摘要机制的单向属性,索引没有办法撤销一个测量。(将PCR的值变成过去的某一个值)

四、事件记录(Recording Events)

记录日志条目的方法是将完整的日志条目输入到TPM内部,而不是在TPM外部执行摘要。 TPM支持的每种散列算法都可能被用于对日志条目执行散列。 不超过1024个字节的事件可以使用TPM2_PCR_Event()。 超过1024字节的事件可以使用序列命令:TPM2_HashSequenceStart()TPM2_SequenceUpdate(),和TPM2_EventSequenceComplete()

TPM2_PCR_Event()TPM2_EventSequenceComplete()返回一个带标签的摘要列表。 摘要是使用每种实现的散列算法的事件数据摘要。

例:对于实现两种算法(如SHA256和SM3)的TPM,事件命令返回两个带标签的摘要列表。

TPM2_EventSequenceComplete()要求提供适当的授权(见17.7)。

事件的记录也可能是_TPM_Hash_Start/_TPM_Hash_Data/ _TPM_Hash_End序列(H-CRTM事件序列)的结果。 H-CRTM序列的指示来自TPM接口,而不是通过命令缓冲区。 在接收到_TPM_Hash_Start时,TPM将创建一个事件序列上下文。 如果在TPM接收_TPM_Hash_Start时没有可用的对象上下文空间,TPM将刷新上下文(供应商的选择)以创建事件序列上下文。 _TPM_Hash_Data用于更新H-CRTM事件序列上下文,_TPM_Hash_End完成该序列。 H-CRTM事件序列期间计算的摘要或摘要将被扩展到相关平台特异性规范指定的PCR中。 特定于平台的规范可能允许在TPM2_Startup()之前或之后使用H-CRTM事件序列。 TPM2_Startup()之前的H-CRTM事件会影响PCR[0]。 在TPM2_Startup()之后,一个H-CRTM事件影响PCR[17]。

五、选择多个PCR(Selecting Multiple PCR)

TPM2_PCR_Event()隐式选择具有相同索引的所有PCR。 有些命令允许在不同的bank选择多个PCR。 例如TPM2_PCR_Read()TPM2_Quote()TPM2_PolicyPCR(),它们允许调用者在多个bank中任意选择PCR。

当一个命令允许选择多个PCR时,将使用一个选择器列表。 列表中的每个条目由一个算法ID后跟一个位数组组成。 位数组中的每个位对应一个PCR。 如果设置了一个位,则选择与算法ID对应的bank中指定的PCR。

与PCR对应的位是PCR[n]对应的位是数组“n/8(向下取整)”八位中的(n mod 8)位。

例:在一个PCR为16的TPM中选择PCR[0]和PCR[13]的阵列为01 2016。
PCR[0]的bit为“0/8(向下取整)”=第0位(0116位),PCR[13]的bit为“13/8(向下取整)”=第5位(2016位)。

选择器列表按顺序处理。 被选中的PCR是串联的,在第一个选择器中编号最低的PCR是列表中的第一个,在最后一个选择器中编号最高的PCR是最后一个。

TPM2_PCR_Read()返回与选择器列表中选择的PCR相对应的PCR值列表。 TPM2_Quote()TPM2_PolicyPCR()对PCR的串联进行分解。

六、PCR报告(Reporting on PCR)

6.1 读取PCR

TPM2_PCR_Read()读取选择的PCR的当前值。 对于该命令,调用者使用PCR选择结构指示要读取的PCR列表。 这个结构是一个列表数组。 每个数组条目都有一个哈希标识符和一个位域。 哈希标识符表示PCR的bank,位域表示在bank中选择的PCR。

在响应中,TPM提供了一个PCR选择结构和一个PCR值列表。 PCR选择结构表示返回结构中存在的PCR。 所请求的返回数据结构的大小可能不适合可用的TPM输出缓冲区。 在这种情况下,PCR值列表被截断,响应PCR选择结构指示返回的PCR。 如果返回的结构不包含所有的PCR,调用者可以修改选择结构并发出另一个读请求以获得额外的PCR值。

由于PCR可能在收集所需的全部PCR集合的调用之间改变,TPM返回一个计数器,该计数器在大多数TPM2_PCR_Extend()TPM2_PCR_Event()TPM2_EventSequenceComplete()TPM2_PCR_Reset()的调用中递增(参见17.9的例外情况)。 如果该计数器值在调用之间发生变化,则可能需要重复序列,直到所需的PCR全部返回,而不改变计数器值。

6.2 证明PCR

在某些情况下,需要将选定的PCR置于特定的状态。 在表示这种状态时,不希望必须列出每个PCR的内容。 相反,一个PCR连接的摘要(一个复合的PCR摘要)将显示所有相关PCR的当前内容。

复合摘要中包含的PCR采用与TPM2_PCR_Read()相同类型的结构选择。 首先对选择结构进行过滤,使未实现的PCR不在选择结构中。 然后,创建所有选定PCR的复合摘要。 最后,对过滤后的选择结构和复合摘要进行散列,以创建最终的摘要值。 该摘要可以与所需的摘要进行比较(TPM2_PolicyPCR())或在认证中返回(TPM2_Quote())。

为了验证证明引用(attestation quote),远程调用者通常会使用PCR重新计算摘要值。 TPM 1.2 quote命令返回PCR值和摘要。 在TPM 2.0中,由于哈希灵活性,PCR集可能已经超过了响应缓冲区的大小。 因此,TPM2_Quote()只返回摘要,并且必须单独检索PCR值。

这可能会导致竞态条件。 在引用的时间和读取它们的时间之间,PCR值可能改变。 有几种解决方案。 PCR可以在引用前后读取,以确保它们没有变化。 另外,在将结果返回给远程调用者之前,可以根据PCR在本地验证引用摘要,并且可以重新运行引用,直到验证成功。

七、PCR授权(PCR Authorizations)

TPM2_PCR_Reset()、TPM2_PCR_Extend()、TPM2_PCR_Event()和TPM2_EventSequenceComplete()需要对修改的PCR进行授权。 根据所修改的PCR,授权的类型可能不同。 PCR可定义为具有固定的EmptyAuth; 一个变量authValue; 或变量authPolicy

PCR的授权(authValue或authPolicy)可以应用于一组PCR。 也就是说,可以将几个PCR指定为使用相同的授权值,这样,改变集合中任何PCR的授权值(authValueauthPolicy)将改变集合中所有PCR的值。 由authValue授权的一组PCR位于一个授权集中。 由authPolicy授权的一组PCR位于策略集中。

与每个PCR相关联的授权类型由特定于平台的规范固定。 对于每个集合,特定于平台的规范定义了集合中的PCR。 一个PCR不应该位于多个策略集或授权集中。

PCR可以同时存在于策略集和授权集中。 如果两者都是,那么使用授权集的authValue的唯一方法是使用包含TPM2_PolicyAuthValue()TPM2_PolicyPassword()的策略。

要授权一个PCR,需要正确的授权类型,这将取决于PCR的授权集。 在所有情况下,EmptyAuth值可以在HMAC会话中使用HMAC计算中的零长度authValue提供,也可以作为零长度密码提供。

PCR的一个常用功能是授权。一个TPM实体可以拥有一个如下的policy,只有当特定的PCR的值是一个特定值时才允许使用这个TPM实体。第14章将详细介绍这个功能。这个policy可以选择一组PCR,每个PCR制定不同的值。如果PCR的值与设定的值不同,policy就不会满足,因此相关的TPM实体就不能被访问。

应用案例1:将硬盘加密秘钥与平台的状态绑定

把全磁盘加密软件的秘钥存储在TPM中比存储在磁盘上并且仅使用口令保护要安全很多。
首先,TPM能够防止暴力攻击(第8章有详细介绍TPM防止字典攻击的细节),
这使得针对口令的暴力攻击不会成功。如果软件使用一个强度较弱的口令保护秘钥,
那这个秘钥会很容易受到攻击。
其次,将秘钥存储在磁盘上很容被窃取。
拿到硬盘也就意味着拿到了秘钥。
而如果使用TPM存储秘钥,如果要想窃取秘钥,
就需要整个首先窃取包含TPM的整个平台,或者至少先窃取硬盘和主板。

密封(sealing)操作可以让秘钥不仅可以被一个口令保护,还可以被一个policy保护。
一个典型的policy会将秘钥锁定在sealing操作时的一个PCR值上(代表了软件的状态)。
这个方案还假设系统启动的状态没有变化。
任何事先植入的恶意软件都将在启动过程中被测量到PCR中,
这样一来秘钥就会在不安全的状态下继续被密封。
一个有较低可信度的公司可能会有一个自己的磁盘镜像,
它可以将秘钥密封到代表这个镜像状态的PCR中(可以理解为镜像启动以后PCR的值)。
这些PCR的值可以在更安全的平台上事先被计算好。
一个更加复杂的方案是公司使用TPM2_PolicyAuthorize命令,
并提供用于授权一组可信PCR值得凭据(Tickets)。

尽管使用一个口令也可以保护秘钥,但是即使没有TPM秘钥口令(以上的方案)可以
增加系统的安全性。一个攻击者可能不需要一个TPM秘钥的口令就能启动系统,
但是没有用户明和登录密码他还是不能登录。
OS的安全特性可以用于保护数据。
但是攻击者可以启动另外一个OS,比如通过DVD或者USB设备而不是硬盘启动,
这样一来就可以越过OS登录的安全保护。
还好在使用TPM的情况下,这种不同的启动配置(不从硬盘启动)和不同的OS软件
将会改变PCR的值。又因为这些被改变过的PCR值不能和之前正确的PCR值匹配,
所以TPM不会释放解密磁盘的秘钥,进而硬盘上的数据文件也就不会被解密。

以下是密封操作的步骤:
1. 构建一个Policy,使用TPM2_PolicyPCR,
   选择将来解封(UNseal)秘钥时的PCR值作为输入。
3. 执行以下操作(与TPM1.2类似)
  * 使用TPM2_GetRandom的结果作为对称秘钥,秘钥在TPM之外使用。
  * 使用TPM2_Create命令创建一个TPM秘钥,
  * 将刚刚的对称秘钥作为输入的Secret信息,并选择第1步的policy授权。
  * 或者
  * 使用TPM2_Create,仅仅使用Policy授权,通过改变命令的配置信息,
  * 让TPM自己生成对称秘钥并密封到当前创建的对象中。

 如下的操作用于解封秘钥:
   * TPM2_Load加载TPM2_Create创建的对象。
   * TPM2_PolicyPCR来满足Policy。
   * TPM2_Unseal返回被密封的对称秘钥。
应用案例2:将一个秘钥由OS环境下安全地传递到一个没有OS的环境

一个平台管理员可能想授权终端用户修改BIOS设置,这可能是修改启动顺序。
这是BIOS就需要管理员口令。
这个时候管理员必须向BIOS(没有OS的环境)传递一个访问权限很高的口令,
但是同时又不希望用户能看到。

管理员可以将口令密封到一个PCR中,PCR中存储了BIOS正在运行时的平台状态。
管理员可以将用于密封管理员口令的口令在OS中传递给用户。
用户不能再OS环境下解密管理员口令,
但是BIOS却因为有正确的PCR值而可以解密
(当然用户需要输入不那么重要的“用于密封管理员口令的口令”提供给BIOS)。

以下OS中的操作步骤:
1. 使用TPM2_PolicyPCR构建一个policy,选择PCR[2]全零作为解封时的PCR。
这个PCR只有在启动早期的值为0,这时CRTM刚刚把控制权交给BIOS的第一部分代码。
2. 使用TPM2_Create,输入一个口令和刚刚的policy来创建一个密封对象。
口令通过一个加密会话传递(参见17章),实际上就是通向TPM设备的安全通道。

以下是BIOS阶段的操作:
3. 使用TPM2_Load加载对象。
4. 使用TPM2_PolicyPCR来满足Policy。
5. 使用TPM2_Unseal来返回管理员的口令。

PCR不在集合中

如果PCR不在集合中,授权值可以只有一个EmptyAuth值。

授权集

如果PCR在一个授权集中,那么PCRauthValue将以HMAC会话或密码的形式提供。 当PCR具有一个固定的EmptyAuth值时,仍然需要一个授权会话。

当一个PCR有一个变量authValue时,在每次SARTUP(CLEAR)时,该authValue被重置为EmptyAuth。 它被保存在STARTUP(STATE)中。 一个知道authValue的实体可以使用TPM2_PCR_SetAuthValue()来修改变量authValue。

策略集

策略集的authPolicy具有散列算法和摘要值。

如果authPolicy的散列算法是TPM_ALG_NULL,则该策略还没有设置。 这个未初始化的策略集将使用一个EmptyAuth。

如果策略的摘要算法不是TPM_ALG_NULL,那么策略集是一个初始化的策略集。 如果PCR在一个初始化的策略集中,那么只能通过策略会话授予授权。

TPM2_ChangePPS()将所有策略集的散列算法设置为TPM_ALG_NULL。 与PCR相关联的算法和authPolicy只能由了解平台授权的实体使用TPM2_SetAuthPolicy()来更改。

如果在初始化的策略集中为PCR使用了HMAC会话或密码,那么TPM将返回一个错误(TPM_RC_AUTH_TYPE)。 如果策略会话用于不在初始化策略集中的PCR,那么TPM将返回一个错误(TPM_RC_POLICY_FAIL)。 这两个失败都不会导致字典攻击保护的更新。

顺序检查

在为PCR确定正确的授权类型时,TPM将使用该授权类型。 如果授权是密码或HMAC会话,TPM将检查PCR是否在授权集中。

八、PCR分配(PCR Allocation)

TPM可以支持平台重新分配PCR。 要更改PCR的分配,平台将使用TPM2_PCR_Allocate()。 分配结构对每个实现的散列算法都有一个PCR选择。 要在一个bank中分配一个PCR,相应的位将在该bank的选择中被设置。

TPM2_PCR_Allocate()修改的PCR分配将在下一个_TPM_Init生效,并一直持续到下一个TPM2_PCR_Allocate()

注1:由于RAM的限制,一些实现可能不允许在bank内任意分配PCR。这并没有创建一个部署问题,因为平台预计将能够管理将连接到该平台的TPMs。

如果PCR的属性没有由该TPM的平台特定规范定义,则不能分配PCR。

注2:PCR的属性包括Startup()初始化值、复位的位置和扩展的位置。

有一个要求是每个哈希算法都有一个bank,但不要求bank有任何PCR(也就是说,bank的所有选择的PCR选择位都可以是CLEAR)。

TPM与不可更改的特定PCR分配一起发布是一个有效的实现。 如果TPM不允许更改分配,那么它将不会实现TPM2_PCR_Allocate()。

九、PCR变更轨迹(PCR Change Tracking)

为了支持在策略中使用PCR, TPM维护了一个pcrUpdateCounter。 通常,每次修改PCR(扩展或重置)时,该计数器将增加。 当策略要求PCR具有特定值时使用该计数器(参见19.7.7.6)。

特定于平台的规范可以指定所选PCR的更新不会导致pcrUpdateCounter的变更。

十、PCR的其它用途(Other Uses for PCR)

这个库中定义的与PCR相关的命令涵盖了常见的用例:例如,在引导期间记录组件或TCB中的运行时切换。 特定于平台的规范定义了控制这种行为的PCR属性,并描述了外部软件应该如何使用PCR。

然而,PCR是为更通用的平台状态表示而设计的,平台特定的规范可能会定义额外的PCR行为来捕捉这一点。 通常,平台规范可以定义一个PCR来表示TPM确信知道的任何值,或者TPM已经安全地通信过的任何值。 例如,用于“可信锁”的TPM可能定义一个PCR,该PCR的值为0,表示门是关闭的,而一个PCR的值表示门是打开的,或者一个虚拟TPM规范可能定义一个PCR,该PCR的值表示发出TPM命令的虚拟机的某些特征。 本规范对这种PCR不要求任何特殊的行为或值-语义。
注:PCR可以通过将PCR设置为该值来“表示”一个值,也可以通过将PCR扩展为该值来“表示”该值。 在“可信锁”的情况下,PCR更可能包含0或1来表示锁的状态,而不是每次对锁的更改都扩展到PCR。

这并不意味着特定于平台的工作组可以定义新的命令来操作PCR。

总结

PCR有两个最基本的应用。它们的值可以通过一个签名的认证引用被报告出去,这样允许一个依赖方决定平台的软件状态是否可信。它们还可以用于policy,基于PCR的值来授权其他TPM对象的使用。相对于TPM1.2PCR的算法被固定成SHA-1,TPM2.0做了改进,允许使用其他的哈希算法。

参考:
TPM 2.0规范解读系列——Part 1体系结构第(九)读:PCR状态

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

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

相关文章

Java递归实现树形结构的两种方式

目录0、引言1、数据准备2、类型转化3、递归实现方法3.1、Java7及以下纯Java递归实现3.2、Java8及以上借助lamda表达式实现0、引言 在开发的过程中,很多业务场景需要一个树形结构的结果集进行前端展示,也可以理解为是一个无限父子结构,常见的…

【老师见打系列】:我只是写了一个自动回复讨论的脚本~

文章目录🌟好久不见⛳️实现过程🌴老操作了兄弟们~🐢一步拿捏讨论💖美图结束语专栏Python零基础入门篇💥Python网络蜘蛛💥Python数据分析Django基础入门宝典💥小玩意儿💥Web前端学习…

2022海德堡桂冠论坛(HLF)见闻录

今年9月下旬,我前往德国参加了第九届海德堡桂冠论坛。因疫情原因停摆两年后,海德堡桂冠论坛再次以线下形式举办,会场热闹非凡,作为计算机与数学界的社交盛宴当之无愧。 海德堡桂冠论坛(Heidelberg Laureate Forum, HLF…

IGV-GSAman |「功能基因组时代」的高效率科研工具

写在前面 今天周末,转眼10月份只剩一周。万万没想到,一个月下去,我还是花了不少时间在完善「GSAman」。至于为什么本来「两个小时」就干完的事情,可以干成「22天」?到底还是我对前面的版本,不太满意。当然…

Attack Lab

Attack Lab 从CMU官网下载完所需实验包后,内有官方文档以及.tar压缩包,使用tar -xvf targetk.tar解压后,得到如下文件 The fifiles in targetk include: README.txt: A fifile describing the contents of the directory ctarget: An execut…

web自动化测试框架

本文介绍web自动化测试框架 ●Base:用来对Selenium API进行二次封装。 对Selenium API进行二次封装的目的是简化一些复杂的操作,但是千万不要为了封装而封装。 封装好后,其他页面类可以集成basepage,调用这些方法。 from sele…

【iOS】—— 仿写知乎日报第一周总结

目录知乎日报第一周完成情况遇到的问题:1.线程问题:2.SDWebImage加载图片3.实现无限刷新界面4.点击主界面cell进入后的界面知乎日报第一周完成情况 在这周开始了仿写知乎日报的任务,在第一周里,我完成了主界面,以及滚…

新人入手mac折腾过程中遇到的解决方案

本文将长期更新,以记录个人的使用。 终端美化 …还没有美化完,不过推荐看这篇文章Mac终端美化指南 homebrew homebrew是Mac OS X上的强大的包管理工具,可以高效管理各种软件包,官方说法是:The missing package mana…

ArcGIS:如何新建图层组并添加数据、切换数据视图和布局视图、修改符号系统?

目录 01 如何新建图层组? 02 如何在图层组中添加数据 03 如何切换数据视图和布局视图 03 如何修改符号系统? 3.1 如何快捷的修改一下符号样式? 3.2 如何修改符号系统的色带 3.2.1 色带视图的取消 3.3 修改符号系统中的标注显示 3.4 如…

大气湍流退化图像复原技术研究及DSP实现

目录 第一章 绪论 1 1.1 研究背景 1 1.2 国内外研究现状 1 1.3 本文的研究内容 5 1.4 本文的组织结构 5 第二章 大气湍流退化图像复原技术 7 2.1 图像退化及复原数学模型概述 7 2.1.1 图像退化的数学模型 7 2.1.2 图像复原的数学模型 8 2.2 大气湍流退化图像概述 9 2.2.1 大气…

插件内存分析

rtspsrc 1. 当pipleline为rtspsrc加其他插件时,如果让其他插件卡主会发生什么事情呢?以rtspsrcfakesink为例(gst-launch-1.0.exe rtspsrc locationrtsp://xxx ! fakesink),修改fakesink的代码,让render中…

基于SSM的图书馆阅览室预约管理系统,高质量论文范例,可直接参考使用,附送源码、数据库脚本

目录 1.项目技术栈 2.适合对象 3.适合课题 4.项目功能概述 4.1 项目功能汇总 4.2 项目功能介绍 5. 高质量论文范例 6. 毕业设计撰写视频教程 6.部分运行截图 1.项目技术栈 前端必学三个基础,"HTML、CSS、JS",基本每个B/S架构项目都要…

C++【搜索二叉树】

目录 一、什么是搜索二叉树 二、搜索二叉树如何删除数据 删除的是叶子结点的情况 删除的结点下面仅有一个子节点(托孤)(要删除的结点只有一个孩子) 替换法删除 (要删除的结点有两个个孩子) 三、写一…

腾讯前辈熬夜肝了一个月整理的《Linux内核学习笔记》,啃完受益匪浅不走弯路

小编热衷于收集整理资源,记录踩坑到爬坑的过程。希望能把自己所学,实际工作中使用的技术、学习方法、心得及踩过的一些坑,记录下来。也希望想做Linux内核高级工程师的你一样,通过我的分享可以少走一些弯路,可以形成一套…

【数据结构】常见七大排序总结

目录 一、插入排序:直接插入排序【稳定排序方法】 二、插入排序:希尔排序【不稳定排序方法】 三、选择排序:直接选择排序【不稳定排序方法】 四、选择排序:堆排序【不稳定排序方法】 五、交换排序:冒泡排序【稳定…

基于单片机MC9S12XS128的两轮自平衡小车设计

目 录 1.绪论 1 1.1研究背景与意义 1 1.2两轮自平衡车的关键技术 2 1.2.1系统设计 2 1.2.2数学建模 2 1.2.3姿态检测系统 2 1.2.4控制算法 3 1.3本文主要研究目标与内容 3 1.4论文章节安排 3 2.系统原理分析 5 2.1控制系统要求分析 5 2.2平衡控制原理分析 5 2.3自平衡小车数学…

总结一下flex布局

flex布局 传统布局方案是基于盒状模型,依赖 display position float 方式来实现,灵活性较差;Flex是Flexible Box的缩写,意为”弹性布局”。Flex可以简便、完整、响应式地实现多种页面布局 CSS3 弹性盒子是一种一维的布局&…

[Spring MVC3]MyBatis详解

本章重点讲述了MyBatis映射器,对数据层进行的操作,建议本篇文章和Spring Boot的持久层相互比较来看会更加收获颇多Spring Boot持久层技术 本文需要使用到MVC第一讲的模板格式与配置情况,全部代码已经放在此博客中,Spring MVC1 目…

修改设备管理器的COM端口名称

Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class{4d36e978-e325-11ce-bfc1-08002be10318}\0001] “InfPath”“oem53.inf” “InfSection”“CH341SER_Inst.NTamd64” “ProviderName”“wch.cn” “DriverDateData”hex:00,00…

计算机算法(二)——进入计算机世界

作者介绍: 📞作者:小刘在C站 📀每天分享课堂笔记 🌹夕阳下,是最美的绽放 瑞士著名的科学家Niklaus Wirth教授曾提出:数据结构算法程序。 数据结构是程序的骨 算法是程序的灵魂。 在生活…