WinlogonHack核心DLL源码:通过GINA代理实现Windows登录流程定制

发布时间:2026/10/18 17:22:09
WinlogonHack核心DLL源码:通过GINA代理实现Windows登录流程定制 简介Windows系统的登录过程由Winlogon进程与可插拔的GINA组件协作完成。GINA作为登录引擎通过导出Wlx系列函数与Winlogon交互。基于这一机制开发者可通过代理DLL如gina_hook.dll接管原始msgina.dll的调用在不破坏系统认证的前提下插入自定义逻辑。这种技术广泛应用于工控机、自助终端和登录审计场景可实现登录公告、日志记录或界面定制。然而GINA仅存在于32位Windows 2000/XP/2003Vista之后的系统已转向Credential Provider架构。理解WinlogonHack核心DLL源码有助于深入掌握登录流程的扩展原理并为老系统的定制化登录提供工程化参考。1. WinlogonHack 核心 dll 源码先搞清楚它 Hook 的到底是什么WinlogonHack 核心 dll 源码是一套专门用来 Hook msgina.dll 的登录扩展工程它的思路不是去破坏系统文件而是利用 NT 5.x 时代的 GINA 机制把 Winlogon 默认加载的登录 DLL 替换成自己编译的代理 DLL。手上有 XP 系老机器的人比如维护工控机、自助终端、登录审计设备的开发者应该都撞过类似需求想在用户登录前后弹个公告、写一条审计日志、或者改掉登录界面行为但直接改 msgina.dll 不现实注入其他进程又容易被杀软盯上。这套源码解决的就是这个事Winlogon 仍然负责系统安全但登录界面和交互逻辑从 msgina.dll 换成了你的 gina_hook.dll。它能做什么取决于你在转发层里埋了什么。源码本身只保证一件事把原 msgina.dll 的全部 Wlx 导出函数原样代理让系统登录行为不塌同时给你留出注入点。适合需要做登录流程定制的开发者也适合想理解老 Windows 登录架构的人。需要先说清楚边界GINA 只存在于 Windows 2000/XP/2003 这类 32 位老系统Vista 之后系统换成了 Credential Provider这套源码在新系统上跑不起来。下面从架构开始拆第 4 章会专门列出我踩过的五个坑。2. Winlogon 与 GINA 的协作机制为什么改 msgina.dll 就能接管登录2.1 GINA 是 Winlogon 的一颗可插拔登录引擎Winlogon 是登录进程负责等待用户按下 CtrlAltDel、创建登录桌面、启动用户 shell、处理注销和关机。但 Winlogon 本身不实现登录界面它按注册表里的 GinaDLL 键值加载一个 DLL然后调用这个 DLL 暴露出来的 Wlx 系列函数。默认情况下这个 DLL 就是 msgina.dll负责绘制登录界面、接收密码、和 LSA 做身份验证。这里的关键在于Winlogon 和 GINA 之间是接口约定不是代码绑定。Winlogon 不关心 DLL 叫什么、内部怎么实现只要求 DLL 导出的函数名和调用约定匹配。这就是为什么注册表里把 GinaDLL 改成 gina_hook.dll 之后整个登录流程就被接管了。Winlogon 启动时只读一次这个键所以修改注册表后必须重启系统注销用户都重新加载不了登录组件。这个机制是当年微软官方留出的扩展点不是后门。很多自助终端、医疗设备、工控机都靠它做定制登录。理解这一点很重要你是在替代系统默认的登录实现而不是绕过了认证。登录、验证、解锁这些核心判断最终都会转发给原 msgina.dll代理 DLL 只是在调用链中间插了一脚。注意GINA 运行在 Winlogon 进程里写错一个指针就可能让系统停在登录前。所有实验先放在虚拟机里做别直接上物理机。2.2 必须补齐的 Wlx 导出函数清单Winlogon 会通过 GetProcAddress 按名称查找 GINA 的导出函数。缺失的函数不会在加载时报错但 Winlogon 走到对应场景时就会静默失败表现出来就是黑屏、注销卡死、关机挂起这类问题。整理一份最容易被调用的函数表导出函数Winlogon 调用时机没处理好的后果WlxNegotiateVersionDLL 加载后立即调用协商接口版本版本协商失败Winlogon 直接放弃加载WlxInitialize初始化 GINA生成会话上下文初始化失败系统无法进入登录界面WlxDisplaySASNotice显示按 CtrlAltDel 登录提示提示不显示但组合键还能用WlxLoggedOutSAS登录界面收到 SAS 事件无法进入登录流程可能黑屏WlxActivateUserShell认证通过后启动用户 shellshell 起不来桌面黑屏WlxLoggedOnSAS已登录用户触发 SAS 事件锁屏、切换用户异常WlxLogoff注销流程注销卡死或直接蓝屏WlxShutdown关机流程关机时系统挂住另外还有 WlxIsLogoffOk、WlxIsLockOk、WlxDisplayLockedNotice、WlxDisplayStatusMessage、WlxRemoveStatusMessage、WlxGetStatusMessage 这一批低频函数。完整的 msgina.dll 导出函数大约有二十个左右转发层最稳妥的做法是全量导出、全量转发不要按“我觉得用不到”来裁剪。这套源码的核心工作就是把这张表补齐。很多人拿到类似资源后只关注 WlxLoggedOutSAS 怎么注入忽略了低频函数这是后面翻车的主要来源。代理 DLL 不像普通 DLL 可以只导出需要的接口Winlogon 在哪个时机调用哪个函数是它自己决定的导出表缺了就是缺了。2.3 源码为什么用“代理 转发”而不是重写整个 GINA有人会问既然 Winlogon 只认接口为什么不直接自己写一个完整的 GINA答案很简单没必要。msgina.dll 内部包含了密码框、输入法协调、可信桌面、与 LSA 的交互逻辑这些细节极其复杂重现一遍的代价远超收益。登录界面、系统安全、用户切换这些事情都应该让原厂实现继续做代理层只做两件事把调用原样转过去在关键节点插自己的逻辑。所以这套源码的工程结构是围绕“转发”设计的。安装时把系统自带的 msgina.dll 复制一份并改名为 gina_org.dll注册表把 GinaDLL 指向新编译出来的 gina_hook.dll。gina_hook.dll 启动时用 LoadLibrary 加载 gina_org.dll再通过 GetProcAddress 拿每个 Wlx 函数的地址。自己导出的同名函数被 Winlogon 调用后内部再调原函数指针。这里有一个值得注意的设计点为什么不直接 LoadLibrary(msgina.dll)因为注册表已经改变了 Winlogon 的加载路径再按原名加载 msgina.dll 时加载器很可能会把 gina_hook.dll 自己又加载一遍形成递归。把原文件改名为 gina_org.dll是为了从加载路径上避开同名冲突。这是 GINA 代理类工程里最常见的坑后面第 4 章会专门展开。代理模式下唯一需要动脑子的地方是 pWlxContext。WlxInitialize 会返回一个上下文指针给 Winlogon后续所有 Wlx 函数都会把这个指针作为第一个参数传回来。原 msgina.dll 内部也维护着自己的上下文所以代理层要申请自己的上下文结构体把原上下文存进去再把自定义结构体指针交给 Winlogon。后续每个函数第一件事就是解包拿到原上下文。整个源码的骨架就是围绕这个解包动作展开的理解了 pWlxContext 的交换过程这套代码你就看懂了一大半。3. 编译 gina_hook.dll源码结构、编译命令与注册表挂载3.1 这个 dll 源码的典型文件组织从这套源码的常见工程组织来看一份可落地的 GINA 代理 DLL 至少需要四个部分核心转发实现、导出表定义、编译脚本、安装回滚脚本。拿到资源后可以先把目录对照一遍缺哪个补哪个文件作用gina_hook.cpp核心逻辑加载 gina_org.dll、转发 Wlx 函数、注入点gina_hook.def导出符号表必须和原 msgina.dll 的导出函数对齐build.bat编译脚本生成 gina_hook.dllinstall.cmd备份并复制原 msgina.dll 为 gina_org.dll、注册 GinaDLL 键uninstall.cmd删除注册表键、清理 gina_org.dll用于回滚我一般在拿到源码包后先做三件事看 gina_hook.def 里导出了多少个 Wlx 函数和原 msgina.dll 做差集看 WlxInitialize 里 pWlxContext 怎么包装的看 LoadLibrary 加载的文件名是不是 gina_org.dll。这三个点没问题这套源码的骨架基本就是健康的。编译环境建议用 32 位工具链目标系统是 Windows XP 或 Server 2003 的话工具链越老兼容性越稳。新版编译器编出来的 DLL 在 XP 上经常因为 CRT 版本或安全 cookie 初始化问题加载失败。资源里如果带了 build.bat先打开看它调用的环境初始化脚本路径确认指向的是本机实际安装路径。3.2 核心转发代码解读WlxInitialize 与 WlxLoggedOutSAS下面这段代码是从源码里提炼出来的转发骨架只保留最核心的链路方便看逻辑。实际的完整实现会包含更多函数但模式完全一样// gina_hook.cpp —— 转发核心骨架 #include windows.h #define GINA_ORG Lgina_org.dll typedef BOOL (WINAPI *WLX_INITIALIZE_FN)(LPWSTR, DWORD, PVOID, PVOID*); typedef BOOL (WINAPI *WLX_LOGGED_OUT_SAS_FN)(PVOID, DWORD, PVOID, PVOID, HANDLE, PDWORD); typedef VOID (WINAPI *WLX_LOGGED_ON_SAS_FN)(PVOID, DWORD, PVOID); typedef BOOL (WINAPI *WLX_ACTIVATE_USER_SHELL_FN)(PVOID, PWSTR, PWSTR, PVOID); typedef BOOL (WINAPI *WLX_LOGOFF_FN)(PVOID); typedef VOID (WINAPI *WLX_SHUTDOWN_FN)(PVOID, DWORD); static WLX_INITIALIZE_FN g_origInit; static WLX_LOGGED_OUT_SAS_FN g_origLoggedOutSas; static WLX_LOGGED_ON_SAS_FN g_origLoggedOnSas; static WLX_ACTIVATE_USER_SHELL_FN g_origActivateUserShell; static WLX_LOGOFF_FN g_origLogoff; static WLX_SHUTDOWN_FN g_origShutdown; struct HookContext { PVOID origCtx; // 原 msgina.dll 返回的上下文 DWORD callCount; // 注入点示例字段 }; static BOOL LoadOriginalGina() { HMODULE h LoadLibraryW(GINA_ORG); if (!h) return FALSE; g_origInit (WLX_INITIALIZE_FN)GetProcAddress(h, WlxInitialize); g_origLoggedOutSas (WLX_LOGGED_OUT_SAS_FN)GetProcAddress(h, WlxLoggedOutSAS); g_origLoggedOnSas (WLX_LOGGED_ON_SAS_FN)GetProcAddress(h, WlxLoggedOnSAS); g_origActivateUserShell (WLX_ACTIVATE_USER_SHELL_FN)GetProcAddress(h, WlxActivateUserShell); g_origLogoff (WLX_LOGOFF_FN)GetProcAddress(h, WlxLogoff); g_origShutdown (WLX_SHUTDOWN_FN)GetProcAddress(h, WlxShutdown); return g_origInit ! NULL; } BOOL WINAPI WlxInitialize(LPWSTR winStation, DWORD ver, PVOID res, PVOID* ctx) { if (!g_origInit) return FALSE; PVOID origCtx NULL; if (!g_origInit(winStation, ver, res, origCtx)) return FALSE; HookContext* hc (HookContext*)LocalAlloc(LPTR, sizeof(HookContext)); if (!hc) return FALSE; hc-origCtx origCtx; *ctx hc; // Winlogon 后续会把 hc 回传给我们 OutputDebugStringW(L[gina_hook] WlxInitialize OK); return TRUE; } BOOL WINAPI WlxLoggedOutSAS(PVOID ctx, DWORD sasType, PVOID res, PVOID authId, HANDLE token, PDWORD opts) { HookContext* hc (HookContext*)ctx; OutputDebugStringW(L[gina_hook] WlxLoggedOutSAS called); if (!hc || hc-origCtx NULL) return FALSE; return g_origLoggedOutSas(hc-origCtx, sasType, res, authId, token, opts); }代码的逻辑不复杂但有几个参数细节要解释清楚。LoadOriginalGina 里 GetProcAddress 找不到任何一个关键函数时WlxInitialize 会直接返回 FALSEWinlogon 会弹出“无法加载登录 DLL”的提示。这比半初始化状态安全得多GINA 这种位置最怕的就是初始化一半然后硬着头皮继续跑。pWlxContext 的交换是这套代码的灵魂。原 msgina.dll 的 WlxInitialize 会返回它的原始上下文这是它内部维护的状态。代理层不能把原始上下文直接还给 Winlogon否则后续所有转发函数都拿不到自定义的 HookContext注入点也就无从谈起。反过来转发时也不能把 Winlogon 传进来的包装上下文直接丢给原函数原函数内部会把这个指针当作它自己的结构体来访问结果就是内存越界。解包动作每个转发函数都要做一遍源码里的合法模式是先转成 HookContext*取出 origCtx再转发。WlxLoggedOutSAS 的参数里dwSasType 表示安全注意信号的类型常见的取值是 CtrlAltDel 组合键。HANDLE token 是登录令牌句柄authId 是认证标识。转发时这些参数原样传递不要做任何判断除非你确认要在这个节点改变认证流程。3.3 编译命令生成 gina_hook.dllgina_hook.def 是导出表定义内容要和原 msgina.dll 的导出函数对齐。一个最小可用的 def 文件长这样LIBRARY gina_hook.dll EXPORTS WlxNegotiateVersion WlxInitialize WlxDisplaySASNotice WlxDisplayLockedNotice WlxLoggedOutSAS WlxLoggedOnSAS WlxActivateUserShell WlxLogoff WlxShutdown WlxIsLogoffOk WlxIsLockOk这个清单不是随便写的前三行没有Winlogon 根本不会加载这个 DLL后面的函数缺失会导致注销、锁屏、关机时系统行为异常。def 文件里多写几个不存在的函数问题不大链接器会报错提醒但漏写不会报错只会等到运行时静默失效。编译直接用批处理脚本命令如下echo off call 环境初始化脚本路径\vcvars32.bat cl /nologo /LD /O1 /MT gina_hook.cpp /link /def:gina_hook.def /out:gina_hook.dll dumpbin /exports gina_hook.dll参数里 /LD 表示生成 DLL/MT 是静态链接 CRT这一步很关键。XP 系统上不一定有对应版本的 C 运行库动态链接 CRT 会让 DLL 在目标机器上加载失败静态链接把运行库打进 DLL省掉这个依赖。/O1 按体积优化GINA DLL 很小没必要用 /O2 速度优化。最后一行 dumpbin 检查导出表是编译后必做的动作。提示用较新的 Visual Studio 编译时记得把目标平台设置为 32 位并检查工程是否定义了 _WIN32_WINNT0x0501。默认的 SDK 宏会让部分 API 解析到 XP 不存在的版本编译能过运行时才炸。3.4 注册表挂载与文件部署编译出的 gina_hook.dll 要放到 system32 目录同时把原 msgina.dll 复制一份改名为 gina_org.dll。安装脚本的逻辑参考下面这段echo off rem 1. 备份原登录 DLL名改为 gina_org.dll copy /y %WINDIR%\System32\msgina.dll %WINDIR%\System32\gina_org.dll rem 2. 部署代理 DLL copy /y gina_hook.dll %WINDIR%\System32\ rem 3. 注册 Winlogon 的 GINA 键 reg add HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon /v GinaDLL /t REG_SZ /d C:\Windows\System32\gina_hook.dll /f rem 4. 确认注册结果 reg query HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon /v GinaDLL复制 msgina.dll 时需要 SYSTEM 权限普通管理员命令行可能提示拒绝访问。实际环境中我一般用 PsExec 以 SYSTEM 身份执行安装脚本或者直接在安装阶段让脚本请求提权。注意 gina_hook.dll 和 gina_org.dll 必须放在同一目录代码里 LoadLibraryW(Lgina_org.dll) 的搜索路径依赖 Winlogon 进程的工作目录也就是 system32。回滚脚本更简单但必须保证可用reg delete HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon /v GinaDLL /f del %WINDIR%\System32\gina_hook.dll del %WINDIR%\System32\gina_org.dllGinaDLL 键删除后Winlogon 会回落到默认的 msgina.dll。回滚脚本里不要顺手删原 msgina.dll它一直都在。测试阶段建议随时把回滚脚本放在桌面一旦改完重启发现登录异常先进安全模式跑回滚脚本恢复。4. 避坑与排查最容易让机器开不了机的五个细节4.1 加载阶段注册表写了但 GINA 没生效现象注册表里 GinaDLL 键值确认存在但重启后还是原登录界面gina_hook.dll 根本没有被加载。原因这个坑通常不是代码问题是注册表路径写错了。Winlogon 的注册表项是 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon很多人会手滑写到 HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Winlogon只差一个 NT 字样系统完全无视。另外Winlogon 只在启动时读取一次 GinaDLL改完注册表只注销不重启登录组件不会重新加载看起来就像没生效。解决重启前先执行 reg query HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon /v GinaDLL 确认路径和值都对然后完整重启不要只注销。远程操作时还要确认改的是目标机器而不是自己的会话这个问题在维护多台虚拟机时特别容易犯。4.2 加载阶段LoadLibrary 递归把自己又加载了一遍现象重启后黑屏或者系统提示无法加载登录 DLL连安全模式都进不去只能从故障恢复控制台才能救回来。原因源码里 LoadLibrary 加载的是 msgina.dll 而不是改名的 gina_org.dll。注册表已经让 Winlogon 不再按默认方式加载 msgina.dll加载器在解析这个字符串时很容易找到当前进程里已经加载的 gina_hook.dll因为导出函数名完全一样等于把代理 DLL 自己又加载了一遍形成递归初始化。栈和堆被反复初始化系统直接挂死。解决安装时把原 msgina.dll 复制成 gina_org.dll代码里只允许出现 gina_org.dll 这个加载名。GinaDLL 注册表值则必须指向 gina_hook.dll不能指向 gina_org.dll否则 Winlogon 加载进去的是一个没有代理逻辑的裸转发器。这个文件名分配是整个工程最容易踩的坑拿到源码后先搜一下代码里有没有 msgina.dll 字符串。4.3 登录阶段能出登录界面但登录后黑屏或闪回现象输入密码后转圈随后黑屏几十秒或者直接弹回登录界面system 事件日志里有 winlogon 相关的错误记录。原因最常见的两个点一是 WlxActivateUserShell 没有正确转发或者返回了 FALSEWinlogon 认为用户 shell 启动失败于是回退到登录状态二是 pWlxContext 解包错误某个转发函数把包装后的 ctx 直接传给原 msgina.dll原函数按它的结构体偏移访问内存轻则数据错乱重则进程崩溃。解决先做一次纯转发版本所有函数只解包和转发不插任何业务逻辑确认能完整登录一次再改代码。每个转发函数入口加 OutputDebugStringW 打印 ctx 指针和函数名用第 5 章的调试方法看调用序列先定位是哪个函数路径出的问题。纯转发版本能正常登录才能证明 context 交换逻辑是对的之后注入代码出的问题才值得排查。4.4 运行阶段登录正常但注销或关机蓝屏现象日常登录、锁屏都没问题一旦注销、切换用户或者关机系统卡在“正在注销”或者直接蓝屏。原因漏转发低频函数。登录走的高频路径是 WlxInitialize、WlxLoggedOutSAS、WlxActivateUserShell、WlxLoggedOnSAS这些函数会被频繁调用测试时很容易覆盖到。但注销走的是 WlxIsLogoffOk、WlxLogoff关机走的是 WlxShutdown锁屏走的是 WlxIsLockOk、WlxDisplayLockedNotice。这些函数调用频率低测试时不注意就漏掉了Winlogon 找不到符号时不会报错只会在对应时机静默失败。解决编译后必须做导出表差集比对方法是把原 msgina.dll 和 gina_hook.dll 的导出表分别导出到文本文件再 diffdumpbin /exports %WINDIR%\System32\msgina.dll orig_exports.txt dumpbin /exports gina_hook.dll hook_exports.txt fc orig_exports.txt hook_exports.txtFC 的差异结果里凡是出现在左边而没出现在右边的符号都是可能引发运行时问题的漏洞。补齐后重新编译再走一遍注销、锁屏、关机三连测试。注意GINA 代理里没有“这一步以后不会走到”的函数只要原 msgina.dll 导出了代理层就必须导出。低频不等于低风险。4.5 架构边界Vista 之后这套源码完全不生效现象在一台 64 位 Windows 7 或 Windows 10 上照着设置 GinaDLL重启后完全没反应系统日志也没有报错。原因这是架构变化不是配置问题。Windows Vista 开始GINA 被 Credential Provider 架构取代Winlogon 的代码路径里已经没有加载 GINA DLL 的逻辑GinaDLL 注册表键被彻底忽略。而且即使强行加载64 位 Winlogon 进程也加载不了 32 位 DLL。解决这个源码的适用范围就是 32 位 Windows 2000、XP、Server 2003。新系统要定制登录体验应该去实现 ICredentialProvider 接口那是完全另一套接口体系。拿到源码先确认目标系统版本如果手头只有现代系统就建一个 XP 虚拟机来做验证。在 64 位系统上折腾 GINA 属于浪费时间。5. 进阶最小注入点与上线前的两个验证手法5.1 最小注入点在 WlxLoggedOutSAS 里加一行审计代理框架跑通之后业务改动应该控制在最小范围。一般我只在 WlxLoggedOutSAS 里加逻辑因为这个函数是登录界面上用户按下 CtrlAltDel 后的必经之路覆盖面广而且语义清晰。加日志的操作可以限制在一行BOOL WINAPI WlxLoggedOutSAS(PVOID ctx, DWORD sasType, PVOID res, PVOID authId, HANDLE token, PDWORD opts) { HookContext* hc (HookContext*)ctx; if (sasType WLX_SAS_TYPE_CTRL_ALT_DEL) { OutputDebugStringW(L[gina_hook] CtrlAltDel at logon UI); } if (!hc || hc-origCtx NULL) return FALSE; return g_origLoggedOutSas(hc-origCtx, sasType, res, authId, token, opts); }注入点的原则是“只加不删”判断条件里不要试图阻止原函数执行除非你有完整的会话管理逻辑先记录再转发始终保持原链路畅通。5.2 上线前必做的两个验证每次编译完我会强制走一遍导出表 diff不管改动有多小。GINA 代理的问题往往不在你写的代码里而在你漏掉的函数上。少一个 WlxIsLogoffOk注销时就可能蓝屏而且这种问题在测试登录过程中完全测不出来。导出表差集比对是唯一能提前发现它的手段。第二个验证是加载路径确认。用 DebugView 挂着看 OutputDebugStringW 输出重启虚拟机如果能看到 [gina_hook] WlxInitialize OK说明系统已经加载了你的代理 DLL再按一次 CtrlAltDel能看到 WlxLoggedOutSAS called说明登录界面路径也通了。看不到输出就优先查注册表路径和文件是否真的在 system32 下而不是去翻代码逻辑。改 GINA 这活省掉的检查都会在重启后加倍找回来。我有一次漏转发 WlxIsLogoffOk让测试机注销时直接重启日志还没来得及保存现场只剩一个毫无信息量的蓝屏代码。从那以后我每次编译完都强制把导出表 diff 过一遍才敢碰注册表。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询