Unidbg-Linker部分源码分析(下)

  • Unidbg-Linker部分源码分析(下)

    • 概要
    • align
    • resolveLibrary
    • VirtualModule
    • initFunctions call
    • 总结

概要

上篇我们分析了AndroidElfLoader类中的loadInternal方法,很长,很大,忍一忍才能读完。那么我们下篇就来分析下Unidbg中Linker部分的其他细枝末节

align

上文中有很多地方调用了ARM.align方法进行页对齐操作,我们看一下这个方法的实现

public static Alignment align(long addr, long size, long alignment) {// addr: 起始地址// size: 大小// alignment: 页面大小long mask = -alignment;// 计算右边界long right = addr + size;right = (right + alignment - 1) & mask;// addr就是左边界addr &= mask;size = right - addr;// 这行感觉没有必要,因为左右边界对齐后,差也是对齐的,就没必要再进行对齐了size = (size + alignment - 1) & mask;// 封装对象return new Alignment(addr, size);
}public class Alignment {public final long address;public final long size;public Alignment(long address, long size) {this.address = address;this.size = size;}
}

resolveLibrary

Unidbg是如何加载依赖库的?我们来看下面一段代码,是我们上篇文章分析中的依赖库加载部分

// modules字段保存了所有已经加载过的库,这里就是在寻找是否该So已经被加载过
LinuxModule loaded = modules.get(neededLibrary);
if (loaded != null) {// 如果加载过了,添加引用计数、放到neededLibraries变量loaded.addReferenceCount();neededLibraries.put(FilenameUtils.getBaseName(loaded.name), loaded);continue;
}
// 如果依赖还没有被加载过,就开始寻找这个依赖文件在哪,先在当前So的路径下找
LibraryFile neededLibraryFile = libraryFile.resolveLibrary(emulator, neededLibrary);// 如果当前路径下没有找到,就去找library解析器去找
if (libraryResolver != null && neededLibraryFile == null) {neededLibraryFile = libraryResolver.resolveLibrary(emulator, neededLibrary);
}

先来分析libraryFile.resolveLibrary方法

public LibraryFile resolveLibrary(Emulator<?> emulator, String soName) {// 直接找当前so路径下的对应名字的文件File file = new File(elfFile.getParentFile(), soName);// 如果没有,就返回nullreturn file.canRead() ? new ElfLibraryFile(file, is64Bit) : null;
}

再看下面的查找方法

if (libraryResolver != null && neededLibraryFile == null) {neededLibraryFile = libraryResolver.resolveLibrary(emulator, neededLibrary);
}

这句代码使用libraryResolver来解析一个So文件,这个libraryResolver就是我们用

memory.setLibraryResolver(new AndroidResolver(23));

创建的,那它是怎么解析的呢?继续看

public LibraryFile resolveLibrary(Emulator<?> emulator, String libraryName) {if (needed == null) {return null;}if (!needed.isEmpty() && !needed.contains(libraryName)) {return null;}// 调用下面的重载return resolveLibrary(emulator, libraryName, sdk);
}static LibraryFile resolveLibrary(Emulator<?> emulator, String libraryName, int sdk) {final String lib = emulator.is32Bit() ? "lib" : "lib64";// 很明显吧,找下面路径下的库,我们去找个路径下看看,其实就是一些系统库String name = "/android/sdk" + sdk + "/" + lib + "/" + libraryName.replace('+', 'p');URL url = AndroidResolver.class.getResource(name);if (url != null) {return new URLibraryFile(url, libraryName, sdk, emulator.is64Bit());}return null;
}

VirtualModule

虚拟模块,它的作用是当目标So依赖一个So的时候,而这个So作用不大,甚至基本用不到的时候,就会使用VirtualModule来注册一个虚拟模块,防止So依赖报错

Unidbg提供了两个虚拟模块,也可以自己实现VirtualModule接口

  • libandroid.so
  • libjnigraphics.so
    已经做了一些简单的处理

假如我们要分析的目标So中使用到了libjnigraphics.so这个So,而且它并不影响我们的分析,甚至没用到,一般也没用到,那我们可以这么做

new JniGraphics(emulator, vm).register(memory);

那在目标So加载的时候就不会报错了,我们看下Unidbg是如何处理的

 public Module register(Memory memory) {if (name == null || name.trim().length() < 1) {throw new IllegalArgumentException("name is empty");}if (symbols.isEmpty()) {throw new IllegalArgumentException("symbols is empty");}if (log.isDebugEnabled()) {log.debug(String.format("Register virtual module[%s]: (%s)", name, symbols));}return memory.loadVirtualModule(name, symbols);
}

上面调用了memory.loadVirtualModule方法,最终又回到了AndroidElfLoad的loadVirtualModule方法

public Module loadVirtualModule(String name, Map<String, UnidbgPointer> symbols) {LinuxModule module = LinuxModule.createVirtualModule(name, symbols, emulator);modules.put(name, module);if (maxSoName == null || name.length() > maxSoName.length()) {maxSoName = name;}return module;
}

处理方式简单粗暴,创建一个LinuxModule,直接加载到我们的modules里面,这个modules保存了我们已经加载过的So,所以在目标So加载的时候,就能够找到这个虚拟模块,从而不会报错

initFunctions call

大概补充上篇要讲的加载部分就这么多,下面我们来分析剩余的流程,继续看我们上节课的一个方法

protected final LinuxModule loadInternal(LibraryFile libraryFile, boolean forceCallInit) {// File对象被封装为了LibraryFile对象try {// 接着调用了loadInternal(重载)方法,继续加载流程LinuxModule module = loadInternal(libraryFile);// 处理符号(关于重定位)resolveSymbols(!forceCallInit);// callInitFunction默认trueif (callInitFunction || forceCallInit) {// 调用初始化函数for (LinuxModule m : modules.values().toArray(new LinuxModule[0])) {boolean forceCall = (forceCallInit && m == module) || m.isForceCallInit();if (callInitFunction) {m.callInitFunction(emulator, forceCall);} else if (forceCall) {m.callInitFunction(emulator, true);}m.initFunctionList.clear();}}// 添加引用计数module.addReferenceCount();return module;} catch (IOException e) {throw new IllegalStateException(e);}
}

这整个方法我们就只分析了一句,就分析了整个上篇的内容,接下来我们继续向下分析

resolveSymbols(!forceCallInit);

这个方法我们就不展开讲了,它做的就还是进行了一次对所有未重定位的地方进行重定位最后的确定操作

还记得在加载So的过程中,init函数只是添加到了一个列表里面,保存到了LinuxModule对象中,还未进行执行呢对吧,那接下来看下面部分代码

// 先做了一下判断callInitFunction(默认) || forceCallInit(这个参数是我们传进来的)
if (callInitFunction || forceCallInit) {// 对所有模块进行遍历for (LinuxModule m : modules.values().toArray(new LinuxModule[0])) {// 两种为真的情况// 1. 模块是我们自己加载的模块 且 设置 forceCallInit参数为true// 2. 模块本身有一个forceCallInit参数,默认为trueboolean forceCall = (forceCallInit && m == module) || m.isForceCallInit();// 调用初始化函数if (callInitFunction) {m.callInitFunction(emulator, forceCall);} else if (forceCall) {m.callInitFunction(emulator, true);}// 移除该模块下的所有初始化函数m.initFunctionList.clear();}
}

所以分析了上面,我们知道了forceCallInit这个参数想仅用初始化并没有什么用对吧,因为callInitFunction默认为true,要想生效需调用

memory.disableCallInitFunction();

来禁用默认初始化,继续分析callInitFunction方法

void callInitFunction(Emulator<?> emulator, boolean mustCallInit) throws IOException {// 如果非必需初始化So,且存在未处理的符号,就算了,不初始化了if (!mustCallInit && !unresolvedSymbol.isEmpty()) {for (ModuleSymbol moduleSymbol : unresolvedSymbol) {log.info("[" + name + "]" + moduleSymbol.getSymbol().getName() + " symbol is missing before init relocationAddr=" + moduleSymbol.getRelocationAddr());}return;}// 否则,就要挨着执行初始化函数了,这个initFunctionList就是我们上篇分析过的初始化函数列表while (!initFunctionList.isEmpty()) {InitFunction initFunction = initFunctionList.remove(0);initFunction.call(emulator);}
}

总结

下篇就分析到这里吧,Unidbg中关于加载模块的部分我们就已经分析完了,其实在我们学习过Android源码中的Linker部分之后,再来分析Unidbg中的’Linker’就比较简单了。相信你读懂了这两篇文章,就能驾驭Unidbg在加载So中出现的各种问题了。老规矩V:roysue

Unidbg-Linker部分源码分析(下)相关推荐

  1. SpringCloudAlibaba注册中心与配置中心之利器Nacos实战与源码分析(下)

    源码资料 文档资料 <<Nacos架构与原理>>书籍于2021.12.21发布,并在Nacos官方网站非常Nice的提供其电子书的下载.我们学习Nacos源码更多是要吸取其优秀 ...

  2. 知识小罐头07(tomcat8请求源码分析 下)

    感觉最近想偷懒了,哎,强迫自己也要写点东西,偷懒可是会上瘾的,嘿嘿!一有写博客的想法要赶紧行动起来,养成良好的习惯. ok,继续上一篇所说的一些东西,上一篇说到Connector包装了那两个对象,最后 ...

  3. Linux驱动修炼之道-SPI驱动框架源码分析(上)

    Linux驱动修炼之道-SPI驱动框架源码分析(上)   SPI协议是一种同步的串行数据连接标准,由摩托罗拉公司命名,可工作于全双工模式.相关通讯设备可工作于m/s模式.主设备发起数据帧,允许多个从设 ...

  4. Spring AOP源码分析(七)ProxyFactoryBean介绍

    2019独角兽企业重金招聘Python工程师标准>>> 这篇文章里面就要说说Spring自己的AOP,搞清楚哪种方式是Spring自己实现的AOP,哪种方式是Spring引入aspe ...

  5. SPI驱动框架源码分析

     SPI驱动框架源码分析 2013-04-12 16:13:08 分类: LINUX SPI驱动框架源码分析 SPI协议是一种同步的串行数据连接标准,由摩托罗拉公司命名,可工作于全双工模式.相关通讯设 ...

  6. SRS源码分析-rtmp转rtc流程

    前言 SRS4.0支持将RTMP流转换成RTC流,本文将结合源码分析下这个过程. 配置 首先,需要在SRS4.0的启动配置文件里面开启RTC Server和RTC 能力,可以参考官方提供的配置文件./ ...

  7. GreenDAO基本使用及源码分析

    GreenDAO基本使用及源码分析 GreenDAO介绍 GreenDAO基本使用 添加依赖 创建存储对象实体类 GreenDAO初始化 GreenDAO实现数据库增删改查 增 删 改 查 Green ...

  8. celery源码分析-worker初始化分析(下)

    celery源码分析 本文环境python3.5.2,celery4.0.2,django1.10.x系列 celery的worker启动 在上文中分析到了Hub类的初始化,接下来继续分析Pool类的 ...

  9. Linux下USB suspend/resume源码分析【转】

    转自:http://blog.csdn.net/aaronychen/article/details/3928479 Linux下USB suspend/resume源码分析 Author:aaron ...

最新文章

  1. 【转载】闲话操作系统(二)
  2. 《好未来编程题》求和
  3. trackingmore快递查询平台_国际快递物流信息追踪查询
  4. js 浅拷贝直接赋值_JS中的赋值、浅拷贝与深拷贝
  5. securecrt调试c语言程序,SecureCRT脚本编写常用函数之WaitForString
  6. 控制图的绘制步骤_实战!脚手架排布图绘制步骤和技巧讲解!图文展示
  7. [Bzoj2049][Sdoi2008]Cave 洞穴勘测
  8. 项目进度计划检查方法与项目进度管理相关模版表单(干货+资料)
  9. c语言页面置换算法报告,C语言实现页面置换算法
  10. 用户名或密码错误html,用户名或密码错误【解决办法】
  11. 解决Promise.all一个被rejected,整个都被rejected的缺陷
  12. 单片机工程师如何继续提升自己?
  13. 2022-2028全球及中国光伏金属化铝膏行业研究及十四五规划分析报告
  14. 获取元素在屏幕的相对位置
  15. 信号和电源隔离的有效设计技术
  16. Rundll32.DLL 原理
  17. 13、python对数据进行随机抽样、按比例、分层抽样
  18. Unpacker ExeCryptor 2.x.x. version 1.0 RC1 [Public Build]
  19. dss造模原理肠炎模式动物造模
  20. 【Life系列】挑战成功37公里的徒步毅行!

热门文章

  1. 设计模式入门篇——EIT造型
  2. zabbix 触发器之count函数
  3. 发电机组黑烟净化器TPS
  4. css基础选择器有哪些
  5. 李宏毅机器学习-CNN
  6. jks cer 证书生成
  7. 华尔街老司机:在美火热的智能投顾,如何移植到中国?
  8. oneinstack php报错,快速环境部署–OneinStack | 文艺数学君
  9. 4. 业务数据采集平台搭建
  10. wordpress 外观-编辑-保存 “只能将修改保存到可写的文件。参见Codex文档以了解更多。”...