【网络安全】Linux内核部分文件分析
前言
對于操作系統(tǒng)的分析,是一個(gè)復(fù)雜且枯燥的過程,其中包括中斷、調(diào)用等一系列的問題,需要從原理和代碼兩層的角度進(jìn)行分析,包含匯編、C語言等較為難理解的知識,以及算法的思維。
【學(xué)習(xí)資料】
啟動流程
setup模塊扇區(qū)長度為4個(gè)扇區(qū),各個(gè)模塊存放在內(nèi)存的地址。
這里的ROOT_DEV=0x306表示第二個(gè)硬盤的第一個(gè)扇區(qū)。
基礎(chǔ)
設(shè)備號 = 主設(shè)備號 * 256 + 次設(shè)備號
主設(shè)備號是定義好的:
1-內(nèi)存
2-磁盤
3-硬盤
PC機(jī)的BIOS把bootsect的一個(gè)固定地址拿到了內(nèi)存中某個(gè)固定地址(0x90000),
并且進(jìn)行硬件初始化和參數(shù)設(shè)置。
bootsect中的代碼首先移動到0X7C00,之后又轉(zhuǎn)移到0X90000。
代碼分析
bootsect模塊移入內(nèi)存
start: mov ax,#BOOTSEG mov ds,ax mov ax,#INITSEG mov es,ax mov cx,#256 sub si,si sub di,di rep movw jmpi go,INITSEG把bootsect中代碼移動到內(nèi)存中0X90000中,
可以看到此時(shí)的ds 存儲0x07c0 地址:es 0x90000。
利用寄存器從ds:si到es:di
jmpi go,INITSEGINITSEG是段地址,go是偏移地址,跳轉(zhuǎn)到程序執(zhí)行的地方(0x90000)。
bootsect.s作用
1.首先加載bootsect的代碼(磁盤引導(dǎo)塊程序,在磁盤中第一個(gè)扇區(qū)的程序)
2.將setup.s中代碼加載到bootsect.s中代碼之后
3.將system模塊加載到0x10000地方,最后跳轉(zhuǎn)到setup.s中運(yùn)行
棧的位置
對棧的設(shè)置 es:sp =0x90000:0xff00
setup模塊移入內(nèi)存
load_setup: mov dx,#0x0000 ! drive 0, head 0 //驅(qū)動器號0;磁頭號0 mov cx,#0x0002 ! sector 2, track 0 //起始扇區(qū)2;磁道0 mov bx,#0x0200 ! address = 512, in INITSEG mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors int 0x13 ! read it jnc ok_load_setup ! ok - continue mov dx,#0x0000 mov ax,#0x0000 ! reset the diskette int 0x13 j load_setupsetup模塊加載到0x90200中。
setup.s作用
- 解析BIOS傳遞過來的參數(shù);
- 設(shè)置系統(tǒng)內(nèi)核運(yùn)行的局部描述符,中斷描述寄存器,全局描述符;
- 設(shè)置中斷控制芯片,進(jìn)入保護(hù)模式;
- 跳轉(zhuǎn)到system模塊中head.s中代碼執(zhí)行。
system模塊移入內(nèi)存
! we want to load the system (at 0x10000) mov ax,#SYSSEG mov es,ax ! segment of 0x010000 call read_it call kill_motor把system模塊加載0x10000處,
關(guān)閉驅(qū)動器。
【學(xué)習(xí)資料】
head.s作用
- 加載內(nèi)核運(yùn)行時(shí)的各種數(shù)據(jù)段寄存器,重新設(shè)置中斷描述表;
- 開啟內(nèi)核正常運(yùn)行時(shí)的協(xié)處理器;
- 設(shè)置內(nèi)存管理的分頁機(jī)制;
- 跳轉(zhuǎn)到main.c開始運(yùn)行。
中斷調(diào)用:
有出錯(cuò)號、無出錯(cuò)號
中斷范圍:
int0 ~ int255。
int0 ~ int31 : 軟件中斷,由Intel固定設(shè)置的。
int32 ~ int255: 可由用戶自己設(shè)置。其中int32 ~ int47 對應(yīng)8259A的IRQ0 ~ IRQ15中斷。
特殊的一個(gè):int128 為系統(tǒng)調(diào)用中斷(system_call)。
#int7 – 設(shè)備不存在。
#int14 – 頁錯(cuò)誤。
#int16 – 協(xié)處理器錯(cuò)誤。
#int 0x20 – 時(shí)鐘中斷。
#int 0x80 – 系統(tǒng)調(diào)用。
asm.s分析
寄存器入棧:
no_error_code:xchgl %eax,(%esp)pushl %ebxpushl %ecxpushl %edxpushl %edipushl %esipushl %ebppush %dspush %espush %fs
異常碼入棧:
pushl $0 # “error code”
函數(shù)返回值入棧:
lea 44(%esp),%edx //把中斷的地方壓棧pushl %edxmovl $0x10,%edxmov %dx,%dsmov %dx,%esmov %dx,%fs
調(diào)用中斷服務(wù)函數(shù):
call *%eax //調(diào)用中斷打印函數(shù)
出棧函數(shù)返回值:
addl $8,%esp //函數(shù)參數(shù)出棧pop %fspop %espop %dspopl %ebppopl %esipopl %edipopl %edxpopl %ecxpopl %ebxpopl %eaxiret
error_code:
xchgl %eax,4(%esp) # error code <-> %eax //中斷錯(cuò)誤碼xchgl %ebx,(%esp) # &function <-> %ebx //中斷函數(shù)pushl %ecxpushl %edxpushl %edipushl %esipushl %ebppush %dspush %espush %fspushl %eax # error code //出錯(cuò)號入棧lea 44(%esp),%eax # offsetpushl %eaxmovl 0x10,8,%esp pop %fspop %espop %dspopl %ebppopl %esipopl %edipopl %edxpopl %ecxpopl %ebxpopl %eaxiret
trap.c分析
本程序用來處理硬件陷阱和故障。
asm.s和traps.c 兩個(gè)程序文件的關(guān)系:
asm.s 是匯編文件,主要實(shí)現(xiàn)大部分硬件中斷(異常)引起的中斷處理過程;trap.c
是C語言源文件,內(nèi)部是各種中斷處理的C函數(shù),這些函數(shù)在asm.s中進(jìn)行調(diào)用。
GCC編譯過程:
1.預(yù)處理階段
2.編譯階段
3.匯編階段
4.鏈接階段
內(nèi)聯(lián)匯編格式:
asm(“匯編語句” :輸出寄存器 :輸入寄存器 :會被修改的寄存器)
#define get_seg_byte(seg, addr) \ /*宏定義:取段seg中地址addr處的一個(gè)字節(jié)*/ ({ \ register char __res; \ //定義一個(gè)寄存器 __asm__("push %%fs; //保存fs寄存器的原值 mov %%ax, %%fs; //將seg設(shè)置到fs movb %%fs:%2, %%a1; //將seg:addr處的一個(gè)字節(jié)放置到a1寄存器中 pop %%fs "\ : "=a" (__res) \ //輸出寄存器列表 :"0" (seg), "m"(*(addr)));\ //輸入寄存器列表 __res;}) /*輸入:_res;輸出:seg 內(nèi)存地址* : "=a" (__res) \ //輸出寄存器列表 輸出寄存器,a代表eax,運(yùn)行結(jié)束后把eax的值放入res中 :"0" (seg), "m"(*(addr)));\ //輸入寄存器列表 輸入寄存器,0代表eax(與上面相同),m代表內(nèi)存地址取seg段addr處的一個(gè)字節(jié)。
取seg段addr處的四個(gè)字節(jié)。
取fs段寄存器的值。
str字符串 esp_ptr 棧指針 nr段號(在哪里出錯(cuò))。
static void die(char * str,long esp_ptr,long nr) { long * esp = (long *) esp_ptr; int i; printk("%s: %04x\n\r",str,nr&0xffff); printk("EIP:\t%04x:%p\nEFLAGS:\t%p\nESP:\t%04x:%p\n", esp[1],esp[0],esp[2],esp[4],esp[3]); //打印棧中的一些寄存器 printk("fs: %04x\n",_fs()); printk("base: %p, limit: %p\n",get_base(current->ldt[1]),get_limit(0x17)); if (esp[4] == 0x17) { printk("Stack: "); for (i=0;i<4;i++) printk("%p ",get_seg_long(0x17,i+(long *)esp[3])); printk("\n"); } str(i); printk("Pid: %d, process nr: %d\n\r",current->pid,0xffff & i); for(i=0;i<10;i++) printk("%02x ",0xff & get_seg_byte(esp[1],(i+(char *)esp[0]))); printk("\n\r"); do_exit(11); /* play segment exception */ } //打印當(dāng)前棧中的內(nèi)容trap_init分析
set_trap_gate:優(yōu)先級為0 ,設(shè)置權(quán)限較高,只能由用戶程序調(diào)用。
set_system_gate :
設(shè)置權(quán)限較低,用戶和系統(tǒng)所有進(jìn)程調(diào)用。
system_call.s分析
操作系統(tǒng)的進(jìn)程管理
系統(tǒng)時(shí)間:CPU內(nèi)部有一個(gè)RTC(定時(shí)器),運(yùn)行時(shí)調(diào)用mktime函數(shù),算出時(shí)間差。
給MKTIME函數(shù)傳來的時(shí)間結(jié)構(gòu)體賦值是由初始化時(shí)間從RTC中讀出的:
#include <time.h> /* * This isn't the library routine, it is only used in the kernel. * as such, we don't care about years<1970 etc, but assume everything * is ok. Similarly, TZ etc is happily ignored. We just do everything * as easily as possible. Let's find something public for the library * routines (although I think minix times is public). */ /* * PS. I hate whoever though up the year 1970 - couldn't they have gotten * a leap-year instead? I also hate Gregorius, pope or no. I'm grumpy. */ #define MINUTE 60 #define HOUR (60*MINUTE) #define DAY (24*HOUR) #define YEAR (365*DAY) /* interestingly, we assume leap-years */ static int month[12] = { 0, DAY*(31), DAY*(31+29), DAY*(31+29+31), DAY*(31+29+31+30), DAY*(31+29+31+30+31), DAY*(31+29+31+30+31+30), DAY*(31+29+31+30+31+30+31), DAY*(31+29+31+30+31+30+31+31), DAY*(31+29+31+30+31+30+31+31+30), DAY*(31+29+31+30+31+30+31+31+30+31), DAY*(31+29+31+30+31+30+31+31+30+31+30) }; long kernel_mktime(struct tm * tm) { long res; int year; year = tm->tm_year - 70; /* magic offsets (y+1) needed to get leapyears right.*/ res = YEAR*year + DAY*((year+1)/4); res += month[tm->tm_mon]; /* and (y+2) here. If it wasn't a leap-year, we have to adjust */ if (tm->tm_mon>1 && ((year+2)%4)) res -= DAY; res += DAY*(tm->tm_mday-1); res += HOUR*tm->tm_hour; res += MINUTE*tm->tm_min; res += tm->tm_sec; return res; }main.c中time_init
static void time_init(void) { struct tm time; do { time.tm_sec = CMOS_READ(0); time.tm_min = CMOS_READ(2); time.tm_hour = CMOS_READ(4); time.tm_mday = CMOS_READ(7); time.tm_mon = CMOS_READ(8); time.tm_year = CMOS_READ(9); } while (time.tm_sec != CMOS_READ(0)); BCD_TO_BIN(time.tm_sec); BCD_TO_BIN(time.tm_min); BCD_TO_BIN(time.tm_hour); BCD_TO_BIN(time.tm_mday); BCD_TO_BIN(time.tm_mon); BCD_TO_BIN(time.tm_year); time.tm_mon--; startup_time = kernel_mktime(&time); }小結(jié)
內(nèi)核源碼的時(shí)間片設(shè)計(jì)是很精妙的,對操作系統(tǒng)的分析同時(shí)也需要計(jì)算機(jī)組成原理的知識,硬件的理解以及利用算法如何更好的為用戶和上層軟件服務(wù)。
最后
點(diǎn)擊查看【網(wǎng)絡(luò)安全學(xué)習(xí)資料·攻略】獲取相關(guān)書籍與工具
總結(jié)
以上是生活随笔為你收集整理的【网络安全】Linux内核部分文件分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用WebStor快速检查你组织网络中的
- 下一篇: Linux中如何使用Htop监控工具?【