【问题标题】:How can one pull the (private) data of one's own Android app?如何提取自己的 Android 应用程序的(私人)数据?
【发布时间】:2013-03-11 14:26:48
【问题描述】:

尝试使用

拉取单个文件
adb pull /data/data/com.corp.appName/files/myFile.txt myFile.txt

失败

failed to copy '/data/data/com.corp.appName/files/myFile.txt myFile.txt' to 'myFile.txt': Permission denied

尽管设备上启用了 USB 调试。

我们可以通过古老的路线绕过问题

adb shell
run-as com.corp.appName
cat files/myFile.txt > myFile.txt

但这对于多个文件来说很笨重。

如何将目录 /data/data/com.corp.appName/files 拉到我的 MacBook?

直接或通过在 `/storage/sdcard0/myDir(我可以从那里继续进行 Android 文件传输)中的传输都可以。

附加评论

可能只是在运行

adb backup  -f myFiles com.corp.appName

将生成我正在寻找的文件。在这种情况下,我正在寻找一种方法来解压/解压缩生成的备份!

【问题讨论】:

  • 除非设备已root,否则您根本无法这样做。
  • 我希望你是错的,但以防万一,请移动你的评论作为答案。你能添加一些参考来支持你为什么这么认为吗?我想保存我的程序的错误版本保存的所有垃圾文件,以确保我即将发布的程序能够优雅地处理各种输入文件。
  • 这是否意味着可以将目录从 world:--x chmod 到 world:r-x 足够长以便能够获取文件?
  • @Knossos 尽管这是我的应用程序,但用户能够保存应用程序存储在其设备上的私人数据的副本是否不合法?毕竟,数据可能属于设备所有者,而不是应用作者。
  • 问题是你如何区分所有者和恶意应用程序/程序,试图与你的应用程序私有数据交互。 (其中可能包含用户个人数据)

标签: android adb


【解决方案1】:

adb 备份将写入 Android 特定的存档:

adb backup  -f myAndroidBackup.ab  com.corp.appName

可以使用以下方法将此存档转换为 tar 格式:

dd if=myAndroidBackup.ab bs=4K iflag=skip_bytes skip=24 | openssl zlib -d > myAndroidBackup.tar

参考:

http://nelenkov.blogspot.ca/2012/06/unpacking-android-backups.html

在该链接中搜索“更新”。


或者,使用 Android backup extractor 从 Android 备份 (.ab) 文件中提取文件。

【讨论】:

  • bs=24 skip=1bs=1 skip=24 快得多(在我的系统上,38.6 MB/s vs 1.7 MB/s):)
  • 未使用 zlib 编译 openssl 的 Python 替代方案:dd if=data.ab bs=1 skip=24 | python -c "import zlib,sys;sys.stdout.write(zlib.decompress(sys.stdin.read()))" | tar -xvf -(来自上述来源)
  • 我在工作时在我的 MacBook 上使用 dd 命令从来没有成功过。相反,我使用java -jar ./abe.jar unpack data.ab data.tar。您可以从sourceforge.net/projects/adbextractor/files/?source=navbar下载 abe 实用程序
  • 另外,我注意到我不得不强制关闭要备份其数据的应用程序,否则备份为空! (Android 4.4.2) 与manifest显示android:allowBackup="false"时效果相同
  • 更好,直接将文件解压到本地目录:dd if=myAndroidBackup.ab bs=24 skip=1 | openssl zlib -d | tar -x
【解决方案2】:

我遇到了同样的问题,但运行如下:

$ adb shell
$ run-as {app-package-name}
$ cd /data/data/{app-package-name}
$ chmod 777 {file}
$ cp {file} /mnt/sdcard/

之后就可以运行了

$ adb pull /mnt/sdcard/{file}

【讨论】:

【解决方案3】:

这对我有用:

adb -d shell "run-as com.example.test cat /data/data/com.example.test/databases/data.db" > data.db

我将数据库直接打印到本地文件中。

【讨论】:

  • 正是这个命令导致我的 db 文件损坏。但我使它与以下步骤一起工作: ------ adb shell ------ 在 shell 内运行 run-as com.example.test ------ 在 shell 内运行 cat ...db > /mnt/sdcard/file.db ---- -- adb pull /mnt/sdcard/file.db . ------ 结束(抱歉,换行无效)
  • @KirillOficerov 如果这些命令可以写成一行就好了
  • 非常适合您需要 1 个文件。一直用这个做数据库。现在面对我需要多个文件的情况。
  • 由于某种原因,使用adb shell 对我不起作用,但跟随this answer 并使用adb exec-out 它起作用了!
  • 返回“adb.exe: no devices found”
【解决方案4】:

在 MacOSX 上,通过结合 Calaf 和 Ollie Ford 的答案,以下对我有用。

在命令行上(确保 adb 在您的路径中,我的路径是 ~/Library/Android/sdk/platform-tools/adb),并在您的 android 设备插入并处于 USB 调试模式时,运行:

 adb backup -f backup com.mypackage.myapp

您的安卓设备会询问您是否允许备份您的数据。选择“备份我的数据”

请稍等。

文件备份将出现在您运行 adb 的目录中。

现在运行:

dd if=backup bs=1 skip=24 | python -c "import zlib,sys;sys.stdout.write(zlib.decompress(sys.stdin.read()))" > backup.tar

现在您将拥有一个 backup.tar 文件,您可以像这样解压:

 tar xvf backup.tar

并查看您的应用程序存储的所有文件。

【讨论】:

  • 您的 Mac 版本有效!注意:adb backupdd tar 转换步骤在工作时都不会提供任何状态,如果您有大量数据,可能需要几分钟时间。耐心点!
  • 我得到:“zlib.error: Error -3 while decompressing data: wrong header check” 我的备份要求输入密码来加密。这个 python 方法不需要密码来解密备份。上例中的 cmets 中的 abe.jar 要求输入密码,但我使用该方法解压后并没有得到所有文件。
  • 我第二次尝试使用 abe.jar 进行加密备份成功了。
【解决方案5】:

较新版本的 Android Studio 包括 设备文件资源管理器,我发现它是一种方便的 GUI 方法,可以从我的开发 Nexus 7 下载文件。

您必须确保已在设备上启用 USB 调试

  1. 单击查看 > 工具窗口 > 设备文件资源管理器或单击工具窗口栏中的设备文件资源管理器按钮以打开设备文件资源管理器。
  2. 从下拉列表中选择一个设备。
  3. 在文件资源管理器窗口中与设备内容交互。右键单击文件或目录以创建新文件或目录,将选定的文件或目录保存到您的机器,上传、删除或同步。双击文件以在 Android Studio 中打开它。

    Android Studio 会将您以这种方式打开的文件保存在项目外部的临时目录中。如果您对使用设备文件资源管理器打开的文件进行了修改,并且希望将更改保存回设备,则必须手动将修改后的文件版本上传到设备。

Full Documentation

【讨论】:

  • 太棒了,谢谢,我不知道这存在。否则,那真是太棒了!
【解决方案6】:

您可以在下面使用这个 shell 脚本。它也可以从应用缓存中提取文件,不像adb backup 工具:

#!/bin/sh

if [ -z "$1" ]; then 
    echo "Sorry script requires an argument for the file you want to pull."
    exit 1
fi

adb shell "run-as com.corp.appName cat '/data/data/com.corp.appNamepp/$1' > '/sdcard/$1'"
adb pull "/sdcard/$1"
adb shell "rm '/sdcard/$1'"

那么你可以这样使用它:

./pull.sh files/myFile.txt
./pull.sh cache/someCachedData.txt

【讨论】:

  • 最好的部分是当你不给出论据然后用rm /sdcard/替代,幸运的是它不是-rf
  • 你可以直接 cat 到主机上的一个文件:“adb shell run-as com.whatever.Fnord cat /data/data/com.whatever.Fnord/databases/logging.db > / tmp/foo"(其中 /tmp/foo 是主机上的文件,而不是 android 设备上的文件)
  • 刚刚意识到@dave-thomas 不久前解决了这个问题。删除我的反对票。
  • 或者不是 :-( "除非编辑此答案,否则您的投票现在已锁定。"
  • TBH 我不明白这有什么大惊小怪的。 (原始)脚本永远不会删除目录(缺少 -rf 开关),除非它是空的。我没有检查过,但即使目录是空的,我也怀疑脚本是否会删除目录,因为它是一个挂载点。脚本看起来很危险,但它从来没有......
【解决方案7】:

如果您使用的是 Mac 机器和三星手机,这就是您必须要做的(因为三星上是 run-as doesn't work 和 Mac 上是 zlib doesn't work

  1. 备份应用的数据目录 adb backup -f /Users/username/Desktop/data.ab com.example

  2. 您将被要求输入密码以在您的手机中加密,请勿输入任何密码。只需点击“备份我的数据”。见How to take BackUp?

  3. 成功备份后,您将在桌面上看到data.ab 文件。现在我们需要将其转换为tar 格式。

  4. 为此使用 Android Backup ExtractorDownload | SourceCode

  5. 下载它,您将看到abe.jar 文件。将此添加到您的 PATH 变量中。

  6. 执行此操作以生成 tar 文件:java -jar abe.jar unpack /Users/username/Desktop/data.ab /Users/username/Desktop/data.tar

  7. 解压data.tar文件以访问所有文件

【讨论】:

  • 完全按照这一点,data.tar 只是提取到data.tar.cpgz @henry
  • 我已经阅读了关于此主题的 5 篇其他主题文章“如何从我安装在设备上的应用程序获取数据库”,这是唯一适合我的解决方案。我的手机没有root,来自adb 的大多数命令都不起作用,例如run-as adb shell, cd \data\data ls 等,我不想安装Android Studio(我正在使用Visual Studio Xamarin),这个解决方案是对我来说最无痛。
【解决方案8】:

通过添加以下代码设置正确的权限后:

File myFile = ...;
myFile.setReadable(true, false); // readable, not only for the owner

adb pull 按需要工作。

File.setReadable()

【讨论】:

  • 在调试时更改权限是可以的,但在接近发布的某个时候这样做会破坏默认提供的保护(以防止其他应用程序访问/弄乱文件)。因此,即使它们只是用户可读/可写的,开发人员也需要一种访问文件的方法。
【解决方案9】:

这个答案是基于我对其他答案的经验,以及答案中的 cmets。我希望我能帮助遇到类似情况的人。

我正在通过终端在 OSX 上执行此操作。

以前,Vinicius Avellar 的回答对我很有用。我只是大部分时间都需要来自调试应用程序的设备中的数据库。

今天我遇到了一个需要多个私有文件的用例。我最终得到了两个对这种情况很有效的解决方案。

  1. 将接受的答案与 Somewhere 的 OSX 特定 cmets 一起使用。创建备份并使用第 3 方解决方案, sourceforge.net/projects/adbextractor/files/?source=navbar 解压 变成焦油。我将在这个答案的底部写更多关于我使用这个解决方案的经验。 如果 这就是你要找的。​​em>

  2. 我选择了一个更快的解决方案。我创建了一个用于提取多个文件的脚本,类似于 Tamas 的答案。我能做到 这种方式是因为我的应用程序是调试应用程序,并且我可以访问 run-as on 我的设备。如果您无权访问 run-as 此方法将不起作用 在 OSX 上为您服务。

这是我的脚本,用于提取多个私人文件,我将与你分享,读者也在调查这个很棒的问题;):

#!/bin/bash
#
# Strict mode: http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -euo pipefail
IFS=$'\n\t'

# 
# Usage: script -f fileToPull -p packageName
# 

# This script is for pulling private files from an Android device
# using run-as. Note: not all devices have run-as access, and
# application must be a debug version for run-as to work.
# 
# If run-as is deactivated on your device use one of the
# alternative methods here:
# http://stackoverflow.com/questions/15558353/how-can-one-pull-the-private-data-of-ones-own-android-app
# 
# If you have encrypted backup files use:
# sourceforge.net/projects/adbextractor/files/?source=navbar 
# From comments in the accepted answer in the above SO question
# 
# If your files aren't encrypted use the accepted answer 
# ( see comments and other answers for OSX compatibility )
# 
# This script is open to expansions to allow selecting 
# device used. Currently first selected device from
# adb shell will be used.

#Check we have one connected device
adb devices -l | grep -e 'device\b' > /dev/null

if [ $? -gt 0 ]; then
    echo "No device connected to adb."
    exit 1
fi

# Set filename or directory to pull from device
# Set package name we will run as
while getopts f:p: opt; do
    case $opt in
        f)
            fileToPull=$OPTARG
            ;;
        p)
            packageName=$OPTARG
            ;;
    esac
done;

# Block file arg from being blank
if [ -z "$fileToPull" ]; then
    echo "Please specify file or folder to pull with -f argument"
    exit 1
fi

# Block package name arg from being blank
if [ -z "$packageName" ]; then
    echo "Please specify package name to run as when pulling file"
    exit 1
fi

# Check package exists
adb shell pm list packages | grep "$packageName" > /dev/null
if [ $? -gt 0 ]; then
    echo "Package name $packageName does not exist on device"
    exit 1
fi

# Check file exists and has permission with run-as
fileCheck=`adb shell "run-as $packageName ls $fileToPull"`
if [[ $fileCheck =~ "Permission denied" ]] || [[ $fileCheck =~ "No such file or directory" ]]; then
    echo "Error: $fileCheck"
    echo "With file -> $fileToPull"
    exit 1
fi

# Function to pull private file
#
# param 1 = package name
# param 2 = file to pull
# param 3 = output file
function pull_private_file () {

    mkdir -p `dirname $3`

    echo -e "\033[0;35m***" >&2
    echo -e "\033[0;36m Coping file $2 -> $3" >&2
    echo -e "\033[0;35m***\033[0m" >&2

    adb shell "run-as $1 cat $2" > $3
}

# Check if a file is a directory
# 
# param 1 = directory to check
function is_file_dir() {

    adb shell "if [ -d \"$1\" ]; then echo TRUE; fi"
}

# Check if a file is a symbolic link
# 
# param 1 = directory to check
function is_file_symlink() {

    adb shell "if [ -L \"$1\" ]; then echo TRUE; fi"
}

# recursively pull files from device connected to adb
# 
# param 1 = package name
# param 2 = file to pull
# param 3 = output file
function recurse_pull_private_files() {

    is_dir=`is_file_dir "$2"`
    is_symlink=`is_file_symlink "$2"`

    if [ -n "$is_dir" ]; then

        files=`adb shell "run-as $1 ls \"$2\""`

        # Handle the case where directory is a symbolic link
        if [ -n "$is_symlink" ]; then
            correctPath=`adb shell "run-as $1 ls -l \"$2\"" | sed 's/.*-> //' | tr -d '\r'`
            files=`adb shell "run-as $1 ls \"$correctPath\""`
        fi

        for i in $files; do

            # Android adds nasty carriage return that screws with bash vars
            # This removes it. Otherwise weird behavior happens
            fileName=`echo "$i" | tr -d '\r'` 

            nextFile="$2/$fileName"
            nextOutput="$3/$fileName"
            recurse_pull_private_files "$1" "$nextFile" "$nextOutput"
        done
    else

        pull_private_file "$1" "$2" "$3"
    fi
}

recurse_pull_private_files "$packageName" "$fileToPull" "`basename "$fileToPull"`"

要点: https://gist.github.com/davethomas11/6c88f92c6221ffe6bc26de7335107dd4


回到方法1,使用Android Backup Extractor解密备份

以下是我在 Mac 上执行的步骤以及遇到的问题:

首先我排队备份(并设置密码来加密我的备份,我的设备需要它):

adb backup -f myAndroidBackup.ab  com.corp.appName

第二次我从这里下载了刚刚的 abe.jar:https://sourceforge.net/projects/adbextractor/files/abe.jar/download

接下来我跑了:

java -jar ./abe.jar unpack myAndroidBackup.ab myAndroidBackup.tar

此时我收到一条错误消息。因为我的存档是加密的,java 给了我一个错误,我需要安装一些安全策略库。

  • 所以我去了http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html 并下载了我需要的安全策略jar。现在就我而言,安装说明告诉我放置 jar 文件的位置错误。它说正确的位置是 /lib/security。我先把它们放在那里,但仍然收到错误消息。所以我调查并在我的 Mac 上使用 Java 1.8 放置它们的正确位置是:/jre/lib/security。我确保备份原始策略 jar,并将它们放在那里。 Vola 我能够使用 abe.jar 输入密码并解密为 tar 文件。

最后我刚刚跑了(在再次运行上一个命令之后

tar xvf myAndroidBackup.tar

现在重要的是要注意,如果你可以只 run-as 和 cat,它会快得多。一,你只得到你想要的文件,而不是整个应用程序。第二,文件越多(对我来说是 + 加密),传输速度就越慢。因此,如果您在 OSX 上没有 run-as,那么知道这样做很重要,但脚本应该首先转到调试应用程序。

请注意,我今天刚刚写了它并测试了几次,所以请通知我任何错误!

【讨论】:

  • 感谢您对方法 #1 的回复,我已经能够从安装在生产无根设备上的调试目标应用程序获取和探索包备份,但备份仅包含清单文件。使用方法 #2 我下载了所有应用程序的数据,但二进制文件(即 SQLite 文件)是不可读的。我根据对我有用的脚本添加了一个解决方案。
【解决方案10】:

类似于Tamas's answer,这是一个用于 Mac OS X 的单行程序,用于从您的设备中获取带有your.app.id 的应用程序的所有文件并将它们保存到(在这种情况下)~/Desktop/your.app.id

(
    id=your.app.id &&
    dest=~/Desktop &&
    adb shell "run-as $id cp -r /data/data/$id /sdcard" &&
    adb -d pull "/sdcard/$id" "$dest" &&
    if [ -n "$id" ]; then adb shell "rm -rf /sdcard/$id"; fi
)
  • 排除 -d 以从模拟器中提取
  • 不会影响您的会话变量
  • 您可以将整个块粘贴到 Terminal.app(或根据需要删除换行符)

【讨论】:

    【解决方案11】:

    从 Dave Thomas 脚本开始,我已经能够编写自己的解决方案来克服 2 个问题:

    1. 我的备份只包含清单文件
    2. Dave Thomas 获得的二进制文件无法读取

    这是我的脚本,将应用数据复制到 sdcard 然后拉取它

    #Check we have one connected device
    adb devices -l | grep -e 'device\b' > /dev/null
    
    if [ $? -gt 0 ]; then
        echo "No device connected to adb."
        exit 1
    fi
    
    # Set filename or directory to pull from device
    # Set package name we will run as
    while getopts f:p: opt; do
        case $opt in
            f)
                fileToPull=$OPTARG
                ;;
            p)
                packageName=$OPTARG
                ;;
        esac
    done;
    
    # Block package name arg from being blank
    if [ -z "$packageName" ]; then
        echo "Please specify package name to run as when pulling file"
        exit 1
    fi
    
    # Check package exists
    adb shell pm list packages | grep "$packageName" > /dev/null
    if [ $? -gt 0 ]; then
        echo "Package name $packageName does not exist on device"
        exit 1
    fi
    
        adb shell "run-as $packageName cp -r /data/data/$packageName/ /sdcard/$packageName"
        adb pull /sdcard/$packageName
        adb shell rm -rf /sdcard/$packageName
    

    【讨论】:

      【解决方案12】:

      你可以这样做:

      adb pull /storage/emulated/0/Android/data//

      【讨论】:

      • 不要误会我的意思,但是,您是否正确阅读了这个问题? OP 要求提取“私有”数据NOT“公开”
      【解决方案13】:

      使用 apk 备份游戏数据。牛轧糖一加 2。

      **adb backup "-apk com.nekki.shadowfight" -f "c:\myapk\samsung2.ab"**
      

      【讨论】:

      • adb backup -f myapp.ab -apk com.myapp # backup on one device adb restore myapp.ab # restore to the same or any other device
      【解决方案14】:

      这是否意味着可以将目录从 world:--x chmod 到 world:r-x 足够长以获取文件?

      是的,没错。奇怪的是,您还需要在文件中设置x 位。 (至少在 Android 2.3 上)

      chmod 755 一直在努力复制文件(但如果您打算继续使用该设备,则应在之后恢复权限)。

      【讨论】:

        猜你喜欢
        • 2013-09-24
        • 1970-01-01
        • 2012-10-03
        • 1970-01-01
        • 1970-01-01
        • 2016-06-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多