Учебное руководство по разработке приложения на Python: часть 2
22 августа 2024
ID 201594
В этом руководстве описывается реализация обмена данными с 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
). Форматы событий можно настроить на странице Event format settings в веб-интерфейсе Kaspersky CyberTrace.
1-й этап. Определение функции main()
На этом этапе выполняются следующие действия:
- Импорт модуля
socket
.Функции из этого модуля используются для установления соединения с Kaspersky CyberTrace и получения данных.
- Определение функции
main()
. - В переменных
LISTEN_ADDR
иLISTEN_PORT
необходимо указать адрес и порт, которые приложение будет прослушивать в ожидании входящих событий.Эту информацию можно получить в веб-интерфейсе Kaspersky CyberTrace на странице Service settings, в области Service sends events to.
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() |