Block sms on ios6

怎甘沉沦 提交于 2019-11-28 06:39:29

This is quite tricky. Apple made major changes in this area. It's very easy on iOS 5 but on iOS 6 I couldn't find easy way to do it yet. First, you need to observer __kIMChatItemsDidChangeNotification notification using CTTelephonyCenter. I'm doing it in dylib injected in SpringBoard. Not sure but this may be important.

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]);
        }];
    }
}

Last bit is very important. You can't just delete message. You need to do it on a specific internal thread or you'll get an error. This is why I'm using IMDMessageStore. It's performBlock: method executes block on this special thread. IMDChatRecordDeleteChatForGUID function can be found in IMDPersistence.framework. It deletes whole message tree (chat/conversation) with specific GUID. I couldn't find a way to retrive this GUID so I'm building it manually using GUIDs from SMS sqlite database as a sample.

To delete just one message you can use IMDMessageRecordDeleteMessagesForGUIDs([NSArray arrayWithObject:[msg guid]]);

IMChatItem and IMMessage can be found in IMCore.framework. IMDMessageStore is in IMDaemonCore.framework.

This is easy part. Now when you receive message and block it this way you will see that it still shows in MobileSMS app, you still might get bullein notification, you still get badge telling you there is unread message. But if you open SMS sqlite database you will see that message was indeed deleted. Blocking these is not that easy.

  1. Bullein. In SpringBoard you need to hook BBServer method publishBulletin:destinations:alwaysOnLockScreen:. First argument is BBBulletin object. If it's incoming message bulletin it's section property equals com.apple.MobileSMS. To block bulletin just return from this method and don't call original implementation.
  2. MobileSMS app badge. There is ChatKit.serviceBundle that's being loaded in SpringBoard when there is incoming SMS. You need to hook two methods in MessagesBadgeController - _madridChatRegistered: and _madridUnreadCountChanged:. Their first argument is NSNotification object with object property containing IMChat object. Again, just return from these methods to prevent badge changes.
  3. MobileSMS app. To stop it from showing already deleted messages I'm hooking quite a lot of methods. I will just give you the list: SMSApplication _receivedMessage:, CKTranscriptController _messageReceived:, CKConversationList _handleRegistryDidRegisterChatNotification:, _handleRegistryDidLoadChatNotification:, hasActiveConversations, unreadCount. CKConversationController _chatParticipantsChangedNotification:, updateConversationList, CKMessagesController showConversation:animate:forceToTranscript:

About ChatKit.serviceBundle. To hook it's classes you need to wait when SpringBoard actually loads it. This is done in SBPluginManager loadPluginBundle:. Bundle identifier should be equal to com.apple.SMSPlugin. Only then you can hook methods.

That's it. Quite a lot of work but it's working perfectly - no sign of incoming message, even if you were in MobileSMS application when message arrived.

I'm sure there is easier way to do it. There is com.apple.imagent daemon which sends notifications to various iOS components. It's very important in iOS 6 messaging system. Good place to start.

Found much better and simpler way. Just as I thought com.apple.imagent daemon is very important and it's him who is handling kCTMessageReceivedNotification. This is why we get empty message object when handling kCTMessageReceivedNotification yourself - com.apple.imagent is removing it from CTMessageCenter.

We need to hook just two methods but finding and hooking them is quite tricky. Both methods are hooked in com.apple.imagent daemon.

First, SMSServiceSession -(void)_processReceivedMessage:(CTMessage*)msg. This is where incoming message is being initially processed, saved to the SMS database and passed to all other iOS components. Problem is there is no information about this API anywhere. com.apple.imagent appears to be not using it if you disassemble it. It's because it's being loaded manually at runtime.

When com.apple.imagent is started he's loading several plugins. The one that we need is located in /System/Library/Messages/PlugIns/SMS.imservice/ - this is where SMSServiceSession is implemented. You will not find binary in there because just like all the frameworks it's compiled into /System/Library/Caches/com.apple.dyld/dyld_shared_cache_armv7. IDA recognizes this file and let's you choose which binary inside of it you want to disassemble.

To delete incoming message and prevent any notifications about it you need to call [[CTMessageCenter sharedMessageCenter] acknowledgeIncomingMessageWithId:[msg messageId]] and return from _processReceivedMessage: without calling original implementation. Calling CTMessageCenter method is important because it queues incoming messages.

Now we need to find a way to know whenSMS.imservice plugin is being actually loaded. Initially imagent only creates NSBundle objects without loading any code. So you can't hook any methods because classes are not yet loaded from the plugins. To solve this we can hook IMDService -(void)loadServiceBundle method from private IMDaemonCore.framework. Call original implementation and you can hook methods inside the plugin. To determine which plugin is being loaded you can check bundle identifier in IMDService -(NSBundle*)bundle.

This method works only with SMS and MMS messages. iMessages are processed in similar way but with different plugin - /System/Library/Messages/PlugIns/iMessage.imservice. Hooking MessageServiceSession -(void)_handler:(id) incomingMessage:(id) encryptionType:(id) messageID:(id) fromIdentifier:(id) fromToken:(id) timeStamp:(id) storageContext:(id) allowRetry:(char) completionBlock:(id) should do the trick.

UPDATE

Works on iOS 7

UPDATE 2

On iOS 8 everything works the same way except you need to hook different SMSServiceSession method - -(void)_processReceivedDictionary:(NSDictionary*)msg. Dictionary will contain all SMS message contents.

If you don't want to rewrite everything for iOS 8 you can reuse your old code. Incoming SMS notification is handled by hidden non-exported C callback function - you can't hook it. First, it calls SMSServiceSession -(id)_convertCTMessageToDictionary:(CTMessage*)msg requiresUpload:(BOOL*)upload to convert SMS message object to dictionary. Then it calls SMSServiceSession -(void)_processReceivedDictionary:(NSDictionary*)msg to process message. Finally, it calls SMSServiceSession -(BOOL)relayDictionaryToPeers:(NSDictionary*)msg requiresUpload:(BOOL)upload to notify all other iOS components about incoming message.

To block SMS you need to hook _convertCTMessageToDictionary where you can use the same code you used on previous iOS versions. You also need to hook both _processReceivedDictionary and relayDictionaryToPeers to actually block incoming message. Just return from them without calling original implementation. You can set some global variable in _convertCTMessageToDictionary and check and reset it in other methods. It's perfectly safe to do it that way - these methods are called one after another synchronously. That C callback function is the only places where these methods are called.

i have a better solution to block all SMS messages

%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
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!