注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

编程学习

我的网上家园

 
 
 

日志

 
 

15章子程序练习: 加载用户程序子例程  

2013-04-23 17:24:55|  分类: 《x86汇编语言: |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
;加载用户程序并初始化: load_relocate_program
;所在位置: 内核代码段,需要调用公共例程段的例程以及内核代码段的例程
;入口参数: push 用户程序起始LBA扇区,push 任务控制块起始线性地址.
;返回: 无

用到的公共例程段例程:
----------------------------------------------------------------------------------------------------------------------------------------------------------------
公共例程段选择子: sys_routine_seg_sel

硬盘扇区读取例程: read_hard_disk_0 [入口参数 EAX=逻辑扇区号 DS:EBX=目标缓冲区地址]

内存分配例程: allocate_memory [入口参数 ECX=希望分配的字节数 返回: ECX=起始线性地址]

安装描述符例程: set_up_gdt_descriptor [EDX:EAX=描述符 输出:CX=描述符选择子]

生成描述符例程: make_seg_descriptor [EAX=线性基地址 EBX=段界限 ECX=属性 返回: EDX:EAX=描述符]
----------------------------------------------------------------------------------------------------------------------------------------------------------------

用到的内核代码段例程:
----------------------------------------------------------------------------------------------------------------------------------------------------------------
在LDT内安装描述符:     fill_descriptor_in_ldt [入口参数 EDX:EAX=描述符 EBX=TCB基地址]  [输出 CX=描述符选择子]
备注: 例程返回的选择子,RPL=0.

任务控制块结构:
---------------------------------------------------------------------------------------
TCB结构:
0x44: 头部选择子         (2字节)
0x40: 2特权级栈的初始ESP (4字节)
0x3E: 2特权级栈选择子 (2字节)
0x3A: 2特权级栈基地址 (4字节)
0x36: 2特权级栈以4KB为单位的长度 (4字节)
0x32: 1特权级栈的初始ESP (4字节)
0x30: 1特权级栈选择子 (2字节)
0x2C: 1特权级栈基地址 (4字节)
0x28: 1特权级栈以4KB为单位的长度 (4字节)
0x24: 0特权级栈的初始ESP (4字节)
0x22: 0特权级栈选择子         (2字节)
0x1E: 0特权级栈基地址 (4字节)
0x1A: 0特权级栈以4KB为单位的长度 (4字节)
0x18: TSS选择子         (2字节)
0x14: TSS基地址         (4字节)
0x12: TSS界限值         (2字节)
0x10: LDT选择子         (2字节)
0x0C: LDT基地址         (4字节)
0x0A: LDT当前界限值 (2字节)
0x06: 程序加载基地址 (4字节)
0x04: 任务状态                 (2字节)
0x00: 下一个TCB基地址 (4字节)

TSS结构:
---------------------------------------------------------------------------------------
102: I/O映射基地址 (2字节) 
100: T         (1字节)
 96: LDT段选择子 (2字节)
 92: GS (2字节)
 88: FS (2字节)
 84: DS (2字节)
 80: SS (2字节)
 76: CS (2字节)
 72: ES (2字节)
 68: EDI (4字节)
 64: ESI (4字节)
 60: EBP (4字节)
 56: ESP (4字节)
 52: EBX (4字节)
 48: EDX (4字节)
 44: ECX (4字节)
 40: EAX (4字节)
 36: EFLAGS (4字节)
 32: EIP (4字节)
 28: CR3(PDBR) (4字节)
 24: SS2 (2字节)
 20: ESP2 (4字节)
 16: SS1 (2字节)
 12: ESP1 (4字节)
 8 : SS0 (2字节)
 4 : ESP0 (4字节)
 0 : 前一个任务的指针(2字节)

用户程序结构:
---------------------------------------------------------------------------------------
dd 用户程序总长度 0x0
dd 用户程序头部长度 0x4
dd 用户栈选择子 0x8 [初始值为0,用于接收栈段选择子]
dd 用户栈大小(4KB为单位) 0xc
dd 用户程序入口点偏移量 0x10
dd 用户程序代码段汇编地址 0x14 [用于接收用户代码段选择子]
dd 用户程序代码段长度 0x18
dd 用户程序数据段汇编地址 0x1c [用于接收用户数据段选择子]
dd 用户程序数据段长度 0x20
符号地址检索表条目: 0x24
符号地址检索表: 0x28
==================================================================================================
以下是加载用户程序子过程全部源代码:


load_relocate_program:

pushad
push ds
push es

mov ebp,esp

mov eax,core_data_seg_sel
mov ds,eax

mov ebx,core_buf ;内核缓冲区
mov eax,[ebp+12*4] ;EAX=用户程序起始LBA扇区
call sys_routine_seg_sel:read_hard_disk_0 ;[入口参数 EAX=逻辑扇区号 DS:EBX=目标缓冲区地址]

mov eax,[core_buf] ;用户程序总大小
mov ebx,eax
and ebx,0xfffffe00
add ebx,512
test eax,0x1ff
cmovnz eax,ebx ;使用户程序变成整数个扇区大小
mov ecx,eax
call sys_routine_seg_sel:allocate_memory ;[入口参数 ECX=希望分配的字节数 返回: ECX=起始线性地址]

mov esi,[ebp+11*4] ;ESI=TCB起始线性地址
mov ebx,mem_0_4_gb_seg_sel
mov ds,ebx ;切换DS到4GB内存段
mov [esi+0x06],ecx ;登记程序加载地址到TCB

xor edx,edx
mov ebx,512
div ebx ;EDX:EAX/EBX=> EAX=扇区数
mov ebx,ecx ;用户程序加载地址
mov ecx,eax
mov eax,[ebp+12*4] ;EAX=用户程序起始LBA扇区
.load:
call sys_routine_seg_sel:read_hard_disk_0 ;[入口参数 EAX=逻辑扇区号 DS:EBX=目标缓冲区地址]
inc eax
loop .load ;循环读取用户程序

mov ecx,8*7 ;头部段+代码段+数据段+0 1 2 3栈段,7个描述符即可.
mov ebx,ecx
dec ebx

mov word [esi+0x0a],0xffff ;登记LDT的初始界限值到TCB.
; < fill_descriptor_in_ldt例程依赖TCB中的初始界限值,初始界限值必须为0xffff >

call sys_routine_seg_sel:allocate_memory
mov [esi+0x0c],ecx ;LDT基地址
mov eax,ecx
mov ecx,0x00408200 ;LDT属性 
call sys_routine_seg_sel:make_seg_descriptor 
;[EAX=线性基地址 EBX=段界限 ECX=属性 返回: EDX:EAX=描述符]
call sys_routine_seg_sel:set_up_gdt_descriptor
;[EDX:EAX=描述符 输出:CX=描述符选择子]
mov [esi+0x10],cx ;登记LDT选择子到TCB.

mov edi,[esi+0x06] ;从TCB中取出用户程序加载地址 EDI=用户程序加载地址

mov eax,edi
mov ebx,[edi+0x4] ;头部长度
dec ebx
mov ecx,0x0040f200
call sys_routine_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt ;[入口参数 EDX:EAX=描述符 EBX=TCB基地址][输出 CX=描述符选择子]
or cx,3 ;fill_descriptor_in_ldt例程返回的选择子RPL=0,这里要变成3
mov [esi+0x44],cx ;登记头部段选择子到TCB
mov [edi+0x4],cx ;登记头部段选择子到用户头部

mov eax,[edi+0x14] ;用户程序代码段起始汇编地址
add eax,edi
mov ebx,[edi+0x18]
dec ebx
mov ecx,0x0040f800
call sys_routine_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt
or cx,3 ;使代码段选择子RPL=3
mov [edi+0x14],cx ;更新用户程序代码段选择子到用户头部段,替换原代码段的起始汇编值

mov eax,[edi+0x1c] ;用户程序数据段起始汇编地址
add eax,edi
mov ebx,[edi+0x20]
dec ebx
mov ecx,0x0040f200
call sys_routine_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt
or cx,3 ;使数据段选择子RPL=3
mov [edi+0x1c],cx ;更新用户程序数据段选择子到用户头部段,替换数据段的起始汇编

mov eax,[edi+0xc] ;用户栈大小,4KB单位
mov ebx,0xfffff
sub ebx,eax ;栈界限
mov ecx,4096
mul ecx
mov ecx,eax ;得出以字节为单位的大小.
call sys_routine_seg_sel:allocate_memory ;申请内存
add eax,ecx ;EAX=EAX+ECX=申请到内存的高端地址
mov ecx,0x00c0f600
call sys_routine_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt ;安装3特权级栈到LDT
or cx,3 ;使栈选择子RPL=3
mov [edi+0x8],cx ;更新用户程序栈段选择子到用户头部段

;创建0特权级栈
mov eax,1
mov [esi+0x1a],eax ;登记0特权级栈以4KB为单位的长度到TCB
mov ebx,0xfffff
sub ebx,eax
mov ecx,4096
mul ecx
mov ecx,eax
call sys_routine_seg_sel:allocate_memory ;申请内存创建0特权级栈
add eax,ecx
mov [esi+0x1e],eax ;登记0特权级栈基地址到TCB
mov ecx,0x00c09600
call sys_routine_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt ;例程返回的选择子RPL=0
mov [esi+0x22],cx ;登记0特权级栈选择子到TCB
mov dword [esi+0x24],0 ;登记ESP0到TCB

;创建1特权级栈
mov eax,1
mov [esi+0x28],eax ;登记1特权级栈以4KB为单位的长度到TCB
mov ebx,0xfffff
sub ebx,eax
mov ecx,4096
mul ecx
mov ecx,eax
call sys_routine_seg_sel:allocate_memory ;申请内存创建1特权级栈
add eax,ecx
mov [esi+0x2c],eax ;登记1特权级栈基地址到TCB
mov ecx,0x00c0b600
call sys_routine_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt
or cx,1 ;使栈选择子RPL=1
mov [esi+0x30],cx ;登记1特权级栈选择子到TCB
mov dword [esi+0x32],0 ;登记ESP1到TCB

;创建2特权级栈
mov eax,1
mov [esi+0x36],eax ;登记2特权级栈以4KB为单位的长度到TCB
mov ebx,0xfffff
sub ebx,eax
mov ecx,4096
mul ecx
mov ecx,eax
call sys_routine_seg_sel:allocate_memory ;申请内存创建2特权级栈
add eax,ecx
mov [esi+0x3a],eax ;登记2特权级栈基地址到TCB
mov ecx,0x00c0d600
call sys_routine_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt
or cx,2 ;使栈选择子RPL=2
mov [esi+0x3e],cx ;登记2特权级栈选择子到TCB
mov dword [esi+0x40],0 ;登记ESP2到TCB

;处理SALT表
mov eax,core_data_seg_sel ;内核数据段选择子
mov ds,eax
mov eax,mem_0_4_gb_seg_sel ;4GB内存段选择子
mov es,eax

cld
mov ecx,[es:edi+0x24] ;用户符号数量
add edi,0x28 ;ES:EDI->U_SALT
.salt:
push ecx
mov ecx,salt_items ;内核符号数量
mov esi,salt ;DS:ESI->C_SALT 内核符号表
.c_salt:
push esi
push edi
push ecx
mov ecx,256/4
repz cmpsd
jnz .next_c_salt
mov ax,[esi+4] ;符号对应例程的调用门选择子
or ax,3 ;修改调用门选择子特权级为3
mov [es:edi-252],ax
pop ecx
pop edi
pop esi
jmp .next_usalt
.next_c_salt:
pop ecx
pop edi
pop esi
add esi,salt_item_len ;ESI指向下一个内核符号
loop .c_salt
.next_usalt:
pop ecx
add edi,256 ;指向下一个用户符号
loop .salt

mov eax,mem_0_4_gb_seg_sel
mov ds,eax
mov esi,[ebp+11*4] ;ESI=TCB起始线性地址
mov ecx,104 ;TSS=104字节
mov ebx,ecx
dec ebx
mov [esi+0x12],bx ;登记TSS界限值到TCB
call sys_routine_seg_sel:allocate_memory ;申请TSS内存.
mov [esi+0x14],ecx ;登记TSS基地址到TCB
mov eax,ecx
mov ecx,0x00408900
call sys_routine_seg_sel:make_seg_descriptor
call sys_routine_seg_sel:set_up_gdt_descriptor
;[EDX:EAX=描述符 输出:CX=描述符选择子]
;TSS描述符必须安装在GDT中.
mov [esi+0x18],cx ;登记TSS选择子到TCB.

mov edi,[esi+0x14] ;EDI指向TSS基地址.
;初始化TSS中的内容.
;mov word [edi+0],0 ;反向链=0
mov eax,[esi+0x24]
mov [edi+4],eax ;ESP0
mov ax,[esi+0x22]
mov [edi+8],ax ;SS0
mov eax,[esi+0x32]
mov [edi+12],eax ;ESP1
mov ax,[esi+0x30]
mov [edi+16],ax ;SS1
mov eax,[esi+0x40]
mov [edi+20],eax ;ESP2
mov ax,[esi+0x3e]
mov [edi+24],ax ;SS2
mov dword [edi+28],0 ;CR3
pushfd
pop eax
mov [edi+36],eax ;EFLAGS
mov dword [edi+72],0 ;ES
mov dword [edi+88],0 ;FS
mov dword [edi+92],0 ;GS
mov ax,[esi+0x10]
mov [edi+96],ax ;LDT选择子
mov byte [edi+100],0 ;T=0 
mov ax,[esi+0x12]
mov [edi+102],ax ;I/O映射基地址,无I/O映射字符串

mov esi,[esi+0x6] ;取得用户程序加载基地址
mov eax,[esi+0x10]
mov [edi+32],eax ;EIP

mov dword [edi+56],0 ;ESP=0

mov ax,[esi+0x14] ;取得用户程序代码段选择子
mov [edi+76],ax ;CS
mov ax,[esi+0x8] ;取得用户程序栈段选择子
mov [edi+80],ax ;SS
mov ax,[esi+0x04] ;取得用户程序头部段选择子
mov [edi+84],ax ;DS

pop es
pop ds
popad
ret 8

  评论这张
 
阅读(24)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017