Google云端硬盘文件 - 批量管理版本

时间:2015-02-16 01:22:02

标签: google-drive-api malware

我试图为此寻找解决方案,但无法做到这一点我认为我开始了一个新问题。

我想要做的是还原在特定日期编辑过的文件,以恢复到之前的版本。原因是我的计算机上发现了一个恶意软件,它已经加密了我的所有文件。由于我在桌面上运行了Google云端硬盘应用程序,恶意软件加密了这些文件,谷歌云端硬盘已将其同步到云端......所以目前我在Google云端硬盘中有一堆损坏的文件。

我可以手动进入每个文件"管理修订版"并将其恢复到工作版本,但由于文件数量的原因,这需要花费数小时的时间。

我已经完成了有关可能会被使用的Google Drive API的阅读,但我不是专家程序员,所以我想问一下是否有人有任何建议/知道最好的方法解决这个问题。

干杯, 阿尔弗雷德

4 个答案:

答案 0 :(得分:5)

这并不难。我假设文件在相关日期都被感染(因此被编辑),并且自那时起它们还没有被编辑过。如果是这种情况,那么您可以使用https://developers.google.com/drive/v2/reference/files/list https://developers.google.com/drive/web/search-parameters指定 modifiedDate 来查找这些文件。

然后,您可以检索修订Feed [{3}},我猜您在恶意软件日期之前正在寻找最新版本。

然后,您将获取内容并使用它来创建新文件,可能是在新的"未加密的"夹。或者,您可以尝试使用https://developers.google.com/drive/v2/reference/revisions/list删除加密的修订版,从而在其之前公开未加密的修订版。 NB我还没有尝试过这种方法。

如果您之前从未创建过云端硬盘应用,那么您可以获得很多乐趣。预算2-3天进行阅读并使其正常运行。作为骨架,您可能需要查看https://developers.google.com/drive/v2/reference/revisions/delete。我写这篇文章是为了快速解开所有用户的文件。这是一个不同的用例,但是将q=untrashed替换为q=modifiedDate=yyyy-mm-dd,并将untrash替换为get_revisions, delete top_revision,它距离您想要的距离不是一百万英里。

<强> NB。有点显而易见,但请确保在黑客修订之前备份所有内容。

答案 1 :(得分:3)

我们受到了cerber Ransomware的攻击,它感染了我们的Google驱动器。 我能够创建一个Python脚本,使用Google Drive API来恢复驱动器的转速。代码在这里有指导地再现。不要照原样使用它。 请注意代码顶部的免责声明。 希望它能让你开始完全恢复。

另请注意,要使用Google云端硬盘,您必须使用受感染的帐户登录,并转到https://console.developers.google.com生成client_secret.json文件。将文件放在与此脚本相同的目录中。

运行脚本: %python script_name.py

# This file CHANGES the drive. USE IT AT YOUR OWN RISK. I'M NOT RESPONSIBLE FOR ANY LOSE.
# It removes the revisions of cerber2 encrpted files
# It also renames the file back to what it was before the cerber2 infection
# You will probably have to run it multiple times because it only removes one rev each time.
# Good luck! Hope you get back to a state you were before the infection.
#

from __future__ import print_function
import httplib2
import os
import json

from apiclient import discovery
import oauth2client
from oauth2client import client
from oauth2client import tools

try:
    import argparse
    flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
    flags = None

# If modifying these scopes, delete your previously saved credentials
# at ~/.credentials/drive-python-quickstart.json
#SCOPES = 'https://www.googleapis.com/auth/drive.metadata.readonly'
SCOPES = 'https://www.googleapis.com/auth/drive'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Drive API Python Quickstart'


def get_credentials():
    """Gets valid user credentials from storage.

    If nothing has been stored, or if the stored credentials are invalid,
    the OAuth2 flow is completed to obtain the new credentials.

    Returns:
        Credentials, the obtained credential.
    """
    home_dir = os.path.expanduser('~')
    credential_dir = os.path.join(home_dir, '.credentials')
    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir)
    credential_path = os.path.join(credential_dir,
                                   'drive-python-quickstart.json')

    store = oauth2client.file.Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        if flags:
            credentials = tools.run_flow(flow, store, flags)
        else: # Needed only for compatibility with Python 2.6
            credentials = tools.run(flow, store)
        print('Storing credentials to ' + credential_path)
    return credentials

def main():
    """Shows basic usage of the Google Drive API.

    Creates a Google Drive API service object and outputs the names and IDs
    for up to 10 files.
    """
    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    service = discovery.build('drive', 'v3', http=http)

    results = service.files().list(
      corpus="domain",
      spaces="drive",
      pageSize=1000,
      orderBy="folder,modifiedTime desc,name",
      q= "name contains 'DECRYPT MY FILES'",
      fields="nextPageToken, files(id, name)"   
        ).execute()
    items = results.get('files', [])
    if not items:
        print('No files found.')
    else:
        print('Files:')
        for item in items:
                deleteFile = service.files().delete(fileId=item['id']).execute()
                print("file deleted " + item['name'])


    results = service.files().list(
      corpus="domain",
      spaces="drive",
      pageSize=1000,
      orderBy="folder,modifiedTime desc,name",
      #q="modifiedTime > '2016-09-04T12:00:00'",
      q= "name contains 'cerber2'",
      fields="nextPageToken, files(id, name)"   
        ).execute()
    items = results.get('files', [])
    if not items:
        print('No files found.')
    else:
        print('Files:')
        for item in items:
                details = service.files().get(
                    fileId=item['id'],
                    fields="lastModifyingUser,name").execute()
                #print(details)
                if(details['name'].endswith("cerber2")):
                    print('-------------------------File-------------------------------')
                    print(details)
                    revs = service.revisions().list(fileId=item['id'],fields="kind,revisions").execute()
                    allrev = revs['revisions']
                    print('==checking old revs==')
                    if(len(allrev) > 1):   
                        #print(json.dumps(allrev,indent=4))                    
                        lastRev = allrev[-1]
                        if(lastRev['originalFilename'].endswith("cerber2")):
                          try:
                            print("removing lastrev of file " + details['name'] + " " + lastRev['id'])   # delete the lastRev
                            revDel = service.revisions().delete(fileId=item['id'],revisionId=lastRev['id']).execute()
                            print(revDel)
                          except:
                            print("trying to remove earlier rev") # in case there are two revs with same time stamp, Google does not return the last rev as the last structure and the script fails
                            lastRev = allrev[-2]
                            if(lastRev['originalFilename'].endswith("cerber2")):
                              try:
                                print("removing lastrev of file " + details['name'] + " " + lastRev['id'])   # delete the lastRev
                                revDel = service.revisions().delete(fileId=item['id'],revisionId=lastRev['id']).execute()
                              except:
                                print("Please handle this file yourself. Unable to remove revisions " + details['name'])
                        else:
                          print("lastRev name does not seem infected " + lastRev['originalFilename'])
                          file = {'name': lastRev['originalFilename']}
                          # Rename the file.
                          updated_file = service.files().update(fileId=item['id'],body=file,fields='name').execute()
                          print("Renamed")
                    else:
                        lastRev = allrev[0]
                        print("rename " + details['name'] + " id=" + item['id'] + " to " + lastRev['originalFilename'])
                        file = {'name': lastRev['originalFilename']}
                        # Rename the file.
                        updated_file = service.files().update(fileId=item['id'],body=file,fields='name').execute()
                        print("Renamed")






if __name__ == '__main__':
    main()


#set PYTHONIOENCODING=utf-8  : You may need to set this in case file names have chars that cannot be printed on the console

答案 2 :(得分:0)

刚才找到了这个答案。 2016年9月基本上使用相同的想法,但在Java中。 完整博客:https://www.tmns.com/cerber3-ransomware-infected/

希望这有助于某人。勒索软件很糟糕。

源代码:

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.util.store.FileDataStoreFactory;

import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.*;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.model.Revision;
import com.google.api.services.drive.model.RevisionList;
import com.google.api.services.drive.Drive.Files;
import com.google.api.services.drive.model.File;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;

public class GDriveList {
    /** Application name. */
    private static final String APPLICATION_NAME = "Drive API Java Quickstart";

    /** Directory to store user credentials for this application. */
    private static final java.io.File DATA_STORE_DIR = new java.io.File(System.getProperty("user.home"),
            ".credentials/drive-java-quickstart");

    /** Global instance of the {@link FileDataStoreFactory}. */
    private static FileDataStoreFactory DATA_STORE_FACTORY;

    /** Global instance of the JSON factory. */
    private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();

    /** Global instance of the HTTP transport. */
    private static HttpTransport HTTP_TRANSPORT;

    /**
     * Global instance of the scopes required by this quickstart.
     *
     * If modifying these scopes, delete your previously saved credentials at
     * ~/.credentials/drive-java-quickstart
     */
    private static final List<String> SCOPES = Arrays.asList(DriveScopes.DRIVE);
    // Arrays.asList(DriveScopes.DRIVE_METADATA_READONLY);
    // The DRIVE_METADATA_READONLY scope did not give me enough "power". Switched to "DRIVE". 

    static {
        try {
            HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
            DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
        } catch (Throwable t) {
            t.printStackTrace();
            System.exit(1);
        }
    }

    /**
     * Creates an authorized Credential object.
     * 
     * @return an authorized Credential object.
     * @throws IOException
     */
    public static Credential authorize() throws IOException {
        // Load client secrets.
        InputStream in = GDriveList.class.getResourceAsStream("/client_secret.json");
        GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));

        // Build flow and trigger user authorization request.
        GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY,
                clientSecrets, SCOPES).setDataStoreFactory(DATA_STORE_FACTORY).setAccessType("offline").build();
        Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
        System.out.println("Credentials saved to " + DATA_STORE_DIR.getAbsolutePath());
        return credential;
    }

    /**
     * Build and return an authorized Drive client service.
     * 
     * @return an authorized Drive client service
     * @throws IOException
     */
    public static Drive getDriveService() throws IOException {
        Credential credential = authorize();
        return new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName(APPLICATION_NAME).build();
    }

    /**
     * Retrieve a list of revisions.
     *
     * @param service
     *            Drive API service instance.
     * @param fileId
     *            ID of the file to retrieve revisions for.
     * @return List of revisions.
     */
    private static List<Revision> retrieveRevisions(Drive service, String fileId) {
        try {
            RevisionList revisions = service.revisions().list(fileId).execute();
            return revisions.getRevisions();
        } catch (IOException e) {
            System.out.println("An error occurred: " + e);
        }
        return null;
    }

    /**
     * Rename a file.
     *
     * @param service
     *            Drive API service instance.
     * @param fileId
     *            ID of the file to rename.
     * @param newTitle
     *            New title for the file.
     * @return Updated file metadata if successful, {@code null} otherwise.
     */
    private static File renameFile(Drive service, String fileId, String newTitle) {
        try {
            File file = new File();
            file.setName(newTitle);

            // Rename the file.
            Files.Update updateRequest = service.files().update(fileId, file);
            updateRequest.setFields("name");

            File updatedFile = updateRequest.execute();
            return updatedFile;
        } catch (IOException e) {
            System.out.println("An error occurred: " + e);
            return null;
        }
    }

    public static void main(String[] args) throws IOException {
        // Build a new authorized API client service.
        Drive service = getDriveService();

        String fileId;
        String revisionId;
        String currentFilename = "";
        String originalFilename = "";
        String nonCerberName = "";
        String cerberFilename = "";
        boolean moreThanOneFilename = false;

        // Get files in batches of 500.
        FileList result = service.files().list()
                .setPageSize(500)
                .setQ("name contains '.cerber3'")
                .setSpaces("drive")
                .setFields("nextPageToken, files(id, name)").execute();
        List<File> files = result.getFiles();
        if (files == null || files.size() == 0) {
            System.out.println("No files found."); // And this would be a good
                                                    // thing!
        } else {
            // Decided to put my output in a format that I could later easily
            // paste into Excel
            // So these are the headers of my CSV file.
            // Just remember to start this code with the following command:
            // gradle -q run >> output.csv
            System.out.println("Filenum, Filename, fileId, revisionId, cerberFilename, originalFilename");
            int filenum = 0;
            String realFilename = "";
            String deleteVersionId = "";
            String renameVersionId = "";
            String renameFilename = "";
            for (File file : files) {
                // Note: don't put \n at the end. There will be more stuff
                // printed later on.
                System.out.printf("%s, %s, %s, ", ++filenum, file.getName(), file.getId());
                // Try to get a bit more info in a separate call.
                fileId = file.getId();
                cerberFilename = file.getName();
                moreThanOneFilename = false;

                List<Revision> revisions = retrieveRevisions(service, fileId);
                if (revisions.size() != 2) {
                    // This statement will mess up the CSV-style output I'm
                    // trying to create.
                    // Talk to someone who cares...
                    System.out.println("There are " + revisions.size() + " revisions (and not 2), skipping");
                } else {
                    // Loop through all TWO revisions
                    realFilename = "";
                    for (Revision listRevision : revisions) {
                        renameVersionId = "";
                        deleteVersionId = "";
                        revisionId = listRevision.getId();

                        // OK, got the fileID, now the revisionID, now get the
                        // revision itself.
                        // Think we already had the revision? Think again. We
                        // need to retrieve it via
                        // a separate get(), since we need to tell the API to
                        // get the originalFilename
                        // in the return JSON.
                        Revision revision = service.revisions().get(fileId, revisionId).setFields("id,originalFilename")
                                .execute();
                        originalFilename = revision.getOriginalFilename();
                        if (originalFilename.indexOf(".cerber3") > -1) {
                            // Yeah, found the encrypted file, let's delete this
                            // version
                            // System.out.printf("Going to delete file,
                            // versionid, originalName: %s, %s, %s\n", fileId,
                            // revisionId, originalFilename);
                            deleteVersionId = revisionId;
                        } else {
                            // System.out.printf("Going to rename file,
                            // versionid, originalName: %s, %s, %s\n", fileId,
                            // revisionId, originalFilename);
                            renameVersionId = revisionId;
                            renameFilename = originalFilename;
                        }
                    }
                    // Looped through 2 version, now do the deleting + renaming
                    // First delete the version
                    service.revisions().delete(fileId, deleteVersionId).execute();

                    // Rename the version
                    File renamedFile = renameFile(service, fileId, renameFilename);
                    // And complete the CSV-line, started before (already
                    // printed num, filename, fileid)
                    System.out.printf(" %s, %s, %s\n", deleteVersionId, cerberFilename, renameFilename);
                }
            }
        }
        // Now let's look for the cerber "help" files...
        // These files are named either
        // @___README___@.html or @___README___@.txt or @___README___@.url 
        result = service.files().list()
                .setPageSize(500)
                .setQ("name contains '@___README___@.'")
                .setSpaces("drive")
                .setFields("nextPageToken, files(id, name)").execute();
        files = result.getFiles();
        if (files == null || files.size() == 0) {
            System.out.println("No cerber files found."); // And this would be a good thing!
        } else {
            int filenum = 0;
            for (File file : files) {
                System.out.printf("Going to delete file %s, %s, %s\n", ++filenum, file.getId(), file.getName());
                // .delete only works if you are the owner of the file.
                // Which, in this case, is exactly what we want.
                service.files().delete(file.getId()).execute();
            }
        }
    }
}

答案 3 :(得分:0)

我遇到了同样的问题,勒索软件对PC上的所有文件进行了加密,并且由于我安装了Google Backup&Sync,因此将所有加密的文件推送到Google云端硬盘。

我创建了这个nodejs脚本来删除加密的修订并将文件重命名为原始文件: https://github.com/ronnyvdb/rollbackRevisionsGoogleDrive