原理
代码
导出地址表
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;
}