一、概要介紹
近期較忙,未能及時更新博客,Python告一段落後,開始基於C++的武器庫核心技術實現,本期基於C++MFC,利用socket,通過非阻塞的方式實現client與server端的通信,通過client向server發起指令獲取相關信息。
服務器端函數邏輯: socket()->bind()->accept()->send()/recv()->closesocket();
客戶端函數邏輯:socket()->connet()->send()/recv()->closesocket();
二、系統核心代碼-服務端
// UMDlg.cpp : 實現文件
//
#include "stdafx.h"
#include "UM.h"
#include "UMDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 用於應用程序“關於”菜單項的 CAboutDlg 對話框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 對話框數據
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 實現
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CUMDlg 對話框
CUMDlg::CUMDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CUMDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CUMDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CUMDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_MESSAGE(UM_SERVER, OnSock)
ON_WM_CLOSE()
END_MESSAGE_MAP()
// CUMDlg 消息處理程序
BOOL CUMDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 將“關於...”菜單項添加到系統菜單中。
// IDM_ABOUTBOX 必須在系統命令範圍內。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 設置此對話框的圖標。當應用程序主窗口不是對話框時,框架將自動
// 執行此操作
SetIcon(m_hIcon, TRUE); // 設置大圖標
SetIcon(m_hIcon, FALSE); // 設置小圖標
// TODO: 在此添加額外的初始化代碼
//----------------------------初始化-------------------------
//WSDATA wsaData;
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);//初始化
listenSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); //創建套接字;SOCK_STREAM:流套接字 SOCK_DGRAM:數據包套接字;SOCK_RAM:原始協議套接字;TCP形式
WSAAsyncSelect(listenSocket,GetSafeHwnd(),UM_SERVER,FD_ACCEPT); //非阻塞模式 1:接收消息窗口;2:消息;3:通知碼;
//對socket_in結構體進行填充。包括地址、端口等信息
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.S_un.S_addr =ADDR_ANY; //inet_addr("192.168.1.102"); //將點分10進制IP,轉換成無符號long類型 也可以設置ADDR_ANY; 逆函數 inet_ntor
addr.sin_port = htons(5555);//主機字節轉換成網絡字節,在端口方面,x86爲小尾方式 TCP/IP爲大尾方式,所以需要轉換。大尾與小尾區別爲字節存儲順序相反
bind(listenSocket,(SOCKADDR *)&addr,sizeof(addr)); //綁定IP地址和端口,並處於監聽狀態
listen(listenSocket,1);
return TRUE; // 除非將焦點設置到控件,否則返回 TRUE
}
//處理通知碼
LRESULT CUMDlg::OnSock(WPARAM wParam, LPARAM lParam)
{
if(WSAGETSELECTERROR(lParam)){
return 1;
}
switch(WSAGETSELECTEVENT(lParam)){
case FD_ACCEPT:
{
sockaddr_in clientAddr;
int nSize = sizeof(clientAddr);
clientSocket = accept(listenSocket,(SOCKADDR *)&clientAddr,&nSize); //建立
WSAAsyncSelect(clientSocket,GetSafeHwnd(),UM_SERVER,FD_READ|FD_CLOSE); //非阻塞模式 1:接收消息窗口;2:消息;3:通知碼;
strMsg.Format("請求的地址是%s:%d",inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port));
DATA_MSG dataMsg;
dataMsg.bType = TEXTMSG;
dataMsg.bClass = 0;
lstrcpy(dataMsg.szValue,HELPMSG);
send(clientSocket,(const char *)&dataMsg,sizeof(dataMsg),0); //發送
break;
}
case FD_READ:
{
char szBuf[MAXBYTE] = {0};
recv(clientSocket,szBuf,MAXBYTE,0); //接收
DispatchMsg(szBuf);
strMsg = "對方發來指令";
strMsg += szBuf;
break;
}
case FD_CLOSE:
{
closesocket(clientSocket);
strMsg = "對方關閉連接";
break;
}
}
InsertMsg();
return 1;
}
//匹配對應的操作命令
void CUMDlg::DispatchMsg(char* szBuf)
{
DATA_MSG dataMsg;
ZeroMemory((void *)&dataMsg,sizeof(dataMsg));
if(!strcmp(szBuf,"help")){
dataMsg.bType = TEXTMSG;
dataMsg.bClass = 0;
lstrcpy(dataMsg.szValue,HELPMSG);
}
else if(!strcmp(szBuf,"getsysinfo")){
SYS_INFO SysInfo;
GetSysInfo(&SysInfo);
dataMsg.bType = BINARYMSG;
dataMsg.bClass = SYSINFO;
SysInfo.szComputerName;
CString cName= SysInfo.szComputerName;
CString uName= SysInfo.szUserName;
lstrcpy(dataMsg.szValue,"主機名:"+cName+" 用戶:"+uName);
//memcpy((void *)dataMsg.szValue, &SysInfo.szUserName, sizeof(dataMsg));
//memcpy((void *)dataMsg.szValue,(char *) &SysInfo,sizeof(dataMsg));
}
send(clientSocket,(const char *)&dataMsg,sizeof(dataMsg),0); //發送
}
//獲取系統信息
void CUMDlg::GetSysInfo(PSYS_INFO SysInfo)
{
unsigned long nSize = 0;
SysInfo->OsVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&SysInfo->OsVer);
nSize = NAME_LEN;
GetComputerName(SysInfo->szComputerName, &nSize);
nSize = NAME_LEN;
GetUserName(SysInfo->szUserName, &nSize);
//MessageBox(SysInfo->szUserName);
}
//輸出信息
void CUMDlg::InsertMsg()
{
CString strMsgNew;
GetDlgItemText(IDC_EDIT1, strMsgNew);//獲取內容
strMsg += "\r\n";
strMsg += "----------------------------------------\r\n";
strMsg += strMsgNew;
SetDlgItemText(IDC_EDIT1, strMsg); //填充內容
strMsg = "";
}
void CUMDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default
// 關閉監聽套接字,並釋放Winsock庫
closesocket(clientSocket);
closesocket(listenSocket);
WSACleanup();
CDialogEx::OnClose();
}
void CUMDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向對話框添加最小化按鈕,則需要下面的代碼
// 來繪製該圖標。對於使用文檔/視圖模型的 MFC 應用程序,
// 這將由框架自動完成。
void CUMDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用於繪製的設備上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使圖標在工作區矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 繪製圖標
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//當用戶拖動最小化窗口時系統調用此函數取得光標
//顯示。
HCURSOR CUMDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CUMDlg::OnEnChangeEdit1()
{
// TODO: 如果該控件是 RICHEDIT 控件,它將不
// 發送此通知,除非重寫 CDialogEx::OnInitDialog()
// 函數並調用 CRichEditCtrl().SetEventMask(),
// 同時將 ENM_CHANGE 標誌“或”運算到掩碼中。
// TODO: 在此添加控件通知處理程序代碼
}
三、系統核心代碼-客戶端
// UMCLIENTDlg.cpp : 實現文件
//
#include "stdafx.h"
#include "UMCLIENT.h"
#include "UMCLIENTDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 用於應用程序“關於”菜單項的 CAboutDlg 對話框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 對話框數據
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 實現
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CUMCLIENTDlg 對話框
CUMCLIENTDlg::CUMCLIENTDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CUMCLIENTDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CUMCLIENTDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CUMCLIENTDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, &CUMCLIENTDlg::OnBnClickedButton1)
ON_BN_CLICKED(IDC_BUTTON2, &CUMCLIENTDlg::OnBnClickedButton2)
ON_MESSAGE(UM_CLIENT, OnSock)
END_MESSAGE_MAP()
// CUMCLIENTDlg 消息處理程序
BOOL CUMCLIENTDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 將“關於...”菜單項添加到系統菜單中。
// IDM_ABOUTBOX 必須在系統命令範圍內。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 設置此對話框的圖標。當應用程序主窗口不是對話框時,框架將自動
// 執行此操作
SetIcon(m_hIcon, TRUE); // 設置大圖標
SetIcon(m_hIcon, FALSE); // 設置小圖標
// TODO: 在此添加額外的初始化代碼
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
return TRUE; // 除非將焦點設置到控件,否則返回 TRUE
}
void CUMCLIENTDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向對話框添加最小化按鈕,則需要下面的代碼
// 來繪製該圖標。對於使用文檔/視圖模型的 MFC 應用程序,
// 這將由框架自動完成。
void CUMCLIENTDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用於繪製的設備上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使圖標在工作區矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 繪製圖標
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//當用戶拖動最小化窗口時系統調用此函數取得光標
//顯示。
HCURSOR CUMCLIENTDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
//連接
void CUMCLIENTDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知處理程序代碼
char szBtnName[10] = {0};
GetDlgItemText(IDC_BUTTON1,szBtnName,10);
if(!lstrcmp(szBtnName,"斷開連接")){
SetDlgItemText(IDC_BUTTON1,"連接");
(GetDlgItem(IDC_EDIT2)->EnableWindow(FALSE)); //內容區域
(GetDlgItem(IDC_EDIT4)->EnableWindow(FALSE)); //命令區域
(GetDlgItem(IDC_BUTTON2)->EnableWindow(FALSE)); //發送按鈕
closesocket(mSocket);
strMsg = "主動斷開連接";
InsertMsg();
return;
}
char szIpAddr[MAXBYTE] = {0};
GetDlgItemText(IDC_EDIT1,szIpAddr,MAXBYTE); //獲取IP
mSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); //創建套接字;SOCK_STREAM:流套接字 SOCK_DGRAM:數據包套接字;SOCK_RAM:原始協議套接字;TCP形式
WSAAsyncSelect(mSocket,GetSafeHwnd(),UM_CLIENT,FD_READ|FD_CONNECT|FD_CLOSE); //非阻塞模式 1:接收消息窗口;2:消息;3:通知碼;
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.S_un.S_addr = inet_addr(szIpAddr); //將點分10進制IP,轉換成無符號long類型 也可以設置ADDR_ANY; 逆函數 inet_ntor
addr.sin_port = htons(5555);//主機字節轉換成網絡字節,在端口方面,x86爲小尾方式 TCP/IP爲大尾方式,所以需要轉換。大尾與小尾區別爲字節存儲順序相反
connect(mSocket,(SOCKADDR *)&addr,sizeof(addr)); //連接
}
//發送命令
void CUMCLIENTDlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知處理程序代碼
char szBuf[MAXBYTE] = {0};
GetDlgItemText(IDC_EDIT4,szBuf,MAXBYTE); //獲取命令
send(mSocket,szBuf,MAXBYTE,0); //發送命令
}
//響應通知碼
LRESULT CUMCLIENTDlg::OnSock(WPARAM wParam, LPARAM lParam)
{
if(WSAGETSELECTERROR(lParam)){
return 1;
}
switch(WSAGETSELECTEVENT(lParam)){
//處理 FD_ACCEPT
case FD_CONNECT:
{
(GetDlgItem(IDC_EDIT2)->EnableWindow(TRUE)); //內容區域
(GetDlgItem(IDC_EDIT4)->EnableWindow(TRUE)); //命令區域
(GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE)); //發送按鈕
SetDlgItemText(IDC_BUTTON1,"斷開連接");
strMsg = "連接成功";
break;
}
case FD_READ:
{
DATA_MSG dataMsg;
recv(mSocket,(char *) &dataMsg,sizeof(dataMsg),0); //接收
DispatchMsg((char *)&dataMsg);
break;
}
case FD_CLOSE:
{
(GetDlgItem(IDC_EDIT2)->EnableWindow(FALSE)); //內容區域
(GetDlgItem(IDC_EDIT4)->EnableWindow(FALSE)); //命令區域
(GetDlgItem(IDC_BUTTON2)->EnableWindow(FALSE)); //發送按鈕
closesocket(mSocket);
strMsg = "對方斷開連接";
break;
}
}
InsertMsg();//輸出到前臺
return 1;
}
//匹配消息
void CUMCLIENTDlg::DispatchMsg(char* szBuf)
{
DATA_MSG DataMsg;
memcpy((void*)&DataMsg, (const void *)szBuf, sizeof(DATA_MSG));
if(DataMsg.bType = TEXTMSG){ //幫助信息
strMsg = DataMsg.szValue;
}
else {
if(DataMsg.bClass == SYSINFO){ //系統信息
ParseSysInfo((PSYS_INFO )&DataMsg.szValue);
}
}
}
//解析系統信息
void CUMCLIENTDlg::ParseSysInfo(PSYS_INFO sysInfo)
{
if ( sysInfo->OsVer.dwPlatformId == VER_PLATFORM_WIN32_NT )
{
if ( sysInfo->OsVer.dwMajorVersion == 5 && sysInfo->OsVer.dwMinorVersion == 1 )
{
strMsg.Format("對方系統信息:\r\n\t Windows %s", sysInfo->OsVer.szCSDVersion);
}
else if ( sysInfo->OsVer.dwMajorVersion == 5 && sysInfo->OsVer.dwMinorVersion == 0)
{
strMsg.Format("對方系統信息:\r\n\t Windows 2K");
}
}
else
{
strMsg.Format("對方系統信息:\r\n\t Other System \r\n");
}
strMsg += "\r\n";
strMsg += "\t Computer Name is ";
strMsg += sysInfo->szComputerName;
strMsg += "\r\n";
strMsg += "\t User Name is ";
strMsg += sysInfo->szUserName;
}
void CUMCLIENTDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default
closesocket(mSocket);
WSACleanup();
CUMCLIENTDlg::OnClose();
}
//輸出信息
void CUMCLIENTDlg::InsertMsg()
{
CString strMsgNew;
GetDlgItemText(IDC_EDIT2, strMsgNew);//獲取內容
strMsg += "\r\n";
strMsg += "----------------------------------------\r\n";
strMsg += strMsgNew;
SetDlgItemText(IDC_EDIT2, strMsg); //填充內容
strMsg = "";
}
需要注意的是,在X86架構下,數值存儲方式默認是小尾方式字節順序(內存高位地址存放高位字節數據),TCP/IP數值存儲方式默認是大尾方式字節順序(內存高位地址存放低位字節數據)。
四、後期優化
在深度層面:1.利用系統漏洞或者U盤病毒進行傳播,以反彈方式與client進行交互。2.掃描端口,進行端口複用。
在廣度層面:1.支持執行CMD命令;1.支持獲取遠程文件信息,實現文件目錄瀏覽、文件下載、文件加密等。