20169215 《Linux内核原理与分析》第二周作业
网易云课堂学习笔记 第一周
1、所有带E的寄存器都是32位的。EAX累加器,EBX基地址寄存器,ECX计数寄存器,EDX数据寄存器,EBP堆栈基指针,ESI、EDI变址寄存器,ESP堆栈顶指针。CS代码段寄存器,DS数据段寄存器,ES附加段寄存器,SS堆栈段寄存器,FS附加段寄存器,GS附加段寄存器。带R的寄存器都是64位的。
2、b、w、l、q分别代表8位、16位、32位和64位
movl %eax,%edx edx = eax 寄存器寻址
movl $0x123,%edx edx = 0x123 立即寻址
movl 0x123,%edx edx = *(int32_t*)0x123 直接寻址
movl (%ebx),%edx edx = *(int32_t*)ebx 间接寻址
movl 4(%ebx),%edx edx = *(int32_t*)(ebx+4) 变址寻址
Linux内核使用的是AT&T汇编格式,与Intel汇编格式略有不同。
3、pushl %eax subl $4, %esp
movl %eax, (%esp)
popl %eax movl (%esp), %eax
addl $4, %esp
call 0x12345 pushl %eip
movl $0x12345, %eip
ret popl %eip
%esp指的是esp寄存器的内容,(%esp)表示esp寄存器内容(是一个地址)所指地方的内容。
栈底在髙地址,栈顶在低地址。不能直接使用eip寄存器,只能间接修改。
4、leave指令:movl %ebp, %esp
popl %ebp
撤销函数调用堆栈;
enter指令:pushl %ebp
movl %esp, %ebp
函数调用堆栈。
函数返回值默认使用eax寄存器返回给上一级函数。
实验楼实验
C源码:
生成的汇编代码(删除链接部分后):
因为C源码比较简单,所以这段汇编码看起来十分简单。不过麻雀虽小,五脏俱全。从这段代码中我们可以看到程序执行是通过对堆栈的各种操作进行的。唯一的难点就是函数的调用。从汇编代码可以看出所有函数执行前都要调用堆栈,即执行enter指令。无论是main()函数调用f()函数,还是f()函数调用g()函数,都要先记录断点。call指令用来记录接下来本要执行的指令地址并压栈,用于恢复之前的运行状态;然后修改eip寄存器内容为要跳转到的函数地址,执行接下来的函数;函数执行结束之后要返回之前的运行状态,即执行call指令压入堆栈的那条指令。即call指令引发中断,ret指令恢复中断。如果函数执行完enter指令之后又对堆栈进行了调用,还要执行leave指令来结束对对战的调用。
课本1、2、18章的学习
Linux系统模块程度高,内核设计精巧,可根据用户需要,实时将某些模块从内核中加载或移除;Linux有着广泛的硬件支持,支持多种微处理器,支持对称多处理机制;Linux支持抢占,允许在内核运行的任务优先执行的能力;内核用C语言进行开发,拥有很强的可移植性;作为开源的操作系统,全球所有爱好者都可以参与Linux系统的改进和优化,为Linux系统内核打补丁。
Linux内核开发特点:
- 内核编程时既不能访问C库也不能访问标准的C头文件;
- 内核编程时必须使用GUN C;
- 内核编程时缺乏像用户空间那样的内存保护机制;
- 内核编程时难以执行浮点运算;
- 内核给每个进程只有一个很小的定长堆栈;
- 由于内核支持异步中断、抢占和SMP,因此必须时刻注意同步和兵法;
- 要考虑可移植性的重要性。
问题和解决
-
对寄存器和汇编语言的不熟悉,导致反汇编过程中常常遇到不理解的地方。
可以在课下多查询,多记忆汇编语言命令和各种寄存器名字,并通过多次反汇编的实践巩固和学习。 -
反汇编过程中一直理解不了在调用堆栈即执行enter命令时,为什么要把栈底地址压栈,再对栈底指针进行加一的操作。
通过查找资料了解到通过enter和leave指令可以防止因pushl和popl不配对引起返回中断点时eip寄存器中指令不是响应call指令时压入堆栈的指令,从而引发程序死掉的可能。通过enter和leave指令,可以使返回中断点只和ebp有关,只要ebp不随便改,就不会出现返回到错误的中断点的情况。
学习心得
通过一周的学习,学会了简单的汇编语言,能够反汇编一些简单小程序,最重要的是了解了程序在底层的大致的执行流程,便于对代码的理解。