Hook技術:通過Dll注入Hook IAT讓計算器顯示中文數字!(附源碼)

本文來自於《逆向工程核心原理》學習實踐。

計算機的計算器是我們常用的一個小工具(小時候,總是計算機計算器分不開來,哈哈)。
衆所周知,計算器在計算的過程中,顯示的都是阿拉伯數字,因爲阿拉伯數字是一個全世界通用的表示方法,那麼如何讓計算器顯示中文數字"一、二、三…"呢?
這裏我們就可以使用Dll注入去hook關鍵API實現計算器顯示中文數字

什麼是Dll注入Hook IAT?

通過注入Dll文件來鉤取某個API,Dll文件注入目標進程後,修改IAT來更改進程中調用的特定的API的功能。

實現原理

實現原理,主要是IAT Hook

IAT中保存着程序中調用函數API的地址,所以我們只要將IAT中的地址改成自己函數的地址,就可以實現IAT Hook。

計算器正常執行,會調用SetWindowsTextW(),可以看到調用流程是從IAT中調用地址。
在這裏插入圖片描述
hook後:
這裏是將CALL DWORD PTR[01001110]中,01001110地址保存的API地址換成了我們自己函數的地址10001000。
在這裏插入圖片描述

實現流程

1.找到目標API
2.編寫DLL
2.1 獲得原目標API的地址並保存。
2.2 實現自己的函數,並保存地址。
2.3 找到IAT的地址,並找到API在其中的未知
2.4 使用VirtualProtect將內存修改爲可讀寫。
2.5 修改IAT中的目標API的地址,替換爲自己函數的地址
2.6 恢復元內存屬性。
3.注入dll
4.卸載dll

實戰演練

具體代碼實現參考源碼示例。
這裏傳參的方式,是通過命令行傳參。

int _tmain(int argc, TCHAR* argv[])
{
	if (argc != 4)
	{
		usage();
		return 1;
	}

	// adjust privilege  
	_EnableNTPrivilege(SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED);

	// InjectDll.exe <i|e> <PID> <dll_path>  
	if (!_tcsicmp(argv[1], L"i"))  //輸入 i 注入dll。輸入 e 卸載dll
	{
		InjectDll((DWORD)_tstoi(argv[2]), argv[3]);
	}
	else if (!_tcsicmp(argv[1], L"e"))
	{
		EjectDll((DWORD)_tstoi(argv[2]), argv[3]);
	}


	return 0;
}

dll注入
在這裏插入圖片描述
此時,計算器中已經顯示中文數字。
在這裏插入圖片描述
卸載dll
在這裏插入圖片描述
計算器顯示重新變回阿拉伯數字
在這裏插入圖片描述

優缺點

優點:
工作原理和具體實現都比較簡單
缺點:
如果想鉤取的API不在目標進程的IAT中,那麼就無法使用該技術進行Hook。(也就是,如果API是由程序動態加載DLL文件而調用的,就無法使用該項技術)

代碼示例

cpp文件

#include "stdio.h"  
#include "windows.h"  
#include "tlhelp32.h"  
#include "winbase.h"  
#include "tchar.h"  
#include<iostream>
using namespace std;

void usage()
{
	printf("\nInjectDll.exe by ReverseCore\n"
		"- USAGE : InjectDll.exe <i|e> <PID> <dll_path>\n\n");
}


BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName)
{
	HANDLE hProcess, hThread;
	LPVOID pRemoteBufferData;  //遠程進程中的內存
	DWORD dwBufSize = (DWORD)(_tcslen(szDllName) + 1) * sizeof(TCHAR);
	LPTHREAD_START_ROUTINE pThreadProc;

	if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
	{
		DWORD dwErr = GetLastError();
		return FALSE;
	}

	//申請內存 可讀可寫
	pRemoteBufferData = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);

	//像內存寫入dll路徑
	WriteProcessMemory(hProcess, pRemoteBufferData, (LPVOID)szDllName, dwBufSize, NULL);

	//獲得loadlibraryw地址
	pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
	
	//創建遠程線程
	hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBufferData, 0, NULL);
	WaitForSingleObject(hThread, INFINITE);

	CloseHandle(hThread);
	CloseHandle(hProcess);

	return TRUE;
}


BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName)
{
	BOOL bMore = FALSE, bFound = FALSE;
	HANDLE hSnapshot, hProcess, hThread;
	MODULEENTRY32 me = { sizeof(me) };
	LPTHREAD_START_ROUTINE pThreadProc;

	if (INVALID_HANDLE_VALUE == (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)))
	{
		return FALSE;
	}
		

	bMore = Module32First(hSnapshot, &me);
	for (; bMore; bMore = Module32Next(hSnapshot, &me))
	{
		if (!_tcsicmp(me.szModule, szDllName) || !_tcsicmp(me.szExePath, szDllName))
		{
			bFound = TRUE;
			break;
		}
	}

	if (!bFound)
	{
		CloseHandle(hSnapshot);
		return FALSE;
	}

	if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
	{
		CloseHandle(hSnapshot);
		return FALSE;
	}

	pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "FreeLibrary");
	hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL);
	WaitForSingleObject(hThread, INFINITE);

	CloseHandle(hThread);
	CloseHandle(hProcess);
	CloseHandle(hSnapshot);

	return TRUE;
}


DWORD _EnableNTPrivilege(LPCTSTR szPrivilege, DWORD dwState)
{
	DWORD dwRtn = 0;
	HANDLE hToken;
	if (OpenProcessToken(GetCurrentProcess(),
		TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
	{
		LUID luid;
		if (LookupPrivilegeValue(NULL, szPrivilege, &luid))
		{
			BYTE t1[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];
			BYTE t2[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];
			DWORD cbTP = sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES);

			PTOKEN_PRIVILEGES pTP = (PTOKEN_PRIVILEGES)t1;
			PTOKEN_PRIVILEGES pPrevTP = (PTOKEN_PRIVILEGES)t2;

			pTP->PrivilegeCount = 1;
			pTP->Privileges[0].Luid = luid;
			pTP->Privileges[0].Attributes = dwState;

			if (AdjustTokenPrivileges(hToken, FALSE, pTP, cbTP, pPrevTP, &cbTP))
				dwRtn = pPrevTP->Privileges[0].Attributes;
		}

		CloseHandle(hToken);
	}

	return dwRtn;
}


int _tmain(int argc, TCHAR* argv[])
{
	if (argc != 4)
	{
		usage();
		return 1;
	}

	// adjust privilege  
	_EnableNTPrivilege(SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED);

	// InjectDll.exe <i|e> <PID> <dll_path>  
	if (!_tcsicmp(argv[1], L"i"))
	{
		InjectDll((DWORD)_tstoi(argv[2]), argv[3]);
	}
	else if (!_tcsicmp(argv[1], L"e"))
	{
		EjectDll((DWORD)_tstoi(argv[2]), argv[3]);
	}


	return 0;
}

dll:

// include  
#include "stdio.h"  
#include "wchar.h"  
#include "windows.h"  


// typedef  
typedef BOOL(WINAPI *PFSETWINDOWTEXTW)(HWND hWnd, LPWSTR lpString); //SetWindowsTextW()的地址


// 原函數地址
FARPROC g_pOrginalFunction = NULL;


BOOL WINAPI MySetWindowTextW(HWND hWnd, LPWSTR lpString)
{
	wchar_t* pNum = L"零一二三四五六七八九";
	wchar_t temp[2] = { 0, };
	int i = 0, nLen = 0, nIndex = 0;

	nLen = wcslen(lpString);
	for (i = 0; i < nLen; i++)
	{
		//   將阿拉伯數字轉換爲中文數字  
		//   lpString是寬字符版本(2個字節)字符串  
		if (L'0' <= lpString[i] && lpString[i] <= L'9')
		{
			temp[0] = lpString[i];
			nIndex = _wtoi(temp);
			lpString[i] = pNum[nIndex];
		}
	}

	//   調用原函數;user32.SetWindowTextW  
	//   (修改lpString緩衝區中的內容)  
	return ((PFSETWINDOWTEXTW)g_pOrginalFunction)(hWnd, lpString);
}

/**********************************/
//    函數名:hook_iat
//	  功能  :負責實施IAT鉤取的核心函數
/**********************************/
BOOL hook_iat(LPCSTR szDllName, PROC pfnOrg, PROC pfnNew)
{
	HMODULE hMod;
	LPCSTR szLibName;
	PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
	PIMAGE_THUNK_DATA pThunk;
	DWORD dwOldProtect, dwRVA;
	PBYTE pAddr;

	// hMod, pAddr = ImageBase of calc.exe  
	//             = VA to MZ signature (IMAGE_DOS_HEADER)  
	hMod = GetModuleHandle(NULL);
	pAddr = (PBYTE)hMod;

	// pAddr = VA to PE signature (IMAGE_NT_HEADERS)  
	pAddr += *((DWORD*)&pAddr[0x3C]);

	// dwRVA = RVA to IMAGE_IMPORT_DESCRIPTOR Table  
	dwRVA = *((DWORD*)&pAddr[0x80]);

	// pImportDesc = VA to IMAGE_IMPORT_DESCRIPTOR Table  
	pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod + dwRVA);

	for (; pImportDesc->Name; pImportDesc++)
	{
		// szLibName = VA to IMAGE_IMPORT_DESCRIPTOR.Name  
		szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);
		if (!_stricmp(szLibName, szDllName))
		{
			// pThunk = IMAGE_IMPORT_DESCRIPTOR.FirstThunk  
			//        = VA to IAT(Import Address Table)  
			pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod +
				pImportDesc->FirstThunk);

			// pThunk->u1.Function = VA to API  匹配成功
			for (; pThunk->u1.Function; pThunk++)
			{
				if (pThunk->u1.Function == (DWORD)pfnOrg)
				{
					// 更改爲可讀寫模式  
					VirtualProtect((LPVOID)&pThunk->u1.Function,
						4,
						PAGE_EXECUTE_READWRITE,
						&dwOldProtect);

					// 修改IAT的值  
					pThunk->u1.Function = (DWORD)pfnNew;

					//修改完成後,恢復原保護屬性
					VirtualProtect((LPVOID)&pThunk->u1.Function,
						4,
						dwOldProtect,
						&dwOldProtect);

					return TRUE;
				}
			}
		}
	}

	return FALSE;
}



BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	switch (fdwReason)
	{
	case DLL_PROCESS_ATTACH:
		// 保存原始API的地址  
		g_pOrginalFunction = GetProcAddress(GetModuleHandle(L"user32.dll"),
			"SetWindowTextW");

		// # hook  
		//   用hookiat.MySetWindowText鉤取user32.SetWindowTextW  
		hook_iat("user32.dll", g_pOrginalFunction, (PROC)MySetWindowTextW);
		break;

	case DLL_PROCESS_DETACH:
		// # unhook  
		//   將calc.exe的IAT恢復原值  
		hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrginalFunction);
		break;
	}

	return TRUE;
}

後記:
本文寫的匆忙,只探討了32位的一種情況,也沒有進行詳盡的分析,之後會進行補充。

參考資料:
《逆向工程核心原理》
https://blog.csdn.net/liujiayu2/article/details/72956644

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章