阿里2014移动安全挑战赛第二题调试笔记

0x00前言

  最近在学习安卓安全,看到52破解上面有分析2014年阿里安全挑战赛的第二个crackme的文章。勾起了我的回忆,那是我第一次参加安全比赛,在安卓安全也没有做多深入的学习。第一题比较简单,直接在logcat里面就可以看到输出的信息,只要将数字和文字的关系对应关系搞明白就可以解出来。第二题我就遇到困难了,虽然临时学会了怎么用ida调试so,但只要ida附加到进程上去,程序就退出了,屡试不爽。心里也有往反调试那边想,但是功力不足没有能把反调试干掉,最后止步于这一题。现在又看到基于这道题的调试技巧文章,所以就拿起来练练手,练习练习动态调试。

0x01静态分析

应用程序打开的主界面是

把程序拖到jeb中,可以看到主程序的代码比较简单,就是取得输入的信息,然后调用native的securityCheck方法做比较,根据返回的结果展示不同的内容。没有提供有用的信息。

解压程序,将libcrackme.so拖到ida中,找到Java_com_yaotong_crackme_MainActivity_securityCheck函数直接f5,代码没有做混淆,条理也非常清晰,对比v6和v5的值。

其中v5是用户的输入,看起来比较别扭,参考蒸米《安卓动态调试七种武器之孔雀翎 – Ida Pro》的内容:一个指针加上一个数字,比如v3+676。然后将这个地址作为一个方法指针进行方法调用,并且第一个参数就是指针自己,比如(v3+676)(v3…)。这实际上就是我们在JNI里经常用到的JNIEnv方法。因为Ida并不会自动的对这些方法进行识别,所以当我们对so文件进行调试的时候经常会见到却搞不清楚这个函数究竟在干什么,因为这个函数实在是太抽象了。解决方法非常简单,只需要对JNIEnv指针做一个类型转换即可。比如说上面提到v3指针,我们选中后按一下”y”键,然后将类型声明为”JNIEnv*”。

可以看到off_628C的内容为

看到字符串为wojiushidaan,将该字符串输入,并没有通过校验,可见程序在运行的时候对比较的字符串做了修改,这就要求我们动态去调试这个程序了。

0x02动态调试

到ida安装目录.\IDA 6.8\dbgsrv\下面将android_server拷贝到安卓设备中

adb push android_server /data/local/tmp/

修改文件的权限

adb shell chmod 755 /data/local/tmp/android_server

以9000作为调试端口启动,修改默认端口是因为有些反调试会读取/proc/net/tcp下的信息,默认端口容易躺枪

adb shell /data/local/tmp/android_server -p9000

另开一个窗口,在这个窗口里面做端口转发

adb forward tcp:900 tcp:9000

修改Debugger-Run-RemoteArmLinux/Android debugger中的配置去调试,点击ok

就可以看到所有运行的程序,基本我们启动的需要调试的应用的pid号都比较大,可以按pid排序方便找到调试的程序。

载入的过程较长,其中库的载入有弹框确认,直接略过,在modules框中找到函数,在函数头下断点

F9运行,程序直接退出,ida输出如下

几次尝试都是这个结果,所以肯定有反调试。

0x03反调试

我们用以下命令以调试模式启动一个应用

adb shell am start -D -n com.yaotong.crackme/.MainActivity

其中主activity可以在logcat里面看到,或者反编译manifest文件也可以看到

程序启动停在等待调试器的状态

修改Debugger-debugger options的配置,让程序在载入lib的时候断下来,这样我们才可以调试JNI_Onload函数,还有某些可能会把反调试放在.init_array,该函数的加载比JNI_Onload还要早。

然后再附加进程,然后f9运行程序,可以看到程序还是停在等待调试器的状态

jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700

让程序恢复运行,这时程序会在linker停下来,在JNI_Oload函数上下断点,然后f9运行程序,f8逐条运行,运行到这条函数的时候就会退出,f7跟进去调试

是一个创建线程的库函数,如果在反汇编代码中可以看到具体的地址指令是 BLX R7,R7的值就是pthread_create函数的地址,如果是做比赛可以直接nop  BLX R7,然后去试试有没有过掉反调试。

点击dword_9BC882B4函数也可以看到函数已经被定位到了pthread_create

而在静态中只有符号信息,没有做相应的映射

所以可以知道unk_9BC836A4就是该线程创建完成之后执行的函数,在该函数下断点,f9执行,断在了该函数

F5之后比原来的汇编代码还别扭

可以看到有一个明显的循环体,其中主要的函数调用是BL unk_9BC8330C,可以直接nop这个函数调用,然后去试试反调试是不是已经过掉了,不过现在不是做比赛,我想继续跟进去看看他是怎么做的。

不过里面的代码确实是难看懂,反正是调试,不如就动态跟着一步一步调一下,发现里面读取了/proc/pid/status的tracepid字段,然后做了字符串的比较,最后执行到了

可以看到R2寄存器指向了libc.so下的kill函数,这就是反调试的最后一步,当发现被调试时杀死进程,来实现自毁的目的,在流程图里面也可以看到这个一个单独的分支,之后就没有代码了。

所以我们把BGE loc_9BC59600这个函数nop掉,让这个分支的代码不执行。libcrackme.so加载的基地址为0x9BC58000,修改原文件的地址为0x9BC595D80-x9BC58000=0x15D8

然后重打包运行,ida附加上去没有退出,说明我们的反反调试成功了,在securityCheck函数上下断点,然后f8单步调试

此时R2指向了保存的密码

输入aiyou,bucuoo,答案正确

0x04其他思路

思路一:

在我们输入密码进行比较的时候,logcat有输出信息

所以我们可以使用这条打印信息来把密码打印出来

这是原来的代码布局

选择的patch方法是直接把这个log函数往下移,因为在0x12A4地址处正好有我们需要的打印的数据地址赋值给了R2寄存器,因此将代码段从0x1284到0x129C的地方都用NOP改写,在0x12AC的地方调用log函数,同时为了不影响R1的值,把0x12A0处的R1改成R3: 9BC7F1A8,同时由之前的动态调试我们也确认了此时R2指向的是密码,所以具体的patch方案如下:

  ①0x1284-0x129c:NOP (0000A0E1)

  ②0x12A8:MOV R0,#4 (0400A0E3)

  ③0x12AC:BL  __android_log_print (88FFFFEB)这里的88是由一开始在0x1284的92FFFFEB得出的,两个地址相差40个字节,ARM指令4字节对齐,即最低两位是00,所以地址右移两位,应除以4,对应就为:0x92-10=0x88。

  ④0x12A40-0x12A84:NOP (0000A0E1)

patch之后的代码布局

重新打包运行,随便输入密码,打印出来的log如下

思路二:

静态时密码的偏移量是0x4450

所以我们attach上进程后不运行,直接在libcrackme.so的基地址加上0x4450得到的地址为

0x9BC30000+0x4450=0x9BC34450

G直接跳转到那个位置,然后就看到了答案,请允许我做一个悲伤的表情,为什么当初没有想到。

0x05小结

  除了读取/proc/pid/status下面的tracepid方法外,现在反调试还采用了读取/proc/net/tcp下面的tcp信息,对常见的调试器的调试端口进行检测。还有函数的运行时间,读取进入函数和出来函数的系统时间,然后做差和预定值作比较,来判定是否处于调试状态。
  现在的反调试还是在大粒度上面做的,采用nop线程开启和nop整个函数调用都可以过掉整个反调试,是不是可以采用线程之间互相配合的方式,主线程依赖于子线程的运行,如果子线程不运行,那么主线程也退出,细化一点就是把反调试函数和正常功能函数混合到一起,比如采用生产者消费者模型来建立主从线程的关系,这样过掉反函数的难度就更大一些了。

参考:

http://www.cnblogs.com/Reyzal/p/4857948.html

http://www.52pojie.cn/thread-559205-1-1.html

http://www.wjdiankong.cn/android%E9%80%86%E5%90%91%E4%B9%8B%E6%97%85-%E5%8A%A8%E6%80%81%E6%96%B9%E5%BC%8F%E7%A0%B4%E8%A7%A3apk%E8%BF%9B%E9%98%B6%E7%AF%87ida%E8%B0%83%E8%AF%95so%E6%BA%90%E7%A0%81/

http://wooyun.jozxing.cc/static/drops/mobile-5942.html

http://drops.wooyun.org/tips/6840

时间: 12-13

阿里2014移动安全挑战赛第二题调试笔记的相关文章

2014百度之星第二题Disk Schedule

Disk Schedule Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1463    Accepted Submission(s): 189 Problem Description 有很多从磁盘读取数据的需求,包括顺序读取.随机读取.为了提高效率,需要人为安排磁盘读取.然而,在现实中,这种做法很复杂.我们考虑一个相对简单的场景.磁

阿里移动安全挑战赛第二题研究小结

1.取巧的方法 因为不太会看汇编指令,所以看了别人的解题思路后,自己想了个取巧的方法.o(╯□╰)o 之前学习的过程中学会了不少指令,例如strings,列出文件中所有字符串.突然想起来可以导出apk运行时so文件的内存(其实也是现学的- -'), 验证了一次后被比较的字符串就存放在内存中了,于是使用dd命令将内容导入SD卡: [email protected]:/ # dd if=/proc/2829/mem of=/sdcard/hah.mem bs=1 skip=1750364160 co

2014考研数学一 第二题

设函数$f(x)$具有2阶导数,$g(x)=f(0)(1-x)+f(1)$,则在$[0,1]$上 $(A)$当$f'(x)\ge 0$时,$f(x)\ge g(x);$ $(B)$当$f'(x)\ge 0$时,$f(x)\le g(x);$ $(C)$当$f''(x)\ge 0$时,$f(x)\ge g(x);$ $(D)$当$f''(x)\ge 0$时,$f(x)\le g(x).$ 解:看到比较大小,容易想到构造辅助函数:$$F(x)=f(x)-g(x)=f(x)-[f(0)(1-x)+f(

“金山杯2007逆向分析挑战赛”第一阶段第二题

注:题目来自于以下链接地址: http://www.pediy.com/kssd/ 目录:第13篇 论坛活动 \ 金山杯2007逆向分析挑战赛 \ 第一阶段 \ 第二题 \ 题目 \ [第一阶段 第二题] 题目描述: 己知是一个 PE 格式 EXE 文件,其三个(section)区块的数据文件依次如下:(详见附件)  _text,_rdata,_data 1. 将 _text, _rdata, _data合并成一个 EXE 文件,重建一个 PE 头,一些关键参数,如 EntryPoint,Imp

2014百度之星资格赛第二题

Disk Schedule Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2560    Accepted Submission(s): 366 Problem Description 有很多从磁盘读取数据的需求,包括顺序读取.随机读取.为了提高效率,需要人为安排磁盘读取.然而,在现实中,这种做法很复杂.我们考虑一个相对简单的场景.

MSC阿里比赛第二题详解

Ali第二题相关思路 图/文 听鬼哥说故事 晚上8点等了会儿,大家都登录不上,于是带着群里的小伙伴愉快的去打游戏了,早上醒来发现可以正常登录了,就开始分析吧. 0x1:开始查看 安装到手机上测试,知道是通过输入一个密码去解开的,所以开始分析apk. 拿到第二题的apk,反编译查看主类: 详细查看文档吧,比较详细的: 链接:http://pan.baidu.com/s/1bnpkWzl 密码:roii

阿里在线笔试算法工程师附加题

前几天参加了阿里的在线笔试,报的职位是算法工程师,笔试感觉难度适中,选择题包含数据结构.离散数学.小的智力问题还有一些读程序选结果的题目.其中数据结构和排列组合最多.当时比较慌乱,没做记录.只记下了三个附加题. 第一题很简单.要求实现一个方法,在两个排好序(升序)的整型数组中找到中位数.传入4个参数,分别是两个数组和他们的大小.这个题目既然简单就要写的高效一些.我用的归并排序的思想,将两个数组合并,在合并的过程中找到中位数.并对奇偶分情况讨论,注意偶数情况下有可能出现小数.代码如下: doubl

2016/1/12 第一题 输出 i 出现次数 第二题 用for循环和if条件句去除字符串中空格 第三题不用endwith 实现尾端字符查询

1 import java.util.Scanner; 2 3 4 public class Number { 5 6 private static Object i; 7 8 /* 9 *第一题 mingrikejijavabu中字符“i” 出现了几次,并将结果输出*/ 10 public static void main(String[] args) { 11 12 String r ="imingrikejijavabi"; 13 14 15 //第一种 截取 16 int a=

2014 Hangjs 见闻流水账第二天

前言 第一天传送门: 2014 Hangjs 见闻流水账第一天 写作风格跟第一天还是一样的. Slide 每个slide我都会根据自己的理解重新命名一次,用于表达自己的第一看法,主观意见,不喜可吐槽,但是不要喷,就算要喷请轻碰... angular 大法好 今天第一场slide是由Sofish带来的关于如何优化你的Angular Web App. 作为一名angular用户,这次slide分享的切换路由状态的监听事件,是个不错的收获,之前的loading状态都是到处定义开始-结束和标识,或者自定