text: # Volatools Basic
  # Copyright (C) 2007 Komoku, Inc.
  #
  # This program is free software; you can redistribute it and/or modify
  # it under the terms of the GNU General Public License as published by
  # the Free Software Foundation; either version 2 of the License, or (at
  # your option) any later version.
  #
  # This program is distributed in the hope that it will be useful, but
  # WITHOUT ANY WARRANTY; without even the implied warranty of
  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  # General Public License for more details. 
  #
  # You should have received a copy of the GNU General Public License
  # along with this program; if not, write to the Free Software
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
  #
  
  """
  @author:       AAron Walters and Nick Petroni
  @license:      GNU General Public License 2.0 or later
  @contact:      awalters@komoku.com, npetroni@komoku.com
  @organization: Komoku, Inc.
  """
  
  import struct
  
  builtin_types = {
      'int': (4, 'i'),
      'long': (4, 'i'),
      'unsigned long': (4, 'I'),
      'unsigned int': (4, 'I'),
      'address': (4, 'I'),
      'char': (1, 'c'),
      'unsigned char': (1, 'B'),
      'unsigned short': (2, 'H'),
      'short': (2, 'h'),
      'long long': (8, 'q'),
      'unsigned long long': (8, 'Q'),
      'pointer': (4, 'I'),
  }
  
  
  def obj_size(types, objname):
      if objname not in types:
          raise Exception('Invalid type %s not in types' % objname)
  
      return types[objname][0]
  
  
  def builtin_size(builtin):
      if builtin not in builtin_types:
          raise Exception('Invalid built-in type %s' % builtin)
  
      return builtin_types[builtin][0]
  
  
  def read_value(addr_space, value_type, vaddr):
      """
      Read the low-level value for a built-in type.
      """
  
      if value_type not in builtin_types:
          raise Exception('Invalid built-in type %s' % value_type)
  
      type_unpack_char = builtin_types[value_type][1]
      type_size = builtin_types[value_type][0]
  
      buf = addr_space.read(vaddr, type_size)
      if buf is None:
          return None
  
      try:
          (val,) = struct.unpack(type_unpack_char, buf)
      except Exception:
          return None
  
      return val
  
  
  def read_unicode_string(addr_space, types, member_list, vaddr):
      offset = 0
      if len(member_list) > 1:
          (offset, current_type) = get_obj_offset(types, member_list)
  
      buf = read_obj(addr_space, types, ['_UNICODE_STRING', 'Buffer'], vaddr + offset)
      length = read_obj(addr_space, types, ['_UNICODE_STRING', 'Length'], vaddr + offset)
  
      if length == 0x0:
          return ""
  
      if buf is None or length is None:
          return None
  
      readBuf = read_string(addr_space, types, ['char'], buf, length)
  
      if readBuf is None:
          return None
  
      try:
          readBuf = readBuf.decode('UTF-16').encode('ascii')
      except Exception:
          return None
  
      return readBuf
  
  
  def read_string(addr_space, types, member_list, vaddr, max_length=256):
      offset = 0
      if len(member_list) > 1:
          (offset, current_type) = get_obj_offset(types, member_list)
  
      val = addr_space.read(vaddr + offset, max_length)
  
      return val
  
  
  def read_null_string(addr_space, types, member_list, vaddr, max_length=256):
      string = read_string(addr_space, types, member_list, vaddr, max_length)
  
      if string is None:
          return None
  
      if string.find('\0') == -1:
          return string
      (string, none) = string.split('\0', 1)
      return string
  
  
  def get_obj_offset(types, member_list):
      """
      Returns the (offset, type) pair for a given list
      """
      member_list.reverse()
  
      current_type = member_list.pop()
  
      offset = 0
      current_member = 0
      member_dict = None
  
      while len(member_list) > 0:
          if current_type == 'array':
              if member_dict:
                  current_type = member_dict[current_member][1][2][0]
              if current_type in builtin_types:
                  current_type_size = builtin_size(current_type)
              else:
                  current_type_size = obj_size(types, current_type)
              index = member_list.pop()
              offset += index * current_type_size
              continue
  
          elif current_type not in types:
              raise Exception('Invalid type ' + current_type)
  
          member_dict = types[current_type][1]
  
          current_member = member_list.pop()
          if current_member not in member_dict:
              raise Exception('Invalid member %s in type %s' % (current_member, current_type))
  
          offset += member_dict[current_member][0]
  
          current_type = member_dict[current_member][1][0]
  
      return offset, current_type
  
  
  def read_obj(addr_space, types, member_list, vaddr):
      """
      Read the low-level value for some complex type's member.
      The type must have members.
      """
      if len(member_list) < 2:
          raise Exception('Invalid type/member ' + str(member_list))
  
      (offset, current_type) = get_obj_offset(types, member_list)
      return read_value(addr_space, current_type, vaddr + offset)
  