样本是ClashRoyale,本文由AI生成,就当笔记了,请见谅(
有一说一,ai的算法敏感度和复杂逻辑抽取关键部分的能力真的吊
本文逐轮记录对 Shield 加固层的源码分析过程。每一轮按 thinking → 源码 → 分析 → 结论 结构组织,函数地址均为相对 SO 基址的 RVA。
经过方法论纠正后,第一件事是把几个看似关键的函数放在一起读,看逻辑连贯性,而不是孤立地"这个函数调了 tgkill 所以它是校验"。选的切入点是:一个注册回调的函数(sub_21A69C)、一个被大量引用的数据表首项(sub_45129C)、一个看起来像"空校验"的桩(nullsub_103)、一个走 BIO 风格回调的函数(sub_5E1AB8)。目的是先判断之前认定的"函数指针校验表 / CRC 校验"是不是真的存在。
sub_5E1AB8 (RVA 0x5E1AB8) —— 一个多分支回调,按操作码 n2 分发:
sub_21A69C (RVA 0x21A69C) —— 构造对象并装入回调表:
sub_45129C (RVA 0x45129C) —— 设置 vtable:
nullsub_103 (RVA 0x453834) —— 空桩:
sub_612A7C (RVA 0x612A7C) —— 字符串常量:
把这几个函数放一起看,逻辑高度自洽:
之前认定的"0x692Dxx 是函数指针校验表"完全错误。这片实际上是:
sub_453898(之前认定的"Shield kill 入口")只是某个 C++ 类的虚方法(析构类),跟检测无关。libsupercell_clashroyale.so 的绝大部分体积是静态链接的 LibreSSL,Shield 保护逻辑只占一小部分混在其中。抓到的 crc32 / tgkill 全是 LibreSSL 正常功能,不能作为 Shield 入口。
既然 0x692Dxx 是 vtable,那 Shield 真正的保护层得重新找。方法论给的线索是三条:调 ChaCha20 解密代码段、读写 rwxp 段、mprotect 改权限。先从 ChaCha20 入手 —— 之前 agent 报告说 ChaCha20 在 sub_249D94,但那是 agent 猜的,需要自己读代码确认,然后沿 caller 链往上追,看它解密的是 TLS 数据还是代码段。
sub_249D94 (RVA 0x249D94) —— 含 ChaCha20 常量:
sub_24A228 (RVA 0x24A228) —— caller,ChaCha20 封装:
sub_551630 (RVA 0x551630) —— 上一层 caller:
sub_551750 (RVA 0x551750) —— 再上一层,函数体充满页对齐常量:
这条链(sub_551750 → sub_551630 → sub_24A228 → sub_249D94)是按页(4096 字节)解密代码段的 ChaCha20 解密器,不是 TLS 加密。agent 之前给的 ChaCha20 地址(0x68192C)是错的,真正的 core 在 0x249D94。
但要判断它是 Shield 解密还是 LibreSSL 内部用,还得继续往上追 caller —— 看它服务于什么。
sub_551750 的 caller 是 sub_5512A4,继续往上追。如果 caller 链通往 ssl_pkt / ssl_lib 范围,那是 TLS;如果通往一个独立线程循环,那是 Shield 解密服务。重点看 sub_5512A4 的栈分配(页大小缓冲?)和它的 caller 是不是一个 __noreturn 循环。
sub_5512A4 (RVA 0x5512A4) —— 单页解密入口:
sub_550F20 (RVA 0x550F20) —— sub_5512A4 的调用者,__noreturn:
sub_5ECA08 (RVA 0x5ECA08) —— 等待原语:
sub_550E54 (RVA 0x550E54) —— 创建该线程:
Shield 用的是 生产者-消费者 RPC 线程模型做代码段按需解密(lazy decryption):
这彻底推翻了之前脑补的"周期性轮询 CRC" —— 实际是按需解密,事件驱动。
解密线程找到了,但要理解整体调度,得找到"业务代码 → 提交解密请求"这一环。sub_551110 是请求提交函数(写 dword_6BEE44),它的 caller 就是业务侧调用点。再往上 sub_551110 ← sub_5E244C,而 sub_5E244C 看起来是按 api_id 派发 —— 这可能就是用户洞察的"全局统一的调用派发函数"。重点读它的查表、解密触发、调用逻辑。
sub_5E244C (RVA 0x5E244C) —— 全局派发咽喉:
sub_551110 (RVA 0x551110) —— 解密请求提交:
完整可信执行调度模型串起来了,用户洞察全部命中:
关键 RVA:
模型建立后,下一个问题是 CRC/完整性校验在哪一层。sub_5E244C 的 if (*result != 1) 是状态检查不是 CRC。候选点:sub_62F0C8(状态检查函数内部)和 sub_5512A4(解密入口内部)。sub_5512A4 的 callees 里有 sub_18D6F8 / sub_18D7F0 / sub_18D930 —— 在 0x18Dxxx 区域,跟之前看到的 sub_18D17C(crc32)同区域,可能是 CRC helper。挨个读确认。
sub_62F0C8 (RVA 0x62F0C8) —— 状态检查函数:
sub_5512A4 后半段 —— 解密后的处理:
sub_18D6F8 (RVA 0x18D6F8) —— 被"上千"函数调用:
sub_62F0C8 是 TLS 缓冲区管理,sub_18D6F8 是 string 析构 —— 都不是 CRC。sub_5E244C 里的 *result != 1 检查的是 TLS 缓冲项首字节(解密状态标记),不是完整性比较。sub_5512A4 后半的 sub_550D6C 是候选比较点,下一轮确认。
继续排除。读 sub_18D930(0x18Dxxx 区域,可能是 hash)、sub_18D7F0(同区域)、sub_550D6C(比较候选)。这三个放一起看,判断 sub_5512A4 后半是不是解密后的完整性校验。
sub_18D930 (RVA 0x18D930) —— 被上千函数调用:
sub_18D7F0 (RVA 0x18D7F0) —— 构造+析构临时对象:
sub_550D6C (RVA 0x550D6C) —— "比较候选":
sub_5512A4 后半的 sub_550D6C(dword_6BF258, v14) 是把解密结果 write 到 fd,不是 CRC 比较。dword_6BF258 是 sub_550E54 传入的 fd(解密线程关联的输出 fd)。
sub_18D930 / sub_18D7F0 / sub_18D6F8 全是 std::string 操作。到目前为止整条解密链里没找到 CRC 比较。
还剩 sub_47981C 和 sub_20D574(都在 sub_5512A4 解密后调用)。读这两个,如果还不是 CRC,就能下结论:libsupercell 侧根本没有独立的 CRC 校验代码,完整性靠别的机制。
sub_47981C (RVA 0x47981C) —— 经典编码特征:
sub_20D574 (RVA 0x20D574) —— 字符串操作:
libsupercell 侧整条解密链里没有 CRC 校验。
所有候选都排除了:
初步推断:libsupercell 只是解密服务(ChaCha20 按需解密代码页),完整性校验 + tgkill + 重加密逻辑不在 libsupercell 的反编译里,可能在 libg.so 的 rwxp 段(Shield 解密出的保护代码本身)。(这个推断在第 10 轮被修正 —— 校验其实也在 libsupercell,只是藏在多层派发后面。)
回到第 1 点任务:dump off_6A6F98 表,对比 maps + libc decompile 回注函数名。先看表结构 —— sub_5E244C 里 *(&off_6A6F98 + v7) 取的是指针(QWORD),指向 entry 结构(+0 flags, +8 函数指针)。先 dump 指针数组本身,再 dump 它指向的 entry 内容。
off_6A6F98 指针数组(内存 dump):
整理(off_6A6F98[v7],v7 = api_id - 4):
entry 内容(0x6CD0C8 - 0x6CD1FF,内存 dump):
entry 表运行时按需解密。静态 dump 抓的时机 entry 还未被解密(全 0xFF = 加密态/未初始化态)。
这印证了第 3-4 轮的模型:sub_5E244C 触发时,先经 sub_551110 解密对应 entry → 读 flags + 函数指针 → 调用 → (可能)重新加密回去。所以静态无法回注函数名 —— 抓到的 entry 全是 0xFF。
要拿真实函数指针,必须在运行时 hook sub_5E244C,在 v8 = off_6A6F98[v7] 之后、调用 v9 之前 dump entry。但 hook 本身可能触发检测 —— 鸡生蛋问题,需要先理解检测在哪。
第 4 轮看到 sub_5E244C 的 LABEL_11 有一行 sub_6826D0(1)(返回值被忽略),当时不知道它干啥。现在回头追。
第一步:读 sub_6826D0,发现是 thunk —— return qword_6C5288(),即读 GOT[0x6C5288] 跳转。这是 Shield 的第一层混淆:通过 GOT 间接调用,让静态 caller 关系断裂(sub_6826D0 的 callers 显示 none)。
第二步:读 GOT 0x6C5288 的值。这里我读错了 —— dump 字节是 B0 88 DE 97 6F 00 00 00,我丢了一位读成 0x6FDE88B0(32 位,不像合法 ARM64 指针,查 maps 也查不到,卡住了)。用户纠正为 0x6F97DE88B0(48 位,合法指针)。
第三步:重新算 RVA:0x6F97DE88B0 - 0x6F97C5A000 (libsupercell base) = 0x18E8B0,对照 maps 落在 0x6f97de7000-0x6f982b7000 r-xp 0x18d000 段内。所以 sub_6826D0 不是 libc 函数,是 Shield 自家代码,故意用 GOT 间接调用来抗分析。
第四步:读 sub_18E8B0(RVA 0x18E8B0),发现它又是派发:sub_5533DC(ctx, 4, off_6ACCE8[0])。off_6ACCE8 这张新表浮现。
关键转折是用户纠正地址读错。 没这一步,会一直卡在"0x6FDE88B0 是什么",看不到 sub_18E8B0 和 off_6ACCE8。教训:dump 字节要逐位核对,ARM64 指针是 48 位,丢了高位就会变成无效地址。
sub_6826D0 (RVA 0x6826D0) —— thunk:
GOT 0x6C5288 的值(内存 dump):
0x6F97DE88B0 - 0x6F97C5A000 = 0x18E8B0(RVA,在 libsupercell 内)
sub_18E8B0 (RVA 0x18E8B0) —— callers: none(通过 GOT 间接调用):
off_6ACCE8 表(内存 dump):
整理:
追踪链(从 LABEL_11 到校验核心):
三层间接,每层都用不同手段混淆:GOT 间接(断裂 caller 关系)+ thunk 中转 + 表驱动派发。
off_6ACCE8 的角色 —— 校验目标表
看 sub_5533DC 怎么用它(第三个参数 a3):
a3 = off_6ACCE8[0] = 0x642BF8 是一个代码段地址。sub_5533DC 拿它对应的缓存项 v8,对 v8 处 0x98 字节算 XXH64。所以 off_6ACCE8 每一项 = 一段需要 XXH64 完整性校验的代码的起始地址:
sub_18E8B0 不是孤例 —— 它是一群 thunk 中的一个
sub_5533DC 的 callers 有 22 个,全是 0x18Exxx:
每个都是 sub_18E8B0 的同胞(同样的小 thunk 结构),区别只是用 off_6ACCxx 系列里不同的表 + 不同的 key。每个 thunk 对应一个受保护 API 的校验入口。
两层表的角色对比(容易混)
off_6A6F98 回答"调哪个函数",off_6ACCE8 回答"校验哪段代码"。两张表服务于派发的两个不同阶段。
多级派发的完整图景浮现:
off_6ACCE8 是校验目标表(不是函数表),列出所有需要完整性校验的代码段地址。sub_5533DC 是真正的校验+派发核心。下一轮深读 sub_5533DC + sub_452500,定位校验算法和 kill 路径。
sub_5533DC 是多级派发的最后一层,参数 (ctx, key, code_ptr) 强烈暗示"校验 code_ptr 处的代码然后派发"。读它完整逻辑,重点找:查表、比较、不等时的处理。同时读它调用的 sub_452500(可能是校验算法)和 sub_2ADF90(不等时调用,可能是重加密)。
sub_5533DC (RVA 0x5533DC) —— 校验+派发核心:
sub_452500 (RVA 0x452500) —— 校验算法(常量铁证):
常量识别:
sub_2ADF90 (RVA 0x2ADF90) —— 不等时调用,被 37 处调用:
sub_5533DC 完整逻辑:
sub_452500 的 5 个常量是 XXH64 的素数定义(Yann Collet xxHash 库的 XXH_PRIME64_1..5)。算法是 XXH64,不是 CRC。输入 code_ptr 处 0x98(152)字节,输出 64 位 hash。这正是用户说的"直接比较" —— 算 hash 然后直接和存储值比。
sub_2ADF90 逐字段拆解(提交重加密任务到任务队列):
结构是典型的生产者-消费者任务队列提交:
疑点已解开(汇编确认):sub_5533DC 里 sub_2ADF90() 紧跟 SVC 0x80,而 SVC 解码结果是 exit_group(1)(见下 SVC 解码)—— 进程直接终止。所以 sub_2ADF90 提交的任务根本来不及被消费执行。
那 sub_2ADF90 的真实意义是 取证/记录:
修正之前的说法:之前笼统说"由后台线程处理重加密"完全错误。准确说 —— sub_2ADF90 是异常取证入队,进程随后 exit_group(1) 直接死,队列不会被消费。sub_2ADF90 被 37 处调用(所有 XXH64 校验失败点 + 其他检测点),是统一的"异常记录口"。
关于"重新加密代码段"的观察:如果动态调试时确实看到代码段被重新加密,那不是 sub_5533DC 这条路径干的(它直接 exit)。可能是:
需要进一步动态确认 "重加密" 到底发生在哪条路径。
SVC 0x80 汇编解码 —— 确认是 exit_group(1)
从 IDA 反汇编(loc_553630,RVA 0x553630)解码:
修正之前的判断:之前推测"可能是 tgkill/exit_group/rt_tgsigqueueinfo 之一" —— 现在汇编确认是 exit_group(1)。这同时解开了 sub_2ADF90 的疑点(见下)。
完整检测 + kill 链最终定位:
修正第 7 轮的初步推断:之前说"libsupercell 侧没有 CRC,校验在 libg rwxp 段" —— 不准确。校验就在 libsupercell(sub_5533DC + sub_452500),只是藏在三层派发(sub_6826D0 → sub_18E8B0 → sub_5533DC)后面,且算法是 XXH64 不是 CRC。
第 10 轮确认 sub_5533DC 路径是 sub_2ADF90 + exit_group(1),没有重加密。但 sub_2ADF90 有 37 个 caller,且用户动态观察到过"重新加密代码段"。两条线查重加密到底在哪:
线索:pointers.txt 显示 qword_6C7C10/18/20 的引用全部集中在 0x2AD8F0-0x2AD9DC(sub_2ADF90 之前),说明生产者和消费者都在 0x2ADxxx 区域。0x2AD948/974/9C4/9DC 这些地址读 qword_6C7C18(头指针) 和 qword_6C7C20(计数) —— 这是遍历/消费特征。这些地址都落在 sub_2AD8BC(0x2AD8BC-0x2ADAE4)内部。
sub_5D34BC (RVA 0x5D34BC) —— 代表性 caller(37 个之一):
sub_2AD8BC (RVA 0x2AD8BC) —— 链表消费者,__noreturn,caller = sub_550E54(创建解密线程那个函数!):
libsupercell 里根本没有"重加密代码段"的执行路径。
检测响应链统一是:
qword_6C7C10 是检测事件记录表(供 crash report / forensic),不是重加密队列。没有任何函数从 qword_6C7C10 取节点做 ChaCha20 重加密。
那用户观察到的"重新加密代码段"是什么? 三种可能:
最可能是第 1 种(误判)。按需解密模型下,代码段平时加密存储,首次执行时解密成明文并保持。不存在"解密→执行→重加密"的循环。观察到的"代码段变化"是首次解密,不是重加密。
修正第 10 轮的说法:之前推测"sub_2ADF90 提交重加密任务"完全错误。准确说 —— sub_2ADF90 是检测事件入队,sub_2AD8BC 是事件聚合统计,两者都写 qword_6C7C10 记录表,无消费者做重加密。检测失败的最终响应就是 exit_group(1)。
Shield kill 走 SVC 0x80(直接系统调用),不经过 libc PLT。汇编解码确认是 exit_group(1)(X8=0x5E=94,X0=1),且 syscall 号通过常量混淆(0xE5113923 ^ 0xE511397D = 0x5E)动态算出。所以 hook libc 的 tgkill / exit / exit_group 完全拦不到,连 syscall 层 hook 都要小心处理常量混淆。
qword_6C7C10 是检测事件记录表(不是重加密队列):sub_2ADF90(37 处检测点)和 sub_2AD8BC(聚合器)都向它写,供 crash report / forensic 用,无消费者做重加密。libsupercell 里没有"解密→执行→重加密"循环 —— 代码段首次解密后保持明文。
最干净:patch sub_5533DC 的检测分支条件,让 if (标记有效 && XXH64 != stored && 标记非0) 永远 false。需 IDA 定位 RVA 0x5533DC 处对应的条件跳转指令(B.NE / CBNZ),改成 B(跳过 kill 块)或 NOP 掉比较+跳转。
| 用户洞察 |
代码证据 |
| "stub 持有含目标函数绝对地址的对象" |
off_6A6F98 函数地址表 ✅ |
| "全局统一的调用派发函数" |
sub_5E244C ✅ |
| "在合适时机 hook 解密的函数" |
sub_551110 (请求) / sub_5512A4 (执行) ✅ |
| "周期性轮询"(我脑补) |
错误,实际是按需解密 ✅ |
| 候选 |
实际功能 |
| sub_18D17C |
crc32(但只用于 BIO 文件读取流式校验,非代码段) |
| sub_18D6F8 |
std::string 析构 |
| sub_18D7F0 |
临时 string 构造+析构 |
| sub_18D930 |
std::string::append (memcpy) |
| sub_20D574 |
std::string::replace |
| sub_47981C |
Base64 编码 |
| sub_62F0C8 |
TLS 缓冲区管理 |
| sub_550D6C |
write() 封装 |
| v7 |
api_id |
值(指向 entry) |
| 0 |
4 |
0x6CD128 |
| 1 |
5 |
0x6CD1A8 |
| 2 |
6 |
0x6CD0C8 |
| 3 |
7 |
0x6CD0E8 |
| 4 |
8 |
0x6CD108 |
| 7 |
11 |
0x6CD168 |
| 表 |
角色 |
内容 |
off_6A6F98 |
函数派发表(sub_5E244C 用) |
entry 槽 → 真实函数指针(运行时解密) |
off_6ACCE8 |
校验目标表(sub_5533DC 用) |
代码段 RVA → 被校验的 152 字节起点 |
| 组件 |
RVA |
真身 |
| 校验算法 |
sub_452500 |
XXH64(PRIME1-5 常量铁证) |
| 校验+派发 |
sub_5533DC |
比较 XXH64 → 不等 kill |
| 重加密提交 |
sub_2ADF90 |
挂链表 qword_6C7C10,37 处调用 |
| kill |
SVC 0x80 |
直接 syscall(绕过 libc) |
| 受保护代码表 |
off_6ACCE8 |
代码段 RVA 列表(0x642BF8 等),每段 0x98 字节被校验 |
| 校验点 |
位置 |
类型 |
作用 |
| A |
sub_5E244C *result != 1 |
1 字节状态标记 |
判断代码页是否已解密,未解密则触发 ChaCha20 解密 |
| B |
sub_5533DC XXH64 != stored |
64 位 XXH64 |
完整性校验,每次派发都走,不等则 kill |
| 线程 |
RVA |
职责 |
| sub_550F20 |
0x550F20 |
按需解密 —— cond_wait(dword_6BEE44) 等请求,ChaCha20 解密代码页 |
| sub_2AD8BC |
0x2AD8BC |
事件聚合 —— cond_wait(unk_6C7BA0) 等事件,聚合统计到 qword_6C7C10 |
| 符号 |
RVA |
角色 |
| sub_5E244C |
0x5E244C |
全局派发咽喉 |
| sub_551110 |
0x551110 |
解密请求提交(同步 RPC) |
| sub_550F20 |
0x550F20 |
解密线程主循环 |
| sub_550E54 |
0x550E54 |
创建解密线程 |
| sub_5512A4 |
0x5512A4 |
单页解密入口 |
| sub_249D94 |
0x249D94 |
ChaCha20 core |
| sub_6826D0 |
0x6826D0 |
thunk → GOT → sub_18E8B0 |
| sub_18E8B0 |
0x18E8B0 |
第二级派发 |
| sub_5533DC |
0x5533DC |
校验+kill 核心 |
| sub_452500 |
0x452500 |
XXH64 校验算法 |
| sub_2ADF90 |
0x2ADF90 |
检测事件入队(37 处检测点写 qword_6C7C10) |
| sub_2AD8BC |
0x2AD8BC |
事件聚合线程(第二常驻线程,sub_550E54 启动) |
| off_6A6F98 |
0x6A6F98 |
受保护函数地址表(entry 加密存储) |
| off_6ACCE8 |
0x6ACCE8 |
受保护代码段地址表(每段 0x98 字节被 XXH64) |
| dword_6BEE44 |
0x6BEE44 |
解密请求标志 |
| dword_6BEE40 |
0x6BEE40 |
请求结构起点(1048B) |
__int64 sub_5E1AB8(_QWORD *a1, int *a2, __int64 a3, unsigned __int64 n7, signed int n2) {
...
if (n2 <= 1) {
if (!n2) { ... return; }
if (n2 != 1) return sub_5614E8(...);
v11 = sub_5215A8((__int64)a1, a3, n7);
if ((v11 & 0x8000000000000000LL) == 0) {
...
do {
...
LODWORD(v15) = crc32(v15, a3 + v14, (unsigned int)v16);
...
} while (v14 < v10);
...
}
}
if (n2 == 7) return *((_QWORD *)a2 + 4);
if (n2 == 8) { ... return v9 | ...; }
...
}
_QWORD *sub_21A69C(__int64 a1, __int64 (__fastcall *a2)(...), __int64 a3, __int64 a4) {
v11[0] = sub_59ECE0(a1);
v8 = a2(a1, a3, v11, 8, 8);
...
result = (_QWORD *)sub_19492C(a4);
if (result) {
*result = a1;
result[1] = a2;
result[2] = a3;
result[5] = v9;
}
return result;
}
__int64 sub_45129C(_QWORD *a1) {
*a1 = off_692D10;
return sub_324B20((__int64)(a1 + 2));
}
void nullsub_103() { ; }
void __noreturn sub_612A7C() {
sub_612DAC("Pure virtual function called!");
}
__int64 sub_249D94(_OWORD *a1, _OWORD *a2, unsigned __int64 n0x3F_1, int *a4, __int64 a5, unsigned int a6) {
...
int n1797285236;
int n2036477234;
int n857760878;
int n1634760805;
...
}
__int64 sub_24A228(_OWORD *a1, _OWORD *a2, unsigned __int64 n0x3F, __int64 a4, __int64 a5) {
if (*(_QWORD *)(a4 + 16) != 8 || *(_QWORD *)(a5 + 16) != 32)
return 0;
v5 = *(int **)a4;
if (!v5) { v5 = &byte_6C6540; byte_6C6540 = 0; }
v6 = *(char **)a5;
if (!v6) { v6 = &byte_6C6540; byte_6C6540 = 0; }
return sub_249D94(a1, a2, n0x3F, v5, (__int64)v6, 0x14u);
}
__int64 sub_551630(__int64 a1, __int64 x1_0) {
...
sub_240BF8(*(_QWORD *)a1, a2);
sub_19C758(25, v5);
sub_5A5868(&n4, a2, v5);
...
sub_24A228(*(_OWORD **)(a1 + 4136), *(_OWORD **)(a1 + 4136),
a1 - *(_QWORD *)(a1 + 4136) + 4128, x1_0, (__int64)v8);
return sub_19BF40((__int64)v8);
}
__int64 sub_551750(__int64 a1, __int64 a2) {
...
char n20;
unsigned __int64 n0xFFF;
__int64 n4096;
...
__int64 n32;
...
unsigned __int64 n0x1000;
unsigned __int64 n0xFFF_1;
__int64 n4096_1;
...
}
__int64 sub_5512A4(__int64 *a1) {
...
char v25[4112];
...
sub_682420(v25, 0, 4112);
v3 = sub_551750(v22, &v15);
n1274593961 = *off_6AF320;
...
}
void __noreturn sub_550F20() {
while (1) {
sub_682420(v4, 0, 1040);
for (i = 0; i != 1024; i += 16) {
v1 = &v4[i];
*((_DWORD *)v1 + 4) = 2;
*((_QWORD *)v1 + 3) = 0;
}
v2 = &unk_6CBA88;
v3 = 1;
sub_5ECFEC(&unk_6CBA88);
while (!dword_6BEE44)
sub_5ECA08(&unk_6CBA58, &v2);
sub_682330(v4, &dword_6BEE40, 1048);
if (v3 == 1) sub_5ED040(v2);
sub_2ADB1C();
sub_5512A4(v4);
sub_5EC9E8(&unk_6CBAB0);
}
}
__int64 sub_5ECA08(pthread_cond_t *cond, __int64 a2) {
if (*(_BYTE *)(a2 + 8) != 1) {
... "condition_variable::wait: mutex not locked"
}
result = pthread_cond_wait(cond, *(pthread_mutex_t **)a2);
...
}
__int64 sub_550E54(int a1, __int64 a2, __int64 a3) {
dword_6BF258 = a1;
qword_6CBB08 = a2;
sub_551EEC(&unk_6CBB10, a3);
sub_551004(v4, sub_550F20);
sub_5ED1DC(v4);
sub_5ED170(v4);
...
}
_BYTE *sub_5E244C(unsigned int a1, __int64 a2, __int64 a3) {
result = (_BYTE *)sub_62F0C8(&unk_6C3348);
if (*result != 1) {
sub_551110(a1, a2);
goto LABEL_11;
}
v7 = a1 - 4;
if (a1 - 4 >= 0xD || ((0x129Fu >> v7) & 1) == 0)
LABEL_11:
sub_6826D0(1);
v8 = (__int64)*(&off_6A6F98 + v7);
v9 = *(__int64 (__fastcall **)(_QWORD))(v8 + 8);
if ((*(_BYTE *)v8 & 4) != 0)
return (_BYTE *)v9(a1, a2, a3);
if (v9 != 1) {
if (v9) return (_BYTE *)v9(a1);
...
}
return result;
}
__int64 sub_551110(int a1, __int64 a2) {
v4 = sub_5ECFEC(&unk_6CBA88);
v5 = sub_682410(v4);
dword_6BEE40 = v5;
dword_6BEE44 = a1;
qword_6BEE48 = a2;
do {
n1024 -= 16;
*((_DWORD *)v7 - 2) = 2;
*v7 = 0;
v7 += 2;
} while (n1024);
qword_6BF250 = 0;
sub_439028((__int64)&unk_6BEE50);
sub_5ED040(&unk_6CBA88);
sub_5EC9E8(&unk_6CBA58);
sub_5ECFEC(&unk_6CBAE0);
result = sub_5ECA08(&unk_6CBAB0, &v9);
...
}
__int64 sub_62F0C8(_QWORD *a1) {
v2 = atomic_load(a1 + 2);
if (!v2) {
sub_1E9BE4(dword_6D2C08, sub_62F2F0);
pthread_mutex_lock(&mutex__8);
v2 = a1[2];
if (!v2) {
v2 = ++qword_6D2C10;
atomic_store(qword_6D2C10, a1 + 2);
}
pthread_mutex_unlock(&mutex__8);
pointer_2 = pthread_getspecific(key_3);
...
pointer[1] = v6;
pthread_setspecific(key, pointer);
}
...
*pointer = 1;
return result;
}
LODWORD(v19[0]) = v6 & 0xFF00FFFF;
memset(v14, 0, sizeof(v14));
sub_18D7F0();
sub_18D930(v14, 0, (char *)v19, 0);
...
sub_18D930(&v11, 0, v25, v7);
sub_47981C((__int64)&v11, v19);
sub_20D574(v14, 1u, _1, v19[2]);
sub_18D6F8(v19);
sub_18D6F8(&v11);
if (dword_6BF258 != -1) {
if ((char *)sub_550D6C(dword_6BF258, (__int64)v14) != &byte_88020) {
v10 = (_QWORD *)sub_6826D0(1);
sub_18D6F8(v14);
sub_33BC80(v10);
}
sub_682560((unsigned int)dword_6BF258);
sub_6824D0(10);
}
return sub_18D6F8(v14);
__int64 sub_18D6F8(_QWORD *a1) {
v1 = *a1;
result = sub_18D784(*a1, a1[2]);
if (v1) result = sub_62D93C(v1);
*a1 = 0; a1[1] = 0; a1[2] = 0;
return result;
}
__int64 *sub_18D930(__int64 *result, unsigned __int64 a2, char *a3, unsigned __int64 a4) {
v4 = *result;
if (*result) {
v5 = result[1];
v7 = v5 - a2;
if (v5 >= a2) {
v8 = (char *)(v4 + a2);
if (a4 < v7) v7 = a4;
if (a3 >= v8 || &a3[v7] <= v8) {
do { *v8++ = *a3++; --v11; } while (v11);
} else {
do { *(_BYTE *)(a2 + v4 - 1 + v9) = a3[v9 - 1]; --v9; } while (!v10);
}
if (result[2] < v7 + a2) return sub_18D9BC();
}
}
return result;
}
__int64 sub_18D7F0() {
_QWORD v1[4];
v1[3] = *(_QWORD *)(_ReadStatusReg(TPIDR_EL0) + 40);
sub_18D844(v1);
return sub_18D6F8(v1);
}
__int64 sub_550D6C(unsigned int a1, __int64 a2) {
_1 = *(char **)a2;
if (!*(_QWORD *)a2) { _1 = 1; ... }
v4 = *(_QWORD *)(a2 + 16);
if (!v4) return n4;
n4 = n4_0;
do {
v7 = sub_682490(a1, _1, v4);
if (v7 < 1 || v4 < v7) {
if (!v7) return n4;
n11 = *(_DWORD *)_errno(v7, v8);
if (n11 != 11 && n11 != 4) return n4;
} else {
_1 += v7; v4 -= v7;
}
} while (v4);
return n4_1;
}
__int64 *sub_47981C(__int64 a1, __int64 *a2) {
n3 = *(_QWORD *)(a1 + 16);
v5 = n3 / 3;
n3_2 = 3 * (n3 / 3);
v27 = n3 % 3;
if (n3 % 3) v7 = v5 + 1;
else v7 = n3 / 3;
result = sub_220388(4 * v7, a2);
...
do {
v15 = ((unsigned __int8)_1[3 * v11] << 16)
| ((unsigned __int8)_1[3 * v11 + 1] << 8)
| (unsigned __int8)_1[3 * v11 + 2];
do {
result = sub_479CF4((v15 >> (n24 - 6)) & 0x3F);
n24 -= 6;
*_1_3++ = (char)result;
} while (n24);
++v11; _1_2 += 4;
} while (v11 != v5);
...
return sub_682420(&v16[n3_1], 61, 3 - v27);
}
__int64 *sub_20D574(__int64 *a1, unsigned __int64 a2, char *a3, unsigned __int64 a4) {
v5 = a1[2];
if (a2 <= v5) {
...
sub_18DAD8(a1, n0x7F, v14);
v12 = a1[2];
if (a1[1] - v12 < v8) v8 = a1[1] - v12;
sub_18D930(a1, v8 + a2, (char *)(*a1 + a2), v12 - a2);
sub_18D930(a1, a2, a3, v8);
sub_18D6F8(v14);
}
return a1;
}
0x6A6F90 | 68 2D 5E 00 00 00 00 00 28 D1 6C 00 00 00 00 00 (表前一项)
0x6A6FA0 | A8 D1 6C 00 00 00 00 00 C8 D0 6C 00 00 00 00 00
0x6A6FB0 | E8 D0 6C 00 00 00 00 00 08 D1 6C 00 00 00 00 00
0x6A6FC0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x6A6FD0 | 68 D1 6C 00 00 00 00 00 ...
0x6CD0C8 | FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
0x6CD0D8 | FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
... (全部 0xFF)
0x6CD1FF | FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
__int64 sub_6826D0() {
return qword_6C5288();
}
0x6C5288 | B0 88 DE 97 6F 00 00 00 → 0x6F97DE88B0
__int64 sub_18E8B0(unsigned int a1) {
v2 = sub_553218();
v3 = sub_5533DC(v2, 4, off_6ACCE8[0]);
return v3(a1);
}
0x6ACCE0 | 64 42 63 00 00 00 00 00 F8 2B 64 00 00 00 00 00
0x6ACCF0 | 78 AA 65 00 00 00 00 00 8C 41 63 00 00 00 00 00
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!