Author Topic: New Zoyi multimeter+oscilloscopes - ZT-70xS, up to 50MHz/250MSps (nov 2022)  (Read 431563 times)

0 Members and 17 Guests are viewing this topic.

Offline RedFox01

  • Newbie
  • Posts: 5
  • Country: pl
Some information on the firmware update file format (ZTK):

16 bytes header:
0 to 3: file length - 16 (header length)
4 to 7: unknown, looks like 16 bit uint (bytes 6 and 7 always 0), maybe checksum
8 to 15: always "OSC7EF91"

starting at byte 16 is the firmware

The encryption looks like taking 8 byte blocks. It is not a real block cipher as statisctical analysis reveals, that the bytes
87 D0 A9 A6 1A D6 2A 78 decrypt to 0, when they are in the position 0 to 7 or 8 to 15 respectively

Simple XOR with byte swapping did not bring any usable result

edit: my assumption above, about the block cipher is probably wrong, as the 8 bytes above almost alway appear in the exact order.
The MCU supports DES, 3DES, AES in hardware, because of the 8 byte blocks, DES and 3DES are possible candidates

Yes, Firmware .ZTK is encrypted AES-128-ECB
Key: aff05338fa552bf2f2f05338fa552bf2 <- this is the wrong key
The key can decrypt and re-encrypt .ZTK and get an identical file, but after "unpacking" the firmware .ZTK is still XOR with a mask or function, or custom decode etc, but you can modify the byte, which could mean there is no CRC check?

decode_ztk.py:
Code: [Select]
from Crypto.Cipher import AES
import sys

KEY = bytes.fromhex("aff05338fa552bf2f2f05338fa552bf2")

def decrypt_ztk(infile, outfile):
    with open(infile, "rb") as f:
        data = f.read()
    cipher = AES.new(KEY, AES.MODE_ECB)
    decrypted = cipher.decrypt(data)
    with open(outfile, "wb") as f:
        f.write(decrypted)
    print(f"[✓] ZTK decrypted → {outfile}")

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: decode_ztk.py input.ztk output.bin")
        sys.exit(1)
    decrypt_ztk(sys.argv[1], sys.argv[2])

python decode_ztk.py ZOYI-703S-FW-V156.ZTK decrypted.bin

encode_ztk.py:
Code: [Select]
from Crypto.Cipher import AES
import sys

KEY = bytes.fromhex("aff05338fa552bf2f2f05338fa552bf2")

def encrypt_bin(infile, outfile):
    with open(infile, "rb") as f:
        data = f.read()
    if len(data) % 16 != 0:
        raise ValueError("Input .bin size must be multiple of 16 (AES block size)")
    cipher = AES.new(KEY, AES.MODE_ECB)
    encrypted = cipher.encrypt(data)
    with open(outfile, "wb") as f:
        f.write(encrypted)
    print(f"[✓] BIN encrypted → {outfile}")

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: encode_ztk.py input.bin output.ztk")
        sys.exit(1)
    encrypt_bin(sys.argv[1], sys.argv[2])

python encode_ztk.py decrypted_modified.bin patched.ZTK

« Last Edit: April 14, 2025, 05:50:05 am by RedFox01 »
 
The following users thanked this post: m72

Online OLderDan

  • Regular Contributor
  • *
  • Posts: 74
  • Country: au
User ajar171 hasn't logged into the forum for a month or more. I think he abandoned the custom firmware project.

lol, are you still waiting for your dad to get back from the shops when he ducked out to buy a pack of smokes? I am certain that not being active for a month could have far less drastic reasons than abandoning the project.
 
The following users thanked this post: sp3rat

Offline bffargo

  • Regular Contributor
  • *
  • Posts: 77
  • Country: us
Reports on the mydigit.cn forum is a 906s is soon to be released. No specs yet
 
The following users thanked this post: OLderDan

Online OLderDan

  • Regular Contributor
  • *
  • Posts: 74
  • Country: au
...My post was not for you, so you dont have to be so "kind". English is not my native language, so if my sentence sounded strange, yours written in Polish would probably sound strange too.
 I wrote it to RedFox01 who quoted sjsr171 and will wait for an answer which will not come.

sorry mate, was just having fun. It wasn't language based, more of a country music theme.
 

Offline hawk

  • Newbie
  • Posts: 1
  • Country: pl
is there a way to disable beeping on every function change in multimeter mode?
 

Offline RAPo

  • Frequent Contributor
  • **
  • Posts: 983
  • Country: nl
only for the oscilloscope mode, not the DMM-mode.
is there a way to disable beeping on every function change in multimeter mode?
 

Offline Kris0725pl

  • Newbie
  • Posts: 7
  • Country: pl
    • My Youtube Channel
Hi all...regards from polands. We have create application for zoyi 703s workiong with windows and working with OBS Studio.

Some screen is above:
2547190-0
2547194-1

For the transparent preview in obs we must add filter(chroma key) in obs to color black. Works as well.
I need some people for test it :)
« Last Edit: April 15, 2025, 10:26:08 pm by Kris0725pl »
 

Offline ajar171

  • Regular Contributor
  • *
  • Posts: 98
  • Country: de
Hi guys,

sorry for the long time away, my life got turned upside down because of some private issues.

I will try to pick up, from where I left off in the easter holidays, and also try to read and answer all the posts that I have missed.
 
The following users thanked this post: apollo11fan

Offline ajar171

  • Regular Contributor
  • *
  • Posts: 98
  • Country: de
only for the oscilloscope mode, not the DMM-mode.
is there a way to disable beeping on every function change in multimeter mode?

no, the beeper is directly controlled by the DMM chip. Turning it off is impossible in software, a beeping can be added in the scope mode (by a dirty hack), but then the DMM chip has to be powered on countinously, which just wastes power.
 
The following users thanked this post: RAPo, hawk

Offline ajar171

  • Regular Contributor
  • *
  • Posts: 98
  • Country: de
Yes, Firmware .ZTK is encrypted AES-128-ECB
Key: aff05338fa552bf2f2f05338fa552bf2 <- this is the wrong key
The key can decrypt and re-encrypt .ZTK and get an identical file, but after "unpacking" the firmware .ZTK is still XOR with a mask or function, or custom decode etc, but you can modify the byte, which could mean there is no CRC check?

I don't think its AES, the block size does not make any sense because "87 D0 A9 A6 1A D6 2A 78" is "00 00 00 00 00 00 00 00" decrypted, and that is only 8 bytes, where AES has a 16 byte block size. DES (and 3DES) has an 8 byte block, and is supported in HW by the MCU.
 
The following users thanked this post: RedFox01

Offline ajar171

  • Regular Contributor
  • *
  • Posts: 98
  • Country: de
also there is no unpacking needed, the interrupt vector table can be clearly seen in the .ZTK file, it is just encrypted.

What keeps wondering me, is the .ZTK file size. The MCU used (on the PCB v2.2) has only 128k of flash memory, the update file is a multiple of that... Maybe they used different MCUs over the time of the production and have muliple firmware images in the update file?
 

Offline RedFox01

  • Newbie
  • Posts: 5
  • Country: pl
It is possible, and what I managed to determine is that when you upload damaged firmware, a rescue mode is triggered, and a RAM/Firmware folder appears, about 600KB in size. You can manipulate GUI strings and replace them with others, as long as the byte size remains the same. I tried using Hashcat for testing, with my RTX 3060, the process would take around 4 days, or even up to 10 in some cases, but I’m not sure if I found the correct hashes.
 

Offline ajar171

  • Regular Contributor
  • *
  • Posts: 98
  • Country: de
My GPU is nowhere powerful enough to try a brute force attack (potato PC).

If you have the patience, you could try to DES decrypt (brute force) "87 D0 A9 A6 1A D6 2A 78", when it decrypts to "00 00 00 00 00 00 00 00", then we can try the key on the whole firmware. Brute force for 3DES would not make any sense.

The only interesting part of the firmware is the bootloader, the rest does not work properly anyway, and I don't know, if it is included in the update file. If the bootloader could be extracted from the update file, it would be possible to switch back and forth between firmwares.

If this is worth the time and effort (and also electrical power for your GPU) is up to you to decide. I personally don't need the firmware key or their bootloader.
 

Offline indman

  • Super Contributor
  • ***
  • Posts: 1312
  • Country: by
If the bootloader could be extracted from the update file, it would be possible to switch back and forth between firmwares.
It would be great if you or someone else could do it.
 

Offline ajar171

  • Regular Contributor
  • *
  • Posts: 98
  • Country: de
Even if they are only using DES, searching the whole keyspace with an RTX3090 would take:
2^56 / 2787.3 MH/s = 299,2 days
An RTX4090 would need maximum 132 days and an RTX5090 91,6 days

On average you can find the key in half of that time.

Again, I don't have the hardware to do this.

Glitching the MCU is a much more pratical way.
« Last Edit: April 18, 2025, 04:11:24 pm by ajar171 »
 

Offline chebo

  • Contributor
  • Posts: 32
  • Country: 00
After upgrading to version 156, the following problem appeared. What to do now?
https://youtu.be/NlBVoNhc078?si=AUQjqd4prCf1g7au
The device does not respond to any buttons.
« Last Edit: April 18, 2025, 05:00:28 pm by chebo »
Experience is what we got instead of what we wanted...
 

Offline RAPo

  • Frequent Contributor
  • **
  • Posts: 983
  • Country: nl
Open up the device, take the battery out. Wait 10 seconds, power it up with USB. Reinstall the firmware while on USB power. Shut down and add back the battery. Close up the device, and power it up.

After upgrading to version 156, the following problem appeared. What to do now?
https://youtu.be/NlBVoNhc078?si=AUQjqd4prCf1g7au
The device does not respond to any buttons.
 

Offline chebo

  • Contributor
  • Posts: 32
  • Country: 00
Open up the device, take the battery out. Wait 10 seconds, power it up with USB. Reinstall the firmware while on USB power. Shut down and add back the battery. Close up the device, and power it up.
Thanks a lot! I will try this on Monday at the office. I'll let you know the result.
Experience is what we got instead of what we wanted...
 
The following users thanked this post: RAPo


Offline RedFox01

  • Newbie
  • Posts: 5
  • Country: pl
My GPU is nowhere powerful enough to try a brute force attack (potato PC).

If you have the patience, you could try to DES decrypt (brute force) "87 D0 A9 A6 1A D6 2A 78", when it decrypts to "00 00 00 00 00 00 00 00", then we can try the key on the whole firmware. Brute force for 3DES would not make any sense.

The only interesting part of the firmware is the bootloader, the rest does not work properly anyway, and I don't know, if it is included in the update file. If the bootloader could be extracted from the update file, it would be possible to switch back and forth between firmwares.

If this is worth the time and effort (and also electrical power for your GPU) is up to you to decide. I personally don't need the firmware key or their bootloader.


Correct me if I'm wrong, and I'm not good at cryptography, but it looks like the firmware uses AES-128 ECB.
GUI strings are composed of 2 blocks of 8-byte text, which fits AES encryption of 16-byte blocks:

Code: [Select]
78AB4746A80960D9BC0EA4AAA0C1AB33 = V1.5.6
78AB4746A80960D9AB00A774EF908ADE = V1.5.5
4AB547A5A82E35E9E3D92B6C834E4553 = V1.4.9
DEB87BB419C5412A245324A3D3EEAA26 = V1.4.2
2559D4F1E3E538F3 = V1.2.7
F9F9925DFC02AC2A = V1.2.8
DC3BAA85DE1EAC82 = V1.2.9
1D86350CF252E100 = V1.3.2
7E4FA23AE0F63BFF = V1.3.8
1A88E8D2F13DD44F = V1.3.9
V1.5.6 -> 245324A3D3EEAA2605D715391355B184 = FFT-CH2
V1.5.6 -> E31C013FA78A6BF6ACC543221F240DFE = CHINESE
V1.5.6 -> A1216476C9CECE796213E066BE0C9AA4 = Trig Source
V1.5.6 -> F191D84F18D2E8BA04B2E45E70C092E0 = Auto Off
V1.5.6 -> 9C3C490A77764867E5A23F2EC5841BD4 = Vertical
V1.5.6 -> 7BADED8E6D51ED8B12863980A355D38F = Channel
V1.5.6 -> FB1C51E1D0067FA99C86610A32B71342 = 120min
V1.5.6 -> 255B944F2A9366A2B887A32F6DF3F1A2 = 60min
V1.5.6 -> DB501172D322F31588C157A38B4507F1 = 60s
V1.5.6 -> 74CBE8D46197A2D233A6AB203F4C1557 = Version
V1.5.6 -> 140D8ED0EAF857F595B67DE8D060C15E = H and V
V1.5.6 -> E58074BBF918A3CA3BC9DC9D91B54466 = Uart
V1.5.6 -> 0982A1DBDBC9F3CF2C2D21B5D447A399 = OutPut
V1.5.6 -> D92BC62B312C1AC10476D4C76E89D263 = OFF
V1.5.6 -> 37367548364C4543D9199DD3C281E0E6 = ON
V1.5.6 -> 2D9F547C45BBF8F11F8438E6DE98F02D = language
V1.5.6 -> 47EA7D7161DAB7C41883CAA500BEB7C5 = Курс
V1.5.6 -> 337C7637621A198027A939A074F461ED = Hopm
V1.5.6 -> 36BE9B7685328028C37E3BB7BCED81BE = Настр
V1.5.6 -> E34ABBF74FE95B18F5DDA59A557BBF40 = Mode
V1.5.6 -> E8F579BA180CE3A97DD97F7C2608296E = ting
« Last Edit: April 18, 2025, 09:07:34 pm by RedFox01 »
 

Offline Centrununion

  • Newbie
  • Posts: 2
  • Country: pl
test on wrong key which was above, first change to hex to test raw


Artery chips in Zoyi 703s


Entropy 7.859423 bits per byte.
Optimum compression would reduce the size of this 696368 byte file by 1 percent.
Chi square distribution for 696368 samples is 230456.93, and randomly would exceed this value less than 0.01 percent of the times.
t Arithmetic mean value of data bytes is 127.4196 (127.5 = random).
Monte Carlo value for Pi is 3.137625904 (error 0.13 percent).
Serial correlation coefficient is -0.033368 (totally uncorrelated= 0.0).
« Last Edit: April 18, 2025, 10:58:44 pm by Centrununion »
 

Offline ajar171

  • Regular Contributor
  • *
  • Posts: 98
  • Country: de
Just do a statistical analysis of the Zoyi file and an other STM32 firmware encrypted with DES-ECB (8 byte block) and AES-ECB (16 byte block). You should always start witch statistics, when you are up against an unknown file.

You can clearly see the block size of the Zoyi encryption. Also the block "14 E9 50 27 CD 1E 18 38" keeps repeating in the vector table of the FW1.5.6 giving you a hint about the block size of the cypher.

This is not a password hash, you want to decrypt a file.

Anyway, if you can successfully decrypt the file, after removing the 16byte header, there must be "00 00 00 00 00 00 00 00" at the address 0x20. Also in the interrupt vector table every fourth byte should be 0x08 (eg. at addresses 0x03, 0x07, 0x0B, 0x0F, 0x33, 0x37, 0x3B, 0x3F, and so on), just look at the vector table in the documentation of the ARM CPU.
« Last Edit: April 19, 2025, 06:51:02 am by ajar171 »
 

Offline Casper94

  • Newbie
  • Posts: 2
  • Country: be
Anyone got an idea why I'm running into this issue? I've got the ZT-703S, currently on version 1.39, but when trying to update to 1.55 or 1.56, there doesn't seem to be enough storage available for the update file. Looking at their file sizes, the 1.39 file is 554 kb and the later ones are 681 kb. There aren't any other files such as screenshots on the device ...
 

Offline bffargo

  • Regular Contributor
  • *
  • Posts: 77
  • Country: us
Anyone got an idea why I'm running into this issue? I've got the ZT-703S, currently on version 1.39, but when trying to update to 1.55 or 1.56, there doesn't seem to be enough storage available for the update file. Looking at their file sizes, the 1.39 file is 554 kb and the later ones are 681 kb. There aren't any other files such as screenshots on the device ...

Already addressed earlier in the thread. In the menus, chose to format the storage first, then reboot to USB firmware update mode and it will work.
 

Offline dn1983

  • Newbie
  • Posts: 7
  • Country: ua
Anyone got an idea why I'm running into this issue? I've got the ZT-703S, currently on version 1.39, but when trying to update to 1.55 or 1.56, there doesn't seem to be enough storage available for the update file. Looking at their file sizes, the 1.39 file is 554 kb and the later ones are 681 kb. There aren't any other files such as screenshots on the device ...
Or restore default settings in menu.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf