<?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; 尹晓飞</title>
	<atom:link href="http://software.intel.com/zh-cn/blogs/author/477164/feed/" rel="self" type="application/rss+xml" />
	<link>http://software.intel.com/zh-cn/blogs</link>
	<description></description>
	<lastBuildDate>Mon, 28 May 2012 13:40:23 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.3</generator>
		<item>
		<title>C++ 中模板元编程原理及速度测试</title>
		<link>http://software.intel.com/zh-cn/blogs/2010/05/12/c-3/</link>
		<comments>http://software.intel.com/zh-cn/blogs/2010/05/12/c-3/#comments</comments>
		<pubDate>Wed, 12 May 2010 08:02:13 +0000</pubDate>
		<dc:creator>尹晓飞</dc:creator>
				<category><![CDATA[其他]]></category>
		<category><![CDATA[博客征文专栏]]></category>

		<guid isPermaLink="false">http://software.intel.com/zh-cn/blogs/2010/05/12/c-3/</guid>
		<description><![CDATA[这两天一直被这个模板元编程给迷住了，觉得它真是一个很好的东西！于是好奇就仔细的研究了下，之前看过几篇文章大概的意思就是“编可以编程的程序”。听起来很神奇吧。 其基本原理也就是让编译器在编译期间就计算好一些我们需要计算的值。在程序运行期间就不需要再去计算这些值了，从而提高程序的运行性能。当然这样做会让程序编译起来很慢，一般不常用。不过在一些需要的地方我们还是舍得编译的效率问题的。。 先写个例子看看其中的原理： #include using namespace std; template struct ADD { enum{ Result = ( x &#62; y ) ? x + 20 : y + 100 }; }; int main( void ) { int a = ADD::Result; system( "pause" ); return 0; } 在上边的代码里，我们看模板结构里面传了俩整形的变量。结构体里面用来生成枚举值，我们可以先从理论上得到结论是在编译期间这些枚举值就会被计算出来。是一个透明的数据。而且最终得到的a应该等于103。我们先运行： 输出结果：103 结果没错，但是我们看不到到底是不是在编译期间完成的啊。。怎么办呢？ 于是在 int a = ADD::Result; 执行之前设个断点，或者按F10直接断在main函数开头。我们来查看反汇编代码： 00411B00 push [...]]]></description>
			<content:encoded><![CDATA[<p>这两天一直被这个模板元编程给迷住了，觉得它真是一个很好的东西！于是好奇就仔细的研究了下，之前看过几篇文章大概的意思就是“编可以编程的程序”。听起来很神奇吧。</p>
<p>其基本原理也就是让编译器在编译期间就计算好一些我们需要计算的值。在程序运行期间就不需要再去计算这些值了，从而提高程序的运行性能。当然这样做会让程序编译起来很慢，一般不常用。不过在一些需要的地方我们还是舍得编译的效率问题的。。</p>
<p>先写个例子看看其中的原理：</p>
<p>#include<br />
using namespace std;</p>
<p>template<br />
struct ADD<br />
{<br />
enum{ Result = ( x &gt; y ) ? x + 20 : y + 100 };<br />
};</p>
<p>int main( void )<br />
{<br />
int a = ADD::Result;</p>
<p>system( "pause" );<br />
return 0;<br />
}</p>
<p>在上边的代码里，我们看模板结构里面传了俩整形的变量。结构体里面用来生成枚举值，我们可以先从理论上得到结论是在编译期间这些枚举值就会被计算出来。是一个透明的数据。而且最终得到的a应该等于103。我们先运行：</p>
<p>输出结果：103</p>
<p>结果没错，但是我们看不到到底是不是在编译期间完成的啊。。怎么办呢？ 于是在 int a = ADD::Result; 执行之前设个断点，或者按F10直接断在main函数开头。我们来查看反汇编代码：</p>
<p>00411B00 push ebp<br />
00411B01 mov ebp,esp<br />
00411B03 sub esp,0CCh<br />
00411B09 push ebx<br />
00411B0A push esi<br />
00411B0B push edi<br />
00411B0C lea edi,[ebp-0CCh]<br />
00411B12 mov ecx,33h<br />
00411B17 mov eax,0CCCCCCCCh<br />
00411B1C rep stos dword ptr [edi]</p>
<p>00411B1E mov dword ptr [a],67h</p>
<p>00411B25 push offset string "pause" (4260C8h)<br />
00411B2A call @ILT+565(_system) (41123Ah)<br />
00411B2F add esp,4</p>
<p>00411B32 xor eax,eax</p>
<p>上边是整个main函数的反汇编代码，我们可以看到上边红色的那句汇编代码就是给a 赋枚举值Result，看直接MOV的是67h = 103！ 由此可以证明我们的枚举值是在编译期间就计算好了的。。</p>
<p>前面看过一篇文章，是关于用模板元来解循环。我们以求一个整形数组所有元素之和为例子写了一个测试：</p>
<p>#include</p>
<p>using namespace std;</p>
<p>template<br />
class ADD<br />
{<br />
public:<br />
static int Result( Ty* elem )<br />
{<br />
return elem[ 0 ] + ADD::Result( elem++ );<br />
}<br />
};</p>
<p>template<br />
class ADD<br />
{<br />
public:<br />
static int Result( Ty* elem )<br />
{<br />
return elem[ 0 ];<br />
}<br />
};</p>
<p>int main( void )<br />
{<br />
__int64 BegTime = 0;<br />
__int64 EndTime = 0;<br />
__int32 sum = 0;</p>
<p>int a[ 3 ] = { 4, 5, 6 };</p>
<p>__asm<br />
{<br />
rdtsc<br />
mov dword ptr [ BegTime ], eax<br />
lea eax, dword ptr [ BegTime ]<br />
mov dword ptr [ eax + 4 ], edx<br />
}</p>
<p>ADD::Result( a );</p>
<p>__asm<br />
{<br />
rdtsc<br />
mov dword ptr [ EndTime ], eax<br />
lea eax, dword ptr [ EndTime ]<br />
mov dword ptr [ eax + 4 ], edx<br />
}</p>
<p>cout &lt;&lt; EndTime - BegTime &lt;&lt; endl;</p>
<p>BegTime = 0;<br />
EndTime = 0;</p>
<p>__asm<br />
{<br />
rdtsc<br />
mov dword ptr [ BegTime ], eax<br />
lea eax, dword ptr [ BegTime ]<br />
mov dword ptr [ eax + 4 ], edx<br />
}</p>
<p>for ( int i = 0; i &lt; 3; ++i )<br />
{<br />
sum += a[ i ];<br />
}</p>
<p>__asm<br />
{<br />
rdtsc<br />
mov dword ptr [ EndTime ], eax<br />
lea eax, dword ptr [ EndTime ]<br />
mov dword ptr [ eax + 4 ], edx<br />
}</p>
<p>cout &lt;&lt; EndTime - BegTime &lt;&lt; endl;</p>
<p>system( "pause" );<br />
return 0;<br />
}</p>
<p>上边的程序中红色部分表示模板元终结模板吧，我是这样理解的。。名字无所谓啦 - - 它的意思是当我程序执行到模板参数一为1的情况写就终结调用了。 当然可以参数2或者多个限定。过程是这样的：</p>
<p>调用顺序为： ADD::Result( int* ) =&gt; ADD::Result( int* ) =&gt; ADD::Result( int* )相当于转换成递归了 。。。</p>
<p>那我们在编译期间是不是编译器就已经递归调用了函数计算出结果，我们在运行时只需要取这个值这么简单呢？我们断在ADD::Result( a ); 之前或它上边，来看看反汇编代码：</p>
<p>0041B291 lea eax,[a]<br />
0041B294 push eax<br />
0041B295 call ADD::Result (419A82h)<br />
0041B29A add esp,4</p>
<p>我们在蓝色的地方F11跟进去，</p>
<p>一直跟到这里：</p>
<p>0041BFCE mov eax,dword ptr [elem]<br />
0041BFD1 mov dword ptr [ebp-0C4h],eax<br />
0041BFD7 mov ecx,dword ptr [elem]<br />
0041BFDA add ecx,4<br />
0041BFDD mov dword ptr [elem],ecx<br />
0041BFE0 mov edx,dword ptr [ebp-0C4h]<br />
0041BFE6 push edx<br />
0041BFE7 call ADD::Result (4193CFh)<br />
0041BFEC add esp,4<br />
0041BFEF mov ecx,dword ptr [elem]<br />
0041BFF2 add eax,dword ptr [ecx]</p>
<p>我们再在蓝色的地方跟进去，</p>
<p>一直到这里：</p>
<p>0041C6AE mov eax,dword ptr [elem]<br />
0041C6B1 mov dword ptr [ebp-0C4h],eax<br />
0041C6B7 mov ecx,dword ptr [elem]<br />
0041C6BA add ecx,4<br />
0041C6BD mov dword ptr [elem],ecx<br />
0041C6C0 mov edx,dword ptr [ebp-0C4h]<br />
0041C6C6 push edx<br />
0041C6C7 call ADD::Result (41A063h)<br />
0041C6CC add esp,4<br />
0041C6CF mov ecx,dword ptr [elem]<br />
0041C6D2 add eax,dword ptr [ecx]</p>
<p>我们再在蓝色的地方跟进去，</p>
<p>一直到这里：</p>
<p>0041CAE0 push ebp<br />
0041CAE1 mov ebp,esp<br />
0041CAE3 sub esp,0C0h<br />
0041CAE9 push ebx<br />
0041CAEA push esi<br />
0041CAEB push edi<br />
0041CAEC lea edi,[ebp-0C0h]<br />
0041CAF2 mov ecx,30h<br />
0041CAF7 mov eax,0CCCCCCCCh<br />
0041CAFC rep stos dword ptr [edi]</p>
<p>0041CAFE mov eax,dword ptr [elem]<br />
0041CB01 mov eax,dword ptr [eax]</p>
<p>看到整个过程了吧，这里可以明白一点就是在上边的汇编代码里面我标注成红色的数字是在编译期间计算好了的。我们在定位的时候直接就传入了模板元在编译期间计算后的数字。但是在这里并没有看到整个调用过程在编译期间执行。之前看过的文章说会在编译期间执行也不知道为什么。我们上边的测试是可以在运行期间断下来的并且执行了递归调用。</p>
<p>__asm<br />
{<br />
rdtsc<br />
mov dword ptr [ BegTime ], eax<br />
lea eax, dword ptr [ BegTime ]<br />
mov dword ptr [ eax + 4 ], edx</p>
<p>}</p>
<p>这段代码在前面的博文中已经提到过，是获取程序运行以来的时间（纳秒级）。</p>
<p>这里测试我们模板元解开循环后的速度和循环的速度。</p>
<p>程序输出结果为：</p>
<p>3090周期</p>
<p>130周期</p>
<p>这里的周期除以CPU主频率得到运行的时间,这里不用计算就能明显区分出来了 - -</p>
<p>可见解开循环只是变成了递归，真正起到作用的我认为是在编译期间计算好了每次递归所应该传入的整形数值。但是递归开销很大，而且很容易堆栈溢出。。所以这个问题还得我们深思。。不过在编译期间运算一些比如第一个例子计算枚举或类似的操作时，模板元的好处就得以体现了。。</p>
<p>有什么说的不对的地方望大家指教。。我是菜鸟 - -</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/zh-cn/blogs/2010/05/12/c-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>C/C++ HOOK API（原理深入剖析之- LoadLibraryA）</title>
		<link>http://software.intel.com/zh-cn/blogs/2010/05/12/cc-hook-api-loadlibrarya/</link>
		<comments>http://software.intel.com/zh-cn/blogs/2010/05/12/cc-hook-api-loadlibrarya/#comments</comments>
		<pubDate>Wed, 12 May 2010 07:59:48 +0000</pubDate>
		<dc:creator>尹晓飞</dc:creator>
				<category><![CDATA[其他]]></category>
		<category><![CDATA[博客征文专栏]]></category>
		<category><![CDATA[api]]></category>

		<guid isPermaLink="false">http://software.intel.com/zh-cn/blogs/2010/05/12/cc-hook-api-loadlibrarya/</guid>
		<description><![CDATA[9月都快结束了，之前一直忙到写自己的东西加上上班。基本没有时间研究下汇编和C C++方面的感兴趣的东西。再怎么说嘛，9月还是得写一篇撒，以后每月至少一篇吧。给自己定了，希望大家监督。嘿嘿！ 这篇文章就来谈谈平常很常见的HOOK技术，这里呢。写得比较简单，方法很多。只讲原理！希望大鸟们别吐我口水哈 - -。好！切入正题。 首先是概念吧。什么是钩子（HOOK）？ 钩子(Hook)，是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息，而且所监视的窗口可以是其他进程所创建的。当消息到达后，在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。 钩子实际上是一个处理消息的程序段，通过系统调用，把它挂入系统。每当特定的消息发出，在没有到达目的窗口前，钩子程序就先捕获该消息，亦即钩子函数先得到控制权。这时钩子函数即可以加工处理（改变）该消息，也可以不作处理而继续传递该消息，还可以强制结束消息的传递。 这上面只是一个概念，对它有所了解而已。上面主要应用在Windows消息处理机制里面的一个解释。这里我只是单纯的谈谈拦截我们常用的LoadLibraryA加载这个函数。让我们的程序或者目标程序在调用这个函数加载链接库的时候，先执行我们自己写的函数，然后在进行正常加载。通俗的说就是a-----&#62;b. 我们在中间加上一个c。 a--------&#62;c-----&#62;b让他先执行c然后再执行b。这里的c就是我们自己的函数了。 呵呵，概念说得差不多了，开始行动写代码撒： #include #include using namespace std; #pragma warning( disable: 4309 ) #pragma warning( disable: 4311 ) typedef HMODULE ( WINAPI *HOOKAPI )( IN LPCSTR ); #define MYHOOKMETHOD ( __fun ) HMODULE WINAPI __fun #define DECLARE_REGISTER ( __0bj, __lawfunc, __newfunc ) Inline_Hook __Obj( __lawfunc, __newfunc [...]]]></description>
			<content:encoded><![CDATA[<p>9月都快结束了，之前一直忙到写自己的东西加上上班。基本没有时间研究下汇编和C C++方面的感兴趣的东西。再怎么说嘛，9月还是得写一篇撒，以后每月至少一篇吧。给自己定了，希望大家监督。嘿嘿！</p>
<p>这篇文章就来谈谈平常很常见的HOOK技术，这里呢。写得比较简单，方法很多。只讲原理！希望大鸟们别吐我口水哈 - -。好！切入正题。</p>
<p>首先是概念吧。什么是钩子（HOOK）？</p>
<p>钩子(Hook)，是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息，而且所监视的窗口可以是其他进程所创建的。当消息到达后，在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。<br />
钩子实际上是一个处理消息的程序段，通过系统调用，把它挂入系统。每当特定的消息发出，在没有到达目的窗口前，钩子程序就先捕获该消息，亦即钩子函数先得到控制权。这时钩子函数即可以加工处理（改变）该消息，也可以不作处理而继续传递该消息，还可以强制结束消息的传递。<br />
这上面只是一个概念，对它有所了解而已。上面主要应用在Windows消息处理机制里面的一个解释。这里我只是单纯的谈谈拦截我们常用的LoadLibraryA加载这个函数。让我们的程序或者目标程序在调用这个函数加载链接库的时候，先执行我们自己写的函数，然后在进行正常加载。通俗的说就是a-----&gt;b. 我们在中间加上一个c。 a--------&gt;c-----&gt;b让他先执行c然后再执行b。这里的c就是我们自己的函数了。<br />
呵呵，概念说得差不多了，开始行动写代码撒：</p>
<p>#include<br />
#include<br />
using namespace std;</p>
<p>#pragma warning( disable: 4309 )<br />
#pragma warning( disable: 4311 )</p>
<p>typedef HMODULE ( WINAPI *HOOKAPI )( IN LPCSTR );<br />
#define MYHOOKMETHOD ( __fun ) HMODULE WINAPI __fun<br />
#define DECLARE_REGISTER ( __0bj, __lawfunc, __newfunc ) Inline_Hook __Obj( __lawfunc, __newfunc )</p>
<p>struct __InlineHOOK_Base<br />
{<br />
DWORD _argsBytes;<br />
void* _lawFunc;<br />
void* _newFunc;<br />
char _lawByteCode[16];<br />
char _newByteCode[16];</p>
<p>bool unhook ( void )<br />
{<br />
// It's hooked.<br />
if ( memcmp( _newByteCode, _lawFunc, 16 ) == 0 )<br />
{<br />
DWORD dwOldFlag;<br />
VirtualProtect( _lawFunc, 8, PAGE_EXECUTE_READWRITE, &amp;dwOldFlag );<br />
memcpy( _lawFunc, _lawByteCode, 16 );<br />
VirtualProtect( _lawFunc, 8, dwOldFlag, &amp;dwOldFlag );<br />
return true;<br />
}</p>
<p>return false;<br />
}</p>
<p>bool hook ( void )<br />
{<br />
// It's saved.<br />
if ( memcmp( _lawByteCode, _lawFunc, 16 ) == 0 )<br />
{<br />
DWORD dwOldFlag;<br />
VirtualProtect( _lawFunc, 8, PAGE_EXECUTE_READWRITE, &amp;dwOldFlag );<br />
memcpy( _lawFunc, _newByteCode, 16 );<br />
VirtualProtect( _lawFunc, 8, dwOldFlag, &amp;dwOldFlag );<br />
return true;<br />
}</p>
<p>return false;<br />
}</p>
<p>__InlineHOOK_Base( void* lawfun, void* newfun, DWORD args );<br />
};</p>
<p>void __declspec( naked ) __Inline_Hook_Func ( void )<br />
{<br />
__asm<br />
{<br />
push ebp // save maybe usefull register.<br />
push ebx<br />
push esi<br />
push ecx<br />
call __InlineHOOK_Base::unhook // first, remove the hook in order to call the normal function.<br />
test eax, eax // check the remove was successful<br />
jz __return</p>
<p>__getargnum:<br />
mov eax, dword ptr[esp] // esp just is ecx, also is __InlineHOOK_Base's this pointer.<br />
mov ecx, dword ptr[eax] // get first 4 bytes, that is params total size.<br />
shr ecx, 2 // get params num, equal with __InlineHOOK_Base::_argsBytes / sizeof( DWORD )<br />
test ecx, ecx // check whether there are params.<br />
jz __callfunc // no param</p>
<p>__pushargs:<br />
mov edx, esp // __InlineHOOK_Base's this pointer.<br />
add edx, 14h // navigate to first call ret addr.<br />
add edx, dword ptr[eax]; // add params size.<br />
push dword ptr[edx]; // push the dll file name pointer.<br />
loop __pushargs</p>
<p>__callfunc:<br />
call [eax+8] // call my function .<br />
mov ecx, dword ptr[esp] // get __InlineHOOK_Base's this pointer.</p>
<p>push edx // save my function return value.<br />
push eax</p>
<p>call __InlineHOOK_Base::hook // rehook.</p>
<p>pop eax // get saved return value, provided to my superiors to use<br />
pop edx</p>
<p>__return:<br />
pop ecx<br />
pop esi<br />
pop ebx<br />
pop ebp<br />
ret<br />
}<br />
}</p>
<p>__InlineHOOK_Base ::__ InlineHOOK_Base ( void* lawfun, void* newfun, DWORD args )<br />
: _lawFunc( lawfun ), _newFunc( newfun ), _argsBytes( args * 4 )<br />
{<br />
_newByteCode[ 0 ] = 0xB9; // mov ecx, ...<br />
( DWORD&amp; )_newByteCode[ 1 ] = ( DWORD )this;<br />
_newByteCode[ 5 ] = 0xB8; // mov eax, ...<br />
( DWORD&amp; )_newByteCode[ 6 ] = ( DWORD )__Inline_Hook_Func;<br />
( WORD&amp; )_newByteCode[ 10 ] = 0xD0FF; // call eax<br />
_newByteCode[ 12 ] = 0x000000C3; // ret</p>
<p>if ( _newByteCode &gt; 0 )<br />
{<br />
_newByteCode[ 12 ] = 0xC2; // ret ...<br />
( WORD&amp; )_newByteCode[ 13 ] = ( WORD )_argsBytes;<br />
_newByteCode[ 15 ] = 0;<br />
}</p>
<p>memcpy( _lawByteCode, _lawFunc, 16 ); // save<br />
}</p>
<p>template<br />
struct Inline_Hook : __InlineHOOK_Base<br />
{<br />
Inline_Hook( _function lawfun, _function newfun )<br />
:__InlineHOOK_Base( lawfun, newfun, args ) { hook(); }<br />
~Inline_Hook( void ){ unhook(); }<br />
};</p>
<p>MYHOOKMETHOD ( myLoadLibrary )( LPCSTR lpcStrFileName )<br />
{<br />
::MessageBox( NULL, lpcStrFileName, "LoadLibrary Name", MB_OK | MB_ICONINFORMATION );<br />
return LoadLibraryA( lpcStrFileName );<br />
}</p>
<p>DECLARE_REGISTER ( __inline_hook , LoadLibraryA , myLoadLibrary );</p>
<p>int main ( void )<br />
{<br />
HMODULE hIntstance = LoadLibraryA ( "d3d9.dll" );<br />
return 0;<br />
}<br />
上面这个程序是我写的一个测试。原理很简单，也就是在调用LoadLibraryA 加载动态链接库之前，先把LoadLibraryA的前16个代码字节给替换成我们自己的HOOK拦截代码，原理跟我之前的一篇Shell Code原理类似！改变了前16个字节后，这时就是已经HOOK了的LoadLibraryA了。然后在程序调用这个函数，进入后。将先调用我们自己写的函数。这里我们自己的函数是myLoadLibrary。这里面我就随便写了个测试。弹一个MessageBox显示DLL的名称！然后再执行正常的LoadLibraryA。看到这里，或许大家会产生两个疑问。<br />
1.为什么替换的是16个字节？<br />
2.在调用了我们的函数后，再调用正常的LoadLibraryA。这里的LoadLibraryA不是已经被我们给替换了吗？怎么正常呢？<br />
首先，第一个问题。这里就得看上方蓝色的函数__InlineHOOK_Base 了。先是这个结构体：<br />
DWORD _argsBytes; // 参数所占的字节数<br />
void* _lawFunc; // 指向老的Hook前的LoadLibraryA函数的一个指针<br />
void* _newFunc; // 指向我们自己的中间函数的指针<br />
char _lawByteCode[16]; // 保存正常的LoadLibraryA前16个代码字节，用于UnHook，不然怎么还原呢。呵呵！<br />
char _newByteCode[16]; // 我们替换给LoadLibraryA的16个代码字节，用于Hook，不然怎么执行我们自己的函数呢，呵呵！<br />
所以在我们调用LoadLibraryA之前会调用__InlineHOOK_Base构造函数。因为是全局对象。如：DECLARE_REGISTER ( __inline_hook , LoadLibraryA , myLoadLibrary );</p>
<p>_newByteCode[ 0 ] = 0xB9; // mov ecx, ...<br />
( DWORD&amp; )_newByteCode[ 1 ] = ( DWORD )this;<br />
_newByteCode[ 5 ] = 0xB8; // mov eax, ...<br />
( DWORD&amp; )_newByteCode[ 6 ] = ( DWORD )__Inline_Hook_Func;<br />
( WORD&amp; )_newByteCode[ 10 ] = 0xD0FF; // call eax<br />
_newByteCode[ 12 ] = 0x000000C3; // ret</p>
<p>if ( _newByteCode &gt; 0 )<br />
{<br />
_newByteCode[ 12 ] = 0xC2; // ret ...<br />
( WORD&amp; )_newByteCode[ 13 ] = ( WORD )_argsBytes;<br />
_newByteCode[ 15 ] = 0;<br />
}</p>
<p>上面这段代码的功能就是将语句转化成字节码，存到_newByteCode数组中，然后在HOOK的时候会拷贝到正常的LoadLibraryA中。将其前16字节替换成这里的。为什么是16字节，原因含简单，那就是这里的字节码就只用得到15个。哈哈！将上面的字节码翻译成C++就是：<br />
__Inline_Hook_Func(); //一句！<br />
其他的就是为了将this保存到ECX中，返回如果有参数，且这个LoadLibraryA只有一个参数，在我们替换的字节码中手工给保持堆栈平衡，就会ret _argBytes这么字节数！调用外面就不用ADD ESP了保持堆栈平衡了！这也是LoadLibraryA原先的返回方式！这里这些指令的用法和为什么把this保存到ECX中，就不多说了！我的SHELL CODE那篇文章里有提到！</p>
<p>memcpy( _lawByteCode, _lawFunc, 16 ); // 保存正常的字节码，用于还原！</p>
<p>这样把我们要替换进去的字节码给准备好了，下一步就是拷贝进去的过程了。Inline_Hook 构造函数是调用了HOOK函数的。再看HOOK函数的实现是：<br />
// It's saved.<br />
if ( memcmp( _lawByteCode, _lawFunc, 16 ) == 0 ) // 看看是否保存了，否则还原不了没办法unhook。<br />
{<br />
DWORD dwOldFlag;<br />
VirtualProtect( _lawFunc, 8, PAGE_EXECUTE_READWRITE, &amp;dwOldFlag ); //这个函数就不用说了，呵呵，查资料吧！<br />
memcpy( _lawFunc, _newByteCode, 16 ); // 拷贝我们的HOOK代码进LoadLibraryA中！<br />
VirtualProtect( _lawFunc, 8, dwOldFlag, &amp;dwOldFlag );<br />
return true; // 拷贝成功！<br />
}<br />
unhook原理类似，也就是将正常的拷贝进去！<br />
好了！到了现在，功能函数都差不多了，现在就差__Inline_Hook_Func 这个函数的实现了！<br />
这个函数全是汇编。嘿嘿！我用的英文注释了的哈。大致能看明白，英语太差了！<br />
这里只说它的功能：<br />
1.这个函数的调用代码字节我们已经拷贝到了LoadLibraryA中。如果我们调用LoadLibraryA( "XXXX.DLL" );将会调用__Inline_Hook_Func 这个函数。这个函数的前缀是不是很奇怪？大鸟别笑。呵呵！我前面的文章也提到了这些前缀的意思。这里不用多说。现在看看我们拷贝进LoadLibraryA后，大家可以看看LoadLibraryA的字节码的前后对比：<br />
替换前：<br />
7C801D7B 8B FF mov edi,edi<br />
7C801D7D 55 push ebp<br />
7C801D7E 8B EC mov ebp,esp<br />
7C801D80 83 7D 08 00 cmp dword ptr [ebp+8],0<br />
7C801D84 53 push ebx<br />
7C801D85 56 push esi<br />
7C801D86 74 14 je 7C801D9C<br />
7C801D88 68 60 E1 80 7C push 7C80E160h<br />
7C801D8D FF 75 08 push dword ptr [ebp+8]<br />
7C801D90 FF 15 AC 13 80 7C call dword ptr ds:[7C8013ACh]<br />
7C801D96 85 C0 test eax,eax<br />
7C801D98 59 pop ecx<br />
7C801D99 59 pop ecx<br />
7C801D9A 74 12 je 7C801DAE<br />
7C801D9C 6A 00 push 0<br />
7C801D9E 6A 00 push 0<br />
7C801DA0 FF 75 08 push dword ptr [ebp+8]<br />
7C801DA3 E8 AB FF FF FF call 7C801D53<br />
7C801DA8 5E pop esi<br />
7C801DA9 5B pop ebx<br />
7C801DAA 5D pop ebp<br />
7C801DAB C2 04 00 ret 4<br />
上面红色的字节码就是将要被替换的。 蓝色的ret 4可以看出开始的疑问，为什么是ret 4.<br />
替换后：<br />
7C801D7B B9 E0 A5 42 00 mov ecx,offset __Obj (42A5E0h)<br />
7C801D80 B8 BC 12 41 00 mov eax,offset __Inline_Hook_Func (4112BCh)<br />
7C801D85 FF D0 call eax<br />
7C801D87 C2 04 00 ret 4<br />
7C801D8A 00 80 7C FF 75 08 add byte ptr [eax+875FF7Ch],al<br />
7C801D90 FF 15 AC 13 80 7C call dword ptr ds:[7C8013ACh]<br />
7C801D96 85 C0 test eax,eax<br />
7C801D98 59 pop ecx<br />
7C801D99 59 pop ecx<br />
7C801D9A 74 12 je 7C801DAE<br />
7C801D9C 6A 00 push 0<br />
7C801D9E 6A 00 push 0<br />
7C801DA0 FF 75 08 push dword ptr [ebp+8]<br />
7C801DA3 E8 AB FF FF FF call 7C801D53<br />
7C801DA8 5E pop esi<br />
7C801DA9 5B pop ebx<br />
7C801DAA 5D pop ebp<br />
7C801DAB C2 04 00 ret 4<br />
上面的字节码，相信一看就明白了！呵呵！替换后就会在0x7C801D87这里返回了，下面的代码就作废了！ - -</p>
<p>2. __Inline_Hook_Func 这个函数在进入之后，先是unhook，因为下面要调用myLoadLibrary函数。肯定要正常的LoadLibraryA函数咯。之后就是一系列的参数准备。知道调用了myLoadLibrary函数，弹出了对话框。之后回到 __Inline_Hook_Func 函数。保存返回值（句柄）。之后再hook掉LoadLibraryA函数。让第二次还能进来先调用我们的函数。之后就是返回值给上层主调函数了！<br />
上面的2问题也就解开了。那就是因为 __Inline_Hook_Func一开始就给我们unhook了。 myLoadLibrary函数使用的一直都是正常的 LoadLibraryA 函数！</p>
<p>其他：<br />
这里只是讲了原理，练下手可以。呵呵！一般稍微有水平一点的程序是检测了这个 LoadLibraryA函数是否被修改的。技术这东西就是你有招我也有招！呵呵！</p>
<p>好了。这篇文章终于写完了，累！有什么不对的地方还望各位批评！在此感谢。。。</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/zh-cn/blogs/2010/05/12/cc-hook-api-loadlibrarya/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

