A simple demo for WDM Driver development

简介: Introduction A lot of articles have been investigating in application layer issues, like skin-based dialogs, MFC, ATL, thread, process, registry etc.

Introduction

A lot of articles have been investigating in application layer issues, like skin-based dialogs, MFC, ATL, thread, process, registry etc. It won't be easy to find any driver related articles posted with full source code. The root cause is that most drivers are developed for specific hardware. Without the domain knowledge, you will never want to get in touch with it. I believe a lot of software engineers are afraid when they involve in kernel mode programming for the very first time, and there are not too much resources that can guide them through the whole process from DDK study to program stage. Hence I decided to share some of my experiences in driver programming in Windows. This demo focuses on a quick introduction to WDM Driver's architecture, and will introduce two I/O modes coming with Windows, which are Direct I/O and Buffered I/O, how to communicate with drivers residing in system kernel space, and read/write data to it.

There is no need for you to read the demo program with any hardware related background, the demo drivers are all pseudo drivers. That's drivers installed without a physical device in computer.

The member functions defined in this demo program can be used as templates for later driver development by you.

Background

You might be a well-experienced software engineer and might want to involve in kernel programming.

Create your WDM Driver: a Pseudo Driver tutorial

Before we start, declaration for member routines and structures is required. The most important driver-required data structure is - DEVICE_EXTENSION!

Collapse Copy Code
typedef struct tagDEVICE_EXTENSION {
    PDEVICE_OBJECT DeviceObject;       // device object this driver creates
    PDEVICE_OBJECT NextDeviceObject;   // next-layered device object in this
                                       // device stack
    DEVICE_CAPABILITIES pdc;           // device capability
    IO_REMOVE_LOCK RemoveLock;         // removal control locking structure
    LONG handles;                      // # open handles
    PVOID DataBuffer;                  // Internal Buffer for Read/Write I/O
    UNICODE_STRING Device_Description; // Device Description
    SYSTEM_POWER_STATE SysPwrState;    // Current System Power State
    DEVICE_POWER_STATE DevPwrState;    // Current Device Power State
    PIRP PowerIrp;                     // Current Handling Power-Related IRP
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

Code segment below demonstrates the start of creating a valid WDM Driver.

There are mandatory and optional members in a WDM Driver. A valid WDM Driver should come with the following member routines, the most important task item for DriverEntry is to register all member routines to kernel:

Collapse Copy Code
//
NTSTATUS
DriverEntry( 
IN PDRIVER_OBJECT DriverObject, 
IN PUNICODE_STRING RegistryPath 
)
{
RtlInitUnicodeString(
&Global_sz_Drv_RegInfo,
RegistryPath->Buffer);

// Initialize function pointers

DriverObject->DriverUnload = DriverUnload;
DriverObject->DriverExtension->AddDevice = AddDevice;

DriverObject->MajorFunction[IRP_MJ_CREATE] = PsdoDispatchCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = PsdoDispatchClose;
DriverObject->MajorFunction[IRP_MJ_READ] = PsdoDispatchRead;
DriverObject->MajorFunction[IRP_MJ_WRITE] = PsdoDispatchWrite;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = PsdoDispatchDeviceControl;
DriverObject->MajorFunction[IRP_MJ_POWER] = PsdoDispatchPower;
DriverObject->MajorFunction[IRP_MJ_PNP] = PsdoDispatchPnP;

return STATUS_SUCCESS;
}
//

Normal operation workflow within WDM Driver

Code segment below demonstrates the workflow in AddDevice routine: the most important task for AddDevice routine is to create a Device object, and attach it to the existing device stack.

Collapse Copy Code
NTSTATUS
AddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject 
)
{
    ULONG DeviceExtensionSize;
    PDEVICE_EXTENSION p_DVCEXT;
    PDEVICE_OBJECT ptr_PDO;
    NTSTATUS status;

    RtlInitUnicodeString(
        &Global_sz_DeviceName, L"");
    //Get DEVICE_EXTENSION required memory space
    DeviceExtensionSize = sizeof(DEVICE_EXTENSION);
    //Create Device Object
    status = IoCreateDevice(
        DriverObject,
        DeviceExtensionSize,
        &Global_sz_DeviceName,
        FILE_DEVICE_UNKNOWN,
        FILE_DEVICE_SECURE_OPEN, 
        FALSE,
        &ptr_PDO
        );

    if (NT_SUCCESS(status)) {
        ptr_PDO->Flags &= ~DO_DEVICE_INITIALIZING;
        ptr_PDO->Flags |= DO_BUFFERED_IO;  //For Buffered I/O
        //ptr_PDO->Flags |= DO_DIRECT_IO;  //For Direct I/O
        p_DVCEXT = ptr_PDO->DeviceExtension;
        p_DVCEXT->DeviceObject = ptr_PDO;
        RtlInitUnicodeString(

        /*
        //Other initialization tasks go here
        */

        //Store next-layered device object
        //Attach device object to device stack
        p_DVCEXT->NextDeviceObject = 
            IoAttachDeviceToDeviceStack(ptr_PDO, PhysicalDeviceObject);
    }

    return status;
}

Code segment below shows how to support IRP_MJ_CREATE, it is send when client application tries to connect to the underlying Pseudo Driver. Before proceeding, see graph below in advance to realize the connection process.

Usually, you will use CreateFile/fopen Win32 API to connect to the underlying device. It is the right time that Win32 Subsystem submits IRP_MJ_CREATE and asks driver to connect to the target device!

Collapse Copy Code
NTSTATUS
PsdoDispatchCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
    PIO_STACK_LOCATION p_IO_STK;
    PDEVICE_EXTENSION p_DVCEXT;
    NTSTATUS status;

    p_IO_STK = IoGetCurrentIrpStackLocation(Irp);
    p_DVCEXT = DeviceObject->DeviceExtension;
    status = IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, p_IO_STK->FileObject);
    if (NT_SUCCESS(status)) {
        CompleteRequest(Irp, STATUS_SUCCESS, 0);
        return STATUS_SUCCESS;
    } else {
        IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, p_IO_STK->FileObject);
        CompleteRequest(Irp, status, 0);
        return status;
    }
}

Code segment below shows how to support IRP_MJ_CLOSE, the IRP is sent when client application tries to close connection to the underlying Pseudo Driver. Before proceeding, see graph below in advance to realize the closing process.

Usually, you will use CloseHandle/fclose Win32 API to close connection to the underlying device. It is the right time that Win32 Subsystem submits IRP_MJ_CLOSE and asks driver to close connection to target device!

Collapse Copy Code
NTSTATUS
PsdoDispatchClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
    PIO_STACK_LOCATION p_IO_STK;
    PDEVICE_EXTENSION p_DVCEXT;

    p_IO_STK = IoGetCurrentIrpStackLocation(Irp);
    p_DVCEXT = DeviceObject->DeviceExtension;
    IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, 
    p_IO_STK->FileObject);
    CompleteRequest(Irp, STATUS_SUCCESS, 0);
    return STATUS_SUCCESS;
}

I/O Support : Buffered I/O Mode

There are three I/O modes in Windows kernel, they are Buffer, Direct and Neither modes. Now, we'll talk about Buffered I/O, and this article will not involve Neither mode for data transfer if processing under user-thread occupied memory space, it might be dangerous!! If client application is going to read/write data to and from driver, the memory address of data source will not be directly referenced by the underlying driver. System kernel will allocate another data buffer with equivalent size in kernel. All data transferred must be copied into this area before they are to the target place. Usually, you will call ReadFile/WriteFile or fread/fwrite to make read/write request.

Below code segment demos the workflow in I/O handle for read request. As we can see, the routine that is registered for reading is PsdoDispatchRead in DriverEntry, this member routine will read data out of Driver's internal member - DataBuffer to client application:

Collapse Copy Code
NTSTATUS
PsdoDispatchRead(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
    PVOID Buf; //Buffer provided by user program
    ULONG BufLen; //Buffer length for user provided buffer
    LONGLONG Offset;//Buffer Offset
    PVOID DataBuf; //Buffer provided by Driver
    ULONG DataLen; //Buffer length for Driver Data Buffer
    ULONG ByteTransferred;
    PIO_STACK_LOCATION p_IO_STK;
    PDEVICE_EXTENSION p_DVCEXT;

    DbgPrint("IRP_MJ_READ : Begin/r/n");
    //Get I/o Stack Location & Device Extension
    p_IO_STK = IoGetCurrentIrpStackLocation(Irp);
    p_DVCEXT = DeviceObject->DeviceExtension;

    //Get User Output Buffer & Length 
    BufLen = p_IO_STK->Parameters.Read.Length;
    Offset = p_IO_STK->Parameters.Read.ByteOffset.QuadPart;
    Buf = (PUCHAR)(Irp->AssociatedIrp.SystemBuffer) + Offset;

    //Get Driver Data Buffer & Length
    DataBuf = p_DVCEXT->DataBuffer;
    if (DataBuf == NULL)
        DataLen = 0;
    else
        DataLen = 1024;

    IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, Irp);

    DbgPrint("Output Buffer Length : %d/r/n", BufLen);
    DbgPrint("Driver Data Length : %d/r/n", DataLen);
    //
    if (BufLen <= DataLen) {
        ByteTransferred = BufLen; 
    } else {
        ByteTransferred = DataLen;
    }

    RtlCopyMemory(
        Buf, DataBuf, 
        ByteTransferred);

    IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, Irp);
    CompleteRequest(Irp, STATUS_SUCCESS, ByteTransferred);

    DbgPrint("IRP_MJ_READ : End/r/n");
    return STATUS_SUCCESS;
}

Below code segment demos the possible task items in workflow that can support the normal I/O requests to write data from application to driver.

Collapse Copy Code
NTSTATUS
PsdoDispatchWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
    PVOID Buf; //Buffer provided by user program
    ULONG BufLen; //Buffer length for user provided buffer
    LONGLONG Offset;//Buffer Offset
    PVOID DataBuf; //Buffer provided by Driver
    ULONG DataLen; //Buffer length for Driver Data Buffer
    ULONG ByteTransferred;
    PIO_STACK_LOCATION p_IO_STK;
    PDEVICE_EXTENSION p_DVCEXT;
    NTSTATUS status;

    DbgPrint("IRP_MJ_WRITE : Begin/r/n");

    //Get I/o Stack Location & Device Extension
    p_IO_STK = IoGetCurrentIrpStackLocation(Irp);
    p_DVCEXT = DeviceObject->DeviceExtension;

    //Get User Input Buffer & Length 
    BufLen = p_IO_STK->Parameters.Write.Length;
    Offset = p_IO_STK->Parameters.Read.ByteOffset.QuadPart;
    Buf = (PUCHAR)(Irp->AssociatedIrp.SystemBuffer) + Offset;

    //Get Driver Data Buffer & Length
    DataBuf = p_DVCEXT->DataBuffer;
    DataLen = 1024;

    IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, Irp);

    DbgPrint("Input Buffer Length : %d/r/n", BufLen);
    DbgPrint("Driver Data Length : %d/r/n", DataLen);

    if (BufLen <= DataLen) {
        ByteTransferred = BufLen; 
    } else {
        ByteTransferred = DataLen;
    }

    ByteTransferred = BufLen;
        RtlZeroMemory(
        p_DVCEXT->DataBuffer,
        1024);

    RtlCopyMemory(
        DataBuf,
        Buf, 
        ByteTransferred);

    IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, Irp);
    CompleteRequest(Irp, STATUS_SUCCESS, ByteTransferred);

    DbgPrint("IRP_MJ_WRITE : End/r/n");
    return STATUS_SUCCESS;
}

I/O Support : Direct I/O Mode

Below graph exhibits how Direct I/O mode is supported when data is transferred between client application and driver. Under Direct I/O mode, Memory Manager will create MDL (Memory Descriptor List) to reference the physical address taken by user-provided buffer, all data can be directly referenced via MDL from kernel environment.

In DDK, some MMXxx routines are provided to help you to get MDL that maps to physical address of user-provided buffer.

Below code segment contains the statements that can support data reading under Direct I/O mode. It is achieved by Mmxxx routine, please read it carefully, and you can also find the full code in the zip file. The most important MmXxx you will use in this mode should be - MmGetSystemAddressForMdlSafe, it can obtain the MDL that references the physical address of user-buffer.

Collapse Copy Code
NTSTATUS
PsdoDispatchRead(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
    PVOID Buf; //Buffer provided by user program
    ULONG BufLen; //Buffer length for user provided buffer
    ULONG Offset;//Buffer Offset
    PVOID DataBuf; //Buffer provided by Driver
    ULONG DataLen; //Buffer length for Driver Data Buffer
    ULONG ByteTransferred;
    PIO_STACK_LOCATION p_IO_STK;
    PDEVICE_EXTENSION p_DVCEXT;

    DbgPrint("IRP_MJ_READ : Begin/r/n");
    //Get I/o Stack Location & Device Extension
    p_IO_STK = IoGetCurrentIrpStackLocation(Irp);
    p_DVCEXT = DeviceObject->DeviceExtension;

    //Get User Output Buffer & Length 
    Buf = MmGetSystemAddressForMdlSafe(
        Irp->MdlAddress, HighPagePriority);

    if (Buf == NULL) {
        DbgPrint("Can't get Virtual Address from MDL/r/n");
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    BufLen = MmGetMdlByteCount(Irp->MdlAddress);
    Offset = MmGetMdlByteOffset(Irp->MdlAddress);

    //Get Driver Data Buffer & Length
    DataBuf = p_DVCEXT->DataBuffer;
    if (DataBuf == NULL)
        DataLen = 0;
    else
        DataLen = 1024;

    IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, Irp);

    DbgPrint("Output Buffer Length : %d/r/n", BufLen);
    DbgPrint("Offset for Buffer in the Memory Page: %d/r/n", Offset);
    DbgPrint("Driver Data Length : %d/r/n", DataLen);
    //
    if (BufLen <= DataLen) {
        ByteTransferred = BufLen; 
    } else {
        ByteTransferred = DataLen;
    }

    RtlCopyMemory(
        Buf, 
        DataBuf, 
        ByteTransferred);

    IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, Irp);
    CompleteRequest(Irp, STATUS_SUCCESS, ByteTransferred);

    DbgPrint("IRP_MJ_READ : End/r/n");
    return STATUS_SUCCESS;
}

Below code segment demos the possible workflow to write data from user application to driver:

Collapse Copy Code
NTSTATUS
PsdoDispatchWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
    PVOID Buf; //Buffer provided by user program
    ULONG BufLen; //Buffer length for user provided buffer
    ULONG Offset;//Buffer Offset
    PVOID DataBuf; //Buffer provided by Driver
    ULONG DataLen; //Buffer length for Driver Data Buffer
    ULONG ByteTransferred;
    PIO_STACK_LOCATION p_IO_STK;
    PDEVICE_EXTENSION p_DVCEXT;
    NTSTATUS status;

    DbgPrint("IRP_MJ_WRITE : Begin/r/n");

    //Get I/o Stack Location & Device Extension
    p_IO_STK = IoGetCurrentIrpStackLocation(Irp);
    p_DVCEXT = DeviceObject->DeviceExtension;

    //Get User Input Buffer & Length 
    Buf = MmGetSystemAddressForMdlSafe(
        Irp->MdlAddress, HighPagePriority);

    if (Buf == NULL) {
        DbgPrint("Can't get Virtual Address from MDL/r/n");
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    BufLen = MmGetMdlByteCount(Irp->MdlAddress);
    Offset = MmGetMdlByteOffset(Irp->MdlAddress);

    //Get Driver Data Buffer & Length
    DataBuf = p_DVCEXT->DataBuffer;
    DataLen = 1024;

    IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, Irp);

    DbgPrint("Input Buffer Length : %d/r/n", BufLen);
    DbgPrint("Offset for Buffer in the Memory Page: %d/r/n", Offset);
    DbgPrint("Driver Data Length : %d/r/n", DataLen);

    if (BufLen <= DataLen) {
        ByteTransferred = BufLen; 
    } else {
        ByteTransferred = DataLen;
    }

    ByteTransferred = BufLen;
    RtlZeroMemory(
        p_DVCEXT->DataBuffer,
        1024);

    RtlCopyMemory(
        DataBuf,
        Buf, 
        ByteTransferred);

    IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, Irp);
    CompleteRequest(Irp, STATUS_SUCCESS, ByteTransferred);

    DbgPrint("IRP_MJ_WRITE : End/r/n");
    return STATUS_SUCCESS;
}

Contents of the source zip package

The zip file contains below subfolders:

  1. Application: it contains the client applications to the Pseudo Driver.
  2. bin: it contains the install/uninstall utility for Pseudo Driver.
  3. BufferedIO_PW: it is where the Pseudo Driver that employees Buffered I/O mode for read/write resides.
  4. DirectIO_PW: it is where the Pseudo Driver that employees Direct I/O Mode for read/write resides.
  5. IOCTL_PW: it is where the Pseudo Driver that simply supports user-defined I/O Control Code resides.
  6. ShareFiles: it is the common shared library for PnP, Power Management, I/O completion.
  7. Install: it contains the source code of install/uninstall utility. (Install utility is directly referenced from DDK's sample, I won't provide redundant copy of it, only the uninstall utility source code is provided in it).

How to build the Pseudo Driver?

  1. Unzip the package to some folder you'd like it to be, let's name it ROOT_OF_SOURCE.
  2. Select Start->Programs->Development Kits->Windows DDK xxxx.xxxx->Build Environments->Free Build. (This is for free release without debug information in it.)
  3. Enter ROOT_OF_SOURCE/SharedFiles subfolder, enter build -cefw, it all goes well, shared library will be generated.
  4. Enter ROOT_OF_SOURCE/BufferedIO_PW subfolder, enter build -cefw, it will create Pseudo Driver - BufferDrv.sys. Copy this file into ROOT_OF_SOURCE/BufferedIO_PW/Install if you have made add-in for any new features, the copy is for later driver install.
  5. Enter ROOT_OF_SOURCE/DirectIO_PW subfolder, enter build -cefw, it will create Pseudo Driver - DirectDrv.sys. Copy this file into ROOT_OF_SOURCE/DirectIO_PW/Install if you have made add-in for any new features, the copy is for later driver install.
  6. Enter ROOT_OF_SOURCE/IOCTL_PW subfolder, enter build -cefw, it will create Pseudo Driver - PseudoDrv.sys. Copy this file into ROOT_OF_SOURCE/IOCTL_PW/Install if you have made add-in for any new features, the copy is for later driver install.

Install Pseudo Driver into system (XP)

  1. Unzip the source file, launch DOS prompt-console.
  2. Enter into bin subfolder.
  3. Execute DevInst.bat, it will automatically install the Pseudo Driver into your system.

Uninstall Pseudo Driver from system (XP)

  1. Enter into bin subfolder.
  2. Execute DevRemove.bat, it will automatically uninstall all-driver related resources from your system.

Execute client application

You can enter into ROOT_OF_SOURCE/Application subfolder, execute bufferclient.exe, directclient.exe, and clientapp.exe to verify if the three Pseudo Drivers have been installed successfully.

Known Issues

  • The install/uninstall of Pseudo Driver won't wok on Window 2000, the root cause might be that the Setup API doesn't work on Window 2000, can't allow driver installed without a physical hardware in it. Can anybody help to resolve it? Many Thanks.
  • If you'd like to install/uninstall the Pseudo Driver in Windows 2000, you will need to launch New Hardware Wizard from within Device Manager, and select to install new hardware->Display all hardware->Install from disk->"ROOT_OF_SOURCE/BufferedIO_PW/Install", click on OK button. New Hardware Wizard will install Buffered I/O Pseudo Driver. (This is for Buffered I/O demo driver install. As for Direct I/O, please set source directory to "ROOT_OF_SOURCE/DirectIO_PW/Install").
  • Reboot is required if the driver has been reinstalled after un-installation. I don't know why this happened, I hope somebody can inform me. Many Thanks.

Future Directions for Pseudo Driver

  1. Fix above issues.
  2. WMI support in Pseudo Driver will be added-in.
目录
相关文章
|
机器学习/深度学习 数据采集 数据处理
掌握时间序列特征工程:常用特征总结与 Feature-engine 的应用
本文介绍了时间序列特征工程,包括滚动统计量、滞后特征、差分和变换等技术,用于提升机器学习模型性能。文章还推荐了Python库`feature-engine`,用于简化特征提取,如处理缺失值、编码分类变量和进行时间序列转换。示例代码展示了如何使用`feature-engine`提取时间戳信息、创建滞后特征和窗口特征。通过创建管道,可以高效地完成整个特征工程流程,优化数据预处理并提高模型效果。
2490 15
|
运维 Ubuntu 安全
kali2022.1(安装)
kali2022.1(安装)
561 0
|
jenkins Java Shell
使用 Docker 安装 Jenkins 并实现项目自动化部署
Jenkins 是一款开源的持续集成(DI)工具,广泛用于项目开发,能提供自动构建,测试,部署等功能。作为领先的开源自动化服务器,Jenkins 提供了数百个插件来支持构建、部署和自动化任何项目。
36975 3
使用 Docker 安装 Jenkins 并实现项目自动化部署
|
JavaScript
Vue3按钮(Button)
这是一个高度可定制的按钮组件,支持多种属性设置,包括按钮类型、形状、图标、尺寸、背景透明度、波纹颜色、跳转地址、打开方式、禁用状态、加载状态及指示符样式等。预览图展示了不同配置下的按钮样式变化。组件通过Vue实现,并提供了丰富的自定义选项以适应各种场景需求。
1087 1
Vue3按钮(Button)
|
前端开发
CSS动画变形宝典:Transform属性,打造动态视觉盛宴!
CSS动画变形宝典:Transform属性,打造动态视觉盛宴!
|
Android开发 iOS开发 异构计算
电竞模式,启动!用无影云电脑畅玩3A游戏大作
电竞模式,启动!用无影云电脑畅玩3A游戏大作
|
人工智能 Java 程序员
一文彻底拿下HarmonyOS实战开发之HMRouter实现跳转
本文介绍HarmonyOS页面跳转的两种方式:组件导航(Navigation)和页面路由(@ohos.router)。重点推荐使用组件导航,因其灵活性和多端部署能力更强。此外,还介绍了HMRouter,一个简化页面跳转的工具,支持自定义注解、路由拦截、动画配置等功能。通过详细步骤,展示了如何在项目中集成HMRouter并实现页面跳转,帮助开发者更高效地开发鸿蒙应用。君志所向,一往无前!关注我,带你起飞鸿蒙开发!
1146 0
|
网络虚拟化 网络架构
|
机器学习/深度学习 前端开发 JavaScript
前端小白也能学会的高大上技巧:如何让你的网页支持语音控制?
【10月更文挑战第31天】你是否曾梦想过只需动动嘴皮子就能操控网页?现在,这个梦想触手可及。即使你是前端小白,也能轻松学会让网页支持语音控制的高大上技巧。本文将介绍语音控制的基本概念、实现方法和具体示例,带你走进语音控制的奇妙世界。通过Web Speech API,你只需掌握基本的HTML、CSS和JavaScript知识,就能实现语音识别和控制功能。快来尝试吧!
1664 4
|
Windows
油猴脚本(篡改猴)获取某度网盘链接
本文档介绍如何安装及使用Tampermonkey(油猴)测试版插件来增强浏览器功能,并配合aria2c下载工具实现高效下载。首先需从官方或可靠来源下载油猴测试版并确保移除原有正式版以避免冲突。接着安装aria2c至系统目录使全局可用。利用特定油猴脚本如“网盘直链下载助手”,可以将网盘文件转换为直接下载链接,再通过桌面快捷方式打开PowerShell执行aria2c下载。文档还推荐了一些实用脚本,例如“懒人工具箱”,并提供了获取链接。通过这些步骤,用户能够显著提升日常浏览体验及资源下载效率。
油猴脚本(篡改猴)获取某度网盘链接