Beiträge von marco79cgn
-
-
Unter macOS hat das leider nicht funktioniert, obwohl man die ersten beiden Versionen noch installieren konnte auf Apple Silicon Hardware. Die neueste Version stürzt beim Start direkt ab. Bei älteren Versionen hat nur der Demo-Modus funktioniert. Bereits die Anmeldung ist gescheitert.
-
Gut möglich. Daher habe ich mich irgendwann auch nicht weiter damit beschäftigt.
Die müssen selbst einen Teil der API public machen.
-
Diesen String habe ich auch schon gefunden. Bekommt man auf der Konsole z. B. mit dem Befehl:
strings -a libAppSecret.so
Ich bin so vorgegangen, dass ich mir eine gültige Abfrage aus der App per proxy mitgeschnitten habe (in Proxyman) und dann diesen Request inklusive der gültigen Signatur als Basis genommen habe fur die eigene Signaturberechnung. Ich habe also versucht, die Signatur selbst zu berechnen und auf den gleichen Wert zu kommen wie beim gesnifften Request.
Habe mir eine eigene Funktion gebaut fur diesen JS Fiddle Playground und dann mit allen moglichen Keys versucht, die gleiche Signatur zu berechnen fur die entsprechende Payload. Hier das snippet, welches man im verlinkten Playground nutzen kann:
Code
Alles anzeigencalculateSignature() function calculateSignature() { let headers = 'application/json;responseformat=3\nx-api-signature-nonce:CE5CAF30-E15C-460F-91B6-5820E3D5B1DB\nx-api-signature-version:1.0\n'; let signingString = headers + '\n' + 'setting=charging\n1683739822790\nGET\n/remote-control/vehicle/status/soc/HESX******235'; console.log(signingString) const signingKey = 'b816ffc222a657bef362d874a60337de'; const signature = CryptoJS.HmacSHA1(signingString, signingKey).toString(CryptoJS.enc.Base64); console.log("Signatur: " + signature) return signature; }
-
Nachtrag:
Der signKey wird an einer Stelle im Code auf die Variable "appSecret" gesetzt. Dieses wird wiederum per JNI aus einer externen static library geladen, nämlich aus dem Ordner "lib/arm64-v8a/libAppSecret.so". -
Ich habe es nochmal rausgesucht. Einen besseren Überblick bekommt man übrigens mit dem Mobile Security Framework. Hierzu einfach den Docker Container starten, Browser öffnen und per Web Oberfläche die App (apk) laden.
Die entscheidende Klasse SignInterceptor.java
Code
Alles anzeigen@Override // okhttp3.Interceptor public Response intercept(Interceptor.Chain chain) throws IOException { String str; String[] split; Request request = chain.request(); Request.Builder newBuilder = request.newBuilder(); String method = request.method(); String httpUrl = request.url().toString(); Headers headers = request.headers(); TreeMap<String, String> treeMap = new TreeMap<>(); if (httpUrl.indexOf("?") != -1) { for (String str2 : httpUrl.substring(httpUrl.indexOf("?") + 1).split(DispatchConstants.SIGN_SPLIT_SYMBOL)) { treeMap.put(str2.substring(0, str2.indexOf(SimpleComparison.EQUAL_TO_OPERATION)), str2.substring(str2.indexOf(SimpleComparison.EQUAL_TO_OPERATION) + 1)); } } RequestBody body = request.body(); String str3 = ""; if (body == null) { str = ""; } else if (body instanceof MultipartBody) { Buffer buffer = new Buffer(); body.writeTo(buffer); String[] split2 = buffer.readUtf8().split(EdgeConstants.Defaults.jiangdk); ArrayList arrayList = new ArrayList(); String str4 = ""; for (String str5 : split2) { if (str5.contains("Content-Disposition")) { String replace = str5.replace("Content-Disposition: form-data; name=", "").replace("\"", ""); arrayList.add(replace); if (replace.contains("file")) { str4 = replace; } } } str = str4; } else { Buffer buffer2 = new Buffer(); body.writeTo(buffer2); str = buffer2.readUtf8(); } String valueOf = String.valueOf(System.currentTimeMillis()); SignUtil signUtil = SignUtil.getInstance(); try { String signSecret = CommonOpenApi.getInstance().getCommonConfig().getSignSecret(); if (!ComUtils.checkString(signSecret)) { signSecret = JniUtils.getInstance().getAppSecret(); } str3 = signUtil.sign(signSecret, headers, treeMap, str, valueOf, method, httpUrl, body instanceof MultipartBody ? 1 : 0); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e2) { e2.printStackTrace(); } return chain.proceed(newBuilder.addHeader("X-SIGNATURE", str3.trim()).addHeader("X-TIMESTAMP", valueOf).build()); } @Deprecated public SignInterceptor(String str) { CommonLogUtils.i("other", "SignInterceptor"); } }
Das Secret zur Verschlüsselung wird in den Zeilen 44-47 gesetzt:
CodeString signSecret = CommonOpenApi.getInstance().getCommonConfig().getSignSecret(); if (!ComUtils.checkString(signSecret)) { signSecret = JniUtils.getInstance().getAppSecret(); }
Die Methode zum signieren ist dann in Zeile 48:
str3 = signUtil.sign(signSecret, headers, treeMap, str, valueOf, method, httpUrl, body instanceof MultipartBody ? 1 : 0);
Die Implementierung der sign-Methode sieht so aus:
Codepublic String sign(String str, Headers headers, TreeMap<String, String> treeMap, String str2, String str3, String str4, String str5, int i) throws NoSuchAlgorithmException, InvalidKeyException { String url = getUrl(str5); String md5 = getMD5(str2, i); String str6 = getHeaders(headers) + EdgeConstants.Defaults.jiangdk + getParam(treeMap) + EdgeConstants.Defaults.jiangdk + md5 + str3 + EdgeConstants.Defaults.jiangdk + str4 + EdgeConstants.Defaults.jiangdk + url; Mac mac = Mac.getInstance("HMACSHA1"); mac.init(new SecretKeySpec(str.getBytes(Charset.forName("utf-8")), "HMACSHA1")); return Base64.encodeToString(mac.doFinal(str6.getBytes(Charset.forName("utf-8"))), 0); }
-
Dekomiliert habe ich auch schon vor längerer Zeit. Gescheitert bin ich nach wie vor an der Berechnung der Signatur bzw. dem hierfür benötigten Secret.
Ein Interceptor funktioniert automatisch, indem er sich z.B. wie oben in die HTTP Kommunikation dazwischen schaltet. Er überwacht sozusagen die HTTP Requests nach außen, fängt sie ab und ergänzt noch einige Dinge. In diesem Fall in erster Linie die Header "X-Signature",, "X-Timestamp" und "X-FILE-LENGTH".
Die File Length kann man ignorieren, die ist nur relevant, wenn tatsächlich Dateien verschickt werden als Body (binary). Der Timestamp ist logisch, die aktuelle Zeit in Millisekunden. Die Signatur wird berechnet, indem diverse Parameter als String hintereinander gekettet werden und mit einem Secret verschlüsselt werden, Algorithmus ist hier HMAC-SHA1.
var10 → die bereits vorhandenen Header vom http request, der "intercepted" wird
var11 → eine Key/Value Liste (TreeMap), in der sämtliche Query-Parameter der Ziel-URL gesammelt werden.
Beispiel Ziel-URL: hxxp://smart.com/api?bla=blub&forum=smart1
var11 = { bla=blubb, forum=smart }
var5 → Dateiname des Anhangs (bei uns leer, also X-FILENAME: "", siehe Zeile 26)
var18 → aktueller Timestamp (aktuelle Zeit in Millisekunden seit 1.1.1970, auch bekannt als Unix Timestamp)
var12 → die http Methode des Requests, also bei uns "GET"
var6 → die vollständige Ziel-URL, inklusive aller Query Parameter
Das geht alles in die Methode huandk() rein, die natürlich anders heißt, aber aufgrund des Decompilierens jetzt so komisch aussieht.
Du wirst dann auch eine andere Methode finden, in der die Signatur berechnet wird. Ich suche die Stelle nochmal raus. Da werden im Prinzip diese Eingangsparameter in bestimmter Reihenfolge hintereinander gekettet als String und dann eben verschlüsselt. Die Frage ist nur, mit welchem Key.
-
Das Quick Control Menü wurde optisch angepasst in 1.1.0 (wenn man von oben nach unten wischt). Man kann die einzelnen Funktionen links jetzt auch bearbeiten und entfernen bzw. Hinzufügen.
Was die Sprachpakete angeht, werden mir als Update nur 6 Fremdsprachen angeboten. Eine Aktualisierung der deutschen Sprache steht nicht zur Verfügung. Grammatikfehler sind gefühlt auch noch die gleichen („gibt es eine Stau“, „fahren sie auf die links Spur“ etc.).
Auch schön: Herunterladen oder „Absagen“. Das kommt halt dabei raus, wenn man von Google Translator übersetzen lässt statt von Menschen. 😜
-
Habe zusätzlich einen AirTag im 🚗. Darauf ist momentan mehr Verlass, was den Standort angeht.
Vom Update 1.1.0 bin ich bisher recht angetan. Sind doch einige schöne Verbesserungen drin. Zudem kann ich endlich auch den Müdigkeitssensor für die aktuelle Fahrt abstellen. Das ging ja bei mir bisher nie. Werde aber mal testen, ob er weniger sensibel geworden ist.
Bei den Apps hat sich jedoch nichts getan. Sehe ich das richtig, dass man für TuneIn Radio zwingend ein Premium-Abo benötigt?
-
Ja, dann ist allerdings fraglich, ob mir überhaupt jemals noch ein Update angezeigt/angeboten wird.