Skip to content

Commit

Permalink
Fix issue where restoring a snapshot created with reg=True throws an …
Browse files Browse the repository at this point in the history
…exception

When restoring a snapshot created with reg=True a unicorn cpu exception can
be thrown when restoring the GS register. It looks like for at least 32bit
mode on X86 processors unicorn will validate memory structures pointed to by
loaded GS reg. If the memory is not loaded the validation will surely fail,
and thus the exception. The fix is then to load the memory maps first,
before loading the registers. Note, this validation does not happen when
restoring via cpu_context.

Included is a test case which will have errors if this fix is not applied.
  • Loading branch information
crass committed Aug 20, 2021
1 parent 272a3da commit da480ff
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 3 deletions.
6 changes: 3 additions & 3 deletions qiling/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -804,15 +804,15 @@ def restore(self, saved_states=None, snapshot=None):
with open(snapshot, "rb") as load_state:
saved_states = pickle.load(load_state)

if "mem" in saved_states:
self.mem.restore(saved_states["mem"])

if "cpu_context" in saved_states:
self.arch.context_restore(saved_states["cpu_context"])

if "reg" in saved_states:
self.reg.restore(saved_states["reg"])

if "mem" in saved_states:
self.mem.restore(saved_states["mem"])

if "fd" in saved_states:
self.os.fd.restore(saved_states["fd"])

Expand Down
1 change: 1 addition & 0 deletions tests/test_onlinux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ python3 ./test_android.py &&
python3 ./test_debugger.py &&
python3 ./test_uefi.py &&
python3 ./test_shellcode.py &&
python3 ./test_snapshot.py &&
python3 ./test_edl.py &&
python3 ./test_qnx.py
57 changes: 57 additions & 0 deletions tests/test_snapshot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

import os, sys, unittest

sys.path.append("..")
from qiling import *
from qiling.const import QL_VERBOSE

class SnapshotRestoreTest(unittest.TestCase):

def _test_snapshot_restore_common(self, reg=False, ctx=False):
rootfs = "../examples/rootfs/x86_linux"
cmdline = ["../examples/rootfs/x86_linux/bin/x86_hello"]
snapshot = os.path.join(rootfs, 'snapshot_restore_reg_ctx.snapshot')

ql = Qiling(cmdline, rootfs, verbose=QL_VERBOSE.DEBUG)

X86BASE = int(ql.profile.get("OS32", "load_address"), 16)
hook_address = X86BASE + 0x542 # call printf

def dump(ql):
nonlocal snapshot
nonlocal reg
nonlocal ctx
ql.save(reg=reg, cpu_context=ctx, os_context=True, loader=True, snapshot=snapshot)
ql.emu_stop()
ql.hook_address(dump, hook_address)

ql.run()

# make sure that the ending PC is the same as the hook address because dump stops the emulater
assert ql.reg.arch_pc == hook_address, f"0x{ql.reg.arch_pc:x} != 0x{hook_address:x}"
del ql

ql = Qiling(cmdline, rootfs, verbose=QL_VERBOSE.DEBUG)
ql.restore(snapshot=snapshot)

# ensure that the starting PC is same as the PC we stopped on when taking the snapshot
assert ql.reg.arch_pc == hook_address, f"0x{ql.reg.arch_pc:x} != 0x{hook_address:x}"

ql.run(begin=hook_address)
del ql

def test_snapshot_restore_reg(self):
self._test_snapshot_restore_common(reg=True, ctx=False)

def test_snapshot_restore_ctx(self):
self._test_snapshot_restore_common(reg=False, ctx=True)

def test_snapshot_restore_reg_ctx(self):
self._test_snapshot_restore_common(reg=True, ctx=True)

if __name__ == "__main__":
unittest.main()

0 comments on commit da480ff

Please sign in to comment.