我跟你们说,最近为了搞定一个内部工具的部署问题,我真是折腾得够呛,感觉像是偷偷摸摸练了一套“黑魔法”。今天的实践记录,就是关于怎么把一个死活要连外网更新的安装包,彻底驯服,让它只认我给的“更新地址”。
需求由来:这摊烂泥,谁爱踩谁踩
我们组里有个历史遗留的系统,用的人不多,但非常关键。上面跑着我们自己定制的一个小客户端,但是这个客户端的原版框架,有个特别烦人的毛病——它每次启动都会去连一个固定的官方地址检查更新。关键是,我们内部环境是物理隔离的,根本出不去。更要命的是,就算能出去,那个官方更新包跟我们定制的功能也是冲突的,一更新立马崩。
刚开始我没多想,觉得不就是部署吗?把文件拷贝进去不就行了。结果发现,它每次启动都会检查更新,如果检查不到或者检查到了错误的地址,它就直接罢工,或者弹出难看的报错框。老板让我务必搞定,还强调部署流程要自动化,不能让运维一个个去改配置文件。这事儿就落到了我头上,像一坨没人想接的烫手山芋。
动手:从安装包里揪出更新地址
我上手第一步,就是把那个官方的安装包给扒了。我先用了一个解包工具,把那个自执行文件(EXE)拆开,想看看它里面到底塞了什么东西。结果发现,这压根就不是个标准安装包,它是个下载器,启动后会去拉一个初始的配置文件和几个核心DLL。
我立马意识到,问题的关键不在于文件本身,而在于它启动时读的那个“路标”。
- 我1抓了一把数据包。我架了个本地代理,让那个安装程序跑起来。果然,它刚一启动,就往一个固定的 IP 地址发了一堆请求,问:“我现在最新版本是多少?”
- 根据抓到的数据流,我确定了它访问的那个关键配置文件。我找到了一个被加密压缩的配置清单,里面清清楚楚地写着:
UpdateServerUrl=。 - 官方程序员还挺鸡贼,这个地址不是写在明文配置文件里的,而是被写死在了一个核心的二进制库(DLL)里,或者是在安装时动态写入了注册表的某个隐蔽角落。
我在注册表里找了好久,把所有跟这个软件名字沾边的键值都翻了一遍。终于,在HKEY_CURRENT_USER\Software\VendorName\ProductName下面,我发现了一个叫做InstallPath和ManifestSource的键。Bingo!就是这个ManifestSource,它存储的地址就是检查更新的“黑魔法”入口。
定制:打造自己的“更新地址”
既然知道了它读的是注册表,那事情就好办多了。我的目标就是:在安装过程中,把这个键值覆盖掉,让它指向一个我控制的、永远返回“最新版本就是你现在这个版本”的服务器地址。
但我又不能真的去搭一个复杂的服务器,那太费事了。我的“黑魔法”就是让它指路,但路是条死胡同。
我搞了一个新的自制安装脚本。这个脚本干两件事:
- 先把我们定制好的文件包(包括那些核心DLL)安静地拷贝到目标路径。
- 然后,用Windows批处理命令强制修改注册表键值。我把那个
ManifestSource的值,改成了我们内部一个永远不会响应请求的地址,比如一个本地环回地址的特定端口,或者直接指向一个无效的共享路径。
我一运行这个自制安装包,等它全部跑完,再启动那个客户端。奇迹发生了:它试图去连接那个更新地址,连接失败后,程序没有报错,而是直接进入了运行状态,因为它判断为“无法检查更新,继续使用当前版本”。完美地绕过了官方的校验和更新机制。
我为啥对这种底层的东西这么熟练?说起来都是泪。前两年,我们公司忽然实行了996,我身体扛不住,辞职了。结果我回老家休息时,发现老东家给我交社保的记录断了,当时闹得沸沸扬扬,我跑社保局跑了一个月,天天跟各种条文和系统打交道。那段时间,我研究各种系统流程和注册表的时间,比我写代码的时间还多。我发现,很多所谓的“自动化”和“安全机制”,就是一层窗户纸,捅破了,你就能看到底下藏着的那些乱七八糟的配置。
经过这回折腾,我现在对各种安装包和更新机制都抱有一种警惕心。现在我们的内部部署,就是通过这个“黑魔法”安装包进行,运维只要一键执行,就能保证客户端的版本永远锁死在我们想要的那个状态,再也不用担心被官方的自动更新给捅娄子了。实践出真知,这套路子,虽然粗暴,但是真管用!