求助: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生成,但具体逻辑未完全明确。
需求
- MD5签名计算的详细步骤
- 可实现该计算的工具
MD5签名计算详细步骤
结合反编译代码逻辑,签名计算流程如下:
- 收集核心参数:整理当前请求的所有参数,注意
sig和appid不需要加入签名计算(代码中先生成sig,最后才添加appid) - 参数排序:将核心参数放入
TreeMap,该集合会自动按键的字典序升序排列,排序规则是签名正确性的关键 - 拼接参数字符串:遍历排序后的参数,按
键=值的格式拼接所有键值对,键值对之间无分隔符(例如level=7+language=de-DE拼接为level=7language=de-DE) - 添加固定密钥:在拼接后的字符串末尾追加固定密钥:
dfsgherthdfghkj5j6o78tdftyw4uyr - 计算MD5哈希:对最终拼接字符串计算MD5哈希值,得到的32位小写十六进制字符串即为
sig字段的值 - 组装请求参数:将生成的
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
相关产品推荐
相关产品推荐

