automagical frida setup

This commit is contained in:
Jean-Marie 'Histausse' Mineau 2025-03-14 13:22:47 +01:00
parent 5b4dd74a5e
commit 6380ce9917
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
4 changed files with 123 additions and 57 deletions

78
frida/poetry.lock generated
View file

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.
# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
[[package]]
name = "alembic"
@ -18,7 +18,7 @@ SQLAlchemy = ">=1.3.0"
typing-extensions = ">=4"
[package.extras]
tz = ["backports.zoneinfo", "tzdata"]
tz = ["backports.zoneinfo ; python_version < \"3.9\"", "tzdata"]
[[package]]
name = "androguard"
@ -267,7 +267,7 @@ files = [
]
[package.extras]
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""]
[[package]]
name = "fonttools"
@ -330,40 +330,40 @@ files = [
]
[package.extras]
all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"]
all = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\"", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0) ; python_version <= \"3.12\"", "xattr ; sys_platform == \"darwin\"", "zopfli (>=0.1.4)"]
graphite = ["lz4 (>=1.7.4.2)"]
interpolatable = ["munkres", "pycairo", "scipy"]
interpolatable = ["munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\""]
lxml = ["lxml (>=4.0)"]
pathops = ["skia-pathops (>=0.5.0)"]
plot = ["matplotlib"]
repacker = ["uharfbuzz (>=0.23.0)"]
symfont = ["sympy"]
type1 = ["xattr"]
type1 = ["xattr ; sys_platform == \"darwin\""]
ufo = ["fs (>=2.2.0,<3)"]
unicode = ["unicodedata2 (>=15.1.0)"]
woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
unicode = ["unicodedata2 (>=15.1.0) ; python_version <= \"3.12\""]
woff = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "zopfli (>=0.1.4)"]
[[package]]
name = "frida"
version = "16.6.6"
version = "16.7.0"
description = "Dynamic instrumentation toolkit for developers, reverse-engineers, and security researchers"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "frida-16.6.6-cp37-abi3-macosx_10_13_x86_64.whl", hash = "sha256:b00a2b5f877cbb4955e72413c1f620dc75498a93d4d9a01f663053f2cfb94992"},
{file = "frida-16.6.6-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:dcf59869117f4f6eaa6514555fa0a6499b5efa4e095a5785979b057841037f04"},
{file = "frida-16.6.6-cp37-abi3-manylinux1_i686.whl", hash = "sha256:9dc05717c8079cad31268a9714cc6966931e7400487cfa0c19e6c077dea129ac"},
{file = "frida-16.6.6-cp37-abi3-manylinux1_x86_64.whl", hash = "sha256:288d99fb774bd04b229977bf112878674e851dbb6e14b528aeb04ec085b10a4f"},
{file = "frida-16.6.6-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:d74bd8de1aebd0acae49c89fad6ace2dddf5852df236b805dc83d5c524745e59"},
{file = "frida-16.6.6-cp37-abi3-manylinux2014_armv7l.whl", hash = "sha256:853e1e786b24b726ea0e91e6855ca879271bf3dd4265085c019c19d716f489fd"},
{file = "frida-16.6.6-cp37-abi3-manylinux_2_17_aarch64.whl", hash = "sha256:ab9ca0e41e89cc881dbaa33c724066c9839c1b92a06b9f92fa9ab00dc31d62e0"},
{file = "frida-16.6.6-cp37-abi3-manylinux_2_17_armv7l.whl", hash = "sha256:47aaacb1246a0f8587a1f4083761189aea39c51ce545cf998076814172d31770"},
{file = "frida-16.6.6-cp37-abi3-manylinux_2_5_i686.whl", hash = "sha256:9ba5c342fc7a46599a4835e2fb3693342ec732963a0518b2d884d951bb5ea5f4"},
{file = "frida-16.6.6-cp37-abi3-manylinux_2_5_x86_64.whl", hash = "sha256:f27b230dd243ab3708f4051bcd9567cfe02893a706c5c3fe1573ccc2ef5a2969"},
{file = "frida-16.6.6-cp37-abi3-win32.whl", hash = "sha256:44b4044625afa60f0799fedecfe0a25dab610556a469151dc02569555d0436a9"},
{file = "frida-16.6.6-cp37-abi3-win_amd64.whl", hash = "sha256:12d0117ea035bfcb5e4d685e88af02ce815169945702192ea9c21c0ebba42706"},
{file = "frida-16.6.6.tar.gz", hash = "sha256:384572cd21185e6b152628216e83fcddbb869572ad55609582580494ce8b6c2a"},
{file = "frida-16.7.0-cp37-abi3-macosx_10_13_x86_64.whl", hash = "sha256:5f89f5a0143b5c0ed6e9ea6357ab0a948cf9ff4da650c3a14b7d49b26ff67eae"},
{file = "frida-16.7.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:459fe6f876e075c1a0a7d3f5f577207f2531f24700eed6cb3c4494b1089f9629"},
{file = "frida-16.7.0-cp37-abi3-manylinux1_i686.whl", hash = "sha256:e85c5112d866b412b7ad079b5f2cf8869187a535f35bcfafdf59f25b6a4f2df9"},
{file = "frida-16.7.0-cp37-abi3-manylinux1_x86_64.whl", hash = "sha256:d28437747f9bb2624be4c97ffc15912ff5c3d0279227ad1024e20485525c49fd"},
{file = "frida-16.7.0-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:d65656ab3f393ecacb491f15284db962ce35875a5dba3dc658c4715de886949d"},
{file = "frida-16.7.0-cp37-abi3-manylinux2014_armv7l.whl", hash = "sha256:415afa2435c530fb416c156edeb870340809a54e6fd21b0e49c7e3e440377cde"},
{file = "frida-16.7.0-cp37-abi3-manylinux_2_17_aarch64.whl", hash = "sha256:d854158411644e620a915209adff5cb0291ab42704f0abecdb9807132ec8c2dc"},
{file = "frida-16.7.0-cp37-abi3-manylinux_2_17_armv7l.whl", hash = "sha256:42e46321de58faaf2bd2b23faddbd33133d02f73c9d946ce87bbdb66aa831e83"},
{file = "frida-16.7.0-cp37-abi3-manylinux_2_5_i686.whl", hash = "sha256:2674f36a5ef9218e116e5a92eb37fd444f31b7db3f9d3615ac17c8ab21ba8281"},
{file = "frida-16.7.0-cp37-abi3-manylinux_2_5_x86_64.whl", hash = "sha256:fdc3b59a2ad452b1fb52a4c651cef1140ce9facbd09ba6ff411aa2122d1424a1"},
{file = "frida-16.7.0-cp37-abi3-win32.whl", hash = "sha256:c3ed99c47752a3c945cbaac71b662199eb5d0dc9fc9d0ba0e7b932896e35e946"},
{file = "frida-16.7.0-cp37-abi3-win_amd64.whl", hash = "sha256:735ab69be575e3c9cfd7e3af2daa52423ea1636073bc28657414c8bc2cefba09"},
{file = "frida-16.7.0.tar.gz", hash = "sha256:2ffa6ee671ba17ba51ad347b451d6f5ad392d86dc0d008741cac93a8ec278f7f"},
]
[[package]]
@ -498,7 +498,7 @@ traitlets = ">=5.13.0"
[package.extras]
all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"]
black = ["black"]
doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing_extensions"]
doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli ; python_version < \"3.11\"", "typing_extensions"]
kernel = ["ipykernel"]
matplotlib = ["matplotlib"]
nbconvert = ["nbconvert"]
@ -636,7 +636,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
[package.extras]
dev = ["Sphinx (==8.1.3)", "build (==1.2.2)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.5.0)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.13.0)", "mypy (==v1.4.1)", "myst-parser (==4.0.0)", "pre-commit (==4.0.1)", "pytest (==6.1.2)", "pytest (==8.3.2)", "pytest-cov (==2.12.1)", "pytest-cov (==5.0.0)", "pytest-cov (==6.0.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.1.0)", "sphinx-rtd-theme (==3.0.2)", "tox (==3.27.1)", "tox (==4.23.2)", "twine (==6.0.1)"]
dev = ["Sphinx (==8.1.3) ; python_version >= \"3.11\"", "build (==1.2.2) ; python_version >= \"3.11\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.5.0) ; python_version >= \"3.8\"", "mypy (==v0.910) ; python_version < \"3.6\"", "mypy (==v0.971) ; python_version == \"3.6\"", "mypy (==v1.13.0) ; python_version >= \"3.8\"", "mypy (==v1.4.1) ; python_version == \"3.7\"", "myst-parser (==4.0.0) ; python_version >= \"3.11\"", "pre-commit (==4.0.1) ; python_version >= \"3.9\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==8.3.2) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==5.0.0) ; python_version == \"3.8\"", "pytest-cov (==6.0.0) ; python_version >= \"3.9\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.1.0) ; python_version >= \"3.8\"", "sphinx-rtd-theme (==3.0.2) ; python_version >= \"3.11\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.23.2) ; python_version >= \"3.8\"", "twine (==6.0.1) ; python_version >= \"3.11\""]
[[package]]
name = "lxml"
@ -1213,7 +1213,7 @@ docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline
fpx = ["olefile"]
mic = ["olefile"]
tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "trove-classifiers (>=2024.10.12)"]
typing = ["typing-extensions"]
typing = ["typing-extensions ; python_version < \"3.10\""]
xmp = ["defusedxml"]
[[package]]
@ -1457,25 +1457,25 @@ files = [
greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
[package.extras]
aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"]
aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"]
asyncio = ["greenlet (!=0.4.17)"]
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"]
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)", "mariadb (>=1.0.1,!=1.1.2)"]
aiomysql = ["aiomysql (>=0.2.0) ; python_version >= \"3\"", "greenlet (!=0.4.17) ; python_version >= \"3\""]
aiosqlite = ["aiosqlite ; python_version >= \"3\"", "greenlet (!=0.4.17) ; python_version >= \"3\"", "typing_extensions (!=3.10.0.1)"]
asyncio = ["greenlet (!=0.4.17) ; python_version >= \"3\""]
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4) ; python_version >= \"3\"", "greenlet (!=0.4.17) ; python_version >= \"3\""]
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2) ; python_version >= \"3\"", "mariadb (>=1.0.1,!=1.1.2) ; python_version >= \"3\""]
mssql = ["pyodbc"]
mssql-pymssql = ["pymssql", "pymssql"]
mssql-pyodbc = ["pyodbc", "pyodbc"]
mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"]
mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"]
mypy = ["mypy (>=0.910) ; python_version >= \"3\"", "sqlalchemy2-stubs"]
mysql = ["mysqlclient (>=1.4.0) ; python_version >= \"3\"", "mysqlclient (>=1.4.0,<2) ; python_version < \"3\""]
mysql-connector = ["mysql-connector-python", "mysql-connector-python"]
oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"]
oracle = ["cx_oracle (>=7) ; python_version >= \"3\"", "cx_oracle (>=7,<8) ; python_version < \"3\""]
postgresql = ["psycopg2 (>=2.7)"]
postgresql-asyncpg = ["asyncpg", "asyncpg", "greenlet (!=0.4.17)", "greenlet (!=0.4.17)"]
postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)", "pg8000 (>=1.16.6,!=1.29.0)"]
postgresql-asyncpg = ["asyncpg ; python_version >= \"3\"", "asyncpg ; python_version >= \"3\"", "greenlet (!=0.4.17) ; python_version >= \"3\"", "greenlet (!=0.4.17) ; python_version >= \"3\""]
postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0) ; python_version >= \"3\"", "pg8000 (>=1.16.6,!=1.29.0) ; python_version >= \"3\""]
postgresql-psycopg2binary = ["psycopg2-binary"]
postgresql-psycopg2cffi = ["psycopg2cffi"]
pymysql = ["pymysql", "pymysql (<1)"]
sqlcipher = ["sqlcipher3_binary"]
pymysql = ["pymysql (<1) ; python_version < \"3\"", "pymysql ; python_version >= \"3\""]
sqlcipher = ["sqlcipher3_binary ; python_version >= \"3\""]
[[package]]
name = "stack-data"
@ -1647,9 +1647,9 @@ files = [
]
[package.extras]
dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"]
[metadata]
lock-version = "2.1"
python-versions = ">=3.13,<4.0.0"
content-hash = "5ae1cb4abaa9c3a3056fc7950f540ddb6ce3c43d6f1440b2cffd0cf4f5778fcf"
content-hash = "5f31562f74268b8fd66962ef54ae7ac09e50583c27137cccf77230d2c1461c2d"

View file

@ -8,7 +8,7 @@ authors = [
readme = "README.md"
requires-python = ">=3.13,<4.0.0"
dependencies = [
"frida (>=16.6.6,<17.0.0)",
"frida (==16.7.0)",
"frida-tools (>=13.6.1,<14.0.0)",
"androguard (>=4.1.2,<5.0.0)"
]
@ -24,5 +24,6 @@ collect-runtime-data = 'theseus_frida.__init__:main'
[tool.poetry]
include = [
{ path = "theseus_frida/hook.js", format = ["sdist", "wheel"] },
{ path = "theseus_frida/StackConsumer.dex.b64", format = ["sdist", "wheel"] }
{ path = "theseus_frida/StackConsumer.dex.b64", format = ["sdist", "wheel"] },
{ path = "theseus_frida/frida-server-16.7.0-android-x86_64.xz", format = ["sdist", "wheel"] }
]

View file

@ -24,8 +24,8 @@ HASH_NB_BYTES = 4
# Define handler to event generated by the scripts
def on_message(message, data, data_storage: dict, file_storage: Path):
if message["type"] == "error":
print(f"[error] {message['description']}")
print(message["stack"])
print(f"[!] {message['description']}")
print(" " + message["stack"].replace("\n", "\n "))
elif message["type"] == "send" and message["payload"]["type"] == "invoke":
handle_invoke_data(message["payload"]["data"], data_storage)
elif message["type"] == "send" and message["payload"]["type"] == "class-new-inst":
@ -35,7 +35,7 @@ def on_message(message, data, data_storage: dict, file_storage: Path):
elif message["type"] == "send" and message["payload"]["type"] == "load-dex":
handle_load_dex(message["payload"]["data"], data_storage, file_storage)
else:
print("[on_message] message:", message)
print("[-] message:", message)
def print_stack(stack, prefix: str):
@ -63,7 +63,7 @@ def handle_invoke_data(data, data_storage: dict):
is_static_str = " (static)"
else:
is_static_str = ""
print("Method.Invoke:")
print("[+] Method.Invoke:")
print(f" called: {method}{is_static_str}")
print(f" by: {caller_method}")
print(f" at: 0x{addr:08x}")
@ -96,7 +96,7 @@ def handle_class_new_inst_data(data, data_storage: dict):
return
caller_method = frame["method"]
addr = frame["bytecode_index"]
print("Class.NewInstance:")
print("[+] Class.NewInstance:")
print(f" called: {constructor}")
print(f" by: {caller_method}")
print(f" at: 0x{addr:08x}")
@ -121,7 +121,7 @@ def handle_cnstr_new_inst_data(data, data_storage: dict):
return
caller_method = data["stack"][0]["method"]
addr = data["stack"][0]["bytecode_index"]
print("Constructor.newInstance:")
print("[+] Constructor.newInstance:")
print(f" called: {constructor}")
print(f" by: {caller_method}")
print(f" at: 0x{addr:08x}")
@ -147,7 +147,7 @@ def handle_load_dex(data, data_storage: dict, file_storage: Path):
classloader = classloader.to_bytes(HASH_NB_BYTES).hex()
short_class = classloader_class.split("/")[-1].removesuffix(";")
files = []
print("DEX file loaded:")
print("[+] DEX file loaded:")
print(f" by: {classloader_class} ({classloader})")
for file in dex:
file_bin = base64.b64decode(file)
@ -177,6 +177,73 @@ def handle_load_dex(data, data_storage: dict, file_storage: Path):
)
FRIDA_SERVER_BIN = Path(__file__).parent / "frida-server-16.7.0-android-x86_64.xz"
FRIDA_SERVER_ANDROID_PATH = "/data/local/tmp/frida-server"
def setup_frida(device: str, env: dict[str, str]) -> frida.core.Device:
if device != "":
device = frida.get_device(args.device)
env["ANDROID_SERIAL"] = args.device
else:
device = frida.get_usb_device()
try:
s = device.attach(0)
s.detach()
return device
except frida.ServerNotRunningError:
pass
# Start server
proc = subprocess.run(
["adb", "shell", "whoami"], encoding="utf-8", stdout=subprocess.PIPE, env=env
)
if proc.stdout.strip() != "root":
proc = subprocess.run(["adb", "root"], env=env)
# Rooting adb will disconnect the device
if device != "":
device = frida.get_device(device)
else:
device = frida.get_usb_device()
perm = subprocess.run(
["adb", "shell", "stat", "-c", "%a", FRIDA_SERVER_ANDROID_PATH],
encoding="utf-8",
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
).stdout.strip()
need_perm_resset = (perm == "") or perm[0] not in [
"1",
"3",
"5",
"7",
] # int(perm[0]) & 1 == 1
if perm == "":
subprocess.run(
[
"adb",
"push",
str(FRIDA_SERVER_BIN.absolute()),
FRIDA_SERVER_ANDROID_PATH,
],
env=env,
)
if need_perm_resset:
subprocess.run(["adb", "chmod", "755", FRIDA_SERVER_ANDROID_PATH], env=env)
subprocess.Popen(["adb", "shell", FRIDA_SERVER_ANDROID_PATH], env=env)
# The server take some time to start
# time.sleep(3)
while True:
try:
s = device.attach(0)
s.detach()
print("[*] Server started: begin analysis ")
return device
except frida.ServerNotRunningError:
print("[-] Waiting for frida server to start", end="\r")
time.sleep(0.3)
def main():
parser = argparse.ArgumentParser(
prog="Android Theseus project",
@ -212,14 +279,10 @@ def main():
if not file_storage.exists():
file_storage.mkdir(parents=True)
if not file_storage.is_dir():
print("--dex-dir must be a directory")
print("[!] --dex-dir must be a directory")
exit()
if args.device != "":
device = frida.get_device(args.device)
env["ANDROID_SERIAL"] = args.device
else:
device = frida.get_usb_device()
device = setup_frida(args.device, env)
app = get_apkid(args.apk)[0]
@ -241,8 +304,10 @@ def main():
try:
script = session.create_script(script)
except frida.InvalidArgumentError as e:
print("[!] Error:")
print(
"\n".join(
" "
+ "\n ".join(
map(lambda v: f"{v[0]+1: 3} {v[1]}", enumerate(script.split("\n")))
)
)
@ -265,7 +330,7 @@ def main():
# Resume the execution of the APK
device.resume(pid)
print("Press ENTER to finish the analysis")
print("==> Press ENTER to finish the analysis <==")
input()
if args.output is None:
print(json.dumps(data_storage, indent=" "))