石锅拌饭

一次mac上的反向工程实录

by Robin Lu on Apr.27, 2006, about , ,

连续参加了四天的Debugging培训,知道了不少Visual Studio Debugger和WindDBG的窍门,新鲜内容不多,倒是激起了做reverse engineering的兴趣。Windows平台就算了,Mac上倒是从来没搞过,刚好手上有一个要注册的软件还没注册,就拿它试试。事先声明,本次crack纯属技术演练,无其他用意。涉及到该程序的内容,会尽量用xxx表示。
首先启动程序,程序停留在了填写注册码的对话框上,打开Terminal,运行gdb,attach上进程。bt察看stack,

(gdb) bt
#0 0x9000b0a8 in mach_msg_trap ()
#1 0x9000affc in mach_msg ()
#2 0x907e4114 in __CFRunLoopRun ()
#3 0x907e3a18 in CFRunLoopRunSpecific ()
#4 0x9321d980 in RunCurrentEventLoopInMode ()
#5 0x9321d014 in ReceiveNextEventCommon ()
#6 0x9321ce80 in BlockUntilNextEventMatchingListInMode ()
#7 0×93720104 in _DPSNextEvent ()
#8 0x9371fdc8 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
#9 0x938cae30 in -[NSApplication _realDoModalLoop:peek:] ()
#10 0x938c1808 in -[NSApplication runModalForWindow:] ()
#11 0x00056be8 in -[XXXRegistrationManager xxxxxFrontRegistrationPanel:] ()
#12 0x0000396c in -[AppDelegate xxxxxFrontRegistrationPanel:] ()
#13 0x92980bf8 in __NSFireDelayedPerform ()
#14 0x907f7aec in __CFRunLoopDoTimer ()
#15 0x907e4464 in __CFRunLoopRun ()
#16 0x907e3a18 in CFRunLoopRunSpecific ()
#17 0x9321d980 in RunCurrentEventLoopInMode ()
#18 0x9321d014 in ReceiveNextEventCommon ()
#19 0x9321ce80 in BlockUntilNextEventMatchingListInMode ()
#20 0×93720104 in _DPSNextEvent ()
#21 0x9371fdc8 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
#22 0x9371c30c in -[NSApplication run] ()
#23 0x9380ce68 in NSApplicationMain ()
#24 0×00002440 in _start (argc=1, argv=0xbffff970, envp=0xbffff978) at /SourceCache/Csu/Csu-58/crt.c:272

从这里看不出什么。用info sharedlibrary来察看每个模块的加载地址,发现除了程序本身的二进制文件,其它的库都被加载到了0×80000000以上的地址空间内:

Num Basename Type Address Reason | | Source
| | | | | | | |
1 XXXXXXXX – 0×1000 exec Y Y /Applications/XXXXXXXX.app/Contents/MacOS/XXXXXXXX (offset 0×0)
2 dyld – 0x8fe00000 dyld Y Y /usr/lib/dyld at 0x8fe00000 (offset 0×0) with prefix “__dyld_”
3 libmathCommon.A.dylib – 0×90213000 dyld Y Y /usr/lib/system/libmathCommon.A.dylib at 0×90213000 (offset 0×0)
4 libSystem.B.dylib – 0×90000000 dyld Y Y /usr/lib/libSystem.B.dylib at 0×90000000 (offset 0×0)

这时候再查看上面的stack,发现frame #12和#11都应该是我的关注点。好,先up 12,再disassemble,0x0000396c是stack中的返回地址,这以上是:

0x0000395c <-[AppDelegate xxxxxFrontRegistrationPanel:]+124>: lis r4,35
0×00003960 <-[AppDelegate xxxxxFrontRegistrationPanel:]+128>: mr r5,r30
0×00003964 <-[AppDelegate xxxxxFrontRegistrationPanel:]+132>: lwz r4,-4004(r4)
0×00003968 <-[AppDelegate xxxxxFrontRegistrationPanel:]+136>: bl 0x10cf00 <dyld_stub_objc_msgSend>
0x0000396c <-[AppDelegate xxxxxFrontRegistrationPanel:]+140>: cmpwi cr7,r3,42

dyld_stub_objc_msgSend显然是Object-c的send message,调用的是[XXXRegistrationManager xxxxxFrontRegistrationPanel:]。整个函数也没有什么特别的地方。再看下一个frame,down。
在[XXXRegistrationManager xxxxxFrontRegistrationPanel:]的frame里再次disassemble,仔细察看,发现了一个有趣的函数调用:

0x00056be8 <[XXXRegistrationManager xxxxxFrontRegistrationPanel:]+496>: bl 0x68ac <_Z13is_registeredv>

调用一下看看:

(gdb) p (int)is_registered()
$1 = 0

真是好可疑啊。退出程序,重新用gdb xxxx的方式启动程序,在run之前先用br is_registered设置断点,运行,很快程序停在了is_registered()函数的开头,up到上一级stack,看到调用的过程是这样的:

0×00007110 <-[AppDelegate applicationDidFinishLaunching:]+412>: bl 0x68ac <_Z13is_registeredv>
0×00007114 <-[AppDelegate applicationDidFinishLaunching:]+416>: cmpwi cr7,r3,0
0×00007118 <-[AppDelegate applicationDidFinishLaunching:]+420>: bne+ cr7,0x72e4 <-[AppDelegate applicationDidFinishLaunching:]+880>

花十分钟速成一下PPC汇编,知道了cr是是条件寄存器,r0-r31是通用寄存器,这就够了,上面这段明显就是

if (!is_registered())
{

}
else
{

}

重要的是得知了r3里存着函数的返回值,这是不是PPC函数调用的惯例,没时间管啦。再down到is_registered()的frame里,大段的tree、vector、map操作和sendMsg都忽略,到函数的最后,寻找和r3相关的地方,发现:

0x00006f24 <_Z13is_registeredv+1656>: bl 0x1110fc <_ZNSt6vectorIhSaIhEED1Ev>
0x00006f28 <_Z13is_registeredv+1660>: li r3,1
0x00006f2c <_Z13is_registeredv+1664>: b 0x6f58 <_Z13is_registeredv+1708>
0x00006f30 <_Z13is_registeredv+1668>: mr r3,r17
0x00006f34 <_Z13is_registeredv+1672>: bl 0x1110fc <_ZNSt6vectorIhSaIhEED1Ev>
0x00006f38 <_Z13is_registeredv+1676>: mr r3,r16
0x00006f3c <_Z13is_registeredv+1680>: bl 0x1110fc <_ZNSt6vectorIhSaIhEED1Ev>
0x00006f40 <_Z13is_registeredv+1684>: li r3,0
0x00006f44 <_Z13is_registeredv+1688>: b 0x6f58 <_Z13is_registeredv+1708>

这显然是在某种条件下让返回值为1然后跳转到函数尾,另一种条件下让返回值为0然后到函数尾。察看二进制序列

(gdb) x /20h 0x00006f28
0x6f28 <_Z13is_registeredv+1660>: 0×3860 0×0001 0×4800 0x002c 0x7e23 0x8b78 0×4810 0xa1c9
0x6f38 <_Z13is_registeredv+1676>: 0x7e03 0×8378 0×4810 0xa1c1 0×3860 0×0000 0×4800 0×0014
0x6f48 <_Z13is_registeredv+1692>: 0x3ac0 0×0001 0x3ae0 0×0001

拿出ridiculousfish的hexfiend查找序列,果然有,将其中的0×386000 (li r3, 0)改为0×386001,存盘,双击启动,注册窗口消失,搞定。全部修改只有一个字节。
再次声明,此篇内容纯属兴趣所致,如果我要继续使用这款软件,我会购买版权。

:, ,

3 Comments for this entry

Search

Archives

Browse by tags

agile apple blog book design ecto extension firefox git google hack ichm iphone keyword life mac madfox movie nonsense opensource plugin pm ruby rubyonrails sns software startup wordpress work 财帮子