0x00 简介
这个程序的目的是为了让calc程序显示中文。比如: 我是在XP下使用计算器,上面显示的都是阿拉伯数字,现在要用中文显示,如图所示:
使用OD追踪Windows下的calc程序就会发现,计算器的输入框是一个EDIT空间并且calc程序是通过SetWindowTextW来把键入结果显示在EDIT控件上。
在SetWindowTextW上下断点后运行程序,在calc程序上输入便会发现在SetWindowTextW上断下。这就证明了这个事实。
0x01 原理
由于calc程序是图形界面程序所以USER32.dll必然被加载到calc的线性地址空间中,所以只要在calc中调用GetProcAddress获取其中的SetWindowTextW的地址并且将该API截获替换成我们自己的代码就可以了。采用的方法是IAT Hook。
但是由于保护模式下进程间是隔离的,没办法进入calc的地址空间内,所以需要用到远程线程注入的方式把包含Hook代码的DLL注入到目标进程并运行。
0x02 代码
首先是注入程序:
// 注入程序代码
#include <windows.h>
#include <tchar.h>
#include <TlHelp32.h>
#include <cstdio>
/*
获取DEBUG权限
*/
BOOL SetPrivileges(LPCTSTR pszPriName, BOOL fEnabled) {
HANDLE hToken = NULL;
TOKEN_PRIVILEGES tp = {0};
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
return(FALSE);
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = fEnabled ? SE_PRIVILEGE_ENABLED : 0;
if (!LookupPrivilegeValue(NULL, pszPriName, &(tp.Privileges[0].Luid)))
return(FALSE);
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL))
return(FALSE);
return(TRUE);
}
/*
寻找到特定进程的地址空间中的相应DLL模块
找到后就卸载
*/
PBYTE EnumModules(LPCTSTR pszModuleName, DWORD dwPID) {
HANDLE hSnapshot = INVALID_HANDLE_VALUE;
MODULEENTRY32 me = {sizeof(me)};
BOOL bFound = FALSE;
__try {
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
if (INVALID_HANDLE_VALUE == hSnapshot)
__leave;
if (!Module32First(hSnapshot, &me))
__leave;
do {
if (!_tcscmp(pszModuleName, me.szModule) ||
!_tcscmp(pszModuleName, me.szExePath)) {
bFound = TRUE;
__leave;
}
} while (Module32Next(hSnapshot, &me));
}
__finally {
if (INVALID_HANDLE_VALUE != hSnapshot)
CloseHandle(hSnapshot);
if (bFound)
return(me.modBaseAddr);
}
return(NULL);
}
/*
将指定进程的地址空间中指定模块进行释放
*/
BOOL EjectDll(LPCTSTR pszModuleName, DWORD dwPID) {
HANDLE hProcess = NULL;
LPVOID lpMem = NULL;
DWORD dwSize = 0, dwWrite = 0;
HMODULE hMod = NULL;
HANDLE hThread = NULL;
LPTHREAD_START_ROUTINE pThreadProc = NULL;
PBYTE pAddr = NULL;
if (!(pAddr = EnumModules(pszModuleName, dwPID))) {
_tprintf("模块可能已经被卸载,不存在.\r\n");
return(TRUE);
}
if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
return(FALSE);
dwSize = _tcslen(pszModuleName) + 1;
lpMem = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (NULL == lpMem)
return(FALSE);
if (!WriteProcessMemory(hProcess, lpMem, pszModuleName, dwSize, &dwWrite))
return(FALSE);
if (!(hMod = GetModuleHandle("kernel32.dll")))
return(FALSE);
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "FreeLibrary");
hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, (LPVOID)pAddr, 0, NULL);
if (NULL == hThread)
return(FALSE);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hProcess);
CloseHandle(hThread);
return(TRUE);
}
/*
遍历进程,寻找对应名称的PID
*/
DWORD EnumProcesses(LPCTSTR pszProcessName) {
HANDLE hSnapshot = INVALID_HANDLE_VALUE;
PROCESSENTRY32 pe = {sizeof(pe)};
BOOL fFind = FALSE;
__try {
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot)
__leave;
if (!Process32First(hSnapshot, &pe))
__leave;
do {
if (!_tcscmp(pszProcessName, pe.szExeFile)) {
fFind = TRUE;
__leave;
}
} while (Process32Next(hSnapshot, &pe));
}
__finally {
if (hSnapshot != INVALID_HANDLE_VALUE)
CloseHandle(hSnapshot);
if (fFind)
return(pe.th32ProcessID);
}
return(-1);
}
/*
pszDllName: 注入的DLL名称(要用绝对路径)
pszProcessName: 进程名称, 是被注入目标
*/
BOOL InjectDll(LPCTSTR pszProcessName, LPCTSTR pszDllName) {
DWORD dwPID = 0;
HANDLE hProcess = NULL, hThread = NULL;
LPVOID lpMem = NULL;
DWORD dwSize = 0, dwWritten = 0;
LPTHREAD_START_ROUTINE lpProcThread = NULL;
HMODULE hMod = NULL;
BOOL fOk = FALSE;
if (NULL == pszProcessName || NULL == pszDllName)
return(FALSE);
if (-1 == (dwPID = EnumProcesses(pszProcessName)))
return(FALSE);
if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
return(FALSE);
dwSize = _tcslen(pszDllName) + 1;
__try {
lpMem = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (NULL == lpMem)
__leave;
if (!WriteProcessMemory(hProcess, lpMem, pszDllName, dwSize, &dwWritten))
__leave;
hMod = LoadLibrary("kernel32.dll");
if (NULL == hMod)
__leave;
lpProcThread = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryA");
if (NULL == lpProcThread)
__leave;
hThread = CreateRemoteThread(hProcess, NULL, 0, lpProcThread, (LPVOID)lpMem, 0, NULL);
if (NULL == hThread)
__leave;
WaitForSingleObject(hThread, INFINITE);
fOk = TRUE;
}
__finally {
if (hProcess)
CloseHandle(hProcess);
if (hThread)
CloseHandle(hThread);
if (fOk)
return(TRUE);
}
return(FALSE);
}
#define DLLPATH ("C:\\Documents and Settings\\Administrator\\My Documents\\Visual Studio 2010\\Projects\\IATHook\\Debug\\calcHook.dll")
#define EXENAME ("calc.exe")
int _tmain() {
InjectDll(EXENAME, DLLPATH);
system("pause");
DWORD dwPID = EnumProcesses(EXENAME);
if (EjectDll(DLLPATH, dwPID))
_tprintf("Yes\r\n");
else
_tprintf("No\r\n");
system("pause");
return(0);
}
接着是DLL代码:
#include <windows.h>
#include <tchar.h>
typedef BOOL (WINAPI *PFSETWINDOWTEXTW)(HWND hWnd, LPWSTR lpString);
FARPROC g_pOrgFunc = NULL;
// 新的SetWindowTextW
BOOL WINAPI MySetWindowTextW(HWND hWnd, LPWSTR lpString) {
WCHAR *pNum = L"零一二三四五六七八九";
int i = 0, nLen = 0, nIndex = 0;
nLen = wcslen(lpString);
for (i = 0; i < nLen; ++i)
for (nIndex = 0; nIndex < 10; ++nIndex)
if (lpString[i] == (nIndex + L'0'))
lpString[i] = pNum[nIndex];
return(((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString));
}
// IATHOOK代码
BOOL IAT_Hook(LPCTSTR szDllName, FARPROC pfnOrg, FARPROC pfnNew){
HMODULE hMod = NULL;
LPCTSTR szLibName = NULL;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = NULL;
PIMAGE_THUNK_DATA pThunk = NULL;
DWORD dwOldProtect, dwRVA;
PBYTE pAddr = NULL;
hMod = GetModuleHandle(NULL);
pAddr = (PBYTE)hMod;
pAddr += *((DWORD *)&pAddr[0x3C]);
dwRVA = *((DWORD *)&pAddr[0x80]);
pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((PBYTE)hMod + dwRVA);
for (; pImportDesc->Name; ++pImportDesc) {
szLibName = (LPCTSTR)((PBYTE)hMod + pImportDesc->Name);
if (!_tcscmp(szLibName, szDllName)) {
pThunk = (PIMAGE_THUNK_DATA)((PBYTE)hMod + pImportDesc->FirstThunk);
for (; pThunk->u1.Function; ++pThunk)
if ((DWORD)pfnOrg == pThunk->u1.Function) {
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);
}
BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID lpReserved) {
switch(fdwReason) {
case DLL_PROCESS_ATTACH:
g_pOrgFunc = GetProcAddress(GetModuleHandle("USER32.dll"), "SetWindowTextW");
IAT_Hook("USER32.dll", g_pOrgFunc, (FARPROC)MySetWindowTextW);
break;
case DLL_PROCESS_DETACH:
break;
}
return(TRUE);
}
注意要把在注入程序中要把DLL写成绝对路径。
(完)