文章目录

  • 1 简介
    • 1.1 杀伤链模型
    • 1.2 研究现状
    • 1.3 本文创新点
    • 1.4 解决的主要问题
  • 2 方法提出
  • 3 系统设计
    • 3.1 数据收集和表示
    • 3.2 TTP规范
    • 3.3 HSG构建
    • 3.4避免虚假依赖
    • 3.5 降低噪声
    • 3.6 信号关联和检测
  • 4 系统实施
    • 4.1 构建起源图的流消耗
    • 4.2 规则匹配引擎和HSG构建
    • 4.3 噪声过滤和检测引擎
  • 5 实验评估
    • 5.1 数据
    • 5.2 设定
    • 5.3 结果简述
    • 5.4 寻找最优阈值
    • 5.5 性能
    • 5.6 真实实验
  • 6 相关研究
    • 6.1 报警生成
    • 6.2 报警关联
    • 6.3 场景重建
    • 6.4 攻击粒度

原文标题:HOLMES: Real-time APT Detection through Correlation of Suspicious Information Flows

原文作者:Sadegh M. Milajerdi, Rigel Gjomemo, Birhanu Eshete, R. Sekar, V.N. Venkatakrishnan

原文来源:S&P2019

原文链接:https://arxiv.org/pdf/1810.01594.pdf

1 简介

本文构建了一个可以实时检测高级持续性威胁(Advanced Persistent Threat, APT)的系统——HOLMES。该系统可以通过实时汇总攻击者的行为,产生基于杀伤链模型(kill chain)的高级图(high-level graph),实现了将复杂的数据映射为简洁的APT攻击阶段,从而有利于防御者更加直观地发现威胁并进行防御。

1.1 杀伤链模型

杀伤链模型用来描述APT的生命周期,可以让人们更加直观的认识到APT攻击的各个阶段是如何协作配合完成目标的。

一个典型的APT攻击包含以下阶段:

  • Initial Compromise:例如网站挂马或者鱼叉式钓鱼
  • Establish Foothold:安装木马后门
  • Escalation Privilege:通过漏洞利用
  • Internal Reconnaissance:内部侦察目标系统信息
  • Move Laterally:横向渗透
  • Maintain Presence:Command and Control(C&C)、Remote Access Trojans(RATs)
  • Complete Mission:Exfiltration of Confidential Information

我们注意到,虽然攻击者的手段多种多样,但是映射到高层次的攻击步骤之后,其抽象攻击模式基本不变,基于此可以将复杂的数据映射到具体的攻击阶段。

1.2 研究现状

当前从报警关联(alert correlation)的角度来进行APT入侵检测,使用的大多数都是像Splunk、LogRhythm、IBM QRadar这样的SIEM(Security Information and Event Management )系统,从不同的主机上收集日志来关联它们,此类系统存在的问题包括:

(1)对于从报警到攻击实例之间的复杂关系缺乏可解释性

(2)将不同主机、时间跨度极长的攻击步骤聚合在一起,精确度不高

1.3 本文创新点

(1)能够实时检测系统,产生APT报警

(2)能够实时产生高级别的攻击图来描述攻击者的行为,协助防御者进行实时地安全响应

1.4 解决的主要问题

HOLMES的要解决的问题是将日志和报警信息映射到杀伤链模型上,提供实时、直观、可视化的攻击场景图,并且能将大量的审计数据抽象为少数的几个攻击节点。

具体实现上的问题包括:

(1)如何生成能反映攻击者行为的报警、并降低噪声

(2)如何有效地进行报警关联

(3)如何呈现攻击场景

2 方法提出

实现目标是将原始的日志和报警映射到语意上与APT接近的活动步骤TTPs(Tactics,Techniques and Procedures),然后再映射到Kill Chain

(1)系统设计的主要挑战:低级别的审计数据与高级别的kill-chain之间存在巨大的语意鸿沟。因此基于MITRE’s ATT&CK框架构建了一个中间层来缩小语意鸿沟。ATT&CK中以TTPs的方式定义了200多种行为模式,每个TTP定义了一种实现特定高级功能的可能方法。

(2)为了使低级别的数据有效地映射到TTPs,本文将审计日志表示为有向起源图(directed provenance graph),并且使用系统中低级别实体(如文件、进程等)之间的信息流(Information Flow)作为基础,来进行警报关联.

(3)为了检测到攻击步骤之间的关联性,本文开发了高级别情景图(high-level scenario graph, HSG),HSG的节点对应于TTPs,边表示TTPs实体之间的信息流。在HSG中定义了以下概念:

  • Ancestral Cover:描述节点之间的依赖关系
  • Noise Reduction:降低与良性活动相关的依赖性
  • Ranking and Prioritization:修剪与APT无关的节点和边

(4)为了减少误报,本文提出的方法是:学习可能会产生误报的良性TTPs模式,采用启发式算法,根据其严重程度为图中的节点和路径分配权重,以便可以对HSG进行排名,并将排名最高的HSG呈现给分析人员。

(5)评估数据来自DARPA Transparent Computing #3 的Host Audit Data,使用了9个真实的APT场景进行体统评估,并将系统放在真实环境中运行了2周来评估检测效果。

3 系统设计

系统设计的主要任务是建立威胁模型,因此如何建立审计系统以及如何生成日志数据不在本系统设计的范围之内。

3.1 数据收集和表示

该系统使用的审计日志来源于许多主机的不同操作系统:

  • Linux auditd
  • BSD dtrace
  • Windows ETW

原始数据被收集并处理成与OS无关的格式,输入的**事件(event)**包括:

  • principals :用户
  • files:例如I/O操作、文件创建、文件所有权、权限
  • memory:例如mprotect和mmap
  • processes:例如进程创建和权限更改
  • network connections

数据以图的形式来表示,我们称之为起源图(provenance graph)。图中的节点包括subjects(processes)和objects(files、pipes、sockets),边表示实体之间的依赖关系。该设计与前人的工作有两个不同之处:

(1)provenance graph是不断变化的:当一条边改变了节点的依赖关系,一个新的节点将会被创建并替换旧节点。这种“版本化”的方法使得在不改变分析结果的情况下可以对图进行修剪,而且这种versioned graph是无环的,这可以简化许多图算法。

(2)另一个不同之处是provenance graph是存储在主存中的,每个事件所占空间小于5bytes,这种表示方式可以在较长的时间段内实时消耗事件和构建起源图。

3.2 TTP规范

TTP规范提供了低级别审计事件和高级别APT步骤之间的映射,因此这是本文所提出的方法的核心。

TTP代表了具体审计日志和高级APT步骤之间的中间抽象层。我们依靠两种主要技术将审计日志数据提升到该中间层:

  • provenance graph
  • TTPs实体之间的信息流依赖

Prerequisites(先决条件):表现为因果关系和和信息流的形式。

为了TTP匹配的效率,不能使用backtracking(回溯)。我们发现,大多数TTP可以在我们的框架中使用单个事件进行建模

TTP规范举例:

其中,第四列列出了第三列对应的Event Family中所包含的事件;最后一列既可以表示该TTP的先决条件,也可以表示该TTP的先决TTP。先决条件既可以包含于两个TTPs的两个实体之间的关系,而且能够捕获两个TTP拥有一个共同parent的条件。应用先决条件,我们可以减少误报。

3.3 HSG构建

图中的椭圆表示匹配的TTPs,椭圆内部是匹配的起源图实体;边表示不同TTPs之间的先决条件。

HSG的构建主要是由先决条件驱动的,如果一个TTP的所有先决条件都满足了,那么这个TTP就会被匹配并且被添加到HSG中。这可以随时减少HSG中TTP的数量,从而可以进行复杂的分析而不会影响实时性能。

3.4避免虚假依赖

(1)虚假依赖即与攻击活动不相关的依赖:

  • 假如某个日志轮换系统复制了某个日志文件,文件中包含了攻击者产生的部分信息。这种进程虽然代表了良性活动,但也会在起源图中被标记为与攻击进程存在依赖。如果此类依赖不被修剪的话,将会使HSG变得很大。

所以我们的目标是留下强依赖,修剪掉弱依赖。一般地,在起源图中考虑有一条表示信息流的路径连接的两个实体,如果这两个实体拥有相同的恶意祖先,我们就说

(2)祖先覆盖 ancestral cover A C ( f ) A C(f) AC(f)

  • f表示一条信息流路径

  • 仅针对在f中的所有进程,不影响非进程节点

∀ p ∈ f ∃ a ∈ A C ( f ) a = p \forall p \in f \exists a \in A C(f) \quad a=p ∀p∈f∃a∈AC(f)a=p or a a a is an ancestor of p p p

(3)最小祖先覆盖 minimum ancestral cover A C min ⁡ ( f ) A C_{\min }(f) ACmin​(f)

  • A C min ⁡ ( f ) A C_{\min }(f) ACmin​(f)表示攻击者如果想要控制整条信息流路径,那么他所需要拿下的最少祖先节点数

(3)路径因子 path − _{-} −​ factor ( N 1 , N 2 ) \left(N_{1}, N_{2}\right) (N1​,N2​)

  • 考虑从 N 1 N_{1} N1​ 到 N 2 N_{2} N2​的所有的信息流路径 f 1 , … , f n f_{1}, \dots, f_{n} f1​,…,fn​,令 m i m_{i} mi​表示 f i f_{i} fi​的最小祖先覆盖。那么,path − _{-} −​ factor ( N 1 , N 2 ) \left(N_{1}, N_{2}\right) (N1​,N2​)为 m 1 , … , m n m_{1}, \ldots, m_{n} m1​,…,mn​中的最小值。

3.5 降低噪声

噪声:良性活动匹配到了TTP规则。基于训练数据集产生降噪规则,包括两个概念:

  • 良性前提条件匹配

对于每个进程,我们的系统运行在良性的环境中并学习经常遇到的先决条件。在运行过程中,一旦某个被触发的TTP的先决条件与我们在训练中得到的条件(benign)相匹配,我们便忽略该TTP。

这种方法可能会导致漏报(false negative),比如一个恶意事件如果匹配到了我们在良心环境中训练得到的良性先决条件,那么这个恶意事件就可能会被忽略。例如,即便没有任何攻击,在nginx的启动阶段也会执行read /etc/passwd,然而如果我们将该行为列入白名单的话,如果有恶意事件是读取改文件,那么该恶意事件就会被忽略。

  • 良性数据流数量

为了解决上述问题,提出了用字节转移度量的数据流数量的概念。比如从/etc/passwd到nginx的数据流数量等于/etc/passwd文件的大小。因此,如果观察到更多的字节从/etc/passwd流向nginx,那么该信息流可能是攻击的一部分。为了确定信息流数量的临界点,我们在一段时间的良性活动中观察process-file和process-socket活动。

3.6 信号关联和检测

对每一个HSG定义严重程度评分(severity score),据此来确定一个HSG会构成APT攻击的可能性

  • 威胁元组(Threat Tuples)

对于每一个HSG,每一个Threat Tuple是一个7元组 ⟨ S 1 , S 2 , S 3 , … , S 7 ⟩ \left\langle S_{1}, S_{2}, {S}_{3}, \ldots, S_{7}\right\rangle ⟨S1​,S2​,S3​,…,S7​⟩,其中 S i S_{i} Si​表对应于APT攻击阶段的威胁程度。一个APT阶段通常会包含许多APT,选取威胁程度最高的来构成威胁元组。

例如下图的Threat Tuple为 ⟨ M , L , H , H , − , H , M ⟩ \langle M, L, H, H,-, H, M\rangle ⟨M,L,H,H,−,H,M⟩

  • HSG排名和优先级(HSG Ranking and Prioritization)

为了将HSGs排名,首先根据下图将Threat Tuple转化为数值类型,接下来将代表7个APT阶段的分数合并为一个整体的分数。下图中威胁等级的分数可以根据企业的实际情况自己制定。因此,合并规则基于以下两个准测:(1)灵活性和定制化(2)该分数要能够反映APT攻击步骤是如何展开的。

因此,为元组中的每一个entry设置一个权重:

∏ i = 1 n ( S i ) w i ≥ τ \prod_{i=1}^{n}\left(S_{i}\right)^{w_{i}} \geq \tau i=1∏n​(Si​)wi​≥τ

其中,n表示APT攻击的步骤数, w i w_{i} wi​和 S i S_{i} Si​分别表示步骤 i i i的权重和威胁程度, T T T表示阈值。如果在步骤 i i i中没有TTP出现,我们将 S i S_{i} Si​设置为1.

4 系统实施

下图是整个HOLMES系统的架构

4.1 构建起源图的流消耗

为了实现平台的独立性,来自不同操作系统的审计数据被规范化为通用数据表示(common data representation, CDR)。为基于CDR的审计记录发布到流处理服务器(Kafka),并从流服务器消费来进行实时分析和检测。使用SLEUTH系统进行数据流消耗,因果关系跟踪和起源图构造。

4.2 规则匹配引擎和HSG构建

规则匹配引擎在起源图上进行操作,并且将TTP规则作为输入。论文中用到的TTP规则在Table8中展示。为了匹配到一个TTP,规则匹配引擎将规则表中的每一条规则和它的先决条件进行迭代。这个环节的主要挑战是:对于每一个TTP来说,都要检查之前匹配的TTPs的先决条件和他们之间的路径因子。

为了避免大量的计算,我们不使用回溯法而是使用增量匹配法。这种方法存储先前计算的结果,并沿着图匹配和传播指向这些结果的指针。当一个TTP被匹配时,我们在HSG中创建相应的节点,并创建一个指向该节点的指针。同时这个指针将会指向所有与该TTP有依赖关系的低级别实体。

例如path_factor的计算,假设N1(起源图中的实体)和N2、N1和N3之间已经建立了依赖,当节点N2与N3之间出现了信息流时, 我们需要重新计算N1和N3之间的path_factor,以取path_factor的最小值。计算的过程中之前N1和N2的计算结果便可复用。

这种基于指针的两层之间的关联方法可能存在开销和复杂性较大的问题。但实际情况是,大量的低级别实体指向一个TTP,每个低级别实体只有一个指向TTP的指针,而每个TTP有多个维护多个指向它的指针,因此并没有出现随着起源图的增加而开销和复杂度急剧提升的情况

4.3 噪声过滤和检测引擎

噪声过滤引擎识别的是良性的TTP匹配,所以它被排除在HSG之外。它以从正常行为中学习到的模型为输入,模型包含了与良性活动匹配的TTPs以及从操作系统中读写操作的字节数阈值。

当规则匹配系统匹配到一个TTP时,他的入口和先决条件就会在这个模型中搜索,如果在模型中存在一个入口包含了所有的先决条件和匹配事件,那么所有的被转移的字节数就会与良性阈值进行比对。低于良性阈值该TTP则被过滤,否则在HSG中创建一个该TTP对应的节点。最后检测引擎计算不同HSGs的“权重和”,当其超过检测阈值时,就发出警报。

5 实验评估

实验评估是在由DARPA组织的红蓝对抗中完成的,通过评估计算出HOLMES最佳的阈值,并衡量HOLMES的表现。最终,将HOLMES部署在真实环境中,在没有先验知识的情况下来检测红队的APT攻击。

5.1 数据

评估所用的数据总结为下表,其中包含了7台主机的9个APT攻击场景,时间跨度为20天,其中包含了攻击数据和良性数据,攻击数据在数据总量中少于0.001%。

5.2 设定

在行动之前,每个主机都有一些接下来会被攻击者利用的漏洞软件。为了进一步混合正常日志和攻击日志,红队同时也会进行良性的活动。

HOLMES会订阅7个Kafka的topics(每个主机一个topics)来消耗数据流以进行实时地分析和预测。为TTPs的先决条件设置 p a t h _ t h r e s = 3 path\_thres=3 path_thres=3(与 p a t h _ f a c t o r path\_factor path_factor相比较的量),设置APT阶段 i i i的权重 w e i g h t = ( 10 + i ) / 10 weight=(10+i)/10 weight=(10+i)/10

5.3 结果简述

检测结果如下表所示,注意到最高的良性活动分数是良性活动中最高的威胁分数:

概念:

  • n-th percentile(第n百分位数,10≤n≤90):小明在一次考试中得了50分,而全班有70%的同学的成绩都小于50分,那么对于由整个班级的考试成绩所构成的一个样本集合来说,第70百分位数等于50。
  • n-th quartile(第n四分位数,1≤n≤3):把所有数据由小到大排列并分成四等份,处于三个分割点位置的数据就是四分位数。
    • 第一四分位数 (Q1),又称“下四分位数”,等于该样本中所有数据由小到大排列后第25%的数据
    • 第二四分位数 (Q2),又称“中位数”,等于该样本中所有数据由小到大排列后第50%数据。
    • 第三四分位数 (Q3),又称“上四分位数”,等于该样本中所有数据由小到大排列后第75%的数据。
    • 第三四分位数与第一四分位数的差距又称四分位距(InterQuartile Range, IQR)。

5.4 寻找最优阈值

如下图所示,使用准确率和召回率来寻找最佳阈值。其中F-score表示准确率和召回率的调和平均值,注意到在[338.25, 608.26]时,F-score达到最大,区间的两个端点分别表示良性活动的最高分数和恶意活动的最小分数,因此阈值应该在该区间内选取

如何确定最终的阈值呢?

我们首先将Threat Tuple的Threat scores开n次方根得到一个数,其中 n = ∑ i = 1 7 w i = 1.1 + 1.2 + 1.3 + 1.4 + 1.5 + 1.6 + 1.7 = 9.8 n=\sum_{i=1}^{7}w_{i}=1.1+1.2+1.3+1.4+1.5+1.6+1.7=9.8 n=∑i=17​wi​=1.1+1.2+1.3+1.4+1.5+1.6+1.7=9.8。我们称这个数为平均严重程度,它反映了每一个APT阶段对threat score总分的贡献值。但是到目前为止我们仅考虑了单个主机上的行为,因此将横向移动阶段 w 5 w_{5} w5​排除在外,因此 n = 1.1 + 1.2 + 1.3 + 1.4 + 1.6 + 1.7 = 8.3 n=1.1+1.2+1.3+1.4+1.6+1.7=8.3 n=1.1+1.2+1.3+1.4+1.6+1.7=8.3。

在取n次方根之后我们发现,F-score取得最大值时,在[338.25, 608.26]之间调整阈值获得不同的threat scores,此时对应平均严重程度的收敛区间为[2.01, 2.16],我们取其中位数2.09.

5.5 性能

(1)图大小:在从起源图到HSG的映射过程中,边的数量减少了1875倍。

5.6 真实实验

将系统放在模拟的企业环境中,在对攻击没有先验知识的情况下,由红队发起APT攻击。将检测的阈值设置为 2.0 9 ∑ i = 1 7 w i = 2.0 9 9.8 = 1378. 2.09^{\sum_{i=1}^{7} w_{i}}=2.09^{9.8}=1378 . 2.09∑i=17​wi​=2.099.8=1378.

(1)误报(False Positive)

将系统放在两周的良性活动环境中,没有发现误报

(2)漏报(False Negative)

  • TTP之间的隐式因果关系:对于避免系统调用的信息流,HOLMES无法直接查看系统实体之间的因果关系。 但是,如果攻击的其余部分通过系统调用展开,则HOLMES仍将重构部分攻击。
  • 多个入口点:作为一种主动规避技术,攻击者可能会利用多个入口点来生成分离的子图。 HOLMES会跟踪每个单独的入口点,直到满足我们的检测阈值为止,并且当不相交的子图之间存在信息流时,它们会关联TTP。 但是,可能需要一些其他分析来完全关联攻击步骤,这些攻击步骤来自不同的入口点,并且之间没有信息流。

6 相关研究

6.1 报警生成

基于主机的入侵检测方法主要分为以下三类:

(1)misuse-based :检测与已知攻击相关的行为

(2)anomaly-based :从正常行为中学习模式,并检测偏离该模式的行为

(3)specification-based :根据专家指定的策略检测攻击

本文属于misuse-based,但是,本文的方法超越了传统的误用检测,它使用了先决条件-结果的模式,当匹配的TTP中涉及的实体之间存在信息流依赖性时,这些先决条件-结果模式将被匹配。

6.2 报警关联

IDS生的成警报对于人工操作员而言太多且级别很低。 需要开发一些技术来总结这些低级别警报并减少其数量。

一些方法使用警报相关性,通过对相似警报进行聚类并确定警报之间的因果关系来执行检测。目前的方法依赖运行在用户空间的第三方应用生成的日志,而且基于像时间戳之类的统计特征,这样并不能很好的检测时间跨度很长的APT攻击。因此HOLMES在不同的攻击步骤之间建立了信息流,使用了内核审计数据。

警报关联的另一项工作依赖于警报在时间上的接近程度。 相比之下,HOLMES依靠信息流和因果关系来关联警报,因此即使在执行步骤非常缓慢的情况下,也能够检测到攻击。

6.3 场景重建

许多研究是基于生成和使用系统调用级别的日志来进行的。大多数的方法是从一个给定的恶意事件开始去追溯导致该事件的原因。

6.4 攻击粒度

有时,审计日志的粗粒度会限制对信息流的推理。例如,如果具有之前加载过敏感文件的进程受到攻击,则攻击者可以在不使用系统调用的情况下在其内存区域内搜索敏感内容。HOLMES会将该文件窃取行为与该进程的其他动作相关联,比如敏感文件读取。

Paper Note - HOLMES:基于可疑信息流的实时APT检测相关推荐

  1. 【基于FPGA的运动目标实时跟踪检测】

    基于FPGA的目标实时跟踪检测(一) 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.系统架构 二.算法设计 1.RGB转YCBCR 2.中值滤波算法 3.帧间差 ...

  2. 读文献—基于深度学习模型的APT检测

    我爱读文献 A novel approach for APT attack detection based on combined deep learning model Do Xuan, C., D ...

  3. 【Paper Note】基于情感分析和关系网络的影视产品评论数据文本挖掘研究

    中国知网链接 摘要 [目的]为了挖掘观众对影视产品的主观感受,建立合适的模型评估影视产品的质量. [方法]本文通过对web影视作品评论数据建立评估模型,通过LDA和关系网络进行分析研究,并将结果与影视 ...

  4. 【Paper Note】基于决策树算法的电信运营商客户流失预测

    1.引言 随着互联网业务的速发展,移动业务市场的客户流失预警成为每一个电信运营商重点关注的内容,在商务智能与机器学习快速发展的当下,运用数据挖掘的方法,实现对电信客户的挽留.转化.精准营销越来越彰显其 ...

  5. 基于面部视频的实时心率检测系统 day four

    CSDN上扒了7 8个代码,找到几个好使的,也把人脸检测这部分内容完成,还是很简单的,都是OpenCV自带的库,调用即可. 选用的这个算法,是将视频分解为一帧帧,对每一帧进行人脸检测,效率较低,明天考 ...

  6. DL之Yolov3:基于深度学习Yolov3算法实现视频目标检测之对《我要打篮球》视频段进行实时目标检测

    DL之Yolov3:基于深度学习Yolov3算法实现视频目标检测之对<我要打篮球>视频段进行实时目标检测 目录 输出结果 设计思路 核心代码 相关文章 成功解决AttributeError ...

  7. 基于软总线的实时组件调度技术研究

    基于软总线的实时组件调度技术研究 摘要:首先分析组件软件工程模式比面向对象的开发模式更优越:接着讨论了组件软件工程的核心机制--软总线,提供组件通讯机制,保证组件的即插即用等功能:然后讨论了软总线中的 ...

  8. 如何基于Flink+TensorFlow打造实时智能异常检测平台?只看这一篇就够了

    作者 | 潘国庆编辑 | Natalie AI 前线导读:Flink 已经渐渐成为实时计算引擎的首选之一,从简单的实时 ETL 到复杂的 CEP 场景,Flink 都能够很好地驾驭.本文整理自携程实时 ...

  9. 基于TensorRT的BERT实时自然语言理解(下)

    基于TensorRT的BERT实时自然语言理解(下) BERT Inference with TensorRT 请参阅Python脚本bert_inference.py还有详细的Jupyter not ...

最新文章

  1. [python]自问自答:python -m参数? (转)
  2. 不要活在别人的生活里(摘自开复网)
  3. 【NOIP2013模拟】小喵喵的新家
  4. WP7上Metro风格的程序栏图标汇总
  5. 杀死初创科技公司的四大工程陷阱
  6. Feign的构建过程及自定义扩展功能
  7. Numpy的切片操作
  8. 文本分类问题不需要ResNet?小夕解析DPCNN设计原理(下)
  9. python函数实例化_用Python实例化函数
  10. android fastboot常见命令
  11. 最便宜的鸿蒙手机,今年不再推出鸿蒙手机,却让老旗舰占据优势,降价后变真香...
  12. ie和chrome浏览器下onproperty事件oninput onpropertychange的相应和相应属性的获取
  13. php多进程兑换电影票,通过读写同一个文件锁来解决并发!
  14. PWM常见输出方法及避坑指南
  15. bottom sheets_使用Sheetson建立由Google Sheets支持的免费电子邮件列表
  16. 开源利器分享:BitBar 坐看今天你的项目涨了多少star
  17. 用好图片,让PPT富有冲击力
  18. 新手入门刷题(专题三)暴力枚举
  19. python内置库有哪些_python 内置库
  20. 字节一面:Redis主节点宕机,如何处理?

热门文章

  1. ZONe Energy Programming Contest C.MAD TEAM
  2. HTML + CSS 之 闭合标签与双闭合标签
  3. 弘辽科技:淘宝的退款大单怎么做?做退款单要注意什么?
  4. VoIP Push 在海外音视频业务中的应用
  5. 中电信联手酷派打造千元智能手机新标杆
  6. Linux命令学习压缩打包
  7. 2020年就要过去了,我一点也不怀念它
  8. 几种为相片添加地理GPS坐标信息的方法
  9. 【绝对实用】百度网盘下载助手
  10. DevOps模式下测试左移和测试右移