#!/home/eljakim/Source/tegrax1_plus/source/Shofel2_T124_python/venv/bin/python3 # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # import argparse import os import sys import ast import pickle from pprint import pprint from typing import TYPE_CHECKING, Mapping, Type from unicorn import __version__ as uc_ver from qiling import __version__ as ql_ver from qiling import Qiling from qiling.arch import utils as arch_utils from qiling.debugger.qdb import QlQdb from qiling.const import QL_VERBOSE, QL_ENDIAN, os_map, arch_map, verbose_map from qiling.extensions.coverage import utils as cov_utils from qiling.extensions import report if TYPE_CHECKING: from enum import Enum # read code from file def read_file(fname: str): with open(fname, "rb") as f: content = f.read() return content class __arg_env(argparse.Action): def __call__(self, parser, namespace, values: str, option_string): if os.path.exists(values): with open(values, 'rb') as f: env = pickle.load(f) else: env = ast.literal_eval(values) setattr(namespace, self.dest, env or {}) def __make_enum_arg(enum_rmap: Mapping[str, 'Enum'], aliases: Mapping[str, str] = {}) -> Type[argparse.Action]: class __enum_arg(argparse.Action): def __call__(self, parser, namespace, values: str, option_string): values = values.casefold() if values in aliases: values = aliases[values] setattr(namespace, self.dest, enum_rmap[values]) return __enum_arg __arg_archtype = __make_enum_arg(arch_map, {'x86_64': 'x8664', 'riscv32': 'riscv'}) __arg_ostype = __make_enum_arg(os_map, {'darwin': 'macos'}) __arg_verbose = __make_enum_arg(verbose_map) def handle_code(options: argparse.Namespace): archendian = { 'little': QL_ENDIAN.EL, 'big' : QL_ENDIAN.EB }[options.endian] if options.format == 'hex': if options.input is not None: print("Load HEX from ARGV") code = str(options.input).strip("\\\\x").split("x") code = "".join(code).strip() code = bytes.fromhex(code) elif options.filename is not None: print("Load HEX from FILE") code = str(read_file(options.filename)).strip('b\'').strip('\\n') code = code.strip('x').split("\\\\x") code = "".join(code).strip() code = bytes.fromhex(code) else: print("ERROR: File not found") exit(1) elif options.format == 'asm': print("Load ASM from FILE") assembly = read_file(options.filename) assembler = arch_utils.assembler(options.arch, archendian, options.thumb) code, _ = assembler.asm(assembly) code = bytes(code) elif options.format == 'bin': print("Load BIN from FILE") if options.filename is not None: code = read_file(options.filename) else: print("ERROR: File not found") exit(1) ql = Qiling( rootfs=options.rootfs, env=options.env, code=code, ostype=options.os, archtype=options.arch, verbose=options.verbose, profile=options.profile, filter=options.filter, endian=archendian, thumb=options.thumb, ) return ql def handle_run(options: argparse.Namespace): effective_argv = [] # with argv if options.filename is not None and options.run_args == []: effective_argv = [options.filename] + options.args # Without argv elif options.filename is None and options.args == [] and options.run_args != []: effective_argv = options.run_args else: print("ERROR: Command error!") ql = Qiling( argv=effective_argv, rootfs=options.rootfs, env=options.env, verbose=options.verbose, profile=options.profile, console=options.console, log_file=options.log_file, log_plain=options.log_plain, multithread=options.multithread, filter=options.filter, libcache=options.libcache ) # attach Qdb at entry point if options.qdb is True: QlQdb(ql, rr=options.rr).run() exit() return ql def handle_examples(parser: argparse.ArgumentParser): prog = os.path.basename(__file__) __ql_examples = f"""Examples: With code: {prog} code --os linux --arch arm --format hex -f examples/shellcodes/linarm32_tcp_reverse_shell.hex {prog} code --os linux --arch x86 --format asm -f examples/shellcodes/lin32_execve.asm With binary file: {prog} run -f examples/rootfs/x8664_linux/bin/x8664_hello --rootfs examples/rootfs/x8664_linux {prog} run -f examples/rootfs/mips32el_linux/bin/mips32el_hello --rootfs examples/rootfs/mips32el_linux With binary file and Qdb: {prog} run -f examples/rootfs/mips32el_linux/bin/mips32el_hello --rootfs examples/rootfs/mips32el_linux --qdb {prog} run -f examples/rootfs/mips32el_linux/bin/mips32el_hello --rootfs examples/rootfs/mips32el_linux --qdb --rr With binary file and gdbserver: {prog} run -f examples/rootfs/x8664_linux/bin/x8664_hello --gdb 127.0.0.1:9999 --rootfs examples/rootfs/x8664_linux With binary file and additional argv: {prog} run -f examples/rootfs/x8664_linux/bin/x8664_args --rootfs examples/rootfs/x8664_linux --args test1 test2 test3 With binary file and various output format: {prog} run -f examples/rootfs/mips32el_linux/bin/mips32el_hello --rootfs examples/rootfs/mips32el_linux --verbose disasm {prog} run -f examples/rootfs/mips32el_linux/bin/mips32el_hello --rootfs examples/rootfs/mips32el_linux --filter ^open With UEFI file: {prog} run -f examples/rootfs/x8664_efi/bin/TcgPlatformSetupPolicy --rootfs examples/rootfs/x8664_efi --env examples/rootfs/x8664_efi/rom2_nvar.pickel With binary file and json output: {prog} run -f examples/rootfs/x86_windows/bin/x86_hello.exe --rootfs examples/rootfs/x86_windows --no-console --json """ parser.exit(0, __ql_examples) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--version', action='version', version=f'qltool for Qiling {ql_ver}, using Unicorn {uc_ver}') commands = parser.add_subparsers(title='sub commands', description='select execution mode', dest='subcommand', required=True) # set "run" subcommand options run_parser = commands.add_parser('run', help='run a program') run_parser.add_argument('-f', '--filename', default=None, metavar="FILE", help="filename") run_parser.add_argument('--rootfs', required=True, help='emulated rootfs') run_parser.add_argument('--args', default=[], nargs=argparse.REMAINDER, dest="args", help="args") run_parser.add_argument('run_args', default=[], nargs=argparse.REMAINDER) # set "code" subcommand options code_parser = commands.add_parser('code', help='execute a shellcode') code_parser.add_argument('-f', '--filename', metavar="FILE", help="filename") code_parser.add_argument('-i', '--input', metavar="INPUT", dest="input", help='input hex value') code_parser.add_argument('--arch', required=True, choices=arch_map, action=__arg_archtype) code_parser.add_argument('--thumb', action='store_true', default=False, help='specify thumb mode for ARM') code_parser.add_argument('--endian', choices=('little', 'big'), default='little', help='specify endianess for bi-endian archs') code_parser.add_argument('--os', required=True, choices=os_map, action=__arg_ostype) code_parser.add_argument('--rootfs', default='.', help='emulated root filesystem, that is where all libraries reside') code_parser.add_argument('--format', choices=('asm', 'hex', 'bin'), default='bin', help='input file format') # set "examples" subcommand expl_parser = commands.add_parser('examples', help='show examples and exit', add_help=False) # set "qltui" subcommand qltui_parser = commands.add_parser('qltui', help='show qiling Terminal User Interface', add_help=False) qltui_enabled = False comm_parser = run_parser if len(sys.argv) > 1 and sys.argv[1] == 'code': comm_parser = code_parser # set common options comm_parser.add_argument('-v', '--verbose', choices=verbose_map, default=QL_VERBOSE.DEFAULT, action=__arg_verbose, help='set verbosity level') comm_parser.add_argument('--env', metavar="FILE", action=__arg_env, default={}, help="pickle file containing an environment dictionary") comm_parser.add_argument('-g', '--gdb', nargs='?', metavar='SERVER:PORT', const='gdb', help='enable gdb server') comm_parser.add_argument('--qdb', action='store_true', help='attach Qdb at entry point, it\'s MIPS, ARM(THUMB) supported only for now') comm_parser.add_argument('--rr', action='store_true', help='switch on record and replay feature in qdb, only works with --qdb') comm_parser.add_argument('--profile', help="define a customized profile") comm_parser.add_argument('--no-console', action='store_false', dest='console', help='do not emit output to console') comm_parser.add_argument('-e', '--filter', metavar='REGEXP', default=None, help="apply a filtering regexp on log output") comm_parser.add_argument('--log-file', help="write log to a file") comm_parser.add_argument('--log-plain', action='store_true', help="do not use colors in log output") comm_parser.add_argument('--root', action='store_true', help='enable sudo required mode') comm_parser.add_argument('--debug-stop', action='store_true', help='stop running on error; requires verbose to be set to either "debug" or "dump"') comm_parser.add_argument('-m', '--multithread', action='store_true', help='run in multithread mode') comm_parser.add_argument('--timeout', type=int, default=0, help='set emulation timeout') comm_parser.add_argument('-c', '--coverage-file', default=None, help='code coverage file name') comm_parser.add_argument('--coverage-format', default='drcov', choices=cov_utils.factory.formats, help='code coverage file format') comm_parser.add_argument('--json', action='store_true', help='print a json report of the emulation') comm_parser.add_argument('--libcache', action='store_true', help='enable dll caching for windows') options = parser.parse_args() if options.subcommand == 'examples': handle_examples(parser) if options.subcommand == 'qltui': import qltui options = qltui.get_data() qltui_enabled = True # ql file setup if options.subcommand == 'run': ql = handle_run(options) # ql code setup if options.subcommand == 'code': ql = handle_code(options) # ql execute additional options if options.gdb: argval = options.gdb if argval != 'gdb': argval = f'gdb:{argval}' ql.debugger = argval if options.debug_stop: if options.verbose not in (QL_VERBOSE.DEBUG, QL_VERBOSE.DUMP): parser.error('the debug_stop option requires verbose to be set to either "debug" or "dump"') ql.debug_stop = True if options.root: ql.root = True # ql run with cov_utils.collect_coverage(ql, options.coverage_format, options.coverage_file): if qltui_enabled: hook_dictionary = qltui.hook(ql) ql.run(timeout=options.timeout) if options.json: report = report.generate_report(ql) if qltui_enabled: report["syscalls"] = qltui.transform_syscalls(ql.os.stats.syscalls) qltui.show_report(ql, report, hook_dictionary) else: pprint(report) exit(ql.os.exit_code)