首页
社区
课程
招聘
[讨论]通过修改Mach-O中Load Commands进行全局进程劫持
发表于: 2014-3-3 16:00 10346

[讨论]通过修改Mach-O中Load Commands进行全局进程劫持

2014-3-3 16:00
10346
Mach-O格式, 是Mach object文件格式的缩写,是一种可执行文件、目标代码、共享程序库、动态加载代码和核心DUMP(摘自度娘),类似于Win环境的PE、Linux环境的ELF。

Load Commands数据位于Mach Header数据之后,顾名思义为加载器命令。

    Structure overview
|--------------------------------
|          Mach Header     
|--------------------------------
|        Load Command 1
|        Load Command 2
|        Load Command n
|--------------------------------
|               Data            
|--------------------------------

Load Command定义有诸多,比如LC_SEGMENT、LC_SYMTAB、LC_SYMSEG等等,更多的LC定义可在<mach-o/loader.h>头文件中找到。利用otool -l命令可以查看一个mach-o文件的Load Commands。

利用Load Commands进行进程劫持的大致思路如下:
1.找一个任意进程启动都会加载的dylib;
2.为dylib写入自定义shellcode;
3.修改/添加此dylib的LC_ROUTINES命令,指向shellcode;
4.替换原dylib。

每步骤原理:
1.找到一个任意进程都会加载的dylib,比如/usr/lib/dyld,这样就达到了全局范围的效果。
2.写入shellcode,比如执行一段dlopen的代码,打开指定动态库,或是什么。
3.当链接器加入-init选项时,LC_ROUTINES就会被加入到Load Commands中,描述该dylib的入口。如此我们将原入口替换为shellcode的地址,在shellcode执行完再跳回原入口来运行自定义代码。
4.替换后开启任意进程看效果

以上是本人的假设,还没经过实践。欢迎大家讨论~

注意:任意进程都会加载的dylib一般都是进程严重依赖的系统库,无十足把握,切莫随意操作,否则后果自负。

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (5)
雪    币: 14
活跃值: (26)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
我对MachO的头部不是很熟悉,这种深度的hack也不大会,但最近开始了这方面知识的学习,楼主的思路我觉得理论上是可行的,但在实际情况中,dylib的ctor并不一定在LC_ROUTINES段(当然楼主也说“当链接器加入-init选项时”才会有这个段),也可能在__mod_init_func段(拿我自己的dylib测试了一下,也可以参考2fcK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3u0D9L8$3N6K6i4K6u0W2k6h3#2T1j5i4u0U0j5h3c8W2M7X3!0Q4x3X3g2U0L8$3#2Q4x3V1k6W2j5X3!0D9K9h3&6Y4i4K6u0r3x3U0l9I4x3q4)9J5c8U0l9I4i4K6u0r3x3U0W2Q4x3V1j5#2y4U0x3&6i4K6u0r3i4@1g2r3i4@1u0o6i4K6R3&6i4@1g2r3i4@1u0o6i4K6S2o6i4@1f1$3i4K6R3&6i4K6R3H3i4@1f1@1i4@1u0n7i4@1p5#2i4@1f1#2i4K6W2o6i4@1p5^5i4@1f1$3i4K6W2n7i4@1u0r3i4@1f1$3i4K6S2p5i4@1p5J5i4@1f1#2i4K6R3&6i4K6S2p5i4@1f1@1i4@1t1^5i4@1p5@1i4@1f1@1i4@1t1^5i4@1q4m8i4@1f1$3i4@1q4q4i4@1t1#2i4@1f1&6i4K6R3K6i4@1u0p5i4@1f1$3i4K6W2n7i4@1u0r3i4@1f1$3i4K6S2p5i4@1p5J5i4@1f1@1i4@1t1^5i4K6R3H3i4@1f1@1i4@1t1^5i4K6S2n7i4@1f1$3i4K6V1#2i4K6R3^5i4@1f1$3i4K6W2q4i4K6W2o6i4@1f1#2i4K6S2r3i4@1q4r3i4@1f1^5i4K6R3K6i4@1u0p5i4@1f1@1i4@1u0o6i4K6W2m8i4@1f1$3i4K6W2n7i4@1t1@1i4@1f1%4i4K6V1H3i4K6R3$3i4@1f1$3i4K6R3K6i4@1t1K6i4@1g2r3i4@1u0o6i4K6R3^5i4@1f1#2i4K6R3#2i4@1t1%4i4@1f1@1i4@1u0p5i4K6V1K6i4@1f1%4i4K6V1@1i4@1p5^5i4@1f1@1i4@1u0n7i4K6R3H3i4@1f1@1i4@1t1&6i4K6R3^5i4@1f1#2i4K6R3%4i4@1u0p5i4@1f1$3i4K6V1#2i4@1t1H3i4@1f1$3i4K6R3^5i4K6V1I4i4@1f1@1i4@1t1&6i4K6W2r3i4@1f1@1i4@1t1^5i4K6S2p5i4@1f1$3i4K6V1^5i4@1q4r3i4@1f1#2i4@1p5@1i4@1q4m8i4@1f1$3i4@1t1^5i4K6R3#2i4@1f1$3i4@1p5#2i4K6W2m8i4@1g2r3i4@1u0o6i4K6S2o6i4@1f1@1i4@1u0p5i4K6R3$3e0#2y4j5i4K6u0r3K9f1!0e0i4@1f1#2i4@1p5#2i4@1u0p5i4@1f1#2i4K6R3K6i4K6S2r3i4@1f1$3i4K6W2o6i4K6R3&6i4@1f1@1i4@1t1^5i4K6R3H3i4@1f1%4i4@1t1K6i4@1u0n7i4@1f1#2i4K6R3^5i4K6V1%4i4@1f1#2i4K6R3%4i4@1u0p5i4@1f1$3i4K6V1#2i4@1t1H3i4@1f1$3i4K6V1^5i4@1q4r3i4@1f1@1i4@1t1^5i4K6V1K6i4@1f1&6i4K6V1%4i4@1p5^5i4@1f1#2i4@1p5@1i4K6R3@1i4@1f1%4i4K6V1H3i4K6R3$3e0h3q4U0K9p5!0Q4c8e0g2Q4b7e0c8Q4b7U0c8Q4c8e0W2Q4z5o6y4Q4b7e0S2Q4c8e0N6Q4z5f1q4Q4z5o6c8Q4c8f1k6Q4b7V1y4Q4z5o6V1`.
2014-3-4 23:24
0
雪    币: 183
活跃值: (350)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
3
__mod_init_func这个学习了,按照博文原文
So, how do C++ static constructors get called?  Looking around a bit (see otool -lv), I see sections like __mod_init_func, with the S_MOD_INIT_FUNC_POINTERS flag set.
看得出应该是C++独有的。

在此严谨一下术语:
LC_ROUTINES为Load Commands的一种;
__mod_init_func是LC_SEGMENT_COMMAND中segment的一个section;
S_MOD_INIT_FUNC_POINTERS为section type,这种type的section数据只放了入口地址。

目前还没有深究Loader的内部原理,不过目测LC_ROUTINES和S_MOD_INIT_FUNC_POINTERS是可以并存的。在Load Commands后面往往有一大片空白,如果LC_ROUTINES不存在则手动构造一个便是

或者修改它依赖的DYLIBS估计也是可行的,实现起来似乎也更容易一些。
2014-3-5 09:21
0
雪    币: 14
活跃值: (26)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
MachO的头部格式我还不熟,不过手动加一个LC_ROUTINES应该是可以的。但是,如果LC_ROUTINES和S_MOD_INIT_FUNC_POINTERS如果并存的话,是不是必须两者定义的入口地址也要相同呢?如果不同,哪一个入口地址才是对的呢?
我也觉得加一个依赖dylib更好,不需要更改原来的头部,就保险一些
2014-3-5 13:53
0
雪    币: 6
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
__mod_init_func 不是C++ 独有的。 每个dylib可以定义多个constructor, 这些constructor会被放入 __mod_init_func中,在dylib 装载完毕后执行。

483K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1k6i4k6W2L8r3!0H3k6i4u0Q4x3X3g2S2M7s2m8D9k6g2)9J5k6h3y4G2L8g2)9J5c8X3I4A6j5Y4u0S2M7Y4W2Q4x3V1k6E0j5h3y4Q4x3V1k6V1L8$3y4#2L8h3g2F1N6r3q4@1K9h3!0F1i4K6u0r3c8r3g2$3k6h3I4G2M7r3g2J5g2r3!0G2L8s2y4Q4x3V1k6o6L8$3&6U0k6i4m8@1N6h3q4D9i4K6u0r3c8s2W2F1j5h3#2A6j5@1I4A6j5Y4u0S2M7X3W2W2M7#2)9J5c8U0p5H3x3q4)9J5k6p5q4J5N6r3W2U0L8r3g2K6i4K6u0r3c8s2W2F1j5h3#2A6j5@1I4A6j5Y4u0S2M7Y4W2p5k6i4y4A6k6$3&6s2N6h3W2V1k6h3I4A6L8X3g2K6i4K6u0W2K9s2c8E0L8l9`.`.

1. 用clang -dynamiclib mylib.c -o mylib.dylib 编译下边的代码
2. 用otool -l 查看__mod_init_func的地址
3. 用xxd 读出地址里的值 (little endian)
4. 用otool -tV 反汇编 mylib.dylib, 发现定义的constructor地址正是3中读出来的值

#include <stdlib.h>                                                               
#include <stdio.h>                                                                 
                                                                                   
                                                                                   
__attribute__((constructor))                                                      
static void initializer()                                                         
{                                                                                 
    printf("%s\n", __FUNCTION__);                                                  
}                                                                                 
                                                                                   
                                                                                   
void func1()                                                                       
{                                                                                 
    printf("%s\n", __FUNCTION__);                                                  
}   

另外,严格意义上讲, dyld 不是dylib。 dyld 中也不应该存在 __mod_init_func
2014-3-6 11:26
0
雪    币: 14
活跃值: (26)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
唉,一开始就是搞的iOS,没搞过OSX,忘记参考OSX上的文档啦!感谢提醒
2014-3-6 22:54
0
游客
登录 | 注册 方可回帖
返回