关于Linux一些IO的细节
写这篇博客的原因是在考试中遇到一个很 细 的题目。
题目题目长这样
问最后的运行结果,当然有 fork 存在肯定是多种情况的,当时乍一看这一题,就很自信地写了
xbay ,xaby,xayb
结果却是一个大红叉子,我细数也就四次输出,为什么答案长度为 5??
我运行一遍之后,发现结果:
纳尼,真的是 5 个,哪来的五个呢。
细细品味一下,原来有个天坑。
分析首先 Linux 的 C,它的缓冲区默认不初始化的,如果不初始化缓冲区,就会造成每隔一行才打印一次,或者是等到程序 exit 之后才打印。
如果在 Linux 下面有这样的语句
12printf("input");scanf("%s");
那么你大概率是看不到开头这个 input 的,只有程序结束了才能看到,原因就是调用的 IO 函数它会先把数据存到对应的 FILE 结构体上。
调试我们启动 gdb 调试一下,断在第一个 putchar 下面,在进去之前我们可以看到,stdout 没有初始化。
然后 putchar 内部调用一个 __overflow 函数
最后进入到 ...
windows网络编程——tcp套接字
TCP 是面向有连接的可靠传输协议,来讲讲它的一个 Windows 的基本实现。
学习笔记首先在 Windows 下 socket 编程要添加这样的预处理:
12# include<WinSock2.h># pragma comment(lib,"ws2_32.lib")
然后我们需要启动一下,一般加入以下代码即可:
123WORD wVersionRequested = MAKEWORD(2, 2);WSADATA lpWSAData;int nRet = WSAStartup(wVersionRequested, &lpWSAData);
为了程序健壮可以添加一些判断代码。
12345678910if (nRet != 0) { printf("WSAStartup Error!\n"); system("pause"); return 0;}if (LOBYTE(lpWSAData.wVersion) != 2 || HIBYTE(lpWSAData.wV ...
syspro4-远程线程注入的实现
之前呢细学习了一遍 dll 注入的流程,然而对于 dll 怎么注入进去还是一无所知,所以今天就刚好来学习一下。
动态链接库编写dll我们说 exe 和 dll 文件都是 PE 文件,一般 exe 只有导入表,dll 只有导出表。导入表会显示你导入了哪些 dll ,dll 中导入了哪些函数等等。dll 的导出表则会显示你这里有多少可以被导入的函数。
新建链接库,创建 h 文件,输入申明
1extern "C" __declspec(dllexport) int add(int num1,int num2);
主要是因为 C++ 有名称粉碎机制,因为 具有多态特性的C++支持函数的重载,函数不再以函数名称作为唯一标识。只要满足构成重载的条件,两个(或多个)功能不同的函数可以有相同的函数名称。这样一来,函数的调用者会获得多态性带来的极大方便(虽然函数的编写者的工作量没有改变,所有的同名函数仍需要一个一个地去编写)。构成函数重载的条件是:
作用域相同
函数名称相同
参数不同(类型,个数,顺序)(另外:返回值类型、调用约定类型并不作为参考)
为了支持函数重载这一新 ...
PE文件解析——导入表结构
PE第五课——导入表
课堂笔记上节课讲的是数据目录表,这节课是导入表,导入表好像是数据目录表的一种吧,也不清楚,暂时先这么理解。
现在需要用到库函数已经可以不需要导入表了,可以用 LoadLibarary 或者是 GetProcAddress 来获取函数地址直接使用,但是我们还是有必要学这个。
比较重要的结构体定义如下。
1234567891011121314151617181920212223242526272829typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // 0 for terminating null import descriptor DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) } DUMMYUNIONNAME; DWORD TimeDa ...
PE文件解析——数据目录表
PE第四课——数据目录表
课堂笔记在数据目录表中一般只关心导入表,导出表和资源这几个部分,但是资源实在是太复杂了,而且在一般的病毒木马中也不会存在资源,所以在这个工具中只是简单的解析了一下导出表和导出表。
在 VS 中,数据目录表的结构很简单。
1234typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size;} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
就是一个虚拟地址和大小的字段。
它是一个数组结构,用宏定义可以看见什么表在某个下标的位置。
RVA和FRVARVA 转化为 FRVA 主要是通过某个数据在内存中的相对偏移地址找到其在文件中的相对偏移地址,在对某个程序进行逆向时,如果找到关键的那个变量或者那句指令,我根据变量或者代码指令在内存中的 RVA 找到它在文件中的偏移,就可以找到它的位置,修改它可能就可以破解某个程序。
所以我们写一个 RVA 转为 FRVA 的函数。
它有这么几个参数:一个 ...
PE文件解析——区段表结构
PE第三课——区段表结构
课堂笔记区段表其实上节课就已经讲过了,这个区段呢我们能自己定义名字然后它在 PE 里面有内存地址偏移和文件偏移。我们需要用一个宏来从这个 NTHeaders 获取 SectionHeader。再从 NTheaders 中获取区段的数量 NumberOfSection。
然后就是根据结构体打印一些信息了)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263#include<windows.h>#include<stdio.h>int main() { FILE* fd = fopen("C:\\Users\\xia0ji233\\Desktop\\Home\\C++\\test.exe", "rb"); char* buffer = (char*)malloc(0x10000); ...
PE文件解析——PE头解析
PE第二课——PE头解析
课堂笔记PE 头我们能在 010 editor 打开exe后面可以很明显的看到有一个 PE
这就是 PE 头所在的位置。上节课我们也提到了一个 MS-DOS头的最后一个字段 e_lfanew 其实就是我们 PE 头所在的位置,就是这个 PE 文件的字节地址。我们可以看到最后是 0x80,而 PE 头也是在 0x80 的地方开始的。
PE头PE 头分为标准 PE 头(必要)和扩展 PE 头(不必要)。
PE 头结构在 visual studio 中的结构体为 _IMAGE_NT_HEADERS。
它的定义为
12345typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader;} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
这里的 Signature 就是 5045,也就是我们看到的 PE。
IM ...
关于global_max_fast的利用
关于global max fast的利用技巧。
global_max_fast 的利用现在才知道 global_max_fast 的一个小技巧,我之前一直以为就是在没有 fastbin 的时候能构造 fastbin。但是没想到它能在 libc 中任意写。首先 fastbin 是存在 fastbinY 数组当中,它的长度好像只有 10。
但是我如果把任意的size都视为fastbin的话,那么这就有一个下标越界了。
比如我把 0x40000 的 chunk 当成 fastbin,那么在free 的时候就会有这样的语句出现:
1main_arena->fastbinY[0x4000]=chunkptr
我这个size 如果能改的任意,那么它就能写在 main_arena 之后的任何的地址。并且由于它在 libc 的地址是固定的,所以我要往一个 libc 中的地址写上堆的地址就很容易。
这里就mark一下,以后遇到了来这更一下传送门,目前也没有找到什么好的例题。
祥云杯2022-leak题解
祥云杯做到一道出的还挺好的题目,而且学了较多的利用思路,特此记录。leak.zip
文件分析这一题呢,是一道经典的 2.27 1.6 版本的堆题,实现了增删改的功能,没有查,4,5两个选项目测是摆设,在题目的一开始,把 flag 的内容读到了堆上,delete 操作存在 uaf 漏洞
思路分析这题它主要是没有 IO 函数,所以 IO 结构体一直没有初始化。但是这里小知识点来了:我们只要把 stdout 的 flag 设置为 0xfbad1800,然后再 exit,它就会输出 IO_write_base 到 IO_write_ptr 俩指针中间的内容,并且它是逐字节打印的,只有尝试打印不可读的内存的时候才会崩溃,只要 IO_write_base 往后还有可读内存,它就一定能打印这些信息。
这个知识其实是 IO 里面的,是当时 fmyy 师傅给我的提示,于是我们的思路就是构造合理的 IO 结构体,然后 IO_write_base 改成 堆的地址,flag 和 IO_write_ptr 改成合理的内范围就可以泄露堆上面的内容了,如图所示:
所以我们的思路是要往 IO 这里写一个堆地址 ...
ciscn_2019_c_5 WriteUp
buu刷题记录——ciscn_2019_c_5
文件分析ida 拉进来发现是很经典的堆题,并且 2,3 的改和查都不可用,在增的过程中可以改。
delete中存在很明显的 UAF。data 段上的堆结构体大概是这样的:
1234struct heap{ int size; char *content;}
版本 2.27,我们可以直接 double free。
但是因为保护全开,且地址难泄露,所以我们能利用的点也比较少。在这样的情况下我们只能去打IO泄露 libc 的地址了。首先构造一个 unsorted bin,因为堆块不能直接编辑我们控制另外一个堆块 double free 之后修改这里的地址。这个地址需要自己注意 libc 版本即使差异很小偏移也有可能会变。
打 stdout 的 flag 改为 0xfbad1800,IO_write_base 低字节修改成 0 就可以泄露成功。泄露成功之后直接打 free_hook 就可以 free 一个 /bin/sh 获得shell。
exp123456789101112131 ...