# -*- coding: utf-8 -*-
"""This file contains a helper library to read binary files."""
from __future__ import unicode_literals
import binascii
import logging
import os
from plaso.lib import py2to3
[docs]def ByteArrayCopyToString(byte_array, codepage='utf-8'):
"""Copies a UTF-8 encoded byte array into a Unicode string.
Args:
byte_array: A byte array containing an UTF-8 encoded string.
codepage: The codepage of the byte stream.
Returns:
A Unicode string.
"""
byte_stream = b''.join(map(chr, byte_array))
return ByteStreamCopyToString(byte_stream, codepage=codepage)
[docs]def ByteStreamCopyToString(byte_stream, codepage='utf-8'):
"""Copies a UTF-8 encoded byte stream into a Unicode string.
Args:
byte_stream: A byte stream containing an UTF-8 encoded string.
codepage: The codepage of the byte stream.
Returns:
A Unicode string.
"""
try:
string = byte_stream.decode(codepage)
except UnicodeDecodeError:
logging.warning(
'Unable to decode {0:s} formatted byte stream.'.format(codepage))
string = byte_stream.decode(codepage, errors='ignore')
string, _, _ = string.partition('\x00')
return string
[docs]def ByteStreamCopyToUTF16Stream(byte_stream, byte_stream_size=None):
"""Reads an UTF-16 formatted stream from a byte stream.
The UTF-16 formatted stream should be terminated by an end-of-string
character (\x00\x00). Otherwise the function reads up to the byte stream size.
Args:
byte_stream: The byte stream that contains the UTF-16 formatted stream.
byte_stream_size: Optional byte stream size or None if the entire
byte stream should be read.
Returns:
String containing the UTF-16 formatted stream.
"""
byte_stream_index = 0
if not byte_stream_size:
byte_stream_size = len(byte_stream)
while byte_stream_index + 1 < byte_stream_size:
if (byte_stream[byte_stream_index] == b'\x00' and
byte_stream[byte_stream_index + 1] == b'\x00'):
break
byte_stream_index += 2
return byte_stream[0:byte_stream_index]
[docs]def ReadUTF16Stream(file_object, offset=None, byte_size=0):
"""Reads an UTF-16 formatted stream from a file-like object.
Reads an UTF-16 formatted stream that's terminated by
an end-of-string character (\x00\x00) or up to the byte size.
Args:
file_object: A file-like object to read the data from.
offset: An offset into the file object data, if -1 or not set
the current location into the file object data is used.
byte_size: Maximum number of bytes to read or 0 if the function
should keep reading up to the end of file.
Returns:
An Unicode string.
"""
if offset is not None:
file_object.seek(offset, os.SEEK_SET)
char_buffer = []
stream_index = 0
char_raw = file_object.read(2)
while char_raw:
if byte_size and stream_index >= byte_size:
break
if b'\x00\x00' in char_raw:
break
char_buffer.append(char_raw)
stream_index += 2
char_raw = file_object.read(2)
return ReadUTF16(b''.join(char_buffer))
[docs]def UTF16StreamCopyToString(byte_stream, byte_stream_size=None):
"""Copies an UTF-16 formatted byte stream to a string.
The UTF-16 formatted byte stream should be terminated by an end-of-string
character (\x00\x00). Otherwise the function reads up to the byte stream size.
Args:
byte_stream: The UTF-16 formatted byte stream.
byte_stream_size: The byte stream size or None if the entire byte stream
should be used.
Returns:
An Unicode string.
"""
utf16_stream = ByteStreamCopyToUTF16Stream(
byte_stream, byte_stream_size=byte_stream_size)
try:
return utf16_stream.decode('utf-16-le')
except (UnicodeDecodeError, UnicodeEncodeError) as exception:
logging.error('Unable to decode string: {0:s} with error: {1!s}'.format(
HexifyBuffer(utf16_stream), exception))
return utf16_stream.decode('utf-16-le', errors='ignore')
[docs]def ArrayOfUTF16StreamCopyToString(byte_stream, byte_stream_size=None):
"""Copies an array of UTF-16 formatted byte streams to an array of strings.
The UTF-16 formatted byte stream should be terminated by an end-of-string
character (\x00\x00). Otherwise the function reads up to the byte stream size.
Args:
byte_stream: The UTF-16 formatted byte stream.
byte_stream_size: The byte stream size or None if the entire byte stream
should be used.
Returns:
An array of Unicode strings.
"""
array_of_strings = []
utf16_stream_start = 0
byte_stream_index = 0
if not byte_stream_size:
byte_stream_size = len(byte_stream)
while byte_stream_index + 1 < byte_stream_size:
if (byte_stream[byte_stream_index] == b'\x00' and
byte_stream[byte_stream_index + 1] == b'\x00'):
if byte_stream_index - utf16_stream_start <= 2:
break
array_of_strings.append(
byte_stream[utf16_stream_start:byte_stream_index].decode(
'utf-16-le'))
utf16_stream_start = byte_stream_index + 2
byte_stream_index += 2
return array_of_strings
[docs]def ArrayOfUTF16StreamCopyToStringTable(byte_stream, byte_stream_size=None):
"""Copies an array of UTF-16 formatted byte streams to a string table.
The string table is a dict of strings with the byte offset as their key.
The UTF-16 formatted byte stream should be terminated by an end-of-string
character (\x00\x00). Otherwise the function reads up to the byte stream size.
Args:
byte_stream: The UTF-16 formatted byte stream.
byte_stream_size: The byte stream size or None if the entire byte stream
should be used.
Returns:
A dict of Unicode strings with the byte offset as their key.
"""
string_table = {}
utf16_stream_start = 0
byte_stream_index = 0
if not byte_stream_size:
byte_stream_size = len(byte_stream)
while byte_stream_index + 1 < byte_stream_size:
if (byte_stream[byte_stream_index] == b'\x00' and
byte_stream[byte_stream_index + 1] == b'\x00'):
if byte_stream_index - utf16_stream_start <= 2:
break
string = byte_stream[utf16_stream_start:byte_stream_index].decode(
'utf-16-le')
string_table[utf16_stream_start] = string
utf16_stream_start = byte_stream_index + 2
byte_stream_index += 2
return string_table
[docs]def ReadUTF16(string_buffer):
"""Returns a decoded UTF-16 string from a string buffer."""
if isinstance(string_buffer, (list, tuple)):
use_buffer = ''.join(string_buffer)
else:
use_buffer = string_buffer
if not isinstance(use_buffer, py2to3.STRING_TYPES):
return ''
try:
return use_buffer.decode('utf-16').replace('\x00', '')
except SyntaxError as exception:
logging.error('Unable to decode string: {0:s} with error: {1!s}.'.format(
HexifyBuffer(string_buffer), exception))
except (UnicodeDecodeError, UnicodeEncodeError) as exception:
logging.error('Unable to decode string: {0:s} with error: {1!s}'.format(
HexifyBuffer(string_buffer), exception))
return use_buffer.decode('utf-16', errors='ignore').replace('\x00', '')
[docs]def HexifyBuffer(string_buffer):
"""Return a string with the hex representation of a string buffer."""
chars = []
for char in string_buffer:
chars.append(binascii.hexlify(char))
return '\\x{0:s}'.format('\\x'.join(chars))