首页 > 解决方案 > 将 PyQt5 WebEngine 请求转换为 IP 数据包,反之亦然

问题描述

使用 PyQt5,我已经成功设置了一个 QWebEngineUrlRequestInterceptor 子类,我可以修改 web 请求的 headers 和 data。我正在为类似 VPN 的应用程序构建它,其中请求被转换为数据包(使用 scapy 的IP()对象或类似模块发送),加密并发送到另一个地址,该地址将解密并将数据包数据转换回 QWebEngine 请求. 我的问题是如何将使用 PyQt5 拦截的 Web 请求转换为 IP 数据包格式,反之亦然?

这是拦截器代码:

#Custom url interceptor for modifying headers
#and the url before sending it on
class UrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
  def __init__(self,parent=None):
    super(QWebEngineUrlRequestInterceptor,self).__init__(parent)

  def interceptRequest(self,info):
    #Modify the actual request here - todo: block the
    #request here, packet simulate etc...
    info.setHttpHeader(b"Accept-Language",InterClassVariables.languageCode.encode())

标签: pythonpyqtpyqt5interceptorpacket

解决方案


问题是QWebEngineUrlRequestInterceptor当您需要 IP 信息(第 3 层)时,它与 HTTP(第 7 层)交互。QWebEngineUrlRequestInfo没有任何 IP 属性(同样位于第 7 层)。

虽然可以使用 Qt 的QHostInfo,但使用普通 Python 更简单。在这里,我们使用netifaces。虽然它不在标准库中,但替代方案更复杂、依赖于平台或需要 Internet 连接。

import socket

from PyQt5.Qt import QUrl
from PyQt5.Qt import QWebEngineUrlRequestInterceptor
from PyQt5.Qt import QWebEngineUrlRequestInfo
import netifaces
from scapy.all import IP

#Custom url interceptor for modifying headers
#and the url before sending it on
class UrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
  def __init__(self,parent=None):
    super(QWebEngineUrlRequestInterceptor,self).__init__(parent)
    self.info = None

  def interceptRequest(self, info: QWebEngineUrlRequestInfo):
    #Modify the actual request here - todo: block the
    #request here, packet simulate etc...
    self.info = info
    self.info.setHttpHeader(b"Accept-Language",
      InterClassVariables.languageCode.encode())
    self.sendPacket()

  def getDestIp(self):
    qUrl = self.info.requestURL()
    url = qUrl.toString()
    dest_ip = socket.gethostbyname(url)
    return dest_ip

  def sendPacket()
    src_ip = netifaces.gateways()['default'][netifaces.AF_INET][0]
    dest_ip = get_dest_ip(self.info)
    ip_layer = IP(src=src_ip, dst=dest_ip)
    other_layers = OTHER_PROTOCOLS() # FIXME
    pkt = ip_layer/other_layers
    send(pkt) # Choose whichever send function is most appropriate here

如果我们打印数据包(没有其他层),我们应该得到类似这样的东西:

$ python project.py -show-packet # Pseudo cli
###[ IP ]###
  version   = 4
  ihl       = None
  tos       = 0x0
  len       = None
  id        = 1
  flags     =
  frag      = 0
  ttl       = 64
  proto     = ip
  chksum    = None
  src       = 172.31.98.1
  dst       = 151.101.65.69
  \options   \


推荐阅读