MacOS IPC 之 DO
简介
DO全称是Distributed Objects
,从字面上来看意思很好理解,分布式对象。这是一种IPC方式,简单易用,实现的效果就是:通过launchd和一个proxy object,任何的进程都可以访问到server中的DO对象,也可以调用这个对象的方法,从而实现IPC。下面是一个流程图:
(图来自pj0的博客,ianbeer的文章)
示例代码
server.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #import <objc/Object.h> #import <Foundation/Foundation.h>
@interface VendMe : NSObject - (oneway void) foo: (int) value; @end
@implementation VendMe - (oneway void) foo: (int) value; { NSLog(@"%d", value); } @end
int main (int argc, const char * argv[]) { VendMe* toVend = [[VendMe alloc] init]; NSConnection *conn = [NSConnection defaultConnection]; [conn setRootObject:toVend]; [conn registerName:@"com.foo.my_test_service"]; [[NSRunLoop currentRunLoop] run]; return 0; }
|
client.m
1 2 3 4 5 6 7 8 9
| #import <Cocoa/Cocoa.h>
int main(int argc, char** argv){ id theProxy = [[NSConnection rootProxyForConnectionWithRegisteredName:@"com.foo.my_test_service" host:nil] retain]; [theProxy foo:123]; return 0; }
|
Makefile
1 2 3 4 5
| all: clang client.m -o client -framework Foundation clang server.m -o server -framework Foundation clean: rm server client
|
运行效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ╰─$ ./server 2019-08-10 22:27:07.545 server[28274:2677291] 123 2019-08-10 22:27:08.897 server[28274:2677291] 123 2019-08-10 22:27:09.662 server[28274:2677291] 123 2019-08-10 22:27:10.172 server[28274:2677291] 123
╭─muhe@muheMacBookPro ~/Downloads/DO_Study ╰─$ ./client ╭─muhe@muheMacBookPro ~/Downloads/DO_Study ╰─$ ./client ╭─muhe@muheMacBookPro ~/Downloads/DO_Study ╰─$ ./client ╭─muhe@muheMacBookPro ~/Downloads/DO_Study ╰─$ ./client ╭─muhe@muheMacBookPro ~/Downloads/DO_Study ╰─$
|
效果看起来很简单,类似socket通信那种效果,server跑起来等待连接,client连上去,然后通过launchd和proxy obj调用了server里的方法,参数是client传递的,看起来像是client执行了一个函数,其实真正代码执行的是server。
OC的语法虽然很奇怪,但是问题不大,还是能看懂:
通过NSConnection
注册了一个用于DO的对象,设置的对象是VendMe
,注册名是com.foo.my_test_service
,用来标示这个服务。随后使用Runloop
使得server能随时处理事件但并不退出,关于Runloop
的分析本文不会涉及。
client的逻辑很简单,通过NSConnection
连接目标服务,然后通过proxy obj调用他的foo方法,并传递一个123的参数给他。
大概逻辑就这个样子。
逆向分析
为什么要提及这部分,apple大部分服务都不开源,而且用的IPC方式都不确定,逆向的时候需要想办法确定,并且找到关键函数,比如上例中的foo函数,找到处理函数,方便代码审计。
这里只需要关注服务端(废话),所以直接贴F5后的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int __cdecl main(int argc, const char **argv, const char **envp) { VendMe *v3; VendMe *v4; void *v5; void *v6;
v3 = objc_msgSend(&OBJC_CLASS___VendMe, "alloc", envp); v4 = objc_msgSend(v3, "init"); v5 = objc_msgSend(&OBJC_CLASS___NSConnection, "defaultConnection"); objc_msgSend(v5, "setRootObject:", v4); objc_msgSend(v5, "registerName:", CFSTR("com.foo.my_test_service")); v6 = objc_msgSend(&OBJC_CLASS___NSRunLoop, "currentRunLoop"); objc_msgSend(v6, "run"); return 0; }
|
oc这些函数调用基本都变成objec_msgSend(a,b,xxx)
了,不过不太影响看,我的理解就是
a.b(xxx)
,大概这个样子,知道这行代码在做什么,对于逆向已经够了。
关键点在于找DO的对象以及和他绑定的方法。通过上面的方法,能找到:v4-->v3-->OBJC_CLASS___VendMe
,那么就是OBJC_CLASS___VendMe
这个对象了,下面是找和他绑定的方法。
直接找到的是:
1 2 3 4 5
| __objc_data:0000000100001198 _OBJC_CLASS_$_VendMe __objc2_class <offset _OBJC_METACLASS_$_VendMe, \ __objc_data:0000000100001198 ; DATA XREF: __objc_classlist:0000000100001060↑o __objc_data:0000000100001198 ; __objc_classrefs:classRef_VendMe↑o __objc_data:0000000100001198 offset _OBJC_CLASS_$_NSObject, \ __objc_data:0000000100001198 offset __objc_empty_cache, 0, offset VendMe_$classData>
|
然后根据classData找到 :
1 2 3 4 5
| __objc_const:00000001000010D8 VendMe_$classData __objc2_class_ro <0, 8, 8, 0, 0, offset aVendme, \ __objc_const:00000001000010D8 ; DATA XREF: __objc_data:_OBJC_CLASS_$_VendMe↓o __objc_const:00000001000010D8 offset _OBJC_INSTANCE_METHODS_VendMe, 0, 0, 0, 0> ; "VendMe" __objc_const:00000001000010D8 __objc_const ends __objc_const:00000001000010D8
|
最后
1 2 3 4
| __objc_const:00000001000010B8 _OBJC_INSTANCE_METHODS_VendMe __objc2_meth_list <18h, 1> __objc_const:00000001000010B8 ; DATA XREF: __objc_const:VendMe_$classData↓o __objc_const:00000001000010C0 __objc2_meth <offset sel_foo_, offset aVv2008i16, \ ; -[VendMe foo:] ... __objc_const:00000001000010C0 offset __VendMe_foo__>
|
依靠对classData
的引用看。
其实也可以的知道了DO对象的对象名之后直接搜函数列表,比如:VendeMe:
。 这个是在有符号的情况下,不过符号这个看运气了,不一定有 符号。
VendeMe:foo
1 2 3 4
| void __cdecl -[VendMe foo:](VendMe *self, SEL a2, int a3) { NSLog(CFSTR("%d"), (unsigned int)a3); }
|
用户的参数从a3开始算。
这类服务漏洞挖掘
主动fuzz:找到处理函数,确定类型,然后主动写客户端fuzz。
被动fuzz:hook处理点,但是要一个一个hook,然后修改数据。
代码审计:找处理函数,人肉看逻辑。
引用
revisiting-apple-ipc