Skip to content

Instantly share code, notes, and snippets.

@dmknght
Last active November 2, 2023 09:59
Show Gist options
  • Save dmknght/b1a5e2775bba0808114d50e74e2dc867 to your computer and use it in GitHub Desktop.
Save dmknght/b1a5e2775bba0808114d50e74e2dc867 to your computer and use it in GitHub Desktop.
Use qiling to execute file (rootfs is required). Support showing ASM code and save Dump file
from qiling import *
from qiling.const import *
from unicorn.x86_const import UC_X86_INS_SYSCALL # https://github.com/unicorn-engine/unicorn/blob/master/bindings/python/unicorn/x86_const.py
import argparse
import yara
def mem_scan(ql: Qiling, address: int, size: int, yr_pointer) -> None:
buf = ql.mem.read(address, size)
for insn in ql.arch.disassembler.disasm(buf, address):
if insn.mnemonic == "call":
# In os/memory.py, the search uses the ql.os.map_info[0][0] as start addr, ql.os.map_info[-1][1] as end addr
# This is equal to parsing the procfs's mapfile. However, there's no way to scan the params
# TODO this scan didn't detect UPX
for mem_info in ql.mem.map_info:
data = ql.mem.read(mem_info[0], mem_info[1] - mem_info[0])
matches = yr_pointer.match(data = data)
if matches:
ql.log.info(f"Matched Yara rule {matches}")
ql.stop()
def diassembler(ql: Qiling, address: int, size: int, args) -> None:
# TODO if instruction == sleep, then rip + 5?
f, padding, is_mini = args
buf = ql.mem.read(address, size)
base_addr, mem_name = ql.arch.utils.get_base_and_name(ql.mem.align(address))
if f and (not is_mini or (is_mini and ql.targetname == mem_name)):
f.write(buf)
# If QL_VERBOSE.DEFAULT
# if ql.verbose == 1 and (not is_mini or (is_mini and ql.targetname == mem_name)):
base_addr, mem_name = ql.arch.utils.get_base_and_name(ql.mem.align(address))
for insn in ql.arch.disassembler.disasm(buf, address):
offset = insn.address - base_addr
if ql.verbose == 1 and (not is_mini or (is_mini and ql.targetname == mem_name)):
ql.log.info(f'{insn.address:#0{padding}x} {insn.bytes.hex(" "):32s} {insn.mnemonic:8s} {insn.op_str:42s} [{mem_name} + {offset:#08x}]')
# TODO when program runs the `call`, get and scan variable (arguments) values before and after call invoked (scan return value too) (check stack_write)
def arg_parser():
parser = argparse.ArgumentParser(description='Create memory dump with Qiling framework')
parser.add_argument('--file', "-f", metavar='ELF_file', help='Executable file to run', required=True)
parser.add_argument('--rootfs', '-r', metavar='rootfs_dir', default='/home/dmknght/Desktop/Emulator_test/rootfs', help='RootFS folder', required=True)
parser.add_argument('--dump', '-d', metavar='Dump_file', help='Write program\'s dump to file')
parser.add_argument('--verbose', '-v', default=False, type=bool, action=argparse.BooleanOptionalAction, help="Enable verbose to show disasm code")
parser.add_argument("--mini", "-m", default=False, type=bool, action=argparse.BooleanOptionalAction, help="Enable mini dump (skip library)")
parser.add_argument("--timeout", "-t", metavar=5, default=5, type=int, help="Set emulator timeout (seconds)")
parser.add_argument("--yara", "-s", metavar="path_to_yara_rules", type=str, help="Path to yara rule to scan. Disable dump and verbose")
return parser
def setup_sandbox(program, rootfs, dump, verbose, is_mini, timeout, yara_rules):
f = None
debug_mode = QL_VERBOSE.DISABLED
padding = 16
if dump:
f = open(dump, "ab")
f.truncate(0)
if verbose:
debug_mode = QL_VERBOSE.DEFAULT
ql = Qiling([program], rootfs, verbose=debug_mode)
if ql.arch.type == QL_ARCH.X86:
padding = 8
if yara_rules:
rules = yara.compile(yara_rules)
ql.hook_code(mem_scan, user_data=rules)
else:
ql.hook_code(diassembler, user_data=[f, padding, is_mini])
try:
# FIXME check and test actual value
ql.run(timeout=timeout * 1000000)
except Exception as error:
print(error)
finally:
if f:
f.close()
if __name__ == "__main__":
# TODO run program with args?
# TODO run program with timeout
parser = arg_parser()
args = parser.parse_args()
setup_sandbox(args.file, args.rootfs, args.dump, args.verbose, args.mini, args.timeout, args.yara)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment