165 lines
3.3 KiB
Ruby
165 lines
3.3 KiB
Ruby
# -*- coding: binary -*-
|
|
|
|
##
|
|
# ADB protocol support
|
|
##
|
|
|
|
module Rex
|
|
module Proto
|
|
module ADB
|
|
|
|
# A Message for the ADB protocol. For documentation see:
|
|
# https://android.googlesource.com/platform/system/core/+/master/adb/protocol.txt
|
|
class Message
|
|
|
|
WORD_WIDTH = 4 # bytes
|
|
WORD_PACK = 'L<'
|
|
|
|
attr_accessor :command
|
|
attr_accessor :arg0
|
|
attr_accessor :arg1
|
|
attr_accessor :data
|
|
|
|
def initialize(arg0, arg1, data)
|
|
self.command = self.class::COMMAND if defined?(self.class::COMMAND)
|
|
self.arg0 = arg0
|
|
self.arg1 = arg1
|
|
self.data = data + "\0"
|
|
end
|
|
|
|
def data_check
|
|
# this check is implemented in adb/transport.cpp, in the send_packet method.
|
|
# it is not crc32 as the docs make it appear, it is just a 32bit sum.
|
|
data.bytes.inject(&:+) & 0xffffffff
|
|
end
|
|
|
|
def magic
|
|
command_word ^ 0xffffffff
|
|
end
|
|
|
|
def command_word
|
|
command.unpack(WORD_PACK)[0]
|
|
end
|
|
|
|
def send_recv(socket)
|
|
socket.print self.serialize
|
|
Message.read socket
|
|
end
|
|
|
|
def serialize
|
|
[
|
|
command_word,
|
|
arg0,
|
|
arg1,
|
|
data.bytes.length,
|
|
data_check,
|
|
magic
|
|
].pack(WORD_PACK+'*') + data
|
|
end
|
|
|
|
def to_s
|
|
[
|
|
"command=#{command}",
|
|
"arg0=0x#{arg0.to_s(16)}",
|
|
"arg1=0x#{arg1.to_s(16)}",
|
|
"data=#{data}"
|
|
].join("\n")
|
|
end
|
|
|
|
def self.read(socket)
|
|
header = socket.recvfrom(6 * WORD_WIDTH)[0]
|
|
command = header[0, WORD_WIDTH]
|
|
arg0 = header[WORD_WIDTH, WORD_WIDTH].unpack(WORD_PACK)[0]
|
|
arg1 = header[WORD_WIDTH*2, WORD_WIDTH].unpack(WORD_PACK)[0]
|
|
payload_len = header[WORD_WIDTH*3, WORD_WIDTH].unpack(WORD_PACK)[0]
|
|
payload = socket.recvfrom(payload_len)[0]
|
|
|
|
klass = MESSAGE_TYPES.find { |klass| klass::COMMAND == command }
|
|
if klass.nil?
|
|
raise "Invalid adb command: #{command}"
|
|
end
|
|
|
|
message = klass.allocate
|
|
message.command = command
|
|
message.arg0 = arg0
|
|
message.arg1 = arg1
|
|
message.data = payload
|
|
message
|
|
end
|
|
|
|
#
|
|
# Subclasses inside Message:: namespace for specific message types
|
|
#
|
|
|
|
class Connect < Message
|
|
COMMAND = "CNXN"
|
|
DEFAULT_VERSION = 0x01000000
|
|
DEFAULT_MAXDATA = 4096
|
|
DEFAULT_IDENTITY = "host::"
|
|
|
|
def initialize(version=DEFAULT_VERSION,
|
|
maxdata=DEFAULT_MAXDATA,
|
|
system_identity_string=DEFAULT_IDENTITY)
|
|
super
|
|
end
|
|
end
|
|
|
|
class Auth < Message
|
|
COMMAND = "AUTH"
|
|
TYPE_TOKEN = 1
|
|
TYPE_SIGNATURE = 2
|
|
|
|
def initialize(type, data)
|
|
super(type, 0, data)
|
|
end
|
|
end
|
|
|
|
class Open < Message
|
|
COMMAND = "OPEN"
|
|
|
|
def initialize(local_id, destination)
|
|
super(local_id, 0, destination)
|
|
end
|
|
end
|
|
|
|
class Ready < Message
|
|
COMMAND = "OKAY"
|
|
|
|
def initialize(local_id, remote_id)
|
|
super(local_id, remote_id, "")
|
|
end
|
|
end
|
|
|
|
class Write < Message
|
|
COMMAND = "WRTE"
|
|
|
|
def initialize(local_id, remote_id, data)
|
|
super
|
|
end
|
|
end
|
|
|
|
class Close < Message
|
|
COMMAND = "CLSE"
|
|
|
|
def initialize(local_id, remote_id)
|
|
super(local_id, remote_id, "")
|
|
end
|
|
end
|
|
|
|
class Sync < Message
|
|
COMMAND = "SYNC"
|
|
|
|
def initialize(online, sequence)
|
|
super(online, sequence, "")
|
|
end
|
|
end
|
|
|
|
# Avoid a dependency on Rails's nice Class#subclasses
|
|
MESSAGE_TYPES = [Connect, Auth, Open, Ready, Write, Close, Sync]
|
|
|
|
end # Message
|
|
|
|
end # ADB
|
|
end # Proto
|
|
end # Rex
|