文档章节

解除iPadPiano的网络验证

我爱睡觉
 我爱睡觉
发布于 2017/06/24 17:37
字数 2117
阅读 10
收藏 0

转:http://bbs.pediy.com/showthread.php?p=1377794#post1377794

iPhone和iPad引起科技界革新的同时,也给音乐创作带来了全新的体验。抛开苹果自家的GarageBand如开发程序一般的音乐创作过程不说,AppStore上还有很多和演奏相关的应用。

比如钢琴谱大全(对应的二进制文件为iPadPiano),提供了海量乐谱不说,还有演奏教学功能,就是联动电钢琴,类似学习打字一样的效果,对于每个乐谱片段屏幕提示按键,按对了就继续,不对的话就卡在那里,不识谱也能学会弹琴。官方虽然指定了GEEK智能钢琴,但实际上有MIDI接口的电钢琴都能使用这个App。
不买会员,一天就只让播放一首曲目,让人怎么愉快地找到想要的曲谱呢。用iPad这么久,也该折腾一下了。看雪上有不少好文章,参考着也是边学边用,发现很多技巧都随着系统的升级有了微小的改动。

我用的是iPad mini 3(iOS 8.1),指令集是armv7s,最开始图方便,还是用了cydia.radare.org源提供的gdb(google了一圈,好像目前能在iOS上跑的gdb也就这一个)。实际用起来,发现它对armv7s支持的并不好,没法自动切换thumb指令集,需要人为指定$pc+1为反汇编位置不说,还因此不能单步执行,只能用IDA分析好以后,找特定地址中断查看寄存器聊以自慰。考虑到分析APP的第一步都是解密二进制程序,既然目前常见的文章都是用gdb来dump程序的,就暂时将就一下吧。

iPadPiano有Fat headers,同时包括了armv7和armv7s的指令:
mac$ otool -f iPadPiano
Fat headers
fat_magic 0xcafebabe
nfat_arch 2
architecture 0
    cputype 12
    cpusubtype 9 (armv7)
    capabilities 0x0
    offset 16384
    size 8105232
    align 2^14 (16384)
architecture 1
    cputype 12
    cpusubtype 11 (armv7s)
    capabilities 0x0
    offset 8126464
    size 8105056
    align 2^14 (16384)


加密的区域,无论是armv7还是armv7s,看来都是一样的大小。
mac$ otool -l iPadPiano | grep crypt
     cryptoff 16384
    cryptsize 6733824
      cryptid 1
     cryptoff 16384
    cryptsize 6733824
      cryptid 1


运行iPadPiano后,gdb附加并dump已经解密的代码片段。
iOS# gdb -p 1793
(gdb) set height 20
(gdb) info sharedlibrary 
1 iPadPiano - 0x33000 exec C C /private/var/mobile/Containers/Bundle/Application/96A72F1A-6342-4136-9084-36B83FDA0118/iPadPiano.app/iPadPiano at 0x33000 (offset 0x2f000)


得到iPadPiano的镜像基地址为0x33000,所以要dump的内存范围是
Start = base address + cryptoff = 0x33000 + 16384 = 0x37000
End = base address + cryptoff + cryptize = 0x33000 + 16384 + 6733824 = 0x6a3000
(gdb) dump memory /tmp/dump.bin 0x37000 0x6a3000

然后将dump.bin写回原始二进制文件,由于Fat header的存在,所以实际写回的文件位置需要加上armv7s所在段的偏移,所以写回的起始地址为:8126464 + 16384 = 8142848
mac$ dd seek=8142848 bs=1 conv=notrunc if=./dump.bin of=./iPadPiano 
6733824 bytes transferred in 13.992726 secs (481237 bytes/sec)


最后就是清零cryptid(参考http://bbs.pediy.com/showthread.php?t=152843):
搜索十六进制的2100000014000000,对于Fat header的程序可以找到两个,显然分别对应armv7和armv7s。找到后,加上16的偏移量应该是一个01,这就是cryptid的值,修改为00即可。
007C09B0: 00 00 00 00 21 00 00 00 14 00 00 00 00 40 00 00 
007C09C0: 00 C0 66 00 01 00 00 00


为方便调试,顺便关闭ASLR(参考http://bbs.pediy.com/showthread.php?t=167398):
从Fat header中armv7s所在的偏移8126464找起,将21改为01即可
007C0000: CE FA ED FE 0C 00 00 00 0B 00 00 00 02 00 00 00  
007C0010: 2E 00 00 00 78 12 00 00 85 80 21 00


现在将iPadPiano放回iPad后就可以正常运行了,由于没有ASLR,IDA里看到的地址就是调试时用的地址。

实际试了几轮,gdb带来的麻烦太多,支持的数据类型也少,最后还是选择了lldb,iOS端使用的debugserver需要修改签名什么的,实在不想在支线剧情再花精力了,下载一份修改好的,放到/usr/bin,就能进行调试了:
iOS# ps ax | grep iPadPiano
1933 0:30.00 /var/mobile/Containers/Bundle/Application/96A72F1A-6342-4136-9084-36B83FDA0118/iPadPiano.app/iPadPiano
iOS# debugserver 0.0.0.0:8888 -a 1933
Listening to port 8888 for a connection from 0.0.0.0...
mac$ lldb
(lldb) platform select remote-ios                                                                                       
(lldb) process connect connect://192.168.1.158:8888


每次点选谱子后都有一个下载谱子的过程,所以想当然在IDA里找download字样的函数,一个一个的看里面的字串和网址,配合断点验证,确定了下载谱子的关键函数:
[BrowsersController beginDownloadSpectrum:requestType:]
函数向下两个区块,就到了构造下载曲谱发送请求的位置:


在iOS代码中,函数调用是通过obj_msgSend完成的,如果在blx指令断点处观察,r1寄存器指向的字符串就是要调用的函数名字,r2,r3,r9,r12则是函数的参数。

在上图代码下方不远处,即调用构造请求地址的位置断点:
(lldb) b 0x39e794
Breakpoint 1: where = iPadPiano`___lldb_unnamed_function13$$iPadPiano + 68892, address = 0x0039e794
(lldb) c
Process 1933 resuming



当点击乐谱的客户端播放的时候,断点触发
(lldb) register read
General Purpose Registers:
        r0 = 0x0da93870
        r1 = 0x301dd74b  "stringByAppendingFormat:"
        r2 = 0x0068264c  @"&deviceid=%@&v=%@&%@"
        r3 = 0x009a6ac0
        r4 = 0x3a080f41  libobjc.A.dylib`objc_msgSend + 1
        r5 = 0x0071a6c4  "getUnloginPaymentInfo"
        r6 = 0x00778f6c  
        r7 = 0x008c15fc
        r8 = 0x3a080f41  libobjc.A.dylib`objc_msgSend + 1
        r9 = 0x0068068c  @"2015-02-12"
       r10 = 0x00718b00  "stringByAppendingString:"
       r11 = 0x0071a6c0  "mb_key"
       r12 = 0x0067dafc  @""
(lldb) print (NSString *)$r3
"fa7da8c8a0acaf53e562cc533c781cbf22eab670.020000000000"
(lldb) ni
(lldb) dis -A thumbv7s -s $pc-2
iPadPiano`___lldb_unnamed_function13$$iPadPiano:
    0x39e794 <+68892>: blx    lr
->  0x39e796 <+68894>: str    r0, [sp, #0x134]
(lldb) print (NSString *)$r0
@"http://www.tan8.com/request_ypad_pay.php?ypid=25024&deviceid=fa7da8c8a0acaf53e562cc533c781cbf22eab670.020000000000&v=2015-02-12&"


最终用于下载乐谱的请求网址就是它了,该表达式直接用浏览器提交的话,也是有返回结果的,如下图:


显然今天唯一的一次试用次数已经用完了,将deviceid修改一下(第一个字母从f修改成e),就又通过认证了如下图:


这个deviceid再提交一次请求仍会出现试用已经结束的字样。
每天一次的试用应该是通过deviceid来校验的,服务端数据库存储了当天提交过请求的设备,如果查询到同样的设备号就给毙了。
抛开搞掉服务端的思路不说,至少可以在本地每次提交请求的时候随机改动deviceid,保证大多数时候不出现冲突就行了。

看以往的思路,都是动态库注入,挂钩关键函数来着,所以也打算照着做一次。
不过图省事儿,打算还是纯C实现。
至于挂钩函数是直接替换程序中输入表里面的地址(也不知道叫输入表对不对,反正是IDA中__objc_const区域的内容)

(lldb) x/3xw 006DD200
0x006dd200: 0x00583c9e 0x005ca3eb 0x0039e475
(lldb) x/s 0x00583c9e
0x00583c9e: "beginDownloadSpectrum:requestType:"
(lldb) x/s 0x005ca3eb
0x005ca3eb: "v16@0:4@8i12"
(lldb) dis -A thumbv7s -s 0x0039e475
iPadPiano`___lldb_unnamed_function13$$iPadPiano:


挂钩的时候,只要替换006DD208处的0x0039e475为自定义的函数就可以了。beginDownloadSpectrum有四个参数(要是看得懂"v16@0:4@8i12",估计也交代了参数情况),根据IDA的反汇编结果,对于未知类型的参数用int *代替,反正也是直接转手交给源函数处理,能传递过去就可以。
成功挂钩后,执行原函数之前直接修改
"&deviceid=%@&v=%@&%@"

"&deviceid=%@RND&v=%@"
末尾的&%@,去掉无任何影响,因为本来提交请求中这个部分也是被空字符串填充,富余出来的三个字符位置刚好可以给RND。
RND为3位随机的字符,附加在原来的deviceid参数后形成新的设备号作为请求提交,这样保证每天几百次的下载使用足以了。
按照这个思路,用于注入到原始程序的代码iosinject.c为:

#include <dlfcn.h>
#include <mach/mach.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

void install(void) __attribute__ ((constructor));

int deviceid_address = 0x005e3ae2;
int func_address = 0x6dd208;

void hookfunc(int *,char *,int *,int);
void (*orgfunc)(int *,char *,int *,int);

void install()
{
  int *p = (int *)func_address;
  orgfunc = (void (*)(int *,char *,int *,int))(*p);
  *p = (int)hookfunc;
}

void hookfunc(int *self,char *name,int *id,int zero)
{
  char *deviceid = (char *)deviceid_address;
  char orgstring[] = "&v=%@";
  int i;
  mach_port_t port = mach_task_self();
  vm_protect(port,(vm_address_t)deviceid_address,8,FALSE,VM_PROT_READ|VM_PROT_WRITE|VM_PROT_COPY);
  for(i=0;i<3;i++)
  {
    deviceid[i]=rand()%10+0x30;
  }
  strcpy(deviceid+i,orgstring);
  vm_protect(port,(vm_address_t)deviceid_address,8,FALSE,VM_PROT_READ|VM_PROT_COPY);
  orgfunc(self,name,id,zero);
}


编译上述代码:
mac$ export ISYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk"
mac$ gcc -arch armv7s -L$ISYSROOT/usr/lib/system --sysroot=$ISYSROOT  -o iosinject.dylib  -dynamiclib iosinject.c


得到iosinject.dylib后,再编写一个与之配套的iosinject.plist,内容为
Filter = {Executables = ("iPadPiano");};

共同上传到iOS端的/Library/MobileSubstrate/DynamicLibraries/
有关动态库注入的方法,参考http://www.cydiasubstrate.com/inject/darwin/
这回iPadPiano可以随意下载乐谱了。

本文转载自:http://blog.csdn.net/zhangmiaoping23/article/details/46633135

我爱睡觉
粉丝 3
博文 2120
码字总数 0
作品 0
南昌
私信 提问
Kali Linux 无线渗透测试入门指南 第五章 攻击 Web 设施

第五章 攻击 Web 设施 作者:Vivek Ramachandran, Cameron Buchanan 译者:飞龙 协议:CC BY-NC-SA 4.0 简介 故上兵伐谋 – 孙子,《孙子兵法》 这一章中,我们会攻击 WLAN 设施的核心。我们...

apachecn_飞龙
2016/10/25
0
0
Kali Linux 无线渗透测试入门指南 第三章 绕过 WLAN 身份验证

第三章 绕过 WLAN 身份验证 作者:Vivek Ramachandran, Cameron Buchanan 译者:飞龙 协议:CC BY-NC-SA 4.0 简介 安全的错觉比不安全更加糟糕。 – 佚名 安全的错觉比不安全更加糟糕,因为你...

apachecn_飞龙
2016/10/21
0
0
网络自由访问 巧解除Win XP文件共享限制

< DOCTYPE html PUBLIC -WCDTD XHTML StrictEN httpwwwworgTRxhtmlDTDxhtml-strictdtd>  笔者的很多朋友都曾遇到过这样的问题:在安装了Windows XP的计算机上,即使网络连接和共享设置正确(...

技术小牛人
2017/11/08
0
0
Java使用读写锁替代同步锁

应用情景 前一阵有个做反抄袭检测的小伙伴问了我一个问题。 --- 在多线程里就是有个变量,我需要读取它来判断是否给它写入一些信息。 打算加锁,但是如果读取时候加入readlock,写入时候加入...

野梦M
2017/11/28
0
0
Evil-Twin 框架:一个用于提升 WiFi 安全性的工具

了解一款用于对 WiFi 接入点安全进行渗透测试的工具。 越来越多的设备通过无线传输的方式连接到互联网,以及,大范围可用的 WiFi 接入点为攻击者攻击用户提供了很多机会。通过欺骗用户连接到...

作者: André Esser
02/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

移动开发中的 Web:WebView、WebKit、JSCore、Web 优化、热修复、跨平台、Native、Hybrid……

移动开发领域近年来已经逐渐告别了野蛮生长的时期,进入了相对成熟的时代。而一直以来 Native 和 Web 的争论从未停止,通过开发者孜孜不倦的努力,Web 的效率和 Native 的体验也一直在寻求着...

编辑部的故事
12分钟前
8
0
MySQL8.0.17 - Multi-Valued Indexes 简述

本文主要简单介绍下8.0.17新引入的功能multi-valued index, 顾名思义,索引上对于同一个Primary key, 可以建立多个二级索引项,实际上已经对array类型的基础功能做了支持 (感觉官方未来一定...

阿里云官方博客
58分钟前
9
0
make4.1降级 make-3.81、2错误

在编译 make-3.82 的时候出现如下错误提示 glob/glob.c:xxx: undefined reference to `__alloca'` 修改 /glob/glob.c // #if !defined __alloca && !defined __GNU_LIBRARY__ # ifdef __GNUC......

Domineering
59分钟前
13
0
Rainbond集群的安装和运维的原理

本文将解读Rainbond集群的安装和运维的原理,使用户基本了解Rainbond的安装机制和运维重点,便于用户搭建大型Rainbond集群。 1.Rainbond集群节点概述 1.1 节点分类 属性 类型 说明 manage 管...

好雨云帮
今天
10
0
好程序员大数据学习路线分享UDF函数

1.为什么需要UDF? 1)、因为内部函数没法满足需求。 2)、hive它本身就是一个灵活框架,允许用自定义模块功能,如可以自定义UDF、serde、输入输出等。 2.UDF是什么? UDF:user difine fun...

好程序员官方
今天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部