老李分享:使用 Python 的 Socket 模块开发 UDP 扫描工具
老李分享:使用 Python 的 Socket 模块开发 UDP 扫描工具
poptest是业内唯一的测试开发工程师培训机构,测试开发工程师主要是为测试服务开发测试工具,在工作中要求你做网络级别的安全性测试,但是条件限制你无法用商业工具,所以自己动手要写测试工具,在这里我们在测试开发工程师的就业培训中构建了一个场景,就是自己开发udp扫描工具,我们在现阶段主要是用python为主要开发语言来实现各种场景下的测试,而quicktestprofessional的培训我们已经免费。
首先我们了解下概念:套接字能够访问底层网络信息。如,我们可以用它来检查IP和ICMP报头,他们都是OSI模型中网络层协议。
使用UDP数据包可以当发送信息穿越子网时,不同于TCP,需要三次握手。我们需要做的仅仅是等待ICMP报文回应,来判断对方主机是否可用或不可访问。ICMP协议本质上是一个特殊的控制协议,它可以指示错误报告和控制机器数据传输的的行为。
我们先创建一个socket 并将它绑定到一个外部网卡。这个网卡要启用混淆模式(promiscuous mode),他主要是获取经过这个网卡的所有数据包,包括目标地址不是它的数据包。使用 Windows 时要注意一点:我们需要发送一个 IOCTL 包才能将网卡设置为混淆模式。另外,虽然 linux 需要使用 ICMP,Windows 却可以以一种独立于协议的方式来嗅探收到的数据包。
import socket
import os
# host to listen
HOST = '192.168.1.114'
def sniffing(host, win, socket_prot):
while 1:
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_prot)
sniffer.bind((host, 0))
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
if win == 1:
sniffer.ioctl(socket.SIO_RCVALL, socket_RCVALL_ON)
print sniffer.recvfrom(65565)
def main(host):
if os.name == 'nt':
sniffing(host, 1, socket.IPPROTO_IP)
else:
sniffing(host, 0, socket.IPPROTO_ICMP)
if __name__ == '__main__':
main(HOST)
在终端中运行如下命令进行测试: $
sudo
python sniffer.py
('E\x00\x00T\xb3\xec\x00\x005\x01\xe4\x13J}\xe1\x11\xc0\xa8\x01r\x00\x00v\xdfx\xa2\x00\x01sr\x98T\x00\x00\x00\x008\xe3\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567', ('74.125.225.17', 0))
('E\x00\x00T\xb4\x1b\x00\x005\x01\xe3\xe4J}\xe1\x11\xc0\xa8\x01r\x00\x00~\xd7x\xa2\x00\x02tr\x98T\x00\x00\x00\x00/\xea\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567', ('74.125.225.17', 0))
我们需要对这些数据包进行解码
下面我们来研究下协议数据包组成,进行IP ICMP层解码
IP 头
典型的 IP 头有如下结构,每个字段都对应一个变量
ICMP头
ICMP 由于内容的不同其消息类型也不同,但每个消息都包括三个一致的元素:type,code (告知接收主机ICMP消息的解码类型)和 checksum。
对于我们的扫描器,如果得到的 type 和 code 值是3,这意味着 Destination Unreachable(目标不可达)和 Port Unreachable (端口不可达) ICMP 消息错误
为描述 ICMP 消息头,用 python 的 ctypes 库来创建一个类
import ctypes
class ICMP(ctypes.Structure):
_fields_ = [
('type', ctypes.c_ubyte),
('code', ctypes.c_ubyte),
('checksum', ctypes.c_ushort),
('unused', ctypes.c_ushort),
('next_hop_mtu',ctypes.c_ushort)
]
def __new__(self, socket_buffer):
return self.from_buffer_copy(socket_buffer)
def __init__(self, socket_buffer):
pass
编写消息头解码器
开发 IP/ICMP 消息头解码器。脚本创建了一个 sniffer socket,然后在循环中持续读取数据包并进行解码。
注意代码中将 IP 头的前20个字节读取到了缓存,然后再打印消息头的变量。
ICMP 头数据如下:
import socket
import os
import struct
import ctypes
from ICMPHeader import ICMP
# host to listen on
HOST = '192.168.xxx.xxx'
def main():
socket_protocol = socket.IPPROTO_ICMP
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind(( HOST, 0 ))
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
while 1:
raw_buffer = sniffer.recvfrom(65565)[0]
ip_header = raw_buffer[0:20]
iph = struct.unpack('!BBHHHBBH4s4s' , ip_header)
version_ihl = iph[0]
version = version_ihl >> 4
ihl = version_ihl & 0xF
iph_length = ihl * 4
ttl = iph[5]
protocol = iph[6]
s_addr = socket.inet_ntoa(iph[8]);
d_addr = socket.inet_ntoa(iph[9]);
print 'IP -> Version:' + str(version) + ', Header Length:' + str(ihl) + \
', TTL:' + str(ttl) + ', Protocol:' + str(protocol) + ', Source:'\
+ str(s_addr) + ', Destination:' + str(d_addr)
buf = raw_buffer[iph_length:iph_length + ctypes.sizeof(ICMP)]
icmp_header = ICMP(buf)
print "ICMP -> Type:%d, Code:%d" %(icmp_header.type, icmp_header.code) + '\n'
if __name__ == '__main__':
main()
我们运行 traceroute看下数据包情况:
$ traceroute www.google.com
traceroute to www.google.com (74.125.226.50), 30 hops max, 60 byte packets
1 * * *
2 * * *
3 67.59.255.137 (67.59.255.137) 17.183 ms 67.59.255.129 (67.59.255.129) 70.563 ms 67.59.255.137 (67.59.255.137) 21.480 ms
4 451be075.cst.lightpath.net (65.19.99.117) 14.639 ms rtr102.wan.hcvlny.cv.net (65.19.99.205) 24.086 ms 451be075.cst.lightpath.net (65.19.107.117) 24.025 ms
5 64.15.3.246 (64.15.3.246) 24.005 ms 64.15.0.218 (64.15.0.218) 23.961 ms 451be0c2.cst.lightpath.net (65.19.120.194) 23.935 ms
6 72.14.215.203 (72.14.215.203) 23.872 ms 46.943 ms *
7 216.239.50.141 (216.239.50.141) 48.906 ms 46.138 ms 46.122 ms
8 209.85.245.179 (209.85.245.179) 46.108 ms 46.095 ms 46.074 ms
9 lga15s43-in-f18.1e100.net (74.125.226.50) 45.997 ms 19.507 ms 16.607 ms
会得到这种输出 (注意 ICMP 的响应类型):
sudo python ip_header_decode.py
IP -> Version:4, Header Length:5, TTL:252, Protocol:1, Source:65.19.99.117, Destination:192.168.1.114
ICMP -> Type:11, Code:0(...)IP -> Version:4, Header Length:5, TTL:250, Protocol:1, Source:72.14.215.203, Destination:192.168.1.114
ICMP -> Type:11, Code:0
IP -> Version:4, Header Length:5, TTL:56, Protocol:1, Source:74.125.226.50, Destination:192.168.1.114
ICMP -> Type:3, Code:3
IP -> Version:4, Header Length:5, TTL:249, Protocol:1, Source:216.239.50.141, Destination:192.168.1.114
ICMP -> Type:11, Code:0(...)IP -> Version:4, Header Length:5, TTL:56, Protocol:1, Source:74.125.226.50, Destination:192.168.1.114
ICMP -> Type:3, Code:3
开发扫描器
在编写完整的扫描器前首先要安装 netaddr,它是一个用于表示和处理网络地址的 python 库。
Netaddr 提供了操作 IPv4,IPv6 和子网 Mac 等地址的能力。它非常有用,因为我们会用到子网掩码,如192.168.1.0/24$
sudo
pip
install
netaddr
我们可以使用如下的代码段来测试这个库 (成功会打印“OK”):
import
netaddr
ip
=
'192.168.xxx.xxx'
if
ip
in
netaddr.IPNetwork(
'192.168.xxx.0/24'
):
print
(
'OK!'
)
进一步强化扫描器
我们会将上面所提到的组织在一起来完成我们的扫描器,然后添加一个循环来向目标子网内的所有地址发送 UDP 数据报。
import
threading
import
time
import
socket
import
os
import
struct
from
netaddr
import
IPNetwork, IPAddress
from
ICMPHeader
import
ICMP
import
ctypes
HOST
=
'192.168.xxx.xxx'
SUBNET
=
'192.168.xxx.0/24'
MESSAGE
=
'hellooooo'
def
udp_sender(SUBNET, MESSAGE):
time.sleep(
5
)
sender
=
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for
ip
in
IPNetwork(SUBNET):
try
:
sender.sendto(MESSAGE, (
"%s"
%
ip,
65212
))
except
:
pass
def
main():
t
=
threading.Thread(target
=
udp_sender, args
=
(SUBNET, MESSAGE))
t.start()
socket_protocol
=
socket.IPPROTO_ICMP
sniffer
=
socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind(( HOST,
0
))
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL,
1
)
while
1
:
raw_buffer
=
sniffer.recvfrom(
65565
)[
0
]
ip_header
=
raw_buffer[
0
:
20
]
iph
=
struct.unpack(
'!BBHHHBBH4s4s'
, ip_header)
version_ihl
=
iph[
0
]
ihl
=
version_ihl &
0xF
iph_length
=
ihl
*
4
src_addr
=
socket.inet_ntoa(iph[
8
]);
buf
=
raw_buffer[iph_length:iph_length
+
ctypes.sizeof(ICMP)]
icmp_header
=
ICMP(buf)
# check for the type 3 and code and within our target subnet
if
icmp_header.code
=
=
3
and
icmp_header.
type
=
=
3
:
if
IPAddress(src_addr)
in
IPNetwork(SUBNET):
if
raw_buffer[
len
(raw_buffer)
-
len
(MESSAGE):]
=
=
MESSAGE:
print
(
"Host up: %s"
%
src_addr)
if
__name__
=
=
'__main__'
:
main()
$
sudo
python scanner.py
Host up: 192.168.1.114(...)
原文链接:http://www.cnblogs.com/laoli0201
老李分享:使用 Python 的 Socket 模块开发 UDP 扫描工具的更多相关文章
- Python基于socket模块实现UDP通信功能示例
Python基于socket模块实现UDP通信功能示例 本文实例讲述了Python基于socket模块实现UDP通信功能.分享给大家供大家参考,具体如下: 一 代码 1.接收端 import ...
- 老李分享:Python开发性能测试脚本
老李分享:Python开发性能测试脚本 测试开发工程师的工作主要是根据测试目标来完成,帮助测试人员完成测试目标,测试的业务需求是测试人员提出,但是由于环境的制约,手中没有性能测试工具的时候,性能测 ...
- python中socket模块详解
socket模块简介 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket.socket通常被叫做"套接字",用于描述IP地址和端口,是一个通信 ...
- python基础===socket模块的讲解(转)
一.网络知识的一些介绍 socket 是网络连接端点.例如当你的Web浏览器请求www.jb51.net上的主页时,你的Web浏览器创建一个socket并命令它去连接 www.jb51.net的Web ...
- 【Python】socket模块应用
[Socket] 本文记录了一些socket模块的简单应用,对于具体原理还没来得及深究. ■ 利用socket模块进行端口连接验证和扫描 在linux中常用nc命令来进行远端端口是否开放的验证.但是这 ...
- python之socket模块详解--小白博客
主要是创建一个服务端,在创建服务端的时候,主要步骤如下:创建socket对象socket——>绑定IP地址和端口bind——>监听listen——>得到请求accept——>接 ...
- 使用 Python 的 Socket 模块构建一个 UDP 扫描工具
译文:oschina 英文:bt3gl 当涉及到对一些目标网络的侦察时,出发点无疑是首先发现宿主主机.这个任务还可能包含嗅探和解析网络中数据包的能力. 几周前,我曾经谈到了如何使用Wireshark来 ...
- python的socket编程之udp编程
在上篇文章中,仅仅讲述了如何进行了TCP编程,在本章中,将讲述使用udp进行编码,先看如下的代码,服务器端: root@python 513]# cat serverudp.py #!/usr/bin ...
- Python的socket模块与交互式指令
socket简介 在编程的过程中,我们需要使用网络编程,这时我们不得不和网络通信的底层基础打交道了.我们必须让自己传输的数据符合网络通信的基本协议,即TCP/IP协议,但是网络通信协议本身很复杂.我们 ...
随机推荐
- C#基础笔记1
1>>数据类型: Int double:小数 char:字符型,只能存储一个字符,并且存储的这个字符要用单引号引起来.如:'a'; string:字符串,可以储存多个字符,用双引号引起来( ...
- 2017-2-23 C#基础 for循环
循环语句主要有:for;while;foreach.最常用的是for循环.for循环的四要素:初始条件:循环条件:循环体:状态改变.for循环的顺序是:初始条件--循环条件--循环体--状态改变.br ...
- PostgreSQL指南
PostgreSQL指南 历史简介 最近几年Postgres的关注度变得越来越高. 它加快了Postgres的发展步伐, 与此同时其他 的关系数据库系统的发展放缓. 在数据库领域中 Postgre S ...
- 【2017-03-05】函数基础、函数四种结构、ref和out参数、递归
一.函数基础 1.函数/方法:非常抽象独立完成某项功能的一个个体 2.函数的作用: 提高代码的重用性提高功能开发的效率提高程序代码的可维护性 3.分类 固定功能函数高度抽象函数 4.函数四要素:输入, ...
- Spark性能优化之道——解决Spark数据倾斜(Data Skew)的N种姿势
原创文章,同步首发自作者个人博客转载请务必在文章开头处注明出处. 摘要 本文结合实例详细阐明了Spark数据倾斜的几种场景以及对应的解决方案,包括避免数据源倾斜,调整并行度,使用自定义Partitio ...
- jQ试题的总结
jQuery习题的一些总结 1.在div元素中,包含了一个<span>元素,通过has选择器获取<div>元素中的<span>元素的语法是? 提示使用has $ ...
- progID
ProgID程序员给CLSID指定的容易记住的名字ProgID命名约定:<Program>.<Component>.<Version>AppID:将某个APPID( ...
- Java调度框架Quartz简单示例
Quartz的大名如雷贯耳,这里就不赘述,而且本文也不作为深入探讨,只是看完Quartz的官方文档后,下个简单示例,至少证明曾经花了点时间学习过,以备不时之需. Quartz使用了SLF4J,所以至少 ...
- observe.js 源码 学习笔记
/** * observejs --- By dnt http://kmdjs.github.io/ * Github: https://github.com/kmdjs/observejs * MI ...
- Docker存储驱动之ZFS简介
ZFS是下一代的文件系统,支持了很多存储高级特性,如卷管理.快照.和校验.压缩和重复删除技术.拷贝等. ZFS由Sun公司创建,现属于Oracle,ZFS是开源的,并基于CDDL license.因为 ...