Skip to content

Commit 3a6028d

Browse files
committed
feat(init): rewrite to c
1 parent 4736b21 commit 3a6028d

36 files changed

+747
-1269
lines changed

.github/workflows/android.yml

Lines changed: 54 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,84 +3,95 @@ name: Android CI/CD
33
on:
44
push:
55
branches: [ "main" ]
6-
paths:
7-
- "**.gradle"
8-
- "**.rs"
9-
- "**/Cargo.toml"
10-
- ".github/workflows/**"
6+
paths-ignore:
7+
- '**.md'
8+
- 'docs/**'
119
pull_request:
1210
branches: [ "main" ]
13-
paths:
14-
- "**.gradle"
15-
- "**.rs"
16-
- "**/Cargo.toml"
17-
- ".github/workflows/**"
11+
paths-ignore:
12+
- '**.md'
13+
- 'docs/**'
1814
release:
19-
types: [ created ]
15+
types: [ published ]
2016

2117
permissions:
2218
contents: write
23-
packages: write
2419

2520
jobs:
2621
build:
27-
22+
name: Build Module
2823
runs-on: ubuntu-latest
29-
24+
3025
steps:
31-
- uses: actions/checkout@v4
26+
- name: Checkout Repository
27+
uses: actions/checkout@v4
28+
3229
- name: Set up JDK 21
3330
uses: actions/setup-java@v4
3431
with:
3532
java-version: '21'
3633
distribution: 'temurin'
3734
cache: 'gradle'
38-
- name: Setup | Rust
39-
uses: dtolnay/rust-toolchain@stable
40-
with:
41-
targets: 'armv7-linux-androideabi,i686-linux-android,aarch64-linux-android,x86_64-linux-android'
42-
- name: Setup Android SDK
43-
uses: android-actions/setup-android@v3
44-
with:
45-
packages: platform-tools build-tools;36.1.0 cmake;3.22.1 ndk;29.0.14206865
46-
env:
47-
SKIP_JDK_VERSION_CHECK: true
35+
4836
- name: Grant execute permission for gradlew
4937
run: chmod +x gradlew
38+
5039
- name: Build with Gradle
51-
run: ./gradlew :module:zipRelease -Prelease
52-
- name: Upload Artifacts
40+
run: ./gradlew :module:zipRelease
41+
42+
- name: Upload Artifact
5343
uses: actions/upload-artifact@v4
5444
with:
55-
name: zygiskloader-module-zip
56-
path: out/*.zip
57-
58-
deploy:
45+
name: ZygiskLoader-Release
46+
path: module/release/*.zip
47+
if-no-files-found: error
48+
49+
deploy-release:
50+
name: Deploy to GitHub Release
5951
needs: build
52+
if: github.event_name == 'release'
6053
runs-on: ubuntu-latest
61-
if: github.event_name == 'release' || github.ref == 'refs/heads/main'
6254
permissions:
6355
contents: write
56+
6457
steps:
65-
- name: Download Artifacts
58+
- name: Download Artifact
6659
uses: actions/download-artifact@v4
6760
with:
68-
name: zygiskloader-module-zip
69-
path: out/
70-
- name: Deploy to GitHub Releases
61+
name: ZygiskLoader-Release
62+
path: out
63+
64+
- name: Upload to Release
7165
uses: softprops/action-gh-release@v2
72-
if: github.event_name == 'release'
7366
with:
7467
files: out/*.zip
75-
draft: false
68+
fail_on_unmatched_files: true
7669
env:
7770
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
78-
- name: Deploy to GitHub Releases (Draft)
71+
72+
deploy-nightly:
73+
name: Update Nightly Release
74+
needs: build
75+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
76+
runs-on: ubuntu-latest
77+
permissions:
78+
contents: write
79+
80+
steps:
81+
- name: Download Artifact
82+
uses: actions/download-artifact@v4
83+
with:
84+
name: ZygiskLoader-Release
85+
path: out
86+
87+
- name: Update Nightly Tag
7988
uses: softprops/action-gh-release@v2
80-
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
8189
with:
90+
tag_name: nightly
91+
name: Nightly Build
92+
body: "Latest build from main branch."
8293
files: out/*.zip
83-
draft: true
84-
overwrite_files: true
94+
prerelease: true
95+
overwrite: true
8596
env:
86-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
97+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@
88
.externalNativeBuild
99
.cxx
1010
local.properties
11-
out
11+
module/build
12+
module/release

LICENSE

Lines changed: 0 additions & 21 deletions
This file was deleted.

README.md

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
# ⚡ Zygisk-Loader
22

3-
![Language](https://img.shields.io/badge/Language-Rust-orange)
4-
![API](https://img.shields.io/badge/Zygisk-API%20v5-blue)
3+
![Language](https://img.shields.io/badge/Language-C-blue)
4+
![API](https://img.shields.io/badge/Zygisk-API%20v5-green)
55
[![Channel](https://img.shields.io/badge/Follow-Telegram-blue.svg?logo=telegram)](https://t.me/UnixPhoriaD)
66

7-
**Zygisk-Loader** is a stealthy, high-performance Zygisk module written in **Rust**. It acts as a universal bridge that dynamically injects external shared libraries (`.so`) into specific Android application processes.
7+
**Zygisk-Loader** is a stealthy, ultra-lightweight Zygisk module written in **Pure C**. It acts as a universal bridge that dynamically injects external shared libraries (`.so`) into specific Android application processes.
88

9-
Unlike traditional modules that require rebuilding and rebooting, **Zygisk-Loader** enables a **"Hot-Swap" workflow**. You can recompile your instrumentation library, push it to the device, and simply restart the target app to apply changes instantly.
9+
Rewritten from Rust to C, this module now boasts an incredibly small footprint (**< 20KB**) with zero runtime dependencies. Unlike traditional modules that require rebuilding and rebooting, **Zygisk-Loader** enables a **"Hot-Swap" workflow**. You can recompile your instrumentation library, push it to the device, and simply restart the target app to apply changes instantly.
1010

1111
## Key Features
1212

1313
* **Hot-Swap Capable**: Update your payload (`.so`) and deploy instantly by just restarting the target app. No device reboot required.
14+
* **Ultra-Lightweight**: Built with **Pure C** and standard Android NDK libraries. The module binary is microscopic (<20KB), ensuring minimal memory usage and maximum performance.
1415
* **Robust Injection**: Uses a **RAM-Buffering Strategy**. The payload is read into memory with Root privileges, then written to the app's cache in the post-specialize phase. This ensures compatibility with strict SELinux policies and isolated processes.
1516
* **Stealthy (Self-Deleting)**: The payload is written to disk, loaded, and **immediately unlinked**. The file vanishes from the filesystem instantly, leaving minimal traces for file scanners.
1617
* **Zygisk API v5**: Utilizes the latest Zygisk API for maximum compatibility with Magisk, KernelSU, and APatch.
1718
* **Config-Driven**: Simple text-based configuration. No hardcoded package names.
18-
* **Rust-Powered**: Built with safety and performance in mind using the `jni` and `libc` crates.
1919

2020
## Architecture
2121

@@ -82,10 +82,10 @@ echo "com.target.application" > /data/adb/modules/zygisk-loader/config/target
8282
```
8383

8484
**B. Deploy Payload:**
85-
Copy your compiled Rust/C++ library to the config folder:
85+
Copy your compiled C/C++/Rust library to the config folder:
8686
```bash
8787
# Copy your hook library
88-
cp libunpin.so /data/adb/modules/zygisk-loader/config/payload.so
88+
cp libmyhook.so /data/adb/modules/zygisk-loader/config/payload.so
8989

9090
# Set permissions (Important for Zygote to read it)
9191
chmod 644 /data/adb/modules/zygisk-loader/config/payload.so
@@ -99,7 +99,28 @@ am force-stop com.target.application
9999

100100
## Developing a Payload
101101

102-
Your payload does not need to know about Zygisk. It acts as a standard shared library. In Rust, we recommend using the `ctor` crate for automatic initialization.
102+
Your payload does not need to know about Zygisk. It acts as a standard shared library. You can write your payload in **C, C++, or Rust**.
103+
104+
### Option A: Using C/C++ (Constructor Attribute)
105+
106+
```c
107+
#include <android/log.h>
108+
#include <unistd.h>
109+
110+
#define LOG_TAG "GhostPayload"
111+
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
112+
113+
// This function runs automatically when dlopen() is called
114+
__attribute__((constructor))
115+
void init() {
116+
LOGI("Hello from inside the target application!");
117+
LOGI("I have been loaded and my file on disk is likely already gone.");
118+
119+
// Your hooking logic (e.g., Dobby, And64InlineHook) goes here
120+
}
121+
```
122+
123+
### Option B: Using Rust (ctor crate)
103124
104125
`Cargo.toml`:
105126
```toml
@@ -126,19 +147,18 @@ fn init() {
126147

127148
// logic hooking start here
128149
log::info!("Hello from inside the target application!");
129-
log::info!("I have been loaded and my file on disk is already gone.");
130150
}
131151
```
132152

133153
## Technical Constraints
134154

135155
* **SELinux Compatibility**: This module uses disk injection (Write-Load-Unlink) instead of `memfd` to ensure maximum compatibility across all Android versions and SELinux contexts. `memfd` often fails on `untrusted_app` domains due to `execmem` restrictions.
136-
* **Isolated Processes**: The loader automatically handles isolated processes (e.g., `:remote` services) by resolving the correct data directory path.
156+
* **Isolated Processes**: The loader automatically handles isolated processes (e.g., `:remote` services) by intelligently resolving the correct data directory path.
137157

138158
## Disclaimer
139159

140160
This tool is for **educational purposes and security research only**. The author is not responsible for any misuse of this software.
141161

142162
## License
143163

144-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
164+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

build.gradle

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,66 @@
1-
// Top-level build file where you can add configuration options common to all sub-projects/modules.
2-
buildscript {
3-
repositories {
4-
google()
5-
mavenCentral()
6-
maven{
7-
url "https://plugins.gradle.org/m2/"
1+
plugins {
2+
alias(libs.plugins.agp.app) apply false
3+
}
4+
5+
def execute(String cmd, File currentWorkingDir = file("./"), String fallback = "") {
6+
try {
7+
def byteOut = new ByteArrayOutputStream()
8+
project.exec {
9+
workingDir = currentWorkingDir
10+
commandLine = cmd.split("\s")
11+
standardOutput = byteOut
12+
errorOutput = new ByteArrayOutputStream()
13+
ignoreExitValue = true
814
}
9-
}
10-
dependencies {
11-
classpath "com.android.tools.build:gradle:7.4.2"
12-
// NOTE: Do not place your application dependencies here; they belong
13-
// in the individual module build.gradle files
15+
def result = byteOut.toString().trim()
16+
return result.isEmpty() ? fallback : result
17+
} catch (Exception ignored) {
18+
return fallback
1419
}
1520
}
1621

17-
plugins {
18-
id 'idea'
22+
def gitCommitCount = execute("git rev-list HEAD --count", file("./"), "1").toInteger()
23+
def gitCommitHash = execute("git rev-parse --verify --short HEAD", file("./"), "unknown")
24+
25+
ext {
26+
moduleId = "zygisk-loader"
27+
moduleName = "Zygisk Loader"
28+
verName = "1.0.0"
29+
verCode = gitCommitCount
30+
commitHash = gitCommitHash
31+
abiList = ["arm64-v8a", "armeabi-v7a", "x86", "x86_64"]
32+
33+
androidMinSdkVersion = 26
34+
androidTargetSdkVersion = 36
35+
androidCompileSdkVersion = 36
36+
androidBuildToolsVersion = "36.1.0"
37+
androidCompileNdkVersion = "29.0.14206865"
38+
androidSourceCompatibility = JavaVersion.VERSION_17
39+
androidTargetCompatibility = JavaVersion.VERSION_17
1940
}
2041

21-
idea.module {
22-
excludeDirs += file('out')
23-
resourceDirs += file('zygiskloader')
24-
resourceDirs += file('scripts')
42+
tasks.register("clean", Delete) {
43+
delete rootProject.buildDir
2544
}
2645

27-
ext {
28-
min_sdk = 23
29-
target_sdk = 31
46+
subprojects {
47+
afterEvaluate { project ->
48+
if (project.plugins.hasPlugin("com.android.application")) {
49+
android {
50+
namespace = "io.github.hansobored.zygisk.loader"
51+
compileSdkVersion rootProject.androidCompileSdkVersion
52+
ndkVersion rootProject.androidCompileNdkVersion
53+
buildToolsVersion rootProject.androidBuildToolsVersion
3054

31-
outDir = file("$rootDir/out")
32-
}
55+
defaultConfig {
56+
minSdkVersion rootProject.androidMinSdkVersion
57+
}
3358

34-
task clean(type: Delete) {
35-
delete rootProject.buildDir, outDir
59+
compileOptions {
60+
sourceCompatibility rootProject.androidSourceCompatibility
61+
targetCompatibility rootProject.androidTargetCompatibility
62+
}
63+
}
64+
}
65+
}
3666
}

gradle/libs.versions.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[versions]
2+
agp = "8.11.0"
3+
4+
[plugins]
5+
agp-app = { id = "com.android.application", version.ref = "agp" }
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
#Wed Apr 24 19:18:08 EEST 2024
1+
#Sun Dec 31 12:28:57 CST 2023
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
4-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
4+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
55
zipStoreBase=GRADLE_USER_HOME
66
zipStorePath=wrapper/dists

module.gradle

Lines changed: 0 additions & 11 deletions
This file was deleted.

module/.gitignore

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)