Q:通常情况下选择子中的RPL与对应描述符中的DPL相同,那么是否可以取缔RPL?

RPL是保证内核数据安全的关键要素之一;在内核代码中有决定性作用,绝对不能取缔

A.获取操作系统的内核

该实验主要通过调用门调用将内核数据进行打印

具体的实现代码如下

%include "inc.asm"

org 0x9000

jmp ENTRY_SEGMENT

[section .gdt]

; GDT definition

; 段基址, 段界限, 段属性

GDT_ENTRY : Descriptor 0, 0, 0

CODE32_DESC : Descriptor 0, Code32SegLen - 1, DA_C + DA_32 + DA_DPL3

VIDEO_DESC : Descriptor 0xB8000, 0x07FFF, DA_DRWA + DA_32 + DA_DPL3

DATA32_KERNEL_DESC : Descriptor 0, Data32KernelSegLen - 1, DA_DRW + DA_32 + DA_DPL0

DATA32_USER_DESC : Descriptor 0, Data32UserSegLen - 1, DA_DRW + DA_32 + DA_DPL3

STACK32_KERNEL_DESC : Descriptor 0, TopOfKernelStack32, DA_DRW + DA_32 + DA_DPL0

STACK32_USER_DESC : Descriptor 0, TopOfUserStack32, DA_DRW + DA_32 + DA_DPL3

TSS_DESC : Descriptor 0, TSSLen - 1, DA_386TSS + DA_DPL0

FUNCTION_DESC : Descriptor 0, FunctionSegLen - 1, DA_C + DA_32 + DA_DPL0;内核函数段

; Call Gate

; 选择子, 偏移, 参数个数, 属性

FUNC_GETKERNELDATA_DESC : Gate FunctionSelector, GetKernelData, 0, DA_386CGate + DA_DPL3

; GDT end

GdtLen equ $ - GDT_ENTRY

GdtPtr:

dw GdtLen - 1

dd 0

; GDT Selector

Code32Selector equ (0x0001 << 3) + SA_TIG + SA_RPL3

VideoSelector equ (0x0002 << 3) + SA_TIG + SA_RPL3

KernelData32Selector equ (0x0003 << 3) + SA_TIG + SA_RPL0

UserData32Selector equ (0x0004 << 3) + SA_TIG + SA_RPL3

KernelStack32Selector equ (0x0005 << 3) + SA_TIG + SA_RPL0

UserStack32Selector equ (0x0006 << 3) + SA_TIG + SA_RPL3

TSSSelector equ (0x0007 << 3) + SA_TIG + SA_RPL0

FunctionSelector equ (0x0008 << 3) + SA_TIG + SA_RPL0

; Gate Selector

GetKernelDataSelector equ (0x0009 << 3) + SA_TIG + SA_RPL3

; end of [section .gdt]

TopOfStack16 equ 0x7c00

[section .s16]

[bits 16]

ENTRY_SEGMENT:

mov ax, cs

mov ds, ax

mov es, ax

mov ss, ax

mov sp, TopOfStack16

; initialize GDT for 32 bits code segment

mov esi, CODE32_SEGMENT

mov edi, CODE32_DESC

call InitDescItem

mov esi, DATA32_KERNEL_SEGMENT

mov edi, DATA32_KERNEL_DESC

call InitDescItem

mov esi, DATA32_USER_SEGMENT

mov edi, DATA32_USER_DESC

call InitDescItem

mov esi, STACK32_KERNEL_SEGMENT

mov edi, STACK32_KERNEL_DESC

call InitDescItem

mov esi, STACK32_USER_SEGMENT

mov edi, STACK32_USER_DESC

call InitDescItem

mov esi, FUNCTION_SEGMENT

mov edi, FUNCTION_DESC

call InitDescItem

mov esi, TSS_SEGMENT

mov edi, TSS_DESC

call InitDescItem

; initialize GDT pointer struct

mov eax, 0

mov ax, ds

shl eax, 4

add eax, GDT_ENTRY

mov dword [GdtPtr + 2], eax

; 1. load GDT

lgdt [GdtPtr]

; 2. close interrupt

cli

; 3. open A20

in al, 0x92

or al, 00000010b

out 0x92, al

; 4. enter protect mode

mov eax, cr0

or eax, 0x01

mov cr0, eax

; 5. load TSS

mov ax, TSSSelector

ltr ax

; 6. jump to 32 bits code

;jmp word Code32Selector : 0

push UserStack32Selector

push TopOfUserStack32

push Code32Selector

push 0

retf

; esi --> code segment label

; edi --> descriptor label

InitDescItem:

push eax

mov eax, 0

mov ax, cs

shl eax, 4

add eax, esi

mov word [edi + 2], ax

shr eax, 16

mov byte [edi + 4], al

mov byte [edi + 7], ah

pop eax

ret

[section .kdat]

[bits 32]

DATA32_KERNEL_SEGMENT:

KDAT db "Kernel Data", 0

KDAT_LEN equ $ - KDAT

KDAT_OFFSET equ KDAT - $$

Data32KernelSegLen equ $ - DATA32_KERNEL_SEGMENT

[section .udat]

[bits 32]

DATA32_USER_SEGMENT:

UDAT times 16 db 0

UDAT_LEN equ $ - UDAT

UDAT_OFFSET equ UDAT - $$

Data32UserSegLen equ $ - DATA32_USER_SEGMENT

[section .tss]

[bits 32]

TSS_SEGMENT:

dd 0

dd TopOfKernelStack32 ; 0

dd KernelStack32Selector ;

dd 0 ; 1

dd 0 ;

dd 0 ; 2

dd 0 ;

times 4 * 18 dd 0

dw 0

dw $ - TSS_SEGMENT + 2

db 0xFF

TSSLen equ $ - TSS_SEGMENT

[section .s32]

[bits 32]

CODE32_SEGMENT:

mov ax, VideoSelector

mov gs, ax

mov ax, UserData32Selector

mov es, ax

mov di, UDAT_OFFSET

call GetKernelDataSelector : 0

mov ax, UserData32Selector ; eip ==> 0x17

mov ds, ax

mov ebp, UDAT_OFFSET

mov bx, 0x0C

mov dh, 12

mov dl, 33

call PrintString

jmp $

; ds:ebp --> string address

; bx --> attribute

; dx --> dh : row, dl : col

PrintString:

push ebp

push eax

push edi

push cx

push dx

print:

mov cl, [ds:ebp]

cmp cl, 0

je end

mov eax, 80

mul dh

add al, dl

shl eax, 1

mov edi, eax

mov ah, bl

mov al, cl

mov [gs:edi], ax

inc ebp

inc dl

jmp print

end:

pop dx

pop cx

pop edi

pop eax

pop ebp

ret

Code32SegLen equ $ - CODE32_SEGMENT

[section .func]

[bits 32]

FUNCTION_SEGMENT:

; es:di --> data buffer

GetKernelDataFunc:

mov ax, KernelData32Selector

mov ds, ax

mov si, KDAT_OFFSET

mov cx, KDAT_LEN

call KMemCpy

retf

; ds:si --> source

; es:di --> destination

; cx --> length

KMemCpy:

cmp si, di

ja btoe

add si, cx

add di, cx

dec si

dec di

jmp etob

btoe:

cmp cx, 0

jz done

mov al, [ds:si]

mov byte [es:di], al

inc si

inc di

dec cx

jmp btoe

etob:

cmp cx, 0

jz done

mov al, [ds:si]

mov byte [es:di], al

dec si

dec di

dec cx

jmp etob

done:

ret

GetKernelData equ GetKernelDataFunc - $$

FunctionSegLen equ $ - FUNCTION_SEGMENT

[section .kgs]

[bits 32]

STACK32_KERNEL_SEGMENT:

times 256 db 0

Stack32KernelSegLen equ $ - STACK32_KERNEL_SEGMENT

TopOfKernelStack32 equ Stack32KernelSegLen - 1

[section .ugs]

[bits 32]

STACK32_USER_SEGMENT:

times 256 db 0c

Stack32UserSegLen equ $ - STACK32_USER_SEGMENT

TopOfUserStack32 equ Stack32UserSegLen - 1

实现结果

从结果中可以看到内核数据被用户进行了拷贝,导致内核数据不安全

B.对上述出现的错误进行改进

初步的解决方法-提出一个检查函数

1.获取段寄存器中RPL的值

2.判断RPL的值是否为SA_RPL0;true则为检查通过,可以继续访问数据,如果为false,特权级较低,出发异常

具体实现是在[section .func] [bits 32]中定义一个检查函数,并在拷贝之前将其进行调用,对RPL的值进行判断

代码如下

[section .func]

[bits 32]

FUNCTION_SEGMENT:

; es:di --> data buffer

GetKernelDataFunc:

mov cx, [esp + 4]

and cx, 0x0003

mov ax, es

and ax, 0xFFFC

or ax, cx

mov es, ax

mov ax, KernelData32Selector

mov ds, ax

mov si, KDAT_OFFSET

mov cx, KDAT_LEN

call KMemCpy

retf

; ds:si --> source

; es:di --> destination

; cx --> length

KMemCpy:

mov ax, es

call CheckRPL;进行调用

cmp si, di

ja btoe

add si, cx

add di, cx

dec si

dec di

jmp etob

btoe:

cmp cx, 0

jz done

mov al, [ds:si]

mov byte [es:di], al

inc si

inc di

dec cx

jmp btoe

etob:

cmp cx, 0

jz done

mov al, [ds:si]

mov byte [es:di], al

dec si

dec di

dec cx

jmp etob

done:

ret

; ax --> selector value

;检查函数

CheckRPL:

and ax, 0x0003;进行与操作

cmp ax, SA_RPL0;进行比较操作

jz valid;进行判断跳转

mov ax, 0

mov fs, ax

mov byte [fs:0], 0;触发异常

代码运行结果

从运行结果可以看出,代码没有对内核数据进行拷贝,同时检查函数起到作用, 对RPL的值进行判断,并触发异常进行打印。但是对用户的选择子RPL的值进行修改伪造,将其改为0,又可以将内核数据进行拷贝。

C.用户程序可以通过伪造的选择子中的RPL的值,从而绕开安全检查的机制,在这里需要提出新的解决方案

1.在栈中获取函数远调用前CS寄存器的值(请求者)

2.从之前CS寄存器的值获取RPL

3.用RPL更新到数据缓冲区对应的段寄存器中

4.实验检查函数CheckRPL对段寄存器进行安全检查

代码

%include "inc.asm"

org 0x9000

jmp ENTRY_SEGMENT

[section .gdt]

; GDT definition

; 段基址, 段界限, 段属性

GDT_ENTRY : Descriptor 0, 0, 0

CODE32_DESC : Descriptor 0, Code32SegLen - 1, DA_C + DA_32 + DA_DPL3

VIDEO_DESC : Descriptor 0xB8000, 0x07FFF, DA_DRWA + DA_32 + DA_DPL3

DATA32_KERNEL_DESC : Descriptor 0, Data32KernelSegLen - 1, DA_DRW + DA_32 + DA_DPL0

DATA32_USER_DESC : Descriptor 0, Data32UserSegLen - 1, DA_DRW + DA_32 + DA_DPL3

STACK32_KERNEL_DESC : Descriptor 0, TopOfKernelStack32, DA_DRW + DA_32 + DA_DPL0

STACK32_USER_DESC : Descriptor 0, TopOfUserStack32, DA_DRW + DA_32 + DA_DPL3

TSS_DESC : Descriptor 0, TSSLen - 1, DA_386TSS + DA_DPL0

FUNCTION_DESC : Descriptor 0, FunctionSegLen - 1, DA_C + DA_32 + DA_DPL0;内核函数段

; Call Gate

; 选择子, 偏移, 参数个数, 属性

FUNC_GETKERNELDATA_DESC : Gate FunctionSelector, GetKernelData, 0, DA_386CGate + DA_DPL3

; GDT end

GdtLen equ $ - GDT_ENTRY

GdtPtr:

dw GdtLen - 1

dd 0

; GDT Selector

Code32Selector equ (0x0001 << 3) + SA_TIG + SA_RPL3

VideoSelector equ (0x0002 << 3) + SA_TIG + SA_RPL3

KernelData32Selector equ (0x0003 << 3) + SA_TIG + SA_RPL0

UserData32Selector equ (0x0004 << 3) + SA_TIG + SA_RPL3

KernelStack32Selector equ (0x0005 << 3) + SA_TIG + SA_RPL0

UserStack32Selector equ (0x0006 << 3) + SA_TIG + SA_RPL3

TSSSelector equ (0x0007 << 3) + SA_TIG + SA_RPL0

FunctionSelector equ (0x0008 << 3) + SA_TIG + SA_RPL0

; Gate Selector

GetKernelDataSelector equ (0x0009 << 3) + SA_TIG + SA_RPL3

; end of [section .gdt]

TopOfStack16 equ 0x7c00

[section .s16]

[bits 16]

ENTRY_SEGMENT:

mov ax, cs

mov ds, ax

mov es, ax

mov ss, ax

mov sp, TopOfStack16

; initialize GDT for 32 bits code segment

mov esi, CODE32_SEGMENT

mov edi, CODE32_DESC

call InitDescItem

mov esi, DATA32_KERNEL_SEGMENT

mov edi, DATA32_KERNEL_DESC

call InitDescItem

mov esi, DATA32_USER_SEGMENT

mov edi, DATA32_USER_DESC

call InitDescItem

mov esi, STACK32_KERNEL_SEGMENT

mov edi, STACK32_KERNEL_DESC

call InitDescItem

mov esi, STACK32_USER_SEGMENT

mov edi, STACK32_USER_DESC

call InitDescItem

mov esi, FUNCTION_SEGMENT

mov edi, FUNCTION_DESC

call InitDescItem

mov esi, TSS_SEGMENT

mov edi, TSS_DESC

call InitDescItem

; initialize GDT pointer struct

mov eax, 0

mov ax, ds

shl eax, 4

add eax, GDT_ENTRY

mov dword [GdtPtr + 2], eax

; 1. load GDT

lgdt [GdtPtr]

; 2. close interrupt

cli

; 3. open A20

in al, 0x92

or al, 00000010b

out 0x92, al

; 4. enter protect mode

mov eax, cr0

or eax, 0x01

mov cr0, eax

; 5. load TSS

mov ax, TSSSelector

ltr ax

; 6. jump to 32 bits code

;jmp word Code32Selector : 0

push UserStack32Selector

push TopOfUserStack32

push Code32Selector

push 0

retf

; esi --> code segment label

; edi --> descriptor label

InitDescItem:

push eax

mov eax, 0

mov ax, cs

shl eax, 4

add eax, esi

mov word [edi + 2], ax

shr eax, 16

mov byte [edi + 4], al

mov byte [edi + 7], ah

pop eax

ret

[section .kdat]

[bits 32]

DATA32_KERNEL_SEGMENT:

KDAT db "Kernel Data", 0

KDAT_LEN equ $ - KDAT

KDAT_OFFSET equ KDAT - $$

Data32KernelSegLen equ $ - DATA32_KERNEL_SEGMENT

[section .udat]

[bits 32]

DATA32_USER_SEGMENT:

UDAT times 16 db 0

UDAT_LEN equ $ - UDAT

UDAT_OFFSET equ UDAT - $$

Data32UserSegLen equ $ - DATA32_USER_SEGMENT

[section .tss]

[bits 32]

TSS_SEGMENT:

dd 0

dd TopOfKernelStack32 ; 0

dd KernelStack32Selector ;

dd 0 ; 1

dd 0 ;

dd 0 ; 2

dd 0 ;

times 4 * 18 dd 0

dw 0

dw $ - TSS_SEGMENT + 2

db 0xFF

TSSLen equ $ - TSS_SEGMENT

[section .s32]

[bits 32]

CODE32_SEGMENT:

mov ax, VideoSelector

mov gs, ax

mov ax, UserData32Selector

mov es, ax

mov di, UDAT_OFFSET

call GetKernelDataSelector : 0

mov ax, UserData32Selector ; eip ==> 0x17

mov ds, ax

mov ebp, UDAT_OFFSET

mov bx, 0x0C

mov dh, 12

mov dl, 33

call PrintString

jmp $

; ds:ebp --> string address

; bx --> attribute

; dx --> dh : row, dl : col

PrintString:

push ebp

push eax

push edi

push cx

push dx

print:

mov cl, [ds:ebp]

cmp cl, 0

je end

mov eax, 80

mul dh

add al, dl

shl eax, 1

mov edi, eax

mov ah, bl

mov al, cl

mov [gs:edi], ax

inc ebp

inc dl

jmp print

end:

pop dx

pop cx

pop edi

pop eax

pop ebp

ret

Code32SegLen equ $ - CODE32_SEGMENT

[section .func]

[bits 32]

FUNCTION_SEGMENT:

; es:di --> data buffer

GetKernelDataFunc:

mov cx, [esp + 4]

and cx, 0x0003

mov ax, es

and ax, 0xFFFC

or ax, cx

mov es, ax

mov ax, KernelData32Selector

mov ds, ax

mov si, KDAT_OFFSET

mov cx, KDAT_LEN

call KMemCpy

retf

; ds:si --> source

; es:di --> destination

; cx --> length

KMemCpy:

mov ax, es

call CheckRPL;进行调用

cmp si, di

ja btoe

add si, cx

add di, cx

dec si

dec di

jmp etob

btoe:

cmp cx, 0

jz done

mov al, [ds:si]

mov byte [es:di], al

inc si

inc di

dec cx

jmp btoe

etob:

cmp cx, 0

jz done

mov al, [ds:si]

mov byte [es:di], al

dec si

dec di

dec cx

jmp etob

done:

ret

; ax --> selector value

;检查函数

CheckRPL:

and ax, 0x0003;进行与操作

cmp ax, SA_RPL0;进行比较操作

jz valid;进行判断跳转

mov ax, 0

mov fs, ax

mov byte [fs:0], 0;触发异常

valid:

ret

GetKernelData equ GetKernelDataFunc - $$

FunctionSegLen equ $ - FUNCTION_SEGMENT

[section .kgs]

[bits 32]

STACK32_KERNEL_SEGMENT:

times 256 db 0

Stack32KernelSegLen equ $ - STACK32_KERNEL_SEGMENT

TopOfKernelStack32 equ Stack32KernelSegLen - 1

[section .ugs]

[bits 32]

STACK32_USER_SEGMENT:

times 256 db 0

Stack32UserSegLen equ $ - STACK32_USER_SEGMENT

TopOfUserStack32 equ Stack32UserSegLen - 1

首先通过反编译在call GetKernelDataSelector : 0跳转处设置断点,接下来对通用寄存器eip寄存器进行查看,由92a8到92af之间的差值为7,可以推断mov ax, UserData32Selector ; eip ==> 0x17,该处是返回地址。同时0x17会入栈,cs也会入栈,对寄存器进行查看,此时cs寄存器为0x0b,接下来对栈顶的6个字节进行查看,发现0x17与cs寄存器都入栈,再继续操作,触发异常,过程如图所示

小结

1.RPL是保证内核数据安全的关键要素之一

2.内核代码可通过追踪真实请求者特权级判断操作合法性

3.进行函数远调用时,真实请求者的选择子就会存储于栈中

4.通过提取真实特权级能够保证内核数据安全

特权级 linux,操作系统-特权级与内核安全示例相关推荐

  1. Linux操作系统, 构建自己的内核-陈屹-专题视频课程

    Linux操作系统, 构建自己的内核-237人已学习 课程介绍         本课程的目的是遵从人的认知方式,由感性到理性,由小到大,由浅入深,从一个最简单的引导扇区开始,逐步增添代码,最后形成一个 ...

  2. X86汇编语言从实模式到保护模式16:特权级和特权级保护

    目录 1. 特权级保护机制 1.1 基础段保护机制的不足 1.2 特权级划分 1.3 特权级的表示 1.3.1 当前特权级CPL 1.3.2 描述符特权级DPL 1.3.3 请求特权级RPL 1.4 ...

  3. Linux操作系统原理与应用02:内存寻址

    目录 1. 内存寻址 1.1 X86寻址技术演变 1.1.1 8086引入段机制 1.1.2 80286引入保护模式 1.1.3 80386在段寄存器上构建保护模式 1.2 80x86寄存器简介 1. ...

  4. 【OS学习笔记】二十四 保护模式七:调用门与依从的代码段----特权级保护

    学习交流加 个人qq: 1126137994 个人微信: liu1126137994 学习交流资源分享qq群: 962535112 上一篇文章学习了保护模式下的任务与任务隔离,以及简单介绍了保护模式下 ...

  5. 【OS学习笔记】二十三 保护模式七:保护模式下任务的隔离与任务的特权级概念

    上一篇文章学习了保护模式下操作系统内核如何加载程序并运行:点击链接查看上一篇文章 本篇文章接着上一篇文章学习保护模式下任务的隔离. 包括以下学习内容: 任务的全局空间和局部空间 任务的TSS 任务的L ...

  6. 《Ubuntu Linux操作系统》读书笔记

    文|Seraph 一.Ubuntu安装与基本使用 Linux是一种起源于UNIX,以可移植操作系统接口(Portable Operating System Interface, POSIX)标准为框架 ...

  7. 如何查看Linux操作系统版本

    参考地址:http://www.ggat.cn/newsInfo.html/71 如何查看Linux操作系统版本 1. 查看内核版本命令: [root@tg ]# cat /proc/version ...

  8. Linux 操作系统原理 — 内核态与用户态

    目录 文章目录 目录 Linux 的内核态与用户态 系统调用(System Call) Shell 用户态和内核态的切换 进程的用户空间和内核空间的内存布局 内核空间 用户空间 Linux 的内核态与 ...

  9. 深入理解Linux内核(一)——Linux操作系统基础概念

    文章目录 前言 操作系统基本概念 多用户系统 用户和组 进程 内核体系结构 Unix文件系统概述 文件 硬链接和软链接 文件类型 文件描述符与索引节点 访问权限和文件模式 文件操作的系统调用 打开文件 ...

最新文章

  1. MDK 工程宏定义的应用
  2. python web框架互相融合, Pyramid或取代Django
  3. 银行祖传系统重构实例:创立12年,只支持Python 2,跑着500多个应用程序
  4. php万年历上个月下个月,php 万年历
  5. 二、python框架相关知识体系
  6. 【转】Oracle当中扫描数据的方法
  7. [转载] pythonpandas读取csv文件最后一行_简单小案例(一):使用Pandas在Python中读取和写入CSV文件...
  8. python怎么计算圆上任意两点的距离_圆周上两点距离-python
  9. 好书推荐之【代码整洁之道】
  10. java 8 排序_java8——排序
  11. linux latex 英文字体,LaTeX 中的一些英文字体
  12. redis 集群详解及搭建过程
  13. 关于app的几个核心功能的设计想法
  14. 万物互联时代到来,锐捷发布场景化无线零漫游方案
  15. mysql主从配置duxi_mysql主从配置
  16. 省对应的市区经纬度信息
  17. 第一卷清晨的帝国 第一百四十三章 辩难始
  18. SQL Server的安装和要求
  19. Java 碰壁小球游戏实例教程
  20. PHP框架之ThinkPHP

热门文章

  1. unity导入jslib问题
  2. 操作系统----文件管理 参考王道操作系统与小林coding图解操作系统
  3. “中国网安•深思杯”第八届山东省大学生网络安全技能大赛选拔赛Misc题目Write up
  4. 生活中的技术——从数学的角度量化计算征友的满意度,just funny:)
  5. 【python脚本】-刷CSDN博客流量
  6. Android逆向之旅---微信封了抖音分享功能,而我要把短视频分享到朋友圈!
  7. mSystems:“地球第四极”马里亚纳海沟极端环境中神秘的底栖病毒群落及其生态功能...
  8. 国内银行CNAPS CODE 查询 苹果开发者,应用内购,需要填写税务相关信息必须的...
  9. 【VMware vSAN 7.0】5.5 配置 vSAN 集群的许可证设置—我们有软硬件解决方案
  10. 微型计算机1MB字节,1MB等于多少字节?