目标
实现对App蜂窝数据开关控制。
环境:
iOS 9.1 arm64
前言
对于这方面的分析资料可能比较少,我把自己的一些分析过程和踩过的一些坑记录下来,花了几天时间调试和整理,分享在此。
开始
我们来查找地图开关,先用FLEX查看switch是属于哪个cell
可以看到他是属于PSSubtitleSwitchTableCell,接下来我们ssh手机使用Cycript挂载。
然后查看Preferences界面结构
cy# [[UIApp keyWindow] _autolayoutTrace].toString()
`
UIWindow:0x1366c8060
| UILayoutContainerView:0x1366ec6c0
| | UIView:0x136759130
| | UILayoutContainerView:0x1365f0110
| | | UINavigationTransitionView:0x1365f7c30
| | | | UIViewControllerWrapperView:0x13676bba0
| | | | | UILayoutContainerView:0x1367abcf0
| | | | | | UINavigationTransitionView:0x13783e820
| | | | | | | UIViewControllerWrapperView:0x13799b0a0
| | | | | | | | PSListContainerView:0x137987af0
| | | | | | | | | UITableView:0x136892000
| | | | | | | | | | UITableViewWrapperView:0x136805600
| | | | | | | | | | | PSSubtitleSwitchTableCell:0x13700de00'PSSubtitleSwitchTableCell...'
| | | | | | | | | | | | UITableViewCellContentView:0x1379f6480
| | | | | | | | | | | | | UITableViewLabel:0x137daa030'\u5fae\u4fe1'
| | | | | | | | | | | | | UITableViewLabel:0x137d5a070'19.8 MB'
| | | | | | | | | | | | | UIImageView:0x137b66940
| | | | | | | | | | | | _UITableViewCellSeparatorView:0x1378bc320
| | | | | | | | | | | | UISwitch:0x1378b5750
| | | | | | | | | | | | | _UISwitchInternalViewNeueStyle1:0x13789de80
| | | | | | | | | | | | | | UIView:0x137d84960
| | | | | | | | | | | | | | | UIView:0x137d67b10
| | | | | | | | | | | | | | UIView:0x137d895c0
| | | | | | | | | | | | | | | UIView:0x137a357a0
| | | | | | | | | | | | | | UIView:0x137b22360
| | | | | | | | | | | | | | | UIImageView:0x1379995d0
| | | | | | | | | | | | | | | UIImageView:0x1379ce5d0
| | | | | | | | | | | | | | UIImageView:0x1378bafc0
| | | | | | | | | | | PSSubtitleSwitchTableCell:0x13698f400'PSSubtitleSwitchTableCell...'
| | | | | | | | | | | | UITableViewCellContentView:0x137d87f00
| | | | | | | | | | | | | UITableViewLabel:0x137dbd670'\u901a\u8baf\u5f55'
| | | | | | | | | | | | | UIImageView:0x1379e33a0
| | | | | | | | | | | | _UITableViewCellSeparatorView:0x1378812b0
| | | | | | | | | | | | UISwitch:0x137a07b40
| | | | | | | | | | | | | _UISwitchInternalViewNeueStyle1:0x137a365b0
| | | | | | | | | | | | | | UIView:0x137d888c0
| | | | | | | | | | | | | | | UIView:0x137d84fe0
| | | | | | | | | | | | | | UIView:0x137da4280
| | | | | | | | | | | | | | | UIView:0x13665a400
| | | | | | | | | | | | | | UIView:0x137b2e140
| | | | | | | | | | | | | | | UIImageView:0x137999230
| | | | | | | | | | | | | | | UIImageView:0x1379854c0
| | | | | | | | | | | | | | UIImageView:0x1378f46f0
| | | | | | | | | | | PSSubtitleSwitchTableCell:0x1371ac400'PSSubtitleSwitchTableCell...'
| | | | | | | | | | | | UITableViewCellContentView:0x1379bd1a0
| | | | | | | | | | | | | UITableViewLabel:0x137db1ee0'\u5929\u6c14'
| | | | | | | | | | | | | UIImageView:0x137b36020
| | | | | | | | | | | | _UITableViewCellSeparatorView:0x1379e1590
| | | | | | | | | | | | UISwitch:0x1378bdef0
| | | | | | | | | | | | | _UISwitchInternalViewNeueStyle1:0x1378e9570
| | | | | | | | | | | | | | UIView:0x137d87d60
| | | | | | | | | | | | | | | UIView:0x137a24e00
| | | | | | | | | | | | | | UIView:0x137db1a50
| | | | | | | | | | | | | | | UIView:0x137d678d0
| | | | | | | | | | | | | | UIView:0x1365a3740
| | | | | | | | | | | | | | | UIImageView:0x137b22ec0
| | | | | | | | | | | | | | | UIImageView:0x1379cdfe0
| | | | | | | | | | | | | | UIImageView:0x1378a64f0
我们随便选取一个UISwitch地址定位其action selector:
cy# [#0x137a07b40 allTargets]
[NSSet setWithArray:@[#">"]]]
cy# [#0x137a07b40 actionsForTarget:#0x13698f400 forControlEvent:[#0x137a07b40 allControlEvents]]
@["controlChanged:"]
cy#
1 先用Cycript打印出[button allTargets],是一个数组不过我们这里只有一个对象
2 遍历数组,得到每一个target,然后打印
[button actionsForTarget:target forControlEvent:[button allControlEvents]]
得到selector。
controlChanged:就是Switch点击事件调用的函数
下面我们来寻找它干了什么
首先我们需要查找PSSubtitleSwitchTableCell.h头文件,如果你没有系统头文件点这里。
/*
* This header is generated by classdump-dyld 0.7
* on Thursday, January 14, 2016 at 3:32:07 AM Eastern European Standard Time
* Operating System: Version 9.0.2 (Build 13A452)
* Image Source: /System/Library/PrivateFrameworks/Preferences.framework/Preferences
* classdump-dyld is licensed under GPLv3, Copyright © 2013-2014 by Elias Limneos.
*/
#import
@interface PSSubtitleSwitchTableCell : PSSwitchTableCell
+(long long)cellStyle;
-(void)refreshCellContentsWithSpecifier:(id)arg1 ;
-(BOOL)canReload;
@end
我们可以看到它是属于Preferences.framework
/System/Library/PrivateFrameworks/Preferences.framework/Preferences
继承关系:
PSSubtitleSwitchTableCell
PSSwitchTableCell
PSControlTableCell
PSTableCell
UITableViewCell
开启lldb
一、SSH连接手机(USB模式)
1.映射端口
2.连接手机,并且用grep命令快速筛选当前我们要调试的应用Preferences,附加debugserver开始12345端口等待lldb调试
3.完成以上两步接下来就可以进行lldb调试了,首先要把远端(手机)的12345端口映射到本地,跟前面提到的SSH端口映射一样
下面我们需要借助lldb找到/Preferences.framework/Preferences
(lldb) image list -o -f
[ 0] 0x0000000000084000 /var/db/stash/_.E79Odv/Applications/Preferences.app/Preferences(0x0000000100084000)
[ 1] 0x00000001000bc000 /Library/MobileSubstrate/MobileSubstrate.dylib(0x00000001000bc000)
[ 2] 0x0000000003e18000 /Users/qweqwe/Library/Developer/Xcode/iOS DeviceSupport/9.1 (13B143)/Symbols/System/Library/PrivateFrameworks/BulletinBoard.framework/BulletinBoard
`
`
`
[ 44] 0x0000000003e18000 /Users/qweqwe/Library/Developer/Xcode/iOS DeviceSupport/9.1 (13B143)/Symbols/System/Library/PrivateFrameworks/Preferences.framework/Preferences
看到[44]号地址就是我们要找的Preferences.framework二进制文件
拉进IDA进行查找controlChanged
使用lldb动态调试
我们对其函数头部下断点
(lldb) br s -a 0x0000000003e18000+0x000000019087F7DC
Breakpoint 3: where = Preferences`-[PSControlTableCell controlChanged:], address = 0x00000001946977dc
(lldb)
然后去设置里点一下Switch,我们看到断点命中。
Process 1576 stopped
* thread #1: tid = 0x166aa, 0x00000001946977dc Preferences`-[PSControlTableCell controlChanged:], queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
frame #0: 0x00000001946977dc Preferences`-[PSControlTableCell controlChanged:]
Preferences`-[PSControlTableCell controlChanged:]:
-> 0x1946977dc <+0>: stp x24, x23, [sp, #-64]!
0x1946977e0 <+4>: stp x22, x21, [sp, #16]
0x1946977e4 <+8>: stp x20, x19, [sp, #32]
0x1946977e8 <+12>: stp x29, x30, [sp, #48]
(lldb)
使用命令n单步来到objc_msgSend函数
(lldb) dis
Preferences`-[PSControlTableCell controlChanged:]:
0x1946977dc <+0>: stp x24, x23, [sp, #-64]!
0x1946977e0 <+4>: stp x22, x21, [sp, #16]
0x1946977e4 <+8>: stp x20, x19, [sp, #32]
0x1946977e8 <+12>: stp x29, x30, [sp, #48]
0x1946977ec <+16>: add x29, sp, #48 ; =48
0x1946977f0 <+20>: mov x19, x0
0x1946977f4 <+24>: adrp x8, 53838
0x1946977f8 <+28>: ldr x21, [x8, #2680]
0x1946977fc <+32>: mov x1, x21
-> 0x194697800 <+36>: bl 0x19aa51bc0 ; objc_msgSend
0x194697804 <+40>: mov x29, x29
0x194697808 <+44>: bl 0x19aa59ef0 ; objc_retainAutoreleasedReturnValue
0x19469780c <+48>: mov x22, x0
0x194697810 <+52>: cbz x22, 0x1946978ec ; <+272>
(lldb) po $x0
>
(lldb) po (char*)$x1
"cellTarget"
(lldb)
对应OC代码
[PSSubtitleSwitchTableCell cellTarget]
objc_retainAutoreleasedReturnValue作用是处理返回值是否跳过autorelease机制
0x194697804 <+40>: mov x29, x29
0x194697808 <+44>: bl 0x19aa59ef0 ; objc_retainAutoreleasedReturnValue
单步来到 0x194697810 <+52>: cbz x22, 0x1946978ec ; <+272>
x0返回值给了x22,cbz意思是为0则跳,打印x22
0x19469780c <+48>: mov x22, x0
0x194697810 <+52>: cbz x22, 0x1946978ec ; <+272>
(lldb) po $x22
因为我们x22有值所以没跳,还原OC代码
PSUIAppCellularUsageGroupController *vc = [PSSubtitleSwitchTableCell cellTarget];
if(vc){
‘’‘’‘
}
然后我们单步来到0x194697824
0x194697810 <+52>: cbz x22, 0x1946978ec ; <+272>
0x194697814 <+56>: adrp x8, 53837
0x194697818 <+60>: ldr x20, [x8, #1168]
0x19469781c <+64>: mov x0, x19
0x194697820 <+68>: mov x1, x20
-> 0x194697824 <+72>: bl 0x19aa51bc0 ; objc_msgSend
(lldb) po $x0
>
(lldb) po (char*)$x1
"specifier"
(lldb)
继续单步查看返回值
0x194697814 <+56>: adrp x8, 53837
0x194697818 <+60>: ldr x20, [x8, #1168]
0x19469781c <+64>: mov x0, x19
0x194697820 <+68>: mov x1, x20
0x194697824 <+72>: bl 0x19aa51bc0 ; objc_msgSend
0x194697828 <+76>: mov x29, x29
0x19469782c <+80>: bl 0x19aa59ef0 ; objc_retainAutoreleasedReturnValue
-> 0x194697830 <+84>: mov x23, x0
(lldb) po $x0
>
发现是PSSpecifier,我们找到之前的类的头文件,发现这个类有一个叫做properties的实例方法
(lldb) po [$x0 properties]
{
appIDForLazyIcon = "com.apple.Maps";
cellClass = PSSubtitleSwitchTableCell;
cellObject = ">";
cellSubtitleText = "5.7 MB";
control = ">";
enabled = 1;
id = "com.apple.Maps";
roamingNetworkUsage = 0;
totalNetworkUsage = 5981087;
useLazyIcons = 1;
}
可以看到这个类包含了AppBundID和UISwitch一些信息
继续走
0x194697834 <+88>: bl 0x19aa58150 ; objc_release
0x194697838 <+92>: mov x0, x22
0x19469783c <+96>: bl 0x19aa58150 ; objc_release
0x194697840 <+100>: cbz x23, 0x1946978ec ; <+272>
来到这0x194697840
还原OC代码
PSSpecifier *specifier = [PSSubtitleSwitchTableCell specifier];
if(specifier){
}
0x194697844 <+104>: adrp x8, 53838
0x194697848 <+108>: ldr x1, [x8, #2688]
0x19469784c <+112>: mov x0, x19
0x194697850 <+116>: bl 0x19aa51bc0 ; objc_msgSend
0x194697854 <+120>: mov x22, x0
0x194697858 <+124>: mov x0, x19
0x19469785c <+128>: mov x1, x21
0x194697860 <+132>: bl 0x19aa51bc0 ; objc_msgSend
0x194697864 <+136>: mov x29, x29
0x194697868 <+140>: bl 0x19aa59ef0 ; objc_retainAutoreleasedReturnValue
0x19469786c <+144>: mov x21, x0
0x194697870 <+148>: adrp x8, 53840
0x194697874 <+152>: ldr x1, [x8, #2376]
0x194697878 <+156>: mov x0, x19
0x19469787c <+160>: bl 0x19aa51bc0 ; objc_msgSend
0x194697880 <+164>: mov x29, x29
0x194697884 <+168>: bl 0x19aa59ef0 ; objc_retainAutoreleasedReturnValue
0x194697888 <+172>: mov x23, x0
0x19469788c <+176>: mov x0, x19
0x194697890 <+180>: mov x1, x20
0x194697894 <+184>: bl 0x19aa51bc0 ; objc_msgSend
0x194697898 <+188>: mov x29, x29
0x19469789c <+192>: bl 0x19aa59ef0 ; objc_retainAutoreleasedReturnValue
这段汇编主要为取值为了节省时间读者可自行分析。
对应OC代码
SEL setAppCellularData = [PSSubtitleSwitchTableCell cellAction];
PSUIAppCellularUsageGroupController *vc = [PSSubtitleSwitchTableCell cellTarget];
NSinteger controlValue = [PSSubtitleSwitchTableCell controlValue];
0x1946978a0 <+196>: mov x19, x0
0x1946978a4 <+200>: mov x0, x22
0x1946978a8 <+204>: mov x1, x21
0x1946978ac <+208>: mov x2, x23
0x1946978b0 <+212>: mov x3, x19
-> 0x1946978b4 <+216>: bl 0x1946bb2b4 ; PSPerformSelector2
(lldb) po (char*)$x0
"setAppCellularDataEnabled:forSpecifier:"
(lldb) po $x1
(lldb) po $x2
0
(lldb) po $x3
>
(lldb)
PSPerformSelector2(setAppCellularData,vc, controlValue, specifier);
等价于
[PSUIAppCellularUsageGroupController setAppCellularDataEnabled:NO forSpecifier: specifier];
完整OC
PSUIAppCellularUsageGroupController *vc = [PSSubtitleSwitchTableCell cellTarget];
if(vc){
PSSpecifier *specifier = [PSSubtitleSwitchTableCell specifier];
if(specifier){
SEL setAppCellularData = [PSSubtitleSwitchTableCell cellAction];
PSUIAppCellularUsageGroupController *vc = [PSSubtitleSwitchTableCell cellTarget];
NSinteger controlValue = [PSSubtitleSwitchTableCell controlValue];
PSPerformSelector2(setAppCellularData,vc, controlValue, specifier);
}
}
setAppCellularDataEnabled看这个函数名字已经能确定了,也可以用Cycript试验一下,我这里就不试了。
到这里我们已经找到了设置开关的函数,但是这是UI层面上的,我们还需要继续深入寻找底层函数。
继续上边的套路查找PSUIAppCellularUsageGroupController头文件
/*
* This header is generated by classdump-dyld 0.7
* on Thursday, January 14, 2016 at 3:32:09 AM Eastern European Standard Time
* Operating System: Version 9.0.2 (Build 13A452)
* Image Source: /System/Library/PrivateFrameworks/PreferencesUI.framework/PreferencesUI
* classdump-dyld is licensed under GPLv3, Copyright © 2013-2014 by Elias Limneos.
*/
#import
#import
@class NSArray, NSNumber, PSExpandableAppListGroupController, NSDictionary, NSString;
@interface PSUIAppCellularUsageGroupController : NSObject {
我们看到他是定义在PreferencesUI.framework
/System/Library/PrivateFrameworks/PreferencesUI.framework/PreferencesUI
重复上边lldb寻找过程拖进IDA搜索setAppCellularDataEnabled:forSpecifier:
在函数头部下断点,并且命中。
(lldb) br s -a 0x0000000003e18000+0x0000000190936274
Breakpoint 6: where = PreferencesUI`-[PSUIAppCellularUsageGroupController setAppCellularDataEnabled:forSpecifier:], address = 0x000000019474e274
Process 1576 stopped
* thread #1: tid = 0x166aa, 0x000000019474e274 PreferencesUI`-[PSUIAppCellularUsageGroupController setAppCellularDataEnabled:forSpecifier:], queue = 'com.apple.main-thread', stop reason = breakpoint 6.1
frame #0: 0x000000019474e274 PreferencesUI`-[PSUIAppCellularUsageGroupController setAppCellularDataEnabled:forSpecifier:]
PreferencesUI`-[PSUIAppCellularUsageGroupController setAppCellularDataEnabled:forSpecifier:]:
-> 0x19474e274 <+0>: stp x26, x25, [sp, #-80]!
0x19474e278 <+4>: stp x24, x23, [sp, #16]
0x19474e27c <+8>: stp x22, x21, [sp, #32]
0x19474e280 <+12>: stp x20, x19, [sp, #48]
(lldb)
单步往下走
0x19474e274 <+0>: stp x26, x25, [sp, #-80]!
0x19474e278 <+4>: stp x24, x23, [sp, #16]
0x19474e27c <+8>: stp x22, x21, [sp, #32]
0x19474e280 <+12>: stp x20, x19, [sp, #48]
0x19474e284 <+16>: stp x29, x30, [sp, #64]
0x19474e288 <+20>: add x29, sp, #64 ; =64
0x19474e28c <+24>: sub sp, sp, #48 ; =48
0x19474e290 <+28>: mov x20, x3
0x19474e294 <+32>: mov x22, x0
0x19474e298 <+36>: adrp x25, 46941
0x19474e29c <+40>: ldr x25, [x25, #1672]
0x19474e2a0 <+44>: ldr x25, [x25]
0x19474e2a4 <+48>: str x25, [sp, #40]
0x19474e2a8 <+52>: mov x0, x2
0x19474e2ac <+56>: bl 0x19aa580a0 ; objc_retain
0x19474e2b0 <+60>: mov x19, x0
0x19474e2b4 <+64>: mov x0, x20
-> 0x19474e2b8 <+68>: bl 0x19aa580a0 ; objc_retain
对其一些参数做retain暂不关心
0x19474e2bc <+72>: mov x20, x0
0x19474e2c0 <+76>: adrp x8, 53665
0x19474e2c4 <+80>: ldr x1, [x8, #760]
0x19474e2c8 <+84>: bl 0x19aa51bc0 ; objc_msgSend
(lldb) po $x0
>
(lldb) po (char*)$x1
"identifier"
(lldb)
这里获取PSSpecifier的identifier
0x19474e2cc <+88>: mov x29, x29
0x19474e2d0 <+92>: bl 0x19aa59ef0 ; objc_retainAutoreleasedReturnValue
0x19474e2d4 <+96>: mov x21, x0
(lldb) po $x0
com.apple.Maps
0x19474e2d8 <+100>: adrp x8, 46941
0x19474e2dc <+104>: ldr x8, [x8, #1736]
0x19474e2e0 <+108>: ldr x0, [x8]
0x19474e2e4 <+112>: movz x3, #0
0x19474e2e8 <+116>: adrp x1, 46948
0x19474e2ec <+120>: add x1, x1, #3536 ; =3536
0x19474e2f0 <+124>: adrp x2, 46941
0x19474e2f4 <+128>: ldr x2, [x2, #1680]
-> 0x19474e2f8 <+132>: bl 0x19320cbe4
(lldb) po $x0
(lldb) po $x1
com.apple.Preferences
(lldb) po $x2
(lldb) po $x3
我们s跟进去看看他是什么函数
(lldb) s
Process 1576 stopped
* thread #1: tid = 0x166aa, 0x000000019320cbe4, queue = 'com.apple.main-thread', stop reason = instruction step into
frame #0: 0x000000019320cbe4
-> 0x19320cbe4: b 0x18b4eedb4
0x19320cbe8: b 0x18b4efbf0
0x19320cbec: b 0x18b4ee68c
0x19320cbf0: b 0x18b4ee818
(lldb) s
Process 1576 stopped
* thread #1: tid = 0x166aa, 0x000000018b4eedb4, queue = 'com.apple.main-thread', stop reason = instruction step into
frame #0: 0x000000018b4eedb4
-> 0x18b4eedb4: b 0x186a1953c ; _CTServerConnectionCreateOnTargetQueue
0x18b4eedb8: b 0x186a21024 ; _CTServerConnectionDropIPPackets
0x18b4eedbc: b 0x186a115c4 ; _CTServerConnectionGetRadioAccessTechnology
0x18b4eedc0: b 0x186a11e54 ; _CTServerConnectionGetSignalStrength
(lldb)
_CTServerConnectionCreateOnTargetQueue私有的C函数
它有4个参数
0x19474e2fc <+136>: mov x23, x0
0x19474e300 <+140>: cbz x23, 0x19474e3b0 ; <+316>
0x19474e304 <+144>: adrp x8, 46941
0x19474e308 <+148>: ldr x8, [x8, #1904]
0x19474e30c <+152>: ldr x8, [x8]
0x19474e310 <+156>: stp x8, x19, [sp, #24]
0x19474e314 <+160>: adrp x8, 53668
0x19474e318 <+164>: ldr x0, [x8, #2624]
0x19474e31c <+168>: adrp x8, 53665
0x19474e320 <+172>: ldr x1, [x8, #3168]
0x19474e324 <+176>: add x2, sp, #32 ; =32
0x19474e328 <+180>: add x3, sp, #24 ; =24
0x19474e32c <+184>: orr w4, wzr, #0x1
-> 0x19474e330 <+188>: bl 0x19aa51bc0 ; objc_msgSend
0x19474e334 <+192>: mov x29, x29
0x19474e338 <+196>: bl 0x19aa59ef0 ; objc_retainAutoreleasedReturnValue
0x19474e33c <+200>: mov x24, x0
(lldb) po $x0
NSDictionary
(lldb) po (char*)$x1
"dictionaryWithObjects:forKeys:count:"
(lldb)
单步看其返回值是kCTCellularUsagePolicyDataAllowed
也就是构造一个字典,值是0和1
(lldb) po $x0
{
kCTCellularUsagePolicyDataAllowed = 0;
}
0x19456e340 <+204>: mov x0, x23
0x19456e344 <+208>: mov x1, x21
0x19456e348 <+212>: mov x2, x24
-> 0x19456e34c <+216>: bl 0x19302d3cc
(lldb) po $x0
(lldb) po $x1
com.apple.Maps
(lldb) po $x2
{
kCTCellularUsagePolicyDataAllowed = 0;
}
(lldb) s
Process 683 stopped
* thread #1: tid = 0x0d99, 0x000000019302d3cc, queue = 'com.apple.main-thread', stop reason = instruction step into
frame #0: 0x000000019302d3cc
-> 0x19302d3cc: b 0x18b30fde0
0x19302d3d0: b 0x18b30f1ac
0x19302d3d4: b 0x18b30ef70
0x19302d3d8: b 0x18b30f1c8
(lldb) s
Process 683 stopped
* thread #1: tid = 0x0d99, 0x000000018b30fde0, queue = 'com.apple.main-thread', stop reason = instruction step into
frame #0: 0x000000018b30fde0
-> 0x18b30fde0: b 0x18685544c ; _CTServerConnectionSetCellularUsagePolicy
0x18b30fde4: b 0x185dccb38 ; CFTimeZoneResetSystem
0x18b30fde8: b 0x1867fe9f4 ; CTCallDial
0x18b30fdec: b 0x1867ffd34 ; CTCallGetCallSubType
(lldb)'
_CTServerConnectionSetCellularUsagePolicy私有C函数
3个参数
x0是_CTServerConnectionCreateOnTargetQueue它的返回值
调用
用到的是CoreTelephony.framework里的两个私有C函数
CTServerConnection* _CTServerConnectionCreateOnTargetQueue(CFAllocatorRef, NSString *, dispatch_queue_t, void*/*一个block类型的参数*/)
void _CTServerConnectionSetCellularUsagePolicy(CTServerConnection *, NSString *, NSDictionary *)
要调用私有C函数,需要用dlsym,简单示意如下:
void *CoreTelephonyHandle = dlopen("/System/Library/Frameworks/CoreTelephony.framework/CoreTelephony", RTLD_LAZY);
//用函数指针来调用私有C函数,用符号名从库里寻找函数地址
CFTypeRef (*connectionCreateOnTargetQueue)(CFAllocatorRef, NSString *, dispatch_queue_t, void*) = dlsym(CoreTelephonyHandle, "_CTServerConnectionCreateOnTargetQueue");
int (*changeCellularPolicy)(CFTypeRef, NSString *, NSDictionary *) = dlsym(CoreTelephonyHandle, "_CTServerConnectionSetCellularUsagePolicy");
CFTypeRef connection = connectionCreateOnTargetQueue(kCFAllocatorDefault,@"com.apple.Preferences",dispatch_get_main_queue(),NULL);
changeCellularPolicy(connection, @"需要授权的app的bundle id", @{@"kCTCellularUsagePolicyDataAllowed":@YES});
dlclose(CoreTelephonyHandle);
权限
CommCenter就是这几个私有API通信的对应进程,用于管理设备的网络,如果想在其他进程与CommCenter通信改写数据状态需要Entitlements.plist添加相应权限后对其签名。