首页
社区
课程
招聘
[分享]iOS 第三题解题思路 - 未尝试版
2015-10-19 11:26 2494

[分享]iOS 第三题解题思路 - 未尝试版

2015-10-19 11:26
2494
LZ就自己的分析思路做出一些总结,望点评。

准备工作如第一题,找到弹出alertview的地方。
也就是

__text:0000B070 ; void __cdecl -[ViewController onClick](struct ViewController *self, SEL)


然后往下看,有调用
__text:0000B0CC                 BL              ali_judge //  此处暂且起名为ali_judge


ali_judge作为判断用户输入的入口,返回值是TRUE和FALSE,分别是0、1。
如果匹配成功返回TRUE,失败返回FALSE。

进ali_judge看看,习惯性F5,显示空白
好吧,直接看汇编
__text:0001DCB8 ali_judge                               ; CODE XREF: -[ViewController onClick]+5Cp
__text:0001DCB8
__text:0001DCB8 var_4           = -4
__text:0001DCB8
__text:0001DCB8                 PUSH            {R1-R7,LR}
__text:0001DCBA                 LDR             R1, =func1
__text:0001DCBC                 MOV             R4, LR
__text:0001DCBE                 BL              loc_1DCC2
__text:0001DCC2 ; ---------------------------------------------------------------------------
__text:0001DCC2
__text:0001DCC2 loc_1DCC2                               ; CODE XREF: ali_judge+6j
__text:0001DCC2                 ADDS            R1, #0x1B
__text:0001DCC4                 MOV             LR, R4
__text:0001DCC6                 STR             R1, [SP,#0x1C]
__text:0001DCC8                 POP             {R1-R7,PC} ; goto pc_goto1


从结构来看比较正常,但实际上,很不正常
__text:0001DCC6                 STR             R1, [SP,#0x1C]

这句覆盖了原有的LR值,致使
POP             {R1-R7,PC} ;

直接跳转到了被手动修改的LR值处,也就是:
__text:0001DCEE pc_goto1                                ; r0=userinput
__text:0001DCEE                 PUSH            {R0,R1,LR}
__text:0001DCF0                 MOVW            R0, #0x2A5
__text:0001DCF4
__text:0001DCF4 loc_1DCF4                               ; CODE XREF: __text:0001E7C2j
__text:0001DCF4                 BLX             ali_router

这个地方,BLX调用的func暂且命名为ali_router(why?因为它会被调用N次,然后他又去调用其他FUNC)
现场被保留,带着r0开始了漫长的计算之旅。

进入ali_route后
// 保存现场
__text:00013EB4                 STMFD           SP!, {R0,R1}
// 此时r1为0001DCF4+0x4 = 0001DCF8,是一张表
__text:00013EB8                 MOV             R1, LR  ; r1 = ret
// 表中寻址
__text:00013EBC                 MOV             R1, R1,LSR#1 ; r1 = r1 >> 1
__text:00013EC0                 MOV             R0, R0,LSL#2 ; r0 = r0 << 2
__text:00013EC4                 ADD             R0, R0, #4 ; r0 = r0 + 4
__text:00013EC8                 MOV             R1, R1,LSL#1 ; r1 = r1 << 1
__text:00013ECC                 LDR             R1, [R1,R0] ; r1 = *(r1 + r0)
//  以lr为base,r1为offset,重新覆盖sp+8
__text:00013ED0                 ADD             LR, LR, R1 ; lr = lr + r1
// 恢复现场
__text:00013ED4                 LDMFD           SP!, {R0,R1} ; pop r0, r1
// r0为真正的返回地址
__text:00013ED8                 LDR             R0, [SP,#8] ; r0 = old lr from prev func
// 将手动修改的lr放入栈顶
__text:00013EDC                 STR             LR, [SP,#8] ; *(sp+8) = lr
// 修正真实的lr
__text:00013EE0                 MOV             LR, R0  ; lr = r0
// PC = 栈顶lr的值,直接跳转到手动修改的地址
__text:00013EE4                 LDMFD           SP!, {R0,R1,PC}

可以确定这是手工生成的代码(naked)。
其目的是根据r0的offset在跳转表(暂且称之为ali_route_table)中重新寻址,打乱执行顺序(高,实在是高)。

接下来将程序跑起来,运行一下,在__text:00013EE4处下个断点查看栈顶的值。看他跳转到哪里。

Breakpoint 1, 0x00013ee4 in _mh_execute_header ()
(gdb) p/x *($sp+0x8)
$1 = 0x21437
(gdb) c
Continuing.

Breakpoint 1, 0x00013ee4 in _mh_execute_header ()
(gdb) p/x *($sp+0x8)
$2 = 0x214af


这里截取了两次断点,分别是0x21437和0x214af,其一是入口。
进入IDA查看。
__text:00020FC8                 DCD 0xB5032C78, 0x207EF240, 0xFE90F7FC, 0xF8572900, 0xB5031C7C
__text:00020FC8                 DCD 0x2062F240, 0xFE88F7FC, 0x2101BF08, 0xF240B503, 0xF7FC2063
__text:00020FC8                 DCD 0xF857FE81, 0x42811C6C, 0xF240B503, 0xF7FC10A3, 0xF244FE79

直接就尿了,阿里果然牛逼,居然吧代码做成了一堆data。
IDA目前是不能直接反汇编的,估计有什么设置或者通过修改mach-o文件可以让它反汇编,但是不知道怎么直接搞,也懒得研究了。

既然IDA不能反汇编,就用GDB先将就看看。
(gdb) disassemble 0x21437 0x21437+100
Dump of assembler code from 0x21437 to 0x2149b:
0x00021437 <_mh_execute_header+119863>:	push	{r4, r5, r6, r7, lr}
0x00021439 <_mh_execute_header+119865>:	add	r7, sp, #12
0x0002143b <_mh_execute_header+119867>:	push	{r0, r1, lr}
0x0002143d <_mh_execute_header+119869>:	movw	r0, #685	; 0x2ad
0x00021441 <_mh_execute_header+119873>:	bl	0x1dcf4 <_mh_execute_header+105716>
0x00021445 <_mh_execute_header+119877>:	it	eq
0x00021447 <_mh_execute_header+119879>:	moveq.w	lr, #1	; 0x1
0x0002144b <_mh_execute_header+119883>:	push	{r0, r1, lr}
0x0002144d <_mh_execute_header+119885>:	movw	r0, #676	; 0x2a4
0x00021451 <_mh_execute_header+119889>:	bl	0x1dcf4 <_mh_execute_header+105716>
0x00021455 <_mh_execute_header+119893>:	movt	r3, #0	; 0x0
0x00021459 <_mh_execute_header+119897>:	add	r3, pc
0x0002145b <_mh_execute_header+119899>:	push	{r0, r1, lr}
0x0002145d <_mh_execute_header+119901>:	movw	r0, #327	; 0x147
0x00021461 <_mh_execute_header+119905>:	bl	0x1dcf4 <_mh_execute_header+105716>
0x00021465 <_mh_execute_header+119909>:	movt	r1, #61006	; 0xee4e
0x00021469 <_mh_execute_header+119913>:	movw	r2, #27716	; 0x6c44
0x0002146d <_mh_execute_header+119917>:	push	{r0, r1, lr}
0x0002146f <_mh_execute_header+119919>:	movw	r0, #683	; 0x2ab
0x00021473 <_mh_execute_header+119923>:	bl	0x1dcf4 <_mh_execute_header+105716>
0x00021477 <_mh_execute_header+119927>:	movw	r3, #27804	; 0x6c9c
0x0002147b <_mh_execute_header+119931>:	push	{r0, r1, lr}
0x0002147d <_mh_execute_header+119933>:	movw	r0, #679	; 0x2a7
0x00021481 <_mh_execute_header+119937>:	bl	0x1dcf4 <_mh_execute_header+105716>
0x00021485 <_mh_execute_header+119941>:	ldr	r2, [r2, #0]
0x00021487 <_mh_execute_header+119943>:	push	{r0, r1, lr}
0x00021489 <_mh_execute_header+119945>:	movw	r0, #681	; 0x2a9
0x0002148d <_mh_execute_header+119949>:	bl	0x1dcf4 <_mh_execute_header+105716>
0x00021491 <_mh_execute_header+119953>:	movt	r2, #0	; 0x0
0x00021495 <_mh_execute_header+119957>:	add	r2, pc
0x00021497 <_mh_execute_header+119959>:	push	{r0, r1, lr}
0x00021499 <_mh_execute_header+119961>:	movw	r0, #682	; 0x2aa
End of assembler dump.


前两句还算正常,push {r4-r7, lr},add        r7, sp, #12保存现场,分配Local变量内存。到后面就开始抽风了,哈哈~~
大量的类似这种
0x0002143b <_mh_execute_header+119867>:	push	{r0, r1, lr}
0x0002143d <_mh_execute_header+119869>:	movw	r0, #685	; 0x2ad
0x00021441 <_mh_execute_header+119873>:	bl	0x1dcf4 

其实我们之前分析过,这个0x1dcf4就是ali_router。此处的push        {r0, r1, lr}由ali_router最后一行进行pop。

但这中间也不乏参杂了一些其他指令,如
0x00021445 <_mh_execute_header+119877>:	it	eq
0x00021447 <_mh_execute_header+119879>:	moveq.w	lr, #1	; 0x1

LZ暂且认为他们是真正参与计算的指令。

通过以上的分析,得出一个结论:
实际上真正的计算func,被分成了N个block,然后打乱顺寻,由ali_router重新排序调用,排序规则由ali_route_table决定。最终得到完全执行。

现在就找方法去验证它(听起来好像离结果很近了呢!激动ing)。

方法1:
通过对ali_route的hook,得到每一次调用返回的PC值,
PC值打印顺序,就是正确的调用顺序。实际上。。。

LZ之后做了一个HOOK,记录了PC的值,存到一个文件里,文件超过几百兆(中途觉得不对已经强制停止),估计有几十、几百万行,N多次调用,简直无法直视。

试想,谁会把一个字符串比较做的如此复杂?日了鬼了。感觉里面一定有花指令(只是猜测)。

停下脚步继续分析,是不是这里面就没有包含真正的逻辑代码?于是做了一个小测验。
字符串比较什么的,一定会取得字符串长度,也就是C的strlen。

拿着strlen设计一个陷阱:
假设拿到strlen的返回值后,要开始循环,由此可得
var len = strlen(user_input);
// 我们将len改长一点,让程序出发BAD_ACCESS异常,读取一个不存在的地址或者不可读的地址。
for (int i=0; i<len; i++) {
    var c = user_input[i]; // 此处会崩溃
    // 比较什么的代码
}


LZ写完这个HOOK后,点击“告诉我”,结果真的在那堆代码里崩溃了,初步猜测真正的逻辑确实是在里面执行的。

现在留下一个难点:如何去掉花指令,如何得到真正的执行逻辑。
LZ通过之前的经验,推敲出一个可行方法:静态分析。

原理就是模拟程序的调用,手工解析指令,过滤掉router相关的指令,其他指令保留下来,然后将保留下来的指令组合成2进制文件,再进行分析。。

无奈LZ参赛日还要加班回去以后,比赛已经结束了。

以上。
不知道思路是否正确,这个只有试了才知道!

最后赞一下ali安全团队

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回