5.10 Windows驱动开发:摘除InlineHook内核钩子

在笔者上一篇文章《内核层InlineHook挂钩函数》中介绍了通过替换函数头部代码的方式实现Hook挂钩,对于ARK工具来说实现扫描与摘除InlineHook钩子也是最基本的功能,此类功能的实现一般可在应用层进行,而驱动层只需要保留一个读写字节的函数即可,将复杂的流程放在应用层实现是一个非常明智的选择,与《内核实现进程反汇编》中所使用的读写驱动基本一致,本篇文章中的驱动只保留两个功能,控制信号IOCTL_GET_CUR_CODE用于读取函数的前16个字节的内存,信号IOCTL_SET_ORI_CODE则用于设置前16个字节的内存。

之所以是前16个字节是因为一般的内联Hook只需要使用两条指令就可实现劫持,如下是通用ARK工具扫描到的被挂钩函数的样子。

首先将内核驱动程序代码放到如下,内核驱动程序没有任何特别的,仅仅只是一个通用驱动模板,在其基础上使用CR3读写,如果不理解CR3读写的原理您可以去看《内核CR3切换读写内存》这一篇中的详细介绍。

#include <ntifs.h>
#include <intrin.h>
#include <windef.h>#define DEVICE_NAME         L"\\Device\\WinDDK"
#define LINK_NAME           L"\\DosDevices\\WinDDK"
#define LINK_GLOBAL_NAME    L"\\DosDevices\\Global\\WinDDK"// 控制信号 IOCTL_GET_CUR_CODE 用于读 | IOCTL_SET_ORI_CODE 用于写
#define IOCTL_GET_CUR_CODE  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_SET_ORI_CODE  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)// 引用__readcr0等函数必须增加
#pragma intrinsic(_disable)
#pragma intrinsic(_enable)// 定义读写结构体
typedef struct
{PVOID Address;ULONG64 Length;UCHAR data[256];
} KF_DATA, *PKF_DATA;KIRQL g_irql;// 关闭写保护
void WPOFFx64()
{ULONG64 cr0;g_irql = KeRaiseIrqlToDpcLevel();cr0 = __readcr0();cr0 &= 0xfffffffffffeffff;__writecr0(cr0);_disable();
}// 开启写保护
void WPONx64()
{ULONG64 cr0;cr0 = __readcr0();cr0 |= 0x10000;_enable();__writecr0(cr0);KeLowerIrql(g_irql);
}// 设备创建时触发
NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{pIrp->IoStatus.Status = STATUS_SUCCESS;pIrp->IoStatus.Information = 0;DbgPrint("[LyShark] 设备已创建 \n");IoCompleteRequest(pIrp, IO_NO_INCREMENT);return STATUS_SUCCESS;
}// 设备关闭时触发
NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{pIrp->IoStatus.Status = STATUS_SUCCESS;pIrp->IoStatus.Information = 0;DbgPrint("[LyShark] 设备已关闭 \n");IoCompleteRequest(pIrp, IO_NO_INCREMENT);return STATUS_SUCCESS;
}// 主派遣函数
NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;PIO_STACK_LOCATION pIrpStack;ULONG uIoControlCode;PVOID pIoBuffer;ULONG uInSize;ULONG uOutSize;// 获取当前设备栈pIrpStack = IoGetCurrentIrpStackLocation(pIrp);uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;// 获取缓冲区pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;// 获取缓冲区长度uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;// 输出缓冲区长度uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;switch (uIoControlCode){// 读内存case IOCTL_GET_CUR_CODE:{KF_DATA dat = { 0 };// 将缓冲区格式化为KF_DATA结构体RtlCopyMemory(&dat, pIoBuffer, 16);WPOFFx64();// 将数据写回到缓冲区RtlCopyMemory(pIoBuffer, dat.Address, dat.Length);WPONx64();status = STATUS_SUCCESS;break;}// 写内存case IOCTL_SET_ORI_CODE:{KF_DATA dat = { 0 };// 将缓冲区格式化为KF_DATA结构体RtlCopyMemory(&dat, pIoBuffer, sizeof(KF_DATA));WPOFFx64();// 将数据写回到缓冲区RtlCopyMemory(dat.Address, dat.data, dat.Length);WPONx64();status = STATUS_SUCCESS;break;}}if (status == STATUS_SUCCESS)pIrp->IoStatus.Information = uOutSize;elsepIrp->IoStatus.Information = 0;pIrp->IoStatus.Status = status;IoCompleteRequest(pIrp, IO_NO_INCREMENT);return status;
}// 驱动卸载
VOID DriverUnload(PDRIVER_OBJECT pDriverObj)
{UNICODE_STRING strLink;// 删除符号链接卸载设备RtlInitUnicodeString(&strLink, LINK_NAME);IoDeleteSymbolicLink(&strLink);IoDeleteDevice(pDriverObj->DeviceObject);
}// 驱动程序入口
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
{NTSTATUS status = STATUS_SUCCESS;UNICODE_STRING ustrLinkName;UNICODE_STRING ustrDevName;PDEVICE_OBJECT pDevObj;// 初始化派遣函数pDriverObj->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;pDriverObj->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl;DbgPrint("hello lysahrk.com \n");// 初始化设备名RtlInitUnicodeString(&ustrDevName, DEVICE_NAME);// 创建设备status = IoCreateDevice(pDriverObj, 0, &ustrDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);if (!NT_SUCCESS(status)){return status;}// 创建符号链接RtlInitUnicodeString(&ustrLinkName, LINK_NAME);status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName);if (!NT_SUCCESS(status)){IoDeleteDevice(pDevObj);return status;}pDriverObj->DriverUnload = DriverUnload;return STATUS_SUCCESS;
}

接着来分析下应用层做了什么,首先GetKernelBase64函数的作用,该函数内部通过GetProcAddress()函数动态寻找到ZwQuerySystemInformation()函数的内存地址(此函数未被到处所以只能动态找到),找到后调用ZwQuerySystemInformation()直接拿到系统中的所有模块信息,通过pSystemModuleInformation->Module[0].Base得到系统中第一个模块的基地址,此模块就是ntoskrnl.exe,该模块也是系统运行后的第一个启动的,此时我们即可拿到KernelBase也就是系统内存中的基地址。

此时通过LoadLibraryExA()函数动态加载,此时加载的是磁盘中的被Hook函数的所属模块,获得映射地址后将此地址装入hKernel变量内,此时我们拥有了内存中的KernelBase以及磁盘中加载的hKernel,接着调用RepairRelocationTable()让两者的重定位表保持一致。

此时当用户调用GetSystemRoutineAddress()则执行如下流程,想要获取当前内存地址,则需要使用当前内存中的KernelBase模块基址加上通过GetProcAddress()动态获取到的磁盘基址中的函数地址减去磁盘中的基地址,将内存中的KernelBase加上磁盘中的相对偏移就得到了当前内存中加载函数的实际地址。

  • address1 = KernelBase + (ULONG64)GetProcAddress(hKernel, “NtWriteFile”) - (ULONG64)hKernel
  • address2 = KernelBase - (ULONG64)hKernel + (ULONG64)GetProcAddress(hKernel, “NtWriteFile”)

调用GetOriginalMachineCode()则用于获取相对偏移地址,该地址的获取方式如下,用户传入一个Address当前地址,该地址减去KernelBase内存中的基址,然后再加上hKernel磁盘加载的基址来获取到相对偏移。

  • OffsetAddress = Address - KernelBase + hKernel

有了这两条信息那么功能也就实现了,通过GetOriginalMachineCode()得到指定内存地址处原始机器码,通过GetCurrentMachineCode()得到当前内存机器码,两者通过memcmp()函数比对即可知道是否被挂钩了,如果被挂钩则可以通过CR3切换将原始机器码覆盖到特定位置替换即可,这段程序的完整代码如下;

#include <stdio.h>
#include <Windows.h>#pragma comment(lib,"user32.lib")
#pragma comment(lib,"Advapi32.lib")#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#endif#define BYTE_ARRAY_LENGTH 16
#define SystemModuleInformation 11
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)typedef long(__stdcall *ZWQUERYSYSTEMINFORMATION)
(IN ULONG SystemInformationClass,IN PVOID SystemInformation,IN ULONG SystemInformationLength,IN PULONG ReturnLength OPTIONAL
);typedef struct
{ULONG Unknow1;ULONG Unknow2;ULONG Unknow3;ULONG Unknow4;PVOID Base;ULONG Size;ULONG Flags;USHORT Index;USHORT NameLength;USHORT LoadCount;USHORT ModuleNameOffset;char ImageName[256];
} SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY;typedef struct
{ULONG Count;SYSTEM_MODULE_INFORMATION_ENTRY Module[1];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;typedef struct
{PVOID Address;ULONG64 Length;UCHAR data[256];
} KF_DATA, *PKF_DATA;HANDLE hDriver = 0;
HMODULE hKernel = 0;
ULONG64 KernelBase = 0;
CHAR NtosFullName[260] = { 0 };// 生成控制信号
DWORD CTL_CODE_GEN(DWORD lngFunction)
{return (FILE_DEVICE_UNKNOWN * 65536) | (FILE_ANY_ACCESS * 16384) | (lngFunction * 4) | METHOD_BUFFERED;
}// 发送控制信号的函数
BOOL IoControl(HANDLE hDrvHandle, DWORD dwIoControlCode, PVOID lpInBuffer, DWORD nInBufferSize, PVOID lpOutBuffer, DWORD nOutBufferSize)
{DWORD lDrvRetSize;return DeviceIoControl(hDrvHandle, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, &lDrvRetSize, 0);
}// 动态获取ntdll.dll模块的基地址
ULONG64 GetKernelBase64(PCHAR NtosName)
{ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation;PSYSTEM_MODULE_INFORMATION pSystemModuleInformation;ULONG NeedSize, BufferSize = 0x5000;PVOID pBuffer = NULL;NTSTATUS Result;// 该函数只能通过动态方式得到地址ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(GetModuleHandleA("ntdll.dll"), "ZwQuerySystemInformation");do{pBuffer = malloc(BufferSize);if (pBuffer == NULL) return 0;// 查询系统中的所有模块信息Result = ZwQuerySystemInformation(SystemModuleInformation, pBuffer, BufferSize, &NeedSize);if (Result == STATUS_INFO_LENGTH_MISMATCH){free(pBuffer);BufferSize *= 2;}else if (!NT_SUCCESS(Result)){free(pBuffer);return 0;}} while (Result == STATUS_INFO_LENGTH_MISMATCH);// 取模块信息结构pSystemModuleInformation = (PSYSTEM_MODULE_INFORMATION)pBuffer;// 得到模块基地址ULONG64 ret = (ULONG64)(pSystemModuleInformation->Module[0].Base);// 拷贝模块名if (NtosName != NULL){strcpy(NtosName, pSystemModuleInformation->Module[0].ImageName + pSystemModuleInformation->Module[0].ModuleNameOffset);}free(pBuffer);return ret;
}// 判断并修复重定位表
BOOL RepairRelocationTable(ULONG64 HandleInFile, ULONG64 BaseInKernel)
{PIMAGE_DOS_HEADER       pDosHeader;PIMAGE_NT_HEADERS64     pNtHeader;PIMAGE_BASE_RELOCATION  pRelocTable;ULONG i, dwOldProtect;// 得到DOS头并判断是否符合DOS规范pDosHeader = (PIMAGE_DOS_HEADER)HandleInFile;if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE){return FALSE;}// 得到Nt头pNtHeader = (PIMAGE_NT_HEADERS64)((ULONG64)HandleInFile + pDosHeader->e_lfanew);// 是否存在重定位表if (pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size){// 获取到重定位表基地址pRelocTable = (PIMAGE_BASE_RELOCATION)((ULONG64)HandleInFile + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);do{// 得到重定位号ULONG   numofReloc = (pRelocTable->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;SHORT   minioffset = 0;// 得到重定位数据PUSHORT pRelocData = (PUSHORT)((ULONG64)pRelocTable + sizeof(IMAGE_BASE_RELOCATION));// 循环或直接判断*pRelocData是否为0也可以作为结束标记for (i = 0; i<numofReloc; i++){// 需要重定位的地址PULONG64 RelocAddress;// 重定位的高4位是重定位类型,判断重定位类型if (((*pRelocData) >> 12) == IMAGE_REL_BASED_DIR64){// 计算需要进行重定位的地址// 重定位数据的低12位再加上本重定位块头的RVA即真正需要重定位的数据的RVAminioffset = (*pRelocData) & 0xFFF; // 小偏移// 模块基址+重定位基址+每个数据表示的小偏移量RelocAddress = (PULONG64)(HandleInFile + pRelocTable->VirtualAddress + minioffset);// 直接在RING3修改: 原始数据+基址-IMAGE_OPTINAL_HEADER中的基址VirtualProtect((PVOID)RelocAddress, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);// 因为是R3直接LOAD的所以要修改一下内存权限*RelocAddress = *RelocAddress + BaseInKernel - pNtHeader->OptionalHeader.ImageBase;VirtualProtect((PVOID)RelocAddress, 4, dwOldProtect, NULL);}// 下一个重定位数据pRelocData++;}// 下一个重定位块pRelocTable = (PIMAGE_BASE_RELOCATION)((ULONG64)pRelocTable + pRelocTable->SizeOfBlock);} while (pRelocTable->VirtualAddress);return TRUE;}return FALSE;
}// 初始化
BOOL InitEngine(BOOL IsClear)
{if (IsClear == TRUE){// 动态获取ntdll.dll模块的基地址KernelBase = GetKernelBase64(NtosFullName);printf("模块基址: %llx | 模块名: %s \n", KernelBase, NtosFullName);if (!KernelBase){return FALSE;}// 动态加载模块到内存,并获取到模块句柄hKernel = LoadLibraryExA(NtosFullName, 0, DONT_RESOLVE_DLL_REFERENCES);if (!hKernel){return FALSE;}// 判断并修复重定位表if (!RepairRelocationTable((ULONG64)hKernel, KernelBase)){return FALSE;}return TRUE;}else{FreeLibrary(hKernel);return TRUE;}
}// 获取原始函数机器码
VOID GetOriginalMachineCode(ULONG64 Address, PUCHAR ba, SIZE_T Length)
{ULONG64 OffsetAddress = Address - KernelBase + (ULONG64)hKernel;RtlCopyMemory(ba, (PVOID)OffsetAddress, Length);
}// 获取传入函数的内存地址
ULONG64 GetSystemRoutineAddress(PCHAR FuncName)
{return KernelBase + (ULONG64)GetProcAddress(hKernel, FuncName) - (ULONG64)hKernel;
}// 获取当前函数机器码
VOID GetCurrentMachineCode(ULONG64 Address, PUCHAR ba, SIZE_T Length)
{ULONG64 dat[2] = { 0 };dat[0] = Address;dat[1] = Length;IoControl(hDriver, CTL_CODE_GEN(0x800), dat, 16, ba, Length);
}// 清除特定位置的机器码
VOID ClearInlineHook(ULONG64 Address, PUCHAR ba, SIZE_T Length)
{KF_DATA dat = { 0 };dat.Address = (PVOID)Address;dat.Length = Length;// 直接调用写出控制码RtlCopyMemory(dat.data, ba, Length);IoControl(hDriver, CTL_CODE_GEN(0x801), &dat, sizeof(KF_DATA), 0, 0);
}// 打印数据
VOID PrintBytes(PCHAR DescriptionString, PUCHAR ba, UINT Length)
{printf("%s", DescriptionString);for (UINT i = 0; i<Length; i++){printf("%02x ", ba[i]);}printf("\n");
}int main(int argc, char *argv[])
{UCHAR OriginalMachineCode[BYTE_ARRAY_LENGTH];UCHAR CurrentMachineCode[BYTE_ARRAY_LENGTH];ULONG64 Address = 0;hDriver = CreateFileA("\\\\.\\WinDDK", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);// 初始化if (!InitEngine(TRUE) || hDriver == 0){return 0;}// 需要获取的函数列表CHAR *FunctionList[128] = { "PsLookupProcessByProcessId", "NtCommitEnlistment", "NtCommitComplete", "NtCommitTransaction" };for (size_t i = 0; i < 4; i++){// 清空缓存RtlZeroMemory(OriginalMachineCode, 0, BYTE_ARRAY_LENGTH);RtlZeroMemory(CurrentMachineCode, 0, BYTE_ARRAY_LENGTH);// 获取到当前函数地址Address = GetSystemRoutineAddress(FunctionList[i]);printf("\n函数地址: %p | 函数名: %s\n", Address, FunctionList[i]);if (Address == 0 || Address < KernelBase){return 0;}GetOriginalMachineCode(Address, OriginalMachineCode, BYTE_ARRAY_LENGTH);PrintBytes("原始机器码: ", OriginalMachineCode, BYTE_ARRAY_LENGTH);GetCurrentMachineCode(Address, CurrentMachineCode, BYTE_ARRAY_LENGTH);PrintBytes("当前机器码: ", CurrentMachineCode, BYTE_ARRAY_LENGTH);/*// 不相同则询问是否恢复if (memcmp(OriginalMachineCode, CurrentMachineCode, BYTE_ARRAY_LENGTH)){printf("按下[ENTER]恢复钩子");getchar();ClearInlineHook(Address, OriginalMachineCode, BYTE_ARRAY_LENGTH);}*/}// 注销InitEngine(FALSE);system("pause");return 0;
}

首先编译驱动程序WinDDK.sys并通过KmdManager将驱动程序拉起来,运行客户端lyshark.exe程序会输出当前FunctionList列表中,指定的4个函数的挂钩情况。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/207980.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

得帆云助力容百科技构建CRM系统,实现LTC全流程管理

宁波容百新能源科技股份有限公司 宁波容百新能源科技股份有限公司&#xff08;以下简称“容百科技”&#xff09;于2014年9月建立&#xff0c;是高科技新能源材料行业的跨国型集团公司。专业从事锂电池正极材料的研发、生产和销售&#xff0c;于2019年登陆上交所科创板&#x…

Python 数据分析:日期型数据的玩转之道

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在数据分析的领域中&#xff0c;处理日期型数据是至关重要的一环。Python 提供了丰富的工具和库&#xff0c;使得对日期进行分析、处理、可视化变得更加轻松。本文将深入探讨 Python 中如何玩转日期型数据&#…

解读链上经济“一等公民”:加密AI代理的优势和前沿应用

机器人正在成为加密经济的“一等公民”&#xff0c;最近的案例就能印证这一趋势。 搜索者&#xff08;Searchers&#xff09;部署像Jaredfromsubway.eth这样的机器人&#xff0c;利用真人用户对便利的渴望在DEX抢先交易。Banana Gun和Maestro允许真人用户通过Telegram的便利进…

力扣每日一题day31[101. 对称二叉树]

给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;fals 思路 对于二叉树是否对称&#xff…

二分查找算法

文章目录 二分查找二分的实战讲解二分查找普通二分模版 在排序数组中查找元素的第一个和最后一个位置万能二分模版 总结 二分查找 什么是二分查找:就是定义左右2个指针(此指针非真指针)取中间值 通过一次次取中间值找到要找到的数 二分的实战讲解 二分查找 题目:地址 题目解析…

测试文档---智力冲刺

文章目录 项目背景测试计划UI测试接口测试手工测试 测试总结 项目背景 项目描述&#xff1a;“智力冲刺”是一款网页小游戏&#xff0c;就像我们平时看到的网页游戏一样&#xff0c;前端页面负责展示游戏效果&#xff0c;后端服务器来实现游戏的逻辑。在我们的“智力冲刺”游戏…

postman做接口自动化测试

接口是用来连接服务端和客户端&#xff0c;一般返回的数据都是json。 get和post请求的区别&#xff1a; 1. get请求比post请求安全 2. get请求参数有长度限制&#xff0c;post请求没有 3. get请求没有body&#xff0c;参数都是放在url里面&#xff0c;而post请求是放在body…

大华DSS S2-045 OGNL表达式注入漏洞复现

0x01 产品简介 大华DSS安防监控系统平台是一款集视频、报警、存储、管理于一体的综合安防解决方案。该平台支持多种接入方式,包括网络视频、模拟视频、数字视频、IP电话、对讲机等。此外,该平台还支持多种报警方式,包括移动侦测、区域入侵、越线报警、人员聚集等。 0x02 漏…

元宇宙:重塑游戏行业体验下一个前沿

游戏行业在其整个历史中经历了显著的转变&#xff0c;从超级马里奥的像素化冒险发展到Red Dead Redemption等游戏中迷人的开放世界体验。随着时间的推移&#xff0c;游戏不断突破数字领域所能达到的极限。然而&#xff0c;被称为元宇宙的突破性演变将彻底改变游戏行业&#xff…

PO模式在selenium自动化测试框架有什么好处

PO模式是在UI自动化测试过程当中使用非常频繁的一种设计模式&#xff0c;使用这种模式后&#xff0c;可以有效的提升代码的复用能力&#xff0c;并且让自动化测试代码维护起来更加方便。 PO模式的全称叫page object model&#xff08;POM&#xff09;&#xff0c;有时候叫做 p…

网工内推 | 外企、合资公司急招网工,国内外旅游,健身年卡

01 深圳市耐施菲信息科技有限公司 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、负责项目的计划、实施、过程管控、项目验收等工作&#xff1b; 2、负责大型项目设备实施、安装调试等售后维护工作&#xff1b; 3、分析、设计网络拓扑结构、配置H3C、华为等交换机…

dll动态链接库【C#】

1说明&#xff1a; 在C#中&#xff0c;dll是添加 【类库】生成的。 2添加C#的dll&#xff1a; &#xff08;1&#xff09;在VS中新建一个Windows应用程序项目&#xff0c;并命名为TransferDll。 &#xff08;2&#xff09;打开Windows窗体设计器&#xff0c;从工具箱中为窗体…

Gemini:新一代AI产品的惊人功能和革命性影响

目录 1 前言2 视频分析与交互能力3 策划推理能力4 教育领域的应用能力5 科学领域的论文解读能力6 结语 1 前言 Google最新推出的AI产品Gemini引发了广泛关注&#xff0c;其30分钟的介绍和演示视频展示了令人惊艳的功能。Gemini以其惊人的艺术创作能力脱颖而出&#xff0c;通过…

TCP一对一聊天

客户端 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.IOException; import java.io…

python-04(入门基础篇4——lists相关的部分语法)

python-04&#xff08;入门基础篇4——lists相关的部分语法&#xff09; 1. 前言1.1 python入门1.2 参考官网 2. 关于索引和切片3. 在列表追加元素3.1 支持拼接3.2 使用list.append() 方法在列表末尾添加新项 4. 列表是可变类型4.1 更改其中某元素内容4.2 使用切片更改列表大小…

cesium学习记录

有段时间自学了cesium&#xff0c;这里记录一下自学过程&#xff0c;希望在所需之时查阅~~ 1、cesium源码获取与Index页面介绍 官网网址 www.cesiumjs.org 源代码下载&#xff1a;Platform-Dowmloads 在index.html右击open with Live server开启本地服务 点击Documentation…

VMware安装OpenEuler(安装界面)

本文中使用的OpenEuler版本&#xff1a;22.03 LTS SP2 VMware&#xff1a;17.0.0 一、下载镜像 根据CPU和场景&#xff0c;按需下载 https://www.openeuler.org/zh/download/?versionopenEuler%2022.03%20LTS%20SP2 二、初始化VmWare 三、配置操作系统 四、安装操作系统 …

Nginx漏洞修复

1、漏洞 去掉在请求响应头中存在的信息 Server: nginx X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1;modeblock 修复方法 在Nginx的配置文件中的 server 标签内增加一下配置 server_tokens off; add_header X-Frame-Options SAMEORIGIN; …

Python绘制几种常见的时序分析图

时间序列数据是一种按照时间顺序排列的观测值集合&#xff0c;每个观测值对应于一个特定的时间点。这种数据在许多领域中都具有重要的应用价值&#xff0c;如金融、经济、气候科学等。通过分析时间序列数据&#xff0c;可以帮助我们掌握潜在的模式、发现趋势和季节性波动等重要…

CSGO游戏盲盒开箱源码 盲盒对战、幸运开箱、积分商城、Fl盲盒

源码介绍&#xff1a; CSGO游戏盲盒开箱源码 盲盒对战、幸运开箱、积分商城、Fl盲盒。这个是一个新花样玩法的盲盒程序。 仅供学习&#xff0c;请勿商用&#xff01;请购买正版程序运营。 代码下载&#xff1a;百度网盘