Search code examples
iosobjective-ciphonexcode6iphone-privateapi

Import ChatKit (i.e., Private Framework) OR using CKDBMessage somehow


First - I know private frameworks/APIs won't get me to the AppStore, this is for private use/research only.

I can't get my project to compile with ChatKit.framework.

Basically I need to somehow init a CKDBMessage object and get stuff from it.

The first approach I tried is to be able to call this:

    CKDBMessage* msg = [[CKDBMessage alloc] initWithRecordID:lastID];
    NSLog(@"GOT SMS: %@", msg.text);

I couldn't get it to compile with any combination of these solutions:

  • Simply add only CKDBMessage.h to my project
  • Add all the headers of ChatKit.framework
  • Add also ChatKit.framework file itself

I have the headers and the framework file in Headers folder and I tried adding any/all of these build settings, both on recursive/non-recursive:

  • Framework Search Paths -> $(PROJECT_DIR)/Headers
  • Header Search Paths ->
    • $(SRCROOT)/Headers/ChatKit.framework/Headers
    • $(SRCROOT)/Headers
  • User Header Search Paths ->
    • $(SRCROOT)/Headers
    • $(SRCROOT)/Headers/ChatKit.framework/Headers

Always Search User Paths is always on YES

The second thing I tried was to do everything at runtime, this is what I have:

Class CKDBMessage = NSClassFromString(@"CKDBMessage");// objc_getClass("CKDBMessage");

SEL sel = @selector(initWithRecordID:);

NSMethodSignature *signature = [CKDBMessage methodSignatureForSelector:sel];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.selector = sel;
[invocation setArgument:&lastID atIndex:2];
[invocation invoke];

NSObject * msgWeak = [CKDBMessage alloc];
[invocation getReturnValue:&msgWeak];
NSObject *msg = msgWeak;

NSString *text = [msg performSelector:@selector(text)];

NSLog(@"text: %@", text);

Here I crash at invocationWithMethodSignature: because NSClassFromString returns nil instead of the class...

Any ideas on any of the two approaches?

This is for nonjailbroken, iOS8(.2), using Xcode6


Solution

  • Well not many people viewed this, but for the sake of our wiki community, I managed to solve this by adding the CKDBMessage.h file to my project (actually I added all the headers of ChatKit but I don't think it's necessary), than I loaded the framework dynamically with dlopen like so:

    dlopen("/System/Library/PrivateFrameworks/ChatKit.framework/ChatKit", RTLD_LAZY)
    

    So my full solution is:

    dlopen("/System/Library/PrivateFrameworks/ChatKit.framework/ChatKit", RTLD_LAZY);
    
    Class CKDBMessageClass = NSClassFromString(@"CKDBMessage");
    CKDBMessage *msg = [[CKDBMessageClass alloc] initWithRecordID:lastID];
    
    NSString *text = msg.text;
    NSLog(@"text: %@", text);
    

    Getting the ID of the last message involves another framework: IMDPersistence:

    //SomeFile.h
    // ...
    //declare the function:
    static int (*IMDMessageRecordGetMessagesSequenceNumber)();
    
    // SomeFile.m
    // ...
    //open IMDPersistence framework
    void *libHandleIMD = dlopen("/System/Library/PrivateFrameworks/IMDPersistence.framework/IMDPersistence", RTLD_LAZY);
    
    //make/get symbol from framework + name
    IMDMessageRecordGetMessagesSequenceNumber = (int (*)())dlsym(libHandleIMD, "IMDMessageRecordGetMessagesSequenceNumber");
    
    // get id of last SMS from symbol
    int lastID = IMDMessageRecordGetMessagesSequenceNumber();
    

    Now you can use lastID to get the message contents...