trueseeing 2.2.1__tar.gz → 2.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. trueseeing-2.2.4/Dockerfile +26 -0
  2. {trueseeing-2.2.1 → trueseeing-2.2.4}/PKG-INFO +17 -8
  3. {trueseeing-2.2.1 → trueseeing-2.2.4}/README.md +11 -5
  4. {trueseeing-2.2.1 → trueseeing-2.2.4}/pyproject.toml +6 -2
  5. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/__init__.py +1 -1
  6. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/api.py +8 -4
  7. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/cmd/__init__.py +1 -2
  8. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/cmd/analyze.py +6 -1
  9. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/cmd/android/asm.py +13 -5
  10. trueseeing-2.2.4/trueseeing/app/cmd/android/engage.py +1000 -0
  11. trueseeing-2.2.4/trueseeing/app/cmd/android/recon.py +316 -0
  12. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/cmd/android/search.py +52 -36
  13. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/cmd/android/show.py +46 -34
  14. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/cmd/config.py +42 -17
  15. trueseeing-2.2.4/trueseeing/app/cmd/info.py +64 -0
  16. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/cmd/scan.py +10 -4
  17. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/cmd/search.py +1 -1
  18. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/inspect.py +116 -49
  19. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/scan.py +2 -8
  20. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/shell.py +17 -1
  21. trueseeing-2.2.4/trueseeing/core/android/analysis/flow.py +357 -0
  22. trueseeing-2.2.4/trueseeing/core/android/analysis/op.py +55 -0
  23. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/android/asm.py +38 -10
  24. trueseeing-2.2.4/trueseeing/core/android/context.py +422 -0
  25. trueseeing-2.2.4/trueseeing/core/android/db.py +136 -0
  26. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/android/device.py +13 -7
  27. trueseeing-2.2.4/trueseeing/core/android/model.py +60 -0
  28. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/context.py +99 -26
  29. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/db.py +36 -35
  30. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/env.py +5 -1
  31. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/model/issue.py +3 -5
  32. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/report.py +2 -6
  33. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/scan.py +38 -8
  34. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/store.py +12 -14
  35. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/tools.py +39 -20
  36. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/ui.py +87 -12
  37. trueseeing-2.2.4/trueseeing/core/z.py +8 -0
  38. trueseeing-2.2.4/trueseeing/libs/android/frida-app.smali +16 -0
  39. trueseeing-2.2.4/trueseeing/libs/android/frida-scriptdir.config +7 -0
  40. trueseeing-2.2.4/trueseeing/libs/android/store.0.sql +10 -0
  41. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/libs/store.s.sql +1 -0
  42. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/sig/__init__.py +1 -2
  43. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/sig/android/crypto.py +68 -60
  44. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/sig/android/fingerprint.py +23 -17
  45. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/sig/android/manifest.py +8 -8
  46. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/sig/android/privacy.py +14 -14
  47. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/sig/android/security.py +75 -73
  48. trueseeing-2.2.1/Dockerfile +0 -17
  49. trueseeing-2.2.1/trueseeing/app/cmd/android/exploit.py +0 -445
  50. trueseeing-2.2.1/trueseeing/app/cmd/info.py +0 -110
  51. trueseeing-2.2.1/trueseeing/core/android/analysis/flow.py +0 -331
  52. trueseeing-2.2.1/trueseeing/core/android/analysis/smali.py +0 -147
  53. trueseeing-2.2.1/trueseeing/core/android/context.py +0 -152
  54. trueseeing-2.2.1/trueseeing/core/android/db.py +0 -188
  55. trueseeing-2.2.1/trueseeing/core/android/model/code.py +0 -55
  56. trueseeing-2.2.1/trueseeing/libs/android/store.0.sql +0 -4
  57. trueseeing-2.2.1/trueseeing/libs/android/store.1.sql +0 -77
  58. trueseeing-2.2.1/trueseeing/sig/android/__init__.py +0 -0
  59. {trueseeing-2.2.1 → trueseeing-2.2.4}/.dockerignore +0 -0
  60. {trueseeing-2.2.1 → trueseeing-2.2.4}/.github/workflows/deploy.yaml +0 -0
  61. {trueseeing-2.2.1 → trueseeing-2.2.4}/.github/workflows/lint.yaml +0 -0
  62. {trueseeing-2.2.1 → trueseeing-2.2.4}/.github/workflows/publish.yaml +0 -0
  63. {trueseeing-2.2.1 → trueseeing-2.2.4}/.github/workflows/stale.yaml +0 -0
  64. {trueseeing-2.2.1 → trueseeing-2.2.4}/.gitignore +0 -0
  65. {trueseeing-2.2.1 → trueseeing-2.2.4}/COPYING +0 -0
  66. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/__init__.py +0 -0
  67. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/cmd/alias.py +0 -0
  68. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/cmd/android/__init__.py +0 -0
  69. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/cmd/report.py +0 -0
  70. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/app/cmd/show.py +0 -0
  71. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/__init__.py +0 -0
  72. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/android/analysis/__init__.py +0 -0
  73. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/android/store.py +0 -0
  74. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/android/tools.py +0 -0
  75. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/config.py +0 -0
  76. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/cvss.py +0 -0
  77. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/exc.py +0 -0
  78. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/ext.py +0 -0
  79. {trueseeing-2.2.1/trueseeing/core/android → trueseeing-2.2.4/trueseeing/core}/model/__init__.py +0 -0
  80. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/model/cmd.py +0 -0
  81. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/core/model/sig.py +0 -0
  82. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/libs/LICENSE.md +0 -0
  83. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/libs/android/abe.jar +0 -0
  84. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/libs/android/apkeditor.jar +0 -0
  85. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/libs/android/apksigner.jar +0 -0
  86. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/libs/android/nsc.xml +0 -0
  87. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/libs/files.0.sql +0 -0
  88. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/libs/public_suffix_list.dat +0 -0
  89. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/libs/store.0.sql +0 -0
  90. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/libs/template/report.html +0 -0
  91. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/libs/tlds.txt +0 -0
  92. {trueseeing-2.2.1 → trueseeing-2.2.4}/trueseeing/py.typed +0 -0
  93. {trueseeing-2.2.1/trueseeing/core/model → trueseeing-2.2.4/trueseeing/sig/android}/__init__.py +0 -0
@@ -0,0 +1,26 @@
1
+ from python:3.9-alpine
2
+ run pip install flit
3
+ copy . /tmp/build/
4
+ run (cd /tmp/build && flit build)
5
+
6
+ from python:3.9-alpine
7
+ run mkdir /tmp/dist
8
+ # Building LIEF takes a long time so fetch from our wheel cache
9
+ add https://github.com/alterakey/trueseeing-lief/raw/main/dist/lief-0.14.1-cp39-cp39-musllinux_1_2_aarch64.whl /tmp/dist
10
+ add https://github.com/alterakey/trueseeing-lief/raw/main/dist/lief-0.14.1-cp39-cp39-musllinux_1_2_x86_64.whl /tmp/dist
11
+ # .. if you really prefer building it, comment above and uncomment below
12
+ #run (cd /tmp/dist && pip download 'lief~=0.14') || (apk add --no-cache build-base ninja cmake git ccache && git clone -b 0.14.1 https://github.com/lief-project/LIEF.git /tmp/build && (cd /tmp/build/api/python && pip install -r build-requirements.txt && pyproject-build -w && cp -a dist/*.whl /tmp/dist/))
13
+
14
+ from python:3.9-alpine
15
+ run apk add --no-cache openjdk17-jre-headless zip android-tools
16
+ run mkdir /data /ext /cache /out && ln -sfn /cache /root/.local
17
+ copy --from=0 /tmp/build/dist/*.whl /tmp/dist/
18
+ copy --from=1 /tmp/dist/*.whl /tmp/ext/
19
+ run pip install -f /tmp/ext/ /tmp/dist/*.whl && rm -rf /tmp/dist /tmp/ext
20
+ env PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
21
+ env TS2_IN_DOCKER=1
22
+ env TS2_CACHEDIR=/cache
23
+ env TS2_HOME=/data
24
+ env TS2_EXTDIR=/ext
25
+ workdir /out
26
+ entrypoint ["trueseeing"]
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: trueseeing
3
- Version: 2.2.1
3
+ Version: 2.2.4
4
4
  Summary: Trueseeing is a non-decompiling Android application vulnerability scanner.
5
5
  Keywords: android,security,pentest,hacking
6
- Author-email: Takahiro Yoshimura <altakey@gmail.com>
6
+ Author-email: Takahiro Yoshimura <alterakey@protonmail.com>
7
7
  Requires-Python: >=3.9
8
8
  Description-Content-Type: text/markdown
9
9
  Classifier: Topic :: Security
@@ -16,13 +16,16 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (G
16
16
  Requires-Dist: lxml~=5.0
17
17
  Requires-Dist: pyyaml~=6.0
18
18
  Requires-Dist: jinja2~=3.1
19
- Requires-Dist: attrs~=23.2
20
19
  Requires-Dist: pypubsub~=4.0
21
20
  Requires-Dist: termcolor~=2.4
22
21
  Requires-Dist: progressbar2~=4.3
23
22
  Requires-Dist: importlib_metadata~=7.0
24
23
  Requires-Dist: asn1crypto~=1.5
25
24
  Requires-Dist: zstandard~=0.22
25
+ Requires-Dist: aiohttp~=3.9
26
+ Requires-Dist: lief~=0.14
27
+ Requires-Dist: pyaxmlparser~=0.3
28
+ Requires-Dist: prompt-toolkit~=3.0
26
29
  Requires-Dist: mypy~=1.7 ; extra == "dev"
27
30
  Requires-Dist: pyproject-flake8~=6.1 ; extra == "dev"
28
31
  Requires-Dist: typing_extensions~=4.1 ; extra == "dev"
@@ -43,7 +46,7 @@ trueseeing is a fast, accurate and resillient vulnerability scanner for Android
43
46
  Currently we can:
44
47
 
45
48
  * Automatically scan app for vulnerabilities, reporting in HTML/JSON/text format (see below)
46
- * Manipulate app for easier analysis: e.g. enabling debug bit, enabling full backup, disabling TLS pinning, manipulating target API level, etc.
49
+ * Manipulate app for easier analysis: e.g. enabling debug bit, enabling full backup, disabling TLS pinning, manipulating target API level, injecting frida-gadget, etc.
47
50
  * Examine app for general information
48
51
  * Copy in/out app data through debug interface
49
52
  * Search for certain calls/consts/sput/iput
@@ -81,7 +84,7 @@ Alternatively, you can install our package with pip as follows. This form of ins
81
84
  You can interactively scan/analyze/patch/etc. apps -- making it the ideal choice for manual analysis:
82
85
 
83
86
  $ trueseeing target.apk
84
- [+] trueseeing 2.2.1
87
+ [+] trueseeing 2.2.4
85
88
  ts[target.apk]> ?
86
89
  ...
87
90
  ts[target.apk]> i # show generic information
@@ -225,7 +228,7 @@ The following class will provide a sample command as `t`, for example:
225
228
  ```python
226
229
  from typing import TYPE_CHECKING
227
230
  from trueseeing.api import Command
228
- from truseeing.core.ui import ui
231
+ from trueseeing.core.ui import ui
229
232
  if TYPE_CHECKING:
230
233
  from trueseeing.api import CommandMap, CommandPatternMap, ModifierMap, OptionMap, ConfigMap
231
234
 
@@ -314,7 +317,7 @@ class APKFileFormatHandler(FileFormatHandler):
314
317
  return APKFileFormatHandler()
315
318
 
316
319
  def get_formats(self) -> FormatMap:
317
- return {r'\.apk$':dict(e=self._handle, d='sample file format')}
320
+ return {'apk2':dict(e=self._handle, r=r'\.apk$', d='sample file format')}
318
321
 
319
322
  def get_configs(self) -> ConfigMap:
320
323
  return dict()
@@ -337,9 +340,15 @@ But by design it works only for known types (currently, the `apk`). So if you a
337
340
  context: MyAPKContext = self._helper.get_context().require_type('apk2') # type:ignore[assignment]
338
341
  ```
339
342
 
343
+ It is possible to define multiple formats matching the same pattern. We evaluate patterns in the order of from the most stringent (i.e. long) to the least. You use the `-F` switch to force some format to use with the target file, e.g.:
344
+
345
+ ```
346
+ $ trueseeing -F apk2 target.apk
347
+ ```
348
+
340
349
  #### Package requirements
341
350
 
342
- Extensions can be either: a) any package placed under `/ext` (container) or `~/.truseeing2/extensions` (pip), or b) any installed module named with the prefix of `trueseeing_ext0_`.
351
+ Extensions can be either: a) any package placed under `/ext` (container) or `~/.trueseeing2/extensions` (pip), or b) any installed module named with the prefix of `trueseeing_ext0_`.
343
352
 
344
353
  ### Origin of Project Name?
345
354
 
@@ -12,7 +12,7 @@ trueseeing is a fast, accurate and resillient vulnerability scanner for Android
12
12
  Currently we can:
13
13
 
14
14
  * Automatically scan app for vulnerabilities, reporting in HTML/JSON/text format (see below)
15
- * Manipulate app for easier analysis: e.g. enabling debug bit, enabling full backup, disabling TLS pinning, manipulating target API level, etc.
15
+ * Manipulate app for easier analysis: e.g. enabling debug bit, enabling full backup, disabling TLS pinning, manipulating target API level, injecting frida-gadget, etc.
16
16
  * Examine app for general information
17
17
  * Copy in/out app data through debug interface
18
18
  * Search for certain calls/consts/sput/iput
@@ -50,7 +50,7 @@ Alternatively, you can install our package with pip as follows. This form of ins
50
50
  You can interactively scan/analyze/patch/etc. apps -- making it the ideal choice for manual analysis:
51
51
 
52
52
  $ trueseeing target.apk
53
- [+] trueseeing 2.2.1
53
+ [+] trueseeing 2.2.4
54
54
  ts[target.apk]> ?
55
55
  ...
56
56
  ts[target.apk]> i # show generic information
@@ -194,7 +194,7 @@ The following class will provide a sample command as `t`, for example:
194
194
  ```python
195
195
  from typing import TYPE_CHECKING
196
196
  from trueseeing.api import Command
197
- from truseeing.core.ui import ui
197
+ from trueseeing.core.ui import ui
198
198
  if TYPE_CHECKING:
199
199
  from trueseeing.api import CommandMap, CommandPatternMap, ModifierMap, OptionMap, ConfigMap
200
200
 
@@ -283,7 +283,7 @@ class APKFileFormatHandler(FileFormatHandler):
283
283
  return APKFileFormatHandler()
284
284
 
285
285
  def get_formats(self) -> FormatMap:
286
- return {r'\.apk$':dict(e=self._handle, d='sample file format')}
286
+ return {'apk2':dict(e=self._handle, r=r'\.apk$', d='sample file format')}
287
287
 
288
288
  def get_configs(self) -> ConfigMap:
289
289
  return dict()
@@ -306,9 +306,15 @@ But by design it works only for known types (currently, the `apk`). So if you a
306
306
  context: MyAPKContext = self._helper.get_context().require_type('apk2') # type:ignore[assignment]
307
307
  ```
308
308
 
309
+ It is possible to define multiple formats matching the same pattern. We evaluate patterns in the order of from the most stringent (i.e. long) to the least. You use the `-F` switch to force some format to use with the target file, e.g.:
310
+
311
+ ```
312
+ $ trueseeing -F apk2 target.apk
313
+ ```
314
+
309
315
  #### Package requirements
310
316
 
311
- Extensions can be either: a) any package placed under `/ext` (container) or `~/.truseeing2/extensions` (pip), or b) any installed module named with the prefix of `trueseeing_ext0_`.
317
+ Extensions can be either: a) any package placed under `/ext` (container) or `~/.trueseeing2/extensions` (pip), or b) any installed module named with the prefix of `trueseeing_ext0_`.
312
318
 
313
319
  ### Origin of Project Name?
314
320
 
@@ -5,7 +5,7 @@ build-backend = "flit_core.buildapi"
5
5
  [project]
6
6
  name = 'trueseeing'
7
7
  authors = [
8
- {name='Takahiro Yoshimura', email='altakey@gmail.com'}
8
+ {name='Takahiro Yoshimura', email='alterakey@protonmail.com'}
9
9
  ]
10
10
  classifiers = [
11
11
  "Topic :: Security",
@@ -22,13 +22,16 @@ dependencies = [
22
22
  "lxml~=5.0",
23
23
  "pyyaml~=6.0",
24
24
  "jinja2~=3.1",
25
- "attrs~=23.2",
26
25
  "pypubsub~=4.0",
27
26
  "termcolor~=2.4",
28
27
  "progressbar2~=4.3",
29
28
  "importlib_metadata~=7.0",
30
29
  "asn1crypto~=1.5",
31
30
  "zstandard~=0.22",
31
+ "aiohttp~=3.9",
32
+ "lief~=0.14",
33
+ "pyaxmlparser~=0.3",
34
+ "prompt-toolkit~=3.0",
32
35
  ]
33
36
  requires-python = ">=3.9"
34
37
  dynamic = ['version', 'description']
@@ -57,6 +60,7 @@ module = [
57
60
  "jinja2",
58
61
  "pubsub",
59
62
  "asn1crypto.*",
63
+ "pyaxmlparser.*",
60
64
  ]
61
65
  ignore_missing_imports = true
62
66
 
@@ -1,2 +1,2 @@
1
1
  """Trueseeing is a non-decompiling Android application vulnerability scanner."""
2
- __version__ = '2.2.1'
2
+ __version__ = '2.2.4'
@@ -10,9 +10,12 @@ if TYPE_CHECKING:
10
10
  from trueseeing.core.android.context import APKContext
11
11
  from trueseeing.core.model.issue import Issue, IssueConfidence
12
12
 
13
+ ModifierEvent = Literal['begin', 'end']
14
+
13
15
  CommandEntrypoint = Callable[[deque[str]], Coroutine[Any, Any, None]]
14
16
  CommandlineEntrypoint = Callable[[str], Coroutine[Any, Any, None]]
15
17
  CommandPatternEntrypoints = Union[CommandEntrypoint, CommandlineEntrypoint]
18
+ ModifierListenerEntrypoint = Callable[[ModifierEvent, str], Coroutine[Any, Any, None]]
16
19
  SignatureEntrypoint = Callable[[], Coroutine[Any, Any, None]]
17
20
  FormatHandlerEntrypoint = Callable[[str], Optional[Context]]
18
21
  ConfigGetterEntrypoint = Callable[[], Any]
@@ -34,7 +37,7 @@ if TYPE_CHECKING:
34
37
  pass
35
38
 
36
39
  class ModifierEntry(Entry):
37
- pass
40
+ e: Optional[ModifierListenerEntrypoint] # type: ignore[misc]
38
41
 
39
42
  class ConfigEntry(TypedDict):
40
43
  g: ConfigGetterEntrypoint
@@ -48,6 +51,7 @@ if TYPE_CHECKING:
48
51
 
49
52
  class FormatEntry(TypedDict):
50
53
  e: FormatHandlerEntrypoint
54
+ r: str
51
55
  d: str
52
56
 
53
57
  CommandMap = Mapping[str, CommandEntry]
@@ -71,13 +75,13 @@ if TYPE_CHECKING:
71
75
  def get_context(self, typ: ContextType) -> Context: ...
72
76
  @overload
73
77
  @deprecated('use get_context().analyze(...)')
74
- async def get_context_analyzed(self, *, level: int = 3) -> Context: ...
78
+ async def get_context_analyzed(self, *, level: int = 4) -> Context: ...
75
79
  @overload
76
80
  @deprecated('use get_context().require_type(...).analyze(...)')
77
- async def get_context_analyzed(self, typ: Literal['apk'], *, level: int = 3) -> APKContext: ...
81
+ async def get_context_analyzed(self, typ: Literal['apk'], *, level: int = 4) -> APKContext: ...
78
82
  @overload
79
83
  @deprecated('use get_context().require_type(...).analyze(...)')
80
- async def get_context_analyzed(self, typ: ContextType, *, level: int = 3) -> Context: ...
84
+ async def get_context_analyzed(self, typ: ContextType, *, level: int = 4) -> Context: ...
81
85
  def decode_analysis_level(self, level: int) -> str: ...
82
86
  async def run(self, s: str) -> None: ...
83
87
  async def run_cmd(self, tokens: deque[str], line: Optional[str]) -> bool: ...
@@ -8,11 +8,10 @@ if TYPE_CHECKING:
8
8
  def discover() -> Iterator[Type[Command]]:
9
9
  from trueseeing.api import Command
10
10
  from importlib import import_module
11
- from trueseeing.core.model.cmd import CommandMixin
12
11
  from trueseeing.core.tools import get_public_subclasses, get_missing_methods, discover_modules_under
13
12
 
14
13
  for mod in discover_modules_under('trueseeing.app.cmd'):
15
14
  m = import_module(mod)
16
- for c in get_public_subclasses(m, Command, [CommandMixin]): # type:ignore[type-abstract]
15
+ for c in get_public_subclasses(m, Command, 'CommandMixin'): # type:ignore[type-abstract]
17
16
  assert not get_missing_methods(c)
18
17
  yield c
@@ -19,10 +19,12 @@ class AnalyzeCommand(CommandMixin):
19
19
 
20
20
  def get_commands(self) -> CommandMap:
21
21
  return {
22
- 'a':dict(e=self._analyze, n='a[a][!]', d='analyze target (aa: full analysis)'),
22
+ 'a':dict(e=self._analyze, n='a[a][a][!]', d='analyze target (aa: marginal, aaa: full)'),
23
23
  'a!':dict(e=self._analyze),
24
24
  'aa':dict(e=self._analyze2),
25
25
  'aa!':dict(e=self._analyze2),
26
+ 'aaa':dict(e=self._analyze3),
27
+ 'aaa!':dict(e=self._analyze3),
26
28
  }
27
29
 
28
30
  async def _analyze(self, args: deque[str], level: int = 2) -> None:
@@ -39,3 +41,6 @@ class AnalyzeCommand(CommandMixin):
39
41
 
40
42
  async def _analyze2(self, args: deque[str]) -> None:
41
43
  await self._analyze(args, level=3)
44
+
45
+ async def _analyze3(self, args: deque[str]) -> None:
46
+ await self._analyze(args, level=4)
@@ -47,16 +47,24 @@ class AssembleCommand(CommandMixin):
47
47
  ui.fatal('need root path')
48
48
 
49
49
  import os
50
+ import re
50
51
  import time
51
52
  from tempfile import TemporaryDirectory
52
53
  from trueseeing.core.android.asm import APKAssembler
53
54
  from trueseeing.core.android.tools import move_apk
54
55
 
55
56
  root = args.popleft()
56
- origapk = apk.replace('.apk', '.apk.orig')
57
+ origapk = re.sub(r'(\.x?apk)$', r'\1.orig', apk)
57
58
 
58
- if os.path.exists(origapk) and not cmd.endswith('!'):
59
- ui.fatal('backup file exists; force (!) to overwrite')
59
+ stem = re.sub(r'\.x?apk$', '', apk)
60
+ for typ in ['apk', 'xapk']:
61
+ print(origapk, f'{stem}.{typ}.orig')
62
+ if os.path.exists(f'{stem}.{typ}.orig') and not cmd.endswith('!'):
63
+ ui.fatal('backup file exists; force (!) to overwrite')
64
+
65
+ if apk.endswith('.xapk'):
66
+ ui.warn('assembling xapk is not supported; assembling as merged apk')
67
+ apk = apk.replace('.xapk', '.apk')
60
68
 
61
69
  opts = self._helper.get_effective_options(self._helper.get_modifiers(args))
62
70
 
@@ -140,7 +148,7 @@ class AssembleCommand(CommandMixin):
140
148
  at = time.time()
141
149
 
142
150
  with TemporaryDirectory() as td:
143
- await APKDisassembler.disassemble_to_path(apk, td, nodex=nodex)
151
+ await APKDisassembler.disassemble_to_path(apk, td, nodex=nodex, merge=apk.endswith('.xapk'))
144
152
 
145
153
  if not archive:
146
154
  with FileTransferProgressReporter('disassemble: writing').scoped() as progress:
@@ -185,7 +193,7 @@ class AssembleCommand(CommandMixin):
185
193
 
186
194
  at = time.time()
187
195
  extracted = 0
188
- context = self._helper.get_context().require_type('apk')
196
+ context = self._helper.get_context()
189
197
  q = context.store().query()
190
198
 
191
199
  if not archive: