首页
社区
课程
招聘
[原创]iOS软件调试初探(一个破解app的例子)
发表于: 2013-7-1 09:29 34209

[原创]iOS软件调试初探(一个破解app的例子)

2013-7-1 09:29
34209
iOS软件调试初探
写在前面:
最近热衷于一款背单词软件,但是有学习限制,于是就想尝试着破解。同时也顺便学习iOS安全方面的知识。本文以某某背单词软件为例子,一步一步介绍如何调试、破解,高手请无视,有纰漏之处欢迎指正。

免责声明:
文本涉及到的内容仅供学习,切勿用于商业用途。

准备工作:
动手前在论坛学习了一下,http://www.kanxue.com/bbs/showthread.php?t=167398和http://www.kanxue.com/bbs/showthread.php?t=138472两个帖子,按照文中介绍的步骤来,但是遇到了很多其他的问题,期间问题解决的过程也很坎坷。首先介绍gdb调试的方法:在一台越狱的设备上,cydia改为开发者模式,安装

>OpenSSH,作为SSH服务端;
>GNU Debugger(gdb调试工具):在这个源中cydia.radare.org,版本为1708,低版本不支持ios4.3+。
>adv-cmds:ps命令可以查看进程信息;
>darwin cc tools:otools可以查看可执行文件的详细信息;
>Link Identity Editor:ldid签名;

>PC上安装SSH Secure Shell Client

分析程序、反汇编代码:
在本例中要做的是破解软件学单词的数量限制,在文件管理器中查看软件目录,发现Learn.db应该存储了程序的参数信息,用sqlite browser之类的软件打开数据库,看到如下:

打开这个表,看到这一项:
应该是单词学习上限,只要修改这个值就可以轻松解除限制了,但是还看到了另一个东西:

应该是做校验的,看来直接修改还不行,要调试代码知道如何计算这个码才可以。这里我们先直接修改nStudyLimit为50000(毫不吝啬),另外备份一下这个db,保存复制回手机中。如果此时执行程序,会提示异常数据:


这是自然,因为擅自修改了文件,程序校验失败。用ida6.4打开软件中的执行文件,左侧可以看到程序中的函数名,根据名字猜测,看到在checkLearnNum这个函数中:


有关键信息,应该是在这个函数里做的校验。好我们记住这个函数附近的一个地址:0007610A,一会调试代码下断点要用到。

连接SSH:
要保证pc和手机连入同一个局域网中,ssh客户端中host即为手机ip地址,端口22,第一次接入账号为root,alpine,最好修改默认密码为你自己常用的。

如果没有无线设备可供使用,可以用数据线将手机连入pc,下载itools,打开其中的SSH隧道功能:


这时的host主机名为127.0.0.1。如图就算连接成功了:


熟悉gdb调试:
记住以下常用命令就足够了:

ps -ax:查看当前所有进程

Gdb -p pid:附加到目标进程

Info sh:这个可以查看程序代码在内存中的偏移地址

Break:下断点

display /i $pc | $cpsr.t:显示要执行的下一句指令

continue(或c):继续执行;

Nexti(或ni):单步执行一条汇编指令

Po $rN(N为数字,打印寄存器存储的对象,寄存器实际存储的是对象的地址)

Print $rN(打印寄存器中的值)

Set $rN=xxxx(给寄存器赋值)

先在手机上启动你要调试的程序,执行ps -ax查看目标进程的pid:

 588 ??         0:04.17 /var/mobile/Applications/77FFC04E-1B91-46CE-909C-CE886

然后附加到进程:

Gdb -p 588

过一会会显示........done.执行info -sh查看代码的偏移地址:

Num Basename                 Type Address         Reason | | Source     

  | |                           | |                    | | | |          

  1 LearnEnglish                - 0x8b000           exec Y Y /private/var/mobile/Applications/77FFC04E-1B91-46CE-909C-CE886A9060ED/LearnEnglish.app/LearnEnglish at 0x8b000 (offset 0x8a000)

最后的offset 0x8a000即为偏移地址,记下。此处有个窍门,info -sh后可能会输出很多信息,导致前面的都滚屏了,此时可在执行此命令后立即按pause键暂停,找到偏移地址后按方向键下,继续执行:)

此时需要先下断点,用之前我们找到的那个函数地址,执行break *(0x8a000+0x0007610A)下断点,输出以下说明成功:

Breakpoint 1 at 0x10010a

接着要再执行:

display /i $pc | $cpsr.t

这样之后我们每次单步执行都能输出下一句的代码来提示我们执行到了哪里。

此时执行c,让程序跑起来,在手机上操作程序,点击学习单词,看到gdb输出:

Breakpoint 1, 0x0010010a in -[WordsUIViewController checkLearnNum] ()

1: x/i $pc | $cpsr.t  0x10010a:  6f f0 34 ee                   blx      0x16fd74

说明断点下对了,我们找对了地方。

执行ni,向下走一步,此时要对比着ida中的代码一步步走,以防我们跟丢了:


我们单单只看这几句:

MOVW            R2, #(:lower16:(cfstr_Nstudylimit - 0x7611C)) ; "nStudyLimit"

MOV             R1, R4

MOVT.W          R2, #(:upper16:(cfstr_Nstudylimit - 0x7611C)) ; "nStudyLimit"

ADD             R2, PC  ; "nStudyLimit"   //这里猜测应该是加载函数地址

BLX             _objc_msgSend     //然后这句是执行

MOV             R11, R0 //R0寄存器存储的应该是函数返回的值,也就是获取nStudyLimit的值。

我们ni执行到MOV R11,R0这句,执行po $r0:

(gdb) po $r0                     

50000

可以看到输出的是50000,就是我们之前修改的值。而且这里应该是NSString类型的字符串。

LDR             R1, [R0] ; "currentDevice"

LDR             R0, [R2] ; _OBJC_CLASS_$_UIDevice

BLX             _objc_msgSend

MOV            R1, #(selRef_uniqueGlobalDeviceIdentifier - 0x76164) ; selRef_uniqueGlobalDeviceIdentifier

ADD             R1, PC ; selRef_uniqueGlobalDeviceIdentifier

LDR             R1, [R1] ; "uniqueGlobalDeviceIdentifier"  //获取设备标识

BLX             _objc_msgSend

MOV             R3, R0

MOV             R0, #(selRef_stringWithFormat_ - 0x7617E) ; selRef_stringWithFormat_

MOV             R1, #(classRef_NSString - 0x76180) ; classRef_NSString

ADD             R0, PC ; selRef_stringWithFormat_

ADD             R1, PC ; classRef_NSString

MOVW           R2, #(:lower16:(stru_FC200 - 0x76198)) ; "%@%@%@"

LDR.W           R10, [R0] ; "stringWithFormat:"

MOVT.W         R2, #(:upper16:(stru_FC200 - 0x76198)) ; "%@%@%@"

LDR             R0, [R1] ; _OBJC_CLASS_$_NSString

MOV       R4, #(cfstr_1ea99222e762c6 - 0x7619A) ; "1ea99222e762c6483e165958b584009e"

ADD             R2, PC  ; "%@%@%@"     //这里应该就是将某三个值组合到一起

ADD             R4, PC  ; "1ea99222e762c6483e165958b584009e"

MOV             R1, R10

STMEA.W         SP, {R4,R11}

BLX             _objc_msgSend

MOV             R1, #(selRef_md5 - 0x761AE) ; selRef_md5

ADD             R1, PC ; selRef_md5

LDR             R1, [R1] ; "md5"            //然后算出md5值作为校验码

BLX             _objc_msgSend

MOVW            R1, #(:lower16:(selRef_isEqualToString_ - 0x761C0))

MOV             R2, R5

MOVT.W          R1, #(:upper16:(selRef_isEqualToString_ - 0x761C0))

ADD             R1, PC ; selRef_isEqualToString_

LDR             R4, [R1] ; "isEqualToString:"   //与数据库中sLimitCheckCode对比,如果两个码一致,则通过校验,继续执行。

MOV             R1, R4

BLX             _objc_msgSend

TST.W           R0, #0

BEQ             loc_76284

LDR.W           R0, [R8] ; _OBJC_CLASS_$_CallDB

MOV             R1, R6

BLX           

分析了指令之后我们继续执行,到后面再输出寄存器r0的值:

(gdb) po $r0

0d94efdXXXXfd0d2805cc99e519XXd411ea99222e762c6483e165958b584009e50000

0d94efdXXXXfd0d2805cc99e是我的设备标识(这里插一句,百度uniqueGlobalDeviceIdentifier函数是个开源方法,算法是获取手机WIFI地址,然后md5);

519XXd41是前面获取的getPersonalValue;

1ea99222e762c6483e165958b584009e是代码里的一个常量;

50000是修改的nStudyLimit。

至此,我们就知道了sLimitCheckCode的算法。继续执行知道程序算出md5:

(gdb) po $r0

9ff8f3f58883ecc51eeb32358382894e

这是根据我们当前nStudyLimit算出的校验码;

前面的:

ADD             R2, PC  ; "sLimitCheckCode"

BLX             _objc_msgSend

MOV             R5, R0

可知r5存储的是数据库中的校验码,我们只要把r5赋值为我们自己的那个校验码就能通过检查了:

(gdb) set $r5 = [[NSString] alloc] initWithString:@"9ff8f3f58883ecc51eeb32358382894e"] 

这么写是为了让你看到gdb确实很强大,可以直接写代码上去,我孤陋寡闻了:)

LDR             R4, [R1] ; "isEqualToString:"

MOV             R1, R4

BLX             _objc_msgSend   //比两个字符串较

TST.W           R0, #0   //测试指令,看比较结果是否一致

BEQ             loc_76284  //不一致就跳转了

LDR.W           R0, [R8] ; _OBJC_CLASS_$_CallDB

好,看到代码按我们期望的那样继续执行了,我们直接输入continue大胆放行:)


通过了,先别高兴的太早,我们还要做最后的步骤,将数据库中的校验码改为刚才在内存中算好的校验码:

先在程序断点的情况下执行kill结束进程,然后q退出gdb。


复制到手机中,执行程序,可以看到启动后也没有提示数据异常了,可以正常添加生词没有限制了:)

词库中总共3万多词汇,修改的限制为5万,改一次以后就不用动了。目前还有其他问题,修改了数据库后软件中无法备份学习进度到服务器,提示数据异常,可能还有别的校验,但是这不影响正常使用,这就不再讨论了,有兴趣的同学可以继续研究。

debugserver远程调试遇到的问题及解决:
手机连接xcode真机调试一次,会向iphone中安装debugserver;cydia中安装openSSH等插件(具体看pediy论坛的iOS平台应用程序调试与分析),PC安装SSH客户端,连接手机,执行debugserver来运行要调试的程序;如果出现错误如:failed to get task of process xxxx说明app没有权限,需要在mac系统下执行codesign用Entitlements.plist重新给app签名来添加权限。如果出现签名失败的问题,是系统环境不对,需要安装Command Line Tool(最好搭建完整的越狱开发环境)。参考:http://www.cnblogs.com/Proteas/archive/2012/12/20/2826928.html

创建名为Entitlements.plist文件,内容如下:
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.你要破解的软件的Bundle ID</key>
    <true/>
    <key>get-task-allow</key>
    <true/>
    <key>task_for_pid-allow</key>
    <true/>
    <key>run-unsigned-code</key>
    <true/>
</dict>
</plist>

执行

codesign -f -s "iPhone Developer" --entitlements Entitlements.plist LearnEnglish.app

将这个app文件替换掉手机中的文件夹,记得给里面可执行文件添加执行权限(通常用iFile)。执行:

cd /Developer/usr/bin

./debugserver port:2008 /var/mobile/Applications/7F2975ED-9CB6-42E0-B2AA-A5E017E1C4BB/LearnEnglish.app/LearnEnglish

这时程序自动启动,SSH客户端也应该出现Listening port:2008,IDA中就可以远程调试了。

[课程]Linux pwn 探索篇!

上传的附件:
收藏
免费 5
支持
分享
最新回复 (19)
雪    币: 1098
活跃值: (193)
能力值: (RANK:210 )
在线值:
发帖
回帖
粉丝
2
标记优秀鼓励!
把http://www.modiy.net/bbs/showthread.php?t=167398连接里的www.modiy.net改为www.kanxue.com吧。
2013-7-1 10:04
0
雪    币: 18
活跃值: (28)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
感谢版主。。。
我也是看了你的帖子学习的,主要是遇到了一些别人很少遇到的问题,贴出解决办法,算是经验分享。
2013-7-1 10:22
0
雪    币: 2323
活跃值: (4113)
能力值: ( LV12,RANK:530 )
在线值:
发帖
回帖
粉丝
4
学习~~
2013-7-1 12:20
0
雪    币: 1585
活跃值: (182)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
学习学习灬
2013-7-1 21:21
0
雪    币: 123
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
非常好的教程!谢谢分享!
2013-7-1 21:55
0
雪    币: 153
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
学习了,备用
2013-7-3 13:18
0
雪    币: 116
活跃值: (111)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
学习了, 游戏中买钻能破解吗
2013-7-3 20:32
0
雪    币: 18
活跃值: (28)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
cydia有现成的内购破解
2013-7-4 09:50
0
雪    币: 86
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
学习学习
2013-7-9 14:28
0
雪    币: 12
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
最近的好帖子太多了,收藏学习了
2013-8-15 15:29
0
雪    币: 184
活跃值: (33)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
谢谢了。。
2013-9-12 09:00
0
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
13
怎么操作,遇见这个问题 求教程!
2014-3-16 02:41
0
雪    币: 123
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
这个一定要收藏起来!!!!
2014-4-28 10:33
0
雪    币: 163
活跃值: (1538)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
15
gdb 调试过程中
下完断点,c(continue).
如果程序加载新的模块,程序又没有断到。
输入任何命令都无效了,有办法继续吗,还是只能重来?
2014-7-3 09:24
0
雪    币: 163
活跃值: (1538)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
16
IDA分析为:
__text:00248AE0 C2 F3 C4 CF                         BLX.W           j__objc_msgSend
__text:00248AE4 03 46                                   MOV             R3, R0
__text:00248AE6 46 F6 C0 10 C0 F2 74 20  
MOV             R0, #(selRef_data_ - 0x248AF4)

而gdb调试的时候为
0x2bbae0:  c2 f3 c4 cf                   blx    0xe7ea6c
0x2bbae4:  03 46                         mov    r3, r0
0x2bbae6:  46 f6 c0 10                   movw   r0, #27072      ; 0x69c0
0x2bbaea:  c0 f2 74 20                   movt   r0, #628        ; 0x274

00248AE6  此mov指令被分析错了,是否有办法解决?
2014-7-17 17:40
0
雪    币: 28
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
学习到了,没想到还要这样一个一个程序的破解。。。
2014-7-17 23:51
0
雪    币: 2
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
MARKYIXIA
2016-8-26 11:21
0
雪    币: 2
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
19
路过
2017-3-24 09:40
0
雪    币: 117
活跃值: (1114)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
20
好老的文章
2017-5-3 15:57
0
游客
登录 | 注册 方可回帖
返回
//