您的位置:首页 > 其它

逆向工程核心原理读书笔记-API钩取之计算器显示中文数字

2017-06-09 13:31 471 查看
我们通过一个示例来练习向计算机进程插入用户的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;

}

下面详细讲解hookiat.dll的源代码(hookiat.cpp)。

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;

}

下面看看MySetWindowTextW函数。lpString参数是一块缓冲区,该缓冲区用来存放要输出显示的字符串。for循环将存放在lpString的阿拉伯数字字符串转换为中文数字字符串。for循环结束后,最后再调用函数指针g_pOrgFunc。

[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);

}

接下来分析hook_iat函数,它负责具体钩取API。

[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;

}

首先从ImageBase开始经由PE签名找到IDT。







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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: