diff --git a/build/ctf_all_in_one.pdf b/build/ctf_all_in_one.pdf index 9d94949..d76ab14 100644 Binary files a/build/ctf_all_in_one.pdf and b/build/ctf_all_in_one.pdf differ diff --git a/tex/contents/ZH/chapter1_basic/chapter1_basic.tex b/tex/contents/ZH/chapter1_basic/chapter1_basic.tex index e3e5180..28cbd83 100644 --- a/tex/contents/ZH/chapter1_basic/chapter1_basic.tex +++ b/tex/contents/ZH/chapter1_basic/chapter1_basic.tex @@ -467,15 +467,115 @@ db-peda$ x/s 0xffffd584 \indent 在 Linux 系统中一切皆可以看成是文件,文件又分为:普通文件、目录文件、链接文件和设备文件。文件描述符(file descriptor)是内核管理已被打开的文件所创建的索引,使用一个非负整数来指代被打开的文件。 \indent 标准文件描述符如下: +\indent \\ +\begin{tabular}[ht]{|c|c|c|} + \hline + 文件描述符 & 用途 & stdio 流 \\ + \hline + 0 & 标准输入 & stdin \\ + \hline + 1 & 标准输出 & stdout \\ + \hline + 2 & 标准错误 & stderr \\ + \hline +\end{tabular} -\indent 当一个程序使用 \verb+fork()+ 生成一个子进程后,子进程会继承父进程所打开的文件表,此时,父子进程使用同一个文件表,这可能导致一些安全问题。如果使用 \verb+vfork()+,子进程虽然运行于父进程的空间,但拥有自己的进程表项。 +\indent 当一个程序使用 \verb+fork+ 生成一个子进程后,子进程会继承父进程所打开的文件表,此时,父子进程使用同一个文件表,这可能导致一些安全问题。如果使用 \verb+fork()+,子进程虽然运行于父进程的空间,但拥有自己的进程表项。 \subsubsection{核心转储} \indent \setlength{\parindent}{2em} +\indent 当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存、寄存器状态、堆栈指针、内存管理信息等记录下来,保存在一个文件中,这种行为就叫做核心转储(Core Dump)。 + +\indent 会产生核心转储的信号: + +\begin{tabular}{|c|c|c|} + \hline + Signal & Action & Comment \\ + \hline + SIGQUIT & Core & Quit from keyboard \\ + \hline + SIGILL & Core & Illegal Instruction \\ + \hline + SIGABRT & Core & Abort signal from abort \\ + \hline + SIGSEGV & Core & Invalid memory reference \\ + \hline + SIGTRAP & Core & Trace/breakpoint trap \\ + \hline +\end{tabular} + +\indent 开启核心转储: +\indent \begin{itemize} + \item 输入命令 \verb+ulimit -c+,输出结果为 0,说明默认是关闭的。 + \item 输入命令 \verb+ulimit -c unlimited+ 即可在当前终端开启核心转储功能。 + \item 如果想让核心转储功能永久开启,可以修改文件 \verb+/etc/security/limits.conf+,增加一行: + \item \verb+# + + \item \verb+* soft core unlimited+ +\end{itemize} + +\indent +\indent 修改转储文件保存路径: +\begin{itemize} + \item 通过修改 \verb+/proc/sys/kernel/core_uses_pid+,可以使生成的核心转储文件名变为 \verb+core.[pid]+ 的模式: + \item \verb+# echo 1 > /proc/sys/kernel/core_uses_pid+ + \item 还可以修改 \verb+/proc/sys/kernel/core_pattern+ 来控制生成核心转储文件的保存位置和文件名格式: + \item \verb+# echo /tmp/core-%e-%p-%t > /proc/sys/kernel/core_pattern+ +\end{itemize} + +\indent 此时生成的文件保存在 /tmp/ 目录下,文件名格式为 core-[filename]-[pid]-[time]。 + +\indent 例子: +\begin{lstlisting}[language=bash, style=customStyleBashDark, caption=内存转储示例] +$ cat core.c +#include +void main(int argc, char **argv) { + char buf[5]; + scanf("%s", buf); +} +$ gcc -m32 -fno-stack-protector core.c +$ ./a.out +AAAAAAAAAAAAAAAAAAAA +Segmentation fault (core dumped) +$ file /tmp/core-a.out-12444-1503198911 +/tmp/core-a.out-12444-1503198911: ELF 32-bit LSB core file Intel 80386, version 1 (SYSV), SVR4-style, from './a.out', real uid: 1000, effective uid: 1000, real gid: 1000, effective gid: 1000, execfn: './a.out', platform: 'i686' +$ gdb a.out /tmp/core-a.out-12444-1503198911 -q +Reading symbols from a.out...(no debugging symbols found)...done. +[New LWP 12444] +Core was generated by `./a.out'. +Program terminated with signal SIGSEGV, Segmentation fault. +#0 0x5655559b in main () +gdb-peda$ info frame +Stack level 0, frame at 0x41414141: + eip = 0x5655559b in main; saved eip = + Outermost frame: Cannot access memory at address 0x4141413d + Arglist at 0x41414141, args: + Locals at 0x41414141, Previous frame's sp is 0x41414141 +Cannot access memory at address 0x4141413d +\end{lstlisting} \subsubsection{调用约定} \indent \setlength{\parindent}{2em} +\indent 函数调用约定是对函数调用时如何传递参数的一种约定。关于它的约定有许多种,下面我们分别从内核接口和用户接口介绍 32 位和 64 位 Linux 的调用约定。 + +\indent 内核接口 + +\indent x86-32 系统调用约定:Linux 系统调用使用寄存器传递参数。 + +\indent \verb+eax+ 为 \verb+syscall_number+,\verb+ebx+、\verb+ecx+、\verb+edx+、\verb+esi+、\verb+ebp+ 用于将 6 个参数传递给系统调用。返回值保存在 eax 中。所有其他寄存器(包括 \verb+EFLAGS+)都保留在 \verb+int 0x80+ 中。 + +\indent x86-64 系统调用约定:内核接口使用的寄存器有:\verb+rdi+、\verb+rsi+、\verb+rdx+、\verb+r10+、\verb+r8+、\verb+r9+。系统调用通过 \verb+syscall+ 指令完成。除了 \verb+rcx+、\verb+r11+ 和 \verb+rax+,其他的寄存器都被保留。系统调用的编号必须在寄存器 \verb+rax+ 中传递。系统调用的参数限制为 6 个,不直接从堆栈上传递任何参数。返回时,\verb+rax+ 中包含了系统调用的结果。而且只有 \verb+INTEGER+ 或者 \verb+MEMORY+ 类型的值才会被传递给内核。 + +\indent 用户接口 + +\indent x86-32 函数调用约定: + +\indent 参数通过栈进行传递。最后一个参数第一个被放入栈中,直到所有的参数都放置完毕,然后执行 \verb+call+ 指令。这也是 Linux 上 C 语言函数的方式。 + +\indent x86-64 函数调用约定: + +\indent x86-64 下通过寄存器传递参数,这样做比通过栈有更高的效率。它避免了内存中参数的存取和额外的指令。根据参数类型的不同,会使用寄存器或传参方式。如果参数的类型是 \verb+MEMORY+,则在栈上传递参数。如果类型是 \verb+INTEGER+,则顺序使用 \verb+rdi+、\verb+rsi+、\verb+rdx+、\verb+rcx+、\verb+r8+ 和 \verb+r9+。所以如果有多于 6 个的 \verb+INTEGER+ 参数,则后面的参数在栈上传递。 + \subsubsection{环境变量} \indent \setlength{\parindent}{2em}