【网络入侵检测】基于源码分析Suricata的IP分片重组

news2025/7/16 15:33:56

【作者主页】只道当时是寻常

【专栏介绍】Suricata入侵检测。专注网络、主机安全,欢迎关注与评论。

目录

目录

1.概要

2. 配置信息

2.1 名词介绍

2.2 defrag 配置

3. 代码实现

3.1 配置解析

3.1.1 defrag配置

3.1.2 主机系统策略

3.2 分片重组模块

3.2.1分片追踪器(DefragTracker )

3.2.2 获取分片追踪器(DefragGetTracker)

3.2.3 分片插入(DefragInsertFrag)

3.2.3.1 分片冲突策略

3.2.3.1.1 DEFRAG_POLICY_BSD

3.2.3.1.2 DEFRAG_POLICY_BSD_RIGHT

3.2.3.1.3 DEFRAG_POLICY_LINUX

3.2.3.1.4 DEFRAG_POLICY_WINDOWS

3.2.3.1.5 DEFRAG_POLICY_SOLARIS

3.2.3.2 获取主机系统策略


1.概要

👋 本文针对网络入侵检测中 Suricata 的 IP 分片重组模块展开源码分析。IP 分片重组是网络数据传输关键,对 NIDS 执行 DPI 至关重要。Suricata 作为开源高性能网络威胁检测引擎,通过分析其 IP 分片重组模块源码,解析该模块处理 IP 分片数据、监视重组数据包,并将重组数据传递给后续模块的过程,以确保检测准确完整。文中主要包含Suricata中分片配置、分片重组以及解决重组冲突信息。

2. 配置信息

2.1 名词介绍

IP 分片

IP 分片是因不同网络链路 MTU 不同,当 IP 数据包大小超链路 MTU 时,将其拆成多个小数据包的机制。

IP 分片流

单个 IP 报文经分片处理后,由其形成的多个分片所构成的传输流,被称为 IP 分片流,其中各分片报文的 IP 包头 ID 标识保持一致。

分片重组引擎

Suricata 中专门用于对 IP 分片报文实施内部重组的功能模块。

2.2 defrag 配置

在 Suricata 的配置文件中,defrag 关键字包含的配置信息用于配置分片重组引擎。其默认配置信息如下:

defrag:
  memcap: 32mb
  # memcap-policy: ignore
  hash-size: 65536
  trackers: 65535 # number of defragmented flows to follow
  max-frags: 65535 # number of fragments to keep (higher than trackers)
  prealloc: yes
  timeout: 60

# Enable defrag per host settings
#  host-config:
#
#    - dmz:
#        timeout: 30
#        address: [192.168.1.0/24, 127.0.0.0/8, 1.1.1.0/24, 2.2.2.0/24, "1.1.1.1", "2.2.2.2", "::1"]
#
#    - lan:
#        timeout: 45
#        address:
#          - 192.168.0.0/24
#          - 192.168.10.0/24
#          - 172.16.14.0/24
  • memcap:设置分片重组引擎可申请的最大使用内存,这限制了碎片整理过程中内存的使用量。

  • memcap-policy:[drop-packet/pass-packet/reject/ignore(默认)],适用于IPS模式,例如在分片重组时空间申请失败等因素导致无法重组该分片时,对该分片采取的策略。

  • hash-size:设置哈希表中能存储的元素的上限。

  • trackers:设置预先申请的分片追踪器(DefragTracker )的数量,一般和 prealloc 配合使用,prealloc yes,则申请执行数量的DefragTracker 存储在defragtracker_spare_q结构中,否则不申请。

  • max-frags:设置最大分片数量。

  • prealloc:见 "trackers" 字段解释。

  • timeout:设置分片重组超时时间,默认 60s。

  • host-config: 针对特定主机单独设置分片重组策略。

    • timeout:设置分片重组超时时间,默认 60s。

    • address:设置主机IP地址。

3. 代码实现

3.1 配置解析

3.1.1 defrag配置

DefragInit 函数是Suricata 解析配置文件中 Defrag 配置信息的入口函数,DefragPolicyLoadFromConfig 函数用于解析 defrag.host-config 相关的配置信息,DefragInitConfig 函数用于解析其它配置信息。

3.1.2 主机系统策略

在Suricata中 SCHInfoLoadFromConfig 函数主要用于加载配置文件中 host-os-policy 相关的配置信息。

host-os-policy 的配置格式如下所示,分号前面为操作系统类型,后面列表中包含主机的IP地址,不同的IP地址可使用逗号分割。Suricata依据用户配置的IP地址区分主机系统类型。

# Host specific policies for defragmentation and TCP stream
# reassembly. The host OS lookup is done using a radix tree, just
# like a routing table so the most specific entry matches.
host-os-policy:
    windows: [0.0.0.0/0]
    bsd: []
    bsd_right: []
    old_linux: []
    linux: [10.0.0.0/8, 192.168.1.100, "8762:2352:6241:7245:E000:0000:0000:0000"]
    old_solaris: []solaris: ["::1"]
    hpux10: []
    hpux11: []
    irix: []
    macos: []
    vista: []
    windows2k3: []

下面sc_hinfo_os_policy_map 结构用于将配置信息中主机类型转换成宏变量。

/** Enum map for the various OS flavours */
SCEnumCharMap sc_hinfo_os_policy_map[ ] = {
    { "none",        OS_POLICY_NONE },
    { "bsd",         OS_POLICY_BSD },
    { "bsd-right",   OS_POLICY_BSD_RIGHT },
    { "old-linux",   OS_POLICY_OLD_LINUX },
    { "linux",       OS_POLICY_LINUX },
    { "old-solaris", OS_POLICY_OLD_SOLARIS },
    { "solaris",     OS_POLICY_SOLARIS },
    { "hpux10",      OS_POLICY_HPUX10 },
    { "hpux11",      OS_POLICY_HPUX11 },
    { "irix",        OS_POLICY_IRIX },
    { "macos",       OS_POLICY_MACOS },
    { "windows",     OS_POLICY_WINDOWS },
    { "vista",       OS_POLICY_VISTA },
    { "windows2k3",  OS_POLICY_WINDOWS2K3 },
    { NULL,          -1 },
};

3.2 分片重组模块

Defrag 函数作为分片重组引擎的入口函数,在网络数据包解码过程中发挥着关键作用。当解码进入网络层时,会根据数据包的 IP 版本调用 DecodeIPV4 DecodeIPV6 函数进行处理(有关具体的解码流程以及这两个函数的详细介绍,可查阅 【网络入侵检测】基于源码分析Suricata的解码模块)。在调用这两个解码函数期间,Defrag 函数可能会被触发。IPv4 报文触发的条件是:IP 包头中的偏移字段值大于零,或者更多分片标志位(MF)被设置为 1。只有满足这些条件,才会调用 Defrag 函数对分片数据包进行重组操作。IPv6不是本文重点,本文不再关注。

下面是 Suricata 源码中对于IPv4包头是否分片判断:

int DecodeIPV4(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
        const uint8_t *pkt, uint16_t len)
{
    ... ...
    
     /* If a fragment, pass off for re-assembly. */
    if (unlikely(IPV4_GET_IPOFFSET(p) > 0 || IPV4_GET_MF(p) == 1)) {
        Packet *rp = Defrag(tv, dtv, p);
        if (rp != NULL) {
            PacketEnqueueNoLock(&tv->decode_pq, rp);
        }
        p->flags |= PKT_IS_FRAGMENT;
        return TM_ECODE_OK;
    }
    
    ... ...
}

下面是分片重组模块四个主要函数:

  • Defrag:分片重组入口函数。

  • DefragGetTracker:获取分片追踪器(DefragTracker )。

  • DefragInsertFrag:执行分片重组操作。

  • DefragTrackerRelease:执行 DefragTracker 结构引用计数减一操作,并不是释放该对象。

下面是分片重组模块整体设计:

3.2.1分片追踪器(DefragTracker )

在 Suricata 中 DefragTracker 这个结构体尤为重要,该结构体用于跟踪和管理 IP 分片重组的过程。它的主要作用是记录与特定 IP 分片流相关的信息,以便在接收到所有分片后能够正确地重组原始数据包。下面是该结构体的结构:

/**
 * A defragmentation tracker.  Used to track fragments that make up a
 * single packet.
 */
typedef struct DefragTracker_ {
    SCMutex lock; /**< Mutex for locking list operations on
                           * this tracker. */

    uint16_t vlan_id[VLAN_MAX_LAYERS]; /**< VLAN ID tracker applies to. */

    uint32_t id; /**< IP ID for this tracker.  32 bits for IPv6, 16
                  * for IPv4. */

    uint8_t proto; /**< IP protocol for this tracker. */

    uint8_t policy; /**< Reassembly policy this tracker will use. */

    uint8_t af; /**< Address family for this tracker, AF_INET or
                 * AF_INET6. */

    uint8_t seen_last; /**< Has this tracker seen the last fragment? */

    uint8_t remove; /**< remove */

    Address src_addr; /**< Source address for this tracker. */
    Address dst_addr; /**< Destination address for this tracker. */

    int datalink;           /**< datalink for reassembled packet, set by first fragment */
    SCTime_t timeout;       /**< When this tracker will timeout. */
    uint32_t host_timeout;  /**< Host timeout, statically assigned from the yaml */

    /** use cnt, reference counter */
    SC_ATOMIC_DECLARE(unsigned int, use_cnt);

    struct IP_FRAGMENTS fragment_tree;

    /** hash pointers, protected by hash row mutex/spin */
    struct DefragTracker_ *hnext;
    struct DefragTracker_ *hprev;

    /** list pointers, protected by tracker-queue mutex/spin */
    struct DefragTracker_ *lnext;
    struct DefragTracker_ *lprev;
} DefragTracker;

下面我们挑选几个主要的变量进行解释:

  • vlan_id:记录 VLAN ID,用于标识分片所属的 VLAN 网络。

  • id:IP 分片的标识符(IP ID),用于区分不同的分片流。

  • proto: IP 协议类型(如 TCP、UDP 等),用于确定分片所属的协议。

  • policy:分片重组策略,不同的主机系统在遇到重组冲突时遵循的重组策略是不同的。

  • af:地址族,标识是 IPv4 (AF_INET) 还是 IPv6 (AF_INET6)。

  • seen_last:标记是否已经接收到最后一个分片。

  • remove:标记该跟踪器是否需要被移除。

  • src_addr:记录分片流的源地址。

  • dst_addr:记录分片流的目的地址。

  • datalink:数据链路层类型,由第一个分片设置。

  • timeout:超时时间,用于确定何时丢弃未完成的分片流。

  • fragment_tree: 用于存储分片的红黑树,按分片偏移量排序。

  • hnext hprev: 哈希表指针,用于在哈希表中链接多个跟踪器。

  • lnext lprev: 链表指针,用于在跟踪器队列中链接多个跟踪器。

👋 综上所述,我们能够明确,每个不同的 IP 分片流都与唯一的 DefragTracker 结构相对应。DefragTracker 结构借助红黑树,按照偏移顺序对当前 IP 分片流中的各个数据包进行存储。后续的分片存储、数据包重组等操作,均依赖这一结构体展开。

3.2.2 获取分片追踪器(DefragGetTracker)

在 Defrag 函数对分片包完成统计操作后,会调用 DefragGetTracker 函数,以查找当前分片报文是否存在对应的 DefragTracker 结构。若存在,则返回该 DefragTracker 结构;若不存在,则创建一个新的 DefragTracker 结构,用于存储该分片包所在分片流的相关信息。具体流程如下图所示:

3.2.3 分片插入(DefragInsertFrag)

调用 DefragGetTracker 后已经获取当前分片关联的 DefragTracker 对象,接着根据当前分片的偏移值,将分片插入到分片队列中。下面是分片插入操作流程图:

3.2.3.1 分片冲突策略

分片在重组的过程中可能出现新的分片与旧的分片出现冲突或者覆盖的现象,如下图所示(参考自TCP IP协议 卷2 第十章):

通过观察上图可以看到分片5与分片1和分片2发生重叠,分片7和分片3发生重叠,分片6与分片4发生重叠。其中分片1、2、3、4先到达目标主机,分片5、6、7后到达主机。

👋 为什么分片在传输过程中出现重叠现象?

在正常的网络通信中,IP分片的“Fragment Offset”字段应确保各分片数据在重组时无重叠。然而,攻击者可以故意设置多个分片的偏移量和长度,使它们在重组时发生重叠。

例如,攻击者可能发送两个分片:

  • 分片A:偏移量为0,长度为100字节

  • 分片B:偏移量为50,长度为100字节

在这种情况下,分片B的前50字节将覆盖分片A的后50字节,导致数据重叠。

👋 发生分片重叠时应该怎么处理?

不同操作系统和网络设备在处理重叠分片时的行为可能不同:

  • 某些系统可能保留第一个到达的分片数据,忽略后续重叠部分。

  • 另一些系统可能使用后到达的分片数据覆盖先前的内容。

  • 还有的系统可能在检测到重叠时直接丢弃整个数据包。

Suricata 支持多种分片重组策略来适配多种不同的操作系统,而在配置文件中 host-os-policy 配置可指定目标主机系统类型。

3.2.3.1.1 DEFRAG_POLICY_BSD

该策略遵循左侧优先法,即优先保留左侧偏移较小的分片数据。如果新分片与旧分片完全重合的情况下,优先保留旧分片。如果新分片左侧偏移与旧分片左侧偏移相等的情况下,优先裁剪新分片数据。

该部分逻辑代码实现如下所示:

/**
 * Insert a new IPv4/IPv6 fragment into a tracker.
 *
 * \todo Allocate packet buffers from a pool.
 */
static Packet *
DefragInsertFrag(ThreadVars *tv, DecodeThreadVars *dtv, DefragTracker *tracker, Packet *p)
{
    ... ...
    case DEFRAG_POLICY_BSD:
    if (frag_offset < prev->offset + prev->data_len) {
        if (prev->offset <= frag_offset) {  
            /* We prefer the data from the previous
             * fragment, so trim off the data in the new
             * fragment that exists in the previous
             * fragment. */
            uint16_t prev_end = prev->offset + prev->data_len;
            if (prev_end > frag_end) { 
                /* Just skip. */
                /* TODO: Set overlap flag. */
                goto done;
            }
            ltrim = prev_end - frag_offset;

            if ((next != NULL) && (frag_end > next->offset)) {
                next->ltrim = frag_end - next->offset;
            }

            goto insert;
        }

        /* If the end of this fragment overlaps the start
         * of the previous fragment, then trim up the
         * start of previous fragment so this fragment is
         * used.
         *
         * See:
         * DefragBsdSubsequentOverlapsStartOfOriginal.
         */
        if (frag_offset <= prev->offset && frag_end > prev->offset + prev->ltrim) {
            uint16_t prev_ltrim = frag_end - prev->offset;
            if (prev_ltrim > prev->ltrim) {
                prev->ltrim = prev_ltrim;
            }
        }

        if ((next != NULL) && (frag_end > next->offset)) {
            next->ltrim = frag_end - next->offset;
        }

        goto insert;
    }
    break;
    ... ...
}
3.2.3.1.2 DEFRAG_POLICY_BSD_RIGHT

该策略遵循右侧覆盖策略,即右边的分片覆盖前面的分片数据。

3.2.3.1.3 DEFRAG_POLICY_LINUX

该策略遵循左侧优先法,即优先保留左侧偏移较小的分片数据。如果新分片与旧分片完全重合的情况下,优先保留新分片。如果新分片左侧偏移与旧分片左侧偏移相等的情况下,优先裁剪旧分片数据。

该部分逻辑代码实现如下所示:

/**
 * Insert a new IPv4/IPv6 fragment into a tracker.
 *
 * \todo Allocate packet buffers from a pool.
 */
static Packet *
DefragInsertFrag(ThreadVars *tv, DecodeThreadVars *dtv, DefragTracker *tracker, Packet *p)
{
    ... ...
    case DEFRAG_POLICY_LINUX:
    /* Check if new fragment overlaps the end of previous
     * fragment, if it does, trim the new fragment.
     *
     * Old: AAAAAAAA AAAAAAAA AAAAAAAA
     * New:          BBBBBBBB BBBBBBBB BBBBBBBB
     * Res: AAAAAAAA AAAAAAAA AAAAAAAA BBBBBBBB
     */
    if (prev->offset + prev->ltrim < frag_offset + ltrim &&
            prev->offset + prev->data_len > frag_offset + ltrim) {
        ltrim += prev->offset + prev->data_len - frag_offset;
    }

    /* Check if new fragment overlaps the beginning of
     * previous fragment, if it does, tim the previous
     * fragment.
     *
     * Old:          AAAAAAAA AAAAAAAA
     * New: BBBBBBBB BBBBBBBB BBBBBBBB
     * Res: BBBBBBBB BBBBBBBB BBBBBBBB
     */
    if (frag_offset + ltrim < prev->offset + prev->ltrim &&
            frag_end > prev->offset + prev->ltrim) {
        prev->ltrim += frag_end - (prev->offset + prev->ltrim);
        goto insert;
    }

    /* If the new fragment completely overlaps the
     * previous fragment, mark the previous to be
     * skipped. Re-assembly would succeed without doing
     * this, but this will prevent the bytes from being
     * copied just to be overwritten. */
    if (frag_offset + ltrim <= prev->offset + prev->ltrim &&
            frag_end >= prev->offset + prev->data_len) {
        prev->skip = 1;
        goto insert;
    }

    break;
    ... ...
}
3.2.3.1.4 DEFRAG_POLICY_WINDOWS

该策略遵循"先到先得"原则,即尽量保全先到来的数据包分片。除非新分片包含且不完全重合于旧分片的情况下,保留新分片。如果新分片左侧偏移与旧分片左侧偏移相等的情况下,优先裁剪新分片数据。

该部分逻辑代码实现如下所示:

/**
 * Insert a new IPv4/IPv6 fragment into a tracker.
 *
 * \todo Allocate packet buffers from a pool.
 */
static Packet *
DefragInsertFrag(ThreadVars *tv, DecodeThreadVars *dtv, DefragTracker *tracker, Packet *p)
{
    ... ...
   case DEFRAG_POLICY_WINDOWS:
    /* If new fragment fits inside a previous fragment, drop it. */
    if (frag_offset + ltrim >= prev->offset + ltrim &&
            frag_end <= prev->offset + prev->data_len) {
        goto done;
    }

    /* If new fragment starts before and ends after
     * previous fragment, drop the previous fragment. */ 
    if (frag_offset + ltrim < prev->offset + ltrim &&
            frag_end > prev->offset + prev->data_len) {
        prev->skip = 1;
        goto insert;
    }

    /* Check if new fragment overlaps the end of previous
     * fragment, if it does, trim the new fragment.
     *
     * Old: AAAAAAAA AAAAAAAA AAAAAAAA
     * New:          BBBBBBBB BBBBBBBB BBBBBBBB
     * Res: AAAAAAAA AAAAAAAA AAAAAAAA BBBBBBBB
     */
    if (frag_offset + ltrim > prev->offset + prev->ltrim &&
            frag_offset + ltrim < prev->offset + prev->data_len) {  
        ltrim += prev->offset + prev->data_len - frag_offset; 
        goto insert;
    }

    /* If new fragment starts at same offset as an
     * existing fragment, but ends after it, trim the new
     * fragment. */
    if (frag_offset + ltrim == prev->offset + ltrim &&
            frag_end > prev->offset + prev->data_len) {
        ltrim += prev->offset + prev->data_len - frag_offset;
        goto insert;
    }
    break;
    ... ...
}
3.2.3.1.5 DEFRAG_POLICY_SOLARIS

该策略遵循左侧优先法,即优先保留左侧偏移较小的分片数据。如果新分片与旧分片完全重合的情况下,优先保留旧分片。如果新分片左侧偏移与旧分片左侧偏移相等的情况下,优先裁剪新分片数据。

该部分逻辑代码实现如下所示:

/**
 * Insert a new IPv4/IPv6 fragment into a tracker.
 *
 * \todo Allocate packet buffers from a pool.
 */
static Packet *
DefragInsertFrag(ThreadVars *tv, DecodeThreadVars *dtv, DefragTracker *tracker, Packet *p)
{
    ... ...
    case DEFRAG_POLICY_SOLARIS:
    if (frag_offset < prev->offset + prev->data_len) {
        if (frag_offset >= prev->offset) {
            ltrim = prev->offset + prev->data_len - frag_offset;
        }
        if ((frag_offset < prev->offset) &&
            (frag_end >= prev->offset + prev->data_len)) {
            prev->skip = 1;
        }
        goto insert;
    }
    break;
    ... ...
}
3.2.3.2 获取主机系统策略

在Suricata的配置文件中通过host-os-policy关键字已经配置好主机系统策略,而在初始化DefragTracker对象时,即调用DefragTrackerInit函数时,会通过当前分片的IP地址,结合host-os-policy配置来选择重组策略。

调用逻辑如下所示:

在 DefragGetOsPolicy 函数中,Suricata会将不同的系统策略归为7类。注意,如果未配置,默认重组策略为DEFRAG_POLICY_BSD。

系统策略

分片重组策略

DEFRAG_POLICY_BSD

OS_POLICY_BSD

OS_POLICY_HPUX10

OS_POLICY_IRIX

DEFRAG_POLICY_BSD_RIGHT

OS_POLICY_BSD_RIGHT

DEFRAG_POLICY_LINUX

OS_POLICY_OLD_LINUX

OS_POLICY_LINUX

DEFRAG_POLICY_FIRST

OS_POLICY_OLD_SOLARIS

OS_POLICY_HPUX11

OS_POLICY_MACOS

OS_POLICY_FIRST

DEFRAG_POLICY_SOLARIS

OS_POLICY_SOLARIS

DEFRAG_POLICY_WINDOWS

OS_POLICY_WINDOWS

OS_POLICY_VISTA

OS_POLICY_WINDOWS2K3

DEFRAG_POLICY_LAST

OS_POLICY_LAST

分片重组策略结构如下所示:

/** Fragment reassembly policies. */
enum defrag_policies {
    DEFRAG_POLICY_FIRST = 1,
    DEFRAG_POLICY_LAST,
    DEFRAG_POLICY_BSD,
    DEFRAG_POLICY_BSD_RIGHT,
    DEFRAG_POLICY_LINUX,
    DEFRAG_POLICY_WINDOWS,
    DEFRAG_POLICY_SOLARIS,

    DEFRAG_POLICY_DEFAULT = DEFRAG_POLICY_BSD,
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2375671.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LVGL简易计算器实战

文章目录 &#x1f4c1; 文件结构建议&#x1f539; eval.h 表达式求值头文件&#x1f539; eval.c 表达式求值实现文件&#xff08;带详细注释&#xff09;&#x1f539; ui.h 界面头文件&#x1f539; ui.c 界面实现文件&#x1f539; main.c 主函数入口✅ 总结 项目效果&…

代码随想录算法训练营第60期第三十四天打卡

大家好&#xff0c;我们今天的内容依旧是贪心算法&#xff0c;我们上次的题目主要是围绕多维问题&#xff0c;那种时候我们需要分开讨论&#xff0c;不要一起并发进行很容易顾此失彼&#xff0c;那么我们今天的问题主要是重叠区间问题&#xff0c;又是一种全新的贪心算法思想&a…

关于IDE的相关知识之二【插件推荐】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于ide插件推荐的相关内容&#xff01…

Axure RP9:列表新增

文章目录 列表新增思路新增按钮操作说明保存新增交互设置列表新增 思路 利用中继器新增行实现列表新增功能 新增按钮操作说明 工具栏中添加新增图标及标签,在图标标签基础上添加热区;对热区添加鼠标单击时交互事件,同步插入如下动作:显示/隐藏动作,设置目标元件为新增窗…

06 mysql之DML

一、什么是DML DML 用于操作数据库中的数据。主要命令包括&#xff1a; INSERT&#xff1a;添加数据SELECT&#xff1a;查询数据UPDATE&#xff1a;修改数据DELETE&#xff1a;删除数据 二、插入数据&#xff08;INSERT&#xff09; 2.1 插入单条记录 -- 插入学生记录&…

【最新版】likeshop连锁点餐系统-PHP版+uniapp前端全开源

一.系统介绍 likeshop外卖点餐系统适用于茶饮类的外卖点餐场景&#xff0c;搭建自己的一点点、奈雪、喜茶点餐系统。 系统基于总部多门店的连锁模式&#xff0c;拥有门店独立管理后台&#xff0c;支持总部定价和门店定价LBS定位点餐&#xff0c;可堂食可外卖。无论运营还是二开…

纯Java实现反向传播算法:零依赖神经网络实战

在深度学习框架泛滥的今天,理解算法底层实现变得愈发重要。反向传播(Backpropagation)作为神经网络训练的基石算法,其实现往往被各种框架封装。本文将突破常规,仅用Java标准库实现完整BP算法,帮助开发者: 1) 深入理解BP数学原理。2) 掌握面向对象的神经网络实现。3) 构建可…

海纳思(Hi3798MV300)机顶盒遇到海思摄像头

海纳思机顶盒遇到海思摄像头&#xff0c;正好家里有个海思Hi3516的摄像头模组开发板&#xff0c;结合机顶盒来做个录像。 准备工作 海纳斯机顶盒摄像机模组两根网线、两个电源、路由器一块64G固态硬盘 摄像机模组和机顶盒都接入路由器的LAN口&#xff0c;确保网络正常通信。 …

Axure应用交互设计:表格跟随菜单移动效果(超长表单)

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢!本文如有帮助请订阅 Axure产品经理精品视频课已登录CSDN可点击学习https://edu.csdn.net/course/detail/40420 课程主题:表格跟随菜单移动 主要内容:表格交互设计、动态面板嵌套、拖动时事件、移动动作 应用场景…

7系列 之 I/O标准和终端技术

背景 《ug471_7Series_SelectIO.pdf》介绍了Xilinx 7 系列 SelectIO 的输入/输出特性及逻辑资源的相关内容。 第 1 章《SelectIO Resources》介绍了输出驱动器和输入接收器的电气特性&#xff0c;并通过大量实例解析了各类标准接口的实现。 第 2 章《SelectIO Logic Resource…

github 上的 CI/CD 的尝试

效果 步骤 新建仓库设置仓库的 page 新建一个 vite 的项目&#xff0c;改一下 vite.config.js 中的 base 工作流 在项目的根目录下新建一个 .github/workflows/ci.yml 文件&#xff0c;然后编辑一下内容 name: Build & Deploy Vue 3 Appon:push:branches: [main]permi…

yup 使用 3 - 利用 meta 实现表单字段与表格列的统一结构配置(适配 React Table)

yup 使用 3 - 利用 meta 实现表单字段与表格列的统一结构配置&#xff08;适配 React Table&#xff09; Categories: Tools Last edited time: May 11, 2025 7:45 PM Status: Done Tags: form validation, schema design, yup 本文介绍如何通过 Yup 的 meta() 字段&#xff0…

【OpenCV】imread函数的简单分析

目录 1.imread()1.1 imread()1.2 imread_()1.2.1 查找解码器&#xff08;findDecoder&#xff09;1.2.2 读取数据头&#xff08;JpegDecoder-->readHeader&#xff09;1.2.2.1 初始化错误信息&#xff08;jpeg_std_error&#xff09;1.2.2.2 创建jpeg解压缩对象&#xff08;…

【Linux实践系列】:进程间通信:万字详解共享内存实现通信

&#x1f525; 本文专栏&#xff1a;Linux Linux实践项目 &#x1f338;作者主页&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客励志语录&#xff1a; 人生就像一场马拉松&#xff0c;重要的不是起点&#xff0c;而是坚持到终点的勇气 ★★★ 本文前置知识&#xff1a; …

【笔记】BCEWithLogitsLoss

工作原理 BCEWithLogitsLoss 是 PyTorch 中的一个损失函数&#xff0c;用于二分类问题。 它结合了 Sigmoid 激活函数和二元交叉熵&#xff08;Binary Cross Entropy, BCE&#xff09;损失在一个类中。 这不仅简化了代码&#xff0c;而且通过数值稳定性优化提高了模型训练的效…

关于Go语言的开发环境的搭建

1.Go开发环境的搭建 其实对于GO语言的这个开发环境的搭建的过程&#xff0c;类似于java的开发环境搭建&#xff0c;我们都是需要去安装这个开发工具包的&#xff0c;也就是俗称的这个SDK&#xff0c;他是对于我们的程序进行编译的&#xff0c;不然我们写的这个代码也是跑不起来…

Flutter PIP 插件 ---- 为iOS 重构PipController, Demo界面,更好的体验

接上文 Flutter PIP 插件 ---- 新增PipActivity&#xff0c;Android 11以下支持自动进入PIP Mode 项目地址 PIP&#xff0c; pub.dev也已经同步发布 pip 0.0.3&#xff0c;你的加星和点赞&#xff0c;将是我继续改进最大的动力 在之前的界面设计中&#xff0c;还原动画等体验一…

数据库管理-第325期 ADG Failover后该做啥(20250513)

数据库管理325期 2025-05-13 数据库管理-第325期 ADG Failover后该做啥&#xff08;20250513&#xff09;1 故障处置2 恢复原主库3 其他操作总结 数据库管理-第325期 ADG Failover后该做啥&#xff08;20250513&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&a…

SQLi-Labs 第21-24关

Less-21 http://127.0.0.1/sqli-labs/Less-21/ 1&#xff0c;抓个请求包看看 分析分析cookie被base64URL编码了&#xff0c;解码之后就是admin 2&#xff0c;那么这个网站的漏洞利用方式也是和Less-20关一样的&#xff0c;只是攻击语句要先base64编码&#xff0c;再URL编码&…

PVE WIN10直通无线网卡蓝牙

在 Proxmox VE (PVE) 中直通 Intel AC3165 无线网卡的 **蓝牙模块**&#xff08;通常属于 USB 设备&#xff0c;而非 PCIe 设备&#xff09;需要特殊处理&#xff0c;因为它的蓝牙部分通常通过 USB 连接&#xff0c;而 Wi-Fi 部分才是 PCIe 设备。以下是详细步骤&#xff1a; …