IT박스

adb 쉘에서 바이너리 stdout 데이터를 읽으시겠습니까?

itboxs 2020. 12. 10. 19:42
반응형

adb 쉘에서 바이너리 stdout 데이터를 읽으시겠습니까?


adb 쉘 명령에서 바이너리 표준 출력을 읽을 수 있습니까? 예를 들어 screencap을 사용하는 방법의 모든 예에는 두 단계가 포함됩니다.

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에서 출력을 읽는 것은 어떻습니까? 내가 원하는 것은 다음과 같습니다.

adb shell screencap -p > foo3.png

그리고 SD 카드에 대한 중간 쓰기를 피하십시오. 이렇게 하면 PNG 파일처럼 보이고 (실행 strings foo3.png하면 IHDR, IEND 등으로 무언가가 생성됨) 거의 동일한 크기가 생성되지만 이미지 리더와 관련하여 파일이 손상됩니다.

나는 또한 자바에서 ddmlib를 사용하여 이것을 시도했으며 결과는 동일합니다. 필요한 모든 라이브러리를 기꺼이 사용하겠습니다. 내 목표는 캡처하는 데 걸리는 총 시간을 줄이는 것입니다. 내 장치에서 두 명령 솔루션을 사용하면 이미지를 가져 오는 데 약 3 초가 걸립니다. ddmlib를 사용하고 stdout을 캡처하는 데 900ms 미만이 걸리지 만 작동하지 않습니다!

이것이 가능합니까?

편집 : 여기에 두 파일의 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) 바이트가 추가 된 것 같습니다. 캐리지 리턴 ?? 벨이 울리나요? 빈 줄에 섞여 있습니까?


오래된 질문에 대한 답변을 게시하게되어 죄송합니다.하지만이 문제를 직접 발견했고 쉘을 통해서만 해결하고 싶었습니다. 이것은 나를 위해 잘 작동했습니다.

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

그건 ^M내가 Ctrl + V를 눌러있어 문자입니다 -> Ctrl + m를, 그냥 작업 할 때 복사 붙여 넣기하지 않습니다 나타났습니다.

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

나에게도 트릭을했다.


달리 명령을 사용하지 않는 바이너리 출력을 미치게. 그래서 당신은 할 수 있습니다adb shelladb exec-outpty

adb exec-out screencap -p > test.png

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

STDERR에서 출력을 생성하는 명령에이 기술을 사용하는 경우로 리디렉션해야합니다 /dev/null. 그렇지 않으면 adbSTDOUT에 STDERR이 포함되어 출력이 손상됩니다. 예를 들어, 디렉토리를 백업하고 압축하려는 경우 :

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

언급했듯이 "adb shell"은 캐리지 리턴 + 줄 바꿈 (0x0d 0x0a) 변환으로 줄 바꿈 (0x0a)을 수행합니다. 이것은 pseudo-tty 라인 규율에 의해 수행됩니다. 쉘에 사용할 수있는 "stty"명령이 없기 때문에 터미널 설정을 엉망으로 만드는 쉬운 방법이 없습니다.

그것은의 는 ddmlib으로 원하는 일을 할 수 있습니다. 장치에서 명령을 실행하고 출력을 캡처하여 유선으로 전송하는 코드를 작성해야합니다. 이것은 DDMS가 특정 기능에 대해 수행하는 작업입니다. 이것은 그 가치보다 더 많은 문제가 될 수 있습니다.

repair()모든 CRLF를 LF로 변환 하는 솔루션은 흔들리는 느낌이 들지만 "손상된"LF에서 CRLF 로의 변환이 결정적이기 때문에 실제로 신뢰할 수 있습니다. 실수로 ASCII 모드 FTP 전송을 복구하기 위해 동일한 작업을 수행했습니다.

PNG 파일 형식은이 (및 관련) 문제를 정확히 포착하도록 명시 적으로 설계되었다는 점에 주목할 가치가 있습니다. 매직 넘버는 0x89로 시작하여 상위 비트를 제거하는 모든 것을 포착하고 "PNG"가 뒤 따르므로 파일에 무엇이 있는지 쉽게 알 수 있습니다. CR LF는 다양한 ASCII 라인 변환기를 포착하고 0x1a로 이전 MS-DOS 프로그램을 포착합니다. 특수 파일 끝 마커로 Ctrl-Z를 사용한 다음, 고독한 LF를 사용했습니다. 파일의 처음 몇 바이트를 보면 파일에 수행 된 작업을 정확히 알 수 있습니다.

... 즉, repair()함수가 "손상된"입력과 "순수한"입력을 모두 받아 들일 수 있고 어떤 작업이 필요한지 안정적으로 결정할 수 있습니다.

편집 : 추가 참고 사항 : 장치 측 바이너리가 tty를 사용하여 변환을 방지하도록 tty를 구성 할 수 있습니다 cfmakeraw(). ADB 셸 연결을 통해 라이브 화면 캡처에서 원시 비디오를 보낼 수있는 Android 5.0 prepareRawOutput()screenrecord 명령에 있는 함수를 참조하세요 .


가장 좋은 해결책은 @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 Hextoolshexdump 에서 사용 하고 있습니다).

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 shellV2 프로토콜 을 지원해야한다는 것 입니다.

PC 쪽을 돌보는 것은 다소 사소한 일입니다. platform-tools패키지 ( adb바이너리 포함 )를 최신 버전으로 업데이트하기 만하면됩니다 . adbd기기 데몬 버전은 Android 버전과 연결되어 있습니다. adb shellV2 프로토콜이 함께 완벽한 안드로이드 5.0에 도입 된 adb정비 (에서 예정 cC++코드). 그러나 일부 회귀 (버그)가있어서 adb exec-outAndroid 5.x의 유용성은 여전히 ​​제한적이었습니다. 마지막으로 Android 4.x 및 이전 장치는 지원되지 않습니다. 다행히도 여전히 개발에 사용되는 구형 장치의 점유율이 빠르게 감소하고 있습니다.


16 진 덤프를 더 깊이 파고 들면 문자 0x0A가 방출 될 때마다 쉘이 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();      
}

편집 : 왜 이것을하고 있는지 나에게 밝혔습니다. 구식 DOS처럼 LF를 CR / LF로 변환합니다. 그것을 끄는 설정이 있는지 궁금합니다.


예, Unix / Linux / Mac OS X에서는 앞에 "stty -onlcr;"을 추가하여 adb 쉘의 바이너리 출력을 수신 할 수 있습니다. 당신의 명령에 ( 아니요 ~~ 루팅 안드로이드 필요).

1.Download "stty" executable file.
http://www.busybox.net/downloads/binaries/latest/
For old android, use busybox-armv5l, Others use busybox-armv7l.
rename file to "stty"

2.Uploda file "stty" to android and set proper permission.

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

3.Prepend "stty -onlcr;" to your command like this;

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

Done!

But for Windows OS, by default, LF from android will be converted to CR CR LF.
Even you did above step, you still get CR LF.
This "seems" because local adb.exe use fwrite which cause CR be prepended.
I have no way about this except convert CR LF to LF manually on Windows OS.


Another way:

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

BUT, as @osexp2003 said, that does not work for Windows OS.


Here is solution that works everywhere (Linux and Windows included).

You will need netcat utility, often named nc.
If both nc and busybox nc fail on your device, you need fresh busybox. You can either use busybox installer from Play Market (root required), or use solution by osexp2003 (download busybox from official site, put it into /data/local/tmp/ on device and add execute permission).

The idea is to use netcat as a primitive HTTP server.
Well, not even a proper server in fact. It will just send its input as response to any TCP connection (be it HTTP request from browser, telnet connection or just netcat) and terminate.

Run command you want to get output from like this:

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

In the above example, screencap -p takes a screenshot (PNG image) and pipes it to netcat.
-l tells netcat to act as a server (listen for connection), and -p 8080 tells it to use TCP port 8080. Omiting >/dev/null will simply print e.g. incoming HTTP GET request to your terminal.
The above example will wait for someone to connect, send screenshot and only then terminate.
Of course you can run it without adb shell, e.g. from terminal emulator on your device.

After running your command as above, you can download its output from your phone, by opening http://ip.of.your.phone:8080 in browser or by any other means, for example using netcat:

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

If you want to use USB cable for download, you need to forward connection using ADB like this:

adb forward tcp:7080 tcp:8080

After that you can use localhost:7080 instead of ip.of.your.phone:8080.
You can remove this forwarding with following command:

adb forward --remove tcp:7080

try this guys:

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

It is also possible to use base64 for this, so just encode it using:

base64 foo3.png>foo3.png.base64

and then on windows using some base64 utility or maybe notepad++ to decrypt the file.

Or in linux / cygwin:

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

You can also use the standard dos2unix command if its available.

(apt-get install dos2unix if you're on Debian/Ubuntu. There are probably builds for Windows, OS X, etc. out there somewhere if you google).

dos2unix converts CRLF to LF the same way as Eric Lange's repair() function.

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

or, fix a corrupted file (in-place) :

dos2unix -f screenshot.png

You need the -f to force it to process binary files.


I put the method to use python get image bytes using adb here, maybe this will be helpful to someone who encountered this problem. The code is as following:

 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)

nc was the only way it worked for me. Used:

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

as root to create a hopefully proper backup for /data/media


This is the best way using Shell in OS

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


My bash script (default is exec-out as it works well on both devices):

https://gist.github.com/mj41/14767642e02dda7ce32ed26ce012fe12


This command worked for me on Windows OS:

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

But you want to use this: https://sourceforge.net/projects/dos2unix/

참고URL : https://stackoverflow.com/questions/13578416/read-binary-stdout-data-from-adb-shell

반응형