Created
August 22, 2016 04:48
-
-
Save ysc3839/aa0f424b0fb4c8b70a612b16663b4a38 to your computer and use it in GitHub Desktop.
WallBreaker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
import sys, socket, select, SocketServer, struct, logging | |
from time import sleep | |
logging.basicConfig(level=logging.INFO) | |
BUFLEN = 1024 | |
SLEEPTIME = 0.4 | |
patterns = [ | |
('.apk ', 2), | |
('\r\nHost:', 4), | |
('weixin.qq.com', 3), | |
('.qq.com', 2), | |
('file.ws.126.net', 10), | |
('s1.music.126.net', 12), | |
('.', 2), | |
('userscripts-mirror', 4), | |
(':',1) | |
] | |
# (pattern, offset) | |
def splitHttp(data): | |
list = [] | |
for pattern in patterns: | |
i = data.find(pattern[0]) | |
if i != -1: | |
list.append(data[:i + pattern[1]]) | |
data = data[i + pattern[1]:] | |
list.append(data) | |
return list | |
def unpack(self, fmt): | |
u'''解包 | |
block 是否阻塞 | |
即使非阻塞模式是标准的 recv ,也会阻塞。 | |
阻塞模式会完全阻塞,直到连接关闭或连接超时。 | |
''' | |
length = struct.calcsize(fmt) | |
data = self.recv(length) | |
if len(data) < length: | |
raise Exception("sock.unpack: bad formatted stream") | |
return struct.unpack(fmt, data) | |
def pack(self, fmt, *args): | |
data = struct.pack(fmt, *args) | |
return self.sendall(data) | |
setattr(socket.SocketType, 'unpack', unpack) | |
setattr(socket.SocketType, 'pack', pack) | |
class Socks5Handler(SocketServer.BaseRequestHandler): | |
u'''SOCKS4 SOCKS5 双协议支持 From TcpRoute https://github.com/GameXG/TcpRoute''' | |
def __init__(self, request, client_address, server): | |
self.sock = request | |
SocketServer.BaseRequestHandler.__init__(self, request, client_address, server) | |
def handle(self): | |
(ver,) = self.sock.unpack('B') | |
self.ver = ver | |
if ver == 0x04: | |
self.socks4Handle() | |
elif ver == 0x05: | |
self.socks5Handle() | |
else: | |
logging.error(u'[Socks5Handler]异常的协议类型, ver = %u' % (ver)) | |
self.sock.close() | |
return | |
def socks4Handle(self): | |
logging.info(u'SOCKS4连接 %s:%u' % self.client_address) | |
cd,port,dstip = self.sock.unpack('!BHI') | |
if cd != 1: | |
# 1 == 连接, 2 == 绑定 | |
self.sock.pack('!BBHI',0,91,port,dstip) | |
self.sock.close() | |
# userid = self.sock.readline(end='\0')[:-1] | |
while self.sock.recv(1) != '\0': | |
pass | |
hostname = socket.inet_ntoa(struct.pack('!I', dstip)) | |
# if hostname.startswith('0.0.0.'): | |
# SOCKS4a 支持 ,chrome 不支持 socks4a 扩展。 | |
# hostname = self.sock.readline(end='\0')[:-1] | |
self.hostname = hostname | |
self.port = port | |
# 对外发起请求 | |
try: | |
remote_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
remote_sock.connect((hostname, port)) | |
except: | |
logging.exception(u'所有线路连接 TCP Host:%s Port:%s 失败' % (hostname, port)) | |
# 按照socks4协议,这里返回和请求相同的ip和端口 | |
# https://zh.wikipedia.org/wiki/SOCKS | |
self.sock.pack('!BBHI', 0, 91, port, dstip) | |
self.sock.close() | |
return | |
logging.info(u'连接 TCP目标 Host:%s Port:%u 建立成功。' % (hostname, port)) | |
# 按照socks4协议,这里返回和请求相同的ip和端口 | |
# https://zh.wikipedia.org/wiki/SOCKS | |
self.sock.pack('!BBHI', 0, 90, port, dstip) | |
self.forward(self.sock,remote_sock) | |
def socks5Handle(self): | |
logging.info(u'SOCKS5连接 %s:%u' % self.client_address) | |
# 鉴定 | |
(nmethods,) = self.sock.unpack('B') | |
if nmethods > 0: | |
(methods,) = self.sock.unpack('B' * nmethods) | |
# TODO: 未检查客户端支持的鉴定方式 | |
self.sock.pack('BB', 0x05, 0x00) | |
logging.debug(u'[Socks5Handler]client login') | |
# 接收代理请求 | |
ver, cmd, rsv, atyp = self.sock.unpack('BBBB') | |
if ver != 0x05 or cmd != 0x01: | |
logging.warn(u'[Socks5Handler]收到未知类型的请求,关闭连接 ver=%s, cmd=%s' % (ver, cmd)) | |
self.sock.pack('BBBBIH', 0x05, 0x07, 0x00, 0x01, 0, 0) | |
self.sock.close() | |
return | |
sock_family = socket.AF_INET | |
if atyp == 0x01: | |
# IPv4 | |
host, port = self.sock.unpack('!IH') | |
hostname = socket.inet_ntoa(struct.pack('!I', host)) | |
elif atyp == 0x03: | |
# 域名 | |
length = self.sock.unpack('B')[0] | |
hostname, port = self.sock.unpack("!%usH" % length) | |
elif atyp == 0x04: | |
# IPv6 | |
sock_family = socket.AF_INET6 | |
ipv61, ipv62, port = self.sock.unpack('!2QH') | |
hostname = socket.inet_ntop(socket.AF_INET6, struct.pack('!2Q', ipv61, ipv62)) | |
else: | |
logging.warn(u'[SClient]收到未知的目的地址类型,关闭连接。 atyp=%s ' % (atyp)) | |
self.sock.pack('!BBBBIH', 0x05, 0x07, 0x00, 0x01, 0, 0) | |
self.sock.close() | |
return | |
logging.debug(u'[SClient] Host:%s Port:%s' % (hostname, port)) | |
self.hostname = hostname | |
self.port = port | |
# 对外发起请求 | |
try: | |
remote_sock = socket.socket(sock_family, socket.SOCK_STREAM) | |
remote_sock.connect((hostname, port)) | |
except: | |
logging.exception(u'所有线路连接 TCP Host:%s Port:%s 失败' % (hostname, port)) | |
# TODO: 按照SOCKS5协议,这里应该返回服务器绑定的地址及端口 | |
# http://blog.csdn.net/testcs_dn/article/details/7915505 | |
self.sock.pack('!BBBBIH', 0x05, 0x03, 0x00, 0x01, 0, 0) | |
self.sock.close() | |
return | |
logging.info(u'连接 TCP目标 Host:%s Port:%u 建立成功。' % (hostname, port)) | |
# TODO: 按照SOCKS5协议,这里应该返回服务器绑定的地址及端口 | |
# http://blog.csdn.net/testcs_dn/article/details/7915505 | |
self.sock.pack('!BBBBIH', 0x05, 0x00, 0x00, 0x01, 0, 0) | |
self.forward(self.sock,remote_sock) | |
def forward(self, sock, remote): | |
fdset = [sock, remote] | |
while True: | |
r, w, e = select.select(fdset, [], []) | |
if len(e) != 0: | |
sock.close() | |
remote.close() | |
return | |
if sock in r: | |
buf = sock.recv(BUFLEN) | |
if True: | |
datalist = splitHttp(buf) | |
logging.info(datalist) | |
for data in datalist: | |
remote.sendall(data) | |
sleep(SLEEPTIME) | |
elif remote.send(buf) <= 0: break | |
if remote in r: | |
buf = remote.recv(BUFLEN) | |
i = buf[:512].find('Location: http://172.16.100.2/disable/') | |
j = buf[:512].find('setURL("172.16.100.2")') | |
if i == -1 and j == -1: | |
if sock.send(buf) <= 0: break | |
else: | |
logging.info('BLOCKED!!!') | |
sock.send('HTTP/1.0 403 Forbidden\r\n\r\nBLOCKED!!!') | |
sock.close() | |
remote.close() | |
return | |
def main(): | |
port = 1080 | |
server = SocketServer.ThreadingTCPServer(('127.0.0.1', port), Socks5Handler) | |
logging.info('Starting server at 127.0.0.1:%u.' % port) | |
try: | |
server.serve_forever() | |
except socket.error as e: | |
if e.errno == 10048: | |
logging.error(u'端口 %u 已被使用.' % port) | |
except KeyboardInterrupt: | |
logging.info('Server shutting down.') | |
exit(0) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment