Skip to content

Commit 27652d3

Browse files
authored
Feat: Add Device Selection arg(-d/ --device) (#46)
* add: device var with arg * feat: add set_device function to configure target device * refactor: streamline logging messages and add device option to commands - Updated logging messages to remove f-string formatting for consistency. - Introduced a new device option to allow users to specify the target device when multiple devices are connected, enhancing usability across commands.
1 parent 0570cd7 commit 27652d3

File tree

2 files changed

+48
-20
lines changed

2 files changed

+48
-20
lines changed

android_unpinner/__main__.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from .vendor import frida_tools
1616
from .vendor import gadget_config_file_listen, gadget_config_file_script_directory
1717
from .vendor import gadget_files
18-
from .vendor.platform_tools import adb
18+
from .vendor.platform_tools import adb, set_device
1919

2020
here = Path(__file__).absolute().parent
2121
LIBGADGET = "libgadget.so"
@@ -35,13 +35,13 @@ def patch_apk_file(infile: Path, outfile: Path) -> None:
3535
):
3636
outfile.unlink()
3737

38-
logging.info(f"Make APK debuggable...")
38+
logging.info("Make APK debuggable...")
3939
frida_tools.apk.make_debuggable(
4040
str(infile),
4141
str(outfile),
4242
)
4343

44-
logging.info(f"Zipalign & re-sign APK...")
44+
logging.info("Zipalign & re-sign APK...")
4545
build_tools.zipalign(outfile)
4646
build_tools.sign(outfile)
4747

@@ -88,7 +88,7 @@ def install_apk(apk_files: list[Path]) -> None:
8888
if package_name in get_packages():
8989
if not force:
9090
click.confirm(
91-
f"About to install patched APK. This removes the existing app with all its data. Continue?",
91+
"About to install patched APK. This removes the existing app with all its data. Continue?",
9292
abort=True,
9393
)
9494

@@ -108,7 +108,7 @@ def copy_files() -> None:
108108
"""
109109
# TODO: We could later provide the option to use a custom script dir.
110110
ensure_device_connected()
111-
logging.info(f"Detect architecture...")
111+
logging.info("Detect architecture...")
112112
abi = adb("shell getprop ro.product.cpu.abi").stdout.strip()
113113
if abi == "armeabi-v7a":
114114
abi = "arm"
@@ -117,21 +117,21 @@ def copy_files() -> None:
117117
adb(f"push {gadget_file} /data/local/tmp/{LIBGADGET}")
118118
adb(f"push {gadget_config_file} /data/local/tmp/{LIBGADGET_CONF}")
119119

120-
logging.info(
121-
f"Copying builtin Frida scripts to /data/local/tmp/android-unpinner..."
122-
)
120+
logging.info("Copying builtin Frida scripts to /data/local/tmp/android-unpinner...")
123121
adb(f"push {here / 'scripts'}/. /data/local/tmp/android-unpinner/")
124-
active_scripts = adb(
125-
f"shell ls /data/local/tmp/android-unpinner"
126-
).stdout.splitlines(keepends=False)
122+
active_scripts = adb("shell ls /data/local/tmp/android-unpinner").stdout.splitlines(
123+
keepends=False
124+
)
127125
logging.info(f"Active frida scripts: {active_scripts}")
128126

129127

130128
def start_app_on_device(package_name: str) -> None:
131129
ensure_device_connected()
132130
logging.info("Start app (suspended)...")
133131
adb(f"shell am set-debug-app -w {package_name}")
134-
activity = adb(f"shell cmd \"package resolve-activity --brief {package_name} | tail -n 1\"").stdout.strip()
132+
activity = adb(
133+
f'shell cmd "package resolve-activity --brief {package_name} | tail -n 1"'
134+
).stdout.strip()
135135
adb(f"shell am start -n {activity}")
136136

137137
logging.info("Obtain process id...")
@@ -245,10 +245,25 @@ def _listen(ctx, param, val):
245245
)
246246

247247

248+
def _device(ctx, param, val):
249+
if val:
250+
set_device(val)
251+
252+
253+
device_option = click.option(
254+
"-d",
255+
"--device",
256+
help="Device serial number to use when multiple devices are connected.",
257+
callback=_device,
258+
expose_value=False,
259+
)
260+
261+
248262
@cli.command("all")
249263
@verbosity_option
250264
@force_option
251265
@listen_option
266+
@device_option
252267
@click.argument(
253268
"apk-files",
254269
type=click.Path(path_type=Path, exists=True),
@@ -278,6 +293,7 @@ def all_cmd(apk_files: list[Path]) -> None:
278293
@cli.command("install")
279294
@verbosity_option
280295
@force_option
296+
@device_option
281297
@click.argument(
282298
"apk-files",
283299
type=click.Path(path_type=Path, exists=True),
@@ -313,6 +329,7 @@ def patch_apks(apks: list[Path]) -> None:
313329
@verbosity_option
314330
@force_option
315331
@listen_option
332+
@device_option
316333
def push_resources() -> None:
317334
"""Copy Frida gadget and scripts to device."""
318335
copy_files()
@@ -322,6 +339,7 @@ def push_resources() -> None:
322339
@cli.command()
323340
@verbosity_option
324341
@force_option
342+
@device_option
325343
@click.argument("package-name")
326344
def start_app(package_name: str) -> None:
327345
"""Start app on device and inject Frida gadget."""
@@ -331,10 +349,11 @@ def start_app(package_name: str) -> None:
331349

332350
@cli.command()
333351
@verbosity_option
352+
@device_option
334353
def list_packages() -> None:
335354
"""List all packages installed on the device."""
336355
ensure_device_connected()
337-
logging.info(f"Enumerating packages...")
356+
logging.info("Enumerating packages...")
338357
print("\n".join(get_packages()))
339358
logging.info("All done! 🎉")
340359

@@ -349,6 +368,7 @@ def package_name(apk_file: Path) -> None:
349368
@cli.command()
350369
@verbosity_option
351370
@force_option
371+
@device_option
352372
@click.argument("package", type=str)
353373
@click.argument("outdir", type=click.Path(path_type=Path, file_okay=False))
354374
def get_apks(package: str, outdir: Path) -> None:

android_unpinner/vendor/platform_tools/__init__.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import sys
44
from pathlib import Path
55

6+
device: str | None = None
67
here = Path(__file__).absolute().parent
78

89
if sys.platform == "win32":
@@ -16,14 +17,21 @@
1617
def adb(cmd: str) -> subprocess.CompletedProcess[str]:
1718
"""Helper function to call adb and capture stdout."""
1819
cmd = f"{adb_binary} {cmd}"
20+
if device:
21+
cmd += f" -s {device}"
22+
logging.debug(f"Using device: {device}")
1923
try:
20-
proc = subprocess.run(cmd, shell=True, check=True, capture_output=True, text=True)
24+
proc = subprocess.run(
25+
cmd, shell=True, check=True, capture_output=True, text=True
26+
)
2127
except subprocess.CalledProcessError as e:
22-
logging.debug(f"cmd='{cmd}'\n"
23-
f"{e.stdout=}\n"
24-
f"{e.stderr=}")
28+
logging.debug(f"cmd='{cmd}'\n" f"{e.stdout=}\n" f"{e.stderr=}")
2529
raise
26-
logging.debug(f"cmd='{cmd}'\n"
27-
f"{proc.stdout=}\n"
28-
f"{proc.stderr=}")
30+
logging.debug(f"cmd='{cmd}'\n" f"{proc.stdout=}\n" f"{proc.stderr=}")
2931
return proc
32+
33+
34+
def set_device(device_serial: str | None) -> None:
35+
"""Set the target device for all adb commands."""
36+
global device
37+
device = device_serial

0 commit comments

Comments
 (0)