Handbook
Handbook
6. Rooting 19
6.1. Team Win Project (TWRP) 19
8. Tools 31
8.1. ZSH Con guration File 31
8.2. Development 31
9. Apps 40
9.1. Package Structure 40
10.2. Intent 47
10.4. ContentProvider 49
11. SMALI 53
11.1. Types 53
12.3. BurpSuite 72
15. Frida 86
15.1. Installation 86
15.7. Timing 97
15.8. NDK 99
1.1. VM Installation
Quick summary on how to install the „Androidx86 VM“. Please make sure that you do not
install this VM within another VM. This is not working properly. For the emulator it is ok.
1.1.1. VM Settings
My recommendations regarding to the minimum of the VM settings:
• 2 CPUs
• 4K Ram
• 12 GB Storage
In the extended view, we have an “Auto_Installation” menu, please select this one and
click yes. That’s it :)
After starting the VirtualMachine, the Android screen might not appear. This is a common
VMPlayer bug. We have to provide a certain parameter within the init con guration le.
Then we wait for the system to nish the booting process and press “Enter” a few times
until we have a shell. Now we need to remount the “mnt” partition because we want to
change the “menu.lst” con guration le.
The „menu.lst“ le describes the single entries which can be selected in the
boot menu. These are just single commands with different parameters.
# Terminal in the VM
$ mount -o remount,rw /mnt
$ vi /mnt/grub/menu.lst
Add the value “nomodeset xforcevesa” after the rst parameter as shown below.
Now press ESC and type “:wq!” to save these changes.
After the le has been successfully written and closed we can restart the system via the
VM settings.
When we now select the rst entry: “Androidx86 9.0-r2” the system is booting up with a
graphical interface.
After downloading and installing it, the following window should appear. In case if you
have already created a new project, you can close the project to get this window back.
Then we have to click “Create device” on the top left and we need to make sure, that we
are selecting an image without the Google - Play Store being enabled.
By opening a new terminal or using the command “source ~/.zshrc”, we can now launch
our emulator easily from a terminal.
This can be used to for additional con gurations like setting the GPS location or
redirecting the network tra c. The required token can be found in
~/.emulator_console_auth_token.
Roman Stuehler (Udemy) Page 10 / 104
fi
fi
ffi
fi
3. Android VM - ADB Connection
This part describes, how to establish a connection between the Linux system and the
Android VM / Emulator.
NAT
This interface provides Internet Access via the host system, but your VM is behind another
Network. If you select NAT for both machines, they can reach each other and also use the
internet via your host system.
Host Only
This is some sort of a private network with your host machine. You cannot reach the internet
via your host but if both machines are in the „host only“ network segment, they can reach
each other. I prefer this one because they also cannot send data out of my network :)
Bridged
This network mode will bind your VM into your host network segment. This means, it will be
reachable from all components in your home/company network. I do not recommend this
option! If you want to have an internet connection, check NAT - otherwise Host Only.
$ ip addr
In the android VM you can use the „Terminal Emulator“ app which is shown on the right.
# IP address of the VM
$ adb connect 192.168.227.2
* daemon not running; starting now at tcp:5037
* daemon started successfully
connected to 192.168.227.2:5555
$ adb shell
x86:/ $
The „x86“ prompt shows that we are operating on the Androidx86 Virtual Machine.
There you will also nd the USB 3.0 settings, which I highly recommend to enable it.
Having this set to USB 2.0 can cause problems with the “USB to ethernet adapter” which
will be used later in this course.
Afterwards a new feature „Developer Options“ is available. This can be found in the
system settings (enlarge advanced) or directly within the Settings menu. Depends on the
smartphone manufacture and the version.
There is a little „Easter Egg“ built into the Android system. If you click
multiple times on version, a short animation regarding to your OS version
appears.
$ adb shell
$ adb shell
You should see a new linux prompt regarding to your device. On a nexus 5 it will be
„hammerhead“ and on a pixel 2 it is „walleye“.
You can get the IP address by clicking on the WiFi symbol and extending the advanced
tab. On the bottom of the page you will see the IP address of your smartphone.
Make sure your android device is connected to the virtual machine or host computer that
executes the „adb shell“ command.
Also make sure that USB debugging is enabled in the developer options.
$ adb kill-server
$ adb shell
This will automatically reset the adb service and reconnect to it.
A pop up appears that let’s you chose to switch into data transfer mode. After this restart
the ADB server and we should be good to go.
Anyway, we can try to reset this by revoking all existing ADB permissions on the
smartphone. To do so we go into :
• Settings (sometimes extend advanced tab)
• Developer options
• Revoke USB debugging authorizations
• Press „OK“
Sometimes it is also required to use a certain vendor speci c adb version. This was the
case on an embedded device where the vendor has it’s own fastboot and adb binaries.
Try to contact the vendor and kindly ask if this is the case and if they can provide you the
binaries to connect to your device.
This setup is lagging a bit in performance, due to the nested virtualisation. However it
simply works and you can also use this during the course.
$ adb kill-server
$ adb shell
Source: https://developer.android.com/studio/run/emulator-networking
Example given, the “Insecure Bank” application is using a web server which is running in
our virtual machine. If the smartphone wants to connect to it, it can access the 10.0.2.2
address, which is bound to the local host of the system that is starting the emulator. See
table above.
Unfortunately it is not possible to provide and easy solution that works for every device.
Don’t worry, rooting is not required for this course but I still want to share my experiences
with you. Especially if you have never done it. Getting into it might be a bit confusing.
If you are not able to root your smartphone, this absolutely ne! We can still
perform all the nice hacks without having root access on a real device! This
is the reason why we have the emulator in this course! :) Getting root
access on an android VM / emulator is pretty easy :).
6.1.1. Preparation
To check if TWRP is available for your device, please have a look at the website:
https://twrp.me/Devices/. In my case I am using a „Google Pixel 2“ in Europe. The
corresponding link will be: https://eu.dl.twrp.me/walleye/.
To perform the ashing of a custom recovery image we have to unlock the boot loader
rst. This can be straight forward or even not possible for your device. It depends on the
manufacture.
Take care! By unlocking the bootloader, all your data will be wiped
Finally, there are also devices where unlocking the boot loader is not possible. In this
case just stick to the provided Virtual Machine (VM). This is ne :)
$ sudo su
# gedit ~/.bashrc
This will extend the path variable with the binaries included in platform-tools directory. If
you are running a command from your shell, the system looks up all binaries included in
these directories.
# source ~/.bashrc
After this has been done, put the device into the bootloader mode again and check the
connection with fastboot.
Now we nally start ashing / booting from our custom recovery image which has been
downloaded from the TWRP site.
The TeamWinProject image should also appear on your smartphone now. If we have
provided a „lock“ mechanism (pin,pattern etc) we have to unlock the smartphone now,
because the data is being encrypted with this pattern/code.
Ok, we removed the PIN and did reboot the device into bootloader mode and also did
start the TWRP image via „fastboot“.
You should also consider that rooting the device will lower the security,
! so I highly recommend you not to root your private phone and get some
test device to play with.
To download MAGISK please take a look at the o cial repository here: https://
github.com/topjohnwu/Magisk/releases. I use the „Magisk-v21.4.zip“ archive and upload
it via adb. Remember we are still in TWRP and here we can use adb to interact with the
le system.
Now just press the install button, select your zip archive and „Swipe to con rm the ash“.
Source: https://topjohnwu.github.io/Magisk/install.html
After installing the application, we have to select the boot.img which has been pushed
onto the sdcard before.
Source: https://topjohnwu.github.io/Magisk/install.html
MAGISK is doing the rest. It will inject itself into the boot.img and create a new le out of
it (magisk_patched-25100_F7lFO.img).
The other way to restart your smartphone in bootloader mode depends on the
manufacture. On a Google Pixel device we have to power it o and hold the “volume
down button / up (depends on manufacture)” + “power button” simultaneously for about
10 seconds. Afterwards we can release them and should end up in the bootloader menu.
You can nd out if you have a vbmeta partition by simply unpacking your stock image as
shown below:
If this is the case you have the fastboot command with the “vbmeta” parameter.
After downloading the factory Image, enter the bootloader (volume up or down + power
for a certain amount of time) and check if the connection is successful with fastboot.
Please make sure your bootloader is still unlocked! If this is not the
! case, run „fastboot ashing unlock“.
Now unzip the factory image le on your laptop and start the ashing process by running
the „ ash-all.sh“ script. Once again, all data will be wiped o but if you have no access
anymore, this might be the easiest option to get at least your device back.
Now we can lock the bootloader again. We do not have to setup the whole device again
just to enable ADB.
I recommend locking the bootloader back again. Otherwise if you lose your device, an
attacker will be able to recover you data by ashing images like TWRP.
After locking it you can enjoy your freshly new installed operating system. I also
recommend ashing the stock rmware if you buy some used smartphones. Then you
can be sure that no apps / data are left on the device.
In case if you have multiple devices connected we need to use adb -s “serial number” of
the device to select our targeted device.
Command Description
adb reboot bootloader Reboot the device into the boot loader mode
adb push <local> <remote> Upload a le from the laptop to the phone
adb pull <remote> <local> Download a le from the phone to the laptop
adb shell screen record „/Path/ Capture a video of the device screen
CaptureRecord.mp4“
adb shell pm path <package name> Shows the path to the APK which can be
downloaded (see adb pull) even without root
permissions.
adb input touch <x> <y> Perform a touch event at the given coordinates
This list is based on the useful commands provided here: ADB Commands.
This is useful specially regarding to the emulator because the emulator is running in a
“restricted” environment. This means, not all services are atomically being exposed like
this would be the case on a real smartphone. By forwarding these service we can further
investigate them.
The ES File Explorer is a very popular application which had a vulnerability in the http
server on port 59777. This web server accepted commands that gives an attacker the
ability to fetch information about the device, apps being installed and also the ability to
download les from the smartphone. Further information can be found here: https://
github.com/fs0c131y/ESFileExplorerOpenPortVuln.
This port might be blocked on the emulator, but not on a real smartphone. By having “adb
forward”, we can discover these ndings also on the emulator.
$ cat ~/.zshrc
8.2. Development
The app development will be done in android studio. To view single les, I use the Visual
Studio Code Editor.
8.3.1. JADX
JADX is being used to decompile an APK. It tries to generate the corresponding JAVA
code. JADX is open source and can be installed as follow:
We can now nd the single decompiled JAVA classes in the outdir directory. Here are also
some additional parameters, that might be helpful.
The „-r“ parameter does not decode resources, which is handy because they often cause
errors during the decompilation and are not really necessary for the reversing / patching
process. The same for the „--escape-unicode“ parameter.
Parameter „-j x“
To speed up things a little bit we can use the „-j 4“ to increase the number of thread. This
will be a lot faster because it is using more cores. The amount of possible threads is also
limited based on your CPU(s).
Example Calculation:
To get an overview about the CPU / Cores / Threads of your system we can use “lscpu”.
$ lscpu
Here is the output and we are interested in the following three parameters.
Therefore the max thread count is 4 and max core count is 4. We can provide „-j 4“ as
max value.
Parameter „--show-bad-code“
This parameter forces jadx to include the „bad code“ in the decompiled code. Here is an
example. On the left, we have the Spacepeng.apk with the „—show-bad-
code“ parameter and on the right without.
You can play around with this parameter if you encounter that you are missing something
which might have not been decompiled correctly. I do not use this very often but it is nice
to have. This feature is now also being included in the GUI too.
Parameter „ --log-level“
This parameter is very useful and in my opinion, it is not used very often.
We immediately see, which functions could not be correctly decompiled and also by
checking them with „jadx-gui“ we can see that we are missing some information here.
This is important for the reversing process because we might miss important information.
In this case it is not a big deal because there are only 2 errors and they are in functions
that are not really required. By checking this, we are now pretty sure that we do not miss
any important information.
Deobfuscation - Techniques
It is also doing a good job but to be honest, the code will still be obfuscated. I mean you
will not get the original class names back. Instead of a.b.c.a.aa() we might get something
like p0012.p0192().
You can also check out how the deobfuscation is working. It is an awesome open source
project: https://github.com/skylot/jadx/blob/master/jadx-core/src/main/java/jadx/
core/deobf/Deobfuscator.java.
If you have to deal with deobfuscation that cannot be handled by the tools,
I recommend creating a ow- and call graph of the application. Then we
can track down which classes are doing what and start renaming them.
This approach takes of course a lot of time.
Parameter „—cfg“
This parameter creates a ow chart (.dot - les) of all functions. This is super handy if you
have to trace back a huge function that has been obfuscated. I normally would use the
androguard feature for this process but I JADX will be a lot faster.
Now we have our decompiled code and the corresponding dot les. We need to write a
little python script to convert a .dot le into a png:
On such a small function it might be not that helpful, but when you have to deal with
some obfuscation methods, this might be very handy!
8.3.2. JADX-GUI
JADX-GUI is being used to decompile and display an APK in a nice GUI window.
JADX and JADX-GUI are based on the same GitHub project. The installation process is
the same:
8.3.3. DEX2jar
DEX2jar is being used to reconstruct the JAVA code out of the classes.dex les of the
android APK.
/home/android/tools/jadx/build/jadx/bin:/home/android/tools/dex2jar:/
home/android/.local/bin/:/home/android/.local/bin:/usr/local/sbin:/
usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/
games:/snap/bin
If you have a windows system, you want to use „d2j-dex2jar.bat“. On a Linux system we
are using d2j-dex2jar.sh.
$ unzip de.fgerbig.spacepeng_1581.apk
$ d2j-dex2jar.sh classes.dex
dex2jar classes.dex -> ./classes-dex2jar.jar
$ jd-gui classes-dex2jar.jar
Done :)
8.3.4. JD-GUI
JD-GUI is being used to display the classes inside a JAR le in a nice GUI. There are no
special parameters which I use during my daily work with JD-GUI.
Update! Please make sure you are using the version 2.5.0 or newer of
the apktool. The android build system has recently changed (August
2020). Apps that have been build with the new android-studio cannot
be decompiled anymore with older versions of the apktool.
Basic Parameters
These are the common parameters of my daily work with the APKTOOL.
d Decompile an APK
b Build an APK based on a decompiled version
-r,--no-res Do not decode resources.
--force-manifest Decode the APK's compiled manifest, even if
decoding of resources is set to "false".
-d,--debug Sets android:debuggable to "true" in the APK's
compiled manifest
$ apktool d <APK>
$ apktool b app/
Parameter „-r“
As already mentioned in JADX, this parameter does not decompile resource les. These
les often cause problems and might hinder the APKTOOL on decompiling the app.
Beside the AndroidManifest.xml, the resource les are not really required and can be
skipped.
Parameter „--force-manifest“
It is always helpful to have the AndroidManifest.xml decompiled. But if we choose the „-r“
parameter, this does not happen because this is a resource le. To avoid decompiling the
unnecessary les and keeping the AndroidManifest.xml we can provide this parameter.
After absolving this course, you will be able to solve all of his challenges.
I will not spoiler any solution but I will teach you all the techniques that are required to
solve them.
The tools that are required for this can be installed with the following commands:
Command Description
We should always check the assets directory. There might be certi cates or additional
data that are used in the app which cannot be seen in the decompiled java code. Of
course if this directory is not available, then we do not have to check it :)
If you encounter some problems you can try to decompile the application with the „-
r“ parameter.
$ apktool d -r game_test.apk
The apktool will not try to decompile the resources of the app. If you still get errors try out
other decompilers like jadx or androguard.
$ apktool b game_test/
Even if we have decompiled the app with the „-r“ parameter, it is not required for building
it back again.
Also take care! We need to provide the directory path and not the application APK le
here. If you encounter errors have a look if this is the case.
Here we are creating a new keystore in our home directory with the name „android-app-
hack.keystore“.
The alias_name is not relevant. We can provide any name we want. This name will be later
used to sign the app. It identi es the correct certi cate in the keystore. The keystore can
hold more than one certi cate.
The le „out le.apk“ is now aligned on 4-byte boundaries and can now be signed.
This will create a new application named “new-debug.apk” which has been signed and
can be installed now.
Update! The new build system in android - introduced in august 2020 - will
cause headache if you try to rebuild newer application. I have found a way
to handle this and I will show you here :)
9.2.7. Troubleshooting
This guide provides some common erros I have encountered during this process. If you
encounter a new problem, please send me a message. I try my best to support you on
that.
We have decompiled the app and are now trying to build it back when we encounter this
error:
We also tried decompiling the app with the „-r“ parameter, which seems to work until we
want to install the app. Then we get this error:
This looks like an application that has been compiled with the new build system (aapt2).
The signing has also changed and zipalign is necessary. If you follow the steps above,
you shouldn’t encounter this problem. It was related to the old signing approach.
In case of other errors try the following approach. The second command often solves the
problem. It is like cleaning the project.
In case of this error message, just try to empty the framework dir.
/home/android/AndroidStudioProjects/ndkfrida/app/build/outputs/apk/debug/app-debug/dist $ adb
install -r new-debug.apk
adb: failed to install new-debug.apk: Failure [INSTALL_FAILED_TEST_ONLY: installPackageLI]
This means the application cannot be “regularly” installed. All we have to do is adding the
“-t” parameter as shown below:
This happens when we have added - modi ed some native libraries. We can solve this
issue by adding the following parameter into the AndroidManifest.xml:
android:extractNativeLibs=„true“.
This might happened quite often and simply means, we have forgotten to sign the app or
we are trying to install the none signed version of the app :)
If you have signed the application then please make sure that you are using the correct
version of the app. The output le of the signing command. In this case new-debug.apk is
the signed app.
10.1. Activities
Have a look into the AndroidManifest.xml and check if one of them is being exported.
The “android:name” tag shows the Java-Class of the given activity. This is required for the
exploitation part.
10.1.2.Exploitation
Exported activities can be started via the ADB shell or by writing a custom application
which is triggering the intent. Starting via the ADB shell is enough for a PoC:
$ am start -n com.mwr.example.sieve/.FileSelectActivity
$ am start -n com.mwr.example.sieve/.PWList
We have to check the code if we can exploit this behaviour. Please search for the
following term:
In the following example the “ChangePin” activity is being started with a username
provided by the intent.
If the “ChangePin” activity is being exported, we can trigger the intent with a di erent
username. It depends on the user code what happens next.
10.2.2.Exploitation
To start the activity with a di erent username we can use the activity manager and
provide the intent information as follow:
$ adb shell
$ am start-activity -a com.apphacking.changePin —es “username” “user”
// *Note: Category could be also added with -c android.intent.category.Default
*Note: MobSF is a really nice framework and we can use this for certain tasks. But keep in
mind that it might miss something. Therefore I would like to show, how you can do all of
this by yourself to make sure, we are fetching everything.
10.3.2.Exploitation
Here we have to check the onReceive method in the Java class. This is the only code part
we can trigger by sending our event.
10.4. ContentProvider
Within a ContentProvider we can look for SQL injections and Path-Traversal attacks.
We can write our own app and use the custom permission. The user might be informed
with a popup. It depends on certain things but nevertheless, a missclick can happen and
this would provide us full access to the database.
We need to check if the query method (if available) is ltering our input. If this is not the
case, we are able to inject our own code into the SQL query.
Now we need to identify the tables in the Java code. We can look for the keyword
“content://“.
- com.mwr.example.sieve.DBContentProvider/Keys
- com.mwr.example.sieve.DBContentProvider/Passwords
The actual table names might be different, we have to track this in the
! code because within the SQL injection attack, we have to use the correct
table names and not the authority names.
The “- -“ (comment) will negate the rest of the original query. Therefore we are just
selecting all entries from Key.
Key is the actual table_name. The authority name is being referenced with “Keys”!
Confusing Yes! :D
// Selecting
$ content query --uri content://<authority>/users
// Inserting
$ content insert --uri content://<authority>/users --bind name:s:admin
// Update - Modify
$ content update --uri content://<authority>/users --bind name:s:hacker --where
“name=‘admin’”
// Delete
$ content delete --uri content://<authority>/users --where “name=‘admin’”
If the input “uri” is not being ltered, then we can apply les like “../../../../../etc/hosts”.
This gives us the possiblity to perform an arbitrary read on possible sensitive information
which are being stored in the app directory. This directory is sandboxed and normal users
do not have access. But we are now in the context of the current application which
means, we have the ability to access them.
Path-Traversal - Attack:
11.1. Types
Here is a list of types in SMALI. It takes some time but you will get familiar with these
types. I have marked the most import one which we will encounter very often.
Syntax Meaning
V Void
Boolean:
Z Boolean
0x0 = false (0)
B byte 0x1 = true (>0 is true)
S short
C char
F oat
I int
J long
D double
We verify or clarify this behaviour with the following example. We will decompile this code
with the APKTOOL and walk through the output.
Parameters - Registers:
We can see that the parameters do
start with „p1“ and end with „p9“. We
remember, „p0“ is used as „this“.
Parameters - Type:
Within the manyArgs( ) function we
see those values:
• IICICCICI (9 parameters!)
Check the list above to determine the
types of the single parameters :
• B => Byte
• I => Integer
• C => Char
• V => Void
• […]
Local - Registers
The local registers start with v0 and end with v6. But not all of them are regarding to the
variables above. We will see later :).
Local - Types
The type of the local registers all start with „L“? Take a look above at the data types!
These are the whole class names. So, „Ljava/lang/String“ simply means a String. These
are all Strings.
Locals - SMALI
You have to take a close look on line 132 above. Here we can see the word „locals“ which
refers to our local registers. But we only have v0 - v4? Yes, as local variables, but the
decompiler did need 3 additional registers to handle the whole code.
JADX-GUI is using „.registers 18“ and the APKTOOL uses „.locals 8“. These are some
sort of naming conventions. It does not make any di erence.
For me, I like the APKTOOL approach much more and I will stick to this for the rest of the
course. I just want to let you know that these outputs can di er based on the tools you
use. The SMALI code should stay the same ;)
11.3.1.Variables - Assigning
This Table is based on this amazing website: http://pallergabor.uw.hu/androidblog/
dalvik_opcodes.html. I only changed the examples to make it easier to read. But you
should de nitely bookmark this page!
new-array vx,vy,type_id Generates a new array of type_id byte[] bArr = {0, 1, 2, 3, 4};
type and vy element size and
puts the reference to the array const/4 v0, 0x5 # Array size
into vx. new-array v0, v0, [B
const vx, lit32 Puts the integer constant into vx int level = 10000; // 32 Bit
iget vx, vy, eld_id Reads an instance eld into vx. return this.highScore;
The instance is referenced by vy.
iget v0, p0, Lde/fgerbig/spacepeng/
services/Pro le;->highScore:I
return v0
iput vx,vy, eld_id Puts vx into an instance eld. The this.lastPlayedLevel = lastPlayedLevel2;
instance is referenced by vy.
iput p1, p0, Lde/fgerbig/spacepeng/
services/Pro le;->lastPlayedLevel:I
If you know that „iput” stores a value into a register, then you will also now that „iput-
boolean“ does the same, but only for a boolean value.
You will have all kind of variations but these are the most basic assignments.
It is also important to understand the reading direction. What is the source and what is
the destination register. So let’s take a look at the following example:
add-int vx,vy,vz Calculates vy+vz and puts the result into vx. score = score + 1
The program ow is based on decisions / checks which de ne if we can a ord this item
or if you lose the game because you do not have any hit points left.
Let’s say we have the following code. This code checks if we have the debugger
attached.
We want to make sure that we can debug this application. The function checkDebugger()
needs to return„FALSE“.
There are a lot of possibilities to bypass this check but one working solution will be
enough :) We still discuss some other solutions too.
Solutions:
1. We can change the return value of the checkDebugger() method, to always return
false (0x0).
2. We can negate line 20. This means instead of „if(check)“ we can rewrite it to „if(!
check)“. The downside of this method is, that the application is returning the
opposite, which means, if we have no debugger attached - it will show that we
have one attached. Not ideal here but often very useful. E.g rooting detections.
3. Last solution for now is rerouting the if statement. This means we are jumping over
the part we do not want to have executed.
But… We do not have a certain instruction like „move v0, v1“ or „const v0, 0x1“? How
can we easily modify this?
Solution - Method 1:
We can simply add our own code before the return v0 command. We are writing the result
into v0 but we immediately overwrite it afterwards like shown below.
The return value is a boolean value (Z). This function is now - no matter what - returning
false all the time.
This time we are rerouting the execution ow via the IF statement. It might sound a little
bit complex but we can handle this :).
The application is printing out “No debugger attached”. This means we add the
instruction “goto :cond_0” directly after the if statement in line 53.
.line 21
goto :cond_0
sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
[...]
Now we are redirecting the program ow always into the “correct” blocks :).
There are many methods and ways you can patch di erent things. It is often also not
possible to provide „one - best“ solution. Just remember that you have the possibilities
to :
• change the conditions in the if statements ( TRUE / FALSE)
• that you can create your own registers / overwrite existing values
• that you can add/modify existing / own code
• delete existing code :)
• change the program ow
Having this in mind, you will nd your way through these applications! :)
Sget-object
They look pretty identical but they are di erent as shown below:
• public void increaseScore(int score)
• DoubleShot doubleShot = new DoubleShot();
• public static oat random( oat x, oat y)
• public abstract boolean hasNext ()
The $ signs means inherited classes or enums. Let’s have a look at the following example:
We include
„java.io.Serializable“ and created a
new object.
Now let’s do a „ls“ command in the decompiled code directory and see what we have.
App-debug/com/apphacking/inheritclasses$ ls
BuildConfig.smali
MainActivity$1.smali'
MainActivity$HighScore.smali'
MainActivity.smali
…
Now let’s look into something, that looks a bit more complex. But based on what we have
learned so far, we are able to understand the whole syntax. We just need to do this step
by step and have a little patience. To get good in SMALI we simply have to practice. :)
Example 1:
This might look overwhelming in the beginning but just go through this - step by step:
1. Invoke-static:
invoke-static {parameters}, methodtocall.
• Ok this is a static method - we do not need to create an object rst.
• We also have two parameters, v1 and v2.
• The method that gets called is createCoin.
• This method is de ned in the EntityFactory class.
• The parameters are „com/artemis/world“ and „de/fgerbig…./coin$type“
• We know that „type“ is an inherit class of the class coin.
• The return value is a com/artemis/Entity object.
The same syntax but marked and split into single parts as described before.
The function code might look like this:
But we got a feeling what was going on there and even if we do not fully understand it in
the SMALI code, we do it now.
Beside this, we were correct on the other assumptions which is pretty amazing?! I mean
we did reconstruct the decompiled code, based on the SMALI syntax!
If this has not been the case for you, don’t worry. This topic is very complex. You need to
be patient. Do it step by step and keep practicing. I have learned it and I am not a genius.
You can learn this too!
This is a short collection about some very useful SMALI snippets. These snippets helps
you to solve your tasks like CTF challenges or leaking sensitive information out of
cryptographic functions.
I use this snippet quite often because it is an easy way to simply print out certain
variables or return vales in logcat.
System.out.println(password);
.line 15
We simply put our object / value we want to print into v0 and use sget-object and invoke-
virtual afterwards.
Now just simply run the app and check the logical output for our value to be printed:
$ adb logcat
The corresponding SMALI code looks like this. All we need to do is inserting this piece of
code into the existing SMALI code of the application. We might have to adjust the register
v5 which is referring to the byte array, but that’s it :)
12.1.1.Router (1)
We start with the router. It does not really matter which router you pick. The only thing you
have to do is to disable DHCP. You will nd this in the menu of router. It should also have
WiFi :).
Steps:
1. Plugin your ethernet cable and wait until you receive an IP
2. Log into the interface of your router.
3. Search for network settings „Enable DHCP“ and uncheck this box. We need to disable
DHCP!
4. Restart your device / router.
Here we only see the interfaces „ens33“ and „lo“ which is loopback. So the USB-Ethernet
interface is missing. After adding the USB device to our machine, it should look like this.
If you get an error message, that the connection is not possible then
please check if you have USB 3.0 enabled. If the adapter only supports
USB 3.0 you have to plug it into a USB 3.0 port which is not always that
easy to identify on older laptops :) For more information please have a
look at the video again.
Ok after changing USB-2 to USB-3 and reconnecting the adapter it is working. We have a
new interface named „enx302303bca34c“.
$ ifconfig -a
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.16.240.2 netmask 255.255.255.0 broadcast
172.16.240.255
$ nm-connection-editor
Sometimes things are buggy but after following these steps here, it did work in 99.99% of
the time :)
Check if your router has DHCP disabled. If you connect to it with your host and still
receiving an IP then DHCP is still active and we are getting con icts.
Check if your USB-To-Ethernet-Adapter is connected to a USB 3.0 port.
Check if your USB-To-Ethernet-Adapter is generally working :)
Check if your VM has USB 3.0 enabled
If you are using a docking-station, verify that you have forwarded the correct adapter.
You can check this by having a look at the MAC address of the device.
Unplug potential other devices that are providing a DHCP server (If you have more
then one router connected / docking-stations)
If you have it plugged in a USB hub, try a direct to connect to a direct port on your
laptop.
Check if NAT / HostOnly is enabled. Sometimes it does not provide the network
con guration (shared) if there is none network available.
If all of these fail, please send me a message! :) We will make it work, I am pretty sure!
I showed this setup also to my colleagues and they are using it for several years now.
We had one of the problems mentioned above, but soon or later it did work :)
12.3. BurpSuite
BurpSuite is a proxy written in Java with a lot of capabilities. There are already trainings
out there, that only handle BurpSuite and they are about 2 days long. We will only focus
on the parts that are necessary for this course.
What have we achieved so far? Let’s take a look back on this picture.
10.42.0.1:8080
The smartphone is connected to the router via WiFi. The router is providing DHCP and
DNS which will be handled by our nm-connection editor.
The ip table rules do forward all the tra c on TCP port 80 (HTTP) and TCP port 443
(HTTPS) to our USB-To-Ethernet interface (10.42.0.1:8080). This is the blue line.
If we open now a website, we will not see anything because there is nothing listening on
10.42.0.1:8080. Now we need to start our proxy (burp) on this interface / port and forward
the tra c to the original address. This is the green line
12.3.1.Starting up BurpSuite
The Burp community edition is free to use. It does not contain features like a scanner but
we will not need this and it is incredible that an awesome product with all the necessary
features is available for free. You can download an installer or use the standalone jar le
here: https://portswigger.net/burp/communitydownload.
After downloading the installer you might get this warning here:
This is ne because it has the „.sh“ extension. Click keep but before you run it, take a
look with vim. Yes a lot of stu there but this recommendation has to be given here :)
$ chmod +x burpsuite_community_linux_v2020_12_1.sh
$ ./burpsuite_community_linux_v2020_12_1.sh
$ cd (press enter)
$ ./BurpSuiteCommunity/BurpSuiteCommunity
Take a look at the Terms and Conditions and if you agree you can click „I Accept“. I
normally opt out of anonymous feedback but feel free if you want to support this to make
it a even better product :)
This is the only downside in the community edition, that our requests do not get
permanently stored in a project le but we can save them later into an extra le if we want
to keep them. So also no big deal.
Ok now we are in the main window of the BurpSuite. As already mentioned, we could talk
two days about all the features and possibilities in here but we will focus on the important
parts.
We will use this pager later to fetch the certi cate but for now, we need to run this on the
„router“ interface 10.42.0.1. To do so go into the options menu from above and click edit.
Under binding we can set the
interface and port we want to
listen to. We can be lazy and
select „All interfaces“. This will
make the BurpSuite running on
all available interfaces.
We can also say „Speci c
address“ and select our interface
with the 10.42.0.1 ip address.
This is a better solution in my
opinion because this is the only
interface we want to run the
burp. But it does not make a
di erence.
If you click on the next tab, „Request handling“ we can activate the invisible mode.
This mode is important because it will let the BurpSuite act as a forward proxy, that is not
„seen“ by the device. Therefore „invisible“.
By activating it, the burp will automatically reroute the generated tra c to the original
destination. We can verify that the invisible mode is active, if we get the checkbox as
shown below.
We can open our browser in the smartphone and we will be able to see HTTP tra c in the
BurpSuite proxy.
Why? Because the BurpSuite is providing a certi cate, that is not known to the
smartphone and this is also the case if people will try to man-in-the-middle you. As a
security feature, the browser is warning us! To avoid accepting every single request, we
can install the BurpSuite certi cate as a truster cert on the device.
The name of the le does not matter but the ending needs to be „.crt“. I save it as
„burp.crt“. Afterwards we upload this le via adb to the smartphone.
Please install the burp certi cate into the user storage of the
smartphone. Sometimes people give advices to put it into the system
storage but this does not work on modern smartphones anymore.
! Recon guring this is a lot of pain and required root privileges. See here:
https://chromium.googlesource.com/chromium/src/+/master/net/
docs/certi cate-transparency.md
The best setup would be using a real smartphone with a real router. Then we do not have
to rely on certain emulation / software aspects.
13.1. Setup
Within the con guration menu on the left, please click on the three dots and switch to
Settings.
After hitting apply the smartphone is forwarding the communication to our proxy. The ip
address needs to be the interface of our virtual machine. In my case if I am using the NAT
adapter which means, the ip address 172.16.240.129 has been assigned to my Linux
system (VM).
We already talked about installing our burp certi cate onto the smartphone and select it
as trustworthy. Well, certi cate pinning is exactly the same but it only validates its own
certi cate against the corresponding endpoint. Therefore our global „burp.crt“ will not
work here.
Now we try to get into a man-in-the-middle position. Our certi cate has been successfully
installed on the smartphone. We run the app, but we get an error message. Why?
Because the application is only trusting the certi cate that has been hardcoded into ithe
app. To bypass it we need to patch the application and replace it with the values from our
burp certi cate.
We will focus on the two most common libraries „okhttp“ and „retro t“. But if you can
patch them, you will be also able to patch all the other existing libraries. The schema is
always the same.
The easiest way to patch a library is to write the code by yourself. If you know how to
write a certi cate pinning app, you will also know where you need to patch it.
But let’s assume we are not familiar with writing our own app, we can still search for the
code part: https://square.github.io/okhttp/4.x/okhttp/okhttp3/-certi cate-pinner/.
This is awesome. We already got the code snippet that also other developers will use to
perform their certi cate pinning with the OKHTTP library.
.add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
.build();
.url("https://" + hostname)
.build();
client.newCall(request).execute();
So all we have to do here, is replacing the signature of the correct certi cate with the one
of burp and we are done!? A few more steps are required but this is a good start.
First we take our „.der“ or „.crt“ certi cate and transform it into the public key format.
Then we use this public key format to calculate the public key hash. The output is shown
below and this is the hash we need to replace in the application or/and in the
network_security_con g.xml.
We can also use the openssl command from above to identify the sha256 value of the
original certi cate and search for this value. These steps are explained in detail in the
video of the Udemy course.
14.1.2.1. Approach
<network-security-con g>
<domain-con g>
<trust-anchors>
</trust-anchors>
</domain-con g>
</network-security-con g>
After installing the burp certi cate and starting the app, we can inspect the tra c of the
mobile application.
Sometimes the app contains the whole certi cate in it. You may nd this in the assets
directory. Patching this is more easy.
14.2.1.Approach
• Decompile the application
• Check the type of certi cate „public key“ or „.der“ format
• Generate / Transform the burp certi cate into the right format
• Copy paste / replace the burp certi cate with the correct one
• Compile the application
• Sign the application
• Done :)
Simply decompile the APK, replace the „server.der“ with the exported certi cate of the
BurpSuite and recompile the app.
14.2.2. Commands
15.1. Installation
The installation process is simple but there are some pitfalls we need to be aware of.
For the smartphone we need the „FRIDA-Server“. This binary is available for several
di erent platforms as shown below:
• frida-server-14.2.5-android-arm.xz
• frida-server-14.2.5-android-arm64.xz
• frida-server-14.2.5-android-x86.xz
• frida-server-14.2.5-android-x86_64.xz
ARM and x86 are processor architectures and they are very di erent. Please take a look
into the video if you are interested to hear more about this.
Modern smartphones are most likely using 64 bit but you can determine this by typing
„cat /proc/cpuinfo“ into an adb terminal.
15.1.2.FRIDA - Version
FRIDA is still being heavily improved. After so many years, there are still weekly updates
with complete new version numbers. Newer versions might be sometimes a little bit
buggy regarding to certain architectures (x86).
So in case of any errors, they might have been xed in a newer version of FRIDA so it is
really a good advice to always keep it up to date.
FRIDA contains a live debugging feature, where you can use google chrome to debug
your JavaScript code. This is extremely helpful, if you write more complex scripts.
Unfortunately a lot of things has changed and for me, it did not work in newer versions.
So if we want to use this feature, we have to stick to an „older“ version, which does not
really a ect our testing. We will talk about this next.
15.1.3.FRIDA - Installation
If you want to install a certain version of FRIDA (https://github.com/frida/frida/releases/
tag/14.2.5) we can simply do it with the following command:
Now we only need to install the frida-tools package and we are done :)
$ adb shell
$ cd /data/local/tmp
$ chmod +x /data/local/tmp/frida-server
$ su
# ./frida-server
Please make sure, that the FRIDA CLI version on your laptop / VM is the
same as the FRIDA server that is running on the smartphone. You might
encounter very strange problems if this is not the case. You can verify
this with „frida —version“ on your Laptop and compare it with the
version of the frida-server on your smartphone (banner).
We can verify if the connection is working by listing the running processes of the
smartphone. Open up a new linux terminal and run:
$ frida-ps -U
This will run the frida-ps script via the USB (-U) connection. If the server is running and
the connection is working, we will get an output of the processes running at the moment.
This application is just a simply „dicer“ app. You can shake your smartphone and receive
the result which is based on a SecureRandom functionality. The developer did make
absolutely no mistake because this class provides a cryptographically strong
pseudorandomd number generator (PRNG).
Source: https://developer.android.com/reference/java/security/SecureRandom.
return this.rollDice(param1,param2);
};
});
JavaScript is very unsafe regarding to types. Strings and integers can be automatically
handled. Many other things have to be casted because JavaScript is not aware of certain
Android objects.
Modi cation
Beside accessing and prtinting them, we can of course also modify them. We can also
keep track of the original values. We can do all of this on the y by simply pasting this into
the FRIDA console or reloading the script.
Java.perform(function() {
const Dicer = Java.use('org.secuso.privacyfriendlydicer.dicer.Dicer');
Dicer.rollDice.implementation = function (param1, param2) {
param1 = 2;
param2 = 4;
return this.rollDice(param1,param2);
};
});
15.4.2.Overloading Functions
The same function can be de ned multiple times with di erent parameters. This behavior
is called, overloading.
If we have a di erent amount of parameters, this should be not a big deal.
Java.perform(function => {
const Entity = Java.use('com.element.Entity');
Entity.removeComponent.overload('[Lcom.example.Component').implementation =
function (param1) {
return this.removeComponent(param1);
};
});
But sometimes it can be a little bit tricky. Example given. The function above is returning
back a byte array. Therefore we need to declare a byte array rst, ll it with values and
then can return it back.
The JavaScript code for declaring the byte array looks as follow. I do not want to provide
a full FRIDA script here because this is part of a challenge :)
Here we de ne exactly, that it is an Array from the type „[I“ (SMALI). If we want to have an
byte array („[B“) we can write: var intArray = Java.array('byte', [1, 2, 3, 4, 5, 6]);
Java.perform(function() {
const secuRandom = Java.use('java.security.SecureRandom');
secuRandomInstance = secuRandom.$new();
var buffer = secuRandomInstance.nextInt(5);
send(„Random.nextInt " + buffer);
});
We get a reference to the SecureRandom class and create a new instance with $new().
Afterwards we can call the „nextInt“ function via this instance.
Java.perform(function () {
Java.choose("de.fgerbig.spacepeng.services.Profile", {
onMatch: function(instance) {
send('Found:'+instance)
instance.highScore.value = 1000000
},
onComplete: function() { }
});
});
We have to use „Java.choose“ to fetch all instances. In the onMatch callback, we will get
all references to all found objects. Then we get the instance variable „highScore“ via the
„.value“ param and increase it.
Java.perform(function () {
var getStringClass = Java.use("java.lang.String");
var stringInstance = getStringClass.$new("We have a new String Object!");
send("We can provide this Object as parameter: " + stringInstance.toString());
});
Java.perform(function () {
Java.choose("com.artemis.World", {
onMatch: function(instance) {
send('Found:'+instance);
instance.x = 120;
instance.y = 100;
var entityFactory =
Java.use("de.fgerbig.spacepeng.services.EntityFactory")
entityFactory.createPlayer(instance);
},
onComplete: function() {
send("Done!");
}
});
});
15.7. Timing
Timing can be very Important in FRIDA. Generally speaking, there are two possibilities.
• We can hook into the JVM and start the application
• We can hook into an application, that is already running.
Why is it important to know, when to hook? It can happen that certain methods are not
available at the beginning. They are being loaded later on. If we try to hook these
methods before they have been loaded, our script will be useless.
If we hook into an application that is already running, we might have missed certain
checks like „Rooting Detections“ which makes our script also useless.
Installation
The installation is straight forward. Simply run this on your Linux system.
Running jnitrace
Simply provide the library-name we want to hook with the „-l“ parameter. If you want to
hook before the apps start, then we need to add the „-m“ parameter.
Also take a look at the „Timing“ chapter if you want to know the di erence :)
The output of this tool for the function above is shown below:
We get an amazing trace view about the single JNI functions in this library and we can
also see that „decryptString“ is being called.
The return value of our encrypted string will be printed below. But take a close look, it is
the value „0x69“ which seems to be a reference to the original „Yhiq…“ string. We see it
here :)
You can use FRIDA CLI and jnitrace at the same time. However if you
want to combine this amazing tool with your own scripts, take a look at
the „-p“ and „-a“ parameter.
So even if the calling of the „decryptString“ function only seems to require two
parameters, we need to consider two more. Please have a look at the video for a detailed
explanation.
If we want to print out the „String pw“ and the „int round“ paramter, we can do it with the
following script.
Interceptor.attach(Module.findExportByName("libnative-lib.so",
"Java_com_apphacking_ndkfrida_MainActivity_decryptString"), {
onEnter: function (args) {
Java.perform( function () {
var String = Java.use("java.lang.String");
// The data type of the password is a "jstring"
// Therefore we need to cast it to a "Java String"
var javaStringArg = Java.cast(ptr(password), String)
send("Argument1: " + javaStringArg.toString());
});
// We can simply print out the integer but JSON.stringify comes handy in
the future
send("Argument2: " + JSON.stringify(args[3]));
},
The function that calls the „decryptString“ method in the app, can be triggered with a
button. We therefore don’t need to inject the script before the app starts.
We can simply inject into the already running application.
We can now see the parameter „Udemy App Hacking“ and the integer value „0x4“ (4). To
get a detailed explanation of this example, please take a look into the video. We will only
discuss the most important parts here.
The interceptor, intercepts function calls to the target address. We can retrieve the
address address with the „Module. ndExportByName“ function.
After getting the address, we can de ne two callbacks to interact with this function.
OnEnter - Callback:
We are entering the function and we are able to interact with the parameters. We can print
them out or modify them. But we do not have an impact on the return value.
If we want to overwrite the „pw“ (string) and the „round“ (int) parameter, we have to do it
this way:
Interceptor.attach(Module.findExportByName("libnative-lib.so",
"Java_com_apphacking_ndkfrida_MainActivity_decryptString"), {
onEnter: function (args) {
var password = args[2];
Java.perform( function () {
var String = Java.use("java.lang.String");
// The data type of the password is a "jstring"
// Therefore we need to cast it to a "Java String"
var javaStringArg = Java.cast(ptr(password), String)
send("Argument1: " + javaStringArg.toString());
});
// We can simply print out the integer but JSON.stringify comes handy in
the future
send("Argument2: " + JSON.stringify(args[3]));
// Create a new String
const myArgument = Java.vm.getEnv().newStringUtf("My Argument");
// Overwrite the password argument with "My Argument"
args[2] = myArgument
// Overwritng the round parameter with "1" (ptr stands for pointer)
args[3] = ptr("1");
},
// Return Value
onLeave: function (retval) {
send("retval: " + retval);
}
});
OnLeave:
In this part of our function, we are able to play around with the return value. We can print
it out or simply modify it as shown above:
This part is only included in the videos. It contains ton of useful FRIDA
commands and shows you how to deal with the UI Thread in FRIDA.
- END -
Roman Stuehler (Udemy) Page 104 / 104
ff
fi