Archive for the ‘Reverse Engineering’ category

Interfacing With (Hacking) iChat in Leopard

November 1, 2007

Before you consider using any of the unsupported hacks I’m about to discuss, check to see if existing frameworks , or iChat’s AppleScript interface, will do what you need. Any software update can break unsupported code in unpredictable ways at any time. When Leopard came out, all the hacks I had been using to interface with iChat broke — causing a deadlock, that was harder to track down then a crash. Hacks can have catastrophic consequences.

(I’m not going to go in-depth into the hacks I was using under Tiger. But for the benefit of people who’s code has suddenly broken: The FZDaemon/FZDaemonListener protocols have been significantly changed in Leopard. Every function in FZDaemon is now oneway void for example. My attempts to get a connection to com.apple.iChatAgent were deadlocking. The same approach will not work on both Leopard and Tiger, as far as I can tell. The good news is that the Leopard APIs are a lot cleaner.)

Reverse Engineering iChat and InstantMessage.framework

The first thing I did was check to see if Leopard introduced a supported way of doing what I needed. There were some, like the IMMyStatusChangedNotification, but still no way to set the user’s status message. After reading, I knew what frameworks I needed to poke at. I also perused the iChat, and iChatAgent bundles for further clues.

I used class-dump, otx and strings on: iChat, iChatAgent, InstantMessage.framework, and IMUtils.framework (inside InstantMessage.framework). This gave me private interface declarations, disassembled code, and portentous strings to pour over.

I also tried using gdb on iChat, to see just how it was communicating with iChatAgent. This was not very productive for me at all. I got much further by statically analyzing disassembled code and interfaces. GDB can be a great tool, but you have to know a lot about what you are looking for before you can find it — otherwise you don’t know where to set breakpoints. I’ve noticed myself using gdb less and less over the last few years. (As silly as it sounds, gdb just hasn’t been as much fun after the intel switch, now that all the assembler is x86 goobely-garb.)

Trying things out in my own “tester” project really helped me puzzle out exactly what was going on, why, and how. I had to explicitly add the IMUtils.framework to the “Linked Frameworks” group to get some stuff to link.

I learned a lot by listening to any - (NSNotificationCenter*) notificationCenter that an object exposed. For example,

[[IMService notificationCenter] addObserver:self selector:@selector(iChatNotification:) name:nil object:nil];

where,
- (void) iChatNotification:(NSNotification*)notification{
NSLog(@"iChatNotification: %@", notification);
}

Will print every notification passing through the IMService notification center. Passing in a name: or object: parameter filters what notifications your method receives. Different classes often share the same notification center, so I would verify that one was distinct before printing all it’s notifications.

It turns out that the public [IMService notificationCenter] sends an undocumented notification named “IMMyInfoChangedNotification” when the user’s status message changes. However, the documented IMMyStatusChangedNotification appears to always be sent in such cases — I could not contrive an example where the status message was changed, and it was not sent.

InstantMessagePrivate.h will let you use access private interfaces in InstantMessage.framework. It is a refinement o the class-dump of InstantMessage.framework.

IMServiceAgentImpl is the most feature-rich class for controlling iChat (see also: IMServiceAgent). [IMServiceAgentImpl sharedAgent] will give you the shared instance. Some of the most useful methods are:
- (void)setMyStatus:(IMPersonStatus)statusType message:(NSString*)statusMessage;
- (NSString*) myStatusMessage;
- (NSData*) myPictureData;
- (void) setMyPictureData:(NSData*)newPictureData;
- (NSString*) myProfile;
- (void) setMyProfile:(NSString*)newProfile;
- (NSArray*)myAvailableMessages;
- (NSArray*)myAwayMessages;

Accessor methods will not work correctly, unless you have a connection to the iChatAgent daemon.
[[IMServiceAgentImpl sharedAgent] connectWithLaunch:YES];
[[IMServiceAgentImpl sharedAgent] _blockUntilConnected];
ensures that there is a connection to the daemon. Only calling connectWithLaunch: or _blockUntilConnected didn’t do it for me, I had to call both in order. There may be a better way to get a connection, but I’m not aware of it

Unfortunately, I have not yet figured out how to get iChat to set the status to invisible. Right now I’m concentrating to getting IMLocation working on Leopard, so I’ll look into this later.

iChat’s a damn big program for “just a chat client”, It’s executable and class-dump are actually about 2x as big as Safari’s.

InstantMessagePrivate.h

Advertisements

Getting OS X Icons

September 10, 2007

This article has moved here.

Undebuggable

August 16, 2007

From steike, the undocumented system call ptrace(PT_DENY_ATTACH, 0, 0, 0); lets a process avoid being debugged… at least by people who haven’t read the article.

If you are getting Program exited with code 055. from GDB, this is why.

Interesting stuff.

Selective Muting: Get Out of My Headphones!

April 20, 2007

A feature I thought I would love, but began to hate is automatic muting in “quite” places, like lecture halls. Whenever I take my laptop to a place I have designated as quiet, IMLocation will mute it. This works great at preventing embarrassing beeps during class. But I like to listen to headphones while I work, and they were getting muted whenever I would work in a library or study hall.

What I needed was selective muting — if I was in a quiet place, sound would be turned off, unless I was using headphones.

At first I suspected that this feature would only work on newer Macs. My MacBookPro remembers the volume of my headphones when I plug them in. (By the way, if you don’t know about this handy feature of your new Mac, check it out — it’s an ear saver). But older Macs don’t do this, so I figured there was just no way for them to figure out if headphones were plugged in or not.

Well, it turns out this is not the case. Apparently on older Macs, OS X chooses not to change the volume, even though it can. I have confirmed that this is the case on an iBook G4, iMac G4, 12″ Powerbook, and 15″ TiBook. They can detect if they have headphones plugged in or not, even though OS X ignores this bit of information.

Selective muting is currently available as of IMLocation version 0.24.