Snapshot & Partial Execution

Snapshot & Partial Execution

Partial Execution (Skip-Ahead Pattern)

Save state at a point, then restore and resume from a later address — useful for skipping long sleeps, initialization, or irrelevant code paths.

 1from qiling import Qiling
 2from qiling.const import QL_VERBOSE
 3
 4def dump(ql, *args, **kw):
 5    ql.save(reg=False, cpu_context=True, snapshot="/tmp/snapshot.bin")
 6    ql.emu_stop()
 7
 8# Pass 1: run until just before sleep(), save state
 9ql = Qiling(["examples/rootfs/x8664_linux/bin/sleep_hello"], "examples/rootfs/x8664_linux", verbose=QL_VERBOSE.DEFAULT)
10X64BASE = int(ql.profile.get("OS64", "load_address"), 16)
11ql.hook_address(dump, X64BASE + 0x1094)
12ql.run()
13
14# Pass 2: restore saved state, run from after sleep()
15ql = Qiling(["examples/rootfs/x8664_linux/bin/sleep_hello"], "examples/rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG)
16X64BASE = int(ql.profile.get("OS64", "load_address"), 16)
17ql.restore(snapshot="/tmp/snapshot.bin")
18ql.run(begin=X64BASE + 0x109e, end=X64BASE + 0x10bc)

Full State Save/Restore

1# Save everything to a dict
2ql_all = ql.save()
3
4# Restore from dict
5ql.restore(ql_all)

Save options (all default to True except cpu_ctx):

1ql.save(mem=True, reg=True, fd=True, cpu_ctx=False)

Save to file:

1ql.save(snapshot="/tmp/snapshot.bin")
2ql.restore(snapshot="/tmp/snapshot.bin")

CPU Context Save/Restore

Wraps Unicorn’s uc_context_save / uc_context_restore:

1# Save all CPU registers
2all_registers_context = ql.arch.regs.context_save()
3
4# Restore all CPU registers
5ql.arch.regs.context_restore(all_registers_context)

Register Save/Restore

1# Save all registers to a dict
2all_registers = ql.arch.regs.save()
3
4# Modify a register in the saved state
5all_registers["eip"] = 0xaabbccdd
6
7# Restore registers from dict
8ql.arch.regs.restore(all_registers)

Memory Save/Restore

1all_mem = ql.mem.save()
2ql.mem.restore(all_mem)

File Descriptor Save/Restore

1all_fd = ql.fd.save()
2ql.fd.restore(all_fd)