私有句柄表

news2025/2/27 11:09:05

私有句柄表

实验环境

  • win7 x86

什么是私有句柄表?

私有句柄表是操作系统内部的一种数据结构,用于存储一个进程所拥有的句柄(或称为句柄对象)的信息。在操作系统中,句柄是一个标识符,用于唯一标识一个对象,例如文件、套接字、管道等等。GPT是这样说的,
那我就举个例子让大家更简单的理解:比如我们使用OpenProcessAPI成功打开一个进程时,我们便会得到一个返回值,这个返回值就叫作句柄,我们可以通过这个句柄来间接操作我们OpenProcess打开的那个进程,句柄值会被放入我们打开者的私有句柄表里,我们用到的时候就会去私有句柄表找出来用,当我们不再使用的时候,比如调用CloseHandle便可以把刚才那个句柄值从私有句柄表中移除,随之我们也就无法以常规的方式去操作刚才打开的那个进程。

从内核中看私有句柄表

私有句柄表所在的位置如下:
EPROCESS—>ObjectTable[_HANDLE_TABLE]—>TableCode&0xFFFFFFF8—>第一层句柄表或者句柄表指针表的地址

nt!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x098 ProcessLock      : _EX_PUSH_LOCK
   +0x0a0 CreateTime       : _LARGE_INTEGER
   +0x0a8 ExitTime         : _LARGE_INTEGER
   +0x0b0 RundownProtect   : _EX_RUNDOWN_REF
   +0x0b4 UniqueProcessId  : Ptr32 Void
   +0x0b8 ActiveProcessLinks : _LIST_ENTRY
   +0x0c0 ProcessQuotaUsage : [2] Uint4B
   +0x0c8 ProcessQuotaPeak : [2] Uint4B
   +0x0d0 CommitCharge     : Uint4B
   +0x0d4 QuotaBlock       : Ptr32 _EPROCESS_QUOTA_BLOCK
   +0x0d8 CpuQuotaBlock    : Ptr32 _PS_CPU_QUOTA_BLOCK
   +0x0dc PeakVirtualSize  : Uint4B
   +0x0e0 VirtualSize      : Uint4B
   +0x0e4 SessionProcessLinks : _LIST_ENTRY
   +0x0ec DebugPort        : Ptr32 Void
   +0x0f0 ExceptionPortData : Ptr32 Void
   +0x0f0 ExceptionPortValue : Uint4B
   +0x0f0 ExceptionPortState : Pos 0, 3 Bits
   +0x0f4 ObjectTable      : Ptr32 _HANDLE_TABLE   //进程对应私有句柄表有关结构地址

nt!_HANDLE_TABLE
   +0x000 TableCode        : Uint4B
   +0x004 QuotaProcess     : Ptr32 _EPROCESS
   +0x008 UniqueProcessId  : Ptr32 Void
   +0x00c HandleLock       : _EX_PUSH_LOCK
   +0x010 HandleTableList  : _LIST_ENTRY
   +0x018 HandleContentionEvent : _EX_PUSH_LOCK
   +0x01c DebugInfo        : Ptr32 _HANDLE_TRACE_DEBUG_INFO
   +0x020 ExtraInfoPages   : Int4B
   +0x024 Flags            : Uint4B
   +0x024 StrictFIFO       : Pos 0, 1 Bit
   +0x028 FirstFreeHandle  : Uint4B
   +0x02c LastFreeHandleEntry : Ptr32 _HANDLE_TABLE_ENTRY
   +0x030 HandleCount      : Uint4B
   +0x034 NextHandleNeedingPool : Uint4B
   +0x038 HandleCountHighWatermark : Uint4B

.TableCode的值的最后3bit位代表着这个私有句柄表有几层,如果最后3bit位为0则代表仅有一层,1的话为两层,2的话最多为三层,以此类推,每张句柄表存有4096/8个句柄值,但是我们日常使用的电脑中最多有两层就很够多了,原因如下:
当只有一层的时候那么TableCode直接指向了私有句柄表的首个值,每个值占8字节,一个表的内存大小为4096个字节,一个句柄值为8字节,所以此时能存下的最大句柄值个数为4096/8个;当为两层的时候,TableCode指向了一张句柄表指针的表,TableCode+4*(层数-1)存的就是对应句柄表的指针,这张表依旧是4096个字节,那也就是说总共有4096/4个句柄表指针,那么一个句柄表有4096/8个句柄值,那此时就有(4096/4)x(4096/8)个句柄值了,一次类推,有点类似呈现了短期的指数大爆炸。那通常情况下我们的常规进程是不会打开这么多对象的,很多常规进程基本上就只有1层句柄表。

那句柄和对象的关系是啥呢?
在内核里,私有句柄的句柄值&0xFFFFFFF8后便指向了一个_OBJECT_HEADER结构体,结构体内存布局如下

nt!_OBJECT_HEADER
   +0x000 PointerCount     : Int4B
   +0x004 HandleCount      : Int4B
   +0x004 NextToFree       : Ptr32 Void
   +0x008 Lock             : _EX_PUSH_LOCK
   +0x00c TypeIndex        : UChar       //对象类型
   +0x00d TraceFlags       : UChar
   +0x00e InfoMask         : UChar
   +0x00f Flags            : UChar
   +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged : Ptr32 Void
   +0x014 SecurityDescriptor : Ptr32 Void
   +0x018 Body             : _QUAD                //对象地址

其中,0xc的位置代表了该句柄对应的对象是一个什么类型的对象,比如文件、进程、线程等,0x18的位置便指向了该句柄对应的对象结构。若句柄对应的对象是一个进程对象那么0x18的位置存的就是对应进程对象的_EPROCESS的结构,可以从这个结构便获得进程名、进程ID等等信息

有什么用

既然只要进程对象被打开,那么打开者就会获得打开进程对象的句柄值,并且放入私有句柄表,进而以一定权限操作打开进程,那当我们想知道我们的进程是否被打开或者有哪些进程打开了,我们是否可以通过遍历所有进程的私有句柄表来判断我们进程是否被打开操作呢?进而检测是否有非法内存操作的风险

逆向思路

第一:我们需要的是私有句柄表,那我们就需要找到对应的TableCode;
第二:TableCode存在ObjectTable中,那我们就需要找到任意一个进程的ObjectTable,随后通过该结构[HANDLE_TABLE]的HandleTableList 成员遍历得到所有进程的ObjectTable地址,ObjectTable对象的HANDLE_TABLE结构如下:

nt!_HANDLE_TABLE
   +0x000 TableCode        : 0x968e8000                          
   +0x004 QuotaProcess     : 0x864075e0 _EPROCESS
   +0x008 UniqueProcessId  : 0x00000a98 Void
   +0x00c HandleLock       : _EX_PUSH_LOCK
   // 所有进程私有句柄表的双向链表的某个节点
   +0x010 HandleTableList  : _LIST_ENTRY [ 0x83f50e68 - 0x8bec8960 ] 
   +0x018 HandleContentionEvent : _EX_PUSH_LOCK
   +0x01c DebugInfo        : (null) 
   +0x020 ExtraInfoPages   : 0n0 
   +0x024 Flags            : 0
   +0x024 StrictFIFO       : 0y0
   +0x028 FirstFreeHandle  : 0xc4
   +0x02c LastFreeHandleEntry : 0x968e8ff8 _HANDLE_TABLE_ENTRY
   +0x030 HandleCount      : 0x30
   +0x034 NextHandleNeedingPool : 0x800
   +0x038 HandleCountHighWatermark : 0x31

第三:要想获得任意一个进程ObjectTable的话我们就获取System进程的EPROCESS结构来获取System的ObjectTable地址;

用代码来叙说细节

1 获取System进程的EPROCESS,驱动所属进程就是System

PEPROCESS pEprocess = PsGetCurrentProcess();

2 获取指定进程的私有句柄表地址ObjectTable

PULONG pHanldeForSystem = *(PULONG)((PUCHAR)pEprocess + 0xf4);

3 获取进程ObjectTable的HandleTableList的地址,这个成员是双向链表的某个节点,所有正常进程的ObjectTable+0x10的位置都在这个链表上

PLIST_ENTRY pPriListForSys = (PLIST_ENTRY)((PUCHAR)pHanldeForSystem + 0x10);

4 遍历HandleTableList链表获得所有进程的ObjectTable,并保存下来

PLIST_ENTRY pTmp = pPriListForSys;
int cout = 0;
do 
{
    pTmp = pTmp->Flink;
    cout++;
} while (pTmp != pPriListForSys);
pTmp = pPriListForSys;
PULONG pHandleTable = ExAllocatePool(NonPagedPool, cout*sizeof(PULONG));
RtlZeroMemory(pHandleTable, cout*sizeof(PULONG));
//保存ObjectTable
for (int i = 0; i < cout;i++)
{
    pHandleTable[i] = (PULONG)((PUCHAR)(pTmp->Flink) - 0x10);
    pTmp = pTmp->Flink;
}

5 获取所有进程的ObjectTable–>TableCode,并保存下来

PULONG pTableCode = ExAllocatePool(NonPagedPool, cout*sizeof(PULONG));
RtlZeroMemory(pTableCode, cout*sizeof(PULONG));
for (int i = 0; i < cout;i++)
{
    pTableCode[i] = *(PULONG)pHandleTable[i];
    DbgPrintEx(77, 0, "[db]tablecode地址为:%p\n", pTableCode[i]);
}

6 筛选出只有一层的TableCode,因为超过两层的通常都是系统进程之类的,恶意进程的通常只有一层

ULONG uTmpCode = 0;
int count_2 = 0;
for (int i = 0; i < cout; i++)
{
    uTmpCode =pTableCode[i] & 0x00000007;
    if (uTmpCode>0)
    {
        continue;
    }
    count_2++;
}
count_2 = count_2-1;
PULONG pOneTableCode = ExAllocatePool(NonPagedPool, count_2*sizeof(PULONG));
RtlZeroMemory(pOneTableCode, count_2*sizeof(PULONG));
count_2 = 0;
for (int i = 0; i < cout; i++)
{
    uTmpCode = pTableCode[i] & 0x00000007;
    if (uTmpCode>0)
    {
        continue;
    }
    pOneTableCode[count_2] = pTableCode[i] & 0xFFFFFFF8;
    //此时count_2是一层句柄TableCode的数量
    count_2++;
}

7 遍历出每个只有一层句柄表的表内句柄值,并打印出句柄值(上面忘记说了,句柄值总共64位,低32位才是我们目前说的句柄值),

类型值(我的系统上进程对象的类型是7,你们可以通过取System的进程对应的类型值作比较,只要类型值一样就是进程,我为了方便直接就写了调试出来的0x7),进程名(前提是句柄对应的对象是进程)

count_2 = count_2 - 1;
ULONG64 pTmpTableValue = 0;
ULONG uHight32 = 0;
ULONG uLow32 = 0;
UCHAR uType = 0;
PUCHAR pProcessName = NULL;
for (int i = 0; i < count_2-2;i++)
{
    DbgPrintEx(77, 0, "[db]第%d个表内容,地址为:%p:\n", i, pOneTableCode[i]);
    for (int j = 0; j < 512;j++)
    {
        if (pOneTableCode[i]==0)
        {
            break;
        }
        pTmpTableValue = *((PULONG64)((PUCHAR)pOneTableCode[i] + j * 8));
        uHight32 = pTmpTableValue >> 32;
        uLow32 = pTmpTableValue & 0x00000000ffffffff;
        uLow32 = uLow32 & 0xFFFFFFF8;
        if (uLow32 != 0)
        {
            DbgPrintEx(77, 0, "[db]第%d个句柄表的第%d个值为%lx`%lx\n", i, j,
                uHight32, uLow32);
            uType = *(PUCHAR)((PUCHAR)uLow32 + 0xc);
            DbgPrintEx(77, 0, "[db]Type is %d\n", uType);
            if (uType == 0x7)
            {
                pProcessName = (PUCHAR)((PUCHAR)uLow32 + 0x18 + 0x16c);
                DbgPrintEx(77, 0, "[db][exeinfor]:Processname:%s\n", pProcessName);
            }

        }
    }
}

8 判断是否被打开的话我们直接添加一个全局变量FLAG来计数我们进程对应的句柄在其他进程中出现的次数,比如超过指定次便判断为风险,或者也可以查看是那个进程打开的,获取进程名,我这里就只判断了打开次数,接下来看一下完整代码,以判断123.exe是否被打开1次以上为风险来判断提示,我用了两个exe去OpenProcess 123.exe去实验

#include <ntifs.h>
#include <string.h>

INT openflag = 0;

VOID UnloadDriver(PDRIVER_OBJECT pDriver)
{

}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath)
{
        //获取当前进程system
        PEPROCESS pEprocess = PsGetCurrentProcess();
        //获取指定进程的私有句柄表地址handle_table
        PULONG pHanldeForSystem = *(PULONG)((PUCHAR)pEprocess + 0xf4);
        //获取handle_table中的Table_code 
        PULONG pTableCodeForSystem = *pHanldeForSystem;
        //获取所有进程私有句柄表的地址
        PLIST_ENTRY pPriListForSys = (PLIST_ENTRY)((PUCHAR)pHanldeForSystem + 0x10);
        //打印测试
        DbgPrintEx(77, 0, "[db]地址:\npEprocess:%p\npHanldeForSystem:%p,\nTableCodeForSystem:%p\npPriListForSys:%p\n", pEprocess,
                pHanldeForSystem,
                pTableCodeForSystem,
                pPriListForSys);
        /*
        循环所有进程的私有句柄表
        定义一个tmp去遍历
        把所有私有handle_table的地址保存下来
        */
        PLIST_ENTRY pTmp = pPriListForSys;
        int cout = 0;
        do 
        {
                DbgPrintEx(77, 0, "[db]第%d个handle_table list地址为:%p\n", cout, pTmp->Flink);
                pTmp = pTmp->Flink;
                cout++;
        } while (pTmp != pPriListForSys);
        pTmp = pPriListForSys;
        PULONG pHandleTable = ExAllocatePool(NonPagedPool, cout*sizeof(PULONG));
        RtlZeroMemory(pHandleTable, cout*sizeof(PULONG));
        //保存handle_table
        for (int i = 0; i < cout;i++)
        {
                pHandleTable[i] = (PULONG)((PUCHAR)(pTmp->Flink) - 0x10);
                DbgPrintEx(77, 0, "[db]handle_table地址为:%p\n", pHandleTable[i]);
                pTmp = pTmp->Flink;
        }
        //获取所有的tablecode
        PULONG pTableCode = ExAllocatePool(NonPagedPool, cout*sizeof(PULONG));
        RtlZeroMemory(pTableCode, cout*sizeof(PULONG));
        for (int i = 0; i < cout;i++)
        {
                pTableCode[i] = *(PULONG)pHandleTable[i];
                DbgPrintEx(77, 0, "[db]tablecode地址为:%p\n", pTableCode[i]);
        }
        //筛选出只有一层的TableCode
        ULONG uTmpCode = 0;
        int count_2 = 0;
        for (int i = 0; i < cout; i++)
        {
                uTmpCode =pTableCode[i] & 0x00000007;
                if (uTmpCode>0)
                {
                        continue;
                }
                count_2++;
        }
        count_2 = count_2-1;
        PULONG pOneTableCode = ExAllocatePool(NonPagedPool, count_2*sizeof(PULONG));
        RtlZeroMemory(pOneTableCode, count_2*sizeof(PULONG));
        count_2 = 0;
        for (int i = 0; i < cout; i++)
        {
                uTmpCode = pTableCode[i] & 0x00000007;
                if (uTmpCode>0)
                {
                        continue;
                }
                pOneTableCode[count_2] = pTableCode[i] & 0xFFFFFFF8;
                DbgPrintEx(77, 0, "[db]一层的tablecode:%p\n", pOneTableCode[count_2]);
                //此时count_2是一层句柄TableCode的数量
                count_2++;
        }
        //遍历出每个一层句柄表内的值
        count_2 = count_2 - 1;
        ULONG64 pTmpTableValue = 0;
        ULONG uHight32 = 0;
        ULONG uLow32 = 0;
        UCHAR uType = 0;
        PUCHAR pProcessName = NULL;
        ULONG uFindNum = 0;
        for (int i = 0; i < count_2-2;i++)
        {
                DbgPrintEx(77, 0, "[db]第%d个表内容,地址为:%p:\n", i, pOneTableCode[i]);
                for (int j = 0; j < 512;j++)
                {
                        if (pOneTableCode[i]==0)
                        {
                                break;
                        }
                        pTmpTableValue = *((PULONG64)((PUCHAR)pOneTableCode[i] + j * 8));
                        uHight32 = pTmpTableValue >> 32;
                        uLow32 = pTmpTableValue & 0x00000000ffffffff;
                        uLow32 = uLow32 & 0xFFFFFFF8;
                        if (uLow32 != 0)
                        {
                                DbgPrintEx(77, 0, "[db]第%d个句柄表的第%d个值为%lx`%lx\n", i, j,
                                        uHight32, uLow32);
                                uType = *(PUCHAR)((PUCHAR)uLow32 + 0xc);
                                DbgPrintEx(77, 0, "[db]Type is %d\n", uType);
                                if (uType == 0x7)
                                {
                                        pProcessName = (PUCHAR)((PUCHAR)uLow32 + 0x18 + 0x16c);
                                        if (strcmp(pProcessName,"123.exe")==0)
                                        {
                                                openflag++;
                                        }
                                        DbgPrintEx(77, 0, "[db][exeinfor]:Processname:%s\n", pProcessName);
                                }

                        }
                        uFindNum++;
                }
        }
        DbgPrintEx(77, 0, "[db]总共翻越了%d坐山\n", uFindNum);
        if (openflag>1)
        {
                DbgPrintEx(77, 0, "[db]很遗憾,我们翻山越岭找到了它,它出现了%d次,有风险\n", openflag);
        }
        pDriver->DriverUnload = UnloadDriver;
        return STATUS_SUCCESS;
}

在这里插入图片描述

经过实验得到123.exe进程对象句柄被至少3个其他进程拥有,其中两个是我自己写的exe,另外一个应该是某个系统进程,此时我们看到总过遍历了判断18944次内存区域,打印了接近几分钟,这也许就是翻山越岭的爱吧!(主要是我打印费时了)

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

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

相关文章

【iOS】NSError**和__autoreleasing场景

前言 在看JSONModel源码的时候&#xff0c;JSONModel的自定义Error的方法一直在报错 - (BOOL)validate:(NSError *__autoreleasing *)error {}这个方法在定义error的时候添加上了__autoreleasing修饰符&#xff0c;涉及到了__autoleasing的显式隐式调用就去了解了一下。 发现…

【C++ 三】一维数组、二维数组

数组概述、一维数组、二维数组 文章目录数组概述、一维数组、二维数组前言1 数组1.1 概述2 一维数组2.1 一维数组定义方式2.2 一维数组数组名2.3 冒泡排序3 二维数组3.1 二维数组定义方式3.2 二维数组数组名总结前言 本文包含数组概述、一维数组、二维数组。 1 数组 1.1 概述…

python web 医院加密处方系统

医院加密处方系统 环境要求&#xff1a; 1、python3.8 2、vue 3、django 4、mysql 5、ruoyi快速开发框架 登录界面 可以登录和注册&#xff0c;注册分三个角色&#xff0c;主治医师和药品医师还有配制医师&#xff0c;有验证码和用户权限功能&#xff0c;用户管理、部…

AIGC下一站:期待、警惕充斥着AI剪辑师的世界

上月底&#xff0c;名为“chaindrop”的 Reddit 用户&#xff0c;在 r/StableDiffusion subreddit 上分享了一个由人工智能生成的视频&#xff0c;在业内引起了不小的争议。 视频中&#xff0c;一个由 AI 生成的丑陋畸形的 “威尔史密斯”&#xff0c;以一种可怕的热情将一把意…

vba:消息框基础,massagebox

常量常量值说明vbOKOnly0只显示“确定”按钮&#xff08;缺省值&#xff09;VbOKCancel1显示“确定”和“取消”按钮VbAbortRetryIgnore2显示“终止”、“重试”和“忽略” 按钮VbYesNoCancel3显示“是”、“否”和“取消”按钮VbYesNo4显示“是”和“否”按钮VbRetryCancel5显…

pkg-config

前言 在介绍 pkg-config 之前&#xff0c;先讲一个我的经历。 有一次我想用 libgtk 库在 ubuntu 上实现一个简单的图形界面&#xff0c;就像下面代码 #include <gtk/gtk.h>int main(int argc, char *argv[]) {GtkWidget *window;gtk_init(&argc, &argv);window…

UG NX二次开发(C#)-建模-获取曲面的法矢

文章目录 1、前言2、曲面的法矢示例3、获取曲面的法矢3.1 采用 uFModl.AskFaceProps实现3.2采用 uFSo实现4、结论1、前言 在UG NX二次开发过程中,我们想获取曲面的法矢,是通过ufun函数来获取的。我们以一个平面和一个曲面来说明其开发过程。 2、曲面的法矢示例 创建一张曲…

商城系统开发方案分析

互联网的不断发展&#xff0c;电商行业已经成为了当前最重要的商业形式之一。商城系统的开发也因此而备受关注。商城系统的开发是针对B2C、B2B2C等多种商业模式&#xff0c;如用户熟知的SHOP、商派等一系列商城系统&#xff0c;将商品和服务进行在线销售的一个综合性平台。那么…

【软考备战·希赛网每日一练】2023年4月17日

文章目录一、今日成绩二、错题总结第一题第二题第三题第四题三、知识查缺题目及解析来源&#xff1a;2023年04月17日软件设计师每日一练 一、今日成绩 二、错题总结 第一题 解析&#xff1a; 第二题 解析&#xff1a; 第三题 解析&#xff1a; SCAN调度算法 也叫 “电梯”算…

RocketMQ 发送批量消息、过滤消息和事务消息

前面我们知道RocketMQ 发送延时消息与顺序消息&#xff0c;现在我们看下怎么发送批量消息、过滤消息和事务消息。 发送批量消息 限制是这些批量消息应该有相同的 topic&#xff0c;相同的 waitStoreMsgOK&#xff0c;而且不能是延时消息。 此外&#xff0c;这一批消息的总大小…

如何合理选择ClickHouse表主键

ClickHouse提供索引和数据存储的复杂机制&#xff0c;能够实现在高负载下仍有优异的读写性能。当创建MergeTree表时需要选择主键&#xff0c;主键影响大多数查询性能。本文介绍主键的工作原理&#xff0c;让我们知道如何选择合适的主键。 设置主键 MergeTree表可以设置主键&am…

香橙派5使用RK3588S内置NPU加速yolov5推理,实时识别数字达到50fps

前言&#xff1a; 香橙派5采用了RK3588S&#xff0c;内置的NPU达到了6Tops的算力&#xff0c;博主这里记录一下自己的踩坑过程&#xff0c;好不容易做出来的不能以后忘记了&#xff08;手动狗头&#xff09;。这里博主还在B站上发布了效果视频&#xff0c;大家感兴趣的话可以看…

TensorFlow 和 Keras 应用开发入门:1~4 全

原文&#xff1a;Beginning Application Development with TensorFlow and Keras 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 深度学习 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 不要担心自己的形…

Java 中的 非并发容器

1.四大类容器 java中容器主要有四大类&#xff0c;如下图所示 2.非并发容器 1) List 类 List 类 不支持并发的有 ArrayList 与 LinkedList ArrayList 底层实现 ArrayList 底层为 数组&#xff0c;由于数组的特性&#xff0c;非常适合用于 查询多&#xff0c;增删改的业务…

【数据结构学习1】数据结构

目录数据结构定义数据结构的构成逻辑结构逻辑结构的类型存储结构数据运算数据类型和抽象数据类型算法定义分析基础时间复杂度分析事前分析估算法 -> 分析算法的执行时间时间复杂度时间复杂度类型简化的算法时间复杂度分析空间复杂度分析数据结构 定义 数据&#xff1a;所有…

工作流调度系统 Azkaban介绍与安装(一)

文章目录前言1、为什么要用工作流调度系统2、常见的工作流调度系统1 集群规划2 配置 MySQL3 配置 Executor Server3.1 修改 azkaban.properties3.2 启动3.3 激活4 配置 Web Server4.1 修改 azkaban.properties4.2 修改azkaban-users.xml文件&#xff0c;添加 atguigu 用户4.3 启…

VM 虚拟机没有网络,无法Ping通

场景&#xff1a; 虚拟机用过&#xff0c;之前一切正常&#xff0c;使用NAT模式联网&#xff0c;配置了静态IP换了路由器&#xff0c;推测是主机IP网段变了无法使用ssh工具连接虚拟机&#xff0c;且相互都ping不通&#xff08;后来经历了主机可以ping通虚拟机&#xff0c;虚拟…

PWM寄存器初始化

本模块主要实现输出频率占空比可调的 PWM 波形功能和输入捕获功能&#xff0c;同时也可作为计数器使用。一、主要特性 1. 16位向上或向下计数器&#xff1b; 2. 支持最多6路PWM通道&#xff1b; 3. 每个通道支持输出比较或边缘对齐PWM模式波形输出&#xff0c;支持设置、清除、…

关于 CSDN-AI 机器人 programmer_ada —— 阿达·洛夫莱斯(Ada Lovelace)

收到早期文章的一条新评论&#xff1a; 文笔和内容稍稍透漏着机器人的风格&#xff0c;打开主页果不其然 看到个人介绍中的巴贝奇的分析机&#xff0c;突然觉得头像很是眼熟。 最近刚读了《人工智能简史》&#xff0c;第4章——从汇编语言到TensorFlow&#xff0c;人工智能的…

使用layui组件库制作进度条

使用layui组件库制作进度条 html代码 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>Example</title><!-- 引入 layui 的 CSS 文件 --><link rel"stylesheet" href"https://cdn.staticfil…