在上一篇文章《驱动开发:内核中实现Dump进程转储》中我们实现了ARK工具的转存功能,本篇文章继续以内存为出发点介绍VAD结构,该结构的全程是Virtual Address Descriptor即虚拟地址描述符,VAD是一个AVL自平衡二叉树,树的每一个节点代表一段虚拟地址空间。程序中的代码段,数据段,堆段都会各种占用一个或多个VAD节点,由一个MMVAD结构完整描述。
VAD结构的遍历效果如下:
那么这个结构在哪?每一个进程都有自己单独的VAD结构树,这个结构通常在EPROCESS结构里面里面,在内核调试模式下使用dt _EPROCESS可得到如下信息。- lyshark.com 1: kd> dt _EPROCESS
- ntdll!_EPROCESS
- +0x500 Vm : _MMSUPPORT_FULL
- +0x640 MmProcessLinks : _LIST_ENTRY
- +0x650 ModifiedPageCount : Uint4B
- +0x654 ExitStatus : Int4B
- +0x658 VadRoot : _RTL_AVL_TREE
- +0x660 VadHint : Ptr64 Void
- +0x668 VadCount : Uint8B
- +0x670 VadPhysicalPages : Uint8B
- +0x678 VadPhysicalPagesLimit : Uint8B
复制代码 可以看到在本系统中VAD的偏移是+0x658紧跟其后的还有vadCount的计数等。
VAD结构是如何被添加的?通常情况下系统调用VirtualAllocate等申请一段堆内存时,则会在VAD树上增加一个结点_MMVAD结构体,需要说明的是栈并不受VAD的管理。由系统直接分配空间,并把地址记录在了TEB中。- lyshark.com 0: kd> dt _MMVAD
- nt!_MMVAD
- +0x000 Core : _MMVAD_SHORT
- +0x040 u2 : <anonymous-tag>
- +0x048 Subsection : Ptr64 _SUBSECTION
- +0x050 FirstPrototypePte : Ptr64 _MMPTE
- +0x058 LastContiguousPte : Ptr64 _MMPTE
- +0x060 ViewLinks : _LIST_ENTRY
- +0x070 VadsProcess : Ptr64 _EPROCESS
- +0x078 u4 : <anonymous-tag>
- +0x080 FileObject : Ptr64 _FILE_OBJECT
复制代码 结构体MMVAD则是每一个VAD内存块的属性,这个内存结构定义在WinDBG中可看到。
如上在EPROCESS结构中可以找到VAD结构的相对偏移+0x658以及进程VAD计数偏移+0x668,我们首先通过!process 0 0指令得到当前所有进程的EPROCESS结构,并选中进程。- lyshark.com 0: kd> !process 0 0
- PROCESS ffffe28fbb0860c0
- SessionId: 1 Cid: 11a8 Peb: 0035c000 ParentCid: 11c8
- DirBase: 309f3002 ObjectTable: ffffac87ba3da580 HandleCount: 145.
- Image: x64.exe
复制代码 此处的ffffe28fbb0860c0正是我们所需要的EPROCESS结构。
当需要得到该进程的VAD结构时,只需要使用!vad ffffe28fbb0860c0 + 0x658来显示该进程的VAD树。
至于获取VAD有多少条,则可以直接使用!vad ffffe28fbb0860c0 + 0x668来获取到。
既然手动可以遍历出来,那么自动化也并不难,首先定义头文件vad.h同样这是微软定义,如果想要的到最新的,自己下载WinDBG调试内核输入命令。- #pragma once
- #include <ntifs.h>
- typedef struct _MM_GRAPHICS_VAD_FLAGS // 15 elements, 0x4 bytes (sizeof)
- {
- /*0x000*/ ULONG32 Lock : 1; // 0 BitPosition
- /*0x000*/ ULONG32 LockContended : 1; // 1 BitPosition
- /*0x000*/ ULONG32 DeleteInProgress : 1; // 2 BitPosition
- /*0x000*/ ULONG32 NoChange : 1; // 3 BitPosition
- /*0x000*/ ULONG32 VadType : 3; // 4 BitPosition
- /*0x000*/ ULONG32 Protection : 5; // 7 BitPosition
- /*0x000*/ ULONG32 PreferredNode : 6; // 12 BitPosition
- /*0x000*/ ULONG32 PageSize : 2; // 18 BitPosition
- /*0x000*/ ULONG32 PrivateMemoryAlwaysSet : 1; // 20 BitPosition
- /*0x000*/ ULONG32 WriteWatch : 1; // 21 BitPosition
- /*0x000*/ ULONG32 FixedLargePageSize : 1; // 22 BitPosition
- /*0x000*/ ULONG32 ZeroFillPagesOptional : 1; // 23 BitPosition
- /*0x000*/ ULONG32 GraphicsAlwaysSet : 1; // 24 BitPosition
- /*0x000*/ ULONG32 GraphicsUseCoherentBus : 1; // 25 BitPosition
- /*0x000*/ ULONG32 GraphicsPageProtection : 3; // 26 BitPosition
- }MM_GRAPHICS_VAD_FLAGS, *PMM_GRAPHICS_VAD_FLAGS;
- typedef struct _MM_PRIVATE_VAD_FLAGS // 15 elements, 0x4 bytes (sizeof)
- {
- /*0x000*/ ULONG32 Lock : 1; // 0 BitPosition
- /*0x000*/ ULONG32 LockContended : 1; // 1 BitPosition
- /*0x000*/ ULONG32 DeleteInProgress : 1; // 2 BitPosition
- /*0x000*/ ULONG32 NoChange : 1; // 3 BitPosition
- /*0x000*/ ULONG32 VadType : 3; // 4 BitPosition
- /*0x000*/ ULONG32 Protection : 5; // 7 BitPosition
- /*0x000*/ ULONG32 PreferredNode : 6; // 12 BitPosition
- /*0x000*/ ULONG32 PageSize : 2; // 18 BitPosition
- /*0x000*/ ULONG32 PrivateMemoryAlwaysSet : 1; // 20 BitPosition
- /*0x000*/ ULONG32 WriteWatch : 1; // 21 BitPosition
- /*0x000*/ ULONG32 FixedLargePageSize : 1; // 22 BitPosition
- /*0x000*/ ULONG32 ZeroFillPagesOptional : 1; // 23 BitPosition
- /*0x000*/ ULONG32 Graphics : 1; // 24 BitPosition
- /*0x000*/ ULONG32 Enclave : 1; // 25 BitPosition
- /*0x000*/ ULONG32 ShadowStack : 1; // 26 BitPosition
- }MM_PRIVATE_VAD_FLAGS, *PMM_PRIVATE_VAD_FLAGS;
- typedef struct _MMVAD_FLAGS // 9 elements, 0x4 bytes (sizeof)
- {
- /*0x000*/ ULONG32 Lock : 1; // 0 BitPosition
- /*0x000*/ ULONG32 LockContended : 1; // 1 BitPosition
- /*0x000*/ ULONG32 DeleteInProgress : 1; // 2 BitPosition
- /*0x000*/ ULONG32 NoChange : 1; // 3 BitPosition
- /*0x000*/ ULONG32 VadType : 3; // 4 BitPosition
- /*0x000*/ ULONG32 Protection : 5; // 7 BitPosition
- /*0x000*/ ULONG32 PreferredNode : 6; // 12 BitPosition
- /*0x000*/ ULONG32 PageSize : 2; // 18 BitPosition
- /*0x000*/ ULONG32 PrivateMemory : 1; // 20 BitPosition
- }MMVAD_FLAGS, *PMMVAD_FLAGS;
- typedef struct _MM_SHARED_VAD_FLAGS // 11 elements, 0x4 bytes (sizeof)
- {
- /*0x000*/ ULONG32 Lock : 1; // 0 BitPosition
- /*0x000*/ ULONG32 LockContended : 1; // 1 BitPosition
- /*0x000*/ ULONG32 DeleteInProgress : 1; // 2 BitPosition
- /*0x000*/ ULONG32 NoChange : 1; // 3 BitPosition
- /*0x000*/ ULONG32 VadType : 3; // 4 BitPosition
- /*0x000*/ ULONG32 Protection : 5; // 7 BitPosition
- /*0x000*/ ULONG32 PreferredNode : 6; // 12 BitPosition
- /*0x000*/ ULONG32 PageSize : 2; // 18 BitPosition
- /*0x000*/ ULONG32 PrivateMemoryAlwaysClear : 1; // 20 BitPosition
- /*0x000*/ ULONG32 PrivateFixup : 1; // 21 BitPosition
- /*0x000*/ ULONG32 HotPatchAllowed : 1; // 22 BitPosition
- }MM_SHARED_VAD_FLAGS, *PMM_SHARED_VAD_FLAGS;
- typedef struct _MMVAD_FLAGS2 // 7 elements, 0x4 bytes (sizeof)
- {
- /*0x000*/ ULONG32 FileOffset : 24; // 0 BitPosition
- /*0x000*/ ULONG32 Large : 1; // 24 BitPosition
- /*0x000*/ ULONG32 TrimBehind : 1; // 25 BitPosition
- /*0x000*/ ULONG32 Inherit : 1; // 26 BitPosition
- /*0x000*/ ULONG32 NoValidationNeeded : 1; // 27 BitPosition
- /*0x000*/ ULONG32 PrivateDemandZero : 1; // 28 BitPosition
- /*0x000*/ ULONG32 Spare : 3; // 29 BitPosition
- }MMVAD_FLAGS2, *PMMVAD_FLAGS2;
- typedef struct _MMVAD_SHORT
- {
- RTL_BALANCED_NODE VadNode;
- UINT32 StartingVpn; /*0x18*/
- UINT32 EndingVpn; /*0x01C*/
- UCHAR StartingVpnHigh;
- UCHAR EndingVpnHigh;
- UCHAR CommitChargeHigh;
- UCHAR SpareNT64VadUChar;
- INT32 ReferenceCount;
- EX_PUSH_LOCK PushLock; /*0x028*/
- struct
- {
- union
- {
- ULONG_PTR flag;
- MM_PRIVATE_VAD_FLAGS PrivateVadFlags; /*0x030*/
- MMVAD_FLAGS VadFlags;
- MM_GRAPHICS_VAD_FLAGS GraphicsVadFlags;
- MM_SHARED_VAD_FLAGS SharedVadFlags;
- }Flags;
- }u1;
- PVOID EventList; /*0x038*/
- }MMVAD_SHORT, *PMMVAD_SHORT;
- typedef struct _MMADDRESS_NODE
- {
- ULONG64 u1;
- struct _MMADDRESS_NODE* LeftChild;
- struct _MMADDRESS_NODE* RightChild;
- ULONG64 StartingVpn;
- ULONG64 EndingVpn;
- }MMADDRESS_NODE, *PMMADDRESS_NODE;
- typedef struct _MMEXTEND_INFO // 2 elements, 0x10 bytes (sizeof)
- {
- /*0x000*/ UINT64 CommittedSize;
- /*0x008*/ ULONG32 ReferenceCount;
- /*0x00C*/ UINT8 _PADDING0_[0x4];
- }MMEXTEND_INFO, *PMMEXTEND_INFO;
- struct _SEGMENT
- {
- struct _CONTROL_AREA* ControlArea;
- ULONG TotalNumberOfPtes;
- ULONG SegmentFlags;
- ULONG64 NumberOfCommittedPages;
- ULONG64 SizeOfSegment;
- union
- {
- struct _MMEXTEND_INFO* ExtendInfo;
- void* BasedAddress;
- }u;
- ULONG64 SegmentLock;
- ULONG64 u1;
- ULONG64 u2;
- PVOID* PrototypePte;
- ULONGLONG ThePtes[0x1];
- };
- typedef struct _EX_FAST_REF
- {
- union
- {
- PVOID Object;
- ULONG_PTR RefCnt : 3;
- ULONG_PTR Value;
- };
- } EX_FAST_REF, *PEX_FAST_REF;
- typedef struct _CONTROL_AREA // 17 elements, 0x80 bytes (sizeof)
- {
- /*0x000*/ struct _SEGMENT* Segment;
- union // 2 elements, 0x10 bytes (sizeof)
- {
- /*0x008*/ struct _LIST_ENTRY ListHead; // 2 elements, 0x10 bytes (sizeof)
- /*0x008*/ VOID* AweContext;
- };
- /*0x018*/ UINT64 NumberOfSectionReferences;
- /*0x020*/ UINT64 NumberOfPfnReferences;
- /*0x028*/ UINT64 NumberOfMappedViews;
- /*0x030*/ UINT64 NumberOfUserReferences;
- /*0x038*/ ULONG32 u; // 2 elements, 0x4 bytes (sizeof)
- /*0x03C*/ ULONG32 u1; // 2 elements, 0x4 bytes (sizeof)
- /*0x040*/ struct _EX_FAST_REF FilePointer; // 3 elements, 0x8 bytes (sizeof)
- // 4 elements, 0x8 bytes (sizeof)
- }CONTROL_AREA, *PCONTROL_AREA;
- typedef struct _SUBSECTION_
- {
- struct _CONTROL_AREA* ControlArea;
- }SUBSECTION, *PSUBSECTION;
- typedef struct _MMVAD
- {
- MMVAD_SHORT Core;
- union /*0x040*/
- {
- UINT32 LongFlags2;
- //现在用不到省略
- MMVAD_FLAGS2 VadFlags2;
- }u2;
- PSUBSECTION Subsection; /*0x048*/
- PVOID FirstPrototypePte; /*0x050*/
- PVOID LastContiguousPte; /*0x058*/
- LIST_ENTRY ViewLinks; /*0x060*/
- PEPROCESS VadsProcess; /*0x070*/
- PVOID u4; /*0x078*/
- PVOID FileObject; /*0x080*/
- }MMVAD, *PMMVAD;
- typedef struct _RTL_AVL_TREE // 1 elements, 0x8 bytes (sizeof)
- {
- /*0x000*/ struct _RTL_BALANCED_NODE* Root;
- }RTL_AVL_TREE, *PRTL_AVL_TREE;
- typedef struct _VAD_INFO_
- {
- ULONG_PTR pVad;
- ULONG_PTR startVpn;
- ULONG_PTR endVpn;
- ULONG_PTR pFileObject;
- ULONG_PTR flags;
- }VAD_INFO, *PVAD_INFO;
- typedef struct _ALL_VADS_
- {
- ULONG nCnt;
- VAD_INFO VadInfos[1];
- }ALL_VADS, *PALL_VADS;
- typedef struct _MMSECTION_FLAGS // 27 elements, 0x4 bytes (sizeof)
- {
- /*0x000*/ UINT32 BeingDeleted : 1; // 0 BitPosition
- /*0x000*/ UINT32 BeingCreated : 1; // 1 BitPosition
- /*0x000*/ UINT32 BeingPurged : 1; // 2 BitPosition
- /*0x000*/ UINT32 NoModifiedWriting : 1; // 3 BitPosition
- /*0x000*/ UINT32 FailAllIo : 1; // 4 BitPosition
- /*0x000*/ UINT32 Image : 1; // 5 BitPosition
- /*0x000*/ UINT32 Based : 1; // 6 BitPosition
- /*0x000*/ UINT32 File : 1; // 7 BitPosition
- /*0x000*/ UINT32 AttemptingDelete : 1; // 8 BitPosition
- /*0x000*/ UINT32 PrefetchCreated : 1; // 9 BitPosition
- /*0x000*/ UINT32 PhysicalMemory : 1; // 10 BitPosition
- /*0x000*/ UINT32 ImageControlAreaOnRemovableMedia : 1; // 11 BitPosition
- /*0x000*/ UINT32 Reserve : 1; // 12 BitPosition
- /*0x000*/ UINT32 Commit : 1; // 13 BitPosition
- /*0x000*/ UINT32 NoChange : 1; // 14 BitPosition
- /*0x000*/ UINT32 WasPurged : 1; // 15 BitPosition
- /*0x000*/ UINT32 UserReference : 1; // 16 BitPosition
- /*0x000*/ UINT32 GlobalMemory : 1; // 17 BitPosition
- /*0x000*/ UINT32 DeleteOnClose : 1; // 18 BitPosition
- /*0x000*/ UINT32 FilePointerNull : 1; // 19 BitPosition
- /*0x000*/ ULONG32 PreferredNode : 6; // 20 BitPosition
- /*0x000*/ UINT32 GlobalOnlyPerSession : 1; // 26 BitPosition
- /*0x000*/ UINT32 UserWritable : 1; // 27 BitPosition
- /*0x000*/ UINT32 SystemVaAllocated : 1; // 28 BitPosition
- /*0x000*/ UINT32 PreferredFsCompressionBoundary : 1; // 29 BitPosition
- /*0x000*/ UINT32 UsingFileExtents : 1; // 30 BitPosition
- /*0x000*/ UINT32 PageSize64K : 1; // 31 BitPosition
- }MMSECTION_FLAGS, *PMMSECTION_FLAGS;
- typedef struct _SECTION // 9 elements, 0x40 bytes (sizeof)
- {
- /*0x000*/ struct _RTL_BALANCED_NODE SectionNode; // 6 elements, 0x18 bytes (sizeof)
- /*0x018*/ UINT64 StartingVpn;
- /*0x020*/ UINT64 EndingVpn;
- /*0x028*/ union {
- PCONTROL_AREA ControlArea;
- PVOID FileObject;
- }u1; // 4 elements, 0x8 bytes (sizeof)
- /*0x030*/ UINT64 SizeOfSection;
- /*0x038*/ union {
- ULONG32 LongFlags;
- MMSECTION_FLAGS Flags;
- }u; // 2 elements, 0x4 bytes (sizeof)
- struct // 3 elements, 0x4 bytes (sizeof)
- {
- /*0x03C*/ ULONG32 InitialPageProtection : 12; // 0 BitPosition
- /*0x03C*/ ULONG32 SessionId : 19; // 12 BitPosition
- /*0x03C*/ ULONG32 NoValidationNeeded : 1; // 31 BitPosition
- };
- }SECTION, *PSECTION;
复制代码 引入vad.h头文件,并写入如下代码,此处的eprocess_offset_VadRoot以及eprocess_offset_VadCount 则是上方得出的相对于EPROCESS结构的偏移值,每个系统都不一样,版本不同偏移值会不同。- #include "vad.h"
- #include <ntifs.h>
- // 定义VAD相对于EProcess头部偏移值
- #define eprocess_offset_VadRoot 0x658
- #define eprocess_offset_VadCount 0x668
- VOID EnumVad(PMMVAD Root, PALL_VADS pBuffer, ULONG nCnt)
- {
- if (!Root || !pBuffer || !nCnt)
- {
- return;
- }
- __try
- {
- if (nCnt > pBuffer->nCnt)
- {
- // 得到起始页与结束页
- ULONG64 endptr = (ULONG64)Root->Core.EndingVpnHigh;
- endptr = endptr << 32;
- ULONG64 startptr = (ULONG64)Root->Core.StartingVpnHigh;
- startptr = startptr << 32;
- // 得到根节点
- pBuffer->VadInfos[pBuffer->nCnt].pVad = (ULONG_PTR)Root;
- // 起始页: startingVpn * 0x1000
- pBuffer->VadInfos[pBuffer->nCnt].startVpn = (startptr | Root->Core.StartingVpn) << PAGE_SHIFT;
- // 结束页: EndVpn * 0x1000 + 0xfff
- pBuffer->VadInfos[pBuffer->nCnt].endVpn = ((endptr | Root->Core.EndingVpn) << PAGE_SHIFT) + 0xfff;
- // VAD标志 928 = Mapped 1049088 = Private ....
- pBuffer->VadInfos[pBuffer->nCnt].flags = Root->Core.u1.Flags.flag;
- // 验证节点可读性
- if (MmIsAddressValid(Root->Subsection) && MmIsAddressValid(Root->Subsection->ControlArea))
- {
- if (MmIsAddressValid((PVOID)((Root->Subsection->ControlArea->FilePointer.Value >> 4) << 4)))
- {
- pBuffer->VadInfos[pBuffer->nCnt].pFileObject = ((Root->Subsection->ControlArea->FilePointer.Value >> 4) << 4);
- }
- }
- pBuffer->nCnt++;
- }
- if (MmIsAddressValid(Root->Core.VadNode.Left))
- {
- // 递归枚举左子树
- EnumVad((PMMVAD)Root->Core.VadNode.Left, pBuffer, nCnt);
- }
- if (MmIsAddressValid(Root->Core.VadNode.Right))
- {
- // 递归枚举右子树
- EnumVad((PMMVAD)Root->Core.VadNode.Right, pBuffer, nCnt);
- }
- }
- __except (1)
- {
- }
- }
- BOOLEAN EnumProcessVad(ULONG Pid, PALL_VADS pBuffer, ULONG nCnt)
- {
- PEPROCESS Peprocess = 0;
- PRTL_AVL_TREE Table = NULL;
- PMMVAD Root = NULL;
- // 通过进程PID得到进程EProcess
- if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)Pid, &Peprocess)))
- {
- // 与偏移相加得到VAD头节点
- Table = (PRTL_AVL_TREE)((UCHAR*)Peprocess + eprocess_offset_VadRoot);
- if (!MmIsAddressValid(Table) || !eprocess_offset_VadRoot)
- {
- return FALSE;
- }
- __try
- {
- // 取出头节点
- Root = (PMMVAD)Table->Root;
- if (nCnt > pBuffer->nCnt)
- {
- // 得到起始页与结束页
- ULONG64 endptr = (ULONG64)Root->Core.EndingVpnHigh;
- endptr = endptr << 32;
- ULONG64 startptr = (ULONG64)Root->Core.StartingVpnHigh;
- startptr = startptr << 32;
- pBuffer->VadInfos[pBuffer->nCnt].pVad = (ULONG_PTR)Root;
- // 起始页: startingVpn * 0x1000
- pBuffer->VadInfos[pBuffer->nCnt].startVpn = (startptr | Root->Core.StartingVpn) << PAGE_SHIFT;
- // 结束页: EndVpn * 0x1000 + 0xfff
- pBuffer->VadInfos[pBuffer->nCnt].endVpn = (endptr | Root->Core.EndingVpn) << PAGE_SHIFT;
- pBuffer->VadInfos[pBuffer->nCnt].flags = Root->Core.u1.Flags.flag;
- if (MmIsAddressValid(Root->Subsection) && MmIsAddressValid(Root->Subsection->ControlArea))
- {
- if (MmIsAddressValid((PVOID)((Root->Subsection->ControlArea->FilePointer.Value >> 4) << 4)))
- {
- pBuffer->VadInfos[pBuffer->nCnt].pFileObject = ((Root->Subsection->ControlArea->FilePointer.Value >> 4) << 4);
- }
- }
- pBuffer->nCnt++;
- }
- // 枚举左子树
- if (Table->Root->Left)
- {
- EnumVad((MMVAD*)Table->Root->Left, pBuffer, nCnt);
- }
- // 枚举右子树
- if (Table->Root->Right)
- {
- EnumVad((MMVAD*)Table->Root->Right, pBuffer, nCnt);
- }
- }
- __finally
- {
- ObDereferenceObject(Peprocess);
- }
- }
- else
- {
- return FALSE;
- }
- return TRUE;
- }
- VOID UnDriver(PDRIVER_OBJECT driver)
- {
- DbgPrint(("Uninstall Driver Is OK \n"));
- }
- NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
- {
- DbgPrint(("hello lyshark \n"));
- typedef struct
- {
- ULONG nPid;
- ULONG nSize;
- PALL_VADS pBuffer;
- }VADProcess;
- __try
- {
- VADProcess vad = { 0 };
- vad.nPid = 4520;
- // 默认有1000个线程
- vad.nSize = sizeof(VAD_INFO) * 0x5000 + sizeof(ULONG);
- // 分配临时空间
- vad.pBuffer = (PALL_VADS)ExAllocatePool(PagedPool, vad.nSize);
- // 根据传入长度得到枚举数量
- ULONG nCount = (vad.nSize - sizeof(ULONG)) / sizeof(VAD_INFO);
- // 枚举VAD
- EnumProcessVad(vad.nPid, vad.pBuffer, nCount);
- // 输出VAD
- for (size_t i = 0; i < vad.pBuffer->nCnt; i++)
- {
- DbgPrint("StartVPN = %p | ", vad.pBuffer->VadInfos[i].startVpn);
- DbgPrint("EndVPN = %p | ", vad.pBuffer->VadInfos[i].endVpn);
- DbgPrint("PVAD = %p | ", vad.pBuffer->VadInfos[i].pVad);
- DbgPrint("Flags = %d | ", vad.pBuffer->VadInfos[i].flags);
- DbgPrint("pFileObject = %p \n", vad.pBuffer->VadInfos[i].pFileObject);
- }
- }
- __except (1)
- {
- }
- Driver->DriverUnload = UnDriver;
- return STATUS_SUCCESS;
- }
复制代码 程序运行后输出效果如下:
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |