11 拦截API

原理

代码

导出地址表

Import Address Table 由于导入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于一个或者多个DLL 中.当PE 文件被装入内存的时候,Windows 装载器才将DLL 装入,并将调用导入函数的指令和函数实际所处的地址联系起来(动态连接),这操作就需要导入表完成.其中导入地址表就指示函数实际地址。

 

testdll工程

target.h

#pragma once

extern "C"
{
	__declspec(dllexport) void __stdcall testme();
	__declspec(dllexport) int __stdcall testadd(int a, int b);
	__declspec(dllexport) void __stdcall testhello();
}

target.cpp 

#include "target.h"
#include <stdio.h>

void __stdcall testme()
{
	printf("\ntestme\n");
}

int __stdcall testadd(int a, int b)
{
	return a + b;
}

void __stdcall testhello()
{
	printf("\ntesthello\n");
}

 

demo

使用了

#include <stdio.h>
#include <Windows.h>
#include "target.h"

#pragma comment(lib, "testdll20180620.lib")

int main(void)
{
	testme();
	testadd(3, 5);
	testhello();
  /*
	HMODULE hModule = LoadLibrary(L"hookapi20180620.dll");
	FARPROC pfn = GetProcAddress(hModule, "hookapitest");
	int ret = pfn();
	if (ret == 0)
	{
		testme();
	}
	printf("ret = %d\n", ret);
	FreeLibrary(hModule);
	getchar();*/
	return 0;
}

生成,用dumpbin /imports 可执行文件查看IAT

使用dumpbin /all  可执行文件.exe  > 1.txt 在文件查看

    Section contains the following imports:

    testdll20180620.dll
                41A0E0 Import Address Table
                41A330 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                    0 testadd
                    1 testhello
                    2 testme


                            入口点      函数实现

     777  HIGHLOW            0041A0E8  __imp__testme@0
     77D  HIGHLOW            0041A0E0  __imp__testadd@8
     783  HIGHLOW            0041A0E4  __imp__testhello@0



核心代码

    //获取指定模块hmodCaller导入地址表(IAT)的首导入模块
		pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToDataEx(
			hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize, NULL);

每一个dll都有一个IAT,上面的函数可以拿到一个镜像,里面是进程中所有模块的IAT

	for (; pImportDesc->Name; ++pImportDesc)
	{
		// 得到模块名pszModName(模块基地址 + 名称偏移)
		PSTR pszModName = (PSTR)((PBYTE)hmodCaller + pImportDesc->Name);

		printf("\n%s\n", pszModName);

		// 判断模块名是否为指定模块dll=>找到testdll.dll
		if (lstrcmpiA(pszModName, pszCalleeModName) == 0)
		{
			// 取得调用者导入函数地址表在模块中的函数地址(模块基址 + 偏移)
			PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
				((PBYTE)hmodCaller + pImportDesc->FirstThunk);

			// 用新的函数地址替换当前函数地址 =>找到testme
			for (; pThunk->u1.Function; pThunk++)
			{

				// 取得函数地址
				PROC* ppfn = (PROC*)&pThunk->u1.Function;

				// 是否是需要替换的函数(比较函数地址)
				BOOL bFound = (*ppfn == pfnCurrent);
				if (bFound)
				{
					// 修改地址  mytestme() =》 testme
					if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
						sizeof(pfnNew), NULL) && (ERROR_NOACCESS == GetLastError()))
					{
						// 如果失败,则更改页保护属性
						DWORD dwOldProtect;
						if (VirtualProtect(ppfn, sizeof(pfnNew), PAGE_WRITECOPY,
							&dwOldProtect))
						{

							WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
								sizeof(pfnNew), NULL);
							VirtualProtect(ppfn, sizeof(pfnNew), dwOldProtect,
								&dwOldProtect);
							return 0;
						}
						
					}
					else
					{
						return 0;
					}
					
				}
			}
			
		}
		
	}

以上代码便是找到testdll.dll中的testme并且替换为mytestme

 

正常执行的dll

target.h

#pragma once

extern "C"
{
	__declspec(dllexport) void __stdcall testme();
	__declspec(dllexport) int __stdcall testadd(int a, int b);
	__declspec(dllexport) void __stdcall testhello();
}

 

target.cpp

#include "target.h"
#include <stdio.h>

void __stdcall testme()
{
	printf("\ntestme\n");
}

int __stdcall testadd(int a, int b)
{
	return a + b;
}

void __stdcall testhello()
{
	printf("\ntesthello\n");
}

生成:

 

应用程序:

#include <stdio.h>
#include <Windows.h>
#include "target.h"

#pragma comment(lib, "testdll20180620.lib")

int main(void)
{
	testme();
	testadd(3, 5);
	testhello();

	HMODULE hModule = LoadLibrary(L"hookapi20180620.dll");
	FARPROC pfn = GetProcAddress(hModule, "hookapitest");
	int ret = pfn();
	if (ret == 0)
	{
		testme();
	}
	printf("ret = %d\n", ret);
	FreeLibrary(hModule);
	getchar();
	return 0;
}

拦截dll:

#pragma once

__declspec(dllexport)int __stdcall hookapitest();
#include <Windows.h>
#include <stdio.h>
#include <ImageHlp.h>
//#include "target.h"
#pragma comment(lib, "ImageHlp")

//#pragma comment(lib, "testdll20180620.lib")

LONG WINAPI InvalidReadExceptionFilter(PEXCEPTION_POINTERS pep);

//如在
int ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
	PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller);

int mytestme()
{
	printf("\nmytestme\n");
	return 0;
}

int __stdcall hookapitest()
{
	//testme();
	PROC pfnNew = (PROC)mytestme;

	//HMODULE hModule1 = LoadLibrary(L"testdll20180620.dll");
	//HMODULE hModule2 = LoadLibrary(L"testiat.exe");
	PROC pfnOrig = GetProcAddress(GetModuleHandle(L"testdll20180620"), "testme");
	//PROC pfnOrig = GetProcAddress(hModule1, "testme");
	//PROC pfnOrig = GetProcAddress(GetModuleHandle(L"Kernel32"), "ExitProcess");
	//HMODULE hmodCaller = LoadLibrary(L"testiat.exe");
	HMODULE hmodCaller = GetModuleHandle(L"testiat.exe");//当前dll和exe在同一个地址空间中,属于同一个进程,因此可以获取
	
	return ReplaceIATEntryInOneMod(
		"testdll20180620.dll",
		pfnOrig,
		pfnNew,
		hmodCaller
	);
}


// 异常过滤函数
LONG WINAPI InvalidReadExceptionFilter(PEXCEPTION_POINTERS pep)
{
	LONG lDisposition = EXCEPTION_EXECUTE_HANDLER;
	return lDisposition;
}
/*
PCSTR pszCalleeModName   模块名
PROC pfnCurrent    当前函数名
PROC pfnNew     新函数名
HMODULE hmodCaller    可执行文件名

用pfnNew 去代替 hmodCaller中的pszCalleeModName模块中的fnCurrent函数名
*/
int ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
	PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller)
{
	ULONG ulSize;

	// 一个异常可能被触发,因为诸如explorer.exe这样的进程能够快速地加载和卸载模块
	PIMAGE_IMPORT_DESCRIPTOR pImportDesc = NULL;
	__try
	{
    //获取指定模块hmodCaller导入地址表(IAT)的首导入模块
		pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToDataEx(
			hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize, NULL);
	}
	__except (InvalidReadExceptionFilter(GetExceptionInformation()))
	{
		printf("exception");
		return 1;
	}

	if (pImportDesc == NULL)
		return 2;

	// 在导入描述符中查找导入信息
	for (; pImportDesc->Name; ++pImportDesc)
	{
		// 得到模块名pszModName(模块基地址 + 名称偏移)
		PSTR pszModName = (PSTR)((PBYTE)hmodCaller + pImportDesc->Name);

		printf("\n%s\n", pszModName);

		// 判断模块名是否为指定模块dll
		if (lstrcmpiA(pszModName, pszCalleeModName) == 0)
		{
			// 取得调用者导入函数地址表在模块中的函数地址(模块基址 + 偏移)
			PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
				((PBYTE)hmodCaller + pImportDesc->FirstThunk);

			// 用新的函数地址替换当前函数地址
			for (; pThunk->u1.Function; pThunk++)
			{

				// 取得函数地址
				PROC* ppfn = (PROC*)&pThunk->u1.Function;
        printf("%x- now:%x\n", *ppfn, pfnCurrent);
				// 是否是需要替换的函数(比较函数地址)
				BOOL bFound = (*ppfn == pfnCurrent);
				if (bFound)
				{
					// 修改地址  mytestme() =》 testme
					if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
						sizeof(pfnNew), NULL) && (ERROR_NOACCESS == GetLastError()))
					{
						// 如果失败,则更改页保护属性
						DWORD dwOldProtect;
						if (VirtualProtect(ppfn, sizeof(pfnNew), PAGE_WRITECOPY,
							&dwOldProtect))
						{

							WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
								sizeof(pfnNew), NULL);
							VirtualProtect(ppfn, sizeof(pfnNew), dwOldProtect,
								&dwOldProtect);
							return 0;
						}
						
					}
					else
					{
						return 0;
					}
					
				}
			}
			
		}
		
	}

	//在for循环中始终没有进入到return 0的分支,就意味着没有找到对应的模块
	printf("\ncann't find target module\n");
	return 1;
}

 

远程注入+拦截api

正常情况下:

=》

当使用dllInject远程注入dll后:

=》首先弹出:=》=》

 

代码:

myapp:

核心:

1.加载dll_a.dll 
#pragma comment(lib, "dll_a.lib")

2
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // Parse the menu selections:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
				OutputMsg();
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
执行IDM_EXIT后,调用dll_a中的OutputMsg弹窗

dll_a

#pragma once

__declspec(dllexport) int __stdcall OutputMsg();
// dll_a.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"


int __stdcall OutputMsg()
{
	::MessageBox(NULL, L"DLL_A", L"DLL_A", MB_OK);
	return 0;
}

远程注入:

#include <Windows.h>
#include <stdio.h>
#include <TlHelp32.h>

DWORD FindTarget(LPCTSTR lpszProcess)
{
	DWORD dwRet = 0;
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	PROCESSENTRY32 pe32;
	pe32.dwSize = sizeof(PROCESSENTRY32);
	Process32First(hSnapshot, &pe32);
	do
	{
		if (lstrcmpi(pe32.szExeFile, lpszProcess) == 0)
		{
			dwRet = pe32.th32ProcessID;
			break;
		}
	} while (Process32Next(hSnapshot, &pe32));
	CloseHandle(hSnapshot);
	return dwRet;
}

int main(void)
{
	DWORD dwProcessID = 0;
	printf("请输入目标进程的ID:");
	scanf_s("%d", &dwProcessID);

	//PROCESS_CREATE_THREAD表示我可以通过返回的进程句柄在该进程中创建新的线程,也就是调用CreateRemoteThread的权限;
	//PROCESS_VM_OPERATION则表示在该进程中分配/释放内存的权限,也就是调用VirtualAllocEx/VirtualFreeEx的权限;
	HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, dwProcessID);

	// 向目标进程地址空间写入DLL名称
	DWORD dwSize, dwWritten;
	char lpszDll[] = "dll_b.dll";
	dwSize = lstrlenA(lpszDll) + 1;
	LPVOID lpBuf = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
	if (NULL == lpBuf)
	{
		CloseHandle(hProcess);
		// 失败处理
	}
	if (WriteProcessMemory(hProcess, lpBuf, (LPVOID)lpszDll, dwSize, &dwWritten))
	{
		// 要写入字节数与实际写入字节数不相等,仍属失败
		if (dwWritten != dwSize)
		{
			VirtualFreeEx(hProcess, lpBuf, dwSize, MEM_DECOMMIT);
			CloseHandle(hProcess);
			// 失败处理
		}
	}
	else
	{
		CloseHandle(hProcess);
		// 失败处理
	}
	// 使目标进程调用LoadLibrary,加载DLL
	DWORD dwID;
	LPVOID pFunc = LoadLibraryA;
	HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
		(LPTHREAD_START_ROUTINE)pFunc, lpBuf, 0, &dwID);

	// 等待LoadLibrary加载完毕
	WaitForSingleObject(hThread, INFINITE);
	// 释放目标进程中申请的空间
	VirtualFreeEx(hProcess, lpBuf, dwSize, MEM_DECOMMIT);
	CloseHandle(hThread);
	CloseHandle(hProcess);
	printf("dll_b注入完毕!\n");
	getchar();
	return 0;
}

dll_b:

#pragma once

#include "stdafx.h"

__declspec(dllexport) int __stdcall OutputMsg_B();
// dll_b.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include "dll_b.h"

int __stdcall OutputMsg_B()
{
	::MessageBox(NULL, L"DLL_B", L"DLL_B", MB_OK);
	return 0;
}
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <stdio.h>
#include "dll_b.h"
#include <ImageHlp.h>
#pragma comment(lib, "ImageHlp")

LONG WINAPI InvalidReadExceptionFilter(PEXCEPTION_POINTERS pep);
int ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
	PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller);

int hookapitest()
{
	OutputDebugString(L"hookapitest 1");
	PROC pfnNew = (PROC)OutputMsg_B;  //替换源,用OutputMsg_B替换掉进程空间中已经存在的函数
	OutputDebugString(L"hookapitest 2");
	PROC pfnOrig = GetProcAddress(GetModuleHandle(L"dll_a"), "OutputMsg"); //替换目标OutputMsg
	if (NULL == pfnOrig)
	{
		OutputDebugString(L"hookapitest 3");
		printf("get dll_a OutputMsg failed");
		getchar();
		return 1;
	}
	OutputDebugString(L"hookapitest 3");
	HMODULE hmodCaller = GetModuleHandle(L"MyAPP.exe");
	if (NULL == hmodCaller)
	{
		OutputDebugString(L"hookapitest 4");
		printf("get dll_a GetModuleHandle MyAPP.exe failed");
		getchar();
		return 1;
	}
	OutputDebugString(L"hookapitest 5");
	return ReplaceIATEntryInOneMod(
		"dll_a.dll",
		pfnOrig,
		pfnNew,
		hmodCaller
	);
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		::MessageBox(NULL, L"DLL_PROCESS_ATTACH DLL_B", L"DLL_PROCESS_ATTACH DLL_B", MB_OK);
		hookapitest();
		break;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
		break;
	case DLL_PROCESS_DETACH:
		::MessageBox(NULL, L"DLL_PROCESS_DETACH DLL_B", L"DLL_PROCESS_DETACH DLL_B", MB_OK);
		break;
	}
	return TRUE;
}

// 异常过滤函数
LONG WINAPI InvalidReadExceptionFilter(PEXCEPTION_POINTERS pep)
{
	LONG lDisposition = EXCEPTION_EXECUTE_HANDLER;
	return lDisposition;
}

int ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
	PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller)
{
	ULONG ulSize;

	// 一个异常可能被触发,因为诸如explorer.exe这样的进程能够快速地加载和卸载模块
	PIMAGE_IMPORT_DESCRIPTOR pImportDesc = NULL;
	__try
	{
		pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToDataEx(
			hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize, NULL);
	}
	__except (InvalidReadExceptionFilter(GetExceptionInformation()))
	{
		printf("exception");
		return 1;
	}

	if (pImportDesc == NULL)
		return 2;

	// 在导入描述符中查找导入信息
	for (; pImportDesc->Name; ++pImportDesc)
	{
		// 得到模块名pszModName(模块基地址 + 名称偏移)
		PSTR pszModName = (PSTR)((PBYTE)hmodCaller + pImportDesc->Name);

		printf("\n%s\n", pszModName);

		// 判断模块名是否为指定模块
		if (lstrcmpiA(pszModName, pszCalleeModName) == 0)
		{
			// 取得调用者导入函数地址表在模块中的函数地址(模块基址 + 偏移)
			PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
				((PBYTE)hmodCaller + pImportDesc->FirstThunk);

			// 用新的函数地址替换当前函数地址
			for (; pThunk->u1.Function; pThunk++)
			{

				// 取得函数地址
				PROC* ppfn = (PROC*)&pThunk->u1.Function;

				// 是否是需要替换的函数(比较函数地址)
				BOOL bFound = (*ppfn == pfnCurrent);
				if (bFound)
				{
					// 修改地址
					if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
						sizeof(pfnNew), NULL) && (ERROR_NOACCESS == GetLastError()))
					{
						// 如果失败,则更改页保护属性
						DWORD dwOldProtect;
						if (VirtualProtect(ppfn, sizeof(pfnNew), PAGE_WRITECOPY,
							&dwOldProtect))
						{

							WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
								sizeof(pfnNew), NULL);
							VirtualProtect(ppfn, sizeof(pfnNew), dwOldProtect,
								&dwOldProtect);
							return 0;
						}

					}
					else
					{
						return 0;
					}

				}
			}

		}

	}

	//在for循环中始终没有进入到return 0的分支,就意味着没有找到对应的模块
	printf("\ncann't find target module\n");
	return 1;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值