Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Android CI

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest

env:
ANDROID_SDK_ROOT: ${{ github.workspace }}/android-sdk
ANDROID_HOME: ${{ github.workspace }}/android-sdk

steps:
- uses: actions/checkout@v4

- name: set up JDK 17 for SDK Manager
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'

- name: Install Android SDK
run: |
mkdir -p "$ANDROID_SDK_ROOT/cmdline-tools"
cd "$ANDROID_SDK_ROOT/cmdline-tools"
curl -fsSL -o cmdline-tools.zip https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
unzip -q cmdline-tools.zip
mv cmdline-tools latest

yes | "$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" --licenses
"$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" \
"platform-tools" \
"platforms;android-29" \
"build-tools;29.0.0"

- name: set up JDK 8 for gradle compilation
uses: actions/setup-java@v4
with:
java-version: '8'
distribution: 'temurin'
cache: gradle

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Build with Gradle
run: ./gradlew clean :app:assembleDebug --no-daemon --stacktrace

- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: debug-apk
path: app/build/outputs/apk/debug/*.apk
21 changes: 3 additions & 18 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
//buildscript {
// repositories {
// maven { url 'https://maven.fabric.io/public' }
// }
//
// dependencies {
// classpath 'io.fabric.tools:gradle:1.+'
// }
//}
apply plugin: 'com.android.application'
//apply plugin: 'io.fabric'

repositories {
// maven { url 'https://maven.fabric.io/public' }
maven { url 'https://jitpack.io' }
jcenter()
maven { url 'https://api.xposed.info' }
mavenCentral()
maven { url "https://jitpack.io" }
google()
}


android {
signingConfigs {
release {
Expand Down Expand Up @@ -72,12 +60,9 @@ dependencies {
implementation 'com.google.android.material:material:1.0.0'
implementation 'com.github.apl-devs:appintro:v4.2.3'
implementation 'de.greenrobot:greendao:2.0.0'
// implementation('com.crashlytics.sdk.android:crashlytics:2.5.2@aar') {
// transitive = true
// }

//Glide
implementation ('com.github.bumptech.glide:glide:4.9.0')
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
annotationProcessor 'androidx.annotation:annotation:1.1.0'
}
}
131 changes: 76 additions & 55 deletions app/src/main/java/tw/fatminmin/xposed/minminguard/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.content.res.XModuleResources;
import android.os.Build;

import java.io.File;
Expand Down Expand Up @@ -59,9 +58,9 @@
import tw.fatminmin.xposed.minminguard.blocker.adnetwork.MasAd;
import tw.fatminmin.xposed.minminguard.blocker.adnetwork.MdotM;
import tw.fatminmin.xposed.minminguard.blocker.adnetwork.Millennial;
import tw.fatminmin.xposed.minminguard.blocker.adnetwork.MoPub;
import tw.fatminmin.xposed.minminguard.blocker.adnetwork.MobFox;
import tw.fatminmin.xposed.minminguard.blocker.adnetwork.Mobclix;
import tw.fatminmin.xposed.minminguard.blocker.adnetwork.MoPub;
import tw.fatminmin.xposed.minminguard.blocker.adnetwork.Nend;
import tw.fatminmin.xposed.minminguard.blocker.adnetwork.Og;
import tw.fatminmin.xposed.minminguard.blocker.adnetwork.Onelouder;
Expand All @@ -82,6 +81,7 @@
import tw.fatminmin.xposed.minminguard.blocker.custom_mod.OneWeather;
import tw.fatminmin.xposed.minminguard.blocker.custom_mod.Viafree;
import tw.fatminmin.xposed.minminguard.blocker.custom_mod._2chMate;
import android.content.res.XModuleResources;

public class Main implements IXposedHookZygoteInit, IXposedHookLoadPackage
{
Expand All @@ -102,10 +102,20 @@ public class Main implements IXposedHookZygoteInit, IXposedHookLoadPackage
/* Custom Mod*/
};

private static Set<String> hookedPackages = new HashSet<>();

private static boolean isEnabled(SharedPreferences pref, String pkgName)
{
if (pref == null) return false;

if (pref instanceof XSharedPreferences) {
((XSharedPreferences) pref).reload();
}

String mode = pref.getString(Common.KEY_MODE, Common.VALUE_MODE_BLACKLIST);

if (mode == null) mode = Common.VALUE_MODE_BLACKLIST;

if (mode.equals(Common.VALUE_MODE_AUTO))
{
return true;
Expand Down Expand Up @@ -171,16 +181,18 @@ private void UnpackResources()
{
resources = XModuleResources.createInstance(MODULE_PATH, null);
byte[] array = XposedHelpers.assetAsByteArray(resources, "host/output_file");
String decoded = new String(array);
String[] sUrls = decoded.split("\n");

Collections.addAll(patterns, sUrls);
if (array != null) {
String decoded = new String(array);
String[] sUrls = decoded.split("\n");
Collections.addAll(patterns, sUrls);
}

array = XposedHelpers.assetAsByteArray(resources, "host/mmg_pattern");
decoded = new String(array);
sUrls = decoded.split("\n");

Collections.addAll(patterns, sUrls);
if (array != null) {
String decoded = new String(array);
String[] sUrls = decoded.split("\n");
Collections.addAll(patterns, sUrls);
}
}
catch (Exception e)
{
Expand All @@ -191,7 +203,11 @@ private void UnpackResources()
@Override
public void handleLoadPackage(final LoadPackageParam lpparam)
{
if (lpparam.packageName.equals(MY_PACKAGE_NAME)) {
if (lpparam == null || lpparam.packageName == null) return;

final String packageName = lpparam.packageName;

if (packageName.equals(MY_PACKAGE_NAME)) {
XposedHelpers.findAndHookMethod("tw.fatminmin.xposed.minminguard.blocker.Util", lpparam.classLoader, "xposedEnabled", XC_MethodReplacement.returnConstant(true));
if (xposedVersionCode >= 93)
XposedHelpers.findAndHookMethod("tw.fatminmin.xposed.minminguard.Common", lpparam.classLoader, "getPrefMode", XC_MethodReplacement.returnConstant(Context.MODE_WORLD_READABLE));
Expand All @@ -217,6 +233,9 @@ public void handleLoadPackage(final LoadPackageParam lpparam)
* Note that this implies a) hooking System Framework in LSposed Manager and b) needs a reboot after enabling an app in MinMinGuard
* because only android can access com.android.server.pm.permission.PermissionManagerService !!!
*
* Important: QUERY_ALL_PACKAGES is a highly sensitive permission. Its injection here must be minimal and closely guarded
* so it only affects target apps managed by MinMinGuard to prevent widespread abuse.
*
* The implementation is based on Mino260806's snippet (Thanks!)
* https://forum.xda-developers.com/t/xposed-for-devs-how-to-dynamically-declare-permissions-for-a-target-app-without-altering-its-manifest-and-changing-its-signature.4440379/post-86833435
*
Expand All @@ -225,65 +244,67 @@ public void handleLoadPackage(final LoadPackageParam lpparam)
* https://github.com/Lawiusz/xposed_lockscreen_visualizer/blob/master/app/src/main/java/pl/lawiusz/lockscreenvisualizerxposed/PermGrant.java
*/
if (Build.VERSION.SDK_INT > 29) {
if (lpparam.packageName.equals("android")) {
if (packageName.equals("android")) {
try {
XposedBridge.hookAllMethods(XposedHelpers.findClass("com.android.server.pm.permission.PermissionManagerService", lpparam.classLoader), "restorePermissionState", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
String pkgName = (String) XposedHelpers.getObjectField(param.args[0], "packageName");
Util.log(MY_PACKAGE_NAME, "Package " + pkgName + " is requesting permissions");
if (isEnabled(pref, pkgName)) {
List<String> permissions = (List<String>) XposedHelpers.getObjectField(param.args[0], "requestedPermissions");
String query_all_perm = "android.permission.QUERY_ALL_PACKAGES";
if (!permissions.contains(query_all_perm)) {
permissions.add(query_all_perm);
Util.log(MY_PACKAGE_NAME, "Added " + query_all_perm + " permission to " + pkgName);
Class<?> pmsClass = XposedHelpers.findClassIfExists("com.android.server.pm.permission.PermissionManagerService", lpparam.classLoader);
if (pmsClass != null) {
XposedBridge.hookAllMethods(pmsClass, "restorePermissionState", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
if (param.args == null || param.args.length == 0 || param.args[0] == null) return;

String pkgName = (String) XposedHelpers.getObjectField(param.args[0], "packageName");
if (pkgName == null) return;

if (isEnabled(pref, pkgName)) {
List<String> permissions = (List<String>) XposedHelpers.getObjectField(param.args[0], "requestedPermissions");
if (permissions != null) {
String query_all_perm = "android.permission.QUERY_ALL_PACKAGES";
if (!permissions.contains(query_all_perm)) {
permissions.add(query_all_perm);
}
}
}
}
}
});
});
}
} catch (Exception e) {
Util.log(MY_PACKAGE_NAME, "PermissionManagerService -> " + e);
}
}
}

/**
* https://developer.android.com/reference/android/app/Application.html#onCreate()
* wait for the app started to get remote preferences
*/
XposedHelpers.findAndHookMethod("android.app.Application", lpparam.classLoader, "onCreate", new XC_MethodHook()
{
@Override
protected void afterHookedMethod(MethodHookParam param)
{
final String packageName = lpparam.packageName;
Context context = Util.getCurrentApplication();

if (null == context)
{
Util.log(packageName, "failed to get context");
return;
}
if (hookedPackages.contains(packageName)) return;
hookedPackages.add(packageName);

if (isEnabled(pref, packageName))
try {
XposedHelpers.findAndHookMethod("android.app.Application", lpparam.classLoader, "onCreate", new XC_MethodHook()
{
@Override
protected void afterHookedMethod(MethodHookParam param)
{
Util.log(packageName, "is enabled for MinMinGuard");

// Api based blocking
ApiBlocking.handle(packageName, lpparam, param);
appSpecific(packageName, lpparam);
Context context = Util.getCurrentApplication();

// Name based blocking
NameBlocking.nameBasedBlocking(packageName, lpparam);
if (null == context)
{
return;
}

// url filtering
if (pref.getBoolean(packageName + "_url", false))
if (isEnabled(pref, packageName))
{
UrlFiltering.removeWebViewAds(packageName, lpparam);
ApiBlocking.handle(packageName, lpparam, param);
appSpecific(packageName, lpparam);
NameBlocking.nameBasedBlocking(packageName, lpparam);

if (pref.getBoolean(packageName + "_url", false))
{
UrlFiltering.removeWebViewAds(packageName, lpparam);
}
}
}
}
});
});
} catch (Throwable t) {
Util.log(MY_PACKAGE_NAME, "Failed to hook Application.onCreate in " + packageName);
}
}
}
Loading
Loading