Skip to content

Commit d5d900a

Browse files
authored
Merge pull request #9 from ispointer/NativeModMenu
NativeModMenu 2.5
2 parents ef5155c + d8fc5c9 commit d5d900a

45 files changed

Lines changed: 2896 additions & 1086 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎LICENSE‎

Lines changed: 201 additions & 201 deletions
Large diffs are not rendered by default.

‎README.md‎

Lines changed: 77 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,77 @@
1-
# NativeModMenu
2-
3-
### High-Performance In-Memory Dex Loader for Android
4-
5-
`NativeModMenu` is a sophisticated reimplementation of the LGL Android Mod Menu. It leverages `InMemoryDexClassLoader` to load the Java UI layer directly from system memory, eliminating the need for physical `.dex` files on the device storage.
6-
7-
---
8-
9-
## Technical Overview
10-
11-
This implementation focuses on stealth and efficiency by embedding the `FloatingModMenu.dex` as a HEX-encoded string within the native binary. At runtime, the JNI layer performs a dynamic bootstrap to initialize the menu without leaving any traces in the application's assets or data folders.
12-
13-
### Key Architectural Differences
14-
15-
* **Zero Disk Footprint:** No external dex file is shipped; the menu exists only in memory.
16-
* **Volatile Loading:** Utilizes `dalvik.system.InMemoryDexClassLoader` for runtime execution.
17-
* **Native-Led Initialization:** The entire menu lifecycle is managed via JNI native bindings.
18-
* **Reduced Attack Surface:** Eliminates file-based detection vectors by avoiding asset extraction.
19-
* **Native Compatibility:** Fully compatible with existing LGL Java menu logic.
20-
21-
---
22-
23-
## Core Logic Flow
24-
25-
1. **Data Embedding:** The `FloatingModMenu.dex` is converted to a HEX string and compiled into the C++ source.
26-
2. **Buffer Reconstruction:** At runtime, the HEX string is decoded into a `ByteBuffer`.
27-
3. **Class Loading:** The `InMemoryDexClassLoader` interprets the buffer and loads the `uk.lgl.modmenu.FloatingModMenu` class.
28-
4. **JNI Registration:** Native functions are dynamically bound to the Java class methods.
29-
5. **Execution:** The `binJava()` entry point triggers `FloatingModMenu.antik(Context)` to render the UI.
30-
31-
---
32-
33-
## JNI Native Interface
34-
35-
The following methods are registered dynamically to bridge the Native and Java layers:
36-
37-
| Method | Functionality |
38-
| --- | --- |
39-
| `Icon()` | Returns the primary menu icon |
40-
| `IconWebViewData()` | Manages WebView icon resources |
41-
| `getFeatureList()` | Retrieves the feature configuration array |
42-
| `settingsList()` | Retrieves the menu settings configuration |
43-
| `Changes(...)` | Main event handler for feature toggles |
44-
| `setTitleText(...)` | Customizes the UI title appearance |
45-
| `setHeadingText(...)` | Customizes the UI heading appearance |
46-
47-
---
48-
49-
## Implementation Details
50-
51-
### Requirements
52-
53-
* **Android Version:** 8.0 (API 26) or higher
54-
* **Architecture:** ARMv7, ARM64-v8a
55-
* **Environment:** Native JNI-based injection
56-
57-
### Entry Point
58-
59-
```cpp
60-
// Executed post JNI_OnLoad
61-
void binJava();
62-
63-
```
64-
65-
The `binJava` function is responsible for retrieving the `Application` context via `ActivityThread` and initiating the memory-loading sequence.
66-
67-
---
68-
69-
## Legal Notice
70-
71-
This project is developed strictly for **educational and research purposes**. Modifying the runtime behavior of third-party applications may violate their Terms of Service. The developers assume no liability for misuse.
72-
73-
## Acknowledgments
74-
75-
* **Original Architecture:** LGL Android Mod Menu
76-
* **Lead Developer:** AntikMods
77-
* **Memory Loading Logic:** NepMods
1+
# NativeModMenu
2+
3+
### High-Performance In-Memory Dex Loader for Android
4+
5+
`NativeModMenu` is a sophisticated reimplementation of the LGL Android Mod Menu. It leverages `InMemoryDexClassLoader` to load the Java UI layer directly from system memory, eliminating the need for physical `.dex` files on the device storage.
6+
7+
---
8+
9+
## Technical Overview
10+
11+
This implementation focuses on stealth and efficiency by embedding the `FloatingModMenu.dex` as a HEX-encoded string within the native binary. At runtime, the JNI layer performs a dynamic bootstrap to initialize the menu without leaving any traces in the application's assets or data folders.
12+
13+
### Key Architectural Differences
14+
15+
* **Zero Disk Footprint:** No external dex file is shipped; the menu exists only in memory.
16+
* **Volatile Loading:** Utilizes `dalvik.system.InMemoryDexClassLoader` for runtime execution.
17+
* **Native-Led Initialization:** The entire menu lifecycle is managed via JNI native bindings.
18+
* **Reduced Attack Surface:** Eliminates file-based detection vectors by avoiding asset extraction.
19+
* **Native Compatibility:** Fully compatible with existing LGL Java menu logic.
20+
21+
---
22+
23+
## Core Logic Flow
24+
25+
1. **Data Embedding:** The `FloatingModMenu.dex` is converted to a HEX string and compiled into the C++ source.
26+
2. **Buffer Reconstruction:** At runtime, the HEX string is decoded into a `ByteBuffer`.
27+
3. **Class Loading:** The `InMemoryDexClassLoader` interprets the buffer and loads the `uk.lgl.modmenu.FloatingModMenu` class.
28+
4. **JNI Registration:** Native functions are dynamically bound to the Java class methods.
29+
5. **Execution:** The `binJava()` entry point triggers `FloatingModMenu.antik(Context)` to render the UI.
30+
31+
---
32+
33+
## JNI Native Interface
34+
35+
The following methods are registered dynamically to bridge the Native and Java layers:
36+
37+
| Method | Functionality |
38+
| --- | --- |
39+
| `Icon()` | Returns the primary menu icon |
40+
| `IconWebViewData()` | Manages WebView icon resources |
41+
| `getFeatureList()` | Retrieves the feature configuration array |
42+
| `settingsList()` | Retrieves the menu settings configuration |
43+
| `Changes(...)` | Main event handler for feature toggles |
44+
| `setTitleText(...)` | Customizes the UI title appearance |
45+
| `setHeadingText(...)` | Customizes the UI heading appearance |
46+
47+
---
48+
49+
## Implementation Details
50+
51+
### Requirements
52+
53+
* **Android Version:** 8.0 (API 26) or higher
54+
* **Architecture:** ARMv7, ARM64-v8a
55+
* **Environment:** Native JNI-based injection
56+
57+
### Entry Point
58+
59+
```cpp
60+
// Executed post JNI_OnLoad
61+
void binJava();
62+
63+
```
64+
65+
The `binJava` function is responsible for retrieving the `Application` context via `ActivityThread` and initiating the memory-loading sequence.
66+
67+
---
68+
69+
## Legal Notice
70+
71+
This project is developed strictly for **educational and research purposes**. Modifying the runtime behavior of third-party applications may violate their Terms of Service. The developers assume no liability for misuse.
72+
73+
## Acknowledgments
74+
75+
* **Original Architecture:** LGL Android Mod Menu
76+
* **Lead Developer:** AntikMods
77+
* **Memory Loading Logic:** NepMods

‎app/build.gradle‎

Lines changed: 90 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,91 @@
1-
import com.android.build.gradle.tasks.ExternalNativeBuildTask
2-
import java.io.FileInputStream
3-
4-
plugins {
5-
alias(libs.plugins.android.application)
6-
}
7-
8-
class JavaDex {
9-
public static String xxd_p(String filePath) {
10-
StringBuilder hexString = new StringBuilder()
11-
File file = new File(filePath)
12-
try (FileInputStream fis = new FileInputStream(file)) {
13-
byte[] data = fis.readAllBytes()
14-
for (byte b : data) {
15-
hexString.append(String.format("%02X", b))
16-
}
17-
}
18-
return hexString.toString()
19-
}
20-
}
21-
22-
android {
23-
namespace 'uk.lgl'
24-
compileSdk 36
25-
26-
defaultConfig {
27-
applicationId "uk.lgl"
28-
minSdk 28
29-
targetSdk 36
30-
versionCode 1
31-
versionName "1.0"
32-
33-
ndk {
34-
abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
35-
}
36-
37-
multiDexEnabled false
38-
}
39-
40-
buildTypes {
41-
release {
42-
minifyEnabled true
43-
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
44-
}
45-
}
46-
47-
externalNativeBuild {
48-
ndkBuild {
49-
path file('src/main/jni/Android.mk')
50-
}
51-
}
52-
53-
android.applicationVariants.all { variant ->
54-
def variantName = variant.name.capitalize()
55-
def taskName = "Dex2CppAnd8Up${variantName}"
56-
57-
def customTask = tasks.register(taskName) {
58-
outputs.upToDateWhen { false }
59-
60-
def mergeDexTask = tasks.findByName("mergeDex${variantName}")
61-
if (mergeDexTask) {
62-
dependsOn mergeDexTask
63-
}
64-
65-
doLast {
66-
def dexDir = file("${project.buildDir}/intermediates/dex/${variant.name}/mergeDex${variantName}")
67-
def dexFiles = fileTree(dir: dexDir, include: "**/classes.dex")
68-
69-
if (dexFiles.isEmpty()) {
70-
throw new GradleException("classes.dex not found for ${variant.name} in ${dexDir}")
71-
}
72-
73-
def dexFile = dexFiles.first()
74-
String hexString = JavaDex.xxd_p(dexFile.absolutePath)
75-
def headerFile = file("${project.projectDir}/src/main/jni/JavaGPP/Interface/OreoOrMore.h")
76-
77-
headerFile.parentFile.mkdirs()
78-
headerFile.write("#define OreoOrMore \"${hexString}\"\n")
79-
println ">>> Success: Generated header for ${variantName}"
80-
}
81-
}
82-
83-
tasks.configureEach { task ->
84-
if (task.name.startsWith("buildNdkBuild${variantName}") ||
85-
task.name.startsWith("externalNativeBuild${variantName}")) {
86-
task.dependsOn customTask
87-
}
88-
}
89-
}
1+
import com.android.build.gradle.tasks.ExternalNativeBuildTask
2+
import java.io.FileInputStream
3+
4+
plugins {
5+
alias(libs.plugins.android.application)
6+
}
7+
8+
class JavaDex {
9+
public static String xxd_p(String filePath) {
10+
StringBuilder hexString = new StringBuilder()
11+
File file = new File(filePath)
12+
try (FileInputStream fis = new FileInputStream(file)) {
13+
byte[] data = fis.readAllBytes()
14+
for (byte b : data) {
15+
hexString.append(String.format("%02X", b))
16+
}
17+
}
18+
return hexString.toString()
19+
}
20+
}
21+
22+
android {
23+
namespace 'com.android.support'
24+
compileSdk 36
25+
26+
defaultConfig {
27+
applicationId "com.android.support"
28+
minSdk 28
29+
targetSdk 36
30+
versionCode 1
31+
versionName "1.0"
32+
33+
ndk {
34+
abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
35+
}
36+
37+
multiDexEnabled false
38+
}
39+
40+
buildTypes {
41+
release {
42+
minifyEnabled true
43+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
44+
}
45+
}
46+
47+
externalNativeBuild {
48+
ndkBuild {
49+
path file('src/main/jni/Android.mk')
50+
}
51+
ndkVersion = '28.2.13676358'
52+
}
53+
54+
android.applicationVariants.all { variant ->
55+
def variantName = variant.name.capitalize()
56+
def taskName = "Dex2CppAnd8Up${variantName}"
57+
58+
def customTask = tasks.register(taskName) {
59+
outputs.upToDateWhen { false }
60+
61+
def mergeDexTask = tasks.findByName("mergeDex${variantName}")
62+
if (mergeDexTask) {
63+
dependsOn mergeDexTask
64+
}
65+
66+
doLast {
67+
def dexDir = file("${project.buildDir}/intermediates/dex/${variant.name}/mergeDex${variantName}")
68+
def dexFiles = fileTree(dir: dexDir, include: "**/classes.dex")
69+
70+
if (dexFiles.isEmpty()) {
71+
throw new GradleException("classes.dex not found for ${variant.name} in ${dexDir}")
72+
}
73+
74+
def dexFile = dexFiles.first()
75+
String hexString = JavaDex.xxd_p(dexFile.absolutePath)
76+
def headerFile = file("${project.projectDir}/src/main/jni/Includes/Dex.h")
77+
78+
headerFile.parentFile.mkdirs()
79+
headerFile.write("#define hexdex \"${hexString}\"\n")
80+
println ">>> Success: Generated header for ${variantName}"
81+
}
82+
}
83+
84+
tasks.configureEach { task ->
85+
if (task.name.startsWith("buildNdkBuild${variantName}") ||
86+
task.name.startsWith("externalNativeBuild${variantName}")) {
87+
task.dependsOn customTask
88+
}
89+
}
90+
}
9091
}

‎app/proguard-rules.pro‎

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
# Add project specific ProGuard rules here.
2-
# You can control the set of applied configuration files using the
3-
# proguardFiles setting in build.gradle.
4-
#
5-
# For more details, see
6-
# http://developer.android.com/guide/developing/tools/proguard.html
7-
8-
# If your project uses WebView with JS, uncomment the following
9-
# and specify the fully qualified class name to the JavaScript interface
10-
# class:
11-
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12-
# public *;
13-
#}
14-
15-
# Uncomment this to preserve the line number information for
16-
# debugging stack traces.
17-
#-keepattributes SourceFile,LineNumberTable
18-
19-
# If you keep the line number information, uncomment this to
20-
# hide the original source file name.
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
2121
#-renamesourcefileattribute SourceFile

0 commit comments

Comments
 (0)