def dump(self, tofile=None, width=16, withpadding=False): """Dump object content to specified file object or to stdout if None. Format is a hexdump with some header information at the beginning, addresses on the left, and data on right. @param tofile file-like object to dump to @param width number of bytes per line (i.e. columns) @param withpadding print padding character instead of '--' @raise ValueError if width is not a positive integer """ if width < 1 or int(width) != width: raise ValueError('width must be a positive integer.') # The integer can be of float type - does not work with bit operations width = int(width) if tofile is None: tofile = sys.stdout # start addr possibly if self.start_addr is not None: cs = self.start_addr.get('CS') ip = self.start_addr.get('IP') eip = self.start_addr.get('EIP') if eip is not None and cs is None and ip is None: tofile.write('EIP = 0x%08X\n' % eip) elif eip is None and cs is not None and ip is not None: tofile.write('CS = 0x%04X, IP = 0x%04X\n' % (cs, ip)) else: tofile.write('start_addr = %r\n' % start_addr) # actual data addresses = dict_keys(self._buf) if addresses: addresses.sort() minaddr = addresses[0] maxaddr = addresses[-1] startaddr = (minaddr // width) * width endaddr = ((maxaddr // width) + 1) * width maxdigits = max(len(hex(endaddr)) - 2, 4) # Less 2 to exclude '0x' templa = '%%0%dX' % maxdigits rangewidth = range_l(width) if withpadding: pad = self.padding else: pad = None for i in range_g(startaddr, endaddr, width): tofile.write(templa % i) tofile.write(' ') s = [] for j in rangewidth: x = self._buf.get(i+j, pad) if x is not None: tofile.write(' %02X' % x) if 32 <= x < 127: # GNU less does not like 0x7F (128 decimal) so we'd better show it as dot s.append(chr(x)) else: s.append('.') else: tofile.write(' --') s.append(' ') tofile.write(' |' + ''.join(s) + '|\n')
def __setitem__(self, addr, byte): """Set byte at address.""" t = type(addr) if t in IntTypes: if addr < 0: raise TypeError('Address should be >= 0.') self._buf[addr] = byte elif t == slice: if not isinstance(byte, (list, tuple)): raise ValueError('Slice operation expects sequence of bytes') start = addr.start stop = addr.stop step = addr.step or 1 if None not in (start, stop): ra = range_l(start, stop, step) if len(ra) != len(byte): raise ValueError('Length of bytes sequence does not match ' 'address range') elif (start, stop) == (None, None): raise TypeError('Unsupported address range') elif start is None: start = stop - len(byte) elif stop is None: stop = start + len(byte) if start < 0: raise TypeError('start address cannot be negative') if stop < 0: raise TypeError('stop address cannot be negative') j = 0 for i in range_g(start, stop, step): self._buf[i] = byte[j] j += 1 else: raise TypeError('Address has unsupported type: %s' % t)
def dump(self, tofile=None, width=16, withpadding=False): """Dump object content to specified file object or to stdout if None. Format is a hexdump with some header information at the beginning, addresses on the left, and data on right. @param tofile file-like object to dump to @param width number of bytes per line (i.e. columns) @param withpadding print padding character instead of '--' @raise ValueError if width is not a positive integer """ if not isinstance(width, int) or width < 1: raise ValueError('width must be a positive integer.') # The integer can be of float type - does not work with bit operations width = int(width) if tofile is None: tofile = sys.stdout # start addr possibly if self.start_addr is not None: cs = self.start_addr.get('CS') ip = self.start_addr.get('IP') eip = self.start_addr.get('EIP') if eip is not None and cs is None and ip is None: tofile.write('EIP = 0x%08X\n' % eip) elif eip is None and cs is not None and ip is not None: tofile.write('CS = 0x%04X, IP = 0x%04X\n' % (cs, ip)) else: tofile.write('start_addr = %r\n' % start_addr) # actual data addresses = dict_keys(self._buf) if addresses: addresses.sort() minaddr = addresses[0] maxaddr = addresses[-1] startaddr = (minaddr // width) * width endaddr = ((maxaddr // width) + 1) * width maxdigits = max(len(hex(endaddr)) - 2, 4) # Less 2 to exclude '0x' templa = '%%0%dX' % maxdigits rangewidth = range_l(width) if withpadding: pad = self.padding else: pad = None for i in range_g(startaddr, endaddr, width): tofile.write(templa % i) tofile.write(' ') s = [] for j in rangewidth: x = self._buf.get(i + j, pad) if x is not None: tofile.write(' %02X' % x) if 32 <= x < 127: # GNU less does not like 0x7F (128 decimal) so we'd better show it as dot s.append(chr(x)) else: s.append('.') else: tofile.write(' --') s.append(' ') tofile.write(' |' + ''.join(s) + '|\n')
def dump(self, tofile=None): """Dump object content to specified file object or to stdout if None. Format is a hexdump with some header information at the beginning, addresses on the left, and data on right. @param tofile file-like object to dump to """ if tofile is None: tofile = sys.stdout # start addr possibly if self.start_addr is not None: cs = self.start_addr.get('CS') ip = self.start_addr.get('IP') eip = self.start_addr.get('EIP') if eip is not None and cs is None and ip is None: tofile.write('EIP = 0x%08X\n' % eip) elif eip is None and cs is not None and ip is not None: tofile.write('CS = 0x%04X, IP = 0x%04X\n' % (cs, ip)) else: tofile.write('start_addr = %r\n' % start_addr) # actual data addresses = dict_keys(self._buf) if addresses: addresses.sort() minaddr = addresses[0] maxaddr = addresses[-1] startaddr = int(minaddr>>4)*16 endaddr = int((maxaddr>>4)+1)*16 maxdigits = max(len(str(endaddr)), 4) templa = '%%0%dX' % maxdigits range16 = range_l(16) for i in range_g(startaddr, endaddr, 16): tofile.write(templa % i) tofile.write(' ') s = [] for j in range16: x = self._buf.get(i+j) if x is not None: tofile.write(' %02X' % x) if 32 <= x < 127: # GNU less does not like 0x7F (128 decimal) so we'd better show it as dot s.append(chr(x)) else: s.append('.') else: tofile.write(' --') s.append(' ') tofile.write(' |' + ''.join(s) + '|\n')
def dump(self, tofile=None): """Dump object content to specified file object or to stdout if None. Format is a hexdump with some header information at the beginning, addresses on the left, and data on right. @param tofile file-like object to dump to """ if tofile is None: tofile = sys.stdout # start addr possibly if self.start_addr is not None: cs = self.start_addr.get('CS') ip = self.start_addr.get('IP') eip = self.start_addr.get('EIP') if eip is not None and cs is None and ip is None: tofile.write('EIP = 0x%08X\n' % eip) elif eip is None and cs is not None and ip is not None: tofile.write('CS = 0x%04X, IP = 0x%04X\n' % (cs, ip)) else: tofile.write('start_addr = %r\n' % start_addr) # actual data addresses = dict_keys(self._buf) if addresses: addresses.sort() minaddr = addresses[0] maxaddr = addresses[-1] startaddr = int(minaddr >> 4) * 16 endaddr = int((maxaddr >> 4) + 1) * 16 maxdigits = max(len(str(endaddr)), 4) templa = '%%0%dX' % maxdigits range16 = range_l(16) for i in range_g(startaddr, endaddr, 16): tofile.write(templa % i) tofile.write(' ') s = [] for j in range16: x = self._buf.get(i + j) if x is not None: tofile.write(' %02X' % x) if 32 <= x < 127: # GNU less does not like 0x7F (128 decimal) so we'd better show it as dot s.append(chr(x)) else: s.append('.') else: tofile.write(' --') s.append(' ') tofile.write(' |' + ''.join(s) + '|\n')
def write_hex_file(self, f, write_start_addr=True): """Write data to file f in HEX format. @param f filename or file-like object for writing @param write_start_addr enable or disable writing start address record to file (enabled by default). If there is no start address in obj, nothing will be written regardless of this setting. """ fwrite = getattr(f, "write", None) if fwrite: fobj = f fclose = None else: fobj = open(f, 'w') fwrite = fobj.write fclose = fobj.close # Translation table for uppercasing hex ascii string. # timeit shows that using hexstr.translate(table) # is faster than hexstr.upper(): # 0.452ms vs. 0.652ms (translate vs. upper) if sys.version_info[0] >= 3: # Python 3 table = bytes(range_l(256)).upper() else: # Python 2 table = ''.join(chr(i).upper() for i in range_g(256)) # start address record if any if self.start_addr and write_start_addr: keys = dict_keys(self.start_addr) keys.sort() bin = array('B', asbytes('\0'*9)) if keys == ['CS','IP']: # Start Segment Address Record bin[0] = 4 # reclen bin[1] = 0 # offset msb bin[2] = 0 # offset lsb bin[3] = 3 # rectyp cs = self.start_addr['CS'] bin[4] = (cs >> 8) & 0x0FF bin[5] = cs & 0x0FF ip = self.start_addr['IP'] bin[6] = (ip >> 8) & 0x0FF bin[7] = ip & 0x0FF bin[8] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') elif keys == ['EIP']: # Start Linear Address Record bin[0] = 4 # reclen bin[1] = 0 # offset msb bin[2] = 0 # offset lsb bin[3] = 5 # rectyp eip = self.start_addr['EIP'] bin[4] = (eip >> 24) & 0x0FF bin[5] = (eip >> 16) & 0x0FF bin[6] = (eip >> 8) & 0x0FF bin[7] = eip & 0x0FF bin[8] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') else: if fclose: fclose() raise InvalidStartAddressValueError(start_addr=self.start_addr) # data addresses = dict_keys(self._buf) addresses.sort() addr_len = len(addresses) if addr_len: minaddr = addresses[0] maxaddr = addresses[-1] if maxaddr > 65535: need_offset_record = True else: need_offset_record = False high_ofs = 0 cur_addr = minaddr cur_ix = 0 while cur_addr <= maxaddr: if need_offset_record: bin = array('B', asbytes('\0'*7)) bin[0] = 2 # reclen bin[1] = 0 # offset msb bin[2] = 0 # offset lsb bin[3] = 4 # rectyp high_ofs = int(cur_addr>>16) b = divmod(high_ofs, 256) bin[4] = b[0] # msb of high_ofs bin[5] = b[1] # lsb of high_ofs bin[6] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') while True: # produce one record low_addr = cur_addr & 0x0FFFF # chain_len off by 1 chain_len = min(15, 65535-low_addr, maxaddr-cur_addr) # search continuous chain stop_addr = cur_addr + chain_len if chain_len: ix = bisect_right(addresses, stop_addr, cur_ix, min(cur_ix+chain_len+1, addr_len)) chain_len = ix - cur_ix # real chain_len # there could be small holes in the chain # but we will catch them by try-except later # so for big continuous files we will work # at maximum possible speed else: chain_len = 1 # real chain_len bin = array('B', asbytes('\0'*(5+chain_len))) b = divmod(low_addr, 256) bin[1] = b[0] # msb of low_addr bin[2] = b[1] # lsb of low_addr bin[3] = 0 # rectype try: # if there is small holes we'll catch them for i in range_g(chain_len): bin[4+i] = self._buf[cur_addr+i] except KeyError: # we catch a hole so we should shrink the chain chain_len = i bin = bin[:5+i] bin[0] = chain_len bin[4+chain_len] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') # adjust cur_addr/cur_ix cur_ix += chain_len if cur_ix < addr_len: cur_addr = addresses[cur_ix] else: cur_addr = maxaddr + 1 break high_addr = int(cur_addr>>16) if high_addr > high_ofs: break # end-of-file record fwrite(":00000001FF\n") if fclose: fclose()
def write_hex_file(self, f, write_start_addr=True): """Write data to file f in HEX format. @param f filename or file-like object for writing @param write_start_addr enable or disable writing start address record to file (enabled by default). If there is no start address in obj, nothing will be written regardless of this setting. """ fwrite = getattr(f, "write", None) if fwrite: fobj = f fclose = None else: fobj = open(f, 'w') fwrite = fobj.write fclose = fobj.close # Translation table for uppercasing hex ascii string. # timeit shows that using hexstr.translate(table) # is faster than hexstr.upper(): # 0.452ms vs. 0.652ms (translate vs. upper) if sys.version_info[0] >= 3: # Python 3 table = bytes(range_l(256)).upper() else: # Python 2 table = ''.join(chr(i).upper() for i in range_g(256)) # start address record if any if self.start_addr and write_start_addr: keys = dict_keys(self.start_addr) keys.sort() bin = array('B', asbytes('\0' * 9)) if keys == ['CS', 'IP']: # Start Segment Address Record bin[0] = 4 # reclen bin[1] = 0 # offset msb bin[2] = 0 # offset lsb bin[3] = 3 # rectyp cs = self.start_addr['CS'] bin[4] = (cs >> 8) & 0x0FF bin[5] = cs & 0x0FF ip = self.start_addr['IP'] bin[6] = (ip >> 8) & 0x0FF bin[7] = ip & 0x0FF bin[8] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') elif keys == ['EIP']: # Start Linear Address Record bin[0] = 4 # reclen bin[1] = 0 # offset msb bin[2] = 0 # offset lsb bin[3] = 5 # rectyp eip = self.start_addr['EIP'] bin[4] = (eip >> 24) & 0x0FF bin[5] = (eip >> 16) & 0x0FF bin[6] = (eip >> 8) & 0x0FF bin[7] = eip & 0x0FF bin[8] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') else: if fclose: fclose() raise InvalidStartAddressValueError(start_addr=self.start_addr) # data addresses = dict_keys(self._buf) addresses.sort() addr_len = len(addresses) if addr_len: minaddr = addresses[0] maxaddr = addresses[-1] if maxaddr > 65535: need_offset_record = True else: need_offset_record = False high_ofs = 0 cur_addr = minaddr cur_ix = 0 while cur_addr <= maxaddr: if need_offset_record: bin = array('B', asbytes('\0' * 7)) bin[0] = 2 # reclen bin[1] = 0 # offset msb bin[2] = 0 # offset lsb bin[3] = 4 # rectyp high_ofs = int(cur_addr >> 16) b = divmod(high_ofs, 256) bin[4] = b[0] # msb of high_ofs bin[5] = b[1] # lsb of high_ofs bin[6] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') while True: # produce one record low_addr = cur_addr & 0x0FFFF # chain_len off by 1 chain_len = min(15, 65535 - low_addr, maxaddr - cur_addr) # search continuous chain stop_addr = cur_addr + chain_len if chain_len: ix = bisect_right( addresses, stop_addr, cur_ix, min(cur_ix + chain_len + 1, addr_len)) chain_len = ix - cur_ix # real chain_len # there could be small holes in the chain # but we will catch them by try-except later # so for big continuous files we will work # at maximum possible speed else: chain_len = 1 # real chain_len bin = array('B', asbytes('\0' * (5 + chain_len))) b = divmod(low_addr, 256) bin[1] = b[0] # msb of low_addr bin[2] = b[1] # lsb of low_addr bin[3] = 0 # rectype try: # if there is small holes we'll catch them for i in range_g(chain_len): bin[4 + i] = self._buf[cur_addr + i] except KeyError: # we catch a hole so we should shrink the chain chain_len = i bin = bin[:5 + i] bin[0] = chain_len bin[4 + chain_len] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(bin.tostring()).translate(table)) + '\n') # adjust cur_addr/cur_ix cur_ix += chain_len if cur_ix < addr_len: cur_addr = addresses[cur_ix] else: cur_addr = maxaddr + 1 break high_addr = int(cur_addr >> 16) if high_addr > high_ofs: break # end-of-file record fwrite(":00000001FF\n") if fclose: fclose()