Maybe someone can help me here. I have the following android system and an android app, which gets MCU firmware, boot logo and an app for car settings via a Chinese server: https://2-din.ru/product/Opel-Astra-J-2010-2015-7051
The MCU is responsible for the interaction between my Android car radio and the connected Canbus box. In the past, I unfortunately overwrote the factory MCU from the Chinese and have had problems activating the buttons on the radio ever since. They are assigned incorrectly and cannot be programmed correctly. Although the "correct" car model is selected, the buttons on the radio do not work properly; the steering wheel buttons work fine.
Well, the Russian dealer and his technical support and also the warranty department can't seem to help me any further, nor can the forums 4PDA, RedMOD and XDA. I therefore have to get to the factory MCU of the Chinese myself and have already analyzed and recorded the Car Choose App with IDA and the network traffic with Proxymon. I have a disassembly with SMALI files ready in case anyone needs it to help me.
The Car Choose App makes specific POST requests to the server with the following content, recorded with Proxymon. This is only one example.
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}]}}
As can be seen, a signature is sent with the request (like every request):
"sig":"749f9e7c78c840e8e7ad7c0d5de81dfd"
In another forum it was mentioned to me that this is just an MD5 hash. So far I haven't quite understood how this is composed. From the forum it says:
what is behind "sig:" is a simple MD5 hash, so nothing with "Secret Key". First, a TreeMap object is created, which consists of the following components
file: <output path>
mcu_version : <mcu_version>
sys_version: <sys_version>
This is then passed to the function com/tw/carchoose/upgrade/utils/NetworkUtils;->getSig. The function then converts the TreeMap object into a Stringbuilder object (object for assembling strings efficiently) with a while loop, where the values are concatenated and at the end the whole thing is returned as one long string. The result of the function is in turn appended to a StringBuilder object along with a constant "dfsgherthdfghkj5j6o78tdftyw4uyr". This StringBuilder object is then converted to a string and the final MD5 hash is then created from it, which ends up in the "sig" property in the JSON string of the POST request.
These procedures mainly take place in the classes com/tw/mcudebug/MCUService/MCUService and com/tw/carchoose/upgrade/utils/NetworkUtils.
Update files are then as I see it at first glance under the base URL http://update.cardoor.cn/terminal/software/update/car/android/ downloaded in the requested version.
Who can help me how to recalculate the MD5 and give me step by step instructions? Maybe a small tool can be built for it? For those interested, here is the disassembly with the SMALI files: https://my.hidrive.com/share/29or9vwhkd (or here the complete Car Choose APK: https://my.hidrive.com/lnk/sAKynad1)
UPDATE
I found the following TreeMap code with jadx-gui, which seems to be related to the signature. Unfortunately, my knowledge of Java is not sufficient to understand this function. Can someone explain to me what is happening here with "sig"
and the constant "dfsgherthdfghkj5j6o78tdftyw4uyr"
?
Car Choose - configuration list
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 - checkPwd
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 - getNew 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 - getNew 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 - Single 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 activity
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 - main function!
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;
}