20191218 2021-2022-1 《信息安全系统设计与实现》第一周学习笔记
一、任务要求
- 学教材第1,2章,提交学习笔记(10分)
- 知识点归纳以及自己最有收获的内容 (3分)
- 问题与解决思路(2分)
- 实践内容与截图,代码链接(3分)
- 知识的结构化,知识的完整性等,提交markdown文档,使用openeuler系统等(2分)
二、知识归纳
第一章
(0)书籍实现目标:
- 提供高级编程所需背景知识和技能
了解软件工具、程序开发步骤和程序执行的运行时环境,开发步骤包括汇编器、编译器、链接器、链接库、可执行文件内容、程序执行影响、函数调用约定、参数传递方案、局部变量、栈帧、C程序和汇编代码链接、程序终止和异常处理、Makefile和GDB的使用
第二周的课堂上老师重点讲解了C程序源代码转换成可执行文件的全过程,这其中第一步是通过gcc -E命令将源代码使用编译器转换成.i文件(预处理),再通过gcc -S命令通过将.i文件转换成.s的汇编程序,之后通过gcc -c命令将.s文件转换成.o的目标文件(二进制机器码),最后用链接器-o将目标文件变为可执行文件。(记忆方法:ESc,iso) - 动态数据结构应用
用二叉树模拟Unix/Linux文件系统树,支持pwd、ls、cd、mkdir等操作。 - 进程管理
各种进程的抽象。创建进程、按优先级调度进程、通过上下文切换运行不同进程、通过二叉树维护进程关系、使用sleep和wakeup原语实现等待子进程终止等。进程管理包括fork(),exit(),wait(),exit()等。还包括I/O重定向和管道。 - 并发编程
Pthreads编程,线程连接、互斥量、条件变量、信号量、屏障。 - 定时器和定时功能
- 信号处理和进程间通信
- 文件系统
- TCP/IP和网络编程
(1)Unix发展历史
- AT&T Unix
- Berkeley Unix
- HP Unix
- IBM Unix
- Sun Unix
(2)Linux版本
- Debian Linux
- Ubuntu Linux
- Linux Mint
- 基于RPM的Linux
- Slackware Linux
(3)虚拟机平台的安装
之前大一在娄老师的课上曾安装过Virtual Box虚拟机平台,如下图
之后我发现Virtual Box要和主机传文件需要配置共享文件夹,比较麻烦,再加上参加CTF比赛需要主机和虚拟机之间频繁传文件,我又安装了VMware Workstation平台,如下图
VMware Workstation中的虚拟机在安装增强工具后就能直接通过拖放移动实现文件的转移,比Virtual Box着实方便了不少。
(4)Kali Linux的使用
用户登录
常用命令行操作
注意.gz和.tgz的区别
以上是教材上所有列出命令的使用。
在Kali上尝试添加新用户
(5)Ubuntu Linux的使用
用户登录
其余操作同Kali类似。
第二章
(1)文本编辑器
教材中介绍了vim、gedit以及emacs三种编辑器,我平时用的较多的是VSCode,为更好学习Linux,之后尽量熟练Vin编辑器。
(2)在DebianLinux(Kali)上测试C语言编程
在课堂上跟着老师实现C语言代码的逐步转换
按老师提供方法查看最后的可执行文件的(二进制文件)内容
最后运行可执行文件
这和最开始使用gcc命令一步到位输出可执行文件运行结果是一样的
(3)程序开发步骤分析
- C语言程序中的变量可分为全局变量、局部变量、自动变量和寄存器变量等。
在大一C语言的课堂上,我们有详细了解过全局变量、局部变量,而在这里我了解到局部变量在默认情况下就是自动变量,编译器试图把寄存器变量存在CPU寄存器中,易失性变量用作内存映射I/O的地址或者通过中断处理程序或多个执行线程来访问的全局变量,易失性关键字可以阻止C编译器优化用这些变量进行操作的代码。 - gcc三个步骤:将源文件转换为汇编代码(编译),将汇编代码转换成目标代码(汇编),将目标代码转换成二进制可执行文件(链接)
- .o(汇编)文件包含
- 一个文件头,包含代码段、数据段和BSS段的大小
- 一个代码段,包含机器指令
- 一个数据段,包含初始化全局变量和初始化静态局部变量
- 一个BSS段,包含未初始化全局变量和未初始化静态局部变量
- 代码中的指针以及数据和BSS中的偏移量的重定位信息
- 一个符号表,包含非静态全局变量、函数名称及其属性
- 静态和动态链接
在使用静态库的静态链接中,链接器将所有必要的库函数代码和数据纳入a.out文件中。在执行动态链接的a.out文件时,操作系统将a.out文件和共享库均加载到内存中,使加载的库代码在执行期间可供a.out文件访问。
动态链接的主要优点:可减小每个a.out文件的大小,许多执行程序可在内存中共享相同的库函数,修改苦不需要重新编译源文件。
动态链接所用的库成为动态链接库(DLL)。它们在Linux中成为共享库(.so文件)。动态加载(DL)库是指仅按需加载的共享库。动态加载库可用作插件和动态加载模块。 - 可执行文件
- 二进制可执行平面文件
包含可执行代码和初始化数据 - a.out可执行文件
传统的a.out文件包含文件头(包含文件的加载信息和大小,tsize = 代码段大小,dsize = 数据段大小,bsize = bss段大小,total——size = 加载的a.out文件总大小),然后是代码段(正文段,从标准C启动代码crt0.o开始,改代码调用main()函数)、数据段、bss段、符号表(可选)
符号“_brk”表示bss段结束。a.out文件的总加载大小通常等于_brk(即tszie+dsize+bsize)。如果需要,_brk可以设置为更高值。 - ELF可执行文件
可执行的链接格式文件,包含一个或多个程序段,每个程序段均可可加载至特定的内存地址。在Linux中,默认的二进制可执行文件为ELF文件。
- 二进制可执行平面文件
(4)程序执行过程
在类Unix操作系统中,在sh命令行
a.out one two three
执行a.out文件,以标记字符串作为命令行参数。
为执行命令,sh创建一个子进程并等待改子进程终止。子进程运行时,sh使用a.out文件执行新的执行映像
-
读取a.out文件头
Total_Size = _brk + stackSize
普通的C语言程序将因为任一计算机上的堆栈溢出而产生分段错误(因此操作系统通常使用待启动程序的默认初始堆栈大小)
-
sh从总大小中分配一个内存区给执行映像。
-
sh放弃旧映像,开始执行新映像。
-
执行从crt0.o开始,调用main(),将argc和argv作为参数传递给main(),可以写成
int main(int argc, char *argv[]){ … }
(5)程序终止
-
正常终止
如果程序执行成功,main()最终会返回到crt0.out,调用库函数exit(0)来终止进程。
exit(value)函数会执行一些清理请求,如刷新stdout、关闭I/O流等,然后发出一个_exit(value)系统调用,是进入操作系统内核的进程终止。退出值0通常表示正常终止。如需要,进程也可直接调用exit(value)。再直接一点,进程可能会发出_exit(value)系统调用以立即终止。
内核中某个进程终止时,会在父进程结构体中将_exit(value)系统调用值记录为退出状态,通知其父进程并使该进程变为僵尸进程,父进程可通过系统调用pid = wait(int *status);
找到僵尸子进程,获得其pid和退出状态,并清空僵尸子进程的结构体,使该结构体可以被另一个进程重新使用。
-
异常终止
常见的有无效地址、非法指令、越权等。
当某进程遇到异常时,它会陷入操作系统内核,内核的陷入处理程序将陷入错误类型转换为一个幻数(即信号。)
除了错误导致的陷入,信号也可来自硬件或其他进程。
如“Ctrl+C”组合键会产生硬件中断(向终端上所有进程发送数字2的信号SIGINT),(INT即为Linux中断),或者用户可使用命令kill -s signal_number pid # signal_number = 1 to 31
向pid识别的目标进程发送信号。
对于大多数信号数值,进程的默认操作是终止。
(6)C语言背景
- 进程执行映像
- CPU中都含有的寄存器(或同等寄存器),括号中条目表示x86CPU寄存器
- PC(IP):指向CPU要执行的下一条指令
- SP(SP):指向栈顶
- FP(BP):指向当前激活函数的栈帧
- 返回值寄存器(AX):函数返回值的寄存器
在每个C语言程序中,main()函数均由C启动代码crt0.o调用。当crt0.o调用main()时,它将返回地址(当前PC寄存器)压栈,用main()的入口地址替换PC,使CPU进入main()。为了方便,我们按从左到右的顺序显示堆栈内容。当控制权转移到main()函数时,栈顶包含保存的返回PC。
在每个C函数的人口处,编译后的代码都会完成如下功能:
-
将FP压栈#,这将在堆栈上保存CPU 的FP寄存器。
-
让FP指向保存的FP#建立栈帧。
-
向下移动SP为堆栈上的自动局部变量分配空间。
-
编译后的代码可能会继续向下移动SP,在堆栈上分配一些临时工作空间,用temps
-
long jump
在一系列函数调用中,例如main() -->A( ) --> B( )-->C();
当一个被调用的函数结束时,通常会返回到正在调用的函数,例如C()返回到B(),B()返回到A()等。也可以通过long jump直接返回到调用序列中较早的某个函数。