Last active
July 2, 2025 09:39
-
-
Save alex-spataru/b187bdc3d987a0fcb1cae4e9b17c0e9e to your computer and use it in GitHub Desktop.
Brute-force OEM code/password guesser for Android phones
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# Based on https://github.com/haexhub/huaweiBootloaderHack | |
# | |
# Usage: | |
# python android-bootloader-unlock.py <YOUR_IMEI_CODE> | |
# | |
# Please enable USB debugging on your phone before using this | |
# tool, hope it helps you :) | |
# | |
import sys | |
import json | |
import math | |
import subprocess | |
def get_attempts(): | |
''' | |
Reads the list of failed passwords and returns them as a set. | |
In case the attempts.json file does not exist (or is invalid), | |
this function will return an empty set. | |
''' | |
try: | |
with open('attempts.json', 'r') as file: | |
attempts = json.load(file) | |
if type(attempts) == list: | |
return set(attempts) | |
else: | |
return set([]) | |
except: | |
return set([]) | |
def save_attempts(attempts = []): | |
''' | |
Writes the current failed password attempts to an external file, | |
which is later used to continue trying even more passwords... | |
''' | |
with open('attempts.json', 'w') as file: | |
json.dump(attempts, file) | |
def string_to_int_array(n): | |
''' | |
Converts an array of characters to an array of integers | |
''' | |
return [int(d) for d in str(n)] | |
def luhn_checksum(imei): | |
''' | |
Obtains the checksum of the IMEI string using Luhn's algorithm | |
''' | |
digits = string_to_int_array(imei) | |
odd_digits = digits[-1::-2] | |
even_digits = digits[-2::-2] | |
checksum = 0 | |
checksum = sum(odd_digits) | |
for digit in even_digits: | |
checksum += sum(string_to_int_array(digit * 2)) | |
return checksum % 10 | |
def unlock_bootloader(imei, checksum, failed_attempts = set([])): | |
''' | |
Generates new OEM codes/passwords and tries running "fastboot oem unlock" | |
until it succeeds. | |
@note The function automatically reboots the Android device every 4 | |
failed attempts in order to avoid a lock-up. | |
''' | |
# Initialize parameters | |
unlocked = False | |
attempt_count = 0 | |
oem_code = 1000000000000000 | |
# Execute loop until we get it right | |
while not unlocked: | |
# Increment attempt count | |
attempt_count += 1 | |
# Generate new OEM code/password if needed | |
while oem_code in failed_attempts: | |
oem_code += int(checksum + math.sqrt(imei) * 1024) | |
# Print status | |
print(f'Shot {len(failed_attempts) + 1} with OEM code/password {oem_code}...') | |
# Try to unlock the bootloader | |
answer = subprocess.run( | |
['fastboot', 'oem', 'unlock', str(oem_code)], | |
stdout = subprocess.DEVNULL, | |
stderr = subprocess.DEVNULL | |
) | |
# Check if bootloader unlock succeeded | |
if answer.returncode == 0: | |
unlocked = True | |
return oem_code | |
# Bootloader unlock failed, register failed attempt code | |
else: | |
failed_attempts.add(oem_code) | |
# Reboot device every 4 failed attempts | |
if attempt_count >= 4: | |
# Save current attempts to JSON file | |
attempt_count = 0 | |
save_attempts(list(failed_attempts)) | |
# Reboot device | |
print('Restarting device...\n') | |
subprocess.run( | |
['fastboot', 'reboot', 'bootloader'], | |
stdout = subprocess.DEVNULL, | |
stderr = subprocess.DEVNULL | |
) | |
# Generate new OEM password attempt | |
oem_code += int(checksum + math.sqrt(imei) * 1024) | |
if __name__ == '__main__': | |
''' | |
Main entry point function of the application | |
''' | |
# Get IMEI code from the user | |
args = sys.argv | |
if len(sys.argv) > 1: | |
imei = int(sys.argv[1]) | |
else: | |
imei = int(input('Please type your IMEI code: ')) | |
# Get Luhn checksum of the IMEI | |
checksum = luhn_checksum(imei) | |
# Print IMEI & checksum | |
print(f'Using IMEI: {imei}') | |
print(f'IMEI checksum (Luhn): {checksum}') | |
# Reboot device into fastboot | |
print('Rebooting device...') | |
subprocess.run( | |
['adb', 'reboot', 'bootloader'], | |
stdout = subprocess.DEVNULL, | |
stderr = subprocess.DEVNULL | |
) | |
# Wait for user confirmation | |
input('Press any key when your device is in fastboot mode...\n') | |
# Read failed attempts & continue brute force attack | |
failed_attempts = get_attempts() | |
oem_code = unlock_bootloader(imei, checksum, failed_attempts) | |
# Validate unlocked status & reboot device | |
subprocess.run(['fastboot', 'getvar', 'unlocked']) | |
subprocess.run(['fastboot', 'reboot']) | |
# Print obtained key | |
print('\n\n') | |
print(f'Device unlocked! OEM code/password: {oem_code}') | |
sys.exit() |
Hey there, All !
I'm trying to run the program with the serial number instead of the IMEI right now. I'm trying to bruteforce a Nokia T20, which also does not have any SIM card, and therefore, no IMEI... I'm hoping that giving it enough time, I'll be able to unlock it. In the meantime, How did you do it on your side, @Tomblarom ? Could we get an insight, @alex-spataru ?
Hi all !
I was not able to unlock my Nokia T20 by bruteforcing using this code. I kept it running for about 40 hours (which is not much) but due to an electricity shortage, my PC turned off. I will try other unlocking methods on the wiki page (https://wiki.postmarketos.org/wiki/Unlocking_Bootloaders) before trying to (politely) ask Nokia if necessary.
Have a good one !
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I have a Zebra TC510K, with no SIM, so there is no IMEI. Only a serial-number with 14 digits. Which value should I use for IMEI then?