从 adb shell 读取二进制标准输出数据,比如 Screen cap 数据?

是否可以从 adb shell 命令读取二进制标准输出?例如,如何使用屏幕盖的所有例子包括两个步骤:

adb shell screencap -p /sdcard/foo.png
adb pull /sdcard/foo.png

但是,该服务支持写入 stdout:

adb shell "screencap -p > /sdcard/foo2.png"
adb pull /sdcard/foo2.png

这个方法同样有效。但是,如何解读整个亚洲开发银行的产出呢?我想做的是:

adb shell screencap -p > foo3.png

并避免中间写入 SD 卡。这会生成类似于 PNG 文件的 看起来(运行 strings foo3.png会生成具有 IHDR、 IEND 等的文件) ,并且大小大致相同,但就图像阅读器而言,该文件已损坏。

我还尝试在 java 中使用 ddmlib 来实现这一点,结果是相同的。我很乐意使用任何必要的图书馆。我的目标是减少抓捕的总时间。在我的设备上,使用双命令解决方案,大约需要3秒钟才能得到图像。使用 ddmlib 并捕获标准输出只需要不到900毫秒,但它不起作用!

有可能做到吗?

编辑: 这里是两个文件的 hexdump。第一个,screen. png 来自 stdout,已损坏。第二个 xscreen 来自于双命令解决方案,可以工作。图像应该在视觉上一致。

$ hexdump -C screen.png | head
00000000  89 50 4e 47 0d 0d 0a 1a  0d 0a 00 00 00 0d 49 48  |.PNG..........IH|
00000010  44 52 00 00 02 d0 00 00  05 00 08 06 00 00 00 6e  |DR.............n|
00000020  ce 65 3d 00 00 00 04 73  42 49 54 08 08 08 08 7c  |.e=....sBIT....||
00000030  08 64 88 00 00 20 00 49  44 41 54 78 9c ec bd 79  |.d... .IDATx...y|
00000040  9c 1d 55 9d f7 ff 3e 55  75 f7 de b7 74 77 d2 d9  |..U...>Uu...tw..|
00000050  bb b3 27 10 48 42 16 c0  20 01 86 5d 14 04 11 dc  |..'.HB.. ..]....|
00000060  78 44 9d c7 d1 d1 11 78  70 7e 23 33 8e 1b 38 33  |xD.....xp~#3..83|
00000070  ea 2c 8c 8e 0d 0a 08 a8  23 2a 0e 10 82 ac c1 40  |.,......#*.....@|
00000080  12 02 81 24 64 ef ec 5b  ef fb 5d 6b 3b bf 3f ea  |...$d..[..]k;.?.|
00000090  de db dd 49 27 e9 ee 74  77 3a e3 79 bf 5e 37 e7  |...I'..tw:.y.^7.|


$ hexdump -C xscreen.png | head
00000000  89 50 4e 47 0d 0a 1a 0a  00 00 00 0d 49 48 44 52  |.PNG........IHDR|
00000010  00 00 02 d0 00 00 05 00  08 06 00 00 00 6e ce 65  |.............n.e|
00000020  3d 00 00 00 04 73 42 49  54 08 08 08 08 7c 08 64  |=....sBIT....|.d|
00000030  88 00 00 20 00 49 44 41  54 78 9c ec 9d 77 98 1c  |... .IDATx...w..|
00000040  c5 99 ff 3f d5 dd 93 37  27 69 57 5a e5 55 4e 08  |...?...7'iWZ.UN.|
00000050  24 a1 00 58 18 04 26 08  8c 01 83 31 38 c0 19 9f  |$..X..&....18...|
00000060  ef 7c c6 3e 1f 70 f8 7e  67 ee 71 e2 b0 ef ce f6  |.|.>.p.~g.q.....|
00000070  f9 ec 73 04 1b 1c 31 60  23 84 30 22 88 a0 40 10  |..s...1`#.0"..@.|
00000080  08 65 69 95 d3 4a 9b c3  c4 4e f5 fb a3 67 66 77  |.ei..J...N...gfw|
00000090  a5 95 b4 bb da a4 73 7d  9e 67 55 f3 ed 50 5d dd  |......s}.gU..P].|

快速浏览一下,就会发现似乎增加了几个额外的0x0d (13)字节。马车回程? ? ?有印象吗?是不是混进了一些空行?

44778 次浏览

最好的解决方案是像 @ AjeetKhadke建议的那样使用 adb exec-out命令。

让我举例说明 adb shelladb exec-out输出之间的区别:

~$ adb shell "echo -n '\x0a'" | xxd -g1
00000000: 0d 0a


~$ adb exec-out "echo -n '\x0a'" | xxd -g1
00000000: 0a

它可以在 Windows 下工作(我在演示中使用的是 GNUWin32 Hextools中的 hexdump) :

C:\>adb shell "echo -n '\x0a'" | hexdump
00000000: 0D 0A


C:\>adb exec-out "echo -n '\x0a'" | hexdump
00000000: 0A

缺点是,为了能够从使用 adb exec-out命令中获益,设备和主机 PC 都必须支持 adb shell V2协议。

处理 PC 端相当琐碎——只需将 platform-tools包(其中包含 adb二进制文件)更新到最新版本即可。设备上的 adbd守护进程版本链接到 Android 版本。在 Android 5.0中引入了 adb shell V2协议以及完整的 adb检修(从 cC++代码)。但是有一些回归(也就是 bug) ,所以 adb exec-out在 Android 5.x 中的用处仍然有限。最后,对于 Android 4.x 和更老的设备没有支持。幸运的是,那些仍然用于开发的旧设备的份额正在迅速下降。

在深入挖掘了十六进制转储之后,很明显,每次发出字符0x0A,shell 都会发出0x0D 0x0A。我用以下代码修复了流,现在二进制数据是正确的。当然,现在的问题是,为什么 adb shell 要这么做?但无论如何,这解决了问题。

static byte[] repair(byte[] encoded) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i=0; i<encoded.length; i++) {
if (encoded.length > i+1 && encoded[i] == 0x0d && encoded[i+1] == 0x0a) {
baos.write(0x0a);
i++;
} else {
baos.write(encoded[i]);
}
}
try {
baos.close();
} catch (IOException ioe) {


}


return baos.toByteArray();
}

编辑: 我明白了它为什么这样做。它正在将 LF 转换为 CR/LF,就像老式的 DOS 一样。我想知道有没有什么地方可以把它关掉?

正如前面提到的,“ adb shell”正在执行一个 linefeed (0x0a)到 car-return + linefeed (0x0d 0x0a)的转换。这是由伪 tty 线规程执行的。由于 shell 没有可用的“ stty”命令,因此没有简单的方法来扰乱终端设置。

使用 ddmlib 的是 有可能。您需要编写代码,在设备上执行命令,捕获输出,并通过网络发送。这或多或少是 DDMS 为某些特性所做的。这可能是更多的麻烦比它的价值。

repair()解决方案——将所有 CRLF 转换为 LF ——感觉不稳定,但实际上是可靠的,因为“破坏性”的 LF-to-CRLF 转换是确定性的。我过去常常做同样的事情来修复意外的 ASCII 模式的 FTP 传输。

值得注意的是,PNG 文件格式的显式设计就是为了准确捕捉这个(以及相关的)问题。这个神奇的数字以0x89开头,用来捕捉任何带有高位的内容,接着是“ PNG”,这样你就可以很容易地分辨出文件中的内容,接着是 CR LF,用来捕捉各种 ASCII 行转换器,然后是0x1a,用来捕捉使用 Ctrl-Z 作为特殊文件末尾标记的旧 MS-DOS 程序,最后是一个单独的 LF。通过查看文件的前几个字节,您可以确切地知道对它做了什么。

... 这意味着您的 repair()函数可以接受“损坏”和“纯”输入,并可靠地确定是否需要做任何事情。

编辑: 另一个注意事项: 设备端二进制文件可以使用 cfmakeraw()配置 tty 来避免转换。查看 Android 5.0中 屏幕记录命令中的 prepareRawOutput()函数,它可以通过 ADB shell 连接发送来自实时屏幕捕获的原始视频。

很抱歉要回答一个老问题,但是我自己碰到了这个问题,我只想通过 shell 来解决它。这对我很有效:

adb shell screencap -p | sed 's/^M$//' > screenshot.png

那个 ^M是我按 ctrl + v-> ctrl + m 得到的一个字符,只是注意到它在复制粘贴时不起作用。

adb shell screencap -p | sed 's/\r$//' > screenshot.png

也帮了我大忙。

是的,在 Unix/Linux/Mac OS X 上,您可以通过在命令前加上“ stty-onlcr;”来接收 adb shell 的二进制输出(没有 ~ ~ 需要是一个有根的 android)。

1. 下载“ stty”可执行文件。
Http://www.busybox.net/downloads/binaries/latest/
对于旧的机器人,使用 busybox-armv5l,其他机器人使用 busybox-armv7l。
将文件重命名为“ stty”

2. 将文件“ stty”上传到 android 并设置适当的权限。

adb push somelocaldir/stty /data/local/tmp/
adb shell chmod 777 /data/local/tmp/stty

3. 在命令前面加上“ stty-onlcr;”,如下所示;

adb shell /data/local/tmp/stty -onlcr\; screencap -p  > somelocaldir/output.png
or:
adb shell "/data/local/tmp/stty -onlcr; screencap -p"  > somelocaldir/output.png
or (Only for Windows):
adb shell /data/local/tmp/stty -onlcr; screencap -p  > somelocaldir/output.png

成交!

但是对于 Windows 操作系统,默认情况下,来自 android 的 LF 将被转换为 CR CR LF。
即使你做了上述步骤,你仍然得到 CR 低频。
这“似乎”是因为本地 adb.exe 使用 fwrite 导致 CR 被预先设置。
除了在 Windows 操作系统上手动将 CR LF 转换为 LF,我没有其他办法。

adb shell不同的是,adb exec-out命令不使用破坏二进制输出的 pty

adb exec-out screencap -p > test.png

Https://android.googlesource.com/platform/system/core/+/5d9d434efadf1c535c7fea634d5306e18c68ef1f

注意,如果您正在使用这种技术执行在 STDERR 上生成输出的命令,那么您应该将其重定向到 /dev/null,否则 adb将在其 STDOUT 中包含 STDERR,从而破坏您的输出。例如,如果您试图备份和压缩一个目录:

adb exec-out "tar -zcf - /system 2>/dev/null" > system.tar.gz

也可以使用 base64进行编码,所以只需使用:

base64 foo3.png>foo3.png.base64

然后在窗口上使用一些 Base64实用程序或者记事本 + + 来解密文件。

或者在 linux/cygwin 中:

base64 -d foo3.png.base64>foo3.png

另一种方式:

adb shell "busybox stty raw; screencap -p "> foo3.png

但是,正如@osexp2003所说,不适用于 Windows 操作系统。

下面是 无处不在(包括 Linux 和 Windows)的解决方案。

您将需要 netcat实用程序,通常命名为 nc
如果 ncbusybox nc都在设备上失败,则需要新的 busybox。您可以从 Play Market (需要 root 用户)使用 busybox 安装程序,也可以使用 解决方案由 osexp2003(从 官方网站下载 busybox,将其放入设备上的 /data/local/tmp/并添加执行权限)。

其思想是使用 netcat作为基本的 HTTP 服务器。
事实上,连个像样的服务员都算不上。它只是将其输入作为对 任何 TCP 连接的响应(可以是来自浏览器的 HTTP 请求、 telnet 连接或者仅仅是 netcat)并终止。

运行命令,您希望从中得到如下输出:

adb shell 'screencap -p | busybox nc -p 8080 -l >/dev/null'

在上面的示例中,screencap -p获取一个屏幕快照(PNG 图像)并将其传送到 netcat
-l告诉 netcat充当服务器(侦听连接) ,-p 8080告诉它使用 TCP 端口8080。 省略 >/dev/null只是将传入的 HTTP GET 请求打印到终端。
上面的例子将等待有人连接,发送截图,然后才终止。
当然你也可以在没有 abc0的情况下运行它,例如在你的设备上运行虚拟终端。

运行完上述命令后,通过浏览器打开 http://ip.of.your.phone:8080或其他任何方式,例如使用 netcat:

busybox nc ip.of.your.phone:8080 >screenshot.png

如果你想使用 USB 电缆下载 ,你需要像这样使用 ADB 转发连接:

adb forward tcp:7080 tcp:8080

之后,您可以使用 localhost:7080而不是 ip.of.your.phone:8080
您可以通过以下命令删除此转发:

adb forward --remove tcp:7080

如果标准 dos2unix命令可用,也可以使用它。

(如果你使用的是 Debian/Ubuntu 系统,那么你可以使用 apt-get install dos2unix,如果你在谷歌上搜索的话,可能会找到一些适用于 Windows,OS X 等系统的版本)。

dos2unix将 CRLF 转换为 LF 的方式与 Eric Lange 的 repair()函数相同。

adb shell screencap -p | dos2unix -f > screenshot.png

或者,修复损坏的文件(就地) :

dos2unix -f screenshot.png

您需要 -f来强制它处理二进制文件。

这个命令在 视窗操作系统上为我工作:

adb exec-out screencap -p > test.png && dos2unix.exe -f test.png

但你想用这个: Https://sourceforge.net/projects/dos2unix/

nc是唯一适合我的方式。使用:

adb forward tcp:7080 tcp:8080 &&\
adb shell 'tar -zcvf - /data/media | nc -p 8080 -l 1>/dev/null' &\
sleep 1;\
nc localhost 7080 > media.tar.gz &&\
adb forward --remove tcp:7080

作为 root 用户,以便为/data/media 创建一个合适的备份

试试这个:

adb shell screencap -p | perl -pe 's/\x0D\x0A/\x0A/g' > screen.png

这是在操作系统中使用 Shell 的最佳方式

adb shell screencap -p | perl -pe 's/\x0D\x0A/\x0A/g' > screen.png

我把使用 python 获取图像字节的方法使用 adb 放在这里,也许这对遇到这个问题的人会有帮助。守则如下:

 pipe = subprocess.Popen("adb shell screencap -p",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE, shell=True)
image_bytes = pipe.stdout.read().replace(b'\r\n', b'\n')
gray_image = cv2.imdecode(np.fromstring(image_bytes, np.uint8), cv2.IMREAD_GRAYSCALE)

这是一个老问题,但是这个解决方案可能对某些人有用。

对于从 adb 下载任何文件,您可以使用:

adb run-as your.package.name base64 -w 0 /path/to/your/file.db

然后读取并解码 linux 中的 base64字符串(例如) :

cat saved.base64.str|base64 -d

当然,在将源数据编码为 base64之前,可以先压缩源数据。

我想为这些应用程序添加另一个解决方案,即 防止 adb 截屏

你可以使用 好吧。它可以在 Linux 和 Windows 上运行! 只需运行:

scrcpy

将打开带有设备屏幕的窗口。你现在可以使用 当地的截图工具(例如在 windows 上使用剪切工具,在 linux 上使用截图)来截取当前屏幕的截图!

一般的方法是使用 adb exec-out,如 阿吉特47Alex P. 尖锐 出去

然而,如果中间有任何东西创建了一个伪 tty,事情就会变得混乱。

我想知道为什么我的 tar 文件被破坏,即使我使用 adb exec-out。在我的例子中,问题在于使用 su二进制文件产生 tar进程。su似乎产生了一个伪 tty,你不能关闭它,给你最坏的两个世界。

例如:

% adb exec-out 'su -c '"'"'printf "\n"'"'" | xxd
00000000: 0d0a                                     ..

在这种情况下,您仍然需要通过类似 stty raw的方法告诉 shell 不要使用额外的回车字符来破坏换行符:

% adb exec-out 'su -c '"'"'stty raw; printf "\n"'"'" | xxd
00000000: 0a                                       .

然而,正如其他人指出的,这可能是脆弱的,特别是对于二进制数据。当 exec-outstty raw配对时,我还没有看到在 adb上接收 tar输出时的任何问题,但是肯定会有问题。

因此,如果您真的希望实现位对位的相等,那么如果生成的数据与您希望捕获的惊人情况相匹配,则为 检查完毕。这不会修复它们,但至少会提醒您注意它们。

一种方法是在写出原始数据时对其进行校验和,并对主机上接收到的数据进行校验和。对于兼容 POSIX 的系统和 shell,一种相当复杂但是必要的方法是这样的:

% adb exec-out 'su -c '"'"'stty raw && printf "\n" 2>/data/local/tmp/printf.stderr | { tee /dev/fd/3 | sha512sum -b - > /data/local/tmp/printf.sha512sum; } 3>&1'"'" | sha512sum -b -
be688838ca8686e5c90689bf2ab585cef1137c999b48c70b92f67a5c34dc15697b5d11c982ed6d71be1e1e7f7b4e0733884aa97c3f7a339a8ed03577cf74be09 *-
% adb exec-out 'cat /data/local/tmp/printf.sha512sum'
be688838ca8686e5c90689bf2ab585cef1137c999b48c70b92f67a5c34dc15697b5d11c982ed6d71be1e1e7f7b4e0733884aa97c3f7a339a8ed03577cf74be09

不要试图使用 bash的命令行替换特性(例如 cmd | tee >(sha512sum -b - > ...)) ,因为这样会遗憾地丢失一个尾随的换行符,从而修改原始数据。

您可以使用此命令并将其保存在活动目录中

adb exec-out screencap -p > screen-shot.png