IDA调试android so的.init_array数组



参考: http://www.itdadao.com/articles/c15a190757p0.html


一. 为什么要调试init_array

init_array的用途

1. 一些全局变量的初始化 (我这里试过, 一些全局变量的初始化,会统一用一个init_array表项来完成初始化)

2. 通过__attribute__ ((constructor)) 声明的函数 (可以定义n个)

通过so加载流程来看,init_array是我们程序代码可以控制的最早的时机了, 其次才加载Jni_onload

所以有些样本会在init_array做一些反调试和相关环境检测的活, 所以我们需要在init_array中和对方兵戎相见


当然网上已有很多教我们如何在init_array下断的函数, 但是却都只教了方法, 没有细说原理, 最后我们可能只学会了几个快捷键, 空有招式却无内功, 知其然却不知其所以然, 下面我们就姿势和知识这2方面来进行讨论


二. 断init_array的姿势

1. 定位调试进程中linker的dlopen函数地址

把调试机器中的linker拷贝出来, 路径为/system/bin/linker, 然后开一个IDA分析

在Shift+F12在字符串窗口中查找"dlopen", 跟踪引用到一个函数, 如下图

198684-20170118165140718-786934426.png

 得到其文件偏移为0xF30


附加上调试器后, 我们得到linker加载到内存的起始地址为400BD000 

198684-20170118165141312-1686490026.png

 所以我们在代码窗口Go过去看看400BD000 + F30 = 400BDF30


发现全部是DCB形式的代码(代码没有解析出来), 这个时候我们需要对linker进行分析, 操作如下: 右键->Analyze Module

198684-20170118165141968-265313419.png


go过去我们发现和静态分析中的一样,  在函数头部下一个断点

198684-20170118165142562-941211491.png  


2. 定位到calling相关代码

同样在拷贝出来的ida搜索字符串calling

198684-20170118165143171-1971168493.png


同样定位到代码,得到文件偏移 2720

198684-20170118165143750-1415227038.png


那么我们内存中的地址就是 400BD000 + 2720 = 400BF720

同样在调试的ida中下好断点, 第2个断点就是调用.init_array数组的代码

198684-20170118165148015-838129537.png  


然后按F9,注意观察寄存器窗口, 当有显示调试的是你想要断的so的时候开始注意

当断点断在BLX R4的时候,下一步就是调用init_array数组了, 所以F7跟进去

198684-20170118165148968-718522159.png 


在直接把我们想要分析的so拖到ida分析进行验证, 代码一样, 说明我们成功的断点在了init_array数组

198684-20170118165149687-993559650.png 


二. 断init_array的知识

通过上面的操作我们学会了招式, 内功心法却不见修习, 下面我们通过Android的系统源码来一探究竟


环境介绍

源码环境: Android 6.0.1

没有下载源码的同学可以去androidxref在线看源码也很方面

http://androidxref.com/


1. 回到源头看问题

我们都知道我们要在apk中要加载一个so我们可以通过

System.loadLibrary("libname");  

System.load("lib_path");                  

这2者区别如下:

(1). System.load参数必须为库文件的绝对路径,可以是任意路径;

(2). System.loadLibrary参数为库文件名,不包含库文件的扩展名,必须是在JVM属性Java.library.path所指向的路径中,路径可以通过System.getProperty('java.library.path')


2. java层到native层的过程

我们把android_source\libcore\luni部分的源码作为单独的部分丢进Source Insight进行分析

定位到android_source\libcore\luni\src\main\java\java\lang\System.java, 搜索loadLibrary, 就可以开始分析了


java层代码主要是一些路径, 和标记值的初始化

最后比较关键的函数是JavaVMExt.LoadNativeLibrary, 该函数主要完成如下事情

1. 调用linker的dlopen完成加载

2. 调用dlsym获取目标so的JniOnload地址并调用

3. 初始化SharedLibrary对象并添加到表中, 下次加载相同的so则不在重复加载


linker之前的函数调用流程图如下:

198684-20170118165150656-1061181955.png


3. linker的dlopen简易分析

android系统通过调用linker的dlopen来完成so的转载


把aosp\bionic目录添加到source insight中进行分析

配合AndroidXref站点我们找到, dlopen定义在dlfcn.cpp中


dlopen函数定义如下, 只是简单的调用了dlopen_ext

198684-20170118165151343-950801845.png

 
跟进 dlopen_ext 函数, 该函数返回一个soinfo的结构体指针
而且这个指针最后作为函数返回值返回了
198684-20170118165151984-1247634384.png


do_dlopen简单的判断了一下参数, 然后调用find_library进行转载链接so文件

加载成功后,返回soinfo对象指针,同时调用soinfo的成员函数call_constructors来调用so中的init_array

198684-20170118165152750-1411026654.png


call_constructors先完成其他模块的加载,然后调用call_array()来调用init_array数组的调用

198684-20170118165153593-1640375546.png

 
call_array循环调用call_funtion来进行加载
198684-20170118165154546-1326201850.png
最后call_function只是简单的调用传进来的函数指针, 可以看到我们上面的下断点的字符串就来自于下面 
198684-20170118165155171-1209017084.png 

由于篇幅问题,大致介绍下linker的调用流程, 函数调用流程如下:

198684-20170118165156593-644364000.png

1. 在do_dlopen中通过find_library进行加载so

    在加载完so后通过call_constructors完成init_array的加载

2. find_library最后调用load_libray完成so的转载

3. 最后通过load_library的elf_reader.load完成so的装载


四.总结

由于android是开源的操作系统, android中的很多问题我们都可以通过分析源码来了解细节, 解决问题, 并知其所以然

同时我们还可以通过编译源码来定制我们想要的功能, 达到我们想要的目的












转载于:https://www.cnblogs.com/bingghost/p/6297325.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值