game.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. import time
  2. import sys
  3. import re
  4. import tkinter as tk
  5. from tkinter import scrolledtext
  6. import threading
  7. # --- ВАШИ ИСХОДНЫЕ ДАННЫЕ ---
  8. class Colors:
  9. RESET = "\033[0m"
  10. RED = "\033[91m"
  11. GREEN = "\033[92m"
  12. YELLOW = "\033[93m"
  13. CYAN = "\033[96m"
  14. BOLD = "\033[1m"
  15. def print_slow(text, delay=0.03):
  16. """Функция для эффекта печатной машинки"""
  17. for char in text:
  18. sys.stdout.write(char)
  19. sys.stdout.flush()
  20. time.sleep(delay)
  21. print()
  22. def print_scene_header(scene_num, title):
  23. print(f"\n{Colors.YELLOW}{'='*50}")
  24. print(f"{Colors.BOLD}СЦЕНА {scene_num}: {title.upper()}{Colors.RESET}")
  25. print(f"{Colors.YELLOW}{'='*50}{Colors.RESET}\n")
  26. quiz_data = [
  27. {
  28. "scene": 1, "title": "Брифинг в рубке",
  29. "text": "Главный инженер: «Новичок, наша сеть сейчас под атакой. Чтобы снять первую блокировку с консоли, напомни мне главный постулат современной архитектуры безопасности, на которую мы перешли».",
  30. "question": "Что означает концепция Zero Trust (Нулевое доверие)?",
  31. "options": ["Доверять всем устройствам внутри корпоративной сети по умолчанию", "Никогда не доверять, всегда проверять любой запрос на доступ", "Полностью заблокировать доступ в интернет всем сотрудникам", "Доверять только администраторам, а остальным запрещать всё"],
  32. "correct": 1
  33. },
  34. {
  35. "scene": 2, "title": "Периметр под огнем",
  36. "text": "Система разблокирована. Вы видите попытку входа под учеткой директора. Система запросила подтверждающий код из приложения. Нужно классифицировать эти два шага для журнала аудита.",
  37. "question": "Ввод логина 'Director' — это идентификация или аутентификация? А ввод кода из приложения?",
  38. "options": ["Оба действия — это аутентификация", "Логин — идентификация, код из приложения — аутентификация", "Логин — аутентификация, код из приложения — идентификация", "Оба действия — это идентификация"],
  39. "correct": 1
  40. },
  41. {
  42. "scene": 3, "title": "Троянский конь изнутри",
  43. "text": f"{Colors.RED}ВНИМАНИЕ: Обнаружена аномалия!{Colors.RESET} Сотрудник бухгалтерии в 03:00 ночи пытается скачать базу клиентов. Это не внешний хакер, угроза исходит изнутри периметра.",
  44. "question": "К какому вектору угроз относится эта ситуация и какая система помогла ее обнаружить?",
  45. "options": ["Внешний вектор, обнаружен с помощью антивируса", "Физический вектор, обнаружен камерами видеонаблюдения", "Внутренний вектор (инсайдер), обнаружен SIEM-системой", "Программный вектор, обнаружен файрволом"],
  46. "correct": 2
  47. },
  48. {
  49. "scene": 4, "title": "Физическая брешь",
  50. "text": "Охрана сообщает: «С рабочего стола украден ноутбук аналитика!» На нем хранились отчеты. Вору не осталось ничего, кроме самого железа и жесткого диска.",
  51. "question": "Какие средства защиты гарантируют, что данные на украденном диске останутся нечитаемым набором символов?",
  52. "options": ["Сложный пароль на Windows и скрытые папки", "Шифрование жесткого диска (FDE) и модуль доверенной загрузки (TPM)", "Установленный браузер Tor и отключенный Wi-Fi", "Регулярная дефрагментация диска и очистка корзины"],
  53. "correct": 1
  54. },
  55. {
  56. "scene": 5, "title": "Восстановление системы",
  57. "text": "Благодаря вашим верным решениям, скомпрометированный аккаунт заблокирован, а данные в безопасности. Штатные вирусы изолированы. Настало время закрыть инцидент.",
  58. "question": "Согласно модели NIST, как называется финальный этап жизненного цикла реагирования на инцидент, на котором данные восстанавливаются из бэкапов?",
  59. "options": ["Защита (Protect)", "Обнаружение (Detect)", "Реагирование (Respond)", "Восстановление (Recover)"],
  60. "correct": 3
  61. }
  62. ]
  63. # --- КЛАСС ГРАФИЧЕСКОГО ИНТЕРФЕЙСА ---
  64. class HackerApp:
  65. def __init__(self, root):
  66. self.root = root
  67. self.root.title("ОПЕРАЦИЯ: НУЛЕВОЕ ДОВЕРИЕ")
  68. self.root.geometry("800x600")
  69. self.root.configure(bg="black")
  70. # Настройка шрифта (Consolas или монопшринный по умолчанию)
  71. self.font = ("Consolas", 11)
  72. # Поле вывода текста (Терминал)
  73. self.text_area = scrolledtext.ScrolledText(
  74. root,
  75. bg="#050505",
  76. fg="#00FF41",
  77. font=self.font,
  78. insertbackground="white",
  79. wrap=tk.WORD,
  80. state=tk.DISABLED, # Запрещаем ручной ввод в главное окно
  81. borderwidth=0,
  82. highlightthickness=0
  83. )
  84. self.text_area.pack(padx=10, pady=(10, 0), fill=tk.BOTH, expand=True)
  85. # Строка ввода ответов
  86. self.input_frame = tk.Frame(root, bg="black")
  87. self.input_frame.pack(padx=10, pady=10, fill=tk.X)
  88. self.input_var = tk.StringVar()
  89. self.input_entry = tk.Entry(
  90. self.input_frame,
  91. textvariable=self.input_var,
  92. bg="#0A0A0A",
  93. fg="#00FFFF",
  94. font=self.font,
  95. insertbackground="#00FFFF",
  96. borderwidth=1,
  97. relief="solid"
  98. )
  99. self.input_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, ipady=5)
  100. self.input_entry.bind("<Return>", self.on_enter)
  101. # Механизм замены стандартного ввода/вывода
  102. self.input_ready = threading.Event()
  103. self.user_input = ""
  104. # Перенаправляем стандартный вывод (print) в наше окно
  105. sys.stdout = self
  106. # Функция для записи в текстовое поле (с поддержкой потоков)
  107. def write(self, text):
  108. # Очищаем ANSI-коды, так как Tkinter их не понимает,
  109. # но заменяем их на теги цветов Tkinter
  110. clean_text = re.sub(r'\x1B\[[0-?]*[ -/]*[@-~]', '', text)
  111. def _write():
  112. self.text_area.config(state=tk.NORMAL)
  113. # Простая эмуляция цветов (зеленный по умолчанию)
  114. if Colors.RED in text:
  115. self.text_area.insert(tk.END, clean_text, "red")
  116. elif Colors.YELLOW in text:
  117. self.text_area.insert(tk.END, clean_text, "yellow")
  118. elif Colors.CYAN in text:
  119. self.text_area.insert(tk.END, clean_text, "cyan")
  120. elif Colors.BOLD in text:
  121. self.text_area.insert(tk.END, clean_text, "bold")
  122. else:
  123. self.text_area.insert(tk.END, clean_text)
  124. self.text_area.see(tk.END)
  125. self.text_area.config(state=tk.DISABLED)
  126. # Используем root.after для безопасности потоков в Tkinter
  127. self.root.after(0, _write)
  128. def flush(self):
  129. pass # Заглушка для sys.stdout
  130. # Обработка нажатия Enter в строке ввода
  131. def on_enter(self, event):
  132. self.user_input = self.input_var.get()
  133. self.input_entry.delete(0, tk.END)
  134. self.input_entry.config(state=tk.DISABLED) # Блокируем ввод пока идет процесс
  135. self.input_ready.set() # Сигнализируем основному потоку, что ответ получен
  136. # Замена стандартной функции input()
  137. def gui_input(self, prompt):
  138. self.write(prompt)
  139. self.input_ready.clear()
  140. # Разблокируем поле ввода в главном потоке интерфейса
  141. def unlock():
  142. self.input_entry.config(state=tk.NORMAL)
  143. self.input_entry.focus_set()
  144. self.root.after(0, unlock)
  145. # Ждем, пока пользователь не нажмет Enter
  146. self.input_ready.wait()
  147. # Выводим то, что ввел пользователь (эффект эха)
  148. self.write(self.user_input + "\n")
  149. return self.user_input
  150. # --- ИЗМЕНЕННАЯ ФУНКЦИЯ MAIN ---
  151. def main(app):
  152. print(f"{Colors.CYAN}{Colors.BOLD}")
  153. print("╔══════════════════════════════════════════════╗")
  154. print("║ ОПЕРАЦИЯ: НУЛЕВОЕ ДОВЕРИЕ ║")
  155. print("║ СИМУЛЯЦИЯ ИБ-АНАЛИТИКА ║")
  156. print("╚══════════════════════════════════════════════╝{Colors.RESET}\n")
  157. print_slow("Инициализация терминала безопасности...")
  158. time.sleep(1)
  159. print_slow("Загрузка базы данных лекций по НСД...")
  160. time.sleep(1)
  161. print_slow(f"{Colors.YELLOW}Статус: ТРЕВОГА. Требуется подтверждение допуска.{Colors.RESET}")
  162. time.sleep(1)
  163. score = 0
  164. for level in quiz_data:
  165. print_scene_header(level["scene"], level["title"])
  166. print_slow(level["text"])
  167. time.sleep(0.5)
  168. print(f"\n{Colors.BOLD}>> ВАШ ОТВЕТ:{Colors.RESET}")
  169. for i, option in enumerate(level["options"]):
  170. print(f" [{i + 1}] {option}")
  171. # Заменяем стандартный input() на наш графический
  172. while True:
  173. try:
  174. # ВАЖНО: тут используем app.gui_input вместо обычного input
  175. user_answer = int(app.gui_input(f"\n{Colors.CYAN}Введите номер варианта (1-{len(level['options'])}: ")) - 1
  176. if 0 <= user_answer < len(level["options"]):
  177. break
  178. else:
  179. print(f"{Colors.RED}Ошибка: Выбран несуществующий вариант. Попробуйте снова.{Colors.RESET}")
  180. except ValueError:
  181. print(f"{Colors.RED}Ошибка: Введите пожалуйста цифру.{Colors.RESET}")
  182. print("\n" + "-"*40)
  183. if user_answer == level["correct"]:
  184. print(f"{Colors.GREEN}[ УСПЕХ ] Верный ответ! Угроза нейтрализована.{Colors.RESET}")
  185. score += 1
  186. else:
  187. print(f"{Colors.RED}[ ПРОВАЛ ] Неверный ответ! Системе нанесен ущерб.{Colors.RESET}")
  188. correct_text = level["options"][level["correct"]]
  189. print(f"{Colors.YELLOW}Подсказка системы: Правильный ответ -> {correct_text}{Colors.RESET}")
  190. print("-"*40 + "\n")
  191. time.sleep(1.5)
  192. # Финал игры
  193. print(f"{Colors.YELLOW}{'='*50}{Colors.RESET}")
  194. print(f"{Colors.BOLD}СИМУЛЯЦИЯ ЗАВЕРШЕНА{Colors.RESET}")
  195. print(f"{Colors.YELLOW}{'='*50}{Colors.RESET}\n")
  196. print_slow(f"Итоговый результат: {score} из {len(quiz_data)} верных решений.")
  197. if score == len(quiz_data):
  198. print_slow(f"\n{Colors.GREEN}{Colors.BOLD}РАНГ: ЭКСПЕРТ. Вы полностью остановили утечку данных и доказали, что уместно применять меры защиты заблаговременно. Добро пожаловать в отдел ИБ!{Colors.RESET}")
  199. elif score >= 3:
  200. print_slow(f"\n{Colors.YELLOW}{Colors.BOLD}РАНГ: УЧЕНИК. Атака частично отражена, но компания понесла некоторые потери. Вам стоит перечитать лекцию про внутренние угрозы и криптографию.{Colors.RESET}")
  201. else:
  202. print_slow(f"\n{Colors.RED}{Colors.BOLD}РАНГ: ПРОВАЛ. Сервера скомпрометированы, данные утекли в сеть. Уступить инициативу — значит допустить потерю данных. Симуляция провалена.{Colors.RESET}")
  203. if __name__ == "__main__":
  204. # Создаем графическое окно
  205. root = tk.Tk()
  206. app = HackerApp(root)
  207. # Настраиваем цветовые теги для текста в Tkinter
  208. app.text_area.tag_config("red", foreground="#FF4444")
  209. app.text_area.tag_config("yellow", foreground="#FFFF00")
  210. app.text_area.tag_config("cyan", foreground="#00FFFF")
  211. app.text_area.tag_config("bold", font=("Consolas", 11, "bold"), foreground="#FFFFFF")
  212. # Запускаем логику игры в отдельном потоке, чтобы не зависало окно
  213. game_thread = threading.Thread(target=main, args=(app,), daemon=True)
  214. game_thread.start()
  215. # Запускаем графический цикл
  216. root.mainloop()