Linux内核分析第一周课程小结
张家骥 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
第一部分:课堂笔记
Linux内核采用AT&T汇编格式。
ABI:Application Binary Interface
1.指令编码
2.寄存器约定
3.大多数指令可以直接访问内存
EIP自加一,加一指的是加一条指令的长度,指令可能是不定长指令。
CPU在实际去指令时根据CS:eip来准确定位一个指令
RAX 带R的指64位的寄存器,如:RIP,RFLAGS,RSI,RBP
movl %eax,%edx 以%开头的说明是寄存器表示符,movl的l指的是32位,movb,movw,movq分别指的是8位,16位和64位。
movl $0x123,%edx以$开头的说明是立即数。
Movl 0x123,%edx 直接寻址,数字前没有$,表示直接访问这个内存地址
Movl (ebx),%edx 间接寻址,以ebx的内容作为内存地址
Movl 4(%ebx),%edx 变址寻址,先给ebx加4,再以其内容为内存地址
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
Leave 撤销函数堆栈
等价于 Movl %ebp,%esp
Popl %ebp
eip寄存器不能被直接修改,只能通过特殊指令间接修改。
函数的返回值默认使用eax存储返回给上一级函数。
最初eip指向main:。
第二部分:实验
反汇编一个简单的C程序
实验具体要求
1)实验部分(以下命令为实验楼64位Linux虚拟机环境下适用,32位Linux环境可能会稍有不同) 使用
gcc –S –o main.s main.c -m32
命令编译成汇编代码,如下代码中的数字请自行修改以防与他人雷同
我使用的具体代码为:
使用如下命令编译生成汇编代码:
在生成的main.s文件中,以点开头的都是用户链接时的辅助信息,不能被实际执行,删除以.开头的内容后,剩余部分如下:
************************************************************************************************************************************************************************
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $7, %eax
popl %ebp
ret
f:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
ret
main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $7, (%esp)
call f
addl $7, %eax
leave
ret
************************************************************************************************************************************************************
一开始假设ebp和esp都指向同一位置,用ebp1和esp1标注,接下来,ebp和esp的变动过程通过它们数字角标的依次增大(如esp2,esp3)来表示。
首先从main函数开始分析:
pushl %ebp esp1->esp2, ebp1入栈
movl %esp, %ebp ebp1->ebp2
subl $4, %esp esp2->esp3
movl $7, (%esp) 立即数7入栈
call f esp3->esp4,eip入栈,eip被修改为f:
pushl %ebp esp4->esp5,ebp2入栈
movl %esp, %ebp ebp2->ebp3
subl $4, %esp esp5->esp6
movl 8(%ebp), %eax eax=7
movl %eax, (%esp) eax的内容7入栈
call g esp6->esp7,eip入栈,eip被修改为g:
pushl %ebp esp7->esp8,ebp3入栈
movl %esp, %ebp ebp3->ebp4
movl 8(%ebp), %eax eax=7
addl $7, %eax eax=14
popl %ebp esp8->esp9,ebp4->ebp5
ret esp9->esp10,eip出栈
leave esp10->esp11
esp11->esp12,ebp5->ebp6
ret esp12->esp13,eip出栈
addl $7, %eax eax=21
leave esp13->esp14
esp14->esp15,ebp6->ebp7
ret main函数退出,图中未画出。因为一开始假设了ebp和esp指向同一位置,所以会出现esp跳到ebp之上的情况。更合理的假设应该是ebp在esp之前的一个位置,它们之间隔了一个Old eip,这样ret执行后,eip被设置为了原来的内容。
第三部分:自己对“计算机是如何工作的”理解。
编写程序每一条高级语言都被编译成一条或多条机器指令,这些指令和程序所需要的数据都会在执行时,会被加载到内存中,CPU中的EIP寄存器总是指向下一条将要被执行的指令,当前要被执行的指令会被送入指令寄存器IR中,然后CPU通过硬件逻辑分析IR出中的这条指令的类型,操作码,操作数,执行这条指令,然后取下一条指令继续分析执行。如果程序中有函数调用之类非顺序执行的情况,会有相应指令将当前EIP的值压入栈中,然后修改EIP的值为需要跳转的指令的地址,然后继续从跳转处顺序执行,再函数返回时再从栈中取出EIP,继续执行。