-
-
[分享]iOS 第三题解题思路 - 未尝试版
-
发表于: 2015-10-19 11:26 2610
-
LZ就自己的分析思路做出一些总结,望点评。
准备工作如第一题,找到弹出alertview的地方。
也就是
然后往下看,有调用
ali_judge作为判断用户输入的入口,返回值是TRUE和FALSE,分别是0、1。
如果匹配成功返回TRUE,失败返回FALSE。
进ali_judge看看,习惯性F5,显示空白
好吧,直接看汇编
从结构来看比较正常,但实际上,很不正常
这句覆盖了原有的LR值,致使
直接跳转到了被手动修改的LR值处,也就是:
这个地方,BLX调用的func暂且命名为ali_router(why?因为它会被调用N次,然后他又去调用其他FUNC)
现场被保留,带着r0开始了漫长的计算之旅。
进入ali_route后
可以确定这是手工生成的代码(naked)。
其目的是根据r0的offset在跳转表(暂且称之为ali_route_table)中重新寻址,打乱执行顺序(高,实在是高)。
接下来将程序跑起来,运行一下,在__text:00013EE4处下个断点查看栈顶的值。看他跳转到哪里。
这里截取了两次断点,分别是0x21437和0x214af,其一是入口。
进入IDA查看。
直接就尿了,阿里果然牛逼,居然吧代码做成了一堆data。
IDA目前是不能直接反汇编的,估计有什么设置或者通过修改mach-o文件可以让它反汇编,但是不知道怎么直接搞,也懒得研究了。
既然IDA不能反汇编,就用GDB先将就看看。
前两句还算正常,push {r4-r7, lr},add r7, sp, #12保存现场,分配Local变量内存。到后面就开始抽风了,哈哈~~
大量的类似这种
其实我们之前分析过,这个0x1dcf4就是ali_router。此处的push {r0, r1, lr}由ali_router最后一行进行pop。
但这中间也不乏参杂了一些其他指令,如
LZ暂且认为他们是真正参与计算的指令。
通过以上的分析,得出一个结论:
实际上真正的计算func,被分成了N个block,然后打乱顺寻,由ali_router重新排序调用,排序规则由ali_route_table决定。最终得到完全执行。
现在就找方法去验证它(听起来好像离结果很近了呢!激动ing)。
方法1:
通过对ali_route的hook,得到每一次调用返回的PC值,
PC值打印顺序,就是正确的调用顺序。实际上。。。
LZ之后做了一个HOOK,记录了PC的值,存到一个文件里,文件超过几百兆(中途觉得不对已经强制停止),估计有几十、几百万行,N多次调用,简直无法直视。
试想,谁会把一个字符串比较做的如此复杂?日了鬼了。感觉里面一定有花指令(只是猜测)。
停下脚步继续分析,是不是这里面就没有包含真正的逻辑代码?于是做了一个小测验。
字符串比较什么的,一定会取得字符串长度,也就是C的strlen。
拿着strlen设计一个陷阱:
假设拿到strlen的返回值后,要开始循环,由此可得
LZ写完这个HOOK后,点击“告诉我”,结果真的在那堆代码里崩溃了,初步猜测真正的逻辑确实是在里面执行的。
现在留下一个难点:如何去掉花指令,如何得到真正的执行逻辑。
LZ通过之前的经验,推敲出一个可行方法:静态分析。
原理就是模拟程序的调用,手工解析指令,过滤掉router相关的指令,其他指令保留下来,然后将保留下来的指令组合成2进制文件,再进行分析。。
无奈LZ参赛日还要加班回去以后,比赛已经结束了。
以上。
不知道思路是否正确,这个只有试了才知道!
最后赞一下ali安全团队
准备工作如第一题,找到弹出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安全团队
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
他的文章
- [原创]开源Console版iOS APP内存修改器 26982
- [分享]iOS 第三题解题思路 - 未尝试版 2611
- [分享]iOS 第二题解题日志 3147
- [分享]iOS 第一题解题日志 2850
- [原创]DOTA传奇中Lua防护部分破解 17605
看原图
赞赏
雪币:
留言: