最近刷完了吴恩达(Andrew Ng)的Machine Learning课程,恰巧实验室有相关的需求,看了几个前辈的机器学习检测PHP Webshell 的文章,便打算本身也抄起袖子,在实战中求真知。php
本文会详细的介绍实现机器学习检测PHP Webshell的思路和过程,一步一步和你们一块儿完成这个检测的工具,文章末尾会放出已经写好的下载连接。laravel
php基础知识(PHP opcode)git
php Webshellgithub
Python(scikit-learn)web
PHP:世界上最好的编程语言,这个很少说了。算法
PHP opcode:PHP opcode 是脚本编译后的中间语言,就如同Java 的Bytecode、.NET 的MSL。shell
PHP Webshell:能够简单的理解为 网页后门。编程
Python scikit-learn:小程序
(翻译:用起来美滋滋的Python 机器学习包)windows
PHP Webshell本质上也是一段PHP的代码,在没有深刻研究前,也知道PHP Webshell 必然有一些规律,好比执行了某些操做(执行获取到的命令、列出目录文件、上传文件、查看文件等等)。若是直接用PHP 的源代码分析,会出现不少的噪音,好比注释内容、花操做等等。若是咱们将PHP Webshell 的源代码转化成仅含执行语句操做的内容,就会必定程度上,过滤掉这些噪音。因此,咱们使用PHP opcode 进行分析。
针对opcode这种类型的数据内容,咱们能够采用词袋,词频等方法来进行提取关键特征。最后使用分类的算法来进行训练。
根据上面的简单“分析”,知道我们在大致思路上,是能够行得通的。
要获取到PHP opcode,须要添加一个PHP 的插件 VLD,咱们拿Windows环境来进行举例。
插件下载地址:传送门
选择对应版本进行下载
下载好后,放入到PHP 安装目录下的ext文件夹内,我使用的是PHPstudy环境,
而后编辑php.ini文件,添加一行内容
extension=php_vld.dll
测试是否安装成功:
测试文件1.php
执行命令:
php -dvld.active=1 -dvld.execute=0 1.php
若是显示内容是差很少同样的,那咱们的环境配置就成功了。
咱们须要的就是这段输出中的
ECHO 、RETURN
这样的opcode。
到这里,咱们的PHP环境配置基本完成了。
进行机器学习前,咱们很关键的一步是要准备数据,样本的数量和质量直接影响到了咱们最后的成果。
这里须要准备的数据分为两类,【白名单数据】、【黑名单数据】。
白名单数据指咱们正常的PHP程序,黑名单数据指的是PHP Webshell程序。数据源仍是咱们的老朋友 github.com
在github上搜索PHP,能够获得不少的PHP的项目,我们筛选几个比较知名和经常使用的。
白名单列表(一小部分):
- https://github.com/WordPress/WordPress - https://github.com/typecho/typecho - https://github.com/phpmyadmin/phpmyadmin - https://github.com/laravel/laravel - https://github.com/top-think/framework - https://github.com/symfony/symfony - https://github.com/bcit-ci/CodeIgniter - https://github.com/yiisoft/yii2
再继续搜索一下 Webshell 关键字,也有不少收集 Webshell 的项目。
黑名单列表(一小部分):
- https://github.com/tennc/webshell - https://github.com/ysrc/webshell-sample - https://github.com/xl7dev/WebShell
建立工程文件夹【MLCheckWebshell】,并在目录下建立【black-list】【white-list】文件夹。用于存放黑名单文件和白名单文件。
咱们建立一个utils.py 文件,用来编写提取opcode的工具函数。
工具函数1:
方法load_php_opcode 解读:
用Python 的subprocess 模块来进行执行系统操做,获取其全部输出,并用正则提取opcode,再用空格来链接起来
工具函数2;
工具方法2 recursion_load_php_file_opcode 的做用是遍历目标文件夹内的全部的PHP文件并生成opcode,最后生成一个列表,并返回。
而后咱们在工程目录下,建立train.py文件。
编写prepare_data() 函数
prepare_data 作了如下几个事:
终于到了咱们的重点节目了,编写训练函数。
在这里先简单的介绍一下scikit-learn中咱们须要的一些使用起来很简单的对象和方法。
CountVectorizer 的做用是把一些列文档的集合转化成数值矩阵。
TfidfTransformer 的做用是把数值矩阵规范化为 tf 或 tf-idf 。
train_test_split的做用是“随机”分配训练集和测试集。这里的随机不是每次都随机,在参数肯定的时候,每次随机的结果都是相同的。有时,为了增长训练结果的有效性,咱们会用到交叉验证(cross validations)。
GaussianNB :Scikit-learn 对朴素贝叶斯算法的实现。朴素贝叶斯算法是经常使用的监督型算法。
先上写好的代码:
代码介绍:
首先,咱们用了刚才写的prepare_data()函数来获取咱们的数据集。而后,建立了一个CountVectorizer 对象,初始化的过程当中,咱们告诉CountVectorizer对象,ngram的上下限为(3,3) 【ngram_range=(3,3)】,当出现解码错误的时候,直接忽略【decode_error=”ignore”】,匹配token的方式是【r”\b\w+\b”】,这样匹配咱们以前用空格来隔离每一个opcode 的值。
而后咱们用 cv.fit_transform(X).toarray() 来“格式化”咱们的结果,最终是一个矩阵。
接着建立一个TfidfTransformer对象,用一样的方式处理一次咱们刚才获得的总数据值。
而后使用train_test_split函数来获取打乱的随机的测试集和训练集。这时候,黑名单中的文件和白名单中的文件排列顺序就被随机打乱了,可是X[i] 和 y[i] 的对应关系没有改变,训练集和测试集在总数汇集中分别占比60%和40%。
接下来,建立一个GaussianNB 对象,在Scikit-learn中,已经内置好的算法对象能够直接进行训练,输入内容为训练集的数据(X_train) 和 训练集的标签(y_train)。
gnb.fit(X_train, y_train)
执行完上面这个语句之后,咱们就会获得一个已经训练完成的gnb训练对象,咱们用测试集(X_test) 去预测获得咱们的y_pred 值(预测出来的类型)。
而后咱们对比本来的 y_test 和 用训练算法获得的结果 y_pred。
metrics.accuracy_score(y_test, y_pred)
结果即为在此训练集和测试集下的准确率。
约为97.42%
还须要计算混淆矩阵来评估分类的准确性。
metrics.confusion_matrix(y_test, y_pred)
输出结果见上图。
编写训练函数到这里已经初具雏形。并能够拿来简单的使用了。
编写完训练函数,如今咱们能够拿新的Webshell来挑战一下咱们刚才已经训练好的gnb。
可是,若是每次检测以前,都要从新训练一次,那速度就很是的慢了,咱们须要持久化咱们的训练结果。
在Scikit-learn 中,咱们用joblib.dump() 方法来持久化咱们的训练结果,细心的读者应该发现,在method1() 中有个被注释掉的语句
joblib.dump(gnb, 'save/gnb.pkl')
这个操做就是把咱们训练好的gnb保存到save文件夹内的gnb.pkl文件中。
方面下次使用。
建立check.py
理一下思路:先实例化咱们以前保存的内容,而后将新的检测内容放到gnb中进行检测,判断类型并输出。
核心代码:
最后根据标签来判断结果,0 为 正常程序, 1 为 Webshell。
咱们来进行一个简单的测试。
那么,一个简单的经过朴素贝叶斯训练算法判断Webshell的小程序就完成了。
这个小程序只是一个简单的应用,还有不少的地方能够根据需求去改进
如:
在准备数据时:
在编写训练方法时:
在训练后,获得数据与预期不符合时:
最后我们总结一下机器学习在Webshell 检测过程当中的思路和操做。
本人也是小菜鸡,在此分享一下简单的思路和方法。但愿能抛砖引玉。
https://github.com/hi-WenR0/MLCheckWebshell