1.1.0
This commit is contained in:
parent
c0b515853d
commit
54f2015efc
168
Cargo.lock
generated
168
Cargo.lock
generated
@ -337,6 +337,12 @@ dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
@ -403,7 +409,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
"syn 2.0.107",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -461,6 +467,12 @@ dependencies = [
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.5.2"
|
||||
@ -490,6 +502,16 @@ version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.7.10"
|
||||
@ -938,6 +960,40 @@ version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "8.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb44e1917075637ee8c7bcb865cf8830e3a92b5b1189e44e3a0ab5a0d5be314b"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "8.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "382499b49db77a7c19abd2a574f85ada7e9dbe125d5d1160fa5cad7c4cf71fc9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn 2.0.107",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "8.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21fcbee55c2458836bcdbfffb6ec9ba74bbc23ca7aa6816015a3dd2c4d8fc185"
|
||||
dependencies = [
|
||||
"sha2",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.2"
|
||||
@ -957,6 +1013,15 @@ version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
@ -990,7 +1055,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
"syn 2.0.107",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1006,6 +1071,15 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
@ -1029,6 +1103,17 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
@ -1037,12 +1122,15 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "sign"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"data-encoding",
|
||||
"ntex",
|
||||
"rust-embed",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1093,9 +1181,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
version = "2.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1119,9 +1207,48 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
"syn 2.0.107",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
@ -1158,7 +1285,7 @@ checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
"syn 2.0.107",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1167,6 +1294,16 @@ version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.1+wasi-0.2.4"
|
||||
@ -1198,6 +1335,15 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
@ -1292,6 +1438,12 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.46.0"
|
||||
@ -1315,5 +1467,5 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
"syn 2.0.107",
|
||||
]
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "sign"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
edition = "2024"
|
||||
readme = "README.md"
|
||||
license = "AGPL-3.0-only"
|
||||
@ -12,7 +12,10 @@ cc = "1.2.41"
|
||||
[dependencies]
|
||||
data-encoding = "2.9.0"
|
||||
ntex = { version = "2.16.0", features = ["compio"] }
|
||||
rust-embed = "8.8.0"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
thiserror = "2.0.17"
|
||||
toml = "0.9.8"
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
|
||||
12
README.md
12
README.md
@ -6,7 +6,7 @@ This project can only be used on Linux.
|
||||
|
||||
First, go to the official website to download QQ.
|
||||
|
||||
I recommend downloading the Linux x64 3.2.19-39038 version. If you need to download other versions, you will need to modify the `src/main.rs` file.
|
||||
I recommend downloading the Linux x64 3.2.19-39038 version. If you want use another version, go to visit [Config section](#config).
|
||||
|
||||
Then unzip or install QQ.
|
||||
|
||||
@ -21,6 +21,14 @@ Place the `libsymbols.so` and `target/release/sign` files into the folder that c
|
||||
|
||||
Switch the directory to the folder containing `wrapper.node`, and then run `./sign`.
|
||||
|
||||
The server will listen on `127.0.0.1:8080`. If you want to listen on other endpoints, please go to modify the `src/main.rs` file.
|
||||
The server will listen on `127.0.0.1:8080`. If you want to listen on other endpoints, go to modify the `src/main.rs` file.
|
||||
|
||||
Enjoy!
|
||||
|
||||
## Config
|
||||
|
||||
Copy the `sign.config.toml` file to the folder where the `wrapper.node` is located.
|
||||
|
||||
Then modify the configuration file according to your needs.
|
||||
|
||||
If you use other versions of QQ, put `{version}.json` in the same directory. For specific content, refer to `src/appinfo/3.2.19-39038.json`.
|
||||
|
||||
4
sign.config.toml
Normal file
4
sign.config.toml
Normal file
@ -0,0 +1,4 @@
|
||||
preloads = ["libgnutls.so.30", "./libsymbols.so"]
|
||||
listen = "127.0.0.1:8080"
|
||||
offset = 0x5ADE220
|
||||
version = "3.2.19-39038"
|
||||
18
src/appinfo/3.2.19-39038.json
Normal file
18
src/appinfo/3.2.19-39038.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"Os": "Linux",
|
||||
"VendorOs": "linux",
|
||||
"Kernel": "Linux",
|
||||
"CurrentVersion": "3.2.19-39038",
|
||||
"MiscBitmap": 32764,
|
||||
"PtVersion": "2.0.0",
|
||||
"SsoVersion": 19,
|
||||
"PackageName": "com.tencent.qq",
|
||||
"WtLoginSdk": "nt.wtlogin.0.0.1",
|
||||
"AppId": 1600001615,
|
||||
"SubAppId": 537313942,
|
||||
"AppIdQrCode": 537313942,
|
||||
"AppClientVersion": 39038,
|
||||
"MainSigMap": 169742560,
|
||||
"SubSigMap": 0,
|
||||
"NTLoginType": 1
|
||||
}
|
||||
40
src/config.rs
Normal file
40
src/config.rs
Normal file
@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Copyright (C) 2025 Moew72 <Moew72@proton.me>
|
||||
|
||||
use std::fs;
|
||||
use std::io::Read;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
pub(crate) static CONFIG: LazyLock<Config> = LazyLock::new(|| {
|
||||
if fs::exists("sign.config.toml").unwrap() {
|
||||
let mut buf = Vec::new();
|
||||
fs::File::open("sign.config.toml")
|
||||
.unwrap()
|
||||
.read_to_end(&mut buf)
|
||||
.unwrap();
|
||||
toml::from_slice::<Config>(&buf).unwrap()
|
||||
} else {
|
||||
Config::default()
|
||||
}
|
||||
});
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(crate) struct Config {
|
||||
pub(crate) preloads: Vec<String>,
|
||||
pub(crate) listen: String,
|
||||
pub(crate) offset: usize,
|
||||
pub(crate) version: String,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
preloads: vec!["libgnutls.so.30".to_string(), "./libsymbols.so".to_string()],
|
||||
listen: "127.0.0.1:8080".to_string(),
|
||||
offset: 0x5ADE220,
|
||||
version: "3.2.19-39038".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/error.rs
Normal file
48
src/error.rs
Normal file
@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Copyright (C) 2025 Moew72 <Moew72@proton.me>
|
||||
|
||||
use ntex::web;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub(crate) enum Error {
|
||||
#[error("std io: `{0}`")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("from utf8: `{0}`")]
|
||||
FromUtf8(#[from] std::string::FromUtf8Error),
|
||||
#[error("data_encoding: `{0}`")]
|
||||
DataEncoding(#[from] data_encoding::DecodeError),
|
||||
#[error("ntex runtime JoinHandle: `{0}`")]
|
||||
NtexRuntimeJoinHandle(#[from] ntex::rt::JoinError),
|
||||
#[error("other: `{0}`")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub(crate) trait Context<T> {
|
||||
fn context(self, ctx: Error) -> Result<T>;
|
||||
}
|
||||
|
||||
impl<T> Context<T> for Option<T> {
|
||||
fn context(self, ctx: Error) -> Result<T> {
|
||||
match self {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl web::WebResponseError for Error {
|
||||
fn error_response(&self, _: &ntex::web::HttpRequest) -> ntex::http::Response {
|
||||
#[derive(Debug, Serialize)]
|
||||
struct RespBody {
|
||||
status: u16,
|
||||
message: String,
|
||||
}
|
||||
web::HttpResponse::build(self.status_code()).json(&RespBody {
|
||||
status: 400,
|
||||
message: self.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
134
src/main.rs
134
src/main.rs
@ -1,127 +1,35 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Copyright (C) 2025 Moew72 <Moew72@proton.me>
|
||||
|
||||
use std::ffi::{CString, c_char};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ptr::null;
|
||||
mod config;
|
||||
mod error;
|
||||
mod service;
|
||||
mod sign;
|
||||
|
||||
use data_encoding::HEXUPPER;
|
||||
use ntex::web;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
mod lib {
|
||||
use std::ffi::*;
|
||||
type Func = extern "C" fn(*const c_char, *const c_uchar, c_int, c_int, *mut c_uchar);
|
||||
unsafe extern "C" {
|
||||
pub(super) static mut libs: *mut *const c_char;
|
||||
pub(super) static mut offset: usize;
|
||||
pub(super) static mut sign: Func;
|
||||
pub(super) fn load_module() -> c_int;
|
||||
pub(super) fn unload_module();
|
||||
}
|
||||
}
|
||||
|
||||
fn set_libs(libs: Vec<&str>) {
|
||||
let mut libs = libs
|
||||
.iter()
|
||||
.map(|&x| ManuallyDrop::new(CString::new(x).unwrap()).as_ptr())
|
||||
.collect::<Vec<*const c_char>>();
|
||||
libs.push(null());
|
||||
unsafe {
|
||||
lib::libs = ManuallyDrop::new(libs).as_mut_ptr();
|
||||
}
|
||||
}
|
||||
|
||||
fn set_offset(offset: usize) {
|
||||
unsafe {
|
||||
lib::offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
fn load_module() {
|
||||
let ret = unsafe { lib::load_module() };
|
||||
if ret != 0 {
|
||||
panic!("load module error.");
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn unload_module() {
|
||||
unsafe { lib::unload_module() }
|
||||
}
|
||||
|
||||
fn sign(cmd: &str, src: &[u8], seq: i32) -> [Vec<u8>; 3] {
|
||||
const TOKEN_DATA_OFFSET: usize = 0x000;
|
||||
const TOKEN_LEN_OFFSET: usize = 0x0FF;
|
||||
const EXTRA_DATA_OFFSET: usize = 0x100;
|
||||
const EXTRA_LEN_OFFSET: usize = 0x1FF;
|
||||
const SIGN_DATA_OFFSET: usize = 0x200;
|
||||
const SIGN_LEN_OFFSET: usize = 0x2FF;
|
||||
|
||||
let c_cmd = CString::new(cmd).unwrap();
|
||||
let mut buf = [0u8; 0x300];
|
||||
let _ = unsafe {
|
||||
lib::sign(
|
||||
c_cmd.as_ptr(),
|
||||
src.as_ptr(),
|
||||
src.len() as i32,
|
||||
seq,
|
||||
buf.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
let token_len = buf[TOKEN_LEN_OFFSET];
|
||||
let token = &buf[TOKEN_DATA_OFFSET..TOKEN_DATA_OFFSET + token_len as usize];
|
||||
let extra_len = buf[EXTRA_LEN_OFFSET];
|
||||
let extra = &buf[EXTRA_DATA_OFFSET..EXTRA_DATA_OFFSET + extra_len as usize];
|
||||
let sign_len = buf[SIGN_LEN_OFFSET];
|
||||
let sign = &buf[SIGN_DATA_OFFSET..SIGN_DATA_OFFSET + sign_len as usize];
|
||||
|
||||
[Vec::from(token), Vec::from(extra), Vec::from(sign)]
|
||||
}
|
||||
use crate::{
|
||||
config::CONFIG,
|
||||
sign::{load_module, set_libs, set_offset, unload_module},
|
||||
};
|
||||
|
||||
#[ntex::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
set_libs(vec!["libgnutls.so.30", "./libsymbols.so"]);
|
||||
set_offset(0x5ADE220);
|
||||
set_libs(CONFIG.preloads.clone());
|
||||
set_offset(CONFIG.offset);
|
||||
load_module();
|
||||
web::HttpServer::new(|| web::App::new().service(sign_service))
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
|
||||
#[web::post("/")]
|
||||
async fn sign_service(params: web::types::Json<Params>) -> impl web::Responder {
|
||||
let ret = HEXUPPER.decode(params.src.to_uppercase().as_bytes());
|
||||
let src = match ret {
|
||||
Ok(v) => v,
|
||||
Err(err) => return web::HttpResponse::InternalServerError().body(err.to_string()),
|
||||
};
|
||||
let [token, extra, sign] = sign(¶ms.cmd, &src, params.seq);
|
||||
let token = HEXUPPER.encode(&token);
|
||||
let extra = HEXUPPER.encode(&extra);
|
||||
let sign = HEXUPPER.encode(&sign);
|
||||
let value = Value { token, extra, sign };
|
||||
let body = RespBody { value };
|
||||
web::HttpResponse::Ok().json(&body)
|
||||
}
|
||||
web::HttpServer::new(|| {
|
||||
web::App::new()
|
||||
.service(service::sign_get)
|
||||
.service(service::sign_post)
|
||||
.service(service::appinfo)
|
||||
})
|
||||
.bind(CONFIG.listen.clone())?
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Params {
|
||||
cmd: String,
|
||||
src: String,
|
||||
seq: i32,
|
||||
}
|
||||
unload_module();
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct RespBody {
|
||||
value: Value,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Value {
|
||||
token: String,
|
||||
extra: String,
|
||||
sign: String,
|
||||
Ok(())
|
||||
}
|
||||
|
||||
87
src/service.rs
Normal file
87
src/service.rs
Normal file
@ -0,0 +1,87 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Copyright (C) 2025 Moew72 <Moew72@proton.me>
|
||||
|
||||
use std::fs;
|
||||
use std::io::Read;
|
||||
|
||||
use data_encoding::HEXUPPER;
|
||||
use ntex::http::Response;
|
||||
use ntex::http::StatusCode;
|
||||
use ntex::http::header::CONTENT_TYPE;
|
||||
use ntex::rt::spawn;
|
||||
use ntex::web;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::config::CONFIG;
|
||||
use crate::error::{Context, Error, Result};
|
||||
|
||||
#[derive(rust_embed::Embed)]
|
||||
#[folder = "src/appinfo"]
|
||||
struct Asset;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Params {
|
||||
cmd: String,
|
||||
src: String,
|
||||
seq: i32,
|
||||
}
|
||||
|
||||
#[web::get("/")]
|
||||
pub(crate) async fn sign_get(params: web::types::Query<Params>) -> Result<Response> {
|
||||
sign_common(params.0).await
|
||||
}
|
||||
|
||||
#[web::post("/")]
|
||||
pub(crate) async fn sign_post(params: web::types::Json<Params>) -> Result<Response> {
|
||||
sign_common(params.0).await
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn sign_common(params: Params) -> Result<Response> {
|
||||
#[derive(Serialize)]
|
||||
struct RespBody {
|
||||
platform: String,
|
||||
version: String,
|
||||
value: Value,
|
||||
}
|
||||
#[derive(Serialize)]
|
||||
struct Value {
|
||||
token: String,
|
||||
extra: String,
|
||||
sign: String,
|
||||
}
|
||||
let src = HEXUPPER.decode(params.src.to_uppercase().as_bytes())?;
|
||||
let [token, extra, sign] =
|
||||
spawn(async move { crate::sign::sign(¶ms.cmd, &src, params.seq) }).await?;
|
||||
let token = HEXUPPER.encode(&token);
|
||||
let extra = HEXUPPER.encode(&extra);
|
||||
let sign = HEXUPPER.encode(&sign);
|
||||
let value = Value { token, extra, sign };
|
||||
let body = RespBody {
|
||||
platform: "Linux".to_string(),
|
||||
value,
|
||||
version: CONFIG.version.clone(),
|
||||
};
|
||||
let resp = web::HttpResponse::Ok().json(&body);
|
||||
Ok(resp)
|
||||
}
|
||||
|
||||
#[web::get("/appinfo")]
|
||||
pub(crate) async fn appinfo() -> Result<Response> {
|
||||
let path = &format!("{}.json", CONFIG.version.clone());
|
||||
let body = if fs::exists(path)? {
|
||||
let mut buf = String::new();
|
||||
fs::File::open(path)?.read_to_string(&mut buf)?;
|
||||
buf
|
||||
} else {
|
||||
let data = Asset::get(path)
|
||||
.context(Error::Other(format!("appinfo {} not found.", path)))?
|
||||
.data
|
||||
.to_vec();
|
||||
String::from_utf8(data)?
|
||||
};
|
||||
let resp = web::HttpResponse::build(StatusCode::OK)
|
||||
.set_header(CONTENT_TYPE, "application/json")
|
||||
.body(body);
|
||||
Ok(resp)
|
||||
}
|
||||
77
src/sign.rs
Normal file
77
src/sign.rs
Normal file
@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Copyright (C) 2025 Moew72 <Moew72@proton.me>
|
||||
|
||||
use std::ffi::{CString, c_char};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ptr::null;
|
||||
|
||||
mod lib {
|
||||
use std::ffi::*;
|
||||
type Func = extern "C" fn(*const c_char, *const c_uchar, c_int, c_int, *mut c_uchar);
|
||||
unsafe extern "C" {
|
||||
pub(super) static mut libs: *mut *const c_char;
|
||||
pub(super) static mut offset: usize;
|
||||
pub(super) static mut sign: Func;
|
||||
pub(super) fn load_module() -> c_int;
|
||||
pub(super) fn unload_module();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_libs(libs: Vec<String>) {
|
||||
let mut libs = libs
|
||||
.iter()
|
||||
.map(|x| ManuallyDrop::new(CString::new(x.as_str()).unwrap()).as_ptr())
|
||||
.collect::<Vec<*const c_char>>();
|
||||
libs.push(null());
|
||||
unsafe {
|
||||
lib::libs = ManuallyDrop::new(libs).as_mut_ptr();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_offset(offset: usize) {
|
||||
unsafe {
|
||||
lib::offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn load_module() {
|
||||
let ret = unsafe { lib::load_module() };
|
||||
if ret != 0 {
|
||||
panic!("load module error.");
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn unload_module() {
|
||||
unsafe { lib::unload_module() }
|
||||
}
|
||||
|
||||
pub(crate) fn sign(cmd: &str, src: &[u8], seq: i32) -> [Vec<u8>; 3] {
|
||||
const TOKEN_DATA_OFFSET: usize = 0x000;
|
||||
const TOKEN_LEN_OFFSET: usize = 0x0FF;
|
||||
const EXTRA_DATA_OFFSET: usize = 0x100;
|
||||
const EXTRA_LEN_OFFSET: usize = 0x1FF;
|
||||
const SIGN_DATA_OFFSET: usize = 0x200;
|
||||
const SIGN_LEN_OFFSET: usize = 0x2FF;
|
||||
|
||||
let c_cmd = CString::new(cmd).unwrap();
|
||||
let mut buf = [0u8; 0x300];
|
||||
let _ = unsafe {
|
||||
lib::sign(
|
||||
c_cmd.as_ptr(),
|
||||
src.as_ptr(),
|
||||
src.len() as i32,
|
||||
seq,
|
||||
buf.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
let token_len = buf[TOKEN_LEN_OFFSET];
|
||||
let token = &buf[TOKEN_DATA_OFFSET..TOKEN_DATA_OFFSET + token_len as usize];
|
||||
let extra_len = buf[EXTRA_LEN_OFFSET];
|
||||
let extra = &buf[EXTRA_DATA_OFFSET..EXTRA_DATA_OFFSET + extra_len as usize];
|
||||
let sign_len = buf[SIGN_LEN_OFFSET];
|
||||
let sign = &buf[SIGN_DATA_OFFSET..SIGN_DATA_OFFSET + sign_len as usize];
|
||||
|
||||
[Vec::from(token), Vec::from(extra), Vec::from(sign)]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user