1. 程式人生 > >《web安全之機器學習入門》第7章樸素貝葉斯模型檢測webshell

《web安全之機器學習入門》第7章樸素貝葉斯模型檢測webshell

N-gram演算法,認為第N個詞只與前面的第N-1個詞相關。
例如對於一個句子,I love my country.
那麼2-gram得到的詞集為:
["I love","love my","my country"]

程式碼如下:


檢測webshell的第一種方式的思路為,將php webshell檔案按照單詞分詞後(正則\b\w+\b),按照2-gram演算法得到詞集,從而得到檔案每一行在該詞集上的分佈情況,得到特徵向量;然後將正常的php檔案也按照如上方法在如上詞集上得到特徵向量。

webshell檔案的行標記為1,正常php檔案的行標記為0,特徵向量和標記在一起組成資料集。再在樸素貝葉斯模型上使用交叉驗證的方式得的準確度。

程式碼如下:

#coding:utf-8
import os
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.model_selection import cross_val_score
from sklearn.naive_bayes import GaussianNB

DATAPATH = os.path.dirname(os.path.abspath(__file__)) + "/data"
BLACKPATH = DATAPATH + "/black"
WHITEPATH = DATAPATH + "/white"

def parse_files(path,rtn):
    files = os.listdir(path)
    for item in files:
        if item in ['.', '..']:
            continue
        cpath = path + "/" + item
        if os.path.isdir(cpath):
            parse_files(cpath, rtn)
        else:
            if cpath.endswith(".php"):
                with open(cpath, "r") as f:
                    for line in f.readlines():
                        line = line.strip()
                        rtn.append(line)

if __name__ == '__main__':
    black_lines = list()
    parse_files(BLACKPATH, black_lines)
    white_lines = list()
    parse_files(WHITEPATH, white_lines)

    webshell_vectorizer = CountVectorizer(ngram_range=(2,2), decode_error="ignore", token_pattern=r'\b\w+\b')
    x1 = webshell_vectorizer.fit_transform(black_lines).toarray()
    vocabulary = webshell_vectorizer.vocabulary_
    y1 = len(x1)*[1]

    white_vectorizer = CountVectorizer(ngram_range=(2,2), decode_error="ignore", token_pattern=r'\b\w+\b', vocabulary=vocabulary)
    x2 = white_vectorizer.fit_transform(white_lines).toarray()
    y2 = len(x2)*[0]

    x = np.concatenate((x1, x2))
    y = np.concatenate((y1, y2))

    model = GaussianNB()
    score = cross_val_score(model, x, y, cv=10)
    print score
    print "precision:", np.mean(score)*100

程式碼執行的效果如下:


檢測webshell的第二種方式的思路為,將php webshell檔案按照函式呼叫或者字串常量分詞後(正則\b\w+\b(|\'\w+\'),按照1-gram演算法得到詞集(1-gram應該就是指的單個詞了),從而得到檔案每一行在該詞集上的分佈情況,得到特徵向量;然後將正常的php檔案也按照如上方法在如上詞集上得到特徵向量。

webshell檔案的行標記為1,正常php檔案的行標記為0,特徵向量和標記在一起組成資料集。再在樸素貝葉斯模型上使用交叉驗證的方式得的準確度。

程式碼如下:

#coding:utf-8
import os
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.model_selection import cross_val_score
from sklearn.naive_bayes import GaussianNB

DATAPATH = os.path.dirname(os.path.abspath(__file__)) + "/data"
BLACKPATH = DATAPATH + "/black"
WHITEPATH = DATAPATH + "/white"

def parse_files(path,rtn):
    files = os.listdir(path)
    for item in files:
        if item in ['.', '..']:
            continue
        cpath = path + "/" + item
        if os.path.isdir(cpath):
            parse_files(cpath, rtn)
        else:
            if cpath.endswith(".php"):
                with open(cpath, "r") as f:
                    for line in f.readlines():
                        line = line.strip()
                        rtn.append(line)

if __name__ == '__main__':
    black_lines = list()
    parse_files(BLACKPATH, black_lines)
    white_lines = list()
    parse_files(WHITEPATH, white_lines)

    webshell_vectorizer = CountVectorizer(ngram_range=(1,1), decode_error="ignore", token_pattern=r'\b\w+\b\(|\'\w+\'')
    x1 = webshell_vectorizer.fit_transform(black_lines).toarray()
    vocabulary = webshell_vectorizer.vocabulary_
    y1 = len(x1)*[1]

    white_vectorizer = CountVectorizer(ngram_range=(1,1), decode_error="ignore", token_pattern=r'\b\w+\b\(|\'\w+\'', vocabulary=vocabulary)
    x2 = white_vectorizer.fit_transform(white_lines).toarray()
    y2 = len(x2)*[0]

    x = np.concatenate((x1, x2))
    y = np.concatenate((y1, y2))

    model = GaussianNB()
    score = cross_val_score(model, x, y, cv=10)
    print score
    print "precision:", np.mean(score)*100

程式碼執行的效果如下:


文章還提到了用樸素貝葉斯演算法識別DGA域名(將域名使用2-gram分詞後,得到每個域名在詞集上的分佈向量,然後使用樸素貝葉斯演算法),這個思路還是用得比較多的。另外提到的就是用樸素貝葉斯識別驗證碼(將驗證碼的灰度圖的每一個畫素點的值作為特徵向量的一維,再使用樸素貝葉斯演算法),不過我之前已經用卷積神經網路識別過驗證碼(Caffe的LeNet-5模型上修改),且準確率高達99.996%,一個驗證碼識別比賽的結果,所以就不再展開。