转载 教大家如何打造使用Tcpviewtcp查看器 adsl密码查看器

原文地址:教大家如何打造使用Tcpview(tcp查看器)作者:jybasenet3 一玩 VS 对战平台的同学有一次发现了一个可以踢人的方法,就是用 TcpView把那个连
接关掉。后来VS 平台封掉了这个程序,只要一打开TcpView就会被 VS 关掉。于是我萌生了
自己做个 TcpView的想法。
Tcpview是Winternals公司 Sysinternals系列工具之一,尽管大部分这些工具网上都
有源代码,唯独没有找到TcpView的源代码。能找到的其实只是个命令行下的一个很简易的
Tcpview。现在的目标就是尽量模拟一个界面版的Tcpview。
这个程序主要的功能就是显示系统中当前的TCP和UDP 连接信息,包括本地地址、端口,
远程地址、端口,连接状态,所属进程等,以及这些连接的上传、下载的数据量,并且可以
断开 TCP 连接。我还添加了一个功能,显示远程地址对应的物理地址。程序使用VC 6.0 MFC
的单文档开发,主界面是个ListView。

在该程序中获取 TCP 连接用的是 GetExtendedTcpTable 函数,获取 UDP 连接用的是
GetExtendedUdpTable函数,断开一个TCP 连接用的是 SetTcpEntry。其实这些信息通过调
试 Tcpview都能发现,不过关于如何获取上传下载流量,我真的没弄出来它到底是怎么实现
的(OD 水平太菜了)。但是经过测试,Tcpview 中包含的一个驱动并没有用到,因为任何工
具都没有检测到有驱动加载。希望有高手可以指点一下!

于是,我只好自己用原始套接字实现了,可惜唯一的缺陷是不能获取回环地址通信时的
流量。比如,我本地开了FTP 服务器,然后用127.0.0.1连接上去传输文件,此时是无法截
获到数据包的。关于这一点,我在网上查到的资料说是在Windows下原始套接字是无法办到
的,甚至 Winpcap包也不可以,要捕获的话可以用SPI或是API Hook。


具体实现

下面介绍实现的细节。由于GetExtendedUdpTable与GetExtendedTcpTable的用法非常
相似,故这里只介绍GetExtendedTcpTable的用法。
GetExtendedTcpTable函数在 SDK 中没有,所以要自己定义。
typedef DWORD (WINAPI *PFNGetExtendedTcpTable)(
__outPVOID pTcpTable, //返回查询结构体指针
__in_outPDWORD pdwSize, //第一次调用该参数会返回所需要的
缓冲区大小
__inBOOL bOrder, //是否排序
__inULONG ulAf, //是 AF_INET还是AF_INET6
__inTCP_TABLE_CLASS TableClass, // 表示结构体的种类,
此处设为 TCP_TABLE_OWNER_PID_ALL
__inULONG Reserved //保留不用,设为 0
);
pTcpTable 其实是一个指向 MIB_TCPTABLE_OWNER_PID类型的指针。
MIB_TCPTABLE_OWNER_PID结构定义如下:
typedef struct _MIB_TCPTABLE_OWNER_PID
{
DWORDdwNumEntries;
MIB_TCPROW_OWNER_PID table[ANY_SIZE];
} MIBTCPTABLEOWNERPID, *PMIBTCPTABLEOWNERPID;

dwNumEntries表示MIB_TCPROW_OWNER_PID结构的数目,每个该结构指定一个TCP 连接
的信息。ANY_SIZE 的值被定义为1,可以理解为 table 是 MIB_TCPROW_OWNER_PID 结构体数
组的首地址,这样我们可以任意地访问每个数组的成员。这种定义方式就好比一列火车,告
诉你车厢数以及火车头的地址,我们就可以得到每节车厢的地址。再来看
MIB_TCPROW_OWNER_PID的定义:

介绍完相关的数据结构就可以来使用该函数了。这里是我程序
typedef struct _MIB_TCPROW_OWNER_PID
{
DWORDdwState;//连接状态
DWORDdwLocalAddr;//本地 IP地址
DWORDdwLocalPort;//本地端口
DWORDdwRemoteAddr;//远程 IP 地址
DWORDdwRemotePort;//远程端口

DWORDdwOwningPid;//关联的进程ID
} MIB_TCPROW_OWNER_PID, *PMIB_TCPROW_OWNER_PID;

int GetTcpConnect()
{
HMODULE hMod = LoadLibrary("Iphlpapi.dll");
if(!hMod)
{
AfxMessageBox("加载Iphlpapi.dll出错");
return 0;
}
PFNGetExtendedTcpTable pfnGetTcpTable =
(PFNGetExtendedTcpTable)::GetProcAddress(hMod,"GetExtendedTcpTable");//获取
函数地址
PMIB_TCPTABLE_OWNER_PID pTcpTable = newMIB_TCPTABLE_OWNER_PID;
DWORD dwSize =sizeof(MIB_TCPTABLE_OWNER_PID);
if (pfnGetTcpTable(pTcpTable,&dwSize,
TRUE,AF_INET,TCP_TABLE_OWNER_PID_ALL,0) ==ERROR_INSUFFICIENT_BUFFER)
{//第一次调用时不知道要传入的缓冲区大小,所以要试探一下,参数dwSize会返
回真正需要的大小
delete pTcpTable;

pTcpTable =(MIB_TCPTABLE_OWNER_PID *)new char[dwSize];//重新分配缓
冲区
}
if(pfnGetTcpTable(pTcpTable,&dwSize,TRUE,AF_INET,TCP_TABLE_OWNER_PID_AL
L,0) != NO_ERROR)
{
AfxMessageBox("获取TCP连接出错");
delete pTcpTable;
return 0;
}
int nNum = (int)pTcpTable->dwNumEntries; //TCP连接的数目
for(int i=0;i<nNum;i++)
{
printf(“本地地址:%s:%d远程地址:%s:%d 状态:%d进程ID:%d”,
inet_ntoa(*(in_addr*)&pTcpTable->table[i].dwLocalAddr), //本地IP 地址
htons(pTcpTable->table[i].dwLocalPort), //本地端口
inet_ntoa(*(in_addr*)&pTcpTable->table[i].dwRemoteAddr), //远程IP地址
htons(pTcpTable->table[i].dwRemotePort), //远程端口

pTcpTable->table[i].dwState, //状态
pTcpTable->table[i].dwOwningPid); //所属进程PID
}
delete pTcpTable;
}

获取进程 ID 之后就可以获取进程名和路径了。我使用的是
CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0)方法,在ROCESSENTRY32中没有进程的

路径信息,可以用 GetModuleFileNameEx 函数获得,或是通过
CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,nPId)查找进程模块,出现的第一个模块
就是程序模块,路径存储在me32.szExePath中。这部分代码请参看我的源程序。
既然要模仿TcpView,当然要有进程图标了。一开始我选择去读取图标资源,但是这种
方法兼容性不好,比较麻烦,其实只要用ExtractIcon函数就好了。以下是提取代码:

遇到没有图标的程序可以使用系统默认的程序图标,只要调用
HICON GetExeIcon(CString strExe,int nIdx = 0)//获取第 nIdx+1个图标,一般程序
将 nIdx 设为 0就可以了
{
if(strExe == "") return NULL;
HMODULE hExe = LoadLibrary(strExe);//把EXE当二进制资源加载
if (hExe == NULL)
{
return NULL;
}
int nNum =(int)::ExtractIcon(hExe,strExe,-1);//获取图标数
if(nNum == 0) return NULL;
HICON hIcon =::ExtractIcon(hExe,strExe,nIdx);//提取图标
FreeLibrary(hExe);//释放资源
return hIcon;
}

遇到没有图标的程序可以使用系统默认的程序图标,只要调用
GetExeIcon("shell32.dll",2)就好了。要在每一项前面显示图标,需要在视图类的定义中
添加图像列表指针 CImageList *m_pImgList,并在构造函数中初始化 m_pImgList = new
CImageList; m_pImgList->Create(16, 16,ILC_COLORDDB|ILC_COLOR32, 8, 8),然后在
List 添加列的同时将图像列表加入 List : m_list.SetImageList(m_pImgList,
LVSIL_SMALL)。获取图标句柄后调用m_pImgList->Add(hIcon),返回值为图标在ImageList
中的索引,该索引即是InsertItem时设置的图标索引。
关闭 TCP 连接的方法是用 SetTcpEntry 函数设置连接的状态为:
MIB_TCP_STATE_DELETE_TCB,下面是具体代码:
MIB_TCPROW tcprow;
tcprow.dwLocalAddr = dwLocalIp;//本地 IP地址
tcprow.dwRemoteAddr = dwRemoteIp;//远程IP 地址
tcprow.dwLocalPort = ntohs(nLocalPort);//本地端口
tcprow.dwRemotePort = ntohs(nRemotePort);//远程端口
tcprow.dwState = MIB_TCP_STATE_DELETE_TCB;//删除连接
SetTcpEntry(&tcprow);

使用原始套接字可以监听到接收到的所有IP 数据包,只要分析TCP 包和 UDP 包的相关
信息就可以得到每个连接的流量信息。每个连接在内存中是以 UpDownInfo 结构体的形式存

储的。网上讲述原始套接字的文章有很多,黑防也有不少这种文章,这里只给出关键代码:



//由于代码很长,部分地方会省略冗长的代码,详细请见源程序
vector<DWORD> dwMyIp; //本机IP列表,存放所有IP地址,以便嗅探所有数据包
char szHost[256];
// 取得本地主机名称
::gethostname(szHost, 256);
// 通过主机名得到地址信息
hostent *pHost = ::gethostbyname(szHost);
in_addr addr;
for(int i = 0; ; i++)
{
char *p =pHost->h_addr_list[i];
if(p == NULL) break;
dwMyIp.push_back(inet_addr(inet_ntoa(*(in_addr
*)pHost->h_addr_list[i]))); //保存IP地址
}
nNetNum = i;
for(i = 0;i<nNetNum;i++)

{
::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WorkThread,(LPVOID)this,0,
0); //建立新线程,WorkThread是嗅探的工作线程
}
int WINAPI WorkThread(LPVOID Param)
{ // 创建原始套接字
SOCKET sock;
sock = socket(AF_INET, SOCK_RAW,IPPROTO_IP);
// 设置 IP头操作选项,其中 flag 设置为ture,亲自对 IP头进行处理
BOOL flag=TRUE;
setsockopt(sock, IPPROTO_IP, IP_HDRINCL,(char*)&flag, sizeof(flag));
SOCKADDR_IN addr_in;
addr_in.sin_addr.S_un.S_addr =dwMyIp[nIndex++];//用于设置监听的IP
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(10013); //绑定任意端口
if(bind(sock,(PSOCKADDR)&addr_in, sizeof(addr_in)) ==SOCKET_ERROR)
{
AfxMessageBox("绑定地址失败");
return 1;
}
// dwValue为输入输出参数,为1 时执行,0时取消
DWORD dwValue = 1;
// 设置 SOCK_RAW为SIO_RCVALL,以便接收所有的IP包。其中SIO_RCVALL
// 的定义为: #define SIO_RCVALL_WSAIOW(IOC_VENDOR,1)

ioctlsocket(sock, SIO_RCVALL,&dwValue); //将网卡设置为混合模式
char RecvBuf[BUFFER_SIZE];//接收数据的缓冲区
while(1) //死循环
{
int ret = recv(sock, RecvBuf, BUFFER_SIZE, 0);
if (ret >0)
{
IpHeader*iphdr = (IpHeader *)RecvBuf; //此处略去IP头定义,下同,
详见代码
int nLen =ntohs(iphdr->TotalLen) - sizeof(IpHeader);//获取下层
数据包长度
int bUp =IsMyIp(iphdr->SrcAddr,iphdr->DstAddr);//判断是否是发
送数据,返回-1 表示不是本机数据包,因为原始套接字必须要设为混杂模式,否则无
法监听到数据
if(bUp == -1)continue;//拒绝接收
DWORD dwLocalIp =bUp?iphdr->SrcAddr:iphdr->DstAddr;
DWORDdwRemoteIp =bUp?iphdr->DstAddr:iphdr->SrcAddr;
inthdrLen,i;
if(iphdr->Protocol == IPPROTO_TCP) //是TCP 包
{
TcpHeader *tcphdr = (TcpHeader*)(RecvBuf + iphdr->HdrLen*4);
hdrLen=iphdr->HdrLen*4+sizeof(TcpHeader);
USHORTnLocalPort =
bUp?ntohs(tcphdr->SrcPort):ntohs(tcphdr->DstPort);//获取本地端口
USHORTnRemotePort =
bUp?ntohs(tcphdr->DstPort):ntohs(tcphdr->SrcPort);//获取远程端口
for(inti=0;i<pThis->m_connList.size();i++)
{//m_connList为vector<UpDownInfo>类型,UpDownInfo是保存连接相
关信息的结构体
if( 判断 m_connList中是否存在该连接)
{
[转载]教大家如何打造使用Tcpview(tcp查看器) adsl密码查看器
if(bUp)
{
m_connList[i].dwUpData += nLen - sizeof(TcpHeader);
m_connList[i].nUpPacket ++;
}else{
m_connList[i].dwDownData += nLen-sizeof(TcpHeader);

m_connList[i].nDownPacket++;
}
}
if(i>=m_connList.size())
{
UpDownInfoinfo;
省略初始化结构体代码
m_connList.push_back(info);
}
}
if(iphdr->Protocol == IPPROTO_UDP) //是UDP 包
{
UdpHeader *udphdr = (UdpHeader *)(RecvBuf +iphdr->HdrLen*4);
//省略UDP部分的处理代码,与上类似

}
}else{
//出现错误
break;
}
}
return 0;
}

有一点要注意的是, UDP 协议与 TCP 不同,很多基于P2P 的程序只是绑定一个本地的 UDP
端口,然后监听,此时可能所有的数据包都是发送到该端口上来的。原版的Tcpview是不分
析 UDP 地址的,只看本地端口。我在此基础上做了点改进,会显示远程的 IP 地址,不过显
示的只是收到的最近包的地址。其实也是可以做成显示所有远程通信地址的,只不过编写上
会更麻烦一些(有兴趣读者可以自己完成)。
显示物理地址部分我用的是网上的代码,就是查找纯真 IP 数据库中的记录,得到物理
地址。如果当前程序目录下没有“qqwry.dat”文件,List中是不会添加“物理地址”这一
列的。程序启动后连接默认会根据进程名排序,新加入的连接也会自动进行插入排序。

总结

在程序制作过程中也花了不少精力,比如刚开始总是有严重的资源泄漏问题,后来用的
是 BoundsChecker不断调试解决的。现在我提供的这个版本还是有点简陋,有些细节也没有
处理好,欢迎大家批评指正。最后谈一谈开头的话题,就是 VS 中的踢人问题,原理很简单,作为主机(服务端)时
所有数据包都会发送到我电脑上来,只要我把要踢的人的TCP 连接关了,那他自然就掉线了,
但前提是你要清楚要踢的是谁。还有种踢人挂用的是API Hook的方式,主要是对 send函数
的挂钩,用SPI也是可以实现的。

  

爱华网本文地址 » http://www.aihuau.com/a/25101015/263648.html

更多阅读

教你如何解除NOD32杀毒软件的密码保护 nod32手机杀毒软件

教你如何解除NOD32杀毒软件的密码保护——简介ESET NOD32是一款深受用户喜欢的杀毒软件,近年来可谓声名雀跃,正被越来越多的win7旗舰版用户所选择,ESET NOD32提供了个人密码保护设 置功能,可以避免其他用户非法修改NOD32的设置。比如说,

教大家如何安装Kx驱动详细步骤和教程 剪窗花的详细步骤图

教大家如何安装Kx驱动详细步骤和教程——简介现在很多朋友喜欢网络K歌 喊麦 当然本人也是非常喜欢的 可是有时候不小心声卡松了驱动没了不会安装怎么办? kx驱动也是大家经常碰到的问题今天教大家如何正确安装kx驱动教程 看了我教程的

教大家如何对同步推正版授权 同步推正版授权 免费

教大家如何对同步推正版授权——简介很多朋友都和我一样,iphone没有越狱,只能下载同步推正版来用但是一些新手就不知道怎么对同步推正版进行授权了,因为不授权的话下载的软件就会闪退。下面我统一教大家怎么对同步推正版进行授权吧,非常

教你如何购买使用阿里娱乐宝? 阿里云购买了如何使用

教你如何购买使用阿里娱乐宝?——简介 阿里巴巴数字娱乐事业群宣布推出新一代“宝”——娱乐宝平台。也被称为会赚钱的支付宝,广大网友可以通过单次出资100元投资热门影视作品(仅限娱乐宝支持的电影),官方预计预期年收益率7%。娱乐宝仅支

教你如何正确使用车灯 悦达起亚车灯调法图

教你如何正确使用车灯——简介汽车灯光的使用与安全行车有着直接的关系,了解和正确使用车灯就显得尤为重要了。据我了解,在今年5月份广州交警查乱用灯光的活动中,交警发现很多车主竟然不会调节远近光。面对交警的询问,车主的回答也是五

声明:《转载 教大家如何打造使用Tcpviewtcp查看器 adsl密码查看器》为网友黑白颠倒分享!如侵犯到您的合法权益请联系我们删除