加密解密學習之Hook學習


一、Windows中常指的Hook--鈎子

       全局鈎子,大多需要借助一個dll(鈎子的安裝和卸載以及鈎子處理函數),因為不同的進程間不能隨意相互訪問內存空間,所以通常借助dll。如果只是hook本進程的消息,可以不用dll。通過函數SetWindowsHookEx來向操作系統申請安裝鈎子,函數UnhookWindowsHookEx來卸載。關於二次hook的函數地址問題,所有Hook依次調用它Hook前的Address,形成了一個調用鏈。

 

// dllmain.cpp : 定義 DLL 應用程序的入口點。
#include "stdafx.h"
#include <Windows.h>
#include <fstream>
#include <TlHelp32.h>
using namespace std;




extern "C" _declspec(dllexport) void InstallHook(); 
extern "C" _declspec(dllexport) void UnInstallHook(); 

HHOOK hHook = NULL;
HMODULE g_hModule = NULL;

bool KillProcessEx(char* processName)
{
    HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 
    PROCESSENTRY32 pe;
    pe.dwSize = sizeof(PROCESSENTRY32);
 
    if (!Process32First(hSnapShot, &pe))
    {
        return false;
    }
 
    while (Process32Next(hSnapShot, &pe))
    {
		char strTemp[25]={0};
		int Length = WideCharToMultiByte(CP_ACP, 0, pe.szExeFile, -1, NULL, 0, NULL, NULL);  
		WideCharToMultiByte(CP_ACP, 0, pe.szExeFile, -1, strTemp, Length, NULL, NULL);
        bool bIn = false;
        if (strcmp(processName, strTemp)==0)
        {
			bIn = true;
        }
        if (bIn)
        {
            DWORD dwProcessID = pe.th32ProcessID;
            HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessID);
            ::TerminateProcess(hProcess, 0);
            CloseHandle(hProcess);
        }
    }
 
    return true;
}

LRESULT CALLBACK KeyboardProc(
  _In_  int code,
  _In_  WPARAM wParam,
  _In_  LPARAM lParam
)
{
	if(code == HC_ACTION && lParam & 0x80000000)
	{
		std::ofstream ofile;               //定義輸出文件
		ofile.open("D:\\keybroad.txt",std::ios::app);
		if(wParam>=0x30 && wParam<=0x5A)
			ofile << char(wParam)<<' ';//可顯示的鍵值
		else
			ofile <<std::hex<<"0x"<<wParam<<' ';
		ofile.close();
	}
	return CallNextHookEx(hHook, code, wParam, lParam);
}

void InstallHook()
{
	hHook = SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,g_hModule,0);
	if(hHook)
		MessageBox(NULL, TEXT("成功安裝hook!"), TEXT("提示"), MB_OK);
}
	
VOID UnInstallHook()
{
	MessageBox(NULL, TEXT("即將卸載hook!"), TEXT("提示"), MB_OK);
	if(hHook!=NULL)
	{
		if(UnhookWindowsHookEx(hHook))
			MessageBox(NULL, TEXT("成功卸載hook!"), TEXT("提示"), MB_OK);
	}

	CloseHandle(hHook);
	//KillProcessEx("Test.exe");
	TerminateProcess(GetCurrentProcess(),-1);
}
								
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		g_hModule = hModule;
        break;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}	

 

//Test.cpp
#include <Windows.h>

int main()  
{  
	HMODULE hModule = LoadLibraryA("TestDll.dll");
	FARPROC pfInstallHook = GetProcAddress(hModule,"InstallHook");
	FARPROC pfUninstallHook = GetProcAddress(hModule,"UnInstallHook");

	pfInstallHook();
	pfUninstallHook();

    return 0;  
}

 

注:內核地址空間是唯一的,所以內核中的所有hook操作對所有的進程都有效。

 Hook的作用:對目標函數的執行內容進行攔截、復制、修改和記錄。

Hook的本質:插入特定的代碼,然后干擾程序原本的執行流程。

Hook的實現方式:

  1. Address Hook:修改數據實現hook;
  2. inline Hook:直接修改函數內部的指令實現hook。

注:Address Hook是原子操作不用擔心多線程執行目標函數因修改數據引發的錯誤,但是inline Hook存在該問題,所以修改前最好掛起所有其他的線程。

二、注入Hook

(一) Address Hook

修改各種表:

  1. IAT(輸入表):IAT具體指某個dll模塊中的IAT,存放的是函數的地址,必須以靜態調用的方式才會被hook,動態調用不受影響。
  2. EAT(輸出表):它存放的是函數地址的偏移,使用時需要加上模塊的基址。在hook后,所有試圖通過EAT獲取目標函數地址的操作都會受到影響。同樣僅對靜態調用有效。
  3. IDT(系統的中斷描述符表):是操作系統用於處理中斷機制,當中斷發生操作系統應該交給誰處理。該表的基址存放在idtr寄存器中,表內項目數存放在idtl寄存器中。
  4. SSDT(系統服務描述符表):程序調用API轉入內核處理前首先要用到。
  5. C++虛函數表:父類中定義了虛函數就會有一個虛函數指針指向虛函數表,若子類中也定義了虛函數,則它也有一個自己的虛函數表。(簡而言之,每個使用虛函數的類都被賦予自己的虛表。)該表僅是編譯器在編譯時設置的靜態數組。一個虛擬表為該類的對象可以調用的每個虛擬函數包含一個條目。該表中的每個條目都只是一個函數指針,指向該類可訪問的最衍生函數。創建類實例時,編譯器會自動設置* __ vptr指針,以便它指向該類的虛擬表。

IAT hook原理:通過獲取IAT所在內存頁的可寫權限,修改想要hook的函數在IAT中的函數地址為自己定義的函數地址,從而實現調用自己定義函數的目的。

#include <Windows.h>
#include <stdio.h>
#include <Dbghelp.h>
#pragma comment(lib,"imagehlp.lib") //以MessageBoxA的原型定義一個函數指針類型 typedef int (WINAPI *PFN_MessageBoxA)( HWND hWnd, // handle of owner window LPCTSTR lpText, // address of text in message box LPCTSTR lpCaption, // address of title of message box UINT uType // style of message box ); PFN_MessageBoxA OldMessageBox=NULL; //指向IAT中pThunk的地址 PULONG_PTR g_PointerToIATThunk = NULL; int WINAPI MyMessageBoxA( HWND hWnd, // handle of owner window LPCTSTR lpText, // address of text in message box LPCTSTR lpCaption, // address of title of message box UINT uType // style of message box ) { //在這里,你可以對原始參數進行任意操作 int ret; char newText[1024]={0}; char newCaption[256]="pediy.com"; //為防止原函數提供的緩沖區不夠,這里復制到我們自己的一個緩沖區中再進行操作 lstrcpy(newText,lpText); lstrcat(newText,"\n\tMessageBox Hacked by pediy.com!");//篡改消息框內容 uType|=MB_ICONERROR;//增加一個錯誤圖標 ret = OldMessageBox(hWnd,newText,newCaption,uType);//調用原MessageBox,並保存返回值 //調用原函數之后,可以繼續對OUT(輸出類)參數進行干涉,比如網絡函數的recv,可以干涉返回的內容 return ret; } BOOL InstallHook( HMODULE hModToHook, //待Hook的模塊基址 char* szModuleName, //目標DLL名字 char* szFuncName, //目標函數名字 PVOID DetourFunc, //Detour函數 PULONG_PTR* pThunkPointer, ULONG_PTR* pOriginalFuncAddr ) { PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor; PIMAGE_THUNK_DATA pThunkData; ULONG ulSize; HMODULE hModule = 0; ULONG_PTR TargetFunAddr; PULONG_PTR lpAddr; char* szModName; BOOL result = FALSE; BOOL bRetn = FALSE; hModule = LoadLibrary(szModuleName); TargetFunAddr = (ULONG_PTR)GetProcAddress(hModule, szFuncName); pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModToHook, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);

while(pImportDescriptor->FirstThunk) { szModName = (char*)((PBYTE)hModToHook + pImportDescriptor->Name); if (stricmp(szModName, szModuleName) != 0) { pImportDescriptor++; continue; } pThunkData = (PIMAGE_THUNK_DATA)((BYTE*)hModToHook + pImportDescriptor->FirstThunk); while (pThunkData->u1.Function) { lpAddr = (ULONG_PTR*)pThunkData; if ((*lpAddr) == TargetFunAddr) { DWORD dwOldProtect; MEMORY_BASIC_INFORMATION mbi; VirtualQuery(lpAddr,&mbi,sizeof(mbi)); bRetn = VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&dwOldProtect); if (bRetn) { if (pThunkPointer != NULL) { *pThunkPointer = lpAddr; } if (pOriginalFuncAddr != NULL) { *pOriginalFuncAddr = *lpAddr; } *lpAddr = (ULONG_PTR)DetourFunc; result = TRUE; VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOldProtect, 0); } break; } pThunkData++; } pImportDescriptor++; } FreeLibrary(hModule); return result; } void IAT_InstallHook() { BOOL bResult = FALSE; HMODULE currectExe = GetModuleHandle(NULL); PULONG_PTR pt; ULONG_PTR OrginalAddr; OldMessageBox = MessageBoxA; bResult = InstallHook(currectExe,"user32.dll","MessageBoxA",MyMessageBoxA,&pt,&OrginalAddr); } VOID IAT_UnInstallHook() { DWORD dwOLD; MEMORY_BASIC_INFORMATION mbi; if (g_PointerToIATThunk) { //查詢並修改內存頁的屬性 VirtualQuery((LPCVOID)g_PointerToIATThunk,&mbi,sizeof(mbi)); VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&dwOLD); //將原始的MessageBoxA地址填入IAT中 *g_PointerToIATThunk = (ULONG)OldMessageBox; //恢復內存頁的屬性 VirtualProtect(mbi.BaseAddress,mbi.RegionSize,dwOLD,0); } } int main() { MessageBoxA(NULL,"Before install","IAT",MB_OK); IAT_InstallHook(); MessageBoxA(NULL,"Correct!","IAT",MB_OK); IAT_UnInstallHook(); MessageBoxA(NULL,"After uninstall","IAT",MB_OK); return 0; }

 (二)Inline Hook

 

 

 

 原理:通過獲取目標函數的地址,修改開頭的指令為jmp指令,跳向我們事先定義好的目標函數Detour的地址,在完成我們想要的操作后調用函數Trampoline(該函數的作用是跳回原函數並執行原本修改掉的指令,完成原函數功能。)。

#include <Windows.h>
#include <iostream>

using namespace std;

ULONG64 g_JmpAddr;

int WINAPI OrignalMessageBoxA(
  _In_opt_  HWND hWnd,
  _In_opt_  LPCTSTR lpText,
  _In_opt_  LPCTSTR lpCaption,
  _In_      UINT uType
)
{
	//這里我省略了修改掉的指令,因為參數都保存在棧,這幾條指令是修改ebp的,若執行會導致參數丟失。
	__asm {
		nop
		nop
		nop
		jmp g_JmpAddr
	}
	return 0;
}

int WINAPI My_MessageBoxA(
  _In_opt_  HWND hWnd,
  _In_opt_  LPCTSTR lpText,
  _In_opt_  LPCTSTR lpCaption,
  _In_      UINT uType
)
{
	cout<<"hack success!"<<endl;
	lpText = "Hacker!";
	OrignalMessageBoxA(hWnd,lpText,lpCaption,uType);
	return 0;
}

int main()  
{
	PBYTE AddrMesssageBox =  (PBYTE)GetProcAddress(GetModuleHandle("user32.dll"),"MessageBoxA");
	g_JmpAddr = (ULONG)AddrMesssageBox+5;

	BYTE newEntry[5]={0};
	newEntry[0] = 0xe9;
	//這里的5是指jmp指令占的5個字節,求的是偏移,在當前位置向上或下偏移多少位
	*(ULONG*)(newEntry+1) = (ULONG)My_MessageBoxA - (ULONG)AddrMesssageBox -5;
	DWORD OldProtect;
	MEMORY_BASIC_INFORMATION MBI = {0};
	VirtualQuery((LPCVOID)AddrMesssageBox,&MBI,sizeof(MEMORY_BASIC_INFORMATION));
	VirtualProtect(MBI.BaseAddress,5,PAGE_EXECUTE_READWRITE,&OldProtect);
	memcpy(AddrMesssageBox,newEntry,5);
	VirtualProtect(MBI.BaseAddress,5,OldProtect,&OldProtect);

	MessageBoxA(0,"OK","Tip",MB_OK);

    return 0;  
}

 安裝hook的過程:構造好jmp指令后,調用memcpy函數復制到目標函數中,這里需要使用VirrtualProtect函數修改該地址區域的權限,必須具備可寫權限。

注:jmp指令和call指令占5個字節。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM