C++实现socks5代理检测

Socks5握手协议

握手阶段,向服务端发送一个包含版本识别码和验证方法选择的消息。

VER NMETHODS METHODS
1 byte 1 byte 1-255 byte
0x05 0x03 0x00 0x01 0x02

第一位是版本号,对于socks5协议固定是0x05

第二位是methods的数量,决定了后面methods的长度

第三位是具体的methods

  • 0x00 不需要认证(常用)
  • 0x01 GSSAPI认证
  • 0x02 账号密码认证(常用)
  • 0x03 - 0x7F IANA分配
  • 0x80 - 0xFE 私有方法保留
  • 0xFF 无支持的认证方法

Socks5握手返回可用Methods

无需认证

VERSION METHOD
1字节 1字节
0x05 0x00

账号密码认证

VERSION METHOD
1字节 1字节
0x05 0x02
  • VERSION SOCKS协议版本,目前固定0x05
  • METHOD 本次连接所用的认证方法,上例中为无需认证

发送账号密码认证方式

CVERSION USERNAME_LENGTH USERNAME PASSWORD_LENGTH PASSWORD
1字节 1字节 1-255字节 1字节 1-255字节
0x01 0x01 0x0a 0x01 0x0a
  • VERSION 认证子协商版本(与SOCKS协议版本的0x05无关系)

  • USERNAME_LENGTH 用户名长度

  • USERNAME 用户名字节数组,长度为USERNAME_LENGTH

  • PASSWORD_LENGTH 密码长度

  • PASSWORD 密码字节数组,长度为PASSWORD_LENGTH


<—-more->

服务端响应账号密码认证结果

CVERSION STATUS
1字节 1字节
0x01 0x00 认证通过
0x01 0x01 认证失败
  • CVERSION 认证子协商版本,与密码验证CVERSION字段一致
  • STATUS 认证结果
    • 0x00 认证成功
    • 非 0x00 认证失败

连接服务器指定端口

VER CMD RSV ATYP DST.ADDR DST.PORT
1 byte 1 byte 1 byte 1 byte 4 byte 2 byte
0x05 0x01 0x00 0x01 127,0,0,1 00 , 80
  • VER SOCKS协议版本
    • 0x05
  • CMD CONNECT命令
    • 0x01:表示CONNECT请求
    • 0x02:表示BIND请求
    • 0x03:表示UDP转发
  • RSV RSV保留字段
    • 默认为 0x00
  • ATYP 地址类型为IPV4
    • 0x01 为IPV4
    • 0x03:表示域名格式
    • 0x04:表示IPV6地址
  • DST.ADDR目标服务器IP
    • 当ATYP=0x01 DST.ADDR部分为四字节长度,内容为IP本身,0x7f 0x00 0x00 0x01 = 127.0.0.1。
    • 当ATYP=0x03 第一个部分为一个1字节表示域名长度,第二部分就是剩余内容为具体域名,长度不定,没有\0作为结尾。
  • DST.PORT目标服务器端口
    • 0x00 0x50 = 00 80

服务端返回端口状态

VER RCV RSV ATYP DST.ADDR DST.ADDR
1 byte 1 byte 1 byte 1 byte 4 byte 2 byte
0x05 0x00 0x01 0x01 127,0,0,1 0x04 , 0x38
  • VER 返回版本号
    • 0x05
  • RCV 返回的状态码
    • 0x00:succeeded
    • 0x01:general SOCKS server failure
    • 0x03:Network unreachable
    • 0x04:Host unreachable
    • 0x05:Connection refused
    • 0x06:TTL expired
    • 0x07:Command not supported
    • 0x08: Address type not supported
    • 0x09-0xff unassigned
  • RSV 无实际作用
  • ATYP.仅用于响应BIND命令
    • 0x01:表示IPV4地址
    • 0x03:表示域名格式
    • 0x04:表示IPV6地址
  • DST.ADDR.目的地址,仅用于响应BIND命令
    • 当ATYP=0x01 DST.ADDR部分为四字节长度,内容为IP本身
    • 当ATYP=0x03 第一个部分为一个1字节表示域名长度,第二部分就是剩余内容为具体域名,长度不定。没有\0作为结尾
  • DST.PORT 网络字节序表示的目的端口,仅用于响应客户端BIND命令

发送数据给服务端

连接成功后直接发送数据到Socks服务端即可

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <stdio.h>
#include <Windows.h>
#pragma comment(lib,"ws2_32.lib")

bool Init() {
WSADATA wsa;
/*初始化socket资源*/
if (WSAStartup(MAKEWORD(1, 1), &wsa) != 0)
{
return false; //代表失败
}
return true;
}

void printChar(char * data,DWORD len) {
printf("\nByte:\n ");
for (int i = 0; i < len; i++) {
printf("%02x", data[i] & 0xff);
}
printf("\nStr:\n %s\n",data);
}
void Connect() {
SOCKET Client_Sock = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(1080);
int ret = connect(Client_Sock, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
printf("ret:%d\n", ret);

char bytes[] = { 5,2,0,2};
send(Client_Sock, bytes, sizeof(bytes), 0);
char recvBuf[4096] = {0};
int len = recv(Client_Sock, recvBuf, 50, 0);
printf("len:%d", len);
if (len == -1) return;
printChar(recvBuf, len);

char bytes1[] = { 1,5,'a','d','m','i','n',5,'a','d','m','i','n'};
send(Client_Sock, bytes1, sizeof(bytes1), 0);
len = recv(Client_Sock, recvBuf, 50, 0);
printf("len:%d", len);
printChar(recvBuf, len);


char bytes2[] = { 5 ,1,0 ,1, 220,181,38,148, 1 ,0xbb};
send(Client_Sock, bytes2, sizeof(bytes2), 0);
len = recv(Client_Sock, recvBuf, 50, 0);
printf("len:%d", len);
printChar(recvBuf, len);

char bytes3[] = "GET /index HTTP/1.1\
Host: 127.0.0.1 : 9000\
User - Agent : Mozilla / 5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko / 20100101 Firefox / 63.0\
Accept : text / html, application / xhtml + xml, application / xml; q = 0.9, */*;q=0.8\
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\
Connection: keep-alive\
Upgrade-Insecure-Requests: 1\
Accept-Encoding: gzip, deflate\n\n";
//char bytes[] = {5,5,'a','d','m','i','n',5,'a','d','m','i','n','\0'};
send(Client_Sock, bytes3, sizeof(bytes3), 0);
len = recv(Client_Sock, recvBuf, 4096, 0);
printf("len:%d", len);
printChar(recvBuf, len);

closesocket(Client_Sock);
}

void Uninit() {
WSACleanup();
}

int main() {
if(!Init()){
printf("网络环境初始化失败!\n");
return 0;
}
Connect();
Uninit();
return 0;
}