An Android app login page considered my email address invalid because it limited domain extensions up to four characters — mine has five (.email
). Instead of creating a new email, I patched the APK by modifying the validation regex. Here’s how I did it.
My email address is a bit different from most people. It doesn’t end with .com
, .org
or .fr
, but .email
. Why? Because I always feel the need to play the digital game in hard mode: using Linux, self-hosting my email server, even using an open-source self-hosted Netflix-like streaming service. I can’t help it. Usually it’s manageable, but sometimes, it requires a bit of work.
I recently subscribed to a service, and once on the login screen of the Android app I realized: my email address I used to create my account was now considered invalid. I quickly understood that the app enables the “log in” button only when the email address domain extension contains between two and four characters. .com
? 3, fine. .cool
? 4, okay. .email
? 5, “wrong”.
It’s quite frustrating that email validation is still sometimes so arbitrary. Why only four characters? Why not three, or five, or even better, check against a list of actual domain extensions? I’m sure there are libraries for that. I know, I’m nitpicking, because most people have emails from Gmail, Microsoft or Outlook anyway, and only nerds like me always end up complicating things for “fun”.
So I contacted the tech support (btw sending an email with my “invalid” address…), politely pointing out the issue. Their solution wasn’t appealing: “Ask the marketing team to delete your account and to create a new one with a valid email.” Create a whole new email just for one app? I had a more entertaining idea.
I suspected the email validation system to be purely front-end — probably a regex that checks if the email domain extension is 2, 3, or 4 characters — and that the back-end would accept my email.
What if I perform a man-in-the-middle attack by intercepting the request sent by my phone, change the email, and send it to the server? Long story short: it doesn’t work that easily, because altering an HTTPS request is not that simple to do — thankfully — clients and servers use a mutually trusted certificate authority to encrypt requests.
I tried a different approach: what if I manage to access the source code of the app, find where the email validation is performed, and change it?
Here is the idea:
Spoiler alert: it worked! Here is how to do it.
For example on Fedora:
sudo dnf install android-tools
Download or grab the link for the latest version here: https://developer.android.com/studio.
Download only the command line tools, not the Android Studio app.
# Download it
wget https://dl.google.com/android/repository/commandlinetools-linux-13114758_latest.zip
# Unzip it to the home directory
unzip commandlinetools-linux-*.zip -d $HOME/Android/Sdk/cmdline-tools
# Install the SDK Platform Tools
yes | $HOME/Android/Sdk/cmdline-tools/cmdline-tools/bin/sdkmanager --sdk_root=$HOME/Android/Sdk "platform-tools" "build-tools;34.0.0"
# Put Android paths to bashrc
echo 'export ANDROID_HOME=$HOME/Android/Sdk' >> ~/.bashrc
echo 'export PATH=$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/build-tools/34.0.0' >> ~/.bashrc
source ~/.bashrc
apktool
:Follow instructions here: https://apktool.org/docs/install/.
We will assume from now on that the Android app package name is com.my.app
.
# Check that the phone is recognized by ADB
adb devices
# Create a working directory
mkdir patchedAPK
cd patchedAPK
# Find where the APK files are installed on the phone
adb shell pm path com.my.app
# Extract all files
for path in $(adb shell pm path com.my.app | cut -d: -f2);
do adb pull "$path";
done
# Decompile the APK
apktool d base.apk -o src
We now have some sort of source code. Actually it’s not Java or Kotlin, but Smali, which is the human-readable assembly language for the instruction set that Android really executes. It means we don’t have exactly what the devs coded, but it’s enough to explore how the app is made, change it, and recompile it afterwards.
Let’s find the problematic regex limiting domain extensions. I supposed it’s something that checks if the end of a string contains between two and four letters, which is what I noticed when trying different emails on the login screen. There are many ways to write a regex for that, but what seems certain is “between 2 and 4”, which translate in regex to {2,4}
. Let’s search the source code for that:
grep -R --line-number '{2,4}' src | head
It should return every file with line numbers that contain those characters. In my case I was lucky, 5 unreadable binary files, and only one file with a ridiculously long regex.
Change {2,4}
to {2,5}
to fix the validation issue. It’s that simple.
We will now rebuild and sign the app. For a real-life production app, it would require more work, like Google Play Protect and Play Store compatibility, align the files (using zipalign
) and use a proper signing key. Here I only do the bare minimum for the app to run on my device.
apktool b src -o unsigned_base.apk
keytool -genkeypair -alias mykey -keyalg RSA -keysize 2048 -validity 10000 -keystore mydebug.keystore -storepass changeit -keypass changeit -dname "CN=PixelTest,O=Dev,C=CH"
for f in *.apk;
do apksigner sign --ks mydebug.keystore --ks-pass pass:changeit --key-pass pass:changeit --ks-key-alias mykey --out signed_"$f" "$f"
done
# Remove the original app
adb uninstall com.my.app
# Install the patched version
adb install signed_base.apk
And voila! Now the app accepts email domain extensions with up to 5 characters and I can log in.
Because the signing key is different than the original one, automatic app updates are broken. So every new version requires repeating the patch, but it’s definitely more enjoyable to me than creating a new email address. Told you, I can’t help it…
Happy patching!