You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:Android车机Car Choose App的HTTP请求MD5签名计算与原厂MCU获取

问题背景

我使用适配欧宝Astra J的Android车机系统,自带的Car Choose App通过国内服务器获取MCU固件、开机Logo及车机设置应用。此前误刷第三方MCU固件,导致车机按键映射异常(方向盘按键正常),俄罗斯经销商及4PDA、XDA等论坛均无法提供解决方案。

通过IDA分析Car Choose App、Proxymon抓取网络请求后,发现所有POST请求均携带MD5签名(sig字段)。根据反编译Java代码及论坛提示,sig由TreeMap参数拼接后加上固定字符串dfsgherthdfghkj5j6o78tdftyw4uyr计算MD5生成,但具体逻辑未完全明确。

需求

  1. MD5签名计算的详细步骤
  2. 可实现该计算的工具

MD5签名计算详细步骤

结合反编译代码逻辑,签名计算流程如下:

  1. 收集核心参数:整理当前请求的所有参数,注意sig和appid不需要加入签名计算(代码中先生成sig,最后才添加appid)
  2. 参数排序:将核心参数放入TreeMap,该集合会自动按键的字典序升序排列,排序规则是签名正确性的关键
  3. 拼接参数字符串:遍历排序后的参数,按键=值的格式拼接所有键值对,键值对之间无分隔符(例如level=7+language=de-DE拼接为level=7language=de-DE)
  4. 添加固定密钥:在拼接后的字符串末尾追加固定密钥:dfsgherthdfghkj5j6o78tdftyw4uyr
  5. 计算MD5哈希:对最终拼接字符串计算MD5哈希值,得到的32位小写十六进制字符串即为sig字段的值
  6. 组装请求参数:将生成的sig与固定appid=dfsgherthdfghkj6o78tdftyw4uyrtyj加入请求参数,发送POST请求

实现签名计算的工具(Python脚本)

以下脚本可直接实现上述签名逻辑,且已通过请求示例验证正确性:

import hashlib

def calculate_sig(params, secret_key="dfsgherthdfghkj5j6o78tdftyw4uyr"):
    # 按键的字典序排序参数
    sorted_params = sorted(params.items(), key=lambda x: x[0])
    # 拼接键值对字符串
    combined_str = ''.join([f"{key}={value}" for key, value in sorted_params])
    # 添加固定密钥
    combined_str += secret_key
    # 计算MD5并返回小写十六进制结果
    return hashlib.md5(combined_str.encode('utf-8')).hexdigest()

# 测试示例(对应请求示例中的参数)
if __name__ == "__main__":
    test_params = {
        "level": "7",
        "language": "de-DE",
        "remark": "2_203_81_8111_80_Ts9.4.3_11",
        "id": "11",
        "ratio": "0",
        "resourcesId": "10040312"
    }
    generated_sig = calculate_sig(test_params)
    print(f"生成的sig: {generated_sig}")
    # 预期输出:749f9e7c78c840e8e7ad7c0d5de81dfd

请求示例

POST http://api.mcu.cardoor.cn/move/mcu/queryRelationConfig

POST /move/mcu/queryRelationConfig HTTP/1.1
Host: api.mcu.cardoor.cn
Content-Type: application/json
Charset: UTF-8
Content-Length: 202
User-Agent: Dalvik/2.1.0 (Linux; U; Android 9; Octa - TS9 Build/OPM2.171019.012)
Connection: Keep-Alive
Accept-Encoding: gzip

{"appid":"dfsgherthdfghkj6o78tdftyw4uyrtyj","id":"11","language":"de-DE","level":"7","ratio":"0","remark":"2_203_81_8111_80_Ts9.4.3_11","resourcesId":"10040312","sig":"749f9e7c78c840e8e7ad7c0d5de81dfd"}

HTTP/1.1 200 OK
Server: Tengine
Content-Type: application/json;charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Date: Mon, 11 Jul 2022 22:43:50 GMT
Via: cache30.l2st4-5[50,0], cache7.de3[252,0]
Timing-Allow-Origin: *
EagleId: 4f85b19b16575794305394119e

{"code":"CD000001","msg":"处理成功","body":{"appRelationConfigList":[{"configJson":"","cipherStatus":"","level":"7","configId":"2","name":"A\\C Control","rank":100,"logo":"null","remark":"2_203_81_8111_80_Ts8_100","id":"10040315","superId":"100","type":1}]}}

反编译代码片段

Car Choose - 配置列表

public void hR(String str, String str2) {
        if (!this.gt) {
            iC(str, str2);
            return;
        }
        this.ic.setTitle(2131099702);
        this.ic.setMessage(getString(2131099696));
        this.ic.show();
        TreeMap treeMap = new TreeMap();
        treeMap.put("level", str);
        treeMap.put("language", this.language);
        treeMap.put("remark", str2);
        treeMap.put("id", this.gf);
        treeMap.put("ratio", this.gQ);
        treeMap.put("resourcesId", this.f0if);
        treeMap.put("sig", com.tw.carchoose.upgrade.a.c.q(com.tw.carchoose.upgrade.a.c.p(treeMap) + "dfsgherthdfghkj5j6o78tdftyw4uyr"));
        treeMap.put("appid", "dfsgherthdfghkj6o78tdftyw4uyrtyj");
        Log.e("gss", "params:" + treeMap.toString());
        com.tw.carchoose.upgrade.a.m.au("http://api.mcu.cardoor.cn/move/mcu/queryRelationConfig", treeMap, new C0028ac(this, str));
    }

Car Choose - 密码验证

public void hS(String str, String str2) {
        if (!this.gt) {
            iC(str, str2);
            return;
        }
        TreeMap treeMap = new TreeMap();
        treeMap.put("remark", str2);
        treeMap.put("password", this.hW);
        treeMap.put("id", this.gf);
        treeMap.put("resourcesId", this.f0if);
        treeMap.put("sig", com.tw.carchoose.upgrade.a.c.q(com.tw.carchoose.upgrade.a.c.p(treeMap) + "dfsgherthdfghkj5j6o78tdftyw4uyr"));
        treeMap.put("appid", "dfsgherthdfghkj6o78tdftyw4uyrtyj");
        Log.e("gss", "params:" + treeMap.toString());
        com.tw.carchoose.upgrade.a.m.au("http://api.mcu.cardoor.cn/move/mcu/checkPwd", treeMap, new C0029ad(this));
    }

Car Choose - 获取新版App

private void hW() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        View inflate = LayoutInflater.from(this).inflate(fE ? 2130903070 : 2130903071, (ViewGroup) null);
        this.fV = (Button) inflate.findViewById(2131296379);
        this.ii = (TextView) inflate.findViewById(2131296378);
        this.in = (TextView) inflate.findViewById(2131296328);
        this.ib = (ProgressBar) inflate.findViewById(2131296327);
        this.fV.setVisibility(8);
        this.ib.setVisibility(8);
        this.id = (LinearLayout) inflate.findViewById(2131296383);
        this.id.setVisibility(8);
        ((TextView) inflate.findViewById(2131296384)).setOnClickListener(new aj(this));
        ((TextView) inflate.findViewById(2131296385)).setOnClickListener(new ak(this));
        ((ImageView) inflate.findViewById(2131296316)).setOnClickListener(new al(this));
        this.fV.setOnClickListener(new am(this));
        if (this.gt) {
            TreeMap treeMap = new TreeMap();
            treeMap.put("plat", this.hY);
            treeMap.put("sig", com.tw.carchoose.upgrade.a.c.q(com.tw.carchoose.upgrade.a.c.p(treeMap) + "dfsgherthdfghkj5j6o78tdftyw4uyr"));
            treeMap.put("appid", "dfsgherthdfghkj6o78tdftyw4uyrtyj");
            com.tw.carchoose.upgrade.a.m.au("http://api.mcu.cardoor.cn/carchoose/getNew", treeMap, new an(this));
        } else {
            this.ii.setText(getText(2131099860));
        }
        this.hu = builder.create();
        this.hu.setCanceledOnTouchOutside(false);
        this.hu.setCancelable(false);
        this.hu.show();
        this.hu.getWindow().setContentView(inflate);
        this.hu.getWindow().setGravity(17);
    }

Car Choose - 获取新版Canbox

public void cB() {
        TreeMap treeMap = new TreeMap();
        treeMap.put("canbox_version", this.bO);
        treeMap.put("sig", com.tw.carchoose.upgrade.a.c.q(com.tw.carchoose.upgrade.a.c.p(treeMap) + "dfsgherthdfghkj5j6o78tdftyw4uyr"));
        treeMap.put("appid", "dfsgherthdfghkj6o78tdftyw4uyrtyj");
        com.tw.carchoose.upgrade.a.m.au("http://api.mcu.cardoor.cn/canbox/getNew", treeMap, new A(this));
    }

Car Choose - 单个MCU查询

public void de(String str, AbstractC0035b abstractC0035b) {
        if (!com.tw.carchoose.upgrade.a.c.r(this.cs)) {
            Toast.makeText(com.tw.carchoose.upgrade.a.k.o, this.cs.getString(2131099829), 0).show();
            if (this.cj == null) {
                return;
            }
            this.cj.dT();
            return;
        }
        this.cj = abstractC0035b;
        TreeMap treeMap = new TreeMap();
        treeMap.put("mcu_version", str);
        treeMap.put("sig", com.tw.carchoose.upgrade.a.c.q(com.tw.carchoose.upgrade.a.c.p(treeMap) + "dfsgherthdfghkj5j6o78tdftyw4uyr"));
        treeMap.put("appid", "dfsgherthdfghkj6o78tdftyw4uyrtyj");
        com.tw.carchoose.upgrade.a.m.au("http://api.mcu.cardoor.cn/move/mcu/singlemcu", treeMap, new D(this));
    }

Car Choose - MCU调试日志上传

private void rC() {
        try {
            rF();
            String str = this.path + "/MCUdebug/";
            File file = new File(str);
            if (!file.exists()) {
                file.mkdir();
            }
            File file2 = new File(str + this.pC + "_" + new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) + "_MCUdebug_log.txt");
            FileOutputStream fileOutputStream = new FileOutputStream(file2);
            fileOutputStream.write(this.pK.getText().toString().getBytes());
            fileOutputStream.close();
            if (!com.tw.carchoose.upgrade.a.c.r(this)) {
                this.pE.setText(getResources().getString(2131099667));
                this.pE.show();
                return;
            }
            TreeMap treeMap = new TreeMap();
            treeMap.put("file", file2.getPath());
            treeMap.put("mcu_version", this.pC);
            treeMap.put("sys_version", this.pF);
            treeMap.put("sig", com.tw.carchoose.upgrade.a.c.q(com.tw.carchoose.upgrade.a.c.p(treeMap) + "dfsgherthdfghkj5j6o78tdftyw4uyr"));
            treeMap.put("appid", "dfsgherthdfghkj6o78tdftyw4uyrtyj");
            new b(this, treeMap, file2).start();
        } catch (Exception e) {
            this.pE.setText(getResources().getString(2131099668));
            this.pE.show();
            Log.e("MCUActivity", Log.getStackTraceString(e));
        }
    }

Car Choose - 主更新函数

public void df(String str, String str2, String str3, AbstractC0035b abstractC0035b, String str4, String str5) {
        if (com.tw.carchoose.upgrade.a.k.n) {
            Log.e("gss", "apkId " + str);
        }
        this.cn = false;
        this.id = str;
        if (!com.tw.carchoose.upgrade.a.c.r(this.cs)) {
            Toast.makeText(com.tw.carchoose.upgrade.a.k.o, this.cs.getString(2131099829), 0).show();
            if (abstractC0035b == null) {
                return;
            }
            abstractC0035b.dT();
            return;
        }
        this.cj = abstractC0035b;
        TreeMap treeMap = new TreeMap();
        if (!str4.equals("") && !str5.equals("")) {
            try {
                treeMap.put("longitude", URLDecoder.decode(str4, "UTF-8"));
                treeMap.put("latitude", URLDecoder.decode(str5, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        treeMap.put("sourceId", "");
        treeMap.put("sourceList", "[{'sourceId':'" + str + "','type':'" + str2 + "','version':'" + str3 + "'}]");
        treeMap.put("imeiId", com.tw.carchoose.upgrade.a.d.u());
        treeMap.put("oemId", com.tw.carchoose.upgrade.a.d.v());
        treeMap.put("version", "0");
        treeMap.put("group", "1");
        treeMap.put("requireURLDecoderParam", "true");
        treeMap.put("channel", "car");
        treeMap.put("sourcetype", str2);
        treeMap.put("appId", "CarChoose");
        treeMap.put("sysversion", SystemProperties.get("ro.tw.version"));
        treeMap.put("sig", com.tw.carchoose.upgrade.a.c.q(com.tw.carchoose.upgrade.a.c.p(treeMap) + "dfsgherthdfghkj5j6o78tdftyw4uyr"));
        treeMap.put("appid", "dfsgherthdfghkj6o78tdftyw4uyrtyj");
        if (com.tw.carchoose.upgrade.a.k.n) {
            Log.e("gss", "CarApkUpdateManager params:" + this.co + "  " + treeMap.toString());
        }
        if (str2.equals("zip")) {
            C0044k.eP = false;
        }
        if (!this.co) {
            com.tw.carchoose.upgrade.a.m.au("http://api.mcu.cardoor.cn/move/mcu/queryStaticResourceInfo", treeMap, new E(this));
            return;
        }
        this.cm.sendEmptyMessage(3);
        this.co = true;
    }

内容的提问来源于stack exchange,提问作者fork_stacker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 12:45:34