Merge pull request #1769 from mhils/update-pydivert

update pydivert, fix #1749
This commit is contained in:
Maximilian Hils 2016-11-21 14:40:11 +01:00 committed by GitHub
commit 92516a3b5c
2 changed files with 44 additions and 42 deletions

View File

@ -8,8 +8,8 @@ import threading
import time import time
import configargparse import configargparse
from pydivert import enum import pydivert
from pydivert import windivert import pydivert.consts
import pickle import pickle
import socketserver import socketserver
@ -149,7 +149,7 @@ class TransparentProxy:
Limitations: Limitations:
- No IPv6 support. (Pull Requests welcome) - No IPv6 support. (Pull Requests welcome)
- TCP ports do not get re-used simulateously on the client, i.e. the proxy will fail if application X - TCP ports do not get re-used simultaneously on the client, i.e. the proxy will fail if application X
connects to example.com and example.org from 192.168.0.42:4242 simultaneously. This could be mitigated by connects to example.com and example.org from 192.168.0.42:4242 simultaneously. This could be mitigated by
introducing unique "meta-addresses" which mitmproxy sees, but this would remove the correct client info from introducing unique "meta-addresses" which mitmproxy sees, but this would remove the correct client info from
mitmproxy. mitmproxy.
@ -197,13 +197,10 @@ class TransparentProxy:
self.api_thread = threading.Thread(target=self.api.serve_forever) self.api_thread = threading.Thread(target=self.api.serve_forever)
self.api_thread.daemon = True self.api_thread.daemon = True
self.driver = windivert.WinDivert()
self.driver.register()
self.request_filter = custom_filter or " or ".join( self.request_filter = custom_filter or " or ".join(
("tcp.DstPort == %d" % ("tcp.DstPort == %d" %
p) for p in redirect_ports) p) for p in redirect_ports)
self.request_forward_handle = None self.request_forward_handle = None # type: pydivert.WinDivert
self.request_forward_thread = threading.Thread( self.request_forward_thread = threading.Thread(
target=self.request_forward) target=self.request_forward)
self.request_forward_thread.daemon = True self.request_forward_thread.daemon = True
@ -212,18 +209,18 @@ class TransparentProxy:
self.trusted_pids = set() self.trusted_pids = set()
self.tcptable2 = MIB_TCPTABLE2(0) self.tcptable2 = MIB_TCPTABLE2(0)
self.tcptable2_size = ctypes.wintypes.DWORD(0) self.tcptable2_size = ctypes.wintypes.DWORD(0)
self.request_local_handle = None self.request_local_handle = None # type: pydivert.WinDivert
self.request_local_thread = threading.Thread(target=self.request_local) self.request_local_thread = threading.Thread(target=self.request_local)
self.request_local_thread.daemon = True self.request_local_thread.daemon = True
# The proxy server responds to the client. To the client, # The proxy server responds to the client. To the client,
# this response should look like it has been sent by the real target # this response should look like it has been sent by the real target
self.response_filter = "outbound and tcp.SrcPort == %d" % proxy_port self.response_filter = "outbound and tcp.SrcPort == %d" % proxy_port
self.response_handle = None self.response_handle = None # type: pydivert.WinDivert
self.response_thread = threading.Thread(target=self.response) self.response_thread = threading.Thread(target=self.response)
self.response_thread.daemon = True self.response_thread.daemon = True
self.icmp_handle = None self.icmp_handle = None # type: pydivert.WinDivert
@classmethod @classmethod
def setup(cls): def setup(cls):
@ -241,25 +238,33 @@ class TransparentProxy:
# Block all ICMP requests (which are sent on Windows by default). # Block all ICMP requests (which are sent on Windows by default).
# In layman's terms: If we don't do this, our proxy machine tells the client that it can directly connect to the # In layman's terms: If we don't do this, our proxy machine tells the client that it can directly connect to the
# real gateway if they are on the same network. # real gateway if they are on the same network.
self.icmp_handle = self.driver.open_handle( self.icmp_handle = pydivert.WinDivert(
filter="icmp", filter="icmp",
layer=enum.Layer.NETWORK, layer=pydivert.Layer.NETWORK,
flags=enum.Flag.DROP) flags=pydivert.Flag.DROP
)
self.icmp_handle.open()
self.response_handle = self.driver.open_handle( self.response_handle = pydivert.WinDivert(
filter=self.response_filter, filter=self.response_filter,
layer=enum.Layer.NETWORK) layer=pydivert.Layer.NETWORK
)
self.response_handle.open()
self.response_thread.start() self.response_thread.start()
if self.mode == "forward" or self.mode == "both": if self.mode == "forward" or self.mode == "both":
self.request_forward_handle = self.driver.open_handle( self.request_forward_handle = pydivert.WinDivert(
filter=self.request_filter, filter=self.request_filter,
layer=enum.Layer.NETWORK_FORWARD) layer=pydivert.Layer.NETWORK_FORWARD
)
self.request_forward_handle.open()
self.request_forward_thread.start() self.request_forward_thread.start()
if self.mode == "local" or self.mode == "both": if self.mode == "local" or self.mode == "both":
self.request_local_handle = self.driver.open_handle( self.request_local_handle = pydivert.WinDivert(
filter=self.request_filter, filter=self.request_filter,
layer=enum.Layer.NETWORK) layer=pydivert.Layer.NETWORK
)
self.request_local_handle.open()
self.request_local_thread.start() self.request_local_thread.start()
def shutdown(self): def shutdown(self):
@ -272,17 +277,16 @@ class TransparentProxy:
self.icmp_handle.close() self.icmp_handle.close()
self.api.shutdown() self.api.shutdown()
def recv(self, handle): def recv(self, handle: pydivert.WinDivert) -> pydivert.Packet:
""" """
Convenience function that receives a packet from the passed handler and handles error codes. Convenience function that receives a packet from the passed handler and handles error codes.
If the process has been shut down, (None, None) is returned. If the process has been shut down, (None, None) is returned.
""" """
try: try:
raw_packet, metadata = handle.recv() return handle.recv()
return self.driver.parse_packet(raw_packet), metadata
except WindowsError as e: except WindowsError as e:
if e.winerror == 995: if e.winerror == 995:
return None, None return None
else: else:
raise raise
@ -306,7 +310,7 @@ class TransparentProxy:
def request_local(self): def request_local(self):
while True: while True:
packet, metadata = self.recv(self.request_local_handle) packet = self.recv(self.request_local_handle)
if not packet: if not packet:
return return
@ -321,49 +325,47 @@ class TransparentProxy:
pid = self.addr_pid_map.get(client, None) pid = self.addr_pid_map.get(client, None)
if pid not in self.trusted_pids: if pid not in self.trusted_pids:
self._request(packet, metadata) self._request(packet)
else: else:
self.request_local_handle.send((packet.raw, metadata)) self.request_local_handle.send(packet, recalculate_checksum=False)
def request_forward(self): def request_forward(self):
""" """
Redirect packages to the proxy Redirect packages to the proxy
""" """
while True: while True:
packet, metadata = self.recv(self.request_forward_handle) packet = self.recv(self.request_forward_handle)
if not packet: if not packet:
return return
self._request(packet, metadata) self._request(packet)
def _request(self, packet, metadata): def _request(self, packet: pydivert.Packet):
# print(" * Redirect client -> server to proxy") # print(" * Redirect client -> server to proxy")
# print("%s:%s -> %s:%s" % (packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)) # print("%s:%s -> %s:%s" % (packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port))
client = (packet.src_addr, packet.src_port) client = (packet.src_addr, packet.src_port)
server = (packet.dst_addr, packet.dst_port) server = (packet.dst_addr, packet.dst_port)
if client in self.client_server_map: if client in self.client_server_map:
# Force re-add to mark as "newest" entry in the dict. self.client_server_map.move_to_end(client)
del self.client_server_map[client] else:
while len(self.client_server_map) > self.connection_cache_size: while len(self.client_server_map) > self.connection_cache_size:
self.client_server_map.popitem(False) self.client_server_map.popitem(False)
self.client_server_map[client] = server self.client_server_map[client] = server
packet.dst_addr, packet.dst_port = self.proxy_addr, self.proxy_port packet.dst_addr, packet.dst_port = self.proxy_addr, self.proxy_port
metadata.direction = enum.Direction.INBOUND packet.direction = pydivert.consts.Direction.INBOUND
packet = self.driver.update_packet_checksums(packet)
# Use any handle thats on the NETWORK layer - request_local may be # Use any handle thats on the NETWORK layer - request_local may be
# unavailable. # unavailable.
self.response_handle.send((packet.raw, metadata)) self.response_handle.send(packet)
def response(self): def response(self):
""" """
Spoof source address of packets send from the proxy to the client Spoof source address of packets send from the proxy to the client
""" """
while True: while True:
packet, metadata = self.recv(self.response_handle) packet = self.recv(self.response_handle)
if not packet: if not packet:
return return
@ -373,11 +375,11 @@ class TransparentProxy:
server = self.client_server_map.get(client, None) server = self.client_server_map.get(client, None)
if server: if server:
packet.src_addr, packet.src_port = server packet.src_addr, packet.src_port = server
packet.recalculate_checksums()
else: else:
print("Warning: Previously unseen connection from proxy to %s:%s." % client) print("Warning: Previously unseen connection from proxy to %s:%s." % client)
packet = self.driver.update_packet_checksums(packet) self.response_handle.send(packet, recalculate_checksum=False)
self.response_handle.send((packet.raw, metadata))
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -87,7 +87,7 @@ setup(
], ],
extras_require={ extras_require={
':sys_platform == "win32"': [ ':sys_platform == "win32"': [
"pydivert>=0.0.7, <2.0", "pydivert>=2.0.3, <2.1",
], ],
':sys_platform != "win32"': [ ':sys_platform != "win32"': [
], ],