在调试程序中,经常发现程序最后会调用到系统态的程序。这个过程是怎样的?用户空间的程序怎样进行系统调用,在此过程中是怎样进入和退出内核的。

根据运行状态和执行代码所在的内存空间的不同,CPU既可以运行于非特权的“用户态”,也可以运行于特权的“系统态”。CPU从系统空间装入用户空间是容易的,CPU可以通过一些特权指令改变其运行状态,反过来要从用户空间转到系统空间就不容易了,因为运行于用户态的CPU不能执行特权指令。

一般而言有三种方式可以使运行于用户态的CPU转入到系统态,下面只说一下Windows是如何利用IA-32处理器的SYSENTER、SYSEXIT指令实现快速系统调用的。

Windows 2000或之前的Windows不支持快速系统调用,它们只能使用INT 2E的方式进行系统调研。在Windows XP和Windows Server 2003或以后版本在启动时会通过CPUID指令检测是否支持快速系统调用。如果支持,那么Windows会使用新的方式进行系统调用。

使用快速系统调用需要如下工作:

  1. 在GDT中建立4个段描述符,分别用来提供SYSENTER指令进入内核模式时使用的代码段(CS)和栈段(SS),以及SYSEXIT指令从内核模式返回用户模式时使用的代码段(CS)和栈段(SS)。
  2. 设置专门用于系统调用的MSR寄存器,SYSENTER_EIP_MSR用于指SYSENTER指令要跳转的目标例程地址。Windows会将其设置为KiFastCallEntry的地址。SYSENTER_CS_MSR寄存器用来指定新的代码段,也就是KiFastCallEntry所在的代码段。SYSENTER_ESP_MSR用于指定新的栈指针(ESP)。
  3. 将一段名为SystemCallStub的代码复制到SharedUserData内存区,该区在被映射到每个Win32进程的进程空间中,这样当应用程序每次进行系统调用时,NtDll中的Stub函数便调用这段SystemCallStub代码。

以下以Notepad为例,看看Notepad在调用NtWriteFile时的CPU从用户态到系统态的转换过程。

使用Windbg挂着VWare用运行的Windows XP系统,在Notepad的主线程上设置ntdll!NtWriteFile的断点。

kd> !process 0 3 notepad.exe
PROCESS 81bd4658 SessionId: 0 Cid: 07a8 Peb: 7ffdf000 ParentCid: 0590
DirBase: 1a8a3000 ObjectTable: e1ae2678 HandleCount: 38.
Image: notepad.exe
VadRoot 81e1ef30 Vads 62 Clone 0 Private 169. Modified 13. Locked 0.
DeviceMap e17b2e98
Token e1b8ece8
ElapsedTime 00:01:50.327
UserTime 00:00:00.062
KernelTime 00:00:01.968
QuotaPoolUsage[PagedPool] 32228
QuotaPoolUsage[NonPagedPool] 2480
Working Set Sizes (now,min,max) (1267, 50, 345) (5068KB, 200KB, 1380KB)
PeakWorkingSetSize 1267
VirtualSize 33 Mb
PeakVirtualSize 34 Mb
PageFaultCount 1330
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 479
THREAD 81e068e8 Cid 07a8.0340 Teb: 7ffde000 Win32Thread: e176ad58 RUNNING on processor 0

kd> bp /t 81e068e8 ntdll!ntwritefile

如上设置断点后,在Windows XP中使用Notepad保存文件就会断在ntdll!ZwWriteFile。

kd> kb
ChildEBP RetAddr Args to Child
0006fa04 77e673b1 0000057c 00000000 00000000 ntdll!ZwWriteFile
0006fa64 010048e8 0000057c 00092c40 000000b8 kernel32!WriteFile+0xf7
0006fa9c 01004cb4 0000057c 000004e4 00000400 notepad!___PchSym_+0x6e8
0006fad8 01002a1f 000701c0 01009900 00099570 notepad!___PchSym_+0xab4
0006fafc 00000000 00000000 00000000 00000000 notepad!ServiceStarter+0x6b

反汇编ntdll!ZwWriteFile可以看到调用了SystemCallStub

ntdll!ZwWriteFile:
001b:77f3f010 b812010000 mov eax,112h
001b:77f3f015 ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
001b:77f3f01a ffd2 call edx
001b:77f3f01c c22400 ret 24h
001b:77f3f01f 90 nop

反汇编SharedUserData!SystemCallStub查看它的代码,它代码作用是:把栈指针(ESP)放到EDX中,可以传递参数,也作为从内核模式返回时的栈地址。再执行SYSENTER指令。

SharedUserData!SystemCallStub:
7ffe0300 8bd4 mov edx,esp
7ffe0302 0f34 sysenter
7ffe0304 c3 ret

当执行SYSENTER指令时,CPU进入系统态,并且:

  • 把寄存器SYSENTER_CS_MSR的内容复制到段寄存器CS中。
  • 把寄存器SYSENTER_EIP_MSR的内容复制到寄存器EIP中。
  • 把寄存器SYSENTER_CS_MSR的内容+8写入堆栈段寄存器SS中。
  • 把寄存器SYSENTER_ESP_MSR的内容复制到堆栈指针ESP中。

这样,只要预先设置好三个MSR寄存器的内容中,CPU在执行SYSENTER后就可以进入系统空间并从预定的地址执行程序,同时开始使用系统空间的堆栈。通过WinDbg中rdmsr命令可以查看到MSR寄存器的内容,可以看到SYSENTER_EIP_MSR指向nt!KiFastCallEntry,这就是系统空间中快速系统调用的总入口。nt!KiFastCallEntry中再进入nt!KiSystemService。

kd> rdmsr 174
msr[174] = 00000000`00000008
kd> rdmsr 175
msr[175] = 00000000`00000000
kd> rdmsr 176
msr[176] = 00000000`80599770
kd> u 80599770
nt!KiFastCallEntry:

从nt!KeServiceDescriptorTable中可以看到第112h是对应着nt!NtWriteFile。

kd> dds poi(nt!KeServiceDescriptorTable) + 112*4
8050f0f4 805e81cc nt!NtWriteFile
8050f0f8 805e8a94 nt!NtWriteFileGather
8050f0fc 80635c50 nt!NtWriteRequestData
8050f100 8065ff93 nt!NtWriteVirtualMemory

在nt!NtWriteFile设置断点就可以断下,在Windbg中使用如下命令就可以看到结果。

kd> bp /t 81e068e8 nt!NtWriteFile
kd> k
ChildEBP RetAddr
f59347ac 8059994c nt!NtWriteFile
f59347ac 7ffe0304 nt!KiSystemService+0x13b
0006fa00 77f3f01c SharedUserData!SystemCallStub+0x4
0006fa04 77e673b1 ntdll!ZwWriteFile+0xc
0006fa64 010048e8 kernel32!WriteFile+0xf7
0006fa9c 01004cb4 notepad!___PchSym_+0x6e8
0006fad8 01002a1f notepad!___PchSym_+0xab4
0006fafc 00000000 notepad!ServiceStarter+0x6b

上面列出了程序从用户空间转到系统空间的过程,那么怎么从系统空间中返回呢?堆栈指针保存在哪里?

在nt!KiFastCallEntry中在进入nt!KiSystemServic前会保存0x7ffe0304到ECX,并压入堆栈。0x7ffe0304就是用户态的返回地址,对应着SharedUserData!SystemCallStub函数中的Ret指令。

在调用返回时,会把堆栈POP到ECX中,再调用SYSEXIT。当CPU执行SYSEXIT指令时,CPU回到用户态,并且:

  • 把CS设置成(SYSENTER_CS_MSR的内容+16),这实际上是KGDT_R3_CODE。
  • 把寄存器EDX的内容复制到EIP。
  • 把SS设置成(SYSENTER_CS_MSR的内容+24),这实际上是KGDT_R3_DATA.
  • 把寄存器ECX的内容复制到ESP。

kd> u nt!KiSystemCallExit2
nt!KiSystemCallExit2:
80599ae0 5a pop edx
80599ae1 83c408 add esp,8
80599ae4 59 pop ecx
80599ae5 fb sti
80599ae6 0f35 sysexit

转载于:https://www.cnblogs.com/Quincy/archive/2010/03/27/1698604.html

用户态程序调用系统态程序-快速系统调用相关推荐

  1. [转]mpvue中的小程序调用系统自带查看图片的功能

    mpvue中的小程序调用系统自带查看图片的功能 这里举个栗子: <template><div class="keting"><div class=&q ...

  2. android系统应用程序,Android调用系统应用程序

    Android调用系统应用程序: 1.直接拨打电话: Intent callIntent = new Intent(Intent.ACTION_CALL, Uri .parse("tel:1 ...

  3. python调用系统本地程序

    1. os.system(cmdtext) os.system() 函数可以将字符串转化成命令在服务器上运行:返回执行程序的退出状态码.其原理是每一条 system 函数执行时,其会创建一个子进程在系 ...

  4. Android调用系统相机程序

    一.请求相机功能 声明你的应用依赖于相机,请在清单文件中添加 uses-feature 代码: <manifest ... ><uses-feature android:name=& ...

  5. 【Python应用】Python中调用系统应用程序

    os.system() 在shell中执行一条命令.函数原型如下: 它是最简单的调用系统应用的方式,下面是一个例子: import os import sysos.system("dir&q ...

  6. 【附源码】Java计算机毕业设计基于微信小程序停车系统(程序+LW+部署)

    项目运行 环境配置: Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclis ...

  7. linux c程序调用脚本,C程序与Lua脚本相互调用

    Lua脚本是一种可用于C程序开发/测试的工具,本篇介绍一下C程序与Lua脚本如何进行相互调用,更加详细的操作参见<Programing in Lua>.本文分为3个部分:1.Windows ...

  8. Java程序调用c语言程序

    Java程序调用自定义c语言程序 计算机中有很多的编程语言,各有各的特色和应用范围.Java混合编程就是Java程序和其它应用程序进行通讯和数据交互,比如我们都知道c语言的计算性能要比Java的略好一 ...

  9. C语言小程序——调用系统应用

    写在前面 C语言中有很多system功能函数,能够调用微软系统功能. 而system是一个C语言和C++下的函数,windows操作系统下system () 函数详解主要是在C语言中的应用,syste ...

最新文章

  1. np.asarray和np.array、np.nanmean和np.mean、np.diff、
  2. [收藏] Java 编程的动态性
  3. spring教程--事务管理
  4. [SCOI2008] 奖励关
  5. bootstrap mysql分页_bootstrap分页
  6. mysql ocp 认证 题库_MySQL 8 OCP(1Z0-908)认证考试题库原题(第10题)
  7. 关于angular2更新时机的一些发现
  8. 程序员如何在大公司做管理
  9. 基于STM32L476的锂电池SOC检测
  10. 在linux中怎么装python3环境,在Linux环境下安装Python3
  11. 学习之法 —— 套路
  12. 迅为IMX6ULL开发板点亮第一个led灯之led子系统的使用
  13. 软件设计师真题知识点笔记❀
  14. 服务器系统开机密码怎么关闭,windows server2012怎么去除开机密码
  15. 教师运用计算机技术的难点,浅谈运用电脑技术进行备课的几点优势
  16. expect spawn scp * shell路径名展开
  17. Ubuntu Kylin操作系统介绍及常用命令的使用
  18. Seastar源码阅读(三)future
  19. android 彩信处理
  20. 深入分析IBM的云计算解决方案

热门文章

  1. git 修改全局配置
  2. 【Excle数据透视表】如何创建非共享缓存的数据透视表
  3. windows资源管理器进行ftp登录下载文件报“当前的安全设置不允许从该位置下载文件”...
  4. 《电子基础与维修工具核心教程》——1.3 弱电、强电、高压电
  5. Zend Studio实现移动程序开发一体化的秘密武器——CCM
  6. 对于fmri的设计矩阵构造的一个很直观的解释-by 西南大学xulei教授
  7. ThinkPHP框架搭建网站
  8. js 运算符 || 妙用
  9. Node.js之readline模块的使用
  10. R之ddlpy函数学习[转载]