frooky 0.1.1__tar.gz → 0.1.2__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.
- frooky-0.1.2/.github/workflows/publish.yml +35 -0
- frooky-0.1.2/.github/workflows/sync-labels.yml +117 -0
- frooky-0.1.2/.gitignore +44 -0
- {frooky-0.1.1 → frooky-0.1.2}/PKG-INFO +1 -1
- frooky-0.1.2/docs/examples/example.md +189 -0
- frooky-0.1.2/docs/examples/hooks.json +12 -0
- frooky-0.1.2/docs/examples/hooks2.json +12 -0
- frooky-0.1.2/docs/examples/output.json +7 -0
- frooky-0.1.2/docs/usage.md +292 -0
- frooky-0.1.2/frooky/__init__.py +8 -0
- frooky-0.1.2/frooky/_version.py +34 -0
- {frooky-0.1.1 → frooky-0.1.2}/frooky/cli.py +7 -0
- {frooky-0.1.1 → frooky-0.1.2}/frooky.egg-info/PKG-INFO +1 -1
- {frooky-0.1.1 → frooky-0.1.2}/frooky.egg-info/SOURCES.txt +9 -0
- {frooky-0.1.1 → frooky-0.1.2}/pyproject.toml +5 -2
- frooky-0.1.1/frooky/__init__.py +0 -4
- {frooky-0.1.1 → frooky-0.1.2}/LICENSE +0 -0
- {frooky-0.1.1 → frooky-0.1.2}/README.md +0 -0
- {frooky-0.1.1 → frooky-0.1.2}/frooky/__main__.py +0 -0
- {frooky-0.1.1 → frooky-0.1.2}/frooky/android/android_decoder.js +0 -0
- {frooky-0.1.1 → frooky-0.1.2}/frooky/android/base_script.js +0 -0
- {frooky-0.1.1 → frooky-0.1.2}/frooky/android/native_decoder.js +0 -0
- {frooky-0.1.1 → frooky-0.1.2}/frooky/frida_runner.py +0 -0
- {frooky-0.1.1 → frooky-0.1.2}/frooky/ios/base_script.js +0 -0
- {frooky-0.1.1 → frooky-0.1.2}/frooky/resources.py +0 -0
- {frooky-0.1.1 → frooky-0.1.2}/frooky.egg-info/dependency_links.txt +0 -0
- {frooky-0.1.1 → frooky-0.1.2}/frooky.egg-info/entry_points.txt +0 -0
- {frooky-0.1.1 → frooky-0.1.2}/frooky.egg-info/requires.txt +0 -0
- {frooky-0.1.1 → frooky-0.1.2}/frooky.egg-info/top_level.txt +0 -0
- {frooky-0.1.1 → frooky-0.1.2}/setup.cfg +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
with:
|
|
14
|
+
fetch-depth: 0 # Fetch all history and tags for setuptools-scm
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.x"
|
|
18
|
+
- run: python -m pip install --upgrade build
|
|
19
|
+
- run: python -m build
|
|
20
|
+
- uses: actions/upload-artifact@v4
|
|
21
|
+
with:
|
|
22
|
+
name: dist
|
|
23
|
+
path: dist/*
|
|
24
|
+
|
|
25
|
+
publish:
|
|
26
|
+
needs: build
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
permissions:
|
|
29
|
+
id-token: write
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/download-artifact@v4
|
|
32
|
+
with:
|
|
33
|
+
name: dist
|
|
34
|
+
path: dist
|
|
35
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
name: Sync Labels from Issues to PRs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, edited, reopened, synchronize]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
sync-labels:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
pull-requests: write
|
|
12
|
+
issues: read
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Sync labels from linked issues
|
|
16
|
+
uses: actions/github-script@v7
|
|
17
|
+
with:
|
|
18
|
+
script: |
|
|
19
|
+
const prNumber = context.payload.pull_request.number;
|
|
20
|
+
const prBody = context.payload.pull_request.body || '';
|
|
21
|
+
|
|
22
|
+
const issueNumbers = new Set();
|
|
23
|
+
|
|
24
|
+
// Pattern 1: Simple format - fixes #123, closes#456
|
|
25
|
+
const simpleRegex = /(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s*#(\d+)/gi;
|
|
26
|
+
const simpleMatches = [...prBody.matchAll(simpleRegex)];
|
|
27
|
+
simpleMatches.forEach(match => issueNumbers.add(parseInt(match[1])));
|
|
28
|
+
|
|
29
|
+
// Pattern 2: Owner/repo format - fixes owner/repo#123
|
|
30
|
+
const ownerRepoRegex = /(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s+([a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+)#(\d+)/gi;
|
|
31
|
+
const ownerRepoMatches = [...prBody.matchAll(ownerRepoRegex)];
|
|
32
|
+
ownerRepoMatches.forEach(match => {
|
|
33
|
+
const repoPath = match[1];
|
|
34
|
+
const issueNum = parseInt(match[2]);
|
|
35
|
+
// Only add if it's the same repository
|
|
36
|
+
if (repoPath === `${context.repo.owner}/${context.repo.repo}`) {
|
|
37
|
+
issueNumbers.add(issueNum);
|
|
38
|
+
} else {
|
|
39
|
+
console.log(`Skipping cross-repository issue: ${repoPath}#${issueNum}`);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Pattern 3: Full URL format - fixes https://github.com/owner/repo#123 or /issues/123
|
|
44
|
+
const urlRegex = /(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s+https?:\/\/github\.com\/([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)(?:\/issues\/|#)(\d+)/gi;
|
|
45
|
+
const urlMatches = [...prBody.matchAll(urlRegex)];
|
|
46
|
+
urlMatches.forEach(match => {
|
|
47
|
+
const owner = match[1];
|
|
48
|
+
const repo = match[2];
|
|
49
|
+
const issueNum = parseInt(match[3]);
|
|
50
|
+
// Only add if it's the same repository
|
|
51
|
+
if (owner === context.repo.owner && repo === context.repo.repo) {
|
|
52
|
+
issueNumbers.add(issueNum);
|
|
53
|
+
} else {
|
|
54
|
+
console.log(`Skipping cross-repository issue: ${owner}/${repo}#${issueNum}`);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const issueNumbersArray = Array.from(issueNumbers);
|
|
59
|
+
|
|
60
|
+
if (issueNumbersArray.length === 0) {
|
|
61
|
+
console.log('No linked issues found in PR description');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log(`Found linked issues: ${issueNumbersArray.join(', ')}`);
|
|
66
|
+
|
|
67
|
+
// Collect all labels from linked issues
|
|
68
|
+
const allLabels = new Set();
|
|
69
|
+
|
|
70
|
+
for (const issueNumber of issueNumbersArray) {
|
|
71
|
+
try {
|
|
72
|
+
const { data: issue } = await github.rest.issues.get({
|
|
73
|
+
owner: context.repo.owner,
|
|
74
|
+
repo: context.repo.repo,
|
|
75
|
+
issue_number: issueNumber
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
console.log(`Issue #${issueNumber} has labels: ${issue.labels.map(l => l.name).join(', ')}`);
|
|
79
|
+
|
|
80
|
+
issue.labels.forEach(label => {
|
|
81
|
+
allLabels.add(label.name);
|
|
82
|
+
});
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.log(`Error fetching issue #${issueNumber}: ${error.message}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (allLabels.size === 0) {
|
|
89
|
+
console.log('No labels found on linked issues');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(`Syncing labels to PR: ${Array.from(allLabels).join(', ')}`);
|
|
94
|
+
|
|
95
|
+
// Get current PR labels
|
|
96
|
+
const { data: currentPR } = await github.rest.pulls.get({
|
|
97
|
+
owner: context.repo.owner,
|
|
98
|
+
repo: context.repo.repo,
|
|
99
|
+
pull_number: prNumber
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const currentLabels = new Set(currentPR.labels.map(l => l.name));
|
|
103
|
+
|
|
104
|
+
// Add new labels from issues
|
|
105
|
+
const labelsToAdd = Array.from(allLabels).filter(label => !currentLabels.has(label));
|
|
106
|
+
|
|
107
|
+
if (labelsToAdd.length > 0) {
|
|
108
|
+
await github.rest.issues.addLabels({
|
|
109
|
+
owner: context.repo.owner,
|
|
110
|
+
repo: context.repo.repo,
|
|
111
|
+
issue_number: prNumber,
|
|
112
|
+
labels: labelsToAdd
|
|
113
|
+
});
|
|
114
|
+
console.log(`Added labels: ${labelsToAdd.join(', ')}`);
|
|
115
|
+
} else {
|
|
116
|
+
console.log('All labels already present on PR');
|
|
117
|
+
}
|
frooky-0.1.2/.gitignore
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Frooky local debug artifacts
|
|
2
|
+
tmp/
|
|
3
|
+
output.json
|
|
4
|
+
*.jsonl
|
|
5
|
+
|
|
6
|
+
# node
|
|
7
|
+
node_modules/
|
|
8
|
+
package.json
|
|
9
|
+
package-lock.json
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Python bytecode
|
|
13
|
+
__pycache__/
|
|
14
|
+
*.py[cod]
|
|
15
|
+
*$py.class
|
|
16
|
+
|
|
17
|
+
# Virtual environments
|
|
18
|
+
.venv/
|
|
19
|
+
venv/
|
|
20
|
+
env/
|
|
21
|
+
.env/
|
|
22
|
+
|
|
23
|
+
# Packaging/build outputs
|
|
24
|
+
build/
|
|
25
|
+
dist/
|
|
26
|
+
*.egg-info/
|
|
27
|
+
.eggs/
|
|
28
|
+
pip-wheel-metadata/
|
|
29
|
+
|
|
30
|
+
# Generated version file (created by setuptools-scm during build)
|
|
31
|
+
frooky/_version.py
|
|
32
|
+
|
|
33
|
+
# Test / type-check / lint caches
|
|
34
|
+
.pytest_cache/
|
|
35
|
+
.mypy_cache/
|
|
36
|
+
.ruff_cache/
|
|
37
|
+
.coverage
|
|
38
|
+
coverage.xml
|
|
39
|
+
htmlcov/
|
|
40
|
+
|
|
41
|
+
# IDE / OS
|
|
42
|
+
.DS_Store
|
|
43
|
+
.vscode/
|
|
44
|
+
.idea/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frooky
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Frida-powered hook runner based on JSON hook files.
|
|
5
5
|
Author-email: Carlos Holguera <holguera.cybersec@gmail.com>, Stefan Bernhardsgrütter <stefan.bernhardsgruetter@redguard.ch>
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Example: Hooking EncryptedSharedPreferences Methods
|
|
2
|
+
|
|
3
|
+
In this example we download and install the OWASP MAS [MASTG-DEMO-0060](https://mas.owasp.org/MASTG/demos/android/MASVS-STORAGE/MASTG-DEMO-0060/MASTG-DEMO-0060/) app (App Writing Sensitive Data to Sandbox using EncryptedSharedPreferences), create two hook files to hook methods related to encrypted shared preferences, and run `frooky` with both hook files against the app. We then analyze the output JSON Lines file to see the captured hook events.
|
|
4
|
+
|
|
5
|
+
## Creating Hook Files
|
|
6
|
+
|
|
7
|
+
First, create a hook file `hooks.json` to hook the `putString` and `putStringSet` methods of `android.app.SharedPreferencesImpl$EditorImpl`:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"category": "STORAGE",
|
|
12
|
+
"hooks": [
|
|
13
|
+
{
|
|
14
|
+
"class": "android.app.SharedPreferencesImpl$EditorImpl",
|
|
15
|
+
"methods": [
|
|
16
|
+
"putString",
|
|
17
|
+
"putStringSet"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
To demonstrate merging multiple hook files, we create a second hook file `hooks2.json` to hook the same methods in the underlying implementation class `androidx.security.crypto.EncryptedSharedPreferences$Editor`:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"category": "STORAGE",
|
|
29
|
+
"hooks": [
|
|
30
|
+
{
|
|
31
|
+
"class": "androidx.security.crypto.EncryptedSharedPreferences$Editor",
|
|
32
|
+
"methods": [
|
|
33
|
+
"putString",
|
|
34
|
+
"putStringSet"
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Running Frooky with Multiple Hook Files
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
frooky -U -n MASTestApp --platform android hooks.json hooks2.json
|
|
45
|
+
|
|
46
|
+
___ ____ Powered by Frida 17.6.0
|
|
47
|
+
/ __\ / _ | _ _ _ _ _ _ Target: MASTestApp
|
|
48
|
+
/ _\ | (_) | / _ \ / _ \ | / / | | | |
|
|
49
|
+
/ / / / | | | (_) | (_) || < | |_| | Device: Android Emulator 5554 (emulator-5554)
|
|
50
|
+
\/ /_/ |_| \___/ \___/ |_|\_\ \__, | Platform: android
|
|
51
|
+
|___/ Hook files: 2
|
|
52
|
+
Output: output.json
|
|
53
|
+
|
|
54
|
+
Press Ctrl+C to stop...
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
Resolved Hooks: 4
|
|
58
|
+
Events: 6 | Last: androidx.security.crypto.EncryptedSharedPreferences$Editor.p...
|
|
59
|
+
|
|
60
|
+
Stopping ...
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Notice how `frooky` indicates the Frida version, target app, device, platform, and output file.
|
|
64
|
+
|
|
65
|
+
It reports `Hook files: 2` and `Resolved Hooks: 4`, indicating that hooks from both files were merged and set up successfully.
|
|
66
|
+
|
|
67
|
+
The "Events" line shows the total number of captured hook events (6 in this case) and the last hooked method called.
|
|
68
|
+
|
|
69
|
+
## Analyzing the Output
|
|
70
|
+
|
|
71
|
+
The output file `output.json` will contain the captured hook events:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"type": "summary",
|
|
76
|
+
"hooks": [
|
|
77
|
+
{
|
|
78
|
+
"class": "android.app.SharedPreferencesImpl$EditorImpl",
|
|
79
|
+
"method": "putString",
|
|
80
|
+
"overloads": [
|
|
81
|
+
{
|
|
82
|
+
"args": [
|
|
83
|
+
"java.lang.String",
|
|
84
|
+
"java.lang.String"
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"class": "android.app.SharedPreferencesImpl$EditorImpl",
|
|
91
|
+
"method": "putStringSet",
|
|
92
|
+
"overloads": [
|
|
93
|
+
{
|
|
94
|
+
"args": [
|
|
95
|
+
"java.lang.String",
|
|
96
|
+
"java.util.Set"
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"class": "androidx.security.crypto.EncryptedSharedPreferences$Editor",
|
|
103
|
+
"method": "putString",
|
|
104
|
+
"overloads": [
|
|
105
|
+
{
|
|
106
|
+
"args": [
|
|
107
|
+
"java.lang.String",
|
|
108
|
+
"java.lang.String"
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"class": "androidx.security.crypto.EncryptedSharedPreferences$Editor",
|
|
115
|
+
"method": "putStringSet",
|
|
116
|
+
"overloads": [
|
|
117
|
+
{
|
|
118
|
+
"args": [
|
|
119
|
+
"java.lang.String",
|
|
120
|
+
"java.util.Set"
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
"totalHooks": 4,
|
|
127
|
+
"errors": [],
|
|
128
|
+
"totalErrors": 0
|
|
129
|
+
}
|
|
130
|
+
{
|
|
131
|
+
"id": "98862c65-de96-4872-95fc-c367b90c68a0",
|
|
132
|
+
"type": "hook",
|
|
133
|
+
"category": "STORAGE",
|
|
134
|
+
"time": "2026-01-19T10:45:50.454Z",
|
|
135
|
+
"class": "android.app.SharedPreferencesImpl$EditorImpl",
|
|
136
|
+
"method": "putString",
|
|
137
|
+
"instanceId": 31636504,
|
|
138
|
+
"stackTrace": [
|
|
139
|
+
"android.app.SharedPreferencesImpl$EditorImpl.putString(Native Method)",
|
|
140
|
+
"androidx.security.crypto.EncryptedSharedPreferences$Editor.putEncryptedObject(EncryptedSharedPreferences.java:389)",
|
|
141
|
+
"androidx.security.crypto.EncryptedSharedPreferences$Editor.putString(EncryptedSharedPreferences.java:262)",
|
|
142
|
+
"androidx.security.crypto.EncryptedSharedPreferences$Editor.putString(Native Method)",
|
|
143
|
+
"org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:33)",
|
|
144
|
+
"org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$12$lambda$11(MainActivity.kt:101)",
|
|
145
|
+
"org.owasp.mastestapp.MainActivityKt.$r8$lambda$Pm6AsbKBmypP53K-UABM21E_Xxk(Unknown Source:0)",
|
|
146
|
+
"org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)"
|
|
147
|
+
],
|
|
148
|
+
"inputParameters": [
|
|
149
|
+
{
|
|
150
|
+
"declaredType": "java.lang.String",
|
|
151
|
+
"value": "AQMRC7NJHrnwtE5suFMVSkzr7Zz0m55Yz/Gt4MQ8jXR9LQ+W"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"declaredType": "java.lang.String",
|
|
155
|
+
"value": "AX4R5MZvOLo+hzcBjDtSvkF+ryFEcXM66M/nzU33MpDv6fh//WWbG93gDW6f4JFXsRvq8WHJNI4zIoalUw=="
|
|
156
|
+
}
|
|
157
|
+
],
|
|
158
|
+
"returnValue": [
|
|
159
|
+
{
|
|
160
|
+
"declaredType": "android.content.SharedPreferences$Editor",
|
|
161
|
+
"value": "<instance: android.content.SharedPreferences$Editor, $className: android.app.SharedPreferencesImpl$EditorImpl>",
|
|
162
|
+
"runtimeType": "android.app.SharedPreferencesImpl$EditorImpl",
|
|
163
|
+
"instanceId": "31636504",
|
|
164
|
+
"instanceToString": "android.app.SharedPreferencesImpl$EditorImpl@1e2bc18"
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
... <TRUNCATED FOR BREVITY> ...
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
We can see the `summary` event at the start, followed by individual `hook` events capturing calls to the hooked methods along with their parameters, return values, and stack traces.
|
|
172
|
+
|
|
173
|
+
The summary event indicates no errors and a total of 4 resolved hooks:
|
|
174
|
+
|
|
175
|
+
- `android.app.SharedPreferencesImpl$EditorImpl.putString`
|
|
176
|
+
- `android.app.SharedPreferencesImpl$EditorImpl.putStringSet`
|
|
177
|
+
- `androidx.security.crypto.EncryptedSharedPreferences$Editor.putString`
|
|
178
|
+
- `androidx.security.crypto.EncryptedSharedPreferences$Editor.putStringSet`
|
|
179
|
+
|
|
180
|
+
The first hook event shows a call to `android.app.SharedPreferencesImpl$EditorImpl.putString`, capturing the input parameters (the key and encrypted value) and the return value (the editor instance). The stack trace provides context on where the method was called from within the app, specifically from `MastgTest.mastgTest`.
|
|
181
|
+
|
|
182
|
+
The input parameters in this event are:
|
|
183
|
+
|
|
184
|
+
- Key: `"AQMRC7NJHrnwtE5suFMVSkzr7Zz0m55Yz/Gt4MQ8jXR9LQ+W"`
|
|
185
|
+
- Encrypted Value: `"AX4R5MZvOLo+hzcBjDtSvkF+ryFEcXM66M/nzU33MpDv6fh//WWbG93gDW6f4JFXsRvq8WHJNI4zIoalUw=="`
|
|
186
|
+
|
|
187
|
+
Which represents the data being stored in the encrypted shared preferences. The "key" being the identifier for the stored value, and the "encrypted value" being the actual data stored in an encrypted format.
|
|
188
|
+
|
|
189
|
+
The return value indicates that the method returned an instance of `android.content.SharedPreferences$Editor`, allowing for method chaining.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{"type": "summary", "hooks": [{"class": "android.app.SharedPreferencesImpl$EditorImpl", "method": "putString", "overloads": [{"args": ["java.lang.String", "java.lang.String"]}]}, {"class": "android.app.SharedPreferencesImpl$EditorImpl", "method": "putStringSet", "overloads": [{"args": ["java.lang.String", "java.util.Set"]}]}, {"class": "androidx.security.crypto.EncryptedSharedPreferences$Editor", "method": "putString", "overloads": [{"args": ["java.lang.String", "java.lang.String"]}]}, {"class": "androidx.security.crypto.EncryptedSharedPreferences$Editor", "method": "putStringSet", "overloads": [{"args": ["java.lang.String", "java.util.Set"]}]}], "totalHooks": 4, "errors": [], "totalErrors": 0}
|
|
2
|
+
{"id": "98862c65-de96-4872-95fc-c367b90c68a0", "type": "hook", "category": "STORAGE", "time": "2026-01-19T10:45:50.454Z", "class": "android.app.SharedPreferencesImpl$EditorImpl", "method": "putString", "instanceId": 31636504, "stackTrace": ["android.app.SharedPreferencesImpl$EditorImpl.putString(Native Method)", "androidx.security.crypto.EncryptedSharedPreferences$Editor.putEncryptedObject(EncryptedSharedPreferences.java:389)", "androidx.security.crypto.EncryptedSharedPreferences$Editor.putString(EncryptedSharedPreferences.java:262)", "androidx.security.crypto.EncryptedSharedPreferences$Editor.putString(Native Method)", "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:33)", "org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$12$lambda$11(MainActivity.kt:101)", "org.owasp.mastestapp.MainActivityKt.$r8$lambda$Pm6AsbKBmypP53K-UABM21E_Xxk(Unknown Source:0)", "org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)"], "inputParameters": [{"declaredType": "java.lang.String", "value": "AQMRC7NJHrnwtE5suFMVSkzr7Zz0m55Yz/Gt4MQ8jXR9LQ+W"}, {"declaredType": "java.lang.String", "value": "AX4R5MZvOLo+hzcBjDtSvkF+ryFEcXM66M/nzU33MpDv6fh//WWbG93gDW6f4JFXsRvq8WHJNI4zIoalUw=="}], "returnValue": [{"declaredType": "android.content.SharedPreferences$Editor", "value": "<instance: android.content.SharedPreferences$Editor, $className: android.app.SharedPreferencesImpl$EditorImpl>", "runtimeType": "android.app.SharedPreferencesImpl$EditorImpl", "instanceId": "31636504", "instanceToString": "android.app.SharedPreferencesImpl$EditorImpl@1e2bc18"}]}
|
|
3
|
+
{"id": "4f9e6e0b-e150-4957-8cdf-1f95b8002aa4", "type": "hook", "category": "STORAGE", "time": "2026-01-19T10:45:50.448Z", "class": "androidx.security.crypto.EncryptedSharedPreferences$Editor", "method": "putString", "instanceId": 183362555, "stackTrace": ["androidx.security.crypto.EncryptedSharedPreferences$Editor.putString(Native Method)", "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:33)", "org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$12$lambda$11(MainActivity.kt:101)", "org.owasp.mastestapp.MainActivityKt.$r8$lambda$Pm6AsbKBmypP53K-UABM21E_Xxk(Unknown Source:0)", "org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)", "java.lang.Thread.run(Thread.java:1012)"], "inputParameters": [{"declaredType": "java.lang.String", "value": "EncryptedAWSKey"}, {"declaredType": "java.lang.String", "value": "AKIAABCDEFGHIJKLMNOP"}], "returnValue": [{"declaredType": "android.content.SharedPreferences$Editor", "value": "<instance: android.content.SharedPreferences$Editor, $className: androidx.security.crypto.EncryptedSharedPreferences$Editor>", "runtimeType": "androidx.security.crypto.EncryptedSharedPreferences$Editor", "instanceId": "183362555", "instanceToString": "androidx.security.crypto.EncryptedSharedPreferences$Editor@aede3fb"}]}
|
|
4
|
+
{"id": "de5037ee-e9f6-44c8-b1ad-a08c7fa123b8", "type": "hook", "category": "STORAGE", "time": "2026-01-19T10:45:50.464Z", "class": "android.app.SharedPreferencesImpl$EditorImpl", "method": "putString", "instanceId": 31636504, "stackTrace": ["android.app.SharedPreferencesImpl$EditorImpl.putString(Native Method)", "androidx.security.crypto.EncryptedSharedPreferences$Editor.putEncryptedObject(EncryptedSharedPreferences.java:389)", "androidx.security.crypto.EncryptedSharedPreferences$Editor.putString(EncryptedSharedPreferences.java:262)", "androidx.security.crypto.EncryptedSharedPreferences$Editor.putString(Native Method)", "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:34)", "org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$12$lambda$11(MainActivity.kt:101)", "org.owasp.mastestapp.MainActivityKt.$r8$lambda$Pm6AsbKBmypP53K-UABM21E_Xxk(Unknown Source:0)", "org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)"], "inputParameters": [{"declaredType": "java.lang.String", "value": "AQMRC7OWD6/h1iJseuzJVrClpwKE8swB8gOrGnsdaN4="}, {"declaredType": "java.lang.String", "value": "AX4R5MaQJvV8rAXOUTIQ+VZz4OzP5jiyPLkdlQK2sMOlgrXuuIp+7DRPPr//E3JtSQje/WXRy/JSx+jIwwxIOUTfyCwLOpxbhaknMFeQJGJ1"}], "returnValue": [{"declaredType": "android.content.SharedPreferences$Editor", "value": "<instance: android.content.SharedPreferences$Editor, $className: android.app.SharedPreferencesImpl$EditorImpl>", "runtimeType": "android.app.SharedPreferencesImpl$EditorImpl", "instanceId": "31636504", "instanceToString": "android.app.SharedPreferencesImpl$EditorImpl@1e2bc18"}]}
|
|
5
|
+
{"id": "1987cff2-bf9b-4ba0-ab4d-5ab2b8225fdb", "type": "hook", "category": "STORAGE", "time": "2026-01-19T10:45:50.457Z", "class": "androidx.security.crypto.EncryptedSharedPreferences$Editor", "method": "putString", "instanceId": 183362555, "stackTrace": ["androidx.security.crypto.EncryptedSharedPreferences$Editor.putString(Native Method)", "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:34)", "org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$12$lambda$11(MainActivity.kt:101)", "org.owasp.mastestapp.MainActivityKt.$r8$lambda$Pm6AsbKBmypP53K-UABM21E_Xxk(Unknown Source:0)", "org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)", "java.lang.Thread.run(Thread.java:1012)"], "inputParameters": [{"declaredType": "java.lang.String", "value": "GitHubToken"}, {"declaredType": "java.lang.String", "value": "ghp_1234567890abcdefghijklmnopqrstuvABCD"}], "returnValue": [{"declaredType": "android.content.SharedPreferences$Editor", "value": "<instance: android.content.SharedPreferences$Editor, $className: androidx.security.crypto.EncryptedSharedPreferences$Editor>", "runtimeType": "androidx.security.crypto.EncryptedSharedPreferences$Editor", "instanceId": "183362555", "instanceToString": "androidx.security.crypto.EncryptedSharedPreferences$Editor@aede3fb"}]}
|
|
6
|
+
{"id": "ab724a0a-e1fe-43ed-8d5e-0734c2636130", "type": "hook", "category": "STORAGE", "time": "2026-01-19T10:45:50.471Z", "class": "android.app.SharedPreferencesImpl$EditorImpl", "method": "putString", "instanceId": 31636504, "stackTrace": ["android.app.SharedPreferencesImpl$EditorImpl.putString(Native Method)", "androidx.security.crypto.EncryptedSharedPreferences$Editor.putEncryptedObject(EncryptedSharedPreferences.java:389)", "androidx.security.crypto.EncryptedSharedPreferences$Editor.putStringSet(EncryptedSharedPreferences.java:287)", "androidx.security.crypto.EncryptedSharedPreferences$Editor.putStringSet(Native Method)", "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:35)", "org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$12$lambda$11(MainActivity.kt:101)", "org.owasp.mastestapp.MainActivityKt.$r8$lambda$Pm6AsbKBmypP53K-UABM21E_Xxk(Unknown Source:0)", "org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)"], "inputParameters": [{"declaredType": "java.lang.String", "value": "AQMRC7PmmtQeo27DnFbH7Ruc9Gwm36B+0IwxJVaN2xsfZw=="}, {"declaredType": "java.lang.String", "value": "AX4R5MY+JRTS3rMmjIKCV/0sk2z1cS8Bj1McZ75Zzfk1MIh/h+SZjK5L/KMmcnkeFOxpHhVZJp2knj7u8xQH5pSb6xHJOYN+T7ZJf/uvS2VWPEMnUl/op+BxSRq+FjrmH1WhuROAtEdlU8QJFwCNgLD0CdzZlw7l7z4s7MWiiYWN90HWhczjWYBIRRbmoOpIuNF9JkWSKZ2hkixS1ckJShXWnylG+rtvmVs6wMCazCIsl3yr+ABQ2WIx+CxsEZMEXMdK1413B25rO2m2um7b0xOjmGagXj9moesXgIDACXPzjXzOgsGHv93yOmsDuXBqIVHFqjsNDrXkI+VYEwmi44rtkPW9vNw4G3eOMr7DVN3IhVs="}], "returnValue": [{"declaredType": "android.content.SharedPreferences$Editor", "value": "<instance: android.content.SharedPreferences$Editor, $className: android.app.SharedPreferencesImpl$EditorImpl>", "runtimeType": "android.app.SharedPreferencesImpl$EditorImpl", "instanceId": "31636504", "instanceToString": "android.app.SharedPreferencesImpl$EditorImpl@1e2bc18"}]}
|
|
7
|
+
{"id": "27ef8302-d842-4db0-90a8-355d35e163b2", "type": "hook", "category": "STORAGE", "time": "2026-01-19T10:45:50.466Z", "class": "androidx.security.crypto.EncryptedSharedPreferences$Editor", "method": "putStringSet", "instanceId": 183362555, "stackTrace": ["androidx.security.crypto.EncryptedSharedPreferences$Editor.putStringSet(Native Method)", "org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:35)", "org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$12$lambda$11(MainActivity.kt:101)", "org.owasp.mastestapp.MainActivityKt.$r8$lambda$Pm6AsbKBmypP53K-UABM21E_Xxk(Unknown Source:0)", "org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)", "java.lang.Thread.run(Thread.java:1012)"], "inputParameters": [{"declaredType": "java.lang.String", "value": "preSharedKeys"}, {"declaredType": "java.util.Set", "value": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALfX7kbfFv3pc3JjOHQ=\n-----END PRIVATE KEY-----,-----BEGIN PRIVATE KEY-----\ngJXS9EwpuzK8U1TOgfplwfKEVngCE2D5FNBQWvNmuHHbigmTCabsA=\n-----END PRIVATE KEY-----", "runtimeType": "java.util.HashSet", "instanceId": "110008543", "instanceToString": "[-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALfX7kbfFv3pc3JjOHQ=\n-----END PRIVATE KEY-----, -----BEGIN PRIVATE KEY-----\ngJXS9EwpuzK8U1TOgfplwfKEVngCE2D5FNBQWvNmuHHbigmTCabsA=\n-----END PRIVATE KEY-----]"}], "returnValue": [{"declaredType": "android.content.SharedPreferences$Editor", "value": "<instance: android.content.SharedPreferences$Editor, $className: androidx.security.crypto.EncryptedSharedPreferences$Editor>", "runtimeType": "androidx.security.crypto.EncryptedSharedPreferences$Editor", "instanceId": "183362555", "instanceToString": "androidx.security.crypto.EncryptedSharedPreferences$Editor@aede3fb"}]}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# Usage
|
|
2
|
+
|
|
3
|
+
## Hook Files
|
|
4
|
+
|
|
5
|
+
Hook files use JSON format. When multiple hook files are provided, their `hooks` arrays are merged together.
|
|
6
|
+
|
|
7
|
+
### Basic Structure
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"category": "STORAGE",
|
|
12
|
+
"hooks": [
|
|
13
|
+
{
|
|
14
|
+
"class": "com.example.MyClass",
|
|
15
|
+
"methods": ["method1", "method2"]
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Java/Kotlin Hooks
|
|
22
|
+
|
|
23
|
+
#### Simple Method Hook
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"class": "java.io.File",
|
|
28
|
+
"method": "exists"
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
#### Multiple Methods
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"class": "java.io.FileOutputStream",
|
|
37
|
+
"methods": ["write", "close", "flush"]
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
#### Method Overloads
|
|
42
|
+
|
|
43
|
+
Specify exact method signatures using `overloads`:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"class": "java.io.FileOutputStream",
|
|
48
|
+
"method": "write",
|
|
49
|
+
"overloads": [
|
|
50
|
+
{ "args": ["[B"] },
|
|
51
|
+
{ "args": ["[B", "int", "int"] },
|
|
52
|
+
{ "args": ["int"] }
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### Stack Traces
|
|
58
|
+
|
|
59
|
+
Control stack trace depth with `maxFrames`:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"class": "javax.crypto.Cipher",
|
|
64
|
+
"method": "doFinal",
|
|
65
|
+
"maxFrames": 10
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Native Hooks
|
|
70
|
+
|
|
71
|
+
Native hooks intercept C/C++ functions. Set `native: true` and specify the symbol.
|
|
72
|
+
|
|
73
|
+
#### Basic Native Hook
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"native": true,
|
|
78
|
+
"symbol": "open",
|
|
79
|
+
"module": "libc.so"
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### Argument Descriptors
|
|
84
|
+
|
|
85
|
+
Define how arguments should be captured:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"native": true,
|
|
90
|
+
"symbol": "write",
|
|
91
|
+
"module": "libc.so",
|
|
92
|
+
"args": [
|
|
93
|
+
{ "name": "fd", "type": "int32" },
|
|
94
|
+
{ "name": "buf", "type": "bytes", "length": 256 },
|
|
95
|
+
{ "name": "count", "type": "int32" }
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### Dynamic Length from Another Argument
|
|
101
|
+
|
|
102
|
+
Use `lengthInArg` to read length from another argument:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"native": true,
|
|
107
|
+
"symbol": "send",
|
|
108
|
+
"module": "libc.so",
|
|
109
|
+
"args": [
|
|
110
|
+
{ "name": "sockfd", "type": "int32" },
|
|
111
|
+
{ "name": "buf", "type": "bytes", "lengthInArg": 2 },
|
|
112
|
+
{ "name": "len", "type": "int32" },
|
|
113
|
+
{ "name": "flags", "type": "int32" }
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### Capture Return Values
|
|
119
|
+
|
|
120
|
+
Set `returnValue: true` on the last argument:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"native": true,
|
|
125
|
+
"symbol": "read",
|
|
126
|
+
"module": "libc.so",
|
|
127
|
+
"args": [
|
|
128
|
+
{ "name": "fd", "type": "int32" },
|
|
129
|
+
{ "name": "buf", "type": "bytes", "lengthInArg": 2 },
|
|
130
|
+
{ "name": "count", "type": "int32" },
|
|
131
|
+
{ "name": "result", "type": "int32", "returnValue": true }
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
#### Outbound Parameters
|
|
137
|
+
|
|
138
|
+
Use `direction: "out"` for output parameters that should be read after the function returns:
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"native": true,
|
|
143
|
+
"symbol": "CCCrypt",
|
|
144
|
+
"module": "libcommonCrypto.dylib",
|
|
145
|
+
"args": [
|
|
146
|
+
{ "name": "op", "type": "int32" },
|
|
147
|
+
{ "name": "alg", "type": "int32" },
|
|
148
|
+
{ "name": "dataOut", "type": "bytes", "length": 256, "direction": "out" },
|
|
149
|
+
{ "name": "dataOutMoved", "type": "pointer", "direction": "out" }
|
|
150
|
+
]
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### Filter by Value
|
|
155
|
+
|
|
156
|
+
Only capture events when arguments match specific values:
|
|
157
|
+
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"native": true,
|
|
161
|
+
"symbol": "open",
|
|
162
|
+
"module": "libc.so",
|
|
163
|
+
"args": [
|
|
164
|
+
{ "name": "pathname", "type": "string", "filter": ["/data/", "/sdcard/"] }
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
#### Filter by Stack Trace
|
|
170
|
+
|
|
171
|
+
Only capture events when the call stack contains specific patterns:
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"native": true,
|
|
176
|
+
"symbol": "SSL_write",
|
|
177
|
+
"module": "libssl.so",
|
|
178
|
+
"filterEventsByStacktrace": ["com.example.network", "okhttp3"]
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### Debug Mode
|
|
183
|
+
|
|
184
|
+
Enable verbose logging for troubleshooting:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"native": true,
|
|
189
|
+
"symbol": "problematic_function",
|
|
190
|
+
"module": "libfoo.so",
|
|
191
|
+
"debug": true
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Argument Types
|
|
196
|
+
|
|
197
|
+
| Type | Description |
|
|
198
|
+
|------|-------------|
|
|
199
|
+
| `string` | Null-terminated C string |
|
|
200
|
+
| `int32` | 32-bit signed integer |
|
|
201
|
+
| `uint32` | 32-bit unsigned integer |
|
|
202
|
+
| `int64` | 64-bit signed integer |
|
|
203
|
+
| `pointer` | Memory address |
|
|
204
|
+
| `bytes` | Raw bytes (requires `length` or `lengthInArg`) |
|
|
205
|
+
| `bool` | Boolean value |
|
|
206
|
+
| `double` | 64-bit floating point |
|
|
207
|
+
| `CFData` | iOS CFData object |
|
|
208
|
+
| `CFDictionary` | iOS CFDictionary object |
|
|
209
|
+
|
|
210
|
+
### iOS Objective-C Hooks
|
|
211
|
+
|
|
212
|
+
Hook Objective-C methods using `objClass` and `symbol`:
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"native": true,
|
|
217
|
+
"objClass": "NSURLSession",
|
|
218
|
+
"symbol": "dataTaskWithRequest:completionHandler:"
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Output Format
|
|
223
|
+
|
|
224
|
+
Events are written to the output file in JSON Lines format (one JSON object per line, known as NDJSON). You can easily pretty-print it e.g. using `jq . output.json`.
|
|
225
|
+
|
|
226
|
+
First of all, a summary event is written when hooking is initialized, listing all resolved hooks. It includes:
|
|
227
|
+
|
|
228
|
+
- `type`: Indicates this is a summary event
|
|
229
|
+
- `hooks`: An array of all hooked methods with their classes and overloads
|
|
230
|
+
- `totalHooks`: Total number of hooks that were set up
|
|
231
|
+
- `errors`: Any errors encountered while setting up hooks
|
|
232
|
+
- `totalErrors`: Total number of errors encountered
|
|
233
|
+
|
|
234
|
+
After that, individual hook events are written each time a hooked method/function is called.
|
|
235
|
+
|
|
236
|
+
Example hook event (pretty-printed for clarity):
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
{
|
|
240
|
+
"id": "0117229c-b034-4676-ba33-075fc27922ba",
|
|
241
|
+
"type": "hook",
|
|
242
|
+
"category": "STORAGE",
|
|
243
|
+
"time": "2026-01-18T16:17:25.470Z",
|
|
244
|
+
"class": "android.app.SharedPreferencesImpl$EditorImpl",
|
|
245
|
+
"method": "putString",
|
|
246
|
+
"instanceId": 268282727,
|
|
247
|
+
"stackTrace": [
|
|
248
|
+
"android.app.SharedPreferencesImpl$EditorImpl.putString(Native Method)",
|
|
249
|
+
"androidx.security.crypto.EncryptedSharedPreferences$Editor.putEncryptedObject(EncryptedSharedPreferences.java:389)",
|
|
250
|
+
...
|
|
251
|
+
],
|
|
252
|
+
"inputParameters": [
|
|
253
|
+
{
|
|
254
|
+
"declaredType": "java.lang.String",
|
|
255
|
+
"value": "AQMRC7OWD6/h1iJseuzJVrClpwKE8swB8gOrGnsdaN4="
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"declaredType": "java.lang.String",
|
|
259
|
+
"value": "AX4R5MZu+J1p0U3hvKyuEnJDQopI+wupiSi8CAG8dzq0PU76NbbebjhqMtqCD7fFUy2SmmQuQVDlDrrj30d3GQes+PlD8HmRFszVTge039GQ"
|
|
260
|
+
}
|
|
261
|
+
],
|
|
262
|
+
"returnValue": [
|
|
263
|
+
{
|
|
264
|
+
"declaredType": "android.content.SharedPreferences$Editor",
|
|
265
|
+
"value": "<instance: android.content.SharedPreferences$Editor, $className: android.app.SharedPreferencesImpl$EditorImpl>",
|
|
266
|
+
"runtimeType": "android.app.SharedPreferencesImpl$EditorImpl",
|
|
267
|
+
"instanceId": "268282727",
|
|
268
|
+
"instanceToString": "android.app.SharedPreferencesImpl$EditorImpl@ffdab67"
|
|
269
|
+
}
|
|
270
|
+
]
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Explanation of fields:
|
|
275
|
+
|
|
276
|
+
- `id`: Unique identifier for the event (UUID)
|
|
277
|
+
- `type`: Type of event (e.g., "hook", "summary")
|
|
278
|
+
- `category`: Category specified in the hook file (e.g., "STORAGE", "CRYPTO")
|
|
279
|
+
- `time`: Timestamp of the event in ISO 8601 format
|
|
280
|
+
- `class`: Hooked class name
|
|
281
|
+
- `method`: Hooked method name
|
|
282
|
+
- `instanceId`: Unique identifier for the instance on which the method was called
|
|
283
|
+
- `stackTrace`: Captured stack trace leading to the method call
|
|
284
|
+
- `inputParameters`: Array of input parameters with their declared types and values
|
|
285
|
+
- `declaredType`: The declared type of the parameter
|
|
286
|
+
- `value`: The captured value of the parameter
|
|
287
|
+
- `returnValue`: Array of return values with their declared types and values
|
|
288
|
+
- `declaredType`: The declared type of the return value
|
|
289
|
+
- `value`: The captured value of the return value
|
|
290
|
+
- `runtimeType`: The actual runtime type of the return value
|
|
291
|
+
- `instanceId`: Unique identifier for the return value instance
|
|
292
|
+
- `instanceToString`: String representation of the return value instance
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# file generated by setuptools-scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
"__version__",
|
|
6
|
+
"__version_tuple__",
|
|
7
|
+
"version",
|
|
8
|
+
"version_tuple",
|
|
9
|
+
"__commit_id__",
|
|
10
|
+
"commit_id",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
TYPE_CHECKING = False
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from typing import Tuple
|
|
16
|
+
from typing import Union
|
|
17
|
+
|
|
18
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
+
COMMIT_ID = Union[str, None]
|
|
20
|
+
else:
|
|
21
|
+
VERSION_TUPLE = object
|
|
22
|
+
COMMIT_ID = object
|
|
23
|
+
|
|
24
|
+
version: str
|
|
25
|
+
__version__: str
|
|
26
|
+
__version_tuple__: VERSION_TUPLE
|
|
27
|
+
version_tuple: VERSION_TUPLE
|
|
28
|
+
commit_id: COMMIT_ID
|
|
29
|
+
__commit_id__: COMMIT_ID
|
|
30
|
+
|
|
31
|
+
__version__ = version = '0.1.2'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 1, 2)
|
|
33
|
+
|
|
34
|
+
__commit_id__ = commit_id = 'gaa822d4fd'
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import argparse
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
+
from . import __version__
|
|
6
7
|
from .frida_runner import FrookyRunner, RunnerOptions
|
|
7
8
|
|
|
8
9
|
|
|
@@ -12,6 +13,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
12
13
|
description="Run Frooky hooks using Frida's Python bindings.",
|
|
13
14
|
)
|
|
14
15
|
|
|
16
|
+
parser.add_argument(
|
|
17
|
+
"--version",
|
|
18
|
+
action="version",
|
|
19
|
+
version=f"frooky {__version__}",
|
|
20
|
+
)
|
|
21
|
+
|
|
15
22
|
# Device selection group
|
|
16
23
|
device_group = parser.add_argument_group("device selection")
|
|
17
24
|
device_group.add_argument("-D", "--device", metavar="ID", help="Connect to device with the given ID")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frooky
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Frida-powered hook runner based on JSON hook files.
|
|
5
5
|
Author-email: Carlos Holguera <holguera.cybersec@gmail.com>, Stefan Bernhardsgrütter <stefan.bernhardsgruetter@redguard.ch>
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
.gitignore
|
|
1
2
|
LICENSE
|
|
2
3
|
README.md
|
|
3
4
|
pyproject.toml
|
|
5
|
+
.github/workflows/publish.yml
|
|
6
|
+
.github/workflows/sync-labels.yml
|
|
7
|
+
docs/usage.md
|
|
8
|
+
docs/examples/example.md
|
|
9
|
+
docs/examples/hooks.json
|
|
10
|
+
docs/examples/hooks2.json
|
|
11
|
+
docs/examples/output.json
|
|
4
12
|
frooky/__init__.py
|
|
5
13
|
frooky/__main__.py
|
|
14
|
+
frooky/_version.py
|
|
6
15
|
frooky/cli.py
|
|
7
16
|
frooky/frida_runner.py
|
|
8
17
|
frooky/resources.py
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=70"]
|
|
2
|
+
requires = ["setuptools>=70", "setuptools-scm>=8"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "frooky"
|
|
7
|
-
|
|
7
|
+
dynamic = ["version"]
|
|
8
8
|
description = "Frida-powered hook runner based on JSON hook files."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -50,3 +50,6 @@ frooky = [
|
|
|
50
50
|
"android/*.js",
|
|
51
51
|
"ios/*.js",
|
|
52
52
|
]
|
|
53
|
+
|
|
54
|
+
[tool.setuptools_scm]
|
|
55
|
+
version_file = "frooky/_version.py"
|
frooky-0.1.1/frooky/__init__.py
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|