一枚酸心果子

果子果子果子果子果子~~~

iOS代理检测与绕过技术:与Android的对比

iOS vs Android:检测机制对比

系统架构差异

iOS系统特点

  • 沙盒机制:应用运行在严格的沙盒环境中
  • 系统API限制:网络配置API相对封闭
  • 证书管理:系统级证书固定和验证
  • 越狱检测:多重越狱状态检测机制 Android系统特点
  • 开放生态:更多系统级API可访问
  • 权限模型:基于权限的网络访问控制
  • Root检测:相对简单的Root状态检测
  • 系统属性:可直接访问系统配置

检测点对比表

检测维度 iOS检测方法 Android检测方法 绕过难度
系统代理 CFNetwork代理设置、NSURLSession配置 ConnectivityManager、Proxy.getDefaultHost() iOS更难
VPN检测 NWPathMonitor、Network.framework NetworkCapabilities.TRANSPORT_VPN iOS更难
证书检测 SecTrustEvaluate、SSL Pinning X509TrustManager、Certificate Pinning 相当
网络接口 getifaddrs、ifconfig NetworkInterface、ifconfig iOS更难
抓包检测 证书链验证、SNI检测 证书验证、代理头检测 iOS更难
越狱检测 文件系统、API调用、沙盒检查 Root文件、su命令、系统属性 iOS更难

iOS代理检测原理深度解析

1. 系统代理检测

CFNetwork代理检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import Foundation
import Network

class ProxyDetector {
// 检测系统HTTP代理设置
func detectSystemProxy() -> Bool {
let proxySettings = CFNetworkCopySystemProxySettings()
let proxyDict = proxySettings?.takeRetainedValue() as? [String: Any]

if let httpProxy = proxyDict?["HTTPProxy"] as? String,
!httpProxy.isEmpty {
print("检测到HTTP代理: \(httpProxy)")
return true
}

if let httpsProxy = proxyDict?["HTTPSProxy"] as? String,
!httpsProxy.isEmpty {
print("检测到HTTPS代理: \(httpsProxy)")
return true
}

return false
}

// 检测SOCKS代理
func detectSOCKSProxy() -> Bool {
let proxySettings = CFNetworkCopySystemProxySettings()
let proxyDict = proxySettings?.takeRetainedValue() as? [String: Any]

if let socksProxy = proxyDict?["SOCKSProxy"] as? String,
!socksProxy.isEmpty {
print("检测到SOCKS代理: \(socksProxy)")
return true
}

return false
}
}

NSURLSession代理检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class URLSessionProxyDetector {
func detectProxyInURLSession() -> Bool {
let config = URLSessionConfiguration.default

// 检查代理配置
if let proxyDict = config.connectionProxyDictionary {
print("检测到URLSession代理配置: \(proxyDict)")
return true
}

// 检查是否使用了自定义代理
if config.httpProxy != nil || config.httpsProxy != nil {
print("检测到HTTP/HTTPS代理")
return true
}

return false
}
}

2. VPN和隧道检测

Network.framework检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import Network

class VPNDetector {
private let monitor = NWPathMonitor()

func detectVPN() -> Bool {
var isVPNConnected = false

monitor.pathUpdateHandler = { path in
// 检查网络接口类型
for interface in path.availableInterfaces {
if interface.type == .wifi || interface.type == .cellular {
// 检查是否有VPN接口
if self.hasVPNInterface() {
isVPNConnected = true
print("检测到VPN连接")
}
}
}
}

let queue = DispatchQueue(label: "VPNMonitor")
monitor.start(queue: queue)

return isVPNConnected
}

private func hasVPNInterface() -> Bool {
var ifaddr: UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return false }

var current = ifaddr
while current != nil {
let interface = current!.pointee
let name = String(cString: interface.ifa_name)

// 检查常见的VPN接口名称
if name.hasPrefix("utun") || name.hasPrefix("ipsec") ||
name.hasPrefix("ppp") || name.hasPrefix("tun") {
freeifaddrs(ifaddr)
return true
}

current = interface.ifa_next
}

freeifaddrs(ifaddr)
return false
}
}

3. 证书和抓包检测

SSL Pinning检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Security

class CertificateDetector {
func detectCertificatePinning() -> Bool {
// 检查是否有自定义的证书验证逻辑
let session = URLSession(configuration: .default)

// 通过反射检查是否有自定义的URLSessionDelegate
let mirror = Mirror(reflecting: session)
for child in mirror.children {
if child.label == "delegate" && child.value != nil {
print("检测到自定义URLSessionDelegate,可能存在证书固定")
return true
}
}

return false
}

// 检测证书链异常
func detectCertificateChainAnomaly() -> Bool {
// 这里可以检查证书链中的中间证书
// 抓包工具通常会插入自己的CA证书
return false
}
}

SNI检测

1
2
3
4
5
6
7
class SNIDetector {
func detectSNIManipulation() -> Bool {
// 检查SNI字段是否被修改
// 抓包工具可能会修改SNI字段
return false
}
}

4. 网络接口检测

网络接口分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import Foundation

class NetworkInterfaceDetector {
func analyzeNetworkInterfaces() -> [String: Any] {
var interfaces: [String: Any] = [:]
var ifaddr: UnsafeMutablePointer<ifaddrs>?

guard getifaddrs(&ifaddr) == 0 else {
return interfaces
}

var current = ifaddr
while current != nil {
let interface = current!.pointee
let name = String(cString: interface.ifa_name)

if interface.ifa_addr != nil {
let family = interface.ifa_addr.pointee.sa_family

if family == UInt8(AF_INET) || family == UInt8(AF_INET6) {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, 0, NI_NUMERICHOST)

let address = String(cString: hostname)
interfaces[name] = [
"address": address,
"family": family,
"flags": interface.ifa_flags
]
}
}

current = interface.ifa_next
}

freeifaddrs(ifaddr)
return interfaces
}
}

Frida Hook绕过技术

1. Hook CFNetwork代理检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Hook CFNetwork代理设置获取
function hookCFNetworkProxy() {
// Hook CFNetworkCopySystemProxySettings
var CFNetworkCopySystemProxySettings = Module.findExportByName("CFNetwork", "CFNetworkCopySystemProxySettings");
if (CFNetworkCopySystemProxySettings) {
Interceptor.attach(CFNetworkCopySystemProxySettings, {
onEnter: function(args) {
console.log("[+] CFNetworkCopySystemProxySettings called");
},
onLeave: function(retval) {
// 返回空的代理设置
console.log("[+] Returning empty proxy settings");
retval.replace(ptr(0));
}
});
}

// Hook CFNetworkCopyProxiesForURL
var CFNetworkCopyProxiesForURL = Module.findExportByName("CFNetwork", "CFNetworkCopyProxiesForURL");
if (CFNetworkCopyProxiesForURL) {
Interceptor.attach(CFNetworkCopyProxiesForURL, {
onEnter: function(args) {
console.log("[+] CFNetworkCopyProxiesForURL called");
},
onLeave: function(retval) {
// 返回空数组
console.log("[+] Returning empty proxy array");
retval.replace(ptr(0));
}
});
}
}

2. Hook Network.framework

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Hook NWPathMonitor
function hookNWPathMonitor() {
// Hook NWPathMonitor的pathUpdateHandler
var NWPathMonitor = ObjC.classes.NWPathMonitor;
if (NWPathMonitor) {
var originalSetPathUpdateHandler = NWPathMonitor['- setPathUpdateHandler:'];
Interceptor.attach(originalSetPathUpdateHandler.implementation, {
onEnter: function(args) {
console.log("[+] NWPathMonitor setPathUpdateHandler called");
},
onLeave: function(retval) {
// 可以在这里修改pathUpdateHandler的行为
}
});
}
}

3. Hook证书验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Hook SecTrustEvaluate
function hookSecTrustEvaluate() {
var SecTrustEvaluate = Module.findExportByName("Security", "SecTrustEvaluate");
if (SecTrustEvaluate) {
Interceptor.attach(SecTrustEvaluate, {
onEnter: function(args) {
console.log("[+] SecTrustEvaluate called");
},
onLeave: function(retval) {
// 强制返回成功
console.log("[+] Forcing SecTrustEvaluate to return success");
retval.replace(ptr(0)); // errSecSuccess
}
});
}

// Hook SecTrustEvaluateWithError (iOS 12+)
var SecTrustEvaluateWithError = Module.findExportByName("Security", "SecTrustEvaluateWithError");
if (SecTrustEvaluateWithError) {
Interceptor.attach(SecTrustEvaluateWithError, {
onEnter: function(args) {
console.log("[+] SecTrustEvaluateWithError called");
},
onLeave: function(retval) {
// 强制返回true
console.log("[+] Forcing SecTrustEvaluateWithError to return true");
retval.replace(ptr(1));
}
});
}
}

4. Hook网络接口检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Hook getifaddrs
function hookGetifaddrs() {
var getifaddrs = Module.findExportByName("libc", "getifaddrs");
if (getifaddrs) {
Interceptor.attach(getifaddrs, {
onEnter: function(args) {
console.log("[+] getifaddrs called");
},
onLeave: function(retval) {
// 可以在这里修改返回的网络接口信息
console.log("[+] getifaddrs returned: " + retval);
}
});
}
}

越狱环境下的高级绕过

1. 使用Substrate Hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 使用Substrate Hook CFNetwork
%hook NSURLSessionConfiguration

- (NSDictionary *)connectionProxyDictionary {
NSLog(@"[+] NSURLSessionConfiguration connectionProxyDictionary called");
// 返回空字典,隐藏代理设置
return @{};
}

%end

%hook NSURLSession

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler {
NSLog(@"[+] NSURLSession dataTaskWithRequest called");
return %orig;
}

%end

2. 使用Theos开发Tweak

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Tweak.x
%hook NSURLSessionConfiguration

- (NSDictionary *)connectionProxyDictionary {
// 隐藏代理配置
return nil;
}

%end

%hook NWPathMonitor

- (void)setPathUpdateHandler:(void (^)(NWPath *path))handler {
// 修改pathUpdateHandler,隐藏VPN检测
%orig;
}

%end

3. 系统级修改

1
2
3
4
5
6
7
8
# 修改系统代理设置文件
# /var/preferences/SystemConfiguration/preferences.plist

# 使用plutil修改代理设置
plutil -p /var/preferences/SystemConfiguration/preferences.plist

# 或者直接删除代理相关配置
rm -f /var/preferences/SystemConfiguration/preferences.plist

非越狱环境下的绕过策略

1. 使用Shadowrocket等工具

Shadowrocket配置

1
2
3
4
5
6
7
8
9
10
11
[General]
# 启用增强模式
enhanced-mode = true
# 隐藏VPN标识
hide-vpn-icon = true
# 使用系统代理
use-system-proxy = true

[Rule]
# 绕过检测规则
DOMAIN-SUFFIX,example.com,DIRECT

2. 使用Quantumult X

Quantumult X配置

1
2
3
4
5
6
7
8
9
[general]
# 启用增强模式
enhanced-mode = true
# 隐藏VPN标识
hide-vpn-icon = true

[policy]
# 绕过策略
static=bypass, direct, img-url=https://example.com/icon.png

3. 使用Surge

Surge配置

1
2
3
4
5
6
7
8
9
[General]
# 启用增强模式
enhanced-mode = true
# 隐藏VPN标识
hide-vpn-icon = true

[Rule]
# 绕过检测
DOMAIN-SUFFIX,example.com,DIRECT

结语

iOS代理检测与绕过技术相比Android更加复杂,需要更深入的系统知识和更高级的技术手段。理解检测原理,掌握绕过技术,是移动安全研究的重要技能。

本文关键点

  • 系统差异:iOS的封闭生态使得检测更加隐蔽,绕过更加困难
  • 技术演进:从简单Hook到系统级修改,技术手段不断升级
持续输出技术分享,您的支持将鼓励我继续创作!