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
10 changes: 9 additions & 1 deletion .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install pytest rzpipe meson==0.62.0 ninja coverage ciphey frida objection
python -m pip install pytest rzpipe meson==0.62.0 ninja coverage ciphey frida objection r2pipe==1.8.0

# Install graphviz & ninja
sudo apt-get -y install graphviz ninja-build
Expand All @@ -39,6 +39,14 @@ jobs:
sudo ninja -C build install
sudo ldconfig -v
cd -

# Install Radare2 (5.8.8)
sudo apt install -y musl-tools
sudo git clone https://github.com/radareorg/radare2 /opt/radare2/
cd /opt/radare2/
sudo git checkout 5.8.8
sudo sys/install.sh
cd -

# Install click >= 8.0.0 for CLI supports
python -m pip install click==8.0.3
Expand Down
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ rzpipe = "<=0.1.2"
objection = "<=1.11.0"
frida = "<=15.2.2"
ciphey = ">=5.0.0,<=5.14.0"
r2pipe = "==1.8.0"

[requires]
python_version = "3.8"
Expand Down
2 changes: 1 addition & 1 deletion quark/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
"--core-library",
"core_library",
help="Specify the core library used to analyze an APK",
type=click.Choice(("androguard", "rizin"), case_sensitive=False),
type=click.Choice(("androguard", "rizin", "radare2"), case_sensitive=False),
required=False,
default="androguard",
)
Expand Down
42 changes: 23 additions & 19 deletions quark/core/axmlreader/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import pkg_resources
import rzpipe
import r2pipe

# Resource Types Definition
# Please reference to
Expand Down Expand Up @@ -84,7 +85,7 @@ class AxmlReader(object):
A Class that parses the Android XML file
"""

def __init__(self, file_path, structure_path=None):
def __init__(self, file_path, core_library="rizin", structure_path=None):
if structure_path is None:
structure_path = pkg_resources.resource_filename(
"quark.core.axmlreader", "axml_definition"
Expand All @@ -96,10 +97,13 @@ def __init__(self, file_path, structure_path=None):
f" of Rizin in {structure_path}"
)

self._rz = rzpipe.open(file_path)
self._rz.cmd(f"pfo {structure_path}")
if core_library == "rizin":
self._core = rzpipe.open(file_path)
else:
self._core = r2pipe.open(file_path)
self._core.cmd(f"pfo {structure_path}")

self._file_size = int(self._rz.cmd("i~size[1]"), 16)
self._file_size = int(self._core.cmd("i~size[1]"), 16)
self._ptr = 0

self._cache = {}
Expand All @@ -110,7 +114,7 @@ def __init__(self, file_path, structure_path=None):
raise AxmlException("Filesize exceeds theoretical lower bound.")

# File Header
header = self._rz.cmdj("pfj axml_ResChunk_header @ 0x0")
header = self._core.cmdj("pfj axml_ResChunk_header @ 0x0")

self._data_type = header[0]["value"]
self._axml_size = header[2]["value"]
Expand All @@ -133,7 +137,7 @@ def __init__(self, file_path, structure_path=None):
return

# String Pool
string_pool_header = self._rz.cmdj("pfj axml_ResStringPool_header @ 8")
string_pool_header = self._core.cmdj("pfj axml_ResStringPool_header @ 8")

string_pool_size = string_pool_header[0]["value"][2]["value"]

Expand Down Expand Up @@ -163,18 +167,18 @@ def __init__(self, file_path, structure_path=None):
self._stringCount = string_pool_header[1]["value"]
stringStart = string_pool_header[4]["value"]

self._rz.cmd(f"f string_pool_header @ 0x8 ")
self._core.cmd(f"f string_pool_header @ 0x8 ")
string_pool_index = header_size + self._ptr
self._rz.cmd(f"f string_pool_index @ { string_pool_index }")
self._core.cmd(f"f string_pool_index @ { string_pool_index }")
string_pool_data = stringStart + self._ptr
self._rz.cmd(f"f string_pool_data @ { string_pool_data }")
self._core.cmd(f"f string_pool_data @ { string_pool_data }")

self._ptr += string_pool_size
if self._ptr >= self._axml_size:
return

# Resource Map (Optional)
header = self._rz.cmdj(f"pfj axml_ResChunk_header @ {self._ptr}")
header = self._core.cmdj(f"pfj axml_ResChunk_header @ {self._ptr}")

header_type = header[0]["value"]
if header_type == RES_XML_RESOURCE_MAP_TYPE:
Expand All @@ -201,7 +205,7 @@ def __iter__(self) -> Iterator[ResChunkHeader]:
:yield: header of a resource chunk defined in the binary
"""
while self._axml_size - self._ptr >= 16:
header = self._rz.cmdj(f"pfj axml_ResXMLTree_node @ {self._ptr}")
header = self._core.cmdj(f"pfj axml_ResXMLTree_node @ {self._ptr}")

node_type = header[0]["value"][0]["value"]
header_size = header[0]["value"][1]["value"]
Expand All @@ -224,7 +228,7 @@ def __iter__(self) -> Iterator[ResChunkHeader]:
chunk = {"Address": self._ptr, "Type": node_type}

if node_type == RES_XML_START_ELEMENT_TYPE:
ext = self._rz.cmdj(
ext = self._core.cmdj(
f"pfj axml_ResXMLTree_attrExt @ { ext_ptr }"
)

Expand All @@ -235,7 +239,7 @@ def __iter__(self) -> Iterator[ResChunkHeader]:
# node['AttrCount'] = ext[4]['value']

elif node_type == RES_XML_END_ELEMENT_TYPE:
ext = self._rz.cmdj(
ext = self._core.cmdj(
f"pfj axml_ResXMLTree_endElementExt @ { ext_ptr }"
)

Expand All @@ -246,15 +250,15 @@ def __iter__(self) -> Iterator[ResChunkHeader]:
RES_XML_START_NAMESPACE_TYPE,
RES_XML_END_NAMESPACE_TYPE,
]:
ext = self._rz.cmdj(
ext = self._core.cmdj(
f"pfj axml_ResXMLTree_namespaceExt @ { ext_ptr }"
)

chunk["Prefix"] = ext[0]["value"][0]["value"]
chunk["Uri"] = ext[1]["value"][0]["value"]

elif node_type == RES_XML_CDATA_TYPE:
ext = self._rz.cmdj(
ext = self._core.cmdj(
f"pfj axml_ResXMLTree_cdataExt @ { ext_ptr }"
)

Expand All @@ -281,7 +285,7 @@ def get_string(self, index):
if index < 0 or index >= self._stringCount:
return None

return self._rz.cmdj(
return self._core.cmdj(
f"pfj Z @ string_pool_data + `pfv n4 "
f"@ string_pool_index+ {index}*4` + 2"
)[0]["string"]
Expand All @@ -296,14 +300,14 @@ def get_attributes(self, chunk: ResChunkHeader) -> List[ResValue]:
return None
extAddress = int(chunk["Address"]) + 16

attrExt = self._rz.cmdj(f"pfj axml_ResXMLTree_attrExt @ {extAddress}")
attrExt = self._core.cmdj(f"pfj axml_ResXMLTree_attrExt @ {extAddress}")

attrAddress = extAddress + attrExt[2]["value"]
attributeSize = attrExt[3]["value"]
attributeCount = attrExt[4]["value"]
attributes = []
for _ in range(attributeCount):
attr = self._rz.cmdj(
attr = self._core.cmdj(
f"pfj axml_ResXMLTree_attribute @ {attrAddress}"
)

Expand Down Expand Up @@ -402,6 +406,6 @@ def get_xml_tree(self) -> XMLElementTree:

def __del__(self):
try:
self._rz.quit()
self._core.quit()
except BaseException:
pass
3 changes: 3 additions & 0 deletions quark/core/quark.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from quark.core.analysis import QuarkAnalysis
from quark.core.apkinfo import AndroguardImp
from quark.core.rzapkinfo import RizinImp
from quark.core.r2apkinfo import R2Imp
from quark.evaluator.pyeval import PyEval
from quark.utils import tools
from quark.utils.colors import (
Expand Down Expand Up @@ -49,6 +50,8 @@ def __init__(self, apk, core_library="androguard"):
core_library = core_library.lower()
if core_library == "rizin":
self.apkinfo = RizinImp(apk)
elif core_library == "radare2":
self.apkinfo = R2Imp(apk)
elif core_library == "androguard":
self.apkinfo = AndroguardImp(apk)
else:
Expand Down
Loading