零、题目

1.【填空题 15分】 分析倩倩手机逆向包,数据加密app的包名是什么?【答案格式:com.komeiji.satori】
2. 【填空题 10分】 接上题,初始化app时需要至少几位数的密码?【答案格式:10】
3. 【填空题 5分】 接上题,加密后的文件名的后缀是什么?【答案格式:.enc】
4. 【填空题 5分】 接上题,app会自动识别几种后缀的文件为图片类型?【答案格式:8】
5. 【填空题 5分】 接上题,app共从用于自定义加密的so模块导入了几个方法?【答案格式:8】
6. 【填空题 5分】 接上题,app设置的密码是多少?【答案格式:514aa11a4191a98】
7. 【填空题 5分】 接上题,app中存储的门锁密码是多少?【答案格式:5141141919810】
8. 【填空题 5分】 接上题,加密图片里面的隐藏的flag是多少?【答案格式:flag{123456!}】

一、压缩包内容

在这里插入图片描述

二、建立工作目录

在桌面新建一个目录,例如:

D:\qianqian_reverse

把文件放进去:

D:\qianqian_reverse\倩倩手机逆向包.rar

然后新建几个文件夹:

D:\qianqian_reverse\01_unpack
D:\qianqian_reverse\02_app
D:\qianqian_reverse\03_hap
D:\qianqian_reverse\04_phone_files
D:\qianqian_reverse\05_decrypted
D:\qianqian_reverse\scripts

目录含义:

01_unpack 放 rar 解压后的内容
02_app 放 .app 解包后的内容
03_hap 放 .hap 解包后的内容
04_phone_files 放手机检材中提取出来的 app 数据文件
05_decrypted 放解密后的文件
scripts 放 Python 脚本

三、解压逆向包

得到的fpt-default-signed.app放到:

\qianqian_reverse\01_unpack\

在这里插入图片描述

四、识别 .app 文件

重命名为.zip
在这里插入图片描述
解开:
在这里插入图片描述
重点文件是:

entry-default.hap

五、继续解 .hap

.hap 也是压缩包。
复制到:

\qianqian_reverse\03_hap\

在这里插入图片描述

继续解压。
用 7-Zip 解压。
在这里插入图片描述
得到

entry-default
├── module.json
├── resources.index
├── ets
│   └── modules.abc
├── libs
│   └── arm64-v8a
│       ├── libcrypto.so
│       └── libc++_shared.so
└── resources

在这里插入图片描述
这几个是重点:

module.json
ets\modules.abc
libs\arm64-v8a\libcrypto.so

module.json 是鸿蒙应用的模块配置文件,相当于安卓的 AndroidManifest.xml。

ets/modules.abc 是鸿蒙 ArkTS 编译后的字节码文件,简单的说,就是这个 APP 的所有业务代码。

libs/arm64-v8a/libcrypto.so 是OpenSSL 加密库,鸿蒙 / 安卓底层都用它。

六、找数据加密 app 的包名

打开:

\qianqian_reverse\03_hap\module.json

搜索:

bundleName

bundleName 是鸿蒙(HarmonyOS)应用的唯一标识包名。
在这里插入图片描述
所以第一题答案是:

com.koishi.fpt

七、提取 app 字符串

接下来分析:

ets\modules.abc

刚刚说了这个是鸿蒙 ArkTS 编译后的字节码文件。读者不需要完全反编译,先提取字符串就够用。

接下来我介绍两种方法。
方法 1:用 Linux / WSL / Git Bash

strings ets/modules.abc > modules_strings.txt

在这里插入图片描述
得到:
在这里插入图片描述
方法 2:Windows PowerShell
如果你有 Sysinternals 的 strings.exe,执行:

strings.exe .\ets\modules.abc > modules_strings.txt

在这里插入图片描述
两种方法都可以。

八、找初始化密码至少几位

然后在 modules_strings.txt 中没找到。
这不是说这个方式是没用的,这是一种探索题目的过程。
那接下来应该怎么做呢?
笔者依旧给出两种方法。
方法1:写一个从二进制文件里提取并查找 UTF-8 编码的中文字符串的脚本

from pathlib import Path
# 读取二进制文件
b = Path('modules.abc').read_bytes()

# 查找指定中文
for word in ['密码','至少']:
    bs = word.encode()
    print(word,b.find(bs))

# 正则匹配提取连续的可打印字符加中文 UTF - 8 序列(长度大于4)
import re
matches = re.findall(
    rb'(?:[\x09\x0a\x0d\x20-\x7e]|[\xe4-\xe9][\x80-\xbf]{2}){4,}',
    b
)

# 过滤包含中文的字符
cjk = []
for m in matches:
    try:
        t = m.decode('utf-8')
        if any ('\u4e00' <= ch <= '\u9fff' for ch in t):
            cjk.append(t)
    except:
        pass # 解码失败就直接跳过
    
# 结果
print('cjk count',len(cjk))
for t in cjk[:200]:
    print(repr(t[:200])) # repr 方便查看不可见字符

在这里插入图片描述
结果6位出来了。
方法二:用工具abc-decompiler打开modules.abc,然后在AuthPage里面即可找到
AuthPage 就是认证页面 / 登录页面 / 密码验证页面的意思。
下载地址:https://github.com/ohos-decompiler/abc-decompiler
在这里插入图片描述

九、找加密后的文件后缀

在这里插入图片描述
可以知道是.tb

.tb 是鸿蒙页面编译后的二进制文件,存 UI 和字符串,也就是加密后的文件。
我们可以在modules_strings.txt中找到。
在这里插入图片描述
鸿蒙系统一般也就这种加密文件。
当然,反编译后也可以找到。
在这里插入图片描述

十、找 app 自动识别几种图片后缀

在:

modules_strings.txt

搜索这些关键词:

jpg
jpeg
png
gif
webp

直接搜jpg。
可以看到图片类型相关后缀:

jpg
jpeg
png
gif
webp

在这里插入图片描述
一共 5 种。
注意,这里附近可能还能看到视频后缀:

mp4
mov
avi
mkv

但题目问的是:

图片类型

所以只统计图片后缀。
依旧,反编译后也可以找到。
在这里插入图片描述

分析自定义加密 so 模块导入了几个方法

重点文件:

\qianqian_reverse\03_hap\libs\arm64-v8a\libcrypto.so

这个是 app 自定义的 native 加密模块。

strings libs/arm64-v8a/libcrypto.so > libcrypto_strings.txt

在这里插入图片描述
在这里插入图片描述

可能要AI去分析
在这里插入图片描述
或者在反编译中找:
在这里插入图片描述
这个 a b c d 文件夹是原始包名被混淆后的结果,不具备实际语义。
在 d 文件中发现 q1 和 s1 两个方法从 libcrypto.so 中导入了方法。
不过ChatGPT 5.5给的解释是

rot13
xorEncrypt

这两个方法。
答案都是2。

十二、从手机检材中找到 app 数据

需要找到这个 app 的数据目录。包名是:

com.koishi.fpt

所以在手机检材中搜索:

com.koishi.fpt

重点找这些文件:

vault_prefs
*.tb

你需要把找到的文件复制到:

D:\qianqian_reverse\04_phone_files\

最终这个目录中应该有类似文件:

04_phone_files
├── vault_prefs
├── 139346145_p0.png.tb
├── Snipaste_2026-04-03_10-56-53.jpg.tb
└── b88c3348-9389-4fad-8cd6-3490e6bbdd26.json.tb

其中:

vault_prefs

是 app 保存密码信息的偏好配置文件。

几个 .tb 文件是被加密后的文件。
在这里插入图片描述

十三、分析 app 设置的密码

打开:

04_phone_files\vault_prefs

在这里插入图片描述
可以看到 XML 内容:

<string key="password_hash">217sr94q01679u39</string>
<string key="salt">yqWpy+rJX82gRZuCjoB16w==</string>

关键字段是:

password_hash = 217sr94q01679u39

但是这个不是最终明文密码。

因为 libcrypto.so 里有一个方法:

rot13

所以要对它做 ROT13。

怎么确定是rot13的呢?
在这里插入图片描述
到AuthPage类。
在这里插入图片描述

e1.w1(this.password)的意思是密码做哈希。
e12.v1()的意思是生成随机盐。
在这里插入图片描述
登录时调用 e1.z1 进行验证。

我们接着跟进w1和z1。
在这里找:
在这里插入图片描述
找到:
加粗样式
w1(密码) 就是调用 q1 (密码)。
z1验证密码就是把输入的密码再加密一遍,和本地保存的 hash 对比。
那跟进去q1。
在这里插入图片描述
打开so文件。
Shift+F12打开字符串窗口。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
追踪5D58函数。
在这里插入图片描述
这段是鸿蒙 NAPI 注册代码。
在这里插入图片描述
v4 是一个方法列表。
定位D8F0。
在这里插入图片描述
在这里插入图片描述
接下来定位5DE0。
在这里插入图片描述
这里我们细致的分析:

以小写字母为例(因为题目里面密码就是小写字母):

v11 = 97;  // a
v12 = 175;

计算:

v12 + v13 = 175 + 97 = 272

因为代码里面有:

((v12 + v13) & 0xFFu)

所以取低 8 位:

272 & 0xFF = 16 //(其实就是 272 - 256 = 16)

然后:

79 * 16 >> 11 = 1264 >> 11 = 0

所以最终:

新字符 = 175 + 97 - 26 * 0 + 97
       = 369

但赋值给 char,只保留低 8 位:

369 & 0xFF = 113

ASCII 113 是:q
所以:

a -> q

a 往后数 16 位就是 q。
发现不是ROT13,而是ROT16。
在这里插入图片描述
这里为什么是10而不是16,是因为CyberChef用正向参数表示,我们做的是还原,反向前移16位,就是正向后移10位。
答案:

217cb94a01679e39

十四、分析 .tb 文件的加密算法

这里开始要分析xorEncrypt了,因为图片是xor异或加密的。
在这里插入图片描述
在603C里面。
在这里插入图片描述
这么一大段。

v9   = 输入数据 input
v13  = 输出数据 out
v11  = 输入长度 input_len
v25  = key 字符串
v22  = key 长度 key_len
v24  = 当前下标 i

其中:

v24 - v24 / v22 * v22

就是:

v24 % v22

(这里是要训练的表达式: a - a / b * b等价于a % b )

所以核心代码可以改写成:

out[i] = (key[i % key_len] + (i % key_len)) ^ input[i];

更自然地写就是:

out[i] = input[i] ^ (key[i % key_len] + (i % key_len));

文字表达就是:

输出字节 = 原始字节 XOR (key[i % key长度] + i % key长度)

也就是:

out[i] = data[i] ^ (key[i % n] + (i % n))

因为 XOR 加密和解密是同一个过程,所以用同样算法可以解密 .tb 文件。

这里的 key 使用 app 存储的值:

217sr94q01679u39

不是 ROT13 后的明文密码。

十五、写解密脚本

在:

D:\qianqian_reverse\scripts\

新建:

decrypt_tb.py  //脚本借助大模型写的
from pathlib import Path

# 自动获取当前脚本所在目录(不用手动改路径)
BASE = Path(__file__).parent.parent  # 脚本在 scripts 里,自动定位到上级 qianqian_reverse

# 手机检材中提取出来的加密文件目录
IN_DIR = BASE / "04_phone_files"

# 解密输出目录
OUT_DIR = BASE / "05_decrypted"
OUT_DIR.mkdir(exist_ok=True, parents=True)  # parents=True 自动创建多级目录

# 注意:这里使用 vault_prefs 里保存的 password_hash
KEY = "217sr94q01679u39"


def xor_crypt(data: bytes, key: str) -> bytes:
    key_bytes = key.encode()
    key_len = len(key_bytes)

    result = bytearray()

    for i, b in enumerate(data):
        k = key_bytes[i % key_len]
        offset = i % key_len
        result.append(b ^ ((k + offset) & 0xff))

    return bytes(result)


def decrypt_file(path: Path):
    data = path.read_bytes()
    dec = xor_crypt(data, KEY)

    # 去掉 .tb 后缀
    if path.name.endswith(".tb"):
        out_name = path.name[:-3]
    else:
        out_name = path.name + ".dec"

    out_path = OUT_DIR / out_name
    out_path.write_bytes(dec)

    print(f"[+] {path.name} -> {out_name}")


def main():
    tb_files = list(IN_DIR.glob("*.tb"))

    if not tb_files:
        print("[-] 没有找到 .tb 文件,请检查 04_phone_files 目录")
        return

    for file in tb_files:
        decrypt_file(file)

    print("\n[+] 解密完成!文件保存在 05_decrypted 文件夹中")


if __name__ == "__main__":
    main()

在这里插入图片描述

在这里插入图片描述

十六、找门锁密码

打开解密后的 JSON 文件:

05_decrypted\b88c3348-9389-4fad-8cd6-3490e6bbdd26.json

在这里插入图片描述

十七、找推荐的游戏叫什么

打开解密后的图片:

05_decrypted\Snipaste_2026-04-03_10-56-53.jpg

这是一张聊天截图。
在这里插入图片描述

里面有一句话,大意是:

我刷打了半天另一款平面射击类类银河恶魔城游戏呢,叫 zero sievert

所以推荐的游戏叫:

ZERO Sievert

十八、找加密图片中的隐藏 flag

打开解密后的图片:

05_decrypted\139346145_p0.png

在这里插入图片描述

表面上是一张图片。

但题目说:

加密图片里面隐藏的 flag

在这里插入图片描述
010直接打开,翻到尾部就找到了,当然也可以用用 strings 搜索 flag。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐