Здравствуй, codeby! У меня в планах создать асинхронный listener который может работать одновременно c любым количеством соединений, и добавить фитчу, которая позволит узнать точное местоположение(вплоть до дома) жертвы. Для работы этой фитчи, потребуется только интернет соединение, GPS не нужен. Я постараюсь уложится в две статьи. Сегодня мы сделаем каркас для listener.
Предупреждение:
Софт который мы напишем будут не идеален, конечно я могу попробовать сделать его как можно лучше, но это займёт много времени и кода. При желании, вы легко сможете сами расширить функционал кода данного мной. И этот туториал рассчитан на тех, кто знает что такое сокеты и как они работают в python. Если вы в первый раз сталкиваетесь с ними, ссылка ниже - для вас.О сокетах в python:
Ссылка скрыта от гостей
Немного о select
select - функция, которая нужна для мониторинга изменений состояний файловых объектов и сокетов(сокет - это тоже файловый объект)select мониторит состояние файловых объектов, которых мы в неё передали.
Функция select принимает 3 списка с файловыми объектами, эти списки - это объекты, за изменением состояния которых мы хотели бы следить.
Первый список - объекты, за которыми нужно следить когда они станут доступны для чтения.
Второй список - объекты, за которыми нужно следить когда они станут доступны для записи.
Третий список - объекты, у которых мы ожидаем ошибки.
select возвращает те же переданные списки из объектов, только тогда, когда они станут доступны(первый список для чтения, второй список для записи, третий список - просто список объектов с ошибками)
Пример:
Python:
from select import select
import socket
sockets = [] # сокеты которые мы будем мониторить
socket_server = socket.socket()
socket_server.bind(('localhost', 4444))
socket_server.listen()
sockets.append(socket_server)
def get_raddr(string): # функция которая возвращает адрес клиентского сокета
raddr = string.replace('>', '')
return eval(raddr[raddr.find('raddr')::].replace('raddr=', ''))
def event_loop():
while True:
read, _, _ = select(sockets, [], [])
# Нам интересны только те объекты, которые станут доступны для чтения.
# Если клиентский сокет доступен для чтения, из него можно читать.
# Если серверный сокет доступен для чтения, можно делать accept_connection.
for i in read:
if i is socket_server:
conn, addr = socket_server.accept()
print(f'[+] Got connection from {addr[0]}:{addr[1]}')
sockets.append(conn)
else:
try:
print(
f'[+] Got message from {get_raddr(str(i))[1]}:{get_raddr(str(i))[1]}, {i.recv(4096).decode("utf-8")}'.strip())
except ConnectionResetError: # Если клиент отключился, закрываем соединение удаляем его из sockets
print(f'[-] Connection {get_raddr(str(i))[0]}:{get_raddr(str(i))[1]} is lost')
i.close()
sockets.remove(i)
event_loop()
Так же, я вам советую поиграться с этим кодом. Попробуйте подключится к нему через nc, выполнив
Код:
nc localhost 4444
Яндекс локатор
Получите api ключ от Яндекс локатора, он нам понадобится позже.
Ссылка скрыта от гостей
Яндекс локатор определяет местоположение устройства по ближайшим точкам доступа Wi-Fi и базовым станциям сотовой связи — без использования систем спутниковой навигации.Станции сотовой связи мы передавать Яндекс локатору не будем, он и по точкам доступа Wi-Fi прекрасно находит местоположение.
Приступим к кодингу
Создадим файл events.py.Ну и зачем нам events.py? - спросите вы.
Представьте такую картину, вспомнив код выше: Мы хотим сделать так, чтобы server.py постоянно просил нас ввести какую-то команду и исполнял её. Давайте попробуем реализовать команду list для server.py, эта команда будет выводит список всех подключённых к нам(точнее к серверному сокету) клиентских сокетов.
Перед вызовом event_loop в конце файла server.py, добавьте две такие строчки(вызов event_loop() уберите):
Python:
threading.Thread(target=event_loop, daemon=True).start()
manage_process()
Теперь объявим функцию manage_process в файле server.py.
Python:
def manage_process():
while True:
command = input('>>> ').strip()
if command == 'list':
if len(sockets) == 1:
print('[-] There are not any connection yet')
continue
for index, i in enumerate([sock for sock in sockets if sock is not socket_server]):
raddr = get_raddr(str(i))
print(f'{index + 1}\t{raddr[0]}:{raddr[-1]}')
elif not command: # Если введён пробел или команда пустая(пользователь нажал Enter),то мы выполняем цикл заново
continue
elif command == 'leave'
exit_logger.debug('leave...')
#exit_logger записывает сообщение без форматоров([INFO] ...) в messages.log, это сообщение появится в events.py. Что это и зачем вы поймёте ниже.
return
else:
print('[-] Invalid command')
1) запустите обновлённый код,
2) подключитесь к нашему серверному сокету через nc, выполнив
Код:
nc localhost 4444
Вы итоге, вы должны увидеть не приятную картину, print и input конфликтуют.
Что же делать? - спросите вы.
Я нашёл простое решение этой проблемы:
В файле server.py, информацию которую мы хотели бы вывести, мы будем записывать в .log файл, а в events.py эта записанная информация будет отображаться.
Ниже, я прикреплю код, поместите его в events.py
Python:
from time import sleep
from pygtail import Pygtail
import os
from sys import exit
try:
if 'messages.log' in os.listdir(os.getcwd()):
os.remove('messages.log')
# нам не нужно всё время хранить эти файлы(messages.log и messages.log.offset), они каждый раз будут новые.
except:
pass
try:
if 'messages.log.offset' in os.listdir(os.getcwd()):
os.remove('messages.log.offset')
except:
pass
"""
Приятная фитча которую я обнаружил:
Сначала запустим events.py и server.py, подключимся через nc, отправим несколько сообщений.
После этого, перезапустим events.py не останавливая server.py, все сообщения которые были выведены в events.py раньше,
будут сразу выведены в events.py ещё раз.
"""
while True:
if 'messages.log' in os.listdir(os.getcwd()):
for line in Pygtail('messages.log'):
line = line.strip()
if line == 'leave...':
exit()
print(line)
try:
sleep(.5)
except KeyboardInterrupt:
break
Теперь добавим ещё один код в самое начало(после импортов) файла server.py.
Особо не заостряйте на нём внимания.
Python:
import logging
import os
try:
if 'messages.log' in os.listdir(os.getcwd()):
os.remove('messages.log')
# нам не нужно всё время хранить эти файлы(messages.log и messages.log.offset), пусть они каждый раз будут новые.
except:
pass
try:
if 'messages.log.offset' in os.listdir(os.getcwd()):
os.remove('messages.log.offset')
except:
pass
logger = logging.getLogger(
'writer') # логгер который записывает информация в файл, записанная информация появится в events.py
logger.setLevel('DEBUG')
exit_logger = logging.getLogger(
'exit_logger') # # exit_logger записывает сообщение без форматоров([INFO] ...) в messages.log, это сообщение появится в events.py
exit_logger.setLevel('DEBUG')
writer_handler = logging.FileHandler('messages.log', 'a')
writer_handler.setFormatter(logging.Formatter(fmt='[INFO] {asctime} - {message}', style='{', datefmt='%Y-%m-%d-%H-%M'))
logger.addHandler(writer_handler)
exit_handler = logging.FileHandler('messages.log', 'a')
exit_handler.setFormatter(logging.Formatter(fmt='{message}', style='{'))
exit_logger.addHandler(exit_handler)
Финальный код файла server.py:
Python:
from select import select
import socket
import threading
import logging
import os
try:
if 'messages.log' in os.listdir(os.getcwd()):
os.remove('messages.log')
# нам не нужно всё время хранить эти файлы(messages.log и messages.log.offset), они каждый раз будут новые.
except:
pass
try:
if 'messages.log.offset' in os.listdir(os.getcwd()):
os.remove('messages.log.offset')
except:
pass
logger = logging.getLogger(
'writer') # логер который записывает информация в файл, записанная информация появится в events.py
logger.setLevel('DEBUG')
exit_logger = logging.getLogger(
'exit_logger') # exit_logger записывает сообщение без форматаров([INFO] ...) в messages.log, это сообщение появится в events.py
exit_logger.setLevel('DEBUG')
writer_handler = logging.FileHandler('messages.log', 'a')
writer_handler.setFormatter(logging.Formatter(fmt='[INFO] {asctime} - {message}', style='{', datefmt='%Y-%m-%d-%H-%M'))
logger.addHandler(writer_handler)
exit_handler = logging.FileHandler('messages.log', 'a')
exit_handler.setFormatter(logging.Formatter(fmt='{message}', style='{'))
exit_logger.addHandler(exit_handler)
sockets = [] # сокеты которые мы будем мониторить
socket_server = socket.socket()
socket_server.bind(('localhost', 4444))
socket_server.listen()
sockets.append(socket_server)
def get_raddr(string): # функция которая возвращает адрес клиентского сокета
raddr = string.replace('>', '')
return eval(raddr[raddr.find('raddr')::].replace('raddr=', ''))
def manage_process():
while True:
command = input('>>> ').strip()
if command == 'list':
if len(sockets) == 1:
print('[-] There are not any connection yet')
continue
for index, i in enumerate([sock for sock in sockets if sock is not socket_server]):
raddr = get_raddr(str(i))
print(f'{index + 1}\t{raddr[0]}:{raddr[-1]}')
elif not command: # Если введён пробел или команда пустая(пользователь нажал Enter),то мы выполняем цикл заново# Если введён пробел или команда пустая(пользователь нажал Enter),то мы выполняем цикл заново
continue
elif command == 'leave':
exit_logger.debug('leave...')
return
else:
print('[-] Invalid command')
def event_loop():
while True:
read, _, _ = select(sockets, [], [])
# Нам интересны только те объекты, которые станут доступны для чтения.
# Если клиентский сокет доступен для чтения, из него можно читать.
# Если серверный сокет доступен для чтения, можно делать accept_connection.
for i in read:
if i is socket_server:
conn, addr = socket_server.accept()
logger.debug(f'[+] Got connection from {addr[0]}:{addr[1]}')
sockets.append(conn)
else:
try:
message = i.recv(4096).decode("utf-8").strip('\n')
logger.debug(
f'[+] Got message from {get_raddr(str(i))[1]}:{get_raddr(str(i))[1]} - "{message}"')
except ConnectionResetError: # Если клиент отключился, закрываем соединение удаляем его из sockets
logger.debug(f'[-] Connection {get_raddr(str(i))[0]}:{get_raddr(str(i))[1]} is lost')
i.close()
sockets.remove(i)
threading.Thread(target=event_loop, daemon=True).start()
manage_process()
На сегодня всё, статья получилась большая. Ожидайте продолжение.
Так же, если хотите, ставьте лайки. Они мотивируют меня выпускать статьи быстрее.
В следующей части мы полностью реализуем всё обещанное.
Надеюсь был полезен, пока!
Последнее редактирование: