我正在为越狱设备构建应用程序,想要阻止收到新消息。尝试钩取_ingestIncomingCTMessage方法,但该方法无效(在iOS6上似乎不起作用)。还有其他方法可以在iOS6中阻止短信吗?
com.apple.imagent
守护进程非常重要,它处理 kCTMessageReceivedNotification
。这就是为什么当你自己处理 kCTMessageReceivedNotification
时我们获取到空的消息对象 - com.apple.imagent
从 CTMessageCenter
中删除了它。com.apple.imagent
守护进程中。SMSServiceSession -(void)_processReceivedMessage:(CTMessage*)msg
。这是初始处理传入消息、保存到 SMS 数据库并传递给所有其他 iOS 组件的地方。问题是关于此 API 没有任何信息。如果你反汇编 com.apple.imagent
,你会发现它似乎没有使用它。这是因为它在运行时手动加载。com.apple.imagent
启动时,它会加载几个插件。我们需要的那个位于 /System/Library/Messages/PlugIns/SMS.imservice/
- 这是 SMSServiceSession
的实现位置。你不会在那里找到二进制文件,因为就像所有框架一样,它被编译到 /System/Library/Caches/com.apple.dyld/dyld_shared_cache_armv7
内。IDA可以识别此文件,并让你选择要反汇编的其内部的哪个二进制文件。[[CTMessageCenter sharedMessageCenter] acknowledgeIncomingMessageWithId:[msg messageId]]
,并从 _processReceivedMessage:
中返回而不调用原始实现。调用 CTMessageCenter
方法很重要,因为它将传入消息排队。SMS.imservice
插件。最初,imagent
只会创建 NSBundle
对象而不加载任何代码。所以您无法 hook 任何方法,因为类尚未从插件中加载。为解决此问题,我们可以 hook 私有的 IMDaemonCore.framework
中的 IMDService -(void)loadServiceBundle
方法。调用原始实现,然后就可以 hook 插件内的方法。要确定正在加载哪个插件,可以检查 IMDService -(NSBundle*)bundle
中的 bundle 标识符。/System/Library/Messages/PlugIns/iMessage.imservice
。hook MessageServiceSession -(void)_handler:(id) incomingMessage:(id) encryptionType:(id) messageID:(id) fromIdentifier:(id) fromToken:(id) timeStamp:(id) storageContext:(id) allowRetry:(char) completionBlock:(id)
应该可以解决问题。
更新
在 iOS 7 上可行。
更新 2
iOS 8的操作方式与之前相同,但需要挂接不同的SMSServiceSession
方法--(void)_processReceivedDictionary:(NSDictionary*)msg
。字典将包含所有短信内容。
如果您不想为iOS 8重写所有内容,则可以重用旧代码。隐藏的非导出C回调函数处理传入的SMS通知-您无法挂接它。首先,它调用SMSServiceSession -(id)_convertCTMessageToDictionary:(CTMessage*)msg requiresUpload:(BOOL*)upload
将SMS消息对象转换为字典。然后,它调用SMSServiceSession -(void)_processReceivedDictionary:(NSDictionary*)msg
来处理消息。最后,它调用SMSServiceSession -(BOOL)relayDictionaryToPeers:(NSDictionary*)msg requiresUpload:(BOOL)upload
通知所有其他iOS组件有关传入消息的信息。
要拦截短信,您需要挂接_convertCTMessageToDictionary
,在其中可以使用在以前的iOS版本上使用的相同代码。您还需要挂接_processReceivedDictionary
和relayDictionaryToPeers
来实际阻止传入的消息。只需从中返回而不调用原始实现即可。您可以在_convertCTMessageToDictionary
中设置某些全局变量,并在其他方法中检查和重置它。以这种方式进行操作是完全安全的-这些方法是同步依次调用的。该C回调函数是调用这些方法的唯一位置。
%hook SMSServiceSession -(void)_processReceivedMessage:(id)msg {%log;%orig; }
什么也没做。已经尝试在我的.plist
文件中使用Bundles =(“com.apple.imagent”)
和Executables =(“imagent”)
。我应该尝试其他的东西吗? - PanagiotisSMS.imservice
(其中包含SMSServiceSession)是手动加载到imagent中的,它在编译时没有链接,因此您不能随意挂钩方法。Theos在dylib构造函数中钩住方法,但此时尚未加载SMS.imservice
。这就是为什么我们需要loadServiceBundle
- 这是插件实际加载到imagent中的地方。只有这样,您才能挂钩_processReceivedMessage
。在此之前,它不存在。我认为您需要手动使用MobileSubstrate API。 - creker这有点棘手。苹果在这个领域做了重大改变。在iOS 5上很容易,但在iOS 6上我还没有找到简单的方法。 首先,您需要使用CTTelephonyCenter观察__kIMChatItemsDidChangeNotification通知。我是在SpringBoard中注入dylib来完成这个操作。不确定,但这可能很重要。
CTTelephonyCenterAddObserver(CTTelephonyCenterGetDefault(), NULL, Callback, NULL, NULL, CFNotificationSuspensionBehaviourHold);
void Callback(CFNotificationCenterRef, void*, NSString* notification, const void*, NSDictionary* userInfo)
{
if (![notification isEqualToString:@"__kIMChatItemsDidChangeNotification"])
{
return;
}
for (IMChatItem* chatItem in userInfo[@"__kIMChatItemsKey"])
{
IMMessage* msg = [chatItem message];//Incoming message object
NSString* text = [[msg text] string];//message text
NSString* sender = [[msg sender] ID];//message sender
[[IMDMessageStore sharedInstance] performBlock:^{
IMDChatRecordDeleteChatForGUID([NSString stringWithFormat:@"SMS;-;%@", sender]);
}];
}
}
IMDMessageStore
。它的 performBlock:
方法在此特殊线程上执行块。在 IMDPersistence.framework 中可以找到 IMDChatRecordDeleteChatForGUID
函数。它会删除具有特定 GUID 的整个消息树(聊天/对话)。我无法找到检索此 GUID 的方法,因此使用 SMS sqlite 数据库中的 GUID 作为示例手动构建其 GUID。
要仅删除一条消息,可以使用 IMDMessageRecordDeleteMessagesForGUIDs([NSArray arrayWithObject:[msg guid]]);
IMChatItem
和 IMMessage
可在 IMCore.framework
中找到。而 IMDMessageStore
则在 IMDaemonCore.framework
中。
这是容易的部分。现在,当您接收消息并以这种方式阻止它时,您将看到它仍然显示在 MobileSMS 应用程序中,您仍然可能会收到通知,您仍然会得到未读消息的标记。但是,如果您打开 SMS sqlite 数据库,则会发现该消息已被删除。阻止这些不是那么容易的。
BBServer
方法 publishBulletin:destinations:alwaysOnLockScreen:
。第一个参数是 BBBulletin 对象。如果它是输入消息公告,则其 section
属性等于 com.apple.MobileSMS
。要阻止公告,请从此方法返回并不调用原始实现。MessagesBadgeController
中的两个方法 - _madridChatRegistered:
和 _madridUnreadCountChanged:
。它们的第一个参数是包含 IMChat
对象的 NSNotification
对象。同样,只需从这些方法返回即可防止徽章更改。SMSApplication _receivedMessage:
、CKTranscriptController_messageReceived:
、CKConversationList_handleRegistryDidRegisterChatNotification:、_handleRegistryDidLoadChatNotification:, hasActiveConversations, unreadCount
、CKConversationController_chatParticipantsChangedNotification:, updateConversationList
、CKMessagesController showConversation:animate:forceToTranscript:
关于 ChatKit.serviceBundle。要挂钩其类,您需要等待 SpringBoard 实际加载它。这是在 SBPluginManager loadPluginBundle:
中完成的。捆绑标识符应等于 com.apple.SMSPlugin
。只有这样,您才能挂钩方法。
就是这样。虽然工作量很大,但它运行得非常完美——即使您正在MobileSMS应用程序中时,也没有收到任何信息的迹象。
我相信有更简单的方法可以实现。有com.apple.imagent守护进程向各种iOS组件发送通知。在iOS 6消息系统中非常重要。这是一个很好的起点。
%hook CKConversationListController
- (void)viewDidLoad
{
%orig;
CKConversationList *list = MSHookIvar<CKConversationList *>(self, "_conversationList");
if ([list count]) {
[deleteAll release];
}
}
%new
- (void)deleteAll:(id)sender {
CKConversationList *list = MSHookIvar<CKConversationList *>(self, "_conversationList");
UITableView *messages = MSHookIvar<UITableView *>(self, "_table");
for (unsigned int i = 0; i < [[list conversations] count]; i++) {
[list deleteConversationAtIndex:i];
}
[messages reloadData];
}
%end