9月都快結束了,之前一直忙到寫自己的東西加上上班。基本沒有時間研究下彙編和C C++方面的感興趣的東西。再怎麼說嘛,9月還是得寫一篇撒,以後每月至少一篇吧。給自己定了,希望大家監督。嘿嘿!
這篇文章就來談談平常很常見的HOOK技術,這裏呢。寫得比較簡單,方法很多。只講原理!希望大鳥們別吐我口水哈 - -。好!切入正題。
首先是概念吧。什麼是鉤子(HOOK)?
鉤子(Hook),是Windows消息處理機制的一個平臺,應用程序可以在上面設置子程以監視指定窗口的某種消息,而且所監視的窗口可以是其他進程所創建的。當消息到達後,在目標窗口處理函數之前處理它。鉤子機制允許應用程序截獲處理window消息或特定事件。
鉤子實際上是一個處理消息的程序段,通過系統調用,把它掛入系統。每當特定的消息發出,在沒有到達目的窗口前,鉤子程序就先捕獲該消息,亦即鉤子函數先得到控制權。這時鉤子函數即可以加工處理(改變)該消息,也可以不作處理而繼續傳遞該消息,還可以強制結束消息的傳遞。
這上面只是一個概念,對它有所瞭解而已。上面主要應用在Windows消息處理機制裏面的一個解釋。這裏我只是單純的談談攔截我們常用的LoadLibraryA加載這個函數。讓我們的程序或者目標程序在調用這個函數加載鏈接庫的時候,先執行我們自己寫的函數,然後在進行正常加載。通俗的說就是a----->b. 我們在中間加上一個c。 a-------->c----->b讓他先執行c然後再執行b。這裏的c就是我們自己的函數了。
呵呵,概念說得差不多了,開始行動寫代碼撒:
上面這個程序是我寫的一個測試。原理很簡單,也就是在調用LoadLibraryA 加載動態鏈接庫之前,先把LoadLibraryA的前16個代碼字節給替換成我們自己的HOOK攔截代碼,原理跟我之前的一篇Shell Code原理類似!改變了前16個字節後,這時就是已經HOOK了的LoadLibraryA了。然後在程序調用這個函數,進入後。將先調用我們自己寫的函數。這裏我們自己的函數是myLoadLibrary。這裏面我就隨便寫了個測試。彈一個MessageBox顯示DLL的名稱!然後再執行正常的LoadLibraryA。看到這裏,或許大家會產生兩個疑問。
1.爲什麼替換的是16個字節?
2.在調用了我們的函數後,再調用正常的LoadLibraryA。這裏的LoadLibraryA不是已經被我們給替換了嗎?怎麼正常呢?
首先,第一個問題。這裏就得看上方藍色的函數__InlineHOOK_Base 了。先是這個結構體:
DWORD _argsBytes; // 參數所佔的字節數
void* _lawFunc; // 指向老的Hook前的LoadLibraryA函數的一個指針
void* _newFunc; // 指向我們自己的中間函數的指針
char _lawByteCode[16]; // 保存正常的LoadLibraryA前16個代碼字節,用於UnHook,不然怎麼還原呢。呵呵!
char _newByteCode[16]; // 我們替換給LoadLibraryA的16個代碼字節,用於Hook,不然怎麼執行我們自己的函數呢,呵呵!
所以在我們調用LoadLibraryA之前會調用__InlineHOOK_Base構造函數。因爲是全局對象。如:DECLARE_REGISTER ( __inline_hook , LoadLibraryA , myLoadLibrary );
_newByteCode[ 0 ] = 0xB9; // mov ecx, ...
( DWORD& )_newByteCode[ 1 ] = ( DWORD )this;
_newByteCode[ 5 ] = 0xB8; // mov eax, ...
( DWORD& )_newByteCode[ 6 ] = ( DWORD )__Inline_Hook_Func;
( WORD& )_newByteCode[ 10 ] = 0xD0FF; // call eax
_newByteCode[ 12 ] = 0x000000C3; // ret
if ( _newByteCode > 0 )
{
_newByteCode[ 12 ] = 0xC2; // ret ...
( WORD& )_newByteCode[ 13 ] = ( WORD )_argsBytes;
_newByteCode[ 15 ] = 0;
}
上面這段代碼的功能就是將語句轉化成字節碼,存到_newByteCode數組中,然後在HOOK的時候會拷貝到正常的LoadLibraryA中。將其前16字節替換成這裏的。爲什麼是16字節,原因含簡單,那就是這裏的字節碼就只用得到15個。哈哈!將上面的字節碼翻譯成C++就是:
__Inline_Hook_Func(); //一句!
其他的就是爲了將this保存到ECX中,返回如果有參數,且這個LoadLibraryA只有一個參數,在我們替換的字節碼中手工給保持堆棧平衡,就會ret _argBytes這麼字節數!調用外面就不用ADD ESP了保持堆棧平衡了!這也是LoadLibraryA原先的返回方式!這裏這些指令的用法和爲什麼把this保存到ECX中,就不多說了!我的SHELL CODE那篇文章裏有提到!
memcpy( _lawByteCode, _lawFunc, 16 ); // 保存正常的字節碼,用於還原!
這樣把我們要替換進去的字節碼給準備好了,下一步就是拷貝進去的過程了。Inline_Hook 構造函數是調用了HOOK函數的。再看HOOK函數的實現是:
// It's saved.
if ( memcmp( _lawByteCode, _lawFunc, 16 ) == 0 ) // 看看是否保存了,否則還原不了沒辦法unhook。
{
DWORD dwOldFlag;
VirtualProtect( _lawFunc, 8, PAGE_EXECUTE_READWRITE, &dwOldFlag ); //這個函數就不用說了,呵呵,查資料吧!
memcpy( _lawFunc, _newByteCode, 16 ); // 拷貝我們的HOOK代碼進LoadLibraryA中!
VirtualProtect( _lawFunc, 8, dwOldFlag, &dwOldFlag );
return true; // 拷貝成功!
}
unhook原理類似,也就是將正常的拷貝進去!
好了!到了現在,功能函數都差不多了,現在就差__Inline_Hook_Func 這個函數的實現了!
這個函數全是彙編。嘿嘿!我用的英文註釋了的哈。大致能看明白,英語太差了!
這裏只說它的功能:
1.這個函數的調用代碼字節我們已經拷貝到了LoadLibraryA中。如果我們調用LoadLibraryA( "XXXX.DLL" );將會調用__Inline_Hook_Func 這個函數。這個函數的前綴是不是很奇怪?大鳥別笑。呵呵!我前面的文章也提到了這些前綴的意思。這裏不用多說。現在看看我們拷貝進LoadLibraryA後,大家可以看看LoadLibraryA的字節碼的前後對比:
替換前:
7C801D7B 8B FF mov edi,edi
7C801D7D 55 push ebp
7C801D7E 8B EC mov ebp,esp
7C801D80 83 7D 08 00 cmp dword ptr [ebp+8],0
7C801D84 53 push ebx
7C801D85 56 push esi
7C801D86 74 14 je 7C801D9C
7C801D88 68 60 E1 80 7C push 7C80E160h
7C801D8D FF 75 08 push dword ptr [ebp+8]
7C801D90 FF 15 AC 13 80 7C call dword ptr ds:[7C8013ACh]
7C801D96 85 C0 test eax,eax
7C801D98 59 pop ecx
7C801D99 59 pop ecx
7C801D9A 74 12 je 7C801DAE
7C801D9C 6A 00 push 0
7C801D9E 6A 00 push 0
7C801DA0 FF 75 08 push dword ptr [ebp+8]
7C801DA3 E8 AB FF FF FF call 7C801D53
7C801DA8 5E pop esi
7C801DA9 5B pop ebx
7C801DAA 5D pop ebp
7C801DAB C2 04 00 ret 4
上面紅色的字節碼就是將要被替換的。 藍色的ret 4可以看出開始的疑問,爲什麼是ret 4.
替換後:
7C801D7B B9 E0 A5 42 00 mov ecx,offset __Obj (42A5E0h)
7C801D80 B8 BC 12 41 00 mov eax,offset __Inline_Hook_Func (4112BCh)
7C801D85 FF D0 call eax
7C801D87 C2 04 00 ret 4
7C801D8A 00 80 7C FF 75 08 add byte ptr [eax+875FF7Ch],al
7C801D90 FF 15 AC 13 80 7C call dword ptr ds:[7C8013ACh]
7C801D96 85 C0 test eax,eax
7C801D98 59 pop ecx
7C801D99 59 pop ecx
7C801D9A 74 12 je 7C801DAE
7C801D9C 6A 00 push 0
7C801D9E 6A 00 push 0
7C801DA0 FF 75 08 push dword ptr [ebp+8]
7C801DA3 E8 AB FF FF FF call 7C801D53
7C801DA8 5E pop esi
7C801DA9 5B pop ebx
7C801DAA 5D pop ebp
7C801DAB C2 04 00 ret 4
上面的字節碼,相信一看就明白了!呵呵!替換後就會在0x7C801D87這裏返回了,下面的代碼就作廢了! - -
2. __Inline_Hook_Func 這個函數在進入之後,先是unhook,因爲下面要調用myLoadLibrary函數。肯定要正常的LoadLibraryA函數咯。之後就是一系列的參數準備。知道調用了myLoadLibrary函數,彈出了對話框。之後回到 __Inline_Hook_Func 函數。保存返回值(句柄)。之後再hook掉LoadLibraryA函數。讓第二次還能進來先調用我們的函數。之後就是返回值給上層主調函數了!
上面的2問題也就解開了。那就是因爲 __Inline_Hook_Func一開始就給我們unhook了。 myLoadLibrary函數使用的一直都是正常的 LoadLibraryA 函數!
其他:
這裏只是講了原理,練下手可以。呵呵!一般稍微有水平一點的程序是檢測了這個 LoadLibraryA函數是否被修改的。技術這東西就是你有招我也有招!呵呵!
好了。這篇文章終於寫完了,累!有什麼不對的地方還望各位批評!在此感謝。。。
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/masefee/archive/2009/09/18/4566121.aspx