• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

Статья Асинхронный listener и реализация отслеживания без использования GPS — Подготовка

socks.png


Здравствуй, 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()
Поместите код выше в server.py.
Так же, я вам советую поиграться с этим кодом. Попробуйте подключится к нему через 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 конфликтуют.

use_events.png

Что же делать? - спросите вы.
Я нашёл простое решение этой проблемы:
В файле 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
pygtail читает строки в .log файле, которые не были ещё прочитаны.

Теперь добавим ещё один код в самое начало(после импортов) файла 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)
Осталось заменить все print(message) в функции event_loop на logger.debug(message). Метод logger.debug(message) запишет переданное ему сообщение в наш лог файл(messages.log).

Финальный код файла 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()

На сегодня всё, статья получилась большая. Ожидайте продолжение.
Так же, если хотите, ставьте лайки. Они мотивируют меня выпускать статьи быстрее.
В следующей части мы полностью реализуем всё обещанное.
Надеюсь был полезен, пока!
 
Последнее редактирование:

Сергей Попов

Кодебай
30.12.2015
4 694
6 590
BIT
373
@The Codeby Я хотел прикрепить эту(https://codeby.net/attachments/socks-png.45412/) картинку на обложку, я думал что первая картинка во вложениях автоматически станет обложной
Картинку вставил. Хочу сказать, что вверху статьи лучше поднодят узкие прямоугольные, а не квадратные картинки.
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!