diff --git a/docs/api.md b/docs/api.md index f46a7b13d..dc3a29436 100644 --- a/docs/api.md +++ b/docs/api.md @@ -212,6 +212,17 @@ gef_on_exit_unhook > GDB are only present in the very latest version of GDB. +``` +@parse_arguments( {"required_argument_1": DefaultValue1, ...}, {"--optional-argument-1": DefaultValue1, ...} ) +``` +> This decorator aims to facilitate the argument passing to a command. If added, it will use +> the `argparse` module to parse arguments, and will store them in the `kwargs["arguments"]` of +> the calling function (therefore the function **must** have `*args, **kwargs` added to its +> signature). Argument type is inferred directly from the default value **except** for boolean, +> where a value of `True` corresponds to `argparse`'s `store_true` action. For more details on +> `argparse`, refer to its Python doc. + + ### Classes ### For exhaustive documentation, run diff --git a/gef.py b/gef.py index 5d4ce60d3..41ead8fae 100644 --- a/gef.py +++ b/gef.py @@ -2621,7 +2621,7 @@ def wrapper(*args, **kwargs): parser.add_argument(argname, type=argtype, required=True, default=argvalue) else: # positional args - parser.add_argument(argname, type=argtype, default=argvalue, nargs='?') + parser.add_argument(argname, type=argtype, default=argvalue, nargs='*') for argname in optional_arguments: if not argname.startswith("-"): @@ -3181,10 +3181,7 @@ def get_generic_arch(module, prefix, arch, mode, big_endian, to_string=False): else: arch = getattr(module, "{:s}_ARCH_{:s}".format(prefix, arch)) - if prefix == "KS" and mode == "ARM": - # this workaround is because keystone.KS_MODE_ARM doesn't exist, must use 0 - mode = 0 - elif mode: + if mode: mode = getattr(module, "{:s}_MODE_{:s}".format(prefix, mode)) else: mode = 0 @@ -4359,7 +4356,7 @@ def do_invoke(self, argv, *args, **kwargs): err("Language must be in: {}".format(str(self.valid_formats))) return - start_addr = int(gdb.parse_and_eval(args.location)) + start_addr = int(gdb.parse_and_eval(args.location[0])) size = int(args.bitlen / 8) end_addr = start_addr + args.length * size fmt = self.format_matrix[args.bitlen][0] @@ -5871,7 +5868,7 @@ def pre_load(self): def do_invoke(self, argv, *args, **kwargs): args = kwargs["arguments"] start_address = args.start or current_arch.pc - end_address = args.until or self.get_unicorn_end_addr(start_address, args.nb) + end_address = args.until or self.get_unicorn_end_addr(start_address, args.nb[0]) self.run_unicorn(start_address, end_address, skip_emulation=args.skip_emulation, to_file=args.output_file) return @@ -6388,7 +6385,7 @@ def __init__(self): @parse_arguments({"address": ""}, {"--retval": 0}) def do_invoke(self, argv, *args, **kwargs): args = kwargs["arguments"] - loc = args.address if args.address else "*{:#x}".format(current_arch.pc) + loc = args.address[0] if args.address else "*{:#x}".format(current_arch.pc) StubBreakpoint(loc, args.retval) return @@ -7275,16 +7272,14 @@ def do_invoke(self, argv): @register_command class AssembleCommand(GenericCommand): - """Inline code assemble. Architecture can be set in GEF runtime config (default x86-32). """ + """Inline code assemble. Architecture can be set in GEF runtime config. """ _cmdline_ = "assemble" _syntax_ = "{:s} [-a ARCH] [-m MODE] [-e] [-s] [-l LOCATION] instruction;[instruction;...instruction;])".format(_cmdline_) _aliases_ = ["asm",] _example_ = "\n{0:s} -a x86 -m 32 nop ; nop ; inc eax ; int3\n{0:s} -a arm -m arm add r0, r0, 1".format(_cmdline_) - def __init__(self, *args, **kwargs): - super().__init__(complete=gdb.COMPLETE_LOCATION) - self.valid_arch_modes = { + valid_arch_modes = { "ARM": ["ARM", "THUMB"], "ARM64": ["ARM", "THUMB", "V5", "V8", ], "MIPS": ["MICRO", "MIPS3", "MIPS32", "MIPS32R6", "MIPS64",], @@ -7293,6 +7288,13 @@ def __init__(self, *args, **kwargs): "SYSTEMZ": ["32",], "X86": ["16", "32", "64"], } + valid_archs = valid_arch_modes.keys() + valid_modes = [_ for sublist in valid_arch_modes.values() for _ in sublist] + + def __init__(self): + super().__init__() + self.add_setting("default_architecture", "X86", "Specify the default architecture to use when assembling") + self.add_setting("default_mode", "64", "Specify the default architecture to use when assembling") return def pre_load(self): @@ -7312,52 +7314,37 @@ def usage(self): gef_print(" * {}".format(" / ".join(self.valid_arch_modes[arch]))) return - def do_invoke(self, argv): - arch_s, mode_s, big_endian, as_shellcode, write_to_location = None, None, False, False, None - opts, args = getopt.getopt(argv, "a:m:l:esh") - for o, a in opts: - if o == "-a": arch_s = a.upper() - if o == "-m": mode_s = a.upper() - if o == "-e": big_endian = True - if o == "-s": as_shellcode = True - if o == "-l": write_to_location = int(gdb.parse_and_eval(a)) - if o == "-h": - self.usage() - return + @parse_arguments({"instructions": ""}, {"--mode": "", "--arch": "", "--overwrite-location": 0, "--big-endian": True, "--as-shellcode": True, }) + def do_invoke(self, argv, *args, **kwargs): + arch_s, mode_s, endian_s = self.get_setting("default_architecture"), self.get_setting("default_mode"), "" - if not args: + args = kwargs["arguments"] + if not args.instructions: + err("No instruction given.") return - if (arch_s, mode_s) == (None, None): - if is_alive(): - arch_s, mode_s = current_arch.arch, current_arch.mode - endian_s = "big" if is_big_endian() else "little" - arch, mode = get_keystone_arch(arch=arch_s, mode=mode_s, endian=is_big_endian()) - else: - # if not alive, defaults to x86-32 - arch_s = "X86" - mode_s = "32" - endian_s = "little" - arch, mode = get_keystone_arch(arch=arch_s, mode=mode_s, endian=False) - elif not arch_s: - err("An architecture (-a) must be provided") - return - elif not mode_s: - err("A mode (-m) must be provided") - return - else: - arch, mode = get_keystone_arch(arch=arch_s, mode=mode_s, endian=big_endian) - endian_s = "big" if big_endian else "little" + if is_alive(): + arch_s, mode_s = current_arch.arch, current_arch.mode + endian_s = "big" if is_big_endian() else "" + + if args.arch: + arch_s = args.arch + + if args.mode: + mode_s = args.mode + + if args.big_endian: + endian_s = "big" - insns = " ".join(args) - insns = [x.strip() for x in insns.split(";") if x is not None] + if arch_s.upper() not in self.valid_archs or mode_s.upper() not in self.valid_modes: + raise AttributeError("invalid arch/mode") - info("Assembling {} instruction{} for {} ({} endian)".format(len(insns), - "s" if len(insns)>1 else "", - ":".join([arch_s, mode_s]), - endian_s)) + # this is fire a ValueError if the arch/mode/endianess are invalid + arch, mode = get_keystone_arch(arch=arch_s.upper(), mode=mode_s.upper(), endian=endian_s.upper()) + insns = [x.strip() for x in " ".join(args.instructions).split(";") if x] + info("Assembling {} instruction(s) for {}:{}".format(len(insns),arch_s, mode_s)) - if as_shellcode: + if args.as_shellcode: gef_print("""sc="" """) raw = b"" @@ -7367,7 +7354,7 @@ def do_invoke(self, argv): gef_print("(Invalid)") continue - if write_to_location: + if args.overwrite_location: raw += res continue @@ -7375,15 +7362,15 @@ def do_invoke(self, argv): res = b"\\x" + b"\\x".join([s[i:i + 2] for i in range(0, len(s), 2)]) res = res.decode("utf-8") - if as_shellcode: + if args.as_shellcode: res = """sc+="{0:s}" """.format(res) gef_print("{0:60s} # {1}".format(res, insn)) - if write_to_location: + if args.overwrite_location: l = len(raw) - info("Overwriting {:d} bytes at {:s}".format(l, format_address(write_to_location))) - write_memory(write_to_location, raw, l) + info("Overwriting {:d} bytes at {:s}".format(l, format_address(args.overwrite_location))) + write_memory(args.overwrite_location, raw, l) return diff --git a/tests/runtests.py b/tests/runtests.py index f89593d25..5deb4d7c5 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -249,11 +249,11 @@ def test_cmd_hexdump(self): def test_cmd_keystone_assemble(self): valid_cmds = [ - "assemble nop; xor eax, eax; int 0x80", - "assemble -a arm -m arm add r0, r1, r2", - "assemble -a mips -m mips32 add $v0, 1", - "assemble -a sparc -m sparc32 set 0, %o0", - "assemble -a arm64 -m little_endian add x29, sp, 0; mov w0, 0; ret" + "assemble nop; xor eax, eax; syscall", + "assemble --arch arm --mode arm add r0, r1, r2", + "assemble --arch mips --mode mips32 add $v0, 1", + "assemble --arch sparc --mode sparc32 set 0, %o0", + "assemble --arch arm64 --mode arm add x29, sp, 0; mov w0, 0; ret" ] for cmd in valid_cmds: res = gdb_start_silent_cmd(cmd)