内容摘要
近日我在开发一个小工具的时候,用到了一个加载动态库的函数dlopen
,基于我的windows平台的开发背景,我想当然的认为其返回值是当前module
的load address
,即模块基地址。然后经验证后发现,其和windows
下的LoadLibrary
还是有本质的差别。那么,它返回的究竟是一个什么东西呢?又该如何通过这样的返回值,来顺利获取到我们需要的load address
甚至其它更重要的信息呢?带着这样的疑惑,我与几位资深经验的开发者对这个问题进行了深入的探究。本文是探究思路和研究过程的记录。在读文本文后,你应当会对此方面了解有所加深,并且上述疑惑应当能够释疑。
前知储备
本文面向有一定技术水准的读者,所以不再赘述模块
,基地址
等相关定义。但相对地,下文出现的一些术语或者俗语,在此做一个简短的说明,如果需要进一步了解,请参考附录或者自行检索相关资料。
aslr(Address space layout randomization)
,加载地址随机化,通俗来讲,就是同一个模块在每个进程每次被加载后的地址(可能)不一致。
load address
,模块加载地址,即模块基地址
,也等同于header address
。
patch
补丁,这里名次动用做打补丁
。
为什么需要load address
在通常开发情境下,一般地代码片段如下:
int main(int argc, char **argv) {
void *handle;
bool (*fuck)(girl);
char *error;
handle = dlopen ("libfuck.dylib", RTLD_LAZY);
if (!handle) {
fprintf (stderr, "%s ", dlerror());
exit(1);
}
fuck = dlsym(handle, "fuck");
if ((error = dlerror()) != NULL) {
fprintf (stderr, "%s ", error);
exit(1);
}
girl* xx= make_girl_byname("xx");
fuck(girl);
dlclose(handle);
return 0;
}
但特殊情况下,我们无法通过dlsym
来完成符号的寻找。如该模块根本没有导出符号,或者我们想要显式的调用某个未导出符号的函数,或者我们需要patch
掉header
中的某个字段等情形时,我们就需要知道该模块的load address
。
如何获取load address
由于aslr
的原因,现行通用的解决方案是通过遍历模块来获取。典型的代码片段如下:
int32_t nModNums= _dyld_image_count();
const char *pszModName = NULL;
intptr_t pModSlide = 0;
const struct mach_header* pModHeader = NULL;
for (uint32_t i = 0; i < nModNums; i++)
{
pszModName = _dyld_get_image_name(i);
if(!strcmp(pszModName,"/path/to/xx.dylib")){
pModSlide = _dyld_get_image_vmaddr_slide(i);
pModHeader = _dyld_get_image_header(i);
NSLog(@"[FLWB]:vm_slide:%lx,load addr:%p",pModSlide,pModHeader);
}
}
这样,我们就通过对比名字的方式,来获取到了header address
。
然而我们思考一下,这样做真的没有问题吗???
我认为,起码有两三个问题。
- 遍历导致的性能损失。
- 此处至少需要调用三个api来完成操作,而此三个api有较明显的特征,非常容易被hook针对。
- 也是最重要的,此处使用的几个api是线程不安全的。这里是一般情况下我们不会注意到的,实际上,这个是一个常见的安全漏洞。而文档上也对此有说明
/*
* The following functions allow you to iterate through all loaded images.
* This is not a thread safe operation. Another thread can add or remove
* an image during the iteration.
*
* Many uses of these routines can be replace by a call to dladdr() which
* will return the mach_header and name of an image, given an address in
* the image. dladdr() is thread safe.
*/
extern uint32_t _dyld_image_count(void) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0);
extern const struct mach_header* _dyld_get_image_header(uint32_t image_index) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0);
extern intptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0);
extern const char* _dyld_get_image_name(uint32_t image_index) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0);
大意是要警惕在遍历时其它进程的添加或者修改操作。那么。显然地,假使我们在遍历的过程中,其它线程删除了一个模块,那么将导致我们的索引移位,得到一个完全不匹配的结果,同时也有可能造成内存访问错误。
那么,要想同时解决以上三个问题,(我认为)最优秀的解决应当是通过dlopen
返回的结果来获取。从推理上来讲,dlsym
既然能通过这个参数来获取到符号信息,而符号表是需要通过header
来计算的。换言之,通过这个dlopen
的返回值一定可以获取到header
。搜索了一番,网上对此没有任何资料。
但理论上行得通的事情,实际上应该是可以的。于是我开始了探寻。
探寻过程
有了上文的资料,显然,dlopen
的返回值必定是一个结构
。我们由此来做发散思维。与此同时,我与几位大佬交流后,他们也都给出了非常重要的建议。
头文件及注释
这个是一个最简单的方案,我们去看一下dlopen
的头文件声明,其位于/usr/include/dlfcn.h
extern int dlclose(void * __handle);
extern char * dlerror(void);
extern void * dlopen(const char * __path, int __mode);
extern void * dlsym(void * __handle, const char * __symbol);
然而这里面定义它为void* 类型,且变量名字为handle
。
我们幻想中此处应为一个结构体指针的美梦破碎了。
借用linux头文件
此方案由咸🐟
提出。
On Linux, dlopen doesn't return the address where the ELF binary was loaded. It returns struct link_map instead, which has .l_addr member. So you'll want something like:
struct link_map *lm = (struct link_map*) dlopen(0, RTLD_NOW);
printf("%p\n", lm->l_addr);
However, despite what comment in /usr/include/link.h says, .l_addr is actually not a load address either. Instead, it's the difference between where ELF image was linked to load, and where it was actually loaded.
看到这份资料的时候我还是比较兴奋的,因为这无疑证明了我们的推断是正确的。然而我一番寻找,发现在macos下根本没有link.h
这个头文件。一番搜索没有答案之下,我们安装windows subsystem
然后顺利找到这个头文件。
其中关键部分如下:
struct link_map
{
/* These first few members are part of the protocol with the debugger.
This is the same format used in SVR4. */
ElfW(Addr) l_addr; /* Difference between the address in the ELF
file and the addresses in memory. */
char *l_name; /* Absolute file name object was found in. */
ElfW(Dyn) *l_ld; /* Dynamic section of the shared object. */
struct link_map *l_next, *l_prev; /* Chain of loaded objects. */
};
这里我们简单的复制此关键部分,并修改为
struct link_map
{
/* These first few members are part of the protocol with the debugger.
This is the same format used in SVR4. */
void l_addr; /* Difference between the address in the ELF
file and the addresses in memory. */
char *l_name; /* Absolute file name object was found in. */
void*l_ld; /* Dynamic section of the shared object. */
struct link_map *l_next, *l_prev; /* Chain of loaded objects. */
};
我们的代码如下:
void* xxHandle=dlopen("xx.dylib",RTLD_LAZY);
NSLog(@"[FLWB]:xxHandle:%p",xxHandle);
struct link_map *lm = (struct link_map*)xxHandle;
NSLog(@"[FLWB]:%s = %p",lm->l_name, lm->l_addr);
运行之后神奇的事情发生了,模块名字竟然正确的输出了!虽然基地址返回的明显不对。但至少证明了它确实是有组织的。但可能因为苹果做了修改导致了不同。
我们继续尝试,发现除了名字奇迹般的正确输出以外,其它的无一相符。
这里提一句题外话,我们显然可以看到linux下的设计还是比较精巧的,它使用了链表来存在模块信息,方便插入和遍历。这一点上和windows是一致的。在此情形下,我们需要隐藏模块的话,就需要修改相邻的两个链表左右节点,也就是所谓的“断链”。有兴趣的话,可以自行研究。
借用anroid头文件
linux
的头文件无法适用于ios
,是否是因为版本的问题呢,android是否可行?一番搜索之下我们找到来安卓的对应头文件
struct soinfo {
public:
char name[SOINFO_NAME_LEN];
const Elf32_Phdr* phdr;
size_t phnum;
Elf32_Addr entry;
Elf32_Addr base;
unsigned size;
uint32_t unused1; // DO NOT USE, maintained for compatibility.
Elf32_Dyn* dynamic;
uint32_t unused2; // DO NOT USE, maintained for compatibility
uint32_t unused3; // DO NOT USE, maintained for compatibility
soinfo* next;
unsigned flags;
const char* strtab;
Elf32_Sym* symtab;
size_t nbucket;
size_t nchain;
unsigned* bucket;
unsigned* chain;
unsigned* plt_got;
Elf32_Rel* plt_rel;
size_t plt_rel_count;
Elf32_Rel* rel;
size_t rel_count;
linker_function_t* preinit_array;
size_t preinit_array_count;
linker_function_t* init_array;
size_t init_array_count;
linker_function_t* fini_array;
size_t fini_array_count;
linker_function_t init_func;
linker_function_t fini_func;
#if defined(ANDROID_ARM_LINKER)
// ARM EABI section used for stack unwinding.
unsigned* ARM_exidx;
size_t ARM_exidx_count;
#elif defined(ANDROID_MIPS_LINKER)
unsigned mips_symtabno;
unsigned mips_local_gotno;
unsigned mips_gotsym;
#endif
size_t ref_count;
link_map_t link_map;
bool constructors_called;
// When you read a virtual address from the ELF file, add this
// value to get the corresponding address in the process' address space.
Elf32_Addr load_bias;
bool has_text_relocations;
bool has_DT_SYMBOLIC;
void CallConstructors();
void CallDestructors();
void CallPreInitConstructors();
private:
void CallArray(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
void CallFunction(const char* function_name, linker_function_t function);
};
然而一番尝试后同样无果,这彻底说明了,至少这部分apple是有自己的实现。
特殊符号定位法
注意,这里的特殊符号不是指的标点符号,而是特殊的symbol
。
此方案同样由咸🐟
提出,并由Zhang
扩充。
原理是通过macho里导出的特殊符号来直接获取。具体的符号如
/*
* The value of the link editor defined symbol _MH_DYLIB_SYM is the address
* of the mach header in a Mach-O dylib file type. It does not appear in
* any file type other than a MH_DYLIB file type. The type of the symbol is
* an N_SECT symbol even thought the header is not part of any section. This
* symbol is private to the code in the library it is a part of.
*/
#define _MH_DYLIB_SYM "__mh_dylib_header"
#define MH_DYLIB_SYM "_mh_dylib_header"
我们修改代码如下:
void* xxHandle=dlopen("fuck.dylib",RTLD_LAZY);
void* fuck1 = dlsym(xxHandle, "__mh_dylib_header");
void* fuck2 = dlsym(xxHandle, "_mh_dylib_header");
结果均获取不到这两个符号,推测为我这边测试环境的问题,或者在链接的过程中被去掉了。
特定符号偏移修正法
此方案由柳下惠
提出,原理是通过查找给定的某个符号,然后减去对应的偏移来获取。这种是理论上肯定能行得通的解决方案,但实际上如若是遇到某个模块根本没有导出符号的话,此方案也是束手无策。此处有jmpzws
基于此而来的优化方案。即是查找dyld_stub_binder
这个特定的符号,align然后减去0x4000。
但同样地,我这边也是无法获取到这个符号。
回调获取法
此方案由Zhang
提出,原理是先通过_dyld_register_func_for_add_image
来注册一个回调,然后再dlopen
即可。因为此回调回调用void (*func)(const mach_header *mh, intptr_t vmaddr_slide)
这样的callback,在参数里面刚好就有mach_header
,以此来获取模块基地址。此方案从理论上来讲,是完全可行的。但由于操作过于复杂,且(我)没有找到对应的取消注册钩子的办法导致使用代价过大,所以没有深入探寻。
暴力偏移法
多番尝试无果后,现行简单粗暴的方法。即dump
出疑似结构的handle的内存,观察load address
的位置,然后计算偏移,往后通过偏移来获取。
这种思路类似于游戏外挂的思路,即不管缘由,不考虑理论,先行实现。
于是我们修改代码如下
void* xxHandle=dlopen("xx.dylib",RTLD_LAZY);
NSLog(@"[FLWB]:xxHandle:%p",xxHandle);
NSData *dataData = [NSData dataWithBytes:xxHandle length:0x80];
NSLog(@"[FLWB]:%p = %@",xxHandle, dataData);
果然,我们在第0x58个成员的位置找到了疑似load address
的位置。但这在实际使用上会有很大的问题。其中最为麻烦的是32位和64位的问题,由于类型长度可能不一致,所以在32位下未必是0x58/2,而且在执行时要定义宏或者手动判断系统位数,显然是一个比较繁琐的事情。
但这进一步说明了我们的思路的正确的,一定存在这样的一个结构体。正是由于种种方案给定我们信心,我们才能坚定的走下去。
所以我们下一步就是要找到这个头文件,如果找不到,那我们就需要猜测,然后自行做一个这样的头文件。(反正windows
下都是这么做的。)
源码追溯法
正在此时,突然想起来jmpzws
曾发的一个截图。
ImageLoader* image = (ImageLoader*)(((uintptr_t)handle) & (-4)); // clear mode bits
那么,这个ImageLoader
可不就是我们苦苦寻找的结构体吗?于是我们找到对应的dyld的源码。
我们一番寻找后,发现其原来是个类,头文件如下
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
* Copyright (c) 2004-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#ifndef __IMAGELOADER__
#define __IMAGELOADER__
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <mach/mach_time.h> // struct mach_timebase_info
#include <mach/mach_init.h> // struct mach_thread_self
#include <mach/shared_region.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <stdint.h>
#include <stdlib.h>
#include <TargetConditionals.h>
#include <vector>
#include <new>
#include <uuid/uuid.h>
#if __arm__
#include <mach/vm_page_size.h>
#endif
#if __x86_64__ || __i386__
#include <CrashReporterClient.h>
#else
// work around until iOS has CrashReporterClient.h
#define CRSetCrashLogMessage(x)
#define CRSetCrashLogMessage2(x)
#endif
#ifndef SHARED_REGION_BASE_ARM64
#define SHARED_REGION_BASE_ARM64 0x7FFF80000000LL
#endif
#ifndef SHARED_REGION_SIZE_ARM64
#define SHARED_REGION_SIZE_ARM64 0x10000000LL
#endif
#define LOG_BINDINGS 0
#include "mach-o/dyld_images.h"
#include "mach-o/dyld_priv.h"
#include "DyldSharedCache.h"
#if __i386__
#define SHARED_REGION_BASE SHARED_REGION_BASE_I386
#define SHARED_REGION_SIZE SHARED_REGION_SIZE_I386
#elif __x86_64__
#define SHARED_REGION_BASE SHARED_REGION_BASE_X86_64
#define SHARED_REGION_SIZE SHARED_REGION_SIZE_X86_64
#elif __arm__
#define SHARED_REGION_BASE SHARED_REGION_BASE_ARM
#define SHARED_REGION_SIZE SHARED_REGION_SIZE_ARM
#elif __arm64__
#define SHARED_REGION_BASE SHARED_REGION_BASE_ARM64
#define SHARED_REGION_SIZE SHARED_REGION_SIZE_ARM64
#endif
#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
#define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10
#endif
#ifndef EXPORT_SYMBOL_FLAGS_REEXPORT
#define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08
#endif
#ifndef LC_MAIN
#define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */
struct entry_point_command {
uint32_t cmd; /* LC_MAIN only used in MH_EXECUTE filetypes */
uint32_t cmdsize; /* 24 */
uint64_t entryoff; /* file (__TEXT) offset of main() */
uint64_t stacksize;/* if not zero, initial stack size */
};
#endif
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#define SPLIT_SEG_SHARED_REGION_SUPPORT 0
#define SPLIT_SEG_DYLIB_SUPPORT 0
#define PREBOUND_IMAGE_SUPPORT __arm__
#define TEXT_RELOC_SUPPORT __i386__
#define SUPPORT_OLD_CRT_INITIALIZATION 0
#define SUPPORT_LC_DYLD_ENVIRONMENT 1
#define SUPPORT_VERSIONED_PATHS 1
#define SUPPORT_CLASSIC_MACHO __arm__
#define SUPPORT_ZERO_COST_EXCEPTIONS (!__USING_SJLJ_EXCEPTIONS__)
#define INITIAL_IMAGE_COUNT 150
#define SUPPORT_ACCELERATE_TABLES !TARGET_IPHONE_SIMULATOR
#define SUPPORT_ROOT_PATH TARGET_IPHONE_SIMULATOR
#define USES_CHAINED_BINDS (__arm64e__)
#else
#define SPLIT_SEG_SHARED_REGION_SUPPORT 0
#define SPLIT_SEG_DYLIB_SUPPORT __i386__
#define PREBOUND_IMAGE_SUPPORT __i386__
#define TEXT_RELOC_SUPPORT __i386__
#define SUPPORT_OLD_CRT_INITIALIZATION __i386__
#define SUPPORT_LC_DYLD_ENVIRONMENT (__i386__ || __x86_64__)
#define SUPPORT_VERSIONED_PATHS 1
#define SUPPORT_CLASSIC_MACHO 1
#define SUPPORT_ZERO_COST_EXCEPTIONS 1
#define INITIAL_IMAGE_COUNT 200
#define SUPPORT_ACCELERATE_TABLES 0
#define SUPPORT_ROOT_PATH 1
#define USES_CHAINED_BINDS 0
#endif
#define MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE (32*1024)
#define MH_HAS_OBJC 0x40000000
// <rdar://problem/13590567> optimize away dyld's initializers
#define VECTOR_NEVER_DESTRUCTED(type) \
namespace std { \
template <> \
__vector_base<type, std::allocator<type> >::~__vector_base() { } \
}
#define VECTOR_NEVER_DESTRUCTED_EXTERN(type) \
namespace std { \
template <> \
__vector_base<type, std::allocator<type> >::~__vector_base(); \
}
#define VECTOR_NEVER_DESTRUCTED_IMPL(type) \
namespace std { \
template <> \
__vector_base<type, std::allocator<type> >::~__vector_base() { } \
}
// utilities
namespace dyld {
extern __attribute__((noreturn)) void throwf(const char* format, ...) __attribute__((format(printf, 1, 2)));
extern void log(const char* format, ...) __attribute__((format(printf, 1, 2)));
extern void warn(const char* format, ...) __attribute__((format(printf, 1, 2)));
extern const char* mkstringf(const char* format, ...) __attribute__((format(printf, 1, 2)));
#if LOG_BINDINGS
extern void logBindings(const char* format, ...) __attribute__((format(printf, 1, 2)));
#endif
}
extern "C" int vm_alloc(vm_address_t* addr, vm_size_t size, uint32_t flags);
extern "C" void* xmmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset);
#if __LP64__
struct macho_header : public mach_header_64 {};
struct macho_nlist : public nlist_64 {};
#else
struct macho_header : public mach_header {};
struct macho_nlist : public nlist {};
#endif
#if __arm64__
#define dyld_page_trunc(__addr) (__addr & (-16384))
#define dyld_page_round(__addr) ((__addr + 16383) & (-16384))
#define dyld_page_size 16384
#elif __arm__
#define dyld_page_trunc(__addr) trunc_page_kernel(__addr)
#define dyld_page_round(__addr) round_page_kernel(__addr)
#define dyld_page_size vm_kernel_page_size
#else
#define dyld_page_trunc(__addr) (__addr & (-4096))
#define dyld_page_round(__addr) ((__addr + 4095) & (-4096))
#define dyld_page_size 4096
#endif
struct ProgramVars
{
const void* mh;
int* NXArgcPtr;
const char*** NXArgvPtr;
const char*** environPtr;
const char** __prognamePtr;
};
//
// ImageLoader is an abstract base class. To support loading a particular executable
// file format, you make a concrete subclass of ImageLoader.
//
// For each executable file (dynamic shared object) in use, an ImageLoader is instantiated.
//
// The ImageLoader base class does the work of linking together images, but it knows nothing
// about any particular file format.
//
//
class ImageLoader {
public:
typedef uint32_t DefinitionFlags;
static const DefinitionFlags kNoDefinitionOptions = 0;
static const DefinitionFlags kWeakDefinition = 1;
typedef uint32_t ReferenceFlags;
static const ReferenceFlags kNoReferenceOptions = 0;
static const ReferenceFlags kWeakReference = 1;
static const ReferenceFlags kTentativeDefinition = 2;
enum PrebindMode { kUseAllPrebinding, kUseSplitSegPrebinding, kUseAllButAppPredbinding, kUseNoPrebinding };
enum BindingOptions { kBindingNone, kBindingLazyPointers, kBindingNeverSetLazyPointers };
enum SharedRegionMode { kUseSharedRegion, kUsePrivateSharedRegion, kDontUseSharedRegion, kSharedRegionIsSharedCache };
struct Symbol; // abstact symbol
struct MappedRegion {
uintptr_t address;
size_t size;
};
struct RPathChain {
RPathChain(const RPathChain* n, std::vector<const char*>* p) : next(n), paths(p) {};
const RPathChain* next;
std::vector<const char*>* paths;
};
struct DOFInfo {
void* dof;
const mach_header* imageHeader;
const char* imageShortName;
};
struct DynamicReference {
ImageLoader* from;
ImageLoader* to;
};
struct InitializerTimingList
{
uintptr_t count;
struct {
const char* shortName;
uint64_t initTime;
} images[1];
void addTime(const char* name, uint64_t time);
};
typedef void (^CoalesceNotifier)(const Symbol* implSym, const ImageLoader* implIn, const mach_header* implMh);
struct LinkContext {
ImageLoader* (*loadLibrary)(const char* libraryName, bool search, const char* origin, const RPathChain* rpaths, bool enforceIOSMac, unsigned& cacheIndex);
void (*terminationRecorder)(ImageLoader* image);
bool (*flatExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image);
bool (*coalescedExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image, CoalesceNotifier);
unsigned int (*getCoalescedImages)(ImageLoader* images[], unsigned imageIndex[]);
void (*undefinedHandler)(const char* name);
MappedRegion* (*getAllMappedRegions)(MappedRegion*);
void * (*bindingHandler)(const char *, const char *, void *);
void (*notifySingle)(dyld_image_states, const ImageLoader* image, InitializerTimingList*);
void (*notifyBatch)(dyld_image_states state, bool preflightOnly);
void (*removeImage)(ImageLoader* image);
void (*registerDOFs)(const std::vector<DOFInfo>& dofs);
void (*clearAllDepths)();
void (*printAllDepths)();
unsigned int (*imageCount)();
void (*setNewProgramVars)(const ProgramVars&);
bool (*inSharedCache)(const char* path);
void (*setErrorStrings)(unsigned errorCode, const char* errorClientOfDylibPath,
const char* errorTargetDylibPath, const char* errorSymbol);
ImageLoader* (*findImageContainingAddress)(const void* addr);
void (*addDynamicReference)(ImageLoader* from, ImageLoader* to);
#if SUPPORT_ACCELERATE_TABLES
void (*notifySingleFromCache)(dyld_image_states, const mach_header* mh, const char* path);
dyld_image_state_change_handler (*getPreInitNotifyHandler)(unsigned index);
dyld_image_state_change_handler (*getBoundBatchHandler)(unsigned index);
#endif
#if SUPPORT_OLD_CRT_INITIALIZATION
void (*setRunInitialzersOldWay)();
#endif
BindingOptions bindingOptions;
int argc;
const char** argv;
const char** envp;
const char** apple;
const char* progname;
ProgramVars programVars;
ImageLoader* mainExecutable;
const char* const * imageSuffix;
#if SUPPORT_ROOT_PATH
const char** rootPaths;
#endif
const DyldSharedCache* dyldCache;
const dyld_interpose_tuple* dynamicInterposeArray;
size_t dynamicInterposeCount;
PrebindMode prebindUsage;
SharedRegionMode sharedRegionMode;
bool dyldLoadedAtSameAddressNeededBySharedCache;
bool strictMachORequired;
bool allowAtPaths;
bool allowEnvVarsPrint;
bool allowEnvVarsPath;
bool allowEnvVarsSharedCache;
bool allowClassicFallbackPaths;
bool allowInsertFailures;
bool mainExecutableCodeSigned;
bool preFetchDisabled;
bool prebinding;
bool bindFlat;
bool linkingMainExecutable;
bool startedInitializingMainExecutable;
#if __MAC_OS_X_VERSION_MIN_REQUIRED
bool marzipan;
#endif
bool verboseOpts;
bool verboseEnv;
bool verboseLoading;
bool verboseMapping;
bool verboseRebase;
bool verboseBind;
bool verboseWeakBind;
bool verboseInit;
bool verboseDOF;
bool verbosePrebinding;
bool verboseCoreSymbolication;
bool verboseWarnings;
bool verboseRPaths;
bool verboseInterposing;
bool verboseCodeSignatures;
};
struct CoalIterator
{
ImageLoader* image;
const char* symbolName;
unsigned int loadOrder;
bool weakSymbol;
bool symbolMatches;
bool done;
// the following are private to the ImageLoader subclass
uintptr_t curIndex;
uintptr_t endIndex;
uintptr_t address;
uintptr_t type;
uintptr_t addend;
uintptr_t imageIndex;
};
virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned imageIndex) = 0;
virtual bool incrementCoalIterator(CoalIterator&) = 0;
virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& context) = 0;
virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context) = 0;
struct UninitedUpwards
{
uintptr_t count;
ImageLoader* images[1];
};
// constructor is protected, but anyone can delete an image
virtual ~ImageLoader();
// link() takes a newly instantiated ImageLoader and does all
// fixups needed to make it usable by the process
void link(const LinkContext& context, bool forceLazysBound, bool preflight, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath);
// runInitializers() is normally called in link() but the main executable must
// run crt code before initializers
void runInitializers(const LinkContext& context, InitializerTimingList& timingInfo);
// called after link() forces all lazy pointers to be bound
void bindAllLazyPointers(const LinkContext& context, bool recursive);
// used by dyld to see if a requested library is already loaded (might be symlink)
bool statMatch(const struct stat& stat_buf) const;
// get short name of this image
const char* getShortName() const;
// returns leaf name
static const char* shortName(const char* fullName);
// get path used to load this image, not necessarily the "real" path
const char* getPath() const { return fPath; }
uint32_t getPathHash() const { return fPathHash; }
// get the "real" path for this image (e.g. no @rpath)
const char* getRealPath() const;
// get path this image is intended to be placed on disk or NULL if no preferred install location
virtual const char* getInstallPath() const = 0;
// image was loaded with NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME and all clients are looking for install path
bool matchInstallPath() const;
void setMatchInstallPath(bool);
// mark that this image's exported symbols should be ignored when linking other images (e.g. RTLD_LOCAL)
void setHideExports(bool hide = true);
// check if this image's exported symbols should be ignored when linking other images
bool hasHiddenExports() const;
// checks if this image is already linked into the process
bool isLinked() const;
// even if image is deleted, leave segments mapped in
void setLeaveMapped();
// even if image is deleted, leave segments mapped in
bool leaveMapped() { return fLeaveMapped; }
// image resides in dyld shared cache
virtual bool inSharedCache() const { return false; };
// checks if the specifed address is within one of this image's segments
virtual bool containsAddress(const void* addr) const;
// checks if the specifed symbol is within this image's symbol table
virtual bool containsSymbol(const void* addr) const = 0;
// checks if the specifed address range overlaps any of this image's segments
virtual bool overlapsWithAddressRange(const void* start, const void* end) const;
// adds to list of ranges of memory mapped in
void getMappedRegions(MappedRegion*& region) const;
// st_mtime from stat() on file
time_t lastModified() const;
// only valid for main executables, returns a pointer its entry point from LC_MAIN
virtual void* getEntryFromLC_MAIN() const = 0;
// only valid for main executables, returns a pointer its main from LC_UNIXTHREAD
virtual void* getEntryFromLC_UNIXTHREAD() const = 0;
// dyld API's require each image to have an associated mach_header
virtual const struct mach_header* machHeader() const = 0;
// dyld API's require each image to have a slide (actual load address minus preferred load address)
virtual uintptr_t getSlide() const = 0;
// last address mapped by image
virtual const void* getEnd() const = 0;
// image has exports that participate in runtime coalescing
virtual bool hasCoalescedExports() const = 0;
// search symbol table of definitions in this image for requested name
virtual bool findExportedSymbolAddress(const LinkContext& context, const char* symbolName,
const ImageLoader* requestorImage, int requestorOrdinalOfDef,
bool runResolver, const ImageLoader** foundIn, uintptr_t* address) const;
// search symbol table of definitions in this image for requested name
virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const = 0;
// search symbol table of definitions in this image for requested name
virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const ImageLoader** foundIn) const {
return findExportedSymbol(name, searchReExports, this->getPath(), foundIn);
}
// gets address of implementation (code) of the specified exported symbol
virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context,
const ImageLoader* requestor=NULL, bool runResolver=false, const char* symbolName=NULL) const = 0;
// gets attributes of the specified exported symbol
virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const = 0;
// gets name of the specified exported symbol
virtual const char* getExportedSymbolName(const Symbol* sym) const = 0;
// gets how many symbols are exported by this image
virtual uint32_t getExportedSymbolCount() const = 0;
// gets the i'th exported symbol
virtual const Symbol* getIndexedExportedSymbol(uint32_t index) const = 0;
// find exported symbol as if imported by this image
// used by RTLD_NEXT
virtual const Symbol* findExportedSymbolInDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const;
// find exported symbol as if imported by this image
// used by RTLD_SELF
virtual const Symbol* findExportedSymbolInImageOrDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const;
// gets how many symbols are imported by this image
virtual uint32_t getImportedSymbolCount() const = 0;
// gets the i'th imported symbol
virtual const Symbol* getIndexedImportedSymbol(uint32_t index) const = 0;
// gets attributes of the specified imported symbol
virtual ReferenceFlags getImportedSymbolInfo(const Symbol* sym) const = 0;
// gets name of the specified imported symbol
virtual const char* getImportedSymbolName(const Symbol* sym) const = 0;
// find the closest symbol before addr
virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const = 0;
// for use with accelerator tables
virtual const char* getIndexedPath(unsigned) const { return getPath(); }
virtual const char* getIndexedShortName(unsigned) const { return getShortName(); }
// checks if this image is a bundle and can be loaded but not linked
virtual bool isBundle() const = 0;
// checks if this image is a dylib
virtual bool isDylib() const = 0;
// checks if this image is a main executable
virtual bool isExecutable() const = 0;
// checks if this image is a main executable
virtual bool isPositionIndependentExecutable() const = 0;
// only for main executable
virtual bool forceFlat() const = 0;
// called at runtime when a lazily bound function is first called
virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) = 0;
// called at runtime when a fast lazily bound function is first called
virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context,
void (*lock)(), void (*unlock)()) = 0;
// calls termination routines (e.g. C++ static destructors for image)
virtual void doTermination(const LinkContext& context) = 0;
// return if this image has initialization routines
virtual bool needsInitialization() = 0;
// return if this image has specified section and set start and length
virtual bool getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length) = 0;
// fills in info about __eh_frame and __unwind_info sections
virtual void getUnwindInfo(dyld_unwind_sections* info) = 0;
// given a pointer into an image, find which segment and section it is in
virtual const struct macho_section* findSection(const void* imageInterior) const = 0;
// given a pointer into an image, find which segment and section it is in
virtual bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) = 0;
// the image supports being prebound
virtual bool isPrebindable() const = 0;
// the image is prebindable and its prebinding is valid
virtual bool usablePrebinding(const LinkContext& context) const = 0;
// add all RPATH paths this image contains
virtual void getRPaths(const LinkContext& context, std::vector<const char*>&) const = 0;
// image has or uses weak definitions that need runtime coalescing
virtual bool participatesInCoalescing() const = 0;
// if image has a UUID, copy into parameter and return true
virtual bool getUUID(uuid_t) const = 0;
// dynamic interpose values onto this image
virtual void dynamicInterpose(const LinkContext& context) = 0;
// record interposing for any late binding
void addDynamicInterposingTuples(const struct dyld_interpose_tuple array[], size_t count);
virtual const char* libPath(unsigned int) const = 0;
// Image has objc sections, so information objc about when it comes and goes
virtual bool notifyObjC() const { return false; }
virtual bool overridesCachedDylib(uint32_t& num) const { return false; }
virtual void setOverridesCachedDylib(uint32_t num) { }
//
// A segment is a chunk of an executable file that is mapped into memory.
//
virtual unsigned int segmentCount() const = 0;
virtual const char* segName(unsigned int) const = 0;
virtual uintptr_t segSize(unsigned int) const = 0;
virtual uintptr_t segFileSize(unsigned int) const = 0;
virtual bool segHasTrailingZeroFill(unsigned int) = 0;
virtual uintptr_t segFileOffset(unsigned int) const = 0;
virtual bool segReadable(unsigned int) const = 0;
virtual bool segWriteable(unsigned int) const = 0;
virtual bool segExecutable(unsigned int) const = 0;
virtual bool segUnaccessible(unsigned int) const = 0;
virtual bool segHasPreferredLoadAddress(unsigned int) const = 0;
virtual uintptr_t segPreferredLoadAddress(unsigned int) const = 0;
virtual uintptr_t segActualLoadAddress(unsigned int) const = 0;
virtual uintptr_t segActualEndAddress(unsigned int) const = 0;
// info from LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS
virtual uint32_t sdkVersion() const = 0;
virtual uint32_t minOSVersion() const = 0;
// if the image contains interposing functions, register them
virtual void registerInterposing(const LinkContext& context) = 0;
virtual bool usesChainedFixups() const { return false; }
// when resolving symbols look in subImage if symbol can't be found
void reExport(ImageLoader* subImage);
virtual void recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload);
virtual void recursiveBindWithAccounting(const LinkContext& context, bool forceLazysBound, bool neverUnload);
void weakBind(const LinkContext& context);
void applyInterposing(const LinkContext& context);
dyld_image_states getState() { return (dyld_image_states)fState; }
ino_t getInode() const { return fInode; }
dev_t getDevice() const { return fDevice; }
// used to sort images bottom-up
int compare(const ImageLoader* right) const;
void incrementDlopenReferenceCount() { ++fDlopenReferenceCount; }
bool decrementDlopenReferenceCount();
void printReferenceCounts();
uint32_t dlopenCount() const { return fDlopenReferenceCount; }
void setCanUnload() { fNeverUnload = false; fLeaveMapped = false; }
bool neverUnload() const { return fNeverUnload; }
void setNeverUnload() { fNeverUnload = true; fLeaveMapped = true; }
void setNeverUnloadRecursive();
bool isReferencedDownward() { return fIsReferencedDownward; }
virtual uintptr_t resolveWeak(const LinkContext& context, const char* symbolName, bool weak_import, bool runResolver,
const ImageLoader** foundIn) { return 0; }
// triggered by DYLD_PRINT_STATISTICS to write info on work done and how fast
static void printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo);
static void printStatisticsDetails(unsigned int imageCount, const InitializerTimingList& timingInfo);
// used with DYLD_IMAGE_SUFFIX
static void addSuffix(const char* path, const char* suffix, char* result);
static uint32_t hash(const char*);
static const uint8_t* trieWalk(const uint8_t* start, const uint8_t* end, const char* stringToFind);
// used instead of directly deleting image
static void deleteImage(ImageLoader*);
static bool haveInterposingTuples() { return !fgInterposingTuples.empty(); }
static void clearInterposingTuples() { fgInterposingTuples.clear(); }
static void applyInterposingToDyldCache(const LinkContext& context);
bool dependsOn(ImageLoader* image);
void setPath(const char* path);
void setPaths(const char* path, const char* realPath);
void setPathUnowned(const char* path);
void clearDepth() { fDepth = 0; }
int getDepth() { return fDepth; }
void setBeingRemoved() { fBeingRemoved = true; }
bool isBeingRemoved() const { return fBeingRemoved; }
void markNotUsed() { fMarkedInUse = false; }
void markedUsedRecursive(const std::vector<DynamicReference>&);
bool isMarkedInUse() const { return fMarkedInUse; }
void setAddFuncNotified() { fAddFuncNotified = true; }
bool addFuncNotified() const { return fAddFuncNotified; }
struct InterposeTuple {
uintptr_t replacement;
ImageLoader* neverImage; // don't apply replacement to this image
ImageLoader* onlyImage; // only apply replacement to this image
uintptr_t replacee;
};
static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end);
static intptr_t read_sleb128(const uint8_t*& p, const uint8_t* end);
void vmAccountingSetSuspended(const LinkContext& context, bool suspend);
protected:
// abstract base class so all constructors protected
ImageLoader(const char* path, unsigned int libCount);
ImageLoader(const ImageLoader&);
void operator=(const ImageLoader&);
void operator delete(void* image) throw() { ::free(image); }
struct LibraryInfo {
uint32_t checksum;
uint32_t minVersion;
uint32_t maxVersion;
};
struct DependentLibrary {
ImageLoader* image;
uint32_t required : 1,
checksumMatches : 1,
isReExported : 1,
isSubFramework : 1;
};
struct DependentLibraryInfo {
const char* name;
LibraryInfo info;
bool required;
bool reExported;
bool upward;
};
typedef void (*Initializer)(int argc, const char* argv[], const char* envp[], const char* apple[], const ProgramVars* vars);
typedef void (*Terminator)(void);
unsigned int libraryCount() const { return fLibraryCount; }
virtual ImageLoader* libImage(unsigned int) const = 0;
virtual bool libReExported(unsigned int) const = 0;
virtual bool libIsUpward(unsigned int) const = 0;
virtual void setLibImage(unsigned int, ImageLoader*, bool, bool) = 0;
// To link() an image, its dependent libraries are loaded, it is rebased, bound, and initialized.
// These methods do the above, exactly once, and it the right order
virtual void recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath);
virtual unsigned recursiveUpdateDepth(unsigned int maxDepth);
virtual void recursiveRebase(const LinkContext& context);
virtual void recursiveApplyInterposing(const LinkContext& context);
virtual void recursiveGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs);
virtual void recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&);
// fill in information about dependent libraries (array length is fLibraryCount)
virtual void doGetDependentLibraries(DependentLibraryInfo libs[]) = 0;
// called on images that are libraries, returns info about itself
virtual LibraryInfo doGetLibraryInfo(const LibraryInfo& requestorInfo) = 0;
// do any fix ups in this image that depend only on the load address of the image
virtual void doRebase(const LinkContext& context) = 0;
// do any symbolic fix ups in this image
virtual void doBind(const LinkContext& context, bool forceLazysBound) = 0;
// called later via API to force all lazy pointer to be bound
virtual void doBindJustLazies(const LinkContext& context) = 0;
// if image has any dtrace DOF sections, append them to list to be registered
virtual void doGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs) = 0;
// do interpose
virtual void doInterpose(const LinkContext& context) = 0;
// run any initialization routines in this image
virtual bool doInitialization(const LinkContext& context) = 0;
// return if this image has termination routines
virtual bool needsTermination() = 0;
// support for runtimes in which segments don't have to maintain their relative positions
virtual bool segmentsMustSlideTogether() const = 0;
// built with PIC code and can load at any address
virtual bool segmentsCanSlide() const = 0;
// set how much all segments slide
virtual void setSlide(intptr_t slide) = 0;
// returns if all dependent libraries checksum's were as expected and none slide
bool allDependentLibrariesAsWhenPreBound() const;
// in mach-o a child tells it parent to re-export, instead of the other way around...
virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const = 0;
// in mach-o a parent library knows name of sub libraries it re-exports..
virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const = 0;
virtual bool weakSymbolsBound(unsigned index) { return fWeakSymbolsBound; }
virtual void setWeakSymbolsBound(unsigned index) { fWeakSymbolsBound = true; }
// set fState to dyld_image_state_memory_mapped
void setMapped(const LinkContext& context);
void setFileInfo(dev_t device, ino_t inode, time_t modDate);
void setDepth(uint16_t depth) { fDepth = depth; }
static uintptr_t interposedAddress(const LinkContext& context, uintptr_t address, const ImageLoader* notInImage, const ImageLoader* onlyInImage=NULL);
static uintptr_t fgNextPIEDylibAddress;
static uint32_t fgImagesWithUsedPrebinding;
static uint32_t fgImagesUsedFromSharedCache;
static uint32_t fgImagesHasWeakDefinitions;
static uint32_t fgImagesRequiringCoalescing;
static uint32_t fgTotalRebaseFixups;
static uint32_t fgTotalBindFixups;
static uint32_t fgTotalBindSymbolsResolved;
static uint32_t fgTotalBindImageSearches;
static uint32_t fgTotalLazyBindFixups;
static uint32_t fgTotalPossibleLazyBindFixups;
static uint32_t fgTotalSegmentsMapped;
static uint32_t fgSymbolTrieSearchs;
static uint64_t fgTotalBytesMapped;
static uint64_t fgTotalBytesPreFetched;
static uint64_t fgTotalLoadLibrariesTime;
public:
static uint64_t fgTotalObjCSetupTime;
static uint64_t fgTotalDebuggerPausedTime;
static uint64_t fgTotalRebindCacheTime;
static uint64_t fgTotalRebaseTime;
static uint64_t fgTotalBindTime;
static uint64_t fgTotalWeakBindTime;
static uint64_t fgTotalDOF;
static uint64_t fgTotalInitTime;
protected:
static std::vector<InterposeTuple> fgInterposingTuples;
const char* fPath;
const char* fRealPath;
dev_t fDevice;
ino_t fInode;
time_t fLastModified;
uint32_t fPathHash;
uint32_t fDlopenReferenceCount; // count of how many dlopens have been done on this image
struct recursive_lock {
recursive_lock(mach_port_t t) : thread(t), count(0) {}
mach_port_t thread;
int count;
};
void recursiveSpinLock(recursive_lock&);
void recursiveSpinUnLock();
private:
const ImageLoader::Symbol* findExportedSymbolInDependentImagesExcept(const char* name, const ImageLoader** dsiStart,
const ImageLoader**& dsiCur, const ImageLoader** dsiEnd, const ImageLoader** foundIn) const;
void processInitializers(const LinkContext& context, mach_port_t this_thread,
InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& ups);
recursive_lock* fInitializerRecursiveLock;
uint16_t fDepth;
uint16_t fLoadOrder;
uint32_t fState : 8,
fLibraryCount : 10,
fAllLibraryChecksumsAndLoadAddressesMatch : 1,
fLeaveMapped : 1, // when unloaded, leave image mapped in cause some other code may have pointers into it
fNeverUnload : 1, // image was statically loaded by main executable
fHideSymbols : 1, // ignore this image's exported symbols when linking other images
fMatchByInstallName : 1,// look at image's install-path not its load path
fInterposed : 1,
fRegisteredDOF : 1,
fAllLazyPointersBound : 1,
fMarkedInUse : 1,
fBeingRemoved : 1,
fAddFuncNotified : 1,
fPathOwnedByImage : 1,
fIsReferencedDownward : 1,
fWeakSymbolsBound : 1;
static uint16_t fgLoadOrdinal;
};
VECTOR_NEVER_DESTRUCTED_EXTERN(ImageLoader::InterposeTuple);
#endif
如此之长的头文件,我们去掉方法和静态成员,整理以后,只有以下成员
const char* fPath;
const char* fRealPath;
dev_t fDevice;
ino_t fInode;
time_t fLastModified;
uint32_t fPathHash;
uint32_t fDlopenReferenceCount; // count of how many dlopens have been done on this image
recursive_lock* fInitializerRecursiveLock;
uint16_t fDepth;
uint16_t fLoadOrder;
uint32_t fState : 8,
fLibraryCount : 10,
fAllLibraryChecksumsAndLoadAddressesMatch : 1,
fLeaveMapped : 1, // when unloaded, leave image mapped in cause some other code may have pointers into it
fNeverUnload : 1, // image was statically loaded by main executable
fHideSymbols : 1, // ignore this image's exported symbols when linking other images
fMatchByInstallName : 1,// look at image's install-path not its load path
fInterposed : 1,
fRegisteredDOF : 1,
fAllLazyPointersBound : 1,
fMarkedInUse : 1,
fBeingRemoved : 1,
fAddFuncNotified : 1,
fPathOwnedByImage : 1,
fIsReferencedDownward : 1,
fWeakSymbolsBound : 1;
根据我们上次dump的结果来看,然后是不够的,至少这里面根本没有我们需要的load address
。
那么,这是什么原因呢?
由于c++继承的特性,在c++里,类指针的第一个成员是虚表地址
,往后依次是父类的成员(包括私有成员)和自己类成员。显然这里真正的类型应是它的某个子类。
结合我们上面的dump结果和apple的源码,一番尝试后,我们可以总结出它的结构如下
typedef struct {
void* vTable;
const char* fPath;
const char* fRealPath;
dev_t fDevice;
ino_t fInode;
time_t fLastModified;
uint32_t fPathHash;
uint32_t fDlopenReferenceCount; // count of how many dlopens have been done on this image
void* fInitializerRecursiveLock;
uint16_t fDepth;
uint16_t fLoadOrder;
uint32_t fState : 8,
fLibraryCount : 10,
fAllLibraryChecksumsAndLoadAddressesMatch : 1,
fLeaveMapped : 1, // when unloaded, leave image mapped in cause some other code may have pointers into it
fNeverUnload : 1, // image was statically loaded by main executable
fHideSymbols : 1, // ignore this image's exported symbols when linking other images
fMatchByInstallName : 1,// look at image's install-path not its load path
fInterposed : 1,
fRegisteredDOF : 1,
fAllLazyPointersBound : 1,
fMarkedInUse : 1,
fBeingRemoved : 1,
fAddFuncNotified : 1,
fPathOwnedByImage : 1,
fIsReferencedDownward : 1,
fWeakSymbolsBound : 1;
uint64_t fCoveredCodeLength;
const uint8_t* fMachOData;
const uint8_t* fLinkEditBase; // add any internal "offset" to this to get mapped address
uintptr_t fSlide;
uint32_t fEHFrameSectionOffset;
uint32_t fUnwindInfoSectionOffset;
uint32_t fDylibIDOffset;
uint32_t fSegmentsCount : 8,
fIsSplitSeg : 1,
fInSharedCache : 1,
#if TEXT_RELOC_SUPPORT
fTextSegmentRebases : 1,
fTextSegmentBinds : 1,
#endif
#if __i386__
fReadOnlyImportSegment : 1,
#endif
fHasSubLibraries : 1,
fHasSubUmbrella : 1,
fInUmbrella : 1,
fHasDOFSections : 1,
fHasDashInit : 1,
fHasInitializers : 1,
fHasTerminators : 1,
fNotifyObjC : 1,
fRetainForObjC : 1,
fRegisteredAsRequiresCoalescing : 1, // <rdar://problem/7886402> Loading MH_DYLIB_STUB causing coalescable miscount
fOverrideOfCacheImageNum : 12;
static uint32_t fgSymbolTableBinarySearchs;
} MachoImage;
由此,我们可以顺利的通过以下代码来获取到他基地址。
MachoImage* image=(MachoImage*)dlopen("xx.dylib",RTLD_LAZY);
NSLog(@"[FLWB]:xxHandle:%p",image->fLinkEditBase);
当然fMachOData
也是可以的,但考虑到后面的注释
add any internal "offset" to this to get mapped address
我们还是优先使用fLinkEditBase
。
此外,我们也可以通过此头文件来获取其它未公开的成员。
到此为止,我们不但达到了我们获取基地址的需求,还能够额外的获取甚至关键的信息。
免责声明
本文所公开的技术细节仅做技术交流,任何权利责任人需自行承担其用途产生的后果。另外,转载本文需注明来源及作者信息。
鸣谢
排名不分先后
资料文献
- https://stackoverflow.com/questions/19451791/get-loaded-address-of-a-elf-binary-dlopen-is-not-working-as-expected
- 从dlsym()源码看android 动态链接过程
- dyld source