Windows核心编程_HOOk SOCKET实现封包拦截
Socket的HOOK技术是目前网络拦截程序的基础功能,还有浏览器的抓包工具都是通过拦截Socket函数实现的
浏览器也好,通讯软件也好,他们只是使用的通讯协议不一样,其最底层的全部都是通过封装Socket里的TCP/UDP实现的
如最常用的就是Send函数与Recv函数,一个是发送一个是接收,所以我们只需要通过Hook住Send和Recv函数就可以实现抓包功能
https://blog.csdn.net/bjbz_cxy/article/details/90574824 之前写的这篇文章里很详细的介绍了APIHOOK的原理以及实践,如果你对APIHOOK的理论知识不是很理解,那么建议你看完我写的这篇文章在学习本篇文章。
这里我先创建了一个基本的控制台程序:
#include <stdio.h>int main(int argc, char* argv[])
{return 0;
}
在封装一个APIHOOK的类
这篇代码是通过我之前写的:https://blog.csdn.net/bjbz_cxy/article/details/90637158 这篇文章里的代码修改而来,去掉了汇编部分,用更简单的方法实现了
对上一篇文章里的代码进行了重构
class MyHookClass {
public:MyHookClass(){}~MyHookClass(){}}
准备工作,申请中间变量,用于保存一些函数信息,以供hook完后的复原操作,声明为保护,防止被开发人员手动修改
private:PROC m_pfnOld;//原API函数地址BYTE m_bOldBytes[5];//原api前5个字节的跳转地址BYTE m_bNewBytes[5];//新的跳转地址
第一步为APIHOOK类编写一个Hook函数
BOOL Hook(char* szModuleName/*模块名*/, char* szFuncName/*函数名*/, PROC pHookFunc/*新的函数地址*/){}
参数作用:
char* szModuleName HOOk的API属于哪个DLL模块里
char* szFuncName HOOK的APImingzi
PROC pHookFunc 新的函数地址
//第一步获取DLL句柄,Windows下操作任何设备以及应用模块都以句柄为钥匙,同时Windows内核也会记录每一次句柄操作的次数
BOOL Hook(char* szModuleName/*模块名*/, char* szFuncName/*函数名*/, PROC pHookFunc/*新的函数地址*/){m_pfnOld = GetProcAddress(GetModuleHandleA(szModuleName), szFuncName);if (!m_pfnOld){return FALSE;}}
//第二步保存原API地址
DWORD dwNum = 0;
ReadProcessMemory(GetCurrentProcess(), m_pfnOld, m_bOldBytes, 5, &dwNum);
//第三步取当前函数的地址 这一步将之前的APIHOOK里的汇编代码修改掉了,所以这里我需要说一下
m_bNewBytes[0] = '\xe9';*(DWORD*)(m_bNewBytes + 1) = (DWORD)pHookFunc - (DWORD)m_pfnOld - 5;
之前的方式是这样的:
实际地址=我们的api地址-拦截的api地址-要写入的数据长度
//地址转换ULONG Api_MoPtr = JumpApiPtr - (ULONG)&Api;Api_MoPtr = Api_MoPtr - 5;//将地址写入到操作码的后四位char My_FuncPtr[5] = { 0 };_asm{mov eax, Api_MoPtr //获取刚刚获得的地址mov dword ptr[My_FuncPtr + 1], eax //将算出的地址保存到Arr后面4个字节//注:一个函数地址占4个字节}
后来我发现其实这样写更直观和简单一点
m_bNewBytes[0] = '\xe9';*(DWORD*)(m_bNewBytes + 1) = (DWORD)pHookFunc - (DWORD)m_pfnOld - 5;
最后一步写入到cll的地址下就可以啦
WriteProcessMemory(GetCurrentProcess(), m_pfnOld, m_bNewBytes, 5, &dwNum);
完整的HOOK函数代码:
BOOL Hook(char* szModuleName/*模块名*/, char* szFuncName/*函数名*/, PROC pHookFunc/*新的函数地址*/){m_pfnOld = GetProcAddress(GetModuleHandleA(szModuleName), szFuncName);if (!m_pfnOld){return FALSE;}DWORD dwNum = 0;ReadProcessMemory(GetCurrentProcess(), m_pfnOld, m_bOldBytes, 5, &dwNum);m_bNewBytes[0] = '\xe9';*(DWORD*)(m_bNewBytes + 1) = (DWORD)pHookFunc - (DWORD)m_pfnOld - 5;WriteProcessMemory(GetCurrentProcess(), m_pfnOld, m_bNewBytes, 5, &dwNum);return TRUE;}
除此之外,我们还要写一个复原和复位的函数,用于还原API和重新HOOK的功能
//复原void UnHook(){if (m_pfnOld != nullptr){DWORD dwNum = 0;WriteProcessMemory(GetCurrentProcess(), m_pfnOld, m_bOldBytes, 5, &dwNum);}}//重置bool ReHook(){if (m_pfnOld != nullptr){DWORD dwNum = 0;WriteProcessMemory(GetCurrentProcess(), m_pfnOld, m_bNewBytes, 5, &dwNum);return FALSE;}return TRUE;}
这两个函数用在调用里,因为比如是recv函数,如果直接修改跳转到我们的函数里了,那么我们是取不到任何数据的,所以我们需要在函数里调用UnHook函数把函数复原,然后在里面调用一次原生态的Recv获取到数据之后,在使用Rehook将其重置到我们的函数里。
完整代码:
class MyHookClass {
public:MyHookClass(){m_pfnOld = nullptr;ZeroMemory(m_bNewBytes, 5);ZeroMemory(m_bOldBytes, 5);}~MyHookClass(){UnHook();}BOOL Hook(char* szModuleName/*模块名*/, char* szFuncName/*函数名*/, PROC pHookFunc/*新的函数地址*/){m_pfnOld = GetProcAddress(GetModuleHandleA(szModuleName), szFuncName);if (!m_pfnOld){return FALSE;}DWORD dwNum = 0;ReadProcessMemory(GetCurrentProcess(), m_pfnOld, m_bOldBytes, 5, &dwNum);m_bNewBytes[0] = '\xe9';*(DWORD*)(m_bNewBytes + 1) = (DWORD)pHookFunc - (DWORD)m_pfnOld - 5;WriteProcessMemory(GetCurrentProcess(), m_pfnOld, m_bNewBytes, 5, &dwNum);return TRUE;}//复原void UnHook(){if (m_pfnOld != nullptr){DWORD dwNum = 0;WriteProcessMemory(GetCurrentProcess(), m_pfnOld, m_bOldBytes, 5, &dwNum);}}//重置bool ReHook(){if (m_pfnOld != nullptr){DWORD dwNum = 0;WriteProcessMemory(GetCurrentProcess(), m_pfnOld, m_bNewBytes, 5, &dwNum);return FALSE;}return TRUE;}
private:PROC m_pfnOld;//原API函数地址BYTE m_bOldBytes[5];//原api前5个字节的跳转地址BYTE m_bNewBytes[5];//新的跳转地址
};
上面介绍的这个方法是最常用的方法,还有其它很多方法,如PE方式的HOOk
这个方法难度不是很大,技术点就在于APIHOOK的原理还有逻辑地址转换https://blog.csdn.net/bjbz_cxy/article/details/90574824 我在这篇文章中有很详细的介绍API HOOK
如果你想拦截其它程序的封包数据需要使用DLL注入的方式,注入到目标进程下就可以了,原因是因为内存保护,用户态下不能随便越过自己的内存区的。
实战:
这个是Recv函数的原型
int recv(_In_ SOCKET s, _Out_ char* buf, _In_ int len, _In_ int flags) ;
根据原型写一个我们自己的函数:
int recv1(_In_ SOCKET s, _Out_ char* buf, _In_ int len, _In_ int flags)
我们在写一个客户端/服务器程序
#include <winsock2.h>
#include <WS2tcpip.h>
#include<stdio.h>
#include<iostream>
#include<cstring>
#include <Windows.h>#pragma comment(lib,"ws2_32.lib")
注意包含头文件的时候,Windows头文件要在Winsock2的下面,不然会出现很多类型错误的问题
其次是要注意一个问题,因为是DLL,如果我们在没调用任何Socket函数的情况下,ws2_32dll是不会载入到程序里的,所以如果我们在Socket之前调用了我们的APIHOOK是不会成功的,所以我们需要当目标程序在调用任何Socket函数后再去使用APIHOOK,因为动态库的特点就是需要时才会加载到内存
我直接把APIHOOK的代码放在一个程序下,也就是说HOOK当前程序下的Recv实现封包拦截功能,如果你想HOOK别人的程序可以参考我前面关于HOOK的文章,里面有详细的方法和代码
这个是客户端代码
int main(int argc, char* argv[])
{// hook windows apichar szModuleName[MAXBYTE] = { 0 };//"user32.dll";char szFuncName[MAXBYTE] = { 0 };// "MessageBoxW";strcpy_s(szModuleName, MAXBYTE, "WS2_32.dll");strcpy_s(szFuncName, MAXBYTE, "recv");WORD sockVersion = MAKEWORD(2, 2);WSADATA data;if (WSAStartup(sockVersion, &data) != 0){return 0;}sockaddr_in serAddr;serAddr.sin_family = AF_INET;serAddr.sin_port = htons(8888);inet_pton(AF_INET, "127.0.0.1", &serAddr.sin_addr);if (FALSE == g_MsgHook.Hook(szModuleName, szFuncName, (PROC)recv1)) {printf("error");}while (true){SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sclient == INVALID_SOCKET){printf("invalid socket!");return 0;}if (connect(sclient, (sockaddr*)&serAddr, sizeof(serAddr)) == SOCKET_ERROR){ //连接失败 printf("connect error !");closesocket(sclient);return 0;}send(sclient, "asd", strlen("asd"), 0);char recData[255] = {0};int ret = recv(sclient, recData, 12, 0);if (ret > 0) {recData[ret] = 0x00;printf(recData);}closesocket(sclient);}WSACleanup();return 0;
}
recv部分
这里我们复原后然后调用原生态的recv去读数据,这样既没有让原生态的recv失效,我们也能提前拿到数据,非常美哉
因为最开始用户态调用的recv是进入到recv1里了,然后我们在根据传递进来的socket参数(需要注意一点Sock通信中传递进来的数据会根据协议放在对应的缓冲区里,术语上叫做窗口,实际上只是一块内存缓冲区,如TCP窗口等等,如果不去调用recv函数读的话,我们是无法获取这块缓冲区的内容的,必须通过sock提供的接口,理论上来说recv读完缓冲区后会将缓冲区的内容清空)
MyHookClass g_MsgHook;int recv1(_In_ SOCKET s, _Out_ char* buf, _In_ int len, _In_ int flags) {//重置recv函数g_MsgHook.UnHook();//用recv函数接收服务器消息recv(s, buf, 255, 0);//打印printf("hook:%s",buf);//重新挂钩g_MsgHook.ReHook();return 0;}
服务器:
// ConsoleApplication2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <stdio.h>
#include <winsock2.h> #pragma comment(lib,"ws2_32.lib") int main(int argc, char* argv[])
{//初始化WSA WORD sockVersion = MAKEWORD(2, 2);WSADATA wsaData;if (WSAStartup(sockVersion, &wsaData) != 0){return 0;}//创建套接字 SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (slisten == INVALID_SOCKET){printf("socket error !");return 0;}//绑定IP和端口 sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(8888);sin.sin_addr.S_un.S_addr = INADDR_ANY;if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR){printf("bind error !");}//开始监听 if (listen(slisten, 5) == SOCKET_ERROR){printf("listen error !");return 0;}//循环接收数据 SOCKET sClient;sockaddr_in remoteAddr;int nAddrlen = sizeof(remoteAddr);char revData[1024];while (true){printf("等待连接...\n");sClient = accept(slisten, (SOCKADDR*)&remoteAddr, &nAddrlen);if (sClient == INVALID_SOCKET){printf("accept error !");continue;}//接收数据 int ret = recv(sClient, revData, 255, 0);if (ret > 0){revData[ret] = 0x00;printf(revData);}//发送数据 const char* sendData = "hello \n";send(sClient, sendData, strlen(sendData), 0);}closesocket(sClient);closesocket(slisten);WSACleanup();return 0;
}
先运行服务器程序,当有连接来的时候返回hello
看下我们的recv函数是否拦截成功:
成功HOOK到了
完整代码:
API HOOK:
class MyHookClass {
public:MyHookClass(){m_pfnOld = nullptr;ZeroMemory(m_bNewBytes, 5);ZeroMemory(m_bOldBytes, 5);}~MyHookClass(){UnHook();}BOOL Hook(char* szModuleName/*模块名*/, char* szFuncName/*函数名*/, PROC pHookFunc/*新的函数地址*/){m_pfnOld = GetProcAddress(GetModuleHandleA(szModuleName), szFuncName);if (!m_pfnOld){return FALSE;}DWORD dwNum = 0;ReadProcessMemory(GetCurrentProcess(), m_pfnOld, m_bOldBytes, 5, &dwNum);m_bNewBytes[0] = '\xe9';*(DWORD*)(m_bNewBytes + 1) = (DWORD)pHookFunc - (DWORD)m_pfnOld - 5;WriteProcessMemory(GetCurrentProcess(), m_pfnOld, m_bNewBytes, 5, &dwNum);return TRUE;}//复原void UnHook(){if (m_pfnOld != nullptr){DWORD dwNum = 0;WriteProcessMemory(GetCurrentProcess(), m_pfnOld, m_bOldBytes, 5, &dwNum);}}//重置bool ReHook(){if (m_pfnOld != nullptr){DWORD dwNum = 0;WriteProcessMemory(GetCurrentProcess(), m_pfnOld, m_bNewBytes, 5, &dwNum);return FALSE;}return TRUE;}
private:PROC m_pfnOld;//原API函数地址BYTE m_bOldBytes[5];//原api前5个字节的跳转地址BYTE m_bNewBytes[5];//新的跳转地址
};
demo代码:
客户端:
// HookMessage.cpp: 定义控制台应用程序的入口点。
//#include <stdio.h>#include <winsock2.h>
#include <WS2tcpip.h>
#include<stdio.h>
#include<iostream>
#include<cstring>#include <Windows.h>
#pragma comment(lib,"ws2_32.lib")
class MyHookClass {
public:MyHookClass(){m_pfnOld = nullptr;ZeroMemory(m_bNewBytes, 5);ZeroMemory(m_bOldBytes, 5);}~MyHookClass(){UnHook();}BOOL Hook(char* szModuleName/*模块名*/, char* szFuncName/*函数名*/, PROC pHookFunc/*新的函数地址*/){m_pfnOld = GetProcAddress(GetModuleHandleA(szModuleName), szFuncName);if (!m_pfnOld){return FALSE;}DWORD dwNum = 0;ReadProcessMemory(GetCurrentProcess(), m_pfnOld, m_bOldBytes, 5, &dwNum);m_bNewBytes[0] = '\xe9';*(DWORD*)(m_bNewBytes + 1) = (DWORD)pHookFunc - (DWORD)m_pfnOld - 5;WriteProcessMemory(GetCurrentProcess(), m_pfnOld, m_bNewBytes, 5, &dwNum);return TRUE;}//复原void UnHook(){if (m_pfnOld != nullptr){DWORD dwNum = 0;WriteProcessMemory(GetCurrentProcess(), m_pfnOld, m_bOldBytes, 5, &dwNum);}}//重置bool ReHook(){if (m_pfnOld != nullptr){DWORD dwNum = 0;WriteProcessMemory(GetCurrentProcess(), m_pfnOld, m_bNewBytes, 5, &dwNum);return FALSE;}return TRUE;}
private:PROC m_pfnOld;//原API函数地址BYTE m_bOldBytes[5];//原api前5个字节的跳转地址BYTE m_bNewBytes[5];//新的跳转地址
};MyHookClass g_MsgHook;int recv1(_In_ SOCKET s, _Out_ char* buf, _In_ int len, _In_ int flags) {g_MsgHook.UnHook();recv(s, buf, 255, 0);printf("hook:%s",buf);g_MsgHook.ReHook();getchar();return 0;}
int main(int argc, char* argv[])
{// hook windows apichar szModuleName[MAXBYTE] = { 0 };//"user32.dll";char szFuncName[MAXBYTE] = { 0 };// "MessageBoxW";strcpy_s(szModuleName, MAXBYTE, "WS2_32.dll");strcpy_s(szFuncName, MAXBYTE, "recv");WORD sockVersion = MAKEWORD(2, 2);WSADATA data;if (WSAStartup(sockVersion, &data) != 0){return 0;}sockaddr_in serAddr;serAddr.sin_family = AF_INET;serAddr.sin_port = htons(8888);inet_pton(AF_INET, "127.0.0.1", &serAddr.sin_addr);if (FALSE == g_MsgHook.Hook(szModuleName, szFuncName, (PROC)recv1)) {printf("error");}while (true){SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sclient == INVALID_SOCKET){printf("invalid socket!");return 0;}if (connect(sclient, (sockaddr*)&serAddr, sizeof(serAddr)) == SOCKET_ERROR){ //连接失败 printf("connect error !");closesocket(sclient);return 0;}send(sclient, "asd", strlen("asd"), 0);char recData[255] = {0};int ret = recv(sclient, recData, 12, 0);if (ret > 0) {recData[ret] = 0x00;printf(recData);}closesocket(sclient);}WSACleanup();return 0;
}
服务器:
// ConsoleApplication2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <stdio.h>
#include <winsock2.h> #pragma comment(lib,"ws2_32.lib") int main(int argc, char* argv[])
{//初始化WSA WORD sockVersion = MAKEWORD(2, 2);WSADATA wsaData;if (WSAStartup(sockVersion, &wsaData) != 0){return 0;}//创建套接字 SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (slisten == INVALID_SOCKET){printf("socket error !");return 0;}//绑定IP和端口 sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(8888);sin.sin_addr.S_un.S_addr = INADDR_ANY;if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR){printf("bind error !");}//开始监听 if (listen(slisten, 5) == SOCKET_ERROR){printf("listen error !");return 0;}//循环接收数据 SOCKET sClient;sockaddr_in remoteAddr;int nAddrlen = sizeof(remoteAddr);char revData[1024];while (true){printf("等待连接...\n");sClient = accept(slisten, (SOCKADDR*)&remoteAddr, &nAddrlen);if (sClient == INVALID_SOCKET){printf("accept error !");continue;}//接收数据 int ret = recv(sClient, revData, 255, 0);if (ret > 0){revData[ret] = 0x00;printf(revData);}//发送数据 const char* sendData = "hello \n";send(sClient, sendData, strlen(sendData), 0);}closesocket(sClient);closesocket(slisten);WSACleanup();return 0;
}
Windows核心编程_HOOk SOCKET实现封包拦截相关推荐
- Windows核心编程_HOOK(续)_APIHOOK
啰嗦啰嗦: 开始之前还是要啰嗦几句,看到自己博客粉丝增加,访问量也越来越多,感到非常开心,而且好评也是不少,指错也非常感谢,从错误中发现了很多问题,非常感谢,也高兴自己的文章能帮助到其它人. 就比如之 ...
- C++Windows核心编程读书笔记(转)
http://www.makaidong.com/(马开东博客) 这篇笔记是我在读<windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的 ...
- [C++]《Windows核心编程》读书笔记
这篇笔记是我在读<Windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对实现的推断,因此不少条款和Windows实际机制可能有出入 ...
- 《windows核心编程系列》二谈谈ANSI和Unicode字符集
第二章:字符和字符串处理 使用vc编程时项目-->属性-->常规栏下我们可以设置项目字符集合,它可以是ANSI(多字节)字符集,也可以是unicode字符集.一般情况下说Unicode都是 ...
- 《windows核心编程系列》十八谈谈windows钩子
windows应用程序是基于消息驱动的.各种应用程序对各种消息作出响应从而实现各种功能. windows钩子是windows消息处理机制的一个监视点,通过安装钩子能够达到监视指定窗体某种类型的消息的功 ...
- [笔记]Windows核心编程《二十》DLL的高级操作技术
系列文章目录 [笔记]Windows核心编程<一>错误处理.字符编码 [笔记]Windows核心编程<二>内核对象 [笔记]Windows核心编程<三>进程 [笔记 ...
- [笔记]Windows核心编程《十六》线程栈
系列文章目录 [笔记]Windows核心编程<一>错误处理.字符编码 [笔记]Windows核心编程<二>内核对象 [笔记]Windows核心编程<三>进程 [笔记 ...
- [笔记]Windows核心编程《十九》DLL基础
系列文章目录 [笔记]Windows核心编程<一>错误处理.字符编码 [笔记]Windows核心编程<二>内核对象 [笔记]Windows核心编程<三>进程 [笔记 ...
- Windows核心编程 - API HOOK应用
#Windows核心编程 - API HOOK应用 如需转载请标明出处:http://blog.csdn.net/itas109 QQ技术交流群:129518033 目录 文章目录 #Windows核 ...
最新文章
- python每隔2s执行一次hello world!
- spring三: 装配bean( 在xml中进行显式配置, 在java中进行显式配置)
- java基础(八) 深入解析常量池与装拆箱机制
- echarts地图在ie浏览器上不显示
- zhajinhuagame为了迎接新版
- Niginx笔记-Linux上源码安装Niginx
- js生成一周内的日期+周几
- 从客户端(Content=p666/p)中检测到有潜在危险的 Request.Form 值。
- d3 v4 api transitions
- Python numpy基础知识
- shapenet各类数据(转载)
- java中$和 的区别详解_MyBatis中#{}和${}的区别详解
- tomcat使用中出现的问题及其解决之道
- Win10安装Ubuntu16.04 双系统
- 职业学校计算机课评课,信息技术课评课范文
- 实验01 使用网络协议分析仪Wireshark分析数据链路层帧结构实验报告
- 用Python获取好看听书网中的《星期五有鬼》有声小说
- K-th Closest Distance (主席树)
- prometheus如何评估告警策略以及如何推送告警消息到alertmanager?
- 如何把microsoft store里面的软件添加到桌面
热门文章
- shedlock 重启系统报错问题_闲谈ShedLock解决分布式定时任务重复执行问题
- atm系统的用例模型_ATM银行系统用例图.doc
- eclipse代码量统计插件_Android Studio 代码行数统计插件Statistic的简单使用
- 施耐德 m340 编程手册_施耐德PLC漏洞历险记
- python中高阶函数和装饰器_Python高阶函数与装饰器函数的深入讲解
- c语言函数可变长参数,一种使用变长参数为C程序构造灵活回调函数的方法
- 苹果CMS V10 播放记录_苹果cms采集后无法播放怎么解决?
- docker创建mysql实例_使用docker创建mysql实例
- 如何隐藏地址栏中的真实地址_代理IP如何隐藏真实IP
- java 接口开发时间_Java开发中的日期和时间使用