<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Lagon &#187; KifastCallEntry地址</title>
	<atom:link href="http://lagon.eu5.org/tag/kifastcallentry%e5%9c%b0%e5%9d%80/feed" rel="self" type="application/rss+xml" />
	<link>http://lagon.eu5.org</link>
	<description>Window Kernel &#38; Web InfoSec</description>
	<lastBuildDate>Sun, 12 Apr 2015 13:56:55 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=4.1.1</generator>
	<item>
		<title>系统调用计算KiFastCallEntry地址的过程以及由此得到的一种获取KiFastCallEntry地址的方式</title>
		<link>http://lagon.eu5.org/windows-kernel/id=14</link>
		<comments>http://lagon.eu5.org/windows-kernel/id=14#comments</comments>
		<pubDate>Wed, 26 Mar 2014 04:27:25 +0000</pubDate>
		<dc:creator><![CDATA[Lagon]]></dc:creator>
				<category><![CDATA[Windows Kernel]]></category>
		<category><![CDATA[KifastCallEntry地址]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://lagon.eu5.org/?p=14</guid>
		<description><![CDATA[SYENTER和SYSEXIT指令首次出现于Pentium II处理器中,其目的是为了提供一个快速(低负载) 的调用操作系统或者执行体过程的机制.SYSENTER用于运行特权级别为3的用户 调用特权级0 的系统内核代码。 &#160; WinDbg中反汇编 KiFastSystemCall可以看到SYSENTER指令。 &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; 在调用SYSENTER指令<a class="read-more" href="http://lagon.eu5.org/windows-kernel/id=14">[ 更多... ]</a>]]></description>
				<content:encoded><![CDATA[<div>
<p>SYENTER和<i>SYSEXIT</i>指令首次出现于Pentium II处理器中,其目的是为了提供一个快速(低负载) 的调用操作系统或者执行体过程的机制.<i>SYSENTER</i>用于运行特权级别为3的用户</p>
<p>调用特权级0 的系统内核代码。</p>
<p>&nbsp;</p>
<p>WinDbg中反汇编 KiFastSystemCall可以看到SYSENTER指令。</p>
<p>&nbsp;</p>
<p align="center"><img alt="" src="http://img.blog.csdn.net/20140323002740593?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGFnb25f/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" align="left" /></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>在调用SYSENTER指令前，CPU会通过下面的MSR寄存器，获取ring0的代码段和代码指针，ring0的堆栈段和堆栈指针：</p>
<p>MSR寄存器可以通过指令RDMSR/WRMSR来进行读写。寄存器地址如下表。</p>
<p><img alt="" src="http://img.blog.csdn.net/20140323003144406?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGFnb25f/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" align="left" /><img alt="" src="http://img.blog.csdn.net/20140323002910375?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGFnb25f/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" width="0" height="0" align="left" /></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>当执行SYSENTER，处理器会</p>
<p>1.从IA32_SYSENTER_CS取出值（seg selector）加载到CS中。</p>
<p>2.从IA32_SYSENTER_EIP取出指令指针放到EIP中</p>
<p>3.将IA32_SYSENTER_CS的值加上8，将其结果加载到SS中。</p>
<p>4.从IA32_SYSENTER_ESP取出堆栈指针放到ESP寄存器中</p>
<p>这里不讨论SYSEXIT，关于SYSEXIT执行的主要操作见<span style="font-size: small;"><a href="http://blog.csdn.net/lagon_/article/details/21641433" target="_blank">《SYSENTER和SYSEXIT》</a></span></p>
<p>看了上面的流程之后，下面开始手工一步步计算出SYSENTER指令行执行过程。</p>
<p>先计算CS段地址,CS 的Seg Selector在MSR的0x174号地址中存放,Windbg内核态下使用如下命令</p>
<p>lkd&gt;rdmsr 174</p>
<p>msr[174]= 00000000`00000008</p>
<p>即CS=0008H,Seg Selector为16位，结构如下：</p>
<p>Index:（3~15）指向GDT或者LDT中8192个描述符之一。处理器将该Index值乘8加上GDT的基址（GDTR中）或者LDT的基址（LDTR中）就得到在该描述符地址。</p>
<p>TI（Table Indicator）（2）：指定是在GDT（TI=0）中还是在LDT中（TI=1）</p>
<p>RPL：（0~1）：请求的特权级，该值在0~3之间。0为优先级最高的级别。</p>
<p><img alt="" src="http://img.blog.csdn.net/20140323003212937?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGFnb25f/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" align="left" /><br />
<img alt="" src="http://img.blog.csdn.net/20140323002943453?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGFnb25f/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" width="0" height="0" align="left" /></p>
<p>由上面可以算出index=1，并且TI=0，所以段描述符保存在GDT（Globlal Descriptor Table ）中（保护模式下）。</p>
<p>下面是段描述符的结构：</p>
<p><img alt="SEG DES" src="http://img.blog.csdn.net/20140323003234812?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGFnb25f/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" align="left" /></p>
<p>&nbsp;</p>
<p>其中3个base构成的是32位的段基址，对我们来说只要找到基址就行了，其余的结构暂时不讨论。</p>
<p>段描述符是保存在GDT中的，Selector则包含了index指明该段描述符在GDT中的索引。由此可知，我们要找到段基址还需要找到段描述符，一个描述符占用8个字节，我们要找第一个描述符，计算得到描述符的地址为：GDT 基址 +8*1。Windbg执行:</p>
<p>!pcr</p>
<p>lkd&gt; !pcr</p>
<p>KPCR for Processor 0 at 83f7cc00:</p>
<p>Major 1 Minor 1</p>
<p>NtTib.ExceptionList: 8e21e9ac</p>
<p>NtTib.StackBase: 00000000</p>
<p>NtTib.StackLimit: 00000000</p>
<p>NtTib.SubSystemTib: 801e4000</p>
<p>NtTib.Version: 0016ed4b</p>
<p>NtTib.UserPointer: 00000001</p>
<p>NtTib.SelfTib: 7ffde000</p>
<p>&nbsp;</p>
<p>SelfPcr: 83f7cc00</p>
<p>Prcb: 83f7cd20</p>
<p>Irql: 00000002</p>
<p>IRR: 00000000</p>
<p>IDR: ffffffff</p>
<p>InterruptMode: 00000000</p>
<p><b> </b> IDT: 80495400</p>
<p><b><span style="text-decoration: underline;"><span style="color: #ff0000;"> GDT: 80b95000</span></span></b></p>
<p>TSS: 801e4000</p>
<p>&nbsp;</p>
<p>CurrentThread: 8661b8b0</p>
<p>NextThread: 00000000</p>
<p>IdleThread: 83f86280</p>
<p>&nbsp;</p>
<p>DpcQueue:</p>
<p>即可得到GDT基址0x80b95000，在计算得到段描述符的地址为0x80b95008 ，WinDbg查看该地址：dq /c 1 80b95008</p>
<p>80b95008  <span style="text-decoration: underline;"><b><span style="color: #ff0000;">00cf9a00`0000ffff</span></b></span></p>
<p>80b95010  00cf9200`0000ffff</p>
<p>80b95018  00cffa00`0000ffff</p>
<p>80b95020  00cff200`0000ffff</p>
<p>80b95028  80008b1e`400020ab</p>
<p>80b95030  834092f7`cc003748</p>
<p>80b95038  7f40f2fd`e0000fff</p>
<p>80b95040  0000f200`0400ffff</p>
<p>得到段描述符为：0x00cf9a00 0000ffff。由此得到：<span style="color: #cc0000; font-size: medium;">段基址=0x00000000</span><br />
然后计算EIP：</p>
<p>EIP放在MSR的176H号地址中，内核态Windbg用如下指令读取MSR：</p>
<p>rdmsr 176</p>
<p>lkd&gt; rdmsr 176</p>
<p>msr[176] =00000000`83e49300</p>
<p>EIP = 0x83e49300</p>
<p>这样就得到了一个逻辑地址：<span style="color: #ff0000; font-size: large;">CS：EIP=0x</span><span style="color: #ff0000; font-size: large;">83e49300</span></p>
<p>Windbg中 反汇编查看改地址：</p>
<p>lkd&gt;u 83e49300</p>
<p>nt!KiFastCallEntry:</p>
<p>83e49300 b923000000      mov     ecx,23h</p>
<p>83e49305 6a30            push   30h</p>
<p>83e49307 0fa1            pop     fs</p>
<p>83e49309 8ed9            mov     ds,cx</p>
<p>83e4930b 8ec1            mov     es,cx</p>
<p>83e4930d 648b0d40000000  mov     ecx,dword ptr fs:[40h]</p>
<p>83e49314 8b6104          mov     esp,dword ptr [ecx+4]</p>
<p>83e49317 6a23            push   23h</p>
<p><span style="color: #ff0000; font-size: large;">得到了函数Nt!KiFastCallEntry</span>，</p>
<h1><span style="color: red;">nt!KiFastCallEntry</span><span style="color: red;">地址的获取</span></h1>
<p>系统调用过程一定会通过KiFastCallEntry函数，所以无论是对于安全软件还是恶意软件，这个函数都是十分重要的。</p>
<p>而为了监控所有系统调用，就可以通过Hook这个函数来实现。</p>
<p>栈回溯是一个常用的获取KiFastCallEntry地址的方法。根据网上的说法，360就是通过栈回溯获取地址。</p>
<p>360 先Hook住了一个nt!NtSetEvent函数，系统是先调用KiFastCallEntry，再在KiFastCallEntry中在调用 nt!NtSetEvent。因此在刚进入nt!NtSetEvent的时候利用栈回溯，找到当前函数的返回地址，这个地址一定是在上层函数内的，得到这 个地址就以后，就可以根据特征码搜索需要Inline Hook的目标地址了。</p>
<p>栈回溯看起来很简单巧妙，但是内核中对KiFastCallEntry函数进行FPO(帧指针优化)，那这种方法就不能使用了。</p>
<p>也可以通过本文所说的利用上面的计算方法和MSR获取KiFastcallEntry地址。</p>
<p>已编译通过的代码如下：</p>
<p><script src="https://code.csdn.net/snippets/251228.js"></script></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://lagon.eu5.org/windows-kernel/id=14/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
