机器学习实践-DGA检测
什么是DGA
DGA(Domain Generate Algorithm域名生成算法)是一种使用时间,字典,硬编码的常量利用一定的算法生成的域名。DGA生成的域名具有为随机性,用于中心结构的僵尸网络中与C&C服务器的连接,以逃避域名黑名单检测技术。
DGA域名使用的大致流程如下:首先攻击者通过运行算法生成DGA域名,然后随机选择其中的少量域名进行注册,并将域名绑定到C&C服务器。受害者的机器被植入恶意程序后运行DGA算法生成域名,并检测域名是否可以连接,如果不能连接就尝试下一个域名,如果可以连接就选取该域名作为该恶意程序的控制端服务器域名。
DGA域名特点主要有以下几点:1)DGA域名大部分都没有注册,因为注册域名需要一定的成本,而且从DGA域名的使用流程来说只需要注册一个域名就可以达到目的。2)DGA域名具有伪随机性,与正常的域名具有一定的差别,一般正常的域名在拼写上符合一定的规律。3)DGA域名一般长度较长,长度短的域名往往已经被注册了。
DGA域名举例:
CryptoLocker(ryhqdtlbwacoikd.net)
Gameover(ocdjtg1pm9csac7cbzpx7r75a.com)
Necurs(mjlglqmatygruta.pw)
Wannacry(iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea.com)
xshell(xmponmzmxkxkh.com)
ccleaner(ab3d685a0c37.com)
pykspa_v2_fake(sgsqoqmyceqm.org)
基于DGA域名的特点,虽然采用传统的域名黑名单也可以检测一些已知的DGA域名,但是对于一些未知DGA域名就束手无策了。我们还可以通过域名长度,是否注册,以及注册的时间和注册信息进行判断,但是这种方式需要借助第三方信誉系统,而且效果往往也不是很理想。由于DGA域名和正常域名存在一定的差异,以及存在大量的正负样本,所以可以尝试使用机器学习来进行DGA域名的检测。
机器学习二分类算法的衡量指标
1)混淆矩阵
说到二分类问题的性能指标,我们首先会想到混淆矩阵,下面为混淆矩阵的示意图
- FP表示预测为YES实际为NO的数量
- TP表示预测为YES实际为YES的数量
- FN表示预测为NO实际为YES的数量
- TN表示预测为NO实际为NO的数量
- Y’=TP+FP 表示预测为YES的数量
- N’=FN+TN表示预测为NO的数量
- Y=TP+FN表示实际为YES的数量
- N=FP+TN表示实际为NO的数量
有了混淆矩阵我们可以很方便的计算下面的准确率,精确率,召回率等指标
2)准确率
准确率是使用最普遍和直观的指标,意义是预测准确样本的占所有样本的比例,可表示为(TP+TN)/(Y+N)
3)精确率
精确率也叫查准率,意义是所有预测正确的正类占预测为正类的比例,可表示为P=TP/Y'
4)召回率
召回率也叫查全率,意义是所有预测正确的正类占实际为正类的比例,可表示为R=TP/Y
5)特异度
特异度也称真阴性率,意义是所有预测正确的负类占实际为负的样本负类的比例,可表示为TN/N
6)误报率
误报率意义是所有预测错误的正类占实际为负类的比例,可表示为FP/N
7)漏报率
漏报率意义是所有预测错误的负类占实际为正类的比例,可表示为FN/Y
8)综合评价指标(F-Measure又称为F-Score)
精确率和召回率时常会产生矛盾的情况,需要综合考量他们,于是就有了F-Measure(又称为F-Score)。F-Measure是Precision和Recall加权调和平均F=(a*a+1)P*R/a*a*(P+R)
当参数α=1时,就是最常见的F1,也即F1=2P*R/(P+R)。可知F1综合了P和R的结果,当F1较高时则能说明试验方法比较有效。
9)ROC曲线
ROC曲线的全称叫做Receiver Operating Characteristic,常常被用来判别一个分类器的好坏程度。如下图所示:
上图引自(http://alexkong.net/2013/06/introduction-to-auc-and-roc/)
横坐标可表示为FPR=FP/N,也就是误报率
纵坐标可表示为TPR=TP/Y,也就是查全率
一些分类器得到的结果往往不是0,1这样的标签,如神经网络得到诸如0.5,0.8这样的分类结果。这时,我们人为取一个阈值比方说0.6,那么小于0.6的归为0类,大于等于0.6的归为1类,可以得到一个分类结果。同样,这个阈值我们可以取0.1或0.2等等。取不同的阈值,最后得到的分类情况也就不同。通过遍历不同的阈值,分别计算FPR和TPR,作为一个点。将这些点连接起来就组成了ROC曲线。ROC曲线越靠近左上角,分类器效果越好。
10)AUC
AUC全称Area Under ROC Curve。是一种用来度量分类模型好坏的一个标准,ROC虽然直观,但是不够量化。AUC是对ROC的量化,其值为ROC曲线所覆盖的区域面积,显然,AUC越大,分类器分类效果越好。
AUC = 1,是完美分类器,但是绝大多数预测的场合,不存在完美分类器。
0.5 < AUC < 1,优于随机猜测。这个分类器(模型)有预测价值。
AUC = 0.5,等同于随机猜测,模型没有意义。
AUC < 0.5,比随机猜测还差;但只要反着预测,就优于随机猜测。
训练数据
DGA样本数据,我们采用已知DGA生成算法生成的数据加上360 netlab开放的黑样本数据。360 netlab黑样本数据的下载链接:http://data.netlab.360.com/feeds/dga/dga.txt。
正常域名采用长度小于5的字符串组合以及alexa发布的排名前100万的网站域名,其下载地址为http://s3.amazonaws.com/alexa-static/top-1m.csv.zip
另外,在进行模型训练时,尽量保证正常域名数量和DGA域名数量在一个量级。
DGA分类识别-朴素贝叶斯
特征提取
从正常域名拼写符合一定拼写规律,DGA域名伪随机性来看,可以使用N-GRAM模型进行建模,这里我们采用2-GRAM,首先将域名当作一个字符串,去除顶级域然后进行建模。那pingan.com举例:去除顶级域后得到字符串pingan,进行2-gram处理得到字符串列表’pi’ ‘in’ ’ng’ ‘ga’ ‘an’。我们可以使用python库sklearn提供的CountVectorizer函数直接进行处理即可:
from sklearn.feature_extraction.text import CountVectorizer ngram_vectorizer = CountVectorizer(analyzer='char', ngram_range=(2, 2),dtype=np.int32)
模型工具
使用python库sklearn提供的MultinomialNB
代码片段:
from sklearn.naive_bayes import MultinomialNB
代码流程介绍:
1)将数据集划分训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(X , y, test_size=0.2)
2)将数据集进行2-GRAM处理,并进行数据拟合和标准化。
count_vec = CountVectorizer(analyzer='char', ngram_range=(2, 2),dtype=np.int32) x_train = count_vec.fit_transform(x_train) x_test = count_vec.transform(x_test)
3)生成朴素贝叶斯算法模型,并在训练集上训练,获得模型数据
mtnb = MultinomialNB() mtnb.fit(x_train,y_train)
4)使用模型在测试集上进行预测,并打印各项分类性能指标
y_pred=mtnb.predict(x_test) print(classification_report(y_test, y_pred)) //打印精确度,召回率 F1-score等指标 print metrics.confusion_matrix(y_test, y_pred) //打印混淆矩阵 t_probs = mtnb.predict_proba(x_test)[:,1] t_auc = sklearn.metrics.roc_auc_score(y_test, t_probs) print('MultinomialNB: auc = %f ' % ( t_auc)) //打印AUC
测试结果
如下图所示:精确度77%,召回率76% f1-Score 76% , AUC 87%,效果不是很理想。
DGA分类识别-XGBoost
特征提取
同样采用2-GRAM进行处理
模型工具
使用python的xgboost库,代码片段:
import xgboost as xgb
代码流程介绍
1)将数据集划分训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(X , y, test_size=0.2)
2)将数据集进行2-GRAM处理,并进行数据拟合和标准化。
count_vec = CountVectorizer(analyzer='char', ngram_range=(2, 2),dtype=np.int32) x_train = count_vec.fit_transform(x_train) x_test = count_vec.transform(x_test)
3)生成朴素贝叶斯算法模型,并在训练集上训练,获得模型数据
model = xgb.XGBClassifier() model.fit(x_train, y_train)
4)使用模型在测试集上进行预测,并打印各项分类性能指标
y_pred=model .predict(x_test) print(classification_report(y_test, y_pred)) //打印精确度,召回率 F1-score等指标 print metrics.confusion_matrix(y_test, y_pred) //打印混淆矩阵 t_probs = model.predict_proba(x_test)[:,1] t_auc = sklearn.metrics.roc_auc_score(y_test, t_probs) print( 'XGBClassifier: auc = %f ' % ( t_auc)) //打印AUC
测试结果
如下图所示:精确度84%,召回率81% f1-Score 81% , AUC 91.7264%,效果尚可。
DGA分类识别-神经网络多层感知机
特征提取
同样采用2-GRAM进行处理
模型工具
Keras是一个高层级的python神经网络框架,Keras后端可以在Tensorflow或者Theano上运行,Keras 是为支持快速实验而生,具有高度模块化,极简,和可扩充特性。我们选用keras进行神经网络多层感知机模型的测试。
代码流程介绍
1)将数据集进行2-GRAM处理,并进行数据拟合和标准化。然后将数据集划分训练集和测试集
ngram_vectorizer = CountVectorizer(analyzer='char', ngram_range=(2, 2),dtype=np.int32) count_vec = ngram_vectorizer.fit_transform(X) //数据拟合和标准化 max_features = count_vec.shape[1] //计算输入维度
//划分训练集和测试集
X_train, X_test, y_train, y_test, _, label_test = train_test_split(count_vec, y,labels, test_size=0.2) X_train, X_holdout, y_train, y_holdout = train_test_split(X_train, y_train, test_size=0.05)
2)利用keras生成神经网络多层感知机模型。这里简要介绍下其中一些概念:
激活函数activation:激活函数的主要作用是提供网络的非线性建模能力,常见的有 sigmoid函数,tanh函数,ReLU函数, ELU函数, PReLU函数,softmax函数等
损失函数loss:就是一个评分函数,用来对模型预测准确性进行打分,模型的训练就是要通过样本将损失函数最小化,常见的有:0-1损失函数 ,平方损失函数,绝对损失函数,对数损失函数等
优化器optimizer:最小化损失函数的求解方法。(常用的有最小二乘法,梯度下降法)
//创建一个Sequential模型
model = Sequential() //添加一个全连接层,激活函数使用sigmoid,输出维度128 model.add(Dense(128, input_dim=max_features, activation='sigmoid')) //添加一个Dropout层,用于防止过拟合 model.add(Dropout(0.5)) model.add(Dense(64, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(1, activation='sigmoid')) //编译模型,损失函数采用对数损失函数,优化器选用adam model.compile(loss='binary_crossentropy',optimizer='adam')
3)使用模型进行最多50轮训练,并记录训练每轮AUC的值,在相隔两轮的AUC值没有增加时退出训练
for ep in range(50): model.fit(X_train, y_train, batch_size=batch_size, nb_epoch=1) t_probs = model.predict_proba(X_holdout) t_auc = sklearn.metrics.roc_auc_score(y_holdout, t_probs)//计算AUC值 if t_auc > best_auc: best_auc = t_auc best_iter = ep //打印分类模型指标 probs = model.predict_proba(X_test) print(classification_report(y_test, probs > .5)) print('mlp: auc = %f ' %confusion_matrix(y_test, probs > .5)) else: if (ep-best_iter) > 2: Break
测试结果
如下图所示:精确度99%,召回率99% f1-Score 99% , AUC 99.9138%,效果很好。
DGA分类识别-循环神经网络单层LSTM
特征提取
简单的将域名转换为整数下标列表,然后使用keras的Embedding嵌入层转换为固定大小的向量
模型工具
我们选用keras进行循环神经网络单层LSTM的测试。
代码流程介绍
1)将数据集转换为整数下标数组的模式,并将数据划分为训练集和测试集
site_chars="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_" valid_chars = {x:idx+1 for idx, x in enumerate(set(''.join(site_chars)))} max_features = len(valid_chars) + 1 //计算特征字符长度 maxlen = np.max([len(x) for x in X]) //记录最长的域名长度 X = [[valid_chars[y] for y in x] for x in X] //转换为下标数组 X = sequence.pad_sequences(X, maxlen=maxlen)//进行长度填充 //划分训练集和测试集 X_train, X_test, y_train, y_test, _, label_test = train_test_split(X, y, labels,test_size=0.2) X_train, X_holdout, y_train, y_holdout = train_test_split(X_train, y_train, test_size=0.05)
2)利用keras生成循环神经网络单层LSTM模型:
//创建一个Sequential模型 model = Sequential() //添加一个嵌入层,输出向量大小256,嵌入层是将正整数(下标)转换为具有固定大小的向量, //其本质就是word2vec,底层实现是2-gram(词频)+神经网络 model.add(Embedding(max_features, output_dim=256, input_length=maxlen)) //添加长短期记忆网络LSTM,从样本中学习特征,这个是核心层 model.add(LSTM(128)) //添加Dropout层防止过拟合 model.add(Dropout(0.5)) //添加全连接层,激活函数使用'sigmoid' model.add(Dense(1, activation='sigmoid')) //编译模型,损失函数采用对数损失函数,优化器选用rmsprop, //该优化器通常是面对递归神经网络时的一个良好选择 model.compile(loss='binary_crossentropy',optimizer='rmsprop')
3)使用模型进行最多25轮训练,并记录训练每轮AUC的值,在相隔两轮的AUC值没有增加时退出训练
for ep in range(25): model.fit(X_train, y_train, batch_size=batch_size, nb_epoch=1) t_probs = model.predict_proba(X_holdout) t_auc = sklearn.metrics.roc_auc_score(y_holdout, t_probs)//计算AUC值 if t_auc > best_auc: best_auc = t_auc best_iter = ep //打印分类模型指标 probs = model.predict_proba(X_test) print(classification_report(y_test, probs > .5)) print('Single LSTM: auc = %f ' %confusion_matrix(y_test, probs > .5)) else: if (ep-best_iter) > 2: Break
测试结果
如下图所示:精确度99%,召回率99% f1-Score 99% , AUC 99.9233%,效果很好。
DGA分类识别-循环神经网络多层LSTM
特征提取和模型选择同单层LSTM
代码流程介绍
1)将数据集转换为整数下标数组的模式,并将数据划分为训练集和测试集
site_chars="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_" valid_chars = {x:idx+1 for idx, x in enumerate(set(''.join(site_chars)))} max_features = len(valid_chars) + 1 //计算特征字符长度 maxlen = np.max([len(x) for x in X]) //记录最长的域名长度 X = [[valid_chars[y] for y in x] for x in X] //转换为下标数组 X = sequence.pad_sequences(X, maxlen=maxlen)//进行长度填充 //划分训练集和测试集 X_train, X_test, y_train, y_test, _, label_test = train_test_split(X, y, labels,test_size=0.2) X_train, X_holdout, y_train, y_holdout = train_test_split(X_train, y_train, test_size=0.05)
2)利用keras生成循环神经网络多层LSTM模型:
//创建一个Sequential模型 model = Sequential() //添加一个嵌入层,输出向量大小256,嵌入层是将正整数(下标)转换为具有固定大小的向量, //其本质就是word2vec,底层实现是2-gram(词频)+神经网络 model.add(Embedding(max_features, output_dim=256, input_length=maxlen)) //添加长短期记忆网络LSTM,从样本中学习特征,这个是核心层 model.add(LSTM(128, return_sequences=True)) //返回维度128的向量序列 model.add(LSTM(64, return_sequences=True)) //返回维度64的向量序列 model.add(LSTM(64)) //返回维度64的单个向量 //添加全连接层,激活函数使用'sigmoid' model.add(Dense(1, activation='sigmoid')) //编译模型,损失函数采用对数损失函数,优化器选用rmsprop, //该优化器通常是面对递归神经网络时的一个良好选择 model.compile(loss='binary_crossentropy',optimizer='rmsprop')
4)使用模型进行最多25轮训练,并记录训练每轮AUC的值,在相隔两轮的AUC值没有增加时退出训练
for ep in range(25): model.fit(X_train, y_train, batch_size=batch_size, nb_epoch=1) t_probs = model.predict_proba(X_holdout) t_auc = sklearn.metrics.roc_auc_score(y_holdout, t_probs)//计算AUC值 if t_auc > best_auc: best_auc = t_auc best_iter = ep //打印分类模型指标 probs = model.predict_proba(X_test) print(classification_report(y_test, probs > .5)) print('Single LSTM: auc = %f ' %confusion_matrix(y_test, probs > .5)) else: if (ep-best_iter) > 2: Break
测试结果
如下图所示:精确度99%,召回率99% f1-Score 99% , AUC 99.9476%,效果很好。