常用稳定的apiHook源码

//=============================================================================
// 程序: apiHook.c
// 作者: luoluo [luoluonet@hotmail.com]
// 参考: 《挂钩Windows API》SOBEIT翻译
//  《通用Shellcode深入剖析》yellow文
// 说明: 通过修改IAT来进行API Hook的例子,没有加载dll,直接跳转到我
//       远程注入的code中去,如果你要写功能复杂的程序,还是写dll方便。
// 环境: windows 2000/xp/2003
//=============================================================================
#include <Windows.h>
#include <Tlhelp32.h>
#include <Imagehlp.h>

#pragma comment(lib, "advapi32.lib")

#define THREADSIZE 1024 * 4

//———–数据结构定义开始————–//
//
// 远程线程函数参数
//
typedef struct _RemotePara
{
 //
 // 一些函数的地址
 //
 DWORD dwMessageBox;
 DWORD dwGetModuleHandle;
 DWORD dwGetProcAddress;
 DWORD dwLoadLibrary;
 DWORD dwVirtualQuery;
 DWORD dwVirtualProtect;
 DWORD dwGetWindowText;
 DWORD dwZeroMemory;
 DWORD dw_stricmp;

 char  lpLibName[32];   // 要挂钩的模块
 char  lpHookApiName[32];  // 要挂钩的API函数
 char  lpImagehlp[32];   // Imagehlp.dll名称
 char  lpImageDirectoryEntryToData[32]; // 函数名称
 char  lpKernel32[32];   // Kernel32.dll模块名称
} RemotePara, *PRemotePara;
//———–数据结构定义结束————–//

//———–函数原型定义开始————//
DWORD WINAPI GetProcessIdByName(LPCTSTR lpProcessName);
BOOL WINAPI ThreadInject(DWORD pId);
BOOL WINAPI CreateParameter(PRemotePara pRemotePara);
//———–函数原型定义结束————//

//
// 远程线程函数
//
DWORD WINAPI ThreadProc(PRemotePara lpPara)
{
 typedef int (__stdcall *PMessageBox)(HWND, LPCTSTR, LPCTSTR, UINT);
 typedef HMODULE (__stdcall *PGetModuleHandle)(LPCTSTR);
 typedef FARPROC (__stdcall *PGetProcAddress)(HMODULE, LPCSTR);
 typedef HMODULE (__stdcall *PLoadLibrary)(LPCTSTR);
 typedef SIZE_T (__stdcall *PVirtualQuery)(LPCVOID, PMEMORY_BASIC_INFORMATION, SIZE_T);
 typedef BOOL (__stdcall *PVirtualProtect)(LPVOID, SIZE_T, DWORD, PDWORD);
 typedef PVOID (__stdcall *PImageDirectoryEntryToData)(PVOID, BOOLEAN, USHORT, PULONG);
 typedef VOID (__stdcall *PZeroMemory)(LPVOID, SIZE_T);
 typedef int (__cdecl *P_stricmp)(const char *, const char *);

 PMessageBox   pfnMessageBox = (PMessageBox)lpPara->dwMessageBox;
 PGetModuleHandle  pfnGetModuleHandle = (PGetModuleHandle)lpPara->dwGetModuleHandle;
 PGetProcAddress   pfnGetProcAddress = (PGetProcAddress)lpPara->dwGetProcAddress;
 PLoadLibrary   pfnLoadLibrary = (PLoadLibrary)lpPara->dwLoadLibrary;
 PVirtualQuery   pfnVirtualQuery = (PVirtualQuery)lpPara->dwVirtualQuery;
 PVirtualProtect   pfnVirtualProtect = (PVirtualProtect)lpPara->dwVirtualProtect;
 PZeroMemory   pfnZeroMemory = (PZeroMemory)lpPara->dwZeroMemory;
 P_stricmp   pfn_stricmp = (P_stricmp)lpPara->dw_stricmp;
 DWORD    pfnHookApiAddr;
 PImageDirectoryEntryToData pfnImageDirectoryEntryToData;

 DWORD    pfnHookAPIAddr;
 DWORD    pfnMyHookCode;
 HMODULE    hModule;
 PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
 PIMAGE_THUNK_DATA  pThunk;
 ULONG    ulSize;
 PSTR    pLibName;
 MEMORY_BASIC_INFORMATION mbi;
 PDWORD    pdwfn;
 DWORD    dwOldProtect;
 DWORD    dwLibBaseAddr;

 //
 // 调试代码
 //
/*
 __asm
 {
  push ecx
  call _lpMsg
  _emit ’H’
  _emit ’e’
  _emit ’l’
  _emit ’l’
  _emit ’o’
  _emit 0×00
 _lpMsg:
  pop ecx
  push 0×00
  push ecx
  push ecx
  push 0×00
  call pfnMessageBox
  pop ecx
 }
*/

 __asm
 {
  push ecx
  mov eax, 0×12345678  // 设置是否在本线程的标识
  call _next1
 _next1:
  pop ecx   // 取得当前的EIP
  add ecx, 0×07  // 跳3个指令
  mov pfnMyHookCode, ecx // 挂钩代码地址
 _hookCodeStart:
  cmp eax, 0×12345678  // 检查标识
  jz _hookCodeEnd  // 如果在本线程,则跳过挂钩代码

  push ebp
  mov ebp, esp
  sub esp, 0×20  // 开辟局部存储区,存放局部变量

//
// Start
// 这段利用SEH取得kernel32.dll映像基址的程序,拷贝自yellow的《通用shellcode的深入剖析》一文
/
  push esi
  push ecx
  mov esi, fs:0
  lodsd
 GetExeceptionFilter:
  cmp [eax], 0xffffffff
  je GetedExeceptionFilter // 如果到达最后一个节点(它的pfnHandler指向UnhandledExceptionFilter)
  mov eax, [eax]  // 否则往后遍历,一直到最后一个节点
  jmp GetExeceptionFilter
 GetedExeceptionFilter:
  mov eax, [eax+4]
 FindMZ:
  and eax, 0xffff0000  // 根据PE执行文件以64k对界的特征加快查找速度
  cmp word ptr [eax], ‘ZM’ // 根据PE可执行文件特征查找KERNEL32.DLL的基址
  jne MoveUp   // 如果当前地址不符全MZ头部特征,则向上查找
  mov ecx, [eax+0x3c]
  add ecx, eax
  cmp word ptr [ecx], ‘EP’ // 根据PE可执行文件特征查找KERNEL32.DLL的基址
  je Found   // 如果符合MZ及PE头部特征,则认为已经找到,并通过Eax返回给调用者
 MoveUp:
  dec eax   // 准备指向下一个界起始地址
  jmp FindMZ
 Found:
  mov ecx, eax  // 保存Kernel32.dll基址
/
//
// End
//
  mov eax, dword ptr [eax + 0x3c]  // PE头偏移
  add ecx, eax
  mov dword ptr [esp + 0x04], ecx  // 保存PE头地址
  mov ecx, [ecx - 0x05]  // 取得MessageBoxA地址
  mov byte ptr [ebp - 0x0c], ‘H’
  mov byte ptr [ebp - 0x0b], ‘o’
  mov byte ptr [ebp - 0x0a], ‘o’
  mov byte ptr [ebp - 0x09], ‘k’
  mov byte ptr [ebp - 0x08], ‘S’
  mov byte ptr [ebp - 0x07], ‘u’
  mov byte ptr [ebp - 0x06], ‘c’
  mov byte ptr [ebp - 0x05], ‘c’
  mov byte ptr [ebp - 0x04], ‘e’
  mov byte ptr [ebp - 0x03], ’s’
  mov byte ptr [ebp - 0x02], ’s’
  mov byte ptr [ebp - 0x01], 0×00
  lea eax, [ebp - 0x0c]
  push 0    // MB_OK
  push eax    // "HookSuccess"
  push eax    // "HookSuccess"
  push 0    // NULL
  call ecx    // 调用MessageBoxA
  pop ecx
  pop esi
  add esp, 0×20   // 恢复堆栈
  pop ebp    // 恢复堆栈
  pop eax    // 寄存EIP
  add esp, 0×10   // 恢复堆栈
  push eax    // 压入EIP
  mov eax, 0×00   // 设置返回值
  ret


_hookCodeEnd:
  pop ecx
 }

 //
 // 取得要挂钩的API的地址
 //
 hModule = pfnGetModuleHandle(lpPara->lpLibName);
 pfnHookApiAddr = (DWORD)pfnGetProcAddress(hModule, lpPara->lpHookApiName);

 //
 // 载入Imagehlp.dll,并取得相关函数地址
 //
 hModule = pfnLoadLibrary(lpPara->lpImagehlp);
 pfnImageDirectoryEntryToData = (PImageDirectoryEntryToData)pfnGetProcAddress(hModule, lpPara->lpImageDirectoryEntryToData);

 //
 // 取得改进程执行体映象的IAT地址
 //
 hModule = pfnGetModuleHandle(NULL);
 pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)pfnImageDirectoryEntryToData(hModule, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);

 //
 // 遍历IMAGE_IMPORT_DESCRIPTOR结构
 //
 while (pImportDesc->Name)
 {
  // 取得模块名称
  pLibName = (PSTR)((DWORD)hModule + pImportDesc->Name);
  // 对比模块名称,如果是要挂钩的API所在的模块则跳出
  if (pfn_stricmp(pLibName, lpPara->lpLibName) == 0)
   break;
  pImportDesc ++;
 }

 // 取得要挂钩模块的API函数地址数组
 pThunk = (PIMAGE_THUNK_DATA)((DWORD)hModule + pImportDesc->FirstThunk);

 //
 // 遍历该数组
 //
 while (pThunk->u1.Function)
 {
  // 取得函数地址
  pdwfn = (PDWORD)&pThunk->u1.Function;

  // 对比函数地址,看是否是要挂钩的API函数地址
  if ((*pdwfn) == pfnHookApiAddr)
  {
   //
   // 修改函数入口地址
   //
   pfnZeroMemory(&mbi, sizeof(MEMORY_BASIC_INFORMATION));
   pfnVirtualQuery((LPCVOID)pdwfn, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
   pfnVirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_READWRITE, &mbi.Protect);
   *pdwfn = pfnMyHookCode;
   pfnVirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &dwOldProtect);

   //
   // 保存MessageBoxA地址到Kernel32的PE头地址 – 5,供Hook Code调用,偶想的笨方法^_^
   //
   dwLibBaseAddr = (DWORD)pfnGetModuleHandle(lpPara->lpKernel32);
   // 取得Kernel32映象PE头地址
   dwLibBaseAddr += *(PDWORD)(dwLibBaseAddr + 0×3c);
   pfnZeroMemory(&mbi, sizeof(MEMORY_BASIC_INFORMATION));
   pfnVirtualQuery((LPCVOID)(dwLibBaseAddr – 5), &mbi, sizeof(MEMORY_BASIC_INFORMATION));
   pfnVirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_READWRITE, &mbi.Protect);
   *(PDWORD)(dwLibBaseAddr – 5) = (DWORD)pfnMessageBox;
   pfnVirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &dwOldProtect);

   break;
  }
  pThunk ++;
 }

FreeAndExit:
 return 0;
}

//
// Start of WinMain
//
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow)
{
 DWORD   pId;
 OSVERSIONINFOEX  osvi;
 BOOL   bRet;
 TCHAR   procName[] = TEXT("notepad.exe");

 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

 //
 // Get system version
 //
 bRet = GetVersionEx((OSVERSIONINFO *)&osvi);
 if (! bRet)
 {
  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  bRet = GetVersionEx((OSVERSIONINFO *)&osvi);
  if (! bRet)
   goto FreeAndExit;
 }

 // Verify if it is NT system
 if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
 {
  pId = GetProcessIdByName(procName);
  if (pId != 0)
   ThreadInject(pId);
 }

FreeAndExit:
 return 0;

}
//
// End of WinMain
//

//
// @Name:  GetProcessIdByName
// @Author:  luoluo
// @Time:  2005-04-17
// @Param:  lpProcessName spacifies the ProcessName
// @Ret:  if success, return the process id
//    if failed, return 0
//  
DWORD WINAPI GetProcessIdByName(LPCTSTR lpProcessName)
{
 HANDLE   hSnapshot;
 DWORD   dwRet = 0;
 LPPROCESSENTRY32 pPe32;
 BOOL   bRet;

 // Get all the processes in the snapshot 
 hSnapshot = CreateToolhelp32Snapshot(0×00000002, 0);
 if (hSnapshot == INVALID_HANDLE_VALUE)
 {
  goto FreeAndExit;
 }

 pPe32 = (LPPROCESSENTRY32)malloc(sizeof(PROCESSENTRY32));
 ZeroMemory(pPe32, sizeof(PROCESSENTRY32));
 pPe32->dwSize = sizeof(PROCESSENTRY32);

 // Get the first process
 bRet = Process32First(hSnapshot, pPe32);
 if (! bRet)
 {
  goto FreeAndExit;
 }

 if (stricmp(lpProcessName, pPe32->szExeFile) == 0)
 {
  dwRet = pPe32->th32ProcessID;
  goto FreeAndExit;
 }

 // Travesal the left processes
 while (TRUE)
 {
  bRet = Process32Next(hSnapshot, pPe32);
  if (! bRet)
  {
   goto FreeAndExit;
  }

  if (stricmp(lpProcessName, pPe32->szExeFile) == 0)
  {
   dwRet = pPe32->th32ProcessID;
   goto FreeAndExit;
  }
 }

FreeAndExit:
 if (pPe32 != NULL) free(pPe32);
 if (hSnapshot != NULL) CloseHandle(hSnapshot);

 return dwRet;
}

//
// @Name:  ThreadInject
// @Author:  luoluo
// @Time:  2005-04-17
// @Param:  pid spacifies the pid of the process to be thread injected
// @Ret:  if success return TRUE else return FALSE
//
BOOL WINAPI ThreadInject(DWORD pId)

 HANDLE    hProcess;
 LPVOID    lpCodeMemory;
 BOOL    bRet = FALSE;
 BOOL    bRetVal;
 RemotePara   myRemotePara;
 PRemotePara   pRemotePara;
 HANDLE    hThread;
 DWORD    dwByteWrite;
 HANDLE    hToken;
 TOKEN_PRIVILEGES  tkp;
 MEMORY_BASIC_INFORMATION mbi;
 SIZE_T    szRet;
 DWORD    dwOldProtect;

 // Open process token to ajust privileges
 bRetVal = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);

 if (! bRetVal)
 {
  goto FreeAndExit;
 }

 // Get the LUID for debug privilege
 bRetVal = LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid);

 if (! bRetVal)
 {
  goto FreeAndExit;
 }

 tkp.PrivilegeCount = 1;
 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

 // Adjust token privileges
 bRetVal = AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(&tkp), (PTOKEN_PRIVILEGES)NULL, 0);
 if (! bRetVal)
 {
  goto FreeAndExit;
 }

 // Open remote process
 hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION, FALSE, pId);
 if (hProcess == NULL)
 {
  goto FreeAndExit;
 }

 // Allocate memory from remote process
 lpCodeMemory = VirtualAllocEx(hProcess, NULL, THREADSIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
 if (lpCodeMemory == NULL)
 {
  goto FreeAndExit;
 }

 // Query the page information
 ZeroMemory(&mbi, sizeof(MEMORY_BASIC_INFORMATION));
 szRet = VirtualQueryEx(hProcess, lpCodeMemory, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
 if (szRet == 0)
 {
  goto FreeAndExit;
 }

 // Modify the page protection for write
 bRetVal = VirtualProtectEx(hProcess, mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);
 if (! bRetVal)
 {
  goto FreeAndExit;
 }

 // Write my code to remote process memory
 bRetVal = WriteProcessMemory(hProcess, lpCodeMemory, &ThreadProc, THREADSIZE, 0);
 if (! bRetVal)
 {
  VirtualFreeEx(hProcess, lpCodeMemory, THREADSIZE, MEM_RELEASE);
  goto FreeAndExit;
 }

 // Modify the page protection to protect
 bRetVal = VirtualProtectEx(hProcess, mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &dwOldProtect);
 if (! bRetVal)
 {
  goto FreeAndExit;
 }

 // Fill in the parameter
 ZeroMemory(&myRemotePara, sizeof(RemotePara));
 CreateParameter((PRemotePara)&myRemotePara);

 // Allocate memory in the remote process to store the parameter
 pRemotePara = (PRemotePara)VirtualAllocEx(hProcess, NULL, sizeof(RemotePara), MEM_COMMIT, PAGE_READWRITE);
 if (pRemotePara == NULL)
 {
  VirtualFreeEx(hProcess, lpCodeMemory, THREADSIZE, MEM_RELEASE);
  goto FreeAndExit;
 }

 // Query page information
 ZeroMemory(&mbi, sizeof(MEMORY_BASIC_INFORMATION));
 szRet = VirtualQueryEx(hProcess, pRemotePara, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
 if (szRet == 0)
  goto FreeAndExit;

 // Modify page protection for write
 bRetVal = VirtualProtectEx(hProcess, mbi.BaseAddress, mbi.RegionSize, PAGE_READWRITE, &mbi.Protect);
 if (! bRetVal)
  goto FreeAndExit;

 // Write para to the remote process’s memory
 bRetVal = WriteProcessMemory(hProcess, pRemotePara, &myRemotePara, sizeof(myRemotePara), 0);
 if (! bRetVal)
 {
  VirtualFreeEx(hProcess, lpCodeMemory, THREADSIZE, MEM_RELEASE);
  VirtualFreeEx(hProcess, pRemotePara, sizeof(RemotePara), MEM_RELEASE);
  goto FreeAndExit;
 }

 // Modify page protection to protect
 bRetVal = VirtualProtectEx(hProcess, mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &dwOldProtect);
 if (! bRetVal)
  goto FreeAndExit;

 // Create remote thread
 hThread = CreateRemoteThread(hProcess, 0, 0, lpCodeMemory, pRemotePara, 0, &dwByteWrite);
 if (hThread == NULL)
 {
  VirtualFreeEx(hProcess, lpCodeMemory, THREADSIZE, MEM_RELEASE);
  VirtualFreeEx(hProcess, pRemotePara, sizeof(RemotePara), MEM_RELEASE);
  goto FreeAndExit;
 }

 bRet = TRUE;

FreeAndExit:
 if (hProcess != NULL) CloseHandle(hProcess);
 if (hToken != NULL) CloseHandle(hToken);

 return bRet;
}

//
// @Name:  CreateParameter
// @Author:  luoluo
// @Time:  2005-04-17
// @Param:  pRemotePara will be filled with data
// @Ret:  if success return TRUE else return FALSE
//
BOOL WINAPI CreateParameter(PRemotePara pRemotePara)
{
 HMODULE hModule;
 BOOL bRet;

 bRet = TRUE;

 hModule = LoadLibrary("Kernel32.dll");
 pRemotePara->dwGetModuleHandle = (DWORD)GetProcAddress(hModule, "GetModuleHandleA");
 pRemotePara->dwGetProcAddress = (DWORD)GetProcAddress(hModule, "GetProcAddress");
 pRemotePara->dwLoadLibrary = (DWORD)GetProcAddress(hModule, "LoadLibraryA");
 pRemotePara->dwVirtualQuery = (DWORD)GetProcAddress(hModule, "VirtualQuery");
 pRemotePara->dwVirtualProtect = (DWORD)GetProcAddress(hModule, "VirtualProtect");

 hModule = LoadLibrary("User32.dll");
 pRemotePara->dwMessageBox = (DWORD)GetProcAddress(hModule, "MessageBoxA");

 hModule = LoadLibrary("Ntdll.dll");
 pRemotePara->dwZeroMemory = (DWORD)GetProcAddress(hModule, "RtlZeroMemory");

 hModule = LoadLibrary("Msvcrt.dll");
 pRemotePara->dw_stricmp = (DWORD)GetProcAddress(hModule, "_stricmp");

 strcpy(pRemotePara->lpLibName, "User32.dll/0");
 strcpy(pRemotePara->lpHookApiName, "MessageBoxW/0");
 strcpy(pRemotePara->lpImagehlp, "Imagehlp.dll/0");
 strcpy(pRemotePara->lpImageDirectoryEntryToData, "ImageDirectoryEntryToData/0");
 strcpy(pRemotePara->lpKernel32, "Kernel32.dll/0");

FreeAndExit:
 return bRet;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习是一种人工智能(AI)的子领域,致力于研究如何利用数据和算法让计算机系统具备学习能力,从而能够自动地完成特定任务或者改进自身性能。机器学习的核心思想是让计算机系统通过学习数据中的模式和规律来实现目标,而不需要显式地编程。 机器学习应用非常广泛,包括但不限于以下领域: 图像识别和计算机视觉: 机器学习在图像识别、目标检测、人脸识别、图像分割等方面有着广泛的应用。例如,通过深度学习技术,可以训练神经网络来识别图像中的对象、人脸或者场景,用于智能监控、自动驾驶、医学影像分析等领域。 自然语言处理: 机器学习在自然语言处理领域有着重要的应用,包括文本分类、情感分析、机器翻译、语音识别等。例如,通过深度学习模型,可以训练神经网络来理解和生成自然语言,用于智能客服、智能助手、机器翻译等场景。 推荐系统: 推荐系统利用机器学习算法分析用户的行为和偏好,为用户推荐个性化的产品或服务。例如,电商网站可以利用机器学习算法分析用户的购买历史和浏览行为,向用户推荐感兴趣的商品。 预测和预测分析: 机器学习可以用于预测未来事件的发生概率或者趋势。例如,金融领域可以利用机器学习算法进行股票价格预测、信用评分、欺诈检测等。 医疗诊断和生物信息学: 机器学习在医疗诊断、药物研发、基因组学等领域有着重要的应用。例如,可以利用机器学习算法分析医学影像数据进行疾病诊断,或者利用机器学习算法分析基因数据进行疾病风险预测。 智能交通和物联网: 机器学习可以应用于智能交通系统、智能城市管理和物联网等领域。例如,可以利用机器学习算法分析交通数据优化交通流量,或者利用机器学习算法分析传感器数据监测设备状态。 以上仅是机器学习应用的一部分,随着机器学习技术的不断发展和应用场景的不断拓展,机器学习在各个领域都有着重要的应用价值,并且正在改变我们的生活和工作方式。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值