[网络安全篇] 图像思维解决恶意软件问题
以图像分类的手段去解决目前的主流安卓恶意软件问题。全流程代码,无脑运行跑通
[网络安全篇] 图像思维初步解决恶意软件问题
近年来,恶意软件的出现尤为猖獗。所以解决恶意软件的问题也成为了刻不容缓的事情,当然,现在运用机器学习去完成恶意软件分类的手段已经比较的成熟了,但是,机器学习去特征的处理要求是比较高的,这里我采用深度学习的思维去解决。与此同时,Canadian Institute for Cybersecurity这个研究所提供了近几年的安卓恶意软件的提取特征文件,我在此基础上进行改进,初步完成一个较为入门级别的恶意软件分类项目。当然,因为是第一版的项目,准确率不会太高。(如果可能,后续会不断的迭代更新)
0 背景
本项目将会以处理图像的手段完成恶意软件的分类问题,当然这个解决方式并不新颖,算是一个对已有方法的简单复现。
1 数据集
详情参照CIC研究所的具体发布内容。
这里我采用了其中470维度的一个数据进行处理,数据集已经挂载在项目数据集当中。
# 对数据进行一个copy
!cp data/data157200/syscallsbinders_frequency_5_Cat.csv work/
2 数据处理
2.1 数据展示
因为是csv文件,我这里采用pandas的方式进行读取,更为方便快速。
可以通过表头看到,是各种权限、调用函数、绑定器的使用频次统计。
每一行的最后一列是该行数据所属于的分类类别。
import pandas as pd
# 按文件名读取整个文件
data_471 = pd.read_csv("work/syscallsbinders_frequency_5_Cat.csv")
data_471.head(2) # 展示前几行数据,通常为展示前5行
ACCESS_PERSONAL_INFO___ | ALTER_PHONE_STATE___ | ANTI_DEBUG_____ | CREATE_FOLDER_____ | CREATE_PROCESS`_____ | CREATE_THREAD_____ | DEVICE_ACCESS_____ | EXECUTE_____ | FS_ACCESS____ | FS_ACCESS()____ | ... | utimes | vfork | vibrate | vibratePattern | wait4 | watchRotation | windowGainedFocus | write | writev | Class | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 3 | 0 | 14 | 2 | 0 | 3 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 37 | 10 | 1 |
1 | 3 | 0 | 0 | 6 | 0 | 42 | 91 | 0 | 32 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 2838 | 46 | 1 |
2 rows × 471 columns
2.2 无效数据过滤
我这里的思维就是,竖向的每一列,如果求和他的值为0,那么他的这一列的数值存在将是毫无意义的。
换句话来讲,其实这里也是一种特征降维。
因为我渴望通过图像的方式去解决问题,那么我就可以将每一行的数据保留到 N x N NxN NxN的一个维度。
那么,这里是不是可以放大这个0值,每一列小于某个阈值的话,我们都可以视为这一列起不到作用。
这里我采用63这个阈值,这样刚好可以把数据降到290维,最后一位为分类类别,相当于就是
17 * 17 = 289 + 1 = 290
import pandas as pd
# 按文件名读取整个文件
data_471 = pd.read_csv("work/syscallsbinders_frequency_5_Cat.csv")
df = pd.DataFrame(data_471)
len(df.columns)
# 记录每一列求和小于阈值的列
threshold = 63
de = []
de_max = 0
de_va = []
for i in range(0,470):
name = df.columns.values[i]
if(df[name].sum() <= threshold):
de.append(name)
if(df[name].max() >= de_max):
de_max = df[name].max()
# 删除小于阈值的列
for j in de:
df.drop(j,axis=1,inplace=True)
len_new = len(df.columns)
len_new
290
PS: 不合理点
这里有些频次的调用很大,这里统计了最大的数值de_max,为3697410.
那么,我们将其转换为图像时,范围是0-255才是合理的。
所以需要使用归一化操作,但是这里,这个值过于庞大,肯定是会将其他数值缩放之后几乎为0的状态。
我这里采取,大值统一设置为10000,当然大家可以尝试其他值看看结果的差异有多大。
de_max
3697410
# 新数据写入csv
df.to_csv("new_date"+str(len_new)+".csv",index=False,sep=',')
import numpy as np
import pandas as pd
mydata = pd.read_csv("new_date290.csv")
mydata_array = np.array(mydata)
mydata_array
array([[ 1, 0, 3, ..., 37, 10, 1],
[ 3, 0, 6, ..., 2838, 46, 1],
[ 2, 0, 4, ..., 111, 20, 1],
...,
[ 0, 0, 0, ..., 241, 67, 5],
[ 1, 0, 15, ..., 1703, 774, 5],
[ 0, 0, 10, ..., 3102, 186, 5]])
mydata_array.shape
(11598, 290)
# 获取最后一维的数据,也就是分类类别 1-5
mydata_array[0][-1]
1
# 删除最后一列
del_arr = np.delete(mydata_array, -1, axis=1)
del_arr
array([[ 1, 0, 3, ..., 0, 37, 10],
[ 3, 0, 6, ..., 2, 2838, 46],
[ 2, 0, 4, ..., 1, 111, 20],
...,
[ 0, 0, 0, ..., 5, 241, 67],
[ 1, 0, 15, ..., 3, 1703, 774],
[ 0, 0, 10, ..., 13, 3102, 186]])
# 将其转换为11598条17*17的数据
del_arr = del_arr.reshape(-1,17,17)
del_arr.shape
(11598, 17, 17)
# 单纯为了看一下数据样式,可以从左侧的1.txt查看
np.savetxt('1.txt',del_arr[0])
import os
def init_mkdir():
for i in range(1,6):
data_path = os.path.join('mal_data',str(i)) # 文件夹路径'data\1'
if not os.path.exists(data_path): # 判断文件夹是否存在
os.makedirs(data_path) # 不存在则新建文件夹
2.3 生成灰度图
这里使用了阈值去讲大值设置为阈值的操作。
同时进行了数据的归一化
import cv2
import numpy as np
import matplotlib.pylab as pylab
from sklearn.utils import shuffle
# 忽略(垃圾)警告信息
import warnings
warnings.filterwarnings("ignore")
# 在 notebook 画图展示
%matplotlib inline
# 大于10000的数据会被变成10000
img_yuzhi = 10000
# 生成灰度图图像
def mk_img():
# 初始化文件夹
init_mkdir()
print("灰度图生成ing...")
with_label_txt = []
for i in range(11598):
# 使用归一化
x= np.clip(del_arr[i],0,img_yuzhi)
x = (x-np.min(x))/(np.max(x)-np.min(x)) # 缩放到0-1
img = x * 255 # 放大到0-255
label = mydata_array[i][-1]
img_path = "mal_data/" + str(label) + "/" +str(i+1) + ".jpg"
# 生成带有标签的txt 例: data/1/1.jpg 1
with_label_txt.append(img_path+" " +str(label-1) + "\n")
cv2.imwrite(img_path,img)
shuf_with_label_txt = shuffle(with_label_txt)
all_str = ''.join(shuf_with_label_txt)
f = open('shuf_with_label_txt.txt','w',encoding='utf-8')
f.write(all_str)
print("标签文件TXT已生成,灰度图生成完成!")
return shuf_with_label_txt,len(shuf_with_label_txt)
shuf_txt, length = mk_img()
print(length)
灰度图生成ing...
标签文件TXT已生成,灰度图生成完成!
11598
到这里位置,其实大家就可以采用任何的图像分类的方式去实现软件的分类了。
灰度图可视化:
# 按照比例划分数据集 总数据有11598张图片,我这里采用8:2来划分数据
train_size = int(length * 0.8)
train_list = shuf_txt[:train_size]
val_list = shuf_txt[train_size:]
print(len(train_list))
print(len(val_list))
9278
2320
# 运行cell,生成训练集txt
train_txt = ''.join(train_list)
f_train = open('train_list.txt','w',encoding='utf-8')
f_train.write(train_txt)
f_train.close()
print("train_list.txt 生成成功!")
# 运行cell,生成验证集txt
val_txt = ''.join(val_list)
f_val = open('val_list.txt','w',encoding='utf-8')
f_val.write(val_txt)
f_val.close()
print("val_list.txt 生成成功!")
train_list.txt 生成成功!
val_list.txt 生成成功!
# ['maldata/4/7659.jpg 4\n',...]
train_img = []
train_label = []
for img_label in train_list:
img,label = img_label.split("\n")[0].split(" ")
train_img.append(img)
train_label.append(label)
val_img = []
val_label = []
for img_label in val_list:
img,label = img_label.split("\n")[0].split(" ")
val_img.append(img)
val_label.append(label)
print("-----------------")
print(train_list[0])
print(train_img[0])
print(train_label[0])
print("-----------------")
print(val_list[0])
print(val_img[0])
print(val_label[0])
-----------------
mal_data/5/10213.jpg 4
mal_data/5/10213.jpg
4
-----------------
mal_data/3/3684.jpg 2
mal_data/3/3684.jpg
2
3 dataset类编写
from paddle.io import Dataset
from PIL import Image
class MyDataset(Dataset):
def __init__(self, mode = 'train'):
# 训练样本数量
self.training_data, self.training_label, self.test_data, self.test_label = train_img, train_label, val_img, val_label
if mode == 'train':
self.num_samples = len(train_img)
else:
self.num_samples = len(val_img)
self.mode = mode
def __getitem__(self, idx):
if self.mode == 'train':
image = self.training_data[idx]
label = self.training_label[idx]
else:
image = self.test_data[idx]
label = self.test_label[idx]
im = Image.open(image).convert('L')
im = im.resize((17, 17), Image.ANTIALIAS)
img = np.array(im).astype('float32')
return img, np.array(label, dtype='int64')
def __len__(self):
# 返回样本总数量
return self.num_samples
# 训练的数据提供器
train_dataset = MyDataset(mode='train')
# 测试的数据提供器
eval_dataset = MyDataset(mode='val')
# 查看训练和测试数据的大小
print('train大小:', train_dataset.__len__())
print('eval大小:', eval_dataset.__len__())
# 查看图片数据、大小及标签
for data, label in train_dataset:
print(data)
print(np.array(data).shape)
print(label)
break
train大小: 9278
eval大小: 2320
[[ 0. 2. 1. 1. 14. 4. 0. 4. 3. 4. 2. 0. 0. 6.
0. 13. 5.]
[ 3. 0. 4. 0. 0. 4. 2. 4. 1. 0. 0. 4. 4. 1.
15. 8. 3.]
[ 19. 1. 0. 0. 0. 0. 47. 0. 0. 4. 4. 0. 0. 0.
2. 2. 125.]
[ 12. 31. 1. 5. 0. 1. 0. 0. 2. 1. 0. 0. 5. 3.
3. 3. 0.]
[ 4. 6. 1. 0. 0. 4. 0. 30. 3. 0. 3. 255. 1. 1.
0. 2. 0.]
[ 1. 0. 0. 2. 1. 2. 0. 3. 0. 2. 0. 0. 0. 0.
1. 0. 0.]
[ 0. 0. 3. 0. 0. 4. 0. 0. 0. 0. 1. 0. 5. 0.
2. 0. 0.]
[ 2. 1. 0. 0. 3. 0. 0. 1. 0. 1. 0. 5. 0. 0.
0. 2. 0.]
[ 0. 1. 0. 0. 0. 2. 1. 0. 0. 2. 1. 0. 0. 1.
1. 0. 0.]
[ 3. 0. 2. 4. 0. 0. 0. 0. 0. 20. 0. 26. 3. 2.
6. 30. 22.]
[ 25. 0. 2. 0. 0. 0. 1. 0. 74. 0. 2. 0. 0. 0.
0. 0. 0.]
[ 0. 4. 0. 5. 5. 74. 3. 0. 0. 69. 230. 0. 0. 37.
5. 1. 0.]
[ 0. 3. 28. 4. 1. 3. 0. 2. 84. 14. 1. 3. 1. 0.
1. 44. 1.]
[ 0. 5. 0. 0. 1. 0. 3. 0. 5. 0. 2. 0. 0. 4.
0. 0. 0.]
[ 3. 0. 1. 0. 0. 4. 0. 0. 0. 0. 0. 1. 2. 0.
2. 0. 1.]
[ 0. 3. 0. 1. 15. 0. 1. 1. 7. 0. 2. 0. 19. 31.
0. 6. 0.]
[ 0. 26. 0. 0. 0. 0. 1. 1. 2. 0. 1. 0. 0. 0.
0. 34. 63.]]
(17, 17)
4
4 网络搭建
4.1 模仿LENet搭建网络
这里就简答的搭建一个类似与LENet的网络。
import paddle
import paddle.nn.functional as F
class LeNet(paddle.nn.Layer):
def __init__(self):
super(LeNet, self).__init__()
self.conv1 = paddle.nn.Conv2D(in_channels=1, out_channels=6, kernel_size=5, stride=1, padding=1)
self.max_pool1 = paddle.nn.MaxPool2D(kernel_size=2, stride=2)
self.conv2 = paddle.nn.Conv2D(in_channels=6, out_channels=32, kernel_size=3, stride=1)
self.max_pool2 = paddle.nn.MaxPool2D(kernel_size=2, stride=2)
self.linear1 = paddle.nn.Linear(in_features=32*2*2, out_features=64)
self.linear2 = paddle.nn.Linear(in_features=64, out_features=17)
self.linear3 = paddle.nn.Linear(in_features=17, out_features=5)
def forward(self, x):
# 将输入数据的样子该变成[1,3,100,100]
x = paddle.reshape(x,shape=[-1,1,17,17]) # 转换维读
x = self.conv1(x)
x = F.relu(x)
x = self.max_pool1(x)
x = self.conv2(x)
x = F.relu(x)
x = self.max_pool2(x)
x = paddle.flatten(x, start_axis=1,stop_axis=-1)
x = self.linear1(x)
x = F.relu(x)
x = self.linear2(x)
x = F.relu(x)
x = self.linear3(x)
return x
model = LeNet() # 模型实例化
paddle.summary(model, (1,1,17,17)) # 模型结构查看
---------------------------------------------------------------------------
Layer (type) Input Shape Output Shape Param #
===========================================================================
Conv2D-23 [[1, 1, 17, 17]] [1, 6, 15, 15] 156
MaxPool2D-23 [[1, 6, 15, 15]] [1, 6, 7, 7] 0
Conv2D-24 [[1, 6, 7, 7]] [1, 32, 5, 5] 1,760
MaxPool2D-24 [[1, 32, 5, 5]] [1, 32, 2, 2] 0
Linear-34 [[1, 128]] [1, 64] 8,256
Linear-35 [[1, 64]] [1, 17] 1,105
Linear-36 [[1, 17]] [1, 5] 90
===========================================================================
Total params: 11,367
Trainable params: 11,367
Non-trainable params: 0
---------------------------------------------------------------------------
Input size (MB): 0.00
Forward/backward pass size (MB): 0.02
Params size (MB): 0.04
Estimated Total Size (MB): 0.06
---------------------------------------------------------------------------
{'total_params': 11367, 'trainable_params': 11367}
4.2 模型训练
EPOCH_NUM = 100
model = paddle.Model(model) # 模型封装
scheduler = paddle.optimizer.lr.CosineAnnealingDecay(learning_rate=0.00125, T_max=int(EPOCH_NUM//2), verbose=True)
# 配置优化器、损失函数、评估指标
model.prepare(paddle.optimizer.Adam(learning_rate=scheduler, parameters=model.parameters()),
paddle.nn.CrossEntropyLoss(),
paddle.metric.Accuracy())
# 训练可视化VisualDL工具的回调函数
visualdl = paddle.callbacks.VisualDL(log_dir='visualdl_log')
# 启动模型全流程训练
model.fit(train_dataset, # 训练数据集
eval_dataset, # 评估数据集
epochs=EPOCH_NUM, # 训练的总轮次
batch_size=32, # 训练使用的批大小
verbose=1, # 日志展示形式
callbacks=[visualdl]) # 设置可视化
4.3 训练日志可视化
经典版环境不支持看可视化,这里在本地进行可视化的查看。
在本地打开anaconda的终端环境:
(paddle-cpu) C:\Users\Administrator>pip install visualdl
(paddle-cpu) C:\Users\Administrator>d:
执行可视化命令会出现:
(paddle-cpu) D:\>visualdl --logdir d:Desktop --port 8080
VisualDL 2.2.3
Running VisualDL at http://localhost:8080/ (Press CTRL+C to quit)
Serving VisualDL on localhost; to expose to the network, use a proxy or pass --host 0.0.0.0
在本地打开上面的网址即可:
5 模型保存及简单验证
5.1 模型保存
model.save('output-model/model') # 保存模型
5.2 模型验证
其实上面的可视化图已经是对结果的一个很好的验证了,这里我们通过实际的数据的展示,来看一下效果。
label_txt = ['Adware','Banking','SMS malware','Riskware','Benign']
model = LeNet()
para_dict = paddle.load('output-model/model.pdparams')
model.load_dict(para_dict)
model.eval()
# 找一张验证集的数据进行展示 mal_data/3/3684.jpg 2
image_path = 'mal_data/3/3684.jpg'
im = Image.open(image_path).convert('L')
im = im.resize((17, 17), Image.ANTIALIAS)
im = np.array(im).reshape(1, 1, 17, 17).astype(np.float32)
x = paddle.to_tensor(im)
predicts = model(x)
p = predicts.numpy().argmax() # 最大索引
print("实际标签为:",label_txt[int(image_path.split('/')[1])-1])
print("预测标签为:",label_txt[p])
S)
im = np.array(im).reshape(1, 1, 17, 17).astype(np.float32)
x = paddle.to_tensor(im)
predicts = model(x)
p = predicts.numpy().argmax() # 最大索引
print("实际标签为:",label_txt[int(image_path.split('/')[1])-1])
print("预测标签为:",label_txt[p])
print(F.softmax(predicts)) # 0 对应的就是标签1的 预测正确.
实际标签为: SMS malware
预测标签为: SMS malware
Tensor(shape=[1, 5], dtype=float32, place=Place(gpu:0), stop_gradient=False,
[[0.00118610, 0.00161026, 0.99717569, 0.00001615, 0.00001183]])
项目总结
本项目用图像的思想去解决恶意软件的分类问题,且达到了一个还不错的效果。
当然,后续还是要大量优化的,因为分类问题需求的精度还是很高的。
个人总结
全网同名:
iterhui
我在AI Studio上获得至尊等级,点亮10个徽章,来互关呀~
https://aistudio.baidu.com/aistudio/personalcenter/thirdview/643467
文章仅为搬运,原作地址:https://aistudio.baidu.com/aistudio/projectdetail/4325980
更多推荐
所有评论(0)