Saltar al contenido

¿Cómo puedo ver los datos ocultos de la aplicación en Google Drive?

Solución:

De hecho, Google no le permite acceder directamente a esta carpeta de datos de aplicaciones oculta.

Pero, si puede tener en sus manos el ID de cliente / secreto de cliente / firma digital de la aplicación que se usa para la autenticación contra los servidores de Google, entonces sí, básicamente puede emular la aplicación y acceder a los datos ocultos en su Google Drive usando la API de Drive. .

Cómo funciona en Android

Por lo general, cuando una aplicación de Android desea acceder a una API de Google (como Drive, Juegos o Inicio de sesión de Google, no todos son compatibles), se comunica con la biblioteca cliente de los servicios de Google Play, que a su vez obtiene un token de acceso de Google en nombre. de la aplicación. Este token de acceso luego se envía con cada solicitud a la API, para que Google sepa quién lo está usando y qué puede hacer con su cuenta (OAuth 2.0). Para obtener este token de acceso por primera vez, el servicio Google Play envía una solicitud HTTPS POST a android.clients.google.com/auth con estos campos (junto con otros detalles):

  • Token – un “token maestro” que identifica la cuenta de Google y básicamente permite el acceso completo a ella
  • app – el nombre del paquete de la aplicación, como com.whatsapp
  • client_sig – las aplicaciones firma digital (enviado como SHA1)
  • device – ID de Android del dispositivo
  • service – los alcances (permisos) que la aplicación quiere tener

Entonces, antes de que podamos comenzar a usar la API de Drive en el nombre de una aplicación específica, necesitamos saber su firma y el token maestro de nuestra cuenta. Afortunadamente, la firma se puede extraer fácilmente del .apk expediente:

shell> unzip whatsapp.apk META-INF/*
       Archive:  whatsapp.apk
          inflating: META-INF/MANIFEST.MF    
          inflating: META-INF/WHATSAPP.SF    
          inflating: META-INF/WHATSAPP.DSA
shell> cd META-INF
shell> keytool -printcert -file WHATSAPP.DSA   # can be CERT.RSA or similar
       .....
       Certificate fingerprints:
       SHA1: 38:A0:F7:D5:05:FE:18:FE:C6:4F:BF:34:3E:CA:AA:F3:10:DB:D7:99
       Signature algorithm name: SHA1withDSA
       Version: 3

Lo siguiente que necesitamos es el token maestro. Este token especial normalmente se recibe y se almacena en el dispositivo cuando se agrega una nueva cuenta de Google (por ejemplo, cuando se configura el teléfono por primera vez), al realizar una solicitud similar a la misma URL. La diferencia es que ahora la aplicación que solicita permisos es la propia aplicación de servicios de Play (com.google.android.gms), y Google también recibe información adicional Email y Passwd parámetros para iniciar sesión. Si la solicitud es exitosa, recuperaremos nuestro token maestro, que luego podría agregarse a la solicitud de la aplicación del usuario.

Puede leer esta publicación de blog para obtener información más detallada sobre el proceso de autenticación.

Poniendolo todo junto

Ahora, podemos escribir un código para la autenticación usando estas dos solicitudes HTTP directamente: un código que puede explorar los archivos de cualquier aplicación con cualquier cuenta de Google. Simplemente elija su lenguaje de programación y biblioteca cliente favoritos. Lo encontré más fácil con PHP:

require __DIR__ . '/vendor/autoload.php'; // Google Drive API

// HTTPS Authentication
$masterToken = getMasterTokenForAccount("[email protected]", "your_password");
$appSignature = '38a0f7d505fe18fec64fbf343ecaaaf310dbd799';
$appID = 'com.whatsapp';
$accessToken = getGoogleDriveAccessToken($masterToken, $appID, $appSignature);

if ($accessToken === false) return;

// Initializing the Google Drive Client
$client = new Google_Client();
$client->setAccessToken($accessToken);
$client->addScope(Google_Service_Drive::DRIVE_APPDATA);
$client->addScope(Google_Service_Drive::DRIVE_FILE);
$client->setClientId("");    // client id and client secret can be left blank
$client->setClientSecret(""); // because we're faking an android client
$service = new Google_Service_Drive($client);

// Print the names and IDs for up to 10 files.
$optParams = array(
    'spaces' => 'appDataFolder',
    'fields' => 'nextPageToken, files(id, name)',
    'pageSize' => 10
);
$results = $service->files->listFiles($optParams);

if (count($results->getFiles()) == 0) 

    print "No files found.n";
 
else 

    print "Files:n";
    foreach ($results->getFiles() as $file) 
    
        print $file->getName() . " (" . $file->getId() . ")n";
    


/*
$fileId = '1kTFG5TmgIGTPJuVynWfhkXxLPgz32QnPJCe5jxL8dTn0';
$content = $service->files->get($fileId, array('alt' => 'media' ));
echo var_dump($content);
*/

function getGoogleDriveAccessToken($masterToken, $appIdentifier, $appSignature)

    if ($masterToken === false) return false;

    $url = 'https://android.clients.google.com/auth';
    $deviceID = '0000000000000000';
    $requestedService = 'oauth2:https://www.googleapis.com/auth/drive.appdata https://www.googleapis.com/auth/drive.file';
    $data = array('Token' => $masterToken, 'app' => $appIdentifier, 'client_sig' => $appSignature, 'device' => $deviceID, 'google_play_services_version' => '8703000', 'service' => $requestedService, 'has_permission' => '1');

    $options = array(
        'http' => array(
            'header' => "Content-type: application/x-www-form-urlencodedrnConnection: close",
            'method' => 'POST',
            'content' => http_build_query($data),
            'ignore_errors' => TRUE,
            'protocol_version'=>'1.1',
             //'proxy' => 'tcp://127.0.0.1:8080', // optional proxy for debugging
             //'request_fulluri' => true
        )
    );
    $context = stream_context_create($options);
    $result = file_get_contents($url, false, $context);
    if (strpos($http_response_header[0], '200 OK') === false) 
     
        /* Handle error */
        print 'An error occured while requesting an access token: ' . $result . "rn";
        return false;
    

    $startsAt = strpos($result, "Auth=") + strlen("Auth=");
    $endsAt = strpos($result, "n", $startsAt);
    $accessToken = substr($result, $startsAt, $endsAt - $startsAt);

    return ""access_token":"" . $accessToken . "", "refresh_token":"TOKEN", "token_type":"Bearer", "expires_in":360000, "id_token":"TOKEN", "created":" . time() . "";


function getMasterTokenForAccount($email, $password) 

    $url = 'https://android.clients.google.com/auth';
    $deviceID = '0000000000000000';
    $data = array('Email' => $email, 'Passwd' => $password, 'app' => 'com.google.android.gms', 'client_sig' => '38918a453d07199354f8b19af05ec6562ced5788', 'parentAndroidId' => $deviceID);

    $options = array(
        'http' => array(
            'header' => "Content-type: application/x-www-form-urlencodedrnConnection: close",
            'method' => 'POST',
            'content' => http_build_query($data),
            'ignore_errors' => TRUE,
            'protocol_version'=>'1.1',
             //'proxy' => 'tcp://127.0.0.1:8080', // optional proxy for debugging
             //'request_fulluri' => true
        )
    );
    $context = stream_context_create($options);
    $result = file_get_contents($url, false, $context);
    if (strpos($http_response_header[0], '200 OK') === false) 
     
        /* Handle error */
        print 'An error occured while trying to log in: ' . $result . "rn";
        return false;
    

    $startsAt = strpos($result, "Token=") + strlen("Token=");
    $endsAt = strpos($result, "n", $startsAt);
    $token = substr($result, $startsAt, $endsAt - $startsAt);

    return $token;

Y finalmente, los resultados:

Files:
gdrive_file_map (1d9QxgC3p4PTXRm_fkAY0OOuTGAckykmDfFls5bAyE1rp)
Databases/msgstore.db.crypt9    (1kTFG5TmgIGTPJuVynWfhkXxLPgz32QnPJCe5jxL8dTn0)
16467702039-invisible (1yHFaxfmuB5xRQHLyRfKlUCVZDkgT1zkcbNWoOuyv1WAR)
Done.

NOTA: Esta es una solución pirata no oficial, por lo que podría tener algunos problemas. Por ejemplo, el token de acceso está activo solo durante una hora, después de lo cual no se actualizará automáticamente.

El usuario no puede acceder directamente a los datos de las carpetas ocultas de la aplicación, solo la aplicación puede acceder a ellos. Está diseñado para la configuración u otros datos ocultos que el usuario no debe manipular directamente. (El usuario puede optar por eliminar los datos para liberar el espacio utilizado).

La única forma en que el usuario puede acceder a él es a través de alguna funcionalidad expuesta por la aplicación específica.

Un ejemplo práctico a septiembre de 2020

Nota: esto es en realidad una adición a la respuesta de Tomer.

Las cosas cambiaron desde que se publicó la respuesta original de Tomer. Actualmente, para obtener el token maestro y evitar el Error=BadAuthentication, necesitas dos cosas:

  • Reemplazar Passwd campo con EncryptedPasswd y cifrar su valor mediante RSA con el público de Google key (la técnica exacta fue invertida por un tipo) – esto se puede hacer usando phpseclib.
  • Realice una conexión HTTPS al servidor de Google con las mismas opciones de SSL / TLS que en uno de los sistemas Android compatibles. Esto incluye las versiones de TLS y la lista exacta de cifrados admitidos en el orden correcto. Si cambia el orden o agrega / elimina cifrados, obtendrá Error=BadAuthentication. Me tomó un día entero resolver esto … Afortunadamente, PHP> = 7.2 viene con openssl-1.1.1 que tiene todos los cifrados necesarios para emular el cliente Android 10.

Así que aquí está reescrito getMasterTokenForAccount() función que establece los cifrados y utiliza EncryptedPasswd en lugar de simple Passwd. Y abajo esta encryptPasswordWithGoogleKey() implementación que hace el cifrado.

phpseclib es necesario y se puede instalar con composer: composer require phpseclib/phpseclib:~2.0

function getMasterTokenForAccount($email, $password) 

    $url = 'https://android.clients.google.com/auth';
    $deviceID = '0000000000000000';
    $data = array('Email' => $email, 'EncryptedPasswd' => encryptPasswordWithGoogleKey($email, $password), 'app' => 'com.google.android.gms', 'client_sig' => '38918a453d07199354f8b19af05ec6562ced5788', 'parentAndroidId' => $deviceID);

    $options = array(
        'ssl' => array(
            'ciphers' => 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:ECDH+AESGCM:DH+AESGCM:ECDH+AES:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!eNULL:!MD5:!DSS'),
        'http' => array(
            'header' => "Content-type: application/x-www-form-urlencodedrnConnection: close",
            'method' => 'POST',
            'content' => http_build_query($data),
            'ignore_errors' => TRUE,
            'protocol_version'=>'1.1',
             //'proxy' => 'tcp://127.0.0.1:8080', // optional proxy for debugging
             //'request_fulluri' => true
        )
    );
    $context = stream_context_create($options);
    $result = file_get_contents($url, false, $context);
    if (strpos($http_response_header[0], '200 OK') === false) 
     
        /* Handle error */
        print 'An error occured while trying to log in: ' . $result . "rn";
        return false;
    

    $startsAt = strpos($result, "Token=") + strlen("Token=");
    $endsAt = strpos($result, "n", $startsAt);
    $token = substr($result, $startsAt, $endsAt - $startsAt);

    return $token;


function encryptPasswordWithGoogleKey($email, $password)

    define('GOOGLE_KEY_B64', 'AAAAgMom/1a/v0lblO2Ubrt60J2gcuXSljGFQXgcyZWveWLEwo6prwgi3iJIZdodyhKZQrNWp5nKJ3srRXcUW+F1BD3baEVGcmEgqaLZUNBjm057pKRI16kB0YppeGx5qIQ5QjKzsR8ETQbKLNWgRY0QRNVz34kMJR3P/LgHax/6rmf5AAAAAwEAAQ==');

    $google_key_bin = base64_decode(GOOGLE_KEY_B64);
    $modulus_len = unpack('Nl', $google_key_bin)['l'];
    $modulus_bin = substr($google_key_bin, 4, $modulus_len);
    $exponent_len = unpack('Nl', substr($google_key_bin, 4 + $modulus_len, 4))['l'];
    $exponent_bin = substr($google_key_bin, 4 + $modulus_len + 4, $exponent_len);
    $modulus = new phpseclibMathBigInteger($modulus_bin, 256);
    $exponent = new phpseclibMathBigInteger($exponent_bin, 256);

    $rsa = new phpseclibCryptRSA();
    $rsa->loadKey(['n' => $modulus, 'e' => $exponent], phpseclibCryptRSA::PUBLIC_FORMAT_RAW);
    $rsa->setEncryptionMode(phpseclibCryptRSA::ENCRYPTION_OAEP);
    $rsa->setHash('sha1');
    $rsa->setMGFHash('sha1');
    $encrypted = $rsa->encrypt("$emailx00$password");

    $hash = substr(sha1($google_key_bin, true), 0, 4);
    return strtr(base64_encode("x00$hash$encrypted"), '+/', '-_');

Si te apasiona la informática, tienes la opción de dejar un escrito acerca de qué te ha parecido esta división.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *