僞造 UDP 包源 IP 地址

Raw sockets 方式

raw socket 可經過參數  IPV6_HDRINCL 或 IP_HDRINCL 自定義IP頭——僞造UDP報文源IP就全靠它了。python

限制:從xp sp2以後的全部非服務器版本的windows,都不能發送TCP,而且也不能僞造UDP的源地址。linux

這裏使用 dpkt 作 UDP/IP 包的組裝:編程

class SyslogSenderRawScoket:
    def __init__(self, dst, dport, src, sport = 10000):
        self.dst = socket.gethostbyname(dst)
        self.dport = dport
        self.src = src
        self.sport = sport
        
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
        self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
        self.sock.connect((self.dst, 1))
        
    def Send(self, ip_packet):
        self.sock.sendall(str(ip_packet))

    def Process(self, msg):
        u = dpkt.udp.UDP()
        u.sport = self.sport
        u.dport = self.dport
        u.data = msg
        u.ulen = len(u)
    
        # IP 的 str 會觸發 IP 的校驗和計算,也會觸發 TCP UDP 的校驗和計算
        # TCP/UDP的校驗和是: 源IP,目的IP,協議,TCP或UDP包(頭+內容)
        #u.sum = ?
    
        i = dpkt.ip.IP(data = u)
        #i.off = dpkt.ip.IP_DF # frag off
        i.p = dpkt.ip.IP_PROTO_UDP
        i.src = socket.inet_aton(self.src) # xp sp2以後 禁止發送非本機IP地址的數據包;linux, server無限制
        i.dst = socket.inet_aton(self.dst)
        i.len = len(i)
        
        self.Send(i)


Winpcap 方式

對比 raw sockets 方式編程更復雜,但能夠本身構造鏈路層的包。windows

class SyslogSenderWinPcap:
    '''
    原理:先產生一個ping包,以獲取本機、網關的MAC地址,而後經過WinPcap發包
    限制:目前不能發送給本機,但能夠實現;僅支持數據鏈路層爲以太網的狀況
    '''
    
    def __init__(self, dst, dport, src = None, sport = 10000):

        self.dst = dst
        self.dport = dport
        self.src = src
        self.sport = sport
        
        self.errbuf= ctypes.create_string_buffer(winpcapy.PCAP_ERRBUF_SIZE)
        
        interface = self.ChooseDevice()

        self.fp = winpcapy.pcap_open_live(interface, 65536, winpcapy.PCAP_OPENFLAG_PROMISCUOUS, 1000, self.errbuf)
        
        if not self.fp:
            print 'Fatal error: open interface %s failed' % interface
            exit(-1)
            
        if winpcapy.pcap_datalink(self.fp) != winpcapy.pcap_datalink_name_to_val('EN10MB'):
            print 'Fatal error: unsupported datalink layer'
            exit(-1)
        
        self.GetEthernetHeader()
        
    def ChooseDevice(self):
        interface = ctypes.POINTER(winpcapy.pcap_if_t)()
        if -1 == winpcapy.pcap_findalldevs(ctypes.byref(interface), self.errbuf):
            print 'Fatal error: no device'
            exit(-1)
            
        alldevs = []
        while interface:
            alldevs.append((interface.contents.name, interface.contents.description))
            
            interface = interface.contents.next
           
        while True: 
            index = 0
            for dev in alldevs:
                index += 1
                print '%d.' % index,
                print '%s %s' % (dev)
                
            selected = raw_input('Enter the interface number (1-%d):' % index)
            
            try:
                index = int(selected)
            except TypeError:
                print 'Integer expect'
                continue
            
            if index < 1 or index > len(alldevs):
                print 'Too big or too small'
                continue
            
            return alldevs[index - 1][0]
        
    # 發送IP包
    def Send(self, ip_packet):
        e = dpkt.ethernet.Ethernet(
            data = ip_packet,
            dst = self.dst_mac,
            src = self.src_mac)
        
        to_send = str(e)
        
        buf = (ctypes.c_ubyte * len(to_send))(*map(ord, to_send))
        winpcapy.pcap_sendpacket(self.fp, buf, len(buf))
        
    def GetEthernetHeader(self):
        # 抓包過濾
        bpf = ctypes.pointer(winpcapy.bpf_program())
        winpcapy.pcap_compile(self.fp, bpf, 'icmp and host %s' % self.dst, 1, 0)
        winpcapy.pcap_setfilter(self.fp, bpf)
        
        # 抓包回調
        def _packet_handler(param, header, pkt_data):
            s= ''.join([chr(b) for b in pkt_data[:header.contents.len]])
            e = dpkt.ethernet.Ethernet(s)
            
            # 獲取本機MAC和網關MAC
            self.dst_mac = e.dst
            self.src_mac = e.src
        
        packet_handler = winpcapy.pcap_handler(_packet_handler)
        
        # 產生一個ping包
        def ToGenPing():
            self.GenPing(self.dst)
        
        t = threading.Timer(0.5, ToGenPing)
        t.start() # 輸出ping包,以便得到以太網包頭

        winpcapy.pcap_loop(self.fp, 1, packet_handler, None)

    def GenPing(self, dst):
        import random
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
            
            icmp = dpkt.icmp.ICMP(
                type=8, data=dpkt.icmp.ICMP.Echo(id=random.randint(0, 0xffff),
                                                     seq=1, data='Hello'))
            #sock.connect((ip, 1))
            #sock.sendall(str(icmp))
    
            sock.sendto(str(icmp), (dst, 1))
        except socket.error, e:
            print 'Fatal error: (%d) %s' % (e.errno, e.message)
            exit(-1)
        finally:
            sock.close()
        
    
    def MacAddress(self, s):
        return struct.pack('BBBBBB', *[int(i, 16) for i in s.split('-')])
        
    def Process(self, msg):
        u = dpkt.udp.UDP()
        u.sport = self.sport
        u.dport = self.dport
        u.data = msg
        u.ulen = len(u)
    
        #print u.sum
    
        i = dpkt.ip.IP(data = u)
        #i.off = dpkt.ip.IP_DF # frag off
        i.p = dpkt.ip.IP_PROTO_UDP
        i.src = socket.inet_aton(self.src) 
        i.dst = socket.inet_aton(self.dst)
        i.len = len(i)
        
        self.Send(i)
        

 

測試代碼:服務器

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import socket
import time
import threading
import ctypes
import struct

import dpkt
import winpcapy



if __name__ == '__main__':
    #sender = SyslogSenderRawScoket('192.168.12.10', 514, '192.168.128.1')
    sender = SyslogSenderWinPcap('192.168.12.17', 514, '192.168.12.1')
    
    start = time.time()
    
    # 只能達到 1000條/秒
    for i in xrange(5000):
        sender.Process('some udp content with ip header')
    
    print time.time() - start
相關文章
相關標籤/搜索