zoukankan      html  css  js  c++  java
  • Android动态调试so库JNI_Onload函数-----基于IDA实现

    之前看过吾爱破解论坛一个关于Android'逆向动态调试的经验总结帖,那个帖子写的很好,对Android的脱壳和破解很有帮助,之前我们老师在上课的时候也讲过集中调试的方法,但是现在不太实用。对吾爱破解论坛的该贴,我也是看了很多遍,自己也查了不少资料,但是自己动手的时候总觉比较繁琐,并且很多细节的地方没有注意到,按照那个帖子尝试了几遍但是却出现了错误(后面会提到),今天周末重新拾起来试了试,终于把遇到的问题给解决了,顺便做个记录以免忘记了,其中的一些细节我也不是太明白,忘知道的人给指出。


    第一步、给meke.exe工具添加系统环境变量


    对Android的动态调试,要在cmd控制台手动输入命令,比较繁琐,下面我就偷懒用个简单一点的方法。为了能偷懒,我们需要将AndroidNDK提供的make.exe工具位于安装目录C:AndroidDevlopmentandroid-ndk-r9d-windows-x86_64android-ndk-r9dprebuiltwindows-x86_64in下(具体的路径由自己安装目录决定),在进行Android动态调试之前,需要将该路径添加到系统或临时的PATH环境变量中,具体的操作如下:




    将该路径添加到系统环境PATH变量中:




    第二步、准备android_server文件和编写Android动态调试需要的mk文件


    android_server文件是由IDA 6.6提供,具体的文件路径是在IDA的安装路径的IDA 6.6dbgsrv目录下,这里直接拷贝过来使用(只有IDA6.1以上版本才支持Android的动态调试)。具体的mk文件我已经写好了,可以直接拷贝代码保存为.mk后缀文件使用,至于makefile文件的编写我也不知道。


    cmd控制台1使用的listen.mk文件的编写:


    #说明(控制台1)
    #使用Android-NDK提供的make.exe程序,需将该程序的路径 xxandroid-ndk-r9d-windows-x86_64android-ndk-r9dprebuiltwindows-x86_64in添加到环境变量
    #记得要先开启Android模拟器或者将开发的手机连接到电脑
    #这里android_server的版本是IDA 6.6的
    #使用命令(控制台1) make -f listen.mk
    
    
    #文件名称
    MODALE_NAME=crackme.apk
    	
    #安装程序到手机
    listen:
    	adb push $(MODALE_NAME) /data/local/tmp
    	adb shell chmod 755 /data/local/tmp/$(MODALE_NAME)
    
    	adb push android_server /data/local/tmp
    	adb shell chmod 755 /data/local/tmp/android_server
     
    #调试模式启动程序
    #此时,手机界面会出现Waiting For Debugger页面
    #格式 adb shell am start -D -n 包名/.类名
    	adb shell am start -D -n com.yaotong.crackme/.MainActivity
    	
    #端口转发
    	adb forward tcp:23946 tcp:23946
    	
    #启动android_server
    #adb shell su
    	adb shell /data/local/tmp/android_server


    cmd控制台2使用的conn.mk文件的编写


    #接下来 IDA附加,设置调试的选项(控制台2)
    #静态找到目标函数对应所在模块的偏移地址,Ctrl+S找到对应模块的基地址,两个地址相加得到最终地址
    #G跳转至地址,然后下断,F9运行
    #其中port=8700是从ddms中看到的
    #IDA中,F9运行程序,此时是runing状态
    conn:
    	jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700


    简要的说一下.mk文件中的命令的作用:


    #使用命令(控制台1) make -f listen.mk
    //注释 make -f listen.mk 说明是listen.mk文件在控制台使用的命令格式


    #文件名称
    MODALE_NAME=crackme.apk  


    //crackme.apk是我们要动态调试的目标apk应用程序
    adb push $(MODALE_NAME) /data/local/tmp


    //使用adb程序将要调试的目标apk应用程序拷贝Android系统的文件/data/local/tmp目录下
    adb shell chmod 755 /data/local/tmp/$(MODALE_NAME)


    //adb shell命令的意思是进入到Android系统中
    //上面这条命令的作用是修改/data/local/tmp目录下的目标apk应用程序的文件权限为755


    adb push android_server /data/local/tmp
    //使用adb程序将android_server程序文件拷贝Android系统的文件/data/local/tmp目录下


    adb shell chmod 755 /data/local/tmp/android_server
    //修改Android系统目录/data/local/tmp下的android_server程序文件的权限为755


    #格式 adb shell am start -D -n 包名/.类名 或者 adb shell am start -D -n 包名/包名.类名
    adb shell am start -D -n com.yaotong.crackme/.MainActivity


    //使用adb shell am start -D -n 包名/.类名 命令以-D调试模式启动apk应用程序,apk应用程序调试模式启动以后,会停止在Waiting For Debugger界面上。am start命令的具体的使用参考网址:http://developer.android.com/tools/help/adb.html#IntentSpec


    adb forward tcp:23946 tcp:23946
    //adb端口转发


    adb shell /data/local/tmp/android_server
    //启动拷贝到Android系统中android_server程序,等待IDA6.6程序的连接


    #使用命令(控制台2) make -f conn.mk
    //注释 make -f conn.mk 说明是conn.mk文件在控制台使用的命令格式


    jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700
    //jdb调试器的使用,具体命令行含义不知道,貌似Linux下该命令还不是这样的




    OK,需要的文件已经准备齐全了,Android逆向动态调试的脚步又就进了一步。


    第三步、打开Eclipse应用程序和为要调试的目标apk应用程序添加android:exported="true"选项,生成符合调试的apk程序


    在Android逆向动态调试的时候,必须要打开Eclipse程序,并且还要运行Android的模拟器DDMS以方便使用adb程序和jdb程序,有钱的土豪可以使用真机调试。




    在进行Android动态调试的动手之前,我们还需要做一件事情,为要调试的目标apk程序android:exported="true"以及修改listen.mk文件。前面看别人的写的帖子和博客也可能是人品问题!在Android动态调试的时候am start -D命令不能以调试模式启动要调试的目标apk应用程序,也可能是人品爆发,今天百度查资料解决了,解决的方案就是为启动Activity添加android:exported="true"选项,加了这个选项之后执行am start -D命令,被调试的目标Apk程序能够出现Waiting For Debugger界面并停在那儿。那么现在的问题就是如何添加android:exported="true"选项,很简单。我们使用Android逆向工程利器AndroidKiller工具,这个工具的使用很久简单。我这里要动态调试的目标程序为crackme.apk应用程序,接下来打开AndroidKiller程序,直接拖拽crackeme.apk程序到AndroidKiller程序的界面内进行crackeme.apk应用程序的逆向反汇编,在解压的工程选项中找到crackme.apk的配置文件AndroidManifest.xml打开,如下图依次找到crackme.apk文件的包名、主活动的Activity以及正确的在活动中添加android:exported="true"选项:




    在对要动态调试的目标apk程序crackeme.apk文件进行解包逆向修改添加android:exported="true"选项以后,千万不能忘记对修改后的目标程序的工程进行再次打包、签名处理生成新的crakeme.apk程序以备后面调试的时候使用,新生成的crakeme.apk程序在Android Killer的安装目录的C:AndroidDevlopmentAndroidKillerprojectscrackmeBin路径下(具体的路径由Android Killer的安装目录决定)。现在轻松多了,要动态调试的目标应用程序crackme.apk文件有了。


    对了,对应着要调试的目标apk应用程序的名称以及它的主启动Activity相应的修改 listen.mk文件中的变量 MODALE_NAME=crackme.apk以及 命令adb shell am start -D -ncom.yaotong.crackme/.MainActivity


    第四步、开启一个cmd控制台以调试模式启动目标apk程序


    将第三步中修改后重新打包生成的C:AndroidDevlopmentAndroidKillerprojectscrackmeBin路径下的crackme.apk文件以及android_serverlisten.mk文件conn.mk文件拷贝到同一目录下,然后开启一个cmd控制台cd命令进入到该目录,执行命令 make -f listen.mk 如下图:






    其实在这里的时候要注意一下,如果在第三步中没有为要调试的目标apk应用程序添加android:exported="true" 选项,在执行adb shell am start -D -n com.yaotong.crackme/.MainActivity命令的时候会出现如下的错误,但是如果为要调试的目标apk应用程序添加android:exported="true" 选项以后,执行该命令不会出现下面的错误提示,具体的原因我也不知道,之前在尝试Android动态调试的时候也是卡在了这里,可能是Android'应用程序的权限问题吧,在注册Android内容提供者的时候也需要导出,可能道理一样,也可以参考一下这篇文章http://chenxuebinbj.blog.163.com/blog/static/42869151201302235215832/。对于此种错误的情况,我在看雪论坛上也遇到过了。


    :我真的很无语,当我写完这篇笔记想将该错误重现的时候,无语的事情出现了,我没有android:exported="true" 选项再次执行adb shell am start -D -n com.yaotong.crackme/.MainActivity命令的时候,竟然没有报找不到com.yaotong.crackme/.MainActivity类的错误。我还连续试了几次,之前遇到的这个am start -D命令的错误提示都没有出现,真无语。更正一下,看来添加android:exported="true" 选项不是必须的。


    此时Android'模拟中被动态调试的目标crackme.apk程序的运行状态如下,停止在了Waiting For Debugger界面:




    启动一个IDA主程序,点击菜单 Debugger->Attach->Remote ArmLinux/Android debugger,打开调试程序对话框,在hostname一栏输入localhost,点击ok,然后在IDA弹出的窗口中,选择自己要附加的进程com.yaotong.crackme后点击OK即可,如下图:








    此时,要动态调试的目标apk应用程序的进程被IDA6.6程序附加成功以后的状态如下图:




    下面我们为目标apk应用程序的动态调试进行IDA6.6的一些调试设置,点击该IDA菜单项Debugger->Debugger Opitions 在弹出的Debugger setup窗口的Events中选择 suspend on thread start/exit 以及 suspend on library load/unload,再点击OK退出。通过此操作可以设置程序在创建新线程和加载so时自动中断。具体操作见下图:






    OK,IDA6.6的设置完成,然后按IDA快捷键F9。注意此时,cmd控制台不要关闭保持状态,IDA6.6程序不要关闭保持状态,Eclipse程序和Android模拟器也不能关闭继续保持状态,在后面的动态调试中还会用到。


    第五步、再开启一个cmd控制台,在目标apk应用程序的so库的JNI_OnLoad函数处下断点


    根据第四步中com.yaotong.crackme进程调试端口8612/8700修改conn.mk文件中的jdb命令为jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700,如下图:




    再开启一个cmd控制台,cd命令进入到conn.mk所在的目录,执行make -f conn.mk 命令,如下图:




    连接成功后,在第三步的IDA6.6程序中按F9后Android模拟上的“waiting for debugger"提示会自动消失,这个时候应该已经断在新线程,或者加载so处,具体的状态如下图。不过有一点不太明白,在我的调试运行过程中IDA6.6出现了如下的提示,不知道是否出错,请指导的大神帮忙指点一下。




    IDA6.6下方的提示如下图,对于上图中的这个对话框我没有理会,直接一路确定迷迷糊糊的这个Add map对话框就被我给无视了。




    现在就可以在IDA6.6中按下快捷键CTRL+S来查看要调试的so是否已经加载了,如果没有就F9直到加载了为止;如果已经有了,就记下该so的start内存基地址,然后直接用压缩软件解压crackme.apk文件,得到crackme.apk文件要加载的so库,并且再另开一个IDA6.6静态分析该.so库,找到JNI_Onload函数的内存相对虚拟地址(RVA)JNI_Onload_Offset,那么该JNI_OnLoad函数在内存中的真实地址为so.start+ JNI_OnLoad_Offset(so库的内存加载基址+JNI_Onload函数的内存相对虚拟地址)。






    注意:在快捷键CTRL+S跳出的窗口中有几个个同名的so库,我们应当选择class类型为CODE即代码段的内存加载基址so.Start也就是权限为RX的这个,RX一般是代码段,RW一般是数据段。这里的so.Start=AA238000.



    通过另开的IDA6.6静态分析目标apk应用程序的so库文件得到libcrackme.so文件中JNI_Onload函数的内存相对虚拟地址为00001B9C即JNI_OnLoad_Offset=00001B9C,因此JNI_Onload函数在内存中真实地址为so.start+ JNI_OnLoad_Offset=AA238000+00001B9C=AA239B9C.得到真实地址后,在附加目标进程的IDA6.6中按下快捷键G跳转到地址AA239B9C,然后按下快捷键F2就完成在JNI_OnLoad函数入口处下断点了。



    OK,在JNI_Onload函数处下断点成功,下面就可以进行SO文件的动态调试F7、F8单步或者直接F9运行JNI_Onload函数断点处。


    在apk应用程序的JNI_Onload函数进行下断点,对于从arm汇编的角度来产看apk程序的本地方法做了什么操作还是很有效果的,但是这种方法也不是经常有效的。在Android的JNI编程也可以不使用JNI_Onload函数进行Android的JNI编程。



    Mark一下

    有时间关注一下这位作者的使用IDA调试Android程序的思路:http://bbs.csdn.net/topics/390338991



    参考网址

    http://www.cnblogs.com/wanyuanchun/p/3760825.html?utm_source=tuicool

    http://www.52pojie.cn/forum.php?mod=viewthread&tid=293648


    2015/4/12 2:08

  • 相关阅读:
    选择器的区别
    固定DIV样式
    图片自适应不变形
    CSS实现图片在div a标签中水平垂直居中
    2017年校招全国统一模拟笔试 页码统计
    LeetCode 23. Merge k Sorted Lists
    LeetCode 15. 3Sum
    LeetCode 12. Integer to Roman
    LeetCode 11. Container With Most Water
    LeetCode 8. String to Integer (atoi)
  • 原文地址:https://www.cnblogs.com/csnd/p/11800735.html
Copyright © 2011-2022 走看看