DNS入侵检测 datacon2020
1 题目解读与思路
1.1题目要求:域名分类
对给出域名的流量数据进行分析
按照请求特征对域名进行二分类
1.2数据描述
题目给出了四个文件分别为fqdn.csv、flint.csv、access.csv和whois.js四个不同的文件,不同文件存放的内容不用:fqdn存放了域名字符串本身的信息;flint存放的是DNS服务器接收消息的信息;access和whois分别存放了域名访问和注册的相关信息。
1.3解题思路
根据所学知识,先从不同文件中初步提取出一些特征,然后利用类内距离和类间距离对提取的特征进行评价,将评价效果较差的特征排除,得到剩下的最终的特征。
2 特征选择
2.1数据合并
```python
def csv_merge():
# 合并
df_access['date'] = pd.to_datetime(df_access['date'], format='%Y%m%d')
df_access['year'] = df_access['date'].dt.year
df_access['month'] = df_access['date'].dt.month
df_access['day'] = df_access['date'].dt.day
data = pd.merge(df_fqdn, df_access, on='fqdn_no', how='left')
data = pd.merge(df_label, data, on='fqdn_no', how='left')
data.sort_values(by=['fqdn_no'], inplace=True)
print('len :', len(data['fqdn_no']))
# 删去空行
# data.dropna(how='any', inplace=True)
data['encoded_fqdn_last'] = data['encoded_fqdn'].apply(lambda x: x.split('.')[-1])
data['encoded_fqdn_first'] = data['encoded_fqdn'].apply(lambda x: x.split('.')[0])
#拆分成顶级域名(suffix后缀)和前缀两部分
data.reset_index(inplace=True)
data = data.drop(['index'], axis=1)
data.to_csv('./5_question_train/file_out.csv')
2.2特征初步提取
根据所学知识,不用文件中提取了不同的特征信息,分别是:
"domain len":域名长度,即去除特殊字符('['和']')后的域名字符串长度。
"word proportion":单词占比,指特殊字符('['和']')占整个域名长度的比例。
"number proportion":数字占比,指数字字符('0')占整个域名长度的比例。
"char proportion":字母占比,指字母字符('a')占整个域名长度的比例。
"number of special char":特殊字符数量,即域名中的特殊字符('['、']'、'0')的总数。
"max length of domain":域名最大长度,指域名中的子域名个数(以'.'为分隔符)。
"is general suffix":是否为常规后缀,如果域名的最后一个子域名是常见的后缀(如'com'、'cn'、'net'等),则为1,否则为0。
当使用 groupby 函数时,它将数据按照指定的列进行分组,并对每个分组进行聚合操作。在这种情况下,我们使用 groupby 函数将数据按照 ‘fqdn_no’ 列进行分组,然后对 ‘hour’ 列进行统计。
def feature_num():
# hour
hour_max = df.groupby(['fqdn_no'])['hour'].max()
hour_min = df.groupby(['fqdn_no'])['hour'].min()
hour_mean = df.groupby(['fqdn_no'])['hour'].mean()
hour_var = df.groupby(['fqdn_no'])['hour'].var()
hour_median = df.groupby(['fqdn_no'])['hour'].median()
hour_std = df.groupby(['fqdn_no'])['hour'].std()
同样的方法适用于df.groupby([‘fqdn_no’])[‘hour’].min()
通过使用 pd.DataFrame() 构造函数,将每个统计量转换为单列的 DataFrame,并指定列名。例如,对于 hour_max 这个统计量,使用 pd.DataFrame({‘hour_max’: hour_max}) 将其转换为一个只包含一列 ‘hour_max’ 的 DataFrame,其中索引与原始数据中的 ‘fqdn_no’ 相对应。
对域名特征工程
def feature_encoded_fqdn():
domain = df_fqdn['encoded_fqdn']
dns_Len = np.array(range(0, len(domain)))
alpha_Cnt = np.array(range(0, len(domain)))
digit_Cnt = np.array(range(0, len(domain)))
word_Cnt = np.array(range(0, len(domain)))
word_Len = np.array(range(0, len(domain)))
word_Rate = np.zeros(len(domain))
dot_Cnt = np.array(range(0, len(domain)))
sp_Cnt = np.array(range(0, len(domain)))
for i in range(len(domain)):
# 初始化
dns_len, alpha_cnt, digit_cnt, word_cnt, word_len, dot_cnt, sp_cnt = 0, 0, 0, 0, 0, 0, 0
flag = False
dns_len = len(domain[i])
# 读取具体行列值,遍历分析
for j in range(dns_len):
if domain[i][j] == '[':
word_cnt += 1
flag = True
elif domain[i][j] == ']':
flag = False
elif domain[i][j] == 'a':
alpha_cnt += 1
if flag:
word_len += 1
elif domain[i][j] == '0':
digit_cnt += 1
elif domain[i][j] == '.':
dot_cnt += 1
elif not domain[i][j].isdigit() and not domain[i][j].isalpha():
sp_cnt += 1
# 写入数组
dns_Len[i] = dns_len
alpha_Cnt[i] = alpha_cnt
digit_Cnt[i] = digit_cnt
word_Cnt[i] = word_cnt
word_Len[i] = word_len
word_Rate[i] = word_len / dns_len
dot_Cnt[i] = dot_cnt
sp_Cnt[i] = sp_cnt
dns_Len = pd.DataFrame(dns_Len, columns=['dns_len'])
alpha_Cnt = pd.DataFrame(alpha_Cnt, columns=['alpha_cnt'])
digit_Cnt = pd.DataFrame(digit_Cnt, columns=['digit_cnt'])
word_Cnt = pd.DataFrame(word_Cnt, columns=['word_cnt'])
word_Len = pd.DataFrame(word_Len, columns=['word_len'])
word_Rate = pd.DataFrame(word_Rate, columns=['word_rate'])
dot_Cnt = pd.DataFrame(dot_Cnt, columns=['dot_cnt'])
sp_Cnt = pd.DataFrame(sp_Cnt, columns=['sp_cnt'])
fqdn_no = df_fqdn['fqdn_no']
regroup = [fqdn_no, dns_Len, alpha_Cnt, digit_Cnt, word_Cnt, word_Len, word_Rate, dot_Cnt, sp_Cnt]
result = pd.concat(regroup, axis=1)
result.to_csv('5_question_train/feature_encoded_fqdn.csv')