Files
2026-10-06 21:08:12 +03:00

1019 lines
18 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Работа с capture_sessions.
Модуль предоставляет функции для получения
информации о поездках (захватах).
Используется:
dashboard.py
session_page.py
generator.py
"""
from pathlib import Path
from .config import (
CAPTURES_DIR,
)
from .database import (
query_all,
query_one,
query_value,
)
from datetime import datetime
from .utils import (
format_duration,
format_size,
)
# ============================================================
# Список всех поездок
# ============================================================
def get_sessions(conn):
sql = """
SELECT
id,
start_time,
end_time,
capture_file
FROM capture_sessions
ORDER BY
start_time DESC
"""
return query_all(
conn,
sql
)
# ============================================================
# Получить одну поездку
# ============================================================
def get_session(
conn,
session_id
):
sql = """
SELECT
id,
start_time,
end_time,
capture_file
FROM capture_sessions
WHERE id=?
"""
return query_one(
conn,
sql,
(
session_id,
)
)
# ============================================================
# Продолжительность
# ============================================================
def session_duration(
conn,
session_id
):
row = query_one(
conn,
"""
SELECT
start_time,
end_time
FROM capture_sessions
WHERE id=?
""",
(
session_id,
)
)
if row is None:
return None
start = row[0]
end = row[1]
return format_duration(
start,
end
)
# ============================================================
# Реальное время поездки (GPS)
# ============================================================
def trip_time(
conn,
session_id,
):
row = query_one(
conn,
"""
SELECT
MIN(timestamp),
MAX(timestamp)
FROM gps_track
WHERE session_id=?
""",
(
session_id,
)
)
if row is None:
return (
None,
None,
)
return (
row[0],
row[1],
)
# ============================================================
# Количество точек доступа в поездке
# ============================================================
def access_points_count(
conn,
session_id
):
return query_value(
conn,
"""
SELECT COUNT(DISTINCT access_point_id)
FROM access_point_observations
WHERE session_id=?
""",
(
session_id,
),
default=0
)
# ============================================================
# Handshake AP (уникальные точки)
# ============================================================
def handshakes_count(
conn,
session_id
):
return query_value(
conn,
"""
SELECT COUNT(DISTINCT access_point_id)
FROM handshakes
WHERE session_id=?
""",
(
session_id,
),
default=0
)
# ============================================================
# Handshake Records (все записи)
# ============================================================
def handshake_records_count(
conn,
session_id
):
return query_value(
conn,
"""
SELECT COUNT(*)
FROM handshakes
WHERE session_id=?
""",
(
session_id,
),
default=0
)
# ============================================================
# Найденные пароли
# ============================================================
def credentials_count(
conn,
session_id
):
return query_value(
conn,
"""
SELECT COUNT(DISTINCT c.id)
FROM credentials c
JOIN access_points ap
ON ap.id = c.access_point_id
JOIN access_point_observations obs
ON obs.access_point_id = ap.id
WHERE obs.session_id = ?
""",
(
session_id,
),
default=0
)
# ============================================================
# PMKID
# ============================================================
def pmkid_count(
conn,
session_id
):
return query_value(
conn,
"""
SELECT COUNT(DISTINCT ap.id)
FROM access_points ap
JOIN access_point_observations obs
ON obs.access_point_id = ap.id
WHERE
obs.session_id = ?
AND ap.has_pmkid = 1
""",
(
session_id,
),
default=0
)
# ============================================================
# Взломанные сети
# ============================================================
def cracked_count(
conn,
session_id
):
return query_value(
conn,
"""
SELECT COUNT(DISTINCT ap.id)
FROM access_points ap
JOIN access_point_observations obs
ON obs.access_point_id = ap.id
WHERE
obs.session_id = ?
AND ap.is_cracked = 1
""",
(
session_id,
),
default=0
)
def coordinates_restored_count(
conn,
session_id
):
return query_value(
conn,
"""
SELECT COUNT(*)
FROM
(
SELECT
current_obs.access_point_id
FROM access_point_observations AS current_obs
WHERE
current_obs.session_id = ?
AND current_obs.latitude IS NOT NULL
AND current_obs.longitude IS NOT NULL
AND current_obs.latitude != 0
AND current_obs.longitude != 0
AND EXISTS
(
SELECT 1
FROM access_point_observations AS previous_obs
WHERE
previous_obs.access_point_id =
current_obs.access_point_id
AND previous_obs.observed_at <
(
SELECT start_time
FROM capture_sessions
WHERE id=?
)
)
AND NOT EXISTS
(
SELECT 1
FROM access_point_observations AS previous_coords
WHERE
previous_coords.access_point_id =
current_obs.access_point_id
AND previous_coords.observed_at <
(
SELECT start_time
FROM capture_sessions
WHERE id=?
)
AND previous_coords.latitude IS NOT NULL
AND previous_coords.longitude IS NOT NULL
AND previous_coords.latitude != 0
AND previous_coords.longitude != 0
)
GROUP BY current_obs.access_point_id
)
""",
(
session_id,
session_id,
session_id
),
default=0
)
def ap_without_coordinates_count(
conn,
session_id
):
return query_value(
conn,
"""
SELECT COUNT(*)
FROM
(
SELECT
obs.access_point_id
FROM access_point_observations AS obs
WHERE
obs.session_id = ?
GROUP BY obs.access_point_id
HAVING
SUM(
CASE
WHEN obs.latitude IS NOT NULL
AND obs.longitude IS NOT NULL
AND obs.latitude != 0
AND obs.longitude != 0
THEN 1
ELSE 0
END
) = 0
)
""",
(
session_id,
),
default=0
)
# ============================================================
# Полная статистика поездки
# ============================================================
def session_statistics(
conn,
session_id
):
row = get_session(
conn,
session_id
)
if row is None:
return None
(
ident,
start_time,
end_time,
capture_file,
) = row
return {
"id": ident,
"start_time": start_time,
"end_time": end_time,
"duration": session_duration(
conn,
session_id
),
"access_points": access_points_count(
conn,
session_id
),
"coordinates_restored": coordinates_restored_count(
conn,
session_id
),
"ap_without_coordinates": ap_without_coordinates_count(
conn,
session_id
),
"handshakes": handshakes_count(
conn,
session_id
),
"handshake_records": handshake_records_count(
conn,
session_id
),
"credentials": credentials_count(
conn,
session_id
),
"pmkid": pmkid_count(
conn,
session_id
),
"cracked": cracked_count(
conn,
session_id
),
"capture_file": capture_file,
}
# ============================================================
# Каталог поездки
# ============================================================
def session_directory(capture_file):
if capture_file is None:
return None
capture_path = Path(capture_file)
if capture_path.parent.exists():
return capture_path.parent
return CAPTURES_DIR / capture_path.parent.name
# ============================================================
# Проверка существования файла
# ============================================================
def file_exists(path):
if path is None:
return False
return Path(path).exists()
# ============================================================
# Получить все файлы поездки
# ============================================================
def session_files(
conn,
session_id
):
row = get_session(
conn,
session_id
)
if row is None:
return None
directory = session_directory(
row[3]
)
files = {
"directory": directory,
"capture": directory / "capture.pcapng",
"gps_track": directory / "gps_track.nmea",
"parser_log": directory / "parser.log",
"access_points": directory / "access_points.csv",
"handshakes": directory / "handshakes.22000",
"essid": directory / "essid.txt",
}
return files
# ============================================================
# Файлы, реально существующие на диске
# ============================================================
def existing_files(
conn,
session_id
):
files = session_files(
conn,
session_id
)
if files is None:
return {}
result = {}
for name, path in files.items():
if name == "directory":
result[name] = path
continue
result[name] = {
"path": path,
"exists": file_exists(path),
"size": path.stat().st_size if file_exists(path) else 0,
}
return result
# ============================================================
# Размер каталога поездки
# ============================================================
def session_directory_size(
conn,
session_id
):
files = session_files(
conn,
session_id
)
if files is None:
return 0
directory = files["directory"]
if not directory.exists():
return 0
total = 0
for file in directory.iterdir():
if file.is_file():
total += file.stat().st_size
return total
# ============================================================
# Последние поездки
# ============================================================
def recent_sessions(
conn,
limit=10
):
sql = """
SELECT
id
FROM capture_sessions
ORDER BY
start_time DESC
LIMIT ?
"""
rows = conn.execute(
sql,
(
limit,
)
).fetchall()
result = []
for row in rows:
card = session_card_data(
conn,
row[0]
)
if card:
result.append(card)
return result
# ============================================================
# Самая длинная поездка
# ============================================================
def longest_session(conn):
sql = """
SELECT
id,
start_time,
end_time
FROM capture_sessions
WHERE end_time IS NOT NULL
"""
rows = conn.execute(sql).fetchall()
winner = None
seconds = -1
for ident, start, end in rows:
try:
s = datetime.fromisoformat(start)
e = datetime.fromisoformat(end)
except Exception:
continue
diff = (e - s).total_seconds()
if diff > seconds:
seconds = diff
winner = ident
if winner is None:
return None
return session_statistics(
conn,
winner
)
# ============================================================
# Самая короткая поездка
# ============================================================
def shortest_session(conn):
sql = """
SELECT
id,
start_time,
end_time
FROM capture_sessions
WHERE end_time IS NOT NULL
"""
rows = conn.execute(sql).fetchall()
winner = None
seconds = None
for ident, start, end in rows:
try:
s = datetime.fromisoformat(start)
e = datetime.fromisoformat(end)
except Exception:
continue
diff = (e - s).total_seconds()
if seconds is None or diff < seconds:
seconds = diff
winner = ident
if winner is None:
return None
return session_statistics(
conn,
winner
)
# ============================================================
# Карточка поездки
# ============================================================
def session_card_data(
conn,
session_id
):
stat = session_statistics(
conn,
session_id
)
if stat is None:
return None
files = existing_files(
conn,
session_id
)
stat["files"] = files
trip_start, trip_end = trip_time(
conn,
session_id
)
if trip_start:
stat["trip_start"] = trip_start
if trip_end:
stat["trip_end"] = trip_end
if trip_start and trip_end:
stat["duration"] = format_duration(
trip_start,
trip_end
)
stat["directory_size"] = format_size(
session_directory_size(
conn,
session_id
)
)
return stat
# ============================================================
# Карточки всех поездок
# ============================================================
def all_sessions(
conn
):
sql = """
SELECT
id
FROM capture_sessions
ORDER BY
start_time DESC
"""
rows = conn.execute(
sql
).fetchall()
result = []
for row in rows:
card = session_card_data(
conn,
row[0]
)
if card:
result.append(card)
return result
def all_session_cards(conn):
sql = """
SELECT
id
FROM capture_sessions
ORDER BY
start_time DESC
"""
rows = conn.execute(sql).fetchall()
result = []
for row in rows:
card = session_card_data(
conn,
row[0]
)
if card:
result.append(card)
return result
# ============================================================
# Поиск поездки по ID
# ============================================================
def find_session(
conn,
session_id
):
for session in all_session_cards(conn):
if session["id"] == session_id:
return session
return None
# ============================================================
# Всего поездок
# ============================================================
def sessions_count(conn):
return query_value(
conn,
"""
SELECT COUNT(*)
FROM capture_sessions
""",
default=0
)