Пример #1
0
def main():
  arg_parser = argparse.ArgumentParser(
    formatter_class=argparse.RawDescriptionHelpFormatter,
    epilog=textwrap.dedent("""\
    Additional arguments are passed to the disassembler script directly. These include:

      --std-defs <file>       Load additional external function definitions from <file>
      --pie-mode              Change disassembler heuristics to work on position independent code"""))

  arg_parser.add_argument(
      '--disassembler',
      help='Path to disassembler binary',
      required=True)

  arg_parser.add_argument(
      '--arch',
      help='Name of the architecture. Valid names are x86, amd64, and aarch64.',
      choices=SUPPORTED_ARCH,
      required=True)

  arg_parser.add_argument(
      '--os',
      help='Name of the OS. Valid names are {}'.format(SUPPORTED_OS),
      choices=SUPPORTED_OS,
      required=True)

  arg_parser.add_argument(
      '--log_file',
      default=os.devnull,
      help='Where to write the log file.')

  arg_parser.add_argument(
      '--output',
      help='The output control flow graph recovered from this file',
      required=True)

  arg_parser.add_argument(
      '--binary',
      help='Binary to recover control flow graph from',
      required=True)

  arg_parser.add_argument(
      '--entrypoint',
      help="The entrypoint where disassembly should begin",
      required=True)

  args, command_args = arg_parser.parse_known_args()

  if not os.path.isfile(args.binary):
    arg_parser.error("{} passed to --binary is not a valid file.".format(
        args.binary))
    return 1

  if args.arch.endswith("_avx"):
    args.arch = args.arch[:-4]

  if args.arch.endswith("_avx512"):
    args.arch = args.arch[:-7]

  if args.arch not in SUPPORTED_ARCH:
    arg_parser.error("{} passed to --arch is not supported. Valid options are: {}".format(
      args.arch, SUPPORTED_ARCH))
    return 1

  if args.os not in SUPPORTED_OS:
    arg_parser.error("{} passed to --os is not supported. Valid options are: {}".format(
      args.os, SUPPORTED_OS))

  args.binary = os.path.abspath(args.binary)
  args.output = os.path.abspath(args.output)
  args.log_file = os.path.abspath(args.log_file)

  fixed_command_args = []
  # ensure that any paths in arguments to the disassembler
  # are absolute path
  for fix_arg in command_args:
    if os.path.exists(fix_arg):
      fixed_command_args.append(os.path.abspath(fix_arg))
    else:
      fixed_command_args.append(fix_arg)

  disass_dir = os.path.dirname(os.path.abspath(__file__))
  os.chdir(disass_dir)
  sys.path.append(disass_dir)

  new_bin_name = re.sub(r"[^a-zA-Z0-9\.]+", "_", os.path.basename(args.binary))

  workspace_dir = tempfile.mkdtemp()
  temp_bin_path = os.path.join(workspace_dir, new_bin_name)
  shutil.copyfile(args.binary, temp_bin_path)
  args.binary = temp_bin_path

  ret = 1
  try:
    if 'ida' in args.disassembler:
      if 'idat' in args.disassembler:
        import ida7.disass as disass
      else:
        import ida.disass as disass
      ret = disass.execute(args, fixed_command_args)
      # in case IDA somehow says success, but no output was generated
      if not os.path.isfile(args.output):
        sys.stderr.write("Could not generate a CFG. Try using the --log_file option to see an error log.\n")
        ret = 1

      # The disassembler script probably threw an exception
      if 0 == os.path.getsize(args.output):
        sys.stderr.write("Generated an invalid (zero-sized) CFG. Please use the --log_file option to see an error log.\n")
        # remove the zero-sized file
        os.unlink(args.output)
        ret = 1
    elif 'binja' in args.disassembler or 'binaryninja' in args.disassembler:
      if not _find_binary_ninja(args.disassembler):
        arg_parser.error("Could not `import binaryninja`. Is it in your PYTHONPATH?")
      from binja.cfg import get_cfg
      ret = get_cfg(args, fixed_command_args)
    else:
      arg_parser.error("{} passed to --disassembler is not known.".format(
          args.disassembler))

  finally:
    shutil.rmtree(workspace_dir)

  return ret
def main():
    arg_parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=textwrap.dedent("""\
    Additional arguments are passed to the disassembler script directly. These include:

      --std-defs <file>       Load additional external function definitions from <file>
      --pie-mode              Change disassembler heuristics to work on position independent code"""
                               ))

    arg_parser.add_argument(
        '--disassembler',
        help='Path to disassembler binary, or dyninst (binary must be in path)',
        required=True)

    arg_parser.add_argument(
        '--arch',
        help=
        'Name of the architecture. Valid names are x86, amd64, and aarch64.',
        choices=SUPPORTED_ARCH,
        required=True)

    arg_parser.add_argument(
        '--os',
        help='Name of the OS. Valid names are {}'.format(SUPPORTED_OS),
        choices=SUPPORTED_OS,
        required=True)

    arg_parser.add_argument('--log_file',
                            default=os.devnull,
                            help='Where to write the log file.')

    arg_parser.add_argument(
        '--output',
        help='The output control flow graph recovered from this file',
        required=True)

    arg_parser.add_argument('--binary',
                            help='Binary to recover control flow graph from',
                            required=True)

    arg_parser.add_argument(
        '--entrypoint',
        help="The entrypoint where disassembly should begin",
        required=False)

    arg_parser.add_argument('--rebase',
                            help="Amount by which to rebase a binary",
                            required=False,
                            default=0)

    args, command_args = arg_parser.parse_known_args()

    if not os.path.isfile(args.binary):
        arg_parser.error("{} passed to --binary is not a valid file.".format(
            args.binary))
        return 1

    if args.arch.endswith("_avx"):
        args.arch = args.arch[:-4]

    if args.arch.endswith("_avx512"):
        args.arch = args.arch[:-7]

    if args.arch not in SUPPORTED_ARCH:
        arg_parser.error(
            "{} passed to --arch is not supported. Valid options are: {}".
            format(args.arch, SUPPORTED_ARCH))
        return 1

    if args.os not in SUPPORTED_OS:
        arg_parser.error(
            "{} passed to --os is not supported. Valid options are: {}".format(
                args.os, SUPPORTED_OS))

    args.binary = os.path.abspath(args.binary)
    args.output = os.path.abspath(args.output)
    args.log_file = os.path.abspath(args.log_file)

    fixed_command_args = []
    # ensure that any paths in arguments to the disassembler
    # are absolute path
    for fix_arg in command_args:
        if os.path.exists(fix_arg):
            fixed_command_args.append(os.path.abspath(fix_arg))
        else:
            fixed_command_args.append(fix_arg)

    disass_dir = os.path.dirname(os.path.abspath(__file__))
    os.chdir(disass_dir)
    sys.path.append(disass_dir)

    new_bin_name = re.sub(r"[^a-zA-Z0-9\.]+", "_",
                          os.path.basename(args.binary))

    workspace_dir = tempfile.mkdtemp()
    temp_bin_path = os.path.join(workspace_dir, new_bin_name)
    shutil.copyfile(args.binary, temp_bin_path)
    args.binary = temp_bin_path

    ret = 1
    try:
        if 'ida' in args.disassembler:
            import ida7.disass as disass
            ret = disass.execute(args, fixed_command_args)
            # in case IDA somehow says success, but no output was generated
            if not os.path.isfile(args.output):
                sys.stderr.write(
                    "Could not generate a CFG. Try using the --log_file option to see an error log.\n"
                )
                ret = 1

            # The disassembler script probably threw an exception
            if 0 == os.path.getsize(args.output):
                sys.stderr.write(
                    "Generated an invalid (zero-sized) CFG. Please use the --log_file option to see an error log.\n"
                )
                # remove the zero-sized file
                os.unlink(args.output)
                ret = 1

        elif 'dyninst' in args.disassembler:
            # TODO: This can almost certainly be done in cleaner way
            pass_args = [
                "mcsema-dyninst-disass", "--binary", args.binary, "--arch",
                args.arch, "--entrypoint", args.entrypoint, "--os", args.os,
                "--output", args.output, "--binary", args.binary, "--rebase",
                args.rebase
            ]
            subprocess.run(pass_args)
        else:
            arg_parser.error(
                "{} passed to --disassembler is not known.".format(
                    args.disassembler))

    finally:
        shutil.rmtree(workspace_dir)

    return ret