- UID
- 1
- 精华
- 积分
- 76361
- 威望
- 点
- 宅币
- 个
- 贡献
- 次
- 宅之契约
- 份
- 最后登录
- 1970-1-1
- 在线时间
- 小时
|
我来说一下浏览器是怎么上网的。首先,网站的服务器程序开启之后,它会通过TCPIP协议监听80端口并等待连接。
然后,用户通过浏览器上网,浏览器会尝试连接到服务器,连接完成以后,浏览器按照HTTP协议发送一个数据包(HTTP请求)。
服务器接收到浏览器的数据包以后,回复一个数据包给浏览器。这个数据包包括了一个HTTP头和一些其余的数据(比如网页文件、图片等)。
浏览器接收到服务器的数据包以后,显示出网页的内容。这就是浏览器的运行方式。
虽然HTTP协议我们可以自己从网上找到学习的资料,但是如果我们能自己截获这些数据包,我们就能更容易地学到它的原理。因此我写了一个转发程序。
程序运行的原理:通过TCP/IP协议监听80端口。当有浏览器连接到它的时候,它就去连接一个别的、我们指定了的网页(比如www.baidu.com)
然后把浏览器的数据包转发到网站服务器(百度)(顺带再把数据包发一份到stdout)。然后百度的服务器就会回复一个数据包,我这个转发器就截获百度的数据包并转发给浏览器(顺带再把数据包发一份到stdout)。我没有研究过HTTP协议,因此我这里就不给出HTTP协议的详细的细节了。- #include<stdio.h>
- #include<process.h>
- #include<winsock2.h>
- #define BUFSIZE 8192 //缓冲区大小
- #define TARGET_WEBSITE "www.baidu.com"//要访问的网站
- SOCKET g_SockToServer=INVALID_SOCKET;
- SOCKET g_SockToBrowser=INVALID_SOCKET;
- void Repeater1(void *p)//转发器1:接收浏览器数据,转发到服务器
- {
- char buffer[BUFSIZE+1]={0};
- int moredata=0;
- int nRecv=0;
- for(;;)//从浏览器接收数据
- {
- memset(buffer,0,sizeof(buffer));
- nRecv=recv(g_SockToBrowser,buffer,BUFSIZE,0);//接收数据
- if(WSAGetLastError()==WSAEMSGSIZE)//如果WSAGetLastError为WSAEMSGSIZE,表示数据包大小超过了缓冲区
- {
- moredata=1;
- nRecv=BUFSIZE;
- }
- else
- moredata=0;
- printf("浏览器数据包:\n%s\n",buffer);//输出到stdout
- if(send(g_SockToServer,buffer,nRecv,0)!=nRecv)//转发
- {
- puts("转发失败。");
- break;
- }
- if(!moredata)
- break;
- }
- }
- void Repeater2(void *p)//转发器2:接收服务器数据,转发到浏览器
- {
- char buffer[BUFSIZE+1]={0};
- int moredata=0;
- int nRecv=0;
- for(;;)//从服务器接收数据
- {
- memset(buffer,0,sizeof(buffer));
- nRecv=recv(g_SockToServer,buffer,BUFSIZE,0);//接收数据
- if(WSAGetLastError()==WSAEMSGSIZE)
- {
- moredata=1;
- nRecv=BUFSIZE;
- }
- else
- moredata=0;
- printf("服务器数据包:\n%s\n",buffer);//输出到stdout
- if(send(g_SockToBrowser,buffer,nRecv,0)!=nRecv)
- {
- puts("转发失败。");
- break;
- }
- if(!moredata)
- break;
- }
- }
- void DealWithConn(SOCKET s,void*pBuffer,int nRecv)//处理单个连接的程序
- {
- g_SockToBrowser=s;
- g_SockToServer=socket(AF_INET,SOCK_STREAM,0);
- do
- {
- SOCKADDR_IN sAddr={0};//连接目标
- struct hostent *phostaddr;//域名解析
- if(g_SockToServer==INVALID_SOCKET)
- {
- puts("socket失败\n");
- break;
- }
- phostaddr=gethostbyname(TARGET_WEBSITE);//解析域名
- if(!phostaddr)
- {
- puts("域名解析失败。");
- break;
- }
- sAddr.sin_family=AF_INET;
- sAddr.sin_port=htons(80);
- sAddr.sin_addr.s_addr=*(u_long*)(phostaddr->h_addr_list[0]);
- printf("服务器IP:%u.%u.%u.%u\n",
- (unsigned char)phostaddr->h_addr_list[0][0],
- (unsigned char)phostaddr->h_addr_list[0][1],
- (unsigned char)phostaddr->h_addr_list[0][2],
- (unsigned char)phostaddr->h_addr_list[0][3]);//打印IP
- if(connect(g_SockToServer,(SOCKADDR*)&sAddr,sizeof(sAddr)))//连接到服务器
- {
- puts("连接不上目标。");
- break;
- }
- _beginthread(Repeater1,0,NULL);//两个转发器同步执行
- Repeater2(NULL);
- puts("已断开连接。");
- closesocket(g_SockToServer);//关闭连接
- closesocket(g_SockToBrowser);
- return;
- }while(0);
- printf("出BUG了。\nWSAGetLastError=%u\n",WSAGetLastError());
- if(g_SockToServer!=INVALID_SOCKET)
- closesocket(g_SockToServer);
- closesocket(g_SockToBrowser);
- }
- int main()
- {
- WSADATA wsaData;
- SOCKET sockListener=INVALID_SOCKET;
- SOCKET sockConn=INVALID_SOCKET;
- if(WSAStartup(WINSOCK_VERSION,&wsaData))//初始化Winsock库
- {
- printf("WSAStartup失败。\n");
- return 1;
- }
- do
- {
- SOCKADDR_IN sAddr={0};
- char cDataRecv[BUFSIZE+1]={0};//多分配一个字节
- int nRecv=0;
- sockListener=socket(AF_INET,SOCK_STREAM,0);
- if(sockListener==INVALID_SOCKET)
- {
- printf("socket失败\n");
- break;
- }
- sAddr.sin_family=AF_INET;
- sAddr.sin_port=htons(80);
- sAddr.sin_addr.s_addr=INADDR_ANY;
- if(bind(sockListener,(SOCKADDR*)&sAddr,sizeof(sAddr)))//绑定端口
- {
- printf("bind失败\n");
- break;
- }
- if(listen(sockListener,SOMAXCONN))//监听
- {
- printf("listen失败\n");
- break;
- }
- for(;;)
- {
- puts("等待建立连接。");
- sockConn=accept(sockListener,NULL,NULL);//等待建立连接
- if(sockConn!=INVALID_SOCKET)
- {
- puts("已连接。");
- DealWithConn(sockConn,cDataRecv,nRecv);//处理单个连接
- }
- }
- closesocket(sockListener);
- return 0;
- }while(0);
- printf("遇到了某些问题。\nWSAGetLastError=%u\n",WSAGetLastError());
- if(sockListener!=INVALID_SOCKET)
- closesocket(sockListener);
- if(sockConn!=INVALID_SOCKET)
- closesocket(sockConn);
- return 1;
- }
复制代码 详细的使用方法:
方法1、运行它,然后用浏览器直接上“http://localhost/”或“http://127.0.0.1/”虽然看不到数据,但是你能捕获浏览器给出的HTTP头。
方法2、运行它之前改hosts,把目标网站域名重定向到127.0.0.1,然后运行这个程序,用浏览器上目标网站,能捕获到完整的数据。
方法3、运行它,然后在局域网的另一台电脑(或虚拟机)用浏览器访问你的电脑IP。可以看到浏览器发送的数据包。
提示:
1、开启了IIS服务或Apache服务的请事先关闭这两个服务,以免因端口冲突导致这个程序无法运行。
2、我不能保证这个程序的可移植性(因为用到了WSAGetLastError来判断缓冲区外是否还有数据)。
3、本程序由0xAA55原创。转载请指明出处。
4、完整的VC6工程以及编译生成的EXE下载:(请先回帖。回帖后就能下载。)
补充一下,我这里给出两个截获的HTTP头的范例。一定要注意HTTP头后面的两个空行!
以下内容是IE10发送给服务器的数据包:- GET /网页文件路径 HTTP/1.1
- Accept: text/html, application/xhtml+xml, */*
- Accept-Language: zh-CN
- User-Agent: Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko
- Accept-Encoding: gzip, deflate
- Host: 网站域名
- DNT: 1
- Connection: Keep-Alive
-
-
复制代码 以下内容是谷歌浏览器(Chrome)发送给服务器的数据包:- GET /网页文件路径 HTTP/1.1
- Host: 网站域名
- Connection: keep-alive
- Cache-Control: max-age=0
- Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
- User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/33.0.1750.146 Safari/537.36
- Accept-Encoding: gzip,deflate,sdch
- Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4
-
-
复制代码 |
|