通过修改系统框架加载dylib

2016-10-15 15:22:02来源:作者:Cocoa ‘for iOS/Mac’人点击

第七城市

目的:注入dylib到更多的程序里 环境:Mac

比如IOKit:

首先比如目的是注入代码到尽可能最多的进程里,注入到底层于IOKit内核扩展交互的函数,借助环境变量注入的进程并不多,而且如果由用户启动的程序或者有环境变量保护的程序并不会被影响. 做过一个测试,从launchd启动后为launchctl设置全局环境变量,由此注入的进程pid最小的就是UserEventAgent,那次pid为46.而且进入用户界面后,用户界面各种窗口程序都没有被影响,比如Finder、Spotlight

使用环境变量注入的动态库使用DYLD_INTERPOSE方法替换函数,但是只能替换动态库的外部函数,fishhook也是同样的.而且动态库内部对该函数的引用也是无法改变的.因为地址已经写固定了,然后再是jayfreeman开发的substrate框架原理,这个和其他注入方法都要好,MSHookFunction直接修改函数内存,这样可以保证这个函数被修改,不过substrate并没有Mac版,但是有了原理针对个别函数实现也不难.

最关键还是注入到程序的范围,除了环境变量和修改程序二进制,就是去修改框架的二进制,直接修改/System/…/Framework..下面的IOKit二进制,为其加上LC_LOAD_WEAK_DYLIB(for safety)引用,引用到自己的框架,一开始写了一个输出函数作为debug,使用用户运行些程序都可以正常运行到注入代码,然后并没有备份原本的IOKit在文件系统里,保存文件系统后关机,再次开机时很快就卡住了,报错动态代码签名错误,进程cs_invalid_page杀死了,而且在single-user模式里连用mount重新挂接文件系统都无法做到,因为内部有内核扩展的调用.

不过可以使用lldb调试内核,可以重写内核内存.一开始断点在vm相关函数上,检查了cs_enforcement_enabled并没有启用,然后断点在cs_invalid_page,cs_force_kill也为0,在获取到current_proc时,该进程就已经被标记了CS_KILL

然后为了绕过cs_invalid_page用了笨方法,在函数开头修改pc和修改rax为0以跳过检查.

具体为:

断点在0xffffff801669b29b <+27>: movl 0x4721b7(%rip), %eax -> rax=0 と pc = 0xffffff801669b5680xffffff801669b568 <+744>: movl -0x20(%rbp), %eax

果然进程没有被杀死,mount重新挂接文件系统可以成功,然后我就想写个lldb脚本来自动化这个步骤,额,但每次调用mount测试,在cs_invalid_page断点会被停止2次,但在lldb中只会被检测到一次(hit只会+1),然后断点脚本也只会执行一次,这里我也不清楚为什么. 所以lldb脚本并没有起到作用.

我也尝试了另一个不改变pc的方法,在这个位置断点:

0xffffff800429b348 <+200>: cmpl $0x0, %ecx大概为c代码中if (p->p_csflags & CS_KILL) 这个位置 (kern_cs+211行)在断点后修改ecx为0,这样send_kill变量不会被赋值,进程也不会被杀掉,也可以正常执行了,但在lldb上同样失败

也尝试了不再改变判断,转而重写进程结构体

0xffffff800429b338 <+184>: movq -0x18(%rbp), %rax //rax为p结构体0xffffff800429b33c <+188>: movl 0x320(%rax), %ecx //p+0x320(p->p_csflags)0xffffff800429b342 <+194>: andl $0x200, %ecx0xffffff800429b348 <+200>: cmpl $0x0, %ecx断点在+188的位置,原本为0x02804A09 去掉CS_KILL A->8执行(lldb) memory write -s 0x4 '(uint32_t*)($rax+0x320)' 0x02804809

结果也是失败,每次值会不同,我不能改固定的结果.

最后尝试重写内核指令(注意,当kdp断点在该位置时,不能修改,会报kdp错误):

0xffffff800429b342 <+194>: andl $0x200, %ecx0xffffff800429b348 <+200>: cmpl $0x0, %ecx0xffffff800429b34e <+206>: je 0xffffff800429b3da把andl $0x200, %ecx 重写为-> mov ecx,0 (我知道用xor指令可以只要2字节)(lldb) memory write -s 0x4 0xffffff800429b342 0x000000b9(lldb) memory write -s 0x4 0xffffff800429b346 0xf9819000

改好后可以用了,然后退出single-user模式,看着一个个加载kext,启动Daemon进程,但是额,最后应该会跳转到findle界面的地方卡住了…. 不知道原因,但我的猜测是,我只是修改了一个错误处理函数,进程结构体里的flag仍然没变,其他地方也许有不正常的地方.

不过好在sshd可以启动了,使用ssh可以连上,sftp提示Received message too long,网上查了下,普遍是因为初始化bash文件中写了echo,cat等输出函数导致错误…一开始我还是想不到原因,然后用了scp,冒出了我注入动态库里的输出语句就知道错误原因了.. 使用vim修改了注入的动态库.把call printf去掉,sftp成功连接上,然后可以放进原本的IOKit二进制,恢复了系统正常.

其实为修改过的系统框架打上adhoc的自签名(Mac默认是允许执行无效签名),动态代码签名就会通过. 最近很少更新,整理也很花时间,在研究ing 以后一起更新吧.

第七城市

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台