返回列表 发新帖

flutter 某岛后续

[复制链接]

20

主题

220

回帖

1270

积分

高中生

Rank: 4

金币
404
好评
1
信誉
100
发表于 2026-1-22 16:55:28 来自手机  | 显示全部楼层 | 阅读模式  来自 北京
flutter某岛后,解除vip,登录,卡在了下载上。研究下来,不知道是临门一脚,还是game over了。
发上分析。

逐行代码分析
1. 函数入口与栈帧设置 (Prologue)
_ clickDownload(/* No info */) async {
// addr: 0xf5bd78, size: 0x5f4
// 注释:函数起始地址0xf5bd78,总大小0x5f4字节
// 0xf5bd78: EnterFrame
//     0xf5bd78: stp             fp, lr, [SP, #-0x10]!
//     0xf5bd7c: mov             fp, SP
// 分析:标准的函数入口。保存帧指针(FP)和链接寄存器(LR)到栈上,并建立新的栈帧。
// 0xf5bd80: AllocStack(0x80)
//     0xf5bd80: sub             SP, SP, #0x80
// 分析:分配0x80(128)字节的栈空间用于局部变量存储。
// 0xf5bd84: SetupParameters(...)
//     0xf5bd84: stur            NULL, [fp, #-8]
//     0xf5bd88: mov             x0, x3
//     0xf5bd8c: stur            x1, [fp, #-0x10]
//     0xf5bd90: stur            x2, [fp, #-0x18]
//     0xf5bd94: stur            x3, [fp, #-0x20]
//     0xf5bd98: stur            x5, [fp, #-0x28]
// 分析:保存传入的参数(this指针、以及可能的Event参数)到栈帧中(fp-0x10到fp-0x28)。
// 0xf5bd9c: CheckStackOverflow
//     0xf5bd9c: ldr             x16, [THR, #0x48]  ; THR::stack_limit
//     0xf5bda0: cmp             SP, x16
//     0xf5bda4: b.ls            #0xf5c364
// 分析:检查栈溢出。加载线程(THR)中的栈限制,如果SP小于限制,则跳转到溢出处理stub。
2. 上下文初始化与异步设置
// 0xf5bda8: r1 = 2
//     0xf5bda8: mov             x1, #2
// 分析:准备参数,可能用于AllocateContext。
// 0xf5bdac: r0 = AllocateContext()
//     0xf5bdac: bl              #0x118589c  ; AllocateContextStub
//     0xf5bdb0: mov             x2, x0
// 分析:调用运行时Stub分配一个新的ExecutionContext(用于异步操作捕获变量)。
// 0xf5bdb4: ldur            x1, [fp, #-0x10]
// 0xf5bdb8: stur            x2, [fp, #-0x30]
// 分析:将分配的Context对象保存到栈变量(fp-0x30)中。
// ... (中间省略部分字段存储操作) ...
// 0xf5bdc8: InitAsync() -Future
//     0xf5bdc8: mov             x0, NULL
//     0xf5bdcc: bl              #0x943f24  ; InitAsyncStub
// 分析:初始化异步函数,创建并返回一个Future对象。
3. 核心逻辑分支:检查下载状态 (getIsDownloaded)
// 0xf5bdd0: ldur            x0, [fp, #-0x10] ; 加载this
// 0xf5bdd4: LoadField: r1 = r0-field_d7 ; 加载字段
//     0xf5bdd4: ldur            w1, [x0, #0xd7]
// 0xf5bdd8: DecompressPointer r1 ; 解压缩指针(Dart 2.8+压缩指针特性)
//     0xf5bdd8: add             x1, x1, HEAP, lsl #32
// 0xf5bddc: tbnz            w1, #4, #0xf5bde8
// 分析:加载this.field_d7(可能是_asyncCompleter或状态标记)。
//       tbnz:测试第4位是否为1。如果为1(非空/已完成),跳转到0xf5bde8;
//       否则(为0),执行下面的错误处理。
// --- 分支A:条件不满足(执行ReturnAsyncNotFuture) ---
// 0xf5bde0: r0 = Null
//     0xf5bde0: mov             x0, NULL
// 0xf5bde4: r0 = ReturnAsyncNotFuture()
//     0xf5bde4: b               #0x943a1c  ; 跳转到错误处理Stub
// 分析:如果上述条件不满足,直接跳转到异步错误返回。
// --- 分支B:条件满足(继续执行) ---
// 0xf5bde8: ... (跳过错误分支) ...
// 0xf5be00: r0 = getIsDownloaded()
//     0xf5be00: bl              #0xf5c36c  ; 调用getter方法
// 0xf5be04: tbz             w0, #4, #0xf5bf08
// 分析:调用getIsDownloaded()方法。
//       tbz:测试返回值的第4位。在Dart汇编中,布尔值和对象指针通常通过tag bit(如bit 0或bit 1)区分。
//       如果结果为false(或null),跳转到0xf5bf08(可能是显示BottomSheet的逻辑)。
4. 分支逻辑:显示模态底部弹窗 (showModalBottomSheet)
// --- 逻辑块:显示BottomSheet(当!isDownloaded) ---
// 0xf5be08: r0 = InitLateStaticField(0xde0) // User::instance
//     0xf5be08: ... (初始化静态单例User.instance) ...
// 分析:确保User单例已初始化。
// 0xf5be24: ... (获取User.instance的某个字段) ...
// 0xf5be34: r1 = LoadInt32Instr(r0)
//     0xf5be34: sbfx            x1, x0, #1, #0x1f
// 分析:从对象中提取整数值(可能是用户ID或状态码)。
// 0xf5be40: cmp             x1, #0
// 0xf5be44: b.gt            #0xf5bee8
// 分析:比较提取的值是否0。如果大于0,跳转到0xf5bee8(跳过弹窗逻辑?或者执行不同分支)。
// --- 显示BottomSheet的代码(0xf5be48 - 0xf5bed0) ---
// 0xf5be48: ... (准备参数) ...
// 0xf5be4c: d1 = 0.650000 ; 加载浮点常量
// 0xf5be54: d0 = 0.700000 ; 加载浮点常量
// 分析:加载用于UI布局的百分比数值(例如65%, 70%屏幕高度)。
// 0xf5be5c: r0 = LoadStaticField(0xdac) ; 可能是Theme或配置
// 0xf5be64: LoadField: d2 = r0-field_7 ; 加载double值
// 0xf5be68: fmul            d3, d2, d1 ; d3 = d2 * 0.65
// 0xf5be70: fmul            d1, d2, d0 ; d1 = d2 * 0.70
// 分析:计算具体的像素尺寸(基于屏幕宽度或高度*百分比)。
// 0xf5be78: r0 = BoxConstraints()
//     0xf5be78: bl              #0x9e647c
// 分析:创建BoxConstraints对象,用于限制弹窗大小。
// 0xf5bea0: r1 = Function '<anonymous closure':.
//     0xf5bea0: add             x1, PP, #0x2c...
// 分析:加载匿名闭包函数(即BottomSheet内部的builder函数)。
// 0xf5bea8: r0 = AllocateClosure()
//     0xf5bea8: bl              #0x1185c60
// 分析:分配闭包对象(Closure),捕获上下文。
// 0xf5bec8: r4 = const [0x1, 0x5, ... "backgroundColor", ...]
// 分析:加载一个常量List,这通常是Named Arguments的键值对数组,
//       用于传递给showModalBottomSheet的参数名(如backgroundColor, constraints)。
// 0xf5bed0: r0 = showModalBottomSheet()
//     0xf5bed0: bl              #0xb82544
// 分析:调用showModalBottomSheet显示底部弹窗。
//       (注:这段代码之后又将field_d7设为false并返回,说明这个弹窗可能是一个"确认下载"框)。
5. 主逻辑:执行下载与网络请求 (Post Request)
// --- 逻辑块:执行下载(当isDownloaded或其他条件) ---
// 0xf5bf34: r0 = getIsDownloaded() ; 再次调用getter
// 0xf5bf38: tbz             w0, #4, #0xf5c338
// 分析:再次检查状态。如果为false,跳转到0xf5c338(可能是直接返回或调用回调)。
// --- 构建请求参数Map ---
// 0xf5bf50: r0 = Map._fromLiteral()
// 分析:创建一个新的Map对象用于存放请求参数。
// 0xf5bf58: r0 = InitLateStaticField(0xde0) // User::instance
// 0xf5bf74: LoadField: r2 = r0-field_7 ; 获取userId
// 0xf5bf94: r2 = "userId"
// 0xf5bf98: r0 = []=() ; map['userId'] = value
// 分析:向Map中添加"userId"键值对。
// 0xf5bfb0: r2 = "pwd" ; 添加"pwd"字段
// 0xf5bfdc: r2 = "sourceId" ; 添加"sourceId"字段
// 0xf5c000: r2 = "so
回复

使用道具 举报

20

主题

220

回帖

1270

积分

高中生

Rank: 4

金币
404
好评
1
信誉
100
发表于 2026-1-22 16:56:30 来自手机  | 显示全部楼层  来自 北京
// 0xf5c000: r2 = "sourceType" ; 添加"sourceType"字段
// 0xf5c024: r2 = "downloadType" ; 添加"downloadType"字段
// 分析:继续填充请求参数。
// 0xf5c030: ldur            x2, [fp, #-0x28] ; 加载参数
// 0xf5c034: r1 = "router/user/deductionDownloadNum.aspx"
// 分析:准备URL路径。
// 0xf5c03c: r4 = const [0, 0x2, 0, 0x2, null] ; 可能是HTTP配置
// 0xf5c040: r0 = post()
//     0xf5c040: bl              #0x9b02d8
// 分析:调用FHttp::post发起网络请求。
// 0xf5c04c: r0 = Await()
//     0xf5c04c: bl              #0x943cf0
// 分析:Await指令。如果Future未完成,保存状态并返回;如果已完成,继续执行。
6. 结果处理 (Success/Failure)
// 0xf5c054: LoadField: r1 = r0-field_13 ; 获取返回结果的状态码
// 0xf5c058: stur            x1, [fp, #-0x40]
// 0xf5c05c: cmp             x1, #0xc8 ; 比较状态码是否等于200(0xc8)
// 0xf5c060: b.ne            #0xf5c1d8
// 分析:检查HTTP状态码是否为200(成功)。
// --- 分支A:请求成功(Status 200) ---
// 0xf5c064: ... (解析JSON结果) ...
// 0xf5c078: r0 = jsonMap() ; 解析"result" -"user"
// 0xf5c0a8: r0 = jsonInt() ; 解析"downloadNum"(下载次数)
// 0xf5c0b0: tbnz            x2, #0x3f, #0xf5c17c
// 分析:检查downloadNum是否有效(可能检查符号位或特定标志位)。
// --- 子分支A1:扣减成功 ---
// 0xf5c0e4: r0 = value=() ; 更新ValueNotifier(通知UI刷新下载次数)
// 0xf5c0f0: r0 = AllocateArray() ; 创建字符串插值数组
// 0xf5c0f4: r16 = "当前下载券余额:"
// 0xf5c120: r0 = _interpolate() ; 字符串拼接
// 0xf5c128: r0 = fprint() ; 打印日志
// 0xf5c134: r0 = save() ; UserEntity::save保存用户信息
// 0xf5c15c: r0 = setIsDownloaded() ; 标记已下载
// 0xf5c16c: ClosureCall ; 调用传入的回调函数(fp-0x20)
// 分析:更新本地数据、保存用户信息、标记状态、调用回调。
// --- 子分支A2:扣减失败(余额不足等) ---
// 0xf5c17c: ... (构建错误信息"下载失败:") ...
// 0xf5c1c4: r0 = _interpolate()
// 0xf5c1d0: r0 = toast() ; 弹出Toast提示
// 分析:显示错误提示。
// --- 分支B:请求失败(非200) ---
// 0xf5c1d8: ldur            x2, [fp, #-0x30]
// 0xf5c1dc: r17 = 5001 ; 加载错误码5001
// 0xf5c1e0: cmp             x1, x17 ; 比较返回码是否等于5001
// 0xf5c1e4: b.ne            #0xf5c29c
// --- 子分支B1:错误码5001(特定业务错误) ---
// 0xf5c1f8: b.eq            #0xf5c284
// 0xf5c228: r0 = BoxConstraints() ; 创建约束
// 0xf5c258: r0 = AllocateClosure() ; 分配闭包
// 0xf5c280: r0 = showModalBottomSheet() ; 显示底部弹窗(可能是重新登录或确认)
// 0xf5c290: r0 = toast() ; 弹出提示
// 分析:针对特定错误码显示弹窗和提示。
// --- 子分支B2:其他错误码 ---
// 0xf5c29c: r0 = InitLateStaticField(0xfb8) // ServerCode::userVerifiedFailed
// 0xf5c2c8: b.ne            #0xf5c2ec
// 分析:检查是否为"用户验证失败"。
// --- 子子分支:用户验证失败 ---
// 0xf5c2d0: ... (获取错误信息) ...
// 0xf5c2dc: r0 = toast() ; 显示错误
// 0xf5c2e0: r0 = logout() ; 调用登出
// 0xf5c2e4: r0 = openLoginPage() ; 跳转登录页
// 分析:清理状态并跳转。
// --- 子子分支:通用错误 ---
// 0xf5c2ec: ... (构建通用错误信息"失败,请稍后重试") ...
// 0xf5c324: r0 = _interpolate()
// 0xf5c330: r0 = toast()
// 分析:显示通用错误提示。
7. 函数返回 (Epilogue)
// 0xf5c338: ... (另一个返回路径) ...
// 0xf5c344: ClosureCall ; 调用回调
// 0xf5c350: ldur            x1, [fp, #-0x10] ; 加载this
// 0xf5c354: r2 = false
// 0xf5c358: StoreField: r1-field_d7 = r2 ; this.field_d7 = false
// 0xf5c35c: r0 = Null
// 0xf5c360: r0 = ReturnAsyncNotFuture()
//     0xf5c360: b               #0x943a1c
// 分析:清理状态(设置field_d7为false),返回Null。
//       ReturnAsyncNotFuture可能是异步函数的最终返回处理。
}
回复

使用道具 举报

688

主题

1万

回帖

2万

积分

博士后

Rank: 8Rank: 8

金币
3412
好评
164
信誉
119

考神MT论坛新人MT论坛活跃会员MT论坛帅哥MT论坛灌水老大MT论坛侠客MT论坛最佳新人

发表于 2026-1-22 17:20:25 来自手机  | 显示全部楼层  来自 广东
感谢分享受
回复

使用道具 举报

0

主题

2973

回帖

6913

积分

硕士生

Rank: 6Rank: 6

金币
3331
好评
0
信誉
100

MT论坛新人考神MT论坛帅哥

发表于 2026-1-23 17:36:35 来自手机  | 显示全部楼层  来自 浙江
感谢分享
回复

使用道具 举报

4

主题

1197

回帖

2819

积分

大学生

Rank: 5Rank: 5

金币
1968
好评
0
信誉
100

MT论坛帅哥考神

发表于 2026-1-23 22:05:35 来自手机  | 显示全部楼层  来自 海南
感谢分享
回复

使用道具 举报

6

主题

3241

回帖

8439

积分

硕士生

Rank: 6Rank: 6

金币
6108
好评
0
信誉
93
发表于 2026-1-23 22:13:52 来自手机  | 显示全部楼层  来自 广东
好乱啊
回复

使用道具 举报

3

主题

216

回帖

728

积分

初中生

Rank: 3Rank: 3

金币
531
好评
0
信誉
100
发表于 2026-2-15 02:20:16 | 显示全部楼层  来自 香港
大佬怎么lx, 想交流学习一下
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表