From 17f1ad2c182fb0d687917f91210f0393cbaf2005 Mon Sep 17 00:00:00 2001 From: Mufanc Date: Sun, 10 Dec 2023 13:59:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A4=84=E7=90=86=E9=9D=9E=E5=AE=98?= =?UTF-8?q?=E6=96=B9=20URL=20=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + Makefile | 8 ++ PKGBUILD.proto | 46 +++++--- build.sh | 3 +- daemon/.gitignore | 1 + daemon/Cargo.lock | 62 ++++++++++ daemon/Cargo.toml | 8 ++ daemon/rust-toolchain.toml | 2 + daemon/src/main.rs | 128 +++++++++++++++++++++ inject/.gitignore | 1 + inject/Cargo.lock | 226 +++++++++++++++++++++++++++++++++++++ inject/Cargo.toml | 16 +++ inject/src/dlopt.rs | 38 +++++++ inject/src/lib.rs | 115 +++++++++++++++++++ launcher.sh | 27 +++-- 15 files changed, 655 insertions(+), 27 deletions(-) create mode 100644 Makefile create mode 100644 daemon/.gitignore create mode 100644 daemon/Cargo.lock create mode 100644 daemon/Cargo.toml create mode 100644 daemon/rust-toolchain.toml create mode 100644 daemon/src/main.rs create mode 100644 inject/.gitignore create mode 100644 inject/Cargo.lock create mode 100644 inject/Cargo.toml create mode 100644 inject/src/dlopt.rs create mode 100644 inject/src/lib.rs diff --git a/.gitignore b/.gitignore index 0cf6703..80aff61 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/.idea /*.deb /*.tar.zst /PKGBUILD diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e3dd2d1 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +.PHONY : all clean +.OHESHELL : + +all: + ./build.sh + +clean: + rm -rf PKGBUILD src pkg *.deb *.tar.zst diff --git a/PKGBUILD.proto b/PKGBUILD.proto index 49bcc53..59882d1 100644 --- a/PKGBUILD.proto +++ b/PKGBUILD.proto @@ -8,31 +8,41 @@ arch=('x86_64') url='https://github.com/Mufanc/QWrapper' license=('custom') depends=( - # 默认依赖 - 'gtk3' 'libnotify' 'nss' 'libxss' 'libxtst' 'xdg-utils' 'at-spi2-core' 'util-linux-libs' 'libsecret' - # 移除问题库后补的依赖 - 'libvips' 'libunwind' 'libssh2' - ) -makedepends=() + # 默认依赖 + 'gtk3' 'libnotify' 'nss' 'libxss' 'libxtst' 'xdg-utils' 'at-spi2-core' 'util-linux-libs' 'libsecret' + # 移除问题库后补的依赖 + 'libvips' 'libunwind' 'libssh2' + ) +makedepends=('rust') optdepends=('libappindicator-gtk3') conflicts=('linuxqq') source=('$SOURCE' 'launcher.sh') sha256sums=() +build() { + cd "$PKGROOT/daemon" + cargo build --release + + cd "$PKGROOT/inject" + cargo build --release +} + package() { - # 解包 - tar -x --xz -f data.tar.xz --directory="$pkgdir" + # 解包 + tar -x --xz -f data.tar.xz --directory="$pkgdir" - # 替换入口 - mv "$pkgdir/opt/QQ/qq" "$pkgdir/opt/QQ/main" - cp "$srcdir/launcher.sh" "$pkgdir/opt/QQ/qq" + # 替换入口 + mv "$pkgdir/opt/QQ/qq" "$pkgdir/opt/QQ/main" + cp "$srcdir/launcher.sh" "$pkgdir/opt/QQ/launcher.sh" + cp "$PKGROOT/daemon/target/release/daemon" "$pkgdir/opt/QQ/qq" + cp "$PKGROOT/inject/target/release/libinject.so" "$pkgdir/opt/QQ/libinject.so" - # 处理 crash - res="$pkgdir/opt/QQ/resources/app" - rm "$res"/libssh* - rm "$res"/libunwind* - rm "$res"/sharp-lib/* + # 处理 crash + res="$pkgdir/opt/QQ/resources/app" + rm "$res"/libssh* + rm "$res"/libunwind* + rm "$res"/sharp-lib/* - # 替换 desktop 名称 - sed -i -E "s#(Name=.*)#Name=QQ (wrap)#" "$pkgdir/usr/share/applications/qq.desktop" + # 替换 desktop 名称 + sed -i -E "s#(Name=.*)#Name=QQ (wrap)#" "$pkgdir/usr/share/applications/qq.desktop" } diff --git a/build.sh b/build.sh index d0013ef..ce5a936 100755 --- a/build.sh +++ b/build.sh @@ -1,5 +1,6 @@ export SOURCE=$(curl -s https://im.qq.com/rainbow/linuxQQDownload/ | grep -Eo '"deb":"[^"]+"' | grep -Eo 'https://.*_amd64\.deb') -export PKGVER=$(echo $SOURCE | awk -F "linuxqq_|-" '{print $2}') +export PKGVER=$(echo "$SOURCE" | awk -F "linuxqq_|-" '{print $2}') +export PKGROOT=$(realpath "$(dirname "$0")") envsubst "$(env | grep -Po '^[A-Z_]+(?==)' | sed 's/^/$/g')" < PKGBUILD.proto > PKGBUILD updpkgsums makepkg -f diff --git a/daemon/.gitignore b/daemon/.gitignore new file mode 100644 index 0000000..c41cc9e --- /dev/null +++ b/daemon/.gitignore @@ -0,0 +1 @@ +/target \ No newline at end of file diff --git a/daemon/Cargo.lock b/daemon/Cargo.lock new file mode 100644 index 0000000..8a2cba8 --- /dev/null +++ b/daemon/Cargo.lock @@ -0,0 +1,62 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "daemon" +version = "0.1.0" +dependencies = [ + "anyhow", + "nix", +] + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset", +] diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml new file mode 100644 index 0000000..c471ffd --- /dev/null +++ b/daemon/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "daemon" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.75" +nix = { version = "0.27.1", features = ["socket", "process"] } diff --git a/daemon/rust-toolchain.toml b/daemon/rust-toolchain.toml new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/daemon/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/daemon/src/main.rs b/daemon/src/main.rs new file mode 100644 index 0000000..0fe9c69 --- /dev/null +++ b/daemon/src/main.rs @@ -0,0 +1,128 @@ +#![feature(peer_credentials_unix_socket)] + +use std::fs; +use std::fs::Metadata; +use std::io::Read; +use std::os::fd::{AsRawFd, OwnedFd}; +use std::os::unix::fs::MetadataExt; +use std::os::unix::net::UnixListener; +use std::process::{Command, exit}; +use std::thread::sleep; +use std::time::Duration; + +use anyhow::{bail, Result}; +use nix::{libc, unistd}; +use nix::errno::Errno; +use nix::sys::prctl; +use nix::sys::signal::Signal; +use nix::sys::socket; +use nix::sys::socket::{AddressFamily, MsgFlags, SockFlag, SockType, UnixAddr}; + +const BIND_ADDRESS: &str = "qwrapper-daemon"; + + +fn bind_server(fd: &OwnedFd) -> Result<()> { + let addr = &UnixAddr::new_abstract(BIND_ADDRESS.as_bytes())?; + + if let Err(err) = socket::bind(fd.as_raw_fd(), addr) { + if err != Errno::EADDRINUSE { + bail!(err); + } + + eprintln!("address already in use, stop old daemon and retry..."); + + let client = socket::socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None)?; + let client_raw = client.as_raw_fd(); + + socket::connect(client_raw, addr)?; + socket::send(client_raw, "@exit".as_bytes(), MsgFlags::empty())?; + + drop(client); // close fd + + sleep(Duration::from_secs(1)); + + return bind_server(fd); + } + + socket::listen(&fd, 16)?; + + Ok(()) +} + + +fn verify_ns(proc: Option) -> bool { + if proc.is_none() { + return false + } + + fn get_ns(id: libc::pid_t) -> Option { + fs::metadata(format!("/proc/{id}/ns/pid")).ok() + } + + let remote = get_ns(proc.unwrap()); + let local = get_ns(unistd::getpid().as_raw()); + + match (remote, local) { + (Some(remote), Some(local)) => { + remote.dev() == local.dev() && remote.ino() == local.ino() + } + _ => false + } +} + + +fn super_command(cmd: &str) { + match cmd { + "@exit" => exit(0), + "@example" => println!("example"), + _ => () + } +} + + +fn main() -> Result<()> { + let fd = socket::socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None)?; + + bind_server(&fd)?; + + let listener = UnixListener::from(fd); + println!("daemon is listening on @{BIND_ADDRESS}"); + + prctl::set_pdeathsig(Signal::SIGKILL)?; + + let mut command = Command::new("/opt/QQ/launcher.sh"); + let mut child = command.spawn()?; + + for stream in listener.incoming() { + match stream { + Ok(mut client) => { + let remote_pid = client.peer_cred() + .ok() + .and_then(|c| c.pid); + + let mut data = String::new(); + match client.read_to_string(&mut data) { + Ok(_) => { + #[allow(clippy::collapsible_else_if)] + if data.starts_with('@') && verify_ns(remote_pid) { + super_command(&data); + } else { + if let Err(err) = Command::new("xdg-open").arg(data).status() { + eprintln!("{err}"); + } + } + } + Err(err) => eprintln!("error while receiving url: {err}") + } + } + Err(err) => { + eprintln!("error while accepting connection: {err}"); + break + } + } + } + + child.kill()?; + + Ok(()) +} diff --git a/inject/.gitignore b/inject/.gitignore new file mode 100644 index 0000000..c41cc9e --- /dev/null +++ b/inject/.gitignore @@ -0,0 +1 @@ +/target \ No newline at end of file diff --git a/inject/Cargo.lock b/inject/Cargo.lock new file mode 100644 index 0000000..2357eaa --- /dev/null +++ b/inject/Cargo.lock @@ -0,0 +1,226 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ctor" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e366bff8cd32dd8754b0991fb66b279dc48f598c3a18914852a6673deef583" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "goblin" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27c1b4369c2cd341b5de549380158b105a04c331be5db9110eef7b6d2742134" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "inject" +version = "0.1.0" +dependencies = [ + "anyhow", + "ctor", + "goblin", + "nix", + "once_cell", + "url", +] + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "scroll" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] diff --git a/inject/Cargo.toml b/inject/Cargo.toml new file mode 100644 index 0000000..537e1c8 --- /dev/null +++ b/inject/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "inject" +version = "0.1.0" +edition = "2021" + +[lib] +name = "inject" +crate-type = ["cdylib"] + +[dependencies] +anyhow = "1.0.75" +ctor = "0.2.5" +goblin = "0.7.1" +nix = { version = "0.27.1", features = ["socket"] } +once_cell = "1.19.0" +url = "2.5.0" diff --git a/inject/src/dlopt.rs b/inject/src/dlopt.rs new file mode 100644 index 0000000..10a0a31 --- /dev/null +++ b/inject/src/dlopt.rs @@ -0,0 +1,38 @@ +use std::ffi::{c_void, CStr, CString}; + +use anyhow::Result; +use nix::libc; +use nix::libc::c_int; + +#[derive(Debug)] +pub struct Handle(String, *mut c_void); + +pub fn dlopen(path: &str, flag: c_int) -> Result { + let filename = CString::new(path)?; + + unsafe { + let handle = libc::dlopen(filename.as_ptr(), flag); + + if handle.is_null() { + let error = CStr::from_ptr(libc::dlerror()).to_str()?; + anyhow::bail!("dlopen {path} failed: {error}"); + } + + Ok(Handle(path.to_string(), handle)) + } +} + +pub fn dlsym(handle: Handle, symbol: &str) -> Result<*mut c_void> { + let name = CString::new(symbol)?; + + unsafe { + let addr = libc::dlsym(handle.1, name.as_ptr()); + + if addr.is_null() { + let error = CStr::from_ptr(libc::dlerror()).to_str()?; + anyhow::bail!("failed to dlsym for `{symbol}`: {error}"); + } + + Ok(addr) + } +} diff --git a/inject/src/lib.rs b/inject/src/lib.rs new file mode 100644 index 0000000..1fae40e --- /dev/null +++ b/inject/src/lib.rs @@ -0,0 +1,115 @@ +use std::{fs, mem}; +use std::collections::HashMap; +use std::ffi::{c_char, CStr}; +use std::ops::Deref; +use std::os::fd::AsRawFd; +use std::process::exit; + +use anyhow::Result; +use ctor::ctor; +use goblin::elf::dynamic::DT_NEEDED; +use goblin::elf::Elf; +use nix::libc::{c_int, RTLD_LAZY}; +use nix::sys::socket; +use nix::sys::socket::{AddressFamily, MsgFlags, SockFlag, SockType, UnixAddr}; +use once_cell::sync::Lazy; +use url::Url; + +mod dlopt; + +static SERVER_ADDRESS: Lazy = Lazy::new(|| { + UnixAddr::new_abstract("qwrapper-daemon".as_bytes()).unwrap() +}); + + +fn find_libc() -> Result { + const QQNT_ELF: &str = "/opt/QQ/main"; + + let data = fs::read(QQNT_ELF)?; + let elf = Elf::parse(&data)?; + + if let Some(dynamic) = elf.dynamic { + for item in &dynamic.dyns { + if item.d_tag != DT_NEEDED { + continue; + } + + if let Some(name) = elf.dynstrtab.get_at(item.d_val as usize) { + if !name.starts_with("libc.so") { + continue + } + + return Ok(name.to_string()) + } + } + } + + anyhow::bail!("failed to find libc!") +} + + +fn open_url(url: &str) -> Result<()> { + let client = socket::socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None)?; + let client_raw = client.as_raw_fd(); + + socket::connect(client_raw, SERVER_ADDRESS.deref())?; + socket::send(client_raw, url.as_bytes(), MsgFlags::empty())?; + + Ok(()) +} + + +fn handle_open(argv: *const *const c_char) { + unsafe { + CStr::from_ptr(*argv.add(1)).to_str() + .ok() + .and_then(|url| Url::parse(url).ok()) + .and_then(|url| { + match url.domain() { + Some("c.pc.qq.com") => { + let queries: HashMap<_, _> = url.query_pairs().into_owned().collect(); + queries.get("pfurl").or(queries.get("url")).cloned() + } + _ => Some(url.to_string()) + } + }) + .and_then(|url| open_url(&url).ok()); + } + + exit(0); +} + + +type ExecvpFn = fn(*const c_char, *const *const c_char) -> i32; + +#[no_mangle] +#[allow(non_upper_case_globals)] +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn execvp(file: *const c_char, argv: *const *const c_char) -> c_int { + static real_execvp: Lazy = Lazy::new(|| { + let libc = find_libc().unwrap(); + + println!("found libc: {libc}"); + + let handle = dlopt::dlopen(&libc, RTLD_LAZY).unwrap(); + let execvp = dlopt::dlsym(handle, "execvp").unwrap(); + + println!("found execvp: {execvp:?}"); + + unsafe { mem::transmute(execvp) } + }); + + unsafe { + if CStr::from_ptr(file).to_str() == Ok("xdg-open") { + handle_open(argv); + } + } + + real_execvp(file, argv) +} + + +#[ctor] +fn main() { + println!("injected!"); +} diff --git a/launcher.sh b/launcher.sh index 4f3e4f8..89d1407 100755 --- a/launcher.sh +++ b/launcher.sh @@ -1,9 +1,12 @@ #!/usr/bin/env sh +BASE="/opt/QQ" LITELOADER="$HOME/.config/QQ/LiteLoaderQQNT" -mkdir -p $HOME/.config/QQ -killall -9 /opt/QQ/main 2>/dev/null +mkdir -p "$HOME/.config/QQ" + +killall /opt/QQ/qq 2>/dev/null +killall /opt/QQ/main 2>/dev/null args="" args="$args --unshare-all --share-net" # 分离命名空间(主要是 /proc 隔离) @@ -14,22 +17,30 @@ args="$args --bind $HOME/Downloads $HOME/Downloads" # args="$args --tmpfs $HOME/.config --bind $HOME/.config/QQ $HOME/.config/QQ" # 隔离配置目录 args="$args --tmpfs $HOME/.config/QQ/crash_files" # 解决 libvips 导致的 crash +if [ -n "$DEBUG" ]; then + inject="$(realpath "$(dirname "$0")")/inject/target/debug/libinject.so" + args="$args --setenv LD_PRELOAD $inject" +else + args="$args --setenv LD_PRELOAD $BASE/libinject.so" +fi + if [ -d "$LITELOADER" ]; then # 支持 LiteLoaderQQNT profile="$HOME/.config/LiteLoaderQQNT" # 修改配置目录 mkdir -p "$profile" args="$args --bind $profile $profile" args="$args --setenv LITELOADERQQNT_PROFILE $profile" - entry=/opt/QQ/resources/app/package.json + entry="$BASE/resources/app/package.json" fake_entry=$(mktemp) - sed 's#./app_launcher/index.js#../LiteLoader#' "$entry" > $fake_entry # 替换入口点 + sed 's#./app_launcher/index.js#../LiteLoader#' "$entry" > "$fake_entry" # 替换入口点 - args="$args --tmpfs /opt/QQ/resources" # 需要挂 tmpfs 否则下步无权创建文件夹 - args="$args --bind /opt/QQ/resources/app /opt/QQ/resources/app" # 挂回 app 目录 - args="$args --bind $LITELOADER /opt/QQ/resources/LiteLoader" # 挂载插件目录 + args="$args --tmpfs $BASE/resources" # 需要挂 tmpfs 否则下步无权创建文件夹 + args="$args --bind $BASE/resources/app $BASE/resources/app" # 挂回 app 目录 + args="$args --bind $LITELOADER $BASE/resources/LiteLoader" # 挂载插件目录 args="$args --bind $fake_entry $entry" # 挂载假入口 fi -args="$args --chdir $HOME /opt/QQ/main" # 启动主程序 +args="$args --chdir $HOME $BASE/main" # 启动主程序 +# shellcheck disable=SC2086 exec bwrap $args