These are some of the existing methods to implement IPC on iOS

634 阅读5分钟

这是我参与8月更文挑战的第22天,活动详情查看: 8月更文挑战” juejin.cn/post/698796… ”

前言

These are some of the existing methods to implement IPC on iOS:

  • Universal Links、URL Scheme

kunnan.blog.csdn.net/article/det…

blog.csdn.net/z929118967/…

blog.csdn.net/z929118967/…

iOS 逆向:Tweak的开发例子【发红包】使用tweak和lua脚本结合进行实现 blog.csdn.net/z929118967/…

	UIPasteboard* pasteboard = [UIPasteboard generalPasteboard];	
	[pasteboard setString:@"A1"];
//使用tweak和lua脚本结合进行实现
//1、tweak侧的功能是hookapp的原生功能
//2、lua 是实现模拟用户点击
//3、通信通过剪切板:tweak 通过剪切板和lua脚本进行通信
//其实后面我继续研究,把lua侧的功能全部用tweak实现了。 这里分享的是一个思路。



  • AppleEvents & AppleScript
  • Distributed Objects
  • XPC

developer.apple.com/library/con…

iphonedevwiki.net/index.php/N…

iphonedevwiki.net/index.php/L…

github.com/rpetrich/Li…

Community Libraries

  • 1、RocketBootstrap: Service registration and lookup system for iOS
  • 2、OBJCIPC :High-level API for hosting services inside apps (by Alan Yip/a1anyip)
  • 3、LightMessaging: Header-only library for simple IPC

本文重点讲解 RocketBootstrap的两种包装方式:CFMessagePort、CPDistributedMessagingCenter

I 、librocketbootstrap

1、Uses iOS7’s security model: Privileged processes can register, any process can look up 2、Works with existing mach-based IPC mechanisms 3、Similar to Apple’s bootstrap APIs: bootstrap_look_up becomes rocketbootstrap_look_up bootstrap_register becomes rocketbootstrap_register 4、Easy to use wrappers for CFMessagePort and CPDistributedMessagingCenter(todo: XPC) 5、Bring your own security model by using audit_token_to_au32 to know who’s calling 6、Requires package dependency, commonly installed on users’ devices

1.0 获取librocketbootstrap :

-Install this from Cydia 直接搜索rocketbootstrap安装即可

iPhone:/var/log root# ls -l /usr/lib/librocketbootstrap.dylib
-rwxr-xr-x 1 root wheel 221776 Feb  6  2017 /usr/lib/librocketbootstrap.dylib*

/Library/LaunchDaemons/com.rpetrich.rocketbootstrapd.plist
/usr/libexec/rocketd
launchctl load  /Library/LaunchDaemons/com.rpetrich.rocketbootstrapd.plist
launchctl unload /Library/LaunchDaemons/com.rpetrich.rocketbootstrapd.plist


1.1 CFMessagePort

  • registerMsgCenter 基本可以解决双向通信; 例子:避免重启其他进程从sb获取源地址信息的变更。 守护进行都可以注册成为服务
+ (kern_return_t)rockettest_messageport_server
{
    static CFMessagePortRef messagePort;//
    if (messagePort)
        return 0;
    messagePort = CFMessagePortCreateLocal(kCFAllocatorDefault, CFSTR("rockettest_messageport"), messagePortCallback, NULL, NULL);//CFSTR("rockettest_messageport")即server key,
    CFRunLoopSourceRef source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, messagePort, 0);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), source, (CFStringRef)UITrackingRunLoopMode);
    return rocketbootstrap_cfmessageportexposelocal(messagePort);
}
typedef CFDataRef (*CFMessagePortCallBack)(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info);
//回调的定义
static CFDataRef messagePortCallback(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info)
{
    NSLog(@"rockettest_messageport_server: received %@", data);
    return CFDataCreate(kCFAllocatorDefault, (const UInt8 *)"bootstrap", 9);
}

#import <UIKit/UIKit.h>
#include "log.h"

#import "rocketbootstrap.h"

@implementation NSObject (rocketbootstrap)

+ (kern_return_t)rocketbootstrap_unlock:(NSString *)name
{
	return rocketbootstrap_unlock([name UTF8String]);
}

static CFDataRef messagePortCallback(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info)
{
	NSLog(@"rockettest_messageport_server: received %@", data);
	return CFDataCreate(kCFAllocatorDefault, (const UInt8 *)"bootstrap", 9);
}
/**
registerMsgCenter 基本可以解决双向通信;
例子:避免重启其他进程从sb获取源地址信息的变更。 守护进行都可以注册成为服务
*/
+ (kern_return_t)rockettest_messageport_server
{
	static CFMessagePortRef messagePort;
	if (messagePort)
		return 0;
	messagePort = CFMessagePortCreateLocal(kCFAllocatorDefault, CFSTR("rockettest_messageport"), messagePortCallback, NULL, NULL);
	CFRunLoopSourceRef source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, messagePort, 0);
	CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
	CFRunLoopAddSource(CFRunLoopGetCurrent(), source, (CFStringRef)UITrackingRunLoopMode);
	return rocketbootstrap_cfmessageportexposelocal(messagePort);
}

+ (NSData *)rockettest_messageport_client
{
	CFMessagePortRef remote = rocketbootstrap_cfmessageportcreateremote(kCFAllocatorDefault, CFSTR("rockettest_messageport"));
	if (!remote)
		return nil;
	CFDataRef request = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)"rocket", 6);
	CFDataRef response = NULL;
	CFMessagePortSendRequest(remote, 0, request, 10, 10, CFSTR("rocketboostrap_wait"), &response);
	CFRelease(remote);
	CFRelease(request);
	return [(NSData *)response autorelease];
}

II CPDistributedMessagingCenter

Asynchronous, if you go through the trouble

2.1 CFMessagePort Example

  • Server
static CFDataRef Callback(CFMessagePortRef port,
                          SInt32 messageID,
                          CFDataRef data,
                          void *info)
{
    // ...
}

%ctor {
    static CFMessagePortRef localPort =
        CFMessagePortCreateLocal(nil,
                                 CFSTR("com.example.app.port.server"),
                                 Callback,
                                 nil,
                                 nil);

    CFRunLoopSourceRef runLoopSource =
        CFMessagePortCreateRunLoopSource(nil, localPort, 0);

    CFRunLoopAddSource(CFRunLoopGetCurrent(),
                       runLoopSource,
                       kCFRunLoopCommonModes);

    rocketbootstrap_cfmessageportexposelocal(localPort);
}

  • Client
void doStuff() {
    CFDataRef data;
    SInt32 messageID = 0x1111; // Arbitrary
    CFTimeInterval timeout = 10.0;

    CFMessagePortRef remotePort =
        rocketbootstrap_cfmessageportcreateremote(nil,
                                  CFSTR("com.example.app.port.client"));

    SInt32 status =
        CFMessagePortSendRequest(remotePort,
                                 messageID,
                                 data,
                                 timeout,
                                 timeout,
                                 NULL,
                                 NULL);

    if (status == kCFMessagePortSuccess) {
        // ...
    }
}

2.2 CPDistributedMessagingCenter的例子

目前发现这种方式只能在SpringBoard 注册为服务端,处理消息,;

进程间其实是单向通信;

#TweakDemo.xm  SpringBoard  接受消息
#import "rocketbootstrap.h"

#define kXPCCenterNameKey  @"kXPCCenterNameKey_83641"

%hook SpringBoard
- (void)applicationDidFinishLaunching:(id)application {
   %orig;
   CPDistributedMessagingCenter *c = [%c(CPDistributedMessagingCenter) centerNamed:kXPCCenterNameKey];
   rocketbootstrap_distributedmessagingcenter_apply(c);
   [c runServerOnCurrentThread];
   [c registerForMessageName:@"myMessageName" target:self selector:@selector(handleMessage:withUserInfo:)];
   NSLog(@"注册监听 start");
}

%new
- (void)handleMessage:(NSString *)name withUserInfo:(NSDictionary *)userInfo {
   NSLog(@"handleMessage withUserInfo:%@",userInfo);
   //TODO:something
}

%end
//在需要发送的地方(沙盒 app ),如此这般的写: 
%hook SomeClass

-(void)someMethod{
   %orig;
    NSMutableDictionary *userInfo = [@{} mutableCopy];
    [userInfo setObject:@"123" forKey:@"arg001"];
    [userInfo setObject:@"456" forKey:@"arg002"];
    NSLog(@"发送: %@=%@",kXPCCenterNameKey,userInfo);
    CPDistributedMessagingCenter *c = [%c(CPDistributedMessagingCenter) centerNamed:kXPCCenterNameKey];
    rocketbootstrap_distributedmessagingcenter_apply(c);
    [c sendMessageName:@"myMessageName" userInfo:userInfo];
}
%end

III、 UIPasteboard/NSPasteboard

OpenUDID的使用例子

#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
#import <UIKit/UIPasteboard.h>
#import <UIKit/UIKit.h>
#else
#import <AppKit/NSPasteboard.h>
#endif
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
        UIPasteboard* slotPB = [UIPasteboard pasteboardWithName:slotPBid create:NO];
#else
        NSPasteboard* slotPB = [NSPasteboard pasteboardWithName:slotPBid];
#endif

  • 利用 pasteboardWithName 方法进行数据存储达到通信的目的
/**
 应用级别的,数据在属于自己的应用内部共享;
 (默认情况下是不会把数据写进沙盒的,也就是说(复制、剪切)粘贴内容会因为应用的退出而销毁掉,我们可以设置相关属性 persistent值为 YES让其进行数据的持久化存储起来)
 
 Ps:例如 persistent 是否进行数据持久化 还有 changeCount 改变次数(剪切板)系统重启方才重新计数
 
 */
- (NSObject *)model{
    
    if (_model == nil) {
        
        NSString *contentUserID =@"";
        UIPasteboard *pasteboardUserID = [UIPasteboard pasteboardWithName:KNpasteboardWithNameKeyUserID create:NO];
        
        if (pasteboardUserID){
            contentUserID = pasteboardUserID.string;////获取内容
        }
        
        _model = [[NSObject alloc]init];
//        _model.UserId =contentUserID;
        
        
    }
    return _model;
}

IV、LightMessaging

  • feature

1、Mid-level API, 2、Message-oriented 3、Zero copy, for certain message types 4、No additional cost over standard mach calls 5、Easy integration with RocketBootstrap 6、No package dependencies

  • using

1、Start services with LMStartService 2、Send messages with LMConnectionSendTwoWay (and friends) 3、Send replies with LMSendReply (and friends) 4、Bring your own security checks still :Community developers, your input on API please!

V 、IPC

allows processes to send each other messages and data

更多内容请看原文和关注公众号:iOS逆向

VI、libobjcipc

  • feature

• High level API provides service lookup and IPC, easy to use • Background-launches and fakes app lifecycle for you • Message-oriented • Mostly asynchronous • “Open” security model • Requires separate package dependency, but very small • Simple Objective-C APIs: Register using --registerIncomingMessageFromAppHandlerForMessageName:handler: Send using -- sendMessageToAppWithIdentifier:messageName:dictionary:replyHandler:
• Similar patterns for App to SpringBoard

VII XPC

XPC can be accessed through either the libxpc C API, or the NSXPCConnection Objective-C API.

  • feature

1、High level API, easy to use :One of Apple’s many wrappers for Mach messages; 2、Message-oriented 3、Public API on OS X only 4、Asynchronous always, no synchronous versions 5、Service lookup is restricted on iOS 7+

VIII Tweaks on a multi-process iOS

  • Inter-Process Communication
1、Mechanisms provided by the kernel to facilitate coordinated sharing of data and commands between processes
2、Used heavily in recent versions of iOS and OS X to implement system frameworks and APIs
  • Standard Techniques
1、Save to temp files:High level APIs, easy to use
2、Unix Domain Sockets: Low level API  ,Stream oriented, requires basic parsing to reconstruct
messages
  • Other standard techniques
Shared Memory
• Signals
• Named pipes
• Network sockets 
  • Apple/iOS-specific Techniques
1、Darwin Notifications:No data, only a simple “go” message,Any process can post or observe ,Always asynchronous
2、Mach Ports :seriously low level 
3、CPDistributedNotificationCenter: Private API, does change between iOS versions
4、CFMessagePort: Public API,Only supports synchronous use ,Service lookup is restricted on iOS 6+
5、XPC : High level API, easy to use �,One of Apple’s many wrappers for Mach messages ;
  • Creative Techniques
1、Relax existing service permissions
2、Repurpose existing iOS services’ IPC channels:Service internals are frequently rewritten in new iOS versions
3Delegate to someone else
  • Libraries that use IPC under the hood
1、http://iphonedevwiki.net/index.php/AppList
2、http://iphonedevwiki.net/index.php/Flipswitch
3、http://iphonedevwiki.net/index.php/Libactivator
4、https://github.com/r-plus/libcanopenurl
  • Community Libraries to Help
1、RocketBootstrap: Service registration and lookup system for iOS
  • Be aware of potential deadlocks
1、 SpringBoard will block on backboardd—don’t call from backboardd to SpringBoard! 
2、Communicate with these processes using one-way IPC, asynchronous IPC, or two-way IPC with timeouts 
3、Avoid the pitfall of accidentally sending blocking API calls to one’s own process
4、SpringBoard is usually a good choice for coordinator as it often has much work to do anyway
5、Batch all of the operations for a single user action into one IPC call, if possible 
6、Filter to only the data required before sending 

IV、 see also

更多内容请看原文,或者关注公众号:iOS逆向