VC 消息钩子编程

article/2025/9/24 17:51:11

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

一、消息钩子的概念
1、基本概念
   Windows应用程序是基于消息驱动的,任何线程只要注册窗口类都会有一个消息队列用于接收用户输入的消息和系统消息。为了拦截消息,Windows提出了钩子的概念。钩子(Hook)是Windows消息处理机制中的一个监视点,钩子提供一个回调函数。当在某个程序中安装钩子后,它将监视该程序的消息,在指定消息还没到达窗口之前钩子程序先捕获这个消息。这样就有机会对此消息进行过滤,或者对Windows消息实现监控。
    2、分类
    消息钩子分为局部钩子和全局钩子。局部钩子是指仅拦截指定一个进程的指定消息,全局钩子将拦截系统中所有进程的指定消息。
    3、实现步骤
    使用钩子技术拦截消息通常分为如下几个步骤:
  •     设置钩子回调函数;(拦截到消息后所调用的函数)
  •     安装钩子;(使用SetWindowsHookEx函数)
  •     卸载钩子。(使用UnhookWindowsHookEx函数)
    4、功能
    利用消息钩子可以实现特效界面、同步消息、监控消息、自启动等功效。

二、病毒对消息钩子技术的利用
    计算机病毒经常利用消息钩子实现两种功能:
    1、监控用户按键,盗取用户信息。
    这样的病毒会启动一个常驻内存的EXE病毒进程,然后安装一个全局键盘消息钩子,钩子回调函数位于病毒进程中,这样系统中任何有按键操作的进程,其按键详细信息都会被病毒进程拦截记录。
    2、自启动
    这样的病毒会将钩子回调函数放在一个DLL文件中,然后安装一个全局消息(容易触发的消息,如WH_CBT、WH_GETMESSAGE等)钩子,这样凡响应该消息的进程都会自动加载病毒的DLL,病毒也就跟着自动运行了。

三、消息钩子病毒的对抗技术(重点)
    1、对抗技术原理
    对付消息钩子病毒方法很简单,只要将病毒安装的钩子卸载掉即可。(注意:对于系统中许多进程已经因为全局钩子而加载了病毒DLL的情况,并不需要去卸载这些DLL,只要安装的消息钩子被卸载那么对应的DLL也都会被在这些进程中自动卸载。)卸载钩子有两种方法:
    (1)、结束掉安装钩子的进程
    将设置钩子的进程结束,进程在退出之前会自行卸载掉该进程安装的所有消息钩子。这种方法很适合对付监控用户按键的病毒。
    (2)、获得消息钩子句柄,然后调用UnhookWindowsHookEx函数即可将消息钩子卸载。
    如果病毒单独启动了一个病毒进程安装了一个全局消息钩子,然后就常驻内存。这时我们将这个病毒进程结束掉即可。但是如果病毒在系统进程中注入代码而安装的钩子,这样钩子句柄就位于系统进程中,我们不可以结束系统进程,这时就只能获取这个消息钩子句柄,然后调用函数卸载。
    2、对抗技术实现细节
    对于结束掉安装钩子进程从而卸载病毒消息钩子的方法很容易实现,只要找到病毒进程结束即可。而对于获取病毒消息钩子句柄,然后调用函数卸载钩子的方法比较复杂,也是本文重点讨论的内容,将在下一个标题中详细介绍。

四、查找病毒消息钩子句柄然后卸载的方法实现(重点、难点)
    1、实现原理分析
    系统会将所有安装的钩子句柄保存在内核中,要查找病毒安装的消息钩子句柄,我们要枚举所有的消息钩子句柄。如何枚举稍后讲解,还要解决一个问题,就是在枚举过程中,我们怎么知道哪个句柄是病毒安装的呢?
    通过分析病毒样本我们通常可以得到病毒安装钩子就是为了令其他合法进程加载病毒DLL,所以它会将钩子回调函数写在该DLL中。在枚举消息钩子句柄时,同时也可以得到该句柄所对应的回调函数所属的DLL模块,根据这个DLL模块是不是病毒的DLL模块即可找到病毒的消息钩子句柄,最后将其卸载即可。
    关于如何枚举系统消息钩子句柄,对于不同的操作系统方法大不相同,这里介绍一种用户层读内存的方法,此方法仅在2000/XP系统下可用。
    在2000/XP系统下有一个Windows用户界面相关的应用程序接口User32.dll。它用于包括Windows窗口处理,基本用户界面等特性,如创建窗口和发送消息。当它被加载到内存后,它保存了所有Windows窗口、消息相关的句柄,其中就包括消息钩子句柄。这些句柄被保存在一块共享内存段中,通常称为R3层的GUI TABLE。所以只要我们找到GUI TABLE,然后在其中的句柄中筛选出消息钩子句柄。GUI TABLE这块内存段可以被所有进程空间访问。GUI TABLE被定义成如下结构:
typedef struct tagSHAREDINFO {
  struct tagSERVERINFO *pServerInfo;  //指向tagSERVERINFO结构的指针
  struct _HANDLEENTRY *pHandleEntry;  // 指向句柄表
  struct tagDISPLAYINFO *pDispInfo;  //指向tagDISPLAYINFO结构的指针
  ULONG ulSharedDelta;
  LPWSTR pszDllList;
} SHAREDINFO, *PSHAREDINFO;
    tagSHAREDINFO结构体的第一个成员pServerInfo所指向的tagSERVERINFO结构体定义如下。
typedef struct tagSERVERINFO {   
    short wRIPFlags ;             
    short wSRVIFlags ;           
    short wRIPPID ;             
    short wRIPError ;       
    ULONG cHandleEntries;          //句柄表中句柄的个数
}SERVERINFO,*PSERVERINFO;
    可以看出通过tagSERVERINFO结构的cHandleEntries成员即可得到tagSHAREDINFO结构的pHandleEntry成员所指向的句柄表中的句柄数。
    tagSHAREDINFO结构体的第二个成员pHandleEntry是指向_HANDLEENTRY结构体数组起始地址的指针,该数组的一个成员对应一个句柄。句柄结构体_HANDLEENTRY定义如下。
typedef struct _HANDLEENTRY{
    PVOID  pObject;            //指向句柄所对应的内核对象
    ULONG  pOwner;        
      BYTE  bType;               //句柄的类型
    BYTE  bFlags;      
    short  wUniq;     
}HANDLEENTRY,*PHANDLEENTRY;
    _HANDLEENTRY结构体成员bType是句柄的类型,通过该变量的判断可以筛选消息钩子句柄。User32中保存的句柄类型通常有如下种类。
typedef enum  _HANDLE_TYPE
{
        TYPE_FREE = 0,
        TYPE_WINDOW = 1 ,
        TYPE_MENU = 2,                     //菜单句柄
     TYPE_CURSOR = 3,                   //光标句柄
     TYPE_SETWINDOWPOS = 4,
        TYPE_HOOK = 5,                     //消息钩子句柄
     TYPE_CLIPDATA = 6  ,           
        TYPE_CALLPROC = 7,
        TYPE_ACCELTABLE = 8,
        TYPE_DDEACCESS = 9,
        TYPE_DDECONV = 10,
        TYPE_DDEXACT = 11,         
        TYPE_MONITOR = 12,
        TYPE_KBDLAYOUT = 13   ,         
        TYPE_KBDFILE = 14    ,           
        TYPE_WINEVENTHOOK = 15  ,      
        TYPE_TIMER = 16,
        TYPE_INPUTCONTEXT = 17  ,      
        TYPE_CTYPES = 18         ,       
        TYPE_GENERIC = 255            
}HANDLE_TYPE;
    _HANDLEENTRY结构体的成员pObject是指向句柄对应的内核对象的指针。
    这样只要通过pObject就可以得到句柄的详细信息(其中包括创建进程,线程、回调函数等信息),通过bType就可以的值句柄的类型。
_HANDLEENTRY结构体的其他成员可以忽略不看。
    (知识要点补充:如何在用户层程序中读取内核内存)
    需要注意的是,pObject指针指向的是内核内存,不可以在用户层直接访问内核内存。后面还有些地方也同样是内核内存,需要加以注意。应该把内核内存的数据读取到用户层内存才可以访问。且不可以直接访问,毕竟不是在驱动中。
    在用户层读取内核内存使用ZwSystemDebugControl函数,它是一个Native API。其原型如下。
NTSYSAPI
NTSTATUS
NTAPI
ZwSystemDebugControl(
    IN DEBUG_CONTROL_CODE ControlCode,//控制代码
   IN PVOID InputBuffer OPTIONAL,    //输入内存
   IN ULONG InputBufferLength,    //输入内存长度
   OUT PVOID OutputBuffer OPTIONAL,  //输出内存
   IN ULONG OutputBufferLength,    //输出内存长度
   OUT PULONG ReturnLength OPTIONAL  //实际输出的长度);
ZwSystemDebugControl函数可以用于读/写内核空间、读/写MSR、读/写物理内存、读/写IO端口、读/写总线数据、KdVersionBlock等。由第一个参数ControlCode控制其功能,可以取如下枚举值。
    typedef enum _SYSDBG_COMMAND {   
    //以下5个在Windows NT各个版本上都有
    SysDbgGetTraceInformation = 1,
      SysDbgSetInternalBreakpoint = 2,
      SysDbgSetSpecialCall = 3,
      SysDbgClearSpecialCalls = 4,
      SysDbgQuerySpecialCalls = 5,
    // 以下是NT 5.1 新增的
    SysDbgDbgBreakPointWithStatus = 6,
    //获取KdVersionBlock
      SysDbgSysGetVersion = 7,
    //从内核空间复制到用户空间,或者从用户空间复制到用户空间
    //但是不能从用户空间复制到内核空间
    SysDbgCopyMemoryChunks_0 = 8,
   //SysDbgReadVirtualMemory = 8,
    //从用户空间复制到内核空间,或者从用户空间复制到用户空间
    //但是不能从内核空间复制到用户空间
    SysDbgCopyMemoryChunks_1 = 9,
    //SysDbgWriteVirtualMemory = 9,
    //从物理地址复制到用户空间,不能写到内核空间
    SysDbgCopyMemoryChunks_2 = 10,
    //SysDbgReadVirtualMemory = 10,
    //从用户空间复制到物理地址,不能读取内核空间
    SysDbgCopyMemoryChunks_3 = 11,
    //SysDbgWriteVirtualMemory = 11,
    //读/写处理器相关控制块
    SysDbgSysReadControlSpace = 12,
    SysDbgSysWriteControlSpace = 13,
    //读/写端口
    SysDbgSysReadIoSpace = 14,
      SysDbgSysWriteIoSpace = 15,
    //分别调用RDMSR@4和_WRMSR@12
      SysDbgSysReadMsr = 16,
      SysDbgSysWriteMsr = 17,
    //读/写总线数据
    SysDbgSysReadBusData = 18,
      SysDbgSysWriteBusData = 19,
      SysDbgSysCheckLowMemory = 20,
// 以下是NT 5.2 新增的
    //分别调用_KdEnableDebugger@0和_KdDisableDebugger@0
      SysDbgEnableDebugger = 21,
      SysDbgDisableDebugger = 22,  
    //获取和设置一些调试相关的变量
    SysDbgGetAutoEnableOnEvent = 23,
      SysDbgSetAutoEnableOnEvent = 24,
      SysDbgGetPitchDebugger = 25,
      SysDbgSetDbgPrintBufferSize = 26,
      SysDbgGetIgnoreUmExceptions = 27,
      SysDbgSetIgnoreUmExceptions = 28
    } SYSDBG_COMMAND, *PSYSDBG_COMMAND;
    我们这里要读取内核内存,所以参数ControlCode应取值为SysDbgReadVirtualMemory。
当ControlCode取值为SysDbgReadVirtualMemory时,ZwSystemDebugControl函数的第4个参数和第5个参数被忽略,使用时传入0即可。第二个参数InputBuffer是一个指向结构体_MEMORY_CHUNKS的指针,该结构体定义如下。
typedef struct _MEMORY_CHUNKS {
    ULONG Address;      //内核内存地址指针(要读的数据)
    PVOID Data;         //用户层内存地址指针(存放读出的数据)
    ULONG Length;      //读取的长度
}MEMORY_CHUNKS, *PMEMORY_CHUNKS;
第三个参数InputBufferLength是_MEMORY_CHUNKS结构体的大小。使用sizeof运算符得到即可。
SysDbgReadVirtualMemory函数执行成功将返回0。否则返回错误代码。
为了方便使用,我们可以封装一个读取内核内存的函数GetKernelMemory,实现如下:
#define SysDbgReadVirtualMemory 8
//定义ZwSystemDebugControl函数指针类型
typedef DWORD (WINAPI *ZWSYSTEMDEBUGCONTROL)(DWORD,PVOID,
DWORD,PVOID,DWORD,PVOID);
BOOL GetKernelMemory(PVOID pKernelAddr, PBYTE pBuffer, ULONG uLength)

    MEMORY_CHUNKS mc ;
    ULONG uReaded = 0;
    mc.Address=(ULONG)pKernelAddr;  //内核内存地址
    mc.pData = pBuffer;//用户层内存地址
    mc.Length = uLength;       //读取内存的长度 
    ULONG st  = -1 ;
  //获得ZwSystemDebugControl函数地址
  ZWSYSTEMDEBUGCONTROL ZwSystemDebugControl = (ZWSYSTEMDEBUGCONTROL) GetProcAddress(
    GetModuleHandle("ntdll.dll"), "ZwSystemDebugControl");
  //读取内核内存数据到用户层
    st = ZwSystemDebugControl(SysDbgReadVirtualMemory, &mc, sizeof(mc), 0, 0, &uReaded);
    return st == 0;
}


    对于不同类型的句柄,其内核对象所属内存对应的结构体不同,对于消息钩子句柄,它的内核对象所属内存对应的结构体实际上是_HOOK_INFO类型,其定义如下。
typedef struct _HOOK_INFO
{
  HANDLE hHandle; //钩子的句柄
  DWORD Unknown1;
  PVOID Win32Thread; //一个指向 win32k!_W32THREAD 结构体的指针
  PVOID Unknown2;
  PVOID SelfHook; //指向结构体的首地址
  PVOID NextHook; //指向下一个钩子结构体
  int iHookType; //钩子的类型。
  DWORD OffPfn; //钩子函数的地址偏移,相对于所在模块的偏移
  int iHookFlags; //钩子标志
  int iMod; //钩子函数做在模块的索引号码,利用它可以得到模块基址
  PVOID Win32ThreadHooked; //被钩的线程结构指针
} HOOK_INFO,*PHOOK_INFO;
由上可以看出,得到钩子内核对象数据后,该数据对应HOOK_INFO结构体信息。其中:
hHandle是钩子句柄,使用它就可以卸载钩子。
iHookType是钩子的类型,消息钩子类型定义如下。
typedef enum  _HOOK_TYPE{
        MY_WH_MSGFILTER = -1,
        MY_WH_JOURNALRECORD = 0,
        MY_WH_JOURNALPLAYBACK = 1,
        MY_WH_KEYBOARD = 2,
        MY_WH_GETMESSAGE = 3,
        MY_WH_CALLWNDPROC = 4,
        MY_WH_CBT = 5,
        MY_WH_SYSMSGFILTER = 6,
        MY_WH_MOUSE = 7,
        MY_WH_HARDWARE = 8,
        MY_WH_DEBUG = 9,
        MY_WH_SHELL = 10,
        MY_WH_FOREGROUNDIDLE = 11,
        MY_WH_CALLWNDPROCRET = 12,
        MY_WH_KEYBOARD_LL = 13,
        MY_WH_MOUSE_LL = 14
}HOOK_TYPE;
    OffPfn是钩子回调函数的偏移地址,该偏移地址是相对于钩子函数所在模块基址的偏移。
   Win32Thread是指向_W32THREAD结构体的指针,通过这个结构体可以获得钩子所在进程ID和线程ID。该结构体定义如下。
typedef struct _W32THREAD
{
    PVOID    pEThread ;    //该指针用以获得进程ID和线程ID
    ULONG   RefCount ;
    ULONG  ptlW32 ;
    ULONG  pgdiDcattr ;
    ULONG   pgdiBrushAttr ;
    ULONG   pUMPDObjs ;
    ULONG    pUMPDHeap ;
    ULONG    dwEngAcquireCount ;
    ULONG    pSemTable ;
    ULONG    pUMPDObj ;
    PVOID ptl;
    PVOID ppi;            //该指针用以获得模块基址
}W32THREAD, *PW32THREAD;
    _W32THREAD结构体第一个参数pEThread指向的内存偏移0x01EC处分别保存着进程ID和线程ID。注意pEThread指针指向的内存是内核内存。
   _W32THREAD结构体最后一个参数ppi指向的内存偏移0xA8处是所有模块基址的地址表,   _HOOK_INFO结构体的iMod成员就标识了本钩子所属模块基址在此地址表中的位置。(每个地址占4个字节)所以通常使用ppi+0xa8+iMod*4定位模块基址的地址。注意ppi指向的内存是内核内存。
    2、实现细节
    首先编写程序枚举消息钩子句柄,需要得到GUI TABLE,它的地址实际上存储于User32.dll的一个全局变量中,该模块导出的函数UserRegisterWowHandlers将返回该全局变量的值。所以我们只要调用这个函数就能够得到GUI TABLE。然而UserRegisterWowHandlers是一个未公开的函数,不确定它的函数原型,需要反汇编猜出它的原型。笔者反汇编后得到的原型如下。
typedef PSHAREDINFO (__stdcall *USERREGISTERWOWHANDLERS) (PBYTE ,PBYTE );
仅知道它两个参数是两个指针,但是不知道它的两个参数的含义,所以我们无法构造出合理的参数。如果随便构造参数传进去又会导致user32.dll模块发生错误。所以通过调用这个函数接收其返回值的方法就不能用了。再次反汇编该函数的实现可以看出,在不同操作系统下该函数的最后三行代码如下。
2K系统:(5.0.2195.7032)
:77E3565D B880D2E477 mov eax, 77E4D280
:77E35662 C20800 ret 0008
XP系统:(5.1.2600.2180)
:77D535F5 B88000D777 mov eax, 77D70080
:77D535FA 5D pop ebp
:77D535FB C20800 ret 0008
2003系统:(5.2.3790.1830)
:77E514D9 B8C024E777 mov eax, 77E724C0
:77E514DE C9 leave
:77E514DF C2080000 ret 0008
可以看到共同点,该函数的倒数第三行代码就是将保存GUI TABLE指针的全局变量值赋值给寄存器EAX,只要我们想办法搜索到这个值即可。能够看出无论是哪个版本的函数实现中,都有 C20800代码,含义是ret 0008。我们可以自UserRegisterWowHandlers函数的入口地址开始一直搜索到C20800,找到它以后再向前搜索B8指令,搜到以后B8指令后面的四个字节数据就是我们需要的数据。代码如下。
//获得UserRegisterWowHandlers函数的入口地址
DWORD UserRegisterWowHandlers = (DWORD) GetProcAddress(LoadLibrary("user32.dll"), "UserRegisterWowHandlers");
PSHAREDINFO pGUITable;  //保存GUITable地址的指针
for(DWORD i=UserRegisterWowHandlers; i<UserRegisterWowHandlers+1000; i++)
{
  if((*(USHORT*)i==0x08c2)&&*(BYTE *)(i+2)== 0x00)
  {     //已找到ret 0008指令,然后往回搜索B8
    for (int j=i; j>UserRegisterWowHandlers; j--)
    {   //找到B8它后面四个字节保存的数值即为GUITable地址
      if (*(BYTE *)j == 0xB8)         
      {
        pGUITable = (PSHAREDINFO)*(DWORD *)(j+1);
        break;
      }
    }break;
  }
}
    得到SHAREDINFO结构指针后,它的成员pServerInfo的成员cHandleEntries就是句柄的总个数,然后循环遍历每一个句柄,找到属于指定模块的消息钩子句柄。代码如下。
int iHandleCount = pGUITable->pServerInfo->cHandleEntries;
HOOK_INFO HookInfo;
DWORD dwModuleBase;
struct TINFO
{
  DWORD dwProcessID;
  DWORD dwThreadID;
};
char cModuleName[256] = {0};
for (i=0; i<iHandleCount; i++)
{              //判断句柄类型是否为消息钩子句柄
  if (pGUITable->pHandleEntry[i].bType == TYPE_HOOK)
  {
    DWORD dwValue = (DWORD)pGUITable->pHandleEntry[i].pObject;
    //获得消息钩子内核对象数据
    GetKernelMemory(pGUITable->pHandleEntry[i].pObject, (BYTE *)&HookInfo, sizeof(HookInfo));
    W32THREAD w32thd;
    if( GetKernelMemory(HookInfo.pWin32Thread,(BYTE *)&w32thd , sizeof(w32thd)) )
    {  //获取钩子函数所在模块的基址
      if (!GetKernelMemory((PVOID)((ULONG)w32thd.ppi+0xA8+4*HookInfo.iMod),
        (BYTE *)&dwModuleBase,   sizeof(dwModuleBase)))
      {
        continue;
      }
      TINFO tInfo;
      //获取钩子所属进程ID和线程ID
      if (!GetKernelMemory((PVOID)((ULONG)w32thd.pEThread+0x1ec),
        (BYTE *)&tInfo,   sizeof(tInfo)))
      {
        continue;
      }
      HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, tInfo.dwProcessID);
      if (hProcess == INVALID_HANDLE_VALUE)
      {
        continue;
      }
      //根据模块基址,获取钩子函数所属模块的名称
      if (GetModuleFileNameEx(hProcess, (HMODULE)dwModuleBase, cModuleName, 256))
      {
        OutputDebugString(cModuleName);
        OutputDebugString("\r\n");
      }   
    }
  }
}

    利用上面的代码就可以找到所属病毒DLL的消息钩子句柄,然后调用UnhookWindowsHookEx函数卸载这个消息钩子就OK了。
           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

这里写图片描述

http://chatgpt.dhexx.cn/article/eLX4svCM.shtml

相关文章

Windows消息钩子[键盘监控]

之前看书&#xff0c;看到一眼消息钩子&#xff0c;一直没实践&#xff0c;现在有空弄了下&#xff0c; 主要原理是利用windows自带SetWindowsHookEx API函数 HHOOK SetWindowsHookEx( int idHook, //hook形式 HOOKPROC lpfn //hook过程 HI…

一、C++反作弊对抗实战 (基础篇 —— 4.利用消息钩子注入DLL)

这里将主要用到Windows API函数SetWindowsHookEx,它是微软提供给程序开发人员进行消息拦截的一个API,不过它的功能不仅用作消息拦截,还可以进行DLL注入。 一、关于SetWindowsHookEx 提这个函数在微软官网MSDN文档中原型声明如下: SetWindowsHookExA function (winuser.h…

windows10 记事本进程 键盘消息钩子 dll注入

看了很多文档&#xff0c;垮了很多坎&#xff0c;终于完成了这个demo&#xff1b; 有很多个人理解&#xff0c;可能不完全正确&#xff0c;见谅&#xff1b; 先上实现的图片&#xff1a; 如图&#xff0c;我通过SetWindowsHookEx()函数向记事本进程中当前窗口线程注入了自己写…

消息钩子学习工程

前奏 近来一直在自学Windows Hook相关的知识&#xff0c;已经尝试多种注入方式。尤其对消息钩子方式很感兴趣&#xff0c;因为看到Spy能够截获系统中绝大多数应用的消息流&#xff0c;就很想知道它的工作原理&#xff0c;打算制作属于自己的Spy。 消息钩子简介&#xff1a; 消息…

DLL注入技术之消息钩子注入(HOOK简单的实现)

低头不是认输&#xff0c;是要看清自己的路。仰头不是骄傲&#xff0c;是看见自己的天空。——致自己 Hook&#xff0c;是Windows消息处理机制的一个平台&#xff0c;应用程序可以在上面设置子程序以监视指定窗口的某种消息&#xff0c;而且所监视的窗口可以是其他进程所创建的…

powerdesigner制作数据字典

powerdesigner制作数据字典TOC 配置好数据库连接之后&#xff0c;点击File→New Model 这步是默认的点OK就行。 数据传输完之后点击保存。 选择好保存路径后&#xff0c;关闭软件&#xff08;关闭时注意选择保存&#xff09;&#xff0c;然后再重现打开。 重新打开Po…

设计 - 数据字典

文档分类 写文档目的 你有没有遇到过开晨会、周会的时候某个问题已经讨论的很清晰。 但是几天后或者临近周末的时候再说这个问题的时候&#xff0c;团队中有的童鞋会说:“我不知道&#xff0c;没有说过这个问题或者这个方案”&#xff0c;因此而造成的BB事很伤神费心。 为了避免…

Oracle 数据字典详解

Oracle 数据字典详解 什么叫数据字典&#xff1f; 数据字典指的是描述数据的数据。 举个例子&#xff1a;我们在数据库里面创建了一个表&#xff0c;这个表位于哪个数据文件、这个表有哪些列、这个表的每一个列的数据类型、这个表的约束等等。这些信息都是描述这个表的&#…

数据字典及其使用(方案一)

1 数据字典 1.1 什么是数据字典 将如下这些具有相同类型的配置项&#xff0c;配置到系统的数据字典表中&#xff0c;方便系统维护&#xff0c;由超级管理员统一在后台进行数据字典维护&#xff0c;如果用户需求要增加变更配置项&#xff0c;只需要修改数据字典表记录…

什么是mysql数据字典_数据字典是什么?

展开全部 数据e69da5e6ba9062616964757a686964616f31333366306434字典是指对数据的数据项、数据结构、数据流、数据存储、处理逻辑等进行定义和描述,其目的是对数据流程图中的各个元素做出详细的说明,使用数据字典为简单的建模项目。简而言之,数据字典是描述数据的信息集合,…

matlab新建数据字典及如何导入

一、如何创建 点击model explorer 依次点击file->new->Data dictionary 输入数据字典的名字 创建之后为一下界面&#xff1a; 点击 进行创建 右键save change 进行保存 没保存时候带星号 &#xff0c;保存之后信号消失。 二、如何导入 点击file—>model proper…

数据治理浅谈之数据字典

导读 数据字典作为数据治理整体体系中重要的一环&#xff0c;理解和使用数据字典尤为重要。本文将从数据字典概念、定义、结构、应用、治理等方面进行详细阐述。 01概念 数据字典&#xff08;Data Dictionary&#xff09;是一个容器&#xff0c;用于包含有组织定义和使用的所…

【数据库管理】⑩数据字典

1. 数据字典的概述 数据字典&#xff08;Data Dictionary&#xff09;是数据库管理系统中的一个重要组成部分&#xff0c;它是一个存储数据库元数据的集合&#xff0c;包含了数据库中所有对象的定义和描述信息。数据字典可以帮助用户了解数据库中的各种对象和数据结构&#xff…

List中remove()的用法

List remove的三种正确方法 1、倒序循环&#xff0c;因为list删除只会导致当前元素之后的元素位置发生改变&#xff0c;所以采用倒序可以保证前面的元素没有变化&#xff1b; for(int ilist.size()-1;i>0;i--){ list.remove(i); }2、顺序循环时&#xff0c;删除当前位置的…

JSP内置对象Session——setAttribute/getAttibute/removeAttribute

本文章代码使用了request内置对象&#xff0c;可先查看之前的文章学习。 前言 一、Session使用环境 二、常用方法 三.使用示例 1. 设计某系统登陆模块&#xff0c;该模块需满足以下条件&#xff1a; 2.login.jsp登录页面 2.check.jsp校验页面 3. success.jsp成功登录页面…

Iterator remove()详解

转自&#xff1a;http://blog.51cto.com/tianxingzhe/1693218 一、Iterator的API 关于Iterator主要有三个方法&#xff1a;hasNext()、next()、remove() hasNext:没有指针下移操作&#xff0c;只是判断是否存在下一个元素 next&#xff1a;指针下移&#xff…

VLAN命令行配置

VLNA配置任务概览 各配置任务间的逻辑关系&#xff1a; VLAN配置任务概览&#xff1a; 配置任务描述划分VLAN创建并划分VLAN&#xff0c;将没有二层互通需求的用户进行隔离&#xff0c;可增强网络的安全性、减少广播流量&#xff0c;同时也减少了广播风暴的产生。配置通过VLA…

VLAN配置命令

VLAN作用 分割广播域 VLAN分类 静态VLAN 基于端口划分 动态VLAN 基于MAC地址划分 VLAN范围 0-4095 共4096个vlanID 0与4095系统保留 1-4094可用 VLAN配置命令 VLAN基础配置代码 添加vlan&#xff08;2-4094&#xff09; vlan 10 删除vlan undo vlan 10 批量增加 vlan batch …

华为 -VLAN配置

按照实验拓扑图接线。 配置PC1&#xff1a;IP&#xff08;192.168.1.1/24&#xff09;,PC2:IP(192.168.2.1/24)。测试PC1和PC2的连通性&#xff1a;用PC1去PingPC2&#xff0c;查看Ping的结果&#xff0c;并解释原因。 PC1和PC2在不同的网段&#xff0c;不能ping通 配置PC1&…

华为交换机vlan配置

拓扑图&#xff1a; 配置命令&#xff1a; sw1: sys sys sw1 vlan 10 vlan 20 int e0/0/1 port link-type access port default vlan 10 int e0/0/2 port link-type access port default vlan 20 int e0/0/3 port link-type trunk port trunk allow vlan 10 20sw2: sys sy…