广西科学院学报
Journal of Guangxi Academy of Sciences
2007,23(4):266~269
Vo1.23,No.4 November 2007
一 个 Windows进程抓包器的 C++实现
Impl ementation of the W indows Process Packetcapture
Based on C+ +
胡小春 ,陈 燕。,何潜航。,李陶深。
HU Xiao—chun ,CHEN Yan。,HE Qian—hang。,LI Tao—shen。
(1.广西财经学院计算机与信息管理系,广西南宁 530003;2,广西大学计算机与电子信息学
院,广西南宁 530004)
(1 Department of Computer and Information Management,Guangxi University of Finance&
Economics,Nanning,Guangxi,530003,China;2. School of Computer,Electronics and
Information,Guangxi University,Nanning,Guangxi,530004,China)
摘要:对 Windows进程中的模块 WS2
— 32.dll和 wsock32.dll代码段进行修改,通过 C++的 API函数编程
实现进程抓包器,并以实现 IEXPLORE浏览器抓包的过程为例,给出了进程抓包器的具体编程
。
关键 词:Windows进程 抓包 数据包
中图法分类号 :TP393.2 文献标识码 :A 文章编 号:1002—7378(2007)04—0266—04
Abstract:By modifying the codes of WS 32.dl1 and wsock32.dl1 windows models。this paper
describes how to implement the process packetcapture based on the C + + API function
programming and shows how tO program the process packetcapture by taking the example of how
tO implement the IEXPLORE packetcapture.
Key words:W indows process,packetcapture,datapacket
Windows是一个开放 的系统,一个进程可以对
其它进程进行控制 ,包括内存的访问和读取、虚拟内
存的控制、系统权限的控制等,甚至可以调试其它进
程控制 目标进程的运行 1 ]。要实现这些功能的控制
首先要分析、研究 Windows系统的内核对象管理,
然后研究利用 Win32 ASM对 Windows进程的具体
实现进行分析 、研究 。通过对进程运行的机器码反编
译来分析程序,了解其逻辑结构并加以修改,就可以
让 目标进程按照设定的程序来运行 “]。对于一个经
过加密的进程数据包 ,目前互联网上的软件并不能
截取到进程的明文数据包 。本文通过对 Windows进
程中的模块 WS2 32.dll和 wsock32.dll代码段进
行修改,编程实现进程抓包器,并以 VC++.NET
实现 IEXPLORE浏览器抓包的过程为例 ,介绍进程
抓包器的具体编程实现。
收稿 日期:2007—09—18
作者简介:胡小春(1974一),男,讲师,主要从事计算机网络与并行分
布式计算研究。
*广西大学院 校共 管项 目(X061002)和广西大学创新学分项 目联合
资助 。
l 进程抓包器的实现过程[。]
抓包器实现的目的为了查看和了解进程中数据
包 的 内容 和结构 ,它的 实现主要 是通过 C++的
API函数实现。实现抓包器 的主要过程如下。
(1)在目标进程的窗口中再生成一个窗口,实现
“注入”目标进程的内部 窗 口用于显示截取到的数
据包内容。“注入”的操作就是把一个模块或者 自己
编写功能代码段潜入到目标进程 中去 。“注入”的方
法有远端线程注入、系统内核级注入等,最常用的是
在 系 统 上 安 装 “钩 子 (HOOK)”。API的 函数
SetWindowsHookEx可 以实施 一个钩子的安装 ,并
且可以把相应的消息传送到指定的回调函数处理。
系统发送给各种程序窗口的消息都要先经过钩子处
理之后再送到 目的窗 口,而在钩子处理到来的消息
之前 Windows已经 自动把 “钩子”“钩”在 了消息 目
的进程窗口上了,也就是说此时“钩子”已经“混入”
了目的窗口的内部。利用HOoK可以把一个或几个
模块加载进入目标进程,并截取目标进程的窗口消
息。钩子设定成功后,当系统有键盘消息要传给目标
维普资讯 http://www.cqvip.com
胡小春等:一个Windows进程抓包器的c++实现 267
进 程 窗 口时 ,键 盘 消 息 就 会 先 经 过 回 调 函 数
KeyboardProc进行处理 ;而模块 g hlnstance就会
在钩子设定成功后,系统传给目标进程窗口第一个
键盘消息的时候被注入到目标进程。
(2)修 改 目标 进 程 的模 块 WS2 32.dll和
wsock32.dll数据包传输函数的代码段,使得程序跳
转到设计好的数据包处理 函数里执行 ,完成之后再
跳转回原来的代码执行 。设计的函数主要完成数据
包过滤,并把过滤的信息在数据包窗口中显示出来。
模块 WS2 32.dll中数据包的发送和接收函数分别
是 send和 recv,模块 wsock32.dll中数据包 的接收
函数是 recv。Windows socket中有两个接收函数 ,由
于不可能预先知道 目标进程将用哪个模块 中的接收
函数,所 以在设计时同时处理 。
(3)最后,在用 自己定义的 mysend()函数时要
先把 send函数的代码还原,然后再调用,之后如果
想继续截取 send函数 的数据 ,就再次修 改 send函
数的代码 。
通过 以上 3个 过程 ,可 以截取到进程发送 的数
据包,也可以截取该进程任何函数的数据。以下是用
到的两个关键 函数 。
①回调函数:
LRESULT CALLBACK KeyboardProc(int
nCode,WPARAM wParam,LPARAM 1Param ){
B0OL bKeyUp一 (BOOL)1Param & (1< < 31);
if(bKeyUp&& wParam — VK H0ME&& nCode
— HC ACT10N)//截取到按下 HOME键的消息
1 ⋯ ⋯ ;
//按下 H()ME键后的处理代码 ,生成数据包
显示窗口 )
)
②SetWindowsHookEx函数:
SetWindowsHookEx(W H
—
KEYBOARD,
(HOOKPROC)KeyboardProc, g
—
hInstance,
wThreadID);//KEYBOARD是 指定 是 键 盘消 息 ;
(HO0KPROC)KeyboardProc是消息处理的回调 函
数 ;g hlnstance 指 定 要 注 入 的 模 块 基 址;
dwThreadID是 目标进程窗 口线程 ID。
2 进程抓包器的具体实现
2.1 抓包器注入模块的实现
抓包器 的注入模块通过以下 7个步骤实现。
(1)用 VC++.NET新建一个解决
,在方
案里添加一个 MFC DLL工程 ,命名 为 Packet。在
Packet中添加窗 口 ID为 IDD DIALOG MAIN。
然后为这个 窗 口添加类 CMainWnd继承 CDialog。
窗 口中三个勾选框 Send,Recv2,Recvs分别表示是
否 打 开对 wS2 32.dll中 send,WS2 32.dll中
recv,wsock32.dll中 recv行数 的拦截。
(2)新 建 类 CJmpHookApi,对 应 的 文 件 为
JmpHookApi.cpp和 JmpHookApi.h。CJmpHookApi
类 目的是获得 IE的句柄、得到 IE的控制权 ,并在其
中实现抓包的最终目的。
@JmpHookApi.h文件的主要代码 :
public:
FARPROC In lpOldFunc;//要钩的目标的函数
FARPROC m lpNewFunc;//新的函数
CJmpHookApi():
virtual~CJmpHookApi();
BOOL InitMemory();//初始化得到内存数据
(模块名,函数名 ,新函数 FARPROC)
void SetHookOn();//启用 新函数
void SetHookOff();//不启用 新 函数
void UnLock();
void Lock();
protected:
HANDLE In hProc;//进程句柄
BYTE ITI bO1dData[5];//内存旧数据
BYTE ITI bNewData~5 ;//内存新数据
CRITICAL SECTION ITI CS;};//CRITICAL
SECTION 属于轻量级的线程同步对象
②JmpHookApi.cpp里几个重要的函数实现代
码 :
CJmpHookApi::CJmpHookApi()
//得到当前进程的伪句柄,并初始化 CRITICAL
SECTION
BOOL CJmpHookApi::InitMemory()
{ DWORD dwOldF!ag;//修改旧函数内存前 5
字节为 PAGE READWRITE
if(VirtualProtectEx(m
—
hProc,ITI
—
lpOldFunc,5,
PAGE
READWRITE,g>dwOldFlag))
{ //调入旧函数内存前 5字节
if(ReadProcessM ernory(m hProc,ITI
lpOldFunc,
Ill
—
bOldData,5,O))
{ //取得旧函数内存前 5字节内存数据
if(VirtualProtectEx (ITI
—
hProc,ITI
—
lpOldFunc,
5,dwOldFlag,g>dwOldFlag))
维普资讯 http://www.cqvip.com
268 广西科学院学报 第 23卷 第 4期 2007年 11月
{ m bNewData[03—0xE9;//JMP指
令
DW ORD pNewFuncAddress:
//得到新函数的地址
· · · · · · 、
void CJmpHookApi::SetHoOkOn()
//修改旧函数内存前 5字节为新的内容
void CJmpHookApi::SetHookOff()
//修改旧函数内存前 5字节为原来内容
void CJmpHookApi::Lock()//多线程下使用 进
入临界区
void CJmpHookApi::UnLock()//多线程下使用离
开临界区
(3)新建文件Globa1.h和 Globa1.cpp,主要是定
义全局变量和 自定义的函数实现,例如 mysend、
myrecv、myrecvs函数的实现。Globa1.h的几个主要
变量 :
typedef int (W SAAPI * FUNW S2 32) (IN
SOCKET,IN const char FAR *,IN int,IN int);
extern FUNWS2 32 funRecv;//WS2 32.dll原
recv函数基址
extern FUNWS2 32 funRecvs;//wsock32.dll原
recv函数基址
extern FUNWS2 32 funSend;//WS2 32.dll原
send函数基址
extern SOCKET g Socket;//上 次 send函数 的
SOCKET
extern CMainW nd g
—
pMainWnd;//抓包器 窗
体指针
//新的数据包处理函数声明
int stdcall FAR mysend (SOCKET S,const char
FAR * buf,int len,int flags);
int stdcall FAR myrecv (S0CKET S,const char
FAR * buf,int len,int flags);
int stdcall FAR myrecvs(SOCKET S,const char
FAR * buf,int len,int flags);
BOOL HooklnitMemory(void);//内存初始化
void ShowPack(char pData,int len,int type);//
显示数据包
BOOL CheckFilter(const char FAR pData,int
nFlag);//判断数据包第一个字节的过滤器
BOOL CheckFilterLen(int nLen,int nFlag); //N
断数据包长度的过滤器
int StringToHex(char* strIn,char* strOut);
//字符串转换为数据
void Char2Cstring(char* buf,int len,CString
Str);//数据转换为字符 串
CString GetConfigFileName();//获得保存的配置
文件名
BOOL GetCListCtrl(CListCtrl pList,CString
strListKey,int nColCount,CString strFileName)
//读取并设定 List控件内容
BOOL SaveCListCtrl(CListCtrl pList,CString
strListKey,int nColCount,CString strFileName)
//保存 List控件 内容
(4)新建文件 Hook.h和 Hook.cpp,主要实现
Hook成功后的回调函数的处理 ,代码如下 :
||Hook.cpp
extern HINSTANCE g
— hlnstance;//当前模块的基
址
BOOL declspec(dllexport)W INAPI tnstallHook
(DWORD dwThreadId);//安装勾子
B0()L declspec (dllexport ) WINAPI
UninstallHook(void);//取消勾子
LRESULT CALLBACK KeyboardProc(int nCode,
WPARAM wParam,LPARAM 1Param);//消息处
理回调函数
||Hook.cpp
#include”stdafx.h”
#include"Globa1.h,,
#include Hook.h”
HHOOK g
—
hhook—NULL;//勾子
HINSTANCE g
~
hlnstance— NULL;//当前模
块 的基址
//HOOK成功后 处 理 WH KEYBOARD 消息的
回调函数
LRESULT CALLBACK KeyboardProc(int nCode,
W PARAM wParam ,LPARAM 1Param )
( if(bKeyUp 8L& wParam — VK
—
HOME 8L8L
nCode=HC ACTION)//截取到按下 HOME键的
消息
{if(g
—
pMainWnd一一 NULL)//窗口生成代
码
{CWnd::GetForegroundWindow();//找到
当前窗口
if(pCW nd! 一NULL)
{g
—
pMainW nd — new CM ainW nd(). g
—
pMainW nd 一 > Create (CM ainW nd:: IDD,
pCW nd);
维普资讯 http://www.cqvip.com
胡小春等:一个 Windows进程抓包器的c++实现 269
⋯ ⋯ //在当前窗 口创建抓包器窗口
}
(5)在 Packet.cpp中包括文件 Hook.h,添加函
数 Initlnstance()和 ExitInstance()。
#inc1ude”Hook.h ,
BOOL CPacketApp::Initlnstance()
{ AfxInitRichEdit();
CW inAPP::Initlnstance();
g—hInstance-二 theApp.m
—
hInstance;
//得到当前模块的基址
if(!AfxSocketInit())
{AfxMessageBox(IDP
—
SOCKETS
—
INIT
—
FAILED):
return FALSE;}
return TRUE;
}
BOOL CPacketApp::Exitlnstance()
{ JmpSend.SetHookOff(); //还原代码段
JmpRecv.SetHookOff(); //还原代码段
UninstallHook(); //清理钩子
}
(6)在解决方案的配置处选择 Release,并把工
程 Packet的输出 目录改为 out。
(7)用 Release生成工程 Packet,生成后 ,在../
out目录可以看到 Packet.dll和 Packet.1ib文件。
2.2 抓包器注入程序的实现
抓包器的注入程序通过 以下 3个步骤实现 。
(1)在解决方案添加一个 MFC应用程序工程,
作为简单的CDialog应用程序,命名为Begin。在窗
体中添加开始按钮 ,按钮执行的代码段如下:
#include ../Packet/Hook.h”//勾 子 函数定 义文
件
void CBeginDlg::OnBnC1ickedButtonBegin()//开始
按钮
{DW0RD dwThreadlD 一 0x0;
CW nd pCW nd — FindW indow ( T (
IEFrame”),NULL);//打开 IE浏览器
if(dwThreadID ! ===0x0)
{InstallHook(dwThreadlD);//安装钩子
OutputDebugString(’ InstallHook(dwThreadID) ’);
CString outM sg;
outMsg.Format(”目标 :%08x”,dwThreadlD);
SetW indowText(outMsg);
GetDlgltem (IDC
—
BUTTON
—
BEGIN ) 一 >
EnableW indow(FALSE)
}
else MessageBox( 没有找到目标 ,”提示”,MB
—
UK);
}
(2)把 Begin的输 出 目录也 改为../out,并输
入 ,附加依赖../out/Packet.1ib。
(3)用 Release生成 Begin。
2.3 抓包器应用测试
按以下步骤对抓包器进行测试。
(1)运行 2.2中生成的 Begin.exe程序,点击开
始 ,如果没有 打开 IE,则提示找不到 目标。如果 IE
已经 打开,则可 以看到 Begin的窗 口标
变 为:目
标 :0X000xxxx。找到目标之后 ,在 IE里按下 HOME
键 ,就可以看到抓包器的窗 口了。
(2)用调试器 OllyICE调试 。重新打开 IE,然后
打 开调试器 ,动态加载 IE进程 。加载完成后察看
OllyICE的模块列表 。
(3)运行 Begin程序,点击开始,然后在被调试
的 IE里按 下 HOME键 ,可以看 到调试器 OllylCE
的模块列表里面增加了Packet.dll模块,
已经
成功注入抓包器模块了。
(4)在调试器 OllyICE中查找所有模块 中的名
称 ,找到 WS2 32.dll中的 send函数,双击 send函
数转到CPU视图的send函数的基址。然后钩选抓
包器 中的 send,可以在 OllyICE里看到 WS2 32.dll
中的send函数的前 5个字节的指令被改为:JMP
Packet.XXXX,取消抓包器的 send的钩选,这条指令
又会被还原。可以用 同样的方法测试其它两个 recv
函数 。
3 结束语
利 用 HOOK 来 截 取 Windows的 消 息 是
Windows核心编程的基础知识,可以利用 HOOK实
现丰 富的 Windows程序 效果。更 重要 的是 ,通过
HOOK可以实现 Windows进程对象的控制,进一步
了解 Windows对内核对象的管理原理。
参考 文献:
[1] 孟庆倩 ,李清宝.基于 Windows环境进程监控的设计
与实现[J].信息工程大学学报,2007,8(1):26—29.
[2] 王峰,董亮卫.Windows(2000/XP)下隐藏进程的检测
机制EJ].计算机工程,2006,32(2O):95—96,99.
[3] 周炎涛.Windows中的多线程编程技术和实现EJ].计
算技术与 自动化 ,2002,21(3):109—116.
[4] 梁骥.防火墙与入侵检测系统(IDS)互动模型的构建
EJ].广西科学院学报,2007,23(2):80—81,84.
(责任编辑:韦廷宗)
维普资讯 http://www.cqvip.com