示例#1
0
def do_equiv_instrs(instrs, gen_patched, all_diffs=None):
  """Check each instruction if it has an equivalent one and computes the
  changed bytes set. Optionally, it can generate a changed file with the
  equivalent instructions. Returns the list of changed bytes."""

  changed_bytes = set()
  changed = []

  for ins in instrs: 
    if check_equiv(ins):
      changed.append(ins)

  diff = inp.get_diff(changed)

  if gen_patched:
    inp.patch(diff, "equiv")

  changed_bytes.update((ea for ea, orig, new in diff))

  for ins in changed:
    if all_diffs != None:
      all_diffs.append(inp.get_diff([ins]))
    ins.reset_changed()

  return changed_bytes
示例#2
0
文件: swap.py 项目: terry2012/ropf
def do_single_swaps(swaps, gen_patched, all_diffs=None):
    """Applies one swap at a time and optionally generates the patched .dll.
    Returns a set of the linear addresses of the changed bytes."""

    changed_bytes = set()

    for i, swap in enumerate(swaps):
        success, changed = apply_swap_comb([swap])

        if success:
            diff = inp.get_diff(changed)
            if gen_patched:
                inp.patch(diff, "swap-%06d" % i)
            if all_diffs != None:
                all_diffs.append(diff)
            changed_bytes.update((ea for ea, orig, curr in diff))

        for ins in changed:
            ins.reset_changed()

    return changed_bytes
示例#3
0
文件: swap.py 项目: kevinkoo001/ropf
def do_single_swaps(swaps, gen_patched, all_diffs=None):
    """Applies one swap at a time and optionally generates the patched .dll.
    Returns a set of the linear addresses of the changed bytes."""

    changed_bytes = set()

    for i, swap in enumerate(swaps):
        success, changed = apply_swap_comb([swap])

        if success:
            diff = inp.get_diff(changed)
            if gen_patched:
                inp.patch(diff, "swap-%06d" % i)
            if all_diffs != None:
                all_diffs.append(diff)
            changed_bytes.update((ea for ea, orig, curr in diff))

        for ins in changed:
            ins.reset_changed()

    return changed_bytes
示例#4
0
文件: eval.py 项目: terry2012/ropf
def get_entropy(f, g, live_regs, swaps, preservs, avail_regs):
    """Given a gadget, it returns the number of different ways that it can be
    broken. A second return value indicates whether this gadget can be
    eliminated (entropy)."""
    def _add_if_hit(g, diff, diffs):
        gdiff = tuple(d for d in diff if g.start <= d[0] < g.end)  # d[0] is ea
        old_diffs_len = len(diffs)
        if len(gdiff) > 0:
            diffs.add(gdiff)
        return old_diffs_len != len(diffs)

    entropy = 1
    eliminated = False

    # 1. swaps
    swap_diffs = set()
    gswaps = []  # filter swaps that actually hit the gadget
    gbytes = range(g.start, g.end)

    for s in swaps:
        # sbytes = [xrange(i.addr, i.addr+i.inst_len) for i in swap.get_instrs()]
        # if set(itertools.chain(*sbytes)) & set(xrange(g.start, g.end)):
        #  gswaps.append(swap)
        for ins in s.get_instrs():
            if any(ins.addr <= b < ins.addr + len(ins.bytes) for b in gbytes):
                gswaps.append(s)
                break

    # XXX use only at most 10 swaps here .. otherwise it takes forever
    for i, swap_comb in enumerate(swap.gen_swap_combinations(gswaps[:10])):
        success, changed = swap.apply_swap_comb(swap_comb)
        if not success:
            continue
        diff = inp.get_diff(changed)
        _add_if_hit(g, diff, swap_diffs)
        for ins in changed:
            ins.reset_changed()
        if i > 1000:
            print "killing after 1000 swap combinations"
            break

    entropy *= len(swap_diffs) + 1
    eliminated = any(g.addrs[-1] == d[1] for d in itertools.chain(*swap_diffs))

    print "\tswaps:", len(swap_diffs)

    # 2. preservs
    preserv_diffs = set()

    # 2.1 global substitution
    implicit = reduce(lambda x, y: x | y,
                      [i.implicit for i in f.instrs if not i.f_exit], set())
    preserv_regs = [
        reg for reg, pushes, pops in preservs if reg not in implicit
    ]
    preserv_combs = []

    if len(preserv_regs) >= 2:
        preserv_combs.extend(itertools.combinations(preserv_regs, 2))

    preserv_combs.extend(itertools.product(preserv_regs, avail_regs))

    for r1, r2 in preserv_combs:
        for ins in f.instrs:
            if not ins.swap_registers(r1, r2):
                break
        else:
            diff = inp.get_diff(f.instrs)
            _add_if_hit(g, diff, preserv_diffs)
        for ins in f.instrs:
            ins.reset_changed()

    # 2.2 reorder pushes/pops
    # XXX: a preserved register is used only after it's pushed!
    #      and only before it's poped!
    # augment each ins in the preservs with blocks and find first/last
    preserv_blocks = set()

    for reg, pushes, pops in preservs:
        for ins in itertools.chain(pushes, pops):
            ins.block = next((b for b in f.blocks if ins in b.instrs), None)
            if not hasattr(ins.block, 'first'):
                ins.block.first = ins.block.instrs.index(ins)
                ins.block.last = ins.block.instrs.index(ins)
            else:
                ins.block.first = min(ins.block.first,
                                      ins.block.instrs.index(ins))
                ins.block.last = max(ins.block.last,
                                     ins.block.instrs.index(ins))
            preserv_blocks.add(ins.block)

    # group preservs in the same block and reorder them
    preservs_groups = []

    for reg, pushes, pops in preservs:
        if len(preservs_groups) == 0:
            preservs_groups.append([(reg, pushes, pops)])
            continue
        for group in preservs_groups:
            if (all(p1.block == p2.block
                    for p1, p2 in zip(group[0][1], pushes))
                    and all(p1.block == p2.block
                            for p1, p2 in zip(group[0][2], pops))):
                group.append((reg, pushes, pops))
                break
            else:
                preservs_groups.append([(reg, pushes, pops)])

    # do reorder of pushes/pops
    for group in (g for g in preservs_groups if len(g) > 1):
        for group_perm in itertools.permutations(group):
            for block in preserv_blocks:
                block.rinstrs = block.instrs[:]
                block.curr_first = block.first
                block.curr_last = block.last
            for reg, pushes, pops in group:
                for push in pushes:
                    push.block.rinstrs.remove(push)
                    push.block.rinstrs.insert(push.block.curr_first, push)
                    push.block.curr_first += 1
                for pop in reversed(pops):
                    pop.block.rinstrs.remove(pop)
                    pop.block.rinstrs.insert(pop.block.curr_last - 1, pop)
                    pop.block.curr_last -= 1
            diff = set()
            for block in preserv_blocks:
                diff.update(inp.get_block_diff(block))
            _add_if_hit(g, diff, preserv_diffs)

    entropy *= len(preserv_diffs) + 1
    eliminated = eliminated or any(g.addrs[-1] == d[1]
                                   for d in itertools.chain(*preserv_diffs))

    print "\tpreservs:", len(preserv_diffs)

    # overlaping gadgets!
    if g.overlap:

        # 3. equiv
        equiv_instrs = []

        for ins in f.instrs:
            if equiv.check_equiv(ins):
                diff = inp.get_diff([ins])
                if any(g.start <= ea < g.end for ea, orig, curr in diff):
                    equiv_instrs.append(ins)
                eliminated = eliminated or any(g.addrs[-1] == d[1]
                                               for d in diff)
                ins.reset_changed()

        sr_equiv = [
            i for i in equiv_instrs if i.bytes[i.opc_off] in objs.same_regs
        ]
        entropy *= 2**(len(equiv_instrs) - len(sr_equiv))
        entropy *= 5**len(sr_equiv)

        print "\tequiv (instrs):", len(
            equiv_instrs), "(%d sr_equiv)" % len(sr_equiv)

        # 4. reorder
        reorder_diffs = set()

        # find the block(s) of the gadget
        for block in f.blocks:
            if block.begin <= g.start < block.end or block.begin <= g.end < block.end or (
                    g.start <= block.begin and g.end >= block.end):
                # dag = reorder.BuildBBDependenceDAG(block)
                # for order in itertools.permutations(block.instrs):
                #  # all should be forward edges, from left to right
                #  if any(order.index(u) < order.index(v) for u, v in dag.edges()):
                #    continue
                for order in reorder.gen_topological_sortings(block):
                    block.rinstrs = order
                    diff = inp.get_block_diff(block)
                    _add_if_hit(g, diff, reorder_diffs)

        eliminated = any(g.addrs[-1] == d[1]
                         for d in itertools.chain(*reorder_diffs))
        entropy *= len(reorder_diffs) + 1

        print "\treorder:", len(reorder_diffs)

    if not g.overlap:
        # 4. reorder in non-overlapping
        reorder_breakes = 0

        # find the block(s) of the gadget
        for block in f.blocks:
            if block.begin <= g.start < block.end or block.begin <= g.end < block.end or (
                    g.start <= block.begin and g.end >= block.end):
                # print "will work on block:", block
                for order in reorder.gen_topological_sortings(block):
                    # print "test this order:", order
                    block.rinstrs = order
                    # update the address of reordered instrs
                    for i, rins in enumerate(block.rinstrs):
                        if i == 0:
                            rins.raddr = block.begin
                        else:
                            rins.raddr = block.rinstrs[i - 1].raddr + len(
                                block.rinstrs[i - 1].bytes)
                    for ins in (f.code[a] for a in g.addrs if a in f.code):
                        if ins.addr != ins.raddr and ins.raddr < g.start or ins.raddr >= g.end:
                            reorder_breakes += 1
                            break

        entropy *= reorder_breakes + 1

        print "\treorder (non-overlapping):", reorder_breakes

    print "\tgadget entropy:", entropy, "eliminated:", eliminated

    return entropy, eliminated
示例#5
0
文件: eval.py 项目: kevinkoo001/ropf
def get_entropy(f, g, live_regs, swaps, preservs, avail_regs):
    """Given a gadget, it returns the number of different ways that it can be
    broken. A second return value indicates whether this gadget can be
    eliminated (entropy)."""

    def _add_if_hit(g, diff, diffs):
        gdiff = tuple(d for d in diff if g.start <= d[0] < g.end)  # d[0] is ea
        old_diffs_len = len(diffs)
        if len(gdiff) > 0:
            diffs.add(gdiff)
        return old_diffs_len != len(diffs)

    entropy = 1
    eliminated = False

    # 1. swaps
    swap_diffs = set()
    gswaps = []  # filter swaps that actually hit the gadget
    gbytes = range(g.start, g.end)

    for s in swaps:
        # sbytes = [xrange(i.addr, i.addr+i.inst_len) for i in swap.get_instrs()]
        # if set(itertools.chain(*sbytes)) & set(xrange(g.start, g.end)):
        #  gswaps.append(swap)
        for ins in s.get_instrs():
            if any(ins.addr <= b < ins.addr + len(ins.bytes) for b in gbytes):
                gswaps.append(s)
                break

    # XXX use only at most 10 swaps here .. otherwise it takes forever
    for i, swap_comb in enumerate(swap.gen_swap_combinations(gswaps[:10])):
        success, changed = swap.apply_swap_comb(swap_comb)
        if not success:
            continue
        diff = inp.get_diff(changed)
        _add_if_hit(g, diff, swap_diffs)
        for ins in changed:
            ins.reset_changed()
        if i > 1000:
            print "killing after 1000 swap combinations"
            break

    entropy *= len(swap_diffs) + 1
    eliminated = any(g.addrs[-1] == d[1] for d in itertools.chain(*swap_diffs))

    print "\tswaps:", len(swap_diffs)

    # 2. preservs
    preserv_diffs = set()

    # 2.1 global substitution
    implicit = reduce(lambda x, y: x | y, [i.implicit for i in f.instrs if not i.f_exit], set())
    preserv_regs = [reg for reg, pushes, pops in preservs if reg not in implicit]
    preserv_combs = []

    if len(preserv_regs) >= 2:
        preserv_combs.extend(itertools.combinations(preserv_regs, 2))

    preserv_combs.extend(itertools.product(preserv_regs, avail_regs))

    for r1, r2 in preserv_combs:
        for ins in f.instrs:
            if not ins.swap_registers(r1, r2):
                break
        else:
            diff = inp.get_diff(f.instrs)
            _add_if_hit(g, diff, preserv_diffs)
        for ins in f.instrs:
            ins.reset_changed()

    # 2.2 reorder pushes/pops
    # XXX: a preserved register is used only after it's pushed!
    #      and only before it's poped!
    # augment each ins in the preservs with blocks and find first/last
    preserv_blocks = set()

    for reg, pushes, pops in preservs:
        for ins in itertools.chain(pushes, pops):
            ins.block = next((b for b in f.blocks if ins in b.instrs), None)
            if not hasattr(ins.block, 'first'):
                ins.block.first = ins.block.instrs.index(ins)
                ins.block.last = ins.block.instrs.index(ins)
            else:
                ins.block.first = min(ins.block.first, ins.block.instrs.index(ins))
                ins.block.last = max(ins.block.last, ins.block.instrs.index(ins))
            preserv_blocks.add(ins.block)

    # group preservs in the same block and reorder them
    preservs_groups = []

    for reg, pushes, pops in preservs:
        if len(preservs_groups) == 0:
            preservs_groups.append([(reg, pushes, pops)])
            continue
        for group in preservs_groups:
            if (all(p1.block == p2.block for p1, p2 in zip(group[0][1], pushes)) and
                    all(p1.block == p2.block for p1, p2 in zip(group[0][2], pops))):
                group.append((reg, pushes, pops))
                break
            else:
                preservs_groups.append([(reg, pushes, pops)])

    # do reorder of pushes/pops
    for group in (g for g in preservs_groups if len(g) > 1):
        for group_perm in itertools.permutations(group):
            for block in preserv_blocks:
                block.rinstrs = block.instrs[:]
                block.curr_first = block.first
                block.curr_last = block.last
            for reg, pushes, pops in group:
                for push in pushes:
                    push.block.rinstrs.remove(push)
                    push.block.rinstrs.insert(push.block.curr_first, push)
                    push.block.curr_first += 1
                for pop in reversed(pops):
                    pop.block.rinstrs.remove(pop)
                    pop.block.rinstrs.insert(pop.block.curr_last - 1, pop)
                    pop.block.curr_last -= 1
            diff = set()
            for block in preserv_blocks:
                diff.update(inp.get_block_diff(block))
            _add_if_hit(g, diff, preserv_diffs)

    entropy *= len(preserv_diffs) + 1
    eliminated = eliminated or any(g.addrs[-1] == d[1] for d in itertools.chain(*preserv_diffs))

    print "\tpreservs:", len(preserv_diffs)

    # overlaping gadgets!
    if g.overlap:

        # 3. equiv
        equiv_instrs = []

        for ins in f.instrs:
            if equiv.check_equiv(ins):
                diff = inp.get_diff([ins])
                if any(g.start <= ea < g.end for ea, orig, curr in diff):
                    equiv_instrs.append(ins)
                eliminated = eliminated or any(g.addrs[-1] == d[1] for d in diff)
                ins.reset_changed()

        sr_equiv = [i for i in equiv_instrs if i.bytes[i.opc_off] in objs.same_regs]
        entropy *= 2 ** (len(equiv_instrs) - len(sr_equiv))
        entropy *= 5 ** len(sr_equiv)

        print "\tequiv (instrs):", len(equiv_instrs), "(%d sr_equiv)" % len(sr_equiv)

        # 4. reorder
        reorder_diffs = set()

        # find the block(s) of the gadget
        for block in f.blocks:
            if block.begin <= g.start < block.end or block.begin <= g.end < block.end or (
                            g.start <= block.begin and g.end >= block.end):
                # dag = reorder.BuildBBDependenceDAG(block)
                # for order in itertools.permutations(block.instrs):
                #  # all should be forward edges, from left to right
                #  if any(order.index(u) < order.index(v) for u, v in dag.edges()):
                #    continue
                for order in reorder.gen_topological_sortings(block):
                    block.rinstrs = order
                    diff = inp.get_block_diff(block)
                    _add_if_hit(g, diff, reorder_diffs)

        eliminated = any(g.addrs[-1] == d[1] for d in itertools.chain(*reorder_diffs))
        entropy *= len(reorder_diffs) + 1

        print "\treorder:", len(reorder_diffs)

    if not g.overlap:
        # 4. reorder in non-overlapping
        reorder_breakes = 0

        # find the block(s) of the gadget
        for block in f.blocks:
            if block.begin <= g.start < block.end or block.begin <= g.end < block.end or (
                            g.start <= block.begin and g.end >= block.end):
                # print "will work on block:", block
                for order in reorder.gen_topological_sortings(block):
                    # print "test this order:", order
                    block.rinstrs = order
                    # update the address of reordered instrs
                    for i, rins in enumerate(block.rinstrs):
                        if i == 0:
                            rins.raddr = block.begin
                        else:
                            rins.raddr = block.rinstrs[i - 1].raddr + len(block.rinstrs[i - 1].bytes)
                    for ins in (f.code[a] for a in g.addrs if a in f.code):
                        if ins.addr != ins.raddr and ins.raddr < g.start or ins.raddr >= g.end:
                            reorder_breakes += 1
                            break

        entropy *= reorder_breakes + 1

        print "\treorder (non-overlapping):", reorder_breakes

    print "\tgadget entropy:", entropy, "eliminated:", eliminated

    return entropy, eliminated
示例#6
0
文件: preserv.py 项目: vpappas/orp
def do_reg_preservs(instrs,
                    blocks,
                    preservs,
                    avail_regs,
                    gen_patched,
                    all_diffs=None):
    """Changes the preserved registers within the given instructions and optionally 
  generates instances of the input file with these permuted register preservations.
  Returns the changed bytes set."""

    changed_bytes = set()

    #f_exit instructions (ret) implicitly use the preserved registers.. exclude them!
    implicit = reduce(lambda x, y: x | y,
                      [i.implicit for i in instrs if not i.f_exit], set())

    regs = [reg for reg, pushes, pops in preservs if reg not in implicit]

    if not preservs or (len(regs) == 1 and not avail_regs):
        return changed_bytes

    # preserved registers that are implicitly used should not be touched
    # hmm .. rotation should still happen even when all regs are implicitly used..
    #if not regs:
    #  return changed_bytes
    if len(regs) == 1:  #we know we have available
        regs.append(avail_regs.pop())

    if len(regs) >= 2:
        # we do need the combinations here, because we want to change as much
        # as possible in case a register cannot be swapped (e.g. weird modrm/sib)
        for r1, r2 in itertools.combinations(regs, 2):
            for ins in instrs:
                if not ins.swap_registers(r1, r2):
                    #print "MAYBEBUG: broke for", ins.disas, "in register preservations!", r1, r2, ins.regs
                    break
            else:  #no break!
                diff = inp.get_diff(instrs)
                if gen_patched:
                    inp.patch(diff, "preserv-%s-%s" % (r1, r2))
                if all_diffs != None:
                    all_diffs.append(diff)
                changed_bytes.update((ea for ea, orig, curr in diff))
            for ins in instrs:
                ins.reset_changed()

    # the second part bellow can be useful in cases where global swap
    # cannot  be applied due to implicit uses

    # group preservs in the same block and reorder them
    # augment each ins in the preservs with blocks
    for reg, pushes, pops in preservs:
        for ins in itertools.chain(pushes, pops):
            ins.block = next((b for b in blocks if ins in b.instrs), None)

    preservs_groups = []

    for reg, pushes, pops in preservs:
        if len(preservs_groups) == 0:
            preservs_groups.append([[reg, pushes, pops]])
            continue
        for group in preservs_groups:
            if (all(p1.block == p2.block
                    for p1, p2 in zip(group[0][1], pushes))
                    and all(p1.block == p2.block
                            for p1, p2 in zip(group[0][2], pops))):
                group.append([reg, pushes, pops])
                break
            else:
                preservs_groups.append([[reg, pushes, pops]])
    #print "grouping!:", '\n'.join((str(g) for g in preservs_groups))

    # reorder (rotate) the pushes/pops by placing the last one in the
    # first's position and shifting all the other instrs down
    # TODO: breaks if esp is used within the reordered block! (BIB @ 070012F1)
    for group in (g for g in preservs_groups if len(g) > 1):
        # group them by block
        #pushes_by_block = {}
        ins_by_block = {}
        for reg, pushes, pops in group:
            for ins in itertools.chain(pushes, pops):
                if (ins.block, ins.mnem) not in ins_by_block:
                    ins_by_block[(ins.block, ins.mnem)] = []
                ins_by_block[(ins.block, ins.mnem)].append(ins)
        # in each block, move the latest ins to the ealiest's position
        for (block, mnem), instrs in ins_by_block.iteritems():
            instrs.sort(key=lambda x: x.addr)
            first = block.instrs.index(instrs[0])
            last = block.instrs.index(instrs[-1])
            block.rinstrs = block.instrs[:first]
            block.rinstrs.append(block.instrs[last])
            block.rinstrs.extend(block.instrs[first:last])
            block.rinstrs.extend(block.instrs[last + 1:])
            diff = inp.get_block_diff(block)
            changed_bytes.update((ea for ea, orig, curr in diff))
            #XXX should also generate patched files ...

    return changed_bytes
示例#7
0
def do_reg_preservs(instrs, blocks, preservs, avail_regs, gen_patched, all_diffs=None):
    """Changes the preserved registers within the given instructions and optionally
    generates instances of the input file with these permuted register preservations.
    Returns the changed bytes set."""

    changed_bytes = set()

    # f_exit instructions (ret) implicitly use the preserved registers.. exclude them!
    implicit = reduce(lambda x, y: x | y, [i.implicit for i in instrs if not i.f_exit], set())

    regs = [reg for reg, pushes, pops in preservs if reg not in implicit]

    if not preservs or (len(regs) == 1 and not avail_regs):
        return changed_bytes

    # preserved registers that are implicitly used should not be touched
    # hmm .. rotation should still happen even when all regs are implicitly used..
    # if not regs:
    #  return changed_bytes
    if len(regs) == 1:  # we know we have available
        regs.append(avail_regs.pop())

    if len(regs) >= 2:
        # we do need the combinations here, because we want to change as much
        # as possible in case a register cannot be swapped (e.g. weird modrm/sib)
        for r1, r2 in itertools.combinations(regs, 2):
            for ins in instrs:
                if not ins.swap_registers(r1, r2):
                    # print "MAYBEBUG: broke for", ins.disas, "in register preservations!", r1, r2, ins.regs
                    break
            else:  # no break!
                diff = inp.get_diff(instrs)
                if gen_patched:
                    inp.patch(diff, "preserv-%s-%s" % (r1, r2))
                if all_diffs != None:
                    all_diffs.append(diff)
                changed_bytes.update((ea for ea, orig, curr in diff))
            for ins in instrs:
                ins.reset_changed()

    # the second part bellow can be useful in cases where global swap
    # cannot  be applied due to implicit uses

    # group preservs in the same block and reorder them
    # augment each ins in the preservs with blocks
    for reg, pushes, pops in preservs:
        for ins in itertools.chain(pushes, pops):
            ins.block = next((b for b in blocks if ins in b.instrs), None)

    preservs_groups = []

    for reg, pushes, pops in preservs:
        if len(preservs_groups) == 0:
            preservs_groups.append([[reg, pushes, pops]])
            continue
        for group in preservs_groups:
            if (all(p1.block == p2.block for p1, p2 in zip(group[0][1], pushes)) and
                    all(p1.block == p2.block for p1, p2 in zip(group[0][2], pops))):
                group.append([reg, pushes, pops])
                break
            else:
                preservs_groups.append([[reg, pushes, pops]])
    # print "grouping!:", '\n'.join((str(g) for g in preservs_groups))

    # reorder (rotate) the pushes/pops by placing the last one in the
    # first's position and shifting all the other instrs down
    # TODO: breaks if esp is used within the reordered block! (BIB @ 070012F1)
    for group in (g for g in preservs_groups if len(g) > 1):
        # group them by block
        # pushes_by_block = {}
        ins_by_block = {}
        for reg, pushes, pops in group:
            for ins in itertools.chain(pushes, pops):
                if (ins.block, ins.mnem) not in ins_by_block:
                    ins_by_block[(ins.block, ins.mnem)] = []
                ins_by_block[(ins.block, ins.mnem)].append(ins)
        # in each block, move the latest ins to the ealiest's position
        for (block, mnem), instrs in ins_by_block.iteritems():
            instrs.sort(key=lambda x: x.addr)
            first = block.instrs.index(instrs[0])
            last = block.instrs.index(instrs[-1])
            block.rinstrs = block.instrs[:first]
            block.rinstrs.append(block.instrs[last])
            block.rinstrs.extend(block.instrs[first:last])
            block.rinstrs.extend(block.instrs[last + 1:])
            diff = inp.get_block_diff(block)
            changed_bytes.update((ea for ea, orig, curr in diff))
            # XXX should also generate patched files ...

    return changed_bytes