我们通过一个示例来练习向计算机进程插入用户的DLL文件,钩取负责向计算器显示文本的SetWindowTextW,使得计算器中显示中文数字而不是原来的阿拉伯数字。钩取前后的原理图如下所示。
下面我们先测试一下代码。
运行calc.exe并查看PID。
在命令行窗口中输入命令与参数。
在计算器中任意输入一些数字,发现变成了中文数字。
我们来分析一下源代码,看看是怎么实现的。
InjectDll.cpp源代码与DLL注入的代码基本结构类似。
[cpp]
view plain
copy
- #include "stdio.h"
- #include "windows.h"
- #include "tlhelp32.h"
- #include "winbase.h"
- #include "tchar.h"
- void usage()
- {
- printf("\nInjectDll.exe by ReverseCore\n"
- "- blog : http://www.reversecore.com\n"
- "- email : reversecore@gmail.com\n\n"
- "- USAGE : InjectDll.exe <i|e> <PID> <dll_path>\n\n");
- }
- BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName)
- {
- HANDLE hProcess, hThread;
- LPVOID pRemoteBuf;
- 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;
- }
- pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);
- WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, dwBufSize, NULL);
- pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
- hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 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;
- }
DLLMain的代码非常简单。在DLL_PROCESS_ATTACH事件中先获取user32.SetWindowTextW的地址,然后保存到全局变量g_pOrgFunc中,后面脱钩会用到这个地址。
[cpp]
view plain
copy
- BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
- {
- switch( fdwReason )
- {
- case DLL_PROCESS_ATTACH :
- // 保存原始API的地址
- g_pOrgFunc = GetProcAddress(GetModuleHandle(L"user32.dll"),
- "SetWindowTextW");
- // # hook
- // 用hookiat.MySetWindowText钩取user32.SetWindowTextW
- hook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTextW);
- break;
- case DLL_PROCESS_DETACH :
- // # unhook
- // 将calc.exe的IAT恢复原值
- hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc);
- break;
- }
- return TRUE;
- }
[cpp]
view plain
copy
- 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_pOrgFunc)(hWnd, lpString);
- }
[cpp]
view plain
copy
- 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);
- pThunk->u1.Function = (DWORD)pfnNew;
- VirtualProtect((LPVOID)&pThunk->u1.Function,
- 4,
- dwOldProtect,
- &dwOldProtect);
- return TRUE;
- }
- }
- }
- }
- return FALSE;
- }
pImportDesc变量中存储着IMAGE_IMPORT_DESCRIPTOR结构体的起始地址,后者是calc.exe进程IDT的第一个结构体。IDT是由IMAGE_IMPORT_DESCRIPTOR结构体组成的数组。若想查找到IAT,先要查找到这个位置。使用PEView查看该地址(00012B80+01000000=01012B80),如图所示。
在for循环中通过比较查找到user32.dll的IMAGE_IMPORT_DESCRIPTOR结构体地址,从上图可以看出最终pImportDesc的值为01012BF4。接下来进入user32的IAT,pImportDesc->FirstThunk成员所指的就是IAT。使用PEView查看该地址(000010A4+01000000=010010A4),如图所示。
接下来又在for循环中查找SetWindowTextW的IAT地址(01001110),然后修改它的值。因为计算器进程的IAT内存区域是只读的,所以需要使用VirtualProtect在钩取之前将相应的区域改为可读写的,钩取之后再改回来。
完整的代码如下。
[cpp]
view plain
copy
- // include
- #include "stdio.h"
- #include "wchar.h"
- #include "windows.h"
- // typedef
- typedef BOOL (WINAPI *PFSETWINDOWTEXTW)(HWND hWnd, LPWSTR lpString);
- // globals
- FARPROC g_pOrgFunc = 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_pOrgFunc)(hWnd, lpString);
- }
- 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_pOrgFunc = GetProcAddress(GetModuleHandle(L"user32.dll"),
- "SetWindowTextW");
- // # hook
- // 用hookiat.MySetWindowText钩取user32.SetWindowTextW
- hook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTextW);
- break;
- case DLL_PROCESS_DETACH :
- // # unhook
- // 将calc.exe的IAT恢复原值
- hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc);
- break;
- }
- return TRUE;
- }