从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源代码编译过程中的中间产物,具有以下特点:
- 生成方式:通过
xcrun swiftc -dump-ast命令可以生成AST - 编译阶段:AST在语义分析阶段构建,介于源代码解析和代码生成之间
- 结构特点:保留了完整的语法结构信息,包括变量声明、函数定义、控制流等
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中的运算表达式有以下特点:
-
运算符表示:通过
.extension后缀标识.extension +:加法.extension -:减法.extension &:按位与.extension >>:右移.extension ^:按位异或.extension count:计算数组长度.extension subscript:数组下标访问
-
运算顺序:先运算符后操作数
示例:
(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
题目分析
-
函数定义:
func check(encoded: String, keyValue: String) -> Bool -
变量初始化:
let b = Array(encoded.utf8) let k = Array(keyValue.utf8) -
加密逻辑:
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 总结与技巧
-
AST分析技巧:
- 关注
range属性定位代码位置 - 注意
.extension标识的运算符 - 变量赋值优先看
value属性 - 函数调用注意
declref_expr节点
- 关注
-
逆向步骤:
- 先还原整体函数结构
- 再分析变量定义和初始化
- 然后处理控制流结构
- 最后解析运算表达式
-
实用工具:
- 使用
swiftc -dump-ast生成AST - 结合官方Swift语法参考文档
- 使用图形化工具可视化AST结构
- 使用
通过系统性地分析Swift AST结构,可以有效逆向出原始代码逻辑,这在CTF比赛和实际安全研究中都有重要应用价值。