深入注册表监控

这篇具有很好参考价值的文章主要介绍了深入注册表监控。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

注册表是windows的重要数据库,存放了很多重要的信息以及一些应用的设置,对注册表进行监控并防止篡改是十分有必要的。在64位系统下微软提供了CmRegisterCallback这个回调函数来实时监控注册表的操作,那么既然这里微软提供了这么一个方便的接口,病毒木马自然也会利用,这里我们就来探究其实现的原理和如何利用这个回调函数进行对抗

CmRegisterCallback

要想更好的进行对抗,就是深入底层去看这个函数到底做了什么事情,无论是监控还是反监控,这样我们才能够更好的进行利用

首先我们去msdn里面看一下它的结构

NTSTATUS CmRegisterCallback(
  [in]           PEX_CALLBACK_FUNCTION Function,
  [in, optional] PVOID                 Context,
  [out]          PLARGE_INTEGER        Cookie
);

深入注册表监控

image-20220501100957527.png

第一个参数指向RegistryCallback,它这里其实是通过EX_CALLBACK_FUNCTION这个回调函数实现,结构如下

EX_CALLBACK_FUNCTION ExCallbackFunction;

NTSTATUS ExCallbackFunction(
  [in]           PVOID CallbackContext,
  [in, optional] PVOID Argument1,
  [in, optional] PVOID Argument2
)
{...}

深入注册表监控

image-20220501101203532.png

主要是看第三个参数,REG_NOTIFY_CLASS结构如下

typedef enum _REG_NOTIFY_CLASS {
    RegNtDeleteKey,
    RegNtPreDeleteKey = RegNtDeleteKey,
    RegNtSetValueKey,
    RegNtPreSetValueKey = RegNtSetValueKey,
    RegNtDeleteValueKey,
    RegNtPreDeleteValueKey = RegNtDeleteValueKey,
    RegNtSetInformationKey,
    RegNtPreSetInformationKey = RegNtSetInformationKey,
    RegNtRenameKey,
    RegNtPreRenameKey = RegNtRenameKey,
    RegNtEnumerateKey,
    RegNtPreEnumerateKey = RegNtEnumerateKey,
    RegNtEnumerateValueKey,
    RegNtPreEnumerateValueKey = RegNtEnumerateValueKey,
    RegNtQueryKey,
    RegNtPreQueryKey = RegNtQueryKey,
    RegNtQueryValueKey,
    RegNtPreQueryValueKey = RegNtQueryValueKey,
    RegNtQueryMultipleValueKey,
    RegNtPreQueryMultipleValueKey = RegNtQueryMultipleValueKey,
    RegNtPreCreateKey,
    RegNtPostCreateKey,
    RegNtPreOpenKey,
    RegNtPostOpenKey,
    RegNtKeyHandleClose,
    RegNtPreKeyHandleClose = RegNtKeyHandleClose,
    //
    // .Net only
    //    
    RegNtPostDeleteKey,
    RegNtPostSetValueKey,
    RegNtPostDeleteValueKey,
    RegNtPostSetInformationKey,
    RegNtPostRenameKey,
    RegNtPostEnumerateKey,
    RegNtPostEnumerateValueKey,
    RegNtPostQueryKey,
    RegNtPostQueryValueKey,
    RegNtPostQueryMultipleValueKey,
    RegNtPostKeyHandleClose,
    RegNtPreCreateKeyEx,
    RegNtPostCreateKeyEx,
    RegNtPreOpenKeyEx,
    RegNtPostOpenKeyEx,
    //

    //
    RegNtPreFlushKey,
    RegNtPostFlushKey,
    RegNtPreLoadKey,
    RegNtPostLoadKey,
    RegNtPreUnLoadKey,
    RegNtPostUnLoadKey,
    RegNtPreQueryKeySecurity,
    RegNtPostQueryKeySecurity,
    RegNtPreSetKeySecurity,
    RegNtPostSetKeySecurity,
    //
    // per-object context cleanup
    //
    RegNtCallbackObjectContextCleanup,
    //
    // new in Vista SP2 
    //
    RegNtPreRestoreKey,
    RegNtPostRestoreKey,
    RegNtPreSaveKey,
    RegNtPostSaveKey,
    RegNtPreReplaceKey,
    RegNtPostReplaceKey,

    MaxRegNtNotifyClass //should always be the last enum
} REG_NOTIFY_CLASS;

这里有几个比较常见的类型

  • • RegNtPreCreateKey 创建注册表 对应的Argument2PREG_CREATE_KEY_INFORMATION

  • • RegNtPreOpenKey 打开注册表 对应的Argument2PREG_CREATE_KEY_INFORMATION

  • • RegNtPreDeleteKey 删除键 对应的Argument2PREG_DELETE_KEY_INFORMATION

  • • RegNtPreDeleteValueKey 删除键值 对应的Argument2PREG_DELETE_VALUE_KEY_INFORMATION

  • • RegNtPreSetValueKey 修改键值 对应的Argument2PREG_SET_VALUE_KEY_INFORMATION

这里RegNtPreCreateKeyRegNtPreOpenKeyArgument2都是使用到PREG_CREATE_KEY_INFORMATION这个结构体,我们看下结构

其中两个关键的参数就是CompleteName表示指向路径的指针,以及RootObject表示指向注册表项的指针

typedef struct _REG_CREATE_KEY_INFORMATION {
    PUNICODE_STRING     CompleteName; // IN
    PVOID               RootObject;   // IN
    PVOID               ObjectType;   
    ULONG               CreateOptions;
    PUNICODE_STRING     Class;        
    PVOID               SecurityDescriptor;
    PVOID               SecurityQualityOfService;
    ACCESS_MASK         DesiredAccess;
    ACCESS_MASK         GrantedAccess;
                                        // to be filled in by callbacks 
                                      // when bypassing native code
    PULONG              Disposition;  
                                      // on pass through, callback should fill 
                                      // in disposition
    PVOID               *ResultObject;
                                      // on pass through, callback should return 
                                      // object to be used for the return handle
    PVOID               CallContext;  
    PVOID               RootObjectContext;  
    PVOID               Transaction;  
    PVOID               Reserved;     
} REG_CREATE_KEY_INFORMATION, REG_OPEN_KEY_INFORMATION,*PREG_CREATE_KEY_INFORMATION, *PREG_OPEN_KEY_INFORMATION;

然后就是RegNtPreDeleteKey对应PREG_DELETE_KEY_INFORMATION结构体,这里的话因为只需要删除注册表,使用到Object参数指向要删除注册表的指针

typedef struct _REG_DELETE_KEY_INFORMATION {
    PVOID    Object;                      // IN
    PVOID    CallContext;  
    PVOID    ObjectContext;
    PVOID    Reserved;     
} REG_DELETE_KEY_INFORMATION, *PREG_DELETE_KEY_INFORMATION

深入注册表监控

image-20220501102243154.png

再就是RegNtPreDeleteValueKey对应PREG_DELETE_VALUE_KEY_INFORMATION结构体,我们通过名字可以判断这里我们要删除表项里面的值,所以这里Object还是指向要删除的注册表的指针,而ValueName就是指向具体需要删除的值

typedef struct _REG_DELETE_VALUE_KEY_INFORMATION {
  PVOID           Object;
  PUNICODE_STRING ValueName;
  PVOID           CallContext;
  PVOID           ObjectContext;
  PVOID           Reserved;
} REG_DELETE_VALUE_KEY_INFORMATION, *PREG_DELETE_VALUE_KEY_INFORMATION;

深入注册表监控

image-20220501102233472.png

RegNtPreSetValueKey对应的是PREG_SET_VALUE_KEY_INFORMATION结构,同样是Object指向要修改的注册表的指针,而ValueName就是指向具体需要修改的值

typedef struct _REG_CREATE_KEY_INFORMATION {
    PUNICODE_STRING     CompleteName; // IN
    PVOID               RootObject;   // IN
    PVOID               ObjectType;   
    ULONG               CreateOptions;
    PUNICODE_STRING     Class;        
    PVOID               SecurityDescriptor;
    PVOID               SecurityQualityOfService;
    ACCESS_MASK         DesiredAccess;
    ACCESS_MASK         GrantedAccess;
                                        // to be filled in by callbacks 
                                      // when bypassing native code
    PULONG              Disposition;  
                                      // on pass through, callback should fill 
                                      // in disposition
    PVOID               *ResultObject;
                                      // on pass through, callback should return 
                                      // object to be used for the return handle
    PVOID               CallContext;  
    PVOID               RootObjectContext;  
    PVOID               Transaction;  
    PVOID               Reserved;     
} REG_CREATE_KEY_INFORMATION, REG_OPEN_KEY_INFORMATION,*PREG_CREATE_KEY_INFORMATION, *PREG_OPEN_KEY_INFORMATION;

我们这里了解了CmRegisterCallback的一些常用结构,这里我们去IDA里面看一下其底层实现

这个函数首先调用了CmpRegisterCallbackInternal

深入注册表监控

image-20220501103504282.png

我们跟进去看看,首先是通过ExAllocatePoolWithTag来申请0x30大小的空间,然后通过test esi,esiBlinkFlink都指向自己,然后进行判断后跳转到69436B这个地址

深入注册表监控

image-20220501103825124.png

这段函数主要是将双向链表存入申请空间的前8字节,然后将Context结构保存到0x18的位置,将回调函数保存到0x1C的位置,然后进行判断后跳转到6943B1这个地址

深入注册表监控

image-20220501104354270.png

这段函数的主要作用就是将Cookie的值存入0x10偏移处,这里的设计很巧妙,因为一个cookie是占8字节的,这里首先将[esi + 0x10]的值存入eax,然后将ebx地址存入eax,相当于赋值前4位,然后再进行同样的操作,这里取的是[esi + 0x14],也就是赋值后四位

深入注册表监控

image-20220501104945941.png

然后再就是后面的代码,这里就是一些释放内存的操作

深入注册表监控

image-20220501105207771.png

我们从上面的分析可以得出CmRegisterCallback其实就是申请了一块空间,保存了一个双向链表、CookieContext、回调函数的地址,那么如果要存放注册表,只可能是放在了双向链表里面,这里我们就有理由猜测注册表监控的回调函数就是通过一个双向链表连接起来的

监控

我们在上面已经分析了CmRegisterCallback函数的原理,那么我们首先注册一个回调函数

NTSTATUS status = CmRegisterCallback(RegisterCallback, NULL, &RegCookie);
if (!NT_SUCCESS(status))
{
    ShowError("CmRegisterCallback", status);
    RegCookie.QuadPart = 0;
    return status;
}

然后我们编写RegisterCallback这个回调函数,首先传入3个参数

NTSTATUS RegisterMonCallback(_In_ PVOID CallbackContext,_In_opt_ PVOID Argument1,_In_opt_ PVOID Argument2)

我们在前面已经说过CmRegisterCallback的第一个参数是操作类型,我们首先获取一下

LONG lOperateType = (REG_NOTIFY_CLASS)Argument1;

通过ExAllocatePool申请一块内存,使用ustrRegPath定义注册表的路径,这里设置非分页内存即可

ustrRegPath.Buffer = ExAllocatePool(NonPagedPool, ustrRegPath.MaxLength);

然后我们通过switch...case循环来判断lOperateType来进行具体的操作,比如这里是RegNtPreCreateKey

首先需要获取注册表的路径,这里就需要用到ObQueryNameString这个API

NTSTATUS
  ObQueryNameString(
    IN PVOID  Object,
    OUT POBJECT_NAME_INFORMATION  ObjectNameInfo,
    IN ULONG  Length,
    OUT PULONG  ReturnLength
    );

第二个参数指向OBJECT_NAME_INFORMATION结构,保存的是返回的名称

typedef struct _OBJECT_NAME_INFORMATION {
    UNICODE_STRING Name;
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;

那么这里我们调用ObQueryNameString获取函数地址,写成一个函数方便调用

PVOID lpObjectNameInfo = ExAllocatePool(NonPagedPool, ulSize);
NTSTATUS status = ObQueryNameString(pRegistryObject, (POBJECT_NAME_INFORMATION)lpObjectNameInfo, ulSize, &ulRetLen);
RtlCopyUnicodeString(pRegistryPath, (PUNICODE_STRING)lpObjectNameInfo);

获取一下注册表的路径

GetRegisterPath(&ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject);

这里我们定位到注册表之后,就需要进行判断是否是我们想要保护的注册表,如果是的话就将status改成STATUS_ACCESS_DENIED即可达到保护注册表的效果

通过wcsstr()函数判断路径是否为我们想要保护的注册表名称,编写为Compare函数

BOOLEAN Compare(UNICODE_STRING ustrRegPath)
{
    if (NULL != wcsstr(ustrRegPath.Buffer, L"RegTest"))
    {
        return TRUE;
    }

    return FALSE;
}

如果名称相同则设置为STATUS_ACCESS_DENIED

if (Compare(ustrRegPath))
{
    status = STATUS_ACCESS_DENIED;
}

完整代码如下,这里因为是RegNtPreOpenKey所以Argument2对应的类型就是PREG_CREATE_KEY_INFORMATION,这里只需要修改Argunment2的类型即可,剩下的几个判断再这里就不赘述了

    case RegNtPreOpenKey:
    {
        GetRegisterPath(&ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject);
        // 判断是否是被保护的注册表
        if (Compare(ustrRegPath))
        {
            status = STATUS_ACCESS_DENIED;
        }
        // 显示
        DbgPrint("[RegNtPreOpenKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName);
        break;
    }

深入注册表监控

image-20220501142500180.png

回调函数的完整代码如下

NTSTATUS RegisterCallback(_In_ PVOID CallbackContext,_In_opt_ PVOID Argument1,_In_opt_ PVOID Argument2)
{

    NTSTATUS status = STATUS_SUCCESS;
    UNICODE_STRING ustrRegPath;

    LONG lOperateType = (REG_NOTIFY_CLASS)Argument1;

    ustrRegPath.Length = 0;
    ustrRegPath.MaximumLength = 1024 * sizeof(WCHAR);
    ustrRegPath.Buffer = ExAllocatePool(NonPagedPool, ustrRegPath.MaximumLength);
    if (NULL == ustrRegPath.Buffer)
    {
        printf("ExAllocatePool error : %d\n", GetLastError());
        return status;
    }
    switch (lOperateType)
    {
        // 创建注册表之前
    case RegNtPreCreateKey:
    {
        GetRegisterPath(&ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject);

        if (Compare(ustrRegPath))
        {
            status = STATUS_ACCESS_DENIED;
        }
        DbgPrint("[RegNtPreCreateKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName);
        break;
    }

    // 打开注册表之前
    case RegNtPreOpenKey:
    {
        GetRegisterPath(&ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject);

        if (Compare(ustrRegPath))
        {
            status = STATUS_ACCESS_DENIED;
        }
        DbgPrint("[RegNtPreOpenKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName);
        break;
    }
    // 删除键之前
    case RegNtPreDeleteKey:
    {
        GetRegisterPath(&ustrRegPath, ((PREG_DELETE_KEY_INFORMATION)Argument2)->Object);

        if (Compare(ustrRegPath))
        {
            status = STATUS_ACCESS_DENIED;
        }

        DbgPrint("[RegNtPreDeleteKey][%wZ]\n", &ustrRegPath);
        break;
    }
    // 删除键值之前
    case RegNtPreDeleteValueKey:
    {
        GetRegisterPath(&ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->Object);

        if (Compare(ustrRegPath))
        {
            status = STATUS_ACCESS_DENIED;
        }
        DbgPrint("[RegNtPreDeleteValueKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->ValueName);
        break;
    }
    // 修改键值之前
    case RegNtPreSetValueKey:
    {
        GetRegisterPath(&ustrRegPath, ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Object);

        if (Compare(ustrRegPath))
        {
            status = STATUS_ACCESS_DENIED;
        }
        // 显示
        DbgPrint("[RegNtPreSetValueKey][%wZ][%wZ]\n", &ustrRegPath, ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->ValueName);
        break;
    }
    default:
        break;
    }

    if (NULL != ustrRegPath.Buffer)
    {
        ExFreePool(ustrRegPath.Buffer);
        ustrRegPath.Buffer = NULL;
    }

    PEPROCESS pEProcess = PsGetCurrentProcess();
    if (NULL != pEProcess)
    {
        UCHAR* lpszProcessName = PsGetProcessImageFileName(pEProcess);
        if (NULL != lpszProcessName)
        {
            DbgPrint("Current Process[%s]\n", lpszProcessName);
        }
    }

    return status;
}

实现效果

我们加载驱动可以看到我们想要修改RegTest的二进制的值被拒绝

深入注册表监控

image-20220501142641363.png

删除也会被拦截

深入注册表监控

image-20220501142654260.png

重命名也同样被拦截

深入注册表监控

image-20220501142706899.png

卸载驱动之后能够成功重命名

深入注册表监控

image-20220501142719387.png

也能够修改二进制的内容

深入注册表监控

image-20220501142758141.png

也可以删除字符串

深入注册表监控

image-20220501142809044.png

反监控

我们在前面已经逆向分析了CmRegisterCallback的底层实现,就是通过申请一块内存空间存放双向链表,那么我们只要定位到这个双向链表删除回调函数即可得到反监控的效果

将链表加入我们内存的函数是SetRegisterCallback,这里我因为pdb文件的问题显示得有点问题,我们跟进去看看

深入注册表监控

image-20220501144151656.png

这里往下走可以看到offset CallbackListHead的操作,这里就是将链表头赋值给ebx

深入注册表监控

image-20220501144321163.png

然后将eax的值赋给[esi + 10],这里就是在进行初始化Cookie的操作

深入注册表监控

image-20220501144430141.png

然后最后再比较edi的值是否为ListBegin的地址,跳转到增加链表的代码

image-20220501144540775.png

那么这里我们明确下思路,我们想要定位到链表头,就首先需要通过在CmRegisterCallback里面定位SetRegisterCallback

深入注册表监控

image-20220501144914828.png

然后定位到链表头即可

深入注册表监控

image-20220501145028344.png

那么这里我们进行代码编写,首先定位到CmRegisterCallback函数

    UNICODE_STRING uStrFuncName = RTL_CONSTANT_STRING(L"CmRegisterCallback");

    pCmRegFunc = (PUCHAR)MmGetSystemRoutineAddress(&uStrFuncName);

然后通过硬编码定位到链表头

    pCmRegFunc = (PUCHAR)MmGetSystemRoutineAddress(&uStrFuncName);

    if (pCmRegFunc == NULL)
    {
        DbgPrint("MmGetSystemRoutineAddress error : %d\r\n",GetLastError());
        return pListEntry;
    }

    while (*pCmRegFunc != 0xC2)
    {
        if (*pCmRegFunc == 0xE8)
        {
            pCmcRegFunc = (PUCHAR)((ULONG)pCmRegFunc + 5 + *(PULONG)(pCmRegFunc + 1));
            break;
        }
        pCmRegFunc++;
    }

    if (pCmcRegFunc == NULL)
    {
        DbgPrint("GetCmcRegFunc error : %d\r\n",GetLastError());
        return pListEntry;
    }

    while (*pCmcRegFunc != 0xC2)
    {
        if (*pCmcRegFunc == 0x8B && *(pCmcRegFunc + 1) == 0xC6 && *(pCmcRegFunc + 2) == 0xE8)
        {
            pSetRegFunc = (PUCHAR)((ULONG)pCmcRegFunc + 2 + 5 + *(PULONG)(pCmcRegFunc + 3));
            break;
        }
        pCmcRegFunc++;
    }

    if (pSetRegFunc == NULL)
    {
        DbgPrint("GetSetRegFunc error : %d\r\n", GetLastError());
        return pListEntry;
    }

    while (*pSetRegFunc != 0xC2)
    {
        if (*pSetRegFunc == 0xBB)
        {
            pListEntry = (PULONG) * (PULONG)(pSetRegFunc + 1);
            break;
        }
        pSetRegFunc++;
    }

定位到链表头之后,我们通过MmIsAddressValid对地址进行判断是否可用,通过0x10偏移定位到Cookie

pHead = GetRegisterList();
pListEntry = (PLIST_ENTRY)*pHead;

pLiRegCookie = (PLARGE_INTEGER)((ULONG)pListEntry + 0x10);
pFuncAddr = (PULONG)((ULONG)pListEntry + 0x1C);

然后调用CmUnRegisterCallback删除回调

status = CmUnRegisterCallback(*pLiRegCookie);

实现效果

这里如果连windbg有输出效果会更明显,但是我这台win7没有配双机调试,这里就只能看一下输出的效果了

首先直接加载一个exe,可以看到修改注册表的值成功

深入注册表监控

image-20220501155155905.png

加载驱动发现创建注册表值失败

深入注册表监控

image-20220501155057647.png

然后再加载我们的绕过回调函数的驱动,又可以加载成功

深入注册表监控文章来源地址https://www.toymoban.com/news/detail-481547.html

到了这里,关于深入注册表监控的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Windows注册表清理

    伴随着系统运行时间不断增长,我们的电脑 注册表 中累积了许多垃圾文件。这些垃圾文件都是我们平常安装与卸载程序所留下的无用注册表信息,时间一长,垃圾文件与信息越来越多,我们电脑的运行速度越来越慢。 ​ 而且部分软件由于删除方式不对,导致一些残留注册表

    2024年02月08日
    浏览(47)
  • Windows技巧之注册表

    介绍 注册表是Windows操作系统中的一个核心数据库,其中存放着各种参数,直接控制着Windows的启动、硬件驱动程序的装载以及一些Windows应用程序的运行,从而在整个系统中起着核心作用。这些作用包括了软、硬件的相关配置和状态信息,比如注册表中保存有应用程序和资源管

    2023年04月08日
    浏览(43)
  • 【Windows注册表内容详解】

    一、什么是注册表 注册表是windows操作系统、硬件设备以及客户应用程序得以正常运行和保存设置的核心“数据库”,也可以说是一个非常巨大的树状分层结构的数据库系统。 注册表记录了用户安装在计算机上的软件和每个程序的相互关联信息,它包括了计算机的硬件配置,

    2024年02月09日
    浏览(40)
  • windows注册表启动项

    实际应急响应案例时,发现很多非常规的启动项以及ARK工具未涵盖的启动项,故收集资料对注册表有关的启动项进行总结,以后处置病毒无从下手时可以考虑从启动项排查。 1.Load注册键 介绍该注册键的资料不多,实际上它也能够自动启动程序。位置: HKEY_CURRENT_USERSoftwareMi

    2024年02月05日
    浏览(46)
  • Windows注册表的读写操作

    本文介绍了Windows注册表的基本知识,以及C++中打开关闭查询修改注册表的常用接口。 注册表的基本知识(本文第1节)参考 https://blog.csdn.net/weixin_45300266/article/details/122359920并作修改。 注册表是windows系统中具有层次结构的核心数据库,储存的数据对windows 和Windows上运行的应用

    2024年02月04日
    浏览(50)
  • 【运维】Windows 通过注册表禁用服务

    【运维】Windows 通过注册表禁用服务 以这个服务为例子 Windows Push Notifications User Service 双击查看服务名称 WpnUserService_671f3   打开注册表 HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServices{服务名称} HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesWpnUserService_671f3 选中  将右侧的 Start 改为4  十六

    2024年02月12日
    浏览(35)
  • Windows 10注册表损坏该如何修复?

    注册表是Windows系统中的一个重要的数据库,用于存储系统和应用程序的设置信息。其中包含了安装在所有版本的Microsoft Windows操作系统上的硬件和程序的选项、设置、信息和其他值。一旦损坏或丢失,就将可能影响电脑的正常运行,Windows 10注册表损坏该如何修复? 1、借助启

    2024年02月08日
    浏览(49)
  • 从Windows注册表中查看系统版本

    Windows 注册表项 键值 CurrentVersion 所显示的数据内容所对应的系统版本: Current Number 操作系统: 5 Windows 2000 5.1 Windows XP 5.2 Windows XP 64bit 5.2 Windows Server 2003 / R2 6 Windows Vista / Windows Server 2008 6.1 Windows 7 / Windows Server 2008 R2 6.2 Windows 8 / Windows Server 2012 6.3 Windows 8.1 / 10 / 11 / Windows Server

    2024年02月04日
    浏览(54)
  • Windows注册表脚本文件(简介、添加、修改、删除)

    我们平时大多采用Regedit注册表编辑器来实现对注册表的修改操作,但是这种手工操作费时费力,当你要对多台PC统一修改注册表时,就要怨念了。并且在某些情况下,如果注册表编辑器被禁用了,那么这种方法也会失效。于是,推荐大家使用REG文件来快速完成注册表的修改。

    2024年02月04日
    浏览(49)
  • Python读取Windows注册表的实战代码

      大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作

    2024年02月10日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包