🍭🎉🌊
更新-20251114:
自动修补二进制脚本
更新-20251010:
去除 8.0.x 版本启动时检查更新和系统通知
更新-20250321:
7.0.x 通用修补
8.0.x 通用修补
更新-20240601:
新版本去除启动时授权弹窗
去除强制更新
因为刚注册不久,想着发个帖子经营一下账号。恰好看到了站内大佬 @iamcjsyr 的帖子 《Xshell 7免费版修改最大标签页限制》,正好自己也要用到 Xshell 和 Xftp,加上之前在站内看了不少去广告弹窗的教程和实例,想亲自动手实战一下。我自己也是新手上路,文中有错误或不当的地方,还望各位坛友们批评指正,不胜感激!
这两款软件免费版功能跟付费版基本一致,但不能商用,且需要注册账号进行授权。
(问了个AI,官网目前没找到功能对比)
从 7.0.0134 开始,这两个软件的授权弹窗,由【退出前】改到了【启动前】 👇
不授权验证的话,点【后来】按钮并不能一直推迟,30天后再不授权就不能进入程序主界面了!可以修改系统本地时间验证。
找个临时邮箱注册账号,就可以通过授权验证了!且后续不会再弹窗,也就没有什么去弹窗破解的操作了!
至于用一段时间(半年/一年)就强制要求更新,不同人、不同场景,有不一样的需求。我个人是觉得新版本做了功能升级和BUG漏洞修复,升级一下也要不了多少时间。
预备
本次修改需要用到 x64dbg 反编译调试,难度大概在入门级别,需要一定的基础和耐心。
新手朋友或者嫌麻烦的坛友们,咱电梯直达 修补方法 ?
应用软件下载
目前适用的版本:7.0.x、8.0.x
如果之前安装的是付费版,需要先卸载,里面配置的连接信息看个人需求是否导出保存!!!文件->导出
如果同样是免费版,最新版的安装包会自动识别目录,进行升级安装,不用卸载旧版本,亲自试过了。
x64dbg 下载
官网可以直接打开和下载,GitHub 可能打不开。
基础认识
上面的文章帖子看不懂没关系的,我也没完全看懂,大概了解一下,别因为参考文章没看明白就放弃了!
插件选一些自己需要的安装即可,比如:E_ApiBreak(常用断点)、ScyllaHide(反反调试)、x64dbg_tol(中文搜索支持)。
开始
关键操作点:
- 查壳(简单壳可尝试脱壳,高强度壳咱一般人干不过)
- 设置反反调试,程序能调试运行,事半功倍(通常用不到)
- 设置弹窗断点,定位弹窗入口
- 调用堆栈定位,找出程序级别调用弹窗的关键代码
查壳
尽可能在官方正式渠道下载查壳工具,最好是在虚拟机内操作,避免破解第一步就中木马病毒!
坛内搜索 “查壳”,看看坛友们基本用哪些个查壳工具,自己选一两个。
坛内爱盘工具包内基本都有提供下载 爱盘
查壳结果:xshell.exe、xftp.exe 目前都没有加壳
设置反反调试(目前没有反调试,不用管这一步)
程序启动调试时偶尔会出现报错停止运行,这应该不是反调试,重新加载多试几次是可以正常调试的。
安装 ScyllaHide(反反调试) 插件
安装不上插件也没关系,只是不能调试定位,但还可以通过搜索关键词字符串定位关键代码。先按 F9 让程序运行到入口(EntryPoint)处,再按 Shift+D,在当前模块中搜索字符串。比如本次修改,搜索 exit,就能直接定位到关键代码。
配置插件参数
因为软件是 32 位的,所以打开 x32dbg.exe, 配置插件参数,运行调试。
VMProtect x86/x64
Obsidium x86/x64
Themida x86/x64
上面三个配置对本次修改的程序都可以反反调试,主要区别在于内部的配置项勾选。
具体配置项名称含义和作用,可以参考 ScyllaHide 在 GitHub 上的 说明文档,英文的,想要深入了解反反调试的可以研究一下。由于本人也是业余选手,就没仔细研究了。
VMProtect x86/x64 勾选的配置项在三个里面最少,且能正常运行调试,本次参数配置就选它了。确认后会提示重新载入目标程序后生效。
设置弹窗断点
参考站内 去除WinRAR广告弹窗 帖子,大概知道了弹窗函数是 CreateWindowExW,在 x32dbg 界面下方的命令输入框,输入 bp CreateWindowExW 回车即可打上断点。
如果以后遇到的弹窗不是 CreateWindowExW 这个弹窗断点,怎么办呢?那可以参考 E-ApiBreak 插件里面的 对话框,选择其他弹窗断点。具体函数概念和效果可以自行搜索了解一下。
E-ApiBreak 很实用,目前只找到 32 位的插件,64 位的就自己参照名称,手动下断点了。
E-ApiBreak 插件站内下载
调用堆栈定位
知道了弹窗函数入口,可以通过调用堆栈找到是哪里调用了这个 CreateWindowExW 窗口函数。
有个注意的事项, CreateWindowExW 窗口函数在程序中可能会被调用很多次,因为其他正常窗口加载也会调用这个函数。所以,断点设置之后,先禁用,等到程序进入主界面,在你准备退出程序之前,再启用断点,这样就能准确断在退出弹窗的调用链上。
在退出程序之前,如果程序界面卡着没反应,查看是否卡在断点代码了,点击 → 运行即可,因为在手动退出之前的断点不在修改范围。点击关闭按钮,或命令行输入 exit 之后的断点才是要关注的地方。(可能有人会卡在这,不知道怎么回事)
点击 → 运行有时候会出现主线程 Suspended 暂停的情况(具体原因目前也不清楚),可到线程面板里面,手动把主线程 恢复线程,就可以继续调试了。
准备退出程序之前,启用窗口函数断点,退出程序,发现确实断在了 CreateWindowExW 窗口函数,堆栈调用信息如下:
堆栈最上方自然是 CreateWindowExW 入口,往下找到最近一次主程序调用点 xshell.008F5D85,可能你的地址不一定是这个,只要前面是 xshell 主程序标识即可。
双击 xshell.008F5D85 即可跳转过去,F2 打上断点,点击 → 运行,发现弹窗出现几秒消失后才进到断点,说明断点位置靠后了。
在断点的上方发现一个 call 调用,有 NSLICENSE_PersonalNotify 字样,运气不错,这里很可能就是弹窗代码。
在 call 上方的任意一个 push 按 F2 下断点,比如在 push 0 处。
停止本次调试,点击重新运行,发现好多不需要关注的 CreateWindowExW 断点,先把 CreateWindowExW 断点禁用,在退出之前再启用。(别被 CreateWindowExW 断点一会禁用一会启用搞晕了,你可以试着一直启用,你就会发现问题)
重新运行,退出程序,调试断在了 CreateWindowExW 调用之前,可以逐步调试,确认 call 函数内部是否为创建窗口相关的代码。
由于 call 里面的代码实在是太多了,一步一步看下去头都晕了。后来才发现代码都不在 xshell 主程序,其实不用细看了。
直接点 运行到用户代码 按钮(→👨💼),程序回到了 xshell.008F5D85,即 call 的下一行,说明整个 call 都是在系统代码层面创建弹出窗口。
关键操作来了,只要跳过这个 call 相关的代码,直接走到 Exit xshell 对应的地址 008F5D88,就可以干掉弹窗了。
其实弹窗代码上方正好有一个判断,je xshell.8F5D88,你的反编译地址可能不一样,但 je xshell.XXXXXX 操作是一样的。(前提是都用的 x64dbg,用 OD、IDA 可能反汇编指令会不同,但最终效果是一样的)
Xftp.exe 的关键代码定位,也是这个流程。
尝试修改
在 je xshell.8F5D88 这一行,右键-汇编,把 je xshell.8F5D88 改成 jmp xshell.8F5D88。选项可以参照勾选。确认修改。后续修改框还在,是改别的内容,不用管了,取消。
代码界面在刚修改的 jmp xshell.8F5D88 按 G 转为流程图看修改(G切换流程图或代码),因为主程序还没有打补丁,流程图还是原本的。改成 jmp 之后,就只有一条指向 Exit xshell 的路线了,修改还是比较完好的,改动很少。
Xftp.exe 的关键修改:
打补丁
在代码修改行(代码区域都行),右键-补丁,选中主程序 xshell.exe 的修改,这里只有一个补丁。
注意,修补文件为 xshell.exe,需要先手动备份一份,以便出错后重新来过,不用重装软件。
直接修补 xshell.exe 会报错,因为程序正在运行调试,那就选择修补刚备份的程序文件,自己清楚哪个是修补之后的即可。
修补后,停止调试,把原本的 xshell.exe 改为 xshell.exe.bak ,修补的主程序文件改为 xshell.exe 。
Xftp.exe 打补丁方法流程也是一样的。注意备份!
验证效果
点关闭按钮退出,或是在命令行输入 exit,退出后,没有再弹窗提示,任务管理器里面的进程正常退出,修改完成。
手动修补二进制文件
不用依赖补丁文件,自己手动修补。
前提是软件版本得一致!如果版本不同,请自行按帖子步骤操作。
010Editor 工具破解可参考站内 010 Editor 快速破解方法
附赠 010EditorWin64Portable16.0.2 便携版特征码修补
修补 010EditorWin64Portable\AppData 目录下的 010Editor.exe
7410B813010000 -> 9090B8DB000000
https://www.sweetscape.com/download/010EditorWin64Portable.zip
010EditorWin64Portable16.0.2.zip SHA256
37539f0b0b6d28f4d62eaef0872afc062bb90b9a2ad066eed98c236d568417ae
把 010EditorWin64Portable\AppData 目录下的 010Editor.exe 复制一个副本,修补副本之后,再把副本重命名为主程序名称 010Editor.exe 即可。
附赠结束,回到正题
注意先备份!010Editor 打开 xshell.exe,全局搜索 74 11 6A 00 6A 07 6A 01(别复制符号),把开头的 74 改成 EB 保存即可,注意把输入法设置为大写。为什么搜索这么长?确保全局只搜到一处,精确定位。
修改前后对比,整个程序文件就一处不同。
跟打补丁之后的程序文件是一样的,运行正常,退出没有弹窗。
Xftp.exe 手动修补:
注意先备份!
010Editor 打开 Xftp.exe,全局搜索 75 10 6A 00 6A 07 50 6A,把开头的 75 改为 EB 保存即可。
更新-20240601
7.0.0134 及之后的版本,去除启动前授权弹窗和强制更新。
去除启动前弹窗
bp CreateWindowExW 设置弹窗函数断点
- 点 → 运行按钮或按F9,直到弹窗出现(出现点不动的情况,到线程列表把主线程恢复运行)
- 调用堆栈找到最近一次主程序调用入口
手动修补(注意先备份再修补)
Xshell.exe
75 45 6A 07 6A -> EB 45 6A 07 6A
74 E3 6A 01 6A -> EB E3 6A 01 6A
Xftp.exe
75 45 6A 07 6A -> EB 45 6A 07 6A
74 E3 6A 01 6A -> EB E3 6A 01 6A
没错,两个主程序的修补字节码是一样的。
去除强制更新
目前是半年就要求强制更新了,有点离谱了!
- 修改系统本地时间到一年后
bp CreateWindowExW 设置弹窗函数断点
- 点 → 运行按钮或按F9,直到强制更新弹窗出现(出现点不动的情况,到线程列表把主线程恢复运行)
- 调用堆栈找到最近一次主程序调用入口
追码定位
检查强制更新 CheckLicenseAndPackage
检查版本更新 CheckLiveUpdate
手动修补(注意先备份再修补)
修补 Xshell、Xftp 安装目录下的 nslicense.dll 和 nsutil2.dll,注意!Xshell、Xftp 都要修补,一样的操作,别只修补了 Xshell 就完事了!
nslicense.dll
0F 85 BA 00 00 00 57 56 -> E9 BB 00 00 00 90 57 56
nsutil2.dll
0F 84 89 04 00 00 50 -> E9 8A 04 00 00 90 50
更新-20250321
新增了 7.0.x、8.0.x 版本去除授权登录弹窗、强制更新及终止支持弹窗提示,使用较为通用的修补方式
理论上对应版本的修补可以通用,如果修补完报错不能正常运行,先对比一下改动的地方
如果对比改动没问题,则可能是这个新版本改动了这一块的代码逻辑,导致特征码变化了
需要自己尝试按照帖子步骤自己尝试动态调试追码定位,或者静态分析代码流程图猜测验证关键位置
7.0 终止支持弹窗
7.0 版本终止支持了,还弹窗跳转官网提示下载新的8.0版本
弹窗提示时间判断,读取注册表计算机\HKEY_CURRENT_USER\SOFTWARE\NetSarang\Xshell\7\LiveUpdate
把安装目录下的LiveUpdate.exe改名即可
7.0.x
7.0.x 版本去除授权登录弹窗、强制更新及终止支持弹窗提示
适用于 Xshell-7.0.x、Xftp-7.0.x
注意!Xshell 和 Xftp 都要修补!
【去除授权登录弹窗】需要用邮箱验证登录,目前可以用临时邮箱进行授权
Xshell.exe & Xftp.exe
75 45 6A 07 6A -> EB 45 6A 07 6A(跟8.0的特征码有一个大版本号数值差异)
74 E3 6A 01 6A -> EB E3 6A 01 6A
注意:
Xshell.exe、Xftp.exe 两个主程序都要进行上面两个特征码修补,并不是 Xshell.exe 对应第一行,Xftp.exe 对应第二行;
修补的是安装目录下的主程序,文件修补完成后建议对比一下区别,避免有时候手抖或光标乱跳改错
【去除强制更新弹窗】每半年就要求强制升级版本,否则不让用
nslicense.dll(可以用 IDA 静态分析 CheckLicenseAndPackage 函数)
0F 85 BA 00 00 00 57 56 -> E9 BB 00 00 00 90 57 56
【去除检查更新】也可以在菜单栏“工具-选项-更新”关闭检查更新
【去除终止支持弹窗提示】不走调用 LiveUpdate.exe 检查更新的代码分支
nsutil2.dll(可以用 IDA 静态分析 CheckLiveUpdate 函数)
0F 84 89 04 00 00 50 -> E9 8A 04 00 00 90 50(跟8.0的特征码有差异)
【去除其他可能隐藏的弹窗提示】可选操作,把检查更新的程序干掉
LiveUpdate.exe -> LiveUpdate.exe0000
8.0.x
8.0.x 版本去除授权登录弹窗、强制更新及终止支持弹窗提示
适用于 Xshell-8.0.x、Xftp-8.0.x
注意!Xshell 和 Xftp 都要修补!
【去除授权登录弹窗】需要用邮箱验证登录,目前可以用临时邮箱进行授权
Xshell.exe & Xftp.exe
75 45 6A 08 6A -> EB 45 6A 08 6A
74 E3 6A 01 6A -> EB E3 6A 01 6A
【去除启动时检查更新及系统通知】只有 8.0.x 版本需要修补
Xshell.exe & Xftp.exe
85C074146A006A00 -> 85C0EB146A006A00
(特征码有无空格分隔,复制到 010Editor 都可以查找到)
注意:
Xshell.exe、Xftp.exe 两个主程序都要进行上面两个特征码修补,并不是 Xshell.exe 对应第一行,Xftp.exe 对应第二行;
修补的是安装目录下的主程序,文件修补完成后建议对比一下区别,避免有时候手抖或光标乱跳改错
【去除强制更新弹窗】每半年就要求强制升级版本,否则不让用
nslicense.dll(可以用 IDA 静态分析 CheckLicenseAndPackage 函数)
0F 85 BA 00 00 00 57 56 -> E9 BB 00 00 00 90 57 56
【去除检查更新】也可以在菜单栏“工具-选项-更新”关闭检查更新
【去除终止支持弹窗提示】不走调用 LiveUpdate.exe 检查更新的代码分支
nsutil2.dll(可以用 IDA 静态分析 CheckLiveUpdate 函数)
0F 84 86 00 00 00 50 -> E9 87 00 00 00 90 50
【去除其他可能隐藏的弹窗提示】可选操作,把检查更新的程序干掉
LiveUpdate.exe -> LiveUpdate.exe0000
更新-20251010
去除 8.0.x 版本启动时检查更新和系统通知
8.0.x 版本在启动时会根据设置的周期(默认是7天)定期检测更新,有版本更新则会弹出提示
手动关闭
可以在设置里面关闭检测更新和发送通知
或者也可以把操作系统的通知关闭
修补程序
7.0.x 版本启动时检测更新在 CheckLiveUpdate 业务逻辑当中,之前去掉检查更新已经修补了
8.0.x 版本启动时检测更新使用新的方法,需要重新修补
使用微软官方的进程监视器 Process Monitor 监测程序启动时的注册表操作,可以找到检测更新周期关联的注册表计算机\HKEY_CURRENT_USER\Software\NetSarang\Xshell\8\LiveUpdate
定位关键位置
x32dbg 打开安装目录下的主程序 Xshell.exe,F9 运行到“入口断点”,在汇编代码区域,右键-搜索-所有用户模块-字符串,搜索“LastUpdateCheck”,双击进入第一个结果,往上找到函数开头位置,右键查找引用,找到 NsIsUpdateIntervalPassed 方法,看方法命名猜测是“判断更新周期是否已经过去了(周期是否到期)”,在这个方法内部任意一行 F2 打上断点,继续 F9 运行直到进入该方法断点,查看调用堆栈,找到主程序调用这个方法的代码位置,这个位置后面会根据方法返回结果进行跳转,修补的关键就是跳转到“周期没到期”的逻辑分支即可。
修补特征码
只有 8.0.x 版本需要修补
Xshell.exe、Xftp.exe
85C074146A006A00 -> 85C0EB146A006A00
具体定位细节
搜索关键字符串
查找函数引用
动图太大,传不上来了。
主要是找到函数开头位置,右键查找引用即可。
关键字符串在函数内部,往上找函数开头位置。
查看调用堆栈定位关键位置
更新-20251114
编写自动修补二进制脚本
Xshell、Xftp 两个程序要修补的文件太多,每次升级都要手动逐个修补,效率太低!!高低得写个 PowerShell 自动修补脚本,快速自动完成修补,岂不美哉!
cmd 执行 notepad,打开记事本空白文档,复制脚本内容粘贴进去
Ctrl + S 保存,设置文件后缀名为.ps1,编码格式指定为ANSI
脚本注意事项:
- Windows PowerShell 脚本默认不让执行,需要修改执行策略(当前用户有效):Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
- PowerShell 5.1 版本对 UTF-8 中文支持不友好,中文解析报错,导致脚本不能正常执行,所以脚本编码使用的是 ANSI
# 默认情况下,Windows PowerShell 的执行策略为 Restricted,不允许运行任何脚本。
# 按住 Shift,右键-在此处打开 PowerShell 窗口
# 查看当前执行策略:Get-ExecutionPolicy
# 修改执行策略(当前用户有效):Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
# 确认执行策略为 RemoteSigned,选中这个脚本文件,右键-使用 PowerShell 运行
# PowerShell 5.1 版本对 UTF-8 中文支持不友好,中文解析报错,导致脚本不能正常执行,所以脚本编码使用的是 ANSI
# 查看版本:Get-Host | Select-Object Version
# 脚本自动修补只对【家庭/学校免费版】有效,其他专业收费版本无效
# https://www.xshell.com/zh/free-for-home-school/
# 教程出处:https://www.52pojie.cn/thread-1714055-1-1.html
function Repair-BinaryFile {
param(
[string]$FilePath,
[string]$SearchHex,
[string]$ReplaceHex,
[switch]$ReplaceAll
)
# 验证文件存在
if (-not (Test-Path $FilePath)) {
#Write-Error "文件不存在: $FilePath"
Write-Host "文件不存在: $FilePath" -ForegroundColor Cyan
return
}
Write-Host "正在修补文件: $FilePath" -ForegroundColor Green
# 创建备份
$backupPath = Create-Backup -FilePath $FilePath
# 转换十六进制为字节数组
$SearchHex = $SearchHex -replace '\s',''
$ReplaceHex = $ReplaceHex -replace '\s',''
if ($SearchHex.Length -ne $ReplaceHex.Length) {
throw "搜索和替换的十六进制字符串长度必须相同"
}
if ($SearchHex.Length % 2 -ne 0) {
throw "十六进制字符串长度必须是偶数"
}
$searchBytes = for ($i = 0; $i -lt $SearchHex.Length; $i += 2) {
[Convert]::ToByte($SearchHex.Substring($i, 2), 16)
}
$replaceBytes = for ($i = 0; $i -lt $ReplaceHex.Length; $i += 2) {
[Convert]::ToByte($ReplaceHex.Substring($i, 2), 16)
}
Write-Host "开始直接修补文件..." -ForegroundColor Yellow
Write-Host "搜索: $SearchHex" -ForegroundColor Cyan
Write-Host "替换: $ReplaceHex" -ForegroundColor Cyan
Write-Host "模式: $(if ($ReplaceAll) { '替换所有匹配项' } else { '替换第一个匹配项' })" -ForegroundColor Cyan
# 执行直接替换
$matchesFound = SearchAndReplaceBinary -FilePath $FilePath -SearchBytes $searchBytes -ReplaceBytes $replaceBytes -ReplaceAll:$ReplaceAll
if ($matchesFound -gt 0) {
Write-Host "修补完成! 共替换了 $matchesFound 个匹配项" -ForegroundColor Green
Write-Host "原文件备份: $backupPath" -ForegroundColor Cyan
} else {
Write-Host "未找到匹配的字节序列" -ForegroundColor Yellow
<#
if (Test-Path $backupPath) {
Remove-Item $backupPath -Force
Write-Host "原文件备份已删除: $backupPath" -ForegroundColor Cyan
}
#>
}
}
function SearchAndReplaceBinary {
param(
[string]$FilePath,
[byte[]]$SearchBytes,
[byte[]]$ReplaceBytes,
[bool]$ReplaceAll
)
$searchLength = $SearchBytes.Length
$matchesFound = 0
# 以读写方式打开文件
$stream = [System.IO.File]::Open($FilePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite)
try {
$buffer = New-Object byte[] (64KB) # 64KB缓冲区足够
$fileLength = $stream.Length
$position = 0
while ($position -lt $fileLength) {
# 计算要读取的字节数
$bytesToRead = [Math]::Min($buffer.Length, $fileLength - $position)
# 读取数据到缓冲区
$bytesRead = $stream.Read($buffer, 0, $bytesToRead)
if ($bytesRead -eq 0) { break }
# 在缓冲区中搜索
for ($i = 0; $i -le $bytesRead - $searchLength; $i++) {
$isMatch = $true
for ($j = 0; $j -lt $searchLength; $j++) {
if ($buffer[$i + $j] -ne $SearchBytes[$j]) {
$isMatch = $false
break
}
}
if ($isMatch) {
# 计算文件中的绝对位置
$matchPosition = $position + $i
# 定位并写入替换数据
$stream.Seek($matchPosition, [System.IO.SeekOrigin]::Begin) | Out-Null
$stream.Write($ReplaceBytes, 0, $ReplaceBytes.Length)
Write-Host "在位置 0x$($matchPosition.ToString('X8')) 找到匹配并替换" -ForegroundColor Green
$matchesFound++
# 恢复读取位置
$stream.Seek($position + $bytesRead, [System.IO.SeekOrigin]::Begin) | Out-Null
if (-not $ReplaceAll) {
return $matchesFound
}
}
}
$position += $bytesRead
# 显示进度
$percentComplete = [Math]::Round(($position / $fileLength) * 100, 2)
Write-Progress -Activity "处理文件" -Status "已扫描 $position/$fileLength 字节" -PercentComplete $percentComplete
}
Write-Progress -Activity "处理文件" -Completed
}
finally {
$stream.Close()
}
return $matchesFound
}
function Create-Backup() {
param(
[string]$FilePath
)
#$backupPath = "$FilePath.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
$backupPath = Get-Backup-Path -FilePath $FilePath
if (Test-Path $backupPath) {
Write-Host "原文件备份已存在: $backupPath" -ForegroundColor Cyan
return $backupPath
}
Write-Host "正在创建备份..." -ForegroundColor Yellow
Copy-Item $FilePath $backupPath -Force
Write-Host "备份已创建: $backupPath" -ForegroundColor Green
return $backupPath
}
function Get-Backup-Path() {
param(
[string]$FilePath
)
return $FilePath + "0000";
}
function Get-Install-Path-Parent() {
#$xshellExe = Get-Command -Name "Xshell" | Select-Object -ExpandProperty Source
$xshellExe = (Get-Command -Name "Xshell").Source # D:\Program Files (x86)\NetSarang\Xshell 8\Xshell.exe
if ($xshellExe) {
$installDir = Split-Path -Path $xshellExe -Parent # D:\Program Files (x86)\NetSarang\Xshell 8
$parentDir = Split-Path -Path $installDir -Parent # D:\Program Files (x86)\NetSarang
} else {
$xshellExe = $pwd
#Write-Host "脚本所在路径:$pwd" -ForegroundColor Gray
if ($xshellExe) {
$installDir = Split-Path -Path $xshellExe # D:\Program Files (x86)\NetSarang
#Write-Host "installDir:$installDir" -ForegroundColor Gray
$parentDir = $installDir
}
}
if (-not ($parentDir -match "NetSarang")) {
throw "请确认系统环境变量 Path 中存在程序安装路径,或者将脚本文件放在 Xshell 的安装目录下,并确保安装目录上级为 NetSarang"
}
# 最终返回程序安装父级目录:D:\Program Files (x86)\NetSarang\
return $parentDir + "\"
}
function Show-ExitMessage {
Write-Host "脚本执行完成,按任意键退出..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
# 设置错误处理,报错后不再往下执行
#$ErrorActionPreference = "Stop"
# 修补程序
$xshell7 = $true
$xftp7 = $true
$xshell8 = $true
$xftp8 = $true
# 主程序入口【需要放在函数定义之后】
try {
$installPath = Get-Install-Path-Parent
Write-Host "程序安装父级目录:$installPath" -ForegroundColor Gray
# Xshell 7.0.x
if ($xshell7) {
$fPath = $installPath + "Xshell 7\"
$fName = "Xshell.exe"
# 去除授权登录弹窗
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "75 45 6A 07 6A" -ReplaceHex "EB 45 6A 07 6A"
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "74 E3 6A 01 6A" -ReplaceHex "EB E3 6A 01 6A"
$fName = "nslicense.dll"
# 去除强制更新弹窗
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "0F 85 BA 00 00 00 57 56" -ReplaceHex "E9 BB 00 00 00 90 57 56"
$fName = "nsutil2.dll"
# 去除检查更新、终止支持弹窗提示
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "0F 84 89 04 00 00 50" -ReplaceHex "E9 8A 04 00 00 90 50"
}
# Xftp 7.0.x
if ($xftp7) {
$fPath = $installPath + "Xftp 7\"
$fName = "Xftp.exe"
# 去除授权登录弹窗
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "75 45 6A 07 6A" -ReplaceHex "EB 45 6A 07 6A"
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "74 E3 6A 01 6A" -ReplaceHex "EB E3 6A 01 6A"
$fName = "nslicense.dll"
# 去除强制更新弹窗
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "0F 85 BA 00 00 00 57 56" -ReplaceHex "E9 BB 00 00 00 90 57 56"
$fName = "nsutil2.dll"
# 去除检查更新、终止支持弹窗提示
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "0F 84 89 04 00 00 50" -ReplaceHex "E9 8A 04 00 00 90 50"
}
# Xshell 8.0.x
if ($xshell8) {
$fPath = $installPath + "Xshell 8\"
$fName = "Xshell.exe"
# 去除授权登录弹窗
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "75 45 6A 08 6A" -ReplaceHex "EB 45 6A 08 6A"
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "74 E3 6A 01 6A" -ReplaceHex "EB E3 6A 01 6A"
# 去除启动时检查更新及系统通知
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "85C074146A006A00" -ReplaceHex "85C0EB146A006A00" # 空格分隔符不影响
$fName = "nslicense.dll"
# 去除强制更新弹窗
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "0F 85 BA 00 00 00 57 56" -ReplaceHex "E9 BB 00 00 00 90 57 56"
$fName = "nsutil2.dll"
# 去除检查更新、终止支持弹窗提示
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "0F 84 86 00 00 00 50" -ReplaceHex "E9 87 00 00 00 90 50"
}
# Xftp 8.0.x
if ($xftp8) {
$fPath = $installPath + "Xftp 8\"
$fName = "Xftp.exe"
# 去除授权登录弹窗
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "75 45 6A 08 6A" -ReplaceHex "EB 45 6A 08 6A"
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "74 E3 6A 01 6A" -ReplaceHex "EB E3 6A 01 6A"
# 去除启动时检查更新及系统通知
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "85C074146A006A00" -ReplaceHex "85C0EB146A006A00" # 空格分隔符不影响
$fName = "nslicense.dll"
# 去除强制更新弹窗
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "0F 85 BA 00 00 00 57 56" -ReplaceHex "E9 BB 00 00 00 90 57 56"
$fName = "nsutil2.dll"
# 去除检查更新、终止支持弹窗提示
Repair-BinaryFile -FilePath ($fPath + $fName) -SearchHex "0F 84 86 00 00 00 50" -ReplaceHex "E9 87 00 00 00 90 50"
}
} catch {
Write-Host "错误: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "堆栈跟踪: $($_.Exception.StackTrace)" -ForegroundColor Red
} finally {
Show-ExitMessage
}
参考