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

PE结构(十五)系统调用与函数地址动态寻找

双机调试

当需要分析一个程序时,这个程序一定是可以调试的,操作系统也不例外。在调试过程中下断点是很重要的

当我们对一个应用程序下断点时,应用程序是挂起的。但当我们对操作系统的内核程序下断点时,被挂起的不是内核程序而是整个操作系统。当操作系统被挂起时,网卡显卡等功能无法使用,进而无法在挂起的操作系统的机器上进行任何操作,也就无法调试内核程序了。

为了能够调试内核程序,Windows提供了一个调试子系统,它是一个独立于操作系统的的子系统,负责与调试器进行数据的交互进而实现调试内核程序。当内核程序下断点时,整个操作系统都被挂起,而这个调试子系统却不会被挂起。因此我们可以通过调试子系统去调试内核程序。

双机调试:由于挂起的操作系统我们无法进行任何操作,因此当调试内核程序时,我们需要在另一个机器上进行调试,这就是双机调试的由来。但我们使用两台物理机器来调试内核程序,这显然不现实,因此我们通常在本机上再运行一个虚拟机从而达到双机的效果。虚拟机上运行我们要调试的内核程序的机器,而我们在本机上进行调试内核程序。

调试过程:本机中的windbg首先连接到被调试的内核程序的机器的调试子系统,然后虚拟机通过串口把被调试的内核程序的数据发送给本机的windbg中从而进行信息的交互,进而实现调试。而我们在windbg发送的指令实际上是给调试子系统发送指令

注意:我们需要在操作系统刚开始运行时被迅速打开windbg,否则可能出现无法连接调试子系统的情况

此处具体双击调试环境配置步骤就不多说了,诸位自行百度

驱动开发

大多的驱动程序的启动是基于服务加载,服务基于注册表。驱动程序不像我们写的应用程序,关闭窗口就关闭程序,它必须有一个卸载,才可以关闭程序

一般的驱动程序都是用C写到,因为足够底层。但也可以用C++写,不过不建议

接下来我们在本机上写一个简单的驱动程序:

C驱动程序

#include <ntifs.h>//SYS 驱动程序后缀:.SYSVOID DriverUnload(PDRIVER_OBJECT pDriverObject)//卸载函数,名字自定义
{DbgPrint("Hello DriverUnload\r\n");//打印一句话
}NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)//参数:驱动对象 注册表路径
{pDriverObject->DriverUnload = DriverUnload;//指向卸载函数DbgPrint("Hello DriverEntry\r\n");//一定要加上\r\n,否则可能打印失败return STATUS_SUCCESS;//必须返回成功
}

C++驱动程序

#include <ntifs.h>EXTERN_C VOID DriverUnload(PDRIVER_OBJECT pDriverObject)//由于C++具有名称粉碎机制,因此以C的形式导出,保留名称
{DbgPrint("Hello C++ DriverUnload\r\n");
}EXTERN_C NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegPath)
{pDriverObject->DriverUnload = DriverUnload;DbgPrint("Hello C++ DriverEntry\r\n");return STATUS_SUCCESS;
}

系统调用

大多数的应用程序的函数调用无疑是调用了系统调用(内核函数),而使用系统调用则需要进入内核,进入内核有如下几种方式:

1.int 2E(比较早期)

2.sysenter(x86)

3.syscall(x64)

注意:在进入内核前,通常需要携带参数:系统服务号,有时候也需要携带其他的参数,如进程ID等等。系统服务号用于在内核的SSDT表(系统描述符表)中进行对照寻找系统调用,其充当了索引的作用

在我们日常使用的应用程序,分为x86应用程序和x64应用程序,不同的应用程序有不同的运行环境:x86的应用程序可以在x86操作系统上运行,也在x64操作系统上运行。而x64的应用程序只能在x64的操作系统上运行。

在不同的操作系统上,应用程序的函数调用有不同的情况,接下来我们观察这两种不同情况的应用程序的函数调用流程,也就是观察如何调用系统调用。

现我们写如下程序并生成x86和x64版本的exe

#include<iostream>
#include<Windows.h>int main()
{OpenProcess(PROCESS_ALL_ACCESS, FALSE, 1234)system("pause");return 0;
}

x86应用程序

我们将上文的exe文件拖入x32dbg中进行观察,接下来我们将通过一步步的步入观察该函数由表入里的调用过程:从函数到系统调用

x86OS

1.我们搜索所有用户模块的跨模块调用找到OpenProcess的调用处。

2.跟进该函数,我们发现程序来到Kernel32.dll模块进行一个跳转

3.程序来到kernelbase.dll模块,调用ZwOpenProcess(同NTOpenProcess)

4.程序来到ntdll.dll模块,调用KiFastSystemCall

5.程序在ntdll.dll模块,调用sysenter进入内核

x64OS

1. 我们搜索所有用户模块的跨模块调用找到OpenProcess的调用处。

2.步入该函数,我们发现程序来到Kernel32.dll模块进行一个跳转

3.程序来到kernelbase.dll模块,调用NTOpenProcess

4.程序来到ntdll.dll模块,调用Wow64Transition

Wow64Transition用于处理64位操作系统下32位程序,实现模拟的32位环境并进行转发,然后调用64位函数进入内核

x64应用程序

1. 我们搜索所有用户模块的跨模块调用找到OpenProcess的调用处。

2.步入该函数,我们发现程序来到Kernel32.dll模块进行一个跳转

3.程序来到kernelbase.dll模块,调用NTOpenProcess

4.程序在ntdll.dll模块,调用syscall或int 2E进入内核

函数地址动态寻找

所谓的壳不过是在目标程序中加一个区段使得程序可以执行我们自己的代码,从而达到加密的效果。但在这个实现的过程中,存在一个问题:我们无法知晓目标程序中是否包含我们自己的代码中所使用的一些函数的相关库。如果没有相关库的话,也就无法在我们的代码中使用相关函数。为保险起见,我们需要自己加载相关的动态链接库。但由于我们并不清楚目标程序中包含了哪些头文件,因此也无法使用LoadLibrary加载动态链接库。因此我们便需要动态寻找函数地址,从而实现加载动态链接库,进而实现我们自己的代码书写

为实现以上操作,有以下两个结构很重要:

1.TEB(线程环境块):描述线程的关键成员

2.PEB(进程环境块):描述进程的关键成员

x86应用程序

x86OS

接下来我们将针对x86程序在x86环境下,通过这两个结构,完成函数地址动态寻找

一.我们通过双机调试寻找Kernel32的地址

1.Windbg中输入命令!Process 0 0:显示简略的系统进程相关信息

此处我们找到了目标程序INSTDRV.EXE

2.输入命令.process /i     切换到目标程序环境下

注意:结束以后需要F5重新加载一下上下文  

3.输入命令! process,查看目标程序信息

4.输入命令dt _TEB 0x7ffde00,获取TEB结构,从而获取PEB结构

5.查看PEB结构:获取_PEB_LDR_DATA结构


6.查看_PEB_LDR_DATA结构,其中存在三个重要的双向链表

7._LIST_ENTRY结构记录了双向链表的信息。

如InLoadOrderModuleList的_LIST_ENTRY记录了加载模块列表的相关信息

8.查看InLoadOrderModuleList的第一个_LDR_DATA_TABLE_ENTRY结构,观察第一个进程加载的模块信息

9.查看InLoadOrderModuleList的第二个_LDR_DATA_TABLE_ENTRY结构

10.查看InLoadOrderModuleList的第三个_LDR_DATA_TABLE_ENTRY结构,此处我们找到了Kernel32模块的相关信息

二.在Kernel32的导出表中寻找到GetProcAddress,该函数用于获取动态链接库(DLL)中指定导出函数的地址

三.找到LoadLibrary地址,该函数用于加载动态链接库

四.加载动态链接库

代码实现
#include <iostream>
#include <Windows.h>DWORD GetKernel32Address() {DWORD dwKernel32 = 0;//获取当前线程的TEB指针_TEB *pTeb = NtCurrentTeb();//在TEB中找到PEB,于TEB+0x30位置PDWORD pPeb = (PDWORD)*(PDWORD)((DWORD)pTeb + 0x30);//PEB中找到PEB_LDR_DATA,于PEB+0xC位置PDWORD pLdr = (PDWORD)*(PDWORD)((DWORD)pPeb + 0xC);//PEB_LDR_DATA中找到InLoadOrderModuleList,于PEB_LDR_DATA+0xC的位置PDWORD InLoadOrderModuleList = (PDWORD)((DWORD)pLdr + 0xC);//找到exe模块PDWORD pModuleExe = (PDWORD)*InLoadOrderModuleList;//找到Ntdll模块PDWORD pModuleNtdll = (PDWORD)*pModuleExe;//找到Kernel32模块PDWORD pModuleKernel32 = (PDWORD)*pModuleNtdll;//保存Kernel32模块基址dwKernel32 = pModuleKernel32[4];//返回Kernel32模块基址/模块基址也就是模块句柄return dwKernel32;
}//获取GetProcAddress函数地址
DWORD GrkGetProcessAddress() {//获取Kernel32基址DWORD dwBase = GetKernel32Address();//获取DOS头PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dwBase;//获取NT头PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);//获取PE可选头PIMAGE_OPTIONAL_HEADER  pOptionalHeader = &pNtHeaders->OptionalHeader;//获取Kernel32的导出表PIMAGE_DATA_DIRECTORY pExportDir = &(pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(dwBase + pExportDir->VirtualAddress);DWORD dwFuncCount = pExport->NumberOfFunctions;DWORD dwFuncNameCount = pExport->NumberOfNames;//导出地址表PDWORD pEAT = (PDWORD)(dwBase + pExport->AddressOfFunctions);//导出名称表PDWORD pENT = (PDWORD)(dwBase + pExport->AddressOfNames);//导出序号表PWORD pEIT = (PWORD)(dwBase + pExport->AddressOfNameOrdinals);//遍历导出表,获取我们需要的函数地址for (size_t i = 0; i < dwFuncCount; i++){if (!pEAT[i]){continue;}DWORD dwFunAddrOffset = pEAT[i];for (size_t index = 0; index < dwFuncNameCount; index++){if (pEIT[index] == i){DWORD dwNameOffset = pENT[index];char * szFuncName = (char *)(((DWORD)dwBase) + dwNameOffset);if (strcmp(szFuncName,"GetProcAddress") == 0){return dwBase + dwFunAddrOffset;}}}}
}EXTERN_C typedef HMODULE
(WINAPI * 
fnLoadLibraryA)(_In_ LPCSTR lpLibFileName
);EXTERN_C typedef FARPROC
(WINAPI *
fnGetProcAddress)(_In_ HMODULE hModule,_In_ LPCSTR lpProcName
);EXTERN_C typedef int
(WINAPI *
fnMessageBoxA)(_In_opt_ HWND hWnd,_In_opt_ LPCSTR lpText,_In_opt_ LPCSTR lpCaption,_In_ UINT uType);EXTERN_C typedef VOID
(WINAPI *
fnExitProcess)(_In_ UINT uExitCode
);int main()
{fnGetProcAddress pfnGetProcAddress = (fnGetProcAddress)GrkGetProcessAddress();//获取Kernel32.dll基址HMODULE hKernel32 = (HMODULE)GetKernel32Address();//获取LoadLibraryA地址fnLoadLibraryA pfnLoadLibraryA = (fnLoadLibraryA)pfnGetProcAddress(hKernel32, "LoadLibraryA");//加载user32.dllHMODULE hUesr32 = pfnLoadLibraryA("user32.dll");//获取MessageBoxA地址fnMessageBoxA pfnMessageBoxA = (fnMessageBoxA)pfnGetProcAddress(hUesr32, "MessageBoxA");//获取ExitProcess地址,用于退出进程fnExitProcess pfnExitProcess = (fnExitProcess)pfnGetProcAddress(hKernel32, "ExitProcess");//调用MessageBoxApfnMessageBoxA(NULL, "rkvir", "Msg", MB_OK);//调用ExitProcess,退出进程pfnExitProcess(0);system("pause");return 0;
}

x64OS

双机调试流程同x86大致一样,只是地址由32位变为64位,以及系统进入内核方式由Kernel32所在库变为wow64库。具体流程此处不再演示。

代码实现同上

x64应用程序

代码实现

此处代码实现与x86的代码实现一致,只需把DWORD改为ULONGLONG即可 

#include <iostream>
#include <Windows.h>ULONGLONG GetKernel32Address()
{ULONGLONG dwKernel32 = 0;_TEB *pTeb = NtCurrentTeb();PULONGLONG pPeb = (PULONGLONG)*(PULONGLONG)((ULONGLONG)pTeb + 0x60);PULONGLONG pLdr = (PULONGLONG)*(PULONGLONG)((ULONGLONG)pPeb + 0x18);PULONGLONG InLoadOrderModuleList = (PULONGLONG)((ULONGLONG)pLdr + 0x10);PULONGLONG pModuleExe = (PULONGLONG)*InLoadOrderModuleList;PULONGLONG pModuleNtdll = (PULONGLONG)*pModuleExe;PULONGLONG pModuleKernel32 = (PULONGLONG)*pModuleNtdll;dwKernel32 = pModuleKernel32[6];return dwKernel32;
}ULONGLONG GrkGetProcessAddress()
{ULONGLONG dwBase = GetKernel32Address();PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)dwBase;PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + dwBase);PIMAGE_DATA_DIRECTORY pExportDir = pNt->OptionalHeader.DataDirectory;pExportDir = &(pExportDir[IMAGE_DIRECTORY_ENTRY_EXPORT]);DWORD dwOffset = pExportDir->VirtualAddress;PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(dwBase + dwOffset);DWORD dwFuncCount = pExport->NumberOfFunctions;DWORD dwFuncNameCount = pExport->NumberOfNames;DWORD dwModOffset = pExport->Name;PDWORD pEAT = (PDWORD)(dwBase + pExport->AddressOfFunctions);PDWORD pENT = (PDWORD)(dwBase + pExport->AddressOfNames);PWORD pEIT = (PWORD)(dwBase + pExport->AddressOfNameOrdinals);for (size_t i = 0; i < dwFuncCount; i++){if (!pEAT[i]){continue;}DWORD dwOrdinal = pExport->Base + i;ULONGLONG dwFunAddrOffset = pEAT[i];for (size_t index = 0; index < dwFuncNameCount; index++){if (pEIT[index] == i){ULONGLONG dwNameOffset = pENT[index];char * szFuncName = (char *)(((ULONGLONG)dwBase) + dwNameOffset);if (strcmp(szFuncName,"GetProcAddress") == 0){return dwBase + dwFunAddrOffset;}}}}
}EXTERN_C typedef HMODULE
(WINAPI * 
fnLoadLibraryA)(_In_ LPCSTR lpLibFileName
);EXTERN_C typedef FARPROC
(WINAPI *
fnGetProcAddress)(_In_ HMODULE hModule,_In_ LPCSTR lpProcName
);EXTERN_C typedef int
(WINAPI *
fnMessageBoxA)(_In_opt_ HWND hWnd,_In_opt_ LPCSTR lpText,_In_opt_ LPCSTR lpCaption,_In_ UINT uType);EXTERN_C typedef VOID
(WINAPI *
fnExitProcess)(_In_ UINT uExitCode
);int main()
{fnGetProcAddress pfnGetProcAddress = (fnGetProcAddress)GrkGetProcessAddress();HMODULE hKernel32 = (HMODULE)GetKernel32Address();fnLoadLibraryA pfnLoadLibraryA = (fnLoadLibraryA)pfnGetProcAddress(hKernel32, "LoadLibraryA");HMODULE hUesr32 = pfnLoadLibraryA("user32.dll");fnMessageBoxA pfnMessageBoxA = (fnMessageBoxA)pfnGetProcAddress(hUesr32, "MessageBoxA");fnExitProcess pfnExitProcess = (fnExitProcess)pfnGetProcAddress(hKernel32, "ExitProcess");pfnMessageBoxA(NULL, "rkvir", "Msg", MB_OK);pfnExitProcess(0);system("pause");return 0;
}

相关文章:

PE结构(十五)系统调用与函数地址动态寻找

双机调试 当需要分析一个程序时&#xff0c;这个程序一定是可以调试的&#xff0c;操作系统也不例外。在调试过程中下断点是很重要的 当我们对一个应用程序下断点时&#xff0c;应用程序是挂起的。但当我们对操作系统的内核程序下断点时&#xff0c;被挂起的不是内核程序而是…...

webrtc 本地运行的详细操作步骤 1

前言 选修课的一个课程设计&#xff0c;需要我们本地运行这个开源项目&#xff0c;给我的压力非常大&#xff0c;因为确实不是很熟练这种操作。但是还是得做。谨以此文&#xff0c;纪念这个过程。 之前自己在 github 上面看到有代码仓库&#xff0c;但是比较复杂&#xff0c;在…...

kali——httrack

目录 前言 使用教程 前言 HTTrack 是一款运行于 Kali Linux 系统中的开源网站镜像工具&#xff0c;它能将网站的页面、图片、链接等资源完整地下载到本地&#xff0c;构建出一个和原网站结构相似的离线副本。 使用教程 apt install httrack //安装httrack工具 httrac…...

力扣经典算法篇-6-轮转数组

题干&#xff1a; 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 示例 1: 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7,1,2,3,4,5] 向右轮转 3 步…...

【计算机网络】Linux配置SNAT/DNAT策略

什么是NAT&#xff1f; NAT 全称是 Network Address Translation&#xff08;网络地址转换&#xff09;&#xff0c;是一个用来在多个设备共享一个公网 IP上网的技术。 NAT 的核心作用&#xff1a;将一个网络中的私有 IP 地址&#xff0c;转换为公网 IP 地址&#xff0c;从而…...

火山引擎coze用户市场

火山引擎 **Coze**&#xff08;扣子&#xff09;的用户市场主要集中在 **需要快速构建和部署智能对话应用的企业及开发者群体**&#xff0c;覆盖多个行业与场景。以下是具体分析&#xff1a; --- ### **一、核心用户群体** 1. **企业用户** - **互联网/科技公司**&#…...

qt designer 软件主题程序设计

对于使用Qt Designer设计的界面&#xff0c;主题切换的实现需要结合Qt的信号槽机制、样式表动态加载以及资源管理。以下是针对Qt Designer UI的详细解决方案&#xff1a; 一、UI文件与主题系统的整合架构 二、核心实现步骤 1. 动态样式表加载系统 // ThemeManager.h class …...

2025/4/2 心得

第一题 题目描述 给定1001个范围在[1,1000]的数字&#xff0c;保证只有1个数字重复出现2次&#xff0c;其余数字只出现1次。试用O(n)时间复杂度来求出出现2次的这个数字。 不允许用数组 输入格式 第一行&#xff1a;一个整数1001&#xff1b; 第二行&#xff1a;1001个用…...

AI安全:构建负责任且可靠的系统

AI已成为日常生活中无处不在的助力&#xff0c;随着AI系统能力和普及性的扩展&#xff0c;安全因素变得愈发重要。从基础模型构建者到采用AI解决方案的企业&#xff0c;整个AI生命周期中的所有相关方都必须共同承担责任。 为什么AI安全至关重要&#xff1f; 对于企业而言&…...

VUE+SPRINGBOOT+语音技术实现智能语音歌曲管理系统

语音控制歌曲的播放、暂停、增删改查 <template><div class"Music-container"><div style"margin: 10px 0"><!--检索部分--><el-input style"width: 200px;" placeholder"请输入歌曲名称"v-model"sen…...

使用 SignalR 在 .NET Core 8 最小 API 中构建实时通知

示例代码&#xff1a;https://download.csdn.net/download/hefeng_aspnet/90448094 介绍 构建实时应用程序已成为现代 Web 开发中必不可少的部分&#xff0c;尤其是对于通知、聊天系统和实时更新等功能。SignalR 是 ASP.NET 的一个强大库&#xff0c;可实现服务器端代码和客户…...

Kotlin 集合函数:map 和 first 的使用场景

Kotlin 提供了丰富的集合操作函数&#xff0c;使开发者可以更加简洁、高效地处理数据。其中&#xff0c;map 和 first 是两个常用的函数&#xff0c;分别用于转换集合和获取集合中的第一个元素。 1. map 的使用场景 场景 1&#xff1a;对象列表转换 在开发中&#xff0c;我们…...

Spring Cloud 框架为什么能处理高并发

Spring Cloud框架能够有效处理高并发场景&#xff0c;核心在于其微服务架构设计及多组件的协同作用&#xff0c;具体机制如下&#xff1a; 一、分布式架构设计支撑高扩展性 服务拆分与集群部署 Spring Cloud通过微服务拆分将单体系统解耦为独立子服务&#xff0c;每个服务可独…...

【Python爬虫高级技巧】BeautifulSoup高级教程:数据抓取、性能调优、反爬策略,全方位提升爬虫技能!

大家好&#xff0c;我是唐叔&#xff01;上期我们聊了 BeautifulSoup的基础用法 &#xff0c;今天带来进阶篇。我将分享爬虫老司机总结的BeautifulSoup高阶技巧&#xff0c;以及那些官方文档里不会告诉你的实战经验&#xff01; 文章目录 一、BeautifulSoup性能优化技巧1. 解析…...

复古未来主义屏幕辉光像素化显示器反乌托邦效果PS(PSD)设计模板样机 Analog Retro-Futuristic Monitor Effect

这款模拟复古未来主义显示器效果直接取材于 90 年代赛博朋克电影中的黑客巢穴&#xff0c;将粗糙的屏幕辉光和像素化的魅力强势回归。它精准地模仿了老式阴极射线管显示器&#xff0c;能将任何图像变成故障频出的监控画面或高风险的指挥中心用户界面。和……在一起 2 个完全可编…...

Spring Boot + MySQL + MyBatis(注解和XML配置两种方式)集成Redis的完整启用及配置详解,包含代码示例、注释说明和表格总结

以下是 Spring Boot MySQL MyBatis&#xff08;注解和XML配置两种方式&#xff09;集成Redis的完整启用及配置详解&#xff0c;包含代码示例、注释说明和表格总结&#xff1a; 1. 添加依赖 在pom.xml中添加Spring Boot对MySQL、MyBatis和Redis的支持依赖&#xff1a; <d…...

Webpack vs Vite:现代前端构建工具的巅峰对决与选型指南

构建工具的进化革命当雪碧瓶上的水珠折射出前端工程的变迁史&#xff0c;Webpack与Vite的决战已然成为现代前端开发的分水岭。这场始于打包理念的革命&#xff0c;正在重塑整个前端生态的底层逻辑。本文将从原理架构、性能表现、开发体验三个维度&#xff0c;结合真实项目数据对…...

2023-2024总结记录

概括经历 这一年算是一个人生节点&#xff0c;2023年花了一整年的时间在准备考研&#xff0c;基本上等于一个人奋战&#xff0c;我不怎么去图书馆&#xff0c;只呆在无人的实验室&#xff0c;还好有对象陪我&#xff0c;不然可能要抑郁了。作息上还是很随意&#xff0c;什么时…...

技术驱动革新,强力巨彩LED软模组助力创意显示

随着LED显示技术的不断突破&#xff0c;LED软模组因其独特的柔性特质和个性化显示效果&#xff0c;正逐渐成为各类应用场景的新宠。强力巨彩软模组R3.0H系列具备独特的可塑造型能力与技术创新&#xff0c;为商业展示、数字艺术、建筑装饰等领域开辟全新视觉表达空间。    LED…...

Spring 核心技术解析【纯干货版】- XVIII:Spring 网络模块 Spring-WebSocket 模块精讲

在现代 Web 开发中&#xff0c;实时通信已成为提升用户体验的关键技术之一。传统的 HTTP 轮询方式存在较高的延迟和带宽开销&#xff0c;而 WebSocket 作为一种全双工通信协议&#xff0c;能够在客户端和服务器之间建立持久连接&#xff0c;实现高效的双向数据传输。 Spring 框…...

Spark,HDFS概述

HDFS组成构架&#xff1a; 注&#xff1a; NameNode&#xff08;nn&#xff09;&#xff1a;就是 Master&#xff0c;它是一个主管、管理者。 (1) 管理 HDFS 的名称空间&#xff1b; (2) 配置副本策略。记录某些文件应该保持几个副本&#xff1b; (3) 管理数据块&#xff08;…...

【数据结构】图论进阶:生成树、生成森林与权值网络的终极解析

图的基本概念 导读一、图中的树与森林1.1 生成树与生成森林1.1.1 生成树1.1.2 生成森林1.1.3 生成树、生成森林与连通分量结点的关系边的关系 1.2 有向图中的树与森林1.2.1 有向树与有向森林1.2.2 生产有向树与生成有向森林1.2.3 有向树与生成有向树的区别1.2.4 有向森林与生成…...

C和C++(list)的链表初步

链表是构建其他复杂数据结构的基础&#xff0c;如栈、队列、图和哈希表等。通过对链表进行适当的扩展和修改&#xff0c;可以实现这些数据结构的功能。想学算法&#xff0c;数据结构&#xff0c;不会链表是万万不行的。这篇笔记是一名小白在学习时整理的。 C语言 链表部分 …...

深入浅出 TypeScript 泛型:类型安全的艺术与实践

文章目录 一、泛型的核心概念1.1 类型参数&#xff1a;代码中的类型变量1.2 类型推断&#xff1a;让代码保持简洁 二、泛型的四大应用场景2.1 泛型函数&#xff1a;打造通用工具库2.2 泛型接口&#xff1a;定义灵活的数据结构2.3 泛型类&#xff1a;构建类型安全的容器2.4 泛型…...

【KWDB创作者计划】_KaiwuDB 2.1.0 单节点裸机部署

大家好&#xff0c;这里是 DBA学习之路&#xff0c;专注于提升数据库运维效率。 目录 前言KWDB 介绍安装准备环境信息配置要求操作系统软件依赖端口要求安装包下载 部署 KWDB简单实用连接数据库创建数据库创建用户创建时序表 前言 今天无意间在墨天轮看到一个征文活动 征文大赛…...

洛谷题单3-P5720 【深基4.例4】一尺之棰-python-流程图重构

题目描述 《庄子》中说到&#xff0c;“一尺之棰&#xff0c;日取其半&#xff0c;万世不竭”。第一天有一根长度为 a a a 的木棍&#xff0c;从第二天开始&#xff0c;每天都要将这根木棍锯掉一半&#xff08;每次除 2 2 2&#xff0c;向下取整&#xff09;。第几天的时候木…...

前端快速入门学习3——CSS介绍与选择器

1.概述 CSS全名是cascading style sheets,中文名层叠样式表。 用于定义网页样式和布局的样式表语言。 通过 CSS&#xff0c;你可以指定页面中各个元素的颜色、字体、大小、间距、边框、背景等样式&#xff0c;从而实现更精确的页面设计。 HTML与CSS的关系&#xff1a;HTML相当…...

Redash:一个开源的数据查询与可视化工具

Redash 是一款免费开源的数据可视化与协作工具&#xff0c;可以帮助用户快速连接数据源、编写查询、生成图表并构建交互式仪表盘。它简化了数据探索和共享的过程&#xff0c;尤其适合需要团队协作的数据分析场景。 数据源 Redash 支持各种 SQL、NoSQL、大数据和 API 数据源&am…...

嵌入式Linux驱动—— 1 GPIO配置

目录 1.GPIO操作 1.1 IO命名 1.2 GPIO 时钟使能&#xff08;CCM&#xff09; 1.3 IO 复用&#xff08;IOMUXC&#xff09; 1.4 IO 配置 1.5 GPIO 配置 1.GPIO操作 GPIO操作主要是以下流程&#xff1a; 使能某组GPIO模块&#xff08;GPIO1、2、...&#xff09;&#…...

[ICLR 2025]Biologically Plausible Brain Graph Transformer

论文网址&#xff1a;Biologically Plausible Brain Graph Transformer 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 …...