Delphi反编译实战:无源码EXE的DFM与RTTI恢复指南

发布时间:2026/10/20 14:59:56

Delphi反编译实战:无源码EXE的DFM与RTTI恢复指南 简介DELPHI反编译工具是一款面向Delphi开发者、逆向工程人员及安全分析师的实用软件专门用于解析Delphi编译生成的动态链接库与OCX控件帮助用户在缺失原始源码时理解组件逻辑、定位兼容性问题或开展恶意代码分析。资源包为RAR压缩格式体积约532KB轻量便携适合需要维护遗留系统、学习他人编程实现或验证软件安全性的中高级开发者。目前已有1003人学习下载。该工具提供符号解析、反汇编、源码重构及调试支持等核心功能能将二进制代码还原为接近源代码的结构辅助快速梳理函数、类与变量关系同时可与调试器集成支持断点与单步跟踪便于深入分析执行流程。需注意反编译结果难以完全恢复原始注释与逻辑使用时须遵守法律法规并尊重软件知识产权。1. 接手没有源码的 Delphi 程序反编译工具是唯一的救命稻草某公司交接来一套老旧的进销存系统交付物只有一个编译好的 EXE源码早被前外包团队带走。上线日期迫在眉睫客户的增改需求清单却有十几条。这种情况下DELPHI 反编译工具就成了唯一能落地的技术路线。Delphi 程序编译后是原生 x86 机器码反编译并不是变魔术不可能把 EXE 还原成一份完整的 .pas 源码但一套成熟的反编译工具链能恢复出三样关键资产窗体布局 DFM、组件与类元数据 RTTI、事件处理函数的地址映射。有了这三样再配合反汇编器读机器码足够支撑改功能、修缺陷、做兼容性分析。这套思路适合三类人接手遗留 Delphi 系统但源码缺失的维护工程师需要做软件兼容性适配的逆向分析人员以及做遗留系统安全审计的技术人员。如果你手头的 Delphi 程序还能找到源码直接编译调试即可不必走反编译这条路。下文以一次完整的模拟项目 X 分析过程为主线讲清楚工具怎么选、流程怎么走、参数怎么调、五个高频坑在哪。2. 识别 Delphi 程序和准备反编译环境从 PE 特征到工具链搭配2.1 先确认这是 Delphi 程序PE 头、RTTI 与 VCL 特征反编译之前的第一步不是开工具而是确认目标确实是用 Delphi 写的。常见做法是用 PE 查看工具打开 EXE检查导入表。Delphi 程序几乎必然导入 VCL 相关的运行时库经典版本如 Delphi 5 / 6 / 7 编译出的程序导入表里会出现 VCL50、VCL60、VCL70较新的 Delphi XE 系列则常见 VCL100、VCL120 或 rtl 系列库。另外一个高置信度特征是 RTTIDelphi 默认会为每个窗体类生成完整的运行时类型信息PE 文件里能观察到名为RTTI的段或特定模式的元数据。字符串扫描也是快速验证手段。用任意十六进制编辑器或反汇编器的字符串搜索功能直接搜TForm、TButton、TLabel这类 VCL 类名。如果能在可执行文件的只读数据区看到这些类名字符串基本可以确认目标使用 VCL 库。作为参考我把常用判断特征整理成表方便对照特征来源常见标记置信度导入表VCL50 / VCL60 / VCL70 / rtl 系列高资源段RT_RCDATA 下存在数字编号的 DFM 资源高字符串区TForm、TButton、TLabel 类名高指令特征call 大偏移地址的 VCL 方法调用模式中操作上我习惯先打开 Resource Hacker 一类的资源查看工具看资源段。如果资源列表里出现一连串数字编号的 RCDATA 条目就可以认定这些就是 DFM 编译后的二进制窗体资源。每个窗体对应一条资源资源名是窗体的资源 ID而不是窗体的类名这一点先记住后面坑会出现在这里。2.2 反编译工具链怎么搭分类、选型与版本注意事项DELPHI 反编译不是一个工具干到底而是三层工具链配合。第一层是资源提取工具负责把 EXE 里的 DFM 窗体资源、图标、版本信息完整抠出来第二层是专门面向 Delphi 的反编译工具能够解析 DFM 二进制、读取 RTTI、还原类结构有的还能把事件处理函数的名称映射到代码地址这是整套工具链的灵魂第三层是通用反汇编器负责把函数地址上的机器码还原成可读汇编并做交叉引用分析。工具选型有一条血泪经验专门面向 Delphi 的反编译工具大多停留在某个历史版本区间对 Delphi 5 到 7 的产物支持得最成熟。如果目标是较新的 Delphi XE 或更高版本编译的程序专门工具经常解析失败或输出错乱。此时不要死磕一个工具正确做法是准备一个多版本并存的虚拟机环境让资源提取和结构解析用不同工具取各自完整的那份结果拼接使用。反汇编器选 Ghidra 或 IDA 都可以关键是能导入专门工具导出的符号映射文件。版本问题还有一个容易被忽略的细节DFM 资源本身分旧版二进制格式和后来带压缩变体的格式不同 Delphi 版本对 DFM 的序列化规则有微妙差异。旧工具遇到新格式会反序列化失败或字段丢失。因此环境里最好保留两代以上的资源提取工具并先用一个最小测试程序验证工具输出是否合理再对目标动刀。2.3 用 PE 工具提取基线信息入口点、资源段与导入表环境备好后正式分析的第一步是把目标 EXE 的基线信息抓全。用 Resource Hacker 切换到资源视图记录每个 RCDATA 资源的名称和大小用反汇编器加载 EXE记录入口点地址、映像基址和段布局。这些信息是后续地址换算的基准。映像基址尤其重要如果 EXE 开了 ASLR每次加载地址都变反编译工具输出的地址必须减去重定位偏移才能对齐。我一般会在这一步顺手做一个 Python 脚本遍历 PE 文件的资源目录把 RCDATA 条目连同偏移量导出成清单。脚本思路是对齐到文件偏移后按资源目录结构逐级解析参考代码如下import struct def walk_resources(data, rva_base, entry, depth0): # 遍历 PE 资源目录树打印 RCDATA 类型的资源条目 name_off entry 0x80000000 offset entry 0x7fffffff # 目录项落在资源段内跳过未对齐的脏数据 if offset 16 len(data): return count struct.unpack_from(H, data, offset 12)[0] \ struct.unpack_from(H, data, offset 14)[0] for i in range(count): item_off offset 16 i * 8 name_id, child_off struct.unpack_from(II, data, item_off) if child_off 0x80000000: walk_resources(data, rva_base, child_off, depth 1) elif depth 2: print(fRCDATA resource id{name_id}, data_rva{child_off}) with open(target.exe, rb) as f: blob f.read() # 假设 pe_offsets 已解析出资源段 RVA 和文件偏移 # walk_resources(blob, res_rva, res_root_dir_rva)这个脚本只是资源遍历的骨架实际使用时要先解析出资源段的 RVA 与文件偏移差值再把资源目录项的 RVA 换算成文件偏移才能正确读取。参数上的关键点是child_off的最高位表示该节点是否还有子目录depth 2才命中叶子数据节点。用 Resource Hacker 直接看结果更省事脚本的价值在于批量导出清单和后续自动化。3. 用专门工具恢复 Delphi 结构DFM、RTTI 与事件处理函数的还原流程3.1 DFM 资源提取把窗体二进制转成可读文本拿到 RCDATA 资源清单后下一步是把 DFM 二进制转成人能读懂的形式。专门的反编译工具一般都内置 DFM 解析器但为了不被工具的黑匣子误导理解底层格式是值得的。DFM 的序列化核心是一条属性流从资源开头解析对象声明每个对象先是类名和名称字符串随后跟随一系列属性对每个属性对包含属性名、类型标记和值。属性值的编码规则随类型不同而不同整数是四字节小端字符串是长度前缀加字节序列浮点数是八字节 IEEE 754。如果专门工具解析失败可以退一步自己写解析脚本。一个能读出对象头的小段逻辑如下import struct def read_dfm_objects(raw, pos): # 读取一个 DFM 对象定义返回类名、对象名和下一位置 while True: obj_flags, struct.unpack_from(I, raw, pos) pos 4 if obj_flags 0: break class_name raw[pos:pos raw[pos]].decode(latin1) pos 1 raw[pos] obj_name_len raw[pos] pos 1 obj_name raw[pos:pos obj_name_len].decode(latin1) pos obj_name_len # 后面紧跟属性定义遇到 0x00 0x00 结束 return class_name, obj_name, pos with open(form.dfm, rb) as f: buf f.read() cls, name, next_pos read_dfm_objects(buf, 0) print(cls, name)这段代码的关键在对象头的边界计算类名字符串是一个字节长度前缀加字节序列对象名同样如此。遇到立即数 0 表示对象定义开始属性流里的每个属性名后再跟一个字节的类型标记类型标记决定后面值的宽度。解析时最容易踩的坑是把长度前缀和名称字节数算错一位整个属性流立即乱掉。DFM 转文本还有一种更省事的路径Delphi IDE 自带一个转换小工具能直接处理二进制 DFM 到文本 DFM 的转换。许多反编译工具也直接支持输出文本 DFM不需要自己写解析器。自己写脚本的价值在于理解结构遇到工具全军覆没时兜底。3.2 RTTI 与类信息重建类字段和方法的映射DFM 解决的是窗体长什么样RTTI 解决的是类里有什么。Delphi 的 RTTI 是编译器自动生成的元数据包含类名、父类名、属性表、方法表。专门的反编译工具会把 RTTI 解析成一份类属性清单列出每个 Published 属性及其类型。这相当于把类声明的大部分信息从机器码里捞回来。读 RTTI 时我比较关注三类信息一是类名及继承链能直接看出这个窗体类扩展了哪个基类二是属性名与类型能定位数据字段的类型设计三是发布方法表的地址这里是展开代码分析的中转站。工具导出的 RTTI 表通常以文本形式展示类似ClassName: TMainForm Ancestor: TForm PublishedProps: Left: Integer Top: Integer Caption: string OnClick: TNotifyEvent PublishedMethods: BtnSaveClick BtnCancelClick FormShow拿到这张表之后类结构的骨架基本成形。但要注意RTTI 只含 Published 区的方法Delphi 的可视组件事件回调恰好都在这一区所以对反编译来说够用。而普通私有方法不会出现在 RTTI 里只能靠后续代码分析补充。3.3 事件处理函数定位从 DFM 事件名跳转到代码地址DFM 和 RTTI 都解析完成后就到了最实用的环节定位事件处理函数的代码地址。文本 DFM 里每个组件下都有事件绑定例如OnClick BtnSaveClick而专门工具导出的地址映射表会把BtnSaveClick映射到一个代码段偏移。操作流程是先在文本 DFM 里筛出所有OnClick和OnChange绑定再对照工具生成的过程映射表把每个事件名替换成十六进制地址然后到反汇编器中转跳到那个地址。地址映射表有时按函数名排序有时按地址排序。如果工具不支持导出可以退而求其次用字符串搜索定位事件方法名的引用位置。Delphi 的 RTTI 中方法名以字符串形式驻留在反汇编器里搜BtnSaveClick字符串找到交叉引用的位置再往上找函数头也能定位到方法入口。两种定位方式互为验证。实际分析中,我都是先处理 DFM 事件表把它作为优先级清单业务核心功能百分之八十都挂在按钮和菜单的 OnClick 上这条路走通主逻辑就打开了。4. 用反汇编器补全逻辑从 Pascal 调用约定到字符串池分析4.1 Register 调用约定与 Self 指针快速区分方法和普通函数专门工具还原出的结构只是空壳函数内部的逻辑必须靠反汇编器读机器码。Delphi 默认使用 register 调用约定参数优先用寄存器传递这是分析时的最大抓手。函数参数从左到右依次放入 EAX、EDX、ECX超出三个的部分走栈。对于对象方法还有一个决定性特征Self 指针放在 EAX 中作为第一个参数传入。基于这个特征,在反汇编器里看到一个函数入口先保存 EAX 或立即对 EAX 所指向的内存做偏移访问基本可以断定这是对象方法。一个典型的方法序言长这样push ebp mov ebp, esp mov [ebp-0x4], eax ; 保存 Self mov [ebp-0x8], edx ; 保存第一个参数反汇编器加载好文件后先跳到章节 3.3 定位到的地址看开头几行是否符合这种模式。符合则确认这是方法入口接下来读访问了哪些偏移。比如反复出现[eax0x2C]之类的访存说明在读写对象字段。配合 DFM 里的组件顺序偏移 0x2C 处是哪个组件可以通过专工具导出的类布局来对照逻辑一下就立体起来。4.2 字符串池是业务逻辑的入口搜索场景关键词Delphi 程序里的字符串常量通常集中存放在只读数据段反汇编器能直接列出全部字符串。我的习惯是先导出所有字符串筛出中文或业务相关的英文关键词比如 SQL 语句片段、配置文件路径、合同编号、接口地址等。一个函数里引用了 SQL 字符串这个函数多半就是数据访问方法引用了 URL 字符串多半是网络请求逻辑。中文字符串需要注意编码。Delphi 老版本默认用系统 ANSI 代码页简体中文环境是 GBK直接按 UTF-8 搜索会一无所获。在 Ghidra 里可以给数据段字符集设置 GBK 或 GB2312IDA 里则需要修改字符串分析选项。这一条实践中常被忽略很多人搜不到中文变量就以为程序里没有其实只是编码没设对。字符串池还能辅助识别第三方组件。Delphi 社区组件库非常多第三方组件的类名和字符串特征明显。如果字符串池里出现某个组件库的注册类名说明程序依赖了它后续代码中大量调用的复杂方法可能来自组件库内部常规逻辑分析不需要深入库函数内部跳过即可。4.3 用符号映射二次加工把专门工具的输出导入反汇编器专门工具分析完成后通常能导出过程名与地址对照表接下来把这层符号映射灌进反汇编器代码可读性会有质的提升。Ghidra 支持脚本批量添加标签流程是脚本读入映射文件按地址为对应位置打上函数名标签。映射文件常见格式是文本表00405A10 BtnSaveClick 00405B24 BtnCancelClick 00405C30 FormShow 00405D48 ParseConfigLine导入脚本的逻辑思路如下import re def apply_symbols(program, mapping_path): # mapping_path 中每行: 地址 函数名 with open(mapping_path) as fp: for line in fp: parts line.split() if len(parts) ! 2: continue addr, name parts addr_int int(addr, 16) symbol program.getSymbolTable() # 使用 Ghidra API 在指定地址创建函数标签 symbol.createLabel(addr_int, name)执行时注意地址基准映射文件里的地址通常是文件偏移或静态基址而反汇编器里显示的地址如果做了重定位要把映像基址偏移量加上或减去。很多人在这一步对不上号反复转跳失败实际就是基址不一致。做完符号导入后交叉引用窗口直接显示调用者的函数名业务调用链一眼就能理清。5. DELPHI 反编译过程中的五个高频坑现象、原因与解决5.1 现象DFM 提取后组件丢失一半窗体结构不全专工具导出的文本 DFM 里只有一半组件另一半凭空消失布局完全不完整。原因是新版 Delphi 编译器会把 DFM 加密或压缩后嵌入资源旧版解析器不识别压缩标记直接跳过导致后半段组件全部丢失。解决思路是回到资源层用 Resource Hacker 取出 RCDATA 原始二进制检查前几个字节是否有压缩标记。若确认压缩先离线解压还原标准 DFM再交给解析工具处理。我通常备一个多版本工具环境逐个版本试解析以能完整输出对象列表的版本为准。5.2 现象事件函数名跳转后落在错误位置反汇编代码对不上专门工具给出BtnSaveClick 0x00405A10但跳过去看到的指令明显不是函数开头而是某条指令的中间。原因是工具输出的地址基于旧的映像基址而目标程序启用了 ASLR实际加载基址偏移了固定值另一种可能是工具解析出的地址本身不准遇到的是方法名在 RTTI 里的引用地址而不是代码入口。解决方法是先在反汇编器里确认当前映像基址手动加上差值再看。若仍对不上改用字符串交叉引用定位入口以反汇编器的计算结果为准不再信任工具地址。5.3 现象中文全部乱码业务关键词搜索不到字符串区域全是乱码搜索业务词一无所获。原因是代码页设置错误Delphi 老版本编译时字符串常量按系统 ANSI 编码存储简体中文系统下是 GBK而反汇编器默认按 ASCII 或 UTF-8 解码显示。解决路径是调整反汇编器的字符集分析选项把只读数据段改成 GBK 重新分析。Ghidra 里可以直接对数据段设置字符集IDA 里在字符串设置的默认字符集处改一下。改完之后重新跑一次字符串扫描业务关键词基本都能现身。5.4 现象反汇编代码一团乱函数边界全错专门工具标注的地址范围和分析器自动识别出的函数边界大量错位反汇编窗口里全是错误代码。原因是编译器开了优化且链接器合并了相同代码块函数与函数之间没有明显边界标志分析器的自动函数识别失效。解决方法是关闭自动分析依靠 RTTI 中的发布方法表逐个手动创建函数。先对照工具导出的方法地址清单把每个地址手动设为函数入口再让反汇编器从入口开始线性分析。手动操作量较大但准确性远高于自动识别。5.5 现象反编译工具直接崩溃拖不进 EXE 文件专门工具一加载 EXE 就无响应或报错退出连资源窗口都打不开。原因大概率是程序加了壳或经过混淆干扰了资源目录遍历。常见壳类有 UPX 和各类商业壳。解决思路是先确认加壳情况观察导入表和入口点特征UPX 壳可以直接用工具脱壳还原商业壳则要借助专门脱壳流程处理。务必注意脱壳和反编译操作只应用于你有权分析的程序。脱壳完成后重新用 PE 工具确认导入表恢复原样再走正常反编译流程。工具崩溃问题百分之八十出在这一层。6. 一个能让定位效率翻倍的技巧从 DFM 与 RTTI 交叉反推业务逻辑前面章节解决了“怎么把结构恢复出来”最后补一个实战中效率最高的操作习惯先画 DFM 组件树再对照 RTTI 方法表反推业务链路。很多分析者上手就扎进反汇编代码里逐条读指令结果陷在局部细节里。正确顺序是先铺结构再动代码。具体来说拿到工具输出的文本 DFM 后我做的第一件事是列出所有组件及事件绑定按层级关系排成树。以模拟项目 X 为例窗体根是TMainForm下面挂一个TPanel面板上是btnImportData、btnExportReport、btnSaveConfig三个按钮每个按钮都有OnClick事件。看一眼名字就能推断核心功能是数据导入、报表导出、配置保存。然后在 RTTI 方法表里找到对应方法名确认地址后进入反汇编器。此时关注点不是逐条指令而是这个方法内部引用的字符串和 API 调用。在btnImportData的事件方法里搜到字符串*.csv同时看到文件流对象的标准方法调用序列整体逻辑就可以判定为解析 CSV 文件并入库在btnSaveConfig方法里看到配置文件名和写入配置文件的类方法逻辑同样一目了然。这种从 DFM 组件命名到方法名、再到字符串依据链路的推理方式定位一段业务逻辑通常只需十几分钟比盲目通读反汇编代码快得多。有一次接一个模拟项目 X客户反馈系统保存班次排期后重新打开就丢失。我没有直接搜代码而是先看 DFM 组件树,发现保存按钮绑定的方法名是BtnSaveClick,但窗体上还有一个TMemo的OnExit事件绑到了同名方法。继续看BtnSaveClick里的字符串引用定位到它写的是本地临时路径而真正负责持久化的方法根本没挂在保存按钮上。一个结构层面的错位几分钟就找到了根源。从那以后我每次做 DELPHI 反编译都是先铺 DFM再列方法表最后才动反汇编器。这套顺序能帮你少绕很多弯路希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/20 14:59:56

AI提示词加速3D超休闲点击游戏原型开发:10个可直接复制的提示词

做 3D 超休闲点击类游戏,最大的痛点是验证想法太慢。玩法循环本身很简单:点击目标、造成伤害、拿到金币、购买升级、继续点击;3D 化之后无非是多了一层模型反馈和物理碰撞。但要把这套流程从空场景搭到能玩,传统做法下至少得折腾大…

2026/10/20 14:54:55

Context-Mode 工程模式:多状态切换与上下文管理实践

1. 从“上下文模式”说起:一个被低估的工程概念第一次看到“context-mode”这个词,很多人会以为是某个新出的框架或者库。其实不是。它更像是一种设计思路,一种在系统里管理“上下文”这件事的工程模式。你可以在前端状态管理里见到它&#x…

2026/10/20 14:54:55

AI改写文章的正确打开方式:从底层逻辑到实操模板

做内容这些年,我被问得最多的一个问题,就是AI改写出来的东西能不能直接用。问这话的朋友,多半已经自己试过几次,结果要么觉得改得很平,要么感觉跟原意差得十万八千里,要么盯着改完的稿子看了半天&#xff0…

2026/10/20 15:55:18

Win10安装Git 2.32.0完整避坑指南:PATH、SSH、GitHub配置全解析

1. 这不是“装个软件”那么简单:Win10下Git 2.32.0安装配置的真实价值你点开这个标题,大概率正卡在某个具体动作上——比如刚下载完git-2.32.0-64-bit.exe却不敢双击,比如敲完git --version回车后只看到“不是内部或外部命令”,又…

2026/10/20 15:55:18

医药仓库管理系统实战:Flask+Vue实现批次效期与自动化预警

我拿到这个标题第一反应就是,典型的课设/毕设级项目,但同时又确实能把一个医药仓库的信息化痛点给讲清楚。医药仓储管理跟普通进销存最大的区别在于,它绕不开批次、效期、GSP规范这些硬约束,药品不像螺丝钉,过期了就是…

2026/10/20 15:55:18

Scrapy核心架构与实战:从组件原理到Pipeline反爬调优

1. 与手写Requests脚本相比,Scrapy究竟解决了什么问题1.1 手写爬虫的四个痛点我最早写爬虫的时候,和绝大多数人一样,用Requests加BeautifulSoup。当时抓一个某图书网站的数据,代码大致长这样:一个函数发请求&#xff0…

2026/10/20 15:55:18

Agent Skills架构:解决编码Agent上下文溢出与复用难题

写这篇东西的起因,是我在某次大项目里把Claude Code用到了极限。代码库接近二十万行,涉及五个子项目,Agent频繁出现三类问题:上下文窗口被无关信息塞满、指令理解时好时坏、同一个功能在A模块能用换到B模块就失效。折腾了大半个月…

2026/10/20 15:55:18

ppt-master:基于Python的幻灯片工程化生成系统

1. 项目概述:这不是一个PPT模板,而是一套可编程的幻灯片生成系统 “ppt-master”这个词第一次出现在我日常刷技术社区时,是在某次跨团队协作的复盘会上。当时某高校实验室的A同学在分享课题汇报自动化方案时,随口提到:…

2026/10/20 15:50:16

为什么不能写微信/QQ消息撤回破解教程?——隐私与合规的底线

抱歉,这个主题我不能写。 “解除微信/QQ/TIM消息撤回”本质上是修改第三方商业软件的运行逻辑,绕过对方主动撤回的隐私保护机制。这类操作不仅违反软件用户协议,也涉及侵犯他人隐私权,发出来的教程既可能让读者踩法律红线&#x…

2026/10/19 18:06:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/20 1:05:37

单片机C运行时从启动到多任务:中断安全与共享资源冲突

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:05:37

GCC编译四步详解:预处理、编译、汇编、链接实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:05:37

FIW线是什么?FEP注塑线与普通氟塑料线的区别及应用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑