本文來自於《逆向工程核心原理》學習實踐。
計算機的計算器是我們常用的一個小工具(小時候,總是計算機計算器分不開來,哈哈)。
衆所周知,計算器在計算的過程中,顯示的都是阿拉伯數字,因爲阿拉伯數字是一個全世界通用的表示方法,那麼如何讓計算器顯示中文數字"一、二、三…"呢?
這裏我們就可以使用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