如何提取 React Native Android 应用中的 .spk 文件?
仅凭 FilenameBundle.js.hbc.spk 这个文件名,无法确定 .spk 的格式或生成工具,也无法给出可靠的通用解包命令。应先检查文件内容,并追踪应用读取该资源的代码,确认其中是否包含可直接使用的 Hermes 字节码,以及是否需要额外处理。
即使成功提取 HBC,也不能保证恢复原始 JavaScript 源码。反汇编和反编译可以帮助理解程序逻辑,但无法保证还原注释、原始变量名和项目结构。
为什么改名为 ZIP 无法解决
修改扩展名不会改变文件内部结构。文件头不匹配,目前没有足够依据把它当作普通 ZIP 处理。
文件名中的 .hbc 只是线索,不能证明解压 .xz 后得到的就是完整 HBC,也不能证明 .spk 一定是一层可单独解包的容器。额外压缩、加密、分段或自定义封装都是需要验证的可能性。
1. 查看解压后的实际内容
在安装了相应工具的分析环境中,先执行:
file 'FilenameBundle.js.hbc.spk'
xxd -l 128 'FilenameBundle.js.hbc.spk'
strings -a -n 8 'FilenameBundle.js.hbc.spk' | head -n 40
记录文件大小和开头的十六进制字节,留意是否存在格式标识或可读字符串。file 返回 data 只表示它未识别出格式;strings 没有输出也不能证明文件经过加密。
2. 检查是否存在普通 HBC 文件头
Hermes 的文件格式定义包含 magic、version 等字段,其中普通执行形式的 magic 值为 0x1F1903C103BC1FC6。具体结构应以目标版本为准。参见 Hermes 文件格式定义。
下面的 Python 示例搜索该 magic 的小端字节序表示,并读取其后四字节作为候选版本号:
from pathlib import Path
import struct
path = Path('FilenameBundle.js.hbc.spk')
data = path.read_bytes()
magic = bytes.fromhex('c6 1f bc 03 c1 03 19 1f')
found = False
start = 0
while True:
offset = data.find(magic, start)
if offset == -1:
break
found = True
if offset + 12 <= len(data):
version = struct.unpack_from('<I', data, offset + 8)[0]
print(f'Candidate HBC: offset=0x{offset:x}, version={version}')
else:
print(f'Magic found at 0x{offset:x}, but header is incomplete')
start = offset + 1
if not found:
print('No ordinary HBC magic found')
结果可以这样判断:
- 在偏移
0找到:可以尝试用支持该字节码版本的解析器读取整个文件,确认它是否已经是 HBC。 - 在非零偏移找到:可能存在前置数据或嵌入的 HBC,但仍需验证文件头、长度和内部偏移。不要直接删除前面的字节就认定提取成功。
- 没找到:只能说明没有发现这种形式的明文文件头,不能排除经过转换、压缩或其他处理的 HBC。
magic 命中只是识别线索,需要完整解析成功才能进一步确认文件结构。
3. 追踪应用如何加载 .spk
如果静态检查仍无法识别格式,最有价值的证据是应用自身的读取逻辑。
使用 JADX 等工具查看 DEX 中的代码,搜索完整文件名和 .spk、.hbc、.xz 等字符串。沿引用查找资源读取、解压、数据转换和 bundle 加载的调用顺序。这些只是搜索方向,不能据此认定应用使用了某个特定 SDK。
如果调用进入 JNI,需要继续检查相关 .so 中对应的处理逻辑,弄清资源读取后经过了什么变换,以及最终交给 Hermes 的数据来自文件还是内存缓冲区。
如果处理过程只能在运行时观察,可以在测试环境中捕获最后一次解封装或解密之后、交给 Hermes 之前的数据,并记录准确的缓冲区长度。具体位置需要根据应用的实际调用链确定,不能预设固定函数名。
识别生成工具时,也应沿这条路径寻找证据,例如加载器所属的包、库、格式常量,以及可取得的构建配置。仅凭 .spk 后缀无法可靠归因。
4. 提取 HBC 后再选择分析工具
得到完整 HBC 后,先确认文件头中的字节码版本,再选择明确支持该版本的反汇编或反编译工具。例如 hermes-dec 项目 提供相关能力,具体支持范围和使用方式应查看所用版本的说明。
解析失败时,要区分文件截取不完整、仍有外层处理、字节码版本不兼容等原因,不能直接认定文件已加密或损坏。
如果目标是取得尽可能接近原始的 JavaScript,还应检查是否有构建时保存的源码,或包含 sourcesContent 的 source map。反编译输出需要结合程序行为核对,不能直接视为原始源码。
继续定位需要哪些信息
目前缺少文件内容和加载器证据,无法确认具体 .spk 格式。继续排查时,最有用的材料是解压后的文件大小、前 64~128 字节的十六进制内容,以及读取该文件的代码片段。这些信息比扩展名更有助于判断下一步使用格式解析、静态分析还是运行时提取。
备注:内容仅供参考。
