连续参加了四天的Debugging培训,知道了不少Visual Studio Debugger和WindDBG的窍门,新鲜内容不多,倒是激起了做reverse engineering的兴趣。Windows平台就算了,Mac上倒是从来没搞过,刚好手上有一个要注册的软件还没注册,就拿它试试。事先声明,本次crack纯属技术演练,无其他用意。涉及到该程序的内容,会尽量用xxx表示。
首先启动程序,程序停留在了填写注册码的对话框上,打开Terminal,运行gdb,attach上进程。bt察看stack,
(gdb) bt
#0 0×9000b0a8 in mach_msg_trap ()
#1 0×9000affc in mach_msg ()
#2 0×907e4114 in __CFRunLoopRun ()
#3 0×907e3a18 in CFRunLoopRunSpecific ()
#4 0×9321d980 in RunCurrentEventLoopInMode ()
#5 0×9321d014 in ReceiveNextEventCommon ()
#6 0×9321ce80 in BlockUntilNextEventMatchingListInMode ()
#7 0×93720104 in _DPSNextEvent ()
#8 0×9371fdc8 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
#9 0×938cae30 in -[NSApplication _realDoModalLoop:peek:] ()
#10 0×938c1808 in -[NSApplication runModalForWindow:] ()
#11 0×00056be8 in -[XXXRegistrationManager xxxxxFrontRegistrationPanel:] ()
#12 0×0000396c in -[AppDelegate xxxxxFrontRegistrationPanel:] ()
#13 0×92980bf8 in __NSFireDelayedPerform ()
#14 0×907f7aec in __CFRunLoopDoTimer ()
#15 0×907e4464 in __CFRunLoopRun ()
#16 0×907e3a18 in CFRunLoopRunSpecific ()
#17 0×9321d980 in RunCurrentEventLoopInMode ()
#18 0×9321d014 in ReceiveNextEventCommon ()
#19 0×9321ce80 in BlockUntilNextEventMatchingListInMode ()
#20 0×93720104 in _DPSNextEvent ()
#21 0×9371fdc8 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
#22 0×9371c30c in -[NSApplication run] ()
#23 0×9380ce68 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 - 0×8fe00000 dyld Y Y /usr/lib/dyld at 0×8fe00000 (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,0×0000396c是stack中的返回地址,这以上是:
0×0000395c <-[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 0×10cf00 <dyld_stub_objc_msgSend>
0×0000396c <-[AppDelegate xxxxxFrontRegistrationPanel:]+140>: cmpwi cr7,r3,42
dyld_stub_objc_msgSend显然是Object-c的send message,调用的是[XXXRegistrationManager xxxxxFrontRegistrationPanel:]。整个函数也没有什么特别的地方。再看下一个frame,down。
在[XXXRegistrationManager xxxxxFrontRegistrationPanel:]的frame里再次disassemble,仔细察看,发现了一个有趣的函数调用:
0×00056be8 <[XXXRegistrationManager xxxxxFrontRegistrationPanel:]+496>: bl 0×68ac <_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 0×68ac <_Z13is_registeredv>
0×00007114 <-[AppDelegate applicationDidFinishLaunching:]+416>: cmpwi cr7,r3,0
0×00007118 <-[AppDelegate applicationDidFinishLaunching:]+420>: bne+ cr7,0×72e4 <-[AppDelegate applicationDidFinishLaunching:]+880>
花十分钟速成一下PPC汇编,知道了cr是是条件寄存器,r0-r31是通用寄存器,这就够了,上面这段明显就是
if (!is_registered())
{
…
}
else
{
…
}
重要的是得知了r3里存着函数的返回值,这是不是PPC函数调用的惯例,没时间管啦。再down到is_registered()的frame里,大段的tree、vector、map操作和sendMsg都忽略,到函数的最后,寻找和r3相关的地方,发现:
0×00006f24 <_Z13is_registeredv+1656>: bl 0×1110fc <_ZNSt6vectorIhSaIhEED1Ev>
0×00006f28 <_Z13is_registeredv+1660>: li r3,1
0×00006f2c <_Z13is_registeredv+1664>: b 0×6f58 <_Z13is_registeredv+1708>
0×00006f30 <_Z13is_registeredv+1668>: mr r3,r17
0×00006f34 <_Z13is_registeredv+1672>: bl 0×1110fc <_ZNSt6vectorIhSaIhEED1Ev>
0×00006f38 <_Z13is_registeredv+1676>: mr r3,r16
0×00006f3c <_Z13is_registeredv+1680>: bl 0×1110fc <_ZNSt6vectorIhSaIhEED1Ev>
0×00006f40 <_Z13is_registeredv+1684>: li r3,0
0×00006f44 <_Z13is_registeredv+1688>: b 0×6f58 <_Z13is_registeredv+1708>
这显然是在某种条件下让返回值为1然后跳转到函数尾,另一种条件下让返回值为0然后到函数尾。察看二进制序列
(gdb) x /20h 0×00006f28
0×6f28 <_Z13is_registeredv+1660>: 0×3860 0×0001 0×4800 0×002c 0×7e23 0×8b78 0×4810 0xa1c9
0×6f38 <_Z13is_registeredv+1676>: 0×7e03 0×8378 0×4810 0xa1c1 0×3860 0×0000 0×4800 0×0014
0×6f48 <_Z13is_registeredv+1692>: 0×3ac0 0×0001 0×3ae0 0×0001
拿出ridiculousfish的hexfiend查找序列,果然有,将其中的0×386000 (li r3, 0)改为0×386001,存盘,双击启动,注册窗口消失,搞定。全部修改只有一个字节。
再次声明,此篇内容纯属兴趣所致,如果我要继续使用这款软件,我会购买版权。
来源:http://www.robinlu.com/blog/archives/date/2006/04/27/