avatar

Catalog
API函数的调用过程(3环进0环)

上一篇中分析了ReadProcessMemory函数的3环部分,它实际上没有做太多工作,只是提供了一个调用0环函数的接口,今天我们接着向下分析,看看函数是如何进入0环的。

_KUSER_SHARED_DATA

_KUSER_SHARED_DATA结构

上一篇讲到了NtReadVirtualMemory这部分,调用了一个函数地址0x7FFE0300。那这个地址有什么用呢?这就要介绍一个新的结构_KUSER_SHARED_DATA:

  1. 在User层和Kernel层分别定义了一个_KUSER_SHARED_DATA结构区域,用于User层和Kernel层共享某些数据
  2. 它们使用固定的地址值映射,_KUSER_SHARED_DATA结构区域在User和Kernel层地址分别为:
    • User层地址为:0x7FFE0000
    • Kernel层地址为:0xFFDF0000
  3. 虽然指向的是同一个物理页,但在User层是只读的,在Kernel层是可写的

SystemCall

现在我们知道,0x7FFE0000处是_KUSER_SHARED_DATA结构,使用dt指令查看结构,查找0x300偏移处,也就是0x7FFE0300的位置,这个字段是SystemCall

那这个SystemCall有什么用呢?

SystemCall的作用是选择以什么方式进入0环。这要看CPU是否支持sysenter/sysexit

  • 支持:ntdll.dll!KiFastSystemCall()
  • 不支持:ntdll.dll!KiIntSystemCall()

那如何看CPU是否支持sysenter/sysexit指令呢?

  1. (OD打开任一程序)将eax置1(参数)
  2. 将ecx,edx置0(方便查看)
  3. 执行指令cpuid
  4. 查看edx的SEP位(下标11的位置),若值为1,说明支持sysenter/sysexit

FBFF -> 1111 1011 1111 1111

说明本机支持sysenter/sysexit

KiIntSystemCall进0环

在学过调用门,中断门后,我们知道,凡是提权(例如进0环),都伴随着寄存器中的值发生改变,包括CS,SS,EIP,ESP。所以我们分别分析一下两种进0环方式,看看他们是如何修改寄存器的值的,先从KiIntSystemCall开始。

获取提权后寄存器的值

KiIntSystemCall进0环的方式非常简单,就是我们最熟悉中断门。(这里第一条指令的作用是获取参数的首地址

中断门就很熟悉了,进入IDT表看下0x2E对应的门描述符

根据中断门描述符,可以很快得到:

CS = 0x8 EIP = 8053e481

至于SS,和ESP,会在程序提权时,由tr寄存器指向的TSS中的ESP0和SS0提供。

KiSystemService

根据EIP的值,0x8053e481我们可以定位到一个内核函数KiSystemService

这样就说明进入0环了,所以KiIntSystemCall进0环非常好理解,就是中断门的知识,进入0环后,就跳转到KiSystemService继续执行,至于如何找到想要执行的函数,会在后面的文章中介绍。接下来我们来看支持sysenter/sysexit的KiFastSystemCall是如何进入0环的。

KiFastSystemCall进0环

事实上,基本上现在的CPU都支持sysenter/sysexit,默认也是通过KiFastSystemCall进0环,为什么呢?顾名思义,因为快啊。因为KiFastSystemCall不需要像中断门进0环时,去查IDT表找CS,EIP,查TSS找ESP和SS,而是直接从寄存器里读取这些数据。

获取提权后寄存器的值

来看KiFastSystemCall,只有两行指令。sysenter指令,又称作快速调用,当CPU支持sysenter指令时,操作系统会提前将CS,SS,ESP,EIP写入到MSR寄存器中,当sysenter指令执行时,CPU直接从MSR中将这些值写入相应寄存器中,没有读取内存的过程,所以叫做快速调用。

MSR寄存器

由于MSR寄存器非常大,这里不细讲,只列出用到的值

可以通过RDMSR/WRMST来进行读写(操作系统使用WRMST写该寄存器)

这里读取ESP,EIP,CS的值(SS的值可以通过CS+8计算而得

这样,在进入0环时,便能通过MSR寄存器切换4个寄存器的值了

KiFastCallEntry

前面讲了KiIntSystemCall进入0环后执行KiSystemService,KiFastSystemCall当然也有,执行的是另一个函数,通过MSR得到的EIP我们可以找到函数KiFastCallEntry,同样是内核函数。

这样便成功进入0环了。

总结

进0环的过程非常好理解,一种是咱们熟悉的中断门,另一种快速调用,也仅仅是利用了寄存器实现,不是很困难。但是有没有考虑一个问题,我们虽然成功的从3环进入到0环了,那该怎么出来呢?3环程序调用API实现功能,总该返回继续执行程序吧。既然要从0环返回3环,那就得恢复原来的寄存器的值,那么这些值又保存到了哪里呢?除此之外,现在进入了0环了,但又如何找到所要执行的函数呢?带着这些问题,我们继续学习下面的知识。

参考教程:https://www.bilibili.com/video/BV1NJ411M7aE?p=39

参考文章:https://blog.csdn.net/qq_41988448/article/details/102825241

Author: cataLoc
Link: http://cata1oc.github.io/2020/03/25/API%E5%87%BD%E6%95%B0%E7%9A%84%E8%B0%83%E7%94%A8%E8%BF%87%E7%A8%8B%EF%BC%883%E7%8E%AF%E8%BF%9B0%E7%8E%AF%EF%BC%89/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶