2384 字
12 分钟
我的 NAS 我做主 ~ UGOS Pro 应用开发签名分析

本文所述内容仅面向合法授权场景下的软件逆向分析、安全研究与教学演示,不提供、不鼓励、不支持任何形式的非法破解、商业软件盗版、授权绕过、恶意攻击或侵犯他人权益的行为。读者应在遵守法律法规及软件许可协议的前提下进行学习和实验。作者不对任何未经授权或违法使用本文内容所导致的后果承担责任。

前言#

买断的产品应该享有自主控制权,霞葉一直是这么认为的。这就是为什么我之前一直会选择能解锁 Bootloader、Root 的手机,选择能获取 Console 的路由器,选择能接入 HA 或开放协议的 IoT 设备,选择不锁 BIOS 且有 HDMI 接口的绿联 NAS。我是飞牛和绿联 NAS 双持用户,飞牛的开放性不必多说,适配了一众 x86 设备,活跃的论坛氛围,开放第三方应用社区(conversun/fnos-apps),甚至之前还对所有人开放过任意路径访问。飞牛的发展离不开社区的支持,飞牛自己也明白这一点,至少从 2025 年 11 月 6 日起,飞牛就已开放第三方应用的开发,印证飞牛说的那句“把飞牛 fnOS 打造成 “存储系统界的 Windows””。

image
笑点解析:时间是在飞牛开放后不久

当然,作为绿联 NAS 主力用户,我还是希望绿联也能开放第三方应用的开发。然后发现我们的绿联也是不负众望的把开放第三方应用,放入了已规划中…然后也是毫不意外地,在半年后的 2026 年 5 月 22 日才正式上线开发者平台。嘛 Better late than never,既然开放了,那我也是第一时间就去看了开发文档,却发现这个开发平台很粗糙糊弄,还一堆繁文缛节。开发平台只给了一个 cli 打包工具,少得可怜的 UGOS 系统交互能力,没有测试用虚拟环境。更难绷的是,我在我自己的设备上开发和测试,竟然需要申请签名才能安装自己的应用,而且申请签名还要填写他那鸡巴巨麻烦的模板。没错,不是在上架时要申请,而是你想安装自己开发的应用都要申请,要看他们脸色。虽然但是,我还是在当天随便填了一份,交了申请,毫不意外地到今天都没有收到回复,没想到真的这么严格审核啊。

image

这种事情,社区当然会有很大的意见…吗?然而我去看了社区,发现只有寥寥俩三条帖子反映这个事情。对此,社区的(疑似官方)宣传口也是非常的闹钟式回复“不限制被人投毒挖矿咋办”。对于绿联这种固步自封、僵化腐朽的思想,我想引用社区的一句评价,“玩死自己罢了,真当自己是 Apple 了”。作为一个对电脑控制欲很强的人(笑点解析),这种事情当然容忍不了,怎么可能容忍得了。对它使用 ida 吧,似乎有人这么对我说到。经过分析最终绕过了 ugdev.sig​ 签名校验,在自己的设备上安装了测试应用。不过,本文并不会给出绕过校验的成品,也不会给出任何直接协助绕过校验的工具,毕竟我也不想吃律师函。同时,也不希望被广泛的传播和滥用,如果后续引起绿联官方注意,可能会对程序进行混淆加固。

image

image

应用中心分析#

从 Web 端的 upk​ 文件手动安装应用的入口来看,使用到签名文件 ugdev.sig​ 的是这个名为 应用中心​ 的程序。UGOS Pro 系统是基于 Debian 12 魔改的,ssh 登录之后,可以看到有很多 systemd service,根据名称猜测应用中心应该是 app_serv.service​。程序是前后端分离的模式,后端是一个 stripped 无调试信息的 Go 二进制程序,好处是没有加壳、没有混淆,Go 的元数据都还在,可以恢复大量函数边界和函数名。那还说什么呢,ida 启动!虽是这么说,但这个时代真的还有人从头到尾手动分析吗,推荐使用 IDA Pro MCP,解放双手。

# 一些神秘符号和关键词
app_serv/internal/ugdevcheck.Inspect
app_serv/services/devauth.(*serviceImpl).CheckAllowV1
app_serv/services/devauth.(*serviceImpl).CheckFile
gitlab.ugnas.com/ugos-pro/ugnas/sign.VerifyFileSignWithoutPub
gitlab.ugnas.com/ugos-pro/ugnas/sign.VerifyFileSignV2
gitlab.ugnas.com/ugos-pro/ugnas/sign.VerifyFileSignV3
ugdev.sig
devAuth
CheckAllowV1
UGREEN-PKG-FORMAT
VerifyFileSignWithoutPub
getRootCa
ugreenRootImport.pub
app_serv/services/devauth
app_serv/internal/ugdevcheck

首先,根据 .gopclntab​ 等元数据恢复函数名称,然后从函数名称中分析 ugdev.sig​ 校验机制即可。根据分析得知,系统支持三种版本的 UPK 安装包,分别是 V1, V2, V3,而此次开放的则是最老的 V1 格式 UPK,绿联在让人失望这一块还是不让人失望啊。V1 版本的校验走的是不同的链路,经过分析,UPK 安装的入口是在 services/upk.(*serviceImpl).upkPreCheck​。 upkInfoWithV1Checker​ 是启用 V1 开发者授权检查的包信息入口;upkPreCheck​ 负责文件存在性、扩展名、系统版本和包格式策略;appmaker.GetUpkVersion​ 只读取包头 magic,判断 V1/V2/V3;loadUpk​ 则是在 precheck 通过之后才调用 appmaker.ParseUpk​ 和 appmaker.ExtractPackage​,进入真正的包解析和展开阶段。版本号大于 108000000​ 的系统上,对于 V1 格式的包,会检查开发者授权,也就是绿联给你签发的 ugdev.sig​。检查的函数是 services/devauth.(*serviceImpl).CheckAllowV1。整个流程如下:

用户上传 .upk
-> services/upk.(*serviceImpl).upkInfoWithV1Checker
-> services/upk.(*serviceImpl).upkPreCheck(..., allowV1Check=true)
-> utils.Exists(path)
-> 检查扩展名是否为 .upk
-> device.GetSystemVersion()
-> appmaker.GetUpkVersion(path)
-> 如果 magic == "UGREEN-PKG-FORMAT" 且系统版本 >= 108000000
-> 调用 devAuth.CheckAllowV1()
-> 检查 /ugreen/.config/ugdev.sig
-> 授权失败则拒绝旧格式
-> 授权成功才继续进入 loadUpk / ParseUpk

那么要做的事情就很明显了,顺着 CheckAllowV1​ 往下看,进入 services/devauth 这一层。值得关注的函数有这些:

services/devauth.init
services/devauth.getUserAuthFile
services/devauth.selectCurrentUserAuthFile
services/devauth.(*serviceImpl).CheckAllowV1
services/devauth.(*serviceImpl).HasValidUserDevAuthFile
services/devauth.(*serviceImpl).CheckFile
services/devauth.inspect

CheckAllowV1​ 确实是校验 ugdev.sig ​的函数,但有趣的是,他是校验系统级授权的,对应文件 /ugreen/.config/ugdev.sig​,直接影响 V1 包是否允许继续安装。而 HasValidUserDevAuthFile ​才是校验用户级授权的,对应目录 <user home>/ugdev.sig,用于当前用户的开发者授权状态。哈哈,还有第二关。系统级和用户级校验流程分别如下:

services/devauth.(*serviceImpl).CheckAllowV1
-> utils.Exists("/ugreen/.config/ugdev.sig")
-> 文件不存在:
return (false, false)
-> 文件存在:
-> internal/ugdevcheck.Inspect("/ugreen/.config/ugdev.sig")
-> 校验成功:
logger.Infof("allow v1 upk by %s auth", source)
return (true, true)
-> 校验失败:
logger.Errorf("auth v1 failed, source=%s, err=%v")
return (false, true)
services/devauth.(*serviceImpl).HasValidUserDevAuthFile(username)
-> services/devauth.selectCurrentUserAuthFile(username)
-> getUserAuthFile(username)
-> os/user.Lookup(username)
-> filepath.Join(user.HomeDir, "ugdev.sig")
-> utils.Exists(<home>/ugdev.sig)
-> services/devauth.inspect(path, source=current_user)
-> os.Stat(path)
-> internal/ugdevcheck.Inspect(path)
-> localizeInspectError(err)
-> personalUserDevAuthInspectShouldBlankBody(err)
-> 返回当前用户是否有有效授权

系统级和用户级校验最终都复用了 ugdevcheck.Inspect​,真正的核心函数便是 internal/ugdevcheck.Inspect​,这个函数会先调用 sign.VerifyFileSignWithoutPub 来验证文件签名,也就是说授权文件是 Payload + Signature 的形式,具体为:

ugdev.sig:
+-----------------------------+------------------------------+
| encrypted body prefix | outer signature/trailer tail |
| file[0 : len(file)-3072] | file[len(file)-3072 : end] |
+-----------------------------+------------------------------+
binary payload:
+----------+----------------------+---------+
| nonce | AES-GCM ciphertext | tag |
| 12 bytes | variable | 16 bytes|
+----------+----------------------+---------+
plaintext:
{
"SN": "TESTSN123456",
"Mac": "AA:BB:CC:DD:EE:FF",
"DaysLimit": 365,
"Operator": "operator name",
"SignTime": 1735689600,
"ExpireTime": 1767225600,
"SignFile": "ugdev.sig"
}

很合理的结构,拿不到私钥的话,无论怎么也没法在不修改程序的情况下通过校验,总不能期待绿联自己不小心把私钥泄露出来吧。那么就只能 patch 二进制程序了,经过分析之后,要 patch 哪些点其实已经很清楚了,为了避免不必要的问题我就不直说了。总之核心的校验流程如下,顺带一提,由于有 normalizeMACAddress​,所以 aa:bb:cc:dd:ee:ff​、AA-BB-CC-DD-EE-FF​、aabbccddeeff 这些写法都是可以的。

Inspect(path)
-> utils.Exists(path)
-> VerifyFileSignWithoutPub(path)
-> os.ReadFile(path)
-> 检查 len(file) - 3072 > 0
-> body = file[0 : len(file)-3072]
-> openssl/aes256gcm.Decrypt(body)
-> encoding/json.Unmarshal(plaintext, *SignInfo)
-> device.GetSn()
-> 比较 SignInfo.SN
-> device.GetAllMac()
-> normalizeMACAddress(SignInfo.Mac)
-> normalizeMACAddress(each local MAC)
-> time.Now()
-> 检查过期时间
-> 返回 InspectResult

实战测试#

根据分析,对二进制程序patch之后,替换掉系统中原本的二进制程序,重启服务。可以看到设置里多出了”应用开发设置”,点击授权之后就可以安装自己的UPK包了。

image
应用开发设置

image
安装测试应用

image
运行测试应用

在体验了测试应用能做到什么之后,我也许明白了绿联为什么连测试都要卡用户脖子,但我还是没法接收。正如最后一张图中的Banner文本,“开放生态,共赢未来”,飞牛就是一个很好的例子。绿联并不是苹果,也没有那么多开发者会专门来适配绿联,不会为了开发应用去填麻烦的申请表,更不会专门买一台真机测试。只有开放才能共赢,绿联的固步自封只会自取灭亡。最后,本文所述内容仅面向合法授权场景下的软件逆向分析、安全研究与教学演示,不提供、不鼓励、不支持任何形式的非法破解、商业软件盗版、授权绕过、恶意攻击或侵犯他人权益的行为。读者应在遵守法律法规及软件许可协议的前提下进行学习和实验。作者不对任何未经授权或违法使用本文内容所导致的后果承担责任。

看到便是缘分,希望读者不要传播,更不要到绿联官方那去跳脸。

我的 NAS 我做主 ~ UGOS Pro 应用开发签名分析
https://kasuha.com/posts/ugos-pro-ugdevsig-analysis/
作者
霞葉
发布于
2026-05-30
许可协议
CC BY-NC-SA 4.0
评论将在滚动到此处后加载...