当前位置: 首页 > news >正文

7.1 Windows驱动开发:内核监控进程与线程回调

在前面的文章中LyShark一直在重复的实现对系统底层模块的枚举,今天我们将展开一个新的话题,内核监控,我们以监控进程线程创建为例,在Win10系统中监控进程与线程可以使用微软提供给我们的两个新函数来实现,此类函数的原理是创建一个回调事件,当有进程或线程被创建或者注销时,系统会通过回调机制将该进程相关信息优先返回给我们自己的函数待处理结束后再转向系统层。

PsSetCreateProcessNotifyRoutineEx和PsSetCreateThreadNotifyRoutine是Windows操作系统提供的两个内核回调函数,它们允许开发者在进程或线程发生创建事件时拦截并处理这些事件。这两个函数提供的回调机制是操作系统提供的最基本、最常用的内核监控进程与线程的方式。

PsSetCreateProcessNotifyRoutineEx和PsSetCreateThreadNotifyRoutine的使用方式和参数类型类似,它们都需要开发者提供一个回调函数,当进程或线程被创建时,操作系统会调用这个回调函数。这个回调函数需要满足一定的约束条件,例如不能阻塞或挂起进程或线程的创建或访问,不能调用一些内核API函数等。

PsSetCreateProcessNotifyRoutineEx和PsSetCreateThreadNotifyRoutine的主要区别在于它们所监控的事件不同。PsSetCreateProcessNotifyRoutineEx用于监控进程的创建事件,当有新的进程被创建时,操作系统会调用注册的回调函数。而PsSetCreateThreadNotifyRoutine用于监控线程的创建事件,当有新的线程被创建时,操作系统会调用注册的回调函数。

内核监控进程PsSetCreateProcessNotifyRoutineEx和线程PsSetCreateThreadNotifyRoutine回调在安全软件、系统监控和调试工具等领域有着广泛的应用。需要注意的是,在Windows 8及更高版本的操作系统中,微软推荐开发者使用ExRegisterCallback和ExUnregisterCallback函数进行回调的注册和注销。

进程回调默认会设置CreateProcess通知,而线程回调则会设置CreateThread通知,我们来看ARK工具中的枚举效果。

  • 通常情况下:
    • PsSetCreateProcessNotifyRoutineEx 用于监控进程
    • PsSetCreateThreadNotifyRoutine 用于监控线程

PsSetCreateProcessNotifyRoutineEx

监控进程的启动与退出可以使用 PsSetCreateProcessNotifyRoutineEx来创建回调,当新进程创建时会优先执行回调,我们看下微软是如何定义的结构。

// 参数1: 新进程回调函数
// 参数2: 是否注销
NTSTATUS PsSetCreateProcessNotifyRoutineEx([in] PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine,[in] BOOLEAN                           Remove
);

如上,该函数只有两个参数,第一个参数是回调函数,第二个参数是是否注销,通常在驱动退出时可以传入TRUE对该回调进行注销,通常情况下如果驱动关闭,则必须要注销回调,而对于MyLySharkCreateProcessNotifyEx自定义回调来说,则需要指定三个必须要有的参数传递。

// 参数1: 新进程的EProcess
// 参数2: 新进程PID
// 参数3: 新进程详细信息 (仅在创建进程时有效)VOID MyLySharkCreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)

根据如上函数定义,就可以实现监控功能了,例如我们监控如果进程名是lyshark.exe则直接CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL禁止该进程打开。

#include <ntifs.h>// 两个未公开函数导出
NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);// 通过PID获得进程名
PCHAR GetProcessNameByProcessId(HANDLE ProcessId)
{NTSTATUS st = STATUS_UNSUCCESSFUL;PEPROCESS ProcessObj = NULL;PCHAR string = NULL;st = PsLookupProcessByProcessId(ProcessId, &ProcessObj);if (NT_SUCCESS(st)){string = PsGetProcessImageFileName(ProcessObj);ObfDereferenceObject(ProcessObj);}return string;
}// 绕过签名检查
BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject)
{
#ifdef _WIN64typedef struct _KLDR_DATA_TABLE_ENTRY{LIST_ENTRY listEntry;ULONG64 __Undefined1;ULONG64 __Undefined2;ULONG64 __Undefined3;ULONG64 NonPagedDebugInfo;ULONG64 DllBase;ULONG64 EntryPoint;ULONG SizeOfImage;UNICODE_STRING path;UNICODE_STRING name;ULONG   Flags;USHORT  LoadCount;USHORT  __Undefined5;ULONG64 __Undefined6;ULONG   CheckSum;ULONG   __padding1;ULONG   TimeDateStamp;ULONG   __padding2;} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#elsetypedef struct _KLDR_DATA_TABLE_ENTRY{LIST_ENTRY listEntry;ULONG unknown1;ULONG unknown2;ULONG unknown3;ULONG unknown4;ULONG unknown5;ULONG unknown6;ULONG unknown7;UNICODE_STRING path;UNICODE_STRING name;ULONG   Flags;} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#endifPKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;pLdrData->Flags = pLdrData->Flags | 0x20;return TRUE;
}// 进程回调函数
VOID My_LyShark_Com_CreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{char ProcName[16] = { 0 };if (CreateInfo != NULL){strcpy_s(ProcName, 16, PsGetProcessImageFileName(Process));DbgPrint("[LyShark] 父进程ID: %ld | 父进程名: %s | 进程名: %s | 进程路径:%wZ \n", CreateInfo->ParentProcessId, GetProcessNameByProcessId(CreateInfo->ParentProcessId), PsGetProcessImageFileName(Process), CreateInfo->ImageFileName);// 判断是否为指定进程if (0 == _stricmp(ProcName, "lyshark.exe")){// 禁止打开CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL;}}else{strcpy_s(ProcName, 16, PsGetProcessImageFileName(Process));DbgPrint("[LyShark] 进程[ %s ] 退出了, 程序被关闭", ProcName);}
}VOID UnDriver(PDRIVER_OBJECT driver)
{DWORD32 ref = 0;// 注销进程回调ref = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)My_LyShark_Com_CreateProcessNotifyEx, TRUE);DbgPrint("[lyshark] 注销进程回调: %d \n", ref);
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{NTSTATUS status;// 绕过签名检查// LINKER_FLAGS=/INTEGRITYCHECKBypassCheckSign(Driver);DbgPrint("hello lyshark \n");// 创建进程回调// 参数1: 新进程的EProcess// 参数2: 新进程PID// 参数3: 新进程详细信息 (仅在创建进程时有效)status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)My_LyShark_Com_CreateProcessNotifyEx, FALSE);if (!NT_SUCCESS(status)){DbgPrint("[lyshark] 创建进程回调错误");}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

编译并运行这个驱动程序,我们可以在ARK工具中看到这个驱动所加载的CreateProcess的回调事件。

当驱动加载后,如果你尝试打开lyshark.exe那么会提示连接的设备没有发挥作用,我们则成功拦截了这次打开,当然如果在打开进程之前扫描其特征并根据特征拒绝进程打开,那么就可以实现一个简单的防恶意程序,进程监控在防恶意程序中也是用的最多的。

PsSetCreateThreadNotifyRoutine

说完了PsSetCreateProcessNotifyRoutineEx回调的使用方式,LyShark将继续带大家看看线程监控如何实现,监控线程创建与监控进程差不多,检测线程需要调用PsSetCreateThreadNotifyRoutine 创建回调函数,之后就可监控系统所有线程的创建,具体实现代码如下。

#include <ntifs.h>// 两个未公开函数导出
NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);
NTKERNELAPI NTSTATUS PsLookupThreadByThreadId(HANDLE ThreadId, PETHREAD *Thread);// 绕过签名检查
BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject)
{
#ifdef _WIN64typedef struct _KLDR_DATA_TABLE_ENTRY{LIST_ENTRY listEntry;ULONG64 __Undefined1;ULONG64 __Undefined2;ULONG64 __Undefined3;ULONG64 NonPagedDebugInfo;ULONG64 DllBase;ULONG64 EntryPoint;ULONG SizeOfImage;UNICODE_STRING path;UNICODE_STRING name;ULONG   Flags;USHORT  LoadCount;USHORT  __Undefined5;ULONG64 __Undefined6;ULONG   CheckSum;ULONG   __padding1;ULONG   TimeDateStamp;ULONG   __padding2;} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#elsetypedef struct _KLDR_DATA_TABLE_ENTRY{LIST_ENTRY listEntry;ULONG unknown1;ULONG unknown2;ULONG unknown3;ULONG unknown4;ULONG unknown5;ULONG unknown6;ULONG unknown7;UNICODE_STRING path;UNICODE_STRING name;ULONG   Flags;} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#endifPKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;pLdrData->Flags = pLdrData->Flags | 0x20;return TRUE;
}// 线程回调函数
VOID MyCreateThreadNotify(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN CreateInfo)
{PEPROCESS eprocess = NULL;PETHREAD ethread = NULL;UCHAR *pWin32Address = NULL;// 通过此函数拿到程序的EPROCESS结构PsLookupProcessByProcessId(ProcessId, &eprocess);PsLookupThreadByThreadId(ThreadId, &ethread);if (CreateInfo){DbgPrint("[lyshark] 线程TID: %1d | 所属进程名: %s | 进程PID: %1d \n", ThreadId, PsGetProcessImageFileName(eprocess), PsGetProcessId(eprocess));/*if (0 == _stricmp(PsGetProcessImageFileName(eprocess), "lyshark.exe")){DbgPrint("线程TID: %1d | 所属进程名: %s | 进程PID: %1d \n", ThreadId, PsGetProcessImageFileName(eprocess), PsGetProcessId(eprocess));// dt _kthread// 寻找里面的 Win32StartAddress 并写入retpWin32Address = *(UCHAR**)((UCHAR*)ethread + 0x1c8);if (MmIsAddressValid(pWin32Address)){*pWin32Address = 0xC3;}}*/}else{DbgPrint("[LyShark] %s 线程已退出...", ThreadId);}if (eprocess)ObDereferenceObject(eprocess);if (ethread)ObDereferenceObject(ethread);
}VOID UnDriver(PDRIVER_OBJECT driver)
{NTSTATUS status;// 注销进程回调status = PsRemoveCreateThreadNotifyRoutine(MyCreateThreadNotify);
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{NTSTATUS status;DbgPrint("hello lyshark \n");// 绕过签名检查// LINKER_FLAGS=/INTEGRITYCHECKBypassCheckSign(Driver);// 创建线程回调// 参数1: 新线程ProcessID// 参数2: 新线程ThreadID// 参数3: 线程创建/退出标志status = PsSetCreateThreadNotifyRoutine(MyCreateThreadNotify);if (!NT_SUCCESS(status)){DbgPrint("创建线程回调错误");}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

运行后则可监控到系统总所有线程的创建与退出,效果如下所示:

相关文章:

7.1 Windows驱动开发:内核监控进程与线程回调

在前面的文章中LyShark一直在重复的实现对系统底层模块的枚举&#xff0c;今天我们将展开一个新的话题&#xff0c;内核监控&#xff0c;我们以监控进程线程创建为例&#xff0c;在Win10系统中监控进程与线程可以使用微软提供给我们的两个新函数来实现&#xff0c;此类函数的原…...

基于ssm的汽车论坛管理系统设计与实现

基于ssm的汽车论坛管理系统设计与实现 摘要&#xff1a;信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题…...

实习开发日志经验总结(一)

文章目录 前言实习日志经验总结 前言 自己之前实习过程中遇到的问题以及相应的解决过程&#xff0c;我都有记录形成比较凌乱的实习日志。故想在整个实习日志的基础上&#xff0c;提炼一些技术知识点或者是解决问题的思路。考虑到实习项目的不方便公开性&#xff0c;所以会隐去…...

【Unity基础】8.简单场景的搭建

【Unity基础】8.简单场景的搭建 大家好&#xff0c;我是Lampard~~ 欢迎来到Unity基础系列博客&#xff0c;所学知识来自B站阿发老师~感谢 &#xff08;一&#xff09;场景资源 &#xff08;1&#xff09;Import资源包 今天我们将手动去搭一个简单的场景&#xff0c;当…...

傅里叶变换及其在机器学习中的应用

​​​​​​​一、介绍 傅立叶变换是一种数学技术&#xff0c;在各个科学和工程领域发挥着关键作用&#xff0c;其应用范围从信号处理到量子力学。近年来&#xff0c;它在机器学习领域发现了新的意义。本文探讨了傅里叶变换的基础知识及其在机器学习应用中日益增长的重要性。 …...

xorm源码学习

文章目录 XORM源码浅析及实践ORMORM vs. SQLXORM软件架构 ORM 引擎 Engine——DBM*core.DB Golang&#xff1a;database/sql 源码基本结构连接复用&#xff0c;提高性能。增加数据库连接池数量连接管理 database/sql主要内容&#xff1a;sql.DB创建数据库连接sql.Open()DB.conn…...

Vue3中的<script setup>和<script>的区别

相同点 在一个 Vue3 单文件组件 (SFC)中&#xff0c;<script setup> 和 <script> 它们各自最多只能存在一个。 不同点 <script setup> 这个脚本块将被预处理为组件的 setup() 函数&#xff0c;这意味着它将为每一个(也可以说每一次)组件实例都执行。 <…...

Docker笔记-Docker搭建最新版zabbix服务端(2023-07-31)

前言 一开始问chartgpt上&#xff0c;搭建的思路是对的&#xff0c;但命令和细节有问题&#xff0c;最后还是依靠StackOverflow解决的。一开始在amd的linux上搭建好docker版的zabbix&#xff0c;但放到arm的机器上就报错了&#xff0c;原因是指令集不匹配&#xff0c;最后跑到…...

QT配合CSS隐藏按钮

第一种方法 在Qt的CSS样式表中&#xff0c;使用 visibility 属性来隐藏按钮。设置 visibility 为 hidden 不可见&#xff0c;而设置为 visible 则可见。 隐藏所有 QPushButton QPushButton {visibility: hidden; }隐藏特定的按钮&#xff0c;用按钮的名称或样式类进行定位就…...

2023亚太地区数学建模C题思路分析+模型+代码+论文

目录 1.2023亚太地区各题思路模型&#xff1a;比赛开始后&#xff0c;第一时间更新&#xff0c;获取见文末名片 3 常见数模问题常见模型分类 3.1 分类问题 3.2 优化问题 详细思路见此名片&#xff0c;开赛第一时间更新 1.亚太地区数学建模ABC题思路模型&#xff1a;9比赛开…...

Linguistic Steganalysis in Few-Shot Scenario论文阅读笔记

TIFS期刊 A类期刊 新知识点 Introduction Linguistic Steganalysis in Few-Shot Scenario模型是个预训练方法。 评估了四种文本加密分析方法&#xff0c;TS-CSW、TS-RNN、Zou、SeSy&#xff0c;用于分析和训练的样本都由VAE-Stego生产(编码方式使用AC编码)。 实验是对比在少样…...

详细学习Pyqt5的4种项目部件(Item Widget)

Pyqt5相关文章: 快速掌握Pyqt5的三种主窗口 快速掌握Pyqt5的2种弹簧 快速掌握Pyqt5的5种布局 快速弄懂Pyqt5的5种项目视图&#xff08;Item View&#xff09; 快速弄懂Pyqt5的4种项目部件&#xff08;Item Widget&#xff09; 快速掌握Pyqt5的6种按钮 快速掌握Pyqt5的10种容器&…...

notepad++ 插件JSONView安装

1&#xff0c;前提 开发过程中经常需要处理json格式语句&#xff0c;需要对json数据格式化处理&#xff0c;因为使用的是虚拟机内开发&#xff0c;所以没法连接外网&#xff0c;只能在本地电脑下载插件后&#xff0c;然后上传到虚拟机中&#xff0c;进行安装使用。 2&#xf…...

AKConv:具有任意采样形状和任意数目参数的卷积核

文章目录 摘要1、引言2、相关工作3、方法3.1、定义初始采样位置3.2、可变卷积操作3.3、扩展AKConv3.3、扩展AKConv 4、实验4.1、在COCO2017上的目标检测实验4.2、在VOC 712上的目标检测实验4.3、在VisDrone-DET2021上的目标检测实验4.4、比较实验4.5、探索初始采样形状 5、分析…...

如何使用C++开发集群服务

开发集群服务需要掌握以下技术&#xff1a; 分布式系统原理&#xff1a;了解集群的概念、工作原理、负载均衡、容错等相关概念。 网络编程&#xff1a;掌握Socket编程和HTTP协议等。 C编程&#xff1a;熟练掌握C语言的基础知识和STL等常用库。 多线程编程&#xff1a;了解线…...

docker安装以及idea访问docker

其他目录&#xff1a; docker 安装环境: https://blog.csdn.net/gd898989/article/details/134570167 docker 打包java包&#xff0c;并运行&#xff08;有空更新&#xff09; url “” docker 打包vue &#xff08;有空更新&#xff09; url “” docker 多服务 &#xff08;…...

激光切割头组件中喷嘴的作用是什么

喷嘴是一个不可忽视的部件。尽管喷嘴并不起眼&#xff0c;却有着重要的作用&#xff1b;喷嘴一般是与激光切割头同轴的&#xff0c;且形状多样&#xff1a;圆柱形、锥形、缩放型等。 喷嘴的口径尺寸时不相同的&#xff0c;大口径的喷嘴对聚焦来的激光束没有很严苛的要求;而口径…...

腾讯云双11活动最后一天,错过再等一年!

腾讯云双11活动已经进入尾声&#xff0c;距离活动结束仅剩最后一天&#xff0c;记得抓住这次上云好时机&#xff0c;错过这次&#xff0c;就要等到下一年才能享受到这样的优惠力度了&#xff01; 活动地址&#xff1a; 点此直达腾讯云双11活动主会场 活动详情&#xff1a; 1…...

Java实现飞翔的鸟小游戏

Java实现飞翔的鸟小游戏 1.准备工作 创建一个新的Java项目命名为“飞翔的鸟”&#xff0c;并在src中创建一个包命名为“com.qiku.bird"&#xff0c;在这个包内分别创建4个类命名为**“Bird”、“BirdGame”、“Column”、“Ground”&#xff0c;并向需要的图片**素材导入…...

Python网络请求初级篇:使用Requests库抓取和解析数据

在网络编程中&#xff0c;请求和接收数据是最常见的任务之一。Python的Requests库提供了丰富的功能&#xff0c;使得HTTP请求变得非常简单。在本文中&#xff0c;我们将了解如何使用Requests库发起HTTP请求&#xff0c;并解析返回的数据。 一、安装Requests库 首先&#xff0…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

大模型多显卡多服务器并行计算方法与实践指南

一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列&#xff0c;以便知晓哪些列包含有价值的数据&#xff0c;…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...

接口自动化测试:HttpRunner基础

相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具&#xff0c;支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议&#xff0c;涵盖接口测试、性能测试、数字体验监测等测试类型…...