Wednesday, March 28, 2007

How To Tell If You're A Mac Geek

When a cute girl says she's interested in studying computer science, she goes from cute to pretty.

When she whips out a MacBook Pro, she goes from pretty to beautiful.

When she mentions that she's a certified Apple hardware tech, she goes from beautiful to fucking gorgeous.

True story. Disturbing, but true.

Friday, March 23, 2007

The BOOLs, They Do Nothing

From <CarbonCore/MacTypes.h>:
// kInvalidID  KernelID: NULL is for pointers as kInvalidID is for ID's

#define kInvalidID 0

So I should say-

if (someID == kInvalidID)

instead of-

if (!someID) ?

OK yea, I'll get right on that.

Seriously, every time i start to think that NS X rocks, I find some lameass redefinition like this. Zero is zero- am I missing something? Is this really just a Carbon relic, as the filename implies? Is it going away soon?

Tuesday, March 13, 2007

Obfuscated Me

So I was over at Scott Stevenson's internet recently and was surprised to see not only my name, but an eerily spot-on description of me:

Now Blake, on the other hand; he's a riddle wrapped in a mystery inside an enigma passed to an instance of NSInvocation.


The only thing Bagelturf missed was that I'm usually just invoked with performSelector: - I'm not very complicated. As for the riddle, it's just @encode().

Thursday, March 8, 2007

A Tale of Two CPUs

Exploring and comparing "Accelerated Objective-C Dispatch" on PPC and x86

Links to Darwin source in this post require an Apple ID. They're free, so get one.

I learned about the Obj-C runtime pages last year, and was reminded by this post to one of Apple's mailing lists. Mr. Maebe did a great job of explaining things, but there's always more to the story.

Who 0xfffeff00 is

Mr. Feffoo is a copy of the _objc_msgSend assembly routine defined in objc-msg-ppc.s. As Mr. Maebe pointed out, the only documentation is the source code. The easiest way to find it is to google 0xfffeff00. Apple's policy of requiring a login to view their source code seems to keep google's spider at bay, but our old dead friend, DarwinSource, is still cached. The first found occurrence of 0xfffeff00 is in objc-rtp.h.

<side note>
While we're looking at _objc_msgSend, I'd like to remind everyone who says "messaging nil objects returns nil" that our friend Peter Ammon showed us that this is not always the case. If your code makes that assumption, it wouldn't hurt to fix it now.
</side note>

Notice that this is the PPC version of Darwin. 0xfffeff00 is the value assigned to kRTAddress_objc_msgSend, but only for PPC. While this header file is very interesting, its associated source file is the real fun:

/*
objc-rtp.m
Copyright 2004, Apple Computer, Inc.
Author: Jim Laskey

Implementation of the "objc runtime pages", an fixed area
in high memory that can be reached via an absolute branch.
*/


Hat's off to Mr. Laskey for this file. In my opinion, what happens herein is some of the most interesting code in Darwin. I'll leave it to the reader to enjoy the code that actually copies the _objc_msgSend routine to high memory. Code that treats code like the data it is always makes me smile.

PPC Limits Set

The magic number here is 0xffff8000. Above this address lie the "comm pages", the documentation for which eludes me now(anyone?). The reason this address is important is that a PPC absolute conditional branch can reach any address at or above it, thereby allowing the OS or kernel to copy some commonly used functions to those virtual memory pages.

In the Programming Environments Manual, 'bc', 'bca', 'bcl', and 'bcla' instructions are defined as having a 14 bit address field concatenated with two low zero bits, so that the destination address is guaranteed to be a multiple of four. That means that this is effectively a 16 bit address field. While 'bc' and 'bcl' have their charms, we're more interested in the absolute variants.

If we set the high bit, or sign bit, of the address field and clear the lower bits, the CPU's sign extension produces the number 0xffff8000. And if we use the absolute variants 'bca' and 'bcla', we can branch to that address directly(as opposed to our current address minus 0x8000).

PPC Limits Broken

The comm pages are nice, but they're already used for standard ANSI and/or Unix C functions, not Apple's Obj-C stuff. So how does Apple implement similar behavior for common Obj-C runtime routines like _objc_msgSend?

Simple- build more comm pages below the ones that already exist, and use a branch instruction with a broader range that can reach those addresses.

Assuming you're viewing the conditional branch documentation in the PEM, scroll up a bit to the unconditional branch instructions, specifically 'ba' and 'bla'. Notice the 24 bits available in the destination address field. This field is also concatenated with 2 low zeroes, which effectively gives us 26 bits. (Sure, we could use 'bcctr' to get a full 32 bits, but that would require 2 extra instructions to load the address and would clobber the count register.)

So, if we sacrifice the ability to branch conditionally, we gain 10 bits, or 1024 bytes of addressability*. If we use the unconditional branch instructions, we can branch to anywhere at or above 0xfe000000, which is well below the addresses of the routines defined in objc-rtp.m.

Jackpot.

The _objc_msgSend assembly routine is copied into high RAM at launch time, and PPC code that was compiled with Xcode's "Accelerated Objective-C Dispatch" option uses the high-RAM version of _objc_msgSend instead of the usual symbol stub.

Intel Wins For Once

I don't like x86 assembly and machine code any more than the next guy, but here's something that doesn't suck about the Intel chips:

Darwin x86 has no runtime pages, because it doesn't need them. Not being able to load a 32 bit number in a single instruction is a limitation of the PPC ISA that Intel doesn't share. x86 Obj-C code is "accelerated" by design, and the "Accelerated Objective-C Dispatch" option has no effect whatsoever. Since they don't need symbol stubs, they don't need the "accelerated" workaround.

It's not inconceivable that the "Accelerated Objective-C Dispatch" option will only exist for as long as Apple supports PPC machines.

One More Thing™

Though it's only loosely related to this article, it's interesting to note the following code from objc-rtp.m:


// initialize code in ObjC runtime pages
rtp_set_up_objc_msgSend(kRTAddress_objc_msgSend, kRTSize_objc_msgSend);

rtp_set_up_other(kRTAddress_objc_assign_ivar, kRTSize_objc_assign_ivar,
"objc_assign_ivar", objc_assign_ivar_gc, objc_assign_ivar_non_gc);


An entire function to assign a value to an instance variable? And an optimized version at that? Hmm.

While I currently have no NDA obligation with Apple, I've been told that the information at which this code hints is hush-hush for now. So, out of respect for The Masters, I will only direct the reader to Xcode's "Project Settings" window. Specifically, the description of the "-fobjc-gc" flag.

For those who don't want to fish around for details, "gc" stands for "Great Code".

Many thanks to Mr. Laskey, Mr. Ammon, and all of the Apple coders working on the Obj-C runtime. Our applications only execute correctly and easily because you have all applied an enormous effort in designing a crucial part of the OS X underbelly. We all appreciate your work.

If any of you attend C4[1], the drinks are on me.

* I meant to say 33,521,664 bytes, not 1024 bytes. Minor oversight, sorry about that.

Monday, March 5, 2007

Free Noob Lesson #2

We've all done this-

- (id)init
{
if (!(self = [super init]))
return nil;

// etc.
}


That obviously returns nil when [super init] fails.

But what if you accidentally add(or forget to remove) an extra semicolon?

- (id)init
{
if (!(self = [super init]));
return nil;

// etc.
}


GCC is perfectly happy with that, but your application is not. The above code reduces to this useless logic:

- (id)init
{
self = [super init];

return nil;
}


One might say that we could avoid this by returning self instead of nil, but then the code in 'etc.' would still never be executed, and the resulting bug would probably be much harder to locate.

The only quick and easy protection that I can think of is sobriety, which is clearly out of the question. Any thoughts?

Sunday, February 25, 2007

Open Letter Regarding Display Eater

To Reza Hussain:

There is a lack of official information about versions of Display Eater that predate version 1.85, which is the version I decompiled. You were interviewed by a Swedish web site, and allegedly provided the information we are looking for, but the English translation of that interview is currently only available through a third party.

Reza, I respectfully request an answer to this question:

Did previous versions of Display Eater delete files and/or folders other than your own Application Support folder?

I recognize that version 1.85 only deletes files that your app created, and I only ask this question to provide facts to the Mac community, and to conclude this sad chapter of our shared history.

Be assured that I am not interested in harassing you, whatever your answer may be. We just want to know the truth. In the interest of preserving what is left of your good reputation, please enlighten us, in English.

Thank you.

To everyone else:

The comments to this post are reserved for Reza, and polite commenters. Inflammatory comments will be removed, as there are countless other outlets for those sentiments.

Thank you.

Saturday, February 24, 2007

Behind The Curtain With Display Eater

You may have seen the discussion recently about an app called Display Eater by Reza Hussain. While the author has not responded to the latest discussion, he allegedly admitted that the whole thing was a hoax to deter potential pirates. Reza has just replied with a public letter. While the discussion has been lively, the investigation has been nonexistant. So here it is.

First, the legal stuff:

According to the Display Eater 1.8.5 license agreement:

3. License Restrictions
b. You may not alter, merge, modify, adapt or translate the Software, or decompile, reverse engineer, disassemble, or otherwise reduce the Software to a human-perceivable form.


But I wanted to disassemble the app! Fortunately, the license is not really a legal agreement:

THIS SOFTWARE END USER LICENSE AGREEMENT(EULA) IS A LEGA AGREEMENT BETWEEN YOU(EITHER AN INDIVIDUAL OR, IF PURCHASED OR OTHERWISE AQUIRED BY OR FOR AN ENTITY, AN ENTITY) AND THE AUTHOR OF THIS SOFTWARE.


Since LEGA AGREEMENTs don't hold up in court, we're free to do what we want with the app*. I'd also like to suggest to John Stansbury that while his assertion that users don't have the right to reverse engineer an app is correct in most cases, it's not applicable here, for the above reason.

Reza, fix your license.

Second, the fun stuff:

This is one of those apps that writes copious amounts of crap to the console for no reason. For example, after simply launching the app, declining to register, and quitting, I got this:

display_eater[3338] help me Start Recording
display_eater[3338] no file
display_eater[3338] mmmkay
display_eater[3338] mmmmm
display_eater[3338] selfmutilation DVC PAL
display_eater[3338] lettesta /Applications/Display Eater 1.85/Display Eater.app/Contents/Resources/cursor2.gif
display_eater[3338] onenationunderfuck 640x480
display_eater[3338] cheated--20 fps
display_eater[3338] loadedrender

Yes, that says onenationunderfuck. In my console log. Is it still cursing if you don't use camelCase? Well, at least he's a South Park fan. Here are a few other nice ones I found in the disassembly:

This "emailDeveloper:" method doesn't actually send any mail, but at least it further pollutes my log.

-(void)[mainWindowController emailDeveloper:]
3c600003 lis r3,0x3
38639500 addi r3,r3,0x9500 emailz0r
4801e6d8 b 0x21bd0 _NSLog


And from other methods:

386395e0    addi     r3,r3,0x95e0     FATTTTTTTTTY>>>>>>>
4801e005 bl 0x21bd0 _NSLog

386395a0 addi r3,r3,0x95a0 not saved lol
4801e1a1 bl 0x21bd0 _NSLog

3863a5a0 addi r3,r3,0xa5a0 lawl
4800f438 b 0x21bd0 _NSLog

3863b2c0 addi r3,r3,0xb2c0 OMGFWTFBBQxinFINITY
48003ee8 b 0x21bd0 _NSLog

38639e60 addi r3,r3,0x9e60 SDKNLLLLLLLLLLLLL
480161d5 bl 0x21bd0 _NSLog


And on and on and on. People, mescaline can be loads of fun, but it's not conducive to a healthy programming environment.

Ok, so Reza likes to make jokes at the expense of my log file, but that's not a capital offense. Allegedly deleting user data should be. While we're talking about spurious log data, I should note that Display Eater actually logs the customer's name, email and serial number on every launch. I'll give you one guess why that's a Bad Idea™.

Finally, the good stuff:

Ok, so Reza beat me to the punch by admitting that Display Eater does not really delete your home directory. But it does delete something, and those users who watch their console logs already know what it is. I had planned to present the complete annotated assembly of the methods involved, but that would bloat this post incredibly and bore most of you. Here's a higher level explanation:

The C function that does the deleting is called "destroy". In otool or otx output, search for "_destroy:". Its C declaration would look like this:

void destroy(NSString* inString);


It is called only from itself, and from one Obj-C method in the "recordCreateController" class:

- (id)addRecordWithPath: (NSString*)inPath
andRect: (NSRect*)inRect;


(class-dump says inPath should be of type "id" but the code in Display Eater assumes an NSString*. We'll have to wait for the GPL'd code, but I suspect this is just more sloppy code- see below for the rest)

In the disassembled code, "addRecordWithPath:andRect:" immediately follows "_destroy:".

Note the interestingly named "emptyEntireClipOnAllOfThem" that follows "addRecordWithPath:andRect:". While it smacks of gangland violence, it is not involved with deleting any files. My good conscience prevents me from revealing its purpose :)

So, when does "addRecordWithPath:andRect:" call "destroy"? The Obj-C code looks something like this:

    id  delegate = [[NSApplication sharedApplication] delegate];

// Reza, you may want to call [delegate respondsToSelector:] first...
NSString* support = [delegate applicationSupportFolder];

// "ninjakiller" is the reg file.
// Reza, use stringByAppendingPathComponent, mmmkay?
NSString* regFile = [support stringByAppendingString: @"/ninjakiller"]
NSArray* regArray = [NSUnarchiver unarchiveObjectWithFile: regFile];

if (![[regArray objectAtIndex: 1] compare: @"Special [K]"] ||
![[regArray objectAtIndex: 1] compare: @"KCN"])
{
NSLog(@"BUT I FALTER");
destroy(@"~/Library/Application Support/display_eater/");

// and then show the pirate dialog...
}


Ok, nothing surprising. Whoever Special [K](nice name, btw) and KCN are, they're screwed. So what happens inside "destroy"? It seems clear already that Display Eater's app support folder will get nuked, but is that all that happens?

void destroy(NSString* inString)
{
NSFileManager* manager = [NSFileManager defaultManager];

NSString* path = [inString stringByExpandingTildeInPath];
// Believe it or not, 'path' is only used for the following call.

NSArray* files = [manager directoryContentsAtPath: path];
unsigned int curFile; // I assume this is unsigned.

for (curFile = 0; curFile < [files count]; curFile++)
{
// Reza, you've already expanded this- you don't need to do it twice.
NSString* basePath = [inString stringByExpandingTildeInPath];
NSString* curFileName = [files objectAtIndex: curFile];

// Reza, please use stringByAppendingPathComponent.
NSString* curPath = [NSString stringWithFormat: @"%@/%@"
basePath, curFileName];

NSLog(@"%@", curPath);

// Reza, you did this already...
NSFileManager* manager2 = [NSFileManager defaultManager];

// ...and this, too.
// I'm not sure why passing just the name and not the path works,
// but it does.
if ([manager2 isDeletableFileAtPath: [files objectAtIndex: curFile]])
{
// Calling default manager yet again. At least we're using
// the full path this time.
[[NSFileManager defaultManager]
removeFileAtPath: curPath handler: nil];
NSLog(@"DELETABLE");
}
else
{
destroy(curPath);
}
}
}


So there it is. Display Eater recursively deletes the contents of its own Application Support folder(but not the folder itself), and nothing else. If the user was silly enough to put anything in that folder, it would have been nuked. But in that case, one might argue that they deserved it.

Note that the "piratekiller" file, whose contents seem to indicate a perverted TCL function call(kilo transform ar reza) is never used for anything.

Ultimately, the worst thing about this app is the sloppy Obj-C code. Reza, I would be happy to optimize your code a bit, if you're so inclined.

* I'm no lawyer- if anyone can prove me wrong about this point, please do so.