gh0st源码分析之文件传输上传下载Word文档格式.docx
《gh0st源码分析之文件传输上传下载Word文档格式.docx》由会员分享,可在线阅读,更多相关《gh0st源码分析之文件传输上传下载Word文档格式.docx(13页珍藏版)》请在冰点文库上搜索。
NC_RECEIVE:
//ProcessReceive(pContext);
NC_RECEIVE_COMPLETE:
//这不是刚才传进的参数吗?
ProcessReceiveComplete(pContext);
//熬,原来关键在这里,转进去看看
}
}catch(...){}
我们刚才传递的是NC_RECEIVE_COMPLETE,所以我们只关心对它的处理,接着我们转到ProcessReceiveComplete(pContext)里看看并下断点,我只把跟我们相关的处理拿出来:
caseFILEMANAGER_DLG:
((CFileManagerDlg*)dlg)->
OnReceiveComplete();
break;
为什么一定会来到这个case语句呢?
你怎么知道就一定会来到这里呢?
其实忘记给大家说了,当你右键单击文件管理这个菜单项的时候就会投递一个消息:
PostMessage(WM_OPENAUDIODIALOG,0,(LPARAM)pContext);
而这个消息的响应函数是OnOpenManagerDialog(WPARAMwParam,LPARAMlParam),
打开文件管理对话框类,那就在OnOpenManagerDialog函数看看吧:
ClientContext*pContext=(ClientContext*)lParam;
CFileManagerDlg
*dlg=newCFileManagerDlg(this,m_iocpServer,pContext);
dlg->
Create(IDD_FILE,GetDesktopWindow());
ShowWindow(SW_SHOW);
pContext->
m_Dialog[0]=FILEMANAGER_DLG;
m_Dialog[1]=(int)dlg;
//上面的两句为客户端上下文的m_Dialog成员赋值了,类型是文件管理对话框。
所以在以后的处理接收完数据的函数ProcessReceiveComplete(ClientContext*pContext)里始终运行下面三句代码:
那我们在转到CFileManagerDlg对话框的OnReceiveComplete()函数看看,这里只列出部分:
voidCFileManagerDlg:
:
OnReceiveComplete()
switch(m_pContext->
m_DeCompressionBuffer.GetBuffer(0)[0])
caseTOKEN_FILE_LIST:
FixedRemoteFileList
(
m_pContext->
m_DeCompressionBuffer.GetBuffer(0),
m_DeCompressionBuffer.GetBufferLen()-1
);
caseTOKEN_FILE_SIZE:
//传输文件时的第一个包,文件大小及文件名
CreateLocalRecvFile();
caseTOKEN_FILE_DATA:
//文件的内容
WriteLocalRecvFile();
caseTOKEN_TRANSFER_FINISH:
//传输完成的处理
EndLocalRecvFile();
是一个switch语句,分别处理不同的指令。
在这个函数也下个断点吧。
好了开始动态调试吧!
在投递PostRecv(ClientContext*pContext)函数请求时会接收到服务端发过来的压缩数据包,一共有38个字节长度:
其中前五个字节是Ghost标志,黑色方框内就是标志:
接着4个字节是整个数据包的大小长度,白色方框内既是:
然后是数据未压缩前的大小,蓝色方框内既是:
最好剩下的25个字节是被压缩后的数据,红色方框里便是:
然后转到CIOCPServer:
OnClientReading(ClientContext*pContext,DWORDdwIoSize)函数里,略过部分到:
m_CompressionBuffer.Read((PBYTE)bPacketFlag,sizeof(bPacketFlag));
当这句执行后将m_CompressionBuffer里的前5个字节读到bPacketFlag里去,同时把后面的所有数据往前拷贝,给人的感觉好像是指针往后移动。
现在看内存的情况,前面的5个字节没有了,后面的跟了上来,不过这时候的地址变了:
m_CompressionBuffer.Read((PBYTE)&
nSize,sizeof(int));
这句执行后变成下面的模样,后面的数据也是跟着上来,nSize就被赋值为0x26了,也就是10进制的38了,代表整个数据包的大小长度,包括ghost标志+数据包总大小+未压缩时的大小,我们刚开始整个红色方框的全部内容:
nUnCompressLength,sizeof(int));
等这句执行完毕后nUnCompressLength被赋值为0x13了,也就是10进制的19,代表数据没有压缩时候的长度。
int
nCompressLength=nSize-HDR_SIZE;
//这句是压缩后的真正的数据长度,下图红框内:
以我们的列子为准就是38-13=25了。
PBYTEpData=newBYTE[nCompressLength];
分配一个缓冲区用于盛放压缩的数据,
m_CompressionBuffer.Read(pData,nCompressLength);
这时候pData就被填充上图红框内的数据。
PBYTEpDeCompressionData=newBYTE[nUnCompressLength];
分配一个容纳解压后的缓冲区。
nRet=uncompress(pDeCompressionData,&
destLen,pData,nCompressLength);
这句就是解压上图红色方框内的数据,解压后下图红色方框内的数据:
解压成功后pContext->
m_DeCompressionBuffer.Write(pDeCompressionData,destLen);
这句把pDeCompressionData解压的数据写入到成员变量m_DeCompressionBuffer里。
然后下面的一句代码:
_pNotifyProc((LPVOID)m_pFrame,pContext,NC_RECEIVE_COMPLETE);
上面提到的回调函数,转到NotifyProc函数,再转到ProcessReceiveComplete函数,再转到ProcessReceiveComplete函数,通过
FILEMANAGER_DLG:
((CFileManagerDlg*)dlg)->
转到CFileManagerDlg类的OnReceiveComplete函数,里面有个switch语句,条件是m_pContext->
m_DeCompressionBuffer.GetBuffer(0)[0]找到m_DeCompressionBuffer解压缓冲区偏移0字节的地方看看,下图红色方框便是:
发现是0x69,10进制的105,105代表的是谁呢,当然是TOKEN_FILE_SIZE,不信你把光标放到这上面看看:
于是我们再转到CFileManagerDlg:
CreateLocalRecvFile()函数,真的是转的头都晕了,终于到了我们需要的地方了。
只捡重要的地方说:
FILESIZE
*pFileSize=(FILESIZE*)(m_pContext->
m_DeCompressionBuffer.GetBuffer
(1));
DWORD
dwSizeHigh=pFileSize->
dwSizeHigh;
dwSizeLow=pFileSize->
dwSizeLow;
第一句从解压缓冲区里获取偏移1个字节的地址赋值给pFileSize指针,就是上图的0x02910001这个地址了,长度为8个字节,前面4字节是高位,后面4字节是低位。
这是自定义的一个结构:
typedef
struct
}FILESIZE;
从下面的图片可以看出高位是0,低位是0x2136,说明文件大小是10进制8502字节。
的确文件大小是8502字节:
m_strOperatingFile=m_pContext->
m_DeCompressionBuffer.GetBuffer(9);
获取文件名的,然后创建文件。
在创建文件的前面还有一个指令:
BYTE
bToken[9];
用来容纳指令的。
bToken[0]=COMMAND_CONTINUE;
这是要发送的命令,表示继续传输数据。
接下来就是一些判断,判断客户端机器有没有和服务端同名的文件存在,如果存在说明文件已经建立好,就不用再创建了,可以传送文件的具体内容了,把上次传送的字节填充到bToken里去。
这里肯定是没有的了,需要建立文件,所以下面创建文件。
HANDLEhFile=CreateFile(
m_strReceiveLocalFile.GetBuffer(0),
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
dwCreationDisposition,
FILE_ATTRIBUTE_NORMAL,
0);
上面执行完后将会的D盘创建一个文件名为mk.txt的文件,创建好之后发送一个指令:
m_iocpServer->
Send(m_pContext,bToken,sizeof(bToken));
指令里并没有上次传送的字节数,因为是首次还没有内容数据传送过来,接着就发送指令告诉服务端继续传送数据。
很快我们收到服务端发过来的关于文件传送的第二次压缩数据包:
接着再次进入OnClentReading函数:
上面3句执行完后得到nSize=129,nUnCompressLength=8192。
unsigned
long
destLen=nUnCompressLength;
if
(nRet==Z_OK)
pContext->
m_DeCompressionBuffer.ClearBuffer();
m_pNotifyProc((LPVOID)m_pFrame,pContext,NC_RECEIVE_COMPLETE);
}
等着几句执行完成后我们将会得到去掉前面三个方框内的数据后的真正的文件内容压缩的数据:
上图解压后成下面的图片样子,下面还有但是没有截图出来:
又看到m_pNotifyProc((LPVOID)m_pFrame,pContext,NC_RECEIVE_COMPLETE);
这句了,再次转到:
NotifyProc(LPVOIDlpParam,ClientContext*pContext,UINTnCode)函数里:
caseNC_RECEIVE_COMPLETE:
ProcessReceiveComplete(pContext);
老样子再转到ProcessReceiveComplete(ClientContext*pContext)函数里:
caseFILEMANAGER_DLG:
继续调转voidCFileManagerDlg:
OnReceiveComplete()函数里,是个switch语句:
switch
(m_pContext->
m_DeCompressionBuffer.GetBuffer(0)[0])
{
…
CreateLocalRecvFile();
caseTOKEN_FILE_DATA:
/
根据switch语句条件获取第一个字节内容是6a(看上图),转化为10进制是106对应TOKEN_FILE_DATA
那我们就再转吧,跟进WriteLocalRecvFile函数里。
从函数名就知道是往里面写入文件内容的,第一次是创建文件的。
从逻辑上想,先创建文件,再写入内容,最后写入完成结束。
*pData;
dwBytesToWrite;
dwBytesWrite;
nHeadLength=9;
//1+4+4
*pFileSize;
pData=m_pContext->
m_DeCompressionBuffer.GetBuffer(nHeadLength);
pFileSize=(FILESIZE*)m_pContext->
m_DeCompressionBuffer.GetBuffer
(1);
m_nCounter=MAKEINT64(pFileSize->
dwSizeLow,pFileSize->
dwSizeHigh);
LONG
dwOffsetHigh=pFileSize->
dwOffsetLow=pFileSize->
dwBytesToWrite=m_pContext->
m_DeCompressionBuffer.GetBufferLen()-nHeadLength;
从上图可以看出dwOffsetHigh=dwOffsetLow=0,dwBytesToWrite=8183这是去掉前面9个字节标头后的数据长度,也是我们需要写入文件的字节数。
在写入之前是不是先要打开文件呢?
是的
HANDLEhFile=CreateFil(
m_strReceiveLocalFile.GetBuffer(0),
OPEN_EXISTING,
0
);
打开之后从哪个位置写入呢,是不是要设置一下文件指针?
第一次就是从0写入喽!
因为dwOffsetHigh=dwOffsetLow=0。
SetFilePointer(hFile,dwOffsetLow,&
dwOffsetHigh,FILE_BEGIN);
文件指针设置好之后我们就开始写入内容吧!
nRet=WriteFile(hFile,pData,dwBytesToWrite,&
dwBytesWrite,NULL);
写完之后关闭文件句柄CloseHandle(hFile);
然后把写入的情况通过发送指令告诉服务端:
bToken[0]=COMMAND_CONTINUE;
//要求继续发送,看内存:
dwOffsetLow+=dwBytesWrite;
//记录文件指针,每次递加
memcpy(bToken+1,&
dwOffsetHigh,sizeof(dwOffsetHigh));
//执行后看内存:
memcpy(bToken+5,&
dwOffsetLow,sizeof(dwOffsetLow));
//前面说过dwBytesToWrite=8183所以这句执行后看内存:
等指令发送到服务端后,服务端会根据我们指令里的信息从8183地方读取数据继续发送剩余的内容。
我们第三次接收数据的时候