ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CodeMeter WUPI实现C++软件保护:从入门到上线验证

CodeMeter WUPI实现C++软件保护:从入门到上线验证 简介面向威步CodeMeter/WIBU系列软件保护方案的C开发人员PDF文档整理了WUPI Samples的说明重点解决如何在源码层次上结合AxProtector外壳工具对程序关键函数段进行自定义加密与动态解密调用同时实现按功能模块授权、防盗版与防调试。包内为1个PDF文件整体约1.06MB容量紧凑适合随手查阅。文档以Calculator为例从工程中打开CodeMeter或WibuKey对应的外壳配置开始逐步说明在Advanced options中激活WUPI、给不同计算功能分配许可、添加待加密函数并绑定license list再对应程序内调用WupiDecryptCode() / WupiEncryptCode()进行解密与再加密确保敏感代码不长时间暴露在内存中。此外还介绍了WupiCheckLicense()、WupiCheckDebugger()、计数器减少等常用函数并解释了wibuixap.h、WupiEngine32.lib的引用方式以及编译后需由外壳工具加密的注意事项。已有169人学习下载对正在集成CodeMeter硬件加密锁、希望做细粒度代码保护或模块化授权的开发者有直接的实操参考价值。1. 用 CodeMeter WUPI 做 C 软件保护先别急着写代码拿到一份“CodeMeter WUPI Samples说明For C.pdf”第一反应是打开里面的工程文件直接编译。但如果你没装 CodeMeter Runtime也没理解 WUPI 和 CmAPI 的区别编译报错会给你一个下马威。WUPIWindows Universal Protection Interface是威步软件 CodeMeter 加密狗提供给 C/C 程序员的保护层接口它把底层驱动通讯、许可证校验和加密狗读写封装成一组相对稳定的 C API 风格函数。这篇文章会从运行机制讲起告诉你为什么要在项目里引入硬件加密狗再带你用 C 在 Visual Studio 里跑通第一个 WUPI 调用最后聊几个上线前必须做的验证动作。适合正在做商业软件、共享软件、工业软件和私有算法的工程师新手能跟着配置环境老手可以对照检查自己的许可证设计。2. CodeMeter WUPI 的运行机制与硬件加密狗选型2.1 WUPI 在 CodeMeter 体系中的位置CodeMeter 这套产品线不仅仅是一个加密狗它由三部分构成USB 硬件加密狗CmStick、后台服务CodeMeter Runtime和应用程序编程接口。其中 API 又分为两大体系原生 CmAPICodeMeter API和 WUPI。CmAPI 给你最大自由度但要自己管理会话句柄和二进制缓冲区WUPI 更接近传统加密狗的“傻瓜模式”——它直接对文件、目录或者进程做保护也可以封装成一套简单的许可询问接口。WUPI 的官方定位是“面向 Windows 的通用保护接口”官方提供的 Sample 工程里通常包含三个核心操作读取加密狗信息、查询许可证、设置功能开关。这些 Sample 大多用 C 语言编写C 工程直接调用时需要注意头文件的 C 链接问题后面我会给出处理方法。WUPI 的代码风格是“初始化一次反复查询”它和 CmAPI 最直观的区别是WUPI 不需要你手动打开一个会话Session它内部通过 CodeMeter 系统服务完成通讯所以对多线程调用更宽松。2.2 CmStick 与 CmActLicense硬件狗和软授权的取舍选择 WUPI 做保护时你首先要决定保护载体是插一个 USB 加密狗CmStick还是使用绑定电脑硬件信息的软授权CmActLicense。表格里是我的常用选型参考对比项CmStick USB 加密狗CmActLicense 软授权便携性需要随身携带硬件跟随电脑可远程下发抗破解成本硬件防拆物理隔离依赖机器指纹和加密算法适用场景工业软件、内部工具、高价值单机应用互联网分发、试用版、按年订阅成本每个狗有硬件成本无硬件成本但有授权管理后台费用如果你开发的是 C/S 架构的客户端且客户有严格的资产管理要求建议选择 CmStick。如果你做的是面向大众市场的工具软件用软授权再搭一个服务器端激活逻辑更方便。WUPI 两种载体都能支持它在查询许可时会自动区分当前是硬件狗还是软授权你不需要写两套代码。2.3 WUPI 的 C 接口风格WUPI 头文件里最典型的函数是WUPI_Initialize、WUPI_License和WUPI_GetInfo。这些函数虽然是 C 风格但设计上考虑了 C 调用者的感受大部分函数返回统一的状态码状态码由头文件中的枚举定义而不是用简单的 int 裸值。这样在 C 里可以写if (status WUPI_SUCCESS)可读性比纯 C 好不少。还有一点容易踩坑WUPI 的 API 使用WUPI_HANDLE作为句柄类型本质是一个指针。你在 C 类里保存这个句柄时要注意它可能在WUPI_Initialize时被自动分配在WUPI_Release时被释放。不要尝试自己 delete 它否则内存释放和驱动句柄释放会冲突严重时导致进程崩溃。下面的示例会展示正确的用法。3. 在 Visual Studio 中搭建 CodeMeter WUPI 开发环境并跑通最小示例3.1 安装 CodeMeter Runtime 与 SDKWUPI 的 SDK 不单独安装它包含在 CodeMeter SDK 安装包中。你需要做两件事先装 CodeMeter Runtime运行时服务再装 SDK。Runtime 可以从威步官网下载但注意区分 32 位和 64 位版本。如果你的 C 工程是 x64 平台Runtime 要装对应的 64 位版本。SDK 安装后默认会在C:\Program Files (x86)\WIBU-SYSTEMS\CodeMeter SDK或者C:\Program Files\WIBU-SYSTEMS\CodeMeter SDK下生成头文件、库文件和 Sample 工程。安装完成后打开服务管理器确认CodeMeter.exe服务和WIBU-SYSTEMS Remote服务都在运行。常用检测命令如下sc query CodeMeter输出STATE : 4 RUNNING就代表服务正常。如果状态不是 RUNNING先检查 CodeMeter 控制中心是否能打开或者查看 Windows 事件日志中的CodeMeter来源错误信息。这一步没做好后面所有代码都会报WUPI_E_RM_NOT_CONNECTED之类的错误。3.2 配置 C 项目的包含路径与链接库打开 Visual Studio创建一个空的 C 控制台项目。注意把平台从 Win32 切换到 x64常见做法是先在解决方案配置管理器中新建 x64 平台。接着打开项目属性页进入C/C - 常规 - 附加包含目录添加 SDK 的 include 目录一般是C:\Program Files (x86)\WIBU-SYSTEMS\CodeMeter SDK\Include然后在链接器 - 常规 - 附加库目录添加 Lib 目录C:\Program Files (x86)\WIBU-SYSTEMS\CodeMeter SDK\Lib\x64最后在链接器 - 输入 - 附加依赖项中加上wupi.lib注意WUPI 的 lib 文件名没有版本号后缀。某些文档会提到wupi32.lib那是旧版本新 SDK 统一用wupi.lib。配置完成后你需要在包含头文件时加一层 extern C 保护防止 C 名字修饰导致链接失败extern C { #include WUPI.h }有的头文件可能已经内部处理了__cplusplus但为了兼容多个 SDK 版本我建议手动加。这也是 C 工程师第一次接触 WUPI 最容易踩的坑。3.3 编写第一个 WUPI 调用读取固件版本下面是一个最小可编译的示例功能是初始化 WUPI获取加密狗固件版本并输出#include stdio.h #include tchar.h extern C { #include WUPI.h } int wmain() { WUPI_HANDLE handle NULL; WUPI_STATUS status WUPI_Initialize(handle); if (status ! WUPI_SUCCESS) { printf(WUPI_Initialize failed, status %d\n, static_castint(status)); return 1; } char firmwareVersion[64] { 0 }; status WUPI_GetInfo(handle, WUPI_INFO_FIRMWARE_VERSION, firmwareVersion, sizeof(firmwareVersion)); if (status WUPI_SUCCESS) { printf(CodeMeter firmware version: %s\n, firmwareVersion); } else { printf(WUPI_GetInfo failed, status %d\n, static_castint(status)); } WUPI_Release(handle); return 0; }这段代码里我们调用了三个函数WUPI_Initialize负责建立与 CodeMeter 服务的通讯WUPI_GetInfo的第二个参数WUPI_INFO_FIRMWARE_VERSION是一个枚举值表示我们要读取固件版本WUPI_Release在退出前释放句柄。注意WUPI_Release的参数是WUPI_HANDLE*传handle而不是handle否则句柄不会被正确置空。另外千万不要用 malloc 或 new 给firmwareVersion分配任意大小某些固件版本字符串可能很长建议至少 64 字节。这个示例编译运行后如果你已经插入一个空的 CodeMeter 加密狗会输出类似CodeMeter firmware version: 1.34的字符串如果没插狗会得到WUPI_E_NO_STICK或WUPI_E_NO_LICENSE错误。4. WUPI 的核心操作许可证查询、功能控制与错误处理4.1 查询许可证是否存在WUPI 中最常用的操作是查询某个产品的许可证是否有效。许可证通常用 Entry 标识Entry 是一个整数可以看作是产品 ID。假设我们的产品 Entry 是 100你需要调用WUPI_CheckFeature或者WUPI_License。不同版本 SDK 的函数名称略有差异以头文件为准。老版本常用WUPI_License新版本更推荐WUPI_CheckFeature因为它支持按 Feature功能号细分授权。下面是一个典型的查询逻辑#include stdio.h extern C { #include WUPI.h } int CheckEntry(DWORD entryId) { WUPI_HANDLE handle NULL; WUPI_STATUS status WUPI_Initialize(handle); if (status ! WUPI_SUCCESS) return status; status WUPI_CheckFeature(handle, entryId, 1, 0, nullptr); if (status WUPI_SUCCESS) { printf(Entry %u license valid.\n, entryId); } else { printf(Entry %u license invalid, status %d\n, entryId, static_castint(status)); } WUPI_Release(handle); return 0; }函数WUPI_CheckFeature的第三个参数是 feature 号1表示主功能第四个参数可能用于传递版本号一般传 0第五个参数是预留指针传nullptr。如果WUPI_CheckFeature返回WUPI_SUCCESS说明该 Entry 在加密狗中存在且许可证有效。注意这里的“有效”不包含过期时间判断过期时间要单独用WUPI_CheckFeatureExpiration或者查询许可证动态信息。4.2 用 Feature 控制程序功能商业软件中常常有“基础版”和“专业版”的区分可以用 Feature 号来实现。比如 Entry 100 代表产品Feature 1 是基础功能Feature 2 是高级功能。你可以封装一个 C 类class LicenseManager { public: bool Initialize() { return WUPI_Initialize(m_handle) WUPI_SUCCESS; } bool HasFeature(DWORD featureId) { if (!m_handle) return false; return WUPI_CheckFeature(m_handle, 100, featureId, 0, nullptr) WUPI_SUCCESS; } void Release() { if (m_handle) { WUPI_Release(m_handle); } } private: WUPI_HANDLE m_handle nullptr; };实际使用中不要在每次按钮点击时都调用WUPI_CheckFeature因为软件狗通讯需要经过进程间通讯频繁调用会有性能损耗。常见做法是启动时一次性验证把 Feature 缓存成布尔值。但如果你的程序需要防止调试器篡改内存可以定时重新验证或者在关键算法执行前再次检查。注意 Release 和 Initialize 必须成对调用否则会导致句柄泄漏。4.3 错误码与常见失败原因WUPI 的错误码是WUPI_STATUS类型头文件中有一组以WUPI_E_开头的常量。我整理了几个最常见的错误码及其处理建议错误码含义处理建议WUPI_SUCCESS调用成功正常流程WUPI_E_NO_LICENSE没有找到对应许可证检查加密狗中是否已经写入 EntryEntry 大小写、产品 ID 是否匹配WUPI_E_NO_STICK没有检测到加密狗检查 USB 连接、驱动是否安装、CodeMeter 服务是否启动WUPI_E_HARDWARE_FAIL加密狗硬件故障用 CodeMeter 控制中心检测狗是否被识别WUPI_E_RM_NOT_CONNECTEDCodeMeter 服务未连接确认 Runtime 进程存在重新安装 RuntimeWUPI_E_PARAM参数错误检查句柄是否为空、缓冲区大小是否正确遇到WUPI_E_NO_STICK时先不要急着改代码。拔出加密狗重新插入然后打开 CodeMeter 控制中心看是否能识别到设备。如果控制中心里都没有设备说明是驱动层问题而不是 API 的问题。如果控制中心能识别但 API 报NO_STICK则多半是 API 使用的句柄在初始化时使用了错误的访问权限或者你用了 32 位工程去访问 64 位驱动这种不匹配在混用库时经常出现。检查项目平台的位数是否和 SDK 一致是处理这类问题最快的路径。5. 上线前的验证模拟拔出加密狗、日志与反调试小技巧真正发布之前建议做一轮“断电测试”。这里的断电不是指电脑关机而是指程序运行中拔掉加密狗。WUPI 的默认行为是如果加密狗在程序启动后被拔出已经获得的句柄仍然有效现有功能不会立刻停止只有当你再次调用WUPI_CheckFeature时才会返回错误。如果你的软件需要实时监控加密狗状态就必须自己设计一个看门狗线程。我常用的方案是每 5 秒调用一次WUPI_CheckFeature连续失败 3 次就触发退出流程保存数据并关闭进程。注意看门狗线程不能直接调用exit最好向主线程发送一个安全退出消息。日志记录方面不要用printf硬编码。封装一个宏编译期可以开关#define WUPI_LOG_ENABLE #ifdef WUPI_LOG_ENABLE #define WUPI_LOG(fmt, ...) fprintf(stderr, [WUPI] fmt \n, __VA_ARGS__) #else #define WUPI_LOG(fmt, ...) #endif把错误码转成可读字符串时不要只打印数字。写一个函数用 switch-case 把WUPI_E_NO_LICENSE转成字符串这样客户发回来的日志你能直接看懂。断点调试时WUPI 的句柄值对你调试没有太大意义更重要的是每次调用后的 status。可以在封装类中保存最后一次错误码同时记录调用时刻和线程 ID。反调试技巧WUPI 本身提供了 API 级别的反篡改能力但你可以在业务层加一道防线使用IsDebuggerPresent和NtQueryInformationProcess组合判断。注意这些 API 很容易被反反调试技术绕过所以不要把它当作唯一防护。更好的一种方式是让程序经常检查WUPI_CheckFeature的返回值如果检测到连续错误重试前故意等待 100 毫秒拖慢暴力破解的尝试速度。你也可以在关键算法中使用签名和哈希校验但那是更高级的防御需要在产品设计时预留验证函数。最后一个小技巧利用 WUPI 的 BindToProcess 特性。部分 WUPI 版本支持把许可证绑定到特定进程名这样即使加密狗被复制许可证也无法在别的进程中使用。启用这个特性后调试器附加会导致权限检查失败调试体验会变差但防护强度会明显提升。请根据你的产品定位决定是否启用。本文还有配套的精品资源点击获取
返回列表