├── .github
├── ISSUE_TEMPLATE.md
└── workflows
│ ├── build_docker_image.yml
│ ├── gradle-dev.yml
│ ├── gradle.yml
│ └── release.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── build.gradle.kts
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── libs
└── unidbg-android-1.0.7.jar
├── refresh_token
└── README.md
├── settings.gradle.kts
├── src
└── main
│ ├── java
│ ├── com
│ │ └── tencent
│ │ │ ├── crypt
│ │ │ └── Crypt.java
│ │ │ └── mobileqq
│ │ │ ├── dt
│ │ │ └── model
│ │ │ │ └── FEBound.java
│ │ │ └── qsec
│ │ │ └── qsecest
│ │ │ └── SelfBase64.java
│ └── project
│ │ └── BuildConfig.java
│ └── kotlin
│ ├── Main.kt
│ ├── com
│ └── tencent
│ │ ├── mobileqq
│ │ ├── channel
│ │ │ ├── ChannelManager.kt
│ │ │ └── SsoPacket.kt
│ │ ├── dt
│ │ │ └── Dtn.kt
│ │ ├── fe
│ │ │ └── FEKit.kt
│ │ ├── qsec
│ │ │ ├── qsecdandelionsdk
│ │ │ │ └── Dandelion.kt
│ │ │ └── qsecurity
│ │ │ │ ├── DeepSleepDetector.kt
│ │ │ │ └── QSec.kt
│ │ └── sign
│ │ │ └── QQSecuritySign.kt
│ │ └── secprotocol
│ │ └── ByteData.kt
│ └── moe
│ └── fuqiuluo
│ ├── api
│ ├── added_sign.kt
│ ├── energy.kt
│ ├── exceptions.kt
│ ├── main.kt
│ ├── qsign.kt
│ ├── register.kt
│ ├── request_token.kt
│ ├── session.kt
│ └── submit.kt
│ ├── comm
│ ├── ArgsParser.kt
│ └── QSignConfig.kt
│ ├── ext
│ ├── ByteArray.kt
│ ├── BytePacket.kt
│ ├── Ktor.kt
│ ├── Numbers.kt
│ ├── Types.kt
│ └── Unidbg.kt
│ ├── unidbg
│ ├── QSecVm.kt
│ ├── env
│ │ ├── FileResolver.kt
│ │ ├── QSecJni.kt
│ │ └── files
│ │ │ ├── cpuinfo.kt
│ │ │ ├── meminfo.kt
│ │ │ ├── stat.kt
│ │ │ └── status.kt
│ ├── session
│ │ ├── Session.kt
│ │ └── SessionManager.kt
│ └── vm
│ │ ├── AndroidVM.kt
│ │ └── GlobalData.kt
│ └── utils
│ ├── BytesUtil.kt
│ ├── MD5.kt
│ └── ZlibUtil.kt
└── txlib
├── 3.5.1
├── config.json
├── dtconfig.json
├── libfekit.so
├── libpoxy.so
└── libwtecdh.so
├── 3.5.2
├── config.json
├── dtconfig.json
├── libfekit.so
├── libpoxy.so
└── libwtecdh.so
├── 8.9.63
├── config.json
├── dtconfig.json
└── libfekit.so
├── 8.9.68
├── config.json
├── dtconfig.json
└── libfekit.so
├── 8.9.71
├── config.json
├── dtconfig.json
└── libfekit.so
├── 8.9.73
├── config.json
├── dtconfig.json
└── libfekit.so
└── lookme.txt
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## 问题咨询、讨论,请在 [Discussions](../discussions) 发帖。issues 仅用于提交 Bug。
--------------------------------------------------------------------------------
/.github/workflows/build_docker_image.yml:
--------------------------------------------------------------------------------
1 | name: Build And Push Docker Image
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'master'
7 | # Sequence of patterns matched against refs/tags
8 | tags:
9 | - v[0-9]+.*
10 |
11 | workflow_dispatch:
12 |
13 | jobs:
14 | build:
15 |
16 | runs-on: ubuntu-latest
17 |
18 | permissions:
19 | packages: write
20 | contents: read
21 |
22 | steps:
23 | - uses: actions/checkout@v3
24 |
25 | - name: Set time zone
26 | uses: szenius/set-timezone@v1.1
27 | with:
28 | timezoneLinux: "Asia/Shanghai"
29 | timezoneMacos: "Asia/Shanghai"
30 | timezoneWindows: "China Standard Time"
31 |
32 | # # 如果有 dockerhub 账户,可以在github的secrets中配置下面两个,然后取消下面注释的这几行,并在meta步骤的images增加一行 ${{ github.repository }}
33 | # - name: Login to DockerHub
34 | # uses: docker/login-action@v1
35 | # with:
36 | # username: ${{ secrets.DOCKERHUB_USERNAME }}
37 | # password: ${{ secrets.DOCKERHUB_TOKEN }}
38 |
39 | - name: Login to GHCR
40 | uses: docker/login-action@v2
41 | with:
42 | registry: ghcr.io
43 | username: ${{ github.repository_owner }}
44 | password: ${{ secrets.GITHUB_TOKEN }}
45 |
46 | - name: Extract metadata (tags, labels) for Docker
47 | id: meta
48 | uses: docker/metadata-action@v4
49 | with:
50 | images: |
51 | ghcr.io/${{ github.repository }}
52 | # generate Docker tags based on the following events/attributes
53 | # nightly, master, pr-2, 1.2.3, 1.2, 1
54 | tags: |
55 | type=schedule,pattern=nightly
56 | type=edge
57 | type=ref,event=branch
58 | type=ref,event=pr
59 | type=semver,pattern={{version}}
60 | type=semver,pattern={{major}}.{{minor}}
61 | type=semver,pattern={{major}}
62 |
63 | - name: Set up QEMU
64 | uses: docker/setup-qemu-action@v2
65 |
66 | - name: Set up Docker Buildx
67 | uses: docker/setup-buildx-action@v2
68 |
69 | - name: Build and push
70 | id: docker_build
71 | uses: docker/build-push-action@v4
72 | with:
73 | context: .
74 | push: ${{ github.event_name != 'pull_request' }}
75 | tags: ${{ steps.meta.outputs.tags }}
76 | labels: ${{ steps.meta.outputs.labels }}
77 | cache-from: type=gha
78 | cache-to: type=gha,mode=max
79 | platforms: linux/amd64,linux/arm64
80 |
--------------------------------------------------------------------------------
/.github/workflows/gradle-dev.yml:
--------------------------------------------------------------------------------
1 | name: Java CI with Gradle Dev
2 |
3 | on:
4 | push:
5 | branches:
6 | - dev
7 | paths-ignore:
8 | - '.github/**'
9 | - '.run/**'
10 | - '**.md'
11 | - '!.github/workflows/gradle.yml'
12 | pull_request:
13 | branches:
14 | - dev
15 | paths-ignore:
16 | - '.github/**'
17 | - '.run/**'
18 | - '**.md'
19 | - '!.github/workflows/gradle.yml'
20 |
21 | jobs:
22 | build:
23 | runs-on: ubuntu-latest
24 | steps:
25 | - name: Checkout code
26 | uses: actions/checkout@v3
27 |
28 | - name: Set up JDK 11
29 | uses: actions/setup-java@v3
30 | with:
31 | java-version: 11
32 | java-package: jdk
33 | distribution: 'temurin'
34 |
35 | - name: Build and Package
36 | run: |
37 | ./gradlew distZip
38 |
39 | - name: Upload Artifact
40 | uses: actions/upload-artifact@v3
41 | with:
42 | name: unidbg-fetch-qsign
43 | path: ${{ github.workspace }}/build/distributions/unidbg-fetch-qsign-*.zip
44 |
--------------------------------------------------------------------------------
/.github/workflows/gradle.yml:
--------------------------------------------------------------------------------
1 | name: Java CI with Gradle
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths-ignore:
8 | - '.github/**'
9 | - '.run/**'
10 | - '**.md'
11 | - '!.github/workflows/gradle.yml'
12 | pull_request:
13 | branches:
14 | - master
15 | paths-ignore:
16 | - '.github/**'
17 | - '.run/**'
18 | - '**.md'
19 | - '!.github/workflows/gradle.yml'
20 |
21 | jobs:
22 | build:
23 | runs-on: ubuntu-latest
24 | steps:
25 | - name: Checkout code
26 | uses: actions/checkout@v3
27 |
28 | - name: Set up JDK 11
29 | uses: actions/setup-java@v3
30 | with:
31 | java-version: 11
32 | java-package: jdk
33 | distribution: 'temurin'
34 |
35 | - name: Build and Package
36 | run: |
37 | ./gradlew distZip
38 |
39 | - name: Upload Artifact
40 | uses: actions/upload-artifact@v3
41 | with:
42 | name: unidbg-fetch-qsign
43 | path: ${{ github.workspace }}/build/distributions/unidbg-fetch-qsign-*.zip
44 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - v[0-9]+.*
7 |
8 | jobs:
9 | build:
10 | name: Create Release
11 | runs-on: ubuntu-latest
12 | env:
13 | RELEASE_VERSION: ${{ github.ref }}
14 | steps:
15 | - name: Checkout code
16 | uses: actions/checkout@v3
17 | - name: Set up JDK 11
18 | uses: actions/setup-java@v3
19 | with:
20 | java-version: '11'
21 | distribution: 'temurin'
22 | - name: Build distribution zip
23 | uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
24 | with:
25 | arguments: distZip
26 | - name: Create Release
27 | uses: actions/create-release@v1
28 | env:
29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
30 | with:
31 | tag_name: ${{ github.ref }}
32 | release_name: Release ${{ github.ref }}
33 | draft: false
34 | prerelease: false
35 | - name: Upload Release Assets
36 | uses: actions/upload-release-asset@v1
37 | env:
38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
39 | with:
40 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
41 | asset_path: build/distributions/unidbg-fetch-qsign-${{ github.ref }}.zip
42 | asset_name: unidbg-fetch-qsign-${{ github.ref }}.zip
43 | asset_content_type: application/zip
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # 项目排除路径
2 | /.gradle/
3 | /build/
4 | /build/classes/kotlin/main/
5 | /.idea
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # 第一阶段:使用Gradle镜像构建Jar文件
2 | FROM gradle:7.4.2-jdk11 AS builder
3 |
4 | # 设置工作目录
5 | WORKDIR /app
6 |
7 | # 复制项目文件到容器的工作目录中
8 | COPY . /app
9 |
10 | # 构建项目
11 | RUN gradle build
12 |
13 | # 第二阶段:使用JRE镜像运行应用程序
14 | FROM openjdk:11-jre-slim
15 |
16 | # 设置工作目录
17 | WORKDIR /app
18 |
19 | # 从第一阶段复制构建好的Jar文件到当前镜像
20 | COPY --from=builder /app/build/libs/unidbg-fetch-qsign-*-all.jar ./unidbg-fetch-qsign-all.jar
21 | COPY --from=builder /app/txlib ./txlib
22 |
23 | # 暴露项目运行的端口号
24 | EXPOSE 8080
25 |
26 | # 启动项目
27 | CMD ["java", "-jar", "unidbg-fetch-qsign-all.jar", "--basePath=./txlib/8.9.71"]
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 获取QQSign参数通过Unidbg,开放HTTP API。unidbg-fetch-sign最低从QQ8.9.33(不囊括)开始支持。
2 |
3 | # 切记
4 |
5 | - 公共API具有高风险可能
6 | - 请使用与协议对应版本的libfekit.so文件
7 | - QSign基于Android平台,其它平台Sign计算的参数不同,不互通(例如:IPad)。
8 | - 不支持载入Tim.apk的so文件。
9 |
10 | # 部署方法
11 |
12 | **[Wiki](https://github.com/fuqiuluo/unidbg-fetch-qsign/wiki)**
13 |
14 | # 你可能需要的项目
15 |
16 | - [fix-protocol-version](https://github.com/cssxsh/fix-protocol-version):基于**mirai**的qsign api对接。
17 |
18 | # 使用API
19 |
20 | ## [初始化QSign&刷新token](https://github.com/fuqiuluo/unidbg-fetch-qsign/blob/master/refresh_token/README.md)
21 |
22 | ### 原始energy
23 |
24 | ```kotlin
25 | # http://host:port/custom_energy?uin=[QQ]&salt=[SALT HEX]&data=[DATA]
26 | ```
27 | | 参数名 | 意义 | 例子 |
28 | |-----|---------|--------|
29 | | UIN | Bot的QQ号 | 114514 |
30 |
31 | > 非专业人员勿用。
32 |
33 | ### sign
34 |
35 | ```kotlin
36 | # http://host:port/sign?uin=[UIN]&qua=[QUA]&cmd=[CMD]&seq=[SEQ]&buffer=[BUFFER]
37 | ```
38 | | 参数名 | 意义 | 例子 |
39 | |--------|---------------------------------------------------|-----------------------------|
40 | | UIN | Bot的QQ号 | 114514 |
41 | | QUA | QQ User-Agent,与QQ版本有关 | V1_AND_SQ_8.9.68_4264_YYB_D |
42 | | CMD | 指令类型,CMD有很多种,目前登录、发信息均需要sign | wtlogin.login |
43 | | SEQ | 数据包序列号,用于指示请求的序列或顺序。它是一个用于跟踪请求的顺序的数值,确保请求按正确的顺序处理 | 2333 |
44 | | BUFFER | 数据包包体,不需要长度,将byte数组转换为HEX发送 | 020348010203040506 |
45 |
46 |
47 | POST的支持
48 |
49 | 如果buffer过长,会超出get请求方式的长度上限,因此sign的请求也支持POST的方式。
50 |
51 | 请求头 `Content-Type: application/x-www-form-urlencoded`
52 |
53 | POST的内容:"uin=" + uin + "&qua=" + qua + "&cmd=" + cmd + "&seq=" + seq + "&buffer=" + buffer
54 |
55 |
56 | ### 登录包energy(tlv544)
57 |
58 | 下面这个只是个例子
59 |
60 | ```kotlin
61 | # http://host:port/energy?version=[VERSION]&uin=[UIN]&guid=[GUID]&data=[DATA]
62 | ```
63 |
64 | | 参数名 | 意义 | 例子 |
65 | |---------|--------------------------------------------------------------|----------------------------------|
66 | | VERSION | **注意!**这里的VERSION指的**不是QQ的版本号,而是SDK Version**,可以在QQ安装包中找到此信息 | 6.0.0.2549 |
67 | | UIN | Bot的QQ号 | 114514 |
68 | | GUID | 登录设备的GUID,将byte数组转换为HEX发送,必须是32长度的HEX字符串 | ABCDABCDABCDABCDABCDABCDABCDABCD |
69 | | DATA | QQ发送登录包的CmdId和SubCmdId,例子中810是登陆CmdId,9是SubCmdId | 810_9 |
70 |
71 | # 其他
72 | - 由于项目的特殊性,我们可能~~随时删除本项目~~且不会做出任何声明
73 |
74 | # 奇怪的交际援助
75 |
76 | - 昵称:**[咖啡]** QQ:1456****68
77 | - 昵称:**RinsToln** QQ:339***8297302
78 | - 昵称:**菩提** QQ:919***595
79 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
2 | import kotlin.system.exitProcess
3 |
4 | val ktor_version: String by project
5 | val logback_version: String by project
6 |
7 | plugins {
8 | kotlin("jvm") version "1.8.0"
9 | application
10 | id("org.jetbrains.kotlin.plugin.serialization") version "1.8.22"
11 | id("com.github.johnrengelman.shadow") version "7.1.2"
12 | }
13 |
14 | group = "moe.fuqiuluo"
15 | version = "1.2.1"
16 |
17 | repositories {
18 | mavenCentral()
19 | maven("https://kotlin.bintray.com/ktor")
20 | }
21 |
22 | dependencies {
23 | testImplementation(kotlin("test"))
24 | implementation("io.ktor:ktor-server-content-negotiation-jvm:$ktor_version")
25 | implementation("io.ktor:ktor-serialization-kotlinx-json-jvm:$ktor_version")
26 | implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.3.3")
27 | implementation("io.ktor:ktor-server-core-jvm:$ktor_version")
28 | implementation("io.ktor:ktor-server-netty-jvm:$ktor_version")
29 | implementation("io.ktor:ktor-server-status-pages:$ktor_version")
30 | implementation("ch.qos.logback:logback-classic:$logback_version")
31 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
32 |
33 | implementation(fileTree(mapOf(
34 | "dir" to "libs",
35 | "include" to listOf("*.jar")
36 | )))
37 | }
38 |
39 | tasks.test {
40 | useJUnitPlatform()
41 | }
42 |
43 | kotlin {
44 | jvmToolchain(8)
45 | }
46 |
47 | application {
48 | mainClass.set("MainKt")
49 | }
50 |
51 | distributions {
52 | main {
53 | contents {
54 | from(".") {
55 | include("txlib/**")
56 | }
57 | }
58 | }
59 | }
60 |
61 |
62 | tasks {
63 | register("generateProjectFile") {
64 | val dir = File("src/main/java/project").apply { mkdirs() }
65 | dir.resolve("BuildConfig.java").also {
66 | if (!it.exists()) it.createNewFile()
67 | }.writer().use {
68 | it.write("public class BuildConfig {")
69 | it.write(" public static String version = \"${project.version}\";")
70 | it.write("}")
71 | }
72 | }
73 | named("prepareKotlinBuildScriptModel").configure {
74 | dependsOn("generateProjectFile")
75 | }
76 | named("processResources") {
77 | dependsOn("generateProjectFile")
78 | }
79 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | kotlin.code.style=official
2 | ktor_version=2.3.1
3 | logback_version=1.2.11
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XZhouQD/unidbg-fetch-qsign/e3bb92872719f5e8423d38d182d52b612717e064/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/libs/unidbg-android-1.0.7.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XZhouQD/unidbg-fetch-qsign/e3bb92872719f5e8423d38d182d52b612717e064/libs/unidbg-android-1.0.7.jar
--------------------------------------------------------------------------------
/refresh_token/README.md:
--------------------------------------------------------------------------------
1 | # 初始化Sign
2 |
3 | - 执行```/register```请求,注册对应QQ实例进系统。
4 |
5 | ```kotlin
6 | http://host:port/register?uin=[QQ]&android_id=[ANDROID_ID]&guid=[GUID]&qimei36=[QIMEI36]&key=[KEY]
7 | ```
8 |
9 | 如果是第一次注册实例则会出现以下返回
10 |
11 | ```json5
12 | {
13 | "code": 0,
14 | "msg": "Instance loaded successfully.",
15 | "data": ""
16 | }
17 | ```
18 |
19 | 如果这个QQ已经注册实例了,但是又执行一次,则会以下返回
20 |
21 | ```json5
22 | {
23 | "code": 0,
24 | "msg": "The QQ has already loaded an instance, so this time it is deleting the existing instance and creating a new one.",
25 | "data": ""
26 | }
27 | ```
28 |
29 | 未注册实例请求API会出现以下返回
30 |
31 | ```json5
32 | {
33 | "code": 1,
34 | "msg": "Uin is not registered.",
35 | "data": "/sign?uin=xxx&qua=V1_AND_SQ_8.9.63_4188_HDBM_T&cmd=xxx&seq=xxx&buffer=xxx"
36 | }
37 | ```
38 |
39 | ### 关于自动注册实例的说明
40 |
41 | - 如果不想使用```/register```注册实例,想直接使用```/energy```和```/sign```请求,可以在config.json修改auto_register参数为true,启用自动注册实例功能。
42 | - 此外,需要注意的是,在首次请求```/sign```或```/energy```需提交额外的参数```android_id```和```guid```,后续请求即可省略。
43 |
44 | # 联网更新Token
45 |
46 | - 当首次调用```/sign```的时候会有类似以下返回
47 |
48 | ```json5
49 | {
50 | "code": 0,
51 | "msg": "success",
52 | "data": {
53 | "token": "xxx",
54 | "extra": "xxx",
55 | "sign": "xxx",
56 | "o3did": "xxx",
57 | "requestCallback": [
58 | {
59 | "cmd": "trpc.o3.ecdh_access.EcdhAccess.SsoSecureA2Establish",
60 | "body": "xxx",
61 | "callbackId": 0
62 | },
63 | {
64 | "cmd": "trpc.o3.ecdh_access.EcdhAccess.SsoSecureA2Establish",
65 | "body": "xxx",
66 | "callbackId": 1
67 | },
68 | {
69 | "cmd": "trpc.o3.ecdh_access.EcdhAccess.SsoSecureA2Establish",
70 | "body": "xxx",
71 | "callbackId": 2
72 | }
73 | ]
74 | }
75 | }
76 | ```
77 |
78 | - 你需要发送```requestCallback```内的Packet(部分Packet需要签sign或者登录后才能发送),并携带callback_id提交返回包给API。
79 |
80 | ```kotlin
81 | http://host:port/submit?uin=[QQ]&cmd=[CMD]&callback_id=[CALLBACK_ID]&buffer=[BUFFER]
82 | ```
83 |
84 | > WARN: 其中```buffer```参数无需携带4字节(32bit)的长度。
85 |
86 | # 刷新Token
87 |
88 | sign的token会过期(过期时间在1小时左右,建议每隔30~40分钟请求刷新token)
89 |
90 | ```kotlin
91 | http://host:port/request_token?uin=[QQ]
92 | ```
93 |
94 | # 销毁实例
95 |
96 | 可以主动销毁已经注册的实例,使用本API至少需要1.1.6版本及以上的支持。
97 |
98 | ```kotlin
99 | http://host:port/destroy?uin=[QQ]&key=[key]
100 | ```
101 |
102 | 成功销毁实例返回
103 |
104 | ```json5
105 | {
106 | "code": 0,
107 | "msg": "Instance destroyed successfully.",
108 | "data": ""
109 | }
110 | ```
111 |
112 | 如果该实例未注册则返回
113 |
114 | ```json5
115 | {
116 | "code": 1,
117 | "msg": "Instance does not exist.",
118 | "data": "failed"
119 | }
120 | ```
121 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "unidbg-fetch-qsign"
2 |
3 |
--------------------------------------------------------------------------------
/src/main/java/com/tencent/crypt/Crypt.java:
--------------------------------------------------------------------------------
1 | package com.tencent.crypt;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.util.Random;
5 |
6 | public class Crypt {
7 | // 指向当前的明文块
8 | private byte[] plain;
9 | // 这指向前面一个明文块
10 | private byte[] prePlain;
11 | // 输出的密文或者明文
12 | private byte[] out;
13 | // 当前加密的密文位置和上一次加密的密文块位置,他们相差8
14 | private int crypt, preCrypt;
15 | // 当前处理的加密解密块的位置
16 | private int pos;
17 | // 填充数
18 | private int padding;
19 | // 密钥
20 | private byte[] key;
21 | // 用于加密时,表示当前是否是第一个8字节块,因为加密算法是反馈的
22 | // 但是最开始的8个字节没有反馈可用,所有需要标明这种情况
23 | private boolean header = true;
24 | // 这个表示当前解密开始的位置,之所以要这么一个变量是为了避免当解密到最后时
25 | // 后面已经没有数据,这时候就会出错,这个变量就是用来判断这种情况免得出错
26 | private int contextStart;
27 | // 随机数对象
28 | private static final Random random = new Random();
29 | // 字节输出流
30 | private final ByteArrayOutputStream bas;
31 |
32 | /**
33 | * 构造函数
34 | */
35 | public Crypt() {
36 | bas = new ByteArrayOutputStream(8);
37 | }
38 |
39 | /**
40 | * 解密
41 | *
42 | * @param in 密文
43 | * @param offset 密文开始的位置
44 | * @param len 密文长度
45 | * @param k 密钥
46 | * @return 明文
47 | */
48 | public byte[] decrypt(byte[] in, int offset, int len, byte[] k) {
49 | // 检查密钥
50 | if (k == null)
51 | return null;
52 |
53 | crypt = preCrypt = 0;
54 | this.key = k;
55 | int count;
56 | byte[] m = new byte[offset + 8];
57 |
58 | // 因为QQ消息加密之后至少是16字节,并且肯定是8的倍数,这里检查这种情况
59 | if ((len % 8 != 0) || (len < 16)) return null;
60 | // 得到消息的头部,关键是得到真正明文开始的位置,这个信息存在第一个字节里面,所以其用解密得到的第一个字节与7做与
61 | prePlain = decipher(in, offset);
62 | pos = prePlain[0] & 0x7;
63 | // 得到真正明文的长度
64 | count = len - pos - 10;
65 | // 如果明文长度小于0,那肯定是出错了,比如传输错误之类的,返回
66 | if (count < 0) return null;
67 |
68 | // 这个是临时的preCrypt,和加密时第一个8字节块没有prePlain一样,解密时
69 | // 第一个8字节块也没有preCrypt,所有这里建一个全0的
70 | for (int i = offset; i < m.length; i++)
71 | m[i] = 0;
72 | // 通过了上面的代码,密文应该是没有问题了,我们分配输出缓冲区
73 | out = new byte[count];
74 | // 设置preCrypt的位置等于0,注意目前的preCrypt位置是指向m的,因为java没有指针,所以我们在后面要控制当前密文buf的引用
75 | preCrypt = 0;
76 | // 当前的密文位置,为什么是8不是0呢?注意前面我们已经解密了头部信息了,现在当然该8了
77 | crypt = 8;
78 | // 自然这个也是8
79 | contextStart = 8;
80 | // 加1,和加密算法是对应的
81 | pos++;
82 |
83 | // 开始跳过头部,如果在这个过程中满了8字节,则解密下一块
84 | // 因为是解密下一块,所以我们有一个语句 m = in,下一块当然有preCrypt了,我们不再用m了
85 | // 但是如果不满8,这说明了什么?说明了头8个字节的密文是包含了明文信息的,当然还是要用m把明文弄出来
86 | // 所以,很显然,满了8的话,说明了头8个字节的密文除了一个长度信息有用之外,其他都是无用的填充
87 | padding = 1;
88 | while (padding <= 2) {
89 | if (pos < 8) {
90 | pos++;
91 | padding++;
92 | }
93 | if (pos == 8) {
94 | m = in;
95 | decrypt8Bytes(in, offset, len);
96 | }
97 | }
98 |
99 | // 这里是解密的重要阶段,这个时候头部的填充都已经跳过了,开始解密
100 | // 注意如果上面一个while没有满8,这里第一个if里面用的就是原始的m,否则这个m就是in了
101 | int i = 0;
102 | while (count != 0) {
103 | if (pos < 8) {
104 | out[i] = (byte) (m[offset + preCrypt + pos] ^ prePlain[pos]);
105 | i++;
106 | count--;
107 | pos++;
108 | }
109 | if (pos == 8) {
110 | m = in;
111 | preCrypt = crypt - 8;
112 | decrypt8Bytes(in, offset, len);
113 | }
114 | }
115 |
116 | // 最后的解密部分,上面一个while已经把明文都解出来了,就剩下尾部的填充了,应该全是0
117 | // 所以这里有检查是否解密了之后是不是0,如果不是的话那肯定出错了,返回null
118 | for (padding = 1; padding < 8; padding++) {
119 | if (pos < 8) {
120 | if ((m[offset + preCrypt + pos] ^ prePlain[pos]) != 0)
121 | return null;
122 | pos++;
123 | }
124 | if (pos == 8) {
125 | m = in;
126 | preCrypt = crypt;
127 | decrypt8Bytes(in, offset, len);
128 | }
129 | }
130 | return out;
131 | }
132 |
133 | public byte[] decrypt(byte[] in, byte[] k) {
134 | return decrypt(in, 0, in.length, k);
135 | }
136 |
137 | public byte[] encrypt(byte[] in, int offset, int len, byte[] k) {
138 | // 检查密钥
139 | if (k == null)
140 | return in;
141 |
142 | plain = new byte[8];
143 | prePlain = new byte[8];
144 | padding = 0;
145 | crypt = preCrypt = 0;
146 | this.key = k;
147 | header = true;
148 |
149 | // 计算头部填充字节数
150 | pos = (len + 0x0A) % 8;
151 | if (pos != 0)
152 | pos = 8 - pos;
153 | // 计算输出的密文长度
154 | out = new byte[len + pos + 10];
155 | // 这里的操作把pos存到了plain的第一个字节里面
156 | // 0xF8后面三位是空的,正好留给pos,因为pos是0到7的值,表示文本开始的字节位置
157 | plain[0] = (byte) ((rand() & 0xF8) | pos);
158 |
159 | // 这里用随机产生的数填充plain[1]到plain[pos]之间的内容
160 | for (int i = 1; i <= pos; i++)
161 | plain[i] = (byte) (rand() & 0xFF);
162 | pos++;
163 | // 这个就是prePlain,第一个8字节块当然没有prePlain,所以我们做一个全0的给第一个8字节块
164 | for (int i = 0; i < 8; i++)
165 | prePlain[i] = 0x0;
166 |
167 | // 继续填充2个字节的随机数,这个过程中如果满了8字节就加密之
168 | padding = 1;
169 | while (padding <= 2) {
170 | if (pos < 8) {
171 | plain[pos++] = (byte) (rand() & 0xFF);
172 | padding++;
173 | }
174 | if (pos == 8)
175 | encrypt8Bytes();
176 | }
177 |
178 | // 头部填充完了,这里开始填真正的明文了,也是满了8字节就加密,一直到明文读完
179 | int i = offset;
180 | while (len > 0) {
181 | if (pos < 8) {
182 | plain[pos++] = in[i++];
183 | len--;
184 | }
185 | if (pos == 8)
186 | encrypt8Bytes();
187 | }
188 |
189 | // 最后填上0,以保证是8字节的倍数
190 | padding = 1;
191 | while (padding <= 7) {
192 | if (pos < 8) {
193 | plain[pos++] = 0x0;
194 | padding++;
195 | }
196 | if (pos == 8)
197 | encrypt8Bytes();
198 | }
199 |
200 | return out;
201 | }
202 |
203 | public byte[] encrypt(byte[] in, byte[] k) {
204 | return encrypt(in, 0, in.length, k);
205 | }
206 |
207 | private byte[] encipher(byte[] in) {
208 | // 迭代次数,16次
209 | int loop = 0x10;
210 | // 得到明文和密钥的各个部分,注意java没有无符号类型,所以为了表示一个无符号的整数
211 | // 我们用了long,这个long的前32位是全0的,我们通过这种方式模拟无符号整数,后面用到的long也都是一样的
212 | // 而且为了保证前32位为0,需要和0xFFFFFFFF做一下位与
213 | long y = getUnsignedInt(in, 0, 4);
214 | long z = getUnsignedInt(in, 4, 4);
215 | long a = getUnsignedInt(key, 0, 4);
216 | long b = getUnsignedInt(key, 4, 4);
217 | long c = getUnsignedInt(key, 8, 4);
218 | long d = getUnsignedInt(key, 12, 4);
219 | // 这是算法的一些控制变量,为什么delta是0x9E3779B9呢?
220 | // 这个数是TEA算法的delta,实际是就是(sqr(5) - 1) * 2^31 (根号5,减1,再乘2的31次方)
221 | long sum = 0;
222 | long delta = 0x9E3779B9;
223 | delta &= 0xFFFFFFFFL;
224 |
225 | // 开始迭代了,乱七八糟的,我也看不懂,反正和DES之类的差不多,都是这样倒来倒去
226 | while (loop-- > 0) {
227 | sum += delta;
228 | sum &= 0xFFFFFFFFL;
229 | y += ((z << 4) + a) ^ (z + sum) ^ ((z >>> 5) + b);
230 | y &= 0xFFFFFFFFL;
231 | z += ((y << 4) + c) ^ (y + sum) ^ ((y >>> 5) + d);
232 | z &= 0xFFFFFFFFL;
233 | }
234 |
235 | // 最后,我们输出密文,因为我用的long,所以需要强制转换一下变成int
236 | bas.reset();
237 | writeInt((int) y);
238 | writeInt((int) z);
239 | return bas.toByteArray();
240 | }
241 |
242 | /**
243 | * 解密从offset开始的8字节密文
244 | *
245 | * @param in 密文字节数组
246 | * @param offset 密文开始位置
247 | * @return 明文
248 | */
249 | private byte[] decipher(byte[] in, int offset) {
250 | // 迭代次数,16次
251 | int loop = 0x10;
252 | // 得到密文和密钥的各个部分,注意java没有无符号类型,所以为了表示一个无符号的整数
253 | // 我们用了long,这个long的前32位是全0的,我们通过这种方式模拟无符号整数,后面用到的long也都是一样的
254 | // 而且为了保证前32位为0,需要和0xFFFFFFFF做一下位与
255 | long y = getUnsignedInt(in, offset, 4);
256 | long z = getUnsignedInt(in, offset + 4, 4);
257 | long a = getUnsignedInt(key, 0, 4);
258 | long b = getUnsignedInt(key, 4, 4);
259 | long c = getUnsignedInt(key, 8, 4);
260 | long d = getUnsignedInt(key, 12, 4);
261 | // 算法的一些控制变量,sum在这里也有数了,这个sum和迭代次数有关系
262 | // 因为delta是这么多,所以sum如果是这么多的话,迭代的时候减减减,减16次,最后
263 | // 得到0。反正这就是为了得到和加密时相反顺序的控制变量,这样才能解密呀~~
264 | long sum = 0xE3779B90;
265 | sum &= 0xFFFFFFFFL;
266 | long delta = 0x9E3779B9;
267 | delta &= 0xFFFFFFFFL;
268 |
269 | // 迭代开始了, @_@
270 | while (loop-- > 0) {
271 | z -= ((y << 4) + c) ^ (y + sum) ^ ((y >>> 5) + d);
272 | z &= 0xFFFFFFFFL;
273 | y -= ((z << 4) + a) ^ (z + sum) ^ ((z >>> 5) + b);
274 | y &= 0xFFFFFFFFL;
275 | sum -= delta;
276 | sum &= 0xFFFFFFFFL;
277 | }
278 |
279 | bas.reset();
280 | writeInt((int) y);
281 | writeInt((int) z);
282 | return bas.toByteArray();
283 | }
284 |
285 | /**
286 | * 写入一个整型到输出流,高字节优先
287 | */
288 | private void writeInt(int t) {
289 | bas.write(t >>> 24);
290 | bas.write(t >>> 16);
291 | bas.write(t >>> 8);
292 | bas.write(t);
293 | }
294 |
295 | /**
296 | * 解密
297 | *
298 | * @param in 密文
299 | * @return 明文
300 | */
301 | private byte[] decipher(byte[] in) {
302 | return decipher(in, 0);
303 | }
304 |
305 | /**
306 | * 加密8字节
307 | */
308 | private void encrypt8Bytes() {
309 | // 这部分完成我上面所说的 plain ^ preCrypt,注意这里判断了是不是第一个8字节块,如果是的话,那个prePlain就当作preCrypt用
310 | for (pos = 0; pos < 8; pos++) {
311 | if (header)
312 | plain[pos] ^= prePlain[pos];
313 | else
314 | plain[pos] ^= out[preCrypt + pos];
315 | }
316 | // 这个完成我上面说的 f(plain ^ preCrypt)
317 | byte[] crypted = encipher(plain);
318 | // 这个没什么,就是拷贝一下,java不像c,所以我只好这么干,c就不用这一步了
319 | System.arraycopy(crypted, 0, out, crypt, 8);
320 |
321 | // 这个完成了 f(plain ^ preCrypt) ^ prePlain,ok,下面拷贝一下就行了
322 | for (pos = 0; pos < 8; pos++)
323 | out[crypt + pos] ^= prePlain[pos];
324 | System.arraycopy(plain, 0, prePlain, 0, 8);
325 |
326 | // 完成了加密,现在是调整crypt,preCrypt等等东西的时候了
327 | preCrypt = crypt;
328 | crypt += 8;
329 | pos = 0;
330 | header = false;
331 | }
332 |
333 | /**
334 | * 解密8个字节
335 | *
336 | * @param in 密文字节数组
337 | * @param offset 从何处开始解密
338 | * @param len 密文的长度
339 | * @return true表示解密成功
340 | */
341 | private void decrypt8Bytes(byte[] in, int offset, int len) {
342 | // 这里第一步就是判断后面还有没有数据,没有就返回,如果有,就执行 crypt ^ prePlain
343 | for (pos = 0; pos < 8; pos++) {
344 | if (contextStart + pos >= len)
345 | return;
346 | prePlain[pos] ^= in[offset + crypt + pos];
347 | }
348 |
349 | // 好,这里执行到了 d(crypt ^ prePlain)
350 | prePlain = decipher(prePlain);
351 |
352 | // 解密完成,最后一步好像没做?
353 | // 这里最后一步放到decrypt里面去做了,因为解密的步骤有点不太一样
354 | // 调整这些变量的值先
355 | contextStart += 8;
356 | crypt += 8;
357 | pos = 0;
358 | }
359 |
360 | /**
361 | * 这是个随机因子产生器,用来填充头部的,如果为了调试,可以用一个固定值
362 | * 随机因子可以使相同的明文每次加密出来的密文都不一样
363 | *
364 | * @return 随机因子
365 | */
366 | private int rand() {
367 | return random.nextInt();
368 | }
369 |
370 | public static long getUnsignedInt(byte[] in, int offset, int len) {
371 | long ret = 0L;
372 | int end;
373 | if (len > 8)
374 | end = offset + 8;
375 | else
376 | end = offset + len;
377 | for (int i = offset; i < end; i++) {
378 | ret <<= 8;
379 | ret |= in[i] & 0xff;
380 | }
381 | return ret & 0xffffffffL | ret >>> 32;
382 | }
383 | }
--------------------------------------------------------------------------------
/src/main/java/com/tencent/mobileqq/dt/model/FEBound.java:
--------------------------------------------------------------------------------
1 | package com.tencent.mobileqq.dt.model;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.alibaba.fastjson.JSONArray;
5 | import com.alibaba.fastjson.JSONObject;
6 | import kotlin.io.FilesKt;
7 |
8 | import java.io.File;
9 | import java.nio.charset.Charset;
10 |
11 | public class FEBound {
12 | private static final int LEVEL1 = 32;
13 | private static final int LEVEL2 = 16;
14 | private static final int Type_Decode = 2;
15 | private static final int Type_Encode = 1;
16 | private static final byte[][] mConfigEnCode = {
17 | new byte[]{15, 10, 13, 4, 0, 5, 3, 1, 11, 12, 7, 2, 14, 6, 9, 8},
18 | new byte[]{12, 1, 13, 4, 14, 9, 8, 6, 5, 3, 10, 7, 11, 2, 0, 15},
19 | new byte[]{10, 5, 14, 0, 9, 1, 7, 4, 11, 8, 3, 15, 12, 6, 13, 2},
20 | new byte[]{7, 11, 2, 14, 3, 10, 1, 8, 0, 15, 9, 6, 13, 4, 5, 12},
21 | new byte[]{5, 15, 1, 2, 4, 13, 7, 8, 3, 6, 11, 0, 9, 10, 12, 14},
22 | new byte[]{2, 5, 0, 9, 3, 15, 11, 7, 8, 13, 10, 4, 1, 14, 6, 12},
23 | new byte[]{15, 12, 6, 14, 7, 2, 0, 10, 11, 13, 3, 5, 1, 4, 9, 8},
24 | new byte[]{13, 2, 0, 1, 8, 10, 4, 14, 11, 12, 7, 3, 15, 9, 5, 6},
25 | new byte[]{10, 6, 7, 8, 9, 3, 15, 1, 2, 5, 11, 12, 13, 14, 0, 4},
26 | new byte[]{8, 10, 4, 2, 13, 15, 12, 7, 6, 3, 14, 0, 1, 5, 9, 11},
27 | new byte[]{5, 7, 12, 6, 0, 2, 14, 3, 1, 13, 9, 10, 15, 11, 8, 4},
28 | new byte[]{2, 7, 12, 1, 9, 10, 4, 8, 13, 11, 6, 3, 0, 5, 15, 14},
29 | new byte[]{0, 11, 9, 3, 12, 8, 14, 13, 5, 4, 10, 15, 7, 2, 1, 6},
30 | new byte[]{13, 1, 0, 12, 6, 14, 7, 11, 3, 10, 2, 5, 15, 8, 4, 9},
31 | new byte[]{10, 7, 5, 1, 0, 6, 9, 13, 14, 8, 3, 15, 11, 12, 4, 2},
32 | new byte[]{7, 14, 5, 13, 4, 11, 15, 10, 8, 0, 12, 2, 3, 1, 9, 6},
33 | new byte[]{5, 2, 1, 12, 10, 14, 4, 15, 9, 8, 6, 0, 13, 11, 7, 3},
34 | new byte[]{2, 8, 6, 5, 1, 3, 14, 10, 0, 12, 4, 13, 7, 15, 9, 11},
35 | new byte[]{0, 12, 3, 11, 10, 5, 4, 14, 9, 7, 1, 2, 13, 8, 6, 15},
36 | new byte[]{13, 3, 7, 11, 4, 10, 15, 0, 5, 2, 6, 12, 14, 9, 8, 1},
37 | new byte[]{11, 7, 4, 6, 3, 0, 14, 5, 2, 9, 13, 15, 10, 8, 12, 1},
38 | new byte[]{8, 13, 0, 11, 4, 1, 3, 9, 10, 15, 12, 5, 14, 7, 6, 2},
39 | new byte[]{5, 3, 11, 10, 13, 6, 1, 15, 12, 8, 2, 4, 9, 14, 0, 7},
40 | new byte[]{3, 7, 9, 6, 0, 5, 10, 14, 1, 13, 11, 4, 2, 15, 8, 12},
41 | new byte[]{0, 14, 12, 15, 11, 1, 3, 10, 8, 2, 9, 6, 13, 5, 7, 4},
42 | new byte[]{13, 4, 8, 6, 3, 7, 10, 0, 14, 5, 9, 1, 15, 12, 2, 11},
43 | new byte[]{11, 8, 13, 5, 3, 14, 6, 9, 1, 0, 12, 15, 2, 7, 10, 4},
44 | new byte[]{8, 14, 13, 10, 7, 3, 0, 6, 11, 12, 5, 1, 15, 4, 9, 2},
45 | new byte[]{6, 2, 14, 10, 15, 1, 5, 8, 9, 7, 11, 13, 4, 3, 12, 0},
46 | new byte[]{3, 9, 2, 4, 5, 8, 1, 7, 11, 10, 12, 0, 15, 13, 6, 14},
47 | new byte[]{0, 15, 6, 14, 11, 2, 1, 3, 13, 4, 10, 12, 7, 5, 8, 9},
48 | new byte[]{13, 5, 10, 7, 2, 4, 11, 0, 14, 8, 1, 9, 3, 12, 6, 15}
49 | };
50 | private static final byte[][] mConfigDeCode = {
51 | new byte[]{13, 7, 12, 2, 14, 11, 3, 8, 5, 9, 10, 6, 1, 15, 0, 4},
52 | new byte[]{13, 8, 4, 1, 9, 15, 12, 2, 11, 7, 10, 0, 3, 5, 6, 14},
53 | new byte[]{7, 3, 9, 13, 2, 6, 14, 1, 5, 0, 8, 4, 11, 12, 10, 15},
54 | new byte[]{9, 5, 14, 13, 7, 10, 0, 6, 2, 15, 3, 4, 11, 1, 8, 12},
55 | new byte[]{11, 1, 4, 9, 0, 2, 6, 10, 5, 8, 14, 15, 7, 3, 12, 13},
56 | new byte[]{3, 0, 12, 10, 5, 13, 15, 1, 11, 2, 7, 4, 8, 9, 6, 14},
57 | new byte[]{1, 13, 15, 12, 10, 6, 11, 4, 5, 3, 7, 9, 14, 2, 0, 8},
58 | new byte[]{3, 8, 1, 11, 6, 4, 13, 10, 15, 7, 2, 5, 0, 14, 12, 9},
59 | new byte[]{7, 6, 13, 9, 5, 14, 3, 15, 1, 0, 10, 4, 11, 8, 2, 12},
60 | new byte[]{7, 14, 15, 5, 6, 11, 9, 0, 4, 10, 8, 2, 1, 12, 3, 13},
61 | new byte[]{8, 5, 14, 1, 0, 4, 7, 13, 6, 10, 15, 3, 12, 9, 11, 2},
62 | new byte[]{15, 0, 5, 2, 7, 3, 8, 12, 11, 1, 13, 4, 14, 6, 9, 10},
63 | new byte[]{12, 8, 10, 6, 15, 5, 14, 9, 7, 3, 13, 11, 2, 1, 4, 0},
64 | new byte[]{15, 6, 4, 1, 2, 5, 13, 11, 9, 14, 3, 12, 0, 10, 7, 8},
65 | new byte[]{6, 4, 8, 9, 3, 12, 14, 2, 10, 0, 1, 15, 11, 13, 5, 7},
66 | new byte[]{9, 13, 11, 12, 4, 2, 15, 0, 8, 14, 7, 5, 10, 3, 1, 6},
67 | new byte[]{1, 4, 8, 10, 0, 7, 15, 9, 2, 3, 14, 13, 11, 6, 5, 12},
68 | new byte[]{4, 0, 10, 3, 13, 7, 11, 9, 5, 6, 1, 14, 2, 12, 15, 8},
69 | new byte[]{3, 4, 14, 8, 5, 13, 10, 9, 6, 2, 11, 15, 12, 7, 1, 0},
70 | new byte[]{6, 10, 12, 7, 4, 1, 13, 8, 5, 3, 11, 14, 0, 2, 15, 9},
71 | new byte[]{2, 11, 14, 13, 3, 9, 8, 12, 4, 1, 0, 15, 7, 10, 5, 6},
72 | new byte[]{6, 15, 3, 4, 9, 7, 11, 0, 5, 14, 13, 10, 12, 8, 2, 1},
73 | new byte[]{4, 14, 2, 5, 0, 1, 10, 7, 3, 13, 6, 15, 12, 8, 11, 9},
74 | new byte[]{11, 13, 0, 3, 5, 14, 9, 7, 4, 1, 8, 15, 6, 12, 10, 2},
75 | new byte[]{11, 3, 1, 8, 7, 12, 6, 10, 5, 9, 15, 14, 2, 13, 4, 0},
76 | new byte[]{10, 13, 3, 14, 15, 12, 1, 2, 11, 7, 4, 6, 0, 5, 9, 8},
77 | new byte[]{1, 15, 5, 3, 2, 6, 7, 4, 8, 11, 0, 14, 12, 13, 10, 9},
78 | new byte[]{1, 10, 5, 7, 15, 14, 12, 0, 11, 8, 4, 2, 3, 6, 13, 9},
79 | new byte[]{1, 2, 12, 14, 8, 0, 6, 10, 3, 9, 7, 15, 11, 4, 13, 5},
80 | new byte[]{10, 5, 0, 14, 15, 13, 3, 11, 8, 2, 4, 1, 6, 7, 12, 9},
81 | new byte[]{8, 1, 4, 12, 11, 7, 9, 5, 14, 6, 10, 3, 13, 2, 15, 0},
82 | new byte[]{7, 10, 4, 12, 5, 1, 14, 3, 9, 11, 2, 6, 13, 0, 8, 15}
83 | };
84 |
85 | public static void initAssertConfig(File baseDir) {
86 | File target = new File(baseDir, "dtconfig.json");
87 | String text = FilesKt.readText(target, Charset.defaultCharset());
88 |
89 | JSONObject object = JSON.parseObject(text);
90 |
91 | JSONArray en = object.getJSONArray("en");
92 | for (int i = 0; i < en.size(); i++) {
93 | JSONArray keys = en.getJSONArray(i);
94 | for (int j = 0; j < keys.size(); j++) {
95 | mConfigEnCode[i][j] = keys.getByteValue(j);
96 | }
97 | }
98 |
99 | JSONArray de = object.getJSONArray("de");
100 | for (int i = 0; i < de.size(); i++) {
101 | JSONArray keys = de.getJSONArray(i);
102 | for (int j = 0; j < keys.size(); j++) {
103 | mConfigDeCode[i][j] = keys.getByteValue(j);
104 | }
105 | }
106 | }
107 |
108 | public static int checkCurrent() {
109 | int tmp = 0;
110 | for (byte[] bytes : mConfigEnCode) {
111 | for (byte aByte : bytes) {
112 | tmp += aByte;
113 | }
114 | }
115 | for (byte[] bytes : mConfigDeCode) {
116 | for (byte aByte : bytes) {
117 | tmp += aByte;
118 | }
119 | }
120 | return tmp;
121 | }
122 |
123 | public static byte[] transform(int i, byte[] bArr) {
124 | try {
125 | byte[] bArr2 = new byte[bArr.length];
126 | byte[][] bArr3 = mConfigEnCode;
127 | if (bArr3.length == LEVEL1 && i == Type_Encode) {
128 | transformInner(bArr, bArr2, bArr3);
129 | } else {
130 | byte[][] bArr4 = mConfigDeCode;
131 | if (bArr4.length == LEVEL1 && i == Type_Decode) {
132 | transformInner(bArr, bArr2, bArr4);
133 | } else {
134 | //a.a(TAG, (int) Type_Encode, "transform error!");
135 | }
136 | }
137 | return bArr2;
138 | } catch (Throwable th) {
139 | th.printStackTrace();
140 | //a.a(TAG, (int) Type_Encode, "encode error!" + th);
141 | return null;
142 | }
143 | }
144 |
145 | private static void transformInner(byte[] bArr, byte[] bArr2, byte[][] bArr3) {
146 | for (int i = 0; i < bArr.length; i += Type_Encode) {
147 | int i2 = i * Type_Decode;
148 | bArr2[i] = (byte) (bArr3[(i2 + Type_Encode) % LEVEL1][(byte) (bArr[i] & 15)] | (bArr3[i2 % LEVEL1][(byte) ((bArr[i] >> 4) & 15)] << 4));
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/main/java/com/tencent/mobileqq/qsec/qsecest/SelfBase64.java:
--------------------------------------------------------------------------------
1 | package com.tencent.mobileqq.qsec.qsecest;
2 |
3 | import java.io.OutputStream;
4 | import java.nio.ByteBuffer;
5 | import java.util.Arrays;
6 | import java.util.Objects;
7 |
8 | public class SelfBase64 {
9 |
10 | public static class Encoder {
11 |
12 | private final boolean doPadding;
13 | private final boolean isURL;
14 | private final int linemax;
15 | private final byte[] newline;
16 | private static final String base64chars = "EBnuvwxCD+FGHIopqrstJKLRSTUlmyz012VWXYZaMNOPQbcdefghijk3456789A/";
17 | private static final char[] toBase64 = base64chars.toCharArray();
18 | private static final char[] toBase64URL = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'};
19 | private static final byte[] CRLF = {13, 10};
20 | public static final Encoder RFC4648 = new Encoder(false, null, -1, true);
21 | static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);
22 | private static final int MIMELINEMAX = 76;
23 | static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true);
24 |
25 | private Encoder(boolean z, byte[] bArr, int i, boolean z2) {
26 | this.isURL = z;
27 | this.newline = bArr;
28 | this.linemax = i;
29 | this.doPadding = z2;
30 | }
31 |
32 | private final int outLength(int i) {
33 | int i2;
34 | if (this.doPadding) {
35 | i2 = ((i + 2) / 3) * 4;
36 | } else {
37 | int i3 = i % 3;
38 | i2 = (i3 == 0 ? 0 : i3 + 1) + ((i / 3) * 4);
39 | }
40 | if (this.linemax > 0) {
41 | return i2 + (((i2 - 1) / this.linemax) * this.newline.length);
42 | }
43 | return i2;
44 | }
45 |
46 | public byte[] encode(byte[] bArr) {
47 | try {
48 | byte[] bArr2 = new byte[outLength(bArr.length)];
49 | int encode0 = encode0(bArr, 0, bArr.length, bArr2);
50 | if (encode0 != bArr2.length) {
51 | return Arrays.copyOf(bArr2, encode0);
52 | }
53 | return bArr2;
54 | } catch (Throwable th) {
55 | th.printStackTrace();
56 | return null;
57 | }
58 | }
59 |
60 | public ByteBuffer encode(ByteBuffer byteBuffer) {
61 | int encode0;
62 | byte[] bArr = new byte[outLength(byteBuffer.remaining())];
63 | if (byteBuffer.hasArray()) {
64 | encode0 = encode0(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(), byteBuffer.arrayOffset() + byteBuffer.limit(), bArr);
65 | byteBuffer.position(byteBuffer.limit());
66 | } else {
67 | byte[] bArr2 = new byte[byteBuffer.remaining()];
68 | byteBuffer.get(bArr2);
69 | encode0 = encode0(bArr2, 0, bArr2.length, bArr);
70 | }
71 | return ByteBuffer.wrap(encode0 != bArr.length ? Arrays.copyOf(bArr, encode0) : bArr);
72 | }
73 |
74 | public int encode(byte[] bArr, byte[] bArr2) {
75 | if (bArr2.length < outLength(bArr.length)) {
76 | throw new IllegalArgumentException("Output byte array is too small for encoding all input bytes");
77 | }
78 | return encode0(bArr, 0, bArr.length, bArr2);
79 | }
80 |
81 | public String encodeToString(byte[] bArr) {
82 | try {
83 | byte[] encode = encode(bArr);
84 | return new String(encode, 0, 0, encode.length);
85 | } catch (Throwable th) {
86 | return null;
87 | }
88 | }
89 |
90 | public OutputStream wrap(OutputStream outputStream) {
91 | //Objects.requireNonNull(outputStream);
92 | //return new SelfBase64.EncOutputStream(outputStream, this.isURL ? toBase64URL : toBase64, this.newline, this.linemax, this.doPadding);
93 | return null;
94 | }
95 |
96 | public Encoder withoutPadding() {
97 | return !this.doPadding ? this : new Encoder(this.isURL, this.newline, this.linemax, false);
98 | }
99 |
100 | private int encode0(byte[] bArr, int i, int i2, byte[] bArr2) {
101 | int i3;
102 | char[] cArr = this.isURL ? toBase64URL : toBase64;
103 | int i4 = ((i2 - i) / 3) * 3;
104 | int i5 = i + i4;
105 | if (this.linemax > 0 && i4 > (this.linemax / 4) * 3) {
106 | i4 = (this.linemax / 4) * 3;
107 | }
108 | int i6 = 0;
109 | int i7 = i;
110 | while (i7 < i5) {
111 | int min = Math.min(i7 + i4, i5);
112 | int i8 = i6;
113 | int i9 = i7;
114 | while (i9 < min) {
115 | int i10 = i9 + 1;
116 | int i11 = i10 + 1;
117 | int i12 = ((bArr[i10] & 255) << 8) | ((bArr[i9] & 255) << 16);
118 | i9 = i11 + 1;
119 | int i13 = i12 | (bArr[i11] & 255);
120 | int i14 = i8 + 1;
121 | bArr2[i8] = (byte) cArr[(i13 >>> 18) & 63];
122 | int i15 = i14 + 1;
123 | bArr2[i14] = (byte) cArr[(i13 >>> 12) & 63];
124 | int i16 = i15 + 1;
125 | bArr2[i15] = (byte) cArr[(i13 >>> 6) & 63];
126 | i8 = i16 + 1;
127 | bArr2[i16] = (byte) cArr[i13 & 63];
128 | }
129 | int i17 = ((min - i7) / 3) * 4;
130 | i6 += i17;
131 | if (i17 == this.linemax && min < i2) {
132 | byte[] bArr3 = this.newline;
133 | int length = bArr3.length;
134 | int i18 = 0;
135 | while (i18 < length) {
136 | bArr2[i6] = bArr3[i18];
137 | i18++;
138 | i6++;
139 | }
140 | }
141 | i7 = min;
142 | }
143 | if (i7 < i2) {
144 | int i19 = i7 + 1;
145 | int i20 = bArr[i7] & 255;
146 | int i21 = i6 + 1;
147 | bArr2[i6] = (byte) cArr[i20 >> 2];
148 | if (i19 == i2) {
149 | i3 = i21 + 1;
150 | bArr2[i21] = (byte) cArr[(i20 << 4) & 63];
151 | if (this.doPadding) {
152 | int i22 = i3 + 1;
153 | bArr2[i3] = 61;
154 | int i23 = i22 + 1;
155 | bArr2[i22] = 61;
156 | return i23;
157 | }
158 | } else {
159 | int i24 = i19 + 1;
160 | int i25 = bArr[i19] & 255;
161 | int i26 = i21 + 1;
162 | bArr2[i21] = (byte) cArr[((i20 << 4) & 63) | (i25 >> 4)];
163 | i3 = i26 + 1;
164 | bArr2[i26] = (byte) cArr[(i25 << 2) & 63];
165 | if (this.doPadding) {
166 | int i27 = i3 + 1;
167 | bArr2[i3] = 61;
168 | return i27;
169 | }
170 | }
171 | return i3;
172 | }
173 | return i6;
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/src/main/java/project/BuildConfig.java:
--------------------------------------------------------------------------------
1 | public class BuildConfig { public static String version = "1.2.1";}
--------------------------------------------------------------------------------
/src/main/kotlin/Main.kt:
--------------------------------------------------------------------------------
1 | import com.tencent.mobileqq.dt.model.FEBound
2 | import io.ktor.serialization.kotlinx.json.*
3 | import io.ktor.server.application.*
4 | import io.ktor.server.engine.*
5 | import io.ktor.server.netty.*
6 | import io.ktor.server.plugins.contentnegotiation.*
7 | import io.ktor.server.plugins.statuspages.*
8 | import io.ktor.server.request.*
9 | import io.ktor.server.response.*
10 | import io.ktor.server.routing.*
11 | import kotlinx.serialization.json.Json
12 | import moe.fuqiuluo.api.*
13 | import moe.fuqiuluo.comm.QSignConfig
14 | import moe.fuqiuluo.comm.checkIllegal
15 | import moe.fuqiuluo.comm.invoke
16 | import java.io.File
17 |
18 | lateinit var CONFIG: QSignConfig
19 | lateinit var BASE_PATH: File
20 |
21 | private val API_LIST = arrayOf(
22 | Routing::index,
23 | Routing::sign,
24 | Routing::energy,
25 | Routing::submit,
26 | Routing::requestToken,
27 | Routing::register,
28 | Routing::addedSign
29 | )
30 |
31 | fun main(args: Array) {
32 | args().also {
33 | val baseDir = File(it["basePath", "Lack of basePath."]).also {
34 | BASE_PATH = it
35 | }
36 | if (!baseDir.exists() ||
37 | !baseDir.isDirectory ||
38 | !baseDir.resolve("libfekit.so").exists() ||
39 | !baseDir.resolve("config.json").exists()
40 | || !baseDir.resolve("dtconfig.json").exists()
41 | ) {
42 | error("The base path is invalid, perhaps it is not a directory or something is missing inside.")
43 | } else {
44 | val json = Json { ignoreUnknownKeys = true }
45 | FEBound.initAssertConfig(baseDir)
46 | println("FEBond sum = ${FEBound.checkCurrent()}")
47 | CONFIG = json.decodeFromString(baseDir.resolve("config.json").readText())
48 | .apply { checkIllegal() }
49 | println("Load Package = ${CONFIG.protocol}")
50 | }
51 | }
52 | CONFIG.server.also {
53 | embeddedServer(Netty, host = it.host, port = it.port, module = Application::init)
54 | .start(wait = true)
55 | }
56 |
57 | }
58 |
59 | fun Application.init() {
60 | install(ContentNegotiation) {
61 | json(Json {
62 | prettyPrint = true
63 | isLenient = true
64 | ignoreUnknownKeys = true
65 | })
66 | }
67 | install(StatusPages) {
68 | exception { call, cause ->
69 | if (CONFIG.unidbg.debug) {
70 | cause.printStackTrace()
71 | }
72 | call.respond(APIResult(1, cause.message ?: cause.javaClass.name, call.request.uri))
73 | }
74 | }
75 | routing {
76 | API_LIST.forEach { it(this) }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/tencent/mobileqq/channel/ChannelManager.kt:
--------------------------------------------------------------------------------
1 | package com.tencent.mobileqq.channel
2 |
3 | import com.github.unidbg.linux.android.dvm.DvmObject
4 | import moe.fuqiuluo.unidbg.QSecVM
5 |
6 | object ChannelManager {
7 | fun setChannelProxy(vm: QSecVM, proxy: DvmObject<*>) {
8 | //vm.newInstance("com/tencent/mobileqq/channel/ChannelManager", unique = true)
9 | // .callJniMethod(vm.emulator, "setChannelProxy(Lcom/tencent/mobileqq/channel/ChannelProxy;)V", proxy)
10 | }
11 |
12 | fun initReport(vm: QSecVM, qua: String, version: String, androidOs: String = "12", brand: String = "Redmi", model: String = "23013RK75C",
13 | qimei36: String = vm.global["qimei36"] as? String ?: "", guid: String = vm.global["guid"] as? String ?: "") {
14 | runCatching {
15 | vm.newInstance("com/tencent/mobileqq/channel/ChannelManager", unique = true)
16 | .callJniMethod(vm.emulator, "initReport(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
17 | qua, version, androidOs, brand + model, qimei36, guid
18 | )
19 | }.onFailure {
20 | vm.newInstance("com/tencent/mobileqq/channel/ChannelManager", unique = true)
21 | .callJniMethod(vm.emulator, "initReport(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
22 | qua, version, androidOs, brand + model
23 | )
24 | }
25 | }
26 |
27 | fun onNativeReceive(vm: QSecVM, cmd: String, data: ByteArray, callbackId: Long) {
28 | vm.newInstance("com/tencent/mobileqq/channel/ChannelManager", unique = true)
29 | .callJniMethod(vm.emulator, "onNativeReceive(Ljava/lang/String;[BZJ)V",
30 | cmd, data, true, callbackId)
31 | }
32 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/tencent/mobileqq/channel/SsoPacket.kt:
--------------------------------------------------------------------------------
1 | @file:OptIn(ExperimentalSerializationApi::class)
2 | package com.tencent.mobileqq.channel
3 |
4 | import kotlinx.serialization.ExperimentalSerializationApi
5 | import kotlinx.serialization.Serializable
6 | import kotlinx.serialization.json.JsonNames
7 |
8 | @Serializable
9 | data class SsoPacket(
10 | val cmd: String,
11 | val body: String,
12 | @JsonNames("callback_id")
13 | val callbackId: Long
14 | )
15 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/tencent/mobileqq/dt/Dtn.kt:
--------------------------------------------------------------------------------
1 | package com.tencent.mobileqq.dt
2 |
3 | import com.github.unidbg.linux.android.dvm.DvmObject
4 | import moe.fuqiuluo.unidbg.QSecVM
5 |
6 | object Dtn {
7 | fun initContext(vm: QSecVM, context: DvmObject<*>) {
8 | runCatching {
9 | vm.newInstance("com/tencent/mobileqq/dt/Dtn", unique = true)
10 | .callJniMethod(vm.emulator, "initContext(Landroid/content/Context;)V", context)
11 | }.onFailure {
12 | vm.newInstance("com/tencent/mobileqq/dt/Dtn", unique = true)
13 | .callJniMethod(vm.emulator, "initContext(Landroid/content/Context;Ljava/lang/String;)V",
14 | context, "/data/user/0/${vm.envData.packageName}/files/5463306EE50FE3AA")
15 | }
16 | }
17 |
18 | fun initLog(vm: QSecVM, logger: DvmObject<*>) {
19 | vm.newInstance("com/tencent/mobileqq/dt/Dtn", unique = true)
20 | .callJniMethod(vm.emulator, "initLog(Lcom/tencent/mobileqq/fe/IFEKitLog;)V", logger)
21 | }
22 |
23 | fun initUin(vm: QSecVM, uin: String) {
24 | vm.newInstance("com/tencent/mobileqq/dt/Dtn", unique = true)
25 | .callJniMethod(vm.emulator, "initUin(Ljava/lang/String;)V", uin)
26 | }
27 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/tencent/mobileqq/fe/FEKit.kt:
--------------------------------------------------------------------------------
1 | package com.tencent.mobileqq.fe
2 |
3 | import com.github.unidbg.linux.android.dvm.BaseVM
4 | import com.tencent.mobileqq.channel.ChannelManager
5 | import com.tencent.mobileqq.dt.Dtn
6 | import com.tencent.mobileqq.qsec.qsecurity.DeepSleepDetector
7 | import com.tencent.mobileqq.qsec.qsecurity.QSec
8 | import com.tencent.mobileqq.sign.QQSecuritySign
9 | import moe.fuqiuluo.unidbg.QSecVM
10 |
11 | object FEKit {
12 | fun init(vm: QSecVM, uin: String = "0") {
13 | if ("fekit" in vm.global) return
14 | vm.global["uin"] = uin
15 |
16 | if ("DeepSleepDetector" !in vm.global) {
17 | vm.global["DeepSleepDetector"] = DeepSleepDetector()
18 | }
19 |
20 | QQSecuritySign.initSafeMode(vm, false)
21 | QQSecuritySign.dispatchEvent(vm, "Kicked", uin)
22 |
23 | val context = (vm.vm as BaseVM).resolveClass("android/content/Context", vm.vm.resolveClass("java/io/File")).newObject(null)
24 |
25 | Dtn.initContext(vm, context)
26 |
27 | Dtn.initLog(vm, vm.newInstance("com/tencent/mobileqq/fe/IFEKitLog"))
28 | Dtn.initUin(vm, uin)
29 |
30 | QSec.doSomething(vm, context)
31 |
32 | ChannelManager.setChannelProxy(vm, vm.newInstance("com/tencent/mobileqq/channel/ChannelProxy"))
33 | ChannelManager.initReport(vm, vm.envData.qua, "6.100.248") // TODO(maybe check?)
34 | }
35 |
36 | fun changeUin(vm: QSecVM, uin: String) {
37 | vm.global["uin"] = uin
38 |
39 | Dtn.initUin(vm, uin)
40 | QQSecuritySign.dispatchEvent(vm, "Kicked", uin)
41 | }
42 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/tencent/mobileqq/qsec/qsecdandelionsdk/Dandelion.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("LocalVariableName")
2 | package com.tencent.mobileqq.qsec.qsecdandelionsdk
3 |
4 | import com.github.unidbg.linux.android.dvm.DvmObject
5 | import moe.fuqiuluo.unidbg.QSecVM
6 |
7 | object Dandelion {
8 | @JvmStatic
9 | fun energy(vm: QSecVM, data: String, salt: ByteArray): ByteArray {
10 | val Dandelion = vm.newInstance("com.tencent.mobileqq.qsec.qsecdandelionsdk/Dandelion", unique = true)
11 | return Dandelion.callJniMethodObject>(vm.emulator,
12 | "energy(Ljava/lang/Object;Ljava/lang/Object;)[B", data, salt).value as ByteArray
13 | }
14 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/tencent/mobileqq/qsec/qsecurity/DeepSleepDetector.kt:
--------------------------------------------------------------------------------
1 | package com.tencent.mobileqq.qsec.qsecurity
2 | class DeepSleepDetector {
3 | val lock: Any = Any()
4 | val checkRunnable = CheckRunnable()
5 | var stopped: Boolean = false
6 |
7 | init {
8 | Thread(checkRunnable).start()
9 | }
10 |
11 | fun getCheckResult(): Float {
12 | return checkRunnable.c()
13 | }
14 |
15 | inner class CheckRunnable: Runnable {
16 | var f: Long = 0
17 | val st = System.currentTimeMillis()
18 |
19 | fun c(): Float {
20 | val ela = System.currentTimeMillis() - st
21 | return (ela / 1000.0f) - f
22 | }
23 |
24 | override fun run() {
25 | while (!stopped) {
26 | synchronized(lock) {
27 | f++
28 | Thread.sleep(1000)
29 | }
30 | }
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/tencent/mobileqq/qsec/qsecurity/QSec.kt:
--------------------------------------------------------------------------------
1 | package com.tencent.mobileqq.qsec.qsecurity
2 |
3 | import com.github.unidbg.linux.android.dvm.BaseVM
4 | import com.github.unidbg.linux.android.dvm.DvmObject
5 | import com.github.unidbg.linux.android.dvm.array.ByteArray
6 | import moe.fuqiuluo.unidbg.QSecVM
7 |
8 | object QSec {
9 | fun doSomething(vm: QSecVM, context: DvmObject<*>) {
10 | vm.newInstance("com/tencent/mobileqq/qsec/qsecurity/QSec", unique = true)
11 | .callJniMethodInt(vm.emulator, "doSomething(Landroid/content/Context;I)I", context, 1)
12 | }
13 |
14 | fun getEst(vm: QSecVM): ByteArray? {
15 | val context = (vm.vm as BaseVM).resolveClass("android/content/Context", vm.vm.resolveClass("java/io/File")).newObject(null)
16 |
17 | return vm.newInstance("com/tencent/mobileqq/qsec/qsecest/QsecEst", unique = true)
18 | .callJniMethodObject(
19 | vm.emulator, "d(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)[B",
20 | context, vm.envData.guid, "")
21 | }
22 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/tencent/mobileqq/sign/QQSecuritySign.kt:
--------------------------------------------------------------------------------
1 | package com.tencent.mobileqq.sign
2 |
3 | import com.github.unidbg.linux.android.dvm.BaseVM
4 | import com.github.unidbg.linux.android.dvm.DvmObject
5 | import moe.fuqiuluo.unidbg.QSecVM
6 | import moe.fuqiuluo.utils.BytesUtil
7 | import moe.fuqiuluo.utils.EMPTY_BYTE_ARRAY
8 |
9 | object QQSecuritySign {
10 | data class SignResult(
11 | var sign: ByteArray = EMPTY_BYTE_ARRAY,
12 | var extra: ByteArray = EMPTY_BYTE_ARRAY,
13 | var token: ByteArray = EMPTY_BYTE_ARRAY
14 | )
15 |
16 | class SignResultObject(vm: BaseVM, signResult: SignResult = SignResult()): DvmObject(vm.resolveClass("com/tencent/mobileqq/sign/QQSecuritySign\$SignResult"), signResult) {
17 | fun setToken(token: ByteArray) {
18 | value.token = token
19 | }
20 |
21 | fun setSign(sign: ByteArray) {
22 | value.sign = sign
23 | }
24 |
25 | fun setExtra(extra: ByteArray) {
26 | value.extra = extra
27 | }
28 | }
29 |
30 | fun initSafeMode(vm: QSecVM, safe: Boolean) {
31 | vm.newInstance("com/tencent/mobileqq/sign/QQSecuritySign", unique = true)
32 | .callJniMethod(vm.emulator, "initSafeMode(Z)V", safe)
33 | }
34 |
35 | fun dispatchEvent(vm: QSecVM, cmd: String = "", uin: String = "0") {
36 | runCatching {
37 | vm.newInstance("com/tencent/mobileqq/sign/QQSecuritySign", unique = true)
38 | .callJniMethod(vm.emulator, "dispatchEvent(Ljava/lang/String;Ljava/lang/String;Lcom/tencent/mobileqq/fe/EventCallback;)V",
39 | cmd, uin, vm.newInstance("com/tencent/mobileqq/fe/EventCallback", unique = true))
40 | }.onFailure {
41 | vm.newInstance("com/tencent/mobileqq/sign/QQSecuritySign", unique = true)
42 | .callJniMethod(vm.emulator, "dispatchEvent(Ljava/lang/String;Ljava/lang/String;)V", cmd, uin)
43 | }
44 | }
45 |
46 | fun requestToken(vm: QSecVM) {
47 | vm.newInstance("com/tencent/mobileqq/sign/QQSecuritySign", unique = true)
48 | .callJniMethod(vm.emulator, "requestToken()V")
49 | }
50 |
51 | fun getSign(vm: QSecVM, qua: String, cmd: String, buffer: ByteArray, seq: Int, uin: String, qsec: DvmObject<*> = vm.newInstance("com/tencent/mobileqq/qsec/qsecurity/QSec", unique = true)): SignResultObject {
52 | return runCatching {
53 | (vm.newInstance("com/tencent/mobileqq/sign/QQSecuritySign", unique = true)
54 | .callJniMethodObject(vm.emulator, "getSign(Lcom/tencent/mobileqq/qsec/qsecurity/QSec;Ljava/lang/String;Ljava/lang/String;[B[BLjava/lang/String;)Lcom/tencent/mobileqq/sign/QQSecuritySign\$SignResult;",
55 | qsec, qua, cmd, buffer, BytesUtil.int32ToBuf(seq), uin) as SignResultObject)
56 | }.getOrNull() ?: getSign(vm, qua, cmd, buffer, seq, uin, qsec)
57 | }
58 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/tencent/secprotocol/ByteData.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNUSED_VARIABLE", "LocalVariableName")
2 | package com.tencent.secprotocol
3 |
4 | import com.github.unidbg.linux.android.dvm.BaseVM
5 | import com.github.unidbg.linux.android.dvm.DvmObject
6 | import com.github.unidbg.linux.android.dvm.array.ArrayObject
7 | import moe.fuqiuluo.unidbg.QSecVM
8 |
9 | object ByteData {
10 | fun getByte(vm: QSecVM, uin: String, data: String, salt: ByteArray, guid: String): ByteArray {
11 | val context = (vm.vm as BaseVM).resolveClass("android/content/Context", vm.vm.resolveClass("java/io/File")).newObject(null)
12 | val method = "getByte(Landroid/content/Context;JJJJLjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)[B"
13 | val ByteData = vm.newInstance("com/tencent/secprotocol/ByteData", unique = true)
14 | val obj = ArrayObject.newStringArray(vm.vm, *arrayOf("1", "1", uin, data, guid, "0", "", "init", vm.envData.packageName + ":MSF"))
15 | return ByteData.callJniMethodObject>(vm.emulator, method,
16 | context, 1L, 0L, 0L, 0L, obj, "", uin, salt
17 | ).value as ByteArray
18 | }
19 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/api/added_sign.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.api
2 |
3 | import CONFIG
4 | import com.tencent.mobileqq.qsec.qsecdandelionsdk.Dandelion
5 | import io.ktor.server.application.*
6 | import io.ktor.server.response.*
7 | import io.ktor.server.routing.*
8 | import io.ktor.utils.io.core.*
9 | import moe.fuqiuluo.comm.EnvData
10 | import moe.fuqiuluo.ext.fetchGet
11 | import moe.fuqiuluo.ext.toByteArray
12 | import moe.fuqiuluo.ext.toHexString
13 | import moe.fuqiuluo.unidbg.session.SessionManager
14 |
15 | fun Routing.addedSign() {
16 | get("/friend_sign") {
17 | val addUin = fetchGet("add_uin")!!
18 | val source = fetchGet("source")!!
19 | val uin = fetchGet("uin")!!.toLong()
20 |
21 | val session = initSession(uin) ?: run {
22 | val androidId = fetchGet("android_id", def = "")
23 | val guid = fetchGet("guid", def = "")
24 | if (androidId.isNullOrEmpty() || guid.isNullOrEmpty()) {
25 | throw MissingKeyError
26 | }
27 | SessionManager.register(EnvData(uin, androidId, guid.lowercase(), "", CONFIG.protocol.qua, CONFIG.protocol.version, CONFIG.protocol.code))
28 | findSession(uin)
29 | }
30 |
31 | val sign = session.withRuntime {
32 | Dandelion.energy(session.vm, "add_friend", BytePacketBuilder().also {
33 | it.writeLong(uin)
34 | it.writeLong(addUin.toLong())
35 | it.writeInt(source.toInt())
36 | }.toByteArray())
37 | }
38 | if (sign == null) {
39 | call.respond(APIResult(-1, "failed", null))
40 | } else {
41 | call.respond(APIResult(0, "success", sign.toHexString()))
42 | }
43 | }
44 | get("/group_sign") {
45 | val addUin = fetchGet("group_uin")!!
46 | val source = fetchGet("source")!!
47 | val subsource = fetchGet("sub_source")!!
48 | val uin = fetchGet("uin")!!.toLong()
49 |
50 | val session = initSession(uin) ?: run {
51 | val androidId = fetchGet("android_id", def = "")
52 | val guid = fetchGet("guid", def = "")
53 | if (androidId.isNullOrEmpty() || guid.isNullOrEmpty()) {
54 | throw MissingKeyError
55 | }
56 | SessionManager.register(EnvData(uin, androidId, guid.lowercase(), "", CONFIG.protocol.qua, CONFIG.protocol.version, CONFIG.protocol.code))
57 | findSession(uin)
58 | }
59 |
60 | val sign = session.withRuntime {
61 | Dandelion.energy(session.vm, "add_group", BytePacketBuilder().also {
62 | it.writeLong(uin)
63 | it.writeLong(addUin.toLong())
64 | it.writeInt(source.toInt())
65 | it.writeInt(subsource.toInt())
66 | }.toByteArray())
67 | }
68 | if (sign == null) {
69 | call.respond(APIResult(-1, "failed", null))
70 | } else {
71 | call.respond(APIResult(0, "success", sign.toHexString()))
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/api/energy.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.api
2 |
3 | import CONFIG
4 | import com.tencent.crypt.Crypt
5 | import com.tencent.mobileqq.qsec.qsecdandelionsdk.Dandelion
6 | import com.tencent.mobileqq.qsec.qsecurity.QSec
7 | import com.tencent.secprotocol.ByteData
8 | import io.ktor.server.application.*
9 | import io.ktor.server.response.*
10 | import io.ktor.server.routing.*
11 | import moe.fuqiuluo.comm.EnvData
12 | import moe.fuqiuluo.ext.failure
13 | import moe.fuqiuluo.ext.fetchGet
14 | import moe.fuqiuluo.ext.hex2ByteArray
15 | import moe.fuqiuluo.ext.toHexString
16 | import moe.fuqiuluo.unidbg.session.SessionManager
17 | import moe.fuqiuluo.utils.EMPTY_BYTE_ARRAY
18 | import moe.fuqiuluo.utils.MD5
19 | import java.nio.ByteBuffer
20 |
21 | fun Routing.energy() {
22 | get("/custom_energy") {
23 | val uin = fetchGet("uin")!!.toLong()
24 | val data = fetchGet("data")!!
25 | val salt = fetchGet("salt")!!.hex2ByteArray()
26 |
27 | val session = initSession(uin) ?: run {
28 | val androidId = fetchGet("android_id", def = "")
29 | val guid = fetchGet("guid", def = "")
30 | if (androidId.isNullOrEmpty() || guid.isNullOrEmpty()) {
31 | throw MissingKeyError
32 | }
33 | SessionManager.register(EnvData(uin, androidId, guid.lowercase(), "", CONFIG.protocol.qua, CONFIG.protocol.version, CONFIG.protocol.code))
34 | findSession(uin)
35 | }
36 |
37 | val sign = session.withRuntime {
38 | Dandelion.energy(session.vm, data, salt)
39 | }
40 | if (sign == null) {
41 | call.respond(APIResult(-1, "failed", null))
42 | } else {
43 | call.respond(APIResult(0, "success", sign.toHexString()))
44 | }
45 | }
46 |
47 | get("/get_byte") {
48 | val uin = fetchGet("uin")!!.toLong()
49 | val guid = fetchGet("guid", err = "lack of guid") ?: return@get
50 |
51 | val session = initSession(uin) ?: run {
52 | val androidId = fetchGet("android_id", def = "")
53 | if (androidId.isNullOrEmpty() || guid.isEmpty()) {
54 | throw MissingKeyError
55 | }
56 | SessionManager.register(EnvData(uin, androidId, guid.lowercase(), "", CONFIG.protocol.qua, CONFIG.protocol.version, CONFIG.protocol.code))
57 | findSession(uin)
58 | }
59 |
60 | val data = fetchGet("data")!!
61 | if(!(data.startsWith("810_") || data.startsWith("812_"))) {
62 | failure(-2, "data参数不合法")
63 | }
64 |
65 | val mode = fetchGet("mode", def = when(data) {
66 | "810_d", "810_a", "810_f", "810_9" -> "v2"
67 | "810_2", "810_25", "810_7", "810_24" -> "v1"
68 | "812_a" -> "v3"
69 | "812_5" -> "v4"
70 | else -> ""
71 | })?.also {
72 | if (it.isBlank()) failure(-3, "无法自动决断mode,请主动提供")
73 | }
74 |
75 | val salt = when (mode) {
76 | "v1" -> {
77 | val version = fetchGet("version", err = "lack of version") ?: return@get
78 | val sub = data.substring(4).toInt(16)
79 | val guidBytes = guid.hex2ByteArray()
80 | val salt = ByteBuffer.allocate(8 + 2 + guidBytes.size + 2 + 10 + 4)
81 | salt.putLong(uin)
82 | salt.putShort(guidBytes.size.toShort())
83 | salt.put(guidBytes)
84 | salt.putShort(version.length.toShort())
85 | salt.put(version.toByteArray())
86 | salt.putInt(sub)
87 | salt.array()
88 | }
89 | "v2" -> {
90 | val version = fetchGet("version", err = "lack of version") ?: return@get
91 | val guidBytes = guid.hex2ByteArray()
92 | val sub = data.substring(4).toInt(16)
93 | val salt = ByteBuffer.allocate(4 + 2 + guidBytes.size + 2 + 10 + 4 + 4)
94 | salt.putInt(0)
95 | salt.putShort(guidBytes.size.toShort())
96 | salt.put(guidBytes)
97 | salt.putShort(version.length.toShort())
98 | salt.put(version.toByteArray())
99 | salt.putInt(sub)
100 | salt.putInt(0)
101 | salt.array()
102 | }
103 | "v3" -> { // 812_a
104 | val version = fetchGet("version", err = "lack of version") ?: return@get
105 | val phone = (fetchGet("phone", err = "lack of phone") ?: return@get).toByteArray() // 86-xxx
106 | val salt = ByteBuffer.allocate(phone.size + 2 + 2 + version.length + 2)
107 | salt.put(phone)
108 | salt.putShort(0)
109 | salt.putShort(version.length.toShort())
110 | salt.put(version.toByteArray())
111 | salt.putShort(0)
112 | salt.array()
113 | }
114 | "v4" -> { // 812_5
115 | val receipt = (fetchGet("receipt", err = "lack of receipt") ?: return@get).toByteArray()
116 | val code = fetchGet("code", err = "lack of code") ?: return@get
117 | val key = MD5.toMD5Byte(code)
118 | val encrypt = Crypt().encrypt(receipt, key)
119 | val salt = ByteBuffer.allocate(receipt.size + 2 + encrypt.size)
120 | salt.put(receipt)
121 | salt.putShort(encrypt.size.toShort())
122 | salt.put(encrypt)
123 | salt.array()
124 | }
125 | else -> {
126 | EMPTY_BYTE_ARRAY
127 | }
128 | }
129 |
130 | val sign = session.withRuntime {
131 | session.vm.global["est_data"] = QSec.getEst(session.vm)
132 | ByteData.getByte(session.vm, uin.toString(), data, salt, guid)
133 | }
134 |
135 | if (sign == null) {
136 | call.respond(APIResult(-1, "failed", null))
137 | } else {
138 | call.respond(APIResult(0, "success", sign.toHexString()))
139 | }
140 | }
141 |
142 | get("/energy") {
143 | val uin = fetchGet("uin")!!.toLong()
144 | val session = initSession(uin) ?: run {
145 | val androidId = fetchGet("android_id", def = "")
146 | val guid = fetchGet("guid", def = "")
147 | if (androidId.isNullOrEmpty() || guid.isNullOrEmpty()) {
148 | throw MissingKeyError
149 | }
150 | SessionManager.register(EnvData(uin, androidId, guid.lowercase(), "", CONFIG.protocol.qua, CONFIG.protocol.version, CONFIG.protocol.code))
151 | findSession(uin)
152 | }
153 |
154 | val data = fetchGet("data")!!
155 | if(!(data.startsWith("810_") || data.startsWith("812_"))) {
156 | failure(-2, "data参数不合法")
157 | }
158 |
159 | val mode = fetchGet("mode", def = when(data) {
160 | "810_d", "810_a", "810_f", "810_9" -> "v2"
161 | "810_2", "810_25", "810_7", "810_24" -> "v1"
162 | "812_a" -> "v3"
163 | "812_5" -> "v4"
164 | else -> ""
165 | })?.also {
166 | if (it.isBlank()) failure(-3, "无法自动决断mode,请主动提供")
167 | }
168 |
169 | val salt = when (mode) {
170 | "v1" -> {
171 | val version = fetchGet("version", err = "lack of version") ?: return@get
172 | val guid = (fetchGet("guid", err = "lack of guid") ?: return@get).hex2ByteArray()
173 | val sub = data.substring(4).toInt(16)
174 | val salt = ByteBuffer.allocate(8 + 2 + guid.size + 2 + 10 + 4)
175 | salt.putLong(uin)
176 | salt.putShort(guid.size.toShort())
177 | salt.put(guid)
178 | salt.putShort(version.length.toShort())
179 | salt.put(version.toByteArray())
180 | salt.putInt(sub)
181 | salt.array()
182 | }
183 | "v2" -> {
184 | val version = fetchGet("version", err = "lack of version") ?: return@get
185 | val guid = (fetchGet("guid", err = "lack of guid") ?: return@get).hex2ByteArray()
186 | val sub = data.substring(4).toInt(16)
187 | val salt = ByteBuffer.allocate(4 + 2 + guid.size + 2 + 10 + 4 + 4)
188 | salt.putInt(0)
189 | salt.putShort(guid.size.toShort())
190 | salt.put(guid)
191 | salt.putShort(version.length.toShort())
192 | salt.put(version.toByteArray())
193 | salt.putInt(sub)
194 | salt.putInt(0)
195 | salt.array()
196 | }
197 | "v3" -> { // 812_a
198 | val version = fetchGet("version", err = "lack of version") ?: return@get
199 | val phone = (fetchGet("phone", err = "lack of phone") ?: return@get).toByteArray() // 86-xxx
200 | val salt = ByteBuffer.allocate(phone.size + 2 + 2 + version.length + 2)
201 | // 38 36 2D 31 37 33 36 30 32 32 39 31 37 32
202 | // 00 00
203 | // 00 06
204 | // 38 2E 39 2E 33 38
205 | // 00 00
206 | // result => 0C051B17347DF3B8EFDE849FC233C88DBEA23F5277099BB313A9CD000000004B744F7A00000000
207 | salt.put(phone)
208 | //println(String(phone))
209 | salt.putShort(0)
210 | salt.putShort(version.length.toShort())
211 | salt.put(version.toByteArray())
212 | salt.putShort(0)
213 | salt.array()
214 | }
215 | "v4" -> { // 812_5
216 | val receipt = (fetchGet("receipt", err = "lack of receipt") ?: return@get).toByteArray()
217 | val code = fetchGet("code", err = "lack of code") ?: return@get
218 | val key = MD5.toMD5Byte(code)
219 | val encrypt = Crypt().encrypt(receipt, key)
220 | val salt = ByteBuffer.allocate(receipt.size + 2 + encrypt.size)
221 | salt.put(receipt)
222 | salt.putShort(encrypt.size.toShort())
223 | salt.put(encrypt)
224 | salt.array()
225 | }
226 | else -> {
227 | EMPTY_BYTE_ARRAY
228 | }
229 | }
230 |
231 | val sign = session.withRuntime {
232 | Dandelion.energy(session.vm, data, salt)
233 | }
234 |
235 | if (sign == null) {
236 | call.respond(APIResult(-1, "failed", null))
237 | } else {
238 | call.respond(APIResult(0, "success", sign.toHexString()))
239 | }
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/api/exceptions.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.api
2 |
3 | import java.lang.RuntimeException
4 |
5 | object SessionNotFoundError: RuntimeException("Uin is not registered.")
6 |
7 | object WrongKeyError: RuntimeException("Wrong API key.")
8 |
9 | object MissingKeyError: RuntimeException("First use must be submitted with android_id and guid.")
10 |
11 | object BlackListError: RuntimeException("Blacklist uin.")
12 |
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/api/main.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.api
2 |
3 | import BuildConfig
4 | import CONFIG
5 | import io.ktor.server.application.*
6 | import io.ktor.server.response.*
7 | import io.ktor.server.routing.*
8 | import kotlinx.serialization.Contextual
9 | import kotlinx.serialization.Serializable
10 | import moe.fuqiuluo.comm.Protocol
11 | import java.lang.management.ManagementFactory
12 |
13 | @Serializable
14 | data class APIResult(
15 | val code: Int,
16 | val msg: String = "",
17 | @Contextual
18 | val data: T? = null
19 | )
20 |
21 | @Serializable
22 | data class APIInfo(
23 | val version: String,
24 | val protocol: Protocol,
25 | val pid: Int
26 | )
27 |
28 | fun Routing.index() {
29 | get("/") {
30 | call.respond(APIResult(0, "IAA 云天明 章北海", APIInfo(
31 | version = BuildConfig.version,
32 | protocol = CONFIG.protocol,
33 | pid = runCatching{ ManagementFactory.getRuntimeMXBean().name.split("@")[0].toInt() }.getOrNull() ?: -1
34 | )))
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/api/qsign.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNCHECKED_CAST")
2 |
3 | package moe.fuqiuluo.api
4 |
5 | import CONFIG
6 | import com.tencent.mobileqq.channel.SsoPacket
7 | import com.tencent.mobileqq.sign.QQSecuritySign
8 | import io.ktor.server.application.*
9 | import io.ktor.server.request.*
10 | import io.ktor.server.response.*
11 | import io.ktor.server.routing.*
12 | import io.ktor.util.pipeline.*
13 | import kotlinx.serialization.Serializable
14 | import moe.fuqiuluo.comm.EnvData
15 | import moe.fuqiuluo.ext.*
16 | import moe.fuqiuluo.unidbg.session.SessionManager
17 |
18 | fun Routing.sign() {
19 | get("/sign") {
20 | val uin = fetchGet("uin")!!
21 | val qua = fetchGet("qua", CONFIG.protocol.qua)!!
22 | val cmd = fetchGet("cmd")!!
23 | val seq = fetchGet("seq")!!.toInt()
24 | val buffer = fetchGet("buffer")!!.hex2ByteArray()
25 | val qimei36 = fetchGet("qimei36", def = "")!!
26 |
27 | val androidId = fetchGet("android_id", def = "")!!
28 | val guid = fetchGet("guid", def = "")!!
29 |
30 | requestSign(cmd, uin, qua, seq, buffer, qimei36, androidId, guid)
31 | }
32 |
33 | post("/sign") {
34 | val param = call.receiveParameters()
35 | val uin = fetchPost(param, "uin")!!
36 | val qua = fetchPost(param, "qua", CONFIG.protocol.qua)!!
37 | val cmd = fetchPost(param, "cmd")!!
38 | val seq = fetchPost(param, "seq")!!.toInt()
39 | val buffer = fetchPost(param, "buffer")!!.hex2ByteArray()
40 | val qimei36 = fetchPost(param, "qimei36", def = "")!!
41 |
42 | val androidId = param["android_id"] ?: ""
43 | val guid = param["guid"] ?: ""
44 |
45 | requestSign(cmd, uin, qua, seq, buffer, qimei36, androidId, guid)
46 | }
47 | }
48 |
49 | @Serializable
50 | private data class Sign(
51 | val token: String,
52 | val extra: String,
53 | val sign: String,
54 | val o3did: String,
55 | val requestCallback: List
56 | )
57 |
58 | private suspend fun PipelineContext.requestSign(
59 | cmd: String,
60 | uin: String,
61 | qua: String,
62 | seq: Int,
63 | buffer: ByteArray,
64 | qimei36: String,
65 | androidId: String,
66 | guid: String
67 | ) {
68 | val session = initSession(uin.toLong()) ?: run {
69 | if (androidId.isEmpty() || guid.isEmpty()) {
70 | throw MissingKeyError
71 | }
72 | SessionManager.register(EnvData(
73 | uin.toLong(),
74 | androidId,
75 | guid.lowercase(),
76 | qimei36,
77 | qua,
78 | CONFIG.protocol.version,
79 | CONFIG.protocol.code
80 | ))
81 | findSession(uin.toLong())
82 | }
83 | val vm = session.vm
84 | if (qimei36.isNotEmpty()) {
85 | vm.global["qimei36"] = qimei36
86 | }
87 |
88 | var o3did = ""
89 | val list = arrayListOf()
90 |
91 | val sign = session.withRuntime {
92 | QQSecuritySign.getSign(vm, qua, cmd, buffer, seq, uin).value.also {
93 | o3did = vm.global["o3did"] as? String ?: ""
94 | val requiredPacket = vm.global["PACKET"] as ArrayList
95 | list.addAll(requiredPacket)
96 | requiredPacket.clear()
97 | }
98 | }
99 |
100 | if (sign == null) {
101 | call.respond(APIResult(-1, "failed", null))
102 | } else {
103 | call.respond(
104 | APIResult(
105 | 0, "success", Sign(
106 | sign.token.toHexString(),
107 | sign.extra.toHexString(),
108 | sign.sign.toHexString(), o3did, list
109 | )
110 | )
111 | )
112 | }
113 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/api/register.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.api
2 |
3 | import CONFIG
4 | import io.ktor.server.application.*
5 | import io.ktor.server.response.*
6 | import io.ktor.server.routing.*
7 | import moe.fuqiuluo.comm.EnvData
8 | import moe.fuqiuluo.ext.failure
9 | import moe.fuqiuluo.ext.fetchGet
10 | import moe.fuqiuluo.unidbg.session.SessionManager
11 |
12 | fun Routing.register() {
13 | get("/register") {
14 | val uin = fetchGet("uin")!!.toLong()
15 | val androidId = fetchGet("android_id")!!
16 | val guid = fetchGet("guid")!!.lowercase()
17 | val qimei36 = fetchGet("qimei36")!!.lowercase()
18 |
19 | val key = fetchGet("key")!!
20 |
21 | val qua = fetchGet("qua", CONFIG.protocol.qua)!!
22 | val version = fetchGet("version", CONFIG.protocol.version)!!
23 | val code = fetchGet("code", CONFIG.protocol.code)!!
24 |
25 | val hasRegister = uin in SessionManager
26 | if (key == CONFIG.key) {
27 | SessionManager.register(EnvData(uin, androidId, guid, qimei36, qua, version, code))
28 | if (hasRegister) {
29 | call.respond(APIResult(0, "The QQ has already loaded an instance, so this time it is deleting the existing instance and creating a new one.", ""))
30 | } else {
31 | call.respond(APIResult(0, "Instance loaded successfully.", ""))
32 | }
33 | } else {
34 | throw WrongKeyError
35 | }
36 | }
37 |
38 | get("/destroy") {
39 | val uin = fetchGet("uin")!!.toLong()
40 | val key = fetchGet("key")!!
41 |
42 | if (key == CONFIG.key) {
43 | if(uin in SessionManager){
44 | SessionManager.close(uin)
45 | call.respond(APIResult(0, "Instance destroyed successfully.", ""))
46 | } else {
47 | failure(1, "Instance does not exist.")
48 | }
49 | } else {
50 | throw WrongKeyError
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/api/request_token.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNCHECKED_CAST")
2 | package moe.fuqiuluo.api
3 |
4 | import com.tencent.mobileqq.channel.SsoPacket
5 | import com.tencent.mobileqq.sign.QQSecuritySign
6 | import io.ktor.server.application.*
7 | import io.ktor.server.response.*
8 | import io.ktor.server.routing.*
9 | import kotlinx.coroutines.sync.Mutex
10 | import moe.fuqiuluo.ext.fetchGet
11 |
12 | fun Routing.requestToken() {
13 | get("/request_token") {
14 | val uin = fetchGet("uin")!!.toLong()
15 | val session = findSession(uin)
16 | val isForced = fetchGet("force", def = "false")
17 |
18 | val vm = session.vm
19 |
20 | if ("HAS_SUBMIT" !in vm.global && isForced == "false") {
21 | call.respond(APIResult(-1, "QSign not initialized, unable to request_token, please submit the initialization package first.", ""))
22 | } else {
23 | val isSuccessful = true
24 | val list = arrayListOf()
25 | session.withRuntime {
26 | val lock = vm.global["mutex"] as Mutex
27 | lock.tryLock()
28 | QQSecuritySign.requestToken(vm)
29 | }
30 | call.respond(APIResult(if (!isSuccessful) -1 else 0, if (!isSuccessful) "request_token timeout" else "submit success", list))
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/api/session.kt:
--------------------------------------------------------------------------------
1 | @file:OptIn(DelicateCoroutinesApi::class)
2 | package moe.fuqiuluo.api
3 |
4 | import CONFIG
5 | import kotlinx.coroutines.*
6 | import moe.fuqiuluo.unidbg.session.Session
7 | import moe.fuqiuluo.unidbg.session.SessionManager
8 |
9 | fun initSession(uin: Long): Session? {
10 | return SessionManager.get(uin) ?: if (!CONFIG.autoRegister) {
11 | throw SessionNotFoundError
12 | } else {
13 | null
14 | }
15 | }
16 |
17 | fun findSession(uin: Long): Session {
18 | return SessionManager.get(uin) ?: throw SessionNotFoundError
19 | }
20 |
21 | internal inline fun Session.withRuntime(crossinline action: () -> T): T? {
22 | val t = action()
23 | pool.release(this)
24 | return t
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/api/submit.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.api
2 |
3 | import com.tencent.mobileqq.channel.ChannelManager
4 | import io.ktor.server.application.*
5 | import io.ktor.server.response.*
6 | import io.ktor.server.routing.*
7 | import moe.fuqiuluo.ext.fetchGet
8 | import moe.fuqiuluo.ext.hex2ByteArray
9 |
10 |
11 | fun Routing.submit() {
12 | get("/submit") {
13 | val uin = fetchGet("uin")!!.toLong()
14 | val cmd = fetchGet("cmd")!!
15 | val callbackId = fetchGet("callback_id")!!.toLong()
16 | val buffer = fetchGet("buffer")!!.hex2ByteArray()
17 |
18 | val session = findSession(uin)
19 |
20 | session.withRuntime {
21 | ChannelManager.onNativeReceive(session.vm, cmd, buffer, callbackId)
22 | session.vm.global["HAS_SUBMIT"] = true
23 | }
24 |
25 | call.respond(APIResult(0, "submit success", ""))
26 | }
27 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/comm/ArgsParser.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.comm
2 |
3 | import moe.fuqiuluo.ext.*
4 |
5 | class ArgsParser(
6 | args: StringArray
7 | ) {
8 | private val map = hashMapOf()
9 |
10 | init {
11 | args.forEach {
12 | it.substring(if (it.startsWith("--")) 2
13 | else if (it.startsWith("-")) 1
14 | else error("Not support the expr.")).split("=").also {
15 | map[it[0]] = it.slice(1 until it.size).joinToString("")
16 | }
17 | }
18 | }
19 |
20 | operator fun get(key: String): String? {
21 | return map[key]
22 | }
23 |
24 | fun getOrDefault(key: String, def: String?): String? {
25 | return get(key) ?: def
26 | }
27 |
28 | operator fun get(key: String, err: String): String {
29 | require(key in this) { err }
30 | return this[key]!!
31 | }
32 |
33 | operator fun contains(key: String): Boolean {
34 | return key in map
35 | }
36 | }
37 |
38 | operator fun StringArray.invoke(): ArgsParser {
39 | return ArgsParser(this)
40 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/comm/QSignConfig.kt:
--------------------------------------------------------------------------------
1 | @file:OptIn(ExperimentalSerializationApi::class)
2 | package moe.fuqiuluo.comm
3 |
4 | import CONFIG
5 | import kotlinx.serialization.ExperimentalSerializationApi
6 | import kotlinx.serialization.SerialName
7 | import kotlinx.serialization.Serializable
8 | import kotlinx.serialization.json.JsonNames
9 |
10 | @Serializable
11 | data class Server(
12 | var host: String,
13 | var port: Int
14 | )
15 |
16 | @Serializable
17 | data class EnvData(
18 | var uin: Long,
19 | @JsonNames("androidId", "android_id", "imei")
20 | var androidId: String,
21 | var guid: String,
22 | var qimei36: String,
23 |
24 | var qua: String = CONFIG.protocol.qua,
25 | var version: String = CONFIG.protocol.version,
26 | var code: String = CONFIG.protocol.code,
27 | var packageName: String = CONFIG.protocol.packageName ?: "com.tencent.mobileqq",
28 | )
29 |
30 | @Serializable
31 | data class Protocol(
32 | var qua: String,
33 | var version: String,
34 | var code: String,
35 | @SerialName("package_name")
36 | var packageName: String? = null,
37 | )
38 |
39 | @Serializable
40 | data class UnidbgConfig(
41 | var dynarmic: Boolean = false,
42 | var unicorn: Boolean = true,
43 | var kvm: Boolean = false,
44 | var debug: Boolean = true,
45 | )
46 |
47 | @Serializable
48 | data class QSignConfig(
49 | var server: Server,
50 | var key: String,
51 | @JsonNames("autoRegister", "auto_register")
52 | var autoRegister:Boolean,
53 | //@JsonNames("reloadInterval", "reload_interval")
54 | //var reloadInterval: Int,
55 | var protocol: Protocol,
56 | var unidbg: UnidbgConfig,
57 | @JsonNames("blackList", "black_list")
58 | var blackList: List? = null,
59 | var count: Int = 1,
60 | @SerialName("share_token") var shareToken: Boolean = true
61 | )
62 |
63 | fun QSignConfig.checkIllegal() {
64 | require(server.port in 1 .. 65535) { "Port is out of range." }
65 | //require(reloadInterval in 20 .. 50) { "ReloadInterval is out of range." }
66 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/ext/ByteArray.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.ext
2 |
3 | import moe.fuqiuluo.utils.BytesUtil
4 | import java.util.*
5 | import kotlin.experimental.xor
6 |
7 | @JvmOverloads fun String.hex2ByteArray(replace: Boolean = false): ByteArray {
8 | val s = if (replace) this.replace(" ", "")
9 | .replace("\n", "")
10 | .replace("\t", "")
11 | .replace("\r", "") else this
12 | val bs = ByteArray(s.length / 2)
13 | for (i in 0 until s.length / 2) {
14 | bs[i] = s.substring(i * 2, i * 2 + 2).toInt(16).toByte()
15 | }
16 | return bs
17 | }
18 |
19 | @JvmOverloads fun ByteArray.toHexString(uppercase: Boolean = true): String = this.joinToString("") {
20 | (it.toInt() and 0xFF).toString(16)
21 | .padStart(2, '0')
22 | .let { s -> if (uppercase) s.lowercase(Locale.getDefault()) else s }
23 | }
24 |
25 | fun ByteArray.xor(key: ByteArray): ByteArray {
26 | val result = ByteArray(this.size)
27 | for (i in 0 until this.size) {
28 | result[i] = (this[i] xor key[i % key.size] xor ((i and 0xFF).toByte()))
29 | }
30 | return result
31 | }
32 |
33 | fun ByteArray.sub(offset: Int, length: Int) = BytesUtil.subByte(this, offset, length)
34 |
35 | fun ByteArray.toAsciiHexString() = joinToString("") {
36 | if (it in 32..127) it.toInt().toChar().toString() else "{${
37 | it.toUByte().toString(16).padStart(2, '0').uppercase(
38 | Locale.getDefault())
39 | }}"
40 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/ext/BytePacket.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.ext
2 |
3 | import com.tencent.crypt.Crypt
4 | import io.ktor.utils.io.core.*
5 | import moe.fuqiuluo.utils.BytesUtil
6 | import kotlin.text.Charsets.UTF_8
7 | import kotlin.text.toByteArray
8 |
9 | inline fun newBuilder() = BytePacketBuilder()
10 |
11 | fun BytePacketBuilder.writeBytes(bytes: ByteArray) = this.writeFully(bytes)
12 |
13 | fun BytePacketBuilder.toByteArray(): ByteArray = use { it.build().readBytes() }
14 |
15 | fun ByteReadPacket.toByteArray(): ByteArray = use { it.readBytes() }
16 |
17 | /**
18 | * 补充功能代码
19 | * @receiver BytePacketBuilder
20 | * @param packet BytePacketBuilder
21 | */
22 | fun BytePacketBuilder.writePacket(packet: BytePacketBuilder) = this.writePacket(packet.build())
23 |
24 | /**
25 | * 写布尔型
26 | * @receiver BytePacketBuilder
27 | * @param z Boolean
28 | */
29 | fun BytePacketBuilder.writeBoolean(z: Boolean) = this.writeByte(if (z) 1 else 0)
30 |
31 | /**
32 | * 自动转换类型
33 | * @receiver BytePacketBuilder
34 | * @param i Int
35 | */
36 | fun BytePacketBuilder.writeShort(i: Int) = this.writeShort(i.toShort())
37 |
38 | fun BytePacketBuilder.writeLongToBuf32(v: Long) {
39 | this.writeBytes(BytesUtil.int64ToBuf32(v))
40 | }
41 |
42 | fun BytePacketBuilder.writeStringWithIntLen(str: String) {
43 | writeBytesWithIntLen(str.toByteArray())
44 | }
45 |
46 | fun BytePacketBuilder.writeStringWithShortLen(str: String) {
47 | writeBytesWithShortLen(str.toByteArray())
48 | }
49 |
50 | fun BytePacketBuilder.writeBytesWithIntLen(bytes: ByteArray) {
51 | writeInt(bytes.size)
52 | writeBytes(bytes)
53 | }
54 |
55 | fun BytePacketBuilder.writeBytesWithShortLen(bytes: ByteArray) {
56 | check(bytes.size <= Short.MAX_VALUE) { "byteArray length is too long" }
57 | writeShort(bytes.size.toShort())
58 | writeBytes(bytes)
59 | }
60 |
61 | inline fun BytePacketBuilder.writeBlockWithIntLen(len : (Int) -> Int = { it }, block: BytePacketBuilder.() -> Unit) {
62 | val builder = newBuilder()
63 | builder.block()
64 | this.writeInt(len(builder.size))
65 | this.writePacket(builder)
66 | builder.close()
67 | }
68 |
69 | inline fun BytePacketBuilder.writeBlockWithShortLen(len : (Int) -> Int = { it }, block: BytePacketBuilder.() -> Unit) {
70 | val builder = newBuilder()
71 | builder.block()
72 | this.writeShort(len(builder.size))
73 | this.writePacket(builder)
74 | builder.close()
75 | }
76 |
77 |
78 | inline fun BytePacketBuilder.writeTeaEncrypt(key: ByteArray, block: BytePacketBuilder.() -> Unit) {
79 | val body = newBuilder()
80 | body.block()
81 | this.writeBytes(Crypt().encrypt(body.toByteArray(), key))
82 | body.close()
83 | }
84 |
85 | fun BytePacketBuilder.writeString(str: String) {
86 | this.writeBytes(str.toByteArray(UTF_8))
87 | }
88 |
89 | fun BytePacketBuilder.writeHex(uHex: String) {
90 | writeBytes(uHex.hex2ByteArray())
91 | }
92 |
93 | fun ByteReadPacket.readString(length: Int) = readBytes(length).decodeToString()
94 |
95 | fun ByteArray.toByteReadPacket() = ByteReadPacket(this)
96 |
97 | inline fun ByteArray.reader(block: ByteReadPacket.() -> Unit) {
98 | this.toByteReadPacket().use(block)
99 | }
100 |
101 | fun ByteReadPacket.readByteReadPacket(length: Int): ByteReadPacket {
102 | return readBytes(length).toByteReadPacket()
103 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/ext/Ktor.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.ext
2 |
3 | import io.ktor.http.*
4 | import io.ktor.server.application.*
5 | import io.ktor.server.response.*
6 | import io.ktor.util.pipeline.*
7 | import moe.fuqiuluo.api.APIResult
8 |
9 | suspend fun PipelineContext.fetchGet(key: String, def: String? = null, err: String? = "Parameter '$key' is missing."): String? {
10 | val data = call.parameters[key] ?: def
11 | if (data == null && err != null) {
12 | failure(1, err)
13 | }
14 | return data
15 | }
16 |
17 | suspend fun PipelineContext.fetchPost(params: Parameters, key: String, def: String? = null, err: String? = "Parameter '$key' is missing."): String? {
18 | val data = params[key] ?: def
19 | if (data == null && err != null) {
20 | failure(1, err)
21 | }
22 | return data
23 | }
24 |
25 | suspend fun PipelineContext.failure(code: Int, msg: String) {
26 | call.respond(APIResult(code, msg, "failed"))
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/ext/Numbers.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.ext
2 |
3 | fun String.toInt(range: IntRange, lazyMessage: () -> Any = { "Failed requirement." }): Int {
4 | val i = toInt()
5 | require(i in range, lazyMessage)
6 | return i
7 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/ext/Types.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.ext
2 |
3 | typealias StringArray = Array
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/ext/Unidbg.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.ext
2 |
3 | import com.github.unidbg.linux.android.dvm.BaseVM
4 | import com.github.unidbg.linux.android.dvm.DvmObject
5 |
6 | fun BaseVM.newInstance(className: String, arg: Any? = null): DvmObject<*> {
7 | return resolveClass(className).newObject(arg)
8 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/unidbg/QSecVm.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.unidbg
2 |
3 | import com.github.unidbg.linux.android.dvm.DvmObject
4 | import com.tencent.mobileqq.qsec.qsecurity.DeepSleepDetector
5 | import moe.fuqiuluo.comm.EnvData
6 | import moe.fuqiuluo.unidbg.env.FileResolver
7 | import moe.fuqiuluo.unidbg.env.QSecJni
8 | import moe.fuqiuluo.unidbg.vm.AndroidVM
9 | import moe.fuqiuluo.unidbg.vm.GlobalData
10 | import java.io.File
11 | import javax.security.auth.Destroyable
12 | import kotlin.system.exitProcess
13 |
14 | class QSecVM(
15 | val coreLibPath: File,
16 | val envData: EnvData,
17 | dynarmic: Boolean,
18 | unicorn: Boolean,
19 | kvm: Boolean
20 | ): Destroyable, AndroidVM(envData.packageName, dynarmic, unicorn, kvm) {
21 | private var destroy: Boolean = false
22 | private var isInit: Boolean = false
23 | internal val global = GlobalData()
24 |
25 | init {
26 | runCatching {
27 | val resolver = FileResolver(23, this@QSecVM)
28 | memory.setLibraryResolver(resolver)
29 | emulator.syscallHandler.addIOResolver(resolver)
30 | vm.setJni(QSecJni(envData, this, global))
31 |
32 | if (envData.packageName == "com.tencent.mobileqq") {
33 | println("QSign-Unidbg 白名单模式")
34 | vm.setWhiteMode(true)
35 | arrayOf(
36 | "android/os/Build\$VERSION",
37 | "android/content/pm/ApplicationInfo",
38 | "com/tencent/mobileqq/fe/IFEKitLog",
39 | "com/tencent/mobileqq/channel/ChannelProxy",
40 | "com/tencent/mobileqq/qsec/qsecurity/QSec",
41 | "com/tencent/mobileqq/qsec/qsecurity/QSecConfig",
42 | "com/tencent/mobileqq/sign/QQSecuritySign\$SignResult",
43 | "java/lang/String",
44 | "com/tencent/mobileqq/qsec/qsecest/QsecEst",
45 | "com/tencent/qqprotect/qsec/QSecFramework",
46 | "com/tencent/mobileqq/dt/app/Dtc",
47 | "android/provider/Settings\$System",
48 | "com/tencent/mobileqq/fe/utils/DeepSleepDetector",
49 | "com/tencent/mobileqq/dt/model/FEBound",
50 | "java/lang/ClassLoader",
51 | "java/lang/Thread",
52 | "android/content/Context",
53 | "android/content/ContentResolver",
54 | "java/io/File",
55 | "java/lang/Integer",
56 | "java/lang/Object",
57 | "com/tencent/mobileqq/sign/QQSecuritySign",
58 | "com/tencent/mobileqq/channel/ChannelManager",
59 | "com/tencent/mobileqq/dt/Dtn",
60 | "com/tencent/mobileqq/qsec/qsecdandelionsdk/Dandelion",
61 | "com/tencent/mobileqq/qsec/qsecprotocol/ByteData",
62 | "com/tencent/mobileqq/qsec/qseccodec/SecCipher",
63 | ).forEach {
64 | vm.addFilterClass(it)
65 | }
66 | } else {
67 | vm.addFilterClass("com/tencent/mobileqq/dt/Dc")
68 | vm.addFilterClass("com/tencent/mobileqq/dt/Dte")
69 | }
70 | }.onFailure {
71 | it.printStackTrace()
72 | }
73 | }
74 |
75 | fun init() {
76 | if (isInit) return
77 | runCatching {
78 | coreLibPath.resolve("libpoxy.so").let {
79 | if (it.exists()) {
80 | loadLibrary(it)
81 | }
82 | }
83 | loadLibrary(coreLibPath.resolve("libfekit.so"))
84 | global["DeepSleepDetector"] = DeepSleepDetector()
85 | this.isInit = true
86 | }.onFailure {
87 | it.printStackTrace()
88 | exitProcess(1)
89 | }
90 | }
91 |
92 | fun newInstance(name: String, value: Any? = null, unique: Boolean = false): DvmObject<*> {
93 | if (unique && name in global) {
94 | return global[name] as DvmObject<*>
95 | }
96 | val obj = findClass(name).newObject(value)
97 | if (unique) {
98 | global[name] = obj
99 | }
100 | return obj
101 | }
102 |
103 | override fun isDestroyed(): Boolean = destroy
104 |
105 | override fun destroy() {
106 | if (isDestroyed) return
107 | this.destroy = true
108 | this.close()
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/unidbg/env/FileResolver.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.unidbg.env
2 |
3 | import CONFIG
4 | import com.github.unidbg.Emulator
5 | import com.github.unidbg.file.FileResult
6 | import com.github.unidbg.file.linux.AndroidFileIO
7 | import com.github.unidbg.linux.android.AndroidResolver
8 | import com.github.unidbg.linux.file.ByteArrayFileIO
9 | import com.github.unidbg.linux.file.DirectoryFileIO
10 | import com.github.unidbg.linux.file.SimpleFileIO
11 | import com.github.unidbg.unix.UnixEmulator
12 | import io.ktor.server.config.*
13 | import moe.fuqiuluo.ext.hex2ByteArray
14 | import moe.fuqiuluo.unidbg.QSecVM
15 | import moe.fuqiuluo.unidbg.env.files.fetchCpuInfo
16 | import moe.fuqiuluo.unidbg.env.files.fetchMemInfo
17 | import moe.fuqiuluo.unidbg.env.files.fetchStat
18 | import moe.fuqiuluo.unidbg.env.files.fetchStatus
19 | import java.io.File
20 | import java.util.UUID
21 | import java.util.logging.Logger
22 |
23 | class FileResolver(
24 | sdk: Int,
25 | val vm: QSecVM
26 | ): AndroidResolver(sdk) {
27 | private val tmpFilePath = vm.coreLibPath
28 | private val uuid = UUID.randomUUID()
29 |
30 | init {
31 | for (s in arrayOf("stdin", "stdout", "stderr")) {
32 | tmpFilePath.resolve(s).also {
33 | if (it.exists()) it.delete()
34 | }
35 | }
36 | }
37 |
38 | override fun resolve(emulator: Emulator, path: String, oflags: Int): FileResult? {
39 | val result = super.resolve(emulator, path, oflags)
40 | if (result == null || !result.isSuccess) {
41 | return this.resolve(emulator, path, oflags, result)
42 | }
43 | return result
44 | }
45 |
46 | private fun resolve(emulator: Emulator, path: String, oflags: Int, def: FileResult?): FileResult? {
47 | if (path == "stdin" || path == "stdout" || path == "stderr") {
48 | return FileResult.success(SimpleFileIO(oflags, tmpFilePath.resolve(path).also {
49 | if (!it.exists()) it.createNewFile()
50 | }, path))
51 | }
52 |
53 | if (path == "/data/data/com.tencent.tim/lib/libwtecdh.so") {
54 | return FileResult.failed(UnixEmulator.ENOENT)
55 | }
56 |
57 |
58 | if (path == "/proc/sys/kernel/random/boot_id") {
59 | return FileResult.success(ByteArrayFileIO(oflags, path, uuid.toString().toByteArray()))
60 | }
61 | if (path == "/proc/self/status") {
62 | return FileResult.success(ByteArrayFileIO(oflags, path, fetchStatus(emulator.pid).toByteArray()))
63 | }
64 | if (path == "/proc/stat") {
65 | return FileResult.success(ByteArrayFileIO(oflags, path, fetchStat()))
66 | }
67 | if (path == "/proc/meminfo") {
68 | return FileResult.success(ByteArrayFileIO(oflags, path, fetchMemInfo()))
69 | }
70 | if (path == "/proc/cpuinfo") {
71 | return FileResult.success(ByteArrayFileIO(oflags, path, fetchCpuInfo()))
72 | }
73 | if (path == "/dev/__properties__") {
74 | return FileResult.success(DirectoryFileIO(oflags, path,
75 | DirectoryFileIO.DirectoryEntry(true, "properties_serial"),
76 | ))
77 | }
78 |
79 | if ("/proc/self/maps" == path) {
80 | return FileResult.success(ByteArrayFileIO(oflags, path, byteArrayOf()))
81 | }
82 |
83 | if (path == "/system/lib") {
84 | return FileResult.success(DirectoryFileIO(oflags, path,
85 | DirectoryFileIO.DirectoryEntry(true, "libhwui.so"),
86 | ))
87 | }
88 |
89 | if (path == "/data/data/${vm.envData.packageName}") {
90 | return FileResult.success(DirectoryFileIO(oflags, path,
91 | DirectoryFileIO.DirectoryEntry(false, "files"),
92 | DirectoryFileIO.DirectoryEntry(false, "shared_prefs"),
93 | DirectoryFileIO.DirectoryEntry(false, "cache"),
94 | DirectoryFileIO.DirectoryEntry(false, "code_cache"),
95 | ))
96 | }
97 |
98 | if (path == "/dev/urandom" ||
99 | path == "/data/local/su" ||
100 | path == "/data/local/bin/su" ||
101 | path == "/data/local/xbin/su" ||
102 | path == "/sbin/su" ||
103 | path == "/su/bin/su" ||
104 | path == "/system/bin/su" ||
105 | path == "/system/bin/.ext/su" ||
106 | path == "/system/bin/failsafe/su" ||
107 | path == "/system/sd/xbin/su" ||
108 | path == "/system/usr/we-need-root/su" ||
109 | path == "/system/xbin/su" ||
110 | path == "/cache/su" ||
111 | path == "/data/su" ||
112 | path == "/dev/su" || path.contains("busybox") || path.contains("magisk")
113 | ) {
114 | return FileResult.failed(UnixEmulator.ENOENT)
115 | }
116 |
117 | if (path == "/sdcard/Android/") {
118 | return FileResult.success(DirectoryFileIO(oflags, path,
119 | DirectoryFileIO.DirectoryEntry(false, "data"),
120 | DirectoryFileIO.DirectoryEntry(false, "obb"),
121 | ))
122 | }
123 |
124 | if (path == "/system/lib64/libhoudini.so" || path == "/system/lib/libhoudini.so") {
125 | return FileResult.failed(UnixEmulator.ENOENT)
126 | }
127 |
128 | if (path == "/proc/self/cmdline"
129 | || path == "/proc/${emulator.pid}/cmdline"
130 | || path == "/proc/stat/cmdline" // an error case
131 | ) {
132 | if (vm.envData.packageName == "com.tencent.tim") {
133 | return FileResult.success(ByteArrayFileIO(oflags, path, vm.envData.packageName.toByteArray()))
134 | } else {
135 | return FileResult.success(ByteArrayFileIO(oflags, path, "${vm.envData.packageName}:MSF".toByteArray()))
136 | }
137 | }
138 |
139 | if (path == "/data/data") {
140 | return FileResult.failed(UnixEmulator.EACCES)
141 | }
142 |
143 | if (path.contains("star_est.xml")) {
144 | return FileResult.success(ByteArrayFileIO(oflags, path, """
145 |
146 |
149 | """.trimIndent().toByteArray()))
150 | }
151 |
152 | if (path == "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq") {
153 | return FileResult.success(ByteArrayFileIO(oflags, path, "1804800".toByteArray()))
154 | }
155 |
156 | if (path == "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq") {
157 | return FileResult.success(ByteArrayFileIO(oflags, path, "300000".toByteArray()))
158 | }
159 |
160 | if (path == "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq") {
161 | return FileResult.success(ByteArrayFileIO(oflags, path, "1670400".toByteArray()))
162 | }
163 |
164 | if (path == "/sys/devices/soc0/serial_number") {
165 | return FileResult.success(ByteArrayFileIO(oflags, path, "0x0000043be8571339".toByteArray()))
166 | }
167 |
168 | if (path == "/proc") {
169 | return FileResult.success(DirectoryFileIO(oflags, path,
170 | DirectoryFileIO.DirectoryEntry(false, emulator.pid.toString()),
171 | ))
172 | }
173 |
174 | if (path == "/data/app/~~vbcRLwPxS0GyVfqT-nCYrQ==/${vm.envData.packageName}-xJKJPVp9lorkCgR_w5zhyA==/lib/arm64") {
175 | //return FileResult.success(DirectoryFileIO(oflags, path,
176 | // DirectoryFileIO.DirectoryEntry(true, "libfekit.so"),
177 | // DirectoryFileIO.DirectoryEntry(true, "libpoxy.so"),
178 | //))
179 | }
180 |
181 | if(path == "/data/app/~~vbcRLwPxS0GyVfqT-nCYrQ==/${vm.envData.packageName}-xJKJPVp9lorkCgR_w5zhyA==/base.apk") {
182 | val f = tmpFilePath.resolve("QQ.apk")
183 | if (f.exists()) {
184 | return FileResult.success(SimpleFileIO(oflags, tmpFilePath.resolve("QQ.apk").also {
185 | if (!it.exists()) it.createNewFile()
186 | }, path))
187 | } else {
188 | return FileResult.failed(UnixEmulator.ENOENT)
189 | }
190 | }
191 |
192 | if (path == "/data/app/~~vbcRLwPxS0GyVfqT-nCYrQ==/${vm.envData.packageName}-xJKJPVp9lorkCgR_w5zhyA==/lib/arm64/libfekit.so") {
193 | return FileResult.success(SimpleFileIO(oflags, tmpFilePath.resolve("libfekit.so"), path))
194 | }
195 |
196 | if (path == "/data/app/~~vbcRLwPxS0GyVfqT-nCYrQ==/${vm.envData.packageName}-xJKJPVp9lorkCgR_w5zhyA==/lib/arm64/libwtecdh.so") {
197 | tmpFilePath.resolve("libwtecdh.so").let {
198 | if (it.exists()) {
199 | return FileResult.success(SimpleFileIO(oflags, it, path))
200 | }
201 | }
202 | }
203 |
204 | if (path == "/system/bin/sh" || path == "/system/bin/ls" || path == "/system/lib/libc.so") {
205 | return FileResult.success(ByteArrayFileIO(oflags, path, byteArrayOf(0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00)))
206 | }
207 |
208 | if (path.startsWith("/data/user/")) {
209 | if (path != "/data/user/0" && path != "/data/user/999") {
210 | return FileResult.failed(UnixEmulator.ENOENT)
211 | } else {
212 | return FileResult.failed(UnixEmulator.EACCES)
213 | }
214 | }
215 |
216 | if (path.contains("system_android_l2") || path.contains("android_lq")) {
217 | val newPath = if (path.startsWith("C:")) path.substring(2) else path
218 | val file = tmpFilePath.resolve(".system_android_l2")
219 | if (!file.exists()) {
220 | file.writeBytes("619F9042CA821CF91DFAF172D464FFC7A6CB8E024CC053F7438429FA38E86854471D6B0A9DE4C39BF02DC18C0CC54A715C9210E8A32B284366849CBB7F88C634AA".hex2ByteArray())
221 | }
222 | return FileResult.success(SimpleFileIO(oflags, file, newPath))
223 | }
224 |
225 | Logger.getLogger("FileResolver").warning("Couldn't find file: $path")
226 | return def
227 | }
228 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/unidbg/env/QSecJni.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNCHECKED_CAST")
2 |
3 | package moe.fuqiuluo.unidbg.env
4 |
5 | import CONFIG
6 | import com.github.unidbg.linux.android.dvm.*
7 | import com.github.unidbg.linux.android.dvm.array.ArrayObject
8 | import com.tencent.mobileqq.channel.SsoPacket
9 | import com.tencent.mobileqq.dt.model.FEBound
10 | import com.tencent.mobileqq.qsec.qsecest.SelfBase64
11 | import com.tencent.mobileqq.qsec.qsecurity.DeepSleepDetector
12 | import com.tencent.mobileqq.sign.QQSecuritySign
13 | import kotlinx.coroutines.sync.Mutex
14 | import moe.fuqiuluo.comm.EnvData
15 | import moe.fuqiuluo.ext.toHexString
16 | import moe.fuqiuluo.unidbg.QSecVM
17 | import moe.fuqiuluo.unidbg.vm.GlobalData
18 | import org.slf4j.LoggerFactory
19 | import java.io.File
20 | import java.security.SecureRandom
21 | import java.util.*
22 | import kotlin.collections.ArrayList
23 | import kotlin.random.Random
24 | import kotlin.random.nextInt
25 |
26 | private val logger = LoggerFactory.getLogger(QSecJni::class.java)
27 |
28 | typealias BytesObject = com.github.unidbg.linux.android.dvm.array.ByteArray
29 |
30 | class QSecJni(
31 | val envData: EnvData,
32 | val vm: QSecVM,
33 | val global: GlobalData
34 | ) : AbstractJni() {
35 | override fun getStaticIntField(vm: BaseVM, dvmClass: DvmClass, signature: String): Int {
36 | if (signature == "android/os/Build\$VERSION->SDK_INT:I") {
37 | return 26
38 | }
39 | return super.getStaticIntField(vm, dvmClass, signature)
40 | }
41 |
42 | override fun getIntField(vm: BaseVM, dvmObject: DvmObject<*>, signature: String): Int {
43 | if (signature == "android/content/pm/ApplicationInfo->targetSdkVersion:I") {
44 | return 29
45 | }
46 | return super.getIntField(vm, dvmObject, signature)
47 | }
48 |
49 | override fun callVoidMethodV(vm: BaseVM, dvmObject: DvmObject<*>, signature: String, vaList: VaList) {
50 | if (signature == "com/tencent/mobileqq/fe/IFEKitLog->i(Ljava/lang/String;ILjava/lang/String;)V") {
51 | val tag = vaList.getObjectArg(0)
52 | val msg = vaList.getObjectArg(2)
53 | println(tag.value + "info: " + msg.value)
54 | return
55 | }
56 | if (signature == "com/tencent/mobileqq/fe/IFEKitLog->e(Ljava/lang/String;ILjava/lang/String;)V") {
57 | val tag = vaList.getObjectArg(0)
58 | val msg = vaList.getObjectArg(2)
59 | println(tag.value + "error: " + msg.value)
60 | return
61 | }
62 | if (signature == "com/tencent/mobileqq/channel/ChannelProxy->sendMessage(Ljava/lang/String;[BJ)V") {
63 | val cmd = vaList.getObjectArg(0).value
64 | val data = vaList.getObjectArg(1).value
65 | val callbackId = vaList.getLongArg(2)
66 | val hex = data.toHexString()
67 |
68 | if (callbackId == -1L) return
69 |
70 | println("uin = ${global["uin"]}, id = $callbackId, sendPacket(cmd = $cmd, data = $hex)")
71 | (global["PACKET"] as ArrayList).add(SsoPacket(cmd, hex, callbackId))
72 | (global["mutex"] as Mutex).also { if (it.isLocked) it.unlock() }
73 | return
74 | }
75 |
76 | if (signature == "com/tencent/mobileqq/qsec/qsecurity/QSec->updateO3DID(Ljava/lang/String;)V") {
77 | val o3did = vaList.getObjectArg(0).value
78 | global["o3did"] = o3did
79 | return
80 | }
81 |
82 | if (signature == "com/tencent/secprotocol/ByteData->putUping(IIILjava/lang/Object;)V") {
83 | return
84 | }
85 |
86 | super.callVoidMethodV(vm, dvmObject, signature, vaList)
87 | }
88 |
89 | override fun getStaticObjectField(vm: BaseVM, dvmClass: DvmClass, signature: String): DvmObject<*> {
90 | if (signature == "com/tencent/mobileqq/qsec/qsecurity/QSecConfig->business_uin:Ljava/lang/String;") {
91 | return StringObject(vm, global["uin"] as String)
92 | }
93 | if (signature == "com/tencent/mobileqq/qsec/qsecurity/QSecConfig->business_seed:Ljava/lang/String;") {
94 | return StringObject(vm, global["seed"] as? String ?: "")
95 | }
96 | if (signature == "com/tencent/mobileqq/qsec/qsecurity/QSecConfig->business_guid:Ljava/lang/String;") {
97 | return StringObject(vm, global["guid"] as? String ?: "")
98 | }
99 | if (signature == "com/tencent/mobileqq/qsec/qsecurity/QSecConfig->business_o3did:Ljava/lang/String;") {
100 | return StringObject(vm, global["o3did"] as? String ?: "")
101 | }
102 | if (signature == "com/tencent/mobileqq/qsec/qsecurity/QSecConfig->business_q36:Ljava/lang/String;") {
103 | return StringObject(vm, global["qimei36"] as? String ?: "")
104 | }
105 | if (signature == "com/tencent/mobileqq/qsec/qsecurity/QSecConfig->business_qua:Ljava/lang/String;") {
106 | return StringObject(vm, this.vm.envData.qua)
107 | }
108 | return super.getStaticObjectField(vm, dvmClass, signature)
109 | }
110 |
111 | override fun getObjectField(vm: BaseVM, dvmObject: DvmObject<*>, signature: String): DvmObject<*> {
112 | if (signature == "android/content/pm/ApplicationInfo->nativeLibraryDir:Ljava/lang/String;") {
113 | return StringObject(
114 | vm,
115 | "/data/app/~~vbcRLwPxS0GyVfqT-nCYrQ==/${envData.packageName}-xJKJPVp9lorkCgR_w5zhyA==/lib/arm64"
116 | )
117 | }
118 | return super.getObjectField(vm, dvmObject, signature)
119 | }
120 |
121 | override fun setObjectField(vm: BaseVM, dvmObject: DvmObject<*>, signature: String, value: DvmObject<*>) {
122 | if (signature == "com/tencent/mobileqq/sign/QQSecuritySign\$SignResult->token:[B") {
123 | val data = value.value as ByteArray
124 | (dvmObject as QQSecuritySign.SignResultObject).setToken(data)
125 | return
126 | }
127 | if (signature == "com/tencent/mobileqq/sign/QQSecuritySign\$SignResult->extra:[B") {
128 | val data = value.value as ByteArray
129 | (dvmObject as QQSecuritySign.SignResultObject).setExtra(data)
130 | return
131 | }
132 | if (signature == "com/tencent/mobileqq/sign/QQSecuritySign\$SignResult->sign:[B") {
133 | val data = value.value as ByteArray
134 | (dvmObject as QQSecuritySign.SignResultObject).setSign(data)
135 | return
136 | }
137 | super.setObjectField(vm, dvmObject, signature, value)
138 | }
139 |
140 | override fun callIntMethodV(vm: BaseVM, dvmObject: DvmObject<*>, signature: String, vaList: VaList): Int {
141 | if ("java/lang/String->hashCode()I" == signature) {
142 | return (dvmObject.value as String).hashCode()
143 | }
144 | return super.callIntMethodV(vm, dvmObject, signature, vaList)
145 | }
146 |
147 | override fun callStaticObjectMethodV(
148 | vm: BaseVM,
149 | dvmClass: DvmClass,
150 | signature: String,
151 | vaList: VaList
152 | ): DvmObject<*> {
153 | if (signature == "com/tencent/mobileqq/dt/app/Dtc->mmKVValue(Ljava/lang/String;)Ljava/lang/String;") {
154 | return StringObject(
155 | vm, when (val key = vaList.getObjectArg(0).value) {
156 | "TuringRiskID-TuringCache-20230511" -> ""
157 | "o3_switch_Xwid", "o3_xwid_switch" -> global["o3_switch_Xwid"] as? String ?: "1"
158 | "DeviceToken-oaid-V001" -> ""
159 | "DeviceToken-MODEL-XX-V001" -> ""
160 | "DeviceToken-ANDROID-ID-V001" -> ""
161 | "DeviceToken-qimei36-V001" -> global["qimei36"] as? String ?: ""
162 | "MQQ_SP_DEVICETOKEN_DID_DEVICEIDUUID_202207072241" -> UUID.randomUUID()
163 | .toString() + "|" + this.vm.envData.version
164 |
165 | "DeviceToken-APN-V001", "DeviceToken-TuringCache-V001", "DeviceToken-MAC-ADR-V001", "DeviceToken-wifissid-V001" -> "-1"
166 | else -> error("Not support mmKVValue:$key")
167 | }
168 | )
169 | }
170 | if (signature == "android/provider/Settings\$System->getString(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;") {
171 | val key = vaList.getObjectArg(1).value
172 | if (key == "android_id") {
173 | return StringObject(vm, envData.androidId.lowercase())
174 | }
175 | }
176 | if (signature == "com/tencent/mobileqq/fe/utils/DeepSleepDetector->getCheckResult()Ljava/lang/String;") {
177 | val result = (global["DeepSleepDetector"] as DeepSleepDetector).getCheckResult()
178 | return StringObject(vm, result.toString())
179 | }
180 | if (signature == "com/tencent/mobileqq/dt/model/FEBound->transform(I[B)[B") {
181 | val mode = vaList.getIntArg(0)
182 | val data = vaList.getObjectArg>(1).value as ByteArray
183 | val result = FEBound.transform(mode, data)
184 | if (mode == 1)
185 | println("FEBound.transform(${data.toHexString()}) => ${result?.toHexString()}")
186 | return BytesObject(vm, result)
187 | }
188 | if (signature == "java/lang/ClassLoader->getSystemClassLoader()Ljava/lang/ClassLoader;") {
189 | return vm.resolveClass("java/lang/ClassLoader")
190 | .newObject(ClassLoader.getSystemClassLoader())
191 | }
192 | if (signature == "com/tencent/mobileqq/dt/app/Dtc->getPropSafe(Ljava/lang/String;)Ljava/lang/String;") {
193 | return StringObject(
194 | vm, when (val key = vaList.getObjectArg(0).value) {
195 | "ro.build.id" -> "TKQ1.220905.001"
196 | "ro.build.display.id" -> "TKQ1.220905.001 test-keys"
197 | "ro.product.device", "ro.product.name" -> "mondrian"
198 | "ro.product.board" -> "taro"
199 | "ro.product.manufacturer" -> "Xiaomi"
200 | "ro.product.brand" -> "Redmi"
201 | "ro.bootloader" -> "unknown"
202 | "persist.sys.timezone" -> "Asia/Shanghai"
203 | "ro.hardware" -> "qcom"
204 | "ro.product.cpu.abilist" -> "arm64-v8a, armeabi-v7a, armeabi"
205 | "ro.build.version.incremental" -> "V14.0.18.0.CNMLGB"
206 | "ro.build.version.release" -> "8.0"
207 | "ro.build.version.base_os", "ro.boot.container", "ro.vendor.build.fingerprint", "ro.build.expect.bootloader", "ro.build.expect.baseband" -> ""
208 | "ro.build.version.security_patch" -> "2077-2-29"
209 | "ro.build.version.preview_sdk" -> "0"
210 | "ro.build.version.codename", "ro.build.version.all_codenames" -> "REL"
211 | "ro.build.type" -> "user"
212 | "ro.build.tags" -> "release-keys"
213 | "ro.treble.enabled" -> "true"
214 | "ro.build.date.utc" -> "1673390476"
215 | "ro.build.user" -> ""
216 | "ro.build.host" -> "build"
217 | "net.bt.name" -> "Android"
218 | "ro.build.characteristics" -> "default"
219 | "ro.build.description" -> "mondrian-user 12 TKQ1.220905.001 release-keys"
220 | "ro.product.locale" -> "zh-CN"
221 | "ro.build.flavor" -> "full_miui_64-user"
222 | "ro.config.ringtone" -> "Ring_Synth_04.ogg"
223 | else -> error("Not support prop:$key")
224 | }
225 | )
226 | }
227 | if (signature == "com/tencent/mobileqq/dt/app/Dtc->getAppVersionName(Ljava/lang/String;)Ljava/lang/String;") {
228 | return StringObject(
229 | vm, when (val key = vaList.getObjectArg(0).value) {
230 | "empty" -> this.vm.envData.version
231 | else -> error("Not support getAppVersionName:$key")
232 | }
233 | )
234 | }
235 | if (signature == "com/tencent/mobileqq/dt/app/Dtc->getAppVersionCode(Ljava/lang/String;)Ljava/lang/String;") {
236 | return StringObject(
237 | vm, when (val key = vaList.getObjectArg(0).value) {
238 | "empty" -> this.vm.envData.code
239 | else -> error("Not support getAppVersionCode:$key")
240 | }
241 | )
242 | }
243 | if (signature == "com/tencent/mobileqq/dt/app/Dtc->getAppInstallTime(Ljava/lang/String;)Ljava/lang/String;") {
244 | return StringObject(
245 | vm, when (val key = vaList.getObjectArg(0).value) {
246 | "empty" -> (System.currentTimeMillis() - 10000).toString()
247 | else -> error("Not support getAppVersionCode:$key")
248 | }
249 | )
250 | }
251 | if (
252 | signature == "com/tencent/mobileqq/dt/app/Dtc->getDensity(Ljava/lang/String;)Ljava/lang/String;" ||
253 | signature == "com/tencent/mobileqq/dt/app/Dtc->getFontDpi(Ljava/lang/String;)Ljava/lang/String;"
254 | ) {
255 | return StringObject(
256 | vm, when (val key = vaList.getObjectArg(0).value) {
257 | "empty" -> "1.3125"
258 | else -> error("Not support getAppVersionCode:$key")
259 | }
260 | )
261 | }
262 | if ("com/tencent/mobileqq/dt/app/Dtc->getScreenSize(Ljava/lang/String;)Ljava/lang/String;" == signature) {
263 | return StringObject(vm, "[800,1217]")
264 | }
265 | if (signature == "com/tencent/mobileqq/dt/app/Dtc->getStorage(Ljava/lang/String;)Ljava/lang/String;") {
266 | return StringObject(vm, "137438953471")
267 | }
268 | if (signature == "com/tencent/mobileqq/dt/app/Dtc->systemGetSafe(Ljava/lang/String;)Ljava/lang/String;") {
269 | return StringObject(
270 | vm, when (val key = vaList.getObjectArg(0).value) {
271 | "user.locale" -> "zh-CN"
272 | "http.agent" -> "Dalvik/2.1.0 (Linux; U; Android 12.0.0; 114514 Build/O11019)"
273 | "java.vm.version" -> "2.1.0"
274 | "os.version" -> "3.18.79"
275 | "persist.sys.timezone" -> "-1"
276 | "java.runtime.version" -> "0.9"
277 | "java.boot.class.path" -> "/system/framework/core-oj.jar:/system/framework/core-libart.jar:/system/framework/conscrypt.jar:/system/frameworkhttp.jar:/system/framework/bouncycastle.jar:/system/framework/apache-xml.jar:/system/framework/legacy-test.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/frameworkoip-common.jar:/system/framework/ims-common.jar:/system/framework/org.apache.http.legacy.boot.jar:/system/framework/android.hidl.base-V1.0-java.jar:/system/framework/android.hidl.manager-V1.0-java.jar:/system/framework/mediatek-common.jar:/system/framework/mediatek-framework.jar:/system/framework/mediatek-telephony-common.jar:/system/framework/mediatek-telephony-base.jar:/system/framework/mediatek-ims-common.jar:/system/framework/mediatek-telecom-common.jar:/system/framework/mediatek-cta.jar"
278 | else -> error("Not support systemGetSafe:$key")
279 | }
280 | )
281 | }
282 | if (signature == "com/tencent/mobileqq/dt/app/Dtc->getIME(Ljava/lang/String;)Ljava/lang/String;") {
283 | return StringObject(vm, "com.netease.nemu_vinput.nemu/com.android.inputmethodcommon.SoftKeyboard")
284 | }
285 |
286 | if (signature == "java/lang/Thread->currentThread()Ljava/lang/Thread;") {
287 | return vm.resolveClass("java/lang/Thread").newObject(null)
288 | }
289 |
290 | if (signature == "com/tencent/mobileqq/qsec/qsecest/QsecEst->p(Landroid/content/Context;I)Ljava/lang/String;") {
291 | val id = vaList.getIntArg(1)
292 | return StringObject(vm, when (id) {
293 | 0 -> "26"
294 | 1 -> "k1"
295 | 23 -> "8" // CPU数量
296 | 25 -> "0.0.12"
297 | 26 -> "90721e0b3a587f77503b6abedd960c2e".uppercase() // 签名md5
298 | 27 -> "0" // 是否有xposed
299 | 28 -> envData.packageName
300 | 31 -> "0" // 是否锁屏
301 | 41 -> "" // Hardware
302 | 42 -> "WiFi"
303 | 43 -> envData.packageName
304 | 44 -> "1919810" // 剩余内存
305 | 45 -> "114514" // 磁盘大小
306 | 46 -> "0" // 是否有qemu环境
307 | 47 -> "0" // 是否存在qemu文件
308 | 48 -> "0" // 是否处于代理状态
309 | 49 -> "0" // SU
310 | 50 -> "1145141919"
311 | 51 -> envData.version
312 | 52 -> envData.code
313 | 68 -> "0" // VPN
314 | 70 -> "java.agent"
315 | 80, 71 -> "Asia/Shanghai"
316 | 72 -> "800,1217"
317 | 73 -> "8.0"
318 | 74 -> "200" // screen_brightness
319 | 75 -> Random.nextInt(0 .. 500000).toString()
320 | 76 -> "1,20,50"
321 | 77 -> (1024 * 1024 * 1024 * 32).toString()
322 | 78 -> "0" // su Bin
323 | 79 -> "1.1.2"
324 | 81 -> "zh"
325 | 82 -> "90721e0b3aaa7f77503b6abedd960c2e".uppercase()
326 | 83 -> "0"
327 | 86 -> fun(): String {
328 | val data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray()
329 | val secureRandom = SecureRandom()
330 | val sb = StringBuilder(32)
331 | for (i2 in 0 until 32) {
332 | sb.append(data[secureRandom.nextInt(data.size)])
333 | }
334 | return sb.toString()
335 | }()
336 | 87 -> "0" // busy box
337 | 88 -> "0" // magisk
338 | 89 -> System.currentTimeMillis().toString()
339 | in 90 .. 105 -> "0"
340 | else -> error("不支持的QSecEstInfo ID: $id")
341 | })
342 | }
343 |
344 | if ("com/tencent/secprotocol/t/s->c(Landroid/content/Context;)Ljava/lang/String;" == signature) {
345 | return StringObject(vm, envData.packageName)
346 | }
347 |
348 | if ("com/tencent/secprotocol/t/s->d(Landroid/content/Context;)Ljava/lang/String;" == signature) {
349 | return StringObject(vm, "90721e0b3a587f77503b6abedd960c2e".uppercase())
350 | }
351 |
352 | return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList)
353 | }
354 |
355 | override fun callStaticIntMethodV(vm: BaseVM?, dvmClass: DvmClass?, signature: String?, vaList: VaList?): Int {
356 | if ("com/tencent/secprotocol/t/s->e(Landroid/content/Context;)I" == signature) {
357 | return when (envData.version) {
358 | "3.5.1" -> 345546704
359 | "3.5.2" -> 345971138
360 | else -> error("不支持该TIM版本")
361 | }
362 | }
363 | return super.callStaticIntMethodV(vm, dvmClass, signature, vaList)
364 | }
365 |
366 | override fun callStaticVoidMethodV(vm: BaseVM, dvmClass: DvmClass, signature: String, vaList: VaList) {
367 | if (signature == "com/tencent/mobileqq/fe/utils/DeepSleepDetector->stopCheck()V") {
368 | if ("DeepSleepDetector" in global) {
369 | (global["DeepSleepDetector"] as DeepSleepDetector).stopped = true
370 | }
371 | return
372 | }
373 | if (signature == "com/tencent/mobileqq/dt/app/Dtc->mmKVSaveValue(Ljava/lang/String;Ljava/lang/String;)V") {
374 | val key = vaList.getObjectArg(0).value
375 | val value = vaList.getObjectArg(1).value
376 | global[key] = value
377 | return
378 | }
379 | if (signature == "com/tencent/mobileqq/dt/app/Dtc->saveList(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V") {
380 | return
381 | }
382 | super.callStaticVoidMethodV(vm, dvmClass, signature, vaList)
383 | }
384 |
385 | override fun callObjectMethodV(
386 | vm: BaseVM,
387 | dvmObject: DvmObject<*>,
388 | signature: String,
389 | vaList: VaList
390 | ): DvmObject<*> {
391 | if (signature == "android/content/Context->getApplicationInfo()Landroid/content/pm/ApplicationInfo;") {
392 | return vm.resolveClass("android/content/pm/ApplicationInfo").newObject(null)
393 | }
394 | if (signature == "android/content/Context->getFilesDir()Ljava/io/File;") {
395 | return vm.resolveClass("android/content/Context")
396 | .also {
397 | it.superClass = vm.resolveClass("java/io/File").apply {
398 | this.superClass = it
399 | }
400 | }
401 | .newObject(File("/data/user/0/${envData.packageName}/files"))
402 | //return vm.resolveClass("java/io/File", vm.resolveClass("android/content/Context"))
403 | // .newObject(File("/data/user/0/${envData.packageName}/files"))
404 |
405 | //if (envData.version == "3.5.1") {
406 | //
407 | //} else {
408 | //}
409 | }
410 | if (signature == "android/content/Context->getContentResolver()Landroid/content/ContentResolver;") {
411 | return vm.resolveClass("android/content/ContentResolver")
412 | .newObject(null)
413 | }
414 | if (signature == "android/content/pm/PackageManager->queryIntentServices(Landroid/content/Intent;I)Ljava/util/List;") {
415 | return vm.resolveClass("java/util/List").newObject(ArrayList())
416 | }
417 | if (signature == "android/content/Intent->addCategory(Ljava/lang/String;)Landroid/content/Intent;") {
418 | return dvmObject
419 | }
420 | if (signature == "java/io/File->getPackageResourcePath()Ljava/lang/String;") {
421 | return StringObject(vm, "/data/app/~~vbcRLwPxS0GyVfqT-nCYrQ==/${envData.packageName}-xJKJPVp9lorkCgR_w5zhyA==/base.apk")
422 | }
423 | if (signature == "android/content/Context->getPackageResourcePath()Ljava/lang/String;") {
424 | return StringObject(vm, "/data/app/~~vbcRLwPxS0GyVfqT-nCYrQ==/${envData.packageName}-xJKJPVp9lorkCgR_w5zhyA==/base.apk")
425 | }
426 | if (signature == "android/content/Context->getPackageName()Ljava/lang/String;") {
427 | return StringObject(vm, envData.packageName)
428 | }
429 | if (signature == "java/lang/ClassLoader->loadClass(Ljava/lang/String;)Ljava/lang/Class;") {
430 | val name = vaList.getObjectArg(0).value
431 | val loader = dvmObject.value as ClassLoader
432 | try {
433 | return vm
434 | .resolveClass("java/lang/Class")
435 | .newObject(loader.loadClass(name))
436 | } catch (e: ClassNotFoundException) {
437 | vm.throwException(
438 | vm
439 | .resolveClass("java.lang.ClassNotFoundException")
440 | .newObject(e)
441 | )
442 | }
443 | return vm
444 | .resolveClass("java/lang/Class")
445 | .newObject(null)
446 | }
447 | if ("java/lang/Thread->getStackTrace()[Ljava/lang/StackTraceElement;" == signature) {
448 | return ArrayObject()
449 | }
450 | if ("com/tencent/mobileqq/qsec/qsecurity/QSec->getEstInfo()Ljava/lang/String;" == signature) {
451 | val est = global["est_data"] as? com.github.unidbg.linux.android.dvm.array.ByteArray
452 | return if (est == null || est.value == null) {
453 | StringObject(vm, "e_null")
454 | } else {
455 | val byteArray = est.value
456 | val b64 = SelfBase64.Encoder.RFC4648.encodeToString(byteArray)
457 | StringObject(vm, b64)
458 | }
459 | }
460 | if ("android/content/Context->getExternalFilesDir(Ljava/lang/String;)Ljava/io/File;" == signature) {
461 | return vm.resolveClass("java/io/File")
462 | .newObject(File("/mnt/sdcard"))
463 | }
464 | if ("android/content/Context->toString()Ljava/lang/String;" == signature) {
465 | return StringObject(vm, dvmObject.value.toString())
466 | }
467 | return super.callObjectMethodV(vm, dvmObject, signature, vaList)
468 | }
469 |
470 | override fun acceptMethod(dvmClass: DvmClass, signature: String, isStatic: Boolean): Boolean {
471 | if (signature == "com/tencent/mobileqq/qsec/qsecest/QsecEst->p(Landroid/content/Context;I)Ljava/lang/String;"
472 | && envData.packageName == "com.tencent.mobileqq") {
473 | return false
474 | }
475 | if (signature == "com/tencent/qqprotect/qsec/QSecFramework->goingUp(JJJJLjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;[Ljava/lang/Object;)I"
476 | && envData.packageName == "com.tencent.mobileqq") {
477 | return false
478 | }
479 | if (CONFIG.unidbg.debug) {
480 | println("Accept ${ if (isStatic) "static" else "" } $signature")
481 | }
482 | return super.acceptMethod(dvmClass, signature, isStatic)
483 | }
484 |
485 | override fun toReflectedMethod(vm: BaseVM?, dvmClass: DvmClass?, signature: String?): DvmObject<*> {
486 | //println("toReflectedMethod")
487 | return super.toReflectedMethod(vm, dvmClass, signature)
488 | }
489 |
490 | override fun newObjectV(vm: BaseVM, dvmClass: DvmClass, signature: String, vaList: VaList): DvmObject<*> {
491 | if (signature == "java/io/File->(Ljava/lang/String;)V") {
492 | val path = vaList.getObjectArg(0).value
493 | return vm
494 | .resolveClass("java/io/File")
495 | .newObject(File(path))
496 | }
497 | if (signature == "com/tencent/mobileqq/sign/QQSecuritySign\$SignResult->()V") {
498 | return QQSecuritySign.SignResultObject(vm)
499 | }
500 | if (signature == "android/content/Intent->(Ljava/lang/String;)V") {
501 | return vm
502 | .resolveClass("android/content/Intent")
503 | .newObject(hashMapOf("action" to (vaList.getObjectArg(0) as StringObject).value))
504 | }
505 | return super.newObjectV(vm, dvmClass, signature, vaList)
506 | }
507 |
508 | override fun callBooleanMethodV(
509 | vm: BaseVM,
510 | dvmObject: DvmObject<*>,
511 | signature: String,
512 | vaList: VaList
513 | ): Boolean {
514 | if (signature == "java/io/File->canRead()Z") {
515 | val file = dvmObject.value as File
516 | if (
517 | file.toString() == "\\data\\data\\${envData.packageName}\\.." ||
518 | file.toString() == "/data/data/${envData.packageName}/.." ||
519 | file.toString() == "/data/data/" ||
520 | file.toString() == "/data/data"
521 | ) {
522 | return false
523 | }
524 | }
525 | return super.callBooleanMethodV(vm, dvmObject, signature, vaList)
526 | }
527 |
528 | override fun callObjectMethod(
529 | vm: BaseVM,
530 | dvmObject: DvmObject<*>,
531 | signature: String,
532 | varArg: VarArg
533 | ): DvmObject<*> {
534 | return super.callObjectMethod(vm, dvmObject, signature, varArg)
535 | }
536 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/unidbg/env/files/cpuinfo.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.unidbg.env.files
2 |
3 | fun fetchCpuInfo(): ByteArray {
4 | return """
5 | processor : 0
6 | BogoMIPS : 38.40
7 | Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bti
8 | CPU implementer : 0x41
9 | CPU architecture: 8
10 | CPU variant : 0x0
11 | CPU part : 0xd46
12 | CPU revision : 3
13 |
14 | processor : 1
15 | BogoMIPS : 38.40
16 | Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bti
17 | CPU implementer : 0x41
18 | CPU architecture: 8
19 | CPU variant : 0x0
20 | CPU part : 0xd46
21 | CPU revision : 3
22 |
23 | processor : 2
24 | BogoMIPS : 38.40
25 | Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bti
26 | CPU implementer : 0x41
27 | CPU architecture: 8
28 | CPU variant : 0x0
29 | CPU part : 0xd46
30 | CPU revision : 3
31 |
32 | processor : 3
33 | BogoMIPS : 38.40
34 | Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bti
35 | CPU implementer : 0x41
36 | CPU architecture: 8
37 | CPU variant : 0x0
38 | CPU part : 0xd46
39 | CPU revision : 3
40 |
41 | processor : 4
42 | BogoMIPS : 38.40
43 | Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bti
44 | CPU implementer : 0x41
45 | CPU architecture: 8
46 | CPU variant : 0x2
47 | CPU part : 0xd47
48 | CPU revision : 0
49 |
50 | processor : 5
51 | BogoMIPS : 38.40
52 | Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bti
53 | CPU implementer : 0x41
54 | CPU architecture: 8
55 | CPU variant : 0x2
56 | CPU part : 0xd47
57 | CPU revision : 0
58 |
59 | processor : 6
60 | BogoMIPS : 38.40
61 | Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bti
62 | CPU implementer : 0x41
63 | CPU architecture: 8
64 | CPU variant : 0x2
65 | CPU part : 0xd47
66 | CPU revision : 0
67 |
68 | processor : 7
69 | BogoMIPS : 38.40
70 | Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint i8mm bti
71 | CPU implementer : 0x41
72 | CPU architecture: 8
73 | CPU variant : 0x2
74 | CPU part : 0xd48
75 | CPU revision : 0
76 |
77 | """.trimIndent().toByteArray()
78 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/unidbg/env/files/meminfo.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.unidbg.env.files
2 |
3 | fun fetchMemInfo(): ByteArray {
4 | return """
5 | MemTotal: 11444400 kB
6 | MemFree: 444328 kB
7 | MemAvailable: 3060808 kB
8 | Buffers: 2292 kB
9 | Cached: 2735080 kB
10 | SwapCached: 0 kB
11 | Active: 1430388 kB
12 | Inactive: 6251000 kB
13 | Active(anon): 28972 kB
14 | Inactive(anon): 5323536 kB
15 | Active(file): 1401416 kB
16 | Inactive(file): 927464 kB
17 | Unevictable: 192884 kB
18 | Mlocked: 191652 kB
19 | SwapTotal: 0 kB
20 | SwapFree: 0 kB
21 | Dirty: 2540 kB
22 | Writeback: 0 kB
23 | AnonPages: 5136980 kB
24 | Mapped: 1164384 kB
25 | Shmem: 221712 kB
26 | KReclaimable: 871112 kB
27 | Slab: 673940 kB
28 | SReclaimable: 180828 kB
29 | SUnreclaim: 493112 kB
30 | KernelStack: 108816 kB
31 | ShadowCallStack: 27232 kB
32 | PageTables: 186052 kB
33 | NFS_Unstable: 0 kB
34 | Bounce: 0 kB
35 | WritebackTmp: 0 kB
36 | CommitLimit: 5722200 kB
37 | Committed_AS: 192359556 kB
38 | VmallocTotal: 262930368 kB
39 | VmallocUsed: 303412 kB
40 | VmallocChunk: 0 kB
41 | Percpu: 13888 kB
42 | AnonHugePages: 0 kB
43 | ShmemHugePages: 0 kB
44 | ShmemPmdMapped: 0 kB
45 | FileHugePages: 4096 kB
46 | FilePmdMapped: 4096 kB
47 | CmaTotal: 499712 kB
48 | CmaFree: 0 kB
49 | """.trimIndent().toByteArray()
50 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/unidbg/env/files/stat.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.unidbg.env.files
2 |
3 | fun fetchStat(): ByteArray {
4 | return buildString {
5 | append("cpu 70908543 2318391 57682285 205626547 265784 11502233 1505790 0 0 0\n")
6 | append("cpu0 7302248 343336 11528431 26247442 27384 2556821 401344 0 0 0\n")
7 | append("cpu1 9124285 580758 11024217 24904821 29376 2635234 311959 0 0 0\n")
8 | append("cpu2 8461182 354844 10910391 3640938 4786 2546246 249470 0 0 0\n")
9 | append("cpu3 7376835 227391 10245171 4156813 4695 2394473 281332 0 0 0\n")
10 | append("cpu4 10388918 244107 4595589 34689258 56474 439396 62430 0 0 0\n")
11 | append("cpu5 9161975 185637 3753596 36858345 42296 357412 122333 0 0 0\n")
12 | append("cpu6 9141509 191244 3678030 37028283 42926 347915 55033 0 0 0\n")
13 | append("cpu7 9951587 191070 1946857 38100643 57843 224732 21887 0 0 0\n")
14 | append(
15 | "intr 6499860109 0 3774845260 213595275 0 0 0 439330144 0 0 0 0 1432590806 0 0 0 0 0 0 8 7784 4505 0 0 0 43914228 87884 6656429 6883140 0 13 115199397 0 0 0 2 36941907 0 79802 19529541 0 50496389 0 0 0 0 0 0 0 0 0 0 0 0 0 99467 0 0 0 0 0 0 0 0 0 0 0 0 215 0 0 0 0 0 0 0 0 0 0 0 0 0 868825 523151 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 63205753 0 0 0 0 0 0 76117 0 0 0 0 30879 95857882 0 0 212 0 1276 0 24696 45 0 0 0 0 25295 0 0 0 0 0 0 171894 0 0 0 0 0 0 0 0 110527 0 0 6774417 0 0 1 0 5053790 0 3597 0 0 0 0 0 0 0 13 0 0 0 0 27028 0 0 0 0 0 0 0 8024 94 9 4138 1 17 3 47020 0 22 4 3 0 18361 50 2354 4143 0 0 0 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 0 0 1 1 0 48909549 37401 0 0 0 64 139379 55198 30 501 5983325 470270 831 0 0 0 0 0 9570 3840590 1661765 1367111 2518122 1384219 0 733594 0 0 0 0 0 0 0 0 0 0 0 14 0 0 0 101135 0 238315 972017 20683186 96649293 6 1420 0 0 0 0 0 0 0 0 0 0 0 0 1001357\n"
16 | )
17 | append("ctxt 9964111933\n")
18 | append("btime 1686854207\n")
19 | append("processes 27829795\n")
20 | append("procs_running 3\n")
21 | append("procs_blocked 0\n")
22 | append("softirq 646165898 227595 128247124 464901 50364798 35293690 0 9753380 65978203 58578 355777629\n")
23 | }.toByteArray()
24 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/unidbg/env/files/status.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.unidbg.env.files
2 |
3 | fun fetchStatus(pid: Int): String {
4 | return buildString {
5 | append("Name:\tencent.mobileqq\n")
6 | append("Umask:\t0077\n")
7 | append("State:\tS (sleeping)\n")
8 | append("Tgid:\t$pid\n")
9 | append("Ngid:\t0\n")
10 | append("Pid:\t$pid\n")
11 | append("PPid:\t1180\n")
12 | append("TracerPid:\t0\n")
13 | append("Uid:\t10281\t10281\t10281\t10281\n")
14 | append("Gid:\t10281\t10281\t10281\t10281\n")
15 | append("FDSize:\t1024\n")
16 | append("Groups:\t1079 3001 3002 3003 9997 20281 50281 99909997 \n")
17 | append("VmPeak:\t76966256 kB\n")
18 | append("VmSize:\t74489196 kB\n")
19 | append("VmLck:\t 0 kB\n")
20 | append("VmPin:\t 0 kB\n")
21 | append("VmHWM:\t 1332896 kB\n")
22 | append("VmRSS:\t 653604 kB\n")
23 | append("RssAnon:\t 481564 kB\n")
24 | append("RssFile:\t 156120 kB\n")
25 | append("RssShmem:\t 15920 kB\n")
26 | append("VmData:\t 7587328 kB\n")
27 | append("VmStk:\t 8192 kB\n")
28 | append("VmExe:\t 8 kB\n")
29 | append("VmLib:\t 432720 kB\n")
30 | append("VmPTE:\t 7252 kB\n")
31 | append("VmSwap:\t 0 kB\n")
32 | append("CoreDumping:\t0\n")
33 | append("THP_enabled:\t1\n")
34 | append("Threads:\t373\n")
35 | append("SigQ:\t0/42559\n")
36 | append("SigPnd:\t0000000000000000\n")
37 | append("ShdPnd:\t0000000000000000\n")
38 | append("SigBlk:\t0000000080001200\n")
39 | append("SigIgn:\t0000000000001001\n")
40 | append("SigCgt:\t0000006e400086fc\n")
41 | append("CapInh:\t0000000000000000\n")
42 | append("CapPrm:\t0000000000000000\n")
43 | append("CapEff:\t0000000000000000\n")
44 | append("CapBnd:\t0000000000000000\n")
45 | append("CapAmb:\t0000000000000000\n")
46 | append("NoNewPrivs:\t0\n")
47 | append("Seccomp:\t2\n")
48 | append("Seccomp_filters:\t1\n")
49 | append("Speculation_Store_Bypass:\tthread vulnerable\n")
50 | append("Cpus_allowed:\t07\n")
51 | append("Cpus_allowed_list:\t0-2\n")
52 | append("Mems_allowed:\t1\n")
53 | append("Mems_allowed_list:\t0\n")
54 | append("voluntary_ctxt_switches:\t819637\n")
55 | append("nonvoluntary_ctxt_switches:\t82707\n")
56 | }
57 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/unidbg/session/Session.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.unidbg.session
2 |
3 | import CONFIG
4 | import BASE_PATH
5 | import com.github.unidbg.worker.Worker
6 | import com.github.unidbg.worker.WorkerPool
7 | import com.tencent.mobileqq.channel.SsoPacket
8 | import com.tencent.mobileqq.fe.FEKit
9 | import kotlinx.coroutines.sync.Mutex
10 | import moe.fuqiuluo.comm.EnvData
11 | import moe.fuqiuluo.unidbg.QSecVM
12 |
13 | class Session(
14 | envData: EnvData,
15 | val pool: WorkerPool
16 | ): Worker(pool) {
17 | internal val vm: QSecVM =
18 | QSecVM(BASE_PATH, envData, CONFIG.unidbg.dynarmic, CONFIG.unidbg.unicorn, CONFIG.unidbg.kvm)
19 |
20 | init {
21 | vm.global["PACKET"] = arrayListOf()
22 | vm.global["mutex"] = Mutex(true)
23 | vm.global["qimei36"] = envData.qimei36.lowercase()
24 | vm.global["guid"] = envData.guid.lowercase()
25 | vm.init()
26 | FEKit.init(vm, envData.uin.toString())
27 | }
28 |
29 | override fun destroy() {
30 | vm.destroy()
31 | }
32 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/unidbg/session/SessionManager.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.unidbg.session
2 |
3 | import CONFIG
4 | import com.github.unidbg.worker.WorkerPool
5 | import com.github.unidbg.worker.WorkerPoolFactory
6 | import moe.fuqiuluo.api.BlackListError
7 | import moe.fuqiuluo.comm.EnvData
8 | import java.util.concurrent.ConcurrentHashMap
9 | import java.util.concurrent.TimeUnit
10 | import kotlin.concurrent.timer
11 |
12 | object SessionManager {
13 | private var workerPoolMap = hashMapOf()
14 | private val envDataByUin = ConcurrentHashMap()
15 |
16 | init {
17 | if (CONFIG.shareToken) {
18 | timer("reload", false, 1000L * 60 * 40, 1000L * 60 * 40) {
19 | workerPoolMap[0]?.close()
20 | }
21 | }
22 | }
23 |
24 | fun get(uin: Long): Session? {
25 | if (uin !in this) {
26 | return null
27 | }
28 |
29 | if (CONFIG.shareToken) {
30 | val envData = envDataByUin[uin]!!
31 | return workerPoolMap[0]?.borrow(60 * 1000L, TimeUnit.MILLISECONDS).also {
32 | val env = it?.vm?.envData
33 | if (env != null && env.uin != uin) {
34 | env.uin = uin
35 | env.androidId = envData.androidId
36 | env.code = envData.code
37 | env.guid = envData.guid
38 | env.packageName = envData.packageName
39 | env.qua = envData.qua
40 | env.version = envData.version
41 | env.qimei36 = envData.qimei36
42 | }
43 | }
44 | } else {
45 | return workerPoolMap[uin]?.borrow(60 * 1000L, TimeUnit.MILLISECONDS)
46 | }
47 | }
48 |
49 | operator fun contains(uin: Long) = envDataByUin.containsKey(uin)
50 |
51 | fun register(envData: EnvData) {
52 | if (CONFIG.blackList?.contains(envData.uin) == true) {
53 | throw BlackListError
54 | }
55 | if (envData.uin in this && !CONFIG.shareToken) {
56 | close(envData.uin)
57 | }
58 |
59 | envDataByUin[envData.uin] = envData
60 | if(workerPoolMap.containsKey(0) && CONFIG.shareToken) {
61 | return
62 | }
63 | workerPoolMap[if (CONFIG.shareToken) 0 else envData.uin] = WorkerPoolFactory.create({ pool ->
64 | Session(envData, pool)
65 | }, CONFIG.count)
66 | }
67 |
68 | fun close(uin: Long) {
69 | envDataByUin.remove(uin)
70 | if (!CONFIG.shareToken) {
71 | workerPoolMap[uin]?.close()
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/unidbg/vm/AndroidVM.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.unidbg.vm
2 |
3 | import CONFIG
4 | import com.github.unidbg.arm.backend.DynarmicFactory
5 | import com.github.unidbg.arm.backend.KvmFactory
6 | import com.github.unidbg.arm.backend.Unicorn2Factory
7 | import com.github.unidbg.linux.android.AndroidEmulatorBuilder
8 | import com.github.unidbg.linux.android.dvm.DalvikModule
9 | import com.github.unidbg.linux.android.dvm.DvmClass
10 | import com.github.unidbg.virtualmodule.android.AndroidModule
11 | import org.apache.commons.logging.LogFactory
12 | import java.io.Closeable
13 | import java.io.File
14 |
15 | open class AndroidVM(packageName: String, dynarmic: Boolean, unicorn: Boolean, kvm: Boolean): Closeable {
16 | internal val emulator = AndroidEmulatorBuilder
17 | .for64Bit()
18 | .setProcessName(packageName)
19 | .apply {
20 | if (dynarmic) addBackendFactory(DynarmicFactory(true))
21 | if (unicorn) addBackendFactory(Unicorn2Factory(true))
22 | if (kvm) addBackendFactory(KvmFactory(true))
23 | }
24 | .build()!!
25 | protected val memory = emulator.memory!!
26 | internal val vm = emulator.createDalvikVM()!!
27 |
28 | init {
29 | if (CONFIG.unidbg.debug) {
30 | System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog")
31 | System.setProperty("org.apache.commons.logging.simplelog.defaultlog", "debug")
32 | LogFactory.getFactory()
33 | .attributeNames.forEach { println(it) }
34 | }
35 | vm.setVerbose(CONFIG.unidbg.debug)
36 | val syscall = emulator.syscallHandler
37 | syscall.isVerbose = CONFIG.unidbg.debug
38 | syscall.setEnableThreadDispatcher(true)
39 | AndroidModule(emulator, vm).register(memory)
40 | }
41 |
42 | fun loadLibrary(soFile: File): DalvikModule {
43 | val dm = vm.loadLibrary(soFile, false)
44 | dm.callJNI_OnLoad(emulator)
45 | return dm
46 | }
47 |
48 | fun findClass(name: String, vararg interfaces: DvmClass): DvmClass {
49 | return vm.resolveClass(name, *interfaces)
50 | }
51 |
52 | override fun close() {
53 | this.emulator.close()
54 | }
55 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/unidbg/vm/GlobalData.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.unidbg.vm
2 |
3 | class GlobalData {
4 | private val cacheMap = hashMapOf()
5 |
6 | operator fun get(key: String): Any? {
7 | return cacheMap[key]
8 | }
9 |
10 | operator fun set(key: String, any: Any?) {
11 | cacheMap[key] = any
12 | }
13 |
14 | fun remove(key: String) = cacheMap.remove(key)
15 |
16 | operator fun contains(key: String) = key in cacheMap
17 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/utils/BytesUtil.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.utils
2 |
3 | import kotlin.random.Random
4 |
5 | val EMPTY_BYTE_ARRAY = byteArrayOf()
6 |
7 | object BytesUtil {
8 | @JvmStatic
9 | fun byteMerger(first: ByteArray, second: ByteArray): ByteArray {
10 | val result = first.copyOf(first.size + second.size)
11 | System.arraycopy(second, 0, result, first.size, second.size)
12 | return result
13 | }
14 |
15 | @JvmStatic
16 | fun int16ToBuf(i: Int): ByteArray {
17 | val out = ByteArray(2)
18 | out[1] = i.toByte()
19 | out[0] = (i shr 8).toByte()
20 | return out
21 | }
22 |
23 | @JvmStatic
24 | fun int32ToBuf(i: Int): ByteArray {
25 | val out = ByteArray(4)
26 | out[3] = i.toByte()
27 | out[2] = (i shr 8).toByte()
28 | out[1] = (i shr 16).toByte()
29 | out[0] = (i shr 24).toByte()
30 | return out
31 | }
32 |
33 | @JvmStatic
34 | fun int64ToBuf(j: Long): ByteArray {
35 | val out = ByteArray(8)
36 | out[7] = j.toInt().toByte()
37 | out[6] = ((j shr 8).toInt()).toByte()
38 | out[5] = ((j shr 16).toInt()).toByte()
39 | out[4] = ((j shr 24).toInt()).toByte()
40 | out[3] = ((j shr 32).toInt()).toByte()
41 | out[2] = ((j shr 40).toInt()).toByte()
42 | out[1] = ((j shr 48).toInt()).toByte()
43 | out[0] = ((j shr 56).toInt()).toByte()
44 | return out
45 | }
46 |
47 | @JvmStatic
48 | fun int64ToBuf32(j: Long): ByteArray {
49 | val out = ByteArray(4)
50 | out[3] = j.toInt().toByte()
51 | out[2] = ((j shr 8).toInt()).toByte()
52 | out[1] = ((j shr 16).toInt()).toByte()
53 | out[0] = ((j shr 24).toInt()).toByte()
54 | return out
55 | }
56 |
57 | @JvmStatic
58 | fun float2byte(f: Float): ByteArray {
59 | val fbit = java.lang.Float.floatToIntBits(f)
60 | val b = ByteArray(4)
61 | for (i in 0..3) {
62 | b[i] = (fbit shr 24 - i * 8).toByte()
63 | }
64 | val len = b.size
65 | val dest = ByteArray(len)
66 | System.arraycopy(b, 0, dest, 0, len)
67 | var temp: Byte
68 | for (i in 0 until len / 2) {
69 | temp = dest[i]
70 | dest[i] = dest[len - i - 1]
71 | dest[len - i - 1] = temp
72 | }
73 | return dest
74 | }
75 |
76 | @JvmStatic
77 | fun doubleToByte(num: Double): ByteArray {
78 | val b = ByteArray(8)
79 | var l = java.lang.Double.doubleToLongBits(num)
80 | for (i in 0..7) {
81 | b[i] = l.toByte()
82 | l = l shr 8
83 | }
84 | return b
85 | }
86 |
87 | @JvmStatic
88 | fun getDouble(b: ByteArray): Double {
89 | var m: Long = b[0].toLong()
90 | m = m and 0xff
91 | m = m or (b[1].toLong() shl 8)
92 | m = m and 0xffff
93 | m = m or (b[2].toLong() shl 16)
94 | m = m and 0xffffff
95 | m = m or (b[3].toLong() shl 24)
96 | m = m and 0xffffffffL
97 | m = m or (b[4].toLong() shl 32)
98 | m = m and 0xffffffffffL
99 | m = m or (b[5].toLong() shl 40)
100 | m = m and 0xffffffffffffL
101 | m = m or (b[6].toLong() shl 48)
102 | m = m and 0xffffffffffffffL
103 | m = m or (b[7].toLong() shl 56)
104 | return java.lang.Double.longBitsToDouble(m)
105 | }
106 |
107 | @JvmStatic
108 | fun byte2float(b: ByteArray, index: Int): Float {
109 | var l = b[index + 0].toInt()
110 | l = l and 0xff
111 | l = l or (b[index + 1].toLong() shl 8).toInt()
112 | l = l and 0xffff
113 | l = l or (b[index + 2].toLong() shl 16).toInt()
114 | l = l and 0xffffff
115 | l = l or (b[index + 3].toLong() shl 24).toInt()
116 | return java.lang.Float.intBitsToFloat(l)
117 | }
118 |
119 | @JvmStatic
120 | fun bufToInt8(bArr: ByteArray, i: Int): Int {
121 | return bArr[i].toInt() and 255
122 | }
123 |
124 | @JvmStatic
125 | fun bufToInt16(bArr: ByteArray, i: Int): Int {
126 | return (bArr[i].toInt() shl 8 and 65280) + (bArr[i + 1].toInt() and 255)
127 | }
128 |
129 | @JvmStatic
130 | fun bufToInt32(bArr: ByteArray, i: Int = 0): Int {
131 | return (bArr[i].toInt() shl 24 and -16777216) +
132 | (bArr[i + 1] .toInt() shl 16 and 16711680) +
133 | (bArr[i + 2].toInt() shl 8 and 65280) +
134 | (bArr[i + 3].toInt() and 255)
135 | }
136 |
137 | @JvmStatic
138 | fun bufToInt64(bArr: ByteArray, i: Int): Long {
139 | return (bArr[i].toLong() shl 56 and -72057594037927936L) +
140 | (bArr[i + 1].toLong() shl 48 and 71776119061217280L) +
141 | (bArr[i + 2].toLong() shl 40 and 280375465082880L) +
142 | (bArr[i + 3].toLong() shl 32 and 1095216660480L) +
143 | (bArr[i + 4].toLong() shl 24 and 4278190080L) +
144 | (bArr[i + 5].toLong() shl 16 and 16711680) +
145 | (bArr[i + 6].toLong() shl 8 and 65280) +
146 | (bArr[i + 7].toLong() and 255)
147 | }
148 |
149 | @JvmStatic
150 | fun subByte(b: ByteArray, off: Int, length: Int): ByteArray? {
151 | if (b.isNotEmpty()) {
152 | val b1 = ByteArray(length)
153 | System.arraycopy(b, off, b1, 0, length)
154 | return b1
155 | }
156 | return null
157 | }
158 |
159 | @JvmStatic
160 | fun randomKey(size: Int) = Random.nextBytes(size)
161 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/utils/MD5.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.utils
2 |
3 | import java.io.ByteArrayInputStream
4 | import java.io.InputStream
5 | import java.io.UnsupportedEncodingException
6 |
7 | class MD5 {
8 | private val buffer = ByteArray(64)
9 | private val count = LongArray(2)
10 | val digest = ByteArray(16)
11 | private val state = LongArray(4)
12 | fun getMD5(bArr: ByteArray): ByteArray {
13 | md5Init()
14 | md5Update(ByteArrayInputStream(bArr), bArr.size.toLong())
15 | md5Final()
16 | return digest
17 | }
18 |
19 | fun getMD5(inputStream: InputStream, len: Long): ByteArray {
20 | md5Init()
21 | if (!md5Update(inputStream, len)) {
22 | return ByteArray(16)
23 | }
24 | md5Final()
25 | return digest
26 | }
27 |
28 | init {
29 | md5Init()
30 | }
31 |
32 | fun md5Init() {
33 | count[0] = 0
34 | count[1] = 0
35 | state[0] = 0x67452301
36 | state[1] = 4023233417L
37 | state[2] = 2562383102L
38 | state[3] = 0x10325476
39 | }
40 |
41 | private fun F(j: Long, j2: Long, j3: Long): Long {
42 | return j and j2 or (j.inv() and j3)
43 | }
44 |
45 | private fun G(j: Long, j2: Long, j3: Long): Long {
46 | return j and j3 or (j3.inv() and j2)
47 | }
48 |
49 | private fun H(j: Long, j2: Long, j3: Long): Long {
50 | return j xor j2 xor j3
51 | }
52 |
53 | private fun I(j: Long, j2: Long, j3: Long): Long {
54 | return j3.inv() or j xor j2
55 | }
56 |
57 | private fun FF(j: Long, j2: Long, j3: Long, j4: Long, j5: Long, j6: Long, j7: Long): Long {
58 | val F = F(j2, j3, j4) + j5 + j7 + j
59 | return ((F.toInt() ushr (32 - j6).toInt()).toLong() or (F.toInt().toLong() shl j6.toInt())) + j2
60 | }
61 |
62 | private fun GG(j: Long, j2: Long, j3: Long, j4: Long, j5: Long, j6: Long, j7: Long): Long {
63 | val G = G(j2, j3, j4) + j5 + j7 + j
64 | return ((G.toInt() ushr (32 - j6).toInt()).toLong() or (G.toInt().toLong() shl j6.toInt())) + j2
65 | }
66 |
67 | private fun HH(j: Long, j2: Long, j3: Long, j4: Long, j5: Long, j6: Long, j7: Long): Long {
68 | val H = H(j2, j3, j4) + j5 + j7 + j
69 | return ((H.toInt() ushr (32 - j6).toInt()).toLong() or (H.toInt().toLong() shl j6.toInt())) + j2
70 | }
71 |
72 | private fun II(j: Long, j2: Long, j3: Long, j4: Long, j5: Long, j6: Long, j7: Long): Long {
73 | val I = I(j2, j3, j4) + j5 + j7 + j
74 | return ((I.toInt() ushr (32 - j6).toInt()).toLong() or (I.toInt().toLong() shl j6.toInt())) + j2
75 | }
76 |
77 | fun md5Update(inputStream: InputStream, j: Long): Boolean {
78 | var i: Int
79 | val bArr = ByteArray(64)
80 | var i2 = (count[0] ushr 3).toInt() and 63
81 | val jArr = count
82 | val j2 = jArr[0] + (j shl 3)
83 | jArr[0] = j2
84 | if (j2 < j shl 3) {
85 | val jArr2 = count
86 | jArr2[1] = jArr2[1] + 1
87 | }
88 | val jArr3 = count
89 | jArr3[1] = jArr3[1] + (j ushr 29)
90 | val i3 = 64 - i2
91 | if (j >= i3.toLong()) {
92 | val bArr2 = ByteArray(i3)
93 | try {
94 | inputStream.read(bArr2, 0, i3)
95 | md5Memcpy(buffer, bArr2, i2, 0, i3)
96 | md5Transform(buffer)
97 | i = i3
98 | while ((i + 63).toLong() < j) {
99 | i += try {
100 | inputStream.read(bArr)
101 | md5Transform(bArr)
102 | 64
103 | } catch (e: Exception) {
104 | e.printStackTrace()
105 | return false
106 | }
107 | }
108 | i2 = 0
109 | } catch (e2: Exception) {
110 | e2.printStackTrace()
111 | return false
112 | }
113 | } else {
114 | i = 0
115 | }
116 | val bArr3 = ByteArray((j - i.toLong()).toInt())
117 | return try {
118 | inputStream.read(bArr3)
119 | md5Memcpy(buffer, bArr3, i2, 0, bArr3.size)
120 | true
121 | } catch (e3: Exception) {
122 | e3.printStackTrace()
123 | false
124 | }
125 | }
126 |
127 | private fun md5Update(bArr: ByteArray, i: Int) {
128 | var i2 = 0
129 | val bArr2 = ByteArray(64)
130 | var i3 = (count[0] ushr 3).toInt() and 63
131 | val jArr = count
132 | val j = jArr[0] + (i.toLong() shl 3)
133 | jArr[0] = j
134 | if (j < i.toLong() shl 3) {
135 | val jArr2 = count
136 | jArr2[1] = jArr2[1] + 1
137 | }
138 | val jArr3 = count
139 | jArr3[1] = jArr3[1] + (i ushr 29).toLong()
140 | var i4 = 64 - i3
141 | if (i >= i4) {
142 | md5Memcpy(buffer, bArr, i3, 0, i4)
143 | md5Transform(buffer)
144 | while (i4 + 63 < i) {
145 | md5Memcpy(bArr2, bArr, 0, i4, 64)
146 | md5Transform(bArr2)
147 | i4 += 64
148 | }
149 | i3 = 0
150 | i2 = i4
151 | }
152 | md5Memcpy(buffer, bArr, i3, i2, i - i2)
153 | }
154 |
155 | fun md5Final() {
156 | val bArr = ByteArray(8)
157 | Encode(bArr, count, 8)
158 | val i = (count[0] ushr 3).toInt() and 63
159 | md5Update(PADDING, if (i < 56) 56 - i else 120 - i)
160 | md5Update(bArr, 8)
161 | Encode(digest, state, 16)
162 | }
163 |
164 | private fun md5Memcpy(bArr: ByteArray, bArr2: ByteArray, i: Int, i2: Int, i3: Int) {
165 | if (i3 >= 0) System.arraycopy(bArr2, i2, bArr, i, i3)
166 | }
167 |
168 | private fun md5Transform(bArr: ByteArray) {
169 | val j = state[0]
170 | val j2 = state[1]
171 | val j3 = state[2]
172 | val j4 = state[3]
173 | val jArr = LongArray(16)
174 | Decode(jArr, bArr, 64)
175 | val FF = FF(j, j2, j3, j4, jArr[0], 7, 3614090360L)
176 | val FF2 = FF(j4, FF, j2, j3, jArr[1], 12, 3905402710L)
177 | val FF3 = FF(j3, FF2, FF, j2, jArr[2], 17, 0x242070db)
178 | val FF4 = FF(j2, FF3, FF2, FF, jArr[3], 22, 3250441966L)
179 | val FF5 = FF(FF, FF4, FF3, FF2, jArr[4], 7, 4118548399L)
180 | val FF6 = FF(FF2, FF5, FF4, FF3, jArr[5], 12, 0x4787c62a)
181 | val FF7 = FF(FF3, FF6, FF5, FF4, jArr[6], 17, 2821735955L)
182 | val FF8 = FF(FF4, FF7, FF6, FF5, jArr[7], 22, 4249261313L)
183 | val FF9 = FF(FF5, FF8, FF7, FF6, jArr[8], 7, 0x698098d8)
184 | val FF10 = FF(FF6, FF9, FF8, FF7, jArr[9], 12, 2336552879L)
185 | val FF11 = FF(FF7, FF10, FF9, FF8, jArr[10], 17, 4294925233L)
186 | val FF12 = FF(FF8, FF11, FF10, FF9, jArr[11], 22, 2304563134L)
187 | val FF13 = FF(FF9, FF12, FF11, FF10, jArr[12], 7, 0x6b901122)
188 | val FF14 = FF(FF10, FF13, FF12, FF11, jArr[13], 12, 4254626195L)
189 | val FF15 = FF(FF11, FF14, FF13, FF12, jArr[14], 17, 2792965006L)
190 | val FF16 = FF(FF12, FF15, FF14, FF13, jArr[15], 22, 0x49b40821)
191 | val GG = GG(FF13, FF16, FF15, FF14, jArr[1], 5, 4129170786L)
192 | val GG2 = GG(FF14, GG, FF16, FF15, jArr[6], 9, 3225465664L)
193 | val GG3 = GG(FF15, GG2, GG, FF16, jArr[11], 14, 0x265e5a51)
194 | val GG4 = GG(FF16, GG3, GG2, GG, jArr[0], 20, 3921069994L)
195 | val GG5 = GG(GG, GG4, GG3, GG2, jArr[5], 5, 3593408605L)
196 | val GG6 = GG(GG2, GG5, GG4, GG3, jArr[10], 9, 0x02441453)
197 | val GG7 = GG(GG3, GG6, GG5, GG4, jArr[15], 14, 3634488961L)
198 | val GG8 = GG(GG4, GG7, GG6, GG5, jArr[4], 20, 3889429448L)
199 | val GG9 = GG(GG5, GG8, GG7, GG6, jArr[9], 5, 0x21e1cde6)
200 | val GG10 = GG(GG6, GG9, GG8, GG7, jArr[14], 9, 3275163606L)
201 | val GG11 = GG(GG7, GG10, GG9, GG8, jArr[3], 14, 4107603335L)
202 | val GG12 = GG(GG8, GG11, GG10, GG9, jArr[8], 20, 0x455a14ed)
203 | val GG13 = GG(GG9, GG12, GG11, GG10, jArr[13], 5, 2850285829L)
204 | val GG14 = GG(GG10, GG13, GG12, GG11, jArr[2], 9, 4243563512L)
205 | val GG15 = GG(GG11, GG14, GG13, GG12, jArr[7], 14, 0x676f02d9)
206 | val GG16 = GG(GG12, GG15, GG14, GG13, jArr[12], 20, 2368359562L)
207 | val HH = HH(GG13, GG16, GG15, GG14, jArr[5], 4, 4294588738L)
208 | val HH2 = HH(GG14, HH, GG16, GG15, jArr[8], 11, 2272392833L)
209 | val HH3 = HH(GG15, HH2, HH, GG16, jArr[11], 16, 0x6d9d6122)
210 | val HH4 = HH(GG16, HH3, HH2, HH, jArr[14], 23, 4259657740L)
211 | val HH5 = HH(HH, HH4, HH3, HH2, jArr[1], 4, 2763975236L)
212 | val HH6 = HH(HH2, HH5, HH4, HH3, jArr[4], 11, 0x4bdecfa9)
213 | val HH7 = HH(HH3, HH6, HH5, HH4, jArr[7], 16, 4139469664L)
214 | val HH8 = HH(HH4, HH7, HH6, HH5, jArr[10], 23, 3200236656L)
215 | val HH9 = HH(HH5, HH8, HH7, HH6, jArr[13], 4, 0x289b7ec6)
216 | val HH10 = HH(HH6, HH9, HH8, HH7, jArr[0], 11, 3936430074L)
217 | val HH11 = HH(HH7, HH10, HH9, HH8, jArr[3], 16, 3572445317L)
218 | val HH12 = HH(HH8, HH11, HH10, HH9, jArr[6], 23, 0x04881d05)
219 | val HH13 = HH(HH9, HH12, HH11, HH10, jArr[9], 4, 3654602809L)
220 | val HH14 = HH(HH10, HH13, HH12, HH11, jArr[12], 11, 3873151461L)
221 | val HH15 = HH(HH11, HH14, HH13, HH12, jArr[15], 16, 0x1fa27cf8)
222 | val HH16 = HH(HH12, HH15, HH14, HH13, jArr[2], 23, 3299628645L)
223 | val II = II(HH13, HH16, HH15, HH14, jArr[0], 6, 4096336452L)
224 | val II2 = II(HH14, II, HH16, HH15, jArr[7], 10, 0x432aff97)
225 | val II3 = II(HH15, II2, II, HH16, jArr[14], 15, 2878612391L)
226 | val II4 = II(HH16, II3, II2, II, jArr[5], 21, 4237533241L)
227 | val II5 = II(II, II4, II3, II2, jArr[12], 6, 0x655b59c3)
228 | val II6 = II(II2, II5, II4, II3, jArr[3], 10, 2399980690L)
229 | val II7 = II(II3, II6, II5, II4, jArr[10], 15, 4293915773L)
230 | val II8 = II(II4, II7, II6, II5, jArr[1], 21, 2240044497L)
231 | val II9 = II(II5, II8, II7, II6, jArr[8], 6, 0x6fa87e4f)
232 | val II10 = II(II6, II9, II8, II7, jArr[15], 10, 4264355552L)
233 | val II11 = II(II7, II10, II9, II8, jArr[6], 15, 2734768916L)
234 | val II12 = II(II8, II11, II10, II9, jArr[13], 21, 0x4e0811a1)
235 | val II13 = II(II9, II12, II11, II10, jArr[4], 6, 4149444226L)
236 | val II14 = II(II10, II13, II12, II11, jArr[11], 10, 3174756917L)
237 | val II15 = II(II11, II14, II13, II12, jArr[2], 15, 0x2ad7d2bb)
238 | val II16 = II(II12, II15, II14, II13, jArr[9], 21, 3951481745L)
239 | val jArr2 = state
240 | jArr2[0] = jArr2[0] + II13
241 | val jArr3 = state
242 | jArr3[1] = II16 + jArr3[1]
243 | val jArr4 = state
244 | jArr4[2] = jArr4[2] + II15
245 | val jArr5 = state
246 | jArr5[3] = jArr5[3] + II14
247 | }
248 |
249 | private fun Encode(bArr: ByteArray, jArr: LongArray, i: Int) {
250 | var i2 = 0
251 | var i3 = 0
252 | while (i3 < i) {
253 | bArr[i3] = (jArr[i2] and 255L).toInt().toByte()
254 | bArr[i3 + 1] = (jArr[i2] ushr 8 and 255L).toInt().toByte()
255 | bArr[i3 + 2] = (jArr[i2] ushr 16 and 255L).toInt().toByte()
256 | bArr[i3 + 3] = (jArr[i2] ushr 24 and 255L).toInt().toByte()
257 | i2++
258 | i3 += 4
259 | }
260 | }
261 |
262 | private fun Decode(jArr: LongArray, bArr: ByteArray, i: Int) {
263 | var i2 = 0
264 | var i3 = 0
265 | while (i3 < i) {
266 | jArr[i2] = b2iu(bArr[i3]) or (b2iu(bArr[i3 + 1]) shl 8) or (b2iu(
267 | bArr[i3 + 2]
268 | ) shl 16) or (b2iu(bArr[i3 + 3]) shl 24)
269 | i2++
270 | i3 += 4
271 | }
272 | }
273 |
274 | companion object {
275 | val PADDING = byteArrayOf(
276 | Byte.MIN_VALUE,
277 | 0,
278 | 0,
279 | 0,
280 | 0,
281 | 0,
282 | 0,
283 | 0,
284 | 0,
285 | 0,
286 | 0,
287 | 0,
288 | 0,
289 | 0,
290 | 0,
291 | 0,
292 | 0,
293 | 0,
294 | 0,
295 | 0,
296 | 0,
297 | 0,
298 | 0,
299 | 0,
300 | 0,
301 | 0,
302 | 0,
303 | 0,
304 | 0,
305 | 0,
306 | 0,
307 | 0,
308 | 0,
309 | 0,
310 | 0,
311 | 0,
312 | 0,
313 | 0,
314 | 0,
315 | 0,
316 | 0,
317 | 0,
318 | 0,
319 | 0,
320 | 0,
321 | 0,
322 | 0,
323 | 0,
324 | 0,
325 | 0,
326 | 0,
327 | 0,
328 | 0,
329 | 0,
330 | 0,
331 | 0,
332 | 0,
333 | 0,
334 | 0,
335 | 0,
336 | 0,
337 | 0,
338 | 0,
339 | 0
340 | )
341 |
342 | fun b2iu(b: Byte): Long {
343 | return if (b < 0) (b.toInt() and 255).toLong() else b.toLong()
344 | }
345 |
346 | fun byteHEX(b: Byte): String {
347 | val cArr = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')
348 | return String(charArrayOf(cArr[b.toInt() ushr 4 and 15], cArr[b.toInt() and 15]))
349 | }
350 |
351 | fun toMD5Byte(bArr: ByteArray): ByteArray {
352 | return MD5().getMD5(bArr)
353 | }
354 |
355 | fun toMD5Byte(str: String): ByteArray {
356 | val bytes: ByteArray
357 | bytes = try {
358 | str.toByteArray(charset("ISO8859_1"))
359 | } catch (e: UnsupportedEncodingException) {
360 | str.toByteArray()
361 | }
362 | return MD5().getMD5(bytes)
363 | }
364 |
365 | fun toMD5Byte(inputStream: InputStream, size: Long): ByteArray {
366 | return MD5().getMD5(inputStream, size)
367 | }
368 |
369 | fun toMD5(bArr: ByteArray): String {
370 | val md5 = MD5().getMD5(bArr)
371 | val str = StringBuilder()
372 | for (i in 0..15) {
373 | str.append(byteHEX(md5[i]))
374 | }
375 | return str.toString()
376 | }
377 |
378 | fun toMD5(str: String): String {
379 | val bytes: ByteArray
380 | bytes = try {
381 | str.toByteArray(charset("ISO8859_1"))
382 | } catch (e: UnsupportedEncodingException) {
383 | str.toByteArray()
384 | }
385 | val md5 = MD5().getMD5(bytes)
386 | val str2 = StringBuilder()
387 | for (i in 0..15) {
388 | str2.append(byteHEX(md5[i]))
389 | }
390 | return str2.toString()
391 | }
392 | }
393 | }
--------------------------------------------------------------------------------
/src/main/kotlin/moe/fuqiuluo/utils/ZlibUtil.kt:
--------------------------------------------------------------------------------
1 | package moe.fuqiuluo.utils
2 |
3 | import java.io.ByteArrayInputStream
4 | import java.io.ByteArrayOutputStream
5 | import java.io.IOException
6 | import java.util.zip.Deflater
7 | import java.util.zip.GZIPInputStream
8 | import java.util.zip.GZIPOutputStream
9 | import java.util.zip.Inflater
10 |
11 | object ZlibUtil {
12 | @JvmStatic
13 | fun unCompress(inputByte: ByteArray?): ByteArray {
14 | var len: Int
15 | val infill = Inflater()
16 | infill.setInput(inputByte)
17 | val bos = ByteArrayOutputStream()
18 | val outByte = ByteArray(1024)
19 | try {
20 | while (!infill.finished()) {
21 | len = infill.inflate(outByte)
22 | if (len == 0) {
23 | break
24 | }
25 | bos.write(outByte, 0, len)
26 | }
27 | infill.end()
28 | } catch (e: Exception) {
29 | e.printStackTrace()
30 | } finally {
31 | try {
32 | bos.close()
33 | } catch (e: IOException) {
34 | e.printStackTrace()
35 | }
36 | }
37 | return bos.toByteArray()
38 | }
39 |
40 | @JvmStatic
41 | fun compress(inputByte: ByteArray?): ByteArray {
42 | var len: Int
43 | val defile = Deflater()
44 | defile.setInput(inputByte)
45 | defile.finish()
46 | val bos = ByteArrayOutputStream()
47 | val outputByte = ByteArray(1024)
48 | try {
49 | while (!defile.finished()) {
50 | len = defile.deflate(outputByte)
51 | bos.write(outputByte, 0, len)
52 | }
53 | defile.end()
54 | } finally {
55 | try {
56 | bos.close()
57 | } catch (e: IOException) {
58 | e.printStackTrace()
59 | }
60 | }
61 | return bos.toByteArray()
62 | }
63 |
64 | @JvmStatic
65 | fun gzip(bytes: ByteArray): ByteArray {
66 | var out = EMPTY_BYTE_ARRAY
67 | if (bytes.isNotEmpty()) {
68 | ByteArrayOutputStream().use {
69 | GZIPOutputStream(it).use {
70 | it.write(bytes)
71 | }
72 | out = it.toByteArray()
73 | }
74 | }
75 | return out
76 | }
77 |
78 | @JvmStatic
79 | fun ungzip(bArr: ByteArray): ByteArray {
80 | val byteArrayOutputStream = ByteArrayOutputStream()
81 | try {
82 | val gZIPInputStream = GZIPInputStream(ByteArrayInputStream(bArr))
83 | val bArr2 = ByteArray(256)
84 | while (true) {
85 | val read = gZIPInputStream.read(bArr2)
86 | if (read < 0) {
87 | break
88 | }
89 | byteArrayOutputStream.write(bArr2, 0, read)
90 | }
91 | } catch (e: Exception) {
92 | e.printStackTrace()
93 | }
94 | return byteArrayOutputStream.toByteArray()
95 | }
96 | }
--------------------------------------------------------------------------------
/txlib/3.5.1/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "server": {
3 | "host": "0.0.0.0",
4 | "port": 8080
5 | },
6 | "share_token": true,
7 | "count": 10,
8 | "key": "114514",
9 | "auto_register": true,
10 | "protocol": {
11 | "package_name": "com.tencent.tim",
12 | "qua": "V1_AND_SQ_8.3.9_351_TIM_D",
13 | "version": "3.5.1",
14 | "code": "1298"
15 | },
16 | "unidbg": {
17 | "dynarmic": false,
18 | "unicorn": true,
19 | "kvm": false,
20 | "debug": true
21 | }
22 | }
--------------------------------------------------------------------------------
/txlib/3.5.1/dtconfig.json:
--------------------------------------------------------------------------------
1 | {"en":[
2 | [2,5,0,14,15,3,10,1,13,12,7,11,6,8,4,9],
3 | [14,13,12,5,7,15,10,11,4,6,2,3,0,8,9,1],
4 | [10,5,6,13,15,3,11,8,2,9,0,14,12,4,7,1],
5 | [6,14,3,0,9,10,8,13,4,11,15,5,2,1,12,7],
6 | [2,6,11,12,15,7,5,8,1,13,4,0,3,14,9,10],
7 | [14,0,6,3,2,12,4,15,8,1,5,9,10,7,11,13],
8 | [10,7,0,13,2,4,1,15,5,12,14,6,8,9,11,3],
9 | [5,1,15,7,2,10,11,12,13,0,14,6,3,4,8,9],
10 | [2,7,5,10,14,3,1,12,4,11,9,6,15,0,13,8],
11 | [13,2,6,7,9,1,5,4,8,10,12,15,0,14,11,3],
12 | [9,10,6,15,2,12,5,13,0,3,1,8,7,11,14,4],
13 | [5,3,11,15,8,14,1,9,6,0,4,7,13,2,12,10],
14 | [1,11,5,6,15,8,7,13,2,4,9,14,3,12,0,10],
15 | [13,3,15,14,7,0,2,5,12,8,4,10,9,6,1,11],
16 | [9,12,10,5,15,2,7,13,4,11,3,14,6,1,0,8],
17 | [5,4,1,13,7,2,12,8,14,0,3,11,9,15,10,6],
18 | [1,12,15,5,2,0,10,7,14,8,3,4,13,9,6,11],
19 | [13,5,9,12,7,4,14,10,3,0,2,8,11,6,1,15],
20 | [9,13,3,4,15,1,14,0,5,11,10,8,7,12,6,2],
21 | [5,0,14,11,7,10,2,9,4,6,3,1,15,8,12,13],
22 | [1,14,8,3,0,6,10,7,9,15,2,5,11,13,4,12],
23 | [13,6,3,10,1,15,9,0,7,12,2,8,5,14,4,11],
24 | [9,14,13,2,15,5,4,11,0,6,1,12,8,10,3,7],
25 | [4,9,14,7,8,12,2,5,10,13,3,6,0,1,11,15],
26 | [0,1,9,15,12,11,4,14,5,10,6,8,3,7,13,2],
27 | [12,9,3,7,8,1,10,6,11,0,5,2,13,4,14,15],
28 | [8,2,13,14,5,15,3,7,10,4,6,11,12,1,0,9],
29 | [4,10,8,6,2,5,15,9,14,0,13,7,3,12,1,11],
30 | [0,2,1,13,15,11,10,4,7,9,14,8,3,6,5,12],
31 | [12,11,13,5,7,0,15,6,4,3,2,10,8,1,9,14],
32 | [8,3,7,12,15,9,0,1,14,5,4,2,13,11,10,6],
33 | [3,14,8,2,0,1,11,6,15,12,13,10,9,4,7,5]
34 | ],
35 | "de": [
36 | [11,0,9,6,14,8,4,1,2,7,10,3,15,12,13,5],
37 | [11,9,3,2,6,15,5,13,12,4,10,14,8,0,7,1],
38 | [12,10,14,7,2,13,11,5,0,3,1,15,4,6,8,9],
39 | [8,13,1,15,0,7,11,6,14,5,3,10,4,12,2,9],
40 | [0,13,3,12,9,11,7,6,2,5,1,15,4,14,10,8],
41 | [12,2,10,1,5,7,14,9,8,6,4,11,0,15,13,3],
42 | [13,11,14,10,8,5,4,3,0,15,9,1,12,2,7,6],
43 | [15,6,12,1,0,10,13,14,2,11,4,7,5,8,3,9],
44 | [0,6,2,8,9,10,3,7,12,5,11,15,1,13,14,4],
45 | [2,7,10,5,1,9,12,15,13,6,4,8,0,3,14,11],
46 | [15,14,7,12,13,3,6,2,0,4,8,10,1,9,5,11],
47 | [14,9,7,11,0,1,4,13,15,3,5,12,2,10,8,6],
48 | [0,8,12,11,3,14,15,13,7,4,10,9,2,1,6,5],
49 | [3,5,11,9,12,1,2,8,14,4,13,15,0,6,7,10],
50 | [9,10,2,14,6,4,8,13,0,12,5,3,11,7,1,15],
51 | [13,8,3,11,0,14,1,12,7,4,5,2,10,6,15,9],
52 | [0,14,6,4,3,2,9,10,8,5,13,11,15,7,1,12],
53 | [10,4,6,9,12,7,15,1,3,2,13,5,0,14,11,8],
54 | [6,9,12,10,5,8,15,11,0,7,2,14,13,4,3,1],
55 | [2,6,8,4,0,12,15,1,9,3,10,14,7,5,11,13],
56 | [0,2,10,6,12,8,11,7,3,4,13,9,5,15,1,14],
57 | [5,14,7,11,6,2,9,10,4,3,1,0,8,15,12,13],
58 | [7,11,1,12,9,4,6,0,5,10,2,8,14,15,3,13],
59 | [9,10,3,0,14,2,7,6,8,11,4,1,12,15,5,13],
60 | [2,9,4,1,6,11,13,8,10,14,12,15,5,3,7,0],
61 | [1,6,4,8,3,14,13,9,15,12,2,0,5,11,7,10],
62 | [5,2,9,8,1,12,10,0,4,7,6,11,3,15,14,13],
63 | [8,11,0,9,5,2,10,6,4,15,3,7,12,14,1,13],
64 | [5,14,8,11,9,4,15,1,7,3,10,12,6,13,0,2],
65 | [2,5,3,4,9,15,8,11,6,10,0,14,12,7,13,1],
66 | [14,15,8,6,2,10,0,1,3,13,5,11,7,9,4,12],
67 | [15,6,0,8,7,13,12,9,10,14,3,4,5,11,1,2]
68 | ]
69 | }
70 |
--------------------------------------------------------------------------------
/txlib/3.5.1/libfekit.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XZhouQD/unidbg-fetch-qsign/e3bb92872719f5e8423d38d182d52b612717e064/txlib/3.5.1/libfekit.so
--------------------------------------------------------------------------------
/txlib/3.5.1/libpoxy.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XZhouQD/unidbg-fetch-qsign/e3bb92872719f5e8423d38d182d52b612717e064/txlib/3.5.1/libpoxy.so
--------------------------------------------------------------------------------
/txlib/3.5.1/libwtecdh.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XZhouQD/unidbg-fetch-qsign/e3bb92872719f5e8423d38d182d52b612717e064/txlib/3.5.1/libwtecdh.so
--------------------------------------------------------------------------------
/txlib/3.5.2/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "server": {
3 | "host": "0.0.0.0",
4 | "port": 8080
5 | },
6 | "share_token": true,
7 | "count": 10,
8 | "key": "114514",
9 | "auto_register": true,
10 | "protocol": {
11 | "package_name": "com.tencent.tim",
12 | "qua": "V1_AND_SQ_8.3.9_352_TIM_D",
13 | "version": "3.5.2",
14 | "code": "1308"
15 | },
16 | "unidbg": {
17 | "dynarmic": false,
18 | "kvm": false,
19 | "unicorn": true,
20 | "debug": true
21 | }
22 | }
--------------------------------------------------------------------------------
/txlib/3.5.2/dtconfig.json:
--------------------------------------------------------------------------------
1 | {"en":[
2 | [2,5,0,14,15,3,10,1,13,12,7,11,6,8,4,9],
3 | [14,13,12,5,7,15,10,11,4,6,2,3,0,8,9,1],
4 | [10,5,6,13,15,3,11,8,2,9,0,14,12,4,7,1],
5 | [6,14,3,0,9,10,8,13,4,11,15,5,2,1,12,7],
6 | [2,6,11,12,15,7,5,8,1,13,4,0,3,14,9,10],
7 | [14,0,6,3,2,12,4,15,8,1,5,9,10,7,11,13],
8 | [10,7,0,13,2,4,1,15,5,12,14,6,8,9,11,3],
9 | [5,1,15,7,2,10,11,12,13,0,14,6,3,4,8,9],
10 | [2,7,5,10,14,3,1,12,4,11,9,6,15,0,13,8],
11 | [13,2,6,7,9,1,5,4,8,10,12,15,0,14,11,3],
12 | [9,10,6,15,2,12,5,13,0,3,1,8,7,11,14,4],
13 | [5,3,11,15,8,14,1,9,6,0,4,7,13,2,12,10],
14 | [1,11,5,6,15,8,7,13,2,4,9,14,3,12,0,10],
15 | [13,3,15,14,7,0,2,5,12,8,4,10,9,6,1,11],
16 | [9,12,10,5,15,2,7,13,4,11,3,14,6,1,0,8],
17 | [5,4,1,13,7,2,12,8,14,0,3,11,9,15,10,6],
18 | [1,12,15,5,2,0,10,7,14,8,3,4,13,9,6,11],
19 | [13,5,9,12,7,4,14,10,3,0,2,8,11,6,1,15],
20 | [9,13,3,4,15,1,14,0,5,11,10,8,7,12,6,2],
21 | [5,0,14,11,7,10,2,9,4,6,3,1,15,8,12,13],
22 | [1,14,8,3,0,6,10,7,9,15,2,5,11,13,4,12],
23 | [13,6,3,10,1,15,9,0,7,12,2,8,5,14,4,11],
24 | [9,14,13,2,15,5,4,11,0,6,1,12,8,10,3,7],
25 | [4,9,14,7,8,12,2,5,10,13,3,6,0,1,11,15],
26 | [0,1,9,15,12,11,4,14,5,10,6,8,3,7,13,2],
27 | [12,9,3,7,8,1,10,6,11,0,5,2,13,4,14,15],
28 | [8,2,13,14,5,15,3,7,10,4,6,11,12,1,0,9],
29 | [4,10,8,6,2,5,15,9,14,0,13,7,3,12,1,11],
30 | [0,2,1,13,15,11,10,4,7,9,14,8,3,6,5,12],
31 | [12,11,13,5,7,0,15,6,4,3,2,10,8,1,9,14],
32 | [8,3,7,12,15,9,0,1,14,5,4,2,13,11,10,6],
33 | [3,14,8,2,0,1,11,6,15,12,13,10,9,4,7,5]
34 | ],
35 | "de": [
36 | [11,0,9,6,14,8,4,1,2,7,10,3,15,12,13,5],
37 | [11,9,3,2,6,15,5,13,12,4,10,14,8,0,7,1],
38 | [12,10,14,7,2,13,11,5,0,3,1,15,4,6,8,9],
39 | [8,13,1,15,0,7,11,6,14,5,3,10,4,12,2,9],
40 | [0,13,3,12,9,11,7,6,2,5,1,15,4,14,10,8],
41 | [12,2,10,1,5,7,14,9,8,6,4,11,0,15,13,3],
42 | [13,11,14,10,8,5,4,3,0,15,9,1,12,2,7,6],
43 | [15,6,12,1,0,10,13,14,2,11,4,7,5,8,3,9],
44 | [0,6,2,8,9,10,3,7,12,5,11,15,1,13,14,4],
45 | [2,7,10,5,1,9,12,15,13,6,4,8,0,3,14,11],
46 | [15,14,7,12,13,3,6,2,0,4,8,10,1,9,5,11],
47 | [14,9,7,11,0,1,4,13,15,3,5,12,2,10,8,6],
48 | [0,8,12,11,3,14,15,13,7,4,10,9,2,1,6,5],
49 | [3,5,11,9,12,1,2,8,14,4,13,15,0,6,7,10],
50 | [9,10,2,14,6,4,8,13,0,12,5,3,11,7,1,15],
51 | [13,8,3,11,0,14,1,12,7,4,5,2,10,6,15,9],
52 | [0,14,6,4,3,2,9,10,8,5,13,11,15,7,1,12],
53 | [10,4,6,9,12,7,15,1,3,2,13,5,0,14,11,8],
54 | [6,9,12,10,5,8,15,11,0,7,2,14,13,4,3,1],
55 | [2,6,8,4,0,12,15,1,9,3,10,14,7,5,11,13],
56 | [0,2,10,6,12,8,11,7,3,4,13,9,5,15,1,14],
57 | [5,14,7,11,6,2,9,10,4,3,1,0,8,15,12,13],
58 | [7,11,1,12,9,4,6,0,5,10,2,8,14,15,3,13],
59 | [9,10,3,0,14,2,7,6,8,11,4,1,12,15,5,13],
60 | [2,9,4,1,6,11,13,8,10,14,12,15,5,3,7,0],
61 | [1,6,4,8,3,14,13,9,15,12,2,0,5,11,7,10],
62 | [5,2,9,8,1,12,10,0,4,7,6,11,3,15,14,13],
63 | [8,11,0,9,5,2,10,6,4,15,3,7,12,14,1,13],
64 | [5,14,8,11,9,4,15,1,7,3,10,12,6,13,0,2],
65 | [2,5,3,4,9,15,8,11,6,10,0,14,12,7,13,1],
66 | [14,15,8,6,2,10,0,1,3,13,5,11,7,9,4,12],
67 | [15,6,0,8,7,13,12,9,10,14,3,4,5,11,1,2]
68 | ]
69 | }
70 |
--------------------------------------------------------------------------------
/txlib/3.5.2/libfekit.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XZhouQD/unidbg-fetch-qsign/e3bb92872719f5e8423d38d182d52b612717e064/txlib/3.5.2/libfekit.so
--------------------------------------------------------------------------------
/txlib/3.5.2/libpoxy.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XZhouQD/unidbg-fetch-qsign/e3bb92872719f5e8423d38d182d52b612717e064/txlib/3.5.2/libpoxy.so
--------------------------------------------------------------------------------
/txlib/3.5.2/libwtecdh.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XZhouQD/unidbg-fetch-qsign/e3bb92872719f5e8423d38d182d52b612717e064/txlib/3.5.2/libwtecdh.so
--------------------------------------------------------------------------------
/txlib/8.9.63/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "server": {
3 | "host": "0.0.0.0",
4 | "port": 8080
5 | },
6 | "share_token": true,
7 | "count": 10,
8 | "key": "114514",
9 | "auto_register": false,
10 | "protocol": {
11 | "package_name": "com.tencent.mobileqq",
12 | "qua": "V1_AND_SQ_8.9.63_4194_YYB_D",
13 | "version": "8.9.63",
14 | "code": "4194"
15 | },
16 | "unidbg": {
17 | "dynarmic": false,
18 | "unicorn": true,
19 | "kvm": false,
20 | "debug": false
21 | }
22 | }
--------------------------------------------------------------------------------
/txlib/8.9.63/dtconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "en": [
3 | [2, 5, 0, 14, 15, 3, 10, 1, 13, 12, 7, 11, 6, 8, 4, 9],
4 | [14, 13, 12, 5, 7, 15, 10, 11, 4, 6, 2, 3, 0, 8, 9, 1],
5 | [10, 5, 6, 13, 15, 3, 11, 8, 2, 9, 0, 14, 12, 4, 7, 1],
6 | [6, 14, 3, 0, 9, 10, 8, 13, 4, 11, 15, 5, 2, 1, 12, 7],
7 | [2, 6, 11, 12, 15, 7, 5, 8, 1, 13, 4, 0, 3, 14, 9, 10],
8 | [14, 0, 6, 3, 2, 12, 4, 15, 8, 1, 5, 9, 10, 7, 11, 13],
9 | [10, 7, 0, 13, 2, 4, 1, 15, 5, 12, 14, 6, 8, 9, 11, 3],
10 | [5, 1, 15, 7, 2, 10, 11, 12, 13, 0, 14, 6, 3, 4, 8, 9],
11 | [2, 7, 5, 10, 14, 3, 1, 12, 4, 11, 9, 6, 15, 0, 13, 8],
12 | [13, 2, 6, 7, 9, 1, 5, 4, 8, 10, 12, 15, 0, 14, 11, 3],
13 | [9, 10, 6, 15, 2, 12, 5, 13, 0, 3, 1, 8, 7, 11, 14, 4],
14 | [5, 3, 11, 15, 8, 14, 1, 9, 6, 0, 4, 7, 13, 2, 12, 10],
15 | [1, 11, 5, 6, 15, 8, 7, 13, 2, 4, 9, 14, 3, 12, 0, 10],
16 | [13, 3, 15, 14, 7, 0, 2, 5, 12, 8, 4, 10, 9, 6, 1, 11],
17 | [9, 12, 10, 5, 15, 2, 7, 13, 4, 11, 3, 14, 6, 1, 0, 8],
18 | [5, 4, 1, 13, 7, 2, 12, 8, 14, 0, 3, 11, 9, 15, 10, 6],
19 | [1, 12, 15, 5, 2, 0, 10, 7, 14, 8, 3, 4, 13, 9, 6, 11],
20 | [13, 5, 9, 12, 7, 4, 14, 10, 3, 0, 2, 8, 11, 6, 1, 15],
21 | [9, 13, 3, 4, 15, 1, 14, 0, 5, 11, 10, 8, 7, 12, 6, 2],
22 | [5, 0, 14, 11, 7, 10, 2, 9, 4, 6, 3, 1, 15, 8, 12, 13],
23 | [1, 14, 8, 3, 0, 6, 10, 7, 9, 15, 2, 5, 11, 13, 4, 12],
24 | [13, 6, 3, 10, 1, 15, 9, 0, 7, 12, 2, 8, 5, 14, 4, 11],
25 | [9, 14, 13, 2, 15, 5, 4, 11, 0, 6, 1, 12, 8, 10, 3, 7],
26 | [4, 9, 14, 7, 8, 12, 2, 5, 10, 13, 3, 6, 0, 1, 11, 15],
27 | [0, 1, 9, 15, 12, 11, 4, 14, 5, 10, 6, 8, 3, 7, 13, 2],
28 | [12, 9, 3, 7, 8, 1, 10, 6, 11, 0, 5, 2, 13, 4, 14, 15],
29 | [8, 2, 13, 14, 5, 15, 3, 7, 10, 4, 6, 11, 12, 1, 0, 9],
30 | [4, 10, 8, 6, 2, 5, 15, 9, 14, 0, 13, 7, 3, 12, 1, 11],
31 | [0, 2, 1, 13, 15, 11, 10, 4, 7, 9, 14, 8, 3, 6, 5, 12],
32 | [12, 11, 13, 5, 7, 0, 15, 6, 4, 3, 2, 10, 8, 1, 9, 14],
33 | [8, 3, 7, 12, 15, 9, 0, 1, 14, 5, 4, 2, 13, 11, 10, 6],
34 | [3, 14, 8, 2, 0, 1, 11, 6, 15, 12, 13, 10, 9, 4, 7, 5]
35 | ],
36 | "de": [
37 | [11, 0, 9, 6, 14, 8, 4, 1, 2, 7, 10, 3, 15, 12, 13, 5],
38 | [11, 9, 3, 2, 6, 15, 5, 13, 12, 4, 10, 14, 8, 0, 7, 1],
39 | [12, 10, 14, 7, 2, 13, 11, 5, 0, 3, 1, 15, 4, 6, 8, 9],
40 | [8, 13, 1, 15, 0, 7, 11, 6, 14, 5, 3, 10, 4, 12, 2, 9],
41 | [0, 13, 3, 12, 9, 11, 7, 6, 2, 5, 1, 15, 4, 14, 10, 8],
42 | [12, 2, 10, 1, 5, 7, 14, 9, 8, 6, 4, 11, 0, 15, 13, 3],
43 | [13, 11, 14, 10, 8, 5, 4, 3, 0, 15, 9, 1, 12, 2, 7, 6],
44 | [15, 6, 12, 1, 0, 10, 13, 14, 2, 11, 4, 7, 5, 8, 3, 9],
45 | [0, 6, 2, 8, 9, 10, 3, 7, 12, 5, 11, 15, 1, 13, 14, 4],
46 | [2, 7, 10, 5, 1, 9, 12, 15, 13, 6, 4, 8, 0, 3, 14, 11],
47 | [15, 14, 7, 12, 13, 3, 6, 2, 0, 4, 8, 10, 1, 9, 5, 11],
48 | [14, 9, 7, 11, 0, 1, 4, 13, 15, 3, 5, 12, 2, 10, 8, 6],
49 | [0, 8, 12, 11, 3, 14, 15, 13, 7, 4, 10, 9, 2, 1, 6, 5],
50 | [3, 5, 11, 9, 12, 1, 2, 8, 14, 4, 13, 15, 0, 6, 7, 10],
51 | [9, 10, 2, 14, 6, 4, 8, 13, 0, 12, 5, 3, 11, 7, 1, 15],
52 | [13, 8, 3, 11, 0, 14, 1, 12, 7, 4, 5, 2, 10, 6, 15, 9],
53 | [0, 14, 6, 4, 3, 2, 9, 10, 8, 5, 13, 11, 15, 7, 1, 12],
54 | [10, 4, 6, 9, 12, 7, 15, 1, 3, 2, 13, 5, 0, 14, 11, 8],
55 | [6, 9, 12, 10, 5, 8, 15, 11, 0, 7, 2, 14, 13, 4, 3, 1],
56 | [2, 6, 8, 4, 0, 12, 15, 1, 9, 3, 10, 14, 7, 5, 11, 13],
57 | [0, 2, 10, 6, 12, 8, 11, 7, 3, 4, 13, 9, 5, 15, 1, 14],
58 | [5, 14, 7, 11, 6, 2, 9, 10, 4, 3, 1, 0, 8, 15, 12, 13],
59 | [7, 11, 1, 12, 9, 4, 6, 0, 5, 10, 2, 8, 14, 15, 3, 13],
60 | [9, 10, 3, 0, 14, 2, 7, 6, 8, 11, 4, 1, 12, 15, 5, 13],
61 | [2, 9, 4, 1, 6, 11, 13, 8, 10, 14, 12, 15, 5, 3, 7, 0],
62 | [1, 6, 4, 8, 3, 14, 13, 9, 15, 12, 2, 0, 5, 11, 7, 10],
63 | [5, 2, 9, 8, 1, 12, 10, 0, 4, 7, 6, 11, 3, 15, 14, 13],
64 | [8, 11, 0, 9, 5, 2, 10, 6, 4, 15, 3, 7, 12, 14, 1, 13],
65 | [5, 14, 8, 11, 9, 4, 15, 1, 7, 3, 10, 12, 6, 13, 0, 2],
66 | [2, 5, 3, 4, 9, 15, 8, 11, 6, 10, 0, 14, 12, 7, 13, 1],
67 | [14, 15, 8, 6, 2, 10, 0, 1, 3, 13, 5, 11, 7, 9, 4, 12],
68 | [15, 6, 0, 8, 7, 13, 12, 9, 10, 14, 3, 4, 5, 11, 1, 2]
69 | ]
70 | }
--------------------------------------------------------------------------------
/txlib/8.9.63/libfekit.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XZhouQD/unidbg-fetch-qsign/e3bb92872719f5e8423d38d182d52b612717e064/txlib/8.9.63/libfekit.so
--------------------------------------------------------------------------------
/txlib/8.9.68/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "server": {
3 | "host": "0.0.0.0",
4 | "port": 8080
5 | },
6 | "share_token": true,
7 | "count": 10,
8 | "key": "114514",
9 | "auto_register": false,
10 | "protocol": {
11 | "package_name": "com.tencent.mobileqq",
12 | "qua": "V1_AND_SQ_8.9.68_4264_YYB_D",
13 | "version": "8.9.68",
14 | "code": "4264"
15 | },
16 | "unidbg": {
17 | "dynarmic": false,
18 | "unicorn": true,
19 | "kvm": false,
20 | "debug": true
21 | },
22 | "black_list": [
23 | 1008611
24 | ]
25 | }
--------------------------------------------------------------------------------
/txlib/8.9.68/dtconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "en": [
3 | [15, 10, 13, 4, 0, 5, 3, 1, 11, 12, 7, 2, 14, 6, 9, 8],
4 | [12, 1, 13, 4, 14, 9, 8, 6, 5, 3, 10, 7, 11, 2, 0, 15],
5 | [10, 5, 14, 0, 9, 1, 7, 4, 11, 8, 3, 15, 12, 6, 13, 2],
6 | [7, 11, 2, 14, 3, 10, 1, 8, 0, 15, 9, 6, 13, 4, 5, 12],
7 | [5, 15, 1, 2, 4, 13, 7, 8, 3, 6, 11, 0, 9, 10, 12, 14],
8 | [2, 5, 0, 9, 3, 15, 11, 7, 8, 13, 10, 4, 1, 14, 6, 12],
9 | [15, 12, 6, 14, 7, 2, 0, 10, 11, 13, 3, 5, 1, 4, 9, 8],
10 | [13, 2, 0, 1, 8, 10, 4, 14, 11, 12, 7, 3, 15, 9, 5, 6],
11 | [10, 6, 7, 8, 9, 3, 15, 1, 2, 5, 11, 12, 13, 14, 0, 4],
12 | [8, 10, 4, 2, 13, 15, 12, 7, 6, 3, 14, 0, 1, 5, 9, 11],
13 | [5, 7, 12, 6, 0, 2, 14, 3, 1, 13, 9, 10, 15, 11, 8, 4],
14 | [2, 7, 12, 1, 9, 10, 4, 8, 13, 11, 6, 3, 0, 5, 15, 14],
15 | [0, 11, 9, 3, 12, 8, 14, 13, 5, 4, 10, 15, 7, 2, 1, 6],
16 | [13, 1, 0, 12, 6, 14, 7, 11, 3, 10, 2, 5, 15, 8, 4, 9],
17 | [10, 7, 5, 1, 0, 6, 9, 13, 14, 8, 3, 15, 11, 12, 4, 2],
18 | [7, 14, 5, 13, 4, 11, 15, 10, 8, 0, 12, 2, 3, 1, 9, 6],
19 | [5, 2, 1, 12, 10, 14, 4, 15, 9, 8, 6, 0, 13, 11, 7, 3],
20 | [2, 8, 6, 5, 1, 3, 14, 10, 0, 12, 4, 13, 7, 15, 9, 11],
21 | [0, 12, 3, 11, 10, 5, 4, 14, 9, 7, 1, 2, 13, 8, 6, 15],
22 | [13, 3, 7, 11, 4, 10, 15, 0, 5, 2, 6, 12, 14, 9, 8, 1],
23 | [11, 7, 4, 6, 3, 0, 14, 5, 2, 9, 13, 15, 10, 8, 12, 1],
24 | [8, 13, 0, 11, 4, 1, 3, 9, 10, 15, 12, 5, 14, 7, 6, 2],
25 | [5, 3, 11, 10, 13, 6, 1, 15, 12, 8, 2, 4, 9, 14, 0, 7],
26 | [3, 7, 9, 6, 0, 5, 10, 14, 1, 13, 11, 4, 2, 15, 8, 12],
27 | [0, 14, 12, 15, 11, 1, 3, 10, 8, 2, 9, 6, 13, 5, 7, 4],
28 | [13, 4, 8, 6, 3, 7, 10, 0, 14, 5, 9, 1, 15, 12, 2, 11],
29 | [11, 8, 13, 5, 3, 14, 6, 9, 1, 0, 12, 15, 2, 7, 10, 4],
30 | [8, 14, 13, 10, 7, 3, 0, 6, 11, 12, 5, 1, 15, 4, 9, 2],
31 | [6, 2, 14, 10, 15, 1, 5, 8, 9, 7, 11, 13, 4, 3, 12, 0],
32 | [3, 9, 2, 4, 5, 8, 1, 7, 11, 10, 12, 0, 15, 13, 6, 14],
33 | [0, 15, 6, 14, 11, 2, 1, 3, 13, 4, 10, 12, 7, 5, 8, 9],
34 | [13, 5, 10, 7, 2, 4, 11, 0, 14, 8, 1, 9, 3, 12, 6, 15]
35 | ],
36 | "de": [
37 | [13, 7, 12, 2, 14, 11, 3, 8, 5, 9, 10, 6, 1, 15, 0, 4],
38 | [13, 8, 4, 1, 9, 15, 12, 2, 11, 7, 10, 0, 3, 5, 6, 14],
39 | [7, 3, 9, 13, 2, 6, 14, 1, 5, 0, 8, 4, 11, 12, 10, 15],
40 | [9, 5, 14, 13, 7, 10, 0, 6, 2, 15, 3, 4, 11, 1, 8, 12],
41 | [11, 1, 4, 9, 0, 2, 6, 10, 5, 8, 14, 15, 7, 3, 12, 13],
42 | [3, 0, 12, 10, 5, 13, 15, 1, 11, 2, 7, 4, 8, 9, 6, 14],
43 | [1, 13, 15, 12, 10, 6, 11, 4, 5, 3, 7, 9, 14, 2, 0, 8],
44 | [3, 8, 1, 11, 6, 4, 13, 10, 15, 7, 2, 5, 0, 14, 12, 9],
45 | [7, 6, 13, 9, 5, 14, 3, 15, 1, 0, 10, 4, 11, 8, 2, 12],
46 | [7, 14, 15, 5, 6, 11, 9, 0, 4, 10, 8, 2, 1, 12, 3, 13],
47 | [8, 5, 14, 1, 0, 4, 7, 13, 6, 10, 15, 3, 12, 9, 11, 2],
48 | [15, 0, 5, 2, 7, 3, 8, 12, 11, 1, 13, 4, 14, 6, 9, 10],
49 | [12, 8, 10, 6, 15, 5, 14, 9, 7, 3, 13, 11, 2, 1, 4, 0],
50 | [15, 6, 4, 1, 2, 5, 13, 11, 9, 14, 3, 12, 0, 10, 7, 8],
51 | [6, 4, 8, 9, 3, 12, 14, 2, 10, 0, 1, 15, 11, 13, 5, 7],
52 | [9, 13, 11, 12, 4, 2, 15, 0, 8, 14, 7, 5, 10, 3, 1, 6],
53 | [1, 4, 8, 10, 0, 7, 15, 9, 2, 3, 14, 13, 11, 6, 5, 12],
54 | [4, 0, 10, 3, 13, 7, 11, 9, 5, 6, 1, 14, 2, 12, 15, 8],
55 | [3, 4, 14, 8, 5, 13, 10, 9, 6, 2, 11, 15, 12, 7, 1, 0],
56 | [6, 10, 12, 7, 4, 1, 13, 8, 5, 3, 11, 14, 0, 2, 15, 9],
57 | [2, 11, 14, 13, 3, 9, 8, 12, 4, 1, 0, 15, 7, 10, 5, 6],
58 | [6, 15, 3, 4, 9, 7, 11, 0, 5, 14, 13, 10, 12, 8, 2, 1],
59 | [4, 14, 2, 5, 0, 1, 10, 7, 3, 13, 6, 15, 12, 8, 11, 9],
60 | [11, 13, 0, 3, 5, 14, 9, 7, 4, 1, 8, 15, 6, 12, 10, 2],
61 | [11, 3, 1, 8, 7, 12, 6, 10, 5, 9, 15, 14, 2, 13, 4, 0],
62 | [10, 13, 3, 14, 15, 12, 1, 2, 11, 7, 4, 6, 0, 5, 9, 8],
63 | [1, 15, 5, 3, 2, 6, 7, 4, 8, 11, 0, 14, 12, 13, 10, 9],
64 | [1, 10, 5, 7, 15, 14, 12, 0, 11, 8, 4, 2, 3, 6, 13, 9],
65 | [1, 2, 12, 14, 8, 0, 6, 10, 3, 9, 7, 15, 11, 4, 13, 5],
66 | [10, 5, 0, 14, 15, 13, 3, 11, 8, 2, 4, 1, 6, 7, 12, 9],
67 | [8, 1, 4, 12, 11, 7, 9, 5, 14, 6, 10, 3, 13, 2, 15, 0],
68 | [7, 10, 4, 12, 5, 1, 14, 3, 9, 11, 2, 6, 13, 0, 8, 15]
69 | ]
70 | }
--------------------------------------------------------------------------------
/txlib/8.9.68/libfekit.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XZhouQD/unidbg-fetch-qsign/e3bb92872719f5e8423d38d182d52b612717e064/txlib/8.9.68/libfekit.so
--------------------------------------------------------------------------------
/txlib/8.9.71/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "server": {
3 | "host": "0.0.0.0",
4 | "port": 8080
5 | },
6 | "share_token": true,
7 | "count": 100,
8 | "key": "114514",
9 | "auto_register": true,
10 | "protocol": {
11 | "package_name": "com.tencent.mobileqq",
12 | "qua": "V1_AND_SQ_8.9.71_4332_YYB_D",
13 | "version": "8.9.71",
14 | "code": "4332"
15 | },
16 | "unidbg": {
17 | "dynarmic": false,
18 | "unicorn": false,
19 | "kvm": false,
20 | "debug": false
21 | },
22 | "black_list": [
23 | 1008611
24 | ]
25 | }
--------------------------------------------------------------------------------
/txlib/8.9.71/dtconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "en": [
3 | [15, 10, 13, 4, 0, 5, 3, 1, 11, 12, 7, 2, 14, 6, 9, 8],
4 | [12, 1, 13, 4, 14, 9, 8, 6, 5, 3, 10, 7, 11, 2, 0, 15],
5 | [10, 5, 14, 0, 9, 1, 7, 4, 11, 8, 3, 15, 12, 6, 13, 2],
6 | [7, 11, 2, 14, 3, 10, 1, 8, 0, 15, 9, 6, 13, 4, 5, 12],
7 | [5, 15, 1, 2, 4, 13, 7, 8, 3, 6, 11, 0, 9, 10, 12, 14],
8 | [2, 5, 0, 9, 3, 15, 11, 7, 8, 13, 10, 4, 1, 14, 6, 12],
9 | [15, 12, 6, 14, 7, 2, 0, 10, 11, 13, 3, 5, 1, 4, 9, 8],
10 | [13, 2, 0, 1, 8, 10, 4, 14, 11, 12, 7, 3, 15, 9, 5, 6],
11 | [10, 6, 7, 8, 9, 3, 15, 1, 2, 5, 11, 12, 13, 14, 0, 4],
12 | [8, 10, 4, 2, 13, 15, 12, 7, 6, 3, 14, 0, 1, 5, 9, 11],
13 | [5, 7, 12, 6, 0, 2, 14, 3, 1, 13, 9, 10, 15, 11, 8, 4],
14 | [2, 7, 12, 1, 9, 10, 4, 8, 13, 11, 6, 3, 0, 5, 15, 14],
15 | [0, 11, 9, 3, 12, 8, 14, 13, 5, 4, 10, 15, 7, 2, 1, 6],
16 | [13, 1, 0, 12, 6, 14, 7, 11, 3, 10, 2, 5, 15, 8, 4, 9],
17 | [10, 7, 5, 1, 0, 6, 9, 13, 14, 8, 3, 15, 11, 12, 4, 2],
18 | [7, 14, 5, 13, 4, 11, 15, 10, 8, 0, 12, 2, 3, 1, 9, 6],
19 | [5, 2, 1, 12, 10, 14, 4, 15, 9, 8, 6, 0, 13, 11, 7, 3],
20 | [2, 8, 6, 5, 1, 3, 14, 10, 0, 12, 4, 13, 7, 15, 9, 11],
21 | [0, 12, 3, 11, 10, 5, 4, 14, 9, 7, 1, 2, 13, 8, 6, 15],
22 | [13, 3, 7, 11, 4, 10, 15, 0, 5, 2, 6, 12, 14, 9, 8, 1],
23 | [11, 7, 4, 6, 3, 0, 14, 5, 2, 9, 13, 15, 10, 8, 12, 1],
24 | [8, 13, 0, 11, 4, 1, 3, 9, 10, 15, 12, 5, 14, 7, 6, 2],
25 | [5, 3, 11, 10, 13, 6, 1, 15, 12, 8, 2, 4, 9, 14, 0, 7],
26 | [3, 7, 9, 6, 0, 5, 10, 14, 1, 13, 11, 4, 2, 15, 8, 12],
27 | [0, 14, 12, 15, 11, 1, 3, 10, 8, 2, 9, 6, 13, 5, 7, 4],
28 | [13, 4, 8, 6, 3, 7, 10, 0, 14, 5, 9, 1, 15, 12, 2, 11],
29 | [11, 8, 13, 5, 3, 14, 6, 9, 1, 0, 12, 15, 2, 7, 10, 4],
30 | [8, 14, 13, 10, 7, 3, 0, 6, 11, 12, 5, 1, 15, 4, 9, 2],
31 | [6, 2, 14, 10, 15, 1, 5, 8, 9, 7, 11, 13, 4, 3, 12, 0],
32 | [3, 9, 2, 4, 5, 8, 1, 7, 11, 10, 12, 0, 15, 13, 6, 14],
33 | [0, 15, 6, 14, 11, 2, 1, 3, 13, 4, 10, 12, 7, 5, 8, 9],
34 | [13, 5, 10, 7, 2, 4, 11, 0, 14, 8, 1, 9, 3, 12, 6, 15]
35 | ],
36 | "de": [
37 | [13, 7, 12, 2, 14, 11, 3, 8, 5, 9, 10, 6, 1, 15, 0, 4],
38 | [13, 8, 4, 1, 9, 15, 12, 2, 11, 7, 10, 0, 3, 5, 6, 14],
39 | [7, 3, 9, 13, 2, 6, 14, 1, 5, 0, 8, 4, 11, 12, 10, 15],
40 | [9, 5, 14, 13, 7, 10, 0, 6, 2, 15, 3, 4, 11, 1, 8, 12],
41 | [11, 1, 4, 9, 0, 2, 6, 10, 5, 8, 14, 15, 7, 3, 12, 13],
42 | [3, 0, 12, 10, 5, 13, 15, 1, 11, 2, 7, 4, 8, 9, 6, 14],
43 | [1, 13, 15, 12, 10, 6, 11, 4, 5, 3, 7, 9, 14, 2, 0, 8],
44 | [3, 8, 1, 11, 6, 4, 13, 10, 15, 7, 2, 5, 0, 14, 12, 9],
45 | [7, 6, 13, 9, 5, 14, 3, 15, 1, 0, 10, 4, 11, 8, 2, 12],
46 | [7, 14, 15, 5, 6, 11, 9, 0, 4, 10, 8, 2, 1, 12, 3, 13],
47 | [8, 5, 14, 1, 0, 4, 7, 13, 6, 10, 15, 3, 12, 9, 11, 2],
48 | [15, 0, 5, 2, 7, 3, 8, 12, 11, 1, 13, 4, 14, 6, 9, 10],
49 | [12, 8, 10, 6, 15, 5, 14, 9, 7, 3, 13, 11, 2, 1, 4, 0],
50 | [15, 6, 4, 1, 2, 5, 13, 11, 9, 14, 3, 12, 0, 10, 7, 8],
51 | [6, 4, 8, 9, 3, 12, 14, 2, 10, 0, 1, 15, 11, 13, 5, 7],
52 | [9, 13, 11, 12, 4, 2, 15, 0, 8, 14, 7, 5, 10, 3, 1, 6],
53 | [1, 4, 8, 10, 0, 7, 15, 9, 2, 3, 14, 13, 11, 6, 5, 12],
54 | [4, 0, 10, 3, 13, 7, 11, 9, 5, 6, 1, 14, 2, 12, 15, 8],
55 | [3, 4, 14, 8, 5, 13, 10, 9, 6, 2, 11, 15, 12, 7, 1, 0],
56 | [6, 10, 12, 7, 4, 1, 13, 8, 5, 3, 11, 14, 0, 2, 15, 9],
57 | [2, 11, 14, 13, 3, 9, 8, 12, 4, 1, 0, 15, 7, 10, 5, 6],
58 | [6, 15, 3, 4, 9, 7, 11, 0, 5, 14, 13, 10, 12, 8, 2, 1],
59 | [4, 14, 2, 5, 0, 1, 10, 7, 3, 13, 6, 15, 12, 8, 11, 9],
60 | [11, 13, 0, 3, 5, 14, 9, 7, 4, 1, 8, 15, 6, 12, 10, 2],
61 | [11, 3, 1, 8, 7, 12, 6, 10, 5, 9, 15, 14, 2, 13, 4, 0],
62 | [10, 13, 3, 14, 15, 12, 1, 2, 11, 7, 4, 6, 0, 5, 9, 8],
63 | [1, 15, 5, 3, 2, 6, 7, 4, 8, 11, 0, 14, 12, 13, 10, 9],
64 | [1, 10, 5, 7, 15, 14, 12, 0, 11, 8, 4, 2, 3, 6, 13, 9],
65 | [1, 2, 12, 14, 8, 0, 6, 10, 3, 9, 7, 15, 11, 4, 13, 5],
66 | [10, 5, 0, 14, 15, 13, 3, 11, 8, 2, 4, 1, 6, 7, 12, 9],
67 | [8, 1, 4, 12, 11, 7, 9, 5, 14, 6, 10, 3, 13, 2, 15, 0],
68 | [7, 10, 4, 12, 5, 1, 14, 3, 9, 11, 2, 6, 13, 0, 8, 15]
69 | ]
70 | }
--------------------------------------------------------------------------------
/txlib/8.9.71/libfekit.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XZhouQD/unidbg-fetch-qsign/e3bb92872719f5e8423d38d182d52b612717e064/txlib/8.9.71/libfekit.so
--------------------------------------------------------------------------------
/txlib/8.9.73/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "server": {
3 | "host": "0.0.0.0",
4 | "port": 8080
5 | },
6 | "share_token": true,
7 | "count": 10,
8 | "key": "114514",
9 | "auto_register": true,
10 | "protocol": {
11 | "package_name": "com.tencent.mobileqq",
12 | "qua": "V1_AND_SQ_8.9.73_4416_YYB_D",
13 | "version": "8.9.73",
14 | "code": "4416"
15 | },
16 | "unidbg": {
17 | "dynarmic": false,
18 | "kvm": false,
19 | "unicorn": true,
20 | "debug": true
21 | },
22 | "black_list": [
23 | 1008611
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/txlib/8.9.73/dtconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "en": [
3 | [15, 10, 13, 4, 0, 5, 3, 1, 11, 12, 7, 2, 14, 6, 9, 8],
4 | [12, 1, 13, 4, 14, 9, 8, 6, 5, 3, 10, 7, 11, 2, 0, 15],
5 | [10, 5, 14, 0, 9, 1, 7, 4, 11, 8, 3, 15, 12, 6, 13, 2],
6 | [7, 11, 2, 14, 3, 10, 1, 8, 0, 15, 9, 6, 13, 4, 5, 12],
7 | [5, 15, 1, 2, 4, 13, 7, 8, 3, 6, 11, 0, 9, 10, 12, 14],
8 | [2, 5, 0, 9, 3, 15, 11, 7, 8, 13, 10, 4, 1, 14, 6, 12],
9 | [15, 12, 6, 14, 7, 2, 0, 10, 11, 13, 3, 5, 1, 4, 9, 8],
10 | [13, 2, 0, 1, 8, 10, 4, 14, 11, 12, 7, 3, 15, 9, 5, 6],
11 | [10, 6, 7, 8, 9, 3, 15, 1, 2, 5, 11, 12, 13, 14, 0, 4],
12 | [8, 10, 4, 2, 13, 15, 12, 7, 6, 3, 14, 0, 1, 5, 9, 11],
13 | [5, 7, 12, 6, 0, 2, 14, 3, 1, 13, 9, 10, 15, 11, 8, 4],
14 | [2, 7, 12, 1, 9, 10, 4, 8, 13, 11, 6, 3, 0, 5, 15, 14],
15 | [0, 11, 9, 3, 12, 8, 14, 13, 5, 4, 10, 15, 7, 2, 1, 6],
16 | [13, 1, 0, 12, 6, 14, 7, 11, 3, 10, 2, 5, 15, 8, 4, 9],
17 | [10, 7, 5, 1, 0, 6, 9, 13, 14, 8, 3, 15, 11, 12, 4, 2],
18 | [7, 14, 5, 13, 4, 11, 15, 10, 8, 0, 12, 2, 3, 1, 9, 6],
19 | [5, 2, 1, 12, 10, 14, 4, 15, 9, 8, 6, 0, 13, 11, 7, 3],
20 | [2, 8, 6, 5, 1, 3, 14, 10, 0, 12, 4, 13, 7, 15, 9, 11],
21 | [0, 12, 3, 11, 10, 5, 4, 14, 9, 7, 1, 2, 13, 8, 6, 15],
22 | [13, 3, 7, 11, 4, 10, 15, 0, 5, 2, 6, 12, 14, 9, 8, 1],
23 | [11, 7, 4, 6, 3, 0, 14, 5, 2, 9, 13, 15, 10, 8, 12, 1],
24 | [8, 13, 0, 11, 4, 1, 3, 9, 10, 15, 12, 5, 14, 7, 6, 2],
25 | [5, 3, 11, 10, 13, 6, 1, 15, 12, 8, 2, 4, 9, 14, 0, 7],
26 | [3, 7, 9, 6, 0, 5, 10, 14, 1, 13, 11, 4, 2, 15, 8, 12],
27 | [0, 14, 12, 15, 11, 1, 3, 10, 8, 2, 9, 6, 13, 5, 7, 4],
28 | [13, 4, 8, 6, 3, 7, 10, 0, 14, 5, 9, 1, 15, 12, 2, 11],
29 | [11, 8, 13, 5, 3, 14, 6, 9, 1, 0, 12, 15, 2, 7, 10, 4],
30 | [8, 14, 13, 10, 7, 3, 0, 6, 11, 12, 5, 1, 15, 4, 9, 2],
31 | [6, 2, 14, 10, 15, 1, 5, 8, 9, 7, 11, 13, 4, 3, 12, 0],
32 | [3, 9, 2, 4, 5, 8, 1, 7, 11, 10, 12, 0, 15, 13, 6, 14],
33 | [0, 15, 6, 14, 11, 2, 1, 3, 13, 4, 10, 12, 7, 5, 8, 9],
34 | [13, 5, 10, 7, 2, 4, 11, 0, 14, 8, 1, 9, 3, 12, 6, 15]
35 | ],
36 | "de": [
37 | [13, 7, 12, 2, 14, 11, 3, 8, 5, 9, 10, 6, 1, 15, 0, 4],
38 | [13, 8, 4, 1, 9, 15, 12, 2, 11, 7, 10, 0, 3, 5, 6, 14],
39 | [7, 3, 9, 13, 2, 6, 14, 1, 5, 0, 8, 4, 11, 12, 10, 15],
40 | [9, 5, 14, 13, 7, 10, 0, 6, 2, 15, 3, 4, 11, 1, 8, 12],
41 | [11, 1, 4, 9, 0, 2, 6, 10, 5, 8, 14, 15, 7, 3, 12, 13],
42 | [3, 0, 12, 10, 5, 13, 15, 1, 11, 2, 7, 4, 8, 9, 6, 14],
43 | [1, 13, 15, 12, 10, 6, 11, 4, 5, 3, 7, 9, 14, 2, 0, 8],
44 | [3, 8, 1, 11, 6, 4, 13, 10, 15, 7, 2, 5, 0, 14, 12, 9],
45 | [7, 6, 13, 9, 5, 14, 3, 15, 1, 0, 10, 4, 11, 8, 2, 12],
46 | [7, 14, 15, 5, 6, 11, 9, 0, 4, 10, 8, 2, 1, 12, 3, 13],
47 | [8, 5, 14, 1, 0, 4, 7, 13, 6, 10, 15, 3, 12, 9, 11, 2],
48 | [15, 0, 5, 2, 7, 3, 8, 12, 11, 1, 13, 4, 14, 6, 9, 10],
49 | [12, 8, 10, 6, 15, 5, 14, 9, 7, 3, 13, 11, 2, 1, 4, 0],
50 | [15, 6, 4, 1, 2, 5, 13, 11, 9, 14, 3, 12, 0, 10, 7, 8],
51 | [6, 4, 8, 9, 3, 12, 14, 2, 10, 0, 1, 15, 11, 13, 5, 7],
52 | [9, 13, 11, 12, 4, 2, 15, 0, 8, 14, 7, 5, 10, 3, 1, 6],
53 | [1, 4, 8, 10, 0, 7, 15, 9, 2, 3, 14, 13, 11, 6, 5, 12],
54 | [4, 0, 10, 3, 13, 7, 11, 9, 5, 6, 1, 14, 2, 12, 15, 8],
55 | [3, 4, 14, 8, 5, 13, 10, 9, 6, 2, 11, 15, 12, 7, 1, 0],
56 | [6, 10, 12, 7, 4, 1, 13, 8, 5, 3, 11, 14, 0, 2, 15, 9],
57 | [2, 11, 14, 13, 3, 9, 8, 12, 4, 1, 0, 15, 7, 10, 5, 6],
58 | [6, 15, 3, 4, 9, 7, 11, 0, 5, 14, 13, 10, 12, 8, 2, 1],
59 | [4, 14, 2, 5, 0, 1, 10, 7, 3, 13, 6, 15, 12, 8, 11, 9],
60 | [11, 13, 0, 3, 5, 14, 9, 7, 4, 1, 8, 15, 6, 12, 10, 2],
61 | [11, 3, 1, 8, 7, 12, 6, 10, 5, 9, 15, 14, 2, 13, 4, 0],
62 | [10, 13, 3, 14, 15, 12, 1, 2, 11, 7, 4, 6, 0, 5, 9, 8],
63 | [1, 15, 5, 3, 2, 6, 7, 4, 8, 11, 0, 14, 12, 13, 10, 9],
64 | [1, 10, 5, 7, 15, 14, 12, 0, 11, 8, 4, 2, 3, 6, 13, 9],
65 | [1, 2, 12, 14, 8, 0, 6, 10, 3, 9, 7, 15, 11, 4, 13, 5],
66 | [10, 5, 0, 14, 15, 13, 3, 11, 8, 2, 4, 1, 6, 7, 12, 9],
67 | [8, 1, 4, 12, 11, 7, 9, 5, 14, 6, 10, 3, 13, 2, 15, 0],
68 | [7, 10, 4, 12, 5, 1, 14, 3, 9, 11, 2, 6, 13, 0, 8, 15]
69 | ]
70 | }
71 |
--------------------------------------------------------------------------------
/txlib/8.9.73/libfekit.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XZhouQD/unidbg-fetch-qsign/e3bb92872719f5e8423d38d182d52b612717e064/txlib/8.9.73/libfekit.so
--------------------------------------------------------------------------------
/txlib/lookme.txt:
--------------------------------------------------------------------------------
1 | {
2 | "server": {
3 | "host": "0.0.0.0",
4 | "port": 8080
5 | },
6 | "share_token": true,
7 | // 共享token , 如果这个是false,且最大实例数量不是1,则一个号会拥有多个token
8 | // token不会导致封号!!!!
9 | "count": 10, // 最大实例数量,如果没共享token,则为单个号最大实例数量
10 | "key": "114514", // 请求密钥
11 | "auto_register": true,
12 | "protocol": {
13 | "package_name": "com.tencent.mobileqq",
14 | "qua": "V1_AND_SQ_8.9.71_4332_YYB_D",
15 | "version": "8.9.71",
16 | "code": "4332"
17 | },
18 | "unidbg": {
19 | "dynarmic": false, // 高并发建议打开这个,但是实例数量不要太多,会爆炸, 10实例,内存会用掉5GB
20 | "unicorn": true, // 追求稳定打开这个,内存占用小
21 | "kvm": false, // 追求稳定打开这个,内存占用小
22 | "debug": false
23 | },
24 | "black_list": [
25 | 1008611 // 黑名单uin
26 | ]
27 | }
--------------------------------------------------------------------------------