Onyx the Black Cat v0.4 for Snow Leopard

I had this one working for a long time but I hadn’t released it because I was trying to hijack fork and vfork calls. My objective was to introduce an int3 so I could attach the debugger to a selected process. At that time I suspected that VLOK was forking and I couldn’t debug the new process since follow on fork gdb function isn’t implemented in OS X (so this looks like a good idea for a protection ;-)). My idea was to inject an int3 or pause the new process so I could attach another gdb to it. Those attempts failed and one of these days I need to get back to the problem and think better about it (if you have a solution for how to do it in the kernel module feel free to contribute!!!).

The method to grab sysent address changed again in Snow Leopard and for now there is an hardcoded address. The method is to find nsysent address (nm /mach_kernel | grep nsysent) and then subtract 0x2850 from it. Matthie Suiche described a method in the paper ADVANCED MAC OS X PHYSICAL MEMORY ANALYSIS.
“Under Mac OS X Snow Leopard (10.6), we have to proceed with a different methodology. First, we have to retrieve the value of nsysent variable, then we multiply its value with the size of sysent structure, and then we subtract this value to nsysent offset to obtain the offset of sysent table.”

I would prefer a more elegant solution to this 🙂

The structures had to change and more stuff had to be ripped off from xnu kernel headers. They are still incomplete but what is there is enough for current purposes.

Here it is:
onyx-the-black-cat-v0.4.tgz
(SHA1(onyx-the-black-cat-v0.4.tgz)= 5dff3c4a9246f2886b470aa0ab60b5e237ca3659)

Have fun,
fG!

36 thoughts on “Onyx the Black Cat v0.4 for Snow Leopard

  1. It seams that sctructures.h are redefining some structs. I’m getting lot of compiling errors like:

    In file included from /Users/…/Downloads/onyx-the-black-cat-snowleopard/onyx_the_black_cat.c:30:
    /Users/…/Downloads/onyx-the-black-cat-snowleopard/structures.h:439: error: redefinition of ‘struct _pcred’
    /Users/…/Downloads/onyx-the-black-cat-snowleopard/structures.h:449: error: redefinition of ‘struct _ucred’
    /Users/…/Downloads/onyx-the-black-cat-snowleopard/structures.h:457: error: redefinition of ‘struct extern_proc’
    /Users/…/Downloads/onyx-the-black-cat-snowleopard/structures.h:513: error: redefinition of ‘struct kinfo_proc’
    /Users/…/Downloads/onyx-the-black-cat-snowleopard/structures.h:515: error: redefinition of ‘struct eproc’

    1. Yeah I was going to answer but you already found by yourself. Have to change project default to use 10.6 SDK.

      Thanks for the test 🙂

  2. Ok. The kext is working. Now PT_DENY_ATTACH/P_LNOATTACH is blocked. But for some reason functions/symbols are not loaded.

    I keep getting:

    Function “xxxxx” not defined.
    Make breakpoint pending on future shared library load? (y or [n])

    But those functions not only are present on the dump (class-dump) and showed by otx, also ARE called (checked sing dtrace)

    #!/usr/sbin/dtrace -qs

    pid$1::objc_msgSend:entry
    {
    self->isa = *(long *)copyin(arg0,4);
    printf(“-[%s %s]\n”,copyinstr(*(long *)copyin(self->isa + 8,
    4)),copyinstr(arg1));
    }

    Any idea why ?

  3. Playing around with traced i just found one interesting object address. (Still I can’t read function names in gdb).

    gdb$ print (int)[0x42e150 licenseIsValid]
    $1 = 0x0

    How can I add a breakpoint into this function?

    gdb$ break licenseIsValid
    Function “licenseIsValid” not defined.

    Thank you!

  4. Can’t get it to work. If I try to load it a second time my mac crashes and I have to reboot.

    navaja:~ navaja$ sudo kextload /System/Library/Extensions/onyx-the-black-cat.kext
    /System/Library/Extensions/onyx-the-black-cat.kext failed to load – (libkern/kext) kext (kmod) start/stop routine failed; check the system/kernel logs for errors or try kextutil(8).

    navaja:~ navaja$ sudo dmesg
    npvhash=4095
    PAE enabled
    64 bit mode enabled
    Darwin Kernel Version 10.3.0: Fri Feb 26 11:58:09 PST 2010; root:xnu-1504.3.12~1/RELEASE_I386

    [onyx-the-black-cat] Starting patching …
    [onyx-the-black-cat] Finding sysent table…
    [onyx-the-black-cat] Sanity check: verifying if number of syscalls arguments are the expected ones
    [onyx-the-black-cat] Sanity check: sanity check failed, could not find sysent table.
    [onyx-the-black-cat] Error: Cannot find sysent table
    Kext com.reverse.put.as.kext.onyx_the_black_cat start failed (result 0x5).
    Kext com.reverse.put.as.kext.onyx_the_black_cat failed to load (0xdc008017).
    Failed to load kext com.reverse.put.as.kext.onyx_the_black_cat (error 0xdc008017).

    Any suggestions?
    Perhaps it has something to do with the pt_deny_attach.kext I was using before?
    http://bit.ly/96OzCq

    1. Most probably has to do with the fact that you are using a 64bit kernel!
      Issue this command “nm /mach_kernel | grep _nosys” and tell me what’s the output 🙂

      Totally forgot about 64bit kernels!

  5. The output is: ffffff800048996c T _nosys

    Does this mean that I should change this line in onyx_the_black_cat.c:
    table = (struct sysent *) ( 0x00831730 – 0x2710 );
    to this:
    table = (struct sysent *) ( 0x0048996c – 0x2710 );
    ?

    1. The address should be 0xffffff800048996c. But the substract size must be different since we are talking about 64 bit addresses. Let me check and compute the right value and I will come back to you 🙂

  6. navaja:~ navaja$ nm -arch i386 /mach_kernel | grep _nosys
    004910f3 T _nosys

    navaja:~ navaja$ nm -arch x86_64 /mach_kernel | grep _nosys
    ffffff800048996c T _nosys

  7. And here is the output for _nsysent for 32bit and 64bit arch:

    $ nm -arch i386 /mach_kernel | grep _nsysent
    00831730 D _nsysent
    0085df7c S _nsysent_size_check

    $ nm -arch x86_64 /mach_kernel | grep _nsysent
    ffffff800065ac90 D _nsysent
    ffffff8000695830 S _nsysent_size_check

  8. Hello again fG!

    I just read that you was working on “Remote Buddy” too. Could you share your experience?. I’m a little bit lost on this one. I can’t get symbols correctly, and I’m running out of luck using just otx/hexeditor.

    I removed _ptraced calls and could locate CopyCore address but i don’t know if that helps. What are RBuddy encrypting on directories?.

    Thank you for all your work by the way.

    1. I don’t remember much about Remote Buddy except that the author is very active in changing protections and he was a bit annoyed by my article. I would expect him to be improving the protection and making things harder. The nice trick he was using was the extended attributes to store information. That’s the reason that I coded that feature into onyx the black cat so I could easily monitor any attempts to it.

  9. In 10.6.4, substracting 0x2850 will not work, It’s now located at -0x28b0 from the _nsysent symbol.

    table = (struct sysent *) ( 0x008317f0 – 0x28b0 );

    1. – table = (struct sysent *) ( 0×008317f0 – 0×28b0 );

      This causes my 10.6.4 mbp to panic, is this for 32bit? How does one find the 64bit values?

      Thanks!

  10. navaja you get it working on 64 bit kernel?? can you telll us which values you used in :

    table = (struct sysent *) ( 0x008317f0 – 0x28b0 );

    I used this ones:

    table = (struct sysent *) ( 0xffffff800065ad90 – 0x1D08D4 );

    it compiles but it throws two warnings:

    integer constant is too large for ‘long’ type
    cast to pointer from integer of different size

    how do I transform this on to 64bit??? any ideas??

  11. 10.6.4 64-bit:
    table = (struct sysent *) ( 0xFFFFFF80006569C0);

    Make sure to change to the 10.6 SDK and change the architectures from ppc and i386 to x86_64. A 64-bit address will not fit into a 32-bit pointer, so you will get a warning and loading the kext will crash your Mac.

  12. I don’t suppose anyone has figured out the magic numbers for 10.6.5 yet, have they?

    Here’s the output from above. I’m running an i386 kernel

    nm -arch i386 /mach_kernel | grep _nosys
    00495402 T _nosys

    nm -arch i386 /mach_kernel | grep _nsysent
    00831790 D _nsysent
    0085df9c S _nsysent_size_check

    I was trying to grok, from your comments, and from some of the linked articles, the algorithm for getting the offset, but I couldn’t. fG! if you post that, I can probably take it from there.

    1. Hi,

      I don’t have really an algorithm and usually use an hex editor and the old offsets to land more or less in the range of the right address.
      The Mac Hackers Handbook book describes an algorithm to find it. I’ve been for quite some time to test it out. I really hate that stuff to be hardcoded. It’s not pretty 😉
      Well I just upgraded to 10.6.5 so I will need to find it. Probably will do it tomorrow. I will post a comment here.

      fG!

    1. Hi again,

      The info you are looking is:
      table = (struct sysent *) ( 0x00831790 – 0x28b0 );

      You can use something like this to use between different versions:
      extern const char osrelease[];
      if (strcmp(osrelease,”10.0.0″) == 0)
      {
      table = (struct sysent *) ( 0x00831730 – 0x3710 );
      }
      else if (strcmp(osrelease,”10.1.0″) == 0) {
      table = (struct sysent *) ( 0x00831730 – 0x2710 );
      }
      else if (strcmp(osrelease,”10.2.0″) == 0) {
      table = (struct sysent *) ( 0x00831870 – 0x2850 );
      }
      //
      else if (strcmp(osrelease,”10.3.0″) == 0) {
      table = (struct sysent *) ( 0x00831730 – 0x2710 );
      }
      // OK
      else if (strcmp(osrelease,”10.4.0″) == 0) {
      table = (struct sysent *) ( 0x008317f0 – 0x28b0 );
      }
      else if (strcmp(osrelease,”10.5.0″) == 0) {
      table = (struct sysent *) ( 0x00831790 – 0x28b0 );
      }

      Have fun!

    1. Find the address of nosys and exit symbols, then search for them in mach_kernel (don’t forget Intel is little endian). The first match should be near the start of the sysent table (check Braeden Thomas that’s linked in a previous post). Then you can use old values to land near the range of the number of syscalls (download xnu kernel to verify the number for that version). If they don’t match, its’s a matter of searching near.

      Not the best method but it works 🙂

  13. Hi,

    at first, great Site! I can’t get onyx to work on my snow leopard. When i try to load the kext, i get a validation error.

    kextutil:
    /System/Library/Extensions/onyx-the-black-cat.kext is invalid; can’t resolve dependencies.
    /System/Library/Extensions/onyx-the-black-cat.kext is invalid; can’t resolve dependencies.
    /System/Library/Extensions/onyx-the-black-cat.kext is invalid; can’t resolve dependencies.
    /System/Library/Extensions/onyx-the-black-cat.kext has problems:
    Validation Failures:
    Kext has a CFBundleExecutable property but the executable can’t be found:
    onyx-the-black-cat

    system.log:

    com.apple.kextd[17]: Can’t load /System/Library/Extensions/onyx-the-black-cat.kext – validation problems.

    com.apple.kextd[17]: Failed to load /System/Library/Extensions/onyx-the-black-cat.kext – (libkern/kext) validation failure (plist/executable).

    what i do wrong?

    btw.: table = (struct sysent *) ( 0×00831790 – 0x28b0 );

    works for me on SL

    thx

  14. fG!, please help me to find the 64bit values for Mountain Lion 10.8.x
    thx
    here is my info:

    nm -arch x86_64 /mach_kernel | grep _nsysent
    ffffff8000839818 D _nsysent
    ffffff80008db150 S _nsysent_size_check

    nm -arch x86_64 /mach_kernel | grep _nosys
    ffffff80005767e0 T _nosys

      1. I tried several times and always got the total kernel panic by using the value from bruteforce tool 🙁
        Here is what i did:

        1. Add kmem to /Library/Preferences/SystemConfiguration/com.apple.Boot.plist

        Kernel Flags
        kmem=1

        2. reboot
        3. sudo ./bruteforcesysent
        _____ _ _____
        | __ |___ _ _| |_ ___| __|___ ___ ___ ___
        | __ -| _| | | _| -_| __| . | _| _| -_|
        |_____|_| |___|_| |___|__| |___|_| |___|___|
        Bruteforce sysent address v0.1 – (c) fG!
        ———————————————
        [OK] Address of interrupt 80 stub is 0xffffff800cacdf50
        [ERROR] Error while trying to read from kmem
        [OK] Found kernel mach-o header address at 0xffffff800ca00000
        [OK] Found __DATA segment at 0xffffff800d000000 (size:0xe3000)!
        [DEBUG] exit() address is 0xffffff800cd56390
        [OK] Found sysent address at 0xffffff800d055840

        4. Edit the line 141 in onyx_the_black_cat.c to:
        table = (struct sysent *) ( 0xffffff8004e55840 );

        5. sudo cp -rf onyx-the-black-cat.kext /System/Library/Extensions
        6. sudo kextload /System/Library/Extensions/onyx-the-black-cat.kext

        -> Kernel Panic 🙁

        1. You missed the point. You can’t put a fixed address because KASLR will change it everytime you reboot.
          What you need to do is to incorporate the brute force method in the kernel module so it searches the right sysent address everytime you load it.

          The method works flawlessly with Mountain Lion 🙂

          1. I got this ASLR. I am not the c-programmer to merge the code of brute force method into the kernel module 🙁
            Could you please release a version for Mountain Lion?

Leave a Reply

Your email address will not be published. Required fields are marked *