Skip to content
View final2halo's full-sized avatar
  • Joined Jul 25, 2026

Block or report final2halo

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
final2halo/README.md

final2halo

把 FinalShell 保存的主机、文件夹层级、密码和私钥,搬到 HaloShell

在线使用:https://final2halo.com —— 也可以自己部署一份,见下方「自己部署」。

final2halo 界面预览:左侧是原样保留的 FinalShell 文件夹层级,右侧是该文件夹下的主机列表,密码默认遮住

纯前端。 没有后端,没有上传,没有埋点。选目录、解密、生成迁移包这三件事全部发生在你自己的浏览器里,断网也能用。

页面的 CSP 里写死了 connect-src 'none',意思是浏览器禁止这个页面发起任何 fetch / XHR / WebSocket。「不会上传」因此不只是一句承诺,而是浏览器强制执行的规则——就算有人往依赖链里塞了偷传代码,它也发不出去。

用法

  1. 打开 https://final2halo.com(或自己部署的地址)。

  2. 把 FinalShell 的数据目录拖进去,或点「选择文件夹」。

    系统 目录
    macOS ~/Library/FinalShell
    Windows FinalShell 安装目录
    Linux ~/.finalshell

    conn/ 子目录也能认,但那样读不到私钥和快捷命令——它们存在 config.json 里。

  3. 核对预览:左边是原样保留的文件夹层级,右边是该文件夹下的主机。

  4. 点「导出迁移包」,得到一个 JSON。

  5. 回到 HaloShell:先解锁保险箱(没解锁的话导入入口是灰的),再点主机列表顶部的「导入 / 导出」→「导入」,选择这个 JSON。

导入完请立刻删掉这个 JSON。 里面是明文密码和明文私钥——这是 HaloShell 明文迁移包的格式要求,不是本工具的选择。别发给别人,别放网盘。

迁移了什么

FinalShell HaloShell
conn/<id>/folder.json 的嵌套文件夹 hostGroups,父子层级原样保留
*_connect_config.json 的主机 hosts,含名称、地址、端口、用户名、备注、排序、最近连接时间
conection_type = 101(Windows 远程桌面) protocol: "rdp"
密码认证(authentication_type = 1 auth.kind = "password",明文进保险箱
私钥认证(authentication_type = 2 auth.kind = "privateKey",私钥与口令进保险箱
config.jsonsecret_key_list 保险箱里的 PEM 私钥
config.jsonquick_commands quickCommands,命名为 分组名/命令名,HaloShell 据此还原分类

没保存密码、或私钥找不到的主机,会落成「本机密钥代理」认证,并在界面上逐条列出来,导入后手动补。

没迁移什么

  • 端口转发:FinalShell 的 port_forwarding_list。真实样本里全为空,没有可验证映射的数据,所以宁可不写没验证过的转换代码。遇到非空配置时工具会明确提示。
  • 代理:同上,proxy_id 在样本里全是 "0"
  • 终端外观、编码、字体等偏好:HaloShell 的迁移包不带这些。
  • backup/deleted/ 目录:里面是历史版本和已删条目,会被主动跳过,免得把删掉的主机又导回来。

解密是怎么做的

FinalShell 把密码用 DES 加密后 base64 存在 JSON 里。密钥不是固定的——它从密文头部 8 个字节,经由 java.util.Random 这个特定的 48 位线性同余发生器派生,再取 MD5 的前 8 字节作为 DES 密钥。

算法来自 FinalShell 自身,最早由 jas502n/FinalShellDecodePass 逆向公开。本项目用 TypeScript 重新实现了 java.util.Random、MD5 和 DES,没有任何运行时依赖(浏览器的 WebCrypto 不支持单 DES)。

测试用的是公开测试向量:

UU8hWV51DmVNgmX/pUd0LlaEo53VTa6s  →  beac3d85988e

开发

npm install
npm run dev        # 本地开发
npm test           # 单元测试
npm run typecheck  # 类型检查
npm run build      # 产出 dist/

自己部署

这就是个静态站点,npm run build 出来的 dist/ 丢到任何地方都能跑。

Cloudflare Workers(官方站点用的就是这套):

npm run deploy          # 等价于 npm run build && wrangler deploy

仓库里的 wrangler.jsonc 配的是纯静态资源、不带 Worker 脚本——服务端压根不存在能碰到用户数据的代码路径。public/_headers 负责下发 CSP 和缓存策略。

官方站点用的是 Cloudflare 的 Workers Builds:在面板里连上这个仓库,构建命令填 npm run build、部署命令填 npx wrangler deploy,之后推 main 就自动发布。

Docker

docker build -t final2halo .
docker run -p 8080:80 final2halo

其他静态托管:直接传 dist/。部署在子路径下时构建要带上 base:

BASE_PATH=/final2halo/ npm run build

安全说明

  • 工具不发起任何网络请求。你可以在浏览器开发者工具的「网络」面板里自己确认,或者干脆断网使用。
  • 解密出来的密码默认在界面上是遮住的,需要逐个点「显示」。
  • 导出的迁移包含明文凭据。仓库的 .gitignore 已经拦掉 finalshell-migration*.json,但请仍然手动删除。
  • 这个工具只能解密你自己机器上的 FinalShell 配置。它不是破解工具——FinalShell 的这套加密对本地攻击者本来就不设防,密钥就藏在密文自己的头部里。
  • 官方部署只有 https://final2halo.com 一个地址。这类工具天然适合被仿冒成钓鱼站,看到别的域名请当心。

完整的威胁模型、以及漏洞报告方式,见 SECURITY.md

贡献

欢迎 Issue 和 PR。发 PR 前跑一遍 npm testnpm run typecheck

提 Issue 时不要贴真实的主机地址、密码或私钥,构造一份假数据复现就够了。安全漏洞请走 SECURITY.md 里的私下渠道,别开公开 Issue。

如果你手上有本项目没有覆盖的 FinalShell 配置——比如用到了端口转发或代理的主机——欢迎脱敏后提供样本结构。这两块目前没迁移,正是因为缺少可验证的真实数据。

许可

MIT

Popular repositories Loading

  1. final2halo final2halo Public

    把 FinalShell 的主机、文件夹层级和凭据迁移到 HaloShell。纯前端,数据不离开浏览器。

    TypeScript