I'm building for a jail-broken device and I want to block incoming messages. I'm trying to hook _ingestIncomingCTMessage, but it has no result( it seems not working on ios6). How else I can block sms in ios6?
-
A naive question: why do you want to block incoming sms? – AlexWien Jan 12 '15 at 15:10
-
@AlexWien I imagine it could be useful for a lot of tweaks, since it allows you to conditionally block texts depending on the sender/content. You could make a text trigger an Activator action or query a device for information, without the text being visible in the thread later on. – Ryan Pendleton Jan 15 '15 at 21:24
3 Answers
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.

- 9,400
- 1
- 30
- 47
-
Sorry if being naive, but `%hook SMSServiceSession -(void)_processReceivedMessage:(id)msg{ %log; %orig; }` does nothing. Have tried with both `Bundles = ("com.apple.imagent",)` and `Executables = ( "imagent" )` in my `.plist` file. Should I try something else? – Panagiotis Nov 01 '13 at 20:11
-
1`SMS.imservice` (SMSServiceSession is inside of it) is being loaded manually into imagent, it's not linked at compile time thus you can't just hook methods whenever you want. Theos hooks methods in dylib constructor but at this point `SMS.imservice` is not loaded. That's why we need `loadServiceBundle` - this is where plugins are actually loaded into imagent. Only then you can hook `_processReceivedMessage`. Until then it doesn't exist. I think you need to use MobileSubstrate API manually for this one. – creker Nov 01 '13 at 20:56
-
It took me ages to understand the Logos tweaks! I guess it will be really hardcore to start with MobileSubstrate API now…! – Panagiotis Nov 01 '13 at 21:10
-
Did you manage to block outgoing messages as well? Being able to send messages without them appearing in Messages.app? – Alexandre Blin Nov 21 '13 at 12:10
-
In my project I still use other method that I described here (in accepted answer). It deals with sent and received messages. If you want to use method above with SMS.imservice I think you can block sent messages by hooking `SMSServiceSession -(void)_processMessageSent:(unsigned int)msgID` but I don't know for sure. I haven't done any research in this area. – creker Nov 22 '13 at 07:34
-
1@AlexandreBlin, check out my answer http://stackoverflow.com/questions/15872553/send-programmatically-sms-on-jailbreak-device/20425853#20425853 – creker Dec 06 '13 at 14:09
-
@creker, Hi. Did you manage to port this for iOS 8? The bundle isn't loaded in iOS 8! – Hamed Jan 01 '15 at 09:20
-
@Hamed, yes. Everything is the same except you need to hook different method in `SMSServiceSession`. I don't have the source code with me and don't remember which one it is. – creker Jan 01 '15 at 16:37
-
@creker, Thanks for reply! I checked dyld_shared_chache and found `[SMSServiceSession _processReceivedDictionary:]` to be the function you say (I guess!). But I have another problem. `IMDService` doesn't load the bundle at all so I can't hook the function. – Hamed Jan 01 '15 at 21:28
-
@creker, Oops! It was my problem. This function is hooked the same way as iOS 7. And `[SMSServiceSession _processReceivedDictionary:]` works. It's argument is a dictionary containing message data. By not calling the original function the sms is blocked! Thanks again:) – Hamed Jan 02 '15 at 09:01
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.
- Bullein. In SpringBoard you need to hook
BBServer
methodpublishBulletin:destinations:alwaysOnLockScreen:
. First argument is BBBulletin object. If it's incoming message bulletin it'ssection
property equalscom.apple.MobileSMS
. To block bulletin just return from this method and don't call original implementation. - 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 isNSNotification
object withobject
property containingIMChat
object. Again, just return from these methods to prevent badge changes. - 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.

- 9,400
- 1
- 30
- 47
-
I cant found function: IMDChatRecordDeleteChatForGUID inIMDPersistence.framework – user1561904 May 09 '13 at 02:12
-
@user1561904, it's there, just try calling it. Remeber that C functions exported with leading underscore. So in IDA, for example, you'll see _IMDChatRecordDeleteChatForGUID function, not IMDChatRecordDeleteChatForGUID. – creker May 09 '13 at 13:19
-
My Problem is message still showing at Inbox, but when touch to read it, it's gone – user1561904 May 09 '13 at 14:35
-
I think this is because com.apple.imagent thinks there is messages and tells that Messages application. But when you read message and Messages app tries to actualy read it from database it's gone becase this message is deleted from SMS.db As I said in your question, try killing imagent. It will reload SMS.db contents and inbox will be empty. This works, I tried it yesterday. Personally, I don't like killing processes so I'm hooking MobileSMS methods to stop it from showing deleted messages. Read my answers, it's all there and tested many times. – creker May 09 '13 at 14:46
-
Hooking MobileSMS is difficult for me, How can I killing "com.apple.imagent" Another question: I catch "kCTMessageReceivedNotification", I used this:http://stackoverflow.com/questions/10681995/how-to-get-the-message-when-receiving-the-kctmessagereceivednotification-notif to get sms content, but sometime, it is null, Why? – user1561904 May 11 '13 at 08:14
-
You can kill it like this `launchctl stop com.apple.imagent`. He will automatically relaunch and reload SMS.db contents. As for notification, I don't use it on both iOS 5 and 6 so I can't help you with that. On iOS 5 there is much more easy and reliable way of blocking incoming SMS. Above is how I'm doing it on iOS 6. Again, reliable, but not very easy. If you want to know how to do it properly on iOS 5 without using notifications, ask another question. – creker May 11 '13 at 08:30
-
OK :D Without get from "sms.db", any ways to get sms content when I received ""kCTMessageReceivedNotification" – user1561904 May 11 '13 at 08:42
-
Having read http://stackoverflow.com/questions/10681995/how-to-get-the-message-when-receiving-the-kctmessagereceivednotification-notif , I remember trying to use kCTMessageReceivedNotification and getting empty contents too. This is due to this message in console "unknown CommCenter[31]
: removing received message 2147483648". It's CoreTelephony notification so CommCenter must be removing message object before we can read it. This is why I dropped it on iOS 6 and found solution above. On iOS 5 there is much better way. – creker May 11 '13 at 08:54 -
That solution can get "normal" message or only iMessage. By the way: I used system"launchctl stop com.apple.imagent" but at Inbox, message is gone but still appear "sender number" – user1561904 May 11 '13 at 08:59
-
I haven't tested it with iMessage. But inside iOS there is no clear difference between SMS, MMS and iMessages. It's all instant messages using identical objects. You can see in the code above that everything has "IM" prefix. So on iOS 6 I think it should work with iMessages. You don't need to implement everything to test it. Just try catching __kIMChatItemsDidChangeNotification with different kinds of messages. – creker May 11 '13 at 09:13
-
"I used system launchctl stop com.apple.imagent but at Inbox, message is gone but still appear sender number" - it's off-topic. Better ask this here http://stackoverflow.com/questions/15939771/delete-message-on-ios-6/16451289 – creker May 11 '13 at 09:21
-
On your method, Did you used "IMCor.framework" or another framwork, can you tell me and maybe, send me that :D – user1561904 May 13 '13 at 08:31
-
`IMChatItem` and `IMMessage` can be found in `IMCore.framework`. `IMDMessageStore` is in `IMDaemonCore.framework` – creker May 13 '13 at 10:36
-
I'm working with xcode 3, but when i add IMCore.framework, Error: gcc 4.2. How can I fix that? plx help – user1561904 May 14 '13 at 02:16
-
Can you tell who it can be done "To hook it's classes you need to wait when SpringBoard actually loads" i mean how to wait – M.Shuaib Imran Jun 20 '13 at 07:03
-
1"This is done in 'SBPluginManager loadPluginBundle:'. Bundle identifier should be equal to com.apple.SMSPlugin. Only then you can hook methods" – creker Jun 20 '13 at 08:04
-
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

- 1,287
- 16
- 29
-
This doesn't block anything. Bulletin is not blocked. MobileSMS badge is not blocked. Only thing this code does is deleting everything once you launch MobileSMS. Moreover, if you launch it after SMS was received you will still see this SMS. – creker Jul 04 '13 at 11:56