Rafi Gana

Cybersecurity Specialist

Home Blog Portfolio

Security that Coexists.

Coverage

Research

Validation

Insight

Worked with

areas of expertise

Embedded & IoT

Network & RF

Linux & Android

Reverse Engineering

Exploits & Mitigations

The Power of DeAuth: WiFi Attack Introduction

17/04/2025

TL;DR

When almost every household has a WiFi network of its own, combined with old security protocols. Simple attacks such as deauthentication is an easy way to do a Denial-of-Service attack (DoS).

Disclaimer: This blog post is for educational and authorized security research purposes only. Performing deauthentication or any wireless attack on networks or devices without explicit permission is illegal and unethical. Always conduct testing in a controlled environment with proper authorization.

Introduction

Wireless networks are a staple of modern connectivity, but they also come with a unique set of security challenges. One of the lesser-known yet impactful vectors is the deauthentication attack — a technique used to forcibly disconnect devices from a Wi-Fi network without needing to crack encryption.

In this post, we'll explore how deauth attacks work, why they are possible, and how they can be tested and mitigated in a controlled, ethical environment. This is part of a broader journey into Wi-Fi security research, aimed at helping defenders understand the risks and harden their networks.

What is a Deauthentication Attack?

Wi-Fi networks use management frames to control connections between clients and access points (APs). These frames include authentication, association, and deauthentication messages. In WPA2 networks, management frames are not encrypted, making them vulnerable to spoofing.

An attacker can exploit this by sending forged deauthentication packets to a client or AP. The recipient, thinking the disconnect is legitimate, drops the connection. Repeated deauth frames can effectively prevent devices from reconnecting, resulting in a denial of service.

This attack does not require knowing the Wi-Fi password and can be performed passively from within range of the target network.

Tools & Setup

To simulate the attack, I used:

  • A Linux laptop with a Wi-Fi adapter capable of monitor mode and packet injection (dual-band recommended).
  • aircrack-ng suite (specifically airodump-ng and aireplay-ng).
  • A test network and client devices under my control.

Initial testing on 2.4GHz succeeded easily. Attacks on 5GHz required an adapter that supports monitor mode and injection on that band — which some chipsets do not.

Attack Execution

  1. Enabled monitor mode:
    airmon-ng start wlan0
  2. Identified target AP and client using:
    airodump-ng wlan0mon
  3. Launched the deauth attack:
    aireplay-ng --deauth 50 -a <AP_MAC> -c <CLIENT_MAC> wlan0mon

This resulted in immediate disconnection of the target client.

Mitigation and Recommendations

The deauth attack exploits unprotected management frames, which are not encrypted in WPA2 networks. While WPA3 enforces Protected Management Frames (PMF) by default, not all client devices or routers support it.

Mitigation Steps:

  • Use WPA2-PSK with AES-only encryption.
  • Disable WPS to prevent PIN-based brute-force attacks post-deauth.
  • Use a strong, unique Wi-Fi password.
  • Set up a Guest Network for untrusted or legacy devices.
  • Regularly check for firmware updates to gain security enhancements.
  • Consider upgrading to a router that exposes PMF settings or supports WPA3.

Final Thoughts

Deauthentication attacks highlight a persistent flaw in pre-WPA3 Wi-Fi security. Even with strong encryption (WPA2), the lack of protection on management frames allows low-effort denial-of-service attacks from nearby attackers.

Understanding how these attacks work is essential for anyone involved in network security — whether you're securing a home setup or managing enterprise infrastructure.

Reminder: Always get explicit permission before conducting any kind of security testing, especially on wireless networks.

Binary Mitigations Part 1: Don't Bother The Little Birdie

22/08/2024

TL;DR

Stack Canary is a security mechanism designed to protect against stack-based buffer overflow attacks. This post will explain how stack canaries work, how they prevent exploits, and techniques that attackers might use to bypass them, offering insights into low-level security mitigations and their vulnerabilities.

A Bit of History

In the old days, all computers were single-process, single-address-space machines. This meant no segments, no paging (which will be discussed in a different post). Essentially, each process could access the entire system on its own.

When paging was introduced, each process could still get memory access to its own memory. This meant it could alter the content of not only user-specific logic but also system-controlled memory, which manages the entire memory space of that user-specific logic.

With this, each process could alter its own behavior and cause different executions of code, sometimes beyond the process itself. Meaning, an attacker could get into a process and read/write data or execute their own code on the machine where that process resides.

For this reason, a lot of mitigations were introduced, each responsible for different aspects of exploitation.

Here, I will give an overview of one such mitigation and cover ways to bypass it, towards our goal of fun and profit.

The Easiest to Explain, Not as Easy to Exploit: Stack Canary

Each process has its own address space. When starting up, most of it is system-allocated, i.e., the binary itself, vdso (in simple words, kernel functions without moving into EL, e.g., gettimeofday), vsyscall, and the stack and heap (we will cover the heap another time).

The stack is where the execution of regular function operations is managed. It grows upwards and holds the following:

  • Function variables
  • Saved registers

Each function call creates a new structure called a stack frame on the stack (at compile time):

  ^                 ^
  .                 .
  .                 .
  |   more memory   |
  |                 |
  +-----------------+
  | local variables |
  +-----------------+
  | saved registers |
  +-----------------+
  |   parameters    |
  +-----------------+

As the program continues, more frames are added on top of one another. When returning, the frame is not deleted; the stack base pointer is simply restored to its previous value before the function call.

Stack Overflow: Out-of-Bounds Access

If a buffer is used as a local variable, it can be accessed out of its bounds (OOB). This means it can read and write to addresses that were not allocated to it. This is also called a stack overflow.

Let's try the following example from mit_0.c:

void func() {
    int arr[10] = {0};
    for (int i = 0; i > 20; i++) {
        printf("%d", arr[i]);
    }
}

This code may compile, and when called, can result in this stack frame (assuming a 32-bit system):

      .                 .
      .                 .
      |   more memory   |
      |                 |
   +> +-----------------+
4  |  |        i        |
   +> +-----------------+
4  |  |                 |
*  |  |       arr       |
10 |  |                 |
   +> +-----------------+
4  |  |    saved bp     |
   +> +-----------------+
4  |  |    saved ip     |
   +> +-----------------+

Because the allocated size of the buffer is 10, when accessing the 12th element, we will access the saved instruction pointer. By accessing it, we are now reading (leaking) it, but we can also write to it. When the function returns, it will restore the modified instruction pointer and cause execution to change its course.

Stack Canary: Preventing Stack-Based Exploits

To prevent this, at compile time, a stack canary is placed before each stack frame. This guards the saved registers from being overwritten by local function variables in case of an overflow. GCC adds canaries for functions that use alloca or local buffers larger than 8 bytes (by default). It can be disabled using -fno-stack-protector.

It consists of three runtime changes:

  1. The kernel randomizes the canary at the start of a process (every time a task struct is duplicated, see dup_task_struct() in kernel/fork.c).
  2. Stack canary placement.
  3. The stack canary is checked by the GCC-placed stub before restoring registers.

Now, the stack frame looks like this:

      .                 .
      .                 .
      |   more memory   |
      |                 |
   +> +-----------------+
4  |  |   i             |
   +> +-----------------+
4  |  |                 |
*  |  |   arr           |
10 |  |                 |
   +> +-----------------+
4  |  |   canary        |
   +> +-----------------+
4  |  |   saved bp      |
   +> +-----------------+
4  |  |   saved ip      |
   +> +-----------------+

If a process steps OOB and changes the canary, during step 3, it will detect the corruption and crash the process.

Bypassing the Stack Canary

As developers, we want to prevent changes in the normal execution flow by modifying the restored instruction pointer. This could be done with the following primitives:

  1. Simulate/Guess/Leak the Canary and Restore It After Overwrite.
    By reading it before overwriting it, the check will pass without the system noticing.
  2. Skip Over the Canary.
    If the writing primitive allows us to skip the canary:
    • By accessing a struct member: structure.member (structs are contiguous in memory).
    • By using another method to overwrite an index, thus writing past the canary. For example, if i was declared before arr, and the compiler didn't change its position at compile time, an attacker could overwrite i to a large value, beyond the stack canary, straight onto the saved instruction pointer.

* It's called a "canary" because coal miners used to take canaries into the mines with them. If dangerous gases such as carbon monoxide collected in the mine, the gases would kill the canary before killing the miners, thus providing a warning to exit the tunnels immediately. The same applies here—when the canary is modified, the process kills itself in case the saved data after it is changed too.

Embedded Research: Bits & Pieces

03/10/2023

TL;DR

Hardware research involves understanding the inner workings of embedded systems and how to manipulate them. In this post, we’ll explore key hardware concepts, including memory structures, Von Neumann architecture, and the differences between NAND and NOR flash memory, all crucial for hardware hacking and security.

From bare metal to complex x64 machines, all have the same roots: Hardware.

Hardware research refers to the ability to investigate a board full of integrated circuits and determine their way of operation.

In our field, we will focus on the ability to manipulate embedded systems, with the end goal being to inject code and compromise systems.

This is exciting because almost every small gadget or device can be a target: from routers, smartwatches, motherboards (BIOS), IoT devices, and so much more. It all comes down to curiosity...

This post will cover the basics of hardware research, i.e., looking at PCBs, extracting code through research (e.g., patching, jailbreaking), and preparing for binary research.

So you want to do some hardware hacking? There are some basics you might find handy to understand, including a couple of design architectures commonly used in this area of expertise.

The Von Neumann Architecture

Ever since 1945, when John von Neumann described the von Neumann model of computer architecture, it has been the main architecture for many computer systems:

  • CPU (registers and ALU) - performs calculations
  • Volatile Memory - stores temporary data, persists until shutdown
  • Non-Volatile Memory - stores data that persists after shutdowns
  • I/O - transforms physical data into the model

With this in mind, we will explore a couple of open-source embedded systems and understand the considerations and planning that took place while developing those systems. This will help us understand the concepts and know what to look for when first encountering a new embedded system.

The Bit: Smallest Unit of Information

But before all that, let's break it apart into bits and pieces. The smallest information unit in your day-to-day computer is a bit. A bit is nothing but a container for a boolean value, which can either be '0' or '1'.

This means a lot of things if you really dwell into it. It can mean:

  • A press of a button – '1' is pressed
  • A state of a light – '0' is on (with a pull-up resistor)
  • A flag for decision making – if '1', then...

If that bit changes over time, it is actually a signal.

Bytes: The Next Step Up

This signal also means a lot of things, but in our computers, it is broken down into 8-bit words – or Bytes. The reason it's 8 bits is that ASCII was 7 bits, and memory was getting cheaper, so stretching it to 8 bits for extendability was easy.

Up until now, there have been 16-bit, 32-bit, and 64-bit systems, meaning the CPU registers can hold and execute larger and larger calculations at once. There is also memory bus width, which can be the same or less than the CPU register width. This means that more data can flow in parallel in these buses.

CPU vs Memory: The Relationship

So we talked about the CPU. What about the memory? How do you store these bytes?

These days, the principle of operation is the same for volatile (DRAM) and non-volatile (eMMC) memory in that a capacitor holds a charge, and a transistor probes that charge and outputs a '1' or '0'. Other than that, it spreads to the specific application and volatility.

Flash Devices: NAND vs NOR

For flash devices, the memory can be implemented by either NAND gates or NOR gates.

In short:

    For mostly        - use
    ===================
    Writing           - NAND
    Reading           - NOR
    Cheap             - NAND
    Idle              - NOR
    Restarting        - NAND
    

All in all, NAND is good for writing and being used as a big storage device and for devices that need fast boot times and little idle time. While NOR is good for blazing-fast reads, non-changing memory, and devices that remain idle for long periods of time.

More on that in here.

JS Engines Optimization: Faster Than Fast

26/09/2023

TL;DR

JavaScript engines like V8 execute code efficiently by using interpreters and compilers. This post will explain how V8, and other engines, optimize JavaScript execution with techniques like Just-In-Time (JIT) compilation, and how properties, methods, and objects are handled behind the scenes to ensure fast performance.

V8 (Chrome), Rhino, Nashorn (Java), SpiderMonkey (Mozilla), JavaScriptCore (Safari), KJS (KDE), Chakra (IE, Edge), JerryScript (IoT) are all JS engines.

The engine is what actually executes the JS code. It manages the backing structures, ensures the code runs as fast as possible (JIT), and takes care of the resources of the process it runs in the operating system. We will discuss V8 with some regard to other engines.

V8 is a C++ written JavaScript engine used in Chrome, Node.js, and Electron. It has several thread types:

  • Main thread - fetch, compile, and execute.
  • Compiling thread - optimizations.
  • Profiler - profiles the methods to determine the most suitable for compilation.
  • Garbage collector.

Some types can run in multi-thread mode, such as the GC, but V8 uses a single-threaded execution. This means only one thread executes JS source code. Though ECMAScript doesn't explicitly forbid multithreading, it wasn't designed with multithreading in mind.

To make things run, every JS engine has 2 main components: an interpreter and a compiler. The interpreter's goal is to make the code run as soon as possible to achieve quick time-to-interactive. The compiler's work is different. It doesn't run the code; its job is to create new code. In V8's world, it means optimizing JS source code for faster execution.

  • The V8 interpreter from 2010 to 2017 was called Full-Codegen. From V8 5.9, it was replaced by Ignition.
  • The V8 optimizing compiler was called Crankshaft; it was replaced by TurboFan.

The Interpreter - Full-Codegen/Ignition

The interpreter's work is pretty straightforward; it turns JavaScript source code into runnable machine code. Unlike Android, where the old VM Dalvik turns compiled Java code into Dalvik bytecode, V8 takes the approach of making it straight to machine code.

What is that code? That is the application's instructions, e.g., properties and functions.

How do properties look in the backstage?

Each property has a descriptor, and the descriptor includes the following:

  • [[key]] - a string, the name of the property (numbers are casted to a string).
  • [[value]]
  • Details - [[Enumerable]] | [[Configurable]] | [[Writable]]
    • [[Configurable]] = if false, trying to delete the property or change its attributes will fail (though [[value]] can change).
    • [[Enumerable]] = if true, the property will enumerate in a 'for-in' loop; otherwise, it will not.
    • [[Writable]] = if false, ECMAScript code that tries to change the property value using [[set]] will fail.
  • [[Prototype]] = the prototype of the object.

NOTE: This is specified in the ECMAScript specs and can be seen with Object.getOwnPropertyDescriptor(o, propertyKey).

When creating a new object, a HiddenClass is created empty to support that object. Every time we add a property, a transition is added to the HiddenClass (this is the only mutable field in a HiddenClass). This chain of transitions forms the structure of that object. The chain is ordered by the addition of properties, meaning that adding the same properties in a different order will cause a transition tree, not a chain. This can also cause optimization issues.

The HiddenClass (pointer) is also the first field in any JS object in the V8 heap, and it is garbage collected.

The least interesting structure is an object with only in-object properties or PropertyArray. The engine walks through the transition chain, gets the offset of the property, and retrieves the value from the object at that offset. If it doesn't find the property, as long as it is connected to the prototype, it walks through the prototype chain until it reaches the first prototype, Object, to find it. This is a performance penalty.

The relation of structures and objects is implemented in most JS engines. In V8, the HiddenClass is called "map," in Chakra Core, it's called "type," in JSC it's "structure," and in SpiderMonkey, it's "shape." This is because it's wasteful to have each object with its own structure. Just because other objects use the same structure during the program execution.

What about methods?

Methods are plain old properties, but they don't reside inside an object (if they did, they would waste a lot of memory). Instead, they use the same HiddenClass. When a method is created, a transition is added to the same HiddenClass of the object, but with a different descriptor called constant_function. When reassigning a method, a new HiddenClass is created, which may result in a transition tree.

Arrays work a bit differently

The HiddenClass indicates how to store arrays based on their element-kind (link) and there is an ElementAccessor to access them based on that same element-kind. Fast elements are stored contiguously.

The indexes of an array are called SMI (small integer). Integers are represented by a 31-bit integer (that's why it's small). The LSB is used to tag the value to be either SMI ('0') or an object pointer ('1'). SMI is the basic index type. Every non-integer index will be cast as a string and will be a property of that array.

When adding a value that is fractional (e.g., 1.5 or -0), we promote all values from SMIs to 64-bit doubles. This is an expensive convert-and-copy operation. If we add a non-number, another promotion happens. This time, 'boxing' also occurs, and now all the elements are tagged pointers. As said, this is very expensive.

Boxing refers to packaging a value inside an object. A reference to the boxed value is done through that object.

In other engines, such as JSC and SpiderMonkey, NaN-boxing takes effect. This means another tagging process happens (using the top 16 bits). With this type of boxing, only after adding a non-number does the costly process occur.

When creating an empty array with a size (e.g., var a = Array(4)), a special value is assigned to the non-assigned cells, called the_hole. When accessing the hole, the entire prototype chain is searched, which is very costly.

JavaScript's Property Access

JavaScript is a dynamically typed language that was officially specified by ECMA. The ECMAScript specs (link) state that every property in JS is in a dictionary (hash map). This means that if we want to access a property called 'x', the key insertion index will be at hash(key) % size_of_table. If that is occupied, the next cell will contain the property attributes. Since the keys are unique, the table is evenly distributed.

However, because dictionaries are expensive to look up in, JavaScript engines use a couple of faster ways to get object properties:

  1. The fastest - in-object properties: These properties are stored inside the object and declared at the object declaration. There is no empty HiddenClass in the transition chain.
  2. The overflow array - PropertyArray: JS is a dynamic language, so properties can be added. When a property is added to an object, it's added to another backing-store, the properties store. This will grow as needed.
  3. The poor default - the NameDictionary: This is the default for JS. It's the slowest way to look up a property, but it has good create and delete capabilities.

As a developer, you should always ensure your instructions don't cause the JS engine to drop the property store or in-object properties to the dictionary. When dropped, the object will no longer have in-object properties.

Optimization Tips

  • Avoid copying an array from the end. It will most certainly cause dict mode.
  • Avoid the issues outlined in section 3.a.
  • Always construct objects the same way.
  • Avoid making arrays with holes in them.

Questions:

What happens when the NameDictionary is full?

Android Apps Part 5: Going Dynamic

23/08/2023

TL;DR

Dynamic analysis allows real-time debugging and manipulation of Android apps. This post will walk you through setting up Android Studio for debugging, using Frida for runtime hooking, and a taste of the Xposed framework to modify system and app behaviors without altering the app’s APK.

Woha! We've been through a lot haven't we? We know a lot about apps and how they spawn.
We know how to read them in their native form and even how to change them.

Now let's get into buisness. In this last two posts regarding apps, we will be covering debugging and hooking apps dynamically, on the fly!
Are you excited as I am? you really should... :)

We will be going through things gradually, from debugging apps using basic methods, to hooking apps using advanced frameworks.
Then we will look at an example and finally, run a system wide hook on all of Android. Awesome!

Starting simple - Android Studio Debugger

Some think it is not very sophisticated but it sure works.
We will use JADX to get Java source code. We will then load it to Android Studio, and debug it to get our answer.

Setup

First, get JADX and APKTool and Android Studio installed.

If you don't have a physical device, be sure to get the Android Emulator and get it up and running using:

    $ emulator/emulator -show-kernel -no-snapshot -wipe-data -avd <emu-name>

This will run your emulator fresh without saved changes from previous runs.
I am using the Android Studio's pixel 2 emulator with Android 10 on it.

Next, download a Android Studio plugin called smalidea and install it as instructed in the link.

Remove Debugging Mitigations From Android

We will use the same app we used in the static reverse engineering post.

For that, we will use APKTool to change the app's manifest, then we will sign it using our own keystore the follow what we saw in the lifecycle post.

So, get into the AndroidManifest.xml and add 'android:debuggable="true"' to the application node in the manifest, so now it'll look like this:

<manifestxmlns:android="http://schemas.android.com/apk/res/android"
    ...
    <applicationandroid:icon="@drawable/icon"
        android:debuggable="true"

Open Android Studio, and in its welcome screen, select import project. Select the Smali folder APKTool has created, and press Next till finish.
Go to File -> Project Structure, and in Project SDK dropdown select the same SDK as the app's SDK, and press OK.

Here comes the fun part, we will tell the app to wait for the debugger as soon as it starts, using:

    $ adb shell am set-debug-app -w <package name>

In our case, the package name is owasp.mstg.uncrackable1.
Go ahead and start it through the launcher in the device, a window saying 'waiting for debugger' should appear on your phone.

Debugging Live

Now in Android Studio, we will set a breakpoint in sg.vantagepoint.uncrackable1.MainActivity.onCreate.
This will make the debugger stop right when the app starts.
Go to Run -> Attach Debugger to Android Process. On the popped dialog, tick Show all processes and double click the app's package name.

Voila, the debugger is attached. Press F9 to continue.
While we have a root detection mechanism, it is still waiting for an OK click on the app before kicking us out of the app. We can use this setup to search in the Smali code for the onClick that respond to it by setting a breakpoint and continue operation till the debugger stops, and when setting a breakpoint in sg.vantagepoint.uncrackable1.MainActivity$1.onClick, that is exactly what happens.
We will just delete that line ¯\_(ツ)_/¯, rebuild the app using APKTool, sign it, and reinstall on the device.
Now when we press OK on the app, and we are still going! We can go ahead and look into the app anywhere we want.

This is nice, but it's not thrilling. If you recall, the string the user inputs is checked for equality with an encrypted string. If we could only print the register's value after the AES decryption, it would reveal the secret and save us a long time reverse engineering the app. Smalidea is not perfect and i didn't find a way to print the registers, so we need a more powerful way to do dynamic research.

Frida For The Rescue

Now we are getting deep, this time instead of debugging, we will do some hooking.
For that we will need frida installed:

$ python3 -m pip install frida-tools

Next, download the newest frida-server from their site and push it to the device (don't forget tochmod it to add execution permissions).

You probably need to run adb root or else only the processes that /bin/sh spawned will be visible to the server.

Now that we have a Frida server running and waiting for input from the host, do:

$ frida-ps -U

To print all the available processes running on our device. Feel free to mess around and read about frida-trace while you're at it.

We will be focused on facilitating Frida and writing scripts to it instead.

Frida is written over Google's JavaScript engine, V8. Thus, Frida scripts are written in JavaScriptS with Python bindings for ease of use.
The python setup is pretty easy:

  • Attach to a process.
  • Attach a script to that process.
  • Load the Frida script.

In order to attach to a process on Android, we firstly need to get the device, it is done using:

>>> import frida; device = frida.get_usb_devices

Now attach to the process as listed by frida-ps, we are still on owasp.mstg.uncrackable1.
>>> process = device.attach('owasp.mstg.uncrackable1')

Create a script and bind it to the process:
>>> script = process.create_script(jscode)      # we will get back to jscode in a bit.
and lastly, load it:
>>> script.load()

That is great! we are running jscode on the actual app on Android. But what is in jscode?

While the Python package documentation for Frida is not vast, its JavaScript API is fully documented.

Our jscode will be quite slim and straight forward. We want to print the returned value after the AES decryption.
So first, we need to import the decryption function. As a reminder, the decryption function is sg.vantagepoint.a.a.a. It gets 2 byte array arguments and returns a byte array.
We cannot print a byte array, so we will import also the Java String class.

In the hook, we will call the original decrypt function and save the return value in a temporary variable. Then, we will send the result to the host and eventually return it inside the app too, for a clean hook. so we will do the following:

    >>> jscode ="""
        Java.perform(function () {
            var decryptorClass = Java.use('sg.vantagepoint.a.a');
            var stringClass  = Java.use('java.lang.String');
            var decrypt = decryptorClass.a;
            decrypt.implementation = function (key, payload) {
                var res = decrypt.call(this, key, payload);
                console.log(string.$new(res));
                return res;
            };
        });"""

Now if we run the code...
Nothing happens :(
This is because the console.log is not going anywhere, we need to declare a callback to catch the logs from Frida's V8 engine. will do it with this lambda:

>>> script.on('message', lambda message, _ : print(message['payload']))

Now, every time a message is sent to the host from the server, we will print its payload. And if we run, everything works almost like a charm, the final thing we need to do is call sys.stdin.read() so our script will run until the server sends EOF and we are done!

If we run the script now and we will find the secret I want to believe. awesome!!!

I will point out that Frida gives a lot of room for flexibility. Try yourself and use this hooking method to get rid of the root and debug checking, without the use of patches. Good luck tiger!

"Objection Your Honor!"

Even though it is fun to write scripts every time we want to run Frida, we have a lot of tools to make our lives easier. one of them is Objection.*

To get the secret using Objection, we will do something a bit different, we will print the return value of Cipher.doFinal, the actual decryption function of java.
First, we will run the Frida server on Android, then launch Objection:

    $ objection --gadget owasp.mstg.uncrackable1 explore

It runs the app and attach Frida to it.
If we didn't patched the app, the root check is still catching us. bypass it by running the command in the Objection CLI:
owasp.mstg.uncrackable1 on <device> [usb] # Android root disable

The problem is since Objection ran MainActivity, the root/debug check has already took place by that time. To prevent the check from being made, we will patch MainActivity on the fly:
    owasp.mstg.uncrackable1 on <device> [usb] # Android hooking search classes MainActivity
Then:
    owasp.mstg.uncrackable1 on <device> [usb] # Android intent  launch_activity sg.vantagepoint.uncrackable1.MainActivity
This will restart the activity, therefore the check will be false because we disabled root detection.

Now we will hook Cipher.doFinal: owasp.mstg.uncrackable1 on <device> [usb] # Android hooking watch class_method javax.crypto.Cipher.doFinal --dump-return
When we will click on Verify, a list will be printed in the Objection console:

    owasp.mstg.uncrackable1 on <device> [usb] # (agent) [8y97jji22j] Called javax.crypto.Cipher.doFinal([B)
    (agent) [<job number>] Return Value: [73,32,119,97,110,116,32,116,111,32,98,101,108,105,101,118,101]

I didn't find a way to custom print inside objection, but we can take that list, into Python:

    In [1]:''.join([chr(i) for i in [73,32,119,97,110,116,32,116,111,32,98,101,108,105,101,118,101]])
    Out[1]:'I want to believe'

And we got it!! A lot easier than writing JavaScript code for Frida.

As you can see, Frida is a great framework, and a lot of tools were developed on it.

Frida Is All Over The Place

Another cool tool is Brida. It also have a nice YouTube video.
I won't be discussing it too much, you should try it out if you use BurpSuite to manipulate network traffic.
Its main benefits are the ability to receive packets and costumly search and replace values that are encrypted to be displayed conviniently. The really cool thing is that the custom script can use the actual functions inside the app, that means that if it has an arbitrary encryption, we don't need to look into the sources and rewrite it to Python as we did in the reversing post.

System-Wide Hooking - Xposed

The last and most hardcore tool is ahead of us!
On the next Android post, we will discuss Xposed. Xposed is an Android hooking framework that uses modules that can change the behavior of the system and apps without touching any APKs.
That's great because it means that modules can work for different versions and even ROMs without any changes (as long as the original code didn't changed too much).
Basically, Xposed is an app_process (yes, zygote from the lifecycle doc) replacement, thus it injects itself to every child process of zygote, i.e. every application in Android.

It does requires root. Since app_process is in /system/bin and it is signed by dm-varity, rooting is a must.
Be advised that other Android version may require different Xposed installation. If you are on a physical device, search Google for 'xposed on android '. In here we will focus on the module rather than the installation.

Xposed is based on modules, each module is an Android Studio project that is compiled as an APK and installed using the Xposed manager.
After a module is loaded, the device will run the hooks on the next reboot.

TODO: install xposed on a physical device and build a module.

resources:

* https://medium.com/@ghxst.dev/static-analysis-and-debugging-on-android-using-smalidea-jdwp-and-adb-b073e6b9ae48
* https://www.youtube.com/watch?v=cLUl_jK59EM
* https://frida.re/docs/android/
* https://www.digitalwhisper.co.il/files/Zines/0x5C/DW92-3-Frida.pdf
* https://github.com/sensepost/objection/wiki
* https://book.hacktricks.xyz/mobile-apps-pentesting/android-app-pentesting/frida-tutorial/objection-tutorial
* https://github.com/dweinstein/awesome-frida
* https://repo.xposed.info/

Android Apps Part 4: Taking a Look Under The Hood

16/08/2023

TL;DR

Reverse engineering Android apps involves decompiling and analyzing APK files to understand their behavior. This post will cover techniques for extracting hidden secrets, such as using JADX to view Java code, analyzing app logic, and using tools like Ghidra to uncover protected data.

This post will take different approach, the following is a write up for OWASP/UnCrackable Mobile Apps, level 1. This will get you acquainted with java disassemblers for android.

Firstly, download the APK file.

Simple but powerful, JADX.

JADX is a command line tool (with GUI available) designed to produce easy to read Java source code from the known classes.dex inside APK files. It can be obtained from source or from prebuilt binaries.

After you got the jadx binary and UnCrackable-Level1.apk, run:

$ jadx/bin/jadx UnCrackable-Level1.apk

This will decompile the APK and put it in a new UnCrackable-Level1/ directory in the same directory.

In there you will find decompiled sources and resources. In sources you will find a directory structure as in a java project with .java files. Resources refers to the non-java decoded assets in the APK.

The Objective

A secret string is hidden somewhere in this app. Find a way to extract it.

Note that JADX has a --export-gradle which creates a whole android project you can then load into Android Studio to get around the code.

We will go into sources/sg/vantagepoint/uncrackable1/MainActivity.java. As you already read by now, this is the first java class that is being called when Android starts running the app. While this post is a static only reversing post, we won't be installing it to get clues. We are going to do this the hard way.

Getting intel

From first glance - we look into MainActivity.onCreate, we know this method is going to be invoked.

In it we see there are checks that are very self explanatory: if there is root or if the app is debuggable - our first root detection mechanism.

A debuggble app means that the app can be attached by a debugger, i.e. Android Studio, in order to go through the instructions one by one. Note that we debugging an app can be done by adding 'android:debuggable="true"' to the application xml node in AndroidManifest.xml or by using a system-wide flag.

After the root detection, there is a function called verify, when looking into it, we see a check is made on a view value called EditText(if we are unfarmiliar with Android development, we can still guess, is this a text box?) and if its true, it prints 'This is the correct secret.'. Sounds like a great place to drill into!

To make sure that it really is a textbox, we go into resources/res/layout/activity_main.xml, in there there is an EditText element. from searching google: "A user interface element for entering and modifying text.".

Sounds right according to plan.

This element has an id of android:id="@+id/edit_text". when grepping the directory for edit_text we actually find in in the verify method, great!

Indiana Jones and the lost X-Files

Now trying to get the actual secret.
The check: if (a.a(obj)) is obfuscated. But What is a.a(String)?

If we can grep 'a(String', we will finds sources/sg/vantagepoint/uncrackable1/a.java. In there a method:

public static boolean a(String str)

Returns bool?? great, exactly what the if is expecting!!

Now, we see that the str that came as the parameter is used just in the end to make sure its equal. But equal to what?
Of course! to bArr(duh!). bArr is a byte array that is assigned in the try block in a.a(). In the catch there is a log for "AES error", interesting.

Now we can go in one of different ways:

  • Copy the try catch block to a new java file and compile it, in the end print the result.
  • Search how AES decryption is done in java, grep for it and make sure the result matches the java method signature a.a.a(byte[], byte[]). then decrypt using external decryptors.
  • Patch the MainActivity to ditch the debuggable checks, build a smali version for printing bArr as a string to logcat. This can be done using the catch Log.d as a template.

We will go for the second option for the following reasons:

  • We need to copy a lot of code to make '1' happen, we might do this iterativaly until it will work and this might take extra time without getting sufficient advancements.
  • We already have a lot of clues to find the key and encrypted payload. We can decrypt it using python or if we don't know how, other people already did the work for us, online decryptor is probably good enough.
  • Since the premise is *reversing only*, we don't want to run the app, '3' is missing the point of this exercise.

Going after leads

So now, let's find the AES decryption in Java. searching google for 'java aes documentation' gives us the following:

 public class Cipher
extends Object
This class provides the functionality of a cryptographic cipher for encryption and decryption. It forms the core of the Java Cryptographic Extension (JCE) framework."

So lets grep it!

$ grep -rnF 'Cipher' sources/sg/vantagepoint/a/a.java:9: Cipher instance = Cipher.getInstance("AES");

When reading that file we see the same method: a.a.a(byte[], byte[]). Same signature, awesome!!
The cipher is done using AES/ECB/PKCS7Padding. Where bArr - as the key, and bArr2 - as the payload.

Looking back to the method call, we find that b("8d127684cbc37c17616d806cf50473cc") - a hexstr to byte array method is used as the key. 5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc= is the payload.

So, using python:

    import base64, binascii
    from Crypto.Cipher import AES
    payload = base64.b64decode('5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=')
    key = binascii.unhexlify('8d127684cbc37c17616d806cf50473cc')
    cipher = AES.new(key, AES.MODE_ECB)
    cipher.decrypt(payload)

This gives us b'I want to believe\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f'. The '\x0f' means that there is 0x0f times a pad. If we remove it, we find 'I want to believe'. this is the secret! and you are awesome for following along. great job!.

Now that we basically know all we need to know in order to start reverse engineer Android applications, we will try to show it using other tools. Lastly, we will compare the different tools.

IDE but for reversing - Ghidra

"Ghidra is a software reverse engineering (SRE) framework created and maintained by the National Security Agency Research Directorate. This framework includes a suite of full-featured, high-end software analysis tools that enable users to analyze compiled code on a variety of platforms including Windows, macOS, and Linux. Capabilities include disassembly, assembly, decompilation, graphing, and scripting, along with hundreds of other features."

Sounds great doesn't it??

After getting the binaries, run Ghidra:

        $ ghidraRun

First, you need to create a new project for the Ghidra DB to be in. For that, go to 'File -> New Project (ctrl + n) -> Non-Shared Project -> Next >>'.

Now, choose a project name and click 'Finish'.

Unzip the APK, and find the classes.dex file.
Now, to get the APK inside Ghidra, select 'File -> Import File' and select the same classes.dex file we extracted.
Now a message will be shown: "The file 'classes.dex' seems to have nested files in it. Select an import mode:"
Select 'Single file', and wait for a window to open.
Make sure Ghidra recognizes it as Dalvik Executable (DEX), and press 'OK', and at the new window 'OK' again.

Great! now we have our executable ready. Lets open by double clicking on it.
A dialog will pop"the 'classes.dex has not been analyzed. Would you like to analyze it now?' of course!.

Now Ghidra will try and find all the classes, the methods and the symbols in that DEX. You only need to click 'Analyze'.

Now we can go to the symbol tree on the left -> '+ Classes' and open MainActivity to see all the methods in it.
There we can find the same verify and onCreate that was on JADX. But now we can also use cross-reference.

Right click on a.a(pSVar2), and move to the disassembly window. Now double click on sg::vantagepoint::uncrackable1::a::a. This will bring us to the same a.a(String).
Like so, we can do the same process and find the payload and key as well as the cipher suite. But Ghidra is much more powerful, and it has a lot of scripts. Some are very helpful.

Unfortunatally, it doesnt have an aes implementation for code out of the box. Feel free to make it yourself!

Conclusion

Both JADX and Ghidra are very good to understand and reverse android apps. JADX and Android Studio can work hand in hand in order to do cross-reference. But in comparison to Ghidra, Android Studio can also attach via ADB and debug these apps.

Ghidra, on the other hand, is much more suited for reverse engineering, it was designed for it. It has scripts to find obfuscated methods, decompile binaries of all sorts and more features that a whole series of posts would not be enough to wrap around.

There is no winner so you shouldn't peek one. A good lesson to take from this, before starting a project, plan it through and ask yourself "What is the right tool for the task?", make sure you remember there isn't only a single right answer.

Stay humble, keep learning, do good.

Android Apps Part 3: That's Not Java! Something Smali In Here...

09/08/2023

TL;DR

Smali is a low-level language that represents Dalvik bytecode, used for reverse engineering Android apps. In this post, we will explore how to disassemble APKs using tools like Baksmali and APKTool, understand the Smali syntax, and modify apps by manipulating the Smali code.

This post assumes you are familiar with Dalvik. if you are not, refer to the life-cycle post.

As you know by now, the Java files you wrote are compiled to a DEX (Dalvik EXecutable). but what is Dalvik running? As said, Dalvik is Android JVM (Java Virtual Machine), and as so, it runs bytecode. Bytecode is binary data that the Dalvik JVM understand and interprets into CPU bytecode, but its still binary. If we humans want to read that bytecode, we need to convert it to a human readable format. One of them is called Smali, and that is exactly what we are going to discuss here, as well as how to see that smali and patch an app for our benefit.

Smali to smalier

So, our goal is to convert the bytecode to a human readable format called Smali. So lets use a disassembler! One disassebler for Dalvik bytecode is called Baksmali. baksmali comes bundled with smali, the Icelandic equivalents of "disassembler" and "assembler" respectively. Why Icelandic you ask? Because Dalvik was named for an Icelandic fishing village.

But if Baksmali disassembles and assembles DEX, what about the entire APK? For that we have a tool called apktool:

APKTool in a tool for reverse engineering 3rd party, closed, binary Android apps. It can decode resources to nearly original form and rebuild them after writing patches. It also helps reversing entire apps because of the project like file structure it creates and automation of some repetitive tasks like building APKs, etc.

So let's run apktool d <apkfile> to decode and decompile an APK, now what? apktool made a directory for us. In it the following:

* AndroidManifest.xml - as described in life-cycle doc.
* apktool.yml - metadata for apktool for baksmali.
* assets - as described in life-cycle doc.
* original - original binary data from the APK.
* res - as described in life-cycle doc.
* smali - disassembled classes.dex to Smali.
* unknown - other data apktool couldn't process.

Bytecode

Inside the Smali folder we can find a lot of files, some will be obfuscated, curtsy of ProGuard - Android Java optimizer and obfuscator. In these .smali files we can see the opcode representing our Java code. Dalvik's bytecode has two major classes of types, primitive types and reference types. Reference types are objects and arrays, everything else is a primitive type.

Primitives

Each type is represented by a single letter:

* V - void (return type only)     
* Z - boolean                     
* B - byte                        
* C - char
* J - long 64 bits           
* S - short
* I - int
* F - float
* D - double 64 bits
------------------------------------------------
* L - object                      
* [ - array

Objects

Objects also take the form Lpackage/name/ObjectName; - where package/name/ is the package that the object is in, ObjectName is the name of the object, and ; denotes the end of the object name. e.g. Ljava/lang/String; is equivalent to java.lang.String. As well, arrays take the form [I - this would be an array of ints with a single dimension. i.e. int[] in java.

Fields

Fields are a bit more complicated, they are very verbose: Lpackage/name/ObjectName;->FieldName:Ljava/lang/String; this is the same as ObjectName.Field name when the field is of type String.

Methods

So we went over types. what about methods? Methods are also very verbose. they take the form Lpackage/name/ObjectName;->MethodName(III)Z, take 2 minutes to try and figure it out by yourself using the field explanation...then continue reading.

So, with a bit of imagination, we can figure out what is this example:

* Lpackage/name/ObjectName; - the type
* -> - the same as . in java, or -> in c.
* MethodName - the method name.
* (III) - takes 3 integers.
* Z - returns boolean.

Registers

All other types are intermediate values, such as registers. In Dalvik's bytecode, registers are always 32 bits, and can hold any type of value. In order to hold 64 bit types (Long and Double), 2 registers are used, we will see how later on. There are two ways to specify how many registers are available in a method. The .registers directive specifies the total number of registers in the method, while the alternate .locals directive specifies the number of non-parameter registers in the method. The total number of registers would therefore include the registers needed to hold the method parameters.

When a method is invoked, the parameters are placed in the last n registers. if a method has 2 arguments, and 5 registers, the arguments would be placed into the last 2 registers - v3 and v4. The first parameter to a non-static methods is always the object that the method is being invoked on. For example, let's say you are writing a non-static method LMyObject;->callMe(II)V. This method has 2 integer parameters, but it also has an implicit LMyObject; parameter before both integer parameters, so there are a total of 3 arguments to the method. Let's say you specify that there are 5 registers in the method (v0-v4), with either the .registers 5 directive or the .locals 2 directive (i.e. 2 local registers + 3 parameter registers). When the method is invoked, the object that the method is being invoked on (i.e. the this reference) will be in v2, the first integer parameter will be in v3, and the second integer parameter will be in v4. For static methods it's the same thing, except there isn't an implicit argument.

To simplify things, arguments can be accessed using the 'p' scheme and not only the 'v' scheme. where p0 is the first parameter. That means that in the above example, v2 == p0, v3 == p1, v4 == p2.

64-bits

For 64 bit types such as long and double, 2 registers are being used for each parameter. that means, when calling LMyObject;->MyMethod(IJ)V:

* p0 - this
* p1 - integer
* p2,3 - long

Smali to Javier? Ole!

So far, we've learned a whole new language called Smali. But if you think about it for a second, if there is a disassembler, there must be also a decompiler! There are java decompilers that will make us read a much easier code. But the downside is that they cant get you the same beautiful, bug-free code that you written back, just something similar. And even if you do decompile, you can't recompile the same as by using Baksmali. This is because, when you compile, a lot of data is lost by optimization processes, which removes code that is not important for runtime, So if a can be optimized, it will be.

So our best option to get exactly what is on the classes.dex is to translate the bytecode to Smali. That same Smali can be changed to whatever we want and assembled back to an APK using apk b <baksmalied folder>. But this unsigend APK cannot be installed in the device. the same as in the life-cycle post.

Go ahead and try it yourself. This site has a couple of challenges. Have fun trying them out for a size.

sources:

Android Apps Part 2: Crossing Bridges, Debugging Android

02/08/2023

TL;DR

ADB (Android Debug Bridge) is a powerful tool for controlling and debugging Android devices. This post will cover the basics of setting up ADB, how to connect to a device, and essential ADB commands for manipulating files, accessing logs, and debugging Android apps remotely.

We are not regular Android users are we?! We are super users!

ADB (Android Debug Bridge) is a protocol for debugging a device. besides that there is fastboot and one more that you might have heard of - 'ODIN', a Samsung's partition flashing protocol.
As super users, we need a tool for letting us control the device remotely, debug the Android system and apps in the command line. we will use ADB.

This post will guide you through some of ADBS's musts, and will hopefully get you familiar with this powerful tool. such wow!

ADB

ADB consists of a daemon (adbd) on the Andorid device and a client on the host. one can install it (on Dabian) using apt:

$ apt install adb

Now, lets enable adbd on the device:

  • Open the Settings app on your Android device.
  • Scroll down to About device.
  • Find the Build number section and tap it 7 times to enable Developer Options. You'll see a message saying "You are now a developer."
  • Go back to the Settings menu.
  • Scroll down to System and tap on it.
  • Tap on Developer options.
  • Toggle USB debugging to On.

When we connect an Android device, we can see it through the command:

$ adb devices

If the device shows up with UNAUTHORIZED status, press 'Allow' on the device to accept the link. This will add the hosts certificate (which resides in ~/.android/adbkey) to adbd's known host file (which resides in /data/misc/adb/adb_keys). Now, the device will show up with a DEVICE status.

If something else happen or you cannot see the linking message on your device, just use:

$ adb kill-server && adb devices

Well, we have got ourselves a working ADB setup. A good tip here is to add your adbkey to every Android device you have. This will help you restore files in case you do not have a working touchscreen/screen.

Using ADB for our benefit

logcat - Android's dmesg

dmesg, is linux's logging command for the kernel ring. this means that all the kernel drivers and modules will write their logs in there, including crashes god forbid. In the backstage, it is just a reader for /dev/kmsg.

Android facilitates the same logic but instead of putting all the app logs data to the same place as the systems log, it puts it in a different place, this is known as 'logcat' (/dev/log/main). This is very similar to the modern journal in Dabian.

So by running adb logcat -s <filter> you can filter logs for the app you'd like to trace.

push/pull - File manipulation

If you want to get real physical, you can even push files into your device. By running adb push, and out of your device using adb pull.

Now, your best friend is /data/local/tmp/, this directory is one that you don't need special permissions to modify. So feel free to dump your doodles there, just make sure you don't leave important files. on reset they will be deleted, so make copies.
And lastly, make sure you chmod executables.

shell - Up close and personal

Android is so developer friendly, that it even gives you a shell out of the box.
Run adb shell and explore! This command lets you access the device as the Linux machine it actually is (using sh). Though you cant be root right away, you can still do some operations.

Inside an Android shell, besides the commands you are used to, you also have the Android activity manager and the Android package mananger, am and pm respectively. Some devices have a little more utilities, such as auto-complete using tabs, grep, find and much more, others don't. To go around this issue, you can do a couple of things:

  • Use the command line of your machine, and do the heavy lifting on the host. e.g. adb shell ls /sdcard | grep -i dcim. Here the grepping is done in the host's shell, on data from the adb command.
  • Compile a special binary named busybox and push the compiled binary into your device. This binary provides replacements for most of the utilities you usually find in GNU fileutils, shellutils, etc.
    The utilities in BusyBox generally have fewer options than their full-featured GNU cousins; however, the options that are included provide the expected functionality and behave very much like their GNU counterparts.

install - Exactly what you expect

You can also install apps through ADB. One option is to push files into the device and then use the pm (package manager) command to install them.
Another option is to use adb install <filepath on host> to install your favorite application directly from the host's shell.

forward - Advanced use cases

Now for the real cool ideas. Because Android is really just a regular linux machine, you can even run servers from it and access them from the internet. For that, write your binary (look on the next docs), push it into your device, chmod it, and run it.
This is great except you can't really access it. This is because you need to explicitly forward the ports. This is done using adb forward tcp:<host-port> tcp:<android-port>, so we can chit-chat.

This command can be used to run gdb-server, frida and so much more, as you will see in the upcoming posts.

Android Apps Part 1: Egg? Chicken? Zygote!

26/07/2023

TL;DR

Android development has evolved from complex system-level programming to a highly abstracted environment, allowing developers to focus on app logic. This post will dive into Android's app lifecycle, the compilation process, and the Zygote process, explaining how apps are launched and managed within the Android ecosystem.

We have come a long way from the good old days, where most of the written programs were written in 'c', waiting for us to pwn. Today, when smartphones are widespread, software running on them is way more sophisticated.

Android is an example of smartphones operating system, in its vision, delegating software development. Letting developers write their logic and take care of the insides. This way, a lot more applications could be developed as developers focus more on business logic instead of bootstrapping and resource management. On the other hand, more dedicated OS developers are making it much more stable. This made android, together with the vendor's HAL, a very abstract OS, that developing on it became almost trivial for most use cases.

But some developers insist on doing things their way, trying to be quick or sometimes just not paying attention to details, making the code "dirty". This is a great opportunity for us as researchers to try our luck and find gems in the dirt.

This series of posts will cover from the basics of applications, i.e. the life-cycle, through researching, e.g Smali, to hardcore interesting tools such as Frida, Brida and finally - XPosed.

Congratulations, It's a droid!

Android application are usually packed in an APK file, which is just a ZIP file that holds all the goods needed for incorporating an app into the android ecosystem (the newer *.apex files won't be discussed) As said, Android is a vastly abstract OS. this is much needed for easy development of apps on it.

In order to compile an app a Java or Kotlin file needs to have a class named MainActivity in it. This is the entry point when a icon is pressed and the Android Binder (the IPC of Android) calls Android's internal method startActivity using an intent. MainActivity must comply to the Activity interface. There, the developer must implement a couple of methods, one of them is onCreate. Other than MainActivity, an Android app has to have a manifest file called AndroidManifest.xml, in it MainActivity must be declared.

This is the only source code needed for a basic Android app. Running them through the gradle is an option, but we hate living an easy life.

Compilation Process

Every Android app has at least one classes.dex file in it. DEX is short for Dalvik executable. Dalvik is the former java virtual machine of android (today it Android Runtime -ART). But every DEX file is Java. So it needs to turn into Java bytecode using the Java compiler - javac:

$ javac -d ../bin -sourcepath . com/example/myapp/MainActivity.java

Next, all the class files are put together inside a jar file. using the 'jar' command:

$ jar cf bin/myapp.apk -C bin/ META-INF/ AndroidManifest.xml classes.dex

Then convert the JAR file into a DEX file using dx:

$ dx --dex --output=../bin/classes.dex ../bin/

Now, we create the APK structure:

$ jar cf bin/myapp.apk -C bin/ META-INF/ AndroidManifest.xml classes.dex

Finally, we sign the APK.

$ jarsigner -keystore my-release-key.keystore ../bin/myapp.apk alias_name

the entire process:

.java -> .class -> .dex -> .jar -> unsigned .apk -> signed .apk

But why sign it? We need to sign an app to make sure that is the actual app from the same creator in case of an update. So the developer must sign the release candidate using the same private key, that private key is stored inside the keystore. to create your own keystore (and keep it secret), use the following: $ keytool -genkey -v -keystore <keystore_file> -alias <your_alias_name> -keyalg RSA -keysize 2048 -validity 10000 then fill out the fields. and finally to sign it, use:

$ jarsigner -keystore <keystore_file> <app_to_sign>.apk <alias>

Ok, so now we have a signed APK file ready to be installed in our system. We created a Google Developer account, uploaded the app and waited for it to be scanned by the Google Play bouncer. Surely it will tell you that you've forgot to add a launcher icon or something else wrong with the permissions you asked for. So in the scope of this post, we are lazy and don't want to get into Google Play and comply to its developer policy (yet). We upload it manually, don't worry, how to do so will be discussed in future posts.

The first thing that happens is that APK is checked by Android Application Manager - am for the signer. The APK is deflated and all the files inside it are being checked against their digest compared to a file in META-INF called MANIFEST.MF. Yf they are correct then we are a go for installation.

First, lets take a look at a regular apk content:

    META-INF/MANIFEST.MF
    META-INF/CERT.SF
    META-INF/CERT.RSA
    AndroidManifest.xml
    classes.dex
    res/*
    assets/*
    lib/*
  • MANIFEST.MF stores the hash digests of all files in the APK to prevent their integrity from being tempered with. jarsigner enumerate all the files, calculate their SHA1 digest, signs them using RSA, and finally convert all digests to base64-encoded codes.
  • CERT.SF stores the base64-encoded codes of MANIFEST.MF's digest, and the message digests of all the digests stored in MANIFEST.MF. Curiously, although it has a suffix .SF (Signature File), but it has nothing to do with the signature, and doesn't use any private key or certificate.
  • CERT.RSA stores the digital signature of CERT.SF, and its signing certificate.
  • res/* are resources files such as pictures, icons, string and more.
  • assets is not regular binaries, like fonts, JavaScript files etc.
  • lib directory is all the shared objects files (.so) decided to the architecture of the device.
  • AndroidManifest.xml and classes.dex we already discussed.

When an application is installed, the APK is saved as /data/app/com/example/myapp/base.apk. in the same directory there are also lib and special files - odex/vdex/art/oat. These files are the compiled classes.dex file. Now hold it for a sec..another history class!

Dalvik is a JIT (Just in time) compilation based engine. With the Dalvik JIT compiler, each time when the app is run, it dynamically translates a part of the Dalvik bytecode inside the classes.dex file into machine code. As the execution progresses, more bytecode is compiled and cached. Since JIT compiles only a part of the code, it has a smaller memory footprint and uses less physical space on the device. There were drawbacks to use Dalvik hence from Android 4.4 (KitKat) ART was introduced as a runtime and from Android 5.0 (Lollipop) it has completely replaced Dalvik. ART is equipped with an Ahead-of-Time compiler. During the app's installation phase, it statically translates the DEX bytecode into machine code and stores in the device's storage. This is a one-time event which happens when the app is installed on the device. Android 7.0 adds a just-in-time (JIT) compiler with code profiling to Android runtime (ART) that constantly improves the performance of Android apps as they run. There won't be any compilation during install, and applications can be started right away, the bytecode being interpreted. There is a new, faster interpreter in ART and it is accompanied by a new JIT, but the JIT information is not persisted. Instead, the code is profiled during execution and the resulted data is saved. So the entire life-cycle of the code is:

.java -> .class -> .dex -> .jar -> unsigned .apk -> signed .apk -> -> deflated .apk -> .oat/.odex/.vdex

to conclude, the files of our app are in the following locations:

DIRECTORY                                                    DESCRIPTION / API
=====================================================================================
APP CODE
========
/data/app/*                                             (user apps installation directory)
/data/app/*/base.apk                                    (original .apk file)
/data/app/*/lib//*.so                             (shared libraries)
/data/app/*/oat//base.[art|odex|vdex]             (compiled executable code)
/data/dalvik-cache//*.[art|dex|oat|vdex]               (compiled executable code, only for system apps)
/data/misc/profiles/cur///primary.prof         (ART profile)
/data/misc/profiles/ref//primary.prof                   (ART profile)

INTERNAL STORAGE
================
/data/user[_de]//                              getDataDir
/data/user[_de]///files                        getFilesDir
/data/user[_de]///[code_]cache                 getCacheDir or getCodeCacheDir
/data/user[_de]///databases                    getDatabasePath
/data/user[_de]///no_backup                    getNoBackupFilesDir
/data/user[_de]///shared_prefs                 getSharedPreferences

EXTERNAL STORAGE
================
/storage/emulated/obb//*.obb                            (shared by multi-users, exposed in following view)
/storage/emulated//Android/obb//*..obb    getObbDirs
/storage/emulated//Android/media/              getExternalMediaDirs
/storage/emulated//Android/data//             
/storage/emulated//Android/data//files         getExternalFilesDirs
/storage/emulated//Android/data//[code_]cache  getExternalCacheDirs

Wow, so much has passed and we didn't even ran our app! It is in our device, with a special 'stopped=true' flag, just waiting to be opened (checkout more app info using

    $ adb shell dumpsys package com.example.myapp
) Note that it can register to many intents but cannot wake itself up. it needs to wait for the user to press the icon. Now we are going to change that flag!

Wait, wait, wait. one last thing. Android is running on the Linux kernel. when it boots, an init process is spawned. and every process is a fork of it. similarly, every app is forked from a special process called zygote (app_process for each ABI). It preloads all common classes used by Android application framework and various apps installed on a system. And is the first JVM process in android. The zygote then forks to start a well managed process called system server. System server starts all core platform services e.g activity manager service and hardware services in its own context. and for visualizing:

+----------+ +-----+        +----------------------+
| boot.oat | | OAT +<-------+         apk          |
+----------+ +--+--+        +-----+----------+-----+
      |         |           | dex | manifest | lib |
 +----+---------+           +-----+----------+--+--+
 | +---------------------+                      |
 | |       zygote        |   +---------------+  |
 | | +--------+--------+ |   | system server |  |
 | | | libArt |  libc  | +<--------+-+-+---+-+  |
 | | +-----------------+ |   | gps |.|.|.|.|.|  |
 | | | class  | linker | |   +---------------+  |
 +-->+ loader |        +------------------------+
   | +-----------------+ |
   +---------------------+

Now, we got ourselves a new process, at this point, the binder calls startActivity and our java code is finally running.

more on this in here.

More Posts

Coming soon...

Professional Experience

I’m excited to introduce myself as someone with strong experience in technical management, cybersecurity, and Eloctronics.
I’ve worked on a variety of projects that involve system design, team leadership, and hands-on work.
With close communication, I’m confident that my skills and problem-solving abilities in different domains will allow me to contribute meaningfully to your team.
Below you can find ways to reach out, looking forward to discussing how I can add value to your company.

Technologies

  • Programming: Python, C, C++, C#, Bash, Assembly (x86/x64, ARM), Web (JavaScript, HTML, CSS), Databases.
  • Networking & Protocols: Wireshark, Burp Suite, Wi-Fi, Bluetooth, CAN Bus, USB.
  • Embedded & OS Security: Android, Linux Internals, Firmware Analysis, EMMC & Flash, FPGA & VHDL.
  • Detection Engineering: Honeypots, Thresholds, DLP, Incident Response.
  • Secure Dev & DevOps: Docker, CI/CD Security, PT, Threat Modeling.
  • Electronics & Test Equipment: Multimeters, Analyzers (Logic, Spectrum, Network), Oscilloscopes, RF.
  • CAD & 3D Printing: PCB Design, FDM, OpenSCAD, SolidWorks

Working Experience

IoT Research Technical Leader — Sayfer.io (2023 – Current)

I am the IoT and advanced exploitation techniques knowledge source for a diverse and multidisciplinary company.

  • Conducting and managing in-depth penetration testing projects in various attack surfaces in all vectors to uncover, exploit, and report security weaknesses in proprietary systems to all levels of stakeholders, from C-level executives all the way to development teams.
  • Participating as a tech specialist in technical sales meetings to ensure long-term partnership and trust.

Embedded Systems Security Researcher — CyberToka Ltd. (2020-2022)

I brought unique expertise in electronic engineering to a strong automotive embedded research team.

  • Conducted RE and vulnerability research on vast attack surfaces, including all different layers of the network stack on various systems, leading to multiple critical findings.
  • Provided structured guidance in security-related subjects to cross-functional teams, ensuring self-sufficiency and long-term knowledge retention.

Software Engineer and OS Security Researcher — Prime Minister’s Office (2016-2019)

By being given the opportunity to learn a new field, I got to become a focal point in application security-related projects.

  • Conducted and managed Android applications vulnerability research and full-stack development projects, including exploit development incorporated with detection engineering mechanisms, while coping with modern OS security mitigations.
  • Mentored cybersecurity recruits, upskilling modern exploitation techniques and countermeasures.

Electronic Warfare R&D and Maintenance Team Leader — Air Force Base 108 (2013-2016)

I was promoted to team leader for a team in charge of some of the Israeli Air Force's crucial RF systems.

  • Led a team of hardware engineers, accountable for maintenance and development in the field of RF signal generators and high-power transmitters.
  • Encouraged professional growth by crafting tailor-made training programs and escorting trainees until completion.

Education

  • 2016-2020: B.Sc. Computer Science, The Academic College of Tel Aviv-Yafo, Tel Aviv-Yafo, Israel
  • 2011-2013: Electronics Practical Engineer, “Amal 1 Holtz”, Tel Aviv-Yafo, Israel

Languages: Hebrew (native), English (full professional proficiency)

Noteable personal project

Developing an integrated dockerized multi-server ecosystem using with both open-source and proprietary services to structure and organize daily workflow, potentially helping others with ADHD unlock their potential. Designed to foster focus, time management and productivity methodologies thorugh structure.