Last active
November 2, 2023 09:59
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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