Учебное руководство по разработке приложения на Python: часть 2
В этом руководстве описывается реализация обмена данными с Kaspersky CyberTrace в приложении на Python.
В части 1 этого руководства описывается приложение, отправляющее данные в Kaspersky CyberTrace.
В части 2 этого руководства описывается приложение, ожидающее получения событий от Kaspersky CyberTrace.
Введение
Эта часть руководства посвящена реализации приложения Python, прослушивающего порт в ожидании событий от Kaspersky CyberTrace.
Для приложения можно использовать любое имя. В примерах в этом руководстве для этого приложения используется имя файла listen_events_cybertrace.py.
О настройках подключения и форматах событий
Приложение будет прослушивать указанный адрес и порт, принимать входящие соединения от Kaspersky CyberTrace и выполнять парсинг полученных данных.
Для определения адреса и порта, на которые отправляются события, в Kaspersky CyberTrace используется параметр OutputSettings > ConnectionString. Адрес и порт для исходящих событий можно просмотреть и настроить на странице Service settings в веб-интерфейсе Kaspersky CyberTrace.
Для определения формата событий Kaspersky CyberTrace использует форматы из раздела OutputSettings конфигурационного файла Kaspersky CyberTrace Service. Каждое событие заканчивается символом новой строки (\n). Форматы событий можно настроить на странице Service alerts settings в веб-интерфейсе Kaspersky CyberTrace.
1-й этап. Определение функции main()
На этом этапе выполняются следующие действия:
- Импорт модуля
socket.Функции из этого модуля используются для установления соединения с Kaspersky CyberTrace и получения данных.
- Определение функции
main(). - В переменных
LISTEN_ADDRиLISTEN_PORTнеобходимо указать адрес и порт, которые приложение будет прослушивать в ожидании входящих событий.Эту информацию можно получить в веб-интерфейсе Kaspersky CyberTrace на странице General settings, в области Входящие события.
import socket
LISTEN_ADDR = "192.0.2.105"
LISTEN_PORT = 9998
def main():
pass
if __name__ == '__main__':
main()
2-й этап. Добавление код сервера
На этом этапе выполняются следующие действия:
- Добавление в функцию
main()кода, запускающего сервер, прослушивающий указанные адрес и порт.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("Starting on {}:{}".format(LISTEN_ADDR, LISTEN_PORT))
server_socket.bind((LISTEN_ADDR, LISTEN_PORT))
server_socket.listen()
- Добавление кода, обрабатывающего входящие соединения.
Функция
socket.accept()принимает входящее соединение. Она возвращает новый объект сокета для соединения (в переменнойconnection) и адрес на другом конце, инициировавший соединение (в переменнойclient_address).На этом этапе приложение игнорирует данные, полученные в результате соединений. Парсинг будет реализован на следующем этапе.
Приложение запускает бесконечный цикл. Его можно завершить стандартной комбинацией клавиш или командой операционной системы. В большинстве операционных систем это ^C или Ctrl+C.
while True:
connection, client_address = server_socket.accept()
try:
print("Connection from {}".format(client_address))
# Сюда добавляется парсинг данныхfinally:
connection.close()
3-й этап. Парсинг полученных данных
На этом этапе выполняются следующие действия:
- В блоке
try... finallyвызывается функцию, которая выполняет парсинг полученных данных на события.Поскольку в рамках каждого соединения может быть отправлено любое количество событий, приложение должно продолжать парсинг полученных данных, пока не будут обработаны все события. Функция
parse_response()возвращает генератор. Затем этот генератор используется в циклеforдля итерации по полученным событиям. На следующем шаге реализуетсяфункция parse_response().try:
print("Connection from {}".format(client_address))
for event in parse_response(connection):
print("Received:\n{}".format(event.decode()))
finally:
connection.close()
- Реализация функции
parse_response().События Kaspersky CyberTrace оканчиваются символом новой строки (
\n). Функцияparse_response()считывает данные из сокетного соединения в буфер до тех пор, пока в буфере не окажется символ новой строки. Затем функция выделяет событие из буфера и возвращает его. Функцияchunk_receive()выполняет итерацию по данным из сокетного соединения. Эта функция будет реализована на следующем этапе.Когда цикл
forиз функцииmain()переходит к следующей итерации, функцияparse_response()выделяет из буфера очередное событие и возвращает его. Если это невозможно, функция считывает дополнительные данные из соединения в буфер. Процесс повторяется до тех пор, пока из соединения не перестанут поступать данные и все события в буфере не будут возвращены.def parse_response(connection):
buff = b''
for data in chunk_receive(connection):
buff += data
while b'\n' in buff:
event, buff = buff.split(b'\n',1)
yield event
- Реализация функции
chunk_receive().Функция
chunk_receive()считывает 4096 байт данных из соединения и возвращает эти данные. Функция возвращает данные до тех пор, пока в соединении не закончатся данные.Функция
socket.recv()получает данные из соединения. Если данных нет, функция ожидает получения новых данных. При закрытии подключения функция возвращает ноль байтов, поэтому при выполнении этого условия циклwhileзавершается.def chunk_receive(connection):
data = None
while data != b'':
data = connection.recv(4096)
yield data
Этап 5. (Необязательный) Изменение приложения, отправляющее события
Чтобы отправлять события в Kaspersky CyberTrace с помощью приложения, созданного в части 1 этого руководства, измените его так, чтобы события отправлялись без ожидания ответа.
На этом этапе выполняются следующие действия:
- Закомментируйте или удалите строку в коде приложения, которая отправляет флаги. Теперь Kaspersky CyberTrace отправляет события приложению-слушателю, поэтому флаг
X-KF-ReplyBackстановится излишним. - Закомментируйте или удалите строки, которые получают и выводят ответ.
Ниже приведен блок try... finally из приложения с закомментированными строками.
try: ct_socket.connect((CYBERTRACE_ADDR, CYBERTRACE_PORT)) #ct_socket.sendall(b'X-KF-SendFinishedEventX-KF-ReplyBack')
for event in events: print("Sending:\n{}".format(event)) ct_socket.sendall(event.encode()) #response = ct_socket.recv(16384) #print("Response:\n{}".format(response.decode()))
finally: ct_socket.close()
|
6-й этап. Запуск приложения
Запуск приложения из консоли:
python3 ./listen_events_cybertrace.py
Приложение прослушивает порт в ожидании событий обнаружения киберугроз и оповещений о событиях из Kaspersky CyberTrace и выводит их в консоль.
Если требуется отправлять события в Kaspersky CyberTrace с помощью приложения, разработанного в части 1 этого руководства, запустите это приложение из консоли:
python3 ./send_events_cybertrace.py
Ниже приведен пример вывода приложения.
Starting on 192.0.2.105:9998 Connection from ('192.0.2.42', 41882) Received: - category=KL_Malicious_Hash_MD5 matchedIndicator=776735A8CA96DB15B422879DA599F474 url=- src=- ip=- md5=776735A8CA96DB15B422879DA599F474 sha1=- sha256=- usrName=- confidence=100 MD5=776735A8CA96DB15B422879DA599F474 SHA1=3B66A1D70562E291DA023E87B349DD89DFE00213 SHA256=1963CBCBB9FDAAD45F782FAAA467EE2C115C3111C003AE14D01181880B03F6ED file_size=1431 first_seen=10.07.2015 23:53 last_seen=14.07.2020 13:35 popularity=1 threat=HEUR:Trojan.Win32.Generic Received: - category=KL_IP_Reputation matchedIndicator=192.0.2.1 url=- src=- ip=192.0.2.1 md5=- sha1=- sha256=- usrName=- confidence=100 category=test first_seen=01.01.2017 00:00 ip=192.0.2.1 ip_geo=ru last_seen=17.07.2020 09:02 popularity=1 threat_score=75 Received: - category=KL_Malicious_Hash_MD5 matchedIndicator=FEAF2058298C1E174C2B79AFFC7CF4DF url=- src=- ip=- md5=FEAF2058298C1E174C2B79AFFC7CF4DF sha1=- sha256=- usrName=- confidence=100 MD5=FEAF2058298C1E174C2B79AFFC7CF4DF SHA1=D01D17F6B13C7255A234F558ED85078EA5DD3F3D SHA256=4CA914C9791CF2BF2AC69F9A2B21006F0361E247F2CE92F0A9F166DBC6B43670 file_size=1989 first_seen=10.07.2015 23:53 last_seen=14.07.2020 13:35 popularity=1 threat=HEUR:Trojan.Win32.Generic Received: - category=KL_IP_Reputation matchedIndicator=192.0.2.3 url=- src=- ip=192.0.2.3 md5=- sha1=- sha256=- usrName=- confidence=100 category=test first_seen=15.01.2017 00:00 ip=192.0.2.3 ip_geo=ru last_seen=17.07.2020 08:51 popularity=1 threat_score=75 |
Полный код для части 2
Ниже приведен полный код для части 2 этого руководства.
import socket
LISTEN_ADDR = "192.0.2.105" LISTEN_PORT = 9998
def chunk_receive(connection): data = None while data != b'': data = connection.recv(4096) yield data
def parse_response(connection): buff = b'' for data in chunk_receive(connection): buff += data while b'\n' in buff: event, buff = buff.split(b'\n',1) yield event
def main():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print("Starting on {}:{}".format(LISTEN_ADDR, LISTEN_PORT)) server_socket.bind((LISTEN_ADDR, LISTEN_PORT)) server_socket.listen()
while True: connection, client_address = server_socket.accept() try: print("Connection from {}".format(client_address)) for event in parse_response(connection): print("Received:\n{}".format(event.decode())) finally: connection.close()
if __name__ == '__main__':
main() |