metasploit-framework/lib/rex/proto/adb/message.rb

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