Informatique

Libgourou v0.5

Sunday, 19 December 2021
|
Écrit par
Grégory Soutadé

Reminder : Libgourou is a free ADEPT protocol implementation (from Adobe) that helps download ACSM files from Linux.

It's Christmas soon. A time of heavy natural resources consumption, in all domains, because tradition and technological progress makes earth burning a bit more again and again. I suggest you to offer culture more than stupid and useless things.

And, what a good news, I have a gift for you ! Libgourou in version 0.5. It doesn't look like great as it's not the v1.0, but it's a big update. First versions were mainly bugfixes (plus PDF support) and I would like to tkank everyone who reported all that, more or less, stupid bugs. Reporting is not funny, but very useful for me and everybody.

So, my first intention when I created libgourou was not to support DRM removal but I saw a lot of people buying PDF (while I was focused on ePub). Using an eReader for reading PDF is not the best solution, big colored screens are so much better. After a long work in both libgourou and uPDFParser, you can now use the new adept_remove utility to remove DRM form ePub AND PDF ! Another good thing is the add of anonymous account support (no need to create or use your account from adobe.com). I recommend to use anonymous account only with a DRM removal software (like adept_remove), because the book will not be linked to your account and in case of failure, you'll have to buy/loan it once again.

I hope you'll enjoy this release. You can retrieve source code here or directly download pre compiled binaries (for Debian testing) here.

See you in 2022 !

Script : find shared library dependencies

Monday, 19 July 2021
|
Écrit par
Grégory Soutadé

While developping the reverse of Adobe's library libRMSDK.so, I needed a script to find all dependencies of this library and dependencies of dependencies. All of them must be copied into one folder in order to be packaged. So I wrote this script that parses recursively objdump's ouput. Here is the code :

#!/bin/bash

# Copyright Grégory Soutadé

# This is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with iwla.  If not, see <http://www.gnu.org/licenses/>.
#


#
# Find all dependant shared libraries of a target (using objdump) and copy them into a directory
#


# Options
TARGET=""
OUTPUT=""
ROOT_LIB_DIRECTORY=""
OBJDUMP=objdump
VERBOSE=0
EXIT_ON_ERROR=0
QUIET_NOT_FOUND=0
CLEAN_BEFORE_START=0
COPY_TARGET=0

function debug()
{
    if [ $VERBOSE -eq 1 ] ; then
    echo -e "$1"
    fi
}

function copy()
{
    target=$1
    symlink_name=$2

    if [ ! -e ${target} ] ; then
    debug "${target} not found"
    return
    fi

    debug "cp --no-dereference ${target} ${OUTPUT}"
    cp --no-dereference ${target} ${OUTPUT}

    if [ ! $? -eq 0 ] ; then
    [ ${EXIT_ON_ERROR} -eq 1 ] && exit 1
    return
    fi

    if [ ! -z "${symlink_name}" ] ; then
    echo ln -s `basename ${target}` ${OUTPUT}/${symlink_name}
    ln -s `basename ${target}` ${OUTPUT}/${symlink_name}
    fi

    # Symlink ? Copy target file
    if [ -L ${target} ] ; then
    copy `readlink -e ${target}`
    fi
}

nb_tabs=0
function find_lib()
{
    target="$1"

    if [ ! -e ${target} ] ; then
    debug "${target} not found"
    return
    fi

    nb_tabs=$((${nb_tabs}+1))
    local tabs=""
    for i in `seq 1 ${nb_tabs}`; do
    tabs="${tabs}  "
    done

    dependencies=`${OBJDUMP} -p ${target}|grep NEEDED|sed "s/ \+/ /g"|cut -d' ' -f3`
    for dependency in ${dependencies} ; do
    symlink_name=""
    echo -e "${tabs}${dependency}"
    debug "find ${ROOT_LIB_DIRECTORY} -name ${dependency}"
    file=`find ${ROOT_LIB_DIRECTORY} -name ${dependency}|head -n 1`
    if [ -z "$file" ] ; then
        # Try lib.so*
        file=`find ${ROOT_LIB_DIRECTORY} -name ${dependency}*|head -n 1`
        if [ -z "$file" ] ; then
        [ ${QUIET_NOT_FOUND} -eq 0 ] && echo "ERROR : ${dependency} not found in ${ROOT_LIB_DIRECTORY}"
        [ ${EXIT_ON_ERROR} -eq 1 ] && exit 1
        continue
        else
        symlink_name=${dependency}
        fi
    fi
    # Already copied
    [ -e "${OUTPUT}/${dependency}" ] && continue
    copy $file ${symlink_name}
    find_lib $file
    done

    nb_tabs=$((${nb_tabs}-1))
}

function usage()
{
    echo "Usage : ./find_libs [-O OBJDUMP] [-v] [-e] [-q] [-c] [-C] -t TARGET -o OUTPUT_DIR -l ROOT_LIBDIR"
    echo -e "\t-O OBJDUMP      objdump command"
    echo -e "\t-v              verbose"
    echo -e "\t-e              exit on error"
    echo -e "\t-q              quiet when dependency not found"
    echo -e "\t-c              clean target before start"
    echo -e "\t-C              Copy target in output directory"
    echo -e "\t-t TARGET       first executable or library to analyze"
    echo -e "\t-o OUTPUT_DIR   output directory where to place find libs"
    echo -e "\t-l ROOT_LIBDIR  root directory where to search dependancy libs"
}

while getopts "ht:o:l:O:veqcC" arg; do
    case $arg in
    t)
        TARGET=$OPTARG
        ;;
    o)
        OUTPUT=$OPTARG
        ;;
    l)
        ROOT_LIB_DIRECTORY=$OPTARG
        ;;
    O)
        OBJDUMP=$OPTARG
        ;;
    v)
        VERBOSE=1
        ;;
    e)
        EXIT_ON_ERROR=1
        ;;
    q)
        QUIET_NOT_FOUND=1
        ;;
    c)
        CLEAN_BEFORE_START=1
        ;;
    C)
        COPY_TARGET=1
        ;;
    h)
        usage
        ;;
    *)
        usage
        ;;
  esac
done

if [ -z "${TARGET}" -o -z "${OUTPUT}" -o -z "${ROOT_LIB_DIRECTORY}" ] ; then
    usage
    exit 0
fi

[ ${CLEAN_BEFORE_START} -eq 1 ] && rm -rf ${OUTPUT}

mkdir -p ${OUTPUT} || exit 1

echo ${TARGET}
[ ${COPY_TARGET} -eq 1 ] && copy ${TARGET}

find_lib ${TARGET}

A file version is available here

Libgourou : a Free ADEPT protocol implementation

Sunday, 04 July 2021
|
Écrit par
Grégory Soutadé

Two months ago I released a software that can use librmsdk.so from Adobe in order to retrieve ePub files (with Adobe DRM) from ACSM request files. It was the result of a long work of reverse engineering. The main problem with it is that it requires to run on an ARMv7 platform.

When I published it, I felt I can go further, but I was afraid of counter measures or cryptic algorithms that can be used by Adobe. Nevertheless, thanks to all knowledge acquired by my first reverse attempt I decided to try. In the end, Adobe choose to use standard algorithms with no obfuscation (maybe because it's delivered with a full SDK for clients). Plus, the target library wasn't compiled with code optimization \o/.

So I'm pleased to announce the first release of libgourou. It's a Free and Open Source implementation of ADEPT protocol. It supports :

  • Account signIn
  • Device activation
  • ePub download from ACSM request file

In addition to libgourou, two utils acsmdownloader and activate are provided in order to create a new device and download ePub from your favorite UNIX platform (like Linux x86/amd64 !) without any call to Adobe's code (no ADE, no WINE !).

Like RMSDK, it's based on a client/server model were the client has to implement some system specific functions (network, crypto...). It allows the library to be very portable (it's written in C++ 11).

The library by itself is licensed under LGPLv3 and the client (reference implementation) is under BSD license.

I can now tell it : we have a real alternative to ADE for Linux platforms !

Source are available on my forge

Basic usage

Basic usage of utils (you can use precompiled version). First, create a "device"

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
./utils/adept_activate -u "AdobeID USERNAME"

Then a ./.adept directory is created with all configuration file

To download an ePub/PDF :

./utils/acsmdownloader -f <ACSM_FILE>

ACSM to EPUB without WINE/ADE (Linux ARMv7 only)

Thursday, 13 May 2021
|
Écrit par
Grégory Soutadé

WARNING : You can also use libgourou and its utils to do the same thing from your platform (standard Linux PC), see this article

I finally did it ! After a long time looking for software that can download EPUB from ACSM file on Linux without need to install WINE software (and an old ADE version), I found the right breach to exploit.

As every Linux user knows, Adobe doesn't provide any support for ADE software on Linux, so we can't download EPUB files protected with Adobe ADEPT DRM because when you buy an ebook you get some ACSM file which is a request file for an ACS server (Adobe Content Server) that encrypts your ebook before returning it. My goal is not to have them decrypted, but just get the EPUB and put it on my eReader (an old Cybook Odyssey) to read it without rebooting on Windows. I can do it easily if I use the integrated bookshop, but :

  • I got a notification it will not work anymore (no support from Bookeen) even if it's still works...
  • I can't easily buy books from other shops

Time to time I look for resources on ADEPT DRM, Linux support, try to reverse protocol, exploit some binaries... I found my way thanks to Kobo firmware updates which include a precompiled version of librmsdk.so (Adobe Reader Mobile SDK) for Linux/ARMv7. I worked a lot with ARMv7 platforms, so it's not a problem for me to reverse it, plus my own server is ARMv7 compatible (iMX6) !

The shared library doesn't contains debug symbols (it's stripped). But it's a shared library, so it needs to expose all entry points in clear. Using readelf util we can find all of them and start to call it without Adobe headers. I first tried to exploit libnickel.so which is implemented by Kobo. It's an upper layer and I hoped it'll be easier to access high level functions. It was not and I decided to directly call librmsdk.so (which is lighter and has few dependencies).

RMSDK is written in C++ which is nice for application developers but a bit more tricky for reverse engineers, especially with virtual functions. Thanks for me the library is compiled without optimization options which make it more human readable.

It was a nice start but I lost a lot of time trying to find API by hand using readelf. So I developed SOAD (SO Advanced Dissector) a Python script which helps me to find automatically the full SO exported C++ API and generate (almost) ready to build C++ headers. It took me some time, but in the end helps me a lot. I was first doubtful this script can produce something interesting and I shouldn't ! I was impressed with the first simple version that produced very nice results ! So I decided to continue to work on it to handle more and more complex cases. One interesting thing was vtable discovery. The script statically parses vtables entries, but code compiled with fPIC option (which is the case here) has these entries filled with 0 which doesn't helps us. Fortunately, Andrey Ponomarenko created vtable-dumper which is a runtime vtable dumper (need to be executed on target platform). I used its output to find all zeroed vtable entries, but I also improved it to display class hierarchy !

I had to go in depth with C++ ABI and some C++ mechanism that are most of the time transparent for user (copy constructor, = operator, implicit cast, virtual tables and so on). Now the hard (and interesting) part is done, I'll make a little web app that will manage ACSM download and EPUB storage for an easy access from my eReader (avoid SSH, command line call and USB copy).

Sources are available in my forge here. I cannot embbed librmsdk.so as I don't have any Adobe license, but there is a script to retrieve it. The only license I got is GPLv3 !

gPass 1.1

Sunday, 14 March 2021
|
Écrit par
Grégory Soutadé

Logo gPass

Petit rappel : gPass est un gestionnaire de mot de passes en ligne. C'est une alternative libre à lastpass. Il permet d'héberger un serveur de mot de passe, qui stockera un mot de passe fort et unique pour chaque site web. Les mots de passes sont chiffrés par une "clé maître" que seul l'utilisateur connaît et sont remplacés à la volée dans le formulaire d'authentification.

Une nouvelle version (mineure) de gPass est en ligne. On y retrouve une correction de bug concernant le passage de l'icône du popup à actif (vert). L'icône s'active désormais seulement si un nom d'utilisateur est présent. Cette version apporte également évolutions intéressantes :

  • La possibilité de désactiver gPass pour tous les sites webs (via le menu ou les options). Ainsi, il n'y aura plus de hook installé dans les formulaires, mais l'on pourra toujours accéder à l'extension via le popup
  • Une case à cocher dans le popup pour envoyer le mot de passe dans le presse papier. C'était un comportement déjà implémenté avec le raccourcis "@_", mais cette fois il est plus explicite
  • Via les options, il est possible de choisir de toujours cocher la case du presse papier (comportement le plus sécurisé)

Les addons sont disponibles ici (firefox) et (Chrome). La partie serveur est à télécharger sur la page du projet.