阻止iOS 6上的短信

3

我正在为越狱设备构建应用程序,想要阻止收到新消息。尝试钩取_ingestIncomingCTMessage方法,但该方法无效(在iOS6上似乎不起作用)。还有其他方法可以在iOS6中阻止短信吗?


一个天真的问题:为什么你想要阻止收到的短信? - AlexWien
@AlexWien 我想这对很多调整可能会有用,因为它允许您根据发送者/内容有条件地阻止文本。您可以使文本触发 Activator 操作或查询设备以获取信息,而不会在后来的线程中显示文本。 - Ryan Pendleton
3个回答

9
发现更好、更简单的方法。就像我所想的那样,com.apple.imagent 守护进程非常重要,它处理 kCTMessageReceivedNotification。这就是为什么当你自己处理 kCTMessageReceivedNotification 时我们获取到空的消息对象 - com.apple.imagentCTMessageCenter 中删除了它。
我们只需 hook 两个方法,但找到并 hook 它们相当棘手。这两个方法都被 hook 在 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 标识符。
此方法仅适用于 SMS 和 MMS 消息。iMessages 以类似的方式处理,但是使用不同的插件 - /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版本上使用的相同代码。您还需要挂接_processReceivedDictionaryrelayDictionaryToPeers来实际阻止传入的消息。只需从中返回而不调用原始实现即可。您可以在_convertCTMessageToDictionary中设置某些全局变量,并在其他方法中检查和重置它。以这种方式进行操作是完全安全的-这些方法是同步依次调用的。该C回调函数是调用这些方法的唯一位置。


抱歉如果我太天真了,但是%hook SMSServiceSession -(void)_processReceivedMessage:(id)msg {%log;%orig; }什么也没做。已经尝试在我的.plist文件中使用Bundles =(“com.apple.imagent”)Executables =(“imagent”)。我应该尝试其他的东西吗? - Panagiotis
1
SMS.imservice(其中包含SMSServiceSession)是手动加载到imagent中的,它在编译时没有链接,因此您不能随意挂钩方法。Theos在dylib构造函数中钩住方法,但此时尚未加载SMS.imservice。这就是为什么我们需要loadServiceBundle - 这是插件实际加载到imagent中的地方。只有这样,您才能挂钩_processReceivedMessage。在此之前,它不存在。我认为您需要手动使用MobileSubstrate API。 - creker
理解 Logos 的微调花了我很长时间!我猜现在开始使用 MobileSubstrate API 会非常困难! - Panagiotis
你成功地阻止了出站消息吗?能够发送消息而不在Messages.app中显示吗? - Alexandre Blin
1
@AlexandreBlin,请查看我的回答https://dev59.com/dGUo5IYBdhLWcg3wpg0f#20425853 - creker
显示剩余5条评论

5

这有点棘手。苹果在这个领域做了重大改变。在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]]);

IMChatItemIMMessage 可在 IMCore.framework 中找到。而 IMDMessageStore 则在 IMDaemonCore.framework 中。

这是容易的部分。现在,当您接收消息并以这种方式阻止它时,您将看到它仍然显示在 MobileSMS 应用程序中,您仍然可能会收到通知,您仍然会得到未读消息的标记。但是,如果您打开 SMS sqlite 数据库,则会发现该消息已被删除。阻止这些不是那么容易的。

  1. Bullein。在 SpringBoard 中,您需要挂钩 BBServer 方法 publishBulletin:destinations:alwaysOnLockScreen:。第一个参数是 BBBulletin 对象。如果它是输入消息公告,则其 section 属性等于 com.apple.MobileSMS。要阻止公告,请从此方法返回并不调用原始实现。
  2. MobileSMS 应用程序标记。有 ChatKit.serviceBundle,当有新短信时 SpringBoard 会加载它。您需要挂钩 MessagesBadgeController 中的两个方法 - _madridChatRegistered:_madridUnreadCountChanged:。它们的第一个参数是包含 IMChat 对象的 NSNotification 对象。同样,只需从这些方法返回即可防止徽章更改。
  3. MobileSMS 应用程序。要防止其显示已删除的消息,我要挂钩相当多的方法。我只会给你列表:SMSApplication _receivedMessage:CKTranscriptController_messageReceived:CKConversationList_handleRegistryDidRegisterChatNotification:、_handleRegistryDidLoadChatNotification:, hasActiveConversations, unreadCountCKConversationController_chatParticipantsChangedNotification:, updateConversationListCKMessagesController showConversation:animate:forceToTranscript:

关于 ChatKit.serviceBundle。要挂钩其类,您需要等待 SpringBoard 实际加载它。这是在 SBPluginManager loadPluginBundle: 中完成的。捆绑标识符应等于 com.apple.SMSPlugin。只有这样,您才能挂钩方法。

就是这样。虽然工作量很大,但它运行得非常完美——即使您正在MobileSMS应用程序中时,也没有收到任何信息的迹象。

我相信有更简单的方法可以实现。有com.apple.imagent守护进程向各种iOS组件发送通知。在iOS 6消息系统中非常重要。这是一个很好的起点。


有没有任何方法可以在收到“kCTMessageReceivedNotification”时获取短信内容,而不是从“sms.db”中获取? - user1561904
我还没有用iMessage测试过它。但在iOS内部,短信、彩信和iMessage之间没有明显的区别。它们都使用相同的对象进行即时通讯。您可以在上面的代码中看到所有内容都有“IM”前缀。因此,在iOS 6上,我认为它应该可以与iMessage一起使用。您不需要实现所有功能来测试它。只需尝试使用不同类型的消息捕获__kIMChatItemsDidChangeNotification即可。 - creker
你能告诉我如何等待SpringBoard实际加载以钩取它的类吗?我的意思是如何等待。 - M.Shuaib Imran
1
这是在'SBPluginManager loadPluginBundle:'中完成的。Bundle标识符应该等于com.apple.SMSPlugin。只有这样才能挂钩方法。 - creker
你能把这个项目发送给我吗? - user1561904
显示剩余12条评论

1
我有一个更好的解决方案来阻止所有短信消息。
%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

这不会阻止任何东西。公告没有被阻止。MobileSMS 徽章也没有被阻止。这段代码唯一的作用就是在你启动 MobileSMS 后删除所有内容。此外,如果你在收到短信后再启动它,你仍然会看到这条短信。 - creker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接