React16源码: React中event事件系统初始化源码实现
event 事件系统初始化
1 )概述
- react事件系统比较的复杂,它是基于dom的事件系统
- 在dom事件系统上面进行了一个深度的封装
- 它里面的很多实现逻辑都是自由的一套
- 在初始化 react-dom 的源码的时候,会为react的事件系统注入 reactdom 相关的一些插件
- 因为react事件系统,它有一个独立的模块,这个模块是一个公用性质的模块
- 就是说它是可以给 react-dom 用,也可以给 react-native 用
- 不同平台它们的事件系统可能会不一样,这个时候就对于不同的平台
- 它们要去使用同一个 event 模块的时候,使用注入的方式来注入一些跟平台相关的逻辑在里面
- 在这个模块,也是有一部分核心的内容是全平台通用的,这部分内容是react抽象出来的
- 我们关注平台插件注入的一个流程,以及它插入之后到底做了什么事情
- 首先要确定一个插件注入的顺序
- 因为在react当中它的插件执行是会按照顺序来的
- 如果不按顺序来,可能会出现一定的问题
- 然后要注入插件模块
- 最后要计算 registationNameModules 等属性
- 首先要确定一个插件注入的顺序
- 在之前 completeWork 的时候,初始化 dom 节点的时候
- 要去绑定 props 对应的 dom 的 attributes 的时候
- 就有遇到过这个 registationNameModules 属性
2 )源码
定位到 packages/react-dom/src/client/ReactDOM.js#L20
import './ReactDOMClientInjection';
再次定位到 packages/react-dom/src/client/ReactDOMClientInjection.js
/*** Copyright (c) Facebook, Inc. and its affiliates.** This source code is licensed under the MIT license found in the* LICENSE file in the root directory of this source tree.*/import * as EventPluginHub from 'events/EventPluginHub';
import * as EventPluginUtils from 'events/EventPluginUtils';import {getFiberCurrentPropsFromNode,getInstanceFromNode,getNodeFromInstance,
} from './ReactDOMComponentTree';
import BeforeInputEventPlugin from '../events/BeforeInputEventPlugin';
import ChangeEventPlugin from '../events/ChangeEventPlugin';
import DOMEventPluginOrder from '../events/DOMEventPluginOrder';
import EnterLeaveEventPlugin from '../events/EnterLeaveEventPlugin';
import SelectEventPlugin from '../events/SelectEventPlugin';
import SimpleEventPlugin from '../events/SimpleEventPlugin';/*** Inject modules for resolving DOM hierarchy and plugin ordering.*/
EventPluginHub.injection.injectEventPluginOrder(DOMEventPluginOrder);
EventPluginUtils.setComponentTree(getFiberCurrentPropsFromNode,getInstanceFromNode,getNodeFromInstance,
);/*** Some important event plugins included by default (without having to require* them).*/
EventPluginHub.injection.injectEventPluginsByName({SimpleEventPlugin: SimpleEventPlugin,EnterLeaveEventPlugin: EnterLeaveEventPlugin,ChangeEventPlugin: ChangeEventPlugin,SelectEventPlugin: SelectEventPlugin,BeforeInputEventPlugin: BeforeInputEventPlugin,
});
-
看到它 import 了一大堆的东西,后续只是调用了3个方法
injectEventPluginOrder
setComponentTree
这个先跳过injectEventPluginsByName
-
看下
EventPluginHub.injection.injectEventPluginOrder(DOMEventPluginOrder);
- 这个
DOMEventPluginOrder
// packages/react-dom/src/events/DOMEventPluginOrder.js const DOMEventPluginOrder = ['ResponderEventPlugin','SimpleEventPlugin','EnterLeaveEventPlugin','ChangeEventPlugin','SelectEventPlugin','BeforeInputEventPlugin', ];export default DOMEventPluginOrder;
- 它单纯的 export 出来了一个数组
- 这个数组可以看到有6项,每一项都以一个 Plugin 为结尾的
- 这些 plugin 都是在 react-dom 这个环境当中要用到的 plugin
- 这边只是用来定义这些 plugin 它的一个顺序
- 这个
-
后续
EventPluginHub.injection.injectEventPluginsByName
这个方法的参数- 发现这里少了一个
ResponderEventPlugin
先不管
- 发现这里少了一个
-
关注下
EventPluginHub
这个模块下的 injection/*** Methods for injecting dependencies.*/ export const injection = {/*** @param {array} InjectedEventPluginOrder* @public*/injectEventPluginOrder,/*** @param {object} injectedNamesToPlugins Map from names to plugin modules.*/injectEventPluginsByName, };
- 上述内部这两个方法来自
./EventPluginRegistry.js
进入/*** Injects an ordering of plugins (by plugin name). This allows the ordering * to be decoupled from injection of the actual plugins so that ordering is * always deterministic regardless of packaging, on-the-fly injection, etc. * * @param {array} InjectedEventPluginOrder * @internal * @see {EventPluginHub.injection.injectEventPluginOrder} */ export function injectEventPluginOrder(injectedEventPluginOrder: EventPluginOrder, ): void {invariant(!eventPluginOrder,'EventPluginRegistry: Cannot inject event plugin ordering more than ' +'once. You are likely trying to load more than one copy of React.',);// Clone the ordering so it cannot be dynamically mutated.// 克隆一个可动态修改的数组eventPluginOrder = Array.prototype.slice.call(injectedEventPluginOrder);recomputePluginOrdering(); }/*** Injects plugins to be used by `EventPluginHub`. The plugin names must be * in the ordering injected by `injectEventPluginOrder`. * * Plugins can be injected as part of page initialization or on-the-fly. * * @param {object} injectedNamesToPlugins Map from names to plugin modules. * @internal * @see {EventPluginHub.injection.injectEventPluginsByName} */ export function injectEventPluginsByName(injectedNamesToPlugins: NamesToPlugins, ): void {let isOrderingDirty = false;// 遍历对象上的 pluginNamefor (const pluginName in injectedNamesToPlugins) {// 非本身拥有,则跳过if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) {continue;}const pluginModule = injectedNamesToPlugins[pluginName];if (!namesToPlugins.hasOwnProperty(pluginName) ||namesToPlugins[pluginName] !== pluginModule) {invariant(!namesToPlugins[pluginName],'EventPluginRegistry: Cannot inject two different event plugins ' +'using the same name, `%s`.',pluginName,);// 重新注入 modulenamesToPlugins[pluginName] = pluginModule;isOrderingDirty = true; // 设置这个 isOrderingDirty 状态}}if (isOrderingDirty) {recomputePluginOrdering(); // 调用这个方法} }
- 上述
namesToPlugins
本来就是一个 空的对象 - 进入
recomputePluginOrdering
/*** Recomputes the plugin list using the injected plugins and plugin ordering. * * @private */ function recomputePluginOrdering(): void {if (!eventPluginOrder) {// Wait until an `eventPluginOrder` is injected.return;}// 遍历在 injectEventPluginsByName 方法中处理好的 namesToPlugins 对象for (const pluginName in namesToPlugins) {const pluginModule = namesToPlugins[pluginName];const pluginIndex = eventPluginOrder.indexOf(pluginName); // 拿到注入顺序invariant(pluginIndex > -1,'EventPluginRegistry: Cannot inject event plugins that do not exist in ' +'the plugin ordering, `%s`.',pluginName,);// plugins 初始化的时候,是一个空的数组,存在则跳过if (plugins[pluginIndex]) {continue;}invariant(pluginModule.extractEvents,'EventPluginRegistry: Event plugins must implement an `extractEvents` ' +'method, but `%s` does not.',pluginName,);// 注意,这里的 index 是从 eventPluginOrder 的顺序插入的,而非有序插入,这里可能会造成数组中的某几项为 undefinedplugins[pluginIndex] = pluginModule;const publishedEvents = pluginModule.eventTypes; // click, change, focus 等类型for (const eventName in publishedEvents) {invariant(// 注意这里publishEventForPlugin(publishedEvents[eventName], // 注意这个数据结构pluginModule,eventName,),'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.',eventName,pluginName,);}} }
- 关于
eventTypes
const eventTypes = {// 这个 对应 dom 中的真实事件,比如 change 事件 document.addEventListener('change', () => {})// 这个 change 代表 event name 存在// 这个 value 对应上面的 dispatchConfigchange: {// 事件的阶段,有冒泡和捕获 两个阶段,对应react中 使用的事件 props 名称phasedRegistrationNames: {bubbled: 'onChange',captured: 'onChangeCapture', // 这个 props 不常用,用于在绑定捕获阶段的事件监听},// 监听 change 事件的同时,需要依赖绑定下面的事件dependencies: [TOP_BLUR,TOP_CHANGE,TOP_CLICK,TOP_FOCUS,TOP_INPUT,TOP_KEY_DOWN,TOP_KEY_UP,TOP_SELECTION_CHANGE,],}, };
- eventTypes 这个对象里面还可以再加其他事件,以上是初始化时候挂载处理的 change 事件,参考下面
- 对于 packages/react-dom/src/events/SimpleEventPlugin.js 里面监听了大部分的常用事件
- 在这里面 会生成一个 type, 定位到
#L143
(143行)function addEventTypeNameToConfig([topEvent, event]: EventTuple,isInteractive: boolean, ) {const capitalizedEvent = event[0].toUpperCase() + event.slice(1);const onEvent = 'on' + capitalizedEvent;// 注意这里const type = {phasedRegistrationNames: {bubbled: onEvent,captured: onEvent + 'Capture',},dependencies: [topEvent],isInteractive,};eventTypes[event] = type;topLevelEventsToDispatchConfig[topEvent] = type; }
- 在这里面 会生成一个 type, 定位到
- 进入
publishEventForPlugin
/*** Publishes an event so that it can be dispatched by the supplied plugin. * * @param {object} dispatchConfig Dispatch configuration for the event. * @param {object} PluginModule Plugin publishing the event. * @return {boolean} True if the event was successfully published. * @private */ function publishEventForPlugin(dispatchConfig: DispatchConfig,pluginModule: PluginModule<AnyNativeEvent>,eventName: string, ): boolean {invariant(!eventNameDispatchConfigs.hasOwnProperty(eventName),'EventPluginHub: More than one plugin attempted to publish the same ' +'event name, `%s`.',eventName,);// 这里 eventNameDispatchConfigs 的结构// { change: ChangeEventPlugin.eventTypes.change }eventNameDispatchConfigs[eventName] = dispatchConfig;// 获取事件 内部的 phasedRegistrationNamesconst phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;if (phasedRegistrationNames) {for (const phaseName in phasedRegistrationNames) {if (phasedRegistrationNames.hasOwnProperty(phaseName)) {const phasedRegistrationName = phasedRegistrationNames[phaseName];publishRegistrationName(phasedRegistrationName,pluginModule,eventName,);}}return true;} else if (dispatchConfig.registrationName) {publishRegistrationName(dispatchConfig.registrationName,pluginModule,eventName,);return true;}return false; }
- 进入
publishRegistrationName
/*** Publishes a registration name that is used to identify dispatched events. * * @param {string} registrationName Registration name to add. * @param {object} PluginModule Plugin publishing the event. * @private */ function publishRegistrationName(registrationName: string,pluginModule: PluginModule<AnyNativeEvent>,eventName: string, ): void {invariant(!registrationNameModules[registrationName],'EventPluginHub: More than one plugin attempted to publish the same ' +'registration name, `%s`.',registrationName,);// onChange: ChangeEventPluginregistrationNameModules[registrationName] = pluginModule;// onChange: [TOP_BLUR ...]registrationNameDependencies[registrationName] =pluginModule.eventTypes[eventName].dependencies;if (__DEV__) {const lowerCasedName = registrationName.toLowerCase();possibleRegistrationNames[lowerCasedName] = registrationName;if (registrationName === 'onDoubleClick') {possibleRegistrationNames.ondblclick = registrationName;}} }
- 进入
- 关于
const publishedEvents = pluginModule.eventTypes;
这里,可参考 packages/react-dom/src/events/ChangeEventPlugin.js#L258const ChangeEventPlugin = {eventTypes: eventTypes,_isInputEventSupported: isInputEventSupported, // 这个 _isInputEventSupported 是一个私有标志位// 这个 extractEvents 是生成事件,比如 onChange 事件对应的事件对象的extractEvents: function(topLevelType,targetInst,nativeEvent,nativeEventTarget,) {const targetNode = targetInst ? getNodeFromInstance(targetInst) : window;let getTargetInstFunc, handleEventFunc;if (shouldUseChangeEvent(targetNode)) {getTargetInstFunc = getTargetInstForChangeEvent;} else if (isTextInputElement(targetNode)) {if (isInputEventSupported) {getTargetInstFunc = getTargetInstForInputOrChangeEvent;} else {getTargetInstFunc = getTargetInstForInputEventPolyfill;handleEventFunc = handleEventsForInputEventPolyfill;}} else if (shouldUseClickEvent(targetNode)) {getTargetInstFunc = getTargetInstForClickEvent;}if (getTargetInstFunc) {const inst = getTargetInstFunc(topLevelType, targetInst);if (inst) {const event = createAndAccumulateChangeEvent(inst,nativeEvent,nativeEventTarget,);return event;}}if (handleEventFunc) {handleEventFunc(topLevelType, targetNode, targetInst);}// When blurring, set the value attribute for number inputsif (topLevelType === TOP_BLUR) {handleControlledInputBlur(targetNode);}}, };
- 关于
- 上述
- 上述内部这两个方法来自
-
通过以上操作,插入了所有的plugin之后,形成了这边的几个变量
let eventPluginOrder: EventPluginOrder = null;
数据结构如下['ResponderEventPlugin', 'SimpleEventPlugin', 'EnterLeaveEventPlugin', 'ChangeEventPlugin', 'SelectEventPlugin', 'BeforeInputEventPlugin' ];
export const plugins = [];
数据结构如下[{eventTypes:{},extractEvents:function,otherProps},.... ]
export const eventNameDispatchConfigs = {};
{click:{dependencies:['click'],phasedRegistrationNames:{bubbled: "onClick"captured: "onClickCapture"},isInteractive: true} }
const namesToPlugins: NamesToPlugins = {};
{SimpleEventPlugin:{eventTypes:{},extractEvents:function,otherProps},// ...其他插件 }
export const registrationNameModules = {};
{onClick:{eventTypes:{},extractEvents:function,otherProps},... }
export const registrationNameDependencies = {};
{onClick: ["click"],onChange: ["blur", "change", "click", "focus", "input", "keydown",keyup", "selectionchange],.... }
-
把这几个变量维护好之后,后面可以很方便的进行一些事件绑定相关的操作
-
对于事件注入这个模块,是初始化事件的前置任务
-
重点关注最终拿到的几个完成注册之后的变量的数据格式
-
以上就是把整个事件的插件它注入到react事件系统当中的过程
相关文章:
React16源码: React中event事件系统初始化源码实现
event 事件系统初始化 1 )概述 react事件系统比较的复杂,它是基于dom的事件系统在dom事件系统上面进行了一个深度的封装它里面的很多实现逻辑都是自由的一套在初始化 react-dom 的源码的时候,会为react的事件系统注入 reactdom 相关的一些插…...

Qt6入门教程 15:QRadioButton
目录 一.简介 二.常用接口 三.实战演练 1.径向渐变 2.QSS贴图 3.开关效果 4.非互斥 一.简介 QRadioButton控件提供了一个带有文本标签的单选按钮。 QRadioButton是一个可以切换选中(checked)或未选中(unchecked)状态的选项…...

Json序列化和反序列化 笔记
跟着施磊老师学C 下载:GitHub - nlohmann/json: JSON for Modern C 在single_include/nlohmann里头有一个json.hpp,把它放到我们的项目中就可以了 #include "json.hpp" using json nlohmann::json;#include <iostream> #include <…...

新媒体与传媒行业数据分析实践:从网络爬虫到文本挖掘的综合应用,以“中国文化“为主题
大家好,我是八块腹肌的小胖, 下面将围绕微博“中国文化”以数据分析、数据处理、建模及可视化等操作 目录 1、数据获取 2、数据处理 3、词频统计及词云展示 4、文本聚类分析 5、文本情感倾向性分析 6、情感倾向演化分析 7、总结 1、数据获取 本…...

Visual Studio使用Git忽略不想上传到远程仓库的文件
前言 作为一个.NET开发者而言,有着宇宙最强IDE:Visual Studio加持,让我们的开发效率得到了更好的提升。我们不需要担心环境变量的配置和其他代码管理工具,因为Visual Studio有着众多的拓展工具。废话不多说,直接进入正…...

Nginx简单阐述及安装配置
目录 一.什么是Nginx 二.Nginx优缺点 1.优点 2.缺点 三.正向代理与反向代理 1.正向代理 2.反向代理 四.安装配置 1.添加Nginx官方yum源 2.使用yum安装Nginx 3.配置防火墙 4.启动后效果 一.什么是Nginx Nginx(“engine x”)是一个高性能的HTTP…...

【遥感入门系列】遥感分类技术之遥感解译
遥感的最终成果之一就是从遥感图像上获取信息,遥感分类是获取信息的重要手段。同时遥感图像分类也是目前遥感技术中的热点研究方向,每年都有新的分类方法推出。 本小节主要内容: 遥感分类基本概念常见遥感分类方法 1 遥感分类概述 遥感图…...

解决:IDEA无法下载源码,Cannot download sources, sources not found for: xxxx
原因 Maven版本太高,遇到http协议的镜像网站会阻塞,要改为使用https协议的镜像网站 解决方案 1.打开设置 2. 拿到settings.xml路径 3. 将步骤2里箭头2的User settings file:settings.xml打开,作以下修改 保存即可。如果还不行…...
什么是IDE,新手改如何选择IDE?
IDE 是 Integrated Development Environment(集成开发环境)的缩写,它是一种软件应用程序,为程序员提供了一站式的开发环境,整合了多种工具和服务,以便高效地创建、修改、编译、调试和运行软件程序。IDE 集成…...

springBoot+Vue汽车销售源码
源码描述: 汽车销售管理系统源码基于spring boot以及Vue开发。 针对汽车销售提供客户信息、车辆信息、订单信息、销售人员管理、 财务报表等功能,提供经理和销售两种角色进行管理。 技术架构: idea(推荐)、jdk1.8、mysql5.X(不能为8驱动不匹配)、ma…...
FPS游戏框架漫谈第五天
今天想了想整理下AnimatorManager 他的职责是负责动画的播放,那么在介绍该对象具备的对外接口,必须先介绍下拥有动画的对象他是怎么管理动画数据的,打个比方如果我们一个把武器需要播放开火动画,那么我们基于unity引擎可视化动画编…...
83.如何设计高可用系统
文章目录 一、简介二、导致系统不可用的常见原因三、高可用系统设计基本原则四、容错性设计五、弹性伸缩六、可观测七、安全防护设计八、自动化 一、简介 什么是高可用 高可用是指系统在面对各种故障和异常情况时,仍能够提供稳定、可靠的服务。对于企业和用户而言&…...

Map和Set讲解
🎥 个人主页:Dikz12📕格言:那些在暗处执拗生长的花,终有一日会馥郁传香欢迎大家👍点赞✍评论⭐收藏 目录 集合框架 模型 Set 常见方法和说明 Set总结 Map说明 Map常见方法和说明 Map 中HashMap的 …...

PHP集成开发环境 PhpStorm 2023 for mac中文激活版
PhpStorm 2023 for Mac是一款功能强大的PHP集成开发环境(IDE),旨在帮助开发者更高效地编写、调试和测试PHP代码。该软件针对Mac用户设计,提供了丰富的功能和工具,以简化开发过程并提高开发效率。 软件下载:…...

数学建模 - 线性规划入门:Gurobi + python
在工程管理、经济管理、科学研究、军事作战训练及日常生产生活等众多领域中,人们常常会遇到各种优化问题。例如,在生产经营中,我们总是希望制定最优的生产计划,充分利用已有的人力、物力资源,获得最大的经济效益&#…...

SpringBoot security 安全认证(二)——登录拦截器
本节内容:实现登录拦截器,除了登录接口之外所有接口访问都要携带Token,并且对Token合法性进行验证,实现登录状态的保持。 核心内容: 1、要实现登录拦截器,从Request请求中获取token,从缓存中获…...

详解WebRTC rtc::Thread实现
rtc::Thread介绍 rtc::Thread类不仅仅实现了线程这个执行器(比如posix底层调用pthread相关接口创建线程,管理线程等),还包括消息队列(message_queue)的实现,rtc::Thread启动后就作为一个永不停止的event l…...

阿赵UE学习笔记——13、贴花
阿赵UE学习笔记目录 大家好,我是阿赵。 继续学习虚幻引擎的使用。这次介绍一种特殊的材质类型,贴花。 一、获取贴花资源 在没有分析贴花的原理之前,可以先去获得一些免费的贴花资源来使用,比如在Quixel上面就有专门的一个资源…...

简单说说mysql的日志
今天我们通过mysql日志了解mysqld的错误日志、慢查询日志、二进制日志,redolog, undolog等。揭示它们的作用和用途,让我们工作中更能驾驭mysql。 redo 日志 如果mysql事务提交后发生了宕机现象,那怎么保证数据的持久性与完整性?…...

如何在CentOS安装DataEase数据分析服务并实现远程访问管理界面
如何在CentOS安装DataEase数据分析服务并实现远程访问管理界面 前言1. 安装DataEase2. 本地访问测试3. 安装 cpolar内网穿透软件4. 配置DataEase公网访问地址5. 公网远程访问Data Ease6. 固定Data Ease公网地址 🌈你好呀!我是 是Yu欸 🌌 202…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
云原生周刊:k0s 成为 CNCF 沙箱项目
开源项目推荐 HAMi HAMi(原名 k8s‑vGPU‑scheduler)是一款 CNCF Sandbox 级别的开源 K8s 中间件,通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度,为容器提供统一接口,实现细粒度资源配额…...

算术操作符与类型转换:从基础到精通
目录 前言:从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符:、-、*、/、% 赋值操作符:和复合赋值 单⽬操作符:、--、、- 前言:从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...
JS红宝书笔记 - 3.3 变量
要定义变量,可以使用var操作符,后跟变量名 ES实现变量初始化,因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符,可以创建一个全局变量 如果需要定义…...

Win系统权限提升篇UAC绕过DLL劫持未引号路径可控服务全检项目
应用场景: 1、常规某个机器被钓鱼后门攻击后,我们需要做更高权限操作或权限维持等。 2、内网域中某个机器被钓鱼后门攻击后,我们需要对后续内网域做安全测试。 #Win10&11-BypassUAC自动提权-MSF&UACME 为了远程执行目标的exe或者b…...