一、概要介绍
近期较忙,未能及时更新博客,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.支持获取远程文件信息,实现文件目录浏览、文件下载、文件加密等。