Skip to content
QQ安全中心 - 动态口令的生成算法
Python
Branch: master
Clone or download

Latest commit

Fetching latest commit…
Cannot retrieve the latest commit at this time.

Files

Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
README.md
decrypt-database.py
decrypt-secret.py
generate-qqtoken.py
requirements.txt

README.md

QQ安全中心 - 动态口令的生成算法

鉴于该App的服务群体是中文用户,故本篇文章不提供英文版。

I won't provide English version for this article. Because the app serves for QQ users which are Chinese mainly.

1. 动态口令的生成算法

动态口令的生成算法和RFC6238协议类似。

这里我们用 secret 代表动态口令的生成密钥,它一般是长度为32的octets,例如:

b'\xa4\x22\x99\xe1\xce\xd4\xe7\x05\x48\x43\x92\x58\x3a\xd0\xb7\x6a\x8d\x6e\x89\x3c\x2d\xd1\x98\xbc\x39\x9b\x4c\x9a\x4e\x34\x21\xeb'

算法的大致过程如下:

  1. 将当前北京时间向下对齐到30s,并按下面的格式格式化:

    yyyy-MM-dd HH:mm:ss
    

    得到一个长度为19的octets,记为 timestamp

    例如,若当前北京时间为 2020-02-12 13:02:46,则向下对齐到30s,得到 timestamp 为:

    b'2020-02-12 13:02:30'
  2. timestamp 添加到 secret 后面,并对新的octets做SHA256运算,得到长度为32的octets,记为 digest

    例如:

    # digest = hashlib.sha256(secret + timestamp).digest()
    digest = b'\x89\x60\xf9\xeb\x42\x67\x61\x0e\x74\xc0\x98\x59\x76\x88\xaf\x6e\x25\xfb\xf2\x6b\x6c\xe3\xbe\x95\x02\xcb\xed\xd8\x3e\x0b\xf9\x44'
  3. digest 的每个octet在原位分割成高四位和低四位,得到 digest_exp

    例如:

    digest_exp = [8, 9, 6, 0, 15, 9, 14, 11, 4, 2, 6, 7, 6, 1, 0, 14, 7, 4, 12, 0, 9, 8, 5, 9, 7, 6, 8, 8, 10, 15, 6, 14, 2, 5, 15, 11, 15, 2, 6, 11, 6, 12, 14, 3, 11, 14, 9, 5, 0, 2, 12, 11, 14, 13, 13, 8, 3, 14, 0, 11, 15, 9, 4, 4]
  4. 按照如下公式得到6位动态口令的每一位:

2. 如何获取secret

要求条件:

  1. 手机安装了“QQ安全中心”这个App,版本6.9.10或以上,并且在App里登录了QQ账号。

  2. 手机已经root。

secret 是存储在

/data/data/com.tencent.token/databases/mobiletoken.db

数据库文件中,该数据库是经过加密的。

并且在数据库中,secret 被加密保存,加密的密钥存在

/data/data/com.tencent.token/shared_prefs/token_save_info.xml

下面将会介绍如何提取 secret

  1. 确保“QQ安全中心”已经退出。

  2. 查看 /data/data/com.tencent.token/shared_prefs/token_save_info.xml 文件的内容。

    你可以用adb来查看:

    $ adb shell
    gemini:/ $ su   
    gemini:/ # cat /data/data/com.tencent.token/shared_prefs/token_save_info.xml                                                                             
    <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
    <map>
        <boolean name="token_status" value="true" />
        <string name="token_info">14C27B6EC74543302F02C01B50E16ED7</string>
        <int name="token_type" value="2" />
    </map>
    gemini:/ # 

    其中 14C27B6EC74543302F02C01B50E16ED7 就是加密 secret 的密钥。

    每次“QQ安全中心”退出,这个值都会变化。

  3. 提取 mobiletoken.db

    $ adb root
    restarting adbd as root
    $ adb pull /data/data/com.tencent.token/databases/mobiletoken.db
    /data/data/com.tencent.token/databases/mobiletoken.db: 1 file pulled. 0.9 MB/s (13312 bytes in 0.013s)
  4. 使用 decrypt-database.py 解密 mobiletoken.db

    Usage:
        ./decrypt-database.py <mobiletoken.db的路径>
    

    例如:

    $ ./decrypt-database.py ./mobiletoken.db
  5. 查看加密的 secret

    $ sqlite3 ./mobiletoken.db 
    SQLite version 3.28.0 2019-04-15 14:49:49
    Enter ".help" for usage hints.
    sqlite> select hex(data) from main.token_conf;
    AE28874351F682CFDD5263E3D71D74E7D8847F00D157923539811AD0920499B9A88A5B8021C1ED2E7B20BD597ADA33AE
    sqlite>

    其中 AE28874351F682CFDD5263E3D71D74E7D8847F00D157923539811AD0920499B9A88A5B8021C1ED2E7B20BD597ADA33AE 就是加密的 secret

  6. 解密出 secret

    Usage:
        ./decrypt-secret.py <加密的secret> <加密secret的密钥>
    

    例如:

    $ ./decrypt-secret.py AE28874351F682CFDD5263E3D71D74E7D8847F00D157923539811AD0920499B9A88A5B8021C1ED2E7B20BD597ADA33AE 14C27B6EC74543302F02C01B50E16ED7
    a42299e1ced4e705484392583ad0b76a8d6e893c2dd198bc399b4c9a4e3421eb

    其中 a42299e1ced4e705484392583ad0b76a8d6e893c2dd198bc399b4c9a4e3421eb 就是我们要找的 secret

3. 使用secret生成动态密码

Usage:
    ./generate-qqtoken.py <secret>

例如:

$ ./generate-qqtoken.py a42299e1ced4e705484392583ad0b76a8d6e893c2dd198bc399b4c9a4e3421eb
530800
You can’t perform that action at this time.