Mac Reversing

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

I smile when I think about this “feature”! I liked it so much that things got out of control and I wrote a crackme to show it. It happens because Apple doesn’t follow their own documentation/specification and the reversing tools of the trade do. The result is that IDA terminates, disassemblers output the wrong disassembly, strings are messed up, lldb disassembles the wrong code (not gdb), class-dump will fail, and the reverser looks at a weird Mach-O header.

In the end, it’s just a funny illusion :-)

If you try to load the crackme into IDA, it will complain of negative sizes and/or offsets. Otool also outputs weird stuff such as sections past end of file. The problem applies to the section command and a few of its fields. The 32 bits version of this structure is:

struct section { /* for 32-bit architectures */
	char		sectname[16];	/* name of this section */
	char		segname[16];	/* segment this section goes in */
	uint32_t	addr;		/* memory address of this section */
	uint32_t	size;		/* size in bytes of this section */
	uint32_t	offset;		/* file offset of this section */
	uint32_t	align;		/* section alignment (power of 2) */
	uint32_t	reloff;		/* file offset of relocation entries */
	uint32_t	nreloc;		/* number of relocation entries */
	uint32_t	flags;		/* flags (section type and attributes)*/
	uint32_t	reserved1;	/* reserved (for offset or index) */
	uint32_t	reserved2;	/* reserved (for count or sizeof) */
};

Let’s start with the one that produces the “best” results: offset. The definition at the reference document is:
“An integer specifying the offset to this section in the file.”

My interpretation of this is (should be?) the offset (anywhere) in the file where the code/data for the section is located at. That makes sense right? It’s an offset so in theory it can be located anywhere in the file – it doesn’t need to be sequential or in a specific order. Once again, it’s open for some kind of abuse :-)

What happens if you change the offset value to somewhere else?
IDA, for example, will respect the content of the offset field and try to read the data pointed by it. Want to do a simple test? Grab a normal file, change the cstring section offset, save and load into IDA. Voila, the strings are now “obfuscated” because IDA is reading the wrong data.

That is fun, right? And if you try to run the modified binary, it works fine! That is, sort of, unexpected. Try the same trick with the text section. Now it’s the program code that is all wrong and it still runs fine. Hum…

What is happening? That is the fun part. I think that a good picture for this is that the kernel loads and maps the binary in a linear way from the disk and ignores the offset field.
The execve() system call is explained in detail starting page 812 in the great Mac OS X Internals book. The exec_mach_imgact() function (bsd/kern/kern_exec.c) calls load_machfile(), which is responsible for load executable, handle certain mach-o load commands, etc.
@bsd/kern/kern_exec.c

        /*
         * Actually load the image file we previously decided to load.
         */
        lret = load_machfile(imgp, mach_header, thread, map, &load_result);

Inside load_machfile(), we have a call to parse the new binary, parse_machfile().
@bsd/kern/mach_loader.c

        lret = parse_machfile(vp, map, thread, header, file_offset, macho_size,
                              0, result);

We can find there a nice description of this function:

/*
 * The file size of a mach-o file is limited to 32 bits; this is because
 * this is the limit on the kalloc() of enough bytes for a mach_header and
 * the contents of its sizeofcmds, which is currently constrained to 32
 * bits in the file format itself.  We read into the kernel buffer the
 * commands section, and then parse it in order to parse the mach-o file
 * format load_command segment(s).  We are only interested in a subset of
 * the total set of possible commands.
 */

Scrolling down that function you can observe a cycle that will process a subset of all possible commands. The section commands are found inside a LC_SEGMENT/LC_SEGMENT_64 command, so you are interested in giving a look at load_segment(). There you can observe that verifications are only done at segment command level, never at section level (that’s why we can’t mangle the segment command :-) ).
When parse_machfile() returns, all parsing is done, linker is loaded and soon the program entrypoint will be called. The binary was mapped as it is found in the disk (why I picture it in a linear way) and the section info wasn’t used for anything. There’s an implicit assumption that the binary will be formatted correctly.

Is this behaviour correct? In my opinion, it’s not. The kernel does not respect the Mach-O specification. Or am I abusing my interpretation of the docs and the implicit assumption is correct? In a age of so much distrust (and wasted money) regarding user input this kind of assumptions should be made explicit and verified accordingly.

By the way, you should continue to read about the full load sequence – there’s another fun trick hidden in the crackme ;-) .

You can also change the flags, size, section and segment names, and the order of the sections. That will confuse the tools and you, the reverser. What you need to do is to make the same assumption as the kernel and ignore those fields. That seems a bit odd, right?

I hope you have enjoyed this one and motivates you to spend some time with xnu and dyld.

Have fun,
fG!

Update:
This is a small PoC that implements the trick described above. The code is only for 32bits, non-fat binaries, console targets. If applied to Objective-C the target will not load because not all sections can be mangled.

manglemacho.c.gz
SHA256(manglemacho.c.gz)= d79a612b72130732d7e47b2925fba7fc0b63824622d05f08e7f33641d522a8b5

Update 2:
As a matter of fact, all the fields in each section can be 0, without any adverse consequences (except the mod_init_func). I had played with this and didn’t took any notes. If there’s no further obfuscation IDA is smart (in some cases) and can disassemble because of the valid entrypoint. IDA is more confused if we play with the offset and sizes fields.
Set the second argument in this improved version to something if you want to zero all fields.

manglemacho_v0.3.c.gz
SHA256(manglemacho_v0.3.c.gz)= 4b33dc5f43bbb9114e6a8c18dba8894ca44b991cd69a5e5e54bfdcd03607fc9c

I developed this funny trick while trying to find a solution for a problem in a freelance project. It is pretty easy to implement and fun :-)

The trick consists in abusing the offset field in the dylib_command and pointing it to somewhere else. From the Mach-O File Format Reference, the command structures are:

struct dylib_command              struct dylib                            union lc_str
{                                 {                                       {
 uint_32 cmd;                      union lc_str name;                      uint32_t offset;
 uint_32 cmdsize;                  uint_32 timestamp;                      #ifndef __LP64__
 struct dylib dylib;               uint_32 current_version;                char *ptr;
}                                  uint_32 compatibility_version;          #endif
                                  }                                       }

The definition of the offset field is:
“A long integer. A byte offset from the start of the load command that contains this string to the start of the string data.”

Usually this field is always 0×18 (24 bytes). This means that the library name string is located after the dylib_command command, whose size is 24 bytes. Right now your evil brain should be interpreting that definition as “an offset (anywhere) that contains the start of the string data“. If not, don’t worry, evilness takes practice :-)

What happens if you put the string somewhere else and change the offset to point there? GDB crashes, otool can’t recognize the offset and so on.

Otool:

Load command 20
          cmd LC_LOAD_DYLIB
      cmdsize 88
         name ?(bad offset 28548)
   time stamp 2 Thu Jan  1 01:00:02 1970
      current version 30.0.0
compatibility version 1.0.0

GDB:

GNU gdb 6.3.50-20050815 (Apple version gdb-1344 + reverse.put.as patches v0.3) (Mon Aug 22 00:31:56 UTC 2011)
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 "x86_64-apple-darwin"...gdb-i386-apple-darwin(68831) malloc: *** mmap(size=18446744073709506560) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug

Fun stuff, right ? :-)
The problem with the debugger attach is that I assumed gdb would also crash if attached and forgot to try if it was true. It is a minor problem – the crackme is designed to resist a debugger attach.

The next trick is even more fun but requires some time to write the post. I didn’t took all the notes and I need to “rediscover” it to show you where the problem is.

Enjoy,
fG!

This Sunday I received a valid keygen solution for my crackme. Congratulations to the reverser who wishes to remain anonymous.

When the solution is available our brain stops thinking and goes into lazy mode. So, my question is when do you want to have me starting to explain some of the tricks used in that crackme? Right now? Next week? In a month?

I did some questions to the keygen author to better understand his attack. From some of his statements I think he attacked from the vector I imagined, which is probably the fastest way to attack this.

Let me know your vote on this.

Have fun,
fG!

My first OS X crackme is finally “ready”, after a long wait and some unnecessary teasing. “Ready” means that it is good enough to be released and hopefully give you some trouble to reverse and crack it. I still have many more ideas to implement and some areas could be more polished – it was time to take an executive decision and freeze the code. There are some assumptions (economists love this term :-) ) due to the crackme nature – if it was an application more fun games could be played. I hope I haven’t missed any simple hole/bug. It’s not an easy task to build something that you are constantly cracking and thinking about ways to defeat it. I haven’t cracked it myself but I have a few neat ideas on how to approach it. The real interest is to read and learn about your approaches and solutions.

This crackme started as a PoC for some tricks I found while working on a project. The original idea was to create something to demonstrate the issues but it got out of control and evolved into a crackme (I hate to lose and love a good challenge!). Two issues were sort of disclosed during an interview with the fruity company since my interest with overflows is almost 0 ;-) . The impact of this is that the crackme is certified to run on OS X 10.6.8 up to 10.7.2. Newer Lion releases is a question mark. It is 32bits only and has no ASLR, due to coding time restrictions. As a matter of fact, I started the PoC with ASLR support, which poses no big problems to the concepts behind this crackme.
The code does nothing malicious or destructive so it’s safe to run! It is only hostile to your reversing efforts ;-)

The challenge is to find the valid name/key pair and keygen it if you wish so. Use the “-h” argument for help. The crackme will accept the name and key via command line parameters, to make your life easier. Run with no parameters for the usual crackme questions.
I will disclose details as soon a solution is sent to me by mail or comment. If you wish to remain anonymous or keep the solution private just tell me.

Probably forgetting about some stuff so this post might be updated soon.

There are two or three little things taken from other people and proper credit will be given in due time.

This should be an advanced crackme with some unusual stuff in OS X (at least for me :-) ). I hope you enjoy reversing it and learn/develop some new tricks.
Hint: it is quite amusing that Apple doesn’t follow its own specifications ;-)

Have fun,
fG!

fg_crackme_nr1.gz
SHA256(fg_crackme_nr1.gz)= 9116e336f3979c1c68e63bec2868d193b6ccbf031e3521bdcdb7e14034c3c636

It sucks, sort of!

Let me rewind to the beginning :-)
I was very curious about this one because it was announced with great fanfare. I interpreted it as something more robust than it really is – maybe I was over enthusiastic with the “we know this will be cracked someday” sentence.
Some brief comments:

- There are no anti-debug measures.
- There are no binary integrity protections – patch whatever you want!
- It has an annoying constant polling for the license file (I observed at least 5 hits per second – what a meaningless waste of cpu!). This can be patched without any problems :-)
- If you patch the polling, the program continues to work. What this means? License is read once and sets something somewhere (you should know what is the real meaning of this). This is bad, real bad!
- Class-dump is able to extract the license structure, although the fields sequence seems incorrect.
- It has a buffer overflow at getenv – doesn’t check the size of HOME environment variable before strcpy it to an allocated buffer. Bang! It is just a detail. Maybe that something somewhere can be taken care with a “proper” HOME variable – that would be a cool crack (never saw this against a real target).

Audio scene groups usually have great crackers so this will be a nice feast to them. Alliance guys, you must raise your efforts, seriously!

You can find below the source code to a license file decryptor, in case you are curious about its format.
I thought a while about this and I really think there is NO great harm in releasing this code. It doesn’t crack anything and the decrypted version of the license is too damn easy to dump from memory.
Alliance, if you are reading this and disagree please tell me.

This was sort of fun (I am disappointed!) and it’s time to move to more interesting things.
If you are using this protection to protect your assets, make some pressure to be improved!

Enjoy,
fG!

decrypt_pa_licensefilev0.2.c.gz
SHA256(decrypt_pa_licensefilev0.2.c.gz)= 071458084a22f91b126389490737e33bb3a6f0d047e205545b0c36f21c8a7ba0

Update:
I noticed (again) that what I call 1st stage in the decryption code is just decoding of a binary encoding format (modified base64?). I had this in mind the first time I approached this but got sick in between and never remembered this again. I was closing the hex-editor and my brain connected the dots again. It went into plain disassembly reverse without caring of what was behind (not that I care much now). Just a lame detail, too late in the night LOL :-)

Let me start this with some sort of disclaimer. I do not support/condone stealing credit card information, logins, and other personal information. Disclosing security issues is always a double edge sword and a tricky problem with some politics in the mix. This problem was reported almost 3 months ago to Apple. It’s still not fixed after, at least, two iTunes releases. I perfectly understand the business side of fixing bugs and how business most of the times must come first (I have experience in critical environments where these type of problems can cost a lot of money and bad publicity).
But it also seems that governments and so on are exploiting security issues to do all kind of stuff on citizens. This is another interesting case (Google translate it). 3 months is more than enough to take a damn decision on this issue. If not, I maybe did a mistake. Live & learn :-)
So let’s start it…

The following problem seems like a pretty “lame” security issue with iTunes. I came across with it when one reader posted a comment about creating an iTunes plugin to have the same m3u removal functionality I did in my loader (original post here). My idea was to patch iTunes memory on the fly and remove the new m3u thing. To my surprise, this is a pretty easy thing to do with a plugin! Plugins are loaded into iTunes memory space (they are bundles/libraries) and can have total control over its memory. How? By using mach_task_self to acquire a port right (we don’t need any special/elevated permissions since the plugin shares the same permissions of iTunes) and then use mach_vm_write to patch whatever we want.

So how can we use this for devilish things? Well, we can capture those iTunes logins and credit card information. One alternative is to patch and redirect the code that asks for this information, another one is to make the plugin a small debugger, installing a breakpoint into the interesting place and retrieving the information.  Or we could maybe steal information from iTunes backups and upload it somewhere?

It’s not the easiest attack vector but it’s a damn easy one. If we have governments sponsoring backdoors installation then any vector is a good vector.

I did not created the PoC for stealing the iTunes account information. I found where to intercept that information and stopped there. Not interested in creating such thing. I will release the code for the m3u removal plugin (maybe tomorrow). Just need to clean some things – last time I used it was 3 months ago.

And that’s it! A simple security issue by design. Now go patch it Apple, close the damn holes :-)

Have fun,
fG!

Update:

Here’s the code. Check VisualPluginHandler in iTunesPlugIn.cpp, which handles the events (iTunes will be patched when plugin is loaded, unpatched if configuration is called, and patched again if show visualizer is called). The juice of the patching is located at hack.cpp. The mach-o headers information is grabbed from memory because of ASLR in Lion. Final step is to find the cstrings location and patch it.
I haven’t tested this with 64bits iTunes. Based on sample code provided by Apple’s Technical Note 2016. Some unnecessary code is still present and plugin could be improved by adding a configuration panel to enable/disable it. Maybe one of these days :-)

Disable_m3u_v0.1.zip
SHA256(Disable_m3u_v0.1.zip)= 7583d483ecba7dff93b958245e6801000251938a4f87e2dc71c58a4d9a7ee739

« Older entries