Skip to content

Latest commit

 

History

History
142 lines (121 loc) · 7.07 KB

File metadata and controls

142 lines (121 loc) · 7.07 KB

Architecture

Overview

Android Proxy Manager is a single APK that acts as both an Android app and an LSPosed module. It uses Magisk for root access and iptables for transparent proxying.

┌─────────────────────────────────────────────────────────────────┐
│                        Android App (APK)                        │
│                                                                 │
│  ┌──────────────┐   ┌──────────────┐   ┌────────────────────┐  │
│  │  Compose UI  │   │  ViewModel   │   │  IntentReceiver    │  │
│  │  (Screens)   │◄──│  (Hilt)      │   │  (CLI/ADB API)     │  │
│  └──────────────┘   └──────┬───────┘   └────────┬───────────┘  │
│                            │                    │               │
│                    ┌───────▼────────────────────▼──────────┐   │
│                    │           ProxyRepository              │   │
│                    │  (Room DB + DataStore + config writer) │   │
│                    └───────────────────┬───────────────────┘   │
│                                        │                        │
│                    ┌───────────────────▼───────────────────┐   │
│                    │           IptablesManager              │   │
│                    │  (root commands via RootCommand/su)    │   │
│                    └───────────────────┬───────────────────┘   │
└────────────────────────────────────────┼────────────────────────┘
                                         │ su + named pipe
┌────────────────────────────────────────▼────────────────────────┐
│                     Magisk Module (service.sh)                   │
│                                                                  │
│  • Runs as root after boot                                       │
│  • Listens on /data/local/proxy-manager/cmd.pipe                 │
│  • Manages iptables TPROXY + MARK rules                          │
│  • Reads /data/local/proxy-manager/config.json on RELOAD         │
└──────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────┐
│                    LSPosed Module (XposedInit)                    │
│                                                                   │
│  • Loaded into target app processes by LSPosed framework          │
│  • Reads /data/local/proxy-manager/config.json                    │
│  • Hooks: ProxySelector.getDefault(), OkHttpClient.Builder.build()│
│  • Sets system properties for WebView proxy                       │
└───────────────────────────────────────────────────────────────────┘

Proxy Modes

Mode Mechanism Scope
SYSTEM settings put global http_proxy Apps that respect system proxy
TPROXY iptables PREROUTING TPROXY + ip rule/route All TCP traffic (transparent)
PER_APP iptables OUTPUT MARK per UID + PREROUTING TPROXY Selected apps by UID
DISABLED Teardown all rules

iptables TPROXY Pattern

TPROXY is only valid in the PREROUTING chain. For locally-generated traffic (OUTPUT), we use MARK to reroute packets back through loopback where the PREROUTING TPROXY rule intercepts them:

# Routing: marked packets → loopback
ip rule add fwmark 0x01 table 100
ip route add local default dev lo table 100

# PREROUTING: intercept marked TCP → tproxy listener
iptables -t mangle -A PREROUTING -p tcp -j TPROXY \
    --tproxy-mark 0x01 --on-port 8118

# OUTPUT (per-app): mark packets from UID
iptables -t mangle -A OUTPUT -m owner --uid-owner $uid -j MARK --set-mark 0x01

The transparent proxy listener must bind with IP_TRANSPARENT socket option.

Shared Config

/data/local/proxy-manager/config.json (chmod 600, root-owned) bridges all three layers:

{
  "enabled": true,
  "mode": "per_app",
  "global_proxy": "1.2.3.4:8080",
  "tproxy_port": 8118,
  "app_proxies": {
    "com.example.app": {
      "host": "1.2.3.4",
      "port": 8080,
      "type": "HTTP",
      "uid": 10123,
      "enabled": true
    }
  }
}

IPC: App → Magisk Service

Commands are sent via named pipe /data/local/proxy-manager/cmd.pipe (chmod 600):

SET_GLOBAL host:port
CLEAR_GLOBAL
SET_APP uid:host:port
CLEAR_APP uid
STATUS
RELOAD

Response is written to /data/local/proxy-manager/cmd.response.

Key Files

android-proxy-rooted/
├── magisk-module/
│   ├── module.prop          # Magisk module metadata
│   ├── customize.sh         # Install-time setup
│   ├── post-fs-data.sh      # Early boot
│   └── service.sh           # Root daemon (iptables + IPC)
└── app/
    └── app/src/main/
        ├── AndroidManifest.xml
        ├── assets/xposed_init           # LSPosed entry point
        └── java/com/proxymanager/
            ├── data/
            │   ├── ProxyDatabase.kt     # Room DB
            │   └── ProxyRepository.kt  # Data layer
            ├── root/
            │   ├── RootCommand.kt       # su exec
            │   ├── IptablesManager.kt   # iptables rules
            │   ├── ProxyTester.kt       # Connectivity test
            │   └── IpChecker.kt         # IP verification
            ├── service/
            │   ├── ProxyService.kt      # Foreground service
            │   ├── IntentReceiver.kt    # CLI/ADB API
            │   └── BootReceiver.kt
            ├── viewmodel/
            │   └── ProxyViewModel.kt
            ├── ui/                      # Compose screens
            └── xposed/
                └── XposedInit.kt        # LSPosed hooks