【摘要】
越来越多的信息以非结构化文本数据的形式在网上共享,尤其是在社交媒体上,这为补充传统的网络威胁情报来源提供了机会。由于人工无法处理如此庞大的数据量,我们探索了一些使用机器学习来协助分析的可能性。我们特别关注检索与指定威胁行动者相关的信息。通过对现有的语言模型进行微调,以特定的下游任务为基础,基于伪自动注释的数据,可以获得检测和提取之前未见过的威胁参与者的模型。我们在不同的条件下执行多次评估,其中一些表明模型确实能够产生在半自动分析设置中有用的结果。此外,我们将此视为将通用语言模型应用于特定领域任务的一个案例研究,并反思了一些更普遍的经验教训。
1.0介绍
威胁情报是网络防御不可或缺的一部分。技术系统的日志记录和监控是网络威胁情报 (CTI) 的传统来源。越来越多的在线信息共享,尤其是在社交媒体上,为补充传统资源以提高网络环境中的态势感知提供了机会。大规模利用这些新资源需要以比任何分析师都高得多的处理速度筛选大量非结构化数据。因此,需要半自动分析,将分析师的思想优势与计算机的处理能力相结合。
近年来,自然语言处理(NLP)和机器学习(ML)的发展提供了强大而通用的语言模型,这些模型通过大量的文本数据和计算能力来表示对语言的一般理解。这些模型可以在更小的数据量上进行微调,以便学习特定的任务。在本工作中,我们探索了将此类语言模型应用于网络威胁情报 (CTI) 场景的可能性,特别是我们重点关注自动识别文本中提到的(以前未见过的)网络威胁参与者的任务。这种功能除了与网络威胁情报 (CTI) 相关的好处之外,还可以作为将通用语言模型应用于特定领域任务的案例研究,这些任务不能假定已有的数据集和评估基准可用。
2.0背景
之前的大量OSINT文本数据中网络威胁情报 (CTI) 自动提取的工作包括在2017年IEEE数据挖掘国际会议研讨会(ICDMW)中提出的一个“在线讨论中的网络威胁的早期预警”系统。该系统能够根据来自Twitter和暗网论坛的数据,使用基于术语和字典的文本分析方法,对特定类型的网络威胁产生预警。在漏洞和数据泄露两种情况下验证了该方法的有效性。
在这项工作中,我们使用了预先训练好的语言模型。在自然语言处理(NLP)中,语言模型是学习语言的概率表示的机器学习模型。这些模型以一种无监督的方式训练,其中模型被给予一个文本语料库和一个无监督的前置任务。前置任务是不需要人工注释的任务。一个常见的例子是语言模型目标(Language Model Objective, LMO),该模型的任务是预测给定文本序列中的下一个单词。前置任务的目的不是解决任务本身,而是为下游任务的微调模型做准备。
最近的语言模型都是基于一种名为Transformer的神经网络。以前的方法一次只处理一个输入序列的元素,而Transformer可以并行处理序列中的所有元素。这种更有效的文本处理形式使得使用更多的训练数据和更大的模型成为可能。这反过来又有助于基于transformer的语言模型在各种自然语言处理(NLP)任务中实现最先进的性能。
在这项工作中,我们评估了四种不同的基于转换器的语言模型:来自Transformer (BERT)的双向编码器表示(Bidirectional Encoder Representation)是推广预训练算法的方法。BERT使用了两个前置任务:掩码语言模型(MLM) 目标和下一个句子预测任务(NSP)。对于掩码语言模型(MLM),在输入文本中的一个标记子集被屏蔽,模型必须预测原始标记。在NSP中,该模型预测两个文本序列在训练语料库中是否重合。本文中使用的所有模型,除了 DistilBERT,本文中使用的所有模型都具有使用两个前置任务的预训练设置。BERT有两种规格可供选择:1.1亿个参数的BERT-base模型和3.4亿个参数的BERT-large模型。由于训练和评估过程中的硬件限制,我们使用较小的BERT-base版本。该模型利用图书语料库和英语维基百科进行预训练。
RoBERTa是BERT的一个改进版本,对文本表示、训练模式和训练语料库进行了细微的调整。最初的BERT模型是在英语维基百科和图书语料库上训练的,而RoBERTa则是在大约160GB的新闻文章和网络文本上训练的。RoBERTa在表示文本的方式上也有所不同,它使用Byte Pair Encoder (BPE) 分词器。与BERT的WordPiece分词器相比,BPE更擅长处理词汇表外的单词,因此它适合于具有不同词汇表的在线文本。与BERT类似,我们使用了具有1.1亿个参数的较小版本。
DistilBERT 是 BERT 的精炼版本。在机器学习的背景下,DistilBERT用较大模型(教师)的输出作为输入来训练较小模型(学生)的过程。DistilBERT可以帮助减小模型的大小,同时仍然保持其性能。较小的模型可以防止对下游任务的过度拟合。DistilBERT 使用 BERT-base 的输出进行训练,包含的参数减少了 40%,同时仍保留了 BERT-base 大约 97% 的性能。
BERTweet 是 RoBERTa 的修改版本,在 8.5 亿条英语推文上进行了预训练。BERTweet 还具有独特的标记器和预处理方案来处理推文中常见的元素,例如主题标签、表情符号和用户名。
3.0方法
在微调语言模型之前,必须定义一个可学习的任务,并获得反映该任务概念的数据。我们使用 STIX(结构化威胁信息表达)数据格式作为网络威胁情报 (CTI) 场景的模型,并作为识别感兴趣的信息类型的起点。使用现有和已建立的框架不仅可以节省时间和精力,还有助于促进通过结构化和非结构化数据获得的信息之间的兼容性。STIX 由许多域对象(例如,威胁参与者、攻击模式、恶意软件)以及属性和对象之间的关系组成。其中两个域对象在这里特别感兴趣,威胁参与者和入侵集。前者代表威胁背后的行为者(例如个人、团体、组织),而后者是威胁本身的更抽象的表示。然而,它们都与命名实体有关。虽然从理论上讲,差异是明确定义的,但这并不能保证它在野外收集的数据中得到很好的反映。基于这一观察,决定从现在开始,威胁参与者的研究对象应涵盖 STIX 威胁参与者和 STIX 入侵集的概念。通过定义任务,可以制作相关数据集(第 3.1 节)并训练机器学习模型(第 3.2 节)。
3.1制作数据集
原始数据是通过Twitter的API收集的,基于一组与网络相关的通用关键词,如“网络”和“恶意软件”。这将生成大量且高度不均衡的数据集,其中只有一小部分实例与威胁行动者相关。有监督的机器学习需要有标签的数据,在这种情况下,推文中提到威胁行动者的正面例子,以及没有提及的反面例子。数据的手工标注通常非常耗时,因此在将机器学习应用于特定任务的过程中可能会成为一个阻碍。相反,我们选择探索使用基于规则的自然语言处理(NLP)技术以伪自动方式注释数据的可能性(第3.1.1节),并结合人工监督的数据清理工作(第3.1.2节)。我们只使用tweet的文本内容,而不使用元数据。
3.1.1 伪自动注释
基于规则的自然语言处理(NLP)技术依赖于手工定义的语言模式,因此相对简单和有限。然而,与数据驱动的技术相反,它们的好处是不需要预先使用任何数据。因此,它们作为一种开始数据收集和探索新数据的快速方法是有价值的。对于威胁行动者,基于规则的自然语言处理(NLP)是一种有效的工具,用于查找提及姓名已知的行动者的信息。因此,我们能够自动将下载的推文分类为两类,文本包含已知威胁参与者的名称,文本中不包含的。虽然这种注释方法无疑节省了时间和精力,但它也增加了标签不准确的风险。任何包含(仅)未知威胁行动者的推文,即没有“在名单上”的名字,将成为虚假反面例子。当已知威胁参与者的名称也具有其他含义并在不同的环境中使用时,将创建假阳性示例。
3.1.2数据清理
最初的伪自动注释有很高的误报率。例如,像“不要忘记我们的铂(Platinum)网络星期一特卖”这样的文本会被贴上积极的标签,因为文字Cyber(一个Twitter流的关键字)和铂(Platinum)(也是一个威胁人员的名字)。为了减少这个问题的影响,我们首先手动检查一个积极推文的子集。基于这些错误的例子,我们构造了额外的基于单词的过滤规则来消除重复出现的错误。
此外,大量的网络推文是由自动转发网络相关新闻文章的Twitter账户产生的。因此,我们的初始数据集包含引用同一篇原始文章但由不同账户生成的多条tweet。这类推文通常都是一样的——唯一不同的是他们引用了多少原始文章,使用了多少标签,以及任何url的格式。这些几乎重复的推文组的大小,以下称为重复组,范围从2到15。在训练和评估过程中,这些重复的群体会造成各种各样的问题。它们可使不太常见的威胁行动者占据所有样本的很大一部分,因此可能会对这些推文过度拟合模型。类似地,如果评估集包含许多重复项,它可能会使评估结果产生偏差。
我们利用 MinHash ,一种局部敏感散列算法,可以在接近线性的时间内找到相似的文档来删除这些重复。我们使用 datasketch python library 中的方法。由于我们正在寻找近乎完美的副本,因此我们使用 k=9 的 shingle 大小。如果两条推文的 Jaccard 相似度超过 0.4 的 shingle ,我们将它们视为重复。为了找到一条推文的所有重复项,我们将数据集中的推文概念化为一个图,其中推文是节点,如果两条推文是重复的,则它们是连接的。然后我们通过使用深度优先搜索遍历图来找到所有连接的组件,并且只保留每个连接组件中最长的推文,丢弃剩余的推文。
最后,我们清理每个tweet的内容。一些令人分心的元素——超链接、用户名和电子邮件地址——被替换为相应的屏蔽标签,因为它们不包含与任务相关的信息。类似地,表情符号也被转换成文字描述,例如👍👍被转换成:thumbs_up。这使得词汇中没有表情符号的模型能够理解角色的情绪。
3.2微调语言模型
本文中提到的查找指定威胁参与者的任务分为两个子任务。第一个,我们称之为威胁行动者检测,是一个二元分类任务。模型的目标是确定给定的文本(tweet)是否提到了威胁行动者。第二个任务(我们称之为威胁参与者提取)的目标是识别威胁参与者的名称。
3.2.2数据分割
在威胁行动者环境中使用机器学习的目的是获得自动发现文本中提到的新的威胁行动者的能力。因此,我们希望避免模型只记住训练数据中威胁参与者的实际名称的情况。我们还希望确保评估反映了模型泛化到以前未见过的威胁参与者名称的能力。为此,我们构造了数据的训练、验证和测试分组,以便在多个分组中不会出现威胁参与者的名称。在我们的数据集中,威胁行动者具有幂定律分布——一小部分威胁行动者构成了大部分正面例子。为了创建分组,我们根据数据集中出现的频率对威胁参与者进行排序。对于训练分组,我们选择最频繁的威胁行动者和他们对应的推文,直到我们获得至少80%的所有正面推文。从剩余的威胁参与者中,我们继续选择最频繁的,直到我们达到至少10%的所有积极推文的验证分割。然后将剩余的威胁行为体用于测试分组。任何包含两个或更多角色的推文将被删除,以防止重叠。由此产生的训练分组包括16个不同的行为者,验证分组6个,测试分组48个。总共使用了大约3300条积极的推文(通过清理过程,这一数字减少到现在的8,000条)。
3.2.3继续训练
除了 BERTweet,我们的预训练语言模型是在语料库上预训练的,这些语料库与我们的网络推文目标域几乎没有相似之处。因此,这些模型在对我们的数据进行微调时可能会发生域转移,从而导致它们表现不佳。解决这个问题的一种方法是继续进行预训练。这个想法是继续对从目标域采样的新数据集进行无监督预训练。这种持续的预训练将模型调整到目标域,从而减少了模型在后续微调期间经历的域偏移。
出于这个原因,从下载的网络推文中抽取了用于继续预训练的额外数据。再次使用之前描述的数据清理方法,并过滤掉与注释数据集重叠的推文。为了研究预训练集的大小如何影响下游性能,我们编译了两个不同大小的数据集——一个有 25000 条推文,另一个有 250000 条推文。然后,我们为四种语言模型中的每一种生成了三个不同的实例,一个没有额外的预训练,一个用于每个预训练数据集。掩码语言模型目标被用作预训练目标。
3.2.4威胁主体检测
威胁参与者检测的任务包括对一条推文是否包含一个(或多个)威胁参与者的提及进行分类。我们用2.2.2节中描述的数据分割来训练每个语言模型。从下载的网络推特集合中抽样了每一个分组的反面例子。为了防止过度拟合到一个小的负样本集,我们在训练集中使用了4比1的负样本对正样本。更大比例的反面训练会产生非常低的召回率,而更小的比例会增加模型过拟合的风险。对于验证集和测试集,正例和反例之间的分布保持均匀。
3.2.5威胁主体提取
威胁参与者提取是一个标签级别的分类任务,其中每个标签要么是威胁参与者名称的一部分,要么不是。对于标签级注释,将对tweet进行标签化,并为与已知威胁参与者名称重叠的标签给予正面标签。训练类似于检测任务,除了在标签输出和最后的分类器层之间有一个额外的退出层。由于时间的限制,对于这个任务,我们只使用RoBERTa语言模型,因为它的标记器是提取正确标记的最简单的工具。为了降低任务的难度,我们假设模型在推理过程中接收到的每条推文至少包含一个威胁行动者,因此在训练过程中只包含这类推文。我们认为这种简化是合理的,因为威胁参与者检测模型旨在解决过滤掉提到威胁参与者的tweet的任务。
3.2.6训练和超参数调优
作为训练transformer的标准,我们从一个热身阶段开始——学习速率初始化为接近0,并不断增加,直到n步之后达到最大学习速率。我们还使用线性学习速率衰减,即学习速率在每一步之后都下降,直到它达到0。所有模型实例都为一个epoch进行了微调。我们在四个参数上执行超参数调优——权重衰减因子、热身步数、最大梯度范数和最大学习速率。对于提取任务,我们也为最终的分类层寻找最优的辍学率。我们使用贝叶斯优化方法来研究参数space。对于每个模型,我们执行50次迭代,并选择在验证集中达到最高Fl-score的模型。
作为训练 Transformer 的标准,我们从一个预热阶段开始——学习率被初始化为接近 0 并增加,直到在 n 步后达到最大学习率。我们还使用线性学习率衰减,其中学习率在每一步之后都会降低,直到达到 0。所有模型实例都针对单个 epoch 进行微调。我们对四个参数进行超参数调整——权重衰减因子、预热步骤数、最大梯度范数和最大学习率。对于提取任务,我们还搜索最终分类层的最优丢失率。我们使用贝叶斯优化来探索参数空间。对于每个模型,我们执行 50 次迭代,并选择在验证集上获得最高 F1 分数的模型。
4.0结果
两组不同的数据被用来评估模型的性能。第一个是用于训练的伪自动标注数据的保留区,这是机器学习领域的一种常见做法。这项评估在第4.1节中描述。第二个是在稍后的时间点收集的一组数据,因此不与第一个重叠。对于第二组数据,评估是在手工应用标签方面进行的,如章节4.2所述。
4.1伪自动标注数据的计算
我们评估了每种语言模型((和预训练条件)在我们的保留测试集中对威胁行动者检测任务的性能。结果如表4-1所示,使用的指标是精度、查全率和Fl-score(这是精度和查全率的调和平均值)。我们注意到,所有模型在每个指标上的性能大致相似。在推特和网络文本(BERTweet和RoBERTa)上预先训练的模型似乎有一点优势,特别是在召回方面。额外的预训练的效果可以忽略不计,也可以略显积极。关于Fl-value的最佳模型是在250K条推文上预训练的RoBERTa模型。然而,在没有任何额外的预训练的情况下,BERTweet具有最高的准确率,同时仍然能够实现合理的召回。
表4-1威胁参与者检测任务对伪自动标注数据的评估结果
威胁行动者提取任务的结果(精度、召回率和标签级别的Fl-score)如表4-2所示。在这里,我们注意到额外的预训练有一个更明显的积极效果,在没有预训练和完全前训练之间,f -score增加了5个百分点。我们观察到低召回率的两个可能原因。首先,该模型难以处理威胁参与者边界,例如,长威胁参与者名称中的最后几个标记经常被忽略。其次,该模型经常忽略标签中提到的威胁行动者,这一点更难检测,因为几乎没有周围的语言环境来帮助该模型。
表4-2威胁行动者抽取任务对伪自动标注数据的评估结果
4.2手工标注评价
作为对伪自动注释数据集评估的补充,通过手动注释对基于 RoBERTa 的模型(对 250k 网络相关推文进行额外预训练)进行额外评估,其中手动应用的标签与检测和提取模型的结果。对一组 800 条新下载的推文进行了注释,每条推文都由三个人阅读,他们将其分类为正面或反面,并(在正面情况下)记录威胁者的姓名。多数票决定了最终的分类标签。对此数据执行的唯一数据清理是手动删除重复项。
检测任务的评估结果如表4-3所示。规模阈值表明某些(从0到1)分析文本的模型必须确实提到威胁行为者被视为一个积极的例子(在评价pseudo-automatically注释数据阈值0.5)中使用。使用的指标还是精确度、召回率和f1分数。人工标注的数据被采样,在阈值为0.5时,从检测模型中进行正预测和反面预测之间的平衡。这种分布与自然分布明显不同,在我们的网络相关推文流中,只有非常小的一部分包含威胁行动者。
表4-3:对不同阈值的威胁行动者检测任务进行手工标注的评估结果
威胁参与者提取模型为文本中的每个标签返回一个值,指示该标签成为威胁参与者名称一部分的概率。此标签级信息可用于生成由原始文本中的完整单词组成的名称建议。这些名称建议(威胁行动者)候选人,可以通过对机器学习结果或多或少的严格条件获得。在这里,我们测试了四组这样的条件,所有这些条件都考虑了文本包含威胁参与者(检测阈值)的确定性和单个标签成为威胁参与者名称一部分的概率。候选类型candH、candm和cand使用0.9、0.75和的检测阈值分别为0.5。候选类型candr也使用阈值0.5,但必须在每个文本中至少找到一个候选。在候选类型之间,标签级概率的条件也不同。
将得到的候选对象与人工记录的威胁参与者名称进行比较,结果如表4-4所示。精度、召回率和Fl-score指标是在整个手工标注的推文集合上计算的。我们还引入了一个额外的度量,我们称之为I-precision。这是只考虑那些注释者共同认为提到了威胁行动者的tweet的精确度。这并不能反映提取模型在现实中的性能,但可以让我们了解在理想条件下,即与完美的检测模型相结合时,提取模型的性能如何。
表4-4:对威胁行动者抽取任务进行人工标注的评估结果
检测任务的结果与基于伪自动标注数据的结果有显著差异(阈值为0.5)。特别是在准确率上有很大的下降,而召回率却略有提高。抽取任务的结果没有直接的可比性,但低正确率和高查全率的趋势是一致的。I-precision值明显较高,说明检测模型的低精度是提取模型精度非常低的主要原因。
为了向模型的性能添加更多的透视图,我们进行了一个额外的评估,这意味着在一个专门构建的仪表板的帮助下,反映更多的用户类条件(参见图4-1)。
图4-1:为人工评估机器学习模型而设计的仪表盘。它显示了在选定的时间间隔内,被分类为包含具有不同阈值的威胁参与者的tweet的百分比(饼图),以及威胁参与者候选的前10个列表。
在两周的时间里,机器学习模型下载并分析了一批新的Twitter数据。然后在24小时的14个数据间隔中对结果进行研究。对于每个间隔,将推文分类为正的比例记录下来。每种候选类型的前10个最常见的威胁参与者候选人被手动分类为以下类别:威胁参与者、恶意软件、组织、人、首字母缩略词、其他。这些类别被选择来涵盖我们根据经验知道在错误分类中常见的命名实体的类型。人工分类前10个候选对象的结果如图4-2所示,对于每个候选类型和类别,显示了每个时间间隔(即每个类别14个数据点)命中次数的分布情况。
图4-2:每种威胁actor候选类型(candH、candM、candz、cande) top10列表中,每种实体类别每24小时命中次数分布。
阈值为0.5时,推文被分类为正的比例在4.0% - 8.4%之间变化,整个时间段的平均值为5.4%。换句话说,即使阈值很低,也有将近95%的推文被过滤掉了。由于自然分布非常不平衡,这是一个好的迹象,这意味着模型比训练数据更接近正反之间的真实分布。这对于早期高召回的迹象也是令人鼓舞的,因为它们没有被大量的积极分类明显地解释掉。
对于概率要求最严格的 candH,我们看到前 10 名中实际威胁参与者的平均数量最高,为 2.4(其他候选类型的平均数都在 2 左右)。对于所有候选类型,威胁参与者数量的中位数为 2。所有类型在 14 个间隔中至少有 1 个有 0 个威胁参与者,candH 在一个间隔内最多有 6 个,candM 最多有 5 个,另外两个最多有 4 个。关于与其他实体类别的混淆,所有的模式都是相同的 候选类型。该模型最常见的特定错误是将恶意软件的名称误认为是威胁参与者的名称。这并不奇怪,因为这些实体类型的语言环境可能非常相似甚至相同——即使是人类读者也可能需要背景知识来区分它们。另一类是最普遍的一类,但没有发现其他错误类别。
5.0讨论
对helout测试集的评价在准确率和召回率方面都得到了很好的结果,而第二次评价的结果表明,准确率严重不足。数据收集、注释和训练的额外迭代可能会提高性能。合理的高召回率表明,检测模型能够对推文进行相对有效的过滤,其中大部分有趣的推文(在本例中是那些提到威胁行动者的推文)被保留,而大量不相关的推文可以被自动丢弃。提取模型提供的大多数威胁参与者候选人是不准确的,无论候选人类型如何,但前10个列表通常包括一到两个相关的建议。在半自动分析中,这可能在某种程度上是有用的。提高提取任务性能的一种可能的方法是利用现有的通用命名实体识别(NER)模型,或作为微调的起点,或作为与我们的模型一起的集成安排。
不同评估的结果提供了一些关于将机器学习和语言模型应用于没有可用数据集或建立评估基准的特定领域和小生境问题的可能性和挑战的见解。
两次评估结果之间的巨大差异表明,基础数据集代表了完整数据流的不同部分(考虑到它们是在不同的时间点以不同的方式构建的事实,这不足为奇),并且模型 还没有学会概括到足够大的程度。这强调了思考如何对数据集进行采样的重要性,尤其是当数据的分布必须明显偏离自然分布时。
基于规则的NLP技术对于数据的伪自动标注是非常有用的工具,这需要数据驱动的方法,如监督机器学习。如果使用嘈杂的数据源(如Twitter),则可能需要进行一些手动数据清理,以使数据可用。重要的是,不仅要考虑数据集中包含了什么,还要考虑是否缺少什么。在威胁行动者的情况下,我们确定的一个潜在的改进路径是在训练数据中包括“更好的”反面例子,例如通过专门收集提到命名恶意软件的tweet,以帮助模型更好地区分不同类型的实体。
另一个重要的问题是,机器学习问题是否应该被划分为子任务,如果是,如何划分。在本例中,我们使用两个模型,一个用于检测,一个用于提取威胁行动者。还可以进一步分割任务,例如,在链的开头添加一个经过训练的模型,以确定一条推文是否与网络安全有关。这样的模型可能会过滤掉不相关的主题,比如“网络欺凌”,从而使威胁行动者检测模型的任务更加明确(因为它可以更多地关注威胁行动者与其他网络安全相关概念的区别)。然而,在链中过早地依赖模型会带来早期错误传播的风险,并且会使模型更加混乱。我们的提取模型显示了这种症状,因为它的性能(不出所料)似乎受到检测模型的低精度的负面影响。在这种情况下,另一种选择是让提取模型在训练时看到大量的反面例子,从而有希望教会它更好地处理未来的检测模型错误。一般来说,当涉及到任务设计时,新的应用程序可能需要一些实验。此外,作为子任务的迭代注释数据和再训练模型可能是有益的。
如何评估模型性能并不总是显而易见的,不同的评估可能产生不同的结果,如威胁行动者的情况所示。在缺乏既定的基准的情况下,不太可能有针对小范围问题的基准,人们必须自己为评价设定框架。重要的是要考虑一个特定的评估方案可能会引入哪些潜在的偏差和错误来源。不仅查看经典的度量标准,而且还可以考虑如何使用结果以及这对性能需求意味着什么,这也可能是有益的。
6.0结论
通过基于规则的自然语言处理(NLP)技术,使用数据的伪自动注释,对网络威胁行动者检测和提取环境中的语言模型进行微调,似乎是可行的。然而,二次评估的结果表明,可能需要数据收集、注释和训练的多次迭代。在更类似用户的条件下进行的评价表明,这些模型能够产生在半自动分析中可能有用的结果。
作者:
瑞典斯德哥尔摩-瑞典国防研究机构(FOI)
汉娜·莉娅 [email protected]
卢卡斯·伦德马克 [email protected]
上述资料原文及机器翻译已上传小编知识星球
长按识别下面的二维码可加入星球
里面已有近千篇资料可供下载
越早加入越便宜
续费五折优惠