问题描述
我有一个 Android 应用程序将我的笔记存储在隐藏的应用程序数据中.我想导出我的笔记,所以问题很简单:
I have an Android app that stores my notes in hidden app data. I want to export my notes so the question is simple:
如何访问特定应用的 Google 云端硬盘中隐藏的应用数据?
How can I access the hidden app data in Google Drive for a specific app?
推荐答案
确实,Google 不允许你直接访问这个隐藏的 app-data 文件夹.
Indeed, Google does not let you access this hidden app-data folder directly.
但是,如果您能够获得用于针对 Google 服务器进行身份验证的应用程序的客户端 ID/客户端密码/数字签名 - 那么是的,您基本上可以模拟该应用程序并使用以下方法访问您的 Google 云端硬盘中的隐藏数据Drive API.
But, if you can get your hands on the app's client ID/client secret/digital signature that is used for authentication against Google's servers - then yes, you can basically emulate the app and access the hidden data in your Google Drive using the Drive API.
通常,当 android 应用程序想要访问 Google API(例如 Drive、游戏或 Google 登录 - 并非全部支持)它与 Google Play 通信services 客户端库,它反过来代表应用程序从 Google 获取访问令牌.然后这个访问令牌随每个请求发送到 API,以便 Google 知道谁在使用它以及允许他对您的帐户做什么 (OAuth 2.0).为了首次获取此访问令牌,Google Play 服务向 android.clients.google.com/auth
发送一个 HTTPS POST 请求,其中包含以下字段(以及其他详细信息):
Usually, when an android application wants to access a Google API (such as Drive, Games or Google Sign-In - not all are supported) it communicates with the Google Play services client library, which in turn obtains an access token from Google on behalf of the app. This access token is then sent with each request to the API, so that Google knows who is using it and what he is allowed to do with your account (OAuth 2.0). In order to get this access token for the first time, the Google Play service sends an HTTPS POST request to android.clients.google.com/auth
with these fields (along with other details):
Token
- 一种主令牌",用于识别 Google 帐户并基本上允许对其进行完全访问app
- 应用包名,如com.whatsapp
client_sig
- 应用程序的数字签名(以 SHA1 形式发送)device
- 设备的 Android身份证service
- 范围(权限)应用想要拥有的
Token
- a "master token" which identifies the Google account and basically allows full access to itapp
- the application package name, such ascom.whatsapp
client_sig
- the application's digital signature (sent as SHA1)device
- the device's Android IDservice
- the scopes (permissions) that the app wants to have
因此,在我们开始以特定应用的名义使用 Drive API 之前,我们需要知道它的签名和我们帐户的主令牌.幸运的是,可以从 .apk
文件中轻松提取签名:
So before we can start using the Drive API in the name of a specific app, we need to know its signature and our account's master token. Fortunately, the signature can be easily extracted from the .apk
file:
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
接下来我们需要的是主令牌.当添加新的 google 帐户时(例如,首次设置手机时),通常会通过向相同的 URL 发出类似的请求来接收此特殊令牌并将其存储在设备上.不同之处在于,现在请求权限的应用是 Play 服务应用本身 (com.google.android.gms
),而且 Google 还获得了额外的 Email
和 Passwd
登录参数.如果请求成功,我们将取回我们的主令牌,然后可以将其添加到用户的应用请求中.
The next thing we need is the master token. This special token is normally received and stored on the device when a new google account is added (for example, when first setting up the phone), by making a similar request to the same URL. The difference is that now the app that's asking for permissions is the Play services app itself (com.google.android.gms
), and Google is also given additional Email
and Passwd
parameters to log in with. If the request is successful, we will get back our master token, which could then be added to the user's app request.
您可以阅读这篇博文 有关身份验证过程的更多详细信息.
You can read this blogpost for more detailed information about the authentication process.
现在,我们可以直接使用这两个 HTTP 请求编写用于身份验证的代码 - 该代码可以使用任何 Google 帐户浏览任何应用程序的文件.只需选择您最喜欢的编程语言和客户端库.我发现使用 PHP 更容易:
Now, we can write a code for authentication using these two HTTP requests directly - a code that can browse any app's files with any Google account. Just choose your favorite programming language and client library. I found it easier with PHP:
require __DIR__ . '/vendor/autoload.php'; // Google Drive API
// HTTPS Authentication
$masterToken = getMasterTokenForAccount("your_username@gmail.com", "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.
";
}
else
{
print "Files:
";
foreach ($results->getFiles() as $file)
{
print $file->getName() . " (" . $file->getId() . ")
";
}
}
/*
$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-urlencoded
Connection: 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 . "
";
return false;
}
$startsAt = strpos($result, "Auth=") + strlen("Auth=");
$endsAt = strpos($result, "
", $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-urlencoded
Connection: 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 . "
";
return false;
}
$startsAt = strpos($result, "Token=") + strlen("Token=");
$endsAt = strpos($result, "
", $startsAt);
$token = substr($result, $startsAt, $endsAt - $startsAt);
return $token;
}
最后,结果——
Files:
gdrive_file_map (1d9QxgC3p4PTXRm_fkAY0OOuTGAckykmDfFls5bAyE1rp)
Databases/msgstore.db.crypt9 (1kTFG5TmgIGTPJuVynWfhkXxLPgz32QnPJCe5jxL8dTn0)
16467702039-invisible (1yHFaxfmuB5xRQHLyRfKlUCVZDkgT1zkcbNWoOuyv1WAR)
Done.
注意:这是一个非官方的、hacky 的解决方案,因此可能存在一些问题.例如,访问令牌仅存活一小时,之后将不会自动刷新.
NOTE: This is an unofficial, hacky solution, and so it might have a few problems. For example, the access token is alive only for one hour, after which it won't be refreshed automatically.
这篇关于如何在 Google Drive 中查看隐藏的应用数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!