Have been tested only in these Boox ereaders. If you found it available for any other Boox ereader, please submit the strings.
Note 1: There is also another method to fetch decrypted update.zip
from @shunf4.
Note 2: There is a way to get downloading url of latest firmware.
Note 3: payload.bin
can be extracted with https://github.com/cyxx/extract_android_ota_payload.
Any other issue and pull request is also welcomed!
The detail of algorithm (Simplified Chinese)
Python3 should be installed to run the script, and pycryptodome
a dependency of the script should also be installed:
(BTW: in some environments, the following pip
and python
should be replaced with pip3
and python3
)
pip install pycryptodome
You can run the application by putting update.upx
file in the project directory and running
python DeBooxUpx.py <device model> [input file name [output file name]]
<device model>
is required, and [input file name [output file name]]
is optional. The input file will be update.upx
and the output file will be saved in the current working directory, if not set in the arguments.
For list of currently supported models please refer to the table.
There is a python class DeBooxUpx
in DeBooxUpx.py to decrypt update.upx
, and dict boox_strings
where there are known strings.
Following is a example to use this class to decrypt the update.upx
of Boox Nove Pro:
from DeBooxUpx import DeBooxUpx, boox_strings
model = 'NovaPro'
updateUpxPath = 'update.upx'
decryptedPath = 'update.zip'
decrypter = DeBooxUpx(**boox_strings[model])
print('When updating, the device decrypt update package into', decrypter.path)
decrypter.deUpx(updateUpxPath, decryptedPath)
Or for manually setting strings:
from DeBooxUpx import DeBooxUpx
MODEL = "NovaPro"
STRING_SETTINGS = "j857wYAQcWZgvIEQ/tcQqzxreUJgFHwJl6D2TN9BuSkQ"
STRING_UPGRADE = "+soGw/YVdGIRJiAs5SMmv1ihW37H1Fa9+/1w2Vgt14Ag"
STRING_LOCAL = "lpsj9NJ8Kzv8jHb+OO8A5lxC+9Zhl243bFmDZYaF"
updateUpxPath = 'update.upx'
decryptedPath = 'update.zip'
decrypter = DeBooxUpx(MODEL, STRING_SETTINGS, STRING_UPGRADE, STRING_LOCAL)
print('When updating, the device decrypt update package into', decrypter.path)
decrypter.deUpx(updateUpxPath, decryptedPath)
MODEL | STRING_SETTINGS | STRING_UPGRADE | STRING_LOCAL | |
---|---|---|---|---|
Gulliver-ru | MC_GULLIVER |
wDNy4yMayA4nnQfzAYYqV3ih6uu363MoyfhaX9MtbCQk |
xzUDklMaz39aPNJAl45ca7c3hOrBQlNYZh+iZdK64fIo |
3xRR0nR2lFLKpw5g71X5JGqc0/hZQhmJM9idyx1b |
KonTiki2-ru | Kon_Tiki2 |
eqxOVE1h8e8hbGNiV2ZHed6hMpcOH3vULx6XMm/WguZ1 |
dKA6Ik0R95nDwjGR/dfPwxkYYNBkfngJk51A2MlRxBsq |
bPBuYxkL37AYgKbD6nRxEL6EaFCFzFgPij53pTwH |
Leaf | Leaf |
kh5V130fLNcO5lgsSDq7aUxQOz98DzEmiur3YsYKreIs |
kBtW135kW9zvvgRlsTolCktkJ7+MeW5ZoNgr3xyr2SpX |
jzx1lloJdIBGjmcyS9oYPmXvTKFp4dpj66fPcaON |
Max2 | Max2 |
vtIkV3LyRAzlnqbIHX3PFsRUI4iP6AgBDlYmkw8OtkZQ |
uqhRVHL0SwBFPHF8wgyzGaR9XhIlkW/1ab0p7UjuDCjo |
pvRyElGZH1rWeTUPJ8K3uV2MqyYmjpYYTW2y1Grc |
Max3 | Max3 |
1wdvUHZmcz32N1pgG4fkHmDsTDVihMJsPCNV4mW/6u1k |
3nxuLgdpBE3B3n1Yyymt4cOS8dNucfQxK8YOsmcemuyO |
yCA9YlFxLBdLbDUl3vwzPkn9vtYuVFZCfhrOTvR1 |
MaxLumi | MaxLumi |
mTZFN0K+oMcGnn2n7+zV5DH7kr/Hbes2x/wKDJp6K7Kq |
mj0zR0Oy3L4R+6y49MIEQT9bdx9AVz8TWyG9q3N+d9VY |
hWAUdhOp9ekIYxIW+LpVj6OviWBbCbRa1c7s1jtW |
MaxLumi2 | MaxLumi2 |
zR5/dfK/2XhmwhA9AIGr+gx6Vzho5sbklSTjLAgnC8/C |
zh98dPzN0QdwbRem1Yfx28DFxU+Gkvg4MpLDIp0GorRU |
|
Note | Note |
0WZeSahj4BlwNAJJkcSJEdktwbc2xdYhN+pEl+7XwuJv |
oRJROqUQ4xgcx7zvmLyPLeysH+cCU39EGXg77NZar8AP |
z0YIfPx+vETibLDToPlDQPl54yE55JUFayfkx1+G |
Note2 | Note2 |
etwiPPEXAQRj3m+e0Q2FOxT16aJ8XexQAqhGn5NqZWv1 |
et0jSPpmd3ueGHLmMf+2yyXVn18sa2HrDg56dCTFH6lf |
YY9wfqN7K1LlSug47Tr5Y8QkDHmmJ4VDCJ58mhoV |
Note3 | Note3 |
uTiMM5JgTXOCZAZKMcZIzc1yQpfX1+jxTFOred3te4z9 |
zEf3SZ8TOADA8QuwOHicGLrrc4EA7sffKfc01TlUfe/q |
pmXVBMt5EllxXhD9L6/NWH/pTZXRURP6QLsrNlx6 |
NoteAir | NoteAir |
U04vqHYo0LFoJQAvP3Rs1aBxySs7z4T1B+asamimEoAf |
VD1ZqgIl08305CVeftRI7qGBtq9bCMrJ3a1VkpkzjOu2 |
PW8K6VI//Zt6iqjYQWN0LIwRTVYyDHJvHNEXFwWV |
NoteAir2 | NoteAir2 |
iCTAyj86sQAvF/XUuLBO2dS4AWZVJsy+pPmvR9wkKqhe |
iCbPzTBHtXSRc3HdNnswRPX6Vp222OGR/rgGb3ZYtKIM |
|
NoteAir2P | NoteAir2P |
9dRjKa5Bv6SumZaiR6B5Bmxgu/JuRoSCdqVEX1P8itHp |
9aBiXK02yte/JCfSq1/AtQg74phmxvZv1dGaRVsM646e |
|
NotePro | NotePro |
MjR72bOazBUacJwDcuWgtm/E0A9F9ahIt1buweEPA020 |
RjV8r7+fx2Wjp6rUSrBOpmqYnHKs7eReqTTcy9k4c3tn |
W2co6eaDmEl7jIjOSqr11C71RDHHiV3p5oG2G54X |
NoteS | NoteS |
+YKimeg208RzM8InqtuUjSIopyM2OLIMpFeeTQby2Au4 |
+vap6p4w0MaT5A8RB4+ru3gnFtIq2g4K+tcjQjy3N599 |
5qT7388vj+/FwLMX/cIXSCgSxSIg9SAf0tB4NcEI |
NoteX | NoteX |
xhffOONo9wDGZquv93yJoLm0z6igW+XL17PUyfR8ky4l |
zBGvTe9t8HZQXu2Q85Dl0iI9R9tEg2VRw/8cEYXauU3o |
|
Note_YDT | Note_YDT |
Utaad8dgPmSgBOjFknI0PQAp/Sc+v71Hml0ldJIzecDx |
ItvgBbJhOxaKjMyiE/yDufmOWwHvTPNfxdOJ2XQTHXaO |
PIe5RuMNYz8G5FmaqRUu2qHI0br1Is3too/sLts/ |
Nova | Nova |
L0uopm+jYaWWf/0e/POLt0kkBuS3H+5axpS6cqUpn4ft |
XE6jpB3WZ9J5xQdh6GFchFbeBMALt6Zx/UIg8jaiaI72 |
Rh/6kzjOT4nJCsXC5JMEkcbPzzBmNkB8i/c6ZNun |
Nova2 | Nova2 |
lxXh4Vv6aqYecCAFc/hsn4mnXNbI6H4S3bZFW5Jh8NHj |
lBabky+FbaOtZ7luDK+7BlApiYcGEi8PndwIc5WaemXQ |
iDDDo3jsN4hhLA3tQhaIkM4XLcxZT4czBMM7ExnK |
Nova3 | Nova3 |
VUkFew9KjsE54uQMSZI+S2tT1RRckT/vKkfiFFqImxi7 |
VEsMcHw88MpwOByhT7zqNhRFTQcruVMqhdllIlY6T+6f |
TGlcNi8npJe4EHxzOKbCXakJKDssoRldHueY5OGl |
Nova3Color | Nova3Color |
5VJqMbDB52k04k29Wc4itQBtLTRDq5kEdQaQ+GZ324PC |
ky0eR7CymhuAUrRBaeii5tq/ezcnXMQkT+WV1OrmRqQa |
/Q89A5Xbsza/FBiIu8LUV5bIiLf9kXDjAdJPjyz+ |
NovaAir | NovaAir |
6NTlwbVEYP1BPTdQE25u6TCpoCg988I/Cjcs0Wxrwa4O |
66aRwLgxGv1vNmOsbVWp37OBy4RugvpZvUom3VQxWGrM |
9YDFh+EuTqFECXRvmEbL78mtW69CQRENjADM8A+L |
NovaAirC | NovaAirC |
ycihhTCxjfQO85KdVPvpA1ZzCPr3eJ/KhcKEKlqMhEfR |
zsnQgzXEhvsfL4pk48rtEckIPyDedhybURHP198tag+Z |
|
NovaPlus | NovaPlus |
MtoBkApRAVwzdGe2CTnaE4MIgevRYNQfaKo606tyUQNY |
Nttwkwxaei8xorBu/uUBpUu8nNZHTRIAZMZc0xrJs9Ti |
LIYj1F9NVXFrOfi24/C76gFFxHYSCJ4mfhYI4q5w |
NovaPro | NovaPro |
j857wYAQcWZgvIEQ/tcQqzxreUJgFHwJl6D2TN9BuSkQ |
+soGw/YVdGIRJiAs5SMmv1ihW37H1Fa9+/1w2Vgt14Ag |
lpsj9NJ8Kzv8jHb+OO8A5lxC+9Zhl243bFmDZYaF |
PadMu3 | PadMuAP3 |
TCP3lGFLuxm7wOXWnaomQAdYikpFPAOj5U2LK0Dck3Un |
PC3wkRM4zhgstNIQLGR+dW9jourXdEXZXU/mN7bTACu0 |
In/UoUFVkUdHTCqlSfCgKi8MEZGHK0Xc70Y5trXs |
Poke2Color | Poke2Color |
I7ewiUSud0x9auT0PKp29393K5Hg3ymr1VJY5eUhoHEm |
JLe4ijbRcj5L8S9cRPRGL7eoEIKjT8OOblhy/wyvSbze |
SODgyWbHLhfjy4WWk6lhqhYXnP1FTjSjtzMTyZkl |
Poke3 | Poke3 |
lU95mOkt0cGucrsrIdAWuYnoJEnTTfIvu/QNUlcmI42A |
kjl4lOMqobWYQyqX4KzBGYS8Q0OwPSfqwf29ymkypULP |
iW9b2bszjJhv3puv87HNQXLW3Fb5uQVhWnnKU4nV |
Poke4 | Poke4 |
uTKx3XVyRhlbI7hoHuy/NiYdwlWViPlc4EecZKYTThN/ |
vzDFqwIEMRfqTPUCV82ahjnz0hXfcZCIxRY8ljuLmTaf |
|
Poke4Lite | Poke4Lite |
9SHJReeuD4hvqiwtY8mNlKTYqGkvgao4d9/9og7/EXhV |
hiDOQumqD/7tGPsr5l69IdujC33cOPwOQw6C9wE8s8Xn |
|
Poke4S | Poke4S |
iq8+KrwLX7JGcq/B823vpSmVn5CJR2QscYDeArKjtn0B |
+txDKcp4Kse6Ve1kBswZWQ68i+Kpab8BoXDrgpmqQrjz |
|
Poke_Pro | Poke_Pro |
6GxQ6Iei3y2PSf6Wlayz+0f6yVnl1GXe5OC3q3i2lasO |
72lZ7vTQoy9/ESDKRQtx2V5uBMDiR+ik3n+soo9wGAbJ |
hkkAqdfM93RpnCgXxhUqzme3OMzT6tDWC3fyibgW |
SP_NoteSL | SP_NoteS |
XjzywnKKh3FNVKphg5MZ9xILmDNYBZiCHTnkd6Q0fqpq |
XDeIyQKAhQKj6/UmdAIWoL0aXmw9PeEFl9OpII/1TO9i |
QmGqhFGXqiuQ9yoeVx0a2SQ8BNojjPn96o6hQW6U |
livingstone-ru | LIVINGSTONE |
ES5zbiHTesxFb+zdkjxiqJ+1dOwyOCv2BzCV7fOYDJxf |
EFwGaFSherxX1k6Hl/U6TiQhGJOzTPDfsPzHg+z9guln |
fQ5TLnfKUOHq0f7XXdu9b1FGAckqI576ZBPkZfPg |
The models sold in Russia are marked with suffix -ru
, some or all of the firmwares for which don't use the same strings with their international versions!
PS: SP_NoteS is SuperStar (chaoxing) verison.
MODEL
string is the model name, exactly the output of getprop ro.product.model
and/or value of ro.product.model
in the file /system/build.prop
.
Other strings can be got in following steps:
-
Get
/system/app/OnyxOtaService/OnyxOtaService.apk
USB Debugging (adb) is one of the the available ways:
-
Turn on adb in Settings -> Applications -> USB Debugging Mode
-
(from @mgrub's note) Connect the ebook to computer by usb, run
adb wait-for-device adb shell 'pm list packages -f | grep ota'
And then the path of ota package will be showed. For example, the following output is in Nova Pro:
package:/system/app/OnyxOtaService/OnyxOtaService.apk=com.onyx.android.onyxotaservice
So in this case the path is
/system/app/OnyxOtaService/OnyxOtaService.apk
.In the following steps, we assume that the path is
/system/app/OnyxOtaService/OnyxOtaService.apk
. -
Run the following command
adb pull /system/app/OnyxOtaService/OnyxOtaService.apk .
Now the apk is got.
-
-
Get the strings from apk
- Use Apktool to decode the apk:
apktool d OnyxOtaService.apk
- Now the strings should be in
./OnyxOtaService/res/values/strings.xml
, for example the NovaPro one:<?xml version="1.0" encoding="utf-8"?> <resources> <string name="settings">j857wYAQcWZgvIEQ/tcQqzxreUJgFHwJl6D2TN9BuSkQ</string> <string name="upgrade">+soGw/YVdGIRJiAs5SMmv1ihW37H1Fa9+/1w2Vgt14Ag</string> <string name="local">lpsj9NJ8Kzv8jHb+OO8A5lxC+9Zhl243bFmDZYaF</string> </resources>
settings
isSTRING_SETTINGS
,upgrade
isSTRING_UPGRADE
, andlocal
isSTRING_LOCAL
- Use Apktool to decode the apk:
-
Add and verify (optional) the strings
Add the strings to
boox_strings
in DeBooxUpx.py, use this script to decrypt aupdate.upx
file (it can be got by the method in #2), and see if theupdate.zip
file is a vaild zip file.
In Boox OS since Android 11, these strings, which have been moved to libota_jni.so
, are empty in strings.xml
. In this case, please install and run GetBooxUpxKeys in your device to get keys, or refer to 22#issuecomment-964035840.
This method requires you install an apk in your device.
If you don't want to install the apk in Method 2, you can use Method 3. This method uses the emulator to get the strings.
MODEL
string is the model name, exactly the output of getprop ro.product.model
and/or value of ro.product.model
in the file /system/build.prop
.
Other strings can be got in following steps:
-
Get
/system/lib/libota_jni.so
(for 32-bit device) or/system/lib64/libota_jni.so
(for 64-bit device)Connect to device via
adb
as above. Then execute command:adb pull /system/lib/libota_jni.so
or
adb pull /system/lib64/libota_jni.so
-
Prepare environment
Clone the submodule.
git submodule init git submodule update
Install python dependencies.
pip3 install unicorn capstone
-
Execute
ota_jni.py
python ota_jni.py libota_jni.so ERROR:androidemu.internal.modules:=> Undefined external symbol: ERROR:androidemu.internal.modules:=> Undefined external symbol: __cxa_finalize ERROR:androidemu.internal.modules:=> Undefined external symbol: __register_atfork ERROR:androidemu.internal.modules:=> Undefined external symbol: __cxa_atexit ro.kernel.qemu was not found in system_properties dictionary. libc.debug.malloc was not found in system_properties dictionary. WARNING:root:File does not exist '/proc/stat' WARNING:androidemu.internal.modules:libcrypto.so needed by libota_jni.so do not exist in vfs ExAndroidNativeEmu/vfs WARNING:androidemu.internal.modules:libutils.so needed by libota_jni.so do not exist in vfs ExAndroidNativeEmu/vfs ---------------- STRING_SETTINGS uTKx3XVyRhlbI7hoHuy/NiYdwlWViPlc4EecZKYTThN/ STRING_UPGRADE vzDFqwIEMRfqTPUCV82ahjnz0hXfcZCIxRY8ljuLmTaf
You can test this method with
python ota_jni.py test/libota_jni.so
, which should give the above output.
Refer to CONTRIBUTING.md.