Tolino Shine 3 – upgrade if you have installed TWRP

If have installed TWRP and you want to upgrate to the latest firmware using the official procedure, you have to restore the original recovery.img.

  1. shutdown the device
  2. on the pc extract recovery.img from the update.zip file which contains your current firmware.

    tolino-15.3.0$ unzip update.zip recovery.img
    
  3. on the pc run the flash command and keep it waiting

    $ fastboot flash recovery recovery.img 
    < waiting for any device >
    
  4. (enter fastboot mode) keep pressed the power up button on the device while inserting the USB cable and in about 25 seconds you should see

    Sending 'recovery' (6474 KB)                       OKAY [  0.245s]
    Writing 'recovery'                                 OKAY [  0.906s]
    Finished. Total time: 1.170s
    
  5. reboot the device and now you should be able to upgrade it with the standard procedure

Tolino Shine 3 – 15.3.0 update

Good news

The adbd binary is the same of 14.1.0:

$ md5sum tolino-1*/ramdisk/sbin/adbd
1d23e203eba05102e6cb642a117b8d64  tolino-14.1.0/ramdisk/sbin/adbd
1d23e203eba05102e6cb642a117b8d64  tolino-15.3.0/ramdisk/sbin/adbd

The direct download link for the new Shine 3 firmware is https://tolinodownload-a.akamaihd.net/ereader/15.3.0/OS44/update.zip.

As a sidenote, you can find the latest Tolino updates on the Tolino website.

Everything documented is still relevant

Compile busybox (Magisk) for Android with ndk

Credits

All the credits to topjohnwu and osm0sis.

Prerequisites

I’m working on Ubuntu 21.10, so

$ apt install git google-android-ndk-installer

Instructions

Clone the repository

git clone https://github.com/topjohnwu/ndk-box-kitchen.git

Follow the instruction, as today are

git clone https://git.busybox.net/busybox/
git clone https://github.com/SELinuxProject/selinux.git jni/selinux
git clone https://android.googlesource.com/platform/external/pcre jni/pcre

Choose the supported busybox version

cd busybox
git checkout 1_34_1
cd ..

Build

/usr/lib/android-ndk/ndk-build all

Push on a device and test

adb push ./obj/local/armeabi-v7a/busybox /data/local/tmp
adb shell chmod 775 /data/local/tmp/busybox
adb shell /data/local/tmp/busybox date
Mon Nov 21 21:08:47 CET 2021

Install as you want, for example

adb shell
cd /data/local/tmp/
mkdir xbin
cd xbin
../busybox --install .
export PATH="$(pwd):$PATH"

Restore stock firmware on the Galaxy A7 (2018) using Linux

There are always good reasons to install a stock factory ROM for a phone, I can think of some of them:

  • clean factory reset before selling the device
  • install the latest firmware
  • install another official firmware (unbrand the phone)
  • downgrade the firmware
  • unbrick the device after a failed hack

Here’s what I’ve done.

Disclaimer

This is a complete reset, all the data will be lost, make a backup if you care about your data.

I am not responsible if you brick / ruin your device in any way. Basic computer skills required. Proceed with caution. I cannot be held responsible if anything goes wrong.

Device details

Model name: Galaxy A7 (2018)
Model code: SM-A750FN

Device Wikipedia page

Prerequisites

I’m working on Ubuntu 21.10, so I’ve to install these packages

$ apt install heimdall-flash lz4

Download original firmware

You can find original firmwares from various sites, in my case, I downloaded A750FNXXU5CUH2_A750FNOXM5CUH2_ITV.zip from https://www.sammobile.com/samsung/galaxy-a7-2018/firmware/#SM-A750FN. I had to create an account, so maybe there are better options.

Extract all the files from the firmware file

Explanation: the downloaded zip contains some .md5 files. They are actually .tar files, so we have to untar them (for the sake of clarity I prefer to rename them before). Each .tar contains, for the most part, .lz4 files. We have to extract the content of those files, too.

$ mkdir tmp
$ cd tmp/
$ unzip ../A750FNXXU5CUH2_A750FNOXM5CUH2_ITV.zip
$ rename 's:.md5$::' *.md5
$ for f in *.tar; do tar xf "$f" && rm "$f"; done
$ for f in *.lz4; do lz4 -d "$f" && rm -f "$f"; done

Here’s the content my folder after the extraction:

$ ls -1R
.:
A7Y18LTE_EUR_OPEN.pit
boot.img
cache.img
cm.bin
hidden.img
meta-data
modem.bin
modem_debug.bin
odm.img
omr.img
param.bin
recovery.img
sboot.bin
system.img
userdata.img
vendor.img

./meta-data:
download-list.txt
fota.zip

Boot the phone in Odin mode / Download mode

  1. shutdown the device
  2. disconnect USB cable
  3. keep pressed Volume UP and Volume DOWN
  4. connect USB cable

Start the procedure of complete firmware replacement

Test heimdall detection of the device

$ heimdall detect
Device detected

Print and store the partition table

$ heimdall download-pit --output a7.pit

(the phone will reboot and you’ll have to enter in Odin mode again. The --no-reboot argument of heimdall works, but the subsequent command will fail even if launched with --resume, so let’s reboot anyway.)

The .pit file is a binary file, but we can inspect it with the right heimdall command

$ heimdall print-pit --file a7.pit >a7.pit-decoded
$ cat a7.pit-decoded

Check if the device partition table matches the firmware’s one

$ md5sum <(heimdall print-pit --file a7.pit) <(heimdall print-pit --file A7Y18LTE_EUR_OPEN.pit)
737ea1830f953bdd7eb5297cf095592d  /dev/fd/63
737ea1830f953bdd7eb5297cf095592d  /dev/fd/62

Associate each file to the right partition to flash, as stated in the .pit file

$ for f in *; do echo -n "# $f: "; { grep -B1 "$f" a7.pit-decoded || echo "__not found__"; } | head -1; done | column -s: -t
# a7.pit                  __not found__   
# a7.pit-decoded          __not found__   
# A7Y18LTE_EUR_OPEN.pit   __not found__   
# boot.img                Partition Name   BOOT
# cache.img               Partition Name   CACHE
# cm.bin                  Partition Name   CM
# hidden.img              Partition Name   HIDDEN
# meta-data               __not found__   
# modem.bin               Partition Name   RADIO
# modem_debug.bin         Partition Name   CP_DEBUG
# odm.img                 Partition Name   ODM
# omr.img                 Partition Name   OMR
# param.bin               Partition Name   PARAM
# recovery.img            Partition Name   RECOVERY
# sboot.bin               Partition Name   BOOTLOADER
# system.img              Partition Name   SYSTEM
# userdata.img            Partition Name   USERDATA
# vendor.img              Partition Name   VENDOR

The .pit files must not be flashed, the meta-data entry in the list is a directory, so everything has a match.

With these informations we can build our flash command

$ heimdall flash --pit A7Y18LTE_EUR_OPEN.pit --BOOT boot.img --CACHE cache.img --CM cm.bin --HIDDEN hidden.img --RADIO modem.bin --CP_DEBUG modem_debug.bin --ODM odm.img --OMR omr.img --PARAM param.bin --RECOVERY recovery.img --BOOTLOADER sboot.bin --SYSTEM system.img --USERDATA userdata.img --VENDOR vendor.img

In my case it took ~3 minutes. Then the phone will reboot. The first boot will take up to ~5 minutes so don’t worry and be patient.

Tolino Shine 3: patch adbd to run as root

References

TL;TR

  1. install prerequisites
  2. create the script nop.sh
  3. extract adbd from initial ramdisk
  4. patch adbd

    ./nop.sh adbd "4f f4 fa 60 0e f0 86 ed 00 28 df d1 4f f4 fa 60 16 f0 47 f8 00 28 d9 d1" && \
        cat adbd.patched > adbd && rm adbd.patched 
    ./nop.sh adbd '0e f0 de ed' && \
        cat adbd.patched > adbd && rm adbd.patched
    
  5. recreate boot.img

  6. test and flash via fastboot

Prerequisites

  1. I’m working on Ubuntu 21.10, so I’ve to install these packages

    apt install abootimg openjdk-11-jdk
    
  2. We are going to patch a binary file, so we have to use a disassembler: Ghidra

Procedure

Extract the adbd binary from Tolino firmware update file

  1. download the right version of your update (for me it’s currently 14.1.0)

    wget https://download.pageplace.de/ereader/14.1.0/alldevices/update.zip
    

    (as a sidenote, you can find the latest tolino update on the Tolino website)

  2. extract boot.img

    unzip update.zip boot.img
    
  3. unpack boot.img (we need the initial ramdisk initrd.img)

    abootimg -x boot.img
    
  4. extract the ramdisk in a directory

    mkdir ramdisk
    zcat initrd.img | ( cd ramdisk/ && cpio -imd )
    

You’ll find adbd in this path ramdisk/sbin/adbd.

Setup disassembler (Ghidra)

  1. open Ghidra and select File, New Project..., Next, choose a directory for the project and give it a name

  2. click on CodeBrowser (it’s a green dragon) ghidra code browser

  3. File, Import File... and navigate in the previous ramdisk/ folder, under ramdisk/sbin/ and choose adbd

  4. go on and select all the possible analysis checkboxes, then Analyze

Find all the interesting functions

When the Tolino starts adbd is not activated. We can enable it, (following these steps), but it will run under uid=2000 and git=2000 (no root).

If you inspect the source code, you’ll see that there are some references to functions related to users/groups/capabilities:

  • setuid(2000)
  • setgid(2000)
  • prctl(PR_CAPBSET_DROP, ...)

We need to inhibit those calls.

Patch setuid and setgid

The first 2 functions are called as shown below (AID_SHELL is 2000, full code here):

    gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_GRAPHICS,
                       AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
                       AID_MOUNT, AID_NET_BW_STATS };
    if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
        exit(1);
    }
    /* then switch user and group to "shell" */
    if (setgid(AID_SHELL) != 0) {
        exit(1);
    }
    if (setuid(AID_SHELL) != 0) {
        exit(1);
    }
    D("Local port disabled\n");

Under Ghidra search for 0x7d0 (200010) using Search, Program Text. Choose Instruction Operands and Search All. You should see something like this ghidra search 0x7d0

The first 2 occurences are near each other. Here’s in the code browser ghidra searched lines

We can translate it as

So it would be great to replace all the highlighted part with nop (no operation instruction in assembler).

I created this simple script just to do it (nop.sh):

#!/bin/bash

set -e

if [[ $# -ne 2 ]]; then
    echo "Usage:"
    echo "  $(basename "$0") <file> <hex string>"
    exit 1
fi
input="$1"
hex="$(tr -d ' :' <<< "$2")"
output="$input.patched"

# build a nop string, the same size of "hex"
nops="$(printf "%*s" $(( ${#hex} / 4)) '' | sed 's: :00bf:g')"

# patch and rebuild binary
xxd -ps "$input" | tr -d '\n' | sed "s:$hex:$nops:g" | xxd -r -p > "$output"

# confirm substitution
[[ $(md5sum <"$input") != "$(md5sum <"$output")" ]] || { echo "$(basename "$0"): [WARNING] nothing done" >&2; exit 2; }

# check file size
[[ $(stat -c%s "$input") = $(stat -c%s "$output") ]] || { echo "$(basename "$0"): [ERROR] size mismatch" >&2; exit 3; }

echo "$output done"

Open the binary view under Windows, Bytes and select the block of code. In the Bytes window copy the highlighted bytes and do this

./nop.sh adbd "4f f4 fa 60 0e f0 86 ed 00 28 df d1 4f f4 fa 60 16 f0 47 f8 00 28 d9 d1"
cat adbd.patched > adbd
rm adbd.patched 

Patch prctl(PR_CAPBSET_DROP, ...)

Then we have to remove prctl(PR_CAPBSET_DROP, ...).

The code is right after ADB_EXTERNAL_STORAGE

Translated to

It’s sufficient to remove the call 0e f0 de ed

./nop.sh adbd '0e f0 de ed'
cat adbd.patched > adbd
rm adbd.patched

This adbd will run with root privileges, but we have to set up the boot.img accordingly. Copy the file as shown and follow these steps.

cp adbd /tmp/adbd.patched