Informatique
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

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

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 !

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.

Wednesday, 03 February 2021
|
Écrit par
Grégory Soutadé

My Cubox server offers a lot of services and one I specially appreciate when I'm not at home is that it contains all my music I can access through HTTP(S) interface. This is really fine for Linux clients where mplayer is installed, but it's not the case for Windows : VLC refuse to play my music (which requires login/password) and Windows player doesn't support m3u playlists, so I have to play each track individually.

I started to look for streamer software and the biggest open source one is Icecast which implement SHOUTcast standard. Like for all my online services, software must be lightweight (I don't have a lot of RAM) ! Plus I don't have any sound card plugged and don't want to spare cpu bandwidth with decoding/encoding files. This is a reason why basic HTTP(S) transfert is good : files are trasfered as is. Even if it's not clearly indicated by the documentation, icecast coupled with ezstream has all the qualities I need ! I was really suprised to find how it was easy to setup !

Here is a tutorial for basic setup (with current Debian stable distribution) :

If ezstream is available in your repository

sudo apt-get install icecast2 ezstream

If not (for an ARM target)

sudo apt-get install icecast2 libshout3 libtagc0 libxml2
wget http://ftp.de.debian.org/debian/pool/main/e/ezstream/ezstream_1.0.1-1_armhf.deb
sudo dpkg -i ezstream_1.0.1-1_armhf.deb

Now, we have to configure icecast. You must edit /etc/icecast2/icecast.xml. Update (at least) :

  • Admin name/address
  • Server address
  • Optional : server port

Some of these values has already been configured by installer. There is a lot of avaible options not needed for basic setting. After that, restart icecast :

sudo service icecast restart

Now, add a NAT rule to redirect external port (8000 by default) to your server. Then, copy an example of ezstream configuration :

cp /usr/share/doc/ezstream/examples/ezstream-minimal.xml .

and edit it (be careful, here end tag are crafted by my editor) :

<ezstream>

  <servers>
    <server>
      <hostname>127.0.0.1</hostname>
      <port>8000</port>
      <password>XXX</password>
    </server>
  </servers>

  <streams>
    <stream>
      <mountpoint>/stream.mp3</mountpoint>
      <format>MP3</format>
    </stream>
  </streams>

  <intakes>
    <intake>
      <filename>/media/MyTrack.mp3</filename>
      <stream_once>1</stream_once>
    </intake>
  </intakes>

</ezstream>

I choose to play track only once. If not set, it will be played indefinitely. Now, we can run ezstream :

ezstream -c ezstream-minimal.xml

On Windows, we can use VLC to read this stream by opening :

http://icecast.soutade.fr:8000/stream.mp3

This is a basic setup, but we can do a lot of more complex things by autogenerating config file and auto start ezsteram from a web frontend for example.