最近刷完了吳恩達(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