| 订阅 | 在线投稿
分享
 
 
 

VB6 中 使用Winsock穿越各种代理的实现(TCP协议)

来源:互联网  宽屏版  评论
2007-01-30 20:19:59

感谢 Oleg Gdalevich 和 CSDN 用户 zyg0(影子)对本文(程序)所做的贡献

本文中引用的RFC文档内容归原作者所有

转载本文请标明出处

本文作者:吴滂

本文中提及的程序可在 枕善居 http://www.mndsoft.com/blog/blogview.asp?logID=494[/url] 下载

关于用vb的winsock穿透代理的讨论归讨论,一直没有什么源代码放出,现在我就放源出来,省的某些人拿这所谓的"技术"去骗钱.

由于缺乏测试环境,本程序只在我自己编写的代理模拟器上测试过,其结果和腾讯QQ,MSN,网易泡泡穿越该模拟器时得出的结果基本一致.因此,代码可能有错误的地方,请各位有条件的用户自行改正,请见谅!

首先,是基础知识,也就是RFC文档.这个是必看内容.我的程序就是基于这些文档写出.下面是各RFC的连接,为了准确,我先提供英文版的连接,在下面的介绍中再把关键部分翻译成中文.另外,要纠正一个错误.国内很多文章说socks5代理的用户名/密码校验标准在 RFC 1928里有说明,其实这是一个完全错误的说法(我很怀疑写那文章的人有没有看过RFC),socks5用户名/密码校验标准其实是在 RFC 1929 里面说明的.

[url=http://www.faqs.org/rfcs/rfc1928.html]RFC 1928 - socks5 代理标准
RFC 1929 - socks5 代理用户名/密码校验标准
RFC ???? - socks4 代理标准
RFC 2616 - HTTP1.1 标准

我们现在直入正题:先说socks5的TCP穿透(有了这个例子大家自己照这可以写UDP穿透)

首先和代理服务器连接-直接用winsock去connect指定的地址端口(通常是1080)即可.然后进入细节商议阶段.

细节商议--无用户名/密码校验

RFC 1928 中对于细节商议的第一步是这样描述的:

The client connects to the server, and sends a version

identifier/method selection message:

+----+----------+----------+

|VER | NMETHODS | METHODS |

+----+----------+----------+

| 1 | 1 | 1 to 255 |

+----+----------+----------+

The VER field is set to X'05' for this version of the protocol. The

NMETHODS field contains the number of method identifier octets that

appear in the METHODS field.

The server selects from one of the methods given in METHODS, and

sends a METHOD selection message:

+----+--------+

|VER | METHOD |

+----+--------+

| 1 | 1 |

+----+--------+

If the selected METHOD is X'FF', none of the methods listed by the

client are acceptable, and the client MUST close the connection.

The values currently defined for METHOD are:

o X'00' NO AUTHENTICATION REQUIRED ---------无用户密码 00

o X'01' GSSAPI ---------??? GSSAPI ?

o X'02' USERNAME/PASSWORD ---------有用户密码 02

o X'03' to X'7F' IANA ASSIGNED

o X'80' to X'FE' RESERVED FOR PRIVATE METHODS

o X'FF' NO ACCEPTABLE METHODS ---------失败 255

The client and server then enter a method-specific sub-negotiation.

换言之,就是向服务器发送三个字节的Byte数组,由于是无须用户/密码校验,展开来写是 05 01 00

然后服务器返回两个字节的信息,第一个字节固定,第而个字节是说明,如果是16进制的FF(即十进制255)表示连接失败(o X'FF' NO ACCEPTABLE METHODS)根据上面的列表,我们连接成功应该第二字节为 00.

然后我们进入第二步,请看以下RFC说明:

Once the method-dependent subnegotiation has completed, the client

sends the request details. If the negotiated method includes

encapsulation for purposes of integrity checking and/or

confidentiality, these requests MUST be encapsulated in the method-

dependent encapsulation.

The SOCKS request is formed as follows:

+----+-----+-------+------+----------+----------+

|VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |

+----+-----+-------+------+----------+----------+

| 1 | 1 | X'00' | 1 | Variable | 2 |

+----+-----+-------+------+----------+----------+

Where:

o VER protocol version: X'05' ------------- 固定 05

o CMD

o CONNECT X'01' ------------- TCP方式 01

o BIND X'02'

o UDP ASSOCIATE X'03' ------------- UDP方式 03

o RSV RESERVED ------------- 固定 00

o ATYP address type of following address

o IP V4 address: X'01' ------------- IPv4 01

o DOMAINNAME: X'03'

o IP V6 address: X'04'

o DST.ADDR desired destination address

o DST.PORT desired destination port in network octet

order

The SOCKS server will typically evaluate the request based on source

and destination addresses, and return one or more reply messages, as

appropriate for the request type.

发送 05 01 00 01 + 目的地址(4字节) + 目的端口(2字节),目的地址和端口都是16进制码(不是字符串)。

例202.103.190.27 - 7201

则发送的信息为:05 01 00 01 CA 67 BE 1B 1C 21

(CA=202 67=103 BE=190 1B=27 1C21=7201)

关于我是怎么把16进制码换成10进制的,请自己看程序

最后,接受服务器返回数据,看RFC:

+----+-----+-------+------+----------+----------+

|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |

+----+-----+-------+------+----------+----------+

| 1 | 1 | X'00' | 1 | Variable | 2 |

+----+-----+-------+------+----------+----------+

Where:

o VER protocol version: X'05' ------------ 固定 05

o REP Reply field:

o X'00' succeeded ------------ 若为 00 成功 其余可以都看成失败

o X'01' general SOCKS server failure

o X'02' connection not allowed by ruleset

o X'03' Network unreachable

o X'04' Host unreachable

o X'05' Connection refused

o X'06' TTL expired

o X'07' Command not supported

o X'08' Address type not supported

o X'09' to X'FF' unassigned

o RSV RESERVED

o ATYP address type of following address

o IP V4 address: X'01'

o DOMAINNAME: X'03'

o IP V6 address: X'04'

o BND.ADDR server bound address

o BND.PORT server bound port in network octet order

Fields marked RESERVED (RSV) must be set to X'00'.

可见,对于返回信息,只须判断第二字节是否为00.若为 00 连接成功,剩下的操作和直连一样,Winsock可直接用SendData 和 GetData 发送\接受数据.

下面介绍需要验证用户名/密码的socks5穿透

第一步还是发送三个字节,只是内容有变,展开来写为: 05 01 02

服务器返回信息也有所不同,正确的返回为 05 02

成功后发送用户/密码信息,请看RFC 说明:

Once the SOCKS V5 server has started, and the client has selected the

Username/Password Authentication protocol, the Username/Password

subnegotiation begins. This begins with the client producing a

Username/Password request:

+----+------+----------+------+----------+

|VER | ULEN | UNAME | PLEN | PASSWD |

+----+------+----------+------+----------+

| 1 | 1 | 1 to 255 | 1 | 1 to 255 |

+----+------+----------+------+----------+

The VER field contains the current version of the subnegotiation,

which is X'01'. The ULEN field contains the length of the UNAME field

that follows. The UNAME field contains the username as known to the

source operating system. The PLEN field contains the length of the

PASSWD field that follows. The PASSWD field contains the password

association with the given UNAME.

The server verifies the supplied UNAME and PASSWD, and sends the

following response:

+----+--------+

|VER | STATUS |

+----+--------+

| 1 | 1 |

+----+--------+

A STATUS field of X'00' indicates success. If the server returns a

`failure' (STATUS value other than X'00') status, it MUST close the

connection.

即 发送 01 + 用户名长度(一字节) + 转换成16进制码的用户名 + 密码长度(一字节) + 转换成16进制码的密码,关于如何把用户名和密码转换为10进制Byte数组,请自己看程序.

然后服务器返回两个字节的信息,只须判断第二字节,00 为成功,其余为失败.

剩下的步骤和无用户名密码校验是一样的,即

发送 05 01 00 01 + 目的地址(4字节) + 目的端口(2字节),目的地址和端口都是16进制码(不是字符串)。

例202.103.190.27 - 7201

则发送的信息为:05 01 00 01 CA 67 BE 1B 1C 21

(CA=202 67=103 BE=190 1B=27 1C21=7201)

关于我是怎么把16进制码换成10进制的,请自己看程序

最后接受服务器返回信息.对于返回信息,只须判断第二字节是否为00.若为 00 连接成功,剩下的操作和直连一样,Winsock可直接用SendData 和 GetData 发送\接受数据.

socks4的TCP穿透(事实上,socks4只支持TCP穿透)

无用户名/密码验证

请看 RFC 说明

1) CONNECT

The client connects to the SOCKS server and sends a CONNECT request when

it wants to establish a connection to an application server. The client

includes in the request packet the IP address and the port number of the

destination host, and userid, in the following format.

+----+----+----+----+----+----+----+----+----+----+....+----+

| VN | CD | DSTPORT | DSTIP | USERID |NULL|

+----+----+----+----+----+----+----+----+----+----+....+----+

1 1 2 4 variable 1

VN is the SOCKS protocol version number and should be 4. CD is the

SOCKS command code and should be 1 for CONNECT request. NULL is a byte

of all zero bits.

我们首先还是连接服务器,然后根据RFC的格式发送数据给服务器.由于是无用户密码验证,我们需要发送9个字节的数据,展开写为 04 01 + 目标端口(2字节) + 目标IP(4字节) + 00,奇怪的是,表中的USERID部分似乎是没有用的,我参照过大量的C++代码,代码中都没有体现该部分.

至于如何转换目标端口和IP为相应的Byte数组,请自己看示例程序.消息发出后,服务器会返回信息,格式如下:

+----+----+----+----+----+----+----+----+

| VN | CD | DSTPORT | DSTIP |

+----+----+----+----+----+----+----+----+

1 1 2 4

VN is the version of the reply code and should be 0. CD is the result

code with one of the following values:

90: request granted -------------- 成功

91: request rejected or failed -------------- 失败

92: request rejected becasue SOCKS server cannot connect to

identd on the client

93: request rejected because the client program and identd

report different user-ids

The remaining fields are ignored.

根据RFC的说法,代理服务器返回8字节的数据,我们只要判断第二字节是否为90即可,若是90连接成功,否则失败.剩下的操作和直连一样,Winsock可直接用SendData 和 GetData 发送\接受数据.

HTTP1.1 代理的穿透

由于RFC 2616过于冗长,加上HTTP代理穿透的步骤比socks简单,这里就不详细说明了,我只给出连接的步骤和发送数据格式.

第一步仍然是用Winsock去连接代理服务器.第二步为发送请求字符,其格式为:

无用户名/密码校验 格式:

"CONNECT" + 空格 + 目标连接地址 + ":" + 目标连接端口 + 空格 + "HTTP/1.1" + Chr(13) + Chr(10) + "Host:" + 空格 + 目标连接地址 + ":" + 目标连接端口 + Chr(13) + Chr(10) + Chr(13) + Chr(10)

用户名/密码验证格式:

"CONNECT" + 空格 + 目标连接地址 + ":" + 目标连接端口 + 空格 + "HTTP/1.1" + Chr(13) + Chr(10) + "Host:" + 空格 + 目标连接地址 + ":" + 目标连接端口 + Chr(13) + Chr(10) + "Authorization: Basic" + 空格 + 经Base64加密过后的[用户名:密码] + Chr(13) + Chr(10) + Chr(13) + Chr(10) + "Proxy-Authorization: Basic" + 空格 + 经Base64加密过后的[用户名:密码] + Chr(13) + Chr(10) + Chr(13) + Chr(10)

发送请求完毕后,将收到代理的回应,根据RFC说明(注意 Status-Line 和 Status-Code):

6 Response

After receiving and interpreting a request message, a server responds

with an HTTP response message.

Response = Status-Line ; Section 6.1

*(( general-header ; Section 4.5

| response-header ; Section 6.2

| entity-header ) CRLF) ; Section 7.1

CRLF

[ message-body ] ; Section 7.2

6.1 Status-Line

The first line of a Response message is the Status-Line, consisting

of the protocol version followed by a numeric status code and its

associated textual phrase, with each element separated by SP

characters. No CR or LF is allowed except in the final CRLF sequence.

Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF

6.1.1 Status Code and Reason Phrase

The Status-Code element is a 3-digit integer result code of the

attempt to understand and satisfy the request. These codes are fully

defined in section 10. The Reason-Phrase is intended to give a short

textual description of the Status-Code. The Status-Code is intended

for use by automata and the Reason-Phrase is intended for the human

user. The client is not required to examine or display the Reason-

Phrase.

The first digit of the Status-Code defines the class of response. The

last two digits do not have any categorization role. There are 5

values for the first digit:

- 1xx: Informational - Request received, continuing process

- 2xx: Success - The action was successfully received,

understood, and accepted

- 3xx: Redirection - Further action must be taken in order to

complete the request

- 4xx: Client Error - The request contains bad syntax or cannot

be fulfilled

- 5xx: Server Error - The server failed to fulfill an apparently

valid request

The individual values of the numeric status codes defined for

HTTP/1.1, and an example set of corresponding Reason-Phrase's, are

presented below. The reason phrases listed here are only

recommendations -- they MAY be replaced by local equivalents without

affecting the protocol.

Status-Code =

"100" ; Section 10.1.1: Continue

| "101" ; Section 10.1.2: Switching Protocols

| "200" ; Section 10.2.1: OK

| "201" ; Section 10.2.2: Created

| "202" ; Section 10.2.3: Accepted

| "203" ; Section 10.2.4: Non-Authoritative Information

| "204" ; Section 10.2.5: No Content

| "205" ; Section 10.2.6: Reset Content

| "206" ; Section 10.2.7: Partial Content

| "300" ; Section 10.3.1: Multiple Choices

| "301" ; Section 10.3.2: Moved Permanently

| "302" ; Section 10.3.3: Found

| "303" ; Section 10.3.4: See Other

| "304" ; Section 10.3.5: Not Modified

| "305" ; Section 10.3.6: Use Proxy

| "307" ; Section 10.3.8: Temporary Redirect

| "400" ; Section 10.4.1: Bad Request

| "401" ; Section 10.4.2: Unauthorized

| "402" ; Section 10.4.3: Payment Required

| "403" ; Section 10.4.4: Forbidden

| "404" ; Section 10.4.5: Not Found

| "405" ; Section 10.4.6: Method Not Allowed

| "406" ; Section 10.4.7: Not Acceptable

| "407" ; Section 10.4.8: Proxy Authentication Required

| "408" ; Section 10.4.9: Request Time-out

| "409" ; Section 10.4.10: Conflict

| "410" ; Section 10.4.11: Gone

| "411" ; Section 10.4.12: Length Required

| "412" ; Section 10.4.13: Precondition Failed

| "413" ; Section 10.4.14: Request Entity Too Large

| "414" ; Section 10.4.15: Request-URI Too Large

| "415" ; Section 10.4.16: Unsupported Media Type

| "416" ; Section 10.4.17: Requested range not satisfiable

| "417" ; Section 10.4.18: Expectation Failed

| "500" ; Section 10.5.1: Internal Server Error

| "501" ; Section 10.5.2: Not Implemented

| "502" ; Section 10.5.3: Bad Gateway

| "503" ; Section 10.5.4: Service Unavailable

| "504" ; Section 10.5.5: Gateway Time-out

| "505" ; Section 10.5.6: HTTP Version not supported

| extension-code

可知,如果连接成功,服务器返回的信息是 "HTTP/" + 代理版本 + "200" + 描述("Connection established")

所以我们只要判断返回的信息是否以"http"开头,是否存在" 200 "字眼即可.

以下是关键函数的源代码:

Public Function ProxyStep(ProxyType As Integer, PStep As Integer)

Dim SendByte() As Byte

If ProxyType = 0 Then '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ sock4代理

If PStep = 1 Then

ReDim SendByte(0 To 8) As Byte

SendByte(0) = 4 ' 04

SendByte(1) = 1 ' 01

SendByte(2) = Int(DestPort / 256)

SendByte(3) = DestPort Mod 256

SendByte(4) = GetIPByte(1, DestIP)

SendByte(5) = GetIPByte(2, DestIP)

SendByte(6) = GetIPByte(3, DestIP)

SendByte(7) = GetIPByte(4, DestIP)

SendByte(8) = 0 '最后要以 0 结束

Form1.Winsock1.SendData SendByte()

ConnStep = PStep + 1

Exit Function

End If

If PStep = 2 Then '代理回复,第二字节为 90 为成功,其余值为失败

If Asc(Mid(RevBuffer, 2, 1)) <> 90 Then

Debug.Print Asc(Mid(RevBuffer, 2, 1))

MsgBox "连接sock4代理失败!", 48, "错误"

Form1.Winsock1.Close

ConnStep = 0

Exit Function

Else

Form1.Label8.Caption = "连接目标服务器成功!"

ConnStep = -1

Form2.Show

Exit Function

End If

End If

End If

'*******************下面的例子有大量重复代码,是为了让大家更清楚地了解sock5穿透过程,大家可以拿回去自己优化 **********************************

If ProxyType = 1 Then '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ sock5代理

Select Case PStep

Case 1

ReDim SendByte(0 To 2) As Byte '第一步 无验证发送 05 01 00, 有验证发送 05 02 02

SendByte(0) = 5 ' 05

SendByte(1) = 1 '01 '在有用户密码验证时此字节是 1 还是 2 有诸多争论,现以腾讯QQ穿越代理模拟器时发送的数据为准,如有错误,请自己修改!

SendByte(2) = IIf(Form1.Check1.Value = 0, 0, 2) '00 或 02

Form1.Winsock1.SendData SendByte()

ConnStep = PStep + 1

Exit Function

Case 2 '代理回复

If Asc(Mid(RevBuffer, 2, 1)) = 255 Then 'FF (255) 为失败

MsgBox "连接代理失败!", 64

Form1.Winsock1.Close

ConnStep = 0

Exit Function

End If

If Asc(Mid(RevBuffer, 2, 1)) = 0 And Asc(Mid(RevBuffer, 1, 1)) = 5 Then '若代理回复 05 00 为无验证连接成功

Form1.Label8.Caption = "连接成功!无验证"

ReDim SendByte(0 To 9) As Byte '第二步 无验证 发送连接请求

SendByte(0) = 5

SendByte(1) = 1

SendByte(2) = 0

SendByte(3) = 1

SendByte(4) = GetIPByte(1, DestIP)

SendByte(5) = GetIPByte(2, DestIP)

SendByte(6) = GetIPByte(3, DestIP)

SendByte(7) = GetIPByte(4, DestIP)

SendByte(8) = Int(DestPort / 256) '把10进制端口分成两个字节

SendByte(9) = DestPort Mod 256 '把10进制端口分成两个字节

Form1.Winsock1.SendData SendByte()

ConnStep = ConnStep + 1

Exit Function

End If

If Asc(Mid(RevBuffer, 2, 1)) = 2 And Asc(Mid(RevBuffer, 1, 1)) = 5 Then '第二步 有用户名密码验证 成功为 05 02

Form1.Label8.Caption = "连接成功!有验证"

ReDim SendByte(0 To 2 + Len(UserName) + Len(UserPassword)) As Byte

SendByte(0) = 1

SendByte(1) = Len(UserName)

MemCopy SendByte(2), ByVal UserName, Len(UserName) '将用户名转换

SendByte(2 + Len(UserName)) = Len(UserPassword)

MemCopy SendByte(3 + Len(UserName)), ByVal UserPassword, Len(UserPassword) '将密码转换

Form1.Winsock1.SendData SendByte()

ConnStep = ConnStep + 1

Exit Function

End If

Case 3

If Asc(Mid(RevBuffer, 2, 1)) <> 0 And Form1.Check1.Value = 1 Then '有验证,验证失败 代理回复第二字节为 00 验证成功,其余值为失败

MsgBox "sock5代理校验用户名、密码失败!", 48, "错误"

Form1.Winsock1.Close

ConnStep = 0

Exit Function

End If

If Asc(Mid(RevBuffer, 2, 1)) = 0 And Form1.Check1.Value = 1 Then '有验证,验证成功,回复值第二字节为 00 ,其余值为失败

Form1.Label8.Caption = "连接成功!有验证!"

ReDim SendByte(0 To 9) As Byte '发送连接请求

SendByte(0) = 5

SendByte(1) = 1

SendByte(2) = 0

SendByte(3) = 1

SendByte(4) = GetIPByte(1, DestIP)

SendByte(5) = GetIPByte(2, DestIP)

SendByte(6) = GetIPByte(3, DestIP)

SendByte(7) = GetIPByte(4, DestIP)

SendByte(8) = Int(DestPort / 256) '把10进制端口分成两个字节

SendByte(9) = DestPort Mod 256 '把10进制端口分成两个字节

Form1.Winsock1.SendData SendByte()

ConnStep = ConnStep + 1

Exit Function

End If

If Asc(Mid(RevBuffer, 2, 1)) = 0 And Form1.Check1.Value = 0 Then

Form1.Label8.Caption = "连接目标服务器成功!" '无验证的最后一步,代理回复第二字节为 00 成功,其余值为失败

ConnStep = -1

Form2.Show

Exit Function

End If

If Asc(Mid(RevBuffer, 2, 1)) <> 0 And Form1.Check1.Value = 0 Then

MsgBox "连接目标服务器失败!", 48, "错误" '无验证的最后一步,代理回复第二字节为 00 成功,其余值为失败

ConnStep = 0

Form1.Winsock1.Close

Exit Function

End If

Case 4 '只有有验证才会用到这一步

If Asc(Mid(RevBuffer, 2, 1)) <> 0 Then

MsgBox "sock5代理连接目标服务器失败!", 48, "错误"

ConnStep = 0

Form1.Winsock1.Close

Exit Function

Else

Form1.Label8.Caption = "连接目标服务器成功!"

ConnStep = -1

Form2.Show

Exit Function

End If

End Select

End If

If ProxyType = 2 Then '@@@@@@@@@@@@@@@@@@@@@@@@HTTP1.1代理

If PStep = 1 Then '无用户名密码验证

If Form1.Check1.Value = 0 Then

HTTPHeader = "CONNECT " & Form1.Text5.Text & ":" & Form1.Text6.Text & _

" HTTP/1.1" & Chr(13) & Chr(10) & "Host: " & Form1.Text5.Text & ":" & Form1.Text6.Text & Chr(13) & Chr(10) & Chr(13) & Chr(10)

ConnStep = PStep + 1

Form1.Winsock1.SendData HTTPHeader

Exit Function

End If

If Form1.Check1.Value = 1 Then ' 有用户名密码验证

HTTPHeader = "CONNECT " & Form1.Text5.Text & ":" & Form1.Text6.Text & _

" HTTP/1.1" & Chr(13) & Chr(10) & "Host: " & Form1.Text5.Text & ":" & _

Form1.Text6.Text & Chr(13) & Chr(10) & "Authorization: Basic " & StrtoBase64(Form1.Text3.Text & _

":" & Form1.Text4.Text) & Chr(13) & Chr(10) & Chr(13) & Chr(10) & "Proxy-Authorization: Basic " & _

StrtoBase64(Form1.Text3.Text & ":" & Form1.Text4.Text) & Chr(13) & Chr(10) & Chr(13) & Chr(10)

' Chr(13) & Chr(10) 能否直接用vbCrLf ? 我不知道

Debug.Print HTTPHeader

ConnStep = PStep + 1

Form1.Winsock1.SendData HTTPHeader

Exit Function

End If

End If

If PStep = 2 Then '代理服务器回复,格式:HTTP/[代理版本] [状态代码] [状态说明]

If LCase(Left(RevBuffer, 4)) = "http" And Mid(" 200 ", 1) <> 0 Then '状态代码为 200 为成功

Form1.Label8.Caption = "连接目标服务器成功!"

Form2.Show

ConnStep = -1

Else

MsgBox "HTTP1.1代理连接目标服务器失败!", 48, "错误"

ConnStep = 0

Form1.Winsock1.Close

Exit Function

End If

End If

End If

End Function

感谢 Oleg Gdalevich 和 CSDN 用户 zyg0(影子)对本文(程序)所做的贡献 本文中引用的RFC文档内容归原作者所有 转载本文请标明出处 本文作者:吴滂 本文中提及的程序可在 枕善居 [url=http://www.mndsoft.com/blog/blogview.asp?logID=494]http://www.mndsoft.com/blog/blogview.asp?logID=494[/url][url=http://www.mndsoft.com/blog/blogview.asp?logID=494][/url] 下载 关于用vb的winsock穿透代理的讨论归讨论,一直没有什么源代码放出,现在我就放源出来,省的某些人拿这所谓的"技术"去骗钱. 由于缺乏测试环境,本程序只在我自己编写的代理模拟器上测试过,其结果和腾讯QQ,MSN,网易泡泡穿越该模拟器时得出的结果基本一致.因此,代码可能有错误的地方,请各位有条件的用户自行改正,请见谅! 首先,是基础知识,也就是RFC文档.这个是必看内容.我的程序就是基于这些文档写出.下面是各RFC的连接,为了准确,我先提供英文版的连接,在下面的介绍中再把关键部分翻译成中文.另外,要纠正一个错误.国内很多文章说socks5代理的用户名/密码校验标准在 RFC 1928里有说明,其实这是一个完全错误的说法(我很怀疑写那文章的人有没有看过RFC),socks5用户名/密码校验标准其实是在 RFC 1929 里面说明的. [url=http://www.faqs.org/rfcs/rfc1928.html]RFC 1928[/url] - socks5 代理标准 [url=http://www.faqs.org/rfcs/rfc1929.html]RFC 1929[/url] - socks5 代理用户名/密码校验标准 [url=http://archive.socks.permeo.com/protocol/socks4.protocol]RFC ????[/url] - socks4 代理标准 [url=http://www.faqs.org/rfcs/rfc2616.html]RFC 2616[/url] - HTTP1.1 标准 我们现在直入正题:先说socks5的TCP穿透(有了这个例子大家自己照这可以写UDP穿透) 首先和代理服务器连接-直接用winsock去connect指定的地址端口(通常是1080)即可.然后进入细节商议阶段. 细节商议--无用户名/密码校验 RFC 1928 中对于细节商议的第一步是这样描述的: The client connects to the server, and sends a version identifier/method selection message: +----+----------+----------+ |VER | NMETHODS | METHODS | +----+----------+----------+ | 1 | 1 | 1 to 255 | +----+----------+----------+ The VER field is set to X'05' for this version of the protocol. The NMETHODS field contains the number of method identifier octets that appear in the METHODS field. The server selects from one of the methods given in METHODS, and sends a METHOD selection message: +----+--------+ |VER | METHOD | +----+--------+ | 1 | 1 | +----+--------+ If the selected METHOD is X'FF', none of the methods listed by the client are acceptable, and the client MUST close the connection. The values currently defined for METHOD are: o X'00' NO AUTHENTICATION REQUIRED ---------无用户密码 00 o X'01' GSSAPI ---------??? GSSAPI ? o X'02' USERNAME/PASSWORD ---------有用户密码 02 o X'03' to X'7F' IANA ASSIGNED o X'80' to X'FE' RESERVED FOR PRIVATE METHODS o X'FF' NO ACCEPTABLE METHODS ---------失败 255 The client and server then enter a method-specific sub-negotiation. 换言之,就是向服务器发送三个字节的Byte数组,由于是无须用户/密码校验,展开来写是 05 01 00 然后服务器返回两个字节的信息,第一个字节固定,第而个字节是说明,如果是16进制的FF(即十进制255)表示连接失败(o X'FF' NO ACCEPTABLE METHODS)根据上面的列表,我们连接成功应该第二字节为 00. 然后我们进入第二步,请看以下RFC说明: Once the method-dependent subnegotiation has completed, the client sends the request details. If the negotiated method includes encapsulation for purposes of integrity checking and/or confidentiality, these requests MUST be encapsulated in the method- dependent encapsulation. The SOCKS request is formed as follows: +----+-----+-------+------+----------+----------+ |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+ Where: o VER protocol version: X'05' ------------- 固定 05 o CMD o CONNECT X'01' ------------- TCP方式 01 o BIND X'02' o UDP ASSOCIATE X'03' ------------- UDP方式 03 o RSV RESERVED ------------- 固定 00 o ATYP address type of following address o IP V4 address: X'01' ------------- IPv4 01 o DOMAINNAME: X'03' o IP V6 address: X'04' o DST.ADDR desired destination address o DST.PORT desired destination port in network octet order The SOCKS server will typically evaluate the request based on source and destination addresses, and return one or more reply messages, as appropriate for the request type. 发送 05 01 00 01 + 目的地址(4字节) + 目的端口(2字节),目的地址和端口都是16进制码(不是字符串)。 例202.103.190.27 - 7201 则发送的信息为:05 01 00 01 CA 67 BE 1B 1C 21 (CA=202 67=103 BE=190 1B=27 1C21=7201) 关于我是怎么把16进制码换成10进制的,请自己看程序 最后,接受服务器返回数据,看RFC: +----+-----+-------+------+----------+----------+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+ Where: o VER protocol version: X'05' ------------ 固定 05 o REP Reply field: o X'00' succeeded ------------ 若为 00 成功 其余可以都看成失败 o X'01' general SOCKS server failure o X'02' connection not allowed by ruleset o X'03' Network unreachable o X'04' Host unreachable o X'05' Connection refused o X'06' TTL expired o X'07' Command not supported o X'08' Address type not supported o X'09' to X'FF' unassigned o RSV RESERVED o ATYP address type of following address o IP V4 address: X'01' o DOMAINNAME: X'03' o IP V6 address: X'04' o BND.ADDR server bound address o BND.PORT server bound port in network octet order Fields marked RESERVED (RSV) must be set to X'00'. 可见,对于返回信息,只须判断第二字节是否为00.若为 00 连接成功,剩下的操作和直连一样,Winsock可直接用SendData 和 GetData 发送\接受数据. 下面介绍需要验证用户名/密码的socks5穿透 第一步还是发送三个字节,只是内容有变,展开来写为: 05 01 02 服务器返回信息也有所不同,正确的返回为 05 02 成功后发送用户/密码信息,请看RFC 说明: Once the SOCKS V5 server has started, and the client has selected the Username/Password Authentication protocol, the Username/Password subnegotiation begins. This begins with the client producing a Username/Password request: +----+------+----------+------+----------+ |VER | ULEN | UNAME | PLEN | PASSWD | +----+------+----------+------+----------+ | 1 | 1 | 1 to 255 | 1 | 1 to 255 | +----+------+----------+------+----------+ The VER field contains the current version of the subnegotiation, which is X'01'. The ULEN field contains the length of the UNAME field that follows. The UNAME field contains the username as known to the source operating system. The PLEN field contains the length of the PASSWD field that follows. The PASSWD field contains the password association with the given UNAME. The server verifies the supplied UNAME and PASSWD, and sends the following response: +----+--------+ |VER | STATUS | +----+--------+ | 1 | 1 | +----+--------+ A STATUS field of X'00' indicates success. If the server returns a `failure' (STATUS value other than X'00') status, it MUST close the connection. 即 发送 01 + 用户名长度(一字节) + 转换成16进制码的用户名 + 密码长度(一字节) + 转换成16进制码的密码,关于如何把用户名和密码转换为10进制Byte数组,请自己看程序. 然后服务器返回两个字节的信息,只须判断第二字节,00 为成功,其余为失败. 剩下的步骤和无用户名密码校验是一样的,即 发送 05 01 00 01 + 目的地址(4字节) + 目的端口(2字节),目的地址和端口都是16进制码(不是字符串)。 例202.103.190.27 - 7201 则发送的信息为:05 01 00 01 CA 67 BE 1B 1C 21 (CA=202 67=103 BE=190 1B=27 1C21=7201) 关于我是怎么把16进制码换成10进制的,请自己看程序 最后接受服务器返回信息.对于返回信息,只须判断第二字节是否为00.若为 00 连接成功,剩下的操作和直连一样,Winsock可直接用SendData 和 GetData 发送\接受数据. socks4的TCP穿透(事实上,socks4只支持TCP穿透) 无用户名/密码验证 请看 RFC 说明 1) CONNECT The client connects to the SOCKS server and sends a CONNECT request when it wants to establish a connection to an application server. The client includes in the request packet the IP address and the port number of the destination host, and userid, in the following format. +----+----+----+----+----+----+----+----+----+----+....+----+ | VN | CD | DSTPORT | DSTIP | USERID |NULL| +----+----+----+----+----+----+----+----+----+----+....+----+ 1 1 2 4 variable 1 VN is the SOCKS protocol version number and should be 4. CD is the SOCKS command code and should be 1 for CONNECT request. NULL is a byte of all zero bits. 我们首先还是连接服务器,然后根据RFC的格式发送数据给服务器.由于是无用户密码验证,我们需要发送9个字节的数据,展开写为 04 01 + 目标端口(2字节) + 目标IP(4字节) + 00,奇怪的是,表中的USERID部分似乎是没有用的,我参照过大量的C++代码,代码中都没有体现该部分. 至于如何转换目标端口和IP为相应的Byte数组,请自己看示例程序.消息发出后,服务器会返回信息,格式如下: +----+----+----+----+----+----+----+----+ | VN | CD | DSTPORT | DSTIP | +----+----+----+----+----+----+----+----+ 1 1 2 4 VN is the version of the reply code and should be 0. CD is the result code with one of the following values: 90: request granted -------------- 成功 91: request rejected or failed -------------- 失败 92: request rejected becasue SOCKS server cannot connect to identd on the client 93: request rejected because the client program and identd report different user-ids The remaining fields are ignored. 根据RFC的说法,代理服务器返回8字节的数据,我们只要判断第二字节是否为90即可,若是90连接成功,否则失败.剩下的操作和直连一样,Winsock可直接用SendData 和 GetData 发送\接受数据. HTTP1.1 代理的穿透 由于RFC 2616过于冗长,加上HTTP代理穿透的步骤比socks简单,这里就不详细说明了,我只给出连接的步骤和发送数据格式. 第一步仍然是用Winsock去连接代理服务器.第二步为发送请求字符,其格式为: 无用户名/密码校验 格式: "CONNECT" + 空格 + 目标连接地址 + ":" + 目标连接端口 + 空格 + "HTTP/1.1" + Chr(13) + Chr(10) + "Host:" + 空格 + 目标连接地址 + ":" + 目标连接端口 + Chr(13) + Chr(10) + Chr(13) + Chr(10) 用户名/密码验证格式: "CONNECT" + 空格 + 目标连接地址 + ":" + 目标连接端口 + 空格 + "HTTP/1.1" + Chr(13) + Chr(10) + "Host:" + 空格 + 目标连接地址 + ":" + 目标连接端口 + Chr(13) + Chr(10) + "Authorization: Basic" + 空格 + 经Base64加密过后的[用户名:密码] + Chr(13) + Chr(10) + Chr(13) + Chr(10) + "Proxy-Authorization: Basic" + 空格 + 经Base64加密过后的[用户名:密码] + Chr(13) + Chr(10) + Chr(13) + Chr(10) 发送请求完毕后,将收到代理的回应,根据RFC说明(注意 Status-Line 和 Status-Code): 6 Response After receiving and interpreting a request message, a server responds with an HTTP response message. Response = Status-Line ; Section 6.1 *(( general-header ; Section 4.5 | response-header ; Section 6.2 | entity-header ) CRLF) ; Section 7.1 CRLF [ message-body ] ; Section 7.2 6.1 Status-Line The first line of a Response message is the Status-Line, consisting of the protocol version followed by a numeric status code and its associated textual phrase, with each element separated by SP characters. No CR or LF is allowed except in the final CRLF sequence. Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF 6.1.1 Status Code and Reason Phrase The Status-Code element is a 3-digit integer result code of the attempt to understand and satisfy the request. These codes are fully defined in section 10. The Reason-Phrase is intended to give a short textual description of the Status-Code. The Status-Code is intended for use by automata and the Reason-Phrase is intended for the human user. The client is not required to examine or display the Reason- Phrase. The first digit of the Status-Code defines the class of response. The last two digits do not have any categorization role. There are 5 values for the first digit: - 1xx: Informational - Request received, continuing process - 2xx: Success - The action was successfully received, understood, and accepted - 3xx: Redirection - Further action must be taken in order to complete the request - 4xx: Client Error - The request contains bad syntax or cannot be fulfilled - 5xx: Server Error - The server failed to fulfill an apparently valid request The individual values of the numeric status codes defined for HTTP/1.1, and an example set of corresponding Reason-Phrase's, are presented below. The reason phrases listed here are only recommendations -- they MAY be replaced by local equivalents without affecting the protocol. Status-Code = "100" ; Section 10.1.1: Continue | "101" ; Section 10.1.2: Switching Protocols | "200" ; Section 10.2.1: OK | "201" ; Section 10.2.2: Created | "202" ; Section 10.2.3: Accepted | "203" ; Section 10.2.4: Non-Authoritative Information | "204" ; Section 10.2.5: No Content | "205" ; Section 10.2.6: Reset Content | "206" ; Section 10.2.7: Partial Content | "300" ; Section 10.3.1: Multiple Choices | "301" ; Section 10.3.2: Moved Permanently | "302" ; Section 10.3.3: Found | "303" ; Section 10.3.4: See Other | "304" ; Section 10.3.5: Not Modified | "305" ; Section 10.3.6: Use Proxy | "307" ; Section 10.3.8: Temporary Redirect | "400" ; Section 10.4.1: Bad Request | "401" ; Section 10.4.2: Unauthorized | "402" ; Section 10.4.3: Payment Required | "403" ; Section 10.4.4: Forbidden | "404" ; Section 10.4.5: Not Found | "405" ; Section 10.4.6: Method Not Allowed | "406" ; Section 10.4.7: Not Acceptable | "407" ; Section 10.4.8: Proxy Authentication Required | "408" ; Section 10.4.9: Request Time-out | "409" ; Section 10.4.10: Conflict | "410" ; Section 10.4.11: Gone | "411" ; Section 10.4.12: Length Required | "412" ; Section 10.4.13: Precondition Failed | "413" ; Section 10.4.14: Request Entity Too Large | "414" ; Section 10.4.15: Request-URI Too Large | "415" ; Section 10.4.16: Unsupported Media Type | "416" ; Section 10.4.17: Requested range not satisfiable | "417" ; Section 10.4.18: Expectation Failed | "500" ; Section 10.5.1: Internal Server Error | "501" ; Section 10.5.2: Not Implemented | "502" ; Section 10.5.3: Bad Gateway | "503" ; Section 10.5.4: Service Unavailable | "504" ; Section 10.5.5: Gateway Time-out | "505" ; Section 10.5.6: HTTP Version not supported | extension-code 可知,如果连接成功,服务器返回的信息是 "HTTP/" + 代理版本 + "200" + 描述("Connection established") 所以我们只要判断返回的信息是否以"http"开头,是否存在" 200 "字眼即可. 以下是关键函数的源代码: Public Function ProxyStep(ProxyType As Integer, PStep As Integer) Dim SendByte() As Byte If ProxyType = 0 Then '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ sock4代理 If PStep = 1 Then ReDim SendByte(0 To 8) As Byte SendByte(0) = 4 ' 04 SendByte(1) = 1 ' 01 SendByte(2) = Int(DestPort / 256) SendByte(3) = DestPort Mod 256 SendByte(4) = GetIPByte(1, DestIP) SendByte(5) = GetIPByte(2, DestIP) SendByte(6) = GetIPByte(3, DestIP) SendByte(7) = GetIPByte(4, DestIP) SendByte(8) = 0 '最后要以 0 结束 Form1.Winsock1.SendData SendByte() ConnStep = PStep + 1 Exit Function End If If PStep = 2 Then '代理回复,第二字节为 90 为成功,其余值为失败 If Asc(Mid(RevBuffer, 2, 1)) <> 90 Then Debug.Print Asc(Mid(RevBuffer, 2, 1)) MsgBox "连接sock4代理失败!", 48, "错误" Form1.Winsock1.Close ConnStep = 0 Exit Function Else Form1.Label8.Caption = "连接目标服务器成功!" ConnStep = -1 Form2.Show Exit Function End If End If End If '*******************下面的例子有大量重复代码,是为了让大家更清楚地了解sock5穿透过程,大家可以拿回去自己优化 ********************************** If ProxyType = 1 Then '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ sock5代理 Select Case PStep Case 1 ReDim SendByte(0 To 2) As Byte '第一步 无验证发送 05 01 00, 有验证发送 05 02 02 SendByte(0) = 5 ' 05 SendByte(1) = 1 '01 '在有用户密码验证时此字节是 1 还是 2 有诸多争论,现以腾讯QQ穿越代理模拟器时发送的数据为准,如有错误,请自己修改! SendByte(2) = IIf(Form1.Check1.Value = 0, 0, 2) '00 或 02 Form1.Winsock1.SendData SendByte() ConnStep = PStep + 1 Exit Function Case 2 '代理回复 If Asc(Mid(RevBuffer, 2, 1)) = 255 Then 'FF (255) 为失败 MsgBox "连接代理失败!", 64 Form1.Winsock1.Close ConnStep = 0 Exit Function End If If Asc(Mid(RevBuffer, 2, 1)) = 0 And Asc(Mid(RevBuffer, 1, 1)) = 5 Then '若代理回复 05 00 为无验证连接成功 Form1.Label8.Caption = "连接成功!无验证" ReDim SendByte(0 To 9) As Byte '第二步 无验证 发送连接请求 SendByte(0) = 5 SendByte(1) = 1 SendByte(2) = 0 SendByte(3) = 1 SendByte(4) = GetIPByte(1, DestIP) SendByte(5) = GetIPByte(2, DestIP) SendByte(6) = GetIPByte(3, DestIP) SendByte(7) = GetIPByte(4, DestIP) SendByte(8) = Int(DestPort / 256) '把10进制端口分成两个字节 SendByte(9) = DestPort Mod 256 '把10进制端口分成两个字节 Form1.Winsock1.SendData SendByte() ConnStep = ConnStep + 1 Exit Function End If If Asc(Mid(RevBuffer, 2, 1)) = 2 And Asc(Mid(RevBuffer, 1, 1)) = 5 Then '第二步 有用户名密码验证 成功为 05 02 Form1.Label8.Caption = "连接成功!有验证" ReDim SendByte(0 To 2 + Len(UserName) + Len(UserPassword)) As Byte SendByte(0) = 1 SendByte(1) = Len(UserName) MemCopy SendByte(2), ByVal UserName, Len(UserName) '将用户名转换 SendByte(2 + Len(UserName)) = Len(UserPassword) MemCopy SendByte(3 + Len(UserName)), ByVal UserPassword, Len(UserPassword) '将密码转换 Form1.Winsock1.SendData SendByte() ConnStep = ConnStep + 1 Exit Function End If Case 3 If Asc(Mid(RevBuffer, 2, 1)) <> 0 And Form1.Check1.Value = 1 Then '有验证,验证失败 代理回复第二字节为 00 验证成功,其余值为失败 MsgBox "sock5代理校验用户名、密码失败!", 48, "错误" Form1.Winsock1.Close ConnStep = 0 Exit Function End If If Asc(Mid(RevBuffer, 2, 1)) = 0 And Form1.Check1.Value = 1 Then '有验证,验证成功,回复值第二字节为 00 ,其余值为失败 Form1.Label8.Caption = "连接成功!有验证!" ReDim SendByte(0 To 9) As Byte '发送连接请求 SendByte(0) = 5 SendByte(1) = 1 SendByte(2) = 0 SendByte(3) = 1 SendByte(4) = GetIPByte(1, DestIP) SendByte(5) = GetIPByte(2, DestIP) SendByte(6) = GetIPByte(3, DestIP) SendByte(7) = GetIPByte(4, DestIP) SendByte(8) = Int(DestPort / 256) '把10进制端口分成两个字节 SendByte(9) = DestPort Mod 256 '把10进制端口分成两个字节 Form1.Winsock1.SendData SendByte() ConnStep = ConnStep + 1 Exit Function End If If Asc(Mid(RevBuffer, 2, 1)) = 0 And Form1.Check1.Value = 0 Then Form1.Label8.Caption = "连接目标服务器成功!" '无验证的最后一步,代理回复第二字节为 00 成功,其余值为失败 ConnStep = -1 Form2.Show Exit Function End If If Asc(Mid(RevBuffer, 2, 1)) <> 0 And Form1.Check1.Value = 0 Then MsgBox "连接目标服务器失败!", 48, "错误" '无验证的最后一步,代理回复第二字节为 00 成功,其余值为失败 ConnStep = 0 Form1.Winsock1.Close Exit Function End If Case 4 '只有有验证才会用到这一步 If Asc(Mid(RevBuffer, 2, 1)) <> 0 Then MsgBox "sock5代理连接目标服务器失败!", 48, "错误" ConnStep = 0 Form1.Winsock1.Close Exit Function Else Form1.Label8.Caption = "连接目标服务器成功!" ConnStep = -1 Form2.Show Exit Function End If End Select End If If ProxyType = 2 Then '@@@@@@@@@@@@@@@@@@@@@@@@HTTP1.1代理 If PStep = 1 Then '无用户名密码验证 If Form1.Check1.Value = 0 Then HTTPHeader = "CONNECT " & Form1.Text5.Text & ":" & Form1.Text6.Text & _ " HTTP/1.1" & Chr(13) & Chr(10) & "Host: " & Form1.Text5.Text & ":" & Form1.Text6.Text & Chr(13) & Chr(10) & Chr(13) & Chr(10) ConnStep = PStep + 1 Form1.Winsock1.SendData HTTPHeader Exit Function End If If Form1.Check1.Value = 1 Then ' 有用户名密码验证 HTTPHeader = "CONNECT " & Form1.Text5.Text & ":" & Form1.Text6.Text & _ " HTTP/1.1" & Chr(13) & Chr(10) & "Host: " & Form1.Text5.Text & ":" & _ Form1.Text6.Text & Chr(13) & Chr(10) & "Authorization: Basic " & StrtoBase64(Form1.Text3.Text & _ ":" & Form1.Text4.Text) & Chr(13) & Chr(10) & Chr(13) & Chr(10) & "Proxy-Authorization: Basic " & _ StrtoBase64(Form1.Text3.Text & ":" & Form1.Text4.Text) & Chr(13) & Chr(10) & Chr(13) & Chr(10) ' Chr(13) & Chr(10) 能否直接用vbCrLf ? 我不知道 Debug.Print HTTPHeader ConnStep = PStep + 1 Form1.Winsock1.SendData HTTPHeader Exit Function End If End If If PStep = 2 Then '代理服务器回复,格式:HTTP/[代理版本] [状态代码] [状态说明] If LCase(Left(RevBuffer, 4)) = "http" And Mid(" 200 ", 1) <> 0 Then '状态代码为 200 为成功 Form1.Label8.Caption = "连接目标服务器成功!" Form2.Show ConnStep = -1 Else MsgBox "HTTP1.1代理连接目标服务器失败!", 48, "错误" ConnStep = 0 Form1.Winsock1.Close Exit Function End If End If End If End Function
󰈣󰈤
 
 
 
>>返回首页<<
 
 热帖排行
 
 
王朝网络微信公众号
微信扫码关注本站公众号wangchaonetcn
 
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
©2005- 王朝网络 版权所有