C++实现文件传输.docx
《C++实现文件传输.docx》由会员分享,可在线阅读,更多相关《C++实现文件传输.docx(13页珍藏版)》请在冰豆网上搜索。
C++实现文件传输
在木马中文件管理的重要性,是无需质疑的,对于文件的管理,做到并不难,但做好却也不易在我们编写一个功能完整的“文件木马”
其实现效果如图所示。
为了文章的完整性,我们将分为数篇来介绍,本文主要介绍程序的整体框架和界面实现,在以后的文章中将以此框架为基础实现详细的功能。
实现:
枚举磁盘,枚举目录,获取文件信息
上传文件,下载文件,执行文件,创建目录,删除目录等
传输控制结构
要实现客户端与服务端的通信,设计一个合理的传输控制结构,会使后面的工作轻松很多,为了使代码易读
首先对要使用的命令进行预定义其各个命令定义如下
#defineGetDriver0x01务端循环接受连接,并把连接交给线程处理
2.线程接受"命令数据",并跟据命令ID将命令对像和SOCKET句柄传给处理函数
3.函数执行指定功能,并返回执行结果
对整体结构的描述,我们用伪代码表述如下
main()
{/*初示化设置......*/
while(true)
{
if(client=accept(server,(sockaddr*)&clientaddr,&len))....*/
WSACleanup();
}
服务端程序运行后循环接受连接,如果有新的连接就传递给新的线程处理,线程代码如下
DWORDWINAPISLisen(LPVOIDlparam)
{
SOCKETclient=(SOCKET)lparam;
COMMANDcommand;
while
(1)
{
if(recv(client,(char*)&command,sizeof(command),0)==SOCKET_ERROR)....*/
}
}
}
}
线程式的功能是接受客户端的"命令数据",并跟跟据命令ID将命令对像传递给处理函数,由函数完成指定的功能
以删除文件命令为例其函数格式如下
DWORDDelFileProc(COMMANDcommand,SOCKETclient)
{
if(DeleteFile((char*)==0).");
}
else
{
send(client,"删除成功...");
}
}
很容易看出,处理函数接受"命令对像"和客户端SOCKET句柄,执行后会把结果传递回去....
客户端结构
客户端结构的实现思路如下
1.跟服务端建立连接
2.发送用户命令
3.启动一个线程,用于接受服务端的返回信息
对整体结构的描述,我们用伪代码表述如下
voidCMyDlg:
:
OnConnect()
{
if(connect(server,(SOCKADDR*)&serveraddr,sizeof(serveraddr))<0)..
{
return;
}
CreateThread(NULL,NULL,CLisen,this,NULL,NULL);
界面实现
程序的主界面如上图所示,主程序是一个对话框,主要包括一个树控件m_tree和列表控件m_list分别
用于显示磁盘目录和文件,在对话框初示化时用以下代码设置树控件的属性
DWORDdwStyle=GetWindowLong,GWL_STYLE);
dwStyle|=TVS_HASBUTTONS|TVS_HASLINES|TVS_LINESATROOT;
SetWindowLong,GWL_STYLE,dwStyle);
对于列表框控件则没有太多要求,要留意的是,如果显示图标应该把Styles显示属性设置为ICON
VC的做出的界面,常常让人有种摔键盘的冲动。
其实稍微留意一下其设置,也可以让它漂亮一些
比如上图所示的界面就是经过简单设置得到的,而没有用其它类库,有点兴趣?
其设置方法为:
1.在对话框属性中设置Styles的Border属性为Thin
2.选重MoreStyles"可见"属性
3.选重ExtendedStyles的"静态边"属性
这样再运行一下程序是不是感觉清新不少?
到这里程序的主要结构框架和界面实现就介绍完了,下一篇将详细介绍其各种功能的实现
c++实现文件传输之二
在上一篇中,我们以经介绍了程序的流程和框架,在本篇将详细讨论各个功能的实现主要包括
1.获取磁盘信息
2.获取目录信息
3.获取文件信息
4.运行指定文件
5.删除指定文件
6.删除指定目录
7.创建指定目录
8.上传下载文件
9.获取远程文件图标
获取磁盘信息
磁盘信息可以用APIGetDriveType来实现,它以路径名作为参数(如C:
/)返回磁盘类型,其实例代码如下
DWORDGetDriverProc(COMMANDcommand,SOCKETclient)
{
for(chari='A';i<='Z';i++)
{
charx[20]={i,':
'};
UINTType=GetDriveType(x);
if(Type==DRIVE_FIXED||Type==DRIVE_REMOVABLE||Type==DRIVE_CDROM)
{
/*返回处理结果...*/
}
}
return0;
}
GetDriveType可能返回的结果如下
#defineDRIVE_UNKNOWN 0");ockBuffer());.*/
}else
if(GetFileAttributes((char*)&FILE_ATTRIBUTE_READONLY)
{
/*只读文件...*/
}
send(client,(char*)&fi,sizeof(fi),0);
FindClose(hFile);
return0;
}
运行指定文件
运行文件有以下几种方法
这里使用的是ShellExecute其实例代码如下
DWORDExecFileProc(COMMANDcommand,SOCKETclient)
{
/*command为要运行的文件路径如(C:
/client为返回结果的SOCKET句柄*/
COMMAND cmd;
memset((char*)&cmd,0,sizeof(cmd));
=ExecFile;
if(ShellExecute(NULL,"open",(char*),NULL,NULL,SW_HIDE)<(HINSTANCE)32)
{
strcpy((char*),"文件执行失败!
");
send(client,(char*)&cmd,sizeof(cmd),0);
}
else
{
strcpy((char*),"文件执行成功!
");
send(client,(char*)&cmd,sizeof(cmd),0);
}
return0;
}
API函数ShellExecute原形为:
HINSTANCEShellExecute(
HWNDhwnd, ",DirName);
BOOLIsFinded=(BOOL)(tempFileFind);
while(IsFinded)
{
IsFinded=(BOOL)();
if(!
())
{
charfoundFileName[200];
strcpy(foundFileName,().GetBuffer(200));
if())
{
chartempDir[200];
sprintf(tempDir,"%s........
创建目录
实例代码如下:
DWORDCreateDirProc(COMMANDcommand,SOCKETclient)
{
/*command为要创建目录的路径如(C:
/)client为返回结果的SOCKET句柄*/
COMMAND cmd;
memset((char*)&cmd,0,sizeof(cmd));
=CreateDir;
if(:
:
CreateDirectory((char*),NULL))
{
strcpy((char*),"创建目录成功!
");
send(client,(char*)&cmd,sizeof(cmd),0);
}
else
{
strcpy((char*),"创建目录失败!
可能有重名文件或文件夹");
send(client,(char*)&cmd,sizeof(cmd),0);
}
return0;
}
在创建目录时应该注意几点,首先创始目录的上层目录必须是存在的,比如想创建C:
/DIR1/DIR2目录,要求
DIR1是必须存在,用CreateDirectory并不能创建多级目录.再者不可以存在和要创建目录同名的目录和文件
因为在磁盘上目录和文件的存放格式是相同的,惟一不同的是 目录的属性与文件属性不同
(FILE_ATTRIBUTE_DIRECTORY属性),所在即使有同名文件也会创始失败.
上传下载文件
上传下载是是文件管理的重点所在,在这里按文件的大小,分两种情况讨论文件的传输方法
小文件的传输相对比较简单可按以下方法进行
1.首先发送文件长度和名称
2.跟据文件长度建立缓冲区
3.读取整个文件到缓冲区
4.发送缓冲区里的内容
其实现代码如下:
CFilefile;
FILEINFOfileinfo;
if(path,CFile:
:
modeRead|CFile:
:
typeBinary))
{
=();面的方法就会力不从心了因为
按思路要创建一个跟文件大小相同的缓冲区,显然这是不太现实的,我们就得采用另种方法了,在这里我们使用
分块文件传输,所谓分块是指把大文件分成若干小文件,然后传输,比如设定每块大小为64KB其思路如下
1.取得文件长度和名称
2.跟据长度/64KB计算文件块数
3.分配64KB缓冲区
4.读文件到缓冲区
5.发送缓冲的数据
6.重复4,5两步直到发完所有数据
其实现代码如下:
#defineCHUNK_SIZE(64*1024)....
我们可以通过如下变通方法得到:
就是跟据文件的扩展名,从本地注册表中查找对应的程序图标
不过这也有它的缺点对于EXE文件它只能显示一个可执行文件的图示,而且只能显示注册过的图示比如,如果
本机装有WINRAR那么就可以识别.RAR的文件图示,否则就无法识别...
实现方法
CImageListm_ImageList;
(32,32,ILC_COLOR32,10,30);..
c++实现文件传输之三:
断点续传与多线程传输转
继木马编程DIY的上两篇,现在我们开始讨论断点续传与多线程文件传输的实现.其实这两项功能是下载软件所
必不可少的功能了,现在我们把它加到自己的木马中来感受感受.提到多线程下载,首先向网络蚂蚁的作者
洪以容前辈致敬,正是由于网络蚂蚁而使得多线程下载被关注并流行起来.在这本篇文章中我们将简单的实现
支持断点续传和多线程传输的程序.为了更清晰的说明问题,我们将断点续传与多线程传输分别用两个程序来实现
多线程传输实现
实现原理
将源文件按长度为分为N块文件,然后开辟N个线程,每个线程传输一块,最后合并所有线线程文件.比如
一个文件500M我们按长度可以分5个线程传输.第一线程从0-100M,第二线程从100M-200M......最后合并5个线程文件.
实现流程
1.客户端向服务端请求文件信息(名称,长度)
2.客户端跟据文件长度开辟N个线程连接服务端
3.服务端开辟新的线程与客户端通信并传输文件
4.客户端将每线程数据保存到一个文件
5.合并所有线程文件
编码实现
大体说来就是按以上步骤进行,详细的实现和一些要点,我们跟据以上流程在编码中实现
结构定义
在通信过程中需要传递的信息包括文件名称,文件长度,文件偏移,操作指令等信息,为了方便操作我们定义如下结构
代码:
typedefstruct
{
char Name[100]; 求文件信息
客户端代码如下
代码:
FILEINFOfi;
memset((char*)&fi,0,sizeof(fi));
=1; 里使用临界区
其文件信息函数GetInfoProc代码如下
代码:
DWORDGetInfoProc(SOCKETclient)
{
CFile file;
if(FileName,CFile:
:
modeRead|CFile:
:
typeBinary))
{
intFileLen=();
if(send(client,(char*)&FileLen,sizeof(FileLen),0)==SOCKET_ERROR)
{
cout<<"SendFileLenError/n";
}else
{
cout<<"TheFilelenis"< }
}
return0;
}
这里主要是向客户端传递文件长度,而客户端收到长度后则开辟线程进行连接传输文件
2.客户端跟据长度开辟线程
其实现代码如下
代码:
FILEINFOFI;
intFileLen=0;
if(recv(client,(char*)&FileLen,sizeof(FileLen),0)==SOCKET_ERROR)务端开辟线程传输数据
在1.请求文件信息中以说明了服务端的结构,这里主要介绍线程函数的实现,其代码如下
代码:
DWORDWINAPIGetFileProc(LPVOIDlparam)
{
EnterCriticalSection(&CS);
4.客户端将线程数据保存到文件
GetFileThread的实现代码如下
代码:
DWORDWINAPIGetFileThread(LPVOIDlparam)
{
charTempName[MAX_PATH];
sprintf(TempName,"TempFile%d",*(DWORD*)lparam); 样整个过程就完成了.最后一步合并线程文件
5.合并线程文件
代码:
intUniteFile() ./n";
int len;
char *date;
CFile file;
CFile file0;
/*其它文件......*/
if(FileName,CFile:
:
modeCreate|CFile:
:
typeBinary|CFile:
:
modeWrite))....*/
();
();
/*.......*/
delete[]date;
returntrue;
}else
{
returnfalse;
}
}
这个简单,就是打开一个文件读取到缓冲区,写入文件,再打开第二个......现在多线程传输部分就介绍完了
下面讨论断断点续传的实现
c++实现文件传输之四:
断点传输
所谓的断点续传就是指:
文件在传输过程式中被中断后,在重新传输时,可以从上次的断点处开始传输,这样就可节省时间,和其它资源.
实现关键在这里有两个关键点,其一是检测本地已经下载的文件长度和断点值,其二是在服务端调整文件指针到断点处
实现方法
我们用一个简单的方法来实现断点续传的功能.在传输文件的时候创建一个临时文件用来存放文件的断点位置
在每次发送接受文件时,先检查有没有临时文件,如果有的话就从临时文件中读取断点值,并把文件指针移动到
断点位置开始传输,这样便可以做到断点续传了
实现流程
首次传输其流程如下
1.服务端向客户端传递文件名称和文件长度
2.跟据文件长度计算文件块数(文件分块传输请参照第二篇文章)
3.客户端将传输的块数写入临时文件(做为断点值)
4.若文件传输成功则删除临时文件
首次传输失败后将按以下流程进行
1.客户端从临时文件读取断点值并发送给服务端
2.服务端与客户端将文件指针移至断点处
3.从断点处传输文件
编码实现
因为程序代码并不复杂,且注释也比较详细,这里就给出完整的实现
其服务端实现代码如下
代码:
int_tmain(intargc,TCHAR*argv[],TCHAR*envp[])
{
cout<<"/t/t服务端-断点续传"<<"/t作者:
冷风/n/n"<<"请输入被下载的文件路径如/n/n"<<"文件路径:
";
cin >>FilePath;
/*这部分为网络参数与设置,详细请参照源代码......*/
while(true)
{
if(client=accept(server,(sockaddr*)&clientaddr,&len))
{
cout<<"haveoneconnect/n";
intnCurrentPos=0;....*/
if(connect(client,(SOCKADDR*)&serveraddr,sizeof(serveraddr))==INVALID_SOCKET)
{
cout<<"ConnectServerError";
return0;
}
intFileLen=0;
intnCurrentPos=0;实现代码比较简单结合上面的流程介绍
看代码应该没什么难度,所以我也就不画蛇添足了.