eufy-security-client: [Question]: Cover_Path returns local file / Cloud images are encrypted
Ask your question
Hey @bropat ,
Seems like Eufy patched more in their API and now the cover_path is moved from a CDN url to T8410REDACTED~/media/mmcblk0p1/Camera00/20221210230725n.jpg
Any idea if it’s possible to fetch an image from the station itself?
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 1
- Comments: 85 (66 by maintainers)
Commits related to this issue
- Implemented new picture handling for notification and stations (#273) Implemented download and decryption of pictures — committed to bropat/eufy-security-client by bropat a year ago
fyi: The image data you receive via the p2p command is encrypted in the same way as the images of the cloud push notification.
I think I am close to the result.
The reverse engineering of .so files is much more complicated (libenc.so)… 😉
Today I finally had time to dedicate myself to this topic again and was able to decode it correctly 🎉.
I will now implement the remaining part in the library.
Stay tuned 😉
Many thanks to all who have helped.
Here is a foretaste of the correct functions:
Sorry guys had a lot to do and so couldn’t invest much time here. Anyone who wants to help is of course welcome. 😃
As you know eufy has encrypted the images you receive through the push notification. The same encryption is also used when you try to download the images via P2P (see commit above from @PhilippEngler).
Example Image data:
The first 41 bytes contain part of the required decryption information. 3 pieces of information are supplied (separated by
:):eufysecurity, the image data is encrypted, otherwise not.A fourth piece of information is needed and this is fetched directly from the cloud from the properties of the station, namely
p2p_did.The following implementation is found in the code used for image decoding:
The following is the class that loads the native external library
libenc.so:This library contains the knowledge of how to derive the AES key to decrypt the image header. For this reason, we need to understand how this works through reverse engineering. I use ghidra for that. So I load the library
libenc.sointo ghidra and have it analysed. From this I get pseudo C code. Here you still have to correct the data types and sizes. Of course, you can also give the variables meaningful names, etc. Behind the functiongenCheckCodeyou will find the following procedures in the native library:I have tried to understand these procedures and translated them into the following functions in typescript:
Unfortunately, I do not get the expected result. I think the error is in the second half of the
getImageKeyfunction… To understand where this effectively is, you have to debug the native library at runtime (e.g. with gdb). So I wrote a simple Java console program that implements and calls the native classEncoderso that I can test the native library better.Encoder.java
HelloWorld.java
The line with
sc.nextLine();was implemented so that I can easily debug the process with gdb and be sure that the native library has already been loaded by the JVM (dalvikVM).I compiled the Java code and created a jar file from the class files. I then converted this into a dex file using the Android SDK Build Tools (v. 33.0.0) with
d8:I then loaded the dex file by
adb pushonto a rooted device or emulator including the libenc.so file (in the correct architecture of the device or emulator):Then I started the program on the device or emulator as follows:
Output of the above command:
If you pass the correct input values here, you will get the AES key to decrypt the image header.
Attention: Only the first 16 bytes are used as AES key (see Java code at the beginning).
I have now fetched the
gdbserverfor the correct architecture of the device or emulator from the Android NDK (older version still supplied with gdb), loaded it withadb pushand then started it.Now you can forward the previously selected port with adb so that you can debug from the PC:
localhost:5055and start debugging.So far, I have not been able to read the correct registers or memory areas to understand what was misinterpreted in the typescript code above. I assume that there is an error in the generated pseudo C code of ghidra. Here you could also check the assembler code using the official documentation from ARM to understand what ghidra has interpreted wrong… Another challenge… 😉
The challenge is now to get the correct result with the following input values and thus solve the mystery 😉
Expected outcome:
D174D8169229AD39F55E6C3D801E4169If someone can be helpful here would be great 😃
I will check this soon…
sorry for the interruption, the updated stuff is now added: previewImageHomeBase_v2-branch. Please keep in mind, that you will have to adapt the implementation in your euyfsecurity.ts (at the end of the file, that’s why I have commented it out) [I do historically some adaptations]. But the rest should work.
@bropat @PhilippEngler I did some debugging on this topic. I managed to attach Frida to the native library functions and call them with the provided args from bropat.
I got the following results:
This is the same as bropat’s
getImageSeedimplementation 🎉So i did a lot of debugging and found out that we have the following values:
Doorbell:
the GenCheckCode is called like
inside the native library:
the gen_check_code_v1 is called with:
argument1 represents the basecode argument2 represents the seed
the basecode is coming from
gen_pic_base_codewhich returns the station_sn+suffixyou can validate this by running
Indoorcam:
the GenCheckCode is called like
inside the native library:
the gen_check_code_v1 is called with:
argument1 represents the basecode argument2 represents the seed
the basecode is coming from
gen_pic_base_codewhich returns the station_sn+suffixyou can validate this by running
Theoretical device:
the GenCheckCode is called like
inside the native library:
the gen_check_code_v1 is called with:
argument1 represents the basecode argument2 represents the seed
the basecode is coming from
gen_pic_base_codewhich returns the station_sn+suffixyou can validate this by running
Concluding:
${DEVICE_SN}${suffix}but if i follow the pattern from above for the keys bropat provide we should have:
I hope this helps. I’ll have another look at this this week, but hopefully this is helpful 🤞🏼 I can share my device_sn’s in the mail if you like 😉
@fuatakgun I implemented it already for Homey.
You can take
getPropertyValue(PropertyName.DevicePicture)Then you get a data field which contains the image buffer which you can turn into a picture 😃
For what it’s worth, this is my integration in Homey: https://github.com/martijnpoppen/com.eufylife.security/blob/main/drivers/main-device.js#L497
If anyone is interested I found an interim solution on Android using Autonotification+Tasker to grab the snapshot and post in it on a Home Assistant camera using the Push Camera integration. Works flawlessly.
Same here indeed, had some other priorities last week. Will try to spend some time on it this week.
@PhilippEngler I’ll forward you the same mails I send to @bropat
The IDA files only contain the relevant functions. Someone else decompiled it for me, as I don’t have the pro version
@martijnpoppen can you send me the IDA functions too? 😃
Ah thanks @PhilippEngler that makes sense. Saw that GenCheckCode is there since app V2.
I do think you’re right about the 3 first functions, also tried some different implementations but got the same result everytime.
Next to that I did just find something in the firmware 😄 There’s home_security file there too with functions
save_the_enc_pic,gen_rand_seed,gen_check_code_v1,gen_base_codeMight be useful ?So small update from my side
I spend some long evenings on this. Unluckily I’m not really helpful…
Java and reverse engineering APK’s are not my expertise. Certainly not reading Peudo Code C files 😅
I did take another approach at getting the
genCheckCoderesult. I hooked upfridato my emulator and ofcourse I also gotD174D8169229AD39F55E6C3D801E4169back.Next to that I tried to dissasemble the
libhome_security.so(same aslibenc.so?) withRetDecandIDA ProandHopper. TheIDA Profile is better readable than the RetDec variant, but still I couldn’t manage to get anything useful out of it.If it helps I can share you the IDA Pro functions.
Also trying another approach: I downloaded the
ROMof my indoorcam. Hoping there would be a encrypt function somewhere which would help us decrypting it. No luck yet, but will dig further. Some info on the ROM hereMight be interesting…: it seems like the Homebase 3 isn’t encrypting it’s images…
If I am right, the image path in the notification event is only contained when the extended push notification is enabled.
I have taken a deeper look at the app and the p2p communication: The app use p2p to fetch the preview image from the HomeBase. Therefor the “cover_path” is used. The “~” is to split the path in station serial and the path to the image.
For now, I was able to build the p2p request, sending it to the station and receiving data from the station. What is missing is parsing the data and store the image (and last but not least the confirmation, that the data contains the image).
You can take a look here: Link to PhilippEngler/eufy-security-client/commit/4bed085f413df9fb04ee35e8a71cac59d45011f1
I checked my Eufy Cam 2 and the notification image is still coming from the cloud, except that the image file is encrypted. The first 41 bytes are a custom header from Eufy that contains info to decrypt the beginning of the image data. Only the first 256 bytes of the image data are encrypted with AES.
Downloading the image does not work. Eventually there is a new CommandType for downloading images. I could not find anything regarding this in the android app (v4.5.1), maybe someone else finds something.
For all recordings done with older firmware, the lifetime of the CDN-Links was reduced from 24 to 1 hour in my case.
Correct, but the Indoor Cam PT (T8410) hasn’t the parameter
cover_path😉 In the next version of the library, the initial image will be fetched from the database (only if an sd card is inserted, formatted and working) for this device (already works):https://github.com/bropat/eufy-security-client/commit/db5057289962d551af1fae3803fd422cd5fee75c
@martijnpoppen
Okay, complicated stuff, hard to distingiush what stuff is done in @bropat stuff and @fuatakgun parts 😃
Will wait for update on his side 😃
Great, appreciated @martijnpoppen
Hi all,
@bropat thanks again for great solution. So, do we need to download the latest image now, rather than relying on url?
@bropat thanks for the update. Works perfect!
One thing: the initial Image is not working. Somehow the command CMD_DATABASE_IMAGE doesn’t seem to work.
Thanks, @bropat and @martijnpoppen. Great work. I had finished the image download from homebase part, do you need it @bropat?
@bropat Nice work! 🎉 💪🏼
I just tried it in my script and works like a charm 😄 So the expected outputs are indeed as the ones I shared last week !
Thanks a lot! Your tasker profile was a good starter, though I had to do some adjustments as the curl call did not work on my Fire Tablet house dashboard (apparently no curl installed on these devices…), so I used the tasker http api. A native implementation decrypting the data in the library would of course be preferred, but at least I got pictures on my tablet again. Hope this works as flawless for me as it does for you. Thanks again! 😃
It’s in your mail @bropat
@bropat @PhilippEngler Sent! 😄
The
gen_check_code_v1function produces a SHA256 hash at the end, whereby only the last 16 bytes (of the total 32 bytes) are converted into uppercase hex string (32 characters long) and returned as the result. From this result, the first 16 characters of the string are then used as the AES key.I’ll have a look soon 😃
it is not an easy thing, agree. Let me put more detailed example here, I am planning to add this feature into Home Assistant integration soon (get the latest recorded video)
Now, you need to merge video and audio bytes into one video file, save it somewhere so you can watch it later on. Or you can live stream these.
So, your question had changed a lot. So you know now it is capable of doing it but you don’t know how to do it.
You can more info below or different previously created issues.
https://github.com/bropat/eufy-security-ws/issues/31
Get video events Get cipher and path from events Send download video command with cipher and path You will receive bytes of video and audio with video data and audio data events
Thank you for clarification, @martijnpoppen, I have misunderstood that point, sorry.
Hi martijnpoppen,
this seem to be implemented by the firmware version 3.2.5.1. My HomeBase E gives another link to the image (T8002REDACTED~/media/mmcblk0p1/video/20221210235159_c00.jpg). My cams on the HomeBase 2 have not detect any motion since the firmware update.
I think, we have two options: We can use the
stationDownloadfunction to download the image (i will try that tonight). Or, if this does not work, we have to implement function like bropat did it in his ioBroker plugin (onStationDownloadStart function in bropat/ioBroker.eusec). There he is downloading the video and create a thumbnail from the downloaded video. But this will add new dependencies (mainly ffmpeg).