trueseeing 2.2.7__tar.gz → 2.2.8__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 (107) hide show
  1. trueseeing-2.2.8/.dockerignore +4 -0
  2. trueseeing-2.2.8/.github/workflows/lint.yaml +27 -0
  3. trueseeing-2.2.8/Dockerfile +24 -0
  4. {trueseeing-2.2.7 → trueseeing-2.2.8}/PKG-INFO +30 -18
  5. {trueseeing-2.2.7 → trueseeing-2.2.8}/README.md +18 -2
  6. {trueseeing-2.2.7 → trueseeing-2.2.8}/pyproject.toml +20 -18
  7. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/__init__.py +1 -1
  8. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/android/engage.py +40 -17
  9. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/android/recon.py +11 -11
  10. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/ios/engage.py +24 -12
  11. trueseeing-2.2.8/trueseeing/app/cmd/ios/recon.py +53 -0
  12. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/inspect.py +1 -1
  13. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/android/asm.py +55 -1
  14. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/android/device.py +6 -6
  15. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/env.py +6 -2
  16. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/ios/device.py +21 -21
  17. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/nat.py +1 -1
  18. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/tools.py +2 -2
  19. trueseeing-2.2.8/uv.lock +1927 -0
  20. trueseeing-2.2.7/.dockerignore +0 -3
  21. trueseeing-2.2.7/.github/workflows/lint.yaml +0 -25
  22. trueseeing-2.2.7/Dockerfile +0 -25
  23. {trueseeing-2.2.7 → trueseeing-2.2.8}/.github/FUNDING.yml +0 -0
  24. {trueseeing-2.2.7 → trueseeing-2.2.8}/.github/workflows/deploy.yaml +0 -0
  25. {trueseeing-2.2.7 → trueseeing-2.2.8}/.github/workflows/publish.yaml +0 -0
  26. {trueseeing-2.2.7 → trueseeing-2.2.8}/.github/workflows/stale.yaml +0 -0
  27. {trueseeing-2.2.7 → trueseeing-2.2.8}/.gitignore +0 -0
  28. {trueseeing-2.2.7 → trueseeing-2.2.8}/COPYING +0 -0
  29. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/api.py +0 -0
  30. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/__init__.py +0 -0
  31. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/__init__.py +0 -0
  32. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/alias.py +0 -0
  33. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/analyze.py +0 -0
  34. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/android/__init__.py +0 -0
  35. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/android/asm.py +0 -0
  36. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/android/search.py +0 -0
  37. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/android/show.py +0 -0
  38. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/config.py +0 -0
  39. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/info.py +0 -0
  40. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/ios/__init__.py +0 -0
  41. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/report.py +0 -0
  42. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/scan.py +0 -0
  43. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/search.py +0 -0
  44. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/cmd/show.py +0 -0
  45. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/scan.py +0 -0
  46. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/app/shell.py +0 -0
  47. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/__init__.py +0 -0
  48. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/analyze.py +0 -0
  49. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/android/analyze/__init__.py +0 -0
  50. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/android/analyze/flow.py +0 -0
  51. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/android/analyze/nat.py +0 -0
  52. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/android/analyze/op.py +0 -0
  53. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/android/context.py +0 -0
  54. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/android/db.py +0 -0
  55. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/android/model.py +0 -0
  56. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/android/store.py +0 -0
  57. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/android/tools.py +0 -0
  58. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/config.py +0 -0
  59. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/context.py +0 -0
  60. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/cvss.py +0 -0
  61. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/db.py +0 -0
  62. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/exc.py +0 -0
  63. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/ext.py +0 -0
  64. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/ios/__init__.py +0 -0
  65. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/ios/analyze.py +0 -0
  66. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/ios/context.py +0 -0
  67. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/ios/db.py +0 -0
  68. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/ios/model.py +0 -0
  69. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/ios/store.py +0 -0
  70. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/ios/swift.py +0 -0
  71. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/model/__init__.py +0 -0
  72. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/model/cmd.py +0 -0
  73. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/model/issue.py +0 -0
  74. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/model/sig.py +0 -0
  75. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/report.py +0 -0
  76. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/scan.py +0 -0
  77. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/store.py +0 -0
  78. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/ui.py +0 -0
  79. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/core/z.py +0 -0
  80. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/LICENSE.md +0 -0
  81. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/android/abe.jar +0 -0
  82. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/android/apkeditor.jar +0 -0
  83. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/android/apksigner.jar +0 -0
  84. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/android/frida-app.smali +0 -0
  85. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/android/frida-scriptdir.config +0 -0
  86. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/android/nsc.xml +0 -0
  87. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/android/store.0.sql +0 -0
  88. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/android/store.1.sql +0 -0
  89. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/files.0.sql +0 -0
  90. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/ios/store.0.sql +0 -0
  91. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/ios/store.1.sql +0 -0
  92. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/public_suffix_list.dat +0 -0
  93. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/store.0.sql +0 -0
  94. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/store.s.sql +0 -0
  95. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/template/report.html +0 -0
  96. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/libs/tlds.txt +0 -0
  97. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/py.typed +0 -0
  98. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/sig/__init__.py +0 -0
  99. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/sig/android/__init__.py +0 -0
  100. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/sig/android/crypto.py +0 -0
  101. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/sig/android/fingerprint.py +0 -0
  102. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/sig/android/manifest.py +0 -0
  103. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/sig/android/nat.py +0 -0
  104. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/sig/android/privacy.py +0 -0
  105. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/sig/android/security.py +0 -0
  106. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/sig/ios/__init__.py +0 -0
  107. {trueseeing-2.2.7 → trueseeing-2.2.8}/trueseeing/sig/ios/base.py +0 -0
@@ -0,0 +1,4 @@
1
+ .git*
2
+ .venv
3
+ dist
4
+ .mypy_cache
@@ -0,0 +1,27 @@
1
+ name: lint
2
+
3
+ on: push
4
+
5
+ jobs:
6
+ lint:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - name: Check out code
10
+ uses: actions/checkout@v3
11
+
12
+ - name: Install uv
13
+ uses: astral-sh/setup-uv@v6
14
+ with:
15
+ version: "0.8.22"
16
+ enable-cache: false
17
+
18
+ - name: Install Python
19
+ run: uv python install
20
+
21
+ - name: Install dependencies
22
+ run: uv sync --locked --dev
23
+
24
+ - name: Analysing the code
25
+ run: |
26
+ uv run mypy trueseeing
27
+ uv run pflake8 --color=never trueseeing
@@ -0,0 +1,24 @@
1
+ from python:3.13-slim-bookworm
2
+ run apt-get update -y
3
+ run apt-get install -y --no-install-recommends git
4
+ run pip install --no-cache-dir uv
5
+ copy . /usr/lib/ts2
6
+ run bash -c "cd /usr/lib/ts2 && uv sync --active --locked --no-dev"
7
+ run (cd /usr/lib && git clone https://github.com/alterakey/ts2-frida-ios-dump.git frida-ios-dump && cd frida-ios-dump && uv venv && uv pip install -r ./requirements.txt)
8
+
9
+ from python:3.13-slim-bookworm
10
+ run apt-get update -y
11
+ run apt-get install -y --no-install-recommends openjdk-17-jre-headless zip adb && rm -rf /var/lib/apt/lists/*
12
+ run install -d -m 777 /data /ext /cache /out && ln -sfn /cache /root/.local
13
+ copy --from=0 /usr/lib/frida-ios-dump /usr/lib/frida-ios-dump
14
+ copy --from=0 /usr/lib/ts2 /usr/lib/ts2
15
+ env PATH=/usr/lib/ts2/.venv/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
16
+ env TS2_IN_DOCKER=1
17
+ env TS2_CACHEDIR=/cache
18
+ env TS2_HOME=/data
19
+ env TS2_EXTDIR=/ext
20
+ env TS2_FRIDA_IOS_DUMP_PATH=/usr/lib/frida-ios-dump/dump.py
21
+ env TS2_FRIDA_IOS_DUMP_INTERP=/usr/lib/frida-ios-dump/.venv/bin/python3
22
+ env TS2_SWIFT_DEMANGLER_URL=http://ts2-swift-demangle
23
+ workdir /out
24
+ entrypoint ["trueseeing"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trueseeing
3
- Version: 2.2.7
3
+ Version: 2.2.8
4
4
  Summary: Trueseeing is a non-decompiling iOS/Android application vulnerability scanner.
5
5
  Keywords: ios,android,security,pentest,hacking
6
6
  Author-email: Takahiro Yoshimura <alterakey@protonmail.com>
@@ -14,25 +14,21 @@ Classifier: Operating System :: MacOS :: MacOS X
14
14
  Classifier: Operating System :: Android
15
15
  Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
16
16
  License-File: COPYING
17
- Requires-Dist: lxml~=5.0
18
- Requires-Dist: pyyaml~=6.0
19
- Requires-Dist: jinja2~=3.1
20
- Requires-Dist: pypubsub~=4.0
21
- Requires-Dist: termcolor~=2.4
22
- Requires-Dist: progressbar2~=4.3
23
- Requires-Dist: importlib_metadata~=7.0
24
- Requires-Dist: asn1crypto~=1.5
25
- Requires-Dist: pyzstd~=0.16
26
17
  Requires-Dist: aiohttp~=3.9
18
+ Requires-Dist: asn1crypto~=1.5
19
+ Requires-Dist: frida-tools~=13.6
20
+ Requires-Dist: importlib-metadata~=7.0
21
+ Requires-Dist: jinja2~=3.1
27
22
  Requires-Dist: lief~=0.14
28
- Requires-Dist: pyaxmlparser~=0.3
23
+ Requires-Dist: lxml~=5.0
24
+ Requires-Dist: progressbar2~=4.3
29
25
  Requires-Dist: prompt-toolkit~=3.0
30
- Requires-Dist: frida-tools~=13.6
31
- Requires-Dist: mypy~=1.13 ; extra == "dev"
32
- Requires-Dist: pyproject-flake8~=7.0 ; extra == "dev"
33
- Requires-Dist: typing_extensions~=4.12 ; extra == "dev"
26
+ Requires-Dist: pyaxmlparser~=0.3
27
+ Requires-Dist: pypubsub~=4.0
28
+ Requires-Dist: pyyaml~=6.0
29
+ Requires-Dist: pyzstd~=0.16
30
+ Requires-Dist: termcolor~=2.4
34
31
  Project-URL: Source, https://github.com/alterakey/trueseeing
35
- Provides-Extra: dev
36
32
 
37
33
  # README
38
34
 
@@ -77,13 +73,18 @@ If you want to run statelessly you omit mounting volume onto /cache (not recomme
77
73
  $ docker run --rm -v $(pwd):/out ghcr.io/alterakey/trueseeing
78
74
 
79
75
 
80
- ### With pip
76
+ ### With pip / uvx
81
77
 
82
78
  Alternatively, you can install our package with pip as follows. This form of installation might be useful for extensions, as it grants them the greatest freedom. Just remember you need a JRE and Android SDK (optionally; to mess with devices):
83
79
 
84
80
  $ pip install --user trueseeing
85
81
  $ trueseeing
86
82
 
83
+ Or, with [uv](https://github.com/astral-sh/uv) you could do:
84
+
85
+ $ uvx trueseeing
86
+
87
+
87
88
  ## Usage
88
89
 
89
90
  ### Interactive mode
@@ -91,7 +92,7 @@ Alternatively, you can install our package with pip as follows. This form of ins
91
92
  You can interactively scan/analyze/patch/etc. apps -- making it the ideal choice for manual analysis:
92
93
 
93
94
  $ trueseeing target.apk
94
- [+] trueseeing 2.2.7
95
+ [+] trueseeing 2.2.8
95
96
  ts[target.apk]> ?
96
97
  ...
97
98
  ts[target.apk]> i # show generic information
@@ -179,6 +180,17 @@ To hack it, you need to create a proper build environment. To create one, set up
179
180
  (.venv) $ flit build # to build (wheel)
180
181
  (.venv) $ docker build -t trueseeing . # to build (container)
181
182
 
183
+ Or, with [uv](https://github.com/astral-sh/uv) you could do:
184
+
185
+ $ git clone https://github.com/alterakey/trueseeing.git wc
186
+ $ uv sync --locked
187
+ $ (... hack ...)
188
+ $ uv run trueseeing ... # to run
189
+ $ uv run mypy trueseeing && uv run pflake8 trueseeing # to validate
190
+ Success: no issues found in XX source files
191
+ $ uv run flit build # to build (wheel)
192
+ $ docker build -t trueseeing . # to build (container)
193
+
182
194
 
183
195
  ## Details
184
196
 
@@ -41,13 +41,18 @@ If you want to run statelessly you omit mounting volume onto /cache (not recomme
41
41
  $ docker run --rm -v $(pwd):/out ghcr.io/alterakey/trueseeing
42
42
 
43
43
 
44
- ### With pip
44
+ ### With pip / uvx
45
45
 
46
46
  Alternatively, you can install our package with pip as follows. This form of installation might be useful for extensions, as it grants them the greatest freedom. Just remember you need a JRE and Android SDK (optionally; to mess with devices):
47
47
 
48
48
  $ pip install --user trueseeing
49
49
  $ trueseeing
50
50
 
51
+ Or, with [uv](https://github.com/astral-sh/uv) you could do:
52
+
53
+ $ uvx trueseeing
54
+
55
+
51
56
  ## Usage
52
57
 
53
58
  ### Interactive mode
@@ -55,7 +60,7 @@ Alternatively, you can install our package with pip as follows. This form of ins
55
60
  You can interactively scan/analyze/patch/etc. apps -- making it the ideal choice for manual analysis:
56
61
 
57
62
  $ trueseeing target.apk
58
- [+] trueseeing 2.2.7
63
+ [+] trueseeing 2.2.8
59
64
  ts[target.apk]> ?
60
65
  ...
61
66
  ts[target.apk]> i # show generic information
@@ -143,6 +148,17 @@ To hack it, you need to create a proper build environment. To create one, set up
143
148
  (.venv) $ flit build # to build (wheel)
144
149
  (.venv) $ docker build -t trueseeing . # to build (container)
145
150
 
151
+ Or, with [uv](https://github.com/astral-sh/uv) you could do:
152
+
153
+ $ git clone https://github.com/alterakey/trueseeing.git wc
154
+ $ uv sync --locked
155
+ $ (... hack ...)
156
+ $ uv run trueseeing ... # to run
157
+ $ uv run mypy trueseeing && uv run pflake8 trueseeing # to validate
158
+ Success: no issues found in XX source files
159
+ $ uv run flit build # to build (wheel)
160
+ $ docker build -t trueseeing . # to build (container)
161
+
146
162
 
147
163
  ## Details
148
164
 
@@ -19,31 +19,24 @@ classifiers = [
19
19
  readme = "README.md"
20
20
  keywords = ['ios', 'android', 'security', 'pentest', 'hacking']
21
21
  dependencies = [
22
- "lxml~=5.0",
23
- "pyyaml~=6.0",
24
- "jinja2~=3.1",
25
- "pypubsub~=4.0",
26
- "termcolor~=2.4",
27
- "progressbar2~=4.3",
28
- "importlib_metadata~=7.0",
29
- "asn1crypto~=1.5",
30
- "pyzstd~=0.16",
31
22
  "aiohttp~=3.9",
23
+ "asn1crypto~=1.5",
24
+ "frida-tools~=13.6",
25
+ "importlib-metadata~=7.0",
26
+ "jinja2~=3.1",
32
27
  "lief~=0.14",
33
- "pyaxmlparser~=0.3",
28
+ "lxml~=5.0",
29
+ "progressbar2~=4.3",
34
30
  "prompt-toolkit~=3.0",
35
- 'frida-tools~=13.6',
31
+ "pyaxmlparser~=0.3",
32
+ "pypubsub~=4.0",
33
+ "pyyaml~=6.0",
34
+ "pyzstd~=0.16",
35
+ "termcolor~=2.4",
36
36
  ]
37
37
  requires-python = ">=3.9"
38
38
  dynamic = ['version', 'description']
39
39
 
40
- [project.optional-dependencies]
41
- dev = [
42
- "mypy~=1.13",
43
- "pyproject-flake8~=7.0",
44
- "typing_extensions~=4.12",
45
- ]
46
-
47
40
  [project.urls]
48
41
  Source = "https://github.com/alterakey/trueseeing"
49
42
 
@@ -69,3 +62,12 @@ ignore_missing_imports = true
69
62
  [tool.flake8]
70
63
  extend-ignore = "E301,E302,E265,E114,E501,E231,E252,E261,E701,E722,E741"
71
64
  indent-size = 2
65
+
66
+ [dependency-groups]
67
+ dev = [
68
+ "flit>=3.12.0",
69
+ "mypy~=1.13",
70
+ "pyproject-flake8~=7.0",
71
+ "types-pygments>=2.19.0.20250809",
72
+ "typing_extensions~=4.12",
73
+ ]
@@ -1,2 +1,2 @@
1
1
  """Trueseeing is a non-decompiling iOS/Android application vulnerability scanner."""
2
- __version__ = '2.2.7'
2
+ __version__ = '2.2.8'
@@ -219,7 +219,7 @@ class EngageCommand(CommandMixin):
219
219
  ui.info('enabling full backup {apk}'.format(apk=apk))
220
220
 
221
221
  at = time.time()
222
- context = await self._helper.get_context().require_type('apk').analyze(level=1)
222
+ context = await self._helper.get_context().require_type('apk').analyze(level=2)
223
223
  with context.store().query().scoped() as q:
224
224
  path = 'AndroidManifest.xml'
225
225
  blob = q.file_get(path, patched=True)
@@ -868,6 +868,8 @@ class EngageCommand(CommandMixin):
868
868
  context: APKContext = self._helper.get_context().require_type('apk')
869
869
  apk = context.target
870
870
 
871
+ import os.path
872
+ from tempfile import TemporaryDirectory
871
873
  from time import time
872
874
  from shlex import quote
873
875
  from pubsub import pub
@@ -888,25 +890,46 @@ class EngageCommand(CommandMixin):
888
890
  if b'success' in l.lower():
889
891
  ui.warn('removing existing package')
890
892
  except CalledProcessError as e:
891
- ui.fatal('uninstall failed: {}'.format(e.stdout.decode().rstrip()))
893
+ ui.warn('uninstalling appears to have failed (continuing anyway): {}'.format(e.stdout.decode().rstrip()))
892
894
 
893
- with AndroidInstallProgressReporter().scoped():
894
- pub.sendMessage('progress.android.adb.begin', what='installing ... ')
895
- try:
896
- async for l in dev.invoke_adb_streaming(f'install --no-streaming {quote(apk)}', redir_stderr=True):
897
- pub.sendMessage('progress.android.adb.update')
898
- if b'failure' in l.lower():
899
- ui.stderr('')
900
- if not cmd.endswith('!'):
901
- ui.fatal('install failed; force (!) to replace ({})'.format(l.decode('UTF-8')))
902
- else:
903
- ui.fatal('install failed ({})'.format(l.decode('UTF-8')))
895
+ async def _do(apk: str, wd: str, *, in_retry: bool = False) -> None:
896
+ with AndroidInstallProgressReporter().scoped():
897
+ pub.sendMessage('progress.android.adb.begin', what='installing ... ')
904
898
 
905
- pub.sendMessage('progress.android.adb.done')
906
- except CalledProcessError as e:
907
- ui.fatal('install failed: {}'.format(e.stdout.decode().rstrip()))
899
+ if not apk.endswith('.xapk'):
900
+ cmd = f'install --no-streaming {quote(apk)}'
901
+ else:
902
+ from trueseeing.core.android.asm import APKDisassembler
903
+ slices = [s async for s in APKDisassembler.get_slices(apk, os.path.join(wd, 'slices'))]
904
+ cmd = ' '.join(['install-multiple', '--no-streaming'] + slices)
905
+
906
+ try:
907
+ async for l in dev.invoke_adb_streaming(cmd, redir_stderr=True):
908
+ pub.sendMessage('progress.android.adb.update')
909
+ msg = l.lower()
910
+ if b'failure' in msg:
911
+ ui.stderr('')
912
+
913
+ if b'signature' in msg and not in_retry: # XXX
914
+ ui.warn('package signature seems to be broken: re-signing')
915
+ with TemporaryDirectory(dir=wd) as td:
916
+ from trueseeing.core.android.asm import APKSigner
917
+ apk, _ = await APKSigner().sign(apk, td)
918
+ return await _do(apk, td, in_retry=True)
919
+
920
+ if not cmd.endswith('!'):
921
+ ui.fatal('install failed; force (!) to replace ({})'.format(l.decode('UTF-8')))
922
+ else:
923
+ ui.fatal('install failed ({})'.format(l.decode('UTF-8')))
924
+
925
+ pub.sendMessage('progress.android.adb.done')
926
+ except CalledProcessError as e:
927
+ ui.fatal('install failed: {}'.format(e.stdout.decode().rstrip()))
928
+
929
+ with TemporaryDirectory() as td:
930
+ await _do(apk, td)
908
931
 
909
- ui.success('done ({t:.02f} sec){trailer}'.format(t=time() - at, trailer=' '*8))
932
+ ui.success('done ({t:.02f} sec){trailer}'.format(t=time() - at, trailer=' '*8))
910
933
 
911
934
  async def _engage_undeploy_package(self, args: deque[str]) -> None:
912
935
  _ = args.popleft()
@@ -39,17 +39,17 @@ class ReconCommand(CommandMixin):
39
39
 
40
40
  def get_commands(self) -> CommandMap:
41
41
  return {
42
- '!!':dict(e=self._recon_shell, n='!!', d='run shell on device'),
43
- 'rwl':dict(e=self._recon_watch_logcat, n='rwl[!] [pat]', d='recon: watch logcat (!: system-wide)'),
44
- 'rwl!':dict(e=self._recon_watch_logcat),
45
- 'rwf':dict(e=self._recon_watch_fs, n='rwf', d='recon: watch filesystem'),
46
- 'rwt':dict(e=self._recon_watch_intent, n='rwt[!] [pat]', d='recon: watch intent'),
47
- 'rwt!':dict(e=self._recon_watch_intent),
48
- 'rwu':dict(e=self._recon_watch_ui, n='rwu[!] [pat|xp:xpath] [output.xml]', d='recon: watch device UI'),
49
- 'rwu!':dict(e=self._recon_watch_ui),
50
- 'rwx':dict(e=self._recon_watch_start, n='rwx', d='recon: start watching'),
51
- 'rp':dict(e=self._recon_list_packages, n='rp', d='recon: list installed packages'),
52
- 'ru':dict(e=self._recon_dump_ui, n='ru [output.xml]', d='recon: dump device UI'),
42
+ '!!':dict(e=self._recon_shell, n='!!', d='run shell on device', t={'apk'}),
43
+ 'rwl':dict(e=self._recon_watch_logcat, n='rwl[!] [pat]', d='recon: watch logcat (!: system-wide)', t={'apk'}),
44
+ 'rwl!':dict(e=self._recon_watch_logcat, t={'apk'}),
45
+ 'rwf':dict(e=self._recon_watch_fs, n='rwf', d='recon: watch filesystem', t={'apk'}),
46
+ 'rwt':dict(e=self._recon_watch_intent, n='rwt[!] [pat]', d='recon: watch intent', t={'apk'}),
47
+ 'rwt!':dict(e=self._recon_watch_intent, t={'apk'}),
48
+ 'rwu':dict(e=self._recon_watch_ui, n='rwu[!] [pat|xp:xpath] [output.xml]', d='recon: watch device UI', t={'apk'}),
49
+ 'rwu!':dict(e=self._recon_watch_ui, t={'apk'}),
50
+ 'rwx':dict(e=self._recon_watch_start, n='rwx', d='recon: start watching', t={'apk'}),
51
+ 'rp':dict(e=self._recon_list_packages, n='rp', d='recon: list installed packages', t={'apk'}),
52
+ 'ru':dict(e=self._recon_dump_ui, n='ru [output.xml]', d='recon: dump device UI', t={'apk'}),
53
53
  }
54
54
 
55
55
  def _get_apk_context(self) -> APKContext:
@@ -8,7 +8,7 @@ from trueseeing.core.model.cmd import CommandMixin
8
8
  from trueseeing.core.ui import ui
9
9
 
10
10
  if TYPE_CHECKING:
11
- from typing import Mapping, List
11
+ from typing import Mapping, List, Tuple
12
12
  from trueseeing.api import CommandHelper, Command, CommandMap, OptionMap
13
13
  from trueseeing.core.ios.context import IPAContext
14
14
  from trueseeing.core.ios.device import IOSDevice
@@ -80,7 +80,7 @@ class EngageCommand(CommandMixin):
80
80
 
81
81
  if force:
82
82
  ui.warn(f"killing {name}")
83
- await dev.invoke_frida_passthru(f"frida-kill -U {name}")
83
+ await dev.invoke_frida_passthru(f"frida-kill @dev@ {name}")
84
84
 
85
85
  ui.info(f"starting frida on {name}")
86
86
 
@@ -130,7 +130,7 @@ class EngageCommand(CommandMixin):
130
130
 
131
131
  if force:
132
132
  ui.warn(f"killing {name}")
133
- await dev.invoke_frida_passthru(f"frida-kill -U {name}")
133
+ await dev.invoke_frida_passthru(f"frida-kill @dev@ {name}")
134
134
 
135
135
  ui.info(f"starting frida on {name}")
136
136
 
@@ -173,20 +173,25 @@ class EngageCommand(CommandMixin):
173
173
 
174
174
  with TemporaryDirectory() as td:
175
175
  from os import chdir, getcwd
176
- from trueseeing.core.env import get_frida_ios_dump_interp, get_frida_ios_dump_path
176
+ from trueseeing.core.env import get_frida_ios_dump_interp, get_frida_ios_dump_path, get_ios_frida_server_host, get_frida_ios_dump_ssh_host
177
177
  from shlex import quote
178
178
  cd = getcwd()
179
+ hoststr = get_frida_ios_dump_ssh_host()
180
+ ssh_host, ssh_port = self._parse_host(hoststr, 2222)
179
181
  try:
180
182
  chdir(td)
181
183
  from subprocess import CalledProcessError
182
184
  try:
183
- await dev.invoke_frida_passthru("{interp} {dump} -o t.ipa {name}".format(
185
+ await dev.invoke_frida_passthru("env FRIDA_HOST={host} {interp} {dump} -H {ssh_host} -p {ssh_port} -o t.ipa {name}".format(
186
+ host=get_ios_frida_server_host(),
184
187
  interp=get_frida_ios_dump_interp(),
185
188
  dump=get_frida_ios_dump_path(),
189
+ ssh_host=ssh_host,
190
+ ssh_port=ssh_port,
186
191
  name=quote(pkg),
187
192
  ))
188
193
  except CalledProcessError:
189
- ui.fatal('cannot attach to process')
194
+ ui.fatal(f'grab failed (try tunnelling {ssh_host}:{ssh_port} to device port 22 or 44)')
190
195
 
191
196
  from shutil import copy2
192
197
  copy2('t.ipa', outfn)
@@ -195,6 +200,13 @@ class EngageCommand(CommandMixin):
195
200
  finally:
196
201
  chdir(cd)
197
202
 
203
+ def _parse_host(self, hoststr: str, default_port: int) -> Tuple[str, int]:
204
+ c = hoststr.split(':', maxsplit=1)
205
+ if len(c) > 1:
206
+ return c[0], int(c[1])
207
+ else:
208
+ return c[0], default_port
209
+
198
210
  def _get_context(self) -> IPAContext:
199
211
  return self._helper.get_context().require_type('ipa') # type:ignore[return-value]
200
212
 
@@ -217,7 +229,7 @@ class FridaAttacher:
217
229
  from asyncio import TimeoutError
218
230
  ui.info('attaching to the foreground process')
219
231
  try:
220
- await self._dev.invoke_frida_passthru("frida -UF {args}".format(
232
+ await self._dev.invoke_frida_passthru("frida @dev@ -F {args}".format(
221
233
  args=self._format_args(),
222
234
  ))
223
235
  except (TimeoutError, CalledProcessError):
@@ -236,7 +248,7 @@ class FridaAttacher:
236
248
  from subprocess import CalledProcessError
237
249
  from asyncio import TimeoutError
238
250
  try:
239
- await self._dev.invoke_frida_passthru("frida -Uf {name} {args}".format(
251
+ await self._dev.invoke_frida_passthru("frida @dev@ -f {name} {args}".format(
240
252
  name=name,
241
253
  args=self._format_args(),
242
254
  ), timeout=3.)
@@ -248,7 +260,7 @@ class FridaAttacher:
248
260
  from asyncio import TimeoutError
249
261
  ui.info('waiting for the process; launch the app on the device in 60s')
250
262
  try:
251
- await self._dev.invoke_frida_passthru("frida -UW {name} {args}".format(
263
+ await self._dev.invoke_frida_passthru("frida @dev@ -W {name} {args}".format(
252
264
  name=name,
253
265
  args=self._format_args(),
254
266
  ), timeout=60.)
@@ -282,7 +294,7 @@ class FridaTracer:
282
294
  from asyncio import TimeoutError
283
295
  ui.info('attaching to the foreground process')
284
296
  try:
285
- await self._dev.invoke_frida_passthru("frida-trace -UF {args}".format(
297
+ await self._dev.invoke_frida_passthru("frida-trace @dev@ -F {args}".format(
286
298
  args=self._format_args(),
287
299
  ))
288
300
  except (TimeoutError, CalledProcessError):
@@ -301,7 +313,7 @@ class FridaTracer:
301
313
  from subprocess import CalledProcessError
302
314
  from asyncio import TimeoutError
303
315
  try:
304
- await self._dev.invoke_frida_passthru("frida-trace -Uf {name} {args}".format(
316
+ await self._dev.invoke_frida_passthru("frida-trace @dev@ -f {name} {args}".format(
305
317
  name=name,
306
318
  args=self._format_args(),
307
319
  ))
@@ -313,7 +325,7 @@ class FridaTracer:
313
325
  from asyncio import TimeoutError
314
326
  ui.info('waiting for the process; launch the app on the device in 60s')
315
327
  try:
316
- await self._dev.invoke_frida_passthru("frida-trace -UW {name} {args}".format(
328
+ await self._dev.invoke_frida_passthru("frida-trace @dev@ -W {name} {args}".format(
317
329
  name=name,
318
330
  args=self._format_args(),
319
331
  ))
@@ -0,0 +1,53 @@
1
+ from __future__ import annotations
2
+ from typing import TYPE_CHECKING
3
+
4
+ from collections import deque
5
+
6
+ from trueseeing.core.model.cmd import CommandMixin
7
+ from trueseeing.core.ui import ui
8
+
9
+ if TYPE_CHECKING:
10
+ from typing import Literal
11
+ from trueseeing.api import CommandHelper, Command, CommandMap
12
+
13
+ UIPatternType = Literal['re', 'xpath']
14
+
15
+ class ReconCommand(CommandMixin):
16
+ def __init__(self, helper: CommandHelper) -> None:
17
+ self._helper = helper
18
+
19
+ @staticmethod
20
+ def create(helper: CommandHelper) -> Command:
21
+ return ReconCommand(helper)
22
+
23
+ def get_commands(self) -> CommandMap:
24
+ return {
25
+ 'rp':dict(e=self._recon_list_packages, n='rp', d='recon: list installed packages', t={'ipa'}),
26
+ }
27
+
28
+ async def _recon_list_packages(self, args: deque[str]) -> None:
29
+ _ = args.popleft()
30
+
31
+ ui.info('listing packages')
32
+
33
+ import time
34
+ import re
35
+ import io
36
+ from trueseeing.core.ios.device import IOSDevice
37
+
38
+ dev = IOSDevice()
39
+
40
+ at = time.time()
41
+ nr = 0
42
+ for l in io.StringIO(await dev.invoke_frida('frida-ps @dev@ -ai')):
43
+ if l.startswith('----'):
44
+ continue
45
+ m = re.match(r'^\s*([0-9-]+)\s+(.*?)\s+([a-z0-9._-]+)\s*$', l)
46
+ if not m:
47
+ continue
48
+ if m.group(1) == '-':
49
+ ui.info('{bid} ({name})'.format(bid=m.group(3), name=m.group(2)))
50
+ else:
51
+ ui.info('{bid} ({name}) [{pid}]'.format(bid=m.group(3), name=m.group(2), pid=m.group(1)))
52
+ nr += 1
53
+ ui.success('done, {nr} packages found ({t:.02f} sec.)'.format(nr=nr, t=(time.time() - at)))
@@ -71,7 +71,7 @@ class InspectMode:
71
71
  sys.ps1, sys.ps2 = ps1, ps2
72
72
 
73
73
  class LambdaConsole(InteractiveConsole):
74
- def __init__(self, /, runner: Runner, locals: Optional[Mapping[str, Any]] = None) -> None:
74
+ def __init__(self, /, runner: Runner, locals: Optional[Dict[str, Any]] = None) -> None:
75
75
  super().__init__(locals=locals, filename='<input>')
76
76
  from prompt_toolkit import PromptSession
77
77
  self._sess: Any = PromptSession()
@@ -10,7 +10,7 @@ from trueseeing.core.env import get_home_dir
10
10
  from trueseeing.core.ui import ui
11
11
 
12
12
  if TYPE_CHECKING:
13
- from typing import Tuple, Optional
13
+ from typing import Tuple, Optional, AsyncIterator
14
14
  from trueseeing.core.context import Context
15
15
  from trueseeing.core.db import FileEntry
16
16
 
@@ -119,6 +119,32 @@ class APKDisassembler:
119
119
 
120
120
  pub.sendMessage('progress.core.asm.disasm.done')
121
121
 
122
+ @classmethod
123
+ async def get_slices(cls, xapk: str, wd: str) -> AsyncIterator[str]:
124
+ from shlex import quote
125
+ from trueseeing.core.tools import invoke_streaming
126
+ from trueseeing.core.android.tools import toolchains
127
+
128
+ td = wd
129
+
130
+ pub.sendMessage('progress.core.asm.disasm.begin')
131
+ with toolchains() as tc:
132
+ async for l in invoke_streaming(
133
+ '(java -jar {apkeditor} d -o {path} -i {xapk} -dex -t raw)'.format(
134
+ xapk=quote(xapk),
135
+ apkeditor=tc['apkeditor'],
136
+ path=quote(td),
137
+ ), redir_stderr=True
138
+ ):
139
+ pub.sendMessage('progress.core.asm.disasm.update')
140
+ pub.sendMessage('progress.core.asm.disasm.done')
141
+
142
+ with open(f'{td}/root/manifest.json') as f:
143
+ import json
144
+ dom = json.loads(f.read())
145
+ for s in dom['split_apks']:
146
+ yield os.path.join(td, 'root', s['file'])
147
+
122
148
  class APKAssembler:
123
149
  @classmethod
124
150
  async def assemble_from_path(cls, wd: str, path: str) -> Tuple[str, str]:
@@ -166,6 +192,34 @@ class APKAssembler:
166
192
 
167
193
  return os.path.join(wd, 'output.apk'), os.path.join(wd, 'output.apk.idsig')
168
194
 
195
+ class APKSigner:
196
+ @classmethod
197
+ async def sign(cls, path: str, wd: str) -> Tuple[str, str]:
198
+ if path.endswith('.xapk'):
199
+ return await APKAssembler.merge_slices(path, wd)
200
+
201
+ import os
202
+ from shlex import quote
203
+ from trueseeing.core.tools import invoke_streaming
204
+ from trueseeing.core.android.tools import toolchains
205
+
206
+ pub.sendMessage('progress.core.asm.asm.begin')
207
+
208
+ with toolchains() as tc:
209
+ async for l in invoke_streaming(
210
+ '(rm -f {wd}/output.apk* && cp {path} {wd}/output.apk && java -jar {apksigner} sign --ks {keystore} --ks-pass pass:android {wd}/output.apk)'.format(
211
+ wd=quote(wd), path=quote(path),
212
+ apksigner=tc['apksigner'],
213
+ keystore=await SigningKey().key(),
214
+ ), redir_stderr=True
215
+ ):
216
+ pub.sendMessage('progress.core.asm.asm.update')
217
+
218
+ pub.sendMessage('progress.core.asm.asm.done')
219
+
220
+ return os.path.join(wd, 'output.apk'), os.path.join(wd, 'output.apk.idsig')
221
+
222
+
169
223
  class SigningKey:
170
224
  _path: str
171
225