A SOCKS5 proxy server that runs on your iPhone or Android phone so other devices on the same network can route traffic through it.
| Platform | Stack | Upstream path |
|---|---|---|
| iOS | Swift + Network.framework | Device default route (as today) |
| Android | Kotlin + Jetpack Compose + Java sockets | Every outbound socket bound to cellular |
The Android tree lives under android/ and does not touch the Xcode project.
When you tether a laptop to your phone, the laptop's packets are forwarded by the phone. They reach the carrier carrying the laptop's TCP/IP fingerprint and a TTL one hop lower than anything the phone sends itself. That difference is what tethering detection looks for.
This proxy removes it. The laptop speaks SOCKS5 to the phone, and the phone opens its own connection to the destination — so the packets that leave the radio are originated by the phone's own IP stack and look like traffic the phone generated.
This only makes sense over the phone's own hotspot. Pointing a client at the proxy across ordinary Wi-Fi achieves nothing — that client already has the network's own uplink — and it leaves a passwordless gateway listening on a network you do not control.
The proxy accepts NO AUTH connections. Anyone who can reach your phone's address and port can use it as an open relay — including anything else on the café Wi-Fi, the hotel LAN, or a VPN that puts your devices on one flat network. Run it only on a network you control, and stop the app when you are done. The app shows the same warning on screen and it is not dismissible.
- SOCKS5: NO AUTH,
CONNECT, andUDP ASSOCIATE - Addressing: IPv4, IPv6, and domain names (ATYP
0x01/0x04/0x03) - Traffic monitoring: live upload/download rate, cumulative totals, active TCP/UDP session counts, and a recent-activity log
- Reachable addresses: every interface a client could connect to, hotspot first, refreshed as you switch networks
- iOS: Swift + Network.framework; background keep-alive via silent audio session (not an App Store path) — see Running in the background
- Android: Kotlin + Jetpack Compose; Foreground Service (
connectedDevice) with an ongoing notification; every upstream socket bound to cellular
- iOS 17.0 or later
- Xcode with a signing account (a free Apple ID works)
- Android 8.0+ (API 26); app targets API 36
- Android Studio or command-line SDK 36 + JDK 17
- A phone with mobile data and the ability to open a personal hotspot
- Clone the project (or a fork):
git clone https://github.com/Nanako0129/SocksBypass.git- Open the Xcode project:
- Open
SocksBypass.xcodeproj - Select your developer account for signing
- Change the Bundle Identifier to your own
- Deploy to a device:
- Connect your iOS device
- Select it in Xcode
- Run
Easiest — install a prebuilt APK
- Open Releases and download
app-debug.apkfrom the latest tag (e.g. v0.1.0). - On the phone, allow install from unknown sources if prompted, then open the APK.
- Follow Usage → Android (hotspot + Start).
The release APK is an unsigned debug build for sideload/testing — not Play Store signed.
Or build from source
- Open the Gradle project:
cd android
# optional: echo "sdk.dir=$HOME/Library/Android/sdk" > local.properties
./gradlew :app:assembleDebug- Install on a device:
adb install -r app/build/outputs/apk/debug/app-debug.apkOr open the android/ folder in Android Studio, run on a physical device
(emulator has no real cellular for the fail-closed upstream path).
- Launch the app
- Wait for the IP address and port to appear (default port 9876)
- On the device that should use the proxy, configure SOCKS5:
- Proxy server: the displayed IP address
- Port: the displayed port
- No authentication
For USB instead of Wi-Fi, use danielpaulus/go-ios
forward mode: ios forward 1080 9876, then point the client at 127.0.0.1.
Android is designed for this flow:
computer --Wi-Fi--> phone hotspot :9876 --SOCKS5--> phone app
|
v
each Socket bound to CELLULAR
|
v
4G / 5G
- On the phone, open Settings → Hotspot / tethering and enable the personal hotspot. The app does not start tethering for you (no privileged APIs).
- Launch SocksBypass. Grant notification permission if asked (foreground service).
- Tap Refresh addresses and select the hotspot private IP (e.g.
192.168.43.1). The app never defaults to0.0.0.0. - Tap Start. Status should read
LISTENING. Upstream should showCELLULAR · …. If mobile data is off, status becomesCELLULAR UNAVAILABLEand new CONNECT sessions are rejected (no silent Wi-Fi fallback). - On the computer (joined to the phone hotspot), configure SOCKS5:
- Host: the selected listen IP
- Port:
9876(unless you changed it) - Authentication: none
| Claim | Status |
|---|---|
| Fail-closed when cellular INTERNET is unavailable (CONNECT rejected, no Wi-Fi fallback) | Proven on device in development |
| Positive “all traffic only on 4G/5G” end-to-end | Not proven without a usable cellular INTERNET Network — unit/CI green does not prove radio path |
| Upstream GitHub PR checks on a cross-fork PR | Often empty until the maintainer approves workflows or CI lands on their default branch; use the fork Actions tab |
See also docs/android/device-verification.md.
# Layer 1 — no phone (CI + local)
python3 Bench/socks_bench.py --mode self-test
# Layer 2 — phone proxy; target must be reachable *from the phone's upstream*
# (prefer an Internet host when testing cellular — not the laptop's 127.0.0.1)
python3 Bench/socks_bench.py \
--mode correctness \
--proxy-host 192.168.43.1 \
--proxy-port 9876 \
--target-host <host-reachable-via-phone-cellular>Unit tests for the pure SOCKS core (no device required):
cd android && ./gradlew :socks-core:testProduction CONNECT/UDP refuse loopback, link-local, any-local, and multicast destinations. Non-literal hosts always resolve via the cellular-bound path — never process-default DNS for fake “IP-looking” strings.
GitHub Actions (.github/workflows/ci.yml) runs on every PR and on main / feature/** pushes:
| Job | Required | What it does |
|---|---|---|
| Android | yes | JDK 17 + SDK 36, ./gradlew :socks-core:test :app:assembleDebug, upload APK + reports |
| Bench | yes | python3 Bench/socks_bench.py --mode self-test |
| Structure | yes | Android tree present, no Gradle in Xcode, FGS/connectedDevice/cellular/SDK gates |
| iOS smoke | soft | xcodebuild -list + best-effort unsigned simulator build |
CI artifacts: green Android jobs upload app-debug.apk and JUnit reports (14-day retention on the Actions run page). Useful for PR smoke installs; not a versioned release.
Releases (normal path): push an annotated tag vX.Y.Z on main.
.github/workflows/release.yml builds the debug APK,
runs unit tests, and creates a GitHub Release
with app-debug.apk attached.
# from a clean main tip that already passed CI
git checkout main && git pull
git tag -a v0.2.0 -m "v0.2.0 — short summary"
git push origin v0.2.0
# → Actions “Release” job → https://github.com/Nanako0129/SocksBypass/releasesStore upload (Play / App Store) is intentionally not automated here.
Details: .github/workflows/ci-docs.md.
The iOS app keeps serving after you switch away or the screen locks. It does that by
holding an active audio session that plays silence, which is why it declares the
audio background mode.
This is a workaround, not a sanctioned mechanism. iOS offers no supported way for
an ordinary app to keep an inbound listener alive: BGTaskScheduler only grants
short opportunistic wake-ups, beginBackgroundTask only buys seconds to finish
work in flight, and Network Extension — the one API designed for networking that
outlives its app — exists to tunnel this device's traffic outward, not to serve
LAN peers connecting in. Apple's audio background mode is meant for apps whose
purpose is playing audio, and App Review rejects this use of it. That is one more
reason this app is not App Store material.
What it costs you: the app stays scheduled continuously, so it draws power the whole time it is running. Quit it when you are done. The audio session is configured to mix with others, so it never interrupts whatever you are actually listening to, and the samples are silent.
The main screen shows whether the keep-alive is actually in effect. If it reads
FOREGROUND ONLY, the app will stop serving as soon as it leaves the screen.
Android uses a user-started Foreground Service (connectedDevice) and an
ongoing notification (with Stop). Grant notification permission on Android 13+,
then Start — the shade entry should remain while LISTENING. See
docs/android/device-verification.md.
If a client half-closes its write side (shutdown(SHUT_WR)) while a large
response is still arriving, the tail of that response can be lost. The cause is
Network.framework: once a half-closed connection finishes closing, bytes still
sitting in the receive buffer are discarded, and the framework exposes no option
to drain them. BSD sockets do not behave this way, which has been confirmed on
device.
The iOS relay never reports this as success — a truncated stream is aborted with a reset rather than a clean end-of-stream, so the client sees a connection error instead of a short file that looks complete. Ordinary HTTP clients and browsers do not half-close mid-response and are not affected.
The Android relay uses blocking Java sockets with shutdownOutput() and does
not share the Network.framework half-close truncation. Listen addresses are
restricted to personal-hotspot / SoftAP interfaces when possible — enable the
phone hotspot before Start (station Wi‑Fi alone is not offered for bind).
- The client and the phone must be able to reach each other: same Wi-Fi network, personal hotspot, or USB
- Not suitable for App Store distribution
Bench/socks_bench.pyis the protocol and throughput harness.--mode self-testruns everything against a local proxy with no device involved.- The
Benchmarkbuild configuration links a vendored hev-socks5-server build, used only to compare engines. The shipping app does not include it. Licences for the vendored code are inThirdPartyNotices/. - The Benchmark configuration does not build on a clean checkout.
Vendor/is gitignored and no XCFramework is committed, so runscripts/build-hev-xcframework.shfirst or the link step fails. Debug and Release need nothing extra — they exclude the bridge entirely. - Engine / binary PRs: shipping iOS stays on the native Swift SOCKS core;
Android on the Kotlin path under
android/. PRs that swap the production engine for a vendored binary need a short design note, a reproducible build from source (or a documented rebuild script + pinned upstream), and must keep alternate engines out of the default app target unless explicitly opted in — Benchmark/hev is the existing comparison path.
This project is licensed under MIT License - see LICENSE file
This project is a fork of nneonneo/socks5-ios. Special thanks to Robert Xiao (nneonneo) for the original implementation, which was based on rofl0r/microsocks. The SOCKS5 core has since been rewritten in Swift and no longer contains microsocks.
跑在 iPhone 或 Android 手機上的 SOCKS5 代理,讓同一網路裡的其他裝置可以透過它連線。
用筆電連手機的個人熱點時,筆電送出的封包是被手機轉發出去的:它們抵達電信商時 帶著筆電自己的 TCP/IP 指紋,而且 TTL 比手機自己發的流量少一跳。分享偵測看的就是 這個差異。
這個代理把差異消掉。筆電對手機講 SOCKS5,由手機自己對目的地開連線——離開基地台的 封包因此是手機自己的 IP stack 產生的,看起來就是手機自身的流量。
這只在手機自己的熱點上才有意義。 讓客戶端透過一般 Wi-Fi 連這個代理沒有任何作 用——那台客戶端本來就有該網路的上行——而且會把一個沒有密碼的閘道留在你不掌控的網 段上。
這個代理接受 NO AUTH 連線。任何能連到你手機位址與連接埠的人都能把它當 開放中繼使用——包括咖啡廳 Wi-Fi、旅館區網,或是把你的裝置放進同一個扁平網路 的 VPN 上的任何東西。只在你自己掌控的網路上使用,用完就把 app 關掉。app 內 也有同樣的警告,且無法關閉。
- SOCKS5:NO AUTH、
CONNECT、UDP ASSOCIATE - 位址型別:IPv4、IPv6、網域名稱(ATYP
0x01/0x04/0x03) - 流量監控:即時上傳/下載速率、累計流量、TCP/UDP 連線數、近期活動記錄
- 可連位址:列出所有客戶端可連的介面位址,熱點優先,切換網路時即時更新
- iOS 背景:靜音音訊保活(非 App Store 路徑)——僅 iOS
- Android 背景:前景服務 + 持續通知;上游強制綁門號 cellular
- iOS 17.0 以上
- Xcode 與一個簽署帳號(免費 Apple ID 即可)
- Android 8.0+;需要門號數據與個人熱點能力
從 Releases 下載最新 app-debug.apk
(例如 v0.1.0),在手機上允許未知來源後安裝。
這是未簽名 debug 包,僅供側載測試。開啟個人熱點 → app 內 Start → 客戶端 SOCKS5 指向顯示的 IP:9876。
- Clone 專案:
git clone https://github.com/Nanako0129/SocksBypass.git- 開啟 Xcode 專案:
- 打開
SocksBypass.xcodeproj - 選擇你的開發者帳號進行簽署
- 修改 Bundle Identifier 為你自己的識別碼
- 部署到裝置:
- 將 iOS 裝置連接到電腦
- 在 Xcode 中選擇你的裝置
- 執行
Android 原始碼建置:cd android && ./gradlew :app:assembleDebug,再
adb install -r app/build/outputs/apk/debug/app-debug.apk。
- 啟動應用程式
- 等待顯示 IP 位址和連接埠(預設為 9876)
- 在需要使用代理的裝置上設定 SOCKS5:
- 代理伺服器:顯示的 IP 位址
- 連接埠:顯示的連接埠
- 不需要認證
如果想用 USB 而非 Wi-Fi,可以使用 danielpaulus/go-ios
的轉發模式:ios forward 1080 9876,用戶端指向 127.0.0.1。
切換到其他 app 或鎖屏之後,代理仍會繼續服務。做法是持續持有一個播放靜音的音訊
工作階段,這也是它宣告 audio 背景模式的原因。
這是變通手法,不是被認可的機制。iOS 沒有提供任何受支援的途徑讓一般 app 維持入站
監聽:BGTaskScheduler 只給系統排定的短暫喚醒,beginBackgroundTask 只換到幾秒
用來收尾,而 Network Extension——唯一為「網路行為活過 app」設計的 API——是用來把
本機流量導出去的,不是對區網提供入站服務。Apple 的 audio 背景模式本意是給功能
本身就是播放音訊的 app,App Review 會擋下這種用法。這也是本專案不適合上架的原因
之一。
代價:app 會持續被排程,執行期間一直耗電,用完請關掉。音訊工作階段設定為與其他 來源混音,所以不會打斷你正在聽的東西,取樣本身也是靜音。
主畫面會顯示保活是否真的生效。如果顯示 FOREGROUND ONLY,代表 app 一離開畫面就
會停止服務。
Android 使用使用者啟動的前景服務(connectedDevice)與持續通知(含 Stop)。
Android 13+ 請允許通知權限後再 Start;詳見
docs/android/device-verification.md。
如果用戶端在大型回應還在傳輸時關閉了自己的寫入端(shutdown(SHUT_WR)),該回應
的尾端可能遺失。原因在 Network.framework:半關閉的連線一旦走完關閉流程,還留在
接收緩衝區裡的資料就會被丟棄,而框架沒有提供排空的選項。BSD socket 沒有這個行為,
這一點已在裝置上驗證。
relay 不會把這種情況當成成功——被截斷的串流會以 reset 中止,而不是送出乾淨的 結束訊號,所以用戶端看到的是連線錯誤,而不是一個看起來完整的短檔案。一般的 HTTP 用戶端與瀏覽器不會在回應中途半關閉,不受影響。
- 用戶端與手機必須能互相連通:同一個 Wi-Fi 網路、個人熱點,或 USB
- 不適合透過 App Store 發布
Bench/socks_bench.py是協定與吞吐量測試工具。--mode self-test完全在本機 對一個本地代理執行,不需要裝置。Benchmark建置組態會連結一份 vendored 的 hev-socks5-server,僅用於引擎 比較,正式 app 不包含它。vendored 程式碼的授權條款放在ThirdPartyNotices/。- 引擎 / 預編譯 binary PR: 正式 iOS 維持 Swift SOCKS 核心,Android 維持
android/Kotlin 路徑。若要以 vendored binary 替換 production 引擎,需附簡短 設計說明、可重現的原始碼建置(或文件化 rebuild script + 鎖定上游),且預設 不要掛進 shipping target(比較引擎請走既有 Benchmark/hev 路徑)。
此專案使用 MIT 授權條款 - 詳見 LICENSE 檔案
本專案修改自 nneonneo/socks5-ios,特別 感謝 Robert Xiao (nneonneo) 開發的原始版本,該版本基於 rofl0r/microsocks。SOCKS5 核心其後已改以 Swift 重寫,不再包含 microsocks。
