示例#1
0
        def parse_device():
            usbmux = context.usbmux
            devices = usbmux.device_list()
            if len(devices) == 0:
                raise MuxError("error: no devices/emulators found")

            if len(devices) == 1:
                return devices[0].udid

            logger.message("more than one device/emulator")

            offset = 1
            for i in range(len(devices)):
                try:
                    name = Device(devices[0].udid, usbmux).name
                except Exception:
                    name = ""
                logger.message(f"%d: %-20s [%s]" %
                               (i + offset, devices[i].udid, name))

            while True:
                offset = 1
                data = input("enter device index %d~%d (default %d): " %
                             (offset, len(devices) + offset - 1, offset))
                if utils.is_empty(data):
                    return devices[0].udid
                index = utils.cast(int, data, offset - 1) - offset
                if 0 <= index < len(devices):
                    return devices[index].udid
示例#2
0
 def on_binary(self, filename: str, mimetype: str):
     with open(filename, "rb") as fd:
         for line in fd.readlines():
             if self.pattern.search(line) is not None:
                 logger.message(Fore.CYAN, filename, Fore.RESET, ": ",
                                Fore.RED, mimetype, Fore.RESET, " match")
                 return
示例#3
0
        def parse_device():
            devices = Adb.devices(alive=True)

            if len(devices) == 0:
                raise AdbError("error: no devices/emulators found")

            if len(devices) == 1:
                return devices[0]

            logger.message("more than one device/emulator")

            offset = 1
            for i in range(len(devices)):
                try:
                    name = Device(devices[i]).get_prop("ro.product.name",
                                                       timeout=1)
                except Exception:
                    name = ""
                logger.message("%d: %-20s [%s]" %
                               (i + offset, devices[i], name))

            while True:
                data = input("enter device index %d~%d (default %d): " %
                             (offset, len(devices) + offset - 1, offset))
                if utils.is_empty(data):
                    return devices[0]
                index = utils.cast(int, data, offset - 1) - offset
                if 0 <= index < len(devices):
                    return devices[index]
示例#4
0
 def on_text(self, filename: str, mimetype: str):
     with open(filename, "rb") as fd:
         lines = fd.readlines()
         for i in range(0, len(lines)):
             out = self.match_content(lines[i].rstrip())
             if not utils.is_empty(out):
                 logger.message(Fore.CYAN, filename, Fore.RESET, ":",
                                Fore.GREEN, i + 1, Fore.RESET, ": ", out)
示例#5
0
    def on_elf(self, filename: str, mimetype: str):
        file = lief.parse(filename)
        for symbol in file.imported_symbols:
            out = self.match_content(symbol.name)
            if not utils.is_empty(out):
                logger.message(Fore.CYAN, filename, Fore.RESET, ":",
                               Fore.GREEN, "import_symbols", Fore.RESET, ": ",
                               out, Fore.RESET, " match")

        for symbol in file.exported_symbols:
            out = self.match_content(symbol.name)
            if not utils.is_empty(out):
                logger.message(Fore.CYAN, filename, Fore.RESET, ":",
                               Fore.GREEN, "export_symbols", Fore.RESET, ": ",
                               out, Fore.RESET, " match")

        self.on_binary(filename, mimetype=mimetype)
示例#6
0
def main():
    tool_names = sorted([tool.name for tool in iter(tools)])

    parser = ArgumentParser(description='tools wrapper')
    group = parser.add_mutually_exclusive_group()
    group.add_argument('-c',
                       '--config',
                       action='store_true',
                       default=False,
                       help='show the config of tool')
    group.add_argument('--download',
                       action='store_true',
                       default=False,
                       help='download tool files')
    group.add_argument('--clear',
                       action='store_true',
                       default=False,
                       help='clear tool files')
    group.add_argument('-d',
                       '--daemon',
                       action='store_true',
                       default=False,
                       help='execute tools as a daemon')
    parser.add_argument('tool', nargs='...', choices=tool_names)

    args = parser.parse_args()
    if len(args.tool) == 0 or args.tool[0] not in tool_names:
        parser.print_help()
        exit(-1)

    tool_name = args.tool[0]
    tool_args = args.tool[1:]

    if args.config:
        logger.message(
            json.dumps(tools[tool_name].config, indent=2, ensure_ascii=False))

    elif args.download:
        if not tools[tool_name].exists:
            tools[tool_name].prepare()
        logger.message(
            f"download tool files success: {tools[tool_name].absolute_path}")

    elif args.clear:
        tools[tool_name].clear()
        logger.message(f"clear tool files success")

    elif args.daemon:
        tools[tool_name].exec(
            *tool_args,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            daemon=True,
        )

    else:
        process, _, _ = tools[tool_name].exec(*tool_args)
        exit(process.returncode)
示例#7
0
 def print(self, text: str = "", indent: int = 0, level=PrintLevel.normal):
     if not self.min <= level <= self.max:
         pass
     elif level == PrintLevel.title:
         logger.message(text, style=Style.BRIGHT, indent=indent)
     elif level == PrintLevel.dangerous:
         logger.message(text,
                        fore=Fore.RED,
                        back=Back.WHITE,
                        style=Style.BRIGHT,
                        indent=indent)
     elif level == PrintLevel.useless:
         logger.message(text,
                        fore=Fore.YELLOW,
                        back=Back.WHITE,
                        style=Style.BRIGHT,
                        indent=indent)
     else:
         logger.message(text, indent=indent)
示例#8
0
def main():
    parser = AndroidArgumentParser(description='Filter logcat by package name',
                                   conflict_handler='resolve')
    parser.add_argument('package',
                        nargs='*',
                        help='application package name(s)')
    parser.add_argument('-w',
                        '--tag-width',
                        metavar='N',
                        dest='tag_width',
                        type=int,
                        default=23,
                        help='width of log tag')
    parser.add_argument('-l',
                        '--min-level',
                        dest='min_level',
                        type=str,
                        choices=LOG_LEVELS + LOG_LEVELS.lower(),
                        default='V',
                        help='minimum level to be displayed')
    parser.add_argument('--color-gc',
                        dest='color_gc',
                        action='store_true',
                        help='color garbage collection')
    parser.add_argument('--always-display-tags',
                        dest='always_tags',
                        action='store_true',
                        help='always display the tag name')
    parser.add_argument('--top',
                        '--current',
                        dest='current_app',
                        action='store_true',
                        help='filter logcat by current running app')
    parser.add_argument('-c',
                        '--clear',
                        dest='clear_logcat',
                        action='store_true',
                        help='clear the entire log before running')
    parser.add_argument('-t',
                        '--tag',
                        dest='tag',
                        action='append',
                        help='filter output by specified tag(s)')
    parser.add_argument('-i',
                        '--ignore-tag',
                        dest='ignored_tag',
                        action='append',
                        help='filter output by ignoring specified tag(s)')
    parser.add_argument('-v',
                        '--version',
                        action='version',
                        version='%(prog)s ' + __version__,
                        help='print the version number and exit')
    parser.add_argument('-a',
                        '--all',
                        dest='all',
                        action='store_true',
                        default=False,
                        help='print all log messages')

    args = parser.parse_args()
    device = args.parse_device()
    package = args.package
    min_level = LOG_LEVELS_MAP[args.min_level.upper()]

    if args.current_app:
        package.append(device.get_current_package())

    if len(package) == 0:
        args.all = True

    # Store the names of packages for which to match all processes.
    catchall_package = list(
        filter(lambda package: package.find(":") == -1, package))
    # Store the name of processes to match exactly.
    named_processes = list(
        filter(lambda package: package.find(":") != -1, package))
    # Convert default process names from <package>: (cli notation) to <package> (android notation) in the exact names match group.
    named_processes = list(
        map(
            lambda package: package
            if package.find(":") != len(package) - 1 else package[:-1],
            named_processes))

    header_size = args.tag_width + 1 + 3 + 1  # space, level, space

    width = -1
    try:
        # Get the current terminal width
        import fcntl, termios, struct

        h, width = struct.unpack(
            'hh', fcntl.ioctl(0, termios.TIOCGWINSZ, struct.pack('hh', 0, 0)))
    except:
        pass

    BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)

    RESET = '\033[0m'

    def termcolor(fg=None, bg=None):
        codes = []
        if fg is not None: codes.append('3%d' % fg)
        if bg is not None: codes.append('10%d' % bg)
        return '\033[%sm' % ';'.join(codes) if codes else ''

    def colorize(message, fg=None, bg=None):
        return termcolor(fg, bg) + message + RESET

    def truncated_string(message, offset, width):
        next = offset
        length = len(message)
        while next < length:
            width = width - get_char_width(message[next])
            if width < 0:
                break
            next = next + 1
        return next

    def indent_wrap(message):
        if width == -1:
            return message
        message = message.replace('\t', '    ')
        wrap_area = width - header_size - 1
        messagebuf = ''
        current = 0
        next = truncated_string(message, current, wrap_area)
        while current < next:
            messagebuf += message[current:next]
            if next < len(message):
                messagebuf += '\n'
                messagebuf += ' ' * header_size
            current = next
            next = truncated_string(message, current, wrap_area)
        return messagebuf

    LAST_USED = [RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN]
    KNOWN_TAGS = {
        'dalvikvm': WHITE,
        'Process': WHITE,
        'ActivityManager': WHITE,
        'ActivityThread': WHITE,
        'AndroidRuntime': CYAN,
        'jdwp': WHITE,
        'StrictMode': WHITE,
        'DEBUG': YELLOW,
    }

    def allocate_color(tag):
        # this will allocate a unique format for the given tag
        # since we dont have very many colors, we always keep track of the LRU
        if tag not in KNOWN_TAGS:
            KNOWN_TAGS[tag] = LAST_USED[0]
        color = KNOWN_TAGS[tag]
        if color in LAST_USED:
            LAST_USED.remove(color)
            LAST_USED.append(color)
        return color

    RULES = {
        # StrictMode policy violation; ~duration=319 ms: android.os.StrictMode$StrictModeDiskWriteViolation: policy=31 violation=1
        re.compile(r'^(StrictMode policy violation)(; ~duration=)(\d+ ms)'):
        r'%s\1%s\2%s\3%s' % (termcolor(RED), RESET, termcolor(YELLOW), RESET),
    }

    # Only enable GC coloring if the user opted-in
    if args.color_gc:
        # GC_CONCURRENT freed 3617K, 29% free 20525K/28648K, paused 4ms+5ms, total 85ms
        key = re.compile(
            r'^(GC_(?:CONCURRENT|FOR_M?ALLOC|EXTERNAL_ALLOC|EXPLICIT) )(freed <?\d+.)(, \d+\% free \d+./\d+., )(paused \d+ms(?:\+\d+ms)?)'
        )
        val = r'\1%s\2%s\3%s\4%s' % (termcolor(GREEN), RESET,
                                     termcolor(YELLOW), RESET)

        RULES[key] = val

    TAGTYPES = {
        'V': colorize(' V ', fg=WHITE, bg=BLACK),
        'D': colorize(' D ', fg=BLACK, bg=BLUE),
        'I': colorize(' I ', fg=BLACK, bg=GREEN),
        'W': colorize(' W ', fg=BLACK, bg=YELLOW),
        'E': colorize(' E ', fg=BLACK, bg=RED),
        'F': colorize(' F ', fg=BLACK, bg=RED),
    }

    PID_LINE = re.compile(
        r'^\w+\s+(\w+)\s+\w+\s+\w+\s+\w+\s+\w+\s+\w+\s+\w\s([\w|\.|\/]+)$')
    PID_START = re.compile(
        r'^.*: Start proc ([a-zA-Z0-9._:]+) for ([a-z]+ [^:]+): pid=(\d+) uid=(\d+) gids=(.*)$'
    )
    PID_START_5_1 = re.compile(
        r'^.*: Start proc (\d+):([a-zA-Z0-9._:]+)/[a-z0-9]+ for (.*)$')
    PID_START_DALVIK = re.compile(
        r'^E/dalvikvm\(\s*(\d+)\): >>>>> ([a-zA-Z0-9._:]+) \[ userId:0 \| appId:(\d+) \]$'
    )
    PID_KILL = re.compile(r'^Killing (\d+):([a-zA-Z0-9._:]+)/[^:]+: (.*)$')
    PID_LEAVE = re.compile(
        r'^No longer want ([a-zA-Z0-9._:]+) \(pid (\d+)\): .*$')
    PID_DEATH = re.compile(
        r'^Process ([a-zA-Z0-9._:]+) \(pid (\d+)\) has died.?$')
    LOG_LINE = re.compile(r'^([A-Z])/(.+?)\( *(\d+)\): (.*?)$')
    BUG_LINE = re.compile(r'.*nativeGetEnabledTags.*')
    BACKTRACE_LINE = re.compile(r'^#(.*?)pc\s(.*?)$')

    adb_command = ['logcat', '-v', 'brief']

    # Clear log before starting logcat
    if args.clear_logcat:
        adb_clear_command = list(adb_command)
        adb_clear_command.append('-c')
        adb_clear = device.popen(*adb_clear_command)

        while adb_clear.poll() is None:
            pass

    # This is a ducktype of the device.popen object
    class FakeStdinProcess():
        def __init__(self):
            self.stdout = sys.stdin

        def poll(self):
            return None

    if sys.stdin.isatty():
        adb = device.popen(*adb_command, stdin=PIPE, stdout=PIPE)
    else:
        adb = FakeStdinProcess()
    pids = set()
    last_tag = None
    app_pid = None

    def match_packages(token):
        if len(package) == 0:
            return True
        if token in named_processes:
            return True
        index = token.find(':')
        return (token in catchall_package) if index == -1 else (
            token[:index] in catchall_package)

    def parse_death(tag, message):
        if tag != 'ActivityManager':
            return None, None
        kill = PID_KILL.match(message)
        if kill:
            pid = kill.group(1)
            package_line = kill.group(2)
            if match_packages(package_line) and pid in pids:
                return pid, package_line
        leave = PID_LEAVE.match(message)
        if leave:
            pid = leave.group(2)
            package_line = leave.group(1)
            if match_packages(package_line) and pid in pids:
                return pid, package_line
        death = PID_DEATH.match(message)
        if death:
            pid = death.group(2)
            package_line = death.group(1)
            if match_packages(package_line) and pid in pids:
                return pid, package_line
        return None, None

    def parse_start_proc(line):
        start = PID_START_5_1.match(line)
        if start is not None:
            line_pid, line_package, target = start.groups()
            return line_package, target, line_pid, '', ''
        start = PID_START.match(line)
        if start is not None:
            line_package, target, line_pid, line_uid, line_gids = start.groups(
            )
            return line_package, target, line_pid, line_uid, line_gids
        start = PID_START_DALVIK.match(line)
        if start is not None:
            line_pid, line_package, line_uid = start.groups()
            return line_package, '', line_pid, line_uid, ''
        return None

    def tag_in_tags_regex(tag, tags):
        return any(
            re.match(r'^' + t + r'$', tag) for t in map(str.strip, tags))

    for line in device.shell("ps", stdout=PIPE, stderr=DEVNULL).splitlines():
        pid_match = PID_LINE.match(line.strip())
        if pid_match is not None:
            pid = pid_match.group(1)
            proc = pid_match.group(2)
            if proc in catchall_package:
                seen_pids = True
                pids.add(pid)

    for line in device.shell("ps", "-A", stdout=PIPE,
                             stderr=DEVNULL).splitlines():
        pid_match = PID_LINE.match(line.strip())
        if pid_match is not None:
            pid = pid_match.group(1)
            proc = pid_match.group(2)
            if proc in catchall_package:
                seen_pids = True
                pids.add(pid)

    while adb.poll() is None:
        try:
            line = adb.stdout.readline().decode('utf-8',
                                                errors="ignore").strip()
        except KeyboardInterrupt:
            break

        bug_line = BUG_LINE.match(line)
        if bug_line is not None:
            continue

        log_line = LOG_LINE.match(line)
        if log_line is None:
            continue

        level, tag, owner, message = log_line.groups()
        tag = tag.strip()
        start = parse_start_proc(line)
        if start:
            line_package, target, line_pid, line_uid, line_gids = start

            if match_packages(line_package):
                pids.add(line_pid)

                app_pid = line_pid

                linebuf = '\n'
                linebuf += colorize(' ' * (header_size - 1), bg=WHITE)
                linebuf += indent_wrap(' Process %s created for %s\n' %
                                       (line_package, target))
                linebuf += colorize(' ' * (header_size - 1), bg=WHITE)
                linebuf += ' PID: %s    UID: %s    GIDs: %s' % (
                    line_pid, line_uid, line_gids)
                linebuf += '\n'
                logger.message(linebuf)
                last_tag = None  # Ensure next log gets a tag printed

        dead_pid, dead_pname = parse_death(tag, message)
        if dead_pid:
            pids.remove(dead_pid)
            linebuf = '\n'
            linebuf += colorize(' ' * (header_size - 1), bg=RED)
            linebuf += ' Process %s (PID: %s) ended' % (dead_pname, dead_pid)
            linebuf += '\n'
            logger.message(linebuf)
            last_tag = None  # Ensure next log gets a tag printed

        # Make sure the backtrace is printed after a native crash
        if tag == 'DEBUG':
            bt_line = BACKTRACE_LINE.match(message.lstrip())
            if bt_line is not None:
                message = message.lstrip()
                owner = app_pid

        if not args.all and owner not in pids:
            continue
        if level in LOG_LEVELS_MAP and LOG_LEVELS_MAP[level] < min_level:
            continue
        if args.ignored_tag and tag_in_tags_regex(tag, args.ignored_tag):
            continue
        if args.tag and not tag_in_tags_regex(tag, args.tag):
            continue

        linebuf = ''

        if args.tag_width > 0:
            # right-align tag title and allocate color if needed
            if tag != last_tag or args.always_tags:
                last_tag = tag
                color = allocate_color(tag)
                tag = tag[-args.tag_width:].rjust(args.tag_width)
                linebuf += colorize(tag, fg=color)
            else:
                linebuf += ' ' * args.tag_width
            linebuf += ' '

        # write out level colored edge
        if level in TAGTYPES:
            linebuf += TAGTYPES[level]
        else:
            linebuf += ' ' + level + ' '
        linebuf += ' '

        # format tag message using rules
        for matcher in RULES:
            replace = RULES[matcher]
            message = matcher.sub(replace, message)

        linebuf += indent_wrap(message)
        logger.message(linebuf)
示例#9
0
def main():
    parser = AndroidArgumentParser(
        description='show current running app\'s basic information')

    group = parser.add_mutually_exclusive_group()
    group.add_argument('-p',
                       '--package',
                       action='store_const',
                       const=True,
                       default=False,
                       help='show current running package name')
    group.add_argument('-a',
                       '--activity',
                       action='store_const',
                       const=True,
                       default=False,
                       help='show current running activity name')
    group.add_argument('--path',
                       action='store_const',
                       const=True,
                       default=False,
                       help='show current running package path')
    group.add_argument('--kill',
                       action='store_const',
                       const=True,
                       default=False,
                       help='kill current running package')
    group.add_argument('--apk',
                       metavar='DEST',
                       action='store',
                       type=str,
                       nargs='?',
                       default=".",
                       help='pull current running apk file')
    group.add_argument('--screen',
                       metavar='DEST',
                       action='store',
                       type=str,
                       nargs='?',
                       default=".",
                       help='capture screen and pull file')

    args = parser.parse_args()
    device = args.parse_device()

    if args.package:
        logger.message(device.get_current_package())
    elif args.activity:
        logger.message(device.get_current_activity())
    elif args.path:
        logger.message(device.get_apk_path(device.get_current_package()))
    elif args.kill:
        device.shell("am",
                     "force-stop",
                     device.get_current_package(),
                     capture_output=False)
    elif "--apk" in sys.argv:
        package_name = device.get_current_package()
        logger.message("get current running package: {}".format(package_name))
        package = utils.get_item(
            device.get_packages(package_name, basic_info=True), 0)
        if package is not None:
            logger.message("get current running package path: {}".format(
                package.sourceDir))
            path = device.get_storage_path("{}_{}.apk".format(
                package.name, package.versionName))
            dest = args.apk if not utils.is_empty(args.apk) else "."
            device.shell("mkdir",
                         "-p",
                         device.get_storage_path(),
                         capture_output=False)
            device.shell("cp", package.sourceDir, path, capture_output=False)
            device.pull(path, dest, capture_output=False)
            device.shell("rm", path)
    elif "--screen" in sys.argv:
        now = datetime.datetime.now()
        path = device.get_storage_path("screenshot-" +
                                       now.strftime("%Y-%m-%d-%H-%M-%S") +
                                       ".png")
        dest = args.screen if not utils.is_empty(args.screen) else "."
        device.shell("mkdir",
                     "-p",
                     device.get_storage_path(),
                     capture_output=False)
        device.shell("screencap", "-p", path, capture_output=False)
        device.pull(path, dest, capture_output=False)
        device.shell("rm", path)
    else:
        package = device.get_current_package()
        logger.message("package:  ", package)
        logger.message("activity: ", device.get_current_activity())
        logger.message("path:     ", device.get_apk_path(package))
示例#10
0
 def print_line(self):
     logger.message()