首页
社区
课程
招聘
[原创]Unidbg-Linker部分源码分析(下)
发表于: 2021-10-29 18:06 28682

[原创]Unidbg-Linker部分源码分析(下)

2021-10-29 18:06
28682

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

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

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

先来分析libraryFile.resolveLibrary方法

再看下面的查找方法

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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;
    }
}
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;
    }
}
// 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);
}
// 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);
}
public LibraryFile resolveLibrary(Emulator<?> emulator, String soName) {
    // 直接找当前so路径下的对应名字的文件
    File file = new File(elfFile.getParentFile(), soName);
    // 如果没有,就返回null
    return file.canRead() ? new ElfLibraryFile(file, is64Bit) : null;
}
public LibraryFile resolveLibrary(Emulator<?> emulator, String soName) {
    // 直接找当前so路径下的对应名字的文件
    File file = new File(elfFile.getParentFile(), soName);
    // 如果没有,就返回null
    return file.canRead() ? new ElfLibraryFile(file, is64Bit) : null;
}
if (libraryResolver != null && neededLibraryFile == null) {
    neededLibraryFile = libraryResolver.resolveLibrary(emulator, neededLibrary);
}
if (libraryResolver != null && neededLibraryFile == null) {
    neededLibraryFile = libraryResolver.resolveLibrary(emulator, neededLibrary);
}
memory.setLibraryResolver(new AndroidResolver(23));
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;
}
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;
}
 
new JniGraphics(emulator, vm).register(memory);
new JniGraphics(emulator, vm).register(memory);
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);
}
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");
    }

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 5
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//