- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我想在 ios 中完全隐藏一个电话。我的首要任务是在 ios 7(目前是最新的 ios 版本!)上执行此操作,但我想知道如何在 ios 6 及以下版本上隐藏电话(如果可能)。我发现了一些函数可以执行此操作,例如连接到 SBUIFullscreenAlertAdapter
类的 initWithAlertController
方法中。感谢 creker 在 this link我找到了另一种更好的 Hook 方法。问题是当电话未锁定或电话锁定时它仍然有一个调用栏,电话显示它正在通话中。以下是屏幕截图: link to image
我想知道处理这个hook的方法是什么?为了实现我想要的目标,还有什么我应该知道的吗?
为了删除通话结束后我想到的任何其他痕迹,我从它的数据库中删除了通话记录。有没有更好的办法?
最佳答案
我会尝试发布尽可能多的代码,但它不会从头开始工作。我使用自己的 macroses 生成 Hook ,因此您必须重写它们才能使用您的代码。我将使用伪函数 IsHiddenCall
来确定给定的调用是否是我们的隐藏调用(简单的电话号码检查)。这是为了简化代码。您显然必须自己实现。还会有其他伪函数,但它们的实现非常简单,从它们的名字就可以看出。这不是一个简单的调整,所以请耐心等待。
此外,代码是非 ARC 的。
基本上,我们会挂接所有可能会告诉 iOS 有电话来电的信息。
让我们从 iOS 7 开始,因为它是目前 iOS 的最后一个版本,隐藏调用的实现比 iOS 6 及更低版本更简单。
我们需要的几乎所有东西都位于私有(private) TelephonyUtilities.framework
中。在 iOS 7 中,Apple 几乎将与电话相关的所有内容都移到了该框架中。这就是它变得更简单的原因 - 所有其他 iOS 组件都使用该框架,因此我们只需要 Hook 它一次,而无需在每个 iOS 守护进程和可能会处理电话调用的框架中四处寻找。
所有方法都 Hook 在两个进程中 - SpringBoard 和 MobilePhone(电话应用程序)。 Bundle ID 分别是 com.apple.springboard
和 com.apple.mobilephone
。
这是我在两个进程中挂接的 TelephonyUtilities.framework
方法的列表。
//TUTelephonyCall -(id)initWithCall:(CTCallRef)call
//Here we return nil in case of a hidden call. That way iOS will ignore it
//as it checks for nil return value.
InsertHookA(id, TUTelephonyCall, initWithCall, CTCallRef call)
{
if (IsHiddenCall(call) == YES)
{
return nil;
}
return CallOriginalA(TUTelephonyCall, initWithCall, call);
}
//TUCallCenter -(void)handleCallerIDChanged:(TUTelephonyCall*)call
//This is CoreTelephony notification handler. We ignore it in case of a hidden call.
//call==nil check is required because of our other hooks that might return
//nil object. Passing nil to original implementation might break something.
InsertHookA(void, TUCallCenter, handleCallerIDChanged, TUTelephonyCall* call)
{
if (call == nil || IsHiddenCall([call destinationID]) == YES)
{
return;
}
CallOriginalA(TUCallCenter, handleCallerIDChanged, call);
}
//TUCallCenter +(id)callForCTCall:(CTCallRef)call;
//Just like TUTelephonyCall -(id)initWithCall:(CTCallRef)call
InsertHookA(id, TUCallCenter, callForCTCall, CTCallRef call)
{
if (IsHiddenCall(call) == YES)
{
return nil;
}
return CallOriginalA(TUCallCenter, callForCTCall, call);
}
//TUCallCenter -(void)disconnectAllCalls
//Here we disconnect every call there is except our hidden call.
//This is required in case of a hidden conference call with hidden call.
//Our call will stay hidden but active while other call is active. This method is called
//when disconnect button is called - we don't wont it to cancel our hidden call
InsertHook(void, TUCallCenter, disconnectAllCalls)
{
DisconnectAllExceptHiddenCall();
}
//TUCallCenter -(void)disconnectCurrentCallAndActivateHeld
//Just like TUCallCenter -(void)disconnectAllCalls
InsertHook(void, TUCallCenter, disconnectCurrentCallAndActivateHeld)
{
DisconnectAllExceptHiddenCall();
}
//TUCallCenter -(int)currentCallCount
//Here we return current calls count minus our hidden call
InsertHook(int, TUCallCenter, currentCallCount)
{
return CallOriginal(TUCallCenter, currentCallCount) - GetHiddenCallsCount();
}
//TUCallCenter -(NSArray*)conferenceParticipantCalls
//Hide our call from conference participants
InsertHook(id, TUCallCenter, conferenceParticipantCalls)
{
NSArray* calls = CallOriginal(TUCallCenter, conferenceParticipantCalls);
BOOL isThereHiddenCall = NO;
NSMutableArray* callsWithoutHiddenCall = [NSMutableArray array];
for (id i in calls)
{
if (IsHiddenCall([i destinationID]) == NO)
{
[callsWithoutHiddenCall addObject:i];
}
else
{
isThereHiddenCall = YES;
}
}
if (callsWithoutHiddenCall.count != calls.count)
{
//If there is only two calls - hidden call and normal - there shouldn't be any sign of a conference call
if (callsWithoutHiddenCall.count == 1 && isThereHiddenCall == YES)
{
[callsWithoutHiddenCall removeAllObjects];
}
[self setConferenceParticipantCalls:callsWithoutHiddenCall];
[self _postConferenceParticipantsChanged];
}
else
{
return calls;
}
}
//TUTelephonyCall -(BOOL)isConferenced
//Hide conference call in case of two calls - our hidden and normal
InsertHook(BOOL, TUTelephonyCall, isConferenced)
{
if (CTGetCurrentCallCount() > 1)
{
if (CTGetCurrentCallCount() > 2)
{
//There is at least two normal calls - let iOS do it's work
return CallOriginal(TUTelephonyCall, isConferenced);
}
if (IsHiddenCallExists() == YES)
{
//There is hidden call and one normal call - conference call should be hidden
return NO;
}
}
return CallOriginal(TUTelephonyCall, isConferenced);
}
//TUCallCenter -(void)handleCallStatusChanged:(TUTelephonyCall*)call userInfo:(id)userInfo
//Call status changes handler. We ignore all events except those
//that we marked with special key in userInfo object. Here we answer hidden call, setup
//audio routing and doing other stuff. Our hidden call is indeed hidden,
//iOS doesn't know about it and don't even setup audio routes. "AVController" is a global variable.
InsertHookAA(void, TUCallCenter, handleCallStatusChanged, userInfo, TUTelephonyCall* call, id userInfo)
{
//'call' is nil when this is a hidden call event that we should ignore
if (call == nil)
{
return;
}
//Detecting special key that tells us that we should process this hidden call event
if ([[userInfo objectForKey:@"HiddenCall"] boolValue] == YES)
{
if (CTCallGetStatus(call) == kCTCallStatusIncoming)
{
CTCallAnswer(call);
}
else if (CTCallGetStatus(call) == kCTCallStatusActive)
{
//Setting up audio routing
[AVController release];
AVController = [[objc_getClass("AVController") alloc] init];
SetupAVController(AVController);
}
else if (CTCallGetStatus(call) == kCTCallStatusHanged)
{
NSArray *calls = CTCopyCurrentCalls(nil);
for (CTCallRef call in calls)
{
CTCallResume(call);
}
[calls release];
if (CTGetCurrentCallCount() == 0)
{
//No calls left - destroying audio controller
[AVController release];
AVController = nil;
}
}
return;
}
else if (IsHiddenCall([call destinationID]) == YES)
{
return;
}
CallOriginalAA(TUCallCenter, handleCallStatusChanged, userInfo, call, userInfo);
}
这是我在两个进程中挂接的 Foundation.framework
方法。
//In iOS 7 telephony events are sent through local NSNotificationCenter. Here we suppress all hidden call notifications.
InsertHookAAA(void, NSNotificationCenter, postNotificationName, object, userInfo, NSString* name, id object, NSDictionary* userInfo)
{
if ([name isEqualToString:@"TUCallCenterControlFailureNotification"] == YES || [name isEqualToString:@"TUCallCenterCauseCodeNotification"] == YES)
{
//'object' usually holds TUCall object. If 'object' is nil it indicates that these notifications are about hidden call and should be suppressed
if (object == nil)
{
return;
}
}
//Suppressing if something goes through
if ([object isKindOfClass:objc_getClass("TUTelephonyCall")] == YES && IsHiddenCall([object destinationID]) == YES)
{
return;
}
CallOriginalAAA(NSNotificationCenter, postNotificationName, object, userInfo, name, object, userInfo);
}
这是我在 CoreTelephony.framwork
//CTCall +(id)callForCTCallRef:(CTCallRef)call
//Return nil in case of hidden call
InsertHookA(id, CTCall, callForCTCallRef, CTCallRef call)
{
if (IsHiddenCall(call) == YES)
{
return nil;
}
return CallOriginalA(CTCall, callForCTCallRef, call);
}
这是我之前使用的 SetupAVController
函数。隐藏的调用是真正隐藏的 - iOS 对此一无所知,所以当我们接听它时,音频路由没有完成,我们不会在另一端听到任何声音。 SetupAVController
执行此操作 - 它设置音频路由,就像 iOS 在有事件电话时所做的那样。我使用私有(private) Celestial.framework
extern id AVController_PickableRoutesAttribute;
extern id AVController_AudioCategoryAttribute;
extern id AVController_PickedRouteAttribute;
extern id AVController_AllowGaplessTransitionsAttribute;
extern id AVController_ClientPriorityAttribute;
extern id AVController_ClientNameAttribute;
extern id AVController_WantsVolumeChangesWhenPaused;
void SetupAVController(id controller)
{
[controller setAttribute:[NSNumber numberWithInt:10] forKey:AVController_ClientPriorityAttribute error:NULL];
[controller setAttribute:@"Phone" forKey:AVController_ClientNameAttribute error:NULL];
[controller setAttribute:[NSNumber numberWithBool:YES] forKey:AVController_WantsVolumeChangesWhenPaused error:NULL];
[controller setAttribute:[NSNumber numberWithBool:YES] forKey:AVController_AllowGaplessTransitionsAttribute error:NULL];
[controller setAttribute:@"PhoneCall" forKey:AVController_AudioCategoryAttribute error:NULL];
}
这里是我只hook在MobilePhone进程中的方法
/*
PHRecentCall -(id)initWithCTCall:(CTCallRef)call
Here we hide hidden call from call history. Doing it in MobilePhone
will hide our call even if we were in MobilePhone application when hidden call
was disconnected. We not only delete it from the database but also prevent UI from
showing it.
*/
InsertHookA(id, PHRecentCall, initWithCTCall, CTCallRef call)
{
if (call == NULL)
{
return CallOriginalA(PHRecentCall, initWithCTCall, call);
}
if (IsHiddenCall(call) == YES)
{
//Delete call from call history
CTCallDeleteFromCallHistory(call);
//Update MobilePhone app UI
id PHRecentsViewController = [[[[[UIApplication sharedApplication] delegate] rootViewController] tabBarViewController] recentsViewController];
if ([PHRecentsViewController isViewLoaded])
{
[PHRecentsViewController resetCachedIndexes];
[PHRecentsViewController _reloadTableViewAndNavigationBar];
}
}
return CallOriginalA(PHRecentCall, initWithCTCall, call);
}
我在 SpringBoard 进程中 Hook 的方法。
//SpringBoard -(void)_updateRejectedInputSettingsForInCallState:(char)state isOutgoing:(char)outgoing triggeredbyRouteWillChangeToReceiverNotification:(char)triggered
//Here we disable proximity sensor
InsertHookAAA(void, SpringBoard, _updateRejectedInputSettingsForInCallState, isOutgoing, triggeredbyRouteWillChangeToReceiverNotification, char state, char outgoing, char triggered)
{
CallOriginalAAA(SpringBoard, _updateRejectedInputSettingsForInCallState, isOutgoing, triggeredbyRouteWillChangeToReceiverNotification, state, outgoing, triggered);
if (IsHiddenCallExists() == YES && CTGetCurrentCallCount() == 1)
{
BKSHIDServicesRequestProximityDetectionMode = (void (*)(int))dlsym(RTLD_SELF, "BKSHIDServicesRequestProximityDetectionMode");
BKSHIDServicesRequestProximityDetectionMode(0);
}
}
//BBServer -(void)publishBulletin:(id)bulletin destinations:(unsigned int)destinations alwaysToLockScreen:(char)toLockScreen
//Suppress hidden call bulletins
InsertHookAAA(void, BBServer, publishBulletin, destinations, alwaysToLockScreen, id bulletin, unsigned int destinations, char toLockScreen)
{
if ([[bulletin section] isEqualToString:@"com.apple.mobilephone"] == YES)
{
NSArray *recordTypeComponents = [[bulletin recordID] componentsSeparatedByString:@" "];
NSString *recordType = recordTypeComponents[0];
NSString *recordCode = recordTypeComponents[1];
//Missed call bulletin
if ([recordType isEqualToString:@"missedcall"] == YES)
{
NSArray *recordCodeComponents = [recordCode componentsSeparatedByString:@"-"];
NSString *phoneNumber = recordCodeComponents[0];
if (IsHiddenCall(phoneNumber) == YES)
{
return;
}
}
}
CallOriginalAAA(BBServer, publishBulletin, destinations, alwaysToLockScreen, bulletin, destinations, toLockScreen);
}
//TUCallCenter -(id)init
//CoreTelephony notifications handler setup
InsertHook(id, TUCallCenter, init)
{
CTTelephonyCenterAddObserver(CTTelephonyCenterGetDefault(), self, CallStatusNotificationCallback, kCTCallStatusChangeNotification, NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
return CallOriginal(TUCallCenter, init);
}
//Call status changes handler. Here we redirect status changes into hooked TUCallCenter method and doing some other stuff.
void CallStatusNotificationCallback(CFNotificationCenterRef center, void* observer, CFStringRef name, const void* object, CFDictionaryRef userInfo)
{
if (object == NULL)
{
return;
}
if (IsHiddenCall((CTCallRef)object) == YES)
{
[observer handleCallStatusChanged:object userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:@"HiddenCall"]];
}
else
{
if (CTCallGetStatus((CTCallRef)object) == kCTCallStatusHanged)
{
if (IsHiddenCallExists() == YES)
{
//Setting up all the audio routing again. When normal call is hanged iOS may break audio routing as it doesn't know there is another active call exists (hidden call)
SetupAVController(AVController);
}
else if (CTGetCurrentCallCount() == 0)
{
[AVController release];
AVController = nil;
}
}
}
if (CTGetCurrentCallCount() > 1 && IsHiddenCallExists() == YES)
{
//Here we setup hidden conference call
NSArray *calls = CTCopyCurrentCalls(nil);
for (CTCallRef call in calls)
{
CTCallJoinConference(call);
}
[calls release];
}
}
iOS 5-6 更复杂。电话代码分散在许多 iOS 组件和 API 中。我可能会稍后发布代码,因为我现在没有时间。答案已经很长了。
关于ios - 在iOS(越狱设备)中完全隐藏电话,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22729003/
iphone设备UDID、iphone设备ID和iphone设备Token之间有什么区别? 通常,当我们使用苹果推送通知服务时,会使用 iPhone 设备 token 。 但我的目标只是识别唯一的 i
我们使用 firebase 从服务器向 Android 和 IOS 设备发送通知,并且我们使用旧版 FCM 发送通知。但是当我们的应用程序在后台时,通知由系统本身处理,因此我们无法通过应用程序处理它。
在 Google 上搜索后,我发现人们说只能通过“MFi 程序”将 iOS 设备与非 iOS 设备连接起来。这是真的吗? 我的项目主要集中于直接通过蓝牙与Arduino设备发送和接收信息。 iOS和非
所以我有一个通用应用程序,我正在设置 UIScrollView 的内容大小。显然,iPhone 和 iPad 上的内容大小会有所不同。如何为 iPad 设置某种尺寸,为 iPhone 和 iPod t
问题:如何在 pod 中使用连接到主机的原始设备作为 block 设备。 我尝试使用类型为“BlockDevice”的“hostPath” volumes: - my-data: hostPath
Implemented GCKDeviceScannerListener Singleton Class on ViewController, however its delegate methods
我有一个 (PhoneGap) 应用程序,它将成功获得 Passbook 通行证,并且还将成功接收与 Passbook 分开的推送通知(当伪造设备 ID 时)。 我遇到的问题是发送给注册设备的设备 I
我正在尝试找到一种方法,通过我目前正在使用的 iOS 应用程序访问我的信标的电池电量。我正在使用 Kontakt 的 iBeacon 设备。我浏览了 Estimote iOS SDK,他们提供了一种实
我正在努力让 CUDA 应用程序也能监控 GPU 的核心温度。可通过 NVAPI 访问该信息。 问题是我想确保在运行代码时监控的是同一个 GPU。 但是,似乎有信息表明我从 NvAPI_EnumPhy
从沙箱模式到生产模式,设备 token 有何不同? 我认为我已将一些设备 token 锁定为生产模式,并且无法将它们从开发中插入。 关于如何检查有什么想法吗? 最佳答案 当您使用开发证书构建应用程序时
目录 /run/user/1000/gvfs 和 ~/.gvfs 分别是空的和不存在的。我的图形文件管理器 (Thunar) 能够检测和访问设备的内部和外部存储器。 命令 gvfs-mount -l
我有一个 Android 平板电脑,它有一个迷你 USB 端口和一个 USB 端口,我想编写一个与 USB key 通信的应用程序。我写了一个demo来找出U盘,但是没有任何反应。 令我不安的是,如果
我们将 PHP 版本从 5.4.25 更改为 5.4.45,并在服务器上安装了 MS SQL 驱动程序。在更改服务器之前,一切正常,但在更改服务器之后,我遇到了 Web 服务问题。我们的身份验证 So
我想知道是否有人使用此 API 在 Android 设备上同时从 2 个后置摄像头捕获图像或视频:https://source.android.com/docs/core/camera/concurr
我正在为客户构建一个物联网解决方案,网络管理员坚持要求设备仅通过访客网络进行连接,该网络有一个强制门户,其中的服务条款必须通过按下 UI 按钮来接受,然后才能获得外部互联网访问。到目前为止,我见过的大
我无法弄清楚这里的格式规则..在我的示例中,代码行太多,无法为每行添加 4 个空格,因此这里是我需要帮助的代码的链接 http://nitemsg.blogspot.com/2011/01/heres
如果我在我的设备上接受推送通知,并且不保存设备 token ,那么我如何在自定义 View 中查看设备 token 或恢复警报 View ? 我删除了应用程序并重新安装,但看不到设备 token 警报
我试图找出在尝试并行比较和复制设备 block 与 pthreads 时我做错了什么。看起来我正在脱离同步并且比较阶段无法正常工作。任何帮助将不胜感激 #ifndef __dbg_h__ #defin
我刚刚写完所有这些内容,但这个红色的小栏告诉我我不能发布图片或两个以上的链接。因此,如果您可以引用 this Imgur album , 那简直太好了。谢谢。 我在这里相对较新,甚至对 android
我需要启用 mysql 常规日志并将其通过 nsf 移动到我系统中的另一个驱动器/设备! 所以,我在 my.cnf 中启用了它: general_log = 1 general_log_fi
我是一名优秀的程序员,十分优秀!