140 lines
4.3 KiB
Python
140 lines
4.3 KiB
Python
import argparse
|
|
import codecs
|
|
import os
|
|
from datetime import datetime, timezone
|
|
from operator import itemgetter
|
|
from typing import Any, List
|
|
|
|
from colorama import Fore, Style
|
|
|
|
from frida_tools.application import ConsoleApplication
|
|
|
|
STYLE_DIR = Fore.BLUE + Style.BRIGHT
|
|
STYLE_EXECUTABLE = Fore.GREEN + Style.BRIGHT
|
|
STYLE_LINK = Fore.CYAN + Style.BRIGHT
|
|
STYLE_ERROR = Fore.RED + Style.BRIGHT
|
|
|
|
|
|
def main() -> None:
|
|
app = LsApplication()
|
|
app.run()
|
|
|
|
|
|
class LsApplication(ConsoleApplication):
|
|
def _add_options(self, parser: argparse.ArgumentParser) -> None:
|
|
parser.add_argument("files", help="files to list information about", nargs="*")
|
|
|
|
def _usage(self) -> str:
|
|
return "%(prog)s [options] [FILE]..."
|
|
|
|
def _initialize(self, parser: argparse.ArgumentParser, options: argparse.Namespace, args: List[str]) -> None:
|
|
self._files = options.files
|
|
|
|
def _needs_target(self) -> bool:
|
|
return False
|
|
|
|
def _start(self) -> None:
|
|
try:
|
|
self._attach(0)
|
|
|
|
data_dir = os.path.dirname(__file__)
|
|
with codecs.open(os.path.join(data_dir, "fs_agent.js"), "r", "utf-8") as f:
|
|
source = f.read()
|
|
|
|
def on_message(message: Any, data: Any) -> None:
|
|
print(message)
|
|
|
|
assert self._session is not None
|
|
script = self._session.create_script(name="ls", source=source)
|
|
script.on("message", on_message)
|
|
self._on_script_created(script)
|
|
script.load()
|
|
|
|
groups = script.exports_sync.ls(self._files)
|
|
except Exception as e:
|
|
self._update_status(f"Failed to retrieve listing: {e}")
|
|
self._exit(1)
|
|
return
|
|
|
|
exit_status = 0
|
|
for i, group in enumerate(sorted(groups, key=lambda g: g["path"])):
|
|
path = group["path"]
|
|
if path != "" and len(groups) > 1:
|
|
if i > 0:
|
|
self._print("")
|
|
self._print(path + ":")
|
|
|
|
for path, message in group["errors"]:
|
|
self._print(STYLE_ERROR + message + Style.RESET_ALL)
|
|
exit_status = 2
|
|
|
|
rows = []
|
|
for name, target, type, access, nlink, owner, group, size, raw_mtime in group["entries"]:
|
|
mtime = datetime.fromtimestamp(raw_mtime / 1000.0, tz=timezone.utc)
|
|
rows.append((type + access, str(nlink), owner, group, str(size), mtime.strftime("%c"), name, target))
|
|
|
|
if len(rows) == 0:
|
|
break
|
|
|
|
widths = []
|
|
for column_index in range(len(rows[0]) - 2):
|
|
width = max(map(lambda row: len(row[column_index]), rows))
|
|
widths.append(width)
|
|
|
|
adjustments = [
|
|
"",
|
|
">",
|
|
"<",
|
|
"<",
|
|
">",
|
|
"<",
|
|
]
|
|
col_formats = []
|
|
for i, width in enumerate(widths):
|
|
adj = adjustments[i]
|
|
if adj != "":
|
|
fmt = "{:" + adj + str(width) + "}"
|
|
else:
|
|
fmt = "{}"
|
|
col_formats.append(fmt)
|
|
row_description = " ".join(col_formats)
|
|
|
|
for row in sorted(rows, key=itemgetter(6)):
|
|
meta_fields = row_description.format(*row[:-2])
|
|
|
|
name, target = row[6:8]
|
|
ftype_and_perms = row[0]
|
|
ftype = ftype_and_perms[0]
|
|
fperms = ftype_and_perms[1:]
|
|
name = format_name(name, ftype, fperms, target)
|
|
|
|
self._print(meta_fields + " " + name)
|
|
|
|
self._exit(exit_status)
|
|
|
|
|
|
def format_name(name: str, ftype: str, fperms: str, target) -> str:
|
|
if ftype == "l":
|
|
target_path, target_details = target
|
|
if target_details is not None:
|
|
target_type, target_perms = target_details
|
|
target_summary = format_name(target_path, target_type, target_perms, None)
|
|
else:
|
|
target_summary = STYLE_ERROR + target_path + Style.RESET_ALL
|
|
return STYLE_LINK + name + Style.RESET_ALL + " -> " + target_summary
|
|
|
|
if ftype == "d":
|
|
return STYLE_DIR + name + Style.RESET_ALL
|
|
|
|
if "x" in fperms:
|
|
return STYLE_EXECUTABLE + name + Style.RESET_ALL
|
|
|
|
return name
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except KeyboardInterrupt:
|
|
pass
|