木马查杀篇—Opcode提取

[复制链接]
发表于 2025-9-10 03:08:11 | 显示全部楼层 |阅读模式
【前言】 介绍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版本
安装脚本
  1. cd vld-0.15.0
  2. # 使用phpize生成配置脚本,并指定php-config路径(与你的 PHP 路径相关 )
  3. /www/server/php/72/bin/phpize
  4. ./configure --with-php-config=/www/server/php/72/bin/php-config --enable-vld
  5. # 编译
  6. make && make install
  7. #可看到一个so文件
  8. /opt/vld-0.15.0/vld-0.15.0/modules/vld.so
  9. # php.ini文件添加
  10. extension=vld.so
  11. # 重启php服务
  12. systemctl restart php7.2
复制代码
测试 111.php
  1. <?php
  2.     echo "Hello World";
  3. ?>
复制代码
查看php文件的Opcode
  1. 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模子训练

   还未实现
  Ⅳ 实战环节

  1. def extract_opcodes(filepath: str, php_executable: str = 'php') -> Optional[str]:
  2.     """
  3.     使用vld扩展从文件中提取PHP opcodes,适配详细输出格式。
  4.     Args:
  5.         filepath: PHP文件的路径。
  6.         php_executable: PHP可执行文件的路径。
  7.     Returns:
  8.         如果成功,返回一个包含opcode的空格分隔字符串,否则返回None。
  9.     """
  10.     if not os.path.exists(filepath):
  11.         logger.error(f"文件未找到用于opcode提取: {filepath}")
  12.         return None
  13.     cmd = [
  14.         php_executable,
  15.         '-dvld.active=1',
  16.         '-dvld.execute=0',
  17.         filepath
  18.     ]
  19.     logger.debug(f"运行命令用于opcode提取: {' '.join(cmd)}")
  20.     try:
  21.         # --- 正确的提取逻辑,用于解析表格 ---
  22.         output = subprocess.check_output(
  23.             cmd,
  24.             stderr=subprocess.STDOUT
  25.         )
  26.         output_str = output.decode('utf-8', errors='ignore') # 使用 utf-8 解码,忽略可能的解码错误
  27.       
  28.         
  29.         tokens = re.findall(r'\s(\b[A-Z_]+\b)\s', output_str)
  30.         opcodes = " ".join(tokens)
  31.         # --- 提取逻辑结束 ---
  32.         if not opcodes:
  33.              # 即使命令成功,也可能因为文件内容或VLD的特殊输出而没有Opcode
  34.             #  logger.warning(f"未从 {filepath} 提取到有效格式的Opcode。VLD输出起始部分: {result.stdout[:300]}...")
  35.              return ""
  36.         print("|==============文件{}提取出来的opcode:{}".format(filepath,opcodes))
  37.         return opcodes
  38.     except FileNotFoundError:
  39.         logger.error(f"'{php_executable}'命令未找到。无法提取opcode。")
  40.         return None
  41.     except subprocess.TimeoutExpired:
  42.         logger.warning(f"Opcode提取超时于{filepath}。跳过。")
  43.         return None
  44.     except Exception as e:
  45.         logger.error(f"在{filepath}提取opcode时发生错误: {e}")
  46.         return None
复制代码


  • 利用 TF-IDF 处理:特征工程 (CountVectorizer + TF-IDF)
  1.     logger.info(f"应用 CountVectorizer (ngrams={ngram_range}, min_df={min_df}, max_df={max_df})...")
  2.     vectorizer = CountVectorizer(
  3.         ngram_range=ngram_range,
  4.         decode_error="ignore",
  5.         # token_pattern=r'\s(\b[A-Z_]+\b)\s', # 匹配 Opcode 的模式
  6.         min_df=min_df,
  7.         max_df=max_df
  8.     )
复制代码


  • 随机划分为训练集和测试集。
  1.     logger.info(f"划分数据 (test_size={test_size}, random_state={random_state})...")
  2.     X_train, X_test, y_train, y_test = train_test_split(
  3.         X_tfidf, y_labels, test_size=test_size, random_state=random_state, stratify=y_labels # stratify 保证训练集和测试集标签比例相似
  4.     )
  5.     logger.info(f"训练集大小: {X_train.shape[0]}, 测试集大小: {X_test.shape[0]}")
复制代码
大概划分 训练集巨细: 13359, 测试集巨细: 5726


  • 利用朴素贝叶斯算法在训练集上训练,获得模子数据。
  1.     logger.info("训练多项式朴素贝叶斯模型...")
  2.     model = MultinomialNB()
  3.     model.fit(X_train, y_train)
  4.     logger.info("模型训练完成。")
复制代码
用模子数据在测试集上进行预测。
  1.     logger.info("--- 在测试集上评估模型 ---")
  2.     y_pred = model.predict(X_test)
  3.     accuracy = accuracy_score(y_test, y_pred)
  4.     precision = precision_score(y_test, y_pred, zero_division=0) # 处理除零情况
  5.     recall = recall_score(y_test, y_pred, zero_division=0)
  6.     f1 = f1_score(y_test, y_pred, zero_division=0)
  7.     conf_matrix = confusion_matrix(y_test, y_pred)
  8.     logger.info(f"准确率 (Accuracy):  {accuracy:.4f}")
  9.     logger.info(f"精确率 (Precision): {precision:.4f}")
  10.     logger.info(f"召回率 (Recall):    {recall:.4f}")
  11.     logger.info(f"F1 分数 (F1-Score):  {f1:.4f}")
复制代码


  • 验证朴素贝叶斯算法预测效果。
  1. --- 在测试集上评估模型 ---
  2. - 准确率 (Accuracy):  0.9555
  3. - 精确率 (Precision): 0.9305
  4. - 召回率 (Recall):    0.8796
  5. - F1 分数 (F1-Score):  0.9043
  6. - 混淆矩阵 (Confusion Matrix):
  7. -
  8. [[4266   90]
  9. [ 165 1205]]
复制代码
实战测试
效果很一般,误报极高,原来只有两个文件的

Ⅴ 当前题目

第一:go进程如果要调用opcode进行预测,是否须要主动编译一个内置环境,还是说有内置go环境
须要手动写一个内置的php环境,用于获取opcode,单纯go程序,无法正常获取
Ⅵ 下一步优化



  • 调参
  • AST解析出操纵序列

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

×
登录参与点评抽奖,加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表