jkaberg / byd-react-app-reverse

7 stars 1 forks source link

Reverse Engineering API Call #2

Open TA2k opened 7 months ago

TA2k commented 7 months ago
  1. Encrypt Payload via AES
    
    AES/CBC/PKCS5Padding mode:  encryption 
    AES/CBC/PKCS5Padding key Base64:OlLzwi7W/N5b9pamwCyecw==
    AES/CBC/PKCS5Padding key Hex:3a52f3c22ed6fcde5bf696a6c02c9e73
    AES/CBC/PKCS5Padding key Utf8:
    AES/CBC/PKCS5Padding iv Base64:AAAAAAAAAAAAAAAAAAAAAA==
    AES/CBC/PKCS5Padding iv Hex:00000000000000000000000000000000
    AES/CBC/PKCS5Padding iv Utf8:

AES/CBC/PKCS5Padding doFinal param Utf8:{"countryCode":"DE","deviceType":"0","imeiMD5":"2EA5EAF78EA945204E99E1BA9ED74B83","networkType":"wifi","random":"0532A56F28264E8D98E239A52B0DC57C","timeStamp":"1698864439417","version":"201"} AES/CBC/PKCS5Padding doFinal result Base64:SXH+7fhEGZgKiODFLHfdf7JdSnbLn0miRsWOwJgpbvmxKG1r7pu2J9mykv8Q3KuPz45A0CRuY/+mrAfxgk0OFLrDSSsrZzhmo7DuXQ+wUitafRK8DuoNBNxIyzHXwQZJEjvSPfGFkWhQiqw/BHRheM6VM26irV6QNhgDo9uOM0cZct2d929qez//mkNOQrBgH8EQ5yx0aIIpF9zeb7XtPb5tLq09VKLMyrb1aqHs3x8A3hMzBIq2eU0XDWUBFGaT AES/CBC/PKCS5Padding doFinal result Hex:4971feedf84419980a88e0c52c77dd7fb25d4a76cb9f49a246c58ec098296ef9b1286d6bee9bb627d9b292ff10dcab8fcf8e40d0246e63ffa6ac07f1824d0e14bac3492b2b673866a3b0ee5d0fb0522b5a7d12bc0eea0d04dc48cb31d7c10649123bd23df1859168508aac3f04746178ce95336ea2ad5e90361803a3db8e33471972dd9df76f6a7b3fff9a434e42b0601fc110e72c7468822917dcde6fb5ed3dbe6d2ead3d54a2cccab6f56aa1ecdf1f00de1333048ab6794d170d6501146693

2. Create a signature via SHA1

SHA-1 update param Utf8:countryCode=DE&deviceType=0&identifier=DE&identifierType=0&imeiMD5=8CCEE4915065D29CCD911DA5DC9C28D3&language=de&networkType=wifi&random=B0E25FF696AF46D2A04D8261AEDEA174&reqTimestamp=1698919441358&timeStamp=1698919441358&version=201&password=3A52F3C22ED6FCDE5BF696A6C02C9E73

SHA-1 digest result Hex:178b71f44aa7b036da503c513bebb167f057c4d0

3. Encrypt with secneo library or other custom library

{"encryData":"4971FEEDF84419980A88E0C52C77DD7FB25D4A76CB9F49A246C58EC098296EF9B1286D6BEE9BB627D9B292FF10DCAB8FFCF16567F8B3A3E37F9D632EAFC41C208D0A805908BFDA5DCEE5AFEBAC217DF2E71C6DF4B2CFC64AC94C73AA442F9DB28524A8A2909A09F7489B9B8F3F2657CD5726DEF972CB8699145FF5FA18A8FBB8E7CCC16351879764EF5C60D874DD7AD7AACCB242B6646EDED7EFBC21AB3A787D5C9DC5C5D33A2AF08345B36F7E473CC83096E1847F74C54B0280AD02B891436A","identifier":"DE","identifierType":"0","imeiMD5":"8CCEE4915065D29CCD911DA5DC9C28D3","language":"de","reqTimestamp":"1698918141562","sign":"178b71f44aa7b036da503c513bebb167f057c4d0"}

to a request like this. This is not the matching request { "request": "F1whE9TJ/js738h9X4pf815gVlcElQWSvkXsTTl9iESDQIa8mEEafEds572XK/rOU17EWIjDrx7gUEJIMxJNEsHt94DQ/zJMNfVZXIqdRwEyUP5P6JYbrWBpTNrRFWf7osI6mA8XhfGPVn/uhKM73LsrLMoLFhVgvc53/7b5ewdGMEaprEJ5Mj3XkhB/e9RLtmOSaX4bGx0JzuU2Vkv6XSCcYY+AM6qNLuA0/mpXNfiMbsl6Ew4k38P40A/n1D93eBHcoKpYkDdj5KBoMWTMuHXrLtwKG4lZF2nCGQ7z9rMwHK28DTP6TdbzwU6NelGMIAr6EpcwWSXLsUyNjzRUiEke5wBH0vXLtCVaOcfe6LzWy5gdLv+kM9rgEbVnvEWfACycxRRHoUiUVhLMUbJWLd9E/RZPGlMsdKT+lCGQ+z9bu8GxyxmTZiuUal69/QjyQFSRq0wgVEJucLigVQhhPPrkQUZptAnGe/GVCLSBZe7PPWt/YZaadTgT2z0ZI2LG1Xl8dQ8V8ZDmSPrlJEIkZqW6TzaoA0aZmJT9eOXgjmq+508y3opvm24vHqJ0wvsm+fORIW4q02dzlXNUlPe0oU6OpYMdc125qrrcRk4vQpidPvUwRGzETlJw1sOFMrPI3EjLj0QZwJRQD0W26oAvL+iUpTfMa0rAzlKEKJht3dqN2BbeUykpHA4qpn5iUW1ByPQRKmXvO7X2zb9Mj1EtGM+PRE04hAYMlzi/QB6JPRgf00/Ch8h/BmLd0eQuSDu4wYoDWvhhLg4qoEB7ZaFdmMQLMZdkSwBbWldelgXVHyDMmUv5x+7G+y0uyN5URgXr5A8qVSHS2Zghe8VKrSFtlWo44kGZv1n7Z+xYe26nt/NS69/bUCjdEcpn0xNgzDZ11m4xqwKkQT2vXsG52EA23ErpYB9tw7KGbTdELeEVI4oglzOBPRJmzMPPop0Wh20ZQEwM+wKqVIVAuxmavIBPC5fZfTb2UYcuPrrYPcJZw4Lu0NwjbLh4UL0TQl4kDPbsX" }


APK dex file:
https://send.cm/lxtd9ikduztn
https://www.mirrored.to/files/0DHI0EGH/class.zip_links

[class.zip](https://github.com/jkaberg/byd-react-app-reverse/files/13250295/class.zip)

com.byd.bydautolink.repository.util.CommonRequestUtil public final CommonTypeRequest com.byd.bydautolink.repository.param.request.CommonTypeRequest public CommonTypeRequest(@e String str, @e String str2, @e String str3, @e String str4, @e String str5, @e String str6, @e String str7) { JniLib.cV(new Object[]{this, str, str2, str3, str4, str5, str6, str7, 481}); } import com.fort.andjni.JniLib; libdexjni.so

Response:

{ "response": "F+Is4sfuTVp28SE2x3HAuHENczYnXHFRB1LODDN7a0j8JxXhttviQZLg4aEoWoZPvkqfyVBUWxS4m9Y/ngaj59kC/ikXLyuMZbsEcM6er/8TwqyINKNXYyuoqjaZeg+/e8s/KF0VavqPK4wdX8VULfFd5hCHE9yWVZAmN+MvRyo05cUyYZVL5cDYXAe7MxkL1YchBNxPOQf7dCVnlxmtvd4dtRE/DQ9k205ydpGI+DkE0n+yCAF1ytjR5GWiHwImTDhaWXpaJOpNKOBKRou8RqbUv2hmhfFD10cvgEEjPzZ0t5kKiqp+vaoWYfOV4Ujj+45OhKPWro9b7bvHT3IIgsHa+GgeZIVtFGksDe0QzBkLsZK4wGHdIbEiVoQxHfEfWrp0Vm1p1CQ/paNyFfVC/DttJD+VITpROACepEfTJK+McB6soCbHgAWFHdaZ7yAMa0w0Vlt/Qbg2G3MviRZes/lAPTm90tcU1mNwX/xzAqRrpGID7g7f+s8oEivjLMcEMqb5aysoWlOdtJ2s1bjdJUmELX8h+MH0Cz921sVUBaIQENfEjnNqpa5Cz1RNm8DEDguA5UsahpgZshBR7U9fQyBauolMbtXTbY+fIDrcn8BRk/Dz9YZYgcbN9JQawK/GgLUWwHG8WjUNqQvW5WiuRwVvZN8uCt17ytcjKKSlBlLBz5Ok5OarHyW0gx5G5TRiAt3DslixtHyYZ7/1uK0JC8GFIdEe/TF82f7TygGE8fSM=" }

{"code":"0","identifier":"DE","message":"成功","respondData":"6841A86D6CA727BD43A3CBAA102705528B95498A2545559576CCCBD46ADE835E23972F46AB202DBDD5407F2D8E7390E4B7205C2C53A3B407FEE76F2B09480C726535BA8DF017D0D9FA377AC876A9528C2EFC3D6F72AF939AD8568CC28788F0FD88475F667F39C0A4F9099A9889385348D5A2141DA5E0BA59BD933C20CAE189E5DE2CF3AE7CCEF1363B115A0F7E20032E6B154F13636054BBCD87308EE0335DA042AA3162B3FBEF7F441CCA0955A1A504479A04026808C165B9290E5CB5E3B221ED5AD64709723C53B9A1C6B0321A4240320CD0790FC108FF4EC544756E78E1DAE5CCBB40C47CDD1AF0890DFBD7AB63B6"}



The missing link is the final encryption with non standard tools. Maybe you have an idea.
jkaberg commented 7 months ago

@TA2k Very interesting, and thanks for the dex dump, that will probably help. I'm currently traveling abroad but will dig into this when I get back home.

ping @codyc1515 - I guess you'd be interested in following this

codyc1515 commented 7 months ago

Fantastic. @TA2k would you mind to please share how you performed the encryption / decryption? I am having no luck with the examples you provided, unfortunately.

jkaberg commented 7 months ago

@TA2k perhaps the answer lies here (follow the functions) image

codyc1515 commented 7 months ago

I'm simply trying (and failing!) to reverse the doFinal result hex back to base64 :(

codyc1515 commented 7 months ago

Looks like most of the good stuff is in here:

com\byd\bydautolink\repository\param\request com\byd\bydautolink\repository\util\a.java

TA2k commented 7 months ago

I use normal Frida with this script https://github.com/ppwang06/frida_js/blob/main/apps/hook_base64.js I recommend to use jadx gui for better overview https://github.com/skylot/jadx

I assume the code you referencing is only checking the response json and parsing the json I think the last part is in com.byd.bydautolink.repository.util.CommonRequestUtil public final CommonTypeRequest com.byd.bydautolink.repository.param.request.CommonTypeRequest

codyc1515 commented 7 months ago

I wasn't able to get Frida working with my device, so appreciate the help there. I am using jadx GUI but found a lot of the classes are still heavily obfuscated. Notably, I cannot work out how we might even decrypt your example yet alone begin to think about even sending / receiving an API request.

codyc1515 commented 7 months ago

com.byd.service.util.AESUtils appears to have two functions which reference CryptoTools.laesDecryptStringWithBase64 and CryptoTools.laesEncryptStringWithBase64. I cannot find these classes, however.

codyc1515 commented 7 months ago

Base64:SXH+7fhEGZgKiODFLHfdf7JdSnbLn0miRsWOwJgpbvmxKG1r7pu2J9mykv8Q3KuPz45A0CRuY

This doesn't look right. The Base64 string should be starting with eyJ.

TA2k commented 7 months ago

It is the base64 encoded hex value of the encryption result

codyc1515 commented 7 months ago

I'm not able to get same result from encrypting the utf-8 value either. There are no details provided on how you did this. Without further information, this is a dead end for me - sorry.

TA2k commented 7 months ago

The output from Frida was incorrect I added the correct key

import crypto from "crypto";
const payload ='{"countryCode":"DE","deviceType":"0","imeiMD5":"2EA5EAF78EA945204E99E1BA9ED74B83","networkType":"wifi","random":"0532A56F28264E8D98E239A52B0DC57C","timeStamp":"1698864439417","version":"201"}'
let cipher = crypto.createCipheriv(
  "aes-128-cbc",
  Buffer.from("OlLzwi7W/N5b9pamwCyecw==", "base64"),
  Buffer.from("00000000000000000000000000000000", "hex")
);
console.log(cipher.update(payload, "utf8", "hex"));
jkaberg commented 7 months ago

Just a small side note and I'll investigate this later, but our friends over at Hassbian (Chinese site) has earlier uncovered an API used with Wechat to interact with the car - this requires an valid Wechat account and refetching cookies every now and then from the GUI Wechat application, and not really feasible for us none Chinese residents.

However they have implemented an crude flow in Node-red which fetches data from the Wechat api (https://bydcloud.byd.com/wechat/) and returns it over MQTT to HASS (see attached file for config flow - byd_car_flows.json). One note on this API is that it uses obfuscated commands to control various aspects of the car, eg for "flash light" the command is "B6D767D2F8ED5D21A44B0E5886680CB9" or for lock doors the command is "8F14E45FCEEA167A5A36DEDD4BEA2543".

codyc1515 commented 7 months ago

Just a small side note and I'll investigate this later, but our friends over at Hassbian (Chinese site) has earlier uncovered an API used with Wechat to interact with the car

Last I checked this, I was not able to get my car working with the WeChat conversation (even directly). Chinese cars appear to use a totally separate set of software (same as they don't have Android Auto, for example) which is why you don't even see China as a selection available in the BYD app.

codyc1515 commented 7 months ago

I made a sample library for the preamble but don't understand how the encryData / secneo part is working. I'm unable to see this part in jadx-gui as it's heavily obfuscated. Did you happen to have a sample code for this?

https://gist.github.com/codyc1515/8d1074233b6aa10d3ccbc074f39446d7

jkaberg commented 7 months ago

Good stuff @codyc1515, I ran some of the essential code through ChatGPT to ease understanding - the result is here. There will obviously be errors here, but it might help. Will look further later today.

codyc1515 commented 7 months ago

Updated the gist to work with PKCS#7. We can now go fully both ways encrypting and decrypting with no data loss. Still haven't worked out the step #3 from @TA2k - unclear where the SecNeo encryption library comes from or how this is being done.

codyc1515 commented 7 months ago

Need the code from JniLib.cL - this appears to be embedded in libdexjni.so in the APK.

codyc1515 commented 7 months ago

Oh! Here are the BangCle keys:

public static String RSA_PUB_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCPuxLBfK4sWmNAsogWPJuKQPqD1bewifuwd1NNAAy5jsTpV4YYZWKnkJV73BmfR9gYZVC2A8+q0vOql9siQ8RMMqr6qQRhL/g5SU2+Ya98ggLyObAy7OYpTMGHOBYyvINndN18tZh4YvPMnteVWBqAj65q5qpGrSkOxqTInWJdcwIDAQAB";
public static String SM2_PUB_KEY = "04F93B986D1E826B1B2A4407A666C0AD583F0E9BE901D49B9FC39619B307411BF15DFCB2C9E8458180F026414CA46FFA743E6FC5441A5017745B0528A95CC663B3";
public static String WBC_KEY_DEC = "2b0b9a2ad5fb378e901250cff15bb9689da83701d5f7ba1feab718c7965b6a540751cffc96c5fa66c15a0b85ca3641a2f54f669deb7da46509a42da0473e9ba35b72d273bc85a48b2c767556bd7ae798505992a3fdc1711cf605750420eead58b77b941d08bb9ad0785ad13d1ebb51c1d0b38ff6227da46dd0bd12490f4f3092c8b122ae8a71cb4696647771c03b87be3ee348083eac73e6ebfe3b2d23e74914716138578d45508c64be89b5bf16641fb9a80cd7";
public static String WBC_KEY_ENC = "8918b88998d151b8718f8812afb7712d092b08d5db840877263ee7feec4c597ed46431047064a8f57834199806f886b4e7740fb39663df39ff434bd15eac12212351481839dfc115676ee4151c7abb8d7ef9f086a0567a1872db4739b54818572d516d25a1806aacb2c25483c6dc3e12b3f7b9069113ab8f202cca81cf99f5ebbb0ebfe1c8c85603b52cbf1342d470665adbef9da20cb908017f4fbfee231f51fdc1e13b52dcb43f3004604de24c98c91ab9b401";
public static String WBC_KEY_IV = "B95EE56818E381BF";

On checking, SM2 appears to be a Chinese cipher. With regard to the RSA public key, this appears to be valid for something:

Algo RSA
Format X.509
 ASN1 Dump
RSA Public Key [a6:10:24:0a:56:11:f3:77:e0:1e:87:87:f6:f1:72:a1:35:2e:ad:08]
            modulus: 8fbb12c17cae2c5a6340b288163c9b8a40fa83d5b7b089fbb077534d000cb98ec4e95786186562a790957bdc199f47d8186550b603cfaad2f3aa97db2243c44c32aafaa904612ff839494dbe61af7c8202f239b032ece6294cc187381632bc836774dd7cb5987862f3cc9ed795581a808fae6ae6aa46ad290ec6a4c89d625d73
    public exponent: 10001
TA2k commented 7 months ago

I assume also this is a custom encryption implemented in c++ library you can find all .so files in the apk The .so file is not using native crypto from Android OS

https://send.cm/76ujx5le6c2k https://www.mirrored.to/files/RN8MO17A/arm64-v8a.zip_links

Hyundai/Kia had the similar issue with a custom.so which is encrypting the current timestamp as a check. They build a docker to run the .so files and pre generate the encrypted timestamps

https://github.com/Hacksore/hyundai-kia-stamp/tree/arm

codyc1515 commented 7 months ago

Good lord. This is all so pointless anyway as - given enough time - because it must run on the clients device, it can be reversed.

i think I’ve about hit my limits of my knowledge here as I’m not a reverse engineering pro and not at all familiar with Android. Sorry I couldn’t help more :(

Will wait and see if someone else picks this up.

TA2k commented 6 months ago

I was able to hook into the jni file via Frida but no usable results here:

let nativeMethods = { methods: [] };
let addrRegisterNatives = null;

const OURLIB = 'libdexjni.so'; // Replace with yours

Process.enumerateModules().forEach(function (m) {
  Module.enumerateSymbolsSync(m.name).forEach(function (s) {
    if (s.name.includes('RegisterNatives') && !s.name.includes('CheckJNI')) {
      addrRegisterNatives = s.address;
    }
  });
});
console.log('RegisterNatives() is at: ' + addrRegisterNatives);
setTimeout(function () {
  Interceptor.attach(addrRegisterNatives, {
    // jint RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);
    onEnter: function (args) {
      var calledFromLibnOffset = String(DebugSymbol.fromAddress(this.returnAddress));
      if (!calledFromLibnOffset.includes(OURLIB)) {
        // Filter out a few calls
        return;
      }
      console.log('\nenv->RegisterNatives()');

      var nMethods = parseInt(args[3]);
      console.log('\tnMethods=' + nMethods);

      var class_name = Java.vm.tryGetEnv().getClassName(args[1]);
      console.log('\tclazz.name=' + class_name);

      console.log('\tmethods[]:');
      var methods_ptr = ptr(args[2]);

      for (var i = 0; i < nMethods; i++) {
        var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));
        var methodName = Memory.readCString(name_ptr);
        var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));
        var sig = Memory.readCString(sig_ptr);
        console.log('\t\t' + methodName + '(), sig:', sig);

        var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));
        var find_module = Process.findModuleByAddress(fnPtr_ptr);
        var fnPtr_ptr_ghidra = ptr(fnPtr_ptr).sub(find_module.base).add(0x00100000);
        console.log('\t\t\tfnPtr:', fnPtr_ptr, ' ghidraOffset:', fnPtr_ptr_ghidra);
        if (methodName === 'cV') {
          Interceptor.attach(fnPtr_ptr, {
            onEnter: function (args) {
              console.log('method onenter ' + args[0]);
              //log string of pointer
              console.log(ptr(args[0]).readU8());
              console.log(ptr(args[0]).readCString());
              console.log(ptr(args[0]).readUtf16String());

              console.log(ptr(args[1]).readU8());
              console.log(ptr(args[1]).readCString());
              console.log(ptr(args[1]).readUtf16String());
              // console.log(ptr(args[1]).readUtf8String());

              console.log(ptr(args[2]).readU8());
              // console.log(Java.vm.getEnv().getStringUtfChars(args[0], null).readCString());
            },
            onLeave: function (retval) {
              console.log('method onleave: ' + retval);
            },
          });
        }
        nativeMethods['methods'].push({
          ghidraOffset: fnPtr_ptr_ghidra,
          methodName: class_name + '.' + methodName,
        });
      }
    },
  });
}, 500);

Also looking via Ghidra in the code of the function cL/ FUN_001167c8 was not helpful https://github.com/NationalSecurityAgency/ghidra/releases

env->RegisterNatives()
        nMethods=10
        clazz.name=com.fort.andjni.JniLib
        methods[]:
                cV(), sig: ([Ljava/lang/Object;)V
                        fnPtr: 0x7d495a0774  ghidraOffset: 0x116774
                cI(), sig: ([Ljava/lang/Object;)I
                        fnPtr: 0x7d495a079c  ghidraOffset: 0x11679c
                cL(), sig: ([Ljava/lang/Object;)Ljava/lang/Object;
                        fnPtr: 0x7d495a07c8  ghidraOffset: 0x1167c8
                cS(), sig: ([Ljava/lang/Object;)S
                        fnPtr: 0x7d495a07f4  ghidraOffset: 0x1167f4
                cC(), sig: ([Ljava/lang/Object;)C
                        fnPtr: 0x7d495a0820  ghidraOffset: 0x116820
                cB(), sig: ([Ljava/lang/Object;)B
                        fnPtr: 0x7d495a084c  ghidraOffset: 0x11684c
                cJ(), sig: ([Ljava/lang/Object;)J
                        fnPtr: 0x7d495a0878  ghidraOffset: 0x116878
                cZ(), sig: ([Ljava/lang/Object;)Z
                        fnPtr: 0x7d495a08a4  ghidraOffset: 0x1168a4
                cF(), sig: ([Ljava/lang/Object;)F
                        fnPtr: 0x7d495a08d0  ghidraOffset: 0x1168d0
                cD(), sig: ([Ljava/lang/Object;)D
                        fnPtr: 0x7d495a08fc  ghidraOffset: 0x1168fc

https://www.transferxl.com/download/0842MDw26n25Q

At the moment I see the best chance to adopt the Hyundai docker container and put it as a docker in HASS

codyc1515 commented 6 months ago

I agree. Hopefully someone else can pick this up.

jkaberg commented 6 months ago

@TA2k are we sure libdexjni.so is actully used for payload encryption? Did we trace payload encryption back to this module?

TA2k commented 6 months ago

Of course not but the final encryption is not using OS encryption libraries. So its very likely the shared libraries are used and we libdexjni.so here in the request builder of the apk

jkaberg commented 6 months ago

https://gist.github.com/jkaberg/d5ef6a546858507c9477dbae8970ffb8#file-com-byd-bydautolink-repository-util-a-java-L45 -> https://gist.github.com/jkaberg/d5ef6a546858507c9477dbae8970ffb8#file-com-byd-service-util-b-java-L80

Mind ChatGPT's "analyze" work (deobfuscate function, variable names etc) -> this doesn't point at any custom lib? Or am I missing something?

codyc1515 commented 6 months ago

I am virtually certain that it is in fact in those libs simply because I see the code referencing them.

codyc1515 commented 6 months ago

Any more thoughts?

jkaberg commented 6 months ago

No, I did take another look however I'm out of ideas in the reverse engineering space.

I'm now leaning towards an Android app to extract the data I want from the car and push that over secure websocket to my mqtt broker (mainly because of ease admin wise since I already run the broker) - everything seems to be included within the SDK (this is my mirror of the official Chinese one located here - but translated to English). Only thing holding me back is time, and presumably the app won't work/transmit when the car is off (I've yet to confirm this, just a hunch) which is a downside in terms of charging and the feedback loop.

codyc1515 commented 6 months ago

That SDK looks like it's made for publishing Android apps that integrate with the in-vehicle display as part of the China-market vehicles "BYD Store" which isn't what we are trying to do here. If I could side load apps on my Dolphin there would really be no need for any of this but it appears that the AU/NZ models are heavily locked down (especially compared to the Atto 3).

jkaberg commented 6 months ago

Sideloading apps both on the Tang (my car) and ATTO3 is possible (I do this quite regularly), Dolphin not released in Norway just yet so IDK. The sample app (and accompanying APK) found within the folder structure there does provide data as one would expect when sideloading it.

jkaberg commented 6 months ago

@codyc1515 did you try to format an USB drive to FAT32, then create an folder within named "third party apps" and plug it in the car? Usually that prompts you for an password, which differ from car to car (for the Tang it's 20211231)

Seems the password for the Dolphin is either "BYD6125F" or "GHY0613byd"

codyc1515 commented 6 months ago

Yes. I never get the password prompt. I can see the USB in the file manager but clicking an APK gives a security warning.

jkaberg commented 6 months ago

As I don't have an Dolphin it's hard to know, but do seem like people have succeeded https://youtu.be/GgtOsd8dlpU?t=102

Also just noticed this one, https://www.youtube.com/watch?v=v6TR7Tq5JHw

TA2k commented 6 months ago

FYI: With https://dilinkappoversea-eu.byd.auto:8666/#/text/fileNotice?agreementType=2&softType=0 You can live debug the website encryption of an request but it is different to the App way

{
    "encryData": {
        "agreementType": "2",
        "random": "4390154853257556",
        "softType": "0",
        "version": "",
        "country": ""
    },
    "language": "",
    "reqTimestamp": "1700819172701",
    "sign": "7B9dE4d14Ca449766a6A8ebE3de67ff17743469"
}

AES-128-CBC Key: E987E8B6BBEC3F920D219F7A4B5C619A A0A415B8975652C9F232B7CA66D8F51C4150CA4E6C17724909637EF73EB1A7B11A0F9FD10A0035EC088CE4055C155A2A054712482FE372BFE87E5D39EE0E654B61F79794A045F4546AC6E9267ACD0A367686C779DD3F75D2913AA994F03C0EE01B04D1A0692062BF16FFFD5D867F07F5C92435A1F99149022F7F8D4AF787CE9565A2F5A38D33F251C3669D5C03A9750386F590A8A31A46218FA02B7A7AAB4E6DDF68AF27A09B67F8BF16002637C86D88211C447D0EBCE807F3AEBC3E7A3AF9D5AC847C04D89BDA70DFBCF72979BE03EF Response in a unencrypted response.

TA2k commented 6 months ago

FYI: I looked into the iOS App but no new findings functions are similar to Android. They set a entry and a signing key

image
jkaberg commented 6 months ago

FYI: With https://dilinkappoversea-eu.byd.auto:8666/#/text/fileNotice?agreementType=2&softType=0 You can live debug the website encryption of an request but it is different to the App way

{
    "encryData": {
        "agreementType": "2",
        "random": "4390154853257556",
        "softType": "0",
        "version": "",
        "country": ""
    },
    "language": "",
    "reqTimestamp": "1700819172701",
    "sign": "7B9dE4d14Ca449766a6A8ebE3de67ff17743469"
}

AES-128-CBC Key: E987E8B6BBEC3F920D219F7A4B5C619A A0A415B8975652C9F232B7CA66D8F51C4150CA4E6C17724909637EF73EB1A7B11A0F9FD10A0035EC088CE4055C155A2A054712482FE372BFE87E5D39EE0E654B61F79794A045F4546AC6E9267ACD0A367686C779DD3F75D2913AA994F03C0EE01B04D1A0692062BF16FFFD5D867F07F5C92435A1F99149022F7F8D4AF787CE9565A2F5A38D33F251C3669D5C03A9750386F590A8A31A46218FA02B7A7AAB4E6DDF68AF27A09B67F8BF16002637C86D88211C447D0EBCE807F3AEBC3E7A3AF9D5AC847C04D89BDA70DFBCF72979BE03EF Response in a unencrypted response.

The thing thats interesting with the webapp is that it shares the same api as the android app atleast, look here. When this host was live all endpoins at https://dilinkappoversea-eu.byd.auto/ was an 1:1 match on http://124.71.64.133:9090/ with the same encrypted response

I didn't bother much going down that path, but it seem'd intersting enough to look at

http://124.71.64.133:9090/app/agreement/getContent https://dilinkappoversea-eu.byd.auto/app/agreement/getContent

codyc1515 commented 6 months ago

Are you able to provide a sample of the code you used for the website to generate the request? I was unable to previously. Then we can just look at the payload generation and go from there.

TA2k commented 6 months ago

https://dilinkappoversea-eu.byd.auto:8666/#/text/fileNotice?agreementType=2&softType=0

Sources: Open chunk-4fc76704.9d06aaea.js

Search for JSON.stringify(m) And set a breakpoint

codyc1515 commented 6 months ago

Can you please re-post the DEX file again? The download expired.

codyc1515 commented 6 months ago

Simply changing the URL in the chunk from ../getContent to https://dilinkappoversea-au.byd.auto:8666/app/account/login provides an encrypted response (not the decrypted JSON we would expect). I'm wondering if there is two methods for encryption (one for content and one for everything else).

TA2k commented 6 months ago

Encryption of the response happens on server side. What we needs is login form to see how the encryption and decryption works I updated the links

codyc1515 commented 6 months ago

Well that's not strictly true. The response is returned unencrypted for content but it seems for anything that requires login it is indeed returned with encryption.

TA2k commented 6 months ago

Everything the app is requesting is encrypted

btgangsta commented 6 months ago

Yes. I never get the password prompt. I can see the USB in the file manager but clicking an APK gives a security warning.

Instead of using "third party apps" as the folder name

try "SilentInstaller"

Note:

codyc1515 commented 6 months ago

Just done that now and the USB is recognised but you cannot click the APK's, as you say. So, where am I meant to see them? They do not show on the main screen.

btgangsta commented 6 months ago

Just done that now and the USB is recognised but you cannot click the APK's, as you say. So, where am I meant to see them? They do not show on the main screen.

Assuming your folder structure is correct you should see this popup, immediately following USB plugged into USB-A port.

Also depends on what you installed? For example PackageInstallerUnlocked will not be visible on the home screen.

0FF38C82-D287-4FDE-9B2F-E954AEBCFE43

codyc1515 commented 6 months ago

Nope and you appear to be using an Atto 3 as an example because it has the PM sensors. Waited about 30 minutes and still didn’t show.

cuter-doc0 commented 5 months ago

silentinstaller method should work on BYD android systems which are older than September 2023 - https://t.me/atto_3/3/7019

cuter-doc0 commented 5 months ago

I believe that the app:

  1. Makes API calls in an encoded and decoded fashion. Not a encrypted and decrypted fashion. (at least initially)
  2. Doesn't have a dynamic keys such that key values are tied to the region or user account. (at least initially)

because:

  1. API calls made when the app first launches and before login are already encoded. Requests look very similar with only parts of the encoded data changing, much like what you'd see with a cypher-type encode. Raw body data is of consistently different lengths, so no salts or fillers.
  2. API calls made to different regional servers show the same results. Further, no key exchange occurs initially yet the raw data exchanged is encoded. Editing the "request" cypher to the server to force an error causes the cloud service (an ec2 instance) to return the same encoded cypher (likely representing an error message) regardless of region. This means that there are static keys or a static cypher algorithm somewhere.

Video of raw request and response body: https://youtu.be/lUlvbW72Fvg