【前言】 介紹Opcode的提取方法,并探討多種機器學習算法在Webshell檢測中的應用,理解如何在實際項目中應用Opcode進行高效的Webshell檢測。
Ⅰ 基本概念
Opcode:計算機指令的一部分,也叫字節碼,一個php文件可以抽取出一個指令序列,如ADD、ECHO、RETURN。
【原因】由于直接對php文件使用詞袋和TF-IDF進行模型訓練會消耗大量計算資源,使用opcode模型進行降維可以有效提升模型效率和模型的準確率。
【作用】避免Webshell中為了繞開靜態檢測惡意添加的無用注釋的干擾
Ⅱ Opcode提取
【背景】php版本72,路徑/www/server/php/72/bin
安裝地址:https://pecl.php.net/package/vld 官網下載對應的vld版本
安裝腳本
cd vld-0.15.0
# 使用phpize生成配置腳本,并指定php-config路徑(與你的 PHP 路徑相關 )
/www/server/php/72/bin/phpize
./configure --with-php-config=/www/server/php/72/bin/php-config --enable-vld
# 編譯
make && make install
#可看到一個so文件
/opt/vld-0.15.0/vld-0.15.0/modules/vld.so# php.ini文件添加
extension=vld.so# 重啟php服務
systemctl restart php7.2
測試 111.php
<?phpecho "Hello World";
?>
查看php文件的Opcode
php -dvld.active=1 -dvld.execute=0 111.php
效果如下,op值就是了,直接提取下來
Ⅲ 訓練過程
提取opcode很簡單,就是采用什么模型訓練,才能盡可能提高召回率和準確率
第一種:樸素貝葉斯
特征提取使用詞袋&TF-IDF模型
- 將 WebShell 樣本以及常見 PHP 開源軟件的文件提取詞袋。
- 使用 TF-IDF 處理。
- 隨機劃分為訓練集和測試集。
- 使用樸素貝葉斯算法在訓練集上訓練,獲得模型數據。
- 使用模型數據在測試集上進行預測。
- 驗證樸素貝葉斯算法預測效果。
第二種:使用MLP算法
特征提取使用特征提取使用opcode&n-gram
完整的處理流程為
- 將 WebShell 樣本以及常見 PHP 開源軟件的文件提取 opcode.
- 使用 n-gram 處理。
- 隨機劃分為訓練集和測試集。
- 使用 MLP 算法在訓練集上訓練,獲得模型數據。
- 使用模型數據在測試集上進行預測。
- 驗證 MLP 算法預測效果。
第三種:CNN模型訓練
還未實現
Ⅳ 實戰環節
def extract_opcodes(filepath: str, php_executable: str = 'php') -> Optional[str]:"""使用vld擴展從文件中提取PHP opcodes,適配詳細輸出格式。Args:filepath: PHP文件的路徑。php_executable: PHP可執行文件的路徑。Returns:如果成功,返回一個包含opcode的空格分隔字符串,否則返回None。"""if not os.path.exists(filepath):logger.error(f"文件未找到用于opcode提取: {filepath}")return Nonecmd = [php_executable,'-dvld.active=1','-dvld.execute=0',filepath]logger.debug(f"運行命令用于opcode提取: {' '.join(cmd)}")try:# --- 正確的提取邏輯,用于解析表格 ---output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)output_str = output.decode('utf-8', errors='ignore') # 使用 utf-8 解碼,忽略可能的解碼錯誤tokens = re.findall(r'\s(\b[A-Z_]+\b)\s', output_str)opcodes = " ".join(tokens)# --- 提取邏輯結束 ---if not opcodes:# 即使命令成功,也可能因為文件內容或VLD的特殊輸出而沒有Opcode# logger.warning(f"未從 {filepath} 提取到有效格式的Opcode。VLD輸出起始部分: {result.stdout[:300]}...")return ""print("|==============文件{}提取出來的opcode:{}".format(filepath,opcodes))return opcodesexcept FileNotFoundError:logger.error(f"'{php_executable}'命令未找到。無法提取opcode。")return Noneexcept subprocess.TimeoutExpired:logger.warning(f"Opcode提取超時于{filepath}。跳過。")return Noneexcept Exception as e:logger.error(f"在{filepath}提取opcode時發生錯誤: {e}")return None
- 使用 TF-IDF 處理:特征工程 (CountVectorizer + TF-IDF)
logger.info(f"應用 CountVectorizer (ngrams={ngram_range}, min_df={min_df}, max_df={max_df})...")vectorizer = CountVectorizer(ngram_range=ngram_range,decode_error="ignore",# token_pattern=r'\s(\b[A-Z_]+\b)\s', # 匹配 Opcode 的模式min_df=min_df,max_df=max_df)
- 隨機劃分為訓練集和測試集。
logger.info(f"劃分數據 (test_size={test_size}, random_state={random_state})...")X_train, X_test, y_train, y_test = train_test_split(X_tfidf, y_labels, test_size=test_size, random_state=random_state, stratify=y_labels # stratify 保證訓練集和測試集標簽比例相似)logger.info(f"訓練集大小: {X_train.shape[0]}, 測試集大小: {X_test.shape[0]}")
大概劃分 訓練集大小: 13359, 測試集大小: 5726
- 使用樸素貝葉斯算法在訓練集上訓練,獲得模型數據。
logger.info("訓練多項式樸素貝葉斯模型...")model = MultinomialNB()model.fit(X_train, y_train)logger.info("模型訓練完成。")
用模型數據在測試集上進行預測。
logger.info("--- 在測試集上評估模型 ---")y_pred = model.predict(X_test)accuracy = accuracy_score(y_test, y_pred)precision = precision_score(y_test, y_pred, zero_division=0) # 處理除零情況recall = recall_score(y_test, y_pred, zero_division=0)f1 = f1_score(y_test, y_pred, zero_division=0)conf_matrix = confusion_matrix(y_test, y_pred)logger.info(f"準確率 (Accuracy): {accuracy:.4f}")logger.info(f"精確率 (Precision): {precision:.4f}")logger.info(f"召回率 (Recall): {recall:.4f}")logger.info(f"F1 分數 (F1-Score): {f1:.4f}")
- 驗證樸素貝葉斯算法預測效果。
--- 在測試集上評估模型 ---
- 準確率 (Accuracy): 0.9555
- 精確率 (Precision): 0.9305
- 召回率 (Recall): 0.8796
- F1 分數 (F1-Score): 0.9043
- 混淆矩陣 (Confusion Matrix):
-
[[4266 90][ 165 1205]]
實戰測試
效果很一般,誤報極高,本來只有兩個文件的
Ⅴ 當前問題
第一:go進程如果要調用opcode進行預測,是否需要自動編譯一個內置環境,還是說有內置go環境
需要手動寫一個內置的php環境,用于獲取opcode,單純go程序,無法正常獲取
Ⅵ 下一步優化
- 調參
- AST解析出操作序列