I used to wonder, how am I suppose to debug a plugin? I can’t just debug the binary just like a ELF, or EXE file and use the disassembled offset right out of the box for me to breakpoint.
Let’s take a good example:
Suppose we have EarthDesk.prefPane (I got it from www.apple.com/download).
The I dump the binary into class header like this:
/*
* Generated by class-dump 3.1.2.
*
* class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard.
*/
struct __CFString;
/*
* File: EarthDesk
* Arch: Intel 80x86 (i386)
*/
.... Cut on purpose
@interface ComXericDesignEarthDeskLicenseController : NSWindowController
{
ComXericDesignEarthDeskPrefPane *bridge;
NSTextField *userField;
NSTextField *orgField;
NSTextField *keyField;
NSButton *okButton;
int fieldKeyCount;
}
- (id)initWithObject:(id)fp8;
- (BOOL)licenseBlocked:(unsigned short)fp8 hVal:(unsigned long)fp12;
- (BOOL)licenseAdded:(unsigned short)fp8 hVal:(unsigned long)fp12;
- (void)setLicenseFields;
- (void)controlTextDidChange:(id)fp8;
- (void)keyCountAlert;
- (void)showLicenseSheet:(id)fp8;
- (void)okButtonAction:(id)fp8;
- (void)cancelButtonAction:(id)fp8;
- (void)sheetDidEnd:(id)fp8 returnCode:(int)fp12 contextInfo:(void *)fp16;
@end
@interface NSMutableData (Compression)
- (BOOL)compress;
- (BOOL)uncompress;
@end
@interface NSData (Compression)
- (id)compressedData;
- (id)compressedDataWithLevel:(int)fp8;
- (id)uncompressedData;
@end
..... Cut on purpose
I fired up the application using System Preferences, and see the Enter License Sheet (Find sheet at: developer.apple.com)
And then I look for some interesting method, and I found -controlTextDidChange:, I am guessing this is a documented callback or something, and find out in Xcode documentation about this method, and I found out that this is a notification received from NSNotificationCenter, and the I disassemble the binary into this:
-(void)[ComXericDesignEarthDeskLicenseController controlTextDidChange:]
+0 000068e0 55 pushl %ebp
+1 000068e1 89e5 movl %esp,%ebp
+3 000068e3 83ec48 subl $0x48,%esp
+6 000068e6 895df4 movl %ebx,0xf4(%ebp)
+9 000068e9 8b4508 movl 0x08(%ebp),%eax
+12 000068ec e800000000 calll 0x000068f1
+17 000068f1 5b popl %ebx
+18 000068f2 8975f8 movl %esi,0xf8(%ebp)
+21 000068f5 897dfc movl %edi,0xfc(%ebp)
+24 000068f8 8945e4 movl %eax,0xe4(%ebp)
+27 000068fb 8b8303890000 movl 0x00008903(%ebx),%eax
+33 00006901 89442404 movl %eax,0x04(%esp)
+37 00006905 8b4510 movl 0x10(%ebp),%eax
+40 00006908 890424 movl %eax,(%esp)
+43 0000690b e8c0980000 calll 0x000101d0 -[(%esp,1) object]
+48 00006910 89c7 movl %eax,%edi
+50 00006912 8b45e4 movl 0xe4(%ebp),%eax
+53 00006915 8b5034 movl 0x34(%eax),%edx
+56 00006918 8b837f880000 movl 0x0000887f(%ebx),%eax
+62 0000691e 891424 movl %edx,(%esp)
..... Cut on purpose
And then I want to set a breakpoint at address: 0×000068e0 which is the beginning of the method.
OK, now where am I suppose to breakpoint it? I can’t run the EarthDesk.prefPane just like a normal app! Why? because only System Preferences can call this program.
Now, I start thinking, if System Preferences is the one who load this program, it means that System Preferences will load it somewhere in the memory.
Now I want GDB to attach the process of System Preferences, and next thing I know, I type this:
$ps -ax | grep Preference
1519 ?? 0:00.54 /Applications/System Preferences.app/Contents/MacOS/System Preferences -psn_0_372827
1538 ttys000 0:00.00 grep Preference
I look for System Preferences PID which is 1519, from looking at the result I know that System Preferences is a Carbon application, you can know this from the -psn id it has.
Now I fire up GDB and attach 1519 to my gdb.
$gdb -q
gdb$ attach 1519
Attaching to process 1519.
Reading symbols for shared libraries . done
Reading symbols for shared libraries ............... done
0x91b56286 in mach_msg_trap ()
--------------------------------------------------------------------------[regs]
EAX: 10004005 EBX: 92C21967 ECX: BFFFEC7C EDX: 91B56286 o d I t s z a P c
ESI: 00000000 EDI: 00000000 EBP: BFFFECB8 ESP: BFFFEC7C EIP: 91B56286
CS: 0007 DS: 001F ES: 001F FS: 0000 GS: 0037 SS: 001F
[001F:BFFFEC7C]----------------------------------------------------------[stack]
BFFFECCC : 50 04 00 00 03 0F 00 00 - 00 00 00 00 00 00 00 00 P...............
BFFFECBC : 4E 20 C2 92 A0 ED FF BF - 06 00 00 03 00 00 00 00 N ..............
BFFFECAC : 06 00 00 03 E8 87 10 00 - A0 ED FF BF 78 F2 FF BF ............x...
BFFFEC9C : 01 00 00 00 03 37 00 00 - 93 58 C7 49 7B 07 C2 92 .....7...X.I{...
BFFFEC8C : 50 04 00 00 03 0F 00 00 - 00 00 00 00 00 00 00 00 P...............
BFFFEC7C : 7C DA B5 91 A0 ED FF BF - 06 00 00 03 00 00 00 00 |...............
[0007:91B56286]-----------------------------------------------------------
0x91b56286 : ret
0x91b56287 : nop
0x91b56288 : mov eax,0xffffffe0
0x91b5628d : call 0x91b56ad4
0x91b56292 : ret
0x91b56293 : nop
0x91b56294 : mov eax,0xffffffdf
0x91b56299 : call 0x91b56ad4
--------------------------------------------------------------------------------
gdb$ continue
Reading symbols for shared libraries . done
And now I open the EarthDesk preference in the System Preference so it will be loaded to the memory. Then I back to terminal hit Ctrl + C to break.
Back to my disassembled source list, I want to set a breakpoint at 0×000068e0. No, I can’t just type:
b *000068e0 to the gdb right? why? because this offset is not correct, why is that? because this is a loaded plugin which mean that it must be put somewhere in the memory as the base address, so we must find out where in memory System Preference put EarthDesk?
Then I get back again to GDB:
gdb$ info shared
.... Cut on purpose
104 EarthDesk - 0x152d0000 dyld Y Y /Users/Syuaibi/Library/PreferencePanes/EarthDesk.prefPane/Contents/MacOS/EarthDesk at 0x152d0000 (offset 0x152d0000)
gdb$
Now I got the base address of EarthDesk, take a look at the bold address: 0×152d0000. This is my base address, now I must add all our breakpoint address with this base address.
I then add 0×000068e0 with 0×152d0000, I got 0×152D68E0
put it back in my GDB console, and voila I breakpoint in the correct address…
Now, everything is easier after this one, you can figure it out yourself.
Have fun!

