首页
社区
课程
招聘
[原创]The shadow over Netfilter: 新一代脏页表利用技巧-CVE-2024-1086
发表于: 2025-7-11 00:01 3486

[原创]The shadow over Netfilter: 新一代脏页表利用技巧-CVE-2024-1086

2025-7-11 00:01
3486

依本人之见,这 CVE-2024-1086,乃是潜藏于 Linux 内核深处,那名为 netfilter 的古老机制(?)中,一处令人不安的裂隙。它并非来自外域的邪神,而是源自内部,那本应秩序井然的内存管理,在某些特定、扭曲的条件下,竟会陷入一种可怖的“用后释放” (Use-After-Free) 状态。
其本质,在于 netfilter 的 nf_tables 组件在处理规则(rules)的垃圾回收和删除时,未能完全斩断其与内存的联系。这就像是,一个本应被彻底遗忘的实体,其幽灵却在内存的深渊中徘徊,等待着被恶意的意志所唤醒。当这幽灵被重新占据,其原有的结构被篡改,便能赋予凡人以僭越神祇的权能——从一个卑微的本地用户,跃升为掌控整个系统的至高存在。这并非寻常的错误,而是一种对底层秩序的亵渎,一种对内存生命周期的扭曲。它揭示了,即使在最坚固的数字堡垒中,亦有其脆弱、可被腐化的角落,足以让混沌的低语
渗透进来,将控制权拱手让与那些窥伺已久的黑暗存在。其影响,正如那无形之触,悄然伸向系统的核心,将权限的锁链一一解开,直至整个世界都暴露在无尽的深渊之前。
------------ Gemini-2.5-flash

该漏洞是一个netfilter中处理HOOK函数时导致的Double Free 释放漏洞,博客涉及了较新(大概)的漏洞利用技术dirty pagetable,利用ip分片机制来无限扩大时间片并且可以控制skb释放时间的技巧,以低特权刷新tlb等技巧。

环境以exp位于我的github仓库,包含rootfs和bzImage,一键启动
linux kernel: linux-6.3.4
rootfs: busybox
内核编译配置需要选中以下选项

本节大部分的知识来源于Linux kernel doc
他用来表示一个网络流量包

在内核中的数据结构如下:

[!NOTE]
这个结构体专门用来存放元数据,对于网络包的数据则存放在其他缓冲区当中

sk_buff.head指向主要的head buffer,而这个head buffer分为两个部分

Netfilter是一种linux内核网络过滤器
Netfilter包含几种table(struct xt_table),每个table用来存储不同的配置信息
每个table有多个chain(struct xt_table_info), chain表示对报文的拦截点,就比如一个网络层ipv4报文传过来,他会对每个拦截点进行检测,也就是每条chain
而每个chain则包含一些rule(struct ipt_entry),一条rule则包含一个或多各正则匹配规则(match),和一个执行动作
其拥有以下几种功能(table):

这个结构体就是上述的表table,存在一个private字段,类型为struct xt_table_info且添加了__rcu的标识,这个标识表示在读取的时候不用加锁,
但在写的时候更新数据

这里的entries的每个字段则存放了每个cpu所对应的专属buf,如下

而每个CPU bufs里面包含的内容则是一个个chains数组

然后每个chains里面则包含了一条条rules

那么由于每个chains里面的rule条数都可能不同,所以需要还存在一定的偏移字段
所以struct xt_table_info.hook_entry[]里面就存的是在cpu bufs里面每个chains的起始偏移
这样就可以精准定位到每个chains下的rule
然后由于这里还存在一个默认规则字段,所以在对应的struct xt_table.underflow[]则存放的是每个chains的默认rule的相对偏移

然后每条rule是使用sturct ipt_entry来表示

而每一条rule包含多个匹配规则struct xt_entry_match和一个执行动作struct xt_entry_target

netfilter通过setsockopt, getsockopt来进行用户-内核的交互,
在此基础上nftables实现了自己的一个框架,允许不同的防火墙来实现自己和用户空间的通信函数
这里主要涉及了nf_register_sockopt()函数将nf_sockopt_ops结构体实例注册到netfilter管理的全局链表当中
注册完毕就可以调用nf_sockopt_find()来查找对应的nf_sockopt_ops

用来表示数据包的处理指令

下面的则是netfilter的hook时机

[!NOTE]
在调试时,开启netfilter的情况下,向自身主机发送ip头dst_addr=255.255.255.255, src_addr=1.1.1.1,
socket系统调用中sock_addr=127.0.0.2的环回地址的情况下,经过的HOOK点顺序是NF_INET_LOCAL_OUT, NF_INET_POST_ROUTING, NF_INET_PRE_ROUTING, 这里在后面的ip发包在调试过程中会有更详细的讲解

这两个库实际上是方便用户来编写netfilter的rules,还有通过netlink来访问内核提供的NFTABLES板块,可以直接从官网获取源码然后自己编译

构造IP包发送,需要用到两种系统调用,分别是socket, sendto
socket系统调用用来构建套接字socket, 套接字用来收发数据,并且与内核sock进行关联
sendto则利用socket传回的fd来发送信息

这里的SOCK_RAW表示我们自己构造IP头部

用户使用sendto向指定地址发送消息的时候,会首先调用内核系统调用__x64_sys_sendto
在本次漏洞利用中我们是构造了RAW_INET包,该flag表示我们是想要由自身来提供头部信息
这里大致介绍一下在sendto系统调用的流程中,执行流在内核中的旅行路线,这里将主要聚焦于netfilter hook点

所以,如果我们发送一个数据包分片的时候,经过的netfilter hook点顺序可能是
NF_INET_LOCAL_OUT -> NF_INET_POST_ROUTING -> NF_INET_PRE_ROUTING -> NF_INET_LOCAL_IN

漏洞出自于nf_hook_slow()

在switch case 语句中NF_DROP的选项,这是由于当用户所设计的netfilter hook函数最后返回NF_DROP时即将执行的程序流程

这里的流程将会首先释放这个skb,但是这里程序再次判断了NF_DROP_GETERR来获取返回参数,
这就给了用户一定的操作空间,如果可以通过这个函数将ret修改为NF_ACCEPT, 也就是1,
那么当这个函数返回后将回到下面的nf_hook()函数

nf_hook()函数返回1则会导致调用后续的okfn(),这里使用ip_forward()进行举例
该函数将在最后调用NF_HOOK()函数

NF_HOOK()函数将会根据nf_hook()函数的返回值来决定是否调用okfn参数指针所指向的地址
返回值为1则说明hook函数处理的返回值为NF_ACCEPT

那么就能在之后对skb进行二次释放,因此导致了double free

知道了漏洞出处,接下来的步骤便是尝试触发,在nf_hook_slow()函数当中

这里的for循环是调用当前hook点里面所有的hook,并且返回值是可以由用户构造的
所以为了满足我们的目的,需要verdict满足:

因此计算得出可以构造verdict = 0xffff0000即可满足这样一个条件,而构造verdict我们可以通过向内核传递在用户态构造的netfilter tables-chains-rules来达成,这一过程可以通过netlink来完成传递。

__free_pages()函数可以看出

当使用put_page_testzero()发现页面的引用计数为1,则将释放他
而如果此时我们不做任何事直接将他应用在double free上,则put_page_testzero()将会发现page.refcount == 0从而报错

因此可以在double free中途插入重分配,使得refcount++,这样就可以绕过这部分检查
作者的例子如下:

__do_kmalloc_node()函数当中,

在使用kmalloc分配内存堆块的过程中,
如果分配size大小大于KMALLOC_MAX_CACHE_SIZE
那么将会使用alloc_page()直接从伙伴系统申请页面,kfree也是同理

但某些对象即使小于KMALLOC_MAX_CACHE_SIZE,同样会由于开发人员减少开销的操作来直接使用alloc_page(),例如PTE page
除此之外,当对象使用alloc_pages()分配或者释放的时候,如果大小小于order 3,则会从PCP list(以前也被叫做冷热页,这里主要是为了防止多次访问全局的buddysystem的时候造成的阻塞,因此每个CPU拥有自己的free page 列表)里面进行分配或者释放,也就是说当内核申请页表空间的时候首先将会使用alloc_pages()在当前CPU的pcp list里面分配页面

详细的内存管理可以查看内存管理浅析
目前目标:

目前可以获取的情报:

综上,skb将会以 order-4(16x4KB)的形态出击

而为了造成pcp list没有符合的页面,需要首先耗尽其中所有的页
内核函数rmqueue_bulk()__rmqueue_pcplist()分配,当pcplist为空的时候,就将使用该函数来填充rmqueue_bulk,
该函数将从buddysystem获取页面来填充pcplist

因此这里可以堆喷PTE page来消耗该pcplist

ip 头部
使用了IP包分片的技巧来巧妙的进行延迟的double free
IP分片这一块是408网络的重点部分:), 如果记得不太详细可以直接查看该博客
在上面基础知识我们知道如果传递IP分片到目标机器,首先并不会直接送入上层进行分析,而是在内核维持一个skb队列,
等待队列满了之后再递交上层处理

我们知道在上面的漏洞点中,我们可以在处理netfilter hook函数的时候将某个skb进行一次释放,而释放过后依旧会走完全程,
在通过漏洞释放完skb之后,skb->len字段将会被随机数覆盖,这是内核的保护机制,此时二次释放该skb可能会导致内核panic
因此何时触发二次释放就十分重要,
这里存在两个技巧:

整合该两点,我们就可以通过构造IP头,在任意我们想要的时机对一个skb进行double free

需要堆喷PTE,在dirtypagetable里面就已经讲解到,可以通过用户使用mmap来分配页表,
但是mmap的时候并不是分配页表的时候,他会首先在内核task_struct中建立VMA,当用户访问VMA区域内的地址的时候才会出发page fault,
然后造成页表分配

但是如果选择的映射区域不准确,则可能会造成一系列分配例如PUD,PMD的额外分配,这样在我们堆喷的过程中就可能造成噪音从而导致利用失败

那么我们就可以选择舍弃一部分PTE,那就是首先通过分配某个PTE的第一个page来提前将PMD和PUD分配完毕,例子如下

对于提前的VMA写入就将导致PUD和PMD的分配,接下来当继续访问在PUD/PMD可表示的虚拟地址范围内的地址就可以保证稳定分配PTE

作者由dirtypagetable启发而来,简单介绍一下dirty pagetable的原理在于:

[!NOTE]
当存在UAF的时候,将漏洞堆块和PTE页表分配到一起,然后就可以通过操作漏洞堆块来影响PTE页表,从而导致任意物理地址写
但是这个步骤需要漏洞堆块稳定能操作,比如说是稳定写入字节

但可惜的是有时候无法获取一个稳定的漏洞利用模块,比如这次漏洞的skb,因此作者仅仅使用一次漏洞触发,
之后的操作完全是对于用户虚拟地址的访问来达成任意物理地址读写
实际操作起来的原理十分的简单,仅仅是使PMD和PTE均分配到同一个物理页面,这样做导致的后果就是:

[!IMPORTANT]
原来PTE访问的page页面会变成PMD访问到的PTE页面,所以如果能够在原来PTE访问的page页面伪造合适的页表项,则就可以通过PMD来执行任意物理地址读写操作

TLB用来缓存虚拟地址到物理地址的映射,从而可以使得加快页表访存,
所以在dirty pagedirectory的工作下我们需要清空TLB来保证该技术的稳定性,而这一步骤也可以被称为刷新TLB

这里该作者提出的刷新方法是在堆喷PMD和PTE的时候,可以将他们标记为共享页面,然后通过创建子进程,然后该子进程调用munmap()刷新内存TLB,从而导致父进程的TLB也会进行刷新,该操作在需要我们不断修改页表的环境下是极其重要的
最后让子进程进入睡眠

接下来讲解具体流程:

e03K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6H3N6$3&6A6L8X3N6Q4x3X3g2@1k6h3y4Z5i4K6u0r3L8X3k6@1j5h3u0D9k6i4y4Q4x3V1j5`.
47cK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1M7$3q4#2j5$3g2Q4x3X3g2Y4K9i4c8Z5N6h3u0Q4x3X3g2A6L8#2)9J5c8U0t1H3x3U0c8Q4x3V1j5H3y4g2)9J5c8U0p5H3i4K6u0r3b7#2k6q4i4K6u0V1x3U0l9J5y4q4)9J5k6o6p5H3z5o6k6Q4x3V1k6Q4x3U0x3J5i4K6u0V1x3#2)9J5k6r3W2H3i4K6t1#2c8e0k6Q4x3U0f1&6y4g2)9J5y4f1t1H3i4K6t1#2c8e0k6Q4x3U0f1^5c8q4)9J5y4f1q4q4i4K6t1#2c8e0g2Q4x3U0f1^5b7#2)9J5y4e0R3#2i4K6t1#2c8e0g2Q4x3U0f1^5z5q4)9J5y4e0R3$3i4K6t1#2c8e0N6Q4x3U0f1^5z5g2)9J5y4e0R3%4
72bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6H3j5h3y4C8k6i4c8H3N6i4y4Z5k6i4u0K6i4K6u0W2L8X3g2@1i4K6u0r3j5X3I4G2k6#2)9J5c8X3W2H3i4K6u0V1k6Y4u0S2k6$3#2W2L8Y4c8S2N6r3W2G2L8W2)9J5k6r3W2F1i4K6u0V1k6r3g2@1j5h3W2D9i4K6u0r3
977K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6W2L8W2)9J5k6i4N6A6K9$3W2H3k6h3c8A6j5g2)9J5k6h3!0J5k6#2)9J5c8Y4N6A6K9$3W2Q4x3V1k6f1M7X3q4F1M7$3I4S2N6r3W2G2L8W2)9#2k6X3I4G2L8$3E0S2M7$3W2V1k6g2)9#2k6X3u0#2k6X3k6W2M7R3`.`.
662K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6@1K9r3g2G2M7X3W2Q4x3X3g2A6L8#2)9J5c8X3u0D9L8$3N6Q4x3V1k6J5k6i4k6A6N6X3W2F1k6#2)9J5k6s2c8Z5k6g2)9J5k6r3#2G2k6s2m8J5L8$3u0W2i4K6u0V1M7r3q4@1K9q4)9J5k6s2c8W2j5$3S2F1K9i4q4#2k6g2)9J5k6r3!0$3k6i4u0U0L8$3#2A6L8X3N6Q4x3X3c8K6k6h3q4J5j5$3S2Q4x3X3c8T1K9h3&6S2M7Y4W2Q4x3X3c8Z5j5h3&6V1L8r3g2J5i4K6u0V1M7r3q4@1j5$3R3`.

CONFIG_NF_TABLES=y
CONFIG_NF_TABLES_INET=y
CONFIG_USER_NS=y
CONFIG_NF_TABLES=y
CONFIG_NF_TABLES_INET=y
CONFIG_USER_NS=y
struct sk_buff {
    ...
    /* These elements must be at the end, see alloc_skb() for details.  */
    sk_buff_data_t      tail;
    sk_buff_data_t      end;
    unsigned char       *head,
                *data;
    unsigned int        truesize;
    refcount_t      users;
    ...
};
struct sk_buff {
    ...
    /* These elements must be at the end, see alloc_skb() for details.  */
    sk_buff_data_t      tail;
    sk_buff_data_t      end;
    unsigned char       *head,
                *data;
    unsigned int        truesize;
    refcount_t      users;
    ...
};
                                ---------------
                               | sk_buff       |
                                ---------------
   ,---------------------------  + head
  /          ,-----------------  + data
 /          /      ,-----------  + tail
|          |      |            , + end
|          |      |           |
v          v      v           v
 -----------------------------------------------
| headroom | data |  tailroom | skb_shared_info |
 -----------------------------------------------
                               + [page frag]
                               + [page frag]
                               + [page frag]
                               + [page frag]       ---------
                               + frag_list    --> | sk_buff |
                                                   ---------
                                ---------------
                               | sk_buff       |
                                ---------------
   ,---------------------------  + head
  /          ,-----------------  + data
 /          /      ,-----------  + tail
|          |      |            , + end
|          |      |           |
v          v      v           v
 -----------------------------------------------
| headroom | data |  tailroom | skb_shared_info |
 -----------------------------------------------
                               + [page frag]
                               + [page frag]
                               + [page frag]
                               + [page frag]       ---------
                               + frag_list    --> | sk_buff |
                                                   ---------
/* Furniture shopping... */
struct xt_table {
    struct list_head list;
 
    /* What hooks you will enter on */
    unsigned int valid_hooks;
 
    /* Man behind the curtain... */
    struct xt_table_info __rcu *private; //用来存放指向`xt_table_info`的指针
 
    /* Set this to THIS_MODULE if you are a module, otherwise NULL */
    struct module *me;
 
    u_int8_t af;        /* address/protocol family */
    int priority;       /* hook order */
 
    /* called when table is needed in the given netns */
    int (*table_init)(struct net *net);
 
    /* A unique name... */
    const char name[XT_TABLE_MAXNAMELEN];
};
/* Furniture shopping... */
struct xt_table {
    struct list_head list;
 
    /* What hooks you will enter on */
    unsigned int valid_hooks;
 
    /* Man behind the curtain... */
    struct xt_table_info __rcu *private; //用来存放指向`xt_table_info`的指针
 
    /* Set this to THIS_MODULE if you are a module, otherwise NULL */
    struct module *me;
 
    u_int8_t af;        /* address/protocol family */
    int priority;       /* hook order */
 
    /* called when table is needed in the given netns */
    int (*table_init)(struct net *net);
 
    /* A unique name... */
    const char name[XT_TABLE_MAXNAMELEN];
};
/* The table itself */
struct xt_table_info {
    /* Size per table */
    unsigned int size;
    /* Number of entries: FIXME. --RR */
    unsigned int number;
    /* Initial number of entries. Needed for module usage count */
    unsigned int initial_entries;
 
    /* Entry points and underflows */
    unsigned int hook_entry[NF_INET_NUMHOOKS];          //存放每个chains的偏移
    unsigned int underflow[NF_INET_NUMHOOKS];           //存放每个chains中default rule的偏移
 
    /*
     * Number of user chains. Since tables cannot have loops, at most
     * @stacksize jumps (number of user chains) can possibly be made.
     */
    unsigned int stacksize;
    void ***jumpstack;
 
    unsigned char entries[] __aligned(8);
};
/* The table itself */
struct xt_table_info {
    /* Size per table */
    unsigned int size;
    /* Number of entries: FIXME. --RR */
    unsigned int number;
    /* Initial number of entries. Needed for module usage count */
    unsigned int initial_entries;
 
    /* Entry points and underflows */
    unsigned int hook_entry[NF_INET_NUMHOOKS];          //存放每个chains的偏移
    unsigned int underflow[NF_INET_NUMHOOKS];           //存放每个chains中default rule的偏移
 
    /*
     * Number of user chains. Since tables cannot have loops, at most
     * @stacksize jumps (number of user chains) can possibly be made.
     */
    unsigned int stacksize;
    void ***jumpstack;
 
    unsigned char entries[] __aligned(8);
};
┌──────────────┬───────────────────┬─────────┐
│void  entries0│                   │CPU0 bufs│
├──────────────┼─────────┐         │         │
│void  entries1│         └─────────┴─────────┘
├──────────────┤
│void  entries2│
└──────────────┘
┌──────────────┬───────────────────┬─────────┐
│void  entries0│                   │CPU0 bufs│
├──────────────┼─────────┐         │         │
│void  entries1│         └─────────┴─────────┘
├──────────────┤
│void  entries2│
└──────────────┘
┌───────────┬──────────────┬───────────┐
│ chains0   │   chains1    │   chains2 │
└───────────┴──────────────┴───────────┘
┌───────────┬──────────────┬───────────┐
│ chains0   │   chains1    │   chains2 │
└───────────┴──────────────┴───────────┘
┌──────┬───────┬──────┐
│rule0 │ rule1 │ rule2│
└──────┴───────┴──────┘
┌──────┬───────┬──────┐
│rule0 │ rule1 │ rule2│
└──────┴───────┴──────┘
struct ipt_entry {
    struct ipt_ip ip;
 
    /* Mark with fields that we care about. */
    unsigned int nfcache;
 
    /* Size of ipt_entry + matches */
    __u16 target_offset;
    /* Size of ipt_entry + matches + target */
    __u16 next_offset;
 
    /* Back pointer */
    unsigned int comefrom;
 
    /* Packet and byte counters. */
    struct xt_counters counters;
 
    /* The matches (if any), then the target. */
    unsigned char elems[0];
};
struct ipt_entry {
    struct ipt_ip ip;
 
    /* Mark with fields that we care about. */
    unsigned int nfcache;
 
    /* Size of ipt_entry + matches */
    __u16 target_offset;
    /* Size of ipt_entry + matches + target */
    __u16 next_offset;
 
    /* Back pointer */
    unsigned int comefrom;
 
    /* Packet and byte counters. */
    struct xt_counters counters;
 
    /* The matches (if any), then the target. */
    unsigned char elems[0];
};
struct xt_entry_match {
    union {
        struct {
            __u16 match_size;
 
            /* Used by userspace */
            char name[XT_EXTENSION_MAXNAMELEN];
            __u8 revision;
        } user;
        struct {
            __u16 match_size;
 
            /* Used inside the kernel */
            struct xt_match *match;
        } kernel;
 
        /* Total length */
        __u16 match_size;
    } u;
 
    unsigned char data[0];
};
 
struct xt_entry_target {
    union {
        struct {
            __u16 target_size;
 
            /* Used by userspace */
            char name[XT_EXTENSION_MAXNAMELEN];
            __u8 revision;
        } user;
        struct {
            __u16 target_size;
 
            /* Used inside the kernel */
            struct xt_target *target;
        } kernel;
 
        /* Total length */
        __u16 target_size;
    } u;
 
    unsigned char data[0];static struct sk_buff *ip_rcv_core(struct sk_buff *skb, struct net *net)
{
};
struct xt_entry_match {
    union {
        struct {
            __u16 match_size;
 
            /* Used by userspace */
            char name[XT_EXTENSION_MAXNAMELEN];
            __u8 revision;
        } user;
        struct {
            __u16 match_size;
 
            /* Used inside the kernel */
            struct xt_match *match;
        } kernel;
 
        /* Total length */
        __u16 match_size;
    } u;
 
    unsigned char data[0];
};
 
struct xt_entry_target {
    union {
        struct {
            __u16 target_size;
 
            /* Used by userspace */
            char name[XT_EXTENSION_MAXNAMELEN];
            __u8 revision;
        } user;
        struct {
            __u16 target_size;
 
            /* Used inside the kernel */
            struct xt_target *target;
        } kernel;
 
        /* Total length */
        __u16 target_size;
    } u;
 
    unsigned char data[0];static struct sk_buff *ip_rcv_core(struct sk_buff *skb, struct net *net)
{
};
/* Responses from hook functions. */
#define NF_DROP 0
#define NF_ACCEPT 1
#define NF_STOLEN 2
#define NF_QUEUE 3
#define NF_REPEAT 4
#define NF_STOP 5   /* Deprecated, for userspace nf_queue compatibility. */
/* Responses from hook functions. */
#define NF_DROP 0
#define NF_ACCEPT 1
#define NF_STOLEN 2
#define NF_QUEUE 3
#define NF_REPEAT 4
#define NF_STOP 5   /* Deprecated, for userspace nf_queue compatibility. */
enum nf_inet_hooks {
    NF_INET_PRE_ROUTING,
    NF_INET_LOCAL_IN,
    NF_INET_FORWARD,
    NF_INET_LOCAL_OUT,
    NF_INET_POST_ROUTING,
    NF_INET_NUMHOOKS,
    NF_INET_INGRESS = NF_INET_NUMHOOKS,
};
enum nf_dev_hooks {
    NF_NETDEV_INGRESS,
    NF_NETDEV_EGRESS,
    NF_NETDEV_NUMHOOKS
};
enum nf_inet_hooks {
    NF_INET_PRE_ROUTING,
    NF_INET_LOCAL_IN,
    NF_INET_FORWARD,
    NF_INET_LOCAL_OUT,
    NF_INET_POST_ROUTING,
    NF_INET_NUMHOOKS,
    NF_INET_INGRESS = NF_INET_NUMHOOKS,
};
enum nf_dev_hooks {
    NF_NETDEV_INGRESS,
    NF_NETDEV_EGRESS,
    NF_NETDEV_NUMHOOKS
};
...
    sendto_ipv4_ip_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
...
...
    sendto_ipv4_ip_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
...
static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
{
    struct inet_sock *inet = inet_sk(sk);
    struct net *net = sock_net(sk);
    struct ipcm_cookie ipc;
...
    if (hdrincl)
        err = raw_send_hdrinc(sk, &fl4, msg, len,
                      &rt, msg->msg_flags, &ipc.sockc);
...
}
static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
               struct msghdr *msg, size_t length,
               struct rtable **rtp, unsigned int flags,
               const struct sockcm_cookie *sockc)
{
...
    err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
              net, sk, skb, NULL, rt->dst.dev,
              dst_output);
...
}
static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
{
    struct inet_sock *inet = inet_sk(sk);
    struct net *net = sock_net(sk);
    struct ipcm_cookie ipc;
...
    if (hdrincl)
        err = raw_send_hdrinc(sk, &fl4, msg, len,
                      &rt, msg->msg_flags, &ipc.sockc);
...
}
static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
               struct msghdr *msg, size_t length,
               struct rtable **rtp, unsigned int flags,
               const struct sockcm_cookie *sockc)
{
...
    err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
              net, sk, skb, NULL, rt->dst.dev,
              dst_output);
...
}
int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
    struct net_device *dev = skb_dst(skb)->dev, *indev = skb->dev;
 
    IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);
 
    skb->dev = dev;
    skb->protocol = htons(ETH_P_IP);
 
    return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
                net, sk, skb, indev, dev,
                ip_finish_output,
                !(IPCB(skb)->flags & IPSKB_REROUTED));
}
EXPORT_SYMBOL(ip_output);
int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
    struct net_device *dev = skb_dst(skb)->dev, *indev = skb->dev;
 
    IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);
 
    skb->dev = dev;
    skb->protocol = htons(ETH_P_IP);
 
    return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
                net, sk, skb, indev, dev,
                ip_finish_output,
                !(IPCB(skb)->flags & IPSKB_REROUTED));
}
EXPORT_SYMBOL(ip_output);
/*
 * IP receive entry point
 */
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
       struct net_device *orig_dev)
{
    struct net *net = dev_net(dev);
 
    skb = ip_rcv_core(skb, net);
    if (skb == NULL)
        return NET_RX_DROP;
 
    return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
               net, NULL, skb, dev, NULL,
               ip_rcv_finish);
}
/*
 * IP receive entry point
 */
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
       struct net_device *orig_dev)
{
    struct net *net = dev_net(dev);
 
    skb = ip_rcv_core(skb, net);
    if (skb == NULL)
        return NET_RX_DROP;
 
    return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
               net, NULL, skb, dev, NULL,
               ip_rcv_finish);
}
/*
 *  Deliver IP Packets to the higher protocol layers.
 */
int ip_local_deliver(struct sk_buff *skb)
{
    /*
     *  Reassemble IP fragments.
     */
    struct net *net = dev_net(skb->dev);
 
    if (ip_is_fragment(ip_hdr(skb))) {
        if (ip_defrag(net, skb, IP_DEFRAG_LOCAL_DELIVER))
            return 0;
    }
 
    return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,
               net, NULL, skb, skb->dev, NULL,
               ip_local_deliver_finish);
}
EXPORT_SYMBOL(ip_local_deliver);
/*
 *  Deliver IP Packets to the higher protocol layers.
 */
int ip_local_deliver(struct sk_buff *skb)
{
    /*
     *  Reassemble IP fragments.
     */
    struct net *net = dev_net(skb->dev);
 
    if (ip_is_fragment(ip_hdr(skb))) {
        if (ip_defrag(net, skb, IP_DEFRAG_LOCAL_DELIVER))
            return 0;
    }
 
    return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,
               net, NULL, skb, skb->dev, NULL,
               ip_local_deliver_finish);
}
EXPORT_SYMBOL(ip_local_deliver);
int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state,
         const struct nf_hook_entries *e, unsigned int s)
{
    unsigned int verdict;
    int ret;
 
    for (; s < e->num_hook_entries; s++) {
        verdict = nf_hook_entry_hookfn(&e->hooks[s], skb, state);
        switch (verdict & NF_VERDICT_MASK) {
        case NF_ACCEPT:
            break;
        case NF_DROP:
            kfree_skb_reason(skb,
                     SKB_DROP_REASON_NETFILTER_DROP);
            ret = NF_DROP_GETERR(verdict);
            if (ret == 0)
                ret = -EPERM;
            return ret;
        case NF_QUEUE:
            ret = nf_queue(skb, state, s, verdict);
            if (ret == 1)
                continue;
            return ret;
        default:
            /* Implicit handling for NF_STOLEN, as well as any other
             * non conventional verdicts.
             */ return 0;
        }
    }
 
    return 1;
}
EXPORT_SYMBOL(nf_hook_slow);
int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state,
         const struct nf_hook_entries *e, unsigned int s)
{
    unsigned int verdict;
    int ret;
 
    for (; s < e->num_hook_entries; s++) {
        verdict = nf_hook_entry_hookfn(&e->hooks[s], skb, state);
        switch (verdict & NF_VERDICT_MASK) {
        case NF_ACCEPT:
            break;
        case NF_DROP:
            kfree_skb_reason(skb,
                     SKB_DROP_REASON_NETFILTER_DROP);
            ret = NF_DROP_GETERR(verdict);
            if (ret == 0)
                ret = -EPERM;
            return ret;
        case NF_QUEUE:
            ret = nf_queue(skb, state, s, verdict);
            if (ret == 1)
                continue;
            return ret;
        default:
            /* Implicit handling for NF_STOLEN, as well as any other
             * non conventional verdicts.

[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

收藏
免费 6
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回