一次mac上的反向工程实录

连续参加了四天的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 0x93720104 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 0x93720104 in _DPSNextEvent ()
#21 0x9371fdc8 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
#22 0x9371c30c in -[NSApplication run] ()
#23 0x9380ce68 in NSApplicationMain ()
#24 0x00002440 in _start (argc=1, argv=0xbffff970, envp=0xbffff978) at /SourceCache/Csu/Csu-58/crt.c:272

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

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

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

0x0000395c <-[AppDelegate xxxxxFrontRegistrationPanel:]+124>: lis r4,35
0x00003960 <-[AppDelegate xxxxxFrontRegistrationPanel:]+128>: mr r5,r30
0x00003964 <-[AppDelegate xxxxxFrontRegistrationPanel:]+132>: lwz r4,-4004(r4)
0x00003968 <-[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,看到调用的过程是这样的:

0x00007110 <-[AppDelegate applicationDidFinishLaunching:]+412>: bl 0x68ac <_Z13is_registeredv>
0x00007114 <-[AppDelegate applicationDidFinishLaunching:]+416>: cmpwi cr7,r3,0
0x00007118 <-[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>: 0x3860 0x0001 0x4800 0x002c 0x7e23 0x8b78 0x4810 0xa1c9
0x6f38 <_Z13is_registeredv+1676>: 0x7e03 0x8378 0x4810 0xa1c1 0x3860 0x0000 0x4800 0x0014
0x6f48 <_Z13is_registeredv+1692>: 0x3ac0 0x0001 0x3ae0 0x0001

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

3 thoughts on “一次mac上的反向工程实录

Comments are closed.