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.