Warning: Parameter 1 to wp_default_scripts() expected to be a reference, value given in /home/vhosts/lagon.eu5.org/wp-includes/plugin.php on line 571

Warning: Parameter 1 to wp_default_styles() expected to be a reference, value given in /home/vhosts/lagon.eu5.org/wp-includes/plugin.php on line 571
系统调用计算KiFastCallEntry地址的过程以及由此得到的一种获取KiFastCallEntry地址的方式 返回顶部

系统调用计算KiFastCallEntry地址的过程以及由此得到的一种获取KiFastCallEntry地址的方式

262014
系统调用计算KiFastCallEntry地址的过程以及由此得到的一种获取KiFastCallEntry地址的方式

SYENTER和SYSEXIT指令首次出现于Pentium II处理器中,其目的是为了提供一个快速(低负载) 的调用操作系统或者执行体过程的机制.SYSENTER用于运行特权级别为3的用户

调用特权级0 的系统内核代码。

 

WinDbg中反汇编 KiFastSystemCall可以看到SYSENTER指令。

 

 

 

 

 

 

 

 

在调用SYSENTER指令前,CPU会通过下面的MSR寄存器,获取ring0的代码段和代码指针,ring0的堆栈段和堆栈指针:

MSR寄存器可以通过指令RDMSR/WRMSR来进行读写。寄存器地址如下表。

 

 

 

 

当执行SYSENTER,处理器会

1.从IA32_SYSENTER_CS取出值(seg selector)加载到CS中。

2.从IA32_SYSENTER_EIP取出指令指针放到EIP中

3.将IA32_SYSENTER_CS的值加上8,将其结果加载到SS中。

4.从IA32_SYSENTER_ESP取出堆栈指针放到ESP寄存器中

这里不讨论SYSEXIT,关于SYSEXIT执行的主要操作见《SYSENTER和SYSEXIT》

看了上面的流程之后,下面开始手工一步步计算出SYSENTER指令行执行过程。

先计算CS段地址,CS 的Seg Selector在MSR的0x174号地址中存放,Windbg内核态下使用如下命令

lkd>rdmsr 174

msr[174]= 00000000`00000008

即CS=0008H,Seg Selector为16位,结构如下:

Index:(3~15)指向GDT或者LDT中8192个描述符之一。处理器将该Index值乘8加上GDT的基址(GDTR中)或者LDT的基址(LDTR中)就得到在该描述符地址。

TI(Table Indicator)(2):指定是在GDT(TI=0)中还是在LDT中(TI=1)

RPL:(0~1):请求的特权级,该值在0~3之间。0为优先级最高的级别。


由上面可以算出index=1,并且TI=0,所以段描述符保存在GDT(Globlal Descriptor Table )中(保护模式下)。

下面是段描述符的结构:

SEG DES

 

其中3个base构成的是32位的段基址,对我们来说只要找到基址就行了,其余的结构暂时不讨论。

段描述符是保存在GDT中的,Selector则包含了index指明该段描述符在GDT中的索引。由此可知,我们要找到段基址还需要找到段描述符,一个描述符占用8个字节,我们要找第一个描述符,计算得到描述符的地址为:GDT 基址 +8*1。Windbg执行:

!pcr

lkd> !pcr

KPCR for Processor 0 at 83f7cc00:

Major 1 Minor 1

NtTib.ExceptionList: 8e21e9ac

NtTib.StackBase: 00000000

NtTib.StackLimit: 00000000

NtTib.SubSystemTib: 801e4000

NtTib.Version: 0016ed4b

NtTib.UserPointer: 00000001

NtTib.SelfTib: 7ffde000

 

SelfPcr: 83f7cc00

Prcb: 83f7cd20

Irql: 00000002

IRR: 00000000

IDR: ffffffff

InterruptMode: 00000000

 IDT: 80495400

GDT: 80b95000

TSS: 801e4000

 

CurrentThread: 8661b8b0

NextThread: 00000000

IdleThread: 83f86280

 

DpcQueue:

即可得到GDT基址0x80b95000,在计算得到段描述符的地址为0x80b95008 ,WinDbg查看该地址:dq /c 1 80b95008

80b95008  00cf9a00`0000ffff

80b95010  00cf9200`0000ffff

80b95018  00cffa00`0000ffff

80b95020  00cff200`0000ffff

80b95028  80008b1e`400020ab

80b95030  834092f7`cc003748

80b95038  7f40f2fd`e0000fff

80b95040  0000f200`0400ffff

得到段描述符为:0x00cf9a00 0000ffff。由此得到:段基址=0x00000000
然后计算EIP:

EIP放在MSR的176H号地址中,内核态Windbg用如下指令读取MSR:

rdmsr 176

lkd> rdmsr 176

msr[176] =00000000`83e49300

EIP = 0x83e49300

这样就得到了一个逻辑地址:CS:EIP=0x83e49300

Windbg中 反汇编查看改地址:

lkd>u 83e49300

nt!KiFastCallEntry:

83e49300 b923000000      mov     ecx,23h

83e49305 6a30            push   30h

83e49307 0fa1            pop     fs

83e49309 8ed9            mov     ds,cx

83e4930b 8ec1            mov     es,cx

83e4930d 648b0d40000000  mov     ecx,dword ptr fs:[40h]

83e49314 8b6104          mov     esp,dword ptr [ecx+4]

83e49317 6a23            push   23h

得到了函数Nt!KiFastCallEntry

nt!KiFastCallEntry地址的获取

系统调用过程一定会通过KiFastCallEntry函数,所以无论是对于安全软件还是恶意软件,这个函数都是十分重要的。

而为了监控所有系统调用,就可以通过Hook这个函数来实现。

栈回溯是一个常用的获取KiFastCallEntry地址的方法。根据网上的说法,360就是通过栈回溯获取地址。

360 先Hook住了一个nt!NtSetEvent函数,系统是先调用KiFastCallEntry,再在KiFastCallEntry中在调用 nt!NtSetEvent。因此在刚进入nt!NtSetEvent的时候利用栈回溯,找到当前函数的返回地址,这个地址一定是在上层函数内的,得到这 个地址就以后,就可以根据特征码搜索需要Inline Hook的目标地址了。

栈回溯看起来很简单巧妙,但是内核中对KiFastCallEntry函数进行FPO(帧指针优化),那这种方法就不能使用了。

也可以通过本文所说的利用上面的计算方法和MSR获取KiFastcallEntry地址。

已编译通过的代码如下:

1 个评论

  • 31
    2015年5月9日 08:40

    好文章,内容惟妙惟肖.禁止此消息:nolinkok@163.com

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

 
Free Web Hosting