神刀安全网

破解Mac版扫雷游戏

Mac App Store有一个扫雷的游戏,免费版只能玩初级和中级的级别,像我这种骨灰级玩家,肯定要玩高级级别的:

破解Mac版扫雷游戏

不过一点击 高级 菜单按钮,就会提示让我购买游戏,像我这种没钱的玩家,只能逆向这个app了。

一、破解游戏级别限制

首先运行 Interface Inspector ,这是一个可以查看Mac应用界面元素结构和属性的软件,功能非常强大,运行后附加上扫雷的进程:

破解Mac版扫雷游戏

然后展开菜单 游戏 –> 新游戏 –> 高级 ,在右边侧边栏可以看到 高级 菜单栏对象的内存地址是 0x101331130

破解Mac版扫雷游戏

接下来用lldb附加扫雷的进程:

Jobs: ~$ lldb -n "Minesweeper Deluxe" (lldb) process attach --name "Minesweeper Deluxe" Process 37343 stopped * thread #1: tid = 0x2b091, 0x00007fff8499cf72 libsystem_kernel.dylib`mach_msg_trap + 10, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP     frame #0: 0x00007fff8499cf72 libsystem_kernel.dylib`mach_msg_trap + 10 libsystem_kernel.dylib`mach_msg_trap: ->  0x7fff8499cf72 <+10>: retq     0x7fff8499cf73 <+11>: nop  libsystem_kernel.dylib`mach_msg_overwrite_trap:     0x7fff8499cf74 <+0>:  movq   %rcx, %r10     0x7fff8499cf77 <+3>:  movl   $0x1000020, %eax          ; imm = 0x1000020  Executable module set to "/Applications/Minesweeper Deluxe.app/Contents/MacOS/Minesweeper Deluxe". Architecture set to: x86_64h-apple-macosx. (lldb)  

然后使用以下命令获取点击菜单按钮时调用的方法名:

(lldb) po 0x101331130 <NSMenuItem: 0x101331130 高级>  (lldb) po [0x101331130 target] <minesweepermacAppDelegate: 0x1013353c0>  (lldb) po [0x101331130 action] 0x0000000100088db1  (lldb) po NSStringFromSelector(0x0000000100088db1) startNewGameExpert: 

由此可知,点击菜单按钮会调用 -[minesweepermacAppDelegate startNewGameExpert:] 方法,用Hopper可以看到该方法的伪代码为:

void -[minesweepermacAppDelegate startNewGameExpert:](void * self, void * _cmd, void * arg2) {     rdx = arg2;     rbx = self;     r14 = *objc_msgSend;     [self dismissLeaderboardView];     rax = [GameState sharedInstance];     rax = [rax fullgame];     if (LOBYTE(rax) != 0x0) {             r14 = *objc_msgSend;             rax = [GameState sharedInstance];             [rax setDifficulty:0x2];             rax = [GameState sharedInstance];             [rax save];             [rbx->beginner setState:0x0];             [rbx->intermediate setState:0x0];             [rbx->expert setState:0x1];             [rbx->custom setState:0x0];             r15 = *objc_ivar_offset_minesweepermacAppDelegate_window_;             [*(rbx + r15) setIsVisible:0x0];             xmm0 = intrinsic_xorps(xmm0, xmm0);             rdi = *(rbx + r15);             var_40 = intrinsic_movaps(var_40, xmm0);             var_30 = 0x408d100000000000;             var_28 = 0x4082800000000000;             [rdi setFrame:0x1 display:0x0 animate:r8];             [*(rbx + r15) center];             rax = [GameManager sharedGameManager];             [rax runSceneWithID:0x65];             rax = [*(rbx + r15) setIsVisible:0x1];     }     else {             rax = [rbx unlockFullgame:0x0];     }     return; } 

由代码可知,当 -[GameState fullgame] 方法的返回值为 YES 的时候,才可以玩高级级别的游戏。

所以可以编写一个插件来hook这个方法,强制返回 YES 值。

按照 《使用EasySIMBL为Mac应用加载插件》 教程里的方法安装EasySIMBL模板,然后用Xcode新建一个EasySIMBL插件工程,工程名为 MinesweeperPlugin

破解Mac版扫雷游戏

然后hook -[GameState fullgame] 方法,代码如下所示:

@implementation NSObject (GameStateHook)  + (void)hook_GameState {     [self jr_swizzleMethod:@selector(fullgame)                 withMethod:@selector(hook_fullgame)                      error:nil]; }  - (BOOL)hook_fullgame {     return YES; }  @end 

编译代码后重新运行游戏,就可以进入高级级别游戏了。

二、破解安全帽数量

扫雷的菜单里还有一个购买安全帽的功能:

破解Mac版扫雷游戏

在帮助里可以看到安全帽的说明:

使用安全帽

==========

使用 “ Alt / Option + 左键点击” 可以保证安全。如果方块下面是雷会被插棋,如果下面不是雷会正常打开。每次会用掉一个安全帽。游戏中随时按下 “ Alt / Option ” 键可以看安全帽数量。安全帽用完了可以在商店菜单中添加,也可以通过分享你的成绩获得免费安全帽。

虽然我玩扫雷时不会使用这种作弊的功能,但是在某些情况下还是有用的。比如有时玩到最后会出现2选1的情况,这时如果靠运气点到地雷就太亏了,所以安全帽可以在这种情况下使用。那么顺便把安全帽的数量修改成无限吧。

同理,在 Interface Inspector 里查看购买5个安全帽的菜单地址:

破解Mac版扫雷游戏

再获取菜单调用的方法名:

(lldb) po 0x10112c2c0 <NSMenuItem: 0x10112c2c0 5个安全帽>  (lldb) po [0x10112c2c0 target] <minesweepermacAppDelegate: 0x10122b630>  (lldb) po [0x10112c2c0 action] 0x00000001000893a1  (lldb) po NSStringFromSelector(0x00000001000893a1) buy5Robot: 

在Hopper里查看 -[minesweepermacAppDelegate buy5Robot:] 方法的伪代码:

void -[minesweepermacAppDelegate buy5Robot:](void * self, void * _cmd, void * arg2) {     rbx = *objc_msgSend;     [self dismissLeaderboardView];     rdi = [InAppPurchaseManager getInstance];     rax = [rdi purchase5robot];     return; } 

再查看 -[InAppPurchaseManager purchase5robot] 方法的伪代码:

void -[InAppPurchaseManager purchase5robot](void * self, void * _cmd) {      //......      r15 = *(var_68 + r14 * 0x8);     rax = [r15 productIdentifier];     rax = [rax isEqualToString:@"com.sg.minesweepermac.robot5"];     if (LOBYTE(rax) != 0x0) {             r12 = *objc_msgSend;             rbx = [SKPayment paymentWithProduct:r15];             rax = [SKPaymentQueue defaultQueue];             [rax addPayment:rbx];     }      //...... } 

从代码可知,购买的产品的id是 com.sg.minesweepermac.robot5

《一种应用内付费(iap)的破解方法》 可知,内购的回调方法为 paymentQueue:updatedTransactions: ,在Hopper里搜一下这个方法,可以发现这个方法在 InAppPurchaseManager 类里。

接下来使用 《Hopper Disassembler批量导出反编译的伪代码》 里的方法,反编译出 InAppPurchaseManager 类所有方法的伪代码。

打开 ~/ClassDecompiles/Minesweeper Deluxe/InAppPurchaseManager.m 文件,搜索 com.sg.minesweepermac.robot5 ,可以很快发现以下代码:

- (void)provideContent:(id)arg2  {     //......      rax = [r14 isEqualToString:@"com.sg.minesweepermac.robot5"];     LODWORD(r13) = 0x5;     if (LOBYTE(rax) == 0x0) {             rax = [r14 isEqualToString:@"com.sg.minesweepermac.robot15"];             LODWORD(r13) = 0xf;             if (LOBYTE(rax) == 0x0) {                     rax = [r14 isEqualToString:@"com.sg.minesweepermac.robot30"];                     LODWORD(r13) = 0x1e;                     if (LOBYTE(rax) == 0x0) {                             rax = [r14 isEqualToString:@"com.sg.minesweepermac.robot60"];                             LODWORD(r13) = 0x3c;                             if (LOBYTE(rax) == 0x0) {                                     rax = [r14 isEqualToString:@"com.sg.minesweepermac.robot90"];                                     LODWORD(r13) = 0x5a;                                     if (LOBYTE(rax) == 0x0) {                                             LODWORD(r13) = LODWORD(0x0);                                     }                             }                     }             }     }     rbx = *objc_msgSend;     LODWORD(r15) = LODWORD([[GameState sharedInstance] robot]);     rax = [GameState sharedInstance];     [rax setRobot:LODWORD(LODWORD(r15) + LODWORD(r13))];     rax = [GameState sharedInstance];     [rax save];      //...... } 

可以发现,购买成功后通过不同的产品id来增加不同的安全帽数量,然后通过 -[GameState setRobot:] 方法保存安全帽的数量。

也就是说,属性 robot 储存了安全帽的数量,如果hook了 -[GameState robot] 方法,返回999个安全帽的话,就有用不完的安全帽了。

参考代码如下:

@implementation NSObject (GameStateHook)  + (void)hook_GameState {     [self jr_swizzleMethod:@selector(robot)                 withMethod:@selector(hook_robot)                      error:nil]; }  - (int)hook_robot {     return 999; }  @end 

具体工程代码可以在 MinesweeperPlugin 下载。

编译工程后,重新运行扫雷,安全帽的数量也变成了999个了:

破解Mac版扫雷游戏

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » 破解Mac版扫雷游戏

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
分享按钮