使用adb协议,无需forward也可以访问手机tcp端口

本文简单讲一下adb协议(非 adb 命令),发现一个很少有人用的功能,支持访问手机的 tcp 端口,无需 forward。

一、背景

以前用 frida 时,老是有人强调要 adb forward,而且还有 forward 27042 和27043两个端口,至今不知道为什么。

【经过简单调研,forward 27043 的就是傻逼,写文不负责任的nmsl!也不知道是哪个傻逼带的这股歪风邪气】

后来发现有 frida -U 这个功能,不需要 forward也可以访问安卓的 tcp 端口,于是有了本文。

第二部分,第三部分主要参考官方的这四个文件,和网上其他人的文章

如果只想知道结果,请直接看第四部分。

二、adb结构简介

server 端(PC)

运行在PC 端,常驻后台的进程,运行在127.0.0.1:5037,通常使用 adb kill-server 去杀死它。

本身的功能是接收并处理 client 端发来的请求,接收并处理 usb 端口的安卓设备,相当于一个桥梁功能。一台电脑仅可有一个。

client端(PC)

运行在 PC 端,与127.0.0.1:5037交互,平时 adb 命令用到的大部分功能就是 client 了。通过对5037抓包,可以获得协议和流量。一台电脑可以有无数个。

adbd(Android)

运行在手机端,ps 可以看到这个 adbd,与此类似的还有远古时期华为手机的 hdbd。通过 usb 和 pc 通信,具体协议没有研究过。一个手机仅可有一个。

adb可执行文件

上面提到的 server 端和 client 端来自同一个 adb 可执行文件,这个可执行文件本身是个 wrapper,通过传参来确认使用 server 功能还是使用 client 功能,包装了大部分常用的 adb 命令,非常强大。

三、adb协议简介

这部分特指 PC 上的 server 和 PC 商的 client 如何通信。

1
2
3
4
5
6
7
8
9
10
1. Client <-> Server protocol:
This details the protocol used between ADB clients and the ADB
server itself. The ADB server listens on TCP:localhost:5037.
A client sends a request using the following format:
1. A 4-byte hexadecimal string giving the length of the payload
2. Followed by the payload itself.
For example, to query the ADB server for its internal version number,
the client will do the following:
1. Connect to tcp:localhost:5037
2. Send the string "000Chost:version" to the corresponding socket

这个协议挺简单的,自己手写一个 client 也很简单。

request 消息,前4字节是消息长度,使用使用字符串类型的十六进制数来表示,后面是消息。

response 消息,前4字节是 OKAY 或者 FAIL,后面是4字节字符串类型的十六进制数,长度加数据内容,但变数较大。

要想和安卓手机交互,需要先发送 host:transport 等指令,然后再发送其他指令进行交互。

四、使用 adb 协议访问手机的 tcp 端口

需要仔细阅读并理解 https://android.googlesource.com/platform/system/core/+/master/adb/SERVICES.TXT,虽然这个挺难看懂的,看多了就懂了。adb 命令实现了一部分这里面的功能,没有全部实现,所以 SERVICES.TXT 是更全更完整的。

首先通过 transport 进入和 adbd 通信的状态,这四个是什么意思就不用我多说了吧

1
2
3
4
host:transport:<serial-number>
host:transport-usb
host:transport-local
host:transport-any

然后使用 tcp 访问手机的本地端口

1
2
tcp:<port>
Tries to connect to tcp port <port> on localhost.

成功后,将直接与该手机上的 socket 进行通信。

五、简单 demo

手机上开启一个简单的 tcp 服务(我懒得写http服务了),监听本地的1234端口,例如

1
OP4ADD:/ $ nc -s 127.0.0.1 -p 1234 -L

执行以下的 py 脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import socket


def adb(s: str):
return ('%04x' % len(s)).encode('ascii') + s.encode()


__socket = socket.socket()
__socket.connect(('127.0.0.1', 5037))
__socket.send(adb('host:transport-any'))
# __socket.send(adb('host:transport:%s' % (device)))
assert __socket.recv(4) == b'OKAY'
__socket.send(adb('tcp:%d' % 1234))
assert __socket.recv(4) == b'OKAY'
__socket.send(b"ping\n")
print(__socket.recv(1024))

输入 pong回车,会看到正确的io

1
2
3
OP4ADD:/ $ nc -s 127.0.0.1 -p 1234 -L
ping
pong
1
2
/usr/local/bin/python3.7 /Users/leadroyal/Python_code/py3_hello/test2.py
b'pongn'

好,不需要 adb forward,完美!