def watch_scanner(d, addrs, verbose = True, block_wordcount = 4, memo_filename = None):

    now = time.time()
    start_timestamp = now
    output_timestamp = now
    round_number = 0

    # We can use a file on disk as memo for debugging or if we need something
    # that handles large sparse address spaces, but usually cStringIO is fine.
    if memo_filename:
        memo = open(memo_filename, 'w+b')
    else:
        memo = cStringIO.StringIO()

    # Initialize the memo with current memory contents
    for addr, fn in break_up_addresses(d, addrs, block_wordcount):
        memo.seek(addr)
        memo.write(fn())

    # Scan in an endless series of shuffled rounds
    while True:
        round_number += 1
        byte_count = 0

        for addr, fn in break_up_addresses(d, addrs, block_wordcount):

            # Timestamp as soon as the block read comes back.
            # If there's an error, keep on going. TinySCSI complains enough already.
            try:
                block = fn()
            except IOError:
                continue

            timestamp = time.time()
            byte_count += len(block)

            # Keep track of differences with our memo buffer
            memo.seek(addr)
            memo_block = memo.read(len(block))
            if block == memo_block:
                continue
            
            # If the block has changed, memoize it and report word-by-word diffs
            memo.seek(addr)
            memo.write(block)

            block_wordcount = len(block) // 4
            block_words = struct.unpack('<%dI' % block_wordcount, block[:block_wordcount * 4])
            memo_words = struct.unpack('<%dI' % block_wordcount, memo_block[:block_wordcount * 4])

            for i in range(block_wordcount):
                old_value = memo_words[i]
                new_value = block_words[i]
                if new_value != old_value:
                    yield (timestamp, addr + i*4, new_value, old_value)

        # Report status
        now = time.time()
        if verbose and now > output_timestamp + 1.0:
            output_timestamp = now
            print "* scanning %d bytes at %.03f Hz" % (byte_count, round_number / (now - start_timestamp))
Repeatedly scan memory, in randomized order, looking for changes.
dataset/ETHPy150Open scanlime/coastermelt/backdoor/watch.py/watch_scanner