文章总结: 本文分析本地签名验签授权方案必然被绕过的原因,指出公钥与验签代码均在用户可控机器上,破解者可替换公钥自签授权或改写验签分支。文章列举常见误区,提出四层加固路线,并建议高价值软件采用服务端验签将信任根移出二进制,以从根本上消除绕过风险。 综合评分: 85 文章分类: 代码审计,逆向分析,安全开发
公钥能换,分支能改:为什么本地签名验签授权必然被绕过
原创
利刃信安 利刃信安
利刃信安
2026年10月6日 09:00 北京
在小说阅读器读本章
去阅读
在公众号小说中沉浸阅读
公钥能换,分支能改:为什么本地签名验签授权必然被绕过
假设存在这样一个可执行程序:授权方持有私钥签发授权,把验签公钥写死在程序里,程序读取授权文件验签并据此决定是否授权。这类”本地签名验签”方案常见于各类商用工具的授权管理。本文以两种典型实现为对象,分析它为何在发布后几乎必然会被绕过,以及防御资源应当投向何处。
假设中的方案:一种典型的本地签名验签授权
从现实中已部署的授权实现可以归纳出一套被反复采用的本地授权架构。其理念是让授权方与程序在密钥上保持”单向控制”:私钥只在签发时使用且始终保密,程序内只保留验签用的公钥。
程序启动后的校验逻辑大致如下,此处以 SM2 变体为例:
/* 内嵌在程序里的公钥(DER 编码) */
staticconstunsignedchar kPubkeyDer[] = { 0x30, 0x5a, 0x30, 0x14, /* ... */ };
constunsignedchar *ptr = kPubkeyDer;
EVP_PKEY *pub = d2i_PUBKEY(NULL, &ptr, (long)sizeof(kPubkeyDer)); /* 解析内嵌公钥 */
if (pub == NULL) { /* 公钥解析失败 */ }
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
if (1 != EVP_DigestVerifyInit(ctx, NULL, EVP_sm3(), NULL, pub)) { /* 初始化失败 */ }
constunsignedchar *msg = (constunsignedchar *)payload; /* 授权载荷 */
int ok = EVP_DigestVerify(ctx, sig, siglen, msg, strlen(payload));
if (ok == 1) 允许使用完整功能;
else 授权失败,程序退出;
/* 为简洁略去 EVP_MD_CTX_free / EVP_PKEY_free 等资源释放与错误处理 */
RSA 变体的验签接口一致,仅摘要算法与公钥载入形态不同。两类方案虽有差异,但其共同点都是”公钥内嵌、验签在本地完成”:
| 环节 | SM2 签名类型 | RSA PKCS#1 v1.5 + SHA-256 类型 |
| — | — | — |
| 授权文件形态 | 整体 Base64 编码的一段 JSON 载荷,签名值作为其中的一个字段 | 整体 Base64;解码后的内容为 载荷 JSON + . + Base64 签名 |
| 公钥内嵌方式 | 65 字节未压缩公钥点(04 + X + Y),硬编码在二进制中 | PKIX PEM 格式公钥,编译期写入 |
| 授权成立的判定 | 授权文件存在,SM2 验签通过,并校验载荷中的有效期 | 授权文件存在,RSA 验签通过,并比对本机机器指纹与到期时间 |
| 签名覆盖范围 | 覆盖载荷中的部分字段,个别字段未纳入签名 | 覆盖按固定键序序列化后的载荷原始字节 |
| 部分方案的额外弱点 | 续期类时间字段未签名,可被单点篡改”延寿”;对称密钥/向量还可能以常量硬编码 | 机器指纹比对在本地完成,比对逻辑可被改写 |
两类方案在关键一点上没有本质区别:验签用的公钥、决定是否放行的代码,都位于一台被使用者完整控制的机器上。下面的分析面向这一共性展开。
为什么它必然可被绕过
当信任锚(公钥)与执行信任锚的代码都处于使用者的完整控制之下时,破解者不必攻破密码学,只需改写二进制。常见的突破口有两处。
突破口一:替换内嵌公钥,自签发授权
程序内的公钥以固定字节块存放。在同一条椭圆曲线下,各公钥的编码长度固定(SM2 即为 65 字节未压缩点),可直接等长原地替换;即便编码长度不同(如 RSA 模长不同导致公钥长度不一),破解者只需在替换时同步修正相关编码中的长度字段,同样可以完成。替换流程如下:
- 1. 生成自己的密钥对;
- 2. 在二进制中定位内嵌公钥,替换为自己的公钥;
- 3. 用对应私钥对载荷重新签名,重新生成授权文件(载荷 + 新签名);
- 4. 程序用替换后的公钥验签,必然通过。
替换完成后,任何拿到程序的人都可以给该软件无限制地自行发行授权。无论授权载荷里写了什么、有效期设到何时,只要公钥由破解者控制,验签就永远成立。这正是”本地签名验签”最直接的失效方式。
突破口二:改写验签分支,绕过校验
比替换公钥更省事的是直接改控制流。在反汇编中定位到验签之后的那个条件跳转——它负责”当验签结果不等于 1 时跳往授权失败分支”——把这条跳转指令改为无条件通过。破解者无需触碰任何与密码学相关的数据,只改动极少量机器指令即可让程序无论如何都放行。
一次典型的绕过过程仅改动一行跳转指令(通常两三个字节):找到判断验签结果的指令序列,把”结果不为 1 即跳失败”改成”不跳转、直落成功”。修改后,即便授权文件没有由任何有效私钥签发,程序也照常输出授权成功。
两条突破口指向同一个结论:签名验签解决的是”谁签的”这一密码学问题,却解决不了”这段代码与这把公钥是否位于可改写之处”这一信任问题。防御对象不是”公钥这一个常量”,而是”验签→判定→授权”这条完整决策链;而链上的每个环节都长在破解者可操作的机器里。
一个易被忽视的细节
程序为向使用者展示信任关系,常会顺带打印一个”公钥指纹”之类的辅助值。这个值往往也是写死的字符串常量,与公钥数组是两个独立的数据。当公钥被替换后,打印出的指纹仍是旧值——这表明一旦校验相关的数据以常量形式存在,破解者就拥有多个可独立改写的点,而这些点未必都经过运行时自洽校验。任何”写死 + 无运行时互验”的组合,都在扩大可破解表面积。
让人误以为安全的几种做法
在讨论正确加固路线之前,先排除几个常见误区:
- • 私钥写入程序。为了让程序具备签名能力而内嵌私钥,等于把密钥交给对手,造成一次性全面泄露。
- • 只签一个布尔值”是否授权”。授权载荷中缺少产品标识、版本、有效期、机器信息,验签通过即放行,篡改空间极大。
- • 让部分时间/有效期字段不参与签名。未纳入签名范围的字段会成为”延寿”的篡改出口。
- • 把对称加密的密钥与初始向量以常量写死,甚至二者共用一个常量。明文常量一经提取即可用明文格式重放或解密相关数据。
- • 把机器指纹比对放在本地且未绑定。比对逻辑与比对数据都在客户机,可被改写或伪造。
- • 把全部校验集中在一个函数里。单点代码一次定位、一次打补丁即整体解除。
该怎么做:把信任根一层层移出二进制
以下按破解成本自低到高排列,是本地授权常见的四层加固路线,可逐层叠加:
| 层级 | 手段 | 作用 | 破解成本 | | — | — | — | — | | L1 反篡改与自校验 | 对含公钥与校验代码的关键段做运行时哈希自检;公钥拆成多段、运行时拼装,不写成整块常量;加壳保护 | 拦截只会用十六进制编辑器改字节的入门破解 | 低→中 | | L2 混淆加功能耦合 | 验证代码用编译器混淆 / 代码虚拟化(VMProtect、Themida、OLLVM 一类);加反调试反转储;让验签结果参与解密或计算,而非单独返回布尔值 | 使静态定位、动态下断点都变难;跳过校验会导致功能错乱而非仅提示失败 | 中 | | L3 绑定硬件指纹 | 授权与磁盘、网卡、CPU 序列或 TPM 度量绑定,一机一码,验签通过后再比对本机指纹 | 遏制”一份授权到处复制”,也抬高伪造特定机器的门槛 | 较高 | | L4 服务端验签 / 在线激活 | 私钥与验签放在授权服务器,客户端只提交授权码与机器指纹,换取密钥或启动许可;程序内不再需要可用于验证授权的公钥 | 信任锚彻底迁出二进制,本地任何改写都不再成立 | 高 |
L1 到 L3 都只是在抬高门槛,可拦截多数破解尝试,却拦不住下定决心的人,因为信任根仍在本机。只有 L4 将信任根移往服务器,才从根上消除”公钥可替换、分支可跳过”这两处命门。
此外,高价值纯本地软件难以抵御长期、有偿的反向工程投入:即使加固充分,盗版者最终仍可能产出破解版,只是时间问题。因此在营销、发版节奏或”核心价值放云端”上建立商业护城河,往往比再把破解成本抬一级更划算。
实施建议
可按软件定位做出取舍:
- • 价值低、主要用于个人或内部环境:落地 L1 自校验加公钥分散即可,不宜在防破解上投入过多。
- • 商用、会传播、存在盗用扩散风险:在 L1 基础上叠加 L2 混淆与功能耦合、L3 硬件绑定,并接受”成本更高、仍非绝对安全”的现实。
- • 高价值、要求授权真正稳固:优先采用 L4 服务端验签,配合硬件绑定,将私钥锁定在服务器。这是唯一能让”替换公钥”彻底失效的做法。
结论指向同一处——信任锚的位置。公钥放在二进制里,就注定能被改写;放到自行控制的服务器上,本地那份公钥即使被替换也无妨,因为授权判定根本不在本地完成。本地签名验签是有用的加固手段,但不应作为授权方案的终点。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:利刃信安 利刃信安 利刃信安《公钥能换,分支能改:为什么本地签名验签授权必然被绕过》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论