一、词法 / 文本层面的混淆(最表层的那一层)

1. 标识符改名(Identifier Renaming)

  • 把有语义的名字改成无意义的短名或乱码

    • download_and_exfiltrate_credentials()a()
    • userToken_0x13f4
  • 特点:

    • 对语义完全无影响,但严重影响可读性;
    • 编译型/解释型语言里都常见,恶意 & 良性(压缩、保护 IP)都会用。
  • 在 npm / PyPI 中:

    • 一些恶意包会配合其它技术,把核心逻辑集中到一个“index.min.js / core.cpython-xyz.so + 乱名变量”的文件里。

2. 去掉格式 / 压缩(Minification)

  • 删除所有换行、缩进、多余空格、注释

    • 正常源码 → 一行 JS / Python / Lua “长蛇阵”。
  • 典型场景:

    • Web 前端打包工具(Webpack、UglifyJS 等);
    • 恶意脚本、恶意 npm 包也利用这个手段“顺便”隐藏逻辑。
  • 检测挑战:

    • 单纯“变成一行代码”不能说明恶意,只能当“可疑信号 +1”。

3. 字符级伪装(Unicode / 同形字 / 看不见字符)

  • 使用Unicode 同形字、零宽字符等:

    • 同形字(homoglyph):a(ASCII)和某些看起来一样的 Unicode 字符;
    • 零宽空格/零宽连接符插到标识符中,肉眼看是 user,内存里是 user
  • 在安全场景:

    • 可以造成“看起来一样,实际上不是同一个标识符/域名/包名”。
    • 配合 typosquatting:reqquests + 同形字之类。

二、字符串 / 数据层混淆(在恶意包领域非常常见)

很多恶意包不会直接把恶意 URL、Token、Payload 写明,而是进行多层编码和加密

1. 字符串编码(Base64 / Hex / 自定义编码)

  • 常见套路:

    • 把 URL、命令、脚本片段编码成 Base64 / Hex:

      1
      2
      3
      import base64, zlib
      payload = zlib.decompress(base64.b64decode(DATA))
      exec(payload)
    • JS 里用:

      1
      eval(atob("ZXZhbChhbGVydCgnSGVsbG8nKSk="))
  • 变种:

    • 多层嵌套:Base64 → zlib → 再 Base64;
    • 自定义编码表、手写解码函数(字符轮换、异或等)。

2. 字符串拆分 / 拼接

  • 把敏感字符串拆成碎片,然后运行时拼接:

    1
    2
    3
    const part1 = "ht" + "tps";
    const part2 = "://exa" + "mple.com";
    const url = part1 + part2;
  • 用途:

    • 逃避基于关键字的简单扫描(比如“检测是否包含某域名/某命令”)。

3. 运行时解密字符串(String Encryption)

  • 将敏感字符串加密存储,运行时再解密:

    1
    2
    3
    4
    5
    6
    def dec(s, key):
    return ''.join(chr(ord(c) ^ key) for c in s)

    enc = "\x10\x02\x07..." # 加密后的 payload
    real = dec(enc, 0x42)
    exec(real)
  • 关键点:

    • 静态分析时看不到明文,只能通过符号执行/仿真运行才能恢复;
    • 如果解密 key 来自环境(比如某个机器 ID),就变成“环境绑定的解密”。

4. 数据结构伪装(用数组/字典藏代码)

  • 把真正要执行的东西藏在数组、字典、长列表里:

    • 例如:把一段脚本按字符拆到数组中,再根据索引重组;
    • 或者把不同部分混在一个大数组,运行时按某个顺序挑选执行。
  • 对特征提取的影响:

    • 使得直接基于 token/字符串统计的特征难以识别“恶意模式”。

三、控制流 / 语法结构混淆(让程序“看起来不走直线”)

这种更多出现在高级混淆器和有一定技术实力的攻击者写的恶意包里。

1. 控制流平坦化(Control-Flow Flattening)

  • 原本清晰的 if / loop 结构,被改写成“状态机 + 大 switch”:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    int state = 0;
    while (1) {
    switch (state) {
    case 0: ...; state = 2; break;
    case 1: ...; state = 3; break;
    case 2: ...; state = 1; break;
    ...
    }
    }
  • 对分析的影响:

    • 控制流图会变成一坨高连接度节点,很难直接读懂执行顺序;
    • 静态分析的路径枚举变得复杂。

2. 伪造分支(Opaque Predicates / Bogus Control Flow)

  • 引入一些逻辑上恒真/恒假的条件,但用复杂表达式写:

    1
    2
    3
    4
    5
    if ((x*x) % 4 == 0) {  // 对某些特定 x 永远为真
    // 真正逻辑
    } else {
    // 无意义的死代码
    }
  • 目的:

    • 让控制流图变复杂;
    • 混淆覆盖率、路径数目,让符号执行成本暴涨。

3. 循环和函数的结构变换

  • 例如:

    • 展开/收缩循环:把一个循环改写成多个重复块,或者反之;
    • 内联 / 反内联:把多个函数“搅和”到一起,或把逻辑拆成很多小函数在不同文件里。
  • 在恶意包中:

    • 有时会把真实行为分散到多个 file / module / function,再通过动态导入、回调串起来。

四、动态特性与自修改:eval / 反射 / 动态导入

这一类非常关键,因为 npm / PyPI 恶意包特别爱用“安装脚本 + 动态执行”

1. 动态执行:eval / exec / Function / new Function

  • JS:

    1
    2
    const code = getDecryptedPayload();
    (new Function(code))();
  • Python:

    1
    2
    code = decrypt(DATA)
    exec(code, {})
  • 效果:

    • 把真正的恶意逻辑藏在字符串/数据里,静态分析只能看到一条 exec
    • 必须模拟运行或做更复杂的分析才能“看到”这段代码。

2. 反射与动态导入(Reflection / Dynamic Import)

  • 例子:

    • Python:__import__(module_name)getattr(mod, func_name)
    • JS:动态 require(someVar)import(someUrl)
  • 应用:

    • 根据环境变量、系统信息、网络响应决定导入哪个模块 / 执行哪段逻辑;
    • 恶意包可以在“正常环境”只执行 benign 逻辑,在目标环境再加载恶意模块。

3. 自修改代码(Self-Modifying Code)

  • 比较少见但很“骚”的玩法:

    • 第一次运行时修改自己的源码文件;
    • 或将新代码写入某个文件,下次再从这个文件加载执行。
  • 在软件包场景:

    • 可见于“安装后悄悄写一个额外脚本/定时任务”的行为,这部分逻辑本身也可能再被混淆。

五、包级结构与加载流程上的混淆(恶意软件“分层包装”)

从“单文件脚本混淆”进化到“整个包的结构设计就是一种混淆”。

1. 多阶段加载(Staged Payload)

  • 第 1 阶段(安装脚本 / 入口文件):

    • 只做网络请求 + 下载第二阶段 payload;
    • 或只做解码/解密,真正恶意逻辑不直接内置。
  • 第 2 阶段(下载的脚本 / 库):

    • 再做一次混淆/解密,执行真正的窃取/挖矿等逻辑。
  • 好处:

    • 提高溯源难度;
    • 即使第一阶段被发现,攻击者可以在服务器上替换第二阶段代码。

2. 借壳 / 依附正常库(Piggybacking)

  • 在一个看起来正常的包里:

    • 大部分文件、API 都正常,甚至 fork 自知名项目;
    • 只在某个角落放入一段混淆的恶意逻辑(比如一个“调试用 script”)。
  • 从检测视角:

    • 抽样扫描、简单统计特征很容易漏掉这部分小而隐蔽的 payload。

3. 资源化/配置化恶意逻辑

  • 不把恶意代码写在 .py / .js 里,而是:

    • 藏在 JSON / 图片 / 配置文件 / README 注释中;
    • 运行时读取这些资源再解码执行。
  • 这类属于“把代码伪装成数据”,往往会逃掉只扫描源文件的工具。


六、对抗分析 / 对抗检测相关(和混淆高度耦合的技巧)

严格说这类更偏“反分析”/“反沙箱检测”,但在恶意包研究中经常与混淆一起出现。

1. 环境检测(Anti-Sandbox / Anti-Debug)

  • 例子:

    • 检查进程列表、用户名、环境变量是否像分析环境;
    • 检查文件路径、网络连通性;
    • 延时执行(只有运行很久才进入恶意分支)。
  • 与混淆结合:

    • 环境检测逻辑本身也被混淆;
    • 只有在“不是沙箱”的情况下才解密真正 payload。

2. 针对特征的对抗性修改(Adversarial Obfuscation)

  • 知道某些检测工具依赖某一类特征(token、字符串、API 统计),就刻意:

    • 插入大量无害但“高度混淆”的代码当噪声;
    • 模仿良性库的风格/依赖关系,欺骗学习模型。
  • 这个在你做“基于 ML/深度学习的恶意包检测”时,需要专门想对策。


七、和你的研究/写作怎么对齐

如果把上面的东西压缩到几句话写进论文/专利,可以概括为三层:

  1. 表层混淆(Lexical / Syntactic): 标识符改名、压缩、伪装字符、简单编码等,主要是降低人工可读性,部分会干扰基于 token 的静态检测。

  2. 语义相关混淆(Control-flow & Data): 控制流平坦化、伪造分支、字符串加密/多层编码、数据结构伪装等,直接破坏控制流图、数据流分析和常规特征提取。

  3. 结构与行为混淆(Structural & Behavioral): 多阶段加载、动态执行、反射和环境绑定、自修改代码,以及把恶意逻辑拆到包结构里的各个角落,从整体上降低静态和简单动态检测的有效性。