使用PE文件格式 HOOK 其他进程API

今天研究了一下HOOK 其他进程API的方法

有2个前提

1. 使用远程线程注入, 具体可以参考《Windows核心编程》中22-InjLib里面的代码

2. 更改被注入线程所要调用的API地址


2.1 方法一:实用一个网上一个大牛写的类CHookInfo

HookInfo.h

typedef struct _HOOKSTRUCT
{
	FARPROC pfFunAddr; //用于保存API函数地址
	BYTE OldCode[5]; //保存原API前5个字节
	BYTE NewCode[5]; //JMP XXXX其中XXXXJMP的地址
}HOOKSTRUCT;

class CHookInfo
{
public:
	CHookInfo(TCHAR *strDllName, char *strFunName, DWORD dwMyFunAddr); //HOOK 钩子的构造处理函数
	//strDllName用于传入模块文件名
	//strFunName用于传入模块里的函数名
	//dwMyFunAddr用于传入处理函数的地址(代理函数的地址)
	virtual ~CHookInfo(); //HOOK 钩子的析构函数
	HOOKSTRUCT *pHook; //HOOK结构
	void HookStatus(BOOL blnHook); //关闭/打开HOOK状态
};

CHookInfo::CHookInfo(TCHAR *strDllName, char *strFunName, DWORD dwMyFunAddr)
{
	pHook = new HOOKSTRUCT;
	HMODULE hModule = LoadLibrary(strDllName); //载入strDllName模块
	//纪录函数地址
	pHook-> pfFunAddr = GetProcAddress(hModule,strFunName); //在strDllName模块里检索strFunName函数的地址,GetProcAddress就是返回strFunName函数的地址
	FreeLibrary(hModule); //卸载strDllName模块
	if(pHook-> pfFunAddr == NULL) { return ; }

	//备份原函数的前5个字节,一般的WIN32 API以__stdcall声明的API理论上都可以这样进行HOOK (师傅的注释)

	memcpy(pHook->OldCode, pHook->pfFunAddr, 5);
	//memcpy:功能:由pHook-> pfFunAddr所指内存区域复制5个字节到pHook-> OldCode所指内存区域。说明:pHook-> pfFunAddr和pHook-> OldCode所指内存区域不能重叠,函数返回指向pHook-> OldCode的指针。
	pHook->NewCode[0] = 0xe9; //构造JMP ,JMP(字节值:0xe9)是汇编指令:程序无条件转移指令
	DWORD dwJmpAddr = dwMyFunAddr - (DWORD)pHook->pfFunAddr - 5; //计算JMP地址(我想这里是技术点!)
	memcpy(&pHook->NewCode[1], &dwJmpAddr, 4);
	HookStatus(TRUE);//开始进行HOOK
}

CHookInfo::~CHookInfo()
{
	HookStatus(FALSE);//关闭HOOK恢复原函数
}

void CHookInfo::HookStatus(BOOL blnHook)
{
	if(blnHook) {
		WriteProcessMemory((HANDLE)-1, pHook-> pfFunAddr, pHook-> NewCode, 5, 0);//替换函数地址 : (HANDLE)-1进程的句柄,pHook-> pfFunAddr进程地址,pHook-> NewCode数据存放地址,5数据的长度,0实际数据的长度
	} else {
		WriteProcessMemory((HANDLE)-1, pHook-> pfFunAddr, pHook-> OldCode, 5, 0);//还原函数地址 
	}
}

2.2 使用PE格式修改函数地址

//#pragma comment(lib,"th32.lib")
#include "..\CommonFiles\CmnHdr.h"     /* See Appendix A. */
#include <tchar.h>
#include "trace.h"
#include "HookInfo.h"

#include <iostream>
using namespace std;

#include "windows.h"
#include "process.h"
#include "tlhelp32.h"
#include "stdio.h"
//#pragma comment(lib, "th32.lib")


PIMAGE_DOS_HEADER			pDosHeader;
PIMAGE_NT_HEADERS			pNTHeaders;
PIMAGE_OPTIONAL_HEADER		pOptHeader;
PIMAGE_IMPORT_DESCRIPTOR	pImportDescriptor;
PIMAGE_THUNK_DATA			pThunkData;
PIMAGE_IMPORT_BY_NAME		pImportByName;
HMODULE hMod;


// 定义MessageBoxA函数原型
typedef int (WINAPI *PFNMESSAGEBOX)(HWND, LPCSTR, LPCSTR, UINT uType);
int WINAPI MessageBoxProxy(IN HWND hWnd, IN LPCSTR lpText, IN LPCSTR lpCaption, IN UINT uType);

int * addr = (int *)MessageBoxW;        //保存函数的入口地址
int * myaddr = (int *)MessageBoxProxy;


void ThreadProc(void *param);//线程函数
int WINAPI MyMessageBox(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
{
	wcout<<L"hWnd:"<<(int)hWnd<<endl;
	wcout<<L"lpText:"<<lpText<<endl;
	wcout<<L"lpCaption:"<<lpCaption<<endl;
	wcout<<L"uType:"<<uType<<endl<<endl;

	_TRACE(_T("嘿嘿! 调用了我的MessageBox了\n"));

	return 0;
}

void TestTP();
//new messagebox function
int WINAPI MessageBoxProxy(IN HWND hWnd, IN LPCSTR lpText, IN LPCSTR lpCaption, IN UINT uType)
{
	//return         ((PFNMESSAGEBOX)addr)(NULL, "gxter_test", "gxter_title", 0);
	//这个地方可以写出对这个API函数的处理代码

	_TRACE(_T("哈哈!使用我的Dll了\n"));
	return 1;
}

//-------------------------------------------------------主函数开始

BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
	if (fdwReason == DLL_PROCESS_ATTACH) {
		//_TRACE(_T("00\n"));
		//_beginthread(ThreadProc,0,NULL);
		//_TRACE(_T("11\n"));
		//CHookInfo hookInfo(_T("User32.Dll"), "MessageBoxW", (DWORD)MyMessageBox);

		TestTP();

		while (1) {}
		_TRACE(_T("22\n"));
	}

	return TRUE; 
}


//结束进程的函数

void TestTP()
{
	//------------hook api----------------
	//_TRACE(_T("TR 00\n"));
	hMod = GetModuleHandle(NULL);
	//_TRACE(_T("TR 11\n"));
	pDosHeader = (PIMAGE_DOS_HEADER)hMod;
	pNTHeaders = (PIMAGE_NT_HEADERS)((BYTE *)hMod + pDosHeader->e_lfanew);
	pOptHeader = (PIMAGE_OPTIONAL_HEADER)&(pNTHeaders->OptionalHeader);

	pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE *)hMod + pOptHeader->DataDirectory[1].VirtualAddress);
	//_TRACE(_T("TR 22\n"));
	while (pImportDescriptor->FirstThunk) {
		_TRACE(_T("TR 33\n"));
		char * dllname = (char *)((BYTE *)hMod + pImportDescriptor->Name);
		TCHAR wszDomain[256];
		MultiByteToWideChar(CP_ACP, 0, dllname,
			strlen(dllname)+1, wszDomain, sizeof(wszDomain)/sizeof(wszDomain[0]));
		//_TRACE(_T("%s\n"), wszDomain);

		pThunkData = (PIMAGE_THUNK_DATA)((BYTE *)hMod + pImportDescriptor->OriginalFirstThunk);

		int no = 1;
		while (pThunkData->u1.Function) {
			char * funname = (char *)((BYTE *)hMod + (DWORD)pThunkData->u1.AddressOfData + 2);

			PDWORD lpAddr = (DWORD *)((BYTE *)hMod + (DWORD)pImportDescriptor->FirstThunk) + (no-1);
			
			// 测试代码(HH)
			TCHAR szUser32[] = _T("USER32.dll");
			if (0 == memcmp(szUser32, wszDomain, sizeof(TCHAR)*2)) {
				_TRACE(_T("*lpAdd:%X PK addr:%X\n"), *lpAddr, addr);
			}
			//修改内存的部分
			if ((*lpAddr) == (int)addr) {
				_TRACE(_T("MessageBoxW addr is : %X\n"), *lpAddr);
				//修改内存页的属性
				DWORD dwOLD;
				MEMORY_BASIC_INFORMATION  mbi;
				VirtualQuery(lpAddr,&mbi,sizeof(mbi));
				VirtualProtect(lpAddr,sizeof(DWORD),PAGE_READWRITE,&dwOLD);

				WriteProcessMemory(GetCurrentProcess(), 
					lpAddr, &myaddr, sizeof(DWORD), NULL);
				//恢复内存页的属性
				//VirtualProtect(lpAddr,sizeof(DWORD),dwOLD,0);
			}
			//---------
			no ++;
			pThunkData++;
		}
		_TRACE(_T("%s有%d个函数\n"), wszDomain, no);

		pImportDescriptor++;
	}
	_TRACE(_T("TR End\n"));
	//-------------------HOOK END-----------------
}

这里要注意部分就是红色部分,要反复调试明白PE格式实际定义。


还学到了一个知识就是,函数地址和函数参数是放在不同的位置的。

参数是放在独立的栈中的。而函数地址是放在另一个地方的,暂时学习就这么多。

以后有又明白的部分再添加。

你可能感兴趣的:(api,import,byte,hook,Descriptor,winapi)