lure-analyze 0.2.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.
- lure_analyze-0.2.0/LICENSE +21 -0
- lure_analyze-0.2.0/PKG-INFO +159 -0
- lure_analyze-0.2.0/README.md +132 -0
- lure_analyze-0.2.0/lure/__init__.py +12 -0
- lure_analyze-0.2.0/lure/diff.py +123 -0
- lure_analyze-0.2.0/lure/inspector.py +575 -0
- lure_analyze-0.2.0/lure/main.py +138 -0
- lure_analyze-0.2.0/lure/runner.py +1035 -0
- lure_analyze-0.2.0/lure_analyze.egg-info/PKG-INFO +159 -0
- lure_analyze-0.2.0/lure_analyze.egg-info/SOURCES.txt +14 -0
- lure_analyze-0.2.0/lure_analyze.egg-info/dependency_links.txt +1 -0
- lure_analyze-0.2.0/lure_analyze.egg-info/entry_points.txt +2 -0
- lure_analyze-0.2.0/lure_analyze.egg-info/requires.txt +3 -0
- lure_analyze-0.2.0/lure_analyze.egg-info/top_level.txt +1 -0
- lure_analyze-0.2.0/pyproject.toml +56 -0
- lure_analyze-0.2.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 0xusmanismail
|
|
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,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lure-analyze
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Local Linux binary analysis tool for security researchers and CTF players
|
|
5
|
+
Author-email: Usman Ismail <0xusmanismail@proton.me>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/0xusmanismail/lure
|
|
8
|
+
Project-URL: Repository, https://github.com/0xusmanismail/lure
|
|
9
|
+
Project-URL: Issues, https://github.com/0xusmanismail/lure/issues
|
|
10
|
+
Keywords: security,malware,sandbox,linux,binary-analysis,strace,elf,ctf,reverse-engineering
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Information Technology
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Topic :: Security
|
|
19
|
+
Classifier: Topic :: System :: Monitoring
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: click>=8.1
|
|
24
|
+
Requires-Dist: rich>=13.7
|
|
25
|
+
Requires-Dist: pyelftools>=0.30
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# lure
|
|
29
|
+
|
|
30
|
+
> Local Linux binary analysis. Zero cloud. Zero root. Zero cost.
|
|
31
|
+
|
|
32
|
+
⚠️ **Early development (v0.2.0).** Core features (`inspect`, `run`,
|
|
33
|
+
`diff`) work end to end on x86_64 Linux. This is a young project —
|
|
34
|
+
expect rough edges, limited error handling on unusual inputs, and
|
|
35
|
+
missing features. Bug reports, feedback, and contributions are very
|
|
36
|
+
welcome. Network isolation is enforced. Filesystem isolation is
|
|
37
|
+
not — run inside a VM when analyzing untrusted samples.
|
|
38
|
+
|
|
39
|
+

|
|
40
|
+
|
|
41
|
+
## What it does
|
|
42
|
+
|
|
43
|
+
Lure runs an untrusted Linux binary in a lightweight monitoring
|
|
44
|
+
environment (user + network namespaces + strace) and tells you
|
|
45
|
+
exactly what it did — which files it touched, what network
|
|
46
|
+
connections it tried, what processes it spawned — then gives you a
|
|
47
|
+
plain verdict: **CLEAN**, **SUSPICIOUS**, or **DANGEROUS**.
|
|
48
|
+
|
|
49
|
+
Everything happens on your machine. Nothing is uploaded anywhere.
|
|
50
|
+
|
|
51
|
+
## Why
|
|
52
|
+
|
|
53
|
+
- **Privacy** — sensitive or client samples never leave your machine
|
|
54
|
+
- **Zero setup** — no VM, no Docker, no Cuckoo install process
|
|
55
|
+
- **Readable** — structured reports instead of raw strace noise
|
|
56
|
+
- **Free** — MIT licensed, runs on tools already on Kali Linux
|
|
57
|
+
|
|
58
|
+
## Isolation model
|
|
59
|
+
|
|
60
|
+
Lure uses Linux user and network namespaces to prevent the binary
|
|
61
|
+
from making outbound network connections. The host filesystem
|
|
62
|
+
remains visible to the analyzed binary. For stronger isolation
|
|
63
|
+
(mount namespace, seccomp, cgroups), run Lure inside a VM or
|
|
64
|
+
container. Lure is primarily a behavioral observation tool, not a
|
|
65
|
+
hardened sandbox.
|
|
66
|
+
|
|
67
|
+
## Install
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
git clone https://github.com/0xusmanismail/lure.git
|
|
71
|
+
cd lure
|
|
72
|
+
pip install -e . --break-system-packages
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The `--break-system-packages` flag is required on Arch Linux and on
|
|
76
|
+
recent Debian/Ubuntu releases, which restrict installing into the
|
|
77
|
+
system Python environment by default (PEP 668).
|
|
78
|
+
|
|
79
|
+
Requires `strace` and `unshare` installed.
|
|
80
|
+
|
|
81
|
+
## Usage
|
|
82
|
+
|
|
83
|
+
### Inspect a binary
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
lure inspect /bin/ls
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Reads ELF headers, architecture, security mitigations, linked libraries, and file hashes — without executing a single byte of code.
|
|
90
|
+
|
|
91
|
+

|
|
92
|
+
|
|
93
|
+
### Run a binary in the sandbox
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
lure run ./suspicious_binary
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Live feed of file access, network attempts, and spawned processes, followed by a full behavioral report.
|
|
100
|
+
|
|
101
|
+

|
|
102
|
+
|
|
103
|
+

|
|
104
|
+
|
|
105
|
+
### Catch suspicious behavior
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
lure run ./demo_dangerous
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Sensitive file access combined with network activity trips a DANGEROUS verdict, with the exact triggers listed.
|
|
112
|
+
|
|
113
|
+

|
|
114
|
+
|
|
115
|
+

|
|
116
|
+
|
|
117
|
+

|
|
118
|
+
|
|
119
|
+
### Compare two runs with lure diff
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
lure run --save /bin/ls
|
|
123
|
+
lure run --save /bin/echo
|
|
124
|
+
lure diff report1.json report2.json
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Shows new/removed files, new connections, verdict changes, and syscall count differences between two saved runs.
|
|
128
|
+
|
|
129
|
+

|
|
130
|
+
|
|
131
|
+
### Save a report
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
lure run --save ./binary
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Saves the full report to `~/.lure/reports/` as both a plain-text `.txt` file and a structured `.json` file.
|
|
138
|
+
|
|
139
|
+
## Status & Roadmap
|
|
140
|
+
|
|
141
|
+
**Working now:**
|
|
142
|
+
- ELF inspection with security mitigation detection
|
|
143
|
+
- Sandboxed execution via `unshare` + `strace`
|
|
144
|
+
- Live event feed during execution
|
|
145
|
+
- Full behavioral report with CLEAN/SUSPICIOUS/DANGEROUS verdict
|
|
146
|
+
- Verdict shows exact triggering files and IPs
|
|
147
|
+
- Report saving (plain text + JSON)
|
|
148
|
+
- Report comparison via `lure diff`
|
|
149
|
+
- Non-ELF file detection with clean error messages
|
|
150
|
+
- Works on Arch Linux, Kali, Debian, Ubuntu
|
|
151
|
+
|
|
152
|
+
**Planned:**
|
|
153
|
+
- Demo GIF showing live execution
|
|
154
|
+
- PyPI package (`pip install lure-analyze`)
|
|
155
|
+
- Packaged releases
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
MIT — see [LICENSE](LICENSE)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# lure
|
|
2
|
+
|
|
3
|
+
> Local Linux binary analysis. Zero cloud. Zero root. Zero cost.
|
|
4
|
+
|
|
5
|
+
⚠️ **Early development (v0.2.0).** Core features (`inspect`, `run`,
|
|
6
|
+
`diff`) work end to end on x86_64 Linux. This is a young project —
|
|
7
|
+
expect rough edges, limited error handling on unusual inputs, and
|
|
8
|
+
missing features. Bug reports, feedback, and contributions are very
|
|
9
|
+
welcome. Network isolation is enforced. Filesystem isolation is
|
|
10
|
+
not — run inside a VM when analyzing untrusted samples.
|
|
11
|
+
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
## What it does
|
|
15
|
+
|
|
16
|
+
Lure runs an untrusted Linux binary in a lightweight monitoring
|
|
17
|
+
environment (user + network namespaces + strace) and tells you
|
|
18
|
+
exactly what it did — which files it touched, what network
|
|
19
|
+
connections it tried, what processes it spawned — then gives you a
|
|
20
|
+
plain verdict: **CLEAN**, **SUSPICIOUS**, or **DANGEROUS**.
|
|
21
|
+
|
|
22
|
+
Everything happens on your machine. Nothing is uploaded anywhere.
|
|
23
|
+
|
|
24
|
+
## Why
|
|
25
|
+
|
|
26
|
+
- **Privacy** — sensitive or client samples never leave your machine
|
|
27
|
+
- **Zero setup** — no VM, no Docker, no Cuckoo install process
|
|
28
|
+
- **Readable** — structured reports instead of raw strace noise
|
|
29
|
+
- **Free** — MIT licensed, runs on tools already on Kali Linux
|
|
30
|
+
|
|
31
|
+
## Isolation model
|
|
32
|
+
|
|
33
|
+
Lure uses Linux user and network namespaces to prevent the binary
|
|
34
|
+
from making outbound network connections. The host filesystem
|
|
35
|
+
remains visible to the analyzed binary. For stronger isolation
|
|
36
|
+
(mount namespace, seccomp, cgroups), run Lure inside a VM or
|
|
37
|
+
container. Lure is primarily a behavioral observation tool, not a
|
|
38
|
+
hardened sandbox.
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
git clone https://github.com/0xusmanismail/lure.git
|
|
44
|
+
cd lure
|
|
45
|
+
pip install -e . --break-system-packages
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The `--break-system-packages` flag is required on Arch Linux and on
|
|
49
|
+
recent Debian/Ubuntu releases, which restrict installing into the
|
|
50
|
+
system Python environment by default (PEP 668).
|
|
51
|
+
|
|
52
|
+
Requires `strace` and `unshare` installed.
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
### Inspect a binary
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
lure inspect /bin/ls
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Reads ELF headers, architecture, security mitigations, linked libraries, and file hashes — without executing a single byte of code.
|
|
63
|
+
|
|
64
|
+

|
|
65
|
+
|
|
66
|
+
### Run a binary in the sandbox
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
lure run ./suspicious_binary
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Live feed of file access, network attempts, and spawned processes, followed by a full behavioral report.
|
|
73
|
+
|
|
74
|
+

|
|
75
|
+
|
|
76
|
+

|
|
77
|
+
|
|
78
|
+
### Catch suspicious behavior
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
lure run ./demo_dangerous
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Sensitive file access combined with network activity trips a DANGEROUS verdict, with the exact triggers listed.
|
|
85
|
+
|
|
86
|
+

|
|
87
|
+
|
|
88
|
+

|
|
89
|
+
|
|
90
|
+

|
|
91
|
+
|
|
92
|
+
### Compare two runs with lure diff
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
lure run --save /bin/ls
|
|
96
|
+
lure run --save /bin/echo
|
|
97
|
+
lure diff report1.json report2.json
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Shows new/removed files, new connections, verdict changes, and syscall count differences between two saved runs.
|
|
101
|
+
|
|
102
|
+

|
|
103
|
+
|
|
104
|
+
### Save a report
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
lure run --save ./binary
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Saves the full report to `~/.lure/reports/` as both a plain-text `.txt` file and a structured `.json` file.
|
|
111
|
+
|
|
112
|
+
## Status & Roadmap
|
|
113
|
+
|
|
114
|
+
**Working now:**
|
|
115
|
+
- ELF inspection with security mitigation detection
|
|
116
|
+
- Sandboxed execution via `unshare` + `strace`
|
|
117
|
+
- Live event feed during execution
|
|
118
|
+
- Full behavioral report with CLEAN/SUSPICIOUS/DANGEROUS verdict
|
|
119
|
+
- Verdict shows exact triggering files and IPs
|
|
120
|
+
- Report saving (plain text + JSON)
|
|
121
|
+
- Report comparison via `lure diff`
|
|
122
|
+
- Non-ELF file detection with clean error messages
|
|
123
|
+
- Works on Arch Linux, Kali, Debian, Ubuntu
|
|
124
|
+
|
|
125
|
+
**Planned:**
|
|
126
|
+
- Demo GIF showing live execution
|
|
127
|
+
- PyPI package (`pip install lure-analyze`)
|
|
128
|
+
- Packaged releases
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
MIT — see [LICENSE](LICENSE)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# FILE: lure/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
Lure — local Linux binary analysis tool.
|
|
4
|
+
|
|
5
|
+
Runs untrusted ELF binaries and shows exactly what they did.
|
|
6
|
+
Files accessed. Network attempts. Processes spawned.
|
|
7
|
+
Zero cloud upload. Zero root. Zero cost.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
__version__ = "0.2.0"
|
|
11
|
+
__author__ = "Lure"
|
|
12
|
+
__license__ = "MIT"
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# FILE: lure/diff.py
|
|
2
|
+
"""
|
|
3
|
+
Report comparison engine for 'lure diff'.
|
|
4
|
+
Compares two JSON reports produced by 'lure run --save' and shows
|
|
5
|
+
what changed between the two runs.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.panel import Panel
|
|
13
|
+
from rich.text import Text
|
|
14
|
+
from rich import box
|
|
15
|
+
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ── Loading ───────────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
def _load_report(path):
|
|
22
|
+
"""Load and parse a JSON report. Returns (data, error_message)."""
|
|
23
|
+
p = Path(path)
|
|
24
|
+
if not p.exists():
|
|
25
|
+
return None, f'file not found: {path}'
|
|
26
|
+
try:
|
|
27
|
+
with open(p, 'r') as f:
|
|
28
|
+
data = json.load(f)
|
|
29
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
30
|
+
return None, f'invalid JSON report — {exc}'
|
|
31
|
+
return data, None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# ── Panels ────────────────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
def _list_panel(title, items, style):
|
|
37
|
+
if not items:
|
|
38
|
+
body = Text(' (none)', style='dim italic')
|
|
39
|
+
else:
|
|
40
|
+
body = Text()
|
|
41
|
+
for item in items:
|
|
42
|
+
body.append(f' • {item}\n', style=style)
|
|
43
|
+
return Panel(
|
|
44
|
+
body,
|
|
45
|
+
title=f'[bold]{title}[/bold] ({len(items)})',
|
|
46
|
+
border_style='dim white',
|
|
47
|
+
box=box.ROUNDED,
|
|
48
|
+
padding=(0, 1),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# ── Main entry point ──────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
def run_diff(report1_path, report2_path):
|
|
55
|
+
"""Full report comparison — called by the 'lure diff' command."""
|
|
56
|
+
|
|
57
|
+
r1, err1 = _load_report(report1_path)
|
|
58
|
+
if err1:
|
|
59
|
+
console.print(f'[red]Error:[/red] {report1_path} — {err1}')
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
r2, err2 = _load_report(report2_path)
|
|
63
|
+
if err2:
|
|
64
|
+
console.print(f'[red]Error:[/red] {report2_path} — {err2}')
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
console.print(Panel.fit(
|
|
68
|
+
f'[bold magenta]DIFF[/bold magenta] '
|
|
69
|
+
f'[white]{Path(report1_path).name}[/white] '
|
|
70
|
+
f'[dim]→[/dim] [white]{Path(report2_path).name}[/white]',
|
|
71
|
+
border_style='magenta',
|
|
72
|
+
box=box.ROUNDED,
|
|
73
|
+
))
|
|
74
|
+
|
|
75
|
+
# ── Files ──────────────────────────────────────────────────────────────────
|
|
76
|
+
files1 = set(r1.get('files_accessed', []))
|
|
77
|
+
files2 = set(r2.get('files_accessed', []))
|
|
78
|
+
new_files = sorted(files2 - files1)
|
|
79
|
+
removed_files = sorted(files1 - files2)
|
|
80
|
+
|
|
81
|
+
console.print(_list_panel('New Files', new_files, 'green'))
|
|
82
|
+
console.print(_list_panel('Removed Files', removed_files, 'red'))
|
|
83
|
+
|
|
84
|
+
# ── Network ────────────────────────────────────────────────────────────────
|
|
85
|
+
net1 = {(n['ip'], n['port']) for n in r1.get('network_attempts', [])}
|
|
86
|
+
net2 = {(n['ip'], n['port']) for n in r2.get('network_attempts', [])}
|
|
87
|
+
new_conns = sorted(f'{ip}:{port}' for ip, port in (net2 - net1))
|
|
88
|
+
|
|
89
|
+
console.print(_list_panel('New Connections', new_conns, 'cyan'))
|
|
90
|
+
|
|
91
|
+
# ── Verdict ────────────────────────────────────────────────────────────────
|
|
92
|
+
verdict1 = r1.get('verdict', '?')
|
|
93
|
+
verdict2 = r2.get('verdict', '?')
|
|
94
|
+
|
|
95
|
+
if verdict1 != verdict2:
|
|
96
|
+
verdict_text = Text.assemble(
|
|
97
|
+
(verdict1, 'bold'), (' → ', 'dim'), (verdict2, 'bold'),
|
|
98
|
+
)
|
|
99
|
+
else:
|
|
100
|
+
verdict_text = Text.assemble((verdict2, 'bold'), (' (unchanged)', 'dim'))
|
|
101
|
+
|
|
102
|
+
console.print(Panel(
|
|
103
|
+
verdict_text,
|
|
104
|
+
title='[bold]Verdict[/bold]',
|
|
105
|
+
border_style='dim white',
|
|
106
|
+
box=box.ROUNDED,
|
|
107
|
+
padding=(0, 1),
|
|
108
|
+
))
|
|
109
|
+
|
|
110
|
+
# ── Syscalls ───────────────────────────────────────────────────────────────
|
|
111
|
+
sc1 = r1.get('syscall_total', 0)
|
|
112
|
+
sc2 = r2.get('syscall_total', 0)
|
|
113
|
+
delta = sc2 - sc1
|
|
114
|
+
sign = '+' if delta >= 0 else ''
|
|
115
|
+
|
|
116
|
+
console.print(Panel(
|
|
117
|
+
Text(f' {sign}{delta} syscalls ({sc1} → {sc2})'),
|
|
118
|
+
title='[bold]Syscall Count[/bold]',
|
|
119
|
+
border_style='dim white',
|
|
120
|
+
box=box.ROUNDED,
|
|
121
|
+
padding=(0, 1),
|
|
122
|
+
))
|
|
123
|
+
console.print()
|