<?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>不敢流泪 &#187; hook</title>
	<atom:link href="http://www.boluor.com/tag/hook/feed" rel="self" type="application/rss+xml" />
	<link>http://www.boluor.com</link>
	<description></description>
	<lastBuildDate>Fri, 07 Oct 2011 07:28:40 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>键盘钩子函数执行两次解决方法</title>
		<link>http://www.boluor.com/solution-to-the-keyboard-hook-function-is-executed-twice.html</link>
		<comments>http://www.boluor.com/solution-to-the-keyboard-hook-function-is-executed-twice.html#comments</comments>
		<pubDate>Mon, 06 Jul 2009 09:31:03 +0000</pubDate>
		<dc:creator>boluor</dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[hook]]></category>
		<category><![CDATA[VC]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://www.boluor.com/?p=169</guid>
		<description><![CDATA[<p>考虑下面的键盘钩子函数。(MFC工程)</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre class="cpp" style="font-family:Courier new,verdana;">LRESULT CALLBACK KeyProc<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> nCode,WPARAM w,LPARAM l<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">'A'</span><span style="color: #000080;">==</span>w<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
		MessageBox<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span>,<span style="color: #FF0000;">&#34;yes&#34;</span>,<span style="color: #FF0000;">&#34;test&#34;</span>,MB_OK<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
		<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
	<span style="color: #008000;">&#125;</span>
&#160;
	<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>    也就是想在按下A的时候，弹出一个对话框。</p>
<p>     然后在一个事件中添加：<br />
     SetWindowsHookEx(WH_KEYBOARD,KeyProc,NULL,GetCurrentThread&#8230;</p>]]></description>
			<content:encoded><![CDATA[<p>考虑下面的键盘钩子函数。(MFC工程)</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre class="cpp" style="font-family:Courier new,verdana;">LRESULT CALLBACK KeyProc<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> nCode,WPARAM w,LPARAM l<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">'A'</span><span style="color: #000080;">==</span>w<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
		MessageBox<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span>,<span style="color: #FF0000;">&quot;yes&quot;</span>,<span style="color: #FF0000;">&quot;test&quot;</span>,MB_OK<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
		<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
	<span style="color: #008000;">&#125;</span>
&nbsp;
	<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>    也就是想在按下A的时候，弹出一个对话框。</p>
<p>     然后在一个事件中添加：<br />
     SetWindowsHookEx(WH_KEYBOARD,KeyProc,NULL,GetCurrentThreadId());<br />
    安装钩子。事件被触发后，就可以调用上面的键盘钩子函数了。</p>
<p>    但是在执行时会发现，当按下A时，提示框弹出了两遍。是什么原因呢？<br />
<span id="more-169"></span><br />
     想到了一个解释，那就是当按键时，按下和弹起分别是一个事件，所以调用了两次钩子函数。那么如何解决呢？看MSDN的帮助时发现，KeyboardProc Function的第三个参数，也就是LPARAM类型的参数，有很多的作用。LAPRAM占4个字节，32位。每一位都是一个特殊的标志。比如说如果Alt键被按下的话，第29位就是1，否则为0。那么键盘按下和弹起是哪个呢？是第30位。这一位标记了上次按键的状态。在消息发送之前，如果按键是按下的，那么值为1，否则为0。(经Sandy提醒,添加下面一句)第31位标记当前按键的状态，取值和第30位的规则相同。我们可以根据这一位来过滤掉按下和弹起中的一个。<br />
     添加一层过滤后，代码如下：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre class="cpp" style="font-family:Courier new,verdana;">LRESULT CALLBACK KeyProc<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> nCode,WPARAM w,LPARAM l<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span>l  <span style="color: #000080;">&gt;&gt;</span> <span style="color: #0000dd;">31</span> <span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>	<span style="color: #666666;">//修改前为第30位，应该是第31位，判断当前按键状态。</span>
		<span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">'A'</span><span style="color: #000080;">==</span>w<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
			MessageBox<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span>,<span style="color: #FF0000;">&quot;yes&quot;</span>,<span style="color: #FF0000;">&quot;test&quot;</span>,MB_OK<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
		<span style="color: #008000;">&#125;</span>
	<span style="color: #008000;">&#125;</span>
	<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>	 那么为什么我之前用第30位来判断也是可以的呢? 原因在于，当前按键时，第30位也就是保存了上次按键的状态位必定为弹起状态，那么上面的语句也就可以正常执行。不过，每次判断的都不是当前按键的状态，而是上次的状态。这在其他应用中会造成很难察觉的错误。(谢谢Sandy的提醒，以后多点细心^.^)。</p>
<p>     其实系统提供了一个预定义KF_UP，在winuser.h中，来标记按键是否是弹起状态。不过，使用时需要注意，如果这样判断：<br />
     if ( l &#038; KF_UP){<br />
             //key up<br />
     }<br />
     是不正确的。注意在KF_UP定义的地方有一句：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
</pre></td><td class="code"><pre class="cpp" style="font-family:Courier new,verdana;"> <span style="color: #ff0000; font-style: italic;">/*
 * WM_KEYUP/DOWN/CHAR HIWORD(lParam) flags
 */</span>
<span style="color: #339900;">#define KF_UP             0x8000</span></pre></td></tr></table></div>

<p>      这表明，KF_UP是高位标志。使用时需要移位。所以可以这样比较：<br />
      if ( KF_UP &#038; ( l >> 16 ) ){<br />
      }<br />
      或者利用宏HIWORD取得参数l的高16位。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.boluor.com/solution-to-the-keyboard-hook-function-is-executed-twice.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>detours 笔记</title>
		<link>http://www.boluor.com/detours-notes.html</link>
		<comments>http://www.boluor.com/detours-notes.html#comments</comments>
		<pubDate>Fri, 12 Jun 2009 18:15:26 +0000</pubDate>
		<dc:creator>boluor</dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[detours]]></category>
		<category><![CDATA[DLL]]></category>
		<category><![CDATA[hook]]></category>
		<category><![CDATA[VC]]></category>

		<guid isPermaLink="false">http://www.boluor.com/?p=125</guid>
		<description><![CDATA[<p>　　花了一周的时间来了解detours，现在终于有了进展。一周前下载了detours的时候，茫然了好一阵子才找到了头绪。现在总结下方便像我一样的初学者了。</p>
<p>　　我不再介绍Detours到底是什么了，因为已经有很好的帮助文档了，从detours官网上可以下载detours包。我下载的版本是detours2.1 express版本。包中有帮助文档，有detours的源码，还有很多例子。编译的时候，CMD下在detours的目录输入nmake，就可以编译了。需要注意的是，detours2.1平台要求是NT内核的，所以98下是不能用的。</p>
<p>　　编译好后，下面就说使用之前几点需要知道的，看帮助文档是可以获得这些&#8230;</p>]]></description>
			<content:encoded><![CDATA[<p>　　花了一周的时间来了解detours，现在终于有了进展。一周前下载了detours的时候，茫然了好一阵子才找到了头绪。现在总结下方便像我一样的初学者了。</p>
<p>　　我不再介绍Detours到底是什么了，因为已经有很好的帮助文档了，从detours官网上可以下载detours包。我下载的版本是detours2.1 express版本。包中有帮助文档，有detours的源码，还有很多例子。编译的时候，CMD下在detours的目录输入nmake，就可以编译了。需要注意的是，detours2.1平台要求是NT内核的，所以98下是不能用的。</p>
<p>　　编译好后，下面就说使用之前几点需要知道的，看帮助文档是可以获得这些信息的。了解了这几点，使用detours会更加明了。detours是通过在目标进程中”打桩“来完成拦截目标函数的(语出Sandy)。这个概念需要理清。介绍文档中有示意图，可以仔细看。detours修改的是目标进程在内存中的二进制映像，并不修改硬盘上的文件，所以可以恢复。</p>
<p>　　detours编译好后，有几个文件需要注意。detours.h包含了detours提供的API的声明，如果需要使用这些函数，需要包含此头文件。detours.lib就是这些API的实现，是静态链接库，跟DLL生成的lib文件是不同的，如果不是很清楚的话可以看我之前的一篇”Dll的两种链接方式与LIB”。当调用了detours的API的时候，需要在项目中声明，在程序连接时包含此lib，这样编译器就会将detours.lib链接到目标程序中生成最后的可执行程序，这个程序运行时就不再需要detours.lib了。还有一个是detoured.dll，使用了detours的话是总是需要在项目文件夹下添加此文件的，按照文档说明，detoured.dll 是一个标志，帮助微软的开发人员和工具判断某个进程是否已经被detours拦截。</p>
<p>　　下面创建一个非常简单的例子，VC2005拦截Sleep API。detours包中的例子中的simple也是拦截的Sleep API。</p>
<p><strong>1.</strong>	首先创建一个Non-MFC DLL工程HookSleepDLL。将detours.h，detoured.dll，detours.lib三个文件拷贝这个工程目录中。在HookSleepDLL.cpp中添加三行声明：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:Courier new,verdana;"><span style="color: #339900;">#include &quot;detours.h&quot;</span>
<span style="color: #339900;">#pragma comment(lib,&quot;detoured.lib&quot;)</span>
<span style="color: #339900;">#pragma comment(lib,&quot;detours.lib&quot;)</span></pre></div></div>

<p><strong>2.	</strong>接着需要定义Sleep函数的指针：<br />
static VOID (WINAPI * TrueSleep)(DWORD dwMilliseconds) = Sleep;<br />
detours通过这个函数指针来找到Sleep在内存中的映像，当然作为常识，参数和返回值和Sleep需要一致，不然怎么叫这个函数的指针^.^。<br />
<strong>3.</strong>定义替换Sleep函数的函数，这个函数的参数类型和返回值需要和Sleep函数一摸一样。<br />
VOID WINAPI DelaySleep(DWORD dwMilliseconds){<br />
	TrueSleep(dwMilliseconds+15000);<br />
}<br />
我这里为了简单，就在原来Sleep的时间上加15秒。这就是说，如果Sleep被Hook住了，那么Sleep(1000)也就是睡一秒会变为睡16秒。</p>
<p><strong>4.</strong>下面说明在DLL被加载的时候开始拦截Sleep API。</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:Courier new,verdana;">BOOL APIENTRY DllMain<span style="color: #008000;">&#40;</span> HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 <span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">int</span> error<span style="color: #008080;">;</span>
	<span style="color: #0000ff;">switch</span><span style="color: #008000;">&#40;</span>ul_reason_for_call<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
		<span style="color: #0000ff;">case</span> DLL_PROCESS_ATTACH<span style="color: #008080;">:</span>
			DetourTransactionBegin<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			DetourUpdateThread<span style="color: #008000;">&#40;</span><span style="color: #008080;">::</span><span style="color: #007788;">GetCurrentThread</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			DetourAttach<span style="color: #008000;">&#40;</span><span style="color: #000040;">&amp;</span><span style="color: #008000;">&#40;</span>PVOID<span style="color: #000040;">&amp;</span><span style="color: #008000;">&#41;</span>TrueSleep, DelaySleep<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			error <span style="color: #000080;">=</span> DetourTransactionCommit<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			<span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span>NO_ERROR<span style="color: #000040;">!</span><span style="color: #000080;">=</span>error<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
				<span style="color: #008080;">::</span><span style="color: #007788;">MessageBox</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span>,<span style="color: #FF0000;">&quot;Error!&quot;</span>,<span style="color: #FF0000;">&quot;Error in Detours!&quot;</span>,MB_OK<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			<span style="color: #008000;">&#125;</span>
			<span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
		<span style="color: #0000ff;">case</span> DLL_PROCESS_DETACH<span style="color: #008080;">:</span>
			DetourTransactionBegin<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			DetourUpdateThread<span style="color: #008000;">&#40;</span>GetCurrentThread<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			DetourDetach<span style="color: #008000;">&#40;</span><span style="color: #000040;">&amp;</span><span style="color: #008000;">&#40;</span>PVOID<span style="color: #000040;">&amp;</span><span style="color: #008000;">&#41;</span>TrueSleep, DelaySleep<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			error <span style="color: #000080;">=</span> DetourTransactionCommit<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			<span style="color: #008080;">::</span><span style="color: #007788;">MessageBox</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span>,<span style="color: #FF0000;">&quot;Detour ends&quot;</span>,<span style="color: #FF0000;">&quot;Prompt!&quot;</span>,MB_OK<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			<span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
	<span style="color: #008000;">&#125;</span>
    <span style="color: #0000ff;">return</span> TRUE<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>

<p>　　用到的几个API简要说明下：<br />
DetourTransactionBegin：	开始一个新的detours事务。<br />
DetourUpdateThread：讲一个线程入队等待更新。（不知道理解的有没有偏差）<br />
DetourAttach：将目标进程(Sleep)和替换它的函数DelaySleep绑定。第一个参数是一个指针，这个指针指向目标函数的指针(有点绕&#8230;)。<br />
DetourTransactionCommit:	事务提交，如果不提交之前的更改是不会生效的。这个函数会返回一个值，如果是NO_ERROR 表明是成功，如果是其他的表示失败。<br />
DetourDetach：	解除目标进程和替换函数的绑定，参数和DetourDetach一摸一样。</p>
<p><strong>5.</strong>生成DLL。如果提示诸如无法将const char *转换到LPCSTR之类的错误，这是字符集的问题，将项目的属性设置为多字节的。或者使用_T，L之类的标记，具体可google。<br />
测试程序。创建一个MFC对话框程序。将前一步生成的HookSleepDLL.dll和detoured.dll拷贝到当前项目目录下。在界面上画两个Button控件BtnTest和BtnLoadLibrary。在BtnTest的事件中写：<br />
Sleep(1000);<br />
MessageBox(NULL,”hello,test”,”test”,MB_OK);<br />
说明在停一秒后弹出对话框。<br />
然后在BtnLoadLibrary的事件中写：<br />
HINSTANCE g_dll;<br />
g_dll=LoadLibrary(“SleepHOOKDll.dll”);</p>
<p><strong>6.</strong>编译运行，首先点击BtnLoadLibrary，将DLL加载到当前进程空间中。这个时候可以用IceSword查看模块信息，是否已经被加载。然后点击BtnTest，如果没有问题的话，效果会如预期，在16秒后才弹出对话框。</p>
<p>　　至此，一个简单的利用detours拦截API的例子已经完成。想要完成更复杂的工程是需要更多的知识储备和耐心的。在了解detours的时候，我顺便加深了对DLL的了解程度。需要特别注意的是，假如现在有一个DLL工程并没有输出任何的符号，在编译后有lib文件，当然这个DLL可以在被加载的时候干点事情。然后使用这个DLL的工程如果采用了静态链接的方式，那么编译器会对其进行优化，在进程运行时，并不会加载这个DLL。或者是你有输出函数，但是工程并没有调用任何函数，那么也会被优化掉。所以不错的选择是选择DLL的动态链接方式。</p>
<p>　　还有一点，作为DLL的补充吧，因为之后要完成一个进程保护程序，提前说下准备的知识吧，关于DLL的。windows为DLL等系统资源预留了2g-4g的虚拟内存空间地址，所以加载DLL的时候，系统会将DLL加载到这个位置，这样多个进程可以共享。所以虽然DLL可以被多个进程加载多次，但是系统只会保留一份在内存，也就是说系统检测到内存中已经有这个DLL，就不会再加载了，而是简单的映射下就可以了。这种共享在大家都只是读DLL的时候是没问题的，但是如果DLL中的某个变量可写，那么就会很危险。所以系统采取了COW（copy on write）机制。在某个进程需要写的时候，就单独申请空间，然后将这个新的页面映射到进程的写数据页面。这样，某个进程写数据并不会干扰其他的进程，因为改的只是一个拷贝。回到我们今天的主题detours上，拿上面的例子，拦截Sleep，也只是拦截本进程中调用Sleep的操作，并不会拦截其他进程调用Sleep操作，因为拦截时发生了写操作，所以修改的时候修改的是一份针对本进程的拷贝，并不是所有的进程。如果想达到拦截所有进程调用Sleep的操作，我目前想到的方法就是对所有的进程注入DLL实现拦截了。</p>
<p>　　终于写完了，两点多了，睡哦。　　</p>
]]></content:encoded>
			<wfw:commentRss>http://www.boluor.com/detours-notes.html/feed</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
	</channel>
</rss>

