【ROOT Android】Steam 3.0 导出令牌的数种方法
【ROOT Android】Steam 3.0 导出令牌的数种方法
在 Steam 更新 3.0 版本之后,原本 2.X 版本下导出令牌的方法已经不再可用。于是我花了一点时间逆向 + Patch,总结出了数个在 Steam 3.0 版本下导出令牌的方法。当然,这些方法的所有前提都是:拥有一台已经 ROOT 过的设备。
0x01 什么被改变了
与 2.X 版本最大的不同是,Steam 3.0 版本使用了 React Native 作为框架来编写,这让我们的逆向分析困难了不少。React 打包之后的 index.android.bundle 是晦涩难懂的,对于程序流的分析花费了我大量的时间。
就令牌这一方面而言,结论是这样的:
令牌被序列化成类似下文的结构,
{ "accounts": { "<your_steam_64_id>": { "steamguard_scheme": "2", "uri": "<...>", "status": 1, "identity_secret": "<...>", "revocation_code": "<...>", "token_gid": "<...>", "account_name": "<...>", "serial_number": "<...>", "server_time": "<...>", "secret_1": "<...>", "steamid": "<your_steam_64_id>", "phone_number_hint": "<last_4_numbers_of_your_phone_number>", "shared_secret": "<...>" }, "<another_steam_64_id>": { "steamguard_scheme": "2", "uri": "<...>", "status": 1, "identity_secret": "<...>", "revocation_code": "<...>", "token_gid": "<...>", "account_name": "<...>", "serial_number": "<...>", "server_time": "<...>", "secret_1": "<...>", "steamid": "<another_steam_64_id>", "phone_number_hint": "<last_4_numbers_of_your_phone_number>", "shared_secret": "<...>" }, ... }}并通过 android.KeyStone 的相关方法,进行 AES 加密后储存于 /data/data/com.valvesoftware.android.steam.community/shared_prefs/SecureStore.xml 的 <string name="SteamGuard_1"> 标签下。
这个标签的内容格式如下:
{ "ct": "BASE64_ENCODED_CONTENT_OF_AES_ENCRYPED_STEAM_GUARD_JSON", "iv": "BASE64_ENCODED_IV", "tlen": 128, "scheme": "aes"}由于 android.KeyStone 的安全性,想要通过逆向 SecretKey 并解密这些密文几乎是不可能的(至少我尝试了很久也没有做到),因此,我们不得不考虑其它的方法进行导出。
0x02 【推荐】通过 Frida Hook Cipher.doFinal 方法
这个方法的前提是:
- 按照 Doc 配置并运行 Android Frida Server,在 USB 调试模式 开启的条件下使用 USB 连接设备。
- 拥有一个安装了
frida模块的 Python 环境
只需运行下面的脚本,即可在标准输出中获取令牌。
import jsonimport fridaimport sys
package = "com.valvesoftware.android.steam.community"cmd = """'use strict;'
if (Java.available) { Java.perform(function() {
//Cipher stuff const Cipher = Java.use('javax.crypto.Cipher');
Cipher.doFinal.overload('[B').implementation = function (input) { var result = this.doFinal.overload('[B').call(this, input); send(result); }
})}"""
def parse_hook(cmd_): print('[*] Parsing hook...') script = session.create_script(cmd_) script.on('message', on_message) script.load()
def on_message(message, _): try: if message: if message['type'] == 'send': result = "".join(chr(i) for i in message['payload']) print(json.dumps(json.loads(result), indent=2, ensure_ascii=False)) except Exception as e: print(e)
if __name__ == '__main__': try: print('[*] Spawning ' + package) pid = frida.get_usb_device().spawn(package) session = frida.get_usb_device().attach(pid) parse_hook(cmd) frida.get_usb_device().resume(pid) print('') sys.stdin.read()
except KeyboardInterrupt: sys.exit(0) except Exception as e: print(e)这个方法是简单的,通过劫持 Cipher.doFinal 方法,将解密后的结果返回,这样做的好处是不需要侵入程序,且较为容易设置。
0x03 通过 Javascript 注入修改 React Native 代码
这个方法的前提是:
- 在 USB 调试模式 开启的条件下使用 USB 连接设备。
- 设备可以绕过签名冲突安装应用,关于这点,可以利用 Xposed 插件 CorePatch
步骤如下:
- 以压缩包格式打开
steam.apk,并将assests/index.android.bundle解压到任意位置。 - 使用诸如
vscode等的编辑器打开index.android.bundle - (Optional) 格式化代码以方便分析
- 搜索
key: "GetSteamGuardInfo",并在value对应的function内,在return上方加入下面的代码
this.m_mapGuardInfo.forEach(function (t, n) { console.error(n); console.error(JSON.stringify(t)); });修改完之后的这部分应该是这个样子的:
{ key: "GetSteamGuardInfo", value: function (t) { this.m_mapGuardInfo.forEach(function (t, n) { console.error(n); console.error(JSON.stringify(t)); }); return this.m_mapGuardInfo.has(t) ? this.m_mapGuardInfo.get(t) : null; }, },- 将修改完的
index.android.bundle复制回steam.apk内的assests/ - 使用
adb install -r steam.apk安装程序 - 在命令行内输入下面的代码
adb logcat *:S ReactNative:V ReactNativeJS:V\- 打开新安装的
steam.apk,切换到令牌页时,你应该能在命令行的标准输出内看到你的所有令牌了。
0x04 降级安装 Steam 2.X 生成令牌并导出后再次升级
一般来说,降级安装是不被允许的,关于这点,你也可以使用 Xposed 插件 CorePatch
这里值得一提的点是:在 /data/data/com.valvesoftware.android.steam.community/databases/RkStorage.db 这个数据库中,表中存在一个 bMigrated 键,用以确定是否需要对令牌进行 migration操作。
0x05 抓包
详情可以看 iOS 3.x新版Steam抓包导出令牌方法 - 免越狱免降级免备份
安卓版本的操作是大同小异的,可以通过搜索 “如何使用 Fiddler (或是其他抓包软件)在 Android 抓取 HTTPS 请求” 获取更多内容。
然而需要注意的是:新的安卓系统拥有一个称为证书锁定(ssl pinning)的机制,这让应用程序抓包十分困难。
我尝试了 Xposed 插件 JustTrustMe 以及 TrustMeAlready,但是它们都不起作用。最后,我的解决方法是使用
android-SSL-unpinning 重新打包 steam.apk才抓取到了请求。
0x06 总结
这篇文章的重点除了在探讨如何导出令牌外,也在于给予更多人逆向的思路,从而创建出更方便的工具(如:第三方扫码登陆平台),这个工作本身难度是很低的,但是却十分繁琐,如果这篇文章能帮各位省下一些前期工作的时间,那就再好不过了。