Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions droidasc/asc_client/apk_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,10 @@ def get_class_dex(self, dalvik_class : str):
fp.close()

def list_classes(self, prefix : str = None):
# deferred like the ProcessPoolExecutor import in for_each_findrefs, so the
# getclass path does not pay for it
from droidasc.asc_core.utils.tinydex import _decode_mutf8_fallback

if self.max_workers <= 0:
raise ValueError("Worker count must be greater than zero")
if prefix is not None:
Expand All @@ -425,6 +429,15 @@ def list_classes(self, prefix : str = None):
prefix = "L" + prefix.replace(".", "/")
prefix_bytes = prefix.encode("ascii") if prefix and prefix.isascii() else None

def decode_name(raw_name : bytes) -> str:
# string_data_item is MUTF-8, so strict UTF-8 rejects a surrogate pair
# above the BMP; decode exactly as DEX.get_string does, or a listing
# names a class differently from every other command
try:
return raw_name.decode("utf-8")
except UnicodeDecodeError:
return _decode_mutf8_fallback(raw_name)

def list_dex_classes(buf):
if len(buf) < 0x70 or buf[:3] != b"dex":
raise ValueError("invalid DEX header")
Expand Down Expand Up @@ -464,9 +477,9 @@ def list_dex_classes(buf):

if prefix_bytes is not None:
if raw_name.startswith(prefix_bytes):
append(raw_name.decode("utf-8", errors="replace"))
append(decode_name(raw_name))
else:
name = raw_name.decode("utf-8", errors="replace")
name = decode_name(raw_name)
if prefix is None or name.startswith(prefix):
append(name)
return names
Expand Down
7 changes: 6 additions & 1 deletion droidasc/asc_core/findrefs/locator/field_locator.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,12 @@ def locate(self, find : dict) -> set:

if clz is None:
if field_precise:
return {fid for fid, item in enumerate(self.dex.fields) if item.name == field}
# see MethodLocator.locate: dex.fields is a proxy without __iter__,
# so enumerate() walks off the field table instead of stopping at it
return {
fid for fid in range(len(self.dex.fields))
if self.dex.fields[fid].name == field
}
name_idxs = self.str_locator.locate(field)
field_maps = self.field_maps
ret = set()
Expand Down
8 changes: 7 additions & 1 deletion droidasc/asc_core/findrefs/locator/method_locator.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,13 @@ def locate(self, find : dict) -> set:

if clz is None:
if method_precise:
return {mid for mid, item in enumerate(self.dex.methods) if item.name == method}
# dex.methods is a proxy without __iter__, so enumerate() falls back
# to the sequence protocol and never sees an IndexError - it walks off
# the method table into garbage. Bound the walk by the table size.
return {
mid for mid in range(len(self.dex.methods))
if self.dex.methods[mid].name == method
}
name_idxs = self.str_locator.locate(method)
method_maps = self.method_maps
ret = set()
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ build-backend = "setuptools.build_meta"

[project]
name = "droidasc"
version = "0.1.1.post4"
version = "0.1.1.post5"
description = "ASC is a super FAST python Android Decompiler for Agents/Mobile Researchers"
readme = "README.md"
license = "Apache-2.0"
license-files = ["LICENSE"]
requires-python = ">=3.10"
dependencies = [
"axml>=0.0.2",
"mutf8>=1.1.0",
"mutf8>=1.0.6",
]

[project.scripts]
Expand Down
Loading