引言
当检测到某个App运行,并在这个App进程下加载一个动态库,可以调用了dyld中的一个宏,DYLD_INSERT_LIBRARIES 插入一个动态库
dyld是一个应用,dyld是怎么做到?
有点疑问,我们要去逆向dyld汇编,然后去搞吗?
不需要,因为dyld,Apple已经开源了这个项目dyld
分析dyld
站在巨人的肩膀来分析dyld,大家在看一个开源代码的时候,代码量小还好,如果代码量大从头开始看,起点->过程->结果,这么一套下来,我都不敢想象,需要花费多长的时间,而且大部分我们只需要一部份的逻辑,关键代码,就好比我们接受一个旧的项目,需要修改某一处,我们需要做的步骤是定位->调试->修改->结果
我想要知道的是DYLD_INSERT_LIBRARIES
在dyld中做了什么样的角色,为什么说站在巨人的肩膀,因为你可以借助别人分析的成果,这样可以节省很多不必要的时间。
在dyld.app中我们找到这句代码
const char* const * DYLD_INSERT_LIBRARIES;
// load any inserted libraries
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);
}
//如果DYLD_INSERT_LIBRARIES的指针不为空,遍历所有的lib然后去加载
因为dyld是在操作系统中启动的时候加载的,你hook的代码的动态库是在App中注入、插入的so,如果你想改变dyld的源码那是没办法的,
然后我在网上找到一个分析dyld源码的一篇博客或者公开课上面已经忘记了,上面着重讲到了这句代码
if ( gLinkContext.processIsRestricted ) {
pruneEnvironmentVariables(envp, &apple);
// set again because envp and apple may have changed or moved
setContext(mainExecutableMH, argc, argv, envp, apple);
}
//如果这个程序是受限制的那么会直接将这个动态库移除也就是不加载这个动态库
然后在根据processIsRestricted这个属性来查找相关代码关联
// any processes with setuid or setgid bit set or with __RESTRICT segment is restricted
if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) {
gLinkContext.processIsRestricted = true;
}
/*有两个判断条件都可以将processIsRestricted 这个属性设置成true,
那么只要我们能够修改issetugid()、hasRestrictedSegment()这两个方法
我们就能直接让DYLD_INSERT_LIBRARIES加载动态库的方式失效
*/
我只分析一下后面的内容hasRestrictedSegment()
//
// Look for a special segment in the mach header.
// Its presences means that the binary wants to have DYLD ignore
// DYLD_ environment variables.
//
#if __MAC_OS_X_VERSION_MIN_REQUIRED
static bool hasRestrictedSegment(const macho_header* mh)
{
const uint32_t cmd_count = mh->ncmds;
const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
const struct load_command* cmd = cmds;
for (uint32_t i = 0; i < cmd_count; ++i) {
switch (cmd->cmd) {
case LC_SEGMENT_COMMAND:
{
const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
//dyld::log("seg name: %s\n", seg->segname);
if (strcmp(seg->segname, "__RESTRICT") == 0) {
const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects];
for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
if (strcmp(sect->sectname, "__restrict") == 0)
return true;
}
}
}
break;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
return false;
}
#endif
这段代码是将整个macho_segment_command拿出来,首先判断segname、然后在判断一下sectname ,具体操作可以通过MachOView这个软件打开MachO文件查看
猜想
我只要找到带有Section64(_RESTRICT,_restrict)这个Section就能使hasRestrictedSegment
返回true,且 gLinkContext.processIsRestricted = true
那么怎么才能添加一个这个Section呐?