Хакер - Змей-стражник. Сигнализация для велосипеда из старого смартфона на Android

Хакер - Змей-стражник. Сигнализация для велосипеда из старого смартфона на Android

hacker_frei

https://t.me/hacker_frei

rexby

Содержание статьи

  • Termux
  • Termux:API
  • Предварительная подготовка
  • Пишем код
  • Проходим сквозь стену
  • Серверная часть
  • Выводы

На­вер­няка у тебя где‑то завалял­ся ста­рый смар­тфон на Android, а может, даже и не один. Я рас­ска­жу, как сде­лать из него надеж­ную сиг­нализа­цию для тво­его велоси­педа, да и вооб­ще для любого дви­жимо­го и нед­вижимо­го иму­щес­тва. Прог­рамми­ровать будем на Python 3, при этом набор фун­кций при­ложе­ния без тру­да может быть изме­нен и рас­ширен для решения дру­гих насущ­ных задач.

Не­дав­но я закон­чил сбор­ку сво­его элек­тро­вело­сипе­да. Теперь я могу не толь­ко колесить в близ­лежащих пар­ках, но и исполь­зовать его в качес­тве пол­ноцен­ного средс­тва перед­вижения. Ско­рость до 55 км/ч поз­воля­ет уве­рен­но дер­жать­ся в город­ском потоке машин, а бес­шумность, отсутс­твие запаха бен­зина плюс неболь­шая мас­са — спо­кой­но ездить по пар­кам и тро­туарам, а так­же хра­нить тран­спорт дома. В общем, сплош­ные плю­сы. Ког­да надо при­пар­ковать­ся и пой­ти по делам, на раме моего велоси­педа защел­кива­ется U-образный замок Kryptonite. Одна­ко мотор и новень­кий акку­муля­тор уже не дела­ют раму самой дорогой частью велоси­педа. И поэто­му остро вста­ет воп­рос защиты тран­спортно­го средс­тва от тех, кто тоже сле­дует за прог­рессом, но не жела­ет на него тра­тить­ся. Короче говоря, от воров.

Име­ющиеся в про­даже сиг­нализа­ции для велоси­педов прос­то отпу­гива­ют воришек гром­ким зву­ком. Поэто­му я решил сде­лать собс­твен­ное устрой­ство с обратной связью. Пер­вое, что приш­ло в голову, — раз­работать сиг­нализа­цию на мик­рокон­трол­лере. Но затем я подумал: почему бы не исполь­зовать ста­рый смар­тфон, который лежит без дела? Ведь у него на бор­ту уже есть все необ­ходимое, что­бы решить пос­тавлен­ную задачу: мобиль­ный интернет, GPS для опре­деле­ния мес­тополо­жения, раз­личные дат­чики для обна­руже­ния внеш­них воз­дей­ствий, камера и сен­сорный экран. В общем, воп­рос с железом был решен, оста­лось толь­ко реали­зовать прог­рам­мную часть. Что­бы луч­ше понять прин­цип работы опи­сан­ного в статье устрой­ства, можешь сра­зу пос­мотреть демонс­тра­цию его работы.

TERMUX

Ес­ли ты еще не слы­шал про Termux, то сроч­но отправ­ляй­ся изу­чать мат­часть. «Хакер» уже пи­сал на эту тему. Termux — это эму­лятор тер­минала для опе­раци­онной сис­темы Android. Но пус­кай тебя не сму­щает сло­во «эму­лятор», весь код выпол­няет­ся натив­но.

Как ты зна­ешь, в осно­ве Android — ядро Linux. Termux добав­ляет к ядру минималь­ное окру­жение в виде набора ути­лит и интер­пре­тато­ра Bash. Этот набор может быть лег­ко рас­ширен хорошо извес­тной поль­зовате­лям Linux коман­дой apt install <package> (или pkg install <package>). При этом спи­сок пакетов дос­таточ­но обширный: от кон­соль­ного редак­тора Vim до окон­ной сис­темы X11 и сре­ды рабоче­го сто­ла Xfce. Все это работа­ет без прав супер­поль­зовате­ля!

Кро­ме того, всег­да есть воз­можность написать свою прог­рамму на С или даже язы­ке ассем­бле­ра. Ком­пилятор GCC так­же мож­но уста­новить из пакетов. Если есть пра­ва root, все ста­новит­ся еще инте­рес­нее: мож­но под­клю­чить репози­торий root-repo и получить дос­туп к таким прог­раммам, как tcpdump или Aircrack-ng. Для наших же целей root-пра­ва не пот­ребу­ются: все, что нам нуж­но, — это уста­новить интер­пре­татор язы­ка Python 3. К счастью, нуж­ный пакет уже име­ется в репози­тории.

Termux я рекомен­дую уста­новить из магази­на соф­та с откры­тым исходным кодом F-Droid. Если у тебя в телефо­не еще нет это­го магази­на, отправ­ляй­ся на сайт f-droid.org и ска­чивай соот­ветс­тву­ющее при­ложе­ние. Далее в самом F-Droid в стро­ке поис­ка напиши Termux и уста­нови сле­дующие при­ложе­ния: Termux, Termux:API и Termux:Widget. Если Android на тво­ем телефо­не ниже седь­мого, то тебе понадо­бит­ся уже архивная вер­сия. Ее ты можешь взять тут. Пос­ле уста­нов­ки запус­кай Termux и инстал­лируй необ­ходимые пакеты и модули питона, которые нам понадо­бят­ся. Для это­го в открыв­шемся тер­минале набери сле­дующие коман­ды:

pkg update

pkg install termux-api

pkg install openssh

pkg install qrencode

pkg install python

pkg install pip

pip install pystun3

Об­рати вни­мание, что pystun3 в пос­ледней стро­ке — это модуль питона, поэто­му мы уста­нови­ли его коман­дой pip install, а не pkg install.

TERMUX:API

Ко­ман­дой pkg install termux-api мы уста­нови­ли под­дер­жку Termux-API, что поз­воля­ет вза­имо­дей­ство­вать с сис­темой Android из коман­дной стро­ки: делать фото, записы­вать и вос­про­изво­дить звук, подавать виб­росиг­нал, получать информа­цию с дат­чиков телефо­на, выводить диало­говые окна и про­чее. Что­бы изу­чить все воз­можнос­ти Termux-API, набери в коман­дной стро­ке termux-, наж­ми два раза Tab, и уви­дишь все дос­тупные ути­литы. Фун­кци­ональ­нос­ти впол­не дос­таточ­но, что­бы орга­низо­вать вза­имо­дей­ствие с поль­зовате­лем с помощью всплы­вающих сооб­щений (termux-toast), диало­говых окон (termux-dialog) и уве­дом­лений (termux-notification).

Для сиг­нализа­ции нам понадо­бит­ся информа­ция с дат­чика уско­рения — аксе­леро­мет­ра. Для это­го пос­мотрим сна­чала, какие дат­чики при­сутс­тву­ют в телефо­не:

termux-sensor -l

В получен­ном спис­ке дат­чиков ищи тот, в наз­вании которо­го есть сло­во accelerometer. Нап­ример, в моем телефо­не име­ется BMI160 Accelerometer и Linear Acceleration. Теперь мож­но поп­робовать опро­сить дат­чик такой коман­дой:

termux-sensor -s "BMI160 Accelerometer" -n 1

В моем слу­чае вывод этой коман­ды име­ет сле­дующий вид:

{

"BMI160 Accelerometer": {

"values": [

0.35223388671875,

0.20556640625,

9.873580932617188

]

}

}

Об­рати вни­мание на две вещи. Во‑пер­вых, вывод информа­ции по одно­му изме­рению занима­ет девять строк. В даль­нейшем мы исполь­зуем это для пар­синга выход­ного потока с дат­чика уско­рения. Во‑вто­рых, уско­рение выводит­ся в виде его ком­понен­тов по трем прос­транс­твен­ным коор­динатам. Если ты еще не забыл школь­ный курс физики, то уже сооб­разил, что сум­ма квад­ратов этих величин даст нам квад­рат уско­рения. Его‑то мы и будем вычис­лять для опре­деле­ния дви­жения телефо­на.

Ес­ли в пре­дыду­щей коман­де опус­тить параметр -n, то дан­ные с дат­чика будут выводить­ся неп­рерыв­но с интерва­лом в одну секун­ду. Интервал, в свою оче­редь, мож­но изме­нить, добавив ключ -d <число миллисекунд>. Этим мы и вос­поль­зуем­ся в прог­рамме. Дат­чик уско­рения дос­таточ­но чувс­тви­тель­ный, что­бы опре­делить не толь­ко переме­щение тво­его девай­са, но и виб­рацию от лег­кого уда­ра по сто­лу, на котором лежит устрой­ство.

ПРЕДВАРИТЕЛЬНАЯ ПОДГОТОВКА

Пи­сать код мож­но пря­мо на телефо­не, уста­новив любой тек­сто­вый редак­тор, нап­ример Vim или Nano. Но про­ще делать это на компь­юте­ре, а затем отпра­вить прог­рамму по SSH на телефон. Для это­го в файл ~/.ssh/authorized_keys нуж­но положить свой ключ SSH. Пос­ле чего в кон­соли Termux запус­тить демон sshd, вве­дя одно­имен­ную коман­ду. Учти, что демон sshd слу­шает нес­тандар­тный порт 8022, пос­коль­ку у него нет при­виле­гий супер­поль­зовате­ля.

Те­перь мож­но с ПК получить дос­туп к телефо­ну коман­дой ssh <IP-адрес телефона> -p 8022. Обра­ти вни­мание, что имя поль­зовате­ля мож­но не ука­зывать, Termux при­мет любое. Узнать IP-адрес телефо­на в тво­ей локаль­ной сети мож­но, вве­дя коман­ду ifconfig в кон­соли Termux. Если ты поль­зователь Windows, то для под­клю­чения исполь­зуй кли­ент Putty. Для пересыл­ки фай­ла прог­раммы из текуще­го катало­га на компь­юте­ре в Termux дос­таточ­но в тер­минале на ком­пе ввес­ти такую коман­ду:

cat program.py | ssh <IP-адрес телефона> -p 8022 "cat > program.py"

для Linux или такую для Windows:

set PATH=C:\путь\до\папки\putty;%PATH%

pscp -P 8022 program.py <IP-адрес телефона>

Ес­ли телефон под­клю­чен к домаш­ней сети Wi-Fi, Termux не очень отзывчив при соеди­нении по SSH. По всей видимос­ти, это свя­зано с перехо­дом передат­чика в телефо­не в энер­госбе­рега­ющий режим. Если ты тоже стол­кнешь­ся с такой проб­лемой, ее мож­но решить коман­дой iw dev wlan0 set power_save off, выпол­ненной от рута. А если таких прав нет, ты можешь под­клю­чить­ся к телефо­ну либо по USB-кабелю, вклю­чив режим USB-модема, либо по Wi-Fi, переве­дя телефон в режим точ­ки дос­тупа.

ПИШЕМ КОД

Прог­рамма сос­тоит из двух час­тей: кли­ент­ской и сер­верной. Кли­ент будет работать на телефо­не, зак­реплен­ном на велоси­педе, и пери­оди­чес­ки отправ­лять UDP-пакеты сер­веру — телефо­ну, который находит­ся рядом с тобой. В содер­жимом пакета может быть либо KNOCK, либо ALARM. Получив сооб­щение KNOCK, сер­вер будет знать, что кли­ент работа­ет штат­но и не име­ет проб­лем с сетью. В слу­чае сооб­щения ALARM будем про­игры­вать заранее под­готов­ленный файл со зву­ком сирены.

Итак, прис­тупа­ем к написа­нию кли­ент­ской час­ти прог­раммы, которую назовем client_alarm.py. Сна­чала сде­лаем импорт необ­ходимых модулей.

#!/data/data/com.termux/files/usr/bin/env python3

import socket

import json

from subprocess import Popen, PIPE

import os

import time

from nat import nat_traversal

Пер­вая стро­ка — это шебанг. Что­бы каж­дый раз не писать такой длин­ный путь, можешь исполь­зовать стан­дар­тный для Linux /usr/bin/env python3, а потом выз­вать ути­литу termux-fix-shebang client_alarm.py. Она испра­вит путь до прог­раммы env на под­ходящий для Termux. Далее зададим необ­ходимые кон­стан­ты.

AVERAGE = 10

KNOCK_DELAY = 5

SENSOR_NAME = 'BMI160 Accelerometer'

ACCELEROMETER_DELAY = 20

ACCELERATION_THRESHOLD = 0.1

DELAY_AFTER_ALARM = 1

RPORT = 0

RHOST = ''

Здесь AVERAGE — чис­ло изме­рений уско­рения, по которым мы будем вычис­лять сред­нее зна­чение. Усредне­ние нуж­но для более ста­биль­ной работы, что­бы исклю­чить лож­ные сра­баты­вания сиг­нализа­ции. KNOCK_DELAY — задер­жка в секун­дах меж­ду «отсту­кива­нием» на сер­вер. ACCELEROMETER_DELAY — задер­жка в мил­лисекун­дах, которая будет переда­на коман­де termux-sensorACCELERATION_THRESHOLD — важ­ный параметр, который опре­деля­ет порог сра­баты­вания сиг­нализа­ции. Если сде­лать его слиш­ком малень­ким, то чувс­тви­тель­ность воз­растет, но при этом мож­но получить лож­ные сра­баты­вания. Рекомен­дую подоб­рать этот параметр экспе­римен­таль­но так, что­бы добить­ся нуж­ной тебе чувс­тви­тель­нос­ти. DELAY_AFTER_ALARM — пауза пос­ле каж­дого сра­баты­вания сиг­нализа­ции. Она нуж­на, что­бы не «засыпать» сер­вер пакета­ми с сиг­налом ALARM. RHOST и RPORT — адрес, куда будем отправ­лять пакеты.

И тут воз­ника­ет проб­лема: что­бы орга­низо­вать ком­муника­цию двух устрой­ств по сети, хотя бы одно из них дол­жно иметь пуб­личный (еще говорят «белый») IP-адрес. Для это­го нуж­но либо арен­довать VPS-сер­вер, либо заказать соот­ветс­тву­ющую услу­гу у тво­его интернет‑про­вай­дера. Пос­ле чего мож­но обес­печить пря­мую видимость устрой­ств, под­клю­чив их к собс­твен­ному сер­веру, нап­ример через OpenVPN или Wireguard. Как это сде­лать, «Хакер» писал здесь. Имен­но так я и сде­лал. Но если у тебя еще нет арен­дован­ного сер­вера, то не спе­ши расс­тра­ивать­ся. Я рас­ска­жу, как соеди­нить два находя­щих­ся за NAT устрой­ства по сети нап­рямую.

Проходим сквозь стену

NAT (network address translation) — это механизм тран­сля­ции сетевых адре­сов, который поз­воля­ет нес­коль­ким устрой­ствам под­клю­чать­ся к хос­там в интерне­те, исполь­зуя один общий IP-адрес. На сегод­няшний день имен­но тех­нология NAT дает воз­можность более чем 20 мил­лиар­дам устрой­ств по все­му миру иметь дос­туп в гло­баль­ную сеть, при том что «белых», то есть мар­шру­тизи­руемых в интерне­те, IPv4-адре­сов все­го чуть более четырех мил­лиар­дов.

У раз­ных про­вай­деров NAT может быть нас­тро­ен по‑раз­ному. Для соеди­нения устрой­ств мы будем исполь­зовать STUN (Session Traversal Utilities for NAT) — спе­циаль­ный сетевой про­токол, который поз­воля­ет кли­енту за NAT узнать свой внеш­ний IP-адрес, а так­же спо­соб тран­сля­ции пор­та во внеш­ней сети. В дан­ном слу­чае за нас все сде­лает фун­кция get_ip_info() модуля pystun3, который мы уста­нови­ли ранее.

При вызове дан­ной фун­кции будет сде­лано нес­коль­ко зап­росов на раз­ные адре­са сер­веров STUN. В отве­тах на зап­росы сер­вер вер­нет информа­цию о том, с какого IP-адре­са и пор­та к нему обра­щались. Для наших целей дос­таточ­но, что­бы при обра­щении на опре­делен­ный порт раз­ных хос­тов сети с одно­го и того же внут­ренне­го пор­та про­вай­дер наз­начал нам один и тот же внеш­ний порт. Такой тип NAT называ­ется Restricted NAT. Если же при обра­щении к раз­ным хос­там каж­дый раз будут наз­начать­ся раз­ные внеш­ние пор­ты, то под­клю­чение извне к такому устрой­ству вызовет серь­езные труд­ности. Такой тип NAT называ­ется Symmetric NAT.

Из про­верен­ных мной опе­рато­ров «боль­шой чет­верки» сим­метрич­ный NAT ока­зал­ся толь­ко у «Билай­на». Так что если ты обла­датель сим‑кар­ты дру­гих опе­рато­ров свя­зи, то все дол­жно получить­ся.

INFO

Статья про раз­ные типы NAT и спо­собы их обхо­да

Итак, для пря­мого соеди­нения двух телефо­нов напишем фун­кцию nat_traversal. Ее мож­но помес­тить в отдель­ный файл nat.py, так как эта фун­кция будет общая для обе­их прог­рамм — кли­ент­ской и сер­верной. Пос­ле чего необ­ходимо импорти­ровать фун­кцию коман­дой from nat import nat_traversal.

def nat_traversal():

from socket import socket, AF_INET, SOCK_DGRAM, timeout

import stun

import json

import os

import sys

import subprocess

import time

LPORT = 65000

LHOST = '0.0.0.0'

nat = stun.get_ip_info(LHOST, LPORT)

nat_json = json.dumps(nat)

os.system("clear")

os.system(f"echo '{nat_json}' | qrencode -t ansiutf8")

print(f"Your host parameters: {nat}")

os.system("termux-clipboard-set '' ")

print("Please, copy remote host info to your clipboard ...")

remote_nat = ''

for i in range(12):

cb = subprocess.getoutput('termux-clipboard-get')

try:

remote_nat = json.loads(cb)

if remote_nat:

break

except json.decoder.JSONDecodeError:

pass

time.sleep(5)

else:

print("Error: can't get remote host info", file=sys.stderr)

sys.exit(1)

print(f"Got remote host parameters: {remote_nat}")

fnat_type, rip, rport = remote_nat

sock = socket(AF_INET, SOCK_DGRAM)

sock.bind((LHOST, LPORT))

sock.settimeout(5)

for i in range(12):

try:

sock.sendto(b'NAT\n', (rip, rport))

sock.recv(1024)

except timeout:

pass

else:

sock.sendto(b'NAT\n', (rip, rport))

break

else:

print(f"Error: can't connect to remote host {rip}:{rport}", file=sys.stderr)

sys.exit(1)

print(f"Connection established with {rip}:{rport}")

sock.settimeout(None)

return rip, rport, sock

Фун­кция nat_tarversal пер­вым делом вызыва­ет фун­кцию get_ip_info модуля pystun3, которая вер­нет три зна­чения: тип NAT, внеш­ний IP-адрес и внеш­ний порт, соот­ветс­тву­ющий нашему внут­ренне­му пор­ту LPORT. Пос­ле опре­деле­ния внеш­него IP-адре­са и пор­та на обо­их телефо­нах необ­ходимо про­извести обмен этой информа­цией меж­ду устрой­ства­ми. Для это­го будем выводить в кон­соль резуль­тат работы фун­кции get_ip_info в виде QR-кода.

В этом нам поможет уста­нов­ленная ранее ути­лита qrencode. Пос­ле вывода QR-кода будем ожи­дать дан­ных о вто­ром устрой­стве в буфере обме­на. Для это­го пред­варитель­но очис­тим его с помощью termux-clipboard-set ''. Если в течение при­мер­но минуты в буфере не появит­ся нуж­ных дан­ных, то фун­кция выведет ошиб­ку и завер­шит работу прог­раммы. Таким обра­зом, что­бы соеди­нить по сети два наших устрой­ства, на каж­дом из них пос­ле запус­ка прог­раммы необ­ходимо с помощью ска­нера про­читать QR-код с экра­на дру­гого телефо­на.

Для этих целей можешь исполь­зовать опен­сор­сный ска­нер с F-Droid или ска­чать ана­лог с Play Market. В нас­трой­ках при­ложе­ния пос­тавь галоч­ки «Копиро­вать в буфер обме­на» и «Авто­запуск ска­нера» для удобс­тва исполь­зования. Недос­таток дан­ного метода в том, что в момент под­клю­чения оба устрой­ства дол­жны рас­полагать­ся рядом, что­бы иметь воз­можность отска­ниро­вать QR-коды. Ты без тру­да можешь исполь­зовать любой дру­гой канал для обме­на, нап­ример СМС с помощью команд termux-sms-send и termux-sms-list.

Ес­ли дан­ные о вто­ром хос­те были успешно про­чита­ны из буфера обме­на, попыта­емся уста­новить соеди­нение с ука­зан­ным хос­том в течение минуты. Для это­го пери­оди­чес­ки будем отправ­лять в адрес вто­рого хос­та UDP-пакеты со стро­кой NAT. Пер­вые отправ­ленные пакеты с одно­го из устрой­ств (кли­ента или сер­вера) сна­чала не будут дос­тигать наз­начения, пос­коль­ку в таб­лице NAT на при­нима­ющей сто­роне еще нет пра­вила, опре­деля­юще­го, куда переда­вать при­шед­шие пакеты. Но как толь­ко со вто­рой сто­роны будет передан хотя бы один пакет, двус­торон­няя связь будет уста­нов­лена. Фун­кция nat_traversal отчи­тает­ся об этом выводом сооб­щения Connection established with host:port и вер­нет в основную прог­рамму адрес и порт уда­лен­ного хос­та, а так­же соз­данный ею сокет.

Сто­ит ска­зать, что соеди­нение устрой­ств «нап­рямую», минуя про­межу­точ­ные хос­ты, час­то исполь­зует­ся в IP-телефо­нии, нап­ример ког­да ты говоришь по WhatsApp. Такой под­ход поз­воля­ет сни­зить наг­рузку на сер­вер, а так­же сущес­твен­но умень­шить сетевой мар­шрут для пакетов дан­ных.

В основной прог­рамме вызыва­ем фун­кцию termux-wake-lock, которая пре­дот­вра­щает «засыпа­ние» при­ложе­ния Termux. Затем соз­дадим сокет, исполь­зуя пару RHOST:RPORT, а если она не задана, то вызовем толь­ко что написан­ную фун­кцию nat_traversal():

os.system('termux-wake-lock')

if RHOST and RPORT:

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

else:

RHOST, RPORT, sock = nat_traversal()

Да­лее добавим в кли­ент­скую часть прог­раммы фун­кцию для получе­ния зна­чения уско­рения.

def sensor_get():

one_measure = ''

while not one_measure:

for i in range(9):

one_measure += p.stdout.readline().decode('utf-8')

if one_measure == '{}\n':

one_measure = ''

break

if one_measure:

data = json.loads(one_measure)

a = data[SENSOR_NAME]['values']

return a

Фун­кция sensor_get() получа­ет зна­чение уско­рения из стан­дар­тно­го вывода коман­ды termux-sensor, которую мы запус­тим в отдель­ном потоке:

os.system('termux-sensor -c')

p = Popen(['/data/data/com.termux/files/usr/bin/termux-sensor', '-s', SENSOR_NAME, '-d', str(ACCELEROMETER_DELAY)], stdin=PIPE, stdout=PIPE)

Вы­зов termux-sensor -c под­готовит дат­чики в слу­чае, если рань­ше работа с ними была завер­шена некор­рек­тно.

Да­лее в бес­конеч­ном цик­ле мы будем получать AVERAGE изме­рений с дат­чика уско­рения и каж­дый раз будем вычис­лять da — раз­ницу текуще­го и пре­дыду­щего зна­чений (если быть точ­ным, da — это квад­рат раз­ности век­торов уско­рений: текуще­го и пре­дыду­щего). Вычис­ленные зна­чения da будем скла­дывать в перемен­ную acceleration. Если пос­ле усредне­ния (acceleration /= AVERAGE) уско­рение пре­высит пре­дел, задан­ный в ACCELERATION_THRESHOLD, то на сер­вер будет отправ­лена стро­ка ALARM и сде­лана задер­жка, что­бы дать сер­веру вре­мя отре­аги­ровать на при­шед­шее сооб­щение.

Ес­ли же с телефо­ном ничего не про­исхо­дит, то все рав­но по про­шес­твии вре­мени KNOCK_DELAY мы будем отправ­лять на сер­вер стро­ку KNOCK, что­бы он «знал», что сиг­нализа­ция и сеть работа­ют исправ­но. В слу­чае пря­мого соеди­нения устрой­ств пери­оди­чес­кая отправ­ка пакетов на сер­вер, кро­ме все­го про­чего, необ­ходима, что­бы из таб­лицы NAT про­вай­дера не исчезло пра­вило тран­сля­ции, соз­данное фун­кци­ей nat_traversal. В ответ сер­вер будет отправ­лять сооб­щение OK, которое так­же слу­жит для под­держа­ния пра­вила NAT на дру­гой сто­роне.

a_prev = sensor_get()

print('sensor ready!')

t_prev = time.time()

while True:

try:

acceleration = 0

for i in range(AVERAGE):

a = sensor_get()

da = sum( (ai - a_prev_i)**2 for ai, a_prev_i in zip(a, a_prev))

acceleration += da

a_prev = a

acceleration /= AVERAGE

if acceleration > ACCELERATION_THRESHOLD:

sock.sendto(b'ALARM\n', (RHOST, RPORT))

print('ALARM')

time.sleep(DELAY_AFTER_ALARM)

t_cur = time.time()

if t_cur - t_prev > KNOCK_DELAY:

sock.sendto(b'KNOCK\n', (RHOST, RPORT))

print('KNOCK')

t_prev = t_cur

except KeyboardInterrupt:

break

p.send_signal(2)

sock.close()

За­вер­шить работу прог­раммы мож­но, нажав Ctrl + C. Пос­ле чего мы вый­дем из бес­конеч­ного цик­ла чте­ния дан­ных с дат­чика уско­рения, а про­цес­су, опра­шива­юще­му дат­чик, будет пос­лан сиг­нал 2 (SIGINT).

На этом кли­ент­ская прог­рамма завер­шена. Оста­лось написать сер­верную часть, которую мы назовем server_alarm.py.

СЕРВЕРНАЯ ЧАСТЬ

Нач­нем с импорта модулей и задания кон­стант.

#!/data/data/com.termux/files/usr/bin/env python3

import threading

import socket

import time

import os

from nat import nat_traversal

LPORT = 0

LHOST = '0.0.0.0'

WAIT_FOR_KNOCK_TIME = 15

DIR_NAME = '/data/data/com.termux/files/home/alarm/'

ALARM_FILE_NAME = 'alarm_short.mp3'

LINK_LOSS_FILE_NAME = 'link_loss.mp3'

LINK_RESTORE_FILE_NAME = 'link_restored.mp3'

KNOCK_FLAG = 0

KNOCK_WARN = 1

Здесь, по ана­логии с кли­ент­ской прог­раммой, можешь задать LPORT, отличный от нуля, и LHOST = '0.0.0.0', тог­да сер­вер будет слу­шать задан­ный порт на всех сетевых интерфей­сах. Либо оставь LPORT нулевым, тог­да будет выз­вана фун­кция nat_traversal. Параметр WAIT_FOR_KNOCK_TIME зада­ет интервал, в течение которо­го дол­жен пос­тупить оче­ред­ной пакет KNOCK от кли­ента. Если пакет не при­дет вов­ремя, будет сде­лано опо­веще­ние, что связь с кли­ент­ской прог­раммой потеря­на.

Па­раметр DIR_NAME зада­ет путь к пап­ке со зву­ковы­ми фай­лами, а ALARM_FILE_NAMELINK_LOSS_FILE_NAME и LINK_RESTORE_FILE_NAME — наз­вания самих фай­лов для опо­веще­ния о тре­воге, потере и вос­ста­нов­лении свя­зи соот­ветс­твен­но.

Скрин­шот с телефо­на, на котором работа­ет сер­верный скрипт

При­нимать UDP-пакет будем в отдель­ном потоке. Для это­го соз­дадим класс — нас­ледник threading.Thread:

class UDP_receive(threading.Thread):

def run(self):

global KNOCK_FLAG

while True:

data, addr = sock.recvfrom(1024)

if data == b'KNOCK\n':

print(data.decode('utf-8'), end = '')

KNOCK_FLAG = 0

sock.sendto(b'OK\n', addr)

elif data == b'ALARM\n':

print(data.decode('utf-8'), end = '')

sock.sendto(b'OK\n', addr)

os.system('termux-media-player play ' + DIR_NAME + ALARM_FILE_NAME)

else:

print('Unknown data:')

print(data.decode('utf-8'))

В слу­чае при­ема сооб­щения KNOCK будем очи­щать флаг KNOCK_FLAG, а в слу­чае получе­ния ALARM сра­зу бить тре­вогу: вос­про­изво­дить файл со зву­ком сирены с помощью коман­ды termux-media-player play.

В основной час­ти прог­раммы вызовем termux-wake-lock, что­бы в фоне или при вык­лючен­ном экра­не наше при­ложе­ние оста­валось активно. Соз­дадим сокет и поток, в котором будем его слу­шать.

os.system('termux-wake-lock')

if LHOST and LPORT:

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

sock.bind((LHOST, LPORT))

else:

RHOST, RPORT, sock = nat_traversal()

thread = UDP_receive()

thread.start()

Да­лее соз­дадим основной цикл прог­раммы, вый­ти из которо­го мож­но c помощью ком­бинации Ctrl + C.

print('Starting alarm server... ')

while True:

try:

KNOCK_FLAG = 1

time.sleep(WAIT_FOR_KNOCK_TIME )

if KNOCK_FLAG:

KNOCK_WARN = 1

print('Связь потеряна')

os.system('termux-media-player play ' + DIR_NAME + LINK_LOSS_FILE_NAME)

else:

if KNOCK_WARN:

KNOCK_WARN = 0

print('Связь установлена')

os.system('termux-media-player play ' + DIR_NAME + LINK_RESTORE_FILE_NAME)

except KeyboardInterrupt:

break

sock.close()

os.kill(thread._native_id, 15)

Флаг KNOCK_WARN уста­нав­лива­ется, если поль­зователь уже был опо­вещен о потере свя­зи. Ког­да связь вос­ста­новит­ся (если при­дет оче­ред­ной пакет KNOCK), поль­зователь получит сооб­щение об этом. В кон­це прог­раммы зак­роем сокет и «убь­ем» про­цесс UDP_receive().

Все, обе час­ти прог­раммы готовы. Копируй их на телефо­ны, не забыв дать пра­ва на выпол­нение коман­дами chmox +x client_alarm.py и chmod +x server_alarm.py. Так­же запиши на оба устрой­ства файл nat.py, а в пап­ку DIR_NAME на телефо­не‑сер­вере положи зву­ки тре­воги и опо­веще­ний. Можешь сох­ранить прог­раммы в пап­ку .shortcuts, пред­варитель­но ее соз­дав:

mkdir -p /data/data/com.termux/files/home/.shortcuts

chmod 700 -R /data/data/com.termux/files/home/.shortcuts

В таком слу­чае мож­но будет помес­тить на рабочий экран телефо­на вид­жет с нашей прог­раммой для быс­тро­го запус­ка. Для это­го вос­поль­зуйся меню нас­трой­ки вид­жетов в тво­ем телефо­не.

При­меры двух Termux-вид­жетов для быс­тро­го запус­ка прог­рамм

ВЫВОДЫ

Ду­маю, ты убе­дил­ся, что Termux в уме­лых руках — это мощ­ный инс­тру­мент для прог­рамми­рова­ния Android-смар­тфо­на. Код прог­рамм и зву­ковые фай­лы я выложу на GitHub. Исполь­зуй их как готовое решение или допол­ни нуж­ными воз­можнос­тями. Нап­ример, с помощью termux-location получай GPS-коор­динаты и отправ­ляй их на сер­вер. Либо исполь­зуй вмес­то дат­чика уско­рения дат­чик осве­щен­ности или приб­лижения.

Кро­ме того, мож­но в слу­чае тре­воги делать фото с камеры (для это­го есть скрипт termux-camera-photo). А так­же совер­шать зво­нок на задан­ный номер телефо­на, исполь­зуя termux-telephony-call, или отправ­лять SMS (termux-sms-send). Пря­мой спо­соб соеди­нения устрой­ств, находя­щих­ся за NAT, можешь сме­ло исполь­зовать в дру­гих сво­их сетевых при­ложе­ниях. В общем, давай волю сво­ей фан­тазии и не бой­ся экспе­римен­тировать. Уда­чи!

Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei



Report Page