├── .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 | 147 | NS23gm77vjYiyYK554L4aY0SYG5Xgjje 148 | 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 | } --------------------------------------------------------------------------------