9.3 Windows驱动开发:内核解析PE结构节表
在笔者上一篇文章《内核解析PE结构导出表》
介绍了如何解析内存导出表结构,本章将继续延申实现解析PE结构的PE头,PE节表等数据,总体而言内核中解析PE结构与应用层没什么不同,在上一篇文章中LyShark
封装实现了KernelMapFile()
内存映射函数,在之后的章节中这个函数会被多次用到,为了减少代码冗余,后期文章只列出重要部分,读者可以自行去前面的文章中寻找特定的片段。
PE结构(Portable Executable Structure)是Windows操作系统用于执行可执行文件和动态链接库(DLL)的标准格式。节表(Section Table)是PE结构中的一个部分,它记录了可执行文件或DLL中每个区域的详细信息,例如代码、数据、资源等。
Windows NT 系统中可执行文件使用微软设计的新的文件格式,PE文件的基本结构如下图所示:
在PE文件中,代码,已初始化的数据,资源和重定位信息等数据被按照属性分类放到不同的Section(节区/或简称为节)
中,而每个节区的属性和位置等信息用一个IMAGE_SECTION_HEADER
结构来描述,所有的IMAGE_SECTION_HEADER
结构组成了一个节表(Section Table)
,节表数据在PE文件中被放在所有节数据的前面.
上面PE结构图中可知PE文件的开头部分包括了一个标准的DOS可执行文件结构,这看上去有些奇怪,但是这对于可执行程序的向下兼容性来说却是不可缺少的,当然现在已经基本不会出现纯DOS程序了,现在来说这个IMAGE_DOS_HEADER
结构纯粹是历史遗留问题。
9.1.1 DOS头结构解析
PE文件中的DOS部分由MZ格式的文件头和可执行代码部分组成,可执行代码被称为DOS块(DOS stub)
,MZ格式的文件头由IMAGE_DOS_HEADER
结构定义,在C语言头文件winnt.h
中有对这个DOS结构详细定义,如下所示:
typedef struct _IMAGE_DOS_HEADER { WORD e_magic; // DOS的头部WORD e_cblp; // Bytes on last page of fileWORD e_cp; // Pages in fileWORD e_crlc; // RelocationsWORD e_cparhdr; // Size of header in paragraphsWORD e_minalloc; // Minimum extra paragraphs neededWORD e_maxalloc; // Maximum extra paragraphs neededWORD e_ss; // Initial (relative) SS valueWORD e_sp; // Initial SP valueWORD e_csum; // ChecksumWORD e_ip; // Initial IP valueWORD e_cs; // Initial (relative) CS valueWORD e_lfarlc; // File address of relocation tableWORD e_ovno; // Overlay numberWORD e_res[4]; // Reserved wordsWORD e_oemid; // OEM identifier (for e_oeminfo)WORD e_oeminfo; // OEM information; e_oemid specificWORD e_res2[10]; // Reserved wordsLONG e_lfanew; // 指向了PE文件的开头(重要)} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
在DOS文件头中,第一个字段e_magic
被定义为MZ
,标志着DOS文件的开头部分,最后一个字段e_lfanew
则指明了PE文件的开头位置,现在来说除了第一个字段和最后一个字段有些用处,其他字段几乎已经废弃了,这里附上读取DOS头的代码。
void DisplayDOSHeadInfo(HANDLE ImageBase)
{PIMAGE_DOS_HEADER pDosHead = NULL;pDosHead = (PIMAGE_DOS_HEADER)ImageBase;printf("DOS头: %x\n", pDosHead->e_magic);printf("文件地址: %x\n", pDosHead->e_lfarlc);printf("PE结构偏移: %x\n", pDosHead->e_lfanew);
}
9.1.2 PE头结构解析
从DOS文件头的e_lfanew
字段向下偏移003CH
的位置,就是真正的PE文件头的位置,该文件头是由IMAGE_NT_HEADERS
结构定义的,定义结构如下:
typedef struct _IMAGE_NT_HEADERS {DWORD Signature; // PE文件标识字符IMAGE_FILE_HEADER FileHeader;IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
如上PE文件头的第一个DWORD是一个标志,默认情况下它被定义为00004550h也就是P,E
两个字符另外加上两个零,而大部分的文件属性由标志后面的IMAGE_FILE_HEADER
和IMAGE_OPTIONAL_HEADER32
结构来定义,我们继续跟进IMAGE_FILE_HEADER
这个结构:
typedef struct _IMAGE_FILE_HEADER {WORD Machine; // 运行平台WORD NumberOfSections; // 文件的节数目DWORD TimeDateStamp; // 文件创建日期和时间DWORD PointerToSymbolTable; // 指向符号表(用于调试)DWORD NumberOfSymbols; // 符号表中的符号数量WORD SizeOfOptionalHeader; // IMAGE_OPTIONAL_HANDLER32结构的长度WORD Characteristics; // 文件的属性 exe=010fh dll=210eh
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
继续跟进 IMAGE_OPTIONAL_HEADER32
结构,该结构体中的数据就丰富了,重要的结构说明经备注好了:
typedef struct _IMAGE_OPTIONAL_HEADER {WORD Magic;BYTE MajorLinkerVersion; // 连接器版本BYTE MinorLinkerVersion;DWORD SizeOfCode; // 所有包含代码节的总大小DWORD SizeOfInitializedData; // 所有已初始化数据的节总大小DWORD SizeOfUninitializedData; // 所有未初始化数据的节总大小DWORD AddressOfEntryPoint; // 程序执行入口RVADWORD BaseOfCode; // 代码节的起始RVADWORD BaseOfData; // 数据节的起始RVADWORD ImageBase; // 程序镜像基地址DWORD SectionAlignment; // 内存中节的对其粒度DWORD FileAlignment; // 文件中节的对其粒度WORD MajorOperatingSystemVersion; // 操作系统主版本号WORD MinorOperatingSystemVersion; // 操作系统副版本号WORD MajorImageVersion; // 可运行于操作系统的最小版本号WORD MinorImageVersion;WORD MajorSubsystemVersion; // 可运行于操作系统的最小子版本号WORD MinorSubsystemVersion;DWORD Win32VersionValue;DWORD SizeOfImage; // 内存中整个PE映像尺寸DWORD SizeOfHeaders; // 所有头加节表的大小DWORD CheckSum;WORD Subsystem;WORD DllCharacteristics;DWORD SizeOfStackReserve; // 初始化时堆栈大小DWORD SizeOfStackCommit;DWORD SizeOfHeapReserve;DWORD SizeOfHeapCommit;DWORD LoaderFlags;DWORD NumberOfRvaAndSizes; // 数据目录的结构数量IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
IMAGE_DATA_DIRECTORY数据目录列表,它由16个相同的IMAGE_DATA_DIRECTORY结构组成,这16个数据目录结构定义很简单仅仅指出了某种数据的位置和长度,定义如下:
typedef struct _IMAGE_DATA_DIRECTORY {DWORD VirtualAddress; // 数据起始RVADWORD Size; // 数据块的长度
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
上方的结构就是PE文件的重要结构,接下来将通过编程读取出PE文件的开头相关数据,读取这些结构也非常简单代码如下所示。
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");NTSTATUS status = STATUS_SUCCESS;HANDLE hFile = NULL;HANDLE hSection = NULL;PVOID pBaseAddress = NULL;UNICODE_STRING FileName = { 0 };// 初始化字符串RtlInitUnicodeString(&FileName, L"\\??\\C:\\Windows\\System32\\ntdll.dll");// 内存映射文件status = KernelMapFile(FileName, &hFile, &hSection, &pBaseAddress);if (!NT_SUCCESS(status)){return 0;}// 获取PE头数据集PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBaseAddress;PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pFileHeader = &pNtHeaders->FileHeader;DbgPrint("运行平台: %x\n", pFileHeader->Machine);DbgPrint("节区数目: %x\n", pFileHeader->NumberOfSections);DbgPrint("时间标记: %x\n", pFileHeader->TimeDateStamp);DbgPrint("可选头大小 %x\n", pFileHeader->SizeOfOptionalHeader);DbgPrint("文件特性: %x\n", pFileHeader->Characteristics);DbgPrint("入口点: %p\n", pNtHeaders->OptionalHeader.AddressOfEntryPoint);DbgPrint("镜像基址: %p\n", pNtHeaders->OptionalHeader.ImageBase);DbgPrint("镜像大小: %p\n", pNtHeaders->OptionalHeader.SizeOfImage);DbgPrint("代码基址: %p\n", pNtHeaders->OptionalHeader.BaseOfCode);DbgPrint("区块对齐: %p\n", pNtHeaders->OptionalHeader.SectionAlignment);DbgPrint("文件块对齐: %p\n", pNtHeaders->OptionalHeader.FileAlignment);DbgPrint("子系统: %x\n", pNtHeaders->OptionalHeader.Subsystem);DbgPrint("区段数目: %d\n", pNtHeaders->FileHeader.NumberOfSections);DbgPrint("时间日期标志: %x\n", pNtHeaders->FileHeader.TimeDateStamp);DbgPrint("首部大小: %x\n", pNtHeaders->OptionalHeader.SizeOfHeaders);DbgPrint("特征值: %x\n", pNtHeaders->FileHeader.Characteristics);DbgPrint("校验和: %x\n", pNtHeaders->OptionalHeader.CheckSum);DbgPrint("可选头部大小: %x\n", pNtHeaders->FileHeader.SizeOfOptionalHeader);DbgPrint("RVA 数及大小: %x\n", pNtHeaders->OptionalHeader.NumberOfRvaAndSizes);ZwUnmapViewOfSection(NtCurrentProcess(), pBaseAddress);ZwClose(hSection);ZwClose(hFile);Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}
运行如上这段代码,即可解析出ntdll.dll
模块的核心内容,如下图所示;
接着来实现解析节表,PE文件中的所有节的属性定义都被定义在节表中,节表由一系列的IMAGE_SECTION_HEADER
结构排列而成,每个结构邮过来描述一个节,节表总被存放在紧接在PE文件头的地方,也即是从PE文件头开始偏移为00f8h
的位置处,如下是节表头部的定义。
typedef struct _IMAGE_SECTION_HEADER {BYTE Name[IMAGE_SIZEOF_SHORT_NAME];union {DWORD PhysicalAddress;DWORD VirtualSize; // 节区尺寸} Misc;DWORD VirtualAddress; // 节区RVADWORD SizeOfRawData; // 在文件中对齐后的尺寸DWORD PointerToRawData; // 在文件中的偏移DWORD PointerToRelocations; // 在OBJ文件中使用DWORD PointerToLinenumbers;WORD NumberOfRelocations;WORD NumberOfLinenumbers;DWORD Characteristics; // 节区属性字段
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
其中,Name是该节的名称,VirtualAddress
是该节在内存中的虚拟地址,SizeOfRawData
是该节在文件中的大小,PointerToRawData
是该节在文件中的偏移地址,Characteristics
描述了该节的属性,例如是否可读、可写、可执行等。
节表通常位于PE结构的文件头后面,它包含了多个节表项,每个节表项描述了一个节的信息,包括:
- 节名称:每个节都有一个名称,例如代码节的名称为
.text
,数据节的名称为.data
等; - 节大小:该节的大小,以字节为单位;
- 节的虚拟地址:该节在内存中的虚拟地址;
- 节的物理地址:该节在文件中的偏移地址;
- 节的属性:例如该节是否可读、可写、可执行等。
总的来说,节表记录了PE文件中每个区域的详细信息,这些信息对于可执行文件或DLL的加载和运行都非常重要。
解析节表也很容易实现,首先通过pFileHeader->NumberOfSections
获取到节数量,然后循环解析直到所有节输出完成,这段代码实现如下所示。
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");NTSTATUS status = STATUS_SUCCESS;HANDLE hFile = NULL;HANDLE hSection = NULL;PVOID pBaseAddress = NULL;UNICODE_STRING FileName = { 0 };// 初始化字符串RtlInitUnicodeString(&FileName, L"\\??\\C:\\Windows\\System32\\ntdll.dll");// 内存映射文件status = KernelMapFile(FileName, &hFile, &hSection, &pBaseAddress);if (!NT_SUCCESS(status)){return 0;}// 获取PE头数据集PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBaseAddress;PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader + pDosHeader->e_lfanew);PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHeaders);PIMAGE_FILE_HEADER pFileHeader = &pNtHeaders->FileHeader;DWORD NumberOfSectinsCount = 0;// 获取区块数量NumberOfSectinsCount = pFileHeader->NumberOfSections;DWORD64 *difA = NULL; // 虚拟地址开头DWORD64 *difS = NULL; // 相对偏移(用于遍历)difA = ExAllocatePool(NonPagedPool, NumberOfSectinsCount*sizeof(DWORD64));difS = ExAllocatePool(NonPagedPool, NumberOfSectinsCount*sizeof(DWORD64));DbgPrint("节区名称 相对偏移\t虚拟大小\tRaw数据指针\tRaw数据大小\t节区属性\n");for (DWORD temp = 0; temp<NumberOfSectinsCount; temp++, pSection++){DbgPrint("%10s\t 0x%x \t 0x%x \t 0x%x \t 0x%x \t 0x%x \n",pSection->Name, pSection->VirtualAddress, pSection->Misc.VirtualSize,pSection->PointerToRawData, pSection->SizeOfRawData, pSection->Characteristics);difA[temp] = pSection->VirtualAddress;difS[temp] = pSection->VirtualAddress - pSection->PointerToRawData;}ZwUnmapViewOfSection(NtCurrentProcess(), pBaseAddress);ZwClose(hSection);ZwClose(hFile);Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}
运行驱动程序,即可输出ntdll.dll
模块的节表信息,如下图;
相关文章:

9.3 Windows驱动开发:内核解析PE结构节表
在笔者上一篇文章《内核解析PE结构导出表》介绍了如何解析内存导出表结构,本章将继续延申实现解析PE结构的PE头,PE节表等数据,总体而言内核中解析PE结构与应用层没什么不同,在上一篇文章中LyShark封装实现了KernelMapFile()内存映…...

Zephyr:Direct Distillation of LM Alignment
Zephyr:Direct Distillation ofLM Alignment IntroductionMethod Introduction dSFT已经被可以提升模型的指令遵循能力的准确性,但是student model 不会超过 teacher model。 作者认为 dSFT虽然可以让模型更好的理解用户意图,但是无法与人类…...
二叉树--算法题总结
1、利用层序遍历的产生的字符串来创建二叉树 /*** 使用层序遍历的字符串创建二叉树* param treeInfo* return*/public static TreeNode generateTreeNodeSecond(String treeInfo) {LinkedList<TreeNode> treeNodeLinkedList new LinkedList<>();if(StringUtils.is…...

PyQt6 QLabel标签控件
锋哥原创的PyQt6视频教程: 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计21条视频,包括:2024版 PyQt6 Python桌面开发 视频教程(无废话…...
即时通讯技术文集(第24期):音视频WebRTC好文合集 [共20篇]
为了更好地分类阅读 52im.net 总计1000多篇精编文章,我将在每周三推送新的一期技术文集,本次是第 24 期。 [- 1 -] 开源实时音视频技术WebRTC的现状 [链接] http://www.52im.net/article-126-1.html [摘要] 作为Google开源的技术,WebRTC并不…...

HTML+CSS+JS网页设计与制作摄影类个人网页
可以使用网页三剑客htmlcssjs实现网页设计与制作,页面排版布局高端大气。 使用HTMLCSS页面布局设计,HTMLCSSJS网页设计与制作摄影类个人网页,这是一个优质的个人网页制作。 凭借简约的设计风格、精湛的制作工艺,突破与创新的理念…...

U-boot(五):启动内核
本文主要探讨210的uboot启动内核过程。 嵌入式系统状态启动 未上电时bootloader、kernel、rootfs以镜像形式存储在启动介质中(X210为iNand/SD卡),运行时搬运到DDR中 未上电时u-boot.bin,zImage,rootfs在SD卡中各自对应的分区中,启动时去对应分区寻找(分区表一…...

tp8 使用rabbitMQ(2)工作队列
代码的参数说明在 第一小节的代码中,如果需要可移步到第一节中查看 工作队列 工作队列(又称:任务队列——Task Queues)是为了避免等待一些占用大量资源、时间的操作。当我们把任务(Task)当作消息发送到队列…...

ZKP11.4 Use CI to instantiate Fiat-Shamir
ZKP学习笔记 ZK-Learning MOOC课程笔记 Lecture 11: From Practice to Theory (Guest Lecturer: Alex Lombardi) 11.4 Use CI to instantiate Fiat-Shamir Avoid Bad Challenges Def: Given false claim x x x and a first message α \alpha α, a challenge β \beta …...
华为云编译构建CodeArts Build常见问答汇总
1.【Build】公有云编译构建是否支持导入外部机器做执行机 答:参考链接:https://support.huaweicloud.com/usermanual-devcloud/devcloud_01_0017.html • 使用代理机功能,需要配备1台4U8G或以上规格、磁盘>80GB的主机。 • 安装代理的…...

009 OpenCV 二值化 threshold
一、环境 本文使用环境为: Windows10Python 3.9.17opencv-python 4.8.0.74 二、二值化算法 2.1、概述 在机器视觉应用中,OpenCV的二值化函数threshold具有不可忽视的作用。主要的功能是将一幅灰度图进行二值化处理,以此大幅降低图像的数…...
基于python的NBA球员数据可视化分析的设计与实现
完整下载:基于python的NBA球员数据可视化分析的设计与实现.docx 基于python的NBA球员数据可视化分析的设计与实现 Design and Implementation of NBA Player Data Visualization Analysis based on Python 目录 目录 2 摘要 3 关键词 4 第一章 引言 4 1.1 研究背景 …...

《使用Python将Excel数据批量写入MongoDB数据库》
在数据分析及处理过程中,我们经常需要将数据写入数据库。而MongoDB作为一种NoSQL数据库,其具有强大的可扩展性、高性能以及支持复杂查询等特性,广泛用于大规模数据存储和分析。在这篇文章中,我们将使用Python编写一个将Excel数据批…...

leetcode_828_统计子串中的唯一字符
题意:所有子串中单个字符出现的次数和 问题转化:对于串中的每个字符,只包含其一次的所有子串的个数和 关于求只包含某位置字符一次的子串个数 class Solution { public:int uniqueLetterString(string s) {/* ...A...A...A...*/int n s.size…...

「Java开发中文指南」IntelliJ IDEA插件安装(一)
IntelliJ IDEA是java编程语言开发的集成环境。IntelliJ在业界被公认为最好的Java开发工具,尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能是非常强大的。 插件扩展了Intel…...
单机多卡训练
参考几个不错的帖子(还没来得及整理): 基于pytorch多GPU单机多卡训练实践_多卡训练效果不如单卡-CSDN博客 关于PyTorch单机多卡训练_能用torch.device()实现多卡训练吗-CSDN博客 Pytorch多机多卡分布式训练 - 知乎 (zhihu.com) 当代研究生…...

数据库基础教程之数据库的创建(一)
双击打开Navicat,点击:文件-》新建连接-》PostgreSQL 在下图新建连接中输入各参数,然后点击:连接测试,连接成功后再点击确定。 点击新建数据库 数据库设置如下:...

Python教程:DataFrame列数据类型的转换
Pandas提供了多种数据类型转换方法。可以使用astype()函数来转换数据类型。例如,可以将字符串类型的列转换为整数类型的列: # Author : 小红牛 # 微信公众号:wdPython import pandas as pd# 创建包含字符串类型列的DataFrame df pd.DataFra…...
4-Python与设计模式--抽象工厂模式
4-Python与设计模式–抽象工厂模式 一、快餐点餐系统 想必大家一定见过类似于麦当劳自助点餐台一类的点餐系统吧。在一个大的触摸显示屏上, 有三类可以选择的上餐品: 汉堡等主餐、小食、饮料。当我们选择好自己需要的食物,支付完成后&#…...

STM32 默认时钟更改 +debug调试
STM32时钟 文章目录 STM32时钟前言一、修改系统时钟二、DEBUG 前言 为什么我们要改STM32的时钟呢,打个比方在做SPI驱动的时候,需要16M的时钟,但是stm32默认是72的分频分不出来,这个时候我们就要改系统时钟了,那么怎么…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...