初探機器學習檢測 PHP Webshell

簡介

最近刷完了吳恩達(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】文件夾。用於存放黑名單文件和白名單文件。

提取opcode

咱們建立一個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 作了如下幾個事:

  1. 把黑名單和白名單中的PHP opcode 統一輩子成並分別寫入到兩個不一樣的文件中。
  2. 若是這兩個文件已經存在,那就再也不次生成了
  3. 把白名單中的PHP opcode 貼上 【0】的標籤
  4. 把黑名單中的PHP opcode 貼上 【1】的標籤
  5. 最後返回全部PHP opcode 的集合數據 X(有序)
  6. 返回全部PHP opcode 的標籤 y(有序)

第三步:編寫訓練函數

終於到了咱們的重點節目了,編寫訓練函數。

在這裏先簡單的介紹一下scikit-learn中咱們須要的一些使用起來很簡單的對象和方法。

  1. CountVectorizer
  2. TfidfTransformer
  3. train_test_split
  4. GaussianNB

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的小程序就完成了。

下一步?

這個小程序只是一個簡單的應用,還有不少的地方能夠根據需求去改進

如:

在準備數據時:

  1. 生成 opcode過程當中,數據量太大沒法所有放入內存中時,更換寫入文件中的方式。

在編寫訓練方法時:

  1. 更換CountVectorizer的ngram參數,提升準確性。
  2. 增長cross validation 來增長可靠性
  3. 更換樸素貝葉斯算法爲其餘的算法,好比MLP、CNN(深度學習算法)等。

在訓練後,獲得數據與預期不符合時:

  1. 重複增量型訓練,優化訓練結果。
  2. 增大訓練數據量
  3. 若是對PHP opcode 有深刻研究的同窗能夠採用其餘的提取特徵的方法來進行訓練。
  4. 選擇多種訓練方法,看看哪種的效果最好,並且不會過分擬合(over fitting)。

 

結語

最後我們總結一下機器學習在Webshell 檢測過程當中的思路和操做。

  1. 提取特徵,準備數據
  2. 找到合適的算法,進行訓練
  3. 檢查是否符合心中預期,會不會出現過分擬合等常見的問題。
  4. 提供更多更精準的數據,或更換算法。
  5. 重複1~4

本人也是小菜雞,在此分享一下簡單的思路和方法。但願能拋磚引玉。

項目下載地址:

https://github.com/hi-WenR0/MLCheckWebshell

參考連接:

基於機器學習的 Webshell 發現技術探索

相關文章
相關標籤/搜索