基于ICMP的反弹式木马研究与实现

基于ICMP的反弹式木马研究与实现

ICMP木马技术最初是为了摆脱端口的束缚而出现,与TCP和UDP协议不同,ICMP并没有端口字段,因为ICMP报文是通过系统内核或进程直接进行。这样,木马的服务器端和木马的客户端的通信采用ICMP协议时,木马的服务器不用开方任何端口。一般意义上的ICMP木马,其实就类似与一个Ping的过程。
传统的木马都是利用一个TCP端口来监听控制端的连接,一旦控制端认证通过,攻击者便会对服务器进行非法控制,但这种方式的隐蔽性非常弱。因此有人提出了反弹窗口式木马,也就是服务器主动向被控制端发送连接请求。

一:ICMP通信的实现
1.1原始套接字的工作原理与规则:
原始套接字是一个特殊的套接字类型,他的创建方式跟TCP/UDP的创建方法几乎类似:
SOCKET socket=socket(AF_INET,SOCK_RAW,IPPRPTO_ICMP)//这说明创建的是一个基于ICMP协议的原始套接字。原始套接字能够访问传输层以下的数据,也就是说,你能够实现上至应用层的数据操作,下至链路层的数据操作。
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))//用这个方式创建的原始套接字就能够直接读取链路层的数据。

(1):对于UDP/TCP产生的IP数据包,内核不将它传递给任何原始套接字,而只是将这些数据交给对应的UDP/TCP数据处理句柄(所以,如果你想要通过原始套接字来访问TCP/UDP或
者其它类型的数据,调用socket函数创建原始套接字第三个参数应该指定为htons(ETH_P_IP),
也就是通过直接访问数据链路层来实现.
(2):对于ICMP和EGP等使用IP数据包承载数据但又在传输层之下的协议类型的IP
数据包,内核不管是否已经有注册了的句柄来处理这些数据,都会将这些IP数据包复制一份传递给协议类型匹配的原始套接字
(3):对于不能识别协议类型的数据包,内核进行必要的校验,然后会查看是否有类型匹配的原始套接字负责处理这些数据,如果有的话,就会将这些IP数据包复制一份传递给匹配的原始套接字,否则,内核将会丢弃这个IP数据包并返回一个ICMP主机不可达的消息给源主机
(4)如果原始套接字bind绑定了一个地址,核心只将目的地址为本机IP地址的数包传递给原始套接字,如果某个原始套接字没有bind地址,核心就会把收到的所有IP数据包发给这个原始套接字
(5): 如果原始套接字调用了connect函数,则核心只将源地址为connect连接的IP地址的IP数据包传递给这个原始套接字
(6):如果原始套接字没有调用bind和connect函数,则核心会将所有协议匹配的IP数据包传递给这个原始套接字

1.2:利用原始套接字发送ICMP消息
由于操作系统的关系,创建一个原始套接字必须运行在管理员权限之下,因此,本程序全部运行在管理员模式。

WORD wVersion=MAKEWORD(2,2);
    WSADATA wsaData;
    WSAStartup(wVersion,&wsaData);
    if (LOBYTE(wsaData.wVersion)!=2||HIBYTE(wsaData.wVersion)!=2)
    {
        WSACleanup();
        return -1;
    }
    addr.sin_family=AF_INET;
    addr.sin_addr.S_un.S_addr=inet_addr(dstIp.c_str());
    addr.sin_port=0;
    sock=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);//创建原始套接字,设置为icmp类型 
    rst=setsockopt(sock,SOL_SOCKET,SO_SNDTIMEO,(char*)&outTime,sizeof(int));//设置发送超时
    if (SOCKET_ERROR==rst)
    {
        cout<<"set outtime error"<<endl;
        WSACleanup();
        return -2;
    }
.
int count=1;//设置发送的次数
    int len=sizeof(sockaddr);
    while (count--)
    {
        ICMPHDR *icmp=new ICMPHDR;//ICMPHDR 定义的ICMP头格式
        memset(icmp,0,sizeof(icmp));
        FillIcmp(icmp);  //用来初始化填充ICMP报文

        SOCKADDR_IN addrFrom;
        int nLen=sizeof(addrFrom);

        icmp->icmp_type=8;//类型为8
        icmp->icmp_code=0;//代表我是发送ping回复
        icmp->icmp_sequence=5678;//证明这是我的木马。
        strcpy_s(icmp->data,str.c_str()); //附带的数据
        icmp->icmp_checksum = CheckSum((USHORT*)icmp, sizeof(ICMPHDR)); //进行ICMP的校验

        int result;
        result = sendto(sock,(char*)icmp, sizeof(ICMPHDR),0,(SOCKADDR*)&addr,sizeof(SOCKADDR)); //向目标主机发送icmp请求包,也就得到这服务器的ip地址。
        if (SOCKET_ERROR == result)
        {
            if (WSAETIMEDOUT == WSAGetLastError())
            {
                cout << "send time out" << endl;
                continue;
            }
            else
            {
                cout << "sendto error" << endl;
                closesocket(sock);
                WSACleanup();
                return -1;
            }
        }
    }

**这样就可以发送一条ICMP消息了。
但是在这里遇到了一个问题,希望能有大神给解答一下。 我在B电脑上创
建一个ICMP的套接字,为什么收不到A电脑发送的ICMP报文,通过抓包分析,A电脑发送的ICMP报文正确到达了B电脑,为什么这个原始套接字不能捕获这条ICMP报文。也正是由于这个原因, 我在下面服务器和客户端的ICMP报文接收中,使用的捕获数据链路层的包来分析ICMP的内容,这实在是一个巨大的遗憾!!**

1.3 接收ICMP消息
由于上述的原因,我并没有成功的直接接收到ICMP报文,因此,我转而通过嗅探的方法来捕获到达的ICMP报文,为了方便,这里使用了wincap来进行。大家也可以使用基于数据链路层的原始套接字来捕获ICMP消息,如果谁有更好的方法,请不吝赐教。
关键代码如下:

if ((devHandle=pcap_open_live(d->name,1600,1,1,er))==NULL)//d-namp为网卡的描述符,1600为缓冲区的大小,适当大小的缓冲区有利于提高响应的速度,1为将网卡置于混杂模式,1为响应的时间为1ms,具体参数的定义大家可以自行百度
    {
        pcap_freealldevs(alldevs);  
        return false;
    }
    if (pcap_datalink(devHandle)!=DLT_EN10MB)
    {
        AfxMessageBox(_T("不是以太网"));
        pcap_freealldevs(alldevs);  
        return false;
    }

    m_ThreadHandle=AfxBeginThread((AFX_THREADPROC)MyProc,this);

while (1)
    {
        pcap_next_ex(pThis->devHandle, &header, &pkt_data);//pkt__data即为捕获到的所有数据,大家可已根据IP和ICMP报文的格式进行分析,我就不再赘述
        datapkt *data = new datapkt;      
        memset(data,0,sizeof(data));
        if(NULL == data)  
        {  
            AfxMessageBox(_T("空间已满,无法接收新的数据包"));  
            return ;  
        }  
        if(pThis->analyze_frame(pkt_data,data)<0)  
        {
            delete data;
            continue;
        }
        else
        {
            pThis->recvMSG(data);//得到了接收到的icmp消息 
            delete data;
        }
    }

二:通信数据格式的定义

互联网上的数据都有一定的格式,我们的木马程序使用的数据格式如下:这只是一个最简单的实现,可根据实际的需要设置自己的数据格式。
ICMP数据报格式:
8位类型码 8位代码 16校验和
16位标志位 16位序号
8位操作类型 最长1024位数据可选

其中,服务端发送的是类型码为8,代码为0的ICMP消息,这会被计算机理解为一个ping请求,故一般不会被防火墙拦截。
客户端发送的是类型码为0,代码为0的ICMP消息,这是一个ping回复。
16位标志位代表发送这个ICMP报文的进程号,
16位序号代表这个ICMP报文的序号,可以用来组装附加的数据
8位操作类型的定义如下:

三:服务器的实现
服务端和客户端的实现比较简单。服务端即被控制端。当被控制端开机,服务器会伴随启动,并发送ICMP请求给客户端,也就是发送操作类型为请求连接的ICMP消息给客户端,客户端收到这个消息以后,若同意,会发送ICMP连接确认,告诉被控主机。这时,就会建立起ICMP连接。其中,正常的ICMP消息和木马的ICMP消息,我是使用16位的标志位来标志这是属于我木马的ICMP消息。
四:客户端的实现
客户端更为简单,大家有疑问可以留言。

转载于:https://www.cnblogs.com/shirelyme/p/4461523.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值