大家好,我是ABC_123。前面几期分享了几篇关于机器学习/深度学习/神经网络学习的文章,没想到自己在2016、2017年时的一些研究,在人工智能备受关注的当下,使得自己还没有掉队。当年在识别DGA域名的任务时,主要采用的是LSTM模型,这两天经过反复试验发现,基于统计和字符特征的XGBoost算法识别效果更好一些,接下来给大家讲解一下。本次围绕 DGA 域名检测任务,实验了多种机器学习模型,如基于字符序列的 MLP、RNN/LSTM、Keras DNN 以及基于 2-gram 手工特征的朴素贝叶斯等,经过充分调参后,最终还是选择了计算速度和准确率都比较好的 XGBoost 算法。XGBoost的核心思想不再停留于元音比例、长度等浅层统计特征,而是通过 TF-IDF 结合 N-Gram 将域名拆解为大量细粒度的字符组合特征,从而刻画 DGA 域名在字符结构上的隐含模式;再借助 GPU 加速的 XGBoost 强大非线性拟合与特征组合建模能力,学习这些复杂特征与恶意域名之间的深层关联。该方法将 DGA 识别视为一个二分类问题,通过从简单特征向 NLP 表征的转变,实现了较好的检测效果。
get_feature_ngram_improved 方法是该脚本中核心的特征工程函数,其主要功能是将原始的文本格式域名(如google.com)转换为机器学习模型能够理解的高维数值矩阵。1. 加载数据:调用 load_alexa() 和 load_dga() 分别读取正常域名白名单和恶意域名黑名单。2. 打标签:将正常域名标记为 0,将 DGA 恶意域名标记为 1,并将两者合并为一个总数据集。3. 显存优化:如果 use_gpu=True(检测到显卡),将 max_features(最大特征维度)设置为 25,000;如果仅使用 CPU,则降为 5,000。这意味着在强力硬件支持下,它能保留多达 5 倍的细节特征。
这个算法可能是目前处理表格和文本分类任务最强的算法之一,但对于这个算法,ABC_123 并未对其数学公式进行深入理解,早期学习卷积神经网络进行图像识别时,那些数学公式耗费大量精力,因此现在更关注这些模型在实际工作中如何调参使其效果更好。它与神经网络算法不同,不是一个逐步学习训练的大脑,也不像LSTM那样模拟人脑的阅读过程,而像是训练一个委员会。它由成百上千棵决策树组成,它的训练过程是迭代的:第1棵树预测错了哪些域名,第2棵树就专门去纠正这些错误,第3棵树再纠正前两棵树的错误......以此类推,误差在多轮迭代中不断被削弱。最后将这 1500 棵树的预测结果进行加权汇总,从而在 DGA 域名识别任务中获得高精度且稳定的预测效果。
刚才提到,在 XGBoost 中,模型是由许多棵决策树组成的。max_depth 限制了每一棵树从根节点到叶子节点的最大层数,本次Python代码中设置了10层。
learning_rate: 0.03 指的是学习率(也叫步长),它决定了每一棵新树对最终预测结果的贡献程度,同时也防止过拟合并提升泛化能力。在 XGBoost 这类集成算法中,模型是通过不断添加新的决策树来修正前一棵树的错误的;这意味着每一棵新生成的树,其计算出的权重在加到总模型中时,会被缩减为原来的 3%。这里打个比方,learning_rate设置为0.3, 模型学得很快,几百轮就能拟合数据,单棵树的影响力过大,容易忽略数据中的细微噪声,甚至在最优解附近震荡,无法达到极致的准确率。learning_rate设置为0.03,每棵决策树只贡献0.03的力量,那么单棵树如果学偏了或者钻牛角尖似的生成了过拟合,对整体结果的影响也是有限的。
num_boost_round=1500(增加轮次): 因为每一步走得只有 0.03 这么小,要走到终点达到收敛,就必须走更多的步数。源码中将训练轮次从默认的 100 左右提升到了 1500 轮,这是一个典型的 “低学习率 + 多树” 策略。深树很容易过拟合(太聪明了,容易记噪音)。此时,0.03 的低学习率起到了刹车/正则化的作用,防止这些"聪明的深树"把模型带偏。
subsample (0.85) : 当模型准备建立第 N 棵树时,它会先做一个随机抽样,只拿出总训练集中 85% 的数据给这棵树学习,剩下的 15% 在这一轮中被忽略。如果不设置(默认为 1.0),每一棵树都会看到完全一样的所有训练数据。这容易让模型“死记硬背”数据中的特定噪音。
colsample_bytree: 0.85:假设你要派 100 个侦探(相当于100 棵决策树)去查案(相当于识别DGA恶意域名),你给每个侦探提供所有的线索工具(指纹、脚印、监控、DNA...),他们可能都会偷懒只盯着最明显的监控录像看。你强制规定(相当于随机抽 85% 的特征列),侦探 A 不许看监控,侦探 B 不许验 DNA,侦探 C 不许看指纹...... 结果这些侦探们只能拼命挖掘其他不起眼的线索去区分DGA域名。
colsample_bylevel: 0.8:表示在决策树的每一层节点进行分裂时,仅从全部特征中随机采样 80% 作为候选特征子集参与该层的最优分裂搜索。当这棵树开始分裂第一层节点时,它不直接用那 21,250 个特征,而是再从中随机抽 80%(约 17,000 个)来寻找最佳分割点;当树生长到第二层时,它会重新从那 21,250 个特征中再随机抽 80%。
在 XGBoost 中,每当决策树想要把一个节点切分为两个子节点,比如把长度 > 5的域名分一组时,它会计算这次切分能让模型的损失函数(Error)下降多少,这个下降值叫增益(Gain)。这里就引出了Gamma 的定义,它是节点分裂所需的最小损失减少值(Minimum Loss Reduction):如果 Gain > Gamma,准许分裂,树继续长高;如果 Gain < Gamma,收益太小,不划算,取消分裂(剪枝),该节点成为叶子。

reg_alpha: 0.05 (L1 正则化):它倾向于让不重要的特征权重变为 0,这被称为稀疏性(Sparsity)。当前python代码使用了 N-gram 提取特征,在 GPU 模式下特征维度高达 25,000 维。在这 25,000 个字符片段中,肯定有大量是无用的噪音。设置 reg_alpha: 0.05,虽然数值不大,就是告诉模型:如果觉得某个特征(比如 'zqx')作用很小,那就干脆把它当作不存在,权重设为0。这能帮助模型自动进行特征筛选,去除冗余信息。
reg_lambda: 1 (L2 正则化):它倾向于让特征的权重变小,但不会变成 0,防止模型对某一个特征产生过度依赖。
min_child_weight:3 :如果模型试图创建一个新的分类规则,但发现按照这个规则切分后,某一边的叶子节点里只有 1 个或 2 个样本(权重和小于 3),模型就会判定这个规则太特立独行了,不具备普遍性,从而放弃这次切分。
早停机制 (Early Stopping):代码在训练时配置了 early_stopping_rounds=100。这意味着如果模型的验证集误差在 100 轮内没有下降,训练就会自动停止。这不仅节省时间,更关键的是能自动找到模型的“最佳状态”,防止训练过度导致在测试集上表现下降。
n-gram 的作用是将一个完整的域名切分成一个个小的字符片段,以便捕捉局部模式。传统的 DGA 检测可能只计算元音比例或字符去重数,但该代码的高精度版本使用了改进的 N-Gram 技术。ngram_range指的把字符串按长度为 n 的连续子串进行切分,对于DGA域名的识别,我们选用 ngram_range=(2, 54) ,表示同时提取 2-gram、3-gram、4-gram 、5-gram 三种特征,意味着脚本不仅看2个字符的组合,还看长达5个字符的组合,扩展到 5-gram 能捕获更长的特定恶意词根。

以域名 google 为例:
2-gram (连续2个字符):go, oo, og, gl, le、3-gram (连续3个字符):goo, oog, ogl, gle
4-gram (连续4个字符):goog, oogl, ogle、5-gram(连续5个字符):googl,oogle
所有这些特征合并在一起,构成最终的特征向量。得益于GPU算力支持,max_features设置为25,000维,由于组合后特征数量非常大,max_features=25,000 会只保留最重要的20,000个特征(按词频/TF-IDF分数排序),避免维度爆炸。
高维特征空间:得益于 GPU 加速,代码将 max_features 提升到了 25,000 维。更大的特征空间意味着模型可以学习到更丰富、更稀疏的字符组合细节,从而大幅提升识别的细腻度。
在特征工程阶段,采用 TF-IDF(Term Frequency–Inverse Document Frequency) 对域名字符串进行向量化处理,具体通过 scikit-learn 提供的 TfidfVectorizer 将原始文本转换为可供模型使用的数值特征矩阵。TF-IDF 不仅能够刻画字符模式在单个域名中出现的频率,还通过对在全量域名中普遍出现的模式进行权重衰减,从而突出具有区分度的局部特征。

TF-IDF(词频-逆文档频率)的作用是对前面切分得到的每一个 n-gram 片段进行加权打分,用以衡量其在区分域名是否具有恶意特征时的贡献程度。其中,TF(Term Frequency)刻画的是某个 n-gram(例如 “qz”)在当前域名中出现的频率,反映其对该样本的局部重要性;而 IDF(Inverse Document Frequency)则描述该 n-gram 在全量域名集合(包括白名单与黑名单)中的稀有程度。举个例子:如果一个片段在很多正常域名里都出现(比如 ".com" 中的 "co"),它的 IDF 值会很低,权重被降低(这属于通用噪音);如果一个片段(比如 "qkz")只在特定的 DGA 域名中频繁出现,而在正常域名中几乎没有,它的 TF-IDF 值会很高。
特征筛选:代码中通过 max_features=25000 (GPU模式) 告诉模型,只保留 TF-IDF 分数最高、最具代表性的 25,000 个 n-gram 组合作为最终的特征向量。
经过各种调参之后,使用GPU云环境训练模型,然后自己制作的一个DGA域名的列表,发现识别率还是不错的。1. n-gram 相当于是捕网,它把域名打碎,捕获各种长度为 2 到 5 的字符碎片;TF-IDF 是筛子,过滤掉那些到处都有的普通碎片,高亮保留那些能显著区分“正常”与“恶意”的独特碎片,并将它们转化为数值矩阵供 XGBoost 训练。2. 大家有好的建议,欢迎给我留言。
为了便于技术交流,现已建立微信群"希水涵-信安技术交流群",欢迎您的加入。
公众号专注于网络安全技术分享,包括APT事件分析、红队攻防、蓝队分析、渗透测试、代码审计等,每周一篇,99%原创,敬请关注。
Contact me: 0day123abc#gmail.com
OR 2332887682#qq.com
(replace # with @)