1. 程式人生 > >windows下的內聯hook實現

windows下的內聯hook實現

HOOK技術正如其名,就像是程式碼中放下的一個“鉤子”,它在靜靜地等待捕獲系統中的某個訊息或動作。在程式設計技術中,鉤子技術在DOS時代就已經存在了。在windows下,鉤子按照實現技術的不同和掛鉤位置的不同,其種類也是越來越多,但是設定鉤子的本質卻是始終不變的。

那麼鉤子究竟有什麼用?它能幹的事非常多,例如輸入監控、API攔截、訊息捕獲、改變程式執行流程等。防毒軟體會用HOOK技術鉤住一些API函式,比如登錄檔讀寫函式,從而防止病毒對登錄檔的操作;病毒使用HOOK技術有針對性的捕獲鍵盤的輸入,從而記錄使用者的密碼;檔案系統通過HOOK技術在不改變使用者操作的情況下對使用者的檔案進行透明加密。這些都屬於HOOK的技術範疇。

內聯HOOK的原理

API函式都儲存在作業系統提供的DLL檔案中。當在程式中呼叫某個API函式並執行程式後,程式會隱式地將API函式所在的DLL檔案載入入程序中。這樣,程式就可以像呼叫自己的函式一樣呼叫API。大體過程如下圖所示:


CreateProcessW()是API 函式,API函式也是程式設計師的程式碼編譯而成,也有其對應的二進位制程式碼。既然是程式碼,就可以被修改。通過一種強制性的手段修改API函式在記憶體中的映像,從而對API 函式進行hook。使用的方法是,直接使用匯編指令的jmp指令將其的程式碼執行流程改變,進而執行自己的程式碼。執行完自己的程式碼後可以選擇性地執行其它的程式碼。

在Windows下,大部分應用程式都是由Explorer.exe程序來建立的。那麼只要把Explorer.exe中建立程序的函式CreateProcessW()  hook住,就可以自主控制是否讓它建立摸個特定程序。執行流程如下:


由於這種方法是直接在程式流程中嵌入jmp指令來改變流程的,所以把它叫做Inline  Hook。

在二進位制檔案中,程式碼部分都是CPU可以用來執行的機器碼,機器碼和彙編指令是一一對應的。使用 :jmp  目的地址,該彙編指令的長度為5個位元組。具體可以用OD開啟任意程式,修改某條指令為jmp格式的指令即可看到效果。其中jmp指令後跟著的是從當前地址到目的地址的偏移量。如下圖所示:

準備修改00402390地質處的反彙編程式碼為jmp指令:


修改後的反彙編程式碼:


下一個指令的末尾是95,由此可見該jmp命令的長度是5位元組。

jmp後的偏移量 = 目標地址 – 原地址 – jcc的指令長度。

下圖是,目標地址 – 原地址後的值,由於本次我們使用的jmp指令長度是5,所以在減去5,最後 偏移量 = 11F432E3,正是上面所給出的值(主機使用的是小端位元組序)。


注:

小端位元組序:低位元組存於記憶體低地址;高位元組存於記憶體高地址。

大端位元組序:高位元組存於記憶體低地址;低位元組存於記憶體高地址。

網路位元組序:就是大端位元組序。規定不同系統間通訊一律採用網路位元組序。

總結一下,InlineHook的流程大致如下:

1.     構造跳轉指令

2.   在記憶體中找到要HOOK的函式地址,並儲存要HOOK位置處的前指令長度位元組數內容。

3.   將構造的跳轉指令寫入需要HOOK 的位置處。

4.   當被HOOK位置被執行時會轉到自己的流程中執行。

5.   如果要執行原來的流程,那麼取消HOOK,也就是還原被修改的位元組。

6.   執行原來的流程。

7.   繼續HOOK原來的位置。

下面是一個簡單的例子:

首先,新建一個win32下的DLL工程。在工程中新增一個類,用於操作鉤子。

.h檔案中的主要程式碼如下:

#include<Windows.h>

class CILHOOK
{
public:
	CILHOOK();			//構造
	~CILHOOK();			//析構

	//HOOK函式
	BOOL Hook(LPSTR pszModuleName,
				LPSTR pszFuncName,
				PROC pfnHookFunc);
	
	//取消HOOK函式
	VOID UnHook();

	//重新進行HOOK
	BOOL ReHook();

private:
	PROC	m_pfnOrig;			//函式地址
	BYTE	m_bOldBytes[5];		//函式入口程式碼
	BYTE	m_bNewBytes[5];		//Inline程式碼
};

.cpp檔案中的主要程式碼如下:

CILHOOK::CILHOOK()
{
	m_pfnOrig = NULL;
	ZeroMemory(m_bOldBytes, 5);
	ZeroMemory(m_bNewBytes, 5);
}

CILHOOK::~CILHOOK()
{
	//取消HOOK
	UnHook();

	m_pfnOrig = NULL;
	ZeroMemory(m_bOldBytes, 5);
	ZeroMemory(m_bNewBytes, 5);
}

/*
函式名:Hook
函式功能:對指定模組的函式進行掛鉤
引數說明:
pszModuleName:模組名稱
pszFuncName:函式名稱
pfnHookFunc:鉤子函式
*/
BOOL CILHOOK::Hook(LPSTR pszModuleName,
				LPSTR pszFuncName,
				PROC pfnHookFunc)
{
	BOOL bRet = FALSE;

	//獲取指定模組中函式的地址
	m_pfnOrig = (PROC)GetProcAddress(GetModuleHandle(pszModuleName), pszFuncName);

	if(m_pfnOrig != NULL)
	{
		DWORD dwNum = 0;
		//將原來的資料存起來
		ReadProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bOldBytes, 5, &dwNum);

		//構造jmp指令
		m_bNewBytes[0] = '\xe9';		//jmp Opcode
		//pfnHookFunc是HOOK後的目標地址
		//m_pfnOrig是原來的地址
		//5是指令長度
		*(DWORD *)(m_bNewBytes + 1) = (DWORD)pfnHookFunc - (DWORD)m_pfnOrig - 5;

		//將構造好的地址寫入該地址處
		WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bNewBytes, 5, &dwNum);

		bRet = TRUE;
	}
	return bRet;
}


/*
函式名稱:UnHook
函式功能:取消函式的掛鉤
*/
VOID CILHOOK::UnHook()
{
	if(m_pfnOrig != 0)
	{
		DWORD dwNum = 0;
		//將原來的內容寫回hook地址
		WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bOldBytes, 5, &dwNum);
	}
}

/*
函式名稱:ReHook
函式功能:重新對函式進行掛鉤
*/
BOOL CILHOOK::ReHook()
{
	BOOL bRet = FALSE;

	if(m_pfnOrig != 0)
	{
		DWORD dwNum = 0;
		//寫入掛鉤地址
		WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bNewBytes, 5, &dwNum);
		bRet = TRUE;
	}
	return bRet;
}

DLL中的cpp主要程式碼:

//全域性HOOK物件
CILHOOK CreateProcessHook;

BOOL WINAPI MyCreateProcessW(LPCWSTR lpApplicationName,
	LPWSTR lpCommandLine,
	LPSECURITY_ATTRIBUTES lpProcessAttributes,
	LPSECURITY_ATTRIBUTES lpThreadAttributes,
	BOOL bInheritHandles,
	DWORD dwCreationFlags,
	LPVOID lpEnvironment,
	LPCWSTR lpCurrentDirectory,
	LPSTARTUPINFOW lpStartupInfo,
	LPPROCESS_INFORMATION lpProcessInformation)
{

	BOOL bRet = FALSE;
	
	//彈出被建立的程序名
	if(MessageBoxW(NULL, lpApplicationName, lpCommandLine, MB_YESNO) == IDYES)
	{
	
		//自己呼叫函式前先去掉鉤子,否則會進入死迴圈
		CreateProcessHook.UnHook();

		bRet = CreateProcessW( lpApplicationName,
		lpCommandLine,
		lpProcessAttributes,
		lpThreadAttributes,
		bInheritHandles,
		dwCreationFlags,
		lpEnvironment,
		lpCurrentDirectory,
		lpStartupInfo,
		lpProcessInformation);

		CreateProcessHook.ReHook();

	}else{
		MessageBox(NULL, "您啟動的程式被攔截", "提示", MB_OK);
	}

	return bRet;

}



BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		  {
			  //HOOK CreateProcessW()函式
			  CreateProcessHook.Hook("kernel32.dll", "CreateProcessW", (PROC)MyCreateProcessW);
			  break;
		  }
	case DLL_PROCESS_DETACH:
		{
			CreateProcessHook.UnHook();
			break;
		}
	}
    return TRUE;
}

接下來用我前面文章中用到的DLL注入器進行注入相應的DLL即可。

下面是執行時的介面:

注入explorer.exe程序,然後點選開啟IE,此時會hook到我們指定的函式。選擇是,可以開啟IE;選擇否,將不開啟IE程式。選否時如下圖所示:



相關推薦

windowshook實現

HOOK技術正如其名,就像是程式碼中放下的一個“鉤子”,它在靜靜地等待捕獲系統中的某個訊息或動作。在程式設計技術中,鉤子技術在DOS時代就已經存在了。在windows下,鉤子按照實現技術的不同和掛鉤位置的不同,其種類也是越來越多,但是設定鉤子的本質卻是始終不變的。 那麼鉤子

合金彈頭 逆向分析與外掛制作報告【HOOK

scm 代碼塊 opc 進行 void HR creat 其它 遊戲數據 一、工具及遊戲介紹 使用工具:Ollydbg,PEID,Cheat Engine 實現功能:玩家無敵 目標:找到全局數據,或關鍵代碼塊。 遊戲版本:合金彈頭1-5代珍藏版

android hook

bit != fff andro html -a 速度 git 其它 先回顧下 x86 下的內聯 hook.1.原理是找到你要 hook 的地址。2.保存這個地址原來的數據。(這裏要保存至少 5 個字節的數據因為一個 call指令為 5 個字節3.把這個地址修改成 call

windows 借助7zip實現命令行解壓縮

電腦下載 調用 處理 文件 left 解壓 壓縮 dll 命令行 windows 下借助7zip實現命令行解壓縮 64位電腦下載 https://www.7-zip.org/a/7z1805-x64.exe 安裝 安裝目錄下所有文件如下: 在命令行下只需要用到 7z

VC彙編實現strcpy+彙編效能分析

使用內聯彙編來實現strcpy的兩種思路: static void _strcpy1( char *dst, char *src ){ __asm {  push ecx;   push eax;   push es

windowsvc呼叫openssl實現RSA加密

     拿到了linux下c實現的RSA呼叫原始碼,想在windows下程式設計實現相同的結果,查了查資料,在vc6和vs2010除錯通過,在win7 x64和winXP 32 執行結果一致,記錄下來,以備日後查詢。 一、安裝openssl 1、進入Win32 Open

windows的網路程式設計實現

下面是一個簡單的windows下的服務端和客戶端的實現程式碼需要注意的是 1、此程式設計實現的只是最簡單的網路通訊,不支援多程序和多執行緒的併發(網上找到很多過於多程序和多執行緒併發的伺服器端設計,不過都是在linux下的,在windows下的建立程序和執行緒的函式不熟悉,也沒怎麼看懂,

windows使用net-snmp實現agent擴充套件(二)

剛剛實現了int型的get命令,可能更多的情況下,我們更需要的是字串型別的。在實現int型的時候,用到了netsnmp_register_int_instance這個函式,很自然想到如果是string型的,用類似的netsnmp_register_string_instanc

windows使用net-snmp實現agent擴充套件(四)

在前三篇的內容裡,介紹了使用net-snmp實現agent的Get/Set命令,下面介紹一下發送trap訊息。傳送trap訊息時,系統預設的埠是162。使用下面的程式碼,可以實現trap訊息的傳送。 //該函式傳送實時報警資訊。與傳送一般資訊埠不同 void init_al

windows使用net-snmp實現agent擴充套件(三)

時間隔得太長了,我都快忘了什麼是snmp了,知識啊知識,很容易在不用的時候忘卻,也可能是自己腦袋不好使了吧?翻了翻程式碼,趕緊總結下,不然真不會了…… 在上篇部落格中,實現了get/set一個字串型變數,現在來實現對多個字串變數的get/set。假設要實現獲取CPU利用率、

windows用Go語言實現第一個hello world

1,下載go編譯器———go編譯器下載地址https://golang.org/dl/ go編譯器下載地址 2,然後點選進行安裝,由於是msi檔案,如果需要.NET元件請自行下載進行安裝

WindowsNginx負載均衡實現

Nginx+iis站點實現負載均衡方法: 1.官網下載nginx壓縮檔案解壓。我本地解壓路徑D盤   2.配置nginx  開啟nginx.conf檔案 在http模組內新增 upstream 模組,插入  upstream www.g.cn {server

windows編譯Fast RCNN實現物體識別

參考:http://blog.csdn.net/happynear/article/details/46822109 一、準備 1. caffe-windows:https://github.com

windows配置mycat,實現mysql資料庫的讀寫分離!

下載Windows的安裝包 配置環境變數: 修改配置檔案 wrapper.conf Server.xml <?xmlversion=

windows 命令列+winscp 實現與linux的遠端檔案傳輸

安裝WinSCP WinSCP 是一個 Windows 環境下使用的 SSH 的開源圖形化 SFTP 客戶端。同時支援 SCP 協議。它的主要功能是在本地與遠端計算機間安全地複製檔案,並且可以直接編輯檔案。 - 下載:https://winscp.net/e

Windows多程序的實現案例

       最近碰到一個多程序的問題牽涉到了Windows下多程序的操作。經過研究也算是找到了一種解決方案。下面具體介紹一下這個題目中自己的收穫。        背景介紹:程式涉及到Win32與Linux兩個平臺,要求同時啟動多個程序,程序下面啟動 多個執行緒。由於Win

windowsbat批處理實現數據庫備份、壓縮、刪除

rar 備份數據庫 data game -i 批處理 android backup files @echo off rem 刪除7天前的數據庫備份 cd D:\db_bak\qd_web_project\dumpforfiles /m *.sql.gz /d -7 /c

Windows條件變數的實現

條件變數是什麼? 是一種同步物件。   條件變數有什麼用? 用於複雜的、多執行緒的、多核的程式中,實現多個執行緒

windows控制臺程序實現窗口顯示

procedure show height ret win32 arr window classname 例程 windows下實現窗口顯示,如果限定是C/C++語言,並且是原生Windows支持,需要使用GDI或GDI+。一般是在Visual Studio裏新建Win32

mysql數據庫在Linux和windows免安裝實現以及框架開發碰到的問題

自動啟動 過程 root mil 是否 call 啟動頁 同時 ice 2017年7月23號下午5:20分,上周我根據自己的實際情況,總結了mysql數據庫在windows系統下和linux系統下免安裝版本的實現,以及在項目開發中遇到的數據庫報錯,今天整理出來,以供日後學習