本着固件更新了就强迫症想更新的态度,看到台湾WH 1000Xm2有了新的固件2.0.1,然后就想去更新版本,查看固件1.0.5并无最新固件,于是开始了折腾之路;本文教程内容源自《魔改Sony Headphone Connect强制升级国行WH-1000XM2

警告

这篇博文讲的任何内容并不受Sony官方支持,本人也并不对此造成的保修作废、耳机变砖、耳机电池爆炸等等等等的任何问题负责。尝试本博文的方法带来的任何后果自负。本文所提供的内容仅适用于WH-1000XM2,其余型号请自行研究。本文所提供的内容针对Sony Headphone Connect 3.2.0版本。

准备工具

  • Apktool (旧版的可能有问题,建议最新的2.3.3)
  • 一个靠谱的文本编辑器

我的研究过程

提醒:你用别的方法反编译后类、方法、命名空间等的命名不一定与我的相同,建议善用文本搜索

TL,DR:如果你不想看我的研究过程只想看教程或者下载我打包的文件,请跳至页尾

首先上网找了个在线反编译的网站反编译了一遍,下到本地。用IJ尝试重新编译成APK,发现失败,于是放弃用在线的反编译工具,仅把这反编译的代码作为参考。然后用Apktool拆包了一下,看到一堆smali指令码,也没什么头绪从何入手。

然后我在手机那抓了个包,发现软件启动的时候请求https://info.update.sony.net/HP001/MDRID289202/info/info.xml,居然返回的是404。不负责任的猜测了一下MDRID289202是机身的内部型号(包括版本号)。全局搜索了一下源码发现info.update.sony.netcom.sony.songpal.mdr.automagic.AutoMagicManager里面。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
private URL m9721d(String str, String str2) {
    String str3 = "info.update.sony.net";
    if (str == null) {
        throw new IllegalStateException(String.format(Locale.getDefault(), "%s:code=%d", new Object[]{"AutoMagicManagerErrorDomain", Integer.valueOf(AutoMagicError.NoCategoryID.ordinal())}));
    } else if (str2 == null) {
        throw new IllegalStateException(String.format(Locale.getDefault(), "%s:code=%d", new Object[]{"AutoMagicManagerErrorDomain", Integer.valueOf(AutoMagicError.NoServiceID.ordinal())}));
    } else {
        try {
            return new URL(String.format(Locale.getDefault(), "http://%s/%s/%s/info/%s", new Object[]{str3, str, str2, "info.xml"}));
        } catch (Throwable e) {
            SpLog.m12039a(f5487a, e);
            return null;
        }
    }
}

这玩意用了两个字符串类的参数,然后在URL里面就是HP001和MDRID289202,于是猜测假如我篡改这个参数的获取程序使其获取成国外行货的代码,大概就能强制更新了(前提是硬件完全一样)。顺藤摸瓜检查m9271d这方法的调用,查到了保存这个参数的类com.sony.songpal.mdr.application.p061b.C1702a

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void m7942a(C2503b c2503b) {
    if (Command.fromByteCode(c2503b.m11224a()) == Command.UPDT_RET_PARAM) {
        cm cmVar = (cm) c2503b;
        if (cmVar.m11579e() != this.f4438n) {
            SpLog.m12040b(f4425a, this.f4438n + " expected, but received " + cmVar.m11579e());
            return;
        }
        af f = cmVar.m11580f();
        switch (cmVar.m11579e()) {
            case CATEGORY_ID:
                this.f4429e = ((ag) f).m11716a();
                break;
            case SERVICE_ID:
                this.f4430f = ((ag) f).m11716a();
                break;
            case NATION_CODE:
                this.f4431g = ((ag) f).m11716a();
                break;
            case LANGUAGE:
                this.f4432h = ((ag) f).m11716a();
                break;
            case SERIAL_NUMBER:
                this.f4433i = ((ag) f).m11716a();
                break;
            case BATTERY_POWER_THRESHOLD:
                this.f4434j = ((ae) f).m11711a();
                break;
            default:
                throw new IllegalStateException("Invalid inquired type " + cmVar.m11579e() + " was expected");
        }
        if (this.f4437m != null) {
            this.f4437m.countDown();
        }
    }
}

SMALI指令码对应如下(com.sony.songpal.mdr.application.b.a

01
02
03
04
05
06
07
08
09
10
11
.line 156
    :pswitch_1
    check-cast v0, Lcom/sony/songpal/tandemfamily/message/mdr/param/ag;
    invoke-virtual {v0}, Lcom/sony/songpal/tandemfamily/message/mdr/param/ag;->a()Ljava/lang/String;
    move-result-object v0
    iput-object v0, p0, Lcom/sony/songpal/mdr/application/b/a;->f:Ljava/lang/String;
    goto :goto_1

把这玩意的SERVICE_ID那改成外国行货的ID就好了。比如

1
2
3
case SERVICE_ID:
    this.f4430f = "MDRID123456";
    break;

但问题来了,不知道外国行货的ID是多少,于是@Lucifer提出了可能ID是连着的,于是我试了试MDRID289200,确认有这个型号。于是修改Apktool解包的SMALI为

01
02
03
04
05
06
07
08
09
10
11
12
13
.line 156
    :pswitch_1
    #check-cast v0, Lcom/sony/songpal/tandemfamily/message/mdr/param/ag;
    #invoke-virtual {v0}, Lcom/sony/songpal/tandemfamily/message/mdr/param/ag;->a()Ljava/lang/String;
    #move-result-object v0
    const-string v0, "MDRID289200"
    iput-object v0, p0, Lcom/sony/songpal/mdr/application/b/a;->f:Ljava/lang/String;
    goto :goto_1

重新用Apktool打包,然后用自己的证书签名。传到手机那,成功检测到更新。然后发给勇士@Lucifer来试了一下(我有点怂不敢试,毕竟如果变砖了我寄回国内可是相当麻烦)。实测貌似没啥毛病,更新成了国外版本的2.0.1固件并且语音变成了英文。于是自己也试了一下,更新后貌似确实没啥毛病。

装回原版程序后再次用抓包抓了一下,发现请求的service id已经变成了MDRID289200。大概不用担心以后又变回国行固件的问题。

简要教程

  1. 用Apktool解包:apktool d a.apk(我为了方便文件名改成了a.apk),这一步会创建一个名为a的文件夹
  2. 进入文件夹,全局搜索invoke-virtual {v0}, Lcom/sony/songpal/tandemfamily/message/mdr/param/ag;->a()Ljava/lang/String;,打开包含这一行的文件。
  3. 跳至.line 156,把里面的内容修改成我上面所述的内容
  4. 重新打包:apktool b a -o b.apk,这会生成b.apk
  5. 给b.apk签名,这个网上都有教程我就不写了
  6. 把原本的App卸掉,安装这个APK,然后连接耳机,如果没毛病的话应该会见到底部的更新提示,按部就班更新就行
  7. 更新完后卸掉这个魔改的App,安装回原版的App,enjoy

我打包的程序

我知道肯定有人会懒得自己动手,于是就把自己打包的给发上来了。警告:仅适用于WH-1000XM2。

百度网盘:https://pan.baidu.com/s/1KrbW6yb0fJho4u7VjtGr0A,密码:5qgc。