分析Windows NT/2000堆内存与虚拟内存组织(http://webcrazy.yeah.net)

Windows 10段堆内部机理.pdf 又一篇非常优秀的论文《Windows 10 Segment Heap Internals》,介绍了Windows 10系统中为apps和若干系统EXE新引入的段堆。 本文详述了Win 10段堆相关的数据结构和算法,同时还对段堆在一开始被设计出来就引入的各种安全机制做了深入剖析,图文并茂的阐释了哲学经典三问“我是谁,我从哪里来,要到哪里去?”。 文章最后还以CVE-2016-0117漏洞作为案例,证明了对段堆的深入理解相当的有助于编写其EXP,对于EXP中堆风水的设计策略以及手法都做了详细的解释。 总之,这依然是一篇不可多得的好文。 来自看雪论坛:https://bbs.pediy.com/thread-246949.htm 立即下载
 分析Windows NT/2000堆内存与虚拟内存组织   
                  WebCrazy( http://webcrazy.yeah.net/)


    在讨论今天这个主题时,我觉得是应该给大家重复推介以下两本书:

    Matt Pietrek
        <<Windows 95 System Programming Secrets>>
    Jeffrey Richter
        <<Programming Microsoft Windows,fourth Edition>>

    两者分别从核心态与用户态出发全面阐述了Windows(虽然不全是Windows NT/2000)的堆内存与虚拟内存管理与组织。在阅读之前,最好是先浏览浏览。我不对一些基本API或是概念进行说明。

    首先谈谈堆内存的组织。Windows NT/2000组织堆内存的一些核心数据结构均存于进程PEB中。PEB结构中定义如下的成员(我不确认PEB的结构):

    typedef struct _PEB
    {
          .
          .
          .
       PVOID ProcessHeap;                   // 进程的默认堆地址,位于PEB后的0x18处
          .
          .
          .
       ULONG NumberOfHeaps;                 // 进程当前堆的数目,位于PEB后的0x88处
       ULONG MaximumNumberOfHeaps;          // 进程能拥有的堆的最大数目,,位于PEB后的0x8C处
       PVOID** ProcessHeaps;                // 进程拥有的所有堆的地址列表,位于PEB后的0x90处
          .
          .
          .
    } PEB, *PPEB;

    关于PEB的地址及其初步的分析请参阅我的 《深入Windows NT/2000模块的组织》 。为了更好的理解,我将SoftICE的部分分析过程列于底下:

    :addr cmd       //以下将只针对cmd.exe进程

    :heap 32 cmd    //显示cmd进程的堆
        Base      Id  Cmmt/Psnt/Rsvd  Segments  Flags     Process
        00130000  01  0025/0024/00DB         1  00000002  cmd
           |
           |_进程默认堆地址(在Windows平台其也作为进程堆的句柄返回给用户)

        00230000  02  0003/0002/000D         1  00008000  cmd
        007C0000  03  0008/0008/0008         1  00001002  cmd
        00800000  04  0004/0004/000C         1  00001002  cmd

    :? #dword(@(7ffdf000+18))   //显示进程的默认堆的地址
    00130000  0001245184  "  "

    :dd 7ffdf000+88 l 10        //显示PEB中的几个与Heap有关的成员,见上
    0010:7FFDF088 00000004  00000010  77FCE380  00450000      ...........w..E.
                     |          |        |
                     |          |        |  ProcessHeaps值,Windows 2000提供API GetProcessHeaps
                     |          |        |_ 用于枚举进程拥有的所有堆的地址列表
                     |          |_MaximumNumberOfHeaps值
                     |_ NumberOfHeaps值

    :dd @(7ffdf000+90) l dword(@(7ffdf000+88))*4
    0023:77FCE380 00130000  00230000  007C0000  00800000      ......#...|.....
                  --------------------------------------
                            |
                            |_cmd进程当前所有堆的地址列表(与上SoftICE的heap 32命令输出比一比)

    上面的分析只针对堆的大概描述,对每个堆内部内存使用情况,Windows NT/2000提供了HeapWalk API用于调试目的,它能遍历指定堆的内容。SoftICE的heap 32命令加上-w参数也可以遍历这些内容。在Windows NT/2000中提供了以Heap(RtlHeap)头始的函数族用于堆内存的操作,在这些函数的内部Windows NT/2000最终调用VirtualAlloc等来实现内存的使用。而这正是我以下要讨论的关于虚拟内存的部分。由于堆内存在Windows NT/2000中的操作是十分频繁的,为避免频繁的Ring切换的时间损耗,Heap内存的一些主要数据结构存于用户态空间的PEB结构中,而不存于内核态空间的KPEB中。要熟悉PE装载例程(Ldr函数族)的话,就不得不看看Microsoft CRT源代码,CRT代码中关于建立CRT使用堆(主要供new语法使用)的代码对理解Heap的应用可以有比较全面的理解,如HeapInit.c、HeapHook.c、HeapDump.c、HeapWalk.c等等。

    下面谈谈虚拟内存的组织。构成虚拟内存管理基础的结构是VAD(Virtual Address Descriptors)。由于Windows NT/2000内存子系统在真正使用存储空间时才真正分配实际物理存储空间的,所以内核就提供VAD这个结构用于存放虚拟内存空间保留、提交等一些数据。VAD结构位于EPROCESS(KPEB)中:

    kd> !processfields
     EPROCESS structure offsets:
          .
          .
          .
    VadRoot:                           0x194
          .
          .
          .

    VAD使用二叉树结构组织,从上面i386kd/windbg的输出结果可看出VAD在KPEB中的位置。SoftICE的query命令可以导出这个系统中非常重要的结构,下面我列出实现这一命令的函数:


    //-----------------------------------------------------------------
    //
    //  QuerySystemProcessVAD--Portion cut from 《Undocumented Windows NT》
    //  Rewrite By WebCrazy(http://webcray.yeah.net) on 11-20-2000!
    //  Only test on Windows 2000 Server Build 2195!
    //
    //-----------------------------------------------------------------

    #define MAX_VAD_ENTRIES 0x200
    #define VADOffset                   0x194

    typedef struct vad {
        void *StartingAddress;
        void *EndingAddress;
        struct vad *ParentLink;
        struct vad *LeftLink;
        struct vad *RightLink;
        ULONG Flags;
        ULONG MMCI;
        ULONG ProtoPTE;
    }VAD, *PVAD;

    #pragma pack(1)
    typedef struct VadInfo {
        void *VadLocation;
        VAD Vad;
    }VADINFO, *PVADINFO;
    #pragma pack()

    VADINFO VadInfoArray[MAX_VAD_ENTRIES];
    int VadInfoArrayIndex;
    PVAD VadTreeRoot;

    void _stdcall VadTreeWalk(PVAD VadNode)
    {
        if (VadNode==NULL) {
           return;
        }

        VadTreeWalk(VadNode->LeftLink);

        if (VadInfoArrayIndex<MAX_VAD_ENTRIES) {
 
            VadInfoArray[VadInfoArrayIndex].VadLocation=VadNode;
            VadInfoArray[VadInfoArrayIndex].Vad.StartingAddress=VadNode->StartingAddress;
            VadInfoArray[VadInfoArrayIndex].Vad.EndingAddress=VadNode->EndingAddress;

           (ULONG)VadInfoArray[VadInfoArrayIndex].Vad.StartingAddress<<=12;
           (ULONG)VadInfoArray[VadInfoArrayIndex].Vad.EndingAddress+=1;
           (ULONG)VadInfoArray[VadInfoArrayIndex].Vad.EndingAddress<<=12;
           (ULONG)VadInfoArray[VadInfoArrayIndex].Vad.EndingAddress-=1;

            VadInfoArray[VadInfoArrayIndex].Vad.ParentLink=VadNode->ParentLink;
            VadInfoArray[VadInfoArrayIndex].Vad.LeftLink=VadNode->LeftLink;
            VadInfoArray[VadInfoArrayIndex].Vad.RightLink=VadNode->RightLink;
            VadInfoArray[VadInfoArrayIndex].Vad.Flags=VadNode->Flags;
            if(VadNode->MMCI>0x80000000)
                 VadInfoArray[VadInfoArrayIndex].Vad.MMCI=VadNode->MMCI;
            else VadInfoArray[VadInfoArrayIndex].Vad.MMCI=0;
            if(VadNode->ProtoPTE>0x80000000)
                 VadInfoArray[VadInfoArrayIndex].Vad.ProtoPTE=VadNode->ProtoPTE;
            else VadInfoArray[VadInfoArrayIndex].Vad.ProtoPTE= 0;
            VadInfoArrayIndex++;

        }
        VadTreeWalk(VadNode->RightLink);
    }

    void VadTreeDisplay()
    {
        int i;
        DbgPrint("/nVadRoot is located @%08x/n", VadTreeRoot);
        DbgPrint("Vad@     Starting Ending   Parent   LeftLink 
                RrightLink Flags    MMCI     PTE/n");
        for (i=0; i<VADINFOARRAYINDEX; i++) {
             DbgPrint("%08X %08X %08X %08X %08X %08X %08X %08X %08X/n",
                 VadInfoArray[i].VadLocation, 
                 VadInfoArray[i].Vad.StartingAddress, 
                 VadInfoArray[i].Vad.EndingAddress, 
                 VadInfoArray[i].Vad.ParentLink, 
                 VadInfoArray[i].Vad.LeftLink, 
                 VadInfoArray[i].Vad.RightLink,
                 VadInfoArray[i].Vad.Flags,
                 VadInfoArray[i].Vad.MMCI,
                 VadInfoArray[i].Vad.ProtoPTE);
        }
    }


    void QuerySystemProcessVAD()
    {
        if(((USHORT)NtBuildNumber)!=2195){
            DbgPrint("Only test on Windows 2000 Server Build 2195!/n");
            return;
        }
      
        // Query system process VAD
        VadTreeRoot=(PVAD)*(ULONG *)((char *)PsInitialSystemProcess+VADOffset);
        VadInfoArrayIndex=0;
        VadTreeWalk(VadTreeRoot);
        VadTreeDisplay();
    }

    看看QuerySystemProcessVAD例程的输出结果吧:

      VadRoot is located @fe4ddf28
      Vad@     Starting Ending   Parent   LeftLink RrightLink Flags    MMCI     PTE
      FE4DDF28 00010000 00042FFF 00000000 00000000 FE354DC8 04000000 FE4DD0E8 E1008A40
      FF874348 00060000 00060FFF FE354DC8 00000000 FF8389A8 C4000001 00000000 00000000
      FF8389A8 00070000 00070FFF FF874348 00000000 FF7B72C8 04000000 FF8345A8 E2747880
      FF7B72C8 00080000 0017FFFF FF8389A8 00000000 00000000 04000000 FE33DB48 E2936040
      FE354DC8 77F80000 77FF8FFF FE4DDF28 FF874348 00000000 07100003 FE354B68 E1630040

    与下面SoftICE的query命令的输出结果,再与上面的输出比较比较:

      :query system
      Address Range      Flags     MMCI      PTE       Name
      00010000-00042000  04000000  FE4DD0E8  E1008A40
      00060000-00060000  C4000001
      00070000-00070000  04000000  FF8345A8  E2747880
      00080000-0017F000  04000000  FE33DB48  E2936040  Heap
      77F80000-77FF8000  07100003  FE354B68  E1630040  ntdll.dll

    好了,到这儿我已经将Windows NT/2000用户代码使用内存的两个主要机制(另一个途径可能是FileMapping对象吧)呈现在你面前了,当然内部使用的方法还牵涉到许多内容,也只能靠你自己了。

    参考资料:
      1.Jeffrey Richter 
          <<Programming Applications for Microsoft Windows,Fourth Edition>>
      2.Matt Pietrek<<Windows 95 System Programming Secrets>>
      3.<<Undocumented Windows NT>>附带源码
云计算虚拟化实验(一)——虚拟机软件VMware的安装以及虚拟机系统的安装(Windows7+CentOS7) 主要内容: 1、虚拟机软件的安装(以VMware为例) 2、虚拟机系统的安装(Window7 + CentOS7) 3、虚拟主机网络互访 4、不同局域网内的虚拟主机互访(软路由——海蜘蛛) 0 ——首先介绍一下什么是虚拟化? 虚拟化,是指通过虚拟化技术将一台计算机虚拟为多台逻辑计算机。在一台计算机上同时运行多个逻辑计算机,每个逻辑计算机可运行不同的操作系统,并且应用程序都可以在 阅读详情

相关推荐

NDIAurora 6D、5D传感器裸线焊接与烧录 6D传感器型号:10001742;5D传感器型号:610157

NDI Aurora 6D、5D传感器裸线焊接与烧录

yanghong2000719的博客 1201

Windows设置虚拟内存

选择一个设置虚拟内存的盘(如果C盘容量不够的话,尽量设置其他盘),点击自定义大小,设置初始大小和最大值。1)输入栏输入“环境变量”,双击打开“,编辑系统环境变量”,进入系统属性。在性能选项中选择高级,点击更改。选择高级,打开性能中的设置。设置完毕,一路点击确定即可。

weixin_74261199的博客 2559

AD21导出gerber文件

文件–>制造输出–>Gerber Files 文件–>制造输出–>Gerber Files 通用、光圈和高级跟第一步一样 3.文件–>制造输出–>NC Drill Files 这个默认就可以了 主要参考视频来自bilibili添加链接描述 此篇博客主要是写给自己好记的,无其他商业用途 ...

新手小杨 1万+

Windows内核理解1(基于Inside Windows NT)

《Windows内核理解1》摘要: 本文基于《Inside Windows NT》梳理Windows内核机制,重点解析进程、线程与纤程的核心概念。进程是资源容器,包含虚拟地址空间(32位2-3GB/64位128TB)、工作集(物理内存驻留部分)、可执行映像、句柄表、安全上下文等要素,由PEB管理模块列表与进程参数。线程是CPU调度实体,共享进程资源但拥有独立栈与寄存器,通过ETHREAD/KTHREAD结构实现抢占式多任务调度。纤程作为用户态轻量线程,由应用自主协作调度,无内核开销但需手动切换。三者形成&q

charlie114514191的博客 1233

Matlab信号处理

Matlab信号处理 需要安装的APP: Signal Analyzer app:提供一个可以在时域和频域可视化、测量、分析和比较信号的工具; Filter Builder和Filter Designer app:可以很方便通过下拉菜单和填写参数的方式来设计和分析数字滤波器; 对于小波分析,Matlab提供了: 1.Wavelet Signal Denoise app:利用离散小波变换对信号去噪; 2.Signal Multiresolution Analyzer app:对信号进行多分辨率的分析和可视化

Huichin_Min的博客 4399

20220601超简单百度地图街景图片爬取+绿视率计算

真的不难咯。

weixin_39676904的博客 1万+

堆(heap)系列_0x08:NT堆调试支持_立刻发现调试支持(DPH)

页堆:将堆的全部或部分调用到另一个堆管理器,会将申请的,且将的下一个页设置为,用这个只读页来检查缓冲区溢出;也可以将保护页(或者叫栅栏页)放在堆块前面;页堆在内存方面会造成巨大开销,因此1.运行notepad.exe,启动任务管理器记录这个notepad实例的PID和提交大小2.启动页堆管理员权限运行(使用方法,点击这里查看),GUI界面上给notepad.exe开启页堆,命令行也可以观察启动和关闭页堆前后的提交大小变化如下,开启后,提交大小明显变大,这是因为页堆提供了额外的内存分配 扩展命令扩展:!he

可乐松子的博客 1851

CODESYS开发实战:EtherCAT总线伺服从站配置与运动控制

本文详细介绍了在CODESYS平台上进行EtherCAT总线伺服从站配置与运动控制的实战教程。内容涵盖从设备描述文件安装、主从站拓扑构建,到核心的伺服使能状态机编程、过程数据映射,以及最终实现点位运动控制的完整流程。文章旨在帮助开发者快速掌握基于CODESYS的EtherCAT总线伺服控制技术,提升工业自动化项目开发效率。

weixin_29281915的博客 327

Windbg下对NT堆异常内存的排查

在 Windows 平台上有一个非常底层的内存分配函数 VirtualAlloc,它会一次性分配 64k 整数倍的内存段,内部对象按4k的内存页对齐,如果让程序直接接触 VirtualAlloc,很容易造成内存的浪费以及分配效能的降低,为了提高性能和内存使用效率, Windows 给进程提供了一个堆管理器对对象分配进行细粒度化管理,32位程序的颗粒度位8Byete,64位程序则为16Byte。HEAP 开头有一块大小为 0x4a8 的 HEAP_ENTRY 堆块,用来存放 _HEAP 结构的元数据信息。

u011447703的博客 905

Cadence 开发合集【总目录】

专栏主要介绍Cadence原理图和PCB信号完整性电源完整性仿真相关的内容,更加注重实操,以帮助大家轻松上手并不断提高;在开始专栏的更新前,博主先列出专栏的大纲,以供大家参考。大家直接点击大纲中的蓝色标题即可轻松传送。 ............

fydar的博客 1万+

第9.3章 从CMake基础到自动驾驶实战:构建跨平台C/C++项目的全能指南

《CMake构建实战:项目开发卷》精要指南 本文系统梳理了许宏旭《CMake构建实战》核心内容,涵盖C++构建全流程: 基础构建:详解MSVC/GCC编译器使用、Makefile编写及静态/动态库构建方法 CMake核心:解析语法结构、变量控制、文件操作等关键命令,阐述项目全生命周期管理 实战应用:通过快速排序、手写数字识别项目演示现代CMake实践,特别针对自动驾驶领域给出多传感器融合、SLAM等场景的CMake配置方案 特色价值:本书填补了官方文档实例不足的空白,提供300+代码示例,是掌握工业级C++

行知SLAM 1343

[内存][转贴]探悉Windows 2000/XP Pool分配流程

WebCrazy(http://webcrazy.yeah.net) 对于Driver编写者,最之烦琐的莫过于各种内存缓冲区的使用(谈到缓冲区,你可能还会想到诸如MDL等概念,其实MDL只是对StartVa指定的Pool的Page Frame Number进行组织而已)。在用户态对于小块零星的内存使用牵涉到Heap,Windows 2000/XP在核心态提供了同样的一个机制用于这部分内...

weixin_34095889的博客 381

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

取证——内存取证

内存取证是指在计算机或其他数字设备运行时,通过对其随时存储的内存数据进行采集、分析和提取,以获取有关设备状态、操作过程和可能存在的安全事件的信息。内存取证是数字取证的一个重要分支,用于从计算机的RAM(随机存取存储器)或其他设备的内存中提取关键信息,以便了解设备在特定时间点的状态和活动。

m0_73756016的博客 3544

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Windows NT 6 终极瘦身

Windows NT 6包括vista win7 win8(win8只有部分适用)    这些系统装完之后,先把虚拟内存,个人文档,临时文件等放到其他盘去,因为   一:备份的时候不需要他们(虚拟内存,临时文件)   二:以免C盘出问题数据丢失(个人文档)   备份的时候先清理磁盘,关掉休眠,尽量作到最小备份。   所有文件,如果是视频、、音频、图片等都是可以安全删除的,其他的系统文件如...

liyuming566的博客 780

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

堆(heap)系列_0x07:NT堆调试支持_滞后发现调试支持

堆被破坏遇到最常见的原因就是溢出,下面主要介绍一下正常的堆结构和一种典型的溢出场景先要熟悉正常的堆块结构,下面是堆块结构和溢出的示意图用户只能访问到堆块的(HeapAlloc函数以返回地址的形式将用户数据区的起始地址返回)指的是访问非用户数据区的内存,有可能破坏了堆的控制数据(等)或者触发了堆检查机制(大多数检查机制都位于用户数据区的尾部)下面是一个在空闲堆块上分配一块内存的示意图:在堆上,分配一块内存简要步骤:示例场景:写入数据太多,导致覆盖当前堆块用户数据区后面结构和,再次申请内存由于破坏导致分配内存失

可乐松子的博客 1552

解析Winndows 2000/XP物理内存管理(http://webcrazy.yeah.net)

解析Winndows 2000/XP物理内存管理            WebCrazy(http://webcrazy.yeah.net)    物理内存是相对比较紧张的资源,合理利用将是一个操作系统的性能的关键。Windows 2000/XP内部使用一个称为页框数据库(Page Frame Database)的结构用于描述物理内存的状态。本文将从这一结构入手详述Windows物理内存的组织与管

FreeXploiT 1598

windows虚拟内存管理

内存管理是操作系统非常重要的部分,处理器每一次的升级都会给内存管理方式带来巨大的变化,向早期的8086cpu的分段式管理,到后来的80x86 系列的32位cpu推出的保护模式和段页式管理。在应用程序中我们无时不刻不在和内存打交道,我们总在不经意间的进行堆内存和栈内存的分配释放,所以内存是我们进行程序设计必不可少的部分。 CPU的内存管理方式 段寄存器怎么消失了? 在学习8086汇编语

Masimaro的专栏 7600

解读Windows 2000/XP分层驱动模型(http://webcrazy.yeah.net)

 解读Windows 2000/XP分层驱动模型            WebCrazy(http://webcrazy.yeah.net)    可扩展性是Windows NT/2000/XP设计的目标之一,其分层驱动模型是可扩展性的最好体现。实现分层依赖于IO管理器的两个重要的设计:1、Windows中的任何一个驱动程序都被设计成Client/Server模式。对于客户端驱动,通过IoGetD

FreeXploiT 2544

windowNT的分页和非分页内存的分配

: WINDOWS Windows规定有些虚拟内存可以交换到文件中,这类内存被称为分页内存 有些虚拟内存永远不会交换到文件中,这些内存叫非分页内存 #define PAGEDCODE  code_seg(“PAGE”);//分页内存 #define LOCKEDCODE  code_seg();//非分页的 #define INITCODE  code_seg(“INIT”);

dasgk的专栏 1177

堆(heap)系列_0x01:堆-Introduction(框架+类型)

1.堆(heap)和栈(stack)的区别; 2.操作系统层面堆的框架; 3.堆的类型介绍(CRT堆+NT堆+段堆+默认堆+私有堆)

可乐松子的博客 706

虚拟内存(VirtualAlloc),堆(HeapAlloc/malloc/new)和Memory Mapped File

内存管理有三种方式: 1. 虚拟内存,VirtualAlloc之类的函数 2. 堆,Heapxxx函数,malloc,new等 3. 内存映射文件,Memory Mapped File 很多人都会困惑,但是看下面的图片就会比较明白了。这个图片从MSDN上拷来。 堆和虚拟内存,从上面的图片就可以看出,其实所谓的堆,也就是在虚拟内存上抽象出来的。如果直接用Virtualxxx系列函数,是

zj510的专栏 8284

在NT中直接访问物理内存

P.bhw98{ PADDING-RIGHT: 0px; PADDING-LEFT: 0px; FONT-SIZE: 9pt; PADDING-BOTTOM: 0px; MARGIN: 10px 0px 5px; LINE-HEIGHT: normal; PADDING-TOP: 0px; FONT-FAMIL

3785

(转载)Gloomy对Windows内核的分析(介绍)

INTRO (写给NT研究者)=============================                                             Мы всего лишь момент во времени                                             Отблеск в глазах,                

峥嵘岁月 5280

【数据结构-5】单链表的实现

单链表,具体来说是单向的、不带头的、不循环的链表。链表由一个一个节点组成,每个节点分为数据域和指针域,数据域存放数据,指针域存放下一节点的地址。这样,就可以根据指针域中的地址找到下一个节点,节点就串起来了构成了单链表。

weixin_44218929的博客 176
上一篇: 剖析Windows NT/2000内核对象组织(http://webcrazy.yeah.net)
下一篇: 解析Windows NT/2000窗口对象的组织(http://webcrazy.yeah.net/)
freexploit
博客等级 码龄22年 315粉丝 778原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值