Nftables栈溢出漏洞(CVE-2022-1015)复现
背景介绍
Nftables
Nftables 是一个基于内核的包过滤框架,用于 Linux
操作系统中的网络安全和防火墙功能。nftables
的设计目标是提供一种更简单、更灵活和更高效的方式来管理网络数据包的流量。
钩子点(Hook Point)
钩子点的作用是拦截数据包,然后对数据包进行修改,比较,丢弃和放行等操作。
{width=“5.833333333333333in”
height=“3.0175185914260716in”}
// include/uapi/linux/netfilter_ipv4.h#define NF_IP_PRE_ROUTING 0 /* After promisc drops, checksum checks. */
#define NF_IP_LOCAL_IN 1 /* If the packet is destined for this box. */
#define NF_IP_FORWARD 2 /* If the packet is destined for another interface. */
#define NF_IP_LOCAL_OUT 3 /* Packets coming from a local process. */
#define NF_IP_POST_ROUTING 4 /* Packets about to hit the wire. */
#define NF_IP_NUMHOOKS 5
Nftables的架构
Nftables由四部分组成
-
table(表):用于指定网络协议的类型,如ip,ip6,arp等
-
chains(链):用于指定流量的类型,如流入的流量或者是流出的流量并可以指定网络接口,如本地回环接口或者以太网接口等。
-
rules(规则):规则是用于过滤数据包所依据的规则,例如检查协议、来源、目的地、端口等规则。
-
express(表达式):表达式则是具体的操作。
图片来源于https://blog.dbouman.nl/2022/04/02/How-The-Tables-Have-Turned-CVE-2022-1015-1016/
使用非常形象的图描述,如下
{width=“5.833333333333333in”
height=“3.905547900262467in”}
表达式(express)
表达式是对一个数据包具体的操作,这里大致介绍后续需要用到的表达式。
nft_payload
nft_payload用于将数据包的值拷贝到寄存器中
struct nft_payload {enum nft_payload_bases base:8;u8 offset;u8 len;u8 dreg;
};
-
base:数据包类型
-
offset:数据包起始位置的偏移
-
len:拷贝的长度
-
dreg:目的寄存器
其中base的类型由enum nft_payload_bases指定
/* include/uapi/linux/netfilter/nf_tables.h */
/*** enum nft_payload_bases - nf_tables payload expression offset bases** @NFT_PAYLOAD_LL_HEADER: link layer header* @NFT_PAYLOAD_NETWORK_HEADER: network header* @NFT_PAYLOAD_TRANSPORT_HEADER: transport header* @NFT_PAYLOAD_INNER_HEADER: inner header / payload*/
enum nft_payload_bases {NFT_PAYLOAD_LL_HEADER, //链路层NFT_PAYLOAD_NETWORK_HEADER, //网络层NFT_PAYLOAD_TRANSPORT_HEADER, //传输层NFT_PAYLOAD_INNER_HEADER, //数据包内部
};
下面这个例子则是将传输层的包偏移16个字节的位置,取出两个字节的内容存放到目的寄存器中,该寄存器的编号为2
base = NFT_PAYLOAD_TRANSPORT_HEADER
offset = 16 -> the checksum is 16 bytes away from the start of the TCP header
len = 2 -> the checksum is 2 bytes
dreg = NFT_REG32_02 (the small registers start frrom NFT_REG32_00)
nft_payload_set
nft_payload_set则是与nft_payload相反,该表达式是将指定寄存器的值存放到数据包里面
/* include/net/netfilter/nf_tables_core.h */
struct nft_payload_set {enum nft_payload_bases base:8;u8 offset;u8 len;u8 sreg;u8 csum_type;u8 csum_offset;u8 csum_flags;
};
与nft_payload不同的是多了校验和的可选选项
帮助网安学习,全套资料S信免费领取:
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)
nft_cmp_expr
nft_cmp_expr表达式则是用于比较,通常用于判断数据包的端口号是否是需要符合要求。
struct nft_cmp_expr {struct nft_data data;u8 sreg;u8 len;enum nft_cmp_ops op:8;
};
-
data:用于设置比较的常量值
-
sreg:源寄存器,可以认为是数据包取出的内容
-
len:比较的长度
-
op:比较的操作,具体操作类型如下所示
<!-- -->
/*** enum nft_cmp_ops - nf_tables relational operator** @NFT_CMP_EQ: equal* @NFT_CMP_NEQ: not equal* @NFT_CMP_LT: less than* @NFT_CMP_LTE: less than or equal to* @NFT_CMP_GT: greater than* @NFT_CMP_GTE: greater than or equal to*/
enum nft_cmp_ops {NFT_CMP_EQ,NFT_CMP_NEQ,NFT_CMP_LT,NFT_CMP_LTE,NFT_CMP_GT,NFT_CMP_GTE,
};
nft_bitwise
nft_bitwise用于对数据包进行比特级别的操作。例如移位,掩码设置等。
struct nft_bitwise {u8 sreg;u8 dreg;enum nft_bitwise_ops op:8;u8 len;struct nft_data mask;struct nft_data xor;struct nft_data data;
};
-
sreg:源寄存器
-
dreg:目的寄存器,用于存放最后的结果
-
op:指定具体的比特操作,具体操作如下
<!-- -->
/*** enum nft_bitwise_ops - nf_tables bitwise operations** @NFT_BITWISE_BOOL: mask-and-xor operation used to implement NOT, AND, OR and* XOR boolean operations* @NFT_BITWISE_LSHIFT: left-shift operation* @NFT_BITWISE_RSHIFT: right-shift operation*/
enum nft_bitwise_ops {NFT_BITWISE_BOOL,NFT_BITWISE_LSHIFT,NFT_BITWISE_RSHIFT,
};
-
mask:当op被指定为NFT_BITWISE_BOOL时,sreg的值会与mask中指定的值进行掩码设置操作。并将结果存放到dreg中
-
xor:当op被指定为NFT_BITWISE_BOOL时,sreg的值会与xor中指定的值进行掩码设置操作。并将结果存放到dreg中
-
data:当op被指定为NFT_BITWISE_LSHIFT或NFT_BITWISE_RSHIFT时,data需要被指定移位的数值。
寄存器(register)
在Nftables中是以寄存器作为存储区,用于存放一段连续的内存,现在Nftables版本每个寄存器的值存放4字节数据,而旧版的Nftables的每个寄存器是存放16个字节的数据,为了保持兼容性,4字节的寄存与16字节的寄存器都被保留。寄存器的枚举值如下所示
enum nft_registers {NFT_REG_VERDICT, //判定寄存器NFT_REG_1,NFT_REG_2,NFT_REG_3,NFT_REG_4,__NFT_REG_MAX,NFT_REG32_00 = 8,NFT_REG32_01,NFT_REG32_02,...NFT_REG32_13,NFT_REG32_14,NFT_REG32_15,
};
{width=“5.833333333333333in”
height=“2.037557961504812in”}
其中NFT_REG_VERDICT被称之为判断寄存器,这个寄存器比较特殊,是用于判定每个数据包需要怎么处理。判定的类型如下
-
NFT_CONTINUE:允许数据包通过防火墙
-
NFT_BREAK:跳过剩余的规则表达式
-
NF_DROP:直接丢弃数据包
-
NF_ACCEPT:接收数据包
-
NFT_GOTO:跳转到其他链执行
-
NFT_JUMP:跳转到其他链执行,若其他链将该数据包判定为NFT_CONTINUE则返回当前链
libmnl与libnftnl
由于Nftables处于内核,需要从用户层向内核发送消息去设置需要拦截数据包的属性,人工构造成本较大,因此使用现成的库libmnl与libnftnl
环境搭建
环境版本
-
ubuntu 20.04
-
qemu-system-x86_64 4.2.1
-
Linux-5.17源码
设置编译选项
cd /home/pwn/CVE/CVE-2022-1015/CVE-2022-1015/linux-5.17
sudo gedit .config
#将下列选项设置为y
CONFIG_NF_TABLES=y
CONFIG_NETFILTER_NETLINK=y
CONFIG_USER_NS=y
CONFIG_E1000=y
CONFIG_E1000E=y
make -j32 bzImage #编译#安装依赖库
sudo apt-get install libmnl-dev
sudo apt-get install libnftnl-dev
漏洞验证
若运行exp显示超过边界则代表没有漏洞
{width=“5.833333333333333in”
height=“2.4473501749781277in”}
若exp正常运行则代表漏洞
{width=“5.833333333333333in”
height=“4.2338702974628175in”}
漏洞分析
源码分析
nft_parse_register_load
nft_cmp_expr:op=NFT_CMP_EQ sreg=8
data=IPPROTO_TCP。该表达式是一个比较的表达式,用于比较下标为8的寄存器中的数据是否为TCP的协议。那么如何将下表为8的寄存器转化为内核中寄存器的内存位置,则需要以来下面列举的函数。
nft_parse_register_load函数就是将用户设定的寄存器的下标转化为内核寄存器的下标,然后存储在源寄存器中。
File: net\netfilter\nf_tables_api.c
9325: int nft_parse_register_load(const struct nlattr *attr, u8 *sreg, u32 len)
9326: {
9327: u32 reg;
9328: int err;
9329:
9330: reg = nft_parse_register(attr); //用于提取数据包中的寄存器的下标,并转化为Nftables中寄存器的下标
9331: err = nft_validate_register_load(reg, len); //用于检验寄存器下表的合法性,漏洞点
9332: if (err < 0)
9333: return err;
9334:
9335: *sreg = reg; //然后将寄存器的下标值存储在源寄存器中
9336: return 0;
9337: }
nft_parse_register
nft_parse_register函数用于将用户设置的寄存器下标转化为内核中寄存器的下标。
File: net\netfilter\nf_tables_api.c
9278: static unsigned int nft_parse_register(const struct nlattr *attr)
9279: {
9280: unsigned int reg;
9281:
9282: reg = ntohl(nla_get_be32(attr)); //提取数据包的寄存器下标,比如上述例子为8
9283: switch (reg) {//0 - 4是16字节寄存器
9284: case NFT_REG_VERDICT...NFT_REG_4:
9285: return reg * NFT_REG_SIZE / NFT_REG32_SIZE; //reg * 4
9286: default://由于4字节寄存器起始下标为8,因此要减去起始下标
9287: return reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00; // reg - 4
9288: }
9289: }
nft_validate_register_load
nft_validate_register_load函数则是用于校验下标是否有问题,但是这个检验存在整型溢出的问题。reg是枚举值,而枚举通常会被编译为int类型。len代表数据包的长度。
-
正常情况下:reg = 100,那么套入校验则为100 * 4 + 0x10 = 0x1a0 >
0x50,那么会检验出寄存器下标存在问题 -
漏洞情况:reg =
0xffffffff(int情况下的最大值),那么逃入检验则为0xffffffff * 4 +
0x10 =
0x40000000c,由于int最大值为0xffffffff,那么最高4个比特会被舍弃,那么最后得到的值为0x0000000c,此时0xc
< 0x50,就可以绕过检验。那么绕过检验后就会执行* sreg =
reg,此时reg = 0xffffffff,就会导致*sreg = 0xff
<!-- -->
File: net\netfilter\nf_tables_api.c
9313: static int nft_validate_register_load(enum nft_registers reg, unsigned int len)
9314: {
9315: if (reg < NFT_REG_1 * NFT_REG_SIZE / NFT_REG32_SIZE) // reg < 4则报错
9316: return -EINVAL;
9317: if (len == 0) //长度为0则报错
9318: return -EINVAL;
9319: if (reg * NFT_REG32_SIZE + len > sizeof_field(struct nft_regs, data)) //reg * 4 + len > 0x50则报错,存在整型溢出漏洞
9320: return -ERANGE;
9321:
9322: return 0;
9323: }
nft_do_chain
每一个被拦截的数据包都需要经过链上的表达式进行处理,而链处理的函数则为nft_do_chains,这个函数会提取出相应的表达式,最后调用expr_call_ops_eval函数进行处理。
File: net\netfilter\nf_tables_core.c
197: unsigned int
198: nft_do_chain(struct nft_pktinfo *pkt, void *priv)
199: {...
224: for (; rule < last_rule; rule = nft_rule_next(rule)) {
225: nft_rule_dp_for_each_expr(expr, last, rule) {
226: if (expr->ops == &nft_cmp_fast_ops)
227: nft_cmp_fast_eval(expr, ®s);
228: else if (expr->ops == &nft_bitwise_fast_ops)
229: nft_bitwise_fast_eval(expr, ®s);
230: else if (expr->ops != &nft_payload_fast_ops ||
231: !nft_payload_fast_eval(expr, ®s, pkt))
232: expr_call_ops_eval(expr, ®s, pkt);
233:
234: if (regs.verdict.code != NFT_CONTINUE)
235: break;
236: }...
expr_call_ops_eval
expr_call_ops_eval函数则是根据不同的表达式选择不同的处理函数,例如若该数据包需要经过nft_payload的表达式处理,则会调用nft_payload_eval。
File: net\netfilter\nf_tables_core.c
161: static void expr_call_ops_eval(const struct nft_expr *expr,
162: struct nft_regs *regs,
163: struct nft_pktinfo *pkt)
164: {
165: #ifdef CONFIG_RETPOLINE
166: unsigned long e = (unsigned long)expr->ops->eval;
167: #define X(e, fun) \
168: do { if ((e) == (unsigned long)(fun)) \
169: return fun(expr, regs, pkt); } while (0)
170:
171: X(e, nft_payload_eval);
172: X(e, nft_cmp_eval);
173: X(e, nft_counter_eval);
174: X(e, nft_meta_get_eval);
175: X(e, nft_lookup_eval);
176: X(e, nft_range_eval);
177: X(e, nft_immediate_eval);
178: X(e, nft_byteorder_eval);
179: X(e, nft_dynset_eval);
180: X(e, nft_rt_get_eval);
181: X(e, nft_bitwise_eval);
182: #undef X
183: #endif /* CONFIG_RETPOLINE */
184: expr->ops->eval(expr, regs, pkt);
185: }
nft_payload_eval
这里可以看到regs存放在栈上面,dest这个变量值是通过®s->data[priv->dreg]取出来的,而priv->dreg则是通过上述的nft_parse_register_load函数进行提取的,那么这里就存在一个非常明显的数组越界的漏洞。
File: net\netfilter\nft_payload.c
121: void nft_payload_eval(const struct nft_expr *expr,
122: struct nft_regs *regs,
123: const struct nft_pktinfo *pkt)
124: {
125: const struct nft_payload *priv = nft_expr_priv(expr);
126: const struct sk_buff *skb = pkt->skb;
127: u32 *dest = ®s->data[priv->dreg];...
165: if (skb_copy_bits(skb, offset, dest, priv->len) < 0) //拷贝数据
166: goto err;
167: return;
168: err:
169: regs->verdict.code = NFT_BREAK;
170: }
因此整型溢出结合越界就能够使我们访问到内核栈上的其他数据,如下图所示。
{width=“5.833333333333333in”
height=“6.234479440069991in”}
漏洞利用
漏洞利用分析
现在我们拥有了访问内核栈上其它地址的能力了,想要做到任意代码执行则需要考虑下列几种情况
-
由于返回地址存在在栈上,需要判断数组越界是否能够到达返回地址的位置
-
如何通过数组越界改写返回地址
-
由于需要进行任意代码执行,那么需要用到内核函数,则需要得到内核的程序基地址才能够根据函数偏移地址计算出函数的实际地址
由于表达式都会对寄存器空间进行操作,因此可以使用表达式对内存空间进行读写操作。
nft_bitwise表达式可以控制源寄存器和目的寄存器,那么采用nft_bitwise可以将源寄存器的内容放置到目的寄存器中,因此可以利用nft_bitwise进行越界读,此时需要分析该数组越界读的边界的大小是多少。这里需要注意的是由于len是sreg与dreg共同拥有的,为了dreg不越界,这里的长度最大值只能为0x40而不能为0xff,因为拥有16个寄存器,每个寄存器的值为4个字节,因此16
* 4 = 64 = 0x40
-
上界:(0xffffffff * 4) + 0x40 = 0x40000003c = 0x3c < 0x50 , 0xff
* 4 = 0x3fc;由于可以拷贝0x40个字节的长度,因此0x3fc + 0x40 =
0x43c。 -
下界:(0xfffffff0 * 4 ) + 0x40 = 0x400000000 = 0x0 < 0x50, 0xf0 *
4 = 0x3c0
内核地址泄露
接着查看regs偏移0x3c0处的地址信息,结果发现在该片区域存在一个明显的内核地址,因此若能将这个地址进行泄露,我们就能获取内核的基地址。
{width=“5.833333333333333in”
height=“3.313192257217848in”}
返回地址覆盖
由于需要构建的payload比较长,而我们如果利用nft_wise最多只能写入0x43c -
0x3c0 =
0x7c的长度,是远远不够的,因此对返回地址进行覆盖时不能使用nft_bitwise,而得改用nft_payload。nft_payload需要dreg的下标以及修改的长度len,由于我们只需要考虑一个寄存器的值,因此该寄存器的长度最大可以达到0xff。因此我们可以在地址更低的位置去搜索有无可以覆盖的返回地址。
可以发现在0x360的地址处也有一个内核的代码段地址
{width=“5.833333333333333in”
height=“3.2392869641294837in”}
并且可以发现该函数主要是处理udp包的发送
{width=“5.833333333333333in”
height=“2.555692257217848in”}
为了检验该地址是否能够修改程序的执行流程,可以使用一个方法,将该地址的值修改为非法值并观察内核是否会崩溃,这里将地址的内容修改为0x1122334455667788,接着运行程序。
{width=“5.833333333333333in”
height=“2.0175787401574805in”}
可以看到内核报错的信息显示RIP的地址为刚刚我们修改的地址,因此该地址可以作为被劫持程序执行流程的地址。
{width=“5.833333333333333in”
height=“3.0425896762904636in”}
exp分析
现在我们已经具有了两个利用条件
-
泄露内核的程序基地址
-
找到可以劫持程序执行流程的地址值
地址泄露
利用nft_bitwise泄露地址,这里注意的是在使用nft_bitwise泄露地址时,需要将data值设置为0,这样就不会进行移位而导致我们的内核地址被修改存储,最后将泄露的地址值放置在NFT_REG32_05下标的寄存器中
{width=“5.833333333333333in”
height=“1.047253937007874in”}
接着使用nft_set_payload将udp数据包的值修改为NFT_REG32_05寄存器的值,最后取出udp数据包的值,获取内核程序地址值
{width=“5.833333333333333in”
height=“1.6994411636045494in”}
返回地址覆盖
利用nft_payload完成返回地址的覆盖
{width=“5.833333333333333in”
height=“3.1172397200349957in”}
在数据包中将payload填充进去,这里需要说明一下如何在内核中拿到shell权限
-
首先需要在内核中拿到root权限,需要调用commit_creds(prepare_kernel_cred
(0))的内核函数获取新的凭证结构,而该结构的uid = 0 ,gid =
0即为root权限 -
其次需要切换命名空间,由于在普通用户下是无法直接调用Nftables的,因为需要管理员的权限,因此在普通用户下需要新开辟一个命名空间,使得该空间与正常的空间隔离,此时才能够正常执行Nftales。那么如果逃逸这段命名空间则需要进行命名空间的切换,则依赖于switch_task_namespace函数,可以将命名空间切换为root的命名空间
-
最后则是实现从内核态切换到用户态,由于我们是在内核空间拿到权限,而我们需要在用户态执行,因此需要完成状态的转换,该状态转换依赖于swapgs_restore_regs函数
{width=“5.833333333333333in”
height=“3.350757874015748in”}
漏洞修复
补丁则是新增一条判断条件,属于4字节寄存器的下标单独处理,而不在16字节寄存器以及4字节寄存器的范围内的下标都进行报错处理
{width=“5.833333333333333in”
height=“4.722735126859143in”}
总结
Nftables栈溢出漏洞攻击流程
-
首先利用nft_bitwise进行内核基地址的泄露。
-
其次是利用nft_payload改写返回地址,并将提权代码注入进去。
-
最后等到代码被触发。
Nftables栈溢出漏洞利用的限制
- 不同的内核版本的内核栈布局几乎不同,因此不同版本之间的利用手法相差较大,因此漏洞的利用十分依赖于内核版本,针对不同的版本需要做出针对性的漏洞利用的exp编写。差别存在于内核栈中存在的内核代码段地址的偏移不同,例如有些内核代码段地址偏移距离regs太大,导致无法利用漏洞进行泄露或者改写。
相关文章:
Nftables栈溢出漏洞(CVE-2022-1015)复现
背景介绍 Nftables Nftables 是一个基于内核的包过滤框架,用于 Linux 操作系统中的网络安全和防火墙功能。nftables 的设计目标是提供一种更简单、更灵活和更高效的方式来管理网络数据包的流量。 钩子点(Hook Point) 钩子点的作用是拦截数…...
【C++】 Qt-事件(上)(事件、重写事件、事件分发)
文章目录 事件重写事件事件分发 事件 事件(event)是由系统或Qt本身在不同的时刻发出的。比如,当用户按下鼠标,敲下键盘,或窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件是在对用户操作做出响应…...
k8s部署springboot
前言 首先以SpringBoot应用为例介绍一下k8s的部署步骤。 1.从代码仓库下载代码,比如GitLab; 2.接着是进行打包,比如使用Maven; 3.编写Dockerfile文件,把步骤2产生的包制作成镜像; 4.上传步骤3的镜像到远程…...
备战秋招002(20230704)
文章目录 前言一、今天学习了什么?二、关于问题的答案1.线程池2.synchronized关键字3、volatile 总结 前言 提示:这里为每天自己的学习内容心情总结; Learn By Doing,Now or Never,Writing is organized thinking. …...
游泳买耳机买什么的比较好,列举几款实战性好的游泳耳机
对于运动用户来说,在运动时都会选择听一些节奏感比较强的音乐,让自己运动是更有活力。现在已经是三伏天中的前伏期间,不少人会选择在三伏天的日子里进行减肥瘦身,耳游泳已经成为很多人都首选运动,游泳是非常好的有氧运…...
【无线传感器】使用 MATLAB和 XBee连续监控温度传感器无线网络研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
Java基础-多线程JUC-生产者和消费者
1. 生产者与消费者 实现线程轮流交替执行的结果; 实现线程休眠和唤醒均要使用到锁对象; 修改标注位(foodFlag); 代码实现: public class demo11 {public static void main(String[] args) {/*** 需求&#…...
day2 QT按钮与容器
目录 按钮 1、QPushButton 2、QToolButton 3、QRadioButton 4、QCheckBox 示例 容器 编辑 1. QGroupBox(分组框) 2. QScrollArea(滚动区域) 3. QToolBox(工具箱) 4. QTabWidget(选…...
JPA 批量插入较大数据 解决性能慢问题
JPA 批量插入较大数据 解决性能慢问题 使用jpa saveAll接口的话需要了解原理: TransactionalOverridepublic <S extends T> List<S> saveAll(Iterable<S> entities) {Assert.notNull(entities, "Entities must not be null!");List<…...
为啥离不了 linux
Linux与Windows都是十分常见的电脑操作系统,相信你对它们二者都有所了解!在你的使用过程中,是否有什么事让你觉得在Linux上顺理成章,换到Windows上就令你费解?亦或者关于这二者你有任何想要分享的,都可以在…...
基于分形的置乱算法和基于混沌系统的置乱算法哪种更安全?
在信息安全领域中,置乱算法是一种重要的加密手段,它可以将明文进行混淆和打乱,从而实现保密性和安全性。常见的置乱算法包括基于分形的置乱算法和基于混沌系统的置乱算法。下面将从理论和实践两方面,对这两种置乱算法进行比较和分…...
pve使用cloud-image创建ubuntu模板
首先连接pve主机的终端 下载ubuntu22.04的cloud-image镜像 wget -P /opt https://mirrors.cloud.tencent.com/ubuntu-cloud-images/jammy/current/jammy-server-cloudimg-amd64.img创建虚拟机,id设为9000,使用VirtIO SCSI控制器 qm create 9000 -core…...
shiro入门
1、概述 Apache Shiro 是一个功能强大且易于使用的 Java 安全(权限)框架。借助 Shiro 您可以快速轻松地保护任何应用程序一一从最小的移动应用程序到最大的 Web 和企业应用程序。 作用:Shiro可以帮我们完成 :认证、授权、加密、会话管理、与 Web 集成、…...
开源 sysgrok — 用于分析、理解和优化系统的人工智能助手
作者:Sean Heelan 在这篇文章中,我将介绍 sysgrok,这是一个研究原型,我们正在研究大型语言模型 (LLM)(例如 OpenAI 的 GPT 模型)如何应用于性能优化、根本原因分析和系统工程领域的问题。 你可以在 GitHub …...
Gitlab保护分支与合并请求
目录 引言 1、成员角色指定 1、保护分支设置 2、合并请求 引言 熟悉了Git工作流之后,有几个重要的分支,如Master(改名为Main)、Develop、Release分支等,是禁止开发成员随意合并和提交的,在此分支上的提交和推送权限仅限项目负责…...
ad18学习笔记九:输出文件
一般来说提供给板卡厂的文件里要包括以下这些文件 1、装配图 2、bom文件 3、gerber文件 4、转孔文件 5、坐标文件 6、ipc网表 AD_PCB:Gerber等各类文件的输出 - 哔哩哔哩 原点|钻孔_硬件设计AD 生成 Gerber 文件 1、装配图 如何输出装配图? 【…...
PostgreSQL 内存配置 与 MemoryContext 的生命周期
PostgreSQL 内存配置与MemoryContext的生命周期 PG/GP 内存配置 数据库可用的内存 gp_vmem 整个 GP 数据库可用的内存 gp_vmem: >>> RAM 128 * GB >>> gp_vmem ((SWAP RAM) - (7.5*GB 0.05 * RAM)) / 1.7 >>> print(gp_vmem / G…...
vue3 组件间通信的方式(setup语法糖写法)
vue3 组件间通信的方式(setup语法糖写法) 1. Props方式 该方式用于父传子,父组件以数据绑定的形式声明要传递的数据,子组件通过defineProps()方法创建props对象,即可拿到父组件传来的数据。 // 父组件 <template><div><son…...
【Cache】Rsync远程同步
文章目录 一、rsync 概念二、rysnc 服务器部署1. 环境配置2. rysnc 同步源服务器2.1 安装 rsync2.2 建立 rsyncd.conf 配置文件2.3 创建数据文件(账号密码)2.4 启动服务2.5 数据配置 3. rysnc 客户端3.1 设置同步方法一方法二 3.2 免交互设置 4. rysnc 认…...
Gitlab升级报错一:rails_migration[gitlab-rails] (gitlab::database_migrations line 51)
Gitlab-ce从V14.0.12升级到V14.3.6或V14.10.5时报错:如下图: 解决办法: 先停掉gitlab: gitlab-ctl stop 单独启动数据库,如果不单独启动数据库,就会报以上错误 sudo gitlab-ctl start postgresql 解决办法&#x…...
M1 Mac 8GB内存跑不动7B模型?手把手教你用1.5B版DeepSeek+RAGFlow搭建个人知识库
M1 Mac 8GB内存跑不动7B模型?手把手教你用1.5B版DeepSeekRAGFlow搭建个人知识库 当M1 Mac用户尝试在本地部署大语言模型时,8GB内存往往成为难以逾越的障碍。特别是运行7B参数模型时,内存不足导致的崩溃和卡顿让许多开发者望而却步。本文将分…...
Vivado平台下PCIe IP核选型指南:从硬核到XDMA的实战抉择
1. PCIe技术基础与Vivado开发环境搭建 第一次接触PCIe接口开发时,我被各种专业术语搞得晕头转向。后来才发现,理解PCIe就像理解高速公路系统一样简单。PCIe本质上是一种点对点的高速串行总线,就像城市间修建的多车道高速公路。每个"车道…...
AI辅助开发:模仿PS创意效果,用快马生成智能艺术风格迁移应用代码
最近在做一个艺术风格迁移的小项目,正好用到了InsCode(快马)平台的AI辅助开发功能,整个过程特别顺畅。这个项目的灵感来源于PS的创意效果,但想用更智能的方式来实现类似功能。下面分享一下我的实现思路和经验。 项目构思 最初是想做一个能让普…...
手把手教你用FUTURE POLICE:会议录音秒变带时间轴字幕
手把手教你用FUTURE POLICE:会议录音秒变带时间轴字幕 1. 为什么需要高精度字幕对齐? 在日常工作中,我们经常遇到这样的场景:重要会议录音需要整理成文字稿,但人工听写耗时耗力;视频剪辑时需要添加字幕&a…...
OLED多级菜单移植与设计实战
1. 低成本嵌入式项目的OLED多级菜单设计 第一次接触OLED多级菜单是在一个智能温控器的DIY项目里。当时为了给设备做个简单的交互界面,我试过各种方案,最后发现0.96寸的OLED屏配上多级菜单是最经济实惠的选择。这种组合特别适合预算有限但又需要基本人机交…...
【Oracle篇】基于OGG 21c全程图形化实现9TB数据从Oracle 11g到19c的不停机迁移(上):微服务架构详解与微服务部署,及同步问题总览(第一篇,总共三篇)
💫《博主主页》: 🔎 CSDN主页: 奈斯DB 🔎 IF Club社区主页: 奈斯、 🔎 微信公众号: 奈斯DB 🔥《擅长领域》: 🗃️ 数据库…...
进程间通信(IPC):原理、场景与选型
在操作系统的世界里,进程是程序运行的基本单元,每个进程都拥有独立的内存空间和资源,彼此之间相互隔离,无法直接访问对方的数据。这种隔离机制保证了系统的稳定性,避免进程间相互干扰,但也带来了一个问题&a…...
CMake实战:用ExternalProject_Add一键集成第三方库(附spdlog完整配置)
CMake实战:用ExternalProject_Add一键集成第三方库(附spdlog完整配置) 在C项目开发中,第三方库的集成往往是最耗时的环节之一。传统的手动下载、编译、配置头文件路径和链接库文件的方式,不仅效率低下,还会…...
STM32驱动SG90舵机:从PWM原理到蓝牙远程控制实战
1. 认识SG90舵机与PWM控制 第一次拿到SG90这个小家伙时,我差点以为是个玩具电机。直到把它接上STM32,看到它能精准地停在指定角度,才意识到这玩意儿在机器人、智能家居里有多实用。SG90是一种微型舵机,三根线分别接电源࿰…...
Qwen1.5-0.5B-Chat实战部署:Docker容器化改造方案
Qwen1.5-0.5B-Chat实战部署:Docker容器化改造方案 本文介绍如何将基于ModelScope的Qwen1.5-0.5B-Chat对话服务进行Docker容器化改造,实现一键部署和跨平台运行。 1. 项目概述与核心价值 Qwen1.5-0.5B-Chat是阿里通义千问开源系列中最轻量的对话模型&…...
