从2022 CISCN 生发的对Swift Ast逆向类题目的一点思路
字数 1122 2025-08-06 23:10:27

Swift AST逆向分析技术详解

0x00 前言

本文基于2022年CISCN比赛中出现的Swift AST逆向题目,系统性地介绍Swift抽象语法树(AST)的分析方法和逆向技巧。通过实际案例展示如何从Swift AST还原原始代码逻辑。

0x01 Swift AST产生原理

Swift AST是Swift源代码编译过程中的中间产物,具有以下特点:

  1. 生成方式:通过xcrun swiftc -dump-ast命令可以生成AST
  2. 编译阶段:AST在语义分析阶段构建,介于源代码解析和代码生成之间
  3. 结构特点:保留了完整的语法结构信息,包括变量声明、函数定义、控制流等

0x02 Swift AST结构解析

函数声明结构

(func_decl range=[re.swift:1:1 - line:14:1] "check(_:_:)" interface type='(String, String) -> Bool' access=internal
  (parameter_list range=[re.swift:1:11 - line:1:49]
    (parameter "encoded" type='String' interface type='String')
    (parameter "keyValue" type='String' interface type='String'))
  (result
    (type_ident
      (component id='Bool' bind=Swift.(file).Bool))))

关键元素:

  • func_decl:函数声明标识
  • range:在源文件中的位置范围
  • 参数列表:包含参数名和类型
  • 返回值类型:明确指定

变量赋值结构

(argument
  (integer_literal_expr type='Int' location=re.swift:6:55 range=[re.swift:6:55 - line:6:55] value=3 builtin_initializer=Swift.(file).Int.init(_builtinIntegerLiteral:) initializer=NULL))

关键属性:

  • type:变量类型
  • range:代码位置范围(格式为文件名:起始行:起始列 - 结束行:结束列
  • value:变量值
  • initializer:初始化方式

列表转换操作

(argument_list
  (argument
    (member_ref_expr type='String.UTF8View' location=re.swift:2:29 range=[re.swift:2:21 - line:2:29] decl=Swift.(file).String extension.utf8
      (declref_expr type='String' location=re.swift:2:21 range=[re.swift:2:21 - line:2:21] decl=re.(file).check(_:_:).encoded@re.swift:1:14 function_ref=unapplied))))

对应Swift代码:

encoded.encode('utf8')

循环结构

(for_each_stmt range=[re.swift:5:5 - line:12:5]
  (pattern_named type='Int' 'i')
  (pattern_named type='Int' 'i')
  (binary_expr type='ClosedRange<Int>' location=re.swift:5:15 range=[re.swift:5:14 - line:5:26] nothrow
    (dot_syntax_call_expr implicit type='(Int, Int) -> ClosedRange<Int>' location=re.swift:5:15 range=[re.swift:5:15 - line:5:15] nothrow
      (declref_expr type='(Int.Type) -> (Int, Int) -> ClosedRange<Int>' location=re.swift:5:15 range=[re.swift:5:15 - line:5:15] decl=Swift.(file).Comparable extension.... function_ref=double)
      (argument_list implicit
        (argument
          (type_expr implicit type='Int.Type' location=re.swift:5:15 range=[re.swift:5:15 - line:5:15] typerepr='Int'))
      ))))

表示一个for循环结构,包含循环变量定义和范围表达式。

0x03 运算表达式分析

Swift AST中的运算表达式有以下特点:

  1. 运算符表示:通过.extension后缀标识

    • .extension +:加法
    • .extension -:减法
    • .extension &:按位与
    • .extension >>:右移
    • .extension ^:按位异或
    • .extension count:计算数组长度
    • .extension subscript:数组下标访问
  2. 运算顺序:先运算符后操作数

示例:

(subscript_expr type='@lvalue UInt8' location=re.swift:6:44 range=[re.swift:6:43 - line:6:48] decl=Swift.(file).Array##extension.subscript(_:)##
  (declref_expr type='@lvalue [UInt8]' location=re.swift:6:43 range=[re.swift:6:43 - line:6:43] decl=re.(file).check(_:_:).##b@re.swift##:2:9 function_ref=unapplied))

对应Swift代码:

b[i]

0x04 实际案例分析:baby_tree

题目分析

  1. 函数定义

    func check(encoded: String, keyValue: String) -> Bool
    
  2. 变量初始化

    let b = Array(encoded.utf8)
    let k = Array(keyValue.utf8)
    
  3. 加密逻辑

    for i in 0..<b.count-3 {
        let r0 = b[i], r1 = b[i+1], r2 = b[i+2], r3 = b[i+3]
        b[i] = r2 ^ ((k[0] + (r0 >> 4)) & 0xff)
        b[i+1] = r3 ^ ((k[1] + (r0 >> 2)) & 0xff)
        b[i+2] = r0 ^ k[2]
        b[i+3] = r1 ^ k[3]
        // 密钥轮换
        (k[0], k[1], k[2], k[3]) = (k[1], k[2], k[3], k[0])
    }
    

完整还原代码

def check(encoded, keyValue):
    b = bytearray(encoded.encode('utf8'))
    k = bytearray(keyValue.encode('utf8'))
    for i in range(len(b)-3):
        r0, r1, r2, r3 = b[i], b[i+1], b[i+2], b[i+3]
        b[i] = r2 ^ ((k[0] + (r0 >> 4)) & 0xff)
        b[i+1] = r3 ^ ((k[1] + (r0 >> 2)) & 0xff)
        b[i+2] = r0 ^ k[2]
        b[i+3] = r1 ^ k[3]
        k[0], k[1], k[2], k[3] = k[1], k[2], k[3], k[0]
    return b == expected_encrypted_data

解密脚本编写

根据加密逻辑,可以编写对应的解密脚本:

def decrypt(encrypted, key):
    b = bytearray(encrypted)
    k = bytearray(key.encode('utf8'))
    for i in reversed(range(len(b)-3)):
        # 逆向密钥轮换
        k[0], k[1], k[2], k[3] = k[3], k[0], k[1], k[2]
        r0_enc, r1_enc, r2_enc, r3_enc = b[i], b[i+1], b[i+2], b[i+3]
        # 逆向计算原始值
        r0 = r2_enc ^ k[2]
        r1 = r3_enc ^ k[3]
        r2 = r0_enc ^ ((k[0] + (r0 >> 4)) & 0xff)
        r3 = r1_enc ^ ((k[1] + (r0 >> 2)) & 0xff)
        b[i], b[i+1], b[i+2], b[i+3] = r0, r1, r2, r3
    return b.decode('utf8')

0x05 总结与技巧

  1. AST分析技巧

    • 关注range属性定位代码位置
    • 注意.extension标识的运算符
    • 变量赋值优先看value属性
    • 函数调用注意declref_expr节点
  2. 逆向步骤

    • 先还原整体函数结构
    • 再分析变量定义和初始化
    • 然后处理控制流结构
    • 最后解析运算表达式
  3. 实用工具

    • 使用swiftc -dump-ast生成AST
    • 结合官方Swift语法参考文档
    • 使用图形化工具可视化AST结构

通过系统性地分析Swift AST结构,可以有效逆向出原始代码逻辑,这在CTF比赛和实际安全研究中都有重要应用价值。

Swift AST逆向分析技术详解 0x00 前言 本文基于2022年CISCN比赛中出现的Swift AST逆向题目,系统性地介绍Swift抽象语法树(AST)的分析方法和逆向技巧。通过实际案例展示如何从Swift AST还原原始代码逻辑。 0x01 Swift AST产生原理 Swift AST是Swift源代码编译过程中的中间产物,具有以下特点: 生成方式 :通过 xcrun swiftc -dump-ast 命令可以生成AST 编译阶段 :AST在语义分析阶段构建,介于源代码解析和代码生成之间 结构特点 :保留了完整的语法结构信息,包括变量声明、函数定义、控制流等 0x02 Swift AST结构解析 函数声明结构 关键元素: func_decl :函数声明标识 range :在源文件中的位置范围 参数列表:包含参数名和类型 返回值类型:明确指定 变量赋值结构 关键属性: type :变量类型 range :代码位置范围(格式为 文件名:起始行:起始列 - 结束行:结束列 ) value :变量值 initializer :初始化方式 列表转换操作 对应Swift代码: 循环结构 表示一个for循环结构,包含循环变量定义和范围表达式。 0x03 运算表达式分析 Swift AST中的运算表达式有以下特点: 运算符表示 :通过 .extension 后缀标识 .extension + :加法 .extension - :减法 .extension & :按位与 .extension >> :右移 .extension ^ :按位异或 .extension count :计算数组长度 .extension subscript :数组下标访问 运算顺序 :先运算符后操作数 示例: 对应Swift代码: 0x04 实际案例分析:baby_ tree 题目分析 函数定义 : 变量初始化 : 加密逻辑 : 完整还原代码 解密脚本编写 根据加密逻辑,可以编写对应的解密脚本: 0x05 总结与技巧 AST分析技巧 : 关注 range 属性定位代码位置 注意 .extension 标识的运算符 变量赋值优先看 value 属性 函数调用注意 declref_expr 节点 逆向步骤 : 先还原整体函数结构 再分析变量定义和初始化 然后处理控制流结构 最后解析运算表达式 实用工具 : 使用 swiftc -dump-ast 生成AST 结合官方Swift语法参考文档 使用图形化工具可视化AST结构 通过系统性地分析Swift AST结构,可以有效逆向出原始代码逻辑,这在CTF比赛和实际安全研究中都有重要应用价值。