本帖最后由 水波摇曳 于 2015-11-2 10:07 编辑
2015移动安全挑战赛是由看雪和阿里巴巴联合举办的面向移动安全爱好者的比赛,感谢逆向工程界的前辈看雪论坛和非常重视技术积累的阿里巴巴为推动移动安全在国内的发展和普及所作出的贡献!
这次实战的目的,是带大家一起做一做这次比赛的第1个iOS题,感受一下iOS逆向工程在比赛实战中的运用,如果你有更好的解题思路,欢迎参与讨论
附第1题下载:百度网盘安装ipa
不知为何,这个ipa在我的iPhone 5, iOS 8.1.2上安装之后无法启动,每次启动均会闪退;我采用了ldid重签名和把app放在/Applications下的方案,都没有解决闪退的问题,不知道是不是出题人故意留给我们的一道坎?我没有深究这个问题,而是尝试在我的iPhone 4s, iOS 6.1.3安装此ipa,竟意外发现app可以成功打开了,如图所示: 把玩app
安装成功之后,我们先随便操作一下,看看针对不同的输入,这个app会给我们怎样的反馈。先直接点击“进入”,发现此app直接退出了;再次打开app,输入snakeninny,结果出现了“密码错误”的提示,如图所示: 跟我一起思考——
错误提示一定是由代码控制的;既然弹出了错误提示,说明在弹出错误提示的代码之前,一定有输入是否错误的判断;它应该就是我们的输入与正确答案是否相同的判断。因为点击“进入”之后弹框,那么判断操作一定是在按钮的响应函数之后发生的;如果能够找到这个按钮响应函数,就可以顺藤摸瓜,找到这个判断。
那么我们今天的任务,就是找到这个判断的代码,从而找到正确答案。开始操作! 用Cycript找到“进入”按钮的响应函数这个步骤在书上已经不知道重复多少遍了,接下来的操作主要以代码表示,伴以少量解说,跟紧喽! [AppleScript] 纯文本查看 复制代码 FunMaker-4s:~ root# ps -e
PID TTY TIME CMD
1 ?? 0:31.21 /sbin/launchd
26 ?? 5:53.88 /usr/libexec/UserEventAgent (System)
...
3060 ?? 0:02.52 /var/mobile/Applications/3E693339-7347-4129-887D-1DBE0C0587AE/level1.app/level1
3079 ?? 0:00.02 /usr/libexec/launchproxy /usr/sbin/sshd -i
3082 ?? 0:00.28 sshd: root@ttys000
3083 ttys000 0:00.04 -sh
3084 ttys000 0:00.01 ps -e
FunMaker-4s:~ root# cycript -p level1
cy# ?expand
expand == true
cy# [[UIApp keyWindow] recursiveDescription]
@"<UIWindow: 0x1fde5d30; frame = (0 0; 320 480); autoresize = W+H; layer = <UIWindowLayer: 0x1fde5e30>>
...
| | <UIButton: 0x1fd5acd0; frame = (110 348; 100 30); opaque = NO; layer = <CALayer: 0x1fd5ac50>>
| | | <UIButtonLabel: 0x20872890; frame = (31 4; 37 22); text = '\xe8\xbf\x9b\xe5\x85\xa5'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x208729f0>>
| | <UIImageView: 0x1fd59030; frame = (0 420; 125.5 40); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x1fd596e0>>
| | <UILabel: 0x1fd58820; frame = (246 430; 64 20); text = '\xe9\x98\xbf\xe9\x87\x8c\xe9\x92\xb1\xe7\x9b\xbe'; clipsToBounds = YES; userInteractionEnabled = NO; layer = <CALayer: 0x1fd587f0>>
| | <UIImageView: 0x1fd57c40; frame = (218 427.5; 25 25); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x1fd57ca0>>"
cy# button = #0x1fd5acd0
#"<UIButton: 0x1fd5acd0; frame = (110 348; 100 30); opaque = NO; layer = <CALayer: 0x1fd5ac50>>"
cy# [button setHidden:YES]
进入”按钮不见了,界面变成了这样:
继续操作:
[AppleScript] 纯文本查看 复制代码 cy# [button allTargets]
[NSSet setWithArray:@[#"<ViewController: 0x20869990>"]]]
cy# [button allControlEvents]
64
cy# [button actionsForTarget:#0x20869990 forControlEvent:64]
@["onClick"]
好了,按钮的响应函数是[ViewController onClick],开始分析汇编代码。 从[ViewController onClick]出发,找到答案[ViewController onClick]的汇编执行流程如图所示:
整个流程并不复杂(比书上的几个例子要简单得多),大致浏览一遍,你就一定会注意到下图这个红色的方块: 它的汇编代码是: 很明显,这段汇编代码从textField里读text,转换成UTF8String之后和某个东西作判断,并根据判断结果跳转。这个现象跟我们上一节作出的预测不谋而合,我们在这段代码的开头下个断点,然后单步执行,看看每个objc_msgSend都是做的什么操作。
先用debugserver挂载level1: FunMaker-4s:~ root# debugserver *:1234 -a level1
debugserver-199 for armv7.
Listening to port 1234...
然后用LLDB连过去: [AppleScript] 纯文本查看 复制代码 192:~ snakeninny$ lldb
(lldb) process connect connect://localhost:1234
2015-10-19 16:29:37.015 lldb[15434:956234] Metadata.framework [Error]: couldn't get the client port
Process 3112 stopped
* thread #1: tid = 0x1e03, 0x3c606eb4 libsystem_kernel.dylib`mach_msg_trap + 20, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x3c606eb4 libsystem_kernel.dylib`mach_msg_trap + 20
libsystem_kernel.dylib`mach_msg_trap:
-> 0x3c606eb4 <+20>: pop {r4, r5, r6, r8}
0x3c606eb8 <+24>: .long 0xe12fff1e ; unknown opcode
libsystem_kernel.dylib`mach_msg_overwrite_trap:
0x3c606ebc <+0>: mov r12, sp
0x3c606ec0 <+4>: push {r4, r5, r6, r8}
(lldb) c
Process 3112 resuming
在0000B76A下断点(这些操作也都在书上重复过无数遍了,下面还是以码代字): [AppleScript] 纯文本查看 复制代码 (lldb) image list -o -f
[ 0] 0x00054000 /var/mobile/Applications/3E693339-7347-4129-887D-1DBE0C0587AE/level1.app/level1(0x0000000000058000)
[ 1] 0x00077000 /Library/MobileSubstrate/MobileSubstrate.dylib(0x0000000000077000)
[ 2] 0x0328e000 /Users/snakeninny/Library/Developer/Xcode/iOS DeviceSupport/6.1.3 (10B329)/Symbols/System/Library/Frameworks/Foundation.framework/Foundation
[ 3] 0x0328e000 /Users/snakeninny/Library/Developer/Xcode/iOS DeviceSupport/6.1.3 (10B329)/Symbols/System/Library/Frameworks/UIKit.framework/UIKit
...
(lldb) br s -a '0x00054000+0x0000B76A'
Breakpoint 1: where = level1`___lldb_unnamed_function36$$level1 + 202, address = 0x0005f76a
(lldb)
点击按钮,触发断点: [AppleScript] 纯文本查看 复制代码 Process 3112 stopped
* thread #1: tid = 0x1e03, 0x0005f76a level1`___lldb_unnamed_function36$$level1 + 202, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0005f76a level1`___lldb_unnamed_function36$$level1 + 202
level1`___lldb_unnamed_function36$$level1:
-> 0x5f76a <+202>: movw r0, #0x346c
0x5f76e <+206>: mov r10, r4
0x5f770 <+208>: movt r0, #0x1
0x5f774 <+212>: ldr.w r8, [sp, #0x10]
(lldb) ni
...
Process 3112 stopped
* thread #1: tid = 0x1e03, 0x0005f792 level1`___lldb_unnamed_function36$$level1 + 242, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x0005f792 level1`___lldb_unnamed_function36$$level1 + 242
level1`___lldb_unnamed_function36$$level1:
-> 0x5f792 <+242>: movw r1, #0x3492
0x5f796 <+246>: movt r1, #0x1
0x5f79a <+250>: add r1, pc
0x5f79c <+252>: ldr r5, [r1]
(lldb) po $r0
snakeninny
(lldb) ni
...
Process 3112 stopped
* thread #1: tid = 0x1e03, 0x0005f7a0 level1`___lldb_unnamed_function36$$level1 + 256, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x0005f7a0 level1`___lldb_unnamed_function36$$level1 + 256
level1`___lldb_unnamed_function36$$level1:
-> 0x5f7a0 <+256>: blx 0x6ff18 ; symbol stub for: objc_msgSend
0x5f7a4 <+260>: mov r4, r0
0x5f7a6 <+262>: mov r0, r6
0x5f7a8 <+264>: mov r1, r5
(lldb) p (char *)$r1
(char *) $3 = 0x39eb1d27 "UTF8String"
(lldb) ni
Process 3112 stopped
* thread #1: tid = 0x1e03, 0x0005f7a4 level1`___lldb_unnamed_function36$$level1 + 260, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x0005f7a4 level1`___lldb_unnamed_function36$$level1 + 260
level1`___lldb_unnamed_function36$$level1:
-> 0x5f7a4 <+260>: mov r4, r0
0x5f7a6 <+262>: mov r0, r6
0x5f7a8 <+264>: mov r1, r5
0x5f7aa <+266>: blx 0x6ff18 ; symbol stub for: objc_msgSend
(lldb) p (char *)$r0
(char *) $4 = 0x1c520110 "snakeninny"
(lldb) ni
Process 3112 stopped
...
* thread #1: tid = 0x1e03, 0x0005f7aa level1`___lldb_unnamed_function36$$level1 + 266, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x0005f7aa level1`___lldb_unnamed_function36$$level1 + 266
level1`___lldb_unnamed_function36$$level1:
-> 0x5f7aa <+266>: blx 0x6ff18 ; symbol stub for: objc_msgSend
0x5f7ae <+270>: mov r5, r0
0x5f7b0 <+272>: ldrb r0, [r5]
0x5f7b2 <+274>: cmp r0, #0x0
(lldb) p (char *)$r1
(char *) $5 = 0x39eb1d27 "UTF8String"
(lldb) po $r0
Sp4rkDr0idKit
(lldb) ni
Process 3112 stopped
* thread #1: tid = 0x1e03, 0x0005f7ae level1`___lldb_unnamed_function36$$level1 + 270, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x0005f7ae level1`___lldb_unnamed_function36$$level1 + 270
level1`___lldb_unnamed_function36$$level1:
-> 0x5f7ae <+270>: mov r5, r0
0x5f7b0 <+272>: ldrb r0, [r5]
0x5f7b2 <+274>: cmp r0, #0x0
0x5f7b4 <+276>: beq 0x5f7d6 ; <+310>
(lldb) p (char *)$r0
(char *) $7 = 0x1d016640 "Sp4rkDr0idKit"
...
看到了一个可疑的字符串“Sp4rkDr0idKit”,你应该已经猜到了,它就是我们的答案(不信的话自己试一下 )。知其然,还要知其所以然,我们继续分析,看看正确的判断逻辑是怎样的。 分析判断逻辑顺着上面的代码,我们接着分析: 每一行汇编是什么意思,我都已经标注在图里了,强烈建议你先自己分析一遍,有不明白的地方再看图,然后自己再分析一遍,直到自己能独立分析为止。
值得一提的是,这个判断逻辑有一个bug——即我们输入的字符串,只需要含有“Sp4rkDr0idKit”这个前缀就可以了,而不用全文匹配这个字符串,如图所示: 很荣幸snakeninny也能为拯救人类出一份力 http://7xibfi.com1.z0.glb.clouddn.com/images/emoji/apple/relaxed.png?v=0 结语从2015年开始,iOS方向的安全问题开始层出不穷,不管是带有后门的抢红包插件,还是植根开发工具的XcodeGhost,都给封闭且“安全”的苹果生态系统敲响了安全的警钟。当我在比赛的网站上提交第1题的答案时,看到早已有多位朋友提交了正确答案,让我知道自己不是一个人在战斗;当我对第2题束手无策时,曾经找我请教问题的一位在读研究生已经接近正确答案了,后浪已经开始把我们前浪拍死在沙滩上 这些朋友平常大都不在微博等社交平台上咋呼着炫技,而是默默钻研着技术,为这个社区贡献着自己的一份力量,“闷声发大财”。我最欣赏的就是这些为人低调,但做事高调的无名英雄,是他们推动着这个行业的发展。向所有默默耕耘,努力成长的朋友们致敬,衷心祝愿大家都能达到目标,实现理想
非常感谢本文作者:snakeninny 分享的文章 |