有很多理由让你的工具箱中保留一个TCP代理,它不仅可以将流量从一个主机转发给另一个主机,而且可以评估基于网路的软件。
——Python 黑帽子:黑客与渗透测试编程之道
目录
一、TCP proxy源码
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys
import socket
import threading
# 16进制处理函数
def hexdump(src, length=16):
result = []
digits = 4 if isinstance(src, unicode) else 2
for i in xrange(0, len(src), length):
s = src[i:i+length]
hexa = b' '.join(["%0*X" % (digits, ord(x)) for x in s])
text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s])
result.append( b"%04X %-*s %s" % (i, length*(digits + 1), hexa, text) )
print b'\n'.join(result)
#数据接收
def receive_from(connection):
buffer = ""
#发送2秒延迟
connection.settimeout(2)
try:
# 从缓冲区里读数据,直到没有数据或超时
while True:
data = connection.recv(4096)
if not data:
break
buffer += data
except:
pass
return buffer
# 请求处理
def request_handler(buffer):
#可以添加修改操作代码
return buffer
# 响应处理
def response_handler(buffer):
# 可以添加修改操作代码
return buffer
def proxy_handler(client_socket, remote_host, remote_port, receive_first):
# 连接远程主机
remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remote_socket.connect((remote_host,remote_port))
# 从远程主机接收数据
if receive_first:
remote_buffer = receive_from(remote_socket)
hexdump(remote_buffer)
# 发送到响应处理函数
remote_buffer = response_handler(remote_buffer)
# 如果有数据给本地客户端,发送它
if len(remote_buffer):
print "[<==] Sending %d bytes to localhost." % len(remote_buffer)
client_socket.send(remote_buffer)
#从本地循环读取数据,发送给远程客户端和本地
while True:
# 从本地读取
local_buffer = receive_from(client_socket)
if len(local_buffer):
print "[==>] Received %d bytes from localhost." % len(local_buffer)
hexdump(local_buffer)
# 发送给响应处理函数
local_buffer = request_handler(local_buffer)
#发送给远程主机
remote_socket.send(local_buffer)
print "[==>] Sent to remote."
# 接收响应信息
remote_buffer = receive_from(remote_socket)
if len(remote_buffer):
print "[<==] Received %d bytes from remote." % len(remote_buffer)
hexdump(remote_buffer)
# 发送给本地响应处理
remote_buffer = response_handler(remote_buffer)
# 将响应发送给本地socket
client_socket.send(remote_buffer)
print "[<==] Sent to localhost."
# 两边都没有数据,关闭连接
if not len(local_buffer) or not len(remote_buffer):
client_socket.close()
remote_socket.close()
print "[*] No more data. Closing connections."
break
def server_loop(local_host,local_port,remote_host,remote_port,receive_first):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
server.bind((local_host,local_port))
except:
print "[!!] Failed to listen on %s:%d" % (local_host,local_port)
print "[!!] Check for other listening sockets or correct permissions."
sys.exit(0)
print "[*] Listening on %s:%d" % (local_host,local_port)
server.listen(5)
while True:
client_socket, addr = server.accept()
#打印本地连接信息
print "[==>] Received incoming connection from %s:%d" % (addr[0],addr[1])
# 创建一个线程去连接远程主机
proxy_thread = threading.Thread(target=proxy_handler,args=(client_socket,remote_host,remote_port,receive_first))
proxy_thread.start()
def main():
if len(sys.argv[1:]) != 5:
print "Usage: ./proxy.py [localhost] [localport] [remotehost] [remoteport] [receive_first]"
print "Example: ./proxy.py 127.0.0.1 9000 10.12.132.1 9000 True"
sys.exit(0)
# 设置本地监听参数
local_host = sys.argv[1]
local_port = int(sys.argv[2])
# 设置远程目标
remote_host = sys.argv[3]
remote_port = int(sys.argv[4])
# 告诉代理在发送给远程主机之前连接和接收数据
receive_first = sys.argv[5]
if "True" in receive_first:
receive_first = True
else:
receive_first = False
# 设置好监听
server_loop(local_host,local_port,remote_host,remote_port,receive_first)
main()
二、proxy源码精讲
2.1 整体框架
2.2 main函数
2.3 server_loop函数
2.4 proxy_handler
三、涉及重要知识整理
3.1 网络编程
Python 提供了两个级别访问的网络服务:
- 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法。
- 高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。
Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求:
格式:socket.socket([family[, type[, protocal]]])
参数
family: 套接字家族可以使AF_UNIX或者AF_INET
type: 套接字类型可以根据是面向连接的还是非连接分为SOCK_STREAM或SOCK_DGRAM
protocol: 一般不填默认为0
函数
3.2 线程编程
Python创建Thread对象语法如下:
import threading
threading.Thread(target=None, name=None, args=())
参数
target 是函数名字,需要调用的函数。
name 设置线程名字。
args 函数需要的参数,以元祖( tuple)的形式传入
函数
run(): 用以表示线程活动的方法。
start():启动线程活动。
join(): 等待至线程中止。
isAlive(): 返回线程是否活动的。
getName(): 返回线程名。
setName(): 设置线程名。
四、总结
有志者自有千计万计,无志者只感千难万难。