Mac Reversing

You are currently browsing the archive for the Mac Reversing category.

Hello,

I have just added a page to collect crackmes for OS X. I have added the ones that I already had and some recommended from user comments. Since corruptfire.com seems down I cannot retrieve the other ones they had.

If you have more crackmes please mail them to me so I can add them to the page. It would be nice to start having more crackmes developed for OS X.

fG!

I have been thinking about this and how to get this blog back to life. My free time has been almost zero but I miss the motivation to put my brain to tinker and create new things to publish, because reversing and everything around it sometimes is a great relaxing activity for me.

The last couple of days I had to revisit one of my favourite books ever, where it is written that “DO NOT COVET YOUR IDEAS: Give away everything you know, and more will come back to you.”. This was my original spirit and I miss it, even if the world is full of idiots who just want personal profit.

Since I don’t have so much time and I always wanted others to contribute, I’m searching for people who want to contribute and have a writing place at this blog. If people really miss this and find it a source of knowledge and value then they should contribute to it because it’s the only way to advance OS X reversing knowledge. Drop me an email so we can talk about it, if you are interested.

The new format will not have anything related to cracking and reversing protections, unless it’s about very specific bits that are really important and advance the general knowledge. I want to focus more on tools and new tricks, malware, packers and all that stuff. I know it will always have some impact into the protections world but if idiots want to release stuff then it is their problem. The latest news about a global treaty for copyright protections and infrigements should change the game.

So let’s see if this can go forward or not. If yes then I will get all the content back (non cracking related) and do my best to get this ship sailing once again.

fG!

Some folks were complaining about problems with OTX and Snow Leopard so I decided to boot my Snow Leopard install and give it a try… Well they were right since Snow Leopard compiles 64 bit binaries by default. OTX v0.16b seems to have problems so you will need to download from the SVN and compile yourself the most recent version. If you try to follow the tutorial you will have problems because you will have 64 bit registers (rax instead eax, for example) so you need to adapt the tutorial.

Here is a short list of problems that I was able to quickly identify:

  • OTX doesn’t support x86_64 binaries. Download latest version from the SVN.
  • gdbinit doesn’t work with x86_64 binaries. Need to update its code to support 64 bit registers.
  • onyx the black cat and rootkits don’t work. nsysent location was moved, this article explains how to find it (nice thing, less work for me!).
  • hummm I had something else but I just forgot :)

I will try to update the tools and texts to this new “world”. Meanwhile, if you are quicker than me and do it first then feel free to send it to me so I can publish them.

That’s it for now. Have fun!
fG!

Update: You can always use the -m32 option to gcc to compile 32 bit binaries :)

After having found the source of the gdb anti-debug trick, I started modifying gdb to work around the problem and fix the number of sections on the fly (it’s damn simple to calculate the real number of sections !!!). I was coding on a long train trip and everything was going great… My hack worked and gdb fixed and loaded the file without a problem… Next step was to run the program but when I tried I had this surprise:

gdb$ r
 
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x00005040
0x8fe146d8 in __dyld__ZN16ImageLoaderMachO13parseLoadCmdsEv ()
--------------------------------------------------------------------------[regs]
EAX: 00005008  EBX: 8FE143C1  ECX: 44001048  EDX: 00000000  o d I t s z a p c
ESI: 00001054  EDI: 00000001  EBP: BFFFE0C8  ESP: BFFFE060  EIP: 8FE146D8
CS: 0017  DS: 001F  ES: 001F  FS: 0000  GS: 0037  SS: 001F
--------------------------------------------------------------------------[code]
0x8fe146d8:     0f b6 50 38                   movzx  edx,BYTE PTR [eax+0x38]
0x8fe146dc:     80 fa 09                      cmp    dl,0x9
0x8fe146df:     75 e2                         jne    0x8fe146c3
0x8fe146e1:     8b 55 08                      mov    edx,DWORD PTR [ebp+0x8]
0x8fe146e4:     81 4a 74 00 80 00 00          or     DWORD PTR [edx+0x74],0x8000
0x8fe146eb:     eb e0                         jmp    0x8fe146cd
0x8fe146ed:     8b 55 08                      mov    edx,DWORD PTR [ebp+0x8]
0x8fe146f0:     81 4a 74 00 08 00 00          or     DWORD PTR [edx+0x74],0x800
--------------------------------------------------------------------------------

Holy crap ! I must have goofed somewhere with my code… This ocurred due to the fact that I had never tried to run the program while I was debugging the problem and modifying gdb so it was natural to think it was my problem (I didn’t even bothered to look to the function name that gave the error! doh ;) ). Gave a quick review into the code and it seemed ok since it was a simple modification. Tried to run the program outside gdb and voila, the real problem has shown itself.

dyldcrash

dyldcrashreport

After some tests, the conclusion was that the problem ocurred for most values except 0xFFFFFFFF (else the trick wouldn’t work!). I downloaded dyld source code but I couldn’t find where the crash was located. Train arrived to destination, weekend time and in the next week I had to finish some documentation since I’m leaving my current job and joining an MBA program. Today I was bored and decided to get back to the problem. After disassembling dyld, setting a breakpoint at ImageLoaderMachO::parseLoadCmds and tracing, I finally found the interesting piece of code:

+753  8fe146a1  8b5630                  movl        0x30(%esi),%edx <- code for case LC_SEGMENT_COMMAND
+756  8fe146a4  8d4638                  leal        0x38(%esi),%eax
+759  8fe146a7  89d1                    movl        %edx,%ecx
+761  8fe146a9  c1e106                  shll        $0x06,%ecx
+764  8fe146ac  8d1491                  leal        (%ecx,%edx,4),%edx
+767  8fe146af  8d0c10                  leal        (%eax,%edx),%ecx
+770  8fe146b2  39c8                    cmpl        %ecx,%eax <- for condition sect < sectionsEnd
+772  8fe146b4  0f8336ffffff            jael        0x8fe145f0
+778  8fe146ba  0fb65038                movzbl      0x38(%eax),%edx
+782  8fe146be  80fa09                  cmpb        $0x09,%dl <- if ( type == S_MOD_INIT_FUNC_POINTERS )
+785  8fe146c1  741e                    je          0x8fe146e1
+787  8fe146c3  80fa0a                  cmpb        $0x0a,%dl <- else if ( type == S_MOD_TERM_FUNC_POINTERS )
+790  8fe146c6  7434                    je          0x8fe146fc
+792  8fe146c8  80fa0f                  cmpb        $0x0f,%dl <- else if ( type == S_DTRACE_DOF )
+795  8fe146cb  7457                    je          0x8fe14724
+797  8fe146cd  83c044                  addl        $0x44,%eax <- ++sect
+800  8fe146d0  39c1                    cmpl        %eax,%ecx <- for condition sect < sectionsEnd
+802  8fe146d2  0f8618ffffff            jbel        0x8fe145f0
+808  8fe146d8  0fb65038                movzbl      0x38(%eax),%edx <- CRASH HERE
(...)

Where the original source code is:

case LC_SEGMENT_COMMAND:
{
const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
#if IMAGE_NOTIFY_SUPPORT
const bool isDataSeg = (strcmp(seg->segname, "__DATA") == 0);
#endif
const struct macho_section* const sectionsStart=(struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
const uint8_t type = sect->flags & SECTION_TYPE;
if ( type == S_MOD_INIT_FUNC_POINTERS )
fHasInitializers = true;
else if ( type == S_MOD_TERM_FUNC_POINTERS )
fHasTerminators = true;
else if ( type == S_DTRACE_DOF )
fHasDOFSections = true;
#if IMAGE_NOTIFY_SUPPORT
else if ( isDataSeg && (strcmp(sect->sectname, "__image_notify") == 0) )
fHasImageNotifySection = true;
#endif
}
}
break;

Ecx holds the value of sectionsEnd and the number of sections described in the header affects it’s value, because sectionsEnd = &sectionsStart[seg->nsects]. Dyld suffers from a similar problem to gdb, because it trusts the header information without any further check. One small thing was left to explain… Why 0xFFFFFFFF works ? By setting a breakpoint at 0x8fe146b2 it’s very easy to see why! If nsects is 0x00FFFFFF then ecx=44001048, if nsects is 0x0FFFFFFF then ecx=40001048, if nsects if 0xFFFFFFFF then ecx=00001048. So 0xFFFFFFFF overflows sectionsEnd and that’s why the tricks works for that value !

Unless Apple fixes this dyld bug (maybe I should report it) there’s no much sense in fixing gdb except to produce a warning about a possible usage of that anti-debug trick. The bug itself is pretty easy to fix in gdb, dyld and other programs (HT Editor, IDA, etc) because the real number of sections can be calcuted using the cmdsize field from the load command. It’s not possible to play tricks with this field (I did a few tests and it doesn’t work) so it must be a reliable value.

I think I can finally close this bug hunt ! It was fun… Only thing left is to release the patches I did for gdb. I must finish the fixes to other read commands and then I will publish them.

That’s all for now folks!
fG!

Today I bring you something from the old projects trunk ! Like many other millions of people I enjoy playing online Texas Hold’em. I started with Pokerstars three years ago, and after a while, diabolical ideas came to my head about reversing the client to have a peek into their communication protocol (what else were you expecting ? I love to break things!).

The project was on hold for a long time (started when Windows was my daily OS). Today, I had a smile when I saw an article about reversing pokerstars protocol. It’s entitled Reversing The Pokerstars Protocol, Part 1: Compression and transport basics, and it’s available here. The author already implemented the MITM proxy, the step where I had stopped. I did had a peek at the communications protocol since it’s pretty easy to hijack the code before it’s crypted and after it’s decrypted (Windows browser trojans and keyloggers use that technique). I even remember that I was about to hijack the openssl library because the first versions of the Mac client were linked against the external openssl library, so it was trivial for example to recompile Openssl with a printf dumping everything. This hole was closed on newer versions but it’s still very easy to attach the debugger and it should be even easier with Dtrace (haven’t tried yet!). It’s much easier to hijack the data before it’s crypted then to code the MITM proxy and reverse the compression and so on.

Since someone gave the first public step I will release a rather useless piece of code that allows you to crypt and decrypt the ini files. The algorithm is still the same since 2006 and it works in Windows and Mac. It’s pretty easy to reverse! The ini files are gzipped, so you first decrypt and then gunzip or you gzip and then crypt. There’s not much info to peek inside the ini files but it might be useful in the future. Maybe one day I will get back to it… I would love to find any vulnerabilities into their protocols!

I hope the Vegas guys don’t come after me (yeahhh too much Vegas movies, damn American movies hehehe).

The gdb bug from previous post is still not over ! The dyld (dynamic linker) has problems and it crashes with some values. I didn’t spot this one on time for the post because I wasn’t executing my test program, just loading into gdb so I could track the bug. I’m trying to track the bug inside dyld (this is when it’s great to have open source code by Apple).

That’s it !
fG!

Pokerstars_decrypter.c SHA1(Decrypter.c)= dd0ebc2e75f512710f9f992048254bb012af5824

Pokerstars_crypter.c SHA1(Crypter.c)= a3ae440b6b78a50bd73ac446b51a626206405d6f

Well, it seems this is the GDB post season ! The past days have been dedicated to mess around with gdb source code and today I have what I think it’s a nice story to tell.

After hacking off my old wish of having the disassembly raw bytes to be printed (like Ollydbg, Softice, IDA, otx, etc…) I was interested in trying to fix one anti-debug trick. This presentation by nemo shows an anti-debug trick that works against gdb and others. The original description is: “If you set the “number of sections” field in a SEGMENT_COMMAND to 0xffffffff many of the popular debuggers will crash. This bug exists in gdb (gnu debugger), IDA pro and the HTE editor.”

Armed with my great reversing skills and my lame C skills I started searching for the problem… Oh man have I told you that gdb code is a nightmare ? Probably I did ! It’s a freaking nightmare…

This is what happens when I modified the number of sections at __TEXT segment (any segment does the trick):

$ gdb ./segment_command_number_of_sections_antidebug
GNU gdb 6.3.50-20050815 (Apple version gdb-768) (Thu Aug 13 13:17:30 UTC 2009)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin"... "./segment_command_number_of_sections_antidebug": not in executable
format: File format not recognized
 
gdb$ r
No executable file specified.
Use the "file" or "exec-file" command.

Gdb can’t work with this modified executable file so the anti-debug trick is doing it’s job very well. Bfd is gdb component responsible for parsing the file headers and other stuff. It has it’s own directory at gdb source and you can find there many files related to the different formats it can parse. The mach-o.c should be naturally our main target and the error message the starting point. After a few attempts at inserting debug messages and many compiles, I finally managed to trace where the error was happening. As usual, this is the flow:
(…) bfd_mach_o_scan_read_command -> bfd_mach_o_scan_read_segment_32 -> bfd_mach_o_scan_read_segment -> bfd_mach_o_scan_read_section -> bfd_mach_o_scan_read_section_32

The important piece of code at bfd_mach_o_sca_read_command is:

case BFD_MACH_O_LC_SEGMENT:
if (bfd_mach_o_scan_read_segment_32 (abfd, command) != 0)
return -1;
break;

Returning -1 signals an error and sets prints the error message that will be displayed by bfd_set_error (bfd_error_file_not_recognized); @ format.c (still at bfd directory). There are two instances of this function call, I used a few simple printf to find which one was used (simple tricks always work best!). The next important piece of code is located at bfd_mach_o_scan_read_segment. Printfs were used once again to confirm the correct piece of code.

for (i = 0; i < seg->nsects; i++)
{
bfd_vma segoff;
if (wide)
segoff = command->offset + 64 + 8 + (i * 80);
else
segoff = command->offset + 48 + 8 + (i * 68);
 
if (bfd_mach_o_scan_read_section
(abfd, &seg->sections[i], segoff, wide) != 0)
return -1;
}

That was the return responsible for the error message. I modified it to 0 and voila, gdb worked without a problem. Time to go deeper into bfd_mach_o_scan_read_section_32. This function had two return -1, so printf to the rescue and the first one is the guilty piece of code.

static int
bfd_mach_o_scan_read_section_32 (bfd *abfd,
bfd_mach_o_section *section,
bfd_vma offset)
{
unsigned char buf[68];
 
bfd_seek (abfd, offset, SEEK_SET);
if (bfd_bread ((PTR) buf, 68, abfd) != 68)
return -1;
 
(...)

If bfd_read can’t retrieve 68 bytes, then it’s an error… Once again, I used printfs (this is getting annoying hehe) to check what offset and what sizes were read and returned. That made clear that failure was due to less than expected retrieved bytes. Let me get back to aaa to resume the problem.

for (i = 0; i < seg->nsects; i++)
{
bfd_vma segoff;
if (wide)
segoff = command->offset + 64 + 8 + (i * 80);
else
segoff = command->offset + 48 + 8 + (i * 68);
 
if (bfd_mach_o_scan_read_section
(abfd, &seg->sections[i], segoff, wide) != 0)
return -1;
}

Seg->nsects holds the number of sections from the header. So if the header says it has 1000 sections, this routine will try to read 1000 sections. I’m pretty sure you can now spot the problem! In reality the executable doesn’t have 1000 sections so the routine will keep reading things outside the header. If the program size is less than the size that will be read by the routine, then an error will be raised (by that small piece of code that expects 68 bytes). In reality the anti-debug trick doesn’t require the number of sections to be 0xFFFFFFFF but just large enough to be bigger than the program size or not be evenly divisible by 68 bytes. Of course 0xFFFFFFFF is the best bet but it’s not the only value that works.

I’m not sure if nemo knew this or just fuzzed the header (most probably he knew since he rules ;) ) but I had a lot of fun tracking this bug/problem !

The problem here is that gdb blindly trusts the information from the mach-o header. This is bad design from a security point of view. You shouldn’t trust external input! Gdb should parse the whole file and verify if the information is true and consistent with the header, else it opens the door for this kind of tricks. An easy workaround to avoid the error is to check if the size given from the number of sections is compatible with the executable size. It’s a lame workaround but it saves you from editing the binary and fixing the header. Yes, I’m a bit lazy sometimes but I do believe that computers exist to do the work for me ;)

About the raw bytes disassembly display, have a look at this example (colours lost in copy&paste):

Breakpoint 1, 0x000023f0 in ?? ()
--------------------------------------------------------------------------[regs]
EAX: 000023F0  EBX: 00001000  ECX: 00000001  EDX: 00000000  o d I t S z A P c
ESI: 00000000  EDI: 00000000  EBP: 00000000  ESP: BFFFF8D4  EIP: 000023F0
CS: 0017  DS: 001F  ES: 001F  FS: 0000  GS: 0037  SS: 001F
--------------------------------------------------------------------------[code]
0x23f0:     6a 00                         push   0x0
0x23f2:     89 e5                         mov    ebp,esp
0x23f4:     83 e4 f0                      and    esp,0xfffffff0
0x23f7:     83 ec 10                      sub    esp,0x10
0x23fa:     8b 5d 04                      mov    ebx,DWORD PTR [ebp+0x4]
0x23fd:     89 5c 24 00                   mov    DWORD PTR [esp+0x0],ebx
0x2401:     8d 4d 08                      lea    ecx,[ebp+0x8]
0x2404:     89 4c 24 04                   mov    DWORD PTR [esp+0x4],ecx
--------------------------------------------------------------------------------

I will post the patches and whole source package soon.

As usual, have fun !
fG!

« Older entries