dissect.target 3.14.dev28__py3-none-any.whl → 3.15__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. dissect/target/containers/ewf.py +1 -1
  2. dissect/target/containers/vhd.py +5 -2
  3. dissect/target/filesystem.py +36 -18
  4. dissect/target/filesystems/dir.py +10 -4
  5. dissect/target/filesystems/jffs.py +122 -0
  6. dissect/target/helpers/compat/path_310.py +506 -0
  7. dissect/target/helpers/compat/path_311.py +539 -0
  8. dissect/target/helpers/compat/path_312.py +443 -0
  9. dissect/target/helpers/compat/path_39.py +545 -0
  10. dissect/target/helpers/compat/path_common.py +223 -0
  11. dissect/target/helpers/cyber.py +512 -0
  12. dissect/target/helpers/fsutil.py +128 -666
  13. dissect/target/helpers/hashutil.py +17 -57
  14. dissect/target/helpers/keychain.py +9 -3
  15. dissect/target/helpers/loaderutil.py +1 -1
  16. dissect/target/helpers/mount.py +47 -4
  17. dissect/target/helpers/polypath.py +73 -0
  18. dissect/target/helpers/record_modifier.py +100 -0
  19. dissect/target/loader.py +2 -1
  20. dissect/target/loaders/asdf.py +2 -0
  21. dissect/target/loaders/cyber.py +37 -0
  22. dissect/target/loaders/log.py +14 -3
  23. dissect/target/loaders/raw.py +2 -0
  24. dissect/target/loaders/remote.py +12 -0
  25. dissect/target/loaders/tar.py +13 -0
  26. dissect/target/loaders/targetd.py +2 -0
  27. dissect/target/loaders/velociraptor.py +12 -3
  28. dissect/target/loaders/vmwarevm.py +2 -0
  29. dissect/target/plugin.py +272 -143
  30. dissect/target/plugins/apps/ssh/openssh.py +11 -54
  31. dissect/target/plugins/apps/ssh/opensshd.py +4 -3
  32. dissect/target/plugins/apps/ssh/putty.py +236 -0
  33. dissect/target/plugins/apps/ssh/ssh.py +58 -0
  34. dissect/target/plugins/apps/vpn/openvpn.py +6 -0
  35. dissect/target/plugins/apps/webserver/apache.py +309 -95
  36. dissect/target/plugins/apps/webserver/caddy.py +5 -2
  37. dissect/target/plugins/apps/webserver/citrix.py +82 -0
  38. dissect/target/plugins/apps/webserver/iis.py +9 -12
  39. dissect/target/plugins/apps/webserver/nginx.py +5 -2
  40. dissect/target/plugins/apps/webserver/webserver.py +25 -41
  41. dissect/target/plugins/child/wsl.py +1 -1
  42. dissect/target/plugins/filesystem/ntfs/mft.py +10 -0
  43. dissect/target/plugins/filesystem/ntfs/mft_timeline.py +10 -0
  44. dissect/target/plugins/filesystem/ntfs/usnjrnl.py +10 -0
  45. dissect/target/plugins/filesystem/ntfs/utils.py +28 -5
  46. dissect/target/plugins/filesystem/resolver.py +6 -4
  47. dissect/target/plugins/general/default.py +0 -2
  48. dissect/target/plugins/general/example.py +0 -1
  49. dissect/target/plugins/general/loaders.py +3 -5
  50. dissect/target/plugins/os/unix/_os.py +3 -3
  51. dissect/target/plugins/os/unix/bsd/citrix/_os.py +68 -28
  52. dissect/target/plugins/os/unix/bsd/citrix/history.py +130 -0
  53. dissect/target/plugins/os/unix/generic.py +17 -10
  54. dissect/target/plugins/os/unix/linux/fortios/__init__.py +0 -0
  55. dissect/target/plugins/os/unix/linux/fortios/_os.py +534 -0
  56. dissect/target/plugins/os/unix/linux/fortios/generic.py +30 -0
  57. dissect/target/plugins/os/unix/linux/fortios/locale.py +109 -0
  58. dissect/target/plugins/os/windows/log/evt.py +1 -1
  59. dissect/target/plugins/os/windows/log/schedlgu.py +155 -0
  60. dissect/target/plugins/os/windows/regf/firewall.py +1 -1
  61. dissect/target/plugins/os/windows/regf/shimcache.py +1 -1
  62. dissect/target/plugins/os/windows/regf/trusteddocs.py +1 -1
  63. dissect/target/plugins/os/windows/registry.py +1 -1
  64. dissect/target/plugins/os/windows/sam.py +3 -0
  65. dissect/target/plugins/os/windows/sru.py +41 -28
  66. dissect/target/plugins/os/windows/tasks.py +5 -2
  67. dissect/target/target.py +7 -3
  68. dissect/target/tools/dd.py +7 -1
  69. dissect/target/tools/fs.py +8 -1
  70. dissect/target/tools/info.py +22 -15
  71. dissect/target/tools/mount.py +28 -3
  72. dissect/target/tools/query.py +146 -117
  73. dissect/target/tools/reg.py +21 -16
  74. dissect/target/tools/shell.py +30 -6
  75. dissect/target/tools/utils.py +28 -0
  76. dissect/target/volumes/bde.py +14 -10
  77. dissect/target/volumes/luks.py +18 -10
  78. {dissect.target-3.14.dev28.dist-info → dissect.target-3.15.dist-info}/METADATA +4 -3
  79. {dissect.target-3.14.dev28.dist-info → dissect.target-3.15.dist-info}/RECORD +85 -67
  80. dissect/target/plugins/os/unix/linux/fortigate/_os.py +0 -175
  81. /dissect/target/{plugins/os/unix/linux/fortigate → helpers/compat}/__init__.py +0 -0
  82. {dissect.target-3.14.dev28.dist-info → dissect.target-3.15.dist-info}/COPYRIGHT +0 -0
  83. {dissect.target-3.14.dev28.dist-info → dissect.target-3.15.dist-info}/LICENSE +0 -0
  84. {dissect.target-3.14.dev28.dist-info → dissect.target-3.15.dist-info}/WHEEL +0 -0
  85. {dissect.target-3.14.dev28.dist-info → dissect.target-3.15.dist-info}/entry_points.txt +0 -0
  86. {dissect.target-3.14.dev28.dist-info → dissect.target-3.15.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,512 @@
1
+ import os
2
+ import random
3
+ import string
4
+ import struct
5
+ import sys
6
+ import time
7
+ from array import array
8
+ from contextlib import contextmanager, redirect_stdout
9
+ from enum import Enum
10
+ from io import StringIO
11
+ from typing import Iterator, Optional
12
+
13
+ try:
14
+ import fcntl
15
+ import termios
16
+
17
+ CAN_CYBER = True
18
+ except ImportError:
19
+ CAN_CYBER = False
20
+
21
+ # fmt: off
22
+ NMS_MASK_TABLE = [
23
+ "!", '"', "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", "~", ".", "/", ":",
24
+ ";", "<", "=", ">", "?", "[", "\\", "]", "_", "{", "}", "A", "B", "C", "D", "E", "F",
25
+ "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
26
+ "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
27
+ "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4",
28
+ "5", "6", "7", "8", "9", "Ç", "ü", "é", "â", "ä", "à", "å", "ç", "ê", "ë", "è", "ï",
29
+ "î", "ì", "Ä", "Å", "É", "æ", "Æ", "ô", "ö", "ò", "û", "ù", "ÿ", "Ö", "Ü", "¢", "£",
30
+ "¥", "ƒ", "á", "í", "ó", "ú", "ñ", "Ñ", "ª", "º", "¿", "¬", "½", "¼", "¡", "«", "»",
31
+ "α", "ß", "Γ", "π", "Σ", "σ", "µ", "τ", "Φ", "Θ", "Ω", "δ", "φ", "ε", "±", "÷", "°",
32
+ "·", "²", "¶", "⌐", "₧", "░", "▒", "▓", "│", "┤", "╡", "╢", "╖", "╕", "╣", "║", "╗",
33
+ "╝", "╜", "╛", "┐", "└", "┴", "┬", "├", "─", "┼", "╞", "╟", "╚", "╔", "╩", "╦", "╠",
34
+ "═", "╬", "╧", "╨", "╤", "╧", "╙", "╘", "╒", "╓", "╫", "╪", "┘", "┌", "█", "▄", "▌",
35
+ "▐", "▀", "∞", "∩", "≡", "≥", "≤", "⌠", "⌡", "≈", "∙", "√", "ⁿ", "■",
36
+ ]
37
+ # fmt: on
38
+
39
+ NMS_TYPE_EFFECT_SPEED = 4 / 1000
40
+ NMS_JUMBLE_SECONDS = 1
41
+ NMS_JUMBLE_LOOP_SPEED = 35
42
+ NMS_REVEAL_SECONDS = 2
43
+ NMS_REVEAL_LOOP_SPEED = 50
44
+
45
+ MATRIX_CHARS = list(map(chr, range(0x20, 0x7F)))
46
+
47
+ MATRIX_MAX_SPEED = 5
48
+ MATRIX_MAX_CASCADES = 600
49
+ MATRIX_MAX_COLS = 20
50
+ MATRIX_FRAME_DELAY = 0.03
51
+ MATRIX_REVEAL_SECONDS = 4
52
+
53
+
54
+ class Color(Enum):
55
+ BLACK = 30
56
+ RED = 31
57
+ GREEN = 32
58
+ YELLOW = 33
59
+ BLUE = 34
60
+ MAGENTA = 35
61
+ CYAN = 36
62
+ WHITE = 37
63
+
64
+
65
+ class CyberIO(StringIO):
66
+ def __init__(self, color: Optional[Color] = None, mask_space: bool = False, run_at_end: bool = False):
67
+ self._color = color
68
+ self._mask_space = mask_space
69
+ self._run_at_end = run_at_end
70
+ super().__init__()
71
+
72
+ def write(self, s: str) -> int:
73
+ if self._run_at_end:
74
+ super().write(s)
75
+ else:
76
+ cyber_print(s, self._color, self._mask_space)
77
+ return len(s)
78
+
79
+
80
+ @contextmanager
81
+ def cyber(color: Optional[Color] = Color.YELLOW, mask_space: bool = False, run_at_end: bool = False) -> None:
82
+ stream = CyberIO(color, mask_space, run_at_end)
83
+ with redirect_stdout(stream):
84
+ yield
85
+
86
+ if run_at_end:
87
+ cyber_print(stream.getvalue(), color, mask_space)
88
+
89
+
90
+ def cyber_print(buf: str, color: Optional[Color] = None, mask_space: bool = False) -> None:
91
+ if not buf or buf == "\n":
92
+ sys.__stdout__.write(buf)
93
+ return
94
+
95
+ if not CAN_CYBER:
96
+ sys.__stdout__.write("you're not cybering hard enough\n")
97
+
98
+ if os.getenv("CYBER") == "💊":
99
+ matrix(buf, color, mask_space)
100
+ else:
101
+ nms(buf, color, mask_space)
102
+
103
+
104
+ # https://github.com/bartobri/libnms
105
+ def nms(buf: str, color: Optional[Color] = None, mask_space: bool = False) -> None:
106
+ orig_row, orig_col = (0, 0)
107
+ with _set_terminal():
108
+ max_rows, max_cols = _get_win_size()
109
+
110
+ orig_row, _ = _get_cursor_pos()
111
+
112
+ _cursor_hide()
113
+ _cursor_move(orig_row, orig_col)
114
+
115
+ characters, remaining, (orig_row, orig_col), _ = _get_character_info(
116
+ buf, max_rows, max_cols, orig_row, orig_col
117
+ )
118
+ character_state = []
119
+
120
+ try:
121
+ # Write initial mask
122
+ for char, has_ansi, end_ansi in characters:
123
+ # Initialize the character state with a mask and reveal time
124
+ if end_ansi:
125
+ reveal_time = random.randint(0, 100)
126
+ else:
127
+ reveal_time = random.randint(100, NMS_REVEAL_SECONDS * 1000)
128
+
129
+ mask = random.choice(NMS_MASK_TABLE)
130
+ character_state.append((char, mask, reveal_time, has_ansi))
131
+
132
+ if ("\n" in char or "\r\n" in char) or (not mask_space and char == " "):
133
+ sys.__stdout__.write(char)
134
+ continue
135
+
136
+ sys.__stdout__.write(mask)
137
+
138
+ sys.__stdout__.flush()
139
+ time.sleep(NMS_TYPE_EFFECT_SPEED)
140
+
141
+ _clear_input()
142
+ time.sleep(1)
143
+
144
+ for _ in range((NMS_JUMBLE_SECONDS * 1000) // NMS_JUMBLE_LOOP_SPEED):
145
+ _cursor_move(orig_row, orig_col)
146
+
147
+ for char, _, _, _ in character_state:
148
+ if ("\n" in char or "\r\n" in char) or (not mask_space and char == " "):
149
+ sys.__stdout__.write(char)
150
+ continue
151
+
152
+ sys.__stdout__.write(random.choice(NMS_MASK_TABLE))
153
+
154
+ sys.__stdout__.flush()
155
+ time.sleep(NMS_JUMBLE_LOOP_SPEED / 1000)
156
+
157
+ revealed = False
158
+
159
+ while not revealed:
160
+ _cursor_move(orig_row, orig_col)
161
+ revealed = True
162
+
163
+ for i, (char, mask, time_remaining, has_ansi) in enumerate(character_state):
164
+ if ("\n" in char or "\r\n" in char) or (not mask_space and char == " "):
165
+ sys.__stdout__.write(char)
166
+ continue
167
+
168
+ if time_remaining > 0:
169
+ if time_remaining < 500:
170
+ if random.randint(0, 3) == 0:
171
+ mask = random.choice(NMS_MASK_TABLE)
172
+ else:
173
+ if random.randint(0, 10) == 0:
174
+ mask = random.choice(NMS_MASK_TABLE)
175
+
176
+ sys.__stdout__.write(mask)
177
+ time_remaining -= NMS_REVEAL_LOOP_SPEED
178
+
179
+ revealed = False
180
+ character_state[i] = (char, mask, time_remaining, has_ansi)
181
+ else:
182
+ if has_ansi:
183
+ sys.__stdout__.write(char)
184
+ else:
185
+ if color:
186
+ _bold()
187
+ _foreground_color(color)
188
+
189
+ sys.__stdout__.write(char)
190
+
191
+ if color:
192
+ _clear_attr()
193
+
194
+ sys.__stdout__.flush()
195
+ time.sleep(NMS_REVEAL_LOOP_SPEED / 1000)
196
+
197
+ _clear_input()
198
+ _cursor_show()
199
+
200
+ if remaining:
201
+ _write_remaining(remaining, color)
202
+ except KeyboardInterrupt:
203
+ _clear_screen()
204
+ finally:
205
+ _clear_attr()
206
+ _cursor_show()
207
+
208
+
209
+ # https://github.com/jsbueno/terminal_matrix
210
+ def matrix(buf: str, color: Optional[Color] = None, mask_space: bool = False) -> None:
211
+ orig_row, orig_col = (0, 0)
212
+ with _set_terminal():
213
+ max_rows, max_cols = _get_win_size()
214
+
215
+ orig_row, _ = _get_cursor_pos()
216
+
217
+ _cursor_hide()
218
+ _clear_screen()
219
+ _cursor_move(orig_row, orig_col)
220
+
221
+ characters, remaining, (orig_row, orig_col), (end_row, _) = _get_character_info(
222
+ buf, max_rows, max_cols, orig_row, orig_col
223
+ )
224
+ reveal_cols = [[" " for _ in range(max_rows + 1)] for _ in range(max_cols)]
225
+
226
+ cur_ansi = ""
227
+ row = column = 0
228
+ for char, has_ansi, end_ansi in characters:
229
+ if has_ansi:
230
+ if end_ansi:
231
+ cur_ansi += char[:-5][:-4]
232
+ else:
233
+ cur_ansi += char[:-1]
234
+ elif color:
235
+ cur_ansi = f"\033[1m\033[0;{color.value}m"
236
+
237
+ if cur_ansi:
238
+ char = cur_ansi + char + "\033[0m"
239
+
240
+ if "\n" in char or "\r\n" in char:
241
+ char = " " + char
242
+
243
+ reveal_cols[column][row] = char
244
+
245
+ if "\n" in char or "\r\n" in char:
246
+ row += 1
247
+ column = 0
248
+ elif column == max_cols:
249
+ row += 1
250
+ column = 0
251
+ else:
252
+ column += 1
253
+
254
+ time_remaining = MATRIX_REVEAL_SECONDS
255
+
256
+ try:
257
+ cascading = set()
258
+ remaining_columns = set(range(1, max_cols))
259
+ occupied_columns = set()
260
+
261
+ while True:
262
+ if time_remaining > 0:
263
+ while True:
264
+ if random.randrange(MATRIX_MAX_CASCADES + 1) > len(cascading):
265
+ start_col = random.randrange(1, max_cols)
266
+ for i in range(random.randrange(MATRIX_MAX_COLS)):
267
+ col = (start_col + i) % (max_cols + 1)
268
+ if col != 0 and col not in occupied_columns:
269
+ cascading.add(_cascade(col, max_rows + 1, reveal_cols[col - 1]))
270
+ occupied_columns.add(col)
271
+ remaining_columns.discard(col)
272
+ break
273
+ elif remaining_columns:
274
+ while remaining_columns:
275
+ col = remaining_columns.pop()
276
+ cascading.add(_cascade(col, max_rows, reveal_cols[col - 1]))
277
+
278
+ stopped = set()
279
+ for c in cascading:
280
+ try:
281
+ next(c)
282
+ except StopIteration:
283
+ stopped.add(c)
284
+
285
+ sys.__stdout__.flush()
286
+
287
+ cascading.difference_update(stopped)
288
+ time_remaining -= MATRIX_FRAME_DELAY
289
+ time.sleep(MATRIX_FRAME_DELAY)
290
+
291
+ if not cascading:
292
+ break
293
+
294
+ _cursor_move(end_row - orig_row + 1, 0)
295
+ if remaining:
296
+ _write_remaining(remaining, color)
297
+ except KeyboardInterrupt:
298
+ _clear_screen()
299
+ finally:
300
+ _clear_attr()
301
+ _cursor_show()
302
+
303
+
304
+ def _cascade(col: int, max_rows: int, reveal_row: list[str]) -> Iterator:
305
+ speed = random.randrange(1, MATRIX_MAX_SPEED)
306
+ erase_speed = random.randrange(1, MATRIX_MAX_SPEED)
307
+
308
+ if speed < erase_speed:
309
+ speed, erase_speed = erase_speed, speed
310
+
311
+ row = counter = erase_counter = 0
312
+ old_row = erase_row = -1
313
+ erasing = False
314
+ bright = True
315
+
316
+ limit = max(0, max_rows - (random.paretovariate(1.16) - 1) * (max_rows // 2))
317
+
318
+ while True:
319
+ counter, row = _update_row(speed, counter, row)
320
+ if random.randrange(10 * speed) < 1:
321
+ bright = False
322
+
323
+ if row > 1 and row <= limit and old_row != row:
324
+ _print_at(random.choice(MATRIX_CHARS), row - 1, col, Color.GREEN, bright)
325
+
326
+ if row < limit:
327
+ _print_at(random.choice(MATRIX_CHARS), row, col, Color.WHITE, bright)
328
+
329
+ if not erasing:
330
+ erasing = random.randrange(row + 1) > (max_rows / 2)
331
+ erase_row = 0
332
+ else:
333
+ erase_counter, erase_row = _update_row(erase_speed, erase_counter, erase_row)
334
+
335
+ for i in range(1, erase_row):
336
+ _print_at(reveal_row[i - 1], i, col)
337
+
338
+ yield None
339
+
340
+ if erase_row >= max_rows:
341
+ for i in range(1, max_rows):
342
+ _print_at(reveal_row[i - 1], i, col)
343
+ break
344
+
345
+
346
+ def _update_row(speed: int, counter: int, row: int) -> tuple[int, int]:
347
+ counter += 1
348
+ if counter >= speed:
349
+ row += 1
350
+ counter = 0
351
+ return counter, row
352
+
353
+
354
+ def _get_character_info(
355
+ buf: str, max_rows: int, max_cols: int, orig_row: int, orig_col: int
356
+ ) -> tuple[list[tuple[str, bool, bool]], str, tuple[int, int], tuple[int, int]]:
357
+ cur_row, cur_col = orig_row, orig_col
358
+
359
+ characters = []
360
+ has_ansi = False
361
+ end_ansi = False
362
+
363
+ i = 0
364
+ while i < len(buf):
365
+ if cur_row - orig_row >= max_rows - 1:
366
+ break
367
+
368
+ if end_ansi:
369
+ has_ansi = False
370
+ end_ansi = False
371
+
372
+ char = buf[i]
373
+ i += 1
374
+
375
+ if char == "\033":
376
+ has_ansi = True
377
+
378
+ while i < len(buf):
379
+ char += buf[i]
380
+ i += 1
381
+
382
+ if char[-1] in string.ascii_letters and i < len(buf):
383
+ # First letter is the end of the ANSI code, read one more
384
+ char += buf[i]
385
+ i += 1
386
+
387
+ if char[-1] != "\033":
388
+ # The real end, now we have a char with ANSI codes prepended to it
389
+ break
390
+
391
+ if (ansi_reset := buf[i : i + 4]) == "\033[0m":
392
+ char += ansi_reset
393
+ i += 4
394
+ end_ansi = True
395
+
396
+ if char == "\r" and i < len(buf) and buf[i] == "\n":
397
+ char += buf[i]
398
+ i += 1
399
+
400
+ characters.append((char, has_ansi, end_ansi))
401
+
402
+ if ("\n" in char or "\r\n" in char) or cur_col > max_cols:
403
+ has_ansi = False
404
+ cur_col = 0
405
+ cur_row += 1
406
+ if cur_row == max_rows + 1 and orig_row > 0:
407
+ orig_row -= 1
408
+ cur_row -= 1
409
+
410
+ remaining = buf[i:]
411
+
412
+ return characters, remaining, (orig_row, orig_col), (cur_row, cur_col)
413
+
414
+
415
+ def _write_remaining(remaining: str, color: Optional[Color]) -> None:
416
+ time.sleep(0.5)
417
+
418
+ if color and "\033" not in remaining:
419
+ _bold()
420
+ _foreground_color(color)
421
+
422
+ sys.__stdout__.write(remaining)
423
+
424
+ if color and "\033" not in remaining:
425
+ _clear_attr()
426
+
427
+
428
+ @contextmanager
429
+ def _set_terminal() -> Iterator[None]:
430
+ attr = termios.tcgetattr(sys.__stdin__)
431
+
432
+ new = attr[:]
433
+ new[3] &= ~termios.ICANON & ~termios.ECHO
434
+
435
+ termios.tcsetattr(sys.__stdin__, termios.TCSAFLUSH, new)
436
+
437
+ try:
438
+ yield
439
+ finally:
440
+ termios.tcsetattr(sys.__stdin__, termios.TCSANOW, attr)
441
+
442
+
443
+ def _get_win_size() -> tuple[int, int]:
444
+ packed = fcntl.ioctl(sys.__stdin__.buffer, termios.TIOCGWINSZ, struct.pack("HHHH", 0, 0, 0, 0))
445
+ rows, cols, _, _ = struct.unpack("HHHH", packed)
446
+ return rows, cols
447
+
448
+
449
+ def _get_cursor_pos() -> int:
450
+ sys.__stdout__.write("\033[6n")
451
+ sys.__stdout__.flush()
452
+
453
+ buf = ""
454
+ while (c := sys.__stdin__.read(1)) != "R":
455
+ if c in ("\033", "["):
456
+ continue
457
+
458
+ buf += c
459
+
460
+ row, col = map(int, buf.split(";"))
461
+
462
+ return row, col
463
+
464
+
465
+ def _print_at(s: str, row: int, col: int, color: Optional[Color] = None, bright: bool = False) -> None:
466
+ _cursor_move(row, col)
467
+ if color:
468
+ _foreground_color(color, bright)
469
+ sys.__stdout__.write(s)
470
+ if color:
471
+ _clear_attr()
472
+
473
+
474
+ def _clear_input() -> None:
475
+ i = array("i", [0])
476
+ fcntl.ioctl(sys.__stdin__.buffer, termios.FIONREAD, i)
477
+ sys.__stdin__.buffer.read(i[0])
478
+
479
+
480
+ def _cursor_move(row: int, col: int) -> None:
481
+ sys.__stdout__.write(f"\033[{row};{col}H")
482
+ sys.__stdout__.flush()
483
+
484
+
485
+ def _bold() -> None:
486
+ sys.__stdout__.write("\033[1m")
487
+ sys.__stdout__.flush()
488
+
489
+
490
+ def _foreground_color(c: Color, bright: bool = False) -> None:
491
+ sys.__stdout__.write(f"\033[{'1' if bright else '0'};{c.value}m")
492
+ sys.__stdout__.flush()
493
+
494
+
495
+ def _clear_attr() -> None:
496
+ sys.__stdout__.write("\033[0m")
497
+ sys.__stdout__.flush()
498
+
499
+
500
+ def _clear_screen() -> None:
501
+ sys.__stdout__.write("\033[2J")
502
+ sys.__stdout__.flush()
503
+
504
+
505
+ def _cursor_hide() -> None:
506
+ sys.__stdout__.write("\033[?25l")
507
+ sys.__stdout__.flush()
508
+
509
+
510
+ def _cursor_show() -> None:
511
+ sys.__stdout__.write("\033[?25h")
512
+ sys.__stdout__.flush()