snikrflow 0.1.0__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 (52) hide show
  1. snikrflow-0.1.0/LICENSE +21 -0
  2. snikrflow-0.1.0/PKG-INFO +240 -0
  3. snikrflow-0.1.0/README.md +187 -0
  4. snikrflow-0.1.0/pyproject.toml +51 -0
  5. snikrflow-0.1.0/setup.cfg +4 -0
  6. snikrflow-0.1.0/snikrflow/__init__.py +0 -0
  7. snikrflow-0.1.0/snikrflow/analysis/__init__.py +0 -0
  8. snikrflow-0.1.0/snikrflow/analysis/backends/__init__.py +0 -0
  9. snikrflow-0.1.0/snikrflow/analysis/backends/angr/__init__.py +0 -0
  10. snikrflow-0.1.0/snikrflow/analysis/backends/angr/audit.py +145 -0
  11. snikrflow-0.1.0/snikrflow/analysis/backends/angr/callgraph.py +40 -0
  12. snikrflow-0.1.0/snikrflow/analysis/backends/angr/cfg.py +49 -0
  13. snikrflow-0.1.0/snikrflow/analysis/backends/angr/dataflow.py +205 -0
  14. snikrflow-0.1.0/snikrflow/analysis/backends/angr/functions.py +99 -0
  15. snikrflow-0.1.0/snikrflow/analysis/backends/angr/jni.py +45 -0
  16. snikrflow-0.1.0/snikrflow/analysis/backends/angr/reachability.py +84 -0
  17. snikrflow-0.1.0/snikrflow/analysis/backends/angr/sinks.py +74 -0
  18. snikrflow-0.1.0/snikrflow/analysis/backends/angr/symbolic.py +152 -0
  19. snikrflow-0.1.0/snikrflow/analysis/interfaces/__init__.py +0 -0
  20. snikrflow-0.1.0/snikrflow/analysis/interfaces/apk_audit.py +97 -0
  21. snikrflow-0.1.0/snikrflow/analysis/interfaces/audit.py +14 -0
  22. snikrflow-0.1.0/snikrflow/analysis/interfaces/callgraph.py +13 -0
  23. snikrflow-0.1.0/snikrflow/analysis/interfaces/cfg.py +9 -0
  24. snikrflow-0.1.0/snikrflow/analysis/interfaces/dataflow.py +31 -0
  25. snikrflow-0.1.0/snikrflow/analysis/interfaces/functions.py +20 -0
  26. snikrflow-0.1.0/snikrflow/analysis/interfaces/jni.py +23 -0
  27. snikrflow-0.1.0/snikrflow/analysis/interfaces/reachability.py +14 -0
  28. snikrflow-0.1.0/snikrflow/analysis/interfaces/sinks.py +22 -0
  29. snikrflow-0.1.0/snikrflow/analysis/interfaces/symbolic.py +30 -0
  30. snikrflow-0.1.0/snikrflow/android/__init__.py +0 -0
  31. snikrflow-0.1.0/snikrflow/android/apk.py +78 -0
  32. snikrflow-0.1.0/snikrflow/cli.py +590 -0
  33. snikrflow-0.1.0/snikrflow/core/__init__.py +0 -0
  34. snikrflow-0.1.0/snikrflow/core/exceptions.py +36 -0
  35. snikrflow-0.1.0/snikrflow/core/models.py +326 -0
  36. snikrflow-0.1.0/snikrflow/elf/__init__.py +0 -0
  37. snikrflow-0.1.0/snikrflow/elf/jni.py +54 -0
  38. snikrflow-0.1.0/snikrflow/elf/loader.py +105 -0
  39. snikrflow-0.1.0/snikrflow/elf/metadata.py +84 -0
  40. snikrflow-0.1.0/snikrflow/reporting/__init__.py +0 -0
  41. snikrflow-0.1.0/snikrflow/reporting/sarif.py +122 -0
  42. snikrflow-0.1.0/snikrflow/reporting/terminal.py +412 -0
  43. snikrflow-0.1.0/snikrflow/sinks/__init__.py +0 -0
  44. snikrflow-0.1.0/snikrflow/sinks/database.py +30 -0
  45. snikrflow-0.1.0/snikrflow/sinks/signatures.py +40 -0
  46. snikrflow-0.1.0/snikrflow/utils/__init__.py +0 -0
  47. snikrflow-0.1.0/snikrflow.egg-info/PKG-INFO +240 -0
  48. snikrflow-0.1.0/snikrflow.egg-info/SOURCES.txt +50 -0
  49. snikrflow-0.1.0/snikrflow.egg-info/dependency_links.txt +1 -0
  50. snikrflow-0.1.0/snikrflow.egg-info/entry_points.txt +2 -0
  51. snikrflow-0.1.0/snikrflow.egg-info/requires.txt +9 -0
  52. snikrflow-0.1.0/snikrflow.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stodachon (redhound)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,240 @@
1
+ Metadata-Version: 2.4
2
+ Name: snikrflow
3
+ Version: 0.1.0
4
+ Summary: Native binary security triage and reachability platform, built on angr and Claripy.
5
+ Author: Stodachon
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Stodachon (redhound)
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/stodachon/snikrflow
29
+ Project-URL: Repository, https://github.com/stodachon/snikrflow
30
+ Project-URL: Issues, https://github.com/stodachon/snikrflow/issues
31
+ Keywords: security,reverse-engineering,android,jni,binary-analysis,angr,symbolic-execution,triage,reachability,elf
32
+ Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Environment :: Console
34
+ Classifier: Intended Audience :: Information Technology
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Operating System :: OS Independent
37
+ Classifier: Programming Language :: Python :: 3.11
38
+ Classifier: Programming Language :: Python :: 3.12
39
+ Classifier: Topic :: Security
40
+ Classifier: Topic :: Software Development :: Disassemblers
41
+ Requires-Python: >=3.11
42
+ Description-Content-Type: text/markdown
43
+ License-File: LICENSE
44
+ Requires-Dist: angr
45
+ Requires-Dist: claripy
46
+ Requires-Dist: pyelftools
47
+ Requires-Dist: typer[all]
48
+ Requires-Dist: rich
49
+ Requires-Dist: networkx
50
+ Provides-Extra: dev
51
+ Requires-Dist: pytest; extra == "dev"
52
+ Dynamic: license-file
53
+
54
+ <p align="center">
55
+ <img src="logo.svg" width="140" alt="SnikrFlow logo"/>
56
+ </p>
57
+
58
+ <h1 align="center">SnikrFlow</h1>
59
+
60
+ <p align="center">
61
+ <b>Native binary security triage and reachability platform.</b><br/>
62
+ Stop wondering where to start reversing a binary. Let SnikrFlow tell you.
63
+ </p>
64
+
65
+ <p align="center">
66
+ <a href="https://pypi.org/project/snikrflow/"><img alt="PyPI" src="https://img.shields.io/pypi/v/snikrflow.svg"></a>
67
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-orange.svg"></a>
68
+ <img alt="Python" src="https://img.shields.io/badge/python-3.11%2B-blue.svg">
69
+ </p>
70
+
71
+ ---
72
+
73
+ ## The problem
74
+
75
+ You've got a large ELF binary or an Android `.so` library. Thousands of functions.
76
+ No idea where the interesting stuff is. You could spend two hours blindly
77
+ following callers, references, and branches in Ghidra... or you could ask:
78
+
79
+ > "Before I spend two hours reversing this binary, tell me where I should
80
+ > spend those two hours."
81
+
82
+ That's what SnikrFlow answers.
83
+
84
+ SnikrFlow is **not** a replacement for Ghidra, IDA, Binary Ninja, or GDB. It's
85
+ the step *before* those tools: automated triage, reachability analysis, and
86
+ symbolic execution that narrows "here are thousands of functions" down to
87
+ "here are the paths you should investigate first."
88
+
89
+ ```
90
+ Large ELF / Android .so
91
+ │
92
+ ▼
93
+ SnikrFlow
94
+ │
95
+ ├── ELF analysis ├── Call graph analysis
96
+ ├── Function discovery ├── Reachability
97
+ ├── JNI discovery ├── Data-flow analysis
98
+ ├── Security sink detect └── Symbolic execution
99
+ ├── CFG analysis
100
+ │
101
+ ▼
102
+ Prioritized findings
103
+ │
104
+ ▼
105
+ Ghidra
106
+ │
107
+ ▼
108
+ Deep manual RE
109
+ ```
110
+
111
+ ## Install
112
+
113
+ ```bash
114
+ pip install snikrflow
115
+ ```
116
+
117
+ (Or, for development: clone the repo and `pip install -e .`)
118
+
119
+ Requires Python 3.11+. Built on [angr](https://angr.io) + Claripy for the
120
+ underlying static/symbolic analysis engine.
121
+
122
+ ## Quick start
123
+
124
+ ```bash
125
+ # The whole pipeline, orchestrated -- this is the one you actually want
126
+ snikrflow audit libfoo.so
127
+
128
+ # Or drill in stage by stage:
129
+ snikrflow info libfoo.so # ELF metadata
130
+ snikrflow functions libfoo.so # discovered functions (angr CFGFast + symbols)
131
+ snikrflow jni libfoo.so # statically-exported JNI entry points
132
+ snikrflow sinks libfoo.so # security-sensitive function calls
133
+ snikrflow cfg libfoo.so # control-flow graph summary
134
+ snikrflow callers libfoo.so 0x400d80 # who calls this address
135
+ snikrflow callees libfoo.so 0x400d80 # what this address calls
136
+ snikrflow reach libfoo.so --source 0x403a00 --sink 0x400d80 # static reachability
137
+ snikrflow solve libfoo.so --source 0x403a00 --target 0x400d80 # symbolic candidate input
138
+ snikrflow dataflow libfoo.so --source 0x403a00 --sink 0x400d80 # does tainted data reach the sink?
139
+ ```
140
+
141
+ Every command supports `--json` for scripting/piping into other tools.
142
+
143
+ ## What `audit` actually does
144
+
145
+ `audit` is the flagship command -- it runs the full pipeline automatically:
146
+
147
+ 1. Discovers all functions, JNI entry points, and security-sensitive sinks.
148
+ 2. Checks static (call-graph) reachability across every JNI-to-sink pair --
149
+ cheap, so it covers everything.
150
+ 3. Ranks the statically-reachable pairs by call-chain directness and picks
151
+ the most promising few for **deep analysis**.
152
+ 4. Runs data-flow (symbolic) analysis on that shortlist -- expensive, so
153
+ it's spent only where it matters.
154
+ 5. Scores risk transparently and prints ranked findings.
155
+
156
+ ```
157
+ SnikrFlow Security Audit
158
+ ═══════════════════════════
159
+ Binary
160
+ Architecture: x86_64
161
+ Type: Shared Object
162
+ Functions
163
+ 294 discovered
164
+ 3 JNI candidates
165
+ Security-sensitive operations
166
+ 2 identified
167
+ Interesting paths
168
+ 1 statically reachable
169
+ 1 selected for data-flow analysis
170
+ Findings
171
+ [MEDIUM] JNI -> memory
172
+ Source: Java_..._MainActivity_init @ 0x403a00
173
+ Sink: strncpy @ 0x400d80 (memory)
174
+ Status: statically_reachable
175
+ ```
176
+
177
+ Risk levels are a **prioritization aid, not a vulnerability verdict**. Manual
178
+ verification is still required -- SnikrFlow narrows the search space, it
179
+ doesn't replace the researcher.
180
+
181
+ ## Architecture
182
+
183
+ SnikrFlow is built in layers, deliberately kept independent from its analysis
184
+ backend:
185
+
186
+ ```
187
+ CLI ──┐
188
+ ├──> Core Engine ──> SnikrFlow Models ──> Analysis Interfaces ──> Backends
189
+ Future GUI ──┘ (angr today;
190
+ Unicorn/Pin/Dyninst
191
+ possible later)
192
+ ```
193
+
194
+ - **SnikrFlow owns the concepts** (`Function`, `SecuritySink`, `AnalysisPath`,
195
+ `SymbolicResult`, `AnalysisFinding`, ...). The CLI never touches angr
196
+ objects directly.
197
+ - **`analysis/interfaces/`** define *what* SnikrFlow can do.
198
+ **`analysis/backends/angr/`** define *how* angr does it today. Adding a
199
+ future backend means writing a new backend module, not rewriting the CLI.
200
+ - The sink signature database (`sinks/signatures.py`) is a plain, editable
201
+ Python dict -- add your own categories/functions freely.
202
+
203
+ ## Terminology (read this before trusting a finding)
204
+
205
+ SnikrFlow is deliberately precise about what different results mean --
206
+ these are never used interchangeably:
207
+
208
+ | Term | Meaning |
209
+ |---|---|
210
+ | `statically_reachable` | A direct call-graph path exists. Doesn't see indirect calls (function pointers, vtables). |
211
+ | `SAT` (solve) | A concrete candidate input exists that walks this exact path. Not a confirmed exploit. |
212
+ | `POSSIBLE` (dataflow) | Symbolic input appears to reach the sink's argument. Heuristic, not sound taint analysis. |
213
+ | Risk level (audit) | A prioritization signal. Never a confirmed vulnerability. |
214
+
215
+ ## Responsible use
216
+
217
+ SnikrFlow is for authorized security research only -- use it against binaries
218
+ you own or have explicit permission to analyze.
219
+
220
+ ## Roadmap
221
+
222
+ - [x] M1 -- ELF foundation (`info`, `functions`)
223
+ - [x] M2 -- Security triage (`jni`, `sinks`)
224
+ - [x] M3 -- Program structure (`cfg`, `callers`, `callees`)
225
+ - [x] M4 -- Reachability (`reach`)
226
+ - [x] M5 -- Symbolic execution (`solve`)
227
+ - [x] M6 -- Data flow (`dataflow`)
228
+ - [x] M7 -- Audit engine (`audit`)
229
+ - [ ] M8 -- Android APK support (`audit app.apk`)
230
+ - [ ] M9 -- Advanced analysis (better taint tracking, caching, SARIF)
231
+ - [ ] M10 -- GUI (presentation layer over the same engine, no duplicated logic)
232
+
233
+ ## Credits
234
+
235
+ Built on [angr](https://angr.io) and Claripy. CLI powered by
236
+ [Typer](https://typer.tiangolo.com) and [Rich](https://rich.readthedocs.io).
237
+
238
+ ## License
239
+
240
+ MIT -- see [LICENSE](LICENSE).
@@ -0,0 +1,187 @@
1
+ <p align="center">
2
+ <img src="logo.svg" width="140" alt="SnikrFlow logo"/>
3
+ </p>
4
+
5
+ <h1 align="center">SnikrFlow</h1>
6
+
7
+ <p align="center">
8
+ <b>Native binary security triage and reachability platform.</b><br/>
9
+ Stop wondering where to start reversing a binary. Let SnikrFlow tell you.
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="https://pypi.org/project/snikrflow/"><img alt="PyPI" src="https://img.shields.io/pypi/v/snikrflow.svg"></a>
14
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-orange.svg"></a>
15
+ <img alt="Python" src="https://img.shields.io/badge/python-3.11%2B-blue.svg">
16
+ </p>
17
+
18
+ ---
19
+
20
+ ## The problem
21
+
22
+ You've got a large ELF binary or an Android `.so` library. Thousands of functions.
23
+ No idea where the interesting stuff is. You could spend two hours blindly
24
+ following callers, references, and branches in Ghidra... or you could ask:
25
+
26
+ > "Before I spend two hours reversing this binary, tell me where I should
27
+ > spend those two hours."
28
+
29
+ That's what SnikrFlow answers.
30
+
31
+ SnikrFlow is **not** a replacement for Ghidra, IDA, Binary Ninja, or GDB. It's
32
+ the step *before* those tools: automated triage, reachability analysis, and
33
+ symbolic execution that narrows "here are thousands of functions" down to
34
+ "here are the paths you should investigate first."
35
+
36
+ ```
37
+ Large ELF / Android .so
38
+ │
39
+ ▼
40
+ SnikrFlow
41
+ │
42
+ ├── ELF analysis ├── Call graph analysis
43
+ ├── Function discovery ├── Reachability
44
+ ├── JNI discovery ├── Data-flow analysis
45
+ ├── Security sink detect └── Symbolic execution
46
+ ├── CFG analysis
47
+ │
48
+ ▼
49
+ Prioritized findings
50
+ │
51
+ ▼
52
+ Ghidra
53
+ │
54
+ ▼
55
+ Deep manual RE
56
+ ```
57
+
58
+ ## Install
59
+
60
+ ```bash
61
+ pip install snikrflow
62
+ ```
63
+
64
+ (Or, for development: clone the repo and `pip install -e .`)
65
+
66
+ Requires Python 3.11+. Built on [angr](https://angr.io) + Claripy for the
67
+ underlying static/symbolic analysis engine.
68
+
69
+ ## Quick start
70
+
71
+ ```bash
72
+ # The whole pipeline, orchestrated -- this is the one you actually want
73
+ snikrflow audit libfoo.so
74
+
75
+ # Or drill in stage by stage:
76
+ snikrflow info libfoo.so # ELF metadata
77
+ snikrflow functions libfoo.so # discovered functions (angr CFGFast + symbols)
78
+ snikrflow jni libfoo.so # statically-exported JNI entry points
79
+ snikrflow sinks libfoo.so # security-sensitive function calls
80
+ snikrflow cfg libfoo.so # control-flow graph summary
81
+ snikrflow callers libfoo.so 0x400d80 # who calls this address
82
+ snikrflow callees libfoo.so 0x400d80 # what this address calls
83
+ snikrflow reach libfoo.so --source 0x403a00 --sink 0x400d80 # static reachability
84
+ snikrflow solve libfoo.so --source 0x403a00 --target 0x400d80 # symbolic candidate input
85
+ snikrflow dataflow libfoo.so --source 0x403a00 --sink 0x400d80 # does tainted data reach the sink?
86
+ ```
87
+
88
+ Every command supports `--json` for scripting/piping into other tools.
89
+
90
+ ## What `audit` actually does
91
+
92
+ `audit` is the flagship command -- it runs the full pipeline automatically:
93
+
94
+ 1. Discovers all functions, JNI entry points, and security-sensitive sinks.
95
+ 2. Checks static (call-graph) reachability across every JNI-to-sink pair --
96
+ cheap, so it covers everything.
97
+ 3. Ranks the statically-reachable pairs by call-chain directness and picks
98
+ the most promising few for **deep analysis**.
99
+ 4. Runs data-flow (symbolic) analysis on that shortlist -- expensive, so
100
+ it's spent only where it matters.
101
+ 5. Scores risk transparently and prints ranked findings.
102
+
103
+ ```
104
+ SnikrFlow Security Audit
105
+ ═══════════════════════════
106
+ Binary
107
+ Architecture: x86_64
108
+ Type: Shared Object
109
+ Functions
110
+ 294 discovered
111
+ 3 JNI candidates
112
+ Security-sensitive operations
113
+ 2 identified
114
+ Interesting paths
115
+ 1 statically reachable
116
+ 1 selected for data-flow analysis
117
+ Findings
118
+ [MEDIUM] JNI -> memory
119
+ Source: Java_..._MainActivity_init @ 0x403a00
120
+ Sink: strncpy @ 0x400d80 (memory)
121
+ Status: statically_reachable
122
+ ```
123
+
124
+ Risk levels are a **prioritization aid, not a vulnerability verdict**. Manual
125
+ verification is still required -- SnikrFlow narrows the search space, it
126
+ doesn't replace the researcher.
127
+
128
+ ## Architecture
129
+
130
+ SnikrFlow is built in layers, deliberately kept independent from its analysis
131
+ backend:
132
+
133
+ ```
134
+ CLI ──┐
135
+ ├──> Core Engine ──> SnikrFlow Models ──> Analysis Interfaces ──> Backends
136
+ Future GUI ──┘ (angr today;
137
+ Unicorn/Pin/Dyninst
138
+ possible later)
139
+ ```
140
+
141
+ - **SnikrFlow owns the concepts** (`Function`, `SecuritySink`, `AnalysisPath`,
142
+ `SymbolicResult`, `AnalysisFinding`, ...). The CLI never touches angr
143
+ objects directly.
144
+ - **`analysis/interfaces/`** define *what* SnikrFlow can do.
145
+ **`analysis/backends/angr/`** define *how* angr does it today. Adding a
146
+ future backend means writing a new backend module, not rewriting the CLI.
147
+ - The sink signature database (`sinks/signatures.py`) is a plain, editable
148
+ Python dict -- add your own categories/functions freely.
149
+
150
+ ## Terminology (read this before trusting a finding)
151
+
152
+ SnikrFlow is deliberately precise about what different results mean --
153
+ these are never used interchangeably:
154
+
155
+ | Term | Meaning |
156
+ |---|---|
157
+ | `statically_reachable` | A direct call-graph path exists. Doesn't see indirect calls (function pointers, vtables). |
158
+ | `SAT` (solve) | A concrete candidate input exists that walks this exact path. Not a confirmed exploit. |
159
+ | `POSSIBLE` (dataflow) | Symbolic input appears to reach the sink's argument. Heuristic, not sound taint analysis. |
160
+ | Risk level (audit) | A prioritization signal. Never a confirmed vulnerability. |
161
+
162
+ ## Responsible use
163
+
164
+ SnikrFlow is for authorized security research only -- use it against binaries
165
+ you own or have explicit permission to analyze.
166
+
167
+ ## Roadmap
168
+
169
+ - [x] M1 -- ELF foundation (`info`, `functions`)
170
+ - [x] M2 -- Security triage (`jni`, `sinks`)
171
+ - [x] M3 -- Program structure (`cfg`, `callers`, `callees`)
172
+ - [x] M4 -- Reachability (`reach`)
173
+ - [x] M5 -- Symbolic execution (`solve`)
174
+ - [x] M6 -- Data flow (`dataflow`)
175
+ - [x] M7 -- Audit engine (`audit`)
176
+ - [ ] M8 -- Android APK support (`audit app.apk`)
177
+ - [ ] M9 -- Advanced analysis (better taint tracking, caching, SARIF)
178
+ - [ ] M10 -- GUI (presentation layer over the same engine, no duplicated logic)
179
+
180
+ ## Credits
181
+
182
+ Built on [angr](https://angr.io) and Claripy. CLI powered by
183
+ [Typer](https://typer.tiangolo.com) and [Rich](https://rich.readthedocs.io).
184
+
185
+ ## License
186
+
187
+ MIT -- see [LICENSE](LICENSE).
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "snikrflow"
7
+ version = "0.1.0"
8
+ description = "Native binary security triage and reachability platform, built on angr and Claripy."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { file = "LICENSE" }
12
+ authors = [
13
+ { name = "Stodachon" }
14
+ ]
15
+ keywords = [
16
+ "security", "reverse-engineering", "android", "jni", "binary-analysis",
17
+ "angr", "symbolic-execution", "triage", "reachability", "elf",
18
+ ]
19
+ classifiers = [
20
+ "Development Status :: 3 - Alpha",
21
+ "Environment :: Console",
22
+ "Intended Audience :: Information Technology",
23
+ "License :: OSI Approved :: MIT License",
24
+ "Operating System :: OS Independent",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Topic :: Security",
28
+ "Topic :: Software Development :: Disassemblers",
29
+ ]
30
+ dependencies = [
31
+ "angr",
32
+ "claripy",
33
+ "pyelftools",
34
+ "typer[all]",
35
+ "rich",
36
+ "networkx",
37
+ ]
38
+
39
+ [project.optional-dependencies]
40
+ dev = ["pytest"]
41
+
42
+ [project.urls]
43
+ Homepage = "https://github.com/stodachon/snikrflow"
44
+ Repository = "https://github.com/stodachon/snikrflow"
45
+ Issues = "https://github.com/stodachon/snikrflow/issues"
46
+
47
+ [project.scripts]
48
+ snikrflow = "snikrflow.cli:app"
49
+
50
+ [tool.setuptools.packages.find]
51
+ include = ["snikrflow*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
File without changes
@@ -0,0 +1,145 @@
1
+ """
2
+ SnikrFlow angr backend -- audit orchestration.
3
+
4
+ Combines every prior analysis stage into one pipeline, per the doc's
5
+ performance philosophy: cheap triage on everything, expensive symbolic/
6
+ data-flow analysis only on the most promising candidates.
7
+
8
+ Pipeline:
9
+ 1. Discover functions, JNI candidates, sinks (cheap -- one CFGFast run,
10
+ reused across all three thanks to the caching guard in functions.py).
11
+ 2. For every (JNI entry, sink) pair, check STATIC call-graph
12
+ reachability (cheap -- no symbolic execution).
13
+ 3. Rank the statically-reachable pairs by call-chain length (shorter =
14
+ more direct = higher priority) and take the top N for deep analysis.
15
+ 4. Run data-flow analysis (symbolic, bounded by a short per-pair
16
+ timeout) ONLY on that top-N subset.
17
+ 5. Score risk transparently (see _score_risk) and sort findings by risk.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ from typing import Optional
23
+
24
+ from snikrflow.analysis.backends.angr.dataflow import check_data_flow
25
+ from snikrflow.analysis.backends.angr.functions import discover_functions
26
+ from snikrflow.analysis.backends.angr.jni import discover_jni_functions
27
+ from snikrflow.analysis.backends.angr.reachability import check_static_reachability
28
+ from snikrflow.analysis.backends.angr.sinks import discover_sinks
29
+ from snikrflow.core.models import (
30
+ AnalysisFinding,
31
+ AuditResult,
32
+ DataFlowStatus,
33
+ ReachabilityType,
34
+ RiskLevel,
35
+ )
36
+ from snikrflow.elf.loader import LoadedBinary
37
+ from snikrflow.elf.metadata import extract_binary_info
38
+
39
+ # Cap on how many statically-reachable pairs get the expensive data-flow
40
+ # treatment. Keeps `audit` bounded on binaries with many JNI x sink
41
+ # combinations -- deep analysis is a scarce resource, spent on the most
42
+ # direct (shortest) paths first.
43
+ MAX_DEEP_ANALYSIS_PAIRS = 5
44
+ DEFAULT_DEEP_TIMEOUT_SECONDS = 30
45
+
46
+ # Rough, documented, and DELIBERATELY simple severity weighting per sink
47
+ # category. This is a transparent prioritization heuristic, not a
48
+ # vulnerability database -- edit freely.
49
+ _CATEGORY_SEVERITY = {
50
+ "process_execution": 3,
51
+ "dynamic_loading": 3,
52
+ "memory": 2,
53
+ "networking": 2,
54
+ "filesystem": 1,
55
+ }
56
+
57
+
58
+ def _score_risk(
59
+ reachability_type: ReachabilityType,
60
+ data_flow_status: Optional[DataFlowStatus],
61
+ category: str,
62
+ ) -> RiskLevel:
63
+ severity = _CATEGORY_SEVERITY.get(category, 1)
64
+
65
+ if reachability_type != ReachabilityType.STATICALLY_REACHABLE:
66
+ return RiskLevel.LOW
67
+
68
+ if data_flow_status == DataFlowStatus.POSSIBLE:
69
+ return RiskLevel.CRITICAL if severity >= 3 else RiskLevel.HIGH
70
+
71
+ # Reachable via call graph, but data flow wasn't confirmed possible
72
+ # (either not checked, or checked and came back inconclusive).
73
+ return RiskLevel.HIGH if severity >= 3 else RiskLevel.MEDIUM
74
+
75
+
76
+ def run_audit(binary: LoadedBinary, deep_timeout_seconds: int = DEFAULT_DEEP_TIMEOUT_SECONDS) -> AuditResult:
77
+ binary_info = extract_binary_info(binary)
78
+
79
+ all_functions = discover_functions(binary) # runs CFGFast once
80
+ jni_functions = discover_jni_functions(binary) # reuses it
81
+ sinks = discover_sinks(binary) # reuses it
82
+
83
+ # Step 1: cheap static reachability across every JNI x sink pair.
84
+ reachable_pairs = []
85
+ for jni_func in jni_functions:
86
+ for sink in sinks:
87
+ path = check_static_reachability(binary, jni_func.address, sink.address)
88
+ if path.reachable and path.reachability_type == ReachabilityType.STATICALLY_REACHABLE:
89
+ reachable_pairs.append((jni_func, sink, path))
90
+
91
+ # Step 2: prioritize shortest (most direct) call chains for deep analysis.
92
+ reachable_pairs.sort(key=lambda item: len(item[2].function_path))
93
+ deep_pairs = reachable_pairs[:MAX_DEEP_ANALYSIS_PAIRS]
94
+ deep_keys = {(j.address, s.address) for j, s, _ in deep_pairs}
95
+
96
+ # Step 3: build findings, running data-flow only on the selected subset.
97
+ findings: list[AnalysisFinding] = []
98
+ for jni_func, sink, path in reachable_pairs:
99
+ data_flow_status: Optional[DataFlowStatus] = None
100
+ confidence = path.confidence
101
+ notes: list[str] = [path.note] if path.note else []
102
+
103
+ if (jni_func.address, sink.address) in deep_keys:
104
+ try:
105
+ df_result = check_data_flow(
106
+ binary, jni_func.address, sink.address,
107
+ timeout_seconds=deep_timeout_seconds,
108
+ )
109
+ data_flow_status = df_result.status
110
+ confidence = df_result.confidence
111
+ if df_result.note:
112
+ notes.append(df_result.note)
113
+ except Exception as e:
114
+ notes.append(f"Data-flow analysis failed: {e}")
115
+
116
+ risk = _score_risk(path.reachability_type, data_flow_status, sink.category)
117
+
118
+ findings.append(
119
+ AnalysisFinding(
120
+ title=f"JNI -> {sink.category}",
121
+ source_address=jni_func.address,
122
+ source_name=jni_func.name,
123
+ sink_address=sink.address,
124
+ sink_name=sink.name,
125
+ sink_category=sink.category,
126
+ reachability_type=path.reachability_type,
127
+ data_flow_status=data_flow_status,
128
+ risk_level=risk,
129
+ confidence=confidence,
130
+ notes=notes,
131
+ )
132
+ )
133
+
134
+ risk_order = {RiskLevel.CRITICAL: 0, RiskLevel.HIGH: 1, RiskLevel.MEDIUM: 2, RiskLevel.LOW: 3}
135
+ findings.sort(key=lambda f: risk_order.get(f.risk_level, 4))
136
+
137
+ return AuditResult(
138
+ binary_info=binary_info,
139
+ function_count=len(all_functions),
140
+ jni_count=len(jni_functions),
141
+ sink_count=len(sinks),
142
+ interesting_path_count=len(reachable_pairs),
143
+ symbolic_analysis_count=len(deep_pairs),
144
+ findings=findings,
145
+ )